blob: 46eaccd5395f9590526393bdbbdae4340615a987 [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
61namespace i = ::v8::internal;
62
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()));
384 v8::internal::Heap::CollectAllGarbage(false);
385 CHECK_EQ(0, TestResource::dispose_count);
386 }
387 v8::internal::CompilationCache::Clear();
388 v8::internal::Heap::CollectAllGarbage(false);
389 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());
405 v8::internal::Heap::CollectAllGarbage(false);
406 CHECK_EQ(0, TestAsciiResource::dispose_count);
407 }
408 v8::internal::CompilationCache::Clear();
409 v8::internal::Heap::CollectAllGarbage(false);
410 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());
430 v8::internal::Heap::CollectAllGarbage(false);
431 CHECK_EQ(0, TestResource::dispose_count);
432 }
433 v8::internal::CompilationCache::Clear();
434 v8::internal::Heap::CollectAllGarbage(false);
435 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());
456 v8::internal::Heap::CollectAllGarbage(false);
457 CHECK_EQ(0, TestAsciiResource::dispose_count);
458 }
459 v8::internal::CompilationCache::Clear();
460 v8::internal::Heap::CollectAllGarbage(false);
461 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
Steve Block3ce2e202009-11-05 08:53:23 +0000613THREADED_TEST(StringConcat) {
614 {
615 v8::HandleScope scope;
616 LocalContext env;
617 const char* one_byte_string_1 = "function a_times_t";
618 const char* two_byte_string_1 = "wo_plus_b(a, b) {return ";
619 const char* one_byte_extern_1 = "a * 2 + b;} a_times_two_plus_b(4, 8) + ";
620 const char* two_byte_extern_1 = "a_times_two_plus_b(4, 8) + ";
621 const char* one_byte_string_2 = "a_times_two_plus_b(4, 8) + ";
622 const char* two_byte_string_2 = "a_times_two_plus_b(4, 8) + ";
623 const char* two_byte_extern_2 = "a_times_two_plus_b(1, 2);";
624 Local<String> left = v8_str(one_byte_string_1);
625 Local<String> right = String::New(AsciiToTwoByteString(two_byte_string_1));
626 Local<String> source = String::Concat(left, right);
627 right = String::NewExternal(
628 new TestAsciiResource(i::StrDup(one_byte_extern_1)));
629 source = String::Concat(source, right);
630 right = String::NewExternal(
631 new TestResource(AsciiToTwoByteString(two_byte_extern_1)));
632 source = String::Concat(source, right);
633 right = v8_str(one_byte_string_2);
634 source = String::Concat(source, right);
635 right = String::New(AsciiToTwoByteString(two_byte_string_2));
636 source = String::Concat(source, right);
637 right = String::NewExternal(
638 new TestResource(AsciiToTwoByteString(two_byte_extern_2)));
639 source = String::Concat(source, right);
640 Local<Script> script = Script::Compile(source);
641 Local<Value> value = script->Run();
642 CHECK(value->IsNumber());
643 CHECK_EQ(68, value->Int32Value());
644 }
645 v8::internal::CompilationCache::Clear();
646 i::Heap::CollectAllGarbage(false);
647 i::Heap::CollectAllGarbage(false);
648}
649
650
Steve Blocka7e24c12009-10-30 11:49:00 +0000651THREADED_TEST(GlobalProperties) {
652 v8::HandleScope scope;
653 LocalContext env;
654 v8::Handle<v8::Object> global = env->Global();
655 global->Set(v8_str("pi"), v8_num(3.1415926));
656 Local<Value> pi = global->Get(v8_str("pi"));
657 CHECK_EQ(3.1415926, pi->NumberValue());
658}
659
660
661static v8::Handle<Value> handle_call(const v8::Arguments& args) {
662 ApiTestFuzzer::Fuzz();
663 return v8_num(102);
664}
665
666
667static v8::Handle<Value> construct_call(const v8::Arguments& args) {
668 ApiTestFuzzer::Fuzz();
669 args.This()->Set(v8_str("x"), v8_num(1));
670 args.This()->Set(v8_str("y"), v8_num(2));
671 return args.This();
672}
673
674THREADED_TEST(FunctionTemplate) {
675 v8::HandleScope scope;
676 LocalContext env;
677 {
678 Local<v8::FunctionTemplate> fun_templ =
679 v8::FunctionTemplate::New(handle_call);
680 Local<Function> fun = fun_templ->GetFunction();
681 env->Global()->Set(v8_str("obj"), fun);
682 Local<Script> script = v8_compile("obj()");
683 CHECK_EQ(102, script->Run()->Int32Value());
684 }
685 // Use SetCallHandler to initialize a function template, should work like the
686 // previous one.
687 {
688 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
689 fun_templ->SetCallHandler(handle_call);
690 Local<Function> fun = fun_templ->GetFunction();
691 env->Global()->Set(v8_str("obj"), fun);
692 Local<Script> script = v8_compile("obj()");
693 CHECK_EQ(102, script->Run()->Int32Value());
694 }
695 // Test constructor calls.
696 {
697 Local<v8::FunctionTemplate> fun_templ =
698 v8::FunctionTemplate::New(construct_call);
699 fun_templ->SetClassName(v8_str("funky"));
700 Local<Function> fun = fun_templ->GetFunction();
701 env->Global()->Set(v8_str("obj"), fun);
702 Local<Script> script = v8_compile("var s = new obj(); s.x");
703 CHECK_EQ(1, script->Run()->Int32Value());
704
705 Local<Value> result = v8_compile("(new obj()).toString()")->Run();
706 CHECK_EQ(v8_str("[object funky]"), result);
707 }
708}
709
710
711THREADED_TEST(FindInstanceInPrototypeChain) {
712 v8::HandleScope scope;
713 LocalContext env;
714
715 Local<v8::FunctionTemplate> base = v8::FunctionTemplate::New();
716 Local<v8::FunctionTemplate> derived = v8::FunctionTemplate::New();
717 Local<v8::FunctionTemplate> other = v8::FunctionTemplate::New();
718 derived->Inherit(base);
719
720 Local<v8::Function> base_function = base->GetFunction();
721 Local<v8::Function> derived_function = derived->GetFunction();
722 Local<v8::Function> other_function = other->GetFunction();
723
724 Local<v8::Object> base_instance = base_function->NewInstance();
725 Local<v8::Object> derived_instance = derived_function->NewInstance();
726 Local<v8::Object> derived_instance2 = derived_function->NewInstance();
727 Local<v8::Object> other_instance = other_function->NewInstance();
728 derived_instance2->Set(v8_str("__proto__"), derived_instance);
729 other_instance->Set(v8_str("__proto__"), derived_instance2);
730
731 // base_instance is only an instance of base.
732 CHECK_EQ(base_instance,
733 base_instance->FindInstanceInPrototypeChain(base));
734 CHECK(base_instance->FindInstanceInPrototypeChain(derived).IsEmpty());
735 CHECK(base_instance->FindInstanceInPrototypeChain(other).IsEmpty());
736
737 // derived_instance is an instance of base and derived.
738 CHECK_EQ(derived_instance,
739 derived_instance->FindInstanceInPrototypeChain(base));
740 CHECK_EQ(derived_instance,
741 derived_instance->FindInstanceInPrototypeChain(derived));
742 CHECK(derived_instance->FindInstanceInPrototypeChain(other).IsEmpty());
743
744 // other_instance is an instance of other and its immediate
745 // prototype derived_instance2 is an instance of base and derived.
746 // Note, derived_instance is an instance of base and derived too,
747 // but it comes after derived_instance2 in the prototype chain of
748 // other_instance.
749 CHECK_EQ(derived_instance2,
750 other_instance->FindInstanceInPrototypeChain(base));
751 CHECK_EQ(derived_instance2,
752 other_instance->FindInstanceInPrototypeChain(derived));
753 CHECK_EQ(other_instance,
754 other_instance->FindInstanceInPrototypeChain(other));
755}
756
757
Steve Block3ce2e202009-11-05 08:53:23 +0000758THREADED_TEST(TinyInteger) {
759 v8::HandleScope scope;
760 LocalContext env;
761 int32_t value = 239;
762 Local<v8::Integer> value_obj = v8::Integer::New(value);
763 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
764}
765
766
767THREADED_TEST(BigSmiInteger) {
768 v8::HandleScope scope;
769 LocalContext env;
770 int32_t value = i::Smi::kMaxValue;
771 // We cannot add one to a Smi::kMaxValue without wrapping.
772 if (i::kSmiValueSize < 32) {
773 CHECK(i::Smi::IsValid(value));
774 CHECK(!i::Smi::IsValid(value + 1));
775 Local<v8::Integer> value_obj = v8::Integer::New(value);
776 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
777 }
778}
779
780
781THREADED_TEST(BigInteger) {
782 v8::HandleScope scope;
783 LocalContext env;
784 // We cannot add one to a Smi::kMaxValue without wrapping.
785 if (i::kSmiValueSize < 32) {
786 // The casts allow this to compile, even if Smi::kMaxValue is 2^31-1.
787 // The code will not be run in that case, due to the "if" guard.
788 int32_t value =
789 static_cast<int32_t>(static_cast<uint32_t>(i::Smi::kMaxValue) + 1);
790 CHECK(value > i::Smi::kMaxValue);
791 CHECK(!i::Smi::IsValid(value));
792 Local<v8::Integer> value_obj = v8::Integer::New(value);
793 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
794 }
795}
796
797
798THREADED_TEST(TinyUnsignedInteger) {
799 v8::HandleScope scope;
800 LocalContext env;
801 uint32_t value = 239;
802 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
803 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
804}
805
806
807THREADED_TEST(BigUnsignedSmiInteger) {
808 v8::HandleScope scope;
809 LocalContext env;
810 uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue);
811 CHECK(i::Smi::IsValid(value));
812 CHECK(!i::Smi::IsValid(value + 1));
813 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
814 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
815}
816
817
818THREADED_TEST(BigUnsignedInteger) {
819 v8::HandleScope scope;
820 LocalContext env;
821 uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue) + 1;
822 CHECK(value > static_cast<uint32_t>(i::Smi::kMaxValue));
823 CHECK(!i::Smi::IsValid(value));
824 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
825 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
826}
827
828
829THREADED_TEST(OutOfSignedRangeUnsignedInteger) {
830 v8::HandleScope scope;
831 LocalContext env;
832 uint32_t INT32_MAX_AS_UINT = (1U << 31) - 1;
833 uint32_t value = INT32_MAX_AS_UINT + 1;
834 CHECK(value > INT32_MAX_AS_UINT); // No overflow.
835 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
836 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
837}
838
839
Steve Blocka7e24c12009-10-30 11:49:00 +0000840THREADED_TEST(Number) {
841 v8::HandleScope scope;
842 LocalContext env;
843 double PI = 3.1415926;
844 Local<v8::Number> pi_obj = v8::Number::New(PI);
845 CHECK_EQ(PI, pi_obj->NumberValue());
846}
847
848
849THREADED_TEST(ToNumber) {
850 v8::HandleScope scope;
851 LocalContext env;
852 Local<String> str = v8_str("3.1415926");
853 CHECK_EQ(3.1415926, str->NumberValue());
854 v8::Handle<v8::Boolean> t = v8::True();
855 CHECK_EQ(1.0, t->NumberValue());
856 v8::Handle<v8::Boolean> f = v8::False();
857 CHECK_EQ(0.0, f->NumberValue());
858}
859
860
861THREADED_TEST(Date) {
862 v8::HandleScope scope;
863 LocalContext env;
864 double PI = 3.1415926;
865 Local<Value> date_obj = v8::Date::New(PI);
866 CHECK_EQ(3.0, date_obj->NumberValue());
867}
868
869
870THREADED_TEST(Boolean) {
871 v8::HandleScope scope;
872 LocalContext env;
873 v8::Handle<v8::Boolean> t = v8::True();
874 CHECK(t->Value());
875 v8::Handle<v8::Boolean> f = v8::False();
876 CHECK(!f->Value());
877 v8::Handle<v8::Primitive> u = v8::Undefined();
878 CHECK(!u->BooleanValue());
879 v8::Handle<v8::Primitive> n = v8::Null();
880 CHECK(!n->BooleanValue());
881 v8::Handle<String> str1 = v8_str("");
882 CHECK(!str1->BooleanValue());
883 v8::Handle<String> str2 = v8_str("x");
884 CHECK(str2->BooleanValue());
885 CHECK(!v8::Number::New(0)->BooleanValue());
886 CHECK(v8::Number::New(-1)->BooleanValue());
887 CHECK(v8::Number::New(1)->BooleanValue());
888 CHECK(v8::Number::New(42)->BooleanValue());
889 CHECK(!v8_compile("NaN")->Run()->BooleanValue());
890}
891
892
893static v8::Handle<Value> DummyCallHandler(const v8::Arguments& args) {
894 ApiTestFuzzer::Fuzz();
895 return v8_num(13.4);
896}
897
898
899static v8::Handle<Value> GetM(Local<String> name, const AccessorInfo&) {
900 ApiTestFuzzer::Fuzz();
901 return v8_num(876);
902}
903
904
905THREADED_TEST(GlobalPrototype) {
906 v8::HandleScope scope;
907 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New();
908 func_templ->PrototypeTemplate()->Set(
909 "dummy",
910 v8::FunctionTemplate::New(DummyCallHandler));
911 v8::Handle<ObjectTemplate> templ = func_templ->InstanceTemplate();
912 templ->Set("x", v8_num(200));
913 templ->SetAccessor(v8_str("m"), GetM);
914 LocalContext env(0, templ);
915 v8::Handle<v8::Object> obj = env->Global();
916 v8::Handle<Script> script = v8_compile("dummy()");
917 v8::Handle<Value> result = script->Run();
918 CHECK_EQ(13.4, result->NumberValue());
919 CHECK_EQ(200, v8_compile("x")->Run()->Int32Value());
920 CHECK_EQ(876, v8_compile("m")->Run()->Int32Value());
921}
922
923
Steve Blocka7e24c12009-10-30 11:49:00 +0000924THREADED_TEST(ObjectTemplate) {
925 v8::HandleScope scope;
926 Local<ObjectTemplate> templ1 = ObjectTemplate::New();
927 templ1->Set("x", v8_num(10));
928 templ1->Set("y", v8_num(13));
929 LocalContext env;
930 Local<v8::Object> instance1 = templ1->NewInstance();
931 env->Global()->Set(v8_str("p"), instance1);
932 CHECK(v8_compile("(p.x == 10)")->Run()->BooleanValue());
933 CHECK(v8_compile("(p.y == 13)")->Run()->BooleanValue());
934 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New();
935 fun->PrototypeTemplate()->Set("nirk", v8_num(123));
936 Local<ObjectTemplate> templ2 = fun->InstanceTemplate();
937 templ2->Set("a", v8_num(12));
938 templ2->Set("b", templ1);
939 Local<v8::Object> instance2 = templ2->NewInstance();
940 env->Global()->Set(v8_str("q"), instance2);
941 CHECK(v8_compile("(q.nirk == 123)")->Run()->BooleanValue());
942 CHECK(v8_compile("(q.a == 12)")->Run()->BooleanValue());
943 CHECK(v8_compile("(q.b.x == 10)")->Run()->BooleanValue());
944 CHECK(v8_compile("(q.b.y == 13)")->Run()->BooleanValue());
945}
946
947
948static v8::Handle<Value> GetFlabby(const v8::Arguments& args) {
949 ApiTestFuzzer::Fuzz();
950 return v8_num(17.2);
951}
952
953
954static v8::Handle<Value> GetKnurd(Local<String> property, const AccessorInfo&) {
955 ApiTestFuzzer::Fuzz();
956 return v8_num(15.2);
957}
958
959
960THREADED_TEST(DescriptorInheritance) {
961 v8::HandleScope scope;
962 v8::Handle<v8::FunctionTemplate> super = v8::FunctionTemplate::New();
963 super->PrototypeTemplate()->Set("flabby",
964 v8::FunctionTemplate::New(GetFlabby));
965 super->PrototypeTemplate()->Set("PI", v8_num(3.14));
966
967 super->InstanceTemplate()->SetAccessor(v8_str("knurd"), GetKnurd);
968
969 v8::Handle<v8::FunctionTemplate> base1 = v8::FunctionTemplate::New();
970 base1->Inherit(super);
971 base1->PrototypeTemplate()->Set("v1", v8_num(20.1));
972
973 v8::Handle<v8::FunctionTemplate> base2 = v8::FunctionTemplate::New();
974 base2->Inherit(super);
975 base2->PrototypeTemplate()->Set("v2", v8_num(10.1));
976
977 LocalContext env;
978
979 env->Global()->Set(v8_str("s"), super->GetFunction());
980 env->Global()->Set(v8_str("base1"), base1->GetFunction());
981 env->Global()->Set(v8_str("base2"), base2->GetFunction());
982
983 // Checks right __proto__ chain.
984 CHECK(CompileRun("base1.prototype.__proto__ == s.prototype")->BooleanValue());
985 CHECK(CompileRun("base2.prototype.__proto__ == s.prototype")->BooleanValue());
986
987 CHECK(v8_compile("s.prototype.PI == 3.14")->Run()->BooleanValue());
988
989 // Instance accessor should not be visible on function object or its prototype
990 CHECK(CompileRun("s.knurd == undefined")->BooleanValue());
991 CHECK(CompileRun("s.prototype.knurd == undefined")->BooleanValue());
992 CHECK(CompileRun("base1.prototype.knurd == undefined")->BooleanValue());
993
994 env->Global()->Set(v8_str("obj"),
995 base1->GetFunction()->NewInstance());
996 CHECK_EQ(17.2, v8_compile("obj.flabby()")->Run()->NumberValue());
997 CHECK(v8_compile("'flabby' in obj")->Run()->BooleanValue());
998 CHECK_EQ(15.2, v8_compile("obj.knurd")->Run()->NumberValue());
999 CHECK(v8_compile("'knurd' in obj")->Run()->BooleanValue());
1000 CHECK_EQ(20.1, v8_compile("obj.v1")->Run()->NumberValue());
1001
1002 env->Global()->Set(v8_str("obj2"),
1003 base2->GetFunction()->NewInstance());
1004 CHECK_EQ(17.2, v8_compile("obj2.flabby()")->Run()->NumberValue());
1005 CHECK(v8_compile("'flabby' in obj2")->Run()->BooleanValue());
1006 CHECK_EQ(15.2, v8_compile("obj2.knurd")->Run()->NumberValue());
1007 CHECK(v8_compile("'knurd' in obj2")->Run()->BooleanValue());
1008 CHECK_EQ(10.1, v8_compile("obj2.v2")->Run()->NumberValue());
1009
1010 // base1 and base2 cannot cross reference to each's prototype
1011 CHECK(v8_compile("obj.v2")->Run()->IsUndefined());
1012 CHECK(v8_compile("obj2.v1")->Run()->IsUndefined());
1013}
1014
1015
1016int echo_named_call_count;
1017
1018
1019static v8::Handle<Value> EchoNamedProperty(Local<String> name,
1020 const AccessorInfo& info) {
1021 ApiTestFuzzer::Fuzz();
1022 CHECK_EQ(v8_str("data"), info.Data());
1023 echo_named_call_count++;
1024 return name;
1025}
1026
1027
1028THREADED_TEST(NamedPropertyHandlerGetter) {
1029 echo_named_call_count = 0;
1030 v8::HandleScope scope;
1031 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1032 templ->InstanceTemplate()->SetNamedPropertyHandler(EchoNamedProperty,
1033 0, 0, 0, 0,
1034 v8_str("data"));
1035 LocalContext env;
1036 env->Global()->Set(v8_str("obj"),
1037 templ->GetFunction()->NewInstance());
1038 CHECK_EQ(echo_named_call_count, 0);
1039 v8_compile("obj.x")->Run();
1040 CHECK_EQ(echo_named_call_count, 1);
1041 const char* code = "var str = 'oddle'; obj[str] + obj.poddle;";
1042 v8::Handle<Value> str = CompileRun(code);
1043 String::AsciiValue value(str);
1044 CHECK_EQ(*value, "oddlepoddle");
1045 // Check default behavior
1046 CHECK_EQ(v8_compile("obj.flob = 10;")->Run()->Int32Value(), 10);
1047 CHECK(v8_compile("'myProperty' in obj")->Run()->BooleanValue());
1048 CHECK(v8_compile("delete obj.myProperty")->Run()->BooleanValue());
1049}
1050
1051
1052int echo_indexed_call_count = 0;
1053
1054
1055static v8::Handle<Value> EchoIndexedProperty(uint32_t index,
1056 const AccessorInfo& info) {
1057 ApiTestFuzzer::Fuzz();
1058 CHECK_EQ(v8_num(637), info.Data());
1059 echo_indexed_call_count++;
1060 return v8_num(index);
1061}
1062
1063
1064THREADED_TEST(IndexedPropertyHandlerGetter) {
1065 v8::HandleScope scope;
1066 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1067 templ->InstanceTemplate()->SetIndexedPropertyHandler(EchoIndexedProperty,
1068 0, 0, 0, 0,
1069 v8_num(637));
1070 LocalContext env;
1071 env->Global()->Set(v8_str("obj"),
1072 templ->GetFunction()->NewInstance());
1073 Local<Script> script = v8_compile("obj[900]");
1074 CHECK_EQ(script->Run()->Int32Value(), 900);
1075}
1076
1077
1078v8::Handle<v8::Object> bottom;
1079
1080static v8::Handle<Value> CheckThisIndexedPropertyHandler(
1081 uint32_t index,
1082 const AccessorInfo& info) {
1083 ApiTestFuzzer::Fuzz();
1084 CHECK(info.This()->Equals(bottom));
1085 return v8::Handle<Value>();
1086}
1087
1088static v8::Handle<Value> CheckThisNamedPropertyHandler(
1089 Local<String> name,
1090 const AccessorInfo& info) {
1091 ApiTestFuzzer::Fuzz();
1092 CHECK(info.This()->Equals(bottom));
1093 return v8::Handle<Value>();
1094}
1095
1096
1097v8::Handle<Value> CheckThisIndexedPropertySetter(uint32_t index,
1098 Local<Value> value,
1099 const AccessorInfo& info) {
1100 ApiTestFuzzer::Fuzz();
1101 CHECK(info.This()->Equals(bottom));
1102 return v8::Handle<Value>();
1103}
1104
1105
1106v8::Handle<Value> CheckThisNamedPropertySetter(Local<String> property,
1107 Local<Value> value,
1108 const AccessorInfo& info) {
1109 ApiTestFuzzer::Fuzz();
1110 CHECK(info.This()->Equals(bottom));
1111 return v8::Handle<Value>();
1112}
1113
1114v8::Handle<v8::Boolean> CheckThisIndexedPropertyQuery(
1115 uint32_t index,
1116 const AccessorInfo& info) {
1117 ApiTestFuzzer::Fuzz();
1118 CHECK(info.This()->Equals(bottom));
1119 return v8::Handle<v8::Boolean>();
1120}
1121
1122
1123v8::Handle<v8::Boolean> CheckThisNamedPropertyQuery(Local<String> property,
1124 const AccessorInfo& info) {
1125 ApiTestFuzzer::Fuzz();
1126 CHECK(info.This()->Equals(bottom));
1127 return v8::Handle<v8::Boolean>();
1128}
1129
1130
1131v8::Handle<v8::Boolean> CheckThisIndexedPropertyDeleter(
1132 uint32_t index,
1133 const AccessorInfo& info) {
1134 ApiTestFuzzer::Fuzz();
1135 CHECK(info.This()->Equals(bottom));
1136 return v8::Handle<v8::Boolean>();
1137}
1138
1139
1140v8::Handle<v8::Boolean> CheckThisNamedPropertyDeleter(
1141 Local<String> property,
1142 const AccessorInfo& info) {
1143 ApiTestFuzzer::Fuzz();
1144 CHECK(info.This()->Equals(bottom));
1145 return v8::Handle<v8::Boolean>();
1146}
1147
1148
1149v8::Handle<v8::Array> CheckThisIndexedPropertyEnumerator(
1150 const AccessorInfo& info) {
1151 ApiTestFuzzer::Fuzz();
1152 CHECK(info.This()->Equals(bottom));
1153 return v8::Handle<v8::Array>();
1154}
1155
1156
1157v8::Handle<v8::Array> CheckThisNamedPropertyEnumerator(
1158 const AccessorInfo& info) {
1159 ApiTestFuzzer::Fuzz();
1160 CHECK(info.This()->Equals(bottom));
1161 return v8::Handle<v8::Array>();
1162}
1163
1164
1165THREADED_TEST(PropertyHandlerInPrototype) {
1166 v8::HandleScope scope;
1167 LocalContext env;
1168
1169 // Set up a prototype chain with three interceptors.
1170 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1171 templ->InstanceTemplate()->SetIndexedPropertyHandler(
1172 CheckThisIndexedPropertyHandler,
1173 CheckThisIndexedPropertySetter,
1174 CheckThisIndexedPropertyQuery,
1175 CheckThisIndexedPropertyDeleter,
1176 CheckThisIndexedPropertyEnumerator);
1177
1178 templ->InstanceTemplate()->SetNamedPropertyHandler(
1179 CheckThisNamedPropertyHandler,
1180 CheckThisNamedPropertySetter,
1181 CheckThisNamedPropertyQuery,
1182 CheckThisNamedPropertyDeleter,
1183 CheckThisNamedPropertyEnumerator);
1184
1185 bottom = templ->GetFunction()->NewInstance();
1186 Local<v8::Object> top = templ->GetFunction()->NewInstance();
1187 Local<v8::Object> middle = templ->GetFunction()->NewInstance();
1188
1189 bottom->Set(v8_str("__proto__"), middle);
1190 middle->Set(v8_str("__proto__"), top);
1191 env->Global()->Set(v8_str("obj"), bottom);
1192
1193 // Indexed and named get.
1194 Script::Compile(v8_str("obj[0]"))->Run();
1195 Script::Compile(v8_str("obj.x"))->Run();
1196
1197 // Indexed and named set.
1198 Script::Compile(v8_str("obj[1] = 42"))->Run();
1199 Script::Compile(v8_str("obj.y = 42"))->Run();
1200
1201 // Indexed and named query.
1202 Script::Compile(v8_str("0 in obj"))->Run();
1203 Script::Compile(v8_str("'x' in obj"))->Run();
1204
1205 // Indexed and named deleter.
1206 Script::Compile(v8_str("delete obj[0]"))->Run();
1207 Script::Compile(v8_str("delete obj.x"))->Run();
1208
1209 // Enumerators.
1210 Script::Compile(v8_str("for (var p in obj) ;"))->Run();
1211}
1212
1213
1214static v8::Handle<Value> PrePropertyHandlerGet(Local<String> key,
1215 const AccessorInfo& info) {
1216 ApiTestFuzzer::Fuzz();
1217 if (v8_str("pre")->Equals(key)) {
1218 return v8_str("PrePropertyHandler: pre");
1219 }
1220 return v8::Handle<String>();
1221}
1222
1223
1224static v8::Handle<v8::Boolean> PrePropertyHandlerHas(Local<String> key,
1225 const AccessorInfo&) {
1226 if (v8_str("pre")->Equals(key)) {
1227 return v8::True();
1228 }
1229
1230 return v8::Handle<v8::Boolean>(); // do not intercept the call
1231}
1232
1233
1234THREADED_TEST(PrePropertyHandler) {
1235 v8::HandleScope scope;
1236 v8::Handle<v8::FunctionTemplate> desc = v8::FunctionTemplate::New();
1237 desc->InstanceTemplate()->SetNamedPropertyHandler(PrePropertyHandlerGet,
1238 0,
1239 PrePropertyHandlerHas);
1240 LocalContext env(NULL, desc->InstanceTemplate());
1241 Script::Compile(v8_str(
1242 "var pre = 'Object: pre'; var on = 'Object: on';"))->Run();
1243 v8::Handle<Value> result_pre = Script::Compile(v8_str("pre"))->Run();
1244 CHECK_EQ(v8_str("PrePropertyHandler: pre"), result_pre);
1245 v8::Handle<Value> result_on = Script::Compile(v8_str("on"))->Run();
1246 CHECK_EQ(v8_str("Object: on"), result_on);
1247 v8::Handle<Value> result_post = Script::Compile(v8_str("post"))->Run();
1248 CHECK(result_post.IsEmpty());
1249}
1250
1251
1252THREADED_TEST(UndefinedIsNotEnumerable) {
1253 v8::HandleScope scope;
1254 LocalContext env;
1255 v8::Handle<Value> result = Script::Compile(v8_str(
1256 "this.propertyIsEnumerable(undefined)"))->Run();
1257 CHECK(result->IsFalse());
1258}
1259
1260
1261v8::Handle<Script> call_recursively_script;
Leon Clarke4515c472010-02-03 11:58:03 +00001262static const int kTargetRecursionDepth = 200; // near maximum
Steve Blocka7e24c12009-10-30 11:49:00 +00001263
1264
1265static v8::Handle<Value> CallScriptRecursivelyCall(const v8::Arguments& args) {
1266 ApiTestFuzzer::Fuzz();
1267 int depth = args.This()->Get(v8_str("depth"))->Int32Value();
1268 if (depth == kTargetRecursionDepth) return v8::Undefined();
1269 args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1));
1270 return call_recursively_script->Run();
1271}
1272
1273
1274static v8::Handle<Value> CallFunctionRecursivelyCall(
1275 const v8::Arguments& args) {
1276 ApiTestFuzzer::Fuzz();
1277 int depth = args.This()->Get(v8_str("depth"))->Int32Value();
1278 if (depth == kTargetRecursionDepth) {
1279 printf("[depth = %d]\n", depth);
1280 return v8::Undefined();
1281 }
1282 args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1));
1283 v8::Handle<Value> function =
1284 args.This()->Get(v8_str("callFunctionRecursively"));
Steve Block6ded16b2010-05-10 14:33:55 +01001285 return function.As<Function>()->Call(args.This(), 0, NULL);
Steve Blocka7e24c12009-10-30 11:49:00 +00001286}
1287
1288
1289THREADED_TEST(DeepCrossLanguageRecursion) {
1290 v8::HandleScope scope;
1291 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New();
1292 global->Set(v8_str("callScriptRecursively"),
1293 v8::FunctionTemplate::New(CallScriptRecursivelyCall));
1294 global->Set(v8_str("callFunctionRecursively"),
1295 v8::FunctionTemplate::New(CallFunctionRecursivelyCall));
1296 LocalContext env(NULL, global);
1297
1298 env->Global()->Set(v8_str("depth"), v8::Integer::New(0));
1299 call_recursively_script = v8_compile("callScriptRecursively()");
1300 v8::Handle<Value> result = call_recursively_script->Run();
1301 call_recursively_script = v8::Handle<Script>();
1302
1303 env->Global()->Set(v8_str("depth"), v8::Integer::New(0));
1304 Script::Compile(v8_str("callFunctionRecursively()"))->Run();
1305}
1306
1307
1308static v8::Handle<Value>
1309 ThrowingPropertyHandlerGet(Local<String> key, const AccessorInfo&) {
1310 ApiTestFuzzer::Fuzz();
1311 return v8::ThrowException(key);
1312}
1313
1314
1315static v8::Handle<Value> ThrowingPropertyHandlerSet(Local<String> key,
1316 Local<Value>,
1317 const AccessorInfo&) {
1318 v8::ThrowException(key);
1319 return v8::Undefined(); // not the same as v8::Handle<v8::Value>()
1320}
1321
1322
1323THREADED_TEST(CallbackExceptionRegression) {
1324 v8::HandleScope scope;
1325 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
1326 obj->SetNamedPropertyHandler(ThrowingPropertyHandlerGet,
1327 ThrowingPropertyHandlerSet);
1328 LocalContext env;
1329 env->Global()->Set(v8_str("obj"), obj->NewInstance());
1330 v8::Handle<Value> otto = Script::Compile(v8_str(
1331 "try { with (obj) { otto; } } catch (e) { e; }"))->Run();
1332 CHECK_EQ(v8_str("otto"), otto);
1333 v8::Handle<Value> netto = Script::Compile(v8_str(
1334 "try { with (obj) { netto = 4; } } catch (e) { e; }"))->Run();
1335 CHECK_EQ(v8_str("netto"), netto);
1336}
1337
1338
Steve Blocka7e24c12009-10-30 11:49:00 +00001339THREADED_TEST(FunctionPrototype) {
1340 v8::HandleScope scope;
1341 Local<v8::FunctionTemplate> Foo = v8::FunctionTemplate::New();
1342 Foo->PrototypeTemplate()->Set(v8_str("plak"), v8_num(321));
1343 LocalContext env;
1344 env->Global()->Set(v8_str("Foo"), Foo->GetFunction());
1345 Local<Script> script = Script::Compile(v8_str("Foo.prototype.plak"));
1346 CHECK_EQ(script->Run()->Int32Value(), 321);
1347}
1348
1349
1350THREADED_TEST(InternalFields) {
1351 v8::HandleScope scope;
1352 LocalContext env;
1353
1354 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1355 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate();
1356 instance_templ->SetInternalFieldCount(1);
1357 Local<v8::Object> obj = templ->GetFunction()->NewInstance();
1358 CHECK_EQ(1, obj->InternalFieldCount());
1359 CHECK(obj->GetInternalField(0)->IsUndefined());
1360 obj->SetInternalField(0, v8_num(17));
1361 CHECK_EQ(17, obj->GetInternalField(0)->Int32Value());
1362}
1363
1364
Steve Block6ded16b2010-05-10 14:33:55 +01001365THREADED_TEST(GlobalObjectInternalFields) {
1366 v8::HandleScope scope;
1367 Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
1368 global_template->SetInternalFieldCount(1);
1369 LocalContext env(NULL, global_template);
1370 v8::Handle<v8::Object> global_proxy = env->Global();
1371 v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>();
1372 CHECK_EQ(1, global->InternalFieldCount());
1373 CHECK(global->GetInternalField(0)->IsUndefined());
1374 global->SetInternalField(0, v8_num(17));
1375 CHECK_EQ(17, global->GetInternalField(0)->Int32Value());
1376}
1377
1378
Steve Blocka7e24c12009-10-30 11:49:00 +00001379THREADED_TEST(InternalFieldsNativePointers) {
1380 v8::HandleScope scope;
1381 LocalContext env;
1382
1383 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1384 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate();
1385 instance_templ->SetInternalFieldCount(1);
1386 Local<v8::Object> obj = templ->GetFunction()->NewInstance();
1387 CHECK_EQ(1, obj->InternalFieldCount());
1388 CHECK(obj->GetPointerFromInternalField(0) == NULL);
1389
1390 char* data = new char[100];
1391
1392 void* aligned = data;
1393 CHECK_EQ(0, reinterpret_cast<uintptr_t>(aligned) & 0x1);
1394 void* unaligned = data + 1;
1395 CHECK_EQ(1, reinterpret_cast<uintptr_t>(unaligned) & 0x1);
1396
1397 // Check reading and writing aligned pointers.
1398 obj->SetPointerInInternalField(0, aligned);
1399 i::Heap::CollectAllGarbage(false);
1400 CHECK_EQ(aligned, obj->GetPointerFromInternalField(0));
1401
1402 // Check reading and writing unaligned pointers.
1403 obj->SetPointerInInternalField(0, unaligned);
1404 i::Heap::CollectAllGarbage(false);
1405 CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0));
1406
1407 delete[] data;
1408}
1409
1410
Steve Block3ce2e202009-11-05 08:53:23 +00001411THREADED_TEST(InternalFieldsNativePointersAndExternal) {
1412 v8::HandleScope scope;
1413 LocalContext env;
1414
1415 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1416 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate();
1417 instance_templ->SetInternalFieldCount(1);
1418 Local<v8::Object> obj = templ->GetFunction()->NewInstance();
1419 CHECK_EQ(1, obj->InternalFieldCount());
1420 CHECK(obj->GetPointerFromInternalField(0) == NULL);
1421
1422 char* data = new char[100];
1423
1424 void* aligned = data;
1425 CHECK_EQ(0, reinterpret_cast<uintptr_t>(aligned) & 0x1);
1426 void* unaligned = data + 1;
1427 CHECK_EQ(1, reinterpret_cast<uintptr_t>(unaligned) & 0x1);
1428
1429 obj->SetPointerInInternalField(0, aligned);
1430 i::Heap::CollectAllGarbage(false);
1431 CHECK_EQ(aligned, v8::External::Unwrap(obj->GetInternalField(0)));
1432
1433 obj->SetPointerInInternalField(0, unaligned);
1434 i::Heap::CollectAllGarbage(false);
1435 CHECK_EQ(unaligned, v8::External::Unwrap(obj->GetInternalField(0)));
1436
1437 obj->SetInternalField(0, v8::External::Wrap(aligned));
1438 i::Heap::CollectAllGarbage(false);
1439 CHECK_EQ(aligned, obj->GetPointerFromInternalField(0));
1440
1441 obj->SetInternalField(0, v8::External::Wrap(unaligned));
1442 i::Heap::CollectAllGarbage(false);
1443 CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0));
1444
1445 delete[] data;
1446}
1447
1448
Steve Blocka7e24c12009-10-30 11:49:00 +00001449THREADED_TEST(IdentityHash) {
1450 v8::HandleScope scope;
1451 LocalContext env;
1452
1453 // Ensure that the test starts with an fresh heap to test whether the hash
1454 // code is based on the address.
1455 i::Heap::CollectAllGarbage(false);
1456 Local<v8::Object> obj = v8::Object::New();
1457 int hash = obj->GetIdentityHash();
1458 int hash1 = obj->GetIdentityHash();
1459 CHECK_EQ(hash, hash1);
1460 int hash2 = v8::Object::New()->GetIdentityHash();
1461 // Since the identity hash is essentially a random number two consecutive
1462 // objects should not be assigned the same hash code. If the test below fails
1463 // the random number generator should be evaluated.
1464 CHECK_NE(hash, hash2);
1465 i::Heap::CollectAllGarbage(false);
1466 int hash3 = v8::Object::New()->GetIdentityHash();
1467 // Make sure that the identity hash is not based on the initial address of
1468 // the object alone. If the test below fails the random number generator
1469 // should be evaluated.
1470 CHECK_NE(hash, hash3);
1471 int hash4 = obj->GetIdentityHash();
1472 CHECK_EQ(hash, hash4);
1473}
1474
1475
1476THREADED_TEST(HiddenProperties) {
1477 v8::HandleScope scope;
1478 LocalContext env;
1479
1480 v8::Local<v8::Object> obj = v8::Object::New();
1481 v8::Local<v8::String> key = v8_str("api-test::hidden-key");
1482 v8::Local<v8::String> empty = v8_str("");
1483 v8::Local<v8::String> prop_name = v8_str("prop_name");
1484
1485 i::Heap::CollectAllGarbage(false);
1486
1487 // Make sure delete of a non-existent hidden value works
1488 CHECK(obj->DeleteHiddenValue(key));
1489
1490 CHECK(obj->SetHiddenValue(key, v8::Integer::New(1503)));
1491 CHECK_EQ(1503, obj->GetHiddenValue(key)->Int32Value());
1492 CHECK(obj->SetHiddenValue(key, v8::Integer::New(2002)));
1493 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1494
1495 i::Heap::CollectAllGarbage(false);
1496
1497 // Make sure we do not find the hidden property.
1498 CHECK(!obj->Has(empty));
1499 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1500 CHECK(obj->Get(empty)->IsUndefined());
1501 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1502 CHECK(obj->Set(empty, v8::Integer::New(2003)));
1503 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1504 CHECK_EQ(2003, obj->Get(empty)->Int32Value());
1505
1506 i::Heap::CollectAllGarbage(false);
1507
1508 // Add another property and delete it afterwards to force the object in
1509 // slow case.
1510 CHECK(obj->Set(prop_name, v8::Integer::New(2008)));
1511 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1512 CHECK_EQ(2008, obj->Get(prop_name)->Int32Value());
1513 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1514 CHECK(obj->Delete(prop_name));
1515 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1516
1517 i::Heap::CollectAllGarbage(false);
1518
1519 CHECK(obj->DeleteHiddenValue(key));
1520 CHECK(obj->GetHiddenValue(key).IsEmpty());
1521}
1522
1523
Steve Blockd0582a62009-12-15 09:54:21 +00001524static bool interceptor_for_hidden_properties_called;
Steve Blocka7e24c12009-10-30 11:49:00 +00001525static v8::Handle<Value> InterceptorForHiddenProperties(
1526 Local<String> name, const AccessorInfo& info) {
Steve Blockd0582a62009-12-15 09:54:21 +00001527 interceptor_for_hidden_properties_called = true;
Steve Blocka7e24c12009-10-30 11:49:00 +00001528 return v8::Handle<Value>();
1529}
1530
1531
1532THREADED_TEST(HiddenPropertiesWithInterceptors) {
1533 v8::HandleScope scope;
1534 LocalContext context;
1535
Steve Blockd0582a62009-12-15 09:54:21 +00001536 interceptor_for_hidden_properties_called = false;
1537
Steve Blocka7e24c12009-10-30 11:49:00 +00001538 v8::Local<v8::String> key = v8_str("api-test::hidden-key");
1539
1540 // Associate an interceptor with an object and start setting hidden values.
1541 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
1542 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
1543 instance_templ->SetNamedPropertyHandler(InterceptorForHiddenProperties);
1544 Local<v8::Function> function = fun_templ->GetFunction();
1545 Local<v8::Object> obj = function->NewInstance();
1546 CHECK(obj->SetHiddenValue(key, v8::Integer::New(2302)));
1547 CHECK_EQ(2302, obj->GetHiddenValue(key)->Int32Value());
Steve Blockd0582a62009-12-15 09:54:21 +00001548 CHECK(!interceptor_for_hidden_properties_called);
Steve Blocka7e24c12009-10-30 11:49:00 +00001549}
1550
1551
1552THREADED_TEST(External) {
1553 v8::HandleScope scope;
1554 int x = 3;
1555 Local<v8::External> ext = v8::External::New(&x);
1556 LocalContext env;
1557 env->Global()->Set(v8_str("ext"), ext);
1558 Local<Value> reext_obj = Script::Compile(v8_str("this.ext"))->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01001559 v8::Handle<v8::External> reext = reext_obj.As<v8::External>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001560 int* ptr = static_cast<int*>(reext->Value());
1561 CHECK_EQ(x, 3);
1562 *ptr = 10;
1563 CHECK_EQ(x, 10);
1564
1565 // Make sure unaligned pointers are wrapped properly.
1566 char* data = i::StrDup("0123456789");
1567 Local<v8::Value> zero = v8::External::Wrap(&data[0]);
1568 Local<v8::Value> one = v8::External::Wrap(&data[1]);
1569 Local<v8::Value> two = v8::External::Wrap(&data[2]);
1570 Local<v8::Value> three = v8::External::Wrap(&data[3]);
1571
1572 char* char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(zero));
1573 CHECK_EQ('0', *char_ptr);
1574 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(one));
1575 CHECK_EQ('1', *char_ptr);
1576 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(two));
1577 CHECK_EQ('2', *char_ptr);
1578 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(three));
1579 CHECK_EQ('3', *char_ptr);
1580 i::DeleteArray(data);
1581}
1582
1583
1584THREADED_TEST(GlobalHandle) {
1585 v8::Persistent<String> global;
1586 {
1587 v8::HandleScope scope;
1588 Local<String> str = v8_str("str");
1589 global = v8::Persistent<String>::New(str);
1590 }
1591 CHECK_EQ(global->Length(), 3);
1592 global.Dispose();
1593}
1594
1595
1596THREADED_TEST(ScriptException) {
1597 v8::HandleScope scope;
1598 LocalContext env;
1599 Local<Script> script = Script::Compile(v8_str("throw 'panama!';"));
1600 v8::TryCatch try_catch;
1601 Local<Value> result = script->Run();
1602 CHECK(result.IsEmpty());
1603 CHECK(try_catch.HasCaught());
1604 String::AsciiValue exception_value(try_catch.Exception());
1605 CHECK_EQ(*exception_value, "panama!");
1606}
1607
1608
1609bool message_received;
1610
1611
1612static void check_message(v8::Handle<v8::Message> message,
1613 v8::Handle<Value> data) {
1614 CHECK_EQ(5.76, data->NumberValue());
1615 CHECK_EQ(6.75, message->GetScriptResourceName()->NumberValue());
1616 CHECK_EQ(7.56, message->GetScriptData()->NumberValue());
1617 message_received = true;
1618}
1619
1620
1621THREADED_TEST(MessageHandlerData) {
1622 message_received = false;
1623 v8::HandleScope scope;
1624 CHECK(!message_received);
1625 v8::V8::AddMessageListener(check_message, v8_num(5.76));
1626 LocalContext context;
1627 v8::ScriptOrigin origin =
1628 v8::ScriptOrigin(v8_str("6.75"));
1629 v8::Handle<v8::Script> script = Script::Compile(v8_str("throw 'error'"),
1630 &origin);
1631 script->SetData(v8_str("7.56"));
1632 script->Run();
1633 CHECK(message_received);
1634 // clear out the message listener
1635 v8::V8::RemoveMessageListeners(check_message);
1636}
1637
1638
1639THREADED_TEST(GetSetProperty) {
1640 v8::HandleScope scope;
1641 LocalContext context;
1642 context->Global()->Set(v8_str("foo"), v8_num(14));
1643 context->Global()->Set(v8_str("12"), v8_num(92));
1644 context->Global()->Set(v8::Integer::New(16), v8_num(32));
1645 context->Global()->Set(v8_num(13), v8_num(56));
1646 Local<Value> foo = Script::Compile(v8_str("this.foo"))->Run();
1647 CHECK_EQ(14, foo->Int32Value());
1648 Local<Value> twelve = Script::Compile(v8_str("this[12]"))->Run();
1649 CHECK_EQ(92, twelve->Int32Value());
1650 Local<Value> sixteen = Script::Compile(v8_str("this[16]"))->Run();
1651 CHECK_EQ(32, sixteen->Int32Value());
1652 Local<Value> thirteen = Script::Compile(v8_str("this[13]"))->Run();
1653 CHECK_EQ(56, thirteen->Int32Value());
1654 CHECK_EQ(92, context->Global()->Get(v8::Integer::New(12))->Int32Value());
1655 CHECK_EQ(92, context->Global()->Get(v8_str("12"))->Int32Value());
1656 CHECK_EQ(92, context->Global()->Get(v8_num(12))->Int32Value());
1657 CHECK_EQ(32, context->Global()->Get(v8::Integer::New(16))->Int32Value());
1658 CHECK_EQ(32, context->Global()->Get(v8_str("16"))->Int32Value());
1659 CHECK_EQ(32, context->Global()->Get(v8_num(16))->Int32Value());
1660 CHECK_EQ(56, context->Global()->Get(v8::Integer::New(13))->Int32Value());
1661 CHECK_EQ(56, context->Global()->Get(v8_str("13"))->Int32Value());
1662 CHECK_EQ(56, context->Global()->Get(v8_num(13))->Int32Value());
1663}
1664
1665
1666THREADED_TEST(PropertyAttributes) {
1667 v8::HandleScope scope;
1668 LocalContext context;
1669 // read-only
1670 Local<String> prop = v8_str("read_only");
1671 context->Global()->Set(prop, v8_num(7), v8::ReadOnly);
1672 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value());
1673 Script::Compile(v8_str("read_only = 9"))->Run();
1674 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value());
1675 context->Global()->Set(prop, v8_num(10));
1676 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value());
1677 // dont-delete
1678 prop = v8_str("dont_delete");
1679 context->Global()->Set(prop, v8_num(13), v8::DontDelete);
1680 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value());
1681 Script::Compile(v8_str("delete dont_delete"))->Run();
1682 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value());
1683}
1684
1685
1686THREADED_TEST(Array) {
1687 v8::HandleScope scope;
1688 LocalContext context;
1689 Local<v8::Array> array = v8::Array::New();
1690 CHECK_EQ(0, array->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001691 CHECK(array->Get(0)->IsUndefined());
Steve Blocka7e24c12009-10-30 11:49:00 +00001692 CHECK(!array->Has(0));
Steve Block6ded16b2010-05-10 14:33:55 +01001693 CHECK(array->Get(100)->IsUndefined());
Steve Blocka7e24c12009-10-30 11:49:00 +00001694 CHECK(!array->Has(100));
Steve Block6ded16b2010-05-10 14:33:55 +01001695 array->Set(2, v8_num(7));
Steve Blocka7e24c12009-10-30 11:49:00 +00001696 CHECK_EQ(3, array->Length());
1697 CHECK(!array->Has(0));
1698 CHECK(!array->Has(1));
1699 CHECK(array->Has(2));
Steve Block6ded16b2010-05-10 14:33:55 +01001700 CHECK_EQ(7, array->Get(2)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001701 Local<Value> obj = Script::Compile(v8_str("[1, 2, 3]"))->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01001702 Local<v8::Array> arr = obj.As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001703 CHECK_EQ(3, arr->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001704 CHECK_EQ(1, arr->Get(0)->Int32Value());
1705 CHECK_EQ(2, arr->Get(1)->Int32Value());
1706 CHECK_EQ(3, arr->Get(2)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001707}
1708
1709
1710v8::Handle<Value> HandleF(const v8::Arguments& args) {
1711 v8::HandleScope scope;
1712 ApiTestFuzzer::Fuzz();
1713 Local<v8::Array> result = v8::Array::New(args.Length());
1714 for (int i = 0; i < args.Length(); i++)
Steve Block6ded16b2010-05-10 14:33:55 +01001715 result->Set(i, args[i]);
Steve Blocka7e24c12009-10-30 11:49:00 +00001716 return scope.Close(result);
1717}
1718
1719
1720THREADED_TEST(Vector) {
1721 v8::HandleScope scope;
1722 Local<ObjectTemplate> global = ObjectTemplate::New();
1723 global->Set(v8_str("f"), v8::FunctionTemplate::New(HandleF));
1724 LocalContext context(0, global);
1725
1726 const char* fun = "f()";
Steve Block6ded16b2010-05-10 14:33:55 +01001727 Local<v8::Array> a0 = CompileRun(fun).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001728 CHECK_EQ(0, a0->Length());
1729
1730 const char* fun2 = "f(11)";
Steve Block6ded16b2010-05-10 14:33:55 +01001731 Local<v8::Array> a1 = CompileRun(fun2).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001732 CHECK_EQ(1, a1->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001733 CHECK_EQ(11, a1->Get(0)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001734
1735 const char* fun3 = "f(12, 13)";
Steve Block6ded16b2010-05-10 14:33:55 +01001736 Local<v8::Array> a2 = CompileRun(fun3).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001737 CHECK_EQ(2, a2->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001738 CHECK_EQ(12, a2->Get(0)->Int32Value());
1739 CHECK_EQ(13, a2->Get(1)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001740
1741 const char* fun4 = "f(14, 15, 16)";
Steve Block6ded16b2010-05-10 14:33:55 +01001742 Local<v8::Array> a3 = CompileRun(fun4).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001743 CHECK_EQ(3, a3->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001744 CHECK_EQ(14, a3->Get(0)->Int32Value());
1745 CHECK_EQ(15, a3->Get(1)->Int32Value());
1746 CHECK_EQ(16, a3->Get(2)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001747
1748 const char* fun5 = "f(17, 18, 19, 20)";
Steve Block6ded16b2010-05-10 14:33:55 +01001749 Local<v8::Array> a4 = CompileRun(fun5).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001750 CHECK_EQ(4, a4->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001751 CHECK_EQ(17, a4->Get(0)->Int32Value());
1752 CHECK_EQ(18, a4->Get(1)->Int32Value());
1753 CHECK_EQ(19, a4->Get(2)->Int32Value());
1754 CHECK_EQ(20, a4->Get(3)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001755}
1756
1757
1758THREADED_TEST(FunctionCall) {
1759 v8::HandleScope scope;
1760 LocalContext context;
1761 CompileRun(
1762 "function Foo() {"
1763 " var result = [];"
1764 " for (var i = 0; i < arguments.length; i++) {"
1765 " result.push(arguments[i]);"
1766 " }"
1767 " return result;"
1768 "}");
1769 Local<Function> Foo =
1770 Local<Function>::Cast(context->Global()->Get(v8_str("Foo")));
1771
1772 v8::Handle<Value>* args0 = NULL;
1773 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->Call(Foo, 0, args0));
1774 CHECK_EQ(0, a0->Length());
1775
1776 v8::Handle<Value> args1[] = { v8_num(1.1) };
1777 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->Call(Foo, 1, args1));
1778 CHECK_EQ(1, a1->Length());
1779 CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue());
1780
1781 v8::Handle<Value> args2[] = { v8_num(2.2),
1782 v8_num(3.3) };
1783 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->Call(Foo, 2, args2));
1784 CHECK_EQ(2, a2->Length());
1785 CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue());
1786 CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue());
1787
1788 v8::Handle<Value> args3[] = { v8_num(4.4),
1789 v8_num(5.5),
1790 v8_num(6.6) };
1791 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->Call(Foo, 3, args3));
1792 CHECK_EQ(3, a3->Length());
1793 CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue());
1794 CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue());
1795 CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue());
1796
1797 v8::Handle<Value> args4[] = { v8_num(7.7),
1798 v8_num(8.8),
1799 v8_num(9.9),
1800 v8_num(10.11) };
1801 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->Call(Foo, 4, args4));
1802 CHECK_EQ(4, a4->Length());
1803 CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue());
1804 CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue());
1805 CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue());
1806 CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue());
1807}
1808
1809
1810static const char* js_code_causing_out_of_memory =
1811 "var a = new Array(); while(true) a.push(a);";
1812
1813
1814// These tests run for a long time and prevent us from running tests
1815// that come after them so they cannot run in parallel.
1816TEST(OutOfMemory) {
1817 // It's not possible to read a snapshot into a heap with different dimensions.
1818 if (v8::internal::Snapshot::IsEnabled()) return;
1819 // Set heap limits.
1820 static const int K = 1024;
1821 v8::ResourceConstraints constraints;
1822 constraints.set_max_young_space_size(256 * K);
1823 constraints.set_max_old_space_size(4 * K * K);
1824 v8::SetResourceConstraints(&constraints);
1825
1826 // Execute a script that causes out of memory.
1827 v8::HandleScope scope;
1828 LocalContext context;
1829 v8::V8::IgnoreOutOfMemoryException();
1830 Local<Script> script =
1831 Script::Compile(String::New(js_code_causing_out_of_memory));
1832 Local<Value> result = script->Run();
1833
1834 // Check for out of memory state.
1835 CHECK(result.IsEmpty());
1836 CHECK(context->HasOutOfMemoryException());
1837}
1838
1839
1840v8::Handle<Value> ProvokeOutOfMemory(const v8::Arguments& args) {
1841 ApiTestFuzzer::Fuzz();
1842
1843 v8::HandleScope scope;
1844 LocalContext context;
1845 Local<Script> script =
1846 Script::Compile(String::New(js_code_causing_out_of_memory));
1847 Local<Value> result = script->Run();
1848
1849 // Check for out of memory state.
1850 CHECK(result.IsEmpty());
1851 CHECK(context->HasOutOfMemoryException());
1852
1853 return result;
1854}
1855
1856
1857TEST(OutOfMemoryNested) {
1858 // It's not possible to read a snapshot into a heap with different dimensions.
1859 if (v8::internal::Snapshot::IsEnabled()) return;
1860 // Set heap limits.
1861 static const int K = 1024;
1862 v8::ResourceConstraints constraints;
1863 constraints.set_max_young_space_size(256 * K);
1864 constraints.set_max_old_space_size(4 * K * K);
1865 v8::SetResourceConstraints(&constraints);
1866
1867 v8::HandleScope scope;
1868 Local<ObjectTemplate> templ = ObjectTemplate::New();
1869 templ->Set(v8_str("ProvokeOutOfMemory"),
1870 v8::FunctionTemplate::New(ProvokeOutOfMemory));
1871 LocalContext context(0, templ);
1872 v8::V8::IgnoreOutOfMemoryException();
1873 Local<Value> result = CompileRun(
1874 "var thrown = false;"
1875 "try {"
1876 " ProvokeOutOfMemory();"
1877 "} catch (e) {"
1878 " thrown = true;"
1879 "}");
1880 // Check for out of memory state.
1881 CHECK(result.IsEmpty());
1882 CHECK(context->HasOutOfMemoryException());
1883}
1884
1885
1886TEST(HugeConsStringOutOfMemory) {
1887 // It's not possible to read a snapshot into a heap with different dimensions.
1888 if (v8::internal::Snapshot::IsEnabled()) return;
1889 v8::HandleScope scope;
1890 LocalContext context;
1891 // Set heap limits.
1892 static const int K = 1024;
1893 v8::ResourceConstraints constraints;
1894 constraints.set_max_young_space_size(256 * K);
1895 constraints.set_max_old_space_size(2 * K * K);
1896 v8::SetResourceConstraints(&constraints);
1897
1898 // Execute a script that causes out of memory.
1899 v8::V8::IgnoreOutOfMemoryException();
1900
1901 // Build huge string. This should fail with out of memory exception.
1902 Local<Value> result = CompileRun(
1903 "var str = Array.prototype.join.call({length: 513}, \"A\").toUpperCase();"
Steve Block3ce2e202009-11-05 08:53:23 +00001904 "for (var i = 0; i < 22; i++) { str = str + str; }");
Steve Blocka7e24c12009-10-30 11:49:00 +00001905
1906 // Check for out of memory state.
1907 CHECK(result.IsEmpty());
1908 CHECK(context->HasOutOfMemoryException());
1909}
1910
1911
1912THREADED_TEST(ConstructCall) {
1913 v8::HandleScope scope;
1914 LocalContext context;
1915 CompileRun(
1916 "function Foo() {"
1917 " var result = [];"
1918 " for (var i = 0; i < arguments.length; i++) {"
1919 " result.push(arguments[i]);"
1920 " }"
1921 " return result;"
1922 "}");
1923 Local<Function> Foo =
1924 Local<Function>::Cast(context->Global()->Get(v8_str("Foo")));
1925
1926 v8::Handle<Value>* args0 = NULL;
1927 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->NewInstance(0, args0));
1928 CHECK_EQ(0, a0->Length());
1929
1930 v8::Handle<Value> args1[] = { v8_num(1.1) };
1931 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->NewInstance(1, args1));
1932 CHECK_EQ(1, a1->Length());
1933 CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue());
1934
1935 v8::Handle<Value> args2[] = { v8_num(2.2),
1936 v8_num(3.3) };
1937 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->NewInstance(2, args2));
1938 CHECK_EQ(2, a2->Length());
1939 CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue());
1940 CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue());
1941
1942 v8::Handle<Value> args3[] = { v8_num(4.4),
1943 v8_num(5.5),
1944 v8_num(6.6) };
1945 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->NewInstance(3, args3));
1946 CHECK_EQ(3, a3->Length());
1947 CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue());
1948 CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue());
1949 CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue());
1950
1951 v8::Handle<Value> args4[] = { v8_num(7.7),
1952 v8_num(8.8),
1953 v8_num(9.9),
1954 v8_num(10.11) };
1955 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->NewInstance(4, args4));
1956 CHECK_EQ(4, a4->Length());
1957 CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue());
1958 CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue());
1959 CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue());
1960 CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue());
1961}
1962
1963
1964static void CheckUncle(v8::TryCatch* try_catch) {
1965 CHECK(try_catch->HasCaught());
1966 String::AsciiValue str_value(try_catch->Exception());
1967 CHECK_EQ(*str_value, "uncle?");
1968 try_catch->Reset();
1969}
1970
1971
Steve Block6ded16b2010-05-10 14:33:55 +01001972THREADED_TEST(ConversionNumber) {
1973 v8::HandleScope scope;
1974 LocalContext env;
1975 // Very large number.
1976 CompileRun("var obj = Math.pow(2,32) * 1237;");
1977 Local<Value> obj = env->Global()->Get(v8_str("obj"));
1978 CHECK_EQ(5312874545152.0, obj->ToNumber()->Value());
1979 CHECK_EQ(0, obj->ToInt32()->Value());
1980 CHECK(0u == obj->ToUint32()->Value()); // NOLINT - no CHECK_EQ for unsigned.
1981 // Large number.
1982 CompileRun("var obj = -1234567890123;");
1983 obj = env->Global()->Get(v8_str("obj"));
1984 CHECK_EQ(-1234567890123.0, obj->ToNumber()->Value());
1985 CHECK_EQ(-1912276171, obj->ToInt32()->Value());
1986 CHECK(2382691125u == obj->ToUint32()->Value()); // NOLINT
1987 // Small positive integer.
1988 CompileRun("var obj = 42;");
1989 obj = env->Global()->Get(v8_str("obj"));
1990 CHECK_EQ(42.0, obj->ToNumber()->Value());
1991 CHECK_EQ(42, obj->ToInt32()->Value());
1992 CHECK(42u == obj->ToUint32()->Value()); // NOLINT
1993 // Negative integer.
1994 CompileRun("var obj = -37;");
1995 obj = env->Global()->Get(v8_str("obj"));
1996 CHECK_EQ(-37.0, obj->ToNumber()->Value());
1997 CHECK_EQ(-37, obj->ToInt32()->Value());
1998 CHECK(4294967259u == obj->ToUint32()->Value()); // NOLINT
1999 // Positive non-int32 integer.
2000 CompileRun("var obj = 0x81234567;");
2001 obj = env->Global()->Get(v8_str("obj"));
2002 CHECK_EQ(2166572391.0, obj->ToNumber()->Value());
2003 CHECK_EQ(-2128394905, obj->ToInt32()->Value());
2004 CHECK(2166572391u == obj->ToUint32()->Value()); // NOLINT
2005 // Fraction.
2006 CompileRun("var obj = 42.3;");
2007 obj = env->Global()->Get(v8_str("obj"));
2008 CHECK_EQ(42.3, obj->ToNumber()->Value());
2009 CHECK_EQ(42, obj->ToInt32()->Value());
2010 CHECK(42u == obj->ToUint32()->Value()); // NOLINT
2011 // Large negative fraction.
2012 CompileRun("var obj = -5726623061.75;");
2013 obj = env->Global()->Get(v8_str("obj"));
2014 CHECK_EQ(-5726623061.75, obj->ToNumber()->Value());
2015 CHECK_EQ(-1431655765, obj->ToInt32()->Value());
2016 CHECK(2863311531u == obj->ToUint32()->Value()); // NOLINT
2017}
2018
2019
2020THREADED_TEST(isNumberType) {
2021 v8::HandleScope scope;
2022 LocalContext env;
2023 // Very large number.
2024 CompileRun("var obj = Math.pow(2,32) * 1237;");
2025 Local<Value> obj = env->Global()->Get(v8_str("obj"));
2026 CHECK(!obj->IsInt32());
2027 CHECK(!obj->IsUint32());
2028 // Large negative number.
2029 CompileRun("var obj = -1234567890123;");
2030 obj = env->Global()->Get(v8_str("obj"));
2031 CHECK(!obj->IsInt32());
2032 CHECK(!obj->IsUint32());
2033 // Small positive integer.
2034 CompileRun("var obj = 42;");
2035 obj = env->Global()->Get(v8_str("obj"));
2036 CHECK(obj->IsInt32());
2037 CHECK(obj->IsUint32());
2038 // Negative integer.
2039 CompileRun("var obj = -37;");
2040 obj = env->Global()->Get(v8_str("obj"));
2041 CHECK(obj->IsInt32());
2042 CHECK(!obj->IsUint32());
2043 // Positive non-int32 integer.
2044 CompileRun("var obj = 0x81234567;");
2045 obj = env->Global()->Get(v8_str("obj"));
2046 CHECK(!obj->IsInt32());
2047 CHECK(obj->IsUint32());
2048 // Fraction.
2049 CompileRun("var obj = 42.3;");
2050 obj = env->Global()->Get(v8_str("obj"));
2051 CHECK(!obj->IsInt32());
2052 CHECK(!obj->IsUint32());
2053 // Large negative fraction.
2054 CompileRun("var obj = -5726623061.75;");
2055 obj = env->Global()->Get(v8_str("obj"));
2056 CHECK(!obj->IsInt32());
2057 CHECK(!obj->IsUint32());
2058}
2059
2060
Steve Blocka7e24c12009-10-30 11:49:00 +00002061THREADED_TEST(ConversionException) {
2062 v8::HandleScope scope;
2063 LocalContext env;
2064 CompileRun(
2065 "function TestClass() { };"
2066 "TestClass.prototype.toString = function () { throw 'uncle?'; };"
2067 "var obj = new TestClass();");
2068 Local<Value> obj = env->Global()->Get(v8_str("obj"));
2069
2070 v8::TryCatch try_catch;
2071
2072 Local<Value> to_string_result = obj->ToString();
2073 CHECK(to_string_result.IsEmpty());
2074 CheckUncle(&try_catch);
2075
2076 Local<Value> to_number_result = obj->ToNumber();
2077 CHECK(to_number_result.IsEmpty());
2078 CheckUncle(&try_catch);
2079
2080 Local<Value> to_integer_result = obj->ToInteger();
2081 CHECK(to_integer_result.IsEmpty());
2082 CheckUncle(&try_catch);
2083
2084 Local<Value> to_uint32_result = obj->ToUint32();
2085 CHECK(to_uint32_result.IsEmpty());
2086 CheckUncle(&try_catch);
2087
2088 Local<Value> to_int32_result = obj->ToInt32();
2089 CHECK(to_int32_result.IsEmpty());
2090 CheckUncle(&try_catch);
2091
2092 Local<Value> to_object_result = v8::Undefined()->ToObject();
2093 CHECK(to_object_result.IsEmpty());
2094 CHECK(try_catch.HasCaught());
2095 try_catch.Reset();
2096
2097 int32_t int32_value = obj->Int32Value();
2098 CHECK_EQ(0, int32_value);
2099 CheckUncle(&try_catch);
2100
2101 uint32_t uint32_value = obj->Uint32Value();
2102 CHECK_EQ(0, uint32_value);
2103 CheckUncle(&try_catch);
2104
2105 double number_value = obj->NumberValue();
2106 CHECK_NE(0, IsNaN(number_value));
2107 CheckUncle(&try_catch);
2108
2109 int64_t integer_value = obj->IntegerValue();
2110 CHECK_EQ(0.0, static_cast<double>(integer_value));
2111 CheckUncle(&try_catch);
2112}
2113
2114
2115v8::Handle<Value> ThrowFromC(const v8::Arguments& args) {
2116 ApiTestFuzzer::Fuzz();
2117 return v8::ThrowException(v8_str("konto"));
2118}
2119
2120
2121v8::Handle<Value> CCatcher(const v8::Arguments& args) {
2122 if (args.Length() < 1) return v8::Boolean::New(false);
2123 v8::HandleScope scope;
2124 v8::TryCatch try_catch;
2125 Local<Value> result = v8::Script::Compile(args[0]->ToString())->Run();
2126 CHECK(!try_catch.HasCaught() || result.IsEmpty());
2127 return v8::Boolean::New(try_catch.HasCaught());
2128}
2129
2130
2131THREADED_TEST(APICatch) {
2132 v8::HandleScope scope;
2133 Local<ObjectTemplate> templ = ObjectTemplate::New();
2134 templ->Set(v8_str("ThrowFromC"),
2135 v8::FunctionTemplate::New(ThrowFromC));
2136 LocalContext context(0, templ);
2137 CompileRun(
2138 "var thrown = false;"
2139 "try {"
2140 " ThrowFromC();"
2141 "} catch (e) {"
2142 " thrown = true;"
2143 "}");
2144 Local<Value> thrown = context->Global()->Get(v8_str("thrown"));
2145 CHECK(thrown->BooleanValue());
2146}
2147
2148
2149THREADED_TEST(APIThrowTryCatch) {
2150 v8::HandleScope scope;
2151 Local<ObjectTemplate> templ = ObjectTemplate::New();
2152 templ->Set(v8_str("ThrowFromC"),
2153 v8::FunctionTemplate::New(ThrowFromC));
2154 LocalContext context(0, templ);
2155 v8::TryCatch try_catch;
2156 CompileRun("ThrowFromC();");
2157 CHECK(try_catch.HasCaught());
2158}
2159
2160
2161// Test that a try-finally block doesn't shadow a try-catch block
2162// when setting up an external handler.
2163//
2164// BUG(271): Some of the exception propagation does not work on the
2165// ARM simulator because the simulator separates the C++ stack and the
2166// JS stack. This test therefore fails on the simulator. The test is
2167// not threaded to allow the threading tests to run on the simulator.
2168TEST(TryCatchInTryFinally) {
2169 v8::HandleScope scope;
2170 Local<ObjectTemplate> templ = ObjectTemplate::New();
2171 templ->Set(v8_str("CCatcher"),
2172 v8::FunctionTemplate::New(CCatcher));
2173 LocalContext context(0, templ);
2174 Local<Value> result = CompileRun("try {"
2175 " try {"
2176 " CCatcher('throw 7;');"
2177 " } finally {"
2178 " }"
2179 "} catch (e) {"
2180 "}");
2181 CHECK(result->IsTrue());
2182}
2183
2184
2185static void receive_message(v8::Handle<v8::Message> message,
2186 v8::Handle<v8::Value> data) {
2187 message->Get();
2188 message_received = true;
2189}
2190
2191
2192TEST(APIThrowMessage) {
2193 message_received = false;
2194 v8::HandleScope scope;
2195 v8::V8::AddMessageListener(receive_message);
2196 Local<ObjectTemplate> templ = ObjectTemplate::New();
2197 templ->Set(v8_str("ThrowFromC"),
2198 v8::FunctionTemplate::New(ThrowFromC));
2199 LocalContext context(0, templ);
2200 CompileRun("ThrowFromC();");
2201 CHECK(message_received);
2202 v8::V8::RemoveMessageListeners(check_message);
2203}
2204
2205
2206TEST(APIThrowMessageAndVerboseTryCatch) {
2207 message_received = false;
2208 v8::HandleScope scope;
2209 v8::V8::AddMessageListener(receive_message);
2210 Local<ObjectTemplate> templ = ObjectTemplate::New();
2211 templ->Set(v8_str("ThrowFromC"),
2212 v8::FunctionTemplate::New(ThrowFromC));
2213 LocalContext context(0, templ);
2214 v8::TryCatch try_catch;
2215 try_catch.SetVerbose(true);
2216 Local<Value> result = CompileRun("ThrowFromC();");
2217 CHECK(try_catch.HasCaught());
2218 CHECK(result.IsEmpty());
2219 CHECK(message_received);
2220 v8::V8::RemoveMessageListeners(check_message);
2221}
2222
2223
2224THREADED_TEST(ExternalScriptException) {
2225 v8::HandleScope scope;
2226 Local<ObjectTemplate> templ = ObjectTemplate::New();
2227 templ->Set(v8_str("ThrowFromC"),
2228 v8::FunctionTemplate::New(ThrowFromC));
2229 LocalContext context(0, templ);
2230
2231 v8::TryCatch try_catch;
2232 Local<Script> script
2233 = Script::Compile(v8_str("ThrowFromC(); throw 'panama';"));
2234 Local<Value> result = script->Run();
2235 CHECK(result.IsEmpty());
2236 CHECK(try_catch.HasCaught());
2237 String::AsciiValue exception_value(try_catch.Exception());
2238 CHECK_EQ("konto", *exception_value);
2239}
2240
2241
2242
2243v8::Handle<Value> CThrowCountDown(const v8::Arguments& args) {
2244 ApiTestFuzzer::Fuzz();
2245 CHECK_EQ(4, args.Length());
2246 int count = args[0]->Int32Value();
2247 int cInterval = args[2]->Int32Value();
2248 if (count == 0) {
2249 return v8::ThrowException(v8_str("FromC"));
2250 } else {
2251 Local<v8::Object> global = Context::GetCurrent()->Global();
2252 Local<Value> fun = global->Get(v8_str("JSThrowCountDown"));
2253 v8::Handle<Value> argv[] = { v8_num(count - 1),
2254 args[1],
2255 args[2],
2256 args[3] };
2257 if (count % cInterval == 0) {
2258 v8::TryCatch try_catch;
Steve Block6ded16b2010-05-10 14:33:55 +01002259 Local<Value> result = fun.As<Function>()->Call(global, 4, argv);
Steve Blocka7e24c12009-10-30 11:49:00 +00002260 int expected = args[3]->Int32Value();
2261 if (try_catch.HasCaught()) {
2262 CHECK_EQ(expected, count);
2263 CHECK(result.IsEmpty());
2264 CHECK(!i::Top::has_scheduled_exception());
2265 } else {
2266 CHECK_NE(expected, count);
2267 }
2268 return result;
2269 } else {
Steve Block6ded16b2010-05-10 14:33:55 +01002270 return fun.As<Function>()->Call(global, 4, argv);
Steve Blocka7e24c12009-10-30 11:49:00 +00002271 }
2272 }
2273}
2274
2275
2276v8::Handle<Value> JSCheck(const v8::Arguments& args) {
2277 ApiTestFuzzer::Fuzz();
2278 CHECK_EQ(3, args.Length());
2279 bool equality = args[0]->BooleanValue();
2280 int count = args[1]->Int32Value();
2281 int expected = args[2]->Int32Value();
2282 if (equality) {
2283 CHECK_EQ(count, expected);
2284 } else {
2285 CHECK_NE(count, expected);
2286 }
2287 return v8::Undefined();
2288}
2289
2290
2291THREADED_TEST(EvalInTryFinally) {
2292 v8::HandleScope scope;
2293 LocalContext context;
2294 v8::TryCatch try_catch;
2295 CompileRun("(function() {"
2296 " try {"
2297 " eval('asldkf (*&^&*^');"
2298 " } finally {"
2299 " return;"
2300 " }"
2301 "})()");
2302 CHECK(!try_catch.HasCaught());
2303}
2304
2305
2306// This test works by making a stack of alternating JavaScript and C
2307// activations. These activations set up exception handlers with regular
2308// intervals, one interval for C activations and another for JavaScript
2309// activations. When enough activations have been created an exception is
2310// thrown and we check that the right activation catches the exception and that
2311// no other activations do. The right activation is always the topmost one with
2312// a handler, regardless of whether it is in JavaScript or C.
2313//
2314// The notation used to describe a test case looks like this:
2315//
2316// *JS[4] *C[3] @JS[2] C[1] JS[0]
2317//
2318// Each entry is an activation, either JS or C. The index is the count at that
2319// level. Stars identify activations with exception handlers, the @ identifies
2320// the exception handler that should catch the exception.
2321//
2322// BUG(271): Some of the exception propagation does not work on the
2323// ARM simulator because the simulator separates the C++ stack and the
2324// JS stack. This test therefore fails on the simulator. The test is
2325// not threaded to allow the threading tests to run on the simulator.
2326TEST(ExceptionOrder) {
2327 v8::HandleScope scope;
2328 Local<ObjectTemplate> templ = ObjectTemplate::New();
2329 templ->Set(v8_str("check"), v8::FunctionTemplate::New(JSCheck));
2330 templ->Set(v8_str("CThrowCountDown"),
2331 v8::FunctionTemplate::New(CThrowCountDown));
2332 LocalContext context(0, templ);
2333 CompileRun(
2334 "function JSThrowCountDown(count, jsInterval, cInterval, expected) {"
2335 " if (count == 0) throw 'FromJS';"
2336 " if (count % jsInterval == 0) {"
2337 " try {"
2338 " var value = CThrowCountDown(count - 1,"
2339 " jsInterval,"
2340 " cInterval,"
2341 " expected);"
2342 " check(false, count, expected);"
2343 " return value;"
2344 " } catch (e) {"
2345 " check(true, count, expected);"
2346 " }"
2347 " } else {"
2348 " return CThrowCountDown(count - 1, jsInterval, cInterval, expected);"
2349 " }"
2350 "}");
2351 Local<Function> fun =
2352 Local<Function>::Cast(context->Global()->Get(v8_str("JSThrowCountDown")));
2353
2354 const int argc = 4;
2355 // count jsInterval cInterval expected
2356
2357 // *JS[4] *C[3] @JS[2] C[1] JS[0]
2358 v8::Handle<Value> a0[argc] = { v8_num(4), v8_num(2), v8_num(3), v8_num(2) };
2359 fun->Call(fun, argc, a0);
2360
2361 // JS[5] *C[4] JS[3] @C[2] JS[1] C[0]
2362 v8::Handle<Value> a1[argc] = { v8_num(5), v8_num(6), v8_num(1), v8_num(2) };
2363 fun->Call(fun, argc, a1);
2364
2365 // JS[6] @C[5] JS[4] C[3] JS[2] C[1] JS[0]
2366 v8::Handle<Value> a2[argc] = { v8_num(6), v8_num(7), v8_num(5), v8_num(5) };
2367 fun->Call(fun, argc, a2);
2368
2369 // @JS[6] C[5] JS[4] C[3] JS[2] C[1] JS[0]
2370 v8::Handle<Value> a3[argc] = { v8_num(6), v8_num(6), v8_num(7), v8_num(6) };
2371 fun->Call(fun, argc, a3);
2372
2373 // JS[6] *C[5] @JS[4] C[3] JS[2] C[1] JS[0]
2374 v8::Handle<Value> a4[argc] = { v8_num(6), v8_num(4), v8_num(5), v8_num(4) };
2375 fun->Call(fun, argc, a4);
2376
2377 // JS[6] C[5] *JS[4] @C[3] JS[2] C[1] JS[0]
2378 v8::Handle<Value> a5[argc] = { v8_num(6), v8_num(4), v8_num(3), v8_num(3) };
2379 fun->Call(fun, argc, a5);
2380}
2381
2382
2383v8::Handle<Value> ThrowValue(const v8::Arguments& args) {
2384 ApiTestFuzzer::Fuzz();
2385 CHECK_EQ(1, args.Length());
2386 return v8::ThrowException(args[0]);
2387}
2388
2389
2390THREADED_TEST(ThrowValues) {
2391 v8::HandleScope scope;
2392 Local<ObjectTemplate> templ = ObjectTemplate::New();
2393 templ->Set(v8_str("Throw"), v8::FunctionTemplate::New(ThrowValue));
2394 LocalContext context(0, templ);
2395 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun(
2396 "function Run(obj) {"
2397 " try {"
2398 " Throw(obj);"
2399 " } catch (e) {"
2400 " return e;"
2401 " }"
2402 " return 'no exception';"
2403 "}"
2404 "[Run('str'), Run(1), Run(0), Run(null), Run(void 0)];"));
2405 CHECK_EQ(5, result->Length());
2406 CHECK(result->Get(v8::Integer::New(0))->IsString());
2407 CHECK(result->Get(v8::Integer::New(1))->IsNumber());
2408 CHECK_EQ(1, result->Get(v8::Integer::New(1))->Int32Value());
2409 CHECK(result->Get(v8::Integer::New(2))->IsNumber());
2410 CHECK_EQ(0, result->Get(v8::Integer::New(2))->Int32Value());
2411 CHECK(result->Get(v8::Integer::New(3))->IsNull());
2412 CHECK(result->Get(v8::Integer::New(4))->IsUndefined());
2413}
2414
2415
2416THREADED_TEST(CatchZero) {
2417 v8::HandleScope scope;
2418 LocalContext context;
2419 v8::TryCatch try_catch;
2420 CHECK(!try_catch.HasCaught());
2421 Script::Compile(v8_str("throw 10"))->Run();
2422 CHECK(try_catch.HasCaught());
2423 CHECK_EQ(10, try_catch.Exception()->Int32Value());
2424 try_catch.Reset();
2425 CHECK(!try_catch.HasCaught());
2426 Script::Compile(v8_str("throw 0"))->Run();
2427 CHECK(try_catch.HasCaught());
2428 CHECK_EQ(0, try_catch.Exception()->Int32Value());
2429}
2430
2431
2432THREADED_TEST(CatchExceptionFromWith) {
2433 v8::HandleScope scope;
2434 LocalContext context;
2435 v8::TryCatch try_catch;
2436 CHECK(!try_catch.HasCaught());
2437 Script::Compile(v8_str("var o = {}; with (o) { throw 42; }"))->Run();
2438 CHECK(try_catch.HasCaught());
2439}
2440
2441
2442THREADED_TEST(Equality) {
2443 v8::HandleScope scope;
2444 LocalContext context;
2445 // Check that equality works at all before relying on CHECK_EQ
2446 CHECK(v8_str("a")->Equals(v8_str("a")));
2447 CHECK(!v8_str("a")->Equals(v8_str("b")));
2448
2449 CHECK_EQ(v8_str("a"), v8_str("a"));
2450 CHECK_NE(v8_str("a"), v8_str("b"));
2451 CHECK_EQ(v8_num(1), v8_num(1));
2452 CHECK_EQ(v8_num(1.00), v8_num(1));
2453 CHECK_NE(v8_num(1), v8_num(2));
2454
2455 // Assume String is not symbol.
2456 CHECK(v8_str("a")->StrictEquals(v8_str("a")));
2457 CHECK(!v8_str("a")->StrictEquals(v8_str("b")));
2458 CHECK(!v8_str("5")->StrictEquals(v8_num(5)));
2459 CHECK(v8_num(1)->StrictEquals(v8_num(1)));
2460 CHECK(!v8_num(1)->StrictEquals(v8_num(2)));
2461 CHECK(v8_num(0)->StrictEquals(v8_num(-0)));
2462 Local<Value> not_a_number = v8_num(i::OS::nan_value());
2463 CHECK(!not_a_number->StrictEquals(not_a_number));
2464 CHECK(v8::False()->StrictEquals(v8::False()));
2465 CHECK(!v8::False()->StrictEquals(v8::Undefined()));
2466
2467 v8::Handle<v8::Object> obj = v8::Object::New();
2468 v8::Persistent<v8::Object> alias = v8::Persistent<v8::Object>::New(obj);
2469 CHECK(alias->StrictEquals(obj));
2470 alias.Dispose();
2471}
2472
2473
2474THREADED_TEST(MultiRun) {
2475 v8::HandleScope scope;
2476 LocalContext context;
2477 Local<Script> script = Script::Compile(v8_str("x"));
2478 for (int i = 0; i < 10; i++)
2479 script->Run();
2480}
2481
2482
2483static v8::Handle<Value> GetXValue(Local<String> name,
2484 const AccessorInfo& info) {
2485 ApiTestFuzzer::Fuzz();
2486 CHECK_EQ(info.Data(), v8_str("donut"));
2487 CHECK_EQ(name, v8_str("x"));
2488 return name;
2489}
2490
2491
2492THREADED_TEST(SimplePropertyRead) {
2493 v8::HandleScope scope;
2494 Local<ObjectTemplate> templ = ObjectTemplate::New();
2495 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"));
2496 LocalContext context;
2497 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2498 Local<Script> script = Script::Compile(v8_str("obj.x"));
2499 for (int i = 0; i < 10; i++) {
2500 Local<Value> result = script->Run();
2501 CHECK_EQ(result, v8_str("x"));
2502 }
2503}
2504
Andrei Popescu31002712010-02-23 13:46:05 +00002505THREADED_TEST(DefinePropertyOnAPIAccessor) {
2506 v8::HandleScope scope;
2507 Local<ObjectTemplate> templ = ObjectTemplate::New();
2508 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"));
2509 LocalContext context;
2510 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2511
2512 // Uses getOwnPropertyDescriptor to check the configurable status
2513 Local<Script> script_desc
Leon Clarkef7060e22010-06-03 12:02:55 +01002514 = Script::Compile(v8_str("var prop = Object.getOwnPropertyDescriptor( "
Andrei Popescu31002712010-02-23 13:46:05 +00002515 "obj, 'x');"
2516 "prop.configurable;"));
2517 Local<Value> result = script_desc->Run();
2518 CHECK_EQ(result->BooleanValue(), true);
2519
2520 // Redefine get - but still configurable
2521 Local<Script> script_define
2522 = Script::Compile(v8_str("var desc = { get: function(){return 42; },"
2523 " configurable: true };"
2524 "Object.defineProperty(obj, 'x', desc);"
2525 "obj.x"));
2526 result = script_define->Run();
2527 CHECK_EQ(result, v8_num(42));
2528
2529 // Check that the accessor is still configurable
2530 result = script_desc->Run();
2531 CHECK_EQ(result->BooleanValue(), true);
2532
2533 // Redefine to a non-configurable
2534 script_define
2535 = Script::Compile(v8_str("var desc = { get: function(){return 43; },"
2536 " configurable: false };"
2537 "Object.defineProperty(obj, 'x', desc);"
2538 "obj.x"));
2539 result = script_define->Run();
2540 CHECK_EQ(result, v8_num(43));
2541 result = script_desc->Run();
2542 CHECK_EQ(result->BooleanValue(), false);
2543
2544 // Make sure that it is not possible to redefine again
2545 v8::TryCatch try_catch;
2546 result = script_define->Run();
2547 CHECK(try_catch.HasCaught());
2548 String::AsciiValue exception_value(try_catch.Exception());
2549 CHECK_EQ(*exception_value,
2550 "TypeError: Cannot redefine property: defineProperty");
2551}
2552
2553THREADED_TEST(DefinePropertyOnDefineGetterSetter) {
2554 v8::HandleScope scope;
2555 Local<ObjectTemplate> templ = ObjectTemplate::New();
2556 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"));
2557 LocalContext context;
2558 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2559
2560 Local<Script> script_desc = Script::Compile(v8_str("var prop ="
2561 "Object.getOwnPropertyDescriptor( "
2562 "obj, 'x');"
2563 "prop.configurable;"));
2564 Local<Value> result = script_desc->Run();
2565 CHECK_EQ(result->BooleanValue(), true);
2566
2567 Local<Script> script_define =
2568 Script::Compile(v8_str("var desc = {get: function(){return 42; },"
2569 " configurable: true };"
2570 "Object.defineProperty(obj, 'x', desc);"
2571 "obj.x"));
2572 result = script_define->Run();
2573 CHECK_EQ(result, v8_num(42));
2574
2575
2576 result = script_desc->Run();
2577 CHECK_EQ(result->BooleanValue(), true);
2578
2579
2580 script_define =
2581 Script::Compile(v8_str("var desc = {get: function(){return 43; },"
2582 " configurable: false };"
2583 "Object.defineProperty(obj, 'x', desc);"
2584 "obj.x"));
2585 result = script_define->Run();
2586 CHECK_EQ(result, v8_num(43));
2587 result = script_desc->Run();
2588
2589 CHECK_EQ(result->BooleanValue(), false);
2590
2591 v8::TryCatch try_catch;
2592 result = script_define->Run();
2593 CHECK(try_catch.HasCaught());
2594 String::AsciiValue exception_value(try_catch.Exception());
2595 CHECK_EQ(*exception_value,
2596 "TypeError: Cannot redefine property: defineProperty");
2597}
2598
2599
Leon Clarkef7060e22010-06-03 12:02:55 +01002600static v8::Handle<v8::Object> GetGlobalProperty(LocalContext* context,
2601 char const* name) {
2602 return v8::Handle<v8::Object>::Cast((*context)->Global()->Get(v8_str(name)));
2603}
Andrei Popescu31002712010-02-23 13:46:05 +00002604
2605
Leon Clarkef7060e22010-06-03 12:02:55 +01002606THREADED_TEST(DefineAPIAccessorOnObject) {
2607 v8::HandleScope scope;
2608 Local<ObjectTemplate> templ = ObjectTemplate::New();
2609 LocalContext context;
2610
2611 context->Global()->Set(v8_str("obj1"), templ->NewInstance());
2612 CompileRun("var obj2 = {};");
2613
2614 CHECK(CompileRun("obj1.x")->IsUndefined());
2615 CHECK(CompileRun("obj2.x")->IsUndefined());
2616
2617 CHECK(GetGlobalProperty(&context, "obj1")->
2618 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2619
2620 ExpectString("obj1.x", "x");
2621 CHECK(CompileRun("obj2.x")->IsUndefined());
2622
2623 CHECK(GetGlobalProperty(&context, "obj2")->
2624 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2625
2626 ExpectString("obj1.x", "x");
2627 ExpectString("obj2.x", "x");
2628
2629 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2630 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2631
2632 CompileRun("Object.defineProperty(obj1, 'x',"
2633 "{ get: function() { return 'y'; }, configurable: true })");
2634
2635 ExpectString("obj1.x", "y");
2636 ExpectString("obj2.x", "x");
2637
2638 CompileRun("Object.defineProperty(obj2, 'x',"
2639 "{ get: function() { return 'y'; }, configurable: true })");
2640
2641 ExpectString("obj1.x", "y");
2642 ExpectString("obj2.x", "y");
2643
2644 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2645 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2646
2647 CHECK(GetGlobalProperty(&context, "obj1")->
2648 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2649 CHECK(GetGlobalProperty(&context, "obj2")->
2650 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2651
2652 ExpectString("obj1.x", "x");
2653 ExpectString("obj2.x", "x");
2654
2655 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2656 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2657
2658 // Define getters/setters, but now make them not configurable.
2659 CompileRun("Object.defineProperty(obj1, 'x',"
2660 "{ get: function() { return 'z'; }, configurable: false })");
2661 CompileRun("Object.defineProperty(obj2, 'x',"
2662 "{ get: function() { return 'z'; }, configurable: false })");
2663
2664 ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2665 ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2666
2667 ExpectString("obj1.x", "z");
2668 ExpectString("obj2.x", "z");
2669
2670 CHECK(!GetGlobalProperty(&context, "obj1")->
2671 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2672 CHECK(!GetGlobalProperty(&context, "obj2")->
2673 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2674
2675 ExpectString("obj1.x", "z");
2676 ExpectString("obj2.x", "z");
2677}
2678
2679
2680THREADED_TEST(DontDeleteAPIAccessorsCannotBeOverriden) {
2681 v8::HandleScope scope;
2682 Local<ObjectTemplate> templ = ObjectTemplate::New();
2683 LocalContext context;
2684
2685 context->Global()->Set(v8_str("obj1"), templ->NewInstance());
2686 CompileRun("var obj2 = {};");
2687
2688 CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor(
2689 v8_str("x"),
2690 GetXValue, NULL,
2691 v8_str("donut"), v8::DEFAULT, v8::DontDelete));
2692 CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor(
2693 v8_str("x"),
2694 GetXValue, NULL,
2695 v8_str("donut"), v8::DEFAULT, v8::DontDelete));
2696
2697 ExpectString("obj1.x", "x");
2698 ExpectString("obj2.x", "x");
2699
2700 ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2701 ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2702
2703 CHECK(!GetGlobalProperty(&context, "obj1")->
2704 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2705 CHECK(!GetGlobalProperty(&context, "obj2")->
2706 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2707
2708 {
2709 v8::TryCatch try_catch;
2710 CompileRun("Object.defineProperty(obj1, 'x',"
2711 "{get: function() { return 'func'; }})");
2712 CHECK(try_catch.HasCaught());
2713 String::AsciiValue exception_value(try_catch.Exception());
2714 CHECK_EQ(*exception_value,
2715 "TypeError: Cannot redefine property: defineProperty");
2716 }
2717 {
2718 v8::TryCatch try_catch;
2719 CompileRun("Object.defineProperty(obj2, 'x',"
2720 "{get: function() { return 'func'; }})");
2721 CHECK(try_catch.HasCaught());
2722 String::AsciiValue exception_value(try_catch.Exception());
2723 CHECK_EQ(*exception_value,
2724 "TypeError: Cannot redefine property: defineProperty");
2725 }
2726}
2727
2728
2729static v8::Handle<Value> Get239Value(Local<String> name,
2730 const AccessorInfo& info) {
2731 ApiTestFuzzer::Fuzz();
2732 CHECK_EQ(info.Data(), v8_str("donut"));
2733 CHECK_EQ(name, v8_str("239"));
2734 return name;
2735}
2736
2737
2738THREADED_TEST(ElementAPIAccessor) {
2739 v8::HandleScope scope;
2740 Local<ObjectTemplate> templ = ObjectTemplate::New();
2741 LocalContext context;
2742
2743 context->Global()->Set(v8_str("obj1"), templ->NewInstance());
2744 CompileRun("var obj2 = {};");
2745
2746 CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor(
2747 v8_str("239"),
2748 Get239Value, NULL,
2749 v8_str("donut")));
2750 CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor(
2751 v8_str("239"),
2752 Get239Value, NULL,
2753 v8_str("donut")));
2754
2755 ExpectString("obj1[239]", "239");
2756 ExpectString("obj2[239]", "239");
2757 ExpectString("obj1['239']", "239");
2758 ExpectString("obj2['239']", "239");
2759}
2760
Steve Blocka7e24c12009-10-30 11:49:00 +00002761
2762v8::Persistent<Value> xValue;
2763
2764
2765static void SetXValue(Local<String> name,
2766 Local<Value> value,
2767 const AccessorInfo& info) {
2768 CHECK_EQ(value, v8_num(4));
2769 CHECK_EQ(info.Data(), v8_str("donut"));
2770 CHECK_EQ(name, v8_str("x"));
2771 CHECK(xValue.IsEmpty());
2772 xValue = v8::Persistent<Value>::New(value);
2773}
2774
2775
2776THREADED_TEST(SimplePropertyWrite) {
2777 v8::HandleScope scope;
2778 Local<ObjectTemplate> templ = ObjectTemplate::New();
2779 templ->SetAccessor(v8_str("x"), GetXValue, SetXValue, v8_str("donut"));
2780 LocalContext context;
2781 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2782 Local<Script> script = Script::Compile(v8_str("obj.x = 4"));
2783 for (int i = 0; i < 10; i++) {
2784 CHECK(xValue.IsEmpty());
2785 script->Run();
2786 CHECK_EQ(v8_num(4), xValue);
2787 xValue.Dispose();
2788 xValue = v8::Persistent<Value>();
2789 }
2790}
2791
2792
2793static v8::Handle<Value> XPropertyGetter(Local<String> property,
2794 const AccessorInfo& info) {
2795 ApiTestFuzzer::Fuzz();
2796 CHECK(info.Data()->IsUndefined());
2797 return property;
2798}
2799
2800
2801THREADED_TEST(NamedInterceptorPropertyRead) {
2802 v8::HandleScope scope;
2803 Local<ObjectTemplate> templ = ObjectTemplate::New();
2804 templ->SetNamedPropertyHandler(XPropertyGetter);
2805 LocalContext context;
2806 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2807 Local<Script> script = Script::Compile(v8_str("obj.x"));
2808 for (int i = 0; i < 10; i++) {
2809 Local<Value> result = script->Run();
2810 CHECK_EQ(result, v8_str("x"));
2811 }
2812}
2813
2814
Steve Block6ded16b2010-05-10 14:33:55 +01002815THREADED_TEST(NamedInterceptorDictionaryIC) {
2816 v8::HandleScope scope;
2817 Local<ObjectTemplate> templ = ObjectTemplate::New();
2818 templ->SetNamedPropertyHandler(XPropertyGetter);
2819 LocalContext context;
2820 // Create an object with a named interceptor.
2821 context->Global()->Set(v8_str("interceptor_obj"), templ->NewInstance());
2822 Local<Script> script = Script::Compile(v8_str("interceptor_obj.x"));
2823 for (int i = 0; i < 10; i++) {
2824 Local<Value> result = script->Run();
2825 CHECK_EQ(result, v8_str("x"));
2826 }
2827 // Create a slow case object and a function accessing a property in
2828 // that slow case object (with dictionary probing in generated
2829 // code). Then force object with a named interceptor into slow-case,
2830 // pass it to the function, and check that the interceptor is called
2831 // instead of accessing the local property.
2832 Local<Value> result =
2833 CompileRun("function get_x(o) { return o.x; };"
2834 "var obj = { x : 42, y : 0 };"
2835 "delete obj.y;"
2836 "for (var i = 0; i < 10; i++) get_x(obj);"
2837 "interceptor_obj.x = 42;"
2838 "interceptor_obj.y = 10;"
2839 "delete interceptor_obj.y;"
2840 "get_x(interceptor_obj)");
2841 CHECK_EQ(result, v8_str("x"));
2842}
2843
2844
Andrei Popescu402d9372010-02-26 13:31:12 +00002845static v8::Handle<Value> SetXOnPrototypeGetter(Local<String> property,
2846 const AccessorInfo& info) {
2847 // Set x on the prototype object and do not handle the get request.
2848 v8::Handle<v8::Value> proto = info.Holder()->GetPrototype();
Steve Block6ded16b2010-05-10 14:33:55 +01002849 proto.As<v8::Object>()->Set(v8_str("x"), v8::Integer::New(23));
Andrei Popescu402d9372010-02-26 13:31:12 +00002850 return v8::Handle<Value>();
2851}
2852
2853
2854// This is a regression test for http://crbug.com/20104. Map
2855// transitions should not interfere with post interceptor lookup.
2856THREADED_TEST(NamedInterceptorMapTransitionRead) {
2857 v8::HandleScope scope;
2858 Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New();
2859 Local<v8::ObjectTemplate> instance_template
2860 = function_template->InstanceTemplate();
2861 instance_template->SetNamedPropertyHandler(SetXOnPrototypeGetter);
2862 LocalContext context;
2863 context->Global()->Set(v8_str("F"), function_template->GetFunction());
2864 // Create an instance of F and introduce a map transition for x.
2865 CompileRun("var o = new F(); o.x = 23;");
2866 // Create an instance of F and invoke the getter. The result should be 23.
2867 Local<Value> result = CompileRun("o = new F(); o.x");
2868 CHECK_EQ(result->Int32Value(), 23);
2869}
2870
2871
Steve Blocka7e24c12009-10-30 11:49:00 +00002872static v8::Handle<Value> IndexedPropertyGetter(uint32_t index,
2873 const AccessorInfo& info) {
2874 ApiTestFuzzer::Fuzz();
2875 if (index == 37) {
2876 return v8::Handle<Value>(v8_num(625));
2877 }
2878 return v8::Handle<Value>();
2879}
2880
2881
2882static v8::Handle<Value> IndexedPropertySetter(uint32_t index,
2883 Local<Value> value,
2884 const AccessorInfo& info) {
2885 ApiTestFuzzer::Fuzz();
2886 if (index == 39) {
2887 return value;
2888 }
2889 return v8::Handle<Value>();
2890}
2891
2892
2893THREADED_TEST(IndexedInterceptorWithIndexedAccessor) {
2894 v8::HandleScope scope;
2895 Local<ObjectTemplate> templ = ObjectTemplate::New();
2896 templ->SetIndexedPropertyHandler(IndexedPropertyGetter,
2897 IndexedPropertySetter);
2898 LocalContext context;
2899 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2900 Local<Script> getter_script = Script::Compile(v8_str(
2901 "obj.__defineGetter__(\"3\", function(){return 5;});obj[3];"));
2902 Local<Script> setter_script = Script::Compile(v8_str(
2903 "obj.__defineSetter__(\"17\", function(val){this.foo = val;});"
2904 "obj[17] = 23;"
2905 "obj.foo;"));
2906 Local<Script> interceptor_setter_script = Script::Compile(v8_str(
2907 "obj.__defineSetter__(\"39\", function(val){this.foo = \"hit\";});"
2908 "obj[39] = 47;"
2909 "obj.foo;")); // This setter should not run, due to the interceptor.
2910 Local<Script> interceptor_getter_script = Script::Compile(v8_str(
2911 "obj[37];"));
2912 Local<Value> result = getter_script->Run();
2913 CHECK_EQ(v8_num(5), result);
2914 result = setter_script->Run();
2915 CHECK_EQ(v8_num(23), result);
2916 result = interceptor_setter_script->Run();
2917 CHECK_EQ(v8_num(23), result);
2918 result = interceptor_getter_script->Run();
2919 CHECK_EQ(v8_num(625), result);
2920}
2921
2922
Leon Clarked91b9f72010-01-27 17:25:45 +00002923static v8::Handle<Value> IdentityIndexedPropertyGetter(
2924 uint32_t index,
2925 const AccessorInfo& info) {
2926 return v8::Integer::New(index);
2927}
2928
2929
2930THREADED_TEST(IndexedInterceptorWithNoSetter) {
2931 v8::HandleScope scope;
2932 Local<ObjectTemplate> templ = ObjectTemplate::New();
2933 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
2934
2935 LocalContext context;
2936 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2937
2938 const char* code =
2939 "try {"
2940 " obj[0] = 239;"
2941 " for (var i = 0; i < 100; i++) {"
2942 " var v = obj[0];"
2943 " if (v != 0) throw 'Wrong value ' + v + ' at iteration ' + i;"
2944 " }"
2945 " 'PASSED'"
2946 "} catch(e) {"
2947 " e"
2948 "}";
2949 ExpectString(code, "PASSED");
2950}
2951
2952
Andrei Popescu402d9372010-02-26 13:31:12 +00002953THREADED_TEST(IndexedInterceptorWithAccessorCheck) {
2954 v8::HandleScope scope;
2955 Local<ObjectTemplate> templ = ObjectTemplate::New();
2956 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
2957
2958 LocalContext context;
2959 Local<v8::Object> obj = templ->NewInstance();
2960 obj->TurnOnAccessCheck();
2961 context->Global()->Set(v8_str("obj"), obj);
2962
2963 const char* code =
2964 "try {"
2965 " for (var i = 0; i < 100; i++) {"
2966 " var v = obj[0];"
2967 " if (v != undefined) throw 'Wrong value ' + v + ' at iteration ' + i;"
2968 " }"
2969 " 'PASSED'"
2970 "} catch(e) {"
2971 " e"
2972 "}";
2973 ExpectString(code, "PASSED");
2974}
2975
2976
2977THREADED_TEST(IndexedInterceptorWithAccessorCheckSwitchedOn) {
2978 i::FLAG_allow_natives_syntax = true;
2979 v8::HandleScope scope;
2980 Local<ObjectTemplate> templ = ObjectTemplate::New();
2981 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
2982
2983 LocalContext context;
2984 Local<v8::Object> obj = templ->NewInstance();
2985 context->Global()->Set(v8_str("obj"), obj);
2986
2987 const char* code =
2988 "try {"
2989 " for (var i = 0; i < 100; i++) {"
2990 " var expected = i;"
2991 " if (i == 5) {"
2992 " %EnableAccessChecks(obj);"
2993 " expected = undefined;"
2994 " }"
2995 " var v = obj[i];"
2996 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
2997 " if (i == 5) %DisableAccessChecks(obj);"
2998 " }"
2999 " 'PASSED'"
3000 "} catch(e) {"
3001 " e"
3002 "}";
3003 ExpectString(code, "PASSED");
3004}
3005
3006
3007THREADED_TEST(IndexedInterceptorWithDifferentIndices) {
3008 v8::HandleScope scope;
3009 Local<ObjectTemplate> templ = ObjectTemplate::New();
3010 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3011
3012 LocalContext context;
3013 Local<v8::Object> obj = templ->NewInstance();
3014 context->Global()->Set(v8_str("obj"), obj);
3015
3016 const char* code =
3017 "try {"
3018 " for (var i = 0; i < 100; i++) {"
3019 " var v = obj[i];"
3020 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;"
3021 " }"
3022 " 'PASSED'"
3023 "} catch(e) {"
3024 " e"
3025 "}";
3026 ExpectString(code, "PASSED");
3027}
3028
3029
3030THREADED_TEST(IndexedInterceptorWithNotSmiLookup) {
3031 v8::HandleScope scope;
3032 Local<ObjectTemplate> templ = ObjectTemplate::New();
3033 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3034
3035 LocalContext context;
3036 Local<v8::Object> obj = templ->NewInstance();
3037 context->Global()->Set(v8_str("obj"), obj);
3038
3039 const char* code =
3040 "try {"
3041 " for (var i = 0; i < 100; i++) {"
3042 " var expected = i;"
3043 " if (i == 50) {"
3044 " i = 'foobar';"
3045 " expected = undefined;"
3046 " }"
3047 " var v = obj[i];"
3048 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3049 " }"
3050 " 'PASSED'"
3051 "} catch(e) {"
3052 " e"
3053 "}";
3054 ExpectString(code, "PASSED");
3055}
3056
3057
3058THREADED_TEST(IndexedInterceptorGoingMegamorphic) {
3059 v8::HandleScope scope;
3060 Local<ObjectTemplate> templ = ObjectTemplate::New();
3061 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3062
3063 LocalContext context;
3064 Local<v8::Object> obj = templ->NewInstance();
3065 context->Global()->Set(v8_str("obj"), obj);
3066
3067 const char* code =
3068 "var original = obj;"
3069 "try {"
3070 " for (var i = 0; i < 100; i++) {"
3071 " var expected = i;"
3072 " if (i == 50) {"
3073 " obj = {50: 'foobar'};"
3074 " expected = 'foobar';"
3075 " }"
3076 " var v = obj[i];"
3077 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3078 " if (i == 50) obj = original;"
3079 " }"
3080 " 'PASSED'"
3081 "} catch(e) {"
3082 " e"
3083 "}";
3084 ExpectString(code, "PASSED");
3085}
3086
3087
3088THREADED_TEST(IndexedInterceptorReceiverTurningSmi) {
3089 v8::HandleScope scope;
3090 Local<ObjectTemplate> templ = ObjectTemplate::New();
3091 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3092
3093 LocalContext context;
3094 Local<v8::Object> obj = templ->NewInstance();
3095 context->Global()->Set(v8_str("obj"), obj);
3096
3097 const char* code =
3098 "var original = obj;"
3099 "try {"
3100 " for (var i = 0; i < 100; i++) {"
3101 " var expected = i;"
3102 " if (i == 5) {"
3103 " obj = 239;"
3104 " expected = undefined;"
3105 " }"
3106 " var v = obj[i];"
3107 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3108 " if (i == 5) obj = original;"
3109 " }"
3110 " 'PASSED'"
3111 "} catch(e) {"
3112 " e"
3113 "}";
3114 ExpectString(code, "PASSED");
3115}
3116
3117
3118THREADED_TEST(IndexedInterceptorOnProto) {
3119 v8::HandleScope scope;
3120 Local<ObjectTemplate> templ = ObjectTemplate::New();
3121 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3122
3123 LocalContext context;
3124 Local<v8::Object> obj = templ->NewInstance();
3125 context->Global()->Set(v8_str("obj"), obj);
3126
3127 const char* code =
3128 "var o = {__proto__: obj};"
3129 "try {"
3130 " for (var i = 0; i < 100; i++) {"
3131 " var v = o[i];"
3132 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;"
3133 " }"
3134 " 'PASSED'"
3135 "} catch(e) {"
3136 " e"
3137 "}";
3138 ExpectString(code, "PASSED");
3139}
3140
3141
Steve Blocka7e24c12009-10-30 11:49:00 +00003142THREADED_TEST(MultiContexts) {
3143 v8::HandleScope scope;
3144 v8::Handle<ObjectTemplate> templ = ObjectTemplate::New();
3145 templ->Set(v8_str("dummy"), v8::FunctionTemplate::New(DummyCallHandler));
3146
3147 Local<String> password = v8_str("Password");
3148
3149 // Create an environment
3150 LocalContext context0(0, templ);
3151 context0->SetSecurityToken(password);
3152 v8::Handle<v8::Object> global0 = context0->Global();
3153 global0->Set(v8_str("custom"), v8_num(1234));
3154 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value());
3155
3156 // Create an independent environment
3157 LocalContext context1(0, templ);
3158 context1->SetSecurityToken(password);
3159 v8::Handle<v8::Object> global1 = context1->Global();
3160 global1->Set(v8_str("custom"), v8_num(1234));
3161 CHECK_NE(global0, global1);
3162 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value());
3163 CHECK_EQ(1234, global1->Get(v8_str("custom"))->Int32Value());
3164
3165 // Now create a new context with the old global
3166 LocalContext context2(0, templ, global1);
3167 context2->SetSecurityToken(password);
3168 v8::Handle<v8::Object> global2 = context2->Global();
3169 CHECK_EQ(global1, global2);
3170 CHECK_EQ(0, global1->Get(v8_str("custom"))->Int32Value());
3171 CHECK_EQ(0, global2->Get(v8_str("custom"))->Int32Value());
3172}
3173
3174
3175THREADED_TEST(FunctionPrototypeAcrossContexts) {
3176 // Make sure that functions created by cloning boilerplates cannot
3177 // communicate through their __proto__ field.
3178
3179 v8::HandleScope scope;
3180
3181 LocalContext env0;
3182 v8::Handle<v8::Object> global0 =
3183 env0->Global();
3184 v8::Handle<v8::Object> object0 =
Steve Block6ded16b2010-05-10 14:33:55 +01003185 global0->Get(v8_str("Object")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003186 v8::Handle<v8::Object> tostring0 =
Steve Block6ded16b2010-05-10 14:33:55 +01003187 object0->Get(v8_str("toString")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003188 v8::Handle<v8::Object> proto0 =
Steve Block6ded16b2010-05-10 14:33:55 +01003189 tostring0->Get(v8_str("__proto__")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003190 proto0->Set(v8_str("custom"), v8_num(1234));
3191
3192 LocalContext env1;
3193 v8::Handle<v8::Object> global1 =
3194 env1->Global();
3195 v8::Handle<v8::Object> object1 =
Steve Block6ded16b2010-05-10 14:33:55 +01003196 global1->Get(v8_str("Object")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003197 v8::Handle<v8::Object> tostring1 =
Steve Block6ded16b2010-05-10 14:33:55 +01003198 object1->Get(v8_str("toString")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003199 v8::Handle<v8::Object> proto1 =
Steve Block6ded16b2010-05-10 14:33:55 +01003200 tostring1->Get(v8_str("__proto__")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003201 CHECK(!proto1->Has(v8_str("custom")));
3202}
3203
3204
3205THREADED_TEST(Regress892105) {
3206 // Make sure that object and array literals created by cloning
3207 // boilerplates cannot communicate through their __proto__
3208 // field. This is rather difficult to check, but we try to add stuff
3209 // to Object.prototype and Array.prototype and create a new
3210 // environment. This should succeed.
3211
3212 v8::HandleScope scope;
3213
3214 Local<String> source = v8_str("Object.prototype.obj = 1234;"
3215 "Array.prototype.arr = 4567;"
3216 "8901");
3217
3218 LocalContext env0;
3219 Local<Script> script0 = Script::Compile(source);
3220 CHECK_EQ(8901.0, script0->Run()->NumberValue());
3221
3222 LocalContext env1;
3223 Local<Script> script1 = Script::Compile(source);
3224 CHECK_EQ(8901.0, script1->Run()->NumberValue());
3225}
3226
3227
Steve Blocka7e24c12009-10-30 11:49:00 +00003228THREADED_TEST(UndetectableObject) {
3229 v8::HandleScope scope;
3230 LocalContext env;
3231
3232 Local<v8::FunctionTemplate> desc =
3233 v8::FunctionTemplate::New(0, v8::Handle<Value>());
3234 desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable
3235
3236 Local<v8::Object> obj = desc->GetFunction()->NewInstance();
3237 env->Global()->Set(v8_str("undetectable"), obj);
3238
3239 ExpectString("undetectable.toString()", "[object Object]");
3240 ExpectString("typeof undetectable", "undefined");
3241 ExpectString("typeof(undetectable)", "undefined");
3242 ExpectBoolean("typeof undetectable == 'undefined'", true);
3243 ExpectBoolean("typeof undetectable == 'object'", false);
3244 ExpectBoolean("if (undetectable) { true; } else { false; }", false);
3245 ExpectBoolean("!undetectable", true);
3246
3247 ExpectObject("true&&undetectable", obj);
3248 ExpectBoolean("false&&undetectable", false);
3249 ExpectBoolean("true||undetectable", true);
3250 ExpectObject("false||undetectable", obj);
3251
3252 ExpectObject("undetectable&&true", obj);
3253 ExpectObject("undetectable&&false", obj);
3254 ExpectBoolean("undetectable||true", true);
3255 ExpectBoolean("undetectable||false", false);
3256
3257 ExpectBoolean("undetectable==null", true);
3258 ExpectBoolean("null==undetectable", true);
3259 ExpectBoolean("undetectable==undefined", true);
3260 ExpectBoolean("undefined==undetectable", true);
3261 ExpectBoolean("undetectable==undetectable", true);
3262
3263
3264 ExpectBoolean("undetectable===null", false);
3265 ExpectBoolean("null===undetectable", false);
3266 ExpectBoolean("undetectable===undefined", false);
3267 ExpectBoolean("undefined===undetectable", false);
3268 ExpectBoolean("undetectable===undetectable", true);
3269}
3270
3271
3272THREADED_TEST(UndetectableString) {
3273 v8::HandleScope scope;
3274 LocalContext env;
3275
3276 Local<String> obj = String::NewUndetectable("foo");
3277 env->Global()->Set(v8_str("undetectable"), obj);
3278
3279 ExpectString("undetectable", "foo");
3280 ExpectString("typeof undetectable", "undefined");
3281 ExpectString("typeof(undetectable)", "undefined");
3282 ExpectBoolean("typeof undetectable == 'undefined'", true);
3283 ExpectBoolean("typeof undetectable == 'string'", false);
3284 ExpectBoolean("if (undetectable) { true; } else { false; }", false);
3285 ExpectBoolean("!undetectable", true);
3286
3287 ExpectObject("true&&undetectable", obj);
3288 ExpectBoolean("false&&undetectable", false);
3289 ExpectBoolean("true||undetectable", true);
3290 ExpectObject("false||undetectable", obj);
3291
3292 ExpectObject("undetectable&&true", obj);
3293 ExpectObject("undetectable&&false", obj);
3294 ExpectBoolean("undetectable||true", true);
3295 ExpectBoolean("undetectable||false", false);
3296
3297 ExpectBoolean("undetectable==null", true);
3298 ExpectBoolean("null==undetectable", true);
3299 ExpectBoolean("undetectable==undefined", true);
3300 ExpectBoolean("undefined==undetectable", true);
3301 ExpectBoolean("undetectable==undetectable", true);
3302
3303
3304 ExpectBoolean("undetectable===null", false);
3305 ExpectBoolean("null===undetectable", false);
3306 ExpectBoolean("undetectable===undefined", false);
3307 ExpectBoolean("undefined===undetectable", false);
3308 ExpectBoolean("undetectable===undetectable", true);
3309}
3310
3311
3312template <typename T> static void USE(T) { }
3313
3314
3315// This test is not intended to be run, just type checked.
3316static void PersistentHandles() {
3317 USE(PersistentHandles);
3318 Local<String> str = v8_str("foo");
3319 v8::Persistent<String> p_str = v8::Persistent<String>::New(str);
3320 USE(p_str);
3321 Local<Script> scr = Script::Compile(v8_str(""));
3322 v8::Persistent<Script> p_scr = v8::Persistent<Script>::New(scr);
3323 USE(p_scr);
3324 Local<ObjectTemplate> templ = ObjectTemplate::New();
3325 v8::Persistent<ObjectTemplate> p_templ =
3326 v8::Persistent<ObjectTemplate>::New(templ);
3327 USE(p_templ);
3328}
3329
3330
3331static v8::Handle<Value> HandleLogDelegator(const v8::Arguments& args) {
3332 ApiTestFuzzer::Fuzz();
3333 return v8::Undefined();
3334}
3335
3336
3337THREADED_TEST(GlobalObjectTemplate) {
3338 v8::HandleScope handle_scope;
3339 Local<ObjectTemplate> global_template = ObjectTemplate::New();
3340 global_template->Set(v8_str("JSNI_Log"),
3341 v8::FunctionTemplate::New(HandleLogDelegator));
3342 v8::Persistent<Context> context = Context::New(0, global_template);
3343 Context::Scope context_scope(context);
3344 Script::Compile(v8_str("JSNI_Log('LOG')"))->Run();
3345 context.Dispose();
3346}
3347
3348
3349static const char* kSimpleExtensionSource =
3350 "function Foo() {"
3351 " return 4;"
3352 "}";
3353
3354
3355THREADED_TEST(SimpleExtensions) {
3356 v8::HandleScope handle_scope;
3357 v8::RegisterExtension(new Extension("simpletest", kSimpleExtensionSource));
3358 const char* extension_names[] = { "simpletest" };
3359 v8::ExtensionConfiguration extensions(1, extension_names);
3360 v8::Handle<Context> context = Context::New(&extensions);
3361 Context::Scope lock(context);
3362 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run();
3363 CHECK_EQ(result, v8::Integer::New(4));
3364}
3365
3366
3367static const char* kEvalExtensionSource1 =
3368 "function UseEval1() {"
3369 " var x = 42;"
3370 " return eval('x');"
3371 "}";
3372
3373
3374static const char* kEvalExtensionSource2 =
3375 "(function() {"
3376 " var x = 42;"
3377 " function e() {"
3378 " return eval('x');"
3379 " }"
3380 " this.UseEval2 = e;"
3381 "})()";
3382
3383
3384THREADED_TEST(UseEvalFromExtension) {
3385 v8::HandleScope handle_scope;
3386 v8::RegisterExtension(new Extension("evaltest1", kEvalExtensionSource1));
3387 v8::RegisterExtension(new Extension("evaltest2", kEvalExtensionSource2));
3388 const char* extension_names[] = { "evaltest1", "evaltest2" };
3389 v8::ExtensionConfiguration extensions(2, extension_names);
3390 v8::Handle<Context> context = Context::New(&extensions);
3391 Context::Scope lock(context);
3392 v8::Handle<Value> result = Script::Compile(v8_str("UseEval1()"))->Run();
3393 CHECK_EQ(result, v8::Integer::New(42));
3394 result = Script::Compile(v8_str("UseEval2()"))->Run();
3395 CHECK_EQ(result, v8::Integer::New(42));
3396}
3397
3398
3399static const char* kWithExtensionSource1 =
3400 "function UseWith1() {"
3401 " var x = 42;"
3402 " with({x:87}) { return x; }"
3403 "}";
3404
3405
3406
3407static const char* kWithExtensionSource2 =
3408 "(function() {"
3409 " var x = 42;"
3410 " function e() {"
3411 " with ({x:87}) { return x; }"
3412 " }"
3413 " this.UseWith2 = e;"
3414 "})()";
3415
3416
3417THREADED_TEST(UseWithFromExtension) {
3418 v8::HandleScope handle_scope;
3419 v8::RegisterExtension(new Extension("withtest1", kWithExtensionSource1));
3420 v8::RegisterExtension(new Extension("withtest2", kWithExtensionSource2));
3421 const char* extension_names[] = { "withtest1", "withtest2" };
3422 v8::ExtensionConfiguration extensions(2, extension_names);
3423 v8::Handle<Context> context = Context::New(&extensions);
3424 Context::Scope lock(context);
3425 v8::Handle<Value> result = Script::Compile(v8_str("UseWith1()"))->Run();
3426 CHECK_EQ(result, v8::Integer::New(87));
3427 result = Script::Compile(v8_str("UseWith2()"))->Run();
3428 CHECK_EQ(result, v8::Integer::New(87));
3429}
3430
3431
3432THREADED_TEST(AutoExtensions) {
3433 v8::HandleScope handle_scope;
3434 Extension* extension = new Extension("autotest", kSimpleExtensionSource);
3435 extension->set_auto_enable(true);
3436 v8::RegisterExtension(extension);
3437 v8::Handle<Context> context = Context::New();
3438 Context::Scope lock(context);
3439 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run();
3440 CHECK_EQ(result, v8::Integer::New(4));
3441}
3442
3443
Steve Blockd0582a62009-12-15 09:54:21 +00003444static const char* kSyntaxErrorInExtensionSource =
3445 "[";
3446
3447
3448// Test that a syntax error in an extension does not cause a fatal
3449// error but results in an empty context.
3450THREADED_TEST(SyntaxErrorExtensions) {
3451 v8::HandleScope handle_scope;
3452 v8::RegisterExtension(new Extension("syntaxerror",
3453 kSyntaxErrorInExtensionSource));
3454 const char* extension_names[] = { "syntaxerror" };
3455 v8::ExtensionConfiguration extensions(1, extension_names);
3456 v8::Handle<Context> context = Context::New(&extensions);
3457 CHECK(context.IsEmpty());
3458}
3459
3460
3461static const char* kExceptionInExtensionSource =
3462 "throw 42";
3463
3464
3465// Test that an exception when installing an extension does not cause
3466// a fatal error but results in an empty context.
3467THREADED_TEST(ExceptionExtensions) {
3468 v8::HandleScope handle_scope;
3469 v8::RegisterExtension(new Extension("exception",
3470 kExceptionInExtensionSource));
3471 const char* extension_names[] = { "exception" };
3472 v8::ExtensionConfiguration extensions(1, extension_names);
3473 v8::Handle<Context> context = Context::New(&extensions);
3474 CHECK(context.IsEmpty());
3475}
3476
3477
Steve Blocka7e24c12009-10-30 11:49:00 +00003478static void CheckDependencies(const char* name, const char* expected) {
3479 v8::HandleScope handle_scope;
3480 v8::ExtensionConfiguration config(1, &name);
3481 LocalContext context(&config);
3482 CHECK_EQ(String::New(expected), context->Global()->Get(v8_str("loaded")));
3483}
3484
3485
3486/*
3487 * Configuration:
3488 *
3489 * /-- B <--\
3490 * A <- -- D <-- E
3491 * \-- C <--/
3492 */
3493THREADED_TEST(ExtensionDependency) {
3494 static const char* kEDeps[] = { "D" };
3495 v8::RegisterExtension(new Extension("E", "this.loaded += 'E';", 1, kEDeps));
3496 static const char* kDDeps[] = { "B", "C" };
3497 v8::RegisterExtension(new Extension("D", "this.loaded += 'D';", 2, kDDeps));
3498 static const char* kBCDeps[] = { "A" };
3499 v8::RegisterExtension(new Extension("B", "this.loaded += 'B';", 1, kBCDeps));
3500 v8::RegisterExtension(new Extension("C", "this.loaded += 'C';", 1, kBCDeps));
3501 v8::RegisterExtension(new Extension("A", "this.loaded += 'A';"));
3502 CheckDependencies("A", "undefinedA");
3503 CheckDependencies("B", "undefinedAB");
3504 CheckDependencies("C", "undefinedAC");
3505 CheckDependencies("D", "undefinedABCD");
3506 CheckDependencies("E", "undefinedABCDE");
3507 v8::HandleScope handle_scope;
3508 static const char* exts[2] = { "C", "E" };
3509 v8::ExtensionConfiguration config(2, exts);
3510 LocalContext context(&config);
3511 CHECK_EQ(v8_str("undefinedACBDE"), context->Global()->Get(v8_str("loaded")));
3512}
3513
3514
3515static const char* kExtensionTestScript =
3516 "native function A();"
3517 "native function B();"
3518 "native function C();"
3519 "function Foo(i) {"
3520 " if (i == 0) return A();"
3521 " if (i == 1) return B();"
3522 " if (i == 2) return C();"
3523 "}";
3524
3525
3526static v8::Handle<Value> CallFun(const v8::Arguments& args) {
3527 ApiTestFuzzer::Fuzz();
Leon Clarkee46be812010-01-19 14:06:41 +00003528 if (args.IsConstructCall()) {
3529 args.This()->Set(v8_str("data"), args.Data());
3530 return v8::Null();
3531 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003532 return args.Data();
3533}
3534
3535
3536class FunctionExtension : public Extension {
3537 public:
3538 FunctionExtension() : Extension("functiontest", kExtensionTestScript) { }
3539 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(
3540 v8::Handle<String> name);
3541};
3542
3543
3544static int lookup_count = 0;
3545v8::Handle<v8::FunctionTemplate> FunctionExtension::GetNativeFunction(
3546 v8::Handle<String> name) {
3547 lookup_count++;
3548 if (name->Equals(v8_str("A"))) {
3549 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(8));
3550 } else if (name->Equals(v8_str("B"))) {
3551 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(7));
3552 } else if (name->Equals(v8_str("C"))) {
3553 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(6));
3554 } else {
3555 return v8::Handle<v8::FunctionTemplate>();
3556 }
3557}
3558
3559
3560THREADED_TEST(FunctionLookup) {
3561 v8::RegisterExtension(new FunctionExtension());
3562 v8::HandleScope handle_scope;
3563 static const char* exts[1] = { "functiontest" };
3564 v8::ExtensionConfiguration config(1, exts);
3565 LocalContext context(&config);
3566 CHECK_EQ(3, lookup_count);
3567 CHECK_EQ(v8::Integer::New(8), Script::Compile(v8_str("Foo(0)"))->Run());
3568 CHECK_EQ(v8::Integer::New(7), Script::Compile(v8_str("Foo(1)"))->Run());
3569 CHECK_EQ(v8::Integer::New(6), Script::Compile(v8_str("Foo(2)"))->Run());
3570}
3571
3572
Leon Clarkee46be812010-01-19 14:06:41 +00003573THREADED_TEST(NativeFunctionConstructCall) {
3574 v8::RegisterExtension(new FunctionExtension());
3575 v8::HandleScope handle_scope;
3576 static const char* exts[1] = { "functiontest" };
3577 v8::ExtensionConfiguration config(1, exts);
3578 LocalContext context(&config);
Leon Clarked91b9f72010-01-27 17:25:45 +00003579 for (int i = 0; i < 10; i++) {
3580 // Run a few times to ensure that allocation of objects doesn't
3581 // change behavior of a constructor function.
3582 CHECK_EQ(v8::Integer::New(8),
3583 Script::Compile(v8_str("(new A()).data"))->Run());
3584 CHECK_EQ(v8::Integer::New(7),
3585 Script::Compile(v8_str("(new B()).data"))->Run());
3586 CHECK_EQ(v8::Integer::New(6),
3587 Script::Compile(v8_str("(new C()).data"))->Run());
3588 }
Leon Clarkee46be812010-01-19 14:06:41 +00003589}
3590
3591
Steve Blocka7e24c12009-10-30 11:49:00 +00003592static const char* last_location;
3593static const char* last_message;
3594void StoringErrorCallback(const char* location, const char* message) {
3595 if (last_location == NULL) {
3596 last_location = location;
3597 last_message = message;
3598 }
3599}
3600
3601
3602// ErrorReporting creates a circular extensions configuration and
3603// tests that the fatal error handler gets called. This renders V8
3604// unusable and therefore this test cannot be run in parallel.
3605TEST(ErrorReporting) {
3606 v8::V8::SetFatalErrorHandler(StoringErrorCallback);
3607 static const char* aDeps[] = { "B" };
3608 v8::RegisterExtension(new Extension("A", "", 1, aDeps));
3609 static const char* bDeps[] = { "A" };
3610 v8::RegisterExtension(new Extension("B", "", 1, bDeps));
3611 last_location = NULL;
3612 v8::ExtensionConfiguration config(1, bDeps);
3613 v8::Handle<Context> context = Context::New(&config);
3614 CHECK(context.IsEmpty());
3615 CHECK_NE(last_location, NULL);
3616}
3617
3618
3619static const char* js_code_causing_huge_string_flattening =
3620 "var str = 'X';"
3621 "for (var i = 0; i < 30; i++) {"
3622 " str = str + str;"
3623 "}"
3624 "str.match(/X/);";
3625
3626
3627void OOMCallback(const char* location, const char* message) {
3628 exit(0);
3629}
3630
3631
3632TEST(RegexpOutOfMemory) {
3633 // Execute a script that causes out of memory when flattening a string.
3634 v8::HandleScope scope;
3635 v8::V8::SetFatalErrorHandler(OOMCallback);
3636 LocalContext context;
3637 Local<Script> script =
3638 Script::Compile(String::New(js_code_causing_huge_string_flattening));
3639 last_location = NULL;
3640 Local<Value> result = script->Run();
3641
3642 CHECK(false); // Should not return.
3643}
3644
3645
3646static void MissingScriptInfoMessageListener(v8::Handle<v8::Message> message,
3647 v8::Handle<Value> data) {
3648 CHECK_EQ(v8::Undefined(), data);
3649 CHECK(message->GetScriptResourceName()->IsUndefined());
3650 CHECK_EQ(v8::Undefined(), message->GetScriptResourceName());
3651 message->GetLineNumber();
3652 message->GetSourceLine();
3653}
3654
3655
3656THREADED_TEST(ErrorWithMissingScriptInfo) {
3657 v8::HandleScope scope;
3658 LocalContext context;
3659 v8::V8::AddMessageListener(MissingScriptInfoMessageListener);
3660 Script::Compile(v8_str("throw Error()"))->Run();
3661 v8::V8::RemoveMessageListeners(MissingScriptInfoMessageListener);
3662}
3663
3664
3665int global_index = 0;
3666
3667class Snorkel {
3668 public:
3669 Snorkel() { index_ = global_index++; }
3670 int index_;
3671};
3672
3673class Whammy {
3674 public:
3675 Whammy() {
3676 cursor_ = 0;
3677 }
3678 ~Whammy() {
3679 script_.Dispose();
3680 }
3681 v8::Handle<Script> getScript() {
3682 if (script_.IsEmpty())
3683 script_ = v8::Persistent<Script>::New(v8_compile("({}).blammo"));
3684 return Local<Script>(*script_);
3685 }
3686
3687 public:
3688 static const int kObjectCount = 256;
3689 int cursor_;
3690 v8::Persistent<v8::Object> objects_[kObjectCount];
3691 v8::Persistent<Script> script_;
3692};
3693
3694static void HandleWeakReference(v8::Persistent<v8::Value> obj, void* data) {
3695 Snorkel* snorkel = reinterpret_cast<Snorkel*>(data);
3696 delete snorkel;
3697 obj.ClearWeak();
3698}
3699
3700v8::Handle<Value> WhammyPropertyGetter(Local<String> name,
3701 const AccessorInfo& info) {
3702 Whammy* whammy =
3703 static_cast<Whammy*>(v8::Handle<v8::External>::Cast(info.Data())->Value());
3704
3705 v8::Persistent<v8::Object> prev = whammy->objects_[whammy->cursor_];
3706
3707 v8::Handle<v8::Object> obj = v8::Object::New();
3708 v8::Persistent<v8::Object> global = v8::Persistent<v8::Object>::New(obj);
3709 if (!prev.IsEmpty()) {
3710 prev->Set(v8_str("next"), obj);
3711 prev.MakeWeak(new Snorkel(), &HandleWeakReference);
3712 whammy->objects_[whammy->cursor_].Clear();
3713 }
3714 whammy->objects_[whammy->cursor_] = global;
3715 whammy->cursor_ = (whammy->cursor_ + 1) % Whammy::kObjectCount;
3716 return whammy->getScript()->Run();
3717}
3718
3719THREADED_TEST(WeakReference) {
3720 v8::HandleScope handle_scope;
3721 v8::Handle<v8::ObjectTemplate> templ= v8::ObjectTemplate::New();
3722 templ->SetNamedPropertyHandler(WhammyPropertyGetter,
3723 0, 0, 0, 0,
3724 v8::External::New(new Whammy()));
3725 const char* extension_list[] = { "v8/gc" };
3726 v8::ExtensionConfiguration extensions(1, extension_list);
3727 v8::Persistent<Context> context = Context::New(&extensions);
3728 Context::Scope context_scope(context);
3729
3730 v8::Handle<v8::Object> interceptor = templ->NewInstance();
3731 context->Global()->Set(v8_str("whammy"), interceptor);
3732 const char* code =
3733 "var last;"
3734 "for (var i = 0; i < 10000; i++) {"
3735 " var obj = whammy.length;"
3736 " if (last) last.next = obj;"
3737 " last = obj;"
3738 "}"
3739 "gc();"
3740 "4";
3741 v8::Handle<Value> result = CompileRun(code);
3742 CHECK_EQ(4.0, result->NumberValue());
3743
3744 context.Dispose();
3745}
3746
3747
Steve Blockd0582a62009-12-15 09:54:21 +00003748static bool in_scavenge = false;
3749static int last = -1;
3750
3751static void ForceScavenge(v8::Persistent<v8::Value> obj, void* data) {
3752 CHECK_EQ(-1, last);
3753 last = 0;
3754 obj.Dispose();
3755 obj.Clear();
3756 in_scavenge = true;
3757 i::Heap::PerformScavenge();
3758 in_scavenge = false;
3759 *(reinterpret_cast<bool*>(data)) = true;
3760}
3761
3762static void CheckIsNotInvokedInScavenge(v8::Persistent<v8::Value> obj,
3763 void* data) {
3764 CHECK_EQ(0, last);
3765 last = 1;
3766 *(reinterpret_cast<bool*>(data)) = in_scavenge;
3767 obj.Dispose();
3768 obj.Clear();
3769}
3770
3771THREADED_TEST(NoWeakRefCallbacksInScavenge) {
3772 // Test verifies that scavenge cannot invoke WeakReferenceCallbacks.
3773 // Calling callbacks from scavenges is unsafe as objects held by those
3774 // handlers might have become strongly reachable, but scavenge doesn't
3775 // check that.
3776 v8::Persistent<Context> context = Context::New();
3777 Context::Scope context_scope(context);
3778
3779 v8::Persistent<v8::Object> object_a;
3780 v8::Persistent<v8::Object> object_b;
3781
3782 {
3783 v8::HandleScope handle_scope;
3784 object_b = v8::Persistent<v8::Object>::New(v8::Object::New());
3785 object_a = v8::Persistent<v8::Object>::New(v8::Object::New());
3786 }
3787
3788 bool object_a_disposed = false;
3789 object_a.MakeWeak(&object_a_disposed, &ForceScavenge);
3790 bool released_in_scavenge = false;
3791 object_b.MakeWeak(&released_in_scavenge, &CheckIsNotInvokedInScavenge);
3792
3793 while (!object_a_disposed) {
3794 i::Heap::CollectAllGarbage(false);
3795 }
3796 CHECK(!released_in_scavenge);
3797}
3798
3799
Steve Blocka7e24c12009-10-30 11:49:00 +00003800v8::Handle<Function> args_fun;
3801
3802
3803static v8::Handle<Value> ArgumentsTestCallback(const v8::Arguments& args) {
3804 ApiTestFuzzer::Fuzz();
3805 CHECK_EQ(args_fun, args.Callee());
3806 CHECK_EQ(3, args.Length());
3807 CHECK_EQ(v8::Integer::New(1), args[0]);
3808 CHECK_EQ(v8::Integer::New(2), args[1]);
3809 CHECK_EQ(v8::Integer::New(3), args[2]);
3810 CHECK_EQ(v8::Undefined(), args[3]);
3811 v8::HandleScope scope;
3812 i::Heap::CollectAllGarbage(false);
3813 return v8::Undefined();
3814}
3815
3816
3817THREADED_TEST(Arguments) {
3818 v8::HandleScope scope;
3819 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New();
3820 global->Set(v8_str("f"), v8::FunctionTemplate::New(ArgumentsTestCallback));
3821 LocalContext context(NULL, global);
Steve Block6ded16b2010-05-10 14:33:55 +01003822 args_fun = context->Global()->Get(v8_str("f")).As<Function>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003823 v8_compile("f(1, 2, 3)")->Run();
3824}
3825
3826
Steve Blocka7e24c12009-10-30 11:49:00 +00003827static v8::Handle<Value> NoBlockGetterX(Local<String> name,
3828 const AccessorInfo&) {
3829 return v8::Handle<Value>();
3830}
3831
3832
3833static v8::Handle<Value> NoBlockGetterI(uint32_t index,
3834 const AccessorInfo&) {
3835 return v8::Handle<Value>();
3836}
3837
3838
3839static v8::Handle<v8::Boolean> PDeleter(Local<String> name,
3840 const AccessorInfo&) {
3841 if (!name->Equals(v8_str("foo"))) {
3842 return v8::Handle<v8::Boolean>(); // not intercepted
3843 }
3844
3845 return v8::False(); // intercepted, and don't delete the property
3846}
3847
3848
3849static v8::Handle<v8::Boolean> IDeleter(uint32_t index, const AccessorInfo&) {
3850 if (index != 2) {
3851 return v8::Handle<v8::Boolean>(); // not intercepted
3852 }
3853
3854 return v8::False(); // intercepted, and don't delete the property
3855}
3856
3857
3858THREADED_TEST(Deleter) {
3859 v8::HandleScope scope;
3860 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
3861 obj->SetNamedPropertyHandler(NoBlockGetterX, NULL, NULL, PDeleter, NULL);
3862 obj->SetIndexedPropertyHandler(NoBlockGetterI, NULL, NULL, IDeleter, NULL);
3863 LocalContext context;
3864 context->Global()->Set(v8_str("k"), obj->NewInstance());
3865 CompileRun(
3866 "k.foo = 'foo';"
3867 "k.bar = 'bar';"
3868 "k[2] = 2;"
3869 "k[4] = 4;");
3870 CHECK(v8_compile("delete k.foo")->Run()->IsFalse());
3871 CHECK(v8_compile("delete k.bar")->Run()->IsTrue());
3872
3873 CHECK_EQ(v8_compile("k.foo")->Run(), v8_str("foo"));
3874 CHECK(v8_compile("k.bar")->Run()->IsUndefined());
3875
3876 CHECK(v8_compile("delete k[2]")->Run()->IsFalse());
3877 CHECK(v8_compile("delete k[4]")->Run()->IsTrue());
3878
3879 CHECK_EQ(v8_compile("k[2]")->Run(), v8_num(2));
3880 CHECK(v8_compile("k[4]")->Run()->IsUndefined());
3881}
3882
3883
3884static v8::Handle<Value> GetK(Local<String> name, const AccessorInfo&) {
3885 ApiTestFuzzer::Fuzz();
3886 if (name->Equals(v8_str("foo")) ||
3887 name->Equals(v8_str("bar")) ||
3888 name->Equals(v8_str("baz"))) {
3889 return v8::Undefined();
3890 }
3891 return v8::Handle<Value>();
3892}
3893
3894
3895static v8::Handle<Value> IndexedGetK(uint32_t index, const AccessorInfo&) {
3896 ApiTestFuzzer::Fuzz();
3897 if (index == 0 || index == 1) return v8::Undefined();
3898 return v8::Handle<Value>();
3899}
3900
3901
3902static v8::Handle<v8::Array> NamedEnum(const AccessorInfo&) {
3903 ApiTestFuzzer::Fuzz();
3904 v8::Handle<v8::Array> result = v8::Array::New(3);
3905 result->Set(v8::Integer::New(0), v8_str("foo"));
3906 result->Set(v8::Integer::New(1), v8_str("bar"));
3907 result->Set(v8::Integer::New(2), v8_str("baz"));
3908 return result;
3909}
3910
3911
3912static v8::Handle<v8::Array> IndexedEnum(const AccessorInfo&) {
3913 ApiTestFuzzer::Fuzz();
3914 v8::Handle<v8::Array> result = v8::Array::New(2);
3915 result->Set(v8::Integer::New(0), v8_str("0"));
3916 result->Set(v8::Integer::New(1), v8_str("1"));
3917 return result;
3918}
3919
3920
3921THREADED_TEST(Enumerators) {
3922 v8::HandleScope scope;
3923 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
3924 obj->SetNamedPropertyHandler(GetK, NULL, NULL, NULL, NamedEnum);
3925 obj->SetIndexedPropertyHandler(IndexedGetK, NULL, NULL, NULL, IndexedEnum);
3926 LocalContext context;
3927 context->Global()->Set(v8_str("k"), obj->NewInstance());
3928 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun(
3929 "k[10] = 0;"
3930 "k.a = 0;"
3931 "k[5] = 0;"
3932 "k.b = 0;"
3933 "k[4294967295] = 0;"
3934 "k.c = 0;"
3935 "k[4294967296] = 0;"
3936 "k.d = 0;"
3937 "k[140000] = 0;"
3938 "k.e = 0;"
3939 "k[30000000000] = 0;"
3940 "k.f = 0;"
3941 "var result = [];"
3942 "for (var prop in k) {"
3943 " result.push(prop);"
3944 "}"
3945 "result"));
3946 // Check that we get all the property names returned including the
3947 // ones from the enumerators in the right order: indexed properties
3948 // in numerical order, indexed interceptor properties, named
3949 // properties in insertion order, named interceptor properties.
3950 // This order is not mandated by the spec, so this test is just
3951 // documenting our behavior.
3952 CHECK_EQ(17, result->Length());
3953 // Indexed properties in numerical order.
3954 CHECK_EQ(v8_str("5"), result->Get(v8::Integer::New(0)));
3955 CHECK_EQ(v8_str("10"), result->Get(v8::Integer::New(1)));
3956 CHECK_EQ(v8_str("140000"), result->Get(v8::Integer::New(2)));
3957 CHECK_EQ(v8_str("4294967295"), result->Get(v8::Integer::New(3)));
3958 // Indexed interceptor properties in the order they are returned
3959 // from the enumerator interceptor.
3960 CHECK_EQ(v8_str("0"), result->Get(v8::Integer::New(4)));
3961 CHECK_EQ(v8_str("1"), result->Get(v8::Integer::New(5)));
3962 // Named properties in insertion order.
3963 CHECK_EQ(v8_str("a"), result->Get(v8::Integer::New(6)));
3964 CHECK_EQ(v8_str("b"), result->Get(v8::Integer::New(7)));
3965 CHECK_EQ(v8_str("c"), result->Get(v8::Integer::New(8)));
3966 CHECK_EQ(v8_str("4294967296"), result->Get(v8::Integer::New(9)));
3967 CHECK_EQ(v8_str("d"), result->Get(v8::Integer::New(10)));
3968 CHECK_EQ(v8_str("e"), result->Get(v8::Integer::New(11)));
3969 CHECK_EQ(v8_str("30000000000"), result->Get(v8::Integer::New(12)));
3970 CHECK_EQ(v8_str("f"), result->Get(v8::Integer::New(13)));
3971 // Named interceptor properties.
3972 CHECK_EQ(v8_str("foo"), result->Get(v8::Integer::New(14)));
3973 CHECK_EQ(v8_str("bar"), result->Get(v8::Integer::New(15)));
3974 CHECK_EQ(v8_str("baz"), result->Get(v8::Integer::New(16)));
3975}
3976
3977
3978int p_getter_count;
3979int p_getter_count2;
3980
3981
3982static v8::Handle<Value> PGetter(Local<String> name, const AccessorInfo& info) {
3983 ApiTestFuzzer::Fuzz();
3984 p_getter_count++;
3985 v8::Handle<v8::Object> global = Context::GetCurrent()->Global();
3986 CHECK_EQ(info.Holder(), global->Get(v8_str("o1")));
3987 if (name->Equals(v8_str("p1"))) {
3988 CHECK_EQ(info.This(), global->Get(v8_str("o1")));
3989 } else if (name->Equals(v8_str("p2"))) {
3990 CHECK_EQ(info.This(), global->Get(v8_str("o2")));
3991 } else if (name->Equals(v8_str("p3"))) {
3992 CHECK_EQ(info.This(), global->Get(v8_str("o3")));
3993 } else if (name->Equals(v8_str("p4"))) {
3994 CHECK_EQ(info.This(), global->Get(v8_str("o4")));
3995 }
3996 return v8::Undefined();
3997}
3998
3999
4000static void RunHolderTest(v8::Handle<v8::ObjectTemplate> obj) {
4001 ApiTestFuzzer::Fuzz();
4002 LocalContext context;
4003 context->Global()->Set(v8_str("o1"), obj->NewInstance());
4004 CompileRun(
4005 "o1.__proto__ = { };"
4006 "var o2 = { __proto__: o1 };"
4007 "var o3 = { __proto__: o2 };"
4008 "var o4 = { __proto__: o3 };"
4009 "for (var i = 0; i < 10; i++) o4.p4;"
4010 "for (var i = 0; i < 10; i++) o3.p3;"
4011 "for (var i = 0; i < 10; i++) o2.p2;"
4012 "for (var i = 0; i < 10; i++) o1.p1;");
4013}
4014
4015
4016static v8::Handle<Value> PGetter2(Local<String> name,
4017 const AccessorInfo& info) {
4018 ApiTestFuzzer::Fuzz();
4019 p_getter_count2++;
4020 v8::Handle<v8::Object> global = Context::GetCurrent()->Global();
4021 CHECK_EQ(info.Holder(), global->Get(v8_str("o1")));
4022 if (name->Equals(v8_str("p1"))) {
4023 CHECK_EQ(info.This(), global->Get(v8_str("o1")));
4024 } else if (name->Equals(v8_str("p2"))) {
4025 CHECK_EQ(info.This(), global->Get(v8_str("o2")));
4026 } else if (name->Equals(v8_str("p3"))) {
4027 CHECK_EQ(info.This(), global->Get(v8_str("o3")));
4028 } else if (name->Equals(v8_str("p4"))) {
4029 CHECK_EQ(info.This(), global->Get(v8_str("o4")));
4030 }
4031 return v8::Undefined();
4032}
4033
4034
4035THREADED_TEST(GetterHolders) {
4036 v8::HandleScope scope;
4037 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4038 obj->SetAccessor(v8_str("p1"), PGetter);
4039 obj->SetAccessor(v8_str("p2"), PGetter);
4040 obj->SetAccessor(v8_str("p3"), PGetter);
4041 obj->SetAccessor(v8_str("p4"), PGetter);
4042 p_getter_count = 0;
4043 RunHolderTest(obj);
4044 CHECK_EQ(40, p_getter_count);
4045}
4046
4047
4048THREADED_TEST(PreInterceptorHolders) {
4049 v8::HandleScope scope;
4050 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4051 obj->SetNamedPropertyHandler(PGetter2);
4052 p_getter_count2 = 0;
4053 RunHolderTest(obj);
4054 CHECK_EQ(40, p_getter_count2);
4055}
4056
4057
4058THREADED_TEST(ObjectInstantiation) {
4059 v8::HandleScope scope;
4060 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
4061 templ->SetAccessor(v8_str("t"), PGetter2);
4062 LocalContext context;
4063 context->Global()->Set(v8_str("o"), templ->NewInstance());
4064 for (int i = 0; i < 100; i++) {
4065 v8::HandleScope inner_scope;
4066 v8::Handle<v8::Object> obj = templ->NewInstance();
4067 CHECK_NE(obj, context->Global()->Get(v8_str("o")));
4068 context->Global()->Set(v8_str("o2"), obj);
4069 v8::Handle<Value> value =
4070 Script::Compile(v8_str("o.__proto__ === o2.__proto__"))->Run();
4071 CHECK_EQ(v8::True(), value);
4072 context->Global()->Set(v8_str("o"), obj);
4073 }
4074}
4075
4076
4077THREADED_TEST(StringWrite) {
4078 v8::HandleScope scope;
4079 v8::Handle<String> str = v8_str("abcde");
4080
4081 char buf[100];
4082 int len;
4083
4084 memset(buf, 0x1, sizeof(buf));
4085 len = str->WriteAscii(buf);
4086 CHECK_EQ(len, 5);
4087 CHECK_EQ(strncmp("abcde\0", buf, 6), 0);
4088
4089 memset(buf, 0x1, sizeof(buf));
4090 len = str->WriteAscii(buf, 0, 4);
4091 CHECK_EQ(len, 4);
4092 CHECK_EQ(strncmp("abcd\1", buf, 5), 0);
4093
4094 memset(buf, 0x1, sizeof(buf));
4095 len = str->WriteAscii(buf, 0, 5);
4096 CHECK_EQ(len, 5);
4097 CHECK_EQ(strncmp("abcde\1", buf, 6), 0);
4098
4099 memset(buf, 0x1, sizeof(buf));
4100 len = str->WriteAscii(buf, 0, 6);
4101 CHECK_EQ(len, 5);
4102 CHECK_EQ(strncmp("abcde\0", buf, 6), 0);
4103
4104 memset(buf, 0x1, sizeof(buf));
4105 len = str->WriteAscii(buf, 4, -1);
4106 CHECK_EQ(len, 1);
4107 CHECK_EQ(strncmp("e\0", buf, 2), 0);
4108
4109 memset(buf, 0x1, sizeof(buf));
4110 len = str->WriteAscii(buf, 4, 6);
4111 CHECK_EQ(len, 1);
4112 CHECK_EQ(strncmp("e\0", buf, 2), 0);
4113
4114 memset(buf, 0x1, sizeof(buf));
4115 len = str->WriteAscii(buf, 4, 1);
4116 CHECK_EQ(len, 1);
4117 CHECK_EQ(strncmp("e\1", buf, 2), 0);
4118}
4119
4120
4121THREADED_TEST(ToArrayIndex) {
4122 v8::HandleScope scope;
4123 LocalContext context;
4124
4125 v8::Handle<String> str = v8_str("42");
4126 v8::Handle<v8::Uint32> index = str->ToArrayIndex();
4127 CHECK(!index.IsEmpty());
4128 CHECK_EQ(42.0, index->Uint32Value());
4129 str = v8_str("42asdf");
4130 index = str->ToArrayIndex();
4131 CHECK(index.IsEmpty());
4132 str = v8_str("-42");
4133 index = str->ToArrayIndex();
4134 CHECK(index.IsEmpty());
4135 str = v8_str("4294967295");
4136 index = str->ToArrayIndex();
4137 CHECK(!index.IsEmpty());
4138 CHECK_EQ(4294967295.0, index->Uint32Value());
4139 v8::Handle<v8::Number> num = v8::Number::New(1);
4140 index = num->ToArrayIndex();
4141 CHECK(!index.IsEmpty());
4142 CHECK_EQ(1.0, index->Uint32Value());
4143 num = v8::Number::New(-1);
4144 index = num->ToArrayIndex();
4145 CHECK(index.IsEmpty());
4146 v8::Handle<v8::Object> obj = v8::Object::New();
4147 index = obj->ToArrayIndex();
4148 CHECK(index.IsEmpty());
4149}
4150
4151
4152THREADED_TEST(ErrorConstruction) {
4153 v8::HandleScope scope;
4154 LocalContext context;
4155
4156 v8::Handle<String> foo = v8_str("foo");
4157 v8::Handle<String> message = v8_str("message");
4158 v8::Handle<Value> range_error = v8::Exception::RangeError(foo);
4159 CHECK(range_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004160 v8::Handle<v8::Object> range_obj = range_error.As<v8::Object>();
4161 CHECK(range_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004162 v8::Handle<Value> reference_error = v8::Exception::ReferenceError(foo);
4163 CHECK(reference_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004164 CHECK(reference_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004165 v8::Handle<Value> syntax_error = v8::Exception::SyntaxError(foo);
4166 CHECK(syntax_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004167 CHECK(syntax_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004168 v8::Handle<Value> type_error = v8::Exception::TypeError(foo);
4169 CHECK(type_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004170 CHECK(type_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004171 v8::Handle<Value> error = v8::Exception::Error(foo);
4172 CHECK(error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004173 CHECK(error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004174}
4175
4176
4177static v8::Handle<Value> YGetter(Local<String> name, const AccessorInfo& info) {
4178 ApiTestFuzzer::Fuzz();
4179 return v8_num(10);
4180}
4181
4182
4183static void YSetter(Local<String> name,
4184 Local<Value> value,
4185 const AccessorInfo& info) {
4186 if (info.This()->Has(name)) {
4187 info.This()->Delete(name);
4188 }
4189 info.This()->Set(name, value);
4190}
4191
4192
4193THREADED_TEST(DeleteAccessor) {
4194 v8::HandleScope scope;
4195 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4196 obj->SetAccessor(v8_str("y"), YGetter, YSetter);
4197 LocalContext context;
4198 v8::Handle<v8::Object> holder = obj->NewInstance();
4199 context->Global()->Set(v8_str("holder"), holder);
4200 v8::Handle<Value> result = CompileRun(
4201 "holder.y = 11; holder.y = 12; holder.y");
4202 CHECK_EQ(12, result->Uint32Value());
4203}
4204
4205
4206THREADED_TEST(TypeSwitch) {
4207 v8::HandleScope scope;
4208 v8::Handle<v8::FunctionTemplate> templ1 = v8::FunctionTemplate::New();
4209 v8::Handle<v8::FunctionTemplate> templ2 = v8::FunctionTemplate::New();
4210 v8::Handle<v8::FunctionTemplate> templ3 = v8::FunctionTemplate::New();
4211 v8::Handle<v8::FunctionTemplate> templs[3] = { templ1, templ2, templ3 };
4212 v8::Handle<v8::TypeSwitch> type_switch = v8::TypeSwitch::New(3, templs);
4213 LocalContext context;
4214 v8::Handle<v8::Object> obj0 = v8::Object::New();
4215 v8::Handle<v8::Object> obj1 = templ1->GetFunction()->NewInstance();
4216 v8::Handle<v8::Object> obj2 = templ2->GetFunction()->NewInstance();
4217 v8::Handle<v8::Object> obj3 = templ3->GetFunction()->NewInstance();
4218 for (int i = 0; i < 10; i++) {
4219 CHECK_EQ(0, type_switch->match(obj0));
4220 CHECK_EQ(1, type_switch->match(obj1));
4221 CHECK_EQ(2, type_switch->match(obj2));
4222 CHECK_EQ(3, type_switch->match(obj3));
4223 CHECK_EQ(3, type_switch->match(obj3));
4224 CHECK_EQ(2, type_switch->match(obj2));
4225 CHECK_EQ(1, type_switch->match(obj1));
4226 CHECK_EQ(0, type_switch->match(obj0));
4227 }
4228}
4229
4230
4231// For use within the TestSecurityHandler() test.
4232static bool g_security_callback_result = false;
4233static bool NamedSecurityTestCallback(Local<v8::Object> global,
4234 Local<Value> name,
4235 v8::AccessType type,
4236 Local<Value> data) {
4237 // Always allow read access.
4238 if (type == v8::ACCESS_GET)
4239 return true;
4240
4241 // Sometimes allow other access.
4242 return g_security_callback_result;
4243}
4244
4245
4246static bool IndexedSecurityTestCallback(Local<v8::Object> global,
4247 uint32_t key,
4248 v8::AccessType type,
4249 Local<Value> data) {
4250 // Always allow read access.
4251 if (type == v8::ACCESS_GET)
4252 return true;
4253
4254 // Sometimes allow other access.
4255 return g_security_callback_result;
4256}
4257
4258
4259static int trouble_nesting = 0;
4260static v8::Handle<Value> TroubleCallback(const v8::Arguments& args) {
4261 ApiTestFuzzer::Fuzz();
4262 trouble_nesting++;
4263
4264 // Call a JS function that throws an uncaught exception.
4265 Local<v8::Object> arg_this = Context::GetCurrent()->Global();
4266 Local<Value> trouble_callee = (trouble_nesting == 3) ?
4267 arg_this->Get(v8_str("trouble_callee")) :
4268 arg_this->Get(v8_str("trouble_caller"));
4269 CHECK(trouble_callee->IsFunction());
4270 return Function::Cast(*trouble_callee)->Call(arg_this, 0, NULL);
4271}
4272
4273
4274static int report_count = 0;
4275static void ApiUncaughtExceptionTestListener(v8::Handle<v8::Message>,
4276 v8::Handle<Value>) {
4277 report_count++;
4278}
4279
4280
4281// Counts uncaught exceptions, but other tests running in parallel
4282// also have uncaught exceptions.
4283TEST(ApiUncaughtException) {
4284 report_count = 0;
4285 v8::HandleScope scope;
4286 LocalContext env;
4287 v8::V8::AddMessageListener(ApiUncaughtExceptionTestListener);
4288
4289 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback);
4290 v8::Local<v8::Object> global = env->Global();
4291 global->Set(v8_str("trouble"), fun->GetFunction());
4292
4293 Script::Compile(v8_str("function trouble_callee() {"
4294 " var x = null;"
4295 " return x.foo;"
4296 "};"
4297 "function trouble_caller() {"
4298 " trouble();"
4299 "};"))->Run();
4300 Local<Value> trouble = global->Get(v8_str("trouble"));
4301 CHECK(trouble->IsFunction());
4302 Local<Value> trouble_callee = global->Get(v8_str("trouble_callee"));
4303 CHECK(trouble_callee->IsFunction());
4304 Local<Value> trouble_caller = global->Get(v8_str("trouble_caller"));
4305 CHECK(trouble_caller->IsFunction());
4306 Function::Cast(*trouble_caller)->Call(global, 0, NULL);
4307 CHECK_EQ(1, report_count);
4308 v8::V8::RemoveMessageListeners(ApiUncaughtExceptionTestListener);
4309}
4310
Leon Clarke4515c472010-02-03 11:58:03 +00004311static const char* script_resource_name = "ExceptionInNativeScript.js";
4312static void ExceptionInNativeScriptTestListener(v8::Handle<v8::Message> message,
4313 v8::Handle<Value>) {
4314 v8::Handle<v8::Value> name_val = message->GetScriptResourceName();
4315 CHECK(!name_val.IsEmpty() && name_val->IsString());
4316 v8::String::AsciiValue name(message->GetScriptResourceName());
4317 CHECK_EQ(script_resource_name, *name);
4318 CHECK_EQ(3, message->GetLineNumber());
4319 v8::String::AsciiValue source_line(message->GetSourceLine());
4320 CHECK_EQ(" new o.foo();", *source_line);
4321}
4322
4323TEST(ExceptionInNativeScript) {
4324 v8::HandleScope scope;
4325 LocalContext env;
4326 v8::V8::AddMessageListener(ExceptionInNativeScriptTestListener);
4327
4328 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback);
4329 v8::Local<v8::Object> global = env->Global();
4330 global->Set(v8_str("trouble"), fun->GetFunction());
4331
4332 Script::Compile(v8_str("function trouble() {\n"
4333 " var o = {};\n"
4334 " new o.foo();\n"
4335 "};"), v8::String::New(script_resource_name))->Run();
4336 Local<Value> trouble = global->Get(v8_str("trouble"));
4337 CHECK(trouble->IsFunction());
4338 Function::Cast(*trouble)->Call(global, 0, NULL);
4339 v8::V8::RemoveMessageListeners(ExceptionInNativeScriptTestListener);
4340}
4341
Steve Blocka7e24c12009-10-30 11:49:00 +00004342
4343TEST(CompilationErrorUsingTryCatchHandler) {
4344 v8::HandleScope scope;
4345 LocalContext env;
4346 v8::TryCatch try_catch;
4347 Script::Compile(v8_str("This doesn't &*&@#$&*^ compile."));
4348 CHECK_NE(NULL, *try_catch.Exception());
4349 CHECK(try_catch.HasCaught());
4350}
4351
4352
4353TEST(TryCatchFinallyUsingTryCatchHandler) {
4354 v8::HandleScope scope;
4355 LocalContext env;
4356 v8::TryCatch try_catch;
4357 Script::Compile(v8_str("try { throw ''; } catch (e) {}"))->Run();
4358 CHECK(!try_catch.HasCaught());
4359 Script::Compile(v8_str("try { throw ''; } finally {}"))->Run();
4360 CHECK(try_catch.HasCaught());
4361 try_catch.Reset();
4362 Script::Compile(v8_str("(function() {"
4363 "try { throw ''; } finally { return; }"
4364 "})()"))->Run();
4365 CHECK(!try_catch.HasCaught());
4366 Script::Compile(v8_str("(function()"
4367 " { try { throw ''; } finally { throw 0; }"
4368 "})()"))->Run();
4369 CHECK(try_catch.HasCaught());
4370}
4371
4372
4373// SecurityHandler can't be run twice
4374TEST(SecurityHandler) {
4375 v8::HandleScope scope0;
4376 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
4377 global_template->SetAccessCheckCallbacks(NamedSecurityTestCallback,
4378 IndexedSecurityTestCallback);
4379 // Create an environment
4380 v8::Persistent<Context> context0 =
4381 Context::New(NULL, global_template);
4382 context0->Enter();
4383
4384 v8::Handle<v8::Object> global0 = context0->Global();
4385 v8::Handle<Script> script0 = v8_compile("foo = 111");
4386 script0->Run();
4387 global0->Set(v8_str("0"), v8_num(999));
4388 v8::Handle<Value> foo0 = global0->Get(v8_str("foo"));
4389 CHECK_EQ(111, foo0->Int32Value());
4390 v8::Handle<Value> z0 = global0->Get(v8_str("0"));
4391 CHECK_EQ(999, z0->Int32Value());
4392
4393 // Create another environment, should fail security checks.
4394 v8::HandleScope scope1;
4395
4396 v8::Persistent<Context> context1 =
4397 Context::New(NULL, global_template);
4398 context1->Enter();
4399
4400 v8::Handle<v8::Object> global1 = context1->Global();
4401 global1->Set(v8_str("othercontext"), global0);
4402 // This set will fail the security check.
4403 v8::Handle<Script> script1 =
4404 v8_compile("othercontext.foo = 222; othercontext[0] = 888;");
4405 script1->Run();
4406 // This read will pass the security check.
4407 v8::Handle<Value> foo1 = global0->Get(v8_str("foo"));
4408 CHECK_EQ(111, foo1->Int32Value());
4409 // This read will pass the security check.
4410 v8::Handle<Value> z1 = global0->Get(v8_str("0"));
4411 CHECK_EQ(999, z1->Int32Value());
4412
4413 // Create another environment, should pass security checks.
4414 { g_security_callback_result = true; // allow security handler to pass.
4415 v8::HandleScope scope2;
4416 LocalContext context2;
4417 v8::Handle<v8::Object> global2 = context2->Global();
4418 global2->Set(v8_str("othercontext"), global0);
4419 v8::Handle<Script> script2 =
4420 v8_compile("othercontext.foo = 333; othercontext[0] = 888;");
4421 script2->Run();
4422 v8::Handle<Value> foo2 = global0->Get(v8_str("foo"));
4423 CHECK_EQ(333, foo2->Int32Value());
4424 v8::Handle<Value> z2 = global0->Get(v8_str("0"));
4425 CHECK_EQ(888, z2->Int32Value());
4426 }
4427
4428 context1->Exit();
4429 context1.Dispose();
4430
4431 context0->Exit();
4432 context0.Dispose();
4433}
4434
4435
4436THREADED_TEST(SecurityChecks) {
4437 v8::HandleScope handle_scope;
4438 LocalContext env1;
4439 v8::Persistent<Context> env2 = Context::New();
4440
4441 Local<Value> foo = v8_str("foo");
4442 Local<Value> bar = v8_str("bar");
4443
4444 // Set to the same domain.
4445 env1->SetSecurityToken(foo);
4446
4447 // Create a function in env1.
4448 Script::Compile(v8_str("spy=function(){return spy;}"))->Run();
4449 Local<Value> spy = env1->Global()->Get(v8_str("spy"));
4450 CHECK(spy->IsFunction());
4451
4452 // Create another function accessing global objects.
4453 Script::Compile(v8_str("spy2=function(){return new this.Array();}"))->Run();
4454 Local<Value> spy2 = env1->Global()->Get(v8_str("spy2"));
4455 CHECK(spy2->IsFunction());
4456
4457 // Switch to env2 in the same domain and invoke spy on env2.
4458 {
4459 env2->SetSecurityToken(foo);
4460 // Enter env2
4461 Context::Scope scope_env2(env2);
4462 Local<Value> result = Function::Cast(*spy)->Call(env2->Global(), 0, NULL);
4463 CHECK(result->IsFunction());
4464 }
4465
4466 {
4467 env2->SetSecurityToken(bar);
4468 Context::Scope scope_env2(env2);
4469
4470 // Call cross_domain_call, it should throw an exception
4471 v8::TryCatch try_catch;
4472 Function::Cast(*spy2)->Call(env2->Global(), 0, NULL);
4473 CHECK(try_catch.HasCaught());
4474 }
4475
4476 env2.Dispose();
4477}
4478
4479
4480// Regression test case for issue 1183439.
4481THREADED_TEST(SecurityChecksForPrototypeChain) {
4482 v8::HandleScope scope;
4483 LocalContext current;
4484 v8::Persistent<Context> other = Context::New();
4485
4486 // Change context to be able to get to the Object function in the
4487 // other context without hitting the security checks.
4488 v8::Local<Value> other_object;
4489 { Context::Scope scope(other);
4490 other_object = other->Global()->Get(v8_str("Object"));
4491 other->Global()->Set(v8_num(42), v8_num(87));
4492 }
4493
4494 current->Global()->Set(v8_str("other"), other->Global());
4495 CHECK(v8_compile("other")->Run()->Equals(other->Global()));
4496
4497 // Make sure the security check fails here and we get an undefined
4498 // result instead of getting the Object function. Repeat in a loop
4499 // to make sure to exercise the IC code.
4500 v8::Local<Script> access_other0 = v8_compile("other.Object");
4501 v8::Local<Script> access_other1 = v8_compile("other[42]");
4502 for (int i = 0; i < 5; i++) {
4503 CHECK(!access_other0->Run()->Equals(other_object));
4504 CHECK(access_other0->Run()->IsUndefined());
4505 CHECK(!access_other1->Run()->Equals(v8_num(87)));
4506 CHECK(access_other1->Run()->IsUndefined());
4507 }
4508
4509 // Create an object that has 'other' in its prototype chain and make
4510 // sure we cannot access the Object function indirectly through
4511 // that. Repeat in a loop to make sure to exercise the IC code.
4512 v8_compile("function F() { };"
4513 "F.prototype = other;"
4514 "var f = new F();")->Run();
4515 v8::Local<Script> access_f0 = v8_compile("f.Object");
4516 v8::Local<Script> access_f1 = v8_compile("f[42]");
4517 for (int j = 0; j < 5; j++) {
4518 CHECK(!access_f0->Run()->Equals(other_object));
4519 CHECK(access_f0->Run()->IsUndefined());
4520 CHECK(!access_f1->Run()->Equals(v8_num(87)));
4521 CHECK(access_f1->Run()->IsUndefined());
4522 }
4523
4524 // Now it gets hairy: Set the prototype for the other global object
4525 // to be the current global object. The prototype chain for 'f' now
4526 // goes through 'other' but ends up in the current global object.
4527 { Context::Scope scope(other);
4528 other->Global()->Set(v8_str("__proto__"), current->Global());
4529 }
4530 // Set a named and an index property on the current global
4531 // object. To force the lookup to go through the other global object,
4532 // the properties must not exist in the other global object.
4533 current->Global()->Set(v8_str("foo"), v8_num(100));
4534 current->Global()->Set(v8_num(99), v8_num(101));
4535 // Try to read the properties from f and make sure that the access
4536 // gets stopped by the security checks on the other global object.
4537 Local<Script> access_f2 = v8_compile("f.foo");
4538 Local<Script> access_f3 = v8_compile("f[99]");
4539 for (int k = 0; k < 5; k++) {
4540 CHECK(!access_f2->Run()->Equals(v8_num(100)));
4541 CHECK(access_f2->Run()->IsUndefined());
4542 CHECK(!access_f3->Run()->Equals(v8_num(101)));
4543 CHECK(access_f3->Run()->IsUndefined());
4544 }
4545 other.Dispose();
4546}
4547
4548
4549THREADED_TEST(CrossDomainDelete) {
4550 v8::HandleScope handle_scope;
4551 LocalContext env1;
4552 v8::Persistent<Context> env2 = Context::New();
4553
4554 Local<Value> foo = v8_str("foo");
4555 Local<Value> bar = v8_str("bar");
4556
4557 // Set to the same domain.
4558 env1->SetSecurityToken(foo);
4559 env2->SetSecurityToken(foo);
4560
4561 env1->Global()->Set(v8_str("prop"), v8_num(3));
4562 env2->Global()->Set(v8_str("env1"), env1->Global());
4563
4564 // Change env2 to a different domain and delete env1.prop.
4565 env2->SetSecurityToken(bar);
4566 {
4567 Context::Scope scope_env2(env2);
4568 Local<Value> result =
4569 Script::Compile(v8_str("delete env1.prop"))->Run();
4570 CHECK(result->IsFalse());
4571 }
4572
4573 // Check that env1.prop still exists.
4574 Local<Value> v = env1->Global()->Get(v8_str("prop"));
4575 CHECK(v->IsNumber());
4576 CHECK_EQ(3, v->Int32Value());
4577
4578 env2.Dispose();
4579}
4580
4581
4582THREADED_TEST(CrossDomainIsPropertyEnumerable) {
4583 v8::HandleScope handle_scope;
4584 LocalContext env1;
4585 v8::Persistent<Context> env2 = Context::New();
4586
4587 Local<Value> foo = v8_str("foo");
4588 Local<Value> bar = v8_str("bar");
4589
4590 // Set to the same domain.
4591 env1->SetSecurityToken(foo);
4592 env2->SetSecurityToken(foo);
4593
4594 env1->Global()->Set(v8_str("prop"), v8_num(3));
4595 env2->Global()->Set(v8_str("env1"), env1->Global());
4596
4597 // env1.prop is enumerable in env2.
4598 Local<String> test = v8_str("propertyIsEnumerable.call(env1, 'prop')");
4599 {
4600 Context::Scope scope_env2(env2);
4601 Local<Value> result = Script::Compile(test)->Run();
4602 CHECK(result->IsTrue());
4603 }
4604
4605 // Change env2 to a different domain and test again.
4606 env2->SetSecurityToken(bar);
4607 {
4608 Context::Scope scope_env2(env2);
4609 Local<Value> result = Script::Compile(test)->Run();
4610 CHECK(result->IsFalse());
4611 }
4612
4613 env2.Dispose();
4614}
4615
4616
4617THREADED_TEST(CrossDomainForIn) {
4618 v8::HandleScope handle_scope;
4619 LocalContext env1;
4620 v8::Persistent<Context> env2 = Context::New();
4621
4622 Local<Value> foo = v8_str("foo");
4623 Local<Value> bar = v8_str("bar");
4624
4625 // Set to the same domain.
4626 env1->SetSecurityToken(foo);
4627 env2->SetSecurityToken(foo);
4628
4629 env1->Global()->Set(v8_str("prop"), v8_num(3));
4630 env2->Global()->Set(v8_str("env1"), env1->Global());
4631
4632 // Change env2 to a different domain and set env1's global object
4633 // as the __proto__ of an object in env2 and enumerate properties
4634 // in for-in. It shouldn't enumerate properties on env1's global
4635 // object.
4636 env2->SetSecurityToken(bar);
4637 {
4638 Context::Scope scope_env2(env2);
4639 Local<Value> result =
4640 CompileRun("(function(){var obj = {'__proto__':env1};"
4641 "for (var p in obj)"
4642 " if (p == 'prop') return false;"
4643 "return true;})()");
4644 CHECK(result->IsTrue());
4645 }
4646 env2.Dispose();
4647}
4648
4649
4650TEST(ContextDetachGlobal) {
4651 v8::HandleScope handle_scope;
4652 LocalContext env1;
4653 v8::Persistent<Context> env2 = Context::New();
4654
4655 Local<v8::Object> global1 = env1->Global();
4656
4657 Local<Value> foo = v8_str("foo");
4658
4659 // Set to the same domain.
4660 env1->SetSecurityToken(foo);
4661 env2->SetSecurityToken(foo);
4662
4663 // Enter env2
4664 env2->Enter();
4665
Andrei Popescu74b3c142010-03-29 12:03:09 +01004666 // Create a function in env2 and add a reference to it in env1.
Steve Blocka7e24c12009-10-30 11:49:00 +00004667 Local<v8::Object> global2 = env2->Global();
4668 global2->Set(v8_str("prop"), v8::Integer::New(1));
4669 CompileRun("function getProp() {return prop;}");
4670
4671 env1->Global()->Set(v8_str("getProp"),
4672 global2->Get(v8_str("getProp")));
4673
Andrei Popescu74b3c142010-03-29 12:03:09 +01004674 // Detach env2's global, and reuse the global object of env2
Steve Blocka7e24c12009-10-30 11:49:00 +00004675 env2->Exit();
4676 env2->DetachGlobal();
4677 // env2 has a new global object.
4678 CHECK(!env2->Global()->Equals(global2));
4679
4680 v8::Persistent<Context> env3 =
4681 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2);
4682 env3->SetSecurityToken(v8_str("bar"));
4683 env3->Enter();
4684
4685 Local<v8::Object> global3 = env3->Global();
4686 CHECK_EQ(global2, global3);
4687 CHECK(global3->Get(v8_str("prop"))->IsUndefined());
4688 CHECK(global3->Get(v8_str("getProp"))->IsUndefined());
4689 global3->Set(v8_str("prop"), v8::Integer::New(-1));
4690 global3->Set(v8_str("prop2"), v8::Integer::New(2));
4691 env3->Exit();
4692
4693 // Call getProp in env1, and it should return the value 1
4694 {
4695 Local<Value> get_prop = global1->Get(v8_str("getProp"));
4696 CHECK(get_prop->IsFunction());
4697 v8::TryCatch try_catch;
4698 Local<Value> r = Function::Cast(*get_prop)->Call(global1, 0, NULL);
4699 CHECK(!try_catch.HasCaught());
4700 CHECK_EQ(1, r->Int32Value());
4701 }
4702
4703 // Check that env3 is not accessible from env1
4704 {
4705 Local<Value> r = global3->Get(v8_str("prop2"));
4706 CHECK(r->IsUndefined());
4707 }
4708
4709 env2.Dispose();
4710 env3.Dispose();
4711}
4712
4713
Andrei Popescu74b3c142010-03-29 12:03:09 +01004714TEST(DetachAndReattachGlobal) {
4715 v8::HandleScope scope;
4716 LocalContext env1;
4717
4718 // Create second environment.
4719 v8::Persistent<Context> env2 = Context::New();
4720
4721 Local<Value> foo = v8_str("foo");
4722
4723 // Set same security token for env1 and env2.
4724 env1->SetSecurityToken(foo);
4725 env2->SetSecurityToken(foo);
4726
4727 // Create a property on the global object in env2.
4728 {
4729 v8::Context::Scope scope(env2);
4730 env2->Global()->Set(v8_str("p"), v8::Integer::New(42));
4731 }
4732
4733 // Create a reference to env2 global from env1 global.
4734 env1->Global()->Set(v8_str("other"), env2->Global());
4735
4736 // Check that we have access to other.p in env2 from env1.
4737 Local<Value> result = CompileRun("other.p");
4738 CHECK(result->IsInt32());
4739 CHECK_EQ(42, result->Int32Value());
4740
4741 // Hold on to global from env2 and detach global from env2.
4742 Local<v8::Object> global2 = env2->Global();
4743 env2->DetachGlobal();
4744
4745 // Check that the global has been detached. No other.p property can
4746 // be found.
4747 result = CompileRun("other.p");
4748 CHECK(result->IsUndefined());
4749
4750 // Reuse global2 for env3.
4751 v8::Persistent<Context> env3 =
4752 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2);
4753 CHECK_EQ(global2, env3->Global());
4754
4755 // Start by using the same security token for env3 as for env1 and env2.
4756 env3->SetSecurityToken(foo);
4757
4758 // Create a property on the global object in env3.
4759 {
4760 v8::Context::Scope scope(env3);
4761 env3->Global()->Set(v8_str("p"), v8::Integer::New(24));
4762 }
4763
4764 // Check that other.p is now the property in env3 and that we have access.
4765 result = CompileRun("other.p");
4766 CHECK(result->IsInt32());
4767 CHECK_EQ(24, result->Int32Value());
4768
4769 // Change security token for env3 to something different from env1 and env2.
4770 env3->SetSecurityToken(v8_str("bar"));
4771
4772 // Check that we do not have access to other.p in env1. |other| is now
4773 // the global object for env3 which has a different security token,
4774 // so access should be blocked.
4775 result = CompileRun("other.p");
4776 CHECK(result->IsUndefined());
4777
4778 // Detach the global for env3 and reattach it to env2.
4779 env3->DetachGlobal();
4780 env2->ReattachGlobal(global2);
4781
4782 // Check that we have access to other.p again in env1. |other| is now
4783 // the global object for env2 which has the same security token as env1.
4784 result = CompileRun("other.p");
4785 CHECK(result->IsInt32());
4786 CHECK_EQ(42, result->Int32Value());
4787
4788 env2.Dispose();
4789 env3.Dispose();
4790}
4791
4792
Steve Blocka7e24c12009-10-30 11:49:00 +00004793static bool NamedAccessBlocker(Local<v8::Object> global,
4794 Local<Value> name,
4795 v8::AccessType type,
4796 Local<Value> data) {
4797 return Context::GetCurrent()->Global()->Equals(global);
4798}
4799
4800
4801static bool IndexedAccessBlocker(Local<v8::Object> global,
4802 uint32_t key,
4803 v8::AccessType type,
4804 Local<Value> data) {
4805 return Context::GetCurrent()->Global()->Equals(global);
4806}
4807
4808
4809static int g_echo_value = -1;
4810static v8::Handle<Value> EchoGetter(Local<String> name,
4811 const AccessorInfo& info) {
4812 return v8_num(g_echo_value);
4813}
4814
4815
4816static void EchoSetter(Local<String> name,
4817 Local<Value> value,
4818 const AccessorInfo&) {
4819 if (value->IsNumber())
4820 g_echo_value = value->Int32Value();
4821}
4822
4823
4824static v8::Handle<Value> UnreachableGetter(Local<String> name,
4825 const AccessorInfo& info) {
4826 CHECK(false); // This function should not be called..
4827 return v8::Undefined();
4828}
4829
4830
4831static void UnreachableSetter(Local<String>, Local<Value>,
4832 const AccessorInfo&) {
4833 CHECK(false); // This function should nto be called.
4834}
4835
4836
4837THREADED_TEST(AccessControl) {
4838 v8::HandleScope handle_scope;
4839 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
4840
4841 global_template->SetAccessCheckCallbacks(NamedAccessBlocker,
4842 IndexedAccessBlocker);
4843
4844 // Add an accessor accessible by cross-domain JS code.
4845 global_template->SetAccessor(
4846 v8_str("accessible_prop"),
4847 EchoGetter, EchoSetter,
4848 v8::Handle<Value>(),
4849 v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE));
4850
4851 // Add an accessor that is not accessible by cross-domain JS code.
4852 global_template->SetAccessor(v8_str("blocked_prop"),
4853 UnreachableGetter, UnreachableSetter,
4854 v8::Handle<Value>(),
4855 v8::DEFAULT);
4856
4857 // Create an environment
4858 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
4859 context0->Enter();
4860
4861 v8::Handle<v8::Object> global0 = context0->Global();
4862
4863 v8::HandleScope scope1;
4864
4865 v8::Persistent<Context> context1 = Context::New();
4866 context1->Enter();
4867
4868 v8::Handle<v8::Object> global1 = context1->Global();
4869 global1->Set(v8_str("other"), global0);
4870
4871 v8::Handle<Value> value;
4872
4873 // Access blocked property
4874 value = v8_compile("other.blocked_prop = 1")->Run();
4875 value = v8_compile("other.blocked_prop")->Run();
4876 CHECK(value->IsUndefined());
4877
4878 value = v8_compile("propertyIsEnumerable.call(other, 'blocked_prop')")->Run();
4879 CHECK(value->IsFalse());
4880
4881 // Access accessible property
4882 value = v8_compile("other.accessible_prop = 3")->Run();
4883 CHECK(value->IsNumber());
4884 CHECK_EQ(3, value->Int32Value());
Andrei Popescu31002712010-02-23 13:46:05 +00004885 CHECK_EQ(3, g_echo_value);
Steve Blocka7e24c12009-10-30 11:49:00 +00004886
4887 value = v8_compile("other.accessible_prop")->Run();
4888 CHECK(value->IsNumber());
4889 CHECK_EQ(3, value->Int32Value());
4890
4891 value =
4892 v8_compile("propertyIsEnumerable.call(other, 'accessible_prop')")->Run();
4893 CHECK(value->IsTrue());
4894
4895 // Enumeration doesn't enumerate accessors from inaccessible objects in
4896 // the prototype chain even if the accessors are in themselves accessible.
4897 Local<Value> result =
4898 CompileRun("(function(){var obj = {'__proto__':other};"
4899 "for (var p in obj)"
4900 " if (p == 'accessible_prop' || p == 'blocked_prop') {"
4901 " return false;"
4902 " }"
4903 "return true;})()");
4904 CHECK(result->IsTrue());
4905
4906 context1->Exit();
4907 context0->Exit();
4908 context1.Dispose();
4909 context0.Dispose();
4910}
4911
4912
Leon Clarke4515c472010-02-03 11:58:03 +00004913static bool GetOwnPropertyNamesNamedBlocker(Local<v8::Object> global,
4914 Local<Value> name,
4915 v8::AccessType type,
4916 Local<Value> data) {
4917 return false;
4918}
4919
4920
4921static bool GetOwnPropertyNamesIndexedBlocker(Local<v8::Object> global,
4922 uint32_t key,
4923 v8::AccessType type,
4924 Local<Value> data) {
4925 return false;
4926}
4927
4928
4929THREADED_TEST(AccessControlGetOwnPropertyNames) {
4930 v8::HandleScope handle_scope;
4931 v8::Handle<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New();
4932
4933 obj_template->Set(v8_str("x"), v8::Integer::New(42));
4934 obj_template->SetAccessCheckCallbacks(GetOwnPropertyNamesNamedBlocker,
4935 GetOwnPropertyNamesIndexedBlocker);
4936
4937 // Create an environment
4938 v8::Persistent<Context> context0 = Context::New(NULL, obj_template);
4939 context0->Enter();
4940
4941 v8::Handle<v8::Object> global0 = context0->Global();
4942
4943 v8::HandleScope scope1;
4944
4945 v8::Persistent<Context> context1 = Context::New();
4946 context1->Enter();
4947
4948 v8::Handle<v8::Object> global1 = context1->Global();
4949 global1->Set(v8_str("other"), global0);
4950 global1->Set(v8_str("object"), obj_template->NewInstance());
4951
4952 v8::Handle<Value> value;
4953
4954 // Attempt to get the property names of the other global object and
4955 // of an object that requires access checks. Accessing the other
4956 // global object should be blocked by access checks on the global
4957 // proxy object. Accessing the object that requires access checks
4958 // is blocked by the access checks on the object itself.
4959 value = CompileRun("Object.getOwnPropertyNames(other).length == 0");
4960 CHECK(value->IsTrue());
4961
4962 value = CompileRun("Object.getOwnPropertyNames(object).length == 0");
4963 CHECK(value->IsTrue());
4964
4965 context1->Exit();
4966 context0->Exit();
4967 context1.Dispose();
4968 context0.Dispose();
4969}
4970
4971
Steve Blocka7e24c12009-10-30 11:49:00 +00004972static v8::Handle<Value> ConstTenGetter(Local<String> name,
4973 const AccessorInfo& info) {
4974 return v8_num(10);
4975}
4976
4977
4978THREADED_TEST(CrossDomainAccessors) {
4979 v8::HandleScope handle_scope;
4980
4981 v8::Handle<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New();
4982
4983 v8::Handle<v8::ObjectTemplate> global_template =
4984 func_template->InstanceTemplate();
4985
4986 v8::Handle<v8::ObjectTemplate> proto_template =
4987 func_template->PrototypeTemplate();
4988
4989 // Add an accessor to proto that's accessible by cross-domain JS code.
4990 proto_template->SetAccessor(v8_str("accessible"),
4991 ConstTenGetter, 0,
4992 v8::Handle<Value>(),
4993 v8::ALL_CAN_READ);
4994
4995 // Add an accessor that is not accessible by cross-domain JS code.
4996 global_template->SetAccessor(v8_str("unreachable"),
4997 UnreachableGetter, 0,
4998 v8::Handle<Value>(),
4999 v8::DEFAULT);
5000
5001 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
5002 context0->Enter();
5003
5004 Local<v8::Object> global = context0->Global();
5005 // Add a normal property that shadows 'accessible'
5006 global->Set(v8_str("accessible"), v8_num(11));
5007
5008 // Enter a new context.
5009 v8::HandleScope scope1;
5010 v8::Persistent<Context> context1 = Context::New();
5011 context1->Enter();
5012
5013 v8::Handle<v8::Object> global1 = context1->Global();
5014 global1->Set(v8_str("other"), global);
5015
5016 // Should return 10, instead of 11
5017 v8::Handle<Value> value = v8_compile("other.accessible")->Run();
5018 CHECK(value->IsNumber());
5019 CHECK_EQ(10, value->Int32Value());
5020
5021 value = v8_compile("other.unreachable")->Run();
5022 CHECK(value->IsUndefined());
5023
5024 context1->Exit();
5025 context0->Exit();
5026 context1.Dispose();
5027 context0.Dispose();
5028}
5029
5030
5031static int named_access_count = 0;
5032static int indexed_access_count = 0;
5033
5034static bool NamedAccessCounter(Local<v8::Object> global,
5035 Local<Value> name,
5036 v8::AccessType type,
5037 Local<Value> data) {
5038 named_access_count++;
5039 return true;
5040}
5041
5042
5043static bool IndexedAccessCounter(Local<v8::Object> global,
5044 uint32_t key,
5045 v8::AccessType type,
5046 Local<Value> data) {
5047 indexed_access_count++;
5048 return true;
5049}
5050
5051
5052// This one is too easily disturbed by other tests.
5053TEST(AccessControlIC) {
5054 named_access_count = 0;
5055 indexed_access_count = 0;
5056
5057 v8::HandleScope handle_scope;
5058
5059 // Create an environment.
5060 v8::Persistent<Context> context0 = Context::New();
5061 context0->Enter();
5062
5063 // Create an object that requires access-check functions to be
5064 // called for cross-domain access.
5065 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
5066 object_template->SetAccessCheckCallbacks(NamedAccessCounter,
5067 IndexedAccessCounter);
5068 Local<v8::Object> object = object_template->NewInstance();
5069
5070 v8::HandleScope scope1;
5071
5072 // Create another environment.
5073 v8::Persistent<Context> context1 = Context::New();
5074 context1->Enter();
5075
5076 // Make easy access to the object from the other environment.
5077 v8::Handle<v8::Object> global1 = context1->Global();
5078 global1->Set(v8_str("obj"), object);
5079
5080 v8::Handle<Value> value;
5081
5082 // Check that the named access-control function is called every time.
5083 CompileRun("function testProp(obj) {"
5084 " for (var i = 0; i < 10; i++) obj.prop = 1;"
5085 " for (var j = 0; j < 10; j++) obj.prop;"
5086 " return obj.prop"
5087 "}");
5088 value = CompileRun("testProp(obj)");
5089 CHECK(value->IsNumber());
5090 CHECK_EQ(1, value->Int32Value());
5091 CHECK_EQ(21, named_access_count);
5092
5093 // Check that the named access-control function is called every time.
5094 CompileRun("var p = 'prop';"
5095 "function testKeyed(obj) {"
5096 " for (var i = 0; i < 10; i++) obj[p] = 1;"
5097 " for (var j = 0; j < 10; j++) obj[p];"
5098 " return obj[p];"
5099 "}");
5100 // Use obj which requires access checks. No inline caching is used
5101 // in that case.
5102 value = CompileRun("testKeyed(obj)");
5103 CHECK(value->IsNumber());
5104 CHECK_EQ(1, value->Int32Value());
5105 CHECK_EQ(42, named_access_count);
5106 // Force the inline caches into generic state and try again.
5107 CompileRun("testKeyed({ a: 0 })");
5108 CompileRun("testKeyed({ b: 0 })");
5109 value = CompileRun("testKeyed(obj)");
5110 CHECK(value->IsNumber());
5111 CHECK_EQ(1, value->Int32Value());
5112 CHECK_EQ(63, named_access_count);
5113
5114 // Check that the indexed access-control function is called every time.
5115 CompileRun("function testIndexed(obj) {"
5116 " for (var i = 0; i < 10; i++) obj[0] = 1;"
5117 " for (var j = 0; j < 10; j++) obj[0];"
5118 " return obj[0]"
5119 "}");
5120 value = CompileRun("testIndexed(obj)");
5121 CHECK(value->IsNumber());
5122 CHECK_EQ(1, value->Int32Value());
5123 CHECK_EQ(21, indexed_access_count);
5124 // Force the inline caches into generic state.
5125 CompileRun("testIndexed(new Array(1))");
5126 // Test that the indexed access check is called.
5127 value = CompileRun("testIndexed(obj)");
5128 CHECK(value->IsNumber());
5129 CHECK_EQ(1, value->Int32Value());
5130 CHECK_EQ(42, indexed_access_count);
5131
5132 // Check that the named access check is called when invoking
5133 // functions on an object that requires access checks.
5134 CompileRun("obj.f = function() {}");
5135 CompileRun("function testCallNormal(obj) {"
5136 " for (var i = 0; i < 10; i++) obj.f();"
5137 "}");
5138 CompileRun("testCallNormal(obj)");
5139 CHECK_EQ(74, named_access_count);
5140
5141 // Force obj into slow case.
5142 value = CompileRun("delete obj.prop");
5143 CHECK(value->BooleanValue());
5144 // Force inline caches into dictionary probing mode.
5145 CompileRun("var o = { x: 0 }; delete o.x; testProp(o);");
5146 // Test that the named access check is called.
5147 value = CompileRun("testProp(obj);");
5148 CHECK(value->IsNumber());
5149 CHECK_EQ(1, value->Int32Value());
5150 CHECK_EQ(96, named_access_count);
5151
5152 // Force the call inline cache into dictionary probing mode.
5153 CompileRun("o.f = function() {}; testCallNormal(o)");
5154 // Test that the named access check is still called for each
5155 // invocation of the function.
5156 value = CompileRun("testCallNormal(obj)");
5157 CHECK_EQ(106, named_access_count);
5158
5159 context1->Exit();
5160 context0->Exit();
5161 context1.Dispose();
5162 context0.Dispose();
5163}
5164
5165
5166static bool NamedAccessFlatten(Local<v8::Object> global,
5167 Local<Value> name,
5168 v8::AccessType type,
5169 Local<Value> data) {
5170 char buf[100];
5171 int len;
5172
5173 CHECK(name->IsString());
5174
5175 memset(buf, 0x1, sizeof(buf));
Steve Block6ded16b2010-05-10 14:33:55 +01005176 len = name.As<String>()->WriteAscii(buf);
Steve Blocka7e24c12009-10-30 11:49:00 +00005177 CHECK_EQ(4, len);
5178
5179 uint16_t buf2[100];
5180
5181 memset(buf, 0x1, sizeof(buf));
Steve Block6ded16b2010-05-10 14:33:55 +01005182 len = name.As<String>()->Write(buf2);
Steve Blocka7e24c12009-10-30 11:49:00 +00005183 CHECK_EQ(4, len);
5184
5185 return true;
5186}
5187
5188
5189static bool IndexedAccessFlatten(Local<v8::Object> global,
5190 uint32_t key,
5191 v8::AccessType type,
5192 Local<Value> data) {
5193 return true;
5194}
5195
5196
5197// Regression test. In access checks, operations that may cause
5198// garbage collection are not allowed. It used to be the case that
5199// using the Write operation on a string could cause a garbage
5200// collection due to flattening of the string. This is no longer the
5201// case.
5202THREADED_TEST(AccessControlFlatten) {
5203 named_access_count = 0;
5204 indexed_access_count = 0;
5205
5206 v8::HandleScope handle_scope;
5207
5208 // Create an environment.
5209 v8::Persistent<Context> context0 = Context::New();
5210 context0->Enter();
5211
5212 // Create an object that requires access-check functions to be
5213 // called for cross-domain access.
5214 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
5215 object_template->SetAccessCheckCallbacks(NamedAccessFlatten,
5216 IndexedAccessFlatten);
5217 Local<v8::Object> object = object_template->NewInstance();
5218
5219 v8::HandleScope scope1;
5220
5221 // Create another environment.
5222 v8::Persistent<Context> context1 = Context::New();
5223 context1->Enter();
5224
5225 // Make easy access to the object from the other environment.
5226 v8::Handle<v8::Object> global1 = context1->Global();
5227 global1->Set(v8_str("obj"), object);
5228
5229 v8::Handle<Value> value;
5230
5231 value = v8_compile("var p = 'as' + 'df';")->Run();
5232 value = v8_compile("obj[p];")->Run();
5233
5234 context1->Exit();
5235 context0->Exit();
5236 context1.Dispose();
5237 context0.Dispose();
5238}
5239
5240
5241static v8::Handle<Value> AccessControlNamedGetter(
5242 Local<String>, const AccessorInfo&) {
5243 return v8::Integer::New(42);
5244}
5245
5246
5247static v8::Handle<Value> AccessControlNamedSetter(
5248 Local<String>, Local<Value> value, const AccessorInfo&) {
5249 return value;
5250}
5251
5252
5253static v8::Handle<Value> AccessControlIndexedGetter(
5254 uint32_t index,
5255 const AccessorInfo& info) {
5256 return v8_num(42);
5257}
5258
5259
5260static v8::Handle<Value> AccessControlIndexedSetter(
5261 uint32_t, Local<Value> value, const AccessorInfo&) {
5262 return value;
5263}
5264
5265
5266THREADED_TEST(AccessControlInterceptorIC) {
5267 named_access_count = 0;
5268 indexed_access_count = 0;
5269
5270 v8::HandleScope handle_scope;
5271
5272 // Create an environment.
5273 v8::Persistent<Context> context0 = Context::New();
5274 context0->Enter();
5275
5276 // Create an object that requires access-check functions to be
5277 // called for cross-domain access. The object also has interceptors
5278 // interceptor.
5279 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
5280 object_template->SetAccessCheckCallbacks(NamedAccessCounter,
5281 IndexedAccessCounter);
5282 object_template->SetNamedPropertyHandler(AccessControlNamedGetter,
5283 AccessControlNamedSetter);
5284 object_template->SetIndexedPropertyHandler(AccessControlIndexedGetter,
5285 AccessControlIndexedSetter);
5286 Local<v8::Object> object = object_template->NewInstance();
5287
5288 v8::HandleScope scope1;
5289
5290 // Create another environment.
5291 v8::Persistent<Context> context1 = Context::New();
5292 context1->Enter();
5293
5294 // Make easy access to the object from the other environment.
5295 v8::Handle<v8::Object> global1 = context1->Global();
5296 global1->Set(v8_str("obj"), object);
5297
5298 v8::Handle<Value> value;
5299
5300 // Check that the named access-control function is called every time
5301 // eventhough there is an interceptor on the object.
5302 value = v8_compile("for (var i = 0; i < 10; i++) obj.x = 1;")->Run();
5303 value = v8_compile("for (var i = 0; i < 10; i++) obj.x;"
5304 "obj.x")->Run();
5305 CHECK(value->IsNumber());
5306 CHECK_EQ(42, value->Int32Value());
5307 CHECK_EQ(21, named_access_count);
5308
5309 value = v8_compile("var p = 'x';")->Run();
5310 value = v8_compile("for (var i = 0; i < 10; i++) obj[p] = 1;")->Run();
5311 value = v8_compile("for (var i = 0; i < 10; i++) obj[p];"
5312 "obj[p]")->Run();
5313 CHECK(value->IsNumber());
5314 CHECK_EQ(42, value->Int32Value());
5315 CHECK_EQ(42, named_access_count);
5316
5317 // Check that the indexed access-control function is called every
5318 // time eventhough there is an interceptor on the object.
5319 value = v8_compile("for (var i = 0; i < 10; i++) obj[0] = 1;")->Run();
5320 value = v8_compile("for (var i = 0; i < 10; i++) obj[0];"
5321 "obj[0]")->Run();
5322 CHECK(value->IsNumber());
5323 CHECK_EQ(42, value->Int32Value());
5324 CHECK_EQ(21, indexed_access_count);
5325
5326 context1->Exit();
5327 context0->Exit();
5328 context1.Dispose();
5329 context0.Dispose();
5330}
5331
5332
5333THREADED_TEST(Version) {
5334 v8::V8::GetVersion();
5335}
5336
5337
5338static v8::Handle<Value> InstanceFunctionCallback(const v8::Arguments& args) {
5339 ApiTestFuzzer::Fuzz();
5340 return v8_num(12);
5341}
5342
5343
5344THREADED_TEST(InstanceProperties) {
5345 v8::HandleScope handle_scope;
5346 LocalContext context;
5347
5348 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5349 Local<ObjectTemplate> instance = t->InstanceTemplate();
5350
5351 instance->Set(v8_str("x"), v8_num(42));
5352 instance->Set(v8_str("f"),
5353 v8::FunctionTemplate::New(InstanceFunctionCallback));
5354
5355 Local<Value> o = t->GetFunction()->NewInstance();
5356
5357 context->Global()->Set(v8_str("i"), o);
5358 Local<Value> value = Script::Compile(v8_str("i.x"))->Run();
5359 CHECK_EQ(42, value->Int32Value());
5360
5361 value = Script::Compile(v8_str("i.f()"))->Run();
5362 CHECK_EQ(12, value->Int32Value());
5363}
5364
5365
5366static v8::Handle<Value>
5367GlobalObjectInstancePropertiesGet(Local<String> key, const AccessorInfo&) {
5368 ApiTestFuzzer::Fuzz();
5369 return v8::Handle<Value>();
5370}
5371
5372
5373THREADED_TEST(GlobalObjectInstanceProperties) {
5374 v8::HandleScope handle_scope;
5375
5376 Local<Value> global_object;
5377
5378 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5379 t->InstanceTemplate()->SetNamedPropertyHandler(
5380 GlobalObjectInstancePropertiesGet);
5381 Local<ObjectTemplate> instance_template = t->InstanceTemplate();
5382 instance_template->Set(v8_str("x"), v8_num(42));
5383 instance_template->Set(v8_str("f"),
5384 v8::FunctionTemplate::New(InstanceFunctionCallback));
5385
5386 {
5387 LocalContext env(NULL, instance_template);
5388 // Hold on to the global object so it can be used again in another
5389 // environment initialization.
5390 global_object = env->Global();
5391
5392 Local<Value> value = Script::Compile(v8_str("x"))->Run();
5393 CHECK_EQ(42, value->Int32Value());
5394 value = Script::Compile(v8_str("f()"))->Run();
5395 CHECK_EQ(12, value->Int32Value());
5396 }
5397
5398 {
5399 // Create new environment reusing the global object.
5400 LocalContext env(NULL, instance_template, global_object);
5401 Local<Value> value = Script::Compile(v8_str("x"))->Run();
5402 CHECK_EQ(42, value->Int32Value());
5403 value = Script::Compile(v8_str("f()"))->Run();
5404 CHECK_EQ(12, value->Int32Value());
5405 }
5406}
5407
5408
5409static v8::Handle<Value> ShadowFunctionCallback(const v8::Arguments& args) {
5410 ApiTestFuzzer::Fuzz();
5411 return v8_num(42);
5412}
5413
5414
5415static int shadow_y;
5416static int shadow_y_setter_call_count;
5417static int shadow_y_getter_call_count;
5418
5419
5420static void ShadowYSetter(Local<String>, Local<Value>, const AccessorInfo&) {
5421 shadow_y_setter_call_count++;
5422 shadow_y = 42;
5423}
5424
5425
5426static v8::Handle<Value> ShadowYGetter(Local<String> name,
5427 const AccessorInfo& info) {
5428 ApiTestFuzzer::Fuzz();
5429 shadow_y_getter_call_count++;
5430 return v8_num(shadow_y);
5431}
5432
5433
5434static v8::Handle<Value> ShadowIndexedGet(uint32_t index,
5435 const AccessorInfo& info) {
5436 return v8::Handle<Value>();
5437}
5438
5439
5440static v8::Handle<Value> ShadowNamedGet(Local<String> key,
5441 const AccessorInfo&) {
5442 return v8::Handle<Value>();
5443}
5444
5445
5446THREADED_TEST(ShadowObject) {
5447 shadow_y = shadow_y_setter_call_count = shadow_y_getter_call_count = 0;
5448 v8::HandleScope handle_scope;
5449
5450 Local<ObjectTemplate> global_template = v8::ObjectTemplate::New();
5451 LocalContext context(NULL, global_template);
5452
5453 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5454 t->InstanceTemplate()->SetNamedPropertyHandler(ShadowNamedGet);
5455 t->InstanceTemplate()->SetIndexedPropertyHandler(ShadowIndexedGet);
5456 Local<ObjectTemplate> proto = t->PrototypeTemplate();
5457 Local<ObjectTemplate> instance = t->InstanceTemplate();
5458
5459 // Only allow calls of f on instances of t.
5460 Local<v8::Signature> signature = v8::Signature::New(t);
5461 proto->Set(v8_str("f"),
5462 v8::FunctionTemplate::New(ShadowFunctionCallback,
5463 Local<Value>(),
5464 signature));
5465 proto->Set(v8_str("x"), v8_num(12));
5466
5467 instance->SetAccessor(v8_str("y"), ShadowYGetter, ShadowYSetter);
5468
5469 Local<Value> o = t->GetFunction()->NewInstance();
5470 context->Global()->Set(v8_str("__proto__"), o);
5471
5472 Local<Value> value =
5473 Script::Compile(v8_str("propertyIsEnumerable(0)"))->Run();
5474 CHECK(value->IsBoolean());
5475 CHECK(!value->BooleanValue());
5476
5477 value = Script::Compile(v8_str("x"))->Run();
5478 CHECK_EQ(12, value->Int32Value());
5479
5480 value = Script::Compile(v8_str("f()"))->Run();
5481 CHECK_EQ(42, value->Int32Value());
5482
5483 Script::Compile(v8_str("y = 42"))->Run();
5484 CHECK_EQ(1, shadow_y_setter_call_count);
5485 value = Script::Compile(v8_str("y"))->Run();
5486 CHECK_EQ(1, shadow_y_getter_call_count);
5487 CHECK_EQ(42, value->Int32Value());
5488}
5489
5490
5491THREADED_TEST(HiddenPrototype) {
5492 v8::HandleScope handle_scope;
5493 LocalContext context;
5494
5495 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New();
5496 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0));
5497 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New();
5498 t1->SetHiddenPrototype(true);
5499 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1));
5500 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New();
5501 t2->SetHiddenPrototype(true);
5502 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2));
5503 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New();
5504 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3));
5505
5506 Local<v8::Object> o0 = t0->GetFunction()->NewInstance();
5507 Local<v8::Object> o1 = t1->GetFunction()->NewInstance();
5508 Local<v8::Object> o2 = t2->GetFunction()->NewInstance();
5509 Local<v8::Object> o3 = t3->GetFunction()->NewInstance();
5510
5511 // Setting the prototype on an object skips hidden prototypes.
5512 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5513 o0->Set(v8_str("__proto__"), o1);
5514 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5515 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5516 o0->Set(v8_str("__proto__"), o2);
5517 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5518 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5519 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
5520 o0->Set(v8_str("__proto__"), o3);
5521 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5522 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5523 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
5524 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value());
5525
5526 // Getting the prototype of o0 should get the first visible one
5527 // which is o3. Therefore, z should not be defined on the prototype
5528 // object.
5529 Local<Value> proto = o0->Get(v8_str("__proto__"));
5530 CHECK(proto->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005531 CHECK(proto.As<v8::Object>()->Get(v8_str("z"))->IsUndefined());
Steve Blocka7e24c12009-10-30 11:49:00 +00005532}
5533
5534
Andrei Popescu402d9372010-02-26 13:31:12 +00005535THREADED_TEST(SetPrototype) {
5536 v8::HandleScope handle_scope;
5537 LocalContext context;
5538
5539 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New();
5540 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0));
5541 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New();
5542 t1->SetHiddenPrototype(true);
5543 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1));
5544 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New();
5545 t2->SetHiddenPrototype(true);
5546 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2));
5547 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New();
5548 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3));
5549
5550 Local<v8::Object> o0 = t0->GetFunction()->NewInstance();
5551 Local<v8::Object> o1 = t1->GetFunction()->NewInstance();
5552 Local<v8::Object> o2 = t2->GetFunction()->NewInstance();
5553 Local<v8::Object> o3 = t3->GetFunction()->NewInstance();
5554
5555 // Setting the prototype on an object does not skip hidden prototypes.
5556 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5557 CHECK(o0->SetPrototype(o1));
5558 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5559 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5560 CHECK(o1->SetPrototype(o2));
5561 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5562 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5563 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
5564 CHECK(o2->SetPrototype(o3));
5565 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5566 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5567 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
5568 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value());
5569
5570 // Getting the prototype of o0 should get the first visible one
5571 // which is o3. Therefore, z should not be defined on the prototype
5572 // object.
5573 Local<Value> proto = o0->Get(v8_str("__proto__"));
5574 CHECK(proto->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005575 CHECK_EQ(proto.As<v8::Object>(), o3);
Andrei Popescu402d9372010-02-26 13:31:12 +00005576
5577 // However, Object::GetPrototype ignores hidden prototype.
5578 Local<Value> proto0 = o0->GetPrototype();
5579 CHECK(proto0->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005580 CHECK_EQ(proto0.As<v8::Object>(), o1);
Andrei Popescu402d9372010-02-26 13:31:12 +00005581
5582 Local<Value> proto1 = o1->GetPrototype();
5583 CHECK(proto1->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005584 CHECK_EQ(proto1.As<v8::Object>(), o2);
Andrei Popescu402d9372010-02-26 13:31:12 +00005585
5586 Local<Value> proto2 = o2->GetPrototype();
5587 CHECK(proto2->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005588 CHECK_EQ(proto2.As<v8::Object>(), o3);
Andrei Popescu402d9372010-02-26 13:31:12 +00005589}
5590
5591
5592THREADED_TEST(SetPrototypeThrows) {
5593 v8::HandleScope handle_scope;
5594 LocalContext context;
5595
5596 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5597
5598 Local<v8::Object> o0 = t->GetFunction()->NewInstance();
5599 Local<v8::Object> o1 = t->GetFunction()->NewInstance();
5600
5601 CHECK(o0->SetPrototype(o1));
5602 // If setting the prototype leads to the cycle, SetPrototype should
5603 // return false and keep VM in sane state.
5604 v8::TryCatch try_catch;
5605 CHECK(!o1->SetPrototype(o0));
5606 CHECK(!try_catch.HasCaught());
5607 ASSERT(!i::Top::has_pending_exception());
5608
5609 CHECK_EQ(42, CompileRun("function f() { return 42; }; f()")->Int32Value());
5610}
5611
5612
Steve Blocka7e24c12009-10-30 11:49:00 +00005613THREADED_TEST(GetterSetterExceptions) {
5614 v8::HandleScope handle_scope;
5615 LocalContext context;
5616 CompileRun(
5617 "function Foo() { };"
5618 "function Throw() { throw 5; };"
5619 "var x = { };"
5620 "x.__defineSetter__('set', Throw);"
5621 "x.__defineGetter__('get', Throw);");
5622 Local<v8::Object> x =
5623 Local<v8::Object>::Cast(context->Global()->Get(v8_str("x")));
5624 v8::TryCatch try_catch;
5625 x->Set(v8_str("set"), v8::Integer::New(8));
5626 x->Get(v8_str("get"));
5627 x->Set(v8_str("set"), v8::Integer::New(8));
5628 x->Get(v8_str("get"));
5629 x->Set(v8_str("set"), v8::Integer::New(8));
5630 x->Get(v8_str("get"));
5631 x->Set(v8_str("set"), v8::Integer::New(8));
5632 x->Get(v8_str("get"));
5633}
5634
5635
5636THREADED_TEST(Constructor) {
5637 v8::HandleScope handle_scope;
5638 LocalContext context;
5639 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
5640 templ->SetClassName(v8_str("Fun"));
5641 Local<Function> cons = templ->GetFunction();
5642 context->Global()->Set(v8_str("Fun"), cons);
5643 Local<v8::Object> inst = cons->NewInstance();
5644 i::Handle<i::JSObject> obj = v8::Utils::OpenHandle(*inst);
5645 Local<Value> value = CompileRun("(new Fun()).constructor === Fun");
5646 CHECK(value->BooleanValue());
5647}
5648
5649THREADED_TEST(FunctionDescriptorException) {
5650 v8::HandleScope handle_scope;
5651 LocalContext context;
5652 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
5653 templ->SetClassName(v8_str("Fun"));
5654 Local<Function> cons = templ->GetFunction();
5655 context->Global()->Set(v8_str("Fun"), cons);
5656 Local<Value> value = CompileRun(
5657 "function test() {"
5658 " try {"
5659 " (new Fun()).blah()"
5660 " } catch (e) {"
5661 " var str = String(e);"
5662 " if (str.indexOf('TypeError') == -1) return 1;"
5663 " if (str.indexOf('[object Fun]') != -1) return 2;"
5664 " if (str.indexOf('#<a Fun>') == -1) return 3;"
5665 " return 0;"
5666 " }"
5667 " return 4;"
5668 "}"
5669 "test();");
5670 CHECK_EQ(0, value->Int32Value());
5671}
5672
5673
5674THREADED_TEST(EvalAliasedDynamic) {
5675 v8::HandleScope scope;
5676 LocalContext current;
5677
5678 // Tests where aliased eval can only be resolved dynamically.
5679 Local<Script> script =
5680 Script::Compile(v8_str("function f(x) { "
5681 " var foo = 2;"
5682 " with (x) { return eval('foo'); }"
5683 "}"
5684 "foo = 0;"
5685 "result1 = f(new Object());"
5686 "result2 = f(this);"
5687 "var x = new Object();"
5688 "x.eval = function(x) { return 1; };"
5689 "result3 = f(x);"));
5690 script->Run();
5691 CHECK_EQ(2, current->Global()->Get(v8_str("result1"))->Int32Value());
5692 CHECK_EQ(0, current->Global()->Get(v8_str("result2"))->Int32Value());
5693 CHECK_EQ(1, current->Global()->Get(v8_str("result3"))->Int32Value());
5694
5695 v8::TryCatch try_catch;
5696 script =
5697 Script::Compile(v8_str("function f(x) { "
5698 " var bar = 2;"
5699 " with (x) { return eval('bar'); }"
5700 "}"
5701 "f(this)"));
5702 script->Run();
5703 CHECK(try_catch.HasCaught());
5704 try_catch.Reset();
5705}
5706
5707
5708THREADED_TEST(CrossEval) {
5709 v8::HandleScope scope;
5710 LocalContext other;
5711 LocalContext current;
5712
5713 Local<String> token = v8_str("<security token>");
5714 other->SetSecurityToken(token);
5715 current->SetSecurityToken(token);
5716
5717 // Setup reference from current to other.
5718 current->Global()->Set(v8_str("other"), other->Global());
5719
5720 // Check that new variables are introduced in other context.
5721 Local<Script> script =
5722 Script::Compile(v8_str("other.eval('var foo = 1234')"));
5723 script->Run();
5724 Local<Value> foo = other->Global()->Get(v8_str("foo"));
5725 CHECK_EQ(1234, foo->Int32Value());
5726 CHECK(!current->Global()->Has(v8_str("foo")));
5727
5728 // Check that writing to non-existing properties introduces them in
5729 // the other context.
5730 script =
5731 Script::Compile(v8_str("other.eval('na = 1234')"));
5732 script->Run();
5733 CHECK_EQ(1234, other->Global()->Get(v8_str("na"))->Int32Value());
5734 CHECK(!current->Global()->Has(v8_str("na")));
5735
5736 // Check that global variables in current context are not visible in other
5737 // context.
5738 v8::TryCatch try_catch;
5739 script =
5740 Script::Compile(v8_str("var bar = 42; other.eval('bar');"));
5741 Local<Value> result = script->Run();
5742 CHECK(try_catch.HasCaught());
5743 try_catch.Reset();
5744
5745 // Check that local variables in current context are not visible in other
5746 // context.
5747 script =
5748 Script::Compile(v8_str("(function() { "
5749 " var baz = 87;"
5750 " return other.eval('baz');"
5751 "})();"));
5752 result = script->Run();
5753 CHECK(try_catch.HasCaught());
5754 try_catch.Reset();
5755
5756 // Check that global variables in the other environment are visible
5757 // when evaluting code.
5758 other->Global()->Set(v8_str("bis"), v8_num(1234));
5759 script = Script::Compile(v8_str("other.eval('bis')"));
5760 CHECK_EQ(1234, script->Run()->Int32Value());
5761 CHECK(!try_catch.HasCaught());
5762
5763 // Check that the 'this' pointer points to the global object evaluating
5764 // code.
5765 other->Global()->Set(v8_str("t"), other->Global());
5766 script = Script::Compile(v8_str("other.eval('this == t')"));
5767 result = script->Run();
5768 CHECK(result->IsTrue());
5769 CHECK(!try_catch.HasCaught());
5770
5771 // Check that variables introduced in with-statement are not visible in
5772 // other context.
5773 script =
5774 Script::Compile(v8_str("with({x:2}){other.eval('x')}"));
5775 result = script->Run();
5776 CHECK(try_catch.HasCaught());
5777 try_catch.Reset();
5778
5779 // Check that you cannot use 'eval.call' with another object than the
5780 // current global object.
5781 script =
5782 Script::Compile(v8_str("other.y = 1; eval.call(other, 'y')"));
5783 result = script->Run();
5784 CHECK(try_catch.HasCaught());
5785}
5786
5787
5788// Test that calling eval in a context which has been detached from
5789// its global throws an exception. This behavior is consistent with
5790// other JavaScript implementations.
5791THREADED_TEST(EvalInDetachedGlobal) {
5792 v8::HandleScope scope;
5793
5794 v8::Persistent<Context> context0 = Context::New();
5795 v8::Persistent<Context> context1 = Context::New();
5796
5797 // Setup function in context0 that uses eval from context0.
5798 context0->Enter();
5799 v8::Handle<v8::Value> fun =
5800 CompileRun("var x = 42;"
5801 "(function() {"
5802 " var e = eval;"
5803 " return function(s) { return e(s); }"
5804 "})()");
5805 context0->Exit();
5806
5807 // Put the function into context1 and call it before and after
5808 // detaching the global. Before detaching, the call succeeds and
5809 // after detaching and exception is thrown.
5810 context1->Enter();
5811 context1->Global()->Set(v8_str("fun"), fun);
5812 v8::Handle<v8::Value> x_value = CompileRun("fun('x')");
5813 CHECK_EQ(42, x_value->Int32Value());
5814 context0->DetachGlobal();
5815 v8::TryCatch catcher;
5816 x_value = CompileRun("fun('x')");
5817 CHECK(x_value.IsEmpty());
5818 CHECK(catcher.HasCaught());
5819 context1->Exit();
5820
5821 context1.Dispose();
5822 context0.Dispose();
5823}
5824
5825
5826THREADED_TEST(CrossLazyLoad) {
5827 v8::HandleScope scope;
5828 LocalContext other;
5829 LocalContext current;
5830
5831 Local<String> token = v8_str("<security token>");
5832 other->SetSecurityToken(token);
5833 current->SetSecurityToken(token);
5834
5835 // Setup reference from current to other.
5836 current->Global()->Set(v8_str("other"), other->Global());
5837
5838 // Trigger lazy loading in other context.
5839 Local<Script> script =
5840 Script::Compile(v8_str("other.eval('new Date(42)')"));
5841 Local<Value> value = script->Run();
5842 CHECK_EQ(42.0, value->NumberValue());
5843}
5844
5845
5846static v8::Handle<Value> call_as_function(const v8::Arguments& args) {
Andrei Popescu402d9372010-02-26 13:31:12 +00005847 ApiTestFuzzer::Fuzz();
Steve Blocka7e24c12009-10-30 11:49:00 +00005848 if (args.IsConstructCall()) {
5849 if (args[0]->IsInt32()) {
5850 return v8_num(-args[0]->Int32Value());
5851 }
5852 }
5853
5854 return args[0];
5855}
5856
5857
5858// Test that a call handler can be set for objects which will allow
5859// non-function objects created through the API to be called as
5860// functions.
5861THREADED_TEST(CallAsFunction) {
5862 v8::HandleScope scope;
5863 LocalContext context;
5864
5865 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5866 Local<ObjectTemplate> instance_template = t->InstanceTemplate();
5867 instance_template->SetCallAsFunctionHandler(call_as_function);
5868 Local<v8::Object> instance = t->GetFunction()->NewInstance();
5869 context->Global()->Set(v8_str("obj"), instance);
5870 v8::TryCatch try_catch;
5871 Local<Value> value;
5872 CHECK(!try_catch.HasCaught());
5873
5874 value = CompileRun("obj(42)");
5875 CHECK(!try_catch.HasCaught());
5876 CHECK_EQ(42, value->Int32Value());
5877
5878 value = CompileRun("(function(o){return o(49)})(obj)");
5879 CHECK(!try_catch.HasCaught());
5880 CHECK_EQ(49, value->Int32Value());
5881
5882 // test special case of call as function
5883 value = CompileRun("[obj]['0'](45)");
5884 CHECK(!try_catch.HasCaught());
5885 CHECK_EQ(45, value->Int32Value());
5886
5887 value = CompileRun("obj.call = Function.prototype.call;"
5888 "obj.call(null, 87)");
5889 CHECK(!try_catch.HasCaught());
5890 CHECK_EQ(87, value->Int32Value());
5891
5892 // Regression tests for bug #1116356: Calling call through call/apply
5893 // must work for non-function receivers.
5894 const char* apply_99 = "Function.prototype.call.apply(obj, [this, 99])";
5895 value = CompileRun(apply_99);
5896 CHECK(!try_catch.HasCaught());
5897 CHECK_EQ(99, value->Int32Value());
5898
5899 const char* call_17 = "Function.prototype.call.call(obj, this, 17)";
5900 value = CompileRun(call_17);
5901 CHECK(!try_catch.HasCaught());
5902 CHECK_EQ(17, value->Int32Value());
5903
5904 // Check that the call-as-function handler can be called through
Leon Clarkee46be812010-01-19 14:06:41 +00005905 // new.
Steve Blocka7e24c12009-10-30 11:49:00 +00005906 value = CompileRun("new obj(43)");
5907 CHECK(!try_catch.HasCaught());
5908 CHECK_EQ(-43, value->Int32Value());
5909}
5910
5911
5912static int CountHandles() {
5913 return v8::HandleScope::NumberOfHandles();
5914}
5915
5916
5917static int Recurse(int depth, int iterations) {
5918 v8::HandleScope scope;
5919 if (depth == 0) return CountHandles();
5920 for (int i = 0; i < iterations; i++) {
5921 Local<v8::Number> n = v8::Integer::New(42);
5922 }
5923 return Recurse(depth - 1, iterations);
5924}
5925
5926
5927THREADED_TEST(HandleIteration) {
5928 static const int kIterations = 500;
5929 static const int kNesting = 200;
5930 CHECK_EQ(0, CountHandles());
5931 {
5932 v8::HandleScope scope1;
5933 CHECK_EQ(0, CountHandles());
5934 for (int i = 0; i < kIterations; i++) {
5935 Local<v8::Number> n = v8::Integer::New(42);
5936 CHECK_EQ(i + 1, CountHandles());
5937 }
5938
5939 CHECK_EQ(kIterations, CountHandles());
5940 {
5941 v8::HandleScope scope2;
5942 for (int j = 0; j < kIterations; j++) {
5943 Local<v8::Number> n = v8::Integer::New(42);
5944 CHECK_EQ(j + 1 + kIterations, CountHandles());
5945 }
5946 }
5947 CHECK_EQ(kIterations, CountHandles());
5948 }
5949 CHECK_EQ(0, CountHandles());
5950 CHECK_EQ(kNesting * kIterations, Recurse(kNesting, kIterations));
5951}
5952
5953
5954static v8::Handle<Value> InterceptorHasOwnPropertyGetter(
5955 Local<String> name,
5956 const AccessorInfo& info) {
5957 ApiTestFuzzer::Fuzz();
5958 return v8::Handle<Value>();
5959}
5960
5961
5962THREADED_TEST(InterceptorHasOwnProperty) {
5963 v8::HandleScope scope;
5964 LocalContext context;
5965 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
5966 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
5967 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetter);
5968 Local<Function> function = fun_templ->GetFunction();
5969 context->Global()->Set(v8_str("constructor"), function);
5970 v8::Handle<Value> value = CompileRun(
5971 "var o = new constructor();"
5972 "o.hasOwnProperty('ostehaps');");
5973 CHECK_EQ(false, value->BooleanValue());
5974 value = CompileRun(
5975 "o.ostehaps = 42;"
5976 "o.hasOwnProperty('ostehaps');");
5977 CHECK_EQ(true, value->BooleanValue());
5978 value = CompileRun(
5979 "var p = new constructor();"
5980 "p.hasOwnProperty('ostehaps');");
5981 CHECK_EQ(false, value->BooleanValue());
5982}
5983
5984
5985static v8::Handle<Value> InterceptorHasOwnPropertyGetterGC(
5986 Local<String> name,
5987 const AccessorInfo& info) {
5988 ApiTestFuzzer::Fuzz();
5989 i::Heap::CollectAllGarbage(false);
5990 return v8::Handle<Value>();
5991}
5992
5993
5994THREADED_TEST(InterceptorHasOwnPropertyCausingGC) {
5995 v8::HandleScope scope;
5996 LocalContext context;
5997 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
5998 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
5999 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetterGC);
6000 Local<Function> function = fun_templ->GetFunction();
6001 context->Global()->Set(v8_str("constructor"), function);
6002 // Let's first make some stuff so we can be sure to get a good GC.
6003 CompileRun(
6004 "function makestr(size) {"
6005 " switch (size) {"
6006 " case 1: return 'f';"
6007 " case 2: return 'fo';"
6008 " case 3: return 'foo';"
6009 " }"
6010 " return makestr(size >> 1) + makestr((size + 1) >> 1);"
6011 "}"
6012 "var x = makestr(12345);"
6013 "x = makestr(31415);"
6014 "x = makestr(23456);");
6015 v8::Handle<Value> value = CompileRun(
6016 "var o = new constructor();"
6017 "o.__proto__ = new String(x);"
6018 "o.hasOwnProperty('ostehaps');");
6019 CHECK_EQ(false, value->BooleanValue());
6020}
6021
6022
6023typedef v8::Handle<Value> (*NamedPropertyGetter)(Local<String> property,
6024 const AccessorInfo& info);
6025
6026
6027static void CheckInterceptorLoadIC(NamedPropertyGetter getter,
6028 const char* source,
6029 int expected) {
6030 v8::HandleScope scope;
6031 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6032 templ->SetNamedPropertyHandler(getter);
6033 LocalContext context;
6034 context->Global()->Set(v8_str("o"), templ->NewInstance());
6035 v8::Handle<Value> value = CompileRun(source);
6036 CHECK_EQ(expected, value->Int32Value());
6037}
6038
6039
6040static v8::Handle<Value> InterceptorLoadICGetter(Local<String> name,
6041 const AccessorInfo& info) {
6042 ApiTestFuzzer::Fuzz();
6043 CHECK(v8_str("x")->Equals(name));
6044 return v8::Integer::New(42);
6045}
6046
6047
6048// This test should hit the load IC for the interceptor case.
6049THREADED_TEST(InterceptorLoadIC) {
6050 CheckInterceptorLoadIC(InterceptorLoadICGetter,
6051 "var result = 0;"
6052 "for (var i = 0; i < 1000; i++) {"
6053 " result = o.x;"
6054 "}",
6055 42);
6056}
6057
6058
6059// Below go several tests which verify that JITing for various
6060// configurations of interceptor and explicit fields works fine
6061// (those cases are special cased to get better performance).
6062
6063static v8::Handle<Value> InterceptorLoadXICGetter(Local<String> name,
6064 const AccessorInfo& info) {
6065 ApiTestFuzzer::Fuzz();
6066 return v8_str("x")->Equals(name)
6067 ? v8::Integer::New(42) : v8::Handle<v8::Value>();
6068}
6069
6070
6071THREADED_TEST(InterceptorLoadICWithFieldOnHolder) {
6072 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6073 "var result = 0;"
6074 "o.y = 239;"
6075 "for (var i = 0; i < 1000; i++) {"
6076 " result = o.y;"
6077 "}",
6078 239);
6079}
6080
6081
6082THREADED_TEST(InterceptorLoadICWithSubstitutedProto) {
6083 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6084 "var result = 0;"
6085 "o.__proto__ = { 'y': 239 };"
6086 "for (var i = 0; i < 1000; i++) {"
6087 " result = o.y + o.x;"
6088 "}",
6089 239 + 42);
6090}
6091
6092
6093THREADED_TEST(InterceptorLoadICWithPropertyOnProto) {
6094 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6095 "var result = 0;"
6096 "o.__proto__.y = 239;"
6097 "for (var i = 0; i < 1000; i++) {"
6098 " result = o.y + o.x;"
6099 "}",
6100 239 + 42);
6101}
6102
6103
6104THREADED_TEST(InterceptorLoadICUndefined) {
6105 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6106 "var result = 0;"
6107 "for (var i = 0; i < 1000; i++) {"
6108 " result = (o.y == undefined) ? 239 : 42;"
6109 "}",
6110 239);
6111}
6112
6113
6114THREADED_TEST(InterceptorLoadICWithOverride) {
6115 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6116 "fst = new Object(); fst.__proto__ = o;"
6117 "snd = new Object(); snd.__proto__ = fst;"
6118 "var result1 = 0;"
6119 "for (var i = 0; i < 1000; i++) {"
6120 " result1 = snd.x;"
6121 "}"
6122 "fst.x = 239;"
6123 "var result = 0;"
6124 "for (var i = 0; i < 1000; i++) {"
6125 " result = snd.x;"
6126 "}"
6127 "result + result1",
6128 239 + 42);
6129}
6130
6131
6132// Test the case when we stored field into
6133// a stub, but interceptor produced value on its own.
6134THREADED_TEST(InterceptorLoadICFieldNotNeeded) {
6135 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6136 "proto = new Object();"
6137 "o.__proto__ = proto;"
6138 "proto.x = 239;"
6139 "for (var i = 0; i < 1000; i++) {"
6140 " o.x;"
6141 // Now it should be ICed and keep a reference to x defined on proto
6142 "}"
6143 "var result = 0;"
6144 "for (var i = 0; i < 1000; i++) {"
6145 " result += o.x;"
6146 "}"
6147 "result;",
6148 42 * 1000);
6149}
6150
6151
6152// Test the case when we stored field into
6153// a stub, but it got invalidated later on.
6154THREADED_TEST(InterceptorLoadICInvalidatedField) {
6155 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6156 "proto1 = new Object();"
6157 "proto2 = new Object();"
6158 "o.__proto__ = proto1;"
6159 "proto1.__proto__ = proto2;"
6160 "proto2.y = 239;"
6161 "for (var i = 0; i < 1000; i++) {"
6162 " o.y;"
6163 // Now it should be ICed and keep a reference to y defined on proto2
6164 "}"
6165 "proto1.y = 42;"
6166 "var result = 0;"
6167 "for (var i = 0; i < 1000; i++) {"
6168 " result += o.y;"
6169 "}"
6170 "result;",
6171 42 * 1000);
6172}
6173
6174
Steve Block6ded16b2010-05-10 14:33:55 +01006175static int interceptor_load_not_handled_calls = 0;
6176static v8::Handle<Value> InterceptorLoadNotHandled(Local<String> name,
6177 const AccessorInfo& info) {
6178 ++interceptor_load_not_handled_calls;
6179 return v8::Handle<v8::Value>();
6180}
6181
6182
6183// Test how post-interceptor lookups are done in the non-cacheable
6184// case: the interceptor should not be invoked during this lookup.
6185THREADED_TEST(InterceptorLoadICPostInterceptor) {
6186 interceptor_load_not_handled_calls = 0;
6187 CheckInterceptorLoadIC(InterceptorLoadNotHandled,
6188 "receiver = new Object();"
6189 "receiver.__proto__ = o;"
6190 "proto = new Object();"
6191 "/* Make proto a slow-case object. */"
6192 "for (var i = 0; i < 1000; i++) {"
6193 " proto[\"xxxxxxxx\" + i] = [];"
6194 "}"
6195 "proto.x = 17;"
6196 "o.__proto__ = proto;"
6197 "var result = 0;"
6198 "for (var i = 0; i < 1000; i++) {"
6199 " result += receiver.x;"
6200 "}"
6201 "result;",
6202 17 * 1000);
6203 CHECK_EQ(1000, interceptor_load_not_handled_calls);
6204}
6205
6206
Steve Blocka7e24c12009-10-30 11:49:00 +00006207// Test the case when we stored field into
6208// a stub, but it got invalidated later on due to override on
6209// global object which is between interceptor and fields' holders.
6210THREADED_TEST(InterceptorLoadICInvalidatedFieldViaGlobal) {
6211 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6212 "o.__proto__ = this;" // set a global to be a proto of o.
6213 "this.__proto__.y = 239;"
6214 "for (var i = 0; i < 10; i++) {"
6215 " if (o.y != 239) throw 'oops: ' + o.y;"
6216 // Now it should be ICed and keep a reference to y defined on field_holder.
6217 "}"
6218 "this.y = 42;" // Assign on a global.
6219 "var result = 0;"
6220 "for (var i = 0; i < 10; i++) {"
6221 " result += o.y;"
6222 "}"
6223 "result;",
6224 42 * 10);
6225}
6226
6227
6228static v8::Handle<Value> Return239(Local<String> name, const AccessorInfo&) {
6229 ApiTestFuzzer::Fuzz();
6230 return v8_num(239);
6231}
6232
6233
6234static void SetOnThis(Local<String> name,
6235 Local<Value> value,
6236 const AccessorInfo& info) {
6237 info.This()->ForceSet(name, value);
6238}
6239
6240
6241THREADED_TEST(InterceptorLoadICWithCallbackOnHolder) {
6242 v8::HandleScope scope;
6243 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6244 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6245 templ->SetAccessor(v8_str("y"), Return239);
6246 LocalContext context;
6247 context->Global()->Set(v8_str("o"), templ->NewInstance());
6248 v8::Handle<Value> value = CompileRun(
6249 "var result = 0;"
6250 "for (var i = 0; i < 7; i++) {"
6251 " result = o.y;"
6252 "}");
6253 CHECK_EQ(239, value->Int32Value());
6254}
6255
6256
6257THREADED_TEST(InterceptorLoadICWithCallbackOnProto) {
6258 v8::HandleScope scope;
6259 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6260 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6261 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
6262 templ_p->SetAccessor(v8_str("y"), Return239);
6263
6264 LocalContext context;
6265 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6266 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
6267
6268 v8::Handle<Value> value = CompileRun(
6269 "o.__proto__ = p;"
6270 "var result = 0;"
6271 "for (var i = 0; i < 7; i++) {"
6272 " result = o.x + o.y;"
6273 "}");
6274 CHECK_EQ(239 + 42, value->Int32Value());
6275}
6276
6277
6278THREADED_TEST(InterceptorLoadICForCallbackWithOverride) {
6279 v8::HandleScope scope;
6280 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6281 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6282 templ->SetAccessor(v8_str("y"), Return239);
6283
6284 LocalContext context;
6285 context->Global()->Set(v8_str("o"), templ->NewInstance());
6286
6287 v8::Handle<Value> value = CompileRun(
6288 "fst = new Object(); fst.__proto__ = o;"
6289 "snd = new Object(); snd.__proto__ = fst;"
6290 "var result1 = 0;"
6291 "for (var i = 0; i < 7; i++) {"
6292 " result1 = snd.x;"
6293 "}"
6294 "fst.x = 239;"
6295 "var result = 0;"
6296 "for (var i = 0; i < 7; i++) {"
6297 " result = snd.x;"
6298 "}"
6299 "result + result1");
6300 CHECK_EQ(239 + 42, value->Int32Value());
6301}
6302
6303
6304// Test the case when we stored callback into
6305// a stub, but interceptor produced value on its own.
6306THREADED_TEST(InterceptorLoadICCallbackNotNeeded) {
6307 v8::HandleScope scope;
6308 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6309 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6310 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
6311 templ_p->SetAccessor(v8_str("y"), Return239);
6312
6313 LocalContext context;
6314 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6315 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
6316
6317 v8::Handle<Value> value = CompileRun(
6318 "o.__proto__ = p;"
6319 "for (var i = 0; i < 7; i++) {"
6320 " o.x;"
6321 // Now it should be ICed and keep a reference to x defined on p
6322 "}"
6323 "var result = 0;"
6324 "for (var i = 0; i < 7; i++) {"
6325 " result += o.x;"
6326 "}"
6327 "result");
6328 CHECK_EQ(42 * 7, value->Int32Value());
6329}
6330
6331
6332// Test the case when we stored callback into
6333// a stub, but it got invalidated later on.
6334THREADED_TEST(InterceptorLoadICInvalidatedCallback) {
6335 v8::HandleScope scope;
6336 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6337 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6338 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
6339 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis);
6340
6341 LocalContext context;
6342 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6343 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
6344
6345 v8::Handle<Value> value = CompileRun(
6346 "inbetween = new Object();"
6347 "o.__proto__ = inbetween;"
6348 "inbetween.__proto__ = p;"
6349 "for (var i = 0; i < 10; i++) {"
6350 " o.y;"
6351 // Now it should be ICed and keep a reference to y defined on p
6352 "}"
6353 "inbetween.y = 42;"
6354 "var result = 0;"
6355 "for (var i = 0; i < 10; i++) {"
6356 " result += o.y;"
6357 "}"
6358 "result");
6359 CHECK_EQ(42 * 10, value->Int32Value());
6360}
6361
6362
6363// Test the case when we stored callback into
6364// a stub, but it got invalidated later on due to override on
6365// global object which is between interceptor and callbacks' holders.
6366THREADED_TEST(InterceptorLoadICInvalidatedCallbackViaGlobal) {
6367 v8::HandleScope scope;
6368 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6369 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6370 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
6371 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis);
6372
6373 LocalContext context;
6374 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6375 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
6376
6377 v8::Handle<Value> value = CompileRun(
6378 "o.__proto__ = this;"
6379 "this.__proto__ = p;"
6380 "for (var i = 0; i < 10; i++) {"
6381 " if (o.y != 239) throw 'oops: ' + o.y;"
6382 // Now it should be ICed and keep a reference to y defined on p
6383 "}"
6384 "this.y = 42;"
6385 "var result = 0;"
6386 "for (var i = 0; i < 10; i++) {"
6387 " result += o.y;"
6388 "}"
6389 "result");
6390 CHECK_EQ(42 * 10, value->Int32Value());
6391}
6392
6393
6394static v8::Handle<Value> InterceptorLoadICGetter0(Local<String> name,
6395 const AccessorInfo& info) {
6396 ApiTestFuzzer::Fuzz();
6397 CHECK(v8_str("x")->Equals(name));
6398 return v8::Integer::New(0);
6399}
6400
6401
6402THREADED_TEST(InterceptorReturningZero) {
6403 CheckInterceptorLoadIC(InterceptorLoadICGetter0,
6404 "o.x == undefined ? 1 : 0",
6405 0);
6406}
6407
6408
6409static v8::Handle<Value> InterceptorStoreICSetter(
6410 Local<String> key, Local<Value> value, const AccessorInfo&) {
6411 CHECK(v8_str("x")->Equals(key));
6412 CHECK_EQ(42, value->Int32Value());
6413 return value;
6414}
6415
6416
6417// This test should hit the store IC for the interceptor case.
6418THREADED_TEST(InterceptorStoreIC) {
6419 v8::HandleScope scope;
6420 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6421 templ->SetNamedPropertyHandler(InterceptorLoadICGetter,
6422 InterceptorStoreICSetter);
6423 LocalContext context;
6424 context->Global()->Set(v8_str("o"), templ->NewInstance());
6425 v8::Handle<Value> value = CompileRun(
6426 "for (var i = 0; i < 1000; i++) {"
6427 " o.x = 42;"
6428 "}");
6429}
6430
6431
6432THREADED_TEST(InterceptorStoreICWithNoSetter) {
6433 v8::HandleScope scope;
6434 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6435 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6436 LocalContext context;
6437 context->Global()->Set(v8_str("o"), templ->NewInstance());
6438 v8::Handle<Value> value = CompileRun(
6439 "for (var i = 0; i < 1000; i++) {"
6440 " o.y = 239;"
6441 "}"
6442 "42 + o.y");
6443 CHECK_EQ(239 + 42, value->Int32Value());
6444}
6445
6446
6447
6448
6449v8::Handle<Value> call_ic_function;
6450v8::Handle<Value> call_ic_function2;
6451v8::Handle<Value> call_ic_function3;
6452
6453static v8::Handle<Value> InterceptorCallICGetter(Local<String> name,
6454 const AccessorInfo& info) {
6455 ApiTestFuzzer::Fuzz();
6456 CHECK(v8_str("x")->Equals(name));
6457 return call_ic_function;
6458}
6459
6460
6461// This test should hit the call IC for the interceptor case.
6462THREADED_TEST(InterceptorCallIC) {
6463 v8::HandleScope scope;
6464 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6465 templ->SetNamedPropertyHandler(InterceptorCallICGetter);
6466 LocalContext context;
6467 context->Global()->Set(v8_str("o"), templ->NewInstance());
6468 call_ic_function =
6469 v8_compile("function f(x) { return x + 1; }; f")->Run();
6470 v8::Handle<Value> value = CompileRun(
6471 "var result = 0;"
6472 "for (var i = 0; i < 1000; i++) {"
6473 " result = o.x(41);"
6474 "}");
6475 CHECK_EQ(42, value->Int32Value());
6476}
6477
6478
6479// This test checks that if interceptor doesn't provide
6480// a value, we can fetch regular value.
6481THREADED_TEST(InterceptorCallICSeesOthers) {
6482 v8::HandleScope scope;
6483 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6484 templ->SetNamedPropertyHandler(NoBlockGetterX);
6485 LocalContext context;
6486 context->Global()->Set(v8_str("o"), templ->NewInstance());
6487 v8::Handle<Value> value = CompileRun(
6488 "o.x = function f(x) { return x + 1; };"
6489 "var result = 0;"
6490 "for (var i = 0; i < 7; i++) {"
6491 " result = o.x(41);"
6492 "}");
6493 CHECK_EQ(42, value->Int32Value());
6494}
6495
6496
6497static v8::Handle<Value> call_ic_function4;
6498static v8::Handle<Value> InterceptorCallICGetter4(Local<String> name,
6499 const AccessorInfo& info) {
6500 ApiTestFuzzer::Fuzz();
6501 CHECK(v8_str("x")->Equals(name));
6502 return call_ic_function4;
6503}
6504
6505
6506// This test checks that if interceptor provides a function,
6507// even if we cached shadowed variant, interceptor's function
6508// is invoked
6509THREADED_TEST(InterceptorCallICCacheableNotNeeded) {
6510 v8::HandleScope scope;
6511 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6512 templ->SetNamedPropertyHandler(InterceptorCallICGetter4);
6513 LocalContext context;
6514 context->Global()->Set(v8_str("o"), templ->NewInstance());
6515 call_ic_function4 =
6516 v8_compile("function f(x) { return x - 1; }; f")->Run();
6517 v8::Handle<Value> value = CompileRun(
6518 "o.__proto__.x = function(x) { return x + 1; };"
6519 "var result = 0;"
6520 "for (var i = 0; i < 1000; i++) {"
6521 " result = o.x(42);"
6522 "}");
6523 CHECK_EQ(41, value->Int32Value());
6524}
6525
6526
6527// Test the case when we stored cacheable lookup into
6528// a stub, but it got invalidated later on
6529THREADED_TEST(InterceptorCallICInvalidatedCacheable) {
6530 v8::HandleScope scope;
6531 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6532 templ->SetNamedPropertyHandler(NoBlockGetterX);
6533 LocalContext context;
6534 context->Global()->Set(v8_str("o"), templ->NewInstance());
6535 v8::Handle<Value> value = CompileRun(
6536 "proto1 = new Object();"
6537 "proto2 = new Object();"
6538 "o.__proto__ = proto1;"
6539 "proto1.__proto__ = proto2;"
6540 "proto2.y = function(x) { return x + 1; };"
6541 // Invoke it many times to compile a stub
6542 "for (var i = 0; i < 7; i++) {"
6543 " o.y(42);"
6544 "}"
6545 "proto1.y = function(x) { return x - 1; };"
6546 "var result = 0;"
6547 "for (var i = 0; i < 7; i++) {"
6548 " result += o.y(42);"
6549 "}");
6550 CHECK_EQ(41 * 7, value->Int32Value());
6551}
6552
6553
6554static v8::Handle<Value> call_ic_function5;
6555static v8::Handle<Value> InterceptorCallICGetter5(Local<String> name,
6556 const AccessorInfo& info) {
6557 ApiTestFuzzer::Fuzz();
6558 if (v8_str("x")->Equals(name))
6559 return call_ic_function5;
6560 else
6561 return Local<Value>();
6562}
6563
6564
6565// This test checks that if interceptor doesn't provide a function,
6566// cached constant function is used
6567THREADED_TEST(InterceptorCallICConstantFunctionUsed) {
6568 v8::HandleScope scope;
6569 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6570 templ->SetNamedPropertyHandler(NoBlockGetterX);
6571 LocalContext context;
6572 context->Global()->Set(v8_str("o"), templ->NewInstance());
6573 v8::Handle<Value> value = CompileRun(
6574 "function inc(x) { return x + 1; };"
6575 "inc(1);"
6576 "o.x = inc;"
6577 "var result = 0;"
6578 "for (var i = 0; i < 1000; i++) {"
6579 " result = o.x(42);"
6580 "}");
6581 CHECK_EQ(43, value->Int32Value());
6582}
6583
6584
6585// This test checks that if interceptor provides a function,
6586// even if we cached constant function, interceptor's function
6587// is invoked
6588THREADED_TEST(InterceptorCallICConstantFunctionNotNeeded) {
6589 v8::HandleScope scope;
6590 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6591 templ->SetNamedPropertyHandler(InterceptorCallICGetter5);
6592 LocalContext context;
6593 context->Global()->Set(v8_str("o"), templ->NewInstance());
6594 call_ic_function5 =
6595 v8_compile("function f(x) { return x - 1; }; f")->Run();
6596 v8::Handle<Value> value = CompileRun(
6597 "function inc(x) { return x + 1; };"
6598 "inc(1);"
6599 "o.x = inc;"
6600 "var result = 0;"
6601 "for (var i = 0; i < 1000; i++) {"
6602 " result = o.x(42);"
6603 "}");
6604 CHECK_EQ(41, value->Int32Value());
6605}
6606
6607
6608// Test the case when we stored constant function into
6609// a stub, but it got invalidated later on
6610THREADED_TEST(InterceptorCallICInvalidatedConstantFunction) {
6611 v8::HandleScope scope;
6612 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6613 templ->SetNamedPropertyHandler(NoBlockGetterX);
6614 LocalContext context;
6615 context->Global()->Set(v8_str("o"), templ->NewInstance());
6616 v8::Handle<Value> value = CompileRun(
6617 "function inc(x) { return x + 1; };"
6618 "inc(1);"
6619 "proto1 = new Object();"
6620 "proto2 = new Object();"
6621 "o.__proto__ = proto1;"
6622 "proto1.__proto__ = proto2;"
6623 "proto2.y = inc;"
6624 // Invoke it many times to compile a stub
6625 "for (var i = 0; i < 7; i++) {"
6626 " o.y(42);"
6627 "}"
6628 "proto1.y = function(x) { return x - 1; };"
6629 "var result = 0;"
6630 "for (var i = 0; i < 7; i++) {"
6631 " result += o.y(42);"
6632 "}");
6633 CHECK_EQ(41 * 7, value->Int32Value());
6634}
6635
6636
6637// Test the case when we stored constant function into
6638// a stub, but it got invalidated later on due to override on
6639// global object which is between interceptor and constant function' holders.
6640THREADED_TEST(InterceptorCallICInvalidatedConstantFunctionViaGlobal) {
6641 v8::HandleScope scope;
6642 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6643 templ->SetNamedPropertyHandler(NoBlockGetterX);
6644 LocalContext context;
6645 context->Global()->Set(v8_str("o"), templ->NewInstance());
6646 v8::Handle<Value> value = CompileRun(
6647 "function inc(x) { return x + 1; };"
6648 "inc(1);"
6649 "o.__proto__ = this;"
6650 "this.__proto__.y = inc;"
6651 // Invoke it many times to compile a stub
6652 "for (var i = 0; i < 7; i++) {"
6653 " if (o.y(42) != 43) throw 'oops: ' + o.y(42);"
6654 "}"
6655 "this.y = function(x) { return x - 1; };"
6656 "var result = 0;"
6657 "for (var i = 0; i < 7; i++) {"
6658 " result += o.y(42);"
6659 "}");
6660 CHECK_EQ(41 * 7, value->Int32Value());
6661}
6662
6663
Leon Clarke4515c472010-02-03 11:58:03 +00006664// Test the case when actual function to call sits on global object.
6665THREADED_TEST(InterceptorCallICCachedFromGlobal) {
6666 v8::HandleScope scope;
6667 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6668 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
6669
6670 LocalContext context;
6671 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6672
6673 v8::Handle<Value> value = CompileRun(
6674 "try {"
6675 " o.__proto__ = this;"
6676 " for (var i = 0; i < 10; i++) {"
6677 " var v = o.parseFloat('239');"
6678 " if (v != 239) throw v;"
6679 // Now it should be ICed and keep a reference to parseFloat.
6680 " }"
6681 " var result = 0;"
6682 " for (var i = 0; i < 10; i++) {"
6683 " result += o.parseFloat('239');"
6684 " }"
6685 " result"
6686 "} catch(e) {"
6687 " e"
6688 "};");
6689 CHECK_EQ(239 * 10, value->Int32Value());
6690}
6691
Andrei Popescu402d9372010-02-26 13:31:12 +00006692static v8::Handle<Value> InterceptorCallICFastApi(Local<String> name,
6693 const AccessorInfo& info) {
6694 ApiTestFuzzer::Fuzz();
6695 int* call_count = reinterpret_cast<int*>(v8::External::Unwrap(info.Data()));
6696 ++(*call_count);
6697 if ((*call_count) % 20 == 0) {
6698 v8::internal::Heap::CollectAllGarbage(true);
6699 }
6700 return v8::Handle<Value>();
6701}
6702
6703static v8::Handle<Value> FastApiCallback_TrivialSignature(
6704 const v8::Arguments& args) {
6705 ApiTestFuzzer::Fuzz();
6706 CHECK_EQ(args.This(), args.Holder());
6707 CHECK(args.Data()->Equals(v8_str("method_data")));
6708 return v8::Integer::New(args[0]->Int32Value() + 1);
6709}
6710
6711static v8::Handle<Value> FastApiCallback_SimpleSignature(
6712 const v8::Arguments& args) {
6713 ApiTestFuzzer::Fuzz();
6714 CHECK_EQ(args.This()->GetPrototype(), args.Holder());
6715 CHECK(args.Data()->Equals(v8_str("method_data")));
6716 // Note, we're using HasRealNamedProperty instead of Has to avoid
6717 // invoking the interceptor again.
6718 CHECK(args.Holder()->HasRealNamedProperty(v8_str("foo")));
6719 return v8::Integer::New(args[0]->Int32Value() + 1);
6720}
6721
6722// Helper to maximize the odds of object moving.
6723static void GenerateSomeGarbage() {
6724 CompileRun(
6725 "var garbage;"
6726 "for (var i = 0; i < 1000; i++) {"
6727 " garbage = [1/i, \"garbage\" + i, garbage, {foo: garbage}];"
6728 "}"
6729 "garbage = undefined;");
6730}
6731
6732THREADED_TEST(InterceptorCallICFastApi_TrivialSignature) {
6733 int interceptor_call_count = 0;
6734 v8::HandleScope scope;
6735 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6736 v8::Handle<v8::FunctionTemplate> method_templ =
6737 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature,
6738 v8_str("method_data"),
6739 v8::Handle<v8::Signature>());
6740 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
6741 proto_templ->Set(v8_str("method"), method_templ);
6742 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
6743 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
6744 NULL, NULL, NULL, NULL,
6745 v8::External::Wrap(&interceptor_call_count));
6746 LocalContext context;
6747 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
6748 GenerateSomeGarbage();
6749 context->Global()->Set(v8_str("o"), fun->NewInstance());
6750 v8::Handle<Value> value = CompileRun(
6751 "var result = 0;"
6752 "for (var i = 0; i < 100; i++) {"
6753 " result = o.method(41);"
6754 "}");
6755 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
6756 CHECK_EQ(100, interceptor_call_count);
6757}
6758
6759THREADED_TEST(InterceptorCallICFastApi_SimpleSignature) {
6760 int interceptor_call_count = 0;
6761 v8::HandleScope scope;
6762 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6763 v8::Handle<v8::FunctionTemplate> method_templ =
6764 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
6765 v8_str("method_data"),
6766 v8::Signature::New(fun_templ));
6767 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
6768 proto_templ->Set(v8_str("method"), method_templ);
6769 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
6770 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
6771 NULL, NULL, NULL, NULL,
6772 v8::External::Wrap(&interceptor_call_count));
6773 LocalContext context;
6774 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
6775 GenerateSomeGarbage();
6776 context->Global()->Set(v8_str("o"), fun->NewInstance());
6777 v8::Handle<Value> value = CompileRun(
6778 "o.foo = 17;"
6779 "var receiver = {};"
6780 "receiver.__proto__ = o;"
6781 "var result = 0;"
6782 "for (var i = 0; i < 100; i++) {"
6783 " result = receiver.method(41);"
6784 "}");
6785 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
6786 CHECK_EQ(100, interceptor_call_count);
6787}
6788
6789THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss1) {
6790 int interceptor_call_count = 0;
6791 v8::HandleScope scope;
6792 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6793 v8::Handle<v8::FunctionTemplate> method_templ =
6794 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
6795 v8_str("method_data"),
6796 v8::Signature::New(fun_templ));
6797 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
6798 proto_templ->Set(v8_str("method"), method_templ);
6799 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
6800 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
6801 NULL, NULL, NULL, NULL,
6802 v8::External::Wrap(&interceptor_call_count));
6803 LocalContext context;
6804 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
6805 GenerateSomeGarbage();
6806 context->Global()->Set(v8_str("o"), fun->NewInstance());
6807 v8::Handle<Value> value = CompileRun(
6808 "o.foo = 17;"
6809 "var receiver = {};"
6810 "receiver.__proto__ = o;"
6811 "var result = 0;"
6812 "var saved_result = 0;"
6813 "for (var i = 0; i < 100; i++) {"
6814 " result = receiver.method(41);"
6815 " if (i == 50) {"
6816 " saved_result = result;"
6817 " receiver = {method: function(x) { return x - 1 }};"
6818 " }"
6819 "}");
6820 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
6821 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
6822 CHECK_GE(interceptor_call_count, 50);
6823}
6824
6825THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss2) {
6826 int interceptor_call_count = 0;
6827 v8::HandleScope scope;
6828 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6829 v8::Handle<v8::FunctionTemplate> method_templ =
6830 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
6831 v8_str("method_data"),
6832 v8::Signature::New(fun_templ));
6833 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
6834 proto_templ->Set(v8_str("method"), method_templ);
6835 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
6836 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
6837 NULL, NULL, NULL, NULL,
6838 v8::External::Wrap(&interceptor_call_count));
6839 LocalContext context;
6840 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
6841 GenerateSomeGarbage();
6842 context->Global()->Set(v8_str("o"), fun->NewInstance());
6843 v8::Handle<Value> value = CompileRun(
6844 "o.foo = 17;"
6845 "var receiver = {};"
6846 "receiver.__proto__ = o;"
6847 "var result = 0;"
6848 "var saved_result = 0;"
6849 "for (var i = 0; i < 100; i++) {"
6850 " result = receiver.method(41);"
6851 " if (i == 50) {"
6852 " saved_result = result;"
6853 " o.method = function(x) { return x - 1 };"
6854 " }"
6855 "}");
6856 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
6857 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
6858 CHECK_GE(interceptor_call_count, 50);
6859}
6860
Steve Block6ded16b2010-05-10 14:33:55 +01006861THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss3) {
6862 int interceptor_call_count = 0;
6863 v8::HandleScope scope;
6864 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6865 v8::Handle<v8::FunctionTemplate> method_templ =
6866 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
6867 v8_str("method_data"),
6868 v8::Signature::New(fun_templ));
6869 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
6870 proto_templ->Set(v8_str("method"), method_templ);
6871 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
6872 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
6873 NULL, NULL, NULL, NULL,
6874 v8::External::Wrap(&interceptor_call_count));
6875 LocalContext context;
6876 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
6877 GenerateSomeGarbage();
6878 context->Global()->Set(v8_str("o"), fun->NewInstance());
6879 v8::TryCatch try_catch;
6880 v8::Handle<Value> value = CompileRun(
6881 "o.foo = 17;"
6882 "var receiver = {};"
6883 "receiver.__proto__ = o;"
6884 "var result = 0;"
6885 "var saved_result = 0;"
6886 "for (var i = 0; i < 100; i++) {"
6887 " result = receiver.method(41);"
6888 " if (i == 50) {"
6889 " saved_result = result;"
6890 " receiver = 333;"
6891 " }"
6892 "}");
6893 CHECK(try_catch.HasCaught());
6894 CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"),
6895 try_catch.Exception()->ToString());
6896 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
6897 CHECK_GE(interceptor_call_count, 50);
6898}
6899
Andrei Popescu402d9372010-02-26 13:31:12 +00006900THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_TypeError) {
6901 int interceptor_call_count = 0;
6902 v8::HandleScope scope;
6903 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6904 v8::Handle<v8::FunctionTemplate> method_templ =
6905 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
6906 v8_str("method_data"),
6907 v8::Signature::New(fun_templ));
6908 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
6909 proto_templ->Set(v8_str("method"), method_templ);
6910 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
6911 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
6912 NULL, NULL, NULL, NULL,
6913 v8::External::Wrap(&interceptor_call_count));
6914 LocalContext context;
6915 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
6916 GenerateSomeGarbage();
6917 context->Global()->Set(v8_str("o"), fun->NewInstance());
6918 v8::TryCatch try_catch;
6919 v8::Handle<Value> value = CompileRun(
6920 "o.foo = 17;"
6921 "var receiver = {};"
6922 "receiver.__proto__ = o;"
6923 "var result = 0;"
6924 "var saved_result = 0;"
6925 "for (var i = 0; i < 100; i++) {"
6926 " result = receiver.method(41);"
6927 " if (i == 50) {"
6928 " saved_result = result;"
6929 " receiver = {method: receiver.method};"
6930 " }"
6931 "}");
6932 CHECK(try_catch.HasCaught());
6933 CHECK_EQ(v8_str("TypeError: Illegal invocation"),
6934 try_catch.Exception()->ToString());
6935 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
6936 CHECK_GE(interceptor_call_count, 50);
6937}
6938
6939THREADED_TEST(CallICFastApi_TrivialSignature) {
6940 v8::HandleScope scope;
6941 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6942 v8::Handle<v8::FunctionTemplate> method_templ =
6943 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature,
6944 v8_str("method_data"),
6945 v8::Handle<v8::Signature>());
6946 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
6947 proto_templ->Set(v8_str("method"), method_templ);
6948 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
6949 LocalContext context;
6950 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
6951 GenerateSomeGarbage();
6952 context->Global()->Set(v8_str("o"), fun->NewInstance());
6953 v8::Handle<Value> value = CompileRun(
6954 "var result = 0;"
6955 "for (var i = 0; i < 100; i++) {"
6956 " result = o.method(41);"
6957 "}");
6958
6959 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
6960}
6961
6962THREADED_TEST(CallICFastApi_SimpleSignature) {
6963 v8::HandleScope scope;
6964 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6965 v8::Handle<v8::FunctionTemplate> method_templ =
6966 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
6967 v8_str("method_data"),
6968 v8::Signature::New(fun_templ));
6969 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
6970 proto_templ->Set(v8_str("method"), method_templ);
6971 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
6972 LocalContext context;
6973 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
6974 GenerateSomeGarbage();
6975 context->Global()->Set(v8_str("o"), fun->NewInstance());
6976 v8::Handle<Value> value = CompileRun(
6977 "o.foo = 17;"
6978 "var receiver = {};"
6979 "receiver.__proto__ = o;"
6980 "var result = 0;"
6981 "for (var i = 0; i < 100; i++) {"
6982 " result = receiver.method(41);"
6983 "}");
6984
6985 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
6986}
6987
Steve Block6ded16b2010-05-10 14:33:55 +01006988THREADED_TEST(CallICFastApi_SimpleSignature_Miss1) {
Andrei Popescu402d9372010-02-26 13:31:12 +00006989 v8::HandleScope scope;
6990 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6991 v8::Handle<v8::FunctionTemplate> method_templ =
6992 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
6993 v8_str("method_data"),
6994 v8::Signature::New(fun_templ));
6995 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
6996 proto_templ->Set(v8_str("method"), method_templ);
6997 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
6998 LocalContext context;
6999 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7000 GenerateSomeGarbage();
7001 context->Global()->Set(v8_str("o"), fun->NewInstance());
7002 v8::Handle<Value> value = CompileRun(
7003 "o.foo = 17;"
7004 "var receiver = {};"
7005 "receiver.__proto__ = o;"
7006 "var result = 0;"
7007 "var saved_result = 0;"
7008 "for (var i = 0; i < 100; i++) {"
7009 " result = receiver.method(41);"
7010 " if (i == 50) {"
7011 " saved_result = result;"
7012 " receiver = {method: function(x) { return x - 1 }};"
7013 " }"
7014 "}");
7015 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
7016 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7017}
7018
Steve Block6ded16b2010-05-10 14:33:55 +01007019THREADED_TEST(CallICFastApi_SimpleSignature_Miss2) {
7020 v8::HandleScope scope;
7021 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7022 v8::Handle<v8::FunctionTemplate> method_templ =
7023 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7024 v8_str("method_data"),
7025 v8::Signature::New(fun_templ));
7026 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7027 proto_templ->Set(v8_str("method"), method_templ);
7028 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7029 LocalContext context;
7030 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7031 GenerateSomeGarbage();
7032 context->Global()->Set(v8_str("o"), fun->NewInstance());
7033 v8::TryCatch try_catch;
7034 v8::Handle<Value> value = CompileRun(
7035 "o.foo = 17;"
7036 "var receiver = {};"
7037 "receiver.__proto__ = o;"
7038 "var result = 0;"
7039 "var saved_result = 0;"
7040 "for (var i = 0; i < 100; i++) {"
7041 " result = receiver.method(41);"
7042 " if (i == 50) {"
7043 " saved_result = result;"
7044 " receiver = 333;"
7045 " }"
7046 "}");
7047 CHECK(try_catch.HasCaught());
7048 CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"),
7049 try_catch.Exception()->ToString());
7050 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7051}
7052
Leon Clarke4515c472010-02-03 11:58:03 +00007053
Steve Blocka7e24c12009-10-30 11:49:00 +00007054static int interceptor_call_count = 0;
7055
7056static v8::Handle<Value> InterceptorICRefErrorGetter(Local<String> name,
7057 const AccessorInfo& info) {
7058 ApiTestFuzzer::Fuzz();
7059 if (v8_str("x")->Equals(name) && interceptor_call_count++ < 20) {
7060 return call_ic_function2;
7061 }
7062 return v8::Handle<Value>();
7063}
7064
7065
7066// This test should hit load and call ICs for the interceptor case.
7067// Once in a while, the interceptor will reply that a property was not
7068// found in which case we should get a reference error.
7069THREADED_TEST(InterceptorICReferenceErrors) {
7070 v8::HandleScope scope;
7071 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7072 templ->SetNamedPropertyHandler(InterceptorICRefErrorGetter);
7073 LocalContext context(0, templ, v8::Handle<Value>());
7074 call_ic_function2 = v8_compile("function h(x) { return x; }; h")->Run();
7075 v8::Handle<Value> value = CompileRun(
7076 "function f() {"
7077 " for (var i = 0; i < 1000; i++) {"
7078 " try { x; } catch(e) { return true; }"
7079 " }"
7080 " return false;"
7081 "};"
7082 "f();");
7083 CHECK_EQ(true, value->BooleanValue());
7084 interceptor_call_count = 0;
7085 value = CompileRun(
7086 "function g() {"
7087 " for (var i = 0; i < 1000; i++) {"
7088 " try { x(42); } catch(e) { return true; }"
7089 " }"
7090 " return false;"
7091 "};"
7092 "g();");
7093 CHECK_EQ(true, value->BooleanValue());
7094}
7095
7096
7097static int interceptor_ic_exception_get_count = 0;
7098
7099static v8::Handle<Value> InterceptorICExceptionGetter(
7100 Local<String> name,
7101 const AccessorInfo& info) {
7102 ApiTestFuzzer::Fuzz();
7103 if (v8_str("x")->Equals(name) && ++interceptor_ic_exception_get_count < 20) {
7104 return call_ic_function3;
7105 }
7106 if (interceptor_ic_exception_get_count == 20) {
7107 return v8::ThrowException(v8_num(42));
7108 }
7109 // Do not handle get for properties other than x.
7110 return v8::Handle<Value>();
7111}
7112
7113// Test interceptor load/call IC where the interceptor throws an
7114// exception once in a while.
7115THREADED_TEST(InterceptorICGetterExceptions) {
7116 interceptor_ic_exception_get_count = 0;
7117 v8::HandleScope scope;
7118 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7119 templ->SetNamedPropertyHandler(InterceptorICExceptionGetter);
7120 LocalContext context(0, templ, v8::Handle<Value>());
7121 call_ic_function3 = v8_compile("function h(x) { return x; }; h")->Run();
7122 v8::Handle<Value> value = CompileRun(
7123 "function f() {"
7124 " for (var i = 0; i < 100; i++) {"
7125 " try { x; } catch(e) { return true; }"
7126 " }"
7127 " return false;"
7128 "};"
7129 "f();");
7130 CHECK_EQ(true, value->BooleanValue());
7131 interceptor_ic_exception_get_count = 0;
7132 value = CompileRun(
7133 "function f() {"
7134 " for (var i = 0; i < 100; i++) {"
7135 " try { x(42); } catch(e) { return true; }"
7136 " }"
7137 " return false;"
7138 "};"
7139 "f();");
7140 CHECK_EQ(true, value->BooleanValue());
7141}
7142
7143
7144static int interceptor_ic_exception_set_count = 0;
7145
7146static v8::Handle<Value> InterceptorICExceptionSetter(
7147 Local<String> key, Local<Value> value, const AccessorInfo&) {
7148 ApiTestFuzzer::Fuzz();
7149 if (++interceptor_ic_exception_set_count > 20) {
7150 return v8::ThrowException(v8_num(42));
7151 }
7152 // Do not actually handle setting.
7153 return v8::Handle<Value>();
7154}
7155
7156// Test interceptor store IC where the interceptor throws an exception
7157// once in a while.
7158THREADED_TEST(InterceptorICSetterExceptions) {
7159 interceptor_ic_exception_set_count = 0;
7160 v8::HandleScope scope;
7161 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7162 templ->SetNamedPropertyHandler(0, InterceptorICExceptionSetter);
7163 LocalContext context(0, templ, v8::Handle<Value>());
7164 v8::Handle<Value> value = CompileRun(
7165 "function f() {"
7166 " for (var i = 0; i < 100; i++) {"
7167 " try { x = 42; } catch(e) { return true; }"
7168 " }"
7169 " return false;"
7170 "};"
7171 "f();");
7172 CHECK_EQ(true, value->BooleanValue());
7173}
7174
7175
7176// Test that we ignore null interceptors.
7177THREADED_TEST(NullNamedInterceptor) {
7178 v8::HandleScope scope;
7179 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7180 templ->SetNamedPropertyHandler(0);
7181 LocalContext context;
7182 templ->Set("x", v8_num(42));
7183 v8::Handle<v8::Object> obj = templ->NewInstance();
7184 context->Global()->Set(v8_str("obj"), obj);
7185 v8::Handle<Value> value = CompileRun("obj.x");
7186 CHECK(value->IsInt32());
7187 CHECK_EQ(42, value->Int32Value());
7188}
7189
7190
7191// Test that we ignore null interceptors.
7192THREADED_TEST(NullIndexedInterceptor) {
7193 v8::HandleScope scope;
7194 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7195 templ->SetIndexedPropertyHandler(0);
7196 LocalContext context;
7197 templ->Set("42", v8_num(42));
7198 v8::Handle<v8::Object> obj = templ->NewInstance();
7199 context->Global()->Set(v8_str("obj"), obj);
7200 v8::Handle<Value> value = CompileRun("obj[42]");
7201 CHECK(value->IsInt32());
7202 CHECK_EQ(42, value->Int32Value());
7203}
7204
7205
7206static v8::Handle<Value> ParentGetter(Local<String> name,
7207 const AccessorInfo& info) {
7208 ApiTestFuzzer::Fuzz();
7209 return v8_num(1);
7210}
7211
7212
7213static v8::Handle<Value> ChildGetter(Local<String> name,
7214 const AccessorInfo& info) {
7215 ApiTestFuzzer::Fuzz();
7216 return v8_num(42);
7217}
7218
7219
7220THREADED_TEST(Overriding) {
7221 v8::HandleScope scope;
7222 LocalContext context;
7223
7224 // Parent template.
7225 Local<v8::FunctionTemplate> parent_templ = v8::FunctionTemplate::New();
7226 Local<ObjectTemplate> parent_instance_templ =
7227 parent_templ->InstanceTemplate();
7228 parent_instance_templ->SetAccessor(v8_str("f"), ParentGetter);
7229
7230 // Template that inherits from the parent template.
7231 Local<v8::FunctionTemplate> child_templ = v8::FunctionTemplate::New();
7232 Local<ObjectTemplate> child_instance_templ =
7233 child_templ->InstanceTemplate();
7234 child_templ->Inherit(parent_templ);
7235 // Override 'f'. The child version of 'f' should get called for child
7236 // instances.
7237 child_instance_templ->SetAccessor(v8_str("f"), ChildGetter);
7238 // Add 'g' twice. The 'g' added last should get called for instances.
7239 child_instance_templ->SetAccessor(v8_str("g"), ParentGetter);
7240 child_instance_templ->SetAccessor(v8_str("g"), ChildGetter);
7241
7242 // Add 'h' as an accessor to the proto template with ReadOnly attributes
7243 // so 'h' can be shadowed on the instance object.
7244 Local<ObjectTemplate> child_proto_templ = child_templ->PrototypeTemplate();
7245 child_proto_templ->SetAccessor(v8_str("h"), ParentGetter, 0,
7246 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly);
7247
7248 // Add 'i' as an accessor to the instance template with ReadOnly attributes
7249 // but the attribute does not have effect because it is duplicated with
7250 // NULL setter.
7251 child_instance_templ->SetAccessor(v8_str("i"), ChildGetter, 0,
7252 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly);
7253
7254
7255
7256 // Instantiate the child template.
7257 Local<v8::Object> instance = child_templ->GetFunction()->NewInstance();
7258
7259 // Check that the child function overrides the parent one.
7260 context->Global()->Set(v8_str("o"), instance);
7261 Local<Value> value = v8_compile("o.f")->Run();
7262 // Check that the 'g' that was added last is hit.
7263 CHECK_EQ(42, value->Int32Value());
7264 value = v8_compile("o.g")->Run();
7265 CHECK_EQ(42, value->Int32Value());
7266
7267 // Check 'h' can be shadowed.
7268 value = v8_compile("o.h = 3; o.h")->Run();
7269 CHECK_EQ(3, value->Int32Value());
7270
7271 // Check 'i' is cannot be shadowed or changed.
7272 value = v8_compile("o.i = 3; o.i")->Run();
7273 CHECK_EQ(42, value->Int32Value());
7274}
7275
7276
7277static v8::Handle<Value> IsConstructHandler(const v8::Arguments& args) {
7278 ApiTestFuzzer::Fuzz();
7279 if (args.IsConstructCall()) {
7280 return v8::Boolean::New(true);
7281 }
7282 return v8::Boolean::New(false);
7283}
7284
7285
7286THREADED_TEST(IsConstructCall) {
7287 v8::HandleScope scope;
7288
7289 // Function template with call handler.
7290 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
7291 templ->SetCallHandler(IsConstructHandler);
7292
7293 LocalContext context;
7294
7295 context->Global()->Set(v8_str("f"), templ->GetFunction());
7296 Local<Value> value = v8_compile("f()")->Run();
7297 CHECK(!value->BooleanValue());
7298 value = v8_compile("new f()")->Run();
7299 CHECK(value->BooleanValue());
7300}
7301
7302
7303THREADED_TEST(ObjectProtoToString) {
7304 v8::HandleScope scope;
7305 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
7306 templ->SetClassName(v8_str("MyClass"));
7307
7308 LocalContext context;
7309
7310 Local<String> customized_tostring = v8_str("customized toString");
7311
7312 // Replace Object.prototype.toString
7313 v8_compile("Object.prototype.toString = function() {"
7314 " return 'customized toString';"
7315 "}")->Run();
7316
7317 // Normal ToString call should call replaced Object.prototype.toString
7318 Local<v8::Object> instance = templ->GetFunction()->NewInstance();
7319 Local<String> value = instance->ToString();
7320 CHECK(value->IsString() && value->Equals(customized_tostring));
7321
7322 // ObjectProtoToString should not call replace toString function.
7323 value = instance->ObjectProtoToString();
7324 CHECK(value->IsString() && value->Equals(v8_str("[object MyClass]")));
7325
7326 // Check global
7327 value = context->Global()->ObjectProtoToString();
7328 CHECK(value->IsString() && value->Equals(v8_str("[object global]")));
7329
7330 // Check ordinary object
7331 Local<Value> object = v8_compile("new Object()")->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01007332 value = object.As<v8::Object>()->ObjectProtoToString();
Steve Blocka7e24c12009-10-30 11:49:00 +00007333 CHECK(value->IsString() && value->Equals(v8_str("[object Object]")));
7334}
7335
7336
7337bool ApiTestFuzzer::fuzzing_ = false;
7338v8::internal::Semaphore* ApiTestFuzzer::all_tests_done_=
7339 v8::internal::OS::CreateSemaphore(0);
7340int ApiTestFuzzer::active_tests_;
7341int ApiTestFuzzer::tests_being_run_;
7342int ApiTestFuzzer::current_;
7343
7344
7345// We are in a callback and want to switch to another thread (if we
7346// are currently running the thread fuzzing test).
7347void ApiTestFuzzer::Fuzz() {
7348 if (!fuzzing_) return;
7349 ApiTestFuzzer* test = RegisterThreadedTest::nth(current_)->fuzzer_;
7350 test->ContextSwitch();
7351}
7352
7353
7354// Let the next thread go. Since it is also waiting on the V8 lock it may
7355// not start immediately.
7356bool ApiTestFuzzer::NextThread() {
7357 int test_position = GetNextTestNumber();
Steve Blockd0582a62009-12-15 09:54:21 +00007358 const char* test_name = RegisterThreadedTest::nth(current_)->name();
Steve Blocka7e24c12009-10-30 11:49:00 +00007359 if (test_position == current_) {
Steve Blockd0582a62009-12-15 09:54:21 +00007360 if (kLogThreading)
7361 printf("Stay with %s\n", test_name);
Steve Blocka7e24c12009-10-30 11:49:00 +00007362 return false;
7363 }
Steve Blockd0582a62009-12-15 09:54:21 +00007364 if (kLogThreading) {
7365 printf("Switch from %s to %s\n",
7366 test_name,
7367 RegisterThreadedTest::nth(test_position)->name());
7368 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007369 current_ = test_position;
7370 RegisterThreadedTest::nth(current_)->fuzzer_->gate_->Signal();
7371 return true;
7372}
7373
7374
7375void ApiTestFuzzer::Run() {
7376 // When it is our turn...
7377 gate_->Wait();
7378 {
7379 // ... get the V8 lock and start running the test.
7380 v8::Locker locker;
7381 CallTest();
7382 }
7383 // This test finished.
7384 active_ = false;
7385 active_tests_--;
7386 // If it was the last then signal that fact.
7387 if (active_tests_ == 0) {
7388 all_tests_done_->Signal();
7389 } else {
7390 // Otherwise select a new test and start that.
7391 NextThread();
7392 }
7393}
7394
7395
7396static unsigned linear_congruential_generator;
7397
7398
7399void ApiTestFuzzer::Setup(PartOfTest part) {
7400 linear_congruential_generator = i::FLAG_testing_prng_seed;
7401 fuzzing_ = true;
7402 int start = (part == FIRST_PART) ? 0 : (RegisterThreadedTest::count() >> 1);
7403 int end = (part == FIRST_PART)
7404 ? (RegisterThreadedTest::count() >> 1)
7405 : RegisterThreadedTest::count();
7406 active_tests_ = tests_being_run_ = end - start;
7407 for (int i = 0; i < tests_being_run_; i++) {
7408 RegisterThreadedTest::nth(i)->fuzzer_ = new ApiTestFuzzer(i + start);
7409 }
7410 for (int i = 0; i < active_tests_; i++) {
7411 RegisterThreadedTest::nth(i)->fuzzer_->Start();
7412 }
7413}
7414
7415
7416static void CallTestNumber(int test_number) {
7417 (RegisterThreadedTest::nth(test_number)->callback())();
7418}
7419
7420
7421void ApiTestFuzzer::RunAllTests() {
7422 // Set off the first test.
7423 current_ = -1;
7424 NextThread();
7425 // Wait till they are all done.
7426 all_tests_done_->Wait();
7427}
7428
7429
7430int ApiTestFuzzer::GetNextTestNumber() {
7431 int next_test;
7432 do {
7433 next_test = (linear_congruential_generator >> 16) % tests_being_run_;
7434 linear_congruential_generator *= 1664525u;
7435 linear_congruential_generator += 1013904223u;
7436 } while (!RegisterThreadedTest::nth(next_test)->fuzzer_->active_);
7437 return next_test;
7438}
7439
7440
7441void ApiTestFuzzer::ContextSwitch() {
7442 // If the new thread is the same as the current thread there is nothing to do.
7443 if (NextThread()) {
7444 // Now it can start.
7445 v8::Unlocker unlocker;
7446 // Wait till someone starts us again.
7447 gate_->Wait();
7448 // And we're off.
7449 }
7450}
7451
7452
7453void ApiTestFuzzer::TearDown() {
7454 fuzzing_ = false;
7455 for (int i = 0; i < RegisterThreadedTest::count(); i++) {
7456 ApiTestFuzzer *fuzzer = RegisterThreadedTest::nth(i)->fuzzer_;
7457 if (fuzzer != NULL) fuzzer->Join();
7458 }
7459}
7460
7461
7462// Lets not be needlessly self-referential.
7463TEST(Threading) {
7464 ApiTestFuzzer::Setup(ApiTestFuzzer::FIRST_PART);
7465 ApiTestFuzzer::RunAllTests();
7466 ApiTestFuzzer::TearDown();
7467}
7468
7469TEST(Threading2) {
7470 ApiTestFuzzer::Setup(ApiTestFuzzer::SECOND_PART);
7471 ApiTestFuzzer::RunAllTests();
7472 ApiTestFuzzer::TearDown();
7473}
7474
7475
7476void ApiTestFuzzer::CallTest() {
Steve Blockd0582a62009-12-15 09:54:21 +00007477 if (kLogThreading)
7478 printf("Start test %d\n", test_number_);
Steve Blocka7e24c12009-10-30 11:49:00 +00007479 CallTestNumber(test_number_);
Steve Blockd0582a62009-12-15 09:54:21 +00007480 if (kLogThreading)
7481 printf("End test %d\n", test_number_);
Steve Blocka7e24c12009-10-30 11:49:00 +00007482}
7483
7484
7485static v8::Handle<Value> ThrowInJS(const v8::Arguments& args) {
7486 CHECK(v8::Locker::IsLocked());
7487 ApiTestFuzzer::Fuzz();
7488 v8::Unlocker unlocker;
7489 const char* code = "throw 7;";
7490 {
7491 v8::Locker nested_locker;
7492 v8::HandleScope scope;
7493 v8::Handle<Value> exception;
7494 { v8::TryCatch try_catch;
7495 v8::Handle<Value> value = CompileRun(code);
7496 CHECK(value.IsEmpty());
7497 CHECK(try_catch.HasCaught());
7498 // Make sure to wrap the exception in a new handle because
7499 // the handle returned from the TryCatch is destroyed
7500 // when the TryCatch is destroyed.
7501 exception = Local<Value>::New(try_catch.Exception());
7502 }
7503 return v8::ThrowException(exception);
7504 }
7505}
7506
7507
7508static v8::Handle<Value> ThrowInJSNoCatch(const v8::Arguments& args) {
7509 CHECK(v8::Locker::IsLocked());
7510 ApiTestFuzzer::Fuzz();
7511 v8::Unlocker unlocker;
7512 const char* code = "throw 7;";
7513 {
7514 v8::Locker nested_locker;
7515 v8::HandleScope scope;
7516 v8::Handle<Value> value = CompileRun(code);
7517 CHECK(value.IsEmpty());
7518 return v8_str("foo");
7519 }
7520}
7521
7522
7523// These are locking tests that don't need to be run again
7524// as part of the locking aggregation tests.
7525TEST(NestedLockers) {
7526 v8::Locker locker;
7527 CHECK(v8::Locker::IsLocked());
7528 v8::HandleScope scope;
7529 LocalContext env;
7530 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(ThrowInJS);
7531 Local<Function> fun = fun_templ->GetFunction();
7532 env->Global()->Set(v8_str("throw_in_js"), fun);
7533 Local<Script> script = v8_compile("(function () {"
7534 " try {"
7535 " throw_in_js();"
7536 " return 42;"
7537 " } catch (e) {"
7538 " return e * 13;"
7539 " }"
7540 "})();");
7541 CHECK_EQ(91, script->Run()->Int32Value());
7542}
7543
7544
7545// These are locking tests that don't need to be run again
7546// as part of the locking aggregation tests.
7547TEST(NestedLockersNoTryCatch) {
7548 v8::Locker locker;
7549 v8::HandleScope scope;
7550 LocalContext env;
7551 Local<v8::FunctionTemplate> fun_templ =
7552 v8::FunctionTemplate::New(ThrowInJSNoCatch);
7553 Local<Function> fun = fun_templ->GetFunction();
7554 env->Global()->Set(v8_str("throw_in_js"), fun);
7555 Local<Script> script = v8_compile("(function () {"
7556 " try {"
7557 " throw_in_js();"
7558 " return 42;"
7559 " } catch (e) {"
7560 " return e * 13;"
7561 " }"
7562 "})();");
7563 CHECK_EQ(91, script->Run()->Int32Value());
7564}
7565
7566
7567THREADED_TEST(RecursiveLocking) {
7568 v8::Locker locker;
7569 {
7570 v8::Locker locker2;
7571 CHECK(v8::Locker::IsLocked());
7572 }
7573}
7574
7575
7576static v8::Handle<Value> UnlockForAMoment(const v8::Arguments& args) {
7577 ApiTestFuzzer::Fuzz();
7578 v8::Unlocker unlocker;
7579 return v8::Undefined();
7580}
7581
7582
7583THREADED_TEST(LockUnlockLock) {
7584 {
7585 v8::Locker locker;
7586 v8::HandleScope scope;
7587 LocalContext env;
7588 Local<v8::FunctionTemplate> fun_templ =
7589 v8::FunctionTemplate::New(UnlockForAMoment);
7590 Local<Function> fun = fun_templ->GetFunction();
7591 env->Global()->Set(v8_str("unlock_for_a_moment"), fun);
7592 Local<Script> script = v8_compile("(function () {"
7593 " unlock_for_a_moment();"
7594 " return 42;"
7595 "})();");
7596 CHECK_EQ(42, script->Run()->Int32Value());
7597 }
7598 {
7599 v8::Locker locker;
7600 v8::HandleScope scope;
7601 LocalContext env;
7602 Local<v8::FunctionTemplate> fun_templ =
7603 v8::FunctionTemplate::New(UnlockForAMoment);
7604 Local<Function> fun = fun_templ->GetFunction();
7605 env->Global()->Set(v8_str("unlock_for_a_moment"), fun);
7606 Local<Script> script = v8_compile("(function () {"
7607 " unlock_for_a_moment();"
7608 " return 42;"
7609 "})();");
7610 CHECK_EQ(42, script->Run()->Int32Value());
7611 }
7612}
7613
7614
Leon Clarked91b9f72010-01-27 17:25:45 +00007615static int GetGlobalObjectsCount() {
Leon Clarkeeab96aa2010-01-27 16:31:12 +00007616 int count = 0;
Leon Clarked91b9f72010-01-27 17:25:45 +00007617 v8::internal::HeapIterator it;
7618 for (i::HeapObject* object = it.next(); object != NULL; object = it.next())
7619 if (object->IsJSGlobalObject()) count++;
7620 return count;
7621}
7622
7623
7624static int GetSurvivingGlobalObjectsCount() {
Steve Blocka7e24c12009-10-30 11:49:00 +00007625 // We need to collect all garbage twice to be sure that everything
7626 // has been collected. This is because inline caches are cleared in
7627 // the first garbage collection but some of the maps have already
7628 // been marked at that point. Therefore some of the maps are not
7629 // collected until the second garbage collection.
7630 v8::internal::Heap::CollectAllGarbage(false);
7631 v8::internal::Heap::CollectAllGarbage(false);
Leon Clarked91b9f72010-01-27 17:25:45 +00007632 int count = GetGlobalObjectsCount();
Steve Blocka7e24c12009-10-30 11:49:00 +00007633#ifdef DEBUG
7634 if (count > 0) v8::internal::Heap::TracePathToGlobal();
7635#endif
7636 return count;
7637}
7638
7639
7640TEST(DontLeakGlobalObjects) {
7641 // Regression test for issues 1139850 and 1174891.
7642
7643 v8::V8::Initialize();
7644
7645 int count = GetSurvivingGlobalObjectsCount();
7646
7647 for (int i = 0; i < 5; i++) {
7648 { v8::HandleScope scope;
7649 LocalContext context;
7650 }
7651 CHECK_EQ(count, GetSurvivingGlobalObjectsCount());
7652
7653 { v8::HandleScope scope;
7654 LocalContext context;
7655 v8_compile("Date")->Run();
7656 }
7657 CHECK_EQ(count, GetSurvivingGlobalObjectsCount());
7658
7659 { v8::HandleScope scope;
7660 LocalContext context;
7661 v8_compile("/aaa/")->Run();
7662 }
7663 CHECK_EQ(count, GetSurvivingGlobalObjectsCount());
7664
7665 { v8::HandleScope scope;
7666 const char* extension_list[] = { "v8/gc" };
7667 v8::ExtensionConfiguration extensions(1, extension_list);
7668 LocalContext context(&extensions);
7669 v8_compile("gc();")->Run();
7670 }
7671 CHECK_EQ(count, GetSurvivingGlobalObjectsCount());
7672 }
7673}
7674
7675
7676v8::Persistent<v8::Object> some_object;
7677v8::Persistent<v8::Object> bad_handle;
7678
7679void NewPersistentHandleCallback(v8::Persistent<v8::Value>, void*) {
7680 v8::HandleScope scope;
7681 bad_handle = v8::Persistent<v8::Object>::New(some_object);
7682}
7683
7684
7685THREADED_TEST(NewPersistentHandleFromWeakCallback) {
7686 LocalContext context;
7687
7688 v8::Persistent<v8::Object> handle1, handle2;
7689 {
7690 v8::HandleScope scope;
7691 some_object = v8::Persistent<v8::Object>::New(v8::Object::New());
7692 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
7693 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
7694 }
7695 // Note: order is implementation dependent alas: currently
7696 // global handle nodes are processed by PostGarbageCollectionProcessing
7697 // in reverse allocation order, so if second allocated handle is deleted,
7698 // weak callback of the first handle would be able to 'reallocate' it.
7699 handle1.MakeWeak(NULL, NewPersistentHandleCallback);
7700 handle2.Dispose();
7701 i::Heap::CollectAllGarbage(false);
7702}
7703
7704
7705v8::Persistent<v8::Object> to_be_disposed;
7706
7707void DisposeAndForceGcCallback(v8::Persistent<v8::Value> handle, void*) {
7708 to_be_disposed.Dispose();
7709 i::Heap::CollectAllGarbage(false);
7710}
7711
7712
7713THREADED_TEST(DoNotUseDeletedNodesInSecondLevelGc) {
7714 LocalContext context;
7715
7716 v8::Persistent<v8::Object> handle1, handle2;
7717 {
7718 v8::HandleScope scope;
7719 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
7720 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
7721 }
7722 handle1.MakeWeak(NULL, DisposeAndForceGcCallback);
7723 to_be_disposed = handle2;
7724 i::Heap::CollectAllGarbage(false);
7725}
7726
Steve Blockd0582a62009-12-15 09:54:21 +00007727void DisposingCallback(v8::Persistent<v8::Value> handle, void*) {
7728 handle.Dispose();
7729}
7730
7731void HandleCreatingCallback(v8::Persistent<v8::Value> handle, void*) {
7732 v8::HandleScope scope;
7733 v8::Persistent<v8::Object>::New(v8::Object::New());
7734}
7735
7736
7737THREADED_TEST(NoGlobalHandlesOrphaningDueToWeakCallback) {
7738 LocalContext context;
7739
7740 v8::Persistent<v8::Object> handle1, handle2, handle3;
7741 {
7742 v8::HandleScope scope;
7743 handle3 = v8::Persistent<v8::Object>::New(v8::Object::New());
7744 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
7745 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
7746 }
7747 handle2.MakeWeak(NULL, DisposingCallback);
7748 handle3.MakeWeak(NULL, HandleCreatingCallback);
7749 i::Heap::CollectAllGarbage(false);
7750}
7751
Steve Blocka7e24c12009-10-30 11:49:00 +00007752
7753THREADED_TEST(CheckForCrossContextObjectLiterals) {
7754 v8::V8::Initialize();
7755
7756 const int nof = 2;
7757 const char* sources[nof] = {
7758 "try { [ 2, 3, 4 ].forEach(5); } catch(e) { e.toString(); }",
7759 "Object()"
7760 };
7761
7762 for (int i = 0; i < nof; i++) {
7763 const char* source = sources[i];
7764 { v8::HandleScope scope;
7765 LocalContext context;
7766 CompileRun(source);
7767 }
7768 { v8::HandleScope scope;
7769 LocalContext context;
7770 CompileRun(source);
7771 }
7772 }
7773}
7774
7775
7776static v8::Handle<Value> NestedScope(v8::Persistent<Context> env) {
7777 v8::HandleScope inner;
7778 env->Enter();
7779 v8::Handle<Value> three = v8_num(3);
7780 v8::Handle<Value> value = inner.Close(three);
7781 env->Exit();
7782 return value;
7783}
7784
7785
7786THREADED_TEST(NestedHandleScopeAndContexts) {
7787 v8::HandleScope outer;
7788 v8::Persistent<Context> env = Context::New();
7789 env->Enter();
7790 v8::Handle<Value> value = NestedScope(env);
7791 v8::Handle<String> str = value->ToString();
7792 env->Exit();
7793 env.Dispose();
7794}
7795
7796
7797THREADED_TEST(ExternalAllocatedMemory) {
7798 v8::HandleScope outer;
7799 v8::Persistent<Context> env = Context::New();
7800 const int kSize = 1024*1024;
7801 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(kSize), kSize);
7802 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(-kSize), 0);
7803}
7804
7805
7806THREADED_TEST(DisposeEnteredContext) {
7807 v8::HandleScope scope;
7808 LocalContext outer;
7809 { v8::Persistent<v8::Context> inner = v8::Context::New();
7810 inner->Enter();
7811 inner.Dispose();
7812 inner.Clear();
7813 inner->Exit();
7814 }
7815}
7816
7817
7818// Regression test for issue 54, object templates with internal fields
7819// but no accessors or interceptors did not get their internal field
7820// count set on instances.
7821THREADED_TEST(Regress54) {
7822 v8::HandleScope outer;
7823 LocalContext context;
7824 static v8::Persistent<v8::ObjectTemplate> templ;
7825 if (templ.IsEmpty()) {
7826 v8::HandleScope inner;
7827 v8::Handle<v8::ObjectTemplate> local = v8::ObjectTemplate::New();
7828 local->SetInternalFieldCount(1);
7829 templ = v8::Persistent<v8::ObjectTemplate>::New(inner.Close(local));
7830 }
7831 v8::Handle<v8::Object> result = templ->NewInstance();
7832 CHECK_EQ(1, result->InternalFieldCount());
7833}
7834
7835
7836// If part of the threaded tests, this test makes ThreadingTest fail
7837// on mac.
7838TEST(CatchStackOverflow) {
7839 v8::HandleScope scope;
7840 LocalContext context;
7841 v8::TryCatch try_catch;
7842 v8::Handle<v8::Script> script = v8::Script::Compile(v8::String::New(
7843 "function f() {"
7844 " return f();"
7845 "}"
7846 ""
7847 "f();"));
7848 v8::Handle<v8::Value> result = script->Run();
7849 CHECK(result.IsEmpty());
7850}
7851
7852
7853static void CheckTryCatchSourceInfo(v8::Handle<v8::Script> script,
7854 const char* resource_name,
7855 int line_offset) {
7856 v8::HandleScope scope;
7857 v8::TryCatch try_catch;
7858 v8::Handle<v8::Value> result = script->Run();
7859 CHECK(result.IsEmpty());
7860 CHECK(try_catch.HasCaught());
7861 v8::Handle<v8::Message> message = try_catch.Message();
7862 CHECK(!message.IsEmpty());
7863 CHECK_EQ(10 + line_offset, message->GetLineNumber());
7864 CHECK_EQ(91, message->GetStartPosition());
7865 CHECK_EQ(92, message->GetEndPosition());
7866 CHECK_EQ(2, message->GetStartColumn());
7867 CHECK_EQ(3, message->GetEndColumn());
7868 v8::String::AsciiValue line(message->GetSourceLine());
7869 CHECK_EQ(" throw 'nirk';", *line);
7870 v8::String::AsciiValue name(message->GetScriptResourceName());
7871 CHECK_EQ(resource_name, *name);
7872}
7873
7874
7875THREADED_TEST(TryCatchSourceInfo) {
7876 v8::HandleScope scope;
7877 LocalContext context;
7878 v8::Handle<v8::String> source = v8::String::New(
7879 "function Foo() {\n"
7880 " return Bar();\n"
7881 "}\n"
7882 "\n"
7883 "function Bar() {\n"
7884 " return Baz();\n"
7885 "}\n"
7886 "\n"
7887 "function Baz() {\n"
7888 " throw 'nirk';\n"
7889 "}\n"
7890 "\n"
7891 "Foo();\n");
7892
7893 const char* resource_name;
7894 v8::Handle<v8::Script> script;
7895 resource_name = "test.js";
7896 script = v8::Script::Compile(source, v8::String::New(resource_name));
7897 CheckTryCatchSourceInfo(script, resource_name, 0);
7898
7899 resource_name = "test1.js";
7900 v8::ScriptOrigin origin1(v8::String::New(resource_name));
7901 script = v8::Script::Compile(source, &origin1);
7902 CheckTryCatchSourceInfo(script, resource_name, 0);
7903
7904 resource_name = "test2.js";
7905 v8::ScriptOrigin origin2(v8::String::New(resource_name), v8::Integer::New(7));
7906 script = v8::Script::Compile(source, &origin2);
7907 CheckTryCatchSourceInfo(script, resource_name, 7);
7908}
7909
7910
7911THREADED_TEST(CompilationCache) {
7912 v8::HandleScope scope;
7913 LocalContext context;
7914 v8::Handle<v8::String> source0 = v8::String::New("1234");
7915 v8::Handle<v8::String> source1 = v8::String::New("1234");
7916 v8::Handle<v8::Script> script0 =
7917 v8::Script::Compile(source0, v8::String::New("test.js"));
7918 v8::Handle<v8::Script> script1 =
7919 v8::Script::Compile(source1, v8::String::New("test.js"));
7920 v8::Handle<v8::Script> script2 =
7921 v8::Script::Compile(source0); // different origin
7922 CHECK_EQ(1234, script0->Run()->Int32Value());
7923 CHECK_EQ(1234, script1->Run()->Int32Value());
7924 CHECK_EQ(1234, script2->Run()->Int32Value());
7925}
7926
7927
7928static v8::Handle<Value> FunctionNameCallback(const v8::Arguments& args) {
7929 ApiTestFuzzer::Fuzz();
7930 return v8_num(42);
7931}
7932
7933
7934THREADED_TEST(CallbackFunctionName) {
7935 v8::HandleScope scope;
7936 LocalContext context;
7937 Local<ObjectTemplate> t = ObjectTemplate::New();
7938 t->Set(v8_str("asdf"), v8::FunctionTemplate::New(FunctionNameCallback));
7939 context->Global()->Set(v8_str("obj"), t->NewInstance());
7940 v8::Handle<v8::Value> value = CompileRun("obj.asdf.name");
7941 CHECK(value->IsString());
7942 v8::String::AsciiValue name(value);
7943 CHECK_EQ("asdf", *name);
7944}
7945
7946
7947THREADED_TEST(DateAccess) {
7948 v8::HandleScope scope;
7949 LocalContext context;
7950 v8::Handle<v8::Value> date = v8::Date::New(1224744689038.0);
7951 CHECK(date->IsDate());
Steve Block6ded16b2010-05-10 14:33:55 +01007952 CHECK_EQ(1224744689038.0, date.As<v8::Date>()->NumberValue());
Steve Blocka7e24c12009-10-30 11:49:00 +00007953}
7954
7955
7956void CheckProperties(v8::Handle<v8::Value> val, int elmc, const char* elmv[]) {
Steve Block6ded16b2010-05-10 14:33:55 +01007957 v8::Handle<v8::Object> obj = val.As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00007958 v8::Handle<v8::Array> props = obj->GetPropertyNames();
7959 CHECK_EQ(elmc, props->Length());
7960 for (int i = 0; i < elmc; i++) {
7961 v8::String::Utf8Value elm(props->Get(v8::Integer::New(i)));
7962 CHECK_EQ(elmv[i], *elm);
7963 }
7964}
7965
7966
7967THREADED_TEST(PropertyEnumeration) {
7968 v8::HandleScope scope;
7969 LocalContext context;
7970 v8::Handle<v8::Value> obj = v8::Script::Compile(v8::String::New(
7971 "var result = [];"
7972 "result[0] = {};"
7973 "result[1] = {a: 1, b: 2};"
7974 "result[2] = [1, 2, 3];"
7975 "var proto = {x: 1, y: 2, z: 3};"
7976 "var x = { __proto__: proto, w: 0, z: 1 };"
7977 "result[3] = x;"
7978 "result;"))->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01007979 v8::Handle<v8::Array> elms = obj.As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00007980 CHECK_EQ(4, elms->Length());
7981 int elmc0 = 0;
7982 const char** elmv0 = NULL;
7983 CheckProperties(elms->Get(v8::Integer::New(0)), elmc0, elmv0);
7984 int elmc1 = 2;
7985 const char* elmv1[] = {"a", "b"};
7986 CheckProperties(elms->Get(v8::Integer::New(1)), elmc1, elmv1);
7987 int elmc2 = 3;
7988 const char* elmv2[] = {"0", "1", "2"};
7989 CheckProperties(elms->Get(v8::Integer::New(2)), elmc2, elmv2);
7990 int elmc3 = 4;
7991 const char* elmv3[] = {"w", "z", "x", "y"};
7992 CheckProperties(elms->Get(v8::Integer::New(3)), elmc3, elmv3);
7993}
7994
7995
Steve Blocka7e24c12009-10-30 11:49:00 +00007996static bool NamedSetAccessBlocker(Local<v8::Object> obj,
7997 Local<Value> name,
7998 v8::AccessType type,
7999 Local<Value> data) {
8000 return type != v8::ACCESS_SET;
8001}
8002
8003
8004static bool IndexedSetAccessBlocker(Local<v8::Object> obj,
8005 uint32_t key,
8006 v8::AccessType type,
8007 Local<Value> data) {
8008 return type != v8::ACCESS_SET;
8009}
8010
8011
8012THREADED_TEST(DisableAccessChecksWhileConfiguring) {
8013 v8::HandleScope scope;
8014 LocalContext context;
8015 Local<ObjectTemplate> templ = ObjectTemplate::New();
8016 templ->SetAccessCheckCallbacks(NamedSetAccessBlocker,
8017 IndexedSetAccessBlocker);
8018 templ->Set(v8_str("x"), v8::True());
8019 Local<v8::Object> instance = templ->NewInstance();
8020 context->Global()->Set(v8_str("obj"), instance);
8021 Local<Value> value = CompileRun("obj.x");
8022 CHECK(value->BooleanValue());
8023}
8024
8025
8026static bool NamedGetAccessBlocker(Local<v8::Object> obj,
8027 Local<Value> name,
8028 v8::AccessType type,
8029 Local<Value> data) {
8030 return false;
8031}
8032
8033
8034static bool IndexedGetAccessBlocker(Local<v8::Object> obj,
8035 uint32_t key,
8036 v8::AccessType type,
8037 Local<Value> data) {
8038 return false;
8039}
8040
8041
8042
8043THREADED_TEST(AccessChecksReenabledCorrectly) {
8044 v8::HandleScope scope;
8045 LocalContext context;
8046 Local<ObjectTemplate> templ = ObjectTemplate::New();
8047 templ->SetAccessCheckCallbacks(NamedGetAccessBlocker,
8048 IndexedGetAccessBlocker);
8049 templ->Set(v8_str("a"), v8_str("a"));
8050 // Add more than 8 (see kMaxFastProperties) properties
8051 // so that the constructor will force copying map.
8052 // Cannot sprintf, gcc complains unsafety.
8053 char buf[4];
8054 for (char i = '0'; i <= '9' ; i++) {
8055 buf[0] = i;
8056 for (char j = '0'; j <= '9'; j++) {
8057 buf[1] = j;
8058 for (char k = '0'; k <= '9'; k++) {
8059 buf[2] = k;
8060 buf[3] = 0;
8061 templ->Set(v8_str(buf), v8::Number::New(k));
8062 }
8063 }
8064 }
8065
8066 Local<v8::Object> instance_1 = templ->NewInstance();
8067 context->Global()->Set(v8_str("obj_1"), instance_1);
8068
8069 Local<Value> value_1 = CompileRun("obj_1.a");
8070 CHECK(value_1->IsUndefined());
8071
8072 Local<v8::Object> instance_2 = templ->NewInstance();
8073 context->Global()->Set(v8_str("obj_2"), instance_2);
8074
8075 Local<Value> value_2 = CompileRun("obj_2.a");
8076 CHECK(value_2->IsUndefined());
8077}
8078
8079
8080// This tests that access check information remains on the global
8081// object template when creating contexts.
8082THREADED_TEST(AccessControlRepeatedContextCreation) {
8083 v8::HandleScope handle_scope;
8084 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
8085 global_template->SetAccessCheckCallbacks(NamedSetAccessBlocker,
8086 IndexedSetAccessBlocker);
8087 i::Handle<i::ObjectTemplateInfo> internal_template =
8088 v8::Utils::OpenHandle(*global_template);
8089 CHECK(!internal_template->constructor()->IsUndefined());
8090 i::Handle<i::FunctionTemplateInfo> constructor(
8091 i::FunctionTemplateInfo::cast(internal_template->constructor()));
8092 CHECK(!constructor->access_check_info()->IsUndefined());
8093 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
8094 CHECK(!constructor->access_check_info()->IsUndefined());
8095}
8096
8097
8098THREADED_TEST(TurnOnAccessCheck) {
8099 v8::HandleScope handle_scope;
8100
8101 // Create an environment with access check to the global object disabled by
8102 // default.
8103 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
8104 global_template->SetAccessCheckCallbacks(NamedGetAccessBlocker,
8105 IndexedGetAccessBlocker,
8106 v8::Handle<v8::Value>(),
8107 false);
8108 v8::Persistent<Context> context = Context::New(NULL, global_template);
8109 Context::Scope context_scope(context);
8110
8111 // Set up a property and a number of functions.
8112 context->Global()->Set(v8_str("a"), v8_num(1));
8113 CompileRun("function f1() {return a;}"
8114 "function f2() {return a;}"
8115 "function g1() {return h();}"
8116 "function g2() {return h();}"
8117 "function h() {return 1;}");
8118 Local<Function> f1 =
8119 Local<Function>::Cast(context->Global()->Get(v8_str("f1")));
8120 Local<Function> f2 =
8121 Local<Function>::Cast(context->Global()->Get(v8_str("f2")));
8122 Local<Function> g1 =
8123 Local<Function>::Cast(context->Global()->Get(v8_str("g1")));
8124 Local<Function> g2 =
8125 Local<Function>::Cast(context->Global()->Get(v8_str("g2")));
8126 Local<Function> h =
8127 Local<Function>::Cast(context->Global()->Get(v8_str("h")));
8128
8129 // Get the global object.
8130 v8::Handle<v8::Object> global = context->Global();
8131
8132 // Call f1 one time and f2 a number of times. This will ensure that f1 still
8133 // uses the runtime system to retreive property a whereas f2 uses global load
8134 // inline cache.
8135 CHECK(f1->Call(global, 0, NULL)->Equals(v8_num(1)));
8136 for (int i = 0; i < 4; i++) {
8137 CHECK(f2->Call(global, 0, NULL)->Equals(v8_num(1)));
8138 }
8139
8140 // Same for g1 and g2.
8141 CHECK(g1->Call(global, 0, NULL)->Equals(v8_num(1)));
8142 for (int i = 0; i < 4; i++) {
8143 CHECK(g2->Call(global, 0, NULL)->Equals(v8_num(1)));
8144 }
8145
8146 // Detach the global and turn on access check.
8147 context->DetachGlobal();
8148 context->Global()->TurnOnAccessCheck();
8149
8150 // Failing access check to property get results in undefined.
8151 CHECK(f1->Call(global, 0, NULL)->IsUndefined());
8152 CHECK(f2->Call(global, 0, NULL)->IsUndefined());
8153
8154 // Failing access check to function call results in exception.
8155 CHECK(g1->Call(global, 0, NULL).IsEmpty());
8156 CHECK(g2->Call(global, 0, NULL).IsEmpty());
8157
8158 // No failing access check when just returning a constant.
8159 CHECK(h->Call(global, 0, NULL)->Equals(v8_num(1)));
8160}
8161
8162
8163// This test verifies that pre-compilation (aka preparsing) can be called
8164// without initializing the whole VM. Thus we cannot run this test in a
8165// multi-threaded setup.
8166TEST(PreCompile) {
8167 // TODO(155): This test would break without the initialization of V8. This is
8168 // a workaround for now to make this test not fail.
8169 v8::V8::Initialize();
Leon Clarkef7060e22010-06-03 12:02:55 +01008170 const char* script = "function foo(a) { return a+1; }";
8171 v8::ScriptData* sd =
Steve Blockd0582a62009-12-15 09:54:21 +00008172 v8::ScriptData::PreCompile(script, i::StrLength(script));
Steve Blocka7e24c12009-10-30 11:49:00 +00008173 CHECK_NE(sd->Length(), 0);
8174 CHECK_NE(sd->Data(), NULL);
Leon Clarkee46be812010-01-19 14:06:41 +00008175 CHECK(!sd->HasError());
8176 delete sd;
8177}
8178
8179
8180TEST(PreCompileWithError) {
8181 v8::V8::Initialize();
Leon Clarkef7060e22010-06-03 12:02:55 +01008182 const char* script = "function foo(a) { return 1 * * 2; }";
8183 v8::ScriptData* sd =
Leon Clarkee46be812010-01-19 14:06:41 +00008184 v8::ScriptData::PreCompile(script, i::StrLength(script));
8185 CHECK(sd->HasError());
8186 delete sd;
8187}
8188
8189
8190TEST(Regress31661) {
8191 v8::V8::Initialize();
Leon Clarkef7060e22010-06-03 12:02:55 +01008192 const char* script = " The Definintive Guide";
8193 v8::ScriptData* sd =
Leon Clarkee46be812010-01-19 14:06:41 +00008194 v8::ScriptData::PreCompile(script, i::StrLength(script));
8195 CHECK(sd->HasError());
Steve Blocka7e24c12009-10-30 11:49:00 +00008196 delete sd;
8197}
8198
8199
Leon Clarkef7060e22010-06-03 12:02:55 +01008200// Tests that ScriptData can be serialized and deserialized.
8201TEST(PreCompileSerialization) {
8202 v8::V8::Initialize();
8203 const char* script = "function foo(a) { return a+1; }";
8204 v8::ScriptData* sd =
8205 v8::ScriptData::PreCompile(script, i::StrLength(script));
8206
8207 // Serialize.
8208 int serialized_data_length = sd->Length();
8209 char* serialized_data = i::NewArray<char>(serialized_data_length);
8210 memcpy(serialized_data, sd->Data(), serialized_data_length);
8211
8212 // Deserialize.
8213 v8::ScriptData* deserialized_sd =
8214 v8::ScriptData::New(serialized_data, serialized_data_length);
8215
8216 // Verify that the original is the same as the deserialized.
8217 CHECK_EQ(sd->Length(), deserialized_sd->Length());
8218 CHECK_EQ(0, memcmp(sd->Data(), deserialized_sd->Data(), sd->Length()));
8219 CHECK_EQ(sd->HasError(), deserialized_sd->HasError());
8220
8221 delete sd;
8222 delete deserialized_sd;
8223}
8224
8225
8226// Attempts to deserialize bad data.
8227TEST(PreCompileDeserializationError) {
8228 v8::V8::Initialize();
8229 const char* data = "DONT CARE";
8230 int invalid_size = 3;
8231 v8::ScriptData* sd = v8::ScriptData::New(data, invalid_size);
8232
8233 CHECK_EQ(0, sd->Length());
8234
8235 delete sd;
8236}
8237
8238
Steve Blocka7e24c12009-10-30 11:49:00 +00008239// This tests that we do not allow dictionary load/call inline caches
8240// to use functions that have not yet been compiled. The potential
8241// problem of loading a function that has not yet been compiled can
8242// arise because we share code between contexts via the compilation
8243// cache.
8244THREADED_TEST(DictionaryICLoadedFunction) {
8245 v8::HandleScope scope;
8246 // Test LoadIC.
8247 for (int i = 0; i < 2; i++) {
8248 LocalContext context;
8249 context->Global()->Set(v8_str("tmp"), v8::True());
8250 context->Global()->Delete(v8_str("tmp"));
8251 CompileRun("for (var j = 0; j < 10; j++) new RegExp('');");
8252 }
8253 // Test CallIC.
8254 for (int i = 0; i < 2; i++) {
8255 LocalContext context;
8256 context->Global()->Set(v8_str("tmp"), v8::True());
8257 context->Global()->Delete(v8_str("tmp"));
8258 CompileRun("for (var j = 0; j < 10; j++) RegExp('')");
8259 }
8260}
8261
8262
8263// Test that cross-context new calls use the context of the callee to
8264// create the new JavaScript object.
8265THREADED_TEST(CrossContextNew) {
8266 v8::HandleScope scope;
8267 v8::Persistent<Context> context0 = Context::New();
8268 v8::Persistent<Context> context1 = Context::New();
8269
8270 // Allow cross-domain access.
8271 Local<String> token = v8_str("<security token>");
8272 context0->SetSecurityToken(token);
8273 context1->SetSecurityToken(token);
8274
8275 // Set an 'x' property on the Object prototype and define a
8276 // constructor function in context0.
8277 context0->Enter();
8278 CompileRun("Object.prototype.x = 42; function C() {};");
8279 context0->Exit();
8280
8281 // Call the constructor function from context0 and check that the
8282 // result has the 'x' property.
8283 context1->Enter();
8284 context1->Global()->Set(v8_str("other"), context0->Global());
8285 Local<Value> value = CompileRun("var instance = new other.C(); instance.x");
8286 CHECK(value->IsInt32());
8287 CHECK_EQ(42, value->Int32Value());
8288 context1->Exit();
8289
8290 // Dispose the contexts to allow them to be garbage collected.
8291 context0.Dispose();
8292 context1.Dispose();
8293}
8294
8295
8296class RegExpInterruptTest {
8297 public:
8298 RegExpInterruptTest() : block_(NULL) {}
8299 ~RegExpInterruptTest() { delete block_; }
8300 void RunTest() {
8301 block_ = i::OS::CreateSemaphore(0);
8302 gc_count_ = 0;
8303 gc_during_regexp_ = 0;
8304 regexp_success_ = false;
8305 gc_success_ = false;
8306 GCThread gc_thread(this);
8307 gc_thread.Start();
8308 v8::Locker::StartPreemption(1);
8309
8310 LongRunningRegExp();
8311 {
8312 v8::Unlocker unlock;
8313 gc_thread.Join();
8314 }
8315 v8::Locker::StopPreemption();
8316 CHECK(regexp_success_);
8317 CHECK(gc_success_);
8318 }
8319 private:
8320 // Number of garbage collections required.
8321 static const int kRequiredGCs = 5;
8322
8323 class GCThread : public i::Thread {
8324 public:
8325 explicit GCThread(RegExpInterruptTest* test)
8326 : test_(test) {}
8327 virtual void Run() {
8328 test_->CollectGarbage();
8329 }
8330 private:
8331 RegExpInterruptTest* test_;
8332 };
8333
8334 void CollectGarbage() {
8335 block_->Wait();
8336 while (gc_during_regexp_ < kRequiredGCs) {
8337 {
8338 v8::Locker lock;
8339 // TODO(lrn): Perhaps create some garbage before collecting.
8340 i::Heap::CollectAllGarbage(false);
8341 gc_count_++;
8342 }
8343 i::OS::Sleep(1);
8344 }
8345 gc_success_ = true;
8346 }
8347
8348 void LongRunningRegExp() {
8349 block_->Signal(); // Enable garbage collection thread on next preemption.
8350 int rounds = 0;
8351 while (gc_during_regexp_ < kRequiredGCs) {
8352 int gc_before = gc_count_;
8353 {
8354 // Match 15-30 "a"'s against 14 and a "b".
8355 const char* c_source =
8356 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
8357 ".exec('aaaaaaaaaaaaaaab') === null";
8358 Local<String> source = String::New(c_source);
8359 Local<Script> script = Script::Compile(source);
8360 Local<Value> result = script->Run();
8361 if (!result->BooleanValue()) {
8362 gc_during_regexp_ = kRequiredGCs; // Allow gc thread to exit.
8363 return;
8364 }
8365 }
8366 {
8367 // Match 15-30 "a"'s against 15 and a "b".
8368 const char* c_source =
8369 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
8370 ".exec('aaaaaaaaaaaaaaaab')[0] === 'aaaaaaaaaaaaaaaa'";
8371 Local<String> source = String::New(c_source);
8372 Local<Script> script = Script::Compile(source);
8373 Local<Value> result = script->Run();
8374 if (!result->BooleanValue()) {
8375 gc_during_regexp_ = kRequiredGCs;
8376 return;
8377 }
8378 }
8379 int gc_after = gc_count_;
8380 gc_during_regexp_ += gc_after - gc_before;
8381 rounds++;
8382 i::OS::Sleep(1);
8383 }
8384 regexp_success_ = true;
8385 }
8386
8387 i::Semaphore* block_;
8388 int gc_count_;
8389 int gc_during_regexp_;
8390 bool regexp_success_;
8391 bool gc_success_;
8392};
8393
8394
8395// Test that a regular expression execution can be interrupted and
8396// survive a garbage collection.
8397TEST(RegExpInterruption) {
8398 v8::Locker lock;
8399 v8::V8::Initialize();
8400 v8::HandleScope scope;
8401 Local<Context> local_env;
8402 {
8403 LocalContext env;
8404 local_env = env.local();
8405 }
8406
8407 // Local context should still be live.
8408 CHECK(!local_env.IsEmpty());
8409 local_env->Enter();
8410
8411 // Should complete without problems.
8412 RegExpInterruptTest().RunTest();
8413
8414 local_env->Exit();
8415}
8416
8417
8418class ApplyInterruptTest {
8419 public:
8420 ApplyInterruptTest() : block_(NULL) {}
8421 ~ApplyInterruptTest() { delete block_; }
8422 void RunTest() {
8423 block_ = i::OS::CreateSemaphore(0);
8424 gc_count_ = 0;
8425 gc_during_apply_ = 0;
8426 apply_success_ = false;
8427 gc_success_ = false;
8428 GCThread gc_thread(this);
8429 gc_thread.Start();
8430 v8::Locker::StartPreemption(1);
8431
8432 LongRunningApply();
8433 {
8434 v8::Unlocker unlock;
8435 gc_thread.Join();
8436 }
8437 v8::Locker::StopPreemption();
8438 CHECK(apply_success_);
8439 CHECK(gc_success_);
8440 }
8441 private:
8442 // Number of garbage collections required.
8443 static const int kRequiredGCs = 2;
8444
8445 class GCThread : public i::Thread {
8446 public:
8447 explicit GCThread(ApplyInterruptTest* test)
8448 : test_(test) {}
8449 virtual void Run() {
8450 test_->CollectGarbage();
8451 }
8452 private:
8453 ApplyInterruptTest* test_;
8454 };
8455
8456 void CollectGarbage() {
8457 block_->Wait();
8458 while (gc_during_apply_ < kRequiredGCs) {
8459 {
8460 v8::Locker lock;
8461 i::Heap::CollectAllGarbage(false);
8462 gc_count_++;
8463 }
8464 i::OS::Sleep(1);
8465 }
8466 gc_success_ = true;
8467 }
8468
8469 void LongRunningApply() {
8470 block_->Signal();
8471 int rounds = 0;
8472 while (gc_during_apply_ < kRequiredGCs) {
8473 int gc_before = gc_count_;
8474 {
8475 const char* c_source =
8476 "function do_very_little(bar) {"
8477 " this.foo = bar;"
8478 "}"
8479 "for (var i = 0; i < 100000; i++) {"
8480 " do_very_little.apply(this, ['bar']);"
8481 "}";
8482 Local<String> source = String::New(c_source);
8483 Local<Script> script = Script::Compile(source);
8484 Local<Value> result = script->Run();
8485 // Check that no exception was thrown.
8486 CHECK(!result.IsEmpty());
8487 }
8488 int gc_after = gc_count_;
8489 gc_during_apply_ += gc_after - gc_before;
8490 rounds++;
8491 }
8492 apply_success_ = true;
8493 }
8494
8495 i::Semaphore* block_;
8496 int gc_count_;
8497 int gc_during_apply_;
8498 bool apply_success_;
8499 bool gc_success_;
8500};
8501
8502
8503// Test that nothing bad happens if we get a preemption just when we were
8504// about to do an apply().
8505TEST(ApplyInterruption) {
8506 v8::Locker lock;
8507 v8::V8::Initialize();
8508 v8::HandleScope scope;
8509 Local<Context> local_env;
8510 {
8511 LocalContext env;
8512 local_env = env.local();
8513 }
8514
8515 // Local context should still be live.
8516 CHECK(!local_env.IsEmpty());
8517 local_env->Enter();
8518
8519 // Should complete without problems.
8520 ApplyInterruptTest().RunTest();
8521
8522 local_env->Exit();
8523}
8524
8525
8526// Verify that we can clone an object
8527TEST(ObjectClone) {
8528 v8::HandleScope scope;
8529 LocalContext env;
8530
8531 const char* sample =
8532 "var rv = {};" \
8533 "rv.alpha = 'hello';" \
8534 "rv.beta = 123;" \
8535 "rv;";
8536
8537 // Create an object, verify basics.
8538 Local<Value> val = CompileRun(sample);
8539 CHECK(val->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01008540 Local<v8::Object> obj = val.As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00008541 obj->Set(v8_str("gamma"), v8_str("cloneme"));
8542
8543 CHECK_EQ(v8_str("hello"), obj->Get(v8_str("alpha")));
8544 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta")));
8545 CHECK_EQ(v8_str("cloneme"), obj->Get(v8_str("gamma")));
8546
8547 // Clone it.
8548 Local<v8::Object> clone = obj->Clone();
8549 CHECK_EQ(v8_str("hello"), clone->Get(v8_str("alpha")));
8550 CHECK_EQ(v8::Integer::New(123), clone->Get(v8_str("beta")));
8551 CHECK_EQ(v8_str("cloneme"), clone->Get(v8_str("gamma")));
8552
8553 // Set a property on the clone, verify each object.
8554 clone->Set(v8_str("beta"), v8::Integer::New(456));
8555 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta")));
8556 CHECK_EQ(v8::Integer::New(456), clone->Get(v8_str("beta")));
8557}
8558
8559
8560class AsciiVectorResource : public v8::String::ExternalAsciiStringResource {
8561 public:
8562 explicit AsciiVectorResource(i::Vector<const char> vector)
8563 : data_(vector) {}
8564 virtual ~AsciiVectorResource() {}
8565 virtual size_t length() const { return data_.length(); }
8566 virtual const char* data() const { return data_.start(); }
8567 private:
8568 i::Vector<const char> data_;
8569};
8570
8571
8572class UC16VectorResource : public v8::String::ExternalStringResource {
8573 public:
8574 explicit UC16VectorResource(i::Vector<const i::uc16> vector)
8575 : data_(vector) {}
8576 virtual ~UC16VectorResource() {}
8577 virtual size_t length() const { return data_.length(); }
8578 virtual const i::uc16* data() const { return data_.start(); }
8579 private:
8580 i::Vector<const i::uc16> data_;
8581};
8582
8583
8584static void MorphAString(i::String* string,
8585 AsciiVectorResource* ascii_resource,
8586 UC16VectorResource* uc16_resource) {
8587 CHECK(i::StringShape(string).IsExternal());
8588 if (string->IsAsciiRepresentation()) {
8589 // Check old map is not symbol or long.
Steve Blockd0582a62009-12-15 09:54:21 +00008590 CHECK(string->map() == i::Heap::external_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00008591 // Morph external string to be TwoByte string.
Steve Blockd0582a62009-12-15 09:54:21 +00008592 string->set_map(i::Heap::external_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00008593 i::ExternalTwoByteString* morphed =
8594 i::ExternalTwoByteString::cast(string);
8595 morphed->set_resource(uc16_resource);
8596 } else {
8597 // Check old map is not symbol or long.
Steve Blockd0582a62009-12-15 09:54:21 +00008598 CHECK(string->map() == i::Heap::external_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00008599 // Morph external string to be ASCII string.
Steve Blockd0582a62009-12-15 09:54:21 +00008600 string->set_map(i::Heap::external_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00008601 i::ExternalAsciiString* morphed =
8602 i::ExternalAsciiString::cast(string);
8603 morphed->set_resource(ascii_resource);
8604 }
8605}
8606
8607
8608// Test that we can still flatten a string if the components it is built up
8609// from have been turned into 16 bit strings in the mean time.
8610THREADED_TEST(MorphCompositeStringTest) {
8611 const char* c_string = "Now is the time for all good men"
8612 " to come to the aid of the party";
8613 uint16_t* two_byte_string = AsciiToTwoByteString(c_string);
8614 {
8615 v8::HandleScope scope;
8616 LocalContext env;
8617 AsciiVectorResource ascii_resource(
Steve Blockd0582a62009-12-15 09:54:21 +00008618 i::Vector<const char>(c_string, i::StrLength(c_string)));
Steve Blocka7e24c12009-10-30 11:49:00 +00008619 UC16VectorResource uc16_resource(
Steve Blockd0582a62009-12-15 09:54:21 +00008620 i::Vector<const uint16_t>(two_byte_string,
8621 i::StrLength(c_string)));
Steve Blocka7e24c12009-10-30 11:49:00 +00008622
8623 Local<String> lhs(v8::Utils::ToLocal(
8624 i::Factory::NewExternalStringFromAscii(&ascii_resource)));
8625 Local<String> rhs(v8::Utils::ToLocal(
8626 i::Factory::NewExternalStringFromAscii(&ascii_resource)));
8627
8628 env->Global()->Set(v8_str("lhs"), lhs);
8629 env->Global()->Set(v8_str("rhs"), rhs);
8630
8631 CompileRun(
8632 "var cons = lhs + rhs;"
8633 "var slice = lhs.substring(1, lhs.length - 1);"
8634 "var slice_on_cons = (lhs + rhs).substring(1, lhs.length *2 - 1);");
8635
8636 MorphAString(*v8::Utils::OpenHandle(*lhs), &ascii_resource, &uc16_resource);
8637 MorphAString(*v8::Utils::OpenHandle(*rhs), &ascii_resource, &uc16_resource);
8638
8639 // Now do some stuff to make sure the strings are flattened, etc.
8640 CompileRun(
8641 "/[^a-z]/.test(cons);"
8642 "/[^a-z]/.test(slice);"
8643 "/[^a-z]/.test(slice_on_cons);");
8644 const char* expected_cons =
8645 "Now is the time for all good men to come to the aid of the party"
8646 "Now is the time for all good men to come to the aid of the party";
8647 const char* expected_slice =
8648 "ow is the time for all good men to come to the aid of the part";
8649 const char* expected_slice_on_cons =
8650 "ow is the time for all good men to come to the aid of the party"
8651 "Now is the time for all good men to come to the aid of the part";
8652 CHECK_EQ(String::New(expected_cons),
8653 env->Global()->Get(v8_str("cons")));
8654 CHECK_EQ(String::New(expected_slice),
8655 env->Global()->Get(v8_str("slice")));
8656 CHECK_EQ(String::New(expected_slice_on_cons),
8657 env->Global()->Get(v8_str("slice_on_cons")));
8658 }
8659}
8660
8661
8662TEST(CompileExternalTwoByteSource) {
8663 v8::HandleScope scope;
8664 LocalContext context;
8665
8666 // This is a very short list of sources, which currently is to check for a
8667 // regression caused by r2703.
8668 const char* ascii_sources[] = {
8669 "0.5",
8670 "-0.5", // This mainly testes PushBack in the Scanner.
8671 "--0.5", // This mainly testes PushBack in the Scanner.
8672 NULL
8673 };
8674
8675 // Compile the sources as external two byte strings.
8676 for (int i = 0; ascii_sources[i] != NULL; i++) {
8677 uint16_t* two_byte_string = AsciiToTwoByteString(ascii_sources[i]);
8678 UC16VectorResource uc16_resource(
Steve Blockd0582a62009-12-15 09:54:21 +00008679 i::Vector<const uint16_t>(two_byte_string,
8680 i::StrLength(ascii_sources[i])));
Steve Blocka7e24c12009-10-30 11:49:00 +00008681 v8::Local<v8::String> source = v8::String::NewExternal(&uc16_resource);
8682 v8::Script::Compile(source);
8683 }
8684}
8685
8686
8687class RegExpStringModificationTest {
8688 public:
8689 RegExpStringModificationTest()
8690 : block_(i::OS::CreateSemaphore(0)),
8691 morphs_(0),
8692 morphs_during_regexp_(0),
8693 ascii_resource_(i::Vector<const char>("aaaaaaaaaaaaaab", 15)),
8694 uc16_resource_(i::Vector<const uint16_t>(two_byte_content_, 15)) {}
8695 ~RegExpStringModificationTest() { delete block_; }
8696 void RunTest() {
8697 regexp_success_ = false;
8698 morph_success_ = false;
8699
8700 // Initialize the contents of two_byte_content_ to be a uc16 representation
8701 // of "aaaaaaaaaaaaaab".
8702 for (int i = 0; i < 14; i++) {
8703 two_byte_content_[i] = 'a';
8704 }
8705 two_byte_content_[14] = 'b';
8706
8707 // Create the input string for the regexp - the one we are going to change
8708 // properties of.
8709 input_ = i::Factory::NewExternalStringFromAscii(&ascii_resource_);
8710
8711 // Inject the input as a global variable.
8712 i::Handle<i::String> input_name =
8713 i::Factory::NewStringFromAscii(i::Vector<const char>("input", 5));
8714 i::Top::global_context()->global()->SetProperty(*input_name, *input_, NONE);
8715
8716
8717 MorphThread morph_thread(this);
8718 morph_thread.Start();
8719 v8::Locker::StartPreemption(1);
8720 LongRunningRegExp();
8721 {
8722 v8::Unlocker unlock;
8723 morph_thread.Join();
8724 }
8725 v8::Locker::StopPreemption();
8726 CHECK(regexp_success_);
8727 CHECK(morph_success_);
8728 }
8729 private:
8730
8731 // Number of string modifications required.
8732 static const int kRequiredModifications = 5;
8733 static const int kMaxModifications = 100;
8734
8735 class MorphThread : public i::Thread {
8736 public:
8737 explicit MorphThread(RegExpStringModificationTest* test)
8738 : test_(test) {}
8739 virtual void Run() {
8740 test_->MorphString();
8741 }
8742 private:
8743 RegExpStringModificationTest* test_;
8744 };
8745
8746 void MorphString() {
8747 block_->Wait();
8748 while (morphs_during_regexp_ < kRequiredModifications &&
8749 morphs_ < kMaxModifications) {
8750 {
8751 v8::Locker lock;
8752 // Swap string between ascii and two-byte representation.
8753 i::String* string = *input_;
8754 MorphAString(string, &ascii_resource_, &uc16_resource_);
8755 morphs_++;
8756 }
8757 i::OS::Sleep(1);
8758 }
8759 morph_success_ = true;
8760 }
8761
8762 void LongRunningRegExp() {
8763 block_->Signal(); // Enable morphing thread on next preemption.
8764 while (morphs_during_regexp_ < kRequiredModifications &&
8765 morphs_ < kMaxModifications) {
8766 int morphs_before = morphs_;
8767 {
8768 // Match 15-30 "a"'s against 14 and a "b".
8769 const char* c_source =
8770 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
8771 ".exec(input) === null";
8772 Local<String> source = String::New(c_source);
8773 Local<Script> script = Script::Compile(source);
8774 Local<Value> result = script->Run();
8775 CHECK(result->IsTrue());
8776 }
8777 int morphs_after = morphs_;
8778 morphs_during_regexp_ += morphs_after - morphs_before;
8779 }
8780 regexp_success_ = true;
8781 }
8782
8783 i::uc16 two_byte_content_[15];
8784 i::Semaphore* block_;
8785 int morphs_;
8786 int morphs_during_regexp_;
8787 bool regexp_success_;
8788 bool morph_success_;
8789 i::Handle<i::String> input_;
8790 AsciiVectorResource ascii_resource_;
8791 UC16VectorResource uc16_resource_;
8792};
8793
8794
8795// Test that a regular expression execution can be interrupted and
8796// the string changed without failing.
8797TEST(RegExpStringModification) {
8798 v8::Locker lock;
8799 v8::V8::Initialize();
8800 v8::HandleScope scope;
8801 Local<Context> local_env;
8802 {
8803 LocalContext env;
8804 local_env = env.local();
8805 }
8806
8807 // Local context should still be live.
8808 CHECK(!local_env.IsEmpty());
8809 local_env->Enter();
8810
8811 // Should complete without problems.
8812 RegExpStringModificationTest().RunTest();
8813
8814 local_env->Exit();
8815}
8816
8817
8818// Test that we can set a property on the global object even if there
8819// is a read-only property in the prototype chain.
8820TEST(ReadOnlyPropertyInGlobalProto) {
8821 v8::HandleScope scope;
8822 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
8823 LocalContext context(0, templ);
8824 v8::Handle<v8::Object> global = context->Global();
8825 v8::Handle<v8::Object> global_proto =
8826 v8::Handle<v8::Object>::Cast(global->Get(v8_str("__proto__")));
8827 global_proto->Set(v8_str("x"), v8::Integer::New(0), v8::ReadOnly);
8828 global_proto->Set(v8_str("y"), v8::Integer::New(0), v8::ReadOnly);
8829 // Check without 'eval' or 'with'.
8830 v8::Handle<v8::Value> res =
8831 CompileRun("function f() { x = 42; return x; }; f()");
8832 // Check with 'eval'.
8833 res = CompileRun("function f() { eval('1'); y = 42; return y; }; f()");
8834 CHECK_EQ(v8::Integer::New(42), res);
8835 // Check with 'with'.
8836 res = CompileRun("function f() { with (this) { y = 42 }; return y; }; f()");
8837 CHECK_EQ(v8::Integer::New(42), res);
8838}
8839
8840static int force_set_set_count = 0;
8841static int force_set_get_count = 0;
8842bool pass_on_get = false;
8843
8844static v8::Handle<v8::Value> ForceSetGetter(v8::Local<v8::String> name,
8845 const v8::AccessorInfo& info) {
8846 force_set_get_count++;
8847 if (pass_on_get) {
8848 return v8::Handle<v8::Value>();
8849 } else {
8850 return v8::Int32::New(3);
8851 }
8852}
8853
8854static void ForceSetSetter(v8::Local<v8::String> name,
8855 v8::Local<v8::Value> value,
8856 const v8::AccessorInfo& info) {
8857 force_set_set_count++;
8858}
8859
8860static v8::Handle<v8::Value> ForceSetInterceptSetter(
8861 v8::Local<v8::String> name,
8862 v8::Local<v8::Value> value,
8863 const v8::AccessorInfo& info) {
8864 force_set_set_count++;
8865 return v8::Undefined();
8866}
8867
8868TEST(ForceSet) {
8869 force_set_get_count = 0;
8870 force_set_set_count = 0;
8871 pass_on_get = false;
8872
8873 v8::HandleScope scope;
8874 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
8875 v8::Handle<v8::String> access_property = v8::String::New("a");
8876 templ->SetAccessor(access_property, ForceSetGetter, ForceSetSetter);
8877 LocalContext context(NULL, templ);
8878 v8::Handle<v8::Object> global = context->Global();
8879
8880 // Ordinary properties
8881 v8::Handle<v8::String> simple_property = v8::String::New("p");
8882 global->Set(simple_property, v8::Int32::New(4), v8::ReadOnly);
8883 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
8884 // This should fail because the property is read-only
8885 global->Set(simple_property, v8::Int32::New(5));
8886 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
8887 // This should succeed even though the property is read-only
8888 global->ForceSet(simple_property, v8::Int32::New(6));
8889 CHECK_EQ(6, global->Get(simple_property)->Int32Value());
8890
8891 // Accessors
8892 CHECK_EQ(0, force_set_set_count);
8893 CHECK_EQ(0, force_set_get_count);
8894 CHECK_EQ(3, global->Get(access_property)->Int32Value());
8895 // CHECK_EQ the property shouldn't override it, just call the setter
8896 // which in this case does nothing.
8897 global->Set(access_property, v8::Int32::New(7));
8898 CHECK_EQ(3, global->Get(access_property)->Int32Value());
8899 CHECK_EQ(1, force_set_set_count);
8900 CHECK_EQ(2, force_set_get_count);
8901 // Forcing the property to be set should override the accessor without
8902 // calling it
8903 global->ForceSet(access_property, v8::Int32::New(8));
8904 CHECK_EQ(8, global->Get(access_property)->Int32Value());
8905 CHECK_EQ(1, force_set_set_count);
8906 CHECK_EQ(2, force_set_get_count);
8907}
8908
8909TEST(ForceSetWithInterceptor) {
8910 force_set_get_count = 0;
8911 force_set_set_count = 0;
8912 pass_on_get = false;
8913
8914 v8::HandleScope scope;
8915 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
8916 templ->SetNamedPropertyHandler(ForceSetGetter, ForceSetInterceptSetter);
8917 LocalContext context(NULL, templ);
8918 v8::Handle<v8::Object> global = context->Global();
8919
8920 v8::Handle<v8::String> some_property = v8::String::New("a");
8921 CHECK_EQ(0, force_set_set_count);
8922 CHECK_EQ(0, force_set_get_count);
8923 CHECK_EQ(3, global->Get(some_property)->Int32Value());
8924 // Setting the property shouldn't override it, just call the setter
8925 // which in this case does nothing.
8926 global->Set(some_property, v8::Int32::New(7));
8927 CHECK_EQ(3, global->Get(some_property)->Int32Value());
8928 CHECK_EQ(1, force_set_set_count);
8929 CHECK_EQ(2, force_set_get_count);
8930 // Getting the property when the interceptor returns an empty handle
8931 // should yield undefined, since the property isn't present on the
8932 // object itself yet.
8933 pass_on_get = true;
8934 CHECK(global->Get(some_property)->IsUndefined());
8935 CHECK_EQ(1, force_set_set_count);
8936 CHECK_EQ(3, force_set_get_count);
8937 // Forcing the property to be set should cause the value to be
8938 // set locally without calling the interceptor.
8939 global->ForceSet(some_property, v8::Int32::New(8));
8940 CHECK_EQ(8, global->Get(some_property)->Int32Value());
8941 CHECK_EQ(1, force_set_set_count);
8942 CHECK_EQ(4, force_set_get_count);
8943 // Reenabling the interceptor should cause it to take precedence over
8944 // the property
8945 pass_on_get = false;
8946 CHECK_EQ(3, global->Get(some_property)->Int32Value());
8947 CHECK_EQ(1, force_set_set_count);
8948 CHECK_EQ(5, force_set_get_count);
8949 // The interceptor should also work for other properties
8950 CHECK_EQ(3, global->Get(v8::String::New("b"))->Int32Value());
8951 CHECK_EQ(1, force_set_set_count);
8952 CHECK_EQ(6, force_set_get_count);
8953}
8954
8955
8956THREADED_TEST(ForceDelete) {
8957 v8::HandleScope scope;
8958 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
8959 LocalContext context(NULL, templ);
8960 v8::Handle<v8::Object> global = context->Global();
8961
8962 // Ordinary properties
8963 v8::Handle<v8::String> simple_property = v8::String::New("p");
8964 global->Set(simple_property, v8::Int32::New(4), v8::DontDelete);
8965 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
8966 // This should fail because the property is dont-delete.
8967 CHECK(!global->Delete(simple_property));
8968 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
8969 // This should succeed even though the property is dont-delete.
8970 CHECK(global->ForceDelete(simple_property));
8971 CHECK(global->Get(simple_property)->IsUndefined());
8972}
8973
8974
8975static int force_delete_interceptor_count = 0;
8976static bool pass_on_delete = false;
8977
8978
8979static v8::Handle<v8::Boolean> ForceDeleteDeleter(
8980 v8::Local<v8::String> name,
8981 const v8::AccessorInfo& info) {
8982 force_delete_interceptor_count++;
8983 if (pass_on_delete) {
8984 return v8::Handle<v8::Boolean>();
8985 } else {
8986 return v8::True();
8987 }
8988}
8989
8990
8991THREADED_TEST(ForceDeleteWithInterceptor) {
8992 force_delete_interceptor_count = 0;
8993 pass_on_delete = false;
8994
8995 v8::HandleScope scope;
8996 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
8997 templ->SetNamedPropertyHandler(0, 0, 0, ForceDeleteDeleter);
8998 LocalContext context(NULL, templ);
8999 v8::Handle<v8::Object> global = context->Global();
9000
9001 v8::Handle<v8::String> some_property = v8::String::New("a");
9002 global->Set(some_property, v8::Integer::New(42), v8::DontDelete);
9003
9004 // Deleting a property should get intercepted and nothing should
9005 // happen.
9006 CHECK_EQ(0, force_delete_interceptor_count);
9007 CHECK(global->Delete(some_property));
9008 CHECK_EQ(1, force_delete_interceptor_count);
9009 CHECK_EQ(42, global->Get(some_property)->Int32Value());
9010 // Deleting the property when the interceptor returns an empty
9011 // handle should not delete the property since it is DontDelete.
9012 pass_on_delete = true;
9013 CHECK(!global->Delete(some_property));
9014 CHECK_EQ(2, force_delete_interceptor_count);
9015 CHECK_EQ(42, global->Get(some_property)->Int32Value());
9016 // Forcing the property to be deleted should delete the value
9017 // without calling the interceptor.
9018 CHECK(global->ForceDelete(some_property));
9019 CHECK(global->Get(some_property)->IsUndefined());
9020 CHECK_EQ(2, force_delete_interceptor_count);
9021}
9022
9023
9024// Make sure that forcing a delete invalidates any IC stubs, so we
9025// don't read the hole value.
9026THREADED_TEST(ForceDeleteIC) {
9027 v8::HandleScope scope;
9028 LocalContext context;
9029 // Create a DontDelete variable on the global object.
9030 CompileRun("this.__proto__ = { foo: 'horse' };"
9031 "var foo = 'fish';"
9032 "function f() { return foo.length; }");
9033 // Initialize the IC for foo in f.
9034 CompileRun("for (var i = 0; i < 4; i++) f();");
9035 // Make sure the value of foo is correct before the deletion.
9036 CHECK_EQ(4, CompileRun("f()")->Int32Value());
9037 // Force the deletion of foo.
9038 CHECK(context->Global()->ForceDelete(v8_str("foo")));
9039 // Make sure the value for foo is read from the prototype, and that
9040 // we don't get in trouble with reading the deleted cell value
9041 // sentinel.
9042 CHECK_EQ(5, CompileRun("f()")->Int32Value());
9043}
9044
9045
9046v8::Persistent<Context> calling_context0;
9047v8::Persistent<Context> calling_context1;
9048v8::Persistent<Context> calling_context2;
9049
9050
9051// Check that the call to the callback is initiated in
9052// calling_context2, the directly calling context is calling_context1
9053// and the callback itself is in calling_context0.
9054static v8::Handle<Value> GetCallingContextCallback(const v8::Arguments& args) {
9055 ApiTestFuzzer::Fuzz();
9056 CHECK(Context::GetCurrent() == calling_context0);
9057 CHECK(Context::GetCalling() == calling_context1);
9058 CHECK(Context::GetEntered() == calling_context2);
9059 return v8::Integer::New(42);
9060}
9061
9062
9063THREADED_TEST(GetCallingContext) {
9064 v8::HandleScope scope;
9065
9066 calling_context0 = Context::New();
9067 calling_context1 = Context::New();
9068 calling_context2 = Context::New();
9069
9070 // Allow cross-domain access.
9071 Local<String> token = v8_str("<security token>");
9072 calling_context0->SetSecurityToken(token);
9073 calling_context1->SetSecurityToken(token);
9074 calling_context2->SetSecurityToken(token);
9075
9076 // Create an object with a C++ callback in context0.
9077 calling_context0->Enter();
9078 Local<v8::FunctionTemplate> callback_templ =
9079 v8::FunctionTemplate::New(GetCallingContextCallback);
9080 calling_context0->Global()->Set(v8_str("callback"),
9081 callback_templ->GetFunction());
9082 calling_context0->Exit();
9083
9084 // Expose context0 in context1 and setup a function that calls the
9085 // callback function.
9086 calling_context1->Enter();
9087 calling_context1->Global()->Set(v8_str("context0"),
9088 calling_context0->Global());
9089 CompileRun("function f() { context0.callback() }");
9090 calling_context1->Exit();
9091
9092 // Expose context1 in context2 and call the callback function in
9093 // context0 indirectly through f in context1.
9094 calling_context2->Enter();
9095 calling_context2->Global()->Set(v8_str("context1"),
9096 calling_context1->Global());
9097 CompileRun("context1.f()");
9098 calling_context2->Exit();
9099
9100 // Dispose the contexts to allow them to be garbage collected.
9101 calling_context0.Dispose();
9102 calling_context1.Dispose();
9103 calling_context2.Dispose();
9104 calling_context0.Clear();
9105 calling_context1.Clear();
9106 calling_context2.Clear();
9107}
9108
9109
9110// Check that a variable declaration with no explicit initialization
9111// value does not shadow an existing property in the prototype chain.
9112//
9113// This is consistent with Firefox and Safari.
9114//
9115// See http://crbug.com/12548.
9116THREADED_TEST(InitGlobalVarInProtoChain) {
9117 v8::HandleScope scope;
9118 LocalContext context;
9119 // Introduce a variable in the prototype chain.
9120 CompileRun("__proto__.x = 42");
9121 v8::Handle<v8::Value> result = CompileRun("var x; x");
9122 CHECK(!result->IsUndefined());
9123 CHECK_EQ(42, result->Int32Value());
9124}
9125
9126
9127// Regression test for issue 398.
9128// If a function is added to an object, creating a constant function
9129// field, and the result is cloned, replacing the constant function on the
9130// original should not affect the clone.
9131// See http://code.google.com/p/v8/issues/detail?id=398
9132THREADED_TEST(ReplaceConstantFunction) {
9133 v8::HandleScope scope;
9134 LocalContext context;
9135 v8::Handle<v8::Object> obj = v8::Object::New();
9136 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New();
9137 v8::Handle<v8::String> foo_string = v8::String::New("foo");
9138 obj->Set(foo_string, func_templ->GetFunction());
9139 v8::Handle<v8::Object> obj_clone = obj->Clone();
9140 obj_clone->Set(foo_string, v8::String::New("Hello"));
9141 CHECK(!obj->Get(foo_string)->IsUndefined());
9142}
9143
9144
9145// Regression test for http://crbug.com/16276.
9146THREADED_TEST(Regress16276) {
9147 v8::HandleScope scope;
9148 LocalContext context;
9149 // Force the IC in f to be a dictionary load IC.
9150 CompileRun("function f(obj) { return obj.x; }\n"
9151 "var obj = { x: { foo: 42 }, y: 87 };\n"
9152 "var x = obj.x;\n"
9153 "delete obj.y;\n"
9154 "for (var i = 0; i < 5; i++) f(obj);");
9155 // Detach the global object to make 'this' refer directly to the
9156 // global object (not the proxy), and make sure that the dictionary
9157 // load IC doesn't mess up loading directly from the global object.
9158 context->DetachGlobal();
9159 CHECK_EQ(42, CompileRun("f(this).foo")->Int32Value());
9160}
9161
9162
9163THREADED_TEST(PixelArray) {
9164 v8::HandleScope scope;
9165 LocalContext context;
Steve Blockd0582a62009-12-15 09:54:21 +00009166 const int kElementCount = 260;
Steve Blocka7e24c12009-10-30 11:49:00 +00009167 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(kElementCount));
9168 i::Handle<i::PixelArray> pixels = i::Factory::NewPixelArray(kElementCount,
9169 pixel_data);
9170 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9171 for (int i = 0; i < kElementCount; i++) {
Steve Blockd0582a62009-12-15 09:54:21 +00009172 pixels->set(i, i % 256);
Steve Blocka7e24c12009-10-30 11:49:00 +00009173 }
9174 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9175 for (int i = 0; i < kElementCount; i++) {
Steve Blockd0582a62009-12-15 09:54:21 +00009176 CHECK_EQ(i % 256, pixels->get(i));
9177 CHECK_EQ(i % 256, pixel_data[i]);
Steve Blocka7e24c12009-10-30 11:49:00 +00009178 }
9179
9180 v8::Handle<v8::Object> obj = v8::Object::New();
9181 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj);
9182 // Set the elements to be the pixels.
9183 // jsobj->set_elements(*pixels);
9184 obj->SetIndexedPropertiesToPixelData(pixel_data, kElementCount);
9185 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1))->value());
9186 obj->Set(v8_str("field"), v8::Int32::New(1503));
9187 context->Global()->Set(v8_str("pixels"), obj);
9188 v8::Handle<v8::Value> result = CompileRun("pixels.field");
9189 CHECK_EQ(1503, result->Int32Value());
9190 result = CompileRun("pixels[1]");
9191 CHECK_EQ(1, result->Int32Value());
9192
9193 result = CompileRun("var sum = 0;"
9194 "for (var i = 0; i < 8; i++) {"
9195 " sum += pixels[i] = pixels[i] = -i;"
9196 "}"
9197 "sum;");
9198 CHECK_EQ(-28, result->Int32Value());
9199
9200 result = CompileRun("var sum = 0;"
9201 "for (var i = 0; i < 8; i++) {"
9202 " sum += pixels[i] = pixels[i] = 0;"
9203 "}"
9204 "sum;");
9205 CHECK_EQ(0, result->Int32Value());
9206
9207 result = CompileRun("var sum = 0;"
9208 "for (var i = 0; i < 8; i++) {"
9209 " sum += pixels[i] = pixels[i] = 255;"
9210 "}"
9211 "sum;");
9212 CHECK_EQ(8 * 255, result->Int32Value());
9213
9214 result = CompileRun("var sum = 0;"
9215 "for (var i = 0; i < 8; i++) {"
9216 " sum += pixels[i] = pixels[i] = 256 + i;"
9217 "}"
9218 "sum;");
9219 CHECK_EQ(2076, result->Int32Value());
9220
9221 result = CompileRun("var sum = 0;"
9222 "for (var i = 0; i < 8; i++) {"
9223 " sum += pixels[i] = pixels[i] = i;"
9224 "}"
9225 "sum;");
9226 CHECK_EQ(28, result->Int32Value());
9227
9228 result = CompileRun("var sum = 0;"
9229 "for (var i = 0; i < 8; i++) {"
9230 " sum += pixels[i];"
9231 "}"
9232 "sum;");
9233 CHECK_EQ(28, result->Int32Value());
9234
9235 i::Handle<i::Smi> value(i::Smi::FromInt(2));
9236 i::SetElement(jsobj, 1, value);
9237 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(1))->value());
9238 *value.location() = i::Smi::FromInt(256);
9239 i::SetElement(jsobj, 1, value);
9240 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(1))->value());
9241 *value.location() = i::Smi::FromInt(-1);
9242 i::SetElement(jsobj, 1, value);
9243 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1))->value());
9244
9245 result = CompileRun("for (var i = 0; i < 8; i++) {"
9246 " pixels[i] = (i * 65) - 109;"
9247 "}"
9248 "pixels[1] + pixels[6];");
9249 CHECK_EQ(255, result->Int32Value());
9250 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0))->value());
9251 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1))->value());
9252 CHECK_EQ(21, i::Smi::cast(jsobj->GetElement(2))->value());
9253 CHECK_EQ(86, i::Smi::cast(jsobj->GetElement(3))->value());
9254 CHECK_EQ(151, i::Smi::cast(jsobj->GetElement(4))->value());
9255 CHECK_EQ(216, i::Smi::cast(jsobj->GetElement(5))->value());
9256 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(6))->value());
9257 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(7))->value());
9258 result = CompileRun("var sum = 0;"
9259 "for (var i = 0; i < 8; i++) {"
9260 " sum += pixels[i];"
9261 "}"
9262 "sum;");
9263 CHECK_EQ(984, result->Int32Value());
9264
9265 result = CompileRun("for (var i = 0; i < 8; i++) {"
9266 " pixels[i] = (i * 1.1);"
9267 "}"
9268 "pixels[1] + pixels[6];");
9269 CHECK_EQ(8, result->Int32Value());
9270 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0))->value());
9271 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1))->value());
9272 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(2))->value());
9273 CHECK_EQ(3, i::Smi::cast(jsobj->GetElement(3))->value());
9274 CHECK_EQ(4, i::Smi::cast(jsobj->GetElement(4))->value());
9275 CHECK_EQ(6, i::Smi::cast(jsobj->GetElement(5))->value());
9276 CHECK_EQ(7, i::Smi::cast(jsobj->GetElement(6))->value());
9277 CHECK_EQ(8, i::Smi::cast(jsobj->GetElement(7))->value());
9278
9279 result = CompileRun("for (var i = 0; i < 8; i++) {"
9280 " pixels[7] = undefined;"
9281 "}"
9282 "pixels[7];");
9283 CHECK_EQ(0, result->Int32Value());
9284 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(7))->value());
9285
9286 result = CompileRun("for (var i = 0; i < 8; i++) {"
9287 " pixels[6] = '2.3';"
9288 "}"
9289 "pixels[6];");
9290 CHECK_EQ(2, result->Int32Value());
9291 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(6))->value());
9292
9293 result = CompileRun("for (var i = 0; i < 8; i++) {"
9294 " pixels[5] = NaN;"
9295 "}"
9296 "pixels[5];");
9297 CHECK_EQ(0, result->Int32Value());
9298 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
9299
9300 result = CompileRun("for (var i = 0; i < 8; i++) {"
9301 " pixels[8] = Infinity;"
9302 "}"
9303 "pixels[8];");
9304 CHECK_EQ(255, result->Int32Value());
9305 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(8))->value());
9306
9307 result = CompileRun("for (var i = 0; i < 8; i++) {"
9308 " pixels[9] = -Infinity;"
9309 "}"
9310 "pixels[9];");
9311 CHECK_EQ(0, result->Int32Value());
9312 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(9))->value());
9313
9314 result = CompileRun("pixels[3] = 33;"
9315 "delete pixels[3];"
9316 "pixels[3];");
9317 CHECK_EQ(33, result->Int32Value());
9318
9319 result = CompileRun("pixels[0] = 10; pixels[1] = 11;"
9320 "pixels[2] = 12; pixels[3] = 13;"
9321 "pixels.__defineGetter__('2',"
9322 "function() { return 120; });"
9323 "pixels[2];");
9324 CHECK_EQ(12, result->Int32Value());
9325
9326 result = CompileRun("var js_array = new Array(40);"
9327 "js_array[0] = 77;"
9328 "js_array;");
9329 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
9330
9331 result = CompileRun("pixels[1] = 23;"
9332 "pixels.__proto__ = [];"
9333 "js_array.__proto__ = pixels;"
9334 "js_array.concat(pixels);");
9335 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
9336 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value());
9337
9338 result = CompileRun("pixels[1] = 23;");
9339 CHECK_EQ(23, result->Int32Value());
9340
Steve Blockd0582a62009-12-15 09:54:21 +00009341 // Test for index greater than 255. Regression test for:
9342 // http://code.google.com/p/chromium/issues/detail?id=26337.
9343 result = CompileRun("pixels[256] = 255;");
9344 CHECK_EQ(255, result->Int32Value());
9345 result = CompileRun("var i = 0;"
9346 "for (var j = 0; j < 8; j++) { i = pixels[256]; }"
9347 "i");
9348 CHECK_EQ(255, result->Int32Value());
9349
Steve Blocka7e24c12009-10-30 11:49:00 +00009350 free(pixel_data);
9351}
9352
9353
Steve Block3ce2e202009-11-05 08:53:23 +00009354template <class ExternalArrayClass, class ElementType>
9355static void ExternalArrayTestHelper(v8::ExternalArrayType array_type,
9356 int64_t low,
9357 int64_t high) {
9358 v8::HandleScope scope;
9359 LocalContext context;
9360 const int kElementCount = 40;
9361 int element_size = 0;
9362 switch (array_type) {
9363 case v8::kExternalByteArray:
9364 case v8::kExternalUnsignedByteArray:
9365 element_size = 1;
9366 break;
9367 case v8::kExternalShortArray:
9368 case v8::kExternalUnsignedShortArray:
9369 element_size = 2;
9370 break;
9371 case v8::kExternalIntArray:
9372 case v8::kExternalUnsignedIntArray:
9373 case v8::kExternalFloatArray:
9374 element_size = 4;
9375 break;
9376 default:
9377 UNREACHABLE();
9378 break;
9379 }
9380 ElementType* array_data =
9381 static_cast<ElementType*>(malloc(kElementCount * element_size));
9382 i::Handle<ExternalArrayClass> array =
9383 i::Handle<ExternalArrayClass>::cast(
9384 i::Factory::NewExternalArray(kElementCount, array_type, array_data));
9385 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9386 for (int i = 0; i < kElementCount; i++) {
9387 array->set(i, static_cast<ElementType>(i));
9388 }
9389 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9390 for (int i = 0; i < kElementCount; i++) {
9391 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array->get(i)));
9392 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array_data[i]));
9393 }
9394
9395 v8::Handle<v8::Object> obj = v8::Object::New();
9396 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj);
9397 // Set the elements to be the external array.
9398 obj->SetIndexedPropertiesToExternalArrayData(array_data,
9399 array_type,
9400 kElementCount);
9401 CHECK_EQ(1, static_cast<int>(jsobj->GetElement(1)->Number()));
9402 obj->Set(v8_str("field"), v8::Int32::New(1503));
9403 context->Global()->Set(v8_str("ext_array"), obj);
9404 v8::Handle<v8::Value> result = CompileRun("ext_array.field");
9405 CHECK_EQ(1503, result->Int32Value());
9406 result = CompileRun("ext_array[1]");
9407 CHECK_EQ(1, result->Int32Value());
9408
9409 // Check pass through of assigned smis
9410 result = CompileRun("var sum = 0;"
9411 "for (var i = 0; i < 8; i++) {"
9412 " sum += ext_array[i] = ext_array[i] = -i;"
9413 "}"
9414 "sum;");
9415 CHECK_EQ(-28, result->Int32Value());
9416
9417 // Check assigned smis
9418 result = CompileRun("for (var i = 0; i < 8; i++) {"
9419 " ext_array[i] = i;"
9420 "}"
9421 "var sum = 0;"
9422 "for (var i = 0; i < 8; i++) {"
9423 " sum += ext_array[i];"
9424 "}"
9425 "sum;");
9426 CHECK_EQ(28, result->Int32Value());
9427
9428 // Check assigned smis in reverse order
9429 result = CompileRun("for (var i = 8; --i >= 0; ) {"
9430 " ext_array[i] = i;"
9431 "}"
9432 "var sum = 0;"
9433 "for (var i = 0; i < 8; i++) {"
9434 " sum += ext_array[i];"
9435 "}"
9436 "sum;");
9437 CHECK_EQ(28, result->Int32Value());
9438
9439 // Check pass through of assigned HeapNumbers
9440 result = CompileRun("var sum = 0;"
9441 "for (var i = 0; i < 16; i+=2) {"
9442 " sum += ext_array[i] = ext_array[i] = (-i * 0.5);"
9443 "}"
9444 "sum;");
9445 CHECK_EQ(-28, result->Int32Value());
9446
9447 // Check assigned HeapNumbers
9448 result = CompileRun("for (var i = 0; i < 16; i+=2) {"
9449 " ext_array[i] = (i * 0.5);"
9450 "}"
9451 "var sum = 0;"
9452 "for (var i = 0; i < 16; i+=2) {"
9453 " sum += ext_array[i];"
9454 "}"
9455 "sum;");
9456 CHECK_EQ(28, result->Int32Value());
9457
9458 // Check assigned HeapNumbers in reverse order
9459 result = CompileRun("for (var i = 14; i >= 0; i-=2) {"
9460 " ext_array[i] = (i * 0.5);"
9461 "}"
9462 "var sum = 0;"
9463 "for (var i = 0; i < 16; i+=2) {"
9464 " sum += ext_array[i];"
9465 "}"
9466 "sum;");
9467 CHECK_EQ(28, result->Int32Value());
9468
9469 i::ScopedVector<char> test_buf(1024);
9470
9471 // Check legal boundary conditions.
9472 // The repeated loads and stores ensure the ICs are exercised.
9473 const char* boundary_program =
9474 "var res = 0;"
9475 "for (var i = 0; i < 16; i++) {"
9476 " ext_array[i] = %lld;"
9477 " if (i > 8) {"
9478 " res = ext_array[i];"
9479 " }"
9480 "}"
9481 "res;";
9482 i::OS::SNPrintF(test_buf,
9483 boundary_program,
9484 low);
9485 result = CompileRun(test_buf.start());
9486 CHECK_EQ(low, result->IntegerValue());
9487
9488 i::OS::SNPrintF(test_buf,
9489 boundary_program,
9490 high);
9491 result = CompileRun(test_buf.start());
9492 CHECK_EQ(high, result->IntegerValue());
9493
9494 // Check misprediction of type in IC.
9495 result = CompileRun("var tmp_array = ext_array;"
9496 "var sum = 0;"
9497 "for (var i = 0; i < 8; i++) {"
9498 " tmp_array[i] = i;"
9499 " sum += tmp_array[i];"
9500 " if (i == 4) {"
9501 " tmp_array = {};"
9502 " }"
9503 "}"
9504 "sum;");
9505 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9506 CHECK_EQ(28, result->Int32Value());
9507
9508 // Make sure out-of-range loads do not throw.
9509 i::OS::SNPrintF(test_buf,
9510 "var caught_exception = false;"
9511 "try {"
9512 " ext_array[%d];"
9513 "} catch (e) {"
9514 " caught_exception = true;"
9515 "}"
9516 "caught_exception;",
9517 kElementCount);
9518 result = CompileRun(test_buf.start());
9519 CHECK_EQ(false, result->BooleanValue());
9520
9521 // Make sure out-of-range stores do not throw.
9522 i::OS::SNPrintF(test_buf,
9523 "var caught_exception = false;"
9524 "try {"
9525 " ext_array[%d] = 1;"
9526 "} catch (e) {"
9527 " caught_exception = true;"
9528 "}"
9529 "caught_exception;",
9530 kElementCount);
9531 result = CompileRun(test_buf.start());
9532 CHECK_EQ(false, result->BooleanValue());
9533
9534 // Check other boundary conditions, values and operations.
9535 result = CompileRun("for (var i = 0; i < 8; i++) {"
9536 " ext_array[7] = undefined;"
9537 "}"
9538 "ext_array[7];");
9539 CHECK_EQ(0, result->Int32Value());
9540 CHECK_EQ(0, static_cast<int>(jsobj->GetElement(7)->Number()));
9541
9542 result = CompileRun("for (var i = 0; i < 8; i++) {"
9543 " ext_array[6] = '2.3';"
9544 "}"
9545 "ext_array[6];");
9546 CHECK_EQ(2, result->Int32Value());
9547 CHECK_EQ(2, static_cast<int>(jsobj->GetElement(6)->Number()));
9548
9549 if (array_type != v8::kExternalFloatArray) {
9550 // Though the specification doesn't state it, be explicit about
9551 // converting NaNs and +/-Infinity to zero.
9552 result = CompileRun("for (var i = 0; i < 8; i++) {"
9553 " ext_array[i] = 5;"
9554 "}"
9555 "for (var i = 0; i < 8; i++) {"
9556 " ext_array[i] = NaN;"
9557 "}"
9558 "ext_array[5];");
9559 CHECK_EQ(0, result->Int32Value());
9560 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
9561
9562 result = CompileRun("for (var i = 0; i < 8; i++) {"
9563 " ext_array[i] = 5;"
9564 "}"
9565 "for (var i = 0; i < 8; i++) {"
9566 " ext_array[i] = Infinity;"
9567 "}"
9568 "ext_array[5];");
9569 CHECK_EQ(0, result->Int32Value());
9570 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
9571
9572 result = CompileRun("for (var i = 0; i < 8; i++) {"
9573 " ext_array[i] = 5;"
9574 "}"
9575 "for (var i = 0; i < 8; i++) {"
9576 " ext_array[i] = -Infinity;"
9577 "}"
9578 "ext_array[5];");
9579 CHECK_EQ(0, result->Int32Value());
9580 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
9581 }
9582
9583 result = CompileRun("ext_array[3] = 33;"
9584 "delete ext_array[3];"
9585 "ext_array[3];");
9586 CHECK_EQ(33, result->Int32Value());
9587
9588 result = CompileRun("ext_array[0] = 10; ext_array[1] = 11;"
9589 "ext_array[2] = 12; ext_array[3] = 13;"
9590 "ext_array.__defineGetter__('2',"
9591 "function() { return 120; });"
9592 "ext_array[2];");
9593 CHECK_EQ(12, result->Int32Value());
9594
9595 result = CompileRun("var js_array = new Array(40);"
9596 "js_array[0] = 77;"
9597 "js_array;");
9598 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
9599
9600 result = CompileRun("ext_array[1] = 23;"
9601 "ext_array.__proto__ = [];"
9602 "js_array.__proto__ = ext_array;"
9603 "js_array.concat(ext_array);");
9604 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
9605 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value());
9606
9607 result = CompileRun("ext_array[1] = 23;");
9608 CHECK_EQ(23, result->Int32Value());
9609
Steve Blockd0582a62009-12-15 09:54:21 +00009610 // Test more complex manipulations which cause eax to contain values
9611 // that won't be completely overwritten by loads from the arrays.
9612 // This catches bugs in the instructions used for the KeyedLoadIC
9613 // for byte and word types.
9614 {
9615 const int kXSize = 300;
9616 const int kYSize = 300;
9617 const int kLargeElementCount = kXSize * kYSize * 4;
9618 ElementType* large_array_data =
9619 static_cast<ElementType*>(malloc(kLargeElementCount * element_size));
9620 i::Handle<ExternalArrayClass> large_array =
9621 i::Handle<ExternalArrayClass>::cast(
9622 i::Factory::NewExternalArray(kLargeElementCount,
9623 array_type,
9624 array_data));
9625 v8::Handle<v8::Object> large_obj = v8::Object::New();
9626 // Set the elements to be the external array.
9627 large_obj->SetIndexedPropertiesToExternalArrayData(large_array_data,
9628 array_type,
9629 kLargeElementCount);
9630 context->Global()->Set(v8_str("large_array"), large_obj);
9631 // Initialize contents of a few rows.
9632 for (int x = 0; x < 300; x++) {
9633 int row = 0;
9634 int offset = row * 300 * 4;
9635 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
9636 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
9637 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
9638 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
9639 row = 150;
9640 offset = row * 300 * 4;
9641 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
9642 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
9643 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
9644 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
9645 row = 298;
9646 offset = row * 300 * 4;
9647 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
9648 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
9649 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
9650 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
9651 }
9652 // The goal of the code below is to make "offset" large enough
9653 // that the computation of the index (which goes into eax) has
9654 // high bits set which will not be overwritten by a byte or short
9655 // load.
9656 result = CompileRun("var failed = false;"
9657 "var offset = 0;"
9658 "for (var i = 0; i < 300; i++) {"
9659 " if (large_array[4 * i] != 127 ||"
9660 " large_array[4 * i + 1] != 0 ||"
9661 " large_array[4 * i + 2] != 0 ||"
9662 " large_array[4 * i + 3] != 127) {"
9663 " failed = true;"
9664 " }"
9665 "}"
9666 "offset = 150 * 300 * 4;"
9667 "for (var i = 0; i < 300; i++) {"
9668 " if (large_array[offset + 4 * i] != 127 ||"
9669 " large_array[offset + 4 * i + 1] != 0 ||"
9670 " large_array[offset + 4 * i + 2] != 0 ||"
9671 " large_array[offset + 4 * i + 3] != 127) {"
9672 " failed = true;"
9673 " }"
9674 "}"
9675 "offset = 298 * 300 * 4;"
9676 "for (var i = 0; i < 300; i++) {"
9677 " if (large_array[offset + 4 * i] != 127 ||"
9678 " large_array[offset + 4 * i + 1] != 0 ||"
9679 " large_array[offset + 4 * i + 2] != 0 ||"
9680 " large_array[offset + 4 * i + 3] != 127) {"
9681 " failed = true;"
9682 " }"
9683 "}"
9684 "!failed;");
9685 CHECK_EQ(true, result->BooleanValue());
9686 free(large_array_data);
9687 }
9688
Steve Block3ce2e202009-11-05 08:53:23 +00009689 free(array_data);
9690}
9691
9692
9693THREADED_TEST(ExternalByteArray) {
9694 ExternalArrayTestHelper<v8::internal::ExternalByteArray, int8_t>(
9695 v8::kExternalByteArray,
9696 -128,
9697 127);
9698}
9699
9700
9701THREADED_TEST(ExternalUnsignedByteArray) {
9702 ExternalArrayTestHelper<v8::internal::ExternalUnsignedByteArray, uint8_t>(
9703 v8::kExternalUnsignedByteArray,
9704 0,
9705 255);
9706}
9707
9708
9709THREADED_TEST(ExternalShortArray) {
9710 ExternalArrayTestHelper<v8::internal::ExternalShortArray, int16_t>(
9711 v8::kExternalShortArray,
9712 -32768,
9713 32767);
9714}
9715
9716
9717THREADED_TEST(ExternalUnsignedShortArray) {
9718 ExternalArrayTestHelper<v8::internal::ExternalUnsignedShortArray, uint16_t>(
9719 v8::kExternalUnsignedShortArray,
9720 0,
9721 65535);
9722}
9723
9724
9725THREADED_TEST(ExternalIntArray) {
9726 ExternalArrayTestHelper<v8::internal::ExternalIntArray, int32_t>(
9727 v8::kExternalIntArray,
9728 INT_MIN, // -2147483648
9729 INT_MAX); // 2147483647
9730}
9731
9732
9733THREADED_TEST(ExternalUnsignedIntArray) {
9734 ExternalArrayTestHelper<v8::internal::ExternalUnsignedIntArray, uint32_t>(
9735 v8::kExternalUnsignedIntArray,
9736 0,
9737 UINT_MAX); // 4294967295
9738}
9739
9740
9741THREADED_TEST(ExternalFloatArray) {
9742 ExternalArrayTestHelper<v8::internal::ExternalFloatArray, float>(
9743 v8::kExternalFloatArray,
9744 -500,
9745 500);
9746}
9747
9748
9749THREADED_TEST(ExternalArrays) {
9750 TestExternalByteArray();
9751 TestExternalUnsignedByteArray();
9752 TestExternalShortArray();
9753 TestExternalUnsignedShortArray();
9754 TestExternalIntArray();
9755 TestExternalUnsignedIntArray();
9756 TestExternalFloatArray();
9757}
9758
9759
Steve Blocka7e24c12009-10-30 11:49:00 +00009760THREADED_TEST(ScriptContextDependence) {
9761 v8::HandleScope scope;
9762 LocalContext c1;
9763 const char *source = "foo";
9764 v8::Handle<v8::Script> dep = v8::Script::Compile(v8::String::New(source));
9765 v8::Handle<v8::Script> indep = v8::Script::New(v8::String::New(source));
9766 c1->Global()->Set(v8::String::New("foo"), v8::Integer::New(100));
9767 CHECK_EQ(dep->Run()->Int32Value(), 100);
9768 CHECK_EQ(indep->Run()->Int32Value(), 100);
9769 LocalContext c2;
9770 c2->Global()->Set(v8::String::New("foo"), v8::Integer::New(101));
9771 CHECK_EQ(dep->Run()->Int32Value(), 100);
9772 CHECK_EQ(indep->Run()->Int32Value(), 101);
9773}
9774
9775
9776THREADED_TEST(StackTrace) {
9777 v8::HandleScope scope;
9778 LocalContext context;
9779 v8::TryCatch try_catch;
9780 const char *source = "function foo() { FAIL.FAIL; }; foo();";
9781 v8::Handle<v8::String> src = v8::String::New(source);
9782 v8::Handle<v8::String> origin = v8::String::New("stack-trace-test");
9783 v8::Script::New(src, origin)->Run();
9784 CHECK(try_catch.HasCaught());
9785 v8::String::Utf8Value stack(try_catch.StackTrace());
9786 CHECK(strstr(*stack, "at foo (stack-trace-test") != NULL);
9787}
9788
9789
Kristian Monsen25f61362010-05-21 11:50:48 +01009790// Checks that a StackFrame has certain expected values.
9791void checkStackFrame(const char* expected_script_name,
9792 const char* expected_func_name, int expected_line_number,
9793 int expected_column, bool is_eval, bool is_constructor,
9794 v8::Handle<v8::StackFrame> frame) {
9795 v8::HandleScope scope;
9796 v8::String::Utf8Value func_name(frame->GetFunctionName());
9797 v8::String::Utf8Value script_name(frame->GetScriptName());
9798 if (*script_name == NULL) {
9799 // The situation where there is no associated script, like for evals.
9800 CHECK(expected_script_name == NULL);
9801 } else {
9802 CHECK(strstr(*script_name, expected_script_name) != NULL);
9803 }
9804 CHECK(strstr(*func_name, expected_func_name) != NULL);
9805 CHECK_EQ(expected_line_number, frame->GetLineNumber());
9806 CHECK_EQ(expected_column, frame->GetColumn());
9807 CHECK_EQ(is_eval, frame->IsEval());
9808 CHECK_EQ(is_constructor, frame->IsConstructor());
9809}
9810
9811
9812v8::Handle<Value> AnalyzeStackInNativeCode(const v8::Arguments& args) {
9813 v8::HandleScope scope;
9814 const char* origin = "capture-stack-trace-test";
9815 const int kOverviewTest = 1;
9816 const int kDetailedTest = 2;
9817
9818 ASSERT(args.Length() == 1);
9819
9820 int testGroup = args[0]->Int32Value();
9821 if (testGroup == kOverviewTest) {
9822 v8::Handle<v8::StackTrace> stackTrace =
9823 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kOverview);
9824 CHECK_EQ(4, stackTrace->GetFrameCount());
9825 checkStackFrame(origin, "bar", 2, 10, false, false,
9826 stackTrace->GetFrame(0));
9827 checkStackFrame(origin, "foo", 6, 3, false, false,
9828 stackTrace->GetFrame(1));
9829 checkStackFrame(NULL, "", 1, 1, false, false,
9830 stackTrace->GetFrame(2));
9831 // The last frame is an anonymous function that has the initial call.
9832 checkStackFrame(origin, "", 8, 7, false, false,
9833 stackTrace->GetFrame(3));
9834
9835 CHECK(stackTrace->AsArray()->IsArray());
9836 } else if (testGroup == kDetailedTest) {
9837 v8::Handle<v8::StackTrace> stackTrace =
9838 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kDetailed);
9839 CHECK_EQ(4, stackTrace->GetFrameCount());
9840 checkStackFrame(origin, "bat", 4, 22, false, false,
9841 stackTrace->GetFrame(0));
9842 checkStackFrame(origin, "baz", 8, 3, false, true,
9843 stackTrace->GetFrame(1));
9844 checkStackFrame(NULL, "", 1, 1, true, false,
9845 stackTrace->GetFrame(2));
9846 // The last frame is an anonymous function that has the initial call to foo.
9847 checkStackFrame(origin, "", 10, 1, false, false,
9848 stackTrace->GetFrame(3));
9849
9850 CHECK(stackTrace->AsArray()->IsArray());
9851 }
9852 return v8::Undefined();
9853}
9854
9855
9856// Tests the C++ StackTrace API.
9857THREADED_TEST(CaptureStackTrace) {
9858 v8::HandleScope scope;
9859 v8::Handle<v8::String> origin = v8::String::New("capture-stack-trace-test");
9860 Local<ObjectTemplate> templ = ObjectTemplate::New();
9861 templ->Set(v8_str("AnalyzeStackInNativeCode"),
9862 v8::FunctionTemplate::New(AnalyzeStackInNativeCode));
9863 LocalContext context(0, templ);
9864
9865 // Test getting OVERVIEW information. Should ignore information that is not
9866 // script name, function name, line number, and column offset.
9867 const char *overview_source =
9868 "function bar() {\n"
9869 " var y; AnalyzeStackInNativeCode(1);\n"
9870 "}\n"
9871 "function foo() {\n"
9872 "\n"
9873 " bar();\n"
9874 "}\n"
9875 "var x;eval('new foo();');";
9876 v8::Handle<v8::String> overview_src = v8::String::New(overview_source);
9877 v8::Handle<Value> overview_result =
9878 v8::Script::New(overview_src, origin)->Run();
9879 ASSERT(!overview_result.IsEmpty());
9880 ASSERT(overview_result->IsObject());
9881
9882 // Test getting DETAILED information.
9883 const char *detailed_source =
9884 "function bat() {AnalyzeStackInNativeCode(2);\n"
9885 "}\n"
9886 "\n"
9887 "function baz() {\n"
9888 " bat();\n"
9889 "}\n"
9890 "eval('new baz();');";
9891 v8::Handle<v8::String> detailed_src = v8::String::New(detailed_source);
9892 // Make the script using a non-zero line and column offset.
9893 v8::Handle<v8::Integer> line_offset = v8::Integer::New(3);
9894 v8::Handle<v8::Integer> column_offset = v8::Integer::New(5);
9895 v8::ScriptOrigin detailed_origin(origin, line_offset, column_offset);
9896 v8::Handle<v8::Script> detailed_script(
9897 v8::Script::New(detailed_src, &detailed_origin));
9898 v8::Handle<Value> detailed_result = detailed_script->Run();
9899 ASSERT(!detailed_result.IsEmpty());
9900 ASSERT(detailed_result->IsObject());
9901}
9902
9903
Steve Block3ce2e202009-11-05 08:53:23 +00009904// Test that idle notification can be handled and eventually returns true.
Steve Blocka7e24c12009-10-30 11:49:00 +00009905THREADED_TEST(IdleNotification) {
Steve Block3ce2e202009-11-05 08:53:23 +00009906 bool rv = false;
9907 for (int i = 0; i < 100; i++) {
9908 rv = v8::V8::IdleNotification();
9909 if (rv)
9910 break;
9911 }
9912 CHECK(rv == true);
Steve Blocka7e24c12009-10-30 11:49:00 +00009913}
9914
9915
9916static uint32_t* stack_limit;
9917
9918static v8::Handle<Value> GetStackLimitCallback(const v8::Arguments& args) {
9919 stack_limit = reinterpret_cast<uint32_t*>(i::StackGuard::climit());
9920 return v8::Undefined();
9921}
9922
9923
9924// Uses the address of a local variable to determine the stack top now.
9925// Given a size, returns an address that is that far from the current
9926// top of stack.
9927static uint32_t* ComputeStackLimit(uint32_t size) {
9928 uint32_t* answer = &size - (size / sizeof(size));
9929 // If the size is very large and the stack is very near the bottom of
9930 // memory then the calculation above may wrap around and give an address
9931 // that is above the (downwards-growing) stack. In that case we return
9932 // a very low address.
9933 if (answer > &size) return reinterpret_cast<uint32_t*>(sizeof(size));
9934 return answer;
9935}
9936
9937
9938TEST(SetResourceConstraints) {
9939 static const int K = 1024;
9940 uint32_t* set_limit = ComputeStackLimit(128 * K);
9941
9942 // Set stack limit.
9943 v8::ResourceConstraints constraints;
9944 constraints.set_stack_limit(set_limit);
9945 CHECK(v8::SetResourceConstraints(&constraints));
9946
9947 // Execute a script.
9948 v8::HandleScope scope;
9949 LocalContext env;
9950 Local<v8::FunctionTemplate> fun_templ =
9951 v8::FunctionTemplate::New(GetStackLimitCallback);
9952 Local<Function> fun = fun_templ->GetFunction();
9953 env->Global()->Set(v8_str("get_stack_limit"), fun);
9954 CompileRun("get_stack_limit();");
9955
9956 CHECK(stack_limit == set_limit);
9957}
9958
9959
9960TEST(SetResourceConstraintsInThread) {
9961 uint32_t* set_limit;
9962 {
9963 v8::Locker locker;
9964 static const int K = 1024;
9965 set_limit = ComputeStackLimit(128 * K);
9966
9967 // Set stack limit.
9968 v8::ResourceConstraints constraints;
9969 constraints.set_stack_limit(set_limit);
9970 CHECK(v8::SetResourceConstraints(&constraints));
9971
9972 // Execute a script.
9973 v8::HandleScope scope;
9974 LocalContext env;
9975 Local<v8::FunctionTemplate> fun_templ =
9976 v8::FunctionTemplate::New(GetStackLimitCallback);
9977 Local<Function> fun = fun_templ->GetFunction();
9978 env->Global()->Set(v8_str("get_stack_limit"), fun);
9979 CompileRun("get_stack_limit();");
9980
9981 CHECK(stack_limit == set_limit);
9982 }
9983 {
9984 v8::Locker locker;
9985 CHECK(stack_limit == set_limit);
9986 }
9987}
Steve Block3ce2e202009-11-05 08:53:23 +00009988
9989
9990THREADED_TEST(GetHeapStatistics) {
9991 v8::HandleScope scope;
9992 LocalContext c1;
9993 v8::HeapStatistics heap_statistics;
Steve Blockd0582a62009-12-15 09:54:21 +00009994 CHECK_EQ(static_cast<int>(heap_statistics.total_heap_size()), 0);
9995 CHECK_EQ(static_cast<int>(heap_statistics.used_heap_size()), 0);
Steve Block3ce2e202009-11-05 08:53:23 +00009996 v8::V8::GetHeapStatistics(&heap_statistics);
Steve Blockd0582a62009-12-15 09:54:21 +00009997 CHECK_NE(static_cast<int>(heap_statistics.total_heap_size()), 0);
9998 CHECK_NE(static_cast<int>(heap_statistics.used_heap_size()), 0);
9999}
10000
10001
10002static double DoubleFromBits(uint64_t value) {
10003 double target;
10004#ifdef BIG_ENDIAN_FLOATING_POINT
10005 const int kIntSize = 4;
10006 // Somebody swapped the lower and higher half of doubles.
10007 memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize);
10008 memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize);
10009#else
10010 memcpy(&target, &value, sizeof(target));
10011#endif
10012 return target;
10013}
10014
10015
10016static uint64_t DoubleToBits(double value) {
10017 uint64_t target;
10018#ifdef BIG_ENDIAN_FLOATING_POINT
10019 const int kIntSize = 4;
10020 // Somebody swapped the lower and higher half of doubles.
10021 memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize);
10022 memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize);
10023#else
10024 memcpy(&target, &value, sizeof(target));
10025#endif
10026 return target;
10027}
10028
10029
10030static double DoubleToDateTime(double input) {
10031 double date_limit = 864e13;
10032 if (IsNaN(input) || input < -date_limit || input > date_limit) {
10033 return i::OS::nan_value();
10034 }
10035 return (input < 0) ? -(floor(-input)) : floor(input);
10036}
10037
10038// We don't have a consistent way to write 64-bit constants syntactically, so we
10039// split them into two 32-bit constants and combine them programmatically.
10040static double DoubleFromBits(uint32_t high_bits, uint32_t low_bits) {
10041 return DoubleFromBits((static_cast<uint64_t>(high_bits) << 32) | low_bits);
10042}
10043
10044
10045THREADED_TEST(QuietSignalingNaNs) {
10046 v8::HandleScope scope;
10047 LocalContext context;
10048 v8::TryCatch try_catch;
10049
10050 // Special double values.
10051 double snan = DoubleFromBits(0x7ff00000, 0x00000001);
10052 double qnan = DoubleFromBits(0x7ff80000, 0x00000000);
10053 double infinity = DoubleFromBits(0x7ff00000, 0x00000000);
10054 double max_normal = DoubleFromBits(0x7fefffff, 0xffffffffu);
10055 double min_normal = DoubleFromBits(0x00100000, 0x00000000);
10056 double max_denormal = DoubleFromBits(0x000fffff, 0xffffffffu);
10057 double min_denormal = DoubleFromBits(0x00000000, 0x00000001);
10058
10059 // Date values are capped at +/-100000000 days (times 864e5 ms per day)
10060 // on either side of the epoch.
10061 double date_limit = 864e13;
10062
10063 double test_values[] = {
10064 snan,
10065 qnan,
10066 infinity,
10067 max_normal,
10068 date_limit + 1,
10069 date_limit,
10070 min_normal,
10071 max_denormal,
10072 min_denormal,
10073 0,
10074 -0,
10075 -min_denormal,
10076 -max_denormal,
10077 -min_normal,
10078 -date_limit,
10079 -date_limit - 1,
10080 -max_normal,
10081 -infinity,
10082 -qnan,
10083 -snan
10084 };
10085 int num_test_values = 20;
10086
10087 for (int i = 0; i < num_test_values; i++) {
10088 double test_value = test_values[i];
10089
10090 // Check that Number::New preserves non-NaNs and quiets SNaNs.
10091 v8::Handle<v8::Value> number = v8::Number::New(test_value);
10092 double stored_number = number->NumberValue();
10093 if (!IsNaN(test_value)) {
10094 CHECK_EQ(test_value, stored_number);
10095 } else {
10096 uint64_t stored_bits = DoubleToBits(stored_number);
10097 // Check if quiet nan (bits 51..62 all set).
10098 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff));
10099 }
10100
10101 // Check that Date::New preserves non-NaNs in the date range and
10102 // quiets SNaNs.
10103 v8::Handle<v8::Value> date = v8::Date::New(test_value);
10104 double expected_stored_date = DoubleToDateTime(test_value);
10105 double stored_date = date->NumberValue();
10106 if (!IsNaN(expected_stored_date)) {
10107 CHECK_EQ(expected_stored_date, stored_date);
10108 } else {
10109 uint64_t stored_bits = DoubleToBits(stored_date);
10110 // Check if quiet nan (bits 51..62 all set).
10111 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff));
10112 }
10113 }
10114}
10115
10116
10117static v8::Handle<Value> SpaghettiIncident(const v8::Arguments& args) {
10118 v8::HandleScope scope;
10119 v8::TryCatch tc;
10120 v8::Handle<v8::String> str = args[0]->ToString();
10121 if (tc.HasCaught())
10122 return tc.ReThrow();
10123 return v8::Undefined();
10124}
10125
10126
10127// Test that an exception can be propagated down through a spaghetti
10128// stack using ReThrow.
10129THREADED_TEST(SpaghettiStackReThrow) {
10130 v8::HandleScope scope;
10131 LocalContext context;
10132 context->Global()->Set(
10133 v8::String::New("s"),
10134 v8::FunctionTemplate::New(SpaghettiIncident)->GetFunction());
10135 v8::TryCatch try_catch;
10136 CompileRun(
10137 "var i = 0;"
10138 "var o = {"
10139 " toString: function () {"
10140 " if (i == 10) {"
10141 " throw 'Hey!';"
10142 " } else {"
10143 " i++;"
10144 " return s(o);"
10145 " }"
10146 " }"
10147 "};"
10148 "s(o);");
10149 CHECK(try_catch.HasCaught());
10150 v8::String::Utf8Value value(try_catch.Exception());
10151 CHECK_EQ(0, strcmp(*value, "Hey!"));
10152}
10153
10154
Steve Blockd0582a62009-12-15 09:54:21 +000010155TEST(Regress528) {
10156 v8::V8::Initialize();
10157
10158 v8::HandleScope scope;
10159 v8::Persistent<Context> context;
10160 v8::Persistent<Context> other_context;
10161 int gc_count;
10162
10163 // Create a context used to keep the code from aging in the compilation
10164 // cache.
10165 other_context = Context::New();
10166
10167 // Context-dependent context data creates reference from the compilation
10168 // cache to the global object.
10169 const char* source_simple = "1";
10170 context = Context::New();
10171 {
10172 v8::HandleScope scope;
10173
10174 context->Enter();
10175 Local<v8::String> obj = v8::String::New("");
10176 context->SetData(obj);
10177 CompileRun(source_simple);
10178 context->Exit();
10179 }
10180 context.Dispose();
10181 for (gc_count = 1; gc_count < 10; gc_count++) {
10182 other_context->Enter();
10183 CompileRun(source_simple);
10184 other_context->Exit();
10185 v8::internal::Heap::CollectAllGarbage(false);
10186 if (GetGlobalObjectsCount() == 1) break;
10187 }
10188 CHECK_GE(2, gc_count);
10189 CHECK_EQ(1, GetGlobalObjectsCount());
10190
10191 // Eval in a function creates reference from the compilation cache to the
10192 // global object.
10193 const char* source_eval = "function f(){eval('1')}; f()";
10194 context = Context::New();
10195 {
10196 v8::HandleScope scope;
10197
10198 context->Enter();
10199 CompileRun(source_eval);
10200 context->Exit();
10201 }
10202 context.Dispose();
10203 for (gc_count = 1; gc_count < 10; gc_count++) {
10204 other_context->Enter();
10205 CompileRun(source_eval);
10206 other_context->Exit();
10207 v8::internal::Heap::CollectAllGarbage(false);
10208 if (GetGlobalObjectsCount() == 1) break;
10209 }
10210 CHECK_GE(2, gc_count);
10211 CHECK_EQ(1, GetGlobalObjectsCount());
10212
10213 // Looking up the line number for an exception creates reference from the
10214 // compilation cache to the global object.
10215 const char* source_exception = "function f(){throw 1;} f()";
10216 context = Context::New();
10217 {
10218 v8::HandleScope scope;
10219
10220 context->Enter();
10221 v8::TryCatch try_catch;
10222 CompileRun(source_exception);
10223 CHECK(try_catch.HasCaught());
10224 v8::Handle<v8::Message> message = try_catch.Message();
10225 CHECK(!message.IsEmpty());
10226 CHECK_EQ(1, message->GetLineNumber());
10227 context->Exit();
10228 }
10229 context.Dispose();
10230 for (gc_count = 1; gc_count < 10; gc_count++) {
10231 other_context->Enter();
10232 CompileRun(source_exception);
10233 other_context->Exit();
10234 v8::internal::Heap::CollectAllGarbage(false);
10235 if (GetGlobalObjectsCount() == 1) break;
10236 }
10237 CHECK_GE(2, gc_count);
10238 CHECK_EQ(1, GetGlobalObjectsCount());
10239
10240 other_context.Dispose();
Steve Block3ce2e202009-11-05 08:53:23 +000010241}
Andrei Popescu402d9372010-02-26 13:31:12 +000010242
10243
10244THREADED_TEST(ScriptOrigin) {
10245 v8::HandleScope scope;
10246 LocalContext env;
10247 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test"));
10248 v8::Handle<v8::String> script = v8::String::New(
10249 "function f() {}\n\nfunction g() {}");
10250 v8::Script::Compile(script, &origin)->Run();
10251 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(
10252 env->Global()->Get(v8::String::New("f")));
10253 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast(
10254 env->Global()->Get(v8::String::New("g")));
10255
10256 v8::ScriptOrigin script_origin_f = f->GetScriptOrigin();
10257 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_f.ResourceName()));
10258 CHECK_EQ(0, script_origin_f.ResourceLineOffset()->Int32Value());
10259
10260 v8::ScriptOrigin script_origin_g = g->GetScriptOrigin();
10261 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_g.ResourceName()));
10262 CHECK_EQ(0, script_origin_g.ResourceLineOffset()->Int32Value());
10263}
10264
10265
10266THREADED_TEST(ScriptLineNumber) {
10267 v8::HandleScope scope;
10268 LocalContext env;
10269 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test"));
10270 v8::Handle<v8::String> script = v8::String::New(
10271 "function f() {}\n\nfunction g() {}");
10272 v8::Script::Compile(script, &origin)->Run();
10273 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(
10274 env->Global()->Get(v8::String::New("f")));
10275 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast(
10276 env->Global()->Get(v8::String::New("g")));
10277 CHECK_EQ(0, f->GetScriptLineNumber());
10278 CHECK_EQ(2, g->GetScriptLineNumber());
10279}
10280
10281
10282static v8::Handle<Value> GetterWhichReturns42(Local<String> name,
10283 const AccessorInfo& info) {
10284 return v8_num(42);
10285}
10286
10287
10288static void SetterWhichSetsYOnThisTo23(Local<String> name,
10289 Local<Value> value,
10290 const AccessorInfo& info) {
10291 info.This()->Set(v8_str("y"), v8_num(23));
10292}
10293
10294
Steve Block6ded16b2010-05-10 14:33:55 +010010295TEST(SetterOnConstructorPrototype) {
Andrei Popescu402d9372010-02-26 13:31:12 +000010296 v8::HandleScope scope;
10297 Local<ObjectTemplate> templ = ObjectTemplate::New();
10298 templ->SetAccessor(v8_str("x"),
10299 GetterWhichReturns42,
10300 SetterWhichSetsYOnThisTo23);
10301 LocalContext context;
10302 context->Global()->Set(v8_str("P"), templ->NewInstance());
10303 CompileRun("function C1() {"
10304 " this.x = 23;"
10305 "};"
10306 "C1.prototype = P;"
10307 "function C2() {"
10308 " this.x = 23"
10309 "};"
10310 "C2.prototype = { };"
10311 "C2.prototype.__proto__ = P;");
10312
10313 v8::Local<v8::Script> script;
10314 script = v8::Script::Compile(v8_str("new C1();"));
10315 for (int i = 0; i < 10; i++) {
10316 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
10317 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value());
10318 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value());
10319 }
10320
10321 script = v8::Script::Compile(v8_str("new C2();"));
10322 for (int i = 0; i < 10; i++) {
10323 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run());
10324 CHECK_EQ(42, c2->Get(v8_str("x"))->Int32Value());
10325 CHECK_EQ(23, c2->Get(v8_str("y"))->Int32Value());
10326 }
10327}
10328
10329
10330static v8::Handle<Value> NamedPropertyGetterWhichReturns42(
10331 Local<String> name, const AccessorInfo& info) {
10332 return v8_num(42);
10333}
10334
10335
10336static v8::Handle<Value> NamedPropertySetterWhichSetsYOnThisTo23(
10337 Local<String> name, Local<Value> value, const AccessorInfo& info) {
10338 if (name->Equals(v8_str("x"))) {
10339 info.This()->Set(v8_str("y"), v8_num(23));
10340 }
10341 return v8::Handle<Value>();
10342}
10343
10344
10345THREADED_TEST(InterceptorOnConstructorPrototype) {
10346 v8::HandleScope scope;
10347 Local<ObjectTemplate> templ = ObjectTemplate::New();
10348 templ->SetNamedPropertyHandler(NamedPropertyGetterWhichReturns42,
10349 NamedPropertySetterWhichSetsYOnThisTo23);
10350 LocalContext context;
10351 context->Global()->Set(v8_str("P"), templ->NewInstance());
10352 CompileRun("function C1() {"
10353 " this.x = 23;"
10354 "};"
10355 "C1.prototype = P;"
10356 "function C2() {"
10357 " this.x = 23"
10358 "};"
10359 "C2.prototype = { };"
10360 "C2.prototype.__proto__ = P;");
10361
10362 v8::Local<v8::Script> script;
10363 script = v8::Script::Compile(v8_str("new C1();"));
10364 for (int i = 0; i < 10; i++) {
10365 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
10366 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value());
10367 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value());
10368 }
10369
10370 script = v8::Script::Compile(v8_str("new C2();"));
10371 for (int i = 0; i < 10; i++) {
10372 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run());
10373 CHECK_EQ(23, c2->Get(v8_str("x"))->Int32Value());
10374 CHECK_EQ(42, c2->Get(v8_str("y"))->Int32Value());
10375 }
10376}
Steve Block6ded16b2010-05-10 14:33:55 +010010377
10378
10379TEST(Bug618) {
10380 const char* source = "function C1() {"
10381 " this.x = 23;"
10382 "};"
10383 "C1.prototype = P;";
10384
10385 v8::HandleScope scope;
10386 LocalContext context;
10387 v8::Local<v8::Script> script;
10388
10389 // Use a simple object as prototype.
10390 v8::Local<v8::Object> prototype = v8::Object::New();
10391 prototype->Set(v8_str("y"), v8_num(42));
10392 context->Global()->Set(v8_str("P"), prototype);
10393
10394 // This compile will add the code to the compilation cache.
10395 CompileRun(source);
10396
10397 script = v8::Script::Compile(v8_str("new C1();"));
10398 for (int i = 0; i < 10; i++) {
10399 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
10400 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value());
10401 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value());
10402 }
10403
10404 // Use an API object with accessors as prototype.
10405 Local<ObjectTemplate> templ = ObjectTemplate::New();
10406 templ->SetAccessor(v8_str("x"),
10407 GetterWhichReturns42,
10408 SetterWhichSetsYOnThisTo23);
10409 context->Global()->Set(v8_str("P"), templ->NewInstance());
10410
10411 // This compile will get the code from the compilation cache.
10412 CompileRun(source);
10413
10414 script = v8::Script::Compile(v8_str("new C1();"));
10415 for (int i = 0; i < 10; i++) {
10416 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
10417 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value());
10418 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value());
10419 }
10420}
10421
10422int prologue_call_count = 0;
10423int epilogue_call_count = 0;
10424int prologue_call_count_second = 0;
10425int epilogue_call_count_second = 0;
10426
10427void PrologueCallback(v8::GCType, v8::GCCallbackFlags) {
10428 ++prologue_call_count;
10429}
10430
10431void EpilogueCallback(v8::GCType, v8::GCCallbackFlags) {
10432 ++epilogue_call_count;
10433}
10434
10435void PrologueCallbackSecond(v8::GCType, v8::GCCallbackFlags) {
10436 ++prologue_call_count_second;
10437}
10438
10439void EpilogueCallbackSecond(v8::GCType, v8::GCCallbackFlags) {
10440 ++epilogue_call_count_second;
10441}
10442
10443TEST(GCCallbacks) {
10444 LocalContext context;
10445
10446 v8::V8::AddGCPrologueCallback(PrologueCallback);
10447 v8::V8::AddGCEpilogueCallback(EpilogueCallback);
10448 CHECK_EQ(0, prologue_call_count);
10449 CHECK_EQ(0, epilogue_call_count);
10450 i::Heap::CollectAllGarbage(false);
10451 CHECK_EQ(1, prologue_call_count);
10452 CHECK_EQ(1, epilogue_call_count);
10453 v8::V8::AddGCPrologueCallback(PrologueCallbackSecond);
10454 v8::V8::AddGCEpilogueCallback(EpilogueCallbackSecond);
10455 i::Heap::CollectAllGarbage(false);
10456 CHECK_EQ(2, prologue_call_count);
10457 CHECK_EQ(2, epilogue_call_count);
10458 CHECK_EQ(1, prologue_call_count_second);
10459 CHECK_EQ(1, epilogue_call_count_second);
10460 v8::V8::RemoveGCPrologueCallback(PrologueCallback);
10461 v8::V8::RemoveGCEpilogueCallback(EpilogueCallback);
10462 i::Heap::CollectAllGarbage(false);
10463 CHECK_EQ(2, prologue_call_count);
10464 CHECK_EQ(2, epilogue_call_count);
10465 CHECK_EQ(2, prologue_call_count_second);
10466 CHECK_EQ(2, epilogue_call_count_second);
10467 v8::V8::RemoveGCPrologueCallback(PrologueCallbackSecond);
10468 v8::V8::RemoveGCEpilogueCallback(EpilogueCallbackSecond);
10469 i::Heap::CollectAllGarbage(false);
10470 CHECK_EQ(2, prologue_call_count);
10471 CHECK_EQ(2, epilogue_call_count);
10472 CHECK_EQ(2, prologue_call_count_second);
10473 CHECK_EQ(2, epilogue_call_count_second);
10474}
Kristian Monsen25f61362010-05-21 11:50:48 +010010475
10476
10477THREADED_TEST(AddToJSFunctionResultCache) {
10478 i::FLAG_allow_natives_syntax = true;
10479 v8::HandleScope scope;
10480
10481 LocalContext context;
10482
10483 const char* code =
10484 "(function() {"
10485 " var key0 = 'a';"
10486 " var key1 = 'b';"
10487 " var r0 = %_GetFromCache(0, key0);"
10488 " var r1 = %_GetFromCache(0, key1);"
10489 " var r0_ = %_GetFromCache(0, key0);"
10490 " if (r0 !== r0_)"
10491 " return 'Different results for ' + key0 + ': ' + r0 + ' vs. ' + r0_;"
10492 " var r1_ = %_GetFromCache(0, key1);"
10493 " if (r1 !== r1_)"
10494 " return 'Different results for ' + key1 + ': ' + r1 + ' vs. ' + r1_;"
10495 " return 'PASSED';"
10496 "})()";
10497 v8::internal::Heap::ClearJSFunctionResultCaches();
10498 ExpectString(code, "PASSED");
10499}
10500
10501
10502static const int k0CacheSize = 16;
10503
10504THREADED_TEST(FillJSFunctionResultCache) {
10505 i::FLAG_allow_natives_syntax = true;
10506 v8::HandleScope scope;
10507
10508 LocalContext context;
10509
10510 const char* code =
10511 "(function() {"
10512 " var k = 'a';"
10513 " var r = %_GetFromCache(0, k);"
10514 " for (var i = 0; i < 16; i++) {"
10515 " %_GetFromCache(0, 'a' + i);"
10516 " };"
10517 " if (r === %_GetFromCache(0, k))"
10518 " return 'FAILED: k0CacheSize is too small';"
10519 " return 'PASSED';"
10520 "})()";
10521 v8::internal::Heap::ClearJSFunctionResultCaches();
10522 ExpectString(code, "PASSED");
10523}
10524
10525
10526THREADED_TEST(RoundRobinGetFromCache) {
10527 i::FLAG_allow_natives_syntax = true;
10528 v8::HandleScope scope;
10529
10530 LocalContext context;
10531
10532 const char* code =
10533 "(function() {"
10534 " var keys = [];"
10535 " for (var i = 0; i < 16; i++) keys.push(i);"
10536 " var values = [];"
10537 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);"
10538 " for (var i = 0; i < 16; i++) {"
10539 " var v = %_GetFromCache(0, keys[i]);"
10540 " if (v !== values[i])"
10541 " return 'Wrong value for ' + "
10542 " keys[i] + ': ' + v + ' vs. ' + values[i];"
10543 " };"
10544 " return 'PASSED';"
10545 "})()";
10546 v8::internal::Heap::ClearJSFunctionResultCaches();
10547 ExpectString(code, "PASSED");
10548}
10549
10550
10551THREADED_TEST(ReverseGetFromCache) {
10552 i::FLAG_allow_natives_syntax = true;
10553 v8::HandleScope scope;
10554
10555 LocalContext context;
10556
10557 const char* code =
10558 "(function() {"
10559 " var keys = [];"
10560 " for (var i = 0; i < 16; i++) keys.push(i);"
10561 " var values = [];"
10562 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);"
10563 " for (var i = 15; i >= 16; i--) {"
10564 " var v = %_GetFromCache(0, keys[i]);"
10565 " if (v !== values[i])"
10566 " return 'Wrong value for ' + "
10567 " keys[i] + ': ' + v + ' vs. ' + values[i];"
10568 " };"
10569 " return 'PASSED';"
10570 "})()";
10571 v8::internal::Heap::ClearJSFunctionResultCaches();
10572 ExpectString(code, "PASSED");
10573}
10574
10575
10576THREADED_TEST(TestEviction) {
10577 i::FLAG_allow_natives_syntax = true;
10578 v8::HandleScope scope;
10579
10580 LocalContext context;
10581
10582 const char* code =
10583 "(function() {"
10584 " for (var i = 0; i < 2*16; i++) {"
10585 " %_GetFromCache(0, 'a' + i);"
10586 " };"
10587 " return 'PASSED';"
10588 "})()";
10589 v8::internal::Heap::ClearJSFunctionResultCaches();
10590 ExpectString(code, "PASSED");
10591}