blob: fd44aec47bb2b3c05f62318fab6f38a9b94e2294 [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"
Iain Merrick9ac36c92010-09-13 15:29:50 +010040#include "parser.h"
Steve Blocka7e24c12009-10-30 11:49:00 +000041
Andrei Popescu31002712010-02-23 13:46:05 +000042static const bool kLogThreading = true;
Steve Blockd0582a62009-12-15 09:54:21 +000043
Steve Blocka7e24c12009-10-30 11:49:00 +000044static bool IsNaN(double x) {
45#ifdef WIN32
46 return _isnan(x);
47#else
48 return isnan(x);
49#endif
50}
51
52using ::v8::ObjectTemplate;
53using ::v8::Value;
54using ::v8::Context;
55using ::v8::Local;
56using ::v8::String;
57using ::v8::Script;
58using ::v8::Function;
59using ::v8::AccessorInfo;
60using ::v8::Extension;
61
Steve Block8defd9f2010-07-08 12:39:36 +010062namespace i = ::i;
Steve Blocka7e24c12009-10-30 11:49:00 +000063
Steve Blocka7e24c12009-10-30 11:49:00 +000064
Leon Clarked91b9f72010-01-27 17:25:45 +000065static void ExpectString(const char* code, const char* expected) {
66 Local<Value> result = CompileRun(code);
67 CHECK(result->IsString());
68 String::AsciiValue ascii(result);
69 CHECK_EQ(expected, *ascii);
70}
71
72
73static void ExpectBoolean(const char* code, bool expected) {
74 Local<Value> result = CompileRun(code);
75 CHECK(result->IsBoolean());
76 CHECK_EQ(expected, result->BooleanValue());
77}
78
79
Leon Clarkef7060e22010-06-03 12:02:55 +010080static void ExpectTrue(const char* code) {
81 ExpectBoolean(code, true);
82}
83
84
Iain Merrick75681382010-08-19 15:07:18 +010085static void ExpectFalse(const char* code) {
86 ExpectBoolean(code, false);
87}
88
89
Leon Clarked91b9f72010-01-27 17:25:45 +000090static void ExpectObject(const char* code, Local<Value> expected) {
91 Local<Value> result = CompileRun(code);
92 CHECK(result->Equals(expected));
93}
94
95
Iain Merrick75681382010-08-19 15:07:18 +010096static void ExpectUndefined(const char* code) {
97 Local<Value> result = CompileRun(code);
98 CHECK(result->IsUndefined());
99}
100
101
Steve Blocka7e24c12009-10-30 11:49:00 +0000102static int signature_callback_count;
103static v8::Handle<Value> IncrementingSignatureCallback(
104 const v8::Arguments& args) {
105 ApiTestFuzzer::Fuzz();
106 signature_callback_count++;
107 v8::Handle<v8::Array> result = v8::Array::New(args.Length());
108 for (int i = 0; i < args.Length(); i++)
109 result->Set(v8::Integer::New(i), args[i]);
110 return result;
111}
112
113
114static v8::Handle<Value> SignatureCallback(const v8::Arguments& args) {
115 ApiTestFuzzer::Fuzz();
116 v8::Handle<v8::Array> result = v8::Array::New(args.Length());
117 for (int i = 0; i < args.Length(); i++) {
118 result->Set(v8::Integer::New(i), args[i]);
119 }
120 return result;
121}
122
123
124THREADED_TEST(Handles) {
125 v8::HandleScope scope;
126 Local<Context> local_env;
127 {
128 LocalContext env;
129 local_env = env.local();
130 }
131
132 // Local context should still be live.
133 CHECK(!local_env.IsEmpty());
134 local_env->Enter();
135
136 v8::Handle<v8::Primitive> undef = v8::Undefined();
137 CHECK(!undef.IsEmpty());
138 CHECK(undef->IsUndefined());
139
140 const char* c_source = "1 + 2 + 3";
141 Local<String> source = String::New(c_source);
142 Local<Script> script = Script::Compile(source);
143 CHECK_EQ(6, script->Run()->Int32Value());
144
145 local_env->Exit();
146}
147
148
Steve Blocka7e24c12009-10-30 11:49:00 +0000149THREADED_TEST(ReceiverSignature) {
150 v8::HandleScope scope;
151 LocalContext env;
152 v8::Handle<v8::FunctionTemplate> fun = v8::FunctionTemplate::New();
153 v8::Handle<v8::Signature> sig = v8::Signature::New(fun);
154 fun->PrototypeTemplate()->Set(
155 v8_str("m"),
156 v8::FunctionTemplate::New(IncrementingSignatureCallback,
157 v8::Handle<Value>(),
158 sig));
159 env->Global()->Set(v8_str("Fun"), fun->GetFunction());
160 signature_callback_count = 0;
161 CompileRun(
162 "var o = new Fun();"
163 "o.m();");
164 CHECK_EQ(1, signature_callback_count);
165 v8::Handle<v8::FunctionTemplate> sub_fun = v8::FunctionTemplate::New();
166 sub_fun->Inherit(fun);
167 env->Global()->Set(v8_str("SubFun"), sub_fun->GetFunction());
168 CompileRun(
169 "var o = new SubFun();"
170 "o.m();");
171 CHECK_EQ(2, signature_callback_count);
172
173 v8::TryCatch try_catch;
174 CompileRun(
175 "var o = { };"
176 "o.m = Fun.prototype.m;"
177 "o.m();");
178 CHECK_EQ(2, signature_callback_count);
179 CHECK(try_catch.HasCaught());
180 try_catch.Reset();
181 v8::Handle<v8::FunctionTemplate> unrel_fun = v8::FunctionTemplate::New();
182 sub_fun->Inherit(fun);
183 env->Global()->Set(v8_str("UnrelFun"), unrel_fun->GetFunction());
184 CompileRun(
185 "var o = new UnrelFun();"
186 "o.m = Fun.prototype.m;"
187 "o.m();");
188 CHECK_EQ(2, signature_callback_count);
189 CHECK(try_catch.HasCaught());
190}
191
192
193
194
195THREADED_TEST(ArgumentSignature) {
196 v8::HandleScope scope;
197 LocalContext env;
198 v8::Handle<v8::FunctionTemplate> cons = v8::FunctionTemplate::New();
199 cons->SetClassName(v8_str("Cons"));
200 v8::Handle<v8::Signature> sig =
201 v8::Signature::New(v8::Handle<v8::FunctionTemplate>(), 1, &cons);
202 v8::Handle<v8::FunctionTemplate> fun =
203 v8::FunctionTemplate::New(SignatureCallback, v8::Handle<Value>(), sig);
204 env->Global()->Set(v8_str("Cons"), cons->GetFunction());
205 env->Global()->Set(v8_str("Fun1"), fun->GetFunction());
206
207 v8::Handle<Value> value1 = CompileRun("Fun1(4) == '';");
208 CHECK(value1->IsTrue());
209
210 v8::Handle<Value> value2 = CompileRun("Fun1(new Cons()) == '[object Cons]';");
211 CHECK(value2->IsTrue());
212
213 v8::Handle<Value> value3 = CompileRun("Fun1() == '';");
214 CHECK(value3->IsTrue());
215
216 v8::Handle<v8::FunctionTemplate> cons1 = v8::FunctionTemplate::New();
217 cons1->SetClassName(v8_str("Cons1"));
218 v8::Handle<v8::FunctionTemplate> cons2 = v8::FunctionTemplate::New();
219 cons2->SetClassName(v8_str("Cons2"));
220 v8::Handle<v8::FunctionTemplate> cons3 = v8::FunctionTemplate::New();
221 cons3->SetClassName(v8_str("Cons3"));
222
223 v8::Handle<v8::FunctionTemplate> args[3] = { cons1, cons2, cons3 };
224 v8::Handle<v8::Signature> wsig =
225 v8::Signature::New(v8::Handle<v8::FunctionTemplate>(), 3, args);
226 v8::Handle<v8::FunctionTemplate> fun2 =
227 v8::FunctionTemplate::New(SignatureCallback, v8::Handle<Value>(), wsig);
228
229 env->Global()->Set(v8_str("Cons1"), cons1->GetFunction());
230 env->Global()->Set(v8_str("Cons2"), cons2->GetFunction());
231 env->Global()->Set(v8_str("Cons3"), cons3->GetFunction());
232 env->Global()->Set(v8_str("Fun2"), fun2->GetFunction());
233 v8::Handle<Value> value4 = CompileRun(
234 "Fun2(new Cons1(), new Cons2(), new Cons3()) =="
235 "'[object Cons1],[object Cons2],[object Cons3]'");
236 CHECK(value4->IsTrue());
237
238 v8::Handle<Value> value5 = CompileRun(
239 "Fun2(new Cons1(), new Cons2(), 5) == '[object Cons1],[object Cons2],'");
240 CHECK(value5->IsTrue());
241
242 v8::Handle<Value> value6 = CompileRun(
243 "Fun2(new Cons3(), new Cons2(), new Cons1()) == ',[object Cons2],'");
244 CHECK(value6->IsTrue());
245
246 v8::Handle<Value> value7 = CompileRun(
247 "Fun2(new Cons1(), new Cons2(), new Cons3(), 'd') == "
248 "'[object Cons1],[object Cons2],[object Cons3],d';");
249 CHECK(value7->IsTrue());
250
251 v8::Handle<Value> value8 = CompileRun(
252 "Fun2(new Cons1(), new Cons2()) == '[object Cons1],[object Cons2]'");
253 CHECK(value8->IsTrue());
254}
255
256
257THREADED_TEST(HulIgennem) {
258 v8::HandleScope scope;
259 LocalContext env;
260 v8::Handle<v8::Primitive> undef = v8::Undefined();
261 Local<String> undef_str = undef->ToString();
262 char* value = i::NewArray<char>(undef_str->Length() + 1);
263 undef_str->WriteAscii(value);
264 CHECK_EQ(0, strcmp(value, "undefined"));
265 i::DeleteArray(value);
266}
267
268
269THREADED_TEST(Access) {
270 v8::HandleScope scope;
271 LocalContext env;
272 Local<v8::Object> obj = v8::Object::New();
273 Local<Value> foo_before = obj->Get(v8_str("foo"));
274 CHECK(foo_before->IsUndefined());
275 Local<String> bar_str = v8_str("bar");
276 obj->Set(v8_str("foo"), bar_str);
277 Local<Value> foo_after = obj->Get(v8_str("foo"));
278 CHECK(!foo_after->IsUndefined());
279 CHECK(foo_after->IsString());
280 CHECK_EQ(bar_str, foo_after);
281}
282
283
Steve Block6ded16b2010-05-10 14:33:55 +0100284THREADED_TEST(AccessElement) {
285 v8::HandleScope scope;
286 LocalContext env;
287 Local<v8::Object> obj = v8::Object::New();
288 Local<Value> before = obj->Get(1);
289 CHECK(before->IsUndefined());
290 Local<String> bar_str = v8_str("bar");
291 obj->Set(1, bar_str);
292 Local<Value> after = obj->Get(1);
293 CHECK(!after->IsUndefined());
294 CHECK(after->IsString());
295 CHECK_EQ(bar_str, after);
296
297 Local<v8::Array> value = CompileRun("[\"a\", \"b\"]").As<v8::Array>();
298 CHECK_EQ(v8_str("a"), value->Get(0));
299 CHECK_EQ(v8_str("b"), value->Get(1));
300}
301
302
Steve Blocka7e24c12009-10-30 11:49:00 +0000303THREADED_TEST(Script) {
304 v8::HandleScope scope;
305 LocalContext env;
306 const char* c_source = "1 + 2 + 3";
307 Local<String> source = String::New(c_source);
308 Local<Script> script = Script::Compile(source);
309 CHECK_EQ(6, script->Run()->Int32Value());
310}
311
312
313static uint16_t* AsciiToTwoByteString(const char* source) {
Steve Blockd0582a62009-12-15 09:54:21 +0000314 int array_length = i::StrLength(source) + 1;
Steve Blocka7e24c12009-10-30 11:49:00 +0000315 uint16_t* converted = i::NewArray<uint16_t>(array_length);
Steve Blockd0582a62009-12-15 09:54:21 +0000316 for (int i = 0; i < array_length; i++) converted[i] = source[i];
Steve Blocka7e24c12009-10-30 11:49:00 +0000317 return converted;
318}
319
320
321class TestResource: public String::ExternalStringResource {
322 public:
323 static int dispose_count;
324
325 explicit TestResource(uint16_t* data)
326 : data_(data), length_(0) {
327 while (data[length_]) ++length_;
328 }
329
330 ~TestResource() {
331 i::DeleteArray(data_);
332 ++dispose_count;
333 }
334
335 const uint16_t* data() const {
336 return data_;
337 }
338
339 size_t length() const {
340 return length_;
341 }
342 private:
343 uint16_t* data_;
344 size_t length_;
345};
346
347
348int TestResource::dispose_count = 0;
349
350
351class TestAsciiResource: public String::ExternalAsciiStringResource {
352 public:
353 static int dispose_count;
354
355 explicit TestAsciiResource(const char* data)
356 : data_(data),
357 length_(strlen(data)) { }
358
359 ~TestAsciiResource() {
360 i::DeleteArray(data_);
361 ++dispose_count;
362 }
363
364 const char* data() const {
365 return data_;
366 }
367
368 size_t length() const {
369 return length_;
370 }
371 private:
372 const char* data_;
373 size_t length_;
374};
375
376
377int TestAsciiResource::dispose_count = 0;
378
379
380THREADED_TEST(ScriptUsingStringResource) {
381 TestResource::dispose_count = 0;
382 const char* c_source = "1 + 2 * 3";
383 uint16_t* two_byte_source = AsciiToTwoByteString(c_source);
384 {
385 v8::HandleScope scope;
386 LocalContext env;
387 TestResource* resource = new TestResource(two_byte_source);
388 Local<String> source = String::NewExternal(resource);
389 Local<Script> script = Script::Compile(source);
390 Local<Value> value = script->Run();
391 CHECK(value->IsNumber());
392 CHECK_EQ(7, value->Int32Value());
393 CHECK(source->IsExternal());
394 CHECK_EQ(resource,
395 static_cast<TestResource*>(source->GetExternalStringResource()));
Steve Block8defd9f2010-07-08 12:39:36 +0100396 i::Heap::CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000397 CHECK_EQ(0, TestResource::dispose_count);
398 }
Steve Block8defd9f2010-07-08 12:39:36 +0100399 i::CompilationCache::Clear();
400 i::Heap::CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000401 CHECK_EQ(1, TestResource::dispose_count);
402}
403
404
405THREADED_TEST(ScriptUsingAsciiStringResource) {
406 TestAsciiResource::dispose_count = 0;
407 const char* c_source = "1 + 2 * 3";
408 {
409 v8::HandleScope scope;
410 LocalContext env;
411 Local<String> source =
412 String::NewExternal(new TestAsciiResource(i::StrDup(c_source)));
413 Local<Script> script = Script::Compile(source);
414 Local<Value> value = script->Run();
415 CHECK(value->IsNumber());
416 CHECK_EQ(7, value->Int32Value());
Steve Block8defd9f2010-07-08 12:39:36 +0100417 i::Heap::CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000418 CHECK_EQ(0, TestAsciiResource::dispose_count);
419 }
Steve Block8defd9f2010-07-08 12:39:36 +0100420 i::CompilationCache::Clear();
421 i::Heap::CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000422 CHECK_EQ(1, TestAsciiResource::dispose_count);
423}
424
425
426THREADED_TEST(ScriptMakingExternalString) {
427 TestResource::dispose_count = 0;
428 uint16_t* two_byte_source = AsciiToTwoByteString("1 + 2 * 3");
429 {
430 v8::HandleScope scope;
431 LocalContext env;
432 Local<String> source = String::New(two_byte_source);
Andrei Popescu402d9372010-02-26 13:31:12 +0000433 // Trigger GCs so that the newly allocated string moves to old gen.
Ben Murdochf87a2032010-10-22 12:50:53 +0100434 i::Heap::CollectGarbage(i::NEW_SPACE); // in survivor space now
435 i::Heap::CollectGarbage(i::NEW_SPACE); // in old gen now
Steve Blocka7e24c12009-10-30 11:49:00 +0000436 bool success = source->MakeExternal(new TestResource(two_byte_source));
437 CHECK(success);
438 Local<Script> script = Script::Compile(source);
439 Local<Value> value = script->Run();
440 CHECK(value->IsNumber());
441 CHECK_EQ(7, value->Int32Value());
Steve Block8defd9f2010-07-08 12:39:36 +0100442 i::Heap::CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000443 CHECK_EQ(0, TestResource::dispose_count);
444 }
Steve Block8defd9f2010-07-08 12:39:36 +0100445 i::CompilationCache::Clear();
446 i::Heap::CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000447 CHECK_EQ(1, TestResource::dispose_count);
448}
449
450
451THREADED_TEST(ScriptMakingExternalAsciiString) {
452 TestAsciiResource::dispose_count = 0;
453 const char* c_source = "1 + 2 * 3";
454 {
455 v8::HandleScope scope;
456 LocalContext env;
457 Local<String> source = v8_str(c_source);
Andrei Popescu402d9372010-02-26 13:31:12 +0000458 // Trigger GCs so that the newly allocated string moves to old gen.
Ben Murdochf87a2032010-10-22 12:50:53 +0100459 i::Heap::CollectGarbage(i::NEW_SPACE); // in survivor space now
460 i::Heap::CollectGarbage(i::NEW_SPACE); // in old gen now
Steve Blocka7e24c12009-10-30 11:49:00 +0000461 bool success = source->MakeExternal(
462 new TestAsciiResource(i::StrDup(c_source)));
463 CHECK(success);
464 Local<Script> script = Script::Compile(source);
465 Local<Value> value = script->Run();
466 CHECK(value->IsNumber());
467 CHECK_EQ(7, value->Int32Value());
Steve Block8defd9f2010-07-08 12:39:36 +0100468 i::Heap::CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000469 CHECK_EQ(0, TestAsciiResource::dispose_count);
470 }
Steve Block8defd9f2010-07-08 12:39:36 +0100471 i::CompilationCache::Clear();
472 i::Heap::CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000473 CHECK_EQ(1, TestAsciiResource::dispose_count);
474}
475
476
Andrei Popescu402d9372010-02-26 13:31:12 +0000477TEST(MakingExternalStringConditions) {
478 v8::HandleScope scope;
479 LocalContext env;
480
481 // Free some space in the new space so that we can check freshness.
Ben Murdochf87a2032010-10-22 12:50:53 +0100482 i::Heap::CollectGarbage(i::NEW_SPACE);
483 i::Heap::CollectGarbage(i::NEW_SPACE);
Andrei Popescu402d9372010-02-26 13:31:12 +0000484
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100485 uint16_t* two_byte_string = AsciiToTwoByteString("small");
486 Local<String> small_string = String::New(two_byte_string);
487 i::DeleteArray(two_byte_string);
488
Andrei Popescu402d9372010-02-26 13:31:12 +0000489 // We should refuse to externalize newly created small string.
490 CHECK(!small_string->CanMakeExternal());
491 // Trigger GCs so that the newly allocated string moves to old gen.
Ben Murdochf87a2032010-10-22 12:50:53 +0100492 i::Heap::CollectGarbage(i::NEW_SPACE); // in survivor space now
493 i::Heap::CollectGarbage(i::NEW_SPACE); // in old gen now
Andrei Popescu402d9372010-02-26 13:31:12 +0000494 // Old space strings should be accepted.
495 CHECK(small_string->CanMakeExternal());
496
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100497 two_byte_string = AsciiToTwoByteString("small 2");
498 small_string = String::New(two_byte_string);
499 i::DeleteArray(two_byte_string);
500
Andrei Popescu402d9372010-02-26 13:31:12 +0000501 // We should refuse externalizing newly created small string.
502 CHECK(!small_string->CanMakeExternal());
503 for (int i = 0; i < 100; i++) {
504 String::Value value(small_string);
505 }
506 // Frequently used strings should be accepted.
507 CHECK(small_string->CanMakeExternal());
508
509 const int buf_size = 10 * 1024;
510 char* buf = i::NewArray<char>(buf_size);
511 memset(buf, 'a', buf_size);
512 buf[buf_size - 1] = '\0';
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100513
514 two_byte_string = AsciiToTwoByteString(buf);
515 Local<String> large_string = String::New(two_byte_string);
Andrei Popescu402d9372010-02-26 13:31:12 +0000516 i::DeleteArray(buf);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100517 i::DeleteArray(two_byte_string);
Andrei Popescu402d9372010-02-26 13:31:12 +0000518 // Large strings should be immediately accepted.
519 CHECK(large_string->CanMakeExternal());
520}
521
522
523TEST(MakingExternalAsciiStringConditions) {
524 v8::HandleScope scope;
525 LocalContext env;
526
527 // Free some space in the new space so that we can check freshness.
Ben Murdochf87a2032010-10-22 12:50:53 +0100528 i::Heap::CollectGarbage(i::NEW_SPACE);
529 i::Heap::CollectGarbage(i::NEW_SPACE);
Andrei Popescu402d9372010-02-26 13:31:12 +0000530
531 Local<String> small_string = String::New("small");
532 // We should refuse to externalize newly created small string.
533 CHECK(!small_string->CanMakeExternal());
534 // Trigger GCs so that the newly allocated string moves to old gen.
Ben Murdochf87a2032010-10-22 12:50:53 +0100535 i::Heap::CollectGarbage(i::NEW_SPACE); // in survivor space now
536 i::Heap::CollectGarbage(i::NEW_SPACE); // in old gen now
Andrei Popescu402d9372010-02-26 13:31:12 +0000537 // Old space strings should be accepted.
538 CHECK(small_string->CanMakeExternal());
539
540 small_string = String::New("small 2");
541 // We should refuse externalizing newly created small string.
542 CHECK(!small_string->CanMakeExternal());
543 for (int i = 0; i < 100; i++) {
544 String::Value value(small_string);
545 }
546 // Frequently used strings should be accepted.
547 CHECK(small_string->CanMakeExternal());
548
549 const int buf_size = 10 * 1024;
550 char* buf = i::NewArray<char>(buf_size);
551 memset(buf, 'a', buf_size);
552 buf[buf_size - 1] = '\0';
553 Local<String> large_string = String::New(buf);
554 i::DeleteArray(buf);
555 // Large strings should be immediately accepted.
556 CHECK(large_string->CanMakeExternal());
557}
558
559
Steve Blocka7e24c12009-10-30 11:49:00 +0000560THREADED_TEST(UsingExternalString) {
561 {
562 v8::HandleScope scope;
563 uint16_t* two_byte_string = AsciiToTwoByteString("test string");
564 Local<String> string =
565 String::NewExternal(new TestResource(two_byte_string));
566 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
567 // Trigger GCs so that the newly allocated string moves to old gen.
Ben Murdochf87a2032010-10-22 12:50:53 +0100568 i::Heap::CollectGarbage(i::NEW_SPACE); // in survivor space now
569 i::Heap::CollectGarbage(i::NEW_SPACE); // in old gen now
Steve Blocka7e24c12009-10-30 11:49:00 +0000570 i::Handle<i::String> isymbol = i::Factory::SymbolFromString(istring);
571 CHECK(isymbol->IsSymbol());
572 }
573 i::Heap::CollectAllGarbage(false);
574 i::Heap::CollectAllGarbage(false);
575}
576
577
578THREADED_TEST(UsingExternalAsciiString) {
579 {
580 v8::HandleScope scope;
581 const char* one_byte_string = "test string";
582 Local<String> string = String::NewExternal(
583 new TestAsciiResource(i::StrDup(one_byte_string)));
584 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
585 // Trigger GCs so that the newly allocated string moves to old gen.
Ben Murdochf87a2032010-10-22 12:50:53 +0100586 i::Heap::CollectGarbage(i::NEW_SPACE); // in survivor space now
587 i::Heap::CollectGarbage(i::NEW_SPACE); // in old gen now
Steve Blocka7e24c12009-10-30 11:49:00 +0000588 i::Handle<i::String> isymbol = i::Factory::SymbolFromString(istring);
589 CHECK(isymbol->IsSymbol());
590 }
591 i::Heap::CollectAllGarbage(false);
592 i::Heap::CollectAllGarbage(false);
593}
594
595
Leon Clarkee46be812010-01-19 14:06:41 +0000596THREADED_TEST(ScavengeExternalString) {
597 TestResource::dispose_count = 0;
Steve Block6ded16b2010-05-10 14:33:55 +0100598 bool in_new_space = false;
Leon Clarkee46be812010-01-19 14:06:41 +0000599 {
600 v8::HandleScope scope;
601 uint16_t* two_byte_string = AsciiToTwoByteString("test string");
602 Local<String> string =
603 String::NewExternal(new TestResource(two_byte_string));
604 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
Ben Murdochf87a2032010-10-22 12:50:53 +0100605 i::Heap::CollectGarbage(i::NEW_SPACE);
Steve Block6ded16b2010-05-10 14:33:55 +0100606 in_new_space = i::Heap::InNewSpace(*istring);
607 CHECK(in_new_space || i::Heap::old_data_space()->Contains(*istring));
Leon Clarkee46be812010-01-19 14:06:41 +0000608 CHECK_EQ(0, TestResource::dispose_count);
609 }
Ben Murdochf87a2032010-10-22 12:50:53 +0100610 i::Heap::CollectGarbage(in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE);
Leon Clarkee46be812010-01-19 14:06:41 +0000611 CHECK_EQ(1, TestResource::dispose_count);
612}
613
614
615THREADED_TEST(ScavengeExternalAsciiString) {
616 TestAsciiResource::dispose_count = 0;
Steve Block6ded16b2010-05-10 14:33:55 +0100617 bool in_new_space = false;
Leon Clarkee46be812010-01-19 14:06:41 +0000618 {
619 v8::HandleScope scope;
620 const char* one_byte_string = "test string";
621 Local<String> string = String::NewExternal(
622 new TestAsciiResource(i::StrDup(one_byte_string)));
623 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
Ben Murdochf87a2032010-10-22 12:50:53 +0100624 i::Heap::CollectGarbage(i::NEW_SPACE);
Steve Block6ded16b2010-05-10 14:33:55 +0100625 in_new_space = i::Heap::InNewSpace(*istring);
626 CHECK(in_new_space || i::Heap::old_data_space()->Contains(*istring));
Leon Clarkee46be812010-01-19 14:06:41 +0000627 CHECK_EQ(0, TestAsciiResource::dispose_count);
628 }
Ben Murdochf87a2032010-10-22 12:50:53 +0100629 i::Heap::CollectGarbage(in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE);
Leon Clarkee46be812010-01-19 14:06:41 +0000630 CHECK_EQ(1, TestAsciiResource::dispose_count);
631}
632
633
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100634class TestAsciiResourceWithDisposeControl: public TestAsciiResource {
635 public:
636 static int dispose_calls;
637
638 TestAsciiResourceWithDisposeControl(const char* data, bool dispose)
639 : TestAsciiResource(data),
640 dispose_(dispose) { }
641
642 void Dispose() {
643 ++dispose_calls;
644 if (dispose_) delete this;
645 }
646 private:
647 bool dispose_;
648};
649
650
651int TestAsciiResourceWithDisposeControl::dispose_calls = 0;
652
653
654TEST(ExternalStringWithDisposeHandling) {
655 const char* c_source = "1 + 2 * 3";
656
657 // Use a stack allocated external string resource allocated object.
658 TestAsciiResource::dispose_count = 0;
659 TestAsciiResourceWithDisposeControl::dispose_calls = 0;
660 TestAsciiResourceWithDisposeControl res_stack(i::StrDup(c_source), false);
661 {
662 v8::HandleScope scope;
663 LocalContext env;
664 Local<String> source = String::NewExternal(&res_stack);
665 Local<Script> script = Script::Compile(source);
666 Local<Value> value = script->Run();
667 CHECK(value->IsNumber());
668 CHECK_EQ(7, value->Int32Value());
Steve Block8defd9f2010-07-08 12:39:36 +0100669 i::Heap::CollectAllGarbage(false);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100670 CHECK_EQ(0, TestAsciiResource::dispose_count);
671 }
Steve Block8defd9f2010-07-08 12:39:36 +0100672 i::CompilationCache::Clear();
673 i::Heap::CollectAllGarbage(false);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100674 CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls);
675 CHECK_EQ(0, TestAsciiResource::dispose_count);
676
677 // Use a heap allocated external string resource allocated object.
678 TestAsciiResource::dispose_count = 0;
679 TestAsciiResourceWithDisposeControl::dispose_calls = 0;
680 TestAsciiResource* res_heap =
681 new TestAsciiResourceWithDisposeControl(i::StrDup(c_source), true);
682 {
683 v8::HandleScope scope;
684 LocalContext env;
685 Local<String> source = String::NewExternal(res_heap);
686 Local<Script> script = Script::Compile(source);
687 Local<Value> value = script->Run();
688 CHECK(value->IsNumber());
689 CHECK_EQ(7, value->Int32Value());
Steve Block8defd9f2010-07-08 12:39:36 +0100690 i::Heap::CollectAllGarbage(false);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100691 CHECK_EQ(0, TestAsciiResource::dispose_count);
692 }
Steve Block8defd9f2010-07-08 12:39:36 +0100693 i::CompilationCache::Clear();
694 i::Heap::CollectAllGarbage(false);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100695 CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls);
696 CHECK_EQ(1, TestAsciiResource::dispose_count);
697}
698
699
Steve Block3ce2e202009-11-05 08:53:23 +0000700THREADED_TEST(StringConcat) {
701 {
702 v8::HandleScope scope;
703 LocalContext env;
704 const char* one_byte_string_1 = "function a_times_t";
705 const char* two_byte_string_1 = "wo_plus_b(a, b) {return ";
706 const char* one_byte_extern_1 = "a * 2 + b;} a_times_two_plus_b(4, 8) + ";
707 const char* two_byte_extern_1 = "a_times_two_plus_b(4, 8) + ";
708 const char* one_byte_string_2 = "a_times_two_plus_b(4, 8) + ";
709 const char* two_byte_string_2 = "a_times_two_plus_b(4, 8) + ";
710 const char* two_byte_extern_2 = "a_times_two_plus_b(1, 2);";
711 Local<String> left = v8_str(one_byte_string_1);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100712
713 uint16_t* two_byte_source = AsciiToTwoByteString(two_byte_string_1);
714 Local<String> right = String::New(two_byte_source);
715 i::DeleteArray(two_byte_source);
716
Steve Block3ce2e202009-11-05 08:53:23 +0000717 Local<String> source = String::Concat(left, right);
718 right = String::NewExternal(
719 new TestAsciiResource(i::StrDup(one_byte_extern_1)));
720 source = String::Concat(source, right);
721 right = String::NewExternal(
722 new TestResource(AsciiToTwoByteString(two_byte_extern_1)));
723 source = String::Concat(source, right);
724 right = v8_str(one_byte_string_2);
725 source = String::Concat(source, right);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100726
727 two_byte_source = AsciiToTwoByteString(two_byte_string_2);
728 right = String::New(two_byte_source);
729 i::DeleteArray(two_byte_source);
730
Steve Block3ce2e202009-11-05 08:53:23 +0000731 source = String::Concat(source, right);
732 right = String::NewExternal(
733 new TestResource(AsciiToTwoByteString(two_byte_extern_2)));
734 source = String::Concat(source, right);
735 Local<Script> script = Script::Compile(source);
736 Local<Value> value = script->Run();
737 CHECK(value->IsNumber());
738 CHECK_EQ(68, value->Int32Value());
739 }
Steve Block8defd9f2010-07-08 12:39:36 +0100740 i::CompilationCache::Clear();
Steve Block3ce2e202009-11-05 08:53:23 +0000741 i::Heap::CollectAllGarbage(false);
742 i::Heap::CollectAllGarbage(false);
743}
744
745
Steve Blocka7e24c12009-10-30 11:49:00 +0000746THREADED_TEST(GlobalProperties) {
747 v8::HandleScope scope;
748 LocalContext env;
749 v8::Handle<v8::Object> global = env->Global();
750 global->Set(v8_str("pi"), v8_num(3.1415926));
751 Local<Value> pi = global->Get(v8_str("pi"));
752 CHECK_EQ(3.1415926, pi->NumberValue());
753}
754
755
756static v8::Handle<Value> handle_call(const v8::Arguments& args) {
757 ApiTestFuzzer::Fuzz();
758 return v8_num(102);
759}
760
761
762static v8::Handle<Value> construct_call(const v8::Arguments& args) {
763 ApiTestFuzzer::Fuzz();
764 args.This()->Set(v8_str("x"), v8_num(1));
765 args.This()->Set(v8_str("y"), v8_num(2));
766 return args.This();
767}
768
Ben Murdochf87a2032010-10-22 12:50:53 +0100769static v8::Handle<Value> Return239(Local<String> name, const AccessorInfo&) {
770 ApiTestFuzzer::Fuzz();
771 return v8_num(239);
772}
773
774
Steve Blocka7e24c12009-10-30 11:49:00 +0000775THREADED_TEST(FunctionTemplate) {
776 v8::HandleScope scope;
777 LocalContext env;
778 {
779 Local<v8::FunctionTemplate> fun_templ =
780 v8::FunctionTemplate::New(handle_call);
781 Local<Function> fun = fun_templ->GetFunction();
782 env->Global()->Set(v8_str("obj"), fun);
783 Local<Script> script = v8_compile("obj()");
784 CHECK_EQ(102, script->Run()->Int32Value());
785 }
786 // Use SetCallHandler to initialize a function template, should work like the
787 // previous one.
788 {
789 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
790 fun_templ->SetCallHandler(handle_call);
791 Local<Function> fun = fun_templ->GetFunction();
792 env->Global()->Set(v8_str("obj"), fun);
793 Local<Script> script = v8_compile("obj()");
794 CHECK_EQ(102, script->Run()->Int32Value());
795 }
796 // Test constructor calls.
797 {
798 Local<v8::FunctionTemplate> fun_templ =
799 v8::FunctionTemplate::New(construct_call);
800 fun_templ->SetClassName(v8_str("funky"));
Ben Murdochf87a2032010-10-22 12:50:53 +0100801 fun_templ->InstanceTemplate()->SetAccessor(v8_str("m"), Return239);
Steve Blocka7e24c12009-10-30 11:49:00 +0000802 Local<Function> fun = fun_templ->GetFunction();
803 env->Global()->Set(v8_str("obj"), fun);
804 Local<Script> script = v8_compile("var s = new obj(); s.x");
805 CHECK_EQ(1, script->Run()->Int32Value());
806
807 Local<Value> result = v8_compile("(new obj()).toString()")->Run();
808 CHECK_EQ(v8_str("[object funky]"), result);
Ben Murdochf87a2032010-10-22 12:50:53 +0100809
810 result = v8_compile("(new obj()).m")->Run();
811 CHECK_EQ(239, result->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +0000812 }
813}
814
815
816THREADED_TEST(FindInstanceInPrototypeChain) {
817 v8::HandleScope scope;
818 LocalContext env;
819
820 Local<v8::FunctionTemplate> base = v8::FunctionTemplate::New();
821 Local<v8::FunctionTemplate> derived = v8::FunctionTemplate::New();
822 Local<v8::FunctionTemplate> other = v8::FunctionTemplate::New();
823 derived->Inherit(base);
824
825 Local<v8::Function> base_function = base->GetFunction();
826 Local<v8::Function> derived_function = derived->GetFunction();
827 Local<v8::Function> other_function = other->GetFunction();
828
829 Local<v8::Object> base_instance = base_function->NewInstance();
830 Local<v8::Object> derived_instance = derived_function->NewInstance();
831 Local<v8::Object> derived_instance2 = derived_function->NewInstance();
832 Local<v8::Object> other_instance = other_function->NewInstance();
833 derived_instance2->Set(v8_str("__proto__"), derived_instance);
834 other_instance->Set(v8_str("__proto__"), derived_instance2);
835
836 // base_instance is only an instance of base.
837 CHECK_EQ(base_instance,
838 base_instance->FindInstanceInPrototypeChain(base));
839 CHECK(base_instance->FindInstanceInPrototypeChain(derived).IsEmpty());
840 CHECK(base_instance->FindInstanceInPrototypeChain(other).IsEmpty());
841
842 // derived_instance is an instance of base and derived.
843 CHECK_EQ(derived_instance,
844 derived_instance->FindInstanceInPrototypeChain(base));
845 CHECK_EQ(derived_instance,
846 derived_instance->FindInstanceInPrototypeChain(derived));
847 CHECK(derived_instance->FindInstanceInPrototypeChain(other).IsEmpty());
848
849 // other_instance is an instance of other and its immediate
850 // prototype derived_instance2 is an instance of base and derived.
851 // Note, derived_instance is an instance of base and derived too,
852 // but it comes after derived_instance2 in the prototype chain of
853 // other_instance.
854 CHECK_EQ(derived_instance2,
855 other_instance->FindInstanceInPrototypeChain(base));
856 CHECK_EQ(derived_instance2,
857 other_instance->FindInstanceInPrototypeChain(derived));
858 CHECK_EQ(other_instance,
859 other_instance->FindInstanceInPrototypeChain(other));
860}
861
862
Steve Block3ce2e202009-11-05 08:53:23 +0000863THREADED_TEST(TinyInteger) {
864 v8::HandleScope scope;
865 LocalContext env;
866 int32_t value = 239;
867 Local<v8::Integer> value_obj = v8::Integer::New(value);
868 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
869}
870
871
872THREADED_TEST(BigSmiInteger) {
873 v8::HandleScope scope;
874 LocalContext env;
875 int32_t value = i::Smi::kMaxValue;
876 // We cannot add one to a Smi::kMaxValue without wrapping.
877 if (i::kSmiValueSize < 32) {
878 CHECK(i::Smi::IsValid(value));
879 CHECK(!i::Smi::IsValid(value + 1));
880 Local<v8::Integer> value_obj = v8::Integer::New(value);
881 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
882 }
883}
884
885
886THREADED_TEST(BigInteger) {
887 v8::HandleScope scope;
888 LocalContext env;
889 // We cannot add one to a Smi::kMaxValue without wrapping.
890 if (i::kSmiValueSize < 32) {
891 // The casts allow this to compile, even if Smi::kMaxValue is 2^31-1.
892 // The code will not be run in that case, due to the "if" guard.
893 int32_t value =
894 static_cast<int32_t>(static_cast<uint32_t>(i::Smi::kMaxValue) + 1);
895 CHECK(value > i::Smi::kMaxValue);
896 CHECK(!i::Smi::IsValid(value));
897 Local<v8::Integer> value_obj = v8::Integer::New(value);
898 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
899 }
900}
901
902
903THREADED_TEST(TinyUnsignedInteger) {
904 v8::HandleScope scope;
905 LocalContext env;
906 uint32_t value = 239;
907 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
908 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
909}
910
911
912THREADED_TEST(BigUnsignedSmiInteger) {
913 v8::HandleScope scope;
914 LocalContext env;
915 uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue);
916 CHECK(i::Smi::IsValid(value));
917 CHECK(!i::Smi::IsValid(value + 1));
918 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
919 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
920}
921
922
923THREADED_TEST(BigUnsignedInteger) {
924 v8::HandleScope scope;
925 LocalContext env;
926 uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue) + 1;
927 CHECK(value > static_cast<uint32_t>(i::Smi::kMaxValue));
928 CHECK(!i::Smi::IsValid(value));
929 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
930 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
931}
932
933
934THREADED_TEST(OutOfSignedRangeUnsignedInteger) {
935 v8::HandleScope scope;
936 LocalContext env;
937 uint32_t INT32_MAX_AS_UINT = (1U << 31) - 1;
938 uint32_t value = INT32_MAX_AS_UINT + 1;
939 CHECK(value > INT32_MAX_AS_UINT); // No overflow.
940 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
941 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
942}
943
944
Steve Blocka7e24c12009-10-30 11:49:00 +0000945THREADED_TEST(Number) {
946 v8::HandleScope scope;
947 LocalContext env;
948 double PI = 3.1415926;
949 Local<v8::Number> pi_obj = v8::Number::New(PI);
950 CHECK_EQ(PI, pi_obj->NumberValue());
951}
952
953
954THREADED_TEST(ToNumber) {
955 v8::HandleScope scope;
956 LocalContext env;
957 Local<String> str = v8_str("3.1415926");
958 CHECK_EQ(3.1415926, str->NumberValue());
959 v8::Handle<v8::Boolean> t = v8::True();
960 CHECK_EQ(1.0, t->NumberValue());
961 v8::Handle<v8::Boolean> f = v8::False();
962 CHECK_EQ(0.0, f->NumberValue());
963}
964
965
966THREADED_TEST(Date) {
967 v8::HandleScope scope;
968 LocalContext env;
969 double PI = 3.1415926;
970 Local<Value> date_obj = v8::Date::New(PI);
971 CHECK_EQ(3.0, date_obj->NumberValue());
972}
973
974
975THREADED_TEST(Boolean) {
976 v8::HandleScope scope;
977 LocalContext env;
978 v8::Handle<v8::Boolean> t = v8::True();
979 CHECK(t->Value());
980 v8::Handle<v8::Boolean> f = v8::False();
981 CHECK(!f->Value());
982 v8::Handle<v8::Primitive> u = v8::Undefined();
983 CHECK(!u->BooleanValue());
984 v8::Handle<v8::Primitive> n = v8::Null();
985 CHECK(!n->BooleanValue());
986 v8::Handle<String> str1 = v8_str("");
987 CHECK(!str1->BooleanValue());
988 v8::Handle<String> str2 = v8_str("x");
989 CHECK(str2->BooleanValue());
990 CHECK(!v8::Number::New(0)->BooleanValue());
991 CHECK(v8::Number::New(-1)->BooleanValue());
992 CHECK(v8::Number::New(1)->BooleanValue());
993 CHECK(v8::Number::New(42)->BooleanValue());
994 CHECK(!v8_compile("NaN")->Run()->BooleanValue());
995}
996
997
998static v8::Handle<Value> DummyCallHandler(const v8::Arguments& args) {
999 ApiTestFuzzer::Fuzz();
1000 return v8_num(13.4);
1001}
1002
1003
1004static v8::Handle<Value> GetM(Local<String> name, const AccessorInfo&) {
1005 ApiTestFuzzer::Fuzz();
1006 return v8_num(876);
1007}
1008
1009
1010THREADED_TEST(GlobalPrototype) {
1011 v8::HandleScope scope;
1012 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New();
1013 func_templ->PrototypeTemplate()->Set(
1014 "dummy",
1015 v8::FunctionTemplate::New(DummyCallHandler));
1016 v8::Handle<ObjectTemplate> templ = func_templ->InstanceTemplate();
1017 templ->Set("x", v8_num(200));
1018 templ->SetAccessor(v8_str("m"), GetM);
1019 LocalContext env(0, templ);
1020 v8::Handle<v8::Object> obj = env->Global();
1021 v8::Handle<Script> script = v8_compile("dummy()");
1022 v8::Handle<Value> result = script->Run();
1023 CHECK_EQ(13.4, result->NumberValue());
1024 CHECK_EQ(200, v8_compile("x")->Run()->Int32Value());
1025 CHECK_EQ(876, v8_compile("m")->Run()->Int32Value());
1026}
1027
1028
Steve Blocka7e24c12009-10-30 11:49:00 +00001029THREADED_TEST(ObjectTemplate) {
1030 v8::HandleScope scope;
1031 Local<ObjectTemplate> templ1 = ObjectTemplate::New();
1032 templ1->Set("x", v8_num(10));
1033 templ1->Set("y", v8_num(13));
1034 LocalContext env;
1035 Local<v8::Object> instance1 = templ1->NewInstance();
1036 env->Global()->Set(v8_str("p"), instance1);
1037 CHECK(v8_compile("(p.x == 10)")->Run()->BooleanValue());
1038 CHECK(v8_compile("(p.y == 13)")->Run()->BooleanValue());
1039 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New();
1040 fun->PrototypeTemplate()->Set("nirk", v8_num(123));
1041 Local<ObjectTemplate> templ2 = fun->InstanceTemplate();
1042 templ2->Set("a", v8_num(12));
1043 templ2->Set("b", templ1);
1044 Local<v8::Object> instance2 = templ2->NewInstance();
1045 env->Global()->Set(v8_str("q"), instance2);
1046 CHECK(v8_compile("(q.nirk == 123)")->Run()->BooleanValue());
1047 CHECK(v8_compile("(q.a == 12)")->Run()->BooleanValue());
1048 CHECK(v8_compile("(q.b.x == 10)")->Run()->BooleanValue());
1049 CHECK(v8_compile("(q.b.y == 13)")->Run()->BooleanValue());
1050}
1051
1052
1053static v8::Handle<Value> GetFlabby(const v8::Arguments& args) {
1054 ApiTestFuzzer::Fuzz();
1055 return v8_num(17.2);
1056}
1057
1058
1059static v8::Handle<Value> GetKnurd(Local<String> property, const AccessorInfo&) {
1060 ApiTestFuzzer::Fuzz();
1061 return v8_num(15.2);
1062}
1063
1064
1065THREADED_TEST(DescriptorInheritance) {
1066 v8::HandleScope scope;
1067 v8::Handle<v8::FunctionTemplate> super = v8::FunctionTemplate::New();
1068 super->PrototypeTemplate()->Set("flabby",
1069 v8::FunctionTemplate::New(GetFlabby));
1070 super->PrototypeTemplate()->Set("PI", v8_num(3.14));
1071
1072 super->InstanceTemplate()->SetAccessor(v8_str("knurd"), GetKnurd);
1073
1074 v8::Handle<v8::FunctionTemplate> base1 = v8::FunctionTemplate::New();
1075 base1->Inherit(super);
1076 base1->PrototypeTemplate()->Set("v1", v8_num(20.1));
1077
1078 v8::Handle<v8::FunctionTemplate> base2 = v8::FunctionTemplate::New();
1079 base2->Inherit(super);
1080 base2->PrototypeTemplate()->Set("v2", v8_num(10.1));
1081
1082 LocalContext env;
1083
1084 env->Global()->Set(v8_str("s"), super->GetFunction());
1085 env->Global()->Set(v8_str("base1"), base1->GetFunction());
1086 env->Global()->Set(v8_str("base2"), base2->GetFunction());
1087
1088 // Checks right __proto__ chain.
1089 CHECK(CompileRun("base1.prototype.__proto__ == s.prototype")->BooleanValue());
1090 CHECK(CompileRun("base2.prototype.__proto__ == s.prototype")->BooleanValue());
1091
1092 CHECK(v8_compile("s.prototype.PI == 3.14")->Run()->BooleanValue());
1093
1094 // Instance accessor should not be visible on function object or its prototype
1095 CHECK(CompileRun("s.knurd == undefined")->BooleanValue());
1096 CHECK(CompileRun("s.prototype.knurd == undefined")->BooleanValue());
1097 CHECK(CompileRun("base1.prototype.knurd == undefined")->BooleanValue());
1098
1099 env->Global()->Set(v8_str("obj"),
1100 base1->GetFunction()->NewInstance());
1101 CHECK_EQ(17.2, v8_compile("obj.flabby()")->Run()->NumberValue());
1102 CHECK(v8_compile("'flabby' in obj")->Run()->BooleanValue());
1103 CHECK_EQ(15.2, v8_compile("obj.knurd")->Run()->NumberValue());
1104 CHECK(v8_compile("'knurd' in obj")->Run()->BooleanValue());
1105 CHECK_EQ(20.1, v8_compile("obj.v1")->Run()->NumberValue());
1106
1107 env->Global()->Set(v8_str("obj2"),
1108 base2->GetFunction()->NewInstance());
1109 CHECK_EQ(17.2, v8_compile("obj2.flabby()")->Run()->NumberValue());
1110 CHECK(v8_compile("'flabby' in obj2")->Run()->BooleanValue());
1111 CHECK_EQ(15.2, v8_compile("obj2.knurd")->Run()->NumberValue());
1112 CHECK(v8_compile("'knurd' in obj2")->Run()->BooleanValue());
1113 CHECK_EQ(10.1, v8_compile("obj2.v2")->Run()->NumberValue());
1114
1115 // base1 and base2 cannot cross reference to each's prototype
1116 CHECK(v8_compile("obj.v2")->Run()->IsUndefined());
1117 CHECK(v8_compile("obj2.v1")->Run()->IsUndefined());
1118}
1119
1120
1121int echo_named_call_count;
1122
1123
1124static v8::Handle<Value> EchoNamedProperty(Local<String> name,
1125 const AccessorInfo& info) {
1126 ApiTestFuzzer::Fuzz();
1127 CHECK_EQ(v8_str("data"), info.Data());
1128 echo_named_call_count++;
1129 return name;
1130}
1131
1132
1133THREADED_TEST(NamedPropertyHandlerGetter) {
1134 echo_named_call_count = 0;
1135 v8::HandleScope scope;
1136 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1137 templ->InstanceTemplate()->SetNamedPropertyHandler(EchoNamedProperty,
1138 0, 0, 0, 0,
1139 v8_str("data"));
1140 LocalContext env;
1141 env->Global()->Set(v8_str("obj"),
1142 templ->GetFunction()->NewInstance());
1143 CHECK_EQ(echo_named_call_count, 0);
1144 v8_compile("obj.x")->Run();
1145 CHECK_EQ(echo_named_call_count, 1);
1146 const char* code = "var str = 'oddle'; obj[str] + obj.poddle;";
1147 v8::Handle<Value> str = CompileRun(code);
1148 String::AsciiValue value(str);
1149 CHECK_EQ(*value, "oddlepoddle");
1150 // Check default behavior
1151 CHECK_EQ(v8_compile("obj.flob = 10;")->Run()->Int32Value(), 10);
1152 CHECK(v8_compile("'myProperty' in obj")->Run()->BooleanValue());
1153 CHECK(v8_compile("delete obj.myProperty")->Run()->BooleanValue());
1154}
1155
1156
1157int echo_indexed_call_count = 0;
1158
1159
1160static v8::Handle<Value> EchoIndexedProperty(uint32_t index,
1161 const AccessorInfo& info) {
1162 ApiTestFuzzer::Fuzz();
1163 CHECK_EQ(v8_num(637), info.Data());
1164 echo_indexed_call_count++;
1165 return v8_num(index);
1166}
1167
1168
1169THREADED_TEST(IndexedPropertyHandlerGetter) {
1170 v8::HandleScope scope;
1171 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1172 templ->InstanceTemplate()->SetIndexedPropertyHandler(EchoIndexedProperty,
1173 0, 0, 0, 0,
1174 v8_num(637));
1175 LocalContext env;
1176 env->Global()->Set(v8_str("obj"),
1177 templ->GetFunction()->NewInstance());
1178 Local<Script> script = v8_compile("obj[900]");
1179 CHECK_EQ(script->Run()->Int32Value(), 900);
1180}
1181
1182
1183v8::Handle<v8::Object> bottom;
1184
1185static v8::Handle<Value> CheckThisIndexedPropertyHandler(
1186 uint32_t index,
1187 const AccessorInfo& info) {
1188 ApiTestFuzzer::Fuzz();
1189 CHECK(info.This()->Equals(bottom));
1190 return v8::Handle<Value>();
1191}
1192
1193static v8::Handle<Value> CheckThisNamedPropertyHandler(
1194 Local<String> name,
1195 const AccessorInfo& info) {
1196 ApiTestFuzzer::Fuzz();
1197 CHECK(info.This()->Equals(bottom));
1198 return v8::Handle<Value>();
1199}
1200
1201
1202v8::Handle<Value> CheckThisIndexedPropertySetter(uint32_t index,
1203 Local<Value> value,
1204 const AccessorInfo& info) {
1205 ApiTestFuzzer::Fuzz();
1206 CHECK(info.This()->Equals(bottom));
1207 return v8::Handle<Value>();
1208}
1209
1210
1211v8::Handle<Value> CheckThisNamedPropertySetter(Local<String> property,
1212 Local<Value> value,
1213 const AccessorInfo& info) {
1214 ApiTestFuzzer::Fuzz();
1215 CHECK(info.This()->Equals(bottom));
1216 return v8::Handle<Value>();
1217}
1218
Iain Merrick75681382010-08-19 15:07:18 +01001219v8::Handle<v8::Integer> CheckThisIndexedPropertyQuery(
Steve Blocka7e24c12009-10-30 11:49:00 +00001220 uint32_t index,
1221 const AccessorInfo& info) {
1222 ApiTestFuzzer::Fuzz();
1223 CHECK(info.This()->Equals(bottom));
Iain Merrick75681382010-08-19 15:07:18 +01001224 return v8::Handle<v8::Integer>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001225}
1226
1227
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001228v8::Handle<v8::Integer> CheckThisNamedPropertyQuery(Local<String> property,
Steve Blocka7e24c12009-10-30 11:49:00 +00001229 const AccessorInfo& info) {
1230 ApiTestFuzzer::Fuzz();
1231 CHECK(info.This()->Equals(bottom));
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001232 return v8::Handle<v8::Integer>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001233}
1234
1235
1236v8::Handle<v8::Boolean> CheckThisIndexedPropertyDeleter(
1237 uint32_t index,
1238 const AccessorInfo& info) {
1239 ApiTestFuzzer::Fuzz();
1240 CHECK(info.This()->Equals(bottom));
1241 return v8::Handle<v8::Boolean>();
1242}
1243
1244
1245v8::Handle<v8::Boolean> CheckThisNamedPropertyDeleter(
1246 Local<String> property,
1247 const AccessorInfo& info) {
1248 ApiTestFuzzer::Fuzz();
1249 CHECK(info.This()->Equals(bottom));
1250 return v8::Handle<v8::Boolean>();
1251}
1252
1253
1254v8::Handle<v8::Array> CheckThisIndexedPropertyEnumerator(
1255 const AccessorInfo& info) {
1256 ApiTestFuzzer::Fuzz();
1257 CHECK(info.This()->Equals(bottom));
1258 return v8::Handle<v8::Array>();
1259}
1260
1261
1262v8::Handle<v8::Array> CheckThisNamedPropertyEnumerator(
1263 const AccessorInfo& info) {
1264 ApiTestFuzzer::Fuzz();
1265 CHECK(info.This()->Equals(bottom));
1266 return v8::Handle<v8::Array>();
1267}
1268
1269
1270THREADED_TEST(PropertyHandlerInPrototype) {
1271 v8::HandleScope scope;
1272 LocalContext env;
1273
1274 // Set up a prototype chain with three interceptors.
1275 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1276 templ->InstanceTemplate()->SetIndexedPropertyHandler(
1277 CheckThisIndexedPropertyHandler,
1278 CheckThisIndexedPropertySetter,
1279 CheckThisIndexedPropertyQuery,
1280 CheckThisIndexedPropertyDeleter,
1281 CheckThisIndexedPropertyEnumerator);
1282
1283 templ->InstanceTemplate()->SetNamedPropertyHandler(
1284 CheckThisNamedPropertyHandler,
1285 CheckThisNamedPropertySetter,
1286 CheckThisNamedPropertyQuery,
1287 CheckThisNamedPropertyDeleter,
1288 CheckThisNamedPropertyEnumerator);
1289
1290 bottom = templ->GetFunction()->NewInstance();
1291 Local<v8::Object> top = templ->GetFunction()->NewInstance();
1292 Local<v8::Object> middle = templ->GetFunction()->NewInstance();
1293
1294 bottom->Set(v8_str("__proto__"), middle);
1295 middle->Set(v8_str("__proto__"), top);
1296 env->Global()->Set(v8_str("obj"), bottom);
1297
1298 // Indexed and named get.
1299 Script::Compile(v8_str("obj[0]"))->Run();
1300 Script::Compile(v8_str("obj.x"))->Run();
1301
1302 // Indexed and named set.
1303 Script::Compile(v8_str("obj[1] = 42"))->Run();
1304 Script::Compile(v8_str("obj.y = 42"))->Run();
1305
1306 // Indexed and named query.
1307 Script::Compile(v8_str("0 in obj"))->Run();
1308 Script::Compile(v8_str("'x' in obj"))->Run();
1309
1310 // Indexed and named deleter.
1311 Script::Compile(v8_str("delete obj[0]"))->Run();
1312 Script::Compile(v8_str("delete obj.x"))->Run();
1313
1314 // Enumerators.
1315 Script::Compile(v8_str("for (var p in obj) ;"))->Run();
1316}
1317
1318
1319static v8::Handle<Value> PrePropertyHandlerGet(Local<String> key,
1320 const AccessorInfo& info) {
1321 ApiTestFuzzer::Fuzz();
1322 if (v8_str("pre")->Equals(key)) {
1323 return v8_str("PrePropertyHandler: pre");
1324 }
1325 return v8::Handle<String>();
1326}
1327
1328
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001329static v8::Handle<v8::Integer> PrePropertyHandlerQuery(Local<String> key,
1330 const AccessorInfo&) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001331 if (v8_str("pre")->Equals(key)) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001332 return v8::Integer::New(v8::None);
Steve Blocka7e24c12009-10-30 11:49:00 +00001333 }
1334
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001335 return v8::Handle<v8::Integer>(); // do not intercept the call
Steve Blocka7e24c12009-10-30 11:49:00 +00001336}
1337
1338
1339THREADED_TEST(PrePropertyHandler) {
1340 v8::HandleScope scope;
1341 v8::Handle<v8::FunctionTemplate> desc = v8::FunctionTemplate::New();
1342 desc->InstanceTemplate()->SetNamedPropertyHandler(PrePropertyHandlerGet,
1343 0,
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001344 PrePropertyHandlerQuery);
Steve Blocka7e24c12009-10-30 11:49:00 +00001345 LocalContext env(NULL, desc->InstanceTemplate());
1346 Script::Compile(v8_str(
1347 "var pre = 'Object: pre'; var on = 'Object: on';"))->Run();
1348 v8::Handle<Value> result_pre = Script::Compile(v8_str("pre"))->Run();
1349 CHECK_EQ(v8_str("PrePropertyHandler: pre"), result_pre);
1350 v8::Handle<Value> result_on = Script::Compile(v8_str("on"))->Run();
1351 CHECK_EQ(v8_str("Object: on"), result_on);
1352 v8::Handle<Value> result_post = Script::Compile(v8_str("post"))->Run();
1353 CHECK(result_post.IsEmpty());
1354}
1355
1356
1357THREADED_TEST(UndefinedIsNotEnumerable) {
1358 v8::HandleScope scope;
1359 LocalContext env;
1360 v8::Handle<Value> result = Script::Compile(v8_str(
1361 "this.propertyIsEnumerable(undefined)"))->Run();
1362 CHECK(result->IsFalse());
1363}
1364
1365
1366v8::Handle<Script> call_recursively_script;
Leon Clarke4515c472010-02-03 11:58:03 +00001367static const int kTargetRecursionDepth = 200; // near maximum
Steve Blocka7e24c12009-10-30 11:49:00 +00001368
1369
1370static v8::Handle<Value> CallScriptRecursivelyCall(const v8::Arguments& args) {
1371 ApiTestFuzzer::Fuzz();
1372 int depth = args.This()->Get(v8_str("depth"))->Int32Value();
1373 if (depth == kTargetRecursionDepth) return v8::Undefined();
1374 args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1));
1375 return call_recursively_script->Run();
1376}
1377
1378
1379static v8::Handle<Value> CallFunctionRecursivelyCall(
1380 const v8::Arguments& args) {
1381 ApiTestFuzzer::Fuzz();
1382 int depth = args.This()->Get(v8_str("depth"))->Int32Value();
1383 if (depth == kTargetRecursionDepth) {
1384 printf("[depth = %d]\n", depth);
1385 return v8::Undefined();
1386 }
1387 args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1));
1388 v8::Handle<Value> function =
1389 args.This()->Get(v8_str("callFunctionRecursively"));
Steve Block6ded16b2010-05-10 14:33:55 +01001390 return function.As<Function>()->Call(args.This(), 0, NULL);
Steve Blocka7e24c12009-10-30 11:49:00 +00001391}
1392
1393
1394THREADED_TEST(DeepCrossLanguageRecursion) {
1395 v8::HandleScope scope;
1396 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New();
1397 global->Set(v8_str("callScriptRecursively"),
1398 v8::FunctionTemplate::New(CallScriptRecursivelyCall));
1399 global->Set(v8_str("callFunctionRecursively"),
1400 v8::FunctionTemplate::New(CallFunctionRecursivelyCall));
1401 LocalContext env(NULL, global);
1402
1403 env->Global()->Set(v8_str("depth"), v8::Integer::New(0));
1404 call_recursively_script = v8_compile("callScriptRecursively()");
1405 v8::Handle<Value> result = call_recursively_script->Run();
1406 call_recursively_script = v8::Handle<Script>();
1407
1408 env->Global()->Set(v8_str("depth"), v8::Integer::New(0));
1409 Script::Compile(v8_str("callFunctionRecursively()"))->Run();
1410}
1411
1412
1413static v8::Handle<Value>
1414 ThrowingPropertyHandlerGet(Local<String> key, const AccessorInfo&) {
1415 ApiTestFuzzer::Fuzz();
1416 return v8::ThrowException(key);
1417}
1418
1419
1420static v8::Handle<Value> ThrowingPropertyHandlerSet(Local<String> key,
1421 Local<Value>,
1422 const AccessorInfo&) {
1423 v8::ThrowException(key);
1424 return v8::Undefined(); // not the same as v8::Handle<v8::Value>()
1425}
1426
1427
1428THREADED_TEST(CallbackExceptionRegression) {
1429 v8::HandleScope scope;
1430 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
1431 obj->SetNamedPropertyHandler(ThrowingPropertyHandlerGet,
1432 ThrowingPropertyHandlerSet);
1433 LocalContext env;
1434 env->Global()->Set(v8_str("obj"), obj->NewInstance());
1435 v8::Handle<Value> otto = Script::Compile(v8_str(
1436 "try { with (obj) { otto; } } catch (e) { e; }"))->Run();
1437 CHECK_EQ(v8_str("otto"), otto);
1438 v8::Handle<Value> netto = Script::Compile(v8_str(
1439 "try { with (obj) { netto = 4; } } catch (e) { e; }"))->Run();
1440 CHECK_EQ(v8_str("netto"), netto);
1441}
1442
1443
Steve Blocka7e24c12009-10-30 11:49:00 +00001444THREADED_TEST(FunctionPrototype) {
1445 v8::HandleScope scope;
1446 Local<v8::FunctionTemplate> Foo = v8::FunctionTemplate::New();
1447 Foo->PrototypeTemplate()->Set(v8_str("plak"), v8_num(321));
1448 LocalContext env;
1449 env->Global()->Set(v8_str("Foo"), Foo->GetFunction());
1450 Local<Script> script = Script::Compile(v8_str("Foo.prototype.plak"));
1451 CHECK_EQ(script->Run()->Int32Value(), 321);
1452}
1453
1454
1455THREADED_TEST(InternalFields) {
1456 v8::HandleScope scope;
1457 LocalContext env;
1458
1459 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1460 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate();
1461 instance_templ->SetInternalFieldCount(1);
1462 Local<v8::Object> obj = templ->GetFunction()->NewInstance();
1463 CHECK_EQ(1, obj->InternalFieldCount());
1464 CHECK(obj->GetInternalField(0)->IsUndefined());
1465 obj->SetInternalField(0, v8_num(17));
1466 CHECK_EQ(17, obj->GetInternalField(0)->Int32Value());
1467}
1468
1469
Steve Block6ded16b2010-05-10 14:33:55 +01001470THREADED_TEST(GlobalObjectInternalFields) {
1471 v8::HandleScope scope;
1472 Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
1473 global_template->SetInternalFieldCount(1);
1474 LocalContext env(NULL, global_template);
1475 v8::Handle<v8::Object> global_proxy = env->Global();
1476 v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>();
1477 CHECK_EQ(1, global->InternalFieldCount());
1478 CHECK(global->GetInternalField(0)->IsUndefined());
1479 global->SetInternalField(0, v8_num(17));
1480 CHECK_EQ(17, global->GetInternalField(0)->Int32Value());
1481}
1482
1483
Steve Blocka7e24c12009-10-30 11:49:00 +00001484THREADED_TEST(InternalFieldsNativePointers) {
1485 v8::HandleScope scope;
1486 LocalContext env;
1487
1488 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1489 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate();
1490 instance_templ->SetInternalFieldCount(1);
1491 Local<v8::Object> obj = templ->GetFunction()->NewInstance();
1492 CHECK_EQ(1, obj->InternalFieldCount());
1493 CHECK(obj->GetPointerFromInternalField(0) == NULL);
1494
1495 char* data = new char[100];
1496
1497 void* aligned = data;
Ben Murdochf87a2032010-10-22 12:50:53 +01001498 CHECK_EQ(0, static_cast<int>(reinterpret_cast<uintptr_t>(aligned) & 0x1));
Steve Blocka7e24c12009-10-30 11:49:00 +00001499 void* unaligned = data + 1;
Ben Murdochf87a2032010-10-22 12:50:53 +01001500 CHECK_EQ(1, static_cast<int>(reinterpret_cast<uintptr_t>(unaligned) & 0x1));
Steve Blocka7e24c12009-10-30 11:49:00 +00001501
1502 // Check reading and writing aligned pointers.
1503 obj->SetPointerInInternalField(0, aligned);
1504 i::Heap::CollectAllGarbage(false);
1505 CHECK_EQ(aligned, obj->GetPointerFromInternalField(0));
1506
1507 // Check reading and writing unaligned pointers.
1508 obj->SetPointerInInternalField(0, unaligned);
1509 i::Heap::CollectAllGarbage(false);
1510 CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0));
1511
1512 delete[] data;
1513}
1514
1515
Steve Block3ce2e202009-11-05 08:53:23 +00001516THREADED_TEST(InternalFieldsNativePointersAndExternal) {
1517 v8::HandleScope scope;
1518 LocalContext env;
1519
1520 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1521 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate();
1522 instance_templ->SetInternalFieldCount(1);
1523 Local<v8::Object> obj = templ->GetFunction()->NewInstance();
1524 CHECK_EQ(1, obj->InternalFieldCount());
1525 CHECK(obj->GetPointerFromInternalField(0) == NULL);
1526
1527 char* data = new char[100];
1528
1529 void* aligned = data;
Ben Murdochf87a2032010-10-22 12:50:53 +01001530 CHECK_EQ(0, static_cast<int>(reinterpret_cast<uintptr_t>(aligned) & 0x1));
Steve Block3ce2e202009-11-05 08:53:23 +00001531 void* unaligned = data + 1;
Ben Murdochf87a2032010-10-22 12:50:53 +01001532 CHECK_EQ(1, static_cast<int>(reinterpret_cast<uintptr_t>(unaligned) & 0x1));
Steve Block3ce2e202009-11-05 08:53:23 +00001533
1534 obj->SetPointerInInternalField(0, aligned);
1535 i::Heap::CollectAllGarbage(false);
1536 CHECK_EQ(aligned, v8::External::Unwrap(obj->GetInternalField(0)));
1537
1538 obj->SetPointerInInternalField(0, unaligned);
1539 i::Heap::CollectAllGarbage(false);
1540 CHECK_EQ(unaligned, v8::External::Unwrap(obj->GetInternalField(0)));
1541
1542 obj->SetInternalField(0, v8::External::Wrap(aligned));
1543 i::Heap::CollectAllGarbage(false);
1544 CHECK_EQ(aligned, obj->GetPointerFromInternalField(0));
1545
1546 obj->SetInternalField(0, v8::External::Wrap(unaligned));
1547 i::Heap::CollectAllGarbage(false);
1548 CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0));
1549
1550 delete[] data;
1551}
1552
1553
Steve Blocka7e24c12009-10-30 11:49:00 +00001554THREADED_TEST(IdentityHash) {
1555 v8::HandleScope scope;
1556 LocalContext env;
1557
1558 // Ensure that the test starts with an fresh heap to test whether the hash
1559 // code is based on the address.
1560 i::Heap::CollectAllGarbage(false);
1561 Local<v8::Object> obj = v8::Object::New();
1562 int hash = obj->GetIdentityHash();
1563 int hash1 = obj->GetIdentityHash();
1564 CHECK_EQ(hash, hash1);
1565 int hash2 = v8::Object::New()->GetIdentityHash();
1566 // Since the identity hash is essentially a random number two consecutive
1567 // objects should not be assigned the same hash code. If the test below fails
1568 // the random number generator should be evaluated.
1569 CHECK_NE(hash, hash2);
1570 i::Heap::CollectAllGarbage(false);
1571 int hash3 = v8::Object::New()->GetIdentityHash();
1572 // Make sure that the identity hash is not based on the initial address of
1573 // the object alone. If the test below fails the random number generator
1574 // should be evaluated.
1575 CHECK_NE(hash, hash3);
1576 int hash4 = obj->GetIdentityHash();
1577 CHECK_EQ(hash, hash4);
1578}
1579
1580
1581THREADED_TEST(HiddenProperties) {
1582 v8::HandleScope scope;
1583 LocalContext env;
1584
1585 v8::Local<v8::Object> obj = v8::Object::New();
1586 v8::Local<v8::String> key = v8_str("api-test::hidden-key");
1587 v8::Local<v8::String> empty = v8_str("");
1588 v8::Local<v8::String> prop_name = v8_str("prop_name");
1589
1590 i::Heap::CollectAllGarbage(false);
1591
1592 // Make sure delete of a non-existent hidden value works
1593 CHECK(obj->DeleteHiddenValue(key));
1594
1595 CHECK(obj->SetHiddenValue(key, v8::Integer::New(1503)));
1596 CHECK_EQ(1503, obj->GetHiddenValue(key)->Int32Value());
1597 CHECK(obj->SetHiddenValue(key, v8::Integer::New(2002)));
1598 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1599
1600 i::Heap::CollectAllGarbage(false);
1601
1602 // Make sure we do not find the hidden property.
1603 CHECK(!obj->Has(empty));
1604 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1605 CHECK(obj->Get(empty)->IsUndefined());
1606 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1607 CHECK(obj->Set(empty, v8::Integer::New(2003)));
1608 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1609 CHECK_EQ(2003, obj->Get(empty)->Int32Value());
1610
1611 i::Heap::CollectAllGarbage(false);
1612
1613 // Add another property and delete it afterwards to force the object in
1614 // slow case.
1615 CHECK(obj->Set(prop_name, v8::Integer::New(2008)));
1616 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1617 CHECK_EQ(2008, obj->Get(prop_name)->Int32Value());
1618 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1619 CHECK(obj->Delete(prop_name));
1620 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1621
1622 i::Heap::CollectAllGarbage(false);
1623
1624 CHECK(obj->DeleteHiddenValue(key));
1625 CHECK(obj->GetHiddenValue(key).IsEmpty());
1626}
1627
1628
Steve Blockd0582a62009-12-15 09:54:21 +00001629static bool interceptor_for_hidden_properties_called;
Steve Blocka7e24c12009-10-30 11:49:00 +00001630static v8::Handle<Value> InterceptorForHiddenProperties(
1631 Local<String> name, const AccessorInfo& info) {
Steve Blockd0582a62009-12-15 09:54:21 +00001632 interceptor_for_hidden_properties_called = true;
Steve Blocka7e24c12009-10-30 11:49:00 +00001633 return v8::Handle<Value>();
1634}
1635
1636
1637THREADED_TEST(HiddenPropertiesWithInterceptors) {
1638 v8::HandleScope scope;
1639 LocalContext context;
1640
Steve Blockd0582a62009-12-15 09:54:21 +00001641 interceptor_for_hidden_properties_called = false;
1642
Steve Blocka7e24c12009-10-30 11:49:00 +00001643 v8::Local<v8::String> key = v8_str("api-test::hidden-key");
1644
1645 // Associate an interceptor with an object and start setting hidden values.
1646 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
1647 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
1648 instance_templ->SetNamedPropertyHandler(InterceptorForHiddenProperties);
1649 Local<v8::Function> function = fun_templ->GetFunction();
1650 Local<v8::Object> obj = function->NewInstance();
1651 CHECK(obj->SetHiddenValue(key, v8::Integer::New(2302)));
1652 CHECK_EQ(2302, obj->GetHiddenValue(key)->Int32Value());
Steve Blockd0582a62009-12-15 09:54:21 +00001653 CHECK(!interceptor_for_hidden_properties_called);
Steve Blocka7e24c12009-10-30 11:49:00 +00001654}
1655
1656
1657THREADED_TEST(External) {
1658 v8::HandleScope scope;
1659 int x = 3;
1660 Local<v8::External> ext = v8::External::New(&x);
1661 LocalContext env;
1662 env->Global()->Set(v8_str("ext"), ext);
1663 Local<Value> reext_obj = Script::Compile(v8_str("this.ext"))->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01001664 v8::Handle<v8::External> reext = reext_obj.As<v8::External>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001665 int* ptr = static_cast<int*>(reext->Value());
1666 CHECK_EQ(x, 3);
1667 *ptr = 10;
1668 CHECK_EQ(x, 10);
1669
1670 // Make sure unaligned pointers are wrapped properly.
1671 char* data = i::StrDup("0123456789");
1672 Local<v8::Value> zero = v8::External::Wrap(&data[0]);
1673 Local<v8::Value> one = v8::External::Wrap(&data[1]);
1674 Local<v8::Value> two = v8::External::Wrap(&data[2]);
1675 Local<v8::Value> three = v8::External::Wrap(&data[3]);
1676
1677 char* char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(zero));
1678 CHECK_EQ('0', *char_ptr);
1679 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(one));
1680 CHECK_EQ('1', *char_ptr);
1681 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(two));
1682 CHECK_EQ('2', *char_ptr);
1683 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(three));
1684 CHECK_EQ('3', *char_ptr);
1685 i::DeleteArray(data);
1686}
1687
1688
1689THREADED_TEST(GlobalHandle) {
1690 v8::Persistent<String> global;
1691 {
1692 v8::HandleScope scope;
1693 Local<String> str = v8_str("str");
1694 global = v8::Persistent<String>::New(str);
1695 }
1696 CHECK_EQ(global->Length(), 3);
1697 global.Dispose();
1698}
1699
1700
1701THREADED_TEST(ScriptException) {
1702 v8::HandleScope scope;
1703 LocalContext env;
1704 Local<Script> script = Script::Compile(v8_str("throw 'panama!';"));
1705 v8::TryCatch try_catch;
1706 Local<Value> result = script->Run();
1707 CHECK(result.IsEmpty());
1708 CHECK(try_catch.HasCaught());
1709 String::AsciiValue exception_value(try_catch.Exception());
1710 CHECK_EQ(*exception_value, "panama!");
1711}
1712
1713
1714bool message_received;
1715
1716
1717static void check_message(v8::Handle<v8::Message> message,
1718 v8::Handle<Value> data) {
1719 CHECK_EQ(5.76, data->NumberValue());
1720 CHECK_EQ(6.75, message->GetScriptResourceName()->NumberValue());
1721 CHECK_EQ(7.56, message->GetScriptData()->NumberValue());
1722 message_received = true;
1723}
1724
1725
1726THREADED_TEST(MessageHandlerData) {
1727 message_received = false;
1728 v8::HandleScope scope;
1729 CHECK(!message_received);
1730 v8::V8::AddMessageListener(check_message, v8_num(5.76));
1731 LocalContext context;
1732 v8::ScriptOrigin origin =
1733 v8::ScriptOrigin(v8_str("6.75"));
1734 v8::Handle<v8::Script> script = Script::Compile(v8_str("throw 'error'"),
1735 &origin);
1736 script->SetData(v8_str("7.56"));
1737 script->Run();
1738 CHECK(message_received);
1739 // clear out the message listener
1740 v8::V8::RemoveMessageListeners(check_message);
1741}
1742
1743
1744THREADED_TEST(GetSetProperty) {
1745 v8::HandleScope scope;
1746 LocalContext context;
1747 context->Global()->Set(v8_str("foo"), v8_num(14));
1748 context->Global()->Set(v8_str("12"), v8_num(92));
1749 context->Global()->Set(v8::Integer::New(16), v8_num(32));
1750 context->Global()->Set(v8_num(13), v8_num(56));
1751 Local<Value> foo = Script::Compile(v8_str("this.foo"))->Run();
1752 CHECK_EQ(14, foo->Int32Value());
1753 Local<Value> twelve = Script::Compile(v8_str("this[12]"))->Run();
1754 CHECK_EQ(92, twelve->Int32Value());
1755 Local<Value> sixteen = Script::Compile(v8_str("this[16]"))->Run();
1756 CHECK_EQ(32, sixteen->Int32Value());
1757 Local<Value> thirteen = Script::Compile(v8_str("this[13]"))->Run();
1758 CHECK_EQ(56, thirteen->Int32Value());
1759 CHECK_EQ(92, context->Global()->Get(v8::Integer::New(12))->Int32Value());
1760 CHECK_EQ(92, context->Global()->Get(v8_str("12"))->Int32Value());
1761 CHECK_EQ(92, context->Global()->Get(v8_num(12))->Int32Value());
1762 CHECK_EQ(32, context->Global()->Get(v8::Integer::New(16))->Int32Value());
1763 CHECK_EQ(32, context->Global()->Get(v8_str("16"))->Int32Value());
1764 CHECK_EQ(32, context->Global()->Get(v8_num(16))->Int32Value());
1765 CHECK_EQ(56, context->Global()->Get(v8::Integer::New(13))->Int32Value());
1766 CHECK_EQ(56, context->Global()->Get(v8_str("13"))->Int32Value());
1767 CHECK_EQ(56, context->Global()->Get(v8_num(13))->Int32Value());
1768}
1769
1770
1771THREADED_TEST(PropertyAttributes) {
1772 v8::HandleScope scope;
1773 LocalContext context;
1774 // read-only
1775 Local<String> prop = v8_str("read_only");
1776 context->Global()->Set(prop, v8_num(7), v8::ReadOnly);
1777 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value());
1778 Script::Compile(v8_str("read_only = 9"))->Run();
1779 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value());
1780 context->Global()->Set(prop, v8_num(10));
1781 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value());
1782 // dont-delete
1783 prop = v8_str("dont_delete");
1784 context->Global()->Set(prop, v8_num(13), v8::DontDelete);
1785 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value());
1786 Script::Compile(v8_str("delete dont_delete"))->Run();
1787 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value());
1788}
1789
1790
1791THREADED_TEST(Array) {
1792 v8::HandleScope scope;
1793 LocalContext context;
1794 Local<v8::Array> array = v8::Array::New();
1795 CHECK_EQ(0, array->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001796 CHECK(array->Get(0)->IsUndefined());
Steve Blocka7e24c12009-10-30 11:49:00 +00001797 CHECK(!array->Has(0));
Steve Block6ded16b2010-05-10 14:33:55 +01001798 CHECK(array->Get(100)->IsUndefined());
Steve Blocka7e24c12009-10-30 11:49:00 +00001799 CHECK(!array->Has(100));
Steve Block6ded16b2010-05-10 14:33:55 +01001800 array->Set(2, v8_num(7));
Steve Blocka7e24c12009-10-30 11:49:00 +00001801 CHECK_EQ(3, array->Length());
1802 CHECK(!array->Has(0));
1803 CHECK(!array->Has(1));
1804 CHECK(array->Has(2));
Steve Block6ded16b2010-05-10 14:33:55 +01001805 CHECK_EQ(7, array->Get(2)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001806 Local<Value> obj = Script::Compile(v8_str("[1, 2, 3]"))->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01001807 Local<v8::Array> arr = obj.As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001808 CHECK_EQ(3, arr->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001809 CHECK_EQ(1, arr->Get(0)->Int32Value());
1810 CHECK_EQ(2, arr->Get(1)->Int32Value());
1811 CHECK_EQ(3, arr->Get(2)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001812}
1813
1814
1815v8::Handle<Value> HandleF(const v8::Arguments& args) {
1816 v8::HandleScope scope;
1817 ApiTestFuzzer::Fuzz();
1818 Local<v8::Array> result = v8::Array::New(args.Length());
1819 for (int i = 0; i < args.Length(); i++)
Steve Block6ded16b2010-05-10 14:33:55 +01001820 result->Set(i, args[i]);
Steve Blocka7e24c12009-10-30 11:49:00 +00001821 return scope.Close(result);
1822}
1823
1824
1825THREADED_TEST(Vector) {
1826 v8::HandleScope scope;
1827 Local<ObjectTemplate> global = ObjectTemplate::New();
1828 global->Set(v8_str("f"), v8::FunctionTemplate::New(HandleF));
1829 LocalContext context(0, global);
1830
1831 const char* fun = "f()";
Steve Block6ded16b2010-05-10 14:33:55 +01001832 Local<v8::Array> a0 = CompileRun(fun).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001833 CHECK_EQ(0, a0->Length());
1834
1835 const char* fun2 = "f(11)";
Steve Block6ded16b2010-05-10 14:33:55 +01001836 Local<v8::Array> a1 = CompileRun(fun2).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001837 CHECK_EQ(1, a1->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001838 CHECK_EQ(11, a1->Get(0)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001839
1840 const char* fun3 = "f(12, 13)";
Steve Block6ded16b2010-05-10 14:33:55 +01001841 Local<v8::Array> a2 = CompileRun(fun3).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001842 CHECK_EQ(2, a2->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001843 CHECK_EQ(12, a2->Get(0)->Int32Value());
1844 CHECK_EQ(13, a2->Get(1)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001845
1846 const char* fun4 = "f(14, 15, 16)";
Steve Block6ded16b2010-05-10 14:33:55 +01001847 Local<v8::Array> a3 = CompileRun(fun4).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001848 CHECK_EQ(3, a3->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001849 CHECK_EQ(14, a3->Get(0)->Int32Value());
1850 CHECK_EQ(15, a3->Get(1)->Int32Value());
1851 CHECK_EQ(16, a3->Get(2)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001852
1853 const char* fun5 = "f(17, 18, 19, 20)";
Steve Block6ded16b2010-05-10 14:33:55 +01001854 Local<v8::Array> a4 = CompileRun(fun5).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001855 CHECK_EQ(4, a4->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001856 CHECK_EQ(17, a4->Get(0)->Int32Value());
1857 CHECK_EQ(18, a4->Get(1)->Int32Value());
1858 CHECK_EQ(19, a4->Get(2)->Int32Value());
1859 CHECK_EQ(20, a4->Get(3)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001860}
1861
1862
1863THREADED_TEST(FunctionCall) {
1864 v8::HandleScope scope;
1865 LocalContext context;
1866 CompileRun(
1867 "function Foo() {"
1868 " var result = [];"
1869 " for (var i = 0; i < arguments.length; i++) {"
1870 " result.push(arguments[i]);"
1871 " }"
1872 " return result;"
1873 "}");
1874 Local<Function> Foo =
1875 Local<Function>::Cast(context->Global()->Get(v8_str("Foo")));
1876
1877 v8::Handle<Value>* args0 = NULL;
1878 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->Call(Foo, 0, args0));
1879 CHECK_EQ(0, a0->Length());
1880
1881 v8::Handle<Value> args1[] = { v8_num(1.1) };
1882 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->Call(Foo, 1, args1));
1883 CHECK_EQ(1, a1->Length());
1884 CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue());
1885
1886 v8::Handle<Value> args2[] = { v8_num(2.2),
1887 v8_num(3.3) };
1888 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->Call(Foo, 2, args2));
1889 CHECK_EQ(2, a2->Length());
1890 CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue());
1891 CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue());
1892
1893 v8::Handle<Value> args3[] = { v8_num(4.4),
1894 v8_num(5.5),
1895 v8_num(6.6) };
1896 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->Call(Foo, 3, args3));
1897 CHECK_EQ(3, a3->Length());
1898 CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue());
1899 CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue());
1900 CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue());
1901
1902 v8::Handle<Value> args4[] = { v8_num(7.7),
1903 v8_num(8.8),
1904 v8_num(9.9),
1905 v8_num(10.11) };
1906 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->Call(Foo, 4, args4));
1907 CHECK_EQ(4, a4->Length());
1908 CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue());
1909 CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue());
1910 CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue());
1911 CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue());
1912}
1913
1914
1915static const char* js_code_causing_out_of_memory =
1916 "var a = new Array(); while(true) a.push(a);";
1917
1918
1919// These tests run for a long time and prevent us from running tests
1920// that come after them so they cannot run in parallel.
1921TEST(OutOfMemory) {
1922 // It's not possible to read a snapshot into a heap with different dimensions.
Steve Block8defd9f2010-07-08 12:39:36 +01001923 if (i::Snapshot::IsEnabled()) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00001924 // Set heap limits.
1925 static const int K = 1024;
1926 v8::ResourceConstraints constraints;
1927 constraints.set_max_young_space_size(256 * K);
1928 constraints.set_max_old_space_size(4 * K * K);
1929 v8::SetResourceConstraints(&constraints);
1930
1931 // Execute a script that causes out of memory.
1932 v8::HandleScope scope;
1933 LocalContext context;
1934 v8::V8::IgnoreOutOfMemoryException();
1935 Local<Script> script =
1936 Script::Compile(String::New(js_code_causing_out_of_memory));
1937 Local<Value> result = script->Run();
1938
1939 // Check for out of memory state.
1940 CHECK(result.IsEmpty());
1941 CHECK(context->HasOutOfMemoryException());
1942}
1943
1944
1945v8::Handle<Value> ProvokeOutOfMemory(const v8::Arguments& args) {
1946 ApiTestFuzzer::Fuzz();
1947
1948 v8::HandleScope scope;
1949 LocalContext context;
1950 Local<Script> script =
1951 Script::Compile(String::New(js_code_causing_out_of_memory));
1952 Local<Value> result = script->Run();
1953
1954 // Check for out of memory state.
1955 CHECK(result.IsEmpty());
1956 CHECK(context->HasOutOfMemoryException());
1957
1958 return result;
1959}
1960
1961
1962TEST(OutOfMemoryNested) {
1963 // It's not possible to read a snapshot into a heap with different dimensions.
Steve Block8defd9f2010-07-08 12:39:36 +01001964 if (i::Snapshot::IsEnabled()) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00001965 // Set heap limits.
1966 static const int K = 1024;
1967 v8::ResourceConstraints constraints;
1968 constraints.set_max_young_space_size(256 * K);
1969 constraints.set_max_old_space_size(4 * K * K);
1970 v8::SetResourceConstraints(&constraints);
1971
1972 v8::HandleScope scope;
1973 Local<ObjectTemplate> templ = ObjectTemplate::New();
1974 templ->Set(v8_str("ProvokeOutOfMemory"),
1975 v8::FunctionTemplate::New(ProvokeOutOfMemory));
1976 LocalContext context(0, templ);
1977 v8::V8::IgnoreOutOfMemoryException();
1978 Local<Value> result = CompileRun(
1979 "var thrown = false;"
1980 "try {"
1981 " ProvokeOutOfMemory();"
1982 "} catch (e) {"
1983 " thrown = true;"
1984 "}");
1985 // Check for out of memory state.
1986 CHECK(result.IsEmpty());
1987 CHECK(context->HasOutOfMemoryException());
1988}
1989
1990
1991TEST(HugeConsStringOutOfMemory) {
1992 // It's not possible to read a snapshot into a heap with different dimensions.
Steve Block8defd9f2010-07-08 12:39:36 +01001993 if (i::Snapshot::IsEnabled()) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00001994 v8::HandleScope scope;
1995 LocalContext context;
1996 // Set heap limits.
1997 static const int K = 1024;
1998 v8::ResourceConstraints constraints;
1999 constraints.set_max_young_space_size(256 * K);
2000 constraints.set_max_old_space_size(2 * K * K);
2001 v8::SetResourceConstraints(&constraints);
2002
2003 // Execute a script that causes out of memory.
2004 v8::V8::IgnoreOutOfMemoryException();
2005
2006 // Build huge string. This should fail with out of memory exception.
2007 Local<Value> result = CompileRun(
2008 "var str = Array.prototype.join.call({length: 513}, \"A\").toUpperCase();"
Steve Block3ce2e202009-11-05 08:53:23 +00002009 "for (var i = 0; i < 22; i++) { str = str + str; }");
Steve Blocka7e24c12009-10-30 11:49:00 +00002010
2011 // Check for out of memory state.
2012 CHECK(result.IsEmpty());
2013 CHECK(context->HasOutOfMemoryException());
2014}
2015
2016
2017THREADED_TEST(ConstructCall) {
2018 v8::HandleScope scope;
2019 LocalContext context;
2020 CompileRun(
2021 "function Foo() {"
2022 " var result = [];"
2023 " for (var i = 0; i < arguments.length; i++) {"
2024 " result.push(arguments[i]);"
2025 " }"
2026 " return result;"
2027 "}");
2028 Local<Function> Foo =
2029 Local<Function>::Cast(context->Global()->Get(v8_str("Foo")));
2030
2031 v8::Handle<Value>* args0 = NULL;
2032 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->NewInstance(0, args0));
2033 CHECK_EQ(0, a0->Length());
2034
2035 v8::Handle<Value> args1[] = { v8_num(1.1) };
2036 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->NewInstance(1, args1));
2037 CHECK_EQ(1, a1->Length());
2038 CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue());
2039
2040 v8::Handle<Value> args2[] = { v8_num(2.2),
2041 v8_num(3.3) };
2042 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->NewInstance(2, args2));
2043 CHECK_EQ(2, a2->Length());
2044 CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue());
2045 CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue());
2046
2047 v8::Handle<Value> args3[] = { v8_num(4.4),
2048 v8_num(5.5),
2049 v8_num(6.6) };
2050 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->NewInstance(3, args3));
2051 CHECK_EQ(3, a3->Length());
2052 CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue());
2053 CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue());
2054 CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue());
2055
2056 v8::Handle<Value> args4[] = { v8_num(7.7),
2057 v8_num(8.8),
2058 v8_num(9.9),
2059 v8_num(10.11) };
2060 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->NewInstance(4, args4));
2061 CHECK_EQ(4, a4->Length());
2062 CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue());
2063 CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue());
2064 CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue());
2065 CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue());
2066}
2067
2068
2069static void CheckUncle(v8::TryCatch* try_catch) {
2070 CHECK(try_catch->HasCaught());
2071 String::AsciiValue str_value(try_catch->Exception());
2072 CHECK_EQ(*str_value, "uncle?");
2073 try_catch->Reset();
2074}
2075
2076
Steve Block6ded16b2010-05-10 14:33:55 +01002077THREADED_TEST(ConversionNumber) {
2078 v8::HandleScope scope;
2079 LocalContext env;
2080 // Very large number.
2081 CompileRun("var obj = Math.pow(2,32) * 1237;");
2082 Local<Value> obj = env->Global()->Get(v8_str("obj"));
2083 CHECK_EQ(5312874545152.0, obj->ToNumber()->Value());
2084 CHECK_EQ(0, obj->ToInt32()->Value());
2085 CHECK(0u == obj->ToUint32()->Value()); // NOLINT - no CHECK_EQ for unsigned.
2086 // Large number.
2087 CompileRun("var obj = -1234567890123;");
2088 obj = env->Global()->Get(v8_str("obj"));
2089 CHECK_EQ(-1234567890123.0, obj->ToNumber()->Value());
2090 CHECK_EQ(-1912276171, obj->ToInt32()->Value());
2091 CHECK(2382691125u == obj->ToUint32()->Value()); // NOLINT
2092 // Small positive integer.
2093 CompileRun("var obj = 42;");
2094 obj = env->Global()->Get(v8_str("obj"));
2095 CHECK_EQ(42.0, obj->ToNumber()->Value());
2096 CHECK_EQ(42, obj->ToInt32()->Value());
2097 CHECK(42u == obj->ToUint32()->Value()); // NOLINT
2098 // Negative integer.
2099 CompileRun("var obj = -37;");
2100 obj = env->Global()->Get(v8_str("obj"));
2101 CHECK_EQ(-37.0, obj->ToNumber()->Value());
2102 CHECK_EQ(-37, obj->ToInt32()->Value());
2103 CHECK(4294967259u == obj->ToUint32()->Value()); // NOLINT
2104 // Positive non-int32 integer.
2105 CompileRun("var obj = 0x81234567;");
2106 obj = env->Global()->Get(v8_str("obj"));
2107 CHECK_EQ(2166572391.0, obj->ToNumber()->Value());
2108 CHECK_EQ(-2128394905, obj->ToInt32()->Value());
2109 CHECK(2166572391u == obj->ToUint32()->Value()); // NOLINT
2110 // Fraction.
2111 CompileRun("var obj = 42.3;");
2112 obj = env->Global()->Get(v8_str("obj"));
2113 CHECK_EQ(42.3, obj->ToNumber()->Value());
2114 CHECK_EQ(42, obj->ToInt32()->Value());
2115 CHECK(42u == obj->ToUint32()->Value()); // NOLINT
2116 // Large negative fraction.
2117 CompileRun("var obj = -5726623061.75;");
2118 obj = env->Global()->Get(v8_str("obj"));
2119 CHECK_EQ(-5726623061.75, obj->ToNumber()->Value());
2120 CHECK_EQ(-1431655765, obj->ToInt32()->Value());
2121 CHECK(2863311531u == obj->ToUint32()->Value()); // NOLINT
2122}
2123
2124
2125THREADED_TEST(isNumberType) {
2126 v8::HandleScope scope;
2127 LocalContext env;
2128 // Very large number.
2129 CompileRun("var obj = Math.pow(2,32) * 1237;");
2130 Local<Value> obj = env->Global()->Get(v8_str("obj"));
2131 CHECK(!obj->IsInt32());
2132 CHECK(!obj->IsUint32());
2133 // Large negative number.
2134 CompileRun("var obj = -1234567890123;");
2135 obj = env->Global()->Get(v8_str("obj"));
2136 CHECK(!obj->IsInt32());
2137 CHECK(!obj->IsUint32());
2138 // Small positive integer.
2139 CompileRun("var obj = 42;");
2140 obj = env->Global()->Get(v8_str("obj"));
2141 CHECK(obj->IsInt32());
2142 CHECK(obj->IsUint32());
2143 // Negative integer.
2144 CompileRun("var obj = -37;");
2145 obj = env->Global()->Get(v8_str("obj"));
2146 CHECK(obj->IsInt32());
2147 CHECK(!obj->IsUint32());
2148 // Positive non-int32 integer.
2149 CompileRun("var obj = 0x81234567;");
2150 obj = env->Global()->Get(v8_str("obj"));
2151 CHECK(!obj->IsInt32());
2152 CHECK(obj->IsUint32());
2153 // Fraction.
2154 CompileRun("var obj = 42.3;");
2155 obj = env->Global()->Get(v8_str("obj"));
2156 CHECK(!obj->IsInt32());
2157 CHECK(!obj->IsUint32());
2158 // Large negative fraction.
2159 CompileRun("var obj = -5726623061.75;");
2160 obj = env->Global()->Get(v8_str("obj"));
2161 CHECK(!obj->IsInt32());
2162 CHECK(!obj->IsUint32());
2163}
2164
2165
Steve Blocka7e24c12009-10-30 11:49:00 +00002166THREADED_TEST(ConversionException) {
2167 v8::HandleScope scope;
2168 LocalContext env;
2169 CompileRun(
2170 "function TestClass() { };"
2171 "TestClass.prototype.toString = function () { throw 'uncle?'; };"
2172 "var obj = new TestClass();");
2173 Local<Value> obj = env->Global()->Get(v8_str("obj"));
2174
2175 v8::TryCatch try_catch;
2176
2177 Local<Value> to_string_result = obj->ToString();
2178 CHECK(to_string_result.IsEmpty());
2179 CheckUncle(&try_catch);
2180
2181 Local<Value> to_number_result = obj->ToNumber();
2182 CHECK(to_number_result.IsEmpty());
2183 CheckUncle(&try_catch);
2184
2185 Local<Value> to_integer_result = obj->ToInteger();
2186 CHECK(to_integer_result.IsEmpty());
2187 CheckUncle(&try_catch);
2188
2189 Local<Value> to_uint32_result = obj->ToUint32();
2190 CHECK(to_uint32_result.IsEmpty());
2191 CheckUncle(&try_catch);
2192
2193 Local<Value> to_int32_result = obj->ToInt32();
2194 CHECK(to_int32_result.IsEmpty());
2195 CheckUncle(&try_catch);
2196
2197 Local<Value> to_object_result = v8::Undefined()->ToObject();
2198 CHECK(to_object_result.IsEmpty());
2199 CHECK(try_catch.HasCaught());
2200 try_catch.Reset();
2201
2202 int32_t int32_value = obj->Int32Value();
2203 CHECK_EQ(0, int32_value);
2204 CheckUncle(&try_catch);
2205
2206 uint32_t uint32_value = obj->Uint32Value();
2207 CHECK_EQ(0, uint32_value);
2208 CheckUncle(&try_catch);
2209
2210 double number_value = obj->NumberValue();
2211 CHECK_NE(0, IsNaN(number_value));
2212 CheckUncle(&try_catch);
2213
2214 int64_t integer_value = obj->IntegerValue();
2215 CHECK_EQ(0.0, static_cast<double>(integer_value));
2216 CheckUncle(&try_catch);
2217}
2218
2219
2220v8::Handle<Value> ThrowFromC(const v8::Arguments& args) {
2221 ApiTestFuzzer::Fuzz();
2222 return v8::ThrowException(v8_str("konto"));
2223}
2224
2225
2226v8::Handle<Value> CCatcher(const v8::Arguments& args) {
2227 if (args.Length() < 1) return v8::Boolean::New(false);
2228 v8::HandleScope scope;
2229 v8::TryCatch try_catch;
2230 Local<Value> result = v8::Script::Compile(args[0]->ToString())->Run();
2231 CHECK(!try_catch.HasCaught() || result.IsEmpty());
2232 return v8::Boolean::New(try_catch.HasCaught());
2233}
2234
2235
2236THREADED_TEST(APICatch) {
2237 v8::HandleScope scope;
2238 Local<ObjectTemplate> templ = ObjectTemplate::New();
2239 templ->Set(v8_str("ThrowFromC"),
2240 v8::FunctionTemplate::New(ThrowFromC));
2241 LocalContext context(0, templ);
2242 CompileRun(
2243 "var thrown = false;"
2244 "try {"
2245 " ThrowFromC();"
2246 "} catch (e) {"
2247 " thrown = true;"
2248 "}");
2249 Local<Value> thrown = context->Global()->Get(v8_str("thrown"));
2250 CHECK(thrown->BooleanValue());
2251}
2252
2253
2254THREADED_TEST(APIThrowTryCatch) {
2255 v8::HandleScope scope;
2256 Local<ObjectTemplate> templ = ObjectTemplate::New();
2257 templ->Set(v8_str("ThrowFromC"),
2258 v8::FunctionTemplate::New(ThrowFromC));
2259 LocalContext context(0, templ);
2260 v8::TryCatch try_catch;
2261 CompileRun("ThrowFromC();");
2262 CHECK(try_catch.HasCaught());
2263}
2264
2265
2266// Test that a try-finally block doesn't shadow a try-catch block
2267// when setting up an external handler.
2268//
2269// BUG(271): Some of the exception propagation does not work on the
2270// ARM simulator because the simulator separates the C++ stack and the
2271// JS stack. This test therefore fails on the simulator. The test is
2272// not threaded to allow the threading tests to run on the simulator.
2273TEST(TryCatchInTryFinally) {
2274 v8::HandleScope scope;
2275 Local<ObjectTemplate> templ = ObjectTemplate::New();
2276 templ->Set(v8_str("CCatcher"),
2277 v8::FunctionTemplate::New(CCatcher));
2278 LocalContext context(0, templ);
2279 Local<Value> result = CompileRun("try {"
2280 " try {"
2281 " CCatcher('throw 7;');"
2282 " } finally {"
2283 " }"
2284 "} catch (e) {"
2285 "}");
2286 CHECK(result->IsTrue());
2287}
2288
2289
2290static void receive_message(v8::Handle<v8::Message> message,
2291 v8::Handle<v8::Value> data) {
2292 message->Get();
2293 message_received = true;
2294}
2295
2296
2297TEST(APIThrowMessage) {
2298 message_received = false;
2299 v8::HandleScope scope;
2300 v8::V8::AddMessageListener(receive_message);
2301 Local<ObjectTemplate> templ = ObjectTemplate::New();
2302 templ->Set(v8_str("ThrowFromC"),
2303 v8::FunctionTemplate::New(ThrowFromC));
2304 LocalContext context(0, templ);
2305 CompileRun("ThrowFromC();");
2306 CHECK(message_received);
2307 v8::V8::RemoveMessageListeners(check_message);
2308}
2309
2310
2311TEST(APIThrowMessageAndVerboseTryCatch) {
2312 message_received = false;
2313 v8::HandleScope scope;
2314 v8::V8::AddMessageListener(receive_message);
2315 Local<ObjectTemplate> templ = ObjectTemplate::New();
2316 templ->Set(v8_str("ThrowFromC"),
2317 v8::FunctionTemplate::New(ThrowFromC));
2318 LocalContext context(0, templ);
2319 v8::TryCatch try_catch;
2320 try_catch.SetVerbose(true);
2321 Local<Value> result = CompileRun("ThrowFromC();");
2322 CHECK(try_catch.HasCaught());
2323 CHECK(result.IsEmpty());
2324 CHECK(message_received);
2325 v8::V8::RemoveMessageListeners(check_message);
2326}
2327
2328
2329THREADED_TEST(ExternalScriptException) {
2330 v8::HandleScope scope;
2331 Local<ObjectTemplate> templ = ObjectTemplate::New();
2332 templ->Set(v8_str("ThrowFromC"),
2333 v8::FunctionTemplate::New(ThrowFromC));
2334 LocalContext context(0, templ);
2335
2336 v8::TryCatch try_catch;
2337 Local<Script> script
2338 = Script::Compile(v8_str("ThrowFromC(); throw 'panama';"));
2339 Local<Value> result = script->Run();
2340 CHECK(result.IsEmpty());
2341 CHECK(try_catch.HasCaught());
2342 String::AsciiValue exception_value(try_catch.Exception());
2343 CHECK_EQ("konto", *exception_value);
2344}
2345
2346
2347
2348v8::Handle<Value> CThrowCountDown(const v8::Arguments& args) {
2349 ApiTestFuzzer::Fuzz();
2350 CHECK_EQ(4, args.Length());
2351 int count = args[0]->Int32Value();
2352 int cInterval = args[2]->Int32Value();
2353 if (count == 0) {
2354 return v8::ThrowException(v8_str("FromC"));
2355 } else {
2356 Local<v8::Object> global = Context::GetCurrent()->Global();
2357 Local<Value> fun = global->Get(v8_str("JSThrowCountDown"));
2358 v8::Handle<Value> argv[] = { v8_num(count - 1),
2359 args[1],
2360 args[2],
2361 args[3] };
2362 if (count % cInterval == 0) {
2363 v8::TryCatch try_catch;
Steve Block6ded16b2010-05-10 14:33:55 +01002364 Local<Value> result = fun.As<Function>()->Call(global, 4, argv);
Steve Blocka7e24c12009-10-30 11:49:00 +00002365 int expected = args[3]->Int32Value();
2366 if (try_catch.HasCaught()) {
2367 CHECK_EQ(expected, count);
2368 CHECK(result.IsEmpty());
2369 CHECK(!i::Top::has_scheduled_exception());
2370 } else {
2371 CHECK_NE(expected, count);
2372 }
2373 return result;
2374 } else {
Steve Block6ded16b2010-05-10 14:33:55 +01002375 return fun.As<Function>()->Call(global, 4, argv);
Steve Blocka7e24c12009-10-30 11:49:00 +00002376 }
2377 }
2378}
2379
2380
2381v8::Handle<Value> JSCheck(const v8::Arguments& args) {
2382 ApiTestFuzzer::Fuzz();
2383 CHECK_EQ(3, args.Length());
2384 bool equality = args[0]->BooleanValue();
2385 int count = args[1]->Int32Value();
2386 int expected = args[2]->Int32Value();
2387 if (equality) {
2388 CHECK_EQ(count, expected);
2389 } else {
2390 CHECK_NE(count, expected);
2391 }
2392 return v8::Undefined();
2393}
2394
2395
2396THREADED_TEST(EvalInTryFinally) {
2397 v8::HandleScope scope;
2398 LocalContext context;
2399 v8::TryCatch try_catch;
2400 CompileRun("(function() {"
2401 " try {"
2402 " eval('asldkf (*&^&*^');"
2403 " } finally {"
2404 " return;"
2405 " }"
2406 "})()");
2407 CHECK(!try_catch.HasCaught());
2408}
2409
2410
2411// This test works by making a stack of alternating JavaScript and C
2412// activations. These activations set up exception handlers with regular
2413// intervals, one interval for C activations and another for JavaScript
2414// activations. When enough activations have been created an exception is
2415// thrown and we check that the right activation catches the exception and that
2416// no other activations do. The right activation is always the topmost one with
2417// a handler, regardless of whether it is in JavaScript or C.
2418//
2419// The notation used to describe a test case looks like this:
2420//
2421// *JS[4] *C[3] @JS[2] C[1] JS[0]
2422//
2423// Each entry is an activation, either JS or C. The index is the count at that
2424// level. Stars identify activations with exception handlers, the @ identifies
2425// the exception handler that should catch the exception.
2426//
2427// BUG(271): Some of the exception propagation does not work on the
2428// ARM simulator because the simulator separates the C++ stack and the
2429// JS stack. This test therefore fails on the simulator. The test is
2430// not threaded to allow the threading tests to run on the simulator.
2431TEST(ExceptionOrder) {
2432 v8::HandleScope scope;
2433 Local<ObjectTemplate> templ = ObjectTemplate::New();
2434 templ->Set(v8_str("check"), v8::FunctionTemplate::New(JSCheck));
2435 templ->Set(v8_str("CThrowCountDown"),
2436 v8::FunctionTemplate::New(CThrowCountDown));
2437 LocalContext context(0, templ);
2438 CompileRun(
2439 "function JSThrowCountDown(count, jsInterval, cInterval, expected) {"
2440 " if (count == 0) throw 'FromJS';"
2441 " if (count % jsInterval == 0) {"
2442 " try {"
2443 " var value = CThrowCountDown(count - 1,"
2444 " jsInterval,"
2445 " cInterval,"
2446 " expected);"
2447 " check(false, count, expected);"
2448 " return value;"
2449 " } catch (e) {"
2450 " check(true, count, expected);"
2451 " }"
2452 " } else {"
2453 " return CThrowCountDown(count - 1, jsInterval, cInterval, expected);"
2454 " }"
2455 "}");
2456 Local<Function> fun =
2457 Local<Function>::Cast(context->Global()->Get(v8_str("JSThrowCountDown")));
2458
2459 const int argc = 4;
2460 // count jsInterval cInterval expected
2461
2462 // *JS[4] *C[3] @JS[2] C[1] JS[0]
2463 v8::Handle<Value> a0[argc] = { v8_num(4), v8_num(2), v8_num(3), v8_num(2) };
2464 fun->Call(fun, argc, a0);
2465
2466 // JS[5] *C[4] JS[3] @C[2] JS[1] C[0]
2467 v8::Handle<Value> a1[argc] = { v8_num(5), v8_num(6), v8_num(1), v8_num(2) };
2468 fun->Call(fun, argc, a1);
2469
2470 // JS[6] @C[5] JS[4] C[3] JS[2] C[1] JS[0]
2471 v8::Handle<Value> a2[argc] = { v8_num(6), v8_num(7), v8_num(5), v8_num(5) };
2472 fun->Call(fun, argc, a2);
2473
2474 // @JS[6] C[5] JS[4] C[3] JS[2] C[1] JS[0]
2475 v8::Handle<Value> a3[argc] = { v8_num(6), v8_num(6), v8_num(7), v8_num(6) };
2476 fun->Call(fun, argc, a3);
2477
2478 // JS[6] *C[5] @JS[4] C[3] JS[2] C[1] JS[0]
2479 v8::Handle<Value> a4[argc] = { v8_num(6), v8_num(4), v8_num(5), v8_num(4) };
2480 fun->Call(fun, argc, a4);
2481
2482 // JS[6] C[5] *JS[4] @C[3] JS[2] C[1] JS[0]
2483 v8::Handle<Value> a5[argc] = { v8_num(6), v8_num(4), v8_num(3), v8_num(3) };
2484 fun->Call(fun, argc, a5);
2485}
2486
2487
2488v8::Handle<Value> ThrowValue(const v8::Arguments& args) {
2489 ApiTestFuzzer::Fuzz();
2490 CHECK_EQ(1, args.Length());
2491 return v8::ThrowException(args[0]);
2492}
2493
2494
2495THREADED_TEST(ThrowValues) {
2496 v8::HandleScope scope;
2497 Local<ObjectTemplate> templ = ObjectTemplate::New();
2498 templ->Set(v8_str("Throw"), v8::FunctionTemplate::New(ThrowValue));
2499 LocalContext context(0, templ);
2500 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun(
2501 "function Run(obj) {"
2502 " try {"
2503 " Throw(obj);"
2504 " } catch (e) {"
2505 " return e;"
2506 " }"
2507 " return 'no exception';"
2508 "}"
2509 "[Run('str'), Run(1), Run(0), Run(null), Run(void 0)];"));
2510 CHECK_EQ(5, result->Length());
2511 CHECK(result->Get(v8::Integer::New(0))->IsString());
2512 CHECK(result->Get(v8::Integer::New(1))->IsNumber());
2513 CHECK_EQ(1, result->Get(v8::Integer::New(1))->Int32Value());
2514 CHECK(result->Get(v8::Integer::New(2))->IsNumber());
2515 CHECK_EQ(0, result->Get(v8::Integer::New(2))->Int32Value());
2516 CHECK(result->Get(v8::Integer::New(3))->IsNull());
2517 CHECK(result->Get(v8::Integer::New(4))->IsUndefined());
2518}
2519
2520
2521THREADED_TEST(CatchZero) {
2522 v8::HandleScope scope;
2523 LocalContext context;
2524 v8::TryCatch try_catch;
2525 CHECK(!try_catch.HasCaught());
2526 Script::Compile(v8_str("throw 10"))->Run();
2527 CHECK(try_catch.HasCaught());
2528 CHECK_EQ(10, try_catch.Exception()->Int32Value());
2529 try_catch.Reset();
2530 CHECK(!try_catch.HasCaught());
2531 Script::Compile(v8_str("throw 0"))->Run();
2532 CHECK(try_catch.HasCaught());
2533 CHECK_EQ(0, try_catch.Exception()->Int32Value());
2534}
2535
2536
2537THREADED_TEST(CatchExceptionFromWith) {
2538 v8::HandleScope scope;
2539 LocalContext context;
2540 v8::TryCatch try_catch;
2541 CHECK(!try_catch.HasCaught());
2542 Script::Compile(v8_str("var o = {}; with (o) { throw 42; }"))->Run();
2543 CHECK(try_catch.HasCaught());
2544}
2545
2546
2547THREADED_TEST(Equality) {
2548 v8::HandleScope scope;
2549 LocalContext context;
2550 // Check that equality works at all before relying on CHECK_EQ
2551 CHECK(v8_str("a")->Equals(v8_str("a")));
2552 CHECK(!v8_str("a")->Equals(v8_str("b")));
2553
2554 CHECK_EQ(v8_str("a"), v8_str("a"));
2555 CHECK_NE(v8_str("a"), v8_str("b"));
2556 CHECK_EQ(v8_num(1), v8_num(1));
2557 CHECK_EQ(v8_num(1.00), v8_num(1));
2558 CHECK_NE(v8_num(1), v8_num(2));
2559
2560 // Assume String is not symbol.
2561 CHECK(v8_str("a")->StrictEquals(v8_str("a")));
2562 CHECK(!v8_str("a")->StrictEquals(v8_str("b")));
2563 CHECK(!v8_str("5")->StrictEquals(v8_num(5)));
2564 CHECK(v8_num(1)->StrictEquals(v8_num(1)));
2565 CHECK(!v8_num(1)->StrictEquals(v8_num(2)));
2566 CHECK(v8_num(0)->StrictEquals(v8_num(-0)));
2567 Local<Value> not_a_number = v8_num(i::OS::nan_value());
2568 CHECK(!not_a_number->StrictEquals(not_a_number));
2569 CHECK(v8::False()->StrictEquals(v8::False()));
2570 CHECK(!v8::False()->StrictEquals(v8::Undefined()));
2571
2572 v8::Handle<v8::Object> obj = v8::Object::New();
2573 v8::Persistent<v8::Object> alias = v8::Persistent<v8::Object>::New(obj);
2574 CHECK(alias->StrictEquals(obj));
2575 alias.Dispose();
2576}
2577
2578
2579THREADED_TEST(MultiRun) {
2580 v8::HandleScope scope;
2581 LocalContext context;
2582 Local<Script> script = Script::Compile(v8_str("x"));
2583 for (int i = 0; i < 10; i++)
2584 script->Run();
2585}
2586
2587
2588static v8::Handle<Value> GetXValue(Local<String> name,
2589 const AccessorInfo& info) {
2590 ApiTestFuzzer::Fuzz();
2591 CHECK_EQ(info.Data(), v8_str("donut"));
2592 CHECK_EQ(name, v8_str("x"));
2593 return name;
2594}
2595
2596
2597THREADED_TEST(SimplePropertyRead) {
2598 v8::HandleScope scope;
2599 Local<ObjectTemplate> templ = ObjectTemplate::New();
2600 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"));
2601 LocalContext context;
2602 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2603 Local<Script> script = Script::Compile(v8_str("obj.x"));
2604 for (int i = 0; i < 10; i++) {
2605 Local<Value> result = script->Run();
2606 CHECK_EQ(result, v8_str("x"));
2607 }
2608}
2609
Andrei Popescu31002712010-02-23 13:46:05 +00002610THREADED_TEST(DefinePropertyOnAPIAccessor) {
2611 v8::HandleScope scope;
2612 Local<ObjectTemplate> templ = ObjectTemplate::New();
2613 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"));
2614 LocalContext context;
2615 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2616
2617 // Uses getOwnPropertyDescriptor to check the configurable status
2618 Local<Script> script_desc
Leon Clarkef7060e22010-06-03 12:02:55 +01002619 = Script::Compile(v8_str("var prop = Object.getOwnPropertyDescriptor( "
Andrei Popescu31002712010-02-23 13:46:05 +00002620 "obj, 'x');"
2621 "prop.configurable;"));
2622 Local<Value> result = script_desc->Run();
2623 CHECK_EQ(result->BooleanValue(), true);
2624
2625 // Redefine get - but still configurable
2626 Local<Script> script_define
2627 = Script::Compile(v8_str("var desc = { get: function(){return 42; },"
2628 " configurable: true };"
2629 "Object.defineProperty(obj, 'x', desc);"
2630 "obj.x"));
2631 result = script_define->Run();
2632 CHECK_EQ(result, v8_num(42));
2633
2634 // Check that the accessor is still configurable
2635 result = script_desc->Run();
2636 CHECK_EQ(result->BooleanValue(), true);
2637
2638 // Redefine to a non-configurable
2639 script_define
2640 = Script::Compile(v8_str("var desc = { get: function(){return 43; },"
2641 " configurable: false };"
2642 "Object.defineProperty(obj, 'x', desc);"
2643 "obj.x"));
2644 result = script_define->Run();
2645 CHECK_EQ(result, v8_num(43));
2646 result = script_desc->Run();
2647 CHECK_EQ(result->BooleanValue(), false);
2648
2649 // Make sure that it is not possible to redefine again
2650 v8::TryCatch try_catch;
2651 result = script_define->Run();
2652 CHECK(try_catch.HasCaught());
2653 String::AsciiValue exception_value(try_catch.Exception());
2654 CHECK_EQ(*exception_value,
2655 "TypeError: Cannot redefine property: defineProperty");
2656}
2657
2658THREADED_TEST(DefinePropertyOnDefineGetterSetter) {
2659 v8::HandleScope scope;
2660 Local<ObjectTemplate> templ = ObjectTemplate::New();
2661 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"));
2662 LocalContext context;
2663 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2664
2665 Local<Script> script_desc = Script::Compile(v8_str("var prop ="
2666 "Object.getOwnPropertyDescriptor( "
2667 "obj, 'x');"
2668 "prop.configurable;"));
2669 Local<Value> result = script_desc->Run();
2670 CHECK_EQ(result->BooleanValue(), true);
2671
2672 Local<Script> script_define =
2673 Script::Compile(v8_str("var desc = {get: function(){return 42; },"
2674 " configurable: true };"
2675 "Object.defineProperty(obj, 'x', desc);"
2676 "obj.x"));
2677 result = script_define->Run();
2678 CHECK_EQ(result, v8_num(42));
2679
2680
2681 result = script_desc->Run();
2682 CHECK_EQ(result->BooleanValue(), true);
2683
2684
2685 script_define =
2686 Script::Compile(v8_str("var desc = {get: function(){return 43; },"
2687 " configurable: false };"
2688 "Object.defineProperty(obj, 'x', desc);"
2689 "obj.x"));
2690 result = script_define->Run();
2691 CHECK_EQ(result, v8_num(43));
2692 result = script_desc->Run();
2693
2694 CHECK_EQ(result->BooleanValue(), false);
2695
2696 v8::TryCatch try_catch;
2697 result = script_define->Run();
2698 CHECK(try_catch.HasCaught());
2699 String::AsciiValue exception_value(try_catch.Exception());
2700 CHECK_EQ(*exception_value,
2701 "TypeError: Cannot redefine property: defineProperty");
2702}
2703
2704
Leon Clarkef7060e22010-06-03 12:02:55 +01002705static v8::Handle<v8::Object> GetGlobalProperty(LocalContext* context,
2706 char const* name) {
2707 return v8::Handle<v8::Object>::Cast((*context)->Global()->Get(v8_str(name)));
2708}
Andrei Popescu31002712010-02-23 13:46:05 +00002709
2710
Leon Clarkef7060e22010-06-03 12:02:55 +01002711THREADED_TEST(DefineAPIAccessorOnObject) {
2712 v8::HandleScope scope;
2713 Local<ObjectTemplate> templ = ObjectTemplate::New();
2714 LocalContext context;
2715
2716 context->Global()->Set(v8_str("obj1"), templ->NewInstance());
2717 CompileRun("var obj2 = {};");
2718
2719 CHECK(CompileRun("obj1.x")->IsUndefined());
2720 CHECK(CompileRun("obj2.x")->IsUndefined());
2721
2722 CHECK(GetGlobalProperty(&context, "obj1")->
2723 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2724
2725 ExpectString("obj1.x", "x");
2726 CHECK(CompileRun("obj2.x")->IsUndefined());
2727
2728 CHECK(GetGlobalProperty(&context, "obj2")->
2729 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2730
2731 ExpectString("obj1.x", "x");
2732 ExpectString("obj2.x", "x");
2733
2734 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2735 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2736
2737 CompileRun("Object.defineProperty(obj1, 'x',"
2738 "{ get: function() { return 'y'; }, configurable: true })");
2739
2740 ExpectString("obj1.x", "y");
2741 ExpectString("obj2.x", "x");
2742
2743 CompileRun("Object.defineProperty(obj2, 'x',"
2744 "{ get: function() { return 'y'; }, configurable: true })");
2745
2746 ExpectString("obj1.x", "y");
2747 ExpectString("obj2.x", "y");
2748
2749 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2750 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2751
2752 CHECK(GetGlobalProperty(&context, "obj1")->
2753 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2754 CHECK(GetGlobalProperty(&context, "obj2")->
2755 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2756
2757 ExpectString("obj1.x", "x");
2758 ExpectString("obj2.x", "x");
2759
2760 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2761 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2762
2763 // Define getters/setters, but now make them not configurable.
2764 CompileRun("Object.defineProperty(obj1, 'x',"
2765 "{ get: function() { return 'z'; }, configurable: false })");
2766 CompileRun("Object.defineProperty(obj2, 'x',"
2767 "{ get: function() { return 'z'; }, configurable: false })");
2768
2769 ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2770 ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2771
2772 ExpectString("obj1.x", "z");
2773 ExpectString("obj2.x", "z");
2774
2775 CHECK(!GetGlobalProperty(&context, "obj1")->
2776 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2777 CHECK(!GetGlobalProperty(&context, "obj2")->
2778 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2779
2780 ExpectString("obj1.x", "z");
2781 ExpectString("obj2.x", "z");
2782}
2783
2784
2785THREADED_TEST(DontDeleteAPIAccessorsCannotBeOverriden) {
2786 v8::HandleScope scope;
2787 Local<ObjectTemplate> templ = ObjectTemplate::New();
2788 LocalContext context;
2789
2790 context->Global()->Set(v8_str("obj1"), templ->NewInstance());
2791 CompileRun("var obj2 = {};");
2792
2793 CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor(
2794 v8_str("x"),
2795 GetXValue, NULL,
2796 v8_str("donut"), v8::DEFAULT, v8::DontDelete));
2797 CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor(
2798 v8_str("x"),
2799 GetXValue, NULL,
2800 v8_str("donut"), v8::DEFAULT, v8::DontDelete));
2801
2802 ExpectString("obj1.x", "x");
2803 ExpectString("obj2.x", "x");
2804
2805 ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2806 ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2807
2808 CHECK(!GetGlobalProperty(&context, "obj1")->
2809 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2810 CHECK(!GetGlobalProperty(&context, "obj2")->
2811 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2812
2813 {
2814 v8::TryCatch try_catch;
2815 CompileRun("Object.defineProperty(obj1, 'x',"
2816 "{get: function() { return 'func'; }})");
2817 CHECK(try_catch.HasCaught());
2818 String::AsciiValue exception_value(try_catch.Exception());
2819 CHECK_EQ(*exception_value,
2820 "TypeError: Cannot redefine property: defineProperty");
2821 }
2822 {
2823 v8::TryCatch try_catch;
2824 CompileRun("Object.defineProperty(obj2, 'x',"
2825 "{get: function() { return 'func'; }})");
2826 CHECK(try_catch.HasCaught());
2827 String::AsciiValue exception_value(try_catch.Exception());
2828 CHECK_EQ(*exception_value,
2829 "TypeError: Cannot redefine property: defineProperty");
2830 }
2831}
2832
2833
2834static v8::Handle<Value> Get239Value(Local<String> name,
2835 const AccessorInfo& info) {
2836 ApiTestFuzzer::Fuzz();
2837 CHECK_EQ(info.Data(), v8_str("donut"));
2838 CHECK_EQ(name, v8_str("239"));
2839 return name;
2840}
2841
2842
2843THREADED_TEST(ElementAPIAccessor) {
2844 v8::HandleScope scope;
2845 Local<ObjectTemplate> templ = ObjectTemplate::New();
2846 LocalContext context;
2847
2848 context->Global()->Set(v8_str("obj1"), templ->NewInstance());
2849 CompileRun("var obj2 = {};");
2850
2851 CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor(
2852 v8_str("239"),
2853 Get239Value, NULL,
2854 v8_str("donut")));
2855 CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor(
2856 v8_str("239"),
2857 Get239Value, NULL,
2858 v8_str("donut")));
2859
2860 ExpectString("obj1[239]", "239");
2861 ExpectString("obj2[239]", "239");
2862 ExpectString("obj1['239']", "239");
2863 ExpectString("obj2['239']", "239");
2864}
2865
Steve Blocka7e24c12009-10-30 11:49:00 +00002866
2867v8::Persistent<Value> xValue;
2868
2869
2870static void SetXValue(Local<String> name,
2871 Local<Value> value,
2872 const AccessorInfo& info) {
2873 CHECK_EQ(value, v8_num(4));
2874 CHECK_EQ(info.Data(), v8_str("donut"));
2875 CHECK_EQ(name, v8_str("x"));
2876 CHECK(xValue.IsEmpty());
2877 xValue = v8::Persistent<Value>::New(value);
2878}
2879
2880
2881THREADED_TEST(SimplePropertyWrite) {
2882 v8::HandleScope scope;
2883 Local<ObjectTemplate> templ = ObjectTemplate::New();
2884 templ->SetAccessor(v8_str("x"), GetXValue, SetXValue, v8_str("donut"));
2885 LocalContext context;
2886 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2887 Local<Script> script = Script::Compile(v8_str("obj.x = 4"));
2888 for (int i = 0; i < 10; i++) {
2889 CHECK(xValue.IsEmpty());
2890 script->Run();
2891 CHECK_EQ(v8_num(4), xValue);
2892 xValue.Dispose();
2893 xValue = v8::Persistent<Value>();
2894 }
2895}
2896
2897
2898static v8::Handle<Value> XPropertyGetter(Local<String> property,
2899 const AccessorInfo& info) {
2900 ApiTestFuzzer::Fuzz();
2901 CHECK(info.Data()->IsUndefined());
2902 return property;
2903}
2904
2905
2906THREADED_TEST(NamedInterceptorPropertyRead) {
2907 v8::HandleScope scope;
2908 Local<ObjectTemplate> templ = ObjectTemplate::New();
2909 templ->SetNamedPropertyHandler(XPropertyGetter);
2910 LocalContext context;
2911 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2912 Local<Script> script = Script::Compile(v8_str("obj.x"));
2913 for (int i = 0; i < 10; i++) {
2914 Local<Value> result = script->Run();
2915 CHECK_EQ(result, v8_str("x"));
2916 }
2917}
2918
2919
Steve Block6ded16b2010-05-10 14:33:55 +01002920THREADED_TEST(NamedInterceptorDictionaryIC) {
2921 v8::HandleScope scope;
2922 Local<ObjectTemplate> templ = ObjectTemplate::New();
2923 templ->SetNamedPropertyHandler(XPropertyGetter);
2924 LocalContext context;
2925 // Create an object with a named interceptor.
2926 context->Global()->Set(v8_str("interceptor_obj"), templ->NewInstance());
2927 Local<Script> script = Script::Compile(v8_str("interceptor_obj.x"));
2928 for (int i = 0; i < 10; i++) {
2929 Local<Value> result = script->Run();
2930 CHECK_EQ(result, v8_str("x"));
2931 }
2932 // Create a slow case object and a function accessing a property in
2933 // that slow case object (with dictionary probing in generated
2934 // code). Then force object with a named interceptor into slow-case,
2935 // pass it to the function, and check that the interceptor is called
2936 // instead of accessing the local property.
2937 Local<Value> result =
2938 CompileRun("function get_x(o) { return o.x; };"
2939 "var obj = { x : 42, y : 0 };"
2940 "delete obj.y;"
2941 "for (var i = 0; i < 10; i++) get_x(obj);"
2942 "interceptor_obj.x = 42;"
2943 "interceptor_obj.y = 10;"
2944 "delete interceptor_obj.y;"
2945 "get_x(interceptor_obj)");
2946 CHECK_EQ(result, v8_str("x"));
2947}
2948
2949
Kristian Monsen0d5e1162010-09-30 15:31:59 +01002950THREADED_TEST(NamedInterceptorDictionaryICMultipleContext) {
2951 v8::HandleScope scope;
2952
2953 v8::Persistent<Context> context1 = Context::New();
2954
2955 context1->Enter();
2956 Local<ObjectTemplate> templ = ObjectTemplate::New();
2957 templ->SetNamedPropertyHandler(XPropertyGetter);
2958 // Create an object with a named interceptor.
2959 v8::Local<v8::Object> object = templ->NewInstance();
2960 context1->Global()->Set(v8_str("interceptor_obj"), object);
2961
2962 // Force the object into the slow case.
2963 CompileRun("interceptor_obj.y = 0;"
2964 "delete interceptor_obj.y;");
2965 context1->Exit();
2966
2967 {
2968 // Introduce the object into a different context.
2969 // Repeat named loads to exercise ICs.
2970 LocalContext context2;
2971 context2->Global()->Set(v8_str("interceptor_obj"), object);
2972 Local<Value> result =
2973 CompileRun("function get_x(o) { return o.x; }"
2974 "interceptor_obj.x = 42;"
2975 "for (var i=0; i != 10; i++) {"
2976 " get_x(interceptor_obj);"
2977 "}"
2978 "get_x(interceptor_obj)");
2979 // Check that the interceptor was actually invoked.
2980 CHECK_EQ(result, v8_str("x"));
2981 }
2982
2983 // Return to the original context and force some object to the slow case
2984 // to cause the NormalizedMapCache to verify.
2985 context1->Enter();
2986 CompileRun("var obj = { x : 0 }; delete obj.x;");
2987 context1->Exit();
2988
2989 context1.Dispose();
2990}
2991
2992
Andrei Popescu402d9372010-02-26 13:31:12 +00002993static v8::Handle<Value> SetXOnPrototypeGetter(Local<String> property,
2994 const AccessorInfo& info) {
2995 // Set x on the prototype object and do not handle the get request.
2996 v8::Handle<v8::Value> proto = info.Holder()->GetPrototype();
Steve Block6ded16b2010-05-10 14:33:55 +01002997 proto.As<v8::Object>()->Set(v8_str("x"), v8::Integer::New(23));
Andrei Popescu402d9372010-02-26 13:31:12 +00002998 return v8::Handle<Value>();
2999}
3000
3001
3002// This is a regression test for http://crbug.com/20104. Map
3003// transitions should not interfere with post interceptor lookup.
3004THREADED_TEST(NamedInterceptorMapTransitionRead) {
3005 v8::HandleScope scope;
3006 Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New();
3007 Local<v8::ObjectTemplate> instance_template
3008 = function_template->InstanceTemplate();
3009 instance_template->SetNamedPropertyHandler(SetXOnPrototypeGetter);
3010 LocalContext context;
3011 context->Global()->Set(v8_str("F"), function_template->GetFunction());
3012 // Create an instance of F and introduce a map transition for x.
3013 CompileRun("var o = new F(); o.x = 23;");
3014 // Create an instance of F and invoke the getter. The result should be 23.
3015 Local<Value> result = CompileRun("o = new F(); o.x");
3016 CHECK_EQ(result->Int32Value(), 23);
3017}
3018
3019
Steve Blocka7e24c12009-10-30 11:49:00 +00003020static v8::Handle<Value> IndexedPropertyGetter(uint32_t index,
3021 const AccessorInfo& info) {
3022 ApiTestFuzzer::Fuzz();
3023 if (index == 37) {
3024 return v8::Handle<Value>(v8_num(625));
3025 }
3026 return v8::Handle<Value>();
3027}
3028
3029
3030static v8::Handle<Value> IndexedPropertySetter(uint32_t index,
3031 Local<Value> value,
3032 const AccessorInfo& info) {
3033 ApiTestFuzzer::Fuzz();
3034 if (index == 39) {
3035 return value;
3036 }
3037 return v8::Handle<Value>();
3038}
3039
3040
3041THREADED_TEST(IndexedInterceptorWithIndexedAccessor) {
3042 v8::HandleScope scope;
3043 Local<ObjectTemplate> templ = ObjectTemplate::New();
3044 templ->SetIndexedPropertyHandler(IndexedPropertyGetter,
3045 IndexedPropertySetter);
3046 LocalContext context;
3047 context->Global()->Set(v8_str("obj"), templ->NewInstance());
3048 Local<Script> getter_script = Script::Compile(v8_str(
3049 "obj.__defineGetter__(\"3\", function(){return 5;});obj[3];"));
3050 Local<Script> setter_script = Script::Compile(v8_str(
3051 "obj.__defineSetter__(\"17\", function(val){this.foo = val;});"
3052 "obj[17] = 23;"
3053 "obj.foo;"));
3054 Local<Script> interceptor_setter_script = Script::Compile(v8_str(
3055 "obj.__defineSetter__(\"39\", function(val){this.foo = \"hit\";});"
3056 "obj[39] = 47;"
3057 "obj.foo;")); // This setter should not run, due to the interceptor.
3058 Local<Script> interceptor_getter_script = Script::Compile(v8_str(
3059 "obj[37];"));
3060 Local<Value> result = getter_script->Run();
3061 CHECK_EQ(v8_num(5), result);
3062 result = setter_script->Run();
3063 CHECK_EQ(v8_num(23), result);
3064 result = interceptor_setter_script->Run();
3065 CHECK_EQ(v8_num(23), result);
3066 result = interceptor_getter_script->Run();
3067 CHECK_EQ(v8_num(625), result);
3068}
3069
3070
Leon Clarked91b9f72010-01-27 17:25:45 +00003071static v8::Handle<Value> IdentityIndexedPropertyGetter(
3072 uint32_t index,
3073 const AccessorInfo& info) {
Ben Murdochf87a2032010-10-22 12:50:53 +01003074 return v8::Integer::NewFromUnsigned(index);
Leon Clarked91b9f72010-01-27 17:25:45 +00003075}
3076
3077
Kristian Monsen0d5e1162010-09-30 15:31:59 +01003078THREADED_TEST(IndexedInterceptorWithGetOwnPropertyDescriptor) {
3079 v8::HandleScope scope;
3080 Local<ObjectTemplate> templ = ObjectTemplate::New();
3081 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3082
3083 LocalContext context;
3084 context->Global()->Set(v8_str("obj"), templ->NewInstance());
3085
3086 // Check fast object case.
3087 const char* fast_case_code =
3088 "Object.getOwnPropertyDescriptor(obj, 0).value.toString()";
3089 ExpectString(fast_case_code, "0");
3090
3091 // Check slow case.
3092 const char* slow_case_code =
3093 "obj.x = 1; delete obj.x;"
3094 "Object.getOwnPropertyDescriptor(obj, 1).value.toString()";
3095 ExpectString(slow_case_code, "1");
3096}
3097
3098
Leon Clarked91b9f72010-01-27 17:25:45 +00003099THREADED_TEST(IndexedInterceptorWithNoSetter) {
3100 v8::HandleScope scope;
3101 Local<ObjectTemplate> templ = ObjectTemplate::New();
3102 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3103
3104 LocalContext context;
3105 context->Global()->Set(v8_str("obj"), templ->NewInstance());
3106
3107 const char* code =
3108 "try {"
3109 " obj[0] = 239;"
3110 " for (var i = 0; i < 100; i++) {"
3111 " var v = obj[0];"
3112 " if (v != 0) throw 'Wrong value ' + v + ' at iteration ' + i;"
3113 " }"
3114 " 'PASSED'"
3115 "} catch(e) {"
3116 " e"
3117 "}";
3118 ExpectString(code, "PASSED");
3119}
3120
3121
Andrei Popescu402d9372010-02-26 13:31:12 +00003122THREADED_TEST(IndexedInterceptorWithAccessorCheck) {
3123 v8::HandleScope scope;
3124 Local<ObjectTemplate> templ = ObjectTemplate::New();
3125 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3126
3127 LocalContext context;
3128 Local<v8::Object> obj = templ->NewInstance();
3129 obj->TurnOnAccessCheck();
3130 context->Global()->Set(v8_str("obj"), obj);
3131
3132 const char* code =
3133 "try {"
3134 " for (var i = 0; i < 100; i++) {"
3135 " var v = obj[0];"
3136 " if (v != undefined) throw 'Wrong value ' + v + ' at iteration ' + i;"
3137 " }"
3138 " 'PASSED'"
3139 "} catch(e) {"
3140 " e"
3141 "}";
3142 ExpectString(code, "PASSED");
3143}
3144
3145
3146THREADED_TEST(IndexedInterceptorWithAccessorCheckSwitchedOn) {
3147 i::FLAG_allow_natives_syntax = true;
3148 v8::HandleScope scope;
3149 Local<ObjectTemplate> templ = ObjectTemplate::New();
3150 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3151
3152 LocalContext context;
3153 Local<v8::Object> obj = templ->NewInstance();
3154 context->Global()->Set(v8_str("obj"), obj);
3155
3156 const char* code =
3157 "try {"
3158 " for (var i = 0; i < 100; i++) {"
3159 " var expected = i;"
3160 " if (i == 5) {"
3161 " %EnableAccessChecks(obj);"
3162 " expected = undefined;"
3163 " }"
3164 " var v = obj[i];"
3165 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3166 " if (i == 5) %DisableAccessChecks(obj);"
3167 " }"
3168 " 'PASSED'"
3169 "} catch(e) {"
3170 " e"
3171 "}";
3172 ExpectString(code, "PASSED");
3173}
3174
3175
3176THREADED_TEST(IndexedInterceptorWithDifferentIndices) {
3177 v8::HandleScope scope;
3178 Local<ObjectTemplate> templ = ObjectTemplate::New();
3179 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3180
3181 LocalContext context;
3182 Local<v8::Object> obj = templ->NewInstance();
3183 context->Global()->Set(v8_str("obj"), obj);
3184
3185 const char* code =
3186 "try {"
3187 " for (var i = 0; i < 100; i++) {"
3188 " var v = obj[i];"
3189 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;"
3190 " }"
3191 " 'PASSED'"
3192 "} catch(e) {"
3193 " e"
3194 "}";
3195 ExpectString(code, "PASSED");
3196}
3197
3198
Ben Murdochf87a2032010-10-22 12:50:53 +01003199THREADED_TEST(IndexedInterceptorWithNegativeIndices) {
3200 v8::HandleScope scope;
3201 Local<ObjectTemplate> templ = ObjectTemplate::New();
3202 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3203
3204 LocalContext context;
3205 Local<v8::Object> obj = templ->NewInstance();
3206 context->Global()->Set(v8_str("obj"), obj);
3207
3208 const char* code =
3209 "try {"
3210 " for (var i = 0; i < 100; i++) {"
3211 " var expected = i;"
3212 " var key = i;"
3213 " if (i == 25) {"
3214 " key = -1;"
3215 " expected = undefined;"
3216 " }"
3217 " if (i == 50) {"
3218 " /* probe minimal Smi number on 32-bit platforms */"
3219 " key = -(1 << 30);"
3220 " expected = undefined;"
3221 " }"
3222 " if (i == 75) {"
3223 " /* probe minimal Smi number on 64-bit platforms */"
3224 " key = 1 << 31;"
3225 " expected = undefined;"
3226 " }"
3227 " var v = obj[key];"
3228 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3229 " }"
3230 " 'PASSED'"
3231 "} catch(e) {"
3232 " e"
3233 "}";
3234 ExpectString(code, "PASSED");
3235}
3236
3237
Andrei Popescu402d9372010-02-26 13:31:12 +00003238THREADED_TEST(IndexedInterceptorWithNotSmiLookup) {
3239 v8::HandleScope scope;
3240 Local<ObjectTemplate> templ = ObjectTemplate::New();
3241 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3242
3243 LocalContext context;
3244 Local<v8::Object> obj = templ->NewInstance();
3245 context->Global()->Set(v8_str("obj"), obj);
3246
3247 const char* code =
3248 "try {"
3249 " for (var i = 0; i < 100; i++) {"
3250 " var expected = i;"
Ben Murdochf87a2032010-10-22 12:50:53 +01003251 " var key = i;"
Andrei Popescu402d9372010-02-26 13:31:12 +00003252 " if (i == 50) {"
Ben Murdochf87a2032010-10-22 12:50:53 +01003253 " key = 'foobar';"
Andrei Popescu402d9372010-02-26 13:31:12 +00003254 " expected = undefined;"
3255 " }"
Ben Murdochf87a2032010-10-22 12:50:53 +01003256 " var v = obj[key];"
Andrei Popescu402d9372010-02-26 13:31:12 +00003257 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3258 " }"
3259 " 'PASSED'"
3260 "} catch(e) {"
3261 " e"
3262 "}";
3263 ExpectString(code, "PASSED");
3264}
3265
3266
3267THREADED_TEST(IndexedInterceptorGoingMegamorphic) {
3268 v8::HandleScope scope;
3269 Local<ObjectTemplate> templ = ObjectTemplate::New();
3270 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3271
3272 LocalContext context;
3273 Local<v8::Object> obj = templ->NewInstance();
3274 context->Global()->Set(v8_str("obj"), obj);
3275
3276 const char* code =
3277 "var original = obj;"
3278 "try {"
3279 " for (var i = 0; i < 100; i++) {"
3280 " var expected = i;"
3281 " if (i == 50) {"
3282 " obj = {50: 'foobar'};"
3283 " expected = 'foobar';"
3284 " }"
3285 " var v = obj[i];"
3286 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3287 " if (i == 50) obj = original;"
3288 " }"
3289 " 'PASSED'"
3290 "} catch(e) {"
3291 " e"
3292 "}";
3293 ExpectString(code, "PASSED");
3294}
3295
3296
3297THREADED_TEST(IndexedInterceptorReceiverTurningSmi) {
3298 v8::HandleScope scope;
3299 Local<ObjectTemplate> templ = ObjectTemplate::New();
3300 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3301
3302 LocalContext context;
3303 Local<v8::Object> obj = templ->NewInstance();
3304 context->Global()->Set(v8_str("obj"), obj);
3305
3306 const char* code =
3307 "var original = obj;"
3308 "try {"
3309 " for (var i = 0; i < 100; i++) {"
3310 " var expected = i;"
3311 " if (i == 5) {"
3312 " obj = 239;"
3313 " expected = undefined;"
3314 " }"
3315 " var v = obj[i];"
3316 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3317 " if (i == 5) obj = original;"
3318 " }"
3319 " 'PASSED'"
3320 "} catch(e) {"
3321 " e"
3322 "}";
3323 ExpectString(code, "PASSED");
3324}
3325
3326
3327THREADED_TEST(IndexedInterceptorOnProto) {
3328 v8::HandleScope scope;
3329 Local<ObjectTemplate> templ = ObjectTemplate::New();
3330 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3331
3332 LocalContext context;
3333 Local<v8::Object> obj = templ->NewInstance();
3334 context->Global()->Set(v8_str("obj"), obj);
3335
3336 const char* code =
3337 "var o = {__proto__: obj};"
3338 "try {"
3339 " for (var i = 0; i < 100; i++) {"
3340 " var v = o[i];"
3341 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;"
3342 " }"
3343 " 'PASSED'"
3344 "} catch(e) {"
3345 " e"
3346 "}";
3347 ExpectString(code, "PASSED");
3348}
3349
3350
Steve Blocka7e24c12009-10-30 11:49:00 +00003351THREADED_TEST(MultiContexts) {
3352 v8::HandleScope scope;
3353 v8::Handle<ObjectTemplate> templ = ObjectTemplate::New();
3354 templ->Set(v8_str("dummy"), v8::FunctionTemplate::New(DummyCallHandler));
3355
3356 Local<String> password = v8_str("Password");
3357
3358 // Create an environment
3359 LocalContext context0(0, templ);
3360 context0->SetSecurityToken(password);
3361 v8::Handle<v8::Object> global0 = context0->Global();
3362 global0->Set(v8_str("custom"), v8_num(1234));
3363 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value());
3364
3365 // Create an independent environment
3366 LocalContext context1(0, templ);
3367 context1->SetSecurityToken(password);
3368 v8::Handle<v8::Object> global1 = context1->Global();
3369 global1->Set(v8_str("custom"), v8_num(1234));
3370 CHECK_NE(global0, global1);
3371 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value());
3372 CHECK_EQ(1234, global1->Get(v8_str("custom"))->Int32Value());
3373
3374 // Now create a new context with the old global
3375 LocalContext context2(0, templ, global1);
3376 context2->SetSecurityToken(password);
3377 v8::Handle<v8::Object> global2 = context2->Global();
3378 CHECK_EQ(global1, global2);
3379 CHECK_EQ(0, global1->Get(v8_str("custom"))->Int32Value());
3380 CHECK_EQ(0, global2->Get(v8_str("custom"))->Int32Value());
3381}
3382
3383
3384THREADED_TEST(FunctionPrototypeAcrossContexts) {
3385 // Make sure that functions created by cloning boilerplates cannot
3386 // communicate through their __proto__ field.
3387
3388 v8::HandleScope scope;
3389
3390 LocalContext env0;
3391 v8::Handle<v8::Object> global0 =
3392 env0->Global();
3393 v8::Handle<v8::Object> object0 =
Steve Block6ded16b2010-05-10 14:33:55 +01003394 global0->Get(v8_str("Object")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003395 v8::Handle<v8::Object> tostring0 =
Steve Block6ded16b2010-05-10 14:33:55 +01003396 object0->Get(v8_str("toString")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003397 v8::Handle<v8::Object> proto0 =
Steve Block6ded16b2010-05-10 14:33:55 +01003398 tostring0->Get(v8_str("__proto__")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003399 proto0->Set(v8_str("custom"), v8_num(1234));
3400
3401 LocalContext env1;
3402 v8::Handle<v8::Object> global1 =
3403 env1->Global();
3404 v8::Handle<v8::Object> object1 =
Steve Block6ded16b2010-05-10 14:33:55 +01003405 global1->Get(v8_str("Object")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003406 v8::Handle<v8::Object> tostring1 =
Steve Block6ded16b2010-05-10 14:33:55 +01003407 object1->Get(v8_str("toString")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003408 v8::Handle<v8::Object> proto1 =
Steve Block6ded16b2010-05-10 14:33:55 +01003409 tostring1->Get(v8_str("__proto__")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003410 CHECK(!proto1->Has(v8_str("custom")));
3411}
3412
3413
3414THREADED_TEST(Regress892105) {
3415 // Make sure that object and array literals created by cloning
3416 // boilerplates cannot communicate through their __proto__
3417 // field. This is rather difficult to check, but we try to add stuff
3418 // to Object.prototype and Array.prototype and create a new
3419 // environment. This should succeed.
3420
3421 v8::HandleScope scope;
3422
3423 Local<String> source = v8_str("Object.prototype.obj = 1234;"
3424 "Array.prototype.arr = 4567;"
3425 "8901");
3426
3427 LocalContext env0;
3428 Local<Script> script0 = Script::Compile(source);
3429 CHECK_EQ(8901.0, script0->Run()->NumberValue());
3430
3431 LocalContext env1;
3432 Local<Script> script1 = Script::Compile(source);
3433 CHECK_EQ(8901.0, script1->Run()->NumberValue());
3434}
3435
3436
Steve Blocka7e24c12009-10-30 11:49:00 +00003437THREADED_TEST(UndetectableObject) {
3438 v8::HandleScope scope;
3439 LocalContext env;
3440
3441 Local<v8::FunctionTemplate> desc =
3442 v8::FunctionTemplate::New(0, v8::Handle<Value>());
3443 desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable
3444
3445 Local<v8::Object> obj = desc->GetFunction()->NewInstance();
3446 env->Global()->Set(v8_str("undetectable"), obj);
3447
3448 ExpectString("undetectable.toString()", "[object Object]");
3449 ExpectString("typeof undetectable", "undefined");
3450 ExpectString("typeof(undetectable)", "undefined");
3451 ExpectBoolean("typeof undetectable == 'undefined'", true);
3452 ExpectBoolean("typeof undetectable == 'object'", false);
3453 ExpectBoolean("if (undetectable) { true; } else { false; }", false);
3454 ExpectBoolean("!undetectable", true);
3455
3456 ExpectObject("true&&undetectable", obj);
3457 ExpectBoolean("false&&undetectable", false);
3458 ExpectBoolean("true||undetectable", true);
3459 ExpectObject("false||undetectable", obj);
3460
3461 ExpectObject("undetectable&&true", obj);
3462 ExpectObject("undetectable&&false", obj);
3463 ExpectBoolean("undetectable||true", true);
3464 ExpectBoolean("undetectable||false", false);
3465
3466 ExpectBoolean("undetectable==null", true);
3467 ExpectBoolean("null==undetectable", true);
3468 ExpectBoolean("undetectable==undefined", true);
3469 ExpectBoolean("undefined==undetectable", true);
3470 ExpectBoolean("undetectable==undetectable", true);
3471
3472
3473 ExpectBoolean("undetectable===null", false);
3474 ExpectBoolean("null===undetectable", false);
3475 ExpectBoolean("undetectable===undefined", false);
3476 ExpectBoolean("undefined===undetectable", false);
3477 ExpectBoolean("undetectable===undetectable", true);
3478}
3479
3480
Steve Block8defd9f2010-07-08 12:39:36 +01003481
3482THREADED_TEST(ExtensibleOnUndetectable) {
3483 v8::HandleScope scope;
3484 LocalContext env;
3485
3486 Local<v8::FunctionTemplate> desc =
3487 v8::FunctionTemplate::New(0, v8::Handle<Value>());
3488 desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable
3489
3490 Local<v8::Object> obj = desc->GetFunction()->NewInstance();
3491 env->Global()->Set(v8_str("undetectable"), obj);
3492
3493 Local<String> source = v8_str("undetectable.x = 42;"
3494 "undetectable.x");
3495
3496 Local<Script> script = Script::Compile(source);
3497
3498 CHECK_EQ(v8::Integer::New(42), script->Run());
3499
3500 ExpectBoolean("Object.isExtensible(undetectable)", true);
3501
3502 source = v8_str("Object.preventExtensions(undetectable);");
3503 script = Script::Compile(source);
3504 script->Run();
3505 ExpectBoolean("Object.isExtensible(undetectable)", false);
3506
3507 source = v8_str("undetectable.y = 2000;");
3508 script = Script::Compile(source);
3509 v8::TryCatch try_catch;
3510 Local<Value> result = script->Run();
3511 CHECK(result.IsEmpty());
3512 CHECK(try_catch.HasCaught());
3513}
3514
3515
3516
Steve Blocka7e24c12009-10-30 11:49:00 +00003517THREADED_TEST(UndetectableString) {
3518 v8::HandleScope scope;
3519 LocalContext env;
3520
3521 Local<String> obj = String::NewUndetectable("foo");
3522 env->Global()->Set(v8_str("undetectable"), obj);
3523
3524 ExpectString("undetectable", "foo");
3525 ExpectString("typeof undetectable", "undefined");
3526 ExpectString("typeof(undetectable)", "undefined");
3527 ExpectBoolean("typeof undetectable == 'undefined'", true);
3528 ExpectBoolean("typeof undetectable == 'string'", false);
3529 ExpectBoolean("if (undetectable) { true; } else { false; }", false);
3530 ExpectBoolean("!undetectable", true);
3531
3532 ExpectObject("true&&undetectable", obj);
3533 ExpectBoolean("false&&undetectable", false);
3534 ExpectBoolean("true||undetectable", true);
3535 ExpectObject("false||undetectable", obj);
3536
3537 ExpectObject("undetectable&&true", obj);
3538 ExpectObject("undetectable&&false", obj);
3539 ExpectBoolean("undetectable||true", true);
3540 ExpectBoolean("undetectable||false", false);
3541
3542 ExpectBoolean("undetectable==null", true);
3543 ExpectBoolean("null==undetectable", true);
3544 ExpectBoolean("undetectable==undefined", true);
3545 ExpectBoolean("undefined==undetectable", true);
3546 ExpectBoolean("undetectable==undetectable", true);
3547
3548
3549 ExpectBoolean("undetectable===null", false);
3550 ExpectBoolean("null===undetectable", false);
3551 ExpectBoolean("undetectable===undefined", false);
3552 ExpectBoolean("undefined===undetectable", false);
3553 ExpectBoolean("undetectable===undetectable", true);
3554}
3555
3556
3557template <typename T> static void USE(T) { }
3558
3559
3560// This test is not intended to be run, just type checked.
3561static void PersistentHandles() {
3562 USE(PersistentHandles);
3563 Local<String> str = v8_str("foo");
3564 v8::Persistent<String> p_str = v8::Persistent<String>::New(str);
3565 USE(p_str);
3566 Local<Script> scr = Script::Compile(v8_str(""));
3567 v8::Persistent<Script> p_scr = v8::Persistent<Script>::New(scr);
3568 USE(p_scr);
3569 Local<ObjectTemplate> templ = ObjectTemplate::New();
3570 v8::Persistent<ObjectTemplate> p_templ =
3571 v8::Persistent<ObjectTemplate>::New(templ);
3572 USE(p_templ);
3573}
3574
3575
3576static v8::Handle<Value> HandleLogDelegator(const v8::Arguments& args) {
3577 ApiTestFuzzer::Fuzz();
3578 return v8::Undefined();
3579}
3580
3581
3582THREADED_TEST(GlobalObjectTemplate) {
3583 v8::HandleScope handle_scope;
3584 Local<ObjectTemplate> global_template = ObjectTemplate::New();
3585 global_template->Set(v8_str("JSNI_Log"),
3586 v8::FunctionTemplate::New(HandleLogDelegator));
3587 v8::Persistent<Context> context = Context::New(0, global_template);
3588 Context::Scope context_scope(context);
3589 Script::Compile(v8_str("JSNI_Log('LOG')"))->Run();
3590 context.Dispose();
3591}
3592
3593
3594static const char* kSimpleExtensionSource =
3595 "function Foo() {"
3596 " return 4;"
3597 "}";
3598
3599
3600THREADED_TEST(SimpleExtensions) {
3601 v8::HandleScope handle_scope;
3602 v8::RegisterExtension(new Extension("simpletest", kSimpleExtensionSource));
3603 const char* extension_names[] = { "simpletest" };
3604 v8::ExtensionConfiguration extensions(1, extension_names);
3605 v8::Handle<Context> context = Context::New(&extensions);
3606 Context::Scope lock(context);
3607 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run();
3608 CHECK_EQ(result, v8::Integer::New(4));
3609}
3610
3611
3612static const char* kEvalExtensionSource1 =
3613 "function UseEval1() {"
3614 " var x = 42;"
3615 " return eval('x');"
3616 "}";
3617
3618
3619static const char* kEvalExtensionSource2 =
3620 "(function() {"
3621 " var x = 42;"
3622 " function e() {"
3623 " return eval('x');"
3624 " }"
3625 " this.UseEval2 = e;"
3626 "})()";
3627
3628
3629THREADED_TEST(UseEvalFromExtension) {
3630 v8::HandleScope handle_scope;
3631 v8::RegisterExtension(new Extension("evaltest1", kEvalExtensionSource1));
3632 v8::RegisterExtension(new Extension("evaltest2", kEvalExtensionSource2));
3633 const char* extension_names[] = { "evaltest1", "evaltest2" };
3634 v8::ExtensionConfiguration extensions(2, extension_names);
3635 v8::Handle<Context> context = Context::New(&extensions);
3636 Context::Scope lock(context);
3637 v8::Handle<Value> result = Script::Compile(v8_str("UseEval1()"))->Run();
3638 CHECK_EQ(result, v8::Integer::New(42));
3639 result = Script::Compile(v8_str("UseEval2()"))->Run();
3640 CHECK_EQ(result, v8::Integer::New(42));
3641}
3642
3643
3644static const char* kWithExtensionSource1 =
3645 "function UseWith1() {"
3646 " var x = 42;"
3647 " with({x:87}) { return x; }"
3648 "}";
3649
3650
3651
3652static const char* kWithExtensionSource2 =
3653 "(function() {"
3654 " var x = 42;"
3655 " function e() {"
3656 " with ({x:87}) { return x; }"
3657 " }"
3658 " this.UseWith2 = e;"
3659 "})()";
3660
3661
3662THREADED_TEST(UseWithFromExtension) {
3663 v8::HandleScope handle_scope;
3664 v8::RegisterExtension(new Extension("withtest1", kWithExtensionSource1));
3665 v8::RegisterExtension(new Extension("withtest2", kWithExtensionSource2));
3666 const char* extension_names[] = { "withtest1", "withtest2" };
3667 v8::ExtensionConfiguration extensions(2, extension_names);
3668 v8::Handle<Context> context = Context::New(&extensions);
3669 Context::Scope lock(context);
3670 v8::Handle<Value> result = Script::Compile(v8_str("UseWith1()"))->Run();
3671 CHECK_EQ(result, v8::Integer::New(87));
3672 result = Script::Compile(v8_str("UseWith2()"))->Run();
3673 CHECK_EQ(result, v8::Integer::New(87));
3674}
3675
3676
3677THREADED_TEST(AutoExtensions) {
3678 v8::HandleScope handle_scope;
3679 Extension* extension = new Extension("autotest", kSimpleExtensionSource);
3680 extension->set_auto_enable(true);
3681 v8::RegisterExtension(extension);
3682 v8::Handle<Context> context = Context::New();
3683 Context::Scope lock(context);
3684 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run();
3685 CHECK_EQ(result, v8::Integer::New(4));
3686}
3687
3688
Steve Blockd0582a62009-12-15 09:54:21 +00003689static const char* kSyntaxErrorInExtensionSource =
3690 "[";
3691
3692
3693// Test that a syntax error in an extension does not cause a fatal
3694// error but results in an empty context.
3695THREADED_TEST(SyntaxErrorExtensions) {
3696 v8::HandleScope handle_scope;
3697 v8::RegisterExtension(new Extension("syntaxerror",
3698 kSyntaxErrorInExtensionSource));
3699 const char* extension_names[] = { "syntaxerror" };
3700 v8::ExtensionConfiguration extensions(1, extension_names);
3701 v8::Handle<Context> context = Context::New(&extensions);
3702 CHECK(context.IsEmpty());
3703}
3704
3705
3706static const char* kExceptionInExtensionSource =
3707 "throw 42";
3708
3709
3710// Test that an exception when installing an extension does not cause
3711// a fatal error but results in an empty context.
3712THREADED_TEST(ExceptionExtensions) {
3713 v8::HandleScope handle_scope;
3714 v8::RegisterExtension(new Extension("exception",
3715 kExceptionInExtensionSource));
3716 const char* extension_names[] = { "exception" };
3717 v8::ExtensionConfiguration extensions(1, extension_names);
3718 v8::Handle<Context> context = Context::New(&extensions);
3719 CHECK(context.IsEmpty());
3720}
3721
3722
Iain Merrick9ac36c92010-09-13 15:29:50 +01003723static const char* kNativeCallInExtensionSource =
3724 "function call_runtime_last_index_of(x) {"
3725 " return %StringLastIndexOf(x, 'bob', 10);"
3726 "}";
3727
3728
3729static const char* kNativeCallTest =
3730 "call_runtime_last_index_of('bobbobboellebobboellebobbob');";
3731
3732// Test that a native runtime calls are supported in extensions.
3733THREADED_TEST(NativeCallInExtensions) {
3734 v8::HandleScope handle_scope;
3735 v8::RegisterExtension(new Extension("nativecall",
3736 kNativeCallInExtensionSource));
3737 const char* extension_names[] = { "nativecall" };
3738 v8::ExtensionConfiguration extensions(1, extension_names);
3739 v8::Handle<Context> context = Context::New(&extensions);
3740 Context::Scope lock(context);
3741 v8::Handle<Value> result = Script::Compile(v8_str(kNativeCallTest))->Run();
3742 CHECK_EQ(result, v8::Integer::New(3));
3743}
3744
3745
Steve Blocka7e24c12009-10-30 11:49:00 +00003746static void CheckDependencies(const char* name, const char* expected) {
3747 v8::HandleScope handle_scope;
3748 v8::ExtensionConfiguration config(1, &name);
3749 LocalContext context(&config);
3750 CHECK_EQ(String::New(expected), context->Global()->Get(v8_str("loaded")));
3751}
3752
3753
3754/*
3755 * Configuration:
3756 *
3757 * /-- B <--\
3758 * A <- -- D <-- E
3759 * \-- C <--/
3760 */
3761THREADED_TEST(ExtensionDependency) {
3762 static const char* kEDeps[] = { "D" };
3763 v8::RegisterExtension(new Extension("E", "this.loaded += 'E';", 1, kEDeps));
3764 static const char* kDDeps[] = { "B", "C" };
3765 v8::RegisterExtension(new Extension("D", "this.loaded += 'D';", 2, kDDeps));
3766 static const char* kBCDeps[] = { "A" };
3767 v8::RegisterExtension(new Extension("B", "this.loaded += 'B';", 1, kBCDeps));
3768 v8::RegisterExtension(new Extension("C", "this.loaded += 'C';", 1, kBCDeps));
3769 v8::RegisterExtension(new Extension("A", "this.loaded += 'A';"));
3770 CheckDependencies("A", "undefinedA");
3771 CheckDependencies("B", "undefinedAB");
3772 CheckDependencies("C", "undefinedAC");
3773 CheckDependencies("D", "undefinedABCD");
3774 CheckDependencies("E", "undefinedABCDE");
3775 v8::HandleScope handle_scope;
3776 static const char* exts[2] = { "C", "E" };
3777 v8::ExtensionConfiguration config(2, exts);
3778 LocalContext context(&config);
3779 CHECK_EQ(v8_str("undefinedACBDE"), context->Global()->Get(v8_str("loaded")));
3780}
3781
3782
3783static const char* kExtensionTestScript =
3784 "native function A();"
3785 "native function B();"
3786 "native function C();"
3787 "function Foo(i) {"
3788 " if (i == 0) return A();"
3789 " if (i == 1) return B();"
3790 " if (i == 2) return C();"
3791 "}";
3792
3793
3794static v8::Handle<Value> CallFun(const v8::Arguments& args) {
3795 ApiTestFuzzer::Fuzz();
Leon Clarkee46be812010-01-19 14:06:41 +00003796 if (args.IsConstructCall()) {
3797 args.This()->Set(v8_str("data"), args.Data());
3798 return v8::Null();
3799 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003800 return args.Data();
3801}
3802
3803
3804class FunctionExtension : public Extension {
3805 public:
3806 FunctionExtension() : Extension("functiontest", kExtensionTestScript) { }
3807 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(
3808 v8::Handle<String> name);
3809};
3810
3811
3812static int lookup_count = 0;
3813v8::Handle<v8::FunctionTemplate> FunctionExtension::GetNativeFunction(
3814 v8::Handle<String> name) {
3815 lookup_count++;
3816 if (name->Equals(v8_str("A"))) {
3817 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(8));
3818 } else if (name->Equals(v8_str("B"))) {
3819 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(7));
3820 } else if (name->Equals(v8_str("C"))) {
3821 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(6));
3822 } else {
3823 return v8::Handle<v8::FunctionTemplate>();
3824 }
3825}
3826
3827
3828THREADED_TEST(FunctionLookup) {
3829 v8::RegisterExtension(new FunctionExtension());
3830 v8::HandleScope handle_scope;
3831 static const char* exts[1] = { "functiontest" };
3832 v8::ExtensionConfiguration config(1, exts);
3833 LocalContext context(&config);
3834 CHECK_EQ(3, lookup_count);
3835 CHECK_EQ(v8::Integer::New(8), Script::Compile(v8_str("Foo(0)"))->Run());
3836 CHECK_EQ(v8::Integer::New(7), Script::Compile(v8_str("Foo(1)"))->Run());
3837 CHECK_EQ(v8::Integer::New(6), Script::Compile(v8_str("Foo(2)"))->Run());
3838}
3839
3840
Leon Clarkee46be812010-01-19 14:06:41 +00003841THREADED_TEST(NativeFunctionConstructCall) {
3842 v8::RegisterExtension(new FunctionExtension());
3843 v8::HandleScope handle_scope;
3844 static const char* exts[1] = { "functiontest" };
3845 v8::ExtensionConfiguration config(1, exts);
3846 LocalContext context(&config);
Leon Clarked91b9f72010-01-27 17:25:45 +00003847 for (int i = 0; i < 10; i++) {
3848 // Run a few times to ensure that allocation of objects doesn't
3849 // change behavior of a constructor function.
3850 CHECK_EQ(v8::Integer::New(8),
3851 Script::Compile(v8_str("(new A()).data"))->Run());
3852 CHECK_EQ(v8::Integer::New(7),
3853 Script::Compile(v8_str("(new B()).data"))->Run());
3854 CHECK_EQ(v8::Integer::New(6),
3855 Script::Compile(v8_str("(new C()).data"))->Run());
3856 }
Leon Clarkee46be812010-01-19 14:06:41 +00003857}
3858
3859
Steve Blocka7e24c12009-10-30 11:49:00 +00003860static const char* last_location;
3861static const char* last_message;
3862void StoringErrorCallback(const char* location, const char* message) {
3863 if (last_location == NULL) {
3864 last_location = location;
3865 last_message = message;
3866 }
3867}
3868
3869
3870// ErrorReporting creates a circular extensions configuration and
3871// tests that the fatal error handler gets called. This renders V8
3872// unusable and therefore this test cannot be run in parallel.
3873TEST(ErrorReporting) {
3874 v8::V8::SetFatalErrorHandler(StoringErrorCallback);
3875 static const char* aDeps[] = { "B" };
3876 v8::RegisterExtension(new Extension("A", "", 1, aDeps));
3877 static const char* bDeps[] = { "A" };
3878 v8::RegisterExtension(new Extension("B", "", 1, bDeps));
3879 last_location = NULL;
3880 v8::ExtensionConfiguration config(1, bDeps);
3881 v8::Handle<Context> context = Context::New(&config);
3882 CHECK(context.IsEmpty());
3883 CHECK_NE(last_location, NULL);
3884}
3885
3886
3887static const char* js_code_causing_huge_string_flattening =
3888 "var str = 'X';"
3889 "for (var i = 0; i < 30; i++) {"
3890 " str = str + str;"
3891 "}"
3892 "str.match(/X/);";
3893
3894
3895void OOMCallback(const char* location, const char* message) {
3896 exit(0);
3897}
3898
3899
3900TEST(RegexpOutOfMemory) {
3901 // Execute a script that causes out of memory when flattening a string.
3902 v8::HandleScope scope;
3903 v8::V8::SetFatalErrorHandler(OOMCallback);
3904 LocalContext context;
3905 Local<Script> script =
3906 Script::Compile(String::New(js_code_causing_huge_string_flattening));
3907 last_location = NULL;
3908 Local<Value> result = script->Run();
3909
3910 CHECK(false); // Should not return.
3911}
3912
3913
3914static void MissingScriptInfoMessageListener(v8::Handle<v8::Message> message,
3915 v8::Handle<Value> data) {
3916 CHECK_EQ(v8::Undefined(), data);
3917 CHECK(message->GetScriptResourceName()->IsUndefined());
3918 CHECK_EQ(v8::Undefined(), message->GetScriptResourceName());
3919 message->GetLineNumber();
3920 message->GetSourceLine();
3921}
3922
3923
3924THREADED_TEST(ErrorWithMissingScriptInfo) {
3925 v8::HandleScope scope;
3926 LocalContext context;
3927 v8::V8::AddMessageListener(MissingScriptInfoMessageListener);
3928 Script::Compile(v8_str("throw Error()"))->Run();
3929 v8::V8::RemoveMessageListeners(MissingScriptInfoMessageListener);
3930}
3931
3932
3933int global_index = 0;
3934
3935class Snorkel {
3936 public:
3937 Snorkel() { index_ = global_index++; }
3938 int index_;
3939};
3940
3941class Whammy {
3942 public:
3943 Whammy() {
3944 cursor_ = 0;
3945 }
3946 ~Whammy() {
3947 script_.Dispose();
3948 }
3949 v8::Handle<Script> getScript() {
3950 if (script_.IsEmpty())
3951 script_ = v8::Persistent<Script>::New(v8_compile("({}).blammo"));
3952 return Local<Script>(*script_);
3953 }
3954
3955 public:
3956 static const int kObjectCount = 256;
3957 int cursor_;
3958 v8::Persistent<v8::Object> objects_[kObjectCount];
3959 v8::Persistent<Script> script_;
3960};
3961
3962static void HandleWeakReference(v8::Persistent<v8::Value> obj, void* data) {
3963 Snorkel* snorkel = reinterpret_cast<Snorkel*>(data);
3964 delete snorkel;
3965 obj.ClearWeak();
3966}
3967
3968v8::Handle<Value> WhammyPropertyGetter(Local<String> name,
3969 const AccessorInfo& info) {
3970 Whammy* whammy =
3971 static_cast<Whammy*>(v8::Handle<v8::External>::Cast(info.Data())->Value());
3972
3973 v8::Persistent<v8::Object> prev = whammy->objects_[whammy->cursor_];
3974
3975 v8::Handle<v8::Object> obj = v8::Object::New();
3976 v8::Persistent<v8::Object> global = v8::Persistent<v8::Object>::New(obj);
3977 if (!prev.IsEmpty()) {
3978 prev->Set(v8_str("next"), obj);
3979 prev.MakeWeak(new Snorkel(), &HandleWeakReference);
3980 whammy->objects_[whammy->cursor_].Clear();
3981 }
3982 whammy->objects_[whammy->cursor_] = global;
3983 whammy->cursor_ = (whammy->cursor_ + 1) % Whammy::kObjectCount;
3984 return whammy->getScript()->Run();
3985}
3986
3987THREADED_TEST(WeakReference) {
3988 v8::HandleScope handle_scope;
3989 v8::Handle<v8::ObjectTemplate> templ= v8::ObjectTemplate::New();
Ben Murdoch3bec4d22010-07-22 14:51:16 +01003990 Whammy* whammy = new Whammy();
Steve Blocka7e24c12009-10-30 11:49:00 +00003991 templ->SetNamedPropertyHandler(WhammyPropertyGetter,
3992 0, 0, 0, 0,
Ben Murdoch3bec4d22010-07-22 14:51:16 +01003993 v8::External::New(whammy));
Steve Blocka7e24c12009-10-30 11:49:00 +00003994 const char* extension_list[] = { "v8/gc" };
3995 v8::ExtensionConfiguration extensions(1, extension_list);
3996 v8::Persistent<Context> context = Context::New(&extensions);
3997 Context::Scope context_scope(context);
3998
3999 v8::Handle<v8::Object> interceptor = templ->NewInstance();
4000 context->Global()->Set(v8_str("whammy"), interceptor);
4001 const char* code =
4002 "var last;"
4003 "for (var i = 0; i < 10000; i++) {"
4004 " var obj = whammy.length;"
4005 " if (last) last.next = obj;"
4006 " last = obj;"
4007 "}"
4008 "gc();"
4009 "4";
4010 v8::Handle<Value> result = CompileRun(code);
4011 CHECK_EQ(4.0, result->NumberValue());
Ben Murdoch3bec4d22010-07-22 14:51:16 +01004012 delete whammy;
Steve Blocka7e24c12009-10-30 11:49:00 +00004013 context.Dispose();
4014}
4015
4016
Steve Blockd0582a62009-12-15 09:54:21 +00004017static bool in_scavenge = false;
4018static int last = -1;
4019
4020static void ForceScavenge(v8::Persistent<v8::Value> obj, void* data) {
4021 CHECK_EQ(-1, last);
4022 last = 0;
4023 obj.Dispose();
4024 obj.Clear();
4025 in_scavenge = true;
4026 i::Heap::PerformScavenge();
4027 in_scavenge = false;
4028 *(reinterpret_cast<bool*>(data)) = true;
4029}
4030
4031static void CheckIsNotInvokedInScavenge(v8::Persistent<v8::Value> obj,
4032 void* data) {
4033 CHECK_EQ(0, last);
4034 last = 1;
4035 *(reinterpret_cast<bool*>(data)) = in_scavenge;
4036 obj.Dispose();
4037 obj.Clear();
4038}
4039
4040THREADED_TEST(NoWeakRefCallbacksInScavenge) {
4041 // Test verifies that scavenge cannot invoke WeakReferenceCallbacks.
4042 // Calling callbacks from scavenges is unsafe as objects held by those
4043 // handlers might have become strongly reachable, but scavenge doesn't
4044 // check that.
4045 v8::Persistent<Context> context = Context::New();
4046 Context::Scope context_scope(context);
4047
4048 v8::Persistent<v8::Object> object_a;
4049 v8::Persistent<v8::Object> object_b;
4050
4051 {
4052 v8::HandleScope handle_scope;
4053 object_b = v8::Persistent<v8::Object>::New(v8::Object::New());
4054 object_a = v8::Persistent<v8::Object>::New(v8::Object::New());
4055 }
4056
4057 bool object_a_disposed = false;
4058 object_a.MakeWeak(&object_a_disposed, &ForceScavenge);
4059 bool released_in_scavenge = false;
4060 object_b.MakeWeak(&released_in_scavenge, &CheckIsNotInvokedInScavenge);
4061
4062 while (!object_a_disposed) {
4063 i::Heap::CollectAllGarbage(false);
4064 }
4065 CHECK(!released_in_scavenge);
4066}
4067
4068
Steve Blocka7e24c12009-10-30 11:49:00 +00004069v8::Handle<Function> args_fun;
4070
4071
4072static v8::Handle<Value> ArgumentsTestCallback(const v8::Arguments& args) {
4073 ApiTestFuzzer::Fuzz();
4074 CHECK_EQ(args_fun, args.Callee());
4075 CHECK_EQ(3, args.Length());
4076 CHECK_EQ(v8::Integer::New(1), args[0]);
4077 CHECK_EQ(v8::Integer::New(2), args[1]);
4078 CHECK_EQ(v8::Integer::New(3), args[2]);
4079 CHECK_EQ(v8::Undefined(), args[3]);
4080 v8::HandleScope scope;
4081 i::Heap::CollectAllGarbage(false);
4082 return v8::Undefined();
4083}
4084
4085
4086THREADED_TEST(Arguments) {
4087 v8::HandleScope scope;
4088 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New();
4089 global->Set(v8_str("f"), v8::FunctionTemplate::New(ArgumentsTestCallback));
4090 LocalContext context(NULL, global);
Steve Block6ded16b2010-05-10 14:33:55 +01004091 args_fun = context->Global()->Get(v8_str("f")).As<Function>();
Steve Blocka7e24c12009-10-30 11:49:00 +00004092 v8_compile("f(1, 2, 3)")->Run();
4093}
4094
4095
Steve Blocka7e24c12009-10-30 11:49:00 +00004096static v8::Handle<Value> NoBlockGetterX(Local<String> name,
4097 const AccessorInfo&) {
4098 return v8::Handle<Value>();
4099}
4100
4101
4102static v8::Handle<Value> NoBlockGetterI(uint32_t index,
4103 const AccessorInfo&) {
4104 return v8::Handle<Value>();
4105}
4106
4107
4108static v8::Handle<v8::Boolean> PDeleter(Local<String> name,
4109 const AccessorInfo&) {
4110 if (!name->Equals(v8_str("foo"))) {
4111 return v8::Handle<v8::Boolean>(); // not intercepted
4112 }
4113
4114 return v8::False(); // intercepted, and don't delete the property
4115}
4116
4117
4118static v8::Handle<v8::Boolean> IDeleter(uint32_t index, const AccessorInfo&) {
4119 if (index != 2) {
4120 return v8::Handle<v8::Boolean>(); // not intercepted
4121 }
4122
4123 return v8::False(); // intercepted, and don't delete the property
4124}
4125
4126
4127THREADED_TEST(Deleter) {
4128 v8::HandleScope scope;
4129 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4130 obj->SetNamedPropertyHandler(NoBlockGetterX, NULL, NULL, PDeleter, NULL);
4131 obj->SetIndexedPropertyHandler(NoBlockGetterI, NULL, NULL, IDeleter, NULL);
4132 LocalContext context;
4133 context->Global()->Set(v8_str("k"), obj->NewInstance());
4134 CompileRun(
4135 "k.foo = 'foo';"
4136 "k.bar = 'bar';"
4137 "k[2] = 2;"
4138 "k[4] = 4;");
4139 CHECK(v8_compile("delete k.foo")->Run()->IsFalse());
4140 CHECK(v8_compile("delete k.bar")->Run()->IsTrue());
4141
4142 CHECK_EQ(v8_compile("k.foo")->Run(), v8_str("foo"));
4143 CHECK(v8_compile("k.bar")->Run()->IsUndefined());
4144
4145 CHECK(v8_compile("delete k[2]")->Run()->IsFalse());
4146 CHECK(v8_compile("delete k[4]")->Run()->IsTrue());
4147
4148 CHECK_EQ(v8_compile("k[2]")->Run(), v8_num(2));
4149 CHECK(v8_compile("k[4]")->Run()->IsUndefined());
4150}
4151
4152
4153static v8::Handle<Value> GetK(Local<String> name, const AccessorInfo&) {
4154 ApiTestFuzzer::Fuzz();
4155 if (name->Equals(v8_str("foo")) ||
4156 name->Equals(v8_str("bar")) ||
4157 name->Equals(v8_str("baz"))) {
4158 return v8::Undefined();
4159 }
4160 return v8::Handle<Value>();
4161}
4162
4163
4164static v8::Handle<Value> IndexedGetK(uint32_t index, const AccessorInfo&) {
4165 ApiTestFuzzer::Fuzz();
4166 if (index == 0 || index == 1) return v8::Undefined();
4167 return v8::Handle<Value>();
4168}
4169
4170
4171static v8::Handle<v8::Array> NamedEnum(const AccessorInfo&) {
4172 ApiTestFuzzer::Fuzz();
4173 v8::Handle<v8::Array> result = v8::Array::New(3);
4174 result->Set(v8::Integer::New(0), v8_str("foo"));
4175 result->Set(v8::Integer::New(1), v8_str("bar"));
4176 result->Set(v8::Integer::New(2), v8_str("baz"));
4177 return result;
4178}
4179
4180
4181static v8::Handle<v8::Array> IndexedEnum(const AccessorInfo&) {
4182 ApiTestFuzzer::Fuzz();
4183 v8::Handle<v8::Array> result = v8::Array::New(2);
4184 result->Set(v8::Integer::New(0), v8_str("0"));
4185 result->Set(v8::Integer::New(1), v8_str("1"));
4186 return result;
4187}
4188
4189
4190THREADED_TEST(Enumerators) {
4191 v8::HandleScope scope;
4192 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4193 obj->SetNamedPropertyHandler(GetK, NULL, NULL, NULL, NamedEnum);
4194 obj->SetIndexedPropertyHandler(IndexedGetK, NULL, NULL, NULL, IndexedEnum);
4195 LocalContext context;
4196 context->Global()->Set(v8_str("k"), obj->NewInstance());
4197 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun(
4198 "k[10] = 0;"
4199 "k.a = 0;"
4200 "k[5] = 0;"
4201 "k.b = 0;"
4202 "k[4294967295] = 0;"
4203 "k.c = 0;"
4204 "k[4294967296] = 0;"
4205 "k.d = 0;"
4206 "k[140000] = 0;"
4207 "k.e = 0;"
4208 "k[30000000000] = 0;"
4209 "k.f = 0;"
4210 "var result = [];"
4211 "for (var prop in k) {"
4212 " result.push(prop);"
4213 "}"
4214 "result"));
4215 // Check that we get all the property names returned including the
4216 // ones from the enumerators in the right order: indexed properties
4217 // in numerical order, indexed interceptor properties, named
4218 // properties in insertion order, named interceptor properties.
4219 // This order is not mandated by the spec, so this test is just
4220 // documenting our behavior.
4221 CHECK_EQ(17, result->Length());
4222 // Indexed properties in numerical order.
4223 CHECK_EQ(v8_str("5"), result->Get(v8::Integer::New(0)));
4224 CHECK_EQ(v8_str("10"), result->Get(v8::Integer::New(1)));
4225 CHECK_EQ(v8_str("140000"), result->Get(v8::Integer::New(2)));
4226 CHECK_EQ(v8_str("4294967295"), result->Get(v8::Integer::New(3)));
4227 // Indexed interceptor properties in the order they are returned
4228 // from the enumerator interceptor.
4229 CHECK_EQ(v8_str("0"), result->Get(v8::Integer::New(4)));
4230 CHECK_EQ(v8_str("1"), result->Get(v8::Integer::New(5)));
4231 // Named properties in insertion order.
4232 CHECK_EQ(v8_str("a"), result->Get(v8::Integer::New(6)));
4233 CHECK_EQ(v8_str("b"), result->Get(v8::Integer::New(7)));
4234 CHECK_EQ(v8_str("c"), result->Get(v8::Integer::New(8)));
4235 CHECK_EQ(v8_str("4294967296"), result->Get(v8::Integer::New(9)));
4236 CHECK_EQ(v8_str("d"), result->Get(v8::Integer::New(10)));
4237 CHECK_EQ(v8_str("e"), result->Get(v8::Integer::New(11)));
4238 CHECK_EQ(v8_str("30000000000"), result->Get(v8::Integer::New(12)));
4239 CHECK_EQ(v8_str("f"), result->Get(v8::Integer::New(13)));
4240 // Named interceptor properties.
4241 CHECK_EQ(v8_str("foo"), result->Get(v8::Integer::New(14)));
4242 CHECK_EQ(v8_str("bar"), result->Get(v8::Integer::New(15)));
4243 CHECK_EQ(v8_str("baz"), result->Get(v8::Integer::New(16)));
4244}
4245
4246
4247int p_getter_count;
4248int p_getter_count2;
4249
4250
4251static v8::Handle<Value> PGetter(Local<String> name, const AccessorInfo& info) {
4252 ApiTestFuzzer::Fuzz();
4253 p_getter_count++;
4254 v8::Handle<v8::Object> global = Context::GetCurrent()->Global();
4255 CHECK_EQ(info.Holder(), global->Get(v8_str("o1")));
4256 if (name->Equals(v8_str("p1"))) {
4257 CHECK_EQ(info.This(), global->Get(v8_str("o1")));
4258 } else if (name->Equals(v8_str("p2"))) {
4259 CHECK_EQ(info.This(), global->Get(v8_str("o2")));
4260 } else if (name->Equals(v8_str("p3"))) {
4261 CHECK_EQ(info.This(), global->Get(v8_str("o3")));
4262 } else if (name->Equals(v8_str("p4"))) {
4263 CHECK_EQ(info.This(), global->Get(v8_str("o4")));
4264 }
4265 return v8::Undefined();
4266}
4267
4268
4269static void RunHolderTest(v8::Handle<v8::ObjectTemplate> obj) {
4270 ApiTestFuzzer::Fuzz();
4271 LocalContext context;
4272 context->Global()->Set(v8_str("o1"), obj->NewInstance());
4273 CompileRun(
4274 "o1.__proto__ = { };"
4275 "var o2 = { __proto__: o1 };"
4276 "var o3 = { __proto__: o2 };"
4277 "var o4 = { __proto__: o3 };"
4278 "for (var i = 0; i < 10; i++) o4.p4;"
4279 "for (var i = 0; i < 10; i++) o3.p3;"
4280 "for (var i = 0; i < 10; i++) o2.p2;"
4281 "for (var i = 0; i < 10; i++) o1.p1;");
4282}
4283
4284
4285static v8::Handle<Value> PGetter2(Local<String> name,
4286 const AccessorInfo& info) {
4287 ApiTestFuzzer::Fuzz();
4288 p_getter_count2++;
4289 v8::Handle<v8::Object> global = Context::GetCurrent()->Global();
4290 CHECK_EQ(info.Holder(), global->Get(v8_str("o1")));
4291 if (name->Equals(v8_str("p1"))) {
4292 CHECK_EQ(info.This(), global->Get(v8_str("o1")));
4293 } else if (name->Equals(v8_str("p2"))) {
4294 CHECK_EQ(info.This(), global->Get(v8_str("o2")));
4295 } else if (name->Equals(v8_str("p3"))) {
4296 CHECK_EQ(info.This(), global->Get(v8_str("o3")));
4297 } else if (name->Equals(v8_str("p4"))) {
4298 CHECK_EQ(info.This(), global->Get(v8_str("o4")));
4299 }
4300 return v8::Undefined();
4301}
4302
4303
4304THREADED_TEST(GetterHolders) {
4305 v8::HandleScope scope;
4306 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4307 obj->SetAccessor(v8_str("p1"), PGetter);
4308 obj->SetAccessor(v8_str("p2"), PGetter);
4309 obj->SetAccessor(v8_str("p3"), PGetter);
4310 obj->SetAccessor(v8_str("p4"), PGetter);
4311 p_getter_count = 0;
4312 RunHolderTest(obj);
4313 CHECK_EQ(40, p_getter_count);
4314}
4315
4316
4317THREADED_TEST(PreInterceptorHolders) {
4318 v8::HandleScope scope;
4319 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4320 obj->SetNamedPropertyHandler(PGetter2);
4321 p_getter_count2 = 0;
4322 RunHolderTest(obj);
4323 CHECK_EQ(40, p_getter_count2);
4324}
4325
4326
4327THREADED_TEST(ObjectInstantiation) {
4328 v8::HandleScope scope;
4329 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
4330 templ->SetAccessor(v8_str("t"), PGetter2);
4331 LocalContext context;
4332 context->Global()->Set(v8_str("o"), templ->NewInstance());
4333 for (int i = 0; i < 100; i++) {
4334 v8::HandleScope inner_scope;
4335 v8::Handle<v8::Object> obj = templ->NewInstance();
4336 CHECK_NE(obj, context->Global()->Get(v8_str("o")));
4337 context->Global()->Set(v8_str("o2"), obj);
4338 v8::Handle<Value> value =
4339 Script::Compile(v8_str("o.__proto__ === o2.__proto__"))->Run();
4340 CHECK_EQ(v8::True(), value);
4341 context->Global()->Set(v8_str("o"), obj);
4342 }
4343}
4344
4345
4346THREADED_TEST(StringWrite) {
4347 v8::HandleScope scope;
4348 v8::Handle<String> str = v8_str("abcde");
4349
4350 char buf[100];
4351 int len;
4352
4353 memset(buf, 0x1, sizeof(buf));
4354 len = str->WriteAscii(buf);
4355 CHECK_EQ(len, 5);
4356 CHECK_EQ(strncmp("abcde\0", buf, 6), 0);
4357
4358 memset(buf, 0x1, sizeof(buf));
4359 len = str->WriteAscii(buf, 0, 4);
4360 CHECK_EQ(len, 4);
4361 CHECK_EQ(strncmp("abcd\1", buf, 5), 0);
4362
4363 memset(buf, 0x1, sizeof(buf));
4364 len = str->WriteAscii(buf, 0, 5);
4365 CHECK_EQ(len, 5);
4366 CHECK_EQ(strncmp("abcde\1", buf, 6), 0);
4367
4368 memset(buf, 0x1, sizeof(buf));
4369 len = str->WriteAscii(buf, 0, 6);
4370 CHECK_EQ(len, 5);
4371 CHECK_EQ(strncmp("abcde\0", buf, 6), 0);
4372
4373 memset(buf, 0x1, sizeof(buf));
4374 len = str->WriteAscii(buf, 4, -1);
4375 CHECK_EQ(len, 1);
4376 CHECK_EQ(strncmp("e\0", buf, 2), 0);
4377
4378 memset(buf, 0x1, sizeof(buf));
4379 len = str->WriteAscii(buf, 4, 6);
4380 CHECK_EQ(len, 1);
4381 CHECK_EQ(strncmp("e\0", buf, 2), 0);
4382
4383 memset(buf, 0x1, sizeof(buf));
4384 len = str->WriteAscii(buf, 4, 1);
4385 CHECK_EQ(len, 1);
4386 CHECK_EQ(strncmp("e\1", buf, 2), 0);
4387}
4388
4389
4390THREADED_TEST(ToArrayIndex) {
4391 v8::HandleScope scope;
4392 LocalContext context;
4393
4394 v8::Handle<String> str = v8_str("42");
4395 v8::Handle<v8::Uint32> index = str->ToArrayIndex();
4396 CHECK(!index.IsEmpty());
4397 CHECK_EQ(42.0, index->Uint32Value());
4398 str = v8_str("42asdf");
4399 index = str->ToArrayIndex();
4400 CHECK(index.IsEmpty());
4401 str = v8_str("-42");
4402 index = str->ToArrayIndex();
4403 CHECK(index.IsEmpty());
4404 str = v8_str("4294967295");
4405 index = str->ToArrayIndex();
4406 CHECK(!index.IsEmpty());
4407 CHECK_EQ(4294967295.0, index->Uint32Value());
4408 v8::Handle<v8::Number> num = v8::Number::New(1);
4409 index = num->ToArrayIndex();
4410 CHECK(!index.IsEmpty());
4411 CHECK_EQ(1.0, index->Uint32Value());
4412 num = v8::Number::New(-1);
4413 index = num->ToArrayIndex();
4414 CHECK(index.IsEmpty());
4415 v8::Handle<v8::Object> obj = v8::Object::New();
4416 index = obj->ToArrayIndex();
4417 CHECK(index.IsEmpty());
4418}
4419
4420
4421THREADED_TEST(ErrorConstruction) {
4422 v8::HandleScope scope;
4423 LocalContext context;
4424
4425 v8::Handle<String> foo = v8_str("foo");
4426 v8::Handle<String> message = v8_str("message");
4427 v8::Handle<Value> range_error = v8::Exception::RangeError(foo);
4428 CHECK(range_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004429 v8::Handle<v8::Object> range_obj = range_error.As<v8::Object>();
4430 CHECK(range_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004431 v8::Handle<Value> reference_error = v8::Exception::ReferenceError(foo);
4432 CHECK(reference_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004433 CHECK(reference_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004434 v8::Handle<Value> syntax_error = v8::Exception::SyntaxError(foo);
4435 CHECK(syntax_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004436 CHECK(syntax_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004437 v8::Handle<Value> type_error = v8::Exception::TypeError(foo);
4438 CHECK(type_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004439 CHECK(type_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004440 v8::Handle<Value> error = v8::Exception::Error(foo);
4441 CHECK(error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004442 CHECK(error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004443}
4444
4445
4446static v8::Handle<Value> YGetter(Local<String> name, const AccessorInfo& info) {
4447 ApiTestFuzzer::Fuzz();
4448 return v8_num(10);
4449}
4450
4451
4452static void YSetter(Local<String> name,
4453 Local<Value> value,
4454 const AccessorInfo& info) {
4455 if (info.This()->Has(name)) {
4456 info.This()->Delete(name);
4457 }
4458 info.This()->Set(name, value);
4459}
4460
4461
4462THREADED_TEST(DeleteAccessor) {
4463 v8::HandleScope scope;
4464 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4465 obj->SetAccessor(v8_str("y"), YGetter, YSetter);
4466 LocalContext context;
4467 v8::Handle<v8::Object> holder = obj->NewInstance();
4468 context->Global()->Set(v8_str("holder"), holder);
4469 v8::Handle<Value> result = CompileRun(
4470 "holder.y = 11; holder.y = 12; holder.y");
4471 CHECK_EQ(12, result->Uint32Value());
4472}
4473
4474
4475THREADED_TEST(TypeSwitch) {
4476 v8::HandleScope scope;
4477 v8::Handle<v8::FunctionTemplate> templ1 = v8::FunctionTemplate::New();
4478 v8::Handle<v8::FunctionTemplate> templ2 = v8::FunctionTemplate::New();
4479 v8::Handle<v8::FunctionTemplate> templ3 = v8::FunctionTemplate::New();
4480 v8::Handle<v8::FunctionTemplate> templs[3] = { templ1, templ2, templ3 };
4481 v8::Handle<v8::TypeSwitch> type_switch = v8::TypeSwitch::New(3, templs);
4482 LocalContext context;
4483 v8::Handle<v8::Object> obj0 = v8::Object::New();
4484 v8::Handle<v8::Object> obj1 = templ1->GetFunction()->NewInstance();
4485 v8::Handle<v8::Object> obj2 = templ2->GetFunction()->NewInstance();
4486 v8::Handle<v8::Object> obj3 = templ3->GetFunction()->NewInstance();
4487 for (int i = 0; i < 10; i++) {
4488 CHECK_EQ(0, type_switch->match(obj0));
4489 CHECK_EQ(1, type_switch->match(obj1));
4490 CHECK_EQ(2, type_switch->match(obj2));
4491 CHECK_EQ(3, type_switch->match(obj3));
4492 CHECK_EQ(3, type_switch->match(obj3));
4493 CHECK_EQ(2, type_switch->match(obj2));
4494 CHECK_EQ(1, type_switch->match(obj1));
4495 CHECK_EQ(0, type_switch->match(obj0));
4496 }
4497}
4498
4499
4500// For use within the TestSecurityHandler() test.
4501static bool g_security_callback_result = false;
4502static bool NamedSecurityTestCallback(Local<v8::Object> global,
4503 Local<Value> name,
4504 v8::AccessType type,
4505 Local<Value> data) {
4506 // Always allow read access.
4507 if (type == v8::ACCESS_GET)
4508 return true;
4509
4510 // Sometimes allow other access.
4511 return g_security_callback_result;
4512}
4513
4514
4515static bool IndexedSecurityTestCallback(Local<v8::Object> global,
4516 uint32_t key,
4517 v8::AccessType type,
4518 Local<Value> data) {
4519 // Always allow read access.
4520 if (type == v8::ACCESS_GET)
4521 return true;
4522
4523 // Sometimes allow other access.
4524 return g_security_callback_result;
4525}
4526
4527
4528static int trouble_nesting = 0;
4529static v8::Handle<Value> TroubleCallback(const v8::Arguments& args) {
4530 ApiTestFuzzer::Fuzz();
4531 trouble_nesting++;
4532
4533 // Call a JS function that throws an uncaught exception.
4534 Local<v8::Object> arg_this = Context::GetCurrent()->Global();
4535 Local<Value> trouble_callee = (trouble_nesting == 3) ?
4536 arg_this->Get(v8_str("trouble_callee")) :
4537 arg_this->Get(v8_str("trouble_caller"));
4538 CHECK(trouble_callee->IsFunction());
4539 return Function::Cast(*trouble_callee)->Call(arg_this, 0, NULL);
4540}
4541
4542
4543static int report_count = 0;
4544static void ApiUncaughtExceptionTestListener(v8::Handle<v8::Message>,
4545 v8::Handle<Value>) {
4546 report_count++;
4547}
4548
4549
4550// Counts uncaught exceptions, but other tests running in parallel
4551// also have uncaught exceptions.
4552TEST(ApiUncaughtException) {
4553 report_count = 0;
4554 v8::HandleScope scope;
4555 LocalContext env;
4556 v8::V8::AddMessageListener(ApiUncaughtExceptionTestListener);
4557
4558 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback);
4559 v8::Local<v8::Object> global = env->Global();
4560 global->Set(v8_str("trouble"), fun->GetFunction());
4561
4562 Script::Compile(v8_str("function trouble_callee() {"
4563 " var x = null;"
4564 " return x.foo;"
4565 "};"
4566 "function trouble_caller() {"
4567 " trouble();"
4568 "};"))->Run();
4569 Local<Value> trouble = global->Get(v8_str("trouble"));
4570 CHECK(trouble->IsFunction());
4571 Local<Value> trouble_callee = global->Get(v8_str("trouble_callee"));
4572 CHECK(trouble_callee->IsFunction());
4573 Local<Value> trouble_caller = global->Get(v8_str("trouble_caller"));
4574 CHECK(trouble_caller->IsFunction());
4575 Function::Cast(*trouble_caller)->Call(global, 0, NULL);
4576 CHECK_EQ(1, report_count);
4577 v8::V8::RemoveMessageListeners(ApiUncaughtExceptionTestListener);
4578}
4579
Leon Clarke4515c472010-02-03 11:58:03 +00004580static const char* script_resource_name = "ExceptionInNativeScript.js";
4581static void ExceptionInNativeScriptTestListener(v8::Handle<v8::Message> message,
4582 v8::Handle<Value>) {
4583 v8::Handle<v8::Value> name_val = message->GetScriptResourceName();
4584 CHECK(!name_val.IsEmpty() && name_val->IsString());
4585 v8::String::AsciiValue name(message->GetScriptResourceName());
4586 CHECK_EQ(script_resource_name, *name);
4587 CHECK_EQ(3, message->GetLineNumber());
4588 v8::String::AsciiValue source_line(message->GetSourceLine());
4589 CHECK_EQ(" new o.foo();", *source_line);
4590}
4591
4592TEST(ExceptionInNativeScript) {
4593 v8::HandleScope scope;
4594 LocalContext env;
4595 v8::V8::AddMessageListener(ExceptionInNativeScriptTestListener);
4596
4597 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback);
4598 v8::Local<v8::Object> global = env->Global();
4599 global->Set(v8_str("trouble"), fun->GetFunction());
4600
4601 Script::Compile(v8_str("function trouble() {\n"
4602 " var o = {};\n"
4603 " new o.foo();\n"
4604 "};"), v8::String::New(script_resource_name))->Run();
4605 Local<Value> trouble = global->Get(v8_str("trouble"));
4606 CHECK(trouble->IsFunction());
4607 Function::Cast(*trouble)->Call(global, 0, NULL);
4608 v8::V8::RemoveMessageListeners(ExceptionInNativeScriptTestListener);
4609}
4610
Steve Blocka7e24c12009-10-30 11:49:00 +00004611
4612TEST(CompilationErrorUsingTryCatchHandler) {
4613 v8::HandleScope scope;
4614 LocalContext env;
4615 v8::TryCatch try_catch;
4616 Script::Compile(v8_str("This doesn't &*&@#$&*^ compile."));
4617 CHECK_NE(NULL, *try_catch.Exception());
4618 CHECK(try_catch.HasCaught());
4619}
4620
4621
4622TEST(TryCatchFinallyUsingTryCatchHandler) {
4623 v8::HandleScope scope;
4624 LocalContext env;
4625 v8::TryCatch try_catch;
4626 Script::Compile(v8_str("try { throw ''; } catch (e) {}"))->Run();
4627 CHECK(!try_catch.HasCaught());
4628 Script::Compile(v8_str("try { throw ''; } finally {}"))->Run();
4629 CHECK(try_catch.HasCaught());
4630 try_catch.Reset();
4631 Script::Compile(v8_str("(function() {"
4632 "try { throw ''; } finally { return; }"
4633 "})()"))->Run();
4634 CHECK(!try_catch.HasCaught());
4635 Script::Compile(v8_str("(function()"
4636 " { try { throw ''; } finally { throw 0; }"
4637 "})()"))->Run();
4638 CHECK(try_catch.HasCaught());
4639}
4640
4641
4642// SecurityHandler can't be run twice
4643TEST(SecurityHandler) {
4644 v8::HandleScope scope0;
4645 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
4646 global_template->SetAccessCheckCallbacks(NamedSecurityTestCallback,
4647 IndexedSecurityTestCallback);
4648 // Create an environment
4649 v8::Persistent<Context> context0 =
4650 Context::New(NULL, global_template);
4651 context0->Enter();
4652
4653 v8::Handle<v8::Object> global0 = context0->Global();
4654 v8::Handle<Script> script0 = v8_compile("foo = 111");
4655 script0->Run();
4656 global0->Set(v8_str("0"), v8_num(999));
4657 v8::Handle<Value> foo0 = global0->Get(v8_str("foo"));
4658 CHECK_EQ(111, foo0->Int32Value());
4659 v8::Handle<Value> z0 = global0->Get(v8_str("0"));
4660 CHECK_EQ(999, z0->Int32Value());
4661
4662 // Create another environment, should fail security checks.
4663 v8::HandleScope scope1;
4664
4665 v8::Persistent<Context> context1 =
4666 Context::New(NULL, global_template);
4667 context1->Enter();
4668
4669 v8::Handle<v8::Object> global1 = context1->Global();
4670 global1->Set(v8_str("othercontext"), global0);
4671 // This set will fail the security check.
4672 v8::Handle<Script> script1 =
4673 v8_compile("othercontext.foo = 222; othercontext[0] = 888;");
4674 script1->Run();
4675 // This read will pass the security check.
4676 v8::Handle<Value> foo1 = global0->Get(v8_str("foo"));
4677 CHECK_EQ(111, foo1->Int32Value());
4678 // This read will pass the security check.
4679 v8::Handle<Value> z1 = global0->Get(v8_str("0"));
4680 CHECK_EQ(999, z1->Int32Value());
4681
4682 // Create another environment, should pass security checks.
4683 { g_security_callback_result = true; // allow security handler to pass.
4684 v8::HandleScope scope2;
4685 LocalContext context2;
4686 v8::Handle<v8::Object> global2 = context2->Global();
4687 global2->Set(v8_str("othercontext"), global0);
4688 v8::Handle<Script> script2 =
4689 v8_compile("othercontext.foo = 333; othercontext[0] = 888;");
4690 script2->Run();
4691 v8::Handle<Value> foo2 = global0->Get(v8_str("foo"));
4692 CHECK_EQ(333, foo2->Int32Value());
4693 v8::Handle<Value> z2 = global0->Get(v8_str("0"));
4694 CHECK_EQ(888, z2->Int32Value());
4695 }
4696
4697 context1->Exit();
4698 context1.Dispose();
4699
4700 context0->Exit();
4701 context0.Dispose();
4702}
4703
4704
4705THREADED_TEST(SecurityChecks) {
4706 v8::HandleScope handle_scope;
4707 LocalContext env1;
4708 v8::Persistent<Context> env2 = Context::New();
4709
4710 Local<Value> foo = v8_str("foo");
4711 Local<Value> bar = v8_str("bar");
4712
4713 // Set to the same domain.
4714 env1->SetSecurityToken(foo);
4715
4716 // Create a function in env1.
4717 Script::Compile(v8_str("spy=function(){return spy;}"))->Run();
4718 Local<Value> spy = env1->Global()->Get(v8_str("spy"));
4719 CHECK(spy->IsFunction());
4720
4721 // Create another function accessing global objects.
4722 Script::Compile(v8_str("spy2=function(){return new this.Array();}"))->Run();
4723 Local<Value> spy2 = env1->Global()->Get(v8_str("spy2"));
4724 CHECK(spy2->IsFunction());
4725
4726 // Switch to env2 in the same domain and invoke spy on env2.
4727 {
4728 env2->SetSecurityToken(foo);
4729 // Enter env2
4730 Context::Scope scope_env2(env2);
4731 Local<Value> result = Function::Cast(*spy)->Call(env2->Global(), 0, NULL);
4732 CHECK(result->IsFunction());
4733 }
4734
4735 {
4736 env2->SetSecurityToken(bar);
4737 Context::Scope scope_env2(env2);
4738
4739 // Call cross_domain_call, it should throw an exception
4740 v8::TryCatch try_catch;
4741 Function::Cast(*spy2)->Call(env2->Global(), 0, NULL);
4742 CHECK(try_catch.HasCaught());
4743 }
4744
4745 env2.Dispose();
4746}
4747
4748
4749// Regression test case for issue 1183439.
4750THREADED_TEST(SecurityChecksForPrototypeChain) {
4751 v8::HandleScope scope;
4752 LocalContext current;
4753 v8::Persistent<Context> other = Context::New();
4754
4755 // Change context to be able to get to the Object function in the
4756 // other context without hitting the security checks.
4757 v8::Local<Value> other_object;
4758 { Context::Scope scope(other);
4759 other_object = other->Global()->Get(v8_str("Object"));
4760 other->Global()->Set(v8_num(42), v8_num(87));
4761 }
4762
4763 current->Global()->Set(v8_str("other"), other->Global());
4764 CHECK(v8_compile("other")->Run()->Equals(other->Global()));
4765
4766 // Make sure the security check fails here and we get an undefined
4767 // result instead of getting the Object function. Repeat in a loop
4768 // to make sure to exercise the IC code.
4769 v8::Local<Script> access_other0 = v8_compile("other.Object");
4770 v8::Local<Script> access_other1 = v8_compile("other[42]");
4771 for (int i = 0; i < 5; i++) {
4772 CHECK(!access_other0->Run()->Equals(other_object));
4773 CHECK(access_other0->Run()->IsUndefined());
4774 CHECK(!access_other1->Run()->Equals(v8_num(87)));
4775 CHECK(access_other1->Run()->IsUndefined());
4776 }
4777
4778 // Create an object that has 'other' in its prototype chain and make
4779 // sure we cannot access the Object function indirectly through
4780 // that. Repeat in a loop to make sure to exercise the IC code.
4781 v8_compile("function F() { };"
4782 "F.prototype = other;"
4783 "var f = new F();")->Run();
4784 v8::Local<Script> access_f0 = v8_compile("f.Object");
4785 v8::Local<Script> access_f1 = v8_compile("f[42]");
4786 for (int j = 0; j < 5; j++) {
4787 CHECK(!access_f0->Run()->Equals(other_object));
4788 CHECK(access_f0->Run()->IsUndefined());
4789 CHECK(!access_f1->Run()->Equals(v8_num(87)));
4790 CHECK(access_f1->Run()->IsUndefined());
4791 }
4792
4793 // Now it gets hairy: Set the prototype for the other global object
4794 // to be the current global object. The prototype chain for 'f' now
4795 // goes through 'other' but ends up in the current global object.
4796 { Context::Scope scope(other);
4797 other->Global()->Set(v8_str("__proto__"), current->Global());
4798 }
4799 // Set a named and an index property on the current global
4800 // object. To force the lookup to go through the other global object,
4801 // the properties must not exist in the other global object.
4802 current->Global()->Set(v8_str("foo"), v8_num(100));
4803 current->Global()->Set(v8_num(99), v8_num(101));
4804 // Try to read the properties from f and make sure that the access
4805 // gets stopped by the security checks on the other global object.
4806 Local<Script> access_f2 = v8_compile("f.foo");
4807 Local<Script> access_f3 = v8_compile("f[99]");
4808 for (int k = 0; k < 5; k++) {
4809 CHECK(!access_f2->Run()->Equals(v8_num(100)));
4810 CHECK(access_f2->Run()->IsUndefined());
4811 CHECK(!access_f3->Run()->Equals(v8_num(101)));
4812 CHECK(access_f3->Run()->IsUndefined());
4813 }
4814 other.Dispose();
4815}
4816
4817
4818THREADED_TEST(CrossDomainDelete) {
4819 v8::HandleScope handle_scope;
4820 LocalContext env1;
4821 v8::Persistent<Context> env2 = Context::New();
4822
4823 Local<Value> foo = v8_str("foo");
4824 Local<Value> bar = v8_str("bar");
4825
4826 // Set to the same domain.
4827 env1->SetSecurityToken(foo);
4828 env2->SetSecurityToken(foo);
4829
4830 env1->Global()->Set(v8_str("prop"), v8_num(3));
4831 env2->Global()->Set(v8_str("env1"), env1->Global());
4832
4833 // Change env2 to a different domain and delete env1.prop.
4834 env2->SetSecurityToken(bar);
4835 {
4836 Context::Scope scope_env2(env2);
4837 Local<Value> result =
4838 Script::Compile(v8_str("delete env1.prop"))->Run();
4839 CHECK(result->IsFalse());
4840 }
4841
4842 // Check that env1.prop still exists.
4843 Local<Value> v = env1->Global()->Get(v8_str("prop"));
4844 CHECK(v->IsNumber());
4845 CHECK_EQ(3, v->Int32Value());
4846
4847 env2.Dispose();
4848}
4849
4850
4851THREADED_TEST(CrossDomainIsPropertyEnumerable) {
4852 v8::HandleScope handle_scope;
4853 LocalContext env1;
4854 v8::Persistent<Context> env2 = Context::New();
4855
4856 Local<Value> foo = v8_str("foo");
4857 Local<Value> bar = v8_str("bar");
4858
4859 // Set to the same domain.
4860 env1->SetSecurityToken(foo);
4861 env2->SetSecurityToken(foo);
4862
4863 env1->Global()->Set(v8_str("prop"), v8_num(3));
4864 env2->Global()->Set(v8_str("env1"), env1->Global());
4865
4866 // env1.prop is enumerable in env2.
4867 Local<String> test = v8_str("propertyIsEnumerable.call(env1, 'prop')");
4868 {
4869 Context::Scope scope_env2(env2);
4870 Local<Value> result = Script::Compile(test)->Run();
4871 CHECK(result->IsTrue());
4872 }
4873
4874 // Change env2 to a different domain and test again.
4875 env2->SetSecurityToken(bar);
4876 {
4877 Context::Scope scope_env2(env2);
4878 Local<Value> result = Script::Compile(test)->Run();
4879 CHECK(result->IsFalse());
4880 }
4881
4882 env2.Dispose();
4883}
4884
4885
4886THREADED_TEST(CrossDomainForIn) {
4887 v8::HandleScope handle_scope;
4888 LocalContext env1;
4889 v8::Persistent<Context> env2 = Context::New();
4890
4891 Local<Value> foo = v8_str("foo");
4892 Local<Value> bar = v8_str("bar");
4893
4894 // Set to the same domain.
4895 env1->SetSecurityToken(foo);
4896 env2->SetSecurityToken(foo);
4897
4898 env1->Global()->Set(v8_str("prop"), v8_num(3));
4899 env2->Global()->Set(v8_str("env1"), env1->Global());
4900
4901 // Change env2 to a different domain and set env1's global object
4902 // as the __proto__ of an object in env2 and enumerate properties
4903 // in for-in. It shouldn't enumerate properties on env1's global
4904 // object.
4905 env2->SetSecurityToken(bar);
4906 {
4907 Context::Scope scope_env2(env2);
4908 Local<Value> result =
4909 CompileRun("(function(){var obj = {'__proto__':env1};"
4910 "for (var p in obj)"
4911 " if (p == 'prop') return false;"
4912 "return true;})()");
4913 CHECK(result->IsTrue());
4914 }
4915 env2.Dispose();
4916}
4917
4918
4919TEST(ContextDetachGlobal) {
4920 v8::HandleScope handle_scope;
4921 LocalContext env1;
4922 v8::Persistent<Context> env2 = Context::New();
4923
4924 Local<v8::Object> global1 = env1->Global();
4925
4926 Local<Value> foo = v8_str("foo");
4927
4928 // Set to the same domain.
4929 env1->SetSecurityToken(foo);
4930 env2->SetSecurityToken(foo);
4931
4932 // Enter env2
4933 env2->Enter();
4934
Andrei Popescu74b3c142010-03-29 12:03:09 +01004935 // Create a function in env2 and add a reference to it in env1.
Steve Blocka7e24c12009-10-30 11:49:00 +00004936 Local<v8::Object> global2 = env2->Global();
4937 global2->Set(v8_str("prop"), v8::Integer::New(1));
4938 CompileRun("function getProp() {return prop;}");
4939
4940 env1->Global()->Set(v8_str("getProp"),
4941 global2->Get(v8_str("getProp")));
4942
Andrei Popescu74b3c142010-03-29 12:03:09 +01004943 // Detach env2's global, and reuse the global object of env2
Steve Blocka7e24c12009-10-30 11:49:00 +00004944 env2->Exit();
4945 env2->DetachGlobal();
4946 // env2 has a new global object.
4947 CHECK(!env2->Global()->Equals(global2));
4948
4949 v8::Persistent<Context> env3 =
4950 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2);
4951 env3->SetSecurityToken(v8_str("bar"));
4952 env3->Enter();
4953
4954 Local<v8::Object> global3 = env3->Global();
4955 CHECK_EQ(global2, global3);
4956 CHECK(global3->Get(v8_str("prop"))->IsUndefined());
4957 CHECK(global3->Get(v8_str("getProp"))->IsUndefined());
4958 global3->Set(v8_str("prop"), v8::Integer::New(-1));
4959 global3->Set(v8_str("prop2"), v8::Integer::New(2));
4960 env3->Exit();
4961
4962 // Call getProp in env1, and it should return the value 1
4963 {
4964 Local<Value> get_prop = global1->Get(v8_str("getProp"));
4965 CHECK(get_prop->IsFunction());
4966 v8::TryCatch try_catch;
4967 Local<Value> r = Function::Cast(*get_prop)->Call(global1, 0, NULL);
4968 CHECK(!try_catch.HasCaught());
4969 CHECK_EQ(1, r->Int32Value());
4970 }
4971
4972 // Check that env3 is not accessible from env1
4973 {
4974 Local<Value> r = global3->Get(v8_str("prop2"));
4975 CHECK(r->IsUndefined());
4976 }
4977
4978 env2.Dispose();
4979 env3.Dispose();
4980}
4981
4982
Andrei Popescu74b3c142010-03-29 12:03:09 +01004983TEST(DetachAndReattachGlobal) {
4984 v8::HandleScope scope;
4985 LocalContext env1;
4986
4987 // Create second environment.
4988 v8::Persistent<Context> env2 = Context::New();
4989
4990 Local<Value> foo = v8_str("foo");
4991
4992 // Set same security token for env1 and env2.
4993 env1->SetSecurityToken(foo);
4994 env2->SetSecurityToken(foo);
4995
4996 // Create a property on the global object in env2.
4997 {
4998 v8::Context::Scope scope(env2);
4999 env2->Global()->Set(v8_str("p"), v8::Integer::New(42));
5000 }
5001
5002 // Create a reference to env2 global from env1 global.
5003 env1->Global()->Set(v8_str("other"), env2->Global());
5004
5005 // Check that we have access to other.p in env2 from env1.
5006 Local<Value> result = CompileRun("other.p");
5007 CHECK(result->IsInt32());
5008 CHECK_EQ(42, result->Int32Value());
5009
5010 // Hold on to global from env2 and detach global from env2.
5011 Local<v8::Object> global2 = env2->Global();
5012 env2->DetachGlobal();
5013
5014 // Check that the global has been detached. No other.p property can
5015 // be found.
5016 result = CompileRun("other.p");
5017 CHECK(result->IsUndefined());
5018
5019 // Reuse global2 for env3.
5020 v8::Persistent<Context> env3 =
5021 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2);
5022 CHECK_EQ(global2, env3->Global());
5023
5024 // Start by using the same security token for env3 as for env1 and env2.
5025 env3->SetSecurityToken(foo);
5026
5027 // Create a property on the global object in env3.
5028 {
5029 v8::Context::Scope scope(env3);
5030 env3->Global()->Set(v8_str("p"), v8::Integer::New(24));
5031 }
5032
5033 // Check that other.p is now the property in env3 and that we have access.
5034 result = CompileRun("other.p");
5035 CHECK(result->IsInt32());
5036 CHECK_EQ(24, result->Int32Value());
5037
5038 // Change security token for env3 to something different from env1 and env2.
5039 env3->SetSecurityToken(v8_str("bar"));
5040
5041 // Check that we do not have access to other.p in env1. |other| is now
5042 // the global object for env3 which has a different security token,
5043 // so access should be blocked.
5044 result = CompileRun("other.p");
5045 CHECK(result->IsUndefined());
5046
5047 // Detach the global for env3 and reattach it to env2.
5048 env3->DetachGlobal();
5049 env2->ReattachGlobal(global2);
5050
5051 // Check that we have access to other.p again in env1. |other| is now
5052 // the global object for env2 which has the same security token as env1.
5053 result = CompileRun("other.p");
5054 CHECK(result->IsInt32());
5055 CHECK_EQ(42, result->Int32Value());
5056
5057 env2.Dispose();
5058 env3.Dispose();
5059}
5060
5061
Steve Blocka7e24c12009-10-30 11:49:00 +00005062static bool NamedAccessBlocker(Local<v8::Object> global,
5063 Local<Value> name,
5064 v8::AccessType type,
5065 Local<Value> data) {
5066 return Context::GetCurrent()->Global()->Equals(global);
5067}
5068
5069
5070static bool IndexedAccessBlocker(Local<v8::Object> global,
5071 uint32_t key,
5072 v8::AccessType type,
5073 Local<Value> data) {
5074 return Context::GetCurrent()->Global()->Equals(global);
5075}
5076
5077
5078static int g_echo_value = -1;
5079static v8::Handle<Value> EchoGetter(Local<String> name,
5080 const AccessorInfo& info) {
5081 return v8_num(g_echo_value);
5082}
5083
5084
5085static void EchoSetter(Local<String> name,
5086 Local<Value> value,
5087 const AccessorInfo&) {
5088 if (value->IsNumber())
5089 g_echo_value = value->Int32Value();
5090}
5091
5092
5093static v8::Handle<Value> UnreachableGetter(Local<String> name,
5094 const AccessorInfo& info) {
5095 CHECK(false); // This function should not be called..
5096 return v8::Undefined();
5097}
5098
5099
5100static void UnreachableSetter(Local<String>, Local<Value>,
5101 const AccessorInfo&) {
5102 CHECK(false); // This function should nto be called.
5103}
5104
5105
5106THREADED_TEST(AccessControl) {
5107 v8::HandleScope handle_scope;
5108 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
5109
5110 global_template->SetAccessCheckCallbacks(NamedAccessBlocker,
5111 IndexedAccessBlocker);
5112
5113 // Add an accessor accessible by cross-domain JS code.
5114 global_template->SetAccessor(
5115 v8_str("accessible_prop"),
5116 EchoGetter, EchoSetter,
5117 v8::Handle<Value>(),
5118 v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE));
5119
5120 // Add an accessor that is not accessible by cross-domain JS code.
5121 global_template->SetAccessor(v8_str("blocked_prop"),
5122 UnreachableGetter, UnreachableSetter,
5123 v8::Handle<Value>(),
5124 v8::DEFAULT);
5125
5126 // Create an environment
5127 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
5128 context0->Enter();
5129
5130 v8::Handle<v8::Object> global0 = context0->Global();
5131
5132 v8::HandleScope scope1;
5133
5134 v8::Persistent<Context> context1 = Context::New();
5135 context1->Enter();
5136
5137 v8::Handle<v8::Object> global1 = context1->Global();
5138 global1->Set(v8_str("other"), global0);
5139
5140 v8::Handle<Value> value;
5141
5142 // Access blocked property
5143 value = v8_compile("other.blocked_prop = 1")->Run();
5144 value = v8_compile("other.blocked_prop")->Run();
5145 CHECK(value->IsUndefined());
5146
5147 value = v8_compile("propertyIsEnumerable.call(other, 'blocked_prop')")->Run();
5148 CHECK(value->IsFalse());
5149
5150 // Access accessible property
5151 value = v8_compile("other.accessible_prop = 3")->Run();
5152 CHECK(value->IsNumber());
5153 CHECK_EQ(3, value->Int32Value());
Andrei Popescu31002712010-02-23 13:46:05 +00005154 CHECK_EQ(3, g_echo_value);
Steve Blocka7e24c12009-10-30 11:49:00 +00005155
5156 value = v8_compile("other.accessible_prop")->Run();
5157 CHECK(value->IsNumber());
5158 CHECK_EQ(3, value->Int32Value());
5159
5160 value =
5161 v8_compile("propertyIsEnumerable.call(other, 'accessible_prop')")->Run();
5162 CHECK(value->IsTrue());
5163
5164 // Enumeration doesn't enumerate accessors from inaccessible objects in
5165 // the prototype chain even if the accessors are in themselves accessible.
5166 Local<Value> result =
5167 CompileRun("(function(){var obj = {'__proto__':other};"
5168 "for (var p in obj)"
5169 " if (p == 'accessible_prop' || p == 'blocked_prop') {"
5170 " return false;"
5171 " }"
5172 "return true;})()");
5173 CHECK(result->IsTrue());
5174
5175 context1->Exit();
5176 context0->Exit();
5177 context1.Dispose();
5178 context0.Dispose();
5179}
5180
5181
Leon Clarke4515c472010-02-03 11:58:03 +00005182static bool GetOwnPropertyNamesNamedBlocker(Local<v8::Object> global,
5183 Local<Value> name,
5184 v8::AccessType type,
5185 Local<Value> data) {
5186 return false;
5187}
5188
5189
5190static bool GetOwnPropertyNamesIndexedBlocker(Local<v8::Object> global,
5191 uint32_t key,
5192 v8::AccessType type,
5193 Local<Value> data) {
5194 return false;
5195}
5196
5197
5198THREADED_TEST(AccessControlGetOwnPropertyNames) {
5199 v8::HandleScope handle_scope;
5200 v8::Handle<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New();
5201
5202 obj_template->Set(v8_str("x"), v8::Integer::New(42));
5203 obj_template->SetAccessCheckCallbacks(GetOwnPropertyNamesNamedBlocker,
5204 GetOwnPropertyNamesIndexedBlocker);
5205
5206 // Create an environment
5207 v8::Persistent<Context> context0 = Context::New(NULL, obj_template);
5208 context0->Enter();
5209
5210 v8::Handle<v8::Object> global0 = context0->Global();
5211
5212 v8::HandleScope scope1;
5213
5214 v8::Persistent<Context> context1 = Context::New();
5215 context1->Enter();
5216
5217 v8::Handle<v8::Object> global1 = context1->Global();
5218 global1->Set(v8_str("other"), global0);
5219 global1->Set(v8_str("object"), obj_template->NewInstance());
5220
5221 v8::Handle<Value> value;
5222
5223 // Attempt to get the property names of the other global object and
5224 // of an object that requires access checks. Accessing the other
5225 // global object should be blocked by access checks on the global
5226 // proxy object. Accessing the object that requires access checks
5227 // is blocked by the access checks on the object itself.
5228 value = CompileRun("Object.getOwnPropertyNames(other).length == 0");
5229 CHECK(value->IsTrue());
5230
5231 value = CompileRun("Object.getOwnPropertyNames(object).length == 0");
5232 CHECK(value->IsTrue());
5233
5234 context1->Exit();
5235 context0->Exit();
5236 context1.Dispose();
5237 context0.Dispose();
5238}
5239
5240
Steve Block8defd9f2010-07-08 12:39:36 +01005241static v8::Handle<v8::Array> NamedPropertyEnumerator(const AccessorInfo& info) {
5242 v8::Handle<v8::Array> result = v8::Array::New(1);
5243 result->Set(0, v8_str("x"));
5244 return result;
5245}
5246
5247
5248THREADED_TEST(GetOwnPropertyNamesWithInterceptor) {
5249 v8::HandleScope handle_scope;
5250 v8::Handle<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New();
5251
5252 obj_template->Set(v8_str("x"), v8::Integer::New(42));
5253 obj_template->SetNamedPropertyHandler(NULL, NULL, NULL, NULL,
5254 NamedPropertyEnumerator);
5255
5256 LocalContext context;
5257 v8::Handle<v8::Object> global = context->Global();
5258 global->Set(v8_str("object"), obj_template->NewInstance());
5259
5260 v8::Handle<Value> value =
5261 CompileRun("Object.getOwnPropertyNames(object).join(',')");
5262 CHECK_EQ(v8_str("x"), value);
5263}
5264
5265
Steve Blocka7e24c12009-10-30 11:49:00 +00005266static v8::Handle<Value> ConstTenGetter(Local<String> name,
5267 const AccessorInfo& info) {
5268 return v8_num(10);
5269}
5270
5271
5272THREADED_TEST(CrossDomainAccessors) {
5273 v8::HandleScope handle_scope;
5274
5275 v8::Handle<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New();
5276
5277 v8::Handle<v8::ObjectTemplate> global_template =
5278 func_template->InstanceTemplate();
5279
5280 v8::Handle<v8::ObjectTemplate> proto_template =
5281 func_template->PrototypeTemplate();
5282
5283 // Add an accessor to proto that's accessible by cross-domain JS code.
5284 proto_template->SetAccessor(v8_str("accessible"),
5285 ConstTenGetter, 0,
5286 v8::Handle<Value>(),
5287 v8::ALL_CAN_READ);
5288
5289 // Add an accessor that is not accessible by cross-domain JS code.
5290 global_template->SetAccessor(v8_str("unreachable"),
5291 UnreachableGetter, 0,
5292 v8::Handle<Value>(),
5293 v8::DEFAULT);
5294
5295 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
5296 context0->Enter();
5297
5298 Local<v8::Object> global = context0->Global();
5299 // Add a normal property that shadows 'accessible'
5300 global->Set(v8_str("accessible"), v8_num(11));
5301
5302 // Enter a new context.
5303 v8::HandleScope scope1;
5304 v8::Persistent<Context> context1 = Context::New();
5305 context1->Enter();
5306
5307 v8::Handle<v8::Object> global1 = context1->Global();
5308 global1->Set(v8_str("other"), global);
5309
5310 // Should return 10, instead of 11
5311 v8::Handle<Value> value = v8_compile("other.accessible")->Run();
5312 CHECK(value->IsNumber());
5313 CHECK_EQ(10, value->Int32Value());
5314
5315 value = v8_compile("other.unreachable")->Run();
5316 CHECK(value->IsUndefined());
5317
5318 context1->Exit();
5319 context0->Exit();
5320 context1.Dispose();
5321 context0.Dispose();
5322}
5323
5324
5325static int named_access_count = 0;
5326static int indexed_access_count = 0;
5327
5328static bool NamedAccessCounter(Local<v8::Object> global,
5329 Local<Value> name,
5330 v8::AccessType type,
5331 Local<Value> data) {
5332 named_access_count++;
5333 return true;
5334}
5335
5336
5337static bool IndexedAccessCounter(Local<v8::Object> global,
5338 uint32_t key,
5339 v8::AccessType type,
5340 Local<Value> data) {
5341 indexed_access_count++;
5342 return true;
5343}
5344
5345
5346// This one is too easily disturbed by other tests.
5347TEST(AccessControlIC) {
5348 named_access_count = 0;
5349 indexed_access_count = 0;
5350
5351 v8::HandleScope handle_scope;
5352
5353 // Create an environment.
5354 v8::Persistent<Context> context0 = Context::New();
5355 context0->Enter();
5356
5357 // Create an object that requires access-check functions to be
5358 // called for cross-domain access.
5359 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
5360 object_template->SetAccessCheckCallbacks(NamedAccessCounter,
5361 IndexedAccessCounter);
5362 Local<v8::Object> object = object_template->NewInstance();
5363
5364 v8::HandleScope scope1;
5365
5366 // Create another environment.
5367 v8::Persistent<Context> context1 = Context::New();
5368 context1->Enter();
5369
5370 // Make easy access to the object from the other environment.
5371 v8::Handle<v8::Object> global1 = context1->Global();
5372 global1->Set(v8_str("obj"), object);
5373
5374 v8::Handle<Value> value;
5375
5376 // Check that the named access-control function is called every time.
5377 CompileRun("function testProp(obj) {"
5378 " for (var i = 0; i < 10; i++) obj.prop = 1;"
5379 " for (var j = 0; j < 10; j++) obj.prop;"
5380 " return obj.prop"
5381 "}");
5382 value = CompileRun("testProp(obj)");
5383 CHECK(value->IsNumber());
5384 CHECK_EQ(1, value->Int32Value());
5385 CHECK_EQ(21, named_access_count);
5386
5387 // Check that the named access-control function is called every time.
5388 CompileRun("var p = 'prop';"
5389 "function testKeyed(obj) {"
5390 " for (var i = 0; i < 10; i++) obj[p] = 1;"
5391 " for (var j = 0; j < 10; j++) obj[p];"
5392 " return obj[p];"
5393 "}");
5394 // Use obj which requires access checks. No inline caching is used
5395 // in that case.
5396 value = CompileRun("testKeyed(obj)");
5397 CHECK(value->IsNumber());
5398 CHECK_EQ(1, value->Int32Value());
5399 CHECK_EQ(42, named_access_count);
5400 // Force the inline caches into generic state and try again.
5401 CompileRun("testKeyed({ a: 0 })");
5402 CompileRun("testKeyed({ b: 0 })");
5403 value = CompileRun("testKeyed(obj)");
5404 CHECK(value->IsNumber());
5405 CHECK_EQ(1, value->Int32Value());
5406 CHECK_EQ(63, named_access_count);
5407
5408 // Check that the indexed access-control function is called every time.
5409 CompileRun("function testIndexed(obj) {"
5410 " for (var i = 0; i < 10; i++) obj[0] = 1;"
5411 " for (var j = 0; j < 10; j++) obj[0];"
5412 " return obj[0]"
5413 "}");
5414 value = CompileRun("testIndexed(obj)");
5415 CHECK(value->IsNumber());
5416 CHECK_EQ(1, value->Int32Value());
5417 CHECK_EQ(21, indexed_access_count);
5418 // Force the inline caches into generic state.
5419 CompileRun("testIndexed(new Array(1))");
5420 // Test that the indexed access check is called.
5421 value = CompileRun("testIndexed(obj)");
5422 CHECK(value->IsNumber());
5423 CHECK_EQ(1, value->Int32Value());
5424 CHECK_EQ(42, indexed_access_count);
5425
5426 // Check that the named access check is called when invoking
5427 // functions on an object that requires access checks.
5428 CompileRun("obj.f = function() {}");
5429 CompileRun("function testCallNormal(obj) {"
5430 " for (var i = 0; i < 10; i++) obj.f();"
5431 "}");
5432 CompileRun("testCallNormal(obj)");
5433 CHECK_EQ(74, named_access_count);
5434
5435 // Force obj into slow case.
5436 value = CompileRun("delete obj.prop");
5437 CHECK(value->BooleanValue());
5438 // Force inline caches into dictionary probing mode.
5439 CompileRun("var o = { x: 0 }; delete o.x; testProp(o);");
5440 // Test that the named access check is called.
5441 value = CompileRun("testProp(obj);");
5442 CHECK(value->IsNumber());
5443 CHECK_EQ(1, value->Int32Value());
5444 CHECK_EQ(96, named_access_count);
5445
5446 // Force the call inline cache into dictionary probing mode.
5447 CompileRun("o.f = function() {}; testCallNormal(o)");
5448 // Test that the named access check is still called for each
5449 // invocation of the function.
5450 value = CompileRun("testCallNormal(obj)");
5451 CHECK_EQ(106, named_access_count);
5452
5453 context1->Exit();
5454 context0->Exit();
5455 context1.Dispose();
5456 context0.Dispose();
5457}
5458
5459
5460static bool NamedAccessFlatten(Local<v8::Object> global,
5461 Local<Value> name,
5462 v8::AccessType type,
5463 Local<Value> data) {
5464 char buf[100];
5465 int len;
5466
5467 CHECK(name->IsString());
5468
5469 memset(buf, 0x1, sizeof(buf));
Steve Block6ded16b2010-05-10 14:33:55 +01005470 len = name.As<String>()->WriteAscii(buf);
Steve Blocka7e24c12009-10-30 11:49:00 +00005471 CHECK_EQ(4, len);
5472
5473 uint16_t buf2[100];
5474
5475 memset(buf, 0x1, sizeof(buf));
Steve Block6ded16b2010-05-10 14:33:55 +01005476 len = name.As<String>()->Write(buf2);
Steve Blocka7e24c12009-10-30 11:49:00 +00005477 CHECK_EQ(4, len);
5478
5479 return true;
5480}
5481
5482
5483static bool IndexedAccessFlatten(Local<v8::Object> global,
5484 uint32_t key,
5485 v8::AccessType type,
5486 Local<Value> data) {
5487 return true;
5488}
5489
5490
5491// Regression test. In access checks, operations that may cause
5492// garbage collection are not allowed. It used to be the case that
5493// using the Write operation on a string could cause a garbage
5494// collection due to flattening of the string. This is no longer the
5495// case.
5496THREADED_TEST(AccessControlFlatten) {
5497 named_access_count = 0;
5498 indexed_access_count = 0;
5499
5500 v8::HandleScope handle_scope;
5501
5502 // Create an environment.
5503 v8::Persistent<Context> context0 = Context::New();
5504 context0->Enter();
5505
5506 // Create an object that requires access-check functions to be
5507 // called for cross-domain access.
5508 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
5509 object_template->SetAccessCheckCallbacks(NamedAccessFlatten,
5510 IndexedAccessFlatten);
5511 Local<v8::Object> object = object_template->NewInstance();
5512
5513 v8::HandleScope scope1;
5514
5515 // Create another environment.
5516 v8::Persistent<Context> context1 = Context::New();
5517 context1->Enter();
5518
5519 // Make easy access to the object from the other environment.
5520 v8::Handle<v8::Object> global1 = context1->Global();
5521 global1->Set(v8_str("obj"), object);
5522
5523 v8::Handle<Value> value;
5524
5525 value = v8_compile("var p = 'as' + 'df';")->Run();
5526 value = v8_compile("obj[p];")->Run();
5527
5528 context1->Exit();
5529 context0->Exit();
5530 context1.Dispose();
5531 context0.Dispose();
5532}
5533
5534
5535static v8::Handle<Value> AccessControlNamedGetter(
5536 Local<String>, const AccessorInfo&) {
5537 return v8::Integer::New(42);
5538}
5539
5540
5541static v8::Handle<Value> AccessControlNamedSetter(
5542 Local<String>, Local<Value> value, const AccessorInfo&) {
5543 return value;
5544}
5545
5546
5547static v8::Handle<Value> AccessControlIndexedGetter(
5548 uint32_t index,
5549 const AccessorInfo& info) {
5550 return v8_num(42);
5551}
5552
5553
5554static v8::Handle<Value> AccessControlIndexedSetter(
5555 uint32_t, Local<Value> value, const AccessorInfo&) {
5556 return value;
5557}
5558
5559
5560THREADED_TEST(AccessControlInterceptorIC) {
5561 named_access_count = 0;
5562 indexed_access_count = 0;
5563
5564 v8::HandleScope handle_scope;
5565
5566 // Create an environment.
5567 v8::Persistent<Context> context0 = Context::New();
5568 context0->Enter();
5569
5570 // Create an object that requires access-check functions to be
5571 // called for cross-domain access. The object also has interceptors
5572 // interceptor.
5573 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
5574 object_template->SetAccessCheckCallbacks(NamedAccessCounter,
5575 IndexedAccessCounter);
5576 object_template->SetNamedPropertyHandler(AccessControlNamedGetter,
5577 AccessControlNamedSetter);
5578 object_template->SetIndexedPropertyHandler(AccessControlIndexedGetter,
5579 AccessControlIndexedSetter);
5580 Local<v8::Object> object = object_template->NewInstance();
5581
5582 v8::HandleScope scope1;
5583
5584 // Create another environment.
5585 v8::Persistent<Context> context1 = Context::New();
5586 context1->Enter();
5587
5588 // Make easy access to the object from the other environment.
5589 v8::Handle<v8::Object> global1 = context1->Global();
5590 global1->Set(v8_str("obj"), object);
5591
5592 v8::Handle<Value> value;
5593
5594 // Check that the named access-control function is called every time
5595 // eventhough there is an interceptor on the object.
5596 value = v8_compile("for (var i = 0; i < 10; i++) obj.x = 1;")->Run();
5597 value = v8_compile("for (var i = 0; i < 10; i++) obj.x;"
5598 "obj.x")->Run();
5599 CHECK(value->IsNumber());
5600 CHECK_EQ(42, value->Int32Value());
5601 CHECK_EQ(21, named_access_count);
5602
5603 value = v8_compile("var p = 'x';")->Run();
5604 value = v8_compile("for (var i = 0; i < 10; i++) obj[p] = 1;")->Run();
5605 value = v8_compile("for (var i = 0; i < 10; i++) obj[p];"
5606 "obj[p]")->Run();
5607 CHECK(value->IsNumber());
5608 CHECK_EQ(42, value->Int32Value());
5609 CHECK_EQ(42, named_access_count);
5610
5611 // Check that the indexed access-control function is called every
5612 // time eventhough there is an interceptor on the object.
5613 value = v8_compile("for (var i = 0; i < 10; i++) obj[0] = 1;")->Run();
5614 value = v8_compile("for (var i = 0; i < 10; i++) obj[0];"
5615 "obj[0]")->Run();
5616 CHECK(value->IsNumber());
5617 CHECK_EQ(42, value->Int32Value());
5618 CHECK_EQ(21, indexed_access_count);
5619
5620 context1->Exit();
5621 context0->Exit();
5622 context1.Dispose();
5623 context0.Dispose();
5624}
5625
5626
5627THREADED_TEST(Version) {
5628 v8::V8::GetVersion();
5629}
5630
5631
5632static v8::Handle<Value> InstanceFunctionCallback(const v8::Arguments& args) {
5633 ApiTestFuzzer::Fuzz();
5634 return v8_num(12);
5635}
5636
5637
5638THREADED_TEST(InstanceProperties) {
5639 v8::HandleScope handle_scope;
5640 LocalContext context;
5641
5642 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5643 Local<ObjectTemplate> instance = t->InstanceTemplate();
5644
5645 instance->Set(v8_str("x"), v8_num(42));
5646 instance->Set(v8_str("f"),
5647 v8::FunctionTemplate::New(InstanceFunctionCallback));
5648
5649 Local<Value> o = t->GetFunction()->NewInstance();
5650
5651 context->Global()->Set(v8_str("i"), o);
5652 Local<Value> value = Script::Compile(v8_str("i.x"))->Run();
5653 CHECK_EQ(42, value->Int32Value());
5654
5655 value = Script::Compile(v8_str("i.f()"))->Run();
5656 CHECK_EQ(12, value->Int32Value());
5657}
5658
5659
5660static v8::Handle<Value>
5661GlobalObjectInstancePropertiesGet(Local<String> key, const AccessorInfo&) {
5662 ApiTestFuzzer::Fuzz();
5663 return v8::Handle<Value>();
5664}
5665
5666
5667THREADED_TEST(GlobalObjectInstanceProperties) {
5668 v8::HandleScope handle_scope;
5669
5670 Local<Value> global_object;
5671
5672 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5673 t->InstanceTemplate()->SetNamedPropertyHandler(
5674 GlobalObjectInstancePropertiesGet);
5675 Local<ObjectTemplate> instance_template = t->InstanceTemplate();
5676 instance_template->Set(v8_str("x"), v8_num(42));
5677 instance_template->Set(v8_str("f"),
5678 v8::FunctionTemplate::New(InstanceFunctionCallback));
5679
5680 {
5681 LocalContext env(NULL, instance_template);
5682 // Hold on to the global object so it can be used again in another
5683 // environment initialization.
5684 global_object = env->Global();
5685
5686 Local<Value> value = Script::Compile(v8_str("x"))->Run();
5687 CHECK_EQ(42, value->Int32Value());
5688 value = Script::Compile(v8_str("f()"))->Run();
5689 CHECK_EQ(12, value->Int32Value());
5690 }
5691
5692 {
5693 // Create new environment reusing the global object.
5694 LocalContext env(NULL, instance_template, global_object);
5695 Local<Value> value = Script::Compile(v8_str("x"))->Run();
5696 CHECK_EQ(42, value->Int32Value());
5697 value = Script::Compile(v8_str("f()"))->Run();
5698 CHECK_EQ(12, value->Int32Value());
5699 }
5700}
5701
5702
5703static v8::Handle<Value> ShadowFunctionCallback(const v8::Arguments& args) {
5704 ApiTestFuzzer::Fuzz();
5705 return v8_num(42);
5706}
5707
5708
5709static int shadow_y;
5710static int shadow_y_setter_call_count;
5711static int shadow_y_getter_call_count;
5712
5713
5714static void ShadowYSetter(Local<String>, Local<Value>, const AccessorInfo&) {
5715 shadow_y_setter_call_count++;
5716 shadow_y = 42;
5717}
5718
5719
5720static v8::Handle<Value> ShadowYGetter(Local<String> name,
5721 const AccessorInfo& info) {
5722 ApiTestFuzzer::Fuzz();
5723 shadow_y_getter_call_count++;
5724 return v8_num(shadow_y);
5725}
5726
5727
5728static v8::Handle<Value> ShadowIndexedGet(uint32_t index,
5729 const AccessorInfo& info) {
5730 return v8::Handle<Value>();
5731}
5732
5733
5734static v8::Handle<Value> ShadowNamedGet(Local<String> key,
5735 const AccessorInfo&) {
5736 return v8::Handle<Value>();
5737}
5738
5739
5740THREADED_TEST(ShadowObject) {
5741 shadow_y = shadow_y_setter_call_count = shadow_y_getter_call_count = 0;
5742 v8::HandleScope handle_scope;
5743
5744 Local<ObjectTemplate> global_template = v8::ObjectTemplate::New();
5745 LocalContext context(NULL, global_template);
5746
5747 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5748 t->InstanceTemplate()->SetNamedPropertyHandler(ShadowNamedGet);
5749 t->InstanceTemplate()->SetIndexedPropertyHandler(ShadowIndexedGet);
5750 Local<ObjectTemplate> proto = t->PrototypeTemplate();
5751 Local<ObjectTemplate> instance = t->InstanceTemplate();
5752
5753 // Only allow calls of f on instances of t.
5754 Local<v8::Signature> signature = v8::Signature::New(t);
5755 proto->Set(v8_str("f"),
5756 v8::FunctionTemplate::New(ShadowFunctionCallback,
5757 Local<Value>(),
5758 signature));
5759 proto->Set(v8_str("x"), v8_num(12));
5760
5761 instance->SetAccessor(v8_str("y"), ShadowYGetter, ShadowYSetter);
5762
5763 Local<Value> o = t->GetFunction()->NewInstance();
5764 context->Global()->Set(v8_str("__proto__"), o);
5765
5766 Local<Value> value =
5767 Script::Compile(v8_str("propertyIsEnumerable(0)"))->Run();
5768 CHECK(value->IsBoolean());
5769 CHECK(!value->BooleanValue());
5770
5771 value = Script::Compile(v8_str("x"))->Run();
5772 CHECK_EQ(12, value->Int32Value());
5773
5774 value = Script::Compile(v8_str("f()"))->Run();
5775 CHECK_EQ(42, value->Int32Value());
5776
5777 Script::Compile(v8_str("y = 42"))->Run();
5778 CHECK_EQ(1, shadow_y_setter_call_count);
5779 value = Script::Compile(v8_str("y"))->Run();
5780 CHECK_EQ(1, shadow_y_getter_call_count);
5781 CHECK_EQ(42, value->Int32Value());
5782}
5783
5784
5785THREADED_TEST(HiddenPrototype) {
5786 v8::HandleScope handle_scope;
5787 LocalContext context;
5788
5789 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New();
5790 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0));
5791 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New();
5792 t1->SetHiddenPrototype(true);
5793 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1));
5794 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New();
5795 t2->SetHiddenPrototype(true);
5796 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2));
5797 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New();
5798 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3));
5799
5800 Local<v8::Object> o0 = t0->GetFunction()->NewInstance();
5801 Local<v8::Object> o1 = t1->GetFunction()->NewInstance();
5802 Local<v8::Object> o2 = t2->GetFunction()->NewInstance();
5803 Local<v8::Object> o3 = t3->GetFunction()->NewInstance();
5804
5805 // Setting the prototype on an object skips hidden prototypes.
5806 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5807 o0->Set(v8_str("__proto__"), o1);
5808 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5809 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5810 o0->Set(v8_str("__proto__"), o2);
5811 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5812 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5813 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
5814 o0->Set(v8_str("__proto__"), o3);
5815 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5816 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5817 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
5818 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value());
5819
5820 // Getting the prototype of o0 should get the first visible one
5821 // which is o3. Therefore, z should not be defined on the prototype
5822 // object.
5823 Local<Value> proto = o0->Get(v8_str("__proto__"));
5824 CHECK(proto->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005825 CHECK(proto.As<v8::Object>()->Get(v8_str("z"))->IsUndefined());
Steve Blocka7e24c12009-10-30 11:49:00 +00005826}
5827
5828
Andrei Popescu402d9372010-02-26 13:31:12 +00005829THREADED_TEST(SetPrototype) {
5830 v8::HandleScope handle_scope;
5831 LocalContext context;
5832
5833 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New();
5834 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0));
5835 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New();
5836 t1->SetHiddenPrototype(true);
5837 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1));
5838 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New();
5839 t2->SetHiddenPrototype(true);
5840 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2));
5841 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New();
5842 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3));
5843
5844 Local<v8::Object> o0 = t0->GetFunction()->NewInstance();
5845 Local<v8::Object> o1 = t1->GetFunction()->NewInstance();
5846 Local<v8::Object> o2 = t2->GetFunction()->NewInstance();
5847 Local<v8::Object> o3 = t3->GetFunction()->NewInstance();
5848
5849 // Setting the prototype on an object does not skip hidden prototypes.
5850 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5851 CHECK(o0->SetPrototype(o1));
5852 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5853 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5854 CHECK(o1->SetPrototype(o2));
5855 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5856 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5857 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
5858 CHECK(o2->SetPrototype(o3));
5859 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5860 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5861 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
5862 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value());
5863
5864 // Getting the prototype of o0 should get the first visible one
5865 // which is o3. Therefore, z should not be defined on the prototype
5866 // object.
5867 Local<Value> proto = o0->Get(v8_str("__proto__"));
5868 CHECK(proto->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005869 CHECK_EQ(proto.As<v8::Object>(), o3);
Andrei Popescu402d9372010-02-26 13:31:12 +00005870
5871 // However, Object::GetPrototype ignores hidden prototype.
5872 Local<Value> proto0 = o0->GetPrototype();
5873 CHECK(proto0->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005874 CHECK_EQ(proto0.As<v8::Object>(), o1);
Andrei Popescu402d9372010-02-26 13:31:12 +00005875
5876 Local<Value> proto1 = o1->GetPrototype();
5877 CHECK(proto1->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005878 CHECK_EQ(proto1.As<v8::Object>(), o2);
Andrei Popescu402d9372010-02-26 13:31:12 +00005879
5880 Local<Value> proto2 = o2->GetPrototype();
5881 CHECK(proto2->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005882 CHECK_EQ(proto2.As<v8::Object>(), o3);
Andrei Popescu402d9372010-02-26 13:31:12 +00005883}
5884
5885
5886THREADED_TEST(SetPrototypeThrows) {
5887 v8::HandleScope handle_scope;
5888 LocalContext context;
5889
5890 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5891
5892 Local<v8::Object> o0 = t->GetFunction()->NewInstance();
5893 Local<v8::Object> o1 = t->GetFunction()->NewInstance();
5894
5895 CHECK(o0->SetPrototype(o1));
5896 // If setting the prototype leads to the cycle, SetPrototype should
5897 // return false and keep VM in sane state.
5898 v8::TryCatch try_catch;
5899 CHECK(!o1->SetPrototype(o0));
5900 CHECK(!try_catch.HasCaught());
5901 ASSERT(!i::Top::has_pending_exception());
5902
5903 CHECK_EQ(42, CompileRun("function f() { return 42; }; f()")->Int32Value());
5904}
5905
5906
Steve Blocka7e24c12009-10-30 11:49:00 +00005907THREADED_TEST(GetterSetterExceptions) {
5908 v8::HandleScope handle_scope;
5909 LocalContext context;
5910 CompileRun(
5911 "function Foo() { };"
5912 "function Throw() { throw 5; };"
5913 "var x = { };"
5914 "x.__defineSetter__('set', Throw);"
5915 "x.__defineGetter__('get', Throw);");
5916 Local<v8::Object> x =
5917 Local<v8::Object>::Cast(context->Global()->Get(v8_str("x")));
5918 v8::TryCatch try_catch;
5919 x->Set(v8_str("set"), v8::Integer::New(8));
5920 x->Get(v8_str("get"));
5921 x->Set(v8_str("set"), v8::Integer::New(8));
5922 x->Get(v8_str("get"));
5923 x->Set(v8_str("set"), v8::Integer::New(8));
5924 x->Get(v8_str("get"));
5925 x->Set(v8_str("set"), v8::Integer::New(8));
5926 x->Get(v8_str("get"));
5927}
5928
5929
5930THREADED_TEST(Constructor) {
5931 v8::HandleScope handle_scope;
5932 LocalContext context;
5933 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
5934 templ->SetClassName(v8_str("Fun"));
5935 Local<Function> cons = templ->GetFunction();
5936 context->Global()->Set(v8_str("Fun"), cons);
5937 Local<v8::Object> inst = cons->NewInstance();
5938 i::Handle<i::JSObject> obj = v8::Utils::OpenHandle(*inst);
5939 Local<Value> value = CompileRun("(new Fun()).constructor === Fun");
5940 CHECK(value->BooleanValue());
5941}
5942
5943THREADED_TEST(FunctionDescriptorException) {
5944 v8::HandleScope handle_scope;
5945 LocalContext context;
5946 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
5947 templ->SetClassName(v8_str("Fun"));
5948 Local<Function> cons = templ->GetFunction();
5949 context->Global()->Set(v8_str("Fun"), cons);
5950 Local<Value> value = CompileRun(
5951 "function test() {"
5952 " try {"
5953 " (new Fun()).blah()"
5954 " } catch (e) {"
5955 " var str = String(e);"
5956 " if (str.indexOf('TypeError') == -1) return 1;"
5957 " if (str.indexOf('[object Fun]') != -1) return 2;"
5958 " if (str.indexOf('#<a Fun>') == -1) return 3;"
5959 " return 0;"
5960 " }"
5961 " return 4;"
5962 "}"
5963 "test();");
5964 CHECK_EQ(0, value->Int32Value());
5965}
5966
5967
5968THREADED_TEST(EvalAliasedDynamic) {
5969 v8::HandleScope scope;
5970 LocalContext current;
5971
5972 // Tests where aliased eval can only be resolved dynamically.
5973 Local<Script> script =
5974 Script::Compile(v8_str("function f(x) { "
5975 " var foo = 2;"
5976 " with (x) { return eval('foo'); }"
5977 "}"
5978 "foo = 0;"
5979 "result1 = f(new Object());"
5980 "result2 = f(this);"
5981 "var x = new Object();"
5982 "x.eval = function(x) { return 1; };"
5983 "result3 = f(x);"));
5984 script->Run();
5985 CHECK_EQ(2, current->Global()->Get(v8_str("result1"))->Int32Value());
5986 CHECK_EQ(0, current->Global()->Get(v8_str("result2"))->Int32Value());
5987 CHECK_EQ(1, current->Global()->Get(v8_str("result3"))->Int32Value());
5988
5989 v8::TryCatch try_catch;
5990 script =
5991 Script::Compile(v8_str("function f(x) { "
5992 " var bar = 2;"
5993 " with (x) { return eval('bar'); }"
5994 "}"
5995 "f(this)"));
5996 script->Run();
5997 CHECK(try_catch.HasCaught());
5998 try_catch.Reset();
5999}
6000
6001
6002THREADED_TEST(CrossEval) {
6003 v8::HandleScope scope;
6004 LocalContext other;
6005 LocalContext current;
6006
6007 Local<String> token = v8_str("<security token>");
6008 other->SetSecurityToken(token);
6009 current->SetSecurityToken(token);
6010
6011 // Setup reference from current to other.
6012 current->Global()->Set(v8_str("other"), other->Global());
6013
6014 // Check that new variables are introduced in other context.
6015 Local<Script> script =
6016 Script::Compile(v8_str("other.eval('var foo = 1234')"));
6017 script->Run();
6018 Local<Value> foo = other->Global()->Get(v8_str("foo"));
6019 CHECK_EQ(1234, foo->Int32Value());
6020 CHECK(!current->Global()->Has(v8_str("foo")));
6021
6022 // Check that writing to non-existing properties introduces them in
6023 // the other context.
6024 script =
6025 Script::Compile(v8_str("other.eval('na = 1234')"));
6026 script->Run();
6027 CHECK_EQ(1234, other->Global()->Get(v8_str("na"))->Int32Value());
6028 CHECK(!current->Global()->Has(v8_str("na")));
6029
6030 // Check that global variables in current context are not visible in other
6031 // context.
6032 v8::TryCatch try_catch;
6033 script =
6034 Script::Compile(v8_str("var bar = 42; other.eval('bar');"));
6035 Local<Value> result = script->Run();
6036 CHECK(try_catch.HasCaught());
6037 try_catch.Reset();
6038
6039 // Check that local variables in current context are not visible in other
6040 // context.
6041 script =
6042 Script::Compile(v8_str("(function() { "
6043 " var baz = 87;"
6044 " return other.eval('baz');"
6045 "})();"));
6046 result = script->Run();
6047 CHECK(try_catch.HasCaught());
6048 try_catch.Reset();
6049
6050 // Check that global variables in the other environment are visible
6051 // when evaluting code.
6052 other->Global()->Set(v8_str("bis"), v8_num(1234));
6053 script = Script::Compile(v8_str("other.eval('bis')"));
6054 CHECK_EQ(1234, script->Run()->Int32Value());
6055 CHECK(!try_catch.HasCaught());
6056
6057 // Check that the 'this' pointer points to the global object evaluating
6058 // code.
6059 other->Global()->Set(v8_str("t"), other->Global());
6060 script = Script::Compile(v8_str("other.eval('this == t')"));
6061 result = script->Run();
6062 CHECK(result->IsTrue());
6063 CHECK(!try_catch.HasCaught());
6064
6065 // Check that variables introduced in with-statement are not visible in
6066 // other context.
6067 script =
6068 Script::Compile(v8_str("with({x:2}){other.eval('x')}"));
6069 result = script->Run();
6070 CHECK(try_catch.HasCaught());
6071 try_catch.Reset();
6072
6073 // Check that you cannot use 'eval.call' with another object than the
6074 // current global object.
6075 script =
6076 Script::Compile(v8_str("other.y = 1; eval.call(other, 'y')"));
6077 result = script->Run();
6078 CHECK(try_catch.HasCaught());
6079}
6080
6081
6082// Test that calling eval in a context which has been detached from
6083// its global throws an exception. This behavior is consistent with
6084// other JavaScript implementations.
6085THREADED_TEST(EvalInDetachedGlobal) {
6086 v8::HandleScope scope;
6087
6088 v8::Persistent<Context> context0 = Context::New();
6089 v8::Persistent<Context> context1 = Context::New();
6090
6091 // Setup function in context0 that uses eval from context0.
6092 context0->Enter();
6093 v8::Handle<v8::Value> fun =
6094 CompileRun("var x = 42;"
6095 "(function() {"
6096 " var e = eval;"
6097 " return function(s) { return e(s); }"
6098 "})()");
6099 context0->Exit();
6100
6101 // Put the function into context1 and call it before and after
6102 // detaching the global. Before detaching, the call succeeds and
6103 // after detaching and exception is thrown.
6104 context1->Enter();
6105 context1->Global()->Set(v8_str("fun"), fun);
6106 v8::Handle<v8::Value> x_value = CompileRun("fun('x')");
6107 CHECK_EQ(42, x_value->Int32Value());
6108 context0->DetachGlobal();
6109 v8::TryCatch catcher;
6110 x_value = CompileRun("fun('x')");
6111 CHECK(x_value.IsEmpty());
6112 CHECK(catcher.HasCaught());
6113 context1->Exit();
6114
6115 context1.Dispose();
6116 context0.Dispose();
6117}
6118
6119
6120THREADED_TEST(CrossLazyLoad) {
6121 v8::HandleScope scope;
6122 LocalContext other;
6123 LocalContext current;
6124
6125 Local<String> token = v8_str("<security token>");
6126 other->SetSecurityToken(token);
6127 current->SetSecurityToken(token);
6128
6129 // Setup reference from current to other.
6130 current->Global()->Set(v8_str("other"), other->Global());
6131
6132 // Trigger lazy loading in other context.
6133 Local<Script> script =
6134 Script::Compile(v8_str("other.eval('new Date(42)')"));
6135 Local<Value> value = script->Run();
6136 CHECK_EQ(42.0, value->NumberValue());
6137}
6138
6139
6140static v8::Handle<Value> call_as_function(const v8::Arguments& args) {
Andrei Popescu402d9372010-02-26 13:31:12 +00006141 ApiTestFuzzer::Fuzz();
Steve Blocka7e24c12009-10-30 11:49:00 +00006142 if (args.IsConstructCall()) {
6143 if (args[0]->IsInt32()) {
6144 return v8_num(-args[0]->Int32Value());
6145 }
6146 }
6147
6148 return args[0];
6149}
6150
6151
6152// Test that a call handler can be set for objects which will allow
6153// non-function objects created through the API to be called as
6154// functions.
6155THREADED_TEST(CallAsFunction) {
6156 v8::HandleScope scope;
6157 LocalContext context;
6158
6159 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
6160 Local<ObjectTemplate> instance_template = t->InstanceTemplate();
6161 instance_template->SetCallAsFunctionHandler(call_as_function);
6162 Local<v8::Object> instance = t->GetFunction()->NewInstance();
6163 context->Global()->Set(v8_str("obj"), instance);
6164 v8::TryCatch try_catch;
6165 Local<Value> value;
6166 CHECK(!try_catch.HasCaught());
6167
6168 value = CompileRun("obj(42)");
6169 CHECK(!try_catch.HasCaught());
6170 CHECK_EQ(42, value->Int32Value());
6171
6172 value = CompileRun("(function(o){return o(49)})(obj)");
6173 CHECK(!try_catch.HasCaught());
6174 CHECK_EQ(49, value->Int32Value());
6175
6176 // test special case of call as function
6177 value = CompileRun("[obj]['0'](45)");
6178 CHECK(!try_catch.HasCaught());
6179 CHECK_EQ(45, value->Int32Value());
6180
6181 value = CompileRun("obj.call = Function.prototype.call;"
6182 "obj.call(null, 87)");
6183 CHECK(!try_catch.HasCaught());
6184 CHECK_EQ(87, value->Int32Value());
6185
6186 // Regression tests for bug #1116356: Calling call through call/apply
6187 // must work for non-function receivers.
6188 const char* apply_99 = "Function.prototype.call.apply(obj, [this, 99])";
6189 value = CompileRun(apply_99);
6190 CHECK(!try_catch.HasCaught());
6191 CHECK_EQ(99, value->Int32Value());
6192
6193 const char* call_17 = "Function.prototype.call.call(obj, this, 17)";
6194 value = CompileRun(call_17);
6195 CHECK(!try_catch.HasCaught());
6196 CHECK_EQ(17, value->Int32Value());
6197
6198 // Check that the call-as-function handler can be called through
Leon Clarkee46be812010-01-19 14:06:41 +00006199 // new.
Steve Blocka7e24c12009-10-30 11:49:00 +00006200 value = CompileRun("new obj(43)");
6201 CHECK(!try_catch.HasCaught());
6202 CHECK_EQ(-43, value->Int32Value());
6203}
6204
6205
6206static int CountHandles() {
6207 return v8::HandleScope::NumberOfHandles();
6208}
6209
6210
6211static int Recurse(int depth, int iterations) {
6212 v8::HandleScope scope;
6213 if (depth == 0) return CountHandles();
6214 for (int i = 0; i < iterations; i++) {
6215 Local<v8::Number> n = v8::Integer::New(42);
6216 }
6217 return Recurse(depth - 1, iterations);
6218}
6219
6220
6221THREADED_TEST(HandleIteration) {
6222 static const int kIterations = 500;
6223 static const int kNesting = 200;
6224 CHECK_EQ(0, CountHandles());
6225 {
6226 v8::HandleScope scope1;
6227 CHECK_EQ(0, CountHandles());
6228 for (int i = 0; i < kIterations; i++) {
6229 Local<v8::Number> n = v8::Integer::New(42);
6230 CHECK_EQ(i + 1, CountHandles());
6231 }
6232
6233 CHECK_EQ(kIterations, CountHandles());
6234 {
6235 v8::HandleScope scope2;
6236 for (int j = 0; j < kIterations; j++) {
6237 Local<v8::Number> n = v8::Integer::New(42);
6238 CHECK_EQ(j + 1 + kIterations, CountHandles());
6239 }
6240 }
6241 CHECK_EQ(kIterations, CountHandles());
6242 }
6243 CHECK_EQ(0, CountHandles());
6244 CHECK_EQ(kNesting * kIterations, Recurse(kNesting, kIterations));
6245}
6246
6247
6248static v8::Handle<Value> InterceptorHasOwnPropertyGetter(
6249 Local<String> name,
6250 const AccessorInfo& info) {
6251 ApiTestFuzzer::Fuzz();
6252 return v8::Handle<Value>();
6253}
6254
6255
6256THREADED_TEST(InterceptorHasOwnProperty) {
6257 v8::HandleScope scope;
6258 LocalContext context;
6259 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6260 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
6261 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetter);
6262 Local<Function> function = fun_templ->GetFunction();
6263 context->Global()->Set(v8_str("constructor"), function);
6264 v8::Handle<Value> value = CompileRun(
6265 "var o = new constructor();"
6266 "o.hasOwnProperty('ostehaps');");
6267 CHECK_EQ(false, value->BooleanValue());
6268 value = CompileRun(
6269 "o.ostehaps = 42;"
6270 "o.hasOwnProperty('ostehaps');");
6271 CHECK_EQ(true, value->BooleanValue());
6272 value = CompileRun(
6273 "var p = new constructor();"
6274 "p.hasOwnProperty('ostehaps');");
6275 CHECK_EQ(false, value->BooleanValue());
6276}
6277
6278
6279static v8::Handle<Value> InterceptorHasOwnPropertyGetterGC(
6280 Local<String> name,
6281 const AccessorInfo& info) {
6282 ApiTestFuzzer::Fuzz();
6283 i::Heap::CollectAllGarbage(false);
6284 return v8::Handle<Value>();
6285}
6286
6287
6288THREADED_TEST(InterceptorHasOwnPropertyCausingGC) {
6289 v8::HandleScope scope;
6290 LocalContext context;
6291 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6292 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
6293 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetterGC);
6294 Local<Function> function = fun_templ->GetFunction();
6295 context->Global()->Set(v8_str("constructor"), function);
6296 // Let's first make some stuff so we can be sure to get a good GC.
6297 CompileRun(
6298 "function makestr(size) {"
6299 " switch (size) {"
6300 " case 1: return 'f';"
6301 " case 2: return 'fo';"
6302 " case 3: return 'foo';"
6303 " }"
6304 " return makestr(size >> 1) + makestr((size + 1) >> 1);"
6305 "}"
6306 "var x = makestr(12345);"
6307 "x = makestr(31415);"
6308 "x = makestr(23456);");
6309 v8::Handle<Value> value = CompileRun(
6310 "var o = new constructor();"
6311 "o.__proto__ = new String(x);"
6312 "o.hasOwnProperty('ostehaps');");
6313 CHECK_EQ(false, value->BooleanValue());
6314}
6315
6316
6317typedef v8::Handle<Value> (*NamedPropertyGetter)(Local<String> property,
6318 const AccessorInfo& info);
6319
6320
6321static void CheckInterceptorLoadIC(NamedPropertyGetter getter,
6322 const char* source,
6323 int expected) {
6324 v8::HandleScope scope;
6325 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6326 templ->SetNamedPropertyHandler(getter);
6327 LocalContext context;
6328 context->Global()->Set(v8_str("o"), templ->NewInstance());
6329 v8::Handle<Value> value = CompileRun(source);
6330 CHECK_EQ(expected, value->Int32Value());
6331}
6332
6333
6334static v8::Handle<Value> InterceptorLoadICGetter(Local<String> name,
6335 const AccessorInfo& info) {
6336 ApiTestFuzzer::Fuzz();
6337 CHECK(v8_str("x")->Equals(name));
6338 return v8::Integer::New(42);
6339}
6340
6341
6342// This test should hit the load IC for the interceptor case.
6343THREADED_TEST(InterceptorLoadIC) {
6344 CheckInterceptorLoadIC(InterceptorLoadICGetter,
6345 "var result = 0;"
6346 "for (var i = 0; i < 1000; i++) {"
6347 " result = o.x;"
6348 "}",
6349 42);
6350}
6351
6352
6353// Below go several tests which verify that JITing for various
6354// configurations of interceptor and explicit fields works fine
6355// (those cases are special cased to get better performance).
6356
6357static v8::Handle<Value> InterceptorLoadXICGetter(Local<String> name,
6358 const AccessorInfo& info) {
6359 ApiTestFuzzer::Fuzz();
6360 return v8_str("x")->Equals(name)
6361 ? v8::Integer::New(42) : v8::Handle<v8::Value>();
6362}
6363
6364
6365THREADED_TEST(InterceptorLoadICWithFieldOnHolder) {
6366 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6367 "var result = 0;"
6368 "o.y = 239;"
6369 "for (var i = 0; i < 1000; i++) {"
6370 " result = o.y;"
6371 "}",
6372 239);
6373}
6374
6375
6376THREADED_TEST(InterceptorLoadICWithSubstitutedProto) {
6377 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6378 "var result = 0;"
6379 "o.__proto__ = { 'y': 239 };"
6380 "for (var i = 0; i < 1000; i++) {"
6381 " result = o.y + o.x;"
6382 "}",
6383 239 + 42);
6384}
6385
6386
6387THREADED_TEST(InterceptorLoadICWithPropertyOnProto) {
6388 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6389 "var result = 0;"
6390 "o.__proto__.y = 239;"
6391 "for (var i = 0; i < 1000; i++) {"
6392 " result = o.y + o.x;"
6393 "}",
6394 239 + 42);
6395}
6396
6397
6398THREADED_TEST(InterceptorLoadICUndefined) {
6399 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6400 "var result = 0;"
6401 "for (var i = 0; i < 1000; i++) {"
6402 " result = (o.y == undefined) ? 239 : 42;"
6403 "}",
6404 239);
6405}
6406
6407
6408THREADED_TEST(InterceptorLoadICWithOverride) {
6409 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6410 "fst = new Object(); fst.__proto__ = o;"
6411 "snd = new Object(); snd.__proto__ = fst;"
6412 "var result1 = 0;"
6413 "for (var i = 0; i < 1000; i++) {"
6414 " result1 = snd.x;"
6415 "}"
6416 "fst.x = 239;"
6417 "var result = 0;"
6418 "for (var i = 0; i < 1000; i++) {"
6419 " result = snd.x;"
6420 "}"
6421 "result + result1",
6422 239 + 42);
6423}
6424
6425
6426// Test the case when we stored field into
6427// a stub, but interceptor produced value on its own.
6428THREADED_TEST(InterceptorLoadICFieldNotNeeded) {
6429 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6430 "proto = new Object();"
6431 "o.__proto__ = proto;"
6432 "proto.x = 239;"
6433 "for (var i = 0; i < 1000; i++) {"
6434 " o.x;"
6435 // Now it should be ICed and keep a reference to x defined on proto
6436 "}"
6437 "var result = 0;"
6438 "for (var i = 0; i < 1000; i++) {"
6439 " result += o.x;"
6440 "}"
6441 "result;",
6442 42 * 1000);
6443}
6444
6445
6446// Test the case when we stored field into
6447// a stub, but it got invalidated later on.
6448THREADED_TEST(InterceptorLoadICInvalidatedField) {
6449 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6450 "proto1 = new Object();"
6451 "proto2 = new Object();"
6452 "o.__proto__ = proto1;"
6453 "proto1.__proto__ = proto2;"
6454 "proto2.y = 239;"
6455 "for (var i = 0; i < 1000; i++) {"
6456 " o.y;"
6457 // Now it should be ICed and keep a reference to y defined on proto2
6458 "}"
6459 "proto1.y = 42;"
6460 "var result = 0;"
6461 "for (var i = 0; i < 1000; i++) {"
6462 " result += o.y;"
6463 "}"
6464 "result;",
6465 42 * 1000);
6466}
6467
6468
Steve Block6ded16b2010-05-10 14:33:55 +01006469static int interceptor_load_not_handled_calls = 0;
6470static v8::Handle<Value> InterceptorLoadNotHandled(Local<String> name,
6471 const AccessorInfo& info) {
6472 ++interceptor_load_not_handled_calls;
6473 return v8::Handle<v8::Value>();
6474}
6475
6476
6477// Test how post-interceptor lookups are done in the non-cacheable
6478// case: the interceptor should not be invoked during this lookup.
6479THREADED_TEST(InterceptorLoadICPostInterceptor) {
6480 interceptor_load_not_handled_calls = 0;
6481 CheckInterceptorLoadIC(InterceptorLoadNotHandled,
6482 "receiver = new Object();"
6483 "receiver.__proto__ = o;"
6484 "proto = new Object();"
6485 "/* Make proto a slow-case object. */"
6486 "for (var i = 0; i < 1000; i++) {"
6487 " proto[\"xxxxxxxx\" + i] = [];"
6488 "}"
6489 "proto.x = 17;"
6490 "o.__proto__ = proto;"
6491 "var result = 0;"
6492 "for (var i = 0; i < 1000; i++) {"
6493 " result += receiver.x;"
6494 "}"
6495 "result;",
6496 17 * 1000);
6497 CHECK_EQ(1000, interceptor_load_not_handled_calls);
6498}
6499
6500
Steve Blocka7e24c12009-10-30 11:49:00 +00006501// Test the case when we stored field into
6502// a stub, but it got invalidated later on due to override on
6503// global object which is between interceptor and fields' holders.
6504THREADED_TEST(InterceptorLoadICInvalidatedFieldViaGlobal) {
6505 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6506 "o.__proto__ = this;" // set a global to be a proto of o.
6507 "this.__proto__.y = 239;"
6508 "for (var i = 0; i < 10; i++) {"
6509 " if (o.y != 239) throw 'oops: ' + o.y;"
6510 // Now it should be ICed and keep a reference to y defined on field_holder.
6511 "}"
6512 "this.y = 42;" // Assign on a global.
6513 "var result = 0;"
6514 "for (var i = 0; i < 10; i++) {"
6515 " result += o.y;"
6516 "}"
6517 "result;",
6518 42 * 10);
6519}
6520
6521
Steve Blocka7e24c12009-10-30 11:49:00 +00006522static void SetOnThis(Local<String> name,
6523 Local<Value> value,
6524 const AccessorInfo& info) {
6525 info.This()->ForceSet(name, value);
6526}
6527
6528
6529THREADED_TEST(InterceptorLoadICWithCallbackOnHolder) {
6530 v8::HandleScope scope;
6531 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6532 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6533 templ->SetAccessor(v8_str("y"), Return239);
6534 LocalContext context;
6535 context->Global()->Set(v8_str("o"), templ->NewInstance());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006536
6537 // Check the case when receiver and interceptor's holder
6538 // are the same objects.
Steve Blocka7e24c12009-10-30 11:49:00 +00006539 v8::Handle<Value> value = CompileRun(
6540 "var result = 0;"
6541 "for (var i = 0; i < 7; i++) {"
6542 " result = o.y;"
6543 "}");
6544 CHECK_EQ(239, value->Int32Value());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006545
6546 // Check the case when interceptor's holder is in proto chain
6547 // of receiver.
6548 value = CompileRun(
6549 "r = { __proto__: o };"
6550 "var result = 0;"
6551 "for (var i = 0; i < 7; i++) {"
6552 " result = r.y;"
6553 "}");
6554 CHECK_EQ(239, value->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00006555}
6556
6557
6558THREADED_TEST(InterceptorLoadICWithCallbackOnProto) {
6559 v8::HandleScope scope;
6560 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6561 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6562 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
6563 templ_p->SetAccessor(v8_str("y"), Return239);
6564
6565 LocalContext context;
6566 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6567 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
6568
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006569 // Check the case when receiver and interceptor's holder
6570 // are the same objects.
Steve Blocka7e24c12009-10-30 11:49:00 +00006571 v8::Handle<Value> value = CompileRun(
6572 "o.__proto__ = p;"
6573 "var result = 0;"
6574 "for (var i = 0; i < 7; i++) {"
6575 " result = o.x + o.y;"
6576 "}");
6577 CHECK_EQ(239 + 42, value->Int32Value());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006578
6579 // Check the case when interceptor's holder is in proto chain
6580 // of receiver.
6581 value = CompileRun(
6582 "r = { __proto__: o };"
6583 "var result = 0;"
6584 "for (var i = 0; i < 7; i++) {"
6585 " result = r.x + r.y;"
6586 "}");
6587 CHECK_EQ(239 + 42, value->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00006588}
6589
6590
6591THREADED_TEST(InterceptorLoadICForCallbackWithOverride) {
6592 v8::HandleScope scope;
6593 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6594 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6595 templ->SetAccessor(v8_str("y"), Return239);
6596
6597 LocalContext context;
6598 context->Global()->Set(v8_str("o"), templ->NewInstance());
6599
6600 v8::Handle<Value> value = CompileRun(
6601 "fst = new Object(); fst.__proto__ = o;"
6602 "snd = new Object(); snd.__proto__ = fst;"
6603 "var result1 = 0;"
6604 "for (var i = 0; i < 7; i++) {"
6605 " result1 = snd.x;"
6606 "}"
6607 "fst.x = 239;"
6608 "var result = 0;"
6609 "for (var i = 0; i < 7; i++) {"
6610 " result = snd.x;"
6611 "}"
6612 "result + result1");
6613 CHECK_EQ(239 + 42, value->Int32Value());
6614}
6615
6616
6617// Test the case when we stored callback into
6618// a stub, but interceptor produced value on its own.
6619THREADED_TEST(InterceptorLoadICCallbackNotNeeded) {
6620 v8::HandleScope scope;
6621 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6622 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6623 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
6624 templ_p->SetAccessor(v8_str("y"), Return239);
6625
6626 LocalContext context;
6627 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6628 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
6629
6630 v8::Handle<Value> value = CompileRun(
6631 "o.__proto__ = p;"
6632 "for (var i = 0; i < 7; i++) {"
6633 " o.x;"
6634 // Now it should be ICed and keep a reference to x defined on p
6635 "}"
6636 "var result = 0;"
6637 "for (var i = 0; i < 7; i++) {"
6638 " result += o.x;"
6639 "}"
6640 "result");
6641 CHECK_EQ(42 * 7, value->Int32Value());
6642}
6643
6644
6645// Test the case when we stored callback into
6646// a stub, but it got invalidated later on.
6647THREADED_TEST(InterceptorLoadICInvalidatedCallback) {
6648 v8::HandleScope scope;
6649 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6650 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6651 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
6652 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis);
6653
6654 LocalContext context;
6655 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6656 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
6657
6658 v8::Handle<Value> value = CompileRun(
6659 "inbetween = new Object();"
6660 "o.__proto__ = inbetween;"
6661 "inbetween.__proto__ = p;"
6662 "for (var i = 0; i < 10; i++) {"
6663 " o.y;"
6664 // Now it should be ICed and keep a reference to y defined on p
6665 "}"
6666 "inbetween.y = 42;"
6667 "var result = 0;"
6668 "for (var i = 0; i < 10; i++) {"
6669 " result += o.y;"
6670 "}"
6671 "result");
6672 CHECK_EQ(42 * 10, value->Int32Value());
6673}
6674
6675
6676// Test the case when we stored callback into
6677// a stub, but it got invalidated later on due to override on
6678// global object which is between interceptor and callbacks' holders.
6679THREADED_TEST(InterceptorLoadICInvalidatedCallbackViaGlobal) {
6680 v8::HandleScope scope;
6681 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6682 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6683 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
6684 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis);
6685
6686 LocalContext context;
6687 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6688 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
6689
6690 v8::Handle<Value> value = CompileRun(
6691 "o.__proto__ = this;"
6692 "this.__proto__ = p;"
6693 "for (var i = 0; i < 10; i++) {"
6694 " if (o.y != 239) throw 'oops: ' + o.y;"
6695 // Now it should be ICed and keep a reference to y defined on p
6696 "}"
6697 "this.y = 42;"
6698 "var result = 0;"
6699 "for (var i = 0; i < 10; i++) {"
6700 " result += o.y;"
6701 "}"
6702 "result");
6703 CHECK_EQ(42 * 10, value->Int32Value());
6704}
6705
6706
6707static v8::Handle<Value> InterceptorLoadICGetter0(Local<String> name,
6708 const AccessorInfo& info) {
6709 ApiTestFuzzer::Fuzz();
6710 CHECK(v8_str("x")->Equals(name));
6711 return v8::Integer::New(0);
6712}
6713
6714
6715THREADED_TEST(InterceptorReturningZero) {
6716 CheckInterceptorLoadIC(InterceptorLoadICGetter0,
6717 "o.x == undefined ? 1 : 0",
6718 0);
6719}
6720
6721
6722static v8::Handle<Value> InterceptorStoreICSetter(
6723 Local<String> key, Local<Value> value, const AccessorInfo&) {
6724 CHECK(v8_str("x")->Equals(key));
6725 CHECK_EQ(42, value->Int32Value());
6726 return value;
6727}
6728
6729
6730// This test should hit the store IC for the interceptor case.
6731THREADED_TEST(InterceptorStoreIC) {
6732 v8::HandleScope scope;
6733 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6734 templ->SetNamedPropertyHandler(InterceptorLoadICGetter,
6735 InterceptorStoreICSetter);
6736 LocalContext context;
6737 context->Global()->Set(v8_str("o"), templ->NewInstance());
6738 v8::Handle<Value> value = CompileRun(
6739 "for (var i = 0; i < 1000; i++) {"
6740 " o.x = 42;"
6741 "}");
6742}
6743
6744
6745THREADED_TEST(InterceptorStoreICWithNoSetter) {
6746 v8::HandleScope scope;
6747 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6748 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6749 LocalContext context;
6750 context->Global()->Set(v8_str("o"), templ->NewInstance());
6751 v8::Handle<Value> value = CompileRun(
6752 "for (var i = 0; i < 1000; i++) {"
6753 " o.y = 239;"
6754 "}"
6755 "42 + o.y");
6756 CHECK_EQ(239 + 42, value->Int32Value());
6757}
6758
6759
6760
6761
6762v8::Handle<Value> call_ic_function;
6763v8::Handle<Value> call_ic_function2;
6764v8::Handle<Value> call_ic_function3;
6765
6766static v8::Handle<Value> InterceptorCallICGetter(Local<String> name,
6767 const AccessorInfo& info) {
6768 ApiTestFuzzer::Fuzz();
6769 CHECK(v8_str("x")->Equals(name));
6770 return call_ic_function;
6771}
6772
6773
6774// This test should hit the call IC for the interceptor case.
6775THREADED_TEST(InterceptorCallIC) {
6776 v8::HandleScope scope;
6777 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6778 templ->SetNamedPropertyHandler(InterceptorCallICGetter);
6779 LocalContext context;
6780 context->Global()->Set(v8_str("o"), templ->NewInstance());
6781 call_ic_function =
6782 v8_compile("function f(x) { return x + 1; }; f")->Run();
6783 v8::Handle<Value> value = CompileRun(
6784 "var result = 0;"
6785 "for (var i = 0; i < 1000; i++) {"
6786 " result = o.x(41);"
6787 "}");
6788 CHECK_EQ(42, value->Int32Value());
6789}
6790
6791
6792// This test checks that if interceptor doesn't provide
6793// a value, we can fetch regular value.
6794THREADED_TEST(InterceptorCallICSeesOthers) {
6795 v8::HandleScope scope;
6796 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6797 templ->SetNamedPropertyHandler(NoBlockGetterX);
6798 LocalContext context;
6799 context->Global()->Set(v8_str("o"), templ->NewInstance());
6800 v8::Handle<Value> value = CompileRun(
6801 "o.x = function f(x) { return x + 1; };"
6802 "var result = 0;"
6803 "for (var i = 0; i < 7; i++) {"
6804 " result = o.x(41);"
6805 "}");
6806 CHECK_EQ(42, value->Int32Value());
6807}
6808
6809
6810static v8::Handle<Value> call_ic_function4;
6811static v8::Handle<Value> InterceptorCallICGetter4(Local<String> name,
6812 const AccessorInfo& info) {
6813 ApiTestFuzzer::Fuzz();
6814 CHECK(v8_str("x")->Equals(name));
6815 return call_ic_function4;
6816}
6817
6818
6819// This test checks that if interceptor provides a function,
6820// even if we cached shadowed variant, interceptor's function
6821// is invoked
6822THREADED_TEST(InterceptorCallICCacheableNotNeeded) {
6823 v8::HandleScope scope;
6824 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6825 templ->SetNamedPropertyHandler(InterceptorCallICGetter4);
6826 LocalContext context;
6827 context->Global()->Set(v8_str("o"), templ->NewInstance());
6828 call_ic_function4 =
6829 v8_compile("function f(x) { return x - 1; }; f")->Run();
6830 v8::Handle<Value> value = CompileRun(
6831 "o.__proto__.x = function(x) { return x + 1; };"
6832 "var result = 0;"
6833 "for (var i = 0; i < 1000; i++) {"
6834 " result = o.x(42);"
6835 "}");
6836 CHECK_EQ(41, value->Int32Value());
6837}
6838
6839
6840// Test the case when we stored cacheable lookup into
6841// a stub, but it got invalidated later on
6842THREADED_TEST(InterceptorCallICInvalidatedCacheable) {
6843 v8::HandleScope scope;
6844 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6845 templ->SetNamedPropertyHandler(NoBlockGetterX);
6846 LocalContext context;
6847 context->Global()->Set(v8_str("o"), templ->NewInstance());
6848 v8::Handle<Value> value = CompileRun(
6849 "proto1 = new Object();"
6850 "proto2 = new Object();"
6851 "o.__proto__ = proto1;"
6852 "proto1.__proto__ = proto2;"
6853 "proto2.y = function(x) { return x + 1; };"
6854 // Invoke it many times to compile a stub
6855 "for (var i = 0; i < 7; i++) {"
6856 " o.y(42);"
6857 "}"
6858 "proto1.y = function(x) { return x - 1; };"
6859 "var result = 0;"
6860 "for (var i = 0; i < 7; i++) {"
6861 " result += o.y(42);"
6862 "}");
6863 CHECK_EQ(41 * 7, value->Int32Value());
6864}
6865
6866
6867static v8::Handle<Value> call_ic_function5;
6868static v8::Handle<Value> InterceptorCallICGetter5(Local<String> name,
6869 const AccessorInfo& info) {
6870 ApiTestFuzzer::Fuzz();
6871 if (v8_str("x")->Equals(name))
6872 return call_ic_function5;
6873 else
6874 return Local<Value>();
6875}
6876
6877
6878// This test checks that if interceptor doesn't provide a function,
6879// cached constant function is used
6880THREADED_TEST(InterceptorCallICConstantFunctionUsed) {
6881 v8::HandleScope scope;
6882 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6883 templ->SetNamedPropertyHandler(NoBlockGetterX);
6884 LocalContext context;
6885 context->Global()->Set(v8_str("o"), templ->NewInstance());
6886 v8::Handle<Value> value = CompileRun(
6887 "function inc(x) { return x + 1; };"
6888 "inc(1);"
6889 "o.x = inc;"
6890 "var result = 0;"
6891 "for (var i = 0; i < 1000; i++) {"
6892 " result = o.x(42);"
6893 "}");
6894 CHECK_EQ(43, value->Int32Value());
6895}
6896
6897
6898// This test checks that if interceptor provides a function,
6899// even if we cached constant function, interceptor's function
6900// is invoked
6901THREADED_TEST(InterceptorCallICConstantFunctionNotNeeded) {
6902 v8::HandleScope scope;
6903 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6904 templ->SetNamedPropertyHandler(InterceptorCallICGetter5);
6905 LocalContext context;
6906 context->Global()->Set(v8_str("o"), templ->NewInstance());
6907 call_ic_function5 =
6908 v8_compile("function f(x) { return x - 1; }; f")->Run();
6909 v8::Handle<Value> value = CompileRun(
6910 "function inc(x) { return x + 1; };"
6911 "inc(1);"
6912 "o.x = inc;"
6913 "var result = 0;"
6914 "for (var i = 0; i < 1000; i++) {"
6915 " result = o.x(42);"
6916 "}");
6917 CHECK_EQ(41, value->Int32Value());
6918}
6919
6920
6921// Test the case when we stored constant function into
6922// a stub, but it got invalidated later on
6923THREADED_TEST(InterceptorCallICInvalidatedConstantFunction) {
6924 v8::HandleScope scope;
6925 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6926 templ->SetNamedPropertyHandler(NoBlockGetterX);
6927 LocalContext context;
6928 context->Global()->Set(v8_str("o"), templ->NewInstance());
6929 v8::Handle<Value> value = CompileRun(
6930 "function inc(x) { return x + 1; };"
6931 "inc(1);"
6932 "proto1 = new Object();"
6933 "proto2 = new Object();"
6934 "o.__proto__ = proto1;"
6935 "proto1.__proto__ = proto2;"
6936 "proto2.y = inc;"
6937 // Invoke it many times to compile a stub
6938 "for (var i = 0; i < 7; i++) {"
6939 " o.y(42);"
6940 "}"
6941 "proto1.y = function(x) { return x - 1; };"
6942 "var result = 0;"
6943 "for (var i = 0; i < 7; i++) {"
6944 " result += o.y(42);"
6945 "}");
6946 CHECK_EQ(41 * 7, value->Int32Value());
6947}
6948
6949
6950// Test the case when we stored constant function into
6951// a stub, but it got invalidated later on due to override on
6952// global object which is between interceptor and constant function' holders.
6953THREADED_TEST(InterceptorCallICInvalidatedConstantFunctionViaGlobal) {
6954 v8::HandleScope scope;
6955 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6956 templ->SetNamedPropertyHandler(NoBlockGetterX);
6957 LocalContext context;
6958 context->Global()->Set(v8_str("o"), templ->NewInstance());
6959 v8::Handle<Value> value = CompileRun(
6960 "function inc(x) { return x + 1; };"
6961 "inc(1);"
6962 "o.__proto__ = this;"
6963 "this.__proto__.y = inc;"
6964 // Invoke it many times to compile a stub
6965 "for (var i = 0; i < 7; i++) {"
6966 " if (o.y(42) != 43) throw 'oops: ' + o.y(42);"
6967 "}"
6968 "this.y = function(x) { return x - 1; };"
6969 "var result = 0;"
6970 "for (var i = 0; i < 7; i++) {"
6971 " result += o.y(42);"
6972 "}");
6973 CHECK_EQ(41 * 7, value->Int32Value());
6974}
6975
6976
Leon Clarke4515c472010-02-03 11:58:03 +00006977// Test the case when actual function to call sits on global object.
6978THREADED_TEST(InterceptorCallICCachedFromGlobal) {
6979 v8::HandleScope scope;
6980 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6981 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
6982
6983 LocalContext context;
6984 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6985
6986 v8::Handle<Value> value = CompileRun(
6987 "try {"
6988 " o.__proto__ = this;"
6989 " for (var i = 0; i < 10; i++) {"
6990 " var v = o.parseFloat('239');"
6991 " if (v != 239) throw v;"
6992 // Now it should be ICed and keep a reference to parseFloat.
6993 " }"
6994 " var result = 0;"
6995 " for (var i = 0; i < 10; i++) {"
6996 " result += o.parseFloat('239');"
6997 " }"
6998 " result"
6999 "} catch(e) {"
7000 " e"
7001 "};");
7002 CHECK_EQ(239 * 10, value->Int32Value());
7003}
7004
Andrei Popescu402d9372010-02-26 13:31:12 +00007005static v8::Handle<Value> InterceptorCallICFastApi(Local<String> name,
7006 const AccessorInfo& info) {
7007 ApiTestFuzzer::Fuzz();
7008 int* call_count = reinterpret_cast<int*>(v8::External::Unwrap(info.Data()));
7009 ++(*call_count);
7010 if ((*call_count) % 20 == 0) {
Steve Block8defd9f2010-07-08 12:39:36 +01007011 i::Heap::CollectAllGarbage(true);
Andrei Popescu402d9372010-02-26 13:31:12 +00007012 }
7013 return v8::Handle<Value>();
7014}
7015
7016static v8::Handle<Value> FastApiCallback_TrivialSignature(
7017 const v8::Arguments& args) {
7018 ApiTestFuzzer::Fuzz();
7019 CHECK_EQ(args.This(), args.Holder());
7020 CHECK(args.Data()->Equals(v8_str("method_data")));
7021 return v8::Integer::New(args[0]->Int32Value() + 1);
7022}
7023
7024static v8::Handle<Value> FastApiCallback_SimpleSignature(
7025 const v8::Arguments& args) {
7026 ApiTestFuzzer::Fuzz();
7027 CHECK_EQ(args.This()->GetPrototype(), args.Holder());
7028 CHECK(args.Data()->Equals(v8_str("method_data")));
7029 // Note, we're using HasRealNamedProperty instead of Has to avoid
7030 // invoking the interceptor again.
7031 CHECK(args.Holder()->HasRealNamedProperty(v8_str("foo")));
7032 return v8::Integer::New(args[0]->Int32Value() + 1);
7033}
7034
7035// Helper to maximize the odds of object moving.
7036static void GenerateSomeGarbage() {
7037 CompileRun(
7038 "var garbage;"
7039 "for (var i = 0; i < 1000; i++) {"
7040 " garbage = [1/i, \"garbage\" + i, garbage, {foo: garbage}];"
7041 "}"
7042 "garbage = undefined;");
7043}
7044
7045THREADED_TEST(InterceptorCallICFastApi_TrivialSignature) {
7046 int interceptor_call_count = 0;
7047 v8::HandleScope scope;
7048 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7049 v8::Handle<v8::FunctionTemplate> method_templ =
7050 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature,
7051 v8_str("method_data"),
7052 v8::Handle<v8::Signature>());
7053 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7054 proto_templ->Set(v8_str("method"), method_templ);
7055 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7056 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
7057 NULL, NULL, NULL, NULL,
7058 v8::External::Wrap(&interceptor_call_count));
7059 LocalContext context;
7060 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7061 GenerateSomeGarbage();
7062 context->Global()->Set(v8_str("o"), fun->NewInstance());
7063 v8::Handle<Value> value = CompileRun(
7064 "var result = 0;"
7065 "for (var i = 0; i < 100; i++) {"
7066 " result = o.method(41);"
7067 "}");
7068 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
7069 CHECK_EQ(100, interceptor_call_count);
7070}
7071
7072THREADED_TEST(InterceptorCallICFastApi_SimpleSignature) {
7073 int interceptor_call_count = 0;
7074 v8::HandleScope scope;
7075 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7076 v8::Handle<v8::FunctionTemplate> method_templ =
7077 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7078 v8_str("method_data"),
7079 v8::Signature::New(fun_templ));
7080 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7081 proto_templ->Set(v8_str("method"), method_templ);
7082 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7083 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
7084 NULL, NULL, NULL, NULL,
7085 v8::External::Wrap(&interceptor_call_count));
7086 LocalContext context;
7087 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7088 GenerateSomeGarbage();
7089 context->Global()->Set(v8_str("o"), fun->NewInstance());
7090 v8::Handle<Value> value = CompileRun(
7091 "o.foo = 17;"
7092 "var receiver = {};"
7093 "receiver.__proto__ = o;"
7094 "var result = 0;"
7095 "for (var i = 0; i < 100; i++) {"
7096 " result = receiver.method(41);"
7097 "}");
7098 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
7099 CHECK_EQ(100, interceptor_call_count);
7100}
7101
7102THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss1) {
7103 int interceptor_call_count = 0;
7104 v8::HandleScope scope;
7105 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7106 v8::Handle<v8::FunctionTemplate> method_templ =
7107 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7108 v8_str("method_data"),
7109 v8::Signature::New(fun_templ));
7110 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7111 proto_templ->Set(v8_str("method"), method_templ);
7112 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7113 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
7114 NULL, NULL, NULL, NULL,
7115 v8::External::Wrap(&interceptor_call_count));
7116 LocalContext context;
7117 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7118 GenerateSomeGarbage();
7119 context->Global()->Set(v8_str("o"), fun->NewInstance());
7120 v8::Handle<Value> value = CompileRun(
7121 "o.foo = 17;"
7122 "var receiver = {};"
7123 "receiver.__proto__ = o;"
7124 "var result = 0;"
7125 "var saved_result = 0;"
7126 "for (var i = 0; i < 100; i++) {"
7127 " result = receiver.method(41);"
7128 " if (i == 50) {"
7129 " saved_result = result;"
7130 " receiver = {method: function(x) { return x - 1 }};"
7131 " }"
7132 "}");
7133 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
7134 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7135 CHECK_GE(interceptor_call_count, 50);
7136}
7137
7138THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss2) {
7139 int interceptor_call_count = 0;
7140 v8::HandleScope scope;
7141 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7142 v8::Handle<v8::FunctionTemplate> method_templ =
7143 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7144 v8_str("method_data"),
7145 v8::Signature::New(fun_templ));
7146 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7147 proto_templ->Set(v8_str("method"), method_templ);
7148 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7149 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
7150 NULL, NULL, NULL, NULL,
7151 v8::External::Wrap(&interceptor_call_count));
7152 LocalContext context;
7153 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7154 GenerateSomeGarbage();
7155 context->Global()->Set(v8_str("o"), fun->NewInstance());
7156 v8::Handle<Value> value = CompileRun(
7157 "o.foo = 17;"
7158 "var receiver = {};"
7159 "receiver.__proto__ = o;"
7160 "var result = 0;"
7161 "var saved_result = 0;"
7162 "for (var i = 0; i < 100; i++) {"
7163 " result = receiver.method(41);"
7164 " if (i == 50) {"
7165 " saved_result = result;"
7166 " o.method = function(x) { return x - 1 };"
7167 " }"
7168 "}");
7169 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
7170 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7171 CHECK_GE(interceptor_call_count, 50);
7172}
7173
Steve Block6ded16b2010-05-10 14:33:55 +01007174THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss3) {
7175 int interceptor_call_count = 0;
7176 v8::HandleScope scope;
7177 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7178 v8::Handle<v8::FunctionTemplate> method_templ =
7179 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7180 v8_str("method_data"),
7181 v8::Signature::New(fun_templ));
7182 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7183 proto_templ->Set(v8_str("method"), method_templ);
7184 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7185 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
7186 NULL, NULL, NULL, NULL,
7187 v8::External::Wrap(&interceptor_call_count));
7188 LocalContext context;
7189 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7190 GenerateSomeGarbage();
7191 context->Global()->Set(v8_str("o"), fun->NewInstance());
7192 v8::TryCatch try_catch;
7193 v8::Handle<Value> value = CompileRun(
7194 "o.foo = 17;"
7195 "var receiver = {};"
7196 "receiver.__proto__ = o;"
7197 "var result = 0;"
7198 "var saved_result = 0;"
7199 "for (var i = 0; i < 100; i++) {"
7200 " result = receiver.method(41);"
7201 " if (i == 50) {"
7202 " saved_result = result;"
7203 " receiver = 333;"
7204 " }"
7205 "}");
7206 CHECK(try_catch.HasCaught());
7207 CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"),
7208 try_catch.Exception()->ToString());
7209 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7210 CHECK_GE(interceptor_call_count, 50);
7211}
7212
Andrei Popescu402d9372010-02-26 13:31:12 +00007213THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_TypeError) {
7214 int interceptor_call_count = 0;
7215 v8::HandleScope scope;
7216 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7217 v8::Handle<v8::FunctionTemplate> method_templ =
7218 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7219 v8_str("method_data"),
7220 v8::Signature::New(fun_templ));
7221 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7222 proto_templ->Set(v8_str("method"), method_templ);
7223 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7224 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
7225 NULL, NULL, NULL, NULL,
7226 v8::External::Wrap(&interceptor_call_count));
7227 LocalContext context;
7228 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7229 GenerateSomeGarbage();
7230 context->Global()->Set(v8_str("o"), fun->NewInstance());
7231 v8::TryCatch try_catch;
7232 v8::Handle<Value> value = CompileRun(
7233 "o.foo = 17;"
7234 "var receiver = {};"
7235 "receiver.__proto__ = o;"
7236 "var result = 0;"
7237 "var saved_result = 0;"
7238 "for (var i = 0; i < 100; i++) {"
7239 " result = receiver.method(41);"
7240 " if (i == 50) {"
7241 " saved_result = result;"
7242 " receiver = {method: receiver.method};"
7243 " }"
7244 "}");
7245 CHECK(try_catch.HasCaught());
7246 CHECK_EQ(v8_str("TypeError: Illegal invocation"),
7247 try_catch.Exception()->ToString());
7248 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7249 CHECK_GE(interceptor_call_count, 50);
7250}
7251
7252THREADED_TEST(CallICFastApi_TrivialSignature) {
7253 v8::HandleScope scope;
7254 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7255 v8::Handle<v8::FunctionTemplate> method_templ =
7256 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature,
7257 v8_str("method_data"),
7258 v8::Handle<v8::Signature>());
7259 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7260 proto_templ->Set(v8_str("method"), method_templ);
7261 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7262 LocalContext context;
7263 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7264 GenerateSomeGarbage();
7265 context->Global()->Set(v8_str("o"), fun->NewInstance());
7266 v8::Handle<Value> value = CompileRun(
7267 "var result = 0;"
7268 "for (var i = 0; i < 100; i++) {"
7269 " result = o.method(41);"
7270 "}");
7271
7272 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
7273}
7274
7275THREADED_TEST(CallICFastApi_SimpleSignature) {
7276 v8::HandleScope scope;
7277 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7278 v8::Handle<v8::FunctionTemplate> method_templ =
7279 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7280 v8_str("method_data"),
7281 v8::Signature::New(fun_templ));
7282 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7283 proto_templ->Set(v8_str("method"), method_templ);
7284 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7285 LocalContext context;
7286 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7287 GenerateSomeGarbage();
7288 context->Global()->Set(v8_str("o"), fun->NewInstance());
7289 v8::Handle<Value> value = CompileRun(
7290 "o.foo = 17;"
7291 "var receiver = {};"
7292 "receiver.__proto__ = o;"
7293 "var result = 0;"
7294 "for (var i = 0; i < 100; i++) {"
7295 " result = receiver.method(41);"
7296 "}");
7297
7298 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
7299}
7300
Steve Block6ded16b2010-05-10 14:33:55 +01007301THREADED_TEST(CallICFastApi_SimpleSignature_Miss1) {
Andrei Popescu402d9372010-02-26 13:31:12 +00007302 v8::HandleScope scope;
7303 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7304 v8::Handle<v8::FunctionTemplate> method_templ =
7305 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7306 v8_str("method_data"),
7307 v8::Signature::New(fun_templ));
7308 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7309 proto_templ->Set(v8_str("method"), method_templ);
7310 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7311 LocalContext context;
7312 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7313 GenerateSomeGarbage();
7314 context->Global()->Set(v8_str("o"), fun->NewInstance());
7315 v8::Handle<Value> value = CompileRun(
7316 "o.foo = 17;"
7317 "var receiver = {};"
7318 "receiver.__proto__ = o;"
7319 "var result = 0;"
7320 "var saved_result = 0;"
7321 "for (var i = 0; i < 100; i++) {"
7322 " result = receiver.method(41);"
7323 " if (i == 50) {"
7324 " saved_result = result;"
7325 " receiver = {method: function(x) { return x - 1 }};"
7326 " }"
7327 "}");
7328 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
7329 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7330}
7331
Steve Block6ded16b2010-05-10 14:33:55 +01007332THREADED_TEST(CallICFastApi_SimpleSignature_Miss2) {
7333 v8::HandleScope scope;
7334 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7335 v8::Handle<v8::FunctionTemplate> method_templ =
7336 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7337 v8_str("method_data"),
7338 v8::Signature::New(fun_templ));
7339 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7340 proto_templ->Set(v8_str("method"), method_templ);
7341 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7342 LocalContext context;
7343 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7344 GenerateSomeGarbage();
7345 context->Global()->Set(v8_str("o"), fun->NewInstance());
7346 v8::TryCatch try_catch;
7347 v8::Handle<Value> value = CompileRun(
7348 "o.foo = 17;"
7349 "var receiver = {};"
7350 "receiver.__proto__ = o;"
7351 "var result = 0;"
7352 "var saved_result = 0;"
7353 "for (var i = 0; i < 100; i++) {"
7354 " result = receiver.method(41);"
7355 " if (i == 50) {"
7356 " saved_result = result;"
7357 " receiver = 333;"
7358 " }"
7359 "}");
7360 CHECK(try_catch.HasCaught());
7361 CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"),
7362 try_catch.Exception()->ToString());
7363 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7364}
7365
Leon Clarke4515c472010-02-03 11:58:03 +00007366
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01007367v8::Handle<Value> keyed_call_ic_function;
7368
7369static v8::Handle<Value> InterceptorKeyedCallICGetter(
7370 Local<String> name, const AccessorInfo& info) {
7371 ApiTestFuzzer::Fuzz();
7372 if (v8_str("x")->Equals(name)) {
7373 return keyed_call_ic_function;
7374 }
7375 return v8::Handle<Value>();
7376}
7377
7378
7379// Test the case when we stored cacheable lookup into
7380// a stub, but the function name changed (to another cacheable function).
7381THREADED_TEST(InterceptorKeyedCallICKeyChange1) {
7382 v8::HandleScope scope;
7383 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7384 templ->SetNamedPropertyHandler(NoBlockGetterX);
7385 LocalContext context;
7386 context->Global()->Set(v8_str("o"), templ->NewInstance());
7387 v8::Handle<Value> value = CompileRun(
7388 "proto = new Object();"
7389 "proto.y = function(x) { return x + 1; };"
7390 "proto.z = function(x) { return x - 1; };"
7391 "o.__proto__ = proto;"
7392 "var result = 0;"
7393 "var method = 'y';"
7394 "for (var i = 0; i < 10; i++) {"
7395 " if (i == 5) { method = 'z'; };"
7396 " result += o[method](41);"
7397 "}");
7398 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
7399}
7400
7401
7402// Test the case when we stored cacheable lookup into
7403// a stub, but the function name changed (and the new function is present
7404// both before and after the interceptor in the prototype chain).
7405THREADED_TEST(InterceptorKeyedCallICKeyChange2) {
7406 v8::HandleScope scope;
7407 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7408 templ->SetNamedPropertyHandler(InterceptorKeyedCallICGetter);
7409 LocalContext context;
7410 context->Global()->Set(v8_str("proto1"), templ->NewInstance());
7411 keyed_call_ic_function =
7412 v8_compile("function f(x) { return x - 1; }; f")->Run();
7413 v8::Handle<Value> value = CompileRun(
7414 "o = new Object();"
7415 "proto2 = new Object();"
7416 "o.y = function(x) { return x + 1; };"
7417 "proto2.y = function(x) { return x + 2; };"
7418 "o.__proto__ = proto1;"
7419 "proto1.__proto__ = proto2;"
7420 "var result = 0;"
7421 "var method = 'x';"
7422 "for (var i = 0; i < 10; i++) {"
7423 " if (i == 5) { method = 'y'; };"
7424 " result += o[method](41);"
7425 "}");
7426 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
7427}
7428
7429
7430// Same as InterceptorKeyedCallICKeyChange1 only the cacheable function sit
7431// on the global object.
7432THREADED_TEST(InterceptorKeyedCallICKeyChangeOnGlobal) {
7433 v8::HandleScope scope;
7434 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7435 templ->SetNamedPropertyHandler(NoBlockGetterX);
7436 LocalContext context;
7437 context->Global()->Set(v8_str("o"), templ->NewInstance());
7438 v8::Handle<Value> value = CompileRun(
7439 "function inc(x) { return x + 1; };"
7440 "inc(1);"
7441 "function dec(x) { return x - 1; };"
7442 "dec(1);"
7443 "o.__proto__ = this;"
7444 "this.__proto__.x = inc;"
7445 "this.__proto__.y = dec;"
7446 "var result = 0;"
7447 "var method = 'x';"
7448 "for (var i = 0; i < 10; i++) {"
7449 " if (i == 5) { method = 'y'; };"
7450 " result += o[method](41);"
7451 "}");
7452 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
7453}
7454
7455
7456// Test the case when actual function to call sits on global object.
7457THREADED_TEST(InterceptorKeyedCallICFromGlobal) {
7458 v8::HandleScope scope;
7459 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7460 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
7461 LocalContext context;
7462 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
7463
7464 v8::Handle<Value> value = CompileRun(
7465 "function len(x) { return x.length; };"
7466 "o.__proto__ = this;"
7467 "var m = 'parseFloat';"
7468 "var result = 0;"
7469 "for (var i = 0; i < 10; i++) {"
7470 " if (i == 5) {"
7471 " m = 'len';"
7472 " saved_result = result;"
7473 " };"
7474 " result = o[m]('239');"
7475 "}");
7476 CHECK_EQ(3, context->Global()->Get(v8_str("result"))->Int32Value());
7477 CHECK_EQ(239, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7478}
7479
7480// Test the map transition before the interceptor.
7481THREADED_TEST(InterceptorKeyedCallICMapChangeBefore) {
7482 v8::HandleScope scope;
7483 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7484 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
7485 LocalContext context;
7486 context->Global()->Set(v8_str("proto"), templ_o->NewInstance());
7487
7488 v8::Handle<Value> value = CompileRun(
7489 "var o = new Object();"
7490 "o.__proto__ = proto;"
7491 "o.method = function(x) { return x + 1; };"
7492 "var m = 'method';"
7493 "var result = 0;"
7494 "for (var i = 0; i < 10; i++) {"
7495 " if (i == 5) { o.method = function(x) { return x - 1; }; };"
7496 " result += o[m](41);"
7497 "}");
7498 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
7499}
7500
7501
7502// Test the map transition after the interceptor.
7503THREADED_TEST(InterceptorKeyedCallICMapChangeAfter) {
7504 v8::HandleScope scope;
7505 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7506 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
7507 LocalContext context;
7508 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
7509
7510 v8::Handle<Value> value = CompileRun(
7511 "var proto = new Object();"
7512 "o.__proto__ = proto;"
7513 "proto.method = function(x) { return x + 1; };"
7514 "var m = 'method';"
7515 "var result = 0;"
7516 "for (var i = 0; i < 10; i++) {"
7517 " if (i == 5) { proto.method = function(x) { return x - 1; }; };"
7518 " result += o[m](41);"
7519 "}");
7520 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
7521}
7522
7523
Steve Blocka7e24c12009-10-30 11:49:00 +00007524static int interceptor_call_count = 0;
7525
7526static v8::Handle<Value> InterceptorICRefErrorGetter(Local<String> name,
7527 const AccessorInfo& info) {
7528 ApiTestFuzzer::Fuzz();
7529 if (v8_str("x")->Equals(name) && interceptor_call_count++ < 20) {
7530 return call_ic_function2;
7531 }
7532 return v8::Handle<Value>();
7533}
7534
7535
7536// This test should hit load and call ICs for the interceptor case.
7537// Once in a while, the interceptor will reply that a property was not
7538// found in which case we should get a reference error.
7539THREADED_TEST(InterceptorICReferenceErrors) {
7540 v8::HandleScope scope;
7541 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7542 templ->SetNamedPropertyHandler(InterceptorICRefErrorGetter);
7543 LocalContext context(0, templ, v8::Handle<Value>());
7544 call_ic_function2 = v8_compile("function h(x) { return x; }; h")->Run();
7545 v8::Handle<Value> value = CompileRun(
7546 "function f() {"
7547 " for (var i = 0; i < 1000; i++) {"
7548 " try { x; } catch(e) { return true; }"
7549 " }"
7550 " return false;"
7551 "};"
7552 "f();");
7553 CHECK_EQ(true, value->BooleanValue());
7554 interceptor_call_count = 0;
7555 value = CompileRun(
7556 "function g() {"
7557 " for (var i = 0; i < 1000; i++) {"
7558 " try { x(42); } catch(e) { return true; }"
7559 " }"
7560 " return false;"
7561 "};"
7562 "g();");
7563 CHECK_EQ(true, value->BooleanValue());
7564}
7565
7566
7567static int interceptor_ic_exception_get_count = 0;
7568
7569static v8::Handle<Value> InterceptorICExceptionGetter(
7570 Local<String> name,
7571 const AccessorInfo& info) {
7572 ApiTestFuzzer::Fuzz();
7573 if (v8_str("x")->Equals(name) && ++interceptor_ic_exception_get_count < 20) {
7574 return call_ic_function3;
7575 }
7576 if (interceptor_ic_exception_get_count == 20) {
7577 return v8::ThrowException(v8_num(42));
7578 }
7579 // Do not handle get for properties other than x.
7580 return v8::Handle<Value>();
7581}
7582
7583// Test interceptor load/call IC where the interceptor throws an
7584// exception once in a while.
7585THREADED_TEST(InterceptorICGetterExceptions) {
7586 interceptor_ic_exception_get_count = 0;
7587 v8::HandleScope scope;
7588 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7589 templ->SetNamedPropertyHandler(InterceptorICExceptionGetter);
7590 LocalContext context(0, templ, v8::Handle<Value>());
7591 call_ic_function3 = v8_compile("function h(x) { return x; }; h")->Run();
7592 v8::Handle<Value> value = CompileRun(
7593 "function f() {"
7594 " for (var i = 0; i < 100; i++) {"
7595 " try { x; } catch(e) { return true; }"
7596 " }"
7597 " return false;"
7598 "};"
7599 "f();");
7600 CHECK_EQ(true, value->BooleanValue());
7601 interceptor_ic_exception_get_count = 0;
7602 value = CompileRun(
7603 "function f() {"
7604 " for (var i = 0; i < 100; i++) {"
7605 " try { x(42); } catch(e) { return true; }"
7606 " }"
7607 " return false;"
7608 "};"
7609 "f();");
7610 CHECK_EQ(true, value->BooleanValue());
7611}
7612
7613
7614static int interceptor_ic_exception_set_count = 0;
7615
7616static v8::Handle<Value> InterceptorICExceptionSetter(
7617 Local<String> key, Local<Value> value, const AccessorInfo&) {
7618 ApiTestFuzzer::Fuzz();
7619 if (++interceptor_ic_exception_set_count > 20) {
7620 return v8::ThrowException(v8_num(42));
7621 }
7622 // Do not actually handle setting.
7623 return v8::Handle<Value>();
7624}
7625
7626// Test interceptor store IC where the interceptor throws an exception
7627// once in a while.
7628THREADED_TEST(InterceptorICSetterExceptions) {
7629 interceptor_ic_exception_set_count = 0;
7630 v8::HandleScope scope;
7631 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7632 templ->SetNamedPropertyHandler(0, InterceptorICExceptionSetter);
7633 LocalContext context(0, templ, v8::Handle<Value>());
7634 v8::Handle<Value> value = CompileRun(
7635 "function f() {"
7636 " for (var i = 0; i < 100; i++) {"
7637 " try { x = 42; } catch(e) { return true; }"
7638 " }"
7639 " return false;"
7640 "};"
7641 "f();");
7642 CHECK_EQ(true, value->BooleanValue());
7643}
7644
7645
7646// Test that we ignore null interceptors.
7647THREADED_TEST(NullNamedInterceptor) {
7648 v8::HandleScope scope;
7649 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7650 templ->SetNamedPropertyHandler(0);
7651 LocalContext context;
7652 templ->Set("x", v8_num(42));
7653 v8::Handle<v8::Object> obj = templ->NewInstance();
7654 context->Global()->Set(v8_str("obj"), obj);
7655 v8::Handle<Value> value = CompileRun("obj.x");
7656 CHECK(value->IsInt32());
7657 CHECK_EQ(42, value->Int32Value());
7658}
7659
7660
7661// Test that we ignore null interceptors.
7662THREADED_TEST(NullIndexedInterceptor) {
7663 v8::HandleScope scope;
7664 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7665 templ->SetIndexedPropertyHandler(0);
7666 LocalContext context;
7667 templ->Set("42", v8_num(42));
7668 v8::Handle<v8::Object> obj = templ->NewInstance();
7669 context->Global()->Set(v8_str("obj"), obj);
7670 v8::Handle<Value> value = CompileRun("obj[42]");
7671 CHECK(value->IsInt32());
7672 CHECK_EQ(42, value->Int32Value());
7673}
7674
7675
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01007676THREADED_TEST(NamedPropertyHandlerGetterAttributes) {
7677 v8::HandleScope scope;
7678 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
7679 templ->InstanceTemplate()->SetNamedPropertyHandler(InterceptorLoadXICGetter);
7680 LocalContext env;
7681 env->Global()->Set(v8_str("obj"),
7682 templ->GetFunction()->NewInstance());
7683 ExpectTrue("obj.x === 42");
7684 ExpectTrue("!obj.propertyIsEnumerable('x')");
7685}
7686
7687
Steve Blocka7e24c12009-10-30 11:49:00 +00007688static v8::Handle<Value> ParentGetter(Local<String> name,
7689 const AccessorInfo& info) {
7690 ApiTestFuzzer::Fuzz();
7691 return v8_num(1);
7692}
7693
7694
7695static v8::Handle<Value> ChildGetter(Local<String> name,
7696 const AccessorInfo& info) {
7697 ApiTestFuzzer::Fuzz();
7698 return v8_num(42);
7699}
7700
7701
7702THREADED_TEST(Overriding) {
7703 v8::HandleScope scope;
7704 LocalContext context;
7705
7706 // Parent template.
7707 Local<v8::FunctionTemplate> parent_templ = v8::FunctionTemplate::New();
7708 Local<ObjectTemplate> parent_instance_templ =
7709 parent_templ->InstanceTemplate();
7710 parent_instance_templ->SetAccessor(v8_str("f"), ParentGetter);
7711
7712 // Template that inherits from the parent template.
7713 Local<v8::FunctionTemplate> child_templ = v8::FunctionTemplate::New();
7714 Local<ObjectTemplate> child_instance_templ =
7715 child_templ->InstanceTemplate();
7716 child_templ->Inherit(parent_templ);
7717 // Override 'f'. The child version of 'f' should get called for child
7718 // instances.
7719 child_instance_templ->SetAccessor(v8_str("f"), ChildGetter);
7720 // Add 'g' twice. The 'g' added last should get called for instances.
7721 child_instance_templ->SetAccessor(v8_str("g"), ParentGetter);
7722 child_instance_templ->SetAccessor(v8_str("g"), ChildGetter);
7723
7724 // Add 'h' as an accessor to the proto template with ReadOnly attributes
7725 // so 'h' can be shadowed on the instance object.
7726 Local<ObjectTemplate> child_proto_templ = child_templ->PrototypeTemplate();
7727 child_proto_templ->SetAccessor(v8_str("h"), ParentGetter, 0,
7728 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly);
7729
7730 // Add 'i' as an accessor to the instance template with ReadOnly attributes
7731 // but the attribute does not have effect because it is duplicated with
7732 // NULL setter.
7733 child_instance_templ->SetAccessor(v8_str("i"), ChildGetter, 0,
7734 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly);
7735
7736
7737
7738 // Instantiate the child template.
7739 Local<v8::Object> instance = child_templ->GetFunction()->NewInstance();
7740
7741 // Check that the child function overrides the parent one.
7742 context->Global()->Set(v8_str("o"), instance);
7743 Local<Value> value = v8_compile("o.f")->Run();
7744 // Check that the 'g' that was added last is hit.
7745 CHECK_EQ(42, value->Int32Value());
7746 value = v8_compile("o.g")->Run();
7747 CHECK_EQ(42, value->Int32Value());
7748
7749 // Check 'h' can be shadowed.
7750 value = v8_compile("o.h = 3; o.h")->Run();
7751 CHECK_EQ(3, value->Int32Value());
7752
7753 // Check 'i' is cannot be shadowed or changed.
7754 value = v8_compile("o.i = 3; o.i")->Run();
7755 CHECK_EQ(42, value->Int32Value());
7756}
7757
7758
7759static v8::Handle<Value> IsConstructHandler(const v8::Arguments& args) {
7760 ApiTestFuzzer::Fuzz();
7761 if (args.IsConstructCall()) {
7762 return v8::Boolean::New(true);
7763 }
7764 return v8::Boolean::New(false);
7765}
7766
7767
7768THREADED_TEST(IsConstructCall) {
7769 v8::HandleScope scope;
7770
7771 // Function template with call handler.
7772 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
7773 templ->SetCallHandler(IsConstructHandler);
7774
7775 LocalContext context;
7776
7777 context->Global()->Set(v8_str("f"), templ->GetFunction());
7778 Local<Value> value = v8_compile("f()")->Run();
7779 CHECK(!value->BooleanValue());
7780 value = v8_compile("new f()")->Run();
7781 CHECK(value->BooleanValue());
7782}
7783
7784
7785THREADED_TEST(ObjectProtoToString) {
7786 v8::HandleScope scope;
7787 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
7788 templ->SetClassName(v8_str("MyClass"));
7789
7790 LocalContext context;
7791
7792 Local<String> customized_tostring = v8_str("customized toString");
7793
7794 // Replace Object.prototype.toString
7795 v8_compile("Object.prototype.toString = function() {"
7796 " return 'customized toString';"
7797 "}")->Run();
7798
7799 // Normal ToString call should call replaced Object.prototype.toString
7800 Local<v8::Object> instance = templ->GetFunction()->NewInstance();
7801 Local<String> value = instance->ToString();
7802 CHECK(value->IsString() && value->Equals(customized_tostring));
7803
7804 // ObjectProtoToString should not call replace toString function.
7805 value = instance->ObjectProtoToString();
7806 CHECK(value->IsString() && value->Equals(v8_str("[object MyClass]")));
7807
7808 // Check global
7809 value = context->Global()->ObjectProtoToString();
7810 CHECK(value->IsString() && value->Equals(v8_str("[object global]")));
7811
7812 // Check ordinary object
7813 Local<Value> object = v8_compile("new Object()")->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01007814 value = object.As<v8::Object>()->ObjectProtoToString();
Steve Blocka7e24c12009-10-30 11:49:00 +00007815 CHECK(value->IsString() && value->Equals(v8_str("[object Object]")));
7816}
7817
7818
7819bool ApiTestFuzzer::fuzzing_ = false;
Steve Block8defd9f2010-07-08 12:39:36 +01007820i::Semaphore* ApiTestFuzzer::all_tests_done_=
7821 i::OS::CreateSemaphore(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00007822int ApiTestFuzzer::active_tests_;
7823int ApiTestFuzzer::tests_being_run_;
7824int ApiTestFuzzer::current_;
7825
7826
7827// We are in a callback and want to switch to another thread (if we
7828// are currently running the thread fuzzing test).
7829void ApiTestFuzzer::Fuzz() {
7830 if (!fuzzing_) return;
7831 ApiTestFuzzer* test = RegisterThreadedTest::nth(current_)->fuzzer_;
7832 test->ContextSwitch();
7833}
7834
7835
7836// Let the next thread go. Since it is also waiting on the V8 lock it may
7837// not start immediately.
7838bool ApiTestFuzzer::NextThread() {
7839 int test_position = GetNextTestNumber();
Steve Blockd0582a62009-12-15 09:54:21 +00007840 const char* test_name = RegisterThreadedTest::nth(current_)->name();
Steve Blocka7e24c12009-10-30 11:49:00 +00007841 if (test_position == current_) {
Steve Blockd0582a62009-12-15 09:54:21 +00007842 if (kLogThreading)
7843 printf("Stay with %s\n", test_name);
Steve Blocka7e24c12009-10-30 11:49:00 +00007844 return false;
7845 }
Steve Blockd0582a62009-12-15 09:54:21 +00007846 if (kLogThreading) {
7847 printf("Switch from %s to %s\n",
7848 test_name,
7849 RegisterThreadedTest::nth(test_position)->name());
7850 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007851 current_ = test_position;
7852 RegisterThreadedTest::nth(current_)->fuzzer_->gate_->Signal();
7853 return true;
7854}
7855
7856
7857void ApiTestFuzzer::Run() {
7858 // When it is our turn...
7859 gate_->Wait();
7860 {
7861 // ... get the V8 lock and start running the test.
7862 v8::Locker locker;
7863 CallTest();
7864 }
7865 // This test finished.
7866 active_ = false;
7867 active_tests_--;
7868 // If it was the last then signal that fact.
7869 if (active_tests_ == 0) {
7870 all_tests_done_->Signal();
7871 } else {
7872 // Otherwise select a new test and start that.
7873 NextThread();
7874 }
7875}
7876
7877
7878static unsigned linear_congruential_generator;
7879
7880
7881void ApiTestFuzzer::Setup(PartOfTest part) {
7882 linear_congruential_generator = i::FLAG_testing_prng_seed;
7883 fuzzing_ = true;
7884 int start = (part == FIRST_PART) ? 0 : (RegisterThreadedTest::count() >> 1);
7885 int end = (part == FIRST_PART)
7886 ? (RegisterThreadedTest::count() >> 1)
7887 : RegisterThreadedTest::count();
7888 active_tests_ = tests_being_run_ = end - start;
7889 for (int i = 0; i < tests_being_run_; i++) {
7890 RegisterThreadedTest::nth(i)->fuzzer_ = new ApiTestFuzzer(i + start);
7891 }
7892 for (int i = 0; i < active_tests_; i++) {
7893 RegisterThreadedTest::nth(i)->fuzzer_->Start();
7894 }
7895}
7896
7897
7898static void CallTestNumber(int test_number) {
7899 (RegisterThreadedTest::nth(test_number)->callback())();
7900}
7901
7902
7903void ApiTestFuzzer::RunAllTests() {
7904 // Set off the first test.
7905 current_ = -1;
7906 NextThread();
7907 // Wait till they are all done.
7908 all_tests_done_->Wait();
7909}
7910
7911
7912int ApiTestFuzzer::GetNextTestNumber() {
7913 int next_test;
7914 do {
7915 next_test = (linear_congruential_generator >> 16) % tests_being_run_;
7916 linear_congruential_generator *= 1664525u;
7917 linear_congruential_generator += 1013904223u;
7918 } while (!RegisterThreadedTest::nth(next_test)->fuzzer_->active_);
7919 return next_test;
7920}
7921
7922
7923void ApiTestFuzzer::ContextSwitch() {
7924 // If the new thread is the same as the current thread there is nothing to do.
7925 if (NextThread()) {
7926 // Now it can start.
7927 v8::Unlocker unlocker;
7928 // Wait till someone starts us again.
7929 gate_->Wait();
7930 // And we're off.
7931 }
7932}
7933
7934
7935void ApiTestFuzzer::TearDown() {
7936 fuzzing_ = false;
7937 for (int i = 0; i < RegisterThreadedTest::count(); i++) {
7938 ApiTestFuzzer *fuzzer = RegisterThreadedTest::nth(i)->fuzzer_;
7939 if (fuzzer != NULL) fuzzer->Join();
7940 }
7941}
7942
7943
7944// Lets not be needlessly self-referential.
7945TEST(Threading) {
7946 ApiTestFuzzer::Setup(ApiTestFuzzer::FIRST_PART);
7947 ApiTestFuzzer::RunAllTests();
7948 ApiTestFuzzer::TearDown();
7949}
7950
7951TEST(Threading2) {
7952 ApiTestFuzzer::Setup(ApiTestFuzzer::SECOND_PART);
7953 ApiTestFuzzer::RunAllTests();
7954 ApiTestFuzzer::TearDown();
7955}
7956
7957
7958void ApiTestFuzzer::CallTest() {
Steve Blockd0582a62009-12-15 09:54:21 +00007959 if (kLogThreading)
7960 printf("Start test %d\n", test_number_);
Steve Blocka7e24c12009-10-30 11:49:00 +00007961 CallTestNumber(test_number_);
Steve Blockd0582a62009-12-15 09:54:21 +00007962 if (kLogThreading)
7963 printf("End test %d\n", test_number_);
Steve Blocka7e24c12009-10-30 11:49:00 +00007964}
7965
7966
7967static v8::Handle<Value> ThrowInJS(const v8::Arguments& args) {
7968 CHECK(v8::Locker::IsLocked());
7969 ApiTestFuzzer::Fuzz();
7970 v8::Unlocker unlocker;
7971 const char* code = "throw 7;";
7972 {
7973 v8::Locker nested_locker;
7974 v8::HandleScope scope;
7975 v8::Handle<Value> exception;
7976 { v8::TryCatch try_catch;
7977 v8::Handle<Value> value = CompileRun(code);
7978 CHECK(value.IsEmpty());
7979 CHECK(try_catch.HasCaught());
7980 // Make sure to wrap the exception in a new handle because
7981 // the handle returned from the TryCatch is destroyed
7982 // when the TryCatch is destroyed.
7983 exception = Local<Value>::New(try_catch.Exception());
7984 }
7985 return v8::ThrowException(exception);
7986 }
7987}
7988
7989
7990static v8::Handle<Value> ThrowInJSNoCatch(const v8::Arguments& args) {
7991 CHECK(v8::Locker::IsLocked());
7992 ApiTestFuzzer::Fuzz();
7993 v8::Unlocker unlocker;
7994 const char* code = "throw 7;";
7995 {
7996 v8::Locker nested_locker;
7997 v8::HandleScope scope;
7998 v8::Handle<Value> value = CompileRun(code);
7999 CHECK(value.IsEmpty());
8000 return v8_str("foo");
8001 }
8002}
8003
8004
8005// These are locking tests that don't need to be run again
8006// as part of the locking aggregation tests.
8007TEST(NestedLockers) {
8008 v8::Locker locker;
8009 CHECK(v8::Locker::IsLocked());
8010 v8::HandleScope scope;
8011 LocalContext env;
8012 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(ThrowInJS);
8013 Local<Function> fun = fun_templ->GetFunction();
8014 env->Global()->Set(v8_str("throw_in_js"), fun);
8015 Local<Script> script = v8_compile("(function () {"
8016 " try {"
8017 " throw_in_js();"
8018 " return 42;"
8019 " } catch (e) {"
8020 " return e * 13;"
8021 " }"
8022 "})();");
8023 CHECK_EQ(91, script->Run()->Int32Value());
8024}
8025
8026
8027// These are locking tests that don't need to be run again
8028// as part of the locking aggregation tests.
8029TEST(NestedLockersNoTryCatch) {
8030 v8::Locker locker;
8031 v8::HandleScope scope;
8032 LocalContext env;
8033 Local<v8::FunctionTemplate> fun_templ =
8034 v8::FunctionTemplate::New(ThrowInJSNoCatch);
8035 Local<Function> fun = fun_templ->GetFunction();
8036 env->Global()->Set(v8_str("throw_in_js"), fun);
8037 Local<Script> script = v8_compile("(function () {"
8038 " try {"
8039 " throw_in_js();"
8040 " return 42;"
8041 " } catch (e) {"
8042 " return e * 13;"
8043 " }"
8044 "})();");
8045 CHECK_EQ(91, script->Run()->Int32Value());
8046}
8047
8048
8049THREADED_TEST(RecursiveLocking) {
8050 v8::Locker locker;
8051 {
8052 v8::Locker locker2;
8053 CHECK(v8::Locker::IsLocked());
8054 }
8055}
8056
8057
8058static v8::Handle<Value> UnlockForAMoment(const v8::Arguments& args) {
8059 ApiTestFuzzer::Fuzz();
8060 v8::Unlocker unlocker;
8061 return v8::Undefined();
8062}
8063
8064
8065THREADED_TEST(LockUnlockLock) {
8066 {
8067 v8::Locker locker;
8068 v8::HandleScope scope;
8069 LocalContext env;
8070 Local<v8::FunctionTemplate> fun_templ =
8071 v8::FunctionTemplate::New(UnlockForAMoment);
8072 Local<Function> fun = fun_templ->GetFunction();
8073 env->Global()->Set(v8_str("unlock_for_a_moment"), fun);
8074 Local<Script> script = v8_compile("(function () {"
8075 " unlock_for_a_moment();"
8076 " return 42;"
8077 "})();");
8078 CHECK_EQ(42, script->Run()->Int32Value());
8079 }
8080 {
8081 v8::Locker locker;
8082 v8::HandleScope scope;
8083 LocalContext env;
8084 Local<v8::FunctionTemplate> fun_templ =
8085 v8::FunctionTemplate::New(UnlockForAMoment);
8086 Local<Function> fun = fun_templ->GetFunction();
8087 env->Global()->Set(v8_str("unlock_for_a_moment"), fun);
8088 Local<Script> script = v8_compile("(function () {"
8089 " unlock_for_a_moment();"
8090 " return 42;"
8091 "})();");
8092 CHECK_EQ(42, script->Run()->Int32Value());
8093 }
8094}
8095
8096
Leon Clarked91b9f72010-01-27 17:25:45 +00008097static int GetGlobalObjectsCount() {
Leon Clarkeeab96aa2010-01-27 16:31:12 +00008098 int count = 0;
Steve Block8defd9f2010-07-08 12:39:36 +01008099 i::HeapIterator it;
Leon Clarked91b9f72010-01-27 17:25:45 +00008100 for (i::HeapObject* object = it.next(); object != NULL; object = it.next())
8101 if (object->IsJSGlobalObject()) count++;
8102 return count;
8103}
8104
8105
Ben Murdochf87a2032010-10-22 12:50:53 +01008106static void CheckSurvivingGlobalObjectsCount(int expected) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008107 // We need to collect all garbage twice to be sure that everything
8108 // has been collected. This is because inline caches are cleared in
8109 // the first garbage collection but some of the maps have already
8110 // been marked at that point. Therefore some of the maps are not
8111 // collected until the second garbage collection.
Steve Block8defd9f2010-07-08 12:39:36 +01008112 i::Heap::CollectAllGarbage(false);
8113 i::Heap::CollectAllGarbage(false);
Leon Clarked91b9f72010-01-27 17:25:45 +00008114 int count = GetGlobalObjectsCount();
Steve Blocka7e24c12009-10-30 11:49:00 +00008115#ifdef DEBUG
Ben Murdochf87a2032010-10-22 12:50:53 +01008116 if (count != expected) i::Heap::TracePathToGlobal();
Steve Blocka7e24c12009-10-30 11:49:00 +00008117#endif
Ben Murdochf87a2032010-10-22 12:50:53 +01008118 CHECK_EQ(expected, count);
Steve Blocka7e24c12009-10-30 11:49:00 +00008119}
8120
8121
8122TEST(DontLeakGlobalObjects) {
8123 // Regression test for issues 1139850 and 1174891.
8124
8125 v8::V8::Initialize();
8126
Steve Blocka7e24c12009-10-30 11:49:00 +00008127 for (int i = 0; i < 5; i++) {
8128 { v8::HandleScope scope;
8129 LocalContext context;
8130 }
Ben Murdochf87a2032010-10-22 12:50:53 +01008131 CheckSurvivingGlobalObjectsCount(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00008132
8133 { v8::HandleScope scope;
8134 LocalContext context;
8135 v8_compile("Date")->Run();
8136 }
Ben Murdochf87a2032010-10-22 12:50:53 +01008137 CheckSurvivingGlobalObjectsCount(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00008138
8139 { v8::HandleScope scope;
8140 LocalContext context;
8141 v8_compile("/aaa/")->Run();
8142 }
Ben Murdochf87a2032010-10-22 12:50:53 +01008143 CheckSurvivingGlobalObjectsCount(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00008144
8145 { v8::HandleScope scope;
8146 const char* extension_list[] = { "v8/gc" };
8147 v8::ExtensionConfiguration extensions(1, extension_list);
8148 LocalContext context(&extensions);
8149 v8_compile("gc();")->Run();
8150 }
Ben Murdochf87a2032010-10-22 12:50:53 +01008151 CheckSurvivingGlobalObjectsCount(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00008152 }
8153}
8154
8155
8156v8::Persistent<v8::Object> some_object;
8157v8::Persistent<v8::Object> bad_handle;
8158
Kristian Monsen50ef84f2010-07-29 15:18:00 +01008159void NewPersistentHandleCallback(v8::Persistent<v8::Value> handle, void*) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008160 v8::HandleScope scope;
8161 bad_handle = v8::Persistent<v8::Object>::New(some_object);
Kristian Monsen50ef84f2010-07-29 15:18:00 +01008162 handle.Dispose();
Steve Blocka7e24c12009-10-30 11:49:00 +00008163}
8164
8165
8166THREADED_TEST(NewPersistentHandleFromWeakCallback) {
8167 LocalContext context;
8168
8169 v8::Persistent<v8::Object> handle1, handle2;
8170 {
8171 v8::HandleScope scope;
8172 some_object = v8::Persistent<v8::Object>::New(v8::Object::New());
8173 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
8174 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
8175 }
8176 // Note: order is implementation dependent alas: currently
8177 // global handle nodes are processed by PostGarbageCollectionProcessing
8178 // in reverse allocation order, so if second allocated handle is deleted,
8179 // weak callback of the first handle would be able to 'reallocate' it.
8180 handle1.MakeWeak(NULL, NewPersistentHandleCallback);
8181 handle2.Dispose();
8182 i::Heap::CollectAllGarbage(false);
8183}
8184
8185
8186v8::Persistent<v8::Object> to_be_disposed;
8187
8188void DisposeAndForceGcCallback(v8::Persistent<v8::Value> handle, void*) {
8189 to_be_disposed.Dispose();
8190 i::Heap::CollectAllGarbage(false);
Kristian Monsen50ef84f2010-07-29 15:18:00 +01008191 handle.Dispose();
Steve Blocka7e24c12009-10-30 11:49:00 +00008192}
8193
8194
8195THREADED_TEST(DoNotUseDeletedNodesInSecondLevelGc) {
8196 LocalContext context;
8197
8198 v8::Persistent<v8::Object> handle1, handle2;
8199 {
8200 v8::HandleScope scope;
8201 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
8202 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
8203 }
8204 handle1.MakeWeak(NULL, DisposeAndForceGcCallback);
8205 to_be_disposed = handle2;
8206 i::Heap::CollectAllGarbage(false);
8207}
8208
Steve Blockd0582a62009-12-15 09:54:21 +00008209void DisposingCallback(v8::Persistent<v8::Value> handle, void*) {
8210 handle.Dispose();
8211}
8212
8213void HandleCreatingCallback(v8::Persistent<v8::Value> handle, void*) {
8214 v8::HandleScope scope;
8215 v8::Persistent<v8::Object>::New(v8::Object::New());
Kristian Monsen50ef84f2010-07-29 15:18:00 +01008216 handle.Dispose();
Steve Blockd0582a62009-12-15 09:54:21 +00008217}
8218
8219
8220THREADED_TEST(NoGlobalHandlesOrphaningDueToWeakCallback) {
8221 LocalContext context;
8222
8223 v8::Persistent<v8::Object> handle1, handle2, handle3;
8224 {
8225 v8::HandleScope scope;
8226 handle3 = v8::Persistent<v8::Object>::New(v8::Object::New());
8227 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
8228 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
8229 }
8230 handle2.MakeWeak(NULL, DisposingCallback);
8231 handle3.MakeWeak(NULL, HandleCreatingCallback);
8232 i::Heap::CollectAllGarbage(false);
8233}
8234
Steve Blocka7e24c12009-10-30 11:49:00 +00008235
8236THREADED_TEST(CheckForCrossContextObjectLiterals) {
8237 v8::V8::Initialize();
8238
8239 const int nof = 2;
8240 const char* sources[nof] = {
8241 "try { [ 2, 3, 4 ].forEach(5); } catch(e) { e.toString(); }",
8242 "Object()"
8243 };
8244
8245 for (int i = 0; i < nof; i++) {
8246 const char* source = sources[i];
8247 { v8::HandleScope scope;
8248 LocalContext context;
8249 CompileRun(source);
8250 }
8251 { v8::HandleScope scope;
8252 LocalContext context;
8253 CompileRun(source);
8254 }
8255 }
8256}
8257
8258
8259static v8::Handle<Value> NestedScope(v8::Persistent<Context> env) {
8260 v8::HandleScope inner;
8261 env->Enter();
8262 v8::Handle<Value> three = v8_num(3);
8263 v8::Handle<Value> value = inner.Close(three);
8264 env->Exit();
8265 return value;
8266}
8267
8268
8269THREADED_TEST(NestedHandleScopeAndContexts) {
8270 v8::HandleScope outer;
8271 v8::Persistent<Context> env = Context::New();
8272 env->Enter();
8273 v8::Handle<Value> value = NestedScope(env);
8274 v8::Handle<String> str = value->ToString();
8275 env->Exit();
8276 env.Dispose();
8277}
8278
8279
8280THREADED_TEST(ExternalAllocatedMemory) {
8281 v8::HandleScope outer;
8282 v8::Persistent<Context> env = Context::New();
8283 const int kSize = 1024*1024;
8284 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(kSize), kSize);
8285 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(-kSize), 0);
8286}
8287
8288
8289THREADED_TEST(DisposeEnteredContext) {
8290 v8::HandleScope scope;
8291 LocalContext outer;
8292 { v8::Persistent<v8::Context> inner = v8::Context::New();
8293 inner->Enter();
8294 inner.Dispose();
8295 inner.Clear();
8296 inner->Exit();
8297 }
8298}
8299
8300
8301// Regression test for issue 54, object templates with internal fields
8302// but no accessors or interceptors did not get their internal field
8303// count set on instances.
8304THREADED_TEST(Regress54) {
8305 v8::HandleScope outer;
8306 LocalContext context;
8307 static v8::Persistent<v8::ObjectTemplate> templ;
8308 if (templ.IsEmpty()) {
8309 v8::HandleScope inner;
8310 v8::Handle<v8::ObjectTemplate> local = v8::ObjectTemplate::New();
8311 local->SetInternalFieldCount(1);
8312 templ = v8::Persistent<v8::ObjectTemplate>::New(inner.Close(local));
8313 }
8314 v8::Handle<v8::Object> result = templ->NewInstance();
8315 CHECK_EQ(1, result->InternalFieldCount());
8316}
8317
8318
8319// If part of the threaded tests, this test makes ThreadingTest fail
8320// on mac.
8321TEST(CatchStackOverflow) {
8322 v8::HandleScope scope;
8323 LocalContext context;
8324 v8::TryCatch try_catch;
8325 v8::Handle<v8::Script> script = v8::Script::Compile(v8::String::New(
8326 "function f() {"
8327 " return f();"
8328 "}"
8329 ""
8330 "f();"));
8331 v8::Handle<v8::Value> result = script->Run();
8332 CHECK(result.IsEmpty());
8333}
8334
8335
8336static void CheckTryCatchSourceInfo(v8::Handle<v8::Script> script,
8337 const char* resource_name,
8338 int line_offset) {
8339 v8::HandleScope scope;
8340 v8::TryCatch try_catch;
8341 v8::Handle<v8::Value> result = script->Run();
8342 CHECK(result.IsEmpty());
8343 CHECK(try_catch.HasCaught());
8344 v8::Handle<v8::Message> message = try_catch.Message();
8345 CHECK(!message.IsEmpty());
8346 CHECK_EQ(10 + line_offset, message->GetLineNumber());
8347 CHECK_EQ(91, message->GetStartPosition());
8348 CHECK_EQ(92, message->GetEndPosition());
8349 CHECK_EQ(2, message->GetStartColumn());
8350 CHECK_EQ(3, message->GetEndColumn());
8351 v8::String::AsciiValue line(message->GetSourceLine());
8352 CHECK_EQ(" throw 'nirk';", *line);
8353 v8::String::AsciiValue name(message->GetScriptResourceName());
8354 CHECK_EQ(resource_name, *name);
8355}
8356
8357
8358THREADED_TEST(TryCatchSourceInfo) {
8359 v8::HandleScope scope;
8360 LocalContext context;
8361 v8::Handle<v8::String> source = v8::String::New(
8362 "function Foo() {\n"
8363 " return Bar();\n"
8364 "}\n"
8365 "\n"
8366 "function Bar() {\n"
8367 " return Baz();\n"
8368 "}\n"
8369 "\n"
8370 "function Baz() {\n"
8371 " throw 'nirk';\n"
8372 "}\n"
8373 "\n"
8374 "Foo();\n");
8375
8376 const char* resource_name;
8377 v8::Handle<v8::Script> script;
8378 resource_name = "test.js";
8379 script = v8::Script::Compile(source, v8::String::New(resource_name));
8380 CheckTryCatchSourceInfo(script, resource_name, 0);
8381
8382 resource_name = "test1.js";
8383 v8::ScriptOrigin origin1(v8::String::New(resource_name));
8384 script = v8::Script::Compile(source, &origin1);
8385 CheckTryCatchSourceInfo(script, resource_name, 0);
8386
8387 resource_name = "test2.js";
8388 v8::ScriptOrigin origin2(v8::String::New(resource_name), v8::Integer::New(7));
8389 script = v8::Script::Compile(source, &origin2);
8390 CheckTryCatchSourceInfo(script, resource_name, 7);
8391}
8392
8393
8394THREADED_TEST(CompilationCache) {
8395 v8::HandleScope scope;
8396 LocalContext context;
8397 v8::Handle<v8::String> source0 = v8::String::New("1234");
8398 v8::Handle<v8::String> source1 = v8::String::New("1234");
8399 v8::Handle<v8::Script> script0 =
8400 v8::Script::Compile(source0, v8::String::New("test.js"));
8401 v8::Handle<v8::Script> script1 =
8402 v8::Script::Compile(source1, v8::String::New("test.js"));
8403 v8::Handle<v8::Script> script2 =
8404 v8::Script::Compile(source0); // different origin
8405 CHECK_EQ(1234, script0->Run()->Int32Value());
8406 CHECK_EQ(1234, script1->Run()->Int32Value());
8407 CHECK_EQ(1234, script2->Run()->Int32Value());
8408}
8409
8410
8411static v8::Handle<Value> FunctionNameCallback(const v8::Arguments& args) {
8412 ApiTestFuzzer::Fuzz();
8413 return v8_num(42);
8414}
8415
8416
8417THREADED_TEST(CallbackFunctionName) {
8418 v8::HandleScope scope;
8419 LocalContext context;
8420 Local<ObjectTemplate> t = ObjectTemplate::New();
8421 t->Set(v8_str("asdf"), v8::FunctionTemplate::New(FunctionNameCallback));
8422 context->Global()->Set(v8_str("obj"), t->NewInstance());
8423 v8::Handle<v8::Value> value = CompileRun("obj.asdf.name");
8424 CHECK(value->IsString());
8425 v8::String::AsciiValue name(value);
8426 CHECK_EQ("asdf", *name);
8427}
8428
8429
8430THREADED_TEST(DateAccess) {
8431 v8::HandleScope scope;
8432 LocalContext context;
8433 v8::Handle<v8::Value> date = v8::Date::New(1224744689038.0);
8434 CHECK(date->IsDate());
Steve Block6ded16b2010-05-10 14:33:55 +01008435 CHECK_EQ(1224744689038.0, date.As<v8::Date>()->NumberValue());
Steve Blocka7e24c12009-10-30 11:49:00 +00008436}
8437
8438
8439void CheckProperties(v8::Handle<v8::Value> val, int elmc, const char* elmv[]) {
Steve Block6ded16b2010-05-10 14:33:55 +01008440 v8::Handle<v8::Object> obj = val.As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00008441 v8::Handle<v8::Array> props = obj->GetPropertyNames();
8442 CHECK_EQ(elmc, props->Length());
8443 for (int i = 0; i < elmc; i++) {
8444 v8::String::Utf8Value elm(props->Get(v8::Integer::New(i)));
8445 CHECK_EQ(elmv[i], *elm);
8446 }
8447}
8448
8449
8450THREADED_TEST(PropertyEnumeration) {
8451 v8::HandleScope scope;
8452 LocalContext context;
8453 v8::Handle<v8::Value> obj = v8::Script::Compile(v8::String::New(
8454 "var result = [];"
8455 "result[0] = {};"
8456 "result[1] = {a: 1, b: 2};"
8457 "result[2] = [1, 2, 3];"
8458 "var proto = {x: 1, y: 2, z: 3};"
8459 "var x = { __proto__: proto, w: 0, z: 1 };"
8460 "result[3] = x;"
8461 "result;"))->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01008462 v8::Handle<v8::Array> elms = obj.As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00008463 CHECK_EQ(4, elms->Length());
8464 int elmc0 = 0;
8465 const char** elmv0 = NULL;
8466 CheckProperties(elms->Get(v8::Integer::New(0)), elmc0, elmv0);
8467 int elmc1 = 2;
8468 const char* elmv1[] = {"a", "b"};
8469 CheckProperties(elms->Get(v8::Integer::New(1)), elmc1, elmv1);
8470 int elmc2 = 3;
8471 const char* elmv2[] = {"0", "1", "2"};
8472 CheckProperties(elms->Get(v8::Integer::New(2)), elmc2, elmv2);
8473 int elmc3 = 4;
8474 const char* elmv3[] = {"w", "z", "x", "y"};
8475 CheckProperties(elms->Get(v8::Integer::New(3)), elmc3, elmv3);
8476}
8477
8478
Steve Blocka7e24c12009-10-30 11:49:00 +00008479static bool NamedSetAccessBlocker(Local<v8::Object> obj,
8480 Local<Value> name,
8481 v8::AccessType type,
8482 Local<Value> data) {
8483 return type != v8::ACCESS_SET;
8484}
8485
8486
8487static bool IndexedSetAccessBlocker(Local<v8::Object> obj,
8488 uint32_t key,
8489 v8::AccessType type,
8490 Local<Value> data) {
8491 return type != v8::ACCESS_SET;
8492}
8493
8494
8495THREADED_TEST(DisableAccessChecksWhileConfiguring) {
8496 v8::HandleScope scope;
8497 LocalContext context;
8498 Local<ObjectTemplate> templ = ObjectTemplate::New();
8499 templ->SetAccessCheckCallbacks(NamedSetAccessBlocker,
8500 IndexedSetAccessBlocker);
8501 templ->Set(v8_str("x"), v8::True());
8502 Local<v8::Object> instance = templ->NewInstance();
8503 context->Global()->Set(v8_str("obj"), instance);
8504 Local<Value> value = CompileRun("obj.x");
8505 CHECK(value->BooleanValue());
8506}
8507
8508
8509static bool NamedGetAccessBlocker(Local<v8::Object> obj,
8510 Local<Value> name,
8511 v8::AccessType type,
8512 Local<Value> data) {
8513 return false;
8514}
8515
8516
8517static bool IndexedGetAccessBlocker(Local<v8::Object> obj,
8518 uint32_t key,
8519 v8::AccessType type,
8520 Local<Value> data) {
8521 return false;
8522}
8523
8524
8525
8526THREADED_TEST(AccessChecksReenabledCorrectly) {
8527 v8::HandleScope scope;
8528 LocalContext context;
8529 Local<ObjectTemplate> templ = ObjectTemplate::New();
8530 templ->SetAccessCheckCallbacks(NamedGetAccessBlocker,
8531 IndexedGetAccessBlocker);
8532 templ->Set(v8_str("a"), v8_str("a"));
8533 // Add more than 8 (see kMaxFastProperties) properties
8534 // so that the constructor will force copying map.
8535 // Cannot sprintf, gcc complains unsafety.
8536 char buf[4];
8537 for (char i = '0'; i <= '9' ; i++) {
8538 buf[0] = i;
8539 for (char j = '0'; j <= '9'; j++) {
8540 buf[1] = j;
8541 for (char k = '0'; k <= '9'; k++) {
8542 buf[2] = k;
8543 buf[3] = 0;
8544 templ->Set(v8_str(buf), v8::Number::New(k));
8545 }
8546 }
8547 }
8548
8549 Local<v8::Object> instance_1 = templ->NewInstance();
8550 context->Global()->Set(v8_str("obj_1"), instance_1);
8551
8552 Local<Value> value_1 = CompileRun("obj_1.a");
8553 CHECK(value_1->IsUndefined());
8554
8555 Local<v8::Object> instance_2 = templ->NewInstance();
8556 context->Global()->Set(v8_str("obj_2"), instance_2);
8557
8558 Local<Value> value_2 = CompileRun("obj_2.a");
8559 CHECK(value_2->IsUndefined());
8560}
8561
8562
8563// This tests that access check information remains on the global
8564// object template when creating contexts.
8565THREADED_TEST(AccessControlRepeatedContextCreation) {
8566 v8::HandleScope handle_scope;
8567 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
8568 global_template->SetAccessCheckCallbacks(NamedSetAccessBlocker,
8569 IndexedSetAccessBlocker);
8570 i::Handle<i::ObjectTemplateInfo> internal_template =
8571 v8::Utils::OpenHandle(*global_template);
8572 CHECK(!internal_template->constructor()->IsUndefined());
8573 i::Handle<i::FunctionTemplateInfo> constructor(
8574 i::FunctionTemplateInfo::cast(internal_template->constructor()));
8575 CHECK(!constructor->access_check_info()->IsUndefined());
8576 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
8577 CHECK(!constructor->access_check_info()->IsUndefined());
8578}
8579
8580
8581THREADED_TEST(TurnOnAccessCheck) {
8582 v8::HandleScope handle_scope;
8583
8584 // Create an environment with access check to the global object disabled by
8585 // default.
8586 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
8587 global_template->SetAccessCheckCallbacks(NamedGetAccessBlocker,
8588 IndexedGetAccessBlocker,
8589 v8::Handle<v8::Value>(),
8590 false);
8591 v8::Persistent<Context> context = Context::New(NULL, global_template);
8592 Context::Scope context_scope(context);
8593
8594 // Set up a property and a number of functions.
8595 context->Global()->Set(v8_str("a"), v8_num(1));
8596 CompileRun("function f1() {return a;}"
8597 "function f2() {return a;}"
8598 "function g1() {return h();}"
8599 "function g2() {return h();}"
8600 "function h() {return 1;}");
8601 Local<Function> f1 =
8602 Local<Function>::Cast(context->Global()->Get(v8_str("f1")));
8603 Local<Function> f2 =
8604 Local<Function>::Cast(context->Global()->Get(v8_str("f2")));
8605 Local<Function> g1 =
8606 Local<Function>::Cast(context->Global()->Get(v8_str("g1")));
8607 Local<Function> g2 =
8608 Local<Function>::Cast(context->Global()->Get(v8_str("g2")));
8609 Local<Function> h =
8610 Local<Function>::Cast(context->Global()->Get(v8_str("h")));
8611
8612 // Get the global object.
8613 v8::Handle<v8::Object> global = context->Global();
8614
8615 // Call f1 one time and f2 a number of times. This will ensure that f1 still
8616 // uses the runtime system to retreive property a whereas f2 uses global load
8617 // inline cache.
8618 CHECK(f1->Call(global, 0, NULL)->Equals(v8_num(1)));
8619 for (int i = 0; i < 4; i++) {
8620 CHECK(f2->Call(global, 0, NULL)->Equals(v8_num(1)));
8621 }
8622
8623 // Same for g1 and g2.
8624 CHECK(g1->Call(global, 0, NULL)->Equals(v8_num(1)));
8625 for (int i = 0; i < 4; i++) {
8626 CHECK(g2->Call(global, 0, NULL)->Equals(v8_num(1)));
8627 }
8628
8629 // Detach the global and turn on access check.
8630 context->DetachGlobal();
8631 context->Global()->TurnOnAccessCheck();
8632
8633 // Failing access check to property get results in undefined.
8634 CHECK(f1->Call(global, 0, NULL)->IsUndefined());
8635 CHECK(f2->Call(global, 0, NULL)->IsUndefined());
8636
8637 // Failing access check to function call results in exception.
8638 CHECK(g1->Call(global, 0, NULL).IsEmpty());
8639 CHECK(g2->Call(global, 0, NULL).IsEmpty());
8640
8641 // No failing access check when just returning a constant.
8642 CHECK(h->Call(global, 0, NULL)->Equals(v8_num(1)));
8643}
8644
8645
8646// This test verifies that pre-compilation (aka preparsing) can be called
8647// without initializing the whole VM. Thus we cannot run this test in a
8648// multi-threaded setup.
8649TEST(PreCompile) {
8650 // TODO(155): This test would break without the initialization of V8. This is
8651 // a workaround for now to make this test not fail.
8652 v8::V8::Initialize();
Leon Clarkef7060e22010-06-03 12:02:55 +01008653 const char* script = "function foo(a) { return a+1; }";
8654 v8::ScriptData* sd =
Steve Blockd0582a62009-12-15 09:54:21 +00008655 v8::ScriptData::PreCompile(script, i::StrLength(script));
Steve Blocka7e24c12009-10-30 11:49:00 +00008656 CHECK_NE(sd->Length(), 0);
8657 CHECK_NE(sd->Data(), NULL);
Leon Clarkee46be812010-01-19 14:06:41 +00008658 CHECK(!sd->HasError());
8659 delete sd;
8660}
8661
8662
8663TEST(PreCompileWithError) {
8664 v8::V8::Initialize();
Leon Clarkef7060e22010-06-03 12:02:55 +01008665 const char* script = "function foo(a) { return 1 * * 2; }";
8666 v8::ScriptData* sd =
Leon Clarkee46be812010-01-19 14:06:41 +00008667 v8::ScriptData::PreCompile(script, i::StrLength(script));
8668 CHECK(sd->HasError());
8669 delete sd;
8670}
8671
8672
8673TEST(Regress31661) {
8674 v8::V8::Initialize();
Leon Clarkef7060e22010-06-03 12:02:55 +01008675 const char* script = " The Definintive Guide";
8676 v8::ScriptData* sd =
Leon Clarkee46be812010-01-19 14:06:41 +00008677 v8::ScriptData::PreCompile(script, i::StrLength(script));
8678 CHECK(sd->HasError());
Steve Blocka7e24c12009-10-30 11:49:00 +00008679 delete sd;
8680}
8681
8682
Leon Clarkef7060e22010-06-03 12:02:55 +01008683// Tests that ScriptData can be serialized and deserialized.
8684TEST(PreCompileSerialization) {
8685 v8::V8::Initialize();
8686 const char* script = "function foo(a) { return a+1; }";
8687 v8::ScriptData* sd =
8688 v8::ScriptData::PreCompile(script, i::StrLength(script));
8689
8690 // Serialize.
8691 int serialized_data_length = sd->Length();
8692 char* serialized_data = i::NewArray<char>(serialized_data_length);
8693 memcpy(serialized_data, sd->Data(), serialized_data_length);
8694
8695 // Deserialize.
8696 v8::ScriptData* deserialized_sd =
8697 v8::ScriptData::New(serialized_data, serialized_data_length);
8698
8699 // Verify that the original is the same as the deserialized.
8700 CHECK_EQ(sd->Length(), deserialized_sd->Length());
8701 CHECK_EQ(0, memcmp(sd->Data(), deserialized_sd->Data(), sd->Length()));
8702 CHECK_EQ(sd->HasError(), deserialized_sd->HasError());
8703
8704 delete sd;
8705 delete deserialized_sd;
8706}
8707
8708
8709// Attempts to deserialize bad data.
8710TEST(PreCompileDeserializationError) {
8711 v8::V8::Initialize();
8712 const char* data = "DONT CARE";
8713 int invalid_size = 3;
8714 v8::ScriptData* sd = v8::ScriptData::New(data, invalid_size);
8715
8716 CHECK_EQ(0, sd->Length());
8717
8718 delete sd;
8719}
8720
8721
Leon Clarkeac952652010-07-15 11:15:24 +01008722// Attempts to deserialize bad data.
8723TEST(PreCompileInvalidPreparseDataError) {
8724 v8::V8::Initialize();
8725 v8::HandleScope scope;
8726 LocalContext context;
8727
8728 const char* script = "function foo(){ return 5;}\n"
8729 "function bar(){ return 6 + 7;} foo();";
8730 v8::ScriptData* sd =
8731 v8::ScriptData::PreCompile(script, i::StrLength(script));
8732 CHECK(!sd->HasError());
8733 // ScriptDataImpl private implementation details
Iain Merrick9ac36c92010-09-13 15:29:50 +01008734 const int kHeaderSize = i::ScriptDataImpl::kHeaderSize;
8735 const int kFunctionEntrySize = i::FunctionEntry::kSize;
Leon Clarkeac952652010-07-15 11:15:24 +01008736 const int kFunctionEntryStartOffset = 0;
8737 const int kFunctionEntryEndOffset = 1;
8738 unsigned* sd_data =
8739 reinterpret_cast<unsigned*>(const_cast<char*>(sd->Data()));
Leon Clarkeac952652010-07-15 11:15:24 +01008740
8741 // Overwrite function bar's end position with 0.
8742 sd_data[kHeaderSize + 1 * kFunctionEntrySize + kFunctionEntryEndOffset] = 0;
8743 v8::TryCatch try_catch;
8744
8745 Local<String> source = String::New(script);
8746 Local<Script> compiled_script = Script::New(source, NULL, sd);
8747 CHECK(try_catch.HasCaught());
8748 String::AsciiValue exception_value(try_catch.Message()->Get());
8749 CHECK_EQ("Uncaught SyntaxError: Invalid preparser data for function bar",
8750 *exception_value);
8751
8752 try_catch.Reset();
8753 // Overwrite function bar's start position with 200. The function entry
8754 // will not be found when searching for it by position.
Kristian Monsen80d68ea2010-09-08 11:05:35 +01008755 sd = v8::ScriptData::PreCompile(script, i::StrLength(script));
8756 sd_data = reinterpret_cast<unsigned*>(const_cast<char*>(sd->Data()));
Leon Clarkeac952652010-07-15 11:15:24 +01008757 sd_data[kHeaderSize + 1 * kFunctionEntrySize + kFunctionEntryStartOffset] =
8758 200;
8759 compiled_script = Script::New(source, NULL, sd);
8760 CHECK(try_catch.HasCaught());
8761 String::AsciiValue second_exception_value(try_catch.Message()->Get());
8762 CHECK_EQ("Uncaught SyntaxError: Invalid preparser data for function bar",
8763 *second_exception_value);
8764
8765 delete sd;
8766}
8767
8768
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008769// Verifies that the Handle<String> and const char* versions of the API produce
8770// the same results (at least for one trivial case).
8771TEST(PreCompileAPIVariationsAreSame) {
8772 v8::V8::Initialize();
8773 v8::HandleScope scope;
8774
8775 const char* cstring = "function foo(a) { return a+1; }";
Ben Murdoch3bec4d22010-07-22 14:51:16 +01008776
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008777 v8::ScriptData* sd_from_cstring =
8778 v8::ScriptData::PreCompile(cstring, i::StrLength(cstring));
8779
8780 TestAsciiResource* resource = new TestAsciiResource(cstring);
Ben Murdoch3bec4d22010-07-22 14:51:16 +01008781 v8::ScriptData* sd_from_external_string = v8::ScriptData::PreCompile(
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008782 v8::String::NewExternal(resource));
8783
Ben Murdoch3bec4d22010-07-22 14:51:16 +01008784 v8::ScriptData* sd_from_string = v8::ScriptData::PreCompile(
8785 v8::String::New(cstring));
8786
8787 CHECK_EQ(sd_from_cstring->Length(), sd_from_external_string->Length());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008788 CHECK_EQ(0, memcmp(sd_from_cstring->Data(),
Ben Murdoch3bec4d22010-07-22 14:51:16 +01008789 sd_from_external_string->Data(),
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008790 sd_from_cstring->Length()));
8791
Ben Murdoch3bec4d22010-07-22 14:51:16 +01008792 CHECK_EQ(sd_from_cstring->Length(), sd_from_string->Length());
8793 CHECK_EQ(0, memcmp(sd_from_cstring->Data(),
8794 sd_from_string->Data(),
8795 sd_from_cstring->Length()));
8796
8797
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008798 delete sd_from_cstring;
Ben Murdoch3bec4d22010-07-22 14:51:16 +01008799 delete sd_from_external_string;
8800 delete sd_from_string;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008801}
8802
8803
Steve Blocka7e24c12009-10-30 11:49:00 +00008804// This tests that we do not allow dictionary load/call inline caches
8805// to use functions that have not yet been compiled. The potential
8806// problem of loading a function that has not yet been compiled can
8807// arise because we share code between contexts via the compilation
8808// cache.
8809THREADED_TEST(DictionaryICLoadedFunction) {
8810 v8::HandleScope scope;
8811 // Test LoadIC.
8812 for (int i = 0; i < 2; i++) {
8813 LocalContext context;
8814 context->Global()->Set(v8_str("tmp"), v8::True());
8815 context->Global()->Delete(v8_str("tmp"));
8816 CompileRun("for (var j = 0; j < 10; j++) new RegExp('');");
8817 }
8818 // Test CallIC.
8819 for (int i = 0; i < 2; i++) {
8820 LocalContext context;
8821 context->Global()->Set(v8_str("tmp"), v8::True());
8822 context->Global()->Delete(v8_str("tmp"));
8823 CompileRun("for (var j = 0; j < 10; j++) RegExp('')");
8824 }
8825}
8826
8827
8828// Test that cross-context new calls use the context of the callee to
8829// create the new JavaScript object.
8830THREADED_TEST(CrossContextNew) {
8831 v8::HandleScope scope;
8832 v8::Persistent<Context> context0 = Context::New();
8833 v8::Persistent<Context> context1 = Context::New();
8834
8835 // Allow cross-domain access.
8836 Local<String> token = v8_str("<security token>");
8837 context0->SetSecurityToken(token);
8838 context1->SetSecurityToken(token);
8839
8840 // Set an 'x' property on the Object prototype and define a
8841 // constructor function in context0.
8842 context0->Enter();
8843 CompileRun("Object.prototype.x = 42; function C() {};");
8844 context0->Exit();
8845
8846 // Call the constructor function from context0 and check that the
8847 // result has the 'x' property.
8848 context1->Enter();
8849 context1->Global()->Set(v8_str("other"), context0->Global());
8850 Local<Value> value = CompileRun("var instance = new other.C(); instance.x");
8851 CHECK(value->IsInt32());
8852 CHECK_EQ(42, value->Int32Value());
8853 context1->Exit();
8854
8855 // Dispose the contexts to allow them to be garbage collected.
8856 context0.Dispose();
8857 context1.Dispose();
8858}
8859
8860
8861class RegExpInterruptTest {
8862 public:
8863 RegExpInterruptTest() : block_(NULL) {}
8864 ~RegExpInterruptTest() { delete block_; }
8865 void RunTest() {
8866 block_ = i::OS::CreateSemaphore(0);
8867 gc_count_ = 0;
8868 gc_during_regexp_ = 0;
8869 regexp_success_ = false;
8870 gc_success_ = false;
8871 GCThread gc_thread(this);
8872 gc_thread.Start();
8873 v8::Locker::StartPreemption(1);
8874
8875 LongRunningRegExp();
8876 {
8877 v8::Unlocker unlock;
8878 gc_thread.Join();
8879 }
8880 v8::Locker::StopPreemption();
8881 CHECK(regexp_success_);
8882 CHECK(gc_success_);
8883 }
8884 private:
8885 // Number of garbage collections required.
8886 static const int kRequiredGCs = 5;
8887
8888 class GCThread : public i::Thread {
8889 public:
8890 explicit GCThread(RegExpInterruptTest* test)
8891 : test_(test) {}
8892 virtual void Run() {
8893 test_->CollectGarbage();
8894 }
8895 private:
8896 RegExpInterruptTest* test_;
8897 };
8898
8899 void CollectGarbage() {
8900 block_->Wait();
8901 while (gc_during_regexp_ < kRequiredGCs) {
8902 {
8903 v8::Locker lock;
8904 // TODO(lrn): Perhaps create some garbage before collecting.
8905 i::Heap::CollectAllGarbage(false);
8906 gc_count_++;
8907 }
8908 i::OS::Sleep(1);
8909 }
8910 gc_success_ = true;
8911 }
8912
8913 void LongRunningRegExp() {
8914 block_->Signal(); // Enable garbage collection thread on next preemption.
8915 int rounds = 0;
8916 while (gc_during_regexp_ < kRequiredGCs) {
8917 int gc_before = gc_count_;
8918 {
8919 // Match 15-30 "a"'s against 14 and a "b".
8920 const char* c_source =
8921 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
8922 ".exec('aaaaaaaaaaaaaaab') === null";
8923 Local<String> source = String::New(c_source);
8924 Local<Script> script = Script::Compile(source);
8925 Local<Value> result = script->Run();
8926 if (!result->BooleanValue()) {
8927 gc_during_regexp_ = kRequiredGCs; // Allow gc thread to exit.
8928 return;
8929 }
8930 }
8931 {
8932 // Match 15-30 "a"'s against 15 and a "b".
8933 const char* c_source =
8934 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
8935 ".exec('aaaaaaaaaaaaaaaab')[0] === 'aaaaaaaaaaaaaaaa'";
8936 Local<String> source = String::New(c_source);
8937 Local<Script> script = Script::Compile(source);
8938 Local<Value> result = script->Run();
8939 if (!result->BooleanValue()) {
8940 gc_during_regexp_ = kRequiredGCs;
8941 return;
8942 }
8943 }
8944 int gc_after = gc_count_;
8945 gc_during_regexp_ += gc_after - gc_before;
8946 rounds++;
8947 i::OS::Sleep(1);
8948 }
8949 regexp_success_ = true;
8950 }
8951
8952 i::Semaphore* block_;
8953 int gc_count_;
8954 int gc_during_regexp_;
8955 bool regexp_success_;
8956 bool gc_success_;
8957};
8958
8959
8960// Test that a regular expression execution can be interrupted and
8961// survive a garbage collection.
8962TEST(RegExpInterruption) {
8963 v8::Locker lock;
8964 v8::V8::Initialize();
8965 v8::HandleScope scope;
8966 Local<Context> local_env;
8967 {
8968 LocalContext env;
8969 local_env = env.local();
8970 }
8971
8972 // Local context should still be live.
8973 CHECK(!local_env.IsEmpty());
8974 local_env->Enter();
8975
8976 // Should complete without problems.
8977 RegExpInterruptTest().RunTest();
8978
8979 local_env->Exit();
8980}
8981
8982
8983class ApplyInterruptTest {
8984 public:
8985 ApplyInterruptTest() : block_(NULL) {}
8986 ~ApplyInterruptTest() { delete block_; }
8987 void RunTest() {
8988 block_ = i::OS::CreateSemaphore(0);
8989 gc_count_ = 0;
8990 gc_during_apply_ = 0;
8991 apply_success_ = false;
8992 gc_success_ = false;
8993 GCThread gc_thread(this);
8994 gc_thread.Start();
8995 v8::Locker::StartPreemption(1);
8996
8997 LongRunningApply();
8998 {
8999 v8::Unlocker unlock;
9000 gc_thread.Join();
9001 }
9002 v8::Locker::StopPreemption();
9003 CHECK(apply_success_);
9004 CHECK(gc_success_);
9005 }
9006 private:
9007 // Number of garbage collections required.
9008 static const int kRequiredGCs = 2;
9009
9010 class GCThread : public i::Thread {
9011 public:
9012 explicit GCThread(ApplyInterruptTest* test)
9013 : test_(test) {}
9014 virtual void Run() {
9015 test_->CollectGarbage();
9016 }
9017 private:
9018 ApplyInterruptTest* test_;
9019 };
9020
9021 void CollectGarbage() {
9022 block_->Wait();
9023 while (gc_during_apply_ < kRequiredGCs) {
9024 {
9025 v8::Locker lock;
9026 i::Heap::CollectAllGarbage(false);
9027 gc_count_++;
9028 }
9029 i::OS::Sleep(1);
9030 }
9031 gc_success_ = true;
9032 }
9033
9034 void LongRunningApply() {
9035 block_->Signal();
9036 int rounds = 0;
9037 while (gc_during_apply_ < kRequiredGCs) {
9038 int gc_before = gc_count_;
9039 {
9040 const char* c_source =
9041 "function do_very_little(bar) {"
9042 " this.foo = bar;"
9043 "}"
9044 "for (var i = 0; i < 100000; i++) {"
9045 " do_very_little.apply(this, ['bar']);"
9046 "}";
9047 Local<String> source = String::New(c_source);
9048 Local<Script> script = Script::Compile(source);
9049 Local<Value> result = script->Run();
9050 // Check that no exception was thrown.
9051 CHECK(!result.IsEmpty());
9052 }
9053 int gc_after = gc_count_;
9054 gc_during_apply_ += gc_after - gc_before;
9055 rounds++;
9056 }
9057 apply_success_ = true;
9058 }
9059
9060 i::Semaphore* block_;
9061 int gc_count_;
9062 int gc_during_apply_;
9063 bool apply_success_;
9064 bool gc_success_;
9065};
9066
9067
9068// Test that nothing bad happens if we get a preemption just when we were
9069// about to do an apply().
9070TEST(ApplyInterruption) {
9071 v8::Locker lock;
9072 v8::V8::Initialize();
9073 v8::HandleScope scope;
9074 Local<Context> local_env;
9075 {
9076 LocalContext env;
9077 local_env = env.local();
9078 }
9079
9080 // Local context should still be live.
9081 CHECK(!local_env.IsEmpty());
9082 local_env->Enter();
9083
9084 // Should complete without problems.
9085 ApplyInterruptTest().RunTest();
9086
9087 local_env->Exit();
9088}
9089
9090
9091// Verify that we can clone an object
9092TEST(ObjectClone) {
9093 v8::HandleScope scope;
9094 LocalContext env;
9095
9096 const char* sample =
9097 "var rv = {};" \
9098 "rv.alpha = 'hello';" \
9099 "rv.beta = 123;" \
9100 "rv;";
9101
9102 // Create an object, verify basics.
9103 Local<Value> val = CompileRun(sample);
9104 CHECK(val->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01009105 Local<v8::Object> obj = val.As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00009106 obj->Set(v8_str("gamma"), v8_str("cloneme"));
9107
9108 CHECK_EQ(v8_str("hello"), obj->Get(v8_str("alpha")));
9109 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta")));
9110 CHECK_EQ(v8_str("cloneme"), obj->Get(v8_str("gamma")));
9111
9112 // Clone it.
9113 Local<v8::Object> clone = obj->Clone();
9114 CHECK_EQ(v8_str("hello"), clone->Get(v8_str("alpha")));
9115 CHECK_EQ(v8::Integer::New(123), clone->Get(v8_str("beta")));
9116 CHECK_EQ(v8_str("cloneme"), clone->Get(v8_str("gamma")));
9117
9118 // Set a property on the clone, verify each object.
9119 clone->Set(v8_str("beta"), v8::Integer::New(456));
9120 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta")));
9121 CHECK_EQ(v8::Integer::New(456), clone->Get(v8_str("beta")));
9122}
9123
9124
9125class AsciiVectorResource : public v8::String::ExternalAsciiStringResource {
9126 public:
9127 explicit AsciiVectorResource(i::Vector<const char> vector)
9128 : data_(vector) {}
9129 virtual ~AsciiVectorResource() {}
9130 virtual size_t length() const { return data_.length(); }
9131 virtual const char* data() const { return data_.start(); }
9132 private:
9133 i::Vector<const char> data_;
9134};
9135
9136
9137class UC16VectorResource : public v8::String::ExternalStringResource {
9138 public:
9139 explicit UC16VectorResource(i::Vector<const i::uc16> vector)
9140 : data_(vector) {}
9141 virtual ~UC16VectorResource() {}
9142 virtual size_t length() const { return data_.length(); }
9143 virtual const i::uc16* data() const { return data_.start(); }
9144 private:
9145 i::Vector<const i::uc16> data_;
9146};
9147
9148
9149static void MorphAString(i::String* string,
9150 AsciiVectorResource* ascii_resource,
9151 UC16VectorResource* uc16_resource) {
9152 CHECK(i::StringShape(string).IsExternal());
9153 if (string->IsAsciiRepresentation()) {
9154 // Check old map is not symbol or long.
Steve Blockd0582a62009-12-15 09:54:21 +00009155 CHECK(string->map() == i::Heap::external_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00009156 // Morph external string to be TwoByte string.
Steve Blockd0582a62009-12-15 09:54:21 +00009157 string->set_map(i::Heap::external_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00009158 i::ExternalTwoByteString* morphed =
9159 i::ExternalTwoByteString::cast(string);
9160 morphed->set_resource(uc16_resource);
9161 } else {
9162 // Check old map is not symbol or long.
Steve Blockd0582a62009-12-15 09:54:21 +00009163 CHECK(string->map() == i::Heap::external_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00009164 // Morph external string to be ASCII string.
Steve Blockd0582a62009-12-15 09:54:21 +00009165 string->set_map(i::Heap::external_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00009166 i::ExternalAsciiString* morphed =
9167 i::ExternalAsciiString::cast(string);
9168 morphed->set_resource(ascii_resource);
9169 }
9170}
9171
9172
9173// Test that we can still flatten a string if the components it is built up
9174// from have been turned into 16 bit strings in the mean time.
9175THREADED_TEST(MorphCompositeStringTest) {
9176 const char* c_string = "Now is the time for all good men"
9177 " to come to the aid of the party";
9178 uint16_t* two_byte_string = AsciiToTwoByteString(c_string);
9179 {
9180 v8::HandleScope scope;
9181 LocalContext env;
9182 AsciiVectorResource ascii_resource(
Steve Blockd0582a62009-12-15 09:54:21 +00009183 i::Vector<const char>(c_string, i::StrLength(c_string)));
Steve Blocka7e24c12009-10-30 11:49:00 +00009184 UC16VectorResource uc16_resource(
Steve Blockd0582a62009-12-15 09:54:21 +00009185 i::Vector<const uint16_t>(two_byte_string,
9186 i::StrLength(c_string)));
Steve Blocka7e24c12009-10-30 11:49:00 +00009187
9188 Local<String> lhs(v8::Utils::ToLocal(
9189 i::Factory::NewExternalStringFromAscii(&ascii_resource)));
9190 Local<String> rhs(v8::Utils::ToLocal(
9191 i::Factory::NewExternalStringFromAscii(&ascii_resource)));
9192
9193 env->Global()->Set(v8_str("lhs"), lhs);
9194 env->Global()->Set(v8_str("rhs"), rhs);
9195
9196 CompileRun(
9197 "var cons = lhs + rhs;"
9198 "var slice = lhs.substring(1, lhs.length - 1);"
9199 "var slice_on_cons = (lhs + rhs).substring(1, lhs.length *2 - 1);");
9200
9201 MorphAString(*v8::Utils::OpenHandle(*lhs), &ascii_resource, &uc16_resource);
9202 MorphAString(*v8::Utils::OpenHandle(*rhs), &ascii_resource, &uc16_resource);
9203
9204 // Now do some stuff to make sure the strings are flattened, etc.
9205 CompileRun(
9206 "/[^a-z]/.test(cons);"
9207 "/[^a-z]/.test(slice);"
9208 "/[^a-z]/.test(slice_on_cons);");
9209 const char* expected_cons =
9210 "Now is the time for all good men to come to the aid of the party"
9211 "Now is the time for all good men to come to the aid of the party";
9212 const char* expected_slice =
9213 "ow is the time for all good men to come to the aid of the part";
9214 const char* expected_slice_on_cons =
9215 "ow is the time for all good men to come to the aid of the party"
9216 "Now is the time for all good men to come to the aid of the part";
9217 CHECK_EQ(String::New(expected_cons),
9218 env->Global()->Get(v8_str("cons")));
9219 CHECK_EQ(String::New(expected_slice),
9220 env->Global()->Get(v8_str("slice")));
9221 CHECK_EQ(String::New(expected_slice_on_cons),
9222 env->Global()->Get(v8_str("slice_on_cons")));
9223 }
Ben Murdoch3bec4d22010-07-22 14:51:16 +01009224 i::DeleteArray(two_byte_string);
Steve Blocka7e24c12009-10-30 11:49:00 +00009225}
9226
9227
9228TEST(CompileExternalTwoByteSource) {
9229 v8::HandleScope scope;
9230 LocalContext context;
9231
9232 // This is a very short list of sources, which currently is to check for a
9233 // regression caused by r2703.
9234 const char* ascii_sources[] = {
9235 "0.5",
9236 "-0.5", // This mainly testes PushBack in the Scanner.
9237 "--0.5", // This mainly testes PushBack in the Scanner.
9238 NULL
9239 };
9240
9241 // Compile the sources as external two byte strings.
9242 for (int i = 0; ascii_sources[i] != NULL; i++) {
9243 uint16_t* two_byte_string = AsciiToTwoByteString(ascii_sources[i]);
9244 UC16VectorResource uc16_resource(
Steve Blockd0582a62009-12-15 09:54:21 +00009245 i::Vector<const uint16_t>(two_byte_string,
9246 i::StrLength(ascii_sources[i])));
Steve Blocka7e24c12009-10-30 11:49:00 +00009247 v8::Local<v8::String> source = v8::String::NewExternal(&uc16_resource);
9248 v8::Script::Compile(source);
Ben Murdoch3bec4d22010-07-22 14:51:16 +01009249 i::DeleteArray(two_byte_string);
Steve Blocka7e24c12009-10-30 11:49:00 +00009250 }
9251}
9252
9253
9254class RegExpStringModificationTest {
9255 public:
9256 RegExpStringModificationTest()
9257 : block_(i::OS::CreateSemaphore(0)),
9258 morphs_(0),
9259 morphs_during_regexp_(0),
9260 ascii_resource_(i::Vector<const char>("aaaaaaaaaaaaaab", 15)),
9261 uc16_resource_(i::Vector<const uint16_t>(two_byte_content_, 15)) {}
9262 ~RegExpStringModificationTest() { delete block_; }
9263 void RunTest() {
9264 regexp_success_ = false;
9265 morph_success_ = false;
9266
9267 // Initialize the contents of two_byte_content_ to be a uc16 representation
9268 // of "aaaaaaaaaaaaaab".
9269 for (int i = 0; i < 14; i++) {
9270 two_byte_content_[i] = 'a';
9271 }
9272 two_byte_content_[14] = 'b';
9273
9274 // Create the input string for the regexp - the one we are going to change
9275 // properties of.
9276 input_ = i::Factory::NewExternalStringFromAscii(&ascii_resource_);
9277
9278 // Inject the input as a global variable.
9279 i::Handle<i::String> input_name =
9280 i::Factory::NewStringFromAscii(i::Vector<const char>("input", 5));
9281 i::Top::global_context()->global()->SetProperty(*input_name, *input_, NONE);
9282
9283
9284 MorphThread morph_thread(this);
9285 morph_thread.Start();
9286 v8::Locker::StartPreemption(1);
9287 LongRunningRegExp();
9288 {
9289 v8::Unlocker unlock;
9290 morph_thread.Join();
9291 }
9292 v8::Locker::StopPreemption();
9293 CHECK(regexp_success_);
9294 CHECK(morph_success_);
9295 }
9296 private:
9297
9298 // Number of string modifications required.
9299 static const int kRequiredModifications = 5;
9300 static const int kMaxModifications = 100;
9301
9302 class MorphThread : public i::Thread {
9303 public:
9304 explicit MorphThread(RegExpStringModificationTest* test)
9305 : test_(test) {}
9306 virtual void Run() {
9307 test_->MorphString();
9308 }
9309 private:
9310 RegExpStringModificationTest* test_;
9311 };
9312
9313 void MorphString() {
9314 block_->Wait();
9315 while (morphs_during_regexp_ < kRequiredModifications &&
9316 morphs_ < kMaxModifications) {
9317 {
9318 v8::Locker lock;
9319 // Swap string between ascii and two-byte representation.
9320 i::String* string = *input_;
9321 MorphAString(string, &ascii_resource_, &uc16_resource_);
9322 morphs_++;
9323 }
9324 i::OS::Sleep(1);
9325 }
9326 morph_success_ = true;
9327 }
9328
9329 void LongRunningRegExp() {
9330 block_->Signal(); // Enable morphing thread on next preemption.
9331 while (morphs_during_regexp_ < kRequiredModifications &&
9332 morphs_ < kMaxModifications) {
9333 int morphs_before = morphs_;
9334 {
Steve Block791712a2010-08-27 10:21:07 +01009335 v8::HandleScope scope;
Steve Blocka7e24c12009-10-30 11:49:00 +00009336 // Match 15-30 "a"'s against 14 and a "b".
9337 const char* c_source =
9338 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
9339 ".exec(input) === null";
9340 Local<String> source = String::New(c_source);
9341 Local<Script> script = Script::Compile(source);
9342 Local<Value> result = script->Run();
9343 CHECK(result->IsTrue());
9344 }
9345 int morphs_after = morphs_;
9346 morphs_during_regexp_ += morphs_after - morphs_before;
9347 }
9348 regexp_success_ = true;
9349 }
9350
9351 i::uc16 two_byte_content_[15];
9352 i::Semaphore* block_;
9353 int morphs_;
9354 int morphs_during_regexp_;
9355 bool regexp_success_;
9356 bool morph_success_;
9357 i::Handle<i::String> input_;
9358 AsciiVectorResource ascii_resource_;
9359 UC16VectorResource uc16_resource_;
9360};
9361
9362
9363// Test that a regular expression execution can be interrupted and
9364// the string changed without failing.
9365TEST(RegExpStringModification) {
9366 v8::Locker lock;
9367 v8::V8::Initialize();
9368 v8::HandleScope scope;
9369 Local<Context> local_env;
9370 {
9371 LocalContext env;
9372 local_env = env.local();
9373 }
9374
9375 // Local context should still be live.
9376 CHECK(!local_env.IsEmpty());
9377 local_env->Enter();
9378
9379 // Should complete without problems.
9380 RegExpStringModificationTest().RunTest();
9381
9382 local_env->Exit();
9383}
9384
9385
9386// Test that we can set a property on the global object even if there
9387// is a read-only property in the prototype chain.
9388TEST(ReadOnlyPropertyInGlobalProto) {
9389 v8::HandleScope scope;
9390 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
9391 LocalContext context(0, templ);
9392 v8::Handle<v8::Object> global = context->Global();
9393 v8::Handle<v8::Object> global_proto =
9394 v8::Handle<v8::Object>::Cast(global->Get(v8_str("__proto__")));
9395 global_proto->Set(v8_str("x"), v8::Integer::New(0), v8::ReadOnly);
9396 global_proto->Set(v8_str("y"), v8::Integer::New(0), v8::ReadOnly);
9397 // Check without 'eval' or 'with'.
9398 v8::Handle<v8::Value> res =
9399 CompileRun("function f() { x = 42; return x; }; f()");
9400 // Check with 'eval'.
9401 res = CompileRun("function f() { eval('1'); y = 42; return y; }; f()");
9402 CHECK_EQ(v8::Integer::New(42), res);
9403 // Check with 'with'.
9404 res = CompileRun("function f() { with (this) { y = 42 }; return y; }; f()");
9405 CHECK_EQ(v8::Integer::New(42), res);
9406}
9407
9408static int force_set_set_count = 0;
9409static int force_set_get_count = 0;
9410bool pass_on_get = false;
9411
9412static v8::Handle<v8::Value> ForceSetGetter(v8::Local<v8::String> name,
9413 const v8::AccessorInfo& info) {
9414 force_set_get_count++;
9415 if (pass_on_get) {
9416 return v8::Handle<v8::Value>();
9417 } else {
9418 return v8::Int32::New(3);
9419 }
9420}
9421
9422static void ForceSetSetter(v8::Local<v8::String> name,
9423 v8::Local<v8::Value> value,
9424 const v8::AccessorInfo& info) {
9425 force_set_set_count++;
9426}
9427
9428static v8::Handle<v8::Value> ForceSetInterceptSetter(
9429 v8::Local<v8::String> name,
9430 v8::Local<v8::Value> value,
9431 const v8::AccessorInfo& info) {
9432 force_set_set_count++;
9433 return v8::Undefined();
9434}
9435
9436TEST(ForceSet) {
9437 force_set_get_count = 0;
9438 force_set_set_count = 0;
9439 pass_on_get = false;
9440
9441 v8::HandleScope scope;
9442 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
9443 v8::Handle<v8::String> access_property = v8::String::New("a");
9444 templ->SetAccessor(access_property, ForceSetGetter, ForceSetSetter);
9445 LocalContext context(NULL, templ);
9446 v8::Handle<v8::Object> global = context->Global();
9447
9448 // Ordinary properties
9449 v8::Handle<v8::String> simple_property = v8::String::New("p");
9450 global->Set(simple_property, v8::Int32::New(4), v8::ReadOnly);
9451 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
9452 // This should fail because the property is read-only
9453 global->Set(simple_property, v8::Int32::New(5));
9454 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
9455 // This should succeed even though the property is read-only
9456 global->ForceSet(simple_property, v8::Int32::New(6));
9457 CHECK_EQ(6, global->Get(simple_property)->Int32Value());
9458
9459 // Accessors
9460 CHECK_EQ(0, force_set_set_count);
9461 CHECK_EQ(0, force_set_get_count);
9462 CHECK_EQ(3, global->Get(access_property)->Int32Value());
9463 // CHECK_EQ the property shouldn't override it, just call the setter
9464 // which in this case does nothing.
9465 global->Set(access_property, v8::Int32::New(7));
9466 CHECK_EQ(3, global->Get(access_property)->Int32Value());
9467 CHECK_EQ(1, force_set_set_count);
9468 CHECK_EQ(2, force_set_get_count);
9469 // Forcing the property to be set should override the accessor without
9470 // calling it
9471 global->ForceSet(access_property, v8::Int32::New(8));
9472 CHECK_EQ(8, global->Get(access_property)->Int32Value());
9473 CHECK_EQ(1, force_set_set_count);
9474 CHECK_EQ(2, force_set_get_count);
9475}
9476
9477TEST(ForceSetWithInterceptor) {
9478 force_set_get_count = 0;
9479 force_set_set_count = 0;
9480 pass_on_get = false;
9481
9482 v8::HandleScope scope;
9483 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
9484 templ->SetNamedPropertyHandler(ForceSetGetter, ForceSetInterceptSetter);
9485 LocalContext context(NULL, templ);
9486 v8::Handle<v8::Object> global = context->Global();
9487
9488 v8::Handle<v8::String> some_property = v8::String::New("a");
9489 CHECK_EQ(0, force_set_set_count);
9490 CHECK_EQ(0, force_set_get_count);
9491 CHECK_EQ(3, global->Get(some_property)->Int32Value());
9492 // Setting the property shouldn't override it, just call the setter
9493 // which in this case does nothing.
9494 global->Set(some_property, v8::Int32::New(7));
9495 CHECK_EQ(3, global->Get(some_property)->Int32Value());
9496 CHECK_EQ(1, force_set_set_count);
9497 CHECK_EQ(2, force_set_get_count);
9498 // Getting the property when the interceptor returns an empty handle
9499 // should yield undefined, since the property isn't present on the
9500 // object itself yet.
9501 pass_on_get = true;
9502 CHECK(global->Get(some_property)->IsUndefined());
9503 CHECK_EQ(1, force_set_set_count);
9504 CHECK_EQ(3, force_set_get_count);
9505 // Forcing the property to be set should cause the value to be
9506 // set locally without calling the interceptor.
9507 global->ForceSet(some_property, v8::Int32::New(8));
9508 CHECK_EQ(8, global->Get(some_property)->Int32Value());
9509 CHECK_EQ(1, force_set_set_count);
9510 CHECK_EQ(4, force_set_get_count);
9511 // Reenabling the interceptor should cause it to take precedence over
9512 // the property
9513 pass_on_get = false;
9514 CHECK_EQ(3, global->Get(some_property)->Int32Value());
9515 CHECK_EQ(1, force_set_set_count);
9516 CHECK_EQ(5, force_set_get_count);
9517 // The interceptor should also work for other properties
9518 CHECK_EQ(3, global->Get(v8::String::New("b"))->Int32Value());
9519 CHECK_EQ(1, force_set_set_count);
9520 CHECK_EQ(6, force_set_get_count);
9521}
9522
9523
9524THREADED_TEST(ForceDelete) {
9525 v8::HandleScope scope;
9526 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
9527 LocalContext context(NULL, templ);
9528 v8::Handle<v8::Object> global = context->Global();
9529
9530 // Ordinary properties
9531 v8::Handle<v8::String> simple_property = v8::String::New("p");
9532 global->Set(simple_property, v8::Int32::New(4), v8::DontDelete);
9533 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
9534 // This should fail because the property is dont-delete.
9535 CHECK(!global->Delete(simple_property));
9536 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
9537 // This should succeed even though the property is dont-delete.
9538 CHECK(global->ForceDelete(simple_property));
9539 CHECK(global->Get(simple_property)->IsUndefined());
9540}
9541
9542
9543static int force_delete_interceptor_count = 0;
9544static bool pass_on_delete = false;
9545
9546
9547static v8::Handle<v8::Boolean> ForceDeleteDeleter(
9548 v8::Local<v8::String> name,
9549 const v8::AccessorInfo& info) {
9550 force_delete_interceptor_count++;
9551 if (pass_on_delete) {
9552 return v8::Handle<v8::Boolean>();
9553 } else {
9554 return v8::True();
9555 }
9556}
9557
9558
9559THREADED_TEST(ForceDeleteWithInterceptor) {
9560 force_delete_interceptor_count = 0;
9561 pass_on_delete = false;
9562
9563 v8::HandleScope scope;
9564 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
9565 templ->SetNamedPropertyHandler(0, 0, 0, ForceDeleteDeleter);
9566 LocalContext context(NULL, templ);
9567 v8::Handle<v8::Object> global = context->Global();
9568
9569 v8::Handle<v8::String> some_property = v8::String::New("a");
9570 global->Set(some_property, v8::Integer::New(42), v8::DontDelete);
9571
9572 // Deleting a property should get intercepted and nothing should
9573 // happen.
9574 CHECK_EQ(0, force_delete_interceptor_count);
9575 CHECK(global->Delete(some_property));
9576 CHECK_EQ(1, force_delete_interceptor_count);
9577 CHECK_EQ(42, global->Get(some_property)->Int32Value());
9578 // Deleting the property when the interceptor returns an empty
9579 // handle should not delete the property since it is DontDelete.
9580 pass_on_delete = true;
9581 CHECK(!global->Delete(some_property));
9582 CHECK_EQ(2, force_delete_interceptor_count);
9583 CHECK_EQ(42, global->Get(some_property)->Int32Value());
9584 // Forcing the property to be deleted should delete the value
9585 // without calling the interceptor.
9586 CHECK(global->ForceDelete(some_property));
9587 CHECK(global->Get(some_property)->IsUndefined());
9588 CHECK_EQ(2, force_delete_interceptor_count);
9589}
9590
9591
9592// Make sure that forcing a delete invalidates any IC stubs, so we
9593// don't read the hole value.
9594THREADED_TEST(ForceDeleteIC) {
9595 v8::HandleScope scope;
9596 LocalContext context;
9597 // Create a DontDelete variable on the global object.
9598 CompileRun("this.__proto__ = { foo: 'horse' };"
9599 "var foo = 'fish';"
9600 "function f() { return foo.length; }");
9601 // Initialize the IC for foo in f.
9602 CompileRun("for (var i = 0; i < 4; i++) f();");
9603 // Make sure the value of foo is correct before the deletion.
9604 CHECK_EQ(4, CompileRun("f()")->Int32Value());
9605 // Force the deletion of foo.
9606 CHECK(context->Global()->ForceDelete(v8_str("foo")));
9607 // Make sure the value for foo is read from the prototype, and that
9608 // we don't get in trouble with reading the deleted cell value
9609 // sentinel.
9610 CHECK_EQ(5, CompileRun("f()")->Int32Value());
9611}
9612
9613
9614v8::Persistent<Context> calling_context0;
9615v8::Persistent<Context> calling_context1;
9616v8::Persistent<Context> calling_context2;
9617
9618
9619// Check that the call to the callback is initiated in
9620// calling_context2, the directly calling context is calling_context1
9621// and the callback itself is in calling_context0.
9622static v8::Handle<Value> GetCallingContextCallback(const v8::Arguments& args) {
9623 ApiTestFuzzer::Fuzz();
9624 CHECK(Context::GetCurrent() == calling_context0);
9625 CHECK(Context::GetCalling() == calling_context1);
9626 CHECK(Context::GetEntered() == calling_context2);
9627 return v8::Integer::New(42);
9628}
9629
9630
9631THREADED_TEST(GetCallingContext) {
9632 v8::HandleScope scope;
9633
9634 calling_context0 = Context::New();
9635 calling_context1 = Context::New();
9636 calling_context2 = Context::New();
9637
9638 // Allow cross-domain access.
9639 Local<String> token = v8_str("<security token>");
9640 calling_context0->SetSecurityToken(token);
9641 calling_context1->SetSecurityToken(token);
9642 calling_context2->SetSecurityToken(token);
9643
9644 // Create an object with a C++ callback in context0.
9645 calling_context0->Enter();
9646 Local<v8::FunctionTemplate> callback_templ =
9647 v8::FunctionTemplate::New(GetCallingContextCallback);
9648 calling_context0->Global()->Set(v8_str("callback"),
9649 callback_templ->GetFunction());
9650 calling_context0->Exit();
9651
9652 // Expose context0 in context1 and setup a function that calls the
9653 // callback function.
9654 calling_context1->Enter();
9655 calling_context1->Global()->Set(v8_str("context0"),
9656 calling_context0->Global());
9657 CompileRun("function f() { context0.callback() }");
9658 calling_context1->Exit();
9659
9660 // Expose context1 in context2 and call the callback function in
9661 // context0 indirectly through f in context1.
9662 calling_context2->Enter();
9663 calling_context2->Global()->Set(v8_str("context1"),
9664 calling_context1->Global());
9665 CompileRun("context1.f()");
9666 calling_context2->Exit();
9667
9668 // Dispose the contexts to allow them to be garbage collected.
9669 calling_context0.Dispose();
9670 calling_context1.Dispose();
9671 calling_context2.Dispose();
9672 calling_context0.Clear();
9673 calling_context1.Clear();
9674 calling_context2.Clear();
9675}
9676
9677
9678// Check that a variable declaration with no explicit initialization
9679// value does not shadow an existing property in the prototype chain.
9680//
9681// This is consistent with Firefox and Safari.
9682//
9683// See http://crbug.com/12548.
9684THREADED_TEST(InitGlobalVarInProtoChain) {
9685 v8::HandleScope scope;
9686 LocalContext context;
9687 // Introduce a variable in the prototype chain.
9688 CompileRun("__proto__.x = 42");
9689 v8::Handle<v8::Value> result = CompileRun("var x; x");
9690 CHECK(!result->IsUndefined());
9691 CHECK_EQ(42, result->Int32Value());
9692}
9693
9694
9695// Regression test for issue 398.
9696// If a function is added to an object, creating a constant function
9697// field, and the result is cloned, replacing the constant function on the
9698// original should not affect the clone.
9699// See http://code.google.com/p/v8/issues/detail?id=398
9700THREADED_TEST(ReplaceConstantFunction) {
9701 v8::HandleScope scope;
9702 LocalContext context;
9703 v8::Handle<v8::Object> obj = v8::Object::New();
9704 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New();
9705 v8::Handle<v8::String> foo_string = v8::String::New("foo");
9706 obj->Set(foo_string, func_templ->GetFunction());
9707 v8::Handle<v8::Object> obj_clone = obj->Clone();
9708 obj_clone->Set(foo_string, v8::String::New("Hello"));
9709 CHECK(!obj->Get(foo_string)->IsUndefined());
9710}
9711
9712
9713// Regression test for http://crbug.com/16276.
9714THREADED_TEST(Regress16276) {
9715 v8::HandleScope scope;
9716 LocalContext context;
9717 // Force the IC in f to be a dictionary load IC.
9718 CompileRun("function f(obj) { return obj.x; }\n"
9719 "var obj = { x: { foo: 42 }, y: 87 };\n"
9720 "var x = obj.x;\n"
9721 "delete obj.y;\n"
9722 "for (var i = 0; i < 5; i++) f(obj);");
9723 // Detach the global object to make 'this' refer directly to the
9724 // global object (not the proxy), and make sure that the dictionary
9725 // load IC doesn't mess up loading directly from the global object.
9726 context->DetachGlobal();
9727 CHECK_EQ(42, CompileRun("f(this).foo")->Int32Value());
9728}
9729
9730
9731THREADED_TEST(PixelArray) {
9732 v8::HandleScope scope;
9733 LocalContext context;
Steve Blockd0582a62009-12-15 09:54:21 +00009734 const int kElementCount = 260;
Steve Blocka7e24c12009-10-30 11:49:00 +00009735 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(kElementCount));
9736 i::Handle<i::PixelArray> pixels = i::Factory::NewPixelArray(kElementCount,
9737 pixel_data);
9738 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9739 for (int i = 0; i < kElementCount; i++) {
Steve Blockd0582a62009-12-15 09:54:21 +00009740 pixels->set(i, i % 256);
Steve Blocka7e24c12009-10-30 11:49:00 +00009741 }
9742 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9743 for (int i = 0; i < kElementCount; i++) {
Steve Blockd0582a62009-12-15 09:54:21 +00009744 CHECK_EQ(i % 256, pixels->get(i));
9745 CHECK_EQ(i % 256, pixel_data[i]);
Steve Blocka7e24c12009-10-30 11:49:00 +00009746 }
9747
9748 v8::Handle<v8::Object> obj = v8::Object::New();
9749 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj);
9750 // Set the elements to be the pixels.
9751 // jsobj->set_elements(*pixels);
9752 obj->SetIndexedPropertiesToPixelData(pixel_data, kElementCount);
9753 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1))->value());
9754 obj->Set(v8_str("field"), v8::Int32::New(1503));
9755 context->Global()->Set(v8_str("pixels"), obj);
9756 v8::Handle<v8::Value> result = CompileRun("pixels.field");
9757 CHECK_EQ(1503, result->Int32Value());
9758 result = CompileRun("pixels[1]");
9759 CHECK_EQ(1, result->Int32Value());
9760
9761 result = CompileRun("var sum = 0;"
9762 "for (var i = 0; i < 8; i++) {"
9763 " sum += pixels[i] = pixels[i] = -i;"
9764 "}"
9765 "sum;");
9766 CHECK_EQ(-28, result->Int32Value());
9767
9768 result = CompileRun("var sum = 0;"
9769 "for (var i = 0; i < 8; i++) {"
9770 " sum += pixels[i] = pixels[i] = 0;"
9771 "}"
9772 "sum;");
9773 CHECK_EQ(0, result->Int32Value());
9774
9775 result = CompileRun("var sum = 0;"
9776 "for (var i = 0; i < 8; i++) {"
9777 " sum += pixels[i] = pixels[i] = 255;"
9778 "}"
9779 "sum;");
9780 CHECK_EQ(8 * 255, result->Int32Value());
9781
9782 result = CompileRun("var sum = 0;"
9783 "for (var i = 0; i < 8; i++) {"
9784 " sum += pixels[i] = pixels[i] = 256 + i;"
9785 "}"
9786 "sum;");
9787 CHECK_EQ(2076, result->Int32Value());
9788
9789 result = CompileRun("var sum = 0;"
9790 "for (var i = 0; i < 8; i++) {"
9791 " sum += pixels[i] = pixels[i] = i;"
9792 "}"
9793 "sum;");
9794 CHECK_EQ(28, result->Int32Value());
9795
9796 result = CompileRun("var sum = 0;"
9797 "for (var i = 0; i < 8; i++) {"
9798 " sum += pixels[i];"
9799 "}"
9800 "sum;");
9801 CHECK_EQ(28, result->Int32Value());
9802
9803 i::Handle<i::Smi> value(i::Smi::FromInt(2));
9804 i::SetElement(jsobj, 1, value);
9805 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(1))->value());
9806 *value.location() = i::Smi::FromInt(256);
9807 i::SetElement(jsobj, 1, value);
9808 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(1))->value());
9809 *value.location() = i::Smi::FromInt(-1);
9810 i::SetElement(jsobj, 1, value);
9811 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1))->value());
9812
9813 result = CompileRun("for (var i = 0; i < 8; i++) {"
9814 " pixels[i] = (i * 65) - 109;"
9815 "}"
9816 "pixels[1] + pixels[6];");
9817 CHECK_EQ(255, result->Int32Value());
9818 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0))->value());
9819 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1))->value());
9820 CHECK_EQ(21, i::Smi::cast(jsobj->GetElement(2))->value());
9821 CHECK_EQ(86, i::Smi::cast(jsobj->GetElement(3))->value());
9822 CHECK_EQ(151, i::Smi::cast(jsobj->GetElement(4))->value());
9823 CHECK_EQ(216, i::Smi::cast(jsobj->GetElement(5))->value());
9824 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(6))->value());
9825 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(7))->value());
9826 result = CompileRun("var sum = 0;"
9827 "for (var i = 0; i < 8; i++) {"
9828 " sum += pixels[i];"
9829 "}"
9830 "sum;");
9831 CHECK_EQ(984, result->Int32Value());
9832
9833 result = CompileRun("for (var i = 0; i < 8; i++) {"
9834 " pixels[i] = (i * 1.1);"
9835 "}"
9836 "pixels[1] + pixels[6];");
9837 CHECK_EQ(8, result->Int32Value());
9838 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0))->value());
9839 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1))->value());
9840 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(2))->value());
9841 CHECK_EQ(3, i::Smi::cast(jsobj->GetElement(3))->value());
9842 CHECK_EQ(4, i::Smi::cast(jsobj->GetElement(4))->value());
9843 CHECK_EQ(6, i::Smi::cast(jsobj->GetElement(5))->value());
9844 CHECK_EQ(7, i::Smi::cast(jsobj->GetElement(6))->value());
9845 CHECK_EQ(8, i::Smi::cast(jsobj->GetElement(7))->value());
9846
9847 result = CompileRun("for (var i = 0; i < 8; i++) {"
9848 " pixels[7] = undefined;"
9849 "}"
9850 "pixels[7];");
9851 CHECK_EQ(0, result->Int32Value());
9852 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(7))->value());
9853
9854 result = CompileRun("for (var i = 0; i < 8; i++) {"
9855 " pixels[6] = '2.3';"
9856 "}"
9857 "pixels[6];");
9858 CHECK_EQ(2, result->Int32Value());
9859 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(6))->value());
9860
9861 result = CompileRun("for (var i = 0; i < 8; i++) {"
9862 " pixels[5] = NaN;"
9863 "}"
9864 "pixels[5];");
9865 CHECK_EQ(0, result->Int32Value());
9866 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
9867
9868 result = CompileRun("for (var i = 0; i < 8; i++) {"
9869 " pixels[8] = Infinity;"
9870 "}"
9871 "pixels[8];");
9872 CHECK_EQ(255, result->Int32Value());
9873 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(8))->value());
9874
9875 result = CompileRun("for (var i = 0; i < 8; i++) {"
9876 " pixels[9] = -Infinity;"
9877 "}"
9878 "pixels[9];");
9879 CHECK_EQ(0, result->Int32Value());
9880 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(9))->value());
9881
9882 result = CompileRun("pixels[3] = 33;"
9883 "delete pixels[3];"
9884 "pixels[3];");
9885 CHECK_EQ(33, result->Int32Value());
9886
9887 result = CompileRun("pixels[0] = 10; pixels[1] = 11;"
9888 "pixels[2] = 12; pixels[3] = 13;"
9889 "pixels.__defineGetter__('2',"
9890 "function() { return 120; });"
9891 "pixels[2];");
9892 CHECK_EQ(12, result->Int32Value());
9893
9894 result = CompileRun("var js_array = new Array(40);"
9895 "js_array[0] = 77;"
9896 "js_array;");
9897 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
9898
9899 result = CompileRun("pixels[1] = 23;"
9900 "pixels.__proto__ = [];"
9901 "js_array.__proto__ = pixels;"
9902 "js_array.concat(pixels);");
9903 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
9904 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value());
9905
9906 result = CompileRun("pixels[1] = 23;");
9907 CHECK_EQ(23, result->Int32Value());
9908
Steve Blockd0582a62009-12-15 09:54:21 +00009909 // Test for index greater than 255. Regression test for:
9910 // http://code.google.com/p/chromium/issues/detail?id=26337.
9911 result = CompileRun("pixels[256] = 255;");
9912 CHECK_EQ(255, result->Int32Value());
9913 result = CompileRun("var i = 0;"
9914 "for (var j = 0; j < 8; j++) { i = pixels[256]; }"
9915 "i");
9916 CHECK_EQ(255, result->Int32Value());
9917
Steve Blocka7e24c12009-10-30 11:49:00 +00009918 free(pixel_data);
9919}
9920
9921
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01009922THREADED_TEST(PixelArrayInfo) {
9923 v8::HandleScope scope;
9924 LocalContext context;
9925 for (int size = 0; size < 100; size += 10) {
9926 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(size));
9927 v8::Handle<v8::Object> obj = v8::Object::New();
9928 obj->SetIndexedPropertiesToPixelData(pixel_data, size);
9929 CHECK(obj->HasIndexedPropertiesInPixelData());
9930 CHECK_EQ(pixel_data, obj->GetIndexedPropertiesPixelData());
9931 CHECK_EQ(size, obj->GetIndexedPropertiesPixelDataLength());
9932 free(pixel_data);
9933 }
9934}
9935
9936
9937static int ExternalArrayElementSize(v8::ExternalArrayType array_type) {
9938 switch (array_type) {
9939 case v8::kExternalByteArray:
9940 case v8::kExternalUnsignedByteArray:
9941 return 1;
9942 break;
9943 case v8::kExternalShortArray:
9944 case v8::kExternalUnsignedShortArray:
9945 return 2;
9946 break;
9947 case v8::kExternalIntArray:
9948 case v8::kExternalUnsignedIntArray:
9949 case v8::kExternalFloatArray:
9950 return 4;
9951 break;
9952 default:
9953 UNREACHABLE();
9954 return -1;
9955 }
9956 UNREACHABLE();
9957 return -1;
9958}
9959
9960
Steve Block3ce2e202009-11-05 08:53:23 +00009961template <class ExternalArrayClass, class ElementType>
9962static void ExternalArrayTestHelper(v8::ExternalArrayType array_type,
9963 int64_t low,
9964 int64_t high) {
9965 v8::HandleScope scope;
9966 LocalContext context;
9967 const int kElementCount = 40;
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01009968 int element_size = ExternalArrayElementSize(array_type);
Steve Block3ce2e202009-11-05 08:53:23 +00009969 ElementType* array_data =
9970 static_cast<ElementType*>(malloc(kElementCount * element_size));
9971 i::Handle<ExternalArrayClass> array =
9972 i::Handle<ExternalArrayClass>::cast(
9973 i::Factory::NewExternalArray(kElementCount, array_type, array_data));
9974 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9975 for (int i = 0; i < kElementCount; i++) {
9976 array->set(i, static_cast<ElementType>(i));
9977 }
9978 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9979 for (int i = 0; i < kElementCount; i++) {
9980 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array->get(i)));
9981 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array_data[i]));
9982 }
9983
9984 v8::Handle<v8::Object> obj = v8::Object::New();
9985 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj);
9986 // Set the elements to be the external array.
9987 obj->SetIndexedPropertiesToExternalArrayData(array_data,
9988 array_type,
9989 kElementCount);
9990 CHECK_EQ(1, static_cast<int>(jsobj->GetElement(1)->Number()));
9991 obj->Set(v8_str("field"), v8::Int32::New(1503));
9992 context->Global()->Set(v8_str("ext_array"), obj);
9993 v8::Handle<v8::Value> result = CompileRun("ext_array.field");
9994 CHECK_EQ(1503, result->Int32Value());
9995 result = CompileRun("ext_array[1]");
9996 CHECK_EQ(1, result->Int32Value());
9997
9998 // Check pass through of assigned smis
9999 result = CompileRun("var sum = 0;"
10000 "for (var i = 0; i < 8; i++) {"
10001 " sum += ext_array[i] = ext_array[i] = -i;"
10002 "}"
10003 "sum;");
10004 CHECK_EQ(-28, result->Int32Value());
10005
10006 // Check assigned smis
10007 result = CompileRun("for (var i = 0; i < 8; i++) {"
10008 " ext_array[i] = i;"
10009 "}"
10010 "var sum = 0;"
10011 "for (var i = 0; i < 8; i++) {"
10012 " sum += ext_array[i];"
10013 "}"
10014 "sum;");
10015 CHECK_EQ(28, result->Int32Value());
10016
10017 // Check assigned smis in reverse order
10018 result = CompileRun("for (var i = 8; --i >= 0; ) {"
10019 " ext_array[i] = i;"
10020 "}"
10021 "var sum = 0;"
10022 "for (var i = 0; i < 8; i++) {"
10023 " sum += ext_array[i];"
10024 "}"
10025 "sum;");
10026 CHECK_EQ(28, result->Int32Value());
10027
10028 // Check pass through of assigned HeapNumbers
10029 result = CompileRun("var sum = 0;"
10030 "for (var i = 0; i < 16; i+=2) {"
10031 " sum += ext_array[i] = ext_array[i] = (-i * 0.5);"
10032 "}"
10033 "sum;");
10034 CHECK_EQ(-28, result->Int32Value());
10035
10036 // Check assigned HeapNumbers
10037 result = CompileRun("for (var i = 0; i < 16; i+=2) {"
10038 " ext_array[i] = (i * 0.5);"
10039 "}"
10040 "var sum = 0;"
10041 "for (var i = 0; i < 16; i+=2) {"
10042 " sum += ext_array[i];"
10043 "}"
10044 "sum;");
10045 CHECK_EQ(28, result->Int32Value());
10046
10047 // Check assigned HeapNumbers in reverse order
10048 result = CompileRun("for (var i = 14; i >= 0; i-=2) {"
10049 " ext_array[i] = (i * 0.5);"
10050 "}"
10051 "var sum = 0;"
10052 "for (var i = 0; i < 16; i+=2) {"
10053 " sum += ext_array[i];"
10054 "}"
10055 "sum;");
10056 CHECK_EQ(28, result->Int32Value());
10057
10058 i::ScopedVector<char> test_buf(1024);
10059
10060 // Check legal boundary conditions.
10061 // The repeated loads and stores ensure the ICs are exercised.
10062 const char* boundary_program =
10063 "var res = 0;"
10064 "for (var i = 0; i < 16; i++) {"
10065 " ext_array[i] = %lld;"
10066 " if (i > 8) {"
10067 " res = ext_array[i];"
10068 " }"
10069 "}"
10070 "res;";
10071 i::OS::SNPrintF(test_buf,
10072 boundary_program,
10073 low);
10074 result = CompileRun(test_buf.start());
10075 CHECK_EQ(low, result->IntegerValue());
10076
10077 i::OS::SNPrintF(test_buf,
10078 boundary_program,
10079 high);
10080 result = CompileRun(test_buf.start());
10081 CHECK_EQ(high, result->IntegerValue());
10082
10083 // Check misprediction of type in IC.
10084 result = CompileRun("var tmp_array = ext_array;"
10085 "var sum = 0;"
10086 "for (var i = 0; i < 8; i++) {"
10087 " tmp_array[i] = i;"
10088 " sum += tmp_array[i];"
10089 " if (i == 4) {"
10090 " tmp_array = {};"
10091 " }"
10092 "}"
10093 "sum;");
10094 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
10095 CHECK_EQ(28, result->Int32Value());
10096
10097 // Make sure out-of-range loads do not throw.
10098 i::OS::SNPrintF(test_buf,
10099 "var caught_exception = false;"
10100 "try {"
10101 " ext_array[%d];"
10102 "} catch (e) {"
10103 " caught_exception = true;"
10104 "}"
10105 "caught_exception;",
10106 kElementCount);
10107 result = CompileRun(test_buf.start());
10108 CHECK_EQ(false, result->BooleanValue());
10109
10110 // Make sure out-of-range stores do not throw.
10111 i::OS::SNPrintF(test_buf,
10112 "var caught_exception = false;"
10113 "try {"
10114 " ext_array[%d] = 1;"
10115 "} catch (e) {"
10116 " caught_exception = true;"
10117 "}"
10118 "caught_exception;",
10119 kElementCount);
10120 result = CompileRun(test_buf.start());
10121 CHECK_EQ(false, result->BooleanValue());
10122
10123 // Check other boundary conditions, values and operations.
10124 result = CompileRun("for (var i = 0; i < 8; i++) {"
10125 " ext_array[7] = undefined;"
10126 "}"
10127 "ext_array[7];");
10128 CHECK_EQ(0, result->Int32Value());
10129 CHECK_EQ(0, static_cast<int>(jsobj->GetElement(7)->Number()));
10130
10131 result = CompileRun("for (var i = 0; i < 8; i++) {"
10132 " ext_array[6] = '2.3';"
10133 "}"
10134 "ext_array[6];");
10135 CHECK_EQ(2, result->Int32Value());
10136 CHECK_EQ(2, static_cast<int>(jsobj->GetElement(6)->Number()));
10137
10138 if (array_type != v8::kExternalFloatArray) {
10139 // Though the specification doesn't state it, be explicit about
10140 // converting NaNs and +/-Infinity to zero.
10141 result = CompileRun("for (var i = 0; i < 8; i++) {"
10142 " ext_array[i] = 5;"
10143 "}"
10144 "for (var i = 0; i < 8; i++) {"
10145 " ext_array[i] = NaN;"
10146 "}"
10147 "ext_array[5];");
10148 CHECK_EQ(0, result->Int32Value());
10149 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
10150
10151 result = CompileRun("for (var i = 0; i < 8; i++) {"
10152 " ext_array[i] = 5;"
10153 "}"
10154 "for (var i = 0; i < 8; i++) {"
10155 " ext_array[i] = Infinity;"
10156 "}"
10157 "ext_array[5];");
10158 CHECK_EQ(0, result->Int32Value());
10159 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
10160
10161 result = CompileRun("for (var i = 0; i < 8; i++) {"
10162 " ext_array[i] = 5;"
10163 "}"
10164 "for (var i = 0; i < 8; i++) {"
10165 " ext_array[i] = -Infinity;"
10166 "}"
10167 "ext_array[5];");
10168 CHECK_EQ(0, result->Int32Value());
10169 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
10170 }
10171
10172 result = CompileRun("ext_array[3] = 33;"
10173 "delete ext_array[3];"
10174 "ext_array[3];");
10175 CHECK_EQ(33, result->Int32Value());
10176
10177 result = CompileRun("ext_array[0] = 10; ext_array[1] = 11;"
10178 "ext_array[2] = 12; ext_array[3] = 13;"
10179 "ext_array.__defineGetter__('2',"
10180 "function() { return 120; });"
10181 "ext_array[2];");
10182 CHECK_EQ(12, result->Int32Value());
10183
10184 result = CompileRun("var js_array = new Array(40);"
10185 "js_array[0] = 77;"
10186 "js_array;");
10187 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
10188
10189 result = CompileRun("ext_array[1] = 23;"
10190 "ext_array.__proto__ = [];"
10191 "js_array.__proto__ = ext_array;"
10192 "js_array.concat(ext_array);");
10193 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
10194 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value());
10195
10196 result = CompileRun("ext_array[1] = 23;");
10197 CHECK_EQ(23, result->Int32Value());
10198
Steve Blockd0582a62009-12-15 09:54:21 +000010199 // Test more complex manipulations which cause eax to contain values
10200 // that won't be completely overwritten by loads from the arrays.
10201 // This catches bugs in the instructions used for the KeyedLoadIC
10202 // for byte and word types.
10203 {
10204 const int kXSize = 300;
10205 const int kYSize = 300;
10206 const int kLargeElementCount = kXSize * kYSize * 4;
10207 ElementType* large_array_data =
10208 static_cast<ElementType*>(malloc(kLargeElementCount * element_size));
10209 i::Handle<ExternalArrayClass> large_array =
10210 i::Handle<ExternalArrayClass>::cast(
10211 i::Factory::NewExternalArray(kLargeElementCount,
10212 array_type,
10213 array_data));
10214 v8::Handle<v8::Object> large_obj = v8::Object::New();
10215 // Set the elements to be the external array.
10216 large_obj->SetIndexedPropertiesToExternalArrayData(large_array_data,
10217 array_type,
10218 kLargeElementCount);
10219 context->Global()->Set(v8_str("large_array"), large_obj);
10220 // Initialize contents of a few rows.
10221 for (int x = 0; x < 300; x++) {
10222 int row = 0;
10223 int offset = row * 300 * 4;
10224 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
10225 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
10226 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
10227 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
10228 row = 150;
10229 offset = row * 300 * 4;
10230 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
10231 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
10232 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
10233 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
10234 row = 298;
10235 offset = row * 300 * 4;
10236 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
10237 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
10238 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
10239 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
10240 }
10241 // The goal of the code below is to make "offset" large enough
10242 // that the computation of the index (which goes into eax) has
10243 // high bits set which will not be overwritten by a byte or short
10244 // load.
10245 result = CompileRun("var failed = false;"
10246 "var offset = 0;"
10247 "for (var i = 0; i < 300; i++) {"
10248 " if (large_array[4 * i] != 127 ||"
10249 " large_array[4 * i + 1] != 0 ||"
10250 " large_array[4 * i + 2] != 0 ||"
10251 " large_array[4 * i + 3] != 127) {"
10252 " failed = true;"
10253 " }"
10254 "}"
10255 "offset = 150 * 300 * 4;"
10256 "for (var i = 0; i < 300; i++) {"
10257 " if (large_array[offset + 4 * i] != 127 ||"
10258 " large_array[offset + 4 * i + 1] != 0 ||"
10259 " large_array[offset + 4 * i + 2] != 0 ||"
10260 " large_array[offset + 4 * i + 3] != 127) {"
10261 " failed = true;"
10262 " }"
10263 "}"
10264 "offset = 298 * 300 * 4;"
10265 "for (var i = 0; i < 300; i++) {"
10266 " if (large_array[offset + 4 * i] != 127 ||"
10267 " large_array[offset + 4 * i + 1] != 0 ||"
10268 " large_array[offset + 4 * i + 2] != 0 ||"
10269 " large_array[offset + 4 * i + 3] != 127) {"
10270 " failed = true;"
10271 " }"
10272 "}"
10273 "!failed;");
10274 CHECK_EQ(true, result->BooleanValue());
10275 free(large_array_data);
10276 }
10277
Steve Block3ce2e202009-11-05 08:53:23 +000010278 free(array_data);
10279}
10280
10281
10282THREADED_TEST(ExternalByteArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010283 ExternalArrayTestHelper<i::ExternalByteArray, int8_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000010284 v8::kExternalByteArray,
10285 -128,
10286 127);
10287}
10288
10289
10290THREADED_TEST(ExternalUnsignedByteArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010291 ExternalArrayTestHelper<i::ExternalUnsignedByteArray, uint8_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000010292 v8::kExternalUnsignedByteArray,
10293 0,
10294 255);
10295}
10296
10297
10298THREADED_TEST(ExternalShortArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010299 ExternalArrayTestHelper<i::ExternalShortArray, int16_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000010300 v8::kExternalShortArray,
10301 -32768,
10302 32767);
10303}
10304
10305
10306THREADED_TEST(ExternalUnsignedShortArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010307 ExternalArrayTestHelper<i::ExternalUnsignedShortArray, uint16_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000010308 v8::kExternalUnsignedShortArray,
10309 0,
10310 65535);
10311}
10312
10313
10314THREADED_TEST(ExternalIntArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010315 ExternalArrayTestHelper<i::ExternalIntArray, int32_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000010316 v8::kExternalIntArray,
10317 INT_MIN, // -2147483648
10318 INT_MAX); // 2147483647
10319}
10320
10321
10322THREADED_TEST(ExternalUnsignedIntArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010323 ExternalArrayTestHelper<i::ExternalUnsignedIntArray, uint32_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000010324 v8::kExternalUnsignedIntArray,
10325 0,
10326 UINT_MAX); // 4294967295
10327}
10328
10329
10330THREADED_TEST(ExternalFloatArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010331 ExternalArrayTestHelper<i::ExternalFloatArray, float>(
Steve Block3ce2e202009-11-05 08:53:23 +000010332 v8::kExternalFloatArray,
10333 -500,
10334 500);
10335}
10336
10337
10338THREADED_TEST(ExternalArrays) {
10339 TestExternalByteArray();
10340 TestExternalUnsignedByteArray();
10341 TestExternalShortArray();
10342 TestExternalUnsignedShortArray();
10343 TestExternalIntArray();
10344 TestExternalUnsignedIntArray();
10345 TestExternalFloatArray();
10346}
10347
10348
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010010349void ExternalArrayInfoTestHelper(v8::ExternalArrayType array_type) {
10350 v8::HandleScope scope;
10351 LocalContext context;
10352 for (int size = 0; size < 100; size += 10) {
10353 int element_size = ExternalArrayElementSize(array_type);
10354 void* external_data = malloc(size * element_size);
10355 v8::Handle<v8::Object> obj = v8::Object::New();
10356 obj->SetIndexedPropertiesToExternalArrayData(
10357 external_data, array_type, size);
10358 CHECK(obj->HasIndexedPropertiesInExternalArrayData());
10359 CHECK_EQ(external_data, obj->GetIndexedPropertiesExternalArrayData());
10360 CHECK_EQ(array_type, obj->GetIndexedPropertiesExternalArrayDataType());
10361 CHECK_EQ(size, obj->GetIndexedPropertiesExternalArrayDataLength());
10362 free(external_data);
10363 }
10364}
10365
10366
10367THREADED_TEST(ExternalArrayInfo) {
10368 ExternalArrayInfoTestHelper(v8::kExternalByteArray);
10369 ExternalArrayInfoTestHelper(v8::kExternalUnsignedByteArray);
10370 ExternalArrayInfoTestHelper(v8::kExternalShortArray);
10371 ExternalArrayInfoTestHelper(v8::kExternalUnsignedShortArray);
10372 ExternalArrayInfoTestHelper(v8::kExternalIntArray);
10373 ExternalArrayInfoTestHelper(v8::kExternalUnsignedIntArray);
10374 ExternalArrayInfoTestHelper(v8::kExternalFloatArray);
10375}
10376
10377
Steve Blocka7e24c12009-10-30 11:49:00 +000010378THREADED_TEST(ScriptContextDependence) {
10379 v8::HandleScope scope;
10380 LocalContext c1;
10381 const char *source = "foo";
10382 v8::Handle<v8::Script> dep = v8::Script::Compile(v8::String::New(source));
10383 v8::Handle<v8::Script> indep = v8::Script::New(v8::String::New(source));
10384 c1->Global()->Set(v8::String::New("foo"), v8::Integer::New(100));
10385 CHECK_EQ(dep->Run()->Int32Value(), 100);
10386 CHECK_EQ(indep->Run()->Int32Value(), 100);
10387 LocalContext c2;
10388 c2->Global()->Set(v8::String::New("foo"), v8::Integer::New(101));
10389 CHECK_EQ(dep->Run()->Int32Value(), 100);
10390 CHECK_EQ(indep->Run()->Int32Value(), 101);
10391}
10392
10393
10394THREADED_TEST(StackTrace) {
10395 v8::HandleScope scope;
10396 LocalContext context;
10397 v8::TryCatch try_catch;
10398 const char *source = "function foo() { FAIL.FAIL; }; foo();";
10399 v8::Handle<v8::String> src = v8::String::New(source);
10400 v8::Handle<v8::String> origin = v8::String::New("stack-trace-test");
10401 v8::Script::New(src, origin)->Run();
10402 CHECK(try_catch.HasCaught());
10403 v8::String::Utf8Value stack(try_catch.StackTrace());
10404 CHECK(strstr(*stack, "at foo (stack-trace-test") != NULL);
10405}
10406
10407
Kristian Monsen25f61362010-05-21 11:50:48 +010010408// Checks that a StackFrame has certain expected values.
10409void checkStackFrame(const char* expected_script_name,
10410 const char* expected_func_name, int expected_line_number,
10411 int expected_column, bool is_eval, bool is_constructor,
10412 v8::Handle<v8::StackFrame> frame) {
10413 v8::HandleScope scope;
10414 v8::String::Utf8Value func_name(frame->GetFunctionName());
10415 v8::String::Utf8Value script_name(frame->GetScriptName());
10416 if (*script_name == NULL) {
10417 // The situation where there is no associated script, like for evals.
10418 CHECK(expected_script_name == NULL);
10419 } else {
10420 CHECK(strstr(*script_name, expected_script_name) != NULL);
10421 }
10422 CHECK(strstr(*func_name, expected_func_name) != NULL);
10423 CHECK_EQ(expected_line_number, frame->GetLineNumber());
10424 CHECK_EQ(expected_column, frame->GetColumn());
10425 CHECK_EQ(is_eval, frame->IsEval());
10426 CHECK_EQ(is_constructor, frame->IsConstructor());
10427}
10428
10429
10430v8::Handle<Value> AnalyzeStackInNativeCode(const v8::Arguments& args) {
10431 v8::HandleScope scope;
10432 const char* origin = "capture-stack-trace-test";
10433 const int kOverviewTest = 1;
10434 const int kDetailedTest = 2;
10435
10436 ASSERT(args.Length() == 1);
10437
10438 int testGroup = args[0]->Int32Value();
10439 if (testGroup == kOverviewTest) {
10440 v8::Handle<v8::StackTrace> stackTrace =
10441 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kOverview);
10442 CHECK_EQ(4, stackTrace->GetFrameCount());
10443 checkStackFrame(origin, "bar", 2, 10, false, false,
10444 stackTrace->GetFrame(0));
10445 checkStackFrame(origin, "foo", 6, 3, false, false,
10446 stackTrace->GetFrame(1));
10447 checkStackFrame(NULL, "", 1, 1, false, false,
10448 stackTrace->GetFrame(2));
10449 // The last frame is an anonymous function that has the initial call.
10450 checkStackFrame(origin, "", 8, 7, false, false,
10451 stackTrace->GetFrame(3));
10452
10453 CHECK(stackTrace->AsArray()->IsArray());
10454 } else if (testGroup == kDetailedTest) {
10455 v8::Handle<v8::StackTrace> stackTrace =
10456 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kDetailed);
10457 CHECK_EQ(4, stackTrace->GetFrameCount());
10458 checkStackFrame(origin, "bat", 4, 22, false, false,
10459 stackTrace->GetFrame(0));
10460 checkStackFrame(origin, "baz", 8, 3, false, true,
10461 stackTrace->GetFrame(1));
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010010462#ifdef ENABLE_DEBUGGER_SUPPORT
10463 bool is_eval = true;
10464#else // ENABLE_DEBUGGER_SUPPORT
10465 bool is_eval = false;
10466#endif // ENABLE_DEBUGGER_SUPPORT
10467
10468 checkStackFrame(NULL, "", 1, 1, is_eval, false,
Kristian Monsen25f61362010-05-21 11:50:48 +010010469 stackTrace->GetFrame(2));
10470 // The last frame is an anonymous function that has the initial call to foo.
10471 checkStackFrame(origin, "", 10, 1, false, false,
10472 stackTrace->GetFrame(3));
10473
10474 CHECK(stackTrace->AsArray()->IsArray());
10475 }
10476 return v8::Undefined();
10477}
10478
10479
10480// Tests the C++ StackTrace API.
10481THREADED_TEST(CaptureStackTrace) {
10482 v8::HandleScope scope;
10483 v8::Handle<v8::String> origin = v8::String::New("capture-stack-trace-test");
10484 Local<ObjectTemplate> templ = ObjectTemplate::New();
10485 templ->Set(v8_str("AnalyzeStackInNativeCode"),
10486 v8::FunctionTemplate::New(AnalyzeStackInNativeCode));
10487 LocalContext context(0, templ);
10488
10489 // Test getting OVERVIEW information. Should ignore information that is not
10490 // script name, function name, line number, and column offset.
10491 const char *overview_source =
10492 "function bar() {\n"
10493 " var y; AnalyzeStackInNativeCode(1);\n"
10494 "}\n"
10495 "function foo() {\n"
10496 "\n"
10497 " bar();\n"
10498 "}\n"
10499 "var x;eval('new foo();');";
10500 v8::Handle<v8::String> overview_src = v8::String::New(overview_source);
10501 v8::Handle<Value> overview_result =
10502 v8::Script::New(overview_src, origin)->Run();
10503 ASSERT(!overview_result.IsEmpty());
10504 ASSERT(overview_result->IsObject());
10505
10506 // Test getting DETAILED information.
10507 const char *detailed_source =
10508 "function bat() {AnalyzeStackInNativeCode(2);\n"
10509 "}\n"
10510 "\n"
10511 "function baz() {\n"
10512 " bat();\n"
10513 "}\n"
10514 "eval('new baz();');";
10515 v8::Handle<v8::String> detailed_src = v8::String::New(detailed_source);
10516 // Make the script using a non-zero line and column offset.
10517 v8::Handle<v8::Integer> line_offset = v8::Integer::New(3);
10518 v8::Handle<v8::Integer> column_offset = v8::Integer::New(5);
10519 v8::ScriptOrigin detailed_origin(origin, line_offset, column_offset);
10520 v8::Handle<v8::Script> detailed_script(
10521 v8::Script::New(detailed_src, &detailed_origin));
10522 v8::Handle<Value> detailed_result = detailed_script->Run();
10523 ASSERT(!detailed_result.IsEmpty());
10524 ASSERT(detailed_result->IsObject());
10525}
10526
10527
Ben Murdoch3bec4d22010-07-22 14:51:16 +010010528static void StackTraceForUncaughtExceptionListener(
10529 v8::Handle<v8::Message> message,
10530 v8::Handle<Value>) {
10531 v8::Handle<v8::StackTrace> stack_trace = message->GetStackTrace();
10532 CHECK_EQ(2, stack_trace->GetFrameCount());
10533 checkStackFrame("origin", "foo", 2, 3, false, false,
10534 stack_trace->GetFrame(0));
10535 checkStackFrame("origin", "bar", 5, 3, false, false,
10536 stack_trace->GetFrame(1));
10537}
10538
10539TEST(CaptureStackTraceForUncaughtException) {
10540 report_count = 0;
10541 v8::HandleScope scope;
10542 LocalContext env;
10543 v8::V8::AddMessageListener(StackTraceForUncaughtExceptionListener);
10544 v8::V8::SetCaptureStackTraceForUncaughtExceptions(true);
10545
10546 Script::Compile(v8_str("function foo() {\n"
10547 " throw 1;\n"
10548 "};\n"
10549 "function bar() {\n"
10550 " foo();\n"
10551 "};"),
10552 v8_str("origin"))->Run();
10553 v8::Local<v8::Object> global = env->Global();
10554 Local<Value> trouble = global->Get(v8_str("bar"));
10555 CHECK(trouble->IsFunction());
10556 Function::Cast(*trouble)->Call(global, 0, NULL);
10557 v8::V8::SetCaptureStackTraceForUncaughtExceptions(false);
10558 v8::V8::RemoveMessageListeners(StackTraceForUncaughtExceptionListener);
10559}
10560
10561
Ben Murdochf87a2032010-10-22 12:50:53 +010010562v8::Handle<Value> AnalyzeStackOfEvalWithSourceURL(const v8::Arguments& args) {
10563 v8::HandleScope scope;
10564 v8::Handle<v8::StackTrace> stackTrace =
10565 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kDetailed);
10566 CHECK_EQ(5, stackTrace->GetFrameCount());
10567 v8::Handle<v8::String> url = v8_str("eval_url");
10568 for (int i = 0; i < 3; i++) {
10569 v8::Handle<v8::String> name =
10570 stackTrace->GetFrame(i)->GetScriptNameOrSourceURL();
10571 CHECK(!name.IsEmpty());
10572 CHECK_EQ(url, name);
10573 }
10574 return v8::Undefined();
10575}
10576
10577
10578TEST(SourceURLInStackTrace) {
10579 v8::HandleScope scope;
10580 Local<ObjectTemplate> templ = ObjectTemplate::New();
10581 templ->Set(v8_str("AnalyzeStackOfEvalWithSourceURL"),
10582 v8::FunctionTemplate::New(AnalyzeStackOfEvalWithSourceURL));
10583 LocalContext context(0, templ);
10584
10585 const char *source =
10586 "function outer() {\n"
10587 "function bar() {\n"
10588 " AnalyzeStackOfEvalWithSourceURL();\n"
10589 "}\n"
10590 "function foo() {\n"
10591 "\n"
10592 " bar();\n"
10593 "}\n"
10594 "foo();\n"
10595 "}\n"
10596 "eval('(' + outer +')()//@ sourceURL=eval_url');";
10597 CHECK(CompileRun(source)->IsUndefined());
10598}
10599
10600
Steve Block3ce2e202009-11-05 08:53:23 +000010601// Test that idle notification can be handled and eventually returns true.
Steve Blocka7e24c12009-10-30 11:49:00 +000010602THREADED_TEST(IdleNotification) {
Steve Block3ce2e202009-11-05 08:53:23 +000010603 bool rv = false;
10604 for (int i = 0; i < 100; i++) {
10605 rv = v8::V8::IdleNotification();
10606 if (rv)
10607 break;
10608 }
10609 CHECK(rv == true);
Steve Blocka7e24c12009-10-30 11:49:00 +000010610}
10611
10612
10613static uint32_t* stack_limit;
10614
10615static v8::Handle<Value> GetStackLimitCallback(const v8::Arguments& args) {
10616 stack_limit = reinterpret_cast<uint32_t*>(i::StackGuard::climit());
10617 return v8::Undefined();
10618}
10619
10620
10621// Uses the address of a local variable to determine the stack top now.
10622// Given a size, returns an address that is that far from the current
10623// top of stack.
10624static uint32_t* ComputeStackLimit(uint32_t size) {
10625 uint32_t* answer = &size - (size / sizeof(size));
10626 // If the size is very large and the stack is very near the bottom of
10627 // memory then the calculation above may wrap around and give an address
10628 // that is above the (downwards-growing) stack. In that case we return
10629 // a very low address.
10630 if (answer > &size) return reinterpret_cast<uint32_t*>(sizeof(size));
10631 return answer;
10632}
10633
10634
10635TEST(SetResourceConstraints) {
10636 static const int K = 1024;
10637 uint32_t* set_limit = ComputeStackLimit(128 * K);
10638
10639 // Set stack limit.
10640 v8::ResourceConstraints constraints;
10641 constraints.set_stack_limit(set_limit);
10642 CHECK(v8::SetResourceConstraints(&constraints));
10643
10644 // Execute a script.
10645 v8::HandleScope scope;
10646 LocalContext env;
10647 Local<v8::FunctionTemplate> fun_templ =
10648 v8::FunctionTemplate::New(GetStackLimitCallback);
10649 Local<Function> fun = fun_templ->GetFunction();
10650 env->Global()->Set(v8_str("get_stack_limit"), fun);
10651 CompileRun("get_stack_limit();");
10652
10653 CHECK(stack_limit == set_limit);
10654}
10655
10656
10657TEST(SetResourceConstraintsInThread) {
10658 uint32_t* set_limit;
10659 {
10660 v8::Locker locker;
10661 static const int K = 1024;
10662 set_limit = ComputeStackLimit(128 * K);
10663
10664 // Set stack limit.
10665 v8::ResourceConstraints constraints;
10666 constraints.set_stack_limit(set_limit);
10667 CHECK(v8::SetResourceConstraints(&constraints));
10668
10669 // Execute a script.
10670 v8::HandleScope scope;
10671 LocalContext env;
10672 Local<v8::FunctionTemplate> fun_templ =
10673 v8::FunctionTemplate::New(GetStackLimitCallback);
10674 Local<Function> fun = fun_templ->GetFunction();
10675 env->Global()->Set(v8_str("get_stack_limit"), fun);
10676 CompileRun("get_stack_limit();");
10677
10678 CHECK(stack_limit == set_limit);
10679 }
10680 {
10681 v8::Locker locker;
10682 CHECK(stack_limit == set_limit);
10683 }
10684}
Steve Block3ce2e202009-11-05 08:53:23 +000010685
10686
10687THREADED_TEST(GetHeapStatistics) {
10688 v8::HandleScope scope;
10689 LocalContext c1;
10690 v8::HeapStatistics heap_statistics;
Steve Blockd0582a62009-12-15 09:54:21 +000010691 CHECK_EQ(static_cast<int>(heap_statistics.total_heap_size()), 0);
10692 CHECK_EQ(static_cast<int>(heap_statistics.used_heap_size()), 0);
Steve Block3ce2e202009-11-05 08:53:23 +000010693 v8::V8::GetHeapStatistics(&heap_statistics);
Steve Blockd0582a62009-12-15 09:54:21 +000010694 CHECK_NE(static_cast<int>(heap_statistics.total_heap_size()), 0);
10695 CHECK_NE(static_cast<int>(heap_statistics.used_heap_size()), 0);
10696}
10697
10698
10699static double DoubleFromBits(uint64_t value) {
10700 double target;
10701#ifdef BIG_ENDIAN_FLOATING_POINT
10702 const int kIntSize = 4;
10703 // Somebody swapped the lower and higher half of doubles.
10704 memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize);
10705 memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize);
10706#else
10707 memcpy(&target, &value, sizeof(target));
10708#endif
10709 return target;
10710}
10711
10712
10713static uint64_t DoubleToBits(double value) {
10714 uint64_t target;
10715#ifdef BIG_ENDIAN_FLOATING_POINT
10716 const int kIntSize = 4;
10717 // Somebody swapped the lower and higher half of doubles.
10718 memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize);
10719 memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize);
10720#else
10721 memcpy(&target, &value, sizeof(target));
10722#endif
10723 return target;
10724}
10725
10726
10727static double DoubleToDateTime(double input) {
10728 double date_limit = 864e13;
10729 if (IsNaN(input) || input < -date_limit || input > date_limit) {
10730 return i::OS::nan_value();
10731 }
10732 return (input < 0) ? -(floor(-input)) : floor(input);
10733}
10734
10735// We don't have a consistent way to write 64-bit constants syntactically, so we
10736// split them into two 32-bit constants and combine them programmatically.
10737static double DoubleFromBits(uint32_t high_bits, uint32_t low_bits) {
10738 return DoubleFromBits((static_cast<uint64_t>(high_bits) << 32) | low_bits);
10739}
10740
10741
10742THREADED_TEST(QuietSignalingNaNs) {
10743 v8::HandleScope scope;
10744 LocalContext context;
10745 v8::TryCatch try_catch;
10746
10747 // Special double values.
10748 double snan = DoubleFromBits(0x7ff00000, 0x00000001);
10749 double qnan = DoubleFromBits(0x7ff80000, 0x00000000);
10750 double infinity = DoubleFromBits(0x7ff00000, 0x00000000);
10751 double max_normal = DoubleFromBits(0x7fefffff, 0xffffffffu);
10752 double min_normal = DoubleFromBits(0x00100000, 0x00000000);
10753 double max_denormal = DoubleFromBits(0x000fffff, 0xffffffffu);
10754 double min_denormal = DoubleFromBits(0x00000000, 0x00000001);
10755
10756 // Date values are capped at +/-100000000 days (times 864e5 ms per day)
10757 // on either side of the epoch.
10758 double date_limit = 864e13;
10759
10760 double test_values[] = {
10761 snan,
10762 qnan,
10763 infinity,
10764 max_normal,
10765 date_limit + 1,
10766 date_limit,
10767 min_normal,
10768 max_denormal,
10769 min_denormal,
10770 0,
10771 -0,
10772 -min_denormal,
10773 -max_denormal,
10774 -min_normal,
10775 -date_limit,
10776 -date_limit - 1,
10777 -max_normal,
10778 -infinity,
10779 -qnan,
10780 -snan
10781 };
10782 int num_test_values = 20;
10783
10784 for (int i = 0; i < num_test_values; i++) {
10785 double test_value = test_values[i];
10786
10787 // Check that Number::New preserves non-NaNs and quiets SNaNs.
10788 v8::Handle<v8::Value> number = v8::Number::New(test_value);
10789 double stored_number = number->NumberValue();
10790 if (!IsNaN(test_value)) {
10791 CHECK_EQ(test_value, stored_number);
10792 } else {
10793 uint64_t stored_bits = DoubleToBits(stored_number);
10794 // Check if quiet nan (bits 51..62 all set).
10795 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff));
10796 }
10797
10798 // Check that Date::New preserves non-NaNs in the date range and
10799 // quiets SNaNs.
10800 v8::Handle<v8::Value> date = v8::Date::New(test_value);
10801 double expected_stored_date = DoubleToDateTime(test_value);
10802 double stored_date = date->NumberValue();
10803 if (!IsNaN(expected_stored_date)) {
10804 CHECK_EQ(expected_stored_date, stored_date);
10805 } else {
10806 uint64_t stored_bits = DoubleToBits(stored_date);
10807 // Check if quiet nan (bits 51..62 all set).
10808 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff));
10809 }
10810 }
10811}
10812
10813
10814static v8::Handle<Value> SpaghettiIncident(const v8::Arguments& args) {
10815 v8::HandleScope scope;
10816 v8::TryCatch tc;
10817 v8::Handle<v8::String> str = args[0]->ToString();
10818 if (tc.HasCaught())
10819 return tc.ReThrow();
10820 return v8::Undefined();
10821}
10822
10823
10824// Test that an exception can be propagated down through a spaghetti
10825// stack using ReThrow.
10826THREADED_TEST(SpaghettiStackReThrow) {
10827 v8::HandleScope scope;
10828 LocalContext context;
10829 context->Global()->Set(
10830 v8::String::New("s"),
10831 v8::FunctionTemplate::New(SpaghettiIncident)->GetFunction());
10832 v8::TryCatch try_catch;
10833 CompileRun(
10834 "var i = 0;"
10835 "var o = {"
10836 " toString: function () {"
10837 " if (i == 10) {"
10838 " throw 'Hey!';"
10839 " } else {"
10840 " i++;"
10841 " return s(o);"
10842 " }"
10843 " }"
10844 "};"
10845 "s(o);");
10846 CHECK(try_catch.HasCaught());
10847 v8::String::Utf8Value value(try_catch.Exception());
10848 CHECK_EQ(0, strcmp(*value, "Hey!"));
10849}
10850
10851
Steve Blockd0582a62009-12-15 09:54:21 +000010852TEST(Regress528) {
10853 v8::V8::Initialize();
10854
10855 v8::HandleScope scope;
10856 v8::Persistent<Context> context;
10857 v8::Persistent<Context> other_context;
10858 int gc_count;
10859
10860 // Create a context used to keep the code from aging in the compilation
10861 // cache.
10862 other_context = Context::New();
10863
10864 // Context-dependent context data creates reference from the compilation
10865 // cache to the global object.
10866 const char* source_simple = "1";
10867 context = Context::New();
10868 {
10869 v8::HandleScope scope;
10870
10871 context->Enter();
10872 Local<v8::String> obj = v8::String::New("");
10873 context->SetData(obj);
10874 CompileRun(source_simple);
10875 context->Exit();
10876 }
10877 context.Dispose();
10878 for (gc_count = 1; gc_count < 10; gc_count++) {
10879 other_context->Enter();
10880 CompileRun(source_simple);
10881 other_context->Exit();
Steve Block8defd9f2010-07-08 12:39:36 +010010882 i::Heap::CollectAllGarbage(false);
Steve Blockd0582a62009-12-15 09:54:21 +000010883 if (GetGlobalObjectsCount() == 1) break;
10884 }
10885 CHECK_GE(2, gc_count);
10886 CHECK_EQ(1, GetGlobalObjectsCount());
10887
10888 // Eval in a function creates reference from the compilation cache to the
10889 // global object.
10890 const char* source_eval = "function f(){eval('1')}; f()";
10891 context = Context::New();
10892 {
10893 v8::HandleScope scope;
10894
10895 context->Enter();
10896 CompileRun(source_eval);
10897 context->Exit();
10898 }
10899 context.Dispose();
10900 for (gc_count = 1; gc_count < 10; gc_count++) {
10901 other_context->Enter();
10902 CompileRun(source_eval);
10903 other_context->Exit();
Steve Block8defd9f2010-07-08 12:39:36 +010010904 i::Heap::CollectAllGarbage(false);
Steve Blockd0582a62009-12-15 09:54:21 +000010905 if (GetGlobalObjectsCount() == 1) break;
10906 }
10907 CHECK_GE(2, gc_count);
10908 CHECK_EQ(1, GetGlobalObjectsCount());
10909
10910 // Looking up the line number for an exception creates reference from the
10911 // compilation cache to the global object.
10912 const char* source_exception = "function f(){throw 1;} f()";
10913 context = Context::New();
10914 {
10915 v8::HandleScope scope;
10916
10917 context->Enter();
10918 v8::TryCatch try_catch;
10919 CompileRun(source_exception);
10920 CHECK(try_catch.HasCaught());
10921 v8::Handle<v8::Message> message = try_catch.Message();
10922 CHECK(!message.IsEmpty());
10923 CHECK_EQ(1, message->GetLineNumber());
10924 context->Exit();
10925 }
10926 context.Dispose();
10927 for (gc_count = 1; gc_count < 10; gc_count++) {
10928 other_context->Enter();
10929 CompileRun(source_exception);
10930 other_context->Exit();
Steve Block8defd9f2010-07-08 12:39:36 +010010931 i::Heap::CollectAllGarbage(false);
Steve Blockd0582a62009-12-15 09:54:21 +000010932 if (GetGlobalObjectsCount() == 1) break;
10933 }
10934 CHECK_GE(2, gc_count);
10935 CHECK_EQ(1, GetGlobalObjectsCount());
10936
10937 other_context.Dispose();
Steve Block3ce2e202009-11-05 08:53:23 +000010938}
Andrei Popescu402d9372010-02-26 13:31:12 +000010939
10940
10941THREADED_TEST(ScriptOrigin) {
10942 v8::HandleScope scope;
10943 LocalContext env;
10944 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test"));
10945 v8::Handle<v8::String> script = v8::String::New(
10946 "function f() {}\n\nfunction g() {}");
10947 v8::Script::Compile(script, &origin)->Run();
10948 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(
10949 env->Global()->Get(v8::String::New("f")));
10950 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast(
10951 env->Global()->Get(v8::String::New("g")));
10952
10953 v8::ScriptOrigin script_origin_f = f->GetScriptOrigin();
10954 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_f.ResourceName()));
10955 CHECK_EQ(0, script_origin_f.ResourceLineOffset()->Int32Value());
10956
10957 v8::ScriptOrigin script_origin_g = g->GetScriptOrigin();
10958 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_g.ResourceName()));
10959 CHECK_EQ(0, script_origin_g.ResourceLineOffset()->Int32Value());
10960}
10961
10962
10963THREADED_TEST(ScriptLineNumber) {
10964 v8::HandleScope scope;
10965 LocalContext env;
10966 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test"));
10967 v8::Handle<v8::String> script = v8::String::New(
10968 "function f() {}\n\nfunction g() {}");
10969 v8::Script::Compile(script, &origin)->Run();
10970 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(
10971 env->Global()->Get(v8::String::New("f")));
10972 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast(
10973 env->Global()->Get(v8::String::New("g")));
10974 CHECK_EQ(0, f->GetScriptLineNumber());
10975 CHECK_EQ(2, g->GetScriptLineNumber());
10976}
10977
10978
10979static v8::Handle<Value> GetterWhichReturns42(Local<String> name,
10980 const AccessorInfo& info) {
10981 return v8_num(42);
10982}
10983
10984
10985static void SetterWhichSetsYOnThisTo23(Local<String> name,
10986 Local<Value> value,
10987 const AccessorInfo& info) {
10988 info.This()->Set(v8_str("y"), v8_num(23));
10989}
10990
10991
Steve Block6ded16b2010-05-10 14:33:55 +010010992TEST(SetterOnConstructorPrototype) {
Andrei Popescu402d9372010-02-26 13:31:12 +000010993 v8::HandleScope scope;
10994 Local<ObjectTemplate> templ = ObjectTemplate::New();
10995 templ->SetAccessor(v8_str("x"),
10996 GetterWhichReturns42,
10997 SetterWhichSetsYOnThisTo23);
10998 LocalContext context;
10999 context->Global()->Set(v8_str("P"), templ->NewInstance());
11000 CompileRun("function C1() {"
11001 " this.x = 23;"
11002 "};"
11003 "C1.prototype = P;"
11004 "function C2() {"
11005 " this.x = 23"
11006 "};"
11007 "C2.prototype = { };"
11008 "C2.prototype.__proto__ = P;");
11009
11010 v8::Local<v8::Script> script;
11011 script = v8::Script::Compile(v8_str("new C1();"));
11012 for (int i = 0; i < 10; i++) {
11013 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
11014 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value());
11015 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value());
11016 }
11017
11018 script = v8::Script::Compile(v8_str("new C2();"));
11019 for (int i = 0; i < 10; i++) {
11020 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run());
11021 CHECK_EQ(42, c2->Get(v8_str("x"))->Int32Value());
11022 CHECK_EQ(23, c2->Get(v8_str("y"))->Int32Value());
11023 }
11024}
11025
11026
11027static v8::Handle<Value> NamedPropertyGetterWhichReturns42(
11028 Local<String> name, const AccessorInfo& info) {
11029 return v8_num(42);
11030}
11031
11032
11033static v8::Handle<Value> NamedPropertySetterWhichSetsYOnThisTo23(
11034 Local<String> name, Local<Value> value, const AccessorInfo& info) {
11035 if (name->Equals(v8_str("x"))) {
11036 info.This()->Set(v8_str("y"), v8_num(23));
11037 }
11038 return v8::Handle<Value>();
11039}
11040
11041
11042THREADED_TEST(InterceptorOnConstructorPrototype) {
11043 v8::HandleScope scope;
11044 Local<ObjectTemplate> templ = ObjectTemplate::New();
11045 templ->SetNamedPropertyHandler(NamedPropertyGetterWhichReturns42,
11046 NamedPropertySetterWhichSetsYOnThisTo23);
11047 LocalContext context;
11048 context->Global()->Set(v8_str("P"), templ->NewInstance());
11049 CompileRun("function C1() {"
11050 " this.x = 23;"
11051 "};"
11052 "C1.prototype = P;"
11053 "function C2() {"
11054 " this.x = 23"
11055 "};"
11056 "C2.prototype = { };"
11057 "C2.prototype.__proto__ = P;");
11058
11059 v8::Local<v8::Script> script;
11060 script = v8::Script::Compile(v8_str("new C1();"));
11061 for (int i = 0; i < 10; i++) {
11062 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
11063 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value());
11064 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value());
11065 }
11066
11067 script = v8::Script::Compile(v8_str("new C2();"));
11068 for (int i = 0; i < 10; i++) {
11069 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run());
11070 CHECK_EQ(23, c2->Get(v8_str("x"))->Int32Value());
11071 CHECK_EQ(42, c2->Get(v8_str("y"))->Int32Value());
11072 }
11073}
Steve Block6ded16b2010-05-10 14:33:55 +010011074
11075
11076TEST(Bug618) {
11077 const char* source = "function C1() {"
11078 " this.x = 23;"
11079 "};"
11080 "C1.prototype = P;";
11081
11082 v8::HandleScope scope;
11083 LocalContext context;
11084 v8::Local<v8::Script> script;
11085
11086 // Use a simple object as prototype.
11087 v8::Local<v8::Object> prototype = v8::Object::New();
11088 prototype->Set(v8_str("y"), v8_num(42));
11089 context->Global()->Set(v8_str("P"), prototype);
11090
11091 // This compile will add the code to the compilation cache.
11092 CompileRun(source);
11093
11094 script = v8::Script::Compile(v8_str("new C1();"));
Kristian Monsen0d5e1162010-09-30 15:31:59 +010011095 // Allow enough iterations for the inobject slack tracking logic
11096 // to finalize instance size and install the fast construct stub.
11097 for (int i = 0; i < 256; i++) {
Steve Block6ded16b2010-05-10 14:33:55 +010011098 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
11099 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value());
11100 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value());
11101 }
11102
11103 // Use an API object with accessors as prototype.
11104 Local<ObjectTemplate> templ = ObjectTemplate::New();
11105 templ->SetAccessor(v8_str("x"),
11106 GetterWhichReturns42,
11107 SetterWhichSetsYOnThisTo23);
11108 context->Global()->Set(v8_str("P"), templ->NewInstance());
11109
11110 // This compile will get the code from the compilation cache.
11111 CompileRun(source);
11112
11113 script = v8::Script::Compile(v8_str("new C1();"));
11114 for (int i = 0; i < 10; i++) {
11115 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
11116 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value());
11117 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value());
11118 }
11119}
11120
11121int prologue_call_count = 0;
11122int epilogue_call_count = 0;
11123int prologue_call_count_second = 0;
11124int epilogue_call_count_second = 0;
11125
11126void PrologueCallback(v8::GCType, v8::GCCallbackFlags) {
11127 ++prologue_call_count;
11128}
11129
11130void EpilogueCallback(v8::GCType, v8::GCCallbackFlags) {
11131 ++epilogue_call_count;
11132}
11133
11134void PrologueCallbackSecond(v8::GCType, v8::GCCallbackFlags) {
11135 ++prologue_call_count_second;
11136}
11137
11138void EpilogueCallbackSecond(v8::GCType, v8::GCCallbackFlags) {
11139 ++epilogue_call_count_second;
11140}
11141
11142TEST(GCCallbacks) {
11143 LocalContext context;
11144
11145 v8::V8::AddGCPrologueCallback(PrologueCallback);
11146 v8::V8::AddGCEpilogueCallback(EpilogueCallback);
11147 CHECK_EQ(0, prologue_call_count);
11148 CHECK_EQ(0, epilogue_call_count);
11149 i::Heap::CollectAllGarbage(false);
11150 CHECK_EQ(1, prologue_call_count);
11151 CHECK_EQ(1, epilogue_call_count);
11152 v8::V8::AddGCPrologueCallback(PrologueCallbackSecond);
11153 v8::V8::AddGCEpilogueCallback(EpilogueCallbackSecond);
11154 i::Heap::CollectAllGarbage(false);
11155 CHECK_EQ(2, prologue_call_count);
11156 CHECK_EQ(2, epilogue_call_count);
11157 CHECK_EQ(1, prologue_call_count_second);
11158 CHECK_EQ(1, epilogue_call_count_second);
11159 v8::V8::RemoveGCPrologueCallback(PrologueCallback);
11160 v8::V8::RemoveGCEpilogueCallback(EpilogueCallback);
11161 i::Heap::CollectAllGarbage(false);
11162 CHECK_EQ(2, prologue_call_count);
11163 CHECK_EQ(2, epilogue_call_count);
11164 CHECK_EQ(2, prologue_call_count_second);
11165 CHECK_EQ(2, epilogue_call_count_second);
11166 v8::V8::RemoveGCPrologueCallback(PrologueCallbackSecond);
11167 v8::V8::RemoveGCEpilogueCallback(EpilogueCallbackSecond);
11168 i::Heap::CollectAllGarbage(false);
11169 CHECK_EQ(2, prologue_call_count);
11170 CHECK_EQ(2, epilogue_call_count);
11171 CHECK_EQ(2, prologue_call_count_second);
11172 CHECK_EQ(2, epilogue_call_count_second);
11173}
Kristian Monsen25f61362010-05-21 11:50:48 +010011174
11175
11176THREADED_TEST(AddToJSFunctionResultCache) {
11177 i::FLAG_allow_natives_syntax = true;
11178 v8::HandleScope scope;
11179
11180 LocalContext context;
11181
11182 const char* code =
11183 "(function() {"
11184 " var key0 = 'a';"
11185 " var key1 = 'b';"
11186 " var r0 = %_GetFromCache(0, key0);"
11187 " var r1 = %_GetFromCache(0, key1);"
11188 " var r0_ = %_GetFromCache(0, key0);"
11189 " if (r0 !== r0_)"
11190 " return 'Different results for ' + key0 + ': ' + r0 + ' vs. ' + r0_;"
11191 " var r1_ = %_GetFromCache(0, key1);"
11192 " if (r1 !== r1_)"
11193 " return 'Different results for ' + key1 + ': ' + r1 + ' vs. ' + r1_;"
11194 " return 'PASSED';"
11195 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010011196 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010011197 ExpectString(code, "PASSED");
11198}
11199
11200
11201static const int k0CacheSize = 16;
11202
11203THREADED_TEST(FillJSFunctionResultCache) {
11204 i::FLAG_allow_natives_syntax = true;
11205 v8::HandleScope scope;
11206
11207 LocalContext context;
11208
11209 const char* code =
11210 "(function() {"
11211 " var k = 'a';"
11212 " var r = %_GetFromCache(0, k);"
11213 " for (var i = 0; i < 16; i++) {"
11214 " %_GetFromCache(0, 'a' + i);"
11215 " };"
11216 " if (r === %_GetFromCache(0, k))"
11217 " return 'FAILED: k0CacheSize is too small';"
11218 " return 'PASSED';"
11219 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010011220 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010011221 ExpectString(code, "PASSED");
11222}
11223
11224
11225THREADED_TEST(RoundRobinGetFromCache) {
11226 i::FLAG_allow_natives_syntax = true;
11227 v8::HandleScope scope;
11228
11229 LocalContext context;
11230
11231 const char* code =
11232 "(function() {"
11233 " var keys = [];"
11234 " for (var i = 0; i < 16; i++) keys.push(i);"
11235 " var values = [];"
11236 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);"
11237 " for (var i = 0; i < 16; i++) {"
11238 " var v = %_GetFromCache(0, keys[i]);"
11239 " if (v !== values[i])"
11240 " return 'Wrong value for ' + "
11241 " keys[i] + ': ' + v + ' vs. ' + values[i];"
11242 " };"
11243 " return 'PASSED';"
11244 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010011245 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010011246 ExpectString(code, "PASSED");
11247}
11248
11249
11250THREADED_TEST(ReverseGetFromCache) {
11251 i::FLAG_allow_natives_syntax = true;
11252 v8::HandleScope scope;
11253
11254 LocalContext context;
11255
11256 const char* code =
11257 "(function() {"
11258 " var keys = [];"
11259 " for (var i = 0; i < 16; i++) keys.push(i);"
11260 " var values = [];"
11261 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);"
11262 " for (var i = 15; i >= 16; i--) {"
11263 " var v = %_GetFromCache(0, keys[i]);"
11264 " if (v !== values[i])"
11265 " return 'Wrong value for ' + "
11266 " keys[i] + ': ' + v + ' vs. ' + values[i];"
11267 " };"
11268 " return 'PASSED';"
11269 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010011270 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010011271 ExpectString(code, "PASSED");
11272}
11273
11274
11275THREADED_TEST(TestEviction) {
11276 i::FLAG_allow_natives_syntax = true;
11277 v8::HandleScope scope;
11278
11279 LocalContext context;
11280
11281 const char* code =
11282 "(function() {"
11283 " for (var i = 0; i < 2*16; i++) {"
11284 " %_GetFromCache(0, 'a' + i);"
11285 " };"
11286 " return 'PASSED';"
11287 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010011288 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010011289 ExpectString(code, "PASSED");
11290}
Steve Block8defd9f2010-07-08 12:39:36 +010011291
11292
11293THREADED_TEST(TwoByteStringInAsciiCons) {
11294 // See Chromium issue 47824.
11295 v8::HandleScope scope;
11296
11297 LocalContext context;
11298 const char* init_code =
11299 "var str1 = 'abelspendabel';"
11300 "var str2 = str1 + str1 + str1;"
11301 "str2;";
11302 Local<Value> result = CompileRun(init_code);
11303
11304 CHECK(result->IsString());
11305 i::Handle<i::String> string = v8::Utils::OpenHandle(String::Cast(*result));
11306 int length = string->length();
11307 CHECK(string->IsAsciiRepresentation());
11308
11309 FlattenString(string);
11310 i::Handle<i::String> flat_string = FlattenGetString(string);
11311
11312 CHECK(string->IsAsciiRepresentation());
11313 CHECK(flat_string->IsAsciiRepresentation());
11314
11315 // Create external resource.
11316 uint16_t* uc16_buffer = new uint16_t[length + 1];
11317
11318 i::String::WriteToFlat(*flat_string, uc16_buffer, 0, length);
11319 uc16_buffer[length] = 0;
11320
11321 TestResource resource(uc16_buffer);
11322
11323 flat_string->MakeExternal(&resource);
11324
11325 CHECK(flat_string->IsTwoByteRepresentation());
11326
11327 // At this point, we should have a Cons string which is flat and ASCII,
11328 // with a first half that is a two-byte string (although it only contains
11329 // ASCII characters). This is a valid sequence of steps, and it can happen
11330 // in real pages.
11331
11332 CHECK(string->IsAsciiRepresentation());
11333 i::ConsString* cons = i::ConsString::cast(*string);
11334 CHECK_EQ(0, cons->second()->length());
11335 CHECK(cons->first()->IsTwoByteRepresentation());
11336
11337 // Check that some string operations work.
11338
11339 // Atom RegExp.
11340 Local<Value> reresult = CompileRun("str2.match(/abel/g).length;");
11341 CHECK_EQ(6, reresult->Int32Value());
11342
11343 // Nonatom RegExp.
11344 reresult = CompileRun("str2.match(/abe./g).length;");
11345 CHECK_EQ(6, reresult->Int32Value());
11346
11347 reresult = CompileRun("str2.search(/bel/g);");
11348 CHECK_EQ(1, reresult->Int32Value());
11349
11350 reresult = CompileRun("str2.search(/be./g);");
11351 CHECK_EQ(1, reresult->Int32Value());
11352
11353 ExpectTrue("/bel/g.test(str2);");
11354
11355 ExpectTrue("/be./g.test(str2);");
11356
11357 reresult = CompileRun("/bel/g.exec(str2);");
11358 CHECK(!reresult->IsNull());
11359
11360 reresult = CompileRun("/be./g.exec(str2);");
11361 CHECK(!reresult->IsNull());
11362
11363 ExpectString("str2.substring(2, 10);", "elspenda");
11364
11365 ExpectString("str2.substring(2, 20);", "elspendabelabelspe");
11366
11367 ExpectString("str2.charAt(2);", "e");
11368
11369 reresult = CompileRun("str2.charCodeAt(2);");
11370 CHECK_EQ(static_cast<int32_t>('e'), reresult->Int32Value());
11371}
Iain Merrick75681382010-08-19 15:07:18 +010011372
11373
11374// Failed access check callback that performs a GC on each invocation.
11375void FailedAccessCheckCallbackGC(Local<v8::Object> target,
11376 v8::AccessType type,
11377 Local<v8::Value> data) {
11378 i::Heap::CollectAllGarbage(true);
11379}
11380
11381
11382TEST(GCInFailedAccessCheckCallback) {
11383 // Install a failed access check callback that performs a GC on each
11384 // invocation. Then force the callback to be called from va
11385
11386 v8::V8::Initialize();
11387 v8::V8::SetFailedAccessCheckCallbackFunction(&FailedAccessCheckCallbackGC);
11388
11389 v8::HandleScope scope;
11390
11391 // Create an ObjectTemplate for global objects and install access
11392 // check callbacks that will block access.
11393 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
11394 global_template->SetAccessCheckCallbacks(NamedGetAccessBlocker,
11395 IndexedGetAccessBlocker,
11396 v8::Handle<v8::Value>(),
11397 false);
11398
11399 // Create a context and set an x property on it's global object.
11400 LocalContext context0(NULL, global_template);
11401 context0->Global()->Set(v8_str("x"), v8_num(42));
11402 v8::Handle<v8::Object> global0 = context0->Global();
11403
11404 // Create a context with a different security token so that the
11405 // failed access check callback will be called on each access.
11406 LocalContext context1(NULL, global_template);
11407 context1->Global()->Set(v8_str("other"), global0);
11408
11409 // Get property with failed access check.
11410 ExpectUndefined("other.x");
11411
11412 // Get element with failed access check.
11413 ExpectUndefined("other[0]");
11414
11415 // Set property with failed access check.
11416 v8::Handle<v8::Value> result = CompileRun("other.x = new Object()");
11417 CHECK(result->IsObject());
11418
11419 // Set element with failed access check.
11420 result = CompileRun("other[0] = new Object()");
11421 CHECK(result->IsObject());
11422
11423 // Get property attribute with failed access check.
11424 ExpectFalse("\'x\' in other");
11425
11426 // Get property attribute for element with failed access check.
11427 ExpectFalse("0 in other");
11428
11429 // Delete property.
11430 ExpectFalse("delete other.x");
11431
11432 // Delete element.
11433 CHECK_EQ(false, global0->Delete(0));
11434
11435 // DefineAccessor.
11436 CHECK_EQ(false,
11437 global0->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("x")));
11438
11439 // Define JavaScript accessor.
11440 ExpectUndefined("Object.prototype.__defineGetter__.call("
11441 " other, \'x\', function() { return 42; })");
11442
11443 // LookupAccessor.
11444 ExpectUndefined("Object.prototype.__lookupGetter__.call("
11445 " other, \'x\')");
11446
11447 // HasLocalElement.
11448 ExpectFalse("Object.prototype.hasOwnProperty.call(other, \'0\')");
11449
11450 CHECK_EQ(false, global0->HasRealIndexedProperty(0));
11451 CHECK_EQ(false, global0->HasRealNamedProperty(v8_str("x")));
11452 CHECK_EQ(false, global0->HasRealNamedCallbackProperty(v8_str("x")));
11453
11454 // Reset the failed access check callback so it does not influence
11455 // the other tests.
11456 v8::V8::SetFailedAccessCheckCallbackFunction(NULL);
11457}
Kristian Monsen0d5e1162010-09-30 15:31:59 +010011458
11459
11460TEST(StringCheckMultipleContexts) {
11461 const char* code =
11462 "(function() { return \"a\".charAt(0); })()";
11463
11464 {
11465 // Run the code twice in the first context to initialize the call IC.
11466 v8::HandleScope scope;
11467 LocalContext context1;
11468 ExpectString(code, "a");
11469 ExpectString(code, "a");
11470 }
11471
11472 {
11473 // Change the String.prototype in the second context and check
11474 // that the right function gets called.
11475 v8::HandleScope scope;
11476 LocalContext context2;
11477 CompileRun("String.prototype.charAt = function() { return \"not a\"; }");
11478 ExpectString(code, "not a");
11479 }
11480}
11481
11482
11483TEST(NumberCheckMultipleContexts) {
11484 const char* code =
11485 "(function() { return (42).toString(); })()";
11486
11487 {
11488 // Run the code twice in the first context to initialize the call IC.
11489 v8::HandleScope scope;
11490 LocalContext context1;
11491 ExpectString(code, "42");
11492 ExpectString(code, "42");
11493 }
11494
11495 {
11496 // Change the Number.prototype in the second context and check
11497 // that the right function gets called.
11498 v8::HandleScope scope;
11499 LocalContext context2;
11500 CompileRun("Number.prototype.toString = function() { return \"not 42\"; }");
11501 ExpectString(code, "not 42");
11502 }
11503}
11504
11505
11506TEST(BooleanCheckMultipleContexts) {
11507 const char* code =
11508 "(function() { return true.toString(); })()";
11509
11510 {
11511 // Run the code twice in the first context to initialize the call IC.
11512 v8::HandleScope scope;
11513 LocalContext context1;
11514 ExpectString(code, "true");
11515 ExpectString(code, "true");
11516 }
11517
11518 {
11519 // Change the Boolean.prototype in the second context and check
11520 // that the right function gets called.
11521 v8::HandleScope scope;
11522 LocalContext context2;
11523 CompileRun("Boolean.prototype.toString = function() { return \"\"; }");
11524 ExpectString(code, "");
11525 }
11526}
Ben Murdochf87a2032010-10-22 12:50:53 +010011527
11528
11529TEST(DontDeleteCellLoadIC) {
11530 const char* function_code =
11531 "function readCell() { while (true) { return cell; } }";
11532
11533 {
11534 // Run the code twice in the first context to initialize the load
11535 // IC for a don't delete cell.
11536 v8::HandleScope scope;
11537 LocalContext context1;
11538 CompileRun("var cell = \"first\";");
11539 ExpectBoolean("delete cell", false);
11540 CompileRun(function_code);
11541 ExpectString("readCell()", "first");
11542 ExpectString("readCell()", "first");
11543 }
11544
11545 {
11546 // Use a deletable cell in the second context.
11547 v8::HandleScope scope;
11548 LocalContext context2;
11549 CompileRun("cell = \"second\";");
11550 CompileRun(function_code);
11551 ExpectString("readCell()", "second");
11552 ExpectBoolean("delete cell", true);
11553 ExpectString("(function() {"
11554 " try {"
11555 " return readCell();"
11556 " } catch(e) {"
11557 " return e.toString();"
11558 " }"
11559 "})()",
11560 "ReferenceError: cell is not defined");
11561 CompileRun("cell = \"new_second\";");
11562 i::Heap::CollectAllGarbage(true);
11563 ExpectString("readCell()", "new_second");
11564 ExpectString("readCell()", "new_second");
11565 }
11566}
11567
11568
11569TEST(DontDeleteCellLoadICForceDelete) {
11570 const char* function_code =
11571 "function readCell() { while (true) { return cell; } }";
11572
11573 // Run the code twice to initialize the load IC for a don't delete
11574 // cell.
11575 v8::HandleScope scope;
11576 LocalContext context;
11577 CompileRun("var cell = \"value\";");
11578 ExpectBoolean("delete cell", false);
11579 CompileRun(function_code);
11580 ExpectString("readCell()", "value");
11581 ExpectString("readCell()", "value");
11582
11583 // Delete the cell using the API and check the inlined code works
11584 // correctly.
11585 CHECK(context->Global()->ForceDelete(v8_str("cell")));
11586 ExpectString("(function() {"
11587 " try {"
11588 " return readCell();"
11589 " } catch(e) {"
11590 " return e.toString();"
11591 " }"
11592 "})()",
11593 "ReferenceError: cell is not defined");
11594}
11595
11596
11597TEST(DontDeleteCellLoadICAPI) {
11598 const char* function_code =
11599 "function readCell() { while (true) { return cell; } }";
11600
11601 // Run the code twice to initialize the load IC for a don't delete
11602 // cell created using the API.
11603 v8::HandleScope scope;
11604 LocalContext context;
11605 context->Global()->Set(v8_str("cell"), v8_str("value"), v8::DontDelete);
11606 ExpectBoolean("delete cell", false);
11607 CompileRun(function_code);
11608 ExpectString("readCell()", "value");
11609 ExpectString("readCell()", "value");
11610
11611 // Delete the cell using the API and check the inlined code works
11612 // correctly.
11613 CHECK(context->Global()->ForceDelete(v8_str("cell")));
11614 ExpectString("(function() {"
11615 " try {"
11616 " return readCell();"
11617 " } catch(e) {"
11618 " return e.toString();"
11619 " }"
11620 "})()",
11621 "ReferenceError: cell is not defined");
11622}
11623
11624
11625TEST(GlobalLoadICGC) {
11626 const char* function_code =
11627 "function readCell() { while (true) { return cell; } }";
11628
11629 // Check inline load code for a don't delete cell is cleared during
11630 // GC.
11631 {
11632 v8::HandleScope scope;
11633 LocalContext context;
11634 CompileRun("var cell = \"value\";");
11635 ExpectBoolean("delete cell", false);
11636 CompileRun(function_code);
11637 ExpectString("readCell()", "value");
11638 ExpectString("readCell()", "value");
11639 }
11640 {
11641 v8::HandleScope scope;
11642 LocalContext context2;
11643 // Hold the code object in the second context.
11644 CompileRun(function_code);
11645 CheckSurvivingGlobalObjectsCount(1);
11646 }
11647
11648 // Check inline load code for a deletable cell is cleared during GC.
11649 {
11650 v8::HandleScope scope;
11651 LocalContext context;
11652 CompileRun("cell = \"value\";");
11653 CompileRun(function_code);
11654 ExpectString("readCell()", "value");
11655 ExpectString("readCell()", "value");
11656 }
11657 {
11658 v8::HandleScope scope;
11659 LocalContext context2;
11660 // Hold the code object in the second context.
11661 CompileRun(function_code);
11662 CheckSurvivingGlobalObjectsCount(1);
11663 }
11664}
11665
11666
11667TEST(RegExp) {
11668 v8::HandleScope scope;
11669 LocalContext context;
11670
11671 v8::Handle<v8::RegExp> re = v8::RegExp::New(v8_str("foo"), v8::RegExp::kNone);
11672 CHECK(re->IsRegExp());
11673 CHECK(re->GetSource()->Equals(v8_str("foo")));
11674 CHECK_EQ(re->GetFlags(), v8::RegExp::kNone);
11675
11676 re = v8::RegExp::New(v8_str("bar"),
11677 static_cast<v8::RegExp::Flags>(v8::RegExp::kIgnoreCase |
11678 v8::RegExp::kGlobal));
11679 CHECK(re->IsRegExp());
11680 CHECK(re->GetSource()->Equals(v8_str("bar")));
11681 CHECK_EQ(static_cast<int>(re->GetFlags()),
11682 v8::RegExp::kIgnoreCase | v8::RegExp::kGlobal);
11683
11684 re = v8::RegExp::New(v8_str("baz"),
11685 static_cast<v8::RegExp::Flags>(v8::RegExp::kIgnoreCase |
11686 v8::RegExp::kMultiline));
11687 CHECK(re->IsRegExp());
11688 CHECK(re->GetSource()->Equals(v8_str("baz")));
11689 CHECK_EQ(static_cast<int>(re->GetFlags()),
11690 v8::RegExp::kIgnoreCase | v8::RegExp::kMultiline);
11691
11692 re = CompileRun("/quux/").As<v8::RegExp>();
11693 CHECK(re->IsRegExp());
11694 CHECK(re->GetSource()->Equals(v8_str("quux")));
11695 CHECK_EQ(re->GetFlags(), v8::RegExp::kNone);
11696
11697 re = CompileRun("/quux/gm").As<v8::RegExp>();
11698 CHECK(re->IsRegExp());
11699 CHECK(re->GetSource()->Equals(v8_str("quux")));
11700 CHECK_EQ(static_cast<int>(re->GetFlags()),
11701 v8::RegExp::kGlobal | v8::RegExp::kMultiline);
11702
11703 // Override the RegExp constructor and check the API constructor
11704 // still works.
11705 CompileRun("RegExp = function() {}");
11706
11707 re = v8::RegExp::New(v8_str("foobar"), v8::RegExp::kNone);
11708 CHECK(re->IsRegExp());
11709 CHECK(re->GetSource()->Equals(v8_str("foobar")));
11710 CHECK_EQ(re->GetFlags(), v8::RegExp::kNone);
11711
11712 re = v8::RegExp::New(v8_str("foobarbaz"),
11713 static_cast<v8::RegExp::Flags>(v8::RegExp::kIgnoreCase |
11714 v8::RegExp::kMultiline));
11715 CHECK(re->IsRegExp());
11716 CHECK(re->GetSource()->Equals(v8_str("foobarbaz")));
11717 CHECK_EQ(static_cast<int>(re->GetFlags()),
11718 v8::RegExp::kIgnoreCase | v8::RegExp::kMultiline);
11719
11720 context->Global()->Set(v8_str("re"), re);
11721 ExpectTrue("re.test('FoobarbaZ')");
11722
11723 v8::TryCatch try_catch;
11724 re = v8::RegExp::New(v8_str("foo["), v8::RegExp::kNone);
11725 CHECK(re.IsEmpty());
11726 CHECK(try_catch.HasCaught());
11727 context->Global()->Set(v8_str("ex"), try_catch.Exception());
11728 ExpectTrue("ex instanceof SyntaxError");
11729}
11730
11731
11732static v8::Handle<v8::Value> Getter(v8::Local<v8::String> property,
11733 const v8::AccessorInfo& info ) {
11734 return v8_str("42!");
11735}
11736
11737
11738static v8::Handle<v8::Array> Enumerator(const v8::AccessorInfo& info) {
11739 v8::Handle<v8::Array> result = v8::Array::New();
11740 result->Set(0, v8_str("universalAnswer"));
11741 return result;
11742}
11743
11744
11745TEST(NamedEnumeratorAndForIn) {
11746 v8::HandleScope handle_scope;
11747 LocalContext context;
11748 v8::Context::Scope context_scope(context.local());
11749
11750 v8::Handle<v8::ObjectTemplate> tmpl = v8::ObjectTemplate::New();
11751 tmpl->SetNamedPropertyHandler(Getter, NULL, NULL, NULL, Enumerator);
11752 context->Global()->Set(v8_str("o"), tmpl->NewInstance());
11753 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun(
11754 "var result = []; for (var k in o) result.push(k); result"));
11755 CHECK_EQ(1, result->Length());
11756 CHECK_EQ(v8_str("universalAnswer"), result->Get(0));
11757}