blob: d7621d12cf2ff1af6c02c36abd4c626b418eb612 [file] [log] [blame]
Ben Murdoch8b112d22011-06-08 16:22:53 +01001// Copyright 2011 the V8 project authors. All rights reserved.
Steve Blocka7e24c12009-10-30 11:49:00 +00002// 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;
Ben Murdoch8b112d22011-06-08 16:22:53 +010053using ::v8::Arguments;
Steve Block44f0eee2011-05-26 01:26:41 +010054using ::v8::Context;
Steve Blocka7e24c12009-10-30 11:49:00 +000055using ::v8::Extension;
Steve Block44f0eee2011-05-26 01:26:41 +010056using ::v8::Function;
Ben Murdoch8b112d22011-06-08 16:22:53 +010057using ::v8::FunctionTemplate;
58using ::v8::Handle;
Steve Block44f0eee2011-05-26 01:26:41 +010059using ::v8::HandleScope;
60using ::v8::Local;
Ben Murdoch8b112d22011-06-08 16:22:53 +010061using ::v8::Message;
62using ::v8::MessageCallback;
Steve Block44f0eee2011-05-26 01:26:41 +010063using ::v8::Object;
64using ::v8::ObjectTemplate;
65using ::v8::Persistent;
66using ::v8::Script;
Ben Murdoch8b112d22011-06-08 16:22:53 +010067using ::v8::StackTrace;
Steve Block44f0eee2011-05-26 01:26:41 +010068using ::v8::String;
Ben Murdoch8b112d22011-06-08 16:22:53 +010069using ::v8::TryCatch;
70using ::v8::Undefined;
Steve Block44f0eee2011-05-26 01:26:41 +010071using ::v8::V8;
Ben Murdoch8b112d22011-06-08 16:22:53 +010072using ::v8::Value;
Steve Blocka7e24c12009-10-30 11:49:00 +000073
Steve Block8defd9f2010-07-08 12:39:36 +010074namespace i = ::i;
Steve Blocka7e24c12009-10-30 11:49:00 +000075
Steve Blocka7e24c12009-10-30 11:49:00 +000076
Leon Clarked91b9f72010-01-27 17:25:45 +000077static void ExpectString(const char* code, const char* expected) {
78 Local<Value> result = CompileRun(code);
79 CHECK(result->IsString());
80 String::AsciiValue ascii(result);
81 CHECK_EQ(expected, *ascii);
82}
83
84
85static void ExpectBoolean(const char* code, bool expected) {
86 Local<Value> result = CompileRun(code);
87 CHECK(result->IsBoolean());
88 CHECK_EQ(expected, result->BooleanValue());
89}
90
91
Leon Clarkef7060e22010-06-03 12:02:55 +010092static void ExpectTrue(const char* code) {
93 ExpectBoolean(code, true);
94}
95
96
Iain Merrick75681382010-08-19 15:07:18 +010097static void ExpectFalse(const char* code) {
98 ExpectBoolean(code, false);
99}
100
101
Leon Clarked91b9f72010-01-27 17:25:45 +0000102static void ExpectObject(const char* code, Local<Value> expected) {
103 Local<Value> result = CompileRun(code);
104 CHECK(result->Equals(expected));
105}
106
107
Iain Merrick75681382010-08-19 15:07:18 +0100108static void ExpectUndefined(const char* code) {
109 Local<Value> result = CompileRun(code);
110 CHECK(result->IsUndefined());
111}
112
113
Steve Blocka7e24c12009-10-30 11:49:00 +0000114static int signature_callback_count;
115static v8::Handle<Value> IncrementingSignatureCallback(
116 const v8::Arguments& args) {
117 ApiTestFuzzer::Fuzz();
118 signature_callback_count++;
119 v8::Handle<v8::Array> result = v8::Array::New(args.Length());
120 for (int i = 0; i < args.Length(); i++)
121 result->Set(v8::Integer::New(i), args[i]);
122 return result;
123}
124
125
126static v8::Handle<Value> SignatureCallback(const v8::Arguments& args) {
127 ApiTestFuzzer::Fuzz();
128 v8::Handle<v8::Array> result = v8::Array::New(args.Length());
129 for (int i = 0; i < args.Length(); i++) {
130 result->Set(v8::Integer::New(i), args[i]);
131 }
132 return result;
133}
134
135
136THREADED_TEST(Handles) {
137 v8::HandleScope scope;
138 Local<Context> local_env;
139 {
140 LocalContext env;
141 local_env = env.local();
142 }
143
144 // Local context should still be live.
145 CHECK(!local_env.IsEmpty());
146 local_env->Enter();
147
148 v8::Handle<v8::Primitive> undef = v8::Undefined();
149 CHECK(!undef.IsEmpty());
150 CHECK(undef->IsUndefined());
151
152 const char* c_source = "1 + 2 + 3";
153 Local<String> source = String::New(c_source);
154 Local<Script> script = Script::Compile(source);
155 CHECK_EQ(6, script->Run()->Int32Value());
156
157 local_env->Exit();
158}
159
160
Steve Blocka7e24c12009-10-30 11:49:00 +0000161THREADED_TEST(ReceiverSignature) {
162 v8::HandleScope scope;
163 LocalContext env;
164 v8::Handle<v8::FunctionTemplate> fun = v8::FunctionTemplate::New();
165 v8::Handle<v8::Signature> sig = v8::Signature::New(fun);
166 fun->PrototypeTemplate()->Set(
167 v8_str("m"),
168 v8::FunctionTemplate::New(IncrementingSignatureCallback,
169 v8::Handle<Value>(),
170 sig));
171 env->Global()->Set(v8_str("Fun"), fun->GetFunction());
172 signature_callback_count = 0;
173 CompileRun(
174 "var o = new Fun();"
175 "o.m();");
176 CHECK_EQ(1, signature_callback_count);
177 v8::Handle<v8::FunctionTemplate> sub_fun = v8::FunctionTemplate::New();
178 sub_fun->Inherit(fun);
179 env->Global()->Set(v8_str("SubFun"), sub_fun->GetFunction());
180 CompileRun(
181 "var o = new SubFun();"
182 "o.m();");
183 CHECK_EQ(2, signature_callback_count);
184
185 v8::TryCatch try_catch;
186 CompileRun(
187 "var o = { };"
188 "o.m = Fun.prototype.m;"
189 "o.m();");
190 CHECK_EQ(2, signature_callback_count);
191 CHECK(try_catch.HasCaught());
192 try_catch.Reset();
193 v8::Handle<v8::FunctionTemplate> unrel_fun = v8::FunctionTemplate::New();
194 sub_fun->Inherit(fun);
195 env->Global()->Set(v8_str("UnrelFun"), unrel_fun->GetFunction());
196 CompileRun(
197 "var o = new UnrelFun();"
198 "o.m = Fun.prototype.m;"
199 "o.m();");
200 CHECK_EQ(2, signature_callback_count);
201 CHECK(try_catch.HasCaught());
202}
203
204
205
206
207THREADED_TEST(ArgumentSignature) {
208 v8::HandleScope scope;
209 LocalContext env;
210 v8::Handle<v8::FunctionTemplate> cons = v8::FunctionTemplate::New();
211 cons->SetClassName(v8_str("Cons"));
212 v8::Handle<v8::Signature> sig =
213 v8::Signature::New(v8::Handle<v8::FunctionTemplate>(), 1, &cons);
214 v8::Handle<v8::FunctionTemplate> fun =
215 v8::FunctionTemplate::New(SignatureCallback, v8::Handle<Value>(), sig);
216 env->Global()->Set(v8_str("Cons"), cons->GetFunction());
217 env->Global()->Set(v8_str("Fun1"), fun->GetFunction());
218
219 v8::Handle<Value> value1 = CompileRun("Fun1(4) == '';");
220 CHECK(value1->IsTrue());
221
222 v8::Handle<Value> value2 = CompileRun("Fun1(new Cons()) == '[object Cons]';");
223 CHECK(value2->IsTrue());
224
225 v8::Handle<Value> value3 = CompileRun("Fun1() == '';");
226 CHECK(value3->IsTrue());
227
228 v8::Handle<v8::FunctionTemplate> cons1 = v8::FunctionTemplate::New();
229 cons1->SetClassName(v8_str("Cons1"));
230 v8::Handle<v8::FunctionTemplate> cons2 = v8::FunctionTemplate::New();
231 cons2->SetClassName(v8_str("Cons2"));
232 v8::Handle<v8::FunctionTemplate> cons3 = v8::FunctionTemplate::New();
233 cons3->SetClassName(v8_str("Cons3"));
234
235 v8::Handle<v8::FunctionTemplate> args[3] = { cons1, cons2, cons3 };
236 v8::Handle<v8::Signature> wsig =
237 v8::Signature::New(v8::Handle<v8::FunctionTemplate>(), 3, args);
238 v8::Handle<v8::FunctionTemplate> fun2 =
239 v8::FunctionTemplate::New(SignatureCallback, v8::Handle<Value>(), wsig);
240
241 env->Global()->Set(v8_str("Cons1"), cons1->GetFunction());
242 env->Global()->Set(v8_str("Cons2"), cons2->GetFunction());
243 env->Global()->Set(v8_str("Cons3"), cons3->GetFunction());
244 env->Global()->Set(v8_str("Fun2"), fun2->GetFunction());
245 v8::Handle<Value> value4 = CompileRun(
246 "Fun2(new Cons1(), new Cons2(), new Cons3()) =="
247 "'[object Cons1],[object Cons2],[object Cons3]'");
248 CHECK(value4->IsTrue());
249
250 v8::Handle<Value> value5 = CompileRun(
251 "Fun2(new Cons1(), new Cons2(), 5) == '[object Cons1],[object Cons2],'");
252 CHECK(value5->IsTrue());
253
254 v8::Handle<Value> value6 = CompileRun(
255 "Fun2(new Cons3(), new Cons2(), new Cons1()) == ',[object Cons2],'");
256 CHECK(value6->IsTrue());
257
258 v8::Handle<Value> value7 = CompileRun(
259 "Fun2(new Cons1(), new Cons2(), new Cons3(), 'd') == "
260 "'[object Cons1],[object Cons2],[object Cons3],d';");
261 CHECK(value7->IsTrue());
262
263 v8::Handle<Value> value8 = CompileRun(
264 "Fun2(new Cons1(), new Cons2()) == '[object Cons1],[object Cons2]'");
265 CHECK(value8->IsTrue());
266}
267
268
269THREADED_TEST(HulIgennem) {
270 v8::HandleScope scope;
271 LocalContext env;
272 v8::Handle<v8::Primitive> undef = v8::Undefined();
273 Local<String> undef_str = undef->ToString();
274 char* value = i::NewArray<char>(undef_str->Length() + 1);
275 undef_str->WriteAscii(value);
276 CHECK_EQ(0, strcmp(value, "undefined"));
277 i::DeleteArray(value);
278}
279
280
281THREADED_TEST(Access) {
282 v8::HandleScope scope;
283 LocalContext env;
284 Local<v8::Object> obj = v8::Object::New();
285 Local<Value> foo_before = obj->Get(v8_str("foo"));
286 CHECK(foo_before->IsUndefined());
287 Local<String> bar_str = v8_str("bar");
288 obj->Set(v8_str("foo"), bar_str);
289 Local<Value> foo_after = obj->Get(v8_str("foo"));
290 CHECK(!foo_after->IsUndefined());
291 CHECK(foo_after->IsString());
292 CHECK_EQ(bar_str, foo_after);
293}
294
295
Steve Block6ded16b2010-05-10 14:33:55 +0100296THREADED_TEST(AccessElement) {
297 v8::HandleScope scope;
298 LocalContext env;
299 Local<v8::Object> obj = v8::Object::New();
300 Local<Value> before = obj->Get(1);
301 CHECK(before->IsUndefined());
302 Local<String> bar_str = v8_str("bar");
303 obj->Set(1, bar_str);
304 Local<Value> after = obj->Get(1);
305 CHECK(!after->IsUndefined());
306 CHECK(after->IsString());
307 CHECK_EQ(bar_str, after);
308
309 Local<v8::Array> value = CompileRun("[\"a\", \"b\"]").As<v8::Array>();
310 CHECK_EQ(v8_str("a"), value->Get(0));
311 CHECK_EQ(v8_str("b"), value->Get(1));
312}
313
314
Steve Blocka7e24c12009-10-30 11:49:00 +0000315THREADED_TEST(Script) {
316 v8::HandleScope scope;
317 LocalContext env;
318 const char* c_source = "1 + 2 + 3";
319 Local<String> source = String::New(c_source);
320 Local<Script> script = Script::Compile(source);
321 CHECK_EQ(6, script->Run()->Int32Value());
322}
323
324
325static uint16_t* AsciiToTwoByteString(const char* source) {
Steve Blockd0582a62009-12-15 09:54:21 +0000326 int array_length = i::StrLength(source) + 1;
Steve Blocka7e24c12009-10-30 11:49:00 +0000327 uint16_t* converted = i::NewArray<uint16_t>(array_length);
Steve Blockd0582a62009-12-15 09:54:21 +0000328 for (int i = 0; i < array_length; i++) converted[i] = source[i];
Steve Blocka7e24c12009-10-30 11:49:00 +0000329 return converted;
330}
331
332
333class TestResource: public String::ExternalStringResource {
334 public:
335 static int dispose_count;
336
337 explicit TestResource(uint16_t* data)
338 : data_(data), length_(0) {
339 while (data[length_]) ++length_;
340 }
341
342 ~TestResource() {
343 i::DeleteArray(data_);
344 ++dispose_count;
345 }
346
347 const uint16_t* data() const {
348 return data_;
349 }
350
351 size_t length() const {
352 return length_;
353 }
354 private:
355 uint16_t* data_;
356 size_t length_;
357};
358
359
360int TestResource::dispose_count = 0;
361
362
363class TestAsciiResource: public String::ExternalAsciiStringResource {
364 public:
365 static int dispose_count;
366
367 explicit TestAsciiResource(const char* data)
368 : data_(data),
369 length_(strlen(data)) { }
370
371 ~TestAsciiResource() {
372 i::DeleteArray(data_);
373 ++dispose_count;
374 }
375
376 const char* data() const {
377 return data_;
378 }
379
380 size_t length() const {
381 return length_;
382 }
383 private:
384 const char* data_;
385 size_t length_;
386};
387
388
389int TestAsciiResource::dispose_count = 0;
390
391
392THREADED_TEST(ScriptUsingStringResource) {
393 TestResource::dispose_count = 0;
394 const char* c_source = "1 + 2 * 3";
395 uint16_t* two_byte_source = AsciiToTwoByteString(c_source);
396 {
397 v8::HandleScope scope;
398 LocalContext env;
399 TestResource* resource = new TestResource(two_byte_source);
400 Local<String> source = String::NewExternal(resource);
401 Local<Script> script = Script::Compile(source);
402 Local<Value> value = script->Run();
403 CHECK(value->IsNumber());
404 CHECK_EQ(7, value->Int32Value());
405 CHECK(source->IsExternal());
406 CHECK_EQ(resource,
407 static_cast<TestResource*>(source->GetExternalStringResource()));
Steve Block44f0eee2011-05-26 01:26:41 +0100408 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000409 CHECK_EQ(0, TestResource::dispose_count);
410 }
Steve Block44f0eee2011-05-26 01:26:41 +0100411 v8::internal::Isolate::Current()->compilation_cache()->Clear();
412 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000413 CHECK_EQ(1, TestResource::dispose_count);
414}
415
416
417THREADED_TEST(ScriptUsingAsciiStringResource) {
418 TestAsciiResource::dispose_count = 0;
419 const char* c_source = "1 + 2 * 3";
420 {
421 v8::HandleScope scope;
422 LocalContext env;
423 Local<String> source =
424 String::NewExternal(new TestAsciiResource(i::StrDup(c_source)));
425 Local<Script> script = Script::Compile(source);
426 Local<Value> value = script->Run();
427 CHECK(value->IsNumber());
428 CHECK_EQ(7, value->Int32Value());
Steve Block44f0eee2011-05-26 01:26:41 +0100429 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000430 CHECK_EQ(0, TestAsciiResource::dispose_count);
431 }
Steve Block44f0eee2011-05-26 01:26:41 +0100432 i::Isolate::Current()->compilation_cache()->Clear();
433 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000434 CHECK_EQ(1, TestAsciiResource::dispose_count);
435}
436
437
438THREADED_TEST(ScriptMakingExternalString) {
439 TestResource::dispose_count = 0;
440 uint16_t* two_byte_source = AsciiToTwoByteString("1 + 2 * 3");
441 {
442 v8::HandleScope scope;
443 LocalContext env;
444 Local<String> source = String::New(two_byte_source);
Andrei Popescu402d9372010-02-26 13:31:12 +0000445 // Trigger GCs so that the newly allocated string moves to old gen.
Steve Block44f0eee2011-05-26 01:26:41 +0100446 HEAP->CollectGarbage(i::NEW_SPACE); // in survivor space now
447 HEAP->CollectGarbage(i::NEW_SPACE); // in old gen now
Steve Blocka7e24c12009-10-30 11:49:00 +0000448 bool success = source->MakeExternal(new TestResource(two_byte_source));
449 CHECK(success);
450 Local<Script> script = Script::Compile(source);
451 Local<Value> value = script->Run();
452 CHECK(value->IsNumber());
453 CHECK_EQ(7, value->Int32Value());
Steve Block44f0eee2011-05-26 01:26:41 +0100454 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000455 CHECK_EQ(0, TestResource::dispose_count);
456 }
Steve Block44f0eee2011-05-26 01:26:41 +0100457 i::Isolate::Current()->compilation_cache()->Clear();
458 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000459 CHECK_EQ(1, TestResource::dispose_count);
460}
461
462
463THREADED_TEST(ScriptMakingExternalAsciiString) {
464 TestAsciiResource::dispose_count = 0;
465 const char* c_source = "1 + 2 * 3";
466 {
467 v8::HandleScope scope;
468 LocalContext env;
469 Local<String> source = v8_str(c_source);
Andrei Popescu402d9372010-02-26 13:31:12 +0000470 // Trigger GCs so that the newly allocated string moves to old gen.
Steve Block44f0eee2011-05-26 01:26:41 +0100471 HEAP->CollectGarbage(i::NEW_SPACE); // in survivor space now
472 HEAP->CollectGarbage(i::NEW_SPACE); // in old gen now
Steve Blocka7e24c12009-10-30 11:49:00 +0000473 bool success = source->MakeExternal(
474 new TestAsciiResource(i::StrDup(c_source)));
475 CHECK(success);
476 Local<Script> script = Script::Compile(source);
477 Local<Value> value = script->Run();
478 CHECK(value->IsNumber());
479 CHECK_EQ(7, value->Int32Value());
Steve Block44f0eee2011-05-26 01:26:41 +0100480 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000481 CHECK_EQ(0, TestAsciiResource::dispose_count);
482 }
Steve Block44f0eee2011-05-26 01:26:41 +0100483 i::Isolate::Current()->compilation_cache()->Clear();
484 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000485 CHECK_EQ(1, TestAsciiResource::dispose_count);
486}
487
488
Andrei Popescu402d9372010-02-26 13:31:12 +0000489TEST(MakingExternalStringConditions) {
490 v8::HandleScope scope;
491 LocalContext env;
492
493 // Free some space in the new space so that we can check freshness.
Steve Block44f0eee2011-05-26 01:26:41 +0100494 HEAP->CollectGarbage(i::NEW_SPACE);
495 HEAP->CollectGarbage(i::NEW_SPACE);
Andrei Popescu402d9372010-02-26 13:31:12 +0000496
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100497 uint16_t* two_byte_string = AsciiToTwoByteString("small");
498 Local<String> small_string = String::New(two_byte_string);
499 i::DeleteArray(two_byte_string);
500
Andrei Popescu402d9372010-02-26 13:31:12 +0000501 // We should refuse to externalize newly created small string.
502 CHECK(!small_string->CanMakeExternal());
503 // Trigger GCs so that the newly allocated string moves to old gen.
Steve Block44f0eee2011-05-26 01:26:41 +0100504 HEAP->CollectGarbage(i::NEW_SPACE); // in survivor space now
505 HEAP->CollectGarbage(i::NEW_SPACE); // in old gen now
Andrei Popescu402d9372010-02-26 13:31:12 +0000506 // Old space strings should be accepted.
507 CHECK(small_string->CanMakeExternal());
508
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100509 two_byte_string = AsciiToTwoByteString("small 2");
510 small_string = String::New(two_byte_string);
511 i::DeleteArray(two_byte_string);
512
Andrei Popescu402d9372010-02-26 13:31:12 +0000513 // We should refuse externalizing newly created small string.
514 CHECK(!small_string->CanMakeExternal());
515 for (int i = 0; i < 100; i++) {
516 String::Value value(small_string);
517 }
518 // Frequently used strings should be accepted.
519 CHECK(small_string->CanMakeExternal());
520
521 const int buf_size = 10 * 1024;
522 char* buf = i::NewArray<char>(buf_size);
523 memset(buf, 'a', buf_size);
524 buf[buf_size - 1] = '\0';
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100525
526 two_byte_string = AsciiToTwoByteString(buf);
527 Local<String> large_string = String::New(two_byte_string);
Andrei Popescu402d9372010-02-26 13:31:12 +0000528 i::DeleteArray(buf);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100529 i::DeleteArray(two_byte_string);
Andrei Popescu402d9372010-02-26 13:31:12 +0000530 // Large strings should be immediately accepted.
531 CHECK(large_string->CanMakeExternal());
532}
533
534
535TEST(MakingExternalAsciiStringConditions) {
536 v8::HandleScope scope;
537 LocalContext env;
538
539 // Free some space in the new space so that we can check freshness.
Steve Block44f0eee2011-05-26 01:26:41 +0100540 HEAP->CollectGarbage(i::NEW_SPACE);
541 HEAP->CollectGarbage(i::NEW_SPACE);
Andrei Popescu402d9372010-02-26 13:31:12 +0000542
543 Local<String> small_string = String::New("small");
544 // We should refuse to externalize newly created small string.
545 CHECK(!small_string->CanMakeExternal());
546 // Trigger GCs so that the newly allocated string moves to old gen.
Steve Block44f0eee2011-05-26 01:26:41 +0100547 HEAP->CollectGarbage(i::NEW_SPACE); // in survivor space now
548 HEAP->CollectGarbage(i::NEW_SPACE); // in old gen now
Andrei Popescu402d9372010-02-26 13:31:12 +0000549 // Old space strings should be accepted.
550 CHECK(small_string->CanMakeExternal());
551
552 small_string = String::New("small 2");
553 // We should refuse externalizing newly created small string.
554 CHECK(!small_string->CanMakeExternal());
555 for (int i = 0; i < 100; i++) {
556 String::Value value(small_string);
557 }
558 // Frequently used strings should be accepted.
559 CHECK(small_string->CanMakeExternal());
560
561 const int buf_size = 10 * 1024;
562 char* buf = i::NewArray<char>(buf_size);
563 memset(buf, 'a', buf_size);
564 buf[buf_size - 1] = '\0';
565 Local<String> large_string = String::New(buf);
566 i::DeleteArray(buf);
567 // Large strings should be immediately accepted.
568 CHECK(large_string->CanMakeExternal());
569}
570
571
Steve Blocka7e24c12009-10-30 11:49:00 +0000572THREADED_TEST(UsingExternalString) {
573 {
574 v8::HandleScope scope;
575 uint16_t* two_byte_string = AsciiToTwoByteString("test string");
576 Local<String> string =
577 String::NewExternal(new TestResource(two_byte_string));
578 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
579 // Trigger GCs so that the newly allocated string moves to old gen.
Steve Block44f0eee2011-05-26 01:26:41 +0100580 HEAP->CollectGarbage(i::NEW_SPACE); // in survivor space now
581 HEAP->CollectGarbage(i::NEW_SPACE); // in old gen now
582 i::Handle<i::String> isymbol = FACTORY->SymbolFromString(istring);
Steve Blocka7e24c12009-10-30 11:49:00 +0000583 CHECK(isymbol->IsSymbol());
584 }
Steve Block44f0eee2011-05-26 01:26:41 +0100585 HEAP->CollectAllGarbage(false);
586 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000587}
588
589
590THREADED_TEST(UsingExternalAsciiString) {
591 {
592 v8::HandleScope scope;
593 const char* one_byte_string = "test string";
594 Local<String> string = String::NewExternal(
595 new TestAsciiResource(i::StrDup(one_byte_string)));
596 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
597 // Trigger GCs so that the newly allocated string moves to old gen.
Steve Block44f0eee2011-05-26 01:26:41 +0100598 HEAP->CollectGarbage(i::NEW_SPACE); // in survivor space now
599 HEAP->CollectGarbage(i::NEW_SPACE); // in old gen now
600 i::Handle<i::String> isymbol = FACTORY->SymbolFromString(istring);
Steve Blocka7e24c12009-10-30 11:49:00 +0000601 CHECK(isymbol->IsSymbol());
602 }
Steve Block44f0eee2011-05-26 01:26:41 +0100603 HEAP->CollectAllGarbage(false);
604 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000605}
606
607
Leon Clarkee46be812010-01-19 14:06:41 +0000608THREADED_TEST(ScavengeExternalString) {
609 TestResource::dispose_count = 0;
Steve Block6ded16b2010-05-10 14:33:55 +0100610 bool in_new_space = false;
Leon Clarkee46be812010-01-19 14:06:41 +0000611 {
612 v8::HandleScope scope;
613 uint16_t* two_byte_string = AsciiToTwoByteString("test string");
614 Local<String> string =
615 String::NewExternal(new TestResource(two_byte_string));
616 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
Steve Block44f0eee2011-05-26 01:26:41 +0100617 HEAP->CollectGarbage(i::NEW_SPACE);
618 in_new_space = HEAP->InNewSpace(*istring);
619 CHECK(in_new_space || HEAP->old_data_space()->Contains(*istring));
Leon Clarkee46be812010-01-19 14:06:41 +0000620 CHECK_EQ(0, TestResource::dispose_count);
621 }
Steve Block44f0eee2011-05-26 01:26:41 +0100622 HEAP->CollectGarbage(in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE);
Leon Clarkee46be812010-01-19 14:06:41 +0000623 CHECK_EQ(1, TestResource::dispose_count);
624}
625
626
627THREADED_TEST(ScavengeExternalAsciiString) {
628 TestAsciiResource::dispose_count = 0;
Steve Block6ded16b2010-05-10 14:33:55 +0100629 bool in_new_space = false;
Leon Clarkee46be812010-01-19 14:06:41 +0000630 {
631 v8::HandleScope scope;
632 const char* one_byte_string = "test string";
633 Local<String> string = String::NewExternal(
634 new TestAsciiResource(i::StrDup(one_byte_string)));
635 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
Steve Block44f0eee2011-05-26 01:26:41 +0100636 HEAP->CollectGarbage(i::NEW_SPACE);
637 in_new_space = HEAP->InNewSpace(*istring);
638 CHECK(in_new_space || HEAP->old_data_space()->Contains(*istring));
Leon Clarkee46be812010-01-19 14:06:41 +0000639 CHECK_EQ(0, TestAsciiResource::dispose_count);
640 }
Steve Block44f0eee2011-05-26 01:26:41 +0100641 HEAP->CollectGarbage(in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE);
Leon Clarkee46be812010-01-19 14:06:41 +0000642 CHECK_EQ(1, TestAsciiResource::dispose_count);
643}
644
645
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100646class TestAsciiResourceWithDisposeControl: public TestAsciiResource {
647 public:
648 static int dispose_calls;
649
650 TestAsciiResourceWithDisposeControl(const char* data, bool dispose)
651 : TestAsciiResource(data),
652 dispose_(dispose) { }
653
654 void Dispose() {
655 ++dispose_calls;
656 if (dispose_) delete this;
657 }
658 private:
659 bool dispose_;
660};
661
662
663int TestAsciiResourceWithDisposeControl::dispose_calls = 0;
664
665
666TEST(ExternalStringWithDisposeHandling) {
667 const char* c_source = "1 + 2 * 3";
668
669 // Use a stack allocated external string resource allocated object.
670 TestAsciiResource::dispose_count = 0;
671 TestAsciiResourceWithDisposeControl::dispose_calls = 0;
672 TestAsciiResourceWithDisposeControl res_stack(i::StrDup(c_source), false);
673 {
674 v8::HandleScope scope;
675 LocalContext env;
676 Local<String> source = String::NewExternal(&res_stack);
677 Local<Script> script = Script::Compile(source);
678 Local<Value> value = script->Run();
679 CHECK(value->IsNumber());
680 CHECK_EQ(7, value->Int32Value());
Steve Block44f0eee2011-05-26 01:26:41 +0100681 HEAP->CollectAllGarbage(false);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100682 CHECK_EQ(0, TestAsciiResource::dispose_count);
683 }
Steve Block44f0eee2011-05-26 01:26:41 +0100684 i::Isolate::Current()->compilation_cache()->Clear();
685 HEAP->CollectAllGarbage(false);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100686 CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls);
687 CHECK_EQ(0, TestAsciiResource::dispose_count);
688
689 // Use a heap allocated external string resource allocated object.
690 TestAsciiResource::dispose_count = 0;
691 TestAsciiResourceWithDisposeControl::dispose_calls = 0;
692 TestAsciiResource* res_heap =
693 new TestAsciiResourceWithDisposeControl(i::StrDup(c_source), true);
694 {
695 v8::HandleScope scope;
696 LocalContext env;
697 Local<String> source = String::NewExternal(res_heap);
698 Local<Script> script = Script::Compile(source);
699 Local<Value> value = script->Run();
700 CHECK(value->IsNumber());
701 CHECK_EQ(7, value->Int32Value());
Steve Block44f0eee2011-05-26 01:26:41 +0100702 HEAP->CollectAllGarbage(false);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100703 CHECK_EQ(0, TestAsciiResource::dispose_count);
704 }
Steve Block44f0eee2011-05-26 01:26:41 +0100705 i::Isolate::Current()->compilation_cache()->Clear();
706 HEAP->CollectAllGarbage(false);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100707 CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls);
708 CHECK_EQ(1, TestAsciiResource::dispose_count);
709}
710
711
Steve Block3ce2e202009-11-05 08:53:23 +0000712THREADED_TEST(StringConcat) {
713 {
714 v8::HandleScope scope;
715 LocalContext env;
716 const char* one_byte_string_1 = "function a_times_t";
717 const char* two_byte_string_1 = "wo_plus_b(a, b) {return ";
718 const char* one_byte_extern_1 = "a * 2 + b;} a_times_two_plus_b(4, 8) + ";
719 const char* two_byte_extern_1 = "a_times_two_plus_b(4, 8) + ";
720 const char* one_byte_string_2 = "a_times_two_plus_b(4, 8) + ";
721 const char* two_byte_string_2 = "a_times_two_plus_b(4, 8) + ";
722 const char* two_byte_extern_2 = "a_times_two_plus_b(1, 2);";
723 Local<String> left = v8_str(one_byte_string_1);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100724
725 uint16_t* two_byte_source = AsciiToTwoByteString(two_byte_string_1);
726 Local<String> right = String::New(two_byte_source);
727 i::DeleteArray(two_byte_source);
728
Steve Block3ce2e202009-11-05 08:53:23 +0000729 Local<String> source = String::Concat(left, right);
730 right = String::NewExternal(
731 new TestAsciiResource(i::StrDup(one_byte_extern_1)));
732 source = String::Concat(source, right);
733 right = String::NewExternal(
734 new TestResource(AsciiToTwoByteString(two_byte_extern_1)));
735 source = String::Concat(source, right);
736 right = v8_str(one_byte_string_2);
737 source = String::Concat(source, right);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100738
739 two_byte_source = AsciiToTwoByteString(two_byte_string_2);
740 right = String::New(two_byte_source);
741 i::DeleteArray(two_byte_source);
742
Steve Block3ce2e202009-11-05 08:53:23 +0000743 source = String::Concat(source, right);
744 right = String::NewExternal(
745 new TestResource(AsciiToTwoByteString(two_byte_extern_2)));
746 source = String::Concat(source, right);
747 Local<Script> script = Script::Compile(source);
748 Local<Value> value = script->Run();
749 CHECK(value->IsNumber());
750 CHECK_EQ(68, value->Int32Value());
751 }
Steve Block44f0eee2011-05-26 01:26:41 +0100752 i::Isolate::Current()->compilation_cache()->Clear();
753 HEAP->CollectAllGarbage(false);
754 HEAP->CollectAllGarbage(false);
Steve Block3ce2e202009-11-05 08:53:23 +0000755}
756
757
Steve Blocka7e24c12009-10-30 11:49:00 +0000758THREADED_TEST(GlobalProperties) {
759 v8::HandleScope scope;
760 LocalContext env;
761 v8::Handle<v8::Object> global = env->Global();
762 global->Set(v8_str("pi"), v8_num(3.1415926));
763 Local<Value> pi = global->Get(v8_str("pi"));
764 CHECK_EQ(3.1415926, pi->NumberValue());
765}
766
767
768static v8::Handle<Value> handle_call(const v8::Arguments& args) {
769 ApiTestFuzzer::Fuzz();
770 return v8_num(102);
771}
772
773
774static v8::Handle<Value> construct_call(const v8::Arguments& args) {
775 ApiTestFuzzer::Fuzz();
776 args.This()->Set(v8_str("x"), v8_num(1));
777 args.This()->Set(v8_str("y"), v8_num(2));
778 return args.This();
779}
780
Ben Murdochf87a2032010-10-22 12:50:53 +0100781static v8::Handle<Value> Return239(Local<String> name, const AccessorInfo&) {
782 ApiTestFuzzer::Fuzz();
783 return v8_num(239);
784}
785
786
Steve Blocka7e24c12009-10-30 11:49:00 +0000787THREADED_TEST(FunctionTemplate) {
788 v8::HandleScope scope;
789 LocalContext env;
790 {
791 Local<v8::FunctionTemplate> fun_templ =
792 v8::FunctionTemplate::New(handle_call);
793 Local<Function> fun = fun_templ->GetFunction();
794 env->Global()->Set(v8_str("obj"), fun);
795 Local<Script> script = v8_compile("obj()");
796 CHECK_EQ(102, script->Run()->Int32Value());
797 }
798 // Use SetCallHandler to initialize a function template, should work like the
799 // previous one.
800 {
801 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
802 fun_templ->SetCallHandler(handle_call);
803 Local<Function> fun = fun_templ->GetFunction();
804 env->Global()->Set(v8_str("obj"), fun);
805 Local<Script> script = v8_compile("obj()");
806 CHECK_EQ(102, script->Run()->Int32Value());
807 }
808 // Test constructor calls.
809 {
810 Local<v8::FunctionTemplate> fun_templ =
811 v8::FunctionTemplate::New(construct_call);
812 fun_templ->SetClassName(v8_str("funky"));
Ben Murdochf87a2032010-10-22 12:50:53 +0100813 fun_templ->InstanceTemplate()->SetAccessor(v8_str("m"), Return239);
Steve Blocka7e24c12009-10-30 11:49:00 +0000814 Local<Function> fun = fun_templ->GetFunction();
815 env->Global()->Set(v8_str("obj"), fun);
816 Local<Script> script = v8_compile("var s = new obj(); s.x");
817 CHECK_EQ(1, script->Run()->Int32Value());
818
819 Local<Value> result = v8_compile("(new obj()).toString()")->Run();
820 CHECK_EQ(v8_str("[object funky]"), result);
Ben Murdochf87a2032010-10-22 12:50:53 +0100821
822 result = v8_compile("(new obj()).m")->Run();
823 CHECK_EQ(239, result->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +0000824 }
825}
826
827
Ben Murdochb8e0da22011-05-16 14:20:40 +0100828static void* expected_ptr;
829static v8::Handle<v8::Value> callback(const v8::Arguments& args) {
830 void* ptr = v8::External::Unwrap(args.Data());
831 CHECK_EQ(expected_ptr, ptr);
832 return v8::Boolean::New(true);
833}
834
835
836static void TestExternalPointerWrapping() {
837 v8::HandleScope scope;
838 LocalContext env;
839
840 v8::Handle<v8::Value> data = v8::External::Wrap(expected_ptr);
841
842 v8::Handle<v8::Object> obj = v8::Object::New();
843 obj->Set(v8_str("func"),
844 v8::FunctionTemplate::New(callback, data)->GetFunction());
845 env->Global()->Set(v8_str("obj"), obj);
846
847 CHECK(CompileRun(
848 "function foo() {\n"
849 " for (var i = 0; i < 13; i++) obj.func();\n"
850 "}\n"
851 "foo(), true")->BooleanValue());
852}
853
854
855THREADED_TEST(ExternalWrap) {
856 // Check heap allocated object.
857 int* ptr = new int;
858 expected_ptr = ptr;
859 TestExternalPointerWrapping();
860 delete ptr;
861
862 // Check stack allocated object.
863 int foo;
864 expected_ptr = &foo;
865 TestExternalPointerWrapping();
866
867 // Check not aligned addresses.
868 const int n = 100;
869 char* s = new char[n];
870 for (int i = 0; i < n; i++) {
871 expected_ptr = s + i;
872 TestExternalPointerWrapping();
873 }
874
875 delete[] s;
876
877 // Check several invalid addresses.
878 expected_ptr = reinterpret_cast<void*>(1);
879 TestExternalPointerWrapping();
880
881 expected_ptr = reinterpret_cast<void*>(0xdeadbeef);
882 TestExternalPointerWrapping();
883
884 expected_ptr = reinterpret_cast<void*>(0xdeadbeef + 1);
885 TestExternalPointerWrapping();
886
887#if defined(V8_HOST_ARCH_X64)
Steve Block1e0659c2011-05-24 12:43:12 +0100888 // Check a value with a leading 1 bit in x64 Smi encoding.
889 expected_ptr = reinterpret_cast<void*>(0x400000000);
890 TestExternalPointerWrapping();
891
Ben Murdochb8e0da22011-05-16 14:20:40 +0100892 expected_ptr = reinterpret_cast<void*>(0xdeadbeefdeadbeef);
893 TestExternalPointerWrapping();
894
895 expected_ptr = reinterpret_cast<void*>(0xdeadbeefdeadbeef + 1);
896 TestExternalPointerWrapping();
897#endif
898}
899
900
Steve Blocka7e24c12009-10-30 11:49:00 +0000901THREADED_TEST(FindInstanceInPrototypeChain) {
902 v8::HandleScope scope;
903 LocalContext env;
904
905 Local<v8::FunctionTemplate> base = v8::FunctionTemplate::New();
906 Local<v8::FunctionTemplate> derived = v8::FunctionTemplate::New();
907 Local<v8::FunctionTemplate> other = v8::FunctionTemplate::New();
908 derived->Inherit(base);
909
910 Local<v8::Function> base_function = base->GetFunction();
911 Local<v8::Function> derived_function = derived->GetFunction();
912 Local<v8::Function> other_function = other->GetFunction();
913
914 Local<v8::Object> base_instance = base_function->NewInstance();
915 Local<v8::Object> derived_instance = derived_function->NewInstance();
916 Local<v8::Object> derived_instance2 = derived_function->NewInstance();
917 Local<v8::Object> other_instance = other_function->NewInstance();
918 derived_instance2->Set(v8_str("__proto__"), derived_instance);
919 other_instance->Set(v8_str("__proto__"), derived_instance2);
920
921 // base_instance is only an instance of base.
922 CHECK_EQ(base_instance,
923 base_instance->FindInstanceInPrototypeChain(base));
924 CHECK(base_instance->FindInstanceInPrototypeChain(derived).IsEmpty());
925 CHECK(base_instance->FindInstanceInPrototypeChain(other).IsEmpty());
926
927 // derived_instance is an instance of base and derived.
928 CHECK_EQ(derived_instance,
929 derived_instance->FindInstanceInPrototypeChain(base));
930 CHECK_EQ(derived_instance,
931 derived_instance->FindInstanceInPrototypeChain(derived));
932 CHECK(derived_instance->FindInstanceInPrototypeChain(other).IsEmpty());
933
934 // other_instance is an instance of other and its immediate
935 // prototype derived_instance2 is an instance of base and derived.
936 // Note, derived_instance is an instance of base and derived too,
937 // but it comes after derived_instance2 in the prototype chain of
938 // other_instance.
939 CHECK_EQ(derived_instance2,
940 other_instance->FindInstanceInPrototypeChain(base));
941 CHECK_EQ(derived_instance2,
942 other_instance->FindInstanceInPrototypeChain(derived));
943 CHECK_EQ(other_instance,
944 other_instance->FindInstanceInPrototypeChain(other));
945}
946
947
Steve Block3ce2e202009-11-05 08:53:23 +0000948THREADED_TEST(TinyInteger) {
949 v8::HandleScope scope;
950 LocalContext env;
951 int32_t value = 239;
952 Local<v8::Integer> value_obj = v8::Integer::New(value);
953 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
954}
955
956
957THREADED_TEST(BigSmiInteger) {
958 v8::HandleScope scope;
959 LocalContext env;
960 int32_t value = i::Smi::kMaxValue;
961 // We cannot add one to a Smi::kMaxValue without wrapping.
962 if (i::kSmiValueSize < 32) {
963 CHECK(i::Smi::IsValid(value));
964 CHECK(!i::Smi::IsValid(value + 1));
965 Local<v8::Integer> value_obj = v8::Integer::New(value);
966 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
967 }
968}
969
970
971THREADED_TEST(BigInteger) {
972 v8::HandleScope scope;
973 LocalContext env;
974 // We cannot add one to a Smi::kMaxValue without wrapping.
975 if (i::kSmiValueSize < 32) {
976 // The casts allow this to compile, even if Smi::kMaxValue is 2^31-1.
977 // The code will not be run in that case, due to the "if" guard.
978 int32_t value =
979 static_cast<int32_t>(static_cast<uint32_t>(i::Smi::kMaxValue) + 1);
980 CHECK(value > i::Smi::kMaxValue);
981 CHECK(!i::Smi::IsValid(value));
982 Local<v8::Integer> value_obj = v8::Integer::New(value);
983 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
984 }
985}
986
987
988THREADED_TEST(TinyUnsignedInteger) {
989 v8::HandleScope scope;
990 LocalContext env;
991 uint32_t value = 239;
992 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
993 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
994}
995
996
997THREADED_TEST(BigUnsignedSmiInteger) {
998 v8::HandleScope scope;
999 LocalContext env;
1000 uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue);
1001 CHECK(i::Smi::IsValid(value));
1002 CHECK(!i::Smi::IsValid(value + 1));
1003 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
1004 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
1005}
1006
1007
1008THREADED_TEST(BigUnsignedInteger) {
1009 v8::HandleScope scope;
1010 LocalContext env;
1011 uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue) + 1;
1012 CHECK(value > static_cast<uint32_t>(i::Smi::kMaxValue));
1013 CHECK(!i::Smi::IsValid(value));
1014 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
1015 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
1016}
1017
1018
1019THREADED_TEST(OutOfSignedRangeUnsignedInteger) {
1020 v8::HandleScope scope;
1021 LocalContext env;
1022 uint32_t INT32_MAX_AS_UINT = (1U << 31) - 1;
1023 uint32_t value = INT32_MAX_AS_UINT + 1;
1024 CHECK(value > INT32_MAX_AS_UINT); // No overflow.
1025 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
1026 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
1027}
1028
1029
Steve Blocka7e24c12009-10-30 11:49:00 +00001030THREADED_TEST(Number) {
1031 v8::HandleScope scope;
1032 LocalContext env;
1033 double PI = 3.1415926;
1034 Local<v8::Number> pi_obj = v8::Number::New(PI);
1035 CHECK_EQ(PI, pi_obj->NumberValue());
1036}
1037
1038
1039THREADED_TEST(ToNumber) {
1040 v8::HandleScope scope;
1041 LocalContext env;
1042 Local<String> str = v8_str("3.1415926");
1043 CHECK_EQ(3.1415926, str->NumberValue());
1044 v8::Handle<v8::Boolean> t = v8::True();
1045 CHECK_EQ(1.0, t->NumberValue());
1046 v8::Handle<v8::Boolean> f = v8::False();
1047 CHECK_EQ(0.0, f->NumberValue());
1048}
1049
1050
1051THREADED_TEST(Date) {
1052 v8::HandleScope scope;
1053 LocalContext env;
1054 double PI = 3.1415926;
1055 Local<Value> date_obj = v8::Date::New(PI);
1056 CHECK_EQ(3.0, date_obj->NumberValue());
1057}
1058
1059
1060THREADED_TEST(Boolean) {
1061 v8::HandleScope scope;
1062 LocalContext env;
1063 v8::Handle<v8::Boolean> t = v8::True();
1064 CHECK(t->Value());
1065 v8::Handle<v8::Boolean> f = v8::False();
1066 CHECK(!f->Value());
1067 v8::Handle<v8::Primitive> u = v8::Undefined();
1068 CHECK(!u->BooleanValue());
1069 v8::Handle<v8::Primitive> n = v8::Null();
1070 CHECK(!n->BooleanValue());
1071 v8::Handle<String> str1 = v8_str("");
1072 CHECK(!str1->BooleanValue());
1073 v8::Handle<String> str2 = v8_str("x");
1074 CHECK(str2->BooleanValue());
1075 CHECK(!v8::Number::New(0)->BooleanValue());
1076 CHECK(v8::Number::New(-1)->BooleanValue());
1077 CHECK(v8::Number::New(1)->BooleanValue());
1078 CHECK(v8::Number::New(42)->BooleanValue());
1079 CHECK(!v8_compile("NaN")->Run()->BooleanValue());
1080}
1081
1082
1083static v8::Handle<Value> DummyCallHandler(const v8::Arguments& args) {
1084 ApiTestFuzzer::Fuzz();
1085 return v8_num(13.4);
1086}
1087
1088
1089static v8::Handle<Value> GetM(Local<String> name, const AccessorInfo&) {
1090 ApiTestFuzzer::Fuzz();
1091 return v8_num(876);
1092}
1093
1094
1095THREADED_TEST(GlobalPrototype) {
1096 v8::HandleScope scope;
1097 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New();
1098 func_templ->PrototypeTemplate()->Set(
1099 "dummy",
1100 v8::FunctionTemplate::New(DummyCallHandler));
1101 v8::Handle<ObjectTemplate> templ = func_templ->InstanceTemplate();
1102 templ->Set("x", v8_num(200));
1103 templ->SetAccessor(v8_str("m"), GetM);
1104 LocalContext env(0, templ);
1105 v8::Handle<v8::Object> obj = env->Global();
1106 v8::Handle<Script> script = v8_compile("dummy()");
1107 v8::Handle<Value> result = script->Run();
1108 CHECK_EQ(13.4, result->NumberValue());
1109 CHECK_EQ(200, v8_compile("x")->Run()->Int32Value());
1110 CHECK_EQ(876, v8_compile("m")->Run()->Int32Value());
1111}
1112
1113
Steve Blocka7e24c12009-10-30 11:49:00 +00001114THREADED_TEST(ObjectTemplate) {
1115 v8::HandleScope scope;
1116 Local<ObjectTemplate> templ1 = ObjectTemplate::New();
1117 templ1->Set("x", v8_num(10));
1118 templ1->Set("y", v8_num(13));
1119 LocalContext env;
1120 Local<v8::Object> instance1 = templ1->NewInstance();
1121 env->Global()->Set(v8_str("p"), instance1);
1122 CHECK(v8_compile("(p.x == 10)")->Run()->BooleanValue());
1123 CHECK(v8_compile("(p.y == 13)")->Run()->BooleanValue());
1124 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New();
1125 fun->PrototypeTemplate()->Set("nirk", v8_num(123));
1126 Local<ObjectTemplate> templ2 = fun->InstanceTemplate();
1127 templ2->Set("a", v8_num(12));
1128 templ2->Set("b", templ1);
1129 Local<v8::Object> instance2 = templ2->NewInstance();
1130 env->Global()->Set(v8_str("q"), instance2);
1131 CHECK(v8_compile("(q.nirk == 123)")->Run()->BooleanValue());
1132 CHECK(v8_compile("(q.a == 12)")->Run()->BooleanValue());
1133 CHECK(v8_compile("(q.b.x == 10)")->Run()->BooleanValue());
1134 CHECK(v8_compile("(q.b.y == 13)")->Run()->BooleanValue());
1135}
1136
1137
1138static v8::Handle<Value> GetFlabby(const v8::Arguments& args) {
1139 ApiTestFuzzer::Fuzz();
1140 return v8_num(17.2);
1141}
1142
1143
1144static v8::Handle<Value> GetKnurd(Local<String> property, const AccessorInfo&) {
1145 ApiTestFuzzer::Fuzz();
1146 return v8_num(15.2);
1147}
1148
1149
1150THREADED_TEST(DescriptorInheritance) {
1151 v8::HandleScope scope;
1152 v8::Handle<v8::FunctionTemplate> super = v8::FunctionTemplate::New();
1153 super->PrototypeTemplate()->Set("flabby",
1154 v8::FunctionTemplate::New(GetFlabby));
1155 super->PrototypeTemplate()->Set("PI", v8_num(3.14));
1156
1157 super->InstanceTemplate()->SetAccessor(v8_str("knurd"), GetKnurd);
1158
1159 v8::Handle<v8::FunctionTemplate> base1 = v8::FunctionTemplate::New();
1160 base1->Inherit(super);
1161 base1->PrototypeTemplate()->Set("v1", v8_num(20.1));
1162
1163 v8::Handle<v8::FunctionTemplate> base2 = v8::FunctionTemplate::New();
1164 base2->Inherit(super);
1165 base2->PrototypeTemplate()->Set("v2", v8_num(10.1));
1166
1167 LocalContext env;
1168
1169 env->Global()->Set(v8_str("s"), super->GetFunction());
1170 env->Global()->Set(v8_str("base1"), base1->GetFunction());
1171 env->Global()->Set(v8_str("base2"), base2->GetFunction());
1172
1173 // Checks right __proto__ chain.
1174 CHECK(CompileRun("base1.prototype.__proto__ == s.prototype")->BooleanValue());
1175 CHECK(CompileRun("base2.prototype.__proto__ == s.prototype")->BooleanValue());
1176
1177 CHECK(v8_compile("s.prototype.PI == 3.14")->Run()->BooleanValue());
1178
1179 // Instance accessor should not be visible on function object or its prototype
1180 CHECK(CompileRun("s.knurd == undefined")->BooleanValue());
1181 CHECK(CompileRun("s.prototype.knurd == undefined")->BooleanValue());
1182 CHECK(CompileRun("base1.prototype.knurd == undefined")->BooleanValue());
1183
1184 env->Global()->Set(v8_str("obj"),
1185 base1->GetFunction()->NewInstance());
1186 CHECK_EQ(17.2, v8_compile("obj.flabby()")->Run()->NumberValue());
1187 CHECK(v8_compile("'flabby' in obj")->Run()->BooleanValue());
1188 CHECK_EQ(15.2, v8_compile("obj.knurd")->Run()->NumberValue());
1189 CHECK(v8_compile("'knurd' in obj")->Run()->BooleanValue());
1190 CHECK_EQ(20.1, v8_compile("obj.v1")->Run()->NumberValue());
1191
1192 env->Global()->Set(v8_str("obj2"),
1193 base2->GetFunction()->NewInstance());
1194 CHECK_EQ(17.2, v8_compile("obj2.flabby()")->Run()->NumberValue());
1195 CHECK(v8_compile("'flabby' in obj2")->Run()->BooleanValue());
1196 CHECK_EQ(15.2, v8_compile("obj2.knurd")->Run()->NumberValue());
1197 CHECK(v8_compile("'knurd' in obj2")->Run()->BooleanValue());
1198 CHECK_EQ(10.1, v8_compile("obj2.v2")->Run()->NumberValue());
1199
1200 // base1 and base2 cannot cross reference to each's prototype
1201 CHECK(v8_compile("obj.v2")->Run()->IsUndefined());
1202 CHECK(v8_compile("obj2.v1")->Run()->IsUndefined());
1203}
1204
1205
1206int echo_named_call_count;
1207
1208
1209static v8::Handle<Value> EchoNamedProperty(Local<String> name,
1210 const AccessorInfo& info) {
1211 ApiTestFuzzer::Fuzz();
1212 CHECK_EQ(v8_str("data"), info.Data());
1213 echo_named_call_count++;
1214 return name;
1215}
1216
1217
1218THREADED_TEST(NamedPropertyHandlerGetter) {
1219 echo_named_call_count = 0;
1220 v8::HandleScope scope;
1221 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1222 templ->InstanceTemplate()->SetNamedPropertyHandler(EchoNamedProperty,
1223 0, 0, 0, 0,
1224 v8_str("data"));
1225 LocalContext env;
1226 env->Global()->Set(v8_str("obj"),
1227 templ->GetFunction()->NewInstance());
1228 CHECK_EQ(echo_named_call_count, 0);
1229 v8_compile("obj.x")->Run();
1230 CHECK_EQ(echo_named_call_count, 1);
1231 const char* code = "var str = 'oddle'; obj[str] + obj.poddle;";
1232 v8::Handle<Value> str = CompileRun(code);
1233 String::AsciiValue value(str);
1234 CHECK_EQ(*value, "oddlepoddle");
1235 // Check default behavior
1236 CHECK_EQ(v8_compile("obj.flob = 10;")->Run()->Int32Value(), 10);
1237 CHECK(v8_compile("'myProperty' in obj")->Run()->BooleanValue());
1238 CHECK(v8_compile("delete obj.myProperty")->Run()->BooleanValue());
1239}
1240
1241
1242int echo_indexed_call_count = 0;
1243
1244
1245static v8::Handle<Value> EchoIndexedProperty(uint32_t index,
1246 const AccessorInfo& info) {
1247 ApiTestFuzzer::Fuzz();
1248 CHECK_EQ(v8_num(637), info.Data());
1249 echo_indexed_call_count++;
1250 return v8_num(index);
1251}
1252
1253
1254THREADED_TEST(IndexedPropertyHandlerGetter) {
1255 v8::HandleScope scope;
1256 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1257 templ->InstanceTemplate()->SetIndexedPropertyHandler(EchoIndexedProperty,
1258 0, 0, 0, 0,
1259 v8_num(637));
1260 LocalContext env;
1261 env->Global()->Set(v8_str("obj"),
1262 templ->GetFunction()->NewInstance());
1263 Local<Script> script = v8_compile("obj[900]");
1264 CHECK_EQ(script->Run()->Int32Value(), 900);
1265}
1266
1267
1268v8::Handle<v8::Object> bottom;
1269
1270static v8::Handle<Value> CheckThisIndexedPropertyHandler(
1271 uint32_t index,
1272 const AccessorInfo& info) {
1273 ApiTestFuzzer::Fuzz();
1274 CHECK(info.This()->Equals(bottom));
1275 return v8::Handle<Value>();
1276}
1277
1278static v8::Handle<Value> CheckThisNamedPropertyHandler(
1279 Local<String> name,
1280 const AccessorInfo& info) {
1281 ApiTestFuzzer::Fuzz();
1282 CHECK(info.This()->Equals(bottom));
1283 return v8::Handle<Value>();
1284}
1285
1286
1287v8::Handle<Value> CheckThisIndexedPropertySetter(uint32_t index,
1288 Local<Value> value,
1289 const AccessorInfo& info) {
1290 ApiTestFuzzer::Fuzz();
1291 CHECK(info.This()->Equals(bottom));
1292 return v8::Handle<Value>();
1293}
1294
1295
1296v8::Handle<Value> CheckThisNamedPropertySetter(Local<String> property,
1297 Local<Value> value,
1298 const AccessorInfo& info) {
1299 ApiTestFuzzer::Fuzz();
1300 CHECK(info.This()->Equals(bottom));
1301 return v8::Handle<Value>();
1302}
1303
Iain Merrick75681382010-08-19 15:07:18 +01001304v8::Handle<v8::Integer> CheckThisIndexedPropertyQuery(
Steve Blocka7e24c12009-10-30 11:49:00 +00001305 uint32_t index,
1306 const AccessorInfo& info) {
1307 ApiTestFuzzer::Fuzz();
1308 CHECK(info.This()->Equals(bottom));
Iain Merrick75681382010-08-19 15:07:18 +01001309 return v8::Handle<v8::Integer>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001310}
1311
1312
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001313v8::Handle<v8::Integer> CheckThisNamedPropertyQuery(Local<String> property,
Steve Blocka7e24c12009-10-30 11:49:00 +00001314 const AccessorInfo& info) {
1315 ApiTestFuzzer::Fuzz();
1316 CHECK(info.This()->Equals(bottom));
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001317 return v8::Handle<v8::Integer>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001318}
1319
1320
1321v8::Handle<v8::Boolean> CheckThisIndexedPropertyDeleter(
1322 uint32_t index,
1323 const AccessorInfo& info) {
1324 ApiTestFuzzer::Fuzz();
1325 CHECK(info.This()->Equals(bottom));
1326 return v8::Handle<v8::Boolean>();
1327}
1328
1329
1330v8::Handle<v8::Boolean> CheckThisNamedPropertyDeleter(
1331 Local<String> property,
1332 const AccessorInfo& info) {
1333 ApiTestFuzzer::Fuzz();
1334 CHECK(info.This()->Equals(bottom));
1335 return v8::Handle<v8::Boolean>();
1336}
1337
1338
1339v8::Handle<v8::Array> CheckThisIndexedPropertyEnumerator(
1340 const AccessorInfo& info) {
1341 ApiTestFuzzer::Fuzz();
1342 CHECK(info.This()->Equals(bottom));
1343 return v8::Handle<v8::Array>();
1344}
1345
1346
1347v8::Handle<v8::Array> CheckThisNamedPropertyEnumerator(
1348 const AccessorInfo& info) {
1349 ApiTestFuzzer::Fuzz();
1350 CHECK(info.This()->Equals(bottom));
1351 return v8::Handle<v8::Array>();
1352}
1353
1354
1355THREADED_TEST(PropertyHandlerInPrototype) {
1356 v8::HandleScope scope;
1357 LocalContext env;
1358
1359 // Set up a prototype chain with three interceptors.
1360 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1361 templ->InstanceTemplate()->SetIndexedPropertyHandler(
1362 CheckThisIndexedPropertyHandler,
1363 CheckThisIndexedPropertySetter,
1364 CheckThisIndexedPropertyQuery,
1365 CheckThisIndexedPropertyDeleter,
1366 CheckThisIndexedPropertyEnumerator);
1367
1368 templ->InstanceTemplate()->SetNamedPropertyHandler(
1369 CheckThisNamedPropertyHandler,
1370 CheckThisNamedPropertySetter,
1371 CheckThisNamedPropertyQuery,
1372 CheckThisNamedPropertyDeleter,
1373 CheckThisNamedPropertyEnumerator);
1374
1375 bottom = templ->GetFunction()->NewInstance();
1376 Local<v8::Object> top = templ->GetFunction()->NewInstance();
1377 Local<v8::Object> middle = templ->GetFunction()->NewInstance();
1378
1379 bottom->Set(v8_str("__proto__"), middle);
1380 middle->Set(v8_str("__proto__"), top);
1381 env->Global()->Set(v8_str("obj"), bottom);
1382
1383 // Indexed and named get.
1384 Script::Compile(v8_str("obj[0]"))->Run();
1385 Script::Compile(v8_str("obj.x"))->Run();
1386
1387 // Indexed and named set.
1388 Script::Compile(v8_str("obj[1] = 42"))->Run();
1389 Script::Compile(v8_str("obj.y = 42"))->Run();
1390
1391 // Indexed and named query.
1392 Script::Compile(v8_str("0 in obj"))->Run();
1393 Script::Compile(v8_str("'x' in obj"))->Run();
1394
1395 // Indexed and named deleter.
1396 Script::Compile(v8_str("delete obj[0]"))->Run();
1397 Script::Compile(v8_str("delete obj.x"))->Run();
1398
1399 // Enumerators.
1400 Script::Compile(v8_str("for (var p in obj) ;"))->Run();
1401}
1402
1403
1404static v8::Handle<Value> PrePropertyHandlerGet(Local<String> key,
1405 const AccessorInfo& info) {
1406 ApiTestFuzzer::Fuzz();
1407 if (v8_str("pre")->Equals(key)) {
1408 return v8_str("PrePropertyHandler: pre");
1409 }
1410 return v8::Handle<String>();
1411}
1412
1413
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001414static v8::Handle<v8::Integer> PrePropertyHandlerQuery(Local<String> key,
1415 const AccessorInfo&) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001416 if (v8_str("pre")->Equals(key)) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001417 return v8::Integer::New(v8::None);
Steve Blocka7e24c12009-10-30 11:49:00 +00001418 }
1419
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001420 return v8::Handle<v8::Integer>(); // do not intercept the call
Steve Blocka7e24c12009-10-30 11:49:00 +00001421}
1422
1423
1424THREADED_TEST(PrePropertyHandler) {
1425 v8::HandleScope scope;
1426 v8::Handle<v8::FunctionTemplate> desc = v8::FunctionTemplate::New();
1427 desc->InstanceTemplate()->SetNamedPropertyHandler(PrePropertyHandlerGet,
1428 0,
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001429 PrePropertyHandlerQuery);
Steve Blocka7e24c12009-10-30 11:49:00 +00001430 LocalContext env(NULL, desc->InstanceTemplate());
1431 Script::Compile(v8_str(
1432 "var pre = 'Object: pre'; var on = 'Object: on';"))->Run();
1433 v8::Handle<Value> result_pre = Script::Compile(v8_str("pre"))->Run();
1434 CHECK_EQ(v8_str("PrePropertyHandler: pre"), result_pre);
1435 v8::Handle<Value> result_on = Script::Compile(v8_str("on"))->Run();
1436 CHECK_EQ(v8_str("Object: on"), result_on);
1437 v8::Handle<Value> result_post = Script::Compile(v8_str("post"))->Run();
1438 CHECK(result_post.IsEmpty());
1439}
1440
1441
1442THREADED_TEST(UndefinedIsNotEnumerable) {
1443 v8::HandleScope scope;
1444 LocalContext env;
1445 v8::Handle<Value> result = Script::Compile(v8_str(
1446 "this.propertyIsEnumerable(undefined)"))->Run();
1447 CHECK(result->IsFalse());
1448}
1449
1450
1451v8::Handle<Script> call_recursively_script;
Leon Clarke4515c472010-02-03 11:58:03 +00001452static const int kTargetRecursionDepth = 200; // near maximum
Steve Blocka7e24c12009-10-30 11:49:00 +00001453
1454
1455static v8::Handle<Value> CallScriptRecursivelyCall(const v8::Arguments& args) {
1456 ApiTestFuzzer::Fuzz();
1457 int depth = args.This()->Get(v8_str("depth"))->Int32Value();
1458 if (depth == kTargetRecursionDepth) return v8::Undefined();
1459 args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1));
1460 return call_recursively_script->Run();
1461}
1462
1463
1464static v8::Handle<Value> CallFunctionRecursivelyCall(
1465 const v8::Arguments& args) {
1466 ApiTestFuzzer::Fuzz();
1467 int depth = args.This()->Get(v8_str("depth"))->Int32Value();
1468 if (depth == kTargetRecursionDepth) {
1469 printf("[depth = %d]\n", depth);
1470 return v8::Undefined();
1471 }
1472 args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1));
1473 v8::Handle<Value> function =
1474 args.This()->Get(v8_str("callFunctionRecursively"));
Steve Block6ded16b2010-05-10 14:33:55 +01001475 return function.As<Function>()->Call(args.This(), 0, NULL);
Steve Blocka7e24c12009-10-30 11:49:00 +00001476}
1477
1478
1479THREADED_TEST(DeepCrossLanguageRecursion) {
1480 v8::HandleScope scope;
1481 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New();
1482 global->Set(v8_str("callScriptRecursively"),
1483 v8::FunctionTemplate::New(CallScriptRecursivelyCall));
1484 global->Set(v8_str("callFunctionRecursively"),
1485 v8::FunctionTemplate::New(CallFunctionRecursivelyCall));
1486 LocalContext env(NULL, global);
1487
1488 env->Global()->Set(v8_str("depth"), v8::Integer::New(0));
1489 call_recursively_script = v8_compile("callScriptRecursively()");
1490 v8::Handle<Value> result = call_recursively_script->Run();
1491 call_recursively_script = v8::Handle<Script>();
1492
1493 env->Global()->Set(v8_str("depth"), v8::Integer::New(0));
1494 Script::Compile(v8_str("callFunctionRecursively()"))->Run();
1495}
1496
1497
1498static v8::Handle<Value>
1499 ThrowingPropertyHandlerGet(Local<String> key, const AccessorInfo&) {
1500 ApiTestFuzzer::Fuzz();
1501 return v8::ThrowException(key);
1502}
1503
1504
1505static v8::Handle<Value> ThrowingPropertyHandlerSet(Local<String> key,
1506 Local<Value>,
1507 const AccessorInfo&) {
1508 v8::ThrowException(key);
1509 return v8::Undefined(); // not the same as v8::Handle<v8::Value>()
1510}
1511
1512
1513THREADED_TEST(CallbackExceptionRegression) {
1514 v8::HandleScope scope;
1515 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
1516 obj->SetNamedPropertyHandler(ThrowingPropertyHandlerGet,
1517 ThrowingPropertyHandlerSet);
1518 LocalContext env;
1519 env->Global()->Set(v8_str("obj"), obj->NewInstance());
1520 v8::Handle<Value> otto = Script::Compile(v8_str(
1521 "try { with (obj) { otto; } } catch (e) { e; }"))->Run();
1522 CHECK_EQ(v8_str("otto"), otto);
1523 v8::Handle<Value> netto = Script::Compile(v8_str(
1524 "try { with (obj) { netto = 4; } } catch (e) { e; }"))->Run();
1525 CHECK_EQ(v8_str("netto"), netto);
1526}
1527
1528
Steve Blocka7e24c12009-10-30 11:49:00 +00001529THREADED_TEST(FunctionPrototype) {
1530 v8::HandleScope scope;
1531 Local<v8::FunctionTemplate> Foo = v8::FunctionTemplate::New();
1532 Foo->PrototypeTemplate()->Set(v8_str("plak"), v8_num(321));
1533 LocalContext env;
1534 env->Global()->Set(v8_str("Foo"), Foo->GetFunction());
1535 Local<Script> script = Script::Compile(v8_str("Foo.prototype.plak"));
1536 CHECK_EQ(script->Run()->Int32Value(), 321);
1537}
1538
1539
1540THREADED_TEST(InternalFields) {
1541 v8::HandleScope scope;
1542 LocalContext env;
1543
1544 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1545 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate();
1546 instance_templ->SetInternalFieldCount(1);
1547 Local<v8::Object> obj = templ->GetFunction()->NewInstance();
1548 CHECK_EQ(1, obj->InternalFieldCount());
1549 CHECK(obj->GetInternalField(0)->IsUndefined());
1550 obj->SetInternalField(0, v8_num(17));
1551 CHECK_EQ(17, obj->GetInternalField(0)->Int32Value());
1552}
1553
1554
Steve Block6ded16b2010-05-10 14:33:55 +01001555THREADED_TEST(GlobalObjectInternalFields) {
1556 v8::HandleScope scope;
1557 Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
1558 global_template->SetInternalFieldCount(1);
1559 LocalContext env(NULL, global_template);
1560 v8::Handle<v8::Object> global_proxy = env->Global();
1561 v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>();
1562 CHECK_EQ(1, global->InternalFieldCount());
1563 CHECK(global->GetInternalField(0)->IsUndefined());
1564 global->SetInternalField(0, v8_num(17));
1565 CHECK_EQ(17, global->GetInternalField(0)->Int32Value());
1566}
1567
1568
Steve Blocka7e24c12009-10-30 11:49:00 +00001569THREADED_TEST(InternalFieldsNativePointers) {
1570 v8::HandleScope scope;
1571 LocalContext env;
1572
1573 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1574 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate();
1575 instance_templ->SetInternalFieldCount(1);
1576 Local<v8::Object> obj = templ->GetFunction()->NewInstance();
1577 CHECK_EQ(1, obj->InternalFieldCount());
1578 CHECK(obj->GetPointerFromInternalField(0) == NULL);
1579
1580 char* data = new char[100];
1581
1582 void* aligned = data;
Ben Murdochf87a2032010-10-22 12:50:53 +01001583 CHECK_EQ(0, static_cast<int>(reinterpret_cast<uintptr_t>(aligned) & 0x1));
Steve Blocka7e24c12009-10-30 11:49:00 +00001584 void* unaligned = data + 1;
Ben Murdochf87a2032010-10-22 12:50:53 +01001585 CHECK_EQ(1, static_cast<int>(reinterpret_cast<uintptr_t>(unaligned) & 0x1));
Steve Blocka7e24c12009-10-30 11:49:00 +00001586
1587 // Check reading and writing aligned pointers.
1588 obj->SetPointerInInternalField(0, aligned);
Steve Block44f0eee2011-05-26 01:26:41 +01001589 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +00001590 CHECK_EQ(aligned, obj->GetPointerFromInternalField(0));
1591
1592 // Check reading and writing unaligned pointers.
1593 obj->SetPointerInInternalField(0, unaligned);
Steve Block44f0eee2011-05-26 01:26:41 +01001594 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +00001595 CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0));
1596
1597 delete[] data;
1598}
1599
1600
Steve Block3ce2e202009-11-05 08:53:23 +00001601THREADED_TEST(InternalFieldsNativePointersAndExternal) {
1602 v8::HandleScope scope;
1603 LocalContext env;
1604
1605 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1606 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate();
1607 instance_templ->SetInternalFieldCount(1);
1608 Local<v8::Object> obj = templ->GetFunction()->NewInstance();
1609 CHECK_EQ(1, obj->InternalFieldCount());
1610 CHECK(obj->GetPointerFromInternalField(0) == NULL);
1611
1612 char* data = new char[100];
1613
1614 void* aligned = data;
Ben Murdochf87a2032010-10-22 12:50:53 +01001615 CHECK_EQ(0, static_cast<int>(reinterpret_cast<uintptr_t>(aligned) & 0x1));
Steve Block3ce2e202009-11-05 08:53:23 +00001616 void* unaligned = data + 1;
Ben Murdochf87a2032010-10-22 12:50:53 +01001617 CHECK_EQ(1, static_cast<int>(reinterpret_cast<uintptr_t>(unaligned) & 0x1));
Steve Block3ce2e202009-11-05 08:53:23 +00001618
1619 obj->SetPointerInInternalField(0, aligned);
Steve Block44f0eee2011-05-26 01:26:41 +01001620 HEAP->CollectAllGarbage(false);
Steve Block3ce2e202009-11-05 08:53:23 +00001621 CHECK_EQ(aligned, v8::External::Unwrap(obj->GetInternalField(0)));
1622
1623 obj->SetPointerInInternalField(0, unaligned);
Steve Block44f0eee2011-05-26 01:26:41 +01001624 HEAP->CollectAllGarbage(false);
Steve Block3ce2e202009-11-05 08:53:23 +00001625 CHECK_EQ(unaligned, v8::External::Unwrap(obj->GetInternalField(0)));
1626
1627 obj->SetInternalField(0, v8::External::Wrap(aligned));
Steve Block44f0eee2011-05-26 01:26:41 +01001628 HEAP->CollectAllGarbage(false);
Steve Block3ce2e202009-11-05 08:53:23 +00001629 CHECK_EQ(aligned, obj->GetPointerFromInternalField(0));
1630
1631 obj->SetInternalField(0, v8::External::Wrap(unaligned));
Steve Block44f0eee2011-05-26 01:26:41 +01001632 HEAP->CollectAllGarbage(false);
Steve Block3ce2e202009-11-05 08:53:23 +00001633 CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0));
1634
1635 delete[] data;
1636}
1637
1638
Steve Blocka7e24c12009-10-30 11:49:00 +00001639THREADED_TEST(IdentityHash) {
1640 v8::HandleScope scope;
1641 LocalContext env;
1642
1643 // Ensure that the test starts with an fresh heap to test whether the hash
1644 // code is based on the address.
Steve Block44f0eee2011-05-26 01:26:41 +01001645 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +00001646 Local<v8::Object> obj = v8::Object::New();
1647 int hash = obj->GetIdentityHash();
1648 int hash1 = obj->GetIdentityHash();
1649 CHECK_EQ(hash, hash1);
1650 int hash2 = v8::Object::New()->GetIdentityHash();
1651 // Since the identity hash is essentially a random number two consecutive
1652 // objects should not be assigned the same hash code. If the test below fails
1653 // the random number generator should be evaluated.
1654 CHECK_NE(hash, hash2);
Steve Block44f0eee2011-05-26 01:26:41 +01001655 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +00001656 int hash3 = v8::Object::New()->GetIdentityHash();
1657 // Make sure that the identity hash is not based on the initial address of
1658 // the object alone. If the test below fails the random number generator
1659 // should be evaluated.
1660 CHECK_NE(hash, hash3);
1661 int hash4 = obj->GetIdentityHash();
1662 CHECK_EQ(hash, hash4);
Steve Block1e0659c2011-05-24 12:43:12 +01001663
1664 // Check identity hashes behaviour in the presence of JS accessors.
1665 // Put a getter for 'v8::IdentityHash' on the Object's prototype:
1666 {
1667 CompileRun("Object.prototype['v8::IdentityHash'] = 42;\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 }
1672 {
1673 CompileRun(
1674 "function cnst() { return 42; };\n"
1675 "Object.prototype.__defineGetter__('v8::IdentityHash', cnst);\n");
1676 Local<v8::Object> o1 = v8::Object::New();
1677 Local<v8::Object> o2 = v8::Object::New();
1678 CHECK_NE(o1->GetIdentityHash(), o2->GetIdentityHash());
1679 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001680}
1681
1682
1683THREADED_TEST(HiddenProperties) {
1684 v8::HandleScope scope;
1685 LocalContext env;
1686
1687 v8::Local<v8::Object> obj = v8::Object::New();
1688 v8::Local<v8::String> key = v8_str("api-test::hidden-key");
1689 v8::Local<v8::String> empty = v8_str("");
1690 v8::Local<v8::String> prop_name = v8_str("prop_name");
1691
Steve Block44f0eee2011-05-26 01:26:41 +01001692 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +00001693
1694 // Make sure delete of a non-existent hidden value works
1695 CHECK(obj->DeleteHiddenValue(key));
1696
1697 CHECK(obj->SetHiddenValue(key, v8::Integer::New(1503)));
1698 CHECK_EQ(1503, obj->GetHiddenValue(key)->Int32Value());
1699 CHECK(obj->SetHiddenValue(key, v8::Integer::New(2002)));
1700 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1701
Steve Block44f0eee2011-05-26 01:26:41 +01001702 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +00001703
1704 // Make sure we do not find the hidden property.
1705 CHECK(!obj->Has(empty));
1706 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1707 CHECK(obj->Get(empty)->IsUndefined());
1708 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1709 CHECK(obj->Set(empty, v8::Integer::New(2003)));
1710 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1711 CHECK_EQ(2003, obj->Get(empty)->Int32Value());
1712
Steve Block44f0eee2011-05-26 01:26:41 +01001713 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +00001714
1715 // Add another property and delete it afterwards to force the object in
1716 // slow case.
1717 CHECK(obj->Set(prop_name, v8::Integer::New(2008)));
1718 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1719 CHECK_EQ(2008, obj->Get(prop_name)->Int32Value());
1720 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1721 CHECK(obj->Delete(prop_name));
1722 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1723
Steve Block44f0eee2011-05-26 01:26:41 +01001724 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +00001725
1726 CHECK(obj->DeleteHiddenValue(key));
1727 CHECK(obj->GetHiddenValue(key).IsEmpty());
1728}
1729
1730
Steve Blockd0582a62009-12-15 09:54:21 +00001731static bool interceptor_for_hidden_properties_called;
Steve Blocka7e24c12009-10-30 11:49:00 +00001732static v8::Handle<Value> InterceptorForHiddenProperties(
1733 Local<String> name, const AccessorInfo& info) {
Steve Blockd0582a62009-12-15 09:54:21 +00001734 interceptor_for_hidden_properties_called = true;
Steve Blocka7e24c12009-10-30 11:49:00 +00001735 return v8::Handle<Value>();
1736}
1737
1738
1739THREADED_TEST(HiddenPropertiesWithInterceptors) {
1740 v8::HandleScope scope;
1741 LocalContext context;
1742
Steve Blockd0582a62009-12-15 09:54:21 +00001743 interceptor_for_hidden_properties_called = false;
1744
Steve Blocka7e24c12009-10-30 11:49:00 +00001745 v8::Local<v8::String> key = v8_str("api-test::hidden-key");
1746
1747 // Associate an interceptor with an object and start setting hidden values.
1748 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
1749 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
1750 instance_templ->SetNamedPropertyHandler(InterceptorForHiddenProperties);
1751 Local<v8::Function> function = fun_templ->GetFunction();
1752 Local<v8::Object> obj = function->NewInstance();
1753 CHECK(obj->SetHiddenValue(key, v8::Integer::New(2302)));
1754 CHECK_EQ(2302, obj->GetHiddenValue(key)->Int32Value());
Steve Blockd0582a62009-12-15 09:54:21 +00001755 CHECK(!interceptor_for_hidden_properties_called);
Steve Blocka7e24c12009-10-30 11:49:00 +00001756}
1757
1758
1759THREADED_TEST(External) {
1760 v8::HandleScope scope;
1761 int x = 3;
1762 Local<v8::External> ext = v8::External::New(&x);
1763 LocalContext env;
1764 env->Global()->Set(v8_str("ext"), ext);
1765 Local<Value> reext_obj = Script::Compile(v8_str("this.ext"))->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01001766 v8::Handle<v8::External> reext = reext_obj.As<v8::External>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001767 int* ptr = static_cast<int*>(reext->Value());
1768 CHECK_EQ(x, 3);
1769 *ptr = 10;
1770 CHECK_EQ(x, 10);
1771
1772 // Make sure unaligned pointers are wrapped properly.
1773 char* data = i::StrDup("0123456789");
1774 Local<v8::Value> zero = v8::External::Wrap(&data[0]);
1775 Local<v8::Value> one = v8::External::Wrap(&data[1]);
1776 Local<v8::Value> two = v8::External::Wrap(&data[2]);
1777 Local<v8::Value> three = v8::External::Wrap(&data[3]);
1778
1779 char* char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(zero));
1780 CHECK_EQ('0', *char_ptr);
1781 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(one));
1782 CHECK_EQ('1', *char_ptr);
1783 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(two));
1784 CHECK_EQ('2', *char_ptr);
1785 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(three));
1786 CHECK_EQ('3', *char_ptr);
1787 i::DeleteArray(data);
1788}
1789
1790
1791THREADED_TEST(GlobalHandle) {
1792 v8::Persistent<String> global;
1793 {
1794 v8::HandleScope scope;
1795 Local<String> str = v8_str("str");
1796 global = v8::Persistent<String>::New(str);
1797 }
1798 CHECK_EQ(global->Length(), 3);
1799 global.Dispose();
1800}
1801
1802
Steve Block44f0eee2011-05-26 01:26:41 +01001803static int NumberOfWeakCalls = 0;
1804static void WeakPointerCallback(Persistent<Value> handle, void* id) {
1805 CHECK_EQ(reinterpret_cast<void*>(1234), id);
1806 NumberOfWeakCalls++;
1807 handle.Dispose();
1808}
1809
1810THREADED_TEST(ApiObjectGroups) {
1811 HandleScope scope;
1812 LocalContext env;
1813
1814 NumberOfWeakCalls = 0;
1815
1816 Persistent<Object> g1s1;
1817 Persistent<Object> g1s2;
1818 Persistent<Object> g1c1;
1819 Persistent<Object> g2s1;
1820 Persistent<Object> g2s2;
1821 Persistent<Object> g2c1;
1822
1823 {
1824 HandleScope scope;
1825 g1s1 = Persistent<Object>::New(Object::New());
1826 g1s2 = Persistent<Object>::New(Object::New());
1827 g1c1 = Persistent<Object>::New(Object::New());
1828 g1s1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
1829 g1s2.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
1830 g1c1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
1831
1832 g2s1 = Persistent<Object>::New(Object::New());
1833 g2s2 = Persistent<Object>::New(Object::New());
1834 g2c1 = Persistent<Object>::New(Object::New());
1835 g2s1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
1836 g2s2.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
1837 g2c1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
1838 }
1839
1840 Persistent<Object> root = Persistent<Object>::New(g1s1); // make a root.
1841
1842 // Connect group 1 and 2, make a cycle.
1843 CHECK(g1s2->Set(0, g2s2));
1844 CHECK(g2s1->Set(0, g1s1));
1845
1846 {
1847 Persistent<Value> g1_objects[] = { g1s1, g1s2 };
1848 Persistent<Value> g1_children[] = { g1c1 };
1849 Persistent<Value> g2_objects[] = { g2s1, g2s2 };
1850 Persistent<Value> g2_children[] = { g2c1 };
1851 V8::AddObjectGroup(g1_objects, 2);
1852 V8::AddImplicitReferences(g1s1, g1_children, 1);
1853 V8::AddObjectGroup(g2_objects, 2);
1854 V8::AddImplicitReferences(g2s2, g2_children, 1);
1855 }
1856 // Do a full GC
1857 HEAP->CollectGarbage(i::OLD_POINTER_SPACE);
1858
1859 // All object should be alive.
1860 CHECK_EQ(0, NumberOfWeakCalls);
1861
1862 // Weaken the root.
1863 root.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
1864 // But make children strong roots---all the objects (except for children)
1865 // should be collectable now.
1866 g1c1.ClearWeak();
1867 g2c1.ClearWeak();
1868
1869 // Groups are deleted, rebuild groups.
1870 {
1871 Persistent<Value> g1_objects[] = { g1s1, g1s2 };
1872 Persistent<Value> g1_children[] = { g1c1 };
1873 Persistent<Value> g2_objects[] = { g2s1, g2s2 };
1874 Persistent<Value> g2_children[] = { g2c1 };
1875 V8::AddObjectGroup(g1_objects, 2);
1876 V8::AddImplicitReferences(g1s1, g1_children, 1);
1877 V8::AddObjectGroup(g2_objects, 2);
1878 V8::AddImplicitReferences(g2s2, g2_children, 1);
1879 }
1880
1881 HEAP->CollectGarbage(i::OLD_POINTER_SPACE);
1882
1883 // All objects should be gone. 5 global handles in total.
1884 CHECK_EQ(5, NumberOfWeakCalls);
1885
1886 // And now make children weak again and collect them.
1887 g1c1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
1888 g2c1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
1889
1890 HEAP->CollectGarbage(i::OLD_POINTER_SPACE);
1891 CHECK_EQ(7, NumberOfWeakCalls);
1892}
1893
1894
1895THREADED_TEST(ApiObjectGroupsCycle) {
1896 HandleScope scope;
1897 LocalContext env;
1898
1899 NumberOfWeakCalls = 0;
1900
1901 Persistent<Object> g1s1;
1902 Persistent<Object> g1s2;
1903 Persistent<Object> g2s1;
1904 Persistent<Object> g2s2;
1905 Persistent<Object> g3s1;
1906 Persistent<Object> g3s2;
1907
1908 {
1909 HandleScope scope;
1910 g1s1 = Persistent<Object>::New(Object::New());
1911 g1s2 = Persistent<Object>::New(Object::New());
1912 g1s1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
1913 g1s2.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
1914
1915 g2s1 = Persistent<Object>::New(Object::New());
1916 g2s2 = Persistent<Object>::New(Object::New());
1917 g2s1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
1918 g2s2.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
1919
1920 g3s1 = Persistent<Object>::New(Object::New());
1921 g3s2 = Persistent<Object>::New(Object::New());
1922 g3s1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
1923 g3s2.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
1924 }
1925
1926 Persistent<Object> root = Persistent<Object>::New(g1s1); // make a root.
1927
1928 // Connect groups. We're building the following cycle:
1929 // G1: { g1s1, g2s1 }, g1s1 implicitly references g2s1, ditto for other
1930 // groups.
1931 {
1932 Persistent<Value> g1_objects[] = { g1s1, g1s2 };
1933 Persistent<Value> g1_children[] = { g2s1 };
1934 Persistent<Value> g2_objects[] = { g2s1, g2s2 };
1935 Persistent<Value> g2_children[] = { g3s1 };
1936 Persistent<Value> g3_objects[] = { g3s1, g3s2 };
1937 Persistent<Value> g3_children[] = { g1s1 };
1938 V8::AddObjectGroup(g1_objects, 2);
1939 V8::AddImplicitReferences(g1s1, g1_children, 1);
1940 V8::AddObjectGroup(g2_objects, 2);
1941 V8::AddImplicitReferences(g2s1, g2_children, 1);
1942 V8::AddObjectGroup(g3_objects, 2);
1943 V8::AddImplicitReferences(g3s1, g3_children, 1);
1944 }
1945 // Do a full GC
1946 HEAP->CollectGarbage(i::OLD_POINTER_SPACE);
1947
1948 // All object should be alive.
1949 CHECK_EQ(0, NumberOfWeakCalls);
1950
1951 // Weaken the root.
1952 root.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
1953
1954 // Groups are deleted, rebuild groups.
1955 {
1956 Persistent<Value> g1_objects[] = { g1s1, g1s2 };
1957 Persistent<Value> g1_children[] = { g2s1 };
1958 Persistent<Value> g2_objects[] = { g2s1, g2s2 };
1959 Persistent<Value> g2_children[] = { g3s1 };
1960 Persistent<Value> g3_objects[] = { g3s1, g3s2 };
1961 Persistent<Value> g3_children[] = { g1s1 };
1962 V8::AddObjectGroup(g1_objects, 2);
1963 V8::AddImplicitReferences(g1s1, g1_children, 1);
1964 V8::AddObjectGroup(g2_objects, 2);
1965 V8::AddImplicitReferences(g2s1, g2_children, 1);
1966 V8::AddObjectGroup(g3_objects, 2);
1967 V8::AddImplicitReferences(g3s1, g3_children, 1);
1968 }
1969
1970 HEAP->CollectGarbage(i::OLD_POINTER_SPACE);
1971
1972 // All objects should be gone. 7 global handles in total.
1973 CHECK_EQ(7, NumberOfWeakCalls);
1974}
1975
1976
Steve Blocka7e24c12009-10-30 11:49:00 +00001977THREADED_TEST(ScriptException) {
1978 v8::HandleScope scope;
1979 LocalContext env;
1980 Local<Script> script = Script::Compile(v8_str("throw 'panama!';"));
1981 v8::TryCatch try_catch;
1982 Local<Value> result = script->Run();
1983 CHECK(result.IsEmpty());
1984 CHECK(try_catch.HasCaught());
1985 String::AsciiValue exception_value(try_catch.Exception());
1986 CHECK_EQ(*exception_value, "panama!");
1987}
1988
1989
1990bool message_received;
1991
1992
1993static void check_message(v8::Handle<v8::Message> message,
1994 v8::Handle<Value> data) {
1995 CHECK_EQ(5.76, data->NumberValue());
1996 CHECK_EQ(6.75, message->GetScriptResourceName()->NumberValue());
1997 CHECK_EQ(7.56, message->GetScriptData()->NumberValue());
1998 message_received = true;
1999}
2000
2001
2002THREADED_TEST(MessageHandlerData) {
2003 message_received = false;
2004 v8::HandleScope scope;
2005 CHECK(!message_received);
2006 v8::V8::AddMessageListener(check_message, v8_num(5.76));
2007 LocalContext context;
2008 v8::ScriptOrigin origin =
2009 v8::ScriptOrigin(v8_str("6.75"));
2010 v8::Handle<v8::Script> script = Script::Compile(v8_str("throw 'error'"),
2011 &origin);
2012 script->SetData(v8_str("7.56"));
2013 script->Run();
2014 CHECK(message_received);
2015 // clear out the message listener
2016 v8::V8::RemoveMessageListeners(check_message);
2017}
2018
2019
2020THREADED_TEST(GetSetProperty) {
2021 v8::HandleScope scope;
2022 LocalContext context;
2023 context->Global()->Set(v8_str("foo"), v8_num(14));
2024 context->Global()->Set(v8_str("12"), v8_num(92));
2025 context->Global()->Set(v8::Integer::New(16), v8_num(32));
2026 context->Global()->Set(v8_num(13), v8_num(56));
2027 Local<Value> foo = Script::Compile(v8_str("this.foo"))->Run();
2028 CHECK_EQ(14, foo->Int32Value());
2029 Local<Value> twelve = Script::Compile(v8_str("this[12]"))->Run();
2030 CHECK_EQ(92, twelve->Int32Value());
2031 Local<Value> sixteen = Script::Compile(v8_str("this[16]"))->Run();
2032 CHECK_EQ(32, sixteen->Int32Value());
2033 Local<Value> thirteen = Script::Compile(v8_str("this[13]"))->Run();
2034 CHECK_EQ(56, thirteen->Int32Value());
2035 CHECK_EQ(92, context->Global()->Get(v8::Integer::New(12))->Int32Value());
2036 CHECK_EQ(92, context->Global()->Get(v8_str("12"))->Int32Value());
2037 CHECK_EQ(92, context->Global()->Get(v8_num(12))->Int32Value());
2038 CHECK_EQ(32, context->Global()->Get(v8::Integer::New(16))->Int32Value());
2039 CHECK_EQ(32, context->Global()->Get(v8_str("16"))->Int32Value());
2040 CHECK_EQ(32, context->Global()->Get(v8_num(16))->Int32Value());
2041 CHECK_EQ(56, context->Global()->Get(v8::Integer::New(13))->Int32Value());
2042 CHECK_EQ(56, context->Global()->Get(v8_str("13"))->Int32Value());
2043 CHECK_EQ(56, context->Global()->Get(v8_num(13))->Int32Value());
2044}
2045
2046
2047THREADED_TEST(PropertyAttributes) {
2048 v8::HandleScope scope;
2049 LocalContext context;
2050 // read-only
2051 Local<String> prop = v8_str("read_only");
2052 context->Global()->Set(prop, v8_num(7), v8::ReadOnly);
2053 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value());
2054 Script::Compile(v8_str("read_only = 9"))->Run();
2055 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value());
2056 context->Global()->Set(prop, v8_num(10));
2057 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value());
2058 // dont-delete
2059 prop = v8_str("dont_delete");
2060 context->Global()->Set(prop, v8_num(13), v8::DontDelete);
2061 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value());
2062 Script::Compile(v8_str("delete dont_delete"))->Run();
2063 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value());
2064}
2065
2066
2067THREADED_TEST(Array) {
2068 v8::HandleScope scope;
2069 LocalContext context;
2070 Local<v8::Array> array = v8::Array::New();
2071 CHECK_EQ(0, array->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01002072 CHECK(array->Get(0)->IsUndefined());
Steve Blocka7e24c12009-10-30 11:49:00 +00002073 CHECK(!array->Has(0));
Steve Block6ded16b2010-05-10 14:33:55 +01002074 CHECK(array->Get(100)->IsUndefined());
Steve Blocka7e24c12009-10-30 11:49:00 +00002075 CHECK(!array->Has(100));
Steve Block6ded16b2010-05-10 14:33:55 +01002076 array->Set(2, v8_num(7));
Steve Blocka7e24c12009-10-30 11:49:00 +00002077 CHECK_EQ(3, array->Length());
2078 CHECK(!array->Has(0));
2079 CHECK(!array->Has(1));
2080 CHECK(array->Has(2));
Steve Block6ded16b2010-05-10 14:33:55 +01002081 CHECK_EQ(7, array->Get(2)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00002082 Local<Value> obj = Script::Compile(v8_str("[1, 2, 3]"))->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01002083 Local<v8::Array> arr = obj.As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00002084 CHECK_EQ(3, arr->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01002085 CHECK_EQ(1, arr->Get(0)->Int32Value());
2086 CHECK_EQ(2, arr->Get(1)->Int32Value());
2087 CHECK_EQ(3, arr->Get(2)->Int32Value());
Steve Block44f0eee2011-05-26 01:26:41 +01002088 array = v8::Array::New(27);
2089 CHECK_EQ(27, array->Length());
2090 array = v8::Array::New(-27);
2091 CHECK_EQ(0, array->Length());
Steve Blocka7e24c12009-10-30 11:49:00 +00002092}
2093
2094
2095v8::Handle<Value> HandleF(const v8::Arguments& args) {
2096 v8::HandleScope scope;
2097 ApiTestFuzzer::Fuzz();
2098 Local<v8::Array> result = v8::Array::New(args.Length());
2099 for (int i = 0; i < args.Length(); i++)
Steve Block6ded16b2010-05-10 14:33:55 +01002100 result->Set(i, args[i]);
Steve Blocka7e24c12009-10-30 11:49:00 +00002101 return scope.Close(result);
2102}
2103
2104
2105THREADED_TEST(Vector) {
2106 v8::HandleScope scope;
2107 Local<ObjectTemplate> global = ObjectTemplate::New();
2108 global->Set(v8_str("f"), v8::FunctionTemplate::New(HandleF));
2109 LocalContext context(0, global);
2110
2111 const char* fun = "f()";
Steve Block6ded16b2010-05-10 14:33:55 +01002112 Local<v8::Array> a0 = CompileRun(fun).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00002113 CHECK_EQ(0, a0->Length());
2114
2115 const char* fun2 = "f(11)";
Steve Block6ded16b2010-05-10 14:33:55 +01002116 Local<v8::Array> a1 = CompileRun(fun2).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00002117 CHECK_EQ(1, a1->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01002118 CHECK_EQ(11, a1->Get(0)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00002119
2120 const char* fun3 = "f(12, 13)";
Steve Block6ded16b2010-05-10 14:33:55 +01002121 Local<v8::Array> a2 = CompileRun(fun3).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00002122 CHECK_EQ(2, a2->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01002123 CHECK_EQ(12, a2->Get(0)->Int32Value());
2124 CHECK_EQ(13, a2->Get(1)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00002125
2126 const char* fun4 = "f(14, 15, 16)";
Steve Block6ded16b2010-05-10 14:33:55 +01002127 Local<v8::Array> a3 = CompileRun(fun4).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00002128 CHECK_EQ(3, a3->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01002129 CHECK_EQ(14, a3->Get(0)->Int32Value());
2130 CHECK_EQ(15, a3->Get(1)->Int32Value());
2131 CHECK_EQ(16, a3->Get(2)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00002132
2133 const char* fun5 = "f(17, 18, 19, 20)";
Steve Block6ded16b2010-05-10 14:33:55 +01002134 Local<v8::Array> a4 = CompileRun(fun5).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00002135 CHECK_EQ(4, a4->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01002136 CHECK_EQ(17, a4->Get(0)->Int32Value());
2137 CHECK_EQ(18, a4->Get(1)->Int32Value());
2138 CHECK_EQ(19, a4->Get(2)->Int32Value());
2139 CHECK_EQ(20, a4->Get(3)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00002140}
2141
2142
2143THREADED_TEST(FunctionCall) {
2144 v8::HandleScope scope;
2145 LocalContext context;
2146 CompileRun(
2147 "function Foo() {"
2148 " var result = [];"
2149 " for (var i = 0; i < arguments.length; i++) {"
2150 " result.push(arguments[i]);"
2151 " }"
2152 " return result;"
2153 "}");
2154 Local<Function> Foo =
2155 Local<Function>::Cast(context->Global()->Get(v8_str("Foo")));
2156
2157 v8::Handle<Value>* args0 = NULL;
2158 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->Call(Foo, 0, args0));
2159 CHECK_EQ(0, a0->Length());
2160
2161 v8::Handle<Value> args1[] = { v8_num(1.1) };
2162 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->Call(Foo, 1, args1));
2163 CHECK_EQ(1, a1->Length());
2164 CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue());
2165
2166 v8::Handle<Value> args2[] = { v8_num(2.2),
2167 v8_num(3.3) };
2168 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->Call(Foo, 2, args2));
2169 CHECK_EQ(2, a2->Length());
2170 CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue());
2171 CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue());
2172
2173 v8::Handle<Value> args3[] = { v8_num(4.4),
2174 v8_num(5.5),
2175 v8_num(6.6) };
2176 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->Call(Foo, 3, args3));
2177 CHECK_EQ(3, a3->Length());
2178 CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue());
2179 CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue());
2180 CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue());
2181
2182 v8::Handle<Value> args4[] = { v8_num(7.7),
2183 v8_num(8.8),
2184 v8_num(9.9),
2185 v8_num(10.11) };
2186 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->Call(Foo, 4, args4));
2187 CHECK_EQ(4, a4->Length());
2188 CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue());
2189 CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue());
2190 CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue());
2191 CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue());
2192}
2193
2194
2195static const char* js_code_causing_out_of_memory =
2196 "var a = new Array(); while(true) a.push(a);";
2197
2198
2199// These tests run for a long time and prevent us from running tests
2200// that come after them so they cannot run in parallel.
2201TEST(OutOfMemory) {
2202 // It's not possible to read a snapshot into a heap with different dimensions.
Steve Block8defd9f2010-07-08 12:39:36 +01002203 if (i::Snapshot::IsEnabled()) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00002204 // Set heap limits.
2205 static const int K = 1024;
2206 v8::ResourceConstraints constraints;
2207 constraints.set_max_young_space_size(256 * K);
2208 constraints.set_max_old_space_size(4 * K * K);
2209 v8::SetResourceConstraints(&constraints);
2210
2211 // Execute a script that causes out of memory.
2212 v8::HandleScope scope;
2213 LocalContext context;
2214 v8::V8::IgnoreOutOfMemoryException();
2215 Local<Script> script =
2216 Script::Compile(String::New(js_code_causing_out_of_memory));
2217 Local<Value> result = script->Run();
2218
2219 // Check for out of memory state.
2220 CHECK(result.IsEmpty());
2221 CHECK(context->HasOutOfMemoryException());
2222}
2223
2224
2225v8::Handle<Value> ProvokeOutOfMemory(const v8::Arguments& args) {
2226 ApiTestFuzzer::Fuzz();
2227
2228 v8::HandleScope scope;
2229 LocalContext context;
2230 Local<Script> script =
2231 Script::Compile(String::New(js_code_causing_out_of_memory));
2232 Local<Value> result = script->Run();
2233
2234 // Check for out of memory state.
2235 CHECK(result.IsEmpty());
2236 CHECK(context->HasOutOfMemoryException());
2237
2238 return result;
2239}
2240
2241
2242TEST(OutOfMemoryNested) {
2243 // It's not possible to read a snapshot into a heap with different dimensions.
Steve Block8defd9f2010-07-08 12:39:36 +01002244 if (i::Snapshot::IsEnabled()) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00002245 // Set heap limits.
2246 static const int K = 1024;
2247 v8::ResourceConstraints constraints;
2248 constraints.set_max_young_space_size(256 * K);
2249 constraints.set_max_old_space_size(4 * K * K);
2250 v8::SetResourceConstraints(&constraints);
2251
2252 v8::HandleScope scope;
2253 Local<ObjectTemplate> templ = ObjectTemplate::New();
2254 templ->Set(v8_str("ProvokeOutOfMemory"),
2255 v8::FunctionTemplate::New(ProvokeOutOfMemory));
2256 LocalContext context(0, templ);
2257 v8::V8::IgnoreOutOfMemoryException();
2258 Local<Value> result = CompileRun(
2259 "var thrown = false;"
2260 "try {"
2261 " ProvokeOutOfMemory();"
2262 "} catch (e) {"
2263 " thrown = true;"
2264 "}");
2265 // Check for out of memory state.
2266 CHECK(result.IsEmpty());
2267 CHECK(context->HasOutOfMemoryException());
2268}
2269
2270
2271TEST(HugeConsStringOutOfMemory) {
2272 // It's not possible to read a snapshot into a heap with different dimensions.
Steve Block8defd9f2010-07-08 12:39:36 +01002273 if (i::Snapshot::IsEnabled()) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00002274 // Set heap limits.
2275 static const int K = 1024;
2276 v8::ResourceConstraints constraints;
2277 constraints.set_max_young_space_size(256 * K);
2278 constraints.set_max_old_space_size(2 * K * K);
2279 v8::SetResourceConstraints(&constraints);
2280
2281 // Execute a script that causes out of memory.
2282 v8::V8::IgnoreOutOfMemoryException();
2283
Steve Block44f0eee2011-05-26 01:26:41 +01002284 v8::HandleScope scope;
2285 LocalContext context;
2286
Steve Blocka7e24c12009-10-30 11:49:00 +00002287 // Build huge string. This should fail with out of memory exception.
2288 Local<Value> result = CompileRun(
2289 "var str = Array.prototype.join.call({length: 513}, \"A\").toUpperCase();"
Steve Block3ce2e202009-11-05 08:53:23 +00002290 "for (var i = 0; i < 22; i++) { str = str + str; }");
Steve Blocka7e24c12009-10-30 11:49:00 +00002291
2292 // Check for out of memory state.
2293 CHECK(result.IsEmpty());
2294 CHECK(context->HasOutOfMemoryException());
2295}
2296
2297
2298THREADED_TEST(ConstructCall) {
2299 v8::HandleScope scope;
2300 LocalContext context;
2301 CompileRun(
2302 "function Foo() {"
2303 " var result = [];"
2304 " for (var i = 0; i < arguments.length; i++) {"
2305 " result.push(arguments[i]);"
2306 " }"
2307 " return result;"
2308 "}");
2309 Local<Function> Foo =
2310 Local<Function>::Cast(context->Global()->Get(v8_str("Foo")));
2311
2312 v8::Handle<Value>* args0 = NULL;
2313 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->NewInstance(0, args0));
2314 CHECK_EQ(0, a0->Length());
2315
2316 v8::Handle<Value> args1[] = { v8_num(1.1) };
2317 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->NewInstance(1, args1));
2318 CHECK_EQ(1, a1->Length());
2319 CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue());
2320
2321 v8::Handle<Value> args2[] = { v8_num(2.2),
2322 v8_num(3.3) };
2323 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->NewInstance(2, args2));
2324 CHECK_EQ(2, a2->Length());
2325 CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue());
2326 CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue());
2327
2328 v8::Handle<Value> args3[] = { v8_num(4.4),
2329 v8_num(5.5),
2330 v8_num(6.6) };
2331 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->NewInstance(3, args3));
2332 CHECK_EQ(3, a3->Length());
2333 CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue());
2334 CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue());
2335 CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue());
2336
2337 v8::Handle<Value> args4[] = { v8_num(7.7),
2338 v8_num(8.8),
2339 v8_num(9.9),
2340 v8_num(10.11) };
2341 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->NewInstance(4, args4));
2342 CHECK_EQ(4, a4->Length());
2343 CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue());
2344 CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue());
2345 CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue());
2346 CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue());
2347}
2348
2349
2350static void CheckUncle(v8::TryCatch* try_catch) {
2351 CHECK(try_catch->HasCaught());
2352 String::AsciiValue str_value(try_catch->Exception());
2353 CHECK_EQ(*str_value, "uncle?");
2354 try_catch->Reset();
2355}
2356
2357
Steve Block6ded16b2010-05-10 14:33:55 +01002358THREADED_TEST(ConversionNumber) {
2359 v8::HandleScope scope;
2360 LocalContext env;
2361 // Very large number.
2362 CompileRun("var obj = Math.pow(2,32) * 1237;");
2363 Local<Value> obj = env->Global()->Get(v8_str("obj"));
2364 CHECK_EQ(5312874545152.0, obj->ToNumber()->Value());
2365 CHECK_EQ(0, obj->ToInt32()->Value());
2366 CHECK(0u == obj->ToUint32()->Value()); // NOLINT - no CHECK_EQ for unsigned.
2367 // Large number.
2368 CompileRun("var obj = -1234567890123;");
2369 obj = env->Global()->Get(v8_str("obj"));
2370 CHECK_EQ(-1234567890123.0, obj->ToNumber()->Value());
2371 CHECK_EQ(-1912276171, obj->ToInt32()->Value());
2372 CHECK(2382691125u == obj->ToUint32()->Value()); // NOLINT
2373 // Small positive integer.
2374 CompileRun("var obj = 42;");
2375 obj = env->Global()->Get(v8_str("obj"));
2376 CHECK_EQ(42.0, obj->ToNumber()->Value());
2377 CHECK_EQ(42, obj->ToInt32()->Value());
2378 CHECK(42u == obj->ToUint32()->Value()); // NOLINT
2379 // Negative integer.
2380 CompileRun("var obj = -37;");
2381 obj = env->Global()->Get(v8_str("obj"));
2382 CHECK_EQ(-37.0, obj->ToNumber()->Value());
2383 CHECK_EQ(-37, obj->ToInt32()->Value());
2384 CHECK(4294967259u == obj->ToUint32()->Value()); // NOLINT
2385 // Positive non-int32 integer.
2386 CompileRun("var obj = 0x81234567;");
2387 obj = env->Global()->Get(v8_str("obj"));
2388 CHECK_EQ(2166572391.0, obj->ToNumber()->Value());
2389 CHECK_EQ(-2128394905, obj->ToInt32()->Value());
2390 CHECK(2166572391u == obj->ToUint32()->Value()); // NOLINT
2391 // Fraction.
2392 CompileRun("var obj = 42.3;");
2393 obj = env->Global()->Get(v8_str("obj"));
2394 CHECK_EQ(42.3, obj->ToNumber()->Value());
2395 CHECK_EQ(42, obj->ToInt32()->Value());
2396 CHECK(42u == obj->ToUint32()->Value()); // NOLINT
2397 // Large negative fraction.
2398 CompileRun("var obj = -5726623061.75;");
2399 obj = env->Global()->Get(v8_str("obj"));
2400 CHECK_EQ(-5726623061.75, obj->ToNumber()->Value());
2401 CHECK_EQ(-1431655765, obj->ToInt32()->Value());
2402 CHECK(2863311531u == obj->ToUint32()->Value()); // NOLINT
2403}
2404
2405
2406THREADED_TEST(isNumberType) {
2407 v8::HandleScope scope;
2408 LocalContext env;
2409 // Very large number.
2410 CompileRun("var obj = Math.pow(2,32) * 1237;");
2411 Local<Value> obj = env->Global()->Get(v8_str("obj"));
2412 CHECK(!obj->IsInt32());
2413 CHECK(!obj->IsUint32());
2414 // Large negative number.
2415 CompileRun("var obj = -1234567890123;");
2416 obj = env->Global()->Get(v8_str("obj"));
2417 CHECK(!obj->IsInt32());
2418 CHECK(!obj->IsUint32());
2419 // Small positive integer.
2420 CompileRun("var obj = 42;");
2421 obj = env->Global()->Get(v8_str("obj"));
2422 CHECK(obj->IsInt32());
2423 CHECK(obj->IsUint32());
2424 // Negative integer.
2425 CompileRun("var obj = -37;");
2426 obj = env->Global()->Get(v8_str("obj"));
2427 CHECK(obj->IsInt32());
2428 CHECK(!obj->IsUint32());
2429 // Positive non-int32 integer.
2430 CompileRun("var obj = 0x81234567;");
2431 obj = env->Global()->Get(v8_str("obj"));
2432 CHECK(!obj->IsInt32());
2433 CHECK(obj->IsUint32());
2434 // Fraction.
2435 CompileRun("var obj = 42.3;");
2436 obj = env->Global()->Get(v8_str("obj"));
2437 CHECK(!obj->IsInt32());
2438 CHECK(!obj->IsUint32());
2439 // Large negative fraction.
2440 CompileRun("var obj = -5726623061.75;");
2441 obj = env->Global()->Get(v8_str("obj"));
2442 CHECK(!obj->IsInt32());
2443 CHECK(!obj->IsUint32());
2444}
2445
2446
Steve Blocka7e24c12009-10-30 11:49:00 +00002447THREADED_TEST(ConversionException) {
2448 v8::HandleScope scope;
2449 LocalContext env;
2450 CompileRun(
2451 "function TestClass() { };"
2452 "TestClass.prototype.toString = function () { throw 'uncle?'; };"
2453 "var obj = new TestClass();");
2454 Local<Value> obj = env->Global()->Get(v8_str("obj"));
2455
2456 v8::TryCatch try_catch;
2457
2458 Local<Value> to_string_result = obj->ToString();
2459 CHECK(to_string_result.IsEmpty());
2460 CheckUncle(&try_catch);
2461
2462 Local<Value> to_number_result = obj->ToNumber();
2463 CHECK(to_number_result.IsEmpty());
2464 CheckUncle(&try_catch);
2465
2466 Local<Value> to_integer_result = obj->ToInteger();
2467 CHECK(to_integer_result.IsEmpty());
2468 CheckUncle(&try_catch);
2469
2470 Local<Value> to_uint32_result = obj->ToUint32();
2471 CHECK(to_uint32_result.IsEmpty());
2472 CheckUncle(&try_catch);
2473
2474 Local<Value> to_int32_result = obj->ToInt32();
2475 CHECK(to_int32_result.IsEmpty());
2476 CheckUncle(&try_catch);
2477
2478 Local<Value> to_object_result = v8::Undefined()->ToObject();
2479 CHECK(to_object_result.IsEmpty());
2480 CHECK(try_catch.HasCaught());
2481 try_catch.Reset();
2482
2483 int32_t int32_value = obj->Int32Value();
2484 CHECK_EQ(0, int32_value);
2485 CheckUncle(&try_catch);
2486
2487 uint32_t uint32_value = obj->Uint32Value();
2488 CHECK_EQ(0, uint32_value);
2489 CheckUncle(&try_catch);
2490
2491 double number_value = obj->NumberValue();
2492 CHECK_NE(0, IsNaN(number_value));
2493 CheckUncle(&try_catch);
2494
2495 int64_t integer_value = obj->IntegerValue();
2496 CHECK_EQ(0.0, static_cast<double>(integer_value));
2497 CheckUncle(&try_catch);
2498}
2499
2500
2501v8::Handle<Value> ThrowFromC(const v8::Arguments& args) {
2502 ApiTestFuzzer::Fuzz();
2503 return v8::ThrowException(v8_str("konto"));
2504}
2505
2506
2507v8::Handle<Value> CCatcher(const v8::Arguments& args) {
2508 if (args.Length() < 1) return v8::Boolean::New(false);
2509 v8::HandleScope scope;
2510 v8::TryCatch try_catch;
2511 Local<Value> result = v8::Script::Compile(args[0]->ToString())->Run();
2512 CHECK(!try_catch.HasCaught() || result.IsEmpty());
2513 return v8::Boolean::New(try_catch.HasCaught());
2514}
2515
2516
2517THREADED_TEST(APICatch) {
2518 v8::HandleScope scope;
2519 Local<ObjectTemplate> templ = ObjectTemplate::New();
2520 templ->Set(v8_str("ThrowFromC"),
2521 v8::FunctionTemplate::New(ThrowFromC));
2522 LocalContext context(0, templ);
2523 CompileRun(
2524 "var thrown = false;"
2525 "try {"
2526 " ThrowFromC();"
2527 "} catch (e) {"
2528 " thrown = true;"
2529 "}");
2530 Local<Value> thrown = context->Global()->Get(v8_str("thrown"));
2531 CHECK(thrown->BooleanValue());
2532}
2533
2534
2535THREADED_TEST(APIThrowTryCatch) {
2536 v8::HandleScope scope;
2537 Local<ObjectTemplate> templ = ObjectTemplate::New();
2538 templ->Set(v8_str("ThrowFromC"),
2539 v8::FunctionTemplate::New(ThrowFromC));
2540 LocalContext context(0, templ);
2541 v8::TryCatch try_catch;
2542 CompileRun("ThrowFromC();");
2543 CHECK(try_catch.HasCaught());
2544}
2545
2546
2547// Test that a try-finally block doesn't shadow a try-catch block
2548// when setting up an external handler.
2549//
2550// BUG(271): Some of the exception propagation does not work on the
2551// ARM simulator because the simulator separates the C++ stack and the
2552// JS stack. This test therefore fails on the simulator. The test is
2553// not threaded to allow the threading tests to run on the simulator.
2554TEST(TryCatchInTryFinally) {
2555 v8::HandleScope scope;
2556 Local<ObjectTemplate> templ = ObjectTemplate::New();
2557 templ->Set(v8_str("CCatcher"),
2558 v8::FunctionTemplate::New(CCatcher));
2559 LocalContext context(0, templ);
2560 Local<Value> result = CompileRun("try {"
2561 " try {"
2562 " CCatcher('throw 7;');"
2563 " } finally {"
2564 " }"
2565 "} catch (e) {"
2566 "}");
2567 CHECK(result->IsTrue());
2568}
2569
2570
Ben Murdochb8e0da22011-05-16 14:20:40 +01002571static void check_reference_error_message(
2572 v8::Handle<v8::Message> message,
2573 v8::Handle<v8::Value> data) {
2574 const char* reference_error = "Uncaught ReferenceError: asdf is not defined";
2575 CHECK(message->Get()->Equals(v8_str(reference_error)));
2576}
2577
2578
Steve Block1e0659c2011-05-24 12:43:12 +01002579static v8::Handle<Value> Fail(const v8::Arguments& args) {
2580 ApiTestFuzzer::Fuzz();
2581 CHECK(false);
2582 return v8::Undefined();
2583}
2584
2585
2586// Test that overwritten methods are not invoked on uncaught exception
2587// formatting. However, they are invoked when performing normal error
2588// string conversions.
Ben Murdochb8e0da22011-05-16 14:20:40 +01002589TEST(APIThrowMessageOverwrittenToString) {
2590 v8::HandleScope scope;
2591 v8::V8::AddMessageListener(check_reference_error_message);
Steve Block1e0659c2011-05-24 12:43:12 +01002592 Local<ObjectTemplate> templ = ObjectTemplate::New();
2593 templ->Set(v8_str("fail"), v8::FunctionTemplate::New(Fail));
2594 LocalContext context(NULL, templ);
2595 CompileRun("asdf;");
2596 CompileRun("var limit = {};"
2597 "limit.valueOf = fail;"
2598 "Error.stackTraceLimit = limit;");
2599 CompileRun("asdf");
2600 CompileRun("Array.prototype.pop = fail;");
2601 CompileRun("Object.prototype.hasOwnProperty = fail;");
2602 CompileRun("Object.prototype.toString = function f() { return 'Yikes'; }");
2603 CompileRun("Number.prototype.toString = function f() { return 'Yikes'; }");
2604 CompileRun("String.prototype.toString = function f() { return 'Yikes'; }");
Ben Murdochb8e0da22011-05-16 14:20:40 +01002605 CompileRun("ReferenceError.prototype.toString ="
2606 " function() { return 'Whoops' }");
2607 CompileRun("asdf;");
Steve Block1e0659c2011-05-24 12:43:12 +01002608 CompileRun("ReferenceError.prototype.constructor.name = void 0;");
2609 CompileRun("asdf;");
2610 CompileRun("ReferenceError.prototype.constructor = void 0;");
2611 CompileRun("asdf;");
2612 CompileRun("ReferenceError.prototype.__proto__ = new Object();");
2613 CompileRun("asdf;");
2614 CompileRun("ReferenceError.prototype = new Object();");
2615 CompileRun("asdf;");
Ben Murdochb8e0da22011-05-16 14:20:40 +01002616 v8::Handle<Value> string = CompileRun("try { asdf; } catch(e) { e + ''; }");
2617 CHECK(string->Equals(v8_str("Whoops")));
Steve Block1e0659c2011-05-24 12:43:12 +01002618 CompileRun("ReferenceError.prototype.constructor = new Object();"
2619 "ReferenceError.prototype.constructor.name = 1;"
2620 "Number.prototype.toString = function() { return 'Whoops'; };"
2621 "ReferenceError.prototype.toString = Object.prototype.toString;");
2622 CompileRun("asdf;");
Ben Murdochb8e0da22011-05-16 14:20:40 +01002623 v8::V8::RemoveMessageListeners(check_message);
2624}
2625
2626
Steve Blocka7e24c12009-10-30 11:49:00 +00002627static void receive_message(v8::Handle<v8::Message> message,
2628 v8::Handle<v8::Value> data) {
2629 message->Get();
2630 message_received = true;
2631}
2632
2633
2634TEST(APIThrowMessage) {
2635 message_received = false;
2636 v8::HandleScope scope;
2637 v8::V8::AddMessageListener(receive_message);
2638 Local<ObjectTemplate> templ = ObjectTemplate::New();
2639 templ->Set(v8_str("ThrowFromC"),
2640 v8::FunctionTemplate::New(ThrowFromC));
2641 LocalContext context(0, templ);
2642 CompileRun("ThrowFromC();");
2643 CHECK(message_received);
2644 v8::V8::RemoveMessageListeners(check_message);
2645}
2646
2647
2648TEST(APIThrowMessageAndVerboseTryCatch) {
2649 message_received = false;
2650 v8::HandleScope scope;
2651 v8::V8::AddMessageListener(receive_message);
2652 Local<ObjectTemplate> templ = ObjectTemplate::New();
2653 templ->Set(v8_str("ThrowFromC"),
2654 v8::FunctionTemplate::New(ThrowFromC));
2655 LocalContext context(0, templ);
2656 v8::TryCatch try_catch;
2657 try_catch.SetVerbose(true);
2658 Local<Value> result = CompileRun("ThrowFromC();");
2659 CHECK(try_catch.HasCaught());
2660 CHECK(result.IsEmpty());
2661 CHECK(message_received);
2662 v8::V8::RemoveMessageListeners(check_message);
2663}
2664
2665
Ben Murdoch8b112d22011-06-08 16:22:53 +01002666TEST(APIStackOverflowAndVerboseTryCatch) {
2667 message_received = false;
2668 v8::HandleScope scope;
2669 v8::V8::AddMessageListener(receive_message);
2670 LocalContext context;
2671 v8::TryCatch try_catch;
2672 try_catch.SetVerbose(true);
2673 Local<Value> result = CompileRun("function foo() { foo(); } foo();");
2674 CHECK(try_catch.HasCaught());
2675 CHECK(result.IsEmpty());
2676 CHECK(message_received);
2677 v8::V8::RemoveMessageListeners(receive_message);
2678}
2679
2680
Steve Blocka7e24c12009-10-30 11:49:00 +00002681THREADED_TEST(ExternalScriptException) {
2682 v8::HandleScope scope;
2683 Local<ObjectTemplate> templ = ObjectTemplate::New();
2684 templ->Set(v8_str("ThrowFromC"),
2685 v8::FunctionTemplate::New(ThrowFromC));
2686 LocalContext context(0, templ);
2687
2688 v8::TryCatch try_catch;
2689 Local<Script> script
2690 = Script::Compile(v8_str("ThrowFromC(); throw 'panama';"));
2691 Local<Value> result = script->Run();
2692 CHECK(result.IsEmpty());
2693 CHECK(try_catch.HasCaught());
2694 String::AsciiValue exception_value(try_catch.Exception());
2695 CHECK_EQ("konto", *exception_value);
2696}
2697
2698
2699
2700v8::Handle<Value> CThrowCountDown(const v8::Arguments& args) {
2701 ApiTestFuzzer::Fuzz();
2702 CHECK_EQ(4, args.Length());
2703 int count = args[0]->Int32Value();
2704 int cInterval = args[2]->Int32Value();
2705 if (count == 0) {
2706 return v8::ThrowException(v8_str("FromC"));
2707 } else {
2708 Local<v8::Object> global = Context::GetCurrent()->Global();
2709 Local<Value> fun = global->Get(v8_str("JSThrowCountDown"));
2710 v8::Handle<Value> argv[] = { v8_num(count - 1),
2711 args[1],
2712 args[2],
2713 args[3] };
2714 if (count % cInterval == 0) {
2715 v8::TryCatch try_catch;
Steve Block6ded16b2010-05-10 14:33:55 +01002716 Local<Value> result = fun.As<Function>()->Call(global, 4, argv);
Steve Blocka7e24c12009-10-30 11:49:00 +00002717 int expected = args[3]->Int32Value();
2718 if (try_catch.HasCaught()) {
2719 CHECK_EQ(expected, count);
2720 CHECK(result.IsEmpty());
Steve Block44f0eee2011-05-26 01:26:41 +01002721 CHECK(!i::Isolate::Current()->has_scheduled_exception());
Steve Blocka7e24c12009-10-30 11:49:00 +00002722 } else {
2723 CHECK_NE(expected, count);
2724 }
2725 return result;
2726 } else {
Steve Block6ded16b2010-05-10 14:33:55 +01002727 return fun.As<Function>()->Call(global, 4, argv);
Steve Blocka7e24c12009-10-30 11:49:00 +00002728 }
2729 }
2730}
2731
2732
2733v8::Handle<Value> JSCheck(const v8::Arguments& args) {
2734 ApiTestFuzzer::Fuzz();
2735 CHECK_EQ(3, args.Length());
2736 bool equality = args[0]->BooleanValue();
2737 int count = args[1]->Int32Value();
2738 int expected = args[2]->Int32Value();
2739 if (equality) {
2740 CHECK_EQ(count, expected);
2741 } else {
2742 CHECK_NE(count, expected);
2743 }
2744 return v8::Undefined();
2745}
2746
2747
2748THREADED_TEST(EvalInTryFinally) {
2749 v8::HandleScope scope;
2750 LocalContext context;
2751 v8::TryCatch try_catch;
2752 CompileRun("(function() {"
2753 " try {"
2754 " eval('asldkf (*&^&*^');"
2755 " } finally {"
2756 " return;"
2757 " }"
2758 "})()");
2759 CHECK(!try_catch.HasCaught());
2760}
2761
2762
2763// This test works by making a stack of alternating JavaScript and C
2764// activations. These activations set up exception handlers with regular
2765// intervals, one interval for C activations and another for JavaScript
2766// activations. When enough activations have been created an exception is
2767// thrown and we check that the right activation catches the exception and that
2768// no other activations do. The right activation is always the topmost one with
2769// a handler, regardless of whether it is in JavaScript or C.
2770//
2771// The notation used to describe a test case looks like this:
2772//
2773// *JS[4] *C[3] @JS[2] C[1] JS[0]
2774//
2775// Each entry is an activation, either JS or C. The index is the count at that
2776// level. Stars identify activations with exception handlers, the @ identifies
2777// the exception handler that should catch the exception.
2778//
2779// BUG(271): Some of the exception propagation does not work on the
2780// ARM simulator because the simulator separates the C++ stack and the
2781// JS stack. This test therefore fails on the simulator. The test is
2782// not threaded to allow the threading tests to run on the simulator.
2783TEST(ExceptionOrder) {
2784 v8::HandleScope scope;
2785 Local<ObjectTemplate> templ = ObjectTemplate::New();
2786 templ->Set(v8_str("check"), v8::FunctionTemplate::New(JSCheck));
2787 templ->Set(v8_str("CThrowCountDown"),
2788 v8::FunctionTemplate::New(CThrowCountDown));
2789 LocalContext context(0, templ);
2790 CompileRun(
2791 "function JSThrowCountDown(count, jsInterval, cInterval, expected) {"
2792 " if (count == 0) throw 'FromJS';"
2793 " if (count % jsInterval == 0) {"
2794 " try {"
2795 " var value = CThrowCountDown(count - 1,"
2796 " jsInterval,"
2797 " cInterval,"
2798 " expected);"
2799 " check(false, count, expected);"
2800 " return value;"
2801 " } catch (e) {"
2802 " check(true, count, expected);"
2803 " }"
2804 " } else {"
2805 " return CThrowCountDown(count - 1, jsInterval, cInterval, expected);"
2806 " }"
2807 "}");
2808 Local<Function> fun =
2809 Local<Function>::Cast(context->Global()->Get(v8_str("JSThrowCountDown")));
2810
2811 const int argc = 4;
2812 // count jsInterval cInterval expected
2813
2814 // *JS[4] *C[3] @JS[2] C[1] JS[0]
2815 v8::Handle<Value> a0[argc] = { v8_num(4), v8_num(2), v8_num(3), v8_num(2) };
2816 fun->Call(fun, argc, a0);
2817
2818 // JS[5] *C[4] JS[3] @C[2] JS[1] C[0]
2819 v8::Handle<Value> a1[argc] = { v8_num(5), v8_num(6), v8_num(1), v8_num(2) };
2820 fun->Call(fun, argc, a1);
2821
2822 // JS[6] @C[5] JS[4] C[3] JS[2] C[1] JS[0]
2823 v8::Handle<Value> a2[argc] = { v8_num(6), v8_num(7), v8_num(5), v8_num(5) };
2824 fun->Call(fun, argc, a2);
2825
2826 // @JS[6] C[5] JS[4] C[3] JS[2] C[1] JS[0]
2827 v8::Handle<Value> a3[argc] = { v8_num(6), v8_num(6), v8_num(7), v8_num(6) };
2828 fun->Call(fun, argc, a3);
2829
2830 // JS[6] *C[5] @JS[4] C[3] JS[2] C[1] JS[0]
2831 v8::Handle<Value> a4[argc] = { v8_num(6), v8_num(4), v8_num(5), v8_num(4) };
2832 fun->Call(fun, argc, a4);
2833
2834 // JS[6] C[5] *JS[4] @C[3] JS[2] C[1] JS[0]
2835 v8::Handle<Value> a5[argc] = { v8_num(6), v8_num(4), v8_num(3), v8_num(3) };
2836 fun->Call(fun, argc, a5);
2837}
2838
2839
2840v8::Handle<Value> ThrowValue(const v8::Arguments& args) {
2841 ApiTestFuzzer::Fuzz();
2842 CHECK_EQ(1, args.Length());
2843 return v8::ThrowException(args[0]);
2844}
2845
2846
2847THREADED_TEST(ThrowValues) {
2848 v8::HandleScope scope;
2849 Local<ObjectTemplate> templ = ObjectTemplate::New();
2850 templ->Set(v8_str("Throw"), v8::FunctionTemplate::New(ThrowValue));
2851 LocalContext context(0, templ);
2852 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun(
2853 "function Run(obj) {"
2854 " try {"
2855 " Throw(obj);"
2856 " } catch (e) {"
2857 " return e;"
2858 " }"
2859 " return 'no exception';"
2860 "}"
2861 "[Run('str'), Run(1), Run(0), Run(null), Run(void 0)];"));
2862 CHECK_EQ(5, result->Length());
2863 CHECK(result->Get(v8::Integer::New(0))->IsString());
2864 CHECK(result->Get(v8::Integer::New(1))->IsNumber());
2865 CHECK_EQ(1, result->Get(v8::Integer::New(1))->Int32Value());
2866 CHECK(result->Get(v8::Integer::New(2))->IsNumber());
2867 CHECK_EQ(0, result->Get(v8::Integer::New(2))->Int32Value());
2868 CHECK(result->Get(v8::Integer::New(3))->IsNull());
2869 CHECK(result->Get(v8::Integer::New(4))->IsUndefined());
2870}
2871
2872
2873THREADED_TEST(CatchZero) {
2874 v8::HandleScope scope;
2875 LocalContext context;
2876 v8::TryCatch try_catch;
2877 CHECK(!try_catch.HasCaught());
2878 Script::Compile(v8_str("throw 10"))->Run();
2879 CHECK(try_catch.HasCaught());
2880 CHECK_EQ(10, try_catch.Exception()->Int32Value());
2881 try_catch.Reset();
2882 CHECK(!try_catch.HasCaught());
2883 Script::Compile(v8_str("throw 0"))->Run();
2884 CHECK(try_catch.HasCaught());
2885 CHECK_EQ(0, try_catch.Exception()->Int32Value());
2886}
2887
2888
2889THREADED_TEST(CatchExceptionFromWith) {
2890 v8::HandleScope scope;
2891 LocalContext context;
2892 v8::TryCatch try_catch;
2893 CHECK(!try_catch.HasCaught());
2894 Script::Compile(v8_str("var o = {}; with (o) { throw 42; }"))->Run();
2895 CHECK(try_catch.HasCaught());
2896}
2897
2898
Ben Murdoche0cee9b2011-05-25 10:26:03 +01002899THREADED_TEST(TryCatchAndFinallyHidingException) {
2900 v8::HandleScope scope;
2901 LocalContext context;
2902 v8::TryCatch try_catch;
2903 CHECK(!try_catch.HasCaught());
2904 CompileRun("function f(k) { try { this[k]; } finally { return 0; } };");
2905 CompileRun("f({toString: function() { throw 42; }});");
2906 CHECK(!try_catch.HasCaught());
2907}
2908
2909
2910v8::Handle<v8::Value> WithTryCatch(const v8::Arguments& args) {
2911 v8::TryCatch try_catch;
2912 return v8::Undefined();
2913}
2914
2915
2916THREADED_TEST(TryCatchAndFinally) {
2917 v8::HandleScope scope;
2918 LocalContext context;
2919 context->Global()->Set(
2920 v8_str("native_with_try_catch"),
2921 v8::FunctionTemplate::New(WithTryCatch)->GetFunction());
2922 v8::TryCatch try_catch;
2923 CHECK(!try_catch.HasCaught());
2924 CompileRun(
2925 "try {\n"
2926 " throw new Error('a');\n"
2927 "} finally {\n"
2928 " native_with_try_catch();\n"
2929 "}\n");
2930 CHECK(try_catch.HasCaught());
2931}
2932
2933
Steve Blocka7e24c12009-10-30 11:49:00 +00002934THREADED_TEST(Equality) {
2935 v8::HandleScope scope;
2936 LocalContext context;
2937 // Check that equality works at all before relying on CHECK_EQ
2938 CHECK(v8_str("a")->Equals(v8_str("a")));
2939 CHECK(!v8_str("a")->Equals(v8_str("b")));
2940
2941 CHECK_EQ(v8_str("a"), v8_str("a"));
2942 CHECK_NE(v8_str("a"), v8_str("b"));
2943 CHECK_EQ(v8_num(1), v8_num(1));
2944 CHECK_EQ(v8_num(1.00), v8_num(1));
2945 CHECK_NE(v8_num(1), v8_num(2));
2946
2947 // Assume String is not symbol.
2948 CHECK(v8_str("a")->StrictEquals(v8_str("a")));
2949 CHECK(!v8_str("a")->StrictEquals(v8_str("b")));
2950 CHECK(!v8_str("5")->StrictEquals(v8_num(5)));
2951 CHECK(v8_num(1)->StrictEquals(v8_num(1)));
2952 CHECK(!v8_num(1)->StrictEquals(v8_num(2)));
2953 CHECK(v8_num(0)->StrictEquals(v8_num(-0)));
2954 Local<Value> not_a_number = v8_num(i::OS::nan_value());
2955 CHECK(!not_a_number->StrictEquals(not_a_number));
2956 CHECK(v8::False()->StrictEquals(v8::False()));
2957 CHECK(!v8::False()->StrictEquals(v8::Undefined()));
2958
2959 v8::Handle<v8::Object> obj = v8::Object::New();
2960 v8::Persistent<v8::Object> alias = v8::Persistent<v8::Object>::New(obj);
2961 CHECK(alias->StrictEquals(obj));
2962 alias.Dispose();
2963}
2964
2965
2966THREADED_TEST(MultiRun) {
2967 v8::HandleScope scope;
2968 LocalContext context;
2969 Local<Script> script = Script::Compile(v8_str("x"));
2970 for (int i = 0; i < 10; i++)
2971 script->Run();
2972}
2973
2974
2975static v8::Handle<Value> GetXValue(Local<String> name,
2976 const AccessorInfo& info) {
2977 ApiTestFuzzer::Fuzz();
2978 CHECK_EQ(info.Data(), v8_str("donut"));
2979 CHECK_EQ(name, v8_str("x"));
2980 return name;
2981}
2982
2983
2984THREADED_TEST(SimplePropertyRead) {
2985 v8::HandleScope scope;
2986 Local<ObjectTemplate> templ = ObjectTemplate::New();
2987 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"));
2988 LocalContext context;
2989 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2990 Local<Script> script = Script::Compile(v8_str("obj.x"));
2991 for (int i = 0; i < 10; i++) {
2992 Local<Value> result = script->Run();
2993 CHECK_EQ(result, v8_str("x"));
2994 }
2995}
2996
Andrei Popescu31002712010-02-23 13:46:05 +00002997THREADED_TEST(DefinePropertyOnAPIAccessor) {
2998 v8::HandleScope scope;
2999 Local<ObjectTemplate> templ = ObjectTemplate::New();
3000 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"));
3001 LocalContext context;
3002 context->Global()->Set(v8_str("obj"), templ->NewInstance());
3003
3004 // Uses getOwnPropertyDescriptor to check the configurable status
3005 Local<Script> script_desc
Leon Clarkef7060e22010-06-03 12:02:55 +01003006 = Script::Compile(v8_str("var prop = Object.getOwnPropertyDescriptor( "
Andrei Popescu31002712010-02-23 13:46:05 +00003007 "obj, 'x');"
3008 "prop.configurable;"));
3009 Local<Value> result = script_desc->Run();
3010 CHECK_EQ(result->BooleanValue(), true);
3011
3012 // Redefine get - but still configurable
3013 Local<Script> script_define
3014 = Script::Compile(v8_str("var desc = { get: function(){return 42; },"
3015 " configurable: true };"
3016 "Object.defineProperty(obj, 'x', desc);"
3017 "obj.x"));
3018 result = script_define->Run();
3019 CHECK_EQ(result, v8_num(42));
3020
3021 // Check that the accessor is still configurable
3022 result = script_desc->Run();
3023 CHECK_EQ(result->BooleanValue(), true);
3024
3025 // Redefine to a non-configurable
3026 script_define
3027 = Script::Compile(v8_str("var desc = { get: function(){return 43; },"
3028 " configurable: false };"
3029 "Object.defineProperty(obj, 'x', desc);"
3030 "obj.x"));
3031 result = script_define->Run();
3032 CHECK_EQ(result, v8_num(43));
3033 result = script_desc->Run();
3034 CHECK_EQ(result->BooleanValue(), false);
3035
3036 // Make sure that it is not possible to redefine again
3037 v8::TryCatch try_catch;
3038 result = script_define->Run();
3039 CHECK(try_catch.HasCaught());
3040 String::AsciiValue exception_value(try_catch.Exception());
3041 CHECK_EQ(*exception_value,
3042 "TypeError: Cannot redefine property: defineProperty");
3043}
3044
3045THREADED_TEST(DefinePropertyOnDefineGetterSetter) {
3046 v8::HandleScope scope;
3047 Local<ObjectTemplate> templ = ObjectTemplate::New();
3048 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"));
3049 LocalContext context;
3050 context->Global()->Set(v8_str("obj"), templ->NewInstance());
3051
3052 Local<Script> script_desc = Script::Compile(v8_str("var prop ="
3053 "Object.getOwnPropertyDescriptor( "
3054 "obj, 'x');"
3055 "prop.configurable;"));
3056 Local<Value> result = script_desc->Run();
3057 CHECK_EQ(result->BooleanValue(), true);
3058
3059 Local<Script> script_define =
3060 Script::Compile(v8_str("var desc = {get: function(){return 42; },"
3061 " configurable: true };"
3062 "Object.defineProperty(obj, 'x', desc);"
3063 "obj.x"));
3064 result = script_define->Run();
3065 CHECK_EQ(result, v8_num(42));
3066
3067
3068 result = script_desc->Run();
3069 CHECK_EQ(result->BooleanValue(), true);
3070
3071
3072 script_define =
3073 Script::Compile(v8_str("var desc = {get: function(){return 43; },"
3074 " configurable: false };"
3075 "Object.defineProperty(obj, 'x', desc);"
3076 "obj.x"));
3077 result = script_define->Run();
3078 CHECK_EQ(result, v8_num(43));
3079 result = script_desc->Run();
3080
3081 CHECK_EQ(result->BooleanValue(), false);
3082
3083 v8::TryCatch try_catch;
3084 result = script_define->Run();
3085 CHECK(try_catch.HasCaught());
3086 String::AsciiValue exception_value(try_catch.Exception());
3087 CHECK_EQ(*exception_value,
3088 "TypeError: Cannot redefine property: defineProperty");
3089}
3090
3091
Leon Clarkef7060e22010-06-03 12:02:55 +01003092static v8::Handle<v8::Object> GetGlobalProperty(LocalContext* context,
3093 char const* name) {
3094 return v8::Handle<v8::Object>::Cast((*context)->Global()->Get(v8_str(name)));
3095}
Andrei Popescu31002712010-02-23 13:46:05 +00003096
3097
Leon Clarkef7060e22010-06-03 12:02:55 +01003098THREADED_TEST(DefineAPIAccessorOnObject) {
3099 v8::HandleScope scope;
3100 Local<ObjectTemplate> templ = ObjectTemplate::New();
3101 LocalContext context;
3102
3103 context->Global()->Set(v8_str("obj1"), templ->NewInstance());
3104 CompileRun("var obj2 = {};");
3105
3106 CHECK(CompileRun("obj1.x")->IsUndefined());
3107 CHECK(CompileRun("obj2.x")->IsUndefined());
3108
3109 CHECK(GetGlobalProperty(&context, "obj1")->
3110 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
3111
3112 ExpectString("obj1.x", "x");
3113 CHECK(CompileRun("obj2.x")->IsUndefined());
3114
3115 CHECK(GetGlobalProperty(&context, "obj2")->
3116 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
3117
3118 ExpectString("obj1.x", "x");
3119 ExpectString("obj2.x", "x");
3120
3121 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
3122 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
3123
3124 CompileRun("Object.defineProperty(obj1, 'x',"
3125 "{ get: function() { return 'y'; }, configurable: true })");
3126
3127 ExpectString("obj1.x", "y");
3128 ExpectString("obj2.x", "x");
3129
3130 CompileRun("Object.defineProperty(obj2, 'x',"
3131 "{ get: function() { return 'y'; }, configurable: true })");
3132
3133 ExpectString("obj1.x", "y");
3134 ExpectString("obj2.x", "y");
3135
3136 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
3137 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
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", "x");
3145 ExpectString("obj2.x", "x");
3146
3147 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
3148 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
3149
3150 // Define getters/setters, but now make them not configurable.
3151 CompileRun("Object.defineProperty(obj1, 'x',"
3152 "{ get: function() { return 'z'; }, configurable: false })");
3153 CompileRun("Object.defineProperty(obj2, 'x',"
3154 "{ get: function() { return 'z'; }, configurable: false })");
3155
3156 ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
3157 ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
3158
3159 ExpectString("obj1.x", "z");
3160 ExpectString("obj2.x", "z");
3161
3162 CHECK(!GetGlobalProperty(&context, "obj1")->
3163 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
3164 CHECK(!GetGlobalProperty(&context, "obj2")->
3165 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
3166
3167 ExpectString("obj1.x", "z");
3168 ExpectString("obj2.x", "z");
3169}
3170
3171
3172THREADED_TEST(DontDeleteAPIAccessorsCannotBeOverriden) {
3173 v8::HandleScope scope;
3174 Local<ObjectTemplate> templ = ObjectTemplate::New();
3175 LocalContext context;
3176
3177 context->Global()->Set(v8_str("obj1"), templ->NewInstance());
3178 CompileRun("var obj2 = {};");
3179
3180 CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor(
3181 v8_str("x"),
3182 GetXValue, NULL,
3183 v8_str("donut"), v8::DEFAULT, v8::DontDelete));
3184 CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor(
3185 v8_str("x"),
3186 GetXValue, NULL,
3187 v8_str("donut"), v8::DEFAULT, v8::DontDelete));
3188
3189 ExpectString("obj1.x", "x");
3190 ExpectString("obj2.x", "x");
3191
3192 ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
3193 ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
3194
3195 CHECK(!GetGlobalProperty(&context, "obj1")->
3196 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
3197 CHECK(!GetGlobalProperty(&context, "obj2")->
3198 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
3199
3200 {
3201 v8::TryCatch try_catch;
3202 CompileRun("Object.defineProperty(obj1, 'x',"
3203 "{get: function() { return 'func'; }})");
3204 CHECK(try_catch.HasCaught());
3205 String::AsciiValue exception_value(try_catch.Exception());
3206 CHECK_EQ(*exception_value,
3207 "TypeError: Cannot redefine property: defineProperty");
3208 }
3209 {
3210 v8::TryCatch try_catch;
3211 CompileRun("Object.defineProperty(obj2, 'x',"
3212 "{get: function() { return 'func'; }})");
3213 CHECK(try_catch.HasCaught());
3214 String::AsciiValue exception_value(try_catch.Exception());
3215 CHECK_EQ(*exception_value,
3216 "TypeError: Cannot redefine property: defineProperty");
3217 }
3218}
3219
3220
3221static v8::Handle<Value> Get239Value(Local<String> name,
3222 const AccessorInfo& info) {
3223 ApiTestFuzzer::Fuzz();
3224 CHECK_EQ(info.Data(), v8_str("donut"));
3225 CHECK_EQ(name, v8_str("239"));
3226 return name;
3227}
3228
3229
3230THREADED_TEST(ElementAPIAccessor) {
3231 v8::HandleScope scope;
3232 Local<ObjectTemplate> templ = ObjectTemplate::New();
3233 LocalContext context;
3234
3235 context->Global()->Set(v8_str("obj1"), templ->NewInstance());
3236 CompileRun("var obj2 = {};");
3237
3238 CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor(
3239 v8_str("239"),
3240 Get239Value, NULL,
3241 v8_str("donut")));
3242 CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor(
3243 v8_str("239"),
3244 Get239Value, NULL,
3245 v8_str("donut")));
3246
3247 ExpectString("obj1[239]", "239");
3248 ExpectString("obj2[239]", "239");
3249 ExpectString("obj1['239']", "239");
3250 ExpectString("obj2['239']", "239");
3251}
3252
Steve Blocka7e24c12009-10-30 11:49:00 +00003253
3254v8::Persistent<Value> xValue;
3255
3256
3257static void SetXValue(Local<String> name,
3258 Local<Value> value,
3259 const AccessorInfo& info) {
3260 CHECK_EQ(value, v8_num(4));
3261 CHECK_EQ(info.Data(), v8_str("donut"));
3262 CHECK_EQ(name, v8_str("x"));
3263 CHECK(xValue.IsEmpty());
3264 xValue = v8::Persistent<Value>::New(value);
3265}
3266
3267
3268THREADED_TEST(SimplePropertyWrite) {
3269 v8::HandleScope scope;
3270 Local<ObjectTemplate> templ = ObjectTemplate::New();
3271 templ->SetAccessor(v8_str("x"), GetXValue, SetXValue, v8_str("donut"));
3272 LocalContext context;
3273 context->Global()->Set(v8_str("obj"), templ->NewInstance());
3274 Local<Script> script = Script::Compile(v8_str("obj.x = 4"));
3275 for (int i = 0; i < 10; i++) {
3276 CHECK(xValue.IsEmpty());
3277 script->Run();
3278 CHECK_EQ(v8_num(4), xValue);
3279 xValue.Dispose();
3280 xValue = v8::Persistent<Value>();
3281 }
3282}
3283
3284
3285static v8::Handle<Value> XPropertyGetter(Local<String> property,
3286 const AccessorInfo& info) {
3287 ApiTestFuzzer::Fuzz();
3288 CHECK(info.Data()->IsUndefined());
3289 return property;
3290}
3291
3292
3293THREADED_TEST(NamedInterceptorPropertyRead) {
3294 v8::HandleScope scope;
3295 Local<ObjectTemplate> templ = ObjectTemplate::New();
3296 templ->SetNamedPropertyHandler(XPropertyGetter);
3297 LocalContext context;
3298 context->Global()->Set(v8_str("obj"), templ->NewInstance());
3299 Local<Script> script = Script::Compile(v8_str("obj.x"));
3300 for (int i = 0; i < 10; i++) {
3301 Local<Value> result = script->Run();
3302 CHECK_EQ(result, v8_str("x"));
3303 }
3304}
3305
3306
Steve Block6ded16b2010-05-10 14:33:55 +01003307THREADED_TEST(NamedInterceptorDictionaryIC) {
3308 v8::HandleScope scope;
3309 Local<ObjectTemplate> templ = ObjectTemplate::New();
3310 templ->SetNamedPropertyHandler(XPropertyGetter);
3311 LocalContext context;
3312 // Create an object with a named interceptor.
3313 context->Global()->Set(v8_str("interceptor_obj"), templ->NewInstance());
3314 Local<Script> script = Script::Compile(v8_str("interceptor_obj.x"));
3315 for (int i = 0; i < 10; i++) {
3316 Local<Value> result = script->Run();
3317 CHECK_EQ(result, v8_str("x"));
3318 }
3319 // Create a slow case object and a function accessing a property in
3320 // that slow case object (with dictionary probing in generated
3321 // code). Then force object with a named interceptor into slow-case,
3322 // pass it to the function, and check that the interceptor is called
3323 // instead of accessing the local property.
3324 Local<Value> result =
3325 CompileRun("function get_x(o) { return o.x; };"
3326 "var obj = { x : 42, y : 0 };"
3327 "delete obj.y;"
3328 "for (var i = 0; i < 10; i++) get_x(obj);"
3329 "interceptor_obj.x = 42;"
3330 "interceptor_obj.y = 10;"
3331 "delete interceptor_obj.y;"
3332 "get_x(interceptor_obj)");
3333 CHECK_EQ(result, v8_str("x"));
3334}
3335
3336
Kristian Monsen0d5e1162010-09-30 15:31:59 +01003337THREADED_TEST(NamedInterceptorDictionaryICMultipleContext) {
3338 v8::HandleScope scope;
3339
3340 v8::Persistent<Context> context1 = Context::New();
3341
3342 context1->Enter();
3343 Local<ObjectTemplate> templ = ObjectTemplate::New();
3344 templ->SetNamedPropertyHandler(XPropertyGetter);
3345 // Create an object with a named interceptor.
3346 v8::Local<v8::Object> object = templ->NewInstance();
3347 context1->Global()->Set(v8_str("interceptor_obj"), object);
3348
3349 // Force the object into the slow case.
3350 CompileRun("interceptor_obj.y = 0;"
3351 "delete interceptor_obj.y;");
3352 context1->Exit();
3353
3354 {
3355 // Introduce the object into a different context.
3356 // Repeat named loads to exercise ICs.
3357 LocalContext context2;
3358 context2->Global()->Set(v8_str("interceptor_obj"), object);
3359 Local<Value> result =
3360 CompileRun("function get_x(o) { return o.x; }"
3361 "interceptor_obj.x = 42;"
3362 "for (var i=0; i != 10; i++) {"
3363 " get_x(interceptor_obj);"
3364 "}"
3365 "get_x(interceptor_obj)");
3366 // Check that the interceptor was actually invoked.
3367 CHECK_EQ(result, v8_str("x"));
3368 }
3369
3370 // Return to the original context and force some object to the slow case
3371 // to cause the NormalizedMapCache to verify.
3372 context1->Enter();
3373 CompileRun("var obj = { x : 0 }; delete obj.x;");
3374 context1->Exit();
3375
3376 context1.Dispose();
3377}
3378
3379
Andrei Popescu402d9372010-02-26 13:31:12 +00003380static v8::Handle<Value> SetXOnPrototypeGetter(Local<String> property,
3381 const AccessorInfo& info) {
3382 // Set x on the prototype object and do not handle the get request.
3383 v8::Handle<v8::Value> proto = info.Holder()->GetPrototype();
Steve Block6ded16b2010-05-10 14:33:55 +01003384 proto.As<v8::Object>()->Set(v8_str("x"), v8::Integer::New(23));
Andrei Popescu402d9372010-02-26 13:31:12 +00003385 return v8::Handle<Value>();
3386}
3387
3388
3389// This is a regression test for http://crbug.com/20104. Map
3390// transitions should not interfere with post interceptor lookup.
3391THREADED_TEST(NamedInterceptorMapTransitionRead) {
3392 v8::HandleScope scope;
3393 Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New();
3394 Local<v8::ObjectTemplate> instance_template
3395 = function_template->InstanceTemplate();
3396 instance_template->SetNamedPropertyHandler(SetXOnPrototypeGetter);
3397 LocalContext context;
3398 context->Global()->Set(v8_str("F"), function_template->GetFunction());
3399 // Create an instance of F and introduce a map transition for x.
3400 CompileRun("var o = new F(); o.x = 23;");
3401 // Create an instance of F and invoke the getter. The result should be 23.
3402 Local<Value> result = CompileRun("o = new F(); o.x");
3403 CHECK_EQ(result->Int32Value(), 23);
3404}
3405
3406
Steve Blocka7e24c12009-10-30 11:49:00 +00003407static v8::Handle<Value> IndexedPropertyGetter(uint32_t index,
3408 const AccessorInfo& info) {
3409 ApiTestFuzzer::Fuzz();
3410 if (index == 37) {
3411 return v8::Handle<Value>(v8_num(625));
3412 }
3413 return v8::Handle<Value>();
3414}
3415
3416
3417static v8::Handle<Value> IndexedPropertySetter(uint32_t index,
3418 Local<Value> value,
3419 const AccessorInfo& info) {
3420 ApiTestFuzzer::Fuzz();
3421 if (index == 39) {
3422 return value;
3423 }
3424 return v8::Handle<Value>();
3425}
3426
3427
3428THREADED_TEST(IndexedInterceptorWithIndexedAccessor) {
3429 v8::HandleScope scope;
3430 Local<ObjectTemplate> templ = ObjectTemplate::New();
3431 templ->SetIndexedPropertyHandler(IndexedPropertyGetter,
3432 IndexedPropertySetter);
3433 LocalContext context;
3434 context->Global()->Set(v8_str("obj"), templ->NewInstance());
3435 Local<Script> getter_script = Script::Compile(v8_str(
3436 "obj.__defineGetter__(\"3\", function(){return 5;});obj[3];"));
3437 Local<Script> setter_script = Script::Compile(v8_str(
3438 "obj.__defineSetter__(\"17\", function(val){this.foo = val;});"
3439 "obj[17] = 23;"
3440 "obj.foo;"));
3441 Local<Script> interceptor_setter_script = Script::Compile(v8_str(
3442 "obj.__defineSetter__(\"39\", function(val){this.foo = \"hit\";});"
3443 "obj[39] = 47;"
3444 "obj.foo;")); // This setter should not run, due to the interceptor.
3445 Local<Script> interceptor_getter_script = Script::Compile(v8_str(
3446 "obj[37];"));
3447 Local<Value> result = getter_script->Run();
3448 CHECK_EQ(v8_num(5), result);
3449 result = setter_script->Run();
3450 CHECK_EQ(v8_num(23), result);
3451 result = interceptor_setter_script->Run();
3452 CHECK_EQ(v8_num(23), result);
3453 result = interceptor_getter_script->Run();
3454 CHECK_EQ(v8_num(625), result);
3455}
3456
3457
Leon Clarked91b9f72010-01-27 17:25:45 +00003458static v8::Handle<Value> IdentityIndexedPropertyGetter(
3459 uint32_t index,
3460 const AccessorInfo& info) {
Ben Murdochf87a2032010-10-22 12:50:53 +01003461 return v8::Integer::NewFromUnsigned(index);
Leon Clarked91b9f72010-01-27 17:25:45 +00003462}
3463
3464
Kristian Monsen0d5e1162010-09-30 15:31:59 +01003465THREADED_TEST(IndexedInterceptorWithGetOwnPropertyDescriptor) {
3466 v8::HandleScope scope;
3467 Local<ObjectTemplate> templ = ObjectTemplate::New();
3468 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3469
3470 LocalContext context;
3471 context->Global()->Set(v8_str("obj"), templ->NewInstance());
3472
3473 // Check fast object case.
3474 const char* fast_case_code =
3475 "Object.getOwnPropertyDescriptor(obj, 0).value.toString()";
3476 ExpectString(fast_case_code, "0");
3477
3478 // Check slow case.
3479 const char* slow_case_code =
3480 "obj.x = 1; delete obj.x;"
3481 "Object.getOwnPropertyDescriptor(obj, 1).value.toString()";
3482 ExpectString(slow_case_code, "1");
3483}
3484
3485
Leon Clarked91b9f72010-01-27 17:25:45 +00003486THREADED_TEST(IndexedInterceptorWithNoSetter) {
3487 v8::HandleScope scope;
3488 Local<ObjectTemplate> templ = ObjectTemplate::New();
3489 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3490
3491 LocalContext context;
3492 context->Global()->Set(v8_str("obj"), templ->NewInstance());
3493
3494 const char* code =
3495 "try {"
3496 " obj[0] = 239;"
3497 " for (var i = 0; i < 100; i++) {"
3498 " var v = obj[0];"
3499 " if (v != 0) throw 'Wrong value ' + v + ' at iteration ' + i;"
3500 " }"
3501 " 'PASSED'"
3502 "} catch(e) {"
3503 " e"
3504 "}";
3505 ExpectString(code, "PASSED");
3506}
3507
3508
Andrei Popescu402d9372010-02-26 13:31:12 +00003509THREADED_TEST(IndexedInterceptorWithAccessorCheck) {
3510 v8::HandleScope scope;
3511 Local<ObjectTemplate> templ = ObjectTemplate::New();
3512 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3513
3514 LocalContext context;
3515 Local<v8::Object> obj = templ->NewInstance();
3516 obj->TurnOnAccessCheck();
3517 context->Global()->Set(v8_str("obj"), obj);
3518
3519 const char* code =
3520 "try {"
3521 " for (var i = 0; i < 100; i++) {"
3522 " var v = obj[0];"
3523 " if (v != undefined) throw 'Wrong value ' + v + ' at iteration ' + i;"
3524 " }"
3525 " 'PASSED'"
3526 "} catch(e) {"
3527 " e"
3528 "}";
3529 ExpectString(code, "PASSED");
3530}
3531
3532
3533THREADED_TEST(IndexedInterceptorWithAccessorCheckSwitchedOn) {
3534 i::FLAG_allow_natives_syntax = true;
3535 v8::HandleScope scope;
3536 Local<ObjectTemplate> templ = ObjectTemplate::New();
3537 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3538
3539 LocalContext context;
3540 Local<v8::Object> obj = templ->NewInstance();
3541 context->Global()->Set(v8_str("obj"), obj);
3542
3543 const char* code =
3544 "try {"
3545 " for (var i = 0; i < 100; i++) {"
3546 " var expected = i;"
3547 " if (i == 5) {"
3548 " %EnableAccessChecks(obj);"
3549 " expected = undefined;"
3550 " }"
3551 " var v = obj[i];"
3552 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3553 " if (i == 5) %DisableAccessChecks(obj);"
3554 " }"
3555 " 'PASSED'"
3556 "} catch(e) {"
3557 " e"
3558 "}";
3559 ExpectString(code, "PASSED");
3560}
3561
3562
3563THREADED_TEST(IndexedInterceptorWithDifferentIndices) {
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 v = obj[i];"
3576 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;"
3577 " }"
3578 " 'PASSED'"
3579 "} catch(e) {"
3580 " e"
3581 "}";
3582 ExpectString(code, "PASSED");
3583}
3584
3585
Ben Murdochf87a2032010-10-22 12:50:53 +01003586THREADED_TEST(IndexedInterceptorWithNegativeIndices) {
3587 v8::HandleScope scope;
3588 Local<ObjectTemplate> templ = ObjectTemplate::New();
3589 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3590
3591 LocalContext context;
3592 Local<v8::Object> obj = templ->NewInstance();
3593 context->Global()->Set(v8_str("obj"), obj);
3594
3595 const char* code =
3596 "try {"
3597 " for (var i = 0; i < 100; i++) {"
3598 " var expected = i;"
3599 " var key = i;"
3600 " if (i == 25) {"
3601 " key = -1;"
3602 " expected = undefined;"
3603 " }"
3604 " if (i == 50) {"
3605 " /* probe minimal Smi number on 32-bit platforms */"
3606 " key = -(1 << 30);"
3607 " expected = undefined;"
3608 " }"
3609 " if (i == 75) {"
3610 " /* probe minimal Smi number on 64-bit platforms */"
3611 " key = 1 << 31;"
3612 " expected = undefined;"
3613 " }"
3614 " var v = obj[key];"
3615 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3616 " }"
3617 " 'PASSED'"
3618 "} catch(e) {"
3619 " e"
3620 "}";
3621 ExpectString(code, "PASSED");
3622}
3623
3624
Andrei Popescu402d9372010-02-26 13:31:12 +00003625THREADED_TEST(IndexedInterceptorWithNotSmiLookup) {
3626 v8::HandleScope scope;
3627 Local<ObjectTemplate> templ = ObjectTemplate::New();
3628 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3629
3630 LocalContext context;
3631 Local<v8::Object> obj = templ->NewInstance();
3632 context->Global()->Set(v8_str("obj"), obj);
3633
3634 const char* code =
3635 "try {"
3636 " for (var i = 0; i < 100; i++) {"
3637 " var expected = i;"
Ben Murdochf87a2032010-10-22 12:50:53 +01003638 " var key = i;"
Andrei Popescu402d9372010-02-26 13:31:12 +00003639 " if (i == 50) {"
Ben Murdochf87a2032010-10-22 12:50:53 +01003640 " key = 'foobar';"
Andrei Popescu402d9372010-02-26 13:31:12 +00003641 " expected = undefined;"
3642 " }"
Ben Murdochf87a2032010-10-22 12:50:53 +01003643 " var v = obj[key];"
Andrei Popescu402d9372010-02-26 13:31:12 +00003644 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3645 " }"
3646 " 'PASSED'"
3647 "} catch(e) {"
3648 " e"
3649 "}";
3650 ExpectString(code, "PASSED");
3651}
3652
3653
3654THREADED_TEST(IndexedInterceptorGoingMegamorphic) {
3655 v8::HandleScope scope;
3656 Local<ObjectTemplate> templ = ObjectTemplate::New();
3657 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3658
3659 LocalContext context;
3660 Local<v8::Object> obj = templ->NewInstance();
3661 context->Global()->Set(v8_str("obj"), obj);
3662
3663 const char* code =
3664 "var original = obj;"
3665 "try {"
3666 " for (var i = 0; i < 100; i++) {"
3667 " var expected = i;"
3668 " if (i == 50) {"
3669 " obj = {50: 'foobar'};"
3670 " expected = 'foobar';"
3671 " }"
3672 " var v = obj[i];"
3673 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3674 " if (i == 50) obj = original;"
3675 " }"
3676 " 'PASSED'"
3677 "} catch(e) {"
3678 " e"
3679 "}";
3680 ExpectString(code, "PASSED");
3681}
3682
3683
3684THREADED_TEST(IndexedInterceptorReceiverTurningSmi) {
3685 v8::HandleScope scope;
3686 Local<ObjectTemplate> templ = ObjectTemplate::New();
3687 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3688
3689 LocalContext context;
3690 Local<v8::Object> obj = templ->NewInstance();
3691 context->Global()->Set(v8_str("obj"), obj);
3692
3693 const char* code =
3694 "var original = obj;"
3695 "try {"
3696 " for (var i = 0; i < 100; i++) {"
3697 " var expected = i;"
3698 " if (i == 5) {"
3699 " obj = 239;"
3700 " expected = undefined;"
3701 " }"
3702 " var v = obj[i];"
3703 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3704 " if (i == 5) obj = original;"
3705 " }"
3706 " 'PASSED'"
3707 "} catch(e) {"
3708 " e"
3709 "}";
3710 ExpectString(code, "PASSED");
3711}
3712
3713
3714THREADED_TEST(IndexedInterceptorOnProto) {
3715 v8::HandleScope scope;
3716 Local<ObjectTemplate> templ = ObjectTemplate::New();
3717 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3718
3719 LocalContext context;
3720 Local<v8::Object> obj = templ->NewInstance();
3721 context->Global()->Set(v8_str("obj"), obj);
3722
3723 const char* code =
3724 "var o = {__proto__: obj};"
3725 "try {"
3726 " for (var i = 0; i < 100; i++) {"
3727 " var v = o[i];"
3728 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;"
3729 " }"
3730 " 'PASSED'"
3731 "} catch(e) {"
3732 " e"
3733 "}";
3734 ExpectString(code, "PASSED");
3735}
3736
3737
Steve Blocka7e24c12009-10-30 11:49:00 +00003738THREADED_TEST(MultiContexts) {
3739 v8::HandleScope scope;
3740 v8::Handle<ObjectTemplate> templ = ObjectTemplate::New();
3741 templ->Set(v8_str("dummy"), v8::FunctionTemplate::New(DummyCallHandler));
3742
3743 Local<String> password = v8_str("Password");
3744
3745 // Create an environment
3746 LocalContext context0(0, templ);
3747 context0->SetSecurityToken(password);
3748 v8::Handle<v8::Object> global0 = context0->Global();
3749 global0->Set(v8_str("custom"), v8_num(1234));
3750 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value());
3751
3752 // Create an independent environment
3753 LocalContext context1(0, templ);
3754 context1->SetSecurityToken(password);
3755 v8::Handle<v8::Object> global1 = context1->Global();
3756 global1->Set(v8_str("custom"), v8_num(1234));
3757 CHECK_NE(global0, global1);
3758 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value());
3759 CHECK_EQ(1234, global1->Get(v8_str("custom"))->Int32Value());
3760
3761 // Now create a new context with the old global
3762 LocalContext context2(0, templ, global1);
3763 context2->SetSecurityToken(password);
3764 v8::Handle<v8::Object> global2 = context2->Global();
3765 CHECK_EQ(global1, global2);
3766 CHECK_EQ(0, global1->Get(v8_str("custom"))->Int32Value());
3767 CHECK_EQ(0, global2->Get(v8_str("custom"))->Int32Value());
3768}
3769
3770
3771THREADED_TEST(FunctionPrototypeAcrossContexts) {
3772 // Make sure that functions created by cloning boilerplates cannot
3773 // communicate through their __proto__ field.
3774
3775 v8::HandleScope scope;
3776
3777 LocalContext env0;
3778 v8::Handle<v8::Object> global0 =
3779 env0->Global();
3780 v8::Handle<v8::Object> object0 =
Steve Block6ded16b2010-05-10 14:33:55 +01003781 global0->Get(v8_str("Object")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003782 v8::Handle<v8::Object> tostring0 =
Steve Block6ded16b2010-05-10 14:33:55 +01003783 object0->Get(v8_str("toString")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003784 v8::Handle<v8::Object> proto0 =
Steve Block6ded16b2010-05-10 14:33:55 +01003785 tostring0->Get(v8_str("__proto__")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003786 proto0->Set(v8_str("custom"), v8_num(1234));
3787
3788 LocalContext env1;
3789 v8::Handle<v8::Object> global1 =
3790 env1->Global();
3791 v8::Handle<v8::Object> object1 =
Steve Block6ded16b2010-05-10 14:33:55 +01003792 global1->Get(v8_str("Object")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003793 v8::Handle<v8::Object> tostring1 =
Steve Block6ded16b2010-05-10 14:33:55 +01003794 object1->Get(v8_str("toString")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003795 v8::Handle<v8::Object> proto1 =
Steve Block6ded16b2010-05-10 14:33:55 +01003796 tostring1->Get(v8_str("__proto__")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003797 CHECK(!proto1->Has(v8_str("custom")));
3798}
3799
3800
3801THREADED_TEST(Regress892105) {
3802 // Make sure that object and array literals created by cloning
3803 // boilerplates cannot communicate through their __proto__
3804 // field. This is rather difficult to check, but we try to add stuff
3805 // to Object.prototype and Array.prototype and create a new
3806 // environment. This should succeed.
3807
3808 v8::HandleScope scope;
3809
3810 Local<String> source = v8_str("Object.prototype.obj = 1234;"
3811 "Array.prototype.arr = 4567;"
3812 "8901");
3813
3814 LocalContext env0;
3815 Local<Script> script0 = Script::Compile(source);
3816 CHECK_EQ(8901.0, script0->Run()->NumberValue());
3817
3818 LocalContext env1;
3819 Local<Script> script1 = Script::Compile(source);
3820 CHECK_EQ(8901.0, script1->Run()->NumberValue());
3821}
3822
3823
Steve Blocka7e24c12009-10-30 11:49:00 +00003824THREADED_TEST(UndetectableObject) {
3825 v8::HandleScope scope;
3826 LocalContext env;
3827
3828 Local<v8::FunctionTemplate> desc =
3829 v8::FunctionTemplate::New(0, v8::Handle<Value>());
3830 desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable
3831
3832 Local<v8::Object> obj = desc->GetFunction()->NewInstance();
3833 env->Global()->Set(v8_str("undetectable"), obj);
3834
3835 ExpectString("undetectable.toString()", "[object Object]");
3836 ExpectString("typeof undetectable", "undefined");
3837 ExpectString("typeof(undetectable)", "undefined");
3838 ExpectBoolean("typeof undetectable == 'undefined'", true);
3839 ExpectBoolean("typeof undetectable == 'object'", false);
3840 ExpectBoolean("if (undetectable) { true; } else { false; }", false);
3841 ExpectBoolean("!undetectable", true);
3842
3843 ExpectObject("true&&undetectable", obj);
3844 ExpectBoolean("false&&undetectable", false);
3845 ExpectBoolean("true||undetectable", true);
3846 ExpectObject("false||undetectable", obj);
3847
3848 ExpectObject("undetectable&&true", obj);
3849 ExpectObject("undetectable&&false", obj);
3850 ExpectBoolean("undetectable||true", true);
3851 ExpectBoolean("undetectable||false", false);
3852
3853 ExpectBoolean("undetectable==null", true);
3854 ExpectBoolean("null==undetectable", true);
3855 ExpectBoolean("undetectable==undefined", true);
3856 ExpectBoolean("undefined==undetectable", true);
3857 ExpectBoolean("undetectable==undetectable", true);
3858
3859
3860 ExpectBoolean("undetectable===null", false);
3861 ExpectBoolean("null===undetectable", false);
3862 ExpectBoolean("undetectable===undefined", false);
3863 ExpectBoolean("undefined===undetectable", false);
3864 ExpectBoolean("undetectable===undetectable", true);
3865}
3866
3867
Steve Block8defd9f2010-07-08 12:39:36 +01003868
3869THREADED_TEST(ExtensibleOnUndetectable) {
3870 v8::HandleScope scope;
3871 LocalContext env;
3872
3873 Local<v8::FunctionTemplate> desc =
3874 v8::FunctionTemplate::New(0, v8::Handle<Value>());
3875 desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable
3876
3877 Local<v8::Object> obj = desc->GetFunction()->NewInstance();
3878 env->Global()->Set(v8_str("undetectable"), obj);
3879
3880 Local<String> source = v8_str("undetectable.x = 42;"
3881 "undetectable.x");
3882
3883 Local<Script> script = Script::Compile(source);
3884
3885 CHECK_EQ(v8::Integer::New(42), script->Run());
3886
3887 ExpectBoolean("Object.isExtensible(undetectable)", true);
3888
3889 source = v8_str("Object.preventExtensions(undetectable);");
3890 script = Script::Compile(source);
3891 script->Run();
3892 ExpectBoolean("Object.isExtensible(undetectable)", false);
3893
3894 source = v8_str("undetectable.y = 2000;");
3895 script = Script::Compile(source);
Steve Block8defd9f2010-07-08 12:39:36 +01003896 Local<Value> result = script->Run();
Steve Block44f0eee2011-05-26 01:26:41 +01003897 ExpectBoolean("undetectable.y == undefined", true);
Steve Block8defd9f2010-07-08 12:39:36 +01003898}
3899
3900
3901
Steve Blocka7e24c12009-10-30 11:49:00 +00003902THREADED_TEST(UndetectableString) {
3903 v8::HandleScope scope;
3904 LocalContext env;
3905
3906 Local<String> obj = String::NewUndetectable("foo");
3907 env->Global()->Set(v8_str("undetectable"), obj);
3908
3909 ExpectString("undetectable", "foo");
3910 ExpectString("typeof undetectable", "undefined");
3911 ExpectString("typeof(undetectable)", "undefined");
3912 ExpectBoolean("typeof undetectable == 'undefined'", true);
3913 ExpectBoolean("typeof undetectable == 'string'", false);
3914 ExpectBoolean("if (undetectable) { true; } else { false; }", false);
3915 ExpectBoolean("!undetectable", true);
3916
3917 ExpectObject("true&&undetectable", obj);
3918 ExpectBoolean("false&&undetectable", false);
3919 ExpectBoolean("true||undetectable", true);
3920 ExpectObject("false||undetectable", obj);
3921
3922 ExpectObject("undetectable&&true", obj);
3923 ExpectObject("undetectable&&false", obj);
3924 ExpectBoolean("undetectable||true", true);
3925 ExpectBoolean("undetectable||false", false);
3926
3927 ExpectBoolean("undetectable==null", true);
3928 ExpectBoolean("null==undetectable", true);
3929 ExpectBoolean("undetectable==undefined", true);
3930 ExpectBoolean("undefined==undetectable", true);
3931 ExpectBoolean("undetectable==undetectable", true);
3932
3933
3934 ExpectBoolean("undetectable===null", false);
3935 ExpectBoolean("null===undetectable", false);
3936 ExpectBoolean("undetectable===undefined", false);
3937 ExpectBoolean("undefined===undetectable", false);
3938 ExpectBoolean("undetectable===undetectable", true);
3939}
3940
3941
3942template <typename T> static void USE(T) { }
3943
3944
3945// This test is not intended to be run, just type checked.
3946static void PersistentHandles() {
3947 USE(PersistentHandles);
3948 Local<String> str = v8_str("foo");
3949 v8::Persistent<String> p_str = v8::Persistent<String>::New(str);
3950 USE(p_str);
3951 Local<Script> scr = Script::Compile(v8_str(""));
3952 v8::Persistent<Script> p_scr = v8::Persistent<Script>::New(scr);
3953 USE(p_scr);
3954 Local<ObjectTemplate> templ = ObjectTemplate::New();
3955 v8::Persistent<ObjectTemplate> p_templ =
3956 v8::Persistent<ObjectTemplate>::New(templ);
3957 USE(p_templ);
3958}
3959
3960
3961static v8::Handle<Value> HandleLogDelegator(const v8::Arguments& args) {
3962 ApiTestFuzzer::Fuzz();
3963 return v8::Undefined();
3964}
3965
3966
3967THREADED_TEST(GlobalObjectTemplate) {
3968 v8::HandleScope handle_scope;
3969 Local<ObjectTemplate> global_template = ObjectTemplate::New();
3970 global_template->Set(v8_str("JSNI_Log"),
3971 v8::FunctionTemplate::New(HandleLogDelegator));
3972 v8::Persistent<Context> context = Context::New(0, global_template);
3973 Context::Scope context_scope(context);
3974 Script::Compile(v8_str("JSNI_Log('LOG')"))->Run();
3975 context.Dispose();
3976}
3977
3978
3979static const char* kSimpleExtensionSource =
3980 "function Foo() {"
3981 " return 4;"
3982 "}";
3983
3984
3985THREADED_TEST(SimpleExtensions) {
3986 v8::HandleScope handle_scope;
3987 v8::RegisterExtension(new Extension("simpletest", kSimpleExtensionSource));
3988 const char* extension_names[] = { "simpletest" };
3989 v8::ExtensionConfiguration extensions(1, extension_names);
3990 v8::Handle<Context> context = Context::New(&extensions);
3991 Context::Scope lock(context);
3992 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run();
3993 CHECK_EQ(result, v8::Integer::New(4));
3994}
3995
3996
3997static const char* kEvalExtensionSource1 =
3998 "function UseEval1() {"
3999 " var x = 42;"
4000 " return eval('x');"
4001 "}";
4002
4003
4004static const char* kEvalExtensionSource2 =
4005 "(function() {"
4006 " var x = 42;"
4007 " function e() {"
4008 " return eval('x');"
4009 " }"
4010 " this.UseEval2 = e;"
4011 "})()";
4012
4013
4014THREADED_TEST(UseEvalFromExtension) {
4015 v8::HandleScope handle_scope;
4016 v8::RegisterExtension(new Extension("evaltest1", kEvalExtensionSource1));
4017 v8::RegisterExtension(new Extension("evaltest2", kEvalExtensionSource2));
4018 const char* extension_names[] = { "evaltest1", "evaltest2" };
4019 v8::ExtensionConfiguration extensions(2, extension_names);
4020 v8::Handle<Context> context = Context::New(&extensions);
4021 Context::Scope lock(context);
4022 v8::Handle<Value> result = Script::Compile(v8_str("UseEval1()"))->Run();
4023 CHECK_EQ(result, v8::Integer::New(42));
4024 result = Script::Compile(v8_str("UseEval2()"))->Run();
4025 CHECK_EQ(result, v8::Integer::New(42));
4026}
4027
4028
4029static const char* kWithExtensionSource1 =
4030 "function UseWith1() {"
4031 " var x = 42;"
4032 " with({x:87}) { return x; }"
4033 "}";
4034
4035
4036
4037static const char* kWithExtensionSource2 =
4038 "(function() {"
4039 " var x = 42;"
4040 " function e() {"
4041 " with ({x:87}) { return x; }"
4042 " }"
4043 " this.UseWith2 = e;"
4044 "})()";
4045
4046
4047THREADED_TEST(UseWithFromExtension) {
4048 v8::HandleScope handle_scope;
4049 v8::RegisterExtension(new Extension("withtest1", kWithExtensionSource1));
4050 v8::RegisterExtension(new Extension("withtest2", kWithExtensionSource2));
4051 const char* extension_names[] = { "withtest1", "withtest2" };
4052 v8::ExtensionConfiguration extensions(2, extension_names);
4053 v8::Handle<Context> context = Context::New(&extensions);
4054 Context::Scope lock(context);
4055 v8::Handle<Value> result = Script::Compile(v8_str("UseWith1()"))->Run();
4056 CHECK_EQ(result, v8::Integer::New(87));
4057 result = Script::Compile(v8_str("UseWith2()"))->Run();
4058 CHECK_EQ(result, v8::Integer::New(87));
4059}
4060
4061
4062THREADED_TEST(AutoExtensions) {
4063 v8::HandleScope handle_scope;
4064 Extension* extension = new Extension("autotest", kSimpleExtensionSource);
4065 extension->set_auto_enable(true);
4066 v8::RegisterExtension(extension);
4067 v8::Handle<Context> context = Context::New();
4068 Context::Scope lock(context);
4069 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run();
4070 CHECK_EQ(result, v8::Integer::New(4));
4071}
4072
4073
Steve Blockd0582a62009-12-15 09:54:21 +00004074static const char* kSyntaxErrorInExtensionSource =
4075 "[";
4076
4077
4078// Test that a syntax error in an extension does not cause a fatal
4079// error but results in an empty context.
4080THREADED_TEST(SyntaxErrorExtensions) {
4081 v8::HandleScope handle_scope;
4082 v8::RegisterExtension(new Extension("syntaxerror",
4083 kSyntaxErrorInExtensionSource));
4084 const char* extension_names[] = { "syntaxerror" };
4085 v8::ExtensionConfiguration extensions(1, extension_names);
4086 v8::Handle<Context> context = Context::New(&extensions);
4087 CHECK(context.IsEmpty());
4088}
4089
4090
4091static const char* kExceptionInExtensionSource =
4092 "throw 42";
4093
4094
4095// Test that an exception when installing an extension does not cause
4096// a fatal error but results in an empty context.
4097THREADED_TEST(ExceptionExtensions) {
4098 v8::HandleScope handle_scope;
4099 v8::RegisterExtension(new Extension("exception",
4100 kExceptionInExtensionSource));
4101 const char* extension_names[] = { "exception" };
4102 v8::ExtensionConfiguration extensions(1, extension_names);
4103 v8::Handle<Context> context = Context::New(&extensions);
4104 CHECK(context.IsEmpty());
4105}
4106
4107
Iain Merrick9ac36c92010-09-13 15:29:50 +01004108static const char* kNativeCallInExtensionSource =
4109 "function call_runtime_last_index_of(x) {"
4110 " return %StringLastIndexOf(x, 'bob', 10);"
4111 "}";
4112
4113
4114static const char* kNativeCallTest =
4115 "call_runtime_last_index_of('bobbobboellebobboellebobbob');";
4116
4117// Test that a native runtime calls are supported in extensions.
4118THREADED_TEST(NativeCallInExtensions) {
4119 v8::HandleScope handle_scope;
4120 v8::RegisterExtension(new Extension("nativecall",
4121 kNativeCallInExtensionSource));
4122 const char* extension_names[] = { "nativecall" };
4123 v8::ExtensionConfiguration extensions(1, extension_names);
4124 v8::Handle<Context> context = Context::New(&extensions);
4125 Context::Scope lock(context);
4126 v8::Handle<Value> result = Script::Compile(v8_str(kNativeCallTest))->Run();
4127 CHECK_EQ(result, v8::Integer::New(3));
4128}
4129
4130
Steve Blocka7e24c12009-10-30 11:49:00 +00004131static void CheckDependencies(const char* name, const char* expected) {
4132 v8::HandleScope handle_scope;
4133 v8::ExtensionConfiguration config(1, &name);
4134 LocalContext context(&config);
4135 CHECK_EQ(String::New(expected), context->Global()->Get(v8_str("loaded")));
4136}
4137
4138
4139/*
4140 * Configuration:
4141 *
4142 * /-- B <--\
4143 * A <- -- D <-- E
4144 * \-- C <--/
4145 */
4146THREADED_TEST(ExtensionDependency) {
4147 static const char* kEDeps[] = { "D" };
4148 v8::RegisterExtension(new Extension("E", "this.loaded += 'E';", 1, kEDeps));
4149 static const char* kDDeps[] = { "B", "C" };
4150 v8::RegisterExtension(new Extension("D", "this.loaded += 'D';", 2, kDDeps));
4151 static const char* kBCDeps[] = { "A" };
4152 v8::RegisterExtension(new Extension("B", "this.loaded += 'B';", 1, kBCDeps));
4153 v8::RegisterExtension(new Extension("C", "this.loaded += 'C';", 1, kBCDeps));
4154 v8::RegisterExtension(new Extension("A", "this.loaded += 'A';"));
4155 CheckDependencies("A", "undefinedA");
4156 CheckDependencies("B", "undefinedAB");
4157 CheckDependencies("C", "undefinedAC");
4158 CheckDependencies("D", "undefinedABCD");
4159 CheckDependencies("E", "undefinedABCDE");
4160 v8::HandleScope handle_scope;
4161 static const char* exts[2] = { "C", "E" };
4162 v8::ExtensionConfiguration config(2, exts);
4163 LocalContext context(&config);
4164 CHECK_EQ(v8_str("undefinedACBDE"), context->Global()->Get(v8_str("loaded")));
4165}
4166
4167
4168static const char* kExtensionTestScript =
4169 "native function A();"
4170 "native function B();"
4171 "native function C();"
4172 "function Foo(i) {"
4173 " if (i == 0) return A();"
4174 " if (i == 1) return B();"
4175 " if (i == 2) return C();"
4176 "}";
4177
4178
4179static v8::Handle<Value> CallFun(const v8::Arguments& args) {
4180 ApiTestFuzzer::Fuzz();
Leon Clarkee46be812010-01-19 14:06:41 +00004181 if (args.IsConstructCall()) {
4182 args.This()->Set(v8_str("data"), args.Data());
4183 return v8::Null();
4184 }
Steve Blocka7e24c12009-10-30 11:49:00 +00004185 return args.Data();
4186}
4187
4188
4189class FunctionExtension : public Extension {
4190 public:
4191 FunctionExtension() : Extension("functiontest", kExtensionTestScript) { }
4192 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(
4193 v8::Handle<String> name);
4194};
4195
4196
4197static int lookup_count = 0;
4198v8::Handle<v8::FunctionTemplate> FunctionExtension::GetNativeFunction(
4199 v8::Handle<String> name) {
4200 lookup_count++;
4201 if (name->Equals(v8_str("A"))) {
4202 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(8));
4203 } else if (name->Equals(v8_str("B"))) {
4204 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(7));
4205 } else if (name->Equals(v8_str("C"))) {
4206 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(6));
4207 } else {
4208 return v8::Handle<v8::FunctionTemplate>();
4209 }
4210}
4211
4212
4213THREADED_TEST(FunctionLookup) {
4214 v8::RegisterExtension(new FunctionExtension());
4215 v8::HandleScope handle_scope;
4216 static const char* exts[1] = { "functiontest" };
4217 v8::ExtensionConfiguration config(1, exts);
4218 LocalContext context(&config);
4219 CHECK_EQ(3, lookup_count);
4220 CHECK_EQ(v8::Integer::New(8), Script::Compile(v8_str("Foo(0)"))->Run());
4221 CHECK_EQ(v8::Integer::New(7), Script::Compile(v8_str("Foo(1)"))->Run());
4222 CHECK_EQ(v8::Integer::New(6), Script::Compile(v8_str("Foo(2)"))->Run());
4223}
4224
4225
Leon Clarkee46be812010-01-19 14:06:41 +00004226THREADED_TEST(NativeFunctionConstructCall) {
4227 v8::RegisterExtension(new FunctionExtension());
4228 v8::HandleScope handle_scope;
4229 static const char* exts[1] = { "functiontest" };
4230 v8::ExtensionConfiguration config(1, exts);
4231 LocalContext context(&config);
Leon Clarked91b9f72010-01-27 17:25:45 +00004232 for (int i = 0; i < 10; i++) {
4233 // Run a few times to ensure that allocation of objects doesn't
4234 // change behavior of a constructor function.
4235 CHECK_EQ(v8::Integer::New(8),
4236 Script::Compile(v8_str("(new A()).data"))->Run());
4237 CHECK_EQ(v8::Integer::New(7),
4238 Script::Compile(v8_str("(new B()).data"))->Run());
4239 CHECK_EQ(v8::Integer::New(6),
4240 Script::Compile(v8_str("(new C()).data"))->Run());
4241 }
Leon Clarkee46be812010-01-19 14:06:41 +00004242}
4243
4244
Steve Blocka7e24c12009-10-30 11:49:00 +00004245static const char* last_location;
4246static const char* last_message;
4247void StoringErrorCallback(const char* location, const char* message) {
4248 if (last_location == NULL) {
4249 last_location = location;
4250 last_message = message;
4251 }
4252}
4253
4254
4255// ErrorReporting creates a circular extensions configuration and
4256// tests that the fatal error handler gets called. This renders V8
4257// unusable and therefore this test cannot be run in parallel.
4258TEST(ErrorReporting) {
4259 v8::V8::SetFatalErrorHandler(StoringErrorCallback);
4260 static const char* aDeps[] = { "B" };
4261 v8::RegisterExtension(new Extension("A", "", 1, aDeps));
4262 static const char* bDeps[] = { "A" };
4263 v8::RegisterExtension(new Extension("B", "", 1, bDeps));
4264 last_location = NULL;
4265 v8::ExtensionConfiguration config(1, bDeps);
4266 v8::Handle<Context> context = Context::New(&config);
4267 CHECK(context.IsEmpty());
4268 CHECK_NE(last_location, NULL);
4269}
4270
4271
4272static const char* js_code_causing_huge_string_flattening =
4273 "var str = 'X';"
4274 "for (var i = 0; i < 30; i++) {"
4275 " str = str + str;"
4276 "}"
4277 "str.match(/X/);";
4278
4279
4280void OOMCallback(const char* location, const char* message) {
4281 exit(0);
4282}
4283
4284
4285TEST(RegexpOutOfMemory) {
4286 // Execute a script that causes out of memory when flattening a string.
4287 v8::HandleScope scope;
4288 v8::V8::SetFatalErrorHandler(OOMCallback);
4289 LocalContext context;
4290 Local<Script> script =
4291 Script::Compile(String::New(js_code_causing_huge_string_flattening));
4292 last_location = NULL;
4293 Local<Value> result = script->Run();
4294
4295 CHECK(false); // Should not return.
4296}
4297
4298
4299static void MissingScriptInfoMessageListener(v8::Handle<v8::Message> message,
4300 v8::Handle<Value> data) {
4301 CHECK_EQ(v8::Undefined(), data);
4302 CHECK(message->GetScriptResourceName()->IsUndefined());
4303 CHECK_EQ(v8::Undefined(), message->GetScriptResourceName());
4304 message->GetLineNumber();
4305 message->GetSourceLine();
4306}
4307
4308
4309THREADED_TEST(ErrorWithMissingScriptInfo) {
4310 v8::HandleScope scope;
4311 LocalContext context;
4312 v8::V8::AddMessageListener(MissingScriptInfoMessageListener);
4313 Script::Compile(v8_str("throw Error()"))->Run();
4314 v8::V8::RemoveMessageListeners(MissingScriptInfoMessageListener);
4315}
4316
4317
4318int global_index = 0;
4319
4320class Snorkel {
4321 public:
4322 Snorkel() { index_ = global_index++; }
4323 int index_;
4324};
4325
4326class Whammy {
4327 public:
4328 Whammy() {
4329 cursor_ = 0;
4330 }
4331 ~Whammy() {
4332 script_.Dispose();
4333 }
4334 v8::Handle<Script> getScript() {
4335 if (script_.IsEmpty())
4336 script_ = v8::Persistent<Script>::New(v8_compile("({}).blammo"));
4337 return Local<Script>(*script_);
4338 }
4339
4340 public:
4341 static const int kObjectCount = 256;
4342 int cursor_;
4343 v8::Persistent<v8::Object> objects_[kObjectCount];
4344 v8::Persistent<Script> script_;
4345};
4346
4347static void HandleWeakReference(v8::Persistent<v8::Value> obj, void* data) {
4348 Snorkel* snorkel = reinterpret_cast<Snorkel*>(data);
4349 delete snorkel;
4350 obj.ClearWeak();
4351}
4352
4353v8::Handle<Value> WhammyPropertyGetter(Local<String> name,
4354 const AccessorInfo& info) {
4355 Whammy* whammy =
4356 static_cast<Whammy*>(v8::Handle<v8::External>::Cast(info.Data())->Value());
4357
4358 v8::Persistent<v8::Object> prev = whammy->objects_[whammy->cursor_];
4359
4360 v8::Handle<v8::Object> obj = v8::Object::New();
4361 v8::Persistent<v8::Object> global = v8::Persistent<v8::Object>::New(obj);
4362 if (!prev.IsEmpty()) {
4363 prev->Set(v8_str("next"), obj);
4364 prev.MakeWeak(new Snorkel(), &HandleWeakReference);
4365 whammy->objects_[whammy->cursor_].Clear();
4366 }
4367 whammy->objects_[whammy->cursor_] = global;
4368 whammy->cursor_ = (whammy->cursor_ + 1) % Whammy::kObjectCount;
4369 return whammy->getScript()->Run();
4370}
4371
4372THREADED_TEST(WeakReference) {
4373 v8::HandleScope handle_scope;
4374 v8::Handle<v8::ObjectTemplate> templ= v8::ObjectTemplate::New();
Ben Murdoch3bec4d22010-07-22 14:51:16 +01004375 Whammy* whammy = new Whammy();
Steve Blocka7e24c12009-10-30 11:49:00 +00004376 templ->SetNamedPropertyHandler(WhammyPropertyGetter,
4377 0, 0, 0, 0,
Ben Murdoch3bec4d22010-07-22 14:51:16 +01004378 v8::External::New(whammy));
Steve Blocka7e24c12009-10-30 11:49:00 +00004379 const char* extension_list[] = { "v8/gc" };
4380 v8::ExtensionConfiguration extensions(1, extension_list);
4381 v8::Persistent<Context> context = Context::New(&extensions);
4382 Context::Scope context_scope(context);
4383
4384 v8::Handle<v8::Object> interceptor = templ->NewInstance();
4385 context->Global()->Set(v8_str("whammy"), interceptor);
4386 const char* code =
4387 "var last;"
4388 "for (var i = 0; i < 10000; i++) {"
4389 " var obj = whammy.length;"
4390 " if (last) last.next = obj;"
4391 " last = obj;"
4392 "}"
4393 "gc();"
4394 "4";
4395 v8::Handle<Value> result = CompileRun(code);
4396 CHECK_EQ(4.0, result->NumberValue());
Ben Murdoch3bec4d22010-07-22 14:51:16 +01004397 delete whammy;
Steve Blocka7e24c12009-10-30 11:49:00 +00004398 context.Dispose();
4399}
4400
4401
Steve Blockd0582a62009-12-15 09:54:21 +00004402static bool in_scavenge = false;
4403static int last = -1;
4404
4405static void ForceScavenge(v8::Persistent<v8::Value> obj, void* data) {
4406 CHECK_EQ(-1, last);
4407 last = 0;
4408 obj.Dispose();
4409 obj.Clear();
4410 in_scavenge = true;
Steve Block44f0eee2011-05-26 01:26:41 +01004411 HEAP->PerformScavenge();
Steve Blockd0582a62009-12-15 09:54:21 +00004412 in_scavenge = false;
4413 *(reinterpret_cast<bool*>(data)) = true;
4414}
4415
4416static void CheckIsNotInvokedInScavenge(v8::Persistent<v8::Value> obj,
4417 void* data) {
4418 CHECK_EQ(0, last);
4419 last = 1;
4420 *(reinterpret_cast<bool*>(data)) = in_scavenge;
4421 obj.Dispose();
4422 obj.Clear();
4423}
4424
4425THREADED_TEST(NoWeakRefCallbacksInScavenge) {
4426 // Test verifies that scavenge cannot invoke WeakReferenceCallbacks.
4427 // Calling callbacks from scavenges is unsafe as objects held by those
4428 // handlers might have become strongly reachable, but scavenge doesn't
4429 // check that.
4430 v8::Persistent<Context> context = Context::New();
4431 Context::Scope context_scope(context);
4432
4433 v8::Persistent<v8::Object> object_a;
4434 v8::Persistent<v8::Object> object_b;
4435
4436 {
4437 v8::HandleScope handle_scope;
4438 object_b = v8::Persistent<v8::Object>::New(v8::Object::New());
4439 object_a = v8::Persistent<v8::Object>::New(v8::Object::New());
4440 }
4441
4442 bool object_a_disposed = false;
4443 object_a.MakeWeak(&object_a_disposed, &ForceScavenge);
4444 bool released_in_scavenge = false;
4445 object_b.MakeWeak(&released_in_scavenge, &CheckIsNotInvokedInScavenge);
4446
4447 while (!object_a_disposed) {
Steve Block44f0eee2011-05-26 01:26:41 +01004448 HEAP->CollectAllGarbage(false);
Steve Blockd0582a62009-12-15 09:54:21 +00004449 }
4450 CHECK(!released_in_scavenge);
4451}
4452
4453
Steve Blocka7e24c12009-10-30 11:49:00 +00004454v8::Handle<Function> args_fun;
4455
4456
4457static v8::Handle<Value> ArgumentsTestCallback(const v8::Arguments& args) {
4458 ApiTestFuzzer::Fuzz();
4459 CHECK_EQ(args_fun, args.Callee());
4460 CHECK_EQ(3, args.Length());
4461 CHECK_EQ(v8::Integer::New(1), args[0]);
4462 CHECK_EQ(v8::Integer::New(2), args[1]);
4463 CHECK_EQ(v8::Integer::New(3), args[2]);
4464 CHECK_EQ(v8::Undefined(), args[3]);
4465 v8::HandleScope scope;
Steve Block44f0eee2011-05-26 01:26:41 +01004466 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +00004467 return v8::Undefined();
4468}
4469
4470
4471THREADED_TEST(Arguments) {
4472 v8::HandleScope scope;
4473 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New();
4474 global->Set(v8_str("f"), v8::FunctionTemplate::New(ArgumentsTestCallback));
4475 LocalContext context(NULL, global);
Steve Block6ded16b2010-05-10 14:33:55 +01004476 args_fun = context->Global()->Get(v8_str("f")).As<Function>();
Steve Blocka7e24c12009-10-30 11:49:00 +00004477 v8_compile("f(1, 2, 3)")->Run();
4478}
4479
4480
Steve Blocka7e24c12009-10-30 11:49:00 +00004481static v8::Handle<Value> NoBlockGetterX(Local<String> name,
4482 const AccessorInfo&) {
4483 return v8::Handle<Value>();
4484}
4485
4486
4487static v8::Handle<Value> NoBlockGetterI(uint32_t index,
4488 const AccessorInfo&) {
4489 return v8::Handle<Value>();
4490}
4491
4492
4493static v8::Handle<v8::Boolean> PDeleter(Local<String> name,
4494 const AccessorInfo&) {
4495 if (!name->Equals(v8_str("foo"))) {
4496 return v8::Handle<v8::Boolean>(); // not intercepted
4497 }
4498
4499 return v8::False(); // intercepted, and don't delete the property
4500}
4501
4502
4503static v8::Handle<v8::Boolean> IDeleter(uint32_t index, const AccessorInfo&) {
4504 if (index != 2) {
4505 return v8::Handle<v8::Boolean>(); // not intercepted
4506 }
4507
4508 return v8::False(); // intercepted, and don't delete the property
4509}
4510
4511
4512THREADED_TEST(Deleter) {
4513 v8::HandleScope scope;
4514 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4515 obj->SetNamedPropertyHandler(NoBlockGetterX, NULL, NULL, PDeleter, NULL);
4516 obj->SetIndexedPropertyHandler(NoBlockGetterI, NULL, NULL, IDeleter, NULL);
4517 LocalContext context;
4518 context->Global()->Set(v8_str("k"), obj->NewInstance());
4519 CompileRun(
4520 "k.foo = 'foo';"
4521 "k.bar = 'bar';"
4522 "k[2] = 2;"
4523 "k[4] = 4;");
4524 CHECK(v8_compile("delete k.foo")->Run()->IsFalse());
4525 CHECK(v8_compile("delete k.bar")->Run()->IsTrue());
4526
4527 CHECK_EQ(v8_compile("k.foo")->Run(), v8_str("foo"));
4528 CHECK(v8_compile("k.bar")->Run()->IsUndefined());
4529
4530 CHECK(v8_compile("delete k[2]")->Run()->IsFalse());
4531 CHECK(v8_compile("delete k[4]")->Run()->IsTrue());
4532
4533 CHECK_EQ(v8_compile("k[2]")->Run(), v8_num(2));
4534 CHECK(v8_compile("k[4]")->Run()->IsUndefined());
4535}
4536
4537
4538static v8::Handle<Value> GetK(Local<String> name, const AccessorInfo&) {
4539 ApiTestFuzzer::Fuzz();
4540 if (name->Equals(v8_str("foo")) ||
4541 name->Equals(v8_str("bar")) ||
4542 name->Equals(v8_str("baz"))) {
4543 return v8::Undefined();
4544 }
4545 return v8::Handle<Value>();
4546}
4547
4548
4549static v8::Handle<Value> IndexedGetK(uint32_t index, const AccessorInfo&) {
4550 ApiTestFuzzer::Fuzz();
4551 if (index == 0 || index == 1) return v8::Undefined();
4552 return v8::Handle<Value>();
4553}
4554
4555
4556static v8::Handle<v8::Array> NamedEnum(const AccessorInfo&) {
4557 ApiTestFuzzer::Fuzz();
4558 v8::Handle<v8::Array> result = v8::Array::New(3);
4559 result->Set(v8::Integer::New(0), v8_str("foo"));
4560 result->Set(v8::Integer::New(1), v8_str("bar"));
4561 result->Set(v8::Integer::New(2), v8_str("baz"));
4562 return result;
4563}
4564
4565
4566static v8::Handle<v8::Array> IndexedEnum(const AccessorInfo&) {
4567 ApiTestFuzzer::Fuzz();
4568 v8::Handle<v8::Array> result = v8::Array::New(2);
4569 result->Set(v8::Integer::New(0), v8_str("0"));
4570 result->Set(v8::Integer::New(1), v8_str("1"));
4571 return result;
4572}
4573
4574
4575THREADED_TEST(Enumerators) {
4576 v8::HandleScope scope;
4577 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4578 obj->SetNamedPropertyHandler(GetK, NULL, NULL, NULL, NamedEnum);
4579 obj->SetIndexedPropertyHandler(IndexedGetK, NULL, NULL, NULL, IndexedEnum);
4580 LocalContext context;
4581 context->Global()->Set(v8_str("k"), obj->NewInstance());
4582 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun(
4583 "k[10] = 0;"
4584 "k.a = 0;"
4585 "k[5] = 0;"
4586 "k.b = 0;"
4587 "k[4294967295] = 0;"
4588 "k.c = 0;"
4589 "k[4294967296] = 0;"
4590 "k.d = 0;"
4591 "k[140000] = 0;"
4592 "k.e = 0;"
4593 "k[30000000000] = 0;"
4594 "k.f = 0;"
4595 "var result = [];"
4596 "for (var prop in k) {"
4597 " result.push(prop);"
4598 "}"
4599 "result"));
4600 // Check that we get all the property names returned including the
4601 // ones from the enumerators in the right order: indexed properties
4602 // in numerical order, indexed interceptor properties, named
4603 // properties in insertion order, named interceptor properties.
4604 // This order is not mandated by the spec, so this test is just
4605 // documenting our behavior.
4606 CHECK_EQ(17, result->Length());
4607 // Indexed properties in numerical order.
4608 CHECK_EQ(v8_str("5"), result->Get(v8::Integer::New(0)));
4609 CHECK_EQ(v8_str("10"), result->Get(v8::Integer::New(1)));
4610 CHECK_EQ(v8_str("140000"), result->Get(v8::Integer::New(2)));
4611 CHECK_EQ(v8_str("4294967295"), result->Get(v8::Integer::New(3)));
4612 // Indexed interceptor properties in the order they are returned
4613 // from the enumerator interceptor.
4614 CHECK_EQ(v8_str("0"), result->Get(v8::Integer::New(4)));
4615 CHECK_EQ(v8_str("1"), result->Get(v8::Integer::New(5)));
4616 // Named properties in insertion order.
4617 CHECK_EQ(v8_str("a"), result->Get(v8::Integer::New(6)));
4618 CHECK_EQ(v8_str("b"), result->Get(v8::Integer::New(7)));
4619 CHECK_EQ(v8_str("c"), result->Get(v8::Integer::New(8)));
4620 CHECK_EQ(v8_str("4294967296"), result->Get(v8::Integer::New(9)));
4621 CHECK_EQ(v8_str("d"), result->Get(v8::Integer::New(10)));
4622 CHECK_EQ(v8_str("e"), result->Get(v8::Integer::New(11)));
4623 CHECK_EQ(v8_str("30000000000"), result->Get(v8::Integer::New(12)));
4624 CHECK_EQ(v8_str("f"), result->Get(v8::Integer::New(13)));
4625 // Named interceptor properties.
4626 CHECK_EQ(v8_str("foo"), result->Get(v8::Integer::New(14)));
4627 CHECK_EQ(v8_str("bar"), result->Get(v8::Integer::New(15)));
4628 CHECK_EQ(v8_str("baz"), result->Get(v8::Integer::New(16)));
4629}
4630
4631
4632int p_getter_count;
4633int p_getter_count2;
4634
4635
4636static v8::Handle<Value> PGetter(Local<String> name, const AccessorInfo& info) {
4637 ApiTestFuzzer::Fuzz();
4638 p_getter_count++;
4639 v8::Handle<v8::Object> global = Context::GetCurrent()->Global();
4640 CHECK_EQ(info.Holder(), global->Get(v8_str("o1")));
4641 if (name->Equals(v8_str("p1"))) {
4642 CHECK_EQ(info.This(), global->Get(v8_str("o1")));
4643 } else if (name->Equals(v8_str("p2"))) {
4644 CHECK_EQ(info.This(), global->Get(v8_str("o2")));
4645 } else if (name->Equals(v8_str("p3"))) {
4646 CHECK_EQ(info.This(), global->Get(v8_str("o3")));
4647 } else if (name->Equals(v8_str("p4"))) {
4648 CHECK_EQ(info.This(), global->Get(v8_str("o4")));
4649 }
4650 return v8::Undefined();
4651}
4652
4653
4654static void RunHolderTest(v8::Handle<v8::ObjectTemplate> obj) {
4655 ApiTestFuzzer::Fuzz();
4656 LocalContext context;
4657 context->Global()->Set(v8_str("o1"), obj->NewInstance());
4658 CompileRun(
4659 "o1.__proto__ = { };"
4660 "var o2 = { __proto__: o1 };"
4661 "var o3 = { __proto__: o2 };"
4662 "var o4 = { __proto__: o3 };"
4663 "for (var i = 0; i < 10; i++) o4.p4;"
4664 "for (var i = 0; i < 10; i++) o3.p3;"
4665 "for (var i = 0; i < 10; i++) o2.p2;"
4666 "for (var i = 0; i < 10; i++) o1.p1;");
4667}
4668
4669
4670static v8::Handle<Value> PGetter2(Local<String> name,
4671 const AccessorInfo& info) {
4672 ApiTestFuzzer::Fuzz();
4673 p_getter_count2++;
4674 v8::Handle<v8::Object> global = Context::GetCurrent()->Global();
4675 CHECK_EQ(info.Holder(), global->Get(v8_str("o1")));
4676 if (name->Equals(v8_str("p1"))) {
4677 CHECK_EQ(info.This(), global->Get(v8_str("o1")));
4678 } else if (name->Equals(v8_str("p2"))) {
4679 CHECK_EQ(info.This(), global->Get(v8_str("o2")));
4680 } else if (name->Equals(v8_str("p3"))) {
4681 CHECK_EQ(info.This(), global->Get(v8_str("o3")));
4682 } else if (name->Equals(v8_str("p4"))) {
4683 CHECK_EQ(info.This(), global->Get(v8_str("o4")));
4684 }
4685 return v8::Undefined();
4686}
4687
4688
4689THREADED_TEST(GetterHolders) {
4690 v8::HandleScope scope;
4691 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4692 obj->SetAccessor(v8_str("p1"), PGetter);
4693 obj->SetAccessor(v8_str("p2"), PGetter);
4694 obj->SetAccessor(v8_str("p3"), PGetter);
4695 obj->SetAccessor(v8_str("p4"), PGetter);
4696 p_getter_count = 0;
4697 RunHolderTest(obj);
4698 CHECK_EQ(40, p_getter_count);
4699}
4700
4701
4702THREADED_TEST(PreInterceptorHolders) {
4703 v8::HandleScope scope;
4704 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4705 obj->SetNamedPropertyHandler(PGetter2);
4706 p_getter_count2 = 0;
4707 RunHolderTest(obj);
4708 CHECK_EQ(40, p_getter_count2);
4709}
4710
4711
4712THREADED_TEST(ObjectInstantiation) {
4713 v8::HandleScope scope;
4714 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
4715 templ->SetAccessor(v8_str("t"), PGetter2);
4716 LocalContext context;
4717 context->Global()->Set(v8_str("o"), templ->NewInstance());
4718 for (int i = 0; i < 100; i++) {
4719 v8::HandleScope inner_scope;
4720 v8::Handle<v8::Object> obj = templ->NewInstance();
4721 CHECK_NE(obj, context->Global()->Get(v8_str("o")));
4722 context->Global()->Set(v8_str("o2"), obj);
4723 v8::Handle<Value> value =
4724 Script::Compile(v8_str("o.__proto__ === o2.__proto__"))->Run();
4725 CHECK_EQ(v8::True(), value);
4726 context->Global()->Set(v8_str("o"), obj);
4727 }
4728}
4729
4730
Ben Murdochb0fe1622011-05-05 13:52:32 +01004731static int StrCmp16(uint16_t* a, uint16_t* b) {
4732 while (true) {
4733 if (*a == 0 && *b == 0) return 0;
4734 if (*a != *b) return 0 + *a - *b;
4735 a++;
4736 b++;
4737 }
4738}
4739
4740
4741static int StrNCmp16(uint16_t* a, uint16_t* b, int n) {
4742 while (true) {
4743 if (n-- == 0) return 0;
4744 if (*a == 0 && *b == 0) return 0;
4745 if (*a != *b) return 0 + *a - *b;
4746 a++;
4747 b++;
4748 }
4749}
4750
4751
Steve Blocka7e24c12009-10-30 11:49:00 +00004752THREADED_TEST(StringWrite) {
4753 v8::HandleScope scope;
4754 v8::Handle<String> str = v8_str("abcde");
Ben Murdochb0fe1622011-05-05 13:52:32 +01004755 // abc<Icelandic eth><Unicode snowman>.
4756 v8::Handle<String> str2 = v8_str("abc\303\260\342\230\203");
4757
4758 CHECK_EQ(5, str2->Length());
Steve Blocka7e24c12009-10-30 11:49:00 +00004759
4760 char buf[100];
Ben Murdochb0fe1622011-05-05 13:52:32 +01004761 char utf8buf[100];
4762 uint16_t wbuf[100];
Steve Blocka7e24c12009-10-30 11:49:00 +00004763 int len;
Ben Murdochb0fe1622011-05-05 13:52:32 +01004764 int charlen;
4765
4766 memset(utf8buf, 0x1, sizeof(utf8buf));
4767 len = str2->WriteUtf8(utf8buf, sizeof(utf8buf), &charlen);
Steve Block44f0eee2011-05-26 01:26:41 +01004768 CHECK_EQ(9, len);
4769 CHECK_EQ(5, charlen);
4770 CHECK_EQ(0, strcmp(utf8buf, "abc\303\260\342\230\203"));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004771
4772 memset(utf8buf, 0x1, sizeof(utf8buf));
4773 len = str2->WriteUtf8(utf8buf, 8, &charlen);
Steve Block44f0eee2011-05-26 01:26:41 +01004774 CHECK_EQ(8, len);
4775 CHECK_EQ(5, charlen);
4776 CHECK_EQ(0, strncmp(utf8buf, "abc\303\260\342\230\203\1", 9));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004777
4778 memset(utf8buf, 0x1, sizeof(utf8buf));
4779 len = str2->WriteUtf8(utf8buf, 7, &charlen);
Steve Block44f0eee2011-05-26 01:26:41 +01004780 CHECK_EQ(5, len);
4781 CHECK_EQ(4, charlen);
4782 CHECK_EQ(0, strncmp(utf8buf, "abc\303\260\1", 5));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004783
4784 memset(utf8buf, 0x1, sizeof(utf8buf));
4785 len = str2->WriteUtf8(utf8buf, 6, &charlen);
Steve Block44f0eee2011-05-26 01:26:41 +01004786 CHECK_EQ(5, len);
4787 CHECK_EQ(4, charlen);
4788 CHECK_EQ(0, strncmp(utf8buf, "abc\303\260\1", 5));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004789
4790 memset(utf8buf, 0x1, sizeof(utf8buf));
4791 len = str2->WriteUtf8(utf8buf, 5, &charlen);
Steve Block44f0eee2011-05-26 01:26:41 +01004792 CHECK_EQ(5, len);
4793 CHECK_EQ(4, charlen);
4794 CHECK_EQ(0, strncmp(utf8buf, "abc\303\260\1", 5));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004795
4796 memset(utf8buf, 0x1, sizeof(utf8buf));
4797 len = str2->WriteUtf8(utf8buf, 4, &charlen);
Steve Block44f0eee2011-05-26 01:26:41 +01004798 CHECK_EQ(3, len);
4799 CHECK_EQ(3, charlen);
4800 CHECK_EQ(0, strncmp(utf8buf, "abc\1", 4));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004801
4802 memset(utf8buf, 0x1, sizeof(utf8buf));
4803 len = str2->WriteUtf8(utf8buf, 3, &charlen);
Steve Block44f0eee2011-05-26 01:26:41 +01004804 CHECK_EQ(3, len);
4805 CHECK_EQ(3, charlen);
4806 CHECK_EQ(0, strncmp(utf8buf, "abc\1", 4));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004807
4808 memset(utf8buf, 0x1, sizeof(utf8buf));
4809 len = str2->WriteUtf8(utf8buf, 2, &charlen);
Steve Block44f0eee2011-05-26 01:26:41 +01004810 CHECK_EQ(2, len);
4811 CHECK_EQ(2, charlen);
4812 CHECK_EQ(0, strncmp(utf8buf, "ab\1", 3));
Steve Blocka7e24c12009-10-30 11:49:00 +00004813
4814 memset(buf, 0x1, sizeof(buf));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004815 memset(wbuf, 0x1, sizeof(wbuf));
Steve Blocka7e24c12009-10-30 11:49:00 +00004816 len = str->WriteAscii(buf);
Steve Block44f0eee2011-05-26 01:26:41 +01004817 CHECK_EQ(5, len);
Ben Murdochb0fe1622011-05-05 13:52:32 +01004818 len = str->Write(wbuf);
Steve Block44f0eee2011-05-26 01:26:41 +01004819 CHECK_EQ(5, len);
4820 CHECK_EQ(0, strcmp("abcde", buf));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004821 uint16_t answer1[] = {'a', 'b', 'c', 'd', 'e', '\0'};
Steve Block44f0eee2011-05-26 01:26:41 +01004822 CHECK_EQ(0, StrCmp16(answer1, wbuf));
Steve Blocka7e24c12009-10-30 11:49:00 +00004823
4824 memset(buf, 0x1, sizeof(buf));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004825 memset(wbuf, 0x1, sizeof(wbuf));
Steve Blocka7e24c12009-10-30 11:49:00 +00004826 len = str->WriteAscii(buf, 0, 4);
Steve Block44f0eee2011-05-26 01:26:41 +01004827 CHECK_EQ(4, len);
Ben Murdochb0fe1622011-05-05 13:52:32 +01004828 len = str->Write(wbuf, 0, 4);
Steve Block44f0eee2011-05-26 01:26:41 +01004829 CHECK_EQ(4, len);
4830 CHECK_EQ(0, strncmp("abcd\1", buf, 5));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004831 uint16_t answer2[] = {'a', 'b', 'c', 'd', 0x101};
Steve Block44f0eee2011-05-26 01:26:41 +01004832 CHECK_EQ(0, StrNCmp16(answer2, wbuf, 5));
Steve Blocka7e24c12009-10-30 11:49:00 +00004833
4834 memset(buf, 0x1, sizeof(buf));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004835 memset(wbuf, 0x1, sizeof(wbuf));
Steve Blocka7e24c12009-10-30 11:49:00 +00004836 len = str->WriteAscii(buf, 0, 5);
Steve Block44f0eee2011-05-26 01:26:41 +01004837 CHECK_EQ(5, len);
Ben Murdochb0fe1622011-05-05 13:52:32 +01004838 len = str->Write(wbuf, 0, 5);
Steve Block44f0eee2011-05-26 01:26:41 +01004839 CHECK_EQ(5, len);
4840 CHECK_EQ(0, strncmp("abcde\1", buf, 6));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004841 uint16_t answer3[] = {'a', 'b', 'c', 'd', 'e', 0x101};
Steve Block44f0eee2011-05-26 01:26:41 +01004842 CHECK_EQ(0, StrNCmp16(answer3, wbuf, 6));
Steve Blocka7e24c12009-10-30 11:49:00 +00004843
4844 memset(buf, 0x1, sizeof(buf));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004845 memset(wbuf, 0x1, sizeof(wbuf));
Steve Blocka7e24c12009-10-30 11:49:00 +00004846 len = str->WriteAscii(buf, 0, 6);
Steve Block44f0eee2011-05-26 01:26:41 +01004847 CHECK_EQ(5, len);
Ben Murdochb0fe1622011-05-05 13:52:32 +01004848 len = str->Write(wbuf, 0, 6);
Steve Block44f0eee2011-05-26 01:26:41 +01004849 CHECK_EQ(5, len);
4850 CHECK_EQ(0, strcmp("abcde", buf));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004851 uint16_t answer4[] = {'a', 'b', 'c', 'd', 'e', '\0'};
Steve Block44f0eee2011-05-26 01:26:41 +01004852 CHECK_EQ(0, StrCmp16(answer4, wbuf));
Steve Blocka7e24c12009-10-30 11:49:00 +00004853
4854 memset(buf, 0x1, sizeof(buf));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004855 memset(wbuf, 0x1, sizeof(wbuf));
Steve Blocka7e24c12009-10-30 11:49:00 +00004856 len = str->WriteAscii(buf, 4, -1);
Steve Block44f0eee2011-05-26 01:26:41 +01004857 CHECK_EQ(1, len);
Ben Murdochb0fe1622011-05-05 13:52:32 +01004858 len = str->Write(wbuf, 4, -1);
Steve Block44f0eee2011-05-26 01:26:41 +01004859 CHECK_EQ(1, len);
4860 CHECK_EQ(0, strcmp("e", buf));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004861 uint16_t answer5[] = {'e', '\0'};
Steve Block44f0eee2011-05-26 01:26:41 +01004862 CHECK_EQ(0, StrCmp16(answer5, wbuf));
Steve Blocka7e24c12009-10-30 11:49:00 +00004863
4864 memset(buf, 0x1, sizeof(buf));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004865 memset(wbuf, 0x1, sizeof(wbuf));
Steve Blocka7e24c12009-10-30 11:49:00 +00004866 len = str->WriteAscii(buf, 4, 6);
Steve Block44f0eee2011-05-26 01:26:41 +01004867 CHECK_EQ(1, len);
Ben Murdochb0fe1622011-05-05 13:52:32 +01004868 len = str->Write(wbuf, 4, 6);
Steve Block44f0eee2011-05-26 01:26:41 +01004869 CHECK_EQ(1, len);
4870 CHECK_EQ(0, strcmp("e", buf));
4871 CHECK_EQ(0, StrCmp16(answer5, wbuf));
Steve Blocka7e24c12009-10-30 11:49:00 +00004872
4873 memset(buf, 0x1, sizeof(buf));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004874 memset(wbuf, 0x1, sizeof(wbuf));
Steve Blocka7e24c12009-10-30 11:49:00 +00004875 len = str->WriteAscii(buf, 4, 1);
Steve Block44f0eee2011-05-26 01:26:41 +01004876 CHECK_EQ(1, len);
Ben Murdochb0fe1622011-05-05 13:52:32 +01004877 len = str->Write(wbuf, 4, 1);
Steve Block44f0eee2011-05-26 01:26:41 +01004878 CHECK_EQ(1, len);
4879 CHECK_EQ(0, strncmp("e\1", buf, 2));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004880 uint16_t answer6[] = {'e', 0x101};
Steve Block44f0eee2011-05-26 01:26:41 +01004881 CHECK_EQ(0, StrNCmp16(answer6, wbuf, 2));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004882
4883 memset(buf, 0x1, sizeof(buf));
4884 memset(wbuf, 0x1, sizeof(wbuf));
4885 len = str->WriteAscii(buf, 3, 1);
Steve Block44f0eee2011-05-26 01:26:41 +01004886 CHECK_EQ(1, len);
Ben Murdochb0fe1622011-05-05 13:52:32 +01004887 len = str->Write(wbuf, 3, 1);
Steve Block44f0eee2011-05-26 01:26:41 +01004888 CHECK_EQ(1, len);
4889 CHECK_EQ(0, strncmp("d\1", buf, 2));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004890 uint16_t answer7[] = {'d', 0x101};
Steve Block44f0eee2011-05-26 01:26:41 +01004891 CHECK_EQ(0, StrNCmp16(answer7, wbuf, 2));
Steve Blocka7e24c12009-10-30 11:49:00 +00004892}
4893
4894
4895THREADED_TEST(ToArrayIndex) {
4896 v8::HandleScope scope;
4897 LocalContext context;
4898
4899 v8::Handle<String> str = v8_str("42");
4900 v8::Handle<v8::Uint32> index = str->ToArrayIndex();
4901 CHECK(!index.IsEmpty());
4902 CHECK_EQ(42.0, index->Uint32Value());
4903 str = v8_str("42asdf");
4904 index = str->ToArrayIndex();
4905 CHECK(index.IsEmpty());
4906 str = v8_str("-42");
4907 index = str->ToArrayIndex();
4908 CHECK(index.IsEmpty());
4909 str = v8_str("4294967295");
4910 index = str->ToArrayIndex();
4911 CHECK(!index.IsEmpty());
4912 CHECK_EQ(4294967295.0, index->Uint32Value());
4913 v8::Handle<v8::Number> num = v8::Number::New(1);
4914 index = num->ToArrayIndex();
4915 CHECK(!index.IsEmpty());
4916 CHECK_EQ(1.0, index->Uint32Value());
4917 num = v8::Number::New(-1);
4918 index = num->ToArrayIndex();
4919 CHECK(index.IsEmpty());
4920 v8::Handle<v8::Object> obj = v8::Object::New();
4921 index = obj->ToArrayIndex();
4922 CHECK(index.IsEmpty());
4923}
4924
4925
4926THREADED_TEST(ErrorConstruction) {
4927 v8::HandleScope scope;
4928 LocalContext context;
4929
4930 v8::Handle<String> foo = v8_str("foo");
4931 v8::Handle<String> message = v8_str("message");
4932 v8::Handle<Value> range_error = v8::Exception::RangeError(foo);
4933 CHECK(range_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004934 v8::Handle<v8::Object> range_obj = range_error.As<v8::Object>();
4935 CHECK(range_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004936 v8::Handle<Value> reference_error = v8::Exception::ReferenceError(foo);
4937 CHECK(reference_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004938 CHECK(reference_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004939 v8::Handle<Value> syntax_error = v8::Exception::SyntaxError(foo);
4940 CHECK(syntax_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004941 CHECK(syntax_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004942 v8::Handle<Value> type_error = v8::Exception::TypeError(foo);
4943 CHECK(type_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004944 CHECK(type_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004945 v8::Handle<Value> error = v8::Exception::Error(foo);
4946 CHECK(error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004947 CHECK(error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004948}
4949
4950
4951static v8::Handle<Value> YGetter(Local<String> name, const AccessorInfo& info) {
4952 ApiTestFuzzer::Fuzz();
4953 return v8_num(10);
4954}
4955
4956
4957static void YSetter(Local<String> name,
4958 Local<Value> value,
4959 const AccessorInfo& info) {
4960 if (info.This()->Has(name)) {
4961 info.This()->Delete(name);
4962 }
4963 info.This()->Set(name, value);
4964}
4965
4966
4967THREADED_TEST(DeleteAccessor) {
4968 v8::HandleScope scope;
4969 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4970 obj->SetAccessor(v8_str("y"), YGetter, YSetter);
4971 LocalContext context;
4972 v8::Handle<v8::Object> holder = obj->NewInstance();
4973 context->Global()->Set(v8_str("holder"), holder);
4974 v8::Handle<Value> result = CompileRun(
4975 "holder.y = 11; holder.y = 12; holder.y");
4976 CHECK_EQ(12, result->Uint32Value());
4977}
4978
4979
4980THREADED_TEST(TypeSwitch) {
4981 v8::HandleScope scope;
4982 v8::Handle<v8::FunctionTemplate> templ1 = v8::FunctionTemplate::New();
4983 v8::Handle<v8::FunctionTemplate> templ2 = v8::FunctionTemplate::New();
4984 v8::Handle<v8::FunctionTemplate> templ3 = v8::FunctionTemplate::New();
4985 v8::Handle<v8::FunctionTemplate> templs[3] = { templ1, templ2, templ3 };
4986 v8::Handle<v8::TypeSwitch> type_switch = v8::TypeSwitch::New(3, templs);
4987 LocalContext context;
4988 v8::Handle<v8::Object> obj0 = v8::Object::New();
4989 v8::Handle<v8::Object> obj1 = templ1->GetFunction()->NewInstance();
4990 v8::Handle<v8::Object> obj2 = templ2->GetFunction()->NewInstance();
4991 v8::Handle<v8::Object> obj3 = templ3->GetFunction()->NewInstance();
4992 for (int i = 0; i < 10; i++) {
4993 CHECK_EQ(0, type_switch->match(obj0));
4994 CHECK_EQ(1, type_switch->match(obj1));
4995 CHECK_EQ(2, type_switch->match(obj2));
4996 CHECK_EQ(3, type_switch->match(obj3));
4997 CHECK_EQ(3, type_switch->match(obj3));
4998 CHECK_EQ(2, type_switch->match(obj2));
4999 CHECK_EQ(1, type_switch->match(obj1));
5000 CHECK_EQ(0, type_switch->match(obj0));
5001 }
5002}
5003
5004
5005// For use within the TestSecurityHandler() test.
5006static bool g_security_callback_result = false;
5007static bool NamedSecurityTestCallback(Local<v8::Object> global,
5008 Local<Value> name,
5009 v8::AccessType type,
5010 Local<Value> data) {
5011 // Always allow read access.
5012 if (type == v8::ACCESS_GET)
5013 return true;
5014
5015 // Sometimes allow other access.
5016 return g_security_callback_result;
5017}
5018
5019
5020static bool IndexedSecurityTestCallback(Local<v8::Object> global,
5021 uint32_t key,
5022 v8::AccessType type,
5023 Local<Value> data) {
5024 // Always allow read access.
5025 if (type == v8::ACCESS_GET)
5026 return true;
5027
5028 // Sometimes allow other access.
5029 return g_security_callback_result;
5030}
5031
5032
5033static int trouble_nesting = 0;
5034static v8::Handle<Value> TroubleCallback(const v8::Arguments& args) {
5035 ApiTestFuzzer::Fuzz();
5036 trouble_nesting++;
5037
5038 // Call a JS function that throws an uncaught exception.
5039 Local<v8::Object> arg_this = Context::GetCurrent()->Global();
5040 Local<Value> trouble_callee = (trouble_nesting == 3) ?
5041 arg_this->Get(v8_str("trouble_callee")) :
5042 arg_this->Get(v8_str("trouble_caller"));
5043 CHECK(trouble_callee->IsFunction());
5044 return Function::Cast(*trouble_callee)->Call(arg_this, 0, NULL);
5045}
5046
5047
5048static int report_count = 0;
5049static void ApiUncaughtExceptionTestListener(v8::Handle<v8::Message>,
5050 v8::Handle<Value>) {
5051 report_count++;
5052}
5053
5054
5055// Counts uncaught exceptions, but other tests running in parallel
5056// also have uncaught exceptions.
5057TEST(ApiUncaughtException) {
5058 report_count = 0;
5059 v8::HandleScope scope;
5060 LocalContext env;
5061 v8::V8::AddMessageListener(ApiUncaughtExceptionTestListener);
5062
5063 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback);
5064 v8::Local<v8::Object> global = env->Global();
5065 global->Set(v8_str("trouble"), fun->GetFunction());
5066
5067 Script::Compile(v8_str("function trouble_callee() {"
5068 " var x = null;"
5069 " return x.foo;"
5070 "};"
5071 "function trouble_caller() {"
5072 " trouble();"
5073 "};"))->Run();
5074 Local<Value> trouble = global->Get(v8_str("trouble"));
5075 CHECK(trouble->IsFunction());
5076 Local<Value> trouble_callee = global->Get(v8_str("trouble_callee"));
5077 CHECK(trouble_callee->IsFunction());
5078 Local<Value> trouble_caller = global->Get(v8_str("trouble_caller"));
5079 CHECK(trouble_caller->IsFunction());
5080 Function::Cast(*trouble_caller)->Call(global, 0, NULL);
5081 CHECK_EQ(1, report_count);
5082 v8::V8::RemoveMessageListeners(ApiUncaughtExceptionTestListener);
5083}
5084
Leon Clarke4515c472010-02-03 11:58:03 +00005085static const char* script_resource_name = "ExceptionInNativeScript.js";
5086static void ExceptionInNativeScriptTestListener(v8::Handle<v8::Message> message,
5087 v8::Handle<Value>) {
5088 v8::Handle<v8::Value> name_val = message->GetScriptResourceName();
5089 CHECK(!name_val.IsEmpty() && name_val->IsString());
5090 v8::String::AsciiValue name(message->GetScriptResourceName());
5091 CHECK_EQ(script_resource_name, *name);
5092 CHECK_EQ(3, message->GetLineNumber());
5093 v8::String::AsciiValue source_line(message->GetSourceLine());
5094 CHECK_EQ(" new o.foo();", *source_line);
5095}
5096
5097TEST(ExceptionInNativeScript) {
5098 v8::HandleScope scope;
5099 LocalContext env;
5100 v8::V8::AddMessageListener(ExceptionInNativeScriptTestListener);
5101
5102 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback);
5103 v8::Local<v8::Object> global = env->Global();
5104 global->Set(v8_str("trouble"), fun->GetFunction());
5105
5106 Script::Compile(v8_str("function trouble() {\n"
5107 " var o = {};\n"
5108 " new o.foo();\n"
5109 "};"), v8::String::New(script_resource_name))->Run();
5110 Local<Value> trouble = global->Get(v8_str("trouble"));
5111 CHECK(trouble->IsFunction());
5112 Function::Cast(*trouble)->Call(global, 0, NULL);
5113 v8::V8::RemoveMessageListeners(ExceptionInNativeScriptTestListener);
5114}
5115
Steve Blocka7e24c12009-10-30 11:49:00 +00005116
5117TEST(CompilationErrorUsingTryCatchHandler) {
5118 v8::HandleScope scope;
5119 LocalContext env;
5120 v8::TryCatch try_catch;
5121 Script::Compile(v8_str("This doesn't &*&@#$&*^ compile."));
5122 CHECK_NE(NULL, *try_catch.Exception());
5123 CHECK(try_catch.HasCaught());
5124}
5125
5126
5127TEST(TryCatchFinallyUsingTryCatchHandler) {
5128 v8::HandleScope scope;
5129 LocalContext env;
5130 v8::TryCatch try_catch;
5131 Script::Compile(v8_str("try { throw ''; } catch (e) {}"))->Run();
5132 CHECK(!try_catch.HasCaught());
5133 Script::Compile(v8_str("try { throw ''; } finally {}"))->Run();
5134 CHECK(try_catch.HasCaught());
5135 try_catch.Reset();
5136 Script::Compile(v8_str("(function() {"
5137 "try { throw ''; } finally { return; }"
5138 "})()"))->Run();
5139 CHECK(!try_catch.HasCaught());
5140 Script::Compile(v8_str("(function()"
5141 " { try { throw ''; } finally { throw 0; }"
5142 "})()"))->Run();
5143 CHECK(try_catch.HasCaught());
5144}
5145
5146
5147// SecurityHandler can't be run twice
5148TEST(SecurityHandler) {
5149 v8::HandleScope scope0;
5150 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
5151 global_template->SetAccessCheckCallbacks(NamedSecurityTestCallback,
5152 IndexedSecurityTestCallback);
5153 // Create an environment
5154 v8::Persistent<Context> context0 =
5155 Context::New(NULL, global_template);
5156 context0->Enter();
5157
5158 v8::Handle<v8::Object> global0 = context0->Global();
5159 v8::Handle<Script> script0 = v8_compile("foo = 111");
5160 script0->Run();
5161 global0->Set(v8_str("0"), v8_num(999));
5162 v8::Handle<Value> foo0 = global0->Get(v8_str("foo"));
5163 CHECK_EQ(111, foo0->Int32Value());
5164 v8::Handle<Value> z0 = global0->Get(v8_str("0"));
5165 CHECK_EQ(999, z0->Int32Value());
5166
5167 // Create another environment, should fail security checks.
5168 v8::HandleScope scope1;
5169
5170 v8::Persistent<Context> context1 =
5171 Context::New(NULL, global_template);
5172 context1->Enter();
5173
5174 v8::Handle<v8::Object> global1 = context1->Global();
5175 global1->Set(v8_str("othercontext"), global0);
5176 // This set will fail the security check.
5177 v8::Handle<Script> script1 =
5178 v8_compile("othercontext.foo = 222; othercontext[0] = 888;");
5179 script1->Run();
5180 // This read will pass the security check.
5181 v8::Handle<Value> foo1 = global0->Get(v8_str("foo"));
5182 CHECK_EQ(111, foo1->Int32Value());
5183 // This read will pass the security check.
5184 v8::Handle<Value> z1 = global0->Get(v8_str("0"));
5185 CHECK_EQ(999, z1->Int32Value());
5186
5187 // Create another environment, should pass security checks.
5188 { g_security_callback_result = true; // allow security handler to pass.
5189 v8::HandleScope scope2;
5190 LocalContext context2;
5191 v8::Handle<v8::Object> global2 = context2->Global();
5192 global2->Set(v8_str("othercontext"), global0);
5193 v8::Handle<Script> script2 =
5194 v8_compile("othercontext.foo = 333; othercontext[0] = 888;");
5195 script2->Run();
5196 v8::Handle<Value> foo2 = global0->Get(v8_str("foo"));
5197 CHECK_EQ(333, foo2->Int32Value());
5198 v8::Handle<Value> z2 = global0->Get(v8_str("0"));
5199 CHECK_EQ(888, z2->Int32Value());
5200 }
5201
5202 context1->Exit();
5203 context1.Dispose();
5204
5205 context0->Exit();
5206 context0.Dispose();
5207}
5208
5209
5210THREADED_TEST(SecurityChecks) {
5211 v8::HandleScope handle_scope;
5212 LocalContext env1;
5213 v8::Persistent<Context> env2 = Context::New();
5214
5215 Local<Value> foo = v8_str("foo");
5216 Local<Value> bar = v8_str("bar");
5217
5218 // Set to the same domain.
5219 env1->SetSecurityToken(foo);
5220
5221 // Create a function in env1.
5222 Script::Compile(v8_str("spy=function(){return spy;}"))->Run();
5223 Local<Value> spy = env1->Global()->Get(v8_str("spy"));
5224 CHECK(spy->IsFunction());
5225
5226 // Create another function accessing global objects.
5227 Script::Compile(v8_str("spy2=function(){return new this.Array();}"))->Run();
5228 Local<Value> spy2 = env1->Global()->Get(v8_str("spy2"));
5229 CHECK(spy2->IsFunction());
5230
5231 // Switch to env2 in the same domain and invoke spy on env2.
5232 {
5233 env2->SetSecurityToken(foo);
5234 // Enter env2
5235 Context::Scope scope_env2(env2);
5236 Local<Value> result = Function::Cast(*spy)->Call(env2->Global(), 0, NULL);
5237 CHECK(result->IsFunction());
5238 }
5239
5240 {
5241 env2->SetSecurityToken(bar);
5242 Context::Scope scope_env2(env2);
5243
5244 // Call cross_domain_call, it should throw an exception
5245 v8::TryCatch try_catch;
5246 Function::Cast(*spy2)->Call(env2->Global(), 0, NULL);
5247 CHECK(try_catch.HasCaught());
5248 }
5249
5250 env2.Dispose();
5251}
5252
5253
5254// Regression test case for issue 1183439.
5255THREADED_TEST(SecurityChecksForPrototypeChain) {
5256 v8::HandleScope scope;
5257 LocalContext current;
5258 v8::Persistent<Context> other = Context::New();
5259
5260 // Change context to be able to get to the Object function in the
5261 // other context without hitting the security checks.
5262 v8::Local<Value> other_object;
5263 { Context::Scope scope(other);
5264 other_object = other->Global()->Get(v8_str("Object"));
5265 other->Global()->Set(v8_num(42), v8_num(87));
5266 }
5267
5268 current->Global()->Set(v8_str("other"), other->Global());
5269 CHECK(v8_compile("other")->Run()->Equals(other->Global()));
5270
5271 // Make sure the security check fails here and we get an undefined
5272 // result instead of getting the Object function. Repeat in a loop
5273 // to make sure to exercise the IC code.
5274 v8::Local<Script> access_other0 = v8_compile("other.Object");
5275 v8::Local<Script> access_other1 = v8_compile("other[42]");
5276 for (int i = 0; i < 5; i++) {
5277 CHECK(!access_other0->Run()->Equals(other_object));
5278 CHECK(access_other0->Run()->IsUndefined());
5279 CHECK(!access_other1->Run()->Equals(v8_num(87)));
5280 CHECK(access_other1->Run()->IsUndefined());
5281 }
5282
5283 // Create an object that has 'other' in its prototype chain and make
5284 // sure we cannot access the Object function indirectly through
5285 // that. Repeat in a loop to make sure to exercise the IC code.
5286 v8_compile("function F() { };"
5287 "F.prototype = other;"
5288 "var f = new F();")->Run();
5289 v8::Local<Script> access_f0 = v8_compile("f.Object");
5290 v8::Local<Script> access_f1 = v8_compile("f[42]");
5291 for (int j = 0; j < 5; j++) {
5292 CHECK(!access_f0->Run()->Equals(other_object));
5293 CHECK(access_f0->Run()->IsUndefined());
5294 CHECK(!access_f1->Run()->Equals(v8_num(87)));
5295 CHECK(access_f1->Run()->IsUndefined());
5296 }
5297
5298 // Now it gets hairy: Set the prototype for the other global object
5299 // to be the current global object. The prototype chain for 'f' now
5300 // goes through 'other' but ends up in the current global object.
5301 { Context::Scope scope(other);
5302 other->Global()->Set(v8_str("__proto__"), current->Global());
5303 }
5304 // Set a named and an index property on the current global
5305 // object. To force the lookup to go through the other global object,
5306 // the properties must not exist in the other global object.
5307 current->Global()->Set(v8_str("foo"), v8_num(100));
5308 current->Global()->Set(v8_num(99), v8_num(101));
5309 // Try to read the properties from f and make sure that the access
5310 // gets stopped by the security checks on the other global object.
5311 Local<Script> access_f2 = v8_compile("f.foo");
5312 Local<Script> access_f3 = v8_compile("f[99]");
5313 for (int k = 0; k < 5; k++) {
5314 CHECK(!access_f2->Run()->Equals(v8_num(100)));
5315 CHECK(access_f2->Run()->IsUndefined());
5316 CHECK(!access_f3->Run()->Equals(v8_num(101)));
5317 CHECK(access_f3->Run()->IsUndefined());
5318 }
5319 other.Dispose();
5320}
5321
5322
5323THREADED_TEST(CrossDomainDelete) {
5324 v8::HandleScope handle_scope;
5325 LocalContext env1;
5326 v8::Persistent<Context> env2 = Context::New();
5327
5328 Local<Value> foo = v8_str("foo");
5329 Local<Value> bar = v8_str("bar");
5330
5331 // Set to the same domain.
5332 env1->SetSecurityToken(foo);
5333 env2->SetSecurityToken(foo);
5334
5335 env1->Global()->Set(v8_str("prop"), v8_num(3));
5336 env2->Global()->Set(v8_str("env1"), env1->Global());
5337
5338 // Change env2 to a different domain and delete env1.prop.
5339 env2->SetSecurityToken(bar);
5340 {
5341 Context::Scope scope_env2(env2);
5342 Local<Value> result =
5343 Script::Compile(v8_str("delete env1.prop"))->Run();
5344 CHECK(result->IsFalse());
5345 }
5346
5347 // Check that env1.prop still exists.
5348 Local<Value> v = env1->Global()->Get(v8_str("prop"));
5349 CHECK(v->IsNumber());
5350 CHECK_EQ(3, v->Int32Value());
5351
5352 env2.Dispose();
5353}
5354
5355
5356THREADED_TEST(CrossDomainIsPropertyEnumerable) {
5357 v8::HandleScope handle_scope;
5358 LocalContext env1;
5359 v8::Persistent<Context> env2 = Context::New();
5360
5361 Local<Value> foo = v8_str("foo");
5362 Local<Value> bar = v8_str("bar");
5363
5364 // Set to the same domain.
5365 env1->SetSecurityToken(foo);
5366 env2->SetSecurityToken(foo);
5367
5368 env1->Global()->Set(v8_str("prop"), v8_num(3));
5369 env2->Global()->Set(v8_str("env1"), env1->Global());
5370
5371 // env1.prop is enumerable in env2.
5372 Local<String> test = v8_str("propertyIsEnumerable.call(env1, 'prop')");
5373 {
5374 Context::Scope scope_env2(env2);
5375 Local<Value> result = Script::Compile(test)->Run();
5376 CHECK(result->IsTrue());
5377 }
5378
5379 // Change env2 to a different domain and test again.
5380 env2->SetSecurityToken(bar);
5381 {
5382 Context::Scope scope_env2(env2);
5383 Local<Value> result = Script::Compile(test)->Run();
5384 CHECK(result->IsFalse());
5385 }
5386
5387 env2.Dispose();
5388}
5389
5390
5391THREADED_TEST(CrossDomainForIn) {
5392 v8::HandleScope handle_scope;
5393 LocalContext env1;
5394 v8::Persistent<Context> env2 = Context::New();
5395
5396 Local<Value> foo = v8_str("foo");
5397 Local<Value> bar = v8_str("bar");
5398
5399 // Set to the same domain.
5400 env1->SetSecurityToken(foo);
5401 env2->SetSecurityToken(foo);
5402
5403 env1->Global()->Set(v8_str("prop"), v8_num(3));
5404 env2->Global()->Set(v8_str("env1"), env1->Global());
5405
5406 // Change env2 to a different domain and set env1's global object
5407 // as the __proto__ of an object in env2 and enumerate properties
5408 // in for-in. It shouldn't enumerate properties on env1's global
5409 // object.
5410 env2->SetSecurityToken(bar);
5411 {
5412 Context::Scope scope_env2(env2);
5413 Local<Value> result =
5414 CompileRun("(function(){var obj = {'__proto__':env1};"
5415 "for (var p in obj)"
5416 " if (p == 'prop') return false;"
5417 "return true;})()");
5418 CHECK(result->IsTrue());
5419 }
5420 env2.Dispose();
5421}
5422
5423
5424TEST(ContextDetachGlobal) {
5425 v8::HandleScope handle_scope;
5426 LocalContext env1;
5427 v8::Persistent<Context> env2 = Context::New();
5428
5429 Local<v8::Object> global1 = env1->Global();
5430
5431 Local<Value> foo = v8_str("foo");
5432
5433 // Set to the same domain.
5434 env1->SetSecurityToken(foo);
5435 env2->SetSecurityToken(foo);
5436
5437 // Enter env2
5438 env2->Enter();
5439
Andrei Popescu74b3c142010-03-29 12:03:09 +01005440 // Create a function in env2 and add a reference to it in env1.
Steve Blocka7e24c12009-10-30 11:49:00 +00005441 Local<v8::Object> global2 = env2->Global();
5442 global2->Set(v8_str("prop"), v8::Integer::New(1));
5443 CompileRun("function getProp() {return prop;}");
5444
5445 env1->Global()->Set(v8_str("getProp"),
5446 global2->Get(v8_str("getProp")));
5447
Andrei Popescu74b3c142010-03-29 12:03:09 +01005448 // Detach env2's global, and reuse the global object of env2
Steve Blocka7e24c12009-10-30 11:49:00 +00005449 env2->Exit();
5450 env2->DetachGlobal();
5451 // env2 has a new global object.
5452 CHECK(!env2->Global()->Equals(global2));
5453
5454 v8::Persistent<Context> env3 =
5455 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2);
5456 env3->SetSecurityToken(v8_str("bar"));
5457 env3->Enter();
5458
5459 Local<v8::Object> global3 = env3->Global();
5460 CHECK_EQ(global2, global3);
5461 CHECK(global3->Get(v8_str("prop"))->IsUndefined());
5462 CHECK(global3->Get(v8_str("getProp"))->IsUndefined());
5463 global3->Set(v8_str("prop"), v8::Integer::New(-1));
5464 global3->Set(v8_str("prop2"), v8::Integer::New(2));
5465 env3->Exit();
5466
5467 // Call getProp in env1, and it should return the value 1
5468 {
5469 Local<Value> get_prop = global1->Get(v8_str("getProp"));
5470 CHECK(get_prop->IsFunction());
5471 v8::TryCatch try_catch;
5472 Local<Value> r = Function::Cast(*get_prop)->Call(global1, 0, NULL);
5473 CHECK(!try_catch.HasCaught());
5474 CHECK_EQ(1, r->Int32Value());
5475 }
5476
5477 // Check that env3 is not accessible from env1
5478 {
5479 Local<Value> r = global3->Get(v8_str("prop2"));
5480 CHECK(r->IsUndefined());
5481 }
5482
5483 env2.Dispose();
5484 env3.Dispose();
5485}
5486
5487
Andrei Popescu74b3c142010-03-29 12:03:09 +01005488TEST(DetachAndReattachGlobal) {
5489 v8::HandleScope scope;
5490 LocalContext env1;
5491
5492 // Create second environment.
5493 v8::Persistent<Context> env2 = Context::New();
5494
5495 Local<Value> foo = v8_str("foo");
5496
5497 // Set same security token for env1 and env2.
5498 env1->SetSecurityToken(foo);
5499 env2->SetSecurityToken(foo);
5500
5501 // Create a property on the global object in env2.
5502 {
5503 v8::Context::Scope scope(env2);
5504 env2->Global()->Set(v8_str("p"), v8::Integer::New(42));
5505 }
5506
5507 // Create a reference to env2 global from env1 global.
5508 env1->Global()->Set(v8_str("other"), env2->Global());
5509
5510 // Check that we have access to other.p in env2 from env1.
5511 Local<Value> result = CompileRun("other.p");
5512 CHECK(result->IsInt32());
5513 CHECK_EQ(42, result->Int32Value());
5514
5515 // Hold on to global from env2 and detach global from env2.
5516 Local<v8::Object> global2 = env2->Global();
5517 env2->DetachGlobal();
5518
5519 // Check that the global has been detached. No other.p property can
5520 // be found.
5521 result = CompileRun("other.p");
5522 CHECK(result->IsUndefined());
5523
5524 // Reuse global2 for env3.
5525 v8::Persistent<Context> env3 =
5526 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2);
5527 CHECK_EQ(global2, env3->Global());
5528
5529 // Start by using the same security token for env3 as for env1 and env2.
5530 env3->SetSecurityToken(foo);
5531
5532 // Create a property on the global object in env3.
5533 {
5534 v8::Context::Scope scope(env3);
5535 env3->Global()->Set(v8_str("p"), v8::Integer::New(24));
5536 }
5537
5538 // Check that other.p is now the property in env3 and that we have access.
5539 result = CompileRun("other.p");
5540 CHECK(result->IsInt32());
5541 CHECK_EQ(24, result->Int32Value());
5542
5543 // Change security token for env3 to something different from env1 and env2.
5544 env3->SetSecurityToken(v8_str("bar"));
5545
5546 // Check that we do not have access to other.p in env1. |other| is now
5547 // the global object for env3 which has a different security token,
5548 // so access should be blocked.
5549 result = CompileRun("other.p");
5550 CHECK(result->IsUndefined());
5551
5552 // Detach the global for env3 and reattach it to env2.
5553 env3->DetachGlobal();
5554 env2->ReattachGlobal(global2);
5555
5556 // Check that we have access to other.p again in env1. |other| is now
5557 // the global object for env2 which has the same security token as env1.
5558 result = CompileRun("other.p");
5559 CHECK(result->IsInt32());
5560 CHECK_EQ(42, result->Int32Value());
5561
5562 env2.Dispose();
5563 env3.Dispose();
5564}
5565
5566
Steve Block1e0659c2011-05-24 12:43:12 +01005567static bool allowed_access_type[v8::ACCESS_KEYS + 1] = { false };
Steve Blocka7e24c12009-10-30 11:49:00 +00005568static bool NamedAccessBlocker(Local<v8::Object> global,
5569 Local<Value> name,
5570 v8::AccessType type,
5571 Local<Value> data) {
Steve Block1e0659c2011-05-24 12:43:12 +01005572 return Context::GetCurrent()->Global()->Equals(global) ||
5573 allowed_access_type[type];
Steve Blocka7e24c12009-10-30 11:49:00 +00005574}
5575
5576
5577static bool IndexedAccessBlocker(Local<v8::Object> global,
5578 uint32_t key,
5579 v8::AccessType type,
5580 Local<Value> data) {
Steve Block1e0659c2011-05-24 12:43:12 +01005581 return Context::GetCurrent()->Global()->Equals(global) ||
5582 allowed_access_type[type];
Steve Blocka7e24c12009-10-30 11:49:00 +00005583}
5584
5585
5586static int g_echo_value = -1;
5587static v8::Handle<Value> EchoGetter(Local<String> name,
5588 const AccessorInfo& info) {
5589 return v8_num(g_echo_value);
5590}
5591
5592
5593static void EchoSetter(Local<String> name,
5594 Local<Value> value,
5595 const AccessorInfo&) {
5596 if (value->IsNumber())
5597 g_echo_value = value->Int32Value();
5598}
5599
5600
5601static v8::Handle<Value> UnreachableGetter(Local<String> name,
5602 const AccessorInfo& info) {
5603 CHECK(false); // This function should not be called..
5604 return v8::Undefined();
5605}
5606
5607
5608static void UnreachableSetter(Local<String>, Local<Value>,
5609 const AccessorInfo&) {
5610 CHECK(false); // This function should nto be called.
5611}
5612
5613
Steve Block1e0659c2011-05-24 12:43:12 +01005614TEST(AccessControl) {
Steve Blocka7e24c12009-10-30 11:49:00 +00005615 v8::HandleScope handle_scope;
5616 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
5617
5618 global_template->SetAccessCheckCallbacks(NamedAccessBlocker,
5619 IndexedAccessBlocker);
5620
5621 // Add an accessor accessible by cross-domain JS code.
5622 global_template->SetAccessor(
5623 v8_str("accessible_prop"),
5624 EchoGetter, EchoSetter,
5625 v8::Handle<Value>(),
5626 v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE));
5627
5628 // Add an accessor that is not accessible by cross-domain JS code.
5629 global_template->SetAccessor(v8_str("blocked_prop"),
5630 UnreachableGetter, UnreachableSetter,
5631 v8::Handle<Value>(),
5632 v8::DEFAULT);
5633
5634 // Create an environment
5635 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
5636 context0->Enter();
5637
5638 v8::Handle<v8::Object> global0 = context0->Global();
5639
Steve Block1e0659c2011-05-24 12:43:12 +01005640 // Define a property with JS getter and setter.
5641 CompileRun(
5642 "function getter() { return 'getter'; };\n"
5643 "function setter() { return 'setter'; }\n"
5644 "Object.defineProperty(this, 'js_accessor_p', {get:getter, set:setter})");
5645
5646 Local<Value> getter = global0->Get(v8_str("getter"));
5647 Local<Value> setter = global0->Get(v8_str("setter"));
5648
5649 // And define normal element.
5650 global0->Set(239, v8_str("239"));
5651
5652 // Define an element with JS getter and setter.
5653 CompileRun(
5654 "function el_getter() { return 'el_getter'; };\n"
5655 "function el_setter() { return 'el_setter'; };\n"
5656 "Object.defineProperty(this, '42', {get: el_getter, set: el_setter});");
5657
5658 Local<Value> el_getter = global0->Get(v8_str("el_getter"));
5659 Local<Value> el_setter = global0->Get(v8_str("el_setter"));
5660
Steve Blocka7e24c12009-10-30 11:49:00 +00005661 v8::HandleScope scope1;
5662
5663 v8::Persistent<Context> context1 = Context::New();
5664 context1->Enter();
5665
5666 v8::Handle<v8::Object> global1 = context1->Global();
5667 global1->Set(v8_str("other"), global0);
5668
Steve Block1e0659c2011-05-24 12:43:12 +01005669 // Access blocked property.
5670 CompileRun("other.blocked_prop = 1");
5671
5672 ExpectUndefined("other.blocked_prop");
5673 ExpectUndefined(
5674 "Object.getOwnPropertyDescriptor(other, 'blocked_prop')");
5675 ExpectFalse("propertyIsEnumerable.call(other, 'blocked_prop')");
5676
5677 // Enable ACCESS_HAS
5678 allowed_access_type[v8::ACCESS_HAS] = true;
5679 ExpectUndefined("other.blocked_prop");
5680 // ... and now we can get the descriptor...
5681 ExpectUndefined(
5682 "Object.getOwnPropertyDescriptor(other, 'blocked_prop').value");
5683 // ... and enumerate the property.
5684 ExpectTrue("propertyIsEnumerable.call(other, 'blocked_prop')");
5685 allowed_access_type[v8::ACCESS_HAS] = false;
5686
5687 // Access blocked element.
5688 CompileRun("other[239] = 1");
5689
5690 ExpectUndefined("other[239]");
5691 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '239')");
5692 ExpectFalse("propertyIsEnumerable.call(other, '239')");
5693
5694 // Enable ACCESS_HAS
5695 allowed_access_type[v8::ACCESS_HAS] = true;
5696 ExpectUndefined("other[239]");
5697 // ... and now we can get the descriptor...
5698 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '239').value");
5699 // ... and enumerate the property.
5700 ExpectTrue("propertyIsEnumerable.call(other, '239')");
5701 allowed_access_type[v8::ACCESS_HAS] = false;
5702
5703 // Access a property with JS accessor.
5704 CompileRun("other.js_accessor_p = 2");
5705
5706 ExpectUndefined("other.js_accessor_p");
5707 ExpectUndefined(
5708 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p')");
5709
5710 // Enable ACCESS_HAS.
5711 allowed_access_type[v8::ACCESS_HAS] = true;
5712 ExpectUndefined("other.js_accessor_p");
5713 ExpectUndefined(
5714 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').get");
5715 ExpectUndefined(
5716 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').set");
5717 ExpectUndefined(
5718 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').value");
5719 allowed_access_type[v8::ACCESS_HAS] = false;
5720
5721 // Enable both ACCESS_HAS and ACCESS_GET.
5722 allowed_access_type[v8::ACCESS_HAS] = true;
5723 allowed_access_type[v8::ACCESS_GET] = true;
5724
5725 ExpectString("other.js_accessor_p", "getter");
5726 ExpectObject(
5727 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').get", getter);
5728 ExpectUndefined(
5729 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').set");
5730 ExpectUndefined(
5731 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').value");
5732
5733 allowed_access_type[v8::ACCESS_GET] = false;
5734 allowed_access_type[v8::ACCESS_HAS] = false;
5735
5736 // Enable both ACCESS_HAS and ACCESS_SET.
5737 allowed_access_type[v8::ACCESS_HAS] = true;
5738 allowed_access_type[v8::ACCESS_SET] = true;
5739
5740 ExpectUndefined("other.js_accessor_p");
5741 ExpectUndefined(
5742 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').get");
5743 ExpectObject(
5744 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').set", setter);
5745 ExpectUndefined(
5746 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').value");
5747
5748 allowed_access_type[v8::ACCESS_SET] = false;
5749 allowed_access_type[v8::ACCESS_HAS] = false;
5750
5751 // Enable both ACCESS_HAS, ACCESS_GET and ACCESS_SET.
5752 allowed_access_type[v8::ACCESS_HAS] = true;
5753 allowed_access_type[v8::ACCESS_GET] = true;
5754 allowed_access_type[v8::ACCESS_SET] = true;
5755
5756 ExpectString("other.js_accessor_p", "getter");
5757 ExpectObject(
5758 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').get", getter);
5759 ExpectObject(
5760 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').set", setter);
5761 ExpectUndefined(
5762 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').value");
5763
5764 allowed_access_type[v8::ACCESS_SET] = false;
5765 allowed_access_type[v8::ACCESS_GET] = false;
5766 allowed_access_type[v8::ACCESS_HAS] = false;
5767
5768 // Access an element with JS accessor.
5769 CompileRun("other[42] = 2");
5770
5771 ExpectUndefined("other[42]");
5772 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42')");
5773
5774 // Enable ACCESS_HAS.
5775 allowed_access_type[v8::ACCESS_HAS] = true;
5776 ExpectUndefined("other[42]");
5777 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').get");
5778 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').set");
5779 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').value");
5780 allowed_access_type[v8::ACCESS_HAS] = false;
5781
5782 // Enable both ACCESS_HAS and ACCESS_GET.
5783 allowed_access_type[v8::ACCESS_HAS] = true;
5784 allowed_access_type[v8::ACCESS_GET] = true;
5785
5786 ExpectString("other[42]", "el_getter");
5787 ExpectObject("Object.getOwnPropertyDescriptor(other, '42').get", el_getter);
5788 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').set");
5789 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').value");
5790
5791 allowed_access_type[v8::ACCESS_GET] = false;
5792 allowed_access_type[v8::ACCESS_HAS] = false;
5793
5794 // Enable both ACCESS_HAS and ACCESS_SET.
5795 allowed_access_type[v8::ACCESS_HAS] = true;
5796 allowed_access_type[v8::ACCESS_SET] = true;
5797
5798 ExpectUndefined("other[42]");
5799 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').get");
5800 ExpectObject("Object.getOwnPropertyDescriptor(other, '42').set", el_setter);
5801 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').value");
5802
5803 allowed_access_type[v8::ACCESS_SET] = false;
5804 allowed_access_type[v8::ACCESS_HAS] = false;
5805
5806 // Enable both ACCESS_HAS, ACCESS_GET and ACCESS_SET.
5807 allowed_access_type[v8::ACCESS_HAS] = true;
5808 allowed_access_type[v8::ACCESS_GET] = true;
5809 allowed_access_type[v8::ACCESS_SET] = true;
5810
5811 ExpectString("other[42]", "el_getter");
5812 ExpectObject("Object.getOwnPropertyDescriptor(other, '42').get", el_getter);
5813 ExpectObject("Object.getOwnPropertyDescriptor(other, '42').set", el_setter);
5814 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').value");
5815
5816 allowed_access_type[v8::ACCESS_SET] = false;
5817 allowed_access_type[v8::ACCESS_GET] = false;
5818 allowed_access_type[v8::ACCESS_HAS] = false;
5819
Steve Blocka7e24c12009-10-30 11:49:00 +00005820 v8::Handle<Value> value;
5821
Steve Blocka7e24c12009-10-30 11:49:00 +00005822 // Access accessible property
Steve Block1e0659c2011-05-24 12:43:12 +01005823 value = CompileRun("other.accessible_prop = 3");
Steve Blocka7e24c12009-10-30 11:49:00 +00005824 CHECK(value->IsNumber());
5825 CHECK_EQ(3, value->Int32Value());
Andrei Popescu31002712010-02-23 13:46:05 +00005826 CHECK_EQ(3, g_echo_value);
Steve Blocka7e24c12009-10-30 11:49:00 +00005827
Steve Block1e0659c2011-05-24 12:43:12 +01005828 value = CompileRun("other.accessible_prop");
Steve Blocka7e24c12009-10-30 11:49:00 +00005829 CHECK(value->IsNumber());
5830 CHECK_EQ(3, value->Int32Value());
5831
Steve Block1e0659c2011-05-24 12:43:12 +01005832 value = CompileRun(
5833 "Object.getOwnPropertyDescriptor(other, 'accessible_prop').value");
5834 CHECK(value->IsNumber());
5835 CHECK_EQ(3, value->Int32Value());
5836
5837 value = CompileRun("propertyIsEnumerable.call(other, 'accessible_prop')");
Steve Blocka7e24c12009-10-30 11:49:00 +00005838 CHECK(value->IsTrue());
5839
5840 // Enumeration doesn't enumerate accessors from inaccessible objects in
5841 // the prototype chain even if the accessors are in themselves accessible.
Steve Block1e0659c2011-05-24 12:43:12 +01005842 value =
Steve Blocka7e24c12009-10-30 11:49:00 +00005843 CompileRun("(function(){var obj = {'__proto__':other};"
5844 "for (var p in obj)"
5845 " if (p == 'accessible_prop' || p == 'blocked_prop') {"
5846 " return false;"
5847 " }"
5848 "return true;})()");
Steve Block1e0659c2011-05-24 12:43:12 +01005849 CHECK(value->IsTrue());
Steve Blocka7e24c12009-10-30 11:49:00 +00005850
5851 context1->Exit();
5852 context0->Exit();
5853 context1.Dispose();
5854 context0.Dispose();
5855}
5856
5857
Ben Murdoche0cee9b2011-05-25 10:26:03 +01005858TEST(AccessControlES5) {
5859 v8::HandleScope handle_scope;
5860 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
5861
5862 global_template->SetAccessCheckCallbacks(NamedAccessBlocker,
5863 IndexedAccessBlocker);
5864
Steve Block44f0eee2011-05-26 01:26:41 +01005865 // Add accessible accessor.
5866 global_template->SetAccessor(
5867 v8_str("accessible_prop"),
5868 EchoGetter, EchoSetter,
5869 v8::Handle<Value>(),
5870 v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE));
5871
5872
Ben Murdoche0cee9b2011-05-25 10:26:03 +01005873 // Add an accessor that is not accessible by cross-domain JS code.
5874 global_template->SetAccessor(v8_str("blocked_prop"),
5875 UnreachableGetter, UnreachableSetter,
5876 v8::Handle<Value>(),
5877 v8::DEFAULT);
5878
5879 // Create an environment
5880 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
5881 context0->Enter();
5882
5883 v8::Handle<v8::Object> global0 = context0->Global();
5884
5885 v8::Persistent<Context> context1 = Context::New();
5886 context1->Enter();
5887 v8::Handle<v8::Object> global1 = context1->Global();
5888 global1->Set(v8_str("other"), global0);
5889
5890 // Regression test for issue 1154.
5891 ExpectTrue("Object.keys(other).indexOf('blocked_prop') == -1");
5892
5893 ExpectUndefined("other.blocked_prop");
5894
5895 // Regression test for issue 1027.
5896 CompileRun("Object.defineProperty(\n"
5897 " other, 'blocked_prop', {configurable: false})");
5898 ExpectUndefined("other.blocked_prop");
5899 ExpectUndefined(
5900 "Object.getOwnPropertyDescriptor(other, 'blocked_prop')");
5901
5902 // Regression test for issue 1171.
5903 ExpectTrue("Object.isExtensible(other)");
5904 CompileRun("Object.preventExtensions(other)");
5905 ExpectTrue("Object.isExtensible(other)");
5906
5907 // Object.seal and Object.freeze.
5908 CompileRun("Object.freeze(other)");
5909 ExpectTrue("Object.isExtensible(other)");
5910
5911 CompileRun("Object.seal(other)");
5912 ExpectTrue("Object.isExtensible(other)");
Steve Block44f0eee2011-05-26 01:26:41 +01005913
5914 // Regression test for issue 1250.
5915 // Make sure that we can set the accessible accessors value using normal
5916 // assignment.
5917 CompileRun("other.accessible_prop = 42");
5918 CHECK_EQ(42, g_echo_value);
5919
5920 v8::Handle<Value> value;
5921 // We follow Safari in ignoring assignments to host object accessors.
5922 CompileRun("Object.defineProperty(other, 'accessible_prop', {value: -1})");
5923 value = CompileRun("other.accessible_prop == 42");
5924 CHECK(value->IsTrue());
Ben Murdoche0cee9b2011-05-25 10:26:03 +01005925}
5926
5927
Leon Clarke4515c472010-02-03 11:58:03 +00005928static bool GetOwnPropertyNamesNamedBlocker(Local<v8::Object> global,
5929 Local<Value> name,
5930 v8::AccessType type,
5931 Local<Value> data) {
5932 return false;
5933}
5934
5935
5936static bool GetOwnPropertyNamesIndexedBlocker(Local<v8::Object> global,
5937 uint32_t key,
5938 v8::AccessType type,
5939 Local<Value> data) {
5940 return false;
5941}
5942
5943
5944THREADED_TEST(AccessControlGetOwnPropertyNames) {
5945 v8::HandleScope handle_scope;
5946 v8::Handle<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New();
5947
5948 obj_template->Set(v8_str("x"), v8::Integer::New(42));
5949 obj_template->SetAccessCheckCallbacks(GetOwnPropertyNamesNamedBlocker,
5950 GetOwnPropertyNamesIndexedBlocker);
5951
5952 // Create an environment
5953 v8::Persistent<Context> context0 = Context::New(NULL, obj_template);
5954 context0->Enter();
5955
5956 v8::Handle<v8::Object> global0 = context0->Global();
5957
5958 v8::HandleScope scope1;
5959
5960 v8::Persistent<Context> context1 = Context::New();
5961 context1->Enter();
5962
5963 v8::Handle<v8::Object> global1 = context1->Global();
5964 global1->Set(v8_str("other"), global0);
5965 global1->Set(v8_str("object"), obj_template->NewInstance());
5966
5967 v8::Handle<Value> value;
5968
5969 // Attempt to get the property names of the other global object and
5970 // of an object that requires access checks. Accessing the other
5971 // global object should be blocked by access checks on the global
5972 // proxy object. Accessing the object that requires access checks
5973 // is blocked by the access checks on the object itself.
5974 value = CompileRun("Object.getOwnPropertyNames(other).length == 0");
5975 CHECK(value->IsTrue());
5976
5977 value = CompileRun("Object.getOwnPropertyNames(object).length == 0");
5978 CHECK(value->IsTrue());
5979
5980 context1->Exit();
5981 context0->Exit();
5982 context1.Dispose();
5983 context0.Dispose();
5984}
5985
5986
Steve Block8defd9f2010-07-08 12:39:36 +01005987static v8::Handle<v8::Array> NamedPropertyEnumerator(const AccessorInfo& info) {
5988 v8::Handle<v8::Array> result = v8::Array::New(1);
5989 result->Set(0, v8_str("x"));
5990 return result;
5991}
5992
5993
5994THREADED_TEST(GetOwnPropertyNamesWithInterceptor) {
5995 v8::HandleScope handle_scope;
5996 v8::Handle<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New();
5997
5998 obj_template->Set(v8_str("x"), v8::Integer::New(42));
5999 obj_template->SetNamedPropertyHandler(NULL, NULL, NULL, NULL,
6000 NamedPropertyEnumerator);
6001
6002 LocalContext context;
6003 v8::Handle<v8::Object> global = context->Global();
6004 global->Set(v8_str("object"), obj_template->NewInstance());
6005
6006 v8::Handle<Value> value =
6007 CompileRun("Object.getOwnPropertyNames(object).join(',')");
6008 CHECK_EQ(v8_str("x"), value);
6009}
6010
6011
Steve Blocka7e24c12009-10-30 11:49:00 +00006012static v8::Handle<Value> ConstTenGetter(Local<String> name,
6013 const AccessorInfo& info) {
6014 return v8_num(10);
6015}
6016
6017
6018THREADED_TEST(CrossDomainAccessors) {
6019 v8::HandleScope handle_scope;
6020
6021 v8::Handle<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New();
6022
6023 v8::Handle<v8::ObjectTemplate> global_template =
6024 func_template->InstanceTemplate();
6025
6026 v8::Handle<v8::ObjectTemplate> proto_template =
6027 func_template->PrototypeTemplate();
6028
6029 // Add an accessor to proto that's accessible by cross-domain JS code.
6030 proto_template->SetAccessor(v8_str("accessible"),
6031 ConstTenGetter, 0,
6032 v8::Handle<Value>(),
6033 v8::ALL_CAN_READ);
6034
6035 // Add an accessor that is not accessible by cross-domain JS code.
6036 global_template->SetAccessor(v8_str("unreachable"),
6037 UnreachableGetter, 0,
6038 v8::Handle<Value>(),
6039 v8::DEFAULT);
6040
6041 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
6042 context0->Enter();
6043
6044 Local<v8::Object> global = context0->Global();
6045 // Add a normal property that shadows 'accessible'
6046 global->Set(v8_str("accessible"), v8_num(11));
6047
6048 // Enter a new context.
6049 v8::HandleScope scope1;
6050 v8::Persistent<Context> context1 = Context::New();
6051 context1->Enter();
6052
6053 v8::Handle<v8::Object> global1 = context1->Global();
6054 global1->Set(v8_str("other"), global);
6055
6056 // Should return 10, instead of 11
6057 v8::Handle<Value> value = v8_compile("other.accessible")->Run();
6058 CHECK(value->IsNumber());
6059 CHECK_EQ(10, value->Int32Value());
6060
6061 value = v8_compile("other.unreachable")->Run();
6062 CHECK(value->IsUndefined());
6063
6064 context1->Exit();
6065 context0->Exit();
6066 context1.Dispose();
6067 context0.Dispose();
6068}
6069
6070
6071static int named_access_count = 0;
6072static int indexed_access_count = 0;
6073
6074static bool NamedAccessCounter(Local<v8::Object> global,
6075 Local<Value> name,
6076 v8::AccessType type,
6077 Local<Value> data) {
6078 named_access_count++;
6079 return true;
6080}
6081
6082
6083static bool IndexedAccessCounter(Local<v8::Object> global,
6084 uint32_t key,
6085 v8::AccessType type,
6086 Local<Value> data) {
6087 indexed_access_count++;
6088 return true;
6089}
6090
6091
6092// This one is too easily disturbed by other tests.
6093TEST(AccessControlIC) {
6094 named_access_count = 0;
6095 indexed_access_count = 0;
6096
6097 v8::HandleScope handle_scope;
6098
6099 // Create an environment.
6100 v8::Persistent<Context> context0 = Context::New();
6101 context0->Enter();
6102
6103 // Create an object that requires access-check functions to be
6104 // called for cross-domain access.
6105 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
6106 object_template->SetAccessCheckCallbacks(NamedAccessCounter,
6107 IndexedAccessCounter);
6108 Local<v8::Object> object = object_template->NewInstance();
6109
6110 v8::HandleScope scope1;
6111
6112 // Create another environment.
6113 v8::Persistent<Context> context1 = Context::New();
6114 context1->Enter();
6115
6116 // Make easy access to the object from the other environment.
6117 v8::Handle<v8::Object> global1 = context1->Global();
6118 global1->Set(v8_str("obj"), object);
6119
6120 v8::Handle<Value> value;
6121
6122 // Check that the named access-control function is called every time.
6123 CompileRun("function testProp(obj) {"
6124 " for (var i = 0; i < 10; i++) obj.prop = 1;"
6125 " for (var j = 0; j < 10; j++) obj.prop;"
6126 " return obj.prop"
6127 "}");
6128 value = CompileRun("testProp(obj)");
6129 CHECK(value->IsNumber());
6130 CHECK_EQ(1, value->Int32Value());
6131 CHECK_EQ(21, named_access_count);
6132
6133 // Check that the named access-control function is called every time.
6134 CompileRun("var p = 'prop';"
6135 "function testKeyed(obj) {"
6136 " for (var i = 0; i < 10; i++) obj[p] = 1;"
6137 " for (var j = 0; j < 10; j++) obj[p];"
6138 " return obj[p];"
6139 "}");
6140 // Use obj which requires access checks. No inline caching is used
6141 // in that case.
6142 value = CompileRun("testKeyed(obj)");
6143 CHECK(value->IsNumber());
6144 CHECK_EQ(1, value->Int32Value());
6145 CHECK_EQ(42, named_access_count);
6146 // Force the inline caches into generic state and try again.
6147 CompileRun("testKeyed({ a: 0 })");
6148 CompileRun("testKeyed({ b: 0 })");
6149 value = CompileRun("testKeyed(obj)");
6150 CHECK(value->IsNumber());
6151 CHECK_EQ(1, value->Int32Value());
6152 CHECK_EQ(63, named_access_count);
6153
6154 // Check that the indexed access-control function is called every time.
6155 CompileRun("function testIndexed(obj) {"
6156 " for (var i = 0; i < 10; i++) obj[0] = 1;"
6157 " for (var j = 0; j < 10; j++) obj[0];"
6158 " return obj[0]"
6159 "}");
6160 value = CompileRun("testIndexed(obj)");
6161 CHECK(value->IsNumber());
6162 CHECK_EQ(1, value->Int32Value());
6163 CHECK_EQ(21, indexed_access_count);
6164 // Force the inline caches into generic state.
6165 CompileRun("testIndexed(new Array(1))");
6166 // Test that the indexed access check is called.
6167 value = CompileRun("testIndexed(obj)");
6168 CHECK(value->IsNumber());
6169 CHECK_EQ(1, value->Int32Value());
6170 CHECK_EQ(42, indexed_access_count);
6171
6172 // Check that the named access check is called when invoking
6173 // functions on an object that requires access checks.
6174 CompileRun("obj.f = function() {}");
6175 CompileRun("function testCallNormal(obj) {"
6176 " for (var i = 0; i < 10; i++) obj.f();"
6177 "}");
6178 CompileRun("testCallNormal(obj)");
6179 CHECK_EQ(74, named_access_count);
6180
6181 // Force obj into slow case.
6182 value = CompileRun("delete obj.prop");
6183 CHECK(value->BooleanValue());
6184 // Force inline caches into dictionary probing mode.
6185 CompileRun("var o = { x: 0 }; delete o.x; testProp(o);");
6186 // Test that the named access check is called.
6187 value = CompileRun("testProp(obj);");
6188 CHECK(value->IsNumber());
6189 CHECK_EQ(1, value->Int32Value());
6190 CHECK_EQ(96, named_access_count);
6191
6192 // Force the call inline cache into dictionary probing mode.
6193 CompileRun("o.f = function() {}; testCallNormal(o)");
6194 // Test that the named access check is still called for each
6195 // invocation of the function.
6196 value = CompileRun("testCallNormal(obj)");
6197 CHECK_EQ(106, named_access_count);
6198
6199 context1->Exit();
6200 context0->Exit();
6201 context1.Dispose();
6202 context0.Dispose();
6203}
6204
6205
6206static bool NamedAccessFlatten(Local<v8::Object> global,
6207 Local<Value> name,
6208 v8::AccessType type,
6209 Local<Value> data) {
6210 char buf[100];
6211 int len;
6212
6213 CHECK(name->IsString());
6214
6215 memset(buf, 0x1, sizeof(buf));
Steve Block6ded16b2010-05-10 14:33:55 +01006216 len = name.As<String>()->WriteAscii(buf);
Steve Blocka7e24c12009-10-30 11:49:00 +00006217 CHECK_EQ(4, len);
6218
6219 uint16_t buf2[100];
6220
6221 memset(buf, 0x1, sizeof(buf));
Steve Block6ded16b2010-05-10 14:33:55 +01006222 len = name.As<String>()->Write(buf2);
Steve Blocka7e24c12009-10-30 11:49:00 +00006223 CHECK_EQ(4, len);
6224
6225 return true;
6226}
6227
6228
6229static bool IndexedAccessFlatten(Local<v8::Object> global,
6230 uint32_t key,
6231 v8::AccessType type,
6232 Local<Value> data) {
6233 return true;
6234}
6235
6236
6237// Regression test. In access checks, operations that may cause
6238// garbage collection are not allowed. It used to be the case that
6239// using the Write operation on a string could cause a garbage
6240// collection due to flattening of the string. This is no longer the
6241// case.
6242THREADED_TEST(AccessControlFlatten) {
6243 named_access_count = 0;
6244 indexed_access_count = 0;
6245
6246 v8::HandleScope handle_scope;
6247
6248 // Create an environment.
6249 v8::Persistent<Context> context0 = Context::New();
6250 context0->Enter();
6251
6252 // Create an object that requires access-check functions to be
6253 // called for cross-domain access.
6254 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
6255 object_template->SetAccessCheckCallbacks(NamedAccessFlatten,
6256 IndexedAccessFlatten);
6257 Local<v8::Object> object = object_template->NewInstance();
6258
6259 v8::HandleScope scope1;
6260
6261 // Create another environment.
6262 v8::Persistent<Context> context1 = Context::New();
6263 context1->Enter();
6264
6265 // Make easy access to the object from the other environment.
6266 v8::Handle<v8::Object> global1 = context1->Global();
6267 global1->Set(v8_str("obj"), object);
6268
6269 v8::Handle<Value> value;
6270
6271 value = v8_compile("var p = 'as' + 'df';")->Run();
6272 value = v8_compile("obj[p];")->Run();
6273
6274 context1->Exit();
6275 context0->Exit();
6276 context1.Dispose();
6277 context0.Dispose();
6278}
6279
6280
6281static v8::Handle<Value> AccessControlNamedGetter(
6282 Local<String>, const AccessorInfo&) {
6283 return v8::Integer::New(42);
6284}
6285
6286
6287static v8::Handle<Value> AccessControlNamedSetter(
6288 Local<String>, Local<Value> value, const AccessorInfo&) {
6289 return value;
6290}
6291
6292
6293static v8::Handle<Value> AccessControlIndexedGetter(
6294 uint32_t index,
6295 const AccessorInfo& info) {
6296 return v8_num(42);
6297}
6298
6299
6300static v8::Handle<Value> AccessControlIndexedSetter(
6301 uint32_t, Local<Value> value, const AccessorInfo&) {
6302 return value;
6303}
6304
6305
6306THREADED_TEST(AccessControlInterceptorIC) {
6307 named_access_count = 0;
6308 indexed_access_count = 0;
6309
6310 v8::HandleScope handle_scope;
6311
6312 // Create an environment.
6313 v8::Persistent<Context> context0 = Context::New();
6314 context0->Enter();
6315
6316 // Create an object that requires access-check functions to be
6317 // called for cross-domain access. The object also has interceptors
6318 // interceptor.
6319 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
6320 object_template->SetAccessCheckCallbacks(NamedAccessCounter,
6321 IndexedAccessCounter);
6322 object_template->SetNamedPropertyHandler(AccessControlNamedGetter,
6323 AccessControlNamedSetter);
6324 object_template->SetIndexedPropertyHandler(AccessControlIndexedGetter,
6325 AccessControlIndexedSetter);
6326 Local<v8::Object> object = object_template->NewInstance();
6327
6328 v8::HandleScope scope1;
6329
6330 // Create another environment.
6331 v8::Persistent<Context> context1 = Context::New();
6332 context1->Enter();
6333
6334 // Make easy access to the object from the other environment.
6335 v8::Handle<v8::Object> global1 = context1->Global();
6336 global1->Set(v8_str("obj"), object);
6337
6338 v8::Handle<Value> value;
6339
6340 // Check that the named access-control function is called every time
6341 // eventhough there is an interceptor on the object.
6342 value = v8_compile("for (var i = 0; i < 10; i++) obj.x = 1;")->Run();
6343 value = v8_compile("for (var i = 0; i < 10; i++) obj.x;"
6344 "obj.x")->Run();
6345 CHECK(value->IsNumber());
6346 CHECK_EQ(42, value->Int32Value());
6347 CHECK_EQ(21, named_access_count);
6348
6349 value = v8_compile("var p = 'x';")->Run();
6350 value = v8_compile("for (var i = 0; i < 10; i++) obj[p] = 1;")->Run();
6351 value = v8_compile("for (var i = 0; i < 10; i++) obj[p];"
6352 "obj[p]")->Run();
6353 CHECK(value->IsNumber());
6354 CHECK_EQ(42, value->Int32Value());
6355 CHECK_EQ(42, named_access_count);
6356
6357 // Check that the indexed access-control function is called every
6358 // time eventhough there is an interceptor on the object.
6359 value = v8_compile("for (var i = 0; i < 10; i++) obj[0] = 1;")->Run();
6360 value = v8_compile("for (var i = 0; i < 10; i++) obj[0];"
6361 "obj[0]")->Run();
6362 CHECK(value->IsNumber());
6363 CHECK_EQ(42, value->Int32Value());
6364 CHECK_EQ(21, indexed_access_count);
6365
6366 context1->Exit();
6367 context0->Exit();
6368 context1.Dispose();
6369 context0.Dispose();
6370}
6371
6372
6373THREADED_TEST(Version) {
6374 v8::V8::GetVersion();
6375}
6376
6377
6378static v8::Handle<Value> InstanceFunctionCallback(const v8::Arguments& args) {
6379 ApiTestFuzzer::Fuzz();
6380 return v8_num(12);
6381}
6382
6383
6384THREADED_TEST(InstanceProperties) {
6385 v8::HandleScope handle_scope;
6386 LocalContext context;
6387
6388 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
6389 Local<ObjectTemplate> instance = t->InstanceTemplate();
6390
6391 instance->Set(v8_str("x"), v8_num(42));
6392 instance->Set(v8_str("f"),
6393 v8::FunctionTemplate::New(InstanceFunctionCallback));
6394
6395 Local<Value> o = t->GetFunction()->NewInstance();
6396
6397 context->Global()->Set(v8_str("i"), o);
6398 Local<Value> value = Script::Compile(v8_str("i.x"))->Run();
6399 CHECK_EQ(42, value->Int32Value());
6400
6401 value = Script::Compile(v8_str("i.f()"))->Run();
6402 CHECK_EQ(12, value->Int32Value());
6403}
6404
6405
6406static v8::Handle<Value>
6407GlobalObjectInstancePropertiesGet(Local<String> key, const AccessorInfo&) {
6408 ApiTestFuzzer::Fuzz();
6409 return v8::Handle<Value>();
6410}
6411
6412
6413THREADED_TEST(GlobalObjectInstanceProperties) {
6414 v8::HandleScope handle_scope;
6415
6416 Local<Value> global_object;
6417
6418 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
6419 t->InstanceTemplate()->SetNamedPropertyHandler(
6420 GlobalObjectInstancePropertiesGet);
6421 Local<ObjectTemplate> instance_template = t->InstanceTemplate();
6422 instance_template->Set(v8_str("x"), v8_num(42));
6423 instance_template->Set(v8_str("f"),
6424 v8::FunctionTemplate::New(InstanceFunctionCallback));
6425
Ben Murdochb0fe1622011-05-05 13:52:32 +01006426 // The script to check how Crankshaft compiles missing global function
6427 // invocations. function g is not defined and should throw on call.
6428 const char* script =
6429 "function wrapper(call) {"
6430 " var x = 0, y = 1;"
6431 " for (var i = 0; i < 1000; i++) {"
6432 " x += i * 100;"
6433 " y += i * 100;"
6434 " }"
6435 " if (call) g();"
6436 "}"
6437 "for (var i = 0; i < 17; i++) wrapper(false);"
6438 "var thrown = 0;"
6439 "try { wrapper(true); } catch (e) { thrown = 1; };"
6440 "thrown";
6441
Steve Blocka7e24c12009-10-30 11:49:00 +00006442 {
6443 LocalContext env(NULL, instance_template);
6444 // Hold on to the global object so it can be used again in another
6445 // environment initialization.
6446 global_object = env->Global();
6447
6448 Local<Value> value = Script::Compile(v8_str("x"))->Run();
6449 CHECK_EQ(42, value->Int32Value());
6450 value = Script::Compile(v8_str("f()"))->Run();
6451 CHECK_EQ(12, value->Int32Value());
Ben Murdochb0fe1622011-05-05 13:52:32 +01006452 value = Script::Compile(v8_str(script))->Run();
6453 CHECK_EQ(1, value->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00006454 }
6455
6456 {
6457 // Create new environment reusing the global object.
6458 LocalContext env(NULL, instance_template, global_object);
6459 Local<Value> value = Script::Compile(v8_str("x"))->Run();
6460 CHECK_EQ(42, value->Int32Value());
6461 value = Script::Compile(v8_str("f()"))->Run();
6462 CHECK_EQ(12, value->Int32Value());
Ben Murdochb0fe1622011-05-05 13:52:32 +01006463 value = Script::Compile(v8_str(script))->Run();
6464 CHECK_EQ(1, value->Int32Value());
6465 }
6466}
6467
6468
6469THREADED_TEST(CallKnownGlobalReceiver) {
6470 v8::HandleScope handle_scope;
6471
6472 Local<Value> global_object;
6473
6474 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
6475 Local<ObjectTemplate> instance_template = t->InstanceTemplate();
6476
6477 // The script to check that we leave global object not
6478 // global object proxy on stack when we deoptimize from inside
6479 // arguments evaluation.
6480 // To provoke error we need to both force deoptimization
6481 // from arguments evaluation and to force CallIC to take
6482 // CallIC_Miss code path that can't cope with global proxy.
6483 const char* script =
6484 "function bar(x, y) { try { } finally { } }"
6485 "function baz(x) { try { } finally { } }"
6486 "function bom(x) { try { } finally { } }"
6487 "function foo(x) { bar([x], bom(2)); }"
6488 "for (var i = 0; i < 10000; i++) foo(1);"
6489 "foo";
6490
6491 Local<Value> foo;
6492 {
6493 LocalContext env(NULL, instance_template);
6494 // Hold on to the global object so it can be used again in another
6495 // environment initialization.
6496 global_object = env->Global();
6497 foo = Script::Compile(v8_str(script))->Run();
6498 }
6499
6500 {
6501 // Create new environment reusing the global object.
6502 LocalContext env(NULL, instance_template, global_object);
6503 env->Global()->Set(v8_str("foo"), foo);
6504 Local<Value> value = Script::Compile(v8_str("foo()"))->Run();
Steve Blocka7e24c12009-10-30 11:49:00 +00006505 }
6506}
6507
6508
6509static v8::Handle<Value> ShadowFunctionCallback(const v8::Arguments& args) {
6510 ApiTestFuzzer::Fuzz();
6511 return v8_num(42);
6512}
6513
6514
6515static int shadow_y;
6516static int shadow_y_setter_call_count;
6517static int shadow_y_getter_call_count;
6518
6519
6520static void ShadowYSetter(Local<String>, Local<Value>, const AccessorInfo&) {
6521 shadow_y_setter_call_count++;
6522 shadow_y = 42;
6523}
6524
6525
6526static v8::Handle<Value> ShadowYGetter(Local<String> name,
6527 const AccessorInfo& info) {
6528 ApiTestFuzzer::Fuzz();
6529 shadow_y_getter_call_count++;
6530 return v8_num(shadow_y);
6531}
6532
6533
6534static v8::Handle<Value> ShadowIndexedGet(uint32_t index,
6535 const AccessorInfo& info) {
6536 return v8::Handle<Value>();
6537}
6538
6539
6540static v8::Handle<Value> ShadowNamedGet(Local<String> key,
6541 const AccessorInfo&) {
6542 return v8::Handle<Value>();
6543}
6544
6545
6546THREADED_TEST(ShadowObject) {
6547 shadow_y = shadow_y_setter_call_count = shadow_y_getter_call_count = 0;
6548 v8::HandleScope handle_scope;
6549
6550 Local<ObjectTemplate> global_template = v8::ObjectTemplate::New();
6551 LocalContext context(NULL, global_template);
6552
6553 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
6554 t->InstanceTemplate()->SetNamedPropertyHandler(ShadowNamedGet);
6555 t->InstanceTemplate()->SetIndexedPropertyHandler(ShadowIndexedGet);
6556 Local<ObjectTemplate> proto = t->PrototypeTemplate();
6557 Local<ObjectTemplate> instance = t->InstanceTemplate();
6558
6559 // Only allow calls of f on instances of t.
6560 Local<v8::Signature> signature = v8::Signature::New(t);
6561 proto->Set(v8_str("f"),
6562 v8::FunctionTemplate::New(ShadowFunctionCallback,
6563 Local<Value>(),
6564 signature));
6565 proto->Set(v8_str("x"), v8_num(12));
6566
6567 instance->SetAccessor(v8_str("y"), ShadowYGetter, ShadowYSetter);
6568
6569 Local<Value> o = t->GetFunction()->NewInstance();
6570 context->Global()->Set(v8_str("__proto__"), o);
6571
6572 Local<Value> value =
6573 Script::Compile(v8_str("propertyIsEnumerable(0)"))->Run();
6574 CHECK(value->IsBoolean());
6575 CHECK(!value->BooleanValue());
6576
6577 value = Script::Compile(v8_str("x"))->Run();
6578 CHECK_EQ(12, value->Int32Value());
6579
6580 value = Script::Compile(v8_str("f()"))->Run();
6581 CHECK_EQ(42, value->Int32Value());
6582
6583 Script::Compile(v8_str("y = 42"))->Run();
6584 CHECK_EQ(1, shadow_y_setter_call_count);
6585 value = Script::Compile(v8_str("y"))->Run();
6586 CHECK_EQ(1, shadow_y_getter_call_count);
6587 CHECK_EQ(42, value->Int32Value());
6588}
6589
6590
6591THREADED_TEST(HiddenPrototype) {
6592 v8::HandleScope handle_scope;
6593 LocalContext context;
6594
6595 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New();
6596 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0));
6597 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New();
6598 t1->SetHiddenPrototype(true);
6599 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1));
6600 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New();
6601 t2->SetHiddenPrototype(true);
6602 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2));
6603 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New();
6604 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3));
6605
6606 Local<v8::Object> o0 = t0->GetFunction()->NewInstance();
6607 Local<v8::Object> o1 = t1->GetFunction()->NewInstance();
6608 Local<v8::Object> o2 = t2->GetFunction()->NewInstance();
6609 Local<v8::Object> o3 = t3->GetFunction()->NewInstance();
6610
6611 // Setting the prototype on an object skips hidden prototypes.
6612 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
6613 o0->Set(v8_str("__proto__"), o1);
6614 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
6615 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
6616 o0->Set(v8_str("__proto__"), o2);
6617 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
6618 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
6619 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
6620 o0->Set(v8_str("__proto__"), o3);
6621 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
6622 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
6623 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
6624 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value());
6625
6626 // Getting the prototype of o0 should get the first visible one
6627 // which is o3. Therefore, z should not be defined on the prototype
6628 // object.
6629 Local<Value> proto = o0->Get(v8_str("__proto__"));
6630 CHECK(proto->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01006631 CHECK(proto.As<v8::Object>()->Get(v8_str("z"))->IsUndefined());
Steve Blocka7e24c12009-10-30 11:49:00 +00006632}
6633
6634
Andrei Popescu402d9372010-02-26 13:31:12 +00006635THREADED_TEST(SetPrototype) {
6636 v8::HandleScope handle_scope;
6637 LocalContext context;
6638
6639 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New();
6640 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0));
6641 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New();
6642 t1->SetHiddenPrototype(true);
6643 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1));
6644 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New();
6645 t2->SetHiddenPrototype(true);
6646 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2));
6647 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New();
6648 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3));
6649
6650 Local<v8::Object> o0 = t0->GetFunction()->NewInstance();
6651 Local<v8::Object> o1 = t1->GetFunction()->NewInstance();
6652 Local<v8::Object> o2 = t2->GetFunction()->NewInstance();
6653 Local<v8::Object> o3 = t3->GetFunction()->NewInstance();
6654
6655 // Setting the prototype on an object does not skip hidden prototypes.
6656 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
6657 CHECK(o0->SetPrototype(o1));
6658 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
6659 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
6660 CHECK(o1->SetPrototype(o2));
6661 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
6662 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
6663 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
6664 CHECK(o2->SetPrototype(o3));
6665 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
6666 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
6667 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
6668 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value());
6669
6670 // Getting the prototype of o0 should get the first visible one
6671 // which is o3. Therefore, z should not be defined on the prototype
6672 // object.
6673 Local<Value> proto = o0->Get(v8_str("__proto__"));
6674 CHECK(proto->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01006675 CHECK_EQ(proto.As<v8::Object>(), o3);
Andrei Popescu402d9372010-02-26 13:31:12 +00006676
6677 // However, Object::GetPrototype ignores hidden prototype.
6678 Local<Value> proto0 = o0->GetPrototype();
6679 CHECK(proto0->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01006680 CHECK_EQ(proto0.As<v8::Object>(), o1);
Andrei Popescu402d9372010-02-26 13:31:12 +00006681
6682 Local<Value> proto1 = o1->GetPrototype();
6683 CHECK(proto1->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01006684 CHECK_EQ(proto1.As<v8::Object>(), o2);
Andrei Popescu402d9372010-02-26 13:31:12 +00006685
6686 Local<Value> proto2 = o2->GetPrototype();
6687 CHECK(proto2->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01006688 CHECK_EQ(proto2.As<v8::Object>(), o3);
Andrei Popescu402d9372010-02-26 13:31:12 +00006689}
6690
6691
6692THREADED_TEST(SetPrototypeThrows) {
6693 v8::HandleScope handle_scope;
6694 LocalContext context;
6695
6696 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
6697
6698 Local<v8::Object> o0 = t->GetFunction()->NewInstance();
6699 Local<v8::Object> o1 = t->GetFunction()->NewInstance();
6700
6701 CHECK(o0->SetPrototype(o1));
6702 // If setting the prototype leads to the cycle, SetPrototype should
6703 // return false and keep VM in sane state.
6704 v8::TryCatch try_catch;
6705 CHECK(!o1->SetPrototype(o0));
6706 CHECK(!try_catch.HasCaught());
Steve Block44f0eee2011-05-26 01:26:41 +01006707 ASSERT(!i::Isolate::Current()->has_pending_exception());
Andrei Popescu402d9372010-02-26 13:31:12 +00006708
6709 CHECK_EQ(42, CompileRun("function f() { return 42; }; f()")->Int32Value());
6710}
6711
6712
Steve Blocka7e24c12009-10-30 11:49:00 +00006713THREADED_TEST(GetterSetterExceptions) {
6714 v8::HandleScope handle_scope;
6715 LocalContext context;
6716 CompileRun(
6717 "function Foo() { };"
6718 "function Throw() { throw 5; };"
6719 "var x = { };"
6720 "x.__defineSetter__('set', Throw);"
6721 "x.__defineGetter__('get', Throw);");
6722 Local<v8::Object> x =
6723 Local<v8::Object>::Cast(context->Global()->Get(v8_str("x")));
6724 v8::TryCatch try_catch;
6725 x->Set(v8_str("set"), v8::Integer::New(8));
6726 x->Get(v8_str("get"));
6727 x->Set(v8_str("set"), v8::Integer::New(8));
6728 x->Get(v8_str("get"));
6729 x->Set(v8_str("set"), v8::Integer::New(8));
6730 x->Get(v8_str("get"));
6731 x->Set(v8_str("set"), v8::Integer::New(8));
6732 x->Get(v8_str("get"));
6733}
6734
6735
6736THREADED_TEST(Constructor) {
6737 v8::HandleScope handle_scope;
6738 LocalContext context;
6739 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
6740 templ->SetClassName(v8_str("Fun"));
6741 Local<Function> cons = templ->GetFunction();
6742 context->Global()->Set(v8_str("Fun"), cons);
6743 Local<v8::Object> inst = cons->NewInstance();
6744 i::Handle<i::JSObject> obj = v8::Utils::OpenHandle(*inst);
6745 Local<Value> value = CompileRun("(new Fun()).constructor === Fun");
6746 CHECK(value->BooleanValue());
6747}
6748
6749THREADED_TEST(FunctionDescriptorException) {
6750 v8::HandleScope handle_scope;
6751 LocalContext context;
6752 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
6753 templ->SetClassName(v8_str("Fun"));
6754 Local<Function> cons = templ->GetFunction();
6755 context->Global()->Set(v8_str("Fun"), cons);
6756 Local<Value> value = CompileRun(
6757 "function test() {"
6758 " try {"
6759 " (new Fun()).blah()"
6760 " } catch (e) {"
6761 " var str = String(e);"
6762 " if (str.indexOf('TypeError') == -1) return 1;"
6763 " if (str.indexOf('[object Fun]') != -1) return 2;"
Steve Block1e0659c2011-05-24 12:43:12 +01006764 " if (str.indexOf('#<Fun>') == -1) return 3;"
Steve Blocka7e24c12009-10-30 11:49:00 +00006765 " return 0;"
6766 " }"
6767 " return 4;"
6768 "}"
6769 "test();");
6770 CHECK_EQ(0, value->Int32Value());
6771}
6772
6773
6774THREADED_TEST(EvalAliasedDynamic) {
6775 v8::HandleScope scope;
6776 LocalContext current;
6777
6778 // Tests where aliased eval can only be resolved dynamically.
6779 Local<Script> script =
6780 Script::Compile(v8_str("function f(x) { "
6781 " var foo = 2;"
6782 " with (x) { return eval('foo'); }"
6783 "}"
6784 "foo = 0;"
6785 "result1 = f(new Object());"
6786 "result2 = f(this);"
6787 "var x = new Object();"
6788 "x.eval = function(x) { return 1; };"
6789 "result3 = f(x);"));
6790 script->Run();
6791 CHECK_EQ(2, current->Global()->Get(v8_str("result1"))->Int32Value());
6792 CHECK_EQ(0, current->Global()->Get(v8_str("result2"))->Int32Value());
6793 CHECK_EQ(1, current->Global()->Get(v8_str("result3"))->Int32Value());
6794
6795 v8::TryCatch try_catch;
6796 script =
6797 Script::Compile(v8_str("function f(x) { "
6798 " var bar = 2;"
6799 " with (x) { return eval('bar'); }"
6800 "}"
6801 "f(this)"));
6802 script->Run();
6803 CHECK(try_catch.HasCaught());
6804 try_catch.Reset();
6805}
6806
6807
6808THREADED_TEST(CrossEval) {
6809 v8::HandleScope scope;
6810 LocalContext other;
6811 LocalContext current;
6812
6813 Local<String> token = v8_str("<security token>");
6814 other->SetSecurityToken(token);
6815 current->SetSecurityToken(token);
6816
6817 // Setup reference from current to other.
6818 current->Global()->Set(v8_str("other"), other->Global());
6819
6820 // Check that new variables are introduced in other context.
6821 Local<Script> script =
6822 Script::Compile(v8_str("other.eval('var foo = 1234')"));
6823 script->Run();
6824 Local<Value> foo = other->Global()->Get(v8_str("foo"));
6825 CHECK_EQ(1234, foo->Int32Value());
6826 CHECK(!current->Global()->Has(v8_str("foo")));
6827
6828 // Check that writing to non-existing properties introduces them in
6829 // the other context.
6830 script =
6831 Script::Compile(v8_str("other.eval('na = 1234')"));
6832 script->Run();
6833 CHECK_EQ(1234, other->Global()->Get(v8_str("na"))->Int32Value());
6834 CHECK(!current->Global()->Has(v8_str("na")));
6835
6836 // Check that global variables in current context are not visible in other
6837 // context.
6838 v8::TryCatch try_catch;
6839 script =
6840 Script::Compile(v8_str("var bar = 42; other.eval('bar');"));
6841 Local<Value> result = script->Run();
6842 CHECK(try_catch.HasCaught());
6843 try_catch.Reset();
6844
6845 // Check that local variables in current context are not visible in other
6846 // context.
6847 script =
6848 Script::Compile(v8_str("(function() { "
6849 " var baz = 87;"
6850 " return other.eval('baz');"
6851 "})();"));
6852 result = script->Run();
6853 CHECK(try_catch.HasCaught());
6854 try_catch.Reset();
6855
6856 // Check that global variables in the other environment are visible
6857 // when evaluting code.
6858 other->Global()->Set(v8_str("bis"), v8_num(1234));
6859 script = Script::Compile(v8_str("other.eval('bis')"));
6860 CHECK_EQ(1234, script->Run()->Int32Value());
6861 CHECK(!try_catch.HasCaught());
6862
6863 // Check that the 'this' pointer points to the global object evaluating
6864 // code.
6865 other->Global()->Set(v8_str("t"), other->Global());
6866 script = Script::Compile(v8_str("other.eval('this == t')"));
6867 result = script->Run();
6868 CHECK(result->IsTrue());
6869 CHECK(!try_catch.HasCaught());
6870
6871 // Check that variables introduced in with-statement are not visible in
6872 // other context.
6873 script =
6874 Script::Compile(v8_str("with({x:2}){other.eval('x')}"));
6875 result = script->Run();
6876 CHECK(try_catch.HasCaught());
6877 try_catch.Reset();
6878
6879 // Check that you cannot use 'eval.call' with another object than the
6880 // current global object.
6881 script =
6882 Script::Compile(v8_str("other.y = 1; eval.call(other, 'y')"));
6883 result = script->Run();
6884 CHECK(try_catch.HasCaught());
6885}
6886
6887
6888// Test that calling eval in a context which has been detached from
6889// its global throws an exception. This behavior is consistent with
6890// other JavaScript implementations.
6891THREADED_TEST(EvalInDetachedGlobal) {
6892 v8::HandleScope scope;
6893
6894 v8::Persistent<Context> context0 = Context::New();
6895 v8::Persistent<Context> context1 = Context::New();
6896
6897 // Setup function in context0 that uses eval from context0.
6898 context0->Enter();
6899 v8::Handle<v8::Value> fun =
6900 CompileRun("var x = 42;"
6901 "(function() {"
6902 " var e = eval;"
6903 " return function(s) { return e(s); }"
6904 "})()");
6905 context0->Exit();
6906
6907 // Put the function into context1 and call it before and after
6908 // detaching the global. Before detaching, the call succeeds and
6909 // after detaching and exception is thrown.
6910 context1->Enter();
6911 context1->Global()->Set(v8_str("fun"), fun);
6912 v8::Handle<v8::Value> x_value = CompileRun("fun('x')");
6913 CHECK_EQ(42, x_value->Int32Value());
6914 context0->DetachGlobal();
6915 v8::TryCatch catcher;
6916 x_value = CompileRun("fun('x')");
6917 CHECK(x_value.IsEmpty());
6918 CHECK(catcher.HasCaught());
6919 context1->Exit();
6920
6921 context1.Dispose();
6922 context0.Dispose();
6923}
6924
6925
6926THREADED_TEST(CrossLazyLoad) {
6927 v8::HandleScope scope;
6928 LocalContext other;
6929 LocalContext current;
6930
6931 Local<String> token = v8_str("<security token>");
6932 other->SetSecurityToken(token);
6933 current->SetSecurityToken(token);
6934
6935 // Setup reference from current to other.
6936 current->Global()->Set(v8_str("other"), other->Global());
6937
6938 // Trigger lazy loading in other context.
6939 Local<Script> script =
6940 Script::Compile(v8_str("other.eval('new Date(42)')"));
6941 Local<Value> value = script->Run();
6942 CHECK_EQ(42.0, value->NumberValue());
6943}
6944
6945
6946static v8::Handle<Value> call_as_function(const v8::Arguments& args) {
Andrei Popescu402d9372010-02-26 13:31:12 +00006947 ApiTestFuzzer::Fuzz();
Steve Blocka7e24c12009-10-30 11:49:00 +00006948 if (args.IsConstructCall()) {
6949 if (args[0]->IsInt32()) {
6950 return v8_num(-args[0]->Int32Value());
6951 }
6952 }
6953
6954 return args[0];
6955}
6956
6957
6958// Test that a call handler can be set for objects which will allow
6959// non-function objects created through the API to be called as
6960// functions.
6961THREADED_TEST(CallAsFunction) {
6962 v8::HandleScope scope;
6963 LocalContext context;
6964
6965 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
6966 Local<ObjectTemplate> instance_template = t->InstanceTemplate();
6967 instance_template->SetCallAsFunctionHandler(call_as_function);
6968 Local<v8::Object> instance = t->GetFunction()->NewInstance();
6969 context->Global()->Set(v8_str("obj"), instance);
6970 v8::TryCatch try_catch;
6971 Local<Value> value;
6972 CHECK(!try_catch.HasCaught());
6973
6974 value = CompileRun("obj(42)");
6975 CHECK(!try_catch.HasCaught());
6976 CHECK_EQ(42, value->Int32Value());
6977
6978 value = CompileRun("(function(o){return o(49)})(obj)");
6979 CHECK(!try_catch.HasCaught());
6980 CHECK_EQ(49, value->Int32Value());
6981
6982 // test special case of call as function
6983 value = CompileRun("[obj]['0'](45)");
6984 CHECK(!try_catch.HasCaught());
6985 CHECK_EQ(45, value->Int32Value());
6986
6987 value = CompileRun("obj.call = Function.prototype.call;"
6988 "obj.call(null, 87)");
6989 CHECK(!try_catch.HasCaught());
6990 CHECK_EQ(87, value->Int32Value());
6991
6992 // Regression tests for bug #1116356: Calling call through call/apply
6993 // must work for non-function receivers.
6994 const char* apply_99 = "Function.prototype.call.apply(obj, [this, 99])";
6995 value = CompileRun(apply_99);
6996 CHECK(!try_catch.HasCaught());
6997 CHECK_EQ(99, value->Int32Value());
6998
6999 const char* call_17 = "Function.prototype.call.call(obj, this, 17)";
7000 value = CompileRun(call_17);
7001 CHECK(!try_catch.HasCaught());
7002 CHECK_EQ(17, value->Int32Value());
7003
7004 // Check that the call-as-function handler can be called through
Leon Clarkee46be812010-01-19 14:06:41 +00007005 // new.
Steve Blocka7e24c12009-10-30 11:49:00 +00007006 value = CompileRun("new obj(43)");
7007 CHECK(!try_catch.HasCaught());
7008 CHECK_EQ(-43, value->Int32Value());
7009}
7010
7011
7012static int CountHandles() {
7013 return v8::HandleScope::NumberOfHandles();
7014}
7015
7016
7017static int Recurse(int depth, int iterations) {
7018 v8::HandleScope scope;
7019 if (depth == 0) return CountHandles();
7020 for (int i = 0; i < iterations; i++) {
7021 Local<v8::Number> n = v8::Integer::New(42);
7022 }
7023 return Recurse(depth - 1, iterations);
7024}
7025
7026
7027THREADED_TEST(HandleIteration) {
7028 static const int kIterations = 500;
7029 static const int kNesting = 200;
7030 CHECK_EQ(0, CountHandles());
7031 {
7032 v8::HandleScope scope1;
7033 CHECK_EQ(0, CountHandles());
7034 for (int i = 0; i < kIterations; i++) {
7035 Local<v8::Number> n = v8::Integer::New(42);
7036 CHECK_EQ(i + 1, CountHandles());
7037 }
7038
7039 CHECK_EQ(kIterations, CountHandles());
7040 {
7041 v8::HandleScope scope2;
7042 for (int j = 0; j < kIterations; j++) {
7043 Local<v8::Number> n = v8::Integer::New(42);
7044 CHECK_EQ(j + 1 + kIterations, CountHandles());
7045 }
7046 }
7047 CHECK_EQ(kIterations, CountHandles());
7048 }
7049 CHECK_EQ(0, CountHandles());
7050 CHECK_EQ(kNesting * kIterations, Recurse(kNesting, kIterations));
7051}
7052
7053
7054static v8::Handle<Value> InterceptorHasOwnPropertyGetter(
7055 Local<String> name,
7056 const AccessorInfo& info) {
7057 ApiTestFuzzer::Fuzz();
7058 return v8::Handle<Value>();
7059}
7060
7061
7062THREADED_TEST(InterceptorHasOwnProperty) {
7063 v8::HandleScope scope;
7064 LocalContext context;
7065 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7066 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
7067 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetter);
7068 Local<Function> function = fun_templ->GetFunction();
7069 context->Global()->Set(v8_str("constructor"), function);
7070 v8::Handle<Value> value = CompileRun(
7071 "var o = new constructor();"
7072 "o.hasOwnProperty('ostehaps');");
7073 CHECK_EQ(false, value->BooleanValue());
7074 value = CompileRun(
7075 "o.ostehaps = 42;"
7076 "o.hasOwnProperty('ostehaps');");
7077 CHECK_EQ(true, value->BooleanValue());
7078 value = CompileRun(
7079 "var p = new constructor();"
7080 "p.hasOwnProperty('ostehaps');");
7081 CHECK_EQ(false, value->BooleanValue());
7082}
7083
7084
7085static v8::Handle<Value> InterceptorHasOwnPropertyGetterGC(
7086 Local<String> name,
7087 const AccessorInfo& info) {
7088 ApiTestFuzzer::Fuzz();
Steve Block44f0eee2011-05-26 01:26:41 +01007089 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +00007090 return v8::Handle<Value>();
7091}
7092
7093
7094THREADED_TEST(InterceptorHasOwnPropertyCausingGC) {
7095 v8::HandleScope scope;
7096 LocalContext context;
7097 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7098 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
7099 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetterGC);
7100 Local<Function> function = fun_templ->GetFunction();
7101 context->Global()->Set(v8_str("constructor"), function);
7102 // Let's first make some stuff so we can be sure to get a good GC.
7103 CompileRun(
7104 "function makestr(size) {"
7105 " switch (size) {"
7106 " case 1: return 'f';"
7107 " case 2: return 'fo';"
7108 " case 3: return 'foo';"
7109 " }"
7110 " return makestr(size >> 1) + makestr((size + 1) >> 1);"
7111 "}"
7112 "var x = makestr(12345);"
7113 "x = makestr(31415);"
7114 "x = makestr(23456);");
7115 v8::Handle<Value> value = CompileRun(
7116 "var o = new constructor();"
7117 "o.__proto__ = new String(x);"
7118 "o.hasOwnProperty('ostehaps');");
7119 CHECK_EQ(false, value->BooleanValue());
7120}
7121
7122
7123typedef v8::Handle<Value> (*NamedPropertyGetter)(Local<String> property,
7124 const AccessorInfo& info);
7125
7126
7127static void CheckInterceptorLoadIC(NamedPropertyGetter getter,
7128 const char* source,
7129 int expected) {
7130 v8::HandleScope scope;
7131 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -08007132 templ->SetNamedPropertyHandler(getter, 0, 0, 0, 0, v8_str("data"));
Steve Blocka7e24c12009-10-30 11:49:00 +00007133 LocalContext context;
7134 context->Global()->Set(v8_str("o"), templ->NewInstance());
7135 v8::Handle<Value> value = CompileRun(source);
7136 CHECK_EQ(expected, value->Int32Value());
7137}
7138
7139
7140static v8::Handle<Value> InterceptorLoadICGetter(Local<String> name,
7141 const AccessorInfo& info) {
7142 ApiTestFuzzer::Fuzz();
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -08007143 CHECK_EQ(v8_str("data"), info.Data());
7144 CHECK_EQ(v8_str("x"), name);
Steve Blocka7e24c12009-10-30 11:49:00 +00007145 return v8::Integer::New(42);
7146}
7147
7148
7149// This test should hit the load IC for the interceptor case.
7150THREADED_TEST(InterceptorLoadIC) {
7151 CheckInterceptorLoadIC(InterceptorLoadICGetter,
7152 "var result = 0;"
7153 "for (var i = 0; i < 1000; i++) {"
7154 " result = o.x;"
7155 "}",
7156 42);
7157}
7158
7159
7160// Below go several tests which verify that JITing for various
7161// configurations of interceptor and explicit fields works fine
7162// (those cases are special cased to get better performance).
7163
7164static v8::Handle<Value> InterceptorLoadXICGetter(Local<String> name,
7165 const AccessorInfo& info) {
7166 ApiTestFuzzer::Fuzz();
7167 return v8_str("x")->Equals(name)
7168 ? v8::Integer::New(42) : v8::Handle<v8::Value>();
7169}
7170
7171
7172THREADED_TEST(InterceptorLoadICWithFieldOnHolder) {
7173 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
7174 "var result = 0;"
7175 "o.y = 239;"
7176 "for (var i = 0; i < 1000; i++) {"
7177 " result = o.y;"
7178 "}",
7179 239);
7180}
7181
7182
7183THREADED_TEST(InterceptorLoadICWithSubstitutedProto) {
7184 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
7185 "var result = 0;"
7186 "o.__proto__ = { 'y': 239 };"
7187 "for (var i = 0; i < 1000; i++) {"
7188 " result = o.y + o.x;"
7189 "}",
7190 239 + 42);
7191}
7192
7193
7194THREADED_TEST(InterceptorLoadICWithPropertyOnProto) {
7195 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
7196 "var result = 0;"
7197 "o.__proto__.y = 239;"
7198 "for (var i = 0; i < 1000; i++) {"
7199 " result = o.y + o.x;"
7200 "}",
7201 239 + 42);
7202}
7203
7204
7205THREADED_TEST(InterceptorLoadICUndefined) {
7206 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
7207 "var result = 0;"
7208 "for (var i = 0; i < 1000; i++) {"
7209 " result = (o.y == undefined) ? 239 : 42;"
7210 "}",
7211 239);
7212}
7213
7214
7215THREADED_TEST(InterceptorLoadICWithOverride) {
7216 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
7217 "fst = new Object(); fst.__proto__ = o;"
7218 "snd = new Object(); snd.__proto__ = fst;"
7219 "var result1 = 0;"
7220 "for (var i = 0; i < 1000; i++) {"
7221 " result1 = snd.x;"
7222 "}"
7223 "fst.x = 239;"
7224 "var result = 0;"
7225 "for (var i = 0; i < 1000; i++) {"
7226 " result = snd.x;"
7227 "}"
7228 "result + result1",
7229 239 + 42);
7230}
7231
7232
7233// Test the case when we stored field into
7234// a stub, but interceptor produced value on its own.
7235THREADED_TEST(InterceptorLoadICFieldNotNeeded) {
7236 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
7237 "proto = new Object();"
7238 "o.__proto__ = proto;"
7239 "proto.x = 239;"
7240 "for (var i = 0; i < 1000; i++) {"
7241 " o.x;"
7242 // Now it should be ICed and keep a reference to x defined on proto
7243 "}"
7244 "var result = 0;"
7245 "for (var i = 0; i < 1000; i++) {"
7246 " result += o.x;"
7247 "}"
7248 "result;",
7249 42 * 1000);
7250}
7251
7252
7253// Test the case when we stored field into
7254// a stub, but it got invalidated later on.
7255THREADED_TEST(InterceptorLoadICInvalidatedField) {
7256 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
7257 "proto1 = new Object();"
7258 "proto2 = new Object();"
7259 "o.__proto__ = proto1;"
7260 "proto1.__proto__ = proto2;"
7261 "proto2.y = 239;"
7262 "for (var i = 0; i < 1000; i++) {"
7263 " o.y;"
7264 // Now it should be ICed and keep a reference to y defined on proto2
7265 "}"
7266 "proto1.y = 42;"
7267 "var result = 0;"
7268 "for (var i = 0; i < 1000; i++) {"
7269 " result += o.y;"
7270 "}"
7271 "result;",
7272 42 * 1000);
7273}
7274
7275
Steve Block6ded16b2010-05-10 14:33:55 +01007276static int interceptor_load_not_handled_calls = 0;
7277static v8::Handle<Value> InterceptorLoadNotHandled(Local<String> name,
7278 const AccessorInfo& info) {
7279 ++interceptor_load_not_handled_calls;
7280 return v8::Handle<v8::Value>();
7281}
7282
7283
7284// Test how post-interceptor lookups are done in the non-cacheable
7285// case: the interceptor should not be invoked during this lookup.
7286THREADED_TEST(InterceptorLoadICPostInterceptor) {
7287 interceptor_load_not_handled_calls = 0;
7288 CheckInterceptorLoadIC(InterceptorLoadNotHandled,
7289 "receiver = new Object();"
7290 "receiver.__proto__ = o;"
7291 "proto = new Object();"
7292 "/* Make proto a slow-case object. */"
7293 "for (var i = 0; i < 1000; i++) {"
7294 " proto[\"xxxxxxxx\" + i] = [];"
7295 "}"
7296 "proto.x = 17;"
7297 "o.__proto__ = proto;"
7298 "var result = 0;"
7299 "for (var i = 0; i < 1000; i++) {"
7300 " result += receiver.x;"
7301 "}"
7302 "result;",
7303 17 * 1000);
7304 CHECK_EQ(1000, interceptor_load_not_handled_calls);
7305}
7306
7307
Steve Blocka7e24c12009-10-30 11:49:00 +00007308// Test the case when we stored field into
7309// a stub, but it got invalidated later on due to override on
7310// global object which is between interceptor and fields' holders.
7311THREADED_TEST(InterceptorLoadICInvalidatedFieldViaGlobal) {
7312 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
7313 "o.__proto__ = this;" // set a global to be a proto of o.
7314 "this.__proto__.y = 239;"
7315 "for (var i = 0; i < 10; i++) {"
7316 " if (o.y != 239) throw 'oops: ' + o.y;"
7317 // Now it should be ICed and keep a reference to y defined on field_holder.
7318 "}"
7319 "this.y = 42;" // Assign on a global.
7320 "var result = 0;"
7321 "for (var i = 0; i < 10; i++) {"
7322 " result += o.y;"
7323 "}"
7324 "result;",
7325 42 * 10);
7326}
7327
7328
Steve Blocka7e24c12009-10-30 11:49:00 +00007329static void SetOnThis(Local<String> name,
7330 Local<Value> value,
7331 const AccessorInfo& info) {
7332 info.This()->ForceSet(name, value);
7333}
7334
7335
7336THREADED_TEST(InterceptorLoadICWithCallbackOnHolder) {
7337 v8::HandleScope scope;
7338 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7339 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
7340 templ->SetAccessor(v8_str("y"), Return239);
7341 LocalContext context;
7342 context->Global()->Set(v8_str("o"), templ->NewInstance());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01007343
7344 // Check the case when receiver and interceptor's holder
7345 // are the same objects.
Steve Blocka7e24c12009-10-30 11:49:00 +00007346 v8::Handle<Value> value = CompileRun(
7347 "var result = 0;"
7348 "for (var i = 0; i < 7; i++) {"
7349 " result = o.y;"
7350 "}");
7351 CHECK_EQ(239, value->Int32Value());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01007352
7353 // Check the case when interceptor's holder is in proto chain
7354 // of receiver.
7355 value = CompileRun(
7356 "r = { __proto__: o };"
7357 "var result = 0;"
7358 "for (var i = 0; i < 7; i++) {"
7359 " result = r.y;"
7360 "}");
7361 CHECK_EQ(239, value->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00007362}
7363
7364
7365THREADED_TEST(InterceptorLoadICWithCallbackOnProto) {
7366 v8::HandleScope scope;
7367 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7368 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
7369 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
7370 templ_p->SetAccessor(v8_str("y"), Return239);
7371
7372 LocalContext context;
7373 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
7374 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
7375
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01007376 // Check the case when receiver and interceptor's holder
7377 // are the same objects.
Steve Blocka7e24c12009-10-30 11:49:00 +00007378 v8::Handle<Value> value = CompileRun(
7379 "o.__proto__ = p;"
7380 "var result = 0;"
7381 "for (var i = 0; i < 7; i++) {"
7382 " result = o.x + o.y;"
7383 "}");
7384 CHECK_EQ(239 + 42, value->Int32Value());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01007385
7386 // Check the case when interceptor's holder is in proto chain
7387 // of receiver.
7388 value = CompileRun(
7389 "r = { __proto__: o };"
7390 "var result = 0;"
7391 "for (var i = 0; i < 7; i++) {"
7392 " result = r.x + r.y;"
7393 "}");
7394 CHECK_EQ(239 + 42, value->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00007395}
7396
7397
7398THREADED_TEST(InterceptorLoadICForCallbackWithOverride) {
7399 v8::HandleScope scope;
7400 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7401 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
7402 templ->SetAccessor(v8_str("y"), Return239);
7403
7404 LocalContext context;
7405 context->Global()->Set(v8_str("o"), templ->NewInstance());
7406
7407 v8::Handle<Value> value = CompileRun(
7408 "fst = new Object(); fst.__proto__ = o;"
7409 "snd = new Object(); snd.__proto__ = fst;"
7410 "var result1 = 0;"
7411 "for (var i = 0; i < 7; i++) {"
7412 " result1 = snd.x;"
7413 "}"
7414 "fst.x = 239;"
7415 "var result = 0;"
7416 "for (var i = 0; i < 7; i++) {"
7417 " result = snd.x;"
7418 "}"
7419 "result + result1");
7420 CHECK_EQ(239 + 42, value->Int32Value());
7421}
7422
7423
7424// Test the case when we stored callback into
7425// a stub, but interceptor produced value on its own.
7426THREADED_TEST(InterceptorLoadICCallbackNotNeeded) {
7427 v8::HandleScope scope;
7428 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7429 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
7430 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
7431 templ_p->SetAccessor(v8_str("y"), Return239);
7432
7433 LocalContext context;
7434 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
7435 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
7436
7437 v8::Handle<Value> value = CompileRun(
7438 "o.__proto__ = p;"
7439 "for (var i = 0; i < 7; i++) {"
7440 " o.x;"
7441 // Now it should be ICed and keep a reference to x defined on p
7442 "}"
7443 "var result = 0;"
7444 "for (var i = 0; i < 7; i++) {"
7445 " result += o.x;"
7446 "}"
7447 "result");
7448 CHECK_EQ(42 * 7, value->Int32Value());
7449}
7450
7451
7452// Test the case when we stored callback into
7453// a stub, but it got invalidated later on.
7454THREADED_TEST(InterceptorLoadICInvalidatedCallback) {
7455 v8::HandleScope scope;
7456 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7457 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
7458 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
7459 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis);
7460
7461 LocalContext context;
7462 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
7463 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
7464
7465 v8::Handle<Value> value = CompileRun(
7466 "inbetween = new Object();"
7467 "o.__proto__ = inbetween;"
7468 "inbetween.__proto__ = p;"
7469 "for (var i = 0; i < 10; i++) {"
7470 " o.y;"
7471 // Now it should be ICed and keep a reference to y defined on p
7472 "}"
7473 "inbetween.y = 42;"
7474 "var result = 0;"
7475 "for (var i = 0; i < 10; i++) {"
7476 " result += o.y;"
7477 "}"
7478 "result");
7479 CHECK_EQ(42 * 10, value->Int32Value());
7480}
7481
7482
7483// Test the case when we stored callback into
7484// a stub, but it got invalidated later on due to override on
7485// global object which is between interceptor and callbacks' holders.
7486THREADED_TEST(InterceptorLoadICInvalidatedCallbackViaGlobal) {
7487 v8::HandleScope scope;
7488 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7489 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
7490 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
7491 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis);
7492
7493 LocalContext context;
7494 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
7495 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
7496
7497 v8::Handle<Value> value = CompileRun(
7498 "o.__proto__ = this;"
7499 "this.__proto__ = p;"
7500 "for (var i = 0; i < 10; i++) {"
7501 " if (o.y != 239) throw 'oops: ' + o.y;"
7502 // Now it should be ICed and keep a reference to y defined on p
7503 "}"
7504 "this.y = 42;"
7505 "var result = 0;"
7506 "for (var i = 0; i < 10; i++) {"
7507 " result += o.y;"
7508 "}"
7509 "result");
7510 CHECK_EQ(42 * 10, value->Int32Value());
7511}
7512
7513
7514static v8::Handle<Value> InterceptorLoadICGetter0(Local<String> name,
7515 const AccessorInfo& info) {
7516 ApiTestFuzzer::Fuzz();
7517 CHECK(v8_str("x")->Equals(name));
7518 return v8::Integer::New(0);
7519}
7520
7521
7522THREADED_TEST(InterceptorReturningZero) {
7523 CheckInterceptorLoadIC(InterceptorLoadICGetter0,
7524 "o.x == undefined ? 1 : 0",
7525 0);
7526}
7527
7528
7529static v8::Handle<Value> InterceptorStoreICSetter(
7530 Local<String> key, Local<Value> value, const AccessorInfo&) {
7531 CHECK(v8_str("x")->Equals(key));
7532 CHECK_EQ(42, value->Int32Value());
7533 return value;
7534}
7535
7536
7537// This test should hit the store IC for the interceptor case.
7538THREADED_TEST(InterceptorStoreIC) {
7539 v8::HandleScope scope;
7540 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7541 templ->SetNamedPropertyHandler(InterceptorLoadICGetter,
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -08007542 InterceptorStoreICSetter,
7543 0, 0, 0, v8_str("data"));
Steve Blocka7e24c12009-10-30 11:49:00 +00007544 LocalContext context;
7545 context->Global()->Set(v8_str("o"), templ->NewInstance());
7546 v8::Handle<Value> value = CompileRun(
7547 "for (var i = 0; i < 1000; i++) {"
7548 " o.x = 42;"
7549 "}");
7550}
7551
7552
7553THREADED_TEST(InterceptorStoreICWithNoSetter) {
7554 v8::HandleScope scope;
7555 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7556 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
7557 LocalContext context;
7558 context->Global()->Set(v8_str("o"), templ->NewInstance());
7559 v8::Handle<Value> value = CompileRun(
7560 "for (var i = 0; i < 1000; i++) {"
7561 " o.y = 239;"
7562 "}"
7563 "42 + o.y");
7564 CHECK_EQ(239 + 42, value->Int32Value());
7565}
7566
7567
7568
7569
7570v8::Handle<Value> call_ic_function;
7571v8::Handle<Value> call_ic_function2;
7572v8::Handle<Value> call_ic_function3;
7573
7574static v8::Handle<Value> InterceptorCallICGetter(Local<String> name,
7575 const AccessorInfo& info) {
7576 ApiTestFuzzer::Fuzz();
7577 CHECK(v8_str("x")->Equals(name));
7578 return call_ic_function;
7579}
7580
7581
7582// This test should hit the call IC for the interceptor case.
7583THREADED_TEST(InterceptorCallIC) {
7584 v8::HandleScope scope;
7585 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7586 templ->SetNamedPropertyHandler(InterceptorCallICGetter);
7587 LocalContext context;
7588 context->Global()->Set(v8_str("o"), templ->NewInstance());
7589 call_ic_function =
7590 v8_compile("function f(x) { return x + 1; }; f")->Run();
7591 v8::Handle<Value> value = CompileRun(
7592 "var result = 0;"
7593 "for (var i = 0; i < 1000; i++) {"
7594 " result = o.x(41);"
7595 "}");
7596 CHECK_EQ(42, value->Int32Value());
7597}
7598
7599
7600// This test checks that if interceptor doesn't provide
7601// a value, we can fetch regular value.
7602THREADED_TEST(InterceptorCallICSeesOthers) {
7603 v8::HandleScope scope;
7604 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7605 templ->SetNamedPropertyHandler(NoBlockGetterX);
7606 LocalContext context;
7607 context->Global()->Set(v8_str("o"), templ->NewInstance());
7608 v8::Handle<Value> value = CompileRun(
7609 "o.x = function f(x) { return x + 1; };"
7610 "var result = 0;"
7611 "for (var i = 0; i < 7; i++) {"
7612 " result = o.x(41);"
7613 "}");
7614 CHECK_EQ(42, value->Int32Value());
7615}
7616
7617
7618static v8::Handle<Value> call_ic_function4;
7619static v8::Handle<Value> InterceptorCallICGetter4(Local<String> name,
7620 const AccessorInfo& info) {
7621 ApiTestFuzzer::Fuzz();
7622 CHECK(v8_str("x")->Equals(name));
7623 return call_ic_function4;
7624}
7625
7626
7627// This test checks that if interceptor provides a function,
7628// even if we cached shadowed variant, interceptor's function
7629// is invoked
7630THREADED_TEST(InterceptorCallICCacheableNotNeeded) {
7631 v8::HandleScope scope;
7632 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7633 templ->SetNamedPropertyHandler(InterceptorCallICGetter4);
7634 LocalContext context;
7635 context->Global()->Set(v8_str("o"), templ->NewInstance());
7636 call_ic_function4 =
7637 v8_compile("function f(x) { return x - 1; }; f")->Run();
7638 v8::Handle<Value> value = CompileRun(
7639 "o.__proto__.x = function(x) { return x + 1; };"
7640 "var result = 0;"
7641 "for (var i = 0; i < 1000; i++) {"
7642 " result = o.x(42);"
7643 "}");
7644 CHECK_EQ(41, value->Int32Value());
7645}
7646
7647
7648// Test the case when we stored cacheable lookup into
7649// a stub, but it got invalidated later on
7650THREADED_TEST(InterceptorCallICInvalidatedCacheable) {
7651 v8::HandleScope scope;
7652 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7653 templ->SetNamedPropertyHandler(NoBlockGetterX);
7654 LocalContext context;
7655 context->Global()->Set(v8_str("o"), templ->NewInstance());
7656 v8::Handle<Value> value = CompileRun(
7657 "proto1 = new Object();"
7658 "proto2 = new Object();"
7659 "o.__proto__ = proto1;"
7660 "proto1.__proto__ = proto2;"
7661 "proto2.y = function(x) { return x + 1; };"
7662 // Invoke it many times to compile a stub
7663 "for (var i = 0; i < 7; i++) {"
7664 " o.y(42);"
7665 "}"
7666 "proto1.y = function(x) { return x - 1; };"
7667 "var result = 0;"
7668 "for (var i = 0; i < 7; i++) {"
7669 " result += o.y(42);"
7670 "}");
7671 CHECK_EQ(41 * 7, value->Int32Value());
7672}
7673
7674
7675static v8::Handle<Value> call_ic_function5;
7676static v8::Handle<Value> InterceptorCallICGetter5(Local<String> name,
7677 const AccessorInfo& info) {
7678 ApiTestFuzzer::Fuzz();
7679 if (v8_str("x")->Equals(name))
7680 return call_ic_function5;
7681 else
7682 return Local<Value>();
7683}
7684
7685
7686// This test checks that if interceptor doesn't provide a function,
7687// cached constant function is used
7688THREADED_TEST(InterceptorCallICConstantFunctionUsed) {
7689 v8::HandleScope scope;
7690 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7691 templ->SetNamedPropertyHandler(NoBlockGetterX);
7692 LocalContext context;
7693 context->Global()->Set(v8_str("o"), templ->NewInstance());
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(43, value->Int32Value());
7703}
7704
7705
7706// This test checks that if interceptor provides a function,
7707// even if we cached constant function, interceptor's function
7708// is invoked
7709THREADED_TEST(InterceptorCallICConstantFunctionNotNeeded) {
7710 v8::HandleScope scope;
7711 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7712 templ->SetNamedPropertyHandler(InterceptorCallICGetter5);
7713 LocalContext context;
7714 context->Global()->Set(v8_str("o"), templ->NewInstance());
7715 call_ic_function5 =
7716 v8_compile("function f(x) { return x - 1; }; f")->Run();
7717 v8::Handle<Value> value = CompileRun(
7718 "function inc(x) { return x + 1; };"
7719 "inc(1);"
7720 "o.x = inc;"
7721 "var result = 0;"
7722 "for (var i = 0; i < 1000; i++) {"
7723 " result = o.x(42);"
7724 "}");
7725 CHECK_EQ(41, value->Int32Value());
7726}
7727
7728
7729// Test the case when we stored constant function into
7730// a stub, but it got invalidated later on
7731THREADED_TEST(InterceptorCallICInvalidatedConstantFunction) {
7732 v8::HandleScope scope;
7733 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7734 templ->SetNamedPropertyHandler(NoBlockGetterX);
7735 LocalContext context;
7736 context->Global()->Set(v8_str("o"), templ->NewInstance());
7737 v8::Handle<Value> value = CompileRun(
7738 "function inc(x) { return x + 1; };"
7739 "inc(1);"
7740 "proto1 = new Object();"
7741 "proto2 = new Object();"
7742 "o.__proto__ = proto1;"
7743 "proto1.__proto__ = proto2;"
7744 "proto2.y = inc;"
7745 // Invoke it many times to compile a stub
7746 "for (var i = 0; i < 7; i++) {"
7747 " o.y(42);"
7748 "}"
7749 "proto1.y = function(x) { return x - 1; };"
7750 "var result = 0;"
7751 "for (var i = 0; i < 7; i++) {"
7752 " result += o.y(42);"
7753 "}");
7754 CHECK_EQ(41 * 7, value->Int32Value());
7755}
7756
7757
7758// Test the case when we stored constant function into
7759// a stub, but it got invalidated later on due to override on
7760// global object which is between interceptor and constant function' holders.
7761THREADED_TEST(InterceptorCallICInvalidatedConstantFunctionViaGlobal) {
7762 v8::HandleScope scope;
7763 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7764 templ->SetNamedPropertyHandler(NoBlockGetterX);
7765 LocalContext context;
7766 context->Global()->Set(v8_str("o"), templ->NewInstance());
7767 v8::Handle<Value> value = CompileRun(
7768 "function inc(x) { return x + 1; };"
7769 "inc(1);"
7770 "o.__proto__ = this;"
7771 "this.__proto__.y = inc;"
7772 // Invoke it many times to compile a stub
7773 "for (var i = 0; i < 7; i++) {"
7774 " if (o.y(42) != 43) throw 'oops: ' + o.y(42);"
7775 "}"
7776 "this.y = function(x) { return x - 1; };"
7777 "var result = 0;"
7778 "for (var i = 0; i < 7; i++) {"
7779 " result += o.y(42);"
7780 "}");
7781 CHECK_EQ(41 * 7, value->Int32Value());
7782}
7783
7784
Leon Clarke4515c472010-02-03 11:58:03 +00007785// Test the case when actual function to call sits on global object.
7786THREADED_TEST(InterceptorCallICCachedFromGlobal) {
7787 v8::HandleScope scope;
7788 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7789 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
7790
7791 LocalContext context;
7792 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
7793
7794 v8::Handle<Value> value = CompileRun(
7795 "try {"
7796 " o.__proto__ = this;"
7797 " for (var i = 0; i < 10; i++) {"
7798 " var v = o.parseFloat('239');"
7799 " if (v != 239) throw v;"
7800 // Now it should be ICed and keep a reference to parseFloat.
7801 " }"
7802 " var result = 0;"
7803 " for (var i = 0; i < 10; i++) {"
7804 " result += o.parseFloat('239');"
7805 " }"
7806 " result"
7807 "} catch(e) {"
7808 " e"
7809 "};");
7810 CHECK_EQ(239 * 10, value->Int32Value());
7811}
7812
Andrei Popescu402d9372010-02-26 13:31:12 +00007813static v8::Handle<Value> InterceptorCallICFastApi(Local<String> name,
7814 const AccessorInfo& info) {
7815 ApiTestFuzzer::Fuzz();
7816 int* call_count = reinterpret_cast<int*>(v8::External::Unwrap(info.Data()));
7817 ++(*call_count);
7818 if ((*call_count) % 20 == 0) {
Steve Block44f0eee2011-05-26 01:26:41 +01007819 HEAP->CollectAllGarbage(true);
Andrei Popescu402d9372010-02-26 13:31:12 +00007820 }
7821 return v8::Handle<Value>();
7822}
7823
7824static v8::Handle<Value> FastApiCallback_TrivialSignature(
7825 const v8::Arguments& args) {
7826 ApiTestFuzzer::Fuzz();
7827 CHECK_EQ(args.This(), args.Holder());
7828 CHECK(args.Data()->Equals(v8_str("method_data")));
7829 return v8::Integer::New(args[0]->Int32Value() + 1);
7830}
7831
7832static v8::Handle<Value> FastApiCallback_SimpleSignature(
7833 const v8::Arguments& args) {
7834 ApiTestFuzzer::Fuzz();
7835 CHECK_EQ(args.This()->GetPrototype(), args.Holder());
7836 CHECK(args.Data()->Equals(v8_str("method_data")));
7837 // Note, we're using HasRealNamedProperty instead of Has to avoid
7838 // invoking the interceptor again.
7839 CHECK(args.Holder()->HasRealNamedProperty(v8_str("foo")));
7840 return v8::Integer::New(args[0]->Int32Value() + 1);
7841}
7842
7843// Helper to maximize the odds of object moving.
7844static void GenerateSomeGarbage() {
7845 CompileRun(
7846 "var garbage;"
7847 "for (var i = 0; i < 1000; i++) {"
7848 " garbage = [1/i, \"garbage\" + i, garbage, {foo: garbage}];"
7849 "}"
7850 "garbage = undefined;");
7851}
7852
Ben Murdoche0cee9b2011-05-25 10:26:03 +01007853
Steve Block1e0659c2011-05-24 12:43:12 +01007854v8::Handle<v8::Value> DirectApiCallback(const v8::Arguments& args) {
7855 static int count = 0;
7856 if (count++ % 3 == 0) {
Steve Block44f0eee2011-05-26 01:26:41 +01007857 HEAP-> CollectAllGarbage(true); // This should move the stub
Steve Block1e0659c2011-05-24 12:43:12 +01007858 GenerateSomeGarbage(); // This should ensure the old stub memory is flushed
7859 }
7860 return v8::Handle<v8::Value>();
7861}
7862
7863
7864THREADED_TEST(CallICFastApi_DirectCall_GCMoveStub) {
7865 v8::HandleScope scope;
7866 LocalContext context;
7867 v8::Handle<v8::ObjectTemplate> nativeobject_templ = v8::ObjectTemplate::New();
7868 nativeobject_templ->Set("callback",
7869 v8::FunctionTemplate::New(DirectApiCallback));
7870 v8::Local<v8::Object> nativeobject_obj = nativeobject_templ->NewInstance();
7871 context->Global()->Set(v8_str("nativeobject"), nativeobject_obj);
7872 // call the api function multiple times to ensure direct call stub creation.
7873 CompileRun(
7874 "function f() {"
7875 " for (var i = 1; i <= 30; i++) {"
7876 " nativeobject.callback();"
7877 " }"
7878 "}"
7879 "f();");
7880}
7881
7882
7883v8::Handle<v8::Value> ThrowingDirectApiCallback(const v8::Arguments& args) {
7884 return v8::ThrowException(v8_str("g"));
7885}
7886
7887
7888THREADED_TEST(CallICFastApi_DirectCall_Throw) {
7889 v8::HandleScope scope;
7890 LocalContext context;
7891 v8::Handle<v8::ObjectTemplate> nativeobject_templ = v8::ObjectTemplate::New();
7892 nativeobject_templ->Set("callback",
7893 v8::FunctionTemplate::New(ThrowingDirectApiCallback));
7894 v8::Local<v8::Object> nativeobject_obj = nativeobject_templ->NewInstance();
7895 context->Global()->Set(v8_str("nativeobject"), nativeobject_obj);
7896 // call the api function multiple times to ensure direct call stub creation.
7897 v8::Handle<Value> result = CompileRun(
7898 "var result = '';"
7899 "function f() {"
7900 " for (var i = 1; i <= 5; i++) {"
7901 " try { nativeobject.callback(); } catch (e) { result += e; }"
7902 " }"
7903 "}"
7904 "f(); result;");
7905 CHECK_EQ(v8_str("ggggg"), result);
7906}
7907
7908
Ben Murdoche0cee9b2011-05-25 10:26:03 +01007909v8::Handle<v8::Value> DirectGetterCallback(Local<String> name,
7910 const v8::AccessorInfo& info) {
7911 if (++p_getter_count % 3 == 0) {
Steve Block44f0eee2011-05-26 01:26:41 +01007912 HEAP->CollectAllGarbage(true);
Ben Murdoche0cee9b2011-05-25 10:26:03 +01007913 GenerateSomeGarbage();
7914 }
7915 return v8::Handle<v8::Value>();
7916}
7917
7918
7919THREADED_TEST(LoadICFastApi_DirectCall_GCMoveStub) {
7920 v8::HandleScope scope;
7921 LocalContext context;
7922 v8::Handle<v8::ObjectTemplate> obj = v8::ObjectTemplate::New();
7923 obj->SetAccessor(v8_str("p1"), DirectGetterCallback);
7924 context->Global()->Set(v8_str("o1"), obj->NewInstance());
7925 p_getter_count = 0;
7926 CompileRun(
7927 "function f() {"
7928 " for (var i = 0; i < 30; i++) o1.p1;"
7929 "}"
7930 "f();");
7931 CHECK_EQ(30, p_getter_count);
7932}
7933
7934
7935v8::Handle<v8::Value> ThrowingDirectGetterCallback(
7936 Local<String> name, const v8::AccessorInfo& info) {
7937 return v8::ThrowException(v8_str("g"));
7938}
7939
7940
7941THREADED_TEST(LoadICFastApi_DirectCall_Throw) {
7942 v8::HandleScope scope;
7943 LocalContext context;
7944 v8::Handle<v8::ObjectTemplate> obj = v8::ObjectTemplate::New();
7945 obj->SetAccessor(v8_str("p1"), ThrowingDirectGetterCallback);
7946 context->Global()->Set(v8_str("o1"), obj->NewInstance());
7947 v8::Handle<Value> result = CompileRun(
7948 "var result = '';"
7949 "for (var i = 0; i < 5; i++) {"
7950 " try { o1.p1; } catch (e) { result += e; }"
7951 "}"
7952 "result;");
7953 CHECK_EQ(v8_str("ggggg"), result);
7954}
7955
7956
Andrei Popescu402d9372010-02-26 13:31:12 +00007957THREADED_TEST(InterceptorCallICFastApi_TrivialSignature) {
7958 int interceptor_call_count = 0;
7959 v8::HandleScope scope;
7960 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7961 v8::Handle<v8::FunctionTemplate> method_templ =
7962 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature,
7963 v8_str("method_data"),
7964 v8::Handle<v8::Signature>());
7965 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7966 proto_templ->Set(v8_str("method"), method_templ);
7967 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7968 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
7969 NULL, NULL, NULL, NULL,
7970 v8::External::Wrap(&interceptor_call_count));
7971 LocalContext context;
7972 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7973 GenerateSomeGarbage();
7974 context->Global()->Set(v8_str("o"), fun->NewInstance());
7975 v8::Handle<Value> value = CompileRun(
7976 "var result = 0;"
7977 "for (var i = 0; i < 100; i++) {"
7978 " result = o.method(41);"
7979 "}");
7980 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
7981 CHECK_EQ(100, interceptor_call_count);
7982}
7983
7984THREADED_TEST(InterceptorCallICFastApi_SimpleSignature) {
7985 int interceptor_call_count = 0;
7986 v8::HandleScope scope;
7987 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7988 v8::Handle<v8::FunctionTemplate> method_templ =
7989 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7990 v8_str("method_data"),
7991 v8::Signature::New(fun_templ));
7992 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7993 proto_templ->Set(v8_str("method"), method_templ);
7994 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7995 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
7996 NULL, NULL, NULL, NULL,
7997 v8::External::Wrap(&interceptor_call_count));
7998 LocalContext context;
7999 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
8000 GenerateSomeGarbage();
8001 context->Global()->Set(v8_str("o"), fun->NewInstance());
8002 v8::Handle<Value> value = CompileRun(
8003 "o.foo = 17;"
8004 "var receiver = {};"
8005 "receiver.__proto__ = o;"
8006 "var result = 0;"
8007 "for (var i = 0; i < 100; i++) {"
8008 " result = receiver.method(41);"
8009 "}");
8010 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
8011 CHECK_EQ(100, interceptor_call_count);
8012}
8013
8014THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss1) {
8015 int interceptor_call_count = 0;
8016 v8::HandleScope scope;
8017 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
8018 v8::Handle<v8::FunctionTemplate> method_templ =
8019 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
8020 v8_str("method_data"),
8021 v8::Signature::New(fun_templ));
8022 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
8023 proto_templ->Set(v8_str("method"), method_templ);
8024 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
8025 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
8026 NULL, NULL, NULL, NULL,
8027 v8::External::Wrap(&interceptor_call_count));
8028 LocalContext context;
8029 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
8030 GenerateSomeGarbage();
8031 context->Global()->Set(v8_str("o"), fun->NewInstance());
8032 v8::Handle<Value> value = CompileRun(
8033 "o.foo = 17;"
8034 "var receiver = {};"
8035 "receiver.__proto__ = o;"
8036 "var result = 0;"
8037 "var saved_result = 0;"
8038 "for (var i = 0; i < 100; i++) {"
8039 " result = receiver.method(41);"
8040 " if (i == 50) {"
8041 " saved_result = result;"
8042 " receiver = {method: function(x) { return x - 1 }};"
8043 " }"
8044 "}");
8045 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
8046 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
8047 CHECK_GE(interceptor_call_count, 50);
8048}
8049
8050THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss2) {
8051 int interceptor_call_count = 0;
8052 v8::HandleScope scope;
8053 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
8054 v8::Handle<v8::FunctionTemplate> method_templ =
8055 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
8056 v8_str("method_data"),
8057 v8::Signature::New(fun_templ));
8058 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
8059 proto_templ->Set(v8_str("method"), method_templ);
8060 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
8061 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
8062 NULL, NULL, NULL, NULL,
8063 v8::External::Wrap(&interceptor_call_count));
8064 LocalContext context;
8065 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
8066 GenerateSomeGarbage();
8067 context->Global()->Set(v8_str("o"), fun->NewInstance());
8068 v8::Handle<Value> value = CompileRun(
8069 "o.foo = 17;"
8070 "var receiver = {};"
8071 "receiver.__proto__ = o;"
8072 "var result = 0;"
8073 "var saved_result = 0;"
8074 "for (var i = 0; i < 100; i++) {"
8075 " result = receiver.method(41);"
8076 " if (i == 50) {"
8077 " saved_result = result;"
8078 " o.method = function(x) { return x - 1 };"
8079 " }"
8080 "}");
8081 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
8082 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
8083 CHECK_GE(interceptor_call_count, 50);
8084}
8085
Steve Block6ded16b2010-05-10 14:33:55 +01008086THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss3) {
8087 int interceptor_call_count = 0;
8088 v8::HandleScope scope;
8089 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
8090 v8::Handle<v8::FunctionTemplate> method_templ =
8091 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
8092 v8_str("method_data"),
8093 v8::Signature::New(fun_templ));
8094 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
8095 proto_templ->Set(v8_str("method"), method_templ);
8096 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
8097 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
8098 NULL, NULL, NULL, NULL,
8099 v8::External::Wrap(&interceptor_call_count));
8100 LocalContext context;
8101 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
8102 GenerateSomeGarbage();
8103 context->Global()->Set(v8_str("o"), fun->NewInstance());
8104 v8::TryCatch try_catch;
8105 v8::Handle<Value> value = CompileRun(
8106 "o.foo = 17;"
8107 "var receiver = {};"
8108 "receiver.__proto__ = o;"
8109 "var result = 0;"
8110 "var saved_result = 0;"
8111 "for (var i = 0; i < 100; i++) {"
8112 " result = receiver.method(41);"
8113 " if (i == 50) {"
8114 " saved_result = result;"
8115 " receiver = 333;"
8116 " }"
8117 "}");
8118 CHECK(try_catch.HasCaught());
8119 CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"),
8120 try_catch.Exception()->ToString());
8121 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
8122 CHECK_GE(interceptor_call_count, 50);
8123}
8124
Andrei Popescu402d9372010-02-26 13:31:12 +00008125THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_TypeError) {
8126 int interceptor_call_count = 0;
8127 v8::HandleScope scope;
8128 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
8129 v8::Handle<v8::FunctionTemplate> method_templ =
8130 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
8131 v8_str("method_data"),
8132 v8::Signature::New(fun_templ));
8133 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
8134 proto_templ->Set(v8_str("method"), method_templ);
8135 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
8136 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
8137 NULL, NULL, NULL, NULL,
8138 v8::External::Wrap(&interceptor_call_count));
8139 LocalContext context;
8140 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
8141 GenerateSomeGarbage();
8142 context->Global()->Set(v8_str("o"), fun->NewInstance());
8143 v8::TryCatch try_catch;
8144 v8::Handle<Value> value = CompileRun(
8145 "o.foo = 17;"
8146 "var receiver = {};"
8147 "receiver.__proto__ = o;"
8148 "var result = 0;"
8149 "var saved_result = 0;"
8150 "for (var i = 0; i < 100; i++) {"
8151 " result = receiver.method(41);"
8152 " if (i == 50) {"
8153 " saved_result = result;"
8154 " receiver = {method: receiver.method};"
8155 " }"
8156 "}");
8157 CHECK(try_catch.HasCaught());
8158 CHECK_EQ(v8_str("TypeError: Illegal invocation"),
8159 try_catch.Exception()->ToString());
8160 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
8161 CHECK_GE(interceptor_call_count, 50);
8162}
8163
8164THREADED_TEST(CallICFastApi_TrivialSignature) {
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_TrivialSignature,
8169 v8_str("method_data"),
8170 v8::Handle<v8::Signature>());
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 "var result = 0;"
8180 "for (var i = 0; i < 100; i++) {"
8181 " result = o.method(41);"
8182 "}");
8183
8184 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
8185}
8186
8187THREADED_TEST(CallICFastApi_SimpleSignature) {
8188 v8::HandleScope scope;
8189 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
8190 v8::Handle<v8::FunctionTemplate> method_templ =
8191 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
8192 v8_str("method_data"),
8193 v8::Signature::New(fun_templ));
8194 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
8195 proto_templ->Set(v8_str("method"), method_templ);
8196 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
8197 LocalContext context;
8198 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
8199 GenerateSomeGarbage();
8200 context->Global()->Set(v8_str("o"), fun->NewInstance());
8201 v8::Handle<Value> value = CompileRun(
8202 "o.foo = 17;"
8203 "var receiver = {};"
8204 "receiver.__proto__ = o;"
8205 "var result = 0;"
8206 "for (var i = 0; i < 100; i++) {"
8207 " result = receiver.method(41);"
8208 "}");
8209
8210 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
8211}
8212
Steve Block6ded16b2010-05-10 14:33:55 +01008213THREADED_TEST(CallICFastApi_SimpleSignature_Miss1) {
Andrei Popescu402d9372010-02-26 13:31:12 +00008214 v8::HandleScope scope;
8215 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
8216 v8::Handle<v8::FunctionTemplate> method_templ =
8217 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
8218 v8_str("method_data"),
8219 v8::Signature::New(fun_templ));
8220 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
8221 proto_templ->Set(v8_str("method"), method_templ);
8222 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
8223 LocalContext context;
8224 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
8225 GenerateSomeGarbage();
8226 context->Global()->Set(v8_str("o"), fun->NewInstance());
8227 v8::Handle<Value> value = CompileRun(
8228 "o.foo = 17;"
8229 "var receiver = {};"
8230 "receiver.__proto__ = o;"
8231 "var result = 0;"
8232 "var saved_result = 0;"
8233 "for (var i = 0; i < 100; i++) {"
8234 " result = receiver.method(41);"
8235 " if (i == 50) {"
8236 " saved_result = result;"
8237 " receiver = {method: function(x) { return x - 1 }};"
8238 " }"
8239 "}");
8240 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
8241 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
8242}
8243
Steve Block6ded16b2010-05-10 14:33:55 +01008244THREADED_TEST(CallICFastApi_SimpleSignature_Miss2) {
8245 v8::HandleScope scope;
8246 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
8247 v8::Handle<v8::FunctionTemplate> method_templ =
8248 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
8249 v8_str("method_data"),
8250 v8::Signature::New(fun_templ));
8251 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
8252 proto_templ->Set(v8_str("method"), method_templ);
8253 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
8254 LocalContext context;
8255 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
8256 GenerateSomeGarbage();
8257 context->Global()->Set(v8_str("o"), fun->NewInstance());
8258 v8::TryCatch try_catch;
8259 v8::Handle<Value> value = CompileRun(
8260 "o.foo = 17;"
8261 "var receiver = {};"
8262 "receiver.__proto__ = o;"
8263 "var result = 0;"
8264 "var saved_result = 0;"
8265 "for (var i = 0; i < 100; i++) {"
8266 " result = receiver.method(41);"
8267 " if (i == 50) {"
8268 " saved_result = result;"
8269 " receiver = 333;"
8270 " }"
8271 "}");
8272 CHECK(try_catch.HasCaught());
8273 CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"),
8274 try_catch.Exception()->ToString());
8275 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
8276}
8277
Leon Clarke4515c472010-02-03 11:58:03 +00008278
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008279v8::Handle<Value> keyed_call_ic_function;
8280
8281static v8::Handle<Value> InterceptorKeyedCallICGetter(
8282 Local<String> name, const AccessorInfo& info) {
8283 ApiTestFuzzer::Fuzz();
8284 if (v8_str("x")->Equals(name)) {
8285 return keyed_call_ic_function;
8286 }
8287 return v8::Handle<Value>();
8288}
8289
8290
8291// Test the case when we stored cacheable lookup into
8292// a stub, but the function name changed (to another cacheable function).
8293THREADED_TEST(InterceptorKeyedCallICKeyChange1) {
8294 v8::HandleScope scope;
8295 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
8296 templ->SetNamedPropertyHandler(NoBlockGetterX);
8297 LocalContext context;
8298 context->Global()->Set(v8_str("o"), templ->NewInstance());
8299 v8::Handle<Value> value = CompileRun(
8300 "proto = new Object();"
8301 "proto.y = function(x) { return x + 1; };"
8302 "proto.z = function(x) { return x - 1; };"
8303 "o.__proto__ = proto;"
8304 "var result = 0;"
8305 "var method = 'y';"
8306 "for (var i = 0; i < 10; i++) {"
8307 " if (i == 5) { method = 'z'; };"
8308 " result += o[method](41);"
8309 "}");
8310 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
8311}
8312
8313
8314// Test the case when we stored cacheable lookup into
8315// a stub, but the function name changed (and the new function is present
8316// both before and after the interceptor in the prototype chain).
8317THREADED_TEST(InterceptorKeyedCallICKeyChange2) {
8318 v8::HandleScope scope;
8319 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
8320 templ->SetNamedPropertyHandler(InterceptorKeyedCallICGetter);
8321 LocalContext context;
8322 context->Global()->Set(v8_str("proto1"), templ->NewInstance());
8323 keyed_call_ic_function =
8324 v8_compile("function f(x) { return x - 1; }; f")->Run();
8325 v8::Handle<Value> value = CompileRun(
8326 "o = new Object();"
8327 "proto2 = new Object();"
8328 "o.y = function(x) { return x + 1; };"
8329 "proto2.y = function(x) { return x + 2; };"
8330 "o.__proto__ = proto1;"
8331 "proto1.__proto__ = proto2;"
8332 "var result = 0;"
8333 "var method = 'x';"
8334 "for (var i = 0; i < 10; i++) {"
8335 " if (i == 5) { method = 'y'; };"
8336 " result += o[method](41);"
8337 "}");
8338 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
8339}
8340
8341
8342// Same as InterceptorKeyedCallICKeyChange1 only the cacheable function sit
8343// on the global object.
8344THREADED_TEST(InterceptorKeyedCallICKeyChangeOnGlobal) {
8345 v8::HandleScope scope;
8346 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
8347 templ->SetNamedPropertyHandler(NoBlockGetterX);
8348 LocalContext context;
8349 context->Global()->Set(v8_str("o"), templ->NewInstance());
8350 v8::Handle<Value> value = CompileRun(
8351 "function inc(x) { return x + 1; };"
8352 "inc(1);"
8353 "function dec(x) { return x - 1; };"
8354 "dec(1);"
8355 "o.__proto__ = this;"
8356 "this.__proto__.x = inc;"
8357 "this.__proto__.y = dec;"
8358 "var result = 0;"
8359 "var method = 'x';"
8360 "for (var i = 0; i < 10; i++) {"
8361 " if (i == 5) { method = 'y'; };"
8362 " result += o[method](41);"
8363 "}");
8364 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
8365}
8366
8367
8368// Test the case when actual function to call sits on global object.
8369THREADED_TEST(InterceptorKeyedCallICFromGlobal) {
8370 v8::HandleScope scope;
8371 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
8372 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
8373 LocalContext context;
8374 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
8375
8376 v8::Handle<Value> value = CompileRun(
8377 "function len(x) { return x.length; };"
8378 "o.__proto__ = this;"
8379 "var m = 'parseFloat';"
8380 "var result = 0;"
8381 "for (var i = 0; i < 10; i++) {"
8382 " if (i == 5) {"
8383 " m = 'len';"
8384 " saved_result = result;"
8385 " };"
8386 " result = o[m]('239');"
8387 "}");
8388 CHECK_EQ(3, context->Global()->Get(v8_str("result"))->Int32Value());
8389 CHECK_EQ(239, context->Global()->Get(v8_str("saved_result"))->Int32Value());
8390}
8391
8392// Test the map transition before the interceptor.
8393THREADED_TEST(InterceptorKeyedCallICMapChangeBefore) {
8394 v8::HandleScope scope;
8395 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
8396 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
8397 LocalContext context;
8398 context->Global()->Set(v8_str("proto"), templ_o->NewInstance());
8399
8400 v8::Handle<Value> value = CompileRun(
8401 "var o = new Object();"
8402 "o.__proto__ = proto;"
8403 "o.method = function(x) { return x + 1; };"
8404 "var m = 'method';"
8405 "var result = 0;"
8406 "for (var i = 0; i < 10; i++) {"
8407 " if (i == 5) { o.method = function(x) { return x - 1; }; };"
8408 " result += o[m](41);"
8409 "}");
8410 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
8411}
8412
8413
8414// Test the map transition after the interceptor.
8415THREADED_TEST(InterceptorKeyedCallICMapChangeAfter) {
8416 v8::HandleScope scope;
8417 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
8418 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
8419 LocalContext context;
8420 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
8421
8422 v8::Handle<Value> value = CompileRun(
8423 "var proto = new Object();"
8424 "o.__proto__ = proto;"
8425 "proto.method = function(x) { return x + 1; };"
8426 "var m = 'method';"
8427 "var result = 0;"
8428 "for (var i = 0; i < 10; i++) {"
8429 " if (i == 5) { proto.method = function(x) { return x - 1; }; };"
8430 " result += o[m](41);"
8431 "}");
8432 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
8433}
8434
8435
Steve Blocka7e24c12009-10-30 11:49:00 +00008436static int interceptor_call_count = 0;
8437
8438static v8::Handle<Value> InterceptorICRefErrorGetter(Local<String> name,
8439 const AccessorInfo& info) {
8440 ApiTestFuzzer::Fuzz();
8441 if (v8_str("x")->Equals(name) && interceptor_call_count++ < 20) {
8442 return call_ic_function2;
8443 }
8444 return v8::Handle<Value>();
8445}
8446
8447
8448// This test should hit load and call ICs for the interceptor case.
8449// Once in a while, the interceptor will reply that a property was not
8450// found in which case we should get a reference error.
8451THREADED_TEST(InterceptorICReferenceErrors) {
8452 v8::HandleScope scope;
8453 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
8454 templ->SetNamedPropertyHandler(InterceptorICRefErrorGetter);
8455 LocalContext context(0, templ, v8::Handle<Value>());
8456 call_ic_function2 = v8_compile("function h(x) { return x; }; h")->Run();
8457 v8::Handle<Value> value = CompileRun(
8458 "function f() {"
8459 " for (var i = 0; i < 1000; i++) {"
8460 " try { x; } catch(e) { return true; }"
8461 " }"
8462 " return false;"
8463 "};"
8464 "f();");
8465 CHECK_EQ(true, value->BooleanValue());
8466 interceptor_call_count = 0;
8467 value = CompileRun(
8468 "function g() {"
8469 " for (var i = 0; i < 1000; i++) {"
8470 " try { x(42); } catch(e) { return true; }"
8471 " }"
8472 " return false;"
8473 "};"
8474 "g();");
8475 CHECK_EQ(true, value->BooleanValue());
8476}
8477
8478
8479static int interceptor_ic_exception_get_count = 0;
8480
8481static v8::Handle<Value> InterceptorICExceptionGetter(
8482 Local<String> name,
8483 const AccessorInfo& info) {
8484 ApiTestFuzzer::Fuzz();
8485 if (v8_str("x")->Equals(name) && ++interceptor_ic_exception_get_count < 20) {
8486 return call_ic_function3;
8487 }
8488 if (interceptor_ic_exception_get_count == 20) {
8489 return v8::ThrowException(v8_num(42));
8490 }
8491 // Do not handle get for properties other than x.
8492 return v8::Handle<Value>();
8493}
8494
8495// Test interceptor load/call IC where the interceptor throws an
8496// exception once in a while.
8497THREADED_TEST(InterceptorICGetterExceptions) {
8498 interceptor_ic_exception_get_count = 0;
8499 v8::HandleScope scope;
8500 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
8501 templ->SetNamedPropertyHandler(InterceptorICExceptionGetter);
8502 LocalContext context(0, templ, v8::Handle<Value>());
8503 call_ic_function3 = v8_compile("function h(x) { return x; }; h")->Run();
8504 v8::Handle<Value> value = CompileRun(
8505 "function f() {"
8506 " for (var i = 0; i < 100; i++) {"
8507 " try { x; } catch(e) { return true; }"
8508 " }"
8509 " return false;"
8510 "};"
8511 "f();");
8512 CHECK_EQ(true, value->BooleanValue());
8513 interceptor_ic_exception_get_count = 0;
8514 value = CompileRun(
8515 "function f() {"
8516 " for (var i = 0; i < 100; i++) {"
8517 " try { x(42); } catch(e) { return true; }"
8518 " }"
8519 " return false;"
8520 "};"
8521 "f();");
8522 CHECK_EQ(true, value->BooleanValue());
8523}
8524
8525
8526static int interceptor_ic_exception_set_count = 0;
8527
8528static v8::Handle<Value> InterceptorICExceptionSetter(
8529 Local<String> key, Local<Value> value, const AccessorInfo&) {
8530 ApiTestFuzzer::Fuzz();
8531 if (++interceptor_ic_exception_set_count > 20) {
8532 return v8::ThrowException(v8_num(42));
8533 }
8534 // Do not actually handle setting.
8535 return v8::Handle<Value>();
8536}
8537
8538// Test interceptor store IC where the interceptor throws an exception
8539// once in a while.
8540THREADED_TEST(InterceptorICSetterExceptions) {
8541 interceptor_ic_exception_set_count = 0;
8542 v8::HandleScope scope;
8543 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
8544 templ->SetNamedPropertyHandler(0, InterceptorICExceptionSetter);
8545 LocalContext context(0, templ, v8::Handle<Value>());
8546 v8::Handle<Value> value = CompileRun(
8547 "function f() {"
8548 " for (var i = 0; i < 100; i++) {"
8549 " try { x = 42; } catch(e) { return true; }"
8550 " }"
8551 " return false;"
8552 "};"
8553 "f();");
8554 CHECK_EQ(true, value->BooleanValue());
8555}
8556
8557
8558// Test that we ignore null interceptors.
8559THREADED_TEST(NullNamedInterceptor) {
8560 v8::HandleScope scope;
8561 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
8562 templ->SetNamedPropertyHandler(0);
8563 LocalContext context;
8564 templ->Set("x", v8_num(42));
8565 v8::Handle<v8::Object> obj = templ->NewInstance();
8566 context->Global()->Set(v8_str("obj"), obj);
8567 v8::Handle<Value> value = CompileRun("obj.x");
8568 CHECK(value->IsInt32());
8569 CHECK_EQ(42, value->Int32Value());
8570}
8571
8572
8573// Test that we ignore null interceptors.
8574THREADED_TEST(NullIndexedInterceptor) {
8575 v8::HandleScope scope;
8576 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
8577 templ->SetIndexedPropertyHandler(0);
8578 LocalContext context;
8579 templ->Set("42", v8_num(42));
8580 v8::Handle<v8::Object> obj = templ->NewInstance();
8581 context->Global()->Set(v8_str("obj"), obj);
8582 v8::Handle<Value> value = CompileRun("obj[42]");
8583 CHECK(value->IsInt32());
8584 CHECK_EQ(42, value->Int32Value());
8585}
8586
8587
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008588THREADED_TEST(NamedPropertyHandlerGetterAttributes) {
8589 v8::HandleScope scope;
8590 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
8591 templ->InstanceTemplate()->SetNamedPropertyHandler(InterceptorLoadXICGetter);
8592 LocalContext env;
8593 env->Global()->Set(v8_str("obj"),
8594 templ->GetFunction()->NewInstance());
8595 ExpectTrue("obj.x === 42");
8596 ExpectTrue("!obj.propertyIsEnumerable('x')");
8597}
8598
8599
Ben Murdoch8b112d22011-06-08 16:22:53 +01008600static Handle<Value> ThrowingGetter(Local<String> name,
8601 const AccessorInfo& info) {
8602 ApiTestFuzzer::Fuzz();
8603 ThrowException(Handle<Value>());
8604 return Undefined();
8605}
8606
8607
8608THREADED_TEST(VariousGetPropertiesAndThrowingCallbacks) {
8609 HandleScope scope;
8610 LocalContext context;
8611
8612 Local<FunctionTemplate> templ = FunctionTemplate::New();
8613 Local<ObjectTemplate> instance_templ = templ->InstanceTemplate();
8614 instance_templ->SetAccessor(v8_str("f"), ThrowingGetter);
8615
8616 Local<Object> instance = templ->GetFunction()->NewInstance();
8617
8618 Local<Object> another = Object::New();
8619 another->SetPrototype(instance);
8620
8621 Local<Object> with_js_getter = CompileRun(
8622 "o = {};\n"
8623 "o.__defineGetter__('f', function() { throw undefined; });\n"
8624 "o\n").As<Object>();
8625 CHECK(!with_js_getter.IsEmpty());
8626
8627 TryCatch try_catch;
8628
8629 Local<Value> result = instance->GetRealNamedProperty(v8_str("f"));
8630 CHECK(try_catch.HasCaught());
8631 try_catch.Reset();
8632 CHECK(result.IsEmpty());
8633
8634 result = another->GetRealNamedProperty(v8_str("f"));
8635 CHECK(try_catch.HasCaught());
8636 try_catch.Reset();
8637 CHECK(result.IsEmpty());
8638
8639 result = another->GetRealNamedPropertyInPrototypeChain(v8_str("f"));
8640 CHECK(try_catch.HasCaught());
8641 try_catch.Reset();
8642 CHECK(result.IsEmpty());
8643
8644 result = another->Get(v8_str("f"));
8645 CHECK(try_catch.HasCaught());
8646 try_catch.Reset();
8647 CHECK(result.IsEmpty());
8648
8649 result = with_js_getter->GetRealNamedProperty(v8_str("f"));
8650 CHECK(try_catch.HasCaught());
8651 try_catch.Reset();
8652 CHECK(result.IsEmpty());
8653
8654 result = with_js_getter->Get(v8_str("f"));
8655 CHECK(try_catch.HasCaught());
8656 try_catch.Reset();
8657 CHECK(result.IsEmpty());
8658}
8659
8660
8661static Handle<Value> ThrowingCallbackWithTryCatch(const Arguments& args) {
8662 TryCatch try_catch;
8663 // Verboseness is important: it triggers message delivery which can call into
8664 // external code.
8665 try_catch.SetVerbose(true);
8666 CompileRun("throw 'from JS';");
8667 CHECK(try_catch.HasCaught());
8668 CHECK(!i::Isolate::Current()->has_pending_exception());
8669 CHECK(!i::Isolate::Current()->has_scheduled_exception());
8670 return Undefined();
8671}
8672
8673
8674static int call_depth;
8675
8676
8677static void WithTryCatch(Handle<Message> message, Handle<Value> data) {
8678 TryCatch try_catch;
8679}
8680
8681
8682static void ThrowFromJS(Handle<Message> message, Handle<Value> data) {
8683 if (--call_depth) CompileRun("throw 'ThrowInJS';");
8684}
8685
8686
8687static void ThrowViaApi(Handle<Message> message, Handle<Value> data) {
8688 if (--call_depth) ThrowException(v8_str("ThrowViaApi"));
8689}
8690
8691
8692static void WebKitLike(Handle<Message> message, Handle<Value> data) {
8693 Handle<String> errorMessageString = message->Get();
8694 CHECK(!errorMessageString.IsEmpty());
8695 message->GetStackTrace();
8696 message->GetScriptResourceName();
8697}
8698
8699THREADED_TEST(ExceptionsDoNotPropagatePastTryCatch) {
8700 HandleScope scope;
8701 LocalContext context;
8702
8703 Local<Function> func =
8704 FunctionTemplate::New(ThrowingCallbackWithTryCatch)->GetFunction();
8705 context->Global()->Set(v8_str("func"), func);
8706
8707 MessageCallback callbacks[] =
8708 { NULL, WebKitLike, ThrowViaApi, ThrowFromJS, WithTryCatch };
8709 for (unsigned i = 0; i < sizeof(callbacks)/sizeof(callbacks[0]); i++) {
8710 MessageCallback callback = callbacks[i];
8711 if (callback != NULL) {
8712 V8::AddMessageListener(callback);
8713 }
8714 call_depth = 5;
8715 ExpectFalse(
8716 "var thrown = false;\n"
8717 "try { func(); } catch(e) { thrown = true; }\n"
8718 "thrown\n");
8719 if (callback != NULL) {
8720 V8::RemoveMessageListeners(callback);
8721 }
8722 }
8723}
8724
8725
Steve Blocka7e24c12009-10-30 11:49:00 +00008726static v8::Handle<Value> ParentGetter(Local<String> name,
8727 const AccessorInfo& info) {
8728 ApiTestFuzzer::Fuzz();
8729 return v8_num(1);
8730}
8731
8732
8733static v8::Handle<Value> ChildGetter(Local<String> name,
8734 const AccessorInfo& info) {
8735 ApiTestFuzzer::Fuzz();
8736 return v8_num(42);
8737}
8738
8739
8740THREADED_TEST(Overriding) {
8741 v8::HandleScope scope;
8742 LocalContext context;
8743
8744 // Parent template.
8745 Local<v8::FunctionTemplate> parent_templ = v8::FunctionTemplate::New();
8746 Local<ObjectTemplate> parent_instance_templ =
8747 parent_templ->InstanceTemplate();
8748 parent_instance_templ->SetAccessor(v8_str("f"), ParentGetter);
8749
8750 // Template that inherits from the parent template.
8751 Local<v8::FunctionTemplate> child_templ = v8::FunctionTemplate::New();
8752 Local<ObjectTemplate> child_instance_templ =
8753 child_templ->InstanceTemplate();
8754 child_templ->Inherit(parent_templ);
8755 // Override 'f'. The child version of 'f' should get called for child
8756 // instances.
8757 child_instance_templ->SetAccessor(v8_str("f"), ChildGetter);
8758 // Add 'g' twice. The 'g' added last should get called for instances.
8759 child_instance_templ->SetAccessor(v8_str("g"), ParentGetter);
8760 child_instance_templ->SetAccessor(v8_str("g"), ChildGetter);
8761
8762 // Add 'h' as an accessor to the proto template with ReadOnly attributes
8763 // so 'h' can be shadowed on the instance object.
8764 Local<ObjectTemplate> child_proto_templ = child_templ->PrototypeTemplate();
8765 child_proto_templ->SetAccessor(v8_str("h"), ParentGetter, 0,
8766 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly);
8767
8768 // Add 'i' as an accessor to the instance template with ReadOnly attributes
8769 // but the attribute does not have effect because it is duplicated with
8770 // NULL setter.
8771 child_instance_templ->SetAccessor(v8_str("i"), ChildGetter, 0,
8772 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly);
8773
8774
8775
8776 // Instantiate the child template.
8777 Local<v8::Object> instance = child_templ->GetFunction()->NewInstance();
8778
8779 // Check that the child function overrides the parent one.
8780 context->Global()->Set(v8_str("o"), instance);
8781 Local<Value> value = v8_compile("o.f")->Run();
8782 // Check that the 'g' that was added last is hit.
8783 CHECK_EQ(42, value->Int32Value());
8784 value = v8_compile("o.g")->Run();
8785 CHECK_EQ(42, value->Int32Value());
8786
8787 // Check 'h' can be shadowed.
8788 value = v8_compile("o.h = 3; o.h")->Run();
8789 CHECK_EQ(3, value->Int32Value());
8790
8791 // Check 'i' is cannot be shadowed or changed.
8792 value = v8_compile("o.i = 3; o.i")->Run();
8793 CHECK_EQ(42, value->Int32Value());
8794}
8795
8796
8797static v8::Handle<Value> IsConstructHandler(const v8::Arguments& args) {
8798 ApiTestFuzzer::Fuzz();
8799 if (args.IsConstructCall()) {
8800 return v8::Boolean::New(true);
8801 }
8802 return v8::Boolean::New(false);
8803}
8804
8805
8806THREADED_TEST(IsConstructCall) {
8807 v8::HandleScope scope;
8808
8809 // Function template with call handler.
8810 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
8811 templ->SetCallHandler(IsConstructHandler);
8812
8813 LocalContext context;
8814
8815 context->Global()->Set(v8_str("f"), templ->GetFunction());
8816 Local<Value> value = v8_compile("f()")->Run();
8817 CHECK(!value->BooleanValue());
8818 value = v8_compile("new f()")->Run();
8819 CHECK(value->BooleanValue());
8820}
8821
8822
8823THREADED_TEST(ObjectProtoToString) {
8824 v8::HandleScope scope;
8825 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
8826 templ->SetClassName(v8_str("MyClass"));
8827
8828 LocalContext context;
8829
8830 Local<String> customized_tostring = v8_str("customized toString");
8831
8832 // Replace Object.prototype.toString
8833 v8_compile("Object.prototype.toString = function() {"
8834 " return 'customized toString';"
8835 "}")->Run();
8836
8837 // Normal ToString call should call replaced Object.prototype.toString
8838 Local<v8::Object> instance = templ->GetFunction()->NewInstance();
8839 Local<String> value = instance->ToString();
8840 CHECK(value->IsString() && value->Equals(customized_tostring));
8841
8842 // ObjectProtoToString should not call replace toString function.
8843 value = instance->ObjectProtoToString();
8844 CHECK(value->IsString() && value->Equals(v8_str("[object MyClass]")));
8845
8846 // Check global
8847 value = context->Global()->ObjectProtoToString();
8848 CHECK(value->IsString() && value->Equals(v8_str("[object global]")));
8849
8850 // Check ordinary object
8851 Local<Value> object = v8_compile("new Object()")->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01008852 value = object.As<v8::Object>()->ObjectProtoToString();
Steve Blocka7e24c12009-10-30 11:49:00 +00008853 CHECK(value->IsString() && value->Equals(v8_str("[object Object]")));
8854}
8855
8856
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -08008857THREADED_TEST(ObjectGetConstructorName) {
8858 v8::HandleScope scope;
8859 LocalContext context;
8860 v8_compile("function Parent() {};"
8861 "function Child() {};"
8862 "Child.prototype = new Parent();"
8863 "var outer = { inner: function() { } };"
8864 "var p = new Parent();"
8865 "var c = new Child();"
8866 "var x = new outer.inner();")->Run();
8867
8868 Local<v8::Value> p = context->Global()->Get(v8_str("p"));
8869 CHECK(p->IsObject() && p->ToObject()->GetConstructorName()->Equals(
8870 v8_str("Parent")));
8871
8872 Local<v8::Value> c = context->Global()->Get(v8_str("c"));
8873 CHECK(c->IsObject() && c->ToObject()->GetConstructorName()->Equals(
8874 v8_str("Child")));
8875
8876 Local<v8::Value> x = context->Global()->Get(v8_str("x"));
8877 CHECK(x->IsObject() && x->ToObject()->GetConstructorName()->Equals(
8878 v8_str("outer.inner")));
8879}
8880
8881
Steve Blocka7e24c12009-10-30 11:49:00 +00008882bool ApiTestFuzzer::fuzzing_ = false;
Steve Block8defd9f2010-07-08 12:39:36 +01008883i::Semaphore* ApiTestFuzzer::all_tests_done_=
8884 i::OS::CreateSemaphore(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00008885int ApiTestFuzzer::active_tests_;
8886int ApiTestFuzzer::tests_being_run_;
8887int ApiTestFuzzer::current_;
8888
8889
8890// We are in a callback and want to switch to another thread (if we
8891// are currently running the thread fuzzing test).
8892void ApiTestFuzzer::Fuzz() {
8893 if (!fuzzing_) return;
8894 ApiTestFuzzer* test = RegisterThreadedTest::nth(current_)->fuzzer_;
8895 test->ContextSwitch();
8896}
8897
8898
8899// Let the next thread go. Since it is also waiting on the V8 lock it may
8900// not start immediately.
8901bool ApiTestFuzzer::NextThread() {
8902 int test_position = GetNextTestNumber();
Steve Blockd0582a62009-12-15 09:54:21 +00008903 const char* test_name = RegisterThreadedTest::nth(current_)->name();
Steve Blocka7e24c12009-10-30 11:49:00 +00008904 if (test_position == current_) {
Steve Blockd0582a62009-12-15 09:54:21 +00008905 if (kLogThreading)
8906 printf("Stay with %s\n", test_name);
Steve Blocka7e24c12009-10-30 11:49:00 +00008907 return false;
8908 }
Steve Blockd0582a62009-12-15 09:54:21 +00008909 if (kLogThreading) {
8910 printf("Switch from %s to %s\n",
8911 test_name,
8912 RegisterThreadedTest::nth(test_position)->name());
8913 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008914 current_ = test_position;
8915 RegisterThreadedTest::nth(current_)->fuzzer_->gate_->Signal();
8916 return true;
8917}
8918
8919
8920void ApiTestFuzzer::Run() {
8921 // When it is our turn...
8922 gate_->Wait();
8923 {
8924 // ... get the V8 lock and start running the test.
8925 v8::Locker locker;
8926 CallTest();
8927 }
8928 // This test finished.
8929 active_ = false;
8930 active_tests_--;
8931 // If it was the last then signal that fact.
8932 if (active_tests_ == 0) {
8933 all_tests_done_->Signal();
8934 } else {
8935 // Otherwise select a new test and start that.
8936 NextThread();
8937 }
8938}
8939
8940
8941static unsigned linear_congruential_generator;
8942
8943
8944void ApiTestFuzzer::Setup(PartOfTest part) {
8945 linear_congruential_generator = i::FLAG_testing_prng_seed;
8946 fuzzing_ = true;
8947 int start = (part == FIRST_PART) ? 0 : (RegisterThreadedTest::count() >> 1);
8948 int end = (part == FIRST_PART)
8949 ? (RegisterThreadedTest::count() >> 1)
8950 : RegisterThreadedTest::count();
8951 active_tests_ = tests_being_run_ = end - start;
8952 for (int i = 0; i < tests_being_run_; i++) {
Steve Block44f0eee2011-05-26 01:26:41 +01008953 RegisterThreadedTest::nth(i)->fuzzer_ = new ApiTestFuzzer(
8954 i::Isolate::Current(), i + start);
Steve Blocka7e24c12009-10-30 11:49:00 +00008955 }
8956 for (int i = 0; i < active_tests_; i++) {
8957 RegisterThreadedTest::nth(i)->fuzzer_->Start();
8958 }
8959}
8960
8961
8962static void CallTestNumber(int test_number) {
8963 (RegisterThreadedTest::nth(test_number)->callback())();
8964}
8965
8966
8967void ApiTestFuzzer::RunAllTests() {
8968 // Set off the first test.
8969 current_ = -1;
8970 NextThread();
8971 // Wait till they are all done.
8972 all_tests_done_->Wait();
8973}
8974
8975
8976int ApiTestFuzzer::GetNextTestNumber() {
8977 int next_test;
8978 do {
8979 next_test = (linear_congruential_generator >> 16) % tests_being_run_;
8980 linear_congruential_generator *= 1664525u;
8981 linear_congruential_generator += 1013904223u;
8982 } while (!RegisterThreadedTest::nth(next_test)->fuzzer_->active_);
8983 return next_test;
8984}
8985
8986
8987void ApiTestFuzzer::ContextSwitch() {
8988 // If the new thread is the same as the current thread there is nothing to do.
8989 if (NextThread()) {
8990 // Now it can start.
8991 v8::Unlocker unlocker;
8992 // Wait till someone starts us again.
8993 gate_->Wait();
8994 // And we're off.
8995 }
8996}
8997
8998
8999void ApiTestFuzzer::TearDown() {
9000 fuzzing_ = false;
9001 for (int i = 0; i < RegisterThreadedTest::count(); i++) {
9002 ApiTestFuzzer *fuzzer = RegisterThreadedTest::nth(i)->fuzzer_;
9003 if (fuzzer != NULL) fuzzer->Join();
9004 }
9005}
9006
9007
9008// Lets not be needlessly self-referential.
9009TEST(Threading) {
9010 ApiTestFuzzer::Setup(ApiTestFuzzer::FIRST_PART);
9011 ApiTestFuzzer::RunAllTests();
9012 ApiTestFuzzer::TearDown();
9013}
9014
9015TEST(Threading2) {
9016 ApiTestFuzzer::Setup(ApiTestFuzzer::SECOND_PART);
9017 ApiTestFuzzer::RunAllTests();
9018 ApiTestFuzzer::TearDown();
9019}
9020
9021
9022void ApiTestFuzzer::CallTest() {
Steve Blockd0582a62009-12-15 09:54:21 +00009023 if (kLogThreading)
9024 printf("Start test %d\n", test_number_);
Steve Blocka7e24c12009-10-30 11:49:00 +00009025 CallTestNumber(test_number_);
Steve Blockd0582a62009-12-15 09:54:21 +00009026 if (kLogThreading)
9027 printf("End test %d\n", test_number_);
Steve Blocka7e24c12009-10-30 11:49:00 +00009028}
9029
9030
9031static v8::Handle<Value> ThrowInJS(const v8::Arguments& args) {
9032 CHECK(v8::Locker::IsLocked());
9033 ApiTestFuzzer::Fuzz();
9034 v8::Unlocker unlocker;
9035 const char* code = "throw 7;";
9036 {
9037 v8::Locker nested_locker;
9038 v8::HandleScope scope;
9039 v8::Handle<Value> exception;
9040 { v8::TryCatch try_catch;
9041 v8::Handle<Value> value = CompileRun(code);
9042 CHECK(value.IsEmpty());
9043 CHECK(try_catch.HasCaught());
9044 // Make sure to wrap the exception in a new handle because
9045 // the handle returned from the TryCatch is destroyed
9046 // when the TryCatch is destroyed.
9047 exception = Local<Value>::New(try_catch.Exception());
9048 }
9049 return v8::ThrowException(exception);
9050 }
9051}
9052
9053
9054static v8::Handle<Value> ThrowInJSNoCatch(const v8::Arguments& args) {
9055 CHECK(v8::Locker::IsLocked());
9056 ApiTestFuzzer::Fuzz();
9057 v8::Unlocker unlocker;
9058 const char* code = "throw 7;";
9059 {
9060 v8::Locker nested_locker;
9061 v8::HandleScope scope;
9062 v8::Handle<Value> value = CompileRun(code);
9063 CHECK(value.IsEmpty());
9064 return v8_str("foo");
9065 }
9066}
9067
9068
9069// These are locking tests that don't need to be run again
9070// as part of the locking aggregation tests.
9071TEST(NestedLockers) {
9072 v8::Locker locker;
9073 CHECK(v8::Locker::IsLocked());
9074 v8::HandleScope scope;
9075 LocalContext env;
9076 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(ThrowInJS);
9077 Local<Function> fun = fun_templ->GetFunction();
9078 env->Global()->Set(v8_str("throw_in_js"), fun);
9079 Local<Script> script = v8_compile("(function () {"
9080 " try {"
9081 " throw_in_js();"
9082 " return 42;"
9083 " } catch (e) {"
9084 " return e * 13;"
9085 " }"
9086 "})();");
9087 CHECK_EQ(91, script->Run()->Int32Value());
9088}
9089
9090
9091// These are locking tests that don't need to be run again
9092// as part of the locking aggregation tests.
9093TEST(NestedLockersNoTryCatch) {
9094 v8::Locker locker;
9095 v8::HandleScope scope;
9096 LocalContext env;
9097 Local<v8::FunctionTemplate> fun_templ =
9098 v8::FunctionTemplate::New(ThrowInJSNoCatch);
9099 Local<Function> fun = fun_templ->GetFunction();
9100 env->Global()->Set(v8_str("throw_in_js"), fun);
9101 Local<Script> script = v8_compile("(function () {"
9102 " try {"
9103 " throw_in_js();"
9104 " return 42;"
9105 " } catch (e) {"
9106 " return e * 13;"
9107 " }"
9108 "})();");
9109 CHECK_EQ(91, script->Run()->Int32Value());
9110}
9111
9112
9113THREADED_TEST(RecursiveLocking) {
9114 v8::Locker locker;
9115 {
9116 v8::Locker locker2;
9117 CHECK(v8::Locker::IsLocked());
9118 }
9119}
9120
9121
9122static v8::Handle<Value> UnlockForAMoment(const v8::Arguments& args) {
9123 ApiTestFuzzer::Fuzz();
9124 v8::Unlocker unlocker;
9125 return v8::Undefined();
9126}
9127
9128
9129THREADED_TEST(LockUnlockLock) {
9130 {
9131 v8::Locker locker;
9132 v8::HandleScope scope;
9133 LocalContext env;
9134 Local<v8::FunctionTemplate> fun_templ =
9135 v8::FunctionTemplate::New(UnlockForAMoment);
9136 Local<Function> fun = fun_templ->GetFunction();
9137 env->Global()->Set(v8_str("unlock_for_a_moment"), fun);
9138 Local<Script> script = v8_compile("(function () {"
9139 " unlock_for_a_moment();"
9140 " return 42;"
9141 "})();");
9142 CHECK_EQ(42, script->Run()->Int32Value());
9143 }
9144 {
9145 v8::Locker locker;
9146 v8::HandleScope scope;
9147 LocalContext env;
9148 Local<v8::FunctionTemplate> fun_templ =
9149 v8::FunctionTemplate::New(UnlockForAMoment);
9150 Local<Function> fun = fun_templ->GetFunction();
9151 env->Global()->Set(v8_str("unlock_for_a_moment"), fun);
9152 Local<Script> script = v8_compile("(function () {"
9153 " unlock_for_a_moment();"
9154 " return 42;"
9155 "})();");
9156 CHECK_EQ(42, script->Run()->Int32Value());
9157 }
9158}
9159
9160
Leon Clarked91b9f72010-01-27 17:25:45 +00009161static int GetGlobalObjectsCount() {
Leon Clarkeeab96aa2010-01-27 16:31:12 +00009162 int count = 0;
Steve Block8defd9f2010-07-08 12:39:36 +01009163 i::HeapIterator it;
Leon Clarked91b9f72010-01-27 17:25:45 +00009164 for (i::HeapObject* object = it.next(); object != NULL; object = it.next())
9165 if (object->IsJSGlobalObject()) count++;
9166 return count;
9167}
9168
9169
Ben Murdochf87a2032010-10-22 12:50:53 +01009170static void CheckSurvivingGlobalObjectsCount(int expected) {
Steve Blocka7e24c12009-10-30 11:49:00 +00009171 // We need to collect all garbage twice to be sure that everything
9172 // has been collected. This is because inline caches are cleared in
9173 // the first garbage collection but some of the maps have already
9174 // been marked at that point. Therefore some of the maps are not
9175 // collected until the second garbage collection.
Steve Block44f0eee2011-05-26 01:26:41 +01009176 HEAP->CollectAllGarbage(false);
9177 HEAP->CollectAllGarbage(false);
Leon Clarked91b9f72010-01-27 17:25:45 +00009178 int count = GetGlobalObjectsCount();
Steve Blocka7e24c12009-10-30 11:49:00 +00009179#ifdef DEBUG
Steve Block44f0eee2011-05-26 01:26:41 +01009180 if (count != expected) HEAP->TracePathToGlobal();
Steve Blocka7e24c12009-10-30 11:49:00 +00009181#endif
Ben Murdochf87a2032010-10-22 12:50:53 +01009182 CHECK_EQ(expected, count);
Steve Blocka7e24c12009-10-30 11:49:00 +00009183}
9184
9185
9186TEST(DontLeakGlobalObjects) {
9187 // Regression test for issues 1139850 and 1174891.
9188
9189 v8::V8::Initialize();
9190
Steve Blocka7e24c12009-10-30 11:49:00 +00009191 for (int i = 0; i < 5; i++) {
9192 { v8::HandleScope scope;
9193 LocalContext context;
9194 }
Ben Murdochf87a2032010-10-22 12:50:53 +01009195 CheckSurvivingGlobalObjectsCount(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00009196
9197 { v8::HandleScope scope;
9198 LocalContext context;
9199 v8_compile("Date")->Run();
9200 }
Ben Murdochf87a2032010-10-22 12:50:53 +01009201 CheckSurvivingGlobalObjectsCount(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00009202
9203 { v8::HandleScope scope;
9204 LocalContext context;
9205 v8_compile("/aaa/")->Run();
9206 }
Ben Murdochf87a2032010-10-22 12:50:53 +01009207 CheckSurvivingGlobalObjectsCount(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00009208
9209 { v8::HandleScope scope;
9210 const char* extension_list[] = { "v8/gc" };
9211 v8::ExtensionConfiguration extensions(1, extension_list);
9212 LocalContext context(&extensions);
9213 v8_compile("gc();")->Run();
9214 }
Ben Murdochf87a2032010-10-22 12:50:53 +01009215 CheckSurvivingGlobalObjectsCount(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00009216 }
9217}
9218
9219
9220v8::Persistent<v8::Object> some_object;
9221v8::Persistent<v8::Object> bad_handle;
9222
Kristian Monsen50ef84f2010-07-29 15:18:00 +01009223void NewPersistentHandleCallback(v8::Persistent<v8::Value> handle, void*) {
Steve Blocka7e24c12009-10-30 11:49:00 +00009224 v8::HandleScope scope;
9225 bad_handle = v8::Persistent<v8::Object>::New(some_object);
Kristian Monsen50ef84f2010-07-29 15:18:00 +01009226 handle.Dispose();
Steve Blocka7e24c12009-10-30 11:49:00 +00009227}
9228
9229
9230THREADED_TEST(NewPersistentHandleFromWeakCallback) {
9231 LocalContext context;
9232
9233 v8::Persistent<v8::Object> handle1, handle2;
9234 {
9235 v8::HandleScope scope;
9236 some_object = v8::Persistent<v8::Object>::New(v8::Object::New());
9237 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
9238 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
9239 }
9240 // Note: order is implementation dependent alas: currently
9241 // global handle nodes are processed by PostGarbageCollectionProcessing
9242 // in reverse allocation order, so if second allocated handle is deleted,
9243 // weak callback of the first handle would be able to 'reallocate' it.
9244 handle1.MakeWeak(NULL, NewPersistentHandleCallback);
9245 handle2.Dispose();
Steve Block44f0eee2011-05-26 01:26:41 +01009246 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +00009247}
9248
9249
9250v8::Persistent<v8::Object> to_be_disposed;
9251
9252void DisposeAndForceGcCallback(v8::Persistent<v8::Value> handle, void*) {
9253 to_be_disposed.Dispose();
Steve Block44f0eee2011-05-26 01:26:41 +01009254 HEAP->CollectAllGarbage(false);
Kristian Monsen50ef84f2010-07-29 15:18:00 +01009255 handle.Dispose();
Steve Blocka7e24c12009-10-30 11:49:00 +00009256}
9257
9258
9259THREADED_TEST(DoNotUseDeletedNodesInSecondLevelGc) {
9260 LocalContext context;
9261
9262 v8::Persistent<v8::Object> handle1, handle2;
9263 {
9264 v8::HandleScope scope;
9265 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
9266 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
9267 }
9268 handle1.MakeWeak(NULL, DisposeAndForceGcCallback);
9269 to_be_disposed = handle2;
Steve Block44f0eee2011-05-26 01:26:41 +01009270 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +00009271}
9272
Steve Blockd0582a62009-12-15 09:54:21 +00009273void DisposingCallback(v8::Persistent<v8::Value> handle, void*) {
9274 handle.Dispose();
9275}
9276
9277void HandleCreatingCallback(v8::Persistent<v8::Value> handle, void*) {
9278 v8::HandleScope scope;
9279 v8::Persistent<v8::Object>::New(v8::Object::New());
Kristian Monsen50ef84f2010-07-29 15:18:00 +01009280 handle.Dispose();
Steve Blockd0582a62009-12-15 09:54:21 +00009281}
9282
9283
9284THREADED_TEST(NoGlobalHandlesOrphaningDueToWeakCallback) {
9285 LocalContext context;
9286
9287 v8::Persistent<v8::Object> handle1, handle2, handle3;
9288 {
9289 v8::HandleScope scope;
9290 handle3 = v8::Persistent<v8::Object>::New(v8::Object::New());
9291 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
9292 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
9293 }
9294 handle2.MakeWeak(NULL, DisposingCallback);
9295 handle3.MakeWeak(NULL, HandleCreatingCallback);
Steve Block44f0eee2011-05-26 01:26:41 +01009296 HEAP->CollectAllGarbage(false);
Steve Blockd0582a62009-12-15 09:54:21 +00009297}
9298
Steve Blocka7e24c12009-10-30 11:49:00 +00009299
9300THREADED_TEST(CheckForCrossContextObjectLiterals) {
9301 v8::V8::Initialize();
9302
9303 const int nof = 2;
9304 const char* sources[nof] = {
9305 "try { [ 2, 3, 4 ].forEach(5); } catch(e) { e.toString(); }",
9306 "Object()"
9307 };
9308
9309 for (int i = 0; i < nof; i++) {
9310 const char* source = sources[i];
9311 { v8::HandleScope scope;
9312 LocalContext context;
9313 CompileRun(source);
9314 }
9315 { v8::HandleScope scope;
9316 LocalContext context;
9317 CompileRun(source);
9318 }
9319 }
9320}
9321
9322
9323static v8::Handle<Value> NestedScope(v8::Persistent<Context> env) {
9324 v8::HandleScope inner;
9325 env->Enter();
9326 v8::Handle<Value> three = v8_num(3);
9327 v8::Handle<Value> value = inner.Close(three);
9328 env->Exit();
9329 return value;
9330}
9331
9332
9333THREADED_TEST(NestedHandleScopeAndContexts) {
9334 v8::HandleScope outer;
9335 v8::Persistent<Context> env = Context::New();
9336 env->Enter();
9337 v8::Handle<Value> value = NestedScope(env);
9338 v8::Handle<String> str = value->ToString();
9339 env->Exit();
9340 env.Dispose();
9341}
9342
9343
9344THREADED_TEST(ExternalAllocatedMemory) {
9345 v8::HandleScope outer;
9346 v8::Persistent<Context> env = Context::New();
9347 const int kSize = 1024*1024;
9348 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(kSize), kSize);
9349 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(-kSize), 0);
9350}
9351
9352
9353THREADED_TEST(DisposeEnteredContext) {
9354 v8::HandleScope scope;
9355 LocalContext outer;
9356 { v8::Persistent<v8::Context> inner = v8::Context::New();
9357 inner->Enter();
9358 inner.Dispose();
9359 inner.Clear();
9360 inner->Exit();
9361 }
9362}
9363
9364
9365// Regression test for issue 54, object templates with internal fields
9366// but no accessors or interceptors did not get their internal field
9367// count set on instances.
9368THREADED_TEST(Regress54) {
9369 v8::HandleScope outer;
9370 LocalContext context;
9371 static v8::Persistent<v8::ObjectTemplate> templ;
9372 if (templ.IsEmpty()) {
9373 v8::HandleScope inner;
9374 v8::Handle<v8::ObjectTemplate> local = v8::ObjectTemplate::New();
9375 local->SetInternalFieldCount(1);
9376 templ = v8::Persistent<v8::ObjectTemplate>::New(inner.Close(local));
9377 }
9378 v8::Handle<v8::Object> result = templ->NewInstance();
9379 CHECK_EQ(1, result->InternalFieldCount());
9380}
9381
9382
9383// If part of the threaded tests, this test makes ThreadingTest fail
9384// on mac.
9385TEST(CatchStackOverflow) {
9386 v8::HandleScope scope;
9387 LocalContext context;
9388 v8::TryCatch try_catch;
9389 v8::Handle<v8::Script> script = v8::Script::Compile(v8::String::New(
9390 "function f() {"
9391 " return f();"
9392 "}"
9393 ""
9394 "f();"));
9395 v8::Handle<v8::Value> result = script->Run();
9396 CHECK(result.IsEmpty());
9397}
9398
9399
9400static void CheckTryCatchSourceInfo(v8::Handle<v8::Script> script,
9401 const char* resource_name,
9402 int line_offset) {
9403 v8::HandleScope scope;
9404 v8::TryCatch try_catch;
9405 v8::Handle<v8::Value> result = script->Run();
9406 CHECK(result.IsEmpty());
9407 CHECK(try_catch.HasCaught());
9408 v8::Handle<v8::Message> message = try_catch.Message();
9409 CHECK(!message.IsEmpty());
9410 CHECK_EQ(10 + line_offset, message->GetLineNumber());
9411 CHECK_EQ(91, message->GetStartPosition());
9412 CHECK_EQ(92, message->GetEndPosition());
9413 CHECK_EQ(2, message->GetStartColumn());
9414 CHECK_EQ(3, message->GetEndColumn());
9415 v8::String::AsciiValue line(message->GetSourceLine());
9416 CHECK_EQ(" throw 'nirk';", *line);
9417 v8::String::AsciiValue name(message->GetScriptResourceName());
9418 CHECK_EQ(resource_name, *name);
9419}
9420
9421
9422THREADED_TEST(TryCatchSourceInfo) {
9423 v8::HandleScope scope;
9424 LocalContext context;
9425 v8::Handle<v8::String> source = v8::String::New(
9426 "function Foo() {\n"
9427 " return Bar();\n"
9428 "}\n"
9429 "\n"
9430 "function Bar() {\n"
9431 " return Baz();\n"
9432 "}\n"
9433 "\n"
9434 "function Baz() {\n"
9435 " throw 'nirk';\n"
9436 "}\n"
9437 "\n"
9438 "Foo();\n");
9439
9440 const char* resource_name;
9441 v8::Handle<v8::Script> script;
9442 resource_name = "test.js";
9443 script = v8::Script::Compile(source, v8::String::New(resource_name));
9444 CheckTryCatchSourceInfo(script, resource_name, 0);
9445
9446 resource_name = "test1.js";
9447 v8::ScriptOrigin origin1(v8::String::New(resource_name));
9448 script = v8::Script::Compile(source, &origin1);
9449 CheckTryCatchSourceInfo(script, resource_name, 0);
9450
9451 resource_name = "test2.js";
9452 v8::ScriptOrigin origin2(v8::String::New(resource_name), v8::Integer::New(7));
9453 script = v8::Script::Compile(source, &origin2);
9454 CheckTryCatchSourceInfo(script, resource_name, 7);
9455}
9456
9457
9458THREADED_TEST(CompilationCache) {
9459 v8::HandleScope scope;
9460 LocalContext context;
9461 v8::Handle<v8::String> source0 = v8::String::New("1234");
9462 v8::Handle<v8::String> source1 = v8::String::New("1234");
9463 v8::Handle<v8::Script> script0 =
9464 v8::Script::Compile(source0, v8::String::New("test.js"));
9465 v8::Handle<v8::Script> script1 =
9466 v8::Script::Compile(source1, v8::String::New("test.js"));
9467 v8::Handle<v8::Script> script2 =
9468 v8::Script::Compile(source0); // different origin
9469 CHECK_EQ(1234, script0->Run()->Int32Value());
9470 CHECK_EQ(1234, script1->Run()->Int32Value());
9471 CHECK_EQ(1234, script2->Run()->Int32Value());
9472}
9473
9474
9475static v8::Handle<Value> FunctionNameCallback(const v8::Arguments& args) {
9476 ApiTestFuzzer::Fuzz();
9477 return v8_num(42);
9478}
9479
9480
9481THREADED_TEST(CallbackFunctionName) {
9482 v8::HandleScope scope;
9483 LocalContext context;
9484 Local<ObjectTemplate> t = ObjectTemplate::New();
9485 t->Set(v8_str("asdf"), v8::FunctionTemplate::New(FunctionNameCallback));
9486 context->Global()->Set(v8_str("obj"), t->NewInstance());
9487 v8::Handle<v8::Value> value = CompileRun("obj.asdf.name");
9488 CHECK(value->IsString());
9489 v8::String::AsciiValue name(value);
9490 CHECK_EQ("asdf", *name);
9491}
9492
9493
9494THREADED_TEST(DateAccess) {
9495 v8::HandleScope scope;
9496 LocalContext context;
9497 v8::Handle<v8::Value> date = v8::Date::New(1224744689038.0);
9498 CHECK(date->IsDate());
Steve Block6ded16b2010-05-10 14:33:55 +01009499 CHECK_EQ(1224744689038.0, date.As<v8::Date>()->NumberValue());
Steve Blocka7e24c12009-10-30 11:49:00 +00009500}
9501
9502
9503void CheckProperties(v8::Handle<v8::Value> val, int elmc, const char* elmv[]) {
Steve Block6ded16b2010-05-10 14:33:55 +01009504 v8::Handle<v8::Object> obj = val.As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00009505 v8::Handle<v8::Array> props = obj->GetPropertyNames();
9506 CHECK_EQ(elmc, props->Length());
9507 for (int i = 0; i < elmc; i++) {
9508 v8::String::Utf8Value elm(props->Get(v8::Integer::New(i)));
9509 CHECK_EQ(elmv[i], *elm);
9510 }
9511}
9512
9513
9514THREADED_TEST(PropertyEnumeration) {
9515 v8::HandleScope scope;
9516 LocalContext context;
9517 v8::Handle<v8::Value> obj = v8::Script::Compile(v8::String::New(
9518 "var result = [];"
9519 "result[0] = {};"
9520 "result[1] = {a: 1, b: 2};"
9521 "result[2] = [1, 2, 3];"
9522 "var proto = {x: 1, y: 2, z: 3};"
9523 "var x = { __proto__: proto, w: 0, z: 1 };"
9524 "result[3] = x;"
9525 "result;"))->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01009526 v8::Handle<v8::Array> elms = obj.As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00009527 CHECK_EQ(4, elms->Length());
9528 int elmc0 = 0;
9529 const char** elmv0 = NULL;
9530 CheckProperties(elms->Get(v8::Integer::New(0)), elmc0, elmv0);
9531 int elmc1 = 2;
9532 const char* elmv1[] = {"a", "b"};
9533 CheckProperties(elms->Get(v8::Integer::New(1)), elmc1, elmv1);
9534 int elmc2 = 3;
9535 const char* elmv2[] = {"0", "1", "2"};
9536 CheckProperties(elms->Get(v8::Integer::New(2)), elmc2, elmv2);
9537 int elmc3 = 4;
9538 const char* elmv3[] = {"w", "z", "x", "y"};
9539 CheckProperties(elms->Get(v8::Integer::New(3)), elmc3, elmv3);
9540}
9541
Steve Block44f0eee2011-05-26 01:26:41 +01009542THREADED_TEST(PropertyEnumeration2) {
9543 v8::HandleScope scope;
9544 LocalContext context;
9545 v8::Handle<v8::Value> obj = v8::Script::Compile(v8::String::New(
9546 "var result = [];"
9547 "result[0] = {};"
9548 "result[1] = {a: 1, b: 2};"
9549 "result[2] = [1, 2, 3];"
9550 "var proto = {x: 1, y: 2, z: 3};"
9551 "var x = { __proto__: proto, w: 0, z: 1 };"
9552 "result[3] = x;"
9553 "result;"))->Run();
9554 v8::Handle<v8::Array> elms = obj.As<v8::Array>();
9555 CHECK_EQ(4, elms->Length());
9556 int elmc0 = 0;
9557 const char** elmv0 = NULL;
9558 CheckProperties(elms->Get(v8::Integer::New(0)), elmc0, elmv0);
9559
9560 v8::Handle<v8::Value> val = elms->Get(v8::Integer::New(0));
9561 v8::Handle<v8::Array> props = val.As<v8::Object>()->GetPropertyNames();
9562 CHECK_EQ(0, props->Length());
9563 for (uint32_t i = 0; i < props->Length(); i++) {
9564 printf("p[%d]\n", i);
9565 }
9566}
Steve Blocka7e24c12009-10-30 11:49:00 +00009567
Steve Blocka7e24c12009-10-30 11:49:00 +00009568static bool NamedSetAccessBlocker(Local<v8::Object> obj,
9569 Local<Value> name,
9570 v8::AccessType type,
9571 Local<Value> data) {
9572 return type != v8::ACCESS_SET;
9573}
9574
9575
9576static bool IndexedSetAccessBlocker(Local<v8::Object> obj,
9577 uint32_t key,
9578 v8::AccessType type,
9579 Local<Value> data) {
9580 return type != v8::ACCESS_SET;
9581}
9582
9583
9584THREADED_TEST(DisableAccessChecksWhileConfiguring) {
9585 v8::HandleScope scope;
9586 LocalContext context;
9587 Local<ObjectTemplate> templ = ObjectTemplate::New();
9588 templ->SetAccessCheckCallbacks(NamedSetAccessBlocker,
9589 IndexedSetAccessBlocker);
9590 templ->Set(v8_str("x"), v8::True());
9591 Local<v8::Object> instance = templ->NewInstance();
9592 context->Global()->Set(v8_str("obj"), instance);
9593 Local<Value> value = CompileRun("obj.x");
9594 CHECK(value->BooleanValue());
9595}
9596
9597
9598static bool NamedGetAccessBlocker(Local<v8::Object> obj,
9599 Local<Value> name,
9600 v8::AccessType type,
9601 Local<Value> data) {
9602 return false;
9603}
9604
9605
9606static bool IndexedGetAccessBlocker(Local<v8::Object> obj,
9607 uint32_t key,
9608 v8::AccessType type,
9609 Local<Value> data) {
9610 return false;
9611}
9612
9613
9614
9615THREADED_TEST(AccessChecksReenabledCorrectly) {
9616 v8::HandleScope scope;
9617 LocalContext context;
9618 Local<ObjectTemplate> templ = ObjectTemplate::New();
9619 templ->SetAccessCheckCallbacks(NamedGetAccessBlocker,
9620 IndexedGetAccessBlocker);
9621 templ->Set(v8_str("a"), v8_str("a"));
9622 // Add more than 8 (see kMaxFastProperties) properties
9623 // so that the constructor will force copying map.
9624 // Cannot sprintf, gcc complains unsafety.
9625 char buf[4];
9626 for (char i = '0'; i <= '9' ; i++) {
9627 buf[0] = i;
9628 for (char j = '0'; j <= '9'; j++) {
9629 buf[1] = j;
9630 for (char k = '0'; k <= '9'; k++) {
9631 buf[2] = k;
9632 buf[3] = 0;
9633 templ->Set(v8_str(buf), v8::Number::New(k));
9634 }
9635 }
9636 }
9637
9638 Local<v8::Object> instance_1 = templ->NewInstance();
9639 context->Global()->Set(v8_str("obj_1"), instance_1);
9640
9641 Local<Value> value_1 = CompileRun("obj_1.a");
9642 CHECK(value_1->IsUndefined());
9643
9644 Local<v8::Object> instance_2 = templ->NewInstance();
9645 context->Global()->Set(v8_str("obj_2"), instance_2);
9646
9647 Local<Value> value_2 = CompileRun("obj_2.a");
9648 CHECK(value_2->IsUndefined());
9649}
9650
9651
9652// This tests that access check information remains on the global
9653// object template when creating contexts.
9654THREADED_TEST(AccessControlRepeatedContextCreation) {
9655 v8::HandleScope handle_scope;
9656 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
9657 global_template->SetAccessCheckCallbacks(NamedSetAccessBlocker,
9658 IndexedSetAccessBlocker);
9659 i::Handle<i::ObjectTemplateInfo> internal_template =
9660 v8::Utils::OpenHandle(*global_template);
9661 CHECK(!internal_template->constructor()->IsUndefined());
9662 i::Handle<i::FunctionTemplateInfo> constructor(
9663 i::FunctionTemplateInfo::cast(internal_template->constructor()));
9664 CHECK(!constructor->access_check_info()->IsUndefined());
9665 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
9666 CHECK(!constructor->access_check_info()->IsUndefined());
9667}
9668
9669
9670THREADED_TEST(TurnOnAccessCheck) {
9671 v8::HandleScope handle_scope;
9672
9673 // Create an environment with access check to the global object disabled by
9674 // default.
9675 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
9676 global_template->SetAccessCheckCallbacks(NamedGetAccessBlocker,
9677 IndexedGetAccessBlocker,
9678 v8::Handle<v8::Value>(),
9679 false);
9680 v8::Persistent<Context> context = Context::New(NULL, global_template);
9681 Context::Scope context_scope(context);
9682
9683 // Set up a property and a number of functions.
9684 context->Global()->Set(v8_str("a"), v8_num(1));
9685 CompileRun("function f1() {return a;}"
9686 "function f2() {return a;}"
9687 "function g1() {return h();}"
9688 "function g2() {return h();}"
9689 "function h() {return 1;}");
9690 Local<Function> f1 =
9691 Local<Function>::Cast(context->Global()->Get(v8_str("f1")));
9692 Local<Function> f2 =
9693 Local<Function>::Cast(context->Global()->Get(v8_str("f2")));
9694 Local<Function> g1 =
9695 Local<Function>::Cast(context->Global()->Get(v8_str("g1")));
9696 Local<Function> g2 =
9697 Local<Function>::Cast(context->Global()->Get(v8_str("g2")));
9698 Local<Function> h =
9699 Local<Function>::Cast(context->Global()->Get(v8_str("h")));
9700
9701 // Get the global object.
9702 v8::Handle<v8::Object> global = context->Global();
9703
9704 // Call f1 one time and f2 a number of times. This will ensure that f1 still
9705 // uses the runtime system to retreive property a whereas f2 uses global load
9706 // inline cache.
9707 CHECK(f1->Call(global, 0, NULL)->Equals(v8_num(1)));
9708 for (int i = 0; i < 4; i++) {
9709 CHECK(f2->Call(global, 0, NULL)->Equals(v8_num(1)));
9710 }
9711
9712 // Same for g1 and g2.
9713 CHECK(g1->Call(global, 0, NULL)->Equals(v8_num(1)));
9714 for (int i = 0; i < 4; i++) {
9715 CHECK(g2->Call(global, 0, NULL)->Equals(v8_num(1)));
9716 }
9717
9718 // Detach the global and turn on access check.
9719 context->DetachGlobal();
9720 context->Global()->TurnOnAccessCheck();
9721
9722 // Failing access check to property get results in undefined.
9723 CHECK(f1->Call(global, 0, NULL)->IsUndefined());
9724 CHECK(f2->Call(global, 0, NULL)->IsUndefined());
9725
9726 // Failing access check to function call results in exception.
9727 CHECK(g1->Call(global, 0, NULL).IsEmpty());
9728 CHECK(g2->Call(global, 0, NULL).IsEmpty());
9729
9730 // No failing access check when just returning a constant.
9731 CHECK(h->Call(global, 0, NULL)->Equals(v8_num(1)));
9732}
9733
9734
Ben Murdochb0fe1622011-05-05 13:52:32 +01009735v8::Handle<v8::String> a;
9736v8::Handle<v8::String> h;
9737
9738static bool NamedGetAccessBlockAandH(Local<v8::Object> obj,
9739 Local<Value> name,
9740 v8::AccessType type,
9741 Local<Value> data) {
9742 return !(name->Equals(a) || name->Equals(h));
9743}
9744
9745
9746THREADED_TEST(TurnOnAccessCheckAndRecompile) {
9747 v8::HandleScope handle_scope;
9748
9749 // Create an environment with access check to the global object disabled by
9750 // default. When the registered access checker will block access to properties
9751 // a and h
9752 a = v8_str("a");
9753 h = v8_str("h");
9754 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
9755 global_template->SetAccessCheckCallbacks(NamedGetAccessBlockAandH,
9756 IndexedGetAccessBlocker,
9757 v8::Handle<v8::Value>(),
9758 false);
9759 v8::Persistent<Context> context = Context::New(NULL, global_template);
9760 Context::Scope context_scope(context);
9761
9762 // Set up a property and a number of functions.
9763 context->Global()->Set(v8_str("a"), v8_num(1));
9764 static const char* source = "function f1() {return a;}"
9765 "function f2() {return a;}"
9766 "function g1() {return h();}"
9767 "function g2() {return h();}"
9768 "function h() {return 1;}";
9769
9770 CompileRun(source);
9771 Local<Function> f1;
9772 Local<Function> f2;
9773 Local<Function> g1;
9774 Local<Function> g2;
9775 Local<Function> h;
9776 f1 = Local<Function>::Cast(context->Global()->Get(v8_str("f1")));
9777 f2 = Local<Function>::Cast(context->Global()->Get(v8_str("f2")));
9778 g1 = Local<Function>::Cast(context->Global()->Get(v8_str("g1")));
9779 g2 = Local<Function>::Cast(context->Global()->Get(v8_str("g2")));
9780 h = Local<Function>::Cast(context->Global()->Get(v8_str("h")));
9781
9782 // Get the global object.
9783 v8::Handle<v8::Object> global = context->Global();
9784
9785 // Call f1 one time and f2 a number of times. This will ensure that f1 still
9786 // uses the runtime system to retreive property a whereas f2 uses global load
9787 // inline cache.
9788 CHECK(f1->Call(global, 0, NULL)->Equals(v8_num(1)));
9789 for (int i = 0; i < 4; i++) {
9790 CHECK(f2->Call(global, 0, NULL)->Equals(v8_num(1)));
9791 }
9792
9793 // Same for g1 and g2.
9794 CHECK(g1->Call(global, 0, NULL)->Equals(v8_num(1)));
9795 for (int i = 0; i < 4; i++) {
9796 CHECK(g2->Call(global, 0, NULL)->Equals(v8_num(1)));
9797 }
9798
9799 // Detach the global and turn on access check now blocking access to property
9800 // a and function h.
9801 context->DetachGlobal();
9802 context->Global()->TurnOnAccessCheck();
9803
9804 // Failing access check to property get results in undefined.
9805 CHECK(f1->Call(global, 0, NULL)->IsUndefined());
9806 CHECK(f2->Call(global, 0, NULL)->IsUndefined());
9807
9808 // Failing access check to function call results in exception.
9809 CHECK(g1->Call(global, 0, NULL).IsEmpty());
9810 CHECK(g2->Call(global, 0, NULL).IsEmpty());
9811
9812 // No failing access check when just returning a constant.
9813 CHECK(h->Call(global, 0, NULL)->Equals(v8_num(1)));
9814
9815 // Now compile the source again. And get the newly compiled functions, except
9816 // for h for which access is blocked.
9817 CompileRun(source);
9818 f1 = Local<Function>::Cast(context->Global()->Get(v8_str("f1")));
9819 f2 = Local<Function>::Cast(context->Global()->Get(v8_str("f2")));
9820 g1 = Local<Function>::Cast(context->Global()->Get(v8_str("g1")));
9821 g2 = Local<Function>::Cast(context->Global()->Get(v8_str("g2")));
9822 CHECK(context->Global()->Get(v8_str("h"))->IsUndefined());
9823
9824 // Failing access check to property get results in undefined.
9825 CHECK(f1->Call(global, 0, NULL)->IsUndefined());
9826 CHECK(f2->Call(global, 0, NULL)->IsUndefined());
9827
9828 // Failing access check to function call results in exception.
9829 CHECK(g1->Call(global, 0, NULL).IsEmpty());
9830 CHECK(g2->Call(global, 0, NULL).IsEmpty());
9831}
9832
9833
Steve Blocka7e24c12009-10-30 11:49:00 +00009834// This test verifies that pre-compilation (aka preparsing) can be called
9835// without initializing the whole VM. Thus we cannot run this test in a
9836// multi-threaded setup.
9837TEST(PreCompile) {
9838 // TODO(155): This test would break without the initialization of V8. This is
9839 // a workaround for now to make this test not fail.
9840 v8::V8::Initialize();
Leon Clarkef7060e22010-06-03 12:02:55 +01009841 const char* script = "function foo(a) { return a+1; }";
9842 v8::ScriptData* sd =
Steve Blockd0582a62009-12-15 09:54:21 +00009843 v8::ScriptData::PreCompile(script, i::StrLength(script));
Steve Blocka7e24c12009-10-30 11:49:00 +00009844 CHECK_NE(sd->Length(), 0);
9845 CHECK_NE(sd->Data(), NULL);
Leon Clarkee46be812010-01-19 14:06:41 +00009846 CHECK(!sd->HasError());
9847 delete sd;
9848}
9849
9850
9851TEST(PreCompileWithError) {
9852 v8::V8::Initialize();
Leon Clarkef7060e22010-06-03 12:02:55 +01009853 const char* script = "function foo(a) { return 1 * * 2; }";
9854 v8::ScriptData* sd =
Leon Clarkee46be812010-01-19 14:06:41 +00009855 v8::ScriptData::PreCompile(script, i::StrLength(script));
9856 CHECK(sd->HasError());
9857 delete sd;
9858}
9859
9860
9861TEST(Regress31661) {
9862 v8::V8::Initialize();
Leon Clarkef7060e22010-06-03 12:02:55 +01009863 const char* script = " The Definintive Guide";
9864 v8::ScriptData* sd =
Leon Clarkee46be812010-01-19 14:06:41 +00009865 v8::ScriptData::PreCompile(script, i::StrLength(script));
9866 CHECK(sd->HasError());
Steve Blocka7e24c12009-10-30 11:49:00 +00009867 delete sd;
9868}
9869
9870
Leon Clarkef7060e22010-06-03 12:02:55 +01009871// Tests that ScriptData can be serialized and deserialized.
9872TEST(PreCompileSerialization) {
9873 v8::V8::Initialize();
9874 const char* script = "function foo(a) { return a+1; }";
9875 v8::ScriptData* sd =
9876 v8::ScriptData::PreCompile(script, i::StrLength(script));
9877
9878 // Serialize.
9879 int serialized_data_length = sd->Length();
9880 char* serialized_data = i::NewArray<char>(serialized_data_length);
9881 memcpy(serialized_data, sd->Data(), serialized_data_length);
9882
9883 // Deserialize.
9884 v8::ScriptData* deserialized_sd =
9885 v8::ScriptData::New(serialized_data, serialized_data_length);
9886
9887 // Verify that the original is the same as the deserialized.
9888 CHECK_EQ(sd->Length(), deserialized_sd->Length());
9889 CHECK_EQ(0, memcmp(sd->Data(), deserialized_sd->Data(), sd->Length()));
9890 CHECK_EQ(sd->HasError(), deserialized_sd->HasError());
9891
9892 delete sd;
9893 delete deserialized_sd;
9894}
9895
9896
9897// Attempts to deserialize bad data.
9898TEST(PreCompileDeserializationError) {
9899 v8::V8::Initialize();
9900 const char* data = "DONT CARE";
9901 int invalid_size = 3;
9902 v8::ScriptData* sd = v8::ScriptData::New(data, invalid_size);
9903
9904 CHECK_EQ(0, sd->Length());
9905
9906 delete sd;
9907}
9908
9909
Leon Clarkeac952652010-07-15 11:15:24 +01009910// Attempts to deserialize bad data.
9911TEST(PreCompileInvalidPreparseDataError) {
9912 v8::V8::Initialize();
9913 v8::HandleScope scope;
9914 LocalContext context;
9915
9916 const char* script = "function foo(){ return 5;}\n"
9917 "function bar(){ return 6 + 7;} foo();";
9918 v8::ScriptData* sd =
9919 v8::ScriptData::PreCompile(script, i::StrLength(script));
9920 CHECK(!sd->HasError());
9921 // ScriptDataImpl private implementation details
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -08009922 const int kHeaderSize = i::PreparseDataConstants::kHeaderSize;
Iain Merrick9ac36c92010-09-13 15:29:50 +01009923 const int kFunctionEntrySize = i::FunctionEntry::kSize;
Leon Clarkeac952652010-07-15 11:15:24 +01009924 const int kFunctionEntryStartOffset = 0;
9925 const int kFunctionEntryEndOffset = 1;
9926 unsigned* sd_data =
9927 reinterpret_cast<unsigned*>(const_cast<char*>(sd->Data()));
Leon Clarkeac952652010-07-15 11:15:24 +01009928
9929 // Overwrite function bar's end position with 0.
9930 sd_data[kHeaderSize + 1 * kFunctionEntrySize + kFunctionEntryEndOffset] = 0;
9931 v8::TryCatch try_catch;
9932
9933 Local<String> source = String::New(script);
9934 Local<Script> compiled_script = Script::New(source, NULL, sd);
9935 CHECK(try_catch.HasCaught());
9936 String::AsciiValue exception_value(try_catch.Message()->Get());
9937 CHECK_EQ("Uncaught SyntaxError: Invalid preparser data for function bar",
9938 *exception_value);
9939
9940 try_catch.Reset();
9941 // Overwrite function bar's start position with 200. The function entry
9942 // will not be found when searching for it by position.
Kristian Monsen80d68ea2010-09-08 11:05:35 +01009943 sd = v8::ScriptData::PreCompile(script, i::StrLength(script));
9944 sd_data = reinterpret_cast<unsigned*>(const_cast<char*>(sd->Data()));
Leon Clarkeac952652010-07-15 11:15:24 +01009945 sd_data[kHeaderSize + 1 * kFunctionEntrySize + kFunctionEntryStartOffset] =
9946 200;
9947 compiled_script = Script::New(source, NULL, sd);
9948 CHECK(try_catch.HasCaught());
9949 String::AsciiValue second_exception_value(try_catch.Message()->Get());
9950 CHECK_EQ("Uncaught SyntaxError: Invalid preparser data for function bar",
9951 *second_exception_value);
9952
9953 delete sd;
9954}
9955
9956
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01009957// Verifies that the Handle<String> and const char* versions of the API produce
9958// the same results (at least for one trivial case).
9959TEST(PreCompileAPIVariationsAreSame) {
9960 v8::V8::Initialize();
9961 v8::HandleScope scope;
9962
9963 const char* cstring = "function foo(a) { return a+1; }";
Ben Murdoch3bec4d22010-07-22 14:51:16 +01009964
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01009965 v8::ScriptData* sd_from_cstring =
9966 v8::ScriptData::PreCompile(cstring, i::StrLength(cstring));
9967
9968 TestAsciiResource* resource = new TestAsciiResource(cstring);
Ben Murdoch3bec4d22010-07-22 14:51:16 +01009969 v8::ScriptData* sd_from_external_string = v8::ScriptData::PreCompile(
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01009970 v8::String::NewExternal(resource));
9971
Ben Murdoch3bec4d22010-07-22 14:51:16 +01009972 v8::ScriptData* sd_from_string = v8::ScriptData::PreCompile(
9973 v8::String::New(cstring));
9974
9975 CHECK_EQ(sd_from_cstring->Length(), sd_from_external_string->Length());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01009976 CHECK_EQ(0, memcmp(sd_from_cstring->Data(),
Ben Murdoch3bec4d22010-07-22 14:51:16 +01009977 sd_from_external_string->Data(),
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01009978 sd_from_cstring->Length()));
9979
Ben Murdoch3bec4d22010-07-22 14:51:16 +01009980 CHECK_EQ(sd_from_cstring->Length(), sd_from_string->Length());
9981 CHECK_EQ(0, memcmp(sd_from_cstring->Data(),
9982 sd_from_string->Data(),
9983 sd_from_cstring->Length()));
9984
9985
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01009986 delete sd_from_cstring;
Ben Murdoch3bec4d22010-07-22 14:51:16 +01009987 delete sd_from_external_string;
9988 delete sd_from_string;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01009989}
9990
9991
Steve Blocka7e24c12009-10-30 11:49:00 +00009992// This tests that we do not allow dictionary load/call inline caches
9993// to use functions that have not yet been compiled. The potential
9994// problem of loading a function that has not yet been compiled can
9995// arise because we share code between contexts via the compilation
9996// cache.
9997THREADED_TEST(DictionaryICLoadedFunction) {
9998 v8::HandleScope scope;
9999 // Test LoadIC.
10000 for (int i = 0; i < 2; i++) {
10001 LocalContext context;
10002 context->Global()->Set(v8_str("tmp"), v8::True());
10003 context->Global()->Delete(v8_str("tmp"));
10004 CompileRun("for (var j = 0; j < 10; j++) new RegExp('');");
10005 }
10006 // Test CallIC.
10007 for (int i = 0; i < 2; i++) {
10008 LocalContext context;
10009 context->Global()->Set(v8_str("tmp"), v8::True());
10010 context->Global()->Delete(v8_str("tmp"));
10011 CompileRun("for (var j = 0; j < 10; j++) RegExp('')");
10012 }
10013}
10014
10015
10016// Test that cross-context new calls use the context of the callee to
10017// create the new JavaScript object.
10018THREADED_TEST(CrossContextNew) {
10019 v8::HandleScope scope;
10020 v8::Persistent<Context> context0 = Context::New();
10021 v8::Persistent<Context> context1 = Context::New();
10022
10023 // Allow cross-domain access.
10024 Local<String> token = v8_str("<security token>");
10025 context0->SetSecurityToken(token);
10026 context1->SetSecurityToken(token);
10027
10028 // Set an 'x' property on the Object prototype and define a
10029 // constructor function in context0.
10030 context0->Enter();
10031 CompileRun("Object.prototype.x = 42; function C() {};");
10032 context0->Exit();
10033
10034 // Call the constructor function from context0 and check that the
10035 // result has the 'x' property.
10036 context1->Enter();
10037 context1->Global()->Set(v8_str("other"), context0->Global());
10038 Local<Value> value = CompileRun("var instance = new other.C(); instance.x");
10039 CHECK(value->IsInt32());
10040 CHECK_EQ(42, value->Int32Value());
10041 context1->Exit();
10042
10043 // Dispose the contexts to allow them to be garbage collected.
10044 context0.Dispose();
10045 context1.Dispose();
10046}
10047
10048
10049class RegExpInterruptTest {
10050 public:
10051 RegExpInterruptTest() : block_(NULL) {}
10052 ~RegExpInterruptTest() { delete block_; }
10053 void RunTest() {
10054 block_ = i::OS::CreateSemaphore(0);
10055 gc_count_ = 0;
10056 gc_during_regexp_ = 0;
10057 regexp_success_ = false;
10058 gc_success_ = false;
Steve Block44f0eee2011-05-26 01:26:41 +010010059 GCThread gc_thread(i::Isolate::Current(), this);
Steve Blocka7e24c12009-10-30 11:49:00 +000010060 gc_thread.Start();
10061 v8::Locker::StartPreemption(1);
10062
10063 LongRunningRegExp();
10064 {
10065 v8::Unlocker unlock;
10066 gc_thread.Join();
10067 }
10068 v8::Locker::StopPreemption();
10069 CHECK(regexp_success_);
10070 CHECK(gc_success_);
10071 }
10072 private:
10073 // Number of garbage collections required.
10074 static const int kRequiredGCs = 5;
10075
10076 class GCThread : public i::Thread {
10077 public:
Steve Block44f0eee2011-05-26 01:26:41 +010010078 explicit GCThread(i::Isolate* isolate, RegExpInterruptTest* test)
10079 : Thread(isolate, "GCThread"), test_(test) {}
Steve Blocka7e24c12009-10-30 11:49:00 +000010080 virtual void Run() {
10081 test_->CollectGarbage();
10082 }
10083 private:
10084 RegExpInterruptTest* test_;
10085 };
10086
10087 void CollectGarbage() {
10088 block_->Wait();
10089 while (gc_during_regexp_ < kRequiredGCs) {
10090 {
10091 v8::Locker lock;
10092 // TODO(lrn): Perhaps create some garbage before collecting.
Steve Block44f0eee2011-05-26 01:26:41 +010010093 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +000010094 gc_count_++;
10095 }
10096 i::OS::Sleep(1);
10097 }
10098 gc_success_ = true;
10099 }
10100
10101 void LongRunningRegExp() {
10102 block_->Signal(); // Enable garbage collection thread on next preemption.
10103 int rounds = 0;
10104 while (gc_during_regexp_ < kRequiredGCs) {
10105 int gc_before = gc_count_;
10106 {
10107 // Match 15-30 "a"'s against 14 and a "b".
10108 const char* c_source =
10109 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
10110 ".exec('aaaaaaaaaaaaaaab') === null";
10111 Local<String> source = String::New(c_source);
10112 Local<Script> script = Script::Compile(source);
10113 Local<Value> result = script->Run();
10114 if (!result->BooleanValue()) {
10115 gc_during_regexp_ = kRequiredGCs; // Allow gc thread to exit.
10116 return;
10117 }
10118 }
10119 {
10120 // Match 15-30 "a"'s against 15 and a "b".
10121 const char* c_source =
10122 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
10123 ".exec('aaaaaaaaaaaaaaaab')[0] === 'aaaaaaaaaaaaaaaa'";
10124 Local<String> source = String::New(c_source);
10125 Local<Script> script = Script::Compile(source);
10126 Local<Value> result = script->Run();
10127 if (!result->BooleanValue()) {
10128 gc_during_regexp_ = kRequiredGCs;
10129 return;
10130 }
10131 }
10132 int gc_after = gc_count_;
10133 gc_during_regexp_ += gc_after - gc_before;
10134 rounds++;
10135 i::OS::Sleep(1);
10136 }
10137 regexp_success_ = true;
10138 }
10139
10140 i::Semaphore* block_;
10141 int gc_count_;
10142 int gc_during_regexp_;
10143 bool regexp_success_;
10144 bool gc_success_;
10145};
10146
10147
10148// Test that a regular expression execution can be interrupted and
10149// survive a garbage collection.
10150TEST(RegExpInterruption) {
10151 v8::Locker lock;
10152 v8::V8::Initialize();
10153 v8::HandleScope scope;
10154 Local<Context> local_env;
10155 {
10156 LocalContext env;
10157 local_env = env.local();
10158 }
10159
10160 // Local context should still be live.
10161 CHECK(!local_env.IsEmpty());
10162 local_env->Enter();
10163
10164 // Should complete without problems.
10165 RegExpInterruptTest().RunTest();
10166
10167 local_env->Exit();
10168}
10169
10170
10171class ApplyInterruptTest {
10172 public:
10173 ApplyInterruptTest() : block_(NULL) {}
10174 ~ApplyInterruptTest() { delete block_; }
10175 void RunTest() {
10176 block_ = i::OS::CreateSemaphore(0);
10177 gc_count_ = 0;
10178 gc_during_apply_ = 0;
10179 apply_success_ = false;
10180 gc_success_ = false;
Steve Block44f0eee2011-05-26 01:26:41 +010010181 GCThread gc_thread(i::Isolate::Current(), this);
Steve Blocka7e24c12009-10-30 11:49:00 +000010182 gc_thread.Start();
10183 v8::Locker::StartPreemption(1);
10184
10185 LongRunningApply();
10186 {
10187 v8::Unlocker unlock;
10188 gc_thread.Join();
10189 }
10190 v8::Locker::StopPreemption();
10191 CHECK(apply_success_);
10192 CHECK(gc_success_);
10193 }
10194 private:
10195 // Number of garbage collections required.
10196 static const int kRequiredGCs = 2;
10197
10198 class GCThread : public i::Thread {
10199 public:
Steve Block44f0eee2011-05-26 01:26:41 +010010200 explicit GCThread(i::Isolate* isolate, ApplyInterruptTest* test)
10201 : Thread(isolate, "GCThread"), test_(test) {}
Steve Blocka7e24c12009-10-30 11:49:00 +000010202 virtual void Run() {
10203 test_->CollectGarbage();
10204 }
10205 private:
10206 ApplyInterruptTest* test_;
10207 };
10208
10209 void CollectGarbage() {
10210 block_->Wait();
10211 while (gc_during_apply_ < kRequiredGCs) {
10212 {
10213 v8::Locker lock;
Steve Block44f0eee2011-05-26 01:26:41 +010010214 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +000010215 gc_count_++;
10216 }
10217 i::OS::Sleep(1);
10218 }
10219 gc_success_ = true;
10220 }
10221
10222 void LongRunningApply() {
10223 block_->Signal();
10224 int rounds = 0;
10225 while (gc_during_apply_ < kRequiredGCs) {
10226 int gc_before = gc_count_;
10227 {
10228 const char* c_source =
10229 "function do_very_little(bar) {"
10230 " this.foo = bar;"
10231 "}"
10232 "for (var i = 0; i < 100000; i++) {"
10233 " do_very_little.apply(this, ['bar']);"
10234 "}";
10235 Local<String> source = String::New(c_source);
10236 Local<Script> script = Script::Compile(source);
10237 Local<Value> result = script->Run();
10238 // Check that no exception was thrown.
10239 CHECK(!result.IsEmpty());
10240 }
10241 int gc_after = gc_count_;
10242 gc_during_apply_ += gc_after - gc_before;
10243 rounds++;
10244 }
10245 apply_success_ = true;
10246 }
10247
10248 i::Semaphore* block_;
10249 int gc_count_;
10250 int gc_during_apply_;
10251 bool apply_success_;
10252 bool gc_success_;
10253};
10254
10255
10256// Test that nothing bad happens if we get a preemption just when we were
10257// about to do an apply().
10258TEST(ApplyInterruption) {
10259 v8::Locker lock;
10260 v8::V8::Initialize();
10261 v8::HandleScope scope;
10262 Local<Context> local_env;
10263 {
10264 LocalContext env;
10265 local_env = env.local();
10266 }
10267
10268 // Local context should still be live.
10269 CHECK(!local_env.IsEmpty());
10270 local_env->Enter();
10271
10272 // Should complete without problems.
10273 ApplyInterruptTest().RunTest();
10274
10275 local_env->Exit();
10276}
10277
10278
10279// Verify that we can clone an object
10280TEST(ObjectClone) {
10281 v8::HandleScope scope;
10282 LocalContext env;
10283
10284 const char* sample =
10285 "var rv = {};" \
10286 "rv.alpha = 'hello';" \
10287 "rv.beta = 123;" \
10288 "rv;";
10289
10290 // Create an object, verify basics.
10291 Local<Value> val = CompileRun(sample);
10292 CHECK(val->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +010010293 Local<v8::Object> obj = val.As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +000010294 obj->Set(v8_str("gamma"), v8_str("cloneme"));
10295
10296 CHECK_EQ(v8_str("hello"), obj->Get(v8_str("alpha")));
10297 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta")));
10298 CHECK_EQ(v8_str("cloneme"), obj->Get(v8_str("gamma")));
10299
10300 // Clone it.
10301 Local<v8::Object> clone = obj->Clone();
10302 CHECK_EQ(v8_str("hello"), clone->Get(v8_str("alpha")));
10303 CHECK_EQ(v8::Integer::New(123), clone->Get(v8_str("beta")));
10304 CHECK_EQ(v8_str("cloneme"), clone->Get(v8_str("gamma")));
10305
10306 // Set a property on the clone, verify each object.
10307 clone->Set(v8_str("beta"), v8::Integer::New(456));
10308 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta")));
10309 CHECK_EQ(v8::Integer::New(456), clone->Get(v8_str("beta")));
10310}
10311
10312
10313class AsciiVectorResource : public v8::String::ExternalAsciiStringResource {
10314 public:
10315 explicit AsciiVectorResource(i::Vector<const char> vector)
10316 : data_(vector) {}
10317 virtual ~AsciiVectorResource() {}
10318 virtual size_t length() const { return data_.length(); }
10319 virtual const char* data() const { return data_.start(); }
10320 private:
10321 i::Vector<const char> data_;
10322};
10323
10324
10325class UC16VectorResource : public v8::String::ExternalStringResource {
10326 public:
10327 explicit UC16VectorResource(i::Vector<const i::uc16> vector)
10328 : data_(vector) {}
10329 virtual ~UC16VectorResource() {}
10330 virtual size_t length() const { return data_.length(); }
10331 virtual const i::uc16* data() const { return data_.start(); }
10332 private:
10333 i::Vector<const i::uc16> data_;
10334};
10335
10336
10337static void MorphAString(i::String* string,
10338 AsciiVectorResource* ascii_resource,
10339 UC16VectorResource* uc16_resource) {
10340 CHECK(i::StringShape(string).IsExternal());
10341 if (string->IsAsciiRepresentation()) {
10342 // Check old map is not symbol or long.
Steve Block44f0eee2011-05-26 01:26:41 +010010343 CHECK(string->map() == HEAP->external_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +000010344 // Morph external string to be TwoByte string.
Steve Block44f0eee2011-05-26 01:26:41 +010010345 string->set_map(HEAP->external_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +000010346 i::ExternalTwoByteString* morphed =
10347 i::ExternalTwoByteString::cast(string);
10348 morphed->set_resource(uc16_resource);
10349 } else {
10350 // Check old map is not symbol or long.
Steve Block44f0eee2011-05-26 01:26:41 +010010351 CHECK(string->map() == HEAP->external_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +000010352 // Morph external string to be ASCII string.
Steve Block44f0eee2011-05-26 01:26:41 +010010353 string->set_map(HEAP->external_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +000010354 i::ExternalAsciiString* morphed =
10355 i::ExternalAsciiString::cast(string);
10356 morphed->set_resource(ascii_resource);
10357 }
10358}
10359
10360
10361// Test that we can still flatten a string if the components it is built up
10362// from have been turned into 16 bit strings in the mean time.
10363THREADED_TEST(MorphCompositeStringTest) {
10364 const char* c_string = "Now is the time for all good men"
10365 " to come to the aid of the party";
10366 uint16_t* two_byte_string = AsciiToTwoByteString(c_string);
10367 {
10368 v8::HandleScope scope;
10369 LocalContext env;
10370 AsciiVectorResource ascii_resource(
Steve Blockd0582a62009-12-15 09:54:21 +000010371 i::Vector<const char>(c_string, i::StrLength(c_string)));
Steve Blocka7e24c12009-10-30 11:49:00 +000010372 UC16VectorResource uc16_resource(
Steve Blockd0582a62009-12-15 09:54:21 +000010373 i::Vector<const uint16_t>(two_byte_string,
10374 i::StrLength(c_string)));
Steve Blocka7e24c12009-10-30 11:49:00 +000010375
10376 Local<String> lhs(v8::Utils::ToLocal(
Steve Block44f0eee2011-05-26 01:26:41 +010010377 FACTORY->NewExternalStringFromAscii(&ascii_resource)));
Steve Blocka7e24c12009-10-30 11:49:00 +000010378 Local<String> rhs(v8::Utils::ToLocal(
Steve Block44f0eee2011-05-26 01:26:41 +010010379 FACTORY->NewExternalStringFromAscii(&ascii_resource)));
Steve Blocka7e24c12009-10-30 11:49:00 +000010380
10381 env->Global()->Set(v8_str("lhs"), lhs);
10382 env->Global()->Set(v8_str("rhs"), rhs);
10383
10384 CompileRun(
10385 "var cons = lhs + rhs;"
10386 "var slice = lhs.substring(1, lhs.length - 1);"
10387 "var slice_on_cons = (lhs + rhs).substring(1, lhs.length *2 - 1);");
10388
10389 MorphAString(*v8::Utils::OpenHandle(*lhs), &ascii_resource, &uc16_resource);
10390 MorphAString(*v8::Utils::OpenHandle(*rhs), &ascii_resource, &uc16_resource);
10391
10392 // Now do some stuff to make sure the strings are flattened, etc.
10393 CompileRun(
10394 "/[^a-z]/.test(cons);"
10395 "/[^a-z]/.test(slice);"
10396 "/[^a-z]/.test(slice_on_cons);");
10397 const char* expected_cons =
10398 "Now is the time for all good men to come to the aid of the party"
10399 "Now is the time for all good men to come to the aid of the party";
10400 const char* expected_slice =
10401 "ow is the time for all good men to come to the aid of the part";
10402 const char* expected_slice_on_cons =
10403 "ow is the time for all good men to come to the aid of the party"
10404 "Now is the time for all good men to come to the aid of the part";
10405 CHECK_EQ(String::New(expected_cons),
10406 env->Global()->Get(v8_str("cons")));
10407 CHECK_EQ(String::New(expected_slice),
10408 env->Global()->Get(v8_str("slice")));
10409 CHECK_EQ(String::New(expected_slice_on_cons),
10410 env->Global()->Get(v8_str("slice_on_cons")));
10411 }
Ben Murdoch3bec4d22010-07-22 14:51:16 +010010412 i::DeleteArray(two_byte_string);
Steve Blocka7e24c12009-10-30 11:49:00 +000010413}
10414
10415
10416TEST(CompileExternalTwoByteSource) {
10417 v8::HandleScope scope;
10418 LocalContext context;
10419
10420 // This is a very short list of sources, which currently is to check for a
10421 // regression caused by r2703.
10422 const char* ascii_sources[] = {
10423 "0.5",
10424 "-0.5", // This mainly testes PushBack in the Scanner.
10425 "--0.5", // This mainly testes PushBack in the Scanner.
10426 NULL
10427 };
10428
10429 // Compile the sources as external two byte strings.
10430 for (int i = 0; ascii_sources[i] != NULL; i++) {
10431 uint16_t* two_byte_string = AsciiToTwoByteString(ascii_sources[i]);
10432 UC16VectorResource uc16_resource(
Steve Blockd0582a62009-12-15 09:54:21 +000010433 i::Vector<const uint16_t>(two_byte_string,
10434 i::StrLength(ascii_sources[i])));
Steve Blocka7e24c12009-10-30 11:49:00 +000010435 v8::Local<v8::String> source = v8::String::NewExternal(&uc16_resource);
10436 v8::Script::Compile(source);
Ben Murdoch3bec4d22010-07-22 14:51:16 +010010437 i::DeleteArray(two_byte_string);
Steve Blocka7e24c12009-10-30 11:49:00 +000010438 }
10439}
10440
10441
10442class RegExpStringModificationTest {
10443 public:
10444 RegExpStringModificationTest()
10445 : block_(i::OS::CreateSemaphore(0)),
10446 morphs_(0),
10447 morphs_during_regexp_(0),
10448 ascii_resource_(i::Vector<const char>("aaaaaaaaaaaaaab", 15)),
10449 uc16_resource_(i::Vector<const uint16_t>(two_byte_content_, 15)) {}
10450 ~RegExpStringModificationTest() { delete block_; }
10451 void RunTest() {
10452 regexp_success_ = false;
10453 morph_success_ = false;
10454
10455 // Initialize the contents of two_byte_content_ to be a uc16 representation
10456 // of "aaaaaaaaaaaaaab".
10457 for (int i = 0; i < 14; i++) {
10458 two_byte_content_[i] = 'a';
10459 }
10460 two_byte_content_[14] = 'b';
10461
10462 // Create the input string for the regexp - the one we are going to change
10463 // properties of.
Steve Block44f0eee2011-05-26 01:26:41 +010010464 input_ = FACTORY->NewExternalStringFromAscii(&ascii_resource_);
Steve Blocka7e24c12009-10-30 11:49:00 +000010465
10466 // Inject the input as a global variable.
10467 i::Handle<i::String> input_name =
Steve Block44f0eee2011-05-26 01:26:41 +010010468 FACTORY->NewStringFromAscii(i::Vector<const char>("input", 5));
10469 i::Isolate::Current()->global_context()->global()->SetProperty(
Ben Murdoche0cee9b2011-05-25 10:26:03 +010010470 *input_name,
10471 *input_,
10472 NONE,
10473 i::kNonStrictMode)->ToObjectChecked();
Steve Blocka7e24c12009-10-30 11:49:00 +000010474
Steve Block44f0eee2011-05-26 01:26:41 +010010475 MorphThread morph_thread(i::Isolate::Current(), this);
Steve Blocka7e24c12009-10-30 11:49:00 +000010476 morph_thread.Start();
10477 v8::Locker::StartPreemption(1);
10478 LongRunningRegExp();
10479 {
10480 v8::Unlocker unlock;
10481 morph_thread.Join();
10482 }
10483 v8::Locker::StopPreemption();
10484 CHECK(regexp_success_);
10485 CHECK(morph_success_);
10486 }
10487 private:
10488
10489 // Number of string modifications required.
10490 static const int kRequiredModifications = 5;
10491 static const int kMaxModifications = 100;
10492
10493 class MorphThread : public i::Thread {
10494 public:
Steve Block44f0eee2011-05-26 01:26:41 +010010495 explicit MorphThread(i::Isolate* isolate,
10496 RegExpStringModificationTest* test)
10497 : Thread(isolate, "MorphThread"), test_(test) {}
Steve Blocka7e24c12009-10-30 11:49:00 +000010498 virtual void Run() {
10499 test_->MorphString();
10500 }
10501 private:
10502 RegExpStringModificationTest* test_;
10503 };
10504
10505 void MorphString() {
10506 block_->Wait();
10507 while (morphs_during_regexp_ < kRequiredModifications &&
10508 morphs_ < kMaxModifications) {
10509 {
10510 v8::Locker lock;
10511 // Swap string between ascii and two-byte representation.
10512 i::String* string = *input_;
10513 MorphAString(string, &ascii_resource_, &uc16_resource_);
10514 morphs_++;
10515 }
10516 i::OS::Sleep(1);
10517 }
10518 morph_success_ = true;
10519 }
10520
10521 void LongRunningRegExp() {
10522 block_->Signal(); // Enable morphing thread on next preemption.
10523 while (morphs_during_regexp_ < kRequiredModifications &&
10524 morphs_ < kMaxModifications) {
10525 int morphs_before = morphs_;
10526 {
Steve Block791712a2010-08-27 10:21:07 +010010527 v8::HandleScope scope;
Steve Blocka7e24c12009-10-30 11:49:00 +000010528 // Match 15-30 "a"'s against 14 and a "b".
10529 const char* c_source =
10530 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
10531 ".exec(input) === null";
10532 Local<String> source = String::New(c_source);
10533 Local<Script> script = Script::Compile(source);
10534 Local<Value> result = script->Run();
10535 CHECK(result->IsTrue());
10536 }
10537 int morphs_after = morphs_;
10538 morphs_during_regexp_ += morphs_after - morphs_before;
10539 }
10540 regexp_success_ = true;
10541 }
10542
10543 i::uc16 two_byte_content_[15];
10544 i::Semaphore* block_;
10545 int morphs_;
10546 int morphs_during_regexp_;
10547 bool regexp_success_;
10548 bool morph_success_;
10549 i::Handle<i::String> input_;
10550 AsciiVectorResource ascii_resource_;
10551 UC16VectorResource uc16_resource_;
10552};
10553
10554
10555// Test that a regular expression execution can be interrupted and
10556// the string changed without failing.
10557TEST(RegExpStringModification) {
10558 v8::Locker lock;
10559 v8::V8::Initialize();
10560 v8::HandleScope scope;
10561 Local<Context> local_env;
10562 {
10563 LocalContext env;
10564 local_env = env.local();
10565 }
10566
10567 // Local context should still be live.
10568 CHECK(!local_env.IsEmpty());
10569 local_env->Enter();
10570
10571 // Should complete without problems.
10572 RegExpStringModificationTest().RunTest();
10573
10574 local_env->Exit();
10575}
10576
10577
10578// Test that we can set a property on the global object even if there
10579// is a read-only property in the prototype chain.
10580TEST(ReadOnlyPropertyInGlobalProto) {
10581 v8::HandleScope scope;
10582 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
10583 LocalContext context(0, templ);
10584 v8::Handle<v8::Object> global = context->Global();
10585 v8::Handle<v8::Object> global_proto =
10586 v8::Handle<v8::Object>::Cast(global->Get(v8_str("__proto__")));
10587 global_proto->Set(v8_str("x"), v8::Integer::New(0), v8::ReadOnly);
10588 global_proto->Set(v8_str("y"), v8::Integer::New(0), v8::ReadOnly);
10589 // Check without 'eval' or 'with'.
10590 v8::Handle<v8::Value> res =
10591 CompileRun("function f() { x = 42; return x; }; f()");
10592 // Check with 'eval'.
10593 res = CompileRun("function f() { eval('1'); y = 42; return y; }; f()");
10594 CHECK_EQ(v8::Integer::New(42), res);
10595 // Check with 'with'.
10596 res = CompileRun("function f() { with (this) { y = 42 }; return y; }; f()");
10597 CHECK_EQ(v8::Integer::New(42), res);
10598}
10599
10600static int force_set_set_count = 0;
10601static int force_set_get_count = 0;
10602bool pass_on_get = false;
10603
10604static v8::Handle<v8::Value> ForceSetGetter(v8::Local<v8::String> name,
10605 const v8::AccessorInfo& info) {
10606 force_set_get_count++;
10607 if (pass_on_get) {
10608 return v8::Handle<v8::Value>();
10609 } else {
10610 return v8::Int32::New(3);
10611 }
10612}
10613
10614static void ForceSetSetter(v8::Local<v8::String> name,
10615 v8::Local<v8::Value> value,
10616 const v8::AccessorInfo& info) {
10617 force_set_set_count++;
10618}
10619
10620static v8::Handle<v8::Value> ForceSetInterceptSetter(
10621 v8::Local<v8::String> name,
10622 v8::Local<v8::Value> value,
10623 const v8::AccessorInfo& info) {
10624 force_set_set_count++;
10625 return v8::Undefined();
10626}
10627
10628TEST(ForceSet) {
10629 force_set_get_count = 0;
10630 force_set_set_count = 0;
10631 pass_on_get = false;
10632
10633 v8::HandleScope scope;
10634 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
10635 v8::Handle<v8::String> access_property = v8::String::New("a");
10636 templ->SetAccessor(access_property, ForceSetGetter, ForceSetSetter);
10637 LocalContext context(NULL, templ);
10638 v8::Handle<v8::Object> global = context->Global();
10639
10640 // Ordinary properties
10641 v8::Handle<v8::String> simple_property = v8::String::New("p");
10642 global->Set(simple_property, v8::Int32::New(4), v8::ReadOnly);
10643 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
10644 // This should fail because the property is read-only
10645 global->Set(simple_property, v8::Int32::New(5));
10646 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
10647 // This should succeed even though the property is read-only
10648 global->ForceSet(simple_property, v8::Int32::New(6));
10649 CHECK_EQ(6, global->Get(simple_property)->Int32Value());
10650
10651 // Accessors
10652 CHECK_EQ(0, force_set_set_count);
10653 CHECK_EQ(0, force_set_get_count);
10654 CHECK_EQ(3, global->Get(access_property)->Int32Value());
10655 // CHECK_EQ the property shouldn't override it, just call the setter
10656 // which in this case does nothing.
10657 global->Set(access_property, v8::Int32::New(7));
10658 CHECK_EQ(3, global->Get(access_property)->Int32Value());
10659 CHECK_EQ(1, force_set_set_count);
10660 CHECK_EQ(2, force_set_get_count);
10661 // Forcing the property to be set should override the accessor without
10662 // calling it
10663 global->ForceSet(access_property, v8::Int32::New(8));
10664 CHECK_EQ(8, global->Get(access_property)->Int32Value());
10665 CHECK_EQ(1, force_set_set_count);
10666 CHECK_EQ(2, force_set_get_count);
10667}
10668
10669TEST(ForceSetWithInterceptor) {
10670 force_set_get_count = 0;
10671 force_set_set_count = 0;
10672 pass_on_get = false;
10673
10674 v8::HandleScope scope;
10675 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
10676 templ->SetNamedPropertyHandler(ForceSetGetter, ForceSetInterceptSetter);
10677 LocalContext context(NULL, templ);
10678 v8::Handle<v8::Object> global = context->Global();
10679
10680 v8::Handle<v8::String> some_property = v8::String::New("a");
10681 CHECK_EQ(0, force_set_set_count);
10682 CHECK_EQ(0, force_set_get_count);
10683 CHECK_EQ(3, global->Get(some_property)->Int32Value());
10684 // Setting the property shouldn't override it, just call the setter
10685 // which in this case does nothing.
10686 global->Set(some_property, v8::Int32::New(7));
10687 CHECK_EQ(3, global->Get(some_property)->Int32Value());
10688 CHECK_EQ(1, force_set_set_count);
10689 CHECK_EQ(2, force_set_get_count);
10690 // Getting the property when the interceptor returns an empty handle
10691 // should yield undefined, since the property isn't present on the
10692 // object itself yet.
10693 pass_on_get = true;
10694 CHECK(global->Get(some_property)->IsUndefined());
10695 CHECK_EQ(1, force_set_set_count);
10696 CHECK_EQ(3, force_set_get_count);
10697 // Forcing the property to be set should cause the value to be
10698 // set locally without calling the interceptor.
10699 global->ForceSet(some_property, v8::Int32::New(8));
10700 CHECK_EQ(8, global->Get(some_property)->Int32Value());
10701 CHECK_EQ(1, force_set_set_count);
10702 CHECK_EQ(4, force_set_get_count);
10703 // Reenabling the interceptor should cause it to take precedence over
10704 // the property
10705 pass_on_get = false;
10706 CHECK_EQ(3, global->Get(some_property)->Int32Value());
10707 CHECK_EQ(1, force_set_set_count);
10708 CHECK_EQ(5, force_set_get_count);
10709 // The interceptor should also work for other properties
10710 CHECK_EQ(3, global->Get(v8::String::New("b"))->Int32Value());
10711 CHECK_EQ(1, force_set_set_count);
10712 CHECK_EQ(6, force_set_get_count);
10713}
10714
10715
10716THREADED_TEST(ForceDelete) {
10717 v8::HandleScope scope;
10718 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
10719 LocalContext context(NULL, templ);
10720 v8::Handle<v8::Object> global = context->Global();
10721
10722 // Ordinary properties
10723 v8::Handle<v8::String> simple_property = v8::String::New("p");
10724 global->Set(simple_property, v8::Int32::New(4), v8::DontDelete);
10725 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
10726 // This should fail because the property is dont-delete.
10727 CHECK(!global->Delete(simple_property));
10728 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
10729 // This should succeed even though the property is dont-delete.
10730 CHECK(global->ForceDelete(simple_property));
10731 CHECK(global->Get(simple_property)->IsUndefined());
10732}
10733
10734
10735static int force_delete_interceptor_count = 0;
10736static bool pass_on_delete = false;
10737
10738
10739static v8::Handle<v8::Boolean> ForceDeleteDeleter(
10740 v8::Local<v8::String> name,
10741 const v8::AccessorInfo& info) {
10742 force_delete_interceptor_count++;
10743 if (pass_on_delete) {
10744 return v8::Handle<v8::Boolean>();
10745 } else {
10746 return v8::True();
10747 }
10748}
10749
10750
10751THREADED_TEST(ForceDeleteWithInterceptor) {
10752 force_delete_interceptor_count = 0;
10753 pass_on_delete = false;
10754
10755 v8::HandleScope scope;
10756 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
10757 templ->SetNamedPropertyHandler(0, 0, 0, ForceDeleteDeleter);
10758 LocalContext context(NULL, templ);
10759 v8::Handle<v8::Object> global = context->Global();
10760
10761 v8::Handle<v8::String> some_property = v8::String::New("a");
10762 global->Set(some_property, v8::Integer::New(42), v8::DontDelete);
10763
10764 // Deleting a property should get intercepted and nothing should
10765 // happen.
10766 CHECK_EQ(0, force_delete_interceptor_count);
10767 CHECK(global->Delete(some_property));
10768 CHECK_EQ(1, force_delete_interceptor_count);
10769 CHECK_EQ(42, global->Get(some_property)->Int32Value());
10770 // Deleting the property when the interceptor returns an empty
10771 // handle should not delete the property since it is DontDelete.
10772 pass_on_delete = true;
10773 CHECK(!global->Delete(some_property));
10774 CHECK_EQ(2, force_delete_interceptor_count);
10775 CHECK_EQ(42, global->Get(some_property)->Int32Value());
10776 // Forcing the property to be deleted should delete the value
10777 // without calling the interceptor.
10778 CHECK(global->ForceDelete(some_property));
10779 CHECK(global->Get(some_property)->IsUndefined());
10780 CHECK_EQ(2, force_delete_interceptor_count);
10781}
10782
10783
10784// Make sure that forcing a delete invalidates any IC stubs, so we
10785// don't read the hole value.
10786THREADED_TEST(ForceDeleteIC) {
10787 v8::HandleScope scope;
10788 LocalContext context;
10789 // Create a DontDelete variable on the global object.
10790 CompileRun("this.__proto__ = { foo: 'horse' };"
10791 "var foo = 'fish';"
10792 "function f() { return foo.length; }");
10793 // Initialize the IC for foo in f.
10794 CompileRun("for (var i = 0; i < 4; i++) f();");
10795 // Make sure the value of foo is correct before the deletion.
10796 CHECK_EQ(4, CompileRun("f()")->Int32Value());
10797 // Force the deletion of foo.
10798 CHECK(context->Global()->ForceDelete(v8_str("foo")));
10799 // Make sure the value for foo is read from the prototype, and that
10800 // we don't get in trouble with reading the deleted cell value
10801 // sentinel.
10802 CHECK_EQ(5, CompileRun("f()")->Int32Value());
10803}
10804
10805
10806v8::Persistent<Context> calling_context0;
10807v8::Persistent<Context> calling_context1;
10808v8::Persistent<Context> calling_context2;
10809
10810
10811// Check that the call to the callback is initiated in
10812// calling_context2, the directly calling context is calling_context1
10813// and the callback itself is in calling_context0.
10814static v8::Handle<Value> GetCallingContextCallback(const v8::Arguments& args) {
10815 ApiTestFuzzer::Fuzz();
10816 CHECK(Context::GetCurrent() == calling_context0);
10817 CHECK(Context::GetCalling() == calling_context1);
10818 CHECK(Context::GetEntered() == calling_context2);
10819 return v8::Integer::New(42);
10820}
10821
10822
10823THREADED_TEST(GetCallingContext) {
10824 v8::HandleScope scope;
10825
10826 calling_context0 = Context::New();
10827 calling_context1 = Context::New();
10828 calling_context2 = Context::New();
10829
10830 // Allow cross-domain access.
10831 Local<String> token = v8_str("<security token>");
10832 calling_context0->SetSecurityToken(token);
10833 calling_context1->SetSecurityToken(token);
10834 calling_context2->SetSecurityToken(token);
10835
10836 // Create an object with a C++ callback in context0.
10837 calling_context0->Enter();
10838 Local<v8::FunctionTemplate> callback_templ =
10839 v8::FunctionTemplate::New(GetCallingContextCallback);
10840 calling_context0->Global()->Set(v8_str("callback"),
10841 callback_templ->GetFunction());
10842 calling_context0->Exit();
10843
10844 // Expose context0 in context1 and setup a function that calls the
10845 // callback function.
10846 calling_context1->Enter();
10847 calling_context1->Global()->Set(v8_str("context0"),
10848 calling_context0->Global());
10849 CompileRun("function f() { context0.callback() }");
10850 calling_context1->Exit();
10851
10852 // Expose context1 in context2 and call the callback function in
10853 // context0 indirectly through f in context1.
10854 calling_context2->Enter();
10855 calling_context2->Global()->Set(v8_str("context1"),
10856 calling_context1->Global());
10857 CompileRun("context1.f()");
10858 calling_context2->Exit();
10859
10860 // Dispose the contexts to allow them to be garbage collected.
10861 calling_context0.Dispose();
10862 calling_context1.Dispose();
10863 calling_context2.Dispose();
10864 calling_context0.Clear();
10865 calling_context1.Clear();
10866 calling_context2.Clear();
10867}
10868
10869
10870// Check that a variable declaration with no explicit initialization
10871// value does not shadow an existing property in the prototype chain.
10872//
10873// This is consistent with Firefox and Safari.
10874//
10875// See http://crbug.com/12548.
10876THREADED_TEST(InitGlobalVarInProtoChain) {
10877 v8::HandleScope scope;
10878 LocalContext context;
10879 // Introduce a variable in the prototype chain.
10880 CompileRun("__proto__.x = 42");
10881 v8::Handle<v8::Value> result = CompileRun("var x; x");
10882 CHECK(!result->IsUndefined());
10883 CHECK_EQ(42, result->Int32Value());
10884}
10885
10886
10887// Regression test for issue 398.
10888// If a function is added to an object, creating a constant function
10889// field, and the result is cloned, replacing the constant function on the
10890// original should not affect the clone.
10891// See http://code.google.com/p/v8/issues/detail?id=398
10892THREADED_TEST(ReplaceConstantFunction) {
10893 v8::HandleScope scope;
10894 LocalContext context;
10895 v8::Handle<v8::Object> obj = v8::Object::New();
10896 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New();
10897 v8::Handle<v8::String> foo_string = v8::String::New("foo");
10898 obj->Set(foo_string, func_templ->GetFunction());
10899 v8::Handle<v8::Object> obj_clone = obj->Clone();
10900 obj_clone->Set(foo_string, v8::String::New("Hello"));
10901 CHECK(!obj->Get(foo_string)->IsUndefined());
10902}
10903
10904
10905// Regression test for http://crbug.com/16276.
10906THREADED_TEST(Regress16276) {
10907 v8::HandleScope scope;
10908 LocalContext context;
10909 // Force the IC in f to be a dictionary load IC.
10910 CompileRun("function f(obj) { return obj.x; }\n"
10911 "var obj = { x: { foo: 42 }, y: 87 };\n"
10912 "var x = obj.x;\n"
10913 "delete obj.y;\n"
10914 "for (var i = 0; i < 5; i++) f(obj);");
10915 // Detach the global object to make 'this' refer directly to the
10916 // global object (not the proxy), and make sure that the dictionary
10917 // load IC doesn't mess up loading directly from the global object.
10918 context->DetachGlobal();
10919 CHECK_EQ(42, CompileRun("f(this).foo")->Int32Value());
10920}
10921
10922
10923THREADED_TEST(PixelArray) {
10924 v8::HandleScope scope;
10925 LocalContext context;
Steve Blockd0582a62009-12-15 09:54:21 +000010926 const int kElementCount = 260;
Steve Blocka7e24c12009-10-30 11:49:00 +000010927 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(kElementCount));
Steve Block44f0eee2011-05-26 01:26:41 +010010928 i::Handle<i::ExternalPixelArray> pixels =
10929 i::Handle<i::ExternalPixelArray>::cast(
10930 FACTORY->NewExternalArray(kElementCount,
10931 v8::kExternalPixelArray,
10932 pixel_data));
10933 HEAP->CollectAllGarbage(false); // Force GC to trigger verification.
Steve Blocka7e24c12009-10-30 11:49:00 +000010934 for (int i = 0; i < kElementCount; i++) {
Steve Blockd0582a62009-12-15 09:54:21 +000010935 pixels->set(i, i % 256);
Steve Blocka7e24c12009-10-30 11:49:00 +000010936 }
Steve Block44f0eee2011-05-26 01:26:41 +010010937 HEAP->CollectAllGarbage(false); // Force GC to trigger verification.
Steve Blocka7e24c12009-10-30 11:49:00 +000010938 for (int i = 0; i < kElementCount; i++) {
Steve Blockd0582a62009-12-15 09:54:21 +000010939 CHECK_EQ(i % 256, pixels->get(i));
10940 CHECK_EQ(i % 256, pixel_data[i]);
Steve Blocka7e24c12009-10-30 11:49:00 +000010941 }
10942
10943 v8::Handle<v8::Object> obj = v8::Object::New();
10944 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj);
10945 // Set the elements to be the pixels.
10946 // jsobj->set_elements(*pixels);
10947 obj->SetIndexedPropertiesToPixelData(pixel_data, kElementCount);
John Reck59135872010-11-02 12:39:01 -070010948 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000010949 obj->Set(v8_str("field"), v8::Int32::New(1503));
10950 context->Global()->Set(v8_str("pixels"), obj);
10951 v8::Handle<v8::Value> result = CompileRun("pixels.field");
10952 CHECK_EQ(1503, result->Int32Value());
10953 result = CompileRun("pixels[1]");
10954 CHECK_EQ(1, result->Int32Value());
10955
10956 result = CompileRun("var sum = 0;"
10957 "for (var i = 0; i < 8; i++) {"
10958 " sum += pixels[i] = pixels[i] = -i;"
10959 "}"
10960 "sum;");
10961 CHECK_EQ(-28, result->Int32Value());
10962
10963 result = CompileRun("var sum = 0;"
10964 "for (var i = 0; i < 8; i++) {"
10965 " sum += pixels[i] = pixels[i] = 0;"
10966 "}"
10967 "sum;");
10968 CHECK_EQ(0, result->Int32Value());
10969
10970 result = CompileRun("var sum = 0;"
10971 "for (var i = 0; i < 8; i++) {"
10972 " sum += pixels[i] = pixels[i] = 255;"
10973 "}"
10974 "sum;");
10975 CHECK_EQ(8 * 255, result->Int32Value());
10976
10977 result = CompileRun("var sum = 0;"
10978 "for (var i = 0; i < 8; i++) {"
10979 " sum += pixels[i] = pixels[i] = 256 + i;"
10980 "}"
10981 "sum;");
10982 CHECK_EQ(2076, result->Int32Value());
10983
10984 result = CompileRun("var sum = 0;"
10985 "for (var i = 0; i < 8; i++) {"
10986 " sum += pixels[i] = pixels[i] = i;"
10987 "}"
10988 "sum;");
10989 CHECK_EQ(28, result->Int32Value());
10990
10991 result = CompileRun("var sum = 0;"
10992 "for (var i = 0; i < 8; i++) {"
10993 " sum += pixels[i];"
10994 "}"
10995 "sum;");
10996 CHECK_EQ(28, result->Int32Value());
10997
10998 i::Handle<i::Smi> value(i::Smi::FromInt(2));
Ben Murdoche0cee9b2011-05-25 10:26:03 +010010999 i::SetElement(jsobj, 1, value, i::kNonStrictMode);
John Reck59135872010-11-02 12:39:01 -070011000 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000011001 *value.location() = i::Smi::FromInt(256);
Ben Murdoche0cee9b2011-05-25 10:26:03 +010011002 i::SetElement(jsobj, 1, value, i::kNonStrictMode);
John Reck59135872010-11-02 12:39:01 -070011003 CHECK_EQ(255,
11004 i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000011005 *value.location() = i::Smi::FromInt(-1);
Ben Murdoche0cee9b2011-05-25 10:26:03 +010011006 i::SetElement(jsobj, 1, value, i::kNonStrictMode);
John Reck59135872010-11-02 12:39:01 -070011007 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000011008
11009 result = CompileRun("for (var i = 0; i < 8; i++) {"
11010 " pixels[i] = (i * 65) - 109;"
11011 "}"
11012 "pixels[1] + pixels[6];");
11013 CHECK_EQ(255, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070011014 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0)->ToObjectChecked())->value());
11015 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value());
11016 CHECK_EQ(21,
11017 i::Smi::cast(jsobj->GetElement(2)->ToObjectChecked())->value());
11018 CHECK_EQ(86,
11019 i::Smi::cast(jsobj->GetElement(3)->ToObjectChecked())->value());
11020 CHECK_EQ(151,
11021 i::Smi::cast(jsobj->GetElement(4)->ToObjectChecked())->value());
11022 CHECK_EQ(216,
11023 i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value());
11024 CHECK_EQ(255,
11025 i::Smi::cast(jsobj->GetElement(6)->ToObjectChecked())->value());
11026 CHECK_EQ(255,
11027 i::Smi::cast(jsobj->GetElement(7)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000011028 result = CompileRun("var sum = 0;"
11029 "for (var i = 0; i < 8; i++) {"
11030 " sum += pixels[i];"
11031 "}"
11032 "sum;");
11033 CHECK_EQ(984, result->Int32Value());
11034
11035 result = CompileRun("for (var i = 0; i < 8; i++) {"
11036 " pixels[i] = (i * 1.1);"
11037 "}"
11038 "pixels[1] + pixels[6];");
11039 CHECK_EQ(8, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070011040 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0)->ToObjectChecked())->value());
11041 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value());
11042 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(2)->ToObjectChecked())->value());
11043 CHECK_EQ(3, i::Smi::cast(jsobj->GetElement(3)->ToObjectChecked())->value());
11044 CHECK_EQ(4, i::Smi::cast(jsobj->GetElement(4)->ToObjectChecked())->value());
11045 CHECK_EQ(6, i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value());
11046 CHECK_EQ(7, i::Smi::cast(jsobj->GetElement(6)->ToObjectChecked())->value());
11047 CHECK_EQ(8, i::Smi::cast(jsobj->GetElement(7)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000011048
11049 result = CompileRun("for (var i = 0; i < 8; i++) {"
11050 " pixels[7] = undefined;"
11051 "}"
11052 "pixels[7];");
11053 CHECK_EQ(0, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070011054 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(7)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000011055
11056 result = CompileRun("for (var i = 0; i < 8; i++) {"
11057 " pixels[6] = '2.3';"
11058 "}"
11059 "pixels[6];");
11060 CHECK_EQ(2, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070011061 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(6)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000011062
11063 result = CompileRun("for (var i = 0; i < 8; i++) {"
11064 " pixels[5] = NaN;"
11065 "}"
11066 "pixels[5];");
11067 CHECK_EQ(0, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070011068 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000011069
11070 result = CompileRun("for (var i = 0; i < 8; i++) {"
11071 " pixels[8] = Infinity;"
11072 "}"
11073 "pixels[8];");
11074 CHECK_EQ(255, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070011075 CHECK_EQ(255,
11076 i::Smi::cast(jsobj->GetElement(8)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000011077
11078 result = CompileRun("for (var i = 0; i < 8; i++) {"
11079 " pixels[9] = -Infinity;"
11080 "}"
11081 "pixels[9];");
11082 CHECK_EQ(0, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070011083 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(9)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000011084
11085 result = CompileRun("pixels[3] = 33;"
11086 "delete pixels[3];"
11087 "pixels[3];");
11088 CHECK_EQ(33, result->Int32Value());
11089
11090 result = CompileRun("pixels[0] = 10; pixels[1] = 11;"
11091 "pixels[2] = 12; pixels[3] = 13;"
11092 "pixels.__defineGetter__('2',"
11093 "function() { return 120; });"
11094 "pixels[2];");
11095 CHECK_EQ(12, result->Int32Value());
11096
11097 result = CompileRun("var js_array = new Array(40);"
11098 "js_array[0] = 77;"
11099 "js_array;");
11100 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
11101
11102 result = CompileRun("pixels[1] = 23;"
11103 "pixels.__proto__ = [];"
11104 "js_array.__proto__ = pixels;"
11105 "js_array.concat(pixels);");
11106 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
11107 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value());
11108
11109 result = CompileRun("pixels[1] = 23;");
11110 CHECK_EQ(23, result->Int32Value());
11111
Steve Blockd0582a62009-12-15 09:54:21 +000011112 // Test for index greater than 255. Regression test for:
11113 // http://code.google.com/p/chromium/issues/detail?id=26337.
11114 result = CompileRun("pixels[256] = 255;");
11115 CHECK_EQ(255, result->Int32Value());
11116 result = CompileRun("var i = 0;"
11117 "for (var j = 0; j < 8; j++) { i = pixels[256]; }"
11118 "i");
11119 CHECK_EQ(255, result->Int32Value());
11120
Steve Block1e0659c2011-05-24 12:43:12 +010011121 // Make sure that pixel array ICs recognize when a non-pixel array
11122 // is passed to it.
11123 result = CompileRun("function pa_load(p) {"
11124 " var sum = 0;"
11125 " for (var j = 0; j < 256; j++) { sum += p[j]; }"
11126 " return sum;"
11127 "}"
11128 "for (var i = 0; i < 256; ++i) { pixels[i] = i; }"
11129 "for (var i = 0; i < 10; ++i) { pa_load(pixels); }"
11130 "just_ints = new Object();"
11131 "for (var i = 0; i < 256; ++i) { just_ints[i] = i; }"
11132 "for (var i = 0; i < 10; ++i) {"
11133 " result = pa_load(just_ints);"
11134 "}"
11135 "result");
11136 CHECK_EQ(32640, result->Int32Value());
11137
11138 // Make sure that pixel array ICs recognize out-of-bound accesses.
11139 result = CompileRun("function pa_load(p, start) {"
11140 " var sum = 0;"
11141 " for (var j = start; j < 256; j++) { sum += p[j]; }"
11142 " return sum;"
11143 "}"
11144 "for (var i = 0; i < 256; ++i) { pixels[i] = i; }"
11145 "for (var i = 0; i < 10; ++i) { pa_load(pixels,0); }"
11146 "for (var i = 0; i < 10; ++i) {"
11147 " result = pa_load(pixels,-10);"
11148 "}"
11149 "result");
11150 CHECK_EQ(0, result->Int32Value());
11151
11152 // Make sure that generic ICs properly handles a pixel array.
11153 result = CompileRun("function pa_load(p) {"
11154 " var sum = 0;"
11155 " for (var j = 0; j < 256; j++) { sum += p[j]; }"
11156 " return sum;"
11157 "}"
11158 "for (var i = 0; i < 256; ++i) { pixels[i] = i; }"
11159 "just_ints = new Object();"
11160 "for (var i = 0; i < 256; ++i) { just_ints[i] = i; }"
11161 "for (var i = 0; i < 10; ++i) { pa_load(just_ints); }"
11162 "for (var i = 0; i < 10; ++i) {"
11163 " result = pa_load(pixels);"
11164 "}"
11165 "result");
11166 CHECK_EQ(32640, result->Int32Value());
11167
11168 // Make sure that generic load ICs recognize out-of-bound accesses in
11169 // pixel arrays.
11170 result = CompileRun("function pa_load(p, start) {"
11171 " var sum = 0;"
11172 " for (var j = start; j < 256; j++) { sum += p[j]; }"
11173 " return sum;"
11174 "}"
11175 "for (var i = 0; i < 256; ++i) { pixels[i] = i; }"
11176 "just_ints = new Object();"
11177 "for (var i = 0; i < 256; ++i) { just_ints[i] = i; }"
11178 "for (var i = 0; i < 10; ++i) { pa_load(just_ints,0); }"
11179 "for (var i = 0; i < 10; ++i) { pa_load(pixels,0); }"
11180 "for (var i = 0; i < 10; ++i) {"
11181 " result = pa_load(pixels,-10);"
11182 "}"
11183 "result");
11184 CHECK_EQ(0, result->Int32Value());
11185
11186 // Make sure that generic ICs properly handles other types than pixel
11187 // arrays (that the inlined fast pixel array test leaves the right information
11188 // in the right registers).
11189 result = CompileRun("function pa_load(p) {"
11190 " var sum = 0;"
11191 " for (var j = 0; j < 256; j++) { sum += p[j]; }"
11192 " return sum;"
11193 "}"
11194 "for (var i = 0; i < 256; ++i) { pixels[i] = i; }"
11195 "just_ints = new Object();"
11196 "for (var i = 0; i < 256; ++i) { just_ints[i] = i; }"
11197 "for (var i = 0; i < 10; ++i) { pa_load(just_ints); }"
11198 "for (var i = 0; i < 10; ++i) { pa_load(pixels); }"
11199 "sparse_array = new Object();"
11200 "for (var i = 0; i < 256; ++i) { sparse_array[i] = i; }"
11201 "sparse_array[1000000] = 3;"
11202 "for (var i = 0; i < 10; ++i) {"
11203 " result = pa_load(sparse_array);"
11204 "}"
11205 "result");
11206 CHECK_EQ(32640, result->Int32Value());
11207
Ben Murdoche0cee9b2011-05-25 10:26:03 +010011208 // Make sure that pixel array store ICs clamp values correctly.
11209 result = CompileRun("function pa_store(p) {"
11210 " for (var j = 0; j < 256; j++) { p[j] = j * 2; }"
11211 "}"
11212 "pa_store(pixels);"
11213 "var sum = 0;"
11214 "for (var j = 0; j < 256; j++) { sum += pixels[j]; }"
11215 "sum");
11216 CHECK_EQ(48896, result->Int32Value());
11217
11218 // Make sure that pixel array stores correctly handle accesses outside
11219 // of the pixel array..
11220 result = CompileRun("function pa_store(p,start) {"
11221 " for (var j = 0; j < 256; j++) {"
11222 " p[j+start] = j * 2;"
11223 " }"
11224 "}"
11225 "pa_store(pixels,0);"
11226 "pa_store(pixels,-128);"
11227 "var sum = 0;"
11228 "for (var j = 0; j < 256; j++) { sum += pixels[j]; }"
11229 "sum");
11230 CHECK_EQ(65280, result->Int32Value());
11231
11232 // Make sure that the generic store stub correctly handle accesses outside
11233 // of the pixel array..
11234 result = CompileRun("function pa_store(p,start) {"
11235 " for (var j = 0; j < 256; j++) {"
11236 " p[j+start] = j * 2;"
11237 " }"
11238 "}"
11239 "pa_store(pixels,0);"
11240 "just_ints = new Object();"
11241 "for (var i = 0; i < 256; ++i) { just_ints[i] = i; }"
11242 "pa_store(just_ints, 0);"
11243 "pa_store(pixels,-128);"
11244 "var sum = 0;"
11245 "for (var j = 0; j < 256; j++) { sum += pixels[j]; }"
11246 "sum");
11247 CHECK_EQ(65280, result->Int32Value());
11248
11249 // Make sure that the generic keyed store stub clamps pixel array values
11250 // correctly.
11251 result = CompileRun("function pa_store(p) {"
11252 " for (var j = 0; j < 256; j++) { p[j] = j * 2; }"
11253 "}"
11254 "pa_store(pixels);"
11255 "just_ints = new Object();"
11256 "pa_store(just_ints);"
11257 "pa_store(pixels);"
11258 "var sum = 0;"
11259 "for (var j = 0; j < 256; j++) { sum += pixels[j]; }"
11260 "sum");
11261 CHECK_EQ(48896, result->Int32Value());
11262
11263 // Make sure that pixel array loads are optimized by crankshaft.
Steve Block1e0659c2011-05-24 12:43:12 +010011264 result = CompileRun("function pa_load(p) {"
11265 " var sum = 0;"
11266 " for (var i=0; i<256; ++i) {"
11267 " sum += p[i];"
11268 " }"
11269 " return sum; "
11270 "}"
11271 "for (var i = 0; i < 256; ++i) { pixels[i] = i; }"
Steve Block44f0eee2011-05-26 01:26:41 +010011272 "for (var i = 0; i < 5000; ++i) {"
Steve Block1e0659c2011-05-24 12:43:12 +010011273 " result = pa_load(pixels);"
11274 "}"
11275 "result");
11276 CHECK_EQ(32640, result->Int32Value());
11277
Ben Murdoche0cee9b2011-05-25 10:26:03 +010011278 // Make sure that pixel array stores are optimized by crankshaft.
11279 result = CompileRun("function pa_init(p) {"
11280 "for (var i = 0; i < 256; ++i) { p[i] = i; }"
11281 "}"
11282 "function pa_load(p) {"
11283 " var sum = 0;"
11284 " for (var i=0; i<256; ++i) {"
11285 " sum += p[i];"
11286 " }"
11287 " return sum; "
11288 "}"
Steve Block44f0eee2011-05-26 01:26:41 +010011289 "for (var i = 0; i < 5000; ++i) {"
Ben Murdoche0cee9b2011-05-25 10:26:03 +010011290 " pa_init(pixels);"
11291 "}"
11292 "result = pa_load(pixels);"
11293 "result");
11294 CHECK_EQ(32640, result->Int32Value());
11295
Steve Blocka7e24c12009-10-30 11:49:00 +000011296 free(pixel_data);
11297}
11298
11299
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010011300THREADED_TEST(PixelArrayInfo) {
11301 v8::HandleScope scope;
11302 LocalContext context;
11303 for (int size = 0; size < 100; size += 10) {
11304 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(size));
11305 v8::Handle<v8::Object> obj = v8::Object::New();
11306 obj->SetIndexedPropertiesToPixelData(pixel_data, size);
11307 CHECK(obj->HasIndexedPropertiesInPixelData());
11308 CHECK_EQ(pixel_data, obj->GetIndexedPropertiesPixelData());
11309 CHECK_EQ(size, obj->GetIndexedPropertiesPixelDataLength());
11310 free(pixel_data);
11311 }
11312}
11313
11314
Ben Murdoche0cee9b2011-05-25 10:26:03 +010011315static v8::Handle<Value> NotHandledIndexedPropertyGetter(
11316 uint32_t index,
11317 const AccessorInfo& info) {
11318 ApiTestFuzzer::Fuzz();
11319 return v8::Handle<Value>();
11320}
11321
11322
11323static v8::Handle<Value> NotHandledIndexedPropertySetter(
11324 uint32_t index,
11325 Local<Value> value,
11326 const AccessorInfo& info) {
11327 ApiTestFuzzer::Fuzz();
11328 return v8::Handle<Value>();
11329}
11330
11331
11332THREADED_TEST(PixelArrayWithInterceptor) {
11333 v8::HandleScope scope;
11334 LocalContext context;
11335 const int kElementCount = 260;
11336 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(kElementCount));
Steve Block44f0eee2011-05-26 01:26:41 +010011337 i::Handle<i::ExternalPixelArray> pixels =
11338 i::Handle<i::ExternalPixelArray>::cast(
11339 FACTORY->NewExternalArray(kElementCount,
11340 v8::kExternalPixelArray,
11341 pixel_data));
Ben Murdoche0cee9b2011-05-25 10:26:03 +010011342 for (int i = 0; i < kElementCount; i++) {
11343 pixels->set(i, i % 256);
11344 }
11345 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
11346 templ->SetIndexedPropertyHandler(NotHandledIndexedPropertyGetter,
11347 NotHandledIndexedPropertySetter);
11348 v8::Handle<v8::Object> obj = templ->NewInstance();
11349 obj->SetIndexedPropertiesToPixelData(pixel_data, kElementCount);
11350 context->Global()->Set(v8_str("pixels"), obj);
11351 v8::Handle<v8::Value> result = CompileRun("pixels[1]");
11352 CHECK_EQ(1, result->Int32Value());
11353 result = CompileRun("var sum = 0;"
11354 "for (var i = 0; i < 8; i++) {"
11355 " sum += pixels[i] = pixels[i] = -i;"
11356 "}"
11357 "sum;");
11358 CHECK_EQ(-28, result->Int32Value());
11359 result = CompileRun("pixels.hasOwnProperty('1')");
11360 CHECK(result->BooleanValue());
11361 free(pixel_data);
11362}
11363
11364
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010011365static int ExternalArrayElementSize(v8::ExternalArrayType array_type) {
11366 switch (array_type) {
11367 case v8::kExternalByteArray:
11368 case v8::kExternalUnsignedByteArray:
Steve Block44f0eee2011-05-26 01:26:41 +010011369 case v8::kExternalPixelArray:
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010011370 return 1;
11371 break;
11372 case v8::kExternalShortArray:
11373 case v8::kExternalUnsignedShortArray:
11374 return 2;
11375 break;
11376 case v8::kExternalIntArray:
11377 case v8::kExternalUnsignedIntArray:
11378 case v8::kExternalFloatArray:
11379 return 4;
11380 break;
11381 default:
11382 UNREACHABLE();
11383 return -1;
11384 }
11385 UNREACHABLE();
11386 return -1;
11387}
11388
11389
Steve Block3ce2e202009-11-05 08:53:23 +000011390template <class ExternalArrayClass, class ElementType>
11391static void ExternalArrayTestHelper(v8::ExternalArrayType array_type,
11392 int64_t low,
11393 int64_t high) {
11394 v8::HandleScope scope;
11395 LocalContext context;
11396 const int kElementCount = 40;
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010011397 int element_size = ExternalArrayElementSize(array_type);
Steve Block3ce2e202009-11-05 08:53:23 +000011398 ElementType* array_data =
11399 static_cast<ElementType*>(malloc(kElementCount * element_size));
11400 i::Handle<ExternalArrayClass> array =
11401 i::Handle<ExternalArrayClass>::cast(
Steve Block44f0eee2011-05-26 01:26:41 +010011402 FACTORY->NewExternalArray(kElementCount, array_type, array_data));
11403 HEAP->CollectAllGarbage(false); // Force GC to trigger verification.
Steve Block3ce2e202009-11-05 08:53:23 +000011404 for (int i = 0; i < kElementCount; i++) {
11405 array->set(i, static_cast<ElementType>(i));
11406 }
Steve Block44f0eee2011-05-26 01:26:41 +010011407 HEAP->CollectAllGarbage(false); // Force GC to trigger verification.
Steve Block3ce2e202009-11-05 08:53:23 +000011408 for (int i = 0; i < kElementCount; i++) {
11409 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array->get(i)));
11410 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array_data[i]));
11411 }
11412
11413 v8::Handle<v8::Object> obj = v8::Object::New();
11414 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj);
11415 // Set the elements to be the external array.
11416 obj->SetIndexedPropertiesToExternalArrayData(array_data,
11417 array_type,
11418 kElementCount);
John Reck59135872010-11-02 12:39:01 -070011419 CHECK_EQ(
11420 1, static_cast<int>(jsobj->GetElement(1)->ToObjectChecked()->Number()));
Steve Block3ce2e202009-11-05 08:53:23 +000011421 obj->Set(v8_str("field"), v8::Int32::New(1503));
11422 context->Global()->Set(v8_str("ext_array"), obj);
11423 v8::Handle<v8::Value> result = CompileRun("ext_array.field");
11424 CHECK_EQ(1503, result->Int32Value());
11425 result = CompileRun("ext_array[1]");
11426 CHECK_EQ(1, result->Int32Value());
11427
11428 // Check pass through of assigned smis
11429 result = CompileRun("var sum = 0;"
11430 "for (var i = 0; i < 8; i++) {"
11431 " sum += ext_array[i] = ext_array[i] = -i;"
11432 "}"
11433 "sum;");
11434 CHECK_EQ(-28, result->Int32Value());
11435
11436 // Check assigned smis
11437 result = CompileRun("for (var i = 0; i < 8; i++) {"
11438 " ext_array[i] = i;"
11439 "}"
11440 "var sum = 0;"
11441 "for (var i = 0; i < 8; i++) {"
11442 " sum += ext_array[i];"
11443 "}"
11444 "sum;");
11445 CHECK_EQ(28, result->Int32Value());
11446
11447 // Check assigned smis in reverse order
11448 result = CompileRun("for (var i = 8; --i >= 0; ) {"
11449 " ext_array[i] = i;"
11450 "}"
11451 "var sum = 0;"
11452 "for (var i = 0; i < 8; i++) {"
11453 " sum += ext_array[i];"
11454 "}"
11455 "sum;");
11456 CHECK_EQ(28, result->Int32Value());
11457
11458 // Check pass through of assigned HeapNumbers
11459 result = CompileRun("var sum = 0;"
11460 "for (var i = 0; i < 16; i+=2) {"
11461 " sum += ext_array[i] = ext_array[i] = (-i * 0.5);"
11462 "}"
11463 "sum;");
11464 CHECK_EQ(-28, result->Int32Value());
11465
11466 // Check assigned HeapNumbers
11467 result = CompileRun("for (var i = 0; i < 16; i+=2) {"
11468 " ext_array[i] = (i * 0.5);"
11469 "}"
11470 "var sum = 0;"
11471 "for (var i = 0; i < 16; i+=2) {"
11472 " sum += ext_array[i];"
11473 "}"
11474 "sum;");
11475 CHECK_EQ(28, result->Int32Value());
11476
11477 // Check assigned HeapNumbers in reverse order
11478 result = CompileRun("for (var i = 14; i >= 0; i-=2) {"
11479 " ext_array[i] = (i * 0.5);"
11480 "}"
11481 "var sum = 0;"
11482 "for (var i = 0; i < 16; i+=2) {"
11483 " sum += ext_array[i];"
11484 "}"
11485 "sum;");
11486 CHECK_EQ(28, result->Int32Value());
11487
11488 i::ScopedVector<char> test_buf(1024);
11489
11490 // Check legal boundary conditions.
11491 // The repeated loads and stores ensure the ICs are exercised.
11492 const char* boundary_program =
11493 "var res = 0;"
11494 "for (var i = 0; i < 16; i++) {"
11495 " ext_array[i] = %lld;"
11496 " if (i > 8) {"
11497 " res = ext_array[i];"
11498 " }"
11499 "}"
11500 "res;";
11501 i::OS::SNPrintF(test_buf,
11502 boundary_program,
11503 low);
11504 result = CompileRun(test_buf.start());
11505 CHECK_EQ(low, result->IntegerValue());
11506
11507 i::OS::SNPrintF(test_buf,
11508 boundary_program,
11509 high);
11510 result = CompileRun(test_buf.start());
11511 CHECK_EQ(high, result->IntegerValue());
11512
11513 // Check misprediction of type in IC.
11514 result = CompileRun("var tmp_array = ext_array;"
11515 "var sum = 0;"
11516 "for (var i = 0; i < 8; i++) {"
11517 " tmp_array[i] = i;"
11518 " sum += tmp_array[i];"
11519 " if (i == 4) {"
11520 " tmp_array = {};"
11521 " }"
11522 "}"
11523 "sum;");
Steve Block44f0eee2011-05-26 01:26:41 +010011524 HEAP->CollectAllGarbage(false); // Force GC to trigger verification.
Steve Block3ce2e202009-11-05 08:53:23 +000011525 CHECK_EQ(28, result->Int32Value());
11526
11527 // Make sure out-of-range loads do not throw.
11528 i::OS::SNPrintF(test_buf,
11529 "var caught_exception = false;"
11530 "try {"
11531 " ext_array[%d];"
11532 "} catch (e) {"
11533 " caught_exception = true;"
11534 "}"
11535 "caught_exception;",
11536 kElementCount);
11537 result = CompileRun(test_buf.start());
11538 CHECK_EQ(false, result->BooleanValue());
11539
11540 // Make sure out-of-range stores do not throw.
11541 i::OS::SNPrintF(test_buf,
11542 "var caught_exception = false;"
11543 "try {"
11544 " ext_array[%d] = 1;"
11545 "} catch (e) {"
11546 " caught_exception = true;"
11547 "}"
11548 "caught_exception;",
11549 kElementCount);
11550 result = CompileRun(test_buf.start());
11551 CHECK_EQ(false, result->BooleanValue());
11552
11553 // Check other boundary conditions, values and operations.
11554 result = CompileRun("for (var i = 0; i < 8; i++) {"
11555 " ext_array[7] = undefined;"
11556 "}"
11557 "ext_array[7];");
11558 CHECK_EQ(0, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070011559 CHECK_EQ(
11560 0, static_cast<int>(jsobj->GetElement(7)->ToObjectChecked()->Number()));
Steve Block3ce2e202009-11-05 08:53:23 +000011561
11562 result = CompileRun("for (var i = 0; i < 8; i++) {"
11563 " ext_array[6] = '2.3';"
11564 "}"
11565 "ext_array[6];");
11566 CHECK_EQ(2, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070011567 CHECK_EQ(
11568 2, static_cast<int>(jsobj->GetElement(6)->ToObjectChecked()->Number()));
Steve Block3ce2e202009-11-05 08:53:23 +000011569
11570 if (array_type != v8::kExternalFloatArray) {
11571 // Though the specification doesn't state it, be explicit about
11572 // converting NaNs and +/-Infinity to zero.
11573 result = CompileRun("for (var i = 0; i < 8; i++) {"
11574 " ext_array[i] = 5;"
11575 "}"
11576 "for (var i = 0; i < 8; i++) {"
11577 " ext_array[i] = NaN;"
11578 "}"
11579 "ext_array[5];");
11580 CHECK_EQ(0, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070011581 CHECK_EQ(0,
11582 i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value());
Steve Block3ce2e202009-11-05 08:53:23 +000011583
11584 result = CompileRun("for (var i = 0; i < 8; i++) {"
11585 " ext_array[i] = 5;"
11586 "}"
11587 "for (var i = 0; i < 8; i++) {"
11588 " ext_array[i] = Infinity;"
11589 "}"
11590 "ext_array[5];");
Steve Block44f0eee2011-05-26 01:26:41 +010011591 int expected_value =
11592 (array_type == v8::kExternalPixelArray) ? 255 : 0;
11593 CHECK_EQ(expected_value, result->Int32Value());
11594 CHECK_EQ(expected_value,
John Reck59135872010-11-02 12:39:01 -070011595 i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value());
Steve Block3ce2e202009-11-05 08:53:23 +000011596
11597 result = CompileRun("for (var i = 0; i < 8; i++) {"
11598 " ext_array[i] = 5;"
11599 "}"
11600 "for (var i = 0; i < 8; i++) {"
11601 " ext_array[i] = -Infinity;"
11602 "}"
11603 "ext_array[5];");
11604 CHECK_EQ(0, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070011605 CHECK_EQ(0,
11606 i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value());
Steve Block1e0659c2011-05-24 12:43:12 +010011607
11608 // Check truncation behavior of integral arrays.
11609 const char* unsigned_data =
11610 "var source_data = [0.6, 10.6];"
11611 "var expected_results = [0, 10];";
11612 const char* signed_data =
11613 "var source_data = [0.6, 10.6, -0.6, -10.6];"
11614 "var expected_results = [0, 10, 0, -10];";
Steve Block44f0eee2011-05-26 01:26:41 +010011615 const char* pixel_data =
11616 "var source_data = [0.6, 10.6];"
11617 "var expected_results = [1, 11];";
Steve Block1e0659c2011-05-24 12:43:12 +010011618 bool is_unsigned =
11619 (array_type == v8::kExternalUnsignedByteArray ||
11620 array_type == v8::kExternalUnsignedShortArray ||
11621 array_type == v8::kExternalUnsignedIntArray);
Steve Block44f0eee2011-05-26 01:26:41 +010011622 bool is_pixel_data = array_type == v8::kExternalPixelArray;
Steve Block1e0659c2011-05-24 12:43:12 +010011623
11624 i::OS::SNPrintF(test_buf,
11625 "%s"
11626 "var all_passed = true;"
11627 "for (var i = 0; i < source_data.length; i++) {"
11628 " for (var j = 0; j < 8; j++) {"
11629 " ext_array[j] = source_data[i];"
11630 " }"
11631 " all_passed = all_passed &&"
11632 " (ext_array[5] == expected_results[i]);"
11633 "}"
11634 "all_passed;",
Steve Block44f0eee2011-05-26 01:26:41 +010011635 (is_unsigned ?
11636 unsigned_data :
11637 (is_pixel_data ? pixel_data : signed_data)));
Steve Block1e0659c2011-05-24 12:43:12 +010011638 result = CompileRun(test_buf.start());
11639 CHECK_EQ(true, result->BooleanValue());
Steve Block3ce2e202009-11-05 08:53:23 +000011640 }
11641
Steve Block44f0eee2011-05-26 01:26:41 +010011642 // Test crankshaft external array loads
11643 for (int i = 0; i < kElementCount; i++) {
11644 array->set(i, static_cast<ElementType>(i));
11645 }
11646 result = CompileRun("function ee_load_test_func(sum) {"
Ben Murdoch8b112d22011-06-08 16:22:53 +010011647 " for (var i = 0; i < 40; ++i)"
Steve Block44f0eee2011-05-26 01:26:41 +010011648 " sum += ext_array[i];"
11649 " return sum;"
11650 "}"
11651 "sum=0;"
11652 "for (var i=0;i<10000;++i) {"
11653 " sum=ee_load_test_func(sum);"
11654 "}"
11655 "sum;");
11656 CHECK_EQ(7800000, result->Int32Value());
11657
11658 // Test crankshaft external array stores
11659 result = CompileRun("function ee_store_test_func(sum) {"
Ben Murdoch8b112d22011-06-08 16:22:53 +010011660 " for (var i = 0; i < 40; ++i)"
Steve Block44f0eee2011-05-26 01:26:41 +010011661 " sum += ext_array[i] = i;"
11662 " return sum;"
11663 "}"
11664 "sum=0;"
11665 "for (var i=0;i<10000;++i) {"
11666 " sum=ee_store_test_func(sum);"
11667 "}"
11668 "sum;");
11669 CHECK_EQ(7800000, result->Int32Value());
11670
Ben Murdoch8b112d22011-06-08 16:22:53 +010011671 for (int i = 0; i < kElementCount; i++) {
11672 array->set(i, static_cast<ElementType>(i));
11673 }
11674 // Test complex assignments
11675 result = CompileRun("function ee_op_test_complex_func(sum) {"
11676 " for (var i = 0; i < 40; ++i) {"
11677 " sum += (ext_array[i] += 1);"
11678 " sum += (ext_array[i] -= 1);"
11679 " } "
11680 " return sum;"
11681 "}"
11682 "sum=0;"
11683 "for (var i=0;i<10000;++i) {"
11684 " sum=ee_op_test_complex_func(sum);"
11685 "}"
11686 "sum;");
11687 CHECK_EQ(16000000, result->Int32Value());
11688
11689 // Test count operations
11690 result = CompileRun("function ee_op_test_count_func(sum) {"
11691 " for (var i = 0; i < 40; ++i) {"
11692 " sum += (++ext_array[i]);"
11693 " sum += (--ext_array[i]);"
11694 " } "
11695 " return sum;"
11696 "}"
11697 "sum=0;"
11698 "for (var i=0;i<10000;++i) {"
11699 " sum=ee_op_test_count_func(sum);"
11700 "}"
11701 "sum;");
11702 CHECK_EQ(16000000, result->Int32Value());
11703
Steve Block3ce2e202009-11-05 08:53:23 +000011704 result = CompileRun("ext_array[3] = 33;"
11705 "delete ext_array[3];"
11706 "ext_array[3];");
11707 CHECK_EQ(33, result->Int32Value());
11708
11709 result = CompileRun("ext_array[0] = 10; ext_array[1] = 11;"
11710 "ext_array[2] = 12; ext_array[3] = 13;"
11711 "ext_array.__defineGetter__('2',"
11712 "function() { return 120; });"
11713 "ext_array[2];");
11714 CHECK_EQ(12, result->Int32Value());
11715
11716 result = CompileRun("var js_array = new Array(40);"
11717 "js_array[0] = 77;"
11718 "js_array;");
11719 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
11720
11721 result = CompileRun("ext_array[1] = 23;"
11722 "ext_array.__proto__ = [];"
11723 "js_array.__proto__ = ext_array;"
11724 "js_array.concat(ext_array);");
11725 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
11726 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value());
11727
11728 result = CompileRun("ext_array[1] = 23;");
11729 CHECK_EQ(23, result->Int32Value());
11730
Steve Blockd0582a62009-12-15 09:54:21 +000011731 // Test more complex manipulations which cause eax to contain values
11732 // that won't be completely overwritten by loads from the arrays.
11733 // This catches bugs in the instructions used for the KeyedLoadIC
11734 // for byte and word types.
11735 {
11736 const int kXSize = 300;
11737 const int kYSize = 300;
11738 const int kLargeElementCount = kXSize * kYSize * 4;
11739 ElementType* large_array_data =
11740 static_cast<ElementType*>(malloc(kLargeElementCount * element_size));
11741 i::Handle<ExternalArrayClass> large_array =
11742 i::Handle<ExternalArrayClass>::cast(
Steve Block44f0eee2011-05-26 01:26:41 +010011743 FACTORY->NewExternalArray(kLargeElementCount,
Steve Blockd0582a62009-12-15 09:54:21 +000011744 array_type,
11745 array_data));
11746 v8::Handle<v8::Object> large_obj = v8::Object::New();
11747 // Set the elements to be the external array.
11748 large_obj->SetIndexedPropertiesToExternalArrayData(large_array_data,
11749 array_type,
11750 kLargeElementCount);
11751 context->Global()->Set(v8_str("large_array"), large_obj);
11752 // Initialize contents of a few rows.
11753 for (int x = 0; x < 300; x++) {
11754 int row = 0;
11755 int offset = row * 300 * 4;
11756 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
11757 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
11758 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
11759 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
11760 row = 150;
11761 offset = row * 300 * 4;
11762 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
11763 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
11764 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
11765 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
11766 row = 298;
11767 offset = row * 300 * 4;
11768 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
11769 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
11770 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
11771 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
11772 }
11773 // The goal of the code below is to make "offset" large enough
11774 // that the computation of the index (which goes into eax) has
11775 // high bits set which will not be overwritten by a byte or short
11776 // load.
11777 result = CompileRun("var failed = false;"
11778 "var offset = 0;"
11779 "for (var i = 0; i < 300; i++) {"
11780 " if (large_array[4 * i] != 127 ||"
11781 " large_array[4 * i + 1] != 0 ||"
11782 " large_array[4 * i + 2] != 0 ||"
11783 " large_array[4 * i + 3] != 127) {"
11784 " failed = true;"
11785 " }"
11786 "}"
11787 "offset = 150 * 300 * 4;"
11788 "for (var i = 0; i < 300; i++) {"
11789 " if (large_array[offset + 4 * i] != 127 ||"
11790 " large_array[offset + 4 * i + 1] != 0 ||"
11791 " large_array[offset + 4 * i + 2] != 0 ||"
11792 " large_array[offset + 4 * i + 3] != 127) {"
11793 " failed = true;"
11794 " }"
11795 "}"
11796 "offset = 298 * 300 * 4;"
11797 "for (var i = 0; i < 300; i++) {"
11798 " if (large_array[offset + 4 * i] != 127 ||"
11799 " large_array[offset + 4 * i + 1] != 0 ||"
11800 " large_array[offset + 4 * i + 2] != 0 ||"
11801 " large_array[offset + 4 * i + 3] != 127) {"
11802 " failed = true;"
11803 " }"
11804 "}"
11805 "!failed;");
11806 CHECK_EQ(true, result->BooleanValue());
11807 free(large_array_data);
11808 }
11809
Steve Block44f0eee2011-05-26 01:26:41 +010011810 // The "" property descriptor is overloaded to store information about
11811 // the external array. Ensure that setting and accessing the "" property
11812 // works (it should overwrite the information cached about the external
11813 // array in the DescriptorArray) in various situations.
11814 result = CompileRun("ext_array[''] = 23; ext_array['']");
11815 CHECK_EQ(23, result->Int32Value());
11816
11817 // Property "" set after the external array is associated with the object.
11818 {
11819 v8::Handle<v8::Object> obj2 = v8::Object::New();
11820 obj2->Set(v8_str("ee_test_field"), v8::Int32::New(256));
11821 obj2->Set(v8_str(""), v8::Int32::New(1503));
11822 // Set the elements to be the external array.
11823 obj2->SetIndexedPropertiesToExternalArrayData(array_data,
11824 array_type,
11825 kElementCount);
11826 context->Global()->Set(v8_str("ext_array"), obj2);
11827 result = CompileRun("ext_array['']");
11828 CHECK_EQ(1503, result->Int32Value());
11829 }
11830
11831 // Property "" set after the external array is associated with the object.
11832 {
11833 v8::Handle<v8::Object> obj2 = v8::Object::New();
11834 obj2->Set(v8_str("ee_test_field_2"), v8::Int32::New(256));
11835 // Set the elements to be the external array.
11836 obj2->SetIndexedPropertiesToExternalArrayData(array_data,
11837 array_type,
11838 kElementCount);
11839 obj2->Set(v8_str(""), v8::Int32::New(1503));
11840 context->Global()->Set(v8_str("ext_array"), obj2);
11841 result = CompileRun("ext_array['']");
11842 CHECK_EQ(1503, result->Int32Value());
11843 }
11844
11845 // Should reuse the map from previous test.
11846 {
11847 v8::Handle<v8::Object> obj2 = v8::Object::New();
11848 obj2->Set(v8_str("ee_test_field_2"), v8::Int32::New(256));
11849 // Set the elements to be the external array. Should re-use the map
11850 // from previous test.
11851 obj2->SetIndexedPropertiesToExternalArrayData(array_data,
11852 array_type,
11853 kElementCount);
11854 context->Global()->Set(v8_str("ext_array"), obj2);
11855 result = CompileRun("ext_array['']");
11856 }
11857
11858 // Property "" is a constant function that shouldn't not be interfered with
11859 // when an external array is set.
11860 {
11861 v8::Handle<v8::Object> obj2 = v8::Object::New();
11862 // Start
11863 obj2->Set(v8_str("ee_test_field3"), v8::Int32::New(256));
11864
11865 // Add a constant function to an object.
11866 context->Global()->Set(v8_str("ext_array"), obj2);
11867 result = CompileRun("ext_array[''] = function() {return 1503;};"
11868 "ext_array['']();");
11869
11870 // Add an external array transition to the same map that
11871 // has the constant transition.
11872 v8::Handle<v8::Object> obj3 = v8::Object::New();
11873 obj3->Set(v8_str("ee_test_field3"), v8::Int32::New(256));
11874 obj3->SetIndexedPropertiesToExternalArrayData(array_data,
11875 array_type,
11876 kElementCount);
11877 context->Global()->Set(v8_str("ext_array"), obj3);
11878 }
11879
11880 // If a external array transition is in the map, it should get clobbered
11881 // by a constant function.
11882 {
11883 // Add an external array transition.
11884 v8::Handle<v8::Object> obj3 = v8::Object::New();
11885 obj3->Set(v8_str("ee_test_field4"), v8::Int32::New(256));
11886 obj3->SetIndexedPropertiesToExternalArrayData(array_data,
11887 array_type,
11888 kElementCount);
11889
11890 // Add a constant function to the same map that just got an external array
11891 // transition.
11892 v8::Handle<v8::Object> obj2 = v8::Object::New();
11893 obj2->Set(v8_str("ee_test_field4"), v8::Int32::New(256));
11894 context->Global()->Set(v8_str("ext_array"), obj2);
11895 result = CompileRun("ext_array[''] = function() {return 1503;};"
11896 "ext_array['']();");
11897 }
11898
Steve Block3ce2e202009-11-05 08:53:23 +000011899 free(array_data);
11900}
11901
11902
11903THREADED_TEST(ExternalByteArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010011904 ExternalArrayTestHelper<i::ExternalByteArray, int8_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000011905 v8::kExternalByteArray,
11906 -128,
11907 127);
11908}
11909
11910
11911THREADED_TEST(ExternalUnsignedByteArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010011912 ExternalArrayTestHelper<i::ExternalUnsignedByteArray, uint8_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000011913 v8::kExternalUnsignedByteArray,
11914 0,
11915 255);
11916}
11917
11918
Steve Block44f0eee2011-05-26 01:26:41 +010011919THREADED_TEST(ExternalPixelArray) {
11920 ExternalArrayTestHelper<i::ExternalPixelArray, uint8_t>(
11921 v8::kExternalPixelArray,
11922 0,
11923 255);
11924}
11925
11926
Steve Block3ce2e202009-11-05 08:53:23 +000011927THREADED_TEST(ExternalShortArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010011928 ExternalArrayTestHelper<i::ExternalShortArray, int16_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000011929 v8::kExternalShortArray,
11930 -32768,
11931 32767);
11932}
11933
11934
11935THREADED_TEST(ExternalUnsignedShortArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010011936 ExternalArrayTestHelper<i::ExternalUnsignedShortArray, uint16_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000011937 v8::kExternalUnsignedShortArray,
11938 0,
11939 65535);
11940}
11941
11942
11943THREADED_TEST(ExternalIntArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010011944 ExternalArrayTestHelper<i::ExternalIntArray, int32_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000011945 v8::kExternalIntArray,
11946 INT_MIN, // -2147483648
11947 INT_MAX); // 2147483647
11948}
11949
11950
11951THREADED_TEST(ExternalUnsignedIntArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010011952 ExternalArrayTestHelper<i::ExternalUnsignedIntArray, uint32_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000011953 v8::kExternalUnsignedIntArray,
11954 0,
11955 UINT_MAX); // 4294967295
11956}
11957
11958
11959THREADED_TEST(ExternalFloatArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010011960 ExternalArrayTestHelper<i::ExternalFloatArray, float>(
Steve Block3ce2e202009-11-05 08:53:23 +000011961 v8::kExternalFloatArray,
11962 -500,
11963 500);
11964}
11965
11966
11967THREADED_TEST(ExternalArrays) {
11968 TestExternalByteArray();
11969 TestExternalUnsignedByteArray();
11970 TestExternalShortArray();
11971 TestExternalUnsignedShortArray();
11972 TestExternalIntArray();
11973 TestExternalUnsignedIntArray();
11974 TestExternalFloatArray();
11975}
11976
11977
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010011978void ExternalArrayInfoTestHelper(v8::ExternalArrayType array_type) {
11979 v8::HandleScope scope;
11980 LocalContext context;
11981 for (int size = 0; size < 100; size += 10) {
11982 int element_size = ExternalArrayElementSize(array_type);
11983 void* external_data = malloc(size * element_size);
11984 v8::Handle<v8::Object> obj = v8::Object::New();
11985 obj->SetIndexedPropertiesToExternalArrayData(
11986 external_data, array_type, size);
11987 CHECK(obj->HasIndexedPropertiesInExternalArrayData());
11988 CHECK_EQ(external_data, obj->GetIndexedPropertiesExternalArrayData());
11989 CHECK_EQ(array_type, obj->GetIndexedPropertiesExternalArrayDataType());
11990 CHECK_EQ(size, obj->GetIndexedPropertiesExternalArrayDataLength());
11991 free(external_data);
11992 }
11993}
11994
11995
11996THREADED_TEST(ExternalArrayInfo) {
11997 ExternalArrayInfoTestHelper(v8::kExternalByteArray);
11998 ExternalArrayInfoTestHelper(v8::kExternalUnsignedByteArray);
11999 ExternalArrayInfoTestHelper(v8::kExternalShortArray);
12000 ExternalArrayInfoTestHelper(v8::kExternalUnsignedShortArray);
12001 ExternalArrayInfoTestHelper(v8::kExternalIntArray);
12002 ExternalArrayInfoTestHelper(v8::kExternalUnsignedIntArray);
12003 ExternalArrayInfoTestHelper(v8::kExternalFloatArray);
Steve Block44f0eee2011-05-26 01:26:41 +010012004 ExternalArrayInfoTestHelper(v8::kExternalPixelArray);
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010012005}
12006
12007
Steve Blocka7e24c12009-10-30 11:49:00 +000012008THREADED_TEST(ScriptContextDependence) {
12009 v8::HandleScope scope;
12010 LocalContext c1;
12011 const char *source = "foo";
12012 v8::Handle<v8::Script> dep = v8::Script::Compile(v8::String::New(source));
12013 v8::Handle<v8::Script> indep = v8::Script::New(v8::String::New(source));
12014 c1->Global()->Set(v8::String::New("foo"), v8::Integer::New(100));
12015 CHECK_EQ(dep->Run()->Int32Value(), 100);
12016 CHECK_EQ(indep->Run()->Int32Value(), 100);
12017 LocalContext c2;
12018 c2->Global()->Set(v8::String::New("foo"), v8::Integer::New(101));
12019 CHECK_EQ(dep->Run()->Int32Value(), 100);
12020 CHECK_EQ(indep->Run()->Int32Value(), 101);
12021}
12022
12023
12024THREADED_TEST(StackTrace) {
12025 v8::HandleScope scope;
12026 LocalContext context;
12027 v8::TryCatch try_catch;
12028 const char *source = "function foo() { FAIL.FAIL; }; foo();";
12029 v8::Handle<v8::String> src = v8::String::New(source);
12030 v8::Handle<v8::String> origin = v8::String::New("stack-trace-test");
12031 v8::Script::New(src, origin)->Run();
12032 CHECK(try_catch.HasCaught());
12033 v8::String::Utf8Value stack(try_catch.StackTrace());
12034 CHECK(strstr(*stack, "at foo (stack-trace-test") != NULL);
12035}
12036
12037
Kristian Monsen25f61362010-05-21 11:50:48 +010012038// Checks that a StackFrame has certain expected values.
12039void checkStackFrame(const char* expected_script_name,
12040 const char* expected_func_name, int expected_line_number,
12041 int expected_column, bool is_eval, bool is_constructor,
12042 v8::Handle<v8::StackFrame> frame) {
12043 v8::HandleScope scope;
12044 v8::String::Utf8Value func_name(frame->GetFunctionName());
12045 v8::String::Utf8Value script_name(frame->GetScriptName());
12046 if (*script_name == NULL) {
12047 // The situation where there is no associated script, like for evals.
12048 CHECK(expected_script_name == NULL);
12049 } else {
12050 CHECK(strstr(*script_name, expected_script_name) != NULL);
12051 }
12052 CHECK(strstr(*func_name, expected_func_name) != NULL);
12053 CHECK_EQ(expected_line_number, frame->GetLineNumber());
12054 CHECK_EQ(expected_column, frame->GetColumn());
12055 CHECK_EQ(is_eval, frame->IsEval());
12056 CHECK_EQ(is_constructor, frame->IsConstructor());
12057}
12058
12059
12060v8::Handle<Value> AnalyzeStackInNativeCode(const v8::Arguments& args) {
12061 v8::HandleScope scope;
12062 const char* origin = "capture-stack-trace-test";
12063 const int kOverviewTest = 1;
12064 const int kDetailedTest = 2;
12065
12066 ASSERT(args.Length() == 1);
12067
12068 int testGroup = args[0]->Int32Value();
12069 if (testGroup == kOverviewTest) {
12070 v8::Handle<v8::StackTrace> stackTrace =
12071 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kOverview);
12072 CHECK_EQ(4, stackTrace->GetFrameCount());
12073 checkStackFrame(origin, "bar", 2, 10, false, false,
12074 stackTrace->GetFrame(0));
12075 checkStackFrame(origin, "foo", 6, 3, false, false,
12076 stackTrace->GetFrame(1));
12077 checkStackFrame(NULL, "", 1, 1, false, false,
12078 stackTrace->GetFrame(2));
12079 // The last frame is an anonymous function that has the initial call.
12080 checkStackFrame(origin, "", 8, 7, false, false,
12081 stackTrace->GetFrame(3));
12082
12083 CHECK(stackTrace->AsArray()->IsArray());
12084 } else if (testGroup == kDetailedTest) {
12085 v8::Handle<v8::StackTrace> stackTrace =
12086 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kDetailed);
12087 CHECK_EQ(4, stackTrace->GetFrameCount());
12088 checkStackFrame(origin, "bat", 4, 22, false, false,
12089 stackTrace->GetFrame(0));
12090 checkStackFrame(origin, "baz", 8, 3, false, true,
12091 stackTrace->GetFrame(1));
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010012092#ifdef ENABLE_DEBUGGER_SUPPORT
12093 bool is_eval = true;
12094#else // ENABLE_DEBUGGER_SUPPORT
12095 bool is_eval = false;
12096#endif // ENABLE_DEBUGGER_SUPPORT
12097
12098 checkStackFrame(NULL, "", 1, 1, is_eval, false,
Kristian Monsen25f61362010-05-21 11:50:48 +010012099 stackTrace->GetFrame(2));
12100 // The last frame is an anonymous function that has the initial call to foo.
12101 checkStackFrame(origin, "", 10, 1, false, false,
12102 stackTrace->GetFrame(3));
12103
12104 CHECK(stackTrace->AsArray()->IsArray());
12105 }
12106 return v8::Undefined();
12107}
12108
12109
12110// Tests the C++ StackTrace API.
Ben Murdochb0fe1622011-05-05 13:52:32 +010012111// TODO(3074796): Reenable this as a THREADED_TEST once it passes.
12112// THREADED_TEST(CaptureStackTrace) {
12113TEST(CaptureStackTrace) {
Kristian Monsen25f61362010-05-21 11:50:48 +010012114 v8::HandleScope scope;
12115 v8::Handle<v8::String> origin = v8::String::New("capture-stack-trace-test");
12116 Local<ObjectTemplate> templ = ObjectTemplate::New();
12117 templ->Set(v8_str("AnalyzeStackInNativeCode"),
12118 v8::FunctionTemplate::New(AnalyzeStackInNativeCode));
12119 LocalContext context(0, templ);
12120
12121 // Test getting OVERVIEW information. Should ignore information that is not
12122 // script name, function name, line number, and column offset.
12123 const char *overview_source =
12124 "function bar() {\n"
12125 " var y; AnalyzeStackInNativeCode(1);\n"
12126 "}\n"
12127 "function foo() {\n"
12128 "\n"
12129 " bar();\n"
12130 "}\n"
12131 "var x;eval('new foo();');";
12132 v8::Handle<v8::String> overview_src = v8::String::New(overview_source);
12133 v8::Handle<Value> overview_result =
12134 v8::Script::New(overview_src, origin)->Run();
12135 ASSERT(!overview_result.IsEmpty());
12136 ASSERT(overview_result->IsObject());
12137
12138 // Test getting DETAILED information.
12139 const char *detailed_source =
12140 "function bat() {AnalyzeStackInNativeCode(2);\n"
12141 "}\n"
12142 "\n"
12143 "function baz() {\n"
12144 " bat();\n"
12145 "}\n"
12146 "eval('new baz();');";
12147 v8::Handle<v8::String> detailed_src = v8::String::New(detailed_source);
12148 // Make the script using a non-zero line and column offset.
12149 v8::Handle<v8::Integer> line_offset = v8::Integer::New(3);
12150 v8::Handle<v8::Integer> column_offset = v8::Integer::New(5);
12151 v8::ScriptOrigin detailed_origin(origin, line_offset, column_offset);
12152 v8::Handle<v8::Script> detailed_script(
12153 v8::Script::New(detailed_src, &detailed_origin));
12154 v8::Handle<Value> detailed_result = detailed_script->Run();
12155 ASSERT(!detailed_result.IsEmpty());
12156 ASSERT(detailed_result->IsObject());
12157}
12158
12159
Ben Murdoch3bec4d22010-07-22 14:51:16 +010012160static void StackTraceForUncaughtExceptionListener(
12161 v8::Handle<v8::Message> message,
12162 v8::Handle<Value>) {
12163 v8::Handle<v8::StackTrace> stack_trace = message->GetStackTrace();
12164 CHECK_EQ(2, stack_trace->GetFrameCount());
12165 checkStackFrame("origin", "foo", 2, 3, false, false,
12166 stack_trace->GetFrame(0));
12167 checkStackFrame("origin", "bar", 5, 3, false, false,
12168 stack_trace->GetFrame(1));
12169}
12170
12171TEST(CaptureStackTraceForUncaughtException) {
12172 report_count = 0;
12173 v8::HandleScope scope;
12174 LocalContext env;
12175 v8::V8::AddMessageListener(StackTraceForUncaughtExceptionListener);
12176 v8::V8::SetCaptureStackTraceForUncaughtExceptions(true);
12177
12178 Script::Compile(v8_str("function foo() {\n"
12179 " throw 1;\n"
12180 "};\n"
12181 "function bar() {\n"
12182 " foo();\n"
12183 "};"),
12184 v8_str("origin"))->Run();
12185 v8::Local<v8::Object> global = env->Global();
12186 Local<Value> trouble = global->Get(v8_str("bar"));
12187 CHECK(trouble->IsFunction());
12188 Function::Cast(*trouble)->Call(global, 0, NULL);
12189 v8::V8::SetCaptureStackTraceForUncaughtExceptions(false);
12190 v8::V8::RemoveMessageListeners(StackTraceForUncaughtExceptionListener);
12191}
12192
12193
Steve Block1e0659c2011-05-24 12:43:12 +010012194TEST(CaptureStackTraceForUncaughtExceptionAndSetters) {
12195 v8::HandleScope scope;
12196 LocalContext env;
12197 v8::V8::SetCaptureStackTraceForUncaughtExceptions(true,
12198 1024,
12199 v8::StackTrace::kDetailed);
12200
12201 CompileRun(
12202 "var setters = ['column', 'lineNumber', 'scriptName',\n"
12203 " 'scriptNameOrSourceURL', 'functionName', 'isEval',\n"
12204 " 'isConstructor'];\n"
12205 "for (var i = 0; i < setters.length; i++) {\n"
12206 " var prop = setters[i];\n"
12207 " Object.prototype.__defineSetter__(prop, function() { throw prop; });\n"
12208 "}\n");
12209 CompileRun("throw 'exception';");
12210 v8::V8::SetCaptureStackTraceForUncaughtExceptions(false);
12211}
12212
12213
Ben Murdochf87a2032010-10-22 12:50:53 +010012214v8::Handle<Value> AnalyzeStackOfEvalWithSourceURL(const v8::Arguments& args) {
12215 v8::HandleScope scope;
12216 v8::Handle<v8::StackTrace> stackTrace =
12217 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kDetailed);
12218 CHECK_EQ(5, stackTrace->GetFrameCount());
12219 v8::Handle<v8::String> url = v8_str("eval_url");
12220 for (int i = 0; i < 3; i++) {
12221 v8::Handle<v8::String> name =
12222 stackTrace->GetFrame(i)->GetScriptNameOrSourceURL();
12223 CHECK(!name.IsEmpty());
12224 CHECK_EQ(url, name);
12225 }
12226 return v8::Undefined();
12227}
12228
12229
12230TEST(SourceURLInStackTrace) {
12231 v8::HandleScope scope;
12232 Local<ObjectTemplate> templ = ObjectTemplate::New();
12233 templ->Set(v8_str("AnalyzeStackOfEvalWithSourceURL"),
12234 v8::FunctionTemplate::New(AnalyzeStackOfEvalWithSourceURL));
12235 LocalContext context(0, templ);
12236
12237 const char *source =
12238 "function outer() {\n"
12239 "function bar() {\n"
12240 " AnalyzeStackOfEvalWithSourceURL();\n"
12241 "}\n"
12242 "function foo() {\n"
12243 "\n"
12244 " bar();\n"
12245 "}\n"
12246 "foo();\n"
12247 "}\n"
12248 "eval('(' + outer +')()//@ sourceURL=eval_url');";
12249 CHECK(CompileRun(source)->IsUndefined());
12250}
12251
12252
Steve Block3ce2e202009-11-05 08:53:23 +000012253// Test that idle notification can be handled and eventually returns true.
Steve Blocka7e24c12009-10-30 11:49:00 +000012254THREADED_TEST(IdleNotification) {
Steve Block3ce2e202009-11-05 08:53:23 +000012255 bool rv = false;
12256 for (int i = 0; i < 100; i++) {
12257 rv = v8::V8::IdleNotification();
12258 if (rv)
12259 break;
12260 }
12261 CHECK(rv == true);
Steve Blocka7e24c12009-10-30 11:49:00 +000012262}
12263
12264
12265static uint32_t* stack_limit;
12266
12267static v8::Handle<Value> GetStackLimitCallback(const v8::Arguments& args) {
Steve Block44f0eee2011-05-26 01:26:41 +010012268 stack_limit = reinterpret_cast<uint32_t*>(
12269 i::Isolate::Current()->stack_guard()->real_climit());
Steve Blocka7e24c12009-10-30 11:49:00 +000012270 return v8::Undefined();
12271}
12272
12273
12274// Uses the address of a local variable to determine the stack top now.
12275// Given a size, returns an address that is that far from the current
12276// top of stack.
12277static uint32_t* ComputeStackLimit(uint32_t size) {
12278 uint32_t* answer = &size - (size / sizeof(size));
12279 // If the size is very large and the stack is very near the bottom of
12280 // memory then the calculation above may wrap around and give an address
12281 // that is above the (downwards-growing) stack. In that case we return
12282 // a very low address.
12283 if (answer > &size) return reinterpret_cast<uint32_t*>(sizeof(size));
12284 return answer;
12285}
12286
12287
12288TEST(SetResourceConstraints) {
12289 static const int K = 1024;
12290 uint32_t* set_limit = ComputeStackLimit(128 * K);
12291
12292 // Set stack limit.
12293 v8::ResourceConstraints constraints;
12294 constraints.set_stack_limit(set_limit);
12295 CHECK(v8::SetResourceConstraints(&constraints));
12296
12297 // Execute a script.
12298 v8::HandleScope scope;
12299 LocalContext env;
12300 Local<v8::FunctionTemplate> fun_templ =
12301 v8::FunctionTemplate::New(GetStackLimitCallback);
12302 Local<Function> fun = fun_templ->GetFunction();
12303 env->Global()->Set(v8_str("get_stack_limit"), fun);
12304 CompileRun("get_stack_limit();");
12305
12306 CHECK(stack_limit == set_limit);
12307}
12308
12309
12310TEST(SetResourceConstraintsInThread) {
12311 uint32_t* set_limit;
12312 {
12313 v8::Locker locker;
12314 static const int K = 1024;
12315 set_limit = ComputeStackLimit(128 * K);
12316
12317 // Set stack limit.
12318 v8::ResourceConstraints constraints;
12319 constraints.set_stack_limit(set_limit);
12320 CHECK(v8::SetResourceConstraints(&constraints));
12321
12322 // Execute a script.
12323 v8::HandleScope scope;
12324 LocalContext env;
12325 Local<v8::FunctionTemplate> fun_templ =
12326 v8::FunctionTemplate::New(GetStackLimitCallback);
12327 Local<Function> fun = fun_templ->GetFunction();
12328 env->Global()->Set(v8_str("get_stack_limit"), fun);
12329 CompileRun("get_stack_limit();");
12330
12331 CHECK(stack_limit == set_limit);
12332 }
12333 {
12334 v8::Locker locker;
12335 CHECK(stack_limit == set_limit);
12336 }
12337}
Steve Block3ce2e202009-11-05 08:53:23 +000012338
12339
12340THREADED_TEST(GetHeapStatistics) {
12341 v8::HandleScope scope;
12342 LocalContext c1;
12343 v8::HeapStatistics heap_statistics;
Steve Blockd0582a62009-12-15 09:54:21 +000012344 CHECK_EQ(static_cast<int>(heap_statistics.total_heap_size()), 0);
12345 CHECK_EQ(static_cast<int>(heap_statistics.used_heap_size()), 0);
Steve Block3ce2e202009-11-05 08:53:23 +000012346 v8::V8::GetHeapStatistics(&heap_statistics);
Steve Blockd0582a62009-12-15 09:54:21 +000012347 CHECK_NE(static_cast<int>(heap_statistics.total_heap_size()), 0);
12348 CHECK_NE(static_cast<int>(heap_statistics.used_heap_size()), 0);
12349}
12350
12351
12352static double DoubleFromBits(uint64_t value) {
12353 double target;
Steve Blockd0582a62009-12-15 09:54:21 +000012354 memcpy(&target, &value, sizeof(target));
Steve Blockd0582a62009-12-15 09:54:21 +000012355 return target;
12356}
12357
12358
12359static uint64_t DoubleToBits(double value) {
12360 uint64_t target;
Steve Blockd0582a62009-12-15 09:54:21 +000012361 memcpy(&target, &value, sizeof(target));
Steve Blockd0582a62009-12-15 09:54:21 +000012362 return target;
12363}
12364
12365
12366static double DoubleToDateTime(double input) {
12367 double date_limit = 864e13;
12368 if (IsNaN(input) || input < -date_limit || input > date_limit) {
12369 return i::OS::nan_value();
12370 }
12371 return (input < 0) ? -(floor(-input)) : floor(input);
12372}
12373
12374// We don't have a consistent way to write 64-bit constants syntactically, so we
12375// split them into two 32-bit constants and combine them programmatically.
12376static double DoubleFromBits(uint32_t high_bits, uint32_t low_bits) {
12377 return DoubleFromBits((static_cast<uint64_t>(high_bits) << 32) | low_bits);
12378}
12379
12380
12381THREADED_TEST(QuietSignalingNaNs) {
12382 v8::HandleScope scope;
12383 LocalContext context;
12384 v8::TryCatch try_catch;
12385
12386 // Special double values.
12387 double snan = DoubleFromBits(0x7ff00000, 0x00000001);
12388 double qnan = DoubleFromBits(0x7ff80000, 0x00000000);
12389 double infinity = DoubleFromBits(0x7ff00000, 0x00000000);
12390 double max_normal = DoubleFromBits(0x7fefffff, 0xffffffffu);
12391 double min_normal = DoubleFromBits(0x00100000, 0x00000000);
12392 double max_denormal = DoubleFromBits(0x000fffff, 0xffffffffu);
12393 double min_denormal = DoubleFromBits(0x00000000, 0x00000001);
12394
12395 // Date values are capped at +/-100000000 days (times 864e5 ms per day)
12396 // on either side of the epoch.
12397 double date_limit = 864e13;
12398
12399 double test_values[] = {
12400 snan,
12401 qnan,
12402 infinity,
12403 max_normal,
12404 date_limit + 1,
12405 date_limit,
12406 min_normal,
12407 max_denormal,
12408 min_denormal,
12409 0,
12410 -0,
12411 -min_denormal,
12412 -max_denormal,
12413 -min_normal,
12414 -date_limit,
12415 -date_limit - 1,
12416 -max_normal,
12417 -infinity,
12418 -qnan,
12419 -snan
12420 };
12421 int num_test_values = 20;
12422
12423 for (int i = 0; i < num_test_values; i++) {
12424 double test_value = test_values[i];
12425
12426 // Check that Number::New preserves non-NaNs and quiets SNaNs.
12427 v8::Handle<v8::Value> number = v8::Number::New(test_value);
12428 double stored_number = number->NumberValue();
12429 if (!IsNaN(test_value)) {
12430 CHECK_EQ(test_value, stored_number);
12431 } else {
12432 uint64_t stored_bits = DoubleToBits(stored_number);
12433 // Check if quiet nan (bits 51..62 all set).
12434 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff));
12435 }
12436
12437 // Check that Date::New preserves non-NaNs in the date range and
12438 // quiets SNaNs.
12439 v8::Handle<v8::Value> date = v8::Date::New(test_value);
12440 double expected_stored_date = DoubleToDateTime(test_value);
12441 double stored_date = date->NumberValue();
12442 if (!IsNaN(expected_stored_date)) {
12443 CHECK_EQ(expected_stored_date, stored_date);
12444 } else {
12445 uint64_t stored_bits = DoubleToBits(stored_date);
12446 // Check if quiet nan (bits 51..62 all set).
12447 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff));
12448 }
12449 }
12450}
12451
12452
12453static v8::Handle<Value> SpaghettiIncident(const v8::Arguments& args) {
12454 v8::HandleScope scope;
12455 v8::TryCatch tc;
12456 v8::Handle<v8::String> str = args[0]->ToString();
12457 if (tc.HasCaught())
12458 return tc.ReThrow();
12459 return v8::Undefined();
12460}
12461
12462
12463// Test that an exception can be propagated down through a spaghetti
12464// stack using ReThrow.
12465THREADED_TEST(SpaghettiStackReThrow) {
12466 v8::HandleScope scope;
12467 LocalContext context;
12468 context->Global()->Set(
12469 v8::String::New("s"),
12470 v8::FunctionTemplate::New(SpaghettiIncident)->GetFunction());
12471 v8::TryCatch try_catch;
12472 CompileRun(
12473 "var i = 0;"
12474 "var o = {"
12475 " toString: function () {"
12476 " if (i == 10) {"
12477 " throw 'Hey!';"
12478 " } else {"
12479 " i++;"
12480 " return s(o);"
12481 " }"
12482 " }"
12483 "};"
12484 "s(o);");
12485 CHECK(try_catch.HasCaught());
12486 v8::String::Utf8Value value(try_catch.Exception());
12487 CHECK_EQ(0, strcmp(*value, "Hey!"));
12488}
12489
12490
Steve Blockd0582a62009-12-15 09:54:21 +000012491TEST(Regress528) {
12492 v8::V8::Initialize();
12493
12494 v8::HandleScope scope;
12495 v8::Persistent<Context> context;
12496 v8::Persistent<Context> other_context;
12497 int gc_count;
12498
12499 // Create a context used to keep the code from aging in the compilation
12500 // cache.
12501 other_context = Context::New();
12502
12503 // Context-dependent context data creates reference from the compilation
12504 // cache to the global object.
12505 const char* source_simple = "1";
12506 context = Context::New();
12507 {
12508 v8::HandleScope scope;
12509
12510 context->Enter();
12511 Local<v8::String> obj = v8::String::New("");
12512 context->SetData(obj);
12513 CompileRun(source_simple);
12514 context->Exit();
12515 }
12516 context.Dispose();
12517 for (gc_count = 1; gc_count < 10; gc_count++) {
12518 other_context->Enter();
12519 CompileRun(source_simple);
12520 other_context->Exit();
Steve Block44f0eee2011-05-26 01:26:41 +010012521 HEAP->CollectAllGarbage(false);
Steve Blockd0582a62009-12-15 09:54:21 +000012522 if (GetGlobalObjectsCount() == 1) break;
12523 }
12524 CHECK_GE(2, gc_count);
12525 CHECK_EQ(1, GetGlobalObjectsCount());
12526
12527 // Eval in a function creates reference from the compilation cache to the
12528 // global object.
12529 const char* source_eval = "function f(){eval('1')}; f()";
12530 context = Context::New();
12531 {
12532 v8::HandleScope scope;
12533
12534 context->Enter();
12535 CompileRun(source_eval);
12536 context->Exit();
12537 }
12538 context.Dispose();
12539 for (gc_count = 1; gc_count < 10; gc_count++) {
12540 other_context->Enter();
12541 CompileRun(source_eval);
12542 other_context->Exit();
Steve Block44f0eee2011-05-26 01:26:41 +010012543 HEAP->CollectAllGarbage(false);
Steve Blockd0582a62009-12-15 09:54:21 +000012544 if (GetGlobalObjectsCount() == 1) break;
12545 }
12546 CHECK_GE(2, gc_count);
12547 CHECK_EQ(1, GetGlobalObjectsCount());
12548
12549 // Looking up the line number for an exception creates reference from the
12550 // compilation cache to the global object.
12551 const char* source_exception = "function f(){throw 1;} f()";
12552 context = Context::New();
12553 {
12554 v8::HandleScope scope;
12555
12556 context->Enter();
12557 v8::TryCatch try_catch;
12558 CompileRun(source_exception);
12559 CHECK(try_catch.HasCaught());
12560 v8::Handle<v8::Message> message = try_catch.Message();
12561 CHECK(!message.IsEmpty());
12562 CHECK_EQ(1, message->GetLineNumber());
12563 context->Exit();
12564 }
12565 context.Dispose();
12566 for (gc_count = 1; gc_count < 10; gc_count++) {
12567 other_context->Enter();
12568 CompileRun(source_exception);
12569 other_context->Exit();
Steve Block44f0eee2011-05-26 01:26:41 +010012570 HEAP->CollectAllGarbage(false);
Steve Blockd0582a62009-12-15 09:54:21 +000012571 if (GetGlobalObjectsCount() == 1) break;
12572 }
12573 CHECK_GE(2, gc_count);
12574 CHECK_EQ(1, GetGlobalObjectsCount());
12575
12576 other_context.Dispose();
Steve Block3ce2e202009-11-05 08:53:23 +000012577}
Andrei Popescu402d9372010-02-26 13:31:12 +000012578
12579
12580THREADED_TEST(ScriptOrigin) {
12581 v8::HandleScope scope;
12582 LocalContext env;
12583 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test"));
12584 v8::Handle<v8::String> script = v8::String::New(
12585 "function f() {}\n\nfunction g() {}");
12586 v8::Script::Compile(script, &origin)->Run();
12587 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(
12588 env->Global()->Get(v8::String::New("f")));
12589 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast(
12590 env->Global()->Get(v8::String::New("g")));
12591
12592 v8::ScriptOrigin script_origin_f = f->GetScriptOrigin();
12593 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_f.ResourceName()));
12594 CHECK_EQ(0, script_origin_f.ResourceLineOffset()->Int32Value());
12595
12596 v8::ScriptOrigin script_origin_g = g->GetScriptOrigin();
12597 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_g.ResourceName()));
12598 CHECK_EQ(0, script_origin_g.ResourceLineOffset()->Int32Value());
12599}
12600
12601
12602THREADED_TEST(ScriptLineNumber) {
12603 v8::HandleScope scope;
12604 LocalContext env;
12605 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test"));
12606 v8::Handle<v8::String> script = v8::String::New(
12607 "function f() {}\n\nfunction g() {}");
12608 v8::Script::Compile(script, &origin)->Run();
12609 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(
12610 env->Global()->Get(v8::String::New("f")));
12611 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast(
12612 env->Global()->Get(v8::String::New("g")));
12613 CHECK_EQ(0, f->GetScriptLineNumber());
12614 CHECK_EQ(2, g->GetScriptLineNumber());
12615}
12616
12617
12618static v8::Handle<Value> GetterWhichReturns42(Local<String> name,
12619 const AccessorInfo& info) {
12620 return v8_num(42);
12621}
12622
12623
12624static void SetterWhichSetsYOnThisTo23(Local<String> name,
12625 Local<Value> value,
12626 const AccessorInfo& info) {
12627 info.This()->Set(v8_str("y"), v8_num(23));
12628}
12629
12630
Steve Block6ded16b2010-05-10 14:33:55 +010012631TEST(SetterOnConstructorPrototype) {
Andrei Popescu402d9372010-02-26 13:31:12 +000012632 v8::HandleScope scope;
12633 Local<ObjectTemplate> templ = ObjectTemplate::New();
12634 templ->SetAccessor(v8_str("x"),
12635 GetterWhichReturns42,
12636 SetterWhichSetsYOnThisTo23);
12637 LocalContext context;
12638 context->Global()->Set(v8_str("P"), templ->NewInstance());
12639 CompileRun("function C1() {"
12640 " this.x = 23;"
12641 "};"
12642 "C1.prototype = P;"
12643 "function C2() {"
12644 " this.x = 23"
12645 "};"
12646 "C2.prototype = { };"
12647 "C2.prototype.__proto__ = P;");
12648
12649 v8::Local<v8::Script> script;
12650 script = v8::Script::Compile(v8_str("new C1();"));
12651 for (int i = 0; i < 10; i++) {
12652 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
12653 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value());
12654 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value());
12655 }
12656
12657 script = v8::Script::Compile(v8_str("new C2();"));
12658 for (int i = 0; i < 10; i++) {
12659 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run());
12660 CHECK_EQ(42, c2->Get(v8_str("x"))->Int32Value());
12661 CHECK_EQ(23, c2->Get(v8_str("y"))->Int32Value());
12662 }
12663}
12664
12665
12666static v8::Handle<Value> NamedPropertyGetterWhichReturns42(
12667 Local<String> name, const AccessorInfo& info) {
12668 return v8_num(42);
12669}
12670
12671
12672static v8::Handle<Value> NamedPropertySetterWhichSetsYOnThisTo23(
12673 Local<String> name, Local<Value> value, const AccessorInfo& info) {
12674 if (name->Equals(v8_str("x"))) {
12675 info.This()->Set(v8_str("y"), v8_num(23));
12676 }
12677 return v8::Handle<Value>();
12678}
12679
12680
12681THREADED_TEST(InterceptorOnConstructorPrototype) {
12682 v8::HandleScope scope;
12683 Local<ObjectTemplate> templ = ObjectTemplate::New();
12684 templ->SetNamedPropertyHandler(NamedPropertyGetterWhichReturns42,
12685 NamedPropertySetterWhichSetsYOnThisTo23);
12686 LocalContext context;
12687 context->Global()->Set(v8_str("P"), templ->NewInstance());
12688 CompileRun("function C1() {"
12689 " this.x = 23;"
12690 "};"
12691 "C1.prototype = P;"
12692 "function C2() {"
12693 " this.x = 23"
12694 "};"
12695 "C2.prototype = { };"
12696 "C2.prototype.__proto__ = P;");
12697
12698 v8::Local<v8::Script> script;
12699 script = v8::Script::Compile(v8_str("new C1();"));
12700 for (int i = 0; i < 10; i++) {
12701 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
12702 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value());
12703 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value());
12704 }
12705
12706 script = v8::Script::Compile(v8_str("new C2();"));
12707 for (int i = 0; i < 10; i++) {
12708 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run());
12709 CHECK_EQ(23, c2->Get(v8_str("x"))->Int32Value());
12710 CHECK_EQ(42, c2->Get(v8_str("y"))->Int32Value());
12711 }
12712}
Steve Block6ded16b2010-05-10 14:33:55 +010012713
12714
12715TEST(Bug618) {
12716 const char* source = "function C1() {"
12717 " this.x = 23;"
12718 "};"
12719 "C1.prototype = P;";
12720
12721 v8::HandleScope scope;
12722 LocalContext context;
12723 v8::Local<v8::Script> script;
12724
12725 // Use a simple object as prototype.
12726 v8::Local<v8::Object> prototype = v8::Object::New();
12727 prototype->Set(v8_str("y"), v8_num(42));
12728 context->Global()->Set(v8_str("P"), prototype);
12729
12730 // This compile will add the code to the compilation cache.
12731 CompileRun(source);
12732
12733 script = v8::Script::Compile(v8_str("new C1();"));
Kristian Monsen0d5e1162010-09-30 15:31:59 +010012734 // Allow enough iterations for the inobject slack tracking logic
12735 // to finalize instance size and install the fast construct stub.
12736 for (int i = 0; i < 256; i++) {
Steve Block6ded16b2010-05-10 14:33:55 +010012737 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
12738 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value());
12739 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value());
12740 }
12741
12742 // Use an API object with accessors as prototype.
12743 Local<ObjectTemplate> templ = ObjectTemplate::New();
12744 templ->SetAccessor(v8_str("x"),
12745 GetterWhichReturns42,
12746 SetterWhichSetsYOnThisTo23);
12747 context->Global()->Set(v8_str("P"), templ->NewInstance());
12748
12749 // This compile will get the code from the compilation cache.
12750 CompileRun(source);
12751
12752 script = v8::Script::Compile(v8_str("new C1();"));
12753 for (int i = 0; i < 10; i++) {
12754 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
12755 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value());
12756 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value());
12757 }
12758}
12759
12760int prologue_call_count = 0;
12761int epilogue_call_count = 0;
12762int prologue_call_count_second = 0;
12763int epilogue_call_count_second = 0;
12764
12765void PrologueCallback(v8::GCType, v8::GCCallbackFlags) {
12766 ++prologue_call_count;
12767}
12768
12769void EpilogueCallback(v8::GCType, v8::GCCallbackFlags) {
12770 ++epilogue_call_count;
12771}
12772
12773void PrologueCallbackSecond(v8::GCType, v8::GCCallbackFlags) {
12774 ++prologue_call_count_second;
12775}
12776
12777void EpilogueCallbackSecond(v8::GCType, v8::GCCallbackFlags) {
12778 ++epilogue_call_count_second;
12779}
12780
12781TEST(GCCallbacks) {
12782 LocalContext context;
12783
12784 v8::V8::AddGCPrologueCallback(PrologueCallback);
12785 v8::V8::AddGCEpilogueCallback(EpilogueCallback);
12786 CHECK_EQ(0, prologue_call_count);
12787 CHECK_EQ(0, epilogue_call_count);
Steve Block44f0eee2011-05-26 01:26:41 +010012788 HEAP->CollectAllGarbage(false);
Steve Block6ded16b2010-05-10 14:33:55 +010012789 CHECK_EQ(1, prologue_call_count);
12790 CHECK_EQ(1, epilogue_call_count);
12791 v8::V8::AddGCPrologueCallback(PrologueCallbackSecond);
12792 v8::V8::AddGCEpilogueCallback(EpilogueCallbackSecond);
Steve Block44f0eee2011-05-26 01:26:41 +010012793 HEAP->CollectAllGarbage(false);
Steve Block6ded16b2010-05-10 14:33:55 +010012794 CHECK_EQ(2, prologue_call_count);
12795 CHECK_EQ(2, epilogue_call_count);
12796 CHECK_EQ(1, prologue_call_count_second);
12797 CHECK_EQ(1, epilogue_call_count_second);
12798 v8::V8::RemoveGCPrologueCallback(PrologueCallback);
12799 v8::V8::RemoveGCEpilogueCallback(EpilogueCallback);
Steve Block44f0eee2011-05-26 01:26:41 +010012800 HEAP->CollectAllGarbage(false);
Steve Block6ded16b2010-05-10 14:33:55 +010012801 CHECK_EQ(2, prologue_call_count);
12802 CHECK_EQ(2, epilogue_call_count);
12803 CHECK_EQ(2, prologue_call_count_second);
12804 CHECK_EQ(2, epilogue_call_count_second);
12805 v8::V8::RemoveGCPrologueCallback(PrologueCallbackSecond);
12806 v8::V8::RemoveGCEpilogueCallback(EpilogueCallbackSecond);
Steve Block44f0eee2011-05-26 01:26:41 +010012807 HEAP->CollectAllGarbage(false);
Steve Block6ded16b2010-05-10 14:33:55 +010012808 CHECK_EQ(2, prologue_call_count);
12809 CHECK_EQ(2, epilogue_call_count);
12810 CHECK_EQ(2, prologue_call_count_second);
12811 CHECK_EQ(2, epilogue_call_count_second);
12812}
Kristian Monsen25f61362010-05-21 11:50:48 +010012813
12814
12815THREADED_TEST(AddToJSFunctionResultCache) {
12816 i::FLAG_allow_natives_syntax = true;
12817 v8::HandleScope scope;
12818
12819 LocalContext context;
12820
12821 const char* code =
12822 "(function() {"
12823 " var key0 = 'a';"
12824 " var key1 = 'b';"
12825 " var r0 = %_GetFromCache(0, key0);"
12826 " var r1 = %_GetFromCache(0, key1);"
12827 " var r0_ = %_GetFromCache(0, key0);"
12828 " if (r0 !== r0_)"
12829 " return 'Different results for ' + key0 + ': ' + r0 + ' vs. ' + r0_;"
12830 " var r1_ = %_GetFromCache(0, key1);"
12831 " if (r1 !== r1_)"
12832 " return 'Different results for ' + key1 + ': ' + r1 + ' vs. ' + r1_;"
12833 " return 'PASSED';"
12834 "})()";
Steve Block44f0eee2011-05-26 01:26:41 +010012835 HEAP->ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010012836 ExpectString(code, "PASSED");
12837}
12838
12839
12840static const int k0CacheSize = 16;
12841
12842THREADED_TEST(FillJSFunctionResultCache) {
12843 i::FLAG_allow_natives_syntax = true;
12844 v8::HandleScope scope;
12845
12846 LocalContext context;
12847
12848 const char* code =
12849 "(function() {"
12850 " var k = 'a';"
12851 " var r = %_GetFromCache(0, k);"
12852 " for (var i = 0; i < 16; i++) {"
12853 " %_GetFromCache(0, 'a' + i);"
12854 " };"
12855 " if (r === %_GetFromCache(0, k))"
12856 " return 'FAILED: k0CacheSize is too small';"
12857 " return 'PASSED';"
12858 "})()";
Steve Block44f0eee2011-05-26 01:26:41 +010012859 HEAP->ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010012860 ExpectString(code, "PASSED");
12861}
12862
12863
12864THREADED_TEST(RoundRobinGetFromCache) {
12865 i::FLAG_allow_natives_syntax = true;
12866 v8::HandleScope scope;
12867
12868 LocalContext context;
12869
12870 const char* code =
12871 "(function() {"
12872 " var keys = [];"
12873 " for (var i = 0; i < 16; i++) keys.push(i);"
12874 " var values = [];"
12875 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);"
12876 " for (var i = 0; i < 16; i++) {"
12877 " var v = %_GetFromCache(0, keys[i]);"
12878 " if (v !== values[i])"
12879 " return 'Wrong value for ' + "
12880 " keys[i] + ': ' + v + ' vs. ' + values[i];"
12881 " };"
12882 " return 'PASSED';"
12883 "})()";
Steve Block44f0eee2011-05-26 01:26:41 +010012884 HEAP->ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010012885 ExpectString(code, "PASSED");
12886}
12887
12888
12889THREADED_TEST(ReverseGetFromCache) {
12890 i::FLAG_allow_natives_syntax = true;
12891 v8::HandleScope scope;
12892
12893 LocalContext context;
12894
12895 const char* code =
12896 "(function() {"
12897 " var keys = [];"
12898 " for (var i = 0; i < 16; i++) keys.push(i);"
12899 " var values = [];"
12900 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);"
12901 " for (var i = 15; i >= 16; i--) {"
12902 " var v = %_GetFromCache(0, keys[i]);"
12903 " if (v !== values[i])"
12904 " return 'Wrong value for ' + "
12905 " keys[i] + ': ' + v + ' vs. ' + values[i];"
12906 " };"
12907 " return 'PASSED';"
12908 "})()";
Steve Block44f0eee2011-05-26 01:26:41 +010012909 HEAP->ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010012910 ExpectString(code, "PASSED");
12911}
12912
12913
12914THREADED_TEST(TestEviction) {
12915 i::FLAG_allow_natives_syntax = true;
12916 v8::HandleScope scope;
12917
12918 LocalContext context;
12919
12920 const char* code =
12921 "(function() {"
12922 " for (var i = 0; i < 2*16; i++) {"
12923 " %_GetFromCache(0, 'a' + i);"
12924 " };"
12925 " return 'PASSED';"
12926 "})()";
Steve Block44f0eee2011-05-26 01:26:41 +010012927 HEAP->ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010012928 ExpectString(code, "PASSED");
12929}
Steve Block8defd9f2010-07-08 12:39:36 +010012930
12931
12932THREADED_TEST(TwoByteStringInAsciiCons) {
12933 // See Chromium issue 47824.
12934 v8::HandleScope scope;
12935
12936 LocalContext context;
12937 const char* init_code =
12938 "var str1 = 'abelspendabel';"
12939 "var str2 = str1 + str1 + str1;"
12940 "str2;";
12941 Local<Value> result = CompileRun(init_code);
12942
12943 CHECK(result->IsString());
12944 i::Handle<i::String> string = v8::Utils::OpenHandle(String::Cast(*result));
12945 int length = string->length();
12946 CHECK(string->IsAsciiRepresentation());
12947
12948 FlattenString(string);
12949 i::Handle<i::String> flat_string = FlattenGetString(string);
12950
12951 CHECK(string->IsAsciiRepresentation());
12952 CHECK(flat_string->IsAsciiRepresentation());
12953
12954 // Create external resource.
12955 uint16_t* uc16_buffer = new uint16_t[length + 1];
12956
12957 i::String::WriteToFlat(*flat_string, uc16_buffer, 0, length);
12958 uc16_buffer[length] = 0;
12959
12960 TestResource resource(uc16_buffer);
12961
12962 flat_string->MakeExternal(&resource);
12963
12964 CHECK(flat_string->IsTwoByteRepresentation());
12965
12966 // At this point, we should have a Cons string which is flat and ASCII,
12967 // with a first half that is a two-byte string (although it only contains
12968 // ASCII characters). This is a valid sequence of steps, and it can happen
12969 // in real pages.
12970
12971 CHECK(string->IsAsciiRepresentation());
12972 i::ConsString* cons = i::ConsString::cast(*string);
12973 CHECK_EQ(0, cons->second()->length());
12974 CHECK(cons->first()->IsTwoByteRepresentation());
12975
12976 // Check that some string operations work.
12977
12978 // Atom RegExp.
12979 Local<Value> reresult = CompileRun("str2.match(/abel/g).length;");
12980 CHECK_EQ(6, reresult->Int32Value());
12981
12982 // Nonatom RegExp.
12983 reresult = CompileRun("str2.match(/abe./g).length;");
12984 CHECK_EQ(6, reresult->Int32Value());
12985
12986 reresult = CompileRun("str2.search(/bel/g);");
12987 CHECK_EQ(1, reresult->Int32Value());
12988
12989 reresult = CompileRun("str2.search(/be./g);");
12990 CHECK_EQ(1, reresult->Int32Value());
12991
12992 ExpectTrue("/bel/g.test(str2);");
12993
12994 ExpectTrue("/be./g.test(str2);");
12995
12996 reresult = CompileRun("/bel/g.exec(str2);");
12997 CHECK(!reresult->IsNull());
12998
12999 reresult = CompileRun("/be./g.exec(str2);");
13000 CHECK(!reresult->IsNull());
13001
13002 ExpectString("str2.substring(2, 10);", "elspenda");
13003
13004 ExpectString("str2.substring(2, 20);", "elspendabelabelspe");
13005
13006 ExpectString("str2.charAt(2);", "e");
13007
13008 reresult = CompileRun("str2.charCodeAt(2);");
13009 CHECK_EQ(static_cast<int32_t>('e'), reresult->Int32Value());
13010}
Iain Merrick75681382010-08-19 15:07:18 +010013011
13012
13013// Failed access check callback that performs a GC on each invocation.
13014void FailedAccessCheckCallbackGC(Local<v8::Object> target,
13015 v8::AccessType type,
13016 Local<v8::Value> data) {
Steve Block44f0eee2011-05-26 01:26:41 +010013017 HEAP->CollectAllGarbage(true);
Iain Merrick75681382010-08-19 15:07:18 +010013018}
13019
13020
13021TEST(GCInFailedAccessCheckCallback) {
13022 // Install a failed access check callback that performs a GC on each
13023 // invocation. Then force the callback to be called from va
13024
13025 v8::V8::Initialize();
13026 v8::V8::SetFailedAccessCheckCallbackFunction(&FailedAccessCheckCallbackGC);
13027
13028 v8::HandleScope scope;
13029
13030 // Create an ObjectTemplate for global objects and install access
13031 // check callbacks that will block access.
13032 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
13033 global_template->SetAccessCheckCallbacks(NamedGetAccessBlocker,
13034 IndexedGetAccessBlocker,
13035 v8::Handle<v8::Value>(),
13036 false);
13037
13038 // Create a context and set an x property on it's global object.
13039 LocalContext context0(NULL, global_template);
13040 context0->Global()->Set(v8_str("x"), v8_num(42));
13041 v8::Handle<v8::Object> global0 = context0->Global();
13042
13043 // Create a context with a different security token so that the
13044 // failed access check callback will be called on each access.
13045 LocalContext context1(NULL, global_template);
13046 context1->Global()->Set(v8_str("other"), global0);
13047
13048 // Get property with failed access check.
13049 ExpectUndefined("other.x");
13050
13051 // Get element with failed access check.
13052 ExpectUndefined("other[0]");
13053
13054 // Set property with failed access check.
13055 v8::Handle<v8::Value> result = CompileRun("other.x = new Object()");
13056 CHECK(result->IsObject());
13057
13058 // Set element with failed access check.
13059 result = CompileRun("other[0] = new Object()");
13060 CHECK(result->IsObject());
13061
13062 // Get property attribute with failed access check.
13063 ExpectFalse("\'x\' in other");
13064
13065 // Get property attribute for element with failed access check.
13066 ExpectFalse("0 in other");
13067
13068 // Delete property.
13069 ExpectFalse("delete other.x");
13070
13071 // Delete element.
13072 CHECK_EQ(false, global0->Delete(0));
13073
13074 // DefineAccessor.
13075 CHECK_EQ(false,
13076 global0->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("x")));
13077
13078 // Define JavaScript accessor.
13079 ExpectUndefined("Object.prototype.__defineGetter__.call("
13080 " other, \'x\', function() { return 42; })");
13081
13082 // LookupAccessor.
13083 ExpectUndefined("Object.prototype.__lookupGetter__.call("
13084 " other, \'x\')");
13085
13086 // HasLocalElement.
13087 ExpectFalse("Object.prototype.hasOwnProperty.call(other, \'0\')");
13088
13089 CHECK_EQ(false, global0->HasRealIndexedProperty(0));
13090 CHECK_EQ(false, global0->HasRealNamedProperty(v8_str("x")));
13091 CHECK_EQ(false, global0->HasRealNamedCallbackProperty(v8_str("x")));
13092
13093 // Reset the failed access check callback so it does not influence
13094 // the other tests.
13095 v8::V8::SetFailedAccessCheckCallbackFunction(NULL);
13096}
Kristian Monsen0d5e1162010-09-30 15:31:59 +010013097
Steve Block44f0eee2011-05-26 01:26:41 +010013098TEST(DefaultIsolateGetCurrent) {
13099 CHECK(v8::Isolate::GetCurrent() != NULL);
13100 v8::Isolate* isolate = v8::Isolate::GetCurrent();
13101 CHECK(reinterpret_cast<i::Isolate*>(isolate)->IsDefaultIsolate());
13102 printf("*** %s\n", "DefaultIsolateGetCurrent success");
13103}
13104
13105TEST(IsolateNewDispose) {
13106 v8::Isolate* current_isolate = v8::Isolate::GetCurrent();
13107 v8::Isolate* isolate = v8::Isolate::New();
13108 CHECK(isolate != NULL);
13109 CHECK(!reinterpret_cast<i::Isolate*>(isolate)->IsDefaultIsolate());
13110 CHECK(current_isolate != isolate);
13111 CHECK(current_isolate == v8::Isolate::GetCurrent());
13112
13113 v8::V8::SetFatalErrorHandler(StoringErrorCallback);
13114 last_location = last_message = NULL;
13115 isolate->Dispose();
13116 CHECK_EQ(last_location, NULL);
13117 CHECK_EQ(last_message, NULL);
13118}
13119
13120TEST(IsolateEnterExitDefault) {
13121 v8::HandleScope scope;
13122 LocalContext context;
13123 v8::Isolate* current_isolate = v8::Isolate::GetCurrent();
13124 CHECK(current_isolate != NULL); // Default isolate.
13125 ExpectString("'hello'", "hello");
13126 current_isolate->Enter();
13127 ExpectString("'still working'", "still working");
13128 current_isolate->Exit();
13129 ExpectString("'still working 2'", "still working 2");
13130 current_isolate->Exit();
13131 // Default isolate is always, well, 'default current'.
13132 CHECK_EQ(v8::Isolate::GetCurrent(), current_isolate);
13133 // Still working since default isolate is auto-entering any thread
13134 // that has no isolate and attempts to execute V8 APIs.
13135 ExpectString("'still working 3'", "still working 3");
13136}
13137
13138TEST(DisposeDefaultIsolate) {
13139 v8::V8::SetFatalErrorHandler(StoringErrorCallback);
13140
13141 // Run some V8 code to trigger default isolate to become 'current'.
13142 v8::HandleScope scope;
13143 LocalContext context;
13144 ExpectString("'run some V8'", "run some V8");
13145
13146 v8::Isolate* isolate = v8::Isolate::GetCurrent();
13147 CHECK(reinterpret_cast<i::Isolate*>(isolate)->IsDefaultIsolate());
13148 last_location = last_message = NULL;
13149 isolate->Dispose();
13150 // It is not possible to dispose default isolate via Isolate API.
13151 CHECK_NE(last_location, NULL);
13152 CHECK_NE(last_message, NULL);
13153}
13154
13155TEST(RunDefaultAndAnotherIsolate) {
13156 v8::HandleScope scope;
13157 LocalContext context;
13158
13159 // Enter new isolate.
13160 v8::Isolate* isolate = v8::Isolate::New();
13161 CHECK(isolate);
13162 isolate->Enter();
13163 { // Need this block because subsequent Exit() will deallocate Heap,
13164 // so we need all scope objects to be deconstructed when it happens.
13165 v8::HandleScope scope_new;
13166 LocalContext context_new;
13167
13168 // Run something in new isolate.
13169 CompileRun("var foo = 153;");
13170 ExpectTrue("function f() { return foo == 153; }; f()");
13171 }
13172 isolate->Exit();
13173
13174 // This runs automatically in default isolate.
13175 // Variables in another isolate should be not available.
13176 ExpectTrue("function f() {"
13177 " try {"
13178 " foo;"
13179 " return false;"
13180 " } catch(e) {"
13181 " return true;"
13182 " }"
13183 "};"
13184 "var bar = 371;"
13185 "f()");
13186
13187 v8::V8::SetFatalErrorHandler(StoringErrorCallback);
13188 last_location = last_message = NULL;
13189 isolate->Dispose();
13190 CHECK_EQ(last_location, NULL);
13191 CHECK_EQ(last_message, NULL);
13192
13193 // Check that default isolate still runs.
13194 ExpectTrue("function f() { return bar == 371; }; f()");
13195}
13196
13197TEST(DisposeIsolateWhenInUse) {
13198 v8::Isolate* isolate = v8::Isolate::New();
13199 CHECK(isolate);
13200 isolate->Enter();
13201 v8::HandleScope scope;
13202 LocalContext context;
13203 // Run something in this isolate.
13204 ExpectTrue("true");
13205 v8::V8::SetFatalErrorHandler(StoringErrorCallback);
13206 last_location = last_message = NULL;
13207 // Still entered, should fail.
13208 isolate->Dispose();
13209 CHECK_NE(last_location, NULL);
13210 CHECK_NE(last_message, NULL);
13211}
13212
13213TEST(RunTwoIsolatesOnSingleThread) {
13214 // Run isolate 1.
13215 v8::Isolate* isolate1 = v8::Isolate::New();
13216 isolate1->Enter();
13217 v8::Persistent<v8::Context> context1 = v8::Context::New();
13218
13219 {
13220 v8::Context::Scope cscope(context1);
13221 v8::HandleScope scope;
13222 // Run something in new isolate.
13223 CompileRun("var foo = 'isolate 1';");
13224 ExpectString("function f() { return foo; }; f()", "isolate 1");
13225 }
13226
13227 // Run isolate 2.
13228 v8::Isolate* isolate2 = v8::Isolate::New();
13229 v8::Persistent<v8::Context> context2;
13230
13231 {
13232 v8::Isolate::Scope iscope(isolate2);
13233 context2 = v8::Context::New();
13234 v8::Context::Scope cscope(context2);
13235 v8::HandleScope scope;
13236
13237 // Run something in new isolate.
13238 CompileRun("var foo = 'isolate 2';");
13239 ExpectString("function f() { return foo; }; f()", "isolate 2");
13240 }
13241
13242 {
13243 v8::Context::Scope cscope(context1);
13244 v8::HandleScope scope;
13245 // Now again in isolate 1
13246 ExpectString("function f() { return foo; }; f()", "isolate 1");
13247 }
13248
13249 isolate1->Exit();
13250
13251 // Run some stuff in default isolate.
13252 v8::Persistent<v8::Context> context_default = v8::Context::New();
13253
13254 {
13255 v8::Context::Scope cscope(context_default);
13256 v8::HandleScope scope;
13257 // Variables in other isolates should be not available, verify there
13258 // is an exception.
13259 ExpectTrue("function f() {"
13260 " try {"
13261 " foo;"
13262 " return false;"
13263 " } catch(e) {"
13264 " return true;"
13265 " }"
13266 "};"
13267 "var isDefaultIsolate = true;"
13268 "f()");
13269 }
13270
13271 isolate1->Enter();
13272
13273 {
13274 v8::Isolate::Scope iscope(isolate2);
13275 v8::Context::Scope cscope(context2);
13276 v8::HandleScope scope;
13277 ExpectString("function f() { return foo; }; f()", "isolate 2");
13278 }
13279
13280 {
13281 v8::Context::Scope cscope(context1);
13282 v8::HandleScope scope;
13283 ExpectString("function f() { return foo; }; f()", "isolate 1");
13284 }
13285
13286 {
13287 v8::Isolate::Scope iscope(isolate2);
13288 context2.Dispose();
13289 }
13290
13291 context1.Dispose();
13292 isolate1->Exit();
13293
13294 v8::V8::SetFatalErrorHandler(StoringErrorCallback);
13295 last_location = last_message = NULL;
13296
13297 isolate1->Dispose();
13298 CHECK_EQ(last_location, NULL);
13299 CHECK_EQ(last_message, NULL);
13300
13301 isolate2->Dispose();
13302 CHECK_EQ(last_location, NULL);
13303 CHECK_EQ(last_message, NULL);
13304
13305 // Check that default isolate still runs.
13306 {
13307 v8::Context::Scope cscope(context_default);
13308 v8::HandleScope scope;
13309 ExpectTrue("function f() { return isDefaultIsolate; }; f()");
13310 }
13311}
13312
13313static int CalcFibonacci(v8::Isolate* isolate, int limit) {
13314 v8::Isolate::Scope isolate_scope(isolate);
13315 v8::HandleScope scope;
13316 LocalContext context;
13317 i::ScopedVector<char> code(1024);
13318 i::OS::SNPrintF(code, "function fib(n) {"
13319 " if (n <= 2) return 1;"
13320 " return fib(n-1) + fib(n-2);"
13321 "}"
13322 "fib(%d)", limit);
13323 Local<Value> value = CompileRun(code.start());
13324 CHECK(value->IsNumber());
13325 return static_cast<int>(value->NumberValue());
13326}
13327
13328class IsolateThread : public v8::internal::Thread {
13329 public:
13330 explicit IsolateThread(v8::Isolate* isolate, int fib_limit)
13331 : Thread(NULL, "IsolateThread"),
13332 isolate_(isolate),
13333 fib_limit_(fib_limit),
13334 result_(0) { }
13335
13336 void Run() {
13337 result_ = CalcFibonacci(isolate_, fib_limit_);
13338 }
13339
13340 int result() { return result_; }
13341
13342 private:
13343 v8::Isolate* isolate_;
13344 int fib_limit_;
13345 int result_;
13346};
13347
13348TEST(MultipleIsolatesOnIndividualThreads) {
13349 v8::Isolate* isolate1 = v8::Isolate::New();
13350 v8::Isolate* isolate2 = v8::Isolate::New();
13351
13352 IsolateThread thread1(isolate1, 21);
13353 IsolateThread thread2(isolate2, 12);
13354
13355 // Compute some fibonacci numbers on 3 threads in 3 isolates.
13356 thread1.Start();
13357 thread2.Start();
13358
13359 int result1 = CalcFibonacci(v8::Isolate::GetCurrent(), 21);
13360 int result2 = CalcFibonacci(v8::Isolate::GetCurrent(), 12);
13361
13362 thread1.Join();
13363 thread2.Join();
13364
13365 // Compare results. The actual fibonacci numbers for 12 and 21 are taken
13366 // (I'm lazy!) from http://en.wikipedia.org/wiki/Fibonacci_number
13367 CHECK_EQ(result1, 10946);
13368 CHECK_EQ(result2, 144);
13369 CHECK_EQ(result1, thread1.result());
13370 CHECK_EQ(result2, thread2.result());
13371
13372 isolate1->Dispose();
13373 isolate2->Dispose();
13374}
13375
13376
13377class InitDefaultIsolateThread : public v8::internal::Thread {
13378 public:
13379 enum TestCase {
13380 IgnoreOOM,
13381 SetResourceConstraints,
13382 SetFatalHandler,
13383 SetCounterFunction,
13384 SetCreateHistogramFunction,
13385 SetAddHistogramSampleFunction
13386 };
13387
13388 explicit InitDefaultIsolateThread(TestCase testCase)
13389 : Thread(NULL, "InitDefaultIsolateThread"),
13390 testCase_(testCase),
13391 result_(false) { }
13392
13393 void Run() {
13394 switch (testCase_) {
13395 case IgnoreOOM:
13396 v8::V8::IgnoreOutOfMemoryException();
13397 break;
13398
13399 case SetResourceConstraints: {
13400 static const int K = 1024;
13401 v8::ResourceConstraints constraints;
13402 constraints.set_max_young_space_size(256 * K);
13403 constraints.set_max_old_space_size(4 * K * K);
13404 v8::SetResourceConstraints(&constraints);
13405 break;
13406 }
13407
13408 case SetFatalHandler:
13409 v8::V8::SetFatalErrorHandler(NULL);
13410 break;
13411
13412 case SetCounterFunction:
13413 v8::V8::SetCounterFunction(NULL);
13414 break;
13415
13416 case SetCreateHistogramFunction:
13417 v8::V8::SetCreateHistogramFunction(NULL);
13418 break;
13419
13420 case SetAddHistogramSampleFunction:
13421 v8::V8::SetAddHistogramSampleFunction(NULL);
13422 break;
13423 }
13424 result_ = true;
13425 }
13426
13427 bool result() { return result_; }
13428
13429 private:
13430 TestCase testCase_;
13431 bool result_;
13432};
13433
13434
13435static void InitializeTestHelper(InitDefaultIsolateThread::TestCase testCase) {
13436 InitDefaultIsolateThread thread(testCase);
13437 thread.Start();
13438 thread.Join();
13439 CHECK_EQ(thread.result(), true);
13440}
13441
13442TEST(InitializeDefaultIsolateOnSecondaryThread1) {
13443 InitializeTestHelper(InitDefaultIsolateThread::IgnoreOOM);
13444}
13445
13446TEST(InitializeDefaultIsolateOnSecondaryThread2) {
13447 InitializeTestHelper(InitDefaultIsolateThread::SetResourceConstraints);
13448}
13449
13450TEST(InitializeDefaultIsolateOnSecondaryThread3) {
13451 InitializeTestHelper(InitDefaultIsolateThread::SetFatalHandler);
13452}
13453
13454TEST(InitializeDefaultIsolateOnSecondaryThread4) {
13455 InitializeTestHelper(InitDefaultIsolateThread::SetCounterFunction);
13456}
13457
13458TEST(InitializeDefaultIsolateOnSecondaryThread5) {
13459 InitializeTestHelper(InitDefaultIsolateThread::SetCreateHistogramFunction);
13460}
13461
13462TEST(InitializeDefaultIsolateOnSecondaryThread6) {
13463 InitializeTestHelper(InitDefaultIsolateThread::SetAddHistogramSampleFunction);
13464}
13465
Kristian Monsen0d5e1162010-09-30 15:31:59 +010013466
13467TEST(StringCheckMultipleContexts) {
13468 const char* code =
13469 "(function() { return \"a\".charAt(0); })()";
13470
13471 {
13472 // Run the code twice in the first context to initialize the call IC.
13473 v8::HandleScope scope;
13474 LocalContext context1;
13475 ExpectString(code, "a");
13476 ExpectString(code, "a");
13477 }
13478
13479 {
13480 // Change the String.prototype in the second context and check
13481 // that the right function gets called.
13482 v8::HandleScope scope;
13483 LocalContext context2;
13484 CompileRun("String.prototype.charAt = function() { return \"not a\"; }");
13485 ExpectString(code, "not a");
13486 }
13487}
13488
13489
13490TEST(NumberCheckMultipleContexts) {
13491 const char* code =
13492 "(function() { return (42).toString(); })()";
13493
13494 {
13495 // Run the code twice in the first context to initialize the call IC.
13496 v8::HandleScope scope;
13497 LocalContext context1;
13498 ExpectString(code, "42");
13499 ExpectString(code, "42");
13500 }
13501
13502 {
13503 // Change the Number.prototype in the second context and check
13504 // that the right function gets called.
13505 v8::HandleScope scope;
13506 LocalContext context2;
13507 CompileRun("Number.prototype.toString = function() { return \"not 42\"; }");
13508 ExpectString(code, "not 42");
13509 }
13510}
13511
13512
13513TEST(BooleanCheckMultipleContexts) {
13514 const char* code =
13515 "(function() { return true.toString(); })()";
13516
13517 {
13518 // Run the code twice in the first context to initialize the call IC.
13519 v8::HandleScope scope;
13520 LocalContext context1;
13521 ExpectString(code, "true");
13522 ExpectString(code, "true");
13523 }
13524
13525 {
13526 // Change the Boolean.prototype in the second context and check
13527 // that the right function gets called.
13528 v8::HandleScope scope;
13529 LocalContext context2;
13530 CompileRun("Boolean.prototype.toString = function() { return \"\"; }");
13531 ExpectString(code, "");
13532 }
13533}
Ben Murdochf87a2032010-10-22 12:50:53 +010013534
13535
13536TEST(DontDeleteCellLoadIC) {
13537 const char* function_code =
13538 "function readCell() { while (true) { return cell; } }";
13539
13540 {
13541 // Run the code twice in the first context to initialize the load
13542 // IC for a don't delete cell.
13543 v8::HandleScope scope;
13544 LocalContext context1;
13545 CompileRun("var cell = \"first\";");
13546 ExpectBoolean("delete cell", false);
13547 CompileRun(function_code);
13548 ExpectString("readCell()", "first");
13549 ExpectString("readCell()", "first");
13550 }
13551
13552 {
13553 // Use a deletable cell in the second context.
13554 v8::HandleScope scope;
13555 LocalContext context2;
13556 CompileRun("cell = \"second\";");
13557 CompileRun(function_code);
13558 ExpectString("readCell()", "second");
13559 ExpectBoolean("delete cell", true);
13560 ExpectString("(function() {"
13561 " try {"
13562 " return readCell();"
13563 " } catch(e) {"
13564 " return e.toString();"
13565 " }"
13566 "})()",
13567 "ReferenceError: cell is not defined");
13568 CompileRun("cell = \"new_second\";");
Steve Block44f0eee2011-05-26 01:26:41 +010013569 HEAP->CollectAllGarbage(true);
Ben Murdochf87a2032010-10-22 12:50:53 +010013570 ExpectString("readCell()", "new_second");
13571 ExpectString("readCell()", "new_second");
13572 }
13573}
13574
13575
13576TEST(DontDeleteCellLoadICForceDelete) {
13577 const char* function_code =
13578 "function readCell() { while (true) { return cell; } }";
13579
13580 // Run the code twice to initialize the load IC for a don't delete
13581 // cell.
13582 v8::HandleScope scope;
13583 LocalContext context;
13584 CompileRun("var cell = \"value\";");
13585 ExpectBoolean("delete cell", false);
13586 CompileRun(function_code);
13587 ExpectString("readCell()", "value");
13588 ExpectString("readCell()", "value");
13589
13590 // Delete the cell using the API and check the inlined code works
13591 // correctly.
13592 CHECK(context->Global()->ForceDelete(v8_str("cell")));
13593 ExpectString("(function() {"
13594 " try {"
13595 " return readCell();"
13596 " } catch(e) {"
13597 " return e.toString();"
13598 " }"
13599 "})()",
13600 "ReferenceError: cell is not defined");
13601}
13602
13603
13604TEST(DontDeleteCellLoadICAPI) {
13605 const char* function_code =
13606 "function readCell() { while (true) { return cell; } }";
13607
13608 // Run the code twice to initialize the load IC for a don't delete
13609 // cell created using the API.
13610 v8::HandleScope scope;
13611 LocalContext context;
13612 context->Global()->Set(v8_str("cell"), v8_str("value"), v8::DontDelete);
13613 ExpectBoolean("delete cell", false);
13614 CompileRun(function_code);
13615 ExpectString("readCell()", "value");
13616 ExpectString("readCell()", "value");
13617
13618 // Delete the cell using the API and check the inlined code works
13619 // correctly.
13620 CHECK(context->Global()->ForceDelete(v8_str("cell")));
13621 ExpectString("(function() {"
13622 " try {"
13623 " return readCell();"
13624 " } catch(e) {"
13625 " return e.toString();"
13626 " }"
13627 "})()",
13628 "ReferenceError: cell is not defined");
13629}
13630
13631
13632TEST(GlobalLoadICGC) {
13633 const char* function_code =
13634 "function readCell() { while (true) { return cell; } }";
13635
13636 // Check inline load code for a don't delete cell is cleared during
13637 // GC.
13638 {
13639 v8::HandleScope scope;
13640 LocalContext context;
13641 CompileRun("var cell = \"value\";");
13642 ExpectBoolean("delete cell", false);
13643 CompileRun(function_code);
13644 ExpectString("readCell()", "value");
13645 ExpectString("readCell()", "value");
13646 }
13647 {
13648 v8::HandleScope scope;
13649 LocalContext context2;
13650 // Hold the code object in the second context.
13651 CompileRun(function_code);
13652 CheckSurvivingGlobalObjectsCount(1);
13653 }
13654
13655 // Check inline load code for a deletable cell is cleared during GC.
13656 {
13657 v8::HandleScope scope;
13658 LocalContext context;
13659 CompileRun("cell = \"value\";");
13660 CompileRun(function_code);
13661 ExpectString("readCell()", "value");
13662 ExpectString("readCell()", "value");
13663 }
13664 {
13665 v8::HandleScope scope;
13666 LocalContext context2;
13667 // Hold the code object in the second context.
13668 CompileRun(function_code);
13669 CheckSurvivingGlobalObjectsCount(1);
13670 }
13671}
13672
13673
13674TEST(RegExp) {
13675 v8::HandleScope scope;
13676 LocalContext context;
13677
13678 v8::Handle<v8::RegExp> re = v8::RegExp::New(v8_str("foo"), v8::RegExp::kNone);
13679 CHECK(re->IsRegExp());
13680 CHECK(re->GetSource()->Equals(v8_str("foo")));
13681 CHECK_EQ(re->GetFlags(), v8::RegExp::kNone);
13682
13683 re = v8::RegExp::New(v8_str("bar"),
13684 static_cast<v8::RegExp::Flags>(v8::RegExp::kIgnoreCase |
13685 v8::RegExp::kGlobal));
13686 CHECK(re->IsRegExp());
13687 CHECK(re->GetSource()->Equals(v8_str("bar")));
13688 CHECK_EQ(static_cast<int>(re->GetFlags()),
13689 v8::RegExp::kIgnoreCase | v8::RegExp::kGlobal);
13690
13691 re = v8::RegExp::New(v8_str("baz"),
13692 static_cast<v8::RegExp::Flags>(v8::RegExp::kIgnoreCase |
13693 v8::RegExp::kMultiline));
13694 CHECK(re->IsRegExp());
13695 CHECK(re->GetSource()->Equals(v8_str("baz")));
13696 CHECK_EQ(static_cast<int>(re->GetFlags()),
13697 v8::RegExp::kIgnoreCase | v8::RegExp::kMultiline);
13698
13699 re = CompileRun("/quux/").As<v8::RegExp>();
13700 CHECK(re->IsRegExp());
13701 CHECK(re->GetSource()->Equals(v8_str("quux")));
13702 CHECK_EQ(re->GetFlags(), v8::RegExp::kNone);
13703
13704 re = CompileRun("/quux/gm").As<v8::RegExp>();
13705 CHECK(re->IsRegExp());
13706 CHECK(re->GetSource()->Equals(v8_str("quux")));
13707 CHECK_EQ(static_cast<int>(re->GetFlags()),
13708 v8::RegExp::kGlobal | v8::RegExp::kMultiline);
13709
13710 // Override the RegExp constructor and check the API constructor
13711 // still works.
13712 CompileRun("RegExp = function() {}");
13713
13714 re = v8::RegExp::New(v8_str("foobar"), v8::RegExp::kNone);
13715 CHECK(re->IsRegExp());
13716 CHECK(re->GetSource()->Equals(v8_str("foobar")));
13717 CHECK_EQ(re->GetFlags(), v8::RegExp::kNone);
13718
13719 re = v8::RegExp::New(v8_str("foobarbaz"),
13720 static_cast<v8::RegExp::Flags>(v8::RegExp::kIgnoreCase |
13721 v8::RegExp::kMultiline));
13722 CHECK(re->IsRegExp());
13723 CHECK(re->GetSource()->Equals(v8_str("foobarbaz")));
13724 CHECK_EQ(static_cast<int>(re->GetFlags()),
13725 v8::RegExp::kIgnoreCase | v8::RegExp::kMultiline);
13726
13727 context->Global()->Set(v8_str("re"), re);
13728 ExpectTrue("re.test('FoobarbaZ')");
13729
13730 v8::TryCatch try_catch;
13731 re = v8::RegExp::New(v8_str("foo["), v8::RegExp::kNone);
13732 CHECK(re.IsEmpty());
13733 CHECK(try_catch.HasCaught());
13734 context->Global()->Set(v8_str("ex"), try_catch.Exception());
13735 ExpectTrue("ex instanceof SyntaxError");
13736}
13737
13738
Steve Block1e0659c2011-05-24 12:43:12 +010013739THREADED_TEST(Equals) {
13740 v8::HandleScope handleScope;
13741 LocalContext localContext;
13742
13743 v8::Handle<v8::Object> globalProxy = localContext->Global();
13744 v8::Handle<Value> global = globalProxy->GetPrototype();
13745
13746 CHECK(global->StrictEquals(global));
13747 CHECK(!global->StrictEquals(globalProxy));
13748 CHECK(!globalProxy->StrictEquals(global));
13749 CHECK(globalProxy->StrictEquals(globalProxy));
13750
13751 CHECK(global->Equals(global));
13752 CHECK(!global->Equals(globalProxy));
13753 CHECK(!globalProxy->Equals(global));
13754 CHECK(globalProxy->Equals(globalProxy));
13755}
13756
13757
Ben Murdochf87a2032010-10-22 12:50:53 +010013758static v8::Handle<v8::Value> Getter(v8::Local<v8::String> property,
13759 const v8::AccessorInfo& info ) {
13760 return v8_str("42!");
13761}
13762
13763
13764static v8::Handle<v8::Array> Enumerator(const v8::AccessorInfo& info) {
13765 v8::Handle<v8::Array> result = v8::Array::New();
13766 result->Set(0, v8_str("universalAnswer"));
13767 return result;
13768}
13769
13770
13771TEST(NamedEnumeratorAndForIn) {
13772 v8::HandleScope handle_scope;
13773 LocalContext context;
13774 v8::Context::Scope context_scope(context.local());
13775
13776 v8::Handle<v8::ObjectTemplate> tmpl = v8::ObjectTemplate::New();
13777 tmpl->SetNamedPropertyHandler(Getter, NULL, NULL, NULL, Enumerator);
13778 context->Global()->Set(v8_str("o"), tmpl->NewInstance());
13779 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun(
13780 "var result = []; for (var k in o) result.push(k); result"));
13781 CHECK_EQ(1, result->Length());
13782 CHECK_EQ(v8_str("universalAnswer"), result->Get(0));
13783}
Steve Block1e0659c2011-05-24 12:43:12 +010013784
13785
13786TEST(DefinePropertyPostDetach) {
13787 v8::HandleScope scope;
13788 LocalContext context;
13789 v8::Handle<v8::Object> proxy = context->Global();
13790 v8::Handle<v8::Function> define_property =
13791 CompileRun("(function() {"
13792 " Object.defineProperty("
13793 " this,"
13794 " 1,"
13795 " { configurable: true, enumerable: true, value: 3 });"
13796 "})").As<Function>();
13797 context->DetachGlobal();
13798 define_property->Call(proxy, 0, NULL);
13799}
Ben Murdoch8b112d22011-06-08 16:22:53 +010013800
13801
13802static void InstallContextId(v8::Handle<Context> context, int id) {
13803 Context::Scope scope(context);
13804 CompileRun("Object.prototype").As<Object>()->
13805 Set(v8_str("context_id"), v8::Integer::New(id));
13806}
13807
13808
13809static void CheckContextId(v8::Handle<Object> object, int expected) {
13810 CHECK_EQ(expected, object->Get(v8_str("context_id"))->Int32Value());
13811}
13812
13813
13814THREADED_TEST(CreationContext) {
13815 HandleScope handle_scope;
13816 Persistent<Context> context1 = Context::New();
13817 InstallContextId(context1, 1);
13818 Persistent<Context> context2 = Context::New();
13819 InstallContextId(context2, 2);
13820 Persistent<Context> context3 = Context::New();
13821 InstallContextId(context3, 3);
13822
13823 Local<v8::FunctionTemplate> tmpl = v8::FunctionTemplate::New();
13824
13825 Local<Object> object1;
13826 Local<Function> func1;
13827 {
13828 Context::Scope scope(context1);
13829 object1 = Object::New();
13830 func1 = tmpl->GetFunction();
13831 }
13832
13833 Local<Object> object2;
13834 Local<Function> func2;
13835 {
13836 Context::Scope scope(context2);
13837 object2 = Object::New();
13838 func2 = tmpl->GetFunction();
13839 }
13840
13841 Local<Object> instance1;
13842 Local<Object> instance2;
13843
13844 {
13845 Context::Scope scope(context3);
13846 instance1 = func1->NewInstance();
13847 instance2 = func2->NewInstance();
13848 }
13849
13850 CHECK(object1->CreationContext() == context1);
13851 CheckContextId(object1, 1);
13852 CHECK(func1->CreationContext() == context1);
13853 CheckContextId(func1, 1);
13854 CHECK(instance1->CreationContext() == context1);
13855 CheckContextId(instance1, 1);
13856 CHECK(object2->CreationContext() == context2);
13857 CheckContextId(object2, 2);
13858 CHECK(func2->CreationContext() == context2);
13859 CheckContextId(func2, 2);
13860 CHECK(instance2->CreationContext() == context2);
13861 CheckContextId(instance2, 2);
13862
13863 {
13864 Context::Scope scope(context1);
13865 CHECK(object1->CreationContext() == context1);
13866 CheckContextId(object1, 1);
13867 CHECK(func1->CreationContext() == context1);
13868 CheckContextId(func1, 1);
13869 CHECK(instance1->CreationContext() == context1);
13870 CheckContextId(instance1, 1);
13871 CHECK(object2->CreationContext() == context2);
13872 CheckContextId(object2, 2);
13873 CHECK(func2->CreationContext() == context2);
13874 CheckContextId(func2, 2);
13875 CHECK(instance2->CreationContext() == context2);
13876 CheckContextId(instance2, 2);
13877 }
13878
13879 {
13880 Context::Scope scope(context2);
13881 CHECK(object1->CreationContext() == context1);
13882 CheckContextId(object1, 1);
13883 CHECK(func1->CreationContext() == context1);
13884 CheckContextId(func1, 1);
13885 CHECK(instance1->CreationContext() == context1);
13886 CheckContextId(instance1, 1);
13887 CHECK(object2->CreationContext() == context2);
13888 CheckContextId(object2, 2);
13889 CHECK(func2->CreationContext() == context2);
13890 CheckContextId(func2, 2);
13891 CHECK(instance2->CreationContext() == context2);
13892 CheckContextId(instance2, 2);
13893 }
13894
13895 context1.Dispose();
13896 context2.Dispose();
13897 context3.Dispose();
13898}