blob: 33d505eaaa2640c1cb5018c5e60c521602a46a72 [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"
Steve Block3ce2e202009-11-05 08:53:23 +000037#include "utils.h"
Steve Blocka7e24c12009-10-30 11:49:00 +000038#include "cctest.h"
Iain Merrick9ac36c92010-09-13 15:29:50 +010039#include "parser.h"
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -080040#include "unicode-inl.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
Steve Blocka7e24c12009-10-30 11:49:00 +000052using ::v8::AccessorInfo;
Steve Block44f0eee2011-05-26 01:26:41 +010053using ::v8::Context;
Steve Blocka7e24c12009-10-30 11:49:00 +000054using ::v8::Extension;
Steve Block44f0eee2011-05-26 01:26:41 +010055using ::v8::Function;
56using ::v8::HandleScope;
57using ::v8::Local;
58using ::v8::Object;
59using ::v8::ObjectTemplate;
60using ::v8::Persistent;
61using ::v8::Script;
62using ::v8::String;
63using ::v8::Value;
64using ::v8::V8;
Steve Blocka7e24c12009-10-30 11:49:00 +000065
Steve Block8defd9f2010-07-08 12:39:36 +010066namespace i = ::i;
Steve Blocka7e24c12009-10-30 11:49:00 +000067
Steve Blocka7e24c12009-10-30 11:49:00 +000068
Leon Clarked91b9f72010-01-27 17:25:45 +000069static void ExpectString(const char* code, const char* expected) {
70 Local<Value> result = CompileRun(code);
71 CHECK(result->IsString());
72 String::AsciiValue ascii(result);
73 CHECK_EQ(expected, *ascii);
74}
75
76
77static void ExpectBoolean(const char* code, bool expected) {
78 Local<Value> result = CompileRun(code);
79 CHECK(result->IsBoolean());
80 CHECK_EQ(expected, result->BooleanValue());
81}
82
83
Leon Clarkef7060e22010-06-03 12:02:55 +010084static void ExpectTrue(const char* code) {
85 ExpectBoolean(code, true);
86}
87
88
Iain Merrick75681382010-08-19 15:07:18 +010089static void ExpectFalse(const char* code) {
90 ExpectBoolean(code, false);
91}
92
93
Leon Clarked91b9f72010-01-27 17:25:45 +000094static void ExpectObject(const char* code, Local<Value> expected) {
95 Local<Value> result = CompileRun(code);
96 CHECK(result->Equals(expected));
97}
98
99
Iain Merrick75681382010-08-19 15:07:18 +0100100static void ExpectUndefined(const char* code) {
101 Local<Value> result = CompileRun(code);
102 CHECK(result->IsUndefined());
103}
104
105
Steve Blocka7e24c12009-10-30 11:49:00 +0000106static int signature_callback_count;
107static v8::Handle<Value> IncrementingSignatureCallback(
108 const v8::Arguments& args) {
109 ApiTestFuzzer::Fuzz();
110 signature_callback_count++;
111 v8::Handle<v8::Array> result = v8::Array::New(args.Length());
112 for (int i = 0; i < args.Length(); i++)
113 result->Set(v8::Integer::New(i), args[i]);
114 return result;
115}
116
117
118static v8::Handle<Value> SignatureCallback(const v8::Arguments& args) {
119 ApiTestFuzzer::Fuzz();
120 v8::Handle<v8::Array> result = v8::Array::New(args.Length());
121 for (int i = 0; i < args.Length(); i++) {
122 result->Set(v8::Integer::New(i), args[i]);
123 }
124 return result;
125}
126
127
128THREADED_TEST(Handles) {
129 v8::HandleScope scope;
130 Local<Context> local_env;
131 {
132 LocalContext env;
133 local_env = env.local();
134 }
135
136 // Local context should still be live.
137 CHECK(!local_env.IsEmpty());
138 local_env->Enter();
139
140 v8::Handle<v8::Primitive> undef = v8::Undefined();
141 CHECK(!undef.IsEmpty());
142 CHECK(undef->IsUndefined());
143
144 const char* c_source = "1 + 2 + 3";
145 Local<String> source = String::New(c_source);
146 Local<Script> script = Script::Compile(source);
147 CHECK_EQ(6, script->Run()->Int32Value());
148
149 local_env->Exit();
150}
151
152
Steve Blocka7e24c12009-10-30 11:49:00 +0000153THREADED_TEST(ReceiverSignature) {
154 v8::HandleScope scope;
155 LocalContext env;
156 v8::Handle<v8::FunctionTemplate> fun = v8::FunctionTemplate::New();
157 v8::Handle<v8::Signature> sig = v8::Signature::New(fun);
158 fun->PrototypeTemplate()->Set(
159 v8_str("m"),
160 v8::FunctionTemplate::New(IncrementingSignatureCallback,
161 v8::Handle<Value>(),
162 sig));
163 env->Global()->Set(v8_str("Fun"), fun->GetFunction());
164 signature_callback_count = 0;
165 CompileRun(
166 "var o = new Fun();"
167 "o.m();");
168 CHECK_EQ(1, signature_callback_count);
169 v8::Handle<v8::FunctionTemplate> sub_fun = v8::FunctionTemplate::New();
170 sub_fun->Inherit(fun);
171 env->Global()->Set(v8_str("SubFun"), sub_fun->GetFunction());
172 CompileRun(
173 "var o = new SubFun();"
174 "o.m();");
175 CHECK_EQ(2, signature_callback_count);
176
177 v8::TryCatch try_catch;
178 CompileRun(
179 "var o = { };"
180 "o.m = Fun.prototype.m;"
181 "o.m();");
182 CHECK_EQ(2, signature_callback_count);
183 CHECK(try_catch.HasCaught());
184 try_catch.Reset();
185 v8::Handle<v8::FunctionTemplate> unrel_fun = v8::FunctionTemplate::New();
186 sub_fun->Inherit(fun);
187 env->Global()->Set(v8_str("UnrelFun"), unrel_fun->GetFunction());
188 CompileRun(
189 "var o = new UnrelFun();"
190 "o.m = Fun.prototype.m;"
191 "o.m();");
192 CHECK_EQ(2, signature_callback_count);
193 CHECK(try_catch.HasCaught());
194}
195
196
197
198
199THREADED_TEST(ArgumentSignature) {
200 v8::HandleScope scope;
201 LocalContext env;
202 v8::Handle<v8::FunctionTemplate> cons = v8::FunctionTemplate::New();
203 cons->SetClassName(v8_str("Cons"));
204 v8::Handle<v8::Signature> sig =
205 v8::Signature::New(v8::Handle<v8::FunctionTemplate>(), 1, &cons);
206 v8::Handle<v8::FunctionTemplate> fun =
207 v8::FunctionTemplate::New(SignatureCallback, v8::Handle<Value>(), sig);
208 env->Global()->Set(v8_str("Cons"), cons->GetFunction());
209 env->Global()->Set(v8_str("Fun1"), fun->GetFunction());
210
211 v8::Handle<Value> value1 = CompileRun("Fun1(4) == '';");
212 CHECK(value1->IsTrue());
213
214 v8::Handle<Value> value2 = CompileRun("Fun1(new Cons()) == '[object Cons]';");
215 CHECK(value2->IsTrue());
216
217 v8::Handle<Value> value3 = CompileRun("Fun1() == '';");
218 CHECK(value3->IsTrue());
219
220 v8::Handle<v8::FunctionTemplate> cons1 = v8::FunctionTemplate::New();
221 cons1->SetClassName(v8_str("Cons1"));
222 v8::Handle<v8::FunctionTemplate> cons2 = v8::FunctionTemplate::New();
223 cons2->SetClassName(v8_str("Cons2"));
224 v8::Handle<v8::FunctionTemplate> cons3 = v8::FunctionTemplate::New();
225 cons3->SetClassName(v8_str("Cons3"));
226
227 v8::Handle<v8::FunctionTemplate> args[3] = { cons1, cons2, cons3 };
228 v8::Handle<v8::Signature> wsig =
229 v8::Signature::New(v8::Handle<v8::FunctionTemplate>(), 3, args);
230 v8::Handle<v8::FunctionTemplate> fun2 =
231 v8::FunctionTemplate::New(SignatureCallback, v8::Handle<Value>(), wsig);
232
233 env->Global()->Set(v8_str("Cons1"), cons1->GetFunction());
234 env->Global()->Set(v8_str("Cons2"), cons2->GetFunction());
235 env->Global()->Set(v8_str("Cons3"), cons3->GetFunction());
236 env->Global()->Set(v8_str("Fun2"), fun2->GetFunction());
237 v8::Handle<Value> value4 = CompileRun(
238 "Fun2(new Cons1(), new Cons2(), new Cons3()) =="
239 "'[object Cons1],[object Cons2],[object Cons3]'");
240 CHECK(value4->IsTrue());
241
242 v8::Handle<Value> value5 = CompileRun(
243 "Fun2(new Cons1(), new Cons2(), 5) == '[object Cons1],[object Cons2],'");
244 CHECK(value5->IsTrue());
245
246 v8::Handle<Value> value6 = CompileRun(
247 "Fun2(new Cons3(), new Cons2(), new Cons1()) == ',[object Cons2],'");
248 CHECK(value6->IsTrue());
249
250 v8::Handle<Value> value7 = CompileRun(
251 "Fun2(new Cons1(), new Cons2(), new Cons3(), 'd') == "
252 "'[object Cons1],[object Cons2],[object Cons3],d';");
253 CHECK(value7->IsTrue());
254
255 v8::Handle<Value> value8 = CompileRun(
256 "Fun2(new Cons1(), new Cons2()) == '[object Cons1],[object Cons2]'");
257 CHECK(value8->IsTrue());
258}
259
260
261THREADED_TEST(HulIgennem) {
262 v8::HandleScope scope;
263 LocalContext env;
264 v8::Handle<v8::Primitive> undef = v8::Undefined();
265 Local<String> undef_str = undef->ToString();
266 char* value = i::NewArray<char>(undef_str->Length() + 1);
267 undef_str->WriteAscii(value);
268 CHECK_EQ(0, strcmp(value, "undefined"));
269 i::DeleteArray(value);
270}
271
272
273THREADED_TEST(Access) {
274 v8::HandleScope scope;
275 LocalContext env;
276 Local<v8::Object> obj = v8::Object::New();
277 Local<Value> foo_before = obj->Get(v8_str("foo"));
278 CHECK(foo_before->IsUndefined());
279 Local<String> bar_str = v8_str("bar");
280 obj->Set(v8_str("foo"), bar_str);
281 Local<Value> foo_after = obj->Get(v8_str("foo"));
282 CHECK(!foo_after->IsUndefined());
283 CHECK(foo_after->IsString());
284 CHECK_EQ(bar_str, foo_after);
285}
286
287
Steve Block6ded16b2010-05-10 14:33:55 +0100288THREADED_TEST(AccessElement) {
289 v8::HandleScope scope;
290 LocalContext env;
291 Local<v8::Object> obj = v8::Object::New();
292 Local<Value> before = obj->Get(1);
293 CHECK(before->IsUndefined());
294 Local<String> bar_str = v8_str("bar");
295 obj->Set(1, bar_str);
296 Local<Value> after = obj->Get(1);
297 CHECK(!after->IsUndefined());
298 CHECK(after->IsString());
299 CHECK_EQ(bar_str, after);
300
301 Local<v8::Array> value = CompileRun("[\"a\", \"b\"]").As<v8::Array>();
302 CHECK_EQ(v8_str("a"), value->Get(0));
303 CHECK_EQ(v8_str("b"), value->Get(1));
304}
305
306
Steve Blocka7e24c12009-10-30 11:49:00 +0000307THREADED_TEST(Script) {
308 v8::HandleScope scope;
309 LocalContext env;
310 const char* c_source = "1 + 2 + 3";
311 Local<String> source = String::New(c_source);
312 Local<Script> script = Script::Compile(source);
313 CHECK_EQ(6, script->Run()->Int32Value());
314}
315
316
317static uint16_t* AsciiToTwoByteString(const char* source) {
Steve Blockd0582a62009-12-15 09:54:21 +0000318 int array_length = i::StrLength(source) + 1;
Steve Blocka7e24c12009-10-30 11:49:00 +0000319 uint16_t* converted = i::NewArray<uint16_t>(array_length);
Steve Blockd0582a62009-12-15 09:54:21 +0000320 for (int i = 0; i < array_length; i++) converted[i] = source[i];
Steve Blocka7e24c12009-10-30 11:49:00 +0000321 return converted;
322}
323
324
325class TestResource: public String::ExternalStringResource {
326 public:
327 static int dispose_count;
328
329 explicit TestResource(uint16_t* data)
330 : data_(data), length_(0) {
331 while (data[length_]) ++length_;
332 }
333
334 ~TestResource() {
335 i::DeleteArray(data_);
336 ++dispose_count;
337 }
338
339 const uint16_t* data() const {
340 return data_;
341 }
342
343 size_t length() const {
344 return length_;
345 }
346 private:
347 uint16_t* data_;
348 size_t length_;
349};
350
351
352int TestResource::dispose_count = 0;
353
354
355class TestAsciiResource: public String::ExternalAsciiStringResource {
356 public:
357 static int dispose_count;
358
359 explicit TestAsciiResource(const char* data)
360 : data_(data),
361 length_(strlen(data)) { }
362
363 ~TestAsciiResource() {
364 i::DeleteArray(data_);
365 ++dispose_count;
366 }
367
368 const char* data() const {
369 return data_;
370 }
371
372 size_t length() const {
373 return length_;
374 }
375 private:
376 const char* data_;
377 size_t length_;
378};
379
380
381int TestAsciiResource::dispose_count = 0;
382
383
384THREADED_TEST(ScriptUsingStringResource) {
385 TestResource::dispose_count = 0;
386 const char* c_source = "1 + 2 * 3";
387 uint16_t* two_byte_source = AsciiToTwoByteString(c_source);
388 {
389 v8::HandleScope scope;
390 LocalContext env;
391 TestResource* resource = new TestResource(two_byte_source);
392 Local<String> source = String::NewExternal(resource);
393 Local<Script> script = Script::Compile(source);
394 Local<Value> value = script->Run();
395 CHECK(value->IsNumber());
396 CHECK_EQ(7, value->Int32Value());
397 CHECK(source->IsExternal());
398 CHECK_EQ(resource,
399 static_cast<TestResource*>(source->GetExternalStringResource()));
Steve Block44f0eee2011-05-26 01:26:41 +0100400 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000401 CHECK_EQ(0, TestResource::dispose_count);
402 }
Steve Block44f0eee2011-05-26 01:26:41 +0100403 v8::internal::Isolate::Current()->compilation_cache()->Clear();
404 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000405 CHECK_EQ(1, TestResource::dispose_count);
406}
407
408
409THREADED_TEST(ScriptUsingAsciiStringResource) {
410 TestAsciiResource::dispose_count = 0;
411 const char* c_source = "1 + 2 * 3";
412 {
413 v8::HandleScope scope;
414 LocalContext env;
415 Local<String> source =
416 String::NewExternal(new TestAsciiResource(i::StrDup(c_source)));
417 Local<Script> script = Script::Compile(source);
418 Local<Value> value = script->Run();
419 CHECK(value->IsNumber());
420 CHECK_EQ(7, value->Int32Value());
Steve Block44f0eee2011-05-26 01:26:41 +0100421 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000422 CHECK_EQ(0, TestAsciiResource::dispose_count);
423 }
Steve Block44f0eee2011-05-26 01:26:41 +0100424 i::Isolate::Current()->compilation_cache()->Clear();
425 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000426 CHECK_EQ(1, TestAsciiResource::dispose_count);
427}
428
429
430THREADED_TEST(ScriptMakingExternalString) {
431 TestResource::dispose_count = 0;
432 uint16_t* two_byte_source = AsciiToTwoByteString("1 + 2 * 3");
433 {
434 v8::HandleScope scope;
435 LocalContext env;
436 Local<String> source = String::New(two_byte_source);
Andrei Popescu402d9372010-02-26 13:31:12 +0000437 // Trigger GCs so that the newly allocated string moves to old gen.
Steve Block44f0eee2011-05-26 01:26:41 +0100438 HEAP->CollectGarbage(i::NEW_SPACE); // in survivor space now
439 HEAP->CollectGarbage(i::NEW_SPACE); // in old gen now
Steve Blocka7e24c12009-10-30 11:49:00 +0000440 bool success = source->MakeExternal(new TestResource(two_byte_source));
441 CHECK(success);
442 Local<Script> script = Script::Compile(source);
443 Local<Value> value = script->Run();
444 CHECK(value->IsNumber());
445 CHECK_EQ(7, value->Int32Value());
Steve Block44f0eee2011-05-26 01:26:41 +0100446 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000447 CHECK_EQ(0, TestResource::dispose_count);
448 }
Steve Block44f0eee2011-05-26 01:26:41 +0100449 i::Isolate::Current()->compilation_cache()->Clear();
450 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000451 CHECK_EQ(1, TestResource::dispose_count);
452}
453
454
455THREADED_TEST(ScriptMakingExternalAsciiString) {
456 TestAsciiResource::dispose_count = 0;
457 const char* c_source = "1 + 2 * 3";
458 {
459 v8::HandleScope scope;
460 LocalContext env;
461 Local<String> source = v8_str(c_source);
Andrei Popescu402d9372010-02-26 13:31:12 +0000462 // Trigger GCs so that the newly allocated string moves to old gen.
Steve Block44f0eee2011-05-26 01:26:41 +0100463 HEAP->CollectGarbage(i::NEW_SPACE); // in survivor space now
464 HEAP->CollectGarbage(i::NEW_SPACE); // in old gen now
Steve Blocka7e24c12009-10-30 11:49:00 +0000465 bool success = source->MakeExternal(
466 new TestAsciiResource(i::StrDup(c_source)));
467 CHECK(success);
468 Local<Script> script = Script::Compile(source);
469 Local<Value> value = script->Run();
470 CHECK(value->IsNumber());
471 CHECK_EQ(7, value->Int32Value());
Steve Block44f0eee2011-05-26 01:26:41 +0100472 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000473 CHECK_EQ(0, TestAsciiResource::dispose_count);
474 }
Steve Block44f0eee2011-05-26 01:26:41 +0100475 i::Isolate::Current()->compilation_cache()->Clear();
476 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000477 CHECK_EQ(1, TestAsciiResource::dispose_count);
478}
479
480
Andrei Popescu402d9372010-02-26 13:31:12 +0000481TEST(MakingExternalStringConditions) {
482 v8::HandleScope scope;
483 LocalContext env;
484
485 // Free some space in the new space so that we can check freshness.
Steve Block44f0eee2011-05-26 01:26:41 +0100486 HEAP->CollectGarbage(i::NEW_SPACE);
487 HEAP->CollectGarbage(i::NEW_SPACE);
Andrei Popescu402d9372010-02-26 13:31:12 +0000488
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100489 uint16_t* two_byte_string = AsciiToTwoByteString("small");
490 Local<String> small_string = String::New(two_byte_string);
491 i::DeleteArray(two_byte_string);
492
Andrei Popescu402d9372010-02-26 13:31:12 +0000493 // We should refuse to externalize newly created small string.
494 CHECK(!small_string->CanMakeExternal());
495 // Trigger GCs so that the newly allocated string moves to old gen.
Steve Block44f0eee2011-05-26 01:26:41 +0100496 HEAP->CollectGarbage(i::NEW_SPACE); // in survivor space now
497 HEAP->CollectGarbage(i::NEW_SPACE); // in old gen now
Andrei Popescu402d9372010-02-26 13:31:12 +0000498 // Old space strings should be accepted.
499 CHECK(small_string->CanMakeExternal());
500
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100501 two_byte_string = AsciiToTwoByteString("small 2");
502 small_string = String::New(two_byte_string);
503 i::DeleteArray(two_byte_string);
504
Andrei Popescu402d9372010-02-26 13:31:12 +0000505 // We should refuse externalizing newly created small string.
506 CHECK(!small_string->CanMakeExternal());
507 for (int i = 0; i < 100; i++) {
508 String::Value value(small_string);
509 }
510 // Frequently used strings should be accepted.
511 CHECK(small_string->CanMakeExternal());
512
513 const int buf_size = 10 * 1024;
514 char* buf = i::NewArray<char>(buf_size);
515 memset(buf, 'a', buf_size);
516 buf[buf_size - 1] = '\0';
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100517
518 two_byte_string = AsciiToTwoByteString(buf);
519 Local<String> large_string = String::New(two_byte_string);
Andrei Popescu402d9372010-02-26 13:31:12 +0000520 i::DeleteArray(buf);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100521 i::DeleteArray(two_byte_string);
Andrei Popescu402d9372010-02-26 13:31:12 +0000522 // Large strings should be immediately accepted.
523 CHECK(large_string->CanMakeExternal());
524}
525
526
527TEST(MakingExternalAsciiStringConditions) {
528 v8::HandleScope scope;
529 LocalContext env;
530
531 // Free some space in the new space so that we can check freshness.
Steve Block44f0eee2011-05-26 01:26:41 +0100532 HEAP->CollectGarbage(i::NEW_SPACE);
533 HEAP->CollectGarbage(i::NEW_SPACE);
Andrei Popescu402d9372010-02-26 13:31:12 +0000534
535 Local<String> small_string = String::New("small");
536 // We should refuse to externalize newly created small string.
537 CHECK(!small_string->CanMakeExternal());
538 // Trigger GCs so that the newly allocated string moves to old gen.
Steve Block44f0eee2011-05-26 01:26:41 +0100539 HEAP->CollectGarbage(i::NEW_SPACE); // in survivor space now
540 HEAP->CollectGarbage(i::NEW_SPACE); // in old gen now
Andrei Popescu402d9372010-02-26 13:31:12 +0000541 // Old space strings should be accepted.
542 CHECK(small_string->CanMakeExternal());
543
544 small_string = String::New("small 2");
545 // We should refuse externalizing newly created small string.
546 CHECK(!small_string->CanMakeExternal());
547 for (int i = 0; i < 100; i++) {
548 String::Value value(small_string);
549 }
550 // Frequently used strings should be accepted.
551 CHECK(small_string->CanMakeExternal());
552
553 const int buf_size = 10 * 1024;
554 char* buf = i::NewArray<char>(buf_size);
555 memset(buf, 'a', buf_size);
556 buf[buf_size - 1] = '\0';
557 Local<String> large_string = String::New(buf);
558 i::DeleteArray(buf);
559 // Large strings should be immediately accepted.
560 CHECK(large_string->CanMakeExternal());
561}
562
563
Steve Blocka7e24c12009-10-30 11:49:00 +0000564THREADED_TEST(UsingExternalString) {
565 {
566 v8::HandleScope scope;
567 uint16_t* two_byte_string = AsciiToTwoByteString("test string");
568 Local<String> string =
569 String::NewExternal(new TestResource(two_byte_string));
570 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
571 // Trigger GCs so that the newly allocated string moves to old gen.
Steve Block44f0eee2011-05-26 01:26:41 +0100572 HEAP->CollectGarbage(i::NEW_SPACE); // in survivor space now
573 HEAP->CollectGarbage(i::NEW_SPACE); // in old gen now
574 i::Handle<i::String> isymbol = FACTORY->SymbolFromString(istring);
Steve Blocka7e24c12009-10-30 11:49:00 +0000575 CHECK(isymbol->IsSymbol());
576 }
Steve Block44f0eee2011-05-26 01:26:41 +0100577 HEAP->CollectAllGarbage(false);
578 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000579}
580
581
582THREADED_TEST(UsingExternalAsciiString) {
583 {
584 v8::HandleScope scope;
585 const char* one_byte_string = "test string";
586 Local<String> string = String::NewExternal(
587 new TestAsciiResource(i::StrDup(one_byte_string)));
588 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
589 // Trigger GCs so that the newly allocated string moves to old gen.
Steve Block44f0eee2011-05-26 01:26:41 +0100590 HEAP->CollectGarbage(i::NEW_SPACE); // in survivor space now
591 HEAP->CollectGarbage(i::NEW_SPACE); // in old gen now
592 i::Handle<i::String> isymbol = FACTORY->SymbolFromString(istring);
Steve Blocka7e24c12009-10-30 11:49:00 +0000593 CHECK(isymbol->IsSymbol());
594 }
Steve Block44f0eee2011-05-26 01:26:41 +0100595 HEAP->CollectAllGarbage(false);
596 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000597}
598
599
Leon Clarkee46be812010-01-19 14:06:41 +0000600THREADED_TEST(ScavengeExternalString) {
601 TestResource::dispose_count = 0;
Steve Block6ded16b2010-05-10 14:33:55 +0100602 bool in_new_space = false;
Leon Clarkee46be812010-01-19 14:06:41 +0000603 {
604 v8::HandleScope scope;
605 uint16_t* two_byte_string = AsciiToTwoByteString("test string");
606 Local<String> string =
607 String::NewExternal(new TestResource(two_byte_string));
608 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
Steve Block44f0eee2011-05-26 01:26:41 +0100609 HEAP->CollectGarbage(i::NEW_SPACE);
610 in_new_space = HEAP->InNewSpace(*istring);
611 CHECK(in_new_space || HEAP->old_data_space()->Contains(*istring));
Leon Clarkee46be812010-01-19 14:06:41 +0000612 CHECK_EQ(0, TestResource::dispose_count);
613 }
Steve Block44f0eee2011-05-26 01:26:41 +0100614 HEAP->CollectGarbage(in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE);
Leon Clarkee46be812010-01-19 14:06:41 +0000615 CHECK_EQ(1, TestResource::dispose_count);
616}
617
618
619THREADED_TEST(ScavengeExternalAsciiString) {
620 TestAsciiResource::dispose_count = 0;
Steve Block6ded16b2010-05-10 14:33:55 +0100621 bool in_new_space = false;
Leon Clarkee46be812010-01-19 14:06:41 +0000622 {
623 v8::HandleScope scope;
624 const char* one_byte_string = "test string";
625 Local<String> string = String::NewExternal(
626 new TestAsciiResource(i::StrDup(one_byte_string)));
627 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
Steve Block44f0eee2011-05-26 01:26:41 +0100628 HEAP->CollectGarbage(i::NEW_SPACE);
629 in_new_space = HEAP->InNewSpace(*istring);
630 CHECK(in_new_space || HEAP->old_data_space()->Contains(*istring));
Leon Clarkee46be812010-01-19 14:06:41 +0000631 CHECK_EQ(0, TestAsciiResource::dispose_count);
632 }
Steve Block44f0eee2011-05-26 01:26:41 +0100633 HEAP->CollectGarbage(in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE);
Leon Clarkee46be812010-01-19 14:06:41 +0000634 CHECK_EQ(1, TestAsciiResource::dispose_count);
635}
636
637
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100638class TestAsciiResourceWithDisposeControl: public TestAsciiResource {
639 public:
640 static int dispose_calls;
641
642 TestAsciiResourceWithDisposeControl(const char* data, bool dispose)
643 : TestAsciiResource(data),
644 dispose_(dispose) { }
645
646 void Dispose() {
647 ++dispose_calls;
648 if (dispose_) delete this;
649 }
650 private:
651 bool dispose_;
652};
653
654
655int TestAsciiResourceWithDisposeControl::dispose_calls = 0;
656
657
658TEST(ExternalStringWithDisposeHandling) {
659 const char* c_source = "1 + 2 * 3";
660
661 // Use a stack allocated external string resource allocated object.
662 TestAsciiResource::dispose_count = 0;
663 TestAsciiResourceWithDisposeControl::dispose_calls = 0;
664 TestAsciiResourceWithDisposeControl res_stack(i::StrDup(c_source), false);
665 {
666 v8::HandleScope scope;
667 LocalContext env;
668 Local<String> source = String::NewExternal(&res_stack);
669 Local<Script> script = Script::Compile(source);
670 Local<Value> value = script->Run();
671 CHECK(value->IsNumber());
672 CHECK_EQ(7, value->Int32Value());
Steve Block44f0eee2011-05-26 01:26:41 +0100673 HEAP->CollectAllGarbage(false);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100674 CHECK_EQ(0, TestAsciiResource::dispose_count);
675 }
Steve Block44f0eee2011-05-26 01:26:41 +0100676 i::Isolate::Current()->compilation_cache()->Clear();
677 HEAP->CollectAllGarbage(false);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100678 CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls);
679 CHECK_EQ(0, TestAsciiResource::dispose_count);
680
681 // Use a heap allocated external string resource allocated object.
682 TestAsciiResource::dispose_count = 0;
683 TestAsciiResourceWithDisposeControl::dispose_calls = 0;
684 TestAsciiResource* res_heap =
685 new TestAsciiResourceWithDisposeControl(i::StrDup(c_source), true);
686 {
687 v8::HandleScope scope;
688 LocalContext env;
689 Local<String> source = String::NewExternal(res_heap);
690 Local<Script> script = Script::Compile(source);
691 Local<Value> value = script->Run();
692 CHECK(value->IsNumber());
693 CHECK_EQ(7, value->Int32Value());
Steve Block44f0eee2011-05-26 01:26:41 +0100694 HEAP->CollectAllGarbage(false);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100695 CHECK_EQ(0, TestAsciiResource::dispose_count);
696 }
Steve Block44f0eee2011-05-26 01:26:41 +0100697 i::Isolate::Current()->compilation_cache()->Clear();
698 HEAP->CollectAllGarbage(false);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100699 CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls);
700 CHECK_EQ(1, TestAsciiResource::dispose_count);
701}
702
703
Steve Block3ce2e202009-11-05 08:53:23 +0000704THREADED_TEST(StringConcat) {
705 {
706 v8::HandleScope scope;
707 LocalContext env;
708 const char* one_byte_string_1 = "function a_times_t";
709 const char* two_byte_string_1 = "wo_plus_b(a, b) {return ";
710 const char* one_byte_extern_1 = "a * 2 + b;} a_times_two_plus_b(4, 8) + ";
711 const char* two_byte_extern_1 = "a_times_two_plus_b(4, 8) + ";
712 const char* one_byte_string_2 = "a_times_two_plus_b(4, 8) + ";
713 const char* two_byte_string_2 = "a_times_two_plus_b(4, 8) + ";
714 const char* two_byte_extern_2 = "a_times_two_plus_b(1, 2);";
715 Local<String> left = v8_str(one_byte_string_1);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100716
717 uint16_t* two_byte_source = AsciiToTwoByteString(two_byte_string_1);
718 Local<String> right = String::New(two_byte_source);
719 i::DeleteArray(two_byte_source);
720
Steve Block3ce2e202009-11-05 08:53:23 +0000721 Local<String> source = String::Concat(left, right);
722 right = String::NewExternal(
723 new TestAsciiResource(i::StrDup(one_byte_extern_1)));
724 source = String::Concat(source, right);
725 right = String::NewExternal(
726 new TestResource(AsciiToTwoByteString(two_byte_extern_1)));
727 source = String::Concat(source, right);
728 right = v8_str(one_byte_string_2);
729 source = String::Concat(source, right);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100730
731 two_byte_source = AsciiToTwoByteString(two_byte_string_2);
732 right = String::New(two_byte_source);
733 i::DeleteArray(two_byte_source);
734
Steve Block3ce2e202009-11-05 08:53:23 +0000735 source = String::Concat(source, right);
736 right = String::NewExternal(
737 new TestResource(AsciiToTwoByteString(two_byte_extern_2)));
738 source = String::Concat(source, right);
739 Local<Script> script = Script::Compile(source);
740 Local<Value> value = script->Run();
741 CHECK(value->IsNumber());
742 CHECK_EQ(68, value->Int32Value());
743 }
Steve Block44f0eee2011-05-26 01:26:41 +0100744 i::Isolate::Current()->compilation_cache()->Clear();
745 HEAP->CollectAllGarbage(false);
746 HEAP->CollectAllGarbage(false);
Steve Block3ce2e202009-11-05 08:53:23 +0000747}
748
749
Steve Blocka7e24c12009-10-30 11:49:00 +0000750THREADED_TEST(GlobalProperties) {
751 v8::HandleScope scope;
752 LocalContext env;
753 v8::Handle<v8::Object> global = env->Global();
754 global->Set(v8_str("pi"), v8_num(3.1415926));
755 Local<Value> pi = global->Get(v8_str("pi"));
756 CHECK_EQ(3.1415926, pi->NumberValue());
757}
758
759
760static v8::Handle<Value> handle_call(const v8::Arguments& args) {
761 ApiTestFuzzer::Fuzz();
762 return v8_num(102);
763}
764
765
766static v8::Handle<Value> construct_call(const v8::Arguments& args) {
767 ApiTestFuzzer::Fuzz();
768 args.This()->Set(v8_str("x"), v8_num(1));
769 args.This()->Set(v8_str("y"), v8_num(2));
770 return args.This();
771}
772
Ben Murdochf87a2032010-10-22 12:50:53 +0100773static v8::Handle<Value> Return239(Local<String> name, const AccessorInfo&) {
774 ApiTestFuzzer::Fuzz();
775 return v8_num(239);
776}
777
778
Steve Blocka7e24c12009-10-30 11:49:00 +0000779THREADED_TEST(FunctionTemplate) {
780 v8::HandleScope scope;
781 LocalContext env;
782 {
783 Local<v8::FunctionTemplate> fun_templ =
784 v8::FunctionTemplate::New(handle_call);
785 Local<Function> fun = fun_templ->GetFunction();
786 env->Global()->Set(v8_str("obj"), fun);
787 Local<Script> script = v8_compile("obj()");
788 CHECK_EQ(102, script->Run()->Int32Value());
789 }
790 // Use SetCallHandler to initialize a function template, should work like the
791 // previous one.
792 {
793 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
794 fun_templ->SetCallHandler(handle_call);
795 Local<Function> fun = fun_templ->GetFunction();
796 env->Global()->Set(v8_str("obj"), fun);
797 Local<Script> script = v8_compile("obj()");
798 CHECK_EQ(102, script->Run()->Int32Value());
799 }
800 // Test constructor calls.
801 {
802 Local<v8::FunctionTemplate> fun_templ =
803 v8::FunctionTemplate::New(construct_call);
804 fun_templ->SetClassName(v8_str("funky"));
Ben Murdochf87a2032010-10-22 12:50:53 +0100805 fun_templ->InstanceTemplate()->SetAccessor(v8_str("m"), Return239);
Steve Blocka7e24c12009-10-30 11:49:00 +0000806 Local<Function> fun = fun_templ->GetFunction();
807 env->Global()->Set(v8_str("obj"), fun);
808 Local<Script> script = v8_compile("var s = new obj(); s.x");
809 CHECK_EQ(1, script->Run()->Int32Value());
810
811 Local<Value> result = v8_compile("(new obj()).toString()")->Run();
812 CHECK_EQ(v8_str("[object funky]"), result);
Ben Murdochf87a2032010-10-22 12:50:53 +0100813
814 result = v8_compile("(new obj()).m")->Run();
815 CHECK_EQ(239, result->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +0000816 }
817}
818
819
Ben Murdochb8e0da22011-05-16 14:20:40 +0100820static void* expected_ptr;
821static v8::Handle<v8::Value> callback(const v8::Arguments& args) {
822 void* ptr = v8::External::Unwrap(args.Data());
823 CHECK_EQ(expected_ptr, ptr);
824 return v8::Boolean::New(true);
825}
826
827
828static void TestExternalPointerWrapping() {
829 v8::HandleScope scope;
830 LocalContext env;
831
832 v8::Handle<v8::Value> data = v8::External::Wrap(expected_ptr);
833
834 v8::Handle<v8::Object> obj = v8::Object::New();
835 obj->Set(v8_str("func"),
836 v8::FunctionTemplate::New(callback, data)->GetFunction());
837 env->Global()->Set(v8_str("obj"), obj);
838
839 CHECK(CompileRun(
840 "function foo() {\n"
841 " for (var i = 0; i < 13; i++) obj.func();\n"
842 "}\n"
843 "foo(), true")->BooleanValue());
844}
845
846
847THREADED_TEST(ExternalWrap) {
848 // Check heap allocated object.
849 int* ptr = new int;
850 expected_ptr = ptr;
851 TestExternalPointerWrapping();
852 delete ptr;
853
854 // Check stack allocated object.
855 int foo;
856 expected_ptr = &foo;
857 TestExternalPointerWrapping();
858
859 // Check not aligned addresses.
860 const int n = 100;
861 char* s = new char[n];
862 for (int i = 0; i < n; i++) {
863 expected_ptr = s + i;
864 TestExternalPointerWrapping();
865 }
866
867 delete[] s;
868
869 // Check several invalid addresses.
870 expected_ptr = reinterpret_cast<void*>(1);
871 TestExternalPointerWrapping();
872
873 expected_ptr = reinterpret_cast<void*>(0xdeadbeef);
874 TestExternalPointerWrapping();
875
876 expected_ptr = reinterpret_cast<void*>(0xdeadbeef + 1);
877 TestExternalPointerWrapping();
878
879#if defined(V8_HOST_ARCH_X64)
Steve Block1e0659c2011-05-24 12:43:12 +0100880 // Check a value with a leading 1 bit in x64 Smi encoding.
881 expected_ptr = reinterpret_cast<void*>(0x400000000);
882 TestExternalPointerWrapping();
883
Ben Murdochb8e0da22011-05-16 14:20:40 +0100884 expected_ptr = reinterpret_cast<void*>(0xdeadbeefdeadbeef);
885 TestExternalPointerWrapping();
886
887 expected_ptr = reinterpret_cast<void*>(0xdeadbeefdeadbeef + 1);
888 TestExternalPointerWrapping();
889#endif
890}
891
892
Steve Blocka7e24c12009-10-30 11:49:00 +0000893THREADED_TEST(FindInstanceInPrototypeChain) {
894 v8::HandleScope scope;
895 LocalContext env;
896
897 Local<v8::FunctionTemplate> base = v8::FunctionTemplate::New();
898 Local<v8::FunctionTemplate> derived = v8::FunctionTemplate::New();
899 Local<v8::FunctionTemplate> other = v8::FunctionTemplate::New();
900 derived->Inherit(base);
901
902 Local<v8::Function> base_function = base->GetFunction();
903 Local<v8::Function> derived_function = derived->GetFunction();
904 Local<v8::Function> other_function = other->GetFunction();
905
906 Local<v8::Object> base_instance = base_function->NewInstance();
907 Local<v8::Object> derived_instance = derived_function->NewInstance();
908 Local<v8::Object> derived_instance2 = derived_function->NewInstance();
909 Local<v8::Object> other_instance = other_function->NewInstance();
910 derived_instance2->Set(v8_str("__proto__"), derived_instance);
911 other_instance->Set(v8_str("__proto__"), derived_instance2);
912
913 // base_instance is only an instance of base.
914 CHECK_EQ(base_instance,
915 base_instance->FindInstanceInPrototypeChain(base));
916 CHECK(base_instance->FindInstanceInPrototypeChain(derived).IsEmpty());
917 CHECK(base_instance->FindInstanceInPrototypeChain(other).IsEmpty());
918
919 // derived_instance is an instance of base and derived.
920 CHECK_EQ(derived_instance,
921 derived_instance->FindInstanceInPrototypeChain(base));
922 CHECK_EQ(derived_instance,
923 derived_instance->FindInstanceInPrototypeChain(derived));
924 CHECK(derived_instance->FindInstanceInPrototypeChain(other).IsEmpty());
925
926 // other_instance is an instance of other and its immediate
927 // prototype derived_instance2 is an instance of base and derived.
928 // Note, derived_instance is an instance of base and derived too,
929 // but it comes after derived_instance2 in the prototype chain of
930 // other_instance.
931 CHECK_EQ(derived_instance2,
932 other_instance->FindInstanceInPrototypeChain(base));
933 CHECK_EQ(derived_instance2,
934 other_instance->FindInstanceInPrototypeChain(derived));
935 CHECK_EQ(other_instance,
936 other_instance->FindInstanceInPrototypeChain(other));
937}
938
939
Steve Block3ce2e202009-11-05 08:53:23 +0000940THREADED_TEST(TinyInteger) {
941 v8::HandleScope scope;
942 LocalContext env;
943 int32_t value = 239;
944 Local<v8::Integer> value_obj = v8::Integer::New(value);
945 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
946}
947
948
949THREADED_TEST(BigSmiInteger) {
950 v8::HandleScope scope;
951 LocalContext env;
952 int32_t value = i::Smi::kMaxValue;
953 // We cannot add one to a Smi::kMaxValue without wrapping.
954 if (i::kSmiValueSize < 32) {
955 CHECK(i::Smi::IsValid(value));
956 CHECK(!i::Smi::IsValid(value + 1));
957 Local<v8::Integer> value_obj = v8::Integer::New(value);
958 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
959 }
960}
961
962
963THREADED_TEST(BigInteger) {
964 v8::HandleScope scope;
965 LocalContext env;
966 // We cannot add one to a Smi::kMaxValue without wrapping.
967 if (i::kSmiValueSize < 32) {
968 // The casts allow this to compile, even if Smi::kMaxValue is 2^31-1.
969 // The code will not be run in that case, due to the "if" guard.
970 int32_t value =
971 static_cast<int32_t>(static_cast<uint32_t>(i::Smi::kMaxValue) + 1);
972 CHECK(value > i::Smi::kMaxValue);
973 CHECK(!i::Smi::IsValid(value));
974 Local<v8::Integer> value_obj = v8::Integer::New(value);
975 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
976 }
977}
978
979
980THREADED_TEST(TinyUnsignedInteger) {
981 v8::HandleScope scope;
982 LocalContext env;
983 uint32_t value = 239;
984 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
985 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
986}
987
988
989THREADED_TEST(BigUnsignedSmiInteger) {
990 v8::HandleScope scope;
991 LocalContext env;
992 uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue);
993 CHECK(i::Smi::IsValid(value));
994 CHECK(!i::Smi::IsValid(value + 1));
995 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
996 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
997}
998
999
1000THREADED_TEST(BigUnsignedInteger) {
1001 v8::HandleScope scope;
1002 LocalContext env;
1003 uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue) + 1;
1004 CHECK(value > static_cast<uint32_t>(i::Smi::kMaxValue));
1005 CHECK(!i::Smi::IsValid(value));
1006 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
1007 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
1008}
1009
1010
1011THREADED_TEST(OutOfSignedRangeUnsignedInteger) {
1012 v8::HandleScope scope;
1013 LocalContext env;
1014 uint32_t INT32_MAX_AS_UINT = (1U << 31) - 1;
1015 uint32_t value = INT32_MAX_AS_UINT + 1;
1016 CHECK(value > INT32_MAX_AS_UINT); // No overflow.
1017 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
1018 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
1019}
1020
1021
Steve Blocka7e24c12009-10-30 11:49:00 +00001022THREADED_TEST(Number) {
1023 v8::HandleScope scope;
1024 LocalContext env;
1025 double PI = 3.1415926;
1026 Local<v8::Number> pi_obj = v8::Number::New(PI);
1027 CHECK_EQ(PI, pi_obj->NumberValue());
1028}
1029
1030
1031THREADED_TEST(ToNumber) {
1032 v8::HandleScope scope;
1033 LocalContext env;
1034 Local<String> str = v8_str("3.1415926");
1035 CHECK_EQ(3.1415926, str->NumberValue());
1036 v8::Handle<v8::Boolean> t = v8::True();
1037 CHECK_EQ(1.0, t->NumberValue());
1038 v8::Handle<v8::Boolean> f = v8::False();
1039 CHECK_EQ(0.0, f->NumberValue());
1040}
1041
1042
1043THREADED_TEST(Date) {
1044 v8::HandleScope scope;
1045 LocalContext env;
1046 double PI = 3.1415926;
1047 Local<Value> date_obj = v8::Date::New(PI);
1048 CHECK_EQ(3.0, date_obj->NumberValue());
1049}
1050
1051
1052THREADED_TEST(Boolean) {
1053 v8::HandleScope scope;
1054 LocalContext env;
1055 v8::Handle<v8::Boolean> t = v8::True();
1056 CHECK(t->Value());
1057 v8::Handle<v8::Boolean> f = v8::False();
1058 CHECK(!f->Value());
1059 v8::Handle<v8::Primitive> u = v8::Undefined();
1060 CHECK(!u->BooleanValue());
1061 v8::Handle<v8::Primitive> n = v8::Null();
1062 CHECK(!n->BooleanValue());
1063 v8::Handle<String> str1 = v8_str("");
1064 CHECK(!str1->BooleanValue());
1065 v8::Handle<String> str2 = v8_str("x");
1066 CHECK(str2->BooleanValue());
1067 CHECK(!v8::Number::New(0)->BooleanValue());
1068 CHECK(v8::Number::New(-1)->BooleanValue());
1069 CHECK(v8::Number::New(1)->BooleanValue());
1070 CHECK(v8::Number::New(42)->BooleanValue());
1071 CHECK(!v8_compile("NaN")->Run()->BooleanValue());
1072}
1073
1074
1075static v8::Handle<Value> DummyCallHandler(const v8::Arguments& args) {
1076 ApiTestFuzzer::Fuzz();
1077 return v8_num(13.4);
1078}
1079
1080
1081static v8::Handle<Value> GetM(Local<String> name, const AccessorInfo&) {
1082 ApiTestFuzzer::Fuzz();
1083 return v8_num(876);
1084}
1085
1086
1087THREADED_TEST(GlobalPrototype) {
1088 v8::HandleScope scope;
1089 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New();
1090 func_templ->PrototypeTemplate()->Set(
1091 "dummy",
1092 v8::FunctionTemplate::New(DummyCallHandler));
1093 v8::Handle<ObjectTemplate> templ = func_templ->InstanceTemplate();
1094 templ->Set("x", v8_num(200));
1095 templ->SetAccessor(v8_str("m"), GetM);
1096 LocalContext env(0, templ);
1097 v8::Handle<v8::Object> obj = env->Global();
1098 v8::Handle<Script> script = v8_compile("dummy()");
1099 v8::Handle<Value> result = script->Run();
1100 CHECK_EQ(13.4, result->NumberValue());
1101 CHECK_EQ(200, v8_compile("x")->Run()->Int32Value());
1102 CHECK_EQ(876, v8_compile("m")->Run()->Int32Value());
1103}
1104
1105
Steve Blocka7e24c12009-10-30 11:49:00 +00001106THREADED_TEST(ObjectTemplate) {
1107 v8::HandleScope scope;
1108 Local<ObjectTemplate> templ1 = ObjectTemplate::New();
1109 templ1->Set("x", v8_num(10));
1110 templ1->Set("y", v8_num(13));
1111 LocalContext env;
1112 Local<v8::Object> instance1 = templ1->NewInstance();
1113 env->Global()->Set(v8_str("p"), instance1);
1114 CHECK(v8_compile("(p.x == 10)")->Run()->BooleanValue());
1115 CHECK(v8_compile("(p.y == 13)")->Run()->BooleanValue());
1116 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New();
1117 fun->PrototypeTemplate()->Set("nirk", v8_num(123));
1118 Local<ObjectTemplate> templ2 = fun->InstanceTemplate();
1119 templ2->Set("a", v8_num(12));
1120 templ2->Set("b", templ1);
1121 Local<v8::Object> instance2 = templ2->NewInstance();
1122 env->Global()->Set(v8_str("q"), instance2);
1123 CHECK(v8_compile("(q.nirk == 123)")->Run()->BooleanValue());
1124 CHECK(v8_compile("(q.a == 12)")->Run()->BooleanValue());
1125 CHECK(v8_compile("(q.b.x == 10)")->Run()->BooleanValue());
1126 CHECK(v8_compile("(q.b.y == 13)")->Run()->BooleanValue());
1127}
1128
1129
1130static v8::Handle<Value> GetFlabby(const v8::Arguments& args) {
1131 ApiTestFuzzer::Fuzz();
1132 return v8_num(17.2);
1133}
1134
1135
1136static v8::Handle<Value> GetKnurd(Local<String> property, const AccessorInfo&) {
1137 ApiTestFuzzer::Fuzz();
1138 return v8_num(15.2);
1139}
1140
1141
1142THREADED_TEST(DescriptorInheritance) {
1143 v8::HandleScope scope;
1144 v8::Handle<v8::FunctionTemplate> super = v8::FunctionTemplate::New();
1145 super->PrototypeTemplate()->Set("flabby",
1146 v8::FunctionTemplate::New(GetFlabby));
1147 super->PrototypeTemplate()->Set("PI", v8_num(3.14));
1148
1149 super->InstanceTemplate()->SetAccessor(v8_str("knurd"), GetKnurd);
1150
1151 v8::Handle<v8::FunctionTemplate> base1 = v8::FunctionTemplate::New();
1152 base1->Inherit(super);
1153 base1->PrototypeTemplate()->Set("v1", v8_num(20.1));
1154
1155 v8::Handle<v8::FunctionTemplate> base2 = v8::FunctionTemplate::New();
1156 base2->Inherit(super);
1157 base2->PrototypeTemplate()->Set("v2", v8_num(10.1));
1158
1159 LocalContext env;
1160
1161 env->Global()->Set(v8_str("s"), super->GetFunction());
1162 env->Global()->Set(v8_str("base1"), base1->GetFunction());
1163 env->Global()->Set(v8_str("base2"), base2->GetFunction());
1164
1165 // Checks right __proto__ chain.
1166 CHECK(CompileRun("base1.prototype.__proto__ == s.prototype")->BooleanValue());
1167 CHECK(CompileRun("base2.prototype.__proto__ == s.prototype")->BooleanValue());
1168
1169 CHECK(v8_compile("s.prototype.PI == 3.14")->Run()->BooleanValue());
1170
1171 // Instance accessor should not be visible on function object or its prototype
1172 CHECK(CompileRun("s.knurd == undefined")->BooleanValue());
1173 CHECK(CompileRun("s.prototype.knurd == undefined")->BooleanValue());
1174 CHECK(CompileRun("base1.prototype.knurd == undefined")->BooleanValue());
1175
1176 env->Global()->Set(v8_str("obj"),
1177 base1->GetFunction()->NewInstance());
1178 CHECK_EQ(17.2, v8_compile("obj.flabby()")->Run()->NumberValue());
1179 CHECK(v8_compile("'flabby' in obj")->Run()->BooleanValue());
1180 CHECK_EQ(15.2, v8_compile("obj.knurd")->Run()->NumberValue());
1181 CHECK(v8_compile("'knurd' in obj")->Run()->BooleanValue());
1182 CHECK_EQ(20.1, v8_compile("obj.v1")->Run()->NumberValue());
1183
1184 env->Global()->Set(v8_str("obj2"),
1185 base2->GetFunction()->NewInstance());
1186 CHECK_EQ(17.2, v8_compile("obj2.flabby()")->Run()->NumberValue());
1187 CHECK(v8_compile("'flabby' in obj2")->Run()->BooleanValue());
1188 CHECK_EQ(15.2, v8_compile("obj2.knurd")->Run()->NumberValue());
1189 CHECK(v8_compile("'knurd' in obj2")->Run()->BooleanValue());
1190 CHECK_EQ(10.1, v8_compile("obj2.v2")->Run()->NumberValue());
1191
1192 // base1 and base2 cannot cross reference to each's prototype
1193 CHECK(v8_compile("obj.v2")->Run()->IsUndefined());
1194 CHECK(v8_compile("obj2.v1")->Run()->IsUndefined());
1195}
1196
1197
1198int echo_named_call_count;
1199
1200
1201static v8::Handle<Value> EchoNamedProperty(Local<String> name,
1202 const AccessorInfo& info) {
1203 ApiTestFuzzer::Fuzz();
1204 CHECK_EQ(v8_str("data"), info.Data());
1205 echo_named_call_count++;
1206 return name;
1207}
1208
1209
1210THREADED_TEST(NamedPropertyHandlerGetter) {
1211 echo_named_call_count = 0;
1212 v8::HandleScope scope;
1213 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1214 templ->InstanceTemplate()->SetNamedPropertyHandler(EchoNamedProperty,
1215 0, 0, 0, 0,
1216 v8_str("data"));
1217 LocalContext env;
1218 env->Global()->Set(v8_str("obj"),
1219 templ->GetFunction()->NewInstance());
1220 CHECK_EQ(echo_named_call_count, 0);
1221 v8_compile("obj.x")->Run();
1222 CHECK_EQ(echo_named_call_count, 1);
1223 const char* code = "var str = 'oddle'; obj[str] + obj.poddle;";
1224 v8::Handle<Value> str = CompileRun(code);
1225 String::AsciiValue value(str);
1226 CHECK_EQ(*value, "oddlepoddle");
1227 // Check default behavior
1228 CHECK_EQ(v8_compile("obj.flob = 10;")->Run()->Int32Value(), 10);
1229 CHECK(v8_compile("'myProperty' in obj")->Run()->BooleanValue());
1230 CHECK(v8_compile("delete obj.myProperty")->Run()->BooleanValue());
1231}
1232
1233
1234int echo_indexed_call_count = 0;
1235
1236
1237static v8::Handle<Value> EchoIndexedProperty(uint32_t index,
1238 const AccessorInfo& info) {
1239 ApiTestFuzzer::Fuzz();
1240 CHECK_EQ(v8_num(637), info.Data());
1241 echo_indexed_call_count++;
1242 return v8_num(index);
1243}
1244
1245
1246THREADED_TEST(IndexedPropertyHandlerGetter) {
1247 v8::HandleScope scope;
1248 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1249 templ->InstanceTemplate()->SetIndexedPropertyHandler(EchoIndexedProperty,
1250 0, 0, 0, 0,
1251 v8_num(637));
1252 LocalContext env;
1253 env->Global()->Set(v8_str("obj"),
1254 templ->GetFunction()->NewInstance());
1255 Local<Script> script = v8_compile("obj[900]");
1256 CHECK_EQ(script->Run()->Int32Value(), 900);
1257}
1258
1259
1260v8::Handle<v8::Object> bottom;
1261
1262static v8::Handle<Value> CheckThisIndexedPropertyHandler(
1263 uint32_t index,
1264 const AccessorInfo& info) {
1265 ApiTestFuzzer::Fuzz();
1266 CHECK(info.This()->Equals(bottom));
1267 return v8::Handle<Value>();
1268}
1269
1270static v8::Handle<Value> CheckThisNamedPropertyHandler(
1271 Local<String> name,
1272 const AccessorInfo& info) {
1273 ApiTestFuzzer::Fuzz();
1274 CHECK(info.This()->Equals(bottom));
1275 return v8::Handle<Value>();
1276}
1277
1278
1279v8::Handle<Value> CheckThisIndexedPropertySetter(uint32_t index,
1280 Local<Value> value,
1281 const AccessorInfo& info) {
1282 ApiTestFuzzer::Fuzz();
1283 CHECK(info.This()->Equals(bottom));
1284 return v8::Handle<Value>();
1285}
1286
1287
1288v8::Handle<Value> CheckThisNamedPropertySetter(Local<String> property,
1289 Local<Value> value,
1290 const AccessorInfo& info) {
1291 ApiTestFuzzer::Fuzz();
1292 CHECK(info.This()->Equals(bottom));
1293 return v8::Handle<Value>();
1294}
1295
Iain Merrick75681382010-08-19 15:07:18 +01001296v8::Handle<v8::Integer> CheckThisIndexedPropertyQuery(
Steve Blocka7e24c12009-10-30 11:49:00 +00001297 uint32_t index,
1298 const AccessorInfo& info) {
1299 ApiTestFuzzer::Fuzz();
1300 CHECK(info.This()->Equals(bottom));
Iain Merrick75681382010-08-19 15:07:18 +01001301 return v8::Handle<v8::Integer>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001302}
1303
1304
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001305v8::Handle<v8::Integer> CheckThisNamedPropertyQuery(Local<String> property,
Steve Blocka7e24c12009-10-30 11:49:00 +00001306 const AccessorInfo& info) {
1307 ApiTestFuzzer::Fuzz();
1308 CHECK(info.This()->Equals(bottom));
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001309 return v8::Handle<v8::Integer>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001310}
1311
1312
1313v8::Handle<v8::Boolean> CheckThisIndexedPropertyDeleter(
1314 uint32_t index,
1315 const AccessorInfo& info) {
1316 ApiTestFuzzer::Fuzz();
1317 CHECK(info.This()->Equals(bottom));
1318 return v8::Handle<v8::Boolean>();
1319}
1320
1321
1322v8::Handle<v8::Boolean> CheckThisNamedPropertyDeleter(
1323 Local<String> property,
1324 const AccessorInfo& info) {
1325 ApiTestFuzzer::Fuzz();
1326 CHECK(info.This()->Equals(bottom));
1327 return v8::Handle<v8::Boolean>();
1328}
1329
1330
1331v8::Handle<v8::Array> CheckThisIndexedPropertyEnumerator(
1332 const AccessorInfo& info) {
1333 ApiTestFuzzer::Fuzz();
1334 CHECK(info.This()->Equals(bottom));
1335 return v8::Handle<v8::Array>();
1336}
1337
1338
1339v8::Handle<v8::Array> CheckThisNamedPropertyEnumerator(
1340 const AccessorInfo& info) {
1341 ApiTestFuzzer::Fuzz();
1342 CHECK(info.This()->Equals(bottom));
1343 return v8::Handle<v8::Array>();
1344}
1345
1346
1347THREADED_TEST(PropertyHandlerInPrototype) {
1348 v8::HandleScope scope;
1349 LocalContext env;
1350
1351 // Set up a prototype chain with three interceptors.
1352 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1353 templ->InstanceTemplate()->SetIndexedPropertyHandler(
1354 CheckThisIndexedPropertyHandler,
1355 CheckThisIndexedPropertySetter,
1356 CheckThisIndexedPropertyQuery,
1357 CheckThisIndexedPropertyDeleter,
1358 CheckThisIndexedPropertyEnumerator);
1359
1360 templ->InstanceTemplate()->SetNamedPropertyHandler(
1361 CheckThisNamedPropertyHandler,
1362 CheckThisNamedPropertySetter,
1363 CheckThisNamedPropertyQuery,
1364 CheckThisNamedPropertyDeleter,
1365 CheckThisNamedPropertyEnumerator);
1366
1367 bottom = templ->GetFunction()->NewInstance();
1368 Local<v8::Object> top = templ->GetFunction()->NewInstance();
1369 Local<v8::Object> middle = templ->GetFunction()->NewInstance();
1370
1371 bottom->Set(v8_str("__proto__"), middle);
1372 middle->Set(v8_str("__proto__"), top);
1373 env->Global()->Set(v8_str("obj"), bottom);
1374
1375 // Indexed and named get.
1376 Script::Compile(v8_str("obj[0]"))->Run();
1377 Script::Compile(v8_str("obj.x"))->Run();
1378
1379 // Indexed and named set.
1380 Script::Compile(v8_str("obj[1] = 42"))->Run();
1381 Script::Compile(v8_str("obj.y = 42"))->Run();
1382
1383 // Indexed and named query.
1384 Script::Compile(v8_str("0 in obj"))->Run();
1385 Script::Compile(v8_str("'x' in obj"))->Run();
1386
1387 // Indexed and named deleter.
1388 Script::Compile(v8_str("delete obj[0]"))->Run();
1389 Script::Compile(v8_str("delete obj.x"))->Run();
1390
1391 // Enumerators.
1392 Script::Compile(v8_str("for (var p in obj) ;"))->Run();
1393}
1394
1395
1396static v8::Handle<Value> PrePropertyHandlerGet(Local<String> key,
1397 const AccessorInfo& info) {
1398 ApiTestFuzzer::Fuzz();
1399 if (v8_str("pre")->Equals(key)) {
1400 return v8_str("PrePropertyHandler: pre");
1401 }
1402 return v8::Handle<String>();
1403}
1404
1405
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001406static v8::Handle<v8::Integer> PrePropertyHandlerQuery(Local<String> key,
1407 const AccessorInfo&) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001408 if (v8_str("pre")->Equals(key)) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001409 return v8::Integer::New(v8::None);
Steve Blocka7e24c12009-10-30 11:49:00 +00001410 }
1411
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001412 return v8::Handle<v8::Integer>(); // do not intercept the call
Steve Blocka7e24c12009-10-30 11:49:00 +00001413}
1414
1415
1416THREADED_TEST(PrePropertyHandler) {
1417 v8::HandleScope scope;
1418 v8::Handle<v8::FunctionTemplate> desc = v8::FunctionTemplate::New();
1419 desc->InstanceTemplate()->SetNamedPropertyHandler(PrePropertyHandlerGet,
1420 0,
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001421 PrePropertyHandlerQuery);
Steve Blocka7e24c12009-10-30 11:49:00 +00001422 LocalContext env(NULL, desc->InstanceTemplate());
1423 Script::Compile(v8_str(
1424 "var pre = 'Object: pre'; var on = 'Object: on';"))->Run();
1425 v8::Handle<Value> result_pre = Script::Compile(v8_str("pre"))->Run();
1426 CHECK_EQ(v8_str("PrePropertyHandler: pre"), result_pre);
1427 v8::Handle<Value> result_on = Script::Compile(v8_str("on"))->Run();
1428 CHECK_EQ(v8_str("Object: on"), result_on);
1429 v8::Handle<Value> result_post = Script::Compile(v8_str("post"))->Run();
1430 CHECK(result_post.IsEmpty());
1431}
1432
1433
1434THREADED_TEST(UndefinedIsNotEnumerable) {
1435 v8::HandleScope scope;
1436 LocalContext env;
1437 v8::Handle<Value> result = Script::Compile(v8_str(
1438 "this.propertyIsEnumerable(undefined)"))->Run();
1439 CHECK(result->IsFalse());
1440}
1441
1442
1443v8::Handle<Script> call_recursively_script;
Leon Clarke4515c472010-02-03 11:58:03 +00001444static const int kTargetRecursionDepth = 200; // near maximum
Steve Blocka7e24c12009-10-30 11:49:00 +00001445
1446
1447static v8::Handle<Value> CallScriptRecursivelyCall(const v8::Arguments& args) {
1448 ApiTestFuzzer::Fuzz();
1449 int depth = args.This()->Get(v8_str("depth"))->Int32Value();
1450 if (depth == kTargetRecursionDepth) return v8::Undefined();
1451 args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1));
1452 return call_recursively_script->Run();
1453}
1454
1455
1456static v8::Handle<Value> CallFunctionRecursivelyCall(
1457 const v8::Arguments& args) {
1458 ApiTestFuzzer::Fuzz();
1459 int depth = args.This()->Get(v8_str("depth"))->Int32Value();
1460 if (depth == kTargetRecursionDepth) {
1461 printf("[depth = %d]\n", depth);
1462 return v8::Undefined();
1463 }
1464 args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1));
1465 v8::Handle<Value> function =
1466 args.This()->Get(v8_str("callFunctionRecursively"));
Steve Block6ded16b2010-05-10 14:33:55 +01001467 return function.As<Function>()->Call(args.This(), 0, NULL);
Steve Blocka7e24c12009-10-30 11:49:00 +00001468}
1469
1470
1471THREADED_TEST(DeepCrossLanguageRecursion) {
1472 v8::HandleScope scope;
1473 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New();
1474 global->Set(v8_str("callScriptRecursively"),
1475 v8::FunctionTemplate::New(CallScriptRecursivelyCall));
1476 global->Set(v8_str("callFunctionRecursively"),
1477 v8::FunctionTemplate::New(CallFunctionRecursivelyCall));
1478 LocalContext env(NULL, global);
1479
1480 env->Global()->Set(v8_str("depth"), v8::Integer::New(0));
1481 call_recursively_script = v8_compile("callScriptRecursively()");
1482 v8::Handle<Value> result = call_recursively_script->Run();
1483 call_recursively_script = v8::Handle<Script>();
1484
1485 env->Global()->Set(v8_str("depth"), v8::Integer::New(0));
1486 Script::Compile(v8_str("callFunctionRecursively()"))->Run();
1487}
1488
1489
1490static v8::Handle<Value>
1491 ThrowingPropertyHandlerGet(Local<String> key, const AccessorInfo&) {
1492 ApiTestFuzzer::Fuzz();
1493 return v8::ThrowException(key);
1494}
1495
1496
1497static v8::Handle<Value> ThrowingPropertyHandlerSet(Local<String> key,
1498 Local<Value>,
1499 const AccessorInfo&) {
1500 v8::ThrowException(key);
1501 return v8::Undefined(); // not the same as v8::Handle<v8::Value>()
1502}
1503
1504
1505THREADED_TEST(CallbackExceptionRegression) {
1506 v8::HandleScope scope;
1507 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
1508 obj->SetNamedPropertyHandler(ThrowingPropertyHandlerGet,
1509 ThrowingPropertyHandlerSet);
1510 LocalContext env;
1511 env->Global()->Set(v8_str("obj"), obj->NewInstance());
1512 v8::Handle<Value> otto = Script::Compile(v8_str(
1513 "try { with (obj) { otto; } } catch (e) { e; }"))->Run();
1514 CHECK_EQ(v8_str("otto"), otto);
1515 v8::Handle<Value> netto = Script::Compile(v8_str(
1516 "try { with (obj) { netto = 4; } } catch (e) { e; }"))->Run();
1517 CHECK_EQ(v8_str("netto"), netto);
1518}
1519
1520
Steve Blocka7e24c12009-10-30 11:49:00 +00001521THREADED_TEST(FunctionPrototype) {
1522 v8::HandleScope scope;
1523 Local<v8::FunctionTemplate> Foo = v8::FunctionTemplate::New();
1524 Foo->PrototypeTemplate()->Set(v8_str("plak"), v8_num(321));
1525 LocalContext env;
1526 env->Global()->Set(v8_str("Foo"), Foo->GetFunction());
1527 Local<Script> script = Script::Compile(v8_str("Foo.prototype.plak"));
1528 CHECK_EQ(script->Run()->Int32Value(), 321);
1529}
1530
1531
1532THREADED_TEST(InternalFields) {
1533 v8::HandleScope scope;
1534 LocalContext env;
1535
1536 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1537 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate();
1538 instance_templ->SetInternalFieldCount(1);
1539 Local<v8::Object> obj = templ->GetFunction()->NewInstance();
1540 CHECK_EQ(1, obj->InternalFieldCount());
1541 CHECK(obj->GetInternalField(0)->IsUndefined());
1542 obj->SetInternalField(0, v8_num(17));
1543 CHECK_EQ(17, obj->GetInternalField(0)->Int32Value());
1544}
1545
1546
Steve Block6ded16b2010-05-10 14:33:55 +01001547THREADED_TEST(GlobalObjectInternalFields) {
1548 v8::HandleScope scope;
1549 Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
1550 global_template->SetInternalFieldCount(1);
1551 LocalContext env(NULL, global_template);
1552 v8::Handle<v8::Object> global_proxy = env->Global();
1553 v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>();
1554 CHECK_EQ(1, global->InternalFieldCount());
1555 CHECK(global->GetInternalField(0)->IsUndefined());
1556 global->SetInternalField(0, v8_num(17));
1557 CHECK_EQ(17, global->GetInternalField(0)->Int32Value());
1558}
1559
1560
Steve Blocka7e24c12009-10-30 11:49:00 +00001561THREADED_TEST(InternalFieldsNativePointers) {
1562 v8::HandleScope scope;
1563 LocalContext env;
1564
1565 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1566 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate();
1567 instance_templ->SetInternalFieldCount(1);
1568 Local<v8::Object> obj = templ->GetFunction()->NewInstance();
1569 CHECK_EQ(1, obj->InternalFieldCount());
1570 CHECK(obj->GetPointerFromInternalField(0) == NULL);
1571
1572 char* data = new char[100];
1573
1574 void* aligned = data;
Ben Murdochf87a2032010-10-22 12:50:53 +01001575 CHECK_EQ(0, static_cast<int>(reinterpret_cast<uintptr_t>(aligned) & 0x1));
Steve Blocka7e24c12009-10-30 11:49:00 +00001576 void* unaligned = data + 1;
Ben Murdochf87a2032010-10-22 12:50:53 +01001577 CHECK_EQ(1, static_cast<int>(reinterpret_cast<uintptr_t>(unaligned) & 0x1));
Steve Blocka7e24c12009-10-30 11:49:00 +00001578
1579 // Check reading and writing aligned pointers.
1580 obj->SetPointerInInternalField(0, aligned);
Steve Block44f0eee2011-05-26 01:26:41 +01001581 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +00001582 CHECK_EQ(aligned, obj->GetPointerFromInternalField(0));
1583
1584 // Check reading and writing unaligned pointers.
1585 obj->SetPointerInInternalField(0, unaligned);
Steve Block44f0eee2011-05-26 01:26:41 +01001586 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +00001587 CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0));
1588
1589 delete[] data;
1590}
1591
1592
Steve Block3ce2e202009-11-05 08:53:23 +00001593THREADED_TEST(InternalFieldsNativePointersAndExternal) {
1594 v8::HandleScope scope;
1595 LocalContext env;
1596
1597 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1598 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate();
1599 instance_templ->SetInternalFieldCount(1);
1600 Local<v8::Object> obj = templ->GetFunction()->NewInstance();
1601 CHECK_EQ(1, obj->InternalFieldCount());
1602 CHECK(obj->GetPointerFromInternalField(0) == NULL);
1603
1604 char* data = new char[100];
1605
1606 void* aligned = data;
Ben Murdochf87a2032010-10-22 12:50:53 +01001607 CHECK_EQ(0, static_cast<int>(reinterpret_cast<uintptr_t>(aligned) & 0x1));
Steve Block3ce2e202009-11-05 08:53:23 +00001608 void* unaligned = data + 1;
Ben Murdochf87a2032010-10-22 12:50:53 +01001609 CHECK_EQ(1, static_cast<int>(reinterpret_cast<uintptr_t>(unaligned) & 0x1));
Steve Block3ce2e202009-11-05 08:53:23 +00001610
1611 obj->SetPointerInInternalField(0, aligned);
Steve Block44f0eee2011-05-26 01:26:41 +01001612 HEAP->CollectAllGarbage(false);
Steve Block3ce2e202009-11-05 08:53:23 +00001613 CHECK_EQ(aligned, v8::External::Unwrap(obj->GetInternalField(0)));
1614
1615 obj->SetPointerInInternalField(0, unaligned);
Steve Block44f0eee2011-05-26 01:26:41 +01001616 HEAP->CollectAllGarbage(false);
Steve Block3ce2e202009-11-05 08:53:23 +00001617 CHECK_EQ(unaligned, v8::External::Unwrap(obj->GetInternalField(0)));
1618
1619 obj->SetInternalField(0, v8::External::Wrap(aligned));
Steve Block44f0eee2011-05-26 01:26:41 +01001620 HEAP->CollectAllGarbage(false);
Steve Block3ce2e202009-11-05 08:53:23 +00001621 CHECK_EQ(aligned, obj->GetPointerFromInternalField(0));
1622
1623 obj->SetInternalField(0, v8::External::Wrap(unaligned));
Steve Block44f0eee2011-05-26 01:26:41 +01001624 HEAP->CollectAllGarbage(false);
Steve Block3ce2e202009-11-05 08:53:23 +00001625 CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0));
1626
1627 delete[] data;
1628}
1629
1630
Steve Blocka7e24c12009-10-30 11:49:00 +00001631THREADED_TEST(IdentityHash) {
1632 v8::HandleScope scope;
1633 LocalContext env;
1634
1635 // Ensure that the test starts with an fresh heap to test whether the hash
1636 // code is based on the address.
Steve Block44f0eee2011-05-26 01:26:41 +01001637 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +00001638 Local<v8::Object> obj = v8::Object::New();
1639 int hash = obj->GetIdentityHash();
1640 int hash1 = obj->GetIdentityHash();
1641 CHECK_EQ(hash, hash1);
1642 int hash2 = v8::Object::New()->GetIdentityHash();
1643 // Since the identity hash is essentially a random number two consecutive
1644 // objects should not be assigned the same hash code. If the test below fails
1645 // the random number generator should be evaluated.
1646 CHECK_NE(hash, hash2);
Steve Block44f0eee2011-05-26 01:26:41 +01001647 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +00001648 int hash3 = v8::Object::New()->GetIdentityHash();
1649 // Make sure that the identity hash is not based on the initial address of
1650 // the object alone. If the test below fails the random number generator
1651 // should be evaluated.
1652 CHECK_NE(hash, hash3);
1653 int hash4 = obj->GetIdentityHash();
1654 CHECK_EQ(hash, hash4);
Steve Block1e0659c2011-05-24 12:43:12 +01001655
1656 // Check identity hashes behaviour in the presence of JS accessors.
1657 // Put a getter for 'v8::IdentityHash' on the Object's prototype:
1658 {
1659 CompileRun("Object.prototype['v8::IdentityHash'] = 42;\n");
1660 Local<v8::Object> o1 = v8::Object::New();
1661 Local<v8::Object> o2 = v8::Object::New();
1662 CHECK_NE(o1->GetIdentityHash(), o2->GetIdentityHash());
1663 }
1664 {
1665 CompileRun(
1666 "function cnst() { return 42; };\n"
1667 "Object.prototype.__defineGetter__('v8::IdentityHash', cnst);\n");
1668 Local<v8::Object> o1 = v8::Object::New();
1669 Local<v8::Object> o2 = v8::Object::New();
1670 CHECK_NE(o1->GetIdentityHash(), o2->GetIdentityHash());
1671 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001672}
1673
1674
1675THREADED_TEST(HiddenProperties) {
1676 v8::HandleScope scope;
1677 LocalContext env;
1678
1679 v8::Local<v8::Object> obj = v8::Object::New();
1680 v8::Local<v8::String> key = v8_str("api-test::hidden-key");
1681 v8::Local<v8::String> empty = v8_str("");
1682 v8::Local<v8::String> prop_name = v8_str("prop_name");
1683
Steve Block44f0eee2011-05-26 01:26:41 +01001684 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +00001685
1686 // Make sure delete of a non-existent hidden value works
1687 CHECK(obj->DeleteHiddenValue(key));
1688
1689 CHECK(obj->SetHiddenValue(key, v8::Integer::New(1503)));
1690 CHECK_EQ(1503, obj->GetHiddenValue(key)->Int32Value());
1691 CHECK(obj->SetHiddenValue(key, v8::Integer::New(2002)));
1692 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1693
Steve Block44f0eee2011-05-26 01:26:41 +01001694 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +00001695
1696 // Make sure we do not find the hidden property.
1697 CHECK(!obj->Has(empty));
1698 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1699 CHECK(obj->Get(empty)->IsUndefined());
1700 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1701 CHECK(obj->Set(empty, v8::Integer::New(2003)));
1702 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1703 CHECK_EQ(2003, obj->Get(empty)->Int32Value());
1704
Steve Block44f0eee2011-05-26 01:26:41 +01001705 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +00001706
1707 // Add another property and delete it afterwards to force the object in
1708 // slow case.
1709 CHECK(obj->Set(prop_name, v8::Integer::New(2008)));
1710 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1711 CHECK_EQ(2008, obj->Get(prop_name)->Int32Value());
1712 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1713 CHECK(obj->Delete(prop_name));
1714 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1715
Steve Block44f0eee2011-05-26 01:26:41 +01001716 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +00001717
1718 CHECK(obj->DeleteHiddenValue(key));
1719 CHECK(obj->GetHiddenValue(key).IsEmpty());
1720}
1721
1722
Steve Blockd0582a62009-12-15 09:54:21 +00001723static bool interceptor_for_hidden_properties_called;
Steve Blocka7e24c12009-10-30 11:49:00 +00001724static v8::Handle<Value> InterceptorForHiddenProperties(
1725 Local<String> name, const AccessorInfo& info) {
Steve Blockd0582a62009-12-15 09:54:21 +00001726 interceptor_for_hidden_properties_called = true;
Steve Blocka7e24c12009-10-30 11:49:00 +00001727 return v8::Handle<Value>();
1728}
1729
1730
1731THREADED_TEST(HiddenPropertiesWithInterceptors) {
1732 v8::HandleScope scope;
1733 LocalContext context;
1734
Steve Blockd0582a62009-12-15 09:54:21 +00001735 interceptor_for_hidden_properties_called = false;
1736
Steve Blocka7e24c12009-10-30 11:49:00 +00001737 v8::Local<v8::String> key = v8_str("api-test::hidden-key");
1738
1739 // Associate an interceptor with an object and start setting hidden values.
1740 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
1741 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
1742 instance_templ->SetNamedPropertyHandler(InterceptorForHiddenProperties);
1743 Local<v8::Function> function = fun_templ->GetFunction();
1744 Local<v8::Object> obj = function->NewInstance();
1745 CHECK(obj->SetHiddenValue(key, v8::Integer::New(2302)));
1746 CHECK_EQ(2302, obj->GetHiddenValue(key)->Int32Value());
Steve Blockd0582a62009-12-15 09:54:21 +00001747 CHECK(!interceptor_for_hidden_properties_called);
Steve Blocka7e24c12009-10-30 11:49:00 +00001748}
1749
1750
1751THREADED_TEST(External) {
1752 v8::HandleScope scope;
1753 int x = 3;
1754 Local<v8::External> ext = v8::External::New(&x);
1755 LocalContext env;
1756 env->Global()->Set(v8_str("ext"), ext);
1757 Local<Value> reext_obj = Script::Compile(v8_str("this.ext"))->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01001758 v8::Handle<v8::External> reext = reext_obj.As<v8::External>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001759 int* ptr = static_cast<int*>(reext->Value());
1760 CHECK_EQ(x, 3);
1761 *ptr = 10;
1762 CHECK_EQ(x, 10);
1763
1764 // Make sure unaligned pointers are wrapped properly.
1765 char* data = i::StrDup("0123456789");
1766 Local<v8::Value> zero = v8::External::Wrap(&data[0]);
1767 Local<v8::Value> one = v8::External::Wrap(&data[1]);
1768 Local<v8::Value> two = v8::External::Wrap(&data[2]);
1769 Local<v8::Value> three = v8::External::Wrap(&data[3]);
1770
1771 char* char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(zero));
1772 CHECK_EQ('0', *char_ptr);
1773 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(one));
1774 CHECK_EQ('1', *char_ptr);
1775 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(two));
1776 CHECK_EQ('2', *char_ptr);
1777 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(three));
1778 CHECK_EQ('3', *char_ptr);
1779 i::DeleteArray(data);
1780}
1781
1782
1783THREADED_TEST(GlobalHandle) {
1784 v8::Persistent<String> global;
1785 {
1786 v8::HandleScope scope;
1787 Local<String> str = v8_str("str");
1788 global = v8::Persistent<String>::New(str);
1789 }
1790 CHECK_EQ(global->Length(), 3);
1791 global.Dispose();
1792}
1793
1794
Steve Block44f0eee2011-05-26 01:26:41 +01001795static int NumberOfWeakCalls = 0;
1796static void WeakPointerCallback(Persistent<Value> handle, void* id) {
1797 CHECK_EQ(reinterpret_cast<void*>(1234), id);
1798 NumberOfWeakCalls++;
1799 handle.Dispose();
1800}
1801
1802THREADED_TEST(ApiObjectGroups) {
1803 HandleScope scope;
1804 LocalContext env;
1805
1806 NumberOfWeakCalls = 0;
1807
1808 Persistent<Object> g1s1;
1809 Persistent<Object> g1s2;
1810 Persistent<Object> g1c1;
1811 Persistent<Object> g2s1;
1812 Persistent<Object> g2s2;
1813 Persistent<Object> g2c1;
1814
1815 {
1816 HandleScope scope;
1817 g1s1 = Persistent<Object>::New(Object::New());
1818 g1s2 = Persistent<Object>::New(Object::New());
1819 g1c1 = Persistent<Object>::New(Object::New());
1820 g1s1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
1821 g1s2.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
1822 g1c1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
1823
1824 g2s1 = Persistent<Object>::New(Object::New());
1825 g2s2 = Persistent<Object>::New(Object::New());
1826 g2c1 = Persistent<Object>::New(Object::New());
1827 g2s1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
1828 g2s2.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
1829 g2c1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
1830 }
1831
1832 Persistent<Object> root = Persistent<Object>::New(g1s1); // make a root.
1833
1834 // Connect group 1 and 2, make a cycle.
1835 CHECK(g1s2->Set(0, g2s2));
1836 CHECK(g2s1->Set(0, g1s1));
1837
1838 {
1839 Persistent<Value> g1_objects[] = { g1s1, g1s2 };
1840 Persistent<Value> g1_children[] = { g1c1 };
1841 Persistent<Value> g2_objects[] = { g2s1, g2s2 };
1842 Persistent<Value> g2_children[] = { g2c1 };
1843 V8::AddObjectGroup(g1_objects, 2);
1844 V8::AddImplicitReferences(g1s1, g1_children, 1);
1845 V8::AddObjectGroup(g2_objects, 2);
1846 V8::AddImplicitReferences(g2s2, g2_children, 1);
1847 }
1848 // Do a full GC
1849 HEAP->CollectGarbage(i::OLD_POINTER_SPACE);
1850
1851 // All object should be alive.
1852 CHECK_EQ(0, NumberOfWeakCalls);
1853
1854 // Weaken the root.
1855 root.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
1856 // But make children strong roots---all the objects (except for children)
1857 // should be collectable now.
1858 g1c1.ClearWeak();
1859 g2c1.ClearWeak();
1860
1861 // Groups are deleted, rebuild groups.
1862 {
1863 Persistent<Value> g1_objects[] = { g1s1, g1s2 };
1864 Persistent<Value> g1_children[] = { g1c1 };
1865 Persistent<Value> g2_objects[] = { g2s1, g2s2 };
1866 Persistent<Value> g2_children[] = { g2c1 };
1867 V8::AddObjectGroup(g1_objects, 2);
1868 V8::AddImplicitReferences(g1s1, g1_children, 1);
1869 V8::AddObjectGroup(g2_objects, 2);
1870 V8::AddImplicitReferences(g2s2, g2_children, 1);
1871 }
1872
1873 HEAP->CollectGarbage(i::OLD_POINTER_SPACE);
1874
1875 // All objects should be gone. 5 global handles in total.
1876 CHECK_EQ(5, NumberOfWeakCalls);
1877
1878 // And now make children weak again and collect them.
1879 g1c1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
1880 g2c1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
1881
1882 HEAP->CollectGarbage(i::OLD_POINTER_SPACE);
1883 CHECK_EQ(7, NumberOfWeakCalls);
1884}
1885
1886
1887THREADED_TEST(ApiObjectGroupsCycle) {
1888 HandleScope scope;
1889 LocalContext env;
1890
1891 NumberOfWeakCalls = 0;
1892
1893 Persistent<Object> g1s1;
1894 Persistent<Object> g1s2;
1895 Persistent<Object> g2s1;
1896 Persistent<Object> g2s2;
1897 Persistent<Object> g3s1;
1898 Persistent<Object> g3s2;
1899
1900 {
1901 HandleScope scope;
1902 g1s1 = Persistent<Object>::New(Object::New());
1903 g1s2 = Persistent<Object>::New(Object::New());
1904 g1s1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
1905 g1s2.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
1906
1907 g2s1 = Persistent<Object>::New(Object::New());
1908 g2s2 = Persistent<Object>::New(Object::New());
1909 g2s1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
1910 g2s2.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
1911
1912 g3s1 = Persistent<Object>::New(Object::New());
1913 g3s2 = Persistent<Object>::New(Object::New());
1914 g3s1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
1915 g3s2.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
1916 }
1917
1918 Persistent<Object> root = Persistent<Object>::New(g1s1); // make a root.
1919
1920 // Connect groups. We're building the following cycle:
1921 // G1: { g1s1, g2s1 }, g1s1 implicitly references g2s1, ditto for other
1922 // groups.
1923 {
1924 Persistent<Value> g1_objects[] = { g1s1, g1s2 };
1925 Persistent<Value> g1_children[] = { g2s1 };
1926 Persistent<Value> g2_objects[] = { g2s1, g2s2 };
1927 Persistent<Value> g2_children[] = { g3s1 };
1928 Persistent<Value> g3_objects[] = { g3s1, g3s2 };
1929 Persistent<Value> g3_children[] = { g1s1 };
1930 V8::AddObjectGroup(g1_objects, 2);
1931 V8::AddImplicitReferences(g1s1, g1_children, 1);
1932 V8::AddObjectGroup(g2_objects, 2);
1933 V8::AddImplicitReferences(g2s1, g2_children, 1);
1934 V8::AddObjectGroup(g3_objects, 2);
1935 V8::AddImplicitReferences(g3s1, g3_children, 1);
1936 }
1937 // Do a full GC
1938 HEAP->CollectGarbage(i::OLD_POINTER_SPACE);
1939
1940 // All object should be alive.
1941 CHECK_EQ(0, NumberOfWeakCalls);
1942
1943 // Weaken the root.
1944 root.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
1945
1946 // Groups are deleted, rebuild groups.
1947 {
1948 Persistent<Value> g1_objects[] = { g1s1, g1s2 };
1949 Persistent<Value> g1_children[] = { g2s1 };
1950 Persistent<Value> g2_objects[] = { g2s1, g2s2 };
1951 Persistent<Value> g2_children[] = { g3s1 };
1952 Persistent<Value> g3_objects[] = { g3s1, g3s2 };
1953 Persistent<Value> g3_children[] = { g1s1 };
1954 V8::AddObjectGroup(g1_objects, 2);
1955 V8::AddImplicitReferences(g1s1, g1_children, 1);
1956 V8::AddObjectGroup(g2_objects, 2);
1957 V8::AddImplicitReferences(g2s1, g2_children, 1);
1958 V8::AddObjectGroup(g3_objects, 2);
1959 V8::AddImplicitReferences(g3s1, g3_children, 1);
1960 }
1961
1962 HEAP->CollectGarbage(i::OLD_POINTER_SPACE);
1963
1964 // All objects should be gone. 7 global handles in total.
1965 CHECK_EQ(7, NumberOfWeakCalls);
1966}
1967
1968
Steve Blocka7e24c12009-10-30 11:49:00 +00001969THREADED_TEST(ScriptException) {
1970 v8::HandleScope scope;
1971 LocalContext env;
1972 Local<Script> script = Script::Compile(v8_str("throw 'panama!';"));
1973 v8::TryCatch try_catch;
1974 Local<Value> result = script->Run();
1975 CHECK(result.IsEmpty());
1976 CHECK(try_catch.HasCaught());
1977 String::AsciiValue exception_value(try_catch.Exception());
1978 CHECK_EQ(*exception_value, "panama!");
1979}
1980
1981
1982bool message_received;
1983
1984
1985static void check_message(v8::Handle<v8::Message> message,
1986 v8::Handle<Value> data) {
1987 CHECK_EQ(5.76, data->NumberValue());
1988 CHECK_EQ(6.75, message->GetScriptResourceName()->NumberValue());
1989 CHECK_EQ(7.56, message->GetScriptData()->NumberValue());
1990 message_received = true;
1991}
1992
1993
1994THREADED_TEST(MessageHandlerData) {
1995 message_received = false;
1996 v8::HandleScope scope;
1997 CHECK(!message_received);
1998 v8::V8::AddMessageListener(check_message, v8_num(5.76));
1999 LocalContext context;
2000 v8::ScriptOrigin origin =
2001 v8::ScriptOrigin(v8_str("6.75"));
2002 v8::Handle<v8::Script> script = Script::Compile(v8_str("throw 'error'"),
2003 &origin);
2004 script->SetData(v8_str("7.56"));
2005 script->Run();
2006 CHECK(message_received);
2007 // clear out the message listener
2008 v8::V8::RemoveMessageListeners(check_message);
2009}
2010
2011
2012THREADED_TEST(GetSetProperty) {
2013 v8::HandleScope scope;
2014 LocalContext context;
2015 context->Global()->Set(v8_str("foo"), v8_num(14));
2016 context->Global()->Set(v8_str("12"), v8_num(92));
2017 context->Global()->Set(v8::Integer::New(16), v8_num(32));
2018 context->Global()->Set(v8_num(13), v8_num(56));
2019 Local<Value> foo = Script::Compile(v8_str("this.foo"))->Run();
2020 CHECK_EQ(14, foo->Int32Value());
2021 Local<Value> twelve = Script::Compile(v8_str("this[12]"))->Run();
2022 CHECK_EQ(92, twelve->Int32Value());
2023 Local<Value> sixteen = Script::Compile(v8_str("this[16]"))->Run();
2024 CHECK_EQ(32, sixteen->Int32Value());
2025 Local<Value> thirteen = Script::Compile(v8_str("this[13]"))->Run();
2026 CHECK_EQ(56, thirteen->Int32Value());
2027 CHECK_EQ(92, context->Global()->Get(v8::Integer::New(12))->Int32Value());
2028 CHECK_EQ(92, context->Global()->Get(v8_str("12"))->Int32Value());
2029 CHECK_EQ(92, context->Global()->Get(v8_num(12))->Int32Value());
2030 CHECK_EQ(32, context->Global()->Get(v8::Integer::New(16))->Int32Value());
2031 CHECK_EQ(32, context->Global()->Get(v8_str("16"))->Int32Value());
2032 CHECK_EQ(32, context->Global()->Get(v8_num(16))->Int32Value());
2033 CHECK_EQ(56, context->Global()->Get(v8::Integer::New(13))->Int32Value());
2034 CHECK_EQ(56, context->Global()->Get(v8_str("13"))->Int32Value());
2035 CHECK_EQ(56, context->Global()->Get(v8_num(13))->Int32Value());
2036}
2037
2038
2039THREADED_TEST(PropertyAttributes) {
2040 v8::HandleScope scope;
2041 LocalContext context;
2042 // read-only
2043 Local<String> prop = v8_str("read_only");
2044 context->Global()->Set(prop, v8_num(7), v8::ReadOnly);
2045 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value());
2046 Script::Compile(v8_str("read_only = 9"))->Run();
2047 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value());
2048 context->Global()->Set(prop, v8_num(10));
2049 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value());
2050 // dont-delete
2051 prop = v8_str("dont_delete");
2052 context->Global()->Set(prop, v8_num(13), v8::DontDelete);
2053 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value());
2054 Script::Compile(v8_str("delete dont_delete"))->Run();
2055 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value());
2056}
2057
2058
2059THREADED_TEST(Array) {
2060 v8::HandleScope scope;
2061 LocalContext context;
2062 Local<v8::Array> array = v8::Array::New();
2063 CHECK_EQ(0, array->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01002064 CHECK(array->Get(0)->IsUndefined());
Steve Blocka7e24c12009-10-30 11:49:00 +00002065 CHECK(!array->Has(0));
Steve Block6ded16b2010-05-10 14:33:55 +01002066 CHECK(array->Get(100)->IsUndefined());
Steve Blocka7e24c12009-10-30 11:49:00 +00002067 CHECK(!array->Has(100));
Steve Block6ded16b2010-05-10 14:33:55 +01002068 array->Set(2, v8_num(7));
Steve Blocka7e24c12009-10-30 11:49:00 +00002069 CHECK_EQ(3, array->Length());
2070 CHECK(!array->Has(0));
2071 CHECK(!array->Has(1));
2072 CHECK(array->Has(2));
Steve Block6ded16b2010-05-10 14:33:55 +01002073 CHECK_EQ(7, array->Get(2)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00002074 Local<Value> obj = Script::Compile(v8_str("[1, 2, 3]"))->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01002075 Local<v8::Array> arr = obj.As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00002076 CHECK_EQ(3, arr->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01002077 CHECK_EQ(1, arr->Get(0)->Int32Value());
2078 CHECK_EQ(2, arr->Get(1)->Int32Value());
2079 CHECK_EQ(3, arr->Get(2)->Int32Value());
Steve Block44f0eee2011-05-26 01:26:41 +01002080 array = v8::Array::New(27);
2081 CHECK_EQ(27, array->Length());
2082 array = v8::Array::New(-27);
2083 CHECK_EQ(0, array->Length());
Steve Blocka7e24c12009-10-30 11:49:00 +00002084}
2085
2086
2087v8::Handle<Value> HandleF(const v8::Arguments& args) {
2088 v8::HandleScope scope;
2089 ApiTestFuzzer::Fuzz();
2090 Local<v8::Array> result = v8::Array::New(args.Length());
2091 for (int i = 0; i < args.Length(); i++)
Steve Block6ded16b2010-05-10 14:33:55 +01002092 result->Set(i, args[i]);
Steve Blocka7e24c12009-10-30 11:49:00 +00002093 return scope.Close(result);
2094}
2095
2096
2097THREADED_TEST(Vector) {
2098 v8::HandleScope scope;
2099 Local<ObjectTemplate> global = ObjectTemplate::New();
2100 global->Set(v8_str("f"), v8::FunctionTemplate::New(HandleF));
2101 LocalContext context(0, global);
2102
2103 const char* fun = "f()";
Steve Block6ded16b2010-05-10 14:33:55 +01002104 Local<v8::Array> a0 = CompileRun(fun).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00002105 CHECK_EQ(0, a0->Length());
2106
2107 const char* fun2 = "f(11)";
Steve Block6ded16b2010-05-10 14:33:55 +01002108 Local<v8::Array> a1 = CompileRun(fun2).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00002109 CHECK_EQ(1, a1->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01002110 CHECK_EQ(11, a1->Get(0)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00002111
2112 const char* fun3 = "f(12, 13)";
Steve Block6ded16b2010-05-10 14:33:55 +01002113 Local<v8::Array> a2 = CompileRun(fun3).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00002114 CHECK_EQ(2, a2->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01002115 CHECK_EQ(12, a2->Get(0)->Int32Value());
2116 CHECK_EQ(13, a2->Get(1)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00002117
2118 const char* fun4 = "f(14, 15, 16)";
Steve Block6ded16b2010-05-10 14:33:55 +01002119 Local<v8::Array> a3 = CompileRun(fun4).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00002120 CHECK_EQ(3, a3->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01002121 CHECK_EQ(14, a3->Get(0)->Int32Value());
2122 CHECK_EQ(15, a3->Get(1)->Int32Value());
2123 CHECK_EQ(16, a3->Get(2)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00002124
2125 const char* fun5 = "f(17, 18, 19, 20)";
Steve Block6ded16b2010-05-10 14:33:55 +01002126 Local<v8::Array> a4 = CompileRun(fun5).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00002127 CHECK_EQ(4, a4->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01002128 CHECK_EQ(17, a4->Get(0)->Int32Value());
2129 CHECK_EQ(18, a4->Get(1)->Int32Value());
2130 CHECK_EQ(19, a4->Get(2)->Int32Value());
2131 CHECK_EQ(20, a4->Get(3)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00002132}
2133
2134
2135THREADED_TEST(FunctionCall) {
2136 v8::HandleScope scope;
2137 LocalContext context;
2138 CompileRun(
2139 "function Foo() {"
2140 " var result = [];"
2141 " for (var i = 0; i < arguments.length; i++) {"
2142 " result.push(arguments[i]);"
2143 " }"
2144 " return result;"
2145 "}");
2146 Local<Function> Foo =
2147 Local<Function>::Cast(context->Global()->Get(v8_str("Foo")));
2148
2149 v8::Handle<Value>* args0 = NULL;
2150 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->Call(Foo, 0, args0));
2151 CHECK_EQ(0, a0->Length());
2152
2153 v8::Handle<Value> args1[] = { v8_num(1.1) };
2154 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->Call(Foo, 1, args1));
2155 CHECK_EQ(1, a1->Length());
2156 CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue());
2157
2158 v8::Handle<Value> args2[] = { v8_num(2.2),
2159 v8_num(3.3) };
2160 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->Call(Foo, 2, args2));
2161 CHECK_EQ(2, a2->Length());
2162 CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue());
2163 CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue());
2164
2165 v8::Handle<Value> args3[] = { v8_num(4.4),
2166 v8_num(5.5),
2167 v8_num(6.6) };
2168 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->Call(Foo, 3, args3));
2169 CHECK_EQ(3, a3->Length());
2170 CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue());
2171 CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue());
2172 CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue());
2173
2174 v8::Handle<Value> args4[] = { v8_num(7.7),
2175 v8_num(8.8),
2176 v8_num(9.9),
2177 v8_num(10.11) };
2178 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->Call(Foo, 4, args4));
2179 CHECK_EQ(4, a4->Length());
2180 CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue());
2181 CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue());
2182 CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue());
2183 CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue());
2184}
2185
2186
2187static const char* js_code_causing_out_of_memory =
2188 "var a = new Array(); while(true) a.push(a);";
2189
2190
2191// These tests run for a long time and prevent us from running tests
2192// that come after them so they cannot run in parallel.
2193TEST(OutOfMemory) {
2194 // It's not possible to read a snapshot into a heap with different dimensions.
Steve Block8defd9f2010-07-08 12:39:36 +01002195 if (i::Snapshot::IsEnabled()) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00002196 // Set heap limits.
2197 static const int K = 1024;
2198 v8::ResourceConstraints constraints;
2199 constraints.set_max_young_space_size(256 * K);
2200 constraints.set_max_old_space_size(4 * K * K);
2201 v8::SetResourceConstraints(&constraints);
2202
2203 // Execute a script that causes out of memory.
2204 v8::HandleScope scope;
2205 LocalContext context;
2206 v8::V8::IgnoreOutOfMemoryException();
2207 Local<Script> script =
2208 Script::Compile(String::New(js_code_causing_out_of_memory));
2209 Local<Value> result = script->Run();
2210
2211 // Check for out of memory state.
2212 CHECK(result.IsEmpty());
2213 CHECK(context->HasOutOfMemoryException());
2214}
2215
2216
2217v8::Handle<Value> ProvokeOutOfMemory(const v8::Arguments& args) {
2218 ApiTestFuzzer::Fuzz();
2219
2220 v8::HandleScope scope;
2221 LocalContext context;
2222 Local<Script> script =
2223 Script::Compile(String::New(js_code_causing_out_of_memory));
2224 Local<Value> result = script->Run();
2225
2226 // Check for out of memory state.
2227 CHECK(result.IsEmpty());
2228 CHECK(context->HasOutOfMemoryException());
2229
2230 return result;
2231}
2232
2233
2234TEST(OutOfMemoryNested) {
2235 // It's not possible to read a snapshot into a heap with different dimensions.
Steve Block8defd9f2010-07-08 12:39:36 +01002236 if (i::Snapshot::IsEnabled()) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00002237 // Set heap limits.
2238 static const int K = 1024;
2239 v8::ResourceConstraints constraints;
2240 constraints.set_max_young_space_size(256 * K);
2241 constraints.set_max_old_space_size(4 * K * K);
2242 v8::SetResourceConstraints(&constraints);
2243
2244 v8::HandleScope scope;
2245 Local<ObjectTemplate> templ = ObjectTemplate::New();
2246 templ->Set(v8_str("ProvokeOutOfMemory"),
2247 v8::FunctionTemplate::New(ProvokeOutOfMemory));
2248 LocalContext context(0, templ);
2249 v8::V8::IgnoreOutOfMemoryException();
2250 Local<Value> result = CompileRun(
2251 "var thrown = false;"
2252 "try {"
2253 " ProvokeOutOfMemory();"
2254 "} catch (e) {"
2255 " thrown = true;"
2256 "}");
2257 // Check for out of memory state.
2258 CHECK(result.IsEmpty());
2259 CHECK(context->HasOutOfMemoryException());
2260}
2261
2262
2263TEST(HugeConsStringOutOfMemory) {
2264 // It's not possible to read a snapshot into a heap with different dimensions.
Steve Block8defd9f2010-07-08 12:39:36 +01002265 if (i::Snapshot::IsEnabled()) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00002266 // Set heap limits.
2267 static const int K = 1024;
2268 v8::ResourceConstraints constraints;
2269 constraints.set_max_young_space_size(256 * K);
2270 constraints.set_max_old_space_size(2 * K * K);
2271 v8::SetResourceConstraints(&constraints);
2272
2273 // Execute a script that causes out of memory.
2274 v8::V8::IgnoreOutOfMemoryException();
2275
Steve Block44f0eee2011-05-26 01:26:41 +01002276 v8::HandleScope scope;
2277 LocalContext context;
2278
Steve Blocka7e24c12009-10-30 11:49:00 +00002279 // Build huge string. This should fail with out of memory exception.
2280 Local<Value> result = CompileRun(
2281 "var str = Array.prototype.join.call({length: 513}, \"A\").toUpperCase();"
Steve Block3ce2e202009-11-05 08:53:23 +00002282 "for (var i = 0; i < 22; i++) { str = str + str; }");
Steve Blocka7e24c12009-10-30 11:49:00 +00002283
2284 // Check for out of memory state.
2285 CHECK(result.IsEmpty());
2286 CHECK(context->HasOutOfMemoryException());
2287}
2288
2289
2290THREADED_TEST(ConstructCall) {
2291 v8::HandleScope scope;
2292 LocalContext context;
2293 CompileRun(
2294 "function Foo() {"
2295 " var result = [];"
2296 " for (var i = 0; i < arguments.length; i++) {"
2297 " result.push(arguments[i]);"
2298 " }"
2299 " return result;"
2300 "}");
2301 Local<Function> Foo =
2302 Local<Function>::Cast(context->Global()->Get(v8_str("Foo")));
2303
2304 v8::Handle<Value>* args0 = NULL;
2305 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->NewInstance(0, args0));
2306 CHECK_EQ(0, a0->Length());
2307
2308 v8::Handle<Value> args1[] = { v8_num(1.1) };
2309 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->NewInstance(1, args1));
2310 CHECK_EQ(1, a1->Length());
2311 CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue());
2312
2313 v8::Handle<Value> args2[] = { v8_num(2.2),
2314 v8_num(3.3) };
2315 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->NewInstance(2, args2));
2316 CHECK_EQ(2, a2->Length());
2317 CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue());
2318 CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue());
2319
2320 v8::Handle<Value> args3[] = { v8_num(4.4),
2321 v8_num(5.5),
2322 v8_num(6.6) };
2323 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->NewInstance(3, args3));
2324 CHECK_EQ(3, a3->Length());
2325 CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue());
2326 CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue());
2327 CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue());
2328
2329 v8::Handle<Value> args4[] = { v8_num(7.7),
2330 v8_num(8.8),
2331 v8_num(9.9),
2332 v8_num(10.11) };
2333 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->NewInstance(4, args4));
2334 CHECK_EQ(4, a4->Length());
2335 CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue());
2336 CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue());
2337 CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue());
2338 CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue());
2339}
2340
2341
2342static void CheckUncle(v8::TryCatch* try_catch) {
2343 CHECK(try_catch->HasCaught());
2344 String::AsciiValue str_value(try_catch->Exception());
2345 CHECK_EQ(*str_value, "uncle?");
2346 try_catch->Reset();
2347}
2348
2349
Steve Block6ded16b2010-05-10 14:33:55 +01002350THREADED_TEST(ConversionNumber) {
2351 v8::HandleScope scope;
2352 LocalContext env;
2353 // Very large number.
2354 CompileRun("var obj = Math.pow(2,32) * 1237;");
2355 Local<Value> obj = env->Global()->Get(v8_str("obj"));
2356 CHECK_EQ(5312874545152.0, obj->ToNumber()->Value());
2357 CHECK_EQ(0, obj->ToInt32()->Value());
2358 CHECK(0u == obj->ToUint32()->Value()); // NOLINT - no CHECK_EQ for unsigned.
2359 // Large number.
2360 CompileRun("var obj = -1234567890123;");
2361 obj = env->Global()->Get(v8_str("obj"));
2362 CHECK_EQ(-1234567890123.0, obj->ToNumber()->Value());
2363 CHECK_EQ(-1912276171, obj->ToInt32()->Value());
2364 CHECK(2382691125u == obj->ToUint32()->Value()); // NOLINT
2365 // Small positive integer.
2366 CompileRun("var obj = 42;");
2367 obj = env->Global()->Get(v8_str("obj"));
2368 CHECK_EQ(42.0, obj->ToNumber()->Value());
2369 CHECK_EQ(42, obj->ToInt32()->Value());
2370 CHECK(42u == obj->ToUint32()->Value()); // NOLINT
2371 // Negative integer.
2372 CompileRun("var obj = -37;");
2373 obj = env->Global()->Get(v8_str("obj"));
2374 CHECK_EQ(-37.0, obj->ToNumber()->Value());
2375 CHECK_EQ(-37, obj->ToInt32()->Value());
2376 CHECK(4294967259u == obj->ToUint32()->Value()); // NOLINT
2377 // Positive non-int32 integer.
2378 CompileRun("var obj = 0x81234567;");
2379 obj = env->Global()->Get(v8_str("obj"));
2380 CHECK_EQ(2166572391.0, obj->ToNumber()->Value());
2381 CHECK_EQ(-2128394905, obj->ToInt32()->Value());
2382 CHECK(2166572391u == obj->ToUint32()->Value()); // NOLINT
2383 // Fraction.
2384 CompileRun("var obj = 42.3;");
2385 obj = env->Global()->Get(v8_str("obj"));
2386 CHECK_EQ(42.3, obj->ToNumber()->Value());
2387 CHECK_EQ(42, obj->ToInt32()->Value());
2388 CHECK(42u == obj->ToUint32()->Value()); // NOLINT
2389 // Large negative fraction.
2390 CompileRun("var obj = -5726623061.75;");
2391 obj = env->Global()->Get(v8_str("obj"));
2392 CHECK_EQ(-5726623061.75, obj->ToNumber()->Value());
2393 CHECK_EQ(-1431655765, obj->ToInt32()->Value());
2394 CHECK(2863311531u == obj->ToUint32()->Value()); // NOLINT
2395}
2396
2397
2398THREADED_TEST(isNumberType) {
2399 v8::HandleScope scope;
2400 LocalContext env;
2401 // Very large number.
2402 CompileRun("var obj = Math.pow(2,32) * 1237;");
2403 Local<Value> obj = env->Global()->Get(v8_str("obj"));
2404 CHECK(!obj->IsInt32());
2405 CHECK(!obj->IsUint32());
2406 // Large negative number.
2407 CompileRun("var obj = -1234567890123;");
2408 obj = env->Global()->Get(v8_str("obj"));
2409 CHECK(!obj->IsInt32());
2410 CHECK(!obj->IsUint32());
2411 // Small positive integer.
2412 CompileRun("var obj = 42;");
2413 obj = env->Global()->Get(v8_str("obj"));
2414 CHECK(obj->IsInt32());
2415 CHECK(obj->IsUint32());
2416 // Negative integer.
2417 CompileRun("var obj = -37;");
2418 obj = env->Global()->Get(v8_str("obj"));
2419 CHECK(obj->IsInt32());
2420 CHECK(!obj->IsUint32());
2421 // Positive non-int32 integer.
2422 CompileRun("var obj = 0x81234567;");
2423 obj = env->Global()->Get(v8_str("obj"));
2424 CHECK(!obj->IsInt32());
2425 CHECK(obj->IsUint32());
2426 // Fraction.
2427 CompileRun("var obj = 42.3;");
2428 obj = env->Global()->Get(v8_str("obj"));
2429 CHECK(!obj->IsInt32());
2430 CHECK(!obj->IsUint32());
2431 // Large negative fraction.
2432 CompileRun("var obj = -5726623061.75;");
2433 obj = env->Global()->Get(v8_str("obj"));
2434 CHECK(!obj->IsInt32());
2435 CHECK(!obj->IsUint32());
2436}
2437
2438
Steve Blocka7e24c12009-10-30 11:49:00 +00002439THREADED_TEST(ConversionException) {
2440 v8::HandleScope scope;
2441 LocalContext env;
2442 CompileRun(
2443 "function TestClass() { };"
2444 "TestClass.prototype.toString = function () { throw 'uncle?'; };"
2445 "var obj = new TestClass();");
2446 Local<Value> obj = env->Global()->Get(v8_str("obj"));
2447
2448 v8::TryCatch try_catch;
2449
2450 Local<Value> to_string_result = obj->ToString();
2451 CHECK(to_string_result.IsEmpty());
2452 CheckUncle(&try_catch);
2453
2454 Local<Value> to_number_result = obj->ToNumber();
2455 CHECK(to_number_result.IsEmpty());
2456 CheckUncle(&try_catch);
2457
2458 Local<Value> to_integer_result = obj->ToInteger();
2459 CHECK(to_integer_result.IsEmpty());
2460 CheckUncle(&try_catch);
2461
2462 Local<Value> to_uint32_result = obj->ToUint32();
2463 CHECK(to_uint32_result.IsEmpty());
2464 CheckUncle(&try_catch);
2465
2466 Local<Value> to_int32_result = obj->ToInt32();
2467 CHECK(to_int32_result.IsEmpty());
2468 CheckUncle(&try_catch);
2469
2470 Local<Value> to_object_result = v8::Undefined()->ToObject();
2471 CHECK(to_object_result.IsEmpty());
2472 CHECK(try_catch.HasCaught());
2473 try_catch.Reset();
2474
2475 int32_t int32_value = obj->Int32Value();
2476 CHECK_EQ(0, int32_value);
2477 CheckUncle(&try_catch);
2478
2479 uint32_t uint32_value = obj->Uint32Value();
2480 CHECK_EQ(0, uint32_value);
2481 CheckUncle(&try_catch);
2482
2483 double number_value = obj->NumberValue();
2484 CHECK_NE(0, IsNaN(number_value));
2485 CheckUncle(&try_catch);
2486
2487 int64_t integer_value = obj->IntegerValue();
2488 CHECK_EQ(0.0, static_cast<double>(integer_value));
2489 CheckUncle(&try_catch);
2490}
2491
2492
2493v8::Handle<Value> ThrowFromC(const v8::Arguments& args) {
2494 ApiTestFuzzer::Fuzz();
2495 return v8::ThrowException(v8_str("konto"));
2496}
2497
2498
2499v8::Handle<Value> CCatcher(const v8::Arguments& args) {
2500 if (args.Length() < 1) return v8::Boolean::New(false);
2501 v8::HandleScope scope;
2502 v8::TryCatch try_catch;
2503 Local<Value> result = v8::Script::Compile(args[0]->ToString())->Run();
2504 CHECK(!try_catch.HasCaught() || result.IsEmpty());
2505 return v8::Boolean::New(try_catch.HasCaught());
2506}
2507
2508
2509THREADED_TEST(APICatch) {
2510 v8::HandleScope scope;
2511 Local<ObjectTemplate> templ = ObjectTemplate::New();
2512 templ->Set(v8_str("ThrowFromC"),
2513 v8::FunctionTemplate::New(ThrowFromC));
2514 LocalContext context(0, templ);
2515 CompileRun(
2516 "var thrown = false;"
2517 "try {"
2518 " ThrowFromC();"
2519 "} catch (e) {"
2520 " thrown = true;"
2521 "}");
2522 Local<Value> thrown = context->Global()->Get(v8_str("thrown"));
2523 CHECK(thrown->BooleanValue());
2524}
2525
2526
2527THREADED_TEST(APIThrowTryCatch) {
2528 v8::HandleScope scope;
2529 Local<ObjectTemplate> templ = ObjectTemplate::New();
2530 templ->Set(v8_str("ThrowFromC"),
2531 v8::FunctionTemplate::New(ThrowFromC));
2532 LocalContext context(0, templ);
2533 v8::TryCatch try_catch;
2534 CompileRun("ThrowFromC();");
2535 CHECK(try_catch.HasCaught());
2536}
2537
2538
2539// Test that a try-finally block doesn't shadow a try-catch block
2540// when setting up an external handler.
2541//
2542// BUG(271): Some of the exception propagation does not work on the
2543// ARM simulator because the simulator separates the C++ stack and the
2544// JS stack. This test therefore fails on the simulator. The test is
2545// not threaded to allow the threading tests to run on the simulator.
2546TEST(TryCatchInTryFinally) {
2547 v8::HandleScope scope;
2548 Local<ObjectTemplate> templ = ObjectTemplate::New();
2549 templ->Set(v8_str("CCatcher"),
2550 v8::FunctionTemplate::New(CCatcher));
2551 LocalContext context(0, templ);
2552 Local<Value> result = CompileRun("try {"
2553 " try {"
2554 " CCatcher('throw 7;');"
2555 " } finally {"
2556 " }"
2557 "} catch (e) {"
2558 "}");
2559 CHECK(result->IsTrue());
2560}
2561
2562
Ben Murdochb8e0da22011-05-16 14:20:40 +01002563static void check_reference_error_message(
2564 v8::Handle<v8::Message> message,
2565 v8::Handle<v8::Value> data) {
2566 const char* reference_error = "Uncaught ReferenceError: asdf is not defined";
2567 CHECK(message->Get()->Equals(v8_str(reference_error)));
2568}
2569
2570
Steve Block1e0659c2011-05-24 12:43:12 +01002571static v8::Handle<Value> Fail(const v8::Arguments& args) {
2572 ApiTestFuzzer::Fuzz();
2573 CHECK(false);
2574 return v8::Undefined();
2575}
2576
2577
2578// Test that overwritten methods are not invoked on uncaught exception
2579// formatting. However, they are invoked when performing normal error
2580// string conversions.
Ben Murdochb8e0da22011-05-16 14:20:40 +01002581TEST(APIThrowMessageOverwrittenToString) {
2582 v8::HandleScope scope;
2583 v8::V8::AddMessageListener(check_reference_error_message);
Steve Block1e0659c2011-05-24 12:43:12 +01002584 Local<ObjectTemplate> templ = ObjectTemplate::New();
2585 templ->Set(v8_str("fail"), v8::FunctionTemplate::New(Fail));
2586 LocalContext context(NULL, templ);
2587 CompileRun("asdf;");
2588 CompileRun("var limit = {};"
2589 "limit.valueOf = fail;"
2590 "Error.stackTraceLimit = limit;");
2591 CompileRun("asdf");
2592 CompileRun("Array.prototype.pop = fail;");
2593 CompileRun("Object.prototype.hasOwnProperty = fail;");
2594 CompileRun("Object.prototype.toString = function f() { return 'Yikes'; }");
2595 CompileRun("Number.prototype.toString = function f() { return 'Yikes'; }");
2596 CompileRun("String.prototype.toString = function f() { return 'Yikes'; }");
Ben Murdochb8e0da22011-05-16 14:20:40 +01002597 CompileRun("ReferenceError.prototype.toString ="
2598 " function() { return 'Whoops' }");
2599 CompileRun("asdf;");
Steve Block1e0659c2011-05-24 12:43:12 +01002600 CompileRun("ReferenceError.prototype.constructor.name = void 0;");
2601 CompileRun("asdf;");
2602 CompileRun("ReferenceError.prototype.constructor = void 0;");
2603 CompileRun("asdf;");
2604 CompileRun("ReferenceError.prototype.__proto__ = new Object();");
2605 CompileRun("asdf;");
2606 CompileRun("ReferenceError.prototype = new Object();");
2607 CompileRun("asdf;");
Ben Murdochb8e0da22011-05-16 14:20:40 +01002608 v8::Handle<Value> string = CompileRun("try { asdf; } catch(e) { e + ''; }");
2609 CHECK(string->Equals(v8_str("Whoops")));
Steve Block1e0659c2011-05-24 12:43:12 +01002610 CompileRun("ReferenceError.prototype.constructor = new Object();"
2611 "ReferenceError.prototype.constructor.name = 1;"
2612 "Number.prototype.toString = function() { return 'Whoops'; };"
2613 "ReferenceError.prototype.toString = Object.prototype.toString;");
2614 CompileRun("asdf;");
Ben Murdochb8e0da22011-05-16 14:20:40 +01002615 v8::V8::RemoveMessageListeners(check_message);
2616}
2617
2618
Steve Blocka7e24c12009-10-30 11:49:00 +00002619static void receive_message(v8::Handle<v8::Message> message,
2620 v8::Handle<v8::Value> data) {
2621 message->Get();
2622 message_received = true;
2623}
2624
2625
2626TEST(APIThrowMessage) {
2627 message_received = false;
2628 v8::HandleScope scope;
2629 v8::V8::AddMessageListener(receive_message);
2630 Local<ObjectTemplate> templ = ObjectTemplate::New();
2631 templ->Set(v8_str("ThrowFromC"),
2632 v8::FunctionTemplate::New(ThrowFromC));
2633 LocalContext context(0, templ);
2634 CompileRun("ThrowFromC();");
2635 CHECK(message_received);
2636 v8::V8::RemoveMessageListeners(check_message);
2637}
2638
2639
2640TEST(APIThrowMessageAndVerboseTryCatch) {
2641 message_received = false;
2642 v8::HandleScope scope;
2643 v8::V8::AddMessageListener(receive_message);
2644 Local<ObjectTemplate> templ = ObjectTemplate::New();
2645 templ->Set(v8_str("ThrowFromC"),
2646 v8::FunctionTemplate::New(ThrowFromC));
2647 LocalContext context(0, templ);
2648 v8::TryCatch try_catch;
2649 try_catch.SetVerbose(true);
2650 Local<Value> result = CompileRun("ThrowFromC();");
2651 CHECK(try_catch.HasCaught());
2652 CHECK(result.IsEmpty());
2653 CHECK(message_received);
2654 v8::V8::RemoveMessageListeners(check_message);
2655}
2656
2657
2658THREADED_TEST(ExternalScriptException) {
2659 v8::HandleScope scope;
2660 Local<ObjectTemplate> templ = ObjectTemplate::New();
2661 templ->Set(v8_str("ThrowFromC"),
2662 v8::FunctionTemplate::New(ThrowFromC));
2663 LocalContext context(0, templ);
2664
2665 v8::TryCatch try_catch;
2666 Local<Script> script
2667 = Script::Compile(v8_str("ThrowFromC(); throw 'panama';"));
2668 Local<Value> result = script->Run();
2669 CHECK(result.IsEmpty());
2670 CHECK(try_catch.HasCaught());
2671 String::AsciiValue exception_value(try_catch.Exception());
2672 CHECK_EQ("konto", *exception_value);
2673}
2674
2675
2676
2677v8::Handle<Value> CThrowCountDown(const v8::Arguments& args) {
2678 ApiTestFuzzer::Fuzz();
2679 CHECK_EQ(4, args.Length());
2680 int count = args[0]->Int32Value();
2681 int cInterval = args[2]->Int32Value();
2682 if (count == 0) {
2683 return v8::ThrowException(v8_str("FromC"));
2684 } else {
2685 Local<v8::Object> global = Context::GetCurrent()->Global();
2686 Local<Value> fun = global->Get(v8_str("JSThrowCountDown"));
2687 v8::Handle<Value> argv[] = { v8_num(count - 1),
2688 args[1],
2689 args[2],
2690 args[3] };
2691 if (count % cInterval == 0) {
2692 v8::TryCatch try_catch;
Steve Block6ded16b2010-05-10 14:33:55 +01002693 Local<Value> result = fun.As<Function>()->Call(global, 4, argv);
Steve Blocka7e24c12009-10-30 11:49:00 +00002694 int expected = args[3]->Int32Value();
2695 if (try_catch.HasCaught()) {
2696 CHECK_EQ(expected, count);
2697 CHECK(result.IsEmpty());
Steve Block44f0eee2011-05-26 01:26:41 +01002698 CHECK(!i::Isolate::Current()->has_scheduled_exception());
Steve Blocka7e24c12009-10-30 11:49:00 +00002699 } else {
2700 CHECK_NE(expected, count);
2701 }
2702 return result;
2703 } else {
Steve Block6ded16b2010-05-10 14:33:55 +01002704 return fun.As<Function>()->Call(global, 4, argv);
Steve Blocka7e24c12009-10-30 11:49:00 +00002705 }
2706 }
2707}
2708
2709
2710v8::Handle<Value> JSCheck(const v8::Arguments& args) {
2711 ApiTestFuzzer::Fuzz();
2712 CHECK_EQ(3, args.Length());
2713 bool equality = args[0]->BooleanValue();
2714 int count = args[1]->Int32Value();
2715 int expected = args[2]->Int32Value();
2716 if (equality) {
2717 CHECK_EQ(count, expected);
2718 } else {
2719 CHECK_NE(count, expected);
2720 }
2721 return v8::Undefined();
2722}
2723
2724
2725THREADED_TEST(EvalInTryFinally) {
2726 v8::HandleScope scope;
2727 LocalContext context;
2728 v8::TryCatch try_catch;
2729 CompileRun("(function() {"
2730 " try {"
2731 " eval('asldkf (*&^&*^');"
2732 " } finally {"
2733 " return;"
2734 " }"
2735 "})()");
2736 CHECK(!try_catch.HasCaught());
2737}
2738
2739
2740// This test works by making a stack of alternating JavaScript and C
2741// activations. These activations set up exception handlers with regular
2742// intervals, one interval for C activations and another for JavaScript
2743// activations. When enough activations have been created an exception is
2744// thrown and we check that the right activation catches the exception and that
2745// no other activations do. The right activation is always the topmost one with
2746// a handler, regardless of whether it is in JavaScript or C.
2747//
2748// The notation used to describe a test case looks like this:
2749//
2750// *JS[4] *C[3] @JS[2] C[1] JS[0]
2751//
2752// Each entry is an activation, either JS or C. The index is the count at that
2753// level. Stars identify activations with exception handlers, the @ identifies
2754// the exception handler that should catch the exception.
2755//
2756// BUG(271): Some of the exception propagation does not work on the
2757// ARM simulator because the simulator separates the C++ stack and the
2758// JS stack. This test therefore fails on the simulator. The test is
2759// not threaded to allow the threading tests to run on the simulator.
2760TEST(ExceptionOrder) {
2761 v8::HandleScope scope;
2762 Local<ObjectTemplate> templ = ObjectTemplate::New();
2763 templ->Set(v8_str("check"), v8::FunctionTemplate::New(JSCheck));
2764 templ->Set(v8_str("CThrowCountDown"),
2765 v8::FunctionTemplate::New(CThrowCountDown));
2766 LocalContext context(0, templ);
2767 CompileRun(
2768 "function JSThrowCountDown(count, jsInterval, cInterval, expected) {"
2769 " if (count == 0) throw 'FromJS';"
2770 " if (count % jsInterval == 0) {"
2771 " try {"
2772 " var value = CThrowCountDown(count - 1,"
2773 " jsInterval,"
2774 " cInterval,"
2775 " expected);"
2776 " check(false, count, expected);"
2777 " return value;"
2778 " } catch (e) {"
2779 " check(true, count, expected);"
2780 " }"
2781 " } else {"
2782 " return CThrowCountDown(count - 1, jsInterval, cInterval, expected);"
2783 " }"
2784 "}");
2785 Local<Function> fun =
2786 Local<Function>::Cast(context->Global()->Get(v8_str("JSThrowCountDown")));
2787
2788 const int argc = 4;
2789 // count jsInterval cInterval expected
2790
2791 // *JS[4] *C[3] @JS[2] C[1] JS[0]
2792 v8::Handle<Value> a0[argc] = { v8_num(4), v8_num(2), v8_num(3), v8_num(2) };
2793 fun->Call(fun, argc, a0);
2794
2795 // JS[5] *C[4] JS[3] @C[2] JS[1] C[0]
2796 v8::Handle<Value> a1[argc] = { v8_num(5), v8_num(6), v8_num(1), v8_num(2) };
2797 fun->Call(fun, argc, a1);
2798
2799 // JS[6] @C[5] JS[4] C[3] JS[2] C[1] JS[0]
2800 v8::Handle<Value> a2[argc] = { v8_num(6), v8_num(7), v8_num(5), v8_num(5) };
2801 fun->Call(fun, argc, a2);
2802
2803 // @JS[6] C[5] JS[4] C[3] JS[2] C[1] JS[0]
2804 v8::Handle<Value> a3[argc] = { v8_num(6), v8_num(6), v8_num(7), v8_num(6) };
2805 fun->Call(fun, argc, a3);
2806
2807 // JS[6] *C[5] @JS[4] C[3] JS[2] C[1] JS[0]
2808 v8::Handle<Value> a4[argc] = { v8_num(6), v8_num(4), v8_num(5), v8_num(4) };
2809 fun->Call(fun, argc, a4);
2810
2811 // JS[6] C[5] *JS[4] @C[3] JS[2] C[1] JS[0]
2812 v8::Handle<Value> a5[argc] = { v8_num(6), v8_num(4), v8_num(3), v8_num(3) };
2813 fun->Call(fun, argc, a5);
2814}
2815
2816
2817v8::Handle<Value> ThrowValue(const v8::Arguments& args) {
2818 ApiTestFuzzer::Fuzz();
2819 CHECK_EQ(1, args.Length());
2820 return v8::ThrowException(args[0]);
2821}
2822
2823
2824THREADED_TEST(ThrowValues) {
2825 v8::HandleScope scope;
2826 Local<ObjectTemplate> templ = ObjectTemplate::New();
2827 templ->Set(v8_str("Throw"), v8::FunctionTemplate::New(ThrowValue));
2828 LocalContext context(0, templ);
2829 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun(
2830 "function Run(obj) {"
2831 " try {"
2832 " Throw(obj);"
2833 " } catch (e) {"
2834 " return e;"
2835 " }"
2836 " return 'no exception';"
2837 "}"
2838 "[Run('str'), Run(1), Run(0), Run(null), Run(void 0)];"));
2839 CHECK_EQ(5, result->Length());
2840 CHECK(result->Get(v8::Integer::New(0))->IsString());
2841 CHECK(result->Get(v8::Integer::New(1))->IsNumber());
2842 CHECK_EQ(1, result->Get(v8::Integer::New(1))->Int32Value());
2843 CHECK(result->Get(v8::Integer::New(2))->IsNumber());
2844 CHECK_EQ(0, result->Get(v8::Integer::New(2))->Int32Value());
2845 CHECK(result->Get(v8::Integer::New(3))->IsNull());
2846 CHECK(result->Get(v8::Integer::New(4))->IsUndefined());
2847}
2848
2849
2850THREADED_TEST(CatchZero) {
2851 v8::HandleScope scope;
2852 LocalContext context;
2853 v8::TryCatch try_catch;
2854 CHECK(!try_catch.HasCaught());
2855 Script::Compile(v8_str("throw 10"))->Run();
2856 CHECK(try_catch.HasCaught());
2857 CHECK_EQ(10, try_catch.Exception()->Int32Value());
2858 try_catch.Reset();
2859 CHECK(!try_catch.HasCaught());
2860 Script::Compile(v8_str("throw 0"))->Run();
2861 CHECK(try_catch.HasCaught());
2862 CHECK_EQ(0, try_catch.Exception()->Int32Value());
2863}
2864
2865
2866THREADED_TEST(CatchExceptionFromWith) {
2867 v8::HandleScope scope;
2868 LocalContext context;
2869 v8::TryCatch try_catch;
2870 CHECK(!try_catch.HasCaught());
2871 Script::Compile(v8_str("var o = {}; with (o) { throw 42; }"))->Run();
2872 CHECK(try_catch.HasCaught());
2873}
2874
2875
Ben Murdoche0cee9b2011-05-25 10:26:03 +01002876THREADED_TEST(TryCatchAndFinallyHidingException) {
2877 v8::HandleScope scope;
2878 LocalContext context;
2879 v8::TryCatch try_catch;
2880 CHECK(!try_catch.HasCaught());
2881 CompileRun("function f(k) { try { this[k]; } finally { return 0; } };");
2882 CompileRun("f({toString: function() { throw 42; }});");
2883 CHECK(!try_catch.HasCaught());
2884}
2885
2886
2887v8::Handle<v8::Value> WithTryCatch(const v8::Arguments& args) {
2888 v8::TryCatch try_catch;
2889 return v8::Undefined();
2890}
2891
2892
2893THREADED_TEST(TryCatchAndFinally) {
2894 v8::HandleScope scope;
2895 LocalContext context;
2896 context->Global()->Set(
2897 v8_str("native_with_try_catch"),
2898 v8::FunctionTemplate::New(WithTryCatch)->GetFunction());
2899 v8::TryCatch try_catch;
2900 CHECK(!try_catch.HasCaught());
2901 CompileRun(
2902 "try {\n"
2903 " throw new Error('a');\n"
2904 "} finally {\n"
2905 " native_with_try_catch();\n"
2906 "}\n");
2907 CHECK(try_catch.HasCaught());
2908}
2909
2910
Steve Blocka7e24c12009-10-30 11:49:00 +00002911THREADED_TEST(Equality) {
2912 v8::HandleScope scope;
2913 LocalContext context;
2914 // Check that equality works at all before relying on CHECK_EQ
2915 CHECK(v8_str("a")->Equals(v8_str("a")));
2916 CHECK(!v8_str("a")->Equals(v8_str("b")));
2917
2918 CHECK_EQ(v8_str("a"), v8_str("a"));
2919 CHECK_NE(v8_str("a"), v8_str("b"));
2920 CHECK_EQ(v8_num(1), v8_num(1));
2921 CHECK_EQ(v8_num(1.00), v8_num(1));
2922 CHECK_NE(v8_num(1), v8_num(2));
2923
2924 // Assume String is not symbol.
2925 CHECK(v8_str("a")->StrictEquals(v8_str("a")));
2926 CHECK(!v8_str("a")->StrictEquals(v8_str("b")));
2927 CHECK(!v8_str("5")->StrictEquals(v8_num(5)));
2928 CHECK(v8_num(1)->StrictEquals(v8_num(1)));
2929 CHECK(!v8_num(1)->StrictEquals(v8_num(2)));
2930 CHECK(v8_num(0)->StrictEquals(v8_num(-0)));
2931 Local<Value> not_a_number = v8_num(i::OS::nan_value());
2932 CHECK(!not_a_number->StrictEquals(not_a_number));
2933 CHECK(v8::False()->StrictEquals(v8::False()));
2934 CHECK(!v8::False()->StrictEquals(v8::Undefined()));
2935
2936 v8::Handle<v8::Object> obj = v8::Object::New();
2937 v8::Persistent<v8::Object> alias = v8::Persistent<v8::Object>::New(obj);
2938 CHECK(alias->StrictEquals(obj));
2939 alias.Dispose();
2940}
2941
2942
2943THREADED_TEST(MultiRun) {
2944 v8::HandleScope scope;
2945 LocalContext context;
2946 Local<Script> script = Script::Compile(v8_str("x"));
2947 for (int i = 0; i < 10; i++)
2948 script->Run();
2949}
2950
2951
2952static v8::Handle<Value> GetXValue(Local<String> name,
2953 const AccessorInfo& info) {
2954 ApiTestFuzzer::Fuzz();
2955 CHECK_EQ(info.Data(), v8_str("donut"));
2956 CHECK_EQ(name, v8_str("x"));
2957 return name;
2958}
2959
2960
2961THREADED_TEST(SimplePropertyRead) {
2962 v8::HandleScope scope;
2963 Local<ObjectTemplate> templ = ObjectTemplate::New();
2964 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"));
2965 LocalContext context;
2966 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2967 Local<Script> script = Script::Compile(v8_str("obj.x"));
2968 for (int i = 0; i < 10; i++) {
2969 Local<Value> result = script->Run();
2970 CHECK_EQ(result, v8_str("x"));
2971 }
2972}
2973
Andrei Popescu31002712010-02-23 13:46:05 +00002974THREADED_TEST(DefinePropertyOnAPIAccessor) {
2975 v8::HandleScope scope;
2976 Local<ObjectTemplate> templ = ObjectTemplate::New();
2977 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"));
2978 LocalContext context;
2979 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2980
2981 // Uses getOwnPropertyDescriptor to check the configurable status
2982 Local<Script> script_desc
Leon Clarkef7060e22010-06-03 12:02:55 +01002983 = Script::Compile(v8_str("var prop = Object.getOwnPropertyDescriptor( "
Andrei Popescu31002712010-02-23 13:46:05 +00002984 "obj, 'x');"
2985 "prop.configurable;"));
2986 Local<Value> result = script_desc->Run();
2987 CHECK_EQ(result->BooleanValue(), true);
2988
2989 // Redefine get - but still configurable
2990 Local<Script> script_define
2991 = Script::Compile(v8_str("var desc = { get: function(){return 42; },"
2992 " configurable: true };"
2993 "Object.defineProperty(obj, 'x', desc);"
2994 "obj.x"));
2995 result = script_define->Run();
2996 CHECK_EQ(result, v8_num(42));
2997
2998 // Check that the accessor is still configurable
2999 result = script_desc->Run();
3000 CHECK_EQ(result->BooleanValue(), true);
3001
3002 // Redefine to a non-configurable
3003 script_define
3004 = Script::Compile(v8_str("var desc = { get: function(){return 43; },"
3005 " configurable: false };"
3006 "Object.defineProperty(obj, 'x', desc);"
3007 "obj.x"));
3008 result = script_define->Run();
3009 CHECK_EQ(result, v8_num(43));
3010 result = script_desc->Run();
3011 CHECK_EQ(result->BooleanValue(), false);
3012
3013 // Make sure that it is not possible to redefine again
3014 v8::TryCatch try_catch;
3015 result = script_define->Run();
3016 CHECK(try_catch.HasCaught());
3017 String::AsciiValue exception_value(try_catch.Exception());
3018 CHECK_EQ(*exception_value,
3019 "TypeError: Cannot redefine property: defineProperty");
3020}
3021
3022THREADED_TEST(DefinePropertyOnDefineGetterSetter) {
3023 v8::HandleScope scope;
3024 Local<ObjectTemplate> templ = ObjectTemplate::New();
3025 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"));
3026 LocalContext context;
3027 context->Global()->Set(v8_str("obj"), templ->NewInstance());
3028
3029 Local<Script> script_desc = Script::Compile(v8_str("var prop ="
3030 "Object.getOwnPropertyDescriptor( "
3031 "obj, 'x');"
3032 "prop.configurable;"));
3033 Local<Value> result = script_desc->Run();
3034 CHECK_EQ(result->BooleanValue(), true);
3035
3036 Local<Script> script_define =
3037 Script::Compile(v8_str("var desc = {get: function(){return 42; },"
3038 " configurable: true };"
3039 "Object.defineProperty(obj, 'x', desc);"
3040 "obj.x"));
3041 result = script_define->Run();
3042 CHECK_EQ(result, v8_num(42));
3043
3044
3045 result = script_desc->Run();
3046 CHECK_EQ(result->BooleanValue(), true);
3047
3048
3049 script_define =
3050 Script::Compile(v8_str("var desc = {get: function(){return 43; },"
3051 " configurable: false };"
3052 "Object.defineProperty(obj, 'x', desc);"
3053 "obj.x"));
3054 result = script_define->Run();
3055 CHECK_EQ(result, v8_num(43));
3056 result = script_desc->Run();
3057
3058 CHECK_EQ(result->BooleanValue(), false);
3059
3060 v8::TryCatch try_catch;
3061 result = script_define->Run();
3062 CHECK(try_catch.HasCaught());
3063 String::AsciiValue exception_value(try_catch.Exception());
3064 CHECK_EQ(*exception_value,
3065 "TypeError: Cannot redefine property: defineProperty");
3066}
3067
3068
Leon Clarkef7060e22010-06-03 12:02:55 +01003069static v8::Handle<v8::Object> GetGlobalProperty(LocalContext* context,
3070 char const* name) {
3071 return v8::Handle<v8::Object>::Cast((*context)->Global()->Get(v8_str(name)));
3072}
Andrei Popescu31002712010-02-23 13:46:05 +00003073
3074
Leon Clarkef7060e22010-06-03 12:02:55 +01003075THREADED_TEST(DefineAPIAccessorOnObject) {
3076 v8::HandleScope scope;
3077 Local<ObjectTemplate> templ = ObjectTemplate::New();
3078 LocalContext context;
3079
3080 context->Global()->Set(v8_str("obj1"), templ->NewInstance());
3081 CompileRun("var obj2 = {};");
3082
3083 CHECK(CompileRun("obj1.x")->IsUndefined());
3084 CHECK(CompileRun("obj2.x")->IsUndefined());
3085
3086 CHECK(GetGlobalProperty(&context, "obj1")->
3087 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
3088
3089 ExpectString("obj1.x", "x");
3090 CHECK(CompileRun("obj2.x")->IsUndefined());
3091
3092 CHECK(GetGlobalProperty(&context, "obj2")->
3093 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
3094
3095 ExpectString("obj1.x", "x");
3096 ExpectString("obj2.x", "x");
3097
3098 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
3099 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
3100
3101 CompileRun("Object.defineProperty(obj1, 'x',"
3102 "{ get: function() { return 'y'; }, configurable: true })");
3103
3104 ExpectString("obj1.x", "y");
3105 ExpectString("obj2.x", "x");
3106
3107 CompileRun("Object.defineProperty(obj2, 'x',"
3108 "{ get: function() { return 'y'; }, configurable: true })");
3109
3110 ExpectString("obj1.x", "y");
3111 ExpectString("obj2.x", "y");
3112
3113 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
3114 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
3115
3116 CHECK(GetGlobalProperty(&context, "obj1")->
3117 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
3118 CHECK(GetGlobalProperty(&context, "obj2")->
3119 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
3120
3121 ExpectString("obj1.x", "x");
3122 ExpectString("obj2.x", "x");
3123
3124 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
3125 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
3126
3127 // Define getters/setters, but now make them not configurable.
3128 CompileRun("Object.defineProperty(obj1, 'x',"
3129 "{ get: function() { return 'z'; }, configurable: false })");
3130 CompileRun("Object.defineProperty(obj2, 'x',"
3131 "{ get: function() { return 'z'; }, configurable: false })");
3132
3133 ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
3134 ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
3135
3136 ExpectString("obj1.x", "z");
3137 ExpectString("obj2.x", "z");
3138
3139 CHECK(!GetGlobalProperty(&context, "obj1")->
3140 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
3141 CHECK(!GetGlobalProperty(&context, "obj2")->
3142 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
3143
3144 ExpectString("obj1.x", "z");
3145 ExpectString("obj2.x", "z");
3146}
3147
3148
3149THREADED_TEST(DontDeleteAPIAccessorsCannotBeOverriden) {
3150 v8::HandleScope scope;
3151 Local<ObjectTemplate> templ = ObjectTemplate::New();
3152 LocalContext context;
3153
3154 context->Global()->Set(v8_str("obj1"), templ->NewInstance());
3155 CompileRun("var obj2 = {};");
3156
3157 CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor(
3158 v8_str("x"),
3159 GetXValue, NULL,
3160 v8_str("donut"), v8::DEFAULT, v8::DontDelete));
3161 CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor(
3162 v8_str("x"),
3163 GetXValue, NULL,
3164 v8_str("donut"), v8::DEFAULT, v8::DontDelete));
3165
3166 ExpectString("obj1.x", "x");
3167 ExpectString("obj2.x", "x");
3168
3169 ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
3170 ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
3171
3172 CHECK(!GetGlobalProperty(&context, "obj1")->
3173 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
3174 CHECK(!GetGlobalProperty(&context, "obj2")->
3175 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
3176
3177 {
3178 v8::TryCatch try_catch;
3179 CompileRun("Object.defineProperty(obj1, 'x',"
3180 "{get: function() { return 'func'; }})");
3181 CHECK(try_catch.HasCaught());
3182 String::AsciiValue exception_value(try_catch.Exception());
3183 CHECK_EQ(*exception_value,
3184 "TypeError: Cannot redefine property: defineProperty");
3185 }
3186 {
3187 v8::TryCatch try_catch;
3188 CompileRun("Object.defineProperty(obj2, 'x',"
3189 "{get: function() { return 'func'; }})");
3190 CHECK(try_catch.HasCaught());
3191 String::AsciiValue exception_value(try_catch.Exception());
3192 CHECK_EQ(*exception_value,
3193 "TypeError: Cannot redefine property: defineProperty");
3194 }
3195}
3196
3197
3198static v8::Handle<Value> Get239Value(Local<String> name,
3199 const AccessorInfo& info) {
3200 ApiTestFuzzer::Fuzz();
3201 CHECK_EQ(info.Data(), v8_str("donut"));
3202 CHECK_EQ(name, v8_str("239"));
3203 return name;
3204}
3205
3206
3207THREADED_TEST(ElementAPIAccessor) {
3208 v8::HandleScope scope;
3209 Local<ObjectTemplate> templ = ObjectTemplate::New();
3210 LocalContext context;
3211
3212 context->Global()->Set(v8_str("obj1"), templ->NewInstance());
3213 CompileRun("var obj2 = {};");
3214
3215 CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor(
3216 v8_str("239"),
3217 Get239Value, NULL,
3218 v8_str("donut")));
3219 CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor(
3220 v8_str("239"),
3221 Get239Value, NULL,
3222 v8_str("donut")));
3223
3224 ExpectString("obj1[239]", "239");
3225 ExpectString("obj2[239]", "239");
3226 ExpectString("obj1['239']", "239");
3227 ExpectString("obj2['239']", "239");
3228}
3229
Steve Blocka7e24c12009-10-30 11:49:00 +00003230
3231v8::Persistent<Value> xValue;
3232
3233
3234static void SetXValue(Local<String> name,
3235 Local<Value> value,
3236 const AccessorInfo& info) {
3237 CHECK_EQ(value, v8_num(4));
3238 CHECK_EQ(info.Data(), v8_str("donut"));
3239 CHECK_EQ(name, v8_str("x"));
3240 CHECK(xValue.IsEmpty());
3241 xValue = v8::Persistent<Value>::New(value);
3242}
3243
3244
3245THREADED_TEST(SimplePropertyWrite) {
3246 v8::HandleScope scope;
3247 Local<ObjectTemplate> templ = ObjectTemplate::New();
3248 templ->SetAccessor(v8_str("x"), GetXValue, SetXValue, v8_str("donut"));
3249 LocalContext context;
3250 context->Global()->Set(v8_str("obj"), templ->NewInstance());
3251 Local<Script> script = Script::Compile(v8_str("obj.x = 4"));
3252 for (int i = 0; i < 10; i++) {
3253 CHECK(xValue.IsEmpty());
3254 script->Run();
3255 CHECK_EQ(v8_num(4), xValue);
3256 xValue.Dispose();
3257 xValue = v8::Persistent<Value>();
3258 }
3259}
3260
3261
3262static v8::Handle<Value> XPropertyGetter(Local<String> property,
3263 const AccessorInfo& info) {
3264 ApiTestFuzzer::Fuzz();
3265 CHECK(info.Data()->IsUndefined());
3266 return property;
3267}
3268
3269
3270THREADED_TEST(NamedInterceptorPropertyRead) {
3271 v8::HandleScope scope;
3272 Local<ObjectTemplate> templ = ObjectTemplate::New();
3273 templ->SetNamedPropertyHandler(XPropertyGetter);
3274 LocalContext context;
3275 context->Global()->Set(v8_str("obj"), templ->NewInstance());
3276 Local<Script> script = Script::Compile(v8_str("obj.x"));
3277 for (int i = 0; i < 10; i++) {
3278 Local<Value> result = script->Run();
3279 CHECK_EQ(result, v8_str("x"));
3280 }
3281}
3282
3283
Steve Block6ded16b2010-05-10 14:33:55 +01003284THREADED_TEST(NamedInterceptorDictionaryIC) {
3285 v8::HandleScope scope;
3286 Local<ObjectTemplate> templ = ObjectTemplate::New();
3287 templ->SetNamedPropertyHandler(XPropertyGetter);
3288 LocalContext context;
3289 // Create an object with a named interceptor.
3290 context->Global()->Set(v8_str("interceptor_obj"), templ->NewInstance());
3291 Local<Script> script = Script::Compile(v8_str("interceptor_obj.x"));
3292 for (int i = 0; i < 10; i++) {
3293 Local<Value> result = script->Run();
3294 CHECK_EQ(result, v8_str("x"));
3295 }
3296 // Create a slow case object and a function accessing a property in
3297 // that slow case object (with dictionary probing in generated
3298 // code). Then force object with a named interceptor into slow-case,
3299 // pass it to the function, and check that the interceptor is called
3300 // instead of accessing the local property.
3301 Local<Value> result =
3302 CompileRun("function get_x(o) { return o.x; };"
3303 "var obj = { x : 42, y : 0 };"
3304 "delete obj.y;"
3305 "for (var i = 0; i < 10; i++) get_x(obj);"
3306 "interceptor_obj.x = 42;"
3307 "interceptor_obj.y = 10;"
3308 "delete interceptor_obj.y;"
3309 "get_x(interceptor_obj)");
3310 CHECK_EQ(result, v8_str("x"));
3311}
3312
3313
Kristian Monsen0d5e1162010-09-30 15:31:59 +01003314THREADED_TEST(NamedInterceptorDictionaryICMultipleContext) {
3315 v8::HandleScope scope;
3316
3317 v8::Persistent<Context> context1 = Context::New();
3318
3319 context1->Enter();
3320 Local<ObjectTemplate> templ = ObjectTemplate::New();
3321 templ->SetNamedPropertyHandler(XPropertyGetter);
3322 // Create an object with a named interceptor.
3323 v8::Local<v8::Object> object = templ->NewInstance();
3324 context1->Global()->Set(v8_str("interceptor_obj"), object);
3325
3326 // Force the object into the slow case.
3327 CompileRun("interceptor_obj.y = 0;"
3328 "delete interceptor_obj.y;");
3329 context1->Exit();
3330
3331 {
3332 // Introduce the object into a different context.
3333 // Repeat named loads to exercise ICs.
3334 LocalContext context2;
3335 context2->Global()->Set(v8_str("interceptor_obj"), object);
3336 Local<Value> result =
3337 CompileRun("function get_x(o) { return o.x; }"
3338 "interceptor_obj.x = 42;"
3339 "for (var i=0; i != 10; i++) {"
3340 " get_x(interceptor_obj);"
3341 "}"
3342 "get_x(interceptor_obj)");
3343 // Check that the interceptor was actually invoked.
3344 CHECK_EQ(result, v8_str("x"));
3345 }
3346
3347 // Return to the original context and force some object to the slow case
3348 // to cause the NormalizedMapCache to verify.
3349 context1->Enter();
3350 CompileRun("var obj = { x : 0 }; delete obj.x;");
3351 context1->Exit();
3352
3353 context1.Dispose();
3354}
3355
3356
Andrei Popescu402d9372010-02-26 13:31:12 +00003357static v8::Handle<Value> SetXOnPrototypeGetter(Local<String> property,
3358 const AccessorInfo& info) {
3359 // Set x on the prototype object and do not handle the get request.
3360 v8::Handle<v8::Value> proto = info.Holder()->GetPrototype();
Steve Block6ded16b2010-05-10 14:33:55 +01003361 proto.As<v8::Object>()->Set(v8_str("x"), v8::Integer::New(23));
Andrei Popescu402d9372010-02-26 13:31:12 +00003362 return v8::Handle<Value>();
3363}
3364
3365
3366// This is a regression test for http://crbug.com/20104. Map
3367// transitions should not interfere with post interceptor lookup.
3368THREADED_TEST(NamedInterceptorMapTransitionRead) {
3369 v8::HandleScope scope;
3370 Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New();
3371 Local<v8::ObjectTemplate> instance_template
3372 = function_template->InstanceTemplate();
3373 instance_template->SetNamedPropertyHandler(SetXOnPrototypeGetter);
3374 LocalContext context;
3375 context->Global()->Set(v8_str("F"), function_template->GetFunction());
3376 // Create an instance of F and introduce a map transition for x.
3377 CompileRun("var o = new F(); o.x = 23;");
3378 // Create an instance of F and invoke the getter. The result should be 23.
3379 Local<Value> result = CompileRun("o = new F(); o.x");
3380 CHECK_EQ(result->Int32Value(), 23);
3381}
3382
3383
Steve Blocka7e24c12009-10-30 11:49:00 +00003384static v8::Handle<Value> IndexedPropertyGetter(uint32_t index,
3385 const AccessorInfo& info) {
3386 ApiTestFuzzer::Fuzz();
3387 if (index == 37) {
3388 return v8::Handle<Value>(v8_num(625));
3389 }
3390 return v8::Handle<Value>();
3391}
3392
3393
3394static v8::Handle<Value> IndexedPropertySetter(uint32_t index,
3395 Local<Value> value,
3396 const AccessorInfo& info) {
3397 ApiTestFuzzer::Fuzz();
3398 if (index == 39) {
3399 return value;
3400 }
3401 return v8::Handle<Value>();
3402}
3403
3404
3405THREADED_TEST(IndexedInterceptorWithIndexedAccessor) {
3406 v8::HandleScope scope;
3407 Local<ObjectTemplate> templ = ObjectTemplate::New();
3408 templ->SetIndexedPropertyHandler(IndexedPropertyGetter,
3409 IndexedPropertySetter);
3410 LocalContext context;
3411 context->Global()->Set(v8_str("obj"), templ->NewInstance());
3412 Local<Script> getter_script = Script::Compile(v8_str(
3413 "obj.__defineGetter__(\"3\", function(){return 5;});obj[3];"));
3414 Local<Script> setter_script = Script::Compile(v8_str(
3415 "obj.__defineSetter__(\"17\", function(val){this.foo = val;});"
3416 "obj[17] = 23;"
3417 "obj.foo;"));
3418 Local<Script> interceptor_setter_script = Script::Compile(v8_str(
3419 "obj.__defineSetter__(\"39\", function(val){this.foo = \"hit\";});"
3420 "obj[39] = 47;"
3421 "obj.foo;")); // This setter should not run, due to the interceptor.
3422 Local<Script> interceptor_getter_script = Script::Compile(v8_str(
3423 "obj[37];"));
3424 Local<Value> result = getter_script->Run();
3425 CHECK_EQ(v8_num(5), result);
3426 result = setter_script->Run();
3427 CHECK_EQ(v8_num(23), result);
3428 result = interceptor_setter_script->Run();
3429 CHECK_EQ(v8_num(23), result);
3430 result = interceptor_getter_script->Run();
3431 CHECK_EQ(v8_num(625), result);
3432}
3433
3434
Leon Clarked91b9f72010-01-27 17:25:45 +00003435static v8::Handle<Value> IdentityIndexedPropertyGetter(
3436 uint32_t index,
3437 const AccessorInfo& info) {
Ben Murdochf87a2032010-10-22 12:50:53 +01003438 return v8::Integer::NewFromUnsigned(index);
Leon Clarked91b9f72010-01-27 17:25:45 +00003439}
3440
3441
Kristian Monsen0d5e1162010-09-30 15:31:59 +01003442THREADED_TEST(IndexedInterceptorWithGetOwnPropertyDescriptor) {
3443 v8::HandleScope scope;
3444 Local<ObjectTemplate> templ = ObjectTemplate::New();
3445 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3446
3447 LocalContext context;
3448 context->Global()->Set(v8_str("obj"), templ->NewInstance());
3449
3450 // Check fast object case.
3451 const char* fast_case_code =
3452 "Object.getOwnPropertyDescriptor(obj, 0).value.toString()";
3453 ExpectString(fast_case_code, "0");
3454
3455 // Check slow case.
3456 const char* slow_case_code =
3457 "obj.x = 1; delete obj.x;"
3458 "Object.getOwnPropertyDescriptor(obj, 1).value.toString()";
3459 ExpectString(slow_case_code, "1");
3460}
3461
3462
Leon Clarked91b9f72010-01-27 17:25:45 +00003463THREADED_TEST(IndexedInterceptorWithNoSetter) {
3464 v8::HandleScope scope;
3465 Local<ObjectTemplate> templ = ObjectTemplate::New();
3466 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3467
3468 LocalContext context;
3469 context->Global()->Set(v8_str("obj"), templ->NewInstance());
3470
3471 const char* code =
3472 "try {"
3473 " obj[0] = 239;"
3474 " for (var i = 0; i < 100; i++) {"
3475 " var v = obj[0];"
3476 " if (v != 0) throw 'Wrong value ' + v + ' at iteration ' + i;"
3477 " }"
3478 " 'PASSED'"
3479 "} catch(e) {"
3480 " e"
3481 "}";
3482 ExpectString(code, "PASSED");
3483}
3484
3485
Andrei Popescu402d9372010-02-26 13:31:12 +00003486THREADED_TEST(IndexedInterceptorWithAccessorCheck) {
3487 v8::HandleScope scope;
3488 Local<ObjectTemplate> templ = ObjectTemplate::New();
3489 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3490
3491 LocalContext context;
3492 Local<v8::Object> obj = templ->NewInstance();
3493 obj->TurnOnAccessCheck();
3494 context->Global()->Set(v8_str("obj"), obj);
3495
3496 const char* code =
3497 "try {"
3498 " for (var i = 0; i < 100; i++) {"
3499 " var v = obj[0];"
3500 " if (v != undefined) throw 'Wrong value ' + v + ' at iteration ' + i;"
3501 " }"
3502 " 'PASSED'"
3503 "} catch(e) {"
3504 " e"
3505 "}";
3506 ExpectString(code, "PASSED");
3507}
3508
3509
3510THREADED_TEST(IndexedInterceptorWithAccessorCheckSwitchedOn) {
3511 i::FLAG_allow_natives_syntax = true;
3512 v8::HandleScope scope;
3513 Local<ObjectTemplate> templ = ObjectTemplate::New();
3514 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3515
3516 LocalContext context;
3517 Local<v8::Object> obj = templ->NewInstance();
3518 context->Global()->Set(v8_str("obj"), obj);
3519
3520 const char* code =
3521 "try {"
3522 " for (var i = 0; i < 100; i++) {"
3523 " var expected = i;"
3524 " if (i == 5) {"
3525 " %EnableAccessChecks(obj);"
3526 " expected = undefined;"
3527 " }"
3528 " var v = obj[i];"
3529 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3530 " if (i == 5) %DisableAccessChecks(obj);"
3531 " }"
3532 " 'PASSED'"
3533 "} catch(e) {"
3534 " e"
3535 "}";
3536 ExpectString(code, "PASSED");
3537}
3538
3539
3540THREADED_TEST(IndexedInterceptorWithDifferentIndices) {
3541 v8::HandleScope scope;
3542 Local<ObjectTemplate> templ = ObjectTemplate::New();
3543 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3544
3545 LocalContext context;
3546 Local<v8::Object> obj = templ->NewInstance();
3547 context->Global()->Set(v8_str("obj"), obj);
3548
3549 const char* code =
3550 "try {"
3551 " for (var i = 0; i < 100; i++) {"
3552 " var v = obj[i];"
3553 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;"
3554 " }"
3555 " 'PASSED'"
3556 "} catch(e) {"
3557 " e"
3558 "}";
3559 ExpectString(code, "PASSED");
3560}
3561
3562
Ben Murdochf87a2032010-10-22 12:50:53 +01003563THREADED_TEST(IndexedInterceptorWithNegativeIndices) {
3564 v8::HandleScope scope;
3565 Local<ObjectTemplate> templ = ObjectTemplate::New();
3566 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3567
3568 LocalContext context;
3569 Local<v8::Object> obj = templ->NewInstance();
3570 context->Global()->Set(v8_str("obj"), obj);
3571
3572 const char* code =
3573 "try {"
3574 " for (var i = 0; i < 100; i++) {"
3575 " var expected = i;"
3576 " var key = i;"
3577 " if (i == 25) {"
3578 " key = -1;"
3579 " expected = undefined;"
3580 " }"
3581 " if (i == 50) {"
3582 " /* probe minimal Smi number on 32-bit platforms */"
3583 " key = -(1 << 30);"
3584 " expected = undefined;"
3585 " }"
3586 " if (i == 75) {"
3587 " /* probe minimal Smi number on 64-bit platforms */"
3588 " key = 1 << 31;"
3589 " expected = undefined;"
3590 " }"
3591 " var v = obj[key];"
3592 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3593 " }"
3594 " 'PASSED'"
3595 "} catch(e) {"
3596 " e"
3597 "}";
3598 ExpectString(code, "PASSED");
3599}
3600
3601
Andrei Popescu402d9372010-02-26 13:31:12 +00003602THREADED_TEST(IndexedInterceptorWithNotSmiLookup) {
3603 v8::HandleScope scope;
3604 Local<ObjectTemplate> templ = ObjectTemplate::New();
3605 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3606
3607 LocalContext context;
3608 Local<v8::Object> obj = templ->NewInstance();
3609 context->Global()->Set(v8_str("obj"), obj);
3610
3611 const char* code =
3612 "try {"
3613 " for (var i = 0; i < 100; i++) {"
3614 " var expected = i;"
Ben Murdochf87a2032010-10-22 12:50:53 +01003615 " var key = i;"
Andrei Popescu402d9372010-02-26 13:31:12 +00003616 " if (i == 50) {"
Ben Murdochf87a2032010-10-22 12:50:53 +01003617 " key = 'foobar';"
Andrei Popescu402d9372010-02-26 13:31:12 +00003618 " expected = undefined;"
3619 " }"
Ben Murdochf87a2032010-10-22 12:50:53 +01003620 " var v = obj[key];"
Andrei Popescu402d9372010-02-26 13:31:12 +00003621 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3622 " }"
3623 " 'PASSED'"
3624 "} catch(e) {"
3625 " e"
3626 "}";
3627 ExpectString(code, "PASSED");
3628}
3629
3630
3631THREADED_TEST(IndexedInterceptorGoingMegamorphic) {
3632 v8::HandleScope scope;
3633 Local<ObjectTemplate> templ = ObjectTemplate::New();
3634 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3635
3636 LocalContext context;
3637 Local<v8::Object> obj = templ->NewInstance();
3638 context->Global()->Set(v8_str("obj"), obj);
3639
3640 const char* code =
3641 "var original = obj;"
3642 "try {"
3643 " for (var i = 0; i < 100; i++) {"
3644 " var expected = i;"
3645 " if (i == 50) {"
3646 " obj = {50: 'foobar'};"
3647 " expected = 'foobar';"
3648 " }"
3649 " var v = obj[i];"
3650 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3651 " if (i == 50) obj = original;"
3652 " }"
3653 " 'PASSED'"
3654 "} catch(e) {"
3655 " e"
3656 "}";
3657 ExpectString(code, "PASSED");
3658}
3659
3660
3661THREADED_TEST(IndexedInterceptorReceiverTurningSmi) {
3662 v8::HandleScope scope;
3663 Local<ObjectTemplate> templ = ObjectTemplate::New();
3664 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3665
3666 LocalContext context;
3667 Local<v8::Object> obj = templ->NewInstance();
3668 context->Global()->Set(v8_str("obj"), obj);
3669
3670 const char* code =
3671 "var original = obj;"
3672 "try {"
3673 " for (var i = 0; i < 100; i++) {"
3674 " var expected = i;"
3675 " if (i == 5) {"
3676 " obj = 239;"
3677 " expected = undefined;"
3678 " }"
3679 " var v = obj[i];"
3680 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3681 " if (i == 5) obj = original;"
3682 " }"
3683 " 'PASSED'"
3684 "} catch(e) {"
3685 " e"
3686 "}";
3687 ExpectString(code, "PASSED");
3688}
3689
3690
3691THREADED_TEST(IndexedInterceptorOnProto) {
3692 v8::HandleScope scope;
3693 Local<ObjectTemplate> templ = ObjectTemplate::New();
3694 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3695
3696 LocalContext context;
3697 Local<v8::Object> obj = templ->NewInstance();
3698 context->Global()->Set(v8_str("obj"), obj);
3699
3700 const char* code =
3701 "var o = {__proto__: obj};"
3702 "try {"
3703 " for (var i = 0; i < 100; i++) {"
3704 " var v = o[i];"
3705 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;"
3706 " }"
3707 " 'PASSED'"
3708 "} catch(e) {"
3709 " e"
3710 "}";
3711 ExpectString(code, "PASSED");
3712}
3713
3714
Steve Blocka7e24c12009-10-30 11:49:00 +00003715THREADED_TEST(MultiContexts) {
3716 v8::HandleScope scope;
3717 v8::Handle<ObjectTemplate> templ = ObjectTemplate::New();
3718 templ->Set(v8_str("dummy"), v8::FunctionTemplate::New(DummyCallHandler));
3719
3720 Local<String> password = v8_str("Password");
3721
3722 // Create an environment
3723 LocalContext context0(0, templ);
3724 context0->SetSecurityToken(password);
3725 v8::Handle<v8::Object> global0 = context0->Global();
3726 global0->Set(v8_str("custom"), v8_num(1234));
3727 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value());
3728
3729 // Create an independent environment
3730 LocalContext context1(0, templ);
3731 context1->SetSecurityToken(password);
3732 v8::Handle<v8::Object> global1 = context1->Global();
3733 global1->Set(v8_str("custom"), v8_num(1234));
3734 CHECK_NE(global0, global1);
3735 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value());
3736 CHECK_EQ(1234, global1->Get(v8_str("custom"))->Int32Value());
3737
3738 // Now create a new context with the old global
3739 LocalContext context2(0, templ, global1);
3740 context2->SetSecurityToken(password);
3741 v8::Handle<v8::Object> global2 = context2->Global();
3742 CHECK_EQ(global1, global2);
3743 CHECK_EQ(0, global1->Get(v8_str("custom"))->Int32Value());
3744 CHECK_EQ(0, global2->Get(v8_str("custom"))->Int32Value());
3745}
3746
3747
3748THREADED_TEST(FunctionPrototypeAcrossContexts) {
3749 // Make sure that functions created by cloning boilerplates cannot
3750 // communicate through their __proto__ field.
3751
3752 v8::HandleScope scope;
3753
3754 LocalContext env0;
3755 v8::Handle<v8::Object> global0 =
3756 env0->Global();
3757 v8::Handle<v8::Object> object0 =
Steve Block6ded16b2010-05-10 14:33:55 +01003758 global0->Get(v8_str("Object")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003759 v8::Handle<v8::Object> tostring0 =
Steve Block6ded16b2010-05-10 14:33:55 +01003760 object0->Get(v8_str("toString")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003761 v8::Handle<v8::Object> proto0 =
Steve Block6ded16b2010-05-10 14:33:55 +01003762 tostring0->Get(v8_str("__proto__")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003763 proto0->Set(v8_str("custom"), v8_num(1234));
3764
3765 LocalContext env1;
3766 v8::Handle<v8::Object> global1 =
3767 env1->Global();
3768 v8::Handle<v8::Object> object1 =
Steve Block6ded16b2010-05-10 14:33:55 +01003769 global1->Get(v8_str("Object")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003770 v8::Handle<v8::Object> tostring1 =
Steve Block6ded16b2010-05-10 14:33:55 +01003771 object1->Get(v8_str("toString")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003772 v8::Handle<v8::Object> proto1 =
Steve Block6ded16b2010-05-10 14:33:55 +01003773 tostring1->Get(v8_str("__proto__")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003774 CHECK(!proto1->Has(v8_str("custom")));
3775}
3776
3777
3778THREADED_TEST(Regress892105) {
3779 // Make sure that object and array literals created by cloning
3780 // boilerplates cannot communicate through their __proto__
3781 // field. This is rather difficult to check, but we try to add stuff
3782 // to Object.prototype and Array.prototype and create a new
3783 // environment. This should succeed.
3784
3785 v8::HandleScope scope;
3786
3787 Local<String> source = v8_str("Object.prototype.obj = 1234;"
3788 "Array.prototype.arr = 4567;"
3789 "8901");
3790
3791 LocalContext env0;
3792 Local<Script> script0 = Script::Compile(source);
3793 CHECK_EQ(8901.0, script0->Run()->NumberValue());
3794
3795 LocalContext env1;
3796 Local<Script> script1 = Script::Compile(source);
3797 CHECK_EQ(8901.0, script1->Run()->NumberValue());
3798}
3799
3800
Steve Blocka7e24c12009-10-30 11:49:00 +00003801THREADED_TEST(UndetectableObject) {
3802 v8::HandleScope scope;
3803 LocalContext env;
3804
3805 Local<v8::FunctionTemplate> desc =
3806 v8::FunctionTemplate::New(0, v8::Handle<Value>());
3807 desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable
3808
3809 Local<v8::Object> obj = desc->GetFunction()->NewInstance();
3810 env->Global()->Set(v8_str("undetectable"), obj);
3811
3812 ExpectString("undetectable.toString()", "[object Object]");
3813 ExpectString("typeof undetectable", "undefined");
3814 ExpectString("typeof(undetectable)", "undefined");
3815 ExpectBoolean("typeof undetectable == 'undefined'", true);
3816 ExpectBoolean("typeof undetectable == 'object'", false);
3817 ExpectBoolean("if (undetectable) { true; } else { false; }", false);
3818 ExpectBoolean("!undetectable", true);
3819
3820 ExpectObject("true&&undetectable", obj);
3821 ExpectBoolean("false&&undetectable", false);
3822 ExpectBoolean("true||undetectable", true);
3823 ExpectObject("false||undetectable", obj);
3824
3825 ExpectObject("undetectable&&true", obj);
3826 ExpectObject("undetectable&&false", obj);
3827 ExpectBoolean("undetectable||true", true);
3828 ExpectBoolean("undetectable||false", false);
3829
3830 ExpectBoolean("undetectable==null", true);
3831 ExpectBoolean("null==undetectable", true);
3832 ExpectBoolean("undetectable==undefined", true);
3833 ExpectBoolean("undefined==undetectable", true);
3834 ExpectBoolean("undetectable==undetectable", true);
3835
3836
3837 ExpectBoolean("undetectable===null", false);
3838 ExpectBoolean("null===undetectable", false);
3839 ExpectBoolean("undetectable===undefined", false);
3840 ExpectBoolean("undefined===undetectable", false);
3841 ExpectBoolean("undetectable===undetectable", true);
3842}
3843
3844
Steve Block8defd9f2010-07-08 12:39:36 +01003845
3846THREADED_TEST(ExtensibleOnUndetectable) {
3847 v8::HandleScope scope;
3848 LocalContext env;
3849
3850 Local<v8::FunctionTemplate> desc =
3851 v8::FunctionTemplate::New(0, v8::Handle<Value>());
3852 desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable
3853
3854 Local<v8::Object> obj = desc->GetFunction()->NewInstance();
3855 env->Global()->Set(v8_str("undetectable"), obj);
3856
3857 Local<String> source = v8_str("undetectable.x = 42;"
3858 "undetectable.x");
3859
3860 Local<Script> script = Script::Compile(source);
3861
3862 CHECK_EQ(v8::Integer::New(42), script->Run());
3863
3864 ExpectBoolean("Object.isExtensible(undetectable)", true);
3865
3866 source = v8_str("Object.preventExtensions(undetectable);");
3867 script = Script::Compile(source);
3868 script->Run();
3869 ExpectBoolean("Object.isExtensible(undetectable)", false);
3870
3871 source = v8_str("undetectable.y = 2000;");
3872 script = Script::Compile(source);
Steve Block8defd9f2010-07-08 12:39:36 +01003873 Local<Value> result = script->Run();
Steve Block44f0eee2011-05-26 01:26:41 +01003874 ExpectBoolean("undetectable.y == undefined", true);
Steve Block8defd9f2010-07-08 12:39:36 +01003875}
3876
3877
3878
Steve Blocka7e24c12009-10-30 11:49:00 +00003879THREADED_TEST(UndetectableString) {
3880 v8::HandleScope scope;
3881 LocalContext env;
3882
3883 Local<String> obj = String::NewUndetectable("foo");
3884 env->Global()->Set(v8_str("undetectable"), obj);
3885
3886 ExpectString("undetectable", "foo");
3887 ExpectString("typeof undetectable", "undefined");
3888 ExpectString("typeof(undetectable)", "undefined");
3889 ExpectBoolean("typeof undetectable == 'undefined'", true);
3890 ExpectBoolean("typeof undetectable == 'string'", false);
3891 ExpectBoolean("if (undetectable) { true; } else { false; }", false);
3892 ExpectBoolean("!undetectable", true);
3893
3894 ExpectObject("true&&undetectable", obj);
3895 ExpectBoolean("false&&undetectable", false);
3896 ExpectBoolean("true||undetectable", true);
3897 ExpectObject("false||undetectable", obj);
3898
3899 ExpectObject("undetectable&&true", obj);
3900 ExpectObject("undetectable&&false", obj);
3901 ExpectBoolean("undetectable||true", true);
3902 ExpectBoolean("undetectable||false", false);
3903
3904 ExpectBoolean("undetectable==null", true);
3905 ExpectBoolean("null==undetectable", true);
3906 ExpectBoolean("undetectable==undefined", true);
3907 ExpectBoolean("undefined==undetectable", true);
3908 ExpectBoolean("undetectable==undetectable", true);
3909
3910
3911 ExpectBoolean("undetectable===null", false);
3912 ExpectBoolean("null===undetectable", false);
3913 ExpectBoolean("undetectable===undefined", false);
3914 ExpectBoolean("undefined===undetectable", false);
3915 ExpectBoolean("undetectable===undetectable", true);
3916}
3917
3918
3919template <typename T> static void USE(T) { }
3920
3921
3922// This test is not intended to be run, just type checked.
3923static void PersistentHandles() {
3924 USE(PersistentHandles);
3925 Local<String> str = v8_str("foo");
3926 v8::Persistent<String> p_str = v8::Persistent<String>::New(str);
3927 USE(p_str);
3928 Local<Script> scr = Script::Compile(v8_str(""));
3929 v8::Persistent<Script> p_scr = v8::Persistent<Script>::New(scr);
3930 USE(p_scr);
3931 Local<ObjectTemplate> templ = ObjectTemplate::New();
3932 v8::Persistent<ObjectTemplate> p_templ =
3933 v8::Persistent<ObjectTemplate>::New(templ);
3934 USE(p_templ);
3935}
3936
3937
3938static v8::Handle<Value> HandleLogDelegator(const v8::Arguments& args) {
3939 ApiTestFuzzer::Fuzz();
3940 return v8::Undefined();
3941}
3942
3943
3944THREADED_TEST(GlobalObjectTemplate) {
3945 v8::HandleScope handle_scope;
3946 Local<ObjectTemplate> global_template = ObjectTemplate::New();
3947 global_template->Set(v8_str("JSNI_Log"),
3948 v8::FunctionTemplate::New(HandleLogDelegator));
3949 v8::Persistent<Context> context = Context::New(0, global_template);
3950 Context::Scope context_scope(context);
3951 Script::Compile(v8_str("JSNI_Log('LOG')"))->Run();
3952 context.Dispose();
3953}
3954
3955
3956static const char* kSimpleExtensionSource =
3957 "function Foo() {"
3958 " return 4;"
3959 "}";
3960
3961
3962THREADED_TEST(SimpleExtensions) {
3963 v8::HandleScope handle_scope;
3964 v8::RegisterExtension(new Extension("simpletest", kSimpleExtensionSource));
3965 const char* extension_names[] = { "simpletest" };
3966 v8::ExtensionConfiguration extensions(1, extension_names);
3967 v8::Handle<Context> context = Context::New(&extensions);
3968 Context::Scope lock(context);
3969 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run();
3970 CHECK_EQ(result, v8::Integer::New(4));
3971}
3972
3973
3974static const char* kEvalExtensionSource1 =
3975 "function UseEval1() {"
3976 " var x = 42;"
3977 " return eval('x');"
3978 "}";
3979
3980
3981static const char* kEvalExtensionSource2 =
3982 "(function() {"
3983 " var x = 42;"
3984 " function e() {"
3985 " return eval('x');"
3986 " }"
3987 " this.UseEval2 = e;"
3988 "})()";
3989
3990
3991THREADED_TEST(UseEvalFromExtension) {
3992 v8::HandleScope handle_scope;
3993 v8::RegisterExtension(new Extension("evaltest1", kEvalExtensionSource1));
3994 v8::RegisterExtension(new Extension("evaltest2", kEvalExtensionSource2));
3995 const char* extension_names[] = { "evaltest1", "evaltest2" };
3996 v8::ExtensionConfiguration extensions(2, extension_names);
3997 v8::Handle<Context> context = Context::New(&extensions);
3998 Context::Scope lock(context);
3999 v8::Handle<Value> result = Script::Compile(v8_str("UseEval1()"))->Run();
4000 CHECK_EQ(result, v8::Integer::New(42));
4001 result = Script::Compile(v8_str("UseEval2()"))->Run();
4002 CHECK_EQ(result, v8::Integer::New(42));
4003}
4004
4005
4006static const char* kWithExtensionSource1 =
4007 "function UseWith1() {"
4008 " var x = 42;"
4009 " with({x:87}) { return x; }"
4010 "}";
4011
4012
4013
4014static const char* kWithExtensionSource2 =
4015 "(function() {"
4016 " var x = 42;"
4017 " function e() {"
4018 " with ({x:87}) { return x; }"
4019 " }"
4020 " this.UseWith2 = e;"
4021 "})()";
4022
4023
4024THREADED_TEST(UseWithFromExtension) {
4025 v8::HandleScope handle_scope;
4026 v8::RegisterExtension(new Extension("withtest1", kWithExtensionSource1));
4027 v8::RegisterExtension(new Extension("withtest2", kWithExtensionSource2));
4028 const char* extension_names[] = { "withtest1", "withtest2" };
4029 v8::ExtensionConfiguration extensions(2, extension_names);
4030 v8::Handle<Context> context = Context::New(&extensions);
4031 Context::Scope lock(context);
4032 v8::Handle<Value> result = Script::Compile(v8_str("UseWith1()"))->Run();
4033 CHECK_EQ(result, v8::Integer::New(87));
4034 result = Script::Compile(v8_str("UseWith2()"))->Run();
4035 CHECK_EQ(result, v8::Integer::New(87));
4036}
4037
4038
4039THREADED_TEST(AutoExtensions) {
4040 v8::HandleScope handle_scope;
4041 Extension* extension = new Extension("autotest", kSimpleExtensionSource);
4042 extension->set_auto_enable(true);
4043 v8::RegisterExtension(extension);
4044 v8::Handle<Context> context = Context::New();
4045 Context::Scope lock(context);
4046 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run();
4047 CHECK_EQ(result, v8::Integer::New(4));
4048}
4049
4050
Steve Blockd0582a62009-12-15 09:54:21 +00004051static const char* kSyntaxErrorInExtensionSource =
4052 "[";
4053
4054
4055// Test that a syntax error in an extension does not cause a fatal
4056// error but results in an empty context.
4057THREADED_TEST(SyntaxErrorExtensions) {
4058 v8::HandleScope handle_scope;
4059 v8::RegisterExtension(new Extension("syntaxerror",
4060 kSyntaxErrorInExtensionSource));
4061 const char* extension_names[] = { "syntaxerror" };
4062 v8::ExtensionConfiguration extensions(1, extension_names);
4063 v8::Handle<Context> context = Context::New(&extensions);
4064 CHECK(context.IsEmpty());
4065}
4066
4067
4068static const char* kExceptionInExtensionSource =
4069 "throw 42";
4070
4071
4072// Test that an exception when installing an extension does not cause
4073// a fatal error but results in an empty context.
4074THREADED_TEST(ExceptionExtensions) {
4075 v8::HandleScope handle_scope;
4076 v8::RegisterExtension(new Extension("exception",
4077 kExceptionInExtensionSource));
4078 const char* extension_names[] = { "exception" };
4079 v8::ExtensionConfiguration extensions(1, extension_names);
4080 v8::Handle<Context> context = Context::New(&extensions);
4081 CHECK(context.IsEmpty());
4082}
4083
4084
Iain Merrick9ac36c92010-09-13 15:29:50 +01004085static const char* kNativeCallInExtensionSource =
4086 "function call_runtime_last_index_of(x) {"
4087 " return %StringLastIndexOf(x, 'bob', 10);"
4088 "}";
4089
4090
4091static const char* kNativeCallTest =
4092 "call_runtime_last_index_of('bobbobboellebobboellebobbob');";
4093
4094// Test that a native runtime calls are supported in extensions.
4095THREADED_TEST(NativeCallInExtensions) {
4096 v8::HandleScope handle_scope;
4097 v8::RegisterExtension(new Extension("nativecall",
4098 kNativeCallInExtensionSource));
4099 const char* extension_names[] = { "nativecall" };
4100 v8::ExtensionConfiguration extensions(1, extension_names);
4101 v8::Handle<Context> context = Context::New(&extensions);
4102 Context::Scope lock(context);
4103 v8::Handle<Value> result = Script::Compile(v8_str(kNativeCallTest))->Run();
4104 CHECK_EQ(result, v8::Integer::New(3));
4105}
4106
4107
Steve Blocka7e24c12009-10-30 11:49:00 +00004108static void CheckDependencies(const char* name, const char* expected) {
4109 v8::HandleScope handle_scope;
4110 v8::ExtensionConfiguration config(1, &name);
4111 LocalContext context(&config);
4112 CHECK_EQ(String::New(expected), context->Global()->Get(v8_str("loaded")));
4113}
4114
4115
4116/*
4117 * Configuration:
4118 *
4119 * /-- B <--\
4120 * A <- -- D <-- E
4121 * \-- C <--/
4122 */
4123THREADED_TEST(ExtensionDependency) {
4124 static const char* kEDeps[] = { "D" };
4125 v8::RegisterExtension(new Extension("E", "this.loaded += 'E';", 1, kEDeps));
4126 static const char* kDDeps[] = { "B", "C" };
4127 v8::RegisterExtension(new Extension("D", "this.loaded += 'D';", 2, kDDeps));
4128 static const char* kBCDeps[] = { "A" };
4129 v8::RegisterExtension(new Extension("B", "this.loaded += 'B';", 1, kBCDeps));
4130 v8::RegisterExtension(new Extension("C", "this.loaded += 'C';", 1, kBCDeps));
4131 v8::RegisterExtension(new Extension("A", "this.loaded += 'A';"));
4132 CheckDependencies("A", "undefinedA");
4133 CheckDependencies("B", "undefinedAB");
4134 CheckDependencies("C", "undefinedAC");
4135 CheckDependencies("D", "undefinedABCD");
4136 CheckDependencies("E", "undefinedABCDE");
4137 v8::HandleScope handle_scope;
4138 static const char* exts[2] = { "C", "E" };
4139 v8::ExtensionConfiguration config(2, exts);
4140 LocalContext context(&config);
4141 CHECK_EQ(v8_str("undefinedACBDE"), context->Global()->Get(v8_str("loaded")));
4142}
4143
4144
4145static const char* kExtensionTestScript =
4146 "native function A();"
4147 "native function B();"
4148 "native function C();"
4149 "function Foo(i) {"
4150 " if (i == 0) return A();"
4151 " if (i == 1) return B();"
4152 " if (i == 2) return C();"
4153 "}";
4154
4155
4156static v8::Handle<Value> CallFun(const v8::Arguments& args) {
4157 ApiTestFuzzer::Fuzz();
Leon Clarkee46be812010-01-19 14:06:41 +00004158 if (args.IsConstructCall()) {
4159 args.This()->Set(v8_str("data"), args.Data());
4160 return v8::Null();
4161 }
Steve Blocka7e24c12009-10-30 11:49:00 +00004162 return args.Data();
4163}
4164
4165
4166class FunctionExtension : public Extension {
4167 public:
4168 FunctionExtension() : Extension("functiontest", kExtensionTestScript) { }
4169 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(
4170 v8::Handle<String> name);
4171};
4172
4173
4174static int lookup_count = 0;
4175v8::Handle<v8::FunctionTemplate> FunctionExtension::GetNativeFunction(
4176 v8::Handle<String> name) {
4177 lookup_count++;
4178 if (name->Equals(v8_str("A"))) {
4179 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(8));
4180 } else if (name->Equals(v8_str("B"))) {
4181 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(7));
4182 } else if (name->Equals(v8_str("C"))) {
4183 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(6));
4184 } else {
4185 return v8::Handle<v8::FunctionTemplate>();
4186 }
4187}
4188
4189
4190THREADED_TEST(FunctionLookup) {
4191 v8::RegisterExtension(new FunctionExtension());
4192 v8::HandleScope handle_scope;
4193 static const char* exts[1] = { "functiontest" };
4194 v8::ExtensionConfiguration config(1, exts);
4195 LocalContext context(&config);
4196 CHECK_EQ(3, lookup_count);
4197 CHECK_EQ(v8::Integer::New(8), Script::Compile(v8_str("Foo(0)"))->Run());
4198 CHECK_EQ(v8::Integer::New(7), Script::Compile(v8_str("Foo(1)"))->Run());
4199 CHECK_EQ(v8::Integer::New(6), Script::Compile(v8_str("Foo(2)"))->Run());
4200}
4201
4202
Leon Clarkee46be812010-01-19 14:06:41 +00004203THREADED_TEST(NativeFunctionConstructCall) {
4204 v8::RegisterExtension(new FunctionExtension());
4205 v8::HandleScope handle_scope;
4206 static const char* exts[1] = { "functiontest" };
4207 v8::ExtensionConfiguration config(1, exts);
4208 LocalContext context(&config);
Leon Clarked91b9f72010-01-27 17:25:45 +00004209 for (int i = 0; i < 10; i++) {
4210 // Run a few times to ensure that allocation of objects doesn't
4211 // change behavior of a constructor function.
4212 CHECK_EQ(v8::Integer::New(8),
4213 Script::Compile(v8_str("(new A()).data"))->Run());
4214 CHECK_EQ(v8::Integer::New(7),
4215 Script::Compile(v8_str("(new B()).data"))->Run());
4216 CHECK_EQ(v8::Integer::New(6),
4217 Script::Compile(v8_str("(new C()).data"))->Run());
4218 }
Leon Clarkee46be812010-01-19 14:06:41 +00004219}
4220
4221
Steve Blocka7e24c12009-10-30 11:49:00 +00004222static const char* last_location;
4223static const char* last_message;
4224void StoringErrorCallback(const char* location, const char* message) {
4225 if (last_location == NULL) {
4226 last_location = location;
4227 last_message = message;
4228 }
4229}
4230
4231
4232// ErrorReporting creates a circular extensions configuration and
4233// tests that the fatal error handler gets called. This renders V8
4234// unusable and therefore this test cannot be run in parallel.
4235TEST(ErrorReporting) {
4236 v8::V8::SetFatalErrorHandler(StoringErrorCallback);
4237 static const char* aDeps[] = { "B" };
4238 v8::RegisterExtension(new Extension("A", "", 1, aDeps));
4239 static const char* bDeps[] = { "A" };
4240 v8::RegisterExtension(new Extension("B", "", 1, bDeps));
4241 last_location = NULL;
4242 v8::ExtensionConfiguration config(1, bDeps);
4243 v8::Handle<Context> context = Context::New(&config);
4244 CHECK(context.IsEmpty());
4245 CHECK_NE(last_location, NULL);
4246}
4247
4248
4249static const char* js_code_causing_huge_string_flattening =
4250 "var str = 'X';"
4251 "for (var i = 0; i < 30; i++) {"
4252 " str = str + str;"
4253 "}"
4254 "str.match(/X/);";
4255
4256
4257void OOMCallback(const char* location, const char* message) {
4258 exit(0);
4259}
4260
4261
4262TEST(RegexpOutOfMemory) {
4263 // Execute a script that causes out of memory when flattening a string.
4264 v8::HandleScope scope;
4265 v8::V8::SetFatalErrorHandler(OOMCallback);
4266 LocalContext context;
4267 Local<Script> script =
4268 Script::Compile(String::New(js_code_causing_huge_string_flattening));
4269 last_location = NULL;
4270 Local<Value> result = script->Run();
4271
4272 CHECK(false); // Should not return.
4273}
4274
4275
4276static void MissingScriptInfoMessageListener(v8::Handle<v8::Message> message,
4277 v8::Handle<Value> data) {
4278 CHECK_EQ(v8::Undefined(), data);
4279 CHECK(message->GetScriptResourceName()->IsUndefined());
4280 CHECK_EQ(v8::Undefined(), message->GetScriptResourceName());
4281 message->GetLineNumber();
4282 message->GetSourceLine();
4283}
4284
4285
4286THREADED_TEST(ErrorWithMissingScriptInfo) {
4287 v8::HandleScope scope;
4288 LocalContext context;
4289 v8::V8::AddMessageListener(MissingScriptInfoMessageListener);
4290 Script::Compile(v8_str("throw Error()"))->Run();
4291 v8::V8::RemoveMessageListeners(MissingScriptInfoMessageListener);
4292}
4293
4294
4295int global_index = 0;
4296
4297class Snorkel {
4298 public:
4299 Snorkel() { index_ = global_index++; }
4300 int index_;
4301};
4302
4303class Whammy {
4304 public:
4305 Whammy() {
4306 cursor_ = 0;
4307 }
4308 ~Whammy() {
4309 script_.Dispose();
4310 }
4311 v8::Handle<Script> getScript() {
4312 if (script_.IsEmpty())
4313 script_ = v8::Persistent<Script>::New(v8_compile("({}).blammo"));
4314 return Local<Script>(*script_);
4315 }
4316
4317 public:
4318 static const int kObjectCount = 256;
4319 int cursor_;
4320 v8::Persistent<v8::Object> objects_[kObjectCount];
4321 v8::Persistent<Script> script_;
4322};
4323
4324static void HandleWeakReference(v8::Persistent<v8::Value> obj, void* data) {
4325 Snorkel* snorkel = reinterpret_cast<Snorkel*>(data);
4326 delete snorkel;
4327 obj.ClearWeak();
4328}
4329
4330v8::Handle<Value> WhammyPropertyGetter(Local<String> name,
4331 const AccessorInfo& info) {
4332 Whammy* whammy =
4333 static_cast<Whammy*>(v8::Handle<v8::External>::Cast(info.Data())->Value());
4334
4335 v8::Persistent<v8::Object> prev = whammy->objects_[whammy->cursor_];
4336
4337 v8::Handle<v8::Object> obj = v8::Object::New();
4338 v8::Persistent<v8::Object> global = v8::Persistent<v8::Object>::New(obj);
4339 if (!prev.IsEmpty()) {
4340 prev->Set(v8_str("next"), obj);
4341 prev.MakeWeak(new Snorkel(), &HandleWeakReference);
4342 whammy->objects_[whammy->cursor_].Clear();
4343 }
4344 whammy->objects_[whammy->cursor_] = global;
4345 whammy->cursor_ = (whammy->cursor_ + 1) % Whammy::kObjectCount;
4346 return whammy->getScript()->Run();
4347}
4348
4349THREADED_TEST(WeakReference) {
4350 v8::HandleScope handle_scope;
4351 v8::Handle<v8::ObjectTemplate> templ= v8::ObjectTemplate::New();
Ben Murdoch3bec4d22010-07-22 14:51:16 +01004352 Whammy* whammy = new Whammy();
Steve Blocka7e24c12009-10-30 11:49:00 +00004353 templ->SetNamedPropertyHandler(WhammyPropertyGetter,
4354 0, 0, 0, 0,
Ben Murdoch3bec4d22010-07-22 14:51:16 +01004355 v8::External::New(whammy));
Steve Blocka7e24c12009-10-30 11:49:00 +00004356 const char* extension_list[] = { "v8/gc" };
4357 v8::ExtensionConfiguration extensions(1, extension_list);
4358 v8::Persistent<Context> context = Context::New(&extensions);
4359 Context::Scope context_scope(context);
4360
4361 v8::Handle<v8::Object> interceptor = templ->NewInstance();
4362 context->Global()->Set(v8_str("whammy"), interceptor);
4363 const char* code =
4364 "var last;"
4365 "for (var i = 0; i < 10000; i++) {"
4366 " var obj = whammy.length;"
4367 " if (last) last.next = obj;"
4368 " last = obj;"
4369 "}"
4370 "gc();"
4371 "4";
4372 v8::Handle<Value> result = CompileRun(code);
4373 CHECK_EQ(4.0, result->NumberValue());
Ben Murdoch3bec4d22010-07-22 14:51:16 +01004374 delete whammy;
Steve Blocka7e24c12009-10-30 11:49:00 +00004375 context.Dispose();
4376}
4377
4378
Steve Blockd0582a62009-12-15 09:54:21 +00004379static bool in_scavenge = false;
4380static int last = -1;
4381
4382static void ForceScavenge(v8::Persistent<v8::Value> obj, void* data) {
4383 CHECK_EQ(-1, last);
4384 last = 0;
4385 obj.Dispose();
4386 obj.Clear();
4387 in_scavenge = true;
Steve Block44f0eee2011-05-26 01:26:41 +01004388 HEAP->PerformScavenge();
Steve Blockd0582a62009-12-15 09:54:21 +00004389 in_scavenge = false;
4390 *(reinterpret_cast<bool*>(data)) = true;
4391}
4392
4393static void CheckIsNotInvokedInScavenge(v8::Persistent<v8::Value> obj,
4394 void* data) {
4395 CHECK_EQ(0, last);
4396 last = 1;
4397 *(reinterpret_cast<bool*>(data)) = in_scavenge;
4398 obj.Dispose();
4399 obj.Clear();
4400}
4401
4402THREADED_TEST(NoWeakRefCallbacksInScavenge) {
4403 // Test verifies that scavenge cannot invoke WeakReferenceCallbacks.
4404 // Calling callbacks from scavenges is unsafe as objects held by those
4405 // handlers might have become strongly reachable, but scavenge doesn't
4406 // check that.
4407 v8::Persistent<Context> context = Context::New();
4408 Context::Scope context_scope(context);
4409
4410 v8::Persistent<v8::Object> object_a;
4411 v8::Persistent<v8::Object> object_b;
4412
4413 {
4414 v8::HandleScope handle_scope;
4415 object_b = v8::Persistent<v8::Object>::New(v8::Object::New());
4416 object_a = v8::Persistent<v8::Object>::New(v8::Object::New());
4417 }
4418
4419 bool object_a_disposed = false;
4420 object_a.MakeWeak(&object_a_disposed, &ForceScavenge);
4421 bool released_in_scavenge = false;
4422 object_b.MakeWeak(&released_in_scavenge, &CheckIsNotInvokedInScavenge);
4423
4424 while (!object_a_disposed) {
Steve Block44f0eee2011-05-26 01:26:41 +01004425 HEAP->CollectAllGarbage(false);
Steve Blockd0582a62009-12-15 09:54:21 +00004426 }
4427 CHECK(!released_in_scavenge);
4428}
4429
4430
Steve Blocka7e24c12009-10-30 11:49:00 +00004431v8::Handle<Function> args_fun;
4432
4433
4434static v8::Handle<Value> ArgumentsTestCallback(const v8::Arguments& args) {
4435 ApiTestFuzzer::Fuzz();
4436 CHECK_EQ(args_fun, args.Callee());
4437 CHECK_EQ(3, args.Length());
4438 CHECK_EQ(v8::Integer::New(1), args[0]);
4439 CHECK_EQ(v8::Integer::New(2), args[1]);
4440 CHECK_EQ(v8::Integer::New(3), args[2]);
4441 CHECK_EQ(v8::Undefined(), args[3]);
4442 v8::HandleScope scope;
Steve Block44f0eee2011-05-26 01:26:41 +01004443 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +00004444 return v8::Undefined();
4445}
4446
4447
4448THREADED_TEST(Arguments) {
4449 v8::HandleScope scope;
4450 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New();
4451 global->Set(v8_str("f"), v8::FunctionTemplate::New(ArgumentsTestCallback));
4452 LocalContext context(NULL, global);
Steve Block6ded16b2010-05-10 14:33:55 +01004453 args_fun = context->Global()->Get(v8_str("f")).As<Function>();
Steve Blocka7e24c12009-10-30 11:49:00 +00004454 v8_compile("f(1, 2, 3)")->Run();
4455}
4456
4457
Steve Blocka7e24c12009-10-30 11:49:00 +00004458static v8::Handle<Value> NoBlockGetterX(Local<String> name,
4459 const AccessorInfo&) {
4460 return v8::Handle<Value>();
4461}
4462
4463
4464static v8::Handle<Value> NoBlockGetterI(uint32_t index,
4465 const AccessorInfo&) {
4466 return v8::Handle<Value>();
4467}
4468
4469
4470static v8::Handle<v8::Boolean> PDeleter(Local<String> name,
4471 const AccessorInfo&) {
4472 if (!name->Equals(v8_str("foo"))) {
4473 return v8::Handle<v8::Boolean>(); // not intercepted
4474 }
4475
4476 return v8::False(); // intercepted, and don't delete the property
4477}
4478
4479
4480static v8::Handle<v8::Boolean> IDeleter(uint32_t index, const AccessorInfo&) {
4481 if (index != 2) {
4482 return v8::Handle<v8::Boolean>(); // not intercepted
4483 }
4484
4485 return v8::False(); // intercepted, and don't delete the property
4486}
4487
4488
4489THREADED_TEST(Deleter) {
4490 v8::HandleScope scope;
4491 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4492 obj->SetNamedPropertyHandler(NoBlockGetterX, NULL, NULL, PDeleter, NULL);
4493 obj->SetIndexedPropertyHandler(NoBlockGetterI, NULL, NULL, IDeleter, NULL);
4494 LocalContext context;
4495 context->Global()->Set(v8_str("k"), obj->NewInstance());
4496 CompileRun(
4497 "k.foo = 'foo';"
4498 "k.bar = 'bar';"
4499 "k[2] = 2;"
4500 "k[4] = 4;");
4501 CHECK(v8_compile("delete k.foo")->Run()->IsFalse());
4502 CHECK(v8_compile("delete k.bar")->Run()->IsTrue());
4503
4504 CHECK_EQ(v8_compile("k.foo")->Run(), v8_str("foo"));
4505 CHECK(v8_compile("k.bar")->Run()->IsUndefined());
4506
4507 CHECK(v8_compile("delete k[2]")->Run()->IsFalse());
4508 CHECK(v8_compile("delete k[4]")->Run()->IsTrue());
4509
4510 CHECK_EQ(v8_compile("k[2]")->Run(), v8_num(2));
4511 CHECK(v8_compile("k[4]")->Run()->IsUndefined());
4512}
4513
4514
4515static v8::Handle<Value> GetK(Local<String> name, const AccessorInfo&) {
4516 ApiTestFuzzer::Fuzz();
4517 if (name->Equals(v8_str("foo")) ||
4518 name->Equals(v8_str("bar")) ||
4519 name->Equals(v8_str("baz"))) {
4520 return v8::Undefined();
4521 }
4522 return v8::Handle<Value>();
4523}
4524
4525
4526static v8::Handle<Value> IndexedGetK(uint32_t index, const AccessorInfo&) {
4527 ApiTestFuzzer::Fuzz();
4528 if (index == 0 || index == 1) return v8::Undefined();
4529 return v8::Handle<Value>();
4530}
4531
4532
4533static v8::Handle<v8::Array> NamedEnum(const AccessorInfo&) {
4534 ApiTestFuzzer::Fuzz();
4535 v8::Handle<v8::Array> result = v8::Array::New(3);
4536 result->Set(v8::Integer::New(0), v8_str("foo"));
4537 result->Set(v8::Integer::New(1), v8_str("bar"));
4538 result->Set(v8::Integer::New(2), v8_str("baz"));
4539 return result;
4540}
4541
4542
4543static v8::Handle<v8::Array> IndexedEnum(const AccessorInfo&) {
4544 ApiTestFuzzer::Fuzz();
4545 v8::Handle<v8::Array> result = v8::Array::New(2);
4546 result->Set(v8::Integer::New(0), v8_str("0"));
4547 result->Set(v8::Integer::New(1), v8_str("1"));
4548 return result;
4549}
4550
4551
4552THREADED_TEST(Enumerators) {
4553 v8::HandleScope scope;
4554 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4555 obj->SetNamedPropertyHandler(GetK, NULL, NULL, NULL, NamedEnum);
4556 obj->SetIndexedPropertyHandler(IndexedGetK, NULL, NULL, NULL, IndexedEnum);
4557 LocalContext context;
4558 context->Global()->Set(v8_str("k"), obj->NewInstance());
4559 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun(
4560 "k[10] = 0;"
4561 "k.a = 0;"
4562 "k[5] = 0;"
4563 "k.b = 0;"
4564 "k[4294967295] = 0;"
4565 "k.c = 0;"
4566 "k[4294967296] = 0;"
4567 "k.d = 0;"
4568 "k[140000] = 0;"
4569 "k.e = 0;"
4570 "k[30000000000] = 0;"
4571 "k.f = 0;"
4572 "var result = [];"
4573 "for (var prop in k) {"
4574 " result.push(prop);"
4575 "}"
4576 "result"));
4577 // Check that we get all the property names returned including the
4578 // ones from the enumerators in the right order: indexed properties
4579 // in numerical order, indexed interceptor properties, named
4580 // properties in insertion order, named interceptor properties.
4581 // This order is not mandated by the spec, so this test is just
4582 // documenting our behavior.
4583 CHECK_EQ(17, result->Length());
4584 // Indexed properties in numerical order.
4585 CHECK_EQ(v8_str("5"), result->Get(v8::Integer::New(0)));
4586 CHECK_EQ(v8_str("10"), result->Get(v8::Integer::New(1)));
4587 CHECK_EQ(v8_str("140000"), result->Get(v8::Integer::New(2)));
4588 CHECK_EQ(v8_str("4294967295"), result->Get(v8::Integer::New(3)));
4589 // Indexed interceptor properties in the order they are returned
4590 // from the enumerator interceptor.
4591 CHECK_EQ(v8_str("0"), result->Get(v8::Integer::New(4)));
4592 CHECK_EQ(v8_str("1"), result->Get(v8::Integer::New(5)));
4593 // Named properties in insertion order.
4594 CHECK_EQ(v8_str("a"), result->Get(v8::Integer::New(6)));
4595 CHECK_EQ(v8_str("b"), result->Get(v8::Integer::New(7)));
4596 CHECK_EQ(v8_str("c"), result->Get(v8::Integer::New(8)));
4597 CHECK_EQ(v8_str("4294967296"), result->Get(v8::Integer::New(9)));
4598 CHECK_EQ(v8_str("d"), result->Get(v8::Integer::New(10)));
4599 CHECK_EQ(v8_str("e"), result->Get(v8::Integer::New(11)));
4600 CHECK_EQ(v8_str("30000000000"), result->Get(v8::Integer::New(12)));
4601 CHECK_EQ(v8_str("f"), result->Get(v8::Integer::New(13)));
4602 // Named interceptor properties.
4603 CHECK_EQ(v8_str("foo"), result->Get(v8::Integer::New(14)));
4604 CHECK_EQ(v8_str("bar"), result->Get(v8::Integer::New(15)));
4605 CHECK_EQ(v8_str("baz"), result->Get(v8::Integer::New(16)));
4606}
4607
4608
4609int p_getter_count;
4610int p_getter_count2;
4611
4612
4613static v8::Handle<Value> PGetter(Local<String> name, const AccessorInfo& info) {
4614 ApiTestFuzzer::Fuzz();
4615 p_getter_count++;
4616 v8::Handle<v8::Object> global = Context::GetCurrent()->Global();
4617 CHECK_EQ(info.Holder(), global->Get(v8_str("o1")));
4618 if (name->Equals(v8_str("p1"))) {
4619 CHECK_EQ(info.This(), global->Get(v8_str("o1")));
4620 } else if (name->Equals(v8_str("p2"))) {
4621 CHECK_EQ(info.This(), global->Get(v8_str("o2")));
4622 } else if (name->Equals(v8_str("p3"))) {
4623 CHECK_EQ(info.This(), global->Get(v8_str("o3")));
4624 } else if (name->Equals(v8_str("p4"))) {
4625 CHECK_EQ(info.This(), global->Get(v8_str("o4")));
4626 }
4627 return v8::Undefined();
4628}
4629
4630
4631static void RunHolderTest(v8::Handle<v8::ObjectTemplate> obj) {
4632 ApiTestFuzzer::Fuzz();
4633 LocalContext context;
4634 context->Global()->Set(v8_str("o1"), obj->NewInstance());
4635 CompileRun(
4636 "o1.__proto__ = { };"
4637 "var o2 = { __proto__: o1 };"
4638 "var o3 = { __proto__: o2 };"
4639 "var o4 = { __proto__: o3 };"
4640 "for (var i = 0; i < 10; i++) o4.p4;"
4641 "for (var i = 0; i < 10; i++) o3.p3;"
4642 "for (var i = 0; i < 10; i++) o2.p2;"
4643 "for (var i = 0; i < 10; i++) o1.p1;");
4644}
4645
4646
4647static v8::Handle<Value> PGetter2(Local<String> name,
4648 const AccessorInfo& info) {
4649 ApiTestFuzzer::Fuzz();
4650 p_getter_count2++;
4651 v8::Handle<v8::Object> global = Context::GetCurrent()->Global();
4652 CHECK_EQ(info.Holder(), global->Get(v8_str("o1")));
4653 if (name->Equals(v8_str("p1"))) {
4654 CHECK_EQ(info.This(), global->Get(v8_str("o1")));
4655 } else if (name->Equals(v8_str("p2"))) {
4656 CHECK_EQ(info.This(), global->Get(v8_str("o2")));
4657 } else if (name->Equals(v8_str("p3"))) {
4658 CHECK_EQ(info.This(), global->Get(v8_str("o3")));
4659 } else if (name->Equals(v8_str("p4"))) {
4660 CHECK_EQ(info.This(), global->Get(v8_str("o4")));
4661 }
4662 return v8::Undefined();
4663}
4664
4665
4666THREADED_TEST(GetterHolders) {
4667 v8::HandleScope scope;
4668 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4669 obj->SetAccessor(v8_str("p1"), PGetter);
4670 obj->SetAccessor(v8_str("p2"), PGetter);
4671 obj->SetAccessor(v8_str("p3"), PGetter);
4672 obj->SetAccessor(v8_str("p4"), PGetter);
4673 p_getter_count = 0;
4674 RunHolderTest(obj);
4675 CHECK_EQ(40, p_getter_count);
4676}
4677
4678
4679THREADED_TEST(PreInterceptorHolders) {
4680 v8::HandleScope scope;
4681 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4682 obj->SetNamedPropertyHandler(PGetter2);
4683 p_getter_count2 = 0;
4684 RunHolderTest(obj);
4685 CHECK_EQ(40, p_getter_count2);
4686}
4687
4688
4689THREADED_TEST(ObjectInstantiation) {
4690 v8::HandleScope scope;
4691 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
4692 templ->SetAccessor(v8_str("t"), PGetter2);
4693 LocalContext context;
4694 context->Global()->Set(v8_str("o"), templ->NewInstance());
4695 for (int i = 0; i < 100; i++) {
4696 v8::HandleScope inner_scope;
4697 v8::Handle<v8::Object> obj = templ->NewInstance();
4698 CHECK_NE(obj, context->Global()->Get(v8_str("o")));
4699 context->Global()->Set(v8_str("o2"), obj);
4700 v8::Handle<Value> value =
4701 Script::Compile(v8_str("o.__proto__ === o2.__proto__"))->Run();
4702 CHECK_EQ(v8::True(), value);
4703 context->Global()->Set(v8_str("o"), obj);
4704 }
4705}
4706
4707
Ben Murdochb0fe1622011-05-05 13:52:32 +01004708static int StrCmp16(uint16_t* a, uint16_t* b) {
4709 while (true) {
4710 if (*a == 0 && *b == 0) return 0;
4711 if (*a != *b) return 0 + *a - *b;
4712 a++;
4713 b++;
4714 }
4715}
4716
4717
4718static int StrNCmp16(uint16_t* a, uint16_t* b, int n) {
4719 while (true) {
4720 if (n-- == 0) return 0;
4721 if (*a == 0 && *b == 0) return 0;
4722 if (*a != *b) return 0 + *a - *b;
4723 a++;
4724 b++;
4725 }
4726}
4727
4728
Steve Blocka7e24c12009-10-30 11:49:00 +00004729THREADED_TEST(StringWrite) {
4730 v8::HandleScope scope;
4731 v8::Handle<String> str = v8_str("abcde");
Ben Murdochb0fe1622011-05-05 13:52:32 +01004732 // abc<Icelandic eth><Unicode snowman>.
4733 v8::Handle<String> str2 = v8_str("abc\303\260\342\230\203");
4734
4735 CHECK_EQ(5, str2->Length());
Steve Blocka7e24c12009-10-30 11:49:00 +00004736
4737 char buf[100];
Ben Murdochb0fe1622011-05-05 13:52:32 +01004738 char utf8buf[100];
4739 uint16_t wbuf[100];
Steve Blocka7e24c12009-10-30 11:49:00 +00004740 int len;
Ben Murdochb0fe1622011-05-05 13:52:32 +01004741 int charlen;
4742
4743 memset(utf8buf, 0x1, sizeof(utf8buf));
4744 len = str2->WriteUtf8(utf8buf, sizeof(utf8buf), &charlen);
Steve Block44f0eee2011-05-26 01:26:41 +01004745 CHECK_EQ(9, len);
4746 CHECK_EQ(5, charlen);
4747 CHECK_EQ(0, strcmp(utf8buf, "abc\303\260\342\230\203"));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004748
4749 memset(utf8buf, 0x1, sizeof(utf8buf));
4750 len = str2->WriteUtf8(utf8buf, 8, &charlen);
Steve Block44f0eee2011-05-26 01:26:41 +01004751 CHECK_EQ(8, len);
4752 CHECK_EQ(5, charlen);
4753 CHECK_EQ(0, strncmp(utf8buf, "abc\303\260\342\230\203\1", 9));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004754
4755 memset(utf8buf, 0x1, sizeof(utf8buf));
4756 len = str2->WriteUtf8(utf8buf, 7, &charlen);
Steve Block44f0eee2011-05-26 01:26:41 +01004757 CHECK_EQ(5, len);
4758 CHECK_EQ(4, charlen);
4759 CHECK_EQ(0, strncmp(utf8buf, "abc\303\260\1", 5));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004760
4761 memset(utf8buf, 0x1, sizeof(utf8buf));
4762 len = str2->WriteUtf8(utf8buf, 6, &charlen);
Steve Block44f0eee2011-05-26 01:26:41 +01004763 CHECK_EQ(5, len);
4764 CHECK_EQ(4, charlen);
4765 CHECK_EQ(0, strncmp(utf8buf, "abc\303\260\1", 5));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004766
4767 memset(utf8buf, 0x1, sizeof(utf8buf));
4768 len = str2->WriteUtf8(utf8buf, 5, &charlen);
Steve Block44f0eee2011-05-26 01:26:41 +01004769 CHECK_EQ(5, len);
4770 CHECK_EQ(4, charlen);
4771 CHECK_EQ(0, strncmp(utf8buf, "abc\303\260\1", 5));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004772
4773 memset(utf8buf, 0x1, sizeof(utf8buf));
4774 len = str2->WriteUtf8(utf8buf, 4, &charlen);
Steve Block44f0eee2011-05-26 01:26:41 +01004775 CHECK_EQ(3, len);
4776 CHECK_EQ(3, charlen);
4777 CHECK_EQ(0, strncmp(utf8buf, "abc\1", 4));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004778
4779 memset(utf8buf, 0x1, sizeof(utf8buf));
4780 len = str2->WriteUtf8(utf8buf, 3, &charlen);
Steve Block44f0eee2011-05-26 01:26:41 +01004781 CHECK_EQ(3, len);
4782 CHECK_EQ(3, charlen);
4783 CHECK_EQ(0, strncmp(utf8buf, "abc\1", 4));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004784
4785 memset(utf8buf, 0x1, sizeof(utf8buf));
4786 len = str2->WriteUtf8(utf8buf, 2, &charlen);
Steve Block44f0eee2011-05-26 01:26:41 +01004787 CHECK_EQ(2, len);
4788 CHECK_EQ(2, charlen);
4789 CHECK_EQ(0, strncmp(utf8buf, "ab\1", 3));
Steve Blocka7e24c12009-10-30 11:49:00 +00004790
4791 memset(buf, 0x1, sizeof(buf));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004792 memset(wbuf, 0x1, sizeof(wbuf));
Steve Blocka7e24c12009-10-30 11:49:00 +00004793 len = str->WriteAscii(buf);
Steve Block44f0eee2011-05-26 01:26:41 +01004794 CHECK_EQ(5, len);
Ben Murdochb0fe1622011-05-05 13:52:32 +01004795 len = str->Write(wbuf);
Steve Block44f0eee2011-05-26 01:26:41 +01004796 CHECK_EQ(5, len);
4797 CHECK_EQ(0, strcmp("abcde", buf));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004798 uint16_t answer1[] = {'a', 'b', 'c', 'd', 'e', '\0'};
Steve Block44f0eee2011-05-26 01:26:41 +01004799 CHECK_EQ(0, StrCmp16(answer1, wbuf));
Steve Blocka7e24c12009-10-30 11:49:00 +00004800
4801 memset(buf, 0x1, sizeof(buf));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004802 memset(wbuf, 0x1, sizeof(wbuf));
Steve Blocka7e24c12009-10-30 11:49:00 +00004803 len = str->WriteAscii(buf, 0, 4);
Steve Block44f0eee2011-05-26 01:26:41 +01004804 CHECK_EQ(4, len);
Ben Murdochb0fe1622011-05-05 13:52:32 +01004805 len = str->Write(wbuf, 0, 4);
Steve Block44f0eee2011-05-26 01:26:41 +01004806 CHECK_EQ(4, len);
4807 CHECK_EQ(0, strncmp("abcd\1", buf, 5));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004808 uint16_t answer2[] = {'a', 'b', 'c', 'd', 0x101};
Steve Block44f0eee2011-05-26 01:26:41 +01004809 CHECK_EQ(0, StrNCmp16(answer2, wbuf, 5));
Steve Blocka7e24c12009-10-30 11:49:00 +00004810
4811 memset(buf, 0x1, sizeof(buf));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004812 memset(wbuf, 0x1, sizeof(wbuf));
Steve Blocka7e24c12009-10-30 11:49:00 +00004813 len = str->WriteAscii(buf, 0, 5);
Steve Block44f0eee2011-05-26 01:26:41 +01004814 CHECK_EQ(5, len);
Ben Murdochb0fe1622011-05-05 13:52:32 +01004815 len = str->Write(wbuf, 0, 5);
Steve Block44f0eee2011-05-26 01:26:41 +01004816 CHECK_EQ(5, len);
4817 CHECK_EQ(0, strncmp("abcde\1", buf, 6));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004818 uint16_t answer3[] = {'a', 'b', 'c', 'd', 'e', 0x101};
Steve Block44f0eee2011-05-26 01:26:41 +01004819 CHECK_EQ(0, StrNCmp16(answer3, wbuf, 6));
Steve Blocka7e24c12009-10-30 11:49:00 +00004820
4821 memset(buf, 0x1, sizeof(buf));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004822 memset(wbuf, 0x1, sizeof(wbuf));
Steve Blocka7e24c12009-10-30 11:49:00 +00004823 len = str->WriteAscii(buf, 0, 6);
Steve Block44f0eee2011-05-26 01:26:41 +01004824 CHECK_EQ(5, len);
Ben Murdochb0fe1622011-05-05 13:52:32 +01004825 len = str->Write(wbuf, 0, 6);
Steve Block44f0eee2011-05-26 01:26:41 +01004826 CHECK_EQ(5, len);
4827 CHECK_EQ(0, strcmp("abcde", buf));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004828 uint16_t answer4[] = {'a', 'b', 'c', 'd', 'e', '\0'};
Steve Block44f0eee2011-05-26 01:26:41 +01004829 CHECK_EQ(0, StrCmp16(answer4, wbuf));
Steve Blocka7e24c12009-10-30 11:49:00 +00004830
4831 memset(buf, 0x1, sizeof(buf));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004832 memset(wbuf, 0x1, sizeof(wbuf));
Steve Blocka7e24c12009-10-30 11:49:00 +00004833 len = str->WriteAscii(buf, 4, -1);
Steve Block44f0eee2011-05-26 01:26:41 +01004834 CHECK_EQ(1, len);
Ben Murdochb0fe1622011-05-05 13:52:32 +01004835 len = str->Write(wbuf, 4, -1);
Steve Block44f0eee2011-05-26 01:26:41 +01004836 CHECK_EQ(1, len);
4837 CHECK_EQ(0, strcmp("e", buf));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004838 uint16_t answer5[] = {'e', '\0'};
Steve Block44f0eee2011-05-26 01:26:41 +01004839 CHECK_EQ(0, StrCmp16(answer5, wbuf));
Steve Blocka7e24c12009-10-30 11:49:00 +00004840
4841 memset(buf, 0x1, sizeof(buf));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004842 memset(wbuf, 0x1, sizeof(wbuf));
Steve Blocka7e24c12009-10-30 11:49:00 +00004843 len = str->WriteAscii(buf, 4, 6);
Steve Block44f0eee2011-05-26 01:26:41 +01004844 CHECK_EQ(1, len);
Ben Murdochb0fe1622011-05-05 13:52:32 +01004845 len = str->Write(wbuf, 4, 6);
Steve Block44f0eee2011-05-26 01:26:41 +01004846 CHECK_EQ(1, len);
4847 CHECK_EQ(0, strcmp("e", buf));
4848 CHECK_EQ(0, StrCmp16(answer5, wbuf));
Steve Blocka7e24c12009-10-30 11:49:00 +00004849
4850 memset(buf, 0x1, sizeof(buf));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004851 memset(wbuf, 0x1, sizeof(wbuf));
Steve Blocka7e24c12009-10-30 11:49:00 +00004852 len = str->WriteAscii(buf, 4, 1);
Steve Block44f0eee2011-05-26 01:26:41 +01004853 CHECK_EQ(1, len);
Ben Murdochb0fe1622011-05-05 13:52:32 +01004854 len = str->Write(wbuf, 4, 1);
Steve Block44f0eee2011-05-26 01:26:41 +01004855 CHECK_EQ(1, len);
4856 CHECK_EQ(0, strncmp("e\1", buf, 2));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004857 uint16_t answer6[] = {'e', 0x101};
Steve Block44f0eee2011-05-26 01:26:41 +01004858 CHECK_EQ(0, StrNCmp16(answer6, wbuf, 2));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004859
4860 memset(buf, 0x1, sizeof(buf));
4861 memset(wbuf, 0x1, sizeof(wbuf));
4862 len = str->WriteAscii(buf, 3, 1);
Steve Block44f0eee2011-05-26 01:26:41 +01004863 CHECK_EQ(1, len);
Ben Murdochb0fe1622011-05-05 13:52:32 +01004864 len = str->Write(wbuf, 3, 1);
Steve Block44f0eee2011-05-26 01:26:41 +01004865 CHECK_EQ(1, len);
4866 CHECK_EQ(0, strncmp("d\1", buf, 2));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004867 uint16_t answer7[] = {'d', 0x101};
Steve Block44f0eee2011-05-26 01:26:41 +01004868 CHECK_EQ(0, StrNCmp16(answer7, wbuf, 2));
Steve Blocka7e24c12009-10-30 11:49:00 +00004869}
4870
4871
4872THREADED_TEST(ToArrayIndex) {
4873 v8::HandleScope scope;
4874 LocalContext context;
4875
4876 v8::Handle<String> str = v8_str("42");
4877 v8::Handle<v8::Uint32> index = str->ToArrayIndex();
4878 CHECK(!index.IsEmpty());
4879 CHECK_EQ(42.0, index->Uint32Value());
4880 str = v8_str("42asdf");
4881 index = str->ToArrayIndex();
4882 CHECK(index.IsEmpty());
4883 str = v8_str("-42");
4884 index = str->ToArrayIndex();
4885 CHECK(index.IsEmpty());
4886 str = v8_str("4294967295");
4887 index = str->ToArrayIndex();
4888 CHECK(!index.IsEmpty());
4889 CHECK_EQ(4294967295.0, index->Uint32Value());
4890 v8::Handle<v8::Number> num = v8::Number::New(1);
4891 index = num->ToArrayIndex();
4892 CHECK(!index.IsEmpty());
4893 CHECK_EQ(1.0, index->Uint32Value());
4894 num = v8::Number::New(-1);
4895 index = num->ToArrayIndex();
4896 CHECK(index.IsEmpty());
4897 v8::Handle<v8::Object> obj = v8::Object::New();
4898 index = obj->ToArrayIndex();
4899 CHECK(index.IsEmpty());
4900}
4901
4902
4903THREADED_TEST(ErrorConstruction) {
4904 v8::HandleScope scope;
4905 LocalContext context;
4906
4907 v8::Handle<String> foo = v8_str("foo");
4908 v8::Handle<String> message = v8_str("message");
4909 v8::Handle<Value> range_error = v8::Exception::RangeError(foo);
4910 CHECK(range_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004911 v8::Handle<v8::Object> range_obj = range_error.As<v8::Object>();
4912 CHECK(range_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004913 v8::Handle<Value> reference_error = v8::Exception::ReferenceError(foo);
4914 CHECK(reference_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004915 CHECK(reference_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004916 v8::Handle<Value> syntax_error = v8::Exception::SyntaxError(foo);
4917 CHECK(syntax_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004918 CHECK(syntax_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004919 v8::Handle<Value> type_error = v8::Exception::TypeError(foo);
4920 CHECK(type_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004921 CHECK(type_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004922 v8::Handle<Value> error = v8::Exception::Error(foo);
4923 CHECK(error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004924 CHECK(error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004925}
4926
4927
4928static v8::Handle<Value> YGetter(Local<String> name, const AccessorInfo& info) {
4929 ApiTestFuzzer::Fuzz();
4930 return v8_num(10);
4931}
4932
4933
4934static void YSetter(Local<String> name,
4935 Local<Value> value,
4936 const AccessorInfo& info) {
4937 if (info.This()->Has(name)) {
4938 info.This()->Delete(name);
4939 }
4940 info.This()->Set(name, value);
4941}
4942
4943
4944THREADED_TEST(DeleteAccessor) {
4945 v8::HandleScope scope;
4946 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4947 obj->SetAccessor(v8_str("y"), YGetter, YSetter);
4948 LocalContext context;
4949 v8::Handle<v8::Object> holder = obj->NewInstance();
4950 context->Global()->Set(v8_str("holder"), holder);
4951 v8::Handle<Value> result = CompileRun(
4952 "holder.y = 11; holder.y = 12; holder.y");
4953 CHECK_EQ(12, result->Uint32Value());
4954}
4955
4956
4957THREADED_TEST(TypeSwitch) {
4958 v8::HandleScope scope;
4959 v8::Handle<v8::FunctionTemplate> templ1 = v8::FunctionTemplate::New();
4960 v8::Handle<v8::FunctionTemplate> templ2 = v8::FunctionTemplate::New();
4961 v8::Handle<v8::FunctionTemplate> templ3 = v8::FunctionTemplate::New();
4962 v8::Handle<v8::FunctionTemplate> templs[3] = { templ1, templ2, templ3 };
4963 v8::Handle<v8::TypeSwitch> type_switch = v8::TypeSwitch::New(3, templs);
4964 LocalContext context;
4965 v8::Handle<v8::Object> obj0 = v8::Object::New();
4966 v8::Handle<v8::Object> obj1 = templ1->GetFunction()->NewInstance();
4967 v8::Handle<v8::Object> obj2 = templ2->GetFunction()->NewInstance();
4968 v8::Handle<v8::Object> obj3 = templ3->GetFunction()->NewInstance();
4969 for (int i = 0; i < 10; i++) {
4970 CHECK_EQ(0, type_switch->match(obj0));
4971 CHECK_EQ(1, type_switch->match(obj1));
4972 CHECK_EQ(2, type_switch->match(obj2));
4973 CHECK_EQ(3, type_switch->match(obj3));
4974 CHECK_EQ(3, type_switch->match(obj3));
4975 CHECK_EQ(2, type_switch->match(obj2));
4976 CHECK_EQ(1, type_switch->match(obj1));
4977 CHECK_EQ(0, type_switch->match(obj0));
4978 }
4979}
4980
4981
4982// For use within the TestSecurityHandler() test.
4983static bool g_security_callback_result = false;
4984static bool NamedSecurityTestCallback(Local<v8::Object> global,
4985 Local<Value> name,
4986 v8::AccessType type,
4987 Local<Value> data) {
4988 // Always allow read access.
4989 if (type == v8::ACCESS_GET)
4990 return true;
4991
4992 // Sometimes allow other access.
4993 return g_security_callback_result;
4994}
4995
4996
4997static bool IndexedSecurityTestCallback(Local<v8::Object> global,
4998 uint32_t key,
4999 v8::AccessType type,
5000 Local<Value> data) {
5001 // Always allow read access.
5002 if (type == v8::ACCESS_GET)
5003 return true;
5004
5005 // Sometimes allow other access.
5006 return g_security_callback_result;
5007}
5008
5009
5010static int trouble_nesting = 0;
5011static v8::Handle<Value> TroubleCallback(const v8::Arguments& args) {
5012 ApiTestFuzzer::Fuzz();
5013 trouble_nesting++;
5014
5015 // Call a JS function that throws an uncaught exception.
5016 Local<v8::Object> arg_this = Context::GetCurrent()->Global();
5017 Local<Value> trouble_callee = (trouble_nesting == 3) ?
5018 arg_this->Get(v8_str("trouble_callee")) :
5019 arg_this->Get(v8_str("trouble_caller"));
5020 CHECK(trouble_callee->IsFunction());
5021 return Function::Cast(*trouble_callee)->Call(arg_this, 0, NULL);
5022}
5023
5024
5025static int report_count = 0;
5026static void ApiUncaughtExceptionTestListener(v8::Handle<v8::Message>,
5027 v8::Handle<Value>) {
5028 report_count++;
5029}
5030
5031
5032// Counts uncaught exceptions, but other tests running in parallel
5033// also have uncaught exceptions.
5034TEST(ApiUncaughtException) {
5035 report_count = 0;
5036 v8::HandleScope scope;
5037 LocalContext env;
5038 v8::V8::AddMessageListener(ApiUncaughtExceptionTestListener);
5039
5040 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback);
5041 v8::Local<v8::Object> global = env->Global();
5042 global->Set(v8_str("trouble"), fun->GetFunction());
5043
5044 Script::Compile(v8_str("function trouble_callee() {"
5045 " var x = null;"
5046 " return x.foo;"
5047 "};"
5048 "function trouble_caller() {"
5049 " trouble();"
5050 "};"))->Run();
5051 Local<Value> trouble = global->Get(v8_str("trouble"));
5052 CHECK(trouble->IsFunction());
5053 Local<Value> trouble_callee = global->Get(v8_str("trouble_callee"));
5054 CHECK(trouble_callee->IsFunction());
5055 Local<Value> trouble_caller = global->Get(v8_str("trouble_caller"));
5056 CHECK(trouble_caller->IsFunction());
5057 Function::Cast(*trouble_caller)->Call(global, 0, NULL);
5058 CHECK_EQ(1, report_count);
5059 v8::V8::RemoveMessageListeners(ApiUncaughtExceptionTestListener);
5060}
5061
Leon Clarke4515c472010-02-03 11:58:03 +00005062static const char* script_resource_name = "ExceptionInNativeScript.js";
5063static void ExceptionInNativeScriptTestListener(v8::Handle<v8::Message> message,
5064 v8::Handle<Value>) {
5065 v8::Handle<v8::Value> name_val = message->GetScriptResourceName();
5066 CHECK(!name_val.IsEmpty() && name_val->IsString());
5067 v8::String::AsciiValue name(message->GetScriptResourceName());
5068 CHECK_EQ(script_resource_name, *name);
5069 CHECK_EQ(3, message->GetLineNumber());
5070 v8::String::AsciiValue source_line(message->GetSourceLine());
5071 CHECK_EQ(" new o.foo();", *source_line);
5072}
5073
5074TEST(ExceptionInNativeScript) {
5075 v8::HandleScope scope;
5076 LocalContext env;
5077 v8::V8::AddMessageListener(ExceptionInNativeScriptTestListener);
5078
5079 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback);
5080 v8::Local<v8::Object> global = env->Global();
5081 global->Set(v8_str("trouble"), fun->GetFunction());
5082
5083 Script::Compile(v8_str("function trouble() {\n"
5084 " var o = {};\n"
5085 " new o.foo();\n"
5086 "};"), v8::String::New(script_resource_name))->Run();
5087 Local<Value> trouble = global->Get(v8_str("trouble"));
5088 CHECK(trouble->IsFunction());
5089 Function::Cast(*trouble)->Call(global, 0, NULL);
5090 v8::V8::RemoveMessageListeners(ExceptionInNativeScriptTestListener);
5091}
5092
Steve Blocka7e24c12009-10-30 11:49:00 +00005093
5094TEST(CompilationErrorUsingTryCatchHandler) {
5095 v8::HandleScope scope;
5096 LocalContext env;
5097 v8::TryCatch try_catch;
5098 Script::Compile(v8_str("This doesn't &*&@#$&*^ compile."));
5099 CHECK_NE(NULL, *try_catch.Exception());
5100 CHECK(try_catch.HasCaught());
5101}
5102
5103
5104TEST(TryCatchFinallyUsingTryCatchHandler) {
5105 v8::HandleScope scope;
5106 LocalContext env;
5107 v8::TryCatch try_catch;
5108 Script::Compile(v8_str("try { throw ''; } catch (e) {}"))->Run();
5109 CHECK(!try_catch.HasCaught());
5110 Script::Compile(v8_str("try { throw ''; } finally {}"))->Run();
5111 CHECK(try_catch.HasCaught());
5112 try_catch.Reset();
5113 Script::Compile(v8_str("(function() {"
5114 "try { throw ''; } finally { return; }"
5115 "})()"))->Run();
5116 CHECK(!try_catch.HasCaught());
5117 Script::Compile(v8_str("(function()"
5118 " { try { throw ''; } finally { throw 0; }"
5119 "})()"))->Run();
5120 CHECK(try_catch.HasCaught());
5121}
5122
5123
5124// SecurityHandler can't be run twice
5125TEST(SecurityHandler) {
5126 v8::HandleScope scope0;
5127 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
5128 global_template->SetAccessCheckCallbacks(NamedSecurityTestCallback,
5129 IndexedSecurityTestCallback);
5130 // Create an environment
5131 v8::Persistent<Context> context0 =
5132 Context::New(NULL, global_template);
5133 context0->Enter();
5134
5135 v8::Handle<v8::Object> global0 = context0->Global();
5136 v8::Handle<Script> script0 = v8_compile("foo = 111");
5137 script0->Run();
5138 global0->Set(v8_str("0"), v8_num(999));
5139 v8::Handle<Value> foo0 = global0->Get(v8_str("foo"));
5140 CHECK_EQ(111, foo0->Int32Value());
5141 v8::Handle<Value> z0 = global0->Get(v8_str("0"));
5142 CHECK_EQ(999, z0->Int32Value());
5143
5144 // Create another environment, should fail security checks.
5145 v8::HandleScope scope1;
5146
5147 v8::Persistent<Context> context1 =
5148 Context::New(NULL, global_template);
5149 context1->Enter();
5150
5151 v8::Handle<v8::Object> global1 = context1->Global();
5152 global1->Set(v8_str("othercontext"), global0);
5153 // This set will fail the security check.
5154 v8::Handle<Script> script1 =
5155 v8_compile("othercontext.foo = 222; othercontext[0] = 888;");
5156 script1->Run();
5157 // This read will pass the security check.
5158 v8::Handle<Value> foo1 = global0->Get(v8_str("foo"));
5159 CHECK_EQ(111, foo1->Int32Value());
5160 // This read will pass the security check.
5161 v8::Handle<Value> z1 = global0->Get(v8_str("0"));
5162 CHECK_EQ(999, z1->Int32Value());
5163
5164 // Create another environment, should pass security checks.
5165 { g_security_callback_result = true; // allow security handler to pass.
5166 v8::HandleScope scope2;
5167 LocalContext context2;
5168 v8::Handle<v8::Object> global2 = context2->Global();
5169 global2->Set(v8_str("othercontext"), global0);
5170 v8::Handle<Script> script2 =
5171 v8_compile("othercontext.foo = 333; othercontext[0] = 888;");
5172 script2->Run();
5173 v8::Handle<Value> foo2 = global0->Get(v8_str("foo"));
5174 CHECK_EQ(333, foo2->Int32Value());
5175 v8::Handle<Value> z2 = global0->Get(v8_str("0"));
5176 CHECK_EQ(888, z2->Int32Value());
5177 }
5178
5179 context1->Exit();
5180 context1.Dispose();
5181
5182 context0->Exit();
5183 context0.Dispose();
5184}
5185
5186
5187THREADED_TEST(SecurityChecks) {
5188 v8::HandleScope handle_scope;
5189 LocalContext env1;
5190 v8::Persistent<Context> env2 = Context::New();
5191
5192 Local<Value> foo = v8_str("foo");
5193 Local<Value> bar = v8_str("bar");
5194
5195 // Set to the same domain.
5196 env1->SetSecurityToken(foo);
5197
5198 // Create a function in env1.
5199 Script::Compile(v8_str("spy=function(){return spy;}"))->Run();
5200 Local<Value> spy = env1->Global()->Get(v8_str("spy"));
5201 CHECK(spy->IsFunction());
5202
5203 // Create another function accessing global objects.
5204 Script::Compile(v8_str("spy2=function(){return new this.Array();}"))->Run();
5205 Local<Value> spy2 = env1->Global()->Get(v8_str("spy2"));
5206 CHECK(spy2->IsFunction());
5207
5208 // Switch to env2 in the same domain and invoke spy on env2.
5209 {
5210 env2->SetSecurityToken(foo);
5211 // Enter env2
5212 Context::Scope scope_env2(env2);
5213 Local<Value> result = Function::Cast(*spy)->Call(env2->Global(), 0, NULL);
5214 CHECK(result->IsFunction());
5215 }
5216
5217 {
5218 env2->SetSecurityToken(bar);
5219 Context::Scope scope_env2(env2);
5220
5221 // Call cross_domain_call, it should throw an exception
5222 v8::TryCatch try_catch;
5223 Function::Cast(*spy2)->Call(env2->Global(), 0, NULL);
5224 CHECK(try_catch.HasCaught());
5225 }
5226
5227 env2.Dispose();
5228}
5229
5230
5231// Regression test case for issue 1183439.
5232THREADED_TEST(SecurityChecksForPrototypeChain) {
5233 v8::HandleScope scope;
5234 LocalContext current;
5235 v8::Persistent<Context> other = Context::New();
5236
5237 // Change context to be able to get to the Object function in the
5238 // other context without hitting the security checks.
5239 v8::Local<Value> other_object;
5240 { Context::Scope scope(other);
5241 other_object = other->Global()->Get(v8_str("Object"));
5242 other->Global()->Set(v8_num(42), v8_num(87));
5243 }
5244
5245 current->Global()->Set(v8_str("other"), other->Global());
5246 CHECK(v8_compile("other")->Run()->Equals(other->Global()));
5247
5248 // Make sure the security check fails here and we get an undefined
5249 // result instead of getting the Object function. Repeat in a loop
5250 // to make sure to exercise the IC code.
5251 v8::Local<Script> access_other0 = v8_compile("other.Object");
5252 v8::Local<Script> access_other1 = v8_compile("other[42]");
5253 for (int i = 0; i < 5; i++) {
5254 CHECK(!access_other0->Run()->Equals(other_object));
5255 CHECK(access_other0->Run()->IsUndefined());
5256 CHECK(!access_other1->Run()->Equals(v8_num(87)));
5257 CHECK(access_other1->Run()->IsUndefined());
5258 }
5259
5260 // Create an object that has 'other' in its prototype chain and make
5261 // sure we cannot access the Object function indirectly through
5262 // that. Repeat in a loop to make sure to exercise the IC code.
5263 v8_compile("function F() { };"
5264 "F.prototype = other;"
5265 "var f = new F();")->Run();
5266 v8::Local<Script> access_f0 = v8_compile("f.Object");
5267 v8::Local<Script> access_f1 = v8_compile("f[42]");
5268 for (int j = 0; j < 5; j++) {
5269 CHECK(!access_f0->Run()->Equals(other_object));
5270 CHECK(access_f0->Run()->IsUndefined());
5271 CHECK(!access_f1->Run()->Equals(v8_num(87)));
5272 CHECK(access_f1->Run()->IsUndefined());
5273 }
5274
5275 // Now it gets hairy: Set the prototype for the other global object
5276 // to be the current global object. The prototype chain for 'f' now
5277 // goes through 'other' but ends up in the current global object.
5278 { Context::Scope scope(other);
5279 other->Global()->Set(v8_str("__proto__"), current->Global());
5280 }
5281 // Set a named and an index property on the current global
5282 // object. To force the lookup to go through the other global object,
5283 // the properties must not exist in the other global object.
5284 current->Global()->Set(v8_str("foo"), v8_num(100));
5285 current->Global()->Set(v8_num(99), v8_num(101));
5286 // Try to read the properties from f and make sure that the access
5287 // gets stopped by the security checks on the other global object.
5288 Local<Script> access_f2 = v8_compile("f.foo");
5289 Local<Script> access_f3 = v8_compile("f[99]");
5290 for (int k = 0; k < 5; k++) {
5291 CHECK(!access_f2->Run()->Equals(v8_num(100)));
5292 CHECK(access_f2->Run()->IsUndefined());
5293 CHECK(!access_f3->Run()->Equals(v8_num(101)));
5294 CHECK(access_f3->Run()->IsUndefined());
5295 }
5296 other.Dispose();
5297}
5298
5299
5300THREADED_TEST(CrossDomainDelete) {
5301 v8::HandleScope handle_scope;
5302 LocalContext env1;
5303 v8::Persistent<Context> env2 = Context::New();
5304
5305 Local<Value> foo = v8_str("foo");
5306 Local<Value> bar = v8_str("bar");
5307
5308 // Set to the same domain.
5309 env1->SetSecurityToken(foo);
5310 env2->SetSecurityToken(foo);
5311
5312 env1->Global()->Set(v8_str("prop"), v8_num(3));
5313 env2->Global()->Set(v8_str("env1"), env1->Global());
5314
5315 // Change env2 to a different domain and delete env1.prop.
5316 env2->SetSecurityToken(bar);
5317 {
5318 Context::Scope scope_env2(env2);
5319 Local<Value> result =
5320 Script::Compile(v8_str("delete env1.prop"))->Run();
5321 CHECK(result->IsFalse());
5322 }
5323
5324 // Check that env1.prop still exists.
5325 Local<Value> v = env1->Global()->Get(v8_str("prop"));
5326 CHECK(v->IsNumber());
5327 CHECK_EQ(3, v->Int32Value());
5328
5329 env2.Dispose();
5330}
5331
5332
5333THREADED_TEST(CrossDomainIsPropertyEnumerable) {
5334 v8::HandleScope handle_scope;
5335 LocalContext env1;
5336 v8::Persistent<Context> env2 = Context::New();
5337
5338 Local<Value> foo = v8_str("foo");
5339 Local<Value> bar = v8_str("bar");
5340
5341 // Set to the same domain.
5342 env1->SetSecurityToken(foo);
5343 env2->SetSecurityToken(foo);
5344
5345 env1->Global()->Set(v8_str("prop"), v8_num(3));
5346 env2->Global()->Set(v8_str("env1"), env1->Global());
5347
5348 // env1.prop is enumerable in env2.
5349 Local<String> test = v8_str("propertyIsEnumerable.call(env1, 'prop')");
5350 {
5351 Context::Scope scope_env2(env2);
5352 Local<Value> result = Script::Compile(test)->Run();
5353 CHECK(result->IsTrue());
5354 }
5355
5356 // Change env2 to a different domain and test again.
5357 env2->SetSecurityToken(bar);
5358 {
5359 Context::Scope scope_env2(env2);
5360 Local<Value> result = Script::Compile(test)->Run();
5361 CHECK(result->IsFalse());
5362 }
5363
5364 env2.Dispose();
5365}
5366
5367
5368THREADED_TEST(CrossDomainForIn) {
5369 v8::HandleScope handle_scope;
5370 LocalContext env1;
5371 v8::Persistent<Context> env2 = Context::New();
5372
5373 Local<Value> foo = v8_str("foo");
5374 Local<Value> bar = v8_str("bar");
5375
5376 // Set to the same domain.
5377 env1->SetSecurityToken(foo);
5378 env2->SetSecurityToken(foo);
5379
5380 env1->Global()->Set(v8_str("prop"), v8_num(3));
5381 env2->Global()->Set(v8_str("env1"), env1->Global());
5382
5383 // Change env2 to a different domain and set env1's global object
5384 // as the __proto__ of an object in env2 and enumerate properties
5385 // in for-in. It shouldn't enumerate properties on env1's global
5386 // object.
5387 env2->SetSecurityToken(bar);
5388 {
5389 Context::Scope scope_env2(env2);
5390 Local<Value> result =
5391 CompileRun("(function(){var obj = {'__proto__':env1};"
5392 "for (var p in obj)"
5393 " if (p == 'prop') return false;"
5394 "return true;})()");
5395 CHECK(result->IsTrue());
5396 }
5397 env2.Dispose();
5398}
5399
5400
5401TEST(ContextDetachGlobal) {
5402 v8::HandleScope handle_scope;
5403 LocalContext env1;
5404 v8::Persistent<Context> env2 = Context::New();
5405
5406 Local<v8::Object> global1 = env1->Global();
5407
5408 Local<Value> foo = v8_str("foo");
5409
5410 // Set to the same domain.
5411 env1->SetSecurityToken(foo);
5412 env2->SetSecurityToken(foo);
5413
5414 // Enter env2
5415 env2->Enter();
5416
Andrei Popescu74b3c142010-03-29 12:03:09 +01005417 // Create a function in env2 and add a reference to it in env1.
Steve Blocka7e24c12009-10-30 11:49:00 +00005418 Local<v8::Object> global2 = env2->Global();
5419 global2->Set(v8_str("prop"), v8::Integer::New(1));
5420 CompileRun("function getProp() {return prop;}");
5421
5422 env1->Global()->Set(v8_str("getProp"),
5423 global2->Get(v8_str("getProp")));
5424
Andrei Popescu74b3c142010-03-29 12:03:09 +01005425 // Detach env2's global, and reuse the global object of env2
Steve Blocka7e24c12009-10-30 11:49:00 +00005426 env2->Exit();
5427 env2->DetachGlobal();
5428 // env2 has a new global object.
5429 CHECK(!env2->Global()->Equals(global2));
5430
5431 v8::Persistent<Context> env3 =
5432 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2);
5433 env3->SetSecurityToken(v8_str("bar"));
5434 env3->Enter();
5435
5436 Local<v8::Object> global3 = env3->Global();
5437 CHECK_EQ(global2, global3);
5438 CHECK(global3->Get(v8_str("prop"))->IsUndefined());
5439 CHECK(global3->Get(v8_str("getProp"))->IsUndefined());
5440 global3->Set(v8_str("prop"), v8::Integer::New(-1));
5441 global3->Set(v8_str("prop2"), v8::Integer::New(2));
5442 env3->Exit();
5443
5444 // Call getProp in env1, and it should return the value 1
5445 {
5446 Local<Value> get_prop = global1->Get(v8_str("getProp"));
5447 CHECK(get_prop->IsFunction());
5448 v8::TryCatch try_catch;
5449 Local<Value> r = Function::Cast(*get_prop)->Call(global1, 0, NULL);
5450 CHECK(!try_catch.HasCaught());
5451 CHECK_EQ(1, r->Int32Value());
5452 }
5453
5454 // Check that env3 is not accessible from env1
5455 {
5456 Local<Value> r = global3->Get(v8_str("prop2"));
5457 CHECK(r->IsUndefined());
5458 }
5459
5460 env2.Dispose();
5461 env3.Dispose();
5462}
5463
5464
Andrei Popescu74b3c142010-03-29 12:03:09 +01005465TEST(DetachAndReattachGlobal) {
5466 v8::HandleScope scope;
5467 LocalContext env1;
5468
5469 // Create second environment.
5470 v8::Persistent<Context> env2 = Context::New();
5471
5472 Local<Value> foo = v8_str("foo");
5473
5474 // Set same security token for env1 and env2.
5475 env1->SetSecurityToken(foo);
5476 env2->SetSecurityToken(foo);
5477
5478 // Create a property on the global object in env2.
5479 {
5480 v8::Context::Scope scope(env2);
5481 env2->Global()->Set(v8_str("p"), v8::Integer::New(42));
5482 }
5483
5484 // Create a reference to env2 global from env1 global.
5485 env1->Global()->Set(v8_str("other"), env2->Global());
5486
5487 // Check that we have access to other.p in env2 from env1.
5488 Local<Value> result = CompileRun("other.p");
5489 CHECK(result->IsInt32());
5490 CHECK_EQ(42, result->Int32Value());
5491
5492 // Hold on to global from env2 and detach global from env2.
5493 Local<v8::Object> global2 = env2->Global();
5494 env2->DetachGlobal();
5495
5496 // Check that the global has been detached. No other.p property can
5497 // be found.
5498 result = CompileRun("other.p");
5499 CHECK(result->IsUndefined());
5500
5501 // Reuse global2 for env3.
5502 v8::Persistent<Context> env3 =
5503 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2);
5504 CHECK_EQ(global2, env3->Global());
5505
5506 // Start by using the same security token for env3 as for env1 and env2.
5507 env3->SetSecurityToken(foo);
5508
5509 // Create a property on the global object in env3.
5510 {
5511 v8::Context::Scope scope(env3);
5512 env3->Global()->Set(v8_str("p"), v8::Integer::New(24));
5513 }
5514
5515 // Check that other.p is now the property in env3 and that we have access.
5516 result = CompileRun("other.p");
5517 CHECK(result->IsInt32());
5518 CHECK_EQ(24, result->Int32Value());
5519
5520 // Change security token for env3 to something different from env1 and env2.
5521 env3->SetSecurityToken(v8_str("bar"));
5522
5523 // Check that we do not have access to other.p in env1. |other| is now
5524 // the global object for env3 which has a different security token,
5525 // so access should be blocked.
5526 result = CompileRun("other.p");
5527 CHECK(result->IsUndefined());
5528
5529 // Detach the global for env3 and reattach it to env2.
5530 env3->DetachGlobal();
5531 env2->ReattachGlobal(global2);
5532
5533 // Check that we have access to other.p again in env1. |other| is now
5534 // the global object for env2 which has the same security token as env1.
5535 result = CompileRun("other.p");
5536 CHECK(result->IsInt32());
5537 CHECK_EQ(42, result->Int32Value());
5538
5539 env2.Dispose();
5540 env3.Dispose();
5541}
5542
5543
Steve Block1e0659c2011-05-24 12:43:12 +01005544static bool allowed_access_type[v8::ACCESS_KEYS + 1] = { false };
Steve Blocka7e24c12009-10-30 11:49:00 +00005545static bool NamedAccessBlocker(Local<v8::Object> global,
5546 Local<Value> name,
5547 v8::AccessType type,
5548 Local<Value> data) {
Steve Block1e0659c2011-05-24 12:43:12 +01005549 return Context::GetCurrent()->Global()->Equals(global) ||
5550 allowed_access_type[type];
Steve Blocka7e24c12009-10-30 11:49:00 +00005551}
5552
5553
5554static bool IndexedAccessBlocker(Local<v8::Object> global,
5555 uint32_t key,
5556 v8::AccessType type,
5557 Local<Value> data) {
Steve Block1e0659c2011-05-24 12:43:12 +01005558 return Context::GetCurrent()->Global()->Equals(global) ||
5559 allowed_access_type[type];
Steve Blocka7e24c12009-10-30 11:49:00 +00005560}
5561
5562
5563static int g_echo_value = -1;
5564static v8::Handle<Value> EchoGetter(Local<String> name,
5565 const AccessorInfo& info) {
5566 return v8_num(g_echo_value);
5567}
5568
5569
5570static void EchoSetter(Local<String> name,
5571 Local<Value> value,
5572 const AccessorInfo&) {
5573 if (value->IsNumber())
5574 g_echo_value = value->Int32Value();
5575}
5576
5577
5578static v8::Handle<Value> UnreachableGetter(Local<String> name,
5579 const AccessorInfo& info) {
5580 CHECK(false); // This function should not be called..
5581 return v8::Undefined();
5582}
5583
5584
5585static void UnreachableSetter(Local<String>, Local<Value>,
5586 const AccessorInfo&) {
5587 CHECK(false); // This function should nto be called.
5588}
5589
5590
Steve Block1e0659c2011-05-24 12:43:12 +01005591TEST(AccessControl) {
Steve Blocka7e24c12009-10-30 11:49:00 +00005592 v8::HandleScope handle_scope;
5593 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
5594
5595 global_template->SetAccessCheckCallbacks(NamedAccessBlocker,
5596 IndexedAccessBlocker);
5597
5598 // Add an accessor accessible by cross-domain JS code.
5599 global_template->SetAccessor(
5600 v8_str("accessible_prop"),
5601 EchoGetter, EchoSetter,
5602 v8::Handle<Value>(),
5603 v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE));
5604
5605 // Add an accessor that is not accessible by cross-domain JS code.
5606 global_template->SetAccessor(v8_str("blocked_prop"),
5607 UnreachableGetter, UnreachableSetter,
5608 v8::Handle<Value>(),
5609 v8::DEFAULT);
5610
5611 // Create an environment
5612 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
5613 context0->Enter();
5614
5615 v8::Handle<v8::Object> global0 = context0->Global();
5616
Steve Block1e0659c2011-05-24 12:43:12 +01005617 // Define a property with JS getter and setter.
5618 CompileRun(
5619 "function getter() { return 'getter'; };\n"
5620 "function setter() { return 'setter'; }\n"
5621 "Object.defineProperty(this, 'js_accessor_p', {get:getter, set:setter})");
5622
5623 Local<Value> getter = global0->Get(v8_str("getter"));
5624 Local<Value> setter = global0->Get(v8_str("setter"));
5625
5626 // And define normal element.
5627 global0->Set(239, v8_str("239"));
5628
5629 // Define an element with JS getter and setter.
5630 CompileRun(
5631 "function el_getter() { return 'el_getter'; };\n"
5632 "function el_setter() { return 'el_setter'; };\n"
5633 "Object.defineProperty(this, '42', {get: el_getter, set: el_setter});");
5634
5635 Local<Value> el_getter = global0->Get(v8_str("el_getter"));
5636 Local<Value> el_setter = global0->Get(v8_str("el_setter"));
5637
Steve Blocka7e24c12009-10-30 11:49:00 +00005638 v8::HandleScope scope1;
5639
5640 v8::Persistent<Context> context1 = Context::New();
5641 context1->Enter();
5642
5643 v8::Handle<v8::Object> global1 = context1->Global();
5644 global1->Set(v8_str("other"), global0);
5645
Steve Block1e0659c2011-05-24 12:43:12 +01005646 // Access blocked property.
5647 CompileRun("other.blocked_prop = 1");
5648
5649 ExpectUndefined("other.blocked_prop");
5650 ExpectUndefined(
5651 "Object.getOwnPropertyDescriptor(other, 'blocked_prop')");
5652 ExpectFalse("propertyIsEnumerable.call(other, 'blocked_prop')");
5653
5654 // Enable ACCESS_HAS
5655 allowed_access_type[v8::ACCESS_HAS] = true;
5656 ExpectUndefined("other.blocked_prop");
5657 // ... and now we can get the descriptor...
5658 ExpectUndefined(
5659 "Object.getOwnPropertyDescriptor(other, 'blocked_prop').value");
5660 // ... and enumerate the property.
5661 ExpectTrue("propertyIsEnumerable.call(other, 'blocked_prop')");
5662 allowed_access_type[v8::ACCESS_HAS] = false;
5663
5664 // Access blocked element.
5665 CompileRun("other[239] = 1");
5666
5667 ExpectUndefined("other[239]");
5668 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '239')");
5669 ExpectFalse("propertyIsEnumerable.call(other, '239')");
5670
5671 // Enable ACCESS_HAS
5672 allowed_access_type[v8::ACCESS_HAS] = true;
5673 ExpectUndefined("other[239]");
5674 // ... and now we can get the descriptor...
5675 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '239').value");
5676 // ... and enumerate the property.
5677 ExpectTrue("propertyIsEnumerable.call(other, '239')");
5678 allowed_access_type[v8::ACCESS_HAS] = false;
5679
5680 // Access a property with JS accessor.
5681 CompileRun("other.js_accessor_p = 2");
5682
5683 ExpectUndefined("other.js_accessor_p");
5684 ExpectUndefined(
5685 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p')");
5686
5687 // Enable ACCESS_HAS.
5688 allowed_access_type[v8::ACCESS_HAS] = true;
5689 ExpectUndefined("other.js_accessor_p");
5690 ExpectUndefined(
5691 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').get");
5692 ExpectUndefined(
5693 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').set");
5694 ExpectUndefined(
5695 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').value");
5696 allowed_access_type[v8::ACCESS_HAS] = false;
5697
5698 // Enable both ACCESS_HAS and ACCESS_GET.
5699 allowed_access_type[v8::ACCESS_HAS] = true;
5700 allowed_access_type[v8::ACCESS_GET] = true;
5701
5702 ExpectString("other.js_accessor_p", "getter");
5703 ExpectObject(
5704 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').get", getter);
5705 ExpectUndefined(
5706 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').set");
5707 ExpectUndefined(
5708 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').value");
5709
5710 allowed_access_type[v8::ACCESS_GET] = false;
5711 allowed_access_type[v8::ACCESS_HAS] = false;
5712
5713 // Enable both ACCESS_HAS and ACCESS_SET.
5714 allowed_access_type[v8::ACCESS_HAS] = true;
5715 allowed_access_type[v8::ACCESS_SET] = true;
5716
5717 ExpectUndefined("other.js_accessor_p");
5718 ExpectUndefined(
5719 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').get");
5720 ExpectObject(
5721 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').set", setter);
5722 ExpectUndefined(
5723 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').value");
5724
5725 allowed_access_type[v8::ACCESS_SET] = false;
5726 allowed_access_type[v8::ACCESS_HAS] = false;
5727
5728 // Enable both ACCESS_HAS, ACCESS_GET and ACCESS_SET.
5729 allowed_access_type[v8::ACCESS_HAS] = true;
5730 allowed_access_type[v8::ACCESS_GET] = true;
5731 allowed_access_type[v8::ACCESS_SET] = true;
5732
5733 ExpectString("other.js_accessor_p", "getter");
5734 ExpectObject(
5735 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').get", getter);
5736 ExpectObject(
5737 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').set", setter);
5738 ExpectUndefined(
5739 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').value");
5740
5741 allowed_access_type[v8::ACCESS_SET] = false;
5742 allowed_access_type[v8::ACCESS_GET] = false;
5743 allowed_access_type[v8::ACCESS_HAS] = false;
5744
5745 // Access an element with JS accessor.
5746 CompileRun("other[42] = 2");
5747
5748 ExpectUndefined("other[42]");
5749 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42')");
5750
5751 // Enable ACCESS_HAS.
5752 allowed_access_type[v8::ACCESS_HAS] = true;
5753 ExpectUndefined("other[42]");
5754 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').get");
5755 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').set");
5756 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').value");
5757 allowed_access_type[v8::ACCESS_HAS] = false;
5758
5759 // Enable both ACCESS_HAS and ACCESS_GET.
5760 allowed_access_type[v8::ACCESS_HAS] = true;
5761 allowed_access_type[v8::ACCESS_GET] = true;
5762
5763 ExpectString("other[42]", "el_getter");
5764 ExpectObject("Object.getOwnPropertyDescriptor(other, '42').get", el_getter);
5765 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').set");
5766 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').value");
5767
5768 allowed_access_type[v8::ACCESS_GET] = false;
5769 allowed_access_type[v8::ACCESS_HAS] = false;
5770
5771 // Enable both ACCESS_HAS and ACCESS_SET.
5772 allowed_access_type[v8::ACCESS_HAS] = true;
5773 allowed_access_type[v8::ACCESS_SET] = true;
5774
5775 ExpectUndefined("other[42]");
5776 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').get");
5777 ExpectObject("Object.getOwnPropertyDescriptor(other, '42').set", el_setter);
5778 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').value");
5779
5780 allowed_access_type[v8::ACCESS_SET] = false;
5781 allowed_access_type[v8::ACCESS_HAS] = false;
5782
5783 // Enable both ACCESS_HAS, ACCESS_GET and ACCESS_SET.
5784 allowed_access_type[v8::ACCESS_HAS] = true;
5785 allowed_access_type[v8::ACCESS_GET] = true;
5786 allowed_access_type[v8::ACCESS_SET] = true;
5787
5788 ExpectString("other[42]", "el_getter");
5789 ExpectObject("Object.getOwnPropertyDescriptor(other, '42').get", el_getter);
5790 ExpectObject("Object.getOwnPropertyDescriptor(other, '42').set", el_setter);
5791 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').value");
5792
5793 allowed_access_type[v8::ACCESS_SET] = false;
5794 allowed_access_type[v8::ACCESS_GET] = false;
5795 allowed_access_type[v8::ACCESS_HAS] = false;
5796
Steve Blocka7e24c12009-10-30 11:49:00 +00005797 v8::Handle<Value> value;
5798
Steve Blocka7e24c12009-10-30 11:49:00 +00005799 // Access accessible property
Steve Block1e0659c2011-05-24 12:43:12 +01005800 value = CompileRun("other.accessible_prop = 3");
Steve Blocka7e24c12009-10-30 11:49:00 +00005801 CHECK(value->IsNumber());
5802 CHECK_EQ(3, value->Int32Value());
Andrei Popescu31002712010-02-23 13:46:05 +00005803 CHECK_EQ(3, g_echo_value);
Steve Blocka7e24c12009-10-30 11:49:00 +00005804
Steve Block1e0659c2011-05-24 12:43:12 +01005805 value = CompileRun("other.accessible_prop");
Steve Blocka7e24c12009-10-30 11:49:00 +00005806 CHECK(value->IsNumber());
5807 CHECK_EQ(3, value->Int32Value());
5808
Steve Block1e0659c2011-05-24 12:43:12 +01005809 value = CompileRun(
5810 "Object.getOwnPropertyDescriptor(other, 'accessible_prop').value");
5811 CHECK(value->IsNumber());
5812 CHECK_EQ(3, value->Int32Value());
5813
5814 value = CompileRun("propertyIsEnumerable.call(other, 'accessible_prop')");
Steve Blocka7e24c12009-10-30 11:49:00 +00005815 CHECK(value->IsTrue());
5816
5817 // Enumeration doesn't enumerate accessors from inaccessible objects in
5818 // the prototype chain even if the accessors are in themselves accessible.
Steve Block1e0659c2011-05-24 12:43:12 +01005819 value =
Steve Blocka7e24c12009-10-30 11:49:00 +00005820 CompileRun("(function(){var obj = {'__proto__':other};"
5821 "for (var p in obj)"
5822 " if (p == 'accessible_prop' || p == 'blocked_prop') {"
5823 " return false;"
5824 " }"
5825 "return true;})()");
Steve Block1e0659c2011-05-24 12:43:12 +01005826 CHECK(value->IsTrue());
Steve Blocka7e24c12009-10-30 11:49:00 +00005827
5828 context1->Exit();
5829 context0->Exit();
5830 context1.Dispose();
5831 context0.Dispose();
5832}
5833
5834
Ben Murdoche0cee9b2011-05-25 10:26:03 +01005835TEST(AccessControlES5) {
5836 v8::HandleScope handle_scope;
5837 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
5838
5839 global_template->SetAccessCheckCallbacks(NamedAccessBlocker,
5840 IndexedAccessBlocker);
5841
Steve Block44f0eee2011-05-26 01:26:41 +01005842 // Add accessible accessor.
5843 global_template->SetAccessor(
5844 v8_str("accessible_prop"),
5845 EchoGetter, EchoSetter,
5846 v8::Handle<Value>(),
5847 v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE));
5848
5849
Ben Murdoche0cee9b2011-05-25 10:26:03 +01005850 // Add an accessor that is not accessible by cross-domain JS code.
5851 global_template->SetAccessor(v8_str("blocked_prop"),
5852 UnreachableGetter, UnreachableSetter,
5853 v8::Handle<Value>(),
5854 v8::DEFAULT);
5855
5856 // Create an environment
5857 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
5858 context0->Enter();
5859
5860 v8::Handle<v8::Object> global0 = context0->Global();
5861
5862 v8::Persistent<Context> context1 = Context::New();
5863 context1->Enter();
5864 v8::Handle<v8::Object> global1 = context1->Global();
5865 global1->Set(v8_str("other"), global0);
5866
5867 // Regression test for issue 1154.
5868 ExpectTrue("Object.keys(other).indexOf('blocked_prop') == -1");
5869
5870 ExpectUndefined("other.blocked_prop");
5871
5872 // Regression test for issue 1027.
5873 CompileRun("Object.defineProperty(\n"
5874 " other, 'blocked_prop', {configurable: false})");
5875 ExpectUndefined("other.blocked_prop");
5876 ExpectUndefined(
5877 "Object.getOwnPropertyDescriptor(other, 'blocked_prop')");
5878
5879 // Regression test for issue 1171.
5880 ExpectTrue("Object.isExtensible(other)");
5881 CompileRun("Object.preventExtensions(other)");
5882 ExpectTrue("Object.isExtensible(other)");
5883
5884 // Object.seal and Object.freeze.
5885 CompileRun("Object.freeze(other)");
5886 ExpectTrue("Object.isExtensible(other)");
5887
5888 CompileRun("Object.seal(other)");
5889 ExpectTrue("Object.isExtensible(other)");
Steve Block44f0eee2011-05-26 01:26:41 +01005890
5891 // Regression test for issue 1250.
5892 // Make sure that we can set the accessible accessors value using normal
5893 // assignment.
5894 CompileRun("other.accessible_prop = 42");
5895 CHECK_EQ(42, g_echo_value);
5896
5897 v8::Handle<Value> value;
5898 // We follow Safari in ignoring assignments to host object accessors.
5899 CompileRun("Object.defineProperty(other, 'accessible_prop', {value: -1})");
5900 value = CompileRun("other.accessible_prop == 42");
5901 CHECK(value->IsTrue());
Ben Murdoche0cee9b2011-05-25 10:26:03 +01005902}
5903
5904
Leon Clarke4515c472010-02-03 11:58:03 +00005905static bool GetOwnPropertyNamesNamedBlocker(Local<v8::Object> global,
5906 Local<Value> name,
5907 v8::AccessType type,
5908 Local<Value> data) {
5909 return false;
5910}
5911
5912
5913static bool GetOwnPropertyNamesIndexedBlocker(Local<v8::Object> global,
5914 uint32_t key,
5915 v8::AccessType type,
5916 Local<Value> data) {
5917 return false;
5918}
5919
5920
5921THREADED_TEST(AccessControlGetOwnPropertyNames) {
5922 v8::HandleScope handle_scope;
5923 v8::Handle<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New();
5924
5925 obj_template->Set(v8_str("x"), v8::Integer::New(42));
5926 obj_template->SetAccessCheckCallbacks(GetOwnPropertyNamesNamedBlocker,
5927 GetOwnPropertyNamesIndexedBlocker);
5928
5929 // Create an environment
5930 v8::Persistent<Context> context0 = Context::New(NULL, obj_template);
5931 context0->Enter();
5932
5933 v8::Handle<v8::Object> global0 = context0->Global();
5934
5935 v8::HandleScope scope1;
5936
5937 v8::Persistent<Context> context1 = Context::New();
5938 context1->Enter();
5939
5940 v8::Handle<v8::Object> global1 = context1->Global();
5941 global1->Set(v8_str("other"), global0);
5942 global1->Set(v8_str("object"), obj_template->NewInstance());
5943
5944 v8::Handle<Value> value;
5945
5946 // Attempt to get the property names of the other global object and
5947 // of an object that requires access checks. Accessing the other
5948 // global object should be blocked by access checks on the global
5949 // proxy object. Accessing the object that requires access checks
5950 // is blocked by the access checks on the object itself.
5951 value = CompileRun("Object.getOwnPropertyNames(other).length == 0");
5952 CHECK(value->IsTrue());
5953
5954 value = CompileRun("Object.getOwnPropertyNames(object).length == 0");
5955 CHECK(value->IsTrue());
5956
5957 context1->Exit();
5958 context0->Exit();
5959 context1.Dispose();
5960 context0.Dispose();
5961}
5962
5963
Steve Block8defd9f2010-07-08 12:39:36 +01005964static v8::Handle<v8::Array> NamedPropertyEnumerator(const AccessorInfo& info) {
5965 v8::Handle<v8::Array> result = v8::Array::New(1);
5966 result->Set(0, v8_str("x"));
5967 return result;
5968}
5969
5970
5971THREADED_TEST(GetOwnPropertyNamesWithInterceptor) {
5972 v8::HandleScope handle_scope;
5973 v8::Handle<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New();
5974
5975 obj_template->Set(v8_str("x"), v8::Integer::New(42));
5976 obj_template->SetNamedPropertyHandler(NULL, NULL, NULL, NULL,
5977 NamedPropertyEnumerator);
5978
5979 LocalContext context;
5980 v8::Handle<v8::Object> global = context->Global();
5981 global->Set(v8_str("object"), obj_template->NewInstance());
5982
5983 v8::Handle<Value> value =
5984 CompileRun("Object.getOwnPropertyNames(object).join(',')");
5985 CHECK_EQ(v8_str("x"), value);
5986}
5987
5988
Steve Blocka7e24c12009-10-30 11:49:00 +00005989static v8::Handle<Value> ConstTenGetter(Local<String> name,
5990 const AccessorInfo& info) {
5991 return v8_num(10);
5992}
5993
5994
5995THREADED_TEST(CrossDomainAccessors) {
5996 v8::HandleScope handle_scope;
5997
5998 v8::Handle<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New();
5999
6000 v8::Handle<v8::ObjectTemplate> global_template =
6001 func_template->InstanceTemplate();
6002
6003 v8::Handle<v8::ObjectTemplate> proto_template =
6004 func_template->PrototypeTemplate();
6005
6006 // Add an accessor to proto that's accessible by cross-domain JS code.
6007 proto_template->SetAccessor(v8_str("accessible"),
6008 ConstTenGetter, 0,
6009 v8::Handle<Value>(),
6010 v8::ALL_CAN_READ);
6011
6012 // Add an accessor that is not accessible by cross-domain JS code.
6013 global_template->SetAccessor(v8_str("unreachable"),
6014 UnreachableGetter, 0,
6015 v8::Handle<Value>(),
6016 v8::DEFAULT);
6017
6018 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
6019 context0->Enter();
6020
6021 Local<v8::Object> global = context0->Global();
6022 // Add a normal property that shadows 'accessible'
6023 global->Set(v8_str("accessible"), v8_num(11));
6024
6025 // Enter a new context.
6026 v8::HandleScope scope1;
6027 v8::Persistent<Context> context1 = Context::New();
6028 context1->Enter();
6029
6030 v8::Handle<v8::Object> global1 = context1->Global();
6031 global1->Set(v8_str("other"), global);
6032
6033 // Should return 10, instead of 11
6034 v8::Handle<Value> value = v8_compile("other.accessible")->Run();
6035 CHECK(value->IsNumber());
6036 CHECK_EQ(10, value->Int32Value());
6037
6038 value = v8_compile("other.unreachable")->Run();
6039 CHECK(value->IsUndefined());
6040
6041 context1->Exit();
6042 context0->Exit();
6043 context1.Dispose();
6044 context0.Dispose();
6045}
6046
6047
6048static int named_access_count = 0;
6049static int indexed_access_count = 0;
6050
6051static bool NamedAccessCounter(Local<v8::Object> global,
6052 Local<Value> name,
6053 v8::AccessType type,
6054 Local<Value> data) {
6055 named_access_count++;
6056 return true;
6057}
6058
6059
6060static bool IndexedAccessCounter(Local<v8::Object> global,
6061 uint32_t key,
6062 v8::AccessType type,
6063 Local<Value> data) {
6064 indexed_access_count++;
6065 return true;
6066}
6067
6068
6069// This one is too easily disturbed by other tests.
6070TEST(AccessControlIC) {
6071 named_access_count = 0;
6072 indexed_access_count = 0;
6073
6074 v8::HandleScope handle_scope;
6075
6076 // Create an environment.
6077 v8::Persistent<Context> context0 = Context::New();
6078 context0->Enter();
6079
6080 // Create an object that requires access-check functions to be
6081 // called for cross-domain access.
6082 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
6083 object_template->SetAccessCheckCallbacks(NamedAccessCounter,
6084 IndexedAccessCounter);
6085 Local<v8::Object> object = object_template->NewInstance();
6086
6087 v8::HandleScope scope1;
6088
6089 // Create another environment.
6090 v8::Persistent<Context> context1 = Context::New();
6091 context1->Enter();
6092
6093 // Make easy access to the object from the other environment.
6094 v8::Handle<v8::Object> global1 = context1->Global();
6095 global1->Set(v8_str("obj"), object);
6096
6097 v8::Handle<Value> value;
6098
6099 // Check that the named access-control function is called every time.
6100 CompileRun("function testProp(obj) {"
6101 " for (var i = 0; i < 10; i++) obj.prop = 1;"
6102 " for (var j = 0; j < 10; j++) obj.prop;"
6103 " return obj.prop"
6104 "}");
6105 value = CompileRun("testProp(obj)");
6106 CHECK(value->IsNumber());
6107 CHECK_EQ(1, value->Int32Value());
6108 CHECK_EQ(21, named_access_count);
6109
6110 // Check that the named access-control function is called every time.
6111 CompileRun("var p = 'prop';"
6112 "function testKeyed(obj) {"
6113 " for (var i = 0; i < 10; i++) obj[p] = 1;"
6114 " for (var j = 0; j < 10; j++) obj[p];"
6115 " return obj[p];"
6116 "}");
6117 // Use obj which requires access checks. No inline caching is used
6118 // in that case.
6119 value = CompileRun("testKeyed(obj)");
6120 CHECK(value->IsNumber());
6121 CHECK_EQ(1, value->Int32Value());
6122 CHECK_EQ(42, named_access_count);
6123 // Force the inline caches into generic state and try again.
6124 CompileRun("testKeyed({ a: 0 })");
6125 CompileRun("testKeyed({ b: 0 })");
6126 value = CompileRun("testKeyed(obj)");
6127 CHECK(value->IsNumber());
6128 CHECK_EQ(1, value->Int32Value());
6129 CHECK_EQ(63, named_access_count);
6130
6131 // Check that the indexed access-control function is called every time.
6132 CompileRun("function testIndexed(obj) {"
6133 " for (var i = 0; i < 10; i++) obj[0] = 1;"
6134 " for (var j = 0; j < 10; j++) obj[0];"
6135 " return obj[0]"
6136 "}");
6137 value = CompileRun("testIndexed(obj)");
6138 CHECK(value->IsNumber());
6139 CHECK_EQ(1, value->Int32Value());
6140 CHECK_EQ(21, indexed_access_count);
6141 // Force the inline caches into generic state.
6142 CompileRun("testIndexed(new Array(1))");
6143 // Test that the indexed access check is called.
6144 value = CompileRun("testIndexed(obj)");
6145 CHECK(value->IsNumber());
6146 CHECK_EQ(1, value->Int32Value());
6147 CHECK_EQ(42, indexed_access_count);
6148
6149 // Check that the named access check is called when invoking
6150 // functions on an object that requires access checks.
6151 CompileRun("obj.f = function() {}");
6152 CompileRun("function testCallNormal(obj) {"
6153 " for (var i = 0; i < 10; i++) obj.f();"
6154 "}");
6155 CompileRun("testCallNormal(obj)");
6156 CHECK_EQ(74, named_access_count);
6157
6158 // Force obj into slow case.
6159 value = CompileRun("delete obj.prop");
6160 CHECK(value->BooleanValue());
6161 // Force inline caches into dictionary probing mode.
6162 CompileRun("var o = { x: 0 }; delete o.x; testProp(o);");
6163 // Test that the named access check is called.
6164 value = CompileRun("testProp(obj);");
6165 CHECK(value->IsNumber());
6166 CHECK_EQ(1, value->Int32Value());
6167 CHECK_EQ(96, named_access_count);
6168
6169 // Force the call inline cache into dictionary probing mode.
6170 CompileRun("o.f = function() {}; testCallNormal(o)");
6171 // Test that the named access check is still called for each
6172 // invocation of the function.
6173 value = CompileRun("testCallNormal(obj)");
6174 CHECK_EQ(106, named_access_count);
6175
6176 context1->Exit();
6177 context0->Exit();
6178 context1.Dispose();
6179 context0.Dispose();
6180}
6181
6182
6183static bool NamedAccessFlatten(Local<v8::Object> global,
6184 Local<Value> name,
6185 v8::AccessType type,
6186 Local<Value> data) {
6187 char buf[100];
6188 int len;
6189
6190 CHECK(name->IsString());
6191
6192 memset(buf, 0x1, sizeof(buf));
Steve Block6ded16b2010-05-10 14:33:55 +01006193 len = name.As<String>()->WriteAscii(buf);
Steve Blocka7e24c12009-10-30 11:49:00 +00006194 CHECK_EQ(4, len);
6195
6196 uint16_t buf2[100];
6197
6198 memset(buf, 0x1, sizeof(buf));
Steve Block6ded16b2010-05-10 14:33:55 +01006199 len = name.As<String>()->Write(buf2);
Steve Blocka7e24c12009-10-30 11:49:00 +00006200 CHECK_EQ(4, len);
6201
6202 return true;
6203}
6204
6205
6206static bool IndexedAccessFlatten(Local<v8::Object> global,
6207 uint32_t key,
6208 v8::AccessType type,
6209 Local<Value> data) {
6210 return true;
6211}
6212
6213
6214// Regression test. In access checks, operations that may cause
6215// garbage collection are not allowed. It used to be the case that
6216// using the Write operation on a string could cause a garbage
6217// collection due to flattening of the string. This is no longer the
6218// case.
6219THREADED_TEST(AccessControlFlatten) {
6220 named_access_count = 0;
6221 indexed_access_count = 0;
6222
6223 v8::HandleScope handle_scope;
6224
6225 // Create an environment.
6226 v8::Persistent<Context> context0 = Context::New();
6227 context0->Enter();
6228
6229 // Create an object that requires access-check functions to be
6230 // called for cross-domain access.
6231 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
6232 object_template->SetAccessCheckCallbacks(NamedAccessFlatten,
6233 IndexedAccessFlatten);
6234 Local<v8::Object> object = object_template->NewInstance();
6235
6236 v8::HandleScope scope1;
6237
6238 // Create another environment.
6239 v8::Persistent<Context> context1 = Context::New();
6240 context1->Enter();
6241
6242 // Make easy access to the object from the other environment.
6243 v8::Handle<v8::Object> global1 = context1->Global();
6244 global1->Set(v8_str("obj"), object);
6245
6246 v8::Handle<Value> value;
6247
6248 value = v8_compile("var p = 'as' + 'df';")->Run();
6249 value = v8_compile("obj[p];")->Run();
6250
6251 context1->Exit();
6252 context0->Exit();
6253 context1.Dispose();
6254 context0.Dispose();
6255}
6256
6257
6258static v8::Handle<Value> AccessControlNamedGetter(
6259 Local<String>, const AccessorInfo&) {
6260 return v8::Integer::New(42);
6261}
6262
6263
6264static v8::Handle<Value> AccessControlNamedSetter(
6265 Local<String>, Local<Value> value, const AccessorInfo&) {
6266 return value;
6267}
6268
6269
6270static v8::Handle<Value> AccessControlIndexedGetter(
6271 uint32_t index,
6272 const AccessorInfo& info) {
6273 return v8_num(42);
6274}
6275
6276
6277static v8::Handle<Value> AccessControlIndexedSetter(
6278 uint32_t, Local<Value> value, const AccessorInfo&) {
6279 return value;
6280}
6281
6282
6283THREADED_TEST(AccessControlInterceptorIC) {
6284 named_access_count = 0;
6285 indexed_access_count = 0;
6286
6287 v8::HandleScope handle_scope;
6288
6289 // Create an environment.
6290 v8::Persistent<Context> context0 = Context::New();
6291 context0->Enter();
6292
6293 // Create an object that requires access-check functions to be
6294 // called for cross-domain access. The object also has interceptors
6295 // interceptor.
6296 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
6297 object_template->SetAccessCheckCallbacks(NamedAccessCounter,
6298 IndexedAccessCounter);
6299 object_template->SetNamedPropertyHandler(AccessControlNamedGetter,
6300 AccessControlNamedSetter);
6301 object_template->SetIndexedPropertyHandler(AccessControlIndexedGetter,
6302 AccessControlIndexedSetter);
6303 Local<v8::Object> object = object_template->NewInstance();
6304
6305 v8::HandleScope scope1;
6306
6307 // Create another environment.
6308 v8::Persistent<Context> context1 = Context::New();
6309 context1->Enter();
6310
6311 // Make easy access to the object from the other environment.
6312 v8::Handle<v8::Object> global1 = context1->Global();
6313 global1->Set(v8_str("obj"), object);
6314
6315 v8::Handle<Value> value;
6316
6317 // Check that the named access-control function is called every time
6318 // eventhough there is an interceptor on the object.
6319 value = v8_compile("for (var i = 0; i < 10; i++) obj.x = 1;")->Run();
6320 value = v8_compile("for (var i = 0; i < 10; i++) obj.x;"
6321 "obj.x")->Run();
6322 CHECK(value->IsNumber());
6323 CHECK_EQ(42, value->Int32Value());
6324 CHECK_EQ(21, named_access_count);
6325
6326 value = v8_compile("var p = 'x';")->Run();
6327 value = v8_compile("for (var i = 0; i < 10; i++) obj[p] = 1;")->Run();
6328 value = v8_compile("for (var i = 0; i < 10; i++) obj[p];"
6329 "obj[p]")->Run();
6330 CHECK(value->IsNumber());
6331 CHECK_EQ(42, value->Int32Value());
6332 CHECK_EQ(42, named_access_count);
6333
6334 // Check that the indexed access-control function is called every
6335 // time eventhough there is an interceptor on the object.
6336 value = v8_compile("for (var i = 0; i < 10; i++) obj[0] = 1;")->Run();
6337 value = v8_compile("for (var i = 0; i < 10; i++) obj[0];"
6338 "obj[0]")->Run();
6339 CHECK(value->IsNumber());
6340 CHECK_EQ(42, value->Int32Value());
6341 CHECK_EQ(21, indexed_access_count);
6342
6343 context1->Exit();
6344 context0->Exit();
6345 context1.Dispose();
6346 context0.Dispose();
6347}
6348
6349
6350THREADED_TEST(Version) {
6351 v8::V8::GetVersion();
6352}
6353
6354
6355static v8::Handle<Value> InstanceFunctionCallback(const v8::Arguments& args) {
6356 ApiTestFuzzer::Fuzz();
6357 return v8_num(12);
6358}
6359
6360
6361THREADED_TEST(InstanceProperties) {
6362 v8::HandleScope handle_scope;
6363 LocalContext context;
6364
6365 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
6366 Local<ObjectTemplate> instance = t->InstanceTemplate();
6367
6368 instance->Set(v8_str("x"), v8_num(42));
6369 instance->Set(v8_str("f"),
6370 v8::FunctionTemplate::New(InstanceFunctionCallback));
6371
6372 Local<Value> o = t->GetFunction()->NewInstance();
6373
6374 context->Global()->Set(v8_str("i"), o);
6375 Local<Value> value = Script::Compile(v8_str("i.x"))->Run();
6376 CHECK_EQ(42, value->Int32Value());
6377
6378 value = Script::Compile(v8_str("i.f()"))->Run();
6379 CHECK_EQ(12, value->Int32Value());
6380}
6381
6382
6383static v8::Handle<Value>
6384GlobalObjectInstancePropertiesGet(Local<String> key, const AccessorInfo&) {
6385 ApiTestFuzzer::Fuzz();
6386 return v8::Handle<Value>();
6387}
6388
6389
6390THREADED_TEST(GlobalObjectInstanceProperties) {
6391 v8::HandleScope handle_scope;
6392
6393 Local<Value> global_object;
6394
6395 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
6396 t->InstanceTemplate()->SetNamedPropertyHandler(
6397 GlobalObjectInstancePropertiesGet);
6398 Local<ObjectTemplate> instance_template = t->InstanceTemplate();
6399 instance_template->Set(v8_str("x"), v8_num(42));
6400 instance_template->Set(v8_str("f"),
6401 v8::FunctionTemplate::New(InstanceFunctionCallback));
6402
Ben Murdochb0fe1622011-05-05 13:52:32 +01006403 // The script to check how Crankshaft compiles missing global function
6404 // invocations. function g is not defined and should throw on call.
6405 const char* script =
6406 "function wrapper(call) {"
6407 " var x = 0, y = 1;"
6408 " for (var i = 0; i < 1000; i++) {"
6409 " x += i * 100;"
6410 " y += i * 100;"
6411 " }"
6412 " if (call) g();"
6413 "}"
6414 "for (var i = 0; i < 17; i++) wrapper(false);"
6415 "var thrown = 0;"
6416 "try { wrapper(true); } catch (e) { thrown = 1; };"
6417 "thrown";
6418
Steve Blocka7e24c12009-10-30 11:49:00 +00006419 {
6420 LocalContext env(NULL, instance_template);
6421 // Hold on to the global object so it can be used again in another
6422 // environment initialization.
6423 global_object = env->Global();
6424
6425 Local<Value> value = Script::Compile(v8_str("x"))->Run();
6426 CHECK_EQ(42, value->Int32Value());
6427 value = Script::Compile(v8_str("f()"))->Run();
6428 CHECK_EQ(12, value->Int32Value());
Ben Murdochb0fe1622011-05-05 13:52:32 +01006429 value = Script::Compile(v8_str(script))->Run();
6430 CHECK_EQ(1, value->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00006431 }
6432
6433 {
6434 // Create new environment reusing the global object.
6435 LocalContext env(NULL, instance_template, global_object);
6436 Local<Value> value = Script::Compile(v8_str("x"))->Run();
6437 CHECK_EQ(42, value->Int32Value());
6438 value = Script::Compile(v8_str("f()"))->Run();
6439 CHECK_EQ(12, value->Int32Value());
Ben Murdochb0fe1622011-05-05 13:52:32 +01006440 value = Script::Compile(v8_str(script))->Run();
6441 CHECK_EQ(1, value->Int32Value());
6442 }
6443}
6444
6445
6446THREADED_TEST(CallKnownGlobalReceiver) {
6447 v8::HandleScope handle_scope;
6448
6449 Local<Value> global_object;
6450
6451 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
6452 Local<ObjectTemplate> instance_template = t->InstanceTemplate();
6453
6454 // The script to check that we leave global object not
6455 // global object proxy on stack when we deoptimize from inside
6456 // arguments evaluation.
6457 // To provoke error we need to both force deoptimization
6458 // from arguments evaluation and to force CallIC to take
6459 // CallIC_Miss code path that can't cope with global proxy.
6460 const char* script =
6461 "function bar(x, y) { try { } finally { } }"
6462 "function baz(x) { try { } finally { } }"
6463 "function bom(x) { try { } finally { } }"
6464 "function foo(x) { bar([x], bom(2)); }"
6465 "for (var i = 0; i < 10000; i++) foo(1);"
6466 "foo";
6467
6468 Local<Value> foo;
6469 {
6470 LocalContext env(NULL, instance_template);
6471 // Hold on to the global object so it can be used again in another
6472 // environment initialization.
6473 global_object = env->Global();
6474 foo = Script::Compile(v8_str(script))->Run();
6475 }
6476
6477 {
6478 // Create new environment reusing the global object.
6479 LocalContext env(NULL, instance_template, global_object);
6480 env->Global()->Set(v8_str("foo"), foo);
6481 Local<Value> value = Script::Compile(v8_str("foo()"))->Run();
Steve Blocka7e24c12009-10-30 11:49:00 +00006482 }
6483}
6484
6485
6486static v8::Handle<Value> ShadowFunctionCallback(const v8::Arguments& args) {
6487 ApiTestFuzzer::Fuzz();
6488 return v8_num(42);
6489}
6490
6491
6492static int shadow_y;
6493static int shadow_y_setter_call_count;
6494static int shadow_y_getter_call_count;
6495
6496
6497static void ShadowYSetter(Local<String>, Local<Value>, const AccessorInfo&) {
6498 shadow_y_setter_call_count++;
6499 shadow_y = 42;
6500}
6501
6502
6503static v8::Handle<Value> ShadowYGetter(Local<String> name,
6504 const AccessorInfo& info) {
6505 ApiTestFuzzer::Fuzz();
6506 shadow_y_getter_call_count++;
6507 return v8_num(shadow_y);
6508}
6509
6510
6511static v8::Handle<Value> ShadowIndexedGet(uint32_t index,
6512 const AccessorInfo& info) {
6513 return v8::Handle<Value>();
6514}
6515
6516
6517static v8::Handle<Value> ShadowNamedGet(Local<String> key,
6518 const AccessorInfo&) {
6519 return v8::Handle<Value>();
6520}
6521
6522
6523THREADED_TEST(ShadowObject) {
6524 shadow_y = shadow_y_setter_call_count = shadow_y_getter_call_count = 0;
6525 v8::HandleScope handle_scope;
6526
6527 Local<ObjectTemplate> global_template = v8::ObjectTemplate::New();
6528 LocalContext context(NULL, global_template);
6529
6530 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
6531 t->InstanceTemplate()->SetNamedPropertyHandler(ShadowNamedGet);
6532 t->InstanceTemplate()->SetIndexedPropertyHandler(ShadowIndexedGet);
6533 Local<ObjectTemplate> proto = t->PrototypeTemplate();
6534 Local<ObjectTemplate> instance = t->InstanceTemplate();
6535
6536 // Only allow calls of f on instances of t.
6537 Local<v8::Signature> signature = v8::Signature::New(t);
6538 proto->Set(v8_str("f"),
6539 v8::FunctionTemplate::New(ShadowFunctionCallback,
6540 Local<Value>(),
6541 signature));
6542 proto->Set(v8_str("x"), v8_num(12));
6543
6544 instance->SetAccessor(v8_str("y"), ShadowYGetter, ShadowYSetter);
6545
6546 Local<Value> o = t->GetFunction()->NewInstance();
6547 context->Global()->Set(v8_str("__proto__"), o);
6548
6549 Local<Value> value =
6550 Script::Compile(v8_str("propertyIsEnumerable(0)"))->Run();
6551 CHECK(value->IsBoolean());
6552 CHECK(!value->BooleanValue());
6553
6554 value = Script::Compile(v8_str("x"))->Run();
6555 CHECK_EQ(12, value->Int32Value());
6556
6557 value = Script::Compile(v8_str("f()"))->Run();
6558 CHECK_EQ(42, value->Int32Value());
6559
6560 Script::Compile(v8_str("y = 42"))->Run();
6561 CHECK_EQ(1, shadow_y_setter_call_count);
6562 value = Script::Compile(v8_str("y"))->Run();
6563 CHECK_EQ(1, shadow_y_getter_call_count);
6564 CHECK_EQ(42, value->Int32Value());
6565}
6566
6567
6568THREADED_TEST(HiddenPrototype) {
6569 v8::HandleScope handle_scope;
6570 LocalContext context;
6571
6572 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New();
6573 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0));
6574 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New();
6575 t1->SetHiddenPrototype(true);
6576 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1));
6577 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New();
6578 t2->SetHiddenPrototype(true);
6579 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2));
6580 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New();
6581 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3));
6582
6583 Local<v8::Object> o0 = t0->GetFunction()->NewInstance();
6584 Local<v8::Object> o1 = t1->GetFunction()->NewInstance();
6585 Local<v8::Object> o2 = t2->GetFunction()->NewInstance();
6586 Local<v8::Object> o3 = t3->GetFunction()->NewInstance();
6587
6588 // Setting the prototype on an object skips hidden prototypes.
6589 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
6590 o0->Set(v8_str("__proto__"), o1);
6591 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
6592 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
6593 o0->Set(v8_str("__proto__"), o2);
6594 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
6595 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
6596 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
6597 o0->Set(v8_str("__proto__"), o3);
6598 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
6599 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
6600 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
6601 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value());
6602
6603 // Getting the prototype of o0 should get the first visible one
6604 // which is o3. Therefore, z should not be defined on the prototype
6605 // object.
6606 Local<Value> proto = o0->Get(v8_str("__proto__"));
6607 CHECK(proto->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01006608 CHECK(proto.As<v8::Object>()->Get(v8_str("z"))->IsUndefined());
Steve Blocka7e24c12009-10-30 11:49:00 +00006609}
6610
6611
Andrei Popescu402d9372010-02-26 13:31:12 +00006612THREADED_TEST(SetPrototype) {
6613 v8::HandleScope handle_scope;
6614 LocalContext context;
6615
6616 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New();
6617 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0));
6618 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New();
6619 t1->SetHiddenPrototype(true);
6620 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1));
6621 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New();
6622 t2->SetHiddenPrototype(true);
6623 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2));
6624 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New();
6625 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3));
6626
6627 Local<v8::Object> o0 = t0->GetFunction()->NewInstance();
6628 Local<v8::Object> o1 = t1->GetFunction()->NewInstance();
6629 Local<v8::Object> o2 = t2->GetFunction()->NewInstance();
6630 Local<v8::Object> o3 = t3->GetFunction()->NewInstance();
6631
6632 // Setting the prototype on an object does not skip hidden prototypes.
6633 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
6634 CHECK(o0->SetPrototype(o1));
6635 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
6636 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
6637 CHECK(o1->SetPrototype(o2));
6638 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
6639 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
6640 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
6641 CHECK(o2->SetPrototype(o3));
6642 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
6643 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
6644 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
6645 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value());
6646
6647 // Getting the prototype of o0 should get the first visible one
6648 // which is o3. Therefore, z should not be defined on the prototype
6649 // object.
6650 Local<Value> proto = o0->Get(v8_str("__proto__"));
6651 CHECK(proto->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01006652 CHECK_EQ(proto.As<v8::Object>(), o3);
Andrei Popescu402d9372010-02-26 13:31:12 +00006653
6654 // However, Object::GetPrototype ignores hidden prototype.
6655 Local<Value> proto0 = o0->GetPrototype();
6656 CHECK(proto0->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01006657 CHECK_EQ(proto0.As<v8::Object>(), o1);
Andrei Popescu402d9372010-02-26 13:31:12 +00006658
6659 Local<Value> proto1 = o1->GetPrototype();
6660 CHECK(proto1->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01006661 CHECK_EQ(proto1.As<v8::Object>(), o2);
Andrei Popescu402d9372010-02-26 13:31:12 +00006662
6663 Local<Value> proto2 = o2->GetPrototype();
6664 CHECK(proto2->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01006665 CHECK_EQ(proto2.As<v8::Object>(), o3);
Andrei Popescu402d9372010-02-26 13:31:12 +00006666}
6667
6668
6669THREADED_TEST(SetPrototypeThrows) {
6670 v8::HandleScope handle_scope;
6671 LocalContext context;
6672
6673 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
6674
6675 Local<v8::Object> o0 = t->GetFunction()->NewInstance();
6676 Local<v8::Object> o1 = t->GetFunction()->NewInstance();
6677
6678 CHECK(o0->SetPrototype(o1));
6679 // If setting the prototype leads to the cycle, SetPrototype should
6680 // return false and keep VM in sane state.
6681 v8::TryCatch try_catch;
6682 CHECK(!o1->SetPrototype(o0));
6683 CHECK(!try_catch.HasCaught());
Steve Block44f0eee2011-05-26 01:26:41 +01006684 ASSERT(!i::Isolate::Current()->has_pending_exception());
Andrei Popescu402d9372010-02-26 13:31:12 +00006685
6686 CHECK_EQ(42, CompileRun("function f() { return 42; }; f()")->Int32Value());
6687}
6688
6689
Steve Blocka7e24c12009-10-30 11:49:00 +00006690THREADED_TEST(GetterSetterExceptions) {
6691 v8::HandleScope handle_scope;
6692 LocalContext context;
6693 CompileRun(
6694 "function Foo() { };"
6695 "function Throw() { throw 5; };"
6696 "var x = { };"
6697 "x.__defineSetter__('set', Throw);"
6698 "x.__defineGetter__('get', Throw);");
6699 Local<v8::Object> x =
6700 Local<v8::Object>::Cast(context->Global()->Get(v8_str("x")));
6701 v8::TryCatch try_catch;
6702 x->Set(v8_str("set"), v8::Integer::New(8));
6703 x->Get(v8_str("get"));
6704 x->Set(v8_str("set"), v8::Integer::New(8));
6705 x->Get(v8_str("get"));
6706 x->Set(v8_str("set"), v8::Integer::New(8));
6707 x->Get(v8_str("get"));
6708 x->Set(v8_str("set"), v8::Integer::New(8));
6709 x->Get(v8_str("get"));
6710}
6711
6712
6713THREADED_TEST(Constructor) {
6714 v8::HandleScope handle_scope;
6715 LocalContext context;
6716 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
6717 templ->SetClassName(v8_str("Fun"));
6718 Local<Function> cons = templ->GetFunction();
6719 context->Global()->Set(v8_str("Fun"), cons);
6720 Local<v8::Object> inst = cons->NewInstance();
6721 i::Handle<i::JSObject> obj = v8::Utils::OpenHandle(*inst);
6722 Local<Value> value = CompileRun("(new Fun()).constructor === Fun");
6723 CHECK(value->BooleanValue());
6724}
6725
6726THREADED_TEST(FunctionDescriptorException) {
6727 v8::HandleScope handle_scope;
6728 LocalContext context;
6729 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
6730 templ->SetClassName(v8_str("Fun"));
6731 Local<Function> cons = templ->GetFunction();
6732 context->Global()->Set(v8_str("Fun"), cons);
6733 Local<Value> value = CompileRun(
6734 "function test() {"
6735 " try {"
6736 " (new Fun()).blah()"
6737 " } catch (e) {"
6738 " var str = String(e);"
6739 " if (str.indexOf('TypeError') == -1) return 1;"
6740 " if (str.indexOf('[object Fun]') != -1) return 2;"
Steve Block1e0659c2011-05-24 12:43:12 +01006741 " if (str.indexOf('#<Fun>') == -1) return 3;"
Steve Blocka7e24c12009-10-30 11:49:00 +00006742 " return 0;"
6743 " }"
6744 " return 4;"
6745 "}"
6746 "test();");
6747 CHECK_EQ(0, value->Int32Value());
6748}
6749
6750
6751THREADED_TEST(EvalAliasedDynamic) {
6752 v8::HandleScope scope;
6753 LocalContext current;
6754
6755 // Tests where aliased eval can only be resolved dynamically.
6756 Local<Script> script =
6757 Script::Compile(v8_str("function f(x) { "
6758 " var foo = 2;"
6759 " with (x) { return eval('foo'); }"
6760 "}"
6761 "foo = 0;"
6762 "result1 = f(new Object());"
6763 "result2 = f(this);"
6764 "var x = new Object();"
6765 "x.eval = function(x) { return 1; };"
6766 "result3 = f(x);"));
6767 script->Run();
6768 CHECK_EQ(2, current->Global()->Get(v8_str("result1"))->Int32Value());
6769 CHECK_EQ(0, current->Global()->Get(v8_str("result2"))->Int32Value());
6770 CHECK_EQ(1, current->Global()->Get(v8_str("result3"))->Int32Value());
6771
6772 v8::TryCatch try_catch;
6773 script =
6774 Script::Compile(v8_str("function f(x) { "
6775 " var bar = 2;"
6776 " with (x) { return eval('bar'); }"
6777 "}"
6778 "f(this)"));
6779 script->Run();
6780 CHECK(try_catch.HasCaught());
6781 try_catch.Reset();
6782}
6783
6784
6785THREADED_TEST(CrossEval) {
6786 v8::HandleScope scope;
6787 LocalContext other;
6788 LocalContext current;
6789
6790 Local<String> token = v8_str("<security token>");
6791 other->SetSecurityToken(token);
6792 current->SetSecurityToken(token);
6793
6794 // Setup reference from current to other.
6795 current->Global()->Set(v8_str("other"), other->Global());
6796
6797 // Check that new variables are introduced in other context.
6798 Local<Script> script =
6799 Script::Compile(v8_str("other.eval('var foo = 1234')"));
6800 script->Run();
6801 Local<Value> foo = other->Global()->Get(v8_str("foo"));
6802 CHECK_EQ(1234, foo->Int32Value());
6803 CHECK(!current->Global()->Has(v8_str("foo")));
6804
6805 // Check that writing to non-existing properties introduces them in
6806 // the other context.
6807 script =
6808 Script::Compile(v8_str("other.eval('na = 1234')"));
6809 script->Run();
6810 CHECK_EQ(1234, other->Global()->Get(v8_str("na"))->Int32Value());
6811 CHECK(!current->Global()->Has(v8_str("na")));
6812
6813 // Check that global variables in current context are not visible in other
6814 // context.
6815 v8::TryCatch try_catch;
6816 script =
6817 Script::Compile(v8_str("var bar = 42; other.eval('bar');"));
6818 Local<Value> result = script->Run();
6819 CHECK(try_catch.HasCaught());
6820 try_catch.Reset();
6821
6822 // Check that local variables in current context are not visible in other
6823 // context.
6824 script =
6825 Script::Compile(v8_str("(function() { "
6826 " var baz = 87;"
6827 " return other.eval('baz');"
6828 "})();"));
6829 result = script->Run();
6830 CHECK(try_catch.HasCaught());
6831 try_catch.Reset();
6832
6833 // Check that global variables in the other environment are visible
6834 // when evaluting code.
6835 other->Global()->Set(v8_str("bis"), v8_num(1234));
6836 script = Script::Compile(v8_str("other.eval('bis')"));
6837 CHECK_EQ(1234, script->Run()->Int32Value());
6838 CHECK(!try_catch.HasCaught());
6839
6840 // Check that the 'this' pointer points to the global object evaluating
6841 // code.
6842 other->Global()->Set(v8_str("t"), other->Global());
6843 script = Script::Compile(v8_str("other.eval('this == t')"));
6844 result = script->Run();
6845 CHECK(result->IsTrue());
6846 CHECK(!try_catch.HasCaught());
6847
6848 // Check that variables introduced in with-statement are not visible in
6849 // other context.
6850 script =
6851 Script::Compile(v8_str("with({x:2}){other.eval('x')}"));
6852 result = script->Run();
6853 CHECK(try_catch.HasCaught());
6854 try_catch.Reset();
6855
6856 // Check that you cannot use 'eval.call' with another object than the
6857 // current global object.
6858 script =
6859 Script::Compile(v8_str("other.y = 1; eval.call(other, 'y')"));
6860 result = script->Run();
6861 CHECK(try_catch.HasCaught());
6862}
6863
6864
6865// Test that calling eval in a context which has been detached from
6866// its global throws an exception. This behavior is consistent with
6867// other JavaScript implementations.
6868THREADED_TEST(EvalInDetachedGlobal) {
6869 v8::HandleScope scope;
6870
6871 v8::Persistent<Context> context0 = Context::New();
6872 v8::Persistent<Context> context1 = Context::New();
6873
6874 // Setup function in context0 that uses eval from context0.
6875 context0->Enter();
6876 v8::Handle<v8::Value> fun =
6877 CompileRun("var x = 42;"
6878 "(function() {"
6879 " var e = eval;"
6880 " return function(s) { return e(s); }"
6881 "})()");
6882 context0->Exit();
6883
6884 // Put the function into context1 and call it before and after
6885 // detaching the global. Before detaching, the call succeeds and
6886 // after detaching and exception is thrown.
6887 context1->Enter();
6888 context1->Global()->Set(v8_str("fun"), fun);
6889 v8::Handle<v8::Value> x_value = CompileRun("fun('x')");
6890 CHECK_EQ(42, x_value->Int32Value());
6891 context0->DetachGlobal();
6892 v8::TryCatch catcher;
6893 x_value = CompileRun("fun('x')");
6894 CHECK(x_value.IsEmpty());
6895 CHECK(catcher.HasCaught());
6896 context1->Exit();
6897
6898 context1.Dispose();
6899 context0.Dispose();
6900}
6901
6902
6903THREADED_TEST(CrossLazyLoad) {
6904 v8::HandleScope scope;
6905 LocalContext other;
6906 LocalContext current;
6907
6908 Local<String> token = v8_str("<security token>");
6909 other->SetSecurityToken(token);
6910 current->SetSecurityToken(token);
6911
6912 // Setup reference from current to other.
6913 current->Global()->Set(v8_str("other"), other->Global());
6914
6915 // Trigger lazy loading in other context.
6916 Local<Script> script =
6917 Script::Compile(v8_str("other.eval('new Date(42)')"));
6918 Local<Value> value = script->Run();
6919 CHECK_EQ(42.0, value->NumberValue());
6920}
6921
6922
6923static v8::Handle<Value> call_as_function(const v8::Arguments& args) {
Andrei Popescu402d9372010-02-26 13:31:12 +00006924 ApiTestFuzzer::Fuzz();
Steve Blocka7e24c12009-10-30 11:49:00 +00006925 if (args.IsConstructCall()) {
6926 if (args[0]->IsInt32()) {
6927 return v8_num(-args[0]->Int32Value());
6928 }
6929 }
6930
6931 return args[0];
6932}
6933
6934
6935// Test that a call handler can be set for objects which will allow
6936// non-function objects created through the API to be called as
6937// functions.
6938THREADED_TEST(CallAsFunction) {
6939 v8::HandleScope scope;
6940 LocalContext context;
6941
6942 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
6943 Local<ObjectTemplate> instance_template = t->InstanceTemplate();
6944 instance_template->SetCallAsFunctionHandler(call_as_function);
6945 Local<v8::Object> instance = t->GetFunction()->NewInstance();
6946 context->Global()->Set(v8_str("obj"), instance);
6947 v8::TryCatch try_catch;
6948 Local<Value> value;
6949 CHECK(!try_catch.HasCaught());
6950
6951 value = CompileRun("obj(42)");
6952 CHECK(!try_catch.HasCaught());
6953 CHECK_EQ(42, value->Int32Value());
6954
6955 value = CompileRun("(function(o){return o(49)})(obj)");
6956 CHECK(!try_catch.HasCaught());
6957 CHECK_EQ(49, value->Int32Value());
6958
6959 // test special case of call as function
6960 value = CompileRun("[obj]['0'](45)");
6961 CHECK(!try_catch.HasCaught());
6962 CHECK_EQ(45, value->Int32Value());
6963
6964 value = CompileRun("obj.call = Function.prototype.call;"
6965 "obj.call(null, 87)");
6966 CHECK(!try_catch.HasCaught());
6967 CHECK_EQ(87, value->Int32Value());
6968
6969 // Regression tests for bug #1116356: Calling call through call/apply
6970 // must work for non-function receivers.
6971 const char* apply_99 = "Function.prototype.call.apply(obj, [this, 99])";
6972 value = CompileRun(apply_99);
6973 CHECK(!try_catch.HasCaught());
6974 CHECK_EQ(99, value->Int32Value());
6975
6976 const char* call_17 = "Function.prototype.call.call(obj, this, 17)";
6977 value = CompileRun(call_17);
6978 CHECK(!try_catch.HasCaught());
6979 CHECK_EQ(17, value->Int32Value());
6980
6981 // Check that the call-as-function handler can be called through
Leon Clarkee46be812010-01-19 14:06:41 +00006982 // new.
Steve Blocka7e24c12009-10-30 11:49:00 +00006983 value = CompileRun("new obj(43)");
6984 CHECK(!try_catch.HasCaught());
6985 CHECK_EQ(-43, value->Int32Value());
6986}
6987
6988
6989static int CountHandles() {
6990 return v8::HandleScope::NumberOfHandles();
6991}
6992
6993
6994static int Recurse(int depth, int iterations) {
6995 v8::HandleScope scope;
6996 if (depth == 0) return CountHandles();
6997 for (int i = 0; i < iterations; i++) {
6998 Local<v8::Number> n = v8::Integer::New(42);
6999 }
7000 return Recurse(depth - 1, iterations);
7001}
7002
7003
7004THREADED_TEST(HandleIteration) {
7005 static const int kIterations = 500;
7006 static const int kNesting = 200;
7007 CHECK_EQ(0, CountHandles());
7008 {
7009 v8::HandleScope scope1;
7010 CHECK_EQ(0, CountHandles());
7011 for (int i = 0; i < kIterations; i++) {
7012 Local<v8::Number> n = v8::Integer::New(42);
7013 CHECK_EQ(i + 1, CountHandles());
7014 }
7015
7016 CHECK_EQ(kIterations, CountHandles());
7017 {
7018 v8::HandleScope scope2;
7019 for (int j = 0; j < kIterations; j++) {
7020 Local<v8::Number> n = v8::Integer::New(42);
7021 CHECK_EQ(j + 1 + kIterations, CountHandles());
7022 }
7023 }
7024 CHECK_EQ(kIterations, CountHandles());
7025 }
7026 CHECK_EQ(0, CountHandles());
7027 CHECK_EQ(kNesting * kIterations, Recurse(kNesting, kIterations));
7028}
7029
7030
7031static v8::Handle<Value> InterceptorHasOwnPropertyGetter(
7032 Local<String> name,
7033 const AccessorInfo& info) {
7034 ApiTestFuzzer::Fuzz();
7035 return v8::Handle<Value>();
7036}
7037
7038
7039THREADED_TEST(InterceptorHasOwnProperty) {
7040 v8::HandleScope scope;
7041 LocalContext context;
7042 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7043 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
7044 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetter);
7045 Local<Function> function = fun_templ->GetFunction();
7046 context->Global()->Set(v8_str("constructor"), function);
7047 v8::Handle<Value> value = CompileRun(
7048 "var o = new constructor();"
7049 "o.hasOwnProperty('ostehaps');");
7050 CHECK_EQ(false, value->BooleanValue());
7051 value = CompileRun(
7052 "o.ostehaps = 42;"
7053 "o.hasOwnProperty('ostehaps');");
7054 CHECK_EQ(true, value->BooleanValue());
7055 value = CompileRun(
7056 "var p = new constructor();"
7057 "p.hasOwnProperty('ostehaps');");
7058 CHECK_EQ(false, value->BooleanValue());
7059}
7060
7061
7062static v8::Handle<Value> InterceptorHasOwnPropertyGetterGC(
7063 Local<String> name,
7064 const AccessorInfo& info) {
7065 ApiTestFuzzer::Fuzz();
Steve Block44f0eee2011-05-26 01:26:41 +01007066 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +00007067 return v8::Handle<Value>();
7068}
7069
7070
7071THREADED_TEST(InterceptorHasOwnPropertyCausingGC) {
7072 v8::HandleScope scope;
7073 LocalContext context;
7074 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7075 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
7076 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetterGC);
7077 Local<Function> function = fun_templ->GetFunction();
7078 context->Global()->Set(v8_str("constructor"), function);
7079 // Let's first make some stuff so we can be sure to get a good GC.
7080 CompileRun(
7081 "function makestr(size) {"
7082 " switch (size) {"
7083 " case 1: return 'f';"
7084 " case 2: return 'fo';"
7085 " case 3: return 'foo';"
7086 " }"
7087 " return makestr(size >> 1) + makestr((size + 1) >> 1);"
7088 "}"
7089 "var x = makestr(12345);"
7090 "x = makestr(31415);"
7091 "x = makestr(23456);");
7092 v8::Handle<Value> value = CompileRun(
7093 "var o = new constructor();"
7094 "o.__proto__ = new String(x);"
7095 "o.hasOwnProperty('ostehaps');");
7096 CHECK_EQ(false, value->BooleanValue());
7097}
7098
7099
7100typedef v8::Handle<Value> (*NamedPropertyGetter)(Local<String> property,
7101 const AccessorInfo& info);
7102
7103
7104static void CheckInterceptorLoadIC(NamedPropertyGetter getter,
7105 const char* source,
7106 int expected) {
7107 v8::HandleScope scope;
7108 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -08007109 templ->SetNamedPropertyHandler(getter, 0, 0, 0, 0, v8_str("data"));
Steve Blocka7e24c12009-10-30 11:49:00 +00007110 LocalContext context;
7111 context->Global()->Set(v8_str("o"), templ->NewInstance());
7112 v8::Handle<Value> value = CompileRun(source);
7113 CHECK_EQ(expected, value->Int32Value());
7114}
7115
7116
7117static v8::Handle<Value> InterceptorLoadICGetter(Local<String> name,
7118 const AccessorInfo& info) {
7119 ApiTestFuzzer::Fuzz();
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -08007120 CHECK_EQ(v8_str("data"), info.Data());
7121 CHECK_EQ(v8_str("x"), name);
Steve Blocka7e24c12009-10-30 11:49:00 +00007122 return v8::Integer::New(42);
7123}
7124
7125
7126// This test should hit the load IC for the interceptor case.
7127THREADED_TEST(InterceptorLoadIC) {
7128 CheckInterceptorLoadIC(InterceptorLoadICGetter,
7129 "var result = 0;"
7130 "for (var i = 0; i < 1000; i++) {"
7131 " result = o.x;"
7132 "}",
7133 42);
7134}
7135
7136
7137// Below go several tests which verify that JITing for various
7138// configurations of interceptor and explicit fields works fine
7139// (those cases are special cased to get better performance).
7140
7141static v8::Handle<Value> InterceptorLoadXICGetter(Local<String> name,
7142 const AccessorInfo& info) {
7143 ApiTestFuzzer::Fuzz();
7144 return v8_str("x")->Equals(name)
7145 ? v8::Integer::New(42) : v8::Handle<v8::Value>();
7146}
7147
7148
7149THREADED_TEST(InterceptorLoadICWithFieldOnHolder) {
7150 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
7151 "var result = 0;"
7152 "o.y = 239;"
7153 "for (var i = 0; i < 1000; i++) {"
7154 " result = o.y;"
7155 "}",
7156 239);
7157}
7158
7159
7160THREADED_TEST(InterceptorLoadICWithSubstitutedProto) {
7161 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
7162 "var result = 0;"
7163 "o.__proto__ = { 'y': 239 };"
7164 "for (var i = 0; i < 1000; i++) {"
7165 " result = o.y + o.x;"
7166 "}",
7167 239 + 42);
7168}
7169
7170
7171THREADED_TEST(InterceptorLoadICWithPropertyOnProto) {
7172 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
7173 "var result = 0;"
7174 "o.__proto__.y = 239;"
7175 "for (var i = 0; i < 1000; i++) {"
7176 " result = o.y + o.x;"
7177 "}",
7178 239 + 42);
7179}
7180
7181
7182THREADED_TEST(InterceptorLoadICUndefined) {
7183 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
7184 "var result = 0;"
7185 "for (var i = 0; i < 1000; i++) {"
7186 " result = (o.y == undefined) ? 239 : 42;"
7187 "}",
7188 239);
7189}
7190
7191
7192THREADED_TEST(InterceptorLoadICWithOverride) {
7193 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
7194 "fst = new Object(); fst.__proto__ = o;"
7195 "snd = new Object(); snd.__proto__ = fst;"
7196 "var result1 = 0;"
7197 "for (var i = 0; i < 1000; i++) {"
7198 " result1 = snd.x;"
7199 "}"
7200 "fst.x = 239;"
7201 "var result = 0;"
7202 "for (var i = 0; i < 1000; i++) {"
7203 " result = snd.x;"
7204 "}"
7205 "result + result1",
7206 239 + 42);
7207}
7208
7209
7210// Test the case when we stored field into
7211// a stub, but interceptor produced value on its own.
7212THREADED_TEST(InterceptorLoadICFieldNotNeeded) {
7213 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
7214 "proto = new Object();"
7215 "o.__proto__ = proto;"
7216 "proto.x = 239;"
7217 "for (var i = 0; i < 1000; i++) {"
7218 " o.x;"
7219 // Now it should be ICed and keep a reference to x defined on proto
7220 "}"
7221 "var result = 0;"
7222 "for (var i = 0; i < 1000; i++) {"
7223 " result += o.x;"
7224 "}"
7225 "result;",
7226 42 * 1000);
7227}
7228
7229
7230// Test the case when we stored field into
7231// a stub, but it got invalidated later on.
7232THREADED_TEST(InterceptorLoadICInvalidatedField) {
7233 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
7234 "proto1 = new Object();"
7235 "proto2 = new Object();"
7236 "o.__proto__ = proto1;"
7237 "proto1.__proto__ = proto2;"
7238 "proto2.y = 239;"
7239 "for (var i = 0; i < 1000; i++) {"
7240 " o.y;"
7241 // Now it should be ICed and keep a reference to y defined on proto2
7242 "}"
7243 "proto1.y = 42;"
7244 "var result = 0;"
7245 "for (var i = 0; i < 1000; i++) {"
7246 " result += o.y;"
7247 "}"
7248 "result;",
7249 42 * 1000);
7250}
7251
7252
Steve Block6ded16b2010-05-10 14:33:55 +01007253static int interceptor_load_not_handled_calls = 0;
7254static v8::Handle<Value> InterceptorLoadNotHandled(Local<String> name,
7255 const AccessorInfo& info) {
7256 ++interceptor_load_not_handled_calls;
7257 return v8::Handle<v8::Value>();
7258}
7259
7260
7261// Test how post-interceptor lookups are done in the non-cacheable
7262// case: the interceptor should not be invoked during this lookup.
7263THREADED_TEST(InterceptorLoadICPostInterceptor) {
7264 interceptor_load_not_handled_calls = 0;
7265 CheckInterceptorLoadIC(InterceptorLoadNotHandled,
7266 "receiver = new Object();"
7267 "receiver.__proto__ = o;"
7268 "proto = new Object();"
7269 "/* Make proto a slow-case object. */"
7270 "for (var i = 0; i < 1000; i++) {"
7271 " proto[\"xxxxxxxx\" + i] = [];"
7272 "}"
7273 "proto.x = 17;"
7274 "o.__proto__ = proto;"
7275 "var result = 0;"
7276 "for (var i = 0; i < 1000; i++) {"
7277 " result += receiver.x;"
7278 "}"
7279 "result;",
7280 17 * 1000);
7281 CHECK_EQ(1000, interceptor_load_not_handled_calls);
7282}
7283
7284
Steve Blocka7e24c12009-10-30 11:49:00 +00007285// Test the case when we stored field into
7286// a stub, but it got invalidated later on due to override on
7287// global object which is between interceptor and fields' holders.
7288THREADED_TEST(InterceptorLoadICInvalidatedFieldViaGlobal) {
7289 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
7290 "o.__proto__ = this;" // set a global to be a proto of o.
7291 "this.__proto__.y = 239;"
7292 "for (var i = 0; i < 10; i++) {"
7293 " if (o.y != 239) throw 'oops: ' + o.y;"
7294 // Now it should be ICed and keep a reference to y defined on field_holder.
7295 "}"
7296 "this.y = 42;" // Assign on a global.
7297 "var result = 0;"
7298 "for (var i = 0; i < 10; i++) {"
7299 " result += o.y;"
7300 "}"
7301 "result;",
7302 42 * 10);
7303}
7304
7305
Steve Blocka7e24c12009-10-30 11:49:00 +00007306static void SetOnThis(Local<String> name,
7307 Local<Value> value,
7308 const AccessorInfo& info) {
7309 info.This()->ForceSet(name, value);
7310}
7311
7312
7313THREADED_TEST(InterceptorLoadICWithCallbackOnHolder) {
7314 v8::HandleScope scope;
7315 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7316 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
7317 templ->SetAccessor(v8_str("y"), Return239);
7318 LocalContext context;
7319 context->Global()->Set(v8_str("o"), templ->NewInstance());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01007320
7321 // Check the case when receiver and interceptor's holder
7322 // are the same objects.
Steve Blocka7e24c12009-10-30 11:49:00 +00007323 v8::Handle<Value> value = CompileRun(
7324 "var result = 0;"
7325 "for (var i = 0; i < 7; i++) {"
7326 " result = o.y;"
7327 "}");
7328 CHECK_EQ(239, value->Int32Value());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01007329
7330 // Check the case when interceptor's holder is in proto chain
7331 // of receiver.
7332 value = CompileRun(
7333 "r = { __proto__: o };"
7334 "var result = 0;"
7335 "for (var i = 0; i < 7; i++) {"
7336 " result = r.y;"
7337 "}");
7338 CHECK_EQ(239, value->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00007339}
7340
7341
7342THREADED_TEST(InterceptorLoadICWithCallbackOnProto) {
7343 v8::HandleScope scope;
7344 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7345 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
7346 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
7347 templ_p->SetAccessor(v8_str("y"), Return239);
7348
7349 LocalContext context;
7350 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
7351 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
7352
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01007353 // Check the case when receiver and interceptor's holder
7354 // are the same objects.
Steve Blocka7e24c12009-10-30 11:49:00 +00007355 v8::Handle<Value> value = CompileRun(
7356 "o.__proto__ = p;"
7357 "var result = 0;"
7358 "for (var i = 0; i < 7; i++) {"
7359 " result = o.x + o.y;"
7360 "}");
7361 CHECK_EQ(239 + 42, value->Int32Value());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01007362
7363 // Check the case when interceptor's holder is in proto chain
7364 // of receiver.
7365 value = CompileRun(
7366 "r = { __proto__: o };"
7367 "var result = 0;"
7368 "for (var i = 0; i < 7; i++) {"
7369 " result = r.x + r.y;"
7370 "}");
7371 CHECK_EQ(239 + 42, value->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00007372}
7373
7374
7375THREADED_TEST(InterceptorLoadICForCallbackWithOverride) {
7376 v8::HandleScope scope;
7377 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7378 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
7379 templ->SetAccessor(v8_str("y"), Return239);
7380
7381 LocalContext context;
7382 context->Global()->Set(v8_str("o"), templ->NewInstance());
7383
7384 v8::Handle<Value> value = CompileRun(
7385 "fst = new Object(); fst.__proto__ = o;"
7386 "snd = new Object(); snd.__proto__ = fst;"
7387 "var result1 = 0;"
7388 "for (var i = 0; i < 7; i++) {"
7389 " result1 = snd.x;"
7390 "}"
7391 "fst.x = 239;"
7392 "var result = 0;"
7393 "for (var i = 0; i < 7; i++) {"
7394 " result = snd.x;"
7395 "}"
7396 "result + result1");
7397 CHECK_EQ(239 + 42, value->Int32Value());
7398}
7399
7400
7401// Test the case when we stored callback into
7402// a stub, but interceptor produced value on its own.
7403THREADED_TEST(InterceptorLoadICCallbackNotNeeded) {
7404 v8::HandleScope scope;
7405 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7406 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
7407 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
7408 templ_p->SetAccessor(v8_str("y"), Return239);
7409
7410 LocalContext context;
7411 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
7412 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
7413
7414 v8::Handle<Value> value = CompileRun(
7415 "o.__proto__ = p;"
7416 "for (var i = 0; i < 7; i++) {"
7417 " o.x;"
7418 // Now it should be ICed and keep a reference to x defined on p
7419 "}"
7420 "var result = 0;"
7421 "for (var i = 0; i < 7; i++) {"
7422 " result += o.x;"
7423 "}"
7424 "result");
7425 CHECK_EQ(42 * 7, value->Int32Value());
7426}
7427
7428
7429// Test the case when we stored callback into
7430// a stub, but it got invalidated later on.
7431THREADED_TEST(InterceptorLoadICInvalidatedCallback) {
7432 v8::HandleScope scope;
7433 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7434 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
7435 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
7436 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis);
7437
7438 LocalContext context;
7439 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
7440 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
7441
7442 v8::Handle<Value> value = CompileRun(
7443 "inbetween = new Object();"
7444 "o.__proto__ = inbetween;"
7445 "inbetween.__proto__ = p;"
7446 "for (var i = 0; i < 10; i++) {"
7447 " o.y;"
7448 // Now it should be ICed and keep a reference to y defined on p
7449 "}"
7450 "inbetween.y = 42;"
7451 "var result = 0;"
7452 "for (var i = 0; i < 10; i++) {"
7453 " result += o.y;"
7454 "}"
7455 "result");
7456 CHECK_EQ(42 * 10, value->Int32Value());
7457}
7458
7459
7460// Test the case when we stored callback into
7461// a stub, but it got invalidated later on due to override on
7462// global object which is between interceptor and callbacks' holders.
7463THREADED_TEST(InterceptorLoadICInvalidatedCallbackViaGlobal) {
7464 v8::HandleScope scope;
7465 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7466 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
7467 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
7468 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis);
7469
7470 LocalContext context;
7471 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
7472 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
7473
7474 v8::Handle<Value> value = CompileRun(
7475 "o.__proto__ = this;"
7476 "this.__proto__ = p;"
7477 "for (var i = 0; i < 10; i++) {"
7478 " if (o.y != 239) throw 'oops: ' + o.y;"
7479 // Now it should be ICed and keep a reference to y defined on p
7480 "}"
7481 "this.y = 42;"
7482 "var result = 0;"
7483 "for (var i = 0; i < 10; i++) {"
7484 " result += o.y;"
7485 "}"
7486 "result");
7487 CHECK_EQ(42 * 10, value->Int32Value());
7488}
7489
7490
7491static v8::Handle<Value> InterceptorLoadICGetter0(Local<String> name,
7492 const AccessorInfo& info) {
7493 ApiTestFuzzer::Fuzz();
7494 CHECK(v8_str("x")->Equals(name));
7495 return v8::Integer::New(0);
7496}
7497
7498
7499THREADED_TEST(InterceptorReturningZero) {
7500 CheckInterceptorLoadIC(InterceptorLoadICGetter0,
7501 "o.x == undefined ? 1 : 0",
7502 0);
7503}
7504
7505
7506static v8::Handle<Value> InterceptorStoreICSetter(
7507 Local<String> key, Local<Value> value, const AccessorInfo&) {
7508 CHECK(v8_str("x")->Equals(key));
7509 CHECK_EQ(42, value->Int32Value());
7510 return value;
7511}
7512
7513
7514// This test should hit the store IC for the interceptor case.
7515THREADED_TEST(InterceptorStoreIC) {
7516 v8::HandleScope scope;
7517 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7518 templ->SetNamedPropertyHandler(InterceptorLoadICGetter,
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -08007519 InterceptorStoreICSetter,
7520 0, 0, 0, v8_str("data"));
Steve Blocka7e24c12009-10-30 11:49:00 +00007521 LocalContext context;
7522 context->Global()->Set(v8_str("o"), templ->NewInstance());
7523 v8::Handle<Value> value = CompileRun(
7524 "for (var i = 0; i < 1000; i++) {"
7525 " o.x = 42;"
7526 "}");
7527}
7528
7529
7530THREADED_TEST(InterceptorStoreICWithNoSetter) {
7531 v8::HandleScope scope;
7532 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7533 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
7534 LocalContext context;
7535 context->Global()->Set(v8_str("o"), templ->NewInstance());
7536 v8::Handle<Value> value = CompileRun(
7537 "for (var i = 0; i < 1000; i++) {"
7538 " o.y = 239;"
7539 "}"
7540 "42 + o.y");
7541 CHECK_EQ(239 + 42, value->Int32Value());
7542}
7543
7544
7545
7546
7547v8::Handle<Value> call_ic_function;
7548v8::Handle<Value> call_ic_function2;
7549v8::Handle<Value> call_ic_function3;
7550
7551static v8::Handle<Value> InterceptorCallICGetter(Local<String> name,
7552 const AccessorInfo& info) {
7553 ApiTestFuzzer::Fuzz();
7554 CHECK(v8_str("x")->Equals(name));
7555 return call_ic_function;
7556}
7557
7558
7559// This test should hit the call IC for the interceptor case.
7560THREADED_TEST(InterceptorCallIC) {
7561 v8::HandleScope scope;
7562 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7563 templ->SetNamedPropertyHandler(InterceptorCallICGetter);
7564 LocalContext context;
7565 context->Global()->Set(v8_str("o"), templ->NewInstance());
7566 call_ic_function =
7567 v8_compile("function f(x) { return x + 1; }; f")->Run();
7568 v8::Handle<Value> value = CompileRun(
7569 "var result = 0;"
7570 "for (var i = 0; i < 1000; i++) {"
7571 " result = o.x(41);"
7572 "}");
7573 CHECK_EQ(42, value->Int32Value());
7574}
7575
7576
7577// This test checks that if interceptor doesn't provide
7578// a value, we can fetch regular value.
7579THREADED_TEST(InterceptorCallICSeesOthers) {
7580 v8::HandleScope scope;
7581 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7582 templ->SetNamedPropertyHandler(NoBlockGetterX);
7583 LocalContext context;
7584 context->Global()->Set(v8_str("o"), templ->NewInstance());
7585 v8::Handle<Value> value = CompileRun(
7586 "o.x = function f(x) { return x + 1; };"
7587 "var result = 0;"
7588 "for (var i = 0; i < 7; i++) {"
7589 " result = o.x(41);"
7590 "}");
7591 CHECK_EQ(42, value->Int32Value());
7592}
7593
7594
7595static v8::Handle<Value> call_ic_function4;
7596static v8::Handle<Value> InterceptorCallICGetter4(Local<String> name,
7597 const AccessorInfo& info) {
7598 ApiTestFuzzer::Fuzz();
7599 CHECK(v8_str("x")->Equals(name));
7600 return call_ic_function4;
7601}
7602
7603
7604// This test checks that if interceptor provides a function,
7605// even if we cached shadowed variant, interceptor's function
7606// is invoked
7607THREADED_TEST(InterceptorCallICCacheableNotNeeded) {
7608 v8::HandleScope scope;
7609 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7610 templ->SetNamedPropertyHandler(InterceptorCallICGetter4);
7611 LocalContext context;
7612 context->Global()->Set(v8_str("o"), templ->NewInstance());
7613 call_ic_function4 =
7614 v8_compile("function f(x) { return x - 1; }; f")->Run();
7615 v8::Handle<Value> value = CompileRun(
7616 "o.__proto__.x = function(x) { return x + 1; };"
7617 "var result = 0;"
7618 "for (var i = 0; i < 1000; i++) {"
7619 " result = o.x(42);"
7620 "}");
7621 CHECK_EQ(41, value->Int32Value());
7622}
7623
7624
7625// Test the case when we stored cacheable lookup into
7626// a stub, but it got invalidated later on
7627THREADED_TEST(InterceptorCallICInvalidatedCacheable) {
7628 v8::HandleScope scope;
7629 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7630 templ->SetNamedPropertyHandler(NoBlockGetterX);
7631 LocalContext context;
7632 context->Global()->Set(v8_str("o"), templ->NewInstance());
7633 v8::Handle<Value> value = CompileRun(
7634 "proto1 = new Object();"
7635 "proto2 = new Object();"
7636 "o.__proto__ = proto1;"
7637 "proto1.__proto__ = proto2;"
7638 "proto2.y = function(x) { return x + 1; };"
7639 // Invoke it many times to compile a stub
7640 "for (var i = 0; i < 7; i++) {"
7641 " o.y(42);"
7642 "}"
7643 "proto1.y = function(x) { return x - 1; };"
7644 "var result = 0;"
7645 "for (var i = 0; i < 7; i++) {"
7646 " result += o.y(42);"
7647 "}");
7648 CHECK_EQ(41 * 7, value->Int32Value());
7649}
7650
7651
7652static v8::Handle<Value> call_ic_function5;
7653static v8::Handle<Value> InterceptorCallICGetter5(Local<String> name,
7654 const AccessorInfo& info) {
7655 ApiTestFuzzer::Fuzz();
7656 if (v8_str("x")->Equals(name))
7657 return call_ic_function5;
7658 else
7659 return Local<Value>();
7660}
7661
7662
7663// This test checks that if interceptor doesn't provide a function,
7664// cached constant function is used
7665THREADED_TEST(InterceptorCallICConstantFunctionUsed) {
7666 v8::HandleScope scope;
7667 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7668 templ->SetNamedPropertyHandler(NoBlockGetterX);
7669 LocalContext context;
7670 context->Global()->Set(v8_str("o"), templ->NewInstance());
7671 v8::Handle<Value> value = CompileRun(
7672 "function inc(x) { return x + 1; };"
7673 "inc(1);"
7674 "o.x = inc;"
7675 "var result = 0;"
7676 "for (var i = 0; i < 1000; i++) {"
7677 " result = o.x(42);"
7678 "}");
7679 CHECK_EQ(43, value->Int32Value());
7680}
7681
7682
7683// This test checks that if interceptor provides a function,
7684// even if we cached constant function, interceptor's function
7685// is invoked
7686THREADED_TEST(InterceptorCallICConstantFunctionNotNeeded) {
7687 v8::HandleScope scope;
7688 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7689 templ->SetNamedPropertyHandler(InterceptorCallICGetter5);
7690 LocalContext context;
7691 context->Global()->Set(v8_str("o"), templ->NewInstance());
7692 call_ic_function5 =
7693 v8_compile("function f(x) { return x - 1; }; f")->Run();
7694 v8::Handle<Value> value = CompileRun(
7695 "function inc(x) { return x + 1; };"
7696 "inc(1);"
7697 "o.x = inc;"
7698 "var result = 0;"
7699 "for (var i = 0; i < 1000; i++) {"
7700 " result = o.x(42);"
7701 "}");
7702 CHECK_EQ(41, value->Int32Value());
7703}
7704
7705
7706// Test the case when we stored constant function into
7707// a stub, but it got invalidated later on
7708THREADED_TEST(InterceptorCallICInvalidatedConstantFunction) {
7709 v8::HandleScope scope;
7710 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7711 templ->SetNamedPropertyHandler(NoBlockGetterX);
7712 LocalContext context;
7713 context->Global()->Set(v8_str("o"), templ->NewInstance());
7714 v8::Handle<Value> value = CompileRun(
7715 "function inc(x) { return x + 1; };"
7716 "inc(1);"
7717 "proto1 = new Object();"
7718 "proto2 = new Object();"
7719 "o.__proto__ = proto1;"
7720 "proto1.__proto__ = proto2;"
7721 "proto2.y = inc;"
7722 // Invoke it many times to compile a stub
7723 "for (var i = 0; i < 7; i++) {"
7724 " o.y(42);"
7725 "}"
7726 "proto1.y = function(x) { return x - 1; };"
7727 "var result = 0;"
7728 "for (var i = 0; i < 7; i++) {"
7729 " result += o.y(42);"
7730 "}");
7731 CHECK_EQ(41 * 7, value->Int32Value());
7732}
7733
7734
7735// Test the case when we stored constant function into
7736// a stub, but it got invalidated later on due to override on
7737// global object which is between interceptor and constant function' holders.
7738THREADED_TEST(InterceptorCallICInvalidatedConstantFunctionViaGlobal) {
7739 v8::HandleScope scope;
7740 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7741 templ->SetNamedPropertyHandler(NoBlockGetterX);
7742 LocalContext context;
7743 context->Global()->Set(v8_str("o"), templ->NewInstance());
7744 v8::Handle<Value> value = CompileRun(
7745 "function inc(x) { return x + 1; };"
7746 "inc(1);"
7747 "o.__proto__ = this;"
7748 "this.__proto__.y = inc;"
7749 // Invoke it many times to compile a stub
7750 "for (var i = 0; i < 7; i++) {"
7751 " if (o.y(42) != 43) throw 'oops: ' + o.y(42);"
7752 "}"
7753 "this.y = function(x) { return x - 1; };"
7754 "var result = 0;"
7755 "for (var i = 0; i < 7; i++) {"
7756 " result += o.y(42);"
7757 "}");
7758 CHECK_EQ(41 * 7, value->Int32Value());
7759}
7760
7761
Leon Clarke4515c472010-02-03 11:58:03 +00007762// Test the case when actual function to call sits on global object.
7763THREADED_TEST(InterceptorCallICCachedFromGlobal) {
7764 v8::HandleScope scope;
7765 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7766 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
7767
7768 LocalContext context;
7769 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
7770
7771 v8::Handle<Value> value = CompileRun(
7772 "try {"
7773 " o.__proto__ = this;"
7774 " for (var i = 0; i < 10; i++) {"
7775 " var v = o.parseFloat('239');"
7776 " if (v != 239) throw v;"
7777 // Now it should be ICed and keep a reference to parseFloat.
7778 " }"
7779 " var result = 0;"
7780 " for (var i = 0; i < 10; i++) {"
7781 " result += o.parseFloat('239');"
7782 " }"
7783 " result"
7784 "} catch(e) {"
7785 " e"
7786 "};");
7787 CHECK_EQ(239 * 10, value->Int32Value());
7788}
7789
Andrei Popescu402d9372010-02-26 13:31:12 +00007790static v8::Handle<Value> InterceptorCallICFastApi(Local<String> name,
7791 const AccessorInfo& info) {
7792 ApiTestFuzzer::Fuzz();
7793 int* call_count = reinterpret_cast<int*>(v8::External::Unwrap(info.Data()));
7794 ++(*call_count);
7795 if ((*call_count) % 20 == 0) {
Steve Block44f0eee2011-05-26 01:26:41 +01007796 HEAP->CollectAllGarbage(true);
Andrei Popescu402d9372010-02-26 13:31:12 +00007797 }
7798 return v8::Handle<Value>();
7799}
7800
7801static v8::Handle<Value> FastApiCallback_TrivialSignature(
7802 const v8::Arguments& args) {
7803 ApiTestFuzzer::Fuzz();
7804 CHECK_EQ(args.This(), args.Holder());
7805 CHECK(args.Data()->Equals(v8_str("method_data")));
7806 return v8::Integer::New(args[0]->Int32Value() + 1);
7807}
7808
7809static v8::Handle<Value> FastApiCallback_SimpleSignature(
7810 const v8::Arguments& args) {
7811 ApiTestFuzzer::Fuzz();
7812 CHECK_EQ(args.This()->GetPrototype(), args.Holder());
7813 CHECK(args.Data()->Equals(v8_str("method_data")));
7814 // Note, we're using HasRealNamedProperty instead of Has to avoid
7815 // invoking the interceptor again.
7816 CHECK(args.Holder()->HasRealNamedProperty(v8_str("foo")));
7817 return v8::Integer::New(args[0]->Int32Value() + 1);
7818}
7819
7820// Helper to maximize the odds of object moving.
7821static void GenerateSomeGarbage() {
7822 CompileRun(
7823 "var garbage;"
7824 "for (var i = 0; i < 1000; i++) {"
7825 " garbage = [1/i, \"garbage\" + i, garbage, {foo: garbage}];"
7826 "}"
7827 "garbage = undefined;");
7828}
7829
Ben Murdoche0cee9b2011-05-25 10:26:03 +01007830
Steve Block1e0659c2011-05-24 12:43:12 +01007831v8::Handle<v8::Value> DirectApiCallback(const v8::Arguments& args) {
7832 static int count = 0;
7833 if (count++ % 3 == 0) {
Steve Block44f0eee2011-05-26 01:26:41 +01007834 HEAP-> CollectAllGarbage(true); // This should move the stub
Steve Block1e0659c2011-05-24 12:43:12 +01007835 GenerateSomeGarbage(); // This should ensure the old stub memory is flushed
7836 }
7837 return v8::Handle<v8::Value>();
7838}
7839
7840
7841THREADED_TEST(CallICFastApi_DirectCall_GCMoveStub) {
7842 v8::HandleScope scope;
7843 LocalContext context;
7844 v8::Handle<v8::ObjectTemplate> nativeobject_templ = v8::ObjectTemplate::New();
7845 nativeobject_templ->Set("callback",
7846 v8::FunctionTemplate::New(DirectApiCallback));
7847 v8::Local<v8::Object> nativeobject_obj = nativeobject_templ->NewInstance();
7848 context->Global()->Set(v8_str("nativeobject"), nativeobject_obj);
7849 // call the api function multiple times to ensure direct call stub creation.
7850 CompileRun(
7851 "function f() {"
7852 " for (var i = 1; i <= 30; i++) {"
7853 " nativeobject.callback();"
7854 " }"
7855 "}"
7856 "f();");
7857}
7858
7859
7860v8::Handle<v8::Value> ThrowingDirectApiCallback(const v8::Arguments& args) {
7861 return v8::ThrowException(v8_str("g"));
7862}
7863
7864
7865THREADED_TEST(CallICFastApi_DirectCall_Throw) {
7866 v8::HandleScope scope;
7867 LocalContext context;
7868 v8::Handle<v8::ObjectTemplate> nativeobject_templ = v8::ObjectTemplate::New();
7869 nativeobject_templ->Set("callback",
7870 v8::FunctionTemplate::New(ThrowingDirectApiCallback));
7871 v8::Local<v8::Object> nativeobject_obj = nativeobject_templ->NewInstance();
7872 context->Global()->Set(v8_str("nativeobject"), nativeobject_obj);
7873 // call the api function multiple times to ensure direct call stub creation.
7874 v8::Handle<Value> result = CompileRun(
7875 "var result = '';"
7876 "function f() {"
7877 " for (var i = 1; i <= 5; i++) {"
7878 " try { nativeobject.callback(); } catch (e) { result += e; }"
7879 " }"
7880 "}"
7881 "f(); result;");
7882 CHECK_EQ(v8_str("ggggg"), result);
7883}
7884
7885
Ben Murdoche0cee9b2011-05-25 10:26:03 +01007886v8::Handle<v8::Value> DirectGetterCallback(Local<String> name,
7887 const v8::AccessorInfo& info) {
7888 if (++p_getter_count % 3 == 0) {
Steve Block44f0eee2011-05-26 01:26:41 +01007889 HEAP->CollectAllGarbage(true);
Ben Murdoche0cee9b2011-05-25 10:26:03 +01007890 GenerateSomeGarbage();
7891 }
7892 return v8::Handle<v8::Value>();
7893}
7894
7895
7896THREADED_TEST(LoadICFastApi_DirectCall_GCMoveStub) {
7897 v8::HandleScope scope;
7898 LocalContext context;
7899 v8::Handle<v8::ObjectTemplate> obj = v8::ObjectTemplate::New();
7900 obj->SetAccessor(v8_str("p1"), DirectGetterCallback);
7901 context->Global()->Set(v8_str("o1"), obj->NewInstance());
7902 p_getter_count = 0;
7903 CompileRun(
7904 "function f() {"
7905 " for (var i = 0; i < 30; i++) o1.p1;"
7906 "}"
7907 "f();");
7908 CHECK_EQ(30, p_getter_count);
7909}
7910
7911
7912v8::Handle<v8::Value> ThrowingDirectGetterCallback(
7913 Local<String> name, const v8::AccessorInfo& info) {
7914 return v8::ThrowException(v8_str("g"));
7915}
7916
7917
7918THREADED_TEST(LoadICFastApi_DirectCall_Throw) {
7919 v8::HandleScope scope;
7920 LocalContext context;
7921 v8::Handle<v8::ObjectTemplate> obj = v8::ObjectTemplate::New();
7922 obj->SetAccessor(v8_str("p1"), ThrowingDirectGetterCallback);
7923 context->Global()->Set(v8_str("o1"), obj->NewInstance());
7924 v8::Handle<Value> result = CompileRun(
7925 "var result = '';"
7926 "for (var i = 0; i < 5; i++) {"
7927 " try { o1.p1; } catch (e) { result += e; }"
7928 "}"
7929 "result;");
7930 CHECK_EQ(v8_str("ggggg"), result);
7931}
7932
7933
Andrei Popescu402d9372010-02-26 13:31:12 +00007934THREADED_TEST(InterceptorCallICFastApi_TrivialSignature) {
7935 int interceptor_call_count = 0;
7936 v8::HandleScope scope;
7937 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7938 v8::Handle<v8::FunctionTemplate> method_templ =
7939 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature,
7940 v8_str("method_data"),
7941 v8::Handle<v8::Signature>());
7942 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7943 proto_templ->Set(v8_str("method"), method_templ);
7944 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7945 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
7946 NULL, NULL, NULL, NULL,
7947 v8::External::Wrap(&interceptor_call_count));
7948 LocalContext context;
7949 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7950 GenerateSomeGarbage();
7951 context->Global()->Set(v8_str("o"), fun->NewInstance());
7952 v8::Handle<Value> value = CompileRun(
7953 "var result = 0;"
7954 "for (var i = 0; i < 100; i++) {"
7955 " result = o.method(41);"
7956 "}");
7957 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
7958 CHECK_EQ(100, interceptor_call_count);
7959}
7960
7961THREADED_TEST(InterceptorCallICFastApi_SimpleSignature) {
7962 int interceptor_call_count = 0;
7963 v8::HandleScope scope;
7964 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7965 v8::Handle<v8::FunctionTemplate> method_templ =
7966 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7967 v8_str("method_data"),
7968 v8::Signature::New(fun_templ));
7969 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7970 proto_templ->Set(v8_str("method"), method_templ);
7971 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7972 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
7973 NULL, NULL, NULL, NULL,
7974 v8::External::Wrap(&interceptor_call_count));
7975 LocalContext context;
7976 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7977 GenerateSomeGarbage();
7978 context->Global()->Set(v8_str("o"), fun->NewInstance());
7979 v8::Handle<Value> value = CompileRun(
7980 "o.foo = 17;"
7981 "var receiver = {};"
7982 "receiver.__proto__ = o;"
7983 "var result = 0;"
7984 "for (var i = 0; i < 100; i++) {"
7985 " result = receiver.method(41);"
7986 "}");
7987 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
7988 CHECK_EQ(100, interceptor_call_count);
7989}
7990
7991THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss1) {
7992 int interceptor_call_count = 0;
7993 v8::HandleScope scope;
7994 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7995 v8::Handle<v8::FunctionTemplate> method_templ =
7996 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7997 v8_str("method_data"),
7998 v8::Signature::New(fun_templ));
7999 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
8000 proto_templ->Set(v8_str("method"), method_templ);
8001 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
8002 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
8003 NULL, NULL, NULL, NULL,
8004 v8::External::Wrap(&interceptor_call_count));
8005 LocalContext context;
8006 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
8007 GenerateSomeGarbage();
8008 context->Global()->Set(v8_str("o"), fun->NewInstance());
8009 v8::Handle<Value> value = CompileRun(
8010 "o.foo = 17;"
8011 "var receiver = {};"
8012 "receiver.__proto__ = o;"
8013 "var result = 0;"
8014 "var saved_result = 0;"
8015 "for (var i = 0; i < 100; i++) {"
8016 " result = receiver.method(41);"
8017 " if (i == 50) {"
8018 " saved_result = result;"
8019 " receiver = {method: function(x) { return x - 1 }};"
8020 " }"
8021 "}");
8022 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
8023 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
8024 CHECK_GE(interceptor_call_count, 50);
8025}
8026
8027THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss2) {
8028 int interceptor_call_count = 0;
8029 v8::HandleScope scope;
8030 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
8031 v8::Handle<v8::FunctionTemplate> method_templ =
8032 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
8033 v8_str("method_data"),
8034 v8::Signature::New(fun_templ));
8035 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
8036 proto_templ->Set(v8_str("method"), method_templ);
8037 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
8038 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
8039 NULL, NULL, NULL, NULL,
8040 v8::External::Wrap(&interceptor_call_count));
8041 LocalContext context;
8042 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
8043 GenerateSomeGarbage();
8044 context->Global()->Set(v8_str("o"), fun->NewInstance());
8045 v8::Handle<Value> value = CompileRun(
8046 "o.foo = 17;"
8047 "var receiver = {};"
8048 "receiver.__proto__ = o;"
8049 "var result = 0;"
8050 "var saved_result = 0;"
8051 "for (var i = 0; i < 100; i++) {"
8052 " result = receiver.method(41);"
8053 " if (i == 50) {"
8054 " saved_result = result;"
8055 " o.method = function(x) { return x - 1 };"
8056 " }"
8057 "}");
8058 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
8059 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
8060 CHECK_GE(interceptor_call_count, 50);
8061}
8062
Steve Block6ded16b2010-05-10 14:33:55 +01008063THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss3) {
8064 int interceptor_call_count = 0;
8065 v8::HandleScope scope;
8066 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
8067 v8::Handle<v8::FunctionTemplate> method_templ =
8068 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
8069 v8_str("method_data"),
8070 v8::Signature::New(fun_templ));
8071 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
8072 proto_templ->Set(v8_str("method"), method_templ);
8073 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
8074 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
8075 NULL, NULL, NULL, NULL,
8076 v8::External::Wrap(&interceptor_call_count));
8077 LocalContext context;
8078 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
8079 GenerateSomeGarbage();
8080 context->Global()->Set(v8_str("o"), fun->NewInstance());
8081 v8::TryCatch try_catch;
8082 v8::Handle<Value> value = CompileRun(
8083 "o.foo = 17;"
8084 "var receiver = {};"
8085 "receiver.__proto__ = o;"
8086 "var result = 0;"
8087 "var saved_result = 0;"
8088 "for (var i = 0; i < 100; i++) {"
8089 " result = receiver.method(41);"
8090 " if (i == 50) {"
8091 " saved_result = result;"
8092 " receiver = 333;"
8093 " }"
8094 "}");
8095 CHECK(try_catch.HasCaught());
8096 CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"),
8097 try_catch.Exception()->ToString());
8098 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
8099 CHECK_GE(interceptor_call_count, 50);
8100}
8101
Andrei Popescu402d9372010-02-26 13:31:12 +00008102THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_TypeError) {
8103 int interceptor_call_count = 0;
8104 v8::HandleScope scope;
8105 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
8106 v8::Handle<v8::FunctionTemplate> method_templ =
8107 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
8108 v8_str("method_data"),
8109 v8::Signature::New(fun_templ));
8110 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
8111 proto_templ->Set(v8_str("method"), method_templ);
8112 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
8113 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
8114 NULL, NULL, NULL, NULL,
8115 v8::External::Wrap(&interceptor_call_count));
8116 LocalContext context;
8117 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
8118 GenerateSomeGarbage();
8119 context->Global()->Set(v8_str("o"), fun->NewInstance());
8120 v8::TryCatch try_catch;
8121 v8::Handle<Value> value = CompileRun(
8122 "o.foo = 17;"
8123 "var receiver = {};"
8124 "receiver.__proto__ = o;"
8125 "var result = 0;"
8126 "var saved_result = 0;"
8127 "for (var i = 0; i < 100; i++) {"
8128 " result = receiver.method(41);"
8129 " if (i == 50) {"
8130 " saved_result = result;"
8131 " receiver = {method: receiver.method};"
8132 " }"
8133 "}");
8134 CHECK(try_catch.HasCaught());
8135 CHECK_EQ(v8_str("TypeError: Illegal invocation"),
8136 try_catch.Exception()->ToString());
8137 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
8138 CHECK_GE(interceptor_call_count, 50);
8139}
8140
8141THREADED_TEST(CallICFastApi_TrivialSignature) {
8142 v8::HandleScope scope;
8143 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
8144 v8::Handle<v8::FunctionTemplate> method_templ =
8145 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature,
8146 v8_str("method_data"),
8147 v8::Handle<v8::Signature>());
8148 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
8149 proto_templ->Set(v8_str("method"), method_templ);
8150 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
8151 LocalContext context;
8152 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
8153 GenerateSomeGarbage();
8154 context->Global()->Set(v8_str("o"), fun->NewInstance());
8155 v8::Handle<Value> value = CompileRun(
8156 "var result = 0;"
8157 "for (var i = 0; i < 100; i++) {"
8158 " result = o.method(41);"
8159 "}");
8160
8161 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
8162}
8163
8164THREADED_TEST(CallICFastApi_SimpleSignature) {
8165 v8::HandleScope scope;
8166 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
8167 v8::Handle<v8::FunctionTemplate> method_templ =
8168 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
8169 v8_str("method_data"),
8170 v8::Signature::New(fun_templ));
8171 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
8172 proto_templ->Set(v8_str("method"), method_templ);
8173 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
8174 LocalContext context;
8175 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
8176 GenerateSomeGarbage();
8177 context->Global()->Set(v8_str("o"), fun->NewInstance());
8178 v8::Handle<Value> value = CompileRun(
8179 "o.foo = 17;"
8180 "var receiver = {};"
8181 "receiver.__proto__ = o;"
8182 "var result = 0;"
8183 "for (var i = 0; i < 100; i++) {"
8184 " result = receiver.method(41);"
8185 "}");
8186
8187 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
8188}
8189
Steve Block6ded16b2010-05-10 14:33:55 +01008190THREADED_TEST(CallICFastApi_SimpleSignature_Miss1) {
Andrei Popescu402d9372010-02-26 13:31:12 +00008191 v8::HandleScope scope;
8192 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
8193 v8::Handle<v8::FunctionTemplate> method_templ =
8194 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
8195 v8_str("method_data"),
8196 v8::Signature::New(fun_templ));
8197 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
8198 proto_templ->Set(v8_str("method"), method_templ);
8199 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
8200 LocalContext context;
8201 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
8202 GenerateSomeGarbage();
8203 context->Global()->Set(v8_str("o"), fun->NewInstance());
8204 v8::Handle<Value> value = CompileRun(
8205 "o.foo = 17;"
8206 "var receiver = {};"
8207 "receiver.__proto__ = o;"
8208 "var result = 0;"
8209 "var saved_result = 0;"
8210 "for (var i = 0; i < 100; i++) {"
8211 " result = receiver.method(41);"
8212 " if (i == 50) {"
8213 " saved_result = result;"
8214 " receiver = {method: function(x) { return x - 1 }};"
8215 " }"
8216 "}");
8217 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
8218 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
8219}
8220
Steve Block6ded16b2010-05-10 14:33:55 +01008221THREADED_TEST(CallICFastApi_SimpleSignature_Miss2) {
8222 v8::HandleScope scope;
8223 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
8224 v8::Handle<v8::FunctionTemplate> method_templ =
8225 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
8226 v8_str("method_data"),
8227 v8::Signature::New(fun_templ));
8228 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
8229 proto_templ->Set(v8_str("method"), method_templ);
8230 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
8231 LocalContext context;
8232 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
8233 GenerateSomeGarbage();
8234 context->Global()->Set(v8_str("o"), fun->NewInstance());
8235 v8::TryCatch try_catch;
8236 v8::Handle<Value> value = CompileRun(
8237 "o.foo = 17;"
8238 "var receiver = {};"
8239 "receiver.__proto__ = o;"
8240 "var result = 0;"
8241 "var saved_result = 0;"
8242 "for (var i = 0; i < 100; i++) {"
8243 " result = receiver.method(41);"
8244 " if (i == 50) {"
8245 " saved_result = result;"
8246 " receiver = 333;"
8247 " }"
8248 "}");
8249 CHECK(try_catch.HasCaught());
8250 CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"),
8251 try_catch.Exception()->ToString());
8252 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
8253}
8254
Leon Clarke4515c472010-02-03 11:58:03 +00008255
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008256v8::Handle<Value> keyed_call_ic_function;
8257
8258static v8::Handle<Value> InterceptorKeyedCallICGetter(
8259 Local<String> name, const AccessorInfo& info) {
8260 ApiTestFuzzer::Fuzz();
8261 if (v8_str("x")->Equals(name)) {
8262 return keyed_call_ic_function;
8263 }
8264 return v8::Handle<Value>();
8265}
8266
8267
8268// Test the case when we stored cacheable lookup into
8269// a stub, but the function name changed (to another cacheable function).
8270THREADED_TEST(InterceptorKeyedCallICKeyChange1) {
8271 v8::HandleScope scope;
8272 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
8273 templ->SetNamedPropertyHandler(NoBlockGetterX);
8274 LocalContext context;
8275 context->Global()->Set(v8_str("o"), templ->NewInstance());
8276 v8::Handle<Value> value = CompileRun(
8277 "proto = new Object();"
8278 "proto.y = function(x) { return x + 1; };"
8279 "proto.z = function(x) { return x - 1; };"
8280 "o.__proto__ = proto;"
8281 "var result = 0;"
8282 "var method = 'y';"
8283 "for (var i = 0; i < 10; i++) {"
8284 " if (i == 5) { method = 'z'; };"
8285 " result += o[method](41);"
8286 "}");
8287 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
8288}
8289
8290
8291// Test the case when we stored cacheable lookup into
8292// a stub, but the function name changed (and the new function is present
8293// both before and after the interceptor in the prototype chain).
8294THREADED_TEST(InterceptorKeyedCallICKeyChange2) {
8295 v8::HandleScope scope;
8296 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
8297 templ->SetNamedPropertyHandler(InterceptorKeyedCallICGetter);
8298 LocalContext context;
8299 context->Global()->Set(v8_str("proto1"), templ->NewInstance());
8300 keyed_call_ic_function =
8301 v8_compile("function f(x) { return x - 1; }; f")->Run();
8302 v8::Handle<Value> value = CompileRun(
8303 "o = new Object();"
8304 "proto2 = new Object();"
8305 "o.y = function(x) { return x + 1; };"
8306 "proto2.y = function(x) { return x + 2; };"
8307 "o.__proto__ = proto1;"
8308 "proto1.__proto__ = proto2;"
8309 "var result = 0;"
8310 "var method = 'x';"
8311 "for (var i = 0; i < 10; i++) {"
8312 " if (i == 5) { method = 'y'; };"
8313 " result += o[method](41);"
8314 "}");
8315 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
8316}
8317
8318
8319// Same as InterceptorKeyedCallICKeyChange1 only the cacheable function sit
8320// on the global object.
8321THREADED_TEST(InterceptorKeyedCallICKeyChangeOnGlobal) {
8322 v8::HandleScope scope;
8323 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
8324 templ->SetNamedPropertyHandler(NoBlockGetterX);
8325 LocalContext context;
8326 context->Global()->Set(v8_str("o"), templ->NewInstance());
8327 v8::Handle<Value> value = CompileRun(
8328 "function inc(x) { return x + 1; };"
8329 "inc(1);"
8330 "function dec(x) { return x - 1; };"
8331 "dec(1);"
8332 "o.__proto__ = this;"
8333 "this.__proto__.x = inc;"
8334 "this.__proto__.y = dec;"
8335 "var result = 0;"
8336 "var method = 'x';"
8337 "for (var i = 0; i < 10; i++) {"
8338 " if (i == 5) { method = 'y'; };"
8339 " result += o[method](41);"
8340 "}");
8341 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
8342}
8343
8344
8345// Test the case when actual function to call sits on global object.
8346THREADED_TEST(InterceptorKeyedCallICFromGlobal) {
8347 v8::HandleScope scope;
8348 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
8349 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
8350 LocalContext context;
8351 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
8352
8353 v8::Handle<Value> value = CompileRun(
8354 "function len(x) { return x.length; };"
8355 "o.__proto__ = this;"
8356 "var m = 'parseFloat';"
8357 "var result = 0;"
8358 "for (var i = 0; i < 10; i++) {"
8359 " if (i == 5) {"
8360 " m = 'len';"
8361 " saved_result = result;"
8362 " };"
8363 " result = o[m]('239');"
8364 "}");
8365 CHECK_EQ(3, context->Global()->Get(v8_str("result"))->Int32Value());
8366 CHECK_EQ(239, context->Global()->Get(v8_str("saved_result"))->Int32Value());
8367}
8368
8369// Test the map transition before the interceptor.
8370THREADED_TEST(InterceptorKeyedCallICMapChangeBefore) {
8371 v8::HandleScope scope;
8372 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
8373 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
8374 LocalContext context;
8375 context->Global()->Set(v8_str("proto"), templ_o->NewInstance());
8376
8377 v8::Handle<Value> value = CompileRun(
8378 "var o = new Object();"
8379 "o.__proto__ = proto;"
8380 "o.method = function(x) { return x + 1; };"
8381 "var m = 'method';"
8382 "var result = 0;"
8383 "for (var i = 0; i < 10; i++) {"
8384 " if (i == 5) { o.method = function(x) { return x - 1; }; };"
8385 " result += o[m](41);"
8386 "}");
8387 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
8388}
8389
8390
8391// Test the map transition after the interceptor.
8392THREADED_TEST(InterceptorKeyedCallICMapChangeAfter) {
8393 v8::HandleScope scope;
8394 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
8395 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
8396 LocalContext context;
8397 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
8398
8399 v8::Handle<Value> value = CompileRun(
8400 "var proto = new Object();"
8401 "o.__proto__ = proto;"
8402 "proto.method = function(x) { return x + 1; };"
8403 "var m = 'method';"
8404 "var result = 0;"
8405 "for (var i = 0; i < 10; i++) {"
8406 " if (i == 5) { proto.method = function(x) { return x - 1; }; };"
8407 " result += o[m](41);"
8408 "}");
8409 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
8410}
8411
8412
Steve Blocka7e24c12009-10-30 11:49:00 +00008413static int interceptor_call_count = 0;
8414
8415static v8::Handle<Value> InterceptorICRefErrorGetter(Local<String> name,
8416 const AccessorInfo& info) {
8417 ApiTestFuzzer::Fuzz();
8418 if (v8_str("x")->Equals(name) && interceptor_call_count++ < 20) {
8419 return call_ic_function2;
8420 }
8421 return v8::Handle<Value>();
8422}
8423
8424
8425// This test should hit load and call ICs for the interceptor case.
8426// Once in a while, the interceptor will reply that a property was not
8427// found in which case we should get a reference error.
8428THREADED_TEST(InterceptorICReferenceErrors) {
8429 v8::HandleScope scope;
8430 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
8431 templ->SetNamedPropertyHandler(InterceptorICRefErrorGetter);
8432 LocalContext context(0, templ, v8::Handle<Value>());
8433 call_ic_function2 = v8_compile("function h(x) { return x; }; h")->Run();
8434 v8::Handle<Value> value = CompileRun(
8435 "function f() {"
8436 " for (var i = 0; i < 1000; i++) {"
8437 " try { x; } catch(e) { return true; }"
8438 " }"
8439 " return false;"
8440 "};"
8441 "f();");
8442 CHECK_EQ(true, value->BooleanValue());
8443 interceptor_call_count = 0;
8444 value = CompileRun(
8445 "function g() {"
8446 " for (var i = 0; i < 1000; i++) {"
8447 " try { x(42); } catch(e) { return true; }"
8448 " }"
8449 " return false;"
8450 "};"
8451 "g();");
8452 CHECK_EQ(true, value->BooleanValue());
8453}
8454
8455
8456static int interceptor_ic_exception_get_count = 0;
8457
8458static v8::Handle<Value> InterceptorICExceptionGetter(
8459 Local<String> name,
8460 const AccessorInfo& info) {
8461 ApiTestFuzzer::Fuzz();
8462 if (v8_str("x")->Equals(name) && ++interceptor_ic_exception_get_count < 20) {
8463 return call_ic_function3;
8464 }
8465 if (interceptor_ic_exception_get_count == 20) {
8466 return v8::ThrowException(v8_num(42));
8467 }
8468 // Do not handle get for properties other than x.
8469 return v8::Handle<Value>();
8470}
8471
8472// Test interceptor load/call IC where the interceptor throws an
8473// exception once in a while.
8474THREADED_TEST(InterceptorICGetterExceptions) {
8475 interceptor_ic_exception_get_count = 0;
8476 v8::HandleScope scope;
8477 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
8478 templ->SetNamedPropertyHandler(InterceptorICExceptionGetter);
8479 LocalContext context(0, templ, v8::Handle<Value>());
8480 call_ic_function3 = v8_compile("function h(x) { return x; }; h")->Run();
8481 v8::Handle<Value> value = CompileRun(
8482 "function f() {"
8483 " for (var i = 0; i < 100; i++) {"
8484 " try { x; } catch(e) { return true; }"
8485 " }"
8486 " return false;"
8487 "};"
8488 "f();");
8489 CHECK_EQ(true, value->BooleanValue());
8490 interceptor_ic_exception_get_count = 0;
8491 value = CompileRun(
8492 "function f() {"
8493 " for (var i = 0; i < 100; i++) {"
8494 " try { x(42); } catch(e) { return true; }"
8495 " }"
8496 " return false;"
8497 "};"
8498 "f();");
8499 CHECK_EQ(true, value->BooleanValue());
8500}
8501
8502
8503static int interceptor_ic_exception_set_count = 0;
8504
8505static v8::Handle<Value> InterceptorICExceptionSetter(
8506 Local<String> key, Local<Value> value, const AccessorInfo&) {
8507 ApiTestFuzzer::Fuzz();
8508 if (++interceptor_ic_exception_set_count > 20) {
8509 return v8::ThrowException(v8_num(42));
8510 }
8511 // Do not actually handle setting.
8512 return v8::Handle<Value>();
8513}
8514
8515// Test interceptor store IC where the interceptor throws an exception
8516// once in a while.
8517THREADED_TEST(InterceptorICSetterExceptions) {
8518 interceptor_ic_exception_set_count = 0;
8519 v8::HandleScope scope;
8520 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
8521 templ->SetNamedPropertyHandler(0, InterceptorICExceptionSetter);
8522 LocalContext context(0, templ, v8::Handle<Value>());
8523 v8::Handle<Value> value = CompileRun(
8524 "function f() {"
8525 " for (var i = 0; i < 100; i++) {"
8526 " try { x = 42; } catch(e) { return true; }"
8527 " }"
8528 " return false;"
8529 "};"
8530 "f();");
8531 CHECK_EQ(true, value->BooleanValue());
8532}
8533
8534
8535// Test that we ignore null interceptors.
8536THREADED_TEST(NullNamedInterceptor) {
8537 v8::HandleScope scope;
8538 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
8539 templ->SetNamedPropertyHandler(0);
8540 LocalContext context;
8541 templ->Set("x", v8_num(42));
8542 v8::Handle<v8::Object> obj = templ->NewInstance();
8543 context->Global()->Set(v8_str("obj"), obj);
8544 v8::Handle<Value> value = CompileRun("obj.x");
8545 CHECK(value->IsInt32());
8546 CHECK_EQ(42, value->Int32Value());
8547}
8548
8549
8550// Test that we ignore null interceptors.
8551THREADED_TEST(NullIndexedInterceptor) {
8552 v8::HandleScope scope;
8553 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
8554 templ->SetIndexedPropertyHandler(0);
8555 LocalContext context;
8556 templ->Set("42", v8_num(42));
8557 v8::Handle<v8::Object> obj = templ->NewInstance();
8558 context->Global()->Set(v8_str("obj"), obj);
8559 v8::Handle<Value> value = CompileRun("obj[42]");
8560 CHECK(value->IsInt32());
8561 CHECK_EQ(42, value->Int32Value());
8562}
8563
8564
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008565THREADED_TEST(NamedPropertyHandlerGetterAttributes) {
8566 v8::HandleScope scope;
8567 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
8568 templ->InstanceTemplate()->SetNamedPropertyHandler(InterceptorLoadXICGetter);
8569 LocalContext env;
8570 env->Global()->Set(v8_str("obj"),
8571 templ->GetFunction()->NewInstance());
8572 ExpectTrue("obj.x === 42");
8573 ExpectTrue("!obj.propertyIsEnumerable('x')");
8574}
8575
8576
Steve Blocka7e24c12009-10-30 11:49:00 +00008577static v8::Handle<Value> ParentGetter(Local<String> name,
8578 const AccessorInfo& info) {
8579 ApiTestFuzzer::Fuzz();
8580 return v8_num(1);
8581}
8582
8583
8584static v8::Handle<Value> ChildGetter(Local<String> name,
8585 const AccessorInfo& info) {
8586 ApiTestFuzzer::Fuzz();
8587 return v8_num(42);
8588}
8589
8590
8591THREADED_TEST(Overriding) {
8592 v8::HandleScope scope;
8593 LocalContext context;
8594
8595 // Parent template.
8596 Local<v8::FunctionTemplate> parent_templ = v8::FunctionTemplate::New();
8597 Local<ObjectTemplate> parent_instance_templ =
8598 parent_templ->InstanceTemplate();
8599 parent_instance_templ->SetAccessor(v8_str("f"), ParentGetter);
8600
8601 // Template that inherits from the parent template.
8602 Local<v8::FunctionTemplate> child_templ = v8::FunctionTemplate::New();
8603 Local<ObjectTemplate> child_instance_templ =
8604 child_templ->InstanceTemplate();
8605 child_templ->Inherit(parent_templ);
8606 // Override 'f'. The child version of 'f' should get called for child
8607 // instances.
8608 child_instance_templ->SetAccessor(v8_str("f"), ChildGetter);
8609 // Add 'g' twice. The 'g' added last should get called for instances.
8610 child_instance_templ->SetAccessor(v8_str("g"), ParentGetter);
8611 child_instance_templ->SetAccessor(v8_str("g"), ChildGetter);
8612
8613 // Add 'h' as an accessor to the proto template with ReadOnly attributes
8614 // so 'h' can be shadowed on the instance object.
8615 Local<ObjectTemplate> child_proto_templ = child_templ->PrototypeTemplate();
8616 child_proto_templ->SetAccessor(v8_str("h"), ParentGetter, 0,
8617 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly);
8618
8619 // Add 'i' as an accessor to the instance template with ReadOnly attributes
8620 // but the attribute does not have effect because it is duplicated with
8621 // NULL setter.
8622 child_instance_templ->SetAccessor(v8_str("i"), ChildGetter, 0,
8623 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly);
8624
8625
8626
8627 // Instantiate the child template.
8628 Local<v8::Object> instance = child_templ->GetFunction()->NewInstance();
8629
8630 // Check that the child function overrides the parent one.
8631 context->Global()->Set(v8_str("o"), instance);
8632 Local<Value> value = v8_compile("o.f")->Run();
8633 // Check that the 'g' that was added last is hit.
8634 CHECK_EQ(42, value->Int32Value());
8635 value = v8_compile("o.g")->Run();
8636 CHECK_EQ(42, value->Int32Value());
8637
8638 // Check 'h' can be shadowed.
8639 value = v8_compile("o.h = 3; o.h")->Run();
8640 CHECK_EQ(3, value->Int32Value());
8641
8642 // Check 'i' is cannot be shadowed or changed.
8643 value = v8_compile("o.i = 3; o.i")->Run();
8644 CHECK_EQ(42, value->Int32Value());
8645}
8646
8647
8648static v8::Handle<Value> IsConstructHandler(const v8::Arguments& args) {
8649 ApiTestFuzzer::Fuzz();
8650 if (args.IsConstructCall()) {
8651 return v8::Boolean::New(true);
8652 }
8653 return v8::Boolean::New(false);
8654}
8655
8656
8657THREADED_TEST(IsConstructCall) {
8658 v8::HandleScope scope;
8659
8660 // Function template with call handler.
8661 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
8662 templ->SetCallHandler(IsConstructHandler);
8663
8664 LocalContext context;
8665
8666 context->Global()->Set(v8_str("f"), templ->GetFunction());
8667 Local<Value> value = v8_compile("f()")->Run();
8668 CHECK(!value->BooleanValue());
8669 value = v8_compile("new f()")->Run();
8670 CHECK(value->BooleanValue());
8671}
8672
8673
8674THREADED_TEST(ObjectProtoToString) {
8675 v8::HandleScope scope;
8676 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
8677 templ->SetClassName(v8_str("MyClass"));
8678
8679 LocalContext context;
8680
8681 Local<String> customized_tostring = v8_str("customized toString");
8682
8683 // Replace Object.prototype.toString
8684 v8_compile("Object.prototype.toString = function() {"
8685 " return 'customized toString';"
8686 "}")->Run();
8687
8688 // Normal ToString call should call replaced Object.prototype.toString
8689 Local<v8::Object> instance = templ->GetFunction()->NewInstance();
8690 Local<String> value = instance->ToString();
8691 CHECK(value->IsString() && value->Equals(customized_tostring));
8692
8693 // ObjectProtoToString should not call replace toString function.
8694 value = instance->ObjectProtoToString();
8695 CHECK(value->IsString() && value->Equals(v8_str("[object MyClass]")));
8696
8697 // Check global
8698 value = context->Global()->ObjectProtoToString();
8699 CHECK(value->IsString() && value->Equals(v8_str("[object global]")));
8700
8701 // Check ordinary object
8702 Local<Value> object = v8_compile("new Object()")->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01008703 value = object.As<v8::Object>()->ObjectProtoToString();
Steve Blocka7e24c12009-10-30 11:49:00 +00008704 CHECK(value->IsString() && value->Equals(v8_str("[object Object]")));
8705}
8706
8707
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -08008708THREADED_TEST(ObjectGetConstructorName) {
8709 v8::HandleScope scope;
8710 LocalContext context;
8711 v8_compile("function Parent() {};"
8712 "function Child() {};"
8713 "Child.prototype = new Parent();"
8714 "var outer = { inner: function() { } };"
8715 "var p = new Parent();"
8716 "var c = new Child();"
8717 "var x = new outer.inner();")->Run();
8718
8719 Local<v8::Value> p = context->Global()->Get(v8_str("p"));
8720 CHECK(p->IsObject() && p->ToObject()->GetConstructorName()->Equals(
8721 v8_str("Parent")));
8722
8723 Local<v8::Value> c = context->Global()->Get(v8_str("c"));
8724 CHECK(c->IsObject() && c->ToObject()->GetConstructorName()->Equals(
8725 v8_str("Child")));
8726
8727 Local<v8::Value> x = context->Global()->Get(v8_str("x"));
8728 CHECK(x->IsObject() && x->ToObject()->GetConstructorName()->Equals(
8729 v8_str("outer.inner")));
8730}
8731
8732
Steve Blocka7e24c12009-10-30 11:49:00 +00008733bool ApiTestFuzzer::fuzzing_ = false;
Steve Block8defd9f2010-07-08 12:39:36 +01008734i::Semaphore* ApiTestFuzzer::all_tests_done_=
8735 i::OS::CreateSemaphore(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00008736int ApiTestFuzzer::active_tests_;
8737int ApiTestFuzzer::tests_being_run_;
8738int ApiTestFuzzer::current_;
8739
8740
8741// We are in a callback and want to switch to another thread (if we
8742// are currently running the thread fuzzing test).
8743void ApiTestFuzzer::Fuzz() {
8744 if (!fuzzing_) return;
8745 ApiTestFuzzer* test = RegisterThreadedTest::nth(current_)->fuzzer_;
8746 test->ContextSwitch();
8747}
8748
8749
8750// Let the next thread go. Since it is also waiting on the V8 lock it may
8751// not start immediately.
8752bool ApiTestFuzzer::NextThread() {
8753 int test_position = GetNextTestNumber();
Steve Blockd0582a62009-12-15 09:54:21 +00008754 const char* test_name = RegisterThreadedTest::nth(current_)->name();
Steve Blocka7e24c12009-10-30 11:49:00 +00008755 if (test_position == current_) {
Steve Blockd0582a62009-12-15 09:54:21 +00008756 if (kLogThreading)
8757 printf("Stay with %s\n", test_name);
Steve Blocka7e24c12009-10-30 11:49:00 +00008758 return false;
8759 }
Steve Blockd0582a62009-12-15 09:54:21 +00008760 if (kLogThreading) {
8761 printf("Switch from %s to %s\n",
8762 test_name,
8763 RegisterThreadedTest::nth(test_position)->name());
8764 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008765 current_ = test_position;
8766 RegisterThreadedTest::nth(current_)->fuzzer_->gate_->Signal();
8767 return true;
8768}
8769
8770
8771void ApiTestFuzzer::Run() {
8772 // When it is our turn...
8773 gate_->Wait();
8774 {
8775 // ... get the V8 lock and start running the test.
8776 v8::Locker locker;
8777 CallTest();
8778 }
8779 // This test finished.
8780 active_ = false;
8781 active_tests_--;
8782 // If it was the last then signal that fact.
8783 if (active_tests_ == 0) {
8784 all_tests_done_->Signal();
8785 } else {
8786 // Otherwise select a new test and start that.
8787 NextThread();
8788 }
8789}
8790
8791
8792static unsigned linear_congruential_generator;
8793
8794
8795void ApiTestFuzzer::Setup(PartOfTest part) {
8796 linear_congruential_generator = i::FLAG_testing_prng_seed;
8797 fuzzing_ = true;
8798 int start = (part == FIRST_PART) ? 0 : (RegisterThreadedTest::count() >> 1);
8799 int end = (part == FIRST_PART)
8800 ? (RegisterThreadedTest::count() >> 1)
8801 : RegisterThreadedTest::count();
8802 active_tests_ = tests_being_run_ = end - start;
8803 for (int i = 0; i < tests_being_run_; i++) {
Steve Block44f0eee2011-05-26 01:26:41 +01008804 RegisterThreadedTest::nth(i)->fuzzer_ = new ApiTestFuzzer(
8805 i::Isolate::Current(), i + start);
Steve Blocka7e24c12009-10-30 11:49:00 +00008806 }
8807 for (int i = 0; i < active_tests_; i++) {
8808 RegisterThreadedTest::nth(i)->fuzzer_->Start();
8809 }
8810}
8811
8812
8813static void CallTestNumber(int test_number) {
8814 (RegisterThreadedTest::nth(test_number)->callback())();
8815}
8816
8817
8818void ApiTestFuzzer::RunAllTests() {
8819 // Set off the first test.
8820 current_ = -1;
8821 NextThread();
8822 // Wait till they are all done.
8823 all_tests_done_->Wait();
8824}
8825
8826
8827int ApiTestFuzzer::GetNextTestNumber() {
8828 int next_test;
8829 do {
8830 next_test = (linear_congruential_generator >> 16) % tests_being_run_;
8831 linear_congruential_generator *= 1664525u;
8832 linear_congruential_generator += 1013904223u;
8833 } while (!RegisterThreadedTest::nth(next_test)->fuzzer_->active_);
8834 return next_test;
8835}
8836
8837
8838void ApiTestFuzzer::ContextSwitch() {
8839 // If the new thread is the same as the current thread there is nothing to do.
8840 if (NextThread()) {
8841 // Now it can start.
8842 v8::Unlocker unlocker;
8843 // Wait till someone starts us again.
8844 gate_->Wait();
8845 // And we're off.
8846 }
8847}
8848
8849
8850void ApiTestFuzzer::TearDown() {
8851 fuzzing_ = false;
8852 for (int i = 0; i < RegisterThreadedTest::count(); i++) {
8853 ApiTestFuzzer *fuzzer = RegisterThreadedTest::nth(i)->fuzzer_;
8854 if (fuzzer != NULL) fuzzer->Join();
8855 }
8856}
8857
8858
8859// Lets not be needlessly self-referential.
8860TEST(Threading) {
8861 ApiTestFuzzer::Setup(ApiTestFuzzer::FIRST_PART);
8862 ApiTestFuzzer::RunAllTests();
8863 ApiTestFuzzer::TearDown();
8864}
8865
8866TEST(Threading2) {
8867 ApiTestFuzzer::Setup(ApiTestFuzzer::SECOND_PART);
8868 ApiTestFuzzer::RunAllTests();
8869 ApiTestFuzzer::TearDown();
8870}
8871
8872
8873void ApiTestFuzzer::CallTest() {
Steve Blockd0582a62009-12-15 09:54:21 +00008874 if (kLogThreading)
8875 printf("Start test %d\n", test_number_);
Steve Blocka7e24c12009-10-30 11:49:00 +00008876 CallTestNumber(test_number_);
Steve Blockd0582a62009-12-15 09:54:21 +00008877 if (kLogThreading)
8878 printf("End test %d\n", test_number_);
Steve Blocka7e24c12009-10-30 11:49:00 +00008879}
8880
8881
8882static v8::Handle<Value> ThrowInJS(const v8::Arguments& args) {
8883 CHECK(v8::Locker::IsLocked());
8884 ApiTestFuzzer::Fuzz();
8885 v8::Unlocker unlocker;
8886 const char* code = "throw 7;";
8887 {
8888 v8::Locker nested_locker;
8889 v8::HandleScope scope;
8890 v8::Handle<Value> exception;
8891 { v8::TryCatch try_catch;
8892 v8::Handle<Value> value = CompileRun(code);
8893 CHECK(value.IsEmpty());
8894 CHECK(try_catch.HasCaught());
8895 // Make sure to wrap the exception in a new handle because
8896 // the handle returned from the TryCatch is destroyed
8897 // when the TryCatch is destroyed.
8898 exception = Local<Value>::New(try_catch.Exception());
8899 }
8900 return v8::ThrowException(exception);
8901 }
8902}
8903
8904
8905static v8::Handle<Value> ThrowInJSNoCatch(const v8::Arguments& args) {
8906 CHECK(v8::Locker::IsLocked());
8907 ApiTestFuzzer::Fuzz();
8908 v8::Unlocker unlocker;
8909 const char* code = "throw 7;";
8910 {
8911 v8::Locker nested_locker;
8912 v8::HandleScope scope;
8913 v8::Handle<Value> value = CompileRun(code);
8914 CHECK(value.IsEmpty());
8915 return v8_str("foo");
8916 }
8917}
8918
8919
8920// These are locking tests that don't need to be run again
8921// as part of the locking aggregation tests.
8922TEST(NestedLockers) {
8923 v8::Locker locker;
8924 CHECK(v8::Locker::IsLocked());
8925 v8::HandleScope scope;
8926 LocalContext env;
8927 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(ThrowInJS);
8928 Local<Function> fun = fun_templ->GetFunction();
8929 env->Global()->Set(v8_str("throw_in_js"), fun);
8930 Local<Script> script = v8_compile("(function () {"
8931 " try {"
8932 " throw_in_js();"
8933 " return 42;"
8934 " } catch (e) {"
8935 " return e * 13;"
8936 " }"
8937 "})();");
8938 CHECK_EQ(91, script->Run()->Int32Value());
8939}
8940
8941
8942// These are locking tests that don't need to be run again
8943// as part of the locking aggregation tests.
8944TEST(NestedLockersNoTryCatch) {
8945 v8::Locker locker;
8946 v8::HandleScope scope;
8947 LocalContext env;
8948 Local<v8::FunctionTemplate> fun_templ =
8949 v8::FunctionTemplate::New(ThrowInJSNoCatch);
8950 Local<Function> fun = fun_templ->GetFunction();
8951 env->Global()->Set(v8_str("throw_in_js"), fun);
8952 Local<Script> script = v8_compile("(function () {"
8953 " try {"
8954 " throw_in_js();"
8955 " return 42;"
8956 " } catch (e) {"
8957 " return e * 13;"
8958 " }"
8959 "})();");
8960 CHECK_EQ(91, script->Run()->Int32Value());
8961}
8962
8963
8964THREADED_TEST(RecursiveLocking) {
8965 v8::Locker locker;
8966 {
8967 v8::Locker locker2;
8968 CHECK(v8::Locker::IsLocked());
8969 }
8970}
8971
8972
8973static v8::Handle<Value> UnlockForAMoment(const v8::Arguments& args) {
8974 ApiTestFuzzer::Fuzz();
8975 v8::Unlocker unlocker;
8976 return v8::Undefined();
8977}
8978
8979
8980THREADED_TEST(LockUnlockLock) {
8981 {
8982 v8::Locker locker;
8983 v8::HandleScope scope;
8984 LocalContext env;
8985 Local<v8::FunctionTemplate> fun_templ =
8986 v8::FunctionTemplate::New(UnlockForAMoment);
8987 Local<Function> fun = fun_templ->GetFunction();
8988 env->Global()->Set(v8_str("unlock_for_a_moment"), fun);
8989 Local<Script> script = v8_compile("(function () {"
8990 " unlock_for_a_moment();"
8991 " return 42;"
8992 "})();");
8993 CHECK_EQ(42, script->Run()->Int32Value());
8994 }
8995 {
8996 v8::Locker locker;
8997 v8::HandleScope scope;
8998 LocalContext env;
8999 Local<v8::FunctionTemplate> fun_templ =
9000 v8::FunctionTemplate::New(UnlockForAMoment);
9001 Local<Function> fun = fun_templ->GetFunction();
9002 env->Global()->Set(v8_str("unlock_for_a_moment"), fun);
9003 Local<Script> script = v8_compile("(function () {"
9004 " unlock_for_a_moment();"
9005 " return 42;"
9006 "})();");
9007 CHECK_EQ(42, script->Run()->Int32Value());
9008 }
9009}
9010
9011
Leon Clarked91b9f72010-01-27 17:25:45 +00009012static int GetGlobalObjectsCount() {
Leon Clarkeeab96aa2010-01-27 16:31:12 +00009013 int count = 0;
Steve Block8defd9f2010-07-08 12:39:36 +01009014 i::HeapIterator it;
Leon Clarked91b9f72010-01-27 17:25:45 +00009015 for (i::HeapObject* object = it.next(); object != NULL; object = it.next())
9016 if (object->IsJSGlobalObject()) count++;
9017 return count;
9018}
9019
9020
Ben Murdochf87a2032010-10-22 12:50:53 +01009021static void CheckSurvivingGlobalObjectsCount(int expected) {
Steve Blocka7e24c12009-10-30 11:49:00 +00009022 // We need to collect all garbage twice to be sure that everything
9023 // has been collected. This is because inline caches are cleared in
9024 // the first garbage collection but some of the maps have already
9025 // been marked at that point. Therefore some of the maps are not
9026 // collected until the second garbage collection.
Steve Block44f0eee2011-05-26 01:26:41 +01009027 HEAP->CollectAllGarbage(false);
9028 HEAP->CollectAllGarbage(false);
Leon Clarked91b9f72010-01-27 17:25:45 +00009029 int count = GetGlobalObjectsCount();
Steve Blocka7e24c12009-10-30 11:49:00 +00009030#ifdef DEBUG
Steve Block44f0eee2011-05-26 01:26:41 +01009031 if (count != expected) HEAP->TracePathToGlobal();
Steve Blocka7e24c12009-10-30 11:49:00 +00009032#endif
Ben Murdochf87a2032010-10-22 12:50:53 +01009033 CHECK_EQ(expected, count);
Steve Blocka7e24c12009-10-30 11:49:00 +00009034}
9035
9036
9037TEST(DontLeakGlobalObjects) {
9038 // Regression test for issues 1139850 and 1174891.
9039
9040 v8::V8::Initialize();
9041
Steve Blocka7e24c12009-10-30 11:49:00 +00009042 for (int i = 0; i < 5; i++) {
9043 { v8::HandleScope scope;
9044 LocalContext context;
9045 }
Ben Murdochf87a2032010-10-22 12:50:53 +01009046 CheckSurvivingGlobalObjectsCount(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00009047
9048 { v8::HandleScope scope;
9049 LocalContext context;
9050 v8_compile("Date")->Run();
9051 }
Ben Murdochf87a2032010-10-22 12:50:53 +01009052 CheckSurvivingGlobalObjectsCount(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00009053
9054 { v8::HandleScope scope;
9055 LocalContext context;
9056 v8_compile("/aaa/")->Run();
9057 }
Ben Murdochf87a2032010-10-22 12:50:53 +01009058 CheckSurvivingGlobalObjectsCount(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00009059
9060 { v8::HandleScope scope;
9061 const char* extension_list[] = { "v8/gc" };
9062 v8::ExtensionConfiguration extensions(1, extension_list);
9063 LocalContext context(&extensions);
9064 v8_compile("gc();")->Run();
9065 }
Ben Murdochf87a2032010-10-22 12:50:53 +01009066 CheckSurvivingGlobalObjectsCount(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00009067 }
9068}
9069
9070
9071v8::Persistent<v8::Object> some_object;
9072v8::Persistent<v8::Object> bad_handle;
9073
Kristian Monsen50ef84f2010-07-29 15:18:00 +01009074void NewPersistentHandleCallback(v8::Persistent<v8::Value> handle, void*) {
Steve Blocka7e24c12009-10-30 11:49:00 +00009075 v8::HandleScope scope;
9076 bad_handle = v8::Persistent<v8::Object>::New(some_object);
Kristian Monsen50ef84f2010-07-29 15:18:00 +01009077 handle.Dispose();
Steve Blocka7e24c12009-10-30 11:49:00 +00009078}
9079
9080
9081THREADED_TEST(NewPersistentHandleFromWeakCallback) {
9082 LocalContext context;
9083
9084 v8::Persistent<v8::Object> handle1, handle2;
9085 {
9086 v8::HandleScope scope;
9087 some_object = v8::Persistent<v8::Object>::New(v8::Object::New());
9088 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
9089 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
9090 }
9091 // Note: order is implementation dependent alas: currently
9092 // global handle nodes are processed by PostGarbageCollectionProcessing
9093 // in reverse allocation order, so if second allocated handle is deleted,
9094 // weak callback of the first handle would be able to 'reallocate' it.
9095 handle1.MakeWeak(NULL, NewPersistentHandleCallback);
9096 handle2.Dispose();
Steve Block44f0eee2011-05-26 01:26:41 +01009097 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +00009098}
9099
9100
9101v8::Persistent<v8::Object> to_be_disposed;
9102
9103void DisposeAndForceGcCallback(v8::Persistent<v8::Value> handle, void*) {
9104 to_be_disposed.Dispose();
Steve Block44f0eee2011-05-26 01:26:41 +01009105 HEAP->CollectAllGarbage(false);
Kristian Monsen50ef84f2010-07-29 15:18:00 +01009106 handle.Dispose();
Steve Blocka7e24c12009-10-30 11:49:00 +00009107}
9108
9109
9110THREADED_TEST(DoNotUseDeletedNodesInSecondLevelGc) {
9111 LocalContext context;
9112
9113 v8::Persistent<v8::Object> handle1, handle2;
9114 {
9115 v8::HandleScope scope;
9116 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
9117 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
9118 }
9119 handle1.MakeWeak(NULL, DisposeAndForceGcCallback);
9120 to_be_disposed = handle2;
Steve Block44f0eee2011-05-26 01:26:41 +01009121 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +00009122}
9123
Steve Blockd0582a62009-12-15 09:54:21 +00009124void DisposingCallback(v8::Persistent<v8::Value> handle, void*) {
9125 handle.Dispose();
9126}
9127
9128void HandleCreatingCallback(v8::Persistent<v8::Value> handle, void*) {
9129 v8::HandleScope scope;
9130 v8::Persistent<v8::Object>::New(v8::Object::New());
Kristian Monsen50ef84f2010-07-29 15:18:00 +01009131 handle.Dispose();
Steve Blockd0582a62009-12-15 09:54:21 +00009132}
9133
9134
9135THREADED_TEST(NoGlobalHandlesOrphaningDueToWeakCallback) {
9136 LocalContext context;
9137
9138 v8::Persistent<v8::Object> handle1, handle2, handle3;
9139 {
9140 v8::HandleScope scope;
9141 handle3 = v8::Persistent<v8::Object>::New(v8::Object::New());
9142 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
9143 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
9144 }
9145 handle2.MakeWeak(NULL, DisposingCallback);
9146 handle3.MakeWeak(NULL, HandleCreatingCallback);
Steve Block44f0eee2011-05-26 01:26:41 +01009147 HEAP->CollectAllGarbage(false);
Steve Blockd0582a62009-12-15 09:54:21 +00009148}
9149
Steve Blocka7e24c12009-10-30 11:49:00 +00009150
9151THREADED_TEST(CheckForCrossContextObjectLiterals) {
9152 v8::V8::Initialize();
9153
9154 const int nof = 2;
9155 const char* sources[nof] = {
9156 "try { [ 2, 3, 4 ].forEach(5); } catch(e) { e.toString(); }",
9157 "Object()"
9158 };
9159
9160 for (int i = 0; i < nof; i++) {
9161 const char* source = sources[i];
9162 { v8::HandleScope scope;
9163 LocalContext context;
9164 CompileRun(source);
9165 }
9166 { v8::HandleScope scope;
9167 LocalContext context;
9168 CompileRun(source);
9169 }
9170 }
9171}
9172
9173
9174static v8::Handle<Value> NestedScope(v8::Persistent<Context> env) {
9175 v8::HandleScope inner;
9176 env->Enter();
9177 v8::Handle<Value> three = v8_num(3);
9178 v8::Handle<Value> value = inner.Close(three);
9179 env->Exit();
9180 return value;
9181}
9182
9183
9184THREADED_TEST(NestedHandleScopeAndContexts) {
9185 v8::HandleScope outer;
9186 v8::Persistent<Context> env = Context::New();
9187 env->Enter();
9188 v8::Handle<Value> value = NestedScope(env);
9189 v8::Handle<String> str = value->ToString();
9190 env->Exit();
9191 env.Dispose();
9192}
9193
9194
9195THREADED_TEST(ExternalAllocatedMemory) {
9196 v8::HandleScope outer;
9197 v8::Persistent<Context> env = Context::New();
9198 const int kSize = 1024*1024;
9199 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(kSize), kSize);
9200 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(-kSize), 0);
9201}
9202
9203
9204THREADED_TEST(DisposeEnteredContext) {
9205 v8::HandleScope scope;
9206 LocalContext outer;
9207 { v8::Persistent<v8::Context> inner = v8::Context::New();
9208 inner->Enter();
9209 inner.Dispose();
9210 inner.Clear();
9211 inner->Exit();
9212 }
9213}
9214
9215
9216// Regression test for issue 54, object templates with internal fields
9217// but no accessors or interceptors did not get their internal field
9218// count set on instances.
9219THREADED_TEST(Regress54) {
9220 v8::HandleScope outer;
9221 LocalContext context;
9222 static v8::Persistent<v8::ObjectTemplate> templ;
9223 if (templ.IsEmpty()) {
9224 v8::HandleScope inner;
9225 v8::Handle<v8::ObjectTemplate> local = v8::ObjectTemplate::New();
9226 local->SetInternalFieldCount(1);
9227 templ = v8::Persistent<v8::ObjectTemplate>::New(inner.Close(local));
9228 }
9229 v8::Handle<v8::Object> result = templ->NewInstance();
9230 CHECK_EQ(1, result->InternalFieldCount());
9231}
9232
9233
9234// If part of the threaded tests, this test makes ThreadingTest fail
9235// on mac.
9236TEST(CatchStackOverflow) {
9237 v8::HandleScope scope;
9238 LocalContext context;
9239 v8::TryCatch try_catch;
9240 v8::Handle<v8::Script> script = v8::Script::Compile(v8::String::New(
9241 "function f() {"
9242 " return f();"
9243 "}"
9244 ""
9245 "f();"));
9246 v8::Handle<v8::Value> result = script->Run();
9247 CHECK(result.IsEmpty());
9248}
9249
9250
9251static void CheckTryCatchSourceInfo(v8::Handle<v8::Script> script,
9252 const char* resource_name,
9253 int line_offset) {
9254 v8::HandleScope scope;
9255 v8::TryCatch try_catch;
9256 v8::Handle<v8::Value> result = script->Run();
9257 CHECK(result.IsEmpty());
9258 CHECK(try_catch.HasCaught());
9259 v8::Handle<v8::Message> message = try_catch.Message();
9260 CHECK(!message.IsEmpty());
9261 CHECK_EQ(10 + line_offset, message->GetLineNumber());
9262 CHECK_EQ(91, message->GetStartPosition());
9263 CHECK_EQ(92, message->GetEndPosition());
9264 CHECK_EQ(2, message->GetStartColumn());
9265 CHECK_EQ(3, message->GetEndColumn());
9266 v8::String::AsciiValue line(message->GetSourceLine());
9267 CHECK_EQ(" throw 'nirk';", *line);
9268 v8::String::AsciiValue name(message->GetScriptResourceName());
9269 CHECK_EQ(resource_name, *name);
9270}
9271
9272
9273THREADED_TEST(TryCatchSourceInfo) {
9274 v8::HandleScope scope;
9275 LocalContext context;
9276 v8::Handle<v8::String> source = v8::String::New(
9277 "function Foo() {\n"
9278 " return Bar();\n"
9279 "}\n"
9280 "\n"
9281 "function Bar() {\n"
9282 " return Baz();\n"
9283 "}\n"
9284 "\n"
9285 "function Baz() {\n"
9286 " throw 'nirk';\n"
9287 "}\n"
9288 "\n"
9289 "Foo();\n");
9290
9291 const char* resource_name;
9292 v8::Handle<v8::Script> script;
9293 resource_name = "test.js";
9294 script = v8::Script::Compile(source, v8::String::New(resource_name));
9295 CheckTryCatchSourceInfo(script, resource_name, 0);
9296
9297 resource_name = "test1.js";
9298 v8::ScriptOrigin origin1(v8::String::New(resource_name));
9299 script = v8::Script::Compile(source, &origin1);
9300 CheckTryCatchSourceInfo(script, resource_name, 0);
9301
9302 resource_name = "test2.js";
9303 v8::ScriptOrigin origin2(v8::String::New(resource_name), v8::Integer::New(7));
9304 script = v8::Script::Compile(source, &origin2);
9305 CheckTryCatchSourceInfo(script, resource_name, 7);
9306}
9307
9308
9309THREADED_TEST(CompilationCache) {
9310 v8::HandleScope scope;
9311 LocalContext context;
9312 v8::Handle<v8::String> source0 = v8::String::New("1234");
9313 v8::Handle<v8::String> source1 = v8::String::New("1234");
9314 v8::Handle<v8::Script> script0 =
9315 v8::Script::Compile(source0, v8::String::New("test.js"));
9316 v8::Handle<v8::Script> script1 =
9317 v8::Script::Compile(source1, v8::String::New("test.js"));
9318 v8::Handle<v8::Script> script2 =
9319 v8::Script::Compile(source0); // different origin
9320 CHECK_EQ(1234, script0->Run()->Int32Value());
9321 CHECK_EQ(1234, script1->Run()->Int32Value());
9322 CHECK_EQ(1234, script2->Run()->Int32Value());
9323}
9324
9325
9326static v8::Handle<Value> FunctionNameCallback(const v8::Arguments& args) {
9327 ApiTestFuzzer::Fuzz();
9328 return v8_num(42);
9329}
9330
9331
9332THREADED_TEST(CallbackFunctionName) {
9333 v8::HandleScope scope;
9334 LocalContext context;
9335 Local<ObjectTemplate> t = ObjectTemplate::New();
9336 t->Set(v8_str("asdf"), v8::FunctionTemplate::New(FunctionNameCallback));
9337 context->Global()->Set(v8_str("obj"), t->NewInstance());
9338 v8::Handle<v8::Value> value = CompileRun("obj.asdf.name");
9339 CHECK(value->IsString());
9340 v8::String::AsciiValue name(value);
9341 CHECK_EQ("asdf", *name);
9342}
9343
9344
9345THREADED_TEST(DateAccess) {
9346 v8::HandleScope scope;
9347 LocalContext context;
9348 v8::Handle<v8::Value> date = v8::Date::New(1224744689038.0);
9349 CHECK(date->IsDate());
Steve Block6ded16b2010-05-10 14:33:55 +01009350 CHECK_EQ(1224744689038.0, date.As<v8::Date>()->NumberValue());
Steve Blocka7e24c12009-10-30 11:49:00 +00009351}
9352
9353
9354void CheckProperties(v8::Handle<v8::Value> val, int elmc, const char* elmv[]) {
Steve Block6ded16b2010-05-10 14:33:55 +01009355 v8::Handle<v8::Object> obj = val.As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00009356 v8::Handle<v8::Array> props = obj->GetPropertyNames();
9357 CHECK_EQ(elmc, props->Length());
9358 for (int i = 0; i < elmc; i++) {
9359 v8::String::Utf8Value elm(props->Get(v8::Integer::New(i)));
9360 CHECK_EQ(elmv[i], *elm);
9361 }
9362}
9363
9364
9365THREADED_TEST(PropertyEnumeration) {
9366 v8::HandleScope scope;
9367 LocalContext context;
9368 v8::Handle<v8::Value> obj = v8::Script::Compile(v8::String::New(
9369 "var result = [];"
9370 "result[0] = {};"
9371 "result[1] = {a: 1, b: 2};"
9372 "result[2] = [1, 2, 3];"
9373 "var proto = {x: 1, y: 2, z: 3};"
9374 "var x = { __proto__: proto, w: 0, z: 1 };"
9375 "result[3] = x;"
9376 "result;"))->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01009377 v8::Handle<v8::Array> elms = obj.As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00009378 CHECK_EQ(4, elms->Length());
9379 int elmc0 = 0;
9380 const char** elmv0 = NULL;
9381 CheckProperties(elms->Get(v8::Integer::New(0)), elmc0, elmv0);
9382 int elmc1 = 2;
9383 const char* elmv1[] = {"a", "b"};
9384 CheckProperties(elms->Get(v8::Integer::New(1)), elmc1, elmv1);
9385 int elmc2 = 3;
9386 const char* elmv2[] = {"0", "1", "2"};
9387 CheckProperties(elms->Get(v8::Integer::New(2)), elmc2, elmv2);
9388 int elmc3 = 4;
9389 const char* elmv3[] = {"w", "z", "x", "y"};
9390 CheckProperties(elms->Get(v8::Integer::New(3)), elmc3, elmv3);
9391}
9392
Steve Block44f0eee2011-05-26 01:26:41 +01009393THREADED_TEST(PropertyEnumeration2) {
9394 v8::HandleScope scope;
9395 LocalContext context;
9396 v8::Handle<v8::Value> obj = v8::Script::Compile(v8::String::New(
9397 "var result = [];"
9398 "result[0] = {};"
9399 "result[1] = {a: 1, b: 2};"
9400 "result[2] = [1, 2, 3];"
9401 "var proto = {x: 1, y: 2, z: 3};"
9402 "var x = { __proto__: proto, w: 0, z: 1 };"
9403 "result[3] = x;"
9404 "result;"))->Run();
9405 v8::Handle<v8::Array> elms = obj.As<v8::Array>();
9406 CHECK_EQ(4, elms->Length());
9407 int elmc0 = 0;
9408 const char** elmv0 = NULL;
9409 CheckProperties(elms->Get(v8::Integer::New(0)), elmc0, elmv0);
9410
9411 v8::Handle<v8::Value> val = elms->Get(v8::Integer::New(0));
9412 v8::Handle<v8::Array> props = val.As<v8::Object>()->GetPropertyNames();
9413 CHECK_EQ(0, props->Length());
9414 for (uint32_t i = 0; i < props->Length(); i++) {
9415 printf("p[%d]\n", i);
9416 }
9417}
Steve Blocka7e24c12009-10-30 11:49:00 +00009418
Steve Blocka7e24c12009-10-30 11:49:00 +00009419static bool NamedSetAccessBlocker(Local<v8::Object> obj,
9420 Local<Value> name,
9421 v8::AccessType type,
9422 Local<Value> data) {
9423 return type != v8::ACCESS_SET;
9424}
9425
9426
9427static bool IndexedSetAccessBlocker(Local<v8::Object> obj,
9428 uint32_t key,
9429 v8::AccessType type,
9430 Local<Value> data) {
9431 return type != v8::ACCESS_SET;
9432}
9433
9434
9435THREADED_TEST(DisableAccessChecksWhileConfiguring) {
9436 v8::HandleScope scope;
9437 LocalContext context;
9438 Local<ObjectTemplate> templ = ObjectTemplate::New();
9439 templ->SetAccessCheckCallbacks(NamedSetAccessBlocker,
9440 IndexedSetAccessBlocker);
9441 templ->Set(v8_str("x"), v8::True());
9442 Local<v8::Object> instance = templ->NewInstance();
9443 context->Global()->Set(v8_str("obj"), instance);
9444 Local<Value> value = CompileRun("obj.x");
9445 CHECK(value->BooleanValue());
9446}
9447
9448
9449static bool NamedGetAccessBlocker(Local<v8::Object> obj,
9450 Local<Value> name,
9451 v8::AccessType type,
9452 Local<Value> data) {
9453 return false;
9454}
9455
9456
9457static bool IndexedGetAccessBlocker(Local<v8::Object> obj,
9458 uint32_t key,
9459 v8::AccessType type,
9460 Local<Value> data) {
9461 return false;
9462}
9463
9464
9465
9466THREADED_TEST(AccessChecksReenabledCorrectly) {
9467 v8::HandleScope scope;
9468 LocalContext context;
9469 Local<ObjectTemplate> templ = ObjectTemplate::New();
9470 templ->SetAccessCheckCallbacks(NamedGetAccessBlocker,
9471 IndexedGetAccessBlocker);
9472 templ->Set(v8_str("a"), v8_str("a"));
9473 // Add more than 8 (see kMaxFastProperties) properties
9474 // so that the constructor will force copying map.
9475 // Cannot sprintf, gcc complains unsafety.
9476 char buf[4];
9477 for (char i = '0'; i <= '9' ; i++) {
9478 buf[0] = i;
9479 for (char j = '0'; j <= '9'; j++) {
9480 buf[1] = j;
9481 for (char k = '0'; k <= '9'; k++) {
9482 buf[2] = k;
9483 buf[3] = 0;
9484 templ->Set(v8_str(buf), v8::Number::New(k));
9485 }
9486 }
9487 }
9488
9489 Local<v8::Object> instance_1 = templ->NewInstance();
9490 context->Global()->Set(v8_str("obj_1"), instance_1);
9491
9492 Local<Value> value_1 = CompileRun("obj_1.a");
9493 CHECK(value_1->IsUndefined());
9494
9495 Local<v8::Object> instance_2 = templ->NewInstance();
9496 context->Global()->Set(v8_str("obj_2"), instance_2);
9497
9498 Local<Value> value_2 = CompileRun("obj_2.a");
9499 CHECK(value_2->IsUndefined());
9500}
9501
9502
9503// This tests that access check information remains on the global
9504// object template when creating contexts.
9505THREADED_TEST(AccessControlRepeatedContextCreation) {
9506 v8::HandleScope handle_scope;
9507 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
9508 global_template->SetAccessCheckCallbacks(NamedSetAccessBlocker,
9509 IndexedSetAccessBlocker);
9510 i::Handle<i::ObjectTemplateInfo> internal_template =
9511 v8::Utils::OpenHandle(*global_template);
9512 CHECK(!internal_template->constructor()->IsUndefined());
9513 i::Handle<i::FunctionTemplateInfo> constructor(
9514 i::FunctionTemplateInfo::cast(internal_template->constructor()));
9515 CHECK(!constructor->access_check_info()->IsUndefined());
9516 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
9517 CHECK(!constructor->access_check_info()->IsUndefined());
9518}
9519
9520
9521THREADED_TEST(TurnOnAccessCheck) {
9522 v8::HandleScope handle_scope;
9523
9524 // Create an environment with access check to the global object disabled by
9525 // default.
9526 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
9527 global_template->SetAccessCheckCallbacks(NamedGetAccessBlocker,
9528 IndexedGetAccessBlocker,
9529 v8::Handle<v8::Value>(),
9530 false);
9531 v8::Persistent<Context> context = Context::New(NULL, global_template);
9532 Context::Scope context_scope(context);
9533
9534 // Set up a property and a number of functions.
9535 context->Global()->Set(v8_str("a"), v8_num(1));
9536 CompileRun("function f1() {return a;}"
9537 "function f2() {return a;}"
9538 "function g1() {return h();}"
9539 "function g2() {return h();}"
9540 "function h() {return 1;}");
9541 Local<Function> f1 =
9542 Local<Function>::Cast(context->Global()->Get(v8_str("f1")));
9543 Local<Function> f2 =
9544 Local<Function>::Cast(context->Global()->Get(v8_str("f2")));
9545 Local<Function> g1 =
9546 Local<Function>::Cast(context->Global()->Get(v8_str("g1")));
9547 Local<Function> g2 =
9548 Local<Function>::Cast(context->Global()->Get(v8_str("g2")));
9549 Local<Function> h =
9550 Local<Function>::Cast(context->Global()->Get(v8_str("h")));
9551
9552 // Get the global object.
9553 v8::Handle<v8::Object> global = context->Global();
9554
9555 // Call f1 one time and f2 a number of times. This will ensure that f1 still
9556 // uses the runtime system to retreive property a whereas f2 uses global load
9557 // inline cache.
9558 CHECK(f1->Call(global, 0, NULL)->Equals(v8_num(1)));
9559 for (int i = 0; i < 4; i++) {
9560 CHECK(f2->Call(global, 0, NULL)->Equals(v8_num(1)));
9561 }
9562
9563 // Same for g1 and g2.
9564 CHECK(g1->Call(global, 0, NULL)->Equals(v8_num(1)));
9565 for (int i = 0; i < 4; i++) {
9566 CHECK(g2->Call(global, 0, NULL)->Equals(v8_num(1)));
9567 }
9568
9569 // Detach the global and turn on access check.
9570 context->DetachGlobal();
9571 context->Global()->TurnOnAccessCheck();
9572
9573 // Failing access check to property get results in undefined.
9574 CHECK(f1->Call(global, 0, NULL)->IsUndefined());
9575 CHECK(f2->Call(global, 0, NULL)->IsUndefined());
9576
9577 // Failing access check to function call results in exception.
9578 CHECK(g1->Call(global, 0, NULL).IsEmpty());
9579 CHECK(g2->Call(global, 0, NULL).IsEmpty());
9580
9581 // No failing access check when just returning a constant.
9582 CHECK(h->Call(global, 0, NULL)->Equals(v8_num(1)));
9583}
9584
9585
Ben Murdochb0fe1622011-05-05 13:52:32 +01009586v8::Handle<v8::String> a;
9587v8::Handle<v8::String> h;
9588
9589static bool NamedGetAccessBlockAandH(Local<v8::Object> obj,
9590 Local<Value> name,
9591 v8::AccessType type,
9592 Local<Value> data) {
9593 return !(name->Equals(a) || name->Equals(h));
9594}
9595
9596
9597THREADED_TEST(TurnOnAccessCheckAndRecompile) {
9598 v8::HandleScope handle_scope;
9599
9600 // Create an environment with access check to the global object disabled by
9601 // default. When the registered access checker will block access to properties
9602 // a and h
9603 a = v8_str("a");
9604 h = v8_str("h");
9605 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
9606 global_template->SetAccessCheckCallbacks(NamedGetAccessBlockAandH,
9607 IndexedGetAccessBlocker,
9608 v8::Handle<v8::Value>(),
9609 false);
9610 v8::Persistent<Context> context = Context::New(NULL, global_template);
9611 Context::Scope context_scope(context);
9612
9613 // Set up a property and a number of functions.
9614 context->Global()->Set(v8_str("a"), v8_num(1));
9615 static const char* source = "function f1() {return a;}"
9616 "function f2() {return a;}"
9617 "function g1() {return h();}"
9618 "function g2() {return h();}"
9619 "function h() {return 1;}";
9620
9621 CompileRun(source);
9622 Local<Function> f1;
9623 Local<Function> f2;
9624 Local<Function> g1;
9625 Local<Function> g2;
9626 Local<Function> h;
9627 f1 = Local<Function>::Cast(context->Global()->Get(v8_str("f1")));
9628 f2 = Local<Function>::Cast(context->Global()->Get(v8_str("f2")));
9629 g1 = Local<Function>::Cast(context->Global()->Get(v8_str("g1")));
9630 g2 = Local<Function>::Cast(context->Global()->Get(v8_str("g2")));
9631 h = Local<Function>::Cast(context->Global()->Get(v8_str("h")));
9632
9633 // Get the global object.
9634 v8::Handle<v8::Object> global = context->Global();
9635
9636 // Call f1 one time and f2 a number of times. This will ensure that f1 still
9637 // uses the runtime system to retreive property a whereas f2 uses global load
9638 // inline cache.
9639 CHECK(f1->Call(global, 0, NULL)->Equals(v8_num(1)));
9640 for (int i = 0; i < 4; i++) {
9641 CHECK(f2->Call(global, 0, NULL)->Equals(v8_num(1)));
9642 }
9643
9644 // Same for g1 and g2.
9645 CHECK(g1->Call(global, 0, NULL)->Equals(v8_num(1)));
9646 for (int i = 0; i < 4; i++) {
9647 CHECK(g2->Call(global, 0, NULL)->Equals(v8_num(1)));
9648 }
9649
9650 // Detach the global and turn on access check now blocking access to property
9651 // a and function h.
9652 context->DetachGlobal();
9653 context->Global()->TurnOnAccessCheck();
9654
9655 // Failing access check to property get results in undefined.
9656 CHECK(f1->Call(global, 0, NULL)->IsUndefined());
9657 CHECK(f2->Call(global, 0, NULL)->IsUndefined());
9658
9659 // Failing access check to function call results in exception.
9660 CHECK(g1->Call(global, 0, NULL).IsEmpty());
9661 CHECK(g2->Call(global, 0, NULL).IsEmpty());
9662
9663 // No failing access check when just returning a constant.
9664 CHECK(h->Call(global, 0, NULL)->Equals(v8_num(1)));
9665
9666 // Now compile the source again. And get the newly compiled functions, except
9667 // for h for which access is blocked.
9668 CompileRun(source);
9669 f1 = Local<Function>::Cast(context->Global()->Get(v8_str("f1")));
9670 f2 = Local<Function>::Cast(context->Global()->Get(v8_str("f2")));
9671 g1 = Local<Function>::Cast(context->Global()->Get(v8_str("g1")));
9672 g2 = Local<Function>::Cast(context->Global()->Get(v8_str("g2")));
9673 CHECK(context->Global()->Get(v8_str("h"))->IsUndefined());
9674
9675 // Failing access check to property get results in undefined.
9676 CHECK(f1->Call(global, 0, NULL)->IsUndefined());
9677 CHECK(f2->Call(global, 0, NULL)->IsUndefined());
9678
9679 // Failing access check to function call results in exception.
9680 CHECK(g1->Call(global, 0, NULL).IsEmpty());
9681 CHECK(g2->Call(global, 0, NULL).IsEmpty());
9682}
9683
9684
Steve Blocka7e24c12009-10-30 11:49:00 +00009685// This test verifies that pre-compilation (aka preparsing) can be called
9686// without initializing the whole VM. Thus we cannot run this test in a
9687// multi-threaded setup.
9688TEST(PreCompile) {
9689 // TODO(155): This test would break without the initialization of V8. This is
9690 // a workaround for now to make this test not fail.
9691 v8::V8::Initialize();
Leon Clarkef7060e22010-06-03 12:02:55 +01009692 const char* script = "function foo(a) { return a+1; }";
9693 v8::ScriptData* sd =
Steve Blockd0582a62009-12-15 09:54:21 +00009694 v8::ScriptData::PreCompile(script, i::StrLength(script));
Steve Blocka7e24c12009-10-30 11:49:00 +00009695 CHECK_NE(sd->Length(), 0);
9696 CHECK_NE(sd->Data(), NULL);
Leon Clarkee46be812010-01-19 14:06:41 +00009697 CHECK(!sd->HasError());
9698 delete sd;
9699}
9700
9701
9702TEST(PreCompileWithError) {
9703 v8::V8::Initialize();
Leon Clarkef7060e22010-06-03 12:02:55 +01009704 const char* script = "function foo(a) { return 1 * * 2; }";
9705 v8::ScriptData* sd =
Leon Clarkee46be812010-01-19 14:06:41 +00009706 v8::ScriptData::PreCompile(script, i::StrLength(script));
9707 CHECK(sd->HasError());
9708 delete sd;
9709}
9710
9711
9712TEST(Regress31661) {
9713 v8::V8::Initialize();
Leon Clarkef7060e22010-06-03 12:02:55 +01009714 const char* script = " The Definintive Guide";
9715 v8::ScriptData* sd =
Leon Clarkee46be812010-01-19 14:06:41 +00009716 v8::ScriptData::PreCompile(script, i::StrLength(script));
9717 CHECK(sd->HasError());
Steve Blocka7e24c12009-10-30 11:49:00 +00009718 delete sd;
9719}
9720
9721
Leon Clarkef7060e22010-06-03 12:02:55 +01009722// Tests that ScriptData can be serialized and deserialized.
9723TEST(PreCompileSerialization) {
9724 v8::V8::Initialize();
9725 const char* script = "function foo(a) { return a+1; }";
9726 v8::ScriptData* sd =
9727 v8::ScriptData::PreCompile(script, i::StrLength(script));
9728
9729 // Serialize.
9730 int serialized_data_length = sd->Length();
9731 char* serialized_data = i::NewArray<char>(serialized_data_length);
9732 memcpy(serialized_data, sd->Data(), serialized_data_length);
9733
9734 // Deserialize.
9735 v8::ScriptData* deserialized_sd =
9736 v8::ScriptData::New(serialized_data, serialized_data_length);
9737
9738 // Verify that the original is the same as the deserialized.
9739 CHECK_EQ(sd->Length(), deserialized_sd->Length());
9740 CHECK_EQ(0, memcmp(sd->Data(), deserialized_sd->Data(), sd->Length()));
9741 CHECK_EQ(sd->HasError(), deserialized_sd->HasError());
9742
9743 delete sd;
9744 delete deserialized_sd;
9745}
9746
9747
9748// Attempts to deserialize bad data.
9749TEST(PreCompileDeserializationError) {
9750 v8::V8::Initialize();
9751 const char* data = "DONT CARE";
9752 int invalid_size = 3;
9753 v8::ScriptData* sd = v8::ScriptData::New(data, invalid_size);
9754
9755 CHECK_EQ(0, sd->Length());
9756
9757 delete sd;
9758}
9759
9760
Leon Clarkeac952652010-07-15 11:15:24 +01009761// Attempts to deserialize bad data.
9762TEST(PreCompileInvalidPreparseDataError) {
9763 v8::V8::Initialize();
9764 v8::HandleScope scope;
9765 LocalContext context;
9766
9767 const char* script = "function foo(){ return 5;}\n"
9768 "function bar(){ return 6 + 7;} foo();";
9769 v8::ScriptData* sd =
9770 v8::ScriptData::PreCompile(script, i::StrLength(script));
9771 CHECK(!sd->HasError());
9772 // ScriptDataImpl private implementation details
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -08009773 const int kHeaderSize = i::PreparseDataConstants::kHeaderSize;
Iain Merrick9ac36c92010-09-13 15:29:50 +01009774 const int kFunctionEntrySize = i::FunctionEntry::kSize;
Leon Clarkeac952652010-07-15 11:15:24 +01009775 const int kFunctionEntryStartOffset = 0;
9776 const int kFunctionEntryEndOffset = 1;
9777 unsigned* sd_data =
9778 reinterpret_cast<unsigned*>(const_cast<char*>(sd->Data()));
Leon Clarkeac952652010-07-15 11:15:24 +01009779
9780 // Overwrite function bar's end position with 0.
9781 sd_data[kHeaderSize + 1 * kFunctionEntrySize + kFunctionEntryEndOffset] = 0;
9782 v8::TryCatch try_catch;
9783
9784 Local<String> source = String::New(script);
9785 Local<Script> compiled_script = Script::New(source, NULL, sd);
9786 CHECK(try_catch.HasCaught());
9787 String::AsciiValue exception_value(try_catch.Message()->Get());
9788 CHECK_EQ("Uncaught SyntaxError: Invalid preparser data for function bar",
9789 *exception_value);
9790
9791 try_catch.Reset();
9792 // Overwrite function bar's start position with 200. The function entry
9793 // will not be found when searching for it by position.
Kristian Monsen80d68ea2010-09-08 11:05:35 +01009794 sd = v8::ScriptData::PreCompile(script, i::StrLength(script));
9795 sd_data = reinterpret_cast<unsigned*>(const_cast<char*>(sd->Data()));
Leon Clarkeac952652010-07-15 11:15:24 +01009796 sd_data[kHeaderSize + 1 * kFunctionEntrySize + kFunctionEntryStartOffset] =
9797 200;
9798 compiled_script = Script::New(source, NULL, sd);
9799 CHECK(try_catch.HasCaught());
9800 String::AsciiValue second_exception_value(try_catch.Message()->Get());
9801 CHECK_EQ("Uncaught SyntaxError: Invalid preparser data for function bar",
9802 *second_exception_value);
9803
9804 delete sd;
9805}
9806
9807
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01009808// Verifies that the Handle<String> and const char* versions of the API produce
9809// the same results (at least for one trivial case).
9810TEST(PreCompileAPIVariationsAreSame) {
9811 v8::V8::Initialize();
9812 v8::HandleScope scope;
9813
9814 const char* cstring = "function foo(a) { return a+1; }";
Ben Murdoch3bec4d22010-07-22 14:51:16 +01009815
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01009816 v8::ScriptData* sd_from_cstring =
9817 v8::ScriptData::PreCompile(cstring, i::StrLength(cstring));
9818
9819 TestAsciiResource* resource = new TestAsciiResource(cstring);
Ben Murdoch3bec4d22010-07-22 14:51:16 +01009820 v8::ScriptData* sd_from_external_string = v8::ScriptData::PreCompile(
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01009821 v8::String::NewExternal(resource));
9822
Ben Murdoch3bec4d22010-07-22 14:51:16 +01009823 v8::ScriptData* sd_from_string = v8::ScriptData::PreCompile(
9824 v8::String::New(cstring));
9825
9826 CHECK_EQ(sd_from_cstring->Length(), sd_from_external_string->Length());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01009827 CHECK_EQ(0, memcmp(sd_from_cstring->Data(),
Ben Murdoch3bec4d22010-07-22 14:51:16 +01009828 sd_from_external_string->Data(),
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01009829 sd_from_cstring->Length()));
9830
Ben Murdoch3bec4d22010-07-22 14:51:16 +01009831 CHECK_EQ(sd_from_cstring->Length(), sd_from_string->Length());
9832 CHECK_EQ(0, memcmp(sd_from_cstring->Data(),
9833 sd_from_string->Data(),
9834 sd_from_cstring->Length()));
9835
9836
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01009837 delete sd_from_cstring;
Ben Murdoch3bec4d22010-07-22 14:51:16 +01009838 delete sd_from_external_string;
9839 delete sd_from_string;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01009840}
9841
9842
Steve Blocka7e24c12009-10-30 11:49:00 +00009843// This tests that we do not allow dictionary load/call inline caches
9844// to use functions that have not yet been compiled. The potential
9845// problem of loading a function that has not yet been compiled can
9846// arise because we share code between contexts via the compilation
9847// cache.
9848THREADED_TEST(DictionaryICLoadedFunction) {
9849 v8::HandleScope scope;
9850 // Test LoadIC.
9851 for (int i = 0; i < 2; i++) {
9852 LocalContext context;
9853 context->Global()->Set(v8_str("tmp"), v8::True());
9854 context->Global()->Delete(v8_str("tmp"));
9855 CompileRun("for (var j = 0; j < 10; j++) new RegExp('');");
9856 }
9857 // Test CallIC.
9858 for (int i = 0; i < 2; i++) {
9859 LocalContext context;
9860 context->Global()->Set(v8_str("tmp"), v8::True());
9861 context->Global()->Delete(v8_str("tmp"));
9862 CompileRun("for (var j = 0; j < 10; j++) RegExp('')");
9863 }
9864}
9865
9866
9867// Test that cross-context new calls use the context of the callee to
9868// create the new JavaScript object.
9869THREADED_TEST(CrossContextNew) {
9870 v8::HandleScope scope;
9871 v8::Persistent<Context> context0 = Context::New();
9872 v8::Persistent<Context> context1 = Context::New();
9873
9874 // Allow cross-domain access.
9875 Local<String> token = v8_str("<security token>");
9876 context0->SetSecurityToken(token);
9877 context1->SetSecurityToken(token);
9878
9879 // Set an 'x' property on the Object prototype and define a
9880 // constructor function in context0.
9881 context0->Enter();
9882 CompileRun("Object.prototype.x = 42; function C() {};");
9883 context0->Exit();
9884
9885 // Call the constructor function from context0 and check that the
9886 // result has the 'x' property.
9887 context1->Enter();
9888 context1->Global()->Set(v8_str("other"), context0->Global());
9889 Local<Value> value = CompileRun("var instance = new other.C(); instance.x");
9890 CHECK(value->IsInt32());
9891 CHECK_EQ(42, value->Int32Value());
9892 context1->Exit();
9893
9894 // Dispose the contexts to allow them to be garbage collected.
9895 context0.Dispose();
9896 context1.Dispose();
9897}
9898
9899
9900class RegExpInterruptTest {
9901 public:
9902 RegExpInterruptTest() : block_(NULL) {}
9903 ~RegExpInterruptTest() { delete block_; }
9904 void RunTest() {
9905 block_ = i::OS::CreateSemaphore(0);
9906 gc_count_ = 0;
9907 gc_during_regexp_ = 0;
9908 regexp_success_ = false;
9909 gc_success_ = false;
Steve Block44f0eee2011-05-26 01:26:41 +01009910 GCThread gc_thread(i::Isolate::Current(), this);
Steve Blocka7e24c12009-10-30 11:49:00 +00009911 gc_thread.Start();
9912 v8::Locker::StartPreemption(1);
9913
9914 LongRunningRegExp();
9915 {
9916 v8::Unlocker unlock;
9917 gc_thread.Join();
9918 }
9919 v8::Locker::StopPreemption();
9920 CHECK(regexp_success_);
9921 CHECK(gc_success_);
9922 }
9923 private:
9924 // Number of garbage collections required.
9925 static const int kRequiredGCs = 5;
9926
9927 class GCThread : public i::Thread {
9928 public:
Steve Block44f0eee2011-05-26 01:26:41 +01009929 explicit GCThread(i::Isolate* isolate, RegExpInterruptTest* test)
9930 : Thread(isolate, "GCThread"), test_(test) {}
Steve Blocka7e24c12009-10-30 11:49:00 +00009931 virtual void Run() {
9932 test_->CollectGarbage();
9933 }
9934 private:
9935 RegExpInterruptTest* test_;
9936 };
9937
9938 void CollectGarbage() {
9939 block_->Wait();
9940 while (gc_during_regexp_ < kRequiredGCs) {
9941 {
9942 v8::Locker lock;
9943 // TODO(lrn): Perhaps create some garbage before collecting.
Steve Block44f0eee2011-05-26 01:26:41 +01009944 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +00009945 gc_count_++;
9946 }
9947 i::OS::Sleep(1);
9948 }
9949 gc_success_ = true;
9950 }
9951
9952 void LongRunningRegExp() {
9953 block_->Signal(); // Enable garbage collection thread on next preemption.
9954 int rounds = 0;
9955 while (gc_during_regexp_ < kRequiredGCs) {
9956 int gc_before = gc_count_;
9957 {
9958 // Match 15-30 "a"'s against 14 and a "b".
9959 const char* c_source =
9960 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
9961 ".exec('aaaaaaaaaaaaaaab') === null";
9962 Local<String> source = String::New(c_source);
9963 Local<Script> script = Script::Compile(source);
9964 Local<Value> result = script->Run();
9965 if (!result->BooleanValue()) {
9966 gc_during_regexp_ = kRequiredGCs; // Allow gc thread to exit.
9967 return;
9968 }
9969 }
9970 {
9971 // Match 15-30 "a"'s against 15 and a "b".
9972 const char* c_source =
9973 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
9974 ".exec('aaaaaaaaaaaaaaaab')[0] === 'aaaaaaaaaaaaaaaa'";
9975 Local<String> source = String::New(c_source);
9976 Local<Script> script = Script::Compile(source);
9977 Local<Value> result = script->Run();
9978 if (!result->BooleanValue()) {
9979 gc_during_regexp_ = kRequiredGCs;
9980 return;
9981 }
9982 }
9983 int gc_after = gc_count_;
9984 gc_during_regexp_ += gc_after - gc_before;
9985 rounds++;
9986 i::OS::Sleep(1);
9987 }
9988 regexp_success_ = true;
9989 }
9990
9991 i::Semaphore* block_;
9992 int gc_count_;
9993 int gc_during_regexp_;
9994 bool regexp_success_;
9995 bool gc_success_;
9996};
9997
9998
9999// Test that a regular expression execution can be interrupted and
10000// survive a garbage collection.
10001TEST(RegExpInterruption) {
10002 v8::Locker lock;
10003 v8::V8::Initialize();
10004 v8::HandleScope scope;
10005 Local<Context> local_env;
10006 {
10007 LocalContext env;
10008 local_env = env.local();
10009 }
10010
10011 // Local context should still be live.
10012 CHECK(!local_env.IsEmpty());
10013 local_env->Enter();
10014
10015 // Should complete without problems.
10016 RegExpInterruptTest().RunTest();
10017
10018 local_env->Exit();
10019}
10020
10021
10022class ApplyInterruptTest {
10023 public:
10024 ApplyInterruptTest() : block_(NULL) {}
10025 ~ApplyInterruptTest() { delete block_; }
10026 void RunTest() {
10027 block_ = i::OS::CreateSemaphore(0);
10028 gc_count_ = 0;
10029 gc_during_apply_ = 0;
10030 apply_success_ = false;
10031 gc_success_ = false;
Steve Block44f0eee2011-05-26 01:26:41 +010010032 GCThread gc_thread(i::Isolate::Current(), this);
Steve Blocka7e24c12009-10-30 11:49:00 +000010033 gc_thread.Start();
10034 v8::Locker::StartPreemption(1);
10035
10036 LongRunningApply();
10037 {
10038 v8::Unlocker unlock;
10039 gc_thread.Join();
10040 }
10041 v8::Locker::StopPreemption();
10042 CHECK(apply_success_);
10043 CHECK(gc_success_);
10044 }
10045 private:
10046 // Number of garbage collections required.
10047 static const int kRequiredGCs = 2;
10048
10049 class GCThread : public i::Thread {
10050 public:
Steve Block44f0eee2011-05-26 01:26:41 +010010051 explicit GCThread(i::Isolate* isolate, ApplyInterruptTest* test)
10052 : Thread(isolate, "GCThread"), test_(test) {}
Steve Blocka7e24c12009-10-30 11:49:00 +000010053 virtual void Run() {
10054 test_->CollectGarbage();
10055 }
10056 private:
10057 ApplyInterruptTest* test_;
10058 };
10059
10060 void CollectGarbage() {
10061 block_->Wait();
10062 while (gc_during_apply_ < kRequiredGCs) {
10063 {
10064 v8::Locker lock;
Steve Block44f0eee2011-05-26 01:26:41 +010010065 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +000010066 gc_count_++;
10067 }
10068 i::OS::Sleep(1);
10069 }
10070 gc_success_ = true;
10071 }
10072
10073 void LongRunningApply() {
10074 block_->Signal();
10075 int rounds = 0;
10076 while (gc_during_apply_ < kRequiredGCs) {
10077 int gc_before = gc_count_;
10078 {
10079 const char* c_source =
10080 "function do_very_little(bar) {"
10081 " this.foo = bar;"
10082 "}"
10083 "for (var i = 0; i < 100000; i++) {"
10084 " do_very_little.apply(this, ['bar']);"
10085 "}";
10086 Local<String> source = String::New(c_source);
10087 Local<Script> script = Script::Compile(source);
10088 Local<Value> result = script->Run();
10089 // Check that no exception was thrown.
10090 CHECK(!result.IsEmpty());
10091 }
10092 int gc_after = gc_count_;
10093 gc_during_apply_ += gc_after - gc_before;
10094 rounds++;
10095 }
10096 apply_success_ = true;
10097 }
10098
10099 i::Semaphore* block_;
10100 int gc_count_;
10101 int gc_during_apply_;
10102 bool apply_success_;
10103 bool gc_success_;
10104};
10105
10106
10107// Test that nothing bad happens if we get a preemption just when we were
10108// about to do an apply().
10109TEST(ApplyInterruption) {
10110 v8::Locker lock;
10111 v8::V8::Initialize();
10112 v8::HandleScope scope;
10113 Local<Context> local_env;
10114 {
10115 LocalContext env;
10116 local_env = env.local();
10117 }
10118
10119 // Local context should still be live.
10120 CHECK(!local_env.IsEmpty());
10121 local_env->Enter();
10122
10123 // Should complete without problems.
10124 ApplyInterruptTest().RunTest();
10125
10126 local_env->Exit();
10127}
10128
10129
10130// Verify that we can clone an object
10131TEST(ObjectClone) {
10132 v8::HandleScope scope;
10133 LocalContext env;
10134
10135 const char* sample =
10136 "var rv = {};" \
10137 "rv.alpha = 'hello';" \
10138 "rv.beta = 123;" \
10139 "rv;";
10140
10141 // Create an object, verify basics.
10142 Local<Value> val = CompileRun(sample);
10143 CHECK(val->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +010010144 Local<v8::Object> obj = val.As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +000010145 obj->Set(v8_str("gamma"), v8_str("cloneme"));
10146
10147 CHECK_EQ(v8_str("hello"), obj->Get(v8_str("alpha")));
10148 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta")));
10149 CHECK_EQ(v8_str("cloneme"), obj->Get(v8_str("gamma")));
10150
10151 // Clone it.
10152 Local<v8::Object> clone = obj->Clone();
10153 CHECK_EQ(v8_str("hello"), clone->Get(v8_str("alpha")));
10154 CHECK_EQ(v8::Integer::New(123), clone->Get(v8_str("beta")));
10155 CHECK_EQ(v8_str("cloneme"), clone->Get(v8_str("gamma")));
10156
10157 // Set a property on the clone, verify each object.
10158 clone->Set(v8_str("beta"), v8::Integer::New(456));
10159 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta")));
10160 CHECK_EQ(v8::Integer::New(456), clone->Get(v8_str("beta")));
10161}
10162
10163
10164class AsciiVectorResource : public v8::String::ExternalAsciiStringResource {
10165 public:
10166 explicit AsciiVectorResource(i::Vector<const char> vector)
10167 : data_(vector) {}
10168 virtual ~AsciiVectorResource() {}
10169 virtual size_t length() const { return data_.length(); }
10170 virtual const char* data() const { return data_.start(); }
10171 private:
10172 i::Vector<const char> data_;
10173};
10174
10175
10176class UC16VectorResource : public v8::String::ExternalStringResource {
10177 public:
10178 explicit UC16VectorResource(i::Vector<const i::uc16> vector)
10179 : data_(vector) {}
10180 virtual ~UC16VectorResource() {}
10181 virtual size_t length() const { return data_.length(); }
10182 virtual const i::uc16* data() const { return data_.start(); }
10183 private:
10184 i::Vector<const i::uc16> data_;
10185};
10186
10187
10188static void MorphAString(i::String* string,
10189 AsciiVectorResource* ascii_resource,
10190 UC16VectorResource* uc16_resource) {
10191 CHECK(i::StringShape(string).IsExternal());
10192 if (string->IsAsciiRepresentation()) {
10193 // Check old map is not symbol or long.
Steve Block44f0eee2011-05-26 01:26:41 +010010194 CHECK(string->map() == HEAP->external_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +000010195 // Morph external string to be TwoByte string.
Steve Block44f0eee2011-05-26 01:26:41 +010010196 string->set_map(HEAP->external_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +000010197 i::ExternalTwoByteString* morphed =
10198 i::ExternalTwoByteString::cast(string);
10199 morphed->set_resource(uc16_resource);
10200 } else {
10201 // Check old map is not symbol or long.
Steve Block44f0eee2011-05-26 01:26:41 +010010202 CHECK(string->map() == HEAP->external_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +000010203 // Morph external string to be ASCII string.
Steve Block44f0eee2011-05-26 01:26:41 +010010204 string->set_map(HEAP->external_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +000010205 i::ExternalAsciiString* morphed =
10206 i::ExternalAsciiString::cast(string);
10207 morphed->set_resource(ascii_resource);
10208 }
10209}
10210
10211
10212// Test that we can still flatten a string if the components it is built up
10213// from have been turned into 16 bit strings in the mean time.
10214THREADED_TEST(MorphCompositeStringTest) {
10215 const char* c_string = "Now is the time for all good men"
10216 " to come to the aid of the party";
10217 uint16_t* two_byte_string = AsciiToTwoByteString(c_string);
10218 {
10219 v8::HandleScope scope;
10220 LocalContext env;
10221 AsciiVectorResource ascii_resource(
Steve Blockd0582a62009-12-15 09:54:21 +000010222 i::Vector<const char>(c_string, i::StrLength(c_string)));
Steve Blocka7e24c12009-10-30 11:49:00 +000010223 UC16VectorResource uc16_resource(
Steve Blockd0582a62009-12-15 09:54:21 +000010224 i::Vector<const uint16_t>(two_byte_string,
10225 i::StrLength(c_string)));
Steve Blocka7e24c12009-10-30 11:49:00 +000010226
10227 Local<String> lhs(v8::Utils::ToLocal(
Steve Block44f0eee2011-05-26 01:26:41 +010010228 FACTORY->NewExternalStringFromAscii(&ascii_resource)));
Steve Blocka7e24c12009-10-30 11:49:00 +000010229 Local<String> rhs(v8::Utils::ToLocal(
Steve Block44f0eee2011-05-26 01:26:41 +010010230 FACTORY->NewExternalStringFromAscii(&ascii_resource)));
Steve Blocka7e24c12009-10-30 11:49:00 +000010231
10232 env->Global()->Set(v8_str("lhs"), lhs);
10233 env->Global()->Set(v8_str("rhs"), rhs);
10234
10235 CompileRun(
10236 "var cons = lhs + rhs;"
10237 "var slice = lhs.substring(1, lhs.length - 1);"
10238 "var slice_on_cons = (lhs + rhs).substring(1, lhs.length *2 - 1);");
10239
10240 MorphAString(*v8::Utils::OpenHandle(*lhs), &ascii_resource, &uc16_resource);
10241 MorphAString(*v8::Utils::OpenHandle(*rhs), &ascii_resource, &uc16_resource);
10242
10243 // Now do some stuff to make sure the strings are flattened, etc.
10244 CompileRun(
10245 "/[^a-z]/.test(cons);"
10246 "/[^a-z]/.test(slice);"
10247 "/[^a-z]/.test(slice_on_cons);");
10248 const char* expected_cons =
10249 "Now is the time for all good men to come to the aid of the party"
10250 "Now is the time for all good men to come to the aid of the party";
10251 const char* expected_slice =
10252 "ow is the time for all good men to come to the aid of the part";
10253 const char* expected_slice_on_cons =
10254 "ow is the time for all good men to come to the aid of the party"
10255 "Now is the time for all good men to come to the aid of the part";
10256 CHECK_EQ(String::New(expected_cons),
10257 env->Global()->Get(v8_str("cons")));
10258 CHECK_EQ(String::New(expected_slice),
10259 env->Global()->Get(v8_str("slice")));
10260 CHECK_EQ(String::New(expected_slice_on_cons),
10261 env->Global()->Get(v8_str("slice_on_cons")));
10262 }
Ben Murdoch3bec4d22010-07-22 14:51:16 +010010263 i::DeleteArray(two_byte_string);
Steve Blocka7e24c12009-10-30 11:49:00 +000010264}
10265
10266
10267TEST(CompileExternalTwoByteSource) {
10268 v8::HandleScope scope;
10269 LocalContext context;
10270
10271 // This is a very short list of sources, which currently is to check for a
10272 // regression caused by r2703.
10273 const char* ascii_sources[] = {
10274 "0.5",
10275 "-0.5", // This mainly testes PushBack in the Scanner.
10276 "--0.5", // This mainly testes PushBack in the Scanner.
10277 NULL
10278 };
10279
10280 // Compile the sources as external two byte strings.
10281 for (int i = 0; ascii_sources[i] != NULL; i++) {
10282 uint16_t* two_byte_string = AsciiToTwoByteString(ascii_sources[i]);
10283 UC16VectorResource uc16_resource(
Steve Blockd0582a62009-12-15 09:54:21 +000010284 i::Vector<const uint16_t>(two_byte_string,
10285 i::StrLength(ascii_sources[i])));
Steve Blocka7e24c12009-10-30 11:49:00 +000010286 v8::Local<v8::String> source = v8::String::NewExternal(&uc16_resource);
10287 v8::Script::Compile(source);
Ben Murdoch3bec4d22010-07-22 14:51:16 +010010288 i::DeleteArray(two_byte_string);
Steve Blocka7e24c12009-10-30 11:49:00 +000010289 }
10290}
10291
10292
10293class RegExpStringModificationTest {
10294 public:
10295 RegExpStringModificationTest()
10296 : block_(i::OS::CreateSemaphore(0)),
10297 morphs_(0),
10298 morphs_during_regexp_(0),
10299 ascii_resource_(i::Vector<const char>("aaaaaaaaaaaaaab", 15)),
10300 uc16_resource_(i::Vector<const uint16_t>(two_byte_content_, 15)) {}
10301 ~RegExpStringModificationTest() { delete block_; }
10302 void RunTest() {
10303 regexp_success_ = false;
10304 morph_success_ = false;
10305
10306 // Initialize the contents of two_byte_content_ to be a uc16 representation
10307 // of "aaaaaaaaaaaaaab".
10308 for (int i = 0; i < 14; i++) {
10309 two_byte_content_[i] = 'a';
10310 }
10311 two_byte_content_[14] = 'b';
10312
10313 // Create the input string for the regexp - the one we are going to change
10314 // properties of.
Steve Block44f0eee2011-05-26 01:26:41 +010010315 input_ = FACTORY->NewExternalStringFromAscii(&ascii_resource_);
Steve Blocka7e24c12009-10-30 11:49:00 +000010316
10317 // Inject the input as a global variable.
10318 i::Handle<i::String> input_name =
Steve Block44f0eee2011-05-26 01:26:41 +010010319 FACTORY->NewStringFromAscii(i::Vector<const char>("input", 5));
10320 i::Isolate::Current()->global_context()->global()->SetProperty(
Ben Murdoche0cee9b2011-05-25 10:26:03 +010010321 *input_name,
10322 *input_,
10323 NONE,
10324 i::kNonStrictMode)->ToObjectChecked();
Steve Blocka7e24c12009-10-30 11:49:00 +000010325
Steve Block44f0eee2011-05-26 01:26:41 +010010326 MorphThread morph_thread(i::Isolate::Current(), this);
Steve Blocka7e24c12009-10-30 11:49:00 +000010327 morph_thread.Start();
10328 v8::Locker::StartPreemption(1);
10329 LongRunningRegExp();
10330 {
10331 v8::Unlocker unlock;
10332 morph_thread.Join();
10333 }
10334 v8::Locker::StopPreemption();
10335 CHECK(regexp_success_);
10336 CHECK(morph_success_);
10337 }
10338 private:
10339
10340 // Number of string modifications required.
10341 static const int kRequiredModifications = 5;
10342 static const int kMaxModifications = 100;
10343
10344 class MorphThread : public i::Thread {
10345 public:
Steve Block44f0eee2011-05-26 01:26:41 +010010346 explicit MorphThread(i::Isolate* isolate,
10347 RegExpStringModificationTest* test)
10348 : Thread(isolate, "MorphThread"), test_(test) {}
Steve Blocka7e24c12009-10-30 11:49:00 +000010349 virtual void Run() {
10350 test_->MorphString();
10351 }
10352 private:
10353 RegExpStringModificationTest* test_;
10354 };
10355
10356 void MorphString() {
10357 block_->Wait();
10358 while (morphs_during_regexp_ < kRequiredModifications &&
10359 morphs_ < kMaxModifications) {
10360 {
10361 v8::Locker lock;
10362 // Swap string between ascii and two-byte representation.
10363 i::String* string = *input_;
10364 MorphAString(string, &ascii_resource_, &uc16_resource_);
10365 morphs_++;
10366 }
10367 i::OS::Sleep(1);
10368 }
10369 morph_success_ = true;
10370 }
10371
10372 void LongRunningRegExp() {
10373 block_->Signal(); // Enable morphing thread on next preemption.
10374 while (morphs_during_regexp_ < kRequiredModifications &&
10375 morphs_ < kMaxModifications) {
10376 int morphs_before = morphs_;
10377 {
Steve Block791712a2010-08-27 10:21:07 +010010378 v8::HandleScope scope;
Steve Blocka7e24c12009-10-30 11:49:00 +000010379 // Match 15-30 "a"'s against 14 and a "b".
10380 const char* c_source =
10381 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
10382 ".exec(input) === null";
10383 Local<String> source = String::New(c_source);
10384 Local<Script> script = Script::Compile(source);
10385 Local<Value> result = script->Run();
10386 CHECK(result->IsTrue());
10387 }
10388 int morphs_after = morphs_;
10389 morphs_during_regexp_ += morphs_after - morphs_before;
10390 }
10391 regexp_success_ = true;
10392 }
10393
10394 i::uc16 two_byte_content_[15];
10395 i::Semaphore* block_;
10396 int morphs_;
10397 int morphs_during_regexp_;
10398 bool regexp_success_;
10399 bool morph_success_;
10400 i::Handle<i::String> input_;
10401 AsciiVectorResource ascii_resource_;
10402 UC16VectorResource uc16_resource_;
10403};
10404
10405
10406// Test that a regular expression execution can be interrupted and
10407// the string changed without failing.
10408TEST(RegExpStringModification) {
10409 v8::Locker lock;
10410 v8::V8::Initialize();
10411 v8::HandleScope scope;
10412 Local<Context> local_env;
10413 {
10414 LocalContext env;
10415 local_env = env.local();
10416 }
10417
10418 // Local context should still be live.
10419 CHECK(!local_env.IsEmpty());
10420 local_env->Enter();
10421
10422 // Should complete without problems.
10423 RegExpStringModificationTest().RunTest();
10424
10425 local_env->Exit();
10426}
10427
10428
10429// Test that we can set a property on the global object even if there
10430// is a read-only property in the prototype chain.
10431TEST(ReadOnlyPropertyInGlobalProto) {
10432 v8::HandleScope scope;
10433 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
10434 LocalContext context(0, templ);
10435 v8::Handle<v8::Object> global = context->Global();
10436 v8::Handle<v8::Object> global_proto =
10437 v8::Handle<v8::Object>::Cast(global->Get(v8_str("__proto__")));
10438 global_proto->Set(v8_str("x"), v8::Integer::New(0), v8::ReadOnly);
10439 global_proto->Set(v8_str("y"), v8::Integer::New(0), v8::ReadOnly);
10440 // Check without 'eval' or 'with'.
10441 v8::Handle<v8::Value> res =
10442 CompileRun("function f() { x = 42; return x; }; f()");
10443 // Check with 'eval'.
10444 res = CompileRun("function f() { eval('1'); y = 42; return y; }; f()");
10445 CHECK_EQ(v8::Integer::New(42), res);
10446 // Check with 'with'.
10447 res = CompileRun("function f() { with (this) { y = 42 }; return y; }; f()");
10448 CHECK_EQ(v8::Integer::New(42), res);
10449}
10450
10451static int force_set_set_count = 0;
10452static int force_set_get_count = 0;
10453bool pass_on_get = false;
10454
10455static v8::Handle<v8::Value> ForceSetGetter(v8::Local<v8::String> name,
10456 const v8::AccessorInfo& info) {
10457 force_set_get_count++;
10458 if (pass_on_get) {
10459 return v8::Handle<v8::Value>();
10460 } else {
10461 return v8::Int32::New(3);
10462 }
10463}
10464
10465static void ForceSetSetter(v8::Local<v8::String> name,
10466 v8::Local<v8::Value> value,
10467 const v8::AccessorInfo& info) {
10468 force_set_set_count++;
10469}
10470
10471static v8::Handle<v8::Value> ForceSetInterceptSetter(
10472 v8::Local<v8::String> name,
10473 v8::Local<v8::Value> value,
10474 const v8::AccessorInfo& info) {
10475 force_set_set_count++;
10476 return v8::Undefined();
10477}
10478
10479TEST(ForceSet) {
10480 force_set_get_count = 0;
10481 force_set_set_count = 0;
10482 pass_on_get = false;
10483
10484 v8::HandleScope scope;
10485 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
10486 v8::Handle<v8::String> access_property = v8::String::New("a");
10487 templ->SetAccessor(access_property, ForceSetGetter, ForceSetSetter);
10488 LocalContext context(NULL, templ);
10489 v8::Handle<v8::Object> global = context->Global();
10490
10491 // Ordinary properties
10492 v8::Handle<v8::String> simple_property = v8::String::New("p");
10493 global->Set(simple_property, v8::Int32::New(4), v8::ReadOnly);
10494 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
10495 // This should fail because the property is read-only
10496 global->Set(simple_property, v8::Int32::New(5));
10497 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
10498 // This should succeed even though the property is read-only
10499 global->ForceSet(simple_property, v8::Int32::New(6));
10500 CHECK_EQ(6, global->Get(simple_property)->Int32Value());
10501
10502 // Accessors
10503 CHECK_EQ(0, force_set_set_count);
10504 CHECK_EQ(0, force_set_get_count);
10505 CHECK_EQ(3, global->Get(access_property)->Int32Value());
10506 // CHECK_EQ the property shouldn't override it, just call the setter
10507 // which in this case does nothing.
10508 global->Set(access_property, v8::Int32::New(7));
10509 CHECK_EQ(3, global->Get(access_property)->Int32Value());
10510 CHECK_EQ(1, force_set_set_count);
10511 CHECK_EQ(2, force_set_get_count);
10512 // Forcing the property to be set should override the accessor without
10513 // calling it
10514 global->ForceSet(access_property, v8::Int32::New(8));
10515 CHECK_EQ(8, global->Get(access_property)->Int32Value());
10516 CHECK_EQ(1, force_set_set_count);
10517 CHECK_EQ(2, force_set_get_count);
10518}
10519
10520TEST(ForceSetWithInterceptor) {
10521 force_set_get_count = 0;
10522 force_set_set_count = 0;
10523 pass_on_get = false;
10524
10525 v8::HandleScope scope;
10526 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
10527 templ->SetNamedPropertyHandler(ForceSetGetter, ForceSetInterceptSetter);
10528 LocalContext context(NULL, templ);
10529 v8::Handle<v8::Object> global = context->Global();
10530
10531 v8::Handle<v8::String> some_property = v8::String::New("a");
10532 CHECK_EQ(0, force_set_set_count);
10533 CHECK_EQ(0, force_set_get_count);
10534 CHECK_EQ(3, global->Get(some_property)->Int32Value());
10535 // Setting the property shouldn't override it, just call the setter
10536 // which in this case does nothing.
10537 global->Set(some_property, v8::Int32::New(7));
10538 CHECK_EQ(3, global->Get(some_property)->Int32Value());
10539 CHECK_EQ(1, force_set_set_count);
10540 CHECK_EQ(2, force_set_get_count);
10541 // Getting the property when the interceptor returns an empty handle
10542 // should yield undefined, since the property isn't present on the
10543 // object itself yet.
10544 pass_on_get = true;
10545 CHECK(global->Get(some_property)->IsUndefined());
10546 CHECK_EQ(1, force_set_set_count);
10547 CHECK_EQ(3, force_set_get_count);
10548 // Forcing the property to be set should cause the value to be
10549 // set locally without calling the interceptor.
10550 global->ForceSet(some_property, v8::Int32::New(8));
10551 CHECK_EQ(8, global->Get(some_property)->Int32Value());
10552 CHECK_EQ(1, force_set_set_count);
10553 CHECK_EQ(4, force_set_get_count);
10554 // Reenabling the interceptor should cause it to take precedence over
10555 // the property
10556 pass_on_get = false;
10557 CHECK_EQ(3, global->Get(some_property)->Int32Value());
10558 CHECK_EQ(1, force_set_set_count);
10559 CHECK_EQ(5, force_set_get_count);
10560 // The interceptor should also work for other properties
10561 CHECK_EQ(3, global->Get(v8::String::New("b"))->Int32Value());
10562 CHECK_EQ(1, force_set_set_count);
10563 CHECK_EQ(6, force_set_get_count);
10564}
10565
10566
10567THREADED_TEST(ForceDelete) {
10568 v8::HandleScope scope;
10569 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
10570 LocalContext context(NULL, templ);
10571 v8::Handle<v8::Object> global = context->Global();
10572
10573 // Ordinary properties
10574 v8::Handle<v8::String> simple_property = v8::String::New("p");
10575 global->Set(simple_property, v8::Int32::New(4), v8::DontDelete);
10576 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
10577 // This should fail because the property is dont-delete.
10578 CHECK(!global->Delete(simple_property));
10579 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
10580 // This should succeed even though the property is dont-delete.
10581 CHECK(global->ForceDelete(simple_property));
10582 CHECK(global->Get(simple_property)->IsUndefined());
10583}
10584
10585
10586static int force_delete_interceptor_count = 0;
10587static bool pass_on_delete = false;
10588
10589
10590static v8::Handle<v8::Boolean> ForceDeleteDeleter(
10591 v8::Local<v8::String> name,
10592 const v8::AccessorInfo& info) {
10593 force_delete_interceptor_count++;
10594 if (pass_on_delete) {
10595 return v8::Handle<v8::Boolean>();
10596 } else {
10597 return v8::True();
10598 }
10599}
10600
10601
10602THREADED_TEST(ForceDeleteWithInterceptor) {
10603 force_delete_interceptor_count = 0;
10604 pass_on_delete = false;
10605
10606 v8::HandleScope scope;
10607 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
10608 templ->SetNamedPropertyHandler(0, 0, 0, ForceDeleteDeleter);
10609 LocalContext context(NULL, templ);
10610 v8::Handle<v8::Object> global = context->Global();
10611
10612 v8::Handle<v8::String> some_property = v8::String::New("a");
10613 global->Set(some_property, v8::Integer::New(42), v8::DontDelete);
10614
10615 // Deleting a property should get intercepted and nothing should
10616 // happen.
10617 CHECK_EQ(0, force_delete_interceptor_count);
10618 CHECK(global->Delete(some_property));
10619 CHECK_EQ(1, force_delete_interceptor_count);
10620 CHECK_EQ(42, global->Get(some_property)->Int32Value());
10621 // Deleting the property when the interceptor returns an empty
10622 // handle should not delete the property since it is DontDelete.
10623 pass_on_delete = true;
10624 CHECK(!global->Delete(some_property));
10625 CHECK_EQ(2, force_delete_interceptor_count);
10626 CHECK_EQ(42, global->Get(some_property)->Int32Value());
10627 // Forcing the property to be deleted should delete the value
10628 // without calling the interceptor.
10629 CHECK(global->ForceDelete(some_property));
10630 CHECK(global->Get(some_property)->IsUndefined());
10631 CHECK_EQ(2, force_delete_interceptor_count);
10632}
10633
10634
10635// Make sure that forcing a delete invalidates any IC stubs, so we
10636// don't read the hole value.
10637THREADED_TEST(ForceDeleteIC) {
10638 v8::HandleScope scope;
10639 LocalContext context;
10640 // Create a DontDelete variable on the global object.
10641 CompileRun("this.__proto__ = { foo: 'horse' };"
10642 "var foo = 'fish';"
10643 "function f() { return foo.length; }");
10644 // Initialize the IC for foo in f.
10645 CompileRun("for (var i = 0; i < 4; i++) f();");
10646 // Make sure the value of foo is correct before the deletion.
10647 CHECK_EQ(4, CompileRun("f()")->Int32Value());
10648 // Force the deletion of foo.
10649 CHECK(context->Global()->ForceDelete(v8_str("foo")));
10650 // Make sure the value for foo is read from the prototype, and that
10651 // we don't get in trouble with reading the deleted cell value
10652 // sentinel.
10653 CHECK_EQ(5, CompileRun("f()")->Int32Value());
10654}
10655
10656
10657v8::Persistent<Context> calling_context0;
10658v8::Persistent<Context> calling_context1;
10659v8::Persistent<Context> calling_context2;
10660
10661
10662// Check that the call to the callback is initiated in
10663// calling_context2, the directly calling context is calling_context1
10664// and the callback itself is in calling_context0.
10665static v8::Handle<Value> GetCallingContextCallback(const v8::Arguments& args) {
10666 ApiTestFuzzer::Fuzz();
10667 CHECK(Context::GetCurrent() == calling_context0);
10668 CHECK(Context::GetCalling() == calling_context1);
10669 CHECK(Context::GetEntered() == calling_context2);
10670 return v8::Integer::New(42);
10671}
10672
10673
10674THREADED_TEST(GetCallingContext) {
10675 v8::HandleScope scope;
10676
10677 calling_context0 = Context::New();
10678 calling_context1 = Context::New();
10679 calling_context2 = Context::New();
10680
10681 // Allow cross-domain access.
10682 Local<String> token = v8_str("<security token>");
10683 calling_context0->SetSecurityToken(token);
10684 calling_context1->SetSecurityToken(token);
10685 calling_context2->SetSecurityToken(token);
10686
10687 // Create an object with a C++ callback in context0.
10688 calling_context0->Enter();
10689 Local<v8::FunctionTemplate> callback_templ =
10690 v8::FunctionTemplate::New(GetCallingContextCallback);
10691 calling_context0->Global()->Set(v8_str("callback"),
10692 callback_templ->GetFunction());
10693 calling_context0->Exit();
10694
10695 // Expose context0 in context1 and setup a function that calls the
10696 // callback function.
10697 calling_context1->Enter();
10698 calling_context1->Global()->Set(v8_str("context0"),
10699 calling_context0->Global());
10700 CompileRun("function f() { context0.callback() }");
10701 calling_context1->Exit();
10702
10703 // Expose context1 in context2 and call the callback function in
10704 // context0 indirectly through f in context1.
10705 calling_context2->Enter();
10706 calling_context2->Global()->Set(v8_str("context1"),
10707 calling_context1->Global());
10708 CompileRun("context1.f()");
10709 calling_context2->Exit();
10710
10711 // Dispose the contexts to allow them to be garbage collected.
10712 calling_context0.Dispose();
10713 calling_context1.Dispose();
10714 calling_context2.Dispose();
10715 calling_context0.Clear();
10716 calling_context1.Clear();
10717 calling_context2.Clear();
10718}
10719
10720
10721// Check that a variable declaration with no explicit initialization
10722// value does not shadow an existing property in the prototype chain.
10723//
10724// This is consistent with Firefox and Safari.
10725//
10726// See http://crbug.com/12548.
10727THREADED_TEST(InitGlobalVarInProtoChain) {
10728 v8::HandleScope scope;
10729 LocalContext context;
10730 // Introduce a variable in the prototype chain.
10731 CompileRun("__proto__.x = 42");
10732 v8::Handle<v8::Value> result = CompileRun("var x; x");
10733 CHECK(!result->IsUndefined());
10734 CHECK_EQ(42, result->Int32Value());
10735}
10736
10737
10738// Regression test for issue 398.
10739// If a function is added to an object, creating a constant function
10740// field, and the result is cloned, replacing the constant function on the
10741// original should not affect the clone.
10742// See http://code.google.com/p/v8/issues/detail?id=398
10743THREADED_TEST(ReplaceConstantFunction) {
10744 v8::HandleScope scope;
10745 LocalContext context;
10746 v8::Handle<v8::Object> obj = v8::Object::New();
10747 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New();
10748 v8::Handle<v8::String> foo_string = v8::String::New("foo");
10749 obj->Set(foo_string, func_templ->GetFunction());
10750 v8::Handle<v8::Object> obj_clone = obj->Clone();
10751 obj_clone->Set(foo_string, v8::String::New("Hello"));
10752 CHECK(!obj->Get(foo_string)->IsUndefined());
10753}
10754
10755
10756// Regression test for http://crbug.com/16276.
10757THREADED_TEST(Regress16276) {
10758 v8::HandleScope scope;
10759 LocalContext context;
10760 // Force the IC in f to be a dictionary load IC.
10761 CompileRun("function f(obj) { return obj.x; }\n"
10762 "var obj = { x: { foo: 42 }, y: 87 };\n"
10763 "var x = obj.x;\n"
10764 "delete obj.y;\n"
10765 "for (var i = 0; i < 5; i++) f(obj);");
10766 // Detach the global object to make 'this' refer directly to the
10767 // global object (not the proxy), and make sure that the dictionary
10768 // load IC doesn't mess up loading directly from the global object.
10769 context->DetachGlobal();
10770 CHECK_EQ(42, CompileRun("f(this).foo")->Int32Value());
10771}
10772
10773
10774THREADED_TEST(PixelArray) {
10775 v8::HandleScope scope;
10776 LocalContext context;
Steve Blockd0582a62009-12-15 09:54:21 +000010777 const int kElementCount = 260;
Steve Blocka7e24c12009-10-30 11:49:00 +000010778 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(kElementCount));
Steve Block44f0eee2011-05-26 01:26:41 +010010779 i::Handle<i::ExternalPixelArray> pixels =
10780 i::Handle<i::ExternalPixelArray>::cast(
10781 FACTORY->NewExternalArray(kElementCount,
10782 v8::kExternalPixelArray,
10783 pixel_data));
10784 HEAP->CollectAllGarbage(false); // Force GC to trigger verification.
Steve Blocka7e24c12009-10-30 11:49:00 +000010785 for (int i = 0; i < kElementCount; i++) {
Steve Blockd0582a62009-12-15 09:54:21 +000010786 pixels->set(i, i % 256);
Steve Blocka7e24c12009-10-30 11:49:00 +000010787 }
Steve Block44f0eee2011-05-26 01:26:41 +010010788 HEAP->CollectAllGarbage(false); // Force GC to trigger verification.
Steve Blocka7e24c12009-10-30 11:49:00 +000010789 for (int i = 0; i < kElementCount; i++) {
Steve Blockd0582a62009-12-15 09:54:21 +000010790 CHECK_EQ(i % 256, pixels->get(i));
10791 CHECK_EQ(i % 256, pixel_data[i]);
Steve Blocka7e24c12009-10-30 11:49:00 +000010792 }
10793
10794 v8::Handle<v8::Object> obj = v8::Object::New();
10795 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj);
10796 // Set the elements to be the pixels.
10797 // jsobj->set_elements(*pixels);
10798 obj->SetIndexedPropertiesToPixelData(pixel_data, kElementCount);
John Reck59135872010-11-02 12:39:01 -070010799 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000010800 obj->Set(v8_str("field"), v8::Int32::New(1503));
10801 context->Global()->Set(v8_str("pixels"), obj);
10802 v8::Handle<v8::Value> result = CompileRun("pixels.field");
10803 CHECK_EQ(1503, result->Int32Value());
10804 result = CompileRun("pixels[1]");
10805 CHECK_EQ(1, result->Int32Value());
10806
10807 result = CompileRun("var sum = 0;"
10808 "for (var i = 0; i < 8; i++) {"
10809 " sum += pixels[i] = pixels[i] = -i;"
10810 "}"
10811 "sum;");
10812 CHECK_EQ(-28, result->Int32Value());
10813
10814 result = CompileRun("var sum = 0;"
10815 "for (var i = 0; i < 8; i++) {"
10816 " sum += pixels[i] = pixels[i] = 0;"
10817 "}"
10818 "sum;");
10819 CHECK_EQ(0, result->Int32Value());
10820
10821 result = CompileRun("var sum = 0;"
10822 "for (var i = 0; i < 8; i++) {"
10823 " sum += pixels[i] = pixels[i] = 255;"
10824 "}"
10825 "sum;");
10826 CHECK_EQ(8 * 255, result->Int32Value());
10827
10828 result = CompileRun("var sum = 0;"
10829 "for (var i = 0; i < 8; i++) {"
10830 " sum += pixels[i] = pixels[i] = 256 + i;"
10831 "}"
10832 "sum;");
10833 CHECK_EQ(2076, result->Int32Value());
10834
10835 result = CompileRun("var sum = 0;"
10836 "for (var i = 0; i < 8; i++) {"
10837 " sum += pixels[i] = pixels[i] = i;"
10838 "}"
10839 "sum;");
10840 CHECK_EQ(28, result->Int32Value());
10841
10842 result = CompileRun("var sum = 0;"
10843 "for (var i = 0; i < 8; i++) {"
10844 " sum += pixels[i];"
10845 "}"
10846 "sum;");
10847 CHECK_EQ(28, result->Int32Value());
10848
10849 i::Handle<i::Smi> value(i::Smi::FromInt(2));
Ben Murdoche0cee9b2011-05-25 10:26:03 +010010850 i::SetElement(jsobj, 1, value, i::kNonStrictMode);
John Reck59135872010-11-02 12:39:01 -070010851 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000010852 *value.location() = i::Smi::FromInt(256);
Ben Murdoche0cee9b2011-05-25 10:26:03 +010010853 i::SetElement(jsobj, 1, value, i::kNonStrictMode);
John Reck59135872010-11-02 12:39:01 -070010854 CHECK_EQ(255,
10855 i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000010856 *value.location() = i::Smi::FromInt(-1);
Ben Murdoche0cee9b2011-05-25 10:26:03 +010010857 i::SetElement(jsobj, 1, value, i::kNonStrictMode);
John Reck59135872010-11-02 12:39:01 -070010858 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000010859
10860 result = CompileRun("for (var i = 0; i < 8; i++) {"
10861 " pixels[i] = (i * 65) - 109;"
10862 "}"
10863 "pixels[1] + pixels[6];");
10864 CHECK_EQ(255, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070010865 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0)->ToObjectChecked())->value());
10866 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value());
10867 CHECK_EQ(21,
10868 i::Smi::cast(jsobj->GetElement(2)->ToObjectChecked())->value());
10869 CHECK_EQ(86,
10870 i::Smi::cast(jsobj->GetElement(3)->ToObjectChecked())->value());
10871 CHECK_EQ(151,
10872 i::Smi::cast(jsobj->GetElement(4)->ToObjectChecked())->value());
10873 CHECK_EQ(216,
10874 i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value());
10875 CHECK_EQ(255,
10876 i::Smi::cast(jsobj->GetElement(6)->ToObjectChecked())->value());
10877 CHECK_EQ(255,
10878 i::Smi::cast(jsobj->GetElement(7)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000010879 result = CompileRun("var sum = 0;"
10880 "for (var i = 0; i < 8; i++) {"
10881 " sum += pixels[i];"
10882 "}"
10883 "sum;");
10884 CHECK_EQ(984, result->Int32Value());
10885
10886 result = CompileRun("for (var i = 0; i < 8; i++) {"
10887 " pixels[i] = (i * 1.1);"
10888 "}"
10889 "pixels[1] + pixels[6];");
10890 CHECK_EQ(8, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070010891 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0)->ToObjectChecked())->value());
10892 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value());
10893 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(2)->ToObjectChecked())->value());
10894 CHECK_EQ(3, i::Smi::cast(jsobj->GetElement(3)->ToObjectChecked())->value());
10895 CHECK_EQ(4, i::Smi::cast(jsobj->GetElement(4)->ToObjectChecked())->value());
10896 CHECK_EQ(6, i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value());
10897 CHECK_EQ(7, i::Smi::cast(jsobj->GetElement(6)->ToObjectChecked())->value());
10898 CHECK_EQ(8, i::Smi::cast(jsobj->GetElement(7)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000010899
10900 result = CompileRun("for (var i = 0; i < 8; i++) {"
10901 " pixels[7] = undefined;"
10902 "}"
10903 "pixels[7];");
10904 CHECK_EQ(0, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070010905 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(7)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000010906
10907 result = CompileRun("for (var i = 0; i < 8; i++) {"
10908 " pixels[6] = '2.3';"
10909 "}"
10910 "pixels[6];");
10911 CHECK_EQ(2, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070010912 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(6)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000010913
10914 result = CompileRun("for (var i = 0; i < 8; i++) {"
10915 " pixels[5] = NaN;"
10916 "}"
10917 "pixels[5];");
10918 CHECK_EQ(0, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070010919 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000010920
10921 result = CompileRun("for (var i = 0; i < 8; i++) {"
10922 " pixels[8] = Infinity;"
10923 "}"
10924 "pixels[8];");
10925 CHECK_EQ(255, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070010926 CHECK_EQ(255,
10927 i::Smi::cast(jsobj->GetElement(8)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000010928
10929 result = CompileRun("for (var i = 0; i < 8; i++) {"
10930 " pixels[9] = -Infinity;"
10931 "}"
10932 "pixels[9];");
10933 CHECK_EQ(0, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070010934 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(9)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000010935
10936 result = CompileRun("pixels[3] = 33;"
10937 "delete pixels[3];"
10938 "pixels[3];");
10939 CHECK_EQ(33, result->Int32Value());
10940
10941 result = CompileRun("pixels[0] = 10; pixels[1] = 11;"
10942 "pixels[2] = 12; pixels[3] = 13;"
10943 "pixels.__defineGetter__('2',"
10944 "function() { return 120; });"
10945 "pixels[2];");
10946 CHECK_EQ(12, result->Int32Value());
10947
10948 result = CompileRun("var js_array = new Array(40);"
10949 "js_array[0] = 77;"
10950 "js_array;");
10951 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
10952
10953 result = CompileRun("pixels[1] = 23;"
10954 "pixels.__proto__ = [];"
10955 "js_array.__proto__ = pixels;"
10956 "js_array.concat(pixels);");
10957 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
10958 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value());
10959
10960 result = CompileRun("pixels[1] = 23;");
10961 CHECK_EQ(23, result->Int32Value());
10962
Steve Blockd0582a62009-12-15 09:54:21 +000010963 // Test for index greater than 255. Regression test for:
10964 // http://code.google.com/p/chromium/issues/detail?id=26337.
10965 result = CompileRun("pixels[256] = 255;");
10966 CHECK_EQ(255, result->Int32Value());
10967 result = CompileRun("var i = 0;"
10968 "for (var j = 0; j < 8; j++) { i = pixels[256]; }"
10969 "i");
10970 CHECK_EQ(255, result->Int32Value());
10971
Steve Block1e0659c2011-05-24 12:43:12 +010010972 // Make sure that pixel array ICs recognize when a non-pixel array
10973 // is passed to it.
10974 result = CompileRun("function pa_load(p) {"
10975 " var sum = 0;"
10976 " for (var j = 0; j < 256; j++) { sum += p[j]; }"
10977 " return sum;"
10978 "}"
10979 "for (var i = 0; i < 256; ++i) { pixels[i] = i; }"
10980 "for (var i = 0; i < 10; ++i) { pa_load(pixels); }"
10981 "just_ints = new Object();"
10982 "for (var i = 0; i < 256; ++i) { just_ints[i] = i; }"
10983 "for (var i = 0; i < 10; ++i) {"
10984 " result = pa_load(just_ints);"
10985 "}"
10986 "result");
10987 CHECK_EQ(32640, result->Int32Value());
10988
10989 // Make sure that pixel array ICs recognize out-of-bound accesses.
10990 result = CompileRun("function pa_load(p, start) {"
10991 " var sum = 0;"
10992 " for (var j = start; j < 256; j++) { sum += p[j]; }"
10993 " return sum;"
10994 "}"
10995 "for (var i = 0; i < 256; ++i) { pixels[i] = i; }"
10996 "for (var i = 0; i < 10; ++i) { pa_load(pixels,0); }"
10997 "for (var i = 0; i < 10; ++i) {"
10998 " result = pa_load(pixels,-10);"
10999 "}"
11000 "result");
11001 CHECK_EQ(0, result->Int32Value());
11002
11003 // Make sure that generic ICs properly handles a pixel array.
11004 result = CompileRun("function pa_load(p) {"
11005 " var sum = 0;"
11006 " for (var j = 0; j < 256; j++) { sum += p[j]; }"
11007 " return sum;"
11008 "}"
11009 "for (var i = 0; i < 256; ++i) { pixels[i] = i; }"
11010 "just_ints = new Object();"
11011 "for (var i = 0; i < 256; ++i) { just_ints[i] = i; }"
11012 "for (var i = 0; i < 10; ++i) { pa_load(just_ints); }"
11013 "for (var i = 0; i < 10; ++i) {"
11014 " result = pa_load(pixels);"
11015 "}"
11016 "result");
11017 CHECK_EQ(32640, result->Int32Value());
11018
11019 // Make sure that generic load ICs recognize out-of-bound accesses in
11020 // pixel arrays.
11021 result = CompileRun("function pa_load(p, start) {"
11022 " var sum = 0;"
11023 " for (var j = start; j < 256; j++) { sum += p[j]; }"
11024 " return sum;"
11025 "}"
11026 "for (var i = 0; i < 256; ++i) { pixels[i] = i; }"
11027 "just_ints = new Object();"
11028 "for (var i = 0; i < 256; ++i) { just_ints[i] = i; }"
11029 "for (var i = 0; i < 10; ++i) { pa_load(just_ints,0); }"
11030 "for (var i = 0; i < 10; ++i) { pa_load(pixels,0); }"
11031 "for (var i = 0; i < 10; ++i) {"
11032 " result = pa_load(pixels,-10);"
11033 "}"
11034 "result");
11035 CHECK_EQ(0, result->Int32Value());
11036
11037 // Make sure that generic ICs properly handles other types than pixel
11038 // arrays (that the inlined fast pixel array test leaves the right information
11039 // in the right registers).
11040 result = CompileRun("function pa_load(p) {"
11041 " var sum = 0;"
11042 " for (var j = 0; j < 256; j++) { sum += p[j]; }"
11043 " return sum;"
11044 "}"
11045 "for (var i = 0; i < 256; ++i) { pixels[i] = i; }"
11046 "just_ints = new Object();"
11047 "for (var i = 0; i < 256; ++i) { just_ints[i] = i; }"
11048 "for (var i = 0; i < 10; ++i) { pa_load(just_ints); }"
11049 "for (var i = 0; i < 10; ++i) { pa_load(pixels); }"
11050 "sparse_array = new Object();"
11051 "for (var i = 0; i < 256; ++i) { sparse_array[i] = i; }"
11052 "sparse_array[1000000] = 3;"
11053 "for (var i = 0; i < 10; ++i) {"
11054 " result = pa_load(sparse_array);"
11055 "}"
11056 "result");
11057 CHECK_EQ(32640, result->Int32Value());
11058
Ben Murdoche0cee9b2011-05-25 10:26:03 +010011059 // Make sure that pixel array store ICs clamp values correctly.
11060 result = CompileRun("function pa_store(p) {"
11061 " for (var j = 0; j < 256; j++) { p[j] = j * 2; }"
11062 "}"
11063 "pa_store(pixels);"
11064 "var sum = 0;"
11065 "for (var j = 0; j < 256; j++) { sum += pixels[j]; }"
11066 "sum");
11067 CHECK_EQ(48896, result->Int32Value());
11068
11069 // Make sure that pixel array stores correctly handle accesses outside
11070 // of the pixel array..
11071 result = CompileRun("function pa_store(p,start) {"
11072 " for (var j = 0; j < 256; j++) {"
11073 " p[j+start] = j * 2;"
11074 " }"
11075 "}"
11076 "pa_store(pixels,0);"
11077 "pa_store(pixels,-128);"
11078 "var sum = 0;"
11079 "for (var j = 0; j < 256; j++) { sum += pixels[j]; }"
11080 "sum");
11081 CHECK_EQ(65280, result->Int32Value());
11082
11083 // Make sure that the generic store stub correctly handle accesses outside
11084 // of the pixel array..
11085 result = CompileRun("function pa_store(p,start) {"
11086 " for (var j = 0; j < 256; j++) {"
11087 " p[j+start] = j * 2;"
11088 " }"
11089 "}"
11090 "pa_store(pixels,0);"
11091 "just_ints = new Object();"
11092 "for (var i = 0; i < 256; ++i) { just_ints[i] = i; }"
11093 "pa_store(just_ints, 0);"
11094 "pa_store(pixels,-128);"
11095 "var sum = 0;"
11096 "for (var j = 0; j < 256; j++) { sum += pixels[j]; }"
11097 "sum");
11098 CHECK_EQ(65280, result->Int32Value());
11099
11100 // Make sure that the generic keyed store stub clamps pixel array values
11101 // correctly.
11102 result = CompileRun("function pa_store(p) {"
11103 " for (var j = 0; j < 256; j++) { p[j] = j * 2; }"
11104 "}"
11105 "pa_store(pixels);"
11106 "just_ints = new Object();"
11107 "pa_store(just_ints);"
11108 "pa_store(pixels);"
11109 "var sum = 0;"
11110 "for (var j = 0; j < 256; j++) { sum += pixels[j]; }"
11111 "sum");
11112 CHECK_EQ(48896, result->Int32Value());
11113
11114 // Make sure that pixel array loads are optimized by crankshaft.
Steve Block1e0659c2011-05-24 12:43:12 +010011115 result = CompileRun("function pa_load(p) {"
11116 " var sum = 0;"
11117 " for (var i=0; i<256; ++i) {"
11118 " sum += p[i];"
11119 " }"
11120 " return sum; "
11121 "}"
11122 "for (var i = 0; i < 256; ++i) { pixels[i] = i; }"
Steve Block44f0eee2011-05-26 01:26:41 +010011123 "for (var i = 0; i < 5000; ++i) {"
Steve Block1e0659c2011-05-24 12:43:12 +010011124 " result = pa_load(pixels);"
11125 "}"
11126 "result");
11127 CHECK_EQ(32640, result->Int32Value());
11128
Ben Murdoche0cee9b2011-05-25 10:26:03 +010011129 // Make sure that pixel array stores are optimized by crankshaft.
11130 result = CompileRun("function pa_init(p) {"
11131 "for (var i = 0; i < 256; ++i) { p[i] = i; }"
11132 "}"
11133 "function pa_load(p) {"
11134 " var sum = 0;"
11135 " for (var i=0; i<256; ++i) {"
11136 " sum += p[i];"
11137 " }"
11138 " return sum; "
11139 "}"
Steve Block44f0eee2011-05-26 01:26:41 +010011140 "for (var i = 0; i < 5000; ++i) {"
Ben Murdoche0cee9b2011-05-25 10:26:03 +010011141 " pa_init(pixels);"
11142 "}"
11143 "result = pa_load(pixels);"
11144 "result");
11145 CHECK_EQ(32640, result->Int32Value());
11146
Steve Blocka7e24c12009-10-30 11:49:00 +000011147 free(pixel_data);
11148}
11149
11150
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010011151THREADED_TEST(PixelArrayInfo) {
11152 v8::HandleScope scope;
11153 LocalContext context;
11154 for (int size = 0; size < 100; size += 10) {
11155 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(size));
11156 v8::Handle<v8::Object> obj = v8::Object::New();
11157 obj->SetIndexedPropertiesToPixelData(pixel_data, size);
11158 CHECK(obj->HasIndexedPropertiesInPixelData());
11159 CHECK_EQ(pixel_data, obj->GetIndexedPropertiesPixelData());
11160 CHECK_EQ(size, obj->GetIndexedPropertiesPixelDataLength());
11161 free(pixel_data);
11162 }
11163}
11164
11165
Ben Murdoche0cee9b2011-05-25 10:26:03 +010011166static v8::Handle<Value> NotHandledIndexedPropertyGetter(
11167 uint32_t index,
11168 const AccessorInfo& info) {
11169 ApiTestFuzzer::Fuzz();
11170 return v8::Handle<Value>();
11171}
11172
11173
11174static v8::Handle<Value> NotHandledIndexedPropertySetter(
11175 uint32_t index,
11176 Local<Value> value,
11177 const AccessorInfo& info) {
11178 ApiTestFuzzer::Fuzz();
11179 return v8::Handle<Value>();
11180}
11181
11182
11183THREADED_TEST(PixelArrayWithInterceptor) {
11184 v8::HandleScope scope;
11185 LocalContext context;
11186 const int kElementCount = 260;
11187 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(kElementCount));
Steve Block44f0eee2011-05-26 01:26:41 +010011188 i::Handle<i::ExternalPixelArray> pixels =
11189 i::Handle<i::ExternalPixelArray>::cast(
11190 FACTORY->NewExternalArray(kElementCount,
11191 v8::kExternalPixelArray,
11192 pixel_data));
Ben Murdoche0cee9b2011-05-25 10:26:03 +010011193 for (int i = 0; i < kElementCount; i++) {
11194 pixels->set(i, i % 256);
11195 }
11196 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
11197 templ->SetIndexedPropertyHandler(NotHandledIndexedPropertyGetter,
11198 NotHandledIndexedPropertySetter);
11199 v8::Handle<v8::Object> obj = templ->NewInstance();
11200 obj->SetIndexedPropertiesToPixelData(pixel_data, kElementCount);
11201 context->Global()->Set(v8_str("pixels"), obj);
11202 v8::Handle<v8::Value> result = CompileRun("pixels[1]");
11203 CHECK_EQ(1, result->Int32Value());
11204 result = CompileRun("var sum = 0;"
11205 "for (var i = 0; i < 8; i++) {"
11206 " sum += pixels[i] = pixels[i] = -i;"
11207 "}"
11208 "sum;");
11209 CHECK_EQ(-28, result->Int32Value());
11210 result = CompileRun("pixels.hasOwnProperty('1')");
11211 CHECK(result->BooleanValue());
11212 free(pixel_data);
11213}
11214
11215
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010011216static int ExternalArrayElementSize(v8::ExternalArrayType array_type) {
11217 switch (array_type) {
11218 case v8::kExternalByteArray:
11219 case v8::kExternalUnsignedByteArray:
Steve Block44f0eee2011-05-26 01:26:41 +010011220 case v8::kExternalPixelArray:
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010011221 return 1;
11222 break;
11223 case v8::kExternalShortArray:
11224 case v8::kExternalUnsignedShortArray:
11225 return 2;
11226 break;
11227 case v8::kExternalIntArray:
11228 case v8::kExternalUnsignedIntArray:
11229 case v8::kExternalFloatArray:
11230 return 4;
11231 break;
11232 default:
11233 UNREACHABLE();
11234 return -1;
11235 }
11236 UNREACHABLE();
11237 return -1;
11238}
11239
11240
Steve Block3ce2e202009-11-05 08:53:23 +000011241template <class ExternalArrayClass, class ElementType>
11242static void ExternalArrayTestHelper(v8::ExternalArrayType array_type,
11243 int64_t low,
11244 int64_t high) {
11245 v8::HandleScope scope;
11246 LocalContext context;
11247 const int kElementCount = 40;
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010011248 int element_size = ExternalArrayElementSize(array_type);
Steve Block3ce2e202009-11-05 08:53:23 +000011249 ElementType* array_data =
11250 static_cast<ElementType*>(malloc(kElementCount * element_size));
11251 i::Handle<ExternalArrayClass> array =
11252 i::Handle<ExternalArrayClass>::cast(
Steve Block44f0eee2011-05-26 01:26:41 +010011253 FACTORY->NewExternalArray(kElementCount, array_type, array_data));
11254 HEAP->CollectAllGarbage(false); // Force GC to trigger verification.
Steve Block3ce2e202009-11-05 08:53:23 +000011255 for (int i = 0; i < kElementCount; i++) {
11256 array->set(i, static_cast<ElementType>(i));
11257 }
Steve Block44f0eee2011-05-26 01:26:41 +010011258 HEAP->CollectAllGarbage(false); // Force GC to trigger verification.
Steve Block3ce2e202009-11-05 08:53:23 +000011259 for (int i = 0; i < kElementCount; i++) {
11260 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array->get(i)));
11261 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array_data[i]));
11262 }
11263
11264 v8::Handle<v8::Object> obj = v8::Object::New();
11265 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj);
11266 // Set the elements to be the external array.
11267 obj->SetIndexedPropertiesToExternalArrayData(array_data,
11268 array_type,
11269 kElementCount);
John Reck59135872010-11-02 12:39:01 -070011270 CHECK_EQ(
11271 1, static_cast<int>(jsobj->GetElement(1)->ToObjectChecked()->Number()));
Steve Block3ce2e202009-11-05 08:53:23 +000011272 obj->Set(v8_str("field"), v8::Int32::New(1503));
11273 context->Global()->Set(v8_str("ext_array"), obj);
11274 v8::Handle<v8::Value> result = CompileRun("ext_array.field");
11275 CHECK_EQ(1503, result->Int32Value());
11276 result = CompileRun("ext_array[1]");
11277 CHECK_EQ(1, result->Int32Value());
11278
11279 // Check pass through of assigned smis
11280 result = CompileRun("var sum = 0;"
11281 "for (var i = 0; i < 8; i++) {"
11282 " sum += ext_array[i] = ext_array[i] = -i;"
11283 "}"
11284 "sum;");
11285 CHECK_EQ(-28, result->Int32Value());
11286
11287 // Check assigned smis
11288 result = CompileRun("for (var i = 0; i < 8; i++) {"
11289 " ext_array[i] = i;"
11290 "}"
11291 "var sum = 0;"
11292 "for (var i = 0; i < 8; i++) {"
11293 " sum += ext_array[i];"
11294 "}"
11295 "sum;");
11296 CHECK_EQ(28, result->Int32Value());
11297
11298 // Check assigned smis in reverse order
11299 result = CompileRun("for (var i = 8; --i >= 0; ) {"
11300 " ext_array[i] = i;"
11301 "}"
11302 "var sum = 0;"
11303 "for (var i = 0; i < 8; i++) {"
11304 " sum += ext_array[i];"
11305 "}"
11306 "sum;");
11307 CHECK_EQ(28, result->Int32Value());
11308
11309 // Check pass through of assigned HeapNumbers
11310 result = CompileRun("var sum = 0;"
11311 "for (var i = 0; i < 16; i+=2) {"
11312 " sum += ext_array[i] = ext_array[i] = (-i * 0.5);"
11313 "}"
11314 "sum;");
11315 CHECK_EQ(-28, result->Int32Value());
11316
11317 // Check assigned HeapNumbers
11318 result = CompileRun("for (var i = 0; i < 16; i+=2) {"
11319 " ext_array[i] = (i * 0.5);"
11320 "}"
11321 "var sum = 0;"
11322 "for (var i = 0; i < 16; i+=2) {"
11323 " sum += ext_array[i];"
11324 "}"
11325 "sum;");
11326 CHECK_EQ(28, result->Int32Value());
11327
11328 // Check assigned HeapNumbers in reverse order
11329 result = CompileRun("for (var i = 14; i >= 0; i-=2) {"
11330 " ext_array[i] = (i * 0.5);"
11331 "}"
11332 "var sum = 0;"
11333 "for (var i = 0; i < 16; i+=2) {"
11334 " sum += ext_array[i];"
11335 "}"
11336 "sum;");
11337 CHECK_EQ(28, result->Int32Value());
11338
11339 i::ScopedVector<char> test_buf(1024);
11340
11341 // Check legal boundary conditions.
11342 // The repeated loads and stores ensure the ICs are exercised.
11343 const char* boundary_program =
11344 "var res = 0;"
11345 "for (var i = 0; i < 16; i++) {"
11346 " ext_array[i] = %lld;"
11347 " if (i > 8) {"
11348 " res = ext_array[i];"
11349 " }"
11350 "}"
11351 "res;";
11352 i::OS::SNPrintF(test_buf,
11353 boundary_program,
11354 low);
11355 result = CompileRun(test_buf.start());
11356 CHECK_EQ(low, result->IntegerValue());
11357
11358 i::OS::SNPrintF(test_buf,
11359 boundary_program,
11360 high);
11361 result = CompileRun(test_buf.start());
11362 CHECK_EQ(high, result->IntegerValue());
11363
11364 // Check misprediction of type in IC.
11365 result = CompileRun("var tmp_array = ext_array;"
11366 "var sum = 0;"
11367 "for (var i = 0; i < 8; i++) {"
11368 " tmp_array[i] = i;"
11369 " sum += tmp_array[i];"
11370 " if (i == 4) {"
11371 " tmp_array = {};"
11372 " }"
11373 "}"
11374 "sum;");
Steve Block44f0eee2011-05-26 01:26:41 +010011375 HEAP->CollectAllGarbage(false); // Force GC to trigger verification.
Steve Block3ce2e202009-11-05 08:53:23 +000011376 CHECK_EQ(28, result->Int32Value());
11377
11378 // Make sure out-of-range loads do not throw.
11379 i::OS::SNPrintF(test_buf,
11380 "var caught_exception = false;"
11381 "try {"
11382 " ext_array[%d];"
11383 "} catch (e) {"
11384 " caught_exception = true;"
11385 "}"
11386 "caught_exception;",
11387 kElementCount);
11388 result = CompileRun(test_buf.start());
11389 CHECK_EQ(false, result->BooleanValue());
11390
11391 // Make sure out-of-range stores do not throw.
11392 i::OS::SNPrintF(test_buf,
11393 "var caught_exception = false;"
11394 "try {"
11395 " ext_array[%d] = 1;"
11396 "} catch (e) {"
11397 " caught_exception = true;"
11398 "}"
11399 "caught_exception;",
11400 kElementCount);
11401 result = CompileRun(test_buf.start());
11402 CHECK_EQ(false, result->BooleanValue());
11403
11404 // Check other boundary conditions, values and operations.
11405 result = CompileRun("for (var i = 0; i < 8; i++) {"
11406 " ext_array[7] = undefined;"
11407 "}"
11408 "ext_array[7];");
11409 CHECK_EQ(0, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070011410 CHECK_EQ(
11411 0, static_cast<int>(jsobj->GetElement(7)->ToObjectChecked()->Number()));
Steve Block3ce2e202009-11-05 08:53:23 +000011412
11413 result = CompileRun("for (var i = 0; i < 8; i++) {"
11414 " ext_array[6] = '2.3';"
11415 "}"
11416 "ext_array[6];");
11417 CHECK_EQ(2, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070011418 CHECK_EQ(
11419 2, static_cast<int>(jsobj->GetElement(6)->ToObjectChecked()->Number()));
Steve Block3ce2e202009-11-05 08:53:23 +000011420
11421 if (array_type != v8::kExternalFloatArray) {
11422 // Though the specification doesn't state it, be explicit about
11423 // converting NaNs and +/-Infinity to zero.
11424 result = CompileRun("for (var i = 0; i < 8; i++) {"
11425 " ext_array[i] = 5;"
11426 "}"
11427 "for (var i = 0; i < 8; i++) {"
11428 " ext_array[i] = NaN;"
11429 "}"
11430 "ext_array[5];");
11431 CHECK_EQ(0, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070011432 CHECK_EQ(0,
11433 i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value());
Steve Block3ce2e202009-11-05 08:53:23 +000011434
11435 result = CompileRun("for (var i = 0; i < 8; i++) {"
11436 " ext_array[i] = 5;"
11437 "}"
11438 "for (var i = 0; i < 8; i++) {"
11439 " ext_array[i] = Infinity;"
11440 "}"
11441 "ext_array[5];");
Steve Block44f0eee2011-05-26 01:26:41 +010011442 int expected_value =
11443 (array_type == v8::kExternalPixelArray) ? 255 : 0;
11444 CHECK_EQ(expected_value, result->Int32Value());
11445 CHECK_EQ(expected_value,
John Reck59135872010-11-02 12:39:01 -070011446 i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value());
Steve Block3ce2e202009-11-05 08:53:23 +000011447
11448 result = CompileRun("for (var i = 0; i < 8; i++) {"
11449 " ext_array[i] = 5;"
11450 "}"
11451 "for (var i = 0; i < 8; i++) {"
11452 " ext_array[i] = -Infinity;"
11453 "}"
11454 "ext_array[5];");
11455 CHECK_EQ(0, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070011456 CHECK_EQ(0,
11457 i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value());
Steve Block1e0659c2011-05-24 12:43:12 +010011458
11459 // Check truncation behavior of integral arrays.
11460 const char* unsigned_data =
11461 "var source_data = [0.6, 10.6];"
11462 "var expected_results = [0, 10];";
11463 const char* signed_data =
11464 "var source_data = [0.6, 10.6, -0.6, -10.6];"
11465 "var expected_results = [0, 10, 0, -10];";
Steve Block44f0eee2011-05-26 01:26:41 +010011466 const char* pixel_data =
11467 "var source_data = [0.6, 10.6];"
11468 "var expected_results = [1, 11];";
Steve Block1e0659c2011-05-24 12:43:12 +010011469 bool is_unsigned =
11470 (array_type == v8::kExternalUnsignedByteArray ||
11471 array_type == v8::kExternalUnsignedShortArray ||
11472 array_type == v8::kExternalUnsignedIntArray);
Steve Block44f0eee2011-05-26 01:26:41 +010011473 bool is_pixel_data = array_type == v8::kExternalPixelArray;
Steve Block1e0659c2011-05-24 12:43:12 +010011474
11475 i::OS::SNPrintF(test_buf,
11476 "%s"
11477 "var all_passed = true;"
11478 "for (var i = 0; i < source_data.length; i++) {"
11479 " for (var j = 0; j < 8; j++) {"
11480 " ext_array[j] = source_data[i];"
11481 " }"
11482 " all_passed = all_passed &&"
11483 " (ext_array[5] == expected_results[i]);"
11484 "}"
11485 "all_passed;",
Steve Block44f0eee2011-05-26 01:26:41 +010011486 (is_unsigned ?
11487 unsigned_data :
11488 (is_pixel_data ? pixel_data : signed_data)));
Steve Block1e0659c2011-05-24 12:43:12 +010011489 result = CompileRun(test_buf.start());
11490 CHECK_EQ(true, result->BooleanValue());
Steve Block3ce2e202009-11-05 08:53:23 +000011491 }
11492
Steve Block44f0eee2011-05-26 01:26:41 +010011493 // Test crankshaft external array loads
11494 for (int i = 0; i < kElementCount; i++) {
11495 array->set(i, static_cast<ElementType>(i));
11496 }
11497 result = CompileRun("function ee_load_test_func(sum) {"
11498 " for (var i=0;i<40;++i)"
11499 " sum += ext_array[i];"
11500 " return sum;"
11501 "}"
11502 "sum=0;"
11503 "for (var i=0;i<10000;++i) {"
11504 " sum=ee_load_test_func(sum);"
11505 "}"
11506 "sum;");
11507 CHECK_EQ(7800000, result->Int32Value());
11508
11509 // Test crankshaft external array stores
11510 result = CompileRun("function ee_store_test_func(sum) {"
11511 " for (var i=0;i<40;++i)"
11512 " sum += ext_array[i] = i;"
11513 " return sum;"
11514 "}"
11515 "sum=0;"
11516 "for (var i=0;i<10000;++i) {"
11517 " sum=ee_store_test_func(sum);"
11518 "}"
11519 "sum;");
11520 CHECK_EQ(7800000, result->Int32Value());
11521
Steve Block3ce2e202009-11-05 08:53:23 +000011522 result = CompileRun("ext_array[3] = 33;"
11523 "delete ext_array[3];"
11524 "ext_array[3];");
11525 CHECK_EQ(33, result->Int32Value());
11526
11527 result = CompileRun("ext_array[0] = 10; ext_array[1] = 11;"
11528 "ext_array[2] = 12; ext_array[3] = 13;"
11529 "ext_array.__defineGetter__('2',"
11530 "function() { return 120; });"
11531 "ext_array[2];");
11532 CHECK_EQ(12, result->Int32Value());
11533
11534 result = CompileRun("var js_array = new Array(40);"
11535 "js_array[0] = 77;"
11536 "js_array;");
11537 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
11538
11539 result = CompileRun("ext_array[1] = 23;"
11540 "ext_array.__proto__ = [];"
11541 "js_array.__proto__ = ext_array;"
11542 "js_array.concat(ext_array);");
11543 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
11544 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value());
11545
11546 result = CompileRun("ext_array[1] = 23;");
11547 CHECK_EQ(23, result->Int32Value());
11548
Steve Blockd0582a62009-12-15 09:54:21 +000011549 // Test more complex manipulations which cause eax to contain values
11550 // that won't be completely overwritten by loads from the arrays.
11551 // This catches bugs in the instructions used for the KeyedLoadIC
11552 // for byte and word types.
11553 {
11554 const int kXSize = 300;
11555 const int kYSize = 300;
11556 const int kLargeElementCount = kXSize * kYSize * 4;
11557 ElementType* large_array_data =
11558 static_cast<ElementType*>(malloc(kLargeElementCount * element_size));
11559 i::Handle<ExternalArrayClass> large_array =
11560 i::Handle<ExternalArrayClass>::cast(
Steve Block44f0eee2011-05-26 01:26:41 +010011561 FACTORY->NewExternalArray(kLargeElementCount,
Steve Blockd0582a62009-12-15 09:54:21 +000011562 array_type,
11563 array_data));
11564 v8::Handle<v8::Object> large_obj = v8::Object::New();
11565 // Set the elements to be the external array.
11566 large_obj->SetIndexedPropertiesToExternalArrayData(large_array_data,
11567 array_type,
11568 kLargeElementCount);
11569 context->Global()->Set(v8_str("large_array"), large_obj);
11570 // Initialize contents of a few rows.
11571 for (int x = 0; x < 300; x++) {
11572 int row = 0;
11573 int offset = row * 300 * 4;
11574 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
11575 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
11576 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
11577 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
11578 row = 150;
11579 offset = row * 300 * 4;
11580 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
11581 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
11582 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
11583 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
11584 row = 298;
11585 offset = row * 300 * 4;
11586 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
11587 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
11588 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
11589 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
11590 }
11591 // The goal of the code below is to make "offset" large enough
11592 // that the computation of the index (which goes into eax) has
11593 // high bits set which will not be overwritten by a byte or short
11594 // load.
11595 result = CompileRun("var failed = false;"
11596 "var offset = 0;"
11597 "for (var i = 0; i < 300; i++) {"
11598 " if (large_array[4 * i] != 127 ||"
11599 " large_array[4 * i + 1] != 0 ||"
11600 " large_array[4 * i + 2] != 0 ||"
11601 " large_array[4 * i + 3] != 127) {"
11602 " failed = true;"
11603 " }"
11604 "}"
11605 "offset = 150 * 300 * 4;"
11606 "for (var i = 0; i < 300; i++) {"
11607 " if (large_array[offset + 4 * i] != 127 ||"
11608 " large_array[offset + 4 * i + 1] != 0 ||"
11609 " large_array[offset + 4 * i + 2] != 0 ||"
11610 " large_array[offset + 4 * i + 3] != 127) {"
11611 " failed = true;"
11612 " }"
11613 "}"
11614 "offset = 298 * 300 * 4;"
11615 "for (var i = 0; i < 300; i++) {"
11616 " if (large_array[offset + 4 * i] != 127 ||"
11617 " large_array[offset + 4 * i + 1] != 0 ||"
11618 " large_array[offset + 4 * i + 2] != 0 ||"
11619 " large_array[offset + 4 * i + 3] != 127) {"
11620 " failed = true;"
11621 " }"
11622 "}"
11623 "!failed;");
11624 CHECK_EQ(true, result->BooleanValue());
11625 free(large_array_data);
11626 }
11627
Steve Block44f0eee2011-05-26 01:26:41 +010011628 // The "" property descriptor is overloaded to store information about
11629 // the external array. Ensure that setting and accessing the "" property
11630 // works (it should overwrite the information cached about the external
11631 // array in the DescriptorArray) in various situations.
11632 result = CompileRun("ext_array[''] = 23; ext_array['']");
11633 CHECK_EQ(23, result->Int32Value());
11634
11635 // Property "" set after the external array is associated with the object.
11636 {
11637 v8::Handle<v8::Object> obj2 = v8::Object::New();
11638 obj2->Set(v8_str("ee_test_field"), v8::Int32::New(256));
11639 obj2->Set(v8_str(""), v8::Int32::New(1503));
11640 // Set the elements to be the external array.
11641 obj2->SetIndexedPropertiesToExternalArrayData(array_data,
11642 array_type,
11643 kElementCount);
11644 context->Global()->Set(v8_str("ext_array"), obj2);
11645 result = CompileRun("ext_array['']");
11646 CHECK_EQ(1503, result->Int32Value());
11647 }
11648
11649 // Property "" set after the external array is associated with the object.
11650 {
11651 v8::Handle<v8::Object> obj2 = v8::Object::New();
11652 obj2->Set(v8_str("ee_test_field_2"), v8::Int32::New(256));
11653 // Set the elements to be the external array.
11654 obj2->SetIndexedPropertiesToExternalArrayData(array_data,
11655 array_type,
11656 kElementCount);
11657 obj2->Set(v8_str(""), v8::Int32::New(1503));
11658 context->Global()->Set(v8_str("ext_array"), obj2);
11659 result = CompileRun("ext_array['']");
11660 CHECK_EQ(1503, result->Int32Value());
11661 }
11662
11663 // Should reuse the map from previous test.
11664 {
11665 v8::Handle<v8::Object> obj2 = v8::Object::New();
11666 obj2->Set(v8_str("ee_test_field_2"), v8::Int32::New(256));
11667 // Set the elements to be the external array. Should re-use the map
11668 // from previous test.
11669 obj2->SetIndexedPropertiesToExternalArrayData(array_data,
11670 array_type,
11671 kElementCount);
11672 context->Global()->Set(v8_str("ext_array"), obj2);
11673 result = CompileRun("ext_array['']");
11674 }
11675
11676 // Property "" is a constant function that shouldn't not be interfered with
11677 // when an external array is set.
11678 {
11679 v8::Handle<v8::Object> obj2 = v8::Object::New();
11680 // Start
11681 obj2->Set(v8_str("ee_test_field3"), v8::Int32::New(256));
11682
11683 // Add a constant function to an object.
11684 context->Global()->Set(v8_str("ext_array"), obj2);
11685 result = CompileRun("ext_array[''] = function() {return 1503;};"
11686 "ext_array['']();");
11687
11688 // Add an external array transition to the same map that
11689 // has the constant transition.
11690 v8::Handle<v8::Object> obj3 = v8::Object::New();
11691 obj3->Set(v8_str("ee_test_field3"), v8::Int32::New(256));
11692 obj3->SetIndexedPropertiesToExternalArrayData(array_data,
11693 array_type,
11694 kElementCount);
11695 context->Global()->Set(v8_str("ext_array"), obj3);
11696 }
11697
11698 // If a external array transition is in the map, it should get clobbered
11699 // by a constant function.
11700 {
11701 // Add an external array transition.
11702 v8::Handle<v8::Object> obj3 = v8::Object::New();
11703 obj3->Set(v8_str("ee_test_field4"), v8::Int32::New(256));
11704 obj3->SetIndexedPropertiesToExternalArrayData(array_data,
11705 array_type,
11706 kElementCount);
11707
11708 // Add a constant function to the same map that just got an external array
11709 // transition.
11710 v8::Handle<v8::Object> obj2 = v8::Object::New();
11711 obj2->Set(v8_str("ee_test_field4"), v8::Int32::New(256));
11712 context->Global()->Set(v8_str("ext_array"), obj2);
11713 result = CompileRun("ext_array[''] = function() {return 1503;};"
11714 "ext_array['']();");
11715 }
11716
Steve Block3ce2e202009-11-05 08:53:23 +000011717 free(array_data);
11718}
11719
11720
11721THREADED_TEST(ExternalByteArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010011722 ExternalArrayTestHelper<i::ExternalByteArray, int8_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000011723 v8::kExternalByteArray,
11724 -128,
11725 127);
11726}
11727
11728
11729THREADED_TEST(ExternalUnsignedByteArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010011730 ExternalArrayTestHelper<i::ExternalUnsignedByteArray, uint8_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000011731 v8::kExternalUnsignedByteArray,
11732 0,
11733 255);
11734}
11735
11736
Steve Block44f0eee2011-05-26 01:26:41 +010011737THREADED_TEST(ExternalPixelArray) {
11738 ExternalArrayTestHelper<i::ExternalPixelArray, uint8_t>(
11739 v8::kExternalPixelArray,
11740 0,
11741 255);
11742}
11743
11744
Steve Block3ce2e202009-11-05 08:53:23 +000011745THREADED_TEST(ExternalShortArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010011746 ExternalArrayTestHelper<i::ExternalShortArray, int16_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000011747 v8::kExternalShortArray,
11748 -32768,
11749 32767);
11750}
11751
11752
11753THREADED_TEST(ExternalUnsignedShortArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010011754 ExternalArrayTestHelper<i::ExternalUnsignedShortArray, uint16_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000011755 v8::kExternalUnsignedShortArray,
11756 0,
11757 65535);
11758}
11759
11760
11761THREADED_TEST(ExternalIntArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010011762 ExternalArrayTestHelper<i::ExternalIntArray, int32_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000011763 v8::kExternalIntArray,
11764 INT_MIN, // -2147483648
11765 INT_MAX); // 2147483647
11766}
11767
11768
11769THREADED_TEST(ExternalUnsignedIntArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010011770 ExternalArrayTestHelper<i::ExternalUnsignedIntArray, uint32_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000011771 v8::kExternalUnsignedIntArray,
11772 0,
11773 UINT_MAX); // 4294967295
11774}
11775
11776
11777THREADED_TEST(ExternalFloatArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010011778 ExternalArrayTestHelper<i::ExternalFloatArray, float>(
Steve Block3ce2e202009-11-05 08:53:23 +000011779 v8::kExternalFloatArray,
11780 -500,
11781 500);
11782}
11783
11784
11785THREADED_TEST(ExternalArrays) {
11786 TestExternalByteArray();
11787 TestExternalUnsignedByteArray();
11788 TestExternalShortArray();
11789 TestExternalUnsignedShortArray();
11790 TestExternalIntArray();
11791 TestExternalUnsignedIntArray();
11792 TestExternalFloatArray();
11793}
11794
11795
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010011796void ExternalArrayInfoTestHelper(v8::ExternalArrayType array_type) {
11797 v8::HandleScope scope;
11798 LocalContext context;
11799 for (int size = 0; size < 100; size += 10) {
11800 int element_size = ExternalArrayElementSize(array_type);
11801 void* external_data = malloc(size * element_size);
11802 v8::Handle<v8::Object> obj = v8::Object::New();
11803 obj->SetIndexedPropertiesToExternalArrayData(
11804 external_data, array_type, size);
11805 CHECK(obj->HasIndexedPropertiesInExternalArrayData());
11806 CHECK_EQ(external_data, obj->GetIndexedPropertiesExternalArrayData());
11807 CHECK_EQ(array_type, obj->GetIndexedPropertiesExternalArrayDataType());
11808 CHECK_EQ(size, obj->GetIndexedPropertiesExternalArrayDataLength());
11809 free(external_data);
11810 }
11811}
11812
11813
11814THREADED_TEST(ExternalArrayInfo) {
11815 ExternalArrayInfoTestHelper(v8::kExternalByteArray);
11816 ExternalArrayInfoTestHelper(v8::kExternalUnsignedByteArray);
11817 ExternalArrayInfoTestHelper(v8::kExternalShortArray);
11818 ExternalArrayInfoTestHelper(v8::kExternalUnsignedShortArray);
11819 ExternalArrayInfoTestHelper(v8::kExternalIntArray);
11820 ExternalArrayInfoTestHelper(v8::kExternalUnsignedIntArray);
11821 ExternalArrayInfoTestHelper(v8::kExternalFloatArray);
Steve Block44f0eee2011-05-26 01:26:41 +010011822 ExternalArrayInfoTestHelper(v8::kExternalPixelArray);
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010011823}
11824
11825
Steve Blocka7e24c12009-10-30 11:49:00 +000011826THREADED_TEST(ScriptContextDependence) {
11827 v8::HandleScope scope;
11828 LocalContext c1;
11829 const char *source = "foo";
11830 v8::Handle<v8::Script> dep = v8::Script::Compile(v8::String::New(source));
11831 v8::Handle<v8::Script> indep = v8::Script::New(v8::String::New(source));
11832 c1->Global()->Set(v8::String::New("foo"), v8::Integer::New(100));
11833 CHECK_EQ(dep->Run()->Int32Value(), 100);
11834 CHECK_EQ(indep->Run()->Int32Value(), 100);
11835 LocalContext c2;
11836 c2->Global()->Set(v8::String::New("foo"), v8::Integer::New(101));
11837 CHECK_EQ(dep->Run()->Int32Value(), 100);
11838 CHECK_EQ(indep->Run()->Int32Value(), 101);
11839}
11840
11841
11842THREADED_TEST(StackTrace) {
11843 v8::HandleScope scope;
11844 LocalContext context;
11845 v8::TryCatch try_catch;
11846 const char *source = "function foo() { FAIL.FAIL; }; foo();";
11847 v8::Handle<v8::String> src = v8::String::New(source);
11848 v8::Handle<v8::String> origin = v8::String::New("stack-trace-test");
11849 v8::Script::New(src, origin)->Run();
11850 CHECK(try_catch.HasCaught());
11851 v8::String::Utf8Value stack(try_catch.StackTrace());
11852 CHECK(strstr(*stack, "at foo (stack-trace-test") != NULL);
11853}
11854
11855
Kristian Monsen25f61362010-05-21 11:50:48 +010011856// Checks that a StackFrame has certain expected values.
11857void checkStackFrame(const char* expected_script_name,
11858 const char* expected_func_name, int expected_line_number,
11859 int expected_column, bool is_eval, bool is_constructor,
11860 v8::Handle<v8::StackFrame> frame) {
11861 v8::HandleScope scope;
11862 v8::String::Utf8Value func_name(frame->GetFunctionName());
11863 v8::String::Utf8Value script_name(frame->GetScriptName());
11864 if (*script_name == NULL) {
11865 // The situation where there is no associated script, like for evals.
11866 CHECK(expected_script_name == NULL);
11867 } else {
11868 CHECK(strstr(*script_name, expected_script_name) != NULL);
11869 }
11870 CHECK(strstr(*func_name, expected_func_name) != NULL);
11871 CHECK_EQ(expected_line_number, frame->GetLineNumber());
11872 CHECK_EQ(expected_column, frame->GetColumn());
11873 CHECK_EQ(is_eval, frame->IsEval());
11874 CHECK_EQ(is_constructor, frame->IsConstructor());
11875}
11876
11877
11878v8::Handle<Value> AnalyzeStackInNativeCode(const v8::Arguments& args) {
11879 v8::HandleScope scope;
11880 const char* origin = "capture-stack-trace-test";
11881 const int kOverviewTest = 1;
11882 const int kDetailedTest = 2;
11883
11884 ASSERT(args.Length() == 1);
11885
11886 int testGroup = args[0]->Int32Value();
11887 if (testGroup == kOverviewTest) {
11888 v8::Handle<v8::StackTrace> stackTrace =
11889 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kOverview);
11890 CHECK_EQ(4, stackTrace->GetFrameCount());
11891 checkStackFrame(origin, "bar", 2, 10, false, false,
11892 stackTrace->GetFrame(0));
11893 checkStackFrame(origin, "foo", 6, 3, false, false,
11894 stackTrace->GetFrame(1));
11895 checkStackFrame(NULL, "", 1, 1, false, false,
11896 stackTrace->GetFrame(2));
11897 // The last frame is an anonymous function that has the initial call.
11898 checkStackFrame(origin, "", 8, 7, false, false,
11899 stackTrace->GetFrame(3));
11900
11901 CHECK(stackTrace->AsArray()->IsArray());
11902 } else if (testGroup == kDetailedTest) {
11903 v8::Handle<v8::StackTrace> stackTrace =
11904 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kDetailed);
11905 CHECK_EQ(4, stackTrace->GetFrameCount());
11906 checkStackFrame(origin, "bat", 4, 22, false, false,
11907 stackTrace->GetFrame(0));
11908 checkStackFrame(origin, "baz", 8, 3, false, true,
11909 stackTrace->GetFrame(1));
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010011910#ifdef ENABLE_DEBUGGER_SUPPORT
11911 bool is_eval = true;
11912#else // ENABLE_DEBUGGER_SUPPORT
11913 bool is_eval = false;
11914#endif // ENABLE_DEBUGGER_SUPPORT
11915
11916 checkStackFrame(NULL, "", 1, 1, is_eval, false,
Kristian Monsen25f61362010-05-21 11:50:48 +010011917 stackTrace->GetFrame(2));
11918 // The last frame is an anonymous function that has the initial call to foo.
11919 checkStackFrame(origin, "", 10, 1, false, false,
11920 stackTrace->GetFrame(3));
11921
11922 CHECK(stackTrace->AsArray()->IsArray());
11923 }
11924 return v8::Undefined();
11925}
11926
11927
11928// Tests the C++ StackTrace API.
Ben Murdochb0fe1622011-05-05 13:52:32 +010011929// TODO(3074796): Reenable this as a THREADED_TEST once it passes.
11930// THREADED_TEST(CaptureStackTrace) {
11931TEST(CaptureStackTrace) {
Kristian Monsen25f61362010-05-21 11:50:48 +010011932 v8::HandleScope scope;
11933 v8::Handle<v8::String> origin = v8::String::New("capture-stack-trace-test");
11934 Local<ObjectTemplate> templ = ObjectTemplate::New();
11935 templ->Set(v8_str("AnalyzeStackInNativeCode"),
11936 v8::FunctionTemplate::New(AnalyzeStackInNativeCode));
11937 LocalContext context(0, templ);
11938
11939 // Test getting OVERVIEW information. Should ignore information that is not
11940 // script name, function name, line number, and column offset.
11941 const char *overview_source =
11942 "function bar() {\n"
11943 " var y; AnalyzeStackInNativeCode(1);\n"
11944 "}\n"
11945 "function foo() {\n"
11946 "\n"
11947 " bar();\n"
11948 "}\n"
11949 "var x;eval('new foo();');";
11950 v8::Handle<v8::String> overview_src = v8::String::New(overview_source);
11951 v8::Handle<Value> overview_result =
11952 v8::Script::New(overview_src, origin)->Run();
11953 ASSERT(!overview_result.IsEmpty());
11954 ASSERT(overview_result->IsObject());
11955
11956 // Test getting DETAILED information.
11957 const char *detailed_source =
11958 "function bat() {AnalyzeStackInNativeCode(2);\n"
11959 "}\n"
11960 "\n"
11961 "function baz() {\n"
11962 " bat();\n"
11963 "}\n"
11964 "eval('new baz();');";
11965 v8::Handle<v8::String> detailed_src = v8::String::New(detailed_source);
11966 // Make the script using a non-zero line and column offset.
11967 v8::Handle<v8::Integer> line_offset = v8::Integer::New(3);
11968 v8::Handle<v8::Integer> column_offset = v8::Integer::New(5);
11969 v8::ScriptOrigin detailed_origin(origin, line_offset, column_offset);
11970 v8::Handle<v8::Script> detailed_script(
11971 v8::Script::New(detailed_src, &detailed_origin));
11972 v8::Handle<Value> detailed_result = detailed_script->Run();
11973 ASSERT(!detailed_result.IsEmpty());
11974 ASSERT(detailed_result->IsObject());
11975}
11976
11977
Ben Murdoch3bec4d22010-07-22 14:51:16 +010011978static void StackTraceForUncaughtExceptionListener(
11979 v8::Handle<v8::Message> message,
11980 v8::Handle<Value>) {
11981 v8::Handle<v8::StackTrace> stack_trace = message->GetStackTrace();
11982 CHECK_EQ(2, stack_trace->GetFrameCount());
11983 checkStackFrame("origin", "foo", 2, 3, false, false,
11984 stack_trace->GetFrame(0));
11985 checkStackFrame("origin", "bar", 5, 3, false, false,
11986 stack_trace->GetFrame(1));
11987}
11988
11989TEST(CaptureStackTraceForUncaughtException) {
11990 report_count = 0;
11991 v8::HandleScope scope;
11992 LocalContext env;
11993 v8::V8::AddMessageListener(StackTraceForUncaughtExceptionListener);
11994 v8::V8::SetCaptureStackTraceForUncaughtExceptions(true);
11995
11996 Script::Compile(v8_str("function foo() {\n"
11997 " throw 1;\n"
11998 "};\n"
11999 "function bar() {\n"
12000 " foo();\n"
12001 "};"),
12002 v8_str("origin"))->Run();
12003 v8::Local<v8::Object> global = env->Global();
12004 Local<Value> trouble = global->Get(v8_str("bar"));
12005 CHECK(trouble->IsFunction());
12006 Function::Cast(*trouble)->Call(global, 0, NULL);
12007 v8::V8::SetCaptureStackTraceForUncaughtExceptions(false);
12008 v8::V8::RemoveMessageListeners(StackTraceForUncaughtExceptionListener);
12009}
12010
12011
Steve Block1e0659c2011-05-24 12:43:12 +010012012TEST(CaptureStackTraceForUncaughtExceptionAndSetters) {
12013 v8::HandleScope scope;
12014 LocalContext env;
12015 v8::V8::SetCaptureStackTraceForUncaughtExceptions(true,
12016 1024,
12017 v8::StackTrace::kDetailed);
12018
12019 CompileRun(
12020 "var setters = ['column', 'lineNumber', 'scriptName',\n"
12021 " 'scriptNameOrSourceURL', 'functionName', 'isEval',\n"
12022 " 'isConstructor'];\n"
12023 "for (var i = 0; i < setters.length; i++) {\n"
12024 " var prop = setters[i];\n"
12025 " Object.prototype.__defineSetter__(prop, function() { throw prop; });\n"
12026 "}\n");
12027 CompileRun("throw 'exception';");
12028 v8::V8::SetCaptureStackTraceForUncaughtExceptions(false);
12029}
12030
12031
Ben Murdochf87a2032010-10-22 12:50:53 +010012032v8::Handle<Value> AnalyzeStackOfEvalWithSourceURL(const v8::Arguments& args) {
12033 v8::HandleScope scope;
12034 v8::Handle<v8::StackTrace> stackTrace =
12035 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kDetailed);
12036 CHECK_EQ(5, stackTrace->GetFrameCount());
12037 v8::Handle<v8::String> url = v8_str("eval_url");
12038 for (int i = 0; i < 3; i++) {
12039 v8::Handle<v8::String> name =
12040 stackTrace->GetFrame(i)->GetScriptNameOrSourceURL();
12041 CHECK(!name.IsEmpty());
12042 CHECK_EQ(url, name);
12043 }
12044 return v8::Undefined();
12045}
12046
12047
12048TEST(SourceURLInStackTrace) {
12049 v8::HandleScope scope;
12050 Local<ObjectTemplate> templ = ObjectTemplate::New();
12051 templ->Set(v8_str("AnalyzeStackOfEvalWithSourceURL"),
12052 v8::FunctionTemplate::New(AnalyzeStackOfEvalWithSourceURL));
12053 LocalContext context(0, templ);
12054
12055 const char *source =
12056 "function outer() {\n"
12057 "function bar() {\n"
12058 " AnalyzeStackOfEvalWithSourceURL();\n"
12059 "}\n"
12060 "function foo() {\n"
12061 "\n"
12062 " bar();\n"
12063 "}\n"
12064 "foo();\n"
12065 "}\n"
12066 "eval('(' + outer +')()//@ sourceURL=eval_url');";
12067 CHECK(CompileRun(source)->IsUndefined());
12068}
12069
12070
Steve Block3ce2e202009-11-05 08:53:23 +000012071// Test that idle notification can be handled and eventually returns true.
Steve Blocka7e24c12009-10-30 11:49:00 +000012072THREADED_TEST(IdleNotification) {
Steve Block3ce2e202009-11-05 08:53:23 +000012073 bool rv = false;
12074 for (int i = 0; i < 100; i++) {
12075 rv = v8::V8::IdleNotification();
12076 if (rv)
12077 break;
12078 }
12079 CHECK(rv == true);
Steve Blocka7e24c12009-10-30 11:49:00 +000012080}
12081
12082
12083static uint32_t* stack_limit;
12084
12085static v8::Handle<Value> GetStackLimitCallback(const v8::Arguments& args) {
Steve Block44f0eee2011-05-26 01:26:41 +010012086 stack_limit = reinterpret_cast<uint32_t*>(
12087 i::Isolate::Current()->stack_guard()->real_climit());
Steve Blocka7e24c12009-10-30 11:49:00 +000012088 return v8::Undefined();
12089}
12090
12091
12092// Uses the address of a local variable to determine the stack top now.
12093// Given a size, returns an address that is that far from the current
12094// top of stack.
12095static uint32_t* ComputeStackLimit(uint32_t size) {
12096 uint32_t* answer = &size - (size / sizeof(size));
12097 // If the size is very large and the stack is very near the bottom of
12098 // memory then the calculation above may wrap around and give an address
12099 // that is above the (downwards-growing) stack. In that case we return
12100 // a very low address.
12101 if (answer > &size) return reinterpret_cast<uint32_t*>(sizeof(size));
12102 return answer;
12103}
12104
12105
12106TEST(SetResourceConstraints) {
12107 static const int K = 1024;
12108 uint32_t* set_limit = ComputeStackLimit(128 * K);
12109
12110 // Set stack limit.
12111 v8::ResourceConstraints constraints;
12112 constraints.set_stack_limit(set_limit);
12113 CHECK(v8::SetResourceConstraints(&constraints));
12114
12115 // Execute a script.
12116 v8::HandleScope scope;
12117 LocalContext env;
12118 Local<v8::FunctionTemplate> fun_templ =
12119 v8::FunctionTemplate::New(GetStackLimitCallback);
12120 Local<Function> fun = fun_templ->GetFunction();
12121 env->Global()->Set(v8_str("get_stack_limit"), fun);
12122 CompileRun("get_stack_limit();");
12123
12124 CHECK(stack_limit == set_limit);
12125}
12126
12127
12128TEST(SetResourceConstraintsInThread) {
12129 uint32_t* set_limit;
12130 {
12131 v8::Locker locker;
12132 static const int K = 1024;
12133 set_limit = ComputeStackLimit(128 * K);
12134
12135 // Set stack limit.
12136 v8::ResourceConstraints constraints;
12137 constraints.set_stack_limit(set_limit);
12138 CHECK(v8::SetResourceConstraints(&constraints));
12139
12140 // Execute a script.
12141 v8::HandleScope scope;
12142 LocalContext env;
12143 Local<v8::FunctionTemplate> fun_templ =
12144 v8::FunctionTemplate::New(GetStackLimitCallback);
12145 Local<Function> fun = fun_templ->GetFunction();
12146 env->Global()->Set(v8_str("get_stack_limit"), fun);
12147 CompileRun("get_stack_limit();");
12148
12149 CHECK(stack_limit == set_limit);
12150 }
12151 {
12152 v8::Locker locker;
12153 CHECK(stack_limit == set_limit);
12154 }
12155}
Steve Block3ce2e202009-11-05 08:53:23 +000012156
12157
12158THREADED_TEST(GetHeapStatistics) {
12159 v8::HandleScope scope;
12160 LocalContext c1;
12161 v8::HeapStatistics heap_statistics;
Steve Blockd0582a62009-12-15 09:54:21 +000012162 CHECK_EQ(static_cast<int>(heap_statistics.total_heap_size()), 0);
12163 CHECK_EQ(static_cast<int>(heap_statistics.used_heap_size()), 0);
Steve Block3ce2e202009-11-05 08:53:23 +000012164 v8::V8::GetHeapStatistics(&heap_statistics);
Steve Blockd0582a62009-12-15 09:54:21 +000012165 CHECK_NE(static_cast<int>(heap_statistics.total_heap_size()), 0);
12166 CHECK_NE(static_cast<int>(heap_statistics.used_heap_size()), 0);
12167}
12168
12169
12170static double DoubleFromBits(uint64_t value) {
12171 double target;
12172#ifdef BIG_ENDIAN_FLOATING_POINT
12173 const int kIntSize = 4;
12174 // Somebody swapped the lower and higher half of doubles.
12175 memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize);
12176 memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize);
12177#else
12178 memcpy(&target, &value, sizeof(target));
12179#endif
12180 return target;
12181}
12182
12183
12184static uint64_t DoubleToBits(double value) {
12185 uint64_t target;
12186#ifdef BIG_ENDIAN_FLOATING_POINT
12187 const int kIntSize = 4;
12188 // Somebody swapped the lower and higher half of doubles.
12189 memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize);
12190 memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize);
12191#else
12192 memcpy(&target, &value, sizeof(target));
12193#endif
12194 return target;
12195}
12196
12197
12198static double DoubleToDateTime(double input) {
12199 double date_limit = 864e13;
12200 if (IsNaN(input) || input < -date_limit || input > date_limit) {
12201 return i::OS::nan_value();
12202 }
12203 return (input < 0) ? -(floor(-input)) : floor(input);
12204}
12205
12206// We don't have a consistent way to write 64-bit constants syntactically, so we
12207// split them into two 32-bit constants and combine them programmatically.
12208static double DoubleFromBits(uint32_t high_bits, uint32_t low_bits) {
12209 return DoubleFromBits((static_cast<uint64_t>(high_bits) << 32) | low_bits);
12210}
12211
12212
12213THREADED_TEST(QuietSignalingNaNs) {
12214 v8::HandleScope scope;
12215 LocalContext context;
12216 v8::TryCatch try_catch;
12217
12218 // Special double values.
12219 double snan = DoubleFromBits(0x7ff00000, 0x00000001);
12220 double qnan = DoubleFromBits(0x7ff80000, 0x00000000);
12221 double infinity = DoubleFromBits(0x7ff00000, 0x00000000);
12222 double max_normal = DoubleFromBits(0x7fefffff, 0xffffffffu);
12223 double min_normal = DoubleFromBits(0x00100000, 0x00000000);
12224 double max_denormal = DoubleFromBits(0x000fffff, 0xffffffffu);
12225 double min_denormal = DoubleFromBits(0x00000000, 0x00000001);
12226
12227 // Date values are capped at +/-100000000 days (times 864e5 ms per day)
12228 // on either side of the epoch.
12229 double date_limit = 864e13;
12230
12231 double test_values[] = {
12232 snan,
12233 qnan,
12234 infinity,
12235 max_normal,
12236 date_limit + 1,
12237 date_limit,
12238 min_normal,
12239 max_denormal,
12240 min_denormal,
12241 0,
12242 -0,
12243 -min_denormal,
12244 -max_denormal,
12245 -min_normal,
12246 -date_limit,
12247 -date_limit - 1,
12248 -max_normal,
12249 -infinity,
12250 -qnan,
12251 -snan
12252 };
12253 int num_test_values = 20;
12254
12255 for (int i = 0; i < num_test_values; i++) {
12256 double test_value = test_values[i];
12257
12258 // Check that Number::New preserves non-NaNs and quiets SNaNs.
12259 v8::Handle<v8::Value> number = v8::Number::New(test_value);
12260 double stored_number = number->NumberValue();
12261 if (!IsNaN(test_value)) {
12262 CHECK_EQ(test_value, stored_number);
12263 } else {
12264 uint64_t stored_bits = DoubleToBits(stored_number);
12265 // Check if quiet nan (bits 51..62 all set).
12266 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff));
12267 }
12268
12269 // Check that Date::New preserves non-NaNs in the date range and
12270 // quiets SNaNs.
12271 v8::Handle<v8::Value> date = v8::Date::New(test_value);
12272 double expected_stored_date = DoubleToDateTime(test_value);
12273 double stored_date = date->NumberValue();
12274 if (!IsNaN(expected_stored_date)) {
12275 CHECK_EQ(expected_stored_date, stored_date);
12276 } else {
12277 uint64_t stored_bits = DoubleToBits(stored_date);
12278 // Check if quiet nan (bits 51..62 all set).
12279 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff));
12280 }
12281 }
12282}
12283
12284
12285static v8::Handle<Value> SpaghettiIncident(const v8::Arguments& args) {
12286 v8::HandleScope scope;
12287 v8::TryCatch tc;
12288 v8::Handle<v8::String> str = args[0]->ToString();
12289 if (tc.HasCaught())
12290 return tc.ReThrow();
12291 return v8::Undefined();
12292}
12293
12294
12295// Test that an exception can be propagated down through a spaghetti
12296// stack using ReThrow.
12297THREADED_TEST(SpaghettiStackReThrow) {
12298 v8::HandleScope scope;
12299 LocalContext context;
12300 context->Global()->Set(
12301 v8::String::New("s"),
12302 v8::FunctionTemplate::New(SpaghettiIncident)->GetFunction());
12303 v8::TryCatch try_catch;
12304 CompileRun(
12305 "var i = 0;"
12306 "var o = {"
12307 " toString: function () {"
12308 " if (i == 10) {"
12309 " throw 'Hey!';"
12310 " } else {"
12311 " i++;"
12312 " return s(o);"
12313 " }"
12314 " }"
12315 "};"
12316 "s(o);");
12317 CHECK(try_catch.HasCaught());
12318 v8::String::Utf8Value value(try_catch.Exception());
12319 CHECK_EQ(0, strcmp(*value, "Hey!"));
12320}
12321
12322
Steve Blockd0582a62009-12-15 09:54:21 +000012323TEST(Regress528) {
12324 v8::V8::Initialize();
12325
12326 v8::HandleScope scope;
12327 v8::Persistent<Context> context;
12328 v8::Persistent<Context> other_context;
12329 int gc_count;
12330
12331 // Create a context used to keep the code from aging in the compilation
12332 // cache.
12333 other_context = Context::New();
12334
12335 // Context-dependent context data creates reference from the compilation
12336 // cache to the global object.
12337 const char* source_simple = "1";
12338 context = Context::New();
12339 {
12340 v8::HandleScope scope;
12341
12342 context->Enter();
12343 Local<v8::String> obj = v8::String::New("");
12344 context->SetData(obj);
12345 CompileRun(source_simple);
12346 context->Exit();
12347 }
12348 context.Dispose();
12349 for (gc_count = 1; gc_count < 10; gc_count++) {
12350 other_context->Enter();
12351 CompileRun(source_simple);
12352 other_context->Exit();
Steve Block44f0eee2011-05-26 01:26:41 +010012353 HEAP->CollectAllGarbage(false);
Steve Blockd0582a62009-12-15 09:54:21 +000012354 if (GetGlobalObjectsCount() == 1) break;
12355 }
12356 CHECK_GE(2, gc_count);
12357 CHECK_EQ(1, GetGlobalObjectsCount());
12358
12359 // Eval in a function creates reference from the compilation cache to the
12360 // global object.
12361 const char* source_eval = "function f(){eval('1')}; f()";
12362 context = Context::New();
12363 {
12364 v8::HandleScope scope;
12365
12366 context->Enter();
12367 CompileRun(source_eval);
12368 context->Exit();
12369 }
12370 context.Dispose();
12371 for (gc_count = 1; gc_count < 10; gc_count++) {
12372 other_context->Enter();
12373 CompileRun(source_eval);
12374 other_context->Exit();
Steve Block44f0eee2011-05-26 01:26:41 +010012375 HEAP->CollectAllGarbage(false);
Steve Blockd0582a62009-12-15 09:54:21 +000012376 if (GetGlobalObjectsCount() == 1) break;
12377 }
12378 CHECK_GE(2, gc_count);
12379 CHECK_EQ(1, GetGlobalObjectsCount());
12380
12381 // Looking up the line number for an exception creates reference from the
12382 // compilation cache to the global object.
12383 const char* source_exception = "function f(){throw 1;} f()";
12384 context = Context::New();
12385 {
12386 v8::HandleScope scope;
12387
12388 context->Enter();
12389 v8::TryCatch try_catch;
12390 CompileRun(source_exception);
12391 CHECK(try_catch.HasCaught());
12392 v8::Handle<v8::Message> message = try_catch.Message();
12393 CHECK(!message.IsEmpty());
12394 CHECK_EQ(1, message->GetLineNumber());
12395 context->Exit();
12396 }
12397 context.Dispose();
12398 for (gc_count = 1; gc_count < 10; gc_count++) {
12399 other_context->Enter();
12400 CompileRun(source_exception);
12401 other_context->Exit();
Steve Block44f0eee2011-05-26 01:26:41 +010012402 HEAP->CollectAllGarbage(false);
Steve Blockd0582a62009-12-15 09:54:21 +000012403 if (GetGlobalObjectsCount() == 1) break;
12404 }
12405 CHECK_GE(2, gc_count);
12406 CHECK_EQ(1, GetGlobalObjectsCount());
12407
12408 other_context.Dispose();
Steve Block3ce2e202009-11-05 08:53:23 +000012409}
Andrei Popescu402d9372010-02-26 13:31:12 +000012410
12411
12412THREADED_TEST(ScriptOrigin) {
12413 v8::HandleScope scope;
12414 LocalContext env;
12415 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test"));
12416 v8::Handle<v8::String> script = v8::String::New(
12417 "function f() {}\n\nfunction g() {}");
12418 v8::Script::Compile(script, &origin)->Run();
12419 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(
12420 env->Global()->Get(v8::String::New("f")));
12421 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast(
12422 env->Global()->Get(v8::String::New("g")));
12423
12424 v8::ScriptOrigin script_origin_f = f->GetScriptOrigin();
12425 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_f.ResourceName()));
12426 CHECK_EQ(0, script_origin_f.ResourceLineOffset()->Int32Value());
12427
12428 v8::ScriptOrigin script_origin_g = g->GetScriptOrigin();
12429 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_g.ResourceName()));
12430 CHECK_EQ(0, script_origin_g.ResourceLineOffset()->Int32Value());
12431}
12432
12433
12434THREADED_TEST(ScriptLineNumber) {
12435 v8::HandleScope scope;
12436 LocalContext env;
12437 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test"));
12438 v8::Handle<v8::String> script = v8::String::New(
12439 "function f() {}\n\nfunction g() {}");
12440 v8::Script::Compile(script, &origin)->Run();
12441 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(
12442 env->Global()->Get(v8::String::New("f")));
12443 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast(
12444 env->Global()->Get(v8::String::New("g")));
12445 CHECK_EQ(0, f->GetScriptLineNumber());
12446 CHECK_EQ(2, g->GetScriptLineNumber());
12447}
12448
12449
12450static v8::Handle<Value> GetterWhichReturns42(Local<String> name,
12451 const AccessorInfo& info) {
12452 return v8_num(42);
12453}
12454
12455
12456static void SetterWhichSetsYOnThisTo23(Local<String> name,
12457 Local<Value> value,
12458 const AccessorInfo& info) {
12459 info.This()->Set(v8_str("y"), v8_num(23));
12460}
12461
12462
Steve Block6ded16b2010-05-10 14:33:55 +010012463TEST(SetterOnConstructorPrototype) {
Andrei Popescu402d9372010-02-26 13:31:12 +000012464 v8::HandleScope scope;
12465 Local<ObjectTemplate> templ = ObjectTemplate::New();
12466 templ->SetAccessor(v8_str("x"),
12467 GetterWhichReturns42,
12468 SetterWhichSetsYOnThisTo23);
12469 LocalContext context;
12470 context->Global()->Set(v8_str("P"), templ->NewInstance());
12471 CompileRun("function C1() {"
12472 " this.x = 23;"
12473 "};"
12474 "C1.prototype = P;"
12475 "function C2() {"
12476 " this.x = 23"
12477 "};"
12478 "C2.prototype = { };"
12479 "C2.prototype.__proto__ = P;");
12480
12481 v8::Local<v8::Script> script;
12482 script = v8::Script::Compile(v8_str("new C1();"));
12483 for (int i = 0; i < 10; i++) {
12484 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
12485 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value());
12486 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value());
12487 }
12488
12489 script = v8::Script::Compile(v8_str("new C2();"));
12490 for (int i = 0; i < 10; i++) {
12491 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run());
12492 CHECK_EQ(42, c2->Get(v8_str("x"))->Int32Value());
12493 CHECK_EQ(23, c2->Get(v8_str("y"))->Int32Value());
12494 }
12495}
12496
12497
12498static v8::Handle<Value> NamedPropertyGetterWhichReturns42(
12499 Local<String> name, const AccessorInfo& info) {
12500 return v8_num(42);
12501}
12502
12503
12504static v8::Handle<Value> NamedPropertySetterWhichSetsYOnThisTo23(
12505 Local<String> name, Local<Value> value, const AccessorInfo& info) {
12506 if (name->Equals(v8_str("x"))) {
12507 info.This()->Set(v8_str("y"), v8_num(23));
12508 }
12509 return v8::Handle<Value>();
12510}
12511
12512
12513THREADED_TEST(InterceptorOnConstructorPrototype) {
12514 v8::HandleScope scope;
12515 Local<ObjectTemplate> templ = ObjectTemplate::New();
12516 templ->SetNamedPropertyHandler(NamedPropertyGetterWhichReturns42,
12517 NamedPropertySetterWhichSetsYOnThisTo23);
12518 LocalContext context;
12519 context->Global()->Set(v8_str("P"), templ->NewInstance());
12520 CompileRun("function C1() {"
12521 " this.x = 23;"
12522 "};"
12523 "C1.prototype = P;"
12524 "function C2() {"
12525 " this.x = 23"
12526 "};"
12527 "C2.prototype = { };"
12528 "C2.prototype.__proto__ = P;");
12529
12530 v8::Local<v8::Script> script;
12531 script = v8::Script::Compile(v8_str("new C1();"));
12532 for (int i = 0; i < 10; i++) {
12533 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
12534 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value());
12535 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value());
12536 }
12537
12538 script = v8::Script::Compile(v8_str("new C2();"));
12539 for (int i = 0; i < 10; i++) {
12540 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run());
12541 CHECK_EQ(23, c2->Get(v8_str("x"))->Int32Value());
12542 CHECK_EQ(42, c2->Get(v8_str("y"))->Int32Value());
12543 }
12544}
Steve Block6ded16b2010-05-10 14:33:55 +010012545
12546
12547TEST(Bug618) {
12548 const char* source = "function C1() {"
12549 " this.x = 23;"
12550 "};"
12551 "C1.prototype = P;";
12552
12553 v8::HandleScope scope;
12554 LocalContext context;
12555 v8::Local<v8::Script> script;
12556
12557 // Use a simple object as prototype.
12558 v8::Local<v8::Object> prototype = v8::Object::New();
12559 prototype->Set(v8_str("y"), v8_num(42));
12560 context->Global()->Set(v8_str("P"), prototype);
12561
12562 // This compile will add the code to the compilation cache.
12563 CompileRun(source);
12564
12565 script = v8::Script::Compile(v8_str("new C1();"));
Kristian Monsen0d5e1162010-09-30 15:31:59 +010012566 // Allow enough iterations for the inobject slack tracking logic
12567 // to finalize instance size and install the fast construct stub.
12568 for (int i = 0; i < 256; i++) {
Steve Block6ded16b2010-05-10 14:33:55 +010012569 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
12570 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value());
12571 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value());
12572 }
12573
12574 // Use an API object with accessors as prototype.
12575 Local<ObjectTemplate> templ = ObjectTemplate::New();
12576 templ->SetAccessor(v8_str("x"),
12577 GetterWhichReturns42,
12578 SetterWhichSetsYOnThisTo23);
12579 context->Global()->Set(v8_str("P"), templ->NewInstance());
12580
12581 // This compile will get the code from the compilation cache.
12582 CompileRun(source);
12583
12584 script = v8::Script::Compile(v8_str("new C1();"));
12585 for (int i = 0; i < 10; i++) {
12586 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
12587 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value());
12588 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value());
12589 }
12590}
12591
12592int prologue_call_count = 0;
12593int epilogue_call_count = 0;
12594int prologue_call_count_second = 0;
12595int epilogue_call_count_second = 0;
12596
12597void PrologueCallback(v8::GCType, v8::GCCallbackFlags) {
12598 ++prologue_call_count;
12599}
12600
12601void EpilogueCallback(v8::GCType, v8::GCCallbackFlags) {
12602 ++epilogue_call_count;
12603}
12604
12605void PrologueCallbackSecond(v8::GCType, v8::GCCallbackFlags) {
12606 ++prologue_call_count_second;
12607}
12608
12609void EpilogueCallbackSecond(v8::GCType, v8::GCCallbackFlags) {
12610 ++epilogue_call_count_second;
12611}
12612
12613TEST(GCCallbacks) {
12614 LocalContext context;
12615
12616 v8::V8::AddGCPrologueCallback(PrologueCallback);
12617 v8::V8::AddGCEpilogueCallback(EpilogueCallback);
12618 CHECK_EQ(0, prologue_call_count);
12619 CHECK_EQ(0, epilogue_call_count);
Steve Block44f0eee2011-05-26 01:26:41 +010012620 HEAP->CollectAllGarbage(false);
Steve Block6ded16b2010-05-10 14:33:55 +010012621 CHECK_EQ(1, prologue_call_count);
12622 CHECK_EQ(1, epilogue_call_count);
12623 v8::V8::AddGCPrologueCallback(PrologueCallbackSecond);
12624 v8::V8::AddGCEpilogueCallback(EpilogueCallbackSecond);
Steve Block44f0eee2011-05-26 01:26:41 +010012625 HEAP->CollectAllGarbage(false);
Steve Block6ded16b2010-05-10 14:33:55 +010012626 CHECK_EQ(2, prologue_call_count);
12627 CHECK_EQ(2, epilogue_call_count);
12628 CHECK_EQ(1, prologue_call_count_second);
12629 CHECK_EQ(1, epilogue_call_count_second);
12630 v8::V8::RemoveGCPrologueCallback(PrologueCallback);
12631 v8::V8::RemoveGCEpilogueCallback(EpilogueCallback);
Steve Block44f0eee2011-05-26 01:26:41 +010012632 HEAP->CollectAllGarbage(false);
Steve Block6ded16b2010-05-10 14:33:55 +010012633 CHECK_EQ(2, prologue_call_count);
12634 CHECK_EQ(2, epilogue_call_count);
12635 CHECK_EQ(2, prologue_call_count_second);
12636 CHECK_EQ(2, epilogue_call_count_second);
12637 v8::V8::RemoveGCPrologueCallback(PrologueCallbackSecond);
12638 v8::V8::RemoveGCEpilogueCallback(EpilogueCallbackSecond);
Steve Block44f0eee2011-05-26 01:26:41 +010012639 HEAP->CollectAllGarbage(false);
Steve Block6ded16b2010-05-10 14:33:55 +010012640 CHECK_EQ(2, prologue_call_count);
12641 CHECK_EQ(2, epilogue_call_count);
12642 CHECK_EQ(2, prologue_call_count_second);
12643 CHECK_EQ(2, epilogue_call_count_second);
12644}
Kristian Monsen25f61362010-05-21 11:50:48 +010012645
12646
12647THREADED_TEST(AddToJSFunctionResultCache) {
12648 i::FLAG_allow_natives_syntax = true;
12649 v8::HandleScope scope;
12650
12651 LocalContext context;
12652
12653 const char* code =
12654 "(function() {"
12655 " var key0 = 'a';"
12656 " var key1 = 'b';"
12657 " var r0 = %_GetFromCache(0, key0);"
12658 " var r1 = %_GetFromCache(0, key1);"
12659 " var r0_ = %_GetFromCache(0, key0);"
12660 " if (r0 !== r0_)"
12661 " return 'Different results for ' + key0 + ': ' + r0 + ' vs. ' + r0_;"
12662 " var r1_ = %_GetFromCache(0, key1);"
12663 " if (r1 !== r1_)"
12664 " return 'Different results for ' + key1 + ': ' + r1 + ' vs. ' + r1_;"
12665 " return 'PASSED';"
12666 "})()";
Steve Block44f0eee2011-05-26 01:26:41 +010012667 HEAP->ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010012668 ExpectString(code, "PASSED");
12669}
12670
12671
12672static const int k0CacheSize = 16;
12673
12674THREADED_TEST(FillJSFunctionResultCache) {
12675 i::FLAG_allow_natives_syntax = true;
12676 v8::HandleScope scope;
12677
12678 LocalContext context;
12679
12680 const char* code =
12681 "(function() {"
12682 " var k = 'a';"
12683 " var r = %_GetFromCache(0, k);"
12684 " for (var i = 0; i < 16; i++) {"
12685 " %_GetFromCache(0, 'a' + i);"
12686 " };"
12687 " if (r === %_GetFromCache(0, k))"
12688 " return 'FAILED: k0CacheSize is too small';"
12689 " return 'PASSED';"
12690 "})()";
Steve Block44f0eee2011-05-26 01:26:41 +010012691 HEAP->ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010012692 ExpectString(code, "PASSED");
12693}
12694
12695
12696THREADED_TEST(RoundRobinGetFromCache) {
12697 i::FLAG_allow_natives_syntax = true;
12698 v8::HandleScope scope;
12699
12700 LocalContext context;
12701
12702 const char* code =
12703 "(function() {"
12704 " var keys = [];"
12705 " for (var i = 0; i < 16; i++) keys.push(i);"
12706 " var values = [];"
12707 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);"
12708 " for (var i = 0; i < 16; i++) {"
12709 " var v = %_GetFromCache(0, keys[i]);"
12710 " if (v !== values[i])"
12711 " return 'Wrong value for ' + "
12712 " keys[i] + ': ' + v + ' vs. ' + values[i];"
12713 " };"
12714 " return 'PASSED';"
12715 "})()";
Steve Block44f0eee2011-05-26 01:26:41 +010012716 HEAP->ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010012717 ExpectString(code, "PASSED");
12718}
12719
12720
12721THREADED_TEST(ReverseGetFromCache) {
12722 i::FLAG_allow_natives_syntax = true;
12723 v8::HandleScope scope;
12724
12725 LocalContext context;
12726
12727 const char* code =
12728 "(function() {"
12729 " var keys = [];"
12730 " for (var i = 0; i < 16; i++) keys.push(i);"
12731 " var values = [];"
12732 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);"
12733 " for (var i = 15; i >= 16; i--) {"
12734 " var v = %_GetFromCache(0, keys[i]);"
12735 " if (v !== values[i])"
12736 " return 'Wrong value for ' + "
12737 " keys[i] + ': ' + v + ' vs. ' + values[i];"
12738 " };"
12739 " return 'PASSED';"
12740 "})()";
Steve Block44f0eee2011-05-26 01:26:41 +010012741 HEAP->ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010012742 ExpectString(code, "PASSED");
12743}
12744
12745
12746THREADED_TEST(TestEviction) {
12747 i::FLAG_allow_natives_syntax = true;
12748 v8::HandleScope scope;
12749
12750 LocalContext context;
12751
12752 const char* code =
12753 "(function() {"
12754 " for (var i = 0; i < 2*16; i++) {"
12755 " %_GetFromCache(0, 'a' + i);"
12756 " };"
12757 " return 'PASSED';"
12758 "})()";
Steve Block44f0eee2011-05-26 01:26:41 +010012759 HEAP->ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010012760 ExpectString(code, "PASSED");
12761}
Steve Block8defd9f2010-07-08 12:39:36 +010012762
12763
12764THREADED_TEST(TwoByteStringInAsciiCons) {
12765 // See Chromium issue 47824.
12766 v8::HandleScope scope;
12767
12768 LocalContext context;
12769 const char* init_code =
12770 "var str1 = 'abelspendabel';"
12771 "var str2 = str1 + str1 + str1;"
12772 "str2;";
12773 Local<Value> result = CompileRun(init_code);
12774
12775 CHECK(result->IsString());
12776 i::Handle<i::String> string = v8::Utils::OpenHandle(String::Cast(*result));
12777 int length = string->length();
12778 CHECK(string->IsAsciiRepresentation());
12779
12780 FlattenString(string);
12781 i::Handle<i::String> flat_string = FlattenGetString(string);
12782
12783 CHECK(string->IsAsciiRepresentation());
12784 CHECK(flat_string->IsAsciiRepresentation());
12785
12786 // Create external resource.
12787 uint16_t* uc16_buffer = new uint16_t[length + 1];
12788
12789 i::String::WriteToFlat(*flat_string, uc16_buffer, 0, length);
12790 uc16_buffer[length] = 0;
12791
12792 TestResource resource(uc16_buffer);
12793
12794 flat_string->MakeExternal(&resource);
12795
12796 CHECK(flat_string->IsTwoByteRepresentation());
12797
12798 // At this point, we should have a Cons string which is flat and ASCII,
12799 // with a first half that is a two-byte string (although it only contains
12800 // ASCII characters). This is a valid sequence of steps, and it can happen
12801 // in real pages.
12802
12803 CHECK(string->IsAsciiRepresentation());
12804 i::ConsString* cons = i::ConsString::cast(*string);
12805 CHECK_EQ(0, cons->second()->length());
12806 CHECK(cons->first()->IsTwoByteRepresentation());
12807
12808 // Check that some string operations work.
12809
12810 // Atom RegExp.
12811 Local<Value> reresult = CompileRun("str2.match(/abel/g).length;");
12812 CHECK_EQ(6, reresult->Int32Value());
12813
12814 // Nonatom RegExp.
12815 reresult = CompileRun("str2.match(/abe./g).length;");
12816 CHECK_EQ(6, reresult->Int32Value());
12817
12818 reresult = CompileRun("str2.search(/bel/g);");
12819 CHECK_EQ(1, reresult->Int32Value());
12820
12821 reresult = CompileRun("str2.search(/be./g);");
12822 CHECK_EQ(1, reresult->Int32Value());
12823
12824 ExpectTrue("/bel/g.test(str2);");
12825
12826 ExpectTrue("/be./g.test(str2);");
12827
12828 reresult = CompileRun("/bel/g.exec(str2);");
12829 CHECK(!reresult->IsNull());
12830
12831 reresult = CompileRun("/be./g.exec(str2);");
12832 CHECK(!reresult->IsNull());
12833
12834 ExpectString("str2.substring(2, 10);", "elspenda");
12835
12836 ExpectString("str2.substring(2, 20);", "elspendabelabelspe");
12837
12838 ExpectString("str2.charAt(2);", "e");
12839
12840 reresult = CompileRun("str2.charCodeAt(2);");
12841 CHECK_EQ(static_cast<int32_t>('e'), reresult->Int32Value());
12842}
Iain Merrick75681382010-08-19 15:07:18 +010012843
12844
12845// Failed access check callback that performs a GC on each invocation.
12846void FailedAccessCheckCallbackGC(Local<v8::Object> target,
12847 v8::AccessType type,
12848 Local<v8::Value> data) {
Steve Block44f0eee2011-05-26 01:26:41 +010012849 HEAP->CollectAllGarbage(true);
Iain Merrick75681382010-08-19 15:07:18 +010012850}
12851
12852
12853TEST(GCInFailedAccessCheckCallback) {
12854 // Install a failed access check callback that performs a GC on each
12855 // invocation. Then force the callback to be called from va
12856
12857 v8::V8::Initialize();
12858 v8::V8::SetFailedAccessCheckCallbackFunction(&FailedAccessCheckCallbackGC);
12859
12860 v8::HandleScope scope;
12861
12862 // Create an ObjectTemplate for global objects and install access
12863 // check callbacks that will block access.
12864 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
12865 global_template->SetAccessCheckCallbacks(NamedGetAccessBlocker,
12866 IndexedGetAccessBlocker,
12867 v8::Handle<v8::Value>(),
12868 false);
12869
12870 // Create a context and set an x property on it's global object.
12871 LocalContext context0(NULL, global_template);
12872 context0->Global()->Set(v8_str("x"), v8_num(42));
12873 v8::Handle<v8::Object> global0 = context0->Global();
12874
12875 // Create a context with a different security token so that the
12876 // failed access check callback will be called on each access.
12877 LocalContext context1(NULL, global_template);
12878 context1->Global()->Set(v8_str("other"), global0);
12879
12880 // Get property with failed access check.
12881 ExpectUndefined("other.x");
12882
12883 // Get element with failed access check.
12884 ExpectUndefined("other[0]");
12885
12886 // Set property with failed access check.
12887 v8::Handle<v8::Value> result = CompileRun("other.x = new Object()");
12888 CHECK(result->IsObject());
12889
12890 // Set element with failed access check.
12891 result = CompileRun("other[0] = new Object()");
12892 CHECK(result->IsObject());
12893
12894 // Get property attribute with failed access check.
12895 ExpectFalse("\'x\' in other");
12896
12897 // Get property attribute for element with failed access check.
12898 ExpectFalse("0 in other");
12899
12900 // Delete property.
12901 ExpectFalse("delete other.x");
12902
12903 // Delete element.
12904 CHECK_EQ(false, global0->Delete(0));
12905
12906 // DefineAccessor.
12907 CHECK_EQ(false,
12908 global0->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("x")));
12909
12910 // Define JavaScript accessor.
12911 ExpectUndefined("Object.prototype.__defineGetter__.call("
12912 " other, \'x\', function() { return 42; })");
12913
12914 // LookupAccessor.
12915 ExpectUndefined("Object.prototype.__lookupGetter__.call("
12916 " other, \'x\')");
12917
12918 // HasLocalElement.
12919 ExpectFalse("Object.prototype.hasOwnProperty.call(other, \'0\')");
12920
12921 CHECK_EQ(false, global0->HasRealIndexedProperty(0));
12922 CHECK_EQ(false, global0->HasRealNamedProperty(v8_str("x")));
12923 CHECK_EQ(false, global0->HasRealNamedCallbackProperty(v8_str("x")));
12924
12925 // Reset the failed access check callback so it does not influence
12926 // the other tests.
12927 v8::V8::SetFailedAccessCheckCallbackFunction(NULL);
12928}
Kristian Monsen0d5e1162010-09-30 15:31:59 +010012929
Steve Block44f0eee2011-05-26 01:26:41 +010012930TEST(DefaultIsolateGetCurrent) {
12931 CHECK(v8::Isolate::GetCurrent() != NULL);
12932 v8::Isolate* isolate = v8::Isolate::GetCurrent();
12933 CHECK(reinterpret_cast<i::Isolate*>(isolate)->IsDefaultIsolate());
12934 printf("*** %s\n", "DefaultIsolateGetCurrent success");
12935}
12936
12937TEST(IsolateNewDispose) {
12938 v8::Isolate* current_isolate = v8::Isolate::GetCurrent();
12939 v8::Isolate* isolate = v8::Isolate::New();
12940 CHECK(isolate != NULL);
12941 CHECK(!reinterpret_cast<i::Isolate*>(isolate)->IsDefaultIsolate());
12942 CHECK(current_isolate != isolate);
12943 CHECK(current_isolate == v8::Isolate::GetCurrent());
12944
12945 v8::V8::SetFatalErrorHandler(StoringErrorCallback);
12946 last_location = last_message = NULL;
12947 isolate->Dispose();
12948 CHECK_EQ(last_location, NULL);
12949 CHECK_EQ(last_message, NULL);
12950}
12951
12952TEST(IsolateEnterExitDefault) {
12953 v8::HandleScope scope;
12954 LocalContext context;
12955 v8::Isolate* current_isolate = v8::Isolate::GetCurrent();
12956 CHECK(current_isolate != NULL); // Default isolate.
12957 ExpectString("'hello'", "hello");
12958 current_isolate->Enter();
12959 ExpectString("'still working'", "still working");
12960 current_isolate->Exit();
12961 ExpectString("'still working 2'", "still working 2");
12962 current_isolate->Exit();
12963 // Default isolate is always, well, 'default current'.
12964 CHECK_EQ(v8::Isolate::GetCurrent(), current_isolate);
12965 // Still working since default isolate is auto-entering any thread
12966 // that has no isolate and attempts to execute V8 APIs.
12967 ExpectString("'still working 3'", "still working 3");
12968}
12969
12970TEST(DisposeDefaultIsolate) {
12971 v8::V8::SetFatalErrorHandler(StoringErrorCallback);
12972
12973 // Run some V8 code to trigger default isolate to become 'current'.
12974 v8::HandleScope scope;
12975 LocalContext context;
12976 ExpectString("'run some V8'", "run some V8");
12977
12978 v8::Isolate* isolate = v8::Isolate::GetCurrent();
12979 CHECK(reinterpret_cast<i::Isolate*>(isolate)->IsDefaultIsolate());
12980 last_location = last_message = NULL;
12981 isolate->Dispose();
12982 // It is not possible to dispose default isolate via Isolate API.
12983 CHECK_NE(last_location, NULL);
12984 CHECK_NE(last_message, NULL);
12985}
12986
12987TEST(RunDefaultAndAnotherIsolate) {
12988 v8::HandleScope scope;
12989 LocalContext context;
12990
12991 // Enter new isolate.
12992 v8::Isolate* isolate = v8::Isolate::New();
12993 CHECK(isolate);
12994 isolate->Enter();
12995 { // Need this block because subsequent Exit() will deallocate Heap,
12996 // so we need all scope objects to be deconstructed when it happens.
12997 v8::HandleScope scope_new;
12998 LocalContext context_new;
12999
13000 // Run something in new isolate.
13001 CompileRun("var foo = 153;");
13002 ExpectTrue("function f() { return foo == 153; }; f()");
13003 }
13004 isolate->Exit();
13005
13006 // This runs automatically in default isolate.
13007 // Variables in another isolate should be not available.
13008 ExpectTrue("function f() {"
13009 " try {"
13010 " foo;"
13011 " return false;"
13012 " } catch(e) {"
13013 " return true;"
13014 " }"
13015 "};"
13016 "var bar = 371;"
13017 "f()");
13018
13019 v8::V8::SetFatalErrorHandler(StoringErrorCallback);
13020 last_location = last_message = NULL;
13021 isolate->Dispose();
13022 CHECK_EQ(last_location, NULL);
13023 CHECK_EQ(last_message, NULL);
13024
13025 // Check that default isolate still runs.
13026 ExpectTrue("function f() { return bar == 371; }; f()");
13027}
13028
13029TEST(DisposeIsolateWhenInUse) {
13030 v8::Isolate* isolate = v8::Isolate::New();
13031 CHECK(isolate);
13032 isolate->Enter();
13033 v8::HandleScope scope;
13034 LocalContext context;
13035 // Run something in this isolate.
13036 ExpectTrue("true");
13037 v8::V8::SetFatalErrorHandler(StoringErrorCallback);
13038 last_location = last_message = NULL;
13039 // Still entered, should fail.
13040 isolate->Dispose();
13041 CHECK_NE(last_location, NULL);
13042 CHECK_NE(last_message, NULL);
13043}
13044
13045TEST(RunTwoIsolatesOnSingleThread) {
13046 // Run isolate 1.
13047 v8::Isolate* isolate1 = v8::Isolate::New();
13048 isolate1->Enter();
13049 v8::Persistent<v8::Context> context1 = v8::Context::New();
13050
13051 {
13052 v8::Context::Scope cscope(context1);
13053 v8::HandleScope scope;
13054 // Run something in new isolate.
13055 CompileRun("var foo = 'isolate 1';");
13056 ExpectString("function f() { return foo; }; f()", "isolate 1");
13057 }
13058
13059 // Run isolate 2.
13060 v8::Isolate* isolate2 = v8::Isolate::New();
13061 v8::Persistent<v8::Context> context2;
13062
13063 {
13064 v8::Isolate::Scope iscope(isolate2);
13065 context2 = v8::Context::New();
13066 v8::Context::Scope cscope(context2);
13067 v8::HandleScope scope;
13068
13069 // Run something in new isolate.
13070 CompileRun("var foo = 'isolate 2';");
13071 ExpectString("function f() { return foo; }; f()", "isolate 2");
13072 }
13073
13074 {
13075 v8::Context::Scope cscope(context1);
13076 v8::HandleScope scope;
13077 // Now again in isolate 1
13078 ExpectString("function f() { return foo; }; f()", "isolate 1");
13079 }
13080
13081 isolate1->Exit();
13082
13083 // Run some stuff in default isolate.
13084 v8::Persistent<v8::Context> context_default = v8::Context::New();
13085
13086 {
13087 v8::Context::Scope cscope(context_default);
13088 v8::HandleScope scope;
13089 // Variables in other isolates should be not available, verify there
13090 // is an exception.
13091 ExpectTrue("function f() {"
13092 " try {"
13093 " foo;"
13094 " return false;"
13095 " } catch(e) {"
13096 " return true;"
13097 " }"
13098 "};"
13099 "var isDefaultIsolate = true;"
13100 "f()");
13101 }
13102
13103 isolate1->Enter();
13104
13105 {
13106 v8::Isolate::Scope iscope(isolate2);
13107 v8::Context::Scope cscope(context2);
13108 v8::HandleScope scope;
13109 ExpectString("function f() { return foo; }; f()", "isolate 2");
13110 }
13111
13112 {
13113 v8::Context::Scope cscope(context1);
13114 v8::HandleScope scope;
13115 ExpectString("function f() { return foo; }; f()", "isolate 1");
13116 }
13117
13118 {
13119 v8::Isolate::Scope iscope(isolate2);
13120 context2.Dispose();
13121 }
13122
13123 context1.Dispose();
13124 isolate1->Exit();
13125
13126 v8::V8::SetFatalErrorHandler(StoringErrorCallback);
13127 last_location = last_message = NULL;
13128
13129 isolate1->Dispose();
13130 CHECK_EQ(last_location, NULL);
13131 CHECK_EQ(last_message, NULL);
13132
13133 isolate2->Dispose();
13134 CHECK_EQ(last_location, NULL);
13135 CHECK_EQ(last_message, NULL);
13136
13137 // Check that default isolate still runs.
13138 {
13139 v8::Context::Scope cscope(context_default);
13140 v8::HandleScope scope;
13141 ExpectTrue("function f() { return isDefaultIsolate; }; f()");
13142 }
13143}
13144
13145static int CalcFibonacci(v8::Isolate* isolate, int limit) {
13146 v8::Isolate::Scope isolate_scope(isolate);
13147 v8::HandleScope scope;
13148 LocalContext context;
13149 i::ScopedVector<char> code(1024);
13150 i::OS::SNPrintF(code, "function fib(n) {"
13151 " if (n <= 2) return 1;"
13152 " return fib(n-1) + fib(n-2);"
13153 "}"
13154 "fib(%d)", limit);
13155 Local<Value> value = CompileRun(code.start());
13156 CHECK(value->IsNumber());
13157 return static_cast<int>(value->NumberValue());
13158}
13159
13160class IsolateThread : public v8::internal::Thread {
13161 public:
13162 explicit IsolateThread(v8::Isolate* isolate, int fib_limit)
13163 : Thread(NULL, "IsolateThread"),
13164 isolate_(isolate),
13165 fib_limit_(fib_limit),
13166 result_(0) { }
13167
13168 void Run() {
13169 result_ = CalcFibonacci(isolate_, fib_limit_);
13170 }
13171
13172 int result() { return result_; }
13173
13174 private:
13175 v8::Isolate* isolate_;
13176 int fib_limit_;
13177 int result_;
13178};
13179
13180TEST(MultipleIsolatesOnIndividualThreads) {
13181 v8::Isolate* isolate1 = v8::Isolate::New();
13182 v8::Isolate* isolate2 = v8::Isolate::New();
13183
13184 IsolateThread thread1(isolate1, 21);
13185 IsolateThread thread2(isolate2, 12);
13186
13187 // Compute some fibonacci numbers on 3 threads in 3 isolates.
13188 thread1.Start();
13189 thread2.Start();
13190
13191 int result1 = CalcFibonacci(v8::Isolate::GetCurrent(), 21);
13192 int result2 = CalcFibonacci(v8::Isolate::GetCurrent(), 12);
13193
13194 thread1.Join();
13195 thread2.Join();
13196
13197 // Compare results. The actual fibonacci numbers for 12 and 21 are taken
13198 // (I'm lazy!) from http://en.wikipedia.org/wiki/Fibonacci_number
13199 CHECK_EQ(result1, 10946);
13200 CHECK_EQ(result2, 144);
13201 CHECK_EQ(result1, thread1.result());
13202 CHECK_EQ(result2, thread2.result());
13203
13204 isolate1->Dispose();
13205 isolate2->Dispose();
13206}
13207
13208
13209class InitDefaultIsolateThread : public v8::internal::Thread {
13210 public:
13211 enum TestCase {
13212 IgnoreOOM,
13213 SetResourceConstraints,
13214 SetFatalHandler,
13215 SetCounterFunction,
13216 SetCreateHistogramFunction,
13217 SetAddHistogramSampleFunction
13218 };
13219
13220 explicit InitDefaultIsolateThread(TestCase testCase)
13221 : Thread(NULL, "InitDefaultIsolateThread"),
13222 testCase_(testCase),
13223 result_(false) { }
13224
13225 void Run() {
13226 switch (testCase_) {
13227 case IgnoreOOM:
13228 v8::V8::IgnoreOutOfMemoryException();
13229 break;
13230
13231 case SetResourceConstraints: {
13232 static const int K = 1024;
13233 v8::ResourceConstraints constraints;
13234 constraints.set_max_young_space_size(256 * K);
13235 constraints.set_max_old_space_size(4 * K * K);
13236 v8::SetResourceConstraints(&constraints);
13237 break;
13238 }
13239
13240 case SetFatalHandler:
13241 v8::V8::SetFatalErrorHandler(NULL);
13242 break;
13243
13244 case SetCounterFunction:
13245 v8::V8::SetCounterFunction(NULL);
13246 break;
13247
13248 case SetCreateHistogramFunction:
13249 v8::V8::SetCreateHistogramFunction(NULL);
13250 break;
13251
13252 case SetAddHistogramSampleFunction:
13253 v8::V8::SetAddHistogramSampleFunction(NULL);
13254 break;
13255 }
13256 result_ = true;
13257 }
13258
13259 bool result() { return result_; }
13260
13261 private:
13262 TestCase testCase_;
13263 bool result_;
13264};
13265
13266
13267static void InitializeTestHelper(InitDefaultIsolateThread::TestCase testCase) {
13268 InitDefaultIsolateThread thread(testCase);
13269 thread.Start();
13270 thread.Join();
13271 CHECK_EQ(thread.result(), true);
13272}
13273
13274TEST(InitializeDefaultIsolateOnSecondaryThread1) {
13275 InitializeTestHelper(InitDefaultIsolateThread::IgnoreOOM);
13276}
13277
13278TEST(InitializeDefaultIsolateOnSecondaryThread2) {
13279 InitializeTestHelper(InitDefaultIsolateThread::SetResourceConstraints);
13280}
13281
13282TEST(InitializeDefaultIsolateOnSecondaryThread3) {
13283 InitializeTestHelper(InitDefaultIsolateThread::SetFatalHandler);
13284}
13285
13286TEST(InitializeDefaultIsolateOnSecondaryThread4) {
13287 InitializeTestHelper(InitDefaultIsolateThread::SetCounterFunction);
13288}
13289
13290TEST(InitializeDefaultIsolateOnSecondaryThread5) {
13291 InitializeTestHelper(InitDefaultIsolateThread::SetCreateHistogramFunction);
13292}
13293
13294TEST(InitializeDefaultIsolateOnSecondaryThread6) {
13295 InitializeTestHelper(InitDefaultIsolateThread::SetAddHistogramSampleFunction);
13296}
13297
Kristian Monsen0d5e1162010-09-30 15:31:59 +010013298
13299TEST(StringCheckMultipleContexts) {
13300 const char* code =
13301 "(function() { return \"a\".charAt(0); })()";
13302
13303 {
13304 // Run the code twice in the first context to initialize the call IC.
13305 v8::HandleScope scope;
13306 LocalContext context1;
13307 ExpectString(code, "a");
13308 ExpectString(code, "a");
13309 }
13310
13311 {
13312 // Change the String.prototype in the second context and check
13313 // that the right function gets called.
13314 v8::HandleScope scope;
13315 LocalContext context2;
13316 CompileRun("String.prototype.charAt = function() { return \"not a\"; }");
13317 ExpectString(code, "not a");
13318 }
13319}
13320
13321
13322TEST(NumberCheckMultipleContexts) {
13323 const char* code =
13324 "(function() { return (42).toString(); })()";
13325
13326 {
13327 // Run the code twice in the first context to initialize the call IC.
13328 v8::HandleScope scope;
13329 LocalContext context1;
13330 ExpectString(code, "42");
13331 ExpectString(code, "42");
13332 }
13333
13334 {
13335 // Change the Number.prototype in the second context and check
13336 // that the right function gets called.
13337 v8::HandleScope scope;
13338 LocalContext context2;
13339 CompileRun("Number.prototype.toString = function() { return \"not 42\"; }");
13340 ExpectString(code, "not 42");
13341 }
13342}
13343
13344
13345TEST(BooleanCheckMultipleContexts) {
13346 const char* code =
13347 "(function() { return true.toString(); })()";
13348
13349 {
13350 // Run the code twice in the first context to initialize the call IC.
13351 v8::HandleScope scope;
13352 LocalContext context1;
13353 ExpectString(code, "true");
13354 ExpectString(code, "true");
13355 }
13356
13357 {
13358 // Change the Boolean.prototype in the second context and check
13359 // that the right function gets called.
13360 v8::HandleScope scope;
13361 LocalContext context2;
13362 CompileRun("Boolean.prototype.toString = function() { return \"\"; }");
13363 ExpectString(code, "");
13364 }
13365}
Ben Murdochf87a2032010-10-22 12:50:53 +010013366
13367
13368TEST(DontDeleteCellLoadIC) {
13369 const char* function_code =
13370 "function readCell() { while (true) { return cell; } }";
13371
13372 {
13373 // Run the code twice in the first context to initialize the load
13374 // IC for a don't delete cell.
13375 v8::HandleScope scope;
13376 LocalContext context1;
13377 CompileRun("var cell = \"first\";");
13378 ExpectBoolean("delete cell", false);
13379 CompileRun(function_code);
13380 ExpectString("readCell()", "first");
13381 ExpectString("readCell()", "first");
13382 }
13383
13384 {
13385 // Use a deletable cell in the second context.
13386 v8::HandleScope scope;
13387 LocalContext context2;
13388 CompileRun("cell = \"second\";");
13389 CompileRun(function_code);
13390 ExpectString("readCell()", "second");
13391 ExpectBoolean("delete cell", true);
13392 ExpectString("(function() {"
13393 " try {"
13394 " return readCell();"
13395 " } catch(e) {"
13396 " return e.toString();"
13397 " }"
13398 "})()",
13399 "ReferenceError: cell is not defined");
13400 CompileRun("cell = \"new_second\";");
Steve Block44f0eee2011-05-26 01:26:41 +010013401 HEAP->CollectAllGarbage(true);
Ben Murdochf87a2032010-10-22 12:50:53 +010013402 ExpectString("readCell()", "new_second");
13403 ExpectString("readCell()", "new_second");
13404 }
13405}
13406
13407
13408TEST(DontDeleteCellLoadICForceDelete) {
13409 const char* function_code =
13410 "function readCell() { while (true) { return cell; } }";
13411
13412 // Run the code twice to initialize the load IC for a don't delete
13413 // cell.
13414 v8::HandleScope scope;
13415 LocalContext context;
13416 CompileRun("var cell = \"value\";");
13417 ExpectBoolean("delete cell", false);
13418 CompileRun(function_code);
13419 ExpectString("readCell()", "value");
13420 ExpectString("readCell()", "value");
13421
13422 // Delete the cell using the API and check the inlined code works
13423 // correctly.
13424 CHECK(context->Global()->ForceDelete(v8_str("cell")));
13425 ExpectString("(function() {"
13426 " try {"
13427 " return readCell();"
13428 " } catch(e) {"
13429 " return e.toString();"
13430 " }"
13431 "})()",
13432 "ReferenceError: cell is not defined");
13433}
13434
13435
13436TEST(DontDeleteCellLoadICAPI) {
13437 const char* function_code =
13438 "function readCell() { while (true) { return cell; } }";
13439
13440 // Run the code twice to initialize the load IC for a don't delete
13441 // cell created using the API.
13442 v8::HandleScope scope;
13443 LocalContext context;
13444 context->Global()->Set(v8_str("cell"), v8_str("value"), v8::DontDelete);
13445 ExpectBoolean("delete cell", false);
13446 CompileRun(function_code);
13447 ExpectString("readCell()", "value");
13448 ExpectString("readCell()", "value");
13449
13450 // Delete the cell using the API and check the inlined code works
13451 // correctly.
13452 CHECK(context->Global()->ForceDelete(v8_str("cell")));
13453 ExpectString("(function() {"
13454 " try {"
13455 " return readCell();"
13456 " } catch(e) {"
13457 " return e.toString();"
13458 " }"
13459 "})()",
13460 "ReferenceError: cell is not defined");
13461}
13462
13463
13464TEST(GlobalLoadICGC) {
13465 const char* function_code =
13466 "function readCell() { while (true) { return cell; } }";
13467
13468 // Check inline load code for a don't delete cell is cleared during
13469 // GC.
13470 {
13471 v8::HandleScope scope;
13472 LocalContext context;
13473 CompileRun("var cell = \"value\";");
13474 ExpectBoolean("delete cell", false);
13475 CompileRun(function_code);
13476 ExpectString("readCell()", "value");
13477 ExpectString("readCell()", "value");
13478 }
13479 {
13480 v8::HandleScope scope;
13481 LocalContext context2;
13482 // Hold the code object in the second context.
13483 CompileRun(function_code);
13484 CheckSurvivingGlobalObjectsCount(1);
13485 }
13486
13487 // Check inline load code for a deletable cell is cleared during GC.
13488 {
13489 v8::HandleScope scope;
13490 LocalContext context;
13491 CompileRun("cell = \"value\";");
13492 CompileRun(function_code);
13493 ExpectString("readCell()", "value");
13494 ExpectString("readCell()", "value");
13495 }
13496 {
13497 v8::HandleScope scope;
13498 LocalContext context2;
13499 // Hold the code object in the second context.
13500 CompileRun(function_code);
13501 CheckSurvivingGlobalObjectsCount(1);
13502 }
13503}
13504
13505
13506TEST(RegExp) {
13507 v8::HandleScope scope;
13508 LocalContext context;
13509
13510 v8::Handle<v8::RegExp> re = v8::RegExp::New(v8_str("foo"), v8::RegExp::kNone);
13511 CHECK(re->IsRegExp());
13512 CHECK(re->GetSource()->Equals(v8_str("foo")));
13513 CHECK_EQ(re->GetFlags(), v8::RegExp::kNone);
13514
13515 re = v8::RegExp::New(v8_str("bar"),
13516 static_cast<v8::RegExp::Flags>(v8::RegExp::kIgnoreCase |
13517 v8::RegExp::kGlobal));
13518 CHECK(re->IsRegExp());
13519 CHECK(re->GetSource()->Equals(v8_str("bar")));
13520 CHECK_EQ(static_cast<int>(re->GetFlags()),
13521 v8::RegExp::kIgnoreCase | v8::RegExp::kGlobal);
13522
13523 re = v8::RegExp::New(v8_str("baz"),
13524 static_cast<v8::RegExp::Flags>(v8::RegExp::kIgnoreCase |
13525 v8::RegExp::kMultiline));
13526 CHECK(re->IsRegExp());
13527 CHECK(re->GetSource()->Equals(v8_str("baz")));
13528 CHECK_EQ(static_cast<int>(re->GetFlags()),
13529 v8::RegExp::kIgnoreCase | v8::RegExp::kMultiline);
13530
13531 re = CompileRun("/quux/").As<v8::RegExp>();
13532 CHECK(re->IsRegExp());
13533 CHECK(re->GetSource()->Equals(v8_str("quux")));
13534 CHECK_EQ(re->GetFlags(), v8::RegExp::kNone);
13535
13536 re = CompileRun("/quux/gm").As<v8::RegExp>();
13537 CHECK(re->IsRegExp());
13538 CHECK(re->GetSource()->Equals(v8_str("quux")));
13539 CHECK_EQ(static_cast<int>(re->GetFlags()),
13540 v8::RegExp::kGlobal | v8::RegExp::kMultiline);
13541
13542 // Override the RegExp constructor and check the API constructor
13543 // still works.
13544 CompileRun("RegExp = function() {}");
13545
13546 re = v8::RegExp::New(v8_str("foobar"), v8::RegExp::kNone);
13547 CHECK(re->IsRegExp());
13548 CHECK(re->GetSource()->Equals(v8_str("foobar")));
13549 CHECK_EQ(re->GetFlags(), v8::RegExp::kNone);
13550
13551 re = v8::RegExp::New(v8_str("foobarbaz"),
13552 static_cast<v8::RegExp::Flags>(v8::RegExp::kIgnoreCase |
13553 v8::RegExp::kMultiline));
13554 CHECK(re->IsRegExp());
13555 CHECK(re->GetSource()->Equals(v8_str("foobarbaz")));
13556 CHECK_EQ(static_cast<int>(re->GetFlags()),
13557 v8::RegExp::kIgnoreCase | v8::RegExp::kMultiline);
13558
13559 context->Global()->Set(v8_str("re"), re);
13560 ExpectTrue("re.test('FoobarbaZ')");
13561
13562 v8::TryCatch try_catch;
13563 re = v8::RegExp::New(v8_str("foo["), v8::RegExp::kNone);
13564 CHECK(re.IsEmpty());
13565 CHECK(try_catch.HasCaught());
13566 context->Global()->Set(v8_str("ex"), try_catch.Exception());
13567 ExpectTrue("ex instanceof SyntaxError");
13568}
13569
13570
Steve Block1e0659c2011-05-24 12:43:12 +010013571THREADED_TEST(Equals) {
13572 v8::HandleScope handleScope;
13573 LocalContext localContext;
13574
13575 v8::Handle<v8::Object> globalProxy = localContext->Global();
13576 v8::Handle<Value> global = globalProxy->GetPrototype();
13577
13578 CHECK(global->StrictEquals(global));
13579 CHECK(!global->StrictEquals(globalProxy));
13580 CHECK(!globalProxy->StrictEquals(global));
13581 CHECK(globalProxy->StrictEquals(globalProxy));
13582
13583 CHECK(global->Equals(global));
13584 CHECK(!global->Equals(globalProxy));
13585 CHECK(!globalProxy->Equals(global));
13586 CHECK(globalProxy->Equals(globalProxy));
13587}
13588
13589
Ben Murdochf87a2032010-10-22 12:50:53 +010013590static v8::Handle<v8::Value> Getter(v8::Local<v8::String> property,
13591 const v8::AccessorInfo& info ) {
13592 return v8_str("42!");
13593}
13594
13595
13596static v8::Handle<v8::Array> Enumerator(const v8::AccessorInfo& info) {
13597 v8::Handle<v8::Array> result = v8::Array::New();
13598 result->Set(0, v8_str("universalAnswer"));
13599 return result;
13600}
13601
13602
13603TEST(NamedEnumeratorAndForIn) {
13604 v8::HandleScope handle_scope;
13605 LocalContext context;
13606 v8::Context::Scope context_scope(context.local());
13607
13608 v8::Handle<v8::ObjectTemplate> tmpl = v8::ObjectTemplate::New();
13609 tmpl->SetNamedPropertyHandler(Getter, NULL, NULL, NULL, Enumerator);
13610 context->Global()->Set(v8_str("o"), tmpl->NewInstance());
13611 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun(
13612 "var result = []; for (var k in o) result.push(k); result"));
13613 CHECK_EQ(1, result->Length());
13614 CHECK_EQ(v8_str("universalAnswer"), result->Get(0));
13615}
Steve Block1e0659c2011-05-24 12:43:12 +010013616
13617
13618TEST(DefinePropertyPostDetach) {
13619 v8::HandleScope scope;
13620 LocalContext context;
13621 v8::Handle<v8::Object> proxy = context->Global();
13622 v8::Handle<v8::Function> define_property =
13623 CompileRun("(function() {"
13624 " Object.defineProperty("
13625 " this,"
13626 " 1,"
13627 " { configurable: true, enumerable: true, value: 3 });"
13628 "})").As<Function>();
13629 context->DetachGlobal();
13630 define_property->Call(proxy, 0, NULL);
13631}