blob: 1121210760cdb39b89e72224d6cb02f8fe59ebde [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"
Ben Murdoch257744e2011-11-30 15:57:28 +000033#include "isolate.h"
Steve Blocka7e24c12009-10-30 11:49:00 +000034#include "compilation-cache.h"
35#include "execution.h"
36#include "snapshot.h"
37#include "platform.h"
Steve Block3ce2e202009-11-05 08:53:23 +000038#include "utils.h"
Steve Blocka7e24c12009-10-30 11:49:00 +000039#include "cctest.h"
Iain Merrick9ac36c92010-09-13 15:29:50 +010040#include "parser.h"
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -080041#include "unicode-inl.h"
Steve Blocka7e24c12009-10-30 11:49:00 +000042
Ben Murdoch257744e2011-11-30 15:57:28 +000043static const bool kLogThreading = false;
Steve Blockd0582a62009-12-15 09:54:21 +000044
Steve Blocka7e24c12009-10-30 11:49:00 +000045static bool IsNaN(double x) {
46#ifdef WIN32
47 return _isnan(x);
48#else
49 return isnan(x);
50#endif
51}
52
Steve Blocka7e24c12009-10-30 11:49:00 +000053using ::v8::AccessorInfo;
Ben Murdoch8b112d22011-06-08 16:22:53 +010054using ::v8::Arguments;
Steve Block44f0eee2011-05-26 01:26:41 +010055using ::v8::Context;
Steve Blocka7e24c12009-10-30 11:49:00 +000056using ::v8::Extension;
Steve Block44f0eee2011-05-26 01:26:41 +010057using ::v8::Function;
Ben Murdoch8b112d22011-06-08 16:22:53 +010058using ::v8::FunctionTemplate;
59using ::v8::Handle;
Steve Block44f0eee2011-05-26 01:26:41 +010060using ::v8::HandleScope;
61using ::v8::Local;
Ben Murdoch8b112d22011-06-08 16:22:53 +010062using ::v8::Message;
63using ::v8::MessageCallback;
Steve Block44f0eee2011-05-26 01:26:41 +010064using ::v8::Object;
65using ::v8::ObjectTemplate;
66using ::v8::Persistent;
67using ::v8::Script;
Ben Murdoch8b112d22011-06-08 16:22:53 +010068using ::v8::StackTrace;
Steve Block44f0eee2011-05-26 01:26:41 +010069using ::v8::String;
Ben Murdoch8b112d22011-06-08 16:22:53 +010070using ::v8::TryCatch;
71using ::v8::Undefined;
Steve Block44f0eee2011-05-26 01:26:41 +010072using ::v8::V8;
Ben Murdoch8b112d22011-06-08 16:22:53 +010073using ::v8::Value;
Steve Blocka7e24c12009-10-30 11:49:00 +000074
Steve Block8defd9f2010-07-08 12:39:36 +010075namespace i = ::i;
Steve Blocka7e24c12009-10-30 11:49:00 +000076
Steve Blocka7e24c12009-10-30 11:49:00 +000077
Leon Clarked91b9f72010-01-27 17:25:45 +000078static void ExpectString(const char* code, const char* expected) {
79 Local<Value> result = CompileRun(code);
80 CHECK(result->IsString());
81 String::AsciiValue ascii(result);
82 CHECK_EQ(expected, *ascii);
83}
84
85
86static void ExpectBoolean(const char* code, bool expected) {
87 Local<Value> result = CompileRun(code);
88 CHECK(result->IsBoolean());
89 CHECK_EQ(expected, result->BooleanValue());
90}
91
92
Leon Clarkef7060e22010-06-03 12:02:55 +010093static void ExpectTrue(const char* code) {
94 ExpectBoolean(code, true);
95}
96
97
Iain Merrick75681382010-08-19 15:07:18 +010098static void ExpectFalse(const char* code) {
99 ExpectBoolean(code, false);
100}
101
102
Leon Clarked91b9f72010-01-27 17:25:45 +0000103static void ExpectObject(const char* code, Local<Value> expected) {
104 Local<Value> result = CompileRun(code);
105 CHECK(result->Equals(expected));
106}
107
108
Iain Merrick75681382010-08-19 15:07:18 +0100109static void ExpectUndefined(const char* code) {
110 Local<Value> result = CompileRun(code);
111 CHECK(result->IsUndefined());
112}
113
114
Steve Blocka7e24c12009-10-30 11:49:00 +0000115static int signature_callback_count;
116static v8::Handle<Value> IncrementingSignatureCallback(
117 const v8::Arguments& args) {
118 ApiTestFuzzer::Fuzz();
119 signature_callback_count++;
120 v8::Handle<v8::Array> result = v8::Array::New(args.Length());
121 for (int i = 0; i < args.Length(); i++)
122 result->Set(v8::Integer::New(i), args[i]);
123 return result;
124}
125
126
127static v8::Handle<Value> SignatureCallback(const v8::Arguments& args) {
128 ApiTestFuzzer::Fuzz();
129 v8::Handle<v8::Array> result = v8::Array::New(args.Length());
130 for (int i = 0; i < args.Length(); i++) {
131 result->Set(v8::Integer::New(i), args[i]);
132 }
133 return result;
134}
135
136
137THREADED_TEST(Handles) {
138 v8::HandleScope scope;
139 Local<Context> local_env;
140 {
141 LocalContext env;
142 local_env = env.local();
143 }
144
145 // Local context should still be live.
146 CHECK(!local_env.IsEmpty());
147 local_env->Enter();
148
149 v8::Handle<v8::Primitive> undef = v8::Undefined();
150 CHECK(!undef.IsEmpty());
151 CHECK(undef->IsUndefined());
152
153 const char* c_source = "1 + 2 + 3";
154 Local<String> source = String::New(c_source);
155 Local<Script> script = Script::Compile(source);
156 CHECK_EQ(6, script->Run()->Int32Value());
157
158 local_env->Exit();
159}
160
161
Steve Blocka7e24c12009-10-30 11:49:00 +0000162THREADED_TEST(ReceiverSignature) {
163 v8::HandleScope scope;
164 LocalContext env;
165 v8::Handle<v8::FunctionTemplate> fun = v8::FunctionTemplate::New();
166 v8::Handle<v8::Signature> sig = v8::Signature::New(fun);
167 fun->PrototypeTemplate()->Set(
168 v8_str("m"),
169 v8::FunctionTemplate::New(IncrementingSignatureCallback,
170 v8::Handle<Value>(),
171 sig));
172 env->Global()->Set(v8_str("Fun"), fun->GetFunction());
173 signature_callback_count = 0;
174 CompileRun(
175 "var o = new Fun();"
176 "o.m();");
177 CHECK_EQ(1, signature_callback_count);
178 v8::Handle<v8::FunctionTemplate> sub_fun = v8::FunctionTemplate::New();
179 sub_fun->Inherit(fun);
180 env->Global()->Set(v8_str("SubFun"), sub_fun->GetFunction());
181 CompileRun(
182 "var o = new SubFun();"
183 "o.m();");
184 CHECK_EQ(2, signature_callback_count);
185
186 v8::TryCatch try_catch;
187 CompileRun(
188 "var o = { };"
189 "o.m = Fun.prototype.m;"
190 "o.m();");
191 CHECK_EQ(2, signature_callback_count);
192 CHECK(try_catch.HasCaught());
193 try_catch.Reset();
194 v8::Handle<v8::FunctionTemplate> unrel_fun = v8::FunctionTemplate::New();
195 sub_fun->Inherit(fun);
196 env->Global()->Set(v8_str("UnrelFun"), unrel_fun->GetFunction());
197 CompileRun(
198 "var o = new UnrelFun();"
199 "o.m = Fun.prototype.m;"
200 "o.m();");
201 CHECK_EQ(2, signature_callback_count);
202 CHECK(try_catch.HasCaught());
203}
204
205
Steve Blocka7e24c12009-10-30 11:49:00 +0000206THREADED_TEST(ArgumentSignature) {
207 v8::HandleScope scope;
208 LocalContext env;
209 v8::Handle<v8::FunctionTemplate> cons = v8::FunctionTemplate::New();
210 cons->SetClassName(v8_str("Cons"));
211 v8::Handle<v8::Signature> sig =
212 v8::Signature::New(v8::Handle<v8::FunctionTemplate>(), 1, &cons);
213 v8::Handle<v8::FunctionTemplate> fun =
214 v8::FunctionTemplate::New(SignatureCallback, v8::Handle<Value>(), sig);
215 env->Global()->Set(v8_str("Cons"), cons->GetFunction());
216 env->Global()->Set(v8_str("Fun1"), fun->GetFunction());
217
218 v8::Handle<Value> value1 = CompileRun("Fun1(4) == '';");
219 CHECK(value1->IsTrue());
220
221 v8::Handle<Value> value2 = CompileRun("Fun1(new Cons()) == '[object Cons]';");
222 CHECK(value2->IsTrue());
223
224 v8::Handle<Value> value3 = CompileRun("Fun1() == '';");
225 CHECK(value3->IsTrue());
226
227 v8::Handle<v8::FunctionTemplate> cons1 = v8::FunctionTemplate::New();
228 cons1->SetClassName(v8_str("Cons1"));
229 v8::Handle<v8::FunctionTemplate> cons2 = v8::FunctionTemplate::New();
230 cons2->SetClassName(v8_str("Cons2"));
231 v8::Handle<v8::FunctionTemplate> cons3 = v8::FunctionTemplate::New();
232 cons3->SetClassName(v8_str("Cons3"));
233
234 v8::Handle<v8::FunctionTemplate> args[3] = { cons1, cons2, cons3 };
235 v8::Handle<v8::Signature> wsig =
236 v8::Signature::New(v8::Handle<v8::FunctionTemplate>(), 3, args);
237 v8::Handle<v8::FunctionTemplate> fun2 =
238 v8::FunctionTemplate::New(SignatureCallback, v8::Handle<Value>(), wsig);
239
240 env->Global()->Set(v8_str("Cons1"), cons1->GetFunction());
241 env->Global()->Set(v8_str("Cons2"), cons2->GetFunction());
242 env->Global()->Set(v8_str("Cons3"), cons3->GetFunction());
243 env->Global()->Set(v8_str("Fun2"), fun2->GetFunction());
244 v8::Handle<Value> value4 = CompileRun(
245 "Fun2(new Cons1(), new Cons2(), new Cons3()) =="
246 "'[object Cons1],[object Cons2],[object Cons3]'");
247 CHECK(value4->IsTrue());
248
249 v8::Handle<Value> value5 = CompileRun(
250 "Fun2(new Cons1(), new Cons2(), 5) == '[object Cons1],[object Cons2],'");
251 CHECK(value5->IsTrue());
252
253 v8::Handle<Value> value6 = CompileRun(
254 "Fun2(new Cons3(), new Cons2(), new Cons1()) == ',[object Cons2],'");
255 CHECK(value6->IsTrue());
256
257 v8::Handle<Value> value7 = CompileRun(
258 "Fun2(new Cons1(), new Cons2(), new Cons3(), 'd') == "
259 "'[object Cons1],[object Cons2],[object Cons3],d';");
260 CHECK(value7->IsTrue());
261
262 v8::Handle<Value> value8 = CompileRun(
263 "Fun2(new Cons1(), new Cons2()) == '[object Cons1],[object Cons2]'");
264 CHECK(value8->IsTrue());
265}
266
267
268THREADED_TEST(HulIgennem) {
269 v8::HandleScope scope;
270 LocalContext env;
271 v8::Handle<v8::Primitive> undef = v8::Undefined();
272 Local<String> undef_str = undef->ToString();
273 char* value = i::NewArray<char>(undef_str->Length() + 1);
274 undef_str->WriteAscii(value);
275 CHECK_EQ(0, strcmp(value, "undefined"));
276 i::DeleteArray(value);
277}
278
279
280THREADED_TEST(Access) {
281 v8::HandleScope scope;
282 LocalContext env;
283 Local<v8::Object> obj = v8::Object::New();
284 Local<Value> foo_before = obj->Get(v8_str("foo"));
285 CHECK(foo_before->IsUndefined());
286 Local<String> bar_str = v8_str("bar");
287 obj->Set(v8_str("foo"), bar_str);
288 Local<Value> foo_after = obj->Get(v8_str("foo"));
289 CHECK(!foo_after->IsUndefined());
290 CHECK(foo_after->IsString());
291 CHECK_EQ(bar_str, foo_after);
292}
293
294
Steve Block6ded16b2010-05-10 14:33:55 +0100295THREADED_TEST(AccessElement) {
296 v8::HandleScope scope;
297 LocalContext env;
298 Local<v8::Object> obj = v8::Object::New();
299 Local<Value> before = obj->Get(1);
300 CHECK(before->IsUndefined());
301 Local<String> bar_str = v8_str("bar");
302 obj->Set(1, bar_str);
303 Local<Value> after = obj->Get(1);
304 CHECK(!after->IsUndefined());
305 CHECK(after->IsString());
306 CHECK_EQ(bar_str, after);
307
308 Local<v8::Array> value = CompileRun("[\"a\", \"b\"]").As<v8::Array>();
309 CHECK_EQ(v8_str("a"), value->Get(0));
310 CHECK_EQ(v8_str("b"), value->Get(1));
311}
312
313
Steve Blocka7e24c12009-10-30 11:49:00 +0000314THREADED_TEST(Script) {
315 v8::HandleScope scope;
316 LocalContext env;
317 const char* c_source = "1 + 2 + 3";
318 Local<String> source = String::New(c_source);
319 Local<Script> script = Script::Compile(source);
320 CHECK_EQ(6, script->Run()->Int32Value());
321}
322
323
324static uint16_t* AsciiToTwoByteString(const char* source) {
Steve Blockd0582a62009-12-15 09:54:21 +0000325 int array_length = i::StrLength(source) + 1;
Steve Blocka7e24c12009-10-30 11:49:00 +0000326 uint16_t* converted = i::NewArray<uint16_t>(array_length);
Steve Blockd0582a62009-12-15 09:54:21 +0000327 for (int i = 0; i < array_length; i++) converted[i] = source[i];
Steve Blocka7e24c12009-10-30 11:49:00 +0000328 return converted;
329}
330
331
332class TestResource: public String::ExternalStringResource {
333 public:
334 static int dispose_count;
335
336 explicit TestResource(uint16_t* data)
337 : data_(data), length_(0) {
338 while (data[length_]) ++length_;
339 }
340
341 ~TestResource() {
342 i::DeleteArray(data_);
343 ++dispose_count;
344 }
345
346 const uint16_t* data() const {
347 return data_;
348 }
349
350 size_t length() const {
351 return length_;
352 }
353 private:
354 uint16_t* data_;
355 size_t length_;
356};
357
358
359int TestResource::dispose_count = 0;
360
361
362class TestAsciiResource: public String::ExternalAsciiStringResource {
363 public:
364 static int dispose_count;
365
366 explicit TestAsciiResource(const char* data)
367 : data_(data),
368 length_(strlen(data)) { }
369
370 ~TestAsciiResource() {
371 i::DeleteArray(data_);
372 ++dispose_count;
373 }
374
375 const char* data() const {
376 return data_;
377 }
378
379 size_t length() const {
380 return length_;
381 }
382 private:
383 const char* data_;
384 size_t length_;
385};
386
387
388int TestAsciiResource::dispose_count = 0;
389
390
391THREADED_TEST(ScriptUsingStringResource) {
392 TestResource::dispose_count = 0;
393 const char* c_source = "1 + 2 * 3";
394 uint16_t* two_byte_source = AsciiToTwoByteString(c_source);
395 {
396 v8::HandleScope scope;
397 LocalContext env;
398 TestResource* resource = new TestResource(two_byte_source);
399 Local<String> source = String::NewExternal(resource);
400 Local<Script> script = Script::Compile(source);
401 Local<Value> value = script->Run();
402 CHECK(value->IsNumber());
403 CHECK_EQ(7, value->Int32Value());
404 CHECK(source->IsExternal());
405 CHECK_EQ(resource,
406 static_cast<TestResource*>(source->GetExternalStringResource()));
Steve Block44f0eee2011-05-26 01:26:41 +0100407 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000408 CHECK_EQ(0, TestResource::dispose_count);
409 }
Steve Block44f0eee2011-05-26 01:26:41 +0100410 v8::internal::Isolate::Current()->compilation_cache()->Clear();
411 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000412 CHECK_EQ(1, TestResource::dispose_count);
413}
414
415
416THREADED_TEST(ScriptUsingAsciiStringResource) {
417 TestAsciiResource::dispose_count = 0;
418 const char* c_source = "1 + 2 * 3";
419 {
420 v8::HandleScope scope;
421 LocalContext env;
422 Local<String> source =
423 String::NewExternal(new TestAsciiResource(i::StrDup(c_source)));
424 Local<Script> script = Script::Compile(source);
425 Local<Value> value = script->Run();
426 CHECK(value->IsNumber());
427 CHECK_EQ(7, value->Int32Value());
Steve Block44f0eee2011-05-26 01:26:41 +0100428 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000429 CHECK_EQ(0, TestAsciiResource::dispose_count);
430 }
Steve Block44f0eee2011-05-26 01:26:41 +0100431 i::Isolate::Current()->compilation_cache()->Clear();
432 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000433 CHECK_EQ(1, TestAsciiResource::dispose_count);
434}
435
436
437THREADED_TEST(ScriptMakingExternalString) {
438 TestResource::dispose_count = 0;
439 uint16_t* two_byte_source = AsciiToTwoByteString("1 + 2 * 3");
440 {
441 v8::HandleScope scope;
442 LocalContext env;
443 Local<String> source = String::New(two_byte_source);
Andrei Popescu402d9372010-02-26 13:31:12 +0000444 // Trigger GCs so that the newly allocated string moves to old gen.
Steve Block44f0eee2011-05-26 01:26:41 +0100445 HEAP->CollectGarbage(i::NEW_SPACE); // in survivor space now
446 HEAP->CollectGarbage(i::NEW_SPACE); // in old gen now
Steve Blocka7e24c12009-10-30 11:49:00 +0000447 bool success = source->MakeExternal(new TestResource(two_byte_source));
448 CHECK(success);
449 Local<Script> script = Script::Compile(source);
450 Local<Value> value = script->Run();
451 CHECK(value->IsNumber());
452 CHECK_EQ(7, value->Int32Value());
Steve Block44f0eee2011-05-26 01:26:41 +0100453 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000454 CHECK_EQ(0, TestResource::dispose_count);
455 }
Steve Block44f0eee2011-05-26 01:26:41 +0100456 i::Isolate::Current()->compilation_cache()->Clear();
457 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000458 CHECK_EQ(1, TestResource::dispose_count);
459}
460
461
462THREADED_TEST(ScriptMakingExternalAsciiString) {
463 TestAsciiResource::dispose_count = 0;
464 const char* c_source = "1 + 2 * 3";
465 {
466 v8::HandleScope scope;
467 LocalContext env;
468 Local<String> source = v8_str(c_source);
Andrei Popescu402d9372010-02-26 13:31:12 +0000469 // Trigger GCs so that the newly allocated string moves to old gen.
Steve Block44f0eee2011-05-26 01:26:41 +0100470 HEAP->CollectGarbage(i::NEW_SPACE); // in survivor space now
471 HEAP->CollectGarbage(i::NEW_SPACE); // in old gen now
Steve Blocka7e24c12009-10-30 11:49:00 +0000472 bool success = source->MakeExternal(
473 new TestAsciiResource(i::StrDup(c_source)));
474 CHECK(success);
475 Local<Script> script = Script::Compile(source);
476 Local<Value> value = script->Run();
477 CHECK(value->IsNumber());
478 CHECK_EQ(7, value->Int32Value());
Steve Block44f0eee2011-05-26 01:26:41 +0100479 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000480 CHECK_EQ(0, TestAsciiResource::dispose_count);
481 }
Steve Block44f0eee2011-05-26 01:26:41 +0100482 i::Isolate::Current()->compilation_cache()->Clear();
483 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000484 CHECK_EQ(1, TestAsciiResource::dispose_count);
485}
486
487
Andrei Popescu402d9372010-02-26 13:31:12 +0000488TEST(MakingExternalStringConditions) {
489 v8::HandleScope scope;
490 LocalContext env;
491
492 // Free some space in the new space so that we can check freshness.
Steve Block44f0eee2011-05-26 01:26:41 +0100493 HEAP->CollectGarbage(i::NEW_SPACE);
494 HEAP->CollectGarbage(i::NEW_SPACE);
Andrei Popescu402d9372010-02-26 13:31:12 +0000495
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100496 uint16_t* two_byte_string = AsciiToTwoByteString("small");
497 Local<String> small_string = String::New(two_byte_string);
498 i::DeleteArray(two_byte_string);
499
Andrei Popescu402d9372010-02-26 13:31:12 +0000500 // We should refuse to externalize newly created small string.
501 CHECK(!small_string->CanMakeExternal());
502 // Trigger GCs so that the newly allocated string moves to old gen.
Steve Block44f0eee2011-05-26 01:26:41 +0100503 HEAP->CollectGarbage(i::NEW_SPACE); // in survivor space now
504 HEAP->CollectGarbage(i::NEW_SPACE); // in old gen now
Andrei Popescu402d9372010-02-26 13:31:12 +0000505 // Old space strings should be accepted.
506 CHECK(small_string->CanMakeExternal());
507
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100508 two_byte_string = AsciiToTwoByteString("small 2");
509 small_string = String::New(two_byte_string);
510 i::DeleteArray(two_byte_string);
511
Andrei Popescu402d9372010-02-26 13:31:12 +0000512 // We should refuse externalizing newly created small string.
513 CHECK(!small_string->CanMakeExternal());
514 for (int i = 0; i < 100; i++) {
515 String::Value value(small_string);
516 }
517 // Frequently used strings should be accepted.
518 CHECK(small_string->CanMakeExternal());
519
520 const int buf_size = 10 * 1024;
521 char* buf = i::NewArray<char>(buf_size);
522 memset(buf, 'a', buf_size);
523 buf[buf_size - 1] = '\0';
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100524
525 two_byte_string = AsciiToTwoByteString(buf);
526 Local<String> large_string = String::New(two_byte_string);
Andrei Popescu402d9372010-02-26 13:31:12 +0000527 i::DeleteArray(buf);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100528 i::DeleteArray(two_byte_string);
Andrei Popescu402d9372010-02-26 13:31:12 +0000529 // Large strings should be immediately accepted.
530 CHECK(large_string->CanMakeExternal());
531}
532
533
534TEST(MakingExternalAsciiStringConditions) {
535 v8::HandleScope scope;
536 LocalContext env;
537
538 // Free some space in the new space so that we can check freshness.
Steve Block44f0eee2011-05-26 01:26:41 +0100539 HEAP->CollectGarbage(i::NEW_SPACE);
540 HEAP->CollectGarbage(i::NEW_SPACE);
Andrei Popescu402d9372010-02-26 13:31:12 +0000541
542 Local<String> small_string = String::New("small");
543 // We should refuse to externalize newly created small string.
544 CHECK(!small_string->CanMakeExternal());
545 // Trigger GCs so that the newly allocated string moves to old gen.
Steve Block44f0eee2011-05-26 01:26:41 +0100546 HEAP->CollectGarbage(i::NEW_SPACE); // in survivor space now
547 HEAP->CollectGarbage(i::NEW_SPACE); // in old gen now
Andrei Popescu402d9372010-02-26 13:31:12 +0000548 // Old space strings should be accepted.
549 CHECK(small_string->CanMakeExternal());
550
551 small_string = String::New("small 2");
552 // We should refuse externalizing newly created small string.
553 CHECK(!small_string->CanMakeExternal());
554 for (int i = 0; i < 100; i++) {
555 String::Value value(small_string);
556 }
557 // Frequently used strings should be accepted.
558 CHECK(small_string->CanMakeExternal());
559
560 const int buf_size = 10 * 1024;
561 char* buf = i::NewArray<char>(buf_size);
562 memset(buf, 'a', buf_size);
563 buf[buf_size - 1] = '\0';
564 Local<String> large_string = String::New(buf);
565 i::DeleteArray(buf);
566 // Large strings should be immediately accepted.
567 CHECK(large_string->CanMakeExternal());
568}
569
570
Steve Blocka7e24c12009-10-30 11:49:00 +0000571THREADED_TEST(UsingExternalString) {
572 {
573 v8::HandleScope scope;
574 uint16_t* two_byte_string = AsciiToTwoByteString("test string");
575 Local<String> string =
576 String::NewExternal(new TestResource(two_byte_string));
577 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
578 // Trigger GCs so that the newly allocated string moves to old gen.
Steve Block44f0eee2011-05-26 01:26:41 +0100579 HEAP->CollectGarbage(i::NEW_SPACE); // in survivor space now
580 HEAP->CollectGarbage(i::NEW_SPACE); // in old gen now
581 i::Handle<i::String> isymbol = FACTORY->SymbolFromString(istring);
Steve Blocka7e24c12009-10-30 11:49:00 +0000582 CHECK(isymbol->IsSymbol());
583 }
Steve Block44f0eee2011-05-26 01:26:41 +0100584 HEAP->CollectAllGarbage(false);
585 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000586}
587
588
589THREADED_TEST(UsingExternalAsciiString) {
590 {
591 v8::HandleScope scope;
592 const char* one_byte_string = "test string";
593 Local<String> string = String::NewExternal(
594 new TestAsciiResource(i::StrDup(one_byte_string)));
595 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
596 // Trigger GCs so that the newly allocated string moves to old gen.
Steve Block44f0eee2011-05-26 01:26:41 +0100597 HEAP->CollectGarbage(i::NEW_SPACE); // in survivor space now
598 HEAP->CollectGarbage(i::NEW_SPACE); // in old gen now
599 i::Handle<i::String> isymbol = FACTORY->SymbolFromString(istring);
Steve Blocka7e24c12009-10-30 11:49:00 +0000600 CHECK(isymbol->IsSymbol());
601 }
Steve Block44f0eee2011-05-26 01:26:41 +0100602 HEAP->CollectAllGarbage(false);
603 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000604}
605
606
Leon Clarkee46be812010-01-19 14:06:41 +0000607THREADED_TEST(ScavengeExternalString) {
608 TestResource::dispose_count = 0;
Steve Block6ded16b2010-05-10 14:33:55 +0100609 bool in_new_space = false;
Leon Clarkee46be812010-01-19 14:06:41 +0000610 {
611 v8::HandleScope scope;
612 uint16_t* two_byte_string = AsciiToTwoByteString("test string");
613 Local<String> string =
614 String::NewExternal(new TestResource(two_byte_string));
615 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
Steve Block44f0eee2011-05-26 01:26:41 +0100616 HEAP->CollectGarbage(i::NEW_SPACE);
617 in_new_space = HEAP->InNewSpace(*istring);
618 CHECK(in_new_space || HEAP->old_data_space()->Contains(*istring));
Leon Clarkee46be812010-01-19 14:06:41 +0000619 CHECK_EQ(0, TestResource::dispose_count);
620 }
Steve Block44f0eee2011-05-26 01:26:41 +0100621 HEAP->CollectGarbage(in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE);
Leon Clarkee46be812010-01-19 14:06:41 +0000622 CHECK_EQ(1, TestResource::dispose_count);
623}
624
625
626THREADED_TEST(ScavengeExternalAsciiString) {
627 TestAsciiResource::dispose_count = 0;
Steve Block6ded16b2010-05-10 14:33:55 +0100628 bool in_new_space = false;
Leon Clarkee46be812010-01-19 14:06:41 +0000629 {
630 v8::HandleScope scope;
631 const char* one_byte_string = "test string";
632 Local<String> string = String::NewExternal(
633 new TestAsciiResource(i::StrDup(one_byte_string)));
634 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
Steve Block44f0eee2011-05-26 01:26:41 +0100635 HEAP->CollectGarbage(i::NEW_SPACE);
636 in_new_space = HEAP->InNewSpace(*istring);
637 CHECK(in_new_space || HEAP->old_data_space()->Contains(*istring));
Leon Clarkee46be812010-01-19 14:06:41 +0000638 CHECK_EQ(0, TestAsciiResource::dispose_count);
639 }
Steve Block44f0eee2011-05-26 01:26:41 +0100640 HEAP->CollectGarbage(in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE);
Leon Clarkee46be812010-01-19 14:06:41 +0000641 CHECK_EQ(1, TestAsciiResource::dispose_count);
642}
643
644
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100645class TestAsciiResourceWithDisposeControl: public TestAsciiResource {
646 public:
647 static int dispose_calls;
648
649 TestAsciiResourceWithDisposeControl(const char* data, bool dispose)
650 : TestAsciiResource(data),
651 dispose_(dispose) { }
652
653 void Dispose() {
654 ++dispose_calls;
655 if (dispose_) delete this;
656 }
657 private:
658 bool dispose_;
659};
660
661
662int TestAsciiResourceWithDisposeControl::dispose_calls = 0;
663
664
665TEST(ExternalStringWithDisposeHandling) {
666 const char* c_source = "1 + 2 * 3";
667
668 // Use a stack allocated external string resource allocated object.
669 TestAsciiResource::dispose_count = 0;
670 TestAsciiResourceWithDisposeControl::dispose_calls = 0;
671 TestAsciiResourceWithDisposeControl res_stack(i::StrDup(c_source), false);
672 {
673 v8::HandleScope scope;
674 LocalContext env;
675 Local<String> source = String::NewExternal(&res_stack);
676 Local<Script> script = Script::Compile(source);
677 Local<Value> value = script->Run();
678 CHECK(value->IsNumber());
679 CHECK_EQ(7, value->Int32Value());
Steve Block44f0eee2011-05-26 01:26:41 +0100680 HEAP->CollectAllGarbage(false);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100681 CHECK_EQ(0, TestAsciiResource::dispose_count);
682 }
Steve Block44f0eee2011-05-26 01:26:41 +0100683 i::Isolate::Current()->compilation_cache()->Clear();
684 HEAP->CollectAllGarbage(false);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100685 CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls);
686 CHECK_EQ(0, TestAsciiResource::dispose_count);
687
688 // Use a heap allocated external string resource allocated object.
689 TestAsciiResource::dispose_count = 0;
690 TestAsciiResourceWithDisposeControl::dispose_calls = 0;
691 TestAsciiResource* res_heap =
692 new TestAsciiResourceWithDisposeControl(i::StrDup(c_source), true);
693 {
694 v8::HandleScope scope;
695 LocalContext env;
696 Local<String> source = String::NewExternal(res_heap);
697 Local<Script> script = Script::Compile(source);
698 Local<Value> value = script->Run();
699 CHECK(value->IsNumber());
700 CHECK_EQ(7, value->Int32Value());
Steve Block44f0eee2011-05-26 01:26:41 +0100701 HEAP->CollectAllGarbage(false);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100702 CHECK_EQ(0, TestAsciiResource::dispose_count);
703 }
Steve Block44f0eee2011-05-26 01:26:41 +0100704 i::Isolate::Current()->compilation_cache()->Clear();
705 HEAP->CollectAllGarbage(false);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100706 CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls);
707 CHECK_EQ(1, TestAsciiResource::dispose_count);
708}
709
710
Steve Block3ce2e202009-11-05 08:53:23 +0000711THREADED_TEST(StringConcat) {
712 {
713 v8::HandleScope scope;
714 LocalContext env;
715 const char* one_byte_string_1 = "function a_times_t";
716 const char* two_byte_string_1 = "wo_plus_b(a, b) {return ";
717 const char* one_byte_extern_1 = "a * 2 + b;} a_times_two_plus_b(4, 8) + ";
718 const char* two_byte_extern_1 = "a_times_two_plus_b(4, 8) + ";
719 const char* one_byte_string_2 = "a_times_two_plus_b(4, 8) + ";
720 const char* two_byte_string_2 = "a_times_two_plus_b(4, 8) + ";
721 const char* two_byte_extern_2 = "a_times_two_plus_b(1, 2);";
722 Local<String> left = v8_str(one_byte_string_1);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100723
724 uint16_t* two_byte_source = AsciiToTwoByteString(two_byte_string_1);
725 Local<String> right = String::New(two_byte_source);
726 i::DeleteArray(two_byte_source);
727
Steve Block3ce2e202009-11-05 08:53:23 +0000728 Local<String> source = String::Concat(left, right);
729 right = String::NewExternal(
730 new TestAsciiResource(i::StrDup(one_byte_extern_1)));
731 source = String::Concat(source, right);
732 right = String::NewExternal(
733 new TestResource(AsciiToTwoByteString(two_byte_extern_1)));
734 source = String::Concat(source, right);
735 right = v8_str(one_byte_string_2);
736 source = String::Concat(source, right);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100737
738 two_byte_source = AsciiToTwoByteString(two_byte_string_2);
739 right = String::New(two_byte_source);
740 i::DeleteArray(two_byte_source);
741
Steve Block3ce2e202009-11-05 08:53:23 +0000742 source = String::Concat(source, right);
743 right = String::NewExternal(
744 new TestResource(AsciiToTwoByteString(two_byte_extern_2)));
745 source = String::Concat(source, right);
746 Local<Script> script = Script::Compile(source);
747 Local<Value> value = script->Run();
748 CHECK(value->IsNumber());
749 CHECK_EQ(68, value->Int32Value());
750 }
Steve Block44f0eee2011-05-26 01:26:41 +0100751 i::Isolate::Current()->compilation_cache()->Clear();
752 HEAP->CollectAllGarbage(false);
753 HEAP->CollectAllGarbage(false);
Steve Block3ce2e202009-11-05 08:53:23 +0000754}
755
756
Steve Blocka7e24c12009-10-30 11:49:00 +0000757THREADED_TEST(GlobalProperties) {
758 v8::HandleScope scope;
759 LocalContext env;
760 v8::Handle<v8::Object> global = env->Global();
761 global->Set(v8_str("pi"), v8_num(3.1415926));
762 Local<Value> pi = global->Get(v8_str("pi"));
763 CHECK_EQ(3.1415926, pi->NumberValue());
764}
765
766
767static v8::Handle<Value> handle_call(const v8::Arguments& args) {
768 ApiTestFuzzer::Fuzz();
769 return v8_num(102);
770}
771
772
773static v8::Handle<Value> construct_call(const v8::Arguments& args) {
774 ApiTestFuzzer::Fuzz();
775 args.This()->Set(v8_str("x"), v8_num(1));
776 args.This()->Set(v8_str("y"), v8_num(2));
777 return args.This();
778}
779
Ben Murdochf87a2032010-10-22 12:50:53 +0100780static v8::Handle<Value> Return239(Local<String> name, const AccessorInfo&) {
781 ApiTestFuzzer::Fuzz();
782 return v8_num(239);
783}
784
785
Steve Blocka7e24c12009-10-30 11:49:00 +0000786THREADED_TEST(FunctionTemplate) {
787 v8::HandleScope scope;
788 LocalContext env;
789 {
790 Local<v8::FunctionTemplate> fun_templ =
791 v8::FunctionTemplate::New(handle_call);
792 Local<Function> fun = fun_templ->GetFunction();
793 env->Global()->Set(v8_str("obj"), fun);
794 Local<Script> script = v8_compile("obj()");
795 CHECK_EQ(102, script->Run()->Int32Value());
796 }
797 // Use SetCallHandler to initialize a function template, should work like the
798 // previous one.
799 {
800 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
801 fun_templ->SetCallHandler(handle_call);
802 Local<Function> fun = fun_templ->GetFunction();
803 env->Global()->Set(v8_str("obj"), fun);
804 Local<Script> script = v8_compile("obj()");
805 CHECK_EQ(102, script->Run()->Int32Value());
806 }
807 // Test constructor calls.
808 {
809 Local<v8::FunctionTemplate> fun_templ =
810 v8::FunctionTemplate::New(construct_call);
811 fun_templ->SetClassName(v8_str("funky"));
Ben Murdochf87a2032010-10-22 12:50:53 +0100812 fun_templ->InstanceTemplate()->SetAccessor(v8_str("m"), Return239);
Steve Blocka7e24c12009-10-30 11:49:00 +0000813 Local<Function> fun = fun_templ->GetFunction();
814 env->Global()->Set(v8_str("obj"), fun);
815 Local<Script> script = v8_compile("var s = new obj(); s.x");
816 CHECK_EQ(1, script->Run()->Int32Value());
817
818 Local<Value> result = v8_compile("(new obj()).toString()")->Run();
819 CHECK_EQ(v8_str("[object funky]"), result);
Ben Murdochf87a2032010-10-22 12:50:53 +0100820
821 result = v8_compile("(new obj()).m")->Run();
822 CHECK_EQ(239, result->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +0000823 }
824}
825
826
Ben Murdochb8e0da22011-05-16 14:20:40 +0100827static void* expected_ptr;
828static v8::Handle<v8::Value> callback(const v8::Arguments& args) {
829 void* ptr = v8::External::Unwrap(args.Data());
830 CHECK_EQ(expected_ptr, ptr);
831 return v8::Boolean::New(true);
832}
833
834
835static void TestExternalPointerWrapping() {
836 v8::HandleScope scope;
837 LocalContext env;
838
839 v8::Handle<v8::Value> data = v8::External::Wrap(expected_ptr);
840
841 v8::Handle<v8::Object> obj = v8::Object::New();
842 obj->Set(v8_str("func"),
843 v8::FunctionTemplate::New(callback, data)->GetFunction());
844 env->Global()->Set(v8_str("obj"), obj);
845
846 CHECK(CompileRun(
847 "function foo() {\n"
848 " for (var i = 0; i < 13; i++) obj.func();\n"
849 "}\n"
850 "foo(), true")->BooleanValue());
851}
852
853
854THREADED_TEST(ExternalWrap) {
855 // Check heap allocated object.
856 int* ptr = new int;
857 expected_ptr = ptr;
858 TestExternalPointerWrapping();
859 delete ptr;
860
861 // Check stack allocated object.
862 int foo;
863 expected_ptr = &foo;
864 TestExternalPointerWrapping();
865
866 // Check not aligned addresses.
867 const int n = 100;
868 char* s = new char[n];
869 for (int i = 0; i < n; i++) {
870 expected_ptr = s + i;
871 TestExternalPointerWrapping();
872 }
873
874 delete[] s;
875
876 // Check several invalid addresses.
877 expected_ptr = reinterpret_cast<void*>(1);
878 TestExternalPointerWrapping();
879
880 expected_ptr = reinterpret_cast<void*>(0xdeadbeef);
881 TestExternalPointerWrapping();
882
883 expected_ptr = reinterpret_cast<void*>(0xdeadbeef + 1);
884 TestExternalPointerWrapping();
885
886#if defined(V8_HOST_ARCH_X64)
Steve Block1e0659c2011-05-24 12:43:12 +0100887 // Check a value with a leading 1 bit in x64 Smi encoding.
888 expected_ptr = reinterpret_cast<void*>(0x400000000);
889 TestExternalPointerWrapping();
890
Ben Murdochb8e0da22011-05-16 14:20:40 +0100891 expected_ptr = reinterpret_cast<void*>(0xdeadbeefdeadbeef);
892 TestExternalPointerWrapping();
893
894 expected_ptr = reinterpret_cast<void*>(0xdeadbeefdeadbeef + 1);
895 TestExternalPointerWrapping();
896#endif
897}
898
899
Steve Blocka7e24c12009-10-30 11:49:00 +0000900THREADED_TEST(FindInstanceInPrototypeChain) {
901 v8::HandleScope scope;
902 LocalContext env;
903
904 Local<v8::FunctionTemplate> base = v8::FunctionTemplate::New();
905 Local<v8::FunctionTemplate> derived = v8::FunctionTemplate::New();
906 Local<v8::FunctionTemplate> other = v8::FunctionTemplate::New();
907 derived->Inherit(base);
908
909 Local<v8::Function> base_function = base->GetFunction();
910 Local<v8::Function> derived_function = derived->GetFunction();
911 Local<v8::Function> other_function = other->GetFunction();
912
913 Local<v8::Object> base_instance = base_function->NewInstance();
914 Local<v8::Object> derived_instance = derived_function->NewInstance();
915 Local<v8::Object> derived_instance2 = derived_function->NewInstance();
916 Local<v8::Object> other_instance = other_function->NewInstance();
917 derived_instance2->Set(v8_str("__proto__"), derived_instance);
918 other_instance->Set(v8_str("__proto__"), derived_instance2);
919
920 // base_instance is only an instance of base.
921 CHECK_EQ(base_instance,
922 base_instance->FindInstanceInPrototypeChain(base));
923 CHECK(base_instance->FindInstanceInPrototypeChain(derived).IsEmpty());
924 CHECK(base_instance->FindInstanceInPrototypeChain(other).IsEmpty());
925
926 // derived_instance is an instance of base and derived.
927 CHECK_EQ(derived_instance,
928 derived_instance->FindInstanceInPrototypeChain(base));
929 CHECK_EQ(derived_instance,
930 derived_instance->FindInstanceInPrototypeChain(derived));
931 CHECK(derived_instance->FindInstanceInPrototypeChain(other).IsEmpty());
932
933 // other_instance is an instance of other and its immediate
934 // prototype derived_instance2 is an instance of base and derived.
935 // Note, derived_instance is an instance of base and derived too,
936 // but it comes after derived_instance2 in the prototype chain of
937 // other_instance.
938 CHECK_EQ(derived_instance2,
939 other_instance->FindInstanceInPrototypeChain(base));
940 CHECK_EQ(derived_instance2,
941 other_instance->FindInstanceInPrototypeChain(derived));
942 CHECK_EQ(other_instance,
943 other_instance->FindInstanceInPrototypeChain(other));
944}
945
946
Steve Block3ce2e202009-11-05 08:53:23 +0000947THREADED_TEST(TinyInteger) {
948 v8::HandleScope scope;
949 LocalContext env;
950 int32_t value = 239;
951 Local<v8::Integer> value_obj = v8::Integer::New(value);
952 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
953}
954
955
956THREADED_TEST(BigSmiInteger) {
957 v8::HandleScope scope;
958 LocalContext env;
959 int32_t value = i::Smi::kMaxValue;
960 // We cannot add one to a Smi::kMaxValue without wrapping.
961 if (i::kSmiValueSize < 32) {
962 CHECK(i::Smi::IsValid(value));
963 CHECK(!i::Smi::IsValid(value + 1));
964 Local<v8::Integer> value_obj = v8::Integer::New(value);
965 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
966 }
967}
968
969
970THREADED_TEST(BigInteger) {
971 v8::HandleScope scope;
972 LocalContext env;
973 // We cannot add one to a Smi::kMaxValue without wrapping.
974 if (i::kSmiValueSize < 32) {
975 // The casts allow this to compile, even if Smi::kMaxValue is 2^31-1.
976 // The code will not be run in that case, due to the "if" guard.
977 int32_t value =
978 static_cast<int32_t>(static_cast<uint32_t>(i::Smi::kMaxValue) + 1);
979 CHECK(value > i::Smi::kMaxValue);
980 CHECK(!i::Smi::IsValid(value));
981 Local<v8::Integer> value_obj = v8::Integer::New(value);
982 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
983 }
984}
985
986
987THREADED_TEST(TinyUnsignedInteger) {
988 v8::HandleScope scope;
989 LocalContext env;
990 uint32_t value = 239;
991 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
992 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
993}
994
995
996THREADED_TEST(BigUnsignedSmiInteger) {
997 v8::HandleScope scope;
998 LocalContext env;
999 uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue);
1000 CHECK(i::Smi::IsValid(value));
1001 CHECK(!i::Smi::IsValid(value + 1));
1002 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
1003 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
1004}
1005
1006
1007THREADED_TEST(BigUnsignedInteger) {
1008 v8::HandleScope scope;
1009 LocalContext env;
1010 uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue) + 1;
1011 CHECK(value > static_cast<uint32_t>(i::Smi::kMaxValue));
1012 CHECK(!i::Smi::IsValid(value));
1013 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
1014 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
1015}
1016
1017
1018THREADED_TEST(OutOfSignedRangeUnsignedInteger) {
1019 v8::HandleScope scope;
1020 LocalContext env;
1021 uint32_t INT32_MAX_AS_UINT = (1U << 31) - 1;
1022 uint32_t value = INT32_MAX_AS_UINT + 1;
1023 CHECK(value > INT32_MAX_AS_UINT); // No overflow.
1024 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
1025 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
1026}
1027
1028
Steve Blocka7e24c12009-10-30 11:49:00 +00001029THREADED_TEST(Number) {
1030 v8::HandleScope scope;
1031 LocalContext env;
1032 double PI = 3.1415926;
1033 Local<v8::Number> pi_obj = v8::Number::New(PI);
1034 CHECK_EQ(PI, pi_obj->NumberValue());
1035}
1036
1037
1038THREADED_TEST(ToNumber) {
1039 v8::HandleScope scope;
1040 LocalContext env;
1041 Local<String> str = v8_str("3.1415926");
1042 CHECK_EQ(3.1415926, str->NumberValue());
1043 v8::Handle<v8::Boolean> t = v8::True();
1044 CHECK_EQ(1.0, t->NumberValue());
1045 v8::Handle<v8::Boolean> f = v8::False();
1046 CHECK_EQ(0.0, f->NumberValue());
1047}
1048
1049
1050THREADED_TEST(Date) {
1051 v8::HandleScope scope;
1052 LocalContext env;
1053 double PI = 3.1415926;
Ben Murdoch257744e2011-11-30 15:57:28 +00001054 Local<Value> date = v8::Date::New(PI);
1055 CHECK_EQ(3.0, date->NumberValue());
1056 date.As<v8::Date>()->Set(v8_str("property"), v8::Integer::New(42));
1057 CHECK_EQ(42, date.As<v8::Date>()->Get(v8_str("property"))->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001058}
1059
1060
1061THREADED_TEST(Boolean) {
1062 v8::HandleScope scope;
1063 LocalContext env;
1064 v8::Handle<v8::Boolean> t = v8::True();
1065 CHECK(t->Value());
1066 v8::Handle<v8::Boolean> f = v8::False();
1067 CHECK(!f->Value());
1068 v8::Handle<v8::Primitive> u = v8::Undefined();
1069 CHECK(!u->BooleanValue());
1070 v8::Handle<v8::Primitive> n = v8::Null();
1071 CHECK(!n->BooleanValue());
1072 v8::Handle<String> str1 = v8_str("");
1073 CHECK(!str1->BooleanValue());
1074 v8::Handle<String> str2 = v8_str("x");
1075 CHECK(str2->BooleanValue());
1076 CHECK(!v8::Number::New(0)->BooleanValue());
1077 CHECK(v8::Number::New(-1)->BooleanValue());
1078 CHECK(v8::Number::New(1)->BooleanValue());
1079 CHECK(v8::Number::New(42)->BooleanValue());
1080 CHECK(!v8_compile("NaN")->Run()->BooleanValue());
1081}
1082
1083
1084static v8::Handle<Value> DummyCallHandler(const v8::Arguments& args) {
1085 ApiTestFuzzer::Fuzz();
1086 return v8_num(13.4);
1087}
1088
1089
1090static v8::Handle<Value> GetM(Local<String> name, const AccessorInfo&) {
1091 ApiTestFuzzer::Fuzz();
1092 return v8_num(876);
1093}
1094
1095
1096THREADED_TEST(GlobalPrototype) {
1097 v8::HandleScope scope;
1098 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New();
1099 func_templ->PrototypeTemplate()->Set(
1100 "dummy",
1101 v8::FunctionTemplate::New(DummyCallHandler));
1102 v8::Handle<ObjectTemplate> templ = func_templ->InstanceTemplate();
1103 templ->Set("x", v8_num(200));
1104 templ->SetAccessor(v8_str("m"), GetM);
1105 LocalContext env(0, templ);
1106 v8::Handle<v8::Object> obj = env->Global();
1107 v8::Handle<Script> script = v8_compile("dummy()");
1108 v8::Handle<Value> result = script->Run();
1109 CHECK_EQ(13.4, result->NumberValue());
1110 CHECK_EQ(200, v8_compile("x")->Run()->Int32Value());
1111 CHECK_EQ(876, v8_compile("m")->Run()->Int32Value());
1112}
1113
1114
Steve Blocka7e24c12009-10-30 11:49:00 +00001115THREADED_TEST(ObjectTemplate) {
1116 v8::HandleScope scope;
1117 Local<ObjectTemplate> templ1 = ObjectTemplate::New();
1118 templ1->Set("x", v8_num(10));
1119 templ1->Set("y", v8_num(13));
1120 LocalContext env;
1121 Local<v8::Object> instance1 = templ1->NewInstance();
1122 env->Global()->Set(v8_str("p"), instance1);
1123 CHECK(v8_compile("(p.x == 10)")->Run()->BooleanValue());
1124 CHECK(v8_compile("(p.y == 13)")->Run()->BooleanValue());
1125 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New();
1126 fun->PrototypeTemplate()->Set("nirk", v8_num(123));
1127 Local<ObjectTemplate> templ2 = fun->InstanceTemplate();
1128 templ2->Set("a", v8_num(12));
1129 templ2->Set("b", templ1);
1130 Local<v8::Object> instance2 = templ2->NewInstance();
1131 env->Global()->Set(v8_str("q"), instance2);
1132 CHECK(v8_compile("(q.nirk == 123)")->Run()->BooleanValue());
1133 CHECK(v8_compile("(q.a == 12)")->Run()->BooleanValue());
1134 CHECK(v8_compile("(q.b.x == 10)")->Run()->BooleanValue());
1135 CHECK(v8_compile("(q.b.y == 13)")->Run()->BooleanValue());
1136}
1137
1138
1139static v8::Handle<Value> GetFlabby(const v8::Arguments& args) {
1140 ApiTestFuzzer::Fuzz();
1141 return v8_num(17.2);
1142}
1143
1144
1145static v8::Handle<Value> GetKnurd(Local<String> property, const AccessorInfo&) {
1146 ApiTestFuzzer::Fuzz();
1147 return v8_num(15.2);
1148}
1149
1150
1151THREADED_TEST(DescriptorInheritance) {
1152 v8::HandleScope scope;
1153 v8::Handle<v8::FunctionTemplate> super = v8::FunctionTemplate::New();
1154 super->PrototypeTemplate()->Set("flabby",
1155 v8::FunctionTemplate::New(GetFlabby));
1156 super->PrototypeTemplate()->Set("PI", v8_num(3.14));
1157
1158 super->InstanceTemplate()->SetAccessor(v8_str("knurd"), GetKnurd);
1159
1160 v8::Handle<v8::FunctionTemplate> base1 = v8::FunctionTemplate::New();
1161 base1->Inherit(super);
1162 base1->PrototypeTemplate()->Set("v1", v8_num(20.1));
1163
1164 v8::Handle<v8::FunctionTemplate> base2 = v8::FunctionTemplate::New();
1165 base2->Inherit(super);
1166 base2->PrototypeTemplate()->Set("v2", v8_num(10.1));
1167
1168 LocalContext env;
1169
1170 env->Global()->Set(v8_str("s"), super->GetFunction());
1171 env->Global()->Set(v8_str("base1"), base1->GetFunction());
1172 env->Global()->Set(v8_str("base2"), base2->GetFunction());
1173
1174 // Checks right __proto__ chain.
1175 CHECK(CompileRun("base1.prototype.__proto__ == s.prototype")->BooleanValue());
1176 CHECK(CompileRun("base2.prototype.__proto__ == s.prototype")->BooleanValue());
1177
1178 CHECK(v8_compile("s.prototype.PI == 3.14")->Run()->BooleanValue());
1179
1180 // Instance accessor should not be visible on function object or its prototype
1181 CHECK(CompileRun("s.knurd == undefined")->BooleanValue());
1182 CHECK(CompileRun("s.prototype.knurd == undefined")->BooleanValue());
1183 CHECK(CompileRun("base1.prototype.knurd == undefined")->BooleanValue());
1184
1185 env->Global()->Set(v8_str("obj"),
1186 base1->GetFunction()->NewInstance());
1187 CHECK_EQ(17.2, v8_compile("obj.flabby()")->Run()->NumberValue());
1188 CHECK(v8_compile("'flabby' in obj")->Run()->BooleanValue());
1189 CHECK_EQ(15.2, v8_compile("obj.knurd")->Run()->NumberValue());
1190 CHECK(v8_compile("'knurd' in obj")->Run()->BooleanValue());
1191 CHECK_EQ(20.1, v8_compile("obj.v1")->Run()->NumberValue());
1192
1193 env->Global()->Set(v8_str("obj2"),
1194 base2->GetFunction()->NewInstance());
1195 CHECK_EQ(17.2, v8_compile("obj2.flabby()")->Run()->NumberValue());
1196 CHECK(v8_compile("'flabby' in obj2")->Run()->BooleanValue());
1197 CHECK_EQ(15.2, v8_compile("obj2.knurd")->Run()->NumberValue());
1198 CHECK(v8_compile("'knurd' in obj2")->Run()->BooleanValue());
1199 CHECK_EQ(10.1, v8_compile("obj2.v2")->Run()->NumberValue());
1200
1201 // base1 and base2 cannot cross reference to each's prototype
1202 CHECK(v8_compile("obj.v2")->Run()->IsUndefined());
1203 CHECK(v8_compile("obj2.v1")->Run()->IsUndefined());
1204}
1205
1206
1207int echo_named_call_count;
1208
1209
1210static v8::Handle<Value> EchoNamedProperty(Local<String> name,
1211 const AccessorInfo& info) {
1212 ApiTestFuzzer::Fuzz();
1213 CHECK_EQ(v8_str("data"), info.Data());
1214 echo_named_call_count++;
1215 return name;
1216}
1217
1218
1219THREADED_TEST(NamedPropertyHandlerGetter) {
1220 echo_named_call_count = 0;
1221 v8::HandleScope scope;
1222 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1223 templ->InstanceTemplate()->SetNamedPropertyHandler(EchoNamedProperty,
1224 0, 0, 0, 0,
1225 v8_str("data"));
1226 LocalContext env;
1227 env->Global()->Set(v8_str("obj"),
1228 templ->GetFunction()->NewInstance());
1229 CHECK_EQ(echo_named_call_count, 0);
1230 v8_compile("obj.x")->Run();
1231 CHECK_EQ(echo_named_call_count, 1);
1232 const char* code = "var str = 'oddle'; obj[str] + obj.poddle;";
1233 v8::Handle<Value> str = CompileRun(code);
1234 String::AsciiValue value(str);
1235 CHECK_EQ(*value, "oddlepoddle");
1236 // Check default behavior
1237 CHECK_EQ(v8_compile("obj.flob = 10;")->Run()->Int32Value(), 10);
1238 CHECK(v8_compile("'myProperty' in obj")->Run()->BooleanValue());
1239 CHECK(v8_compile("delete obj.myProperty")->Run()->BooleanValue());
1240}
1241
1242
1243int echo_indexed_call_count = 0;
1244
1245
1246static v8::Handle<Value> EchoIndexedProperty(uint32_t index,
1247 const AccessorInfo& info) {
1248 ApiTestFuzzer::Fuzz();
1249 CHECK_EQ(v8_num(637), info.Data());
1250 echo_indexed_call_count++;
1251 return v8_num(index);
1252}
1253
1254
1255THREADED_TEST(IndexedPropertyHandlerGetter) {
1256 v8::HandleScope scope;
1257 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1258 templ->InstanceTemplate()->SetIndexedPropertyHandler(EchoIndexedProperty,
1259 0, 0, 0, 0,
1260 v8_num(637));
1261 LocalContext env;
1262 env->Global()->Set(v8_str("obj"),
1263 templ->GetFunction()->NewInstance());
1264 Local<Script> script = v8_compile("obj[900]");
1265 CHECK_EQ(script->Run()->Int32Value(), 900);
1266}
1267
1268
1269v8::Handle<v8::Object> bottom;
1270
1271static v8::Handle<Value> CheckThisIndexedPropertyHandler(
1272 uint32_t index,
1273 const AccessorInfo& info) {
1274 ApiTestFuzzer::Fuzz();
1275 CHECK(info.This()->Equals(bottom));
1276 return v8::Handle<Value>();
1277}
1278
1279static v8::Handle<Value> CheckThisNamedPropertyHandler(
1280 Local<String> name,
1281 const AccessorInfo& info) {
1282 ApiTestFuzzer::Fuzz();
1283 CHECK(info.This()->Equals(bottom));
1284 return v8::Handle<Value>();
1285}
1286
1287
1288v8::Handle<Value> CheckThisIndexedPropertySetter(uint32_t index,
1289 Local<Value> value,
1290 const AccessorInfo& info) {
1291 ApiTestFuzzer::Fuzz();
1292 CHECK(info.This()->Equals(bottom));
1293 return v8::Handle<Value>();
1294}
1295
1296
1297v8::Handle<Value> CheckThisNamedPropertySetter(Local<String> property,
1298 Local<Value> value,
1299 const AccessorInfo& info) {
1300 ApiTestFuzzer::Fuzz();
1301 CHECK(info.This()->Equals(bottom));
1302 return v8::Handle<Value>();
1303}
1304
Iain Merrick75681382010-08-19 15:07:18 +01001305v8::Handle<v8::Integer> CheckThisIndexedPropertyQuery(
Steve Blocka7e24c12009-10-30 11:49:00 +00001306 uint32_t index,
1307 const AccessorInfo& info) {
1308 ApiTestFuzzer::Fuzz();
1309 CHECK(info.This()->Equals(bottom));
Iain Merrick75681382010-08-19 15:07:18 +01001310 return v8::Handle<v8::Integer>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001311}
1312
1313
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001314v8::Handle<v8::Integer> CheckThisNamedPropertyQuery(Local<String> property,
Steve Blocka7e24c12009-10-30 11:49:00 +00001315 const AccessorInfo& info) {
1316 ApiTestFuzzer::Fuzz();
1317 CHECK(info.This()->Equals(bottom));
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001318 return v8::Handle<v8::Integer>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001319}
1320
1321
1322v8::Handle<v8::Boolean> CheckThisIndexedPropertyDeleter(
1323 uint32_t index,
1324 const AccessorInfo& info) {
1325 ApiTestFuzzer::Fuzz();
1326 CHECK(info.This()->Equals(bottom));
1327 return v8::Handle<v8::Boolean>();
1328}
1329
1330
1331v8::Handle<v8::Boolean> CheckThisNamedPropertyDeleter(
1332 Local<String> property,
1333 const AccessorInfo& info) {
1334 ApiTestFuzzer::Fuzz();
1335 CHECK(info.This()->Equals(bottom));
1336 return v8::Handle<v8::Boolean>();
1337}
1338
1339
1340v8::Handle<v8::Array> CheckThisIndexedPropertyEnumerator(
1341 const AccessorInfo& info) {
1342 ApiTestFuzzer::Fuzz();
1343 CHECK(info.This()->Equals(bottom));
1344 return v8::Handle<v8::Array>();
1345}
1346
1347
1348v8::Handle<v8::Array> CheckThisNamedPropertyEnumerator(
1349 const AccessorInfo& info) {
1350 ApiTestFuzzer::Fuzz();
1351 CHECK(info.This()->Equals(bottom));
1352 return v8::Handle<v8::Array>();
1353}
1354
1355
1356THREADED_TEST(PropertyHandlerInPrototype) {
1357 v8::HandleScope scope;
1358 LocalContext env;
1359
1360 // Set up a prototype chain with three interceptors.
1361 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1362 templ->InstanceTemplate()->SetIndexedPropertyHandler(
1363 CheckThisIndexedPropertyHandler,
1364 CheckThisIndexedPropertySetter,
1365 CheckThisIndexedPropertyQuery,
1366 CheckThisIndexedPropertyDeleter,
1367 CheckThisIndexedPropertyEnumerator);
1368
1369 templ->InstanceTemplate()->SetNamedPropertyHandler(
1370 CheckThisNamedPropertyHandler,
1371 CheckThisNamedPropertySetter,
1372 CheckThisNamedPropertyQuery,
1373 CheckThisNamedPropertyDeleter,
1374 CheckThisNamedPropertyEnumerator);
1375
1376 bottom = templ->GetFunction()->NewInstance();
1377 Local<v8::Object> top = templ->GetFunction()->NewInstance();
1378 Local<v8::Object> middle = templ->GetFunction()->NewInstance();
1379
1380 bottom->Set(v8_str("__proto__"), middle);
1381 middle->Set(v8_str("__proto__"), top);
1382 env->Global()->Set(v8_str("obj"), bottom);
1383
1384 // Indexed and named get.
1385 Script::Compile(v8_str("obj[0]"))->Run();
1386 Script::Compile(v8_str("obj.x"))->Run();
1387
1388 // Indexed and named set.
1389 Script::Compile(v8_str("obj[1] = 42"))->Run();
1390 Script::Compile(v8_str("obj.y = 42"))->Run();
1391
1392 // Indexed and named query.
1393 Script::Compile(v8_str("0 in obj"))->Run();
1394 Script::Compile(v8_str("'x' in obj"))->Run();
1395
1396 // Indexed and named deleter.
1397 Script::Compile(v8_str("delete obj[0]"))->Run();
1398 Script::Compile(v8_str("delete obj.x"))->Run();
1399
1400 // Enumerators.
1401 Script::Compile(v8_str("for (var p in obj) ;"))->Run();
1402}
1403
1404
1405static v8::Handle<Value> PrePropertyHandlerGet(Local<String> key,
1406 const AccessorInfo& info) {
1407 ApiTestFuzzer::Fuzz();
1408 if (v8_str("pre")->Equals(key)) {
1409 return v8_str("PrePropertyHandler: pre");
1410 }
1411 return v8::Handle<String>();
1412}
1413
1414
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001415static v8::Handle<v8::Integer> PrePropertyHandlerQuery(Local<String> key,
1416 const AccessorInfo&) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001417 if (v8_str("pre")->Equals(key)) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001418 return v8::Integer::New(v8::None);
Steve Blocka7e24c12009-10-30 11:49:00 +00001419 }
1420
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001421 return v8::Handle<v8::Integer>(); // do not intercept the call
Steve Blocka7e24c12009-10-30 11:49:00 +00001422}
1423
1424
1425THREADED_TEST(PrePropertyHandler) {
1426 v8::HandleScope scope;
1427 v8::Handle<v8::FunctionTemplate> desc = v8::FunctionTemplate::New();
1428 desc->InstanceTemplate()->SetNamedPropertyHandler(PrePropertyHandlerGet,
1429 0,
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001430 PrePropertyHandlerQuery);
Steve Blocka7e24c12009-10-30 11:49:00 +00001431 LocalContext env(NULL, desc->InstanceTemplate());
1432 Script::Compile(v8_str(
1433 "var pre = 'Object: pre'; var on = 'Object: on';"))->Run();
1434 v8::Handle<Value> result_pre = Script::Compile(v8_str("pre"))->Run();
1435 CHECK_EQ(v8_str("PrePropertyHandler: pre"), result_pre);
1436 v8::Handle<Value> result_on = Script::Compile(v8_str("on"))->Run();
1437 CHECK_EQ(v8_str("Object: on"), result_on);
1438 v8::Handle<Value> result_post = Script::Compile(v8_str("post"))->Run();
1439 CHECK(result_post.IsEmpty());
1440}
1441
1442
1443THREADED_TEST(UndefinedIsNotEnumerable) {
1444 v8::HandleScope scope;
1445 LocalContext env;
1446 v8::Handle<Value> result = Script::Compile(v8_str(
1447 "this.propertyIsEnumerable(undefined)"))->Run();
1448 CHECK(result->IsFalse());
1449}
1450
1451
1452v8::Handle<Script> call_recursively_script;
Leon Clarke4515c472010-02-03 11:58:03 +00001453static const int kTargetRecursionDepth = 200; // near maximum
Steve Blocka7e24c12009-10-30 11:49:00 +00001454
1455
1456static v8::Handle<Value> CallScriptRecursivelyCall(const v8::Arguments& args) {
1457 ApiTestFuzzer::Fuzz();
1458 int depth = args.This()->Get(v8_str("depth"))->Int32Value();
1459 if (depth == kTargetRecursionDepth) return v8::Undefined();
1460 args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1));
1461 return call_recursively_script->Run();
1462}
1463
1464
1465static v8::Handle<Value> CallFunctionRecursivelyCall(
1466 const v8::Arguments& args) {
1467 ApiTestFuzzer::Fuzz();
1468 int depth = args.This()->Get(v8_str("depth"))->Int32Value();
1469 if (depth == kTargetRecursionDepth) {
1470 printf("[depth = %d]\n", depth);
1471 return v8::Undefined();
1472 }
1473 args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1));
1474 v8::Handle<Value> function =
1475 args.This()->Get(v8_str("callFunctionRecursively"));
Steve Block6ded16b2010-05-10 14:33:55 +01001476 return function.As<Function>()->Call(args.This(), 0, NULL);
Steve Blocka7e24c12009-10-30 11:49:00 +00001477}
1478
1479
1480THREADED_TEST(DeepCrossLanguageRecursion) {
1481 v8::HandleScope scope;
1482 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New();
1483 global->Set(v8_str("callScriptRecursively"),
1484 v8::FunctionTemplate::New(CallScriptRecursivelyCall));
1485 global->Set(v8_str("callFunctionRecursively"),
1486 v8::FunctionTemplate::New(CallFunctionRecursivelyCall));
1487 LocalContext env(NULL, global);
1488
1489 env->Global()->Set(v8_str("depth"), v8::Integer::New(0));
1490 call_recursively_script = v8_compile("callScriptRecursively()");
1491 v8::Handle<Value> result = call_recursively_script->Run();
1492 call_recursively_script = v8::Handle<Script>();
1493
1494 env->Global()->Set(v8_str("depth"), v8::Integer::New(0));
1495 Script::Compile(v8_str("callFunctionRecursively()"))->Run();
1496}
1497
1498
1499static v8::Handle<Value>
1500 ThrowingPropertyHandlerGet(Local<String> key, const AccessorInfo&) {
1501 ApiTestFuzzer::Fuzz();
1502 return v8::ThrowException(key);
1503}
1504
1505
1506static v8::Handle<Value> ThrowingPropertyHandlerSet(Local<String> key,
1507 Local<Value>,
1508 const AccessorInfo&) {
1509 v8::ThrowException(key);
1510 return v8::Undefined(); // not the same as v8::Handle<v8::Value>()
1511}
1512
1513
1514THREADED_TEST(CallbackExceptionRegression) {
1515 v8::HandleScope scope;
1516 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
1517 obj->SetNamedPropertyHandler(ThrowingPropertyHandlerGet,
1518 ThrowingPropertyHandlerSet);
1519 LocalContext env;
1520 env->Global()->Set(v8_str("obj"), obj->NewInstance());
1521 v8::Handle<Value> otto = Script::Compile(v8_str(
1522 "try { with (obj) { otto; } } catch (e) { e; }"))->Run();
1523 CHECK_EQ(v8_str("otto"), otto);
1524 v8::Handle<Value> netto = Script::Compile(v8_str(
1525 "try { with (obj) { netto = 4; } } catch (e) { e; }"))->Run();
1526 CHECK_EQ(v8_str("netto"), netto);
1527}
1528
1529
Steve Blocka7e24c12009-10-30 11:49:00 +00001530THREADED_TEST(FunctionPrototype) {
1531 v8::HandleScope scope;
1532 Local<v8::FunctionTemplate> Foo = v8::FunctionTemplate::New();
1533 Foo->PrototypeTemplate()->Set(v8_str("plak"), v8_num(321));
1534 LocalContext env;
1535 env->Global()->Set(v8_str("Foo"), Foo->GetFunction());
1536 Local<Script> script = Script::Compile(v8_str("Foo.prototype.plak"));
1537 CHECK_EQ(script->Run()->Int32Value(), 321);
1538}
1539
1540
1541THREADED_TEST(InternalFields) {
1542 v8::HandleScope scope;
1543 LocalContext env;
1544
1545 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1546 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate();
1547 instance_templ->SetInternalFieldCount(1);
1548 Local<v8::Object> obj = templ->GetFunction()->NewInstance();
1549 CHECK_EQ(1, obj->InternalFieldCount());
1550 CHECK(obj->GetInternalField(0)->IsUndefined());
1551 obj->SetInternalField(0, v8_num(17));
1552 CHECK_EQ(17, obj->GetInternalField(0)->Int32Value());
1553}
1554
1555
Steve Block6ded16b2010-05-10 14:33:55 +01001556THREADED_TEST(GlobalObjectInternalFields) {
1557 v8::HandleScope scope;
1558 Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
1559 global_template->SetInternalFieldCount(1);
1560 LocalContext env(NULL, global_template);
1561 v8::Handle<v8::Object> global_proxy = env->Global();
1562 v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>();
1563 CHECK_EQ(1, global->InternalFieldCount());
1564 CHECK(global->GetInternalField(0)->IsUndefined());
1565 global->SetInternalField(0, v8_num(17));
1566 CHECK_EQ(17, global->GetInternalField(0)->Int32Value());
1567}
1568
1569
Steve Blocka7e24c12009-10-30 11:49:00 +00001570THREADED_TEST(InternalFieldsNativePointers) {
1571 v8::HandleScope scope;
1572 LocalContext env;
1573
1574 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1575 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate();
1576 instance_templ->SetInternalFieldCount(1);
1577 Local<v8::Object> obj = templ->GetFunction()->NewInstance();
1578 CHECK_EQ(1, obj->InternalFieldCount());
1579 CHECK(obj->GetPointerFromInternalField(0) == NULL);
1580
1581 char* data = new char[100];
1582
1583 void* aligned = data;
Ben Murdochf87a2032010-10-22 12:50:53 +01001584 CHECK_EQ(0, static_cast<int>(reinterpret_cast<uintptr_t>(aligned) & 0x1));
Steve Blocka7e24c12009-10-30 11:49:00 +00001585 void* unaligned = data + 1;
Ben Murdochf87a2032010-10-22 12:50:53 +01001586 CHECK_EQ(1, static_cast<int>(reinterpret_cast<uintptr_t>(unaligned) & 0x1));
Steve Blocka7e24c12009-10-30 11:49:00 +00001587
1588 // Check reading and writing aligned pointers.
1589 obj->SetPointerInInternalField(0, aligned);
Steve Block44f0eee2011-05-26 01:26:41 +01001590 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +00001591 CHECK_EQ(aligned, obj->GetPointerFromInternalField(0));
1592
1593 // Check reading and writing unaligned pointers.
1594 obj->SetPointerInInternalField(0, unaligned);
Steve Block44f0eee2011-05-26 01:26:41 +01001595 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +00001596 CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0));
1597
1598 delete[] data;
1599}
1600
1601
Steve Block3ce2e202009-11-05 08:53:23 +00001602THREADED_TEST(InternalFieldsNativePointersAndExternal) {
1603 v8::HandleScope scope;
1604 LocalContext env;
1605
1606 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1607 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate();
1608 instance_templ->SetInternalFieldCount(1);
1609 Local<v8::Object> obj = templ->GetFunction()->NewInstance();
1610 CHECK_EQ(1, obj->InternalFieldCount());
1611 CHECK(obj->GetPointerFromInternalField(0) == NULL);
1612
1613 char* data = new char[100];
1614
1615 void* aligned = data;
Ben Murdochf87a2032010-10-22 12:50:53 +01001616 CHECK_EQ(0, static_cast<int>(reinterpret_cast<uintptr_t>(aligned) & 0x1));
Steve Block3ce2e202009-11-05 08:53:23 +00001617 void* unaligned = data + 1;
Ben Murdochf87a2032010-10-22 12:50:53 +01001618 CHECK_EQ(1, static_cast<int>(reinterpret_cast<uintptr_t>(unaligned) & 0x1));
Steve Block3ce2e202009-11-05 08:53:23 +00001619
1620 obj->SetPointerInInternalField(0, aligned);
Steve Block44f0eee2011-05-26 01:26:41 +01001621 HEAP->CollectAllGarbage(false);
Steve Block3ce2e202009-11-05 08:53:23 +00001622 CHECK_EQ(aligned, v8::External::Unwrap(obj->GetInternalField(0)));
1623
1624 obj->SetPointerInInternalField(0, unaligned);
Steve Block44f0eee2011-05-26 01:26:41 +01001625 HEAP->CollectAllGarbage(false);
Steve Block3ce2e202009-11-05 08:53:23 +00001626 CHECK_EQ(unaligned, v8::External::Unwrap(obj->GetInternalField(0)));
1627
1628 obj->SetInternalField(0, v8::External::Wrap(aligned));
Steve Block44f0eee2011-05-26 01:26:41 +01001629 HEAP->CollectAllGarbage(false);
Steve Block3ce2e202009-11-05 08:53:23 +00001630 CHECK_EQ(aligned, obj->GetPointerFromInternalField(0));
1631
1632 obj->SetInternalField(0, v8::External::Wrap(unaligned));
Steve Block44f0eee2011-05-26 01:26:41 +01001633 HEAP->CollectAllGarbage(false);
Steve Block3ce2e202009-11-05 08:53:23 +00001634 CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0));
1635
1636 delete[] data;
1637}
1638
1639
Steve Blocka7e24c12009-10-30 11:49:00 +00001640THREADED_TEST(IdentityHash) {
1641 v8::HandleScope scope;
1642 LocalContext env;
1643
1644 // Ensure that the test starts with an fresh heap to test whether the hash
1645 // code is based on the address.
Steve Block44f0eee2011-05-26 01:26:41 +01001646 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +00001647 Local<v8::Object> obj = v8::Object::New();
1648 int hash = obj->GetIdentityHash();
1649 int hash1 = obj->GetIdentityHash();
1650 CHECK_EQ(hash, hash1);
1651 int hash2 = v8::Object::New()->GetIdentityHash();
1652 // Since the identity hash is essentially a random number two consecutive
1653 // objects should not be assigned the same hash code. If the test below fails
1654 // the random number generator should be evaluated.
1655 CHECK_NE(hash, hash2);
Steve Block44f0eee2011-05-26 01:26:41 +01001656 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +00001657 int hash3 = v8::Object::New()->GetIdentityHash();
1658 // Make sure that the identity hash is not based on the initial address of
1659 // the object alone. If the test below fails the random number generator
1660 // should be evaluated.
1661 CHECK_NE(hash, hash3);
1662 int hash4 = obj->GetIdentityHash();
1663 CHECK_EQ(hash, hash4);
Steve Block1e0659c2011-05-24 12:43:12 +01001664
1665 // Check identity hashes behaviour in the presence of JS accessors.
1666 // Put a getter for 'v8::IdentityHash' on the Object's prototype:
1667 {
1668 CompileRun("Object.prototype['v8::IdentityHash'] = 42;\n");
1669 Local<v8::Object> o1 = v8::Object::New();
1670 Local<v8::Object> o2 = v8::Object::New();
1671 CHECK_NE(o1->GetIdentityHash(), o2->GetIdentityHash());
1672 }
1673 {
1674 CompileRun(
1675 "function cnst() { return 42; };\n"
1676 "Object.prototype.__defineGetter__('v8::IdentityHash', cnst);\n");
1677 Local<v8::Object> o1 = v8::Object::New();
1678 Local<v8::Object> o2 = v8::Object::New();
1679 CHECK_NE(o1->GetIdentityHash(), o2->GetIdentityHash());
1680 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001681}
1682
1683
1684THREADED_TEST(HiddenProperties) {
1685 v8::HandleScope scope;
1686 LocalContext env;
1687
1688 v8::Local<v8::Object> obj = v8::Object::New();
1689 v8::Local<v8::String> key = v8_str("api-test::hidden-key");
1690 v8::Local<v8::String> empty = v8_str("");
1691 v8::Local<v8::String> prop_name = v8_str("prop_name");
1692
Steve Block44f0eee2011-05-26 01:26:41 +01001693 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +00001694
1695 // Make sure delete of a non-existent hidden value works
1696 CHECK(obj->DeleteHiddenValue(key));
1697
1698 CHECK(obj->SetHiddenValue(key, v8::Integer::New(1503)));
1699 CHECK_EQ(1503, obj->GetHiddenValue(key)->Int32Value());
1700 CHECK(obj->SetHiddenValue(key, v8::Integer::New(2002)));
1701 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1702
Steve Block44f0eee2011-05-26 01:26:41 +01001703 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +00001704
1705 // Make sure we do not find the hidden property.
1706 CHECK(!obj->Has(empty));
1707 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1708 CHECK(obj->Get(empty)->IsUndefined());
1709 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1710 CHECK(obj->Set(empty, v8::Integer::New(2003)));
1711 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1712 CHECK_EQ(2003, obj->Get(empty)->Int32Value());
1713
Steve Block44f0eee2011-05-26 01:26:41 +01001714 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +00001715
1716 // Add another property and delete it afterwards to force the object in
1717 // slow case.
1718 CHECK(obj->Set(prop_name, v8::Integer::New(2008)));
1719 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1720 CHECK_EQ(2008, obj->Get(prop_name)->Int32Value());
1721 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1722 CHECK(obj->Delete(prop_name));
1723 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1724
Steve Block44f0eee2011-05-26 01:26:41 +01001725 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +00001726
1727 CHECK(obj->DeleteHiddenValue(key));
1728 CHECK(obj->GetHiddenValue(key).IsEmpty());
1729}
1730
1731
Steve Blockd0582a62009-12-15 09:54:21 +00001732static bool interceptor_for_hidden_properties_called;
Steve Blocka7e24c12009-10-30 11:49:00 +00001733static v8::Handle<Value> InterceptorForHiddenProperties(
1734 Local<String> name, const AccessorInfo& info) {
Steve Blockd0582a62009-12-15 09:54:21 +00001735 interceptor_for_hidden_properties_called = true;
Steve Blocka7e24c12009-10-30 11:49:00 +00001736 return v8::Handle<Value>();
1737}
1738
1739
1740THREADED_TEST(HiddenPropertiesWithInterceptors) {
1741 v8::HandleScope scope;
1742 LocalContext context;
1743
Steve Blockd0582a62009-12-15 09:54:21 +00001744 interceptor_for_hidden_properties_called = false;
1745
Steve Blocka7e24c12009-10-30 11:49:00 +00001746 v8::Local<v8::String> key = v8_str("api-test::hidden-key");
1747
1748 // Associate an interceptor with an object and start setting hidden values.
1749 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
1750 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
1751 instance_templ->SetNamedPropertyHandler(InterceptorForHiddenProperties);
1752 Local<v8::Function> function = fun_templ->GetFunction();
1753 Local<v8::Object> obj = function->NewInstance();
1754 CHECK(obj->SetHiddenValue(key, v8::Integer::New(2302)));
1755 CHECK_EQ(2302, obj->GetHiddenValue(key)->Int32Value());
Steve Blockd0582a62009-12-15 09:54:21 +00001756 CHECK(!interceptor_for_hidden_properties_called);
Steve Blocka7e24c12009-10-30 11:49:00 +00001757}
1758
1759
1760THREADED_TEST(External) {
1761 v8::HandleScope scope;
1762 int x = 3;
1763 Local<v8::External> ext = v8::External::New(&x);
1764 LocalContext env;
1765 env->Global()->Set(v8_str("ext"), ext);
1766 Local<Value> reext_obj = Script::Compile(v8_str("this.ext"))->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01001767 v8::Handle<v8::External> reext = reext_obj.As<v8::External>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001768 int* ptr = static_cast<int*>(reext->Value());
1769 CHECK_EQ(x, 3);
1770 *ptr = 10;
1771 CHECK_EQ(x, 10);
1772
1773 // Make sure unaligned pointers are wrapped properly.
1774 char* data = i::StrDup("0123456789");
1775 Local<v8::Value> zero = v8::External::Wrap(&data[0]);
1776 Local<v8::Value> one = v8::External::Wrap(&data[1]);
1777 Local<v8::Value> two = v8::External::Wrap(&data[2]);
1778 Local<v8::Value> three = v8::External::Wrap(&data[3]);
1779
1780 char* char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(zero));
1781 CHECK_EQ('0', *char_ptr);
1782 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(one));
1783 CHECK_EQ('1', *char_ptr);
1784 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(two));
1785 CHECK_EQ('2', *char_ptr);
1786 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(three));
1787 CHECK_EQ('3', *char_ptr);
1788 i::DeleteArray(data);
1789}
1790
1791
1792THREADED_TEST(GlobalHandle) {
1793 v8::Persistent<String> global;
1794 {
1795 v8::HandleScope scope;
1796 Local<String> str = v8_str("str");
1797 global = v8::Persistent<String>::New(str);
1798 }
1799 CHECK_EQ(global->Length(), 3);
1800 global.Dispose();
1801}
1802
1803
Steve Block44f0eee2011-05-26 01:26:41 +01001804static int NumberOfWeakCalls = 0;
1805static void WeakPointerCallback(Persistent<Value> handle, void* id) {
1806 CHECK_EQ(reinterpret_cast<void*>(1234), id);
1807 NumberOfWeakCalls++;
1808 handle.Dispose();
1809}
1810
1811THREADED_TEST(ApiObjectGroups) {
1812 HandleScope scope;
1813 LocalContext env;
1814
1815 NumberOfWeakCalls = 0;
1816
1817 Persistent<Object> g1s1;
1818 Persistent<Object> g1s2;
1819 Persistent<Object> g1c1;
1820 Persistent<Object> g2s1;
1821 Persistent<Object> g2s2;
1822 Persistent<Object> g2c1;
1823
1824 {
1825 HandleScope scope;
1826 g1s1 = Persistent<Object>::New(Object::New());
1827 g1s2 = Persistent<Object>::New(Object::New());
1828 g1c1 = Persistent<Object>::New(Object::New());
1829 g1s1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
1830 g1s2.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
1831 g1c1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
1832
1833 g2s1 = Persistent<Object>::New(Object::New());
1834 g2s2 = Persistent<Object>::New(Object::New());
1835 g2c1 = Persistent<Object>::New(Object::New());
1836 g2s1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
1837 g2s2.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
1838 g2c1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
1839 }
1840
1841 Persistent<Object> root = Persistent<Object>::New(g1s1); // make a root.
1842
1843 // Connect group 1 and 2, make a cycle.
1844 CHECK(g1s2->Set(0, g2s2));
1845 CHECK(g2s1->Set(0, g1s1));
1846
1847 {
1848 Persistent<Value> g1_objects[] = { g1s1, g1s2 };
1849 Persistent<Value> g1_children[] = { g1c1 };
1850 Persistent<Value> g2_objects[] = { g2s1, g2s2 };
1851 Persistent<Value> g2_children[] = { g2c1 };
1852 V8::AddObjectGroup(g1_objects, 2);
1853 V8::AddImplicitReferences(g1s1, g1_children, 1);
1854 V8::AddObjectGroup(g2_objects, 2);
1855 V8::AddImplicitReferences(g2s2, g2_children, 1);
1856 }
1857 // Do a full GC
1858 HEAP->CollectGarbage(i::OLD_POINTER_SPACE);
1859
1860 // All object should be alive.
1861 CHECK_EQ(0, NumberOfWeakCalls);
1862
1863 // Weaken the root.
1864 root.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
1865 // But make children strong roots---all the objects (except for children)
1866 // should be collectable now.
1867 g1c1.ClearWeak();
1868 g2c1.ClearWeak();
1869
1870 // Groups are deleted, rebuild groups.
1871 {
1872 Persistent<Value> g1_objects[] = { g1s1, g1s2 };
1873 Persistent<Value> g1_children[] = { g1c1 };
1874 Persistent<Value> g2_objects[] = { g2s1, g2s2 };
1875 Persistent<Value> g2_children[] = { g2c1 };
1876 V8::AddObjectGroup(g1_objects, 2);
1877 V8::AddImplicitReferences(g1s1, g1_children, 1);
1878 V8::AddObjectGroup(g2_objects, 2);
1879 V8::AddImplicitReferences(g2s2, g2_children, 1);
1880 }
1881
1882 HEAP->CollectGarbage(i::OLD_POINTER_SPACE);
1883
1884 // All objects should be gone. 5 global handles in total.
1885 CHECK_EQ(5, NumberOfWeakCalls);
1886
1887 // And now make children weak again and collect them.
1888 g1c1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
1889 g2c1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
1890
1891 HEAP->CollectGarbage(i::OLD_POINTER_SPACE);
1892 CHECK_EQ(7, NumberOfWeakCalls);
1893}
1894
1895
1896THREADED_TEST(ApiObjectGroupsCycle) {
1897 HandleScope scope;
1898 LocalContext env;
1899
1900 NumberOfWeakCalls = 0;
1901
1902 Persistent<Object> g1s1;
1903 Persistent<Object> g1s2;
1904 Persistent<Object> g2s1;
1905 Persistent<Object> g2s2;
1906 Persistent<Object> g3s1;
1907 Persistent<Object> g3s2;
1908
1909 {
1910 HandleScope scope;
1911 g1s1 = Persistent<Object>::New(Object::New());
1912 g1s2 = Persistent<Object>::New(Object::New());
1913 g1s1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
1914 g1s2.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
1915
1916 g2s1 = Persistent<Object>::New(Object::New());
1917 g2s2 = Persistent<Object>::New(Object::New());
1918 g2s1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
1919 g2s2.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
1920
1921 g3s1 = Persistent<Object>::New(Object::New());
1922 g3s2 = Persistent<Object>::New(Object::New());
1923 g3s1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
1924 g3s2.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
1925 }
1926
1927 Persistent<Object> root = Persistent<Object>::New(g1s1); // make a root.
1928
1929 // Connect groups. We're building the following cycle:
1930 // G1: { g1s1, g2s1 }, g1s1 implicitly references g2s1, ditto for other
1931 // groups.
1932 {
1933 Persistent<Value> g1_objects[] = { g1s1, g1s2 };
1934 Persistent<Value> g1_children[] = { g2s1 };
1935 Persistent<Value> g2_objects[] = { g2s1, g2s2 };
1936 Persistent<Value> g2_children[] = { g3s1 };
1937 Persistent<Value> g3_objects[] = { g3s1, g3s2 };
1938 Persistent<Value> g3_children[] = { g1s1 };
1939 V8::AddObjectGroup(g1_objects, 2);
1940 V8::AddImplicitReferences(g1s1, g1_children, 1);
1941 V8::AddObjectGroup(g2_objects, 2);
1942 V8::AddImplicitReferences(g2s1, g2_children, 1);
1943 V8::AddObjectGroup(g3_objects, 2);
1944 V8::AddImplicitReferences(g3s1, g3_children, 1);
1945 }
1946 // Do a full GC
1947 HEAP->CollectGarbage(i::OLD_POINTER_SPACE);
1948
1949 // All object should be alive.
1950 CHECK_EQ(0, NumberOfWeakCalls);
1951
1952 // Weaken the root.
1953 root.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
1954
1955 // Groups are deleted, rebuild groups.
1956 {
1957 Persistent<Value> g1_objects[] = { g1s1, g1s2 };
1958 Persistent<Value> g1_children[] = { g2s1 };
1959 Persistent<Value> g2_objects[] = { g2s1, g2s2 };
1960 Persistent<Value> g2_children[] = { g3s1 };
1961 Persistent<Value> g3_objects[] = { g3s1, g3s2 };
1962 Persistent<Value> g3_children[] = { g1s1 };
1963 V8::AddObjectGroup(g1_objects, 2);
1964 V8::AddImplicitReferences(g1s1, g1_children, 1);
1965 V8::AddObjectGroup(g2_objects, 2);
1966 V8::AddImplicitReferences(g2s1, g2_children, 1);
1967 V8::AddObjectGroup(g3_objects, 2);
1968 V8::AddImplicitReferences(g3s1, g3_children, 1);
1969 }
1970
1971 HEAP->CollectGarbage(i::OLD_POINTER_SPACE);
1972
1973 // All objects should be gone. 7 global handles in total.
1974 CHECK_EQ(7, NumberOfWeakCalls);
1975}
1976
1977
Steve Blocka7e24c12009-10-30 11:49:00 +00001978THREADED_TEST(ScriptException) {
1979 v8::HandleScope scope;
1980 LocalContext env;
1981 Local<Script> script = Script::Compile(v8_str("throw 'panama!';"));
1982 v8::TryCatch try_catch;
1983 Local<Value> result = script->Run();
1984 CHECK(result.IsEmpty());
1985 CHECK(try_catch.HasCaught());
1986 String::AsciiValue exception_value(try_catch.Exception());
1987 CHECK_EQ(*exception_value, "panama!");
1988}
1989
1990
1991bool message_received;
1992
1993
1994static void check_message(v8::Handle<v8::Message> message,
1995 v8::Handle<Value> data) {
1996 CHECK_EQ(5.76, data->NumberValue());
1997 CHECK_EQ(6.75, message->GetScriptResourceName()->NumberValue());
1998 CHECK_EQ(7.56, message->GetScriptData()->NumberValue());
1999 message_received = true;
2000}
2001
2002
2003THREADED_TEST(MessageHandlerData) {
2004 message_received = false;
2005 v8::HandleScope scope;
2006 CHECK(!message_received);
2007 v8::V8::AddMessageListener(check_message, v8_num(5.76));
2008 LocalContext context;
2009 v8::ScriptOrigin origin =
2010 v8::ScriptOrigin(v8_str("6.75"));
2011 v8::Handle<v8::Script> script = Script::Compile(v8_str("throw 'error'"),
2012 &origin);
2013 script->SetData(v8_str("7.56"));
2014 script->Run();
2015 CHECK(message_received);
2016 // clear out the message listener
2017 v8::V8::RemoveMessageListeners(check_message);
2018}
2019
2020
2021THREADED_TEST(GetSetProperty) {
2022 v8::HandleScope scope;
2023 LocalContext context;
2024 context->Global()->Set(v8_str("foo"), v8_num(14));
2025 context->Global()->Set(v8_str("12"), v8_num(92));
2026 context->Global()->Set(v8::Integer::New(16), v8_num(32));
2027 context->Global()->Set(v8_num(13), v8_num(56));
2028 Local<Value> foo = Script::Compile(v8_str("this.foo"))->Run();
2029 CHECK_EQ(14, foo->Int32Value());
2030 Local<Value> twelve = Script::Compile(v8_str("this[12]"))->Run();
2031 CHECK_EQ(92, twelve->Int32Value());
2032 Local<Value> sixteen = Script::Compile(v8_str("this[16]"))->Run();
2033 CHECK_EQ(32, sixteen->Int32Value());
2034 Local<Value> thirteen = Script::Compile(v8_str("this[13]"))->Run();
2035 CHECK_EQ(56, thirteen->Int32Value());
2036 CHECK_EQ(92, context->Global()->Get(v8::Integer::New(12))->Int32Value());
2037 CHECK_EQ(92, context->Global()->Get(v8_str("12"))->Int32Value());
2038 CHECK_EQ(92, context->Global()->Get(v8_num(12))->Int32Value());
2039 CHECK_EQ(32, context->Global()->Get(v8::Integer::New(16))->Int32Value());
2040 CHECK_EQ(32, context->Global()->Get(v8_str("16"))->Int32Value());
2041 CHECK_EQ(32, context->Global()->Get(v8_num(16))->Int32Value());
2042 CHECK_EQ(56, context->Global()->Get(v8::Integer::New(13))->Int32Value());
2043 CHECK_EQ(56, context->Global()->Get(v8_str("13"))->Int32Value());
2044 CHECK_EQ(56, context->Global()->Get(v8_num(13))->Int32Value());
2045}
2046
2047
2048THREADED_TEST(PropertyAttributes) {
2049 v8::HandleScope scope;
2050 LocalContext context;
2051 // read-only
2052 Local<String> prop = v8_str("read_only");
2053 context->Global()->Set(prop, v8_num(7), v8::ReadOnly);
2054 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value());
2055 Script::Compile(v8_str("read_only = 9"))->Run();
2056 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value());
2057 context->Global()->Set(prop, v8_num(10));
2058 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value());
2059 // dont-delete
2060 prop = v8_str("dont_delete");
2061 context->Global()->Set(prop, v8_num(13), v8::DontDelete);
2062 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value());
2063 Script::Compile(v8_str("delete dont_delete"))->Run();
2064 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value());
2065}
2066
2067
2068THREADED_TEST(Array) {
2069 v8::HandleScope scope;
2070 LocalContext context;
2071 Local<v8::Array> array = v8::Array::New();
2072 CHECK_EQ(0, array->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01002073 CHECK(array->Get(0)->IsUndefined());
Steve Blocka7e24c12009-10-30 11:49:00 +00002074 CHECK(!array->Has(0));
Steve Block6ded16b2010-05-10 14:33:55 +01002075 CHECK(array->Get(100)->IsUndefined());
Steve Blocka7e24c12009-10-30 11:49:00 +00002076 CHECK(!array->Has(100));
Steve Block6ded16b2010-05-10 14:33:55 +01002077 array->Set(2, v8_num(7));
Steve Blocka7e24c12009-10-30 11:49:00 +00002078 CHECK_EQ(3, array->Length());
2079 CHECK(!array->Has(0));
2080 CHECK(!array->Has(1));
2081 CHECK(array->Has(2));
Steve Block6ded16b2010-05-10 14:33:55 +01002082 CHECK_EQ(7, array->Get(2)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00002083 Local<Value> obj = Script::Compile(v8_str("[1, 2, 3]"))->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01002084 Local<v8::Array> arr = obj.As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00002085 CHECK_EQ(3, arr->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01002086 CHECK_EQ(1, arr->Get(0)->Int32Value());
2087 CHECK_EQ(2, arr->Get(1)->Int32Value());
2088 CHECK_EQ(3, arr->Get(2)->Int32Value());
Steve Block44f0eee2011-05-26 01:26:41 +01002089 array = v8::Array::New(27);
2090 CHECK_EQ(27, array->Length());
2091 array = v8::Array::New(-27);
2092 CHECK_EQ(0, array->Length());
Steve Blocka7e24c12009-10-30 11:49:00 +00002093}
2094
2095
2096v8::Handle<Value> HandleF(const v8::Arguments& args) {
2097 v8::HandleScope scope;
2098 ApiTestFuzzer::Fuzz();
2099 Local<v8::Array> result = v8::Array::New(args.Length());
2100 for (int i = 0; i < args.Length(); i++)
Steve Block6ded16b2010-05-10 14:33:55 +01002101 result->Set(i, args[i]);
Steve Blocka7e24c12009-10-30 11:49:00 +00002102 return scope.Close(result);
2103}
2104
2105
2106THREADED_TEST(Vector) {
2107 v8::HandleScope scope;
2108 Local<ObjectTemplate> global = ObjectTemplate::New();
2109 global->Set(v8_str("f"), v8::FunctionTemplate::New(HandleF));
2110 LocalContext context(0, global);
2111
2112 const char* fun = "f()";
Steve Block6ded16b2010-05-10 14:33:55 +01002113 Local<v8::Array> a0 = CompileRun(fun).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00002114 CHECK_EQ(0, a0->Length());
2115
2116 const char* fun2 = "f(11)";
Steve Block6ded16b2010-05-10 14:33:55 +01002117 Local<v8::Array> a1 = CompileRun(fun2).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00002118 CHECK_EQ(1, a1->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01002119 CHECK_EQ(11, a1->Get(0)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00002120
2121 const char* fun3 = "f(12, 13)";
Steve Block6ded16b2010-05-10 14:33:55 +01002122 Local<v8::Array> a2 = CompileRun(fun3).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00002123 CHECK_EQ(2, a2->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01002124 CHECK_EQ(12, a2->Get(0)->Int32Value());
2125 CHECK_EQ(13, a2->Get(1)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00002126
2127 const char* fun4 = "f(14, 15, 16)";
Steve Block6ded16b2010-05-10 14:33:55 +01002128 Local<v8::Array> a3 = CompileRun(fun4).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00002129 CHECK_EQ(3, a3->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01002130 CHECK_EQ(14, a3->Get(0)->Int32Value());
2131 CHECK_EQ(15, a3->Get(1)->Int32Value());
2132 CHECK_EQ(16, a3->Get(2)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00002133
2134 const char* fun5 = "f(17, 18, 19, 20)";
Steve Block6ded16b2010-05-10 14:33:55 +01002135 Local<v8::Array> a4 = CompileRun(fun5).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00002136 CHECK_EQ(4, a4->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01002137 CHECK_EQ(17, a4->Get(0)->Int32Value());
2138 CHECK_EQ(18, a4->Get(1)->Int32Value());
2139 CHECK_EQ(19, a4->Get(2)->Int32Value());
2140 CHECK_EQ(20, a4->Get(3)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00002141}
2142
2143
2144THREADED_TEST(FunctionCall) {
2145 v8::HandleScope scope;
2146 LocalContext context;
2147 CompileRun(
2148 "function Foo() {"
2149 " var result = [];"
2150 " for (var i = 0; i < arguments.length; i++) {"
2151 " result.push(arguments[i]);"
2152 " }"
2153 " return result;"
2154 "}");
2155 Local<Function> Foo =
2156 Local<Function>::Cast(context->Global()->Get(v8_str("Foo")));
2157
2158 v8::Handle<Value>* args0 = NULL;
2159 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->Call(Foo, 0, args0));
2160 CHECK_EQ(0, a0->Length());
2161
2162 v8::Handle<Value> args1[] = { v8_num(1.1) };
2163 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->Call(Foo, 1, args1));
2164 CHECK_EQ(1, a1->Length());
2165 CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue());
2166
2167 v8::Handle<Value> args2[] = { v8_num(2.2),
2168 v8_num(3.3) };
2169 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->Call(Foo, 2, args2));
2170 CHECK_EQ(2, a2->Length());
2171 CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue());
2172 CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue());
2173
2174 v8::Handle<Value> args3[] = { v8_num(4.4),
2175 v8_num(5.5),
2176 v8_num(6.6) };
2177 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->Call(Foo, 3, args3));
2178 CHECK_EQ(3, a3->Length());
2179 CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue());
2180 CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue());
2181 CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue());
2182
2183 v8::Handle<Value> args4[] = { v8_num(7.7),
2184 v8_num(8.8),
2185 v8_num(9.9),
2186 v8_num(10.11) };
2187 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->Call(Foo, 4, args4));
2188 CHECK_EQ(4, a4->Length());
2189 CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue());
2190 CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue());
2191 CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue());
2192 CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue());
2193}
2194
2195
2196static const char* js_code_causing_out_of_memory =
2197 "var a = new Array(); while(true) a.push(a);";
2198
2199
2200// These tests run for a long time and prevent us from running tests
2201// that come after them so they cannot run in parallel.
2202TEST(OutOfMemory) {
2203 // It's not possible to read a snapshot into a heap with different dimensions.
Steve Block8defd9f2010-07-08 12:39:36 +01002204 if (i::Snapshot::IsEnabled()) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00002205 // Set heap limits.
2206 static const int K = 1024;
2207 v8::ResourceConstraints constraints;
2208 constraints.set_max_young_space_size(256 * K);
2209 constraints.set_max_old_space_size(4 * K * K);
2210 v8::SetResourceConstraints(&constraints);
2211
2212 // Execute a script that causes out of memory.
2213 v8::HandleScope scope;
2214 LocalContext context;
2215 v8::V8::IgnoreOutOfMemoryException();
2216 Local<Script> script =
2217 Script::Compile(String::New(js_code_causing_out_of_memory));
2218 Local<Value> result = script->Run();
2219
2220 // Check for out of memory state.
2221 CHECK(result.IsEmpty());
2222 CHECK(context->HasOutOfMemoryException());
2223}
2224
2225
2226v8::Handle<Value> ProvokeOutOfMemory(const v8::Arguments& args) {
2227 ApiTestFuzzer::Fuzz();
2228
2229 v8::HandleScope scope;
2230 LocalContext context;
2231 Local<Script> script =
2232 Script::Compile(String::New(js_code_causing_out_of_memory));
2233 Local<Value> result = script->Run();
2234
2235 // Check for out of memory state.
2236 CHECK(result.IsEmpty());
2237 CHECK(context->HasOutOfMemoryException());
2238
2239 return result;
2240}
2241
2242
2243TEST(OutOfMemoryNested) {
2244 // It's not possible to read a snapshot into a heap with different dimensions.
Steve Block8defd9f2010-07-08 12:39:36 +01002245 if (i::Snapshot::IsEnabled()) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00002246 // Set heap limits.
2247 static const int K = 1024;
2248 v8::ResourceConstraints constraints;
2249 constraints.set_max_young_space_size(256 * K);
2250 constraints.set_max_old_space_size(4 * K * K);
2251 v8::SetResourceConstraints(&constraints);
2252
2253 v8::HandleScope scope;
2254 Local<ObjectTemplate> templ = ObjectTemplate::New();
2255 templ->Set(v8_str("ProvokeOutOfMemory"),
2256 v8::FunctionTemplate::New(ProvokeOutOfMemory));
2257 LocalContext context(0, templ);
2258 v8::V8::IgnoreOutOfMemoryException();
2259 Local<Value> result = CompileRun(
2260 "var thrown = false;"
2261 "try {"
2262 " ProvokeOutOfMemory();"
2263 "} catch (e) {"
2264 " thrown = true;"
2265 "}");
2266 // Check for out of memory state.
2267 CHECK(result.IsEmpty());
2268 CHECK(context->HasOutOfMemoryException());
2269}
2270
2271
2272TEST(HugeConsStringOutOfMemory) {
2273 // It's not possible to read a snapshot into a heap with different dimensions.
Steve Block8defd9f2010-07-08 12:39:36 +01002274 if (i::Snapshot::IsEnabled()) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00002275 // Set heap limits.
2276 static const int K = 1024;
2277 v8::ResourceConstraints constraints;
2278 constraints.set_max_young_space_size(256 * K);
2279 constraints.set_max_old_space_size(2 * K * K);
2280 v8::SetResourceConstraints(&constraints);
2281
2282 // Execute a script that causes out of memory.
2283 v8::V8::IgnoreOutOfMemoryException();
2284
Steve Block44f0eee2011-05-26 01:26:41 +01002285 v8::HandleScope scope;
2286 LocalContext context;
2287
Steve Blocka7e24c12009-10-30 11:49:00 +00002288 // Build huge string. This should fail with out of memory exception.
2289 Local<Value> result = CompileRun(
2290 "var str = Array.prototype.join.call({length: 513}, \"A\").toUpperCase();"
Steve Block3ce2e202009-11-05 08:53:23 +00002291 "for (var i = 0; i < 22; i++) { str = str + str; }");
Steve Blocka7e24c12009-10-30 11:49:00 +00002292
2293 // Check for out of memory state.
2294 CHECK(result.IsEmpty());
2295 CHECK(context->HasOutOfMemoryException());
2296}
2297
2298
2299THREADED_TEST(ConstructCall) {
2300 v8::HandleScope scope;
2301 LocalContext context;
2302 CompileRun(
2303 "function Foo() {"
2304 " var result = [];"
2305 " for (var i = 0; i < arguments.length; i++) {"
2306 " result.push(arguments[i]);"
2307 " }"
2308 " return result;"
2309 "}");
2310 Local<Function> Foo =
2311 Local<Function>::Cast(context->Global()->Get(v8_str("Foo")));
2312
2313 v8::Handle<Value>* args0 = NULL;
2314 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->NewInstance(0, args0));
2315 CHECK_EQ(0, a0->Length());
2316
2317 v8::Handle<Value> args1[] = { v8_num(1.1) };
2318 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->NewInstance(1, args1));
2319 CHECK_EQ(1, a1->Length());
2320 CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue());
2321
2322 v8::Handle<Value> args2[] = { v8_num(2.2),
2323 v8_num(3.3) };
2324 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->NewInstance(2, args2));
2325 CHECK_EQ(2, a2->Length());
2326 CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue());
2327 CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue());
2328
2329 v8::Handle<Value> args3[] = { v8_num(4.4),
2330 v8_num(5.5),
2331 v8_num(6.6) };
2332 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->NewInstance(3, args3));
2333 CHECK_EQ(3, a3->Length());
2334 CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue());
2335 CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue());
2336 CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue());
2337
2338 v8::Handle<Value> args4[] = { v8_num(7.7),
2339 v8_num(8.8),
2340 v8_num(9.9),
2341 v8_num(10.11) };
2342 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->NewInstance(4, args4));
2343 CHECK_EQ(4, a4->Length());
2344 CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue());
2345 CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue());
2346 CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue());
2347 CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue());
2348}
2349
2350
2351static void CheckUncle(v8::TryCatch* try_catch) {
2352 CHECK(try_catch->HasCaught());
2353 String::AsciiValue str_value(try_catch->Exception());
2354 CHECK_EQ(*str_value, "uncle?");
2355 try_catch->Reset();
2356}
2357
2358
Steve Block6ded16b2010-05-10 14:33:55 +01002359THREADED_TEST(ConversionNumber) {
2360 v8::HandleScope scope;
2361 LocalContext env;
2362 // Very large number.
2363 CompileRun("var obj = Math.pow(2,32) * 1237;");
2364 Local<Value> obj = env->Global()->Get(v8_str("obj"));
2365 CHECK_EQ(5312874545152.0, obj->ToNumber()->Value());
2366 CHECK_EQ(0, obj->ToInt32()->Value());
2367 CHECK(0u == obj->ToUint32()->Value()); // NOLINT - no CHECK_EQ for unsigned.
2368 // Large number.
2369 CompileRun("var obj = -1234567890123;");
2370 obj = env->Global()->Get(v8_str("obj"));
2371 CHECK_EQ(-1234567890123.0, obj->ToNumber()->Value());
2372 CHECK_EQ(-1912276171, obj->ToInt32()->Value());
2373 CHECK(2382691125u == obj->ToUint32()->Value()); // NOLINT
2374 // Small positive integer.
2375 CompileRun("var obj = 42;");
2376 obj = env->Global()->Get(v8_str("obj"));
2377 CHECK_EQ(42.0, obj->ToNumber()->Value());
2378 CHECK_EQ(42, obj->ToInt32()->Value());
2379 CHECK(42u == obj->ToUint32()->Value()); // NOLINT
2380 // Negative integer.
2381 CompileRun("var obj = -37;");
2382 obj = env->Global()->Get(v8_str("obj"));
2383 CHECK_EQ(-37.0, obj->ToNumber()->Value());
2384 CHECK_EQ(-37, obj->ToInt32()->Value());
2385 CHECK(4294967259u == obj->ToUint32()->Value()); // NOLINT
2386 // Positive non-int32 integer.
2387 CompileRun("var obj = 0x81234567;");
2388 obj = env->Global()->Get(v8_str("obj"));
2389 CHECK_EQ(2166572391.0, obj->ToNumber()->Value());
2390 CHECK_EQ(-2128394905, obj->ToInt32()->Value());
2391 CHECK(2166572391u == obj->ToUint32()->Value()); // NOLINT
2392 // Fraction.
2393 CompileRun("var obj = 42.3;");
2394 obj = env->Global()->Get(v8_str("obj"));
2395 CHECK_EQ(42.3, obj->ToNumber()->Value());
2396 CHECK_EQ(42, obj->ToInt32()->Value());
2397 CHECK(42u == obj->ToUint32()->Value()); // NOLINT
2398 // Large negative fraction.
2399 CompileRun("var obj = -5726623061.75;");
2400 obj = env->Global()->Get(v8_str("obj"));
2401 CHECK_EQ(-5726623061.75, obj->ToNumber()->Value());
2402 CHECK_EQ(-1431655765, obj->ToInt32()->Value());
2403 CHECK(2863311531u == obj->ToUint32()->Value()); // NOLINT
2404}
2405
2406
2407THREADED_TEST(isNumberType) {
2408 v8::HandleScope scope;
2409 LocalContext env;
2410 // Very large number.
2411 CompileRun("var obj = Math.pow(2,32) * 1237;");
2412 Local<Value> obj = env->Global()->Get(v8_str("obj"));
2413 CHECK(!obj->IsInt32());
2414 CHECK(!obj->IsUint32());
2415 // Large negative number.
2416 CompileRun("var obj = -1234567890123;");
2417 obj = env->Global()->Get(v8_str("obj"));
2418 CHECK(!obj->IsInt32());
2419 CHECK(!obj->IsUint32());
2420 // Small positive integer.
2421 CompileRun("var obj = 42;");
2422 obj = env->Global()->Get(v8_str("obj"));
2423 CHECK(obj->IsInt32());
2424 CHECK(obj->IsUint32());
2425 // Negative integer.
2426 CompileRun("var obj = -37;");
2427 obj = env->Global()->Get(v8_str("obj"));
2428 CHECK(obj->IsInt32());
2429 CHECK(!obj->IsUint32());
2430 // Positive non-int32 integer.
2431 CompileRun("var obj = 0x81234567;");
2432 obj = env->Global()->Get(v8_str("obj"));
2433 CHECK(!obj->IsInt32());
2434 CHECK(obj->IsUint32());
2435 // Fraction.
2436 CompileRun("var obj = 42.3;");
2437 obj = env->Global()->Get(v8_str("obj"));
2438 CHECK(!obj->IsInt32());
2439 CHECK(!obj->IsUint32());
2440 // Large negative fraction.
2441 CompileRun("var obj = -5726623061.75;");
2442 obj = env->Global()->Get(v8_str("obj"));
2443 CHECK(!obj->IsInt32());
2444 CHECK(!obj->IsUint32());
2445}
2446
2447
Steve Blocka7e24c12009-10-30 11:49:00 +00002448THREADED_TEST(ConversionException) {
2449 v8::HandleScope scope;
2450 LocalContext env;
2451 CompileRun(
2452 "function TestClass() { };"
2453 "TestClass.prototype.toString = function () { throw 'uncle?'; };"
2454 "var obj = new TestClass();");
2455 Local<Value> obj = env->Global()->Get(v8_str("obj"));
2456
2457 v8::TryCatch try_catch;
2458
2459 Local<Value> to_string_result = obj->ToString();
2460 CHECK(to_string_result.IsEmpty());
2461 CheckUncle(&try_catch);
2462
2463 Local<Value> to_number_result = obj->ToNumber();
2464 CHECK(to_number_result.IsEmpty());
2465 CheckUncle(&try_catch);
2466
2467 Local<Value> to_integer_result = obj->ToInteger();
2468 CHECK(to_integer_result.IsEmpty());
2469 CheckUncle(&try_catch);
2470
2471 Local<Value> to_uint32_result = obj->ToUint32();
2472 CHECK(to_uint32_result.IsEmpty());
2473 CheckUncle(&try_catch);
2474
2475 Local<Value> to_int32_result = obj->ToInt32();
2476 CHECK(to_int32_result.IsEmpty());
2477 CheckUncle(&try_catch);
2478
2479 Local<Value> to_object_result = v8::Undefined()->ToObject();
2480 CHECK(to_object_result.IsEmpty());
2481 CHECK(try_catch.HasCaught());
2482 try_catch.Reset();
2483
2484 int32_t int32_value = obj->Int32Value();
2485 CHECK_EQ(0, int32_value);
2486 CheckUncle(&try_catch);
2487
2488 uint32_t uint32_value = obj->Uint32Value();
2489 CHECK_EQ(0, uint32_value);
2490 CheckUncle(&try_catch);
2491
2492 double number_value = obj->NumberValue();
2493 CHECK_NE(0, IsNaN(number_value));
2494 CheckUncle(&try_catch);
2495
2496 int64_t integer_value = obj->IntegerValue();
2497 CHECK_EQ(0.0, static_cast<double>(integer_value));
2498 CheckUncle(&try_catch);
2499}
2500
2501
2502v8::Handle<Value> ThrowFromC(const v8::Arguments& args) {
2503 ApiTestFuzzer::Fuzz();
2504 return v8::ThrowException(v8_str("konto"));
2505}
2506
2507
2508v8::Handle<Value> CCatcher(const v8::Arguments& args) {
2509 if (args.Length() < 1) return v8::Boolean::New(false);
2510 v8::HandleScope scope;
2511 v8::TryCatch try_catch;
2512 Local<Value> result = v8::Script::Compile(args[0]->ToString())->Run();
2513 CHECK(!try_catch.HasCaught() || result.IsEmpty());
2514 return v8::Boolean::New(try_catch.HasCaught());
2515}
2516
2517
2518THREADED_TEST(APICatch) {
2519 v8::HandleScope scope;
2520 Local<ObjectTemplate> templ = ObjectTemplate::New();
2521 templ->Set(v8_str("ThrowFromC"),
2522 v8::FunctionTemplate::New(ThrowFromC));
2523 LocalContext context(0, templ);
2524 CompileRun(
2525 "var thrown = false;"
2526 "try {"
2527 " ThrowFromC();"
2528 "} catch (e) {"
2529 " thrown = true;"
2530 "}");
2531 Local<Value> thrown = context->Global()->Get(v8_str("thrown"));
2532 CHECK(thrown->BooleanValue());
2533}
2534
2535
2536THREADED_TEST(APIThrowTryCatch) {
2537 v8::HandleScope scope;
2538 Local<ObjectTemplate> templ = ObjectTemplate::New();
2539 templ->Set(v8_str("ThrowFromC"),
2540 v8::FunctionTemplate::New(ThrowFromC));
2541 LocalContext context(0, templ);
2542 v8::TryCatch try_catch;
2543 CompileRun("ThrowFromC();");
2544 CHECK(try_catch.HasCaught());
2545}
2546
2547
2548// Test that a try-finally block doesn't shadow a try-catch block
2549// when setting up an external handler.
2550//
2551// BUG(271): Some of the exception propagation does not work on the
2552// ARM simulator because the simulator separates the C++ stack and the
2553// JS stack. This test therefore fails on the simulator. The test is
2554// not threaded to allow the threading tests to run on the simulator.
2555TEST(TryCatchInTryFinally) {
2556 v8::HandleScope scope;
2557 Local<ObjectTemplate> templ = ObjectTemplate::New();
2558 templ->Set(v8_str("CCatcher"),
2559 v8::FunctionTemplate::New(CCatcher));
2560 LocalContext context(0, templ);
2561 Local<Value> result = CompileRun("try {"
2562 " try {"
2563 " CCatcher('throw 7;');"
2564 " } finally {"
2565 " }"
2566 "} catch (e) {"
2567 "}");
2568 CHECK(result->IsTrue());
2569}
2570
2571
Ben Murdochb8e0da22011-05-16 14:20:40 +01002572static void check_reference_error_message(
2573 v8::Handle<v8::Message> message,
2574 v8::Handle<v8::Value> data) {
2575 const char* reference_error = "Uncaught ReferenceError: asdf is not defined";
2576 CHECK(message->Get()->Equals(v8_str(reference_error)));
2577}
2578
2579
Steve Block1e0659c2011-05-24 12:43:12 +01002580static v8::Handle<Value> Fail(const v8::Arguments& args) {
2581 ApiTestFuzzer::Fuzz();
2582 CHECK(false);
2583 return v8::Undefined();
2584}
2585
2586
2587// Test that overwritten methods are not invoked on uncaught exception
2588// formatting. However, they are invoked when performing normal error
2589// string conversions.
Ben Murdochb8e0da22011-05-16 14:20:40 +01002590TEST(APIThrowMessageOverwrittenToString) {
2591 v8::HandleScope scope;
2592 v8::V8::AddMessageListener(check_reference_error_message);
Steve Block1e0659c2011-05-24 12:43:12 +01002593 Local<ObjectTemplate> templ = ObjectTemplate::New();
2594 templ->Set(v8_str("fail"), v8::FunctionTemplate::New(Fail));
2595 LocalContext context(NULL, templ);
2596 CompileRun("asdf;");
2597 CompileRun("var limit = {};"
2598 "limit.valueOf = fail;"
2599 "Error.stackTraceLimit = limit;");
2600 CompileRun("asdf");
2601 CompileRun("Array.prototype.pop = fail;");
2602 CompileRun("Object.prototype.hasOwnProperty = fail;");
2603 CompileRun("Object.prototype.toString = function f() { return 'Yikes'; }");
2604 CompileRun("Number.prototype.toString = function f() { return 'Yikes'; }");
2605 CompileRun("String.prototype.toString = function f() { return 'Yikes'; }");
Ben Murdochb8e0da22011-05-16 14:20:40 +01002606 CompileRun("ReferenceError.prototype.toString ="
2607 " function() { return 'Whoops' }");
2608 CompileRun("asdf;");
Steve Block1e0659c2011-05-24 12:43:12 +01002609 CompileRun("ReferenceError.prototype.constructor.name = void 0;");
2610 CompileRun("asdf;");
2611 CompileRun("ReferenceError.prototype.constructor = void 0;");
2612 CompileRun("asdf;");
2613 CompileRun("ReferenceError.prototype.__proto__ = new Object();");
2614 CompileRun("asdf;");
2615 CompileRun("ReferenceError.prototype = new Object();");
2616 CompileRun("asdf;");
Ben Murdochb8e0da22011-05-16 14:20:40 +01002617 v8::Handle<Value> string = CompileRun("try { asdf; } catch(e) { e + ''; }");
2618 CHECK(string->Equals(v8_str("Whoops")));
Steve Block1e0659c2011-05-24 12:43:12 +01002619 CompileRun("ReferenceError.prototype.constructor = new Object();"
2620 "ReferenceError.prototype.constructor.name = 1;"
2621 "Number.prototype.toString = function() { return 'Whoops'; };"
2622 "ReferenceError.prototype.toString = Object.prototype.toString;");
2623 CompileRun("asdf;");
Ben Murdochb8e0da22011-05-16 14:20:40 +01002624 v8::V8::RemoveMessageListeners(check_message);
2625}
2626
2627
Steve Blocka7e24c12009-10-30 11:49:00 +00002628static void receive_message(v8::Handle<v8::Message> message,
2629 v8::Handle<v8::Value> data) {
2630 message->Get();
2631 message_received = true;
2632}
2633
2634
2635TEST(APIThrowMessage) {
2636 message_received = false;
2637 v8::HandleScope scope;
2638 v8::V8::AddMessageListener(receive_message);
2639 Local<ObjectTemplate> templ = ObjectTemplate::New();
2640 templ->Set(v8_str("ThrowFromC"),
2641 v8::FunctionTemplate::New(ThrowFromC));
2642 LocalContext context(0, templ);
2643 CompileRun("ThrowFromC();");
2644 CHECK(message_received);
2645 v8::V8::RemoveMessageListeners(check_message);
2646}
2647
2648
2649TEST(APIThrowMessageAndVerboseTryCatch) {
2650 message_received = false;
2651 v8::HandleScope scope;
2652 v8::V8::AddMessageListener(receive_message);
2653 Local<ObjectTemplate> templ = ObjectTemplate::New();
2654 templ->Set(v8_str("ThrowFromC"),
2655 v8::FunctionTemplate::New(ThrowFromC));
2656 LocalContext context(0, templ);
2657 v8::TryCatch try_catch;
2658 try_catch.SetVerbose(true);
2659 Local<Value> result = CompileRun("ThrowFromC();");
2660 CHECK(try_catch.HasCaught());
2661 CHECK(result.IsEmpty());
2662 CHECK(message_received);
2663 v8::V8::RemoveMessageListeners(check_message);
2664}
2665
2666
Ben Murdoch8b112d22011-06-08 16:22:53 +01002667TEST(APIStackOverflowAndVerboseTryCatch) {
2668 message_received = false;
2669 v8::HandleScope scope;
2670 v8::V8::AddMessageListener(receive_message);
2671 LocalContext context;
2672 v8::TryCatch try_catch;
2673 try_catch.SetVerbose(true);
2674 Local<Value> result = CompileRun("function foo() { foo(); } foo();");
2675 CHECK(try_catch.HasCaught());
2676 CHECK(result.IsEmpty());
2677 CHECK(message_received);
2678 v8::V8::RemoveMessageListeners(receive_message);
2679}
2680
2681
Steve Blocka7e24c12009-10-30 11:49:00 +00002682THREADED_TEST(ExternalScriptException) {
2683 v8::HandleScope scope;
2684 Local<ObjectTemplate> templ = ObjectTemplate::New();
2685 templ->Set(v8_str("ThrowFromC"),
2686 v8::FunctionTemplate::New(ThrowFromC));
2687 LocalContext context(0, templ);
2688
2689 v8::TryCatch try_catch;
2690 Local<Script> script
2691 = Script::Compile(v8_str("ThrowFromC(); throw 'panama';"));
2692 Local<Value> result = script->Run();
2693 CHECK(result.IsEmpty());
2694 CHECK(try_catch.HasCaught());
2695 String::AsciiValue exception_value(try_catch.Exception());
2696 CHECK_EQ("konto", *exception_value);
2697}
2698
2699
2700
2701v8::Handle<Value> CThrowCountDown(const v8::Arguments& args) {
2702 ApiTestFuzzer::Fuzz();
2703 CHECK_EQ(4, args.Length());
2704 int count = args[0]->Int32Value();
2705 int cInterval = args[2]->Int32Value();
2706 if (count == 0) {
2707 return v8::ThrowException(v8_str("FromC"));
2708 } else {
2709 Local<v8::Object> global = Context::GetCurrent()->Global();
2710 Local<Value> fun = global->Get(v8_str("JSThrowCountDown"));
2711 v8::Handle<Value> argv[] = { v8_num(count - 1),
2712 args[1],
2713 args[2],
2714 args[3] };
2715 if (count % cInterval == 0) {
2716 v8::TryCatch try_catch;
Steve Block6ded16b2010-05-10 14:33:55 +01002717 Local<Value> result = fun.As<Function>()->Call(global, 4, argv);
Steve Blocka7e24c12009-10-30 11:49:00 +00002718 int expected = args[3]->Int32Value();
2719 if (try_catch.HasCaught()) {
2720 CHECK_EQ(expected, count);
2721 CHECK(result.IsEmpty());
Steve Block44f0eee2011-05-26 01:26:41 +01002722 CHECK(!i::Isolate::Current()->has_scheduled_exception());
Steve Blocka7e24c12009-10-30 11:49:00 +00002723 } else {
2724 CHECK_NE(expected, count);
2725 }
2726 return result;
2727 } else {
Steve Block6ded16b2010-05-10 14:33:55 +01002728 return fun.As<Function>()->Call(global, 4, argv);
Steve Blocka7e24c12009-10-30 11:49:00 +00002729 }
2730 }
2731}
2732
2733
2734v8::Handle<Value> JSCheck(const v8::Arguments& args) {
2735 ApiTestFuzzer::Fuzz();
2736 CHECK_EQ(3, args.Length());
2737 bool equality = args[0]->BooleanValue();
2738 int count = args[1]->Int32Value();
2739 int expected = args[2]->Int32Value();
2740 if (equality) {
2741 CHECK_EQ(count, expected);
2742 } else {
2743 CHECK_NE(count, expected);
2744 }
2745 return v8::Undefined();
2746}
2747
2748
2749THREADED_TEST(EvalInTryFinally) {
2750 v8::HandleScope scope;
2751 LocalContext context;
2752 v8::TryCatch try_catch;
2753 CompileRun("(function() {"
2754 " try {"
2755 " eval('asldkf (*&^&*^');"
2756 " } finally {"
2757 " return;"
2758 " }"
2759 "})()");
2760 CHECK(!try_catch.HasCaught());
2761}
2762
2763
2764// This test works by making a stack of alternating JavaScript and C
2765// activations. These activations set up exception handlers with regular
2766// intervals, one interval for C activations and another for JavaScript
2767// activations. When enough activations have been created an exception is
2768// thrown and we check that the right activation catches the exception and that
2769// no other activations do. The right activation is always the topmost one with
2770// a handler, regardless of whether it is in JavaScript or C.
2771//
2772// The notation used to describe a test case looks like this:
2773//
2774// *JS[4] *C[3] @JS[2] C[1] JS[0]
2775//
2776// Each entry is an activation, either JS or C. The index is the count at that
2777// level. Stars identify activations with exception handlers, the @ identifies
2778// the exception handler that should catch the exception.
2779//
2780// BUG(271): Some of the exception propagation does not work on the
2781// ARM simulator because the simulator separates the C++ stack and the
2782// JS stack. This test therefore fails on the simulator. The test is
2783// not threaded to allow the threading tests to run on the simulator.
2784TEST(ExceptionOrder) {
2785 v8::HandleScope scope;
2786 Local<ObjectTemplate> templ = ObjectTemplate::New();
2787 templ->Set(v8_str("check"), v8::FunctionTemplate::New(JSCheck));
2788 templ->Set(v8_str("CThrowCountDown"),
2789 v8::FunctionTemplate::New(CThrowCountDown));
2790 LocalContext context(0, templ);
2791 CompileRun(
2792 "function JSThrowCountDown(count, jsInterval, cInterval, expected) {"
2793 " if (count == 0) throw 'FromJS';"
2794 " if (count % jsInterval == 0) {"
2795 " try {"
2796 " var value = CThrowCountDown(count - 1,"
2797 " jsInterval,"
2798 " cInterval,"
2799 " expected);"
2800 " check(false, count, expected);"
2801 " return value;"
2802 " } catch (e) {"
2803 " check(true, count, expected);"
2804 " }"
2805 " } else {"
2806 " return CThrowCountDown(count - 1, jsInterval, cInterval, expected);"
2807 " }"
2808 "}");
2809 Local<Function> fun =
2810 Local<Function>::Cast(context->Global()->Get(v8_str("JSThrowCountDown")));
2811
2812 const int argc = 4;
2813 // count jsInterval cInterval expected
2814
2815 // *JS[4] *C[3] @JS[2] C[1] JS[0]
2816 v8::Handle<Value> a0[argc] = { v8_num(4), v8_num(2), v8_num(3), v8_num(2) };
2817 fun->Call(fun, argc, a0);
2818
2819 // JS[5] *C[4] JS[3] @C[2] JS[1] C[0]
2820 v8::Handle<Value> a1[argc] = { v8_num(5), v8_num(6), v8_num(1), v8_num(2) };
2821 fun->Call(fun, argc, a1);
2822
2823 // JS[6] @C[5] JS[4] C[3] JS[2] C[1] JS[0]
2824 v8::Handle<Value> a2[argc] = { v8_num(6), v8_num(7), v8_num(5), v8_num(5) };
2825 fun->Call(fun, argc, a2);
2826
2827 // @JS[6] C[5] JS[4] C[3] JS[2] C[1] JS[0]
2828 v8::Handle<Value> a3[argc] = { v8_num(6), v8_num(6), v8_num(7), v8_num(6) };
2829 fun->Call(fun, argc, a3);
2830
2831 // JS[6] *C[5] @JS[4] C[3] JS[2] C[1] JS[0]
2832 v8::Handle<Value> a4[argc] = { v8_num(6), v8_num(4), v8_num(5), v8_num(4) };
2833 fun->Call(fun, argc, a4);
2834
2835 // JS[6] C[5] *JS[4] @C[3] JS[2] C[1] JS[0]
2836 v8::Handle<Value> a5[argc] = { v8_num(6), v8_num(4), v8_num(3), v8_num(3) };
2837 fun->Call(fun, argc, a5);
2838}
2839
2840
2841v8::Handle<Value> ThrowValue(const v8::Arguments& args) {
2842 ApiTestFuzzer::Fuzz();
2843 CHECK_EQ(1, args.Length());
2844 return v8::ThrowException(args[0]);
2845}
2846
2847
2848THREADED_TEST(ThrowValues) {
2849 v8::HandleScope scope;
2850 Local<ObjectTemplate> templ = ObjectTemplate::New();
2851 templ->Set(v8_str("Throw"), v8::FunctionTemplate::New(ThrowValue));
2852 LocalContext context(0, templ);
2853 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun(
2854 "function Run(obj) {"
2855 " try {"
2856 " Throw(obj);"
2857 " } catch (e) {"
2858 " return e;"
2859 " }"
2860 " return 'no exception';"
2861 "}"
2862 "[Run('str'), Run(1), Run(0), Run(null), Run(void 0)];"));
2863 CHECK_EQ(5, result->Length());
2864 CHECK(result->Get(v8::Integer::New(0))->IsString());
2865 CHECK(result->Get(v8::Integer::New(1))->IsNumber());
2866 CHECK_EQ(1, result->Get(v8::Integer::New(1))->Int32Value());
2867 CHECK(result->Get(v8::Integer::New(2))->IsNumber());
2868 CHECK_EQ(0, result->Get(v8::Integer::New(2))->Int32Value());
2869 CHECK(result->Get(v8::Integer::New(3))->IsNull());
2870 CHECK(result->Get(v8::Integer::New(4))->IsUndefined());
2871}
2872
2873
2874THREADED_TEST(CatchZero) {
2875 v8::HandleScope scope;
2876 LocalContext context;
2877 v8::TryCatch try_catch;
2878 CHECK(!try_catch.HasCaught());
2879 Script::Compile(v8_str("throw 10"))->Run();
2880 CHECK(try_catch.HasCaught());
2881 CHECK_EQ(10, try_catch.Exception()->Int32Value());
2882 try_catch.Reset();
2883 CHECK(!try_catch.HasCaught());
2884 Script::Compile(v8_str("throw 0"))->Run();
2885 CHECK(try_catch.HasCaught());
2886 CHECK_EQ(0, try_catch.Exception()->Int32Value());
2887}
2888
2889
2890THREADED_TEST(CatchExceptionFromWith) {
2891 v8::HandleScope scope;
2892 LocalContext context;
2893 v8::TryCatch try_catch;
2894 CHECK(!try_catch.HasCaught());
2895 Script::Compile(v8_str("var o = {}; with (o) { throw 42; }"))->Run();
2896 CHECK(try_catch.HasCaught());
2897}
2898
2899
Ben Murdoche0cee9b2011-05-25 10:26:03 +01002900THREADED_TEST(TryCatchAndFinallyHidingException) {
2901 v8::HandleScope scope;
2902 LocalContext context;
2903 v8::TryCatch try_catch;
2904 CHECK(!try_catch.HasCaught());
2905 CompileRun("function f(k) { try { this[k]; } finally { return 0; } };");
2906 CompileRun("f({toString: function() { throw 42; }});");
2907 CHECK(!try_catch.HasCaught());
2908}
2909
2910
2911v8::Handle<v8::Value> WithTryCatch(const v8::Arguments& args) {
2912 v8::TryCatch try_catch;
2913 return v8::Undefined();
2914}
2915
2916
2917THREADED_TEST(TryCatchAndFinally) {
2918 v8::HandleScope scope;
2919 LocalContext context;
2920 context->Global()->Set(
2921 v8_str("native_with_try_catch"),
2922 v8::FunctionTemplate::New(WithTryCatch)->GetFunction());
2923 v8::TryCatch try_catch;
2924 CHECK(!try_catch.HasCaught());
2925 CompileRun(
2926 "try {\n"
2927 " throw new Error('a');\n"
2928 "} finally {\n"
2929 " native_with_try_catch();\n"
2930 "}\n");
2931 CHECK(try_catch.HasCaught());
2932}
2933
2934
Steve Blocka7e24c12009-10-30 11:49:00 +00002935THREADED_TEST(Equality) {
2936 v8::HandleScope scope;
2937 LocalContext context;
2938 // Check that equality works at all before relying on CHECK_EQ
2939 CHECK(v8_str("a")->Equals(v8_str("a")));
2940 CHECK(!v8_str("a")->Equals(v8_str("b")));
2941
2942 CHECK_EQ(v8_str("a"), v8_str("a"));
2943 CHECK_NE(v8_str("a"), v8_str("b"));
2944 CHECK_EQ(v8_num(1), v8_num(1));
2945 CHECK_EQ(v8_num(1.00), v8_num(1));
2946 CHECK_NE(v8_num(1), v8_num(2));
2947
2948 // Assume String is not symbol.
2949 CHECK(v8_str("a")->StrictEquals(v8_str("a")));
2950 CHECK(!v8_str("a")->StrictEquals(v8_str("b")));
2951 CHECK(!v8_str("5")->StrictEquals(v8_num(5)));
2952 CHECK(v8_num(1)->StrictEquals(v8_num(1)));
2953 CHECK(!v8_num(1)->StrictEquals(v8_num(2)));
2954 CHECK(v8_num(0)->StrictEquals(v8_num(-0)));
2955 Local<Value> not_a_number = v8_num(i::OS::nan_value());
2956 CHECK(!not_a_number->StrictEquals(not_a_number));
2957 CHECK(v8::False()->StrictEquals(v8::False()));
2958 CHECK(!v8::False()->StrictEquals(v8::Undefined()));
2959
2960 v8::Handle<v8::Object> obj = v8::Object::New();
2961 v8::Persistent<v8::Object> alias = v8::Persistent<v8::Object>::New(obj);
2962 CHECK(alias->StrictEquals(obj));
2963 alias.Dispose();
2964}
2965
2966
2967THREADED_TEST(MultiRun) {
2968 v8::HandleScope scope;
2969 LocalContext context;
2970 Local<Script> script = Script::Compile(v8_str("x"));
2971 for (int i = 0; i < 10; i++)
2972 script->Run();
2973}
2974
2975
2976static v8::Handle<Value> GetXValue(Local<String> name,
2977 const AccessorInfo& info) {
2978 ApiTestFuzzer::Fuzz();
2979 CHECK_EQ(info.Data(), v8_str("donut"));
2980 CHECK_EQ(name, v8_str("x"));
2981 return name;
2982}
2983
2984
2985THREADED_TEST(SimplePropertyRead) {
2986 v8::HandleScope scope;
2987 Local<ObjectTemplate> templ = ObjectTemplate::New();
2988 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"));
2989 LocalContext context;
2990 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2991 Local<Script> script = Script::Compile(v8_str("obj.x"));
2992 for (int i = 0; i < 10; i++) {
2993 Local<Value> result = script->Run();
2994 CHECK_EQ(result, v8_str("x"));
2995 }
2996}
2997
Andrei Popescu31002712010-02-23 13:46:05 +00002998THREADED_TEST(DefinePropertyOnAPIAccessor) {
2999 v8::HandleScope scope;
3000 Local<ObjectTemplate> templ = ObjectTemplate::New();
3001 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"));
3002 LocalContext context;
3003 context->Global()->Set(v8_str("obj"), templ->NewInstance());
3004
3005 // Uses getOwnPropertyDescriptor to check the configurable status
3006 Local<Script> script_desc
Leon Clarkef7060e22010-06-03 12:02:55 +01003007 = Script::Compile(v8_str("var prop = Object.getOwnPropertyDescriptor( "
Andrei Popescu31002712010-02-23 13:46:05 +00003008 "obj, 'x');"
3009 "prop.configurable;"));
3010 Local<Value> result = script_desc->Run();
3011 CHECK_EQ(result->BooleanValue(), true);
3012
3013 // Redefine get - but still configurable
3014 Local<Script> script_define
3015 = Script::Compile(v8_str("var desc = { get: function(){return 42; },"
3016 " configurable: true };"
3017 "Object.defineProperty(obj, 'x', desc);"
3018 "obj.x"));
3019 result = script_define->Run();
3020 CHECK_EQ(result, v8_num(42));
3021
3022 // Check that the accessor is still configurable
3023 result = script_desc->Run();
3024 CHECK_EQ(result->BooleanValue(), true);
3025
3026 // Redefine to a non-configurable
3027 script_define
3028 = Script::Compile(v8_str("var desc = { get: function(){return 43; },"
3029 " configurable: false };"
3030 "Object.defineProperty(obj, 'x', desc);"
3031 "obj.x"));
3032 result = script_define->Run();
3033 CHECK_EQ(result, v8_num(43));
3034 result = script_desc->Run();
3035 CHECK_EQ(result->BooleanValue(), false);
3036
3037 // Make sure that it is not possible to redefine again
3038 v8::TryCatch try_catch;
3039 result = script_define->Run();
3040 CHECK(try_catch.HasCaught());
3041 String::AsciiValue exception_value(try_catch.Exception());
3042 CHECK_EQ(*exception_value,
3043 "TypeError: Cannot redefine property: defineProperty");
3044}
3045
3046THREADED_TEST(DefinePropertyOnDefineGetterSetter) {
3047 v8::HandleScope scope;
3048 Local<ObjectTemplate> templ = ObjectTemplate::New();
3049 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"));
3050 LocalContext context;
3051 context->Global()->Set(v8_str("obj"), templ->NewInstance());
3052
3053 Local<Script> script_desc = Script::Compile(v8_str("var prop ="
3054 "Object.getOwnPropertyDescriptor( "
3055 "obj, 'x');"
3056 "prop.configurable;"));
3057 Local<Value> result = script_desc->Run();
3058 CHECK_EQ(result->BooleanValue(), true);
3059
3060 Local<Script> script_define =
3061 Script::Compile(v8_str("var desc = {get: function(){return 42; },"
3062 " configurable: true };"
3063 "Object.defineProperty(obj, 'x', desc);"
3064 "obj.x"));
3065 result = script_define->Run();
3066 CHECK_EQ(result, v8_num(42));
3067
3068
3069 result = script_desc->Run();
3070 CHECK_EQ(result->BooleanValue(), true);
3071
3072
3073 script_define =
3074 Script::Compile(v8_str("var desc = {get: function(){return 43; },"
3075 " configurable: false };"
3076 "Object.defineProperty(obj, 'x', desc);"
3077 "obj.x"));
3078 result = script_define->Run();
3079 CHECK_EQ(result, v8_num(43));
3080 result = script_desc->Run();
3081
3082 CHECK_EQ(result->BooleanValue(), false);
3083
3084 v8::TryCatch try_catch;
3085 result = script_define->Run();
3086 CHECK(try_catch.HasCaught());
3087 String::AsciiValue exception_value(try_catch.Exception());
3088 CHECK_EQ(*exception_value,
3089 "TypeError: Cannot redefine property: defineProperty");
3090}
3091
3092
Leon Clarkef7060e22010-06-03 12:02:55 +01003093static v8::Handle<v8::Object> GetGlobalProperty(LocalContext* context,
3094 char const* name) {
3095 return v8::Handle<v8::Object>::Cast((*context)->Global()->Get(v8_str(name)));
3096}
Andrei Popescu31002712010-02-23 13:46:05 +00003097
3098
Leon Clarkef7060e22010-06-03 12:02:55 +01003099THREADED_TEST(DefineAPIAccessorOnObject) {
3100 v8::HandleScope scope;
3101 Local<ObjectTemplate> templ = ObjectTemplate::New();
3102 LocalContext context;
3103
3104 context->Global()->Set(v8_str("obj1"), templ->NewInstance());
3105 CompileRun("var obj2 = {};");
3106
3107 CHECK(CompileRun("obj1.x")->IsUndefined());
3108 CHECK(CompileRun("obj2.x")->IsUndefined());
3109
3110 CHECK(GetGlobalProperty(&context, "obj1")->
3111 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
3112
3113 ExpectString("obj1.x", "x");
3114 CHECK(CompileRun("obj2.x")->IsUndefined());
3115
3116 CHECK(GetGlobalProperty(&context, "obj2")->
3117 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
3118
3119 ExpectString("obj1.x", "x");
3120 ExpectString("obj2.x", "x");
3121
3122 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
3123 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
3124
3125 CompileRun("Object.defineProperty(obj1, 'x',"
3126 "{ get: function() { return 'y'; }, configurable: true })");
3127
3128 ExpectString("obj1.x", "y");
3129 ExpectString("obj2.x", "x");
3130
3131 CompileRun("Object.defineProperty(obj2, 'x',"
3132 "{ get: function() { return 'y'; }, configurable: true })");
3133
3134 ExpectString("obj1.x", "y");
3135 ExpectString("obj2.x", "y");
3136
3137 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
3138 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
3139
3140 CHECK(GetGlobalProperty(&context, "obj1")->
3141 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
3142 CHECK(GetGlobalProperty(&context, "obj2")->
3143 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
3144
3145 ExpectString("obj1.x", "x");
3146 ExpectString("obj2.x", "x");
3147
3148 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
3149 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
3150
3151 // Define getters/setters, but now make them not configurable.
3152 CompileRun("Object.defineProperty(obj1, 'x',"
3153 "{ get: function() { return 'z'; }, configurable: false })");
3154 CompileRun("Object.defineProperty(obj2, 'x',"
3155 "{ get: function() { return 'z'; }, configurable: false })");
3156
3157 ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
3158 ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
3159
3160 ExpectString("obj1.x", "z");
3161 ExpectString("obj2.x", "z");
3162
3163 CHECK(!GetGlobalProperty(&context, "obj1")->
3164 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
3165 CHECK(!GetGlobalProperty(&context, "obj2")->
3166 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
3167
3168 ExpectString("obj1.x", "z");
3169 ExpectString("obj2.x", "z");
3170}
3171
3172
3173THREADED_TEST(DontDeleteAPIAccessorsCannotBeOverriden) {
3174 v8::HandleScope scope;
3175 Local<ObjectTemplate> templ = ObjectTemplate::New();
3176 LocalContext context;
3177
3178 context->Global()->Set(v8_str("obj1"), templ->NewInstance());
3179 CompileRun("var obj2 = {};");
3180
3181 CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor(
3182 v8_str("x"),
3183 GetXValue, NULL,
3184 v8_str("donut"), v8::DEFAULT, v8::DontDelete));
3185 CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor(
3186 v8_str("x"),
3187 GetXValue, NULL,
3188 v8_str("donut"), v8::DEFAULT, v8::DontDelete));
3189
3190 ExpectString("obj1.x", "x");
3191 ExpectString("obj2.x", "x");
3192
3193 ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
3194 ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
3195
3196 CHECK(!GetGlobalProperty(&context, "obj1")->
3197 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
3198 CHECK(!GetGlobalProperty(&context, "obj2")->
3199 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
3200
3201 {
3202 v8::TryCatch try_catch;
3203 CompileRun("Object.defineProperty(obj1, 'x',"
3204 "{get: function() { return 'func'; }})");
3205 CHECK(try_catch.HasCaught());
3206 String::AsciiValue exception_value(try_catch.Exception());
3207 CHECK_EQ(*exception_value,
3208 "TypeError: Cannot redefine property: defineProperty");
3209 }
3210 {
3211 v8::TryCatch try_catch;
3212 CompileRun("Object.defineProperty(obj2, 'x',"
3213 "{get: function() { return 'func'; }})");
3214 CHECK(try_catch.HasCaught());
3215 String::AsciiValue exception_value(try_catch.Exception());
3216 CHECK_EQ(*exception_value,
3217 "TypeError: Cannot redefine property: defineProperty");
3218 }
3219}
3220
3221
3222static v8::Handle<Value> Get239Value(Local<String> name,
3223 const AccessorInfo& info) {
3224 ApiTestFuzzer::Fuzz();
3225 CHECK_EQ(info.Data(), v8_str("donut"));
3226 CHECK_EQ(name, v8_str("239"));
3227 return name;
3228}
3229
3230
3231THREADED_TEST(ElementAPIAccessor) {
3232 v8::HandleScope scope;
3233 Local<ObjectTemplate> templ = ObjectTemplate::New();
3234 LocalContext context;
3235
3236 context->Global()->Set(v8_str("obj1"), templ->NewInstance());
3237 CompileRun("var obj2 = {};");
3238
3239 CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor(
3240 v8_str("239"),
3241 Get239Value, NULL,
3242 v8_str("donut")));
3243 CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor(
3244 v8_str("239"),
3245 Get239Value, NULL,
3246 v8_str("donut")));
3247
3248 ExpectString("obj1[239]", "239");
3249 ExpectString("obj2[239]", "239");
3250 ExpectString("obj1['239']", "239");
3251 ExpectString("obj2['239']", "239");
3252}
3253
Steve Blocka7e24c12009-10-30 11:49:00 +00003254
3255v8::Persistent<Value> xValue;
3256
3257
3258static void SetXValue(Local<String> name,
3259 Local<Value> value,
3260 const AccessorInfo& info) {
3261 CHECK_EQ(value, v8_num(4));
3262 CHECK_EQ(info.Data(), v8_str("donut"));
3263 CHECK_EQ(name, v8_str("x"));
3264 CHECK(xValue.IsEmpty());
3265 xValue = v8::Persistent<Value>::New(value);
3266}
3267
3268
3269THREADED_TEST(SimplePropertyWrite) {
3270 v8::HandleScope scope;
3271 Local<ObjectTemplate> templ = ObjectTemplate::New();
3272 templ->SetAccessor(v8_str("x"), GetXValue, SetXValue, v8_str("donut"));
3273 LocalContext context;
3274 context->Global()->Set(v8_str("obj"), templ->NewInstance());
3275 Local<Script> script = Script::Compile(v8_str("obj.x = 4"));
3276 for (int i = 0; i < 10; i++) {
3277 CHECK(xValue.IsEmpty());
3278 script->Run();
3279 CHECK_EQ(v8_num(4), xValue);
3280 xValue.Dispose();
3281 xValue = v8::Persistent<Value>();
3282 }
3283}
3284
3285
3286static v8::Handle<Value> XPropertyGetter(Local<String> property,
3287 const AccessorInfo& info) {
3288 ApiTestFuzzer::Fuzz();
3289 CHECK(info.Data()->IsUndefined());
3290 return property;
3291}
3292
3293
3294THREADED_TEST(NamedInterceptorPropertyRead) {
3295 v8::HandleScope scope;
3296 Local<ObjectTemplate> templ = ObjectTemplate::New();
3297 templ->SetNamedPropertyHandler(XPropertyGetter);
3298 LocalContext context;
3299 context->Global()->Set(v8_str("obj"), templ->NewInstance());
3300 Local<Script> script = Script::Compile(v8_str("obj.x"));
3301 for (int i = 0; i < 10; i++) {
3302 Local<Value> result = script->Run();
3303 CHECK_EQ(result, v8_str("x"));
3304 }
3305}
3306
3307
Steve Block6ded16b2010-05-10 14:33:55 +01003308THREADED_TEST(NamedInterceptorDictionaryIC) {
3309 v8::HandleScope scope;
3310 Local<ObjectTemplate> templ = ObjectTemplate::New();
3311 templ->SetNamedPropertyHandler(XPropertyGetter);
3312 LocalContext context;
3313 // Create an object with a named interceptor.
3314 context->Global()->Set(v8_str("interceptor_obj"), templ->NewInstance());
3315 Local<Script> script = Script::Compile(v8_str("interceptor_obj.x"));
3316 for (int i = 0; i < 10; i++) {
3317 Local<Value> result = script->Run();
3318 CHECK_EQ(result, v8_str("x"));
3319 }
3320 // Create a slow case object and a function accessing a property in
3321 // that slow case object (with dictionary probing in generated
3322 // code). Then force object with a named interceptor into slow-case,
3323 // pass it to the function, and check that the interceptor is called
3324 // instead of accessing the local property.
3325 Local<Value> result =
3326 CompileRun("function get_x(o) { return o.x; };"
3327 "var obj = { x : 42, y : 0 };"
3328 "delete obj.y;"
3329 "for (var i = 0; i < 10; i++) get_x(obj);"
3330 "interceptor_obj.x = 42;"
3331 "interceptor_obj.y = 10;"
3332 "delete interceptor_obj.y;"
3333 "get_x(interceptor_obj)");
3334 CHECK_EQ(result, v8_str("x"));
3335}
3336
3337
Kristian Monsen0d5e1162010-09-30 15:31:59 +01003338THREADED_TEST(NamedInterceptorDictionaryICMultipleContext) {
3339 v8::HandleScope scope;
3340
3341 v8::Persistent<Context> context1 = Context::New();
3342
3343 context1->Enter();
3344 Local<ObjectTemplate> templ = ObjectTemplate::New();
3345 templ->SetNamedPropertyHandler(XPropertyGetter);
3346 // Create an object with a named interceptor.
3347 v8::Local<v8::Object> object = templ->NewInstance();
3348 context1->Global()->Set(v8_str("interceptor_obj"), object);
3349
3350 // Force the object into the slow case.
3351 CompileRun("interceptor_obj.y = 0;"
3352 "delete interceptor_obj.y;");
3353 context1->Exit();
3354
3355 {
3356 // Introduce the object into a different context.
3357 // Repeat named loads to exercise ICs.
3358 LocalContext context2;
3359 context2->Global()->Set(v8_str("interceptor_obj"), object);
3360 Local<Value> result =
3361 CompileRun("function get_x(o) { return o.x; }"
3362 "interceptor_obj.x = 42;"
3363 "for (var i=0; i != 10; i++) {"
3364 " get_x(interceptor_obj);"
3365 "}"
3366 "get_x(interceptor_obj)");
3367 // Check that the interceptor was actually invoked.
3368 CHECK_EQ(result, v8_str("x"));
3369 }
3370
3371 // Return to the original context and force some object to the slow case
3372 // to cause the NormalizedMapCache to verify.
3373 context1->Enter();
3374 CompileRun("var obj = { x : 0 }; delete obj.x;");
3375 context1->Exit();
3376
3377 context1.Dispose();
3378}
3379
3380
Andrei Popescu402d9372010-02-26 13:31:12 +00003381static v8::Handle<Value> SetXOnPrototypeGetter(Local<String> property,
3382 const AccessorInfo& info) {
3383 // Set x on the prototype object and do not handle the get request.
3384 v8::Handle<v8::Value> proto = info.Holder()->GetPrototype();
Steve Block6ded16b2010-05-10 14:33:55 +01003385 proto.As<v8::Object>()->Set(v8_str("x"), v8::Integer::New(23));
Andrei Popescu402d9372010-02-26 13:31:12 +00003386 return v8::Handle<Value>();
3387}
3388
3389
3390// This is a regression test for http://crbug.com/20104. Map
3391// transitions should not interfere with post interceptor lookup.
3392THREADED_TEST(NamedInterceptorMapTransitionRead) {
3393 v8::HandleScope scope;
3394 Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New();
3395 Local<v8::ObjectTemplate> instance_template
3396 = function_template->InstanceTemplate();
3397 instance_template->SetNamedPropertyHandler(SetXOnPrototypeGetter);
3398 LocalContext context;
3399 context->Global()->Set(v8_str("F"), function_template->GetFunction());
3400 // Create an instance of F and introduce a map transition for x.
3401 CompileRun("var o = new F(); o.x = 23;");
3402 // Create an instance of F and invoke the getter. The result should be 23.
3403 Local<Value> result = CompileRun("o = new F(); o.x");
3404 CHECK_EQ(result->Int32Value(), 23);
3405}
3406
3407
Steve Blocka7e24c12009-10-30 11:49:00 +00003408static v8::Handle<Value> IndexedPropertyGetter(uint32_t index,
3409 const AccessorInfo& info) {
3410 ApiTestFuzzer::Fuzz();
3411 if (index == 37) {
3412 return v8::Handle<Value>(v8_num(625));
3413 }
3414 return v8::Handle<Value>();
3415}
3416
3417
3418static v8::Handle<Value> IndexedPropertySetter(uint32_t index,
3419 Local<Value> value,
3420 const AccessorInfo& info) {
3421 ApiTestFuzzer::Fuzz();
3422 if (index == 39) {
3423 return value;
3424 }
3425 return v8::Handle<Value>();
3426}
3427
3428
3429THREADED_TEST(IndexedInterceptorWithIndexedAccessor) {
3430 v8::HandleScope scope;
3431 Local<ObjectTemplate> templ = ObjectTemplate::New();
3432 templ->SetIndexedPropertyHandler(IndexedPropertyGetter,
3433 IndexedPropertySetter);
3434 LocalContext context;
3435 context->Global()->Set(v8_str("obj"), templ->NewInstance());
3436 Local<Script> getter_script = Script::Compile(v8_str(
3437 "obj.__defineGetter__(\"3\", function(){return 5;});obj[3];"));
3438 Local<Script> setter_script = Script::Compile(v8_str(
3439 "obj.__defineSetter__(\"17\", function(val){this.foo = val;});"
3440 "obj[17] = 23;"
3441 "obj.foo;"));
3442 Local<Script> interceptor_setter_script = Script::Compile(v8_str(
3443 "obj.__defineSetter__(\"39\", function(val){this.foo = \"hit\";});"
3444 "obj[39] = 47;"
3445 "obj.foo;")); // This setter should not run, due to the interceptor.
3446 Local<Script> interceptor_getter_script = Script::Compile(v8_str(
3447 "obj[37];"));
3448 Local<Value> result = getter_script->Run();
3449 CHECK_EQ(v8_num(5), result);
3450 result = setter_script->Run();
3451 CHECK_EQ(v8_num(23), result);
3452 result = interceptor_setter_script->Run();
3453 CHECK_EQ(v8_num(23), result);
3454 result = interceptor_getter_script->Run();
3455 CHECK_EQ(v8_num(625), result);
3456}
3457
3458
Leon Clarked91b9f72010-01-27 17:25:45 +00003459static v8::Handle<Value> IdentityIndexedPropertyGetter(
3460 uint32_t index,
3461 const AccessorInfo& info) {
Ben Murdochf87a2032010-10-22 12:50:53 +01003462 return v8::Integer::NewFromUnsigned(index);
Leon Clarked91b9f72010-01-27 17:25:45 +00003463}
3464
3465
Kristian Monsen0d5e1162010-09-30 15:31:59 +01003466THREADED_TEST(IndexedInterceptorWithGetOwnPropertyDescriptor) {
3467 v8::HandleScope scope;
3468 Local<ObjectTemplate> templ = ObjectTemplate::New();
3469 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3470
3471 LocalContext context;
3472 context->Global()->Set(v8_str("obj"), templ->NewInstance());
3473
3474 // Check fast object case.
3475 const char* fast_case_code =
3476 "Object.getOwnPropertyDescriptor(obj, 0).value.toString()";
3477 ExpectString(fast_case_code, "0");
3478
3479 // Check slow case.
3480 const char* slow_case_code =
3481 "obj.x = 1; delete obj.x;"
3482 "Object.getOwnPropertyDescriptor(obj, 1).value.toString()";
3483 ExpectString(slow_case_code, "1");
3484}
3485
3486
Leon Clarked91b9f72010-01-27 17:25:45 +00003487THREADED_TEST(IndexedInterceptorWithNoSetter) {
3488 v8::HandleScope scope;
3489 Local<ObjectTemplate> templ = ObjectTemplate::New();
3490 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3491
3492 LocalContext context;
3493 context->Global()->Set(v8_str("obj"), templ->NewInstance());
3494
3495 const char* code =
3496 "try {"
3497 " obj[0] = 239;"
3498 " for (var i = 0; i < 100; i++) {"
3499 " var v = obj[0];"
3500 " if (v != 0) throw 'Wrong value ' + v + ' at iteration ' + i;"
3501 " }"
3502 " 'PASSED'"
3503 "} catch(e) {"
3504 " e"
3505 "}";
3506 ExpectString(code, "PASSED");
3507}
3508
3509
Andrei Popescu402d9372010-02-26 13:31:12 +00003510THREADED_TEST(IndexedInterceptorWithAccessorCheck) {
3511 v8::HandleScope scope;
3512 Local<ObjectTemplate> templ = ObjectTemplate::New();
3513 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3514
3515 LocalContext context;
3516 Local<v8::Object> obj = templ->NewInstance();
3517 obj->TurnOnAccessCheck();
3518 context->Global()->Set(v8_str("obj"), obj);
3519
3520 const char* code =
3521 "try {"
3522 " for (var i = 0; i < 100; i++) {"
3523 " var v = obj[0];"
3524 " if (v != undefined) throw 'Wrong value ' + v + ' at iteration ' + i;"
3525 " }"
3526 " 'PASSED'"
3527 "} catch(e) {"
3528 " e"
3529 "}";
3530 ExpectString(code, "PASSED");
3531}
3532
3533
3534THREADED_TEST(IndexedInterceptorWithAccessorCheckSwitchedOn) {
3535 i::FLAG_allow_natives_syntax = true;
3536 v8::HandleScope scope;
3537 Local<ObjectTemplate> templ = ObjectTemplate::New();
3538 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3539
3540 LocalContext context;
3541 Local<v8::Object> obj = templ->NewInstance();
3542 context->Global()->Set(v8_str("obj"), obj);
3543
3544 const char* code =
3545 "try {"
3546 " for (var i = 0; i < 100; i++) {"
3547 " var expected = i;"
3548 " if (i == 5) {"
3549 " %EnableAccessChecks(obj);"
3550 " expected = undefined;"
3551 " }"
3552 " var v = obj[i];"
3553 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3554 " if (i == 5) %DisableAccessChecks(obj);"
3555 " }"
3556 " 'PASSED'"
3557 "} catch(e) {"
3558 " e"
3559 "}";
3560 ExpectString(code, "PASSED");
3561}
3562
3563
3564THREADED_TEST(IndexedInterceptorWithDifferentIndices) {
3565 v8::HandleScope scope;
3566 Local<ObjectTemplate> templ = ObjectTemplate::New();
3567 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3568
3569 LocalContext context;
3570 Local<v8::Object> obj = templ->NewInstance();
3571 context->Global()->Set(v8_str("obj"), obj);
3572
3573 const char* code =
3574 "try {"
3575 " for (var i = 0; i < 100; i++) {"
3576 " var v = obj[i];"
3577 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;"
3578 " }"
3579 " 'PASSED'"
3580 "} catch(e) {"
3581 " e"
3582 "}";
3583 ExpectString(code, "PASSED");
3584}
3585
3586
Ben Murdochf87a2032010-10-22 12:50:53 +01003587THREADED_TEST(IndexedInterceptorWithNegativeIndices) {
3588 v8::HandleScope scope;
3589 Local<ObjectTemplate> templ = ObjectTemplate::New();
3590 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3591
3592 LocalContext context;
3593 Local<v8::Object> obj = templ->NewInstance();
3594 context->Global()->Set(v8_str("obj"), obj);
3595
3596 const char* code =
3597 "try {"
3598 " for (var i = 0; i < 100; i++) {"
3599 " var expected = i;"
3600 " var key = i;"
3601 " if (i == 25) {"
3602 " key = -1;"
3603 " expected = undefined;"
3604 " }"
3605 " if (i == 50) {"
3606 " /* probe minimal Smi number on 32-bit platforms */"
3607 " key = -(1 << 30);"
3608 " expected = undefined;"
3609 " }"
3610 " if (i == 75) {"
3611 " /* probe minimal Smi number on 64-bit platforms */"
3612 " key = 1 << 31;"
3613 " expected = undefined;"
3614 " }"
3615 " var v = obj[key];"
3616 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3617 " }"
3618 " 'PASSED'"
3619 "} catch(e) {"
3620 " e"
3621 "}";
3622 ExpectString(code, "PASSED");
3623}
3624
3625
Andrei Popescu402d9372010-02-26 13:31:12 +00003626THREADED_TEST(IndexedInterceptorWithNotSmiLookup) {
3627 v8::HandleScope scope;
3628 Local<ObjectTemplate> templ = ObjectTemplate::New();
3629 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3630
3631 LocalContext context;
3632 Local<v8::Object> obj = templ->NewInstance();
3633 context->Global()->Set(v8_str("obj"), obj);
3634
3635 const char* code =
3636 "try {"
3637 " for (var i = 0; i < 100; i++) {"
3638 " var expected = i;"
Ben Murdochf87a2032010-10-22 12:50:53 +01003639 " var key = i;"
Andrei Popescu402d9372010-02-26 13:31:12 +00003640 " if (i == 50) {"
Ben Murdochf87a2032010-10-22 12:50:53 +01003641 " key = 'foobar';"
Andrei Popescu402d9372010-02-26 13:31:12 +00003642 " expected = undefined;"
3643 " }"
Ben Murdochf87a2032010-10-22 12:50:53 +01003644 " var v = obj[key];"
Andrei Popescu402d9372010-02-26 13:31:12 +00003645 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3646 " }"
3647 " 'PASSED'"
3648 "} catch(e) {"
3649 " e"
3650 "}";
3651 ExpectString(code, "PASSED");
3652}
3653
3654
3655THREADED_TEST(IndexedInterceptorGoingMegamorphic) {
3656 v8::HandleScope scope;
3657 Local<ObjectTemplate> templ = ObjectTemplate::New();
3658 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3659
3660 LocalContext context;
3661 Local<v8::Object> obj = templ->NewInstance();
3662 context->Global()->Set(v8_str("obj"), obj);
3663
3664 const char* code =
3665 "var original = obj;"
3666 "try {"
3667 " for (var i = 0; i < 100; i++) {"
3668 " var expected = i;"
3669 " if (i == 50) {"
3670 " obj = {50: 'foobar'};"
3671 " expected = 'foobar';"
3672 " }"
3673 " var v = obj[i];"
3674 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3675 " if (i == 50) obj = original;"
3676 " }"
3677 " 'PASSED'"
3678 "} catch(e) {"
3679 " e"
3680 "}";
3681 ExpectString(code, "PASSED");
3682}
3683
3684
3685THREADED_TEST(IndexedInterceptorReceiverTurningSmi) {
3686 v8::HandleScope scope;
3687 Local<ObjectTemplate> templ = ObjectTemplate::New();
3688 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3689
3690 LocalContext context;
3691 Local<v8::Object> obj = templ->NewInstance();
3692 context->Global()->Set(v8_str("obj"), obj);
3693
3694 const char* code =
3695 "var original = obj;"
3696 "try {"
3697 " for (var i = 0; i < 100; i++) {"
3698 " var expected = i;"
3699 " if (i == 5) {"
3700 " obj = 239;"
3701 " expected = undefined;"
3702 " }"
3703 " var v = obj[i];"
3704 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3705 " if (i == 5) obj = original;"
3706 " }"
3707 " 'PASSED'"
3708 "} catch(e) {"
3709 " e"
3710 "}";
3711 ExpectString(code, "PASSED");
3712}
3713
3714
3715THREADED_TEST(IndexedInterceptorOnProto) {
3716 v8::HandleScope scope;
3717 Local<ObjectTemplate> templ = ObjectTemplate::New();
3718 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3719
3720 LocalContext context;
3721 Local<v8::Object> obj = templ->NewInstance();
3722 context->Global()->Set(v8_str("obj"), obj);
3723
3724 const char* code =
3725 "var o = {__proto__: obj};"
3726 "try {"
3727 " for (var i = 0; i < 100; i++) {"
3728 " var v = o[i];"
3729 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;"
3730 " }"
3731 " 'PASSED'"
3732 "} catch(e) {"
3733 " e"
3734 "}";
3735 ExpectString(code, "PASSED");
3736}
3737
3738
Steve Blocka7e24c12009-10-30 11:49:00 +00003739THREADED_TEST(MultiContexts) {
3740 v8::HandleScope scope;
3741 v8::Handle<ObjectTemplate> templ = ObjectTemplate::New();
3742 templ->Set(v8_str("dummy"), v8::FunctionTemplate::New(DummyCallHandler));
3743
3744 Local<String> password = v8_str("Password");
3745
3746 // Create an environment
3747 LocalContext context0(0, templ);
3748 context0->SetSecurityToken(password);
3749 v8::Handle<v8::Object> global0 = context0->Global();
3750 global0->Set(v8_str("custom"), v8_num(1234));
3751 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value());
3752
3753 // Create an independent environment
3754 LocalContext context1(0, templ);
3755 context1->SetSecurityToken(password);
3756 v8::Handle<v8::Object> global1 = context1->Global();
3757 global1->Set(v8_str("custom"), v8_num(1234));
3758 CHECK_NE(global0, global1);
3759 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value());
3760 CHECK_EQ(1234, global1->Get(v8_str("custom"))->Int32Value());
3761
3762 // Now create a new context with the old global
3763 LocalContext context2(0, templ, global1);
3764 context2->SetSecurityToken(password);
3765 v8::Handle<v8::Object> global2 = context2->Global();
3766 CHECK_EQ(global1, global2);
3767 CHECK_EQ(0, global1->Get(v8_str("custom"))->Int32Value());
3768 CHECK_EQ(0, global2->Get(v8_str("custom"))->Int32Value());
3769}
3770
3771
3772THREADED_TEST(FunctionPrototypeAcrossContexts) {
3773 // Make sure that functions created by cloning boilerplates cannot
3774 // communicate through their __proto__ field.
3775
3776 v8::HandleScope scope;
3777
3778 LocalContext env0;
3779 v8::Handle<v8::Object> global0 =
3780 env0->Global();
3781 v8::Handle<v8::Object> object0 =
Steve Block6ded16b2010-05-10 14:33:55 +01003782 global0->Get(v8_str("Object")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003783 v8::Handle<v8::Object> tostring0 =
Steve Block6ded16b2010-05-10 14:33:55 +01003784 object0->Get(v8_str("toString")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003785 v8::Handle<v8::Object> proto0 =
Steve Block6ded16b2010-05-10 14:33:55 +01003786 tostring0->Get(v8_str("__proto__")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003787 proto0->Set(v8_str("custom"), v8_num(1234));
3788
3789 LocalContext env1;
3790 v8::Handle<v8::Object> global1 =
3791 env1->Global();
3792 v8::Handle<v8::Object> object1 =
Steve Block6ded16b2010-05-10 14:33:55 +01003793 global1->Get(v8_str("Object")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003794 v8::Handle<v8::Object> tostring1 =
Steve Block6ded16b2010-05-10 14:33:55 +01003795 object1->Get(v8_str("toString")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003796 v8::Handle<v8::Object> proto1 =
Steve Block6ded16b2010-05-10 14:33:55 +01003797 tostring1->Get(v8_str("__proto__")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003798 CHECK(!proto1->Has(v8_str("custom")));
3799}
3800
3801
3802THREADED_TEST(Regress892105) {
3803 // Make sure that object and array literals created by cloning
3804 // boilerplates cannot communicate through their __proto__
3805 // field. This is rather difficult to check, but we try to add stuff
3806 // to Object.prototype and Array.prototype and create a new
3807 // environment. This should succeed.
3808
3809 v8::HandleScope scope;
3810
3811 Local<String> source = v8_str("Object.prototype.obj = 1234;"
3812 "Array.prototype.arr = 4567;"
3813 "8901");
3814
3815 LocalContext env0;
3816 Local<Script> script0 = Script::Compile(source);
3817 CHECK_EQ(8901.0, script0->Run()->NumberValue());
3818
3819 LocalContext env1;
3820 Local<Script> script1 = Script::Compile(source);
3821 CHECK_EQ(8901.0, script1->Run()->NumberValue());
3822}
3823
3824
Steve Blocka7e24c12009-10-30 11:49:00 +00003825THREADED_TEST(UndetectableObject) {
3826 v8::HandleScope scope;
3827 LocalContext env;
3828
3829 Local<v8::FunctionTemplate> desc =
3830 v8::FunctionTemplate::New(0, v8::Handle<Value>());
3831 desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable
3832
3833 Local<v8::Object> obj = desc->GetFunction()->NewInstance();
3834 env->Global()->Set(v8_str("undetectable"), obj);
3835
3836 ExpectString("undetectable.toString()", "[object Object]");
3837 ExpectString("typeof undetectable", "undefined");
3838 ExpectString("typeof(undetectable)", "undefined");
3839 ExpectBoolean("typeof undetectable == 'undefined'", true);
3840 ExpectBoolean("typeof undetectable == 'object'", false);
3841 ExpectBoolean("if (undetectable) { true; } else { false; }", false);
3842 ExpectBoolean("!undetectable", true);
3843
3844 ExpectObject("true&&undetectable", obj);
3845 ExpectBoolean("false&&undetectable", false);
3846 ExpectBoolean("true||undetectable", true);
3847 ExpectObject("false||undetectable", obj);
3848
3849 ExpectObject("undetectable&&true", obj);
3850 ExpectObject("undetectable&&false", obj);
3851 ExpectBoolean("undetectable||true", true);
3852 ExpectBoolean("undetectable||false", false);
3853
3854 ExpectBoolean("undetectable==null", true);
3855 ExpectBoolean("null==undetectable", true);
3856 ExpectBoolean("undetectable==undefined", true);
3857 ExpectBoolean("undefined==undetectable", true);
3858 ExpectBoolean("undetectable==undetectable", true);
3859
3860
3861 ExpectBoolean("undetectable===null", false);
3862 ExpectBoolean("null===undetectable", false);
3863 ExpectBoolean("undetectable===undefined", false);
3864 ExpectBoolean("undefined===undetectable", false);
3865 ExpectBoolean("undetectable===undetectable", true);
3866}
3867
3868
Steve Block8defd9f2010-07-08 12:39:36 +01003869
3870THREADED_TEST(ExtensibleOnUndetectable) {
3871 v8::HandleScope scope;
3872 LocalContext env;
3873
3874 Local<v8::FunctionTemplate> desc =
3875 v8::FunctionTemplate::New(0, v8::Handle<Value>());
3876 desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable
3877
3878 Local<v8::Object> obj = desc->GetFunction()->NewInstance();
3879 env->Global()->Set(v8_str("undetectable"), obj);
3880
3881 Local<String> source = v8_str("undetectable.x = 42;"
3882 "undetectable.x");
3883
3884 Local<Script> script = Script::Compile(source);
3885
3886 CHECK_EQ(v8::Integer::New(42), script->Run());
3887
3888 ExpectBoolean("Object.isExtensible(undetectable)", true);
3889
3890 source = v8_str("Object.preventExtensions(undetectable);");
3891 script = Script::Compile(source);
3892 script->Run();
3893 ExpectBoolean("Object.isExtensible(undetectable)", false);
3894
3895 source = v8_str("undetectable.y = 2000;");
3896 script = Script::Compile(source);
Steve Block8defd9f2010-07-08 12:39:36 +01003897 Local<Value> result = script->Run();
Steve Block44f0eee2011-05-26 01:26:41 +01003898 ExpectBoolean("undetectable.y == undefined", true);
Steve Block8defd9f2010-07-08 12:39:36 +01003899}
3900
3901
3902
Steve Blocka7e24c12009-10-30 11:49:00 +00003903THREADED_TEST(UndetectableString) {
3904 v8::HandleScope scope;
3905 LocalContext env;
3906
3907 Local<String> obj = String::NewUndetectable("foo");
3908 env->Global()->Set(v8_str("undetectable"), obj);
3909
3910 ExpectString("undetectable", "foo");
3911 ExpectString("typeof undetectable", "undefined");
3912 ExpectString("typeof(undetectable)", "undefined");
3913 ExpectBoolean("typeof undetectable == 'undefined'", true);
3914 ExpectBoolean("typeof undetectable == 'string'", false);
3915 ExpectBoolean("if (undetectable) { true; } else { false; }", false);
3916 ExpectBoolean("!undetectable", true);
3917
3918 ExpectObject("true&&undetectable", obj);
3919 ExpectBoolean("false&&undetectable", false);
3920 ExpectBoolean("true||undetectable", true);
3921 ExpectObject("false||undetectable", obj);
3922
3923 ExpectObject("undetectable&&true", obj);
3924 ExpectObject("undetectable&&false", obj);
3925 ExpectBoolean("undetectable||true", true);
3926 ExpectBoolean("undetectable||false", false);
3927
3928 ExpectBoolean("undetectable==null", true);
3929 ExpectBoolean("null==undetectable", true);
3930 ExpectBoolean("undetectable==undefined", true);
3931 ExpectBoolean("undefined==undetectable", true);
3932 ExpectBoolean("undetectable==undetectable", true);
3933
3934
3935 ExpectBoolean("undetectable===null", false);
3936 ExpectBoolean("null===undetectable", false);
3937 ExpectBoolean("undetectable===undefined", false);
3938 ExpectBoolean("undefined===undetectable", false);
3939 ExpectBoolean("undetectable===undetectable", true);
3940}
3941
3942
Ben Murdoch257744e2011-11-30 15:57:28 +00003943TEST(UndetectableOptimized) {
3944 i::FLAG_allow_natives_syntax = true;
3945 v8::HandleScope scope;
3946 LocalContext env;
3947
3948 Local<String> obj = String::NewUndetectable("foo");
3949 env->Global()->Set(v8_str("undetectable"), obj);
3950 env->Global()->Set(v8_str("detectable"), v8_str("bar"));
3951
3952 ExpectString(
3953 "function testBranch() {"
3954 " if (!%_IsUndetectableObject(undetectable)) throw 1;"
3955 " if (%_IsUndetectableObject(detectable)) throw 2;"
3956 "}\n"
3957 "function testBool() {"
3958 " var b1 = !%_IsUndetectableObject(undetectable);"
3959 " var b2 = %_IsUndetectableObject(detectable);"
3960 " if (b1) throw 3;"
3961 " if (b2) throw 4;"
3962 " return b1 == b2;"
3963 "}\n"
3964 "%OptimizeFunctionOnNextCall(testBranch);"
3965 "%OptimizeFunctionOnNextCall(testBool);"
3966 "for (var i = 0; i < 10; i++) {"
3967 " testBranch();"
3968 " testBool();"
3969 "}\n"
3970 "\"PASS\"",
3971 "PASS");
3972}
3973
3974
Steve Blocka7e24c12009-10-30 11:49:00 +00003975template <typename T> static void USE(T) { }
3976
3977
3978// This test is not intended to be run, just type checked.
3979static void PersistentHandles() {
3980 USE(PersistentHandles);
3981 Local<String> str = v8_str("foo");
3982 v8::Persistent<String> p_str = v8::Persistent<String>::New(str);
3983 USE(p_str);
3984 Local<Script> scr = Script::Compile(v8_str(""));
3985 v8::Persistent<Script> p_scr = v8::Persistent<Script>::New(scr);
3986 USE(p_scr);
3987 Local<ObjectTemplate> templ = ObjectTemplate::New();
3988 v8::Persistent<ObjectTemplate> p_templ =
3989 v8::Persistent<ObjectTemplate>::New(templ);
3990 USE(p_templ);
3991}
3992
3993
3994static v8::Handle<Value> HandleLogDelegator(const v8::Arguments& args) {
3995 ApiTestFuzzer::Fuzz();
3996 return v8::Undefined();
3997}
3998
3999
4000THREADED_TEST(GlobalObjectTemplate) {
4001 v8::HandleScope handle_scope;
4002 Local<ObjectTemplate> global_template = ObjectTemplate::New();
4003 global_template->Set(v8_str("JSNI_Log"),
4004 v8::FunctionTemplate::New(HandleLogDelegator));
4005 v8::Persistent<Context> context = Context::New(0, global_template);
4006 Context::Scope context_scope(context);
4007 Script::Compile(v8_str("JSNI_Log('LOG')"))->Run();
4008 context.Dispose();
4009}
4010
4011
4012static const char* kSimpleExtensionSource =
4013 "function Foo() {"
4014 " return 4;"
4015 "}";
4016
4017
4018THREADED_TEST(SimpleExtensions) {
4019 v8::HandleScope handle_scope;
4020 v8::RegisterExtension(new Extension("simpletest", kSimpleExtensionSource));
4021 const char* extension_names[] = { "simpletest" };
4022 v8::ExtensionConfiguration extensions(1, extension_names);
4023 v8::Handle<Context> context = Context::New(&extensions);
4024 Context::Scope lock(context);
4025 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run();
4026 CHECK_EQ(result, v8::Integer::New(4));
4027}
4028
4029
4030static const char* kEvalExtensionSource1 =
4031 "function UseEval1() {"
4032 " var x = 42;"
4033 " return eval('x');"
4034 "}";
4035
4036
4037static const char* kEvalExtensionSource2 =
4038 "(function() {"
4039 " var x = 42;"
4040 " function e() {"
4041 " return eval('x');"
4042 " }"
4043 " this.UseEval2 = e;"
4044 "})()";
4045
4046
4047THREADED_TEST(UseEvalFromExtension) {
4048 v8::HandleScope handle_scope;
4049 v8::RegisterExtension(new Extension("evaltest1", kEvalExtensionSource1));
4050 v8::RegisterExtension(new Extension("evaltest2", kEvalExtensionSource2));
4051 const char* extension_names[] = { "evaltest1", "evaltest2" };
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("UseEval1()"))->Run();
4056 CHECK_EQ(result, v8::Integer::New(42));
4057 result = Script::Compile(v8_str("UseEval2()"))->Run();
4058 CHECK_EQ(result, v8::Integer::New(42));
4059}
4060
4061
4062static const char* kWithExtensionSource1 =
4063 "function UseWith1() {"
4064 " var x = 42;"
4065 " with({x:87}) { return x; }"
4066 "}";
4067
4068
4069
4070static const char* kWithExtensionSource2 =
4071 "(function() {"
4072 " var x = 42;"
4073 " function e() {"
4074 " with ({x:87}) { return x; }"
4075 " }"
4076 " this.UseWith2 = e;"
4077 "})()";
4078
4079
4080THREADED_TEST(UseWithFromExtension) {
4081 v8::HandleScope handle_scope;
4082 v8::RegisterExtension(new Extension("withtest1", kWithExtensionSource1));
4083 v8::RegisterExtension(new Extension("withtest2", kWithExtensionSource2));
4084 const char* extension_names[] = { "withtest1", "withtest2" };
4085 v8::ExtensionConfiguration extensions(2, extension_names);
4086 v8::Handle<Context> context = Context::New(&extensions);
4087 Context::Scope lock(context);
4088 v8::Handle<Value> result = Script::Compile(v8_str("UseWith1()"))->Run();
4089 CHECK_EQ(result, v8::Integer::New(87));
4090 result = Script::Compile(v8_str("UseWith2()"))->Run();
4091 CHECK_EQ(result, v8::Integer::New(87));
4092}
4093
4094
4095THREADED_TEST(AutoExtensions) {
4096 v8::HandleScope handle_scope;
4097 Extension* extension = new Extension("autotest", kSimpleExtensionSource);
4098 extension->set_auto_enable(true);
4099 v8::RegisterExtension(extension);
4100 v8::Handle<Context> context = Context::New();
4101 Context::Scope lock(context);
4102 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run();
4103 CHECK_EQ(result, v8::Integer::New(4));
4104}
4105
4106
Steve Blockd0582a62009-12-15 09:54:21 +00004107static const char* kSyntaxErrorInExtensionSource =
4108 "[";
4109
4110
4111// Test that a syntax error in an extension does not cause a fatal
4112// error but results in an empty context.
4113THREADED_TEST(SyntaxErrorExtensions) {
4114 v8::HandleScope handle_scope;
4115 v8::RegisterExtension(new Extension("syntaxerror",
4116 kSyntaxErrorInExtensionSource));
4117 const char* extension_names[] = { "syntaxerror" };
4118 v8::ExtensionConfiguration extensions(1, extension_names);
4119 v8::Handle<Context> context = Context::New(&extensions);
4120 CHECK(context.IsEmpty());
4121}
4122
4123
4124static const char* kExceptionInExtensionSource =
4125 "throw 42";
4126
4127
4128// Test that an exception when installing an extension does not cause
4129// a fatal error but results in an empty context.
4130THREADED_TEST(ExceptionExtensions) {
4131 v8::HandleScope handle_scope;
4132 v8::RegisterExtension(new Extension("exception",
4133 kExceptionInExtensionSource));
4134 const char* extension_names[] = { "exception" };
4135 v8::ExtensionConfiguration extensions(1, extension_names);
4136 v8::Handle<Context> context = Context::New(&extensions);
4137 CHECK(context.IsEmpty());
4138}
4139
4140
Iain Merrick9ac36c92010-09-13 15:29:50 +01004141static const char* kNativeCallInExtensionSource =
4142 "function call_runtime_last_index_of(x) {"
4143 " return %StringLastIndexOf(x, 'bob', 10);"
4144 "}";
4145
4146
4147static const char* kNativeCallTest =
4148 "call_runtime_last_index_of('bobbobboellebobboellebobbob');";
4149
4150// Test that a native runtime calls are supported in extensions.
4151THREADED_TEST(NativeCallInExtensions) {
4152 v8::HandleScope handle_scope;
4153 v8::RegisterExtension(new Extension("nativecall",
4154 kNativeCallInExtensionSource));
4155 const char* extension_names[] = { "nativecall" };
4156 v8::ExtensionConfiguration extensions(1, extension_names);
4157 v8::Handle<Context> context = Context::New(&extensions);
4158 Context::Scope lock(context);
4159 v8::Handle<Value> result = Script::Compile(v8_str(kNativeCallTest))->Run();
4160 CHECK_EQ(result, v8::Integer::New(3));
4161}
4162
4163
Steve Blocka7e24c12009-10-30 11:49:00 +00004164static void CheckDependencies(const char* name, const char* expected) {
4165 v8::HandleScope handle_scope;
4166 v8::ExtensionConfiguration config(1, &name);
4167 LocalContext context(&config);
4168 CHECK_EQ(String::New(expected), context->Global()->Get(v8_str("loaded")));
4169}
4170
4171
4172/*
4173 * Configuration:
4174 *
4175 * /-- B <--\
4176 * A <- -- D <-- E
4177 * \-- C <--/
4178 */
4179THREADED_TEST(ExtensionDependency) {
4180 static const char* kEDeps[] = { "D" };
4181 v8::RegisterExtension(new Extension("E", "this.loaded += 'E';", 1, kEDeps));
4182 static const char* kDDeps[] = { "B", "C" };
4183 v8::RegisterExtension(new Extension("D", "this.loaded += 'D';", 2, kDDeps));
4184 static const char* kBCDeps[] = { "A" };
4185 v8::RegisterExtension(new Extension("B", "this.loaded += 'B';", 1, kBCDeps));
4186 v8::RegisterExtension(new Extension("C", "this.loaded += 'C';", 1, kBCDeps));
4187 v8::RegisterExtension(new Extension("A", "this.loaded += 'A';"));
4188 CheckDependencies("A", "undefinedA");
4189 CheckDependencies("B", "undefinedAB");
4190 CheckDependencies("C", "undefinedAC");
4191 CheckDependencies("D", "undefinedABCD");
4192 CheckDependencies("E", "undefinedABCDE");
4193 v8::HandleScope handle_scope;
4194 static const char* exts[2] = { "C", "E" };
4195 v8::ExtensionConfiguration config(2, exts);
4196 LocalContext context(&config);
4197 CHECK_EQ(v8_str("undefinedACBDE"), context->Global()->Get(v8_str("loaded")));
4198}
4199
4200
4201static const char* kExtensionTestScript =
4202 "native function A();"
4203 "native function B();"
4204 "native function C();"
4205 "function Foo(i) {"
4206 " if (i == 0) return A();"
4207 " if (i == 1) return B();"
4208 " if (i == 2) return C();"
4209 "}";
4210
4211
4212static v8::Handle<Value> CallFun(const v8::Arguments& args) {
4213 ApiTestFuzzer::Fuzz();
Leon Clarkee46be812010-01-19 14:06:41 +00004214 if (args.IsConstructCall()) {
4215 args.This()->Set(v8_str("data"), args.Data());
4216 return v8::Null();
4217 }
Steve Blocka7e24c12009-10-30 11:49:00 +00004218 return args.Data();
4219}
4220
4221
4222class FunctionExtension : public Extension {
4223 public:
4224 FunctionExtension() : Extension("functiontest", kExtensionTestScript) { }
4225 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(
4226 v8::Handle<String> name);
4227};
4228
4229
4230static int lookup_count = 0;
4231v8::Handle<v8::FunctionTemplate> FunctionExtension::GetNativeFunction(
4232 v8::Handle<String> name) {
4233 lookup_count++;
4234 if (name->Equals(v8_str("A"))) {
4235 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(8));
4236 } else if (name->Equals(v8_str("B"))) {
4237 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(7));
4238 } else if (name->Equals(v8_str("C"))) {
4239 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(6));
4240 } else {
4241 return v8::Handle<v8::FunctionTemplate>();
4242 }
4243}
4244
4245
4246THREADED_TEST(FunctionLookup) {
4247 v8::RegisterExtension(new FunctionExtension());
4248 v8::HandleScope handle_scope;
4249 static const char* exts[1] = { "functiontest" };
4250 v8::ExtensionConfiguration config(1, exts);
4251 LocalContext context(&config);
4252 CHECK_EQ(3, lookup_count);
4253 CHECK_EQ(v8::Integer::New(8), Script::Compile(v8_str("Foo(0)"))->Run());
4254 CHECK_EQ(v8::Integer::New(7), Script::Compile(v8_str("Foo(1)"))->Run());
4255 CHECK_EQ(v8::Integer::New(6), Script::Compile(v8_str("Foo(2)"))->Run());
4256}
4257
4258
Leon Clarkee46be812010-01-19 14:06:41 +00004259THREADED_TEST(NativeFunctionConstructCall) {
4260 v8::RegisterExtension(new FunctionExtension());
4261 v8::HandleScope handle_scope;
4262 static const char* exts[1] = { "functiontest" };
4263 v8::ExtensionConfiguration config(1, exts);
4264 LocalContext context(&config);
Leon Clarked91b9f72010-01-27 17:25:45 +00004265 for (int i = 0; i < 10; i++) {
4266 // Run a few times to ensure that allocation of objects doesn't
4267 // change behavior of a constructor function.
4268 CHECK_EQ(v8::Integer::New(8),
4269 Script::Compile(v8_str("(new A()).data"))->Run());
4270 CHECK_EQ(v8::Integer::New(7),
4271 Script::Compile(v8_str("(new B()).data"))->Run());
4272 CHECK_EQ(v8::Integer::New(6),
4273 Script::Compile(v8_str("(new C()).data"))->Run());
4274 }
Leon Clarkee46be812010-01-19 14:06:41 +00004275}
4276
4277
Steve Blocka7e24c12009-10-30 11:49:00 +00004278static const char* last_location;
4279static const char* last_message;
4280void StoringErrorCallback(const char* location, const char* message) {
4281 if (last_location == NULL) {
4282 last_location = location;
4283 last_message = message;
4284 }
4285}
4286
4287
4288// ErrorReporting creates a circular extensions configuration and
4289// tests that the fatal error handler gets called. This renders V8
4290// unusable and therefore this test cannot be run in parallel.
4291TEST(ErrorReporting) {
4292 v8::V8::SetFatalErrorHandler(StoringErrorCallback);
4293 static const char* aDeps[] = { "B" };
4294 v8::RegisterExtension(new Extension("A", "", 1, aDeps));
4295 static const char* bDeps[] = { "A" };
4296 v8::RegisterExtension(new Extension("B", "", 1, bDeps));
4297 last_location = NULL;
4298 v8::ExtensionConfiguration config(1, bDeps);
4299 v8::Handle<Context> context = Context::New(&config);
4300 CHECK(context.IsEmpty());
4301 CHECK_NE(last_location, NULL);
4302}
4303
4304
4305static const char* js_code_causing_huge_string_flattening =
4306 "var str = 'X';"
4307 "for (var i = 0; i < 30; i++) {"
4308 " str = str + str;"
4309 "}"
4310 "str.match(/X/);";
4311
4312
4313void OOMCallback(const char* location, const char* message) {
4314 exit(0);
4315}
4316
4317
4318TEST(RegexpOutOfMemory) {
4319 // Execute a script that causes out of memory when flattening a string.
4320 v8::HandleScope scope;
4321 v8::V8::SetFatalErrorHandler(OOMCallback);
4322 LocalContext context;
4323 Local<Script> script =
4324 Script::Compile(String::New(js_code_causing_huge_string_flattening));
4325 last_location = NULL;
4326 Local<Value> result = script->Run();
4327
4328 CHECK(false); // Should not return.
4329}
4330
4331
4332static void MissingScriptInfoMessageListener(v8::Handle<v8::Message> message,
4333 v8::Handle<Value> data) {
4334 CHECK_EQ(v8::Undefined(), data);
4335 CHECK(message->GetScriptResourceName()->IsUndefined());
4336 CHECK_EQ(v8::Undefined(), message->GetScriptResourceName());
4337 message->GetLineNumber();
4338 message->GetSourceLine();
4339}
4340
4341
4342THREADED_TEST(ErrorWithMissingScriptInfo) {
4343 v8::HandleScope scope;
4344 LocalContext context;
4345 v8::V8::AddMessageListener(MissingScriptInfoMessageListener);
4346 Script::Compile(v8_str("throw Error()"))->Run();
4347 v8::V8::RemoveMessageListeners(MissingScriptInfoMessageListener);
4348}
4349
4350
4351int global_index = 0;
4352
4353class Snorkel {
4354 public:
4355 Snorkel() { index_ = global_index++; }
4356 int index_;
4357};
4358
4359class Whammy {
4360 public:
4361 Whammy() {
4362 cursor_ = 0;
4363 }
4364 ~Whammy() {
4365 script_.Dispose();
4366 }
4367 v8::Handle<Script> getScript() {
4368 if (script_.IsEmpty())
4369 script_ = v8::Persistent<Script>::New(v8_compile("({}).blammo"));
4370 return Local<Script>(*script_);
4371 }
4372
4373 public:
4374 static const int kObjectCount = 256;
4375 int cursor_;
4376 v8::Persistent<v8::Object> objects_[kObjectCount];
4377 v8::Persistent<Script> script_;
4378};
4379
4380static void HandleWeakReference(v8::Persistent<v8::Value> obj, void* data) {
4381 Snorkel* snorkel = reinterpret_cast<Snorkel*>(data);
4382 delete snorkel;
4383 obj.ClearWeak();
4384}
4385
4386v8::Handle<Value> WhammyPropertyGetter(Local<String> name,
4387 const AccessorInfo& info) {
4388 Whammy* whammy =
4389 static_cast<Whammy*>(v8::Handle<v8::External>::Cast(info.Data())->Value());
4390
4391 v8::Persistent<v8::Object> prev = whammy->objects_[whammy->cursor_];
4392
4393 v8::Handle<v8::Object> obj = v8::Object::New();
4394 v8::Persistent<v8::Object> global = v8::Persistent<v8::Object>::New(obj);
4395 if (!prev.IsEmpty()) {
4396 prev->Set(v8_str("next"), obj);
4397 prev.MakeWeak(new Snorkel(), &HandleWeakReference);
4398 whammy->objects_[whammy->cursor_].Clear();
4399 }
4400 whammy->objects_[whammy->cursor_] = global;
4401 whammy->cursor_ = (whammy->cursor_ + 1) % Whammy::kObjectCount;
4402 return whammy->getScript()->Run();
4403}
4404
4405THREADED_TEST(WeakReference) {
4406 v8::HandleScope handle_scope;
4407 v8::Handle<v8::ObjectTemplate> templ= v8::ObjectTemplate::New();
Ben Murdoch3bec4d22010-07-22 14:51:16 +01004408 Whammy* whammy = new Whammy();
Steve Blocka7e24c12009-10-30 11:49:00 +00004409 templ->SetNamedPropertyHandler(WhammyPropertyGetter,
4410 0, 0, 0, 0,
Ben Murdoch3bec4d22010-07-22 14:51:16 +01004411 v8::External::New(whammy));
Steve Blocka7e24c12009-10-30 11:49:00 +00004412 const char* extension_list[] = { "v8/gc" };
4413 v8::ExtensionConfiguration extensions(1, extension_list);
4414 v8::Persistent<Context> context = Context::New(&extensions);
4415 Context::Scope context_scope(context);
4416
4417 v8::Handle<v8::Object> interceptor = templ->NewInstance();
4418 context->Global()->Set(v8_str("whammy"), interceptor);
4419 const char* code =
4420 "var last;"
4421 "for (var i = 0; i < 10000; i++) {"
4422 " var obj = whammy.length;"
4423 " if (last) last.next = obj;"
4424 " last = obj;"
4425 "}"
4426 "gc();"
4427 "4";
4428 v8::Handle<Value> result = CompileRun(code);
4429 CHECK_EQ(4.0, result->NumberValue());
Ben Murdoch3bec4d22010-07-22 14:51:16 +01004430 delete whammy;
Steve Blocka7e24c12009-10-30 11:49:00 +00004431 context.Dispose();
4432}
4433
4434
Ben Murdoch257744e2011-11-30 15:57:28 +00004435static void DisposeAndSetFlag(v8::Persistent<v8::Value> obj, void* data) {
Steve Blockd0582a62009-12-15 09:54:21 +00004436 obj.Dispose();
4437 obj.Clear();
Steve Blockd0582a62009-12-15 09:54:21 +00004438 *(reinterpret_cast<bool*>(data)) = true;
4439}
4440
Steve Blockd0582a62009-12-15 09:54:21 +00004441
Ben Murdoch257744e2011-11-30 15:57:28 +00004442THREADED_TEST(IndependentWeakHandle) {
Steve Blockd0582a62009-12-15 09:54:21 +00004443 v8::Persistent<Context> context = Context::New();
4444 Context::Scope context_scope(context);
4445
4446 v8::Persistent<v8::Object> object_a;
Steve Blockd0582a62009-12-15 09:54:21 +00004447
4448 {
4449 v8::HandleScope handle_scope;
Steve Blockd0582a62009-12-15 09:54:21 +00004450 object_a = v8::Persistent<v8::Object>::New(v8::Object::New());
4451 }
4452
4453 bool object_a_disposed = false;
Ben Murdoch257744e2011-11-30 15:57:28 +00004454 object_a.MakeWeak(&object_a_disposed, &DisposeAndSetFlag);
4455 object_a.MarkIndependent();
4456 HEAP->PerformScavenge();
4457 CHECK(object_a_disposed);
4458}
Steve Blockd0582a62009-12-15 09:54:21 +00004459
Ben Murdoch257744e2011-11-30 15:57:28 +00004460
4461static void InvokeScavenge() {
4462 HEAP->PerformScavenge();
4463}
4464
4465
4466static void InvokeMarkSweep() {
4467 HEAP->CollectAllGarbage(false);
4468}
4469
4470
4471static void ForceScavenge(v8::Persistent<v8::Value> obj, void* data) {
4472 obj.Dispose();
4473 obj.Clear();
4474 *(reinterpret_cast<bool*>(data)) = true;
4475 InvokeScavenge();
4476}
4477
4478
4479static void ForceMarkSweep(v8::Persistent<v8::Value> obj, void* data) {
4480 obj.Dispose();
4481 obj.Clear();
4482 *(reinterpret_cast<bool*>(data)) = true;
4483 InvokeMarkSweep();
4484}
4485
4486
4487THREADED_TEST(GCFromWeakCallbacks) {
4488 v8::Persistent<Context> context = Context::New();
4489 Context::Scope context_scope(context);
4490
4491 static const int kNumberOfGCTypes = 2;
4492 v8::WeakReferenceCallback gc_forcing_callback[kNumberOfGCTypes] =
4493 {&ForceScavenge, &ForceMarkSweep};
4494
4495 typedef void (*GCInvoker)();
4496 GCInvoker invoke_gc[kNumberOfGCTypes] = {&InvokeScavenge, &InvokeMarkSweep};
4497
4498 for (int outer_gc = 0; outer_gc < kNumberOfGCTypes; outer_gc++) {
4499 for (int inner_gc = 0; inner_gc < kNumberOfGCTypes; inner_gc++) {
4500 v8::Persistent<v8::Object> object;
4501 {
4502 v8::HandleScope handle_scope;
4503 object = v8::Persistent<v8::Object>::New(v8::Object::New());
4504 }
4505 bool disposed = false;
4506 object.MakeWeak(&disposed, gc_forcing_callback[inner_gc]);
4507 object.MarkIndependent();
4508 invoke_gc[outer_gc]();
4509 CHECK(disposed);
4510 }
Steve Blockd0582a62009-12-15 09:54:21 +00004511 }
Ben Murdoch257744e2011-11-30 15:57:28 +00004512}
4513
4514
4515static void RevivingCallback(v8::Persistent<v8::Value> obj, void* data) {
4516 obj.ClearWeak();
4517 *(reinterpret_cast<bool*>(data)) = true;
4518}
4519
4520
4521THREADED_TEST(IndependentHandleRevival) {
4522 v8::Persistent<Context> context = Context::New();
4523 Context::Scope context_scope(context);
4524
4525 v8::Persistent<v8::Object> object;
4526 {
4527 v8::HandleScope handle_scope;
4528 object = v8::Persistent<v8::Object>::New(v8::Object::New());
4529 object->Set(v8_str("x"), v8::Integer::New(1));
4530 v8::Local<String> y_str = v8_str("y");
4531 object->Set(y_str, y_str);
4532 }
4533 bool revived = false;
4534 object.MakeWeak(&revived, &RevivingCallback);
4535 object.MarkIndependent();
4536 HEAP->PerformScavenge();
4537 CHECK(revived);
4538 HEAP->CollectAllGarbage(true);
4539 {
4540 v8::HandleScope handle_scope;
4541 v8::Local<String> y_str = v8_str("y");
4542 CHECK_EQ(v8::Integer::New(1), object->Get(v8_str("x")));
4543 CHECK(object->Get(y_str)->Equals(y_str));
4544 }
Steve Blockd0582a62009-12-15 09:54:21 +00004545}
4546
4547
Steve Blocka7e24c12009-10-30 11:49:00 +00004548v8::Handle<Function> args_fun;
4549
4550
4551static v8::Handle<Value> ArgumentsTestCallback(const v8::Arguments& args) {
4552 ApiTestFuzzer::Fuzz();
4553 CHECK_EQ(args_fun, args.Callee());
4554 CHECK_EQ(3, args.Length());
4555 CHECK_EQ(v8::Integer::New(1), args[0]);
4556 CHECK_EQ(v8::Integer::New(2), args[1]);
4557 CHECK_EQ(v8::Integer::New(3), args[2]);
4558 CHECK_EQ(v8::Undefined(), args[3]);
4559 v8::HandleScope scope;
Steve Block44f0eee2011-05-26 01:26:41 +01004560 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +00004561 return v8::Undefined();
4562}
4563
4564
4565THREADED_TEST(Arguments) {
4566 v8::HandleScope scope;
4567 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New();
4568 global->Set(v8_str("f"), v8::FunctionTemplate::New(ArgumentsTestCallback));
4569 LocalContext context(NULL, global);
Steve Block6ded16b2010-05-10 14:33:55 +01004570 args_fun = context->Global()->Get(v8_str("f")).As<Function>();
Steve Blocka7e24c12009-10-30 11:49:00 +00004571 v8_compile("f(1, 2, 3)")->Run();
4572}
4573
4574
Steve Blocka7e24c12009-10-30 11:49:00 +00004575static v8::Handle<Value> NoBlockGetterX(Local<String> name,
4576 const AccessorInfo&) {
4577 return v8::Handle<Value>();
4578}
4579
4580
4581static v8::Handle<Value> NoBlockGetterI(uint32_t index,
4582 const AccessorInfo&) {
4583 return v8::Handle<Value>();
4584}
4585
4586
4587static v8::Handle<v8::Boolean> PDeleter(Local<String> name,
4588 const AccessorInfo&) {
4589 if (!name->Equals(v8_str("foo"))) {
4590 return v8::Handle<v8::Boolean>(); // not intercepted
4591 }
4592
4593 return v8::False(); // intercepted, and don't delete the property
4594}
4595
4596
4597static v8::Handle<v8::Boolean> IDeleter(uint32_t index, const AccessorInfo&) {
4598 if (index != 2) {
4599 return v8::Handle<v8::Boolean>(); // not intercepted
4600 }
4601
4602 return v8::False(); // intercepted, and don't delete the property
4603}
4604
4605
4606THREADED_TEST(Deleter) {
4607 v8::HandleScope scope;
4608 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4609 obj->SetNamedPropertyHandler(NoBlockGetterX, NULL, NULL, PDeleter, NULL);
4610 obj->SetIndexedPropertyHandler(NoBlockGetterI, NULL, NULL, IDeleter, NULL);
4611 LocalContext context;
4612 context->Global()->Set(v8_str("k"), obj->NewInstance());
4613 CompileRun(
4614 "k.foo = 'foo';"
4615 "k.bar = 'bar';"
4616 "k[2] = 2;"
4617 "k[4] = 4;");
4618 CHECK(v8_compile("delete k.foo")->Run()->IsFalse());
4619 CHECK(v8_compile("delete k.bar")->Run()->IsTrue());
4620
4621 CHECK_EQ(v8_compile("k.foo")->Run(), v8_str("foo"));
4622 CHECK(v8_compile("k.bar")->Run()->IsUndefined());
4623
4624 CHECK(v8_compile("delete k[2]")->Run()->IsFalse());
4625 CHECK(v8_compile("delete k[4]")->Run()->IsTrue());
4626
4627 CHECK_EQ(v8_compile("k[2]")->Run(), v8_num(2));
4628 CHECK(v8_compile("k[4]")->Run()->IsUndefined());
4629}
4630
4631
4632static v8::Handle<Value> GetK(Local<String> name, const AccessorInfo&) {
4633 ApiTestFuzzer::Fuzz();
4634 if (name->Equals(v8_str("foo")) ||
4635 name->Equals(v8_str("bar")) ||
4636 name->Equals(v8_str("baz"))) {
4637 return v8::Undefined();
4638 }
4639 return v8::Handle<Value>();
4640}
4641
4642
4643static v8::Handle<Value> IndexedGetK(uint32_t index, const AccessorInfo&) {
4644 ApiTestFuzzer::Fuzz();
4645 if (index == 0 || index == 1) return v8::Undefined();
4646 return v8::Handle<Value>();
4647}
4648
4649
4650static v8::Handle<v8::Array> NamedEnum(const AccessorInfo&) {
4651 ApiTestFuzzer::Fuzz();
4652 v8::Handle<v8::Array> result = v8::Array::New(3);
4653 result->Set(v8::Integer::New(0), v8_str("foo"));
4654 result->Set(v8::Integer::New(1), v8_str("bar"));
4655 result->Set(v8::Integer::New(2), v8_str("baz"));
4656 return result;
4657}
4658
4659
4660static v8::Handle<v8::Array> IndexedEnum(const AccessorInfo&) {
4661 ApiTestFuzzer::Fuzz();
4662 v8::Handle<v8::Array> result = v8::Array::New(2);
4663 result->Set(v8::Integer::New(0), v8_str("0"));
4664 result->Set(v8::Integer::New(1), v8_str("1"));
4665 return result;
4666}
4667
4668
4669THREADED_TEST(Enumerators) {
4670 v8::HandleScope scope;
4671 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4672 obj->SetNamedPropertyHandler(GetK, NULL, NULL, NULL, NamedEnum);
4673 obj->SetIndexedPropertyHandler(IndexedGetK, NULL, NULL, NULL, IndexedEnum);
4674 LocalContext context;
4675 context->Global()->Set(v8_str("k"), obj->NewInstance());
4676 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun(
4677 "k[10] = 0;"
4678 "k.a = 0;"
4679 "k[5] = 0;"
4680 "k.b = 0;"
4681 "k[4294967295] = 0;"
4682 "k.c = 0;"
4683 "k[4294967296] = 0;"
4684 "k.d = 0;"
4685 "k[140000] = 0;"
4686 "k.e = 0;"
4687 "k[30000000000] = 0;"
4688 "k.f = 0;"
4689 "var result = [];"
4690 "for (var prop in k) {"
4691 " result.push(prop);"
4692 "}"
4693 "result"));
4694 // Check that we get all the property names returned including the
4695 // ones from the enumerators in the right order: indexed properties
4696 // in numerical order, indexed interceptor properties, named
4697 // properties in insertion order, named interceptor properties.
4698 // This order is not mandated by the spec, so this test is just
4699 // documenting our behavior.
4700 CHECK_EQ(17, result->Length());
4701 // Indexed properties in numerical order.
4702 CHECK_EQ(v8_str("5"), result->Get(v8::Integer::New(0)));
4703 CHECK_EQ(v8_str("10"), result->Get(v8::Integer::New(1)));
4704 CHECK_EQ(v8_str("140000"), result->Get(v8::Integer::New(2)));
4705 CHECK_EQ(v8_str("4294967295"), result->Get(v8::Integer::New(3)));
4706 // Indexed interceptor properties in the order they are returned
4707 // from the enumerator interceptor.
4708 CHECK_EQ(v8_str("0"), result->Get(v8::Integer::New(4)));
4709 CHECK_EQ(v8_str("1"), result->Get(v8::Integer::New(5)));
4710 // Named properties in insertion order.
4711 CHECK_EQ(v8_str("a"), result->Get(v8::Integer::New(6)));
4712 CHECK_EQ(v8_str("b"), result->Get(v8::Integer::New(7)));
4713 CHECK_EQ(v8_str("c"), result->Get(v8::Integer::New(8)));
4714 CHECK_EQ(v8_str("4294967296"), result->Get(v8::Integer::New(9)));
4715 CHECK_EQ(v8_str("d"), result->Get(v8::Integer::New(10)));
4716 CHECK_EQ(v8_str("e"), result->Get(v8::Integer::New(11)));
4717 CHECK_EQ(v8_str("30000000000"), result->Get(v8::Integer::New(12)));
4718 CHECK_EQ(v8_str("f"), result->Get(v8::Integer::New(13)));
4719 // Named interceptor properties.
4720 CHECK_EQ(v8_str("foo"), result->Get(v8::Integer::New(14)));
4721 CHECK_EQ(v8_str("bar"), result->Get(v8::Integer::New(15)));
4722 CHECK_EQ(v8_str("baz"), result->Get(v8::Integer::New(16)));
4723}
4724
4725
4726int p_getter_count;
4727int p_getter_count2;
4728
4729
4730static v8::Handle<Value> PGetter(Local<String> name, const AccessorInfo& info) {
4731 ApiTestFuzzer::Fuzz();
4732 p_getter_count++;
4733 v8::Handle<v8::Object> global = Context::GetCurrent()->Global();
4734 CHECK_EQ(info.Holder(), global->Get(v8_str("o1")));
4735 if (name->Equals(v8_str("p1"))) {
4736 CHECK_EQ(info.This(), global->Get(v8_str("o1")));
4737 } else if (name->Equals(v8_str("p2"))) {
4738 CHECK_EQ(info.This(), global->Get(v8_str("o2")));
4739 } else if (name->Equals(v8_str("p3"))) {
4740 CHECK_EQ(info.This(), global->Get(v8_str("o3")));
4741 } else if (name->Equals(v8_str("p4"))) {
4742 CHECK_EQ(info.This(), global->Get(v8_str("o4")));
4743 }
4744 return v8::Undefined();
4745}
4746
4747
4748static void RunHolderTest(v8::Handle<v8::ObjectTemplate> obj) {
4749 ApiTestFuzzer::Fuzz();
4750 LocalContext context;
4751 context->Global()->Set(v8_str("o1"), obj->NewInstance());
4752 CompileRun(
4753 "o1.__proto__ = { };"
4754 "var o2 = { __proto__: o1 };"
4755 "var o3 = { __proto__: o2 };"
4756 "var o4 = { __proto__: o3 };"
4757 "for (var i = 0; i < 10; i++) o4.p4;"
4758 "for (var i = 0; i < 10; i++) o3.p3;"
4759 "for (var i = 0; i < 10; i++) o2.p2;"
4760 "for (var i = 0; i < 10; i++) o1.p1;");
4761}
4762
4763
4764static v8::Handle<Value> PGetter2(Local<String> name,
4765 const AccessorInfo& info) {
4766 ApiTestFuzzer::Fuzz();
4767 p_getter_count2++;
4768 v8::Handle<v8::Object> global = Context::GetCurrent()->Global();
4769 CHECK_EQ(info.Holder(), global->Get(v8_str("o1")));
4770 if (name->Equals(v8_str("p1"))) {
4771 CHECK_EQ(info.This(), global->Get(v8_str("o1")));
4772 } else if (name->Equals(v8_str("p2"))) {
4773 CHECK_EQ(info.This(), global->Get(v8_str("o2")));
4774 } else if (name->Equals(v8_str("p3"))) {
4775 CHECK_EQ(info.This(), global->Get(v8_str("o3")));
4776 } else if (name->Equals(v8_str("p4"))) {
4777 CHECK_EQ(info.This(), global->Get(v8_str("o4")));
4778 }
4779 return v8::Undefined();
4780}
4781
4782
4783THREADED_TEST(GetterHolders) {
4784 v8::HandleScope scope;
4785 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4786 obj->SetAccessor(v8_str("p1"), PGetter);
4787 obj->SetAccessor(v8_str("p2"), PGetter);
4788 obj->SetAccessor(v8_str("p3"), PGetter);
4789 obj->SetAccessor(v8_str("p4"), PGetter);
4790 p_getter_count = 0;
4791 RunHolderTest(obj);
4792 CHECK_EQ(40, p_getter_count);
4793}
4794
4795
4796THREADED_TEST(PreInterceptorHolders) {
4797 v8::HandleScope scope;
4798 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4799 obj->SetNamedPropertyHandler(PGetter2);
4800 p_getter_count2 = 0;
4801 RunHolderTest(obj);
4802 CHECK_EQ(40, p_getter_count2);
4803}
4804
4805
4806THREADED_TEST(ObjectInstantiation) {
4807 v8::HandleScope scope;
4808 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
4809 templ->SetAccessor(v8_str("t"), PGetter2);
4810 LocalContext context;
4811 context->Global()->Set(v8_str("o"), templ->NewInstance());
4812 for (int i = 0; i < 100; i++) {
4813 v8::HandleScope inner_scope;
4814 v8::Handle<v8::Object> obj = templ->NewInstance();
4815 CHECK_NE(obj, context->Global()->Get(v8_str("o")));
4816 context->Global()->Set(v8_str("o2"), obj);
4817 v8::Handle<Value> value =
4818 Script::Compile(v8_str("o.__proto__ === o2.__proto__"))->Run();
4819 CHECK_EQ(v8::True(), value);
4820 context->Global()->Set(v8_str("o"), obj);
4821 }
4822}
4823
4824
Ben Murdochb0fe1622011-05-05 13:52:32 +01004825static int StrCmp16(uint16_t* a, uint16_t* b) {
4826 while (true) {
4827 if (*a == 0 && *b == 0) return 0;
4828 if (*a != *b) return 0 + *a - *b;
4829 a++;
4830 b++;
4831 }
4832}
4833
4834
4835static int StrNCmp16(uint16_t* a, uint16_t* b, int n) {
4836 while (true) {
4837 if (n-- == 0) return 0;
4838 if (*a == 0 && *b == 0) return 0;
4839 if (*a != *b) return 0 + *a - *b;
4840 a++;
4841 b++;
4842 }
4843}
4844
4845
Steve Blocka7e24c12009-10-30 11:49:00 +00004846THREADED_TEST(StringWrite) {
4847 v8::HandleScope scope;
4848 v8::Handle<String> str = v8_str("abcde");
Ben Murdochb0fe1622011-05-05 13:52:32 +01004849 // abc<Icelandic eth><Unicode snowman>.
4850 v8::Handle<String> str2 = v8_str("abc\303\260\342\230\203");
4851
4852 CHECK_EQ(5, str2->Length());
Steve Blocka7e24c12009-10-30 11:49:00 +00004853
4854 char buf[100];
Ben Murdochb0fe1622011-05-05 13:52:32 +01004855 char utf8buf[100];
4856 uint16_t wbuf[100];
Steve Blocka7e24c12009-10-30 11:49:00 +00004857 int len;
Ben Murdochb0fe1622011-05-05 13:52:32 +01004858 int charlen;
4859
4860 memset(utf8buf, 0x1, sizeof(utf8buf));
4861 len = str2->WriteUtf8(utf8buf, sizeof(utf8buf), &charlen);
Steve Block44f0eee2011-05-26 01:26:41 +01004862 CHECK_EQ(9, len);
4863 CHECK_EQ(5, charlen);
4864 CHECK_EQ(0, strcmp(utf8buf, "abc\303\260\342\230\203"));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004865
4866 memset(utf8buf, 0x1, sizeof(utf8buf));
4867 len = str2->WriteUtf8(utf8buf, 8, &charlen);
Steve Block44f0eee2011-05-26 01:26:41 +01004868 CHECK_EQ(8, len);
4869 CHECK_EQ(5, charlen);
4870 CHECK_EQ(0, strncmp(utf8buf, "abc\303\260\342\230\203\1", 9));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004871
4872 memset(utf8buf, 0x1, sizeof(utf8buf));
4873 len = str2->WriteUtf8(utf8buf, 7, &charlen);
Steve Block44f0eee2011-05-26 01:26:41 +01004874 CHECK_EQ(5, len);
4875 CHECK_EQ(4, charlen);
4876 CHECK_EQ(0, strncmp(utf8buf, "abc\303\260\1", 5));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004877
4878 memset(utf8buf, 0x1, sizeof(utf8buf));
4879 len = str2->WriteUtf8(utf8buf, 6, &charlen);
Steve Block44f0eee2011-05-26 01:26:41 +01004880 CHECK_EQ(5, len);
4881 CHECK_EQ(4, charlen);
4882 CHECK_EQ(0, strncmp(utf8buf, "abc\303\260\1", 5));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004883
4884 memset(utf8buf, 0x1, sizeof(utf8buf));
4885 len = str2->WriteUtf8(utf8buf, 5, &charlen);
Steve Block44f0eee2011-05-26 01:26:41 +01004886 CHECK_EQ(5, len);
4887 CHECK_EQ(4, charlen);
4888 CHECK_EQ(0, strncmp(utf8buf, "abc\303\260\1", 5));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004889
4890 memset(utf8buf, 0x1, sizeof(utf8buf));
4891 len = str2->WriteUtf8(utf8buf, 4, &charlen);
Steve Block44f0eee2011-05-26 01:26:41 +01004892 CHECK_EQ(3, len);
4893 CHECK_EQ(3, charlen);
4894 CHECK_EQ(0, strncmp(utf8buf, "abc\1", 4));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004895
4896 memset(utf8buf, 0x1, sizeof(utf8buf));
4897 len = str2->WriteUtf8(utf8buf, 3, &charlen);
Steve Block44f0eee2011-05-26 01:26:41 +01004898 CHECK_EQ(3, len);
4899 CHECK_EQ(3, charlen);
4900 CHECK_EQ(0, strncmp(utf8buf, "abc\1", 4));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004901
4902 memset(utf8buf, 0x1, sizeof(utf8buf));
4903 len = str2->WriteUtf8(utf8buf, 2, &charlen);
Steve Block44f0eee2011-05-26 01:26:41 +01004904 CHECK_EQ(2, len);
4905 CHECK_EQ(2, charlen);
4906 CHECK_EQ(0, strncmp(utf8buf, "ab\1", 3));
Steve Blocka7e24c12009-10-30 11:49:00 +00004907
4908 memset(buf, 0x1, sizeof(buf));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004909 memset(wbuf, 0x1, sizeof(wbuf));
Steve Blocka7e24c12009-10-30 11:49:00 +00004910 len = str->WriteAscii(buf);
Steve Block44f0eee2011-05-26 01:26:41 +01004911 CHECK_EQ(5, len);
Ben Murdochb0fe1622011-05-05 13:52:32 +01004912 len = str->Write(wbuf);
Steve Block44f0eee2011-05-26 01:26:41 +01004913 CHECK_EQ(5, len);
4914 CHECK_EQ(0, strcmp("abcde", buf));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004915 uint16_t answer1[] = {'a', 'b', 'c', 'd', 'e', '\0'};
Steve Block44f0eee2011-05-26 01:26:41 +01004916 CHECK_EQ(0, StrCmp16(answer1, wbuf));
Steve Blocka7e24c12009-10-30 11:49:00 +00004917
4918 memset(buf, 0x1, sizeof(buf));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004919 memset(wbuf, 0x1, sizeof(wbuf));
Steve Blocka7e24c12009-10-30 11:49:00 +00004920 len = str->WriteAscii(buf, 0, 4);
Steve Block44f0eee2011-05-26 01:26:41 +01004921 CHECK_EQ(4, len);
Ben Murdochb0fe1622011-05-05 13:52:32 +01004922 len = str->Write(wbuf, 0, 4);
Steve Block44f0eee2011-05-26 01:26:41 +01004923 CHECK_EQ(4, len);
4924 CHECK_EQ(0, strncmp("abcd\1", buf, 5));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004925 uint16_t answer2[] = {'a', 'b', 'c', 'd', 0x101};
Steve Block44f0eee2011-05-26 01:26:41 +01004926 CHECK_EQ(0, StrNCmp16(answer2, wbuf, 5));
Steve Blocka7e24c12009-10-30 11:49:00 +00004927
4928 memset(buf, 0x1, sizeof(buf));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004929 memset(wbuf, 0x1, sizeof(wbuf));
Steve Blocka7e24c12009-10-30 11:49:00 +00004930 len = str->WriteAscii(buf, 0, 5);
Steve Block44f0eee2011-05-26 01:26:41 +01004931 CHECK_EQ(5, len);
Ben Murdochb0fe1622011-05-05 13:52:32 +01004932 len = str->Write(wbuf, 0, 5);
Steve Block44f0eee2011-05-26 01:26:41 +01004933 CHECK_EQ(5, len);
4934 CHECK_EQ(0, strncmp("abcde\1", buf, 6));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004935 uint16_t answer3[] = {'a', 'b', 'c', 'd', 'e', 0x101};
Steve Block44f0eee2011-05-26 01:26:41 +01004936 CHECK_EQ(0, StrNCmp16(answer3, wbuf, 6));
Steve Blocka7e24c12009-10-30 11:49:00 +00004937
4938 memset(buf, 0x1, sizeof(buf));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004939 memset(wbuf, 0x1, sizeof(wbuf));
Steve Blocka7e24c12009-10-30 11:49:00 +00004940 len = str->WriteAscii(buf, 0, 6);
Steve Block44f0eee2011-05-26 01:26:41 +01004941 CHECK_EQ(5, len);
Ben Murdochb0fe1622011-05-05 13:52:32 +01004942 len = str->Write(wbuf, 0, 6);
Steve Block44f0eee2011-05-26 01:26:41 +01004943 CHECK_EQ(5, len);
4944 CHECK_EQ(0, strcmp("abcde", buf));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004945 uint16_t answer4[] = {'a', 'b', 'c', 'd', 'e', '\0'};
Steve Block44f0eee2011-05-26 01:26:41 +01004946 CHECK_EQ(0, StrCmp16(answer4, wbuf));
Steve Blocka7e24c12009-10-30 11:49:00 +00004947
4948 memset(buf, 0x1, sizeof(buf));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004949 memset(wbuf, 0x1, sizeof(wbuf));
Steve Blocka7e24c12009-10-30 11:49:00 +00004950 len = str->WriteAscii(buf, 4, -1);
Steve Block44f0eee2011-05-26 01:26:41 +01004951 CHECK_EQ(1, len);
Ben Murdochb0fe1622011-05-05 13:52:32 +01004952 len = str->Write(wbuf, 4, -1);
Steve Block44f0eee2011-05-26 01:26:41 +01004953 CHECK_EQ(1, len);
4954 CHECK_EQ(0, strcmp("e", buf));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004955 uint16_t answer5[] = {'e', '\0'};
Steve Block44f0eee2011-05-26 01:26:41 +01004956 CHECK_EQ(0, StrCmp16(answer5, wbuf));
Steve Blocka7e24c12009-10-30 11:49:00 +00004957
4958 memset(buf, 0x1, sizeof(buf));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004959 memset(wbuf, 0x1, sizeof(wbuf));
Steve Blocka7e24c12009-10-30 11:49:00 +00004960 len = str->WriteAscii(buf, 4, 6);
Steve Block44f0eee2011-05-26 01:26:41 +01004961 CHECK_EQ(1, len);
Ben Murdochb0fe1622011-05-05 13:52:32 +01004962 len = str->Write(wbuf, 4, 6);
Steve Block44f0eee2011-05-26 01:26:41 +01004963 CHECK_EQ(1, len);
4964 CHECK_EQ(0, strcmp("e", buf));
4965 CHECK_EQ(0, StrCmp16(answer5, wbuf));
Steve Blocka7e24c12009-10-30 11:49:00 +00004966
4967 memset(buf, 0x1, sizeof(buf));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004968 memset(wbuf, 0x1, sizeof(wbuf));
Steve Blocka7e24c12009-10-30 11:49:00 +00004969 len = str->WriteAscii(buf, 4, 1);
Steve Block44f0eee2011-05-26 01:26:41 +01004970 CHECK_EQ(1, len);
Ben Murdochb0fe1622011-05-05 13:52:32 +01004971 len = str->Write(wbuf, 4, 1);
Steve Block44f0eee2011-05-26 01:26:41 +01004972 CHECK_EQ(1, len);
4973 CHECK_EQ(0, strncmp("e\1", buf, 2));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004974 uint16_t answer6[] = {'e', 0x101};
Steve Block44f0eee2011-05-26 01:26:41 +01004975 CHECK_EQ(0, StrNCmp16(answer6, wbuf, 2));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004976
4977 memset(buf, 0x1, sizeof(buf));
4978 memset(wbuf, 0x1, sizeof(wbuf));
4979 len = str->WriteAscii(buf, 3, 1);
Steve Block44f0eee2011-05-26 01:26:41 +01004980 CHECK_EQ(1, len);
Ben Murdochb0fe1622011-05-05 13:52:32 +01004981 len = str->Write(wbuf, 3, 1);
Steve Block44f0eee2011-05-26 01:26:41 +01004982 CHECK_EQ(1, len);
4983 CHECK_EQ(0, strncmp("d\1", buf, 2));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004984 uint16_t answer7[] = {'d', 0x101};
Steve Block44f0eee2011-05-26 01:26:41 +01004985 CHECK_EQ(0, StrNCmp16(answer7, wbuf, 2));
Steve Blocka7e24c12009-10-30 11:49:00 +00004986}
4987
4988
4989THREADED_TEST(ToArrayIndex) {
4990 v8::HandleScope scope;
4991 LocalContext context;
4992
4993 v8::Handle<String> str = v8_str("42");
4994 v8::Handle<v8::Uint32> index = str->ToArrayIndex();
4995 CHECK(!index.IsEmpty());
4996 CHECK_EQ(42.0, index->Uint32Value());
4997 str = v8_str("42asdf");
4998 index = str->ToArrayIndex();
4999 CHECK(index.IsEmpty());
5000 str = v8_str("-42");
5001 index = str->ToArrayIndex();
5002 CHECK(index.IsEmpty());
5003 str = v8_str("4294967295");
5004 index = str->ToArrayIndex();
5005 CHECK(!index.IsEmpty());
5006 CHECK_EQ(4294967295.0, index->Uint32Value());
5007 v8::Handle<v8::Number> num = v8::Number::New(1);
5008 index = num->ToArrayIndex();
5009 CHECK(!index.IsEmpty());
5010 CHECK_EQ(1.0, index->Uint32Value());
5011 num = v8::Number::New(-1);
5012 index = num->ToArrayIndex();
5013 CHECK(index.IsEmpty());
5014 v8::Handle<v8::Object> obj = v8::Object::New();
5015 index = obj->ToArrayIndex();
5016 CHECK(index.IsEmpty());
5017}
5018
5019
5020THREADED_TEST(ErrorConstruction) {
5021 v8::HandleScope scope;
5022 LocalContext context;
5023
5024 v8::Handle<String> foo = v8_str("foo");
5025 v8::Handle<String> message = v8_str("message");
5026 v8::Handle<Value> range_error = v8::Exception::RangeError(foo);
5027 CHECK(range_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005028 v8::Handle<v8::Object> range_obj = range_error.As<v8::Object>();
5029 CHECK(range_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00005030 v8::Handle<Value> reference_error = v8::Exception::ReferenceError(foo);
5031 CHECK(reference_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005032 CHECK(reference_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00005033 v8::Handle<Value> syntax_error = v8::Exception::SyntaxError(foo);
5034 CHECK(syntax_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005035 CHECK(syntax_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00005036 v8::Handle<Value> type_error = v8::Exception::TypeError(foo);
5037 CHECK(type_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005038 CHECK(type_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00005039 v8::Handle<Value> error = v8::Exception::Error(foo);
5040 CHECK(error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005041 CHECK(error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00005042}
5043
5044
5045static v8::Handle<Value> YGetter(Local<String> name, const AccessorInfo& info) {
5046 ApiTestFuzzer::Fuzz();
5047 return v8_num(10);
5048}
5049
5050
5051static void YSetter(Local<String> name,
5052 Local<Value> value,
5053 const AccessorInfo& info) {
5054 if (info.This()->Has(name)) {
5055 info.This()->Delete(name);
5056 }
5057 info.This()->Set(name, value);
5058}
5059
5060
5061THREADED_TEST(DeleteAccessor) {
5062 v8::HandleScope scope;
5063 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
5064 obj->SetAccessor(v8_str("y"), YGetter, YSetter);
5065 LocalContext context;
5066 v8::Handle<v8::Object> holder = obj->NewInstance();
5067 context->Global()->Set(v8_str("holder"), holder);
5068 v8::Handle<Value> result = CompileRun(
5069 "holder.y = 11; holder.y = 12; holder.y");
5070 CHECK_EQ(12, result->Uint32Value());
5071}
5072
5073
5074THREADED_TEST(TypeSwitch) {
5075 v8::HandleScope scope;
5076 v8::Handle<v8::FunctionTemplate> templ1 = v8::FunctionTemplate::New();
5077 v8::Handle<v8::FunctionTemplate> templ2 = v8::FunctionTemplate::New();
5078 v8::Handle<v8::FunctionTemplate> templ3 = v8::FunctionTemplate::New();
5079 v8::Handle<v8::FunctionTemplate> templs[3] = { templ1, templ2, templ3 };
5080 v8::Handle<v8::TypeSwitch> type_switch = v8::TypeSwitch::New(3, templs);
5081 LocalContext context;
5082 v8::Handle<v8::Object> obj0 = v8::Object::New();
5083 v8::Handle<v8::Object> obj1 = templ1->GetFunction()->NewInstance();
5084 v8::Handle<v8::Object> obj2 = templ2->GetFunction()->NewInstance();
5085 v8::Handle<v8::Object> obj3 = templ3->GetFunction()->NewInstance();
5086 for (int i = 0; i < 10; i++) {
5087 CHECK_EQ(0, type_switch->match(obj0));
5088 CHECK_EQ(1, type_switch->match(obj1));
5089 CHECK_EQ(2, type_switch->match(obj2));
5090 CHECK_EQ(3, type_switch->match(obj3));
5091 CHECK_EQ(3, type_switch->match(obj3));
5092 CHECK_EQ(2, type_switch->match(obj2));
5093 CHECK_EQ(1, type_switch->match(obj1));
5094 CHECK_EQ(0, type_switch->match(obj0));
5095 }
5096}
5097
5098
5099// For use within the TestSecurityHandler() test.
5100static bool g_security_callback_result = false;
5101static bool NamedSecurityTestCallback(Local<v8::Object> global,
5102 Local<Value> name,
5103 v8::AccessType type,
5104 Local<Value> data) {
5105 // Always allow read access.
5106 if (type == v8::ACCESS_GET)
5107 return true;
5108
5109 // Sometimes allow other access.
5110 return g_security_callback_result;
5111}
5112
5113
5114static bool IndexedSecurityTestCallback(Local<v8::Object> global,
5115 uint32_t key,
5116 v8::AccessType type,
5117 Local<Value> data) {
5118 // Always allow read access.
5119 if (type == v8::ACCESS_GET)
5120 return true;
5121
5122 // Sometimes allow other access.
5123 return g_security_callback_result;
5124}
5125
5126
5127static int trouble_nesting = 0;
5128static v8::Handle<Value> TroubleCallback(const v8::Arguments& args) {
5129 ApiTestFuzzer::Fuzz();
5130 trouble_nesting++;
5131
5132 // Call a JS function that throws an uncaught exception.
5133 Local<v8::Object> arg_this = Context::GetCurrent()->Global();
5134 Local<Value> trouble_callee = (trouble_nesting == 3) ?
5135 arg_this->Get(v8_str("trouble_callee")) :
5136 arg_this->Get(v8_str("trouble_caller"));
5137 CHECK(trouble_callee->IsFunction());
5138 return Function::Cast(*trouble_callee)->Call(arg_this, 0, NULL);
5139}
5140
5141
5142static int report_count = 0;
5143static void ApiUncaughtExceptionTestListener(v8::Handle<v8::Message>,
5144 v8::Handle<Value>) {
5145 report_count++;
5146}
5147
5148
5149// Counts uncaught exceptions, but other tests running in parallel
5150// also have uncaught exceptions.
5151TEST(ApiUncaughtException) {
5152 report_count = 0;
5153 v8::HandleScope scope;
5154 LocalContext env;
5155 v8::V8::AddMessageListener(ApiUncaughtExceptionTestListener);
5156
5157 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback);
5158 v8::Local<v8::Object> global = env->Global();
5159 global->Set(v8_str("trouble"), fun->GetFunction());
5160
5161 Script::Compile(v8_str("function trouble_callee() {"
5162 " var x = null;"
5163 " return x.foo;"
5164 "};"
5165 "function trouble_caller() {"
5166 " trouble();"
5167 "};"))->Run();
5168 Local<Value> trouble = global->Get(v8_str("trouble"));
5169 CHECK(trouble->IsFunction());
5170 Local<Value> trouble_callee = global->Get(v8_str("trouble_callee"));
5171 CHECK(trouble_callee->IsFunction());
5172 Local<Value> trouble_caller = global->Get(v8_str("trouble_caller"));
5173 CHECK(trouble_caller->IsFunction());
5174 Function::Cast(*trouble_caller)->Call(global, 0, NULL);
5175 CHECK_EQ(1, report_count);
5176 v8::V8::RemoveMessageListeners(ApiUncaughtExceptionTestListener);
5177}
5178
Leon Clarke4515c472010-02-03 11:58:03 +00005179static const char* script_resource_name = "ExceptionInNativeScript.js";
5180static void ExceptionInNativeScriptTestListener(v8::Handle<v8::Message> message,
5181 v8::Handle<Value>) {
5182 v8::Handle<v8::Value> name_val = message->GetScriptResourceName();
5183 CHECK(!name_val.IsEmpty() && name_val->IsString());
5184 v8::String::AsciiValue name(message->GetScriptResourceName());
5185 CHECK_EQ(script_resource_name, *name);
5186 CHECK_EQ(3, message->GetLineNumber());
5187 v8::String::AsciiValue source_line(message->GetSourceLine());
5188 CHECK_EQ(" new o.foo();", *source_line);
5189}
5190
5191TEST(ExceptionInNativeScript) {
5192 v8::HandleScope scope;
5193 LocalContext env;
5194 v8::V8::AddMessageListener(ExceptionInNativeScriptTestListener);
5195
5196 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback);
5197 v8::Local<v8::Object> global = env->Global();
5198 global->Set(v8_str("trouble"), fun->GetFunction());
5199
5200 Script::Compile(v8_str("function trouble() {\n"
5201 " var o = {};\n"
5202 " new o.foo();\n"
5203 "};"), v8::String::New(script_resource_name))->Run();
5204 Local<Value> trouble = global->Get(v8_str("trouble"));
5205 CHECK(trouble->IsFunction());
5206 Function::Cast(*trouble)->Call(global, 0, NULL);
5207 v8::V8::RemoveMessageListeners(ExceptionInNativeScriptTestListener);
5208}
5209
Steve Blocka7e24c12009-10-30 11:49:00 +00005210
5211TEST(CompilationErrorUsingTryCatchHandler) {
5212 v8::HandleScope scope;
5213 LocalContext env;
5214 v8::TryCatch try_catch;
5215 Script::Compile(v8_str("This doesn't &*&@#$&*^ compile."));
5216 CHECK_NE(NULL, *try_catch.Exception());
5217 CHECK(try_catch.HasCaught());
5218}
5219
5220
5221TEST(TryCatchFinallyUsingTryCatchHandler) {
5222 v8::HandleScope scope;
5223 LocalContext env;
5224 v8::TryCatch try_catch;
5225 Script::Compile(v8_str("try { throw ''; } catch (e) {}"))->Run();
5226 CHECK(!try_catch.HasCaught());
5227 Script::Compile(v8_str("try { throw ''; } finally {}"))->Run();
5228 CHECK(try_catch.HasCaught());
5229 try_catch.Reset();
5230 Script::Compile(v8_str("(function() {"
5231 "try { throw ''; } finally { return; }"
5232 "})()"))->Run();
5233 CHECK(!try_catch.HasCaught());
5234 Script::Compile(v8_str("(function()"
5235 " { try { throw ''; } finally { throw 0; }"
5236 "})()"))->Run();
5237 CHECK(try_catch.HasCaught());
5238}
5239
5240
5241// SecurityHandler can't be run twice
5242TEST(SecurityHandler) {
5243 v8::HandleScope scope0;
5244 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
5245 global_template->SetAccessCheckCallbacks(NamedSecurityTestCallback,
5246 IndexedSecurityTestCallback);
5247 // Create an environment
5248 v8::Persistent<Context> context0 =
5249 Context::New(NULL, global_template);
5250 context0->Enter();
5251
5252 v8::Handle<v8::Object> global0 = context0->Global();
5253 v8::Handle<Script> script0 = v8_compile("foo = 111");
5254 script0->Run();
5255 global0->Set(v8_str("0"), v8_num(999));
5256 v8::Handle<Value> foo0 = global0->Get(v8_str("foo"));
5257 CHECK_EQ(111, foo0->Int32Value());
5258 v8::Handle<Value> z0 = global0->Get(v8_str("0"));
5259 CHECK_EQ(999, z0->Int32Value());
5260
5261 // Create another environment, should fail security checks.
5262 v8::HandleScope scope1;
5263
5264 v8::Persistent<Context> context1 =
5265 Context::New(NULL, global_template);
5266 context1->Enter();
5267
5268 v8::Handle<v8::Object> global1 = context1->Global();
5269 global1->Set(v8_str("othercontext"), global0);
5270 // This set will fail the security check.
5271 v8::Handle<Script> script1 =
5272 v8_compile("othercontext.foo = 222; othercontext[0] = 888;");
5273 script1->Run();
5274 // This read will pass the security check.
5275 v8::Handle<Value> foo1 = global0->Get(v8_str("foo"));
5276 CHECK_EQ(111, foo1->Int32Value());
5277 // This read will pass the security check.
5278 v8::Handle<Value> z1 = global0->Get(v8_str("0"));
5279 CHECK_EQ(999, z1->Int32Value());
5280
5281 // Create another environment, should pass security checks.
5282 { g_security_callback_result = true; // allow security handler to pass.
5283 v8::HandleScope scope2;
5284 LocalContext context2;
5285 v8::Handle<v8::Object> global2 = context2->Global();
5286 global2->Set(v8_str("othercontext"), global0);
5287 v8::Handle<Script> script2 =
5288 v8_compile("othercontext.foo = 333; othercontext[0] = 888;");
5289 script2->Run();
5290 v8::Handle<Value> foo2 = global0->Get(v8_str("foo"));
5291 CHECK_EQ(333, foo2->Int32Value());
5292 v8::Handle<Value> z2 = global0->Get(v8_str("0"));
5293 CHECK_EQ(888, z2->Int32Value());
5294 }
5295
5296 context1->Exit();
5297 context1.Dispose();
5298
5299 context0->Exit();
5300 context0.Dispose();
5301}
5302
5303
5304THREADED_TEST(SecurityChecks) {
5305 v8::HandleScope handle_scope;
5306 LocalContext env1;
5307 v8::Persistent<Context> env2 = Context::New();
5308
5309 Local<Value> foo = v8_str("foo");
5310 Local<Value> bar = v8_str("bar");
5311
5312 // Set to the same domain.
5313 env1->SetSecurityToken(foo);
5314
5315 // Create a function in env1.
5316 Script::Compile(v8_str("spy=function(){return spy;}"))->Run();
5317 Local<Value> spy = env1->Global()->Get(v8_str("spy"));
5318 CHECK(spy->IsFunction());
5319
5320 // Create another function accessing global objects.
5321 Script::Compile(v8_str("spy2=function(){return new this.Array();}"))->Run();
5322 Local<Value> spy2 = env1->Global()->Get(v8_str("spy2"));
5323 CHECK(spy2->IsFunction());
5324
5325 // Switch to env2 in the same domain and invoke spy on env2.
5326 {
5327 env2->SetSecurityToken(foo);
5328 // Enter env2
5329 Context::Scope scope_env2(env2);
5330 Local<Value> result = Function::Cast(*spy)->Call(env2->Global(), 0, NULL);
5331 CHECK(result->IsFunction());
5332 }
5333
5334 {
5335 env2->SetSecurityToken(bar);
5336 Context::Scope scope_env2(env2);
5337
5338 // Call cross_domain_call, it should throw an exception
5339 v8::TryCatch try_catch;
5340 Function::Cast(*spy2)->Call(env2->Global(), 0, NULL);
5341 CHECK(try_catch.HasCaught());
5342 }
5343
5344 env2.Dispose();
5345}
5346
5347
5348// Regression test case for issue 1183439.
5349THREADED_TEST(SecurityChecksForPrototypeChain) {
5350 v8::HandleScope scope;
5351 LocalContext current;
5352 v8::Persistent<Context> other = Context::New();
5353
5354 // Change context to be able to get to the Object function in the
5355 // other context without hitting the security checks.
5356 v8::Local<Value> other_object;
5357 { Context::Scope scope(other);
5358 other_object = other->Global()->Get(v8_str("Object"));
5359 other->Global()->Set(v8_num(42), v8_num(87));
5360 }
5361
5362 current->Global()->Set(v8_str("other"), other->Global());
5363 CHECK(v8_compile("other")->Run()->Equals(other->Global()));
5364
5365 // Make sure the security check fails here and we get an undefined
5366 // result instead of getting the Object function. Repeat in a loop
5367 // to make sure to exercise the IC code.
5368 v8::Local<Script> access_other0 = v8_compile("other.Object");
5369 v8::Local<Script> access_other1 = v8_compile("other[42]");
5370 for (int i = 0; i < 5; i++) {
5371 CHECK(!access_other0->Run()->Equals(other_object));
5372 CHECK(access_other0->Run()->IsUndefined());
5373 CHECK(!access_other1->Run()->Equals(v8_num(87)));
5374 CHECK(access_other1->Run()->IsUndefined());
5375 }
5376
5377 // Create an object that has 'other' in its prototype chain and make
5378 // sure we cannot access the Object function indirectly through
5379 // that. Repeat in a loop to make sure to exercise the IC code.
5380 v8_compile("function F() { };"
5381 "F.prototype = other;"
5382 "var f = new F();")->Run();
5383 v8::Local<Script> access_f0 = v8_compile("f.Object");
5384 v8::Local<Script> access_f1 = v8_compile("f[42]");
5385 for (int j = 0; j < 5; j++) {
5386 CHECK(!access_f0->Run()->Equals(other_object));
5387 CHECK(access_f0->Run()->IsUndefined());
5388 CHECK(!access_f1->Run()->Equals(v8_num(87)));
5389 CHECK(access_f1->Run()->IsUndefined());
5390 }
5391
5392 // Now it gets hairy: Set the prototype for the other global object
5393 // to be the current global object. The prototype chain for 'f' now
5394 // goes through 'other' but ends up in the current global object.
5395 { Context::Scope scope(other);
5396 other->Global()->Set(v8_str("__proto__"), current->Global());
5397 }
5398 // Set a named and an index property on the current global
5399 // object. To force the lookup to go through the other global object,
5400 // the properties must not exist in the other global object.
5401 current->Global()->Set(v8_str("foo"), v8_num(100));
5402 current->Global()->Set(v8_num(99), v8_num(101));
5403 // Try to read the properties from f and make sure that the access
5404 // gets stopped by the security checks on the other global object.
5405 Local<Script> access_f2 = v8_compile("f.foo");
5406 Local<Script> access_f3 = v8_compile("f[99]");
5407 for (int k = 0; k < 5; k++) {
5408 CHECK(!access_f2->Run()->Equals(v8_num(100)));
5409 CHECK(access_f2->Run()->IsUndefined());
5410 CHECK(!access_f3->Run()->Equals(v8_num(101)));
5411 CHECK(access_f3->Run()->IsUndefined());
5412 }
5413 other.Dispose();
5414}
5415
5416
5417THREADED_TEST(CrossDomainDelete) {
5418 v8::HandleScope handle_scope;
5419 LocalContext env1;
5420 v8::Persistent<Context> env2 = Context::New();
5421
5422 Local<Value> foo = v8_str("foo");
5423 Local<Value> bar = v8_str("bar");
5424
5425 // Set to the same domain.
5426 env1->SetSecurityToken(foo);
5427 env2->SetSecurityToken(foo);
5428
5429 env1->Global()->Set(v8_str("prop"), v8_num(3));
5430 env2->Global()->Set(v8_str("env1"), env1->Global());
5431
5432 // Change env2 to a different domain and delete env1.prop.
5433 env2->SetSecurityToken(bar);
5434 {
5435 Context::Scope scope_env2(env2);
5436 Local<Value> result =
5437 Script::Compile(v8_str("delete env1.prop"))->Run();
5438 CHECK(result->IsFalse());
5439 }
5440
5441 // Check that env1.prop still exists.
5442 Local<Value> v = env1->Global()->Get(v8_str("prop"));
5443 CHECK(v->IsNumber());
5444 CHECK_EQ(3, v->Int32Value());
5445
5446 env2.Dispose();
5447}
5448
5449
5450THREADED_TEST(CrossDomainIsPropertyEnumerable) {
5451 v8::HandleScope handle_scope;
5452 LocalContext env1;
5453 v8::Persistent<Context> env2 = Context::New();
5454
5455 Local<Value> foo = v8_str("foo");
5456 Local<Value> bar = v8_str("bar");
5457
5458 // Set to the same domain.
5459 env1->SetSecurityToken(foo);
5460 env2->SetSecurityToken(foo);
5461
5462 env1->Global()->Set(v8_str("prop"), v8_num(3));
5463 env2->Global()->Set(v8_str("env1"), env1->Global());
5464
5465 // env1.prop is enumerable in env2.
5466 Local<String> test = v8_str("propertyIsEnumerable.call(env1, 'prop')");
5467 {
5468 Context::Scope scope_env2(env2);
5469 Local<Value> result = Script::Compile(test)->Run();
5470 CHECK(result->IsTrue());
5471 }
5472
5473 // Change env2 to a different domain and test again.
5474 env2->SetSecurityToken(bar);
5475 {
5476 Context::Scope scope_env2(env2);
5477 Local<Value> result = Script::Compile(test)->Run();
5478 CHECK(result->IsFalse());
5479 }
5480
5481 env2.Dispose();
5482}
5483
5484
5485THREADED_TEST(CrossDomainForIn) {
5486 v8::HandleScope handle_scope;
5487 LocalContext env1;
5488 v8::Persistent<Context> env2 = Context::New();
5489
5490 Local<Value> foo = v8_str("foo");
5491 Local<Value> bar = v8_str("bar");
5492
5493 // Set to the same domain.
5494 env1->SetSecurityToken(foo);
5495 env2->SetSecurityToken(foo);
5496
5497 env1->Global()->Set(v8_str("prop"), v8_num(3));
5498 env2->Global()->Set(v8_str("env1"), env1->Global());
5499
5500 // Change env2 to a different domain and set env1's global object
5501 // as the __proto__ of an object in env2 and enumerate properties
5502 // in for-in. It shouldn't enumerate properties on env1's global
5503 // object.
5504 env2->SetSecurityToken(bar);
5505 {
5506 Context::Scope scope_env2(env2);
5507 Local<Value> result =
5508 CompileRun("(function(){var obj = {'__proto__':env1};"
5509 "for (var p in obj)"
5510 " if (p == 'prop') return false;"
5511 "return true;})()");
5512 CHECK(result->IsTrue());
5513 }
5514 env2.Dispose();
5515}
5516
5517
5518TEST(ContextDetachGlobal) {
5519 v8::HandleScope handle_scope;
5520 LocalContext env1;
5521 v8::Persistent<Context> env2 = Context::New();
5522
5523 Local<v8::Object> global1 = env1->Global();
5524
5525 Local<Value> foo = v8_str("foo");
5526
5527 // Set to the same domain.
5528 env1->SetSecurityToken(foo);
5529 env2->SetSecurityToken(foo);
5530
5531 // Enter env2
5532 env2->Enter();
5533
Andrei Popescu74b3c142010-03-29 12:03:09 +01005534 // Create a function in env2 and add a reference to it in env1.
Steve Blocka7e24c12009-10-30 11:49:00 +00005535 Local<v8::Object> global2 = env2->Global();
5536 global2->Set(v8_str("prop"), v8::Integer::New(1));
5537 CompileRun("function getProp() {return prop;}");
5538
5539 env1->Global()->Set(v8_str("getProp"),
5540 global2->Get(v8_str("getProp")));
5541
Andrei Popescu74b3c142010-03-29 12:03:09 +01005542 // Detach env2's global, and reuse the global object of env2
Steve Blocka7e24c12009-10-30 11:49:00 +00005543 env2->Exit();
5544 env2->DetachGlobal();
5545 // env2 has a new global object.
5546 CHECK(!env2->Global()->Equals(global2));
5547
5548 v8::Persistent<Context> env3 =
5549 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2);
5550 env3->SetSecurityToken(v8_str("bar"));
5551 env3->Enter();
5552
5553 Local<v8::Object> global3 = env3->Global();
5554 CHECK_EQ(global2, global3);
5555 CHECK(global3->Get(v8_str("prop"))->IsUndefined());
5556 CHECK(global3->Get(v8_str("getProp"))->IsUndefined());
5557 global3->Set(v8_str("prop"), v8::Integer::New(-1));
5558 global3->Set(v8_str("prop2"), v8::Integer::New(2));
5559 env3->Exit();
5560
5561 // Call getProp in env1, and it should return the value 1
5562 {
5563 Local<Value> get_prop = global1->Get(v8_str("getProp"));
5564 CHECK(get_prop->IsFunction());
5565 v8::TryCatch try_catch;
5566 Local<Value> r = Function::Cast(*get_prop)->Call(global1, 0, NULL);
5567 CHECK(!try_catch.HasCaught());
5568 CHECK_EQ(1, r->Int32Value());
5569 }
5570
5571 // Check that env3 is not accessible from env1
5572 {
5573 Local<Value> r = global3->Get(v8_str("prop2"));
5574 CHECK(r->IsUndefined());
5575 }
5576
5577 env2.Dispose();
5578 env3.Dispose();
5579}
5580
5581
Andrei Popescu74b3c142010-03-29 12:03:09 +01005582TEST(DetachAndReattachGlobal) {
5583 v8::HandleScope scope;
5584 LocalContext env1;
5585
5586 // Create second environment.
5587 v8::Persistent<Context> env2 = Context::New();
5588
5589 Local<Value> foo = v8_str("foo");
5590
5591 // Set same security token for env1 and env2.
5592 env1->SetSecurityToken(foo);
5593 env2->SetSecurityToken(foo);
5594
5595 // Create a property on the global object in env2.
5596 {
5597 v8::Context::Scope scope(env2);
5598 env2->Global()->Set(v8_str("p"), v8::Integer::New(42));
5599 }
5600
5601 // Create a reference to env2 global from env1 global.
5602 env1->Global()->Set(v8_str("other"), env2->Global());
5603
5604 // Check that we have access to other.p in env2 from env1.
5605 Local<Value> result = CompileRun("other.p");
5606 CHECK(result->IsInt32());
5607 CHECK_EQ(42, result->Int32Value());
5608
5609 // Hold on to global from env2 and detach global from env2.
5610 Local<v8::Object> global2 = env2->Global();
5611 env2->DetachGlobal();
5612
5613 // Check that the global has been detached. No other.p property can
5614 // be found.
5615 result = CompileRun("other.p");
5616 CHECK(result->IsUndefined());
5617
5618 // Reuse global2 for env3.
5619 v8::Persistent<Context> env3 =
5620 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2);
5621 CHECK_EQ(global2, env3->Global());
5622
5623 // Start by using the same security token for env3 as for env1 and env2.
5624 env3->SetSecurityToken(foo);
5625
5626 // Create a property on the global object in env3.
5627 {
5628 v8::Context::Scope scope(env3);
5629 env3->Global()->Set(v8_str("p"), v8::Integer::New(24));
5630 }
5631
5632 // Check that other.p is now the property in env3 and that we have access.
5633 result = CompileRun("other.p");
5634 CHECK(result->IsInt32());
5635 CHECK_EQ(24, result->Int32Value());
5636
5637 // Change security token for env3 to something different from env1 and env2.
5638 env3->SetSecurityToken(v8_str("bar"));
5639
5640 // Check that we do not have access to other.p in env1. |other| is now
5641 // the global object for env3 which has a different security token,
5642 // so access should be blocked.
5643 result = CompileRun("other.p");
5644 CHECK(result->IsUndefined());
5645
5646 // Detach the global for env3 and reattach it to env2.
5647 env3->DetachGlobal();
5648 env2->ReattachGlobal(global2);
5649
5650 // Check that we have access to other.p again in env1. |other| is now
5651 // the global object for env2 which has the same security token as env1.
5652 result = CompileRun("other.p");
5653 CHECK(result->IsInt32());
5654 CHECK_EQ(42, result->Int32Value());
5655
5656 env2.Dispose();
5657 env3.Dispose();
5658}
5659
5660
Steve Block1e0659c2011-05-24 12:43:12 +01005661static bool allowed_access_type[v8::ACCESS_KEYS + 1] = { false };
Steve Blocka7e24c12009-10-30 11:49:00 +00005662static bool NamedAccessBlocker(Local<v8::Object> global,
5663 Local<Value> name,
5664 v8::AccessType type,
5665 Local<Value> data) {
Steve Block1e0659c2011-05-24 12:43:12 +01005666 return Context::GetCurrent()->Global()->Equals(global) ||
5667 allowed_access_type[type];
Steve Blocka7e24c12009-10-30 11:49:00 +00005668}
5669
5670
5671static bool IndexedAccessBlocker(Local<v8::Object> global,
5672 uint32_t key,
5673 v8::AccessType type,
5674 Local<Value> data) {
Steve Block1e0659c2011-05-24 12:43:12 +01005675 return Context::GetCurrent()->Global()->Equals(global) ||
5676 allowed_access_type[type];
Steve Blocka7e24c12009-10-30 11:49:00 +00005677}
5678
5679
5680static int g_echo_value = -1;
5681static v8::Handle<Value> EchoGetter(Local<String> name,
5682 const AccessorInfo& info) {
5683 return v8_num(g_echo_value);
5684}
5685
5686
5687static void EchoSetter(Local<String> name,
5688 Local<Value> value,
5689 const AccessorInfo&) {
5690 if (value->IsNumber())
5691 g_echo_value = value->Int32Value();
5692}
5693
5694
5695static v8::Handle<Value> UnreachableGetter(Local<String> name,
5696 const AccessorInfo& info) {
5697 CHECK(false); // This function should not be called..
5698 return v8::Undefined();
5699}
5700
5701
5702static void UnreachableSetter(Local<String>, Local<Value>,
5703 const AccessorInfo&) {
5704 CHECK(false); // This function should nto be called.
5705}
5706
5707
Steve Block1e0659c2011-05-24 12:43:12 +01005708TEST(AccessControl) {
Steve Blocka7e24c12009-10-30 11:49:00 +00005709 v8::HandleScope handle_scope;
5710 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
5711
5712 global_template->SetAccessCheckCallbacks(NamedAccessBlocker,
5713 IndexedAccessBlocker);
5714
5715 // Add an accessor accessible by cross-domain JS code.
5716 global_template->SetAccessor(
5717 v8_str("accessible_prop"),
5718 EchoGetter, EchoSetter,
5719 v8::Handle<Value>(),
5720 v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE));
5721
5722 // Add an accessor that is not accessible by cross-domain JS code.
5723 global_template->SetAccessor(v8_str("blocked_prop"),
5724 UnreachableGetter, UnreachableSetter,
5725 v8::Handle<Value>(),
5726 v8::DEFAULT);
5727
5728 // Create an environment
5729 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
5730 context0->Enter();
5731
5732 v8::Handle<v8::Object> global0 = context0->Global();
5733
Steve Block1e0659c2011-05-24 12:43:12 +01005734 // Define a property with JS getter and setter.
5735 CompileRun(
5736 "function getter() { return 'getter'; };\n"
5737 "function setter() { return 'setter'; }\n"
5738 "Object.defineProperty(this, 'js_accessor_p', {get:getter, set:setter})");
5739
5740 Local<Value> getter = global0->Get(v8_str("getter"));
5741 Local<Value> setter = global0->Get(v8_str("setter"));
5742
5743 // And define normal element.
5744 global0->Set(239, v8_str("239"));
5745
5746 // Define an element with JS getter and setter.
5747 CompileRun(
5748 "function el_getter() { return 'el_getter'; };\n"
5749 "function el_setter() { return 'el_setter'; };\n"
5750 "Object.defineProperty(this, '42', {get: el_getter, set: el_setter});");
5751
5752 Local<Value> el_getter = global0->Get(v8_str("el_getter"));
5753 Local<Value> el_setter = global0->Get(v8_str("el_setter"));
5754
Steve Blocka7e24c12009-10-30 11:49:00 +00005755 v8::HandleScope scope1;
5756
5757 v8::Persistent<Context> context1 = Context::New();
5758 context1->Enter();
5759
5760 v8::Handle<v8::Object> global1 = context1->Global();
5761 global1->Set(v8_str("other"), global0);
5762
Steve Block1e0659c2011-05-24 12:43:12 +01005763 // Access blocked property.
5764 CompileRun("other.blocked_prop = 1");
5765
5766 ExpectUndefined("other.blocked_prop");
5767 ExpectUndefined(
5768 "Object.getOwnPropertyDescriptor(other, 'blocked_prop')");
5769 ExpectFalse("propertyIsEnumerable.call(other, 'blocked_prop')");
5770
5771 // Enable ACCESS_HAS
5772 allowed_access_type[v8::ACCESS_HAS] = true;
5773 ExpectUndefined("other.blocked_prop");
5774 // ... and now we can get the descriptor...
5775 ExpectUndefined(
5776 "Object.getOwnPropertyDescriptor(other, 'blocked_prop').value");
5777 // ... and enumerate the property.
5778 ExpectTrue("propertyIsEnumerable.call(other, 'blocked_prop')");
5779 allowed_access_type[v8::ACCESS_HAS] = false;
5780
5781 // Access blocked element.
5782 CompileRun("other[239] = 1");
5783
5784 ExpectUndefined("other[239]");
5785 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '239')");
5786 ExpectFalse("propertyIsEnumerable.call(other, '239')");
5787
5788 // Enable ACCESS_HAS
5789 allowed_access_type[v8::ACCESS_HAS] = true;
5790 ExpectUndefined("other[239]");
5791 // ... and now we can get the descriptor...
5792 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '239').value");
5793 // ... and enumerate the property.
5794 ExpectTrue("propertyIsEnumerable.call(other, '239')");
5795 allowed_access_type[v8::ACCESS_HAS] = false;
5796
5797 // Access a property with JS accessor.
5798 CompileRun("other.js_accessor_p = 2");
5799
5800 ExpectUndefined("other.js_accessor_p");
5801 ExpectUndefined(
5802 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p')");
5803
5804 // Enable ACCESS_HAS.
5805 allowed_access_type[v8::ACCESS_HAS] = true;
5806 ExpectUndefined("other.js_accessor_p");
5807 ExpectUndefined(
5808 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').get");
5809 ExpectUndefined(
5810 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').set");
5811 ExpectUndefined(
5812 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').value");
5813 allowed_access_type[v8::ACCESS_HAS] = false;
5814
5815 // Enable both ACCESS_HAS and ACCESS_GET.
5816 allowed_access_type[v8::ACCESS_HAS] = true;
5817 allowed_access_type[v8::ACCESS_GET] = true;
5818
5819 ExpectString("other.js_accessor_p", "getter");
5820 ExpectObject(
5821 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').get", getter);
5822 ExpectUndefined(
5823 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').set");
5824 ExpectUndefined(
5825 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').value");
5826
5827 allowed_access_type[v8::ACCESS_GET] = false;
5828 allowed_access_type[v8::ACCESS_HAS] = false;
5829
5830 // Enable both ACCESS_HAS and ACCESS_SET.
5831 allowed_access_type[v8::ACCESS_HAS] = true;
5832 allowed_access_type[v8::ACCESS_SET] = true;
5833
5834 ExpectUndefined("other.js_accessor_p");
5835 ExpectUndefined(
5836 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').get");
5837 ExpectObject(
5838 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').set", setter);
5839 ExpectUndefined(
5840 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').value");
5841
5842 allowed_access_type[v8::ACCESS_SET] = false;
5843 allowed_access_type[v8::ACCESS_HAS] = false;
5844
5845 // Enable both ACCESS_HAS, ACCESS_GET and ACCESS_SET.
5846 allowed_access_type[v8::ACCESS_HAS] = true;
5847 allowed_access_type[v8::ACCESS_GET] = true;
5848 allowed_access_type[v8::ACCESS_SET] = true;
5849
5850 ExpectString("other.js_accessor_p", "getter");
5851 ExpectObject(
5852 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').get", getter);
5853 ExpectObject(
5854 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').set", setter);
5855 ExpectUndefined(
5856 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').value");
5857
5858 allowed_access_type[v8::ACCESS_SET] = false;
5859 allowed_access_type[v8::ACCESS_GET] = false;
5860 allowed_access_type[v8::ACCESS_HAS] = false;
5861
5862 // Access an element with JS accessor.
5863 CompileRun("other[42] = 2");
5864
5865 ExpectUndefined("other[42]");
5866 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42')");
5867
5868 // Enable ACCESS_HAS.
5869 allowed_access_type[v8::ACCESS_HAS] = true;
5870 ExpectUndefined("other[42]");
5871 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').get");
5872 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').set");
5873 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').value");
5874 allowed_access_type[v8::ACCESS_HAS] = false;
5875
5876 // Enable both ACCESS_HAS and ACCESS_GET.
5877 allowed_access_type[v8::ACCESS_HAS] = true;
5878 allowed_access_type[v8::ACCESS_GET] = true;
5879
5880 ExpectString("other[42]", "el_getter");
5881 ExpectObject("Object.getOwnPropertyDescriptor(other, '42').get", el_getter);
5882 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').set");
5883 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').value");
5884
5885 allowed_access_type[v8::ACCESS_GET] = false;
5886 allowed_access_type[v8::ACCESS_HAS] = false;
5887
5888 // Enable both ACCESS_HAS and ACCESS_SET.
5889 allowed_access_type[v8::ACCESS_HAS] = true;
5890 allowed_access_type[v8::ACCESS_SET] = true;
5891
5892 ExpectUndefined("other[42]");
5893 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').get");
5894 ExpectObject("Object.getOwnPropertyDescriptor(other, '42').set", el_setter);
5895 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').value");
5896
5897 allowed_access_type[v8::ACCESS_SET] = false;
5898 allowed_access_type[v8::ACCESS_HAS] = false;
5899
5900 // Enable both ACCESS_HAS, ACCESS_GET and ACCESS_SET.
5901 allowed_access_type[v8::ACCESS_HAS] = true;
5902 allowed_access_type[v8::ACCESS_GET] = true;
5903 allowed_access_type[v8::ACCESS_SET] = true;
5904
5905 ExpectString("other[42]", "el_getter");
5906 ExpectObject("Object.getOwnPropertyDescriptor(other, '42').get", el_getter);
5907 ExpectObject("Object.getOwnPropertyDescriptor(other, '42').set", el_setter);
5908 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').value");
5909
5910 allowed_access_type[v8::ACCESS_SET] = false;
5911 allowed_access_type[v8::ACCESS_GET] = false;
5912 allowed_access_type[v8::ACCESS_HAS] = false;
5913
Steve Blocka7e24c12009-10-30 11:49:00 +00005914 v8::Handle<Value> value;
5915
Steve Blocka7e24c12009-10-30 11:49:00 +00005916 // Access accessible property
Steve Block1e0659c2011-05-24 12:43:12 +01005917 value = CompileRun("other.accessible_prop = 3");
Steve Blocka7e24c12009-10-30 11:49:00 +00005918 CHECK(value->IsNumber());
5919 CHECK_EQ(3, value->Int32Value());
Andrei Popescu31002712010-02-23 13:46:05 +00005920 CHECK_EQ(3, g_echo_value);
Steve Blocka7e24c12009-10-30 11:49:00 +00005921
Steve Block1e0659c2011-05-24 12:43:12 +01005922 value = CompileRun("other.accessible_prop");
Steve Blocka7e24c12009-10-30 11:49:00 +00005923 CHECK(value->IsNumber());
5924 CHECK_EQ(3, value->Int32Value());
5925
Steve Block1e0659c2011-05-24 12:43:12 +01005926 value = CompileRun(
5927 "Object.getOwnPropertyDescriptor(other, 'accessible_prop').value");
5928 CHECK(value->IsNumber());
5929 CHECK_EQ(3, value->Int32Value());
5930
5931 value = CompileRun("propertyIsEnumerable.call(other, 'accessible_prop')");
Steve Blocka7e24c12009-10-30 11:49:00 +00005932 CHECK(value->IsTrue());
5933
5934 // Enumeration doesn't enumerate accessors from inaccessible objects in
5935 // the prototype chain even if the accessors are in themselves accessible.
Steve Block1e0659c2011-05-24 12:43:12 +01005936 value =
Steve Blocka7e24c12009-10-30 11:49:00 +00005937 CompileRun("(function(){var obj = {'__proto__':other};"
5938 "for (var p in obj)"
5939 " if (p == 'accessible_prop' || p == 'blocked_prop') {"
5940 " return false;"
5941 " }"
5942 "return true;})()");
Steve Block1e0659c2011-05-24 12:43:12 +01005943 CHECK(value->IsTrue());
Steve Blocka7e24c12009-10-30 11:49:00 +00005944
5945 context1->Exit();
5946 context0->Exit();
5947 context1.Dispose();
5948 context0.Dispose();
5949}
5950
5951
Ben Murdoche0cee9b2011-05-25 10:26:03 +01005952TEST(AccessControlES5) {
5953 v8::HandleScope handle_scope;
5954 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
5955
5956 global_template->SetAccessCheckCallbacks(NamedAccessBlocker,
5957 IndexedAccessBlocker);
5958
Steve Block44f0eee2011-05-26 01:26:41 +01005959 // Add accessible accessor.
5960 global_template->SetAccessor(
5961 v8_str("accessible_prop"),
5962 EchoGetter, EchoSetter,
5963 v8::Handle<Value>(),
5964 v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE));
5965
5966
Ben Murdoche0cee9b2011-05-25 10:26:03 +01005967 // Add an accessor that is not accessible by cross-domain JS code.
5968 global_template->SetAccessor(v8_str("blocked_prop"),
5969 UnreachableGetter, UnreachableSetter,
5970 v8::Handle<Value>(),
5971 v8::DEFAULT);
5972
5973 // Create an environment
5974 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
5975 context0->Enter();
5976
5977 v8::Handle<v8::Object> global0 = context0->Global();
5978
5979 v8::Persistent<Context> context1 = Context::New();
5980 context1->Enter();
5981 v8::Handle<v8::Object> global1 = context1->Global();
5982 global1->Set(v8_str("other"), global0);
5983
5984 // Regression test for issue 1154.
5985 ExpectTrue("Object.keys(other).indexOf('blocked_prop') == -1");
5986
5987 ExpectUndefined("other.blocked_prop");
5988
5989 // Regression test for issue 1027.
5990 CompileRun("Object.defineProperty(\n"
5991 " other, 'blocked_prop', {configurable: false})");
5992 ExpectUndefined("other.blocked_prop");
5993 ExpectUndefined(
5994 "Object.getOwnPropertyDescriptor(other, 'blocked_prop')");
5995
5996 // Regression test for issue 1171.
5997 ExpectTrue("Object.isExtensible(other)");
5998 CompileRun("Object.preventExtensions(other)");
5999 ExpectTrue("Object.isExtensible(other)");
6000
6001 // Object.seal and Object.freeze.
6002 CompileRun("Object.freeze(other)");
6003 ExpectTrue("Object.isExtensible(other)");
6004
6005 CompileRun("Object.seal(other)");
6006 ExpectTrue("Object.isExtensible(other)");
Steve Block44f0eee2011-05-26 01:26:41 +01006007
6008 // Regression test for issue 1250.
6009 // Make sure that we can set the accessible accessors value using normal
6010 // assignment.
6011 CompileRun("other.accessible_prop = 42");
6012 CHECK_EQ(42, g_echo_value);
6013
6014 v8::Handle<Value> value;
6015 // We follow Safari in ignoring assignments to host object accessors.
6016 CompileRun("Object.defineProperty(other, 'accessible_prop', {value: -1})");
6017 value = CompileRun("other.accessible_prop == 42");
6018 CHECK(value->IsTrue());
Ben Murdoche0cee9b2011-05-25 10:26:03 +01006019}
6020
6021
Leon Clarke4515c472010-02-03 11:58:03 +00006022static bool GetOwnPropertyNamesNamedBlocker(Local<v8::Object> global,
6023 Local<Value> name,
6024 v8::AccessType type,
6025 Local<Value> data) {
6026 return false;
6027}
6028
6029
6030static bool GetOwnPropertyNamesIndexedBlocker(Local<v8::Object> global,
6031 uint32_t key,
6032 v8::AccessType type,
6033 Local<Value> data) {
6034 return false;
6035}
6036
6037
6038THREADED_TEST(AccessControlGetOwnPropertyNames) {
6039 v8::HandleScope handle_scope;
6040 v8::Handle<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New();
6041
6042 obj_template->Set(v8_str("x"), v8::Integer::New(42));
6043 obj_template->SetAccessCheckCallbacks(GetOwnPropertyNamesNamedBlocker,
6044 GetOwnPropertyNamesIndexedBlocker);
6045
6046 // Create an environment
6047 v8::Persistent<Context> context0 = Context::New(NULL, obj_template);
6048 context0->Enter();
6049
6050 v8::Handle<v8::Object> global0 = context0->Global();
6051
6052 v8::HandleScope scope1;
6053
6054 v8::Persistent<Context> context1 = Context::New();
6055 context1->Enter();
6056
6057 v8::Handle<v8::Object> global1 = context1->Global();
6058 global1->Set(v8_str("other"), global0);
6059 global1->Set(v8_str("object"), obj_template->NewInstance());
6060
6061 v8::Handle<Value> value;
6062
6063 // Attempt to get the property names of the other global object and
6064 // of an object that requires access checks. Accessing the other
6065 // global object should be blocked by access checks on the global
6066 // proxy object. Accessing the object that requires access checks
6067 // is blocked by the access checks on the object itself.
6068 value = CompileRun("Object.getOwnPropertyNames(other).length == 0");
6069 CHECK(value->IsTrue());
6070
6071 value = CompileRun("Object.getOwnPropertyNames(object).length == 0");
6072 CHECK(value->IsTrue());
6073
6074 context1->Exit();
6075 context0->Exit();
6076 context1.Dispose();
6077 context0.Dispose();
6078}
6079
6080
Steve Block8defd9f2010-07-08 12:39:36 +01006081static v8::Handle<v8::Array> NamedPropertyEnumerator(const AccessorInfo& info) {
6082 v8::Handle<v8::Array> result = v8::Array::New(1);
6083 result->Set(0, v8_str("x"));
6084 return result;
6085}
6086
6087
6088THREADED_TEST(GetOwnPropertyNamesWithInterceptor) {
6089 v8::HandleScope handle_scope;
6090 v8::Handle<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New();
6091
6092 obj_template->Set(v8_str("x"), v8::Integer::New(42));
6093 obj_template->SetNamedPropertyHandler(NULL, NULL, NULL, NULL,
6094 NamedPropertyEnumerator);
6095
6096 LocalContext context;
6097 v8::Handle<v8::Object> global = context->Global();
6098 global->Set(v8_str("object"), obj_template->NewInstance());
6099
6100 v8::Handle<Value> value =
6101 CompileRun("Object.getOwnPropertyNames(object).join(',')");
6102 CHECK_EQ(v8_str("x"), value);
6103}
6104
6105
Steve Blocka7e24c12009-10-30 11:49:00 +00006106static v8::Handle<Value> ConstTenGetter(Local<String> name,
6107 const AccessorInfo& info) {
6108 return v8_num(10);
6109}
6110
6111
6112THREADED_TEST(CrossDomainAccessors) {
6113 v8::HandleScope handle_scope;
6114
6115 v8::Handle<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New();
6116
6117 v8::Handle<v8::ObjectTemplate> global_template =
6118 func_template->InstanceTemplate();
6119
6120 v8::Handle<v8::ObjectTemplate> proto_template =
6121 func_template->PrototypeTemplate();
6122
6123 // Add an accessor to proto that's accessible by cross-domain JS code.
6124 proto_template->SetAccessor(v8_str("accessible"),
6125 ConstTenGetter, 0,
6126 v8::Handle<Value>(),
6127 v8::ALL_CAN_READ);
6128
6129 // Add an accessor that is not accessible by cross-domain JS code.
6130 global_template->SetAccessor(v8_str("unreachable"),
6131 UnreachableGetter, 0,
6132 v8::Handle<Value>(),
6133 v8::DEFAULT);
6134
6135 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
6136 context0->Enter();
6137
6138 Local<v8::Object> global = context0->Global();
6139 // Add a normal property that shadows 'accessible'
6140 global->Set(v8_str("accessible"), v8_num(11));
6141
6142 // Enter a new context.
6143 v8::HandleScope scope1;
6144 v8::Persistent<Context> context1 = Context::New();
6145 context1->Enter();
6146
6147 v8::Handle<v8::Object> global1 = context1->Global();
6148 global1->Set(v8_str("other"), global);
6149
6150 // Should return 10, instead of 11
6151 v8::Handle<Value> value = v8_compile("other.accessible")->Run();
6152 CHECK(value->IsNumber());
6153 CHECK_EQ(10, value->Int32Value());
6154
6155 value = v8_compile("other.unreachable")->Run();
6156 CHECK(value->IsUndefined());
6157
6158 context1->Exit();
6159 context0->Exit();
6160 context1.Dispose();
6161 context0.Dispose();
6162}
6163
6164
6165static int named_access_count = 0;
6166static int indexed_access_count = 0;
6167
6168static bool NamedAccessCounter(Local<v8::Object> global,
6169 Local<Value> name,
6170 v8::AccessType type,
6171 Local<Value> data) {
6172 named_access_count++;
6173 return true;
6174}
6175
6176
6177static bool IndexedAccessCounter(Local<v8::Object> global,
6178 uint32_t key,
6179 v8::AccessType type,
6180 Local<Value> data) {
6181 indexed_access_count++;
6182 return true;
6183}
6184
6185
6186// This one is too easily disturbed by other tests.
6187TEST(AccessControlIC) {
6188 named_access_count = 0;
6189 indexed_access_count = 0;
6190
6191 v8::HandleScope handle_scope;
6192
6193 // Create an environment.
6194 v8::Persistent<Context> context0 = Context::New();
6195 context0->Enter();
6196
6197 // Create an object that requires access-check functions to be
6198 // called for cross-domain access.
6199 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
6200 object_template->SetAccessCheckCallbacks(NamedAccessCounter,
6201 IndexedAccessCounter);
6202 Local<v8::Object> object = object_template->NewInstance();
6203
6204 v8::HandleScope scope1;
6205
6206 // Create another environment.
6207 v8::Persistent<Context> context1 = Context::New();
6208 context1->Enter();
6209
6210 // Make easy access to the object from the other environment.
6211 v8::Handle<v8::Object> global1 = context1->Global();
6212 global1->Set(v8_str("obj"), object);
6213
6214 v8::Handle<Value> value;
6215
6216 // Check that the named access-control function is called every time.
6217 CompileRun("function testProp(obj) {"
6218 " for (var i = 0; i < 10; i++) obj.prop = 1;"
6219 " for (var j = 0; j < 10; j++) obj.prop;"
6220 " return obj.prop"
6221 "}");
6222 value = CompileRun("testProp(obj)");
6223 CHECK(value->IsNumber());
6224 CHECK_EQ(1, value->Int32Value());
6225 CHECK_EQ(21, named_access_count);
6226
6227 // Check that the named access-control function is called every time.
6228 CompileRun("var p = 'prop';"
6229 "function testKeyed(obj) {"
6230 " for (var i = 0; i < 10; i++) obj[p] = 1;"
6231 " for (var j = 0; j < 10; j++) obj[p];"
6232 " return obj[p];"
6233 "}");
6234 // Use obj which requires access checks. No inline caching is used
6235 // in that case.
6236 value = CompileRun("testKeyed(obj)");
6237 CHECK(value->IsNumber());
6238 CHECK_EQ(1, value->Int32Value());
6239 CHECK_EQ(42, named_access_count);
6240 // Force the inline caches into generic state and try again.
6241 CompileRun("testKeyed({ a: 0 })");
6242 CompileRun("testKeyed({ b: 0 })");
6243 value = CompileRun("testKeyed(obj)");
6244 CHECK(value->IsNumber());
6245 CHECK_EQ(1, value->Int32Value());
6246 CHECK_EQ(63, named_access_count);
6247
6248 // Check that the indexed access-control function is called every time.
6249 CompileRun("function testIndexed(obj) {"
6250 " for (var i = 0; i < 10; i++) obj[0] = 1;"
6251 " for (var j = 0; j < 10; j++) obj[0];"
6252 " return obj[0]"
6253 "}");
6254 value = CompileRun("testIndexed(obj)");
6255 CHECK(value->IsNumber());
6256 CHECK_EQ(1, value->Int32Value());
6257 CHECK_EQ(21, indexed_access_count);
6258 // Force the inline caches into generic state.
6259 CompileRun("testIndexed(new Array(1))");
6260 // Test that the indexed access check is called.
6261 value = CompileRun("testIndexed(obj)");
6262 CHECK(value->IsNumber());
6263 CHECK_EQ(1, value->Int32Value());
6264 CHECK_EQ(42, indexed_access_count);
6265
6266 // Check that the named access check is called when invoking
6267 // functions on an object that requires access checks.
6268 CompileRun("obj.f = function() {}");
6269 CompileRun("function testCallNormal(obj) {"
6270 " for (var i = 0; i < 10; i++) obj.f();"
6271 "}");
6272 CompileRun("testCallNormal(obj)");
6273 CHECK_EQ(74, named_access_count);
6274
6275 // Force obj into slow case.
6276 value = CompileRun("delete obj.prop");
6277 CHECK(value->BooleanValue());
6278 // Force inline caches into dictionary probing mode.
6279 CompileRun("var o = { x: 0 }; delete o.x; testProp(o);");
6280 // Test that the named access check is called.
6281 value = CompileRun("testProp(obj);");
6282 CHECK(value->IsNumber());
6283 CHECK_EQ(1, value->Int32Value());
6284 CHECK_EQ(96, named_access_count);
6285
6286 // Force the call inline cache into dictionary probing mode.
6287 CompileRun("o.f = function() {}; testCallNormal(o)");
6288 // Test that the named access check is still called for each
6289 // invocation of the function.
6290 value = CompileRun("testCallNormal(obj)");
6291 CHECK_EQ(106, named_access_count);
6292
6293 context1->Exit();
6294 context0->Exit();
6295 context1.Dispose();
6296 context0.Dispose();
6297}
6298
6299
6300static bool NamedAccessFlatten(Local<v8::Object> global,
6301 Local<Value> name,
6302 v8::AccessType type,
6303 Local<Value> data) {
6304 char buf[100];
6305 int len;
6306
6307 CHECK(name->IsString());
6308
6309 memset(buf, 0x1, sizeof(buf));
Steve Block6ded16b2010-05-10 14:33:55 +01006310 len = name.As<String>()->WriteAscii(buf);
Steve Blocka7e24c12009-10-30 11:49:00 +00006311 CHECK_EQ(4, len);
6312
6313 uint16_t buf2[100];
6314
6315 memset(buf, 0x1, sizeof(buf));
Steve Block6ded16b2010-05-10 14:33:55 +01006316 len = name.As<String>()->Write(buf2);
Steve Blocka7e24c12009-10-30 11:49:00 +00006317 CHECK_EQ(4, len);
6318
6319 return true;
6320}
6321
6322
6323static bool IndexedAccessFlatten(Local<v8::Object> global,
6324 uint32_t key,
6325 v8::AccessType type,
6326 Local<Value> data) {
6327 return true;
6328}
6329
6330
6331// Regression test. In access checks, operations that may cause
6332// garbage collection are not allowed. It used to be the case that
6333// using the Write operation on a string could cause a garbage
6334// collection due to flattening of the string. This is no longer the
6335// case.
6336THREADED_TEST(AccessControlFlatten) {
6337 named_access_count = 0;
6338 indexed_access_count = 0;
6339
6340 v8::HandleScope handle_scope;
6341
6342 // Create an environment.
6343 v8::Persistent<Context> context0 = Context::New();
6344 context0->Enter();
6345
6346 // Create an object that requires access-check functions to be
6347 // called for cross-domain access.
6348 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
6349 object_template->SetAccessCheckCallbacks(NamedAccessFlatten,
6350 IndexedAccessFlatten);
6351 Local<v8::Object> object = object_template->NewInstance();
6352
6353 v8::HandleScope scope1;
6354
6355 // Create another environment.
6356 v8::Persistent<Context> context1 = Context::New();
6357 context1->Enter();
6358
6359 // Make easy access to the object from the other environment.
6360 v8::Handle<v8::Object> global1 = context1->Global();
6361 global1->Set(v8_str("obj"), object);
6362
6363 v8::Handle<Value> value;
6364
6365 value = v8_compile("var p = 'as' + 'df';")->Run();
6366 value = v8_compile("obj[p];")->Run();
6367
6368 context1->Exit();
6369 context0->Exit();
6370 context1.Dispose();
6371 context0.Dispose();
6372}
6373
6374
6375static v8::Handle<Value> AccessControlNamedGetter(
6376 Local<String>, const AccessorInfo&) {
6377 return v8::Integer::New(42);
6378}
6379
6380
6381static v8::Handle<Value> AccessControlNamedSetter(
6382 Local<String>, Local<Value> value, const AccessorInfo&) {
6383 return value;
6384}
6385
6386
6387static v8::Handle<Value> AccessControlIndexedGetter(
6388 uint32_t index,
6389 const AccessorInfo& info) {
6390 return v8_num(42);
6391}
6392
6393
6394static v8::Handle<Value> AccessControlIndexedSetter(
6395 uint32_t, Local<Value> value, const AccessorInfo&) {
6396 return value;
6397}
6398
6399
6400THREADED_TEST(AccessControlInterceptorIC) {
6401 named_access_count = 0;
6402 indexed_access_count = 0;
6403
6404 v8::HandleScope handle_scope;
6405
6406 // Create an environment.
6407 v8::Persistent<Context> context0 = Context::New();
6408 context0->Enter();
6409
6410 // Create an object that requires access-check functions to be
6411 // called for cross-domain access. The object also has interceptors
6412 // interceptor.
6413 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
6414 object_template->SetAccessCheckCallbacks(NamedAccessCounter,
6415 IndexedAccessCounter);
6416 object_template->SetNamedPropertyHandler(AccessControlNamedGetter,
6417 AccessControlNamedSetter);
6418 object_template->SetIndexedPropertyHandler(AccessControlIndexedGetter,
6419 AccessControlIndexedSetter);
6420 Local<v8::Object> object = object_template->NewInstance();
6421
6422 v8::HandleScope scope1;
6423
6424 // Create another environment.
6425 v8::Persistent<Context> context1 = Context::New();
6426 context1->Enter();
6427
6428 // Make easy access to the object from the other environment.
6429 v8::Handle<v8::Object> global1 = context1->Global();
6430 global1->Set(v8_str("obj"), object);
6431
6432 v8::Handle<Value> value;
6433
6434 // Check that the named access-control function is called every time
6435 // eventhough there is an interceptor on the object.
6436 value = v8_compile("for (var i = 0; i < 10; i++) obj.x = 1;")->Run();
6437 value = v8_compile("for (var i = 0; i < 10; i++) obj.x;"
6438 "obj.x")->Run();
6439 CHECK(value->IsNumber());
6440 CHECK_EQ(42, value->Int32Value());
6441 CHECK_EQ(21, named_access_count);
6442
6443 value = v8_compile("var p = 'x';")->Run();
6444 value = v8_compile("for (var i = 0; i < 10; i++) obj[p] = 1;")->Run();
6445 value = v8_compile("for (var i = 0; i < 10; i++) obj[p];"
6446 "obj[p]")->Run();
6447 CHECK(value->IsNumber());
6448 CHECK_EQ(42, value->Int32Value());
6449 CHECK_EQ(42, named_access_count);
6450
6451 // Check that the indexed access-control function is called every
6452 // time eventhough there is an interceptor on the object.
6453 value = v8_compile("for (var i = 0; i < 10; i++) obj[0] = 1;")->Run();
6454 value = v8_compile("for (var i = 0; i < 10; i++) obj[0];"
6455 "obj[0]")->Run();
6456 CHECK(value->IsNumber());
6457 CHECK_EQ(42, value->Int32Value());
6458 CHECK_EQ(21, indexed_access_count);
6459
6460 context1->Exit();
6461 context0->Exit();
6462 context1.Dispose();
6463 context0.Dispose();
6464}
6465
6466
6467THREADED_TEST(Version) {
6468 v8::V8::GetVersion();
6469}
6470
6471
6472static v8::Handle<Value> InstanceFunctionCallback(const v8::Arguments& args) {
6473 ApiTestFuzzer::Fuzz();
6474 return v8_num(12);
6475}
6476
6477
6478THREADED_TEST(InstanceProperties) {
6479 v8::HandleScope handle_scope;
6480 LocalContext context;
6481
6482 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
6483 Local<ObjectTemplate> instance = t->InstanceTemplate();
6484
6485 instance->Set(v8_str("x"), v8_num(42));
6486 instance->Set(v8_str("f"),
6487 v8::FunctionTemplate::New(InstanceFunctionCallback));
6488
6489 Local<Value> o = t->GetFunction()->NewInstance();
6490
6491 context->Global()->Set(v8_str("i"), o);
6492 Local<Value> value = Script::Compile(v8_str("i.x"))->Run();
6493 CHECK_EQ(42, value->Int32Value());
6494
6495 value = Script::Compile(v8_str("i.f()"))->Run();
6496 CHECK_EQ(12, value->Int32Value());
6497}
6498
6499
6500static v8::Handle<Value>
6501GlobalObjectInstancePropertiesGet(Local<String> key, const AccessorInfo&) {
6502 ApiTestFuzzer::Fuzz();
6503 return v8::Handle<Value>();
6504}
6505
6506
6507THREADED_TEST(GlobalObjectInstanceProperties) {
6508 v8::HandleScope handle_scope;
6509
6510 Local<Value> global_object;
6511
6512 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
6513 t->InstanceTemplate()->SetNamedPropertyHandler(
6514 GlobalObjectInstancePropertiesGet);
6515 Local<ObjectTemplate> instance_template = t->InstanceTemplate();
6516 instance_template->Set(v8_str("x"), v8_num(42));
6517 instance_template->Set(v8_str("f"),
6518 v8::FunctionTemplate::New(InstanceFunctionCallback));
6519
Ben Murdochb0fe1622011-05-05 13:52:32 +01006520 // The script to check how Crankshaft compiles missing global function
6521 // invocations. function g is not defined and should throw on call.
6522 const char* script =
6523 "function wrapper(call) {"
6524 " var x = 0, y = 1;"
6525 " for (var i = 0; i < 1000; i++) {"
6526 " x += i * 100;"
6527 " y += i * 100;"
6528 " }"
6529 " if (call) g();"
6530 "}"
6531 "for (var i = 0; i < 17; i++) wrapper(false);"
6532 "var thrown = 0;"
6533 "try { wrapper(true); } catch (e) { thrown = 1; };"
6534 "thrown";
6535
Steve Blocka7e24c12009-10-30 11:49:00 +00006536 {
6537 LocalContext env(NULL, instance_template);
6538 // Hold on to the global object so it can be used again in another
6539 // environment initialization.
6540 global_object = env->Global();
6541
6542 Local<Value> value = Script::Compile(v8_str("x"))->Run();
6543 CHECK_EQ(42, value->Int32Value());
6544 value = Script::Compile(v8_str("f()"))->Run();
6545 CHECK_EQ(12, value->Int32Value());
Ben Murdochb0fe1622011-05-05 13:52:32 +01006546 value = Script::Compile(v8_str(script))->Run();
6547 CHECK_EQ(1, value->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00006548 }
6549
6550 {
6551 // Create new environment reusing the global object.
6552 LocalContext env(NULL, instance_template, global_object);
6553 Local<Value> value = Script::Compile(v8_str("x"))->Run();
6554 CHECK_EQ(42, value->Int32Value());
6555 value = Script::Compile(v8_str("f()"))->Run();
6556 CHECK_EQ(12, value->Int32Value());
Ben Murdochb0fe1622011-05-05 13:52:32 +01006557 value = Script::Compile(v8_str(script))->Run();
6558 CHECK_EQ(1, value->Int32Value());
6559 }
6560}
6561
6562
6563THREADED_TEST(CallKnownGlobalReceiver) {
6564 v8::HandleScope handle_scope;
6565
6566 Local<Value> global_object;
6567
6568 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
6569 Local<ObjectTemplate> instance_template = t->InstanceTemplate();
6570
6571 // The script to check that we leave global object not
6572 // global object proxy on stack when we deoptimize from inside
6573 // arguments evaluation.
6574 // To provoke error we need to both force deoptimization
6575 // from arguments evaluation and to force CallIC to take
6576 // CallIC_Miss code path that can't cope with global proxy.
6577 const char* script =
6578 "function bar(x, y) { try { } finally { } }"
6579 "function baz(x) { try { } finally { } }"
6580 "function bom(x) { try { } finally { } }"
6581 "function foo(x) { bar([x], bom(2)); }"
6582 "for (var i = 0; i < 10000; i++) foo(1);"
6583 "foo";
6584
6585 Local<Value> foo;
6586 {
6587 LocalContext env(NULL, instance_template);
6588 // Hold on to the global object so it can be used again in another
6589 // environment initialization.
6590 global_object = env->Global();
6591 foo = Script::Compile(v8_str(script))->Run();
6592 }
6593
6594 {
6595 // Create new environment reusing the global object.
6596 LocalContext env(NULL, instance_template, global_object);
6597 env->Global()->Set(v8_str("foo"), foo);
6598 Local<Value> value = Script::Compile(v8_str("foo()"))->Run();
Steve Blocka7e24c12009-10-30 11:49:00 +00006599 }
6600}
6601
6602
6603static v8::Handle<Value> ShadowFunctionCallback(const v8::Arguments& args) {
6604 ApiTestFuzzer::Fuzz();
6605 return v8_num(42);
6606}
6607
6608
6609static int shadow_y;
6610static int shadow_y_setter_call_count;
6611static int shadow_y_getter_call_count;
6612
6613
6614static void ShadowYSetter(Local<String>, Local<Value>, const AccessorInfo&) {
6615 shadow_y_setter_call_count++;
6616 shadow_y = 42;
6617}
6618
6619
6620static v8::Handle<Value> ShadowYGetter(Local<String> name,
6621 const AccessorInfo& info) {
6622 ApiTestFuzzer::Fuzz();
6623 shadow_y_getter_call_count++;
6624 return v8_num(shadow_y);
6625}
6626
6627
6628static v8::Handle<Value> ShadowIndexedGet(uint32_t index,
6629 const AccessorInfo& info) {
6630 return v8::Handle<Value>();
6631}
6632
6633
6634static v8::Handle<Value> ShadowNamedGet(Local<String> key,
6635 const AccessorInfo&) {
6636 return v8::Handle<Value>();
6637}
6638
6639
6640THREADED_TEST(ShadowObject) {
6641 shadow_y = shadow_y_setter_call_count = shadow_y_getter_call_count = 0;
6642 v8::HandleScope handle_scope;
6643
6644 Local<ObjectTemplate> global_template = v8::ObjectTemplate::New();
6645 LocalContext context(NULL, global_template);
6646
6647 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
6648 t->InstanceTemplate()->SetNamedPropertyHandler(ShadowNamedGet);
6649 t->InstanceTemplate()->SetIndexedPropertyHandler(ShadowIndexedGet);
6650 Local<ObjectTemplate> proto = t->PrototypeTemplate();
6651 Local<ObjectTemplate> instance = t->InstanceTemplate();
6652
6653 // Only allow calls of f on instances of t.
6654 Local<v8::Signature> signature = v8::Signature::New(t);
6655 proto->Set(v8_str("f"),
6656 v8::FunctionTemplate::New(ShadowFunctionCallback,
6657 Local<Value>(),
6658 signature));
6659 proto->Set(v8_str("x"), v8_num(12));
6660
6661 instance->SetAccessor(v8_str("y"), ShadowYGetter, ShadowYSetter);
6662
6663 Local<Value> o = t->GetFunction()->NewInstance();
6664 context->Global()->Set(v8_str("__proto__"), o);
6665
6666 Local<Value> value =
6667 Script::Compile(v8_str("propertyIsEnumerable(0)"))->Run();
6668 CHECK(value->IsBoolean());
6669 CHECK(!value->BooleanValue());
6670
6671 value = Script::Compile(v8_str("x"))->Run();
6672 CHECK_EQ(12, value->Int32Value());
6673
6674 value = Script::Compile(v8_str("f()"))->Run();
6675 CHECK_EQ(42, value->Int32Value());
6676
6677 Script::Compile(v8_str("y = 42"))->Run();
6678 CHECK_EQ(1, shadow_y_setter_call_count);
6679 value = Script::Compile(v8_str("y"))->Run();
6680 CHECK_EQ(1, shadow_y_getter_call_count);
6681 CHECK_EQ(42, value->Int32Value());
6682}
6683
6684
6685THREADED_TEST(HiddenPrototype) {
6686 v8::HandleScope handle_scope;
6687 LocalContext context;
6688
6689 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New();
6690 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0));
6691 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New();
6692 t1->SetHiddenPrototype(true);
6693 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1));
6694 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New();
6695 t2->SetHiddenPrototype(true);
6696 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2));
6697 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New();
6698 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3));
6699
6700 Local<v8::Object> o0 = t0->GetFunction()->NewInstance();
6701 Local<v8::Object> o1 = t1->GetFunction()->NewInstance();
6702 Local<v8::Object> o2 = t2->GetFunction()->NewInstance();
6703 Local<v8::Object> o3 = t3->GetFunction()->NewInstance();
6704
6705 // Setting the prototype on an object skips hidden prototypes.
6706 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
6707 o0->Set(v8_str("__proto__"), o1);
6708 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
6709 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
6710 o0->Set(v8_str("__proto__"), o2);
6711 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
6712 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
6713 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
6714 o0->Set(v8_str("__proto__"), o3);
6715 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
6716 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
6717 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
6718 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value());
6719
6720 // Getting the prototype of o0 should get the first visible one
6721 // which is o3. Therefore, z should not be defined on the prototype
6722 // object.
6723 Local<Value> proto = o0->Get(v8_str("__proto__"));
6724 CHECK(proto->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01006725 CHECK(proto.As<v8::Object>()->Get(v8_str("z"))->IsUndefined());
Steve Blocka7e24c12009-10-30 11:49:00 +00006726}
6727
6728
Andrei Popescu402d9372010-02-26 13:31:12 +00006729THREADED_TEST(SetPrototype) {
6730 v8::HandleScope handle_scope;
6731 LocalContext context;
6732
6733 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New();
6734 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0));
6735 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New();
6736 t1->SetHiddenPrototype(true);
6737 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1));
6738 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New();
6739 t2->SetHiddenPrototype(true);
6740 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2));
6741 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New();
6742 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3));
6743
6744 Local<v8::Object> o0 = t0->GetFunction()->NewInstance();
6745 Local<v8::Object> o1 = t1->GetFunction()->NewInstance();
6746 Local<v8::Object> o2 = t2->GetFunction()->NewInstance();
6747 Local<v8::Object> o3 = t3->GetFunction()->NewInstance();
6748
6749 // Setting the prototype on an object does not skip hidden prototypes.
6750 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
6751 CHECK(o0->SetPrototype(o1));
6752 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
6753 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
6754 CHECK(o1->SetPrototype(o2));
6755 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
6756 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
6757 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
6758 CHECK(o2->SetPrototype(o3));
6759 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
6760 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
6761 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
6762 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value());
6763
6764 // Getting the prototype of o0 should get the first visible one
6765 // which is o3. Therefore, z should not be defined on the prototype
6766 // object.
6767 Local<Value> proto = o0->Get(v8_str("__proto__"));
6768 CHECK(proto->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01006769 CHECK_EQ(proto.As<v8::Object>(), o3);
Andrei Popescu402d9372010-02-26 13:31:12 +00006770
6771 // However, Object::GetPrototype ignores hidden prototype.
6772 Local<Value> proto0 = o0->GetPrototype();
6773 CHECK(proto0->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01006774 CHECK_EQ(proto0.As<v8::Object>(), o1);
Andrei Popescu402d9372010-02-26 13:31:12 +00006775
6776 Local<Value> proto1 = o1->GetPrototype();
6777 CHECK(proto1->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01006778 CHECK_EQ(proto1.As<v8::Object>(), o2);
Andrei Popescu402d9372010-02-26 13:31:12 +00006779
6780 Local<Value> proto2 = o2->GetPrototype();
6781 CHECK(proto2->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01006782 CHECK_EQ(proto2.As<v8::Object>(), o3);
Andrei Popescu402d9372010-02-26 13:31:12 +00006783}
6784
6785
6786THREADED_TEST(SetPrototypeThrows) {
6787 v8::HandleScope handle_scope;
6788 LocalContext context;
6789
6790 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
6791
6792 Local<v8::Object> o0 = t->GetFunction()->NewInstance();
6793 Local<v8::Object> o1 = t->GetFunction()->NewInstance();
6794
6795 CHECK(o0->SetPrototype(o1));
6796 // If setting the prototype leads to the cycle, SetPrototype should
6797 // return false and keep VM in sane state.
6798 v8::TryCatch try_catch;
6799 CHECK(!o1->SetPrototype(o0));
6800 CHECK(!try_catch.HasCaught());
Steve Block44f0eee2011-05-26 01:26:41 +01006801 ASSERT(!i::Isolate::Current()->has_pending_exception());
Andrei Popescu402d9372010-02-26 13:31:12 +00006802
6803 CHECK_EQ(42, CompileRun("function f() { return 42; }; f()")->Int32Value());
6804}
6805
6806
Steve Blocka7e24c12009-10-30 11:49:00 +00006807THREADED_TEST(GetterSetterExceptions) {
6808 v8::HandleScope handle_scope;
6809 LocalContext context;
6810 CompileRun(
6811 "function Foo() { };"
6812 "function Throw() { throw 5; };"
6813 "var x = { };"
6814 "x.__defineSetter__('set', Throw);"
6815 "x.__defineGetter__('get', Throw);");
6816 Local<v8::Object> x =
6817 Local<v8::Object>::Cast(context->Global()->Get(v8_str("x")));
6818 v8::TryCatch try_catch;
6819 x->Set(v8_str("set"), v8::Integer::New(8));
6820 x->Get(v8_str("get"));
6821 x->Set(v8_str("set"), v8::Integer::New(8));
6822 x->Get(v8_str("get"));
6823 x->Set(v8_str("set"), v8::Integer::New(8));
6824 x->Get(v8_str("get"));
6825 x->Set(v8_str("set"), v8::Integer::New(8));
6826 x->Get(v8_str("get"));
6827}
6828
6829
6830THREADED_TEST(Constructor) {
6831 v8::HandleScope handle_scope;
6832 LocalContext context;
6833 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
6834 templ->SetClassName(v8_str("Fun"));
6835 Local<Function> cons = templ->GetFunction();
6836 context->Global()->Set(v8_str("Fun"), cons);
6837 Local<v8::Object> inst = cons->NewInstance();
6838 i::Handle<i::JSObject> obj = v8::Utils::OpenHandle(*inst);
6839 Local<Value> value = CompileRun("(new Fun()).constructor === Fun");
6840 CHECK(value->BooleanValue());
6841}
6842
Ben Murdoch257744e2011-11-30 15:57:28 +00006843
6844static Handle<Value> ConstructorCallback(const Arguments& args) {
6845 ApiTestFuzzer::Fuzz();
6846 Local<Object> This;
6847
6848 if (args.IsConstructCall()) {
6849 Local<Object> Holder = args.Holder();
6850 This = Object::New();
6851 Local<Value> proto = Holder->GetPrototype();
6852 if (proto->IsObject()) {
6853 This->SetPrototype(proto);
6854 }
6855 } else {
6856 This = args.This();
6857 }
6858
6859 This->Set(v8_str("a"), args[0]);
6860 return This;
6861}
6862
6863
6864static Handle<Value> FakeConstructorCallback(const Arguments& args) {
6865 ApiTestFuzzer::Fuzz();
6866 return args[0];
6867}
6868
6869
6870THREADED_TEST(ConstructorForObject) {
6871 v8::HandleScope handle_scope;
6872 LocalContext context;
6873
6874 { Local<ObjectTemplate> instance_template = ObjectTemplate::New();
6875 instance_template->SetCallAsFunctionHandler(ConstructorCallback);
6876 Local<Object> instance = instance_template->NewInstance();
6877 context->Global()->Set(v8_str("obj"), instance);
6878 v8::TryCatch try_catch;
6879 Local<Value> value;
6880 CHECK(!try_catch.HasCaught());
6881
6882 // Call the Object's constructor with a 32-bit signed integer.
6883 value = CompileRun("(function() { var o = new obj(28); return o.a; })()");
6884 CHECK(!try_catch.HasCaught());
6885 CHECK(value->IsInt32());
6886 CHECK_EQ(28, value->Int32Value());
6887
6888 Local<Value> args1[] = { v8_num(28) };
6889 Local<Value> value_obj1 = instance->CallAsConstructor(1, args1);
6890 CHECK(value_obj1->IsObject());
6891 Local<Object> object1 = Local<Object>::Cast(value_obj1);
6892 value = object1->Get(v8_str("a"));
6893 CHECK(value->IsInt32());
6894 CHECK(!try_catch.HasCaught());
6895 CHECK_EQ(28, value->Int32Value());
6896
6897 // Call the Object's constructor with a String.
6898 value = CompileRun(
6899 "(function() { var o = new obj('tipli'); return o.a; })()");
6900 CHECK(!try_catch.HasCaught());
6901 CHECK(value->IsString());
6902 String::AsciiValue string_value1(value->ToString());
6903 CHECK_EQ("tipli", *string_value1);
6904
6905 Local<Value> args2[] = { v8_str("tipli") };
6906 Local<Value> value_obj2 = instance->CallAsConstructor(1, args2);
6907 CHECK(value_obj2->IsObject());
6908 Local<Object> object2 = Local<Object>::Cast(value_obj2);
6909 value = object2->Get(v8_str("a"));
6910 CHECK(!try_catch.HasCaught());
6911 CHECK(value->IsString());
6912 String::AsciiValue string_value2(value->ToString());
6913 CHECK_EQ("tipli", *string_value2);
6914
6915 // Call the Object's constructor with a Boolean.
6916 value = CompileRun("(function() { var o = new obj(true); return o.a; })()");
6917 CHECK(!try_catch.HasCaught());
6918 CHECK(value->IsBoolean());
6919 CHECK_EQ(true, value->BooleanValue());
6920
6921 Handle<Value> args3[] = { v8::Boolean::New(true) };
6922 Local<Value> value_obj3 = instance->CallAsConstructor(1, args3);
6923 CHECK(value_obj3->IsObject());
6924 Local<Object> object3 = Local<Object>::Cast(value_obj3);
6925 value = object3->Get(v8_str("a"));
6926 CHECK(!try_catch.HasCaught());
6927 CHECK(value->IsBoolean());
6928 CHECK_EQ(true, value->BooleanValue());
6929
6930 // Call the Object's constructor with undefined.
6931 Handle<Value> args4[] = { v8::Undefined() };
6932 Local<Value> value_obj4 = instance->CallAsConstructor(1, args4);
6933 CHECK(value_obj4->IsObject());
6934 Local<Object> object4 = Local<Object>::Cast(value_obj4);
6935 value = object4->Get(v8_str("a"));
6936 CHECK(!try_catch.HasCaught());
6937 CHECK(value->IsUndefined());
6938
6939 // Call the Object's constructor with null.
6940 Handle<Value> args5[] = { v8::Null() };
6941 Local<Value> value_obj5 = instance->CallAsConstructor(1, args5);
6942 CHECK(value_obj5->IsObject());
6943 Local<Object> object5 = Local<Object>::Cast(value_obj5);
6944 value = object5->Get(v8_str("a"));
6945 CHECK(!try_catch.HasCaught());
6946 CHECK(value->IsNull());
6947 }
6948
6949 // Check exception handling when there is no constructor set for the Object.
6950 { Local<ObjectTemplate> instance_template = ObjectTemplate::New();
6951 Local<Object> instance = instance_template->NewInstance();
6952 context->Global()->Set(v8_str("obj2"), instance);
6953 v8::TryCatch try_catch;
6954 Local<Value> value;
6955 CHECK(!try_catch.HasCaught());
6956
6957 value = CompileRun("new obj2(28)");
6958 CHECK(try_catch.HasCaught());
6959 String::AsciiValue exception_value1(try_catch.Exception());
6960 CHECK_EQ("TypeError: object is not a function", *exception_value1);
6961 try_catch.Reset();
6962
6963 Local<Value> args[] = { v8_num(29) };
6964 value = instance->CallAsConstructor(1, args);
6965 CHECK(try_catch.HasCaught());
6966 String::AsciiValue exception_value2(try_catch.Exception());
6967 CHECK_EQ("TypeError: #<Object> is not a function", *exception_value2);
6968 try_catch.Reset();
6969 }
6970
6971 // Check the case when constructor throws exception.
6972 { Local<ObjectTemplate> instance_template = ObjectTemplate::New();
6973 instance_template->SetCallAsFunctionHandler(ThrowValue);
6974 Local<Object> instance = instance_template->NewInstance();
6975 context->Global()->Set(v8_str("obj3"), instance);
6976 v8::TryCatch try_catch;
6977 Local<Value> value;
6978 CHECK(!try_catch.HasCaught());
6979
6980 value = CompileRun("new obj3(22)");
6981 CHECK(try_catch.HasCaught());
6982 String::AsciiValue exception_value1(try_catch.Exception());
6983 CHECK_EQ("22", *exception_value1);
6984 try_catch.Reset();
6985
6986 Local<Value> args[] = { v8_num(23) };
6987 value = instance->CallAsConstructor(1, args);
6988 CHECK(try_catch.HasCaught());
6989 String::AsciiValue exception_value2(try_catch.Exception());
6990 CHECK_EQ("23", *exception_value2);
6991 try_catch.Reset();
6992 }
6993
6994 // Check whether constructor returns with an object or non-object.
6995 { Local<FunctionTemplate> function_template =
6996 FunctionTemplate::New(FakeConstructorCallback);
6997 Local<Function> function = function_template->GetFunction();
6998 Local<Object> instance1 = function;
6999 context->Global()->Set(v8_str("obj4"), instance1);
7000 v8::TryCatch try_catch;
7001 Local<Value> value;
7002 CHECK(!try_catch.HasCaught());
7003
7004 CHECK(instance1->IsObject());
7005 CHECK(instance1->IsFunction());
7006
7007 value = CompileRun("new obj4(28)");
7008 CHECK(!try_catch.HasCaught());
7009 CHECK(value->IsObject());
7010
7011 Local<Value> args1[] = { v8_num(28) };
7012 value = instance1->CallAsConstructor(1, args1);
7013 CHECK(!try_catch.HasCaught());
7014 CHECK(value->IsObject());
7015
7016 Local<ObjectTemplate> instance_template = ObjectTemplate::New();
7017 instance_template->SetCallAsFunctionHandler(FakeConstructorCallback);
7018 Local<Object> instance2 = instance_template->NewInstance();
7019 context->Global()->Set(v8_str("obj5"), instance2);
7020 CHECK(!try_catch.HasCaught());
7021
7022 CHECK(instance2->IsObject());
7023 CHECK(!instance2->IsFunction());
7024
7025 value = CompileRun("new obj5(28)");
7026 CHECK(!try_catch.HasCaught());
7027 CHECK(!value->IsObject());
7028
7029 Local<Value> args2[] = { v8_num(28) };
7030 value = instance2->CallAsConstructor(1, args2);
7031 CHECK(!try_catch.HasCaught());
7032 CHECK(!value->IsObject());
7033 }
7034}
7035
7036
Steve Blocka7e24c12009-10-30 11:49:00 +00007037THREADED_TEST(FunctionDescriptorException) {
7038 v8::HandleScope handle_scope;
7039 LocalContext context;
7040 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
7041 templ->SetClassName(v8_str("Fun"));
7042 Local<Function> cons = templ->GetFunction();
7043 context->Global()->Set(v8_str("Fun"), cons);
7044 Local<Value> value = CompileRun(
7045 "function test() {"
7046 " try {"
7047 " (new Fun()).blah()"
7048 " } catch (e) {"
7049 " var str = String(e);"
7050 " if (str.indexOf('TypeError') == -1) return 1;"
7051 " if (str.indexOf('[object Fun]') != -1) return 2;"
Steve Block1e0659c2011-05-24 12:43:12 +01007052 " if (str.indexOf('#<Fun>') == -1) return 3;"
Steve Blocka7e24c12009-10-30 11:49:00 +00007053 " return 0;"
7054 " }"
7055 " return 4;"
7056 "}"
7057 "test();");
7058 CHECK_EQ(0, value->Int32Value());
7059}
7060
7061
7062THREADED_TEST(EvalAliasedDynamic) {
7063 v8::HandleScope scope;
7064 LocalContext current;
7065
7066 // Tests where aliased eval can only be resolved dynamically.
7067 Local<Script> script =
7068 Script::Compile(v8_str("function f(x) { "
7069 " var foo = 2;"
7070 " with (x) { return eval('foo'); }"
7071 "}"
7072 "foo = 0;"
7073 "result1 = f(new Object());"
7074 "result2 = f(this);"
7075 "var x = new Object();"
7076 "x.eval = function(x) { return 1; };"
7077 "result3 = f(x);"));
7078 script->Run();
7079 CHECK_EQ(2, current->Global()->Get(v8_str("result1"))->Int32Value());
7080 CHECK_EQ(0, current->Global()->Get(v8_str("result2"))->Int32Value());
7081 CHECK_EQ(1, current->Global()->Get(v8_str("result3"))->Int32Value());
7082
7083 v8::TryCatch try_catch;
7084 script =
7085 Script::Compile(v8_str("function f(x) { "
7086 " var bar = 2;"
7087 " with (x) { return eval('bar'); }"
7088 "}"
7089 "f(this)"));
7090 script->Run();
7091 CHECK(try_catch.HasCaught());
7092 try_catch.Reset();
7093}
7094
7095
7096THREADED_TEST(CrossEval) {
7097 v8::HandleScope scope;
7098 LocalContext other;
7099 LocalContext current;
7100
7101 Local<String> token = v8_str("<security token>");
7102 other->SetSecurityToken(token);
7103 current->SetSecurityToken(token);
7104
7105 // Setup reference from current to other.
7106 current->Global()->Set(v8_str("other"), other->Global());
7107
7108 // Check that new variables are introduced in other context.
7109 Local<Script> script =
7110 Script::Compile(v8_str("other.eval('var foo = 1234')"));
7111 script->Run();
7112 Local<Value> foo = other->Global()->Get(v8_str("foo"));
7113 CHECK_EQ(1234, foo->Int32Value());
7114 CHECK(!current->Global()->Has(v8_str("foo")));
7115
7116 // Check that writing to non-existing properties introduces them in
7117 // the other context.
7118 script =
7119 Script::Compile(v8_str("other.eval('na = 1234')"));
7120 script->Run();
7121 CHECK_EQ(1234, other->Global()->Get(v8_str("na"))->Int32Value());
7122 CHECK(!current->Global()->Has(v8_str("na")));
7123
7124 // Check that global variables in current context are not visible in other
7125 // context.
7126 v8::TryCatch try_catch;
7127 script =
7128 Script::Compile(v8_str("var bar = 42; other.eval('bar');"));
7129 Local<Value> result = script->Run();
7130 CHECK(try_catch.HasCaught());
7131 try_catch.Reset();
7132
7133 // Check that local variables in current context are not visible in other
7134 // context.
7135 script =
7136 Script::Compile(v8_str("(function() { "
7137 " var baz = 87;"
7138 " return other.eval('baz');"
7139 "})();"));
7140 result = script->Run();
7141 CHECK(try_catch.HasCaught());
7142 try_catch.Reset();
7143
7144 // Check that global variables in the other environment are visible
7145 // when evaluting code.
7146 other->Global()->Set(v8_str("bis"), v8_num(1234));
7147 script = Script::Compile(v8_str("other.eval('bis')"));
7148 CHECK_EQ(1234, script->Run()->Int32Value());
7149 CHECK(!try_catch.HasCaught());
7150
7151 // Check that the 'this' pointer points to the global object evaluating
7152 // code.
7153 other->Global()->Set(v8_str("t"), other->Global());
7154 script = Script::Compile(v8_str("other.eval('this == t')"));
7155 result = script->Run();
7156 CHECK(result->IsTrue());
7157 CHECK(!try_catch.HasCaught());
7158
7159 // Check that variables introduced in with-statement are not visible in
7160 // other context.
7161 script =
7162 Script::Compile(v8_str("with({x:2}){other.eval('x')}"));
7163 result = script->Run();
7164 CHECK(try_catch.HasCaught());
7165 try_catch.Reset();
7166
7167 // Check that you cannot use 'eval.call' with another object than the
7168 // current global object.
7169 script =
7170 Script::Compile(v8_str("other.y = 1; eval.call(other, 'y')"));
7171 result = script->Run();
7172 CHECK(try_catch.HasCaught());
7173}
7174
7175
7176// Test that calling eval in a context which has been detached from
7177// its global throws an exception. This behavior is consistent with
7178// other JavaScript implementations.
7179THREADED_TEST(EvalInDetachedGlobal) {
7180 v8::HandleScope scope;
7181
7182 v8::Persistent<Context> context0 = Context::New();
7183 v8::Persistent<Context> context1 = Context::New();
7184
7185 // Setup function in context0 that uses eval from context0.
7186 context0->Enter();
7187 v8::Handle<v8::Value> fun =
7188 CompileRun("var x = 42;"
7189 "(function() {"
7190 " var e = eval;"
7191 " return function(s) { return e(s); }"
7192 "})()");
7193 context0->Exit();
7194
7195 // Put the function into context1 and call it before and after
7196 // detaching the global. Before detaching, the call succeeds and
7197 // after detaching and exception is thrown.
7198 context1->Enter();
7199 context1->Global()->Set(v8_str("fun"), fun);
7200 v8::Handle<v8::Value> x_value = CompileRun("fun('x')");
7201 CHECK_EQ(42, x_value->Int32Value());
7202 context0->DetachGlobal();
7203 v8::TryCatch catcher;
7204 x_value = CompileRun("fun('x')");
7205 CHECK(x_value.IsEmpty());
7206 CHECK(catcher.HasCaught());
7207 context1->Exit();
7208
7209 context1.Dispose();
7210 context0.Dispose();
7211}
7212
7213
7214THREADED_TEST(CrossLazyLoad) {
7215 v8::HandleScope scope;
7216 LocalContext other;
7217 LocalContext current;
7218
7219 Local<String> token = v8_str("<security token>");
7220 other->SetSecurityToken(token);
7221 current->SetSecurityToken(token);
7222
7223 // Setup reference from current to other.
7224 current->Global()->Set(v8_str("other"), other->Global());
7225
7226 // Trigger lazy loading in other context.
7227 Local<Script> script =
7228 Script::Compile(v8_str("other.eval('new Date(42)')"));
7229 Local<Value> value = script->Run();
7230 CHECK_EQ(42.0, value->NumberValue());
7231}
7232
7233
7234static v8::Handle<Value> call_as_function(const v8::Arguments& args) {
Andrei Popescu402d9372010-02-26 13:31:12 +00007235 ApiTestFuzzer::Fuzz();
Steve Blocka7e24c12009-10-30 11:49:00 +00007236 if (args.IsConstructCall()) {
7237 if (args[0]->IsInt32()) {
7238 return v8_num(-args[0]->Int32Value());
7239 }
7240 }
7241
7242 return args[0];
7243}
7244
7245
7246// Test that a call handler can be set for objects which will allow
7247// non-function objects created through the API to be called as
7248// functions.
7249THREADED_TEST(CallAsFunction) {
7250 v8::HandleScope scope;
7251 LocalContext context;
7252
Ben Murdoch257744e2011-11-30 15:57:28 +00007253 { Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
7254 Local<ObjectTemplate> instance_template = t->InstanceTemplate();
7255 instance_template->SetCallAsFunctionHandler(call_as_function);
7256 Local<v8::Object> instance = t->GetFunction()->NewInstance();
7257 context->Global()->Set(v8_str("obj"), instance);
7258 v8::TryCatch try_catch;
7259 Local<Value> value;
7260 CHECK(!try_catch.HasCaught());
Steve Blocka7e24c12009-10-30 11:49:00 +00007261
Ben Murdoch257744e2011-11-30 15:57:28 +00007262 value = CompileRun("obj(42)");
7263 CHECK(!try_catch.HasCaught());
7264 CHECK_EQ(42, value->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00007265
Ben Murdoch257744e2011-11-30 15:57:28 +00007266 value = CompileRun("(function(o){return o(49)})(obj)");
7267 CHECK(!try_catch.HasCaught());
7268 CHECK_EQ(49, value->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00007269
Ben Murdoch257744e2011-11-30 15:57:28 +00007270 // test special case of call as function
7271 value = CompileRun("[obj]['0'](45)");
7272 CHECK(!try_catch.HasCaught());
7273 CHECK_EQ(45, value->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00007274
Ben Murdoch257744e2011-11-30 15:57:28 +00007275 value = CompileRun("obj.call = Function.prototype.call;"
7276 "obj.call(null, 87)");
7277 CHECK(!try_catch.HasCaught());
7278 CHECK_EQ(87, value->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00007279
Ben Murdoch257744e2011-11-30 15:57:28 +00007280 // Regression tests for bug #1116356: Calling call through call/apply
7281 // must work for non-function receivers.
7282 const char* apply_99 = "Function.prototype.call.apply(obj, [this, 99])";
7283 value = CompileRun(apply_99);
7284 CHECK(!try_catch.HasCaught());
7285 CHECK_EQ(99, value->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00007286
Ben Murdoch257744e2011-11-30 15:57:28 +00007287 const char* call_17 = "Function.prototype.call.call(obj, this, 17)";
7288 value = CompileRun(call_17);
7289 CHECK(!try_catch.HasCaught());
7290 CHECK_EQ(17, value->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00007291
Ben Murdoch257744e2011-11-30 15:57:28 +00007292 // Check that the call-as-function handler can be called through
7293 // new.
7294 value = CompileRun("new obj(43)");
7295 CHECK(!try_catch.HasCaught());
7296 CHECK_EQ(-43, value->Int32Value());
7297
7298 // Check that the call-as-function handler can be called through
7299 // the API.
7300 v8::Handle<Value> args[] = { v8_num(28) };
7301 value = instance->CallAsFunction(instance, 1, args);
7302 CHECK(!try_catch.HasCaught());
7303 CHECK_EQ(28, value->Int32Value());
7304 }
7305
7306 { Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
7307 Local<ObjectTemplate> instance_template = t->InstanceTemplate();
7308 Local<v8::Object> instance = t->GetFunction()->NewInstance();
7309 context->Global()->Set(v8_str("obj2"), instance);
7310 v8::TryCatch try_catch;
7311 Local<Value> value;
7312 CHECK(!try_catch.HasCaught());
7313
7314 // Call an object without call-as-function handler through the JS
7315 value = CompileRun("obj2(28)");
7316 CHECK(value.IsEmpty());
7317 CHECK(try_catch.HasCaught());
7318 String::AsciiValue exception_value1(try_catch.Exception());
7319 CHECK_EQ("TypeError: Property 'obj2' of object #<Object> is not a function",
7320 *exception_value1);
7321 try_catch.Reset();
7322
7323 // Call an object without call-as-function handler through the API
7324 value = CompileRun("obj2(28)");
7325 v8::Handle<Value> args[] = { v8_num(28) };
7326 value = instance->CallAsFunction(instance, 1, args);
7327 CHECK(value.IsEmpty());
7328 CHECK(try_catch.HasCaught());
7329 String::AsciiValue exception_value2(try_catch.Exception());
7330 CHECK_EQ("TypeError: [object Object] is not a function", *exception_value2);
7331 try_catch.Reset();
7332 }
7333
7334 { Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
7335 Local<ObjectTemplate> instance_template = t->InstanceTemplate();
7336 instance_template->SetCallAsFunctionHandler(ThrowValue);
7337 Local<v8::Object> instance = t->GetFunction()->NewInstance();
7338 context->Global()->Set(v8_str("obj3"), instance);
7339 v8::TryCatch try_catch;
7340 Local<Value> value;
7341 CHECK(!try_catch.HasCaught());
7342
7343 // Catch the exception which is thrown by call-as-function handler
7344 value = CompileRun("obj3(22)");
7345 CHECK(try_catch.HasCaught());
7346 String::AsciiValue exception_value1(try_catch.Exception());
7347 CHECK_EQ("22", *exception_value1);
7348 try_catch.Reset();
7349
7350 v8::Handle<Value> args[] = { v8_num(23) };
7351 value = instance->CallAsFunction(instance, 1, args);
7352 CHECK(try_catch.HasCaught());
7353 String::AsciiValue exception_value2(try_catch.Exception());
7354 CHECK_EQ("23", *exception_value2);
7355 try_catch.Reset();
7356 }
7357}
7358
7359
7360// Check whether a non-function object is callable.
7361THREADED_TEST(CallableObject) {
7362 v8::HandleScope scope;
7363 LocalContext context;
7364
7365 { Local<ObjectTemplate> instance_template = ObjectTemplate::New();
7366 instance_template->SetCallAsFunctionHandler(call_as_function);
7367 Local<Object> instance = instance_template->NewInstance();
7368 v8::TryCatch try_catch;
7369
7370 CHECK(instance->IsCallable());
7371 CHECK(!try_catch.HasCaught());
7372 }
7373
7374 { Local<ObjectTemplate> instance_template = ObjectTemplate::New();
7375 Local<Object> instance = instance_template->NewInstance();
7376 v8::TryCatch try_catch;
7377
7378 CHECK(!instance->IsCallable());
7379 CHECK(!try_catch.HasCaught());
7380 }
7381
7382 { Local<FunctionTemplate> function_template =
7383 FunctionTemplate::New(call_as_function);
7384 Local<Function> function = function_template->GetFunction();
7385 Local<Object> instance = function;
7386 v8::TryCatch try_catch;
7387
7388 CHECK(instance->IsCallable());
7389 CHECK(!try_catch.HasCaught());
7390 }
7391
7392 { Local<FunctionTemplate> function_template = FunctionTemplate::New();
7393 Local<Function> function = function_template->GetFunction();
7394 Local<Object> instance = function;
7395 v8::TryCatch try_catch;
7396
7397 CHECK(instance->IsCallable());
7398 CHECK(!try_catch.HasCaught());
7399 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007400}
7401
7402
7403static int CountHandles() {
7404 return v8::HandleScope::NumberOfHandles();
7405}
7406
7407
7408static int Recurse(int depth, int iterations) {
7409 v8::HandleScope scope;
7410 if (depth == 0) return CountHandles();
7411 for (int i = 0; i < iterations; i++) {
7412 Local<v8::Number> n = v8::Integer::New(42);
7413 }
7414 return Recurse(depth - 1, iterations);
7415}
7416
7417
7418THREADED_TEST(HandleIteration) {
7419 static const int kIterations = 500;
7420 static const int kNesting = 200;
7421 CHECK_EQ(0, CountHandles());
7422 {
7423 v8::HandleScope scope1;
7424 CHECK_EQ(0, CountHandles());
7425 for (int i = 0; i < kIterations; i++) {
7426 Local<v8::Number> n = v8::Integer::New(42);
7427 CHECK_EQ(i + 1, CountHandles());
7428 }
7429
7430 CHECK_EQ(kIterations, CountHandles());
7431 {
7432 v8::HandleScope scope2;
7433 for (int j = 0; j < kIterations; j++) {
7434 Local<v8::Number> n = v8::Integer::New(42);
7435 CHECK_EQ(j + 1 + kIterations, CountHandles());
7436 }
7437 }
7438 CHECK_EQ(kIterations, CountHandles());
7439 }
7440 CHECK_EQ(0, CountHandles());
7441 CHECK_EQ(kNesting * kIterations, Recurse(kNesting, kIterations));
7442}
7443
7444
7445static v8::Handle<Value> InterceptorHasOwnPropertyGetter(
7446 Local<String> name,
7447 const AccessorInfo& info) {
7448 ApiTestFuzzer::Fuzz();
7449 return v8::Handle<Value>();
7450}
7451
7452
7453THREADED_TEST(InterceptorHasOwnProperty) {
7454 v8::HandleScope scope;
7455 LocalContext context;
7456 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7457 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
7458 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetter);
7459 Local<Function> function = fun_templ->GetFunction();
7460 context->Global()->Set(v8_str("constructor"), function);
7461 v8::Handle<Value> value = CompileRun(
7462 "var o = new constructor();"
7463 "o.hasOwnProperty('ostehaps');");
7464 CHECK_EQ(false, value->BooleanValue());
7465 value = CompileRun(
7466 "o.ostehaps = 42;"
7467 "o.hasOwnProperty('ostehaps');");
7468 CHECK_EQ(true, value->BooleanValue());
7469 value = CompileRun(
7470 "var p = new constructor();"
7471 "p.hasOwnProperty('ostehaps');");
7472 CHECK_EQ(false, value->BooleanValue());
7473}
7474
7475
7476static v8::Handle<Value> InterceptorHasOwnPropertyGetterGC(
7477 Local<String> name,
7478 const AccessorInfo& info) {
7479 ApiTestFuzzer::Fuzz();
Steve Block44f0eee2011-05-26 01:26:41 +01007480 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +00007481 return v8::Handle<Value>();
7482}
7483
7484
7485THREADED_TEST(InterceptorHasOwnPropertyCausingGC) {
7486 v8::HandleScope scope;
7487 LocalContext context;
7488 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7489 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
7490 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetterGC);
7491 Local<Function> function = fun_templ->GetFunction();
7492 context->Global()->Set(v8_str("constructor"), function);
7493 // Let's first make some stuff so we can be sure to get a good GC.
7494 CompileRun(
7495 "function makestr(size) {"
7496 " switch (size) {"
7497 " case 1: return 'f';"
7498 " case 2: return 'fo';"
7499 " case 3: return 'foo';"
7500 " }"
7501 " return makestr(size >> 1) + makestr((size + 1) >> 1);"
7502 "}"
7503 "var x = makestr(12345);"
7504 "x = makestr(31415);"
7505 "x = makestr(23456);");
7506 v8::Handle<Value> value = CompileRun(
7507 "var o = new constructor();"
7508 "o.__proto__ = new String(x);"
7509 "o.hasOwnProperty('ostehaps');");
7510 CHECK_EQ(false, value->BooleanValue());
7511}
7512
7513
7514typedef v8::Handle<Value> (*NamedPropertyGetter)(Local<String> property,
7515 const AccessorInfo& info);
7516
7517
7518static void CheckInterceptorLoadIC(NamedPropertyGetter getter,
7519 const char* source,
7520 int expected) {
7521 v8::HandleScope scope;
7522 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -08007523 templ->SetNamedPropertyHandler(getter, 0, 0, 0, 0, v8_str("data"));
Steve Blocka7e24c12009-10-30 11:49:00 +00007524 LocalContext context;
7525 context->Global()->Set(v8_str("o"), templ->NewInstance());
7526 v8::Handle<Value> value = CompileRun(source);
7527 CHECK_EQ(expected, value->Int32Value());
7528}
7529
7530
7531static v8::Handle<Value> InterceptorLoadICGetter(Local<String> name,
7532 const AccessorInfo& info) {
7533 ApiTestFuzzer::Fuzz();
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -08007534 CHECK_EQ(v8_str("data"), info.Data());
7535 CHECK_EQ(v8_str("x"), name);
Steve Blocka7e24c12009-10-30 11:49:00 +00007536 return v8::Integer::New(42);
7537}
7538
7539
7540// This test should hit the load IC for the interceptor case.
7541THREADED_TEST(InterceptorLoadIC) {
7542 CheckInterceptorLoadIC(InterceptorLoadICGetter,
7543 "var result = 0;"
7544 "for (var i = 0; i < 1000; i++) {"
7545 " result = o.x;"
7546 "}",
7547 42);
7548}
7549
7550
7551// Below go several tests which verify that JITing for various
7552// configurations of interceptor and explicit fields works fine
7553// (those cases are special cased to get better performance).
7554
7555static v8::Handle<Value> InterceptorLoadXICGetter(Local<String> name,
7556 const AccessorInfo& info) {
7557 ApiTestFuzzer::Fuzz();
7558 return v8_str("x")->Equals(name)
7559 ? v8::Integer::New(42) : v8::Handle<v8::Value>();
7560}
7561
7562
7563THREADED_TEST(InterceptorLoadICWithFieldOnHolder) {
7564 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
7565 "var result = 0;"
7566 "o.y = 239;"
7567 "for (var i = 0; i < 1000; i++) {"
7568 " result = o.y;"
7569 "}",
7570 239);
7571}
7572
7573
7574THREADED_TEST(InterceptorLoadICWithSubstitutedProto) {
7575 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
7576 "var result = 0;"
7577 "o.__proto__ = { 'y': 239 };"
7578 "for (var i = 0; i < 1000; i++) {"
7579 " result = o.y + o.x;"
7580 "}",
7581 239 + 42);
7582}
7583
7584
7585THREADED_TEST(InterceptorLoadICWithPropertyOnProto) {
7586 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
7587 "var result = 0;"
7588 "o.__proto__.y = 239;"
7589 "for (var i = 0; i < 1000; i++) {"
7590 " result = o.y + o.x;"
7591 "}",
7592 239 + 42);
7593}
7594
7595
7596THREADED_TEST(InterceptorLoadICUndefined) {
7597 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
7598 "var result = 0;"
7599 "for (var i = 0; i < 1000; i++) {"
7600 " result = (o.y == undefined) ? 239 : 42;"
7601 "}",
7602 239);
7603}
7604
7605
7606THREADED_TEST(InterceptorLoadICWithOverride) {
7607 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
7608 "fst = new Object(); fst.__proto__ = o;"
7609 "snd = new Object(); snd.__proto__ = fst;"
7610 "var result1 = 0;"
7611 "for (var i = 0; i < 1000; i++) {"
7612 " result1 = snd.x;"
7613 "}"
7614 "fst.x = 239;"
7615 "var result = 0;"
7616 "for (var i = 0; i < 1000; i++) {"
7617 " result = snd.x;"
7618 "}"
7619 "result + result1",
7620 239 + 42);
7621}
7622
7623
7624// Test the case when we stored field into
7625// a stub, but interceptor produced value on its own.
7626THREADED_TEST(InterceptorLoadICFieldNotNeeded) {
7627 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
7628 "proto = new Object();"
7629 "o.__proto__ = proto;"
7630 "proto.x = 239;"
7631 "for (var i = 0; i < 1000; i++) {"
7632 " o.x;"
7633 // Now it should be ICed and keep a reference to x defined on proto
7634 "}"
7635 "var result = 0;"
7636 "for (var i = 0; i < 1000; i++) {"
7637 " result += o.x;"
7638 "}"
7639 "result;",
7640 42 * 1000);
7641}
7642
7643
7644// Test the case when we stored field into
7645// a stub, but it got invalidated later on.
7646THREADED_TEST(InterceptorLoadICInvalidatedField) {
7647 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
7648 "proto1 = new Object();"
7649 "proto2 = new Object();"
7650 "o.__proto__ = proto1;"
7651 "proto1.__proto__ = proto2;"
7652 "proto2.y = 239;"
7653 "for (var i = 0; i < 1000; i++) {"
7654 " o.y;"
7655 // Now it should be ICed and keep a reference to y defined on proto2
7656 "}"
7657 "proto1.y = 42;"
7658 "var result = 0;"
7659 "for (var i = 0; i < 1000; i++) {"
7660 " result += o.y;"
7661 "}"
7662 "result;",
7663 42 * 1000);
7664}
7665
7666
Steve Block6ded16b2010-05-10 14:33:55 +01007667static int interceptor_load_not_handled_calls = 0;
7668static v8::Handle<Value> InterceptorLoadNotHandled(Local<String> name,
7669 const AccessorInfo& info) {
7670 ++interceptor_load_not_handled_calls;
7671 return v8::Handle<v8::Value>();
7672}
7673
7674
7675// Test how post-interceptor lookups are done in the non-cacheable
7676// case: the interceptor should not be invoked during this lookup.
7677THREADED_TEST(InterceptorLoadICPostInterceptor) {
7678 interceptor_load_not_handled_calls = 0;
7679 CheckInterceptorLoadIC(InterceptorLoadNotHandled,
7680 "receiver = new Object();"
7681 "receiver.__proto__ = o;"
7682 "proto = new Object();"
7683 "/* Make proto a slow-case object. */"
7684 "for (var i = 0; i < 1000; i++) {"
7685 " proto[\"xxxxxxxx\" + i] = [];"
7686 "}"
7687 "proto.x = 17;"
7688 "o.__proto__ = proto;"
7689 "var result = 0;"
7690 "for (var i = 0; i < 1000; i++) {"
7691 " result += receiver.x;"
7692 "}"
7693 "result;",
7694 17 * 1000);
7695 CHECK_EQ(1000, interceptor_load_not_handled_calls);
7696}
7697
7698
Steve Blocka7e24c12009-10-30 11:49:00 +00007699// Test the case when we stored field into
7700// a stub, but it got invalidated later on due to override on
7701// global object which is between interceptor and fields' holders.
7702THREADED_TEST(InterceptorLoadICInvalidatedFieldViaGlobal) {
7703 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
7704 "o.__proto__ = this;" // set a global to be a proto of o.
7705 "this.__proto__.y = 239;"
7706 "for (var i = 0; i < 10; i++) {"
7707 " if (o.y != 239) throw 'oops: ' + o.y;"
7708 // Now it should be ICed and keep a reference to y defined on field_holder.
7709 "}"
7710 "this.y = 42;" // Assign on a global.
7711 "var result = 0;"
7712 "for (var i = 0; i < 10; i++) {"
7713 " result += o.y;"
7714 "}"
7715 "result;",
7716 42 * 10);
7717}
7718
7719
Steve Blocka7e24c12009-10-30 11:49:00 +00007720static void SetOnThis(Local<String> name,
7721 Local<Value> value,
7722 const AccessorInfo& info) {
7723 info.This()->ForceSet(name, value);
7724}
7725
7726
7727THREADED_TEST(InterceptorLoadICWithCallbackOnHolder) {
7728 v8::HandleScope scope;
7729 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7730 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
7731 templ->SetAccessor(v8_str("y"), Return239);
7732 LocalContext context;
7733 context->Global()->Set(v8_str("o"), templ->NewInstance());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01007734
7735 // Check the case when receiver and interceptor's holder
7736 // are the same objects.
Steve Blocka7e24c12009-10-30 11:49:00 +00007737 v8::Handle<Value> value = CompileRun(
7738 "var result = 0;"
7739 "for (var i = 0; i < 7; i++) {"
7740 " result = o.y;"
7741 "}");
7742 CHECK_EQ(239, value->Int32Value());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01007743
7744 // Check the case when interceptor's holder is in proto chain
7745 // of receiver.
7746 value = CompileRun(
7747 "r = { __proto__: o };"
7748 "var result = 0;"
7749 "for (var i = 0; i < 7; i++) {"
7750 " result = r.y;"
7751 "}");
7752 CHECK_EQ(239, value->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00007753}
7754
7755
7756THREADED_TEST(InterceptorLoadICWithCallbackOnProto) {
7757 v8::HandleScope scope;
7758 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7759 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
7760 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
7761 templ_p->SetAccessor(v8_str("y"), Return239);
7762
7763 LocalContext context;
7764 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
7765 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
7766
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01007767 // Check the case when receiver and interceptor's holder
7768 // are the same objects.
Steve Blocka7e24c12009-10-30 11:49:00 +00007769 v8::Handle<Value> value = CompileRun(
7770 "o.__proto__ = p;"
7771 "var result = 0;"
7772 "for (var i = 0; i < 7; i++) {"
7773 " result = o.x + o.y;"
7774 "}");
7775 CHECK_EQ(239 + 42, value->Int32Value());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01007776
7777 // Check the case when interceptor's holder is in proto chain
7778 // of receiver.
7779 value = CompileRun(
7780 "r = { __proto__: o };"
7781 "var result = 0;"
7782 "for (var i = 0; i < 7; i++) {"
7783 " result = r.x + r.y;"
7784 "}");
7785 CHECK_EQ(239 + 42, value->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00007786}
7787
7788
7789THREADED_TEST(InterceptorLoadICForCallbackWithOverride) {
7790 v8::HandleScope scope;
7791 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7792 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
7793 templ->SetAccessor(v8_str("y"), Return239);
7794
7795 LocalContext context;
7796 context->Global()->Set(v8_str("o"), templ->NewInstance());
7797
7798 v8::Handle<Value> value = CompileRun(
7799 "fst = new Object(); fst.__proto__ = o;"
7800 "snd = new Object(); snd.__proto__ = fst;"
7801 "var result1 = 0;"
7802 "for (var i = 0; i < 7; i++) {"
7803 " result1 = snd.x;"
7804 "}"
7805 "fst.x = 239;"
7806 "var result = 0;"
7807 "for (var i = 0; i < 7; i++) {"
7808 " result = snd.x;"
7809 "}"
7810 "result + result1");
7811 CHECK_EQ(239 + 42, value->Int32Value());
7812}
7813
7814
7815// Test the case when we stored callback into
7816// a stub, but interceptor produced value on its own.
7817THREADED_TEST(InterceptorLoadICCallbackNotNeeded) {
7818 v8::HandleScope scope;
7819 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7820 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
7821 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
7822 templ_p->SetAccessor(v8_str("y"), Return239);
7823
7824 LocalContext context;
7825 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
7826 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
7827
7828 v8::Handle<Value> value = CompileRun(
7829 "o.__proto__ = p;"
7830 "for (var i = 0; i < 7; i++) {"
7831 " o.x;"
7832 // Now it should be ICed and keep a reference to x defined on p
7833 "}"
7834 "var result = 0;"
7835 "for (var i = 0; i < 7; i++) {"
7836 " result += o.x;"
7837 "}"
7838 "result");
7839 CHECK_EQ(42 * 7, value->Int32Value());
7840}
7841
7842
7843// Test the case when we stored callback into
7844// a stub, but it got invalidated later on.
7845THREADED_TEST(InterceptorLoadICInvalidatedCallback) {
7846 v8::HandleScope scope;
7847 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7848 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
7849 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
7850 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis);
7851
7852 LocalContext context;
7853 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
7854 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
7855
7856 v8::Handle<Value> value = CompileRun(
7857 "inbetween = new Object();"
7858 "o.__proto__ = inbetween;"
7859 "inbetween.__proto__ = p;"
7860 "for (var i = 0; i < 10; i++) {"
7861 " o.y;"
7862 // Now it should be ICed and keep a reference to y defined on p
7863 "}"
7864 "inbetween.y = 42;"
7865 "var result = 0;"
7866 "for (var i = 0; i < 10; i++) {"
7867 " result += o.y;"
7868 "}"
7869 "result");
7870 CHECK_EQ(42 * 10, value->Int32Value());
7871}
7872
7873
7874// Test the case when we stored callback into
7875// a stub, but it got invalidated later on due to override on
7876// global object which is between interceptor and callbacks' holders.
7877THREADED_TEST(InterceptorLoadICInvalidatedCallbackViaGlobal) {
7878 v8::HandleScope scope;
7879 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7880 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
7881 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
7882 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis);
7883
7884 LocalContext context;
7885 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
7886 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
7887
7888 v8::Handle<Value> value = CompileRun(
7889 "o.__proto__ = this;"
7890 "this.__proto__ = p;"
7891 "for (var i = 0; i < 10; i++) {"
7892 " if (o.y != 239) throw 'oops: ' + o.y;"
7893 // Now it should be ICed and keep a reference to y defined on p
7894 "}"
7895 "this.y = 42;"
7896 "var result = 0;"
7897 "for (var i = 0; i < 10; i++) {"
7898 " result += o.y;"
7899 "}"
7900 "result");
7901 CHECK_EQ(42 * 10, value->Int32Value());
7902}
7903
7904
7905static v8::Handle<Value> InterceptorLoadICGetter0(Local<String> name,
7906 const AccessorInfo& info) {
7907 ApiTestFuzzer::Fuzz();
7908 CHECK(v8_str("x")->Equals(name));
7909 return v8::Integer::New(0);
7910}
7911
7912
7913THREADED_TEST(InterceptorReturningZero) {
7914 CheckInterceptorLoadIC(InterceptorLoadICGetter0,
7915 "o.x == undefined ? 1 : 0",
7916 0);
7917}
7918
7919
7920static v8::Handle<Value> InterceptorStoreICSetter(
7921 Local<String> key, Local<Value> value, const AccessorInfo&) {
7922 CHECK(v8_str("x")->Equals(key));
7923 CHECK_EQ(42, value->Int32Value());
7924 return value;
7925}
7926
7927
7928// This test should hit the store IC for the interceptor case.
7929THREADED_TEST(InterceptorStoreIC) {
7930 v8::HandleScope scope;
7931 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7932 templ->SetNamedPropertyHandler(InterceptorLoadICGetter,
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -08007933 InterceptorStoreICSetter,
7934 0, 0, 0, v8_str("data"));
Steve Blocka7e24c12009-10-30 11:49:00 +00007935 LocalContext context;
7936 context->Global()->Set(v8_str("o"), templ->NewInstance());
7937 v8::Handle<Value> value = CompileRun(
7938 "for (var i = 0; i < 1000; i++) {"
7939 " o.x = 42;"
7940 "}");
7941}
7942
7943
7944THREADED_TEST(InterceptorStoreICWithNoSetter) {
7945 v8::HandleScope scope;
7946 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7947 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
7948 LocalContext context;
7949 context->Global()->Set(v8_str("o"), templ->NewInstance());
7950 v8::Handle<Value> value = CompileRun(
7951 "for (var i = 0; i < 1000; i++) {"
7952 " o.y = 239;"
7953 "}"
7954 "42 + o.y");
7955 CHECK_EQ(239 + 42, value->Int32Value());
7956}
7957
7958
7959
7960
7961v8::Handle<Value> call_ic_function;
7962v8::Handle<Value> call_ic_function2;
7963v8::Handle<Value> call_ic_function3;
7964
7965static v8::Handle<Value> InterceptorCallICGetter(Local<String> name,
7966 const AccessorInfo& info) {
7967 ApiTestFuzzer::Fuzz();
7968 CHECK(v8_str("x")->Equals(name));
7969 return call_ic_function;
7970}
7971
7972
7973// This test should hit the call IC for the interceptor case.
7974THREADED_TEST(InterceptorCallIC) {
7975 v8::HandleScope scope;
7976 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7977 templ->SetNamedPropertyHandler(InterceptorCallICGetter);
7978 LocalContext context;
7979 context->Global()->Set(v8_str("o"), templ->NewInstance());
7980 call_ic_function =
7981 v8_compile("function f(x) { return x + 1; }; f")->Run();
7982 v8::Handle<Value> value = CompileRun(
7983 "var result = 0;"
7984 "for (var i = 0; i < 1000; i++) {"
7985 " result = o.x(41);"
7986 "}");
7987 CHECK_EQ(42, value->Int32Value());
7988}
7989
7990
7991// This test checks that if interceptor doesn't provide
7992// a value, we can fetch regular value.
7993THREADED_TEST(InterceptorCallICSeesOthers) {
7994 v8::HandleScope scope;
7995 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7996 templ->SetNamedPropertyHandler(NoBlockGetterX);
7997 LocalContext context;
7998 context->Global()->Set(v8_str("o"), templ->NewInstance());
7999 v8::Handle<Value> value = CompileRun(
8000 "o.x = function f(x) { return x + 1; };"
8001 "var result = 0;"
8002 "for (var i = 0; i < 7; i++) {"
8003 " result = o.x(41);"
8004 "}");
8005 CHECK_EQ(42, value->Int32Value());
8006}
8007
8008
8009static v8::Handle<Value> call_ic_function4;
8010static v8::Handle<Value> InterceptorCallICGetter4(Local<String> name,
8011 const AccessorInfo& info) {
8012 ApiTestFuzzer::Fuzz();
8013 CHECK(v8_str("x")->Equals(name));
8014 return call_ic_function4;
8015}
8016
8017
8018// This test checks that if interceptor provides a function,
8019// even if we cached shadowed variant, interceptor's function
8020// is invoked
8021THREADED_TEST(InterceptorCallICCacheableNotNeeded) {
8022 v8::HandleScope scope;
8023 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
8024 templ->SetNamedPropertyHandler(InterceptorCallICGetter4);
8025 LocalContext context;
8026 context->Global()->Set(v8_str("o"), templ->NewInstance());
8027 call_ic_function4 =
8028 v8_compile("function f(x) { return x - 1; }; f")->Run();
8029 v8::Handle<Value> value = CompileRun(
8030 "o.__proto__.x = function(x) { return x + 1; };"
8031 "var result = 0;"
8032 "for (var i = 0; i < 1000; i++) {"
8033 " result = o.x(42);"
8034 "}");
8035 CHECK_EQ(41, value->Int32Value());
8036}
8037
8038
8039// Test the case when we stored cacheable lookup into
8040// a stub, but it got invalidated later on
8041THREADED_TEST(InterceptorCallICInvalidatedCacheable) {
8042 v8::HandleScope scope;
8043 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
8044 templ->SetNamedPropertyHandler(NoBlockGetterX);
8045 LocalContext context;
8046 context->Global()->Set(v8_str("o"), templ->NewInstance());
8047 v8::Handle<Value> value = CompileRun(
8048 "proto1 = new Object();"
8049 "proto2 = new Object();"
8050 "o.__proto__ = proto1;"
8051 "proto1.__proto__ = proto2;"
8052 "proto2.y = function(x) { return x + 1; };"
8053 // Invoke it many times to compile a stub
8054 "for (var i = 0; i < 7; i++) {"
8055 " o.y(42);"
8056 "}"
8057 "proto1.y = function(x) { return x - 1; };"
8058 "var result = 0;"
8059 "for (var i = 0; i < 7; i++) {"
8060 " result += o.y(42);"
8061 "}");
8062 CHECK_EQ(41 * 7, value->Int32Value());
8063}
8064
8065
8066static v8::Handle<Value> call_ic_function5;
8067static v8::Handle<Value> InterceptorCallICGetter5(Local<String> name,
8068 const AccessorInfo& info) {
8069 ApiTestFuzzer::Fuzz();
8070 if (v8_str("x")->Equals(name))
8071 return call_ic_function5;
8072 else
8073 return Local<Value>();
8074}
8075
8076
8077// This test checks that if interceptor doesn't provide a function,
8078// cached constant function is used
8079THREADED_TEST(InterceptorCallICConstantFunctionUsed) {
8080 v8::HandleScope scope;
8081 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
8082 templ->SetNamedPropertyHandler(NoBlockGetterX);
8083 LocalContext context;
8084 context->Global()->Set(v8_str("o"), templ->NewInstance());
8085 v8::Handle<Value> value = CompileRun(
8086 "function inc(x) { return x + 1; };"
8087 "inc(1);"
8088 "o.x = inc;"
8089 "var result = 0;"
8090 "for (var i = 0; i < 1000; i++) {"
8091 " result = o.x(42);"
8092 "}");
8093 CHECK_EQ(43, value->Int32Value());
8094}
8095
8096
8097// This test checks that if interceptor provides a function,
8098// even if we cached constant function, interceptor's function
8099// is invoked
8100THREADED_TEST(InterceptorCallICConstantFunctionNotNeeded) {
8101 v8::HandleScope scope;
8102 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
8103 templ->SetNamedPropertyHandler(InterceptorCallICGetter5);
8104 LocalContext context;
8105 context->Global()->Set(v8_str("o"), templ->NewInstance());
8106 call_ic_function5 =
8107 v8_compile("function f(x) { return x - 1; }; f")->Run();
8108 v8::Handle<Value> value = CompileRun(
8109 "function inc(x) { return x + 1; };"
8110 "inc(1);"
8111 "o.x = inc;"
8112 "var result = 0;"
8113 "for (var i = 0; i < 1000; i++) {"
8114 " result = o.x(42);"
8115 "}");
8116 CHECK_EQ(41, value->Int32Value());
8117}
8118
8119
8120// Test the case when we stored constant function into
8121// a stub, but it got invalidated later on
8122THREADED_TEST(InterceptorCallICInvalidatedConstantFunction) {
8123 v8::HandleScope scope;
8124 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
8125 templ->SetNamedPropertyHandler(NoBlockGetterX);
8126 LocalContext context;
8127 context->Global()->Set(v8_str("o"), templ->NewInstance());
8128 v8::Handle<Value> value = CompileRun(
8129 "function inc(x) { return x + 1; };"
8130 "inc(1);"
8131 "proto1 = new Object();"
8132 "proto2 = new Object();"
8133 "o.__proto__ = proto1;"
8134 "proto1.__proto__ = proto2;"
8135 "proto2.y = inc;"
8136 // Invoke it many times to compile a stub
8137 "for (var i = 0; i < 7; i++) {"
8138 " o.y(42);"
8139 "}"
8140 "proto1.y = function(x) { return x - 1; };"
8141 "var result = 0;"
8142 "for (var i = 0; i < 7; i++) {"
8143 " result += o.y(42);"
8144 "}");
8145 CHECK_EQ(41 * 7, value->Int32Value());
8146}
8147
8148
8149// Test the case when we stored constant function into
8150// a stub, but it got invalidated later on due to override on
8151// global object which is between interceptor and constant function' holders.
8152THREADED_TEST(InterceptorCallICInvalidatedConstantFunctionViaGlobal) {
8153 v8::HandleScope scope;
8154 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
8155 templ->SetNamedPropertyHandler(NoBlockGetterX);
8156 LocalContext context;
8157 context->Global()->Set(v8_str("o"), templ->NewInstance());
8158 v8::Handle<Value> value = CompileRun(
8159 "function inc(x) { return x + 1; };"
8160 "inc(1);"
8161 "o.__proto__ = this;"
8162 "this.__proto__.y = inc;"
8163 // Invoke it many times to compile a stub
8164 "for (var i = 0; i < 7; i++) {"
8165 " if (o.y(42) != 43) throw 'oops: ' + o.y(42);"
8166 "}"
8167 "this.y = function(x) { return x - 1; };"
8168 "var result = 0;"
8169 "for (var i = 0; i < 7; i++) {"
8170 " result += o.y(42);"
8171 "}");
8172 CHECK_EQ(41 * 7, value->Int32Value());
8173}
8174
8175
Leon Clarke4515c472010-02-03 11:58:03 +00008176// Test the case when actual function to call sits on global object.
8177THREADED_TEST(InterceptorCallICCachedFromGlobal) {
8178 v8::HandleScope scope;
8179 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
8180 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
8181
8182 LocalContext context;
8183 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
8184
8185 v8::Handle<Value> value = CompileRun(
8186 "try {"
8187 " o.__proto__ = this;"
8188 " for (var i = 0; i < 10; i++) {"
8189 " var v = o.parseFloat('239');"
8190 " if (v != 239) throw v;"
8191 // Now it should be ICed and keep a reference to parseFloat.
8192 " }"
8193 " var result = 0;"
8194 " for (var i = 0; i < 10; i++) {"
8195 " result += o.parseFloat('239');"
8196 " }"
8197 " result"
8198 "} catch(e) {"
8199 " e"
8200 "};");
8201 CHECK_EQ(239 * 10, value->Int32Value());
8202}
8203
Andrei Popescu402d9372010-02-26 13:31:12 +00008204static v8::Handle<Value> InterceptorCallICFastApi(Local<String> name,
8205 const AccessorInfo& info) {
8206 ApiTestFuzzer::Fuzz();
8207 int* call_count = reinterpret_cast<int*>(v8::External::Unwrap(info.Data()));
8208 ++(*call_count);
8209 if ((*call_count) % 20 == 0) {
Steve Block44f0eee2011-05-26 01:26:41 +01008210 HEAP->CollectAllGarbage(true);
Andrei Popescu402d9372010-02-26 13:31:12 +00008211 }
8212 return v8::Handle<Value>();
8213}
8214
8215static v8::Handle<Value> FastApiCallback_TrivialSignature(
8216 const v8::Arguments& args) {
8217 ApiTestFuzzer::Fuzz();
8218 CHECK_EQ(args.This(), args.Holder());
8219 CHECK(args.Data()->Equals(v8_str("method_data")));
8220 return v8::Integer::New(args[0]->Int32Value() + 1);
8221}
8222
8223static v8::Handle<Value> FastApiCallback_SimpleSignature(
8224 const v8::Arguments& args) {
8225 ApiTestFuzzer::Fuzz();
8226 CHECK_EQ(args.This()->GetPrototype(), args.Holder());
8227 CHECK(args.Data()->Equals(v8_str("method_data")));
8228 // Note, we're using HasRealNamedProperty instead of Has to avoid
8229 // invoking the interceptor again.
8230 CHECK(args.Holder()->HasRealNamedProperty(v8_str("foo")));
8231 return v8::Integer::New(args[0]->Int32Value() + 1);
8232}
8233
8234// Helper to maximize the odds of object moving.
8235static void GenerateSomeGarbage() {
8236 CompileRun(
8237 "var garbage;"
8238 "for (var i = 0; i < 1000; i++) {"
8239 " garbage = [1/i, \"garbage\" + i, garbage, {foo: garbage}];"
8240 "}"
8241 "garbage = undefined;");
8242}
8243
Ben Murdoche0cee9b2011-05-25 10:26:03 +01008244
Steve Block1e0659c2011-05-24 12:43:12 +01008245v8::Handle<v8::Value> DirectApiCallback(const v8::Arguments& args) {
8246 static int count = 0;
8247 if (count++ % 3 == 0) {
Steve Block44f0eee2011-05-26 01:26:41 +01008248 HEAP-> CollectAllGarbage(true); // This should move the stub
Steve Block1e0659c2011-05-24 12:43:12 +01008249 GenerateSomeGarbage(); // This should ensure the old stub memory is flushed
8250 }
8251 return v8::Handle<v8::Value>();
8252}
8253
8254
8255THREADED_TEST(CallICFastApi_DirectCall_GCMoveStub) {
8256 v8::HandleScope scope;
8257 LocalContext context;
8258 v8::Handle<v8::ObjectTemplate> nativeobject_templ = v8::ObjectTemplate::New();
8259 nativeobject_templ->Set("callback",
8260 v8::FunctionTemplate::New(DirectApiCallback));
8261 v8::Local<v8::Object> nativeobject_obj = nativeobject_templ->NewInstance();
8262 context->Global()->Set(v8_str("nativeobject"), nativeobject_obj);
8263 // call the api function multiple times to ensure direct call stub creation.
8264 CompileRun(
8265 "function f() {"
8266 " for (var i = 1; i <= 30; i++) {"
8267 " nativeobject.callback();"
8268 " }"
8269 "}"
8270 "f();");
8271}
8272
8273
8274v8::Handle<v8::Value> ThrowingDirectApiCallback(const v8::Arguments& args) {
8275 return v8::ThrowException(v8_str("g"));
8276}
8277
8278
8279THREADED_TEST(CallICFastApi_DirectCall_Throw) {
8280 v8::HandleScope scope;
8281 LocalContext context;
8282 v8::Handle<v8::ObjectTemplate> nativeobject_templ = v8::ObjectTemplate::New();
8283 nativeobject_templ->Set("callback",
8284 v8::FunctionTemplate::New(ThrowingDirectApiCallback));
8285 v8::Local<v8::Object> nativeobject_obj = nativeobject_templ->NewInstance();
8286 context->Global()->Set(v8_str("nativeobject"), nativeobject_obj);
8287 // call the api function multiple times to ensure direct call stub creation.
8288 v8::Handle<Value> result = CompileRun(
8289 "var result = '';"
8290 "function f() {"
8291 " for (var i = 1; i <= 5; i++) {"
8292 " try { nativeobject.callback(); } catch (e) { result += e; }"
8293 " }"
8294 "}"
8295 "f(); result;");
8296 CHECK_EQ(v8_str("ggggg"), result);
8297}
8298
8299
Ben Murdoche0cee9b2011-05-25 10:26:03 +01008300v8::Handle<v8::Value> DirectGetterCallback(Local<String> name,
8301 const v8::AccessorInfo& info) {
8302 if (++p_getter_count % 3 == 0) {
Steve Block44f0eee2011-05-26 01:26:41 +01008303 HEAP->CollectAllGarbage(true);
Ben Murdoche0cee9b2011-05-25 10:26:03 +01008304 GenerateSomeGarbage();
8305 }
8306 return v8::Handle<v8::Value>();
8307}
8308
8309
8310THREADED_TEST(LoadICFastApi_DirectCall_GCMoveStub) {
8311 v8::HandleScope scope;
8312 LocalContext context;
8313 v8::Handle<v8::ObjectTemplate> obj = v8::ObjectTemplate::New();
8314 obj->SetAccessor(v8_str("p1"), DirectGetterCallback);
8315 context->Global()->Set(v8_str("o1"), obj->NewInstance());
8316 p_getter_count = 0;
8317 CompileRun(
8318 "function f() {"
8319 " for (var i = 0; i < 30; i++) o1.p1;"
8320 "}"
8321 "f();");
8322 CHECK_EQ(30, p_getter_count);
8323}
8324
8325
8326v8::Handle<v8::Value> ThrowingDirectGetterCallback(
8327 Local<String> name, const v8::AccessorInfo& info) {
8328 return v8::ThrowException(v8_str("g"));
8329}
8330
8331
8332THREADED_TEST(LoadICFastApi_DirectCall_Throw) {
8333 v8::HandleScope scope;
8334 LocalContext context;
8335 v8::Handle<v8::ObjectTemplate> obj = v8::ObjectTemplate::New();
8336 obj->SetAccessor(v8_str("p1"), ThrowingDirectGetterCallback);
8337 context->Global()->Set(v8_str("o1"), obj->NewInstance());
8338 v8::Handle<Value> result = CompileRun(
8339 "var result = '';"
8340 "for (var i = 0; i < 5; i++) {"
8341 " try { o1.p1; } catch (e) { result += e; }"
8342 "}"
8343 "result;");
8344 CHECK_EQ(v8_str("ggggg"), result);
8345}
8346
8347
Andrei Popescu402d9372010-02-26 13:31:12 +00008348THREADED_TEST(InterceptorCallICFastApi_TrivialSignature) {
8349 int interceptor_call_count = 0;
8350 v8::HandleScope scope;
8351 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
8352 v8::Handle<v8::FunctionTemplate> method_templ =
8353 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature,
8354 v8_str("method_data"),
8355 v8::Handle<v8::Signature>());
8356 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
8357 proto_templ->Set(v8_str("method"), method_templ);
8358 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
8359 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
8360 NULL, NULL, NULL, NULL,
8361 v8::External::Wrap(&interceptor_call_count));
8362 LocalContext context;
8363 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
8364 GenerateSomeGarbage();
8365 context->Global()->Set(v8_str("o"), fun->NewInstance());
8366 v8::Handle<Value> value = CompileRun(
8367 "var result = 0;"
8368 "for (var i = 0; i < 100; i++) {"
8369 " result = o.method(41);"
8370 "}");
8371 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
8372 CHECK_EQ(100, interceptor_call_count);
8373}
8374
8375THREADED_TEST(InterceptorCallICFastApi_SimpleSignature) {
8376 int interceptor_call_count = 0;
8377 v8::HandleScope scope;
8378 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
8379 v8::Handle<v8::FunctionTemplate> method_templ =
8380 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
8381 v8_str("method_data"),
8382 v8::Signature::New(fun_templ));
8383 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
8384 proto_templ->Set(v8_str("method"), method_templ);
8385 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
8386 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
8387 NULL, NULL, NULL, NULL,
8388 v8::External::Wrap(&interceptor_call_count));
8389 LocalContext context;
8390 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
8391 GenerateSomeGarbage();
8392 context->Global()->Set(v8_str("o"), fun->NewInstance());
8393 v8::Handle<Value> value = CompileRun(
8394 "o.foo = 17;"
8395 "var receiver = {};"
8396 "receiver.__proto__ = o;"
8397 "var result = 0;"
8398 "for (var i = 0; i < 100; i++) {"
8399 " result = receiver.method(41);"
8400 "}");
8401 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
8402 CHECK_EQ(100, interceptor_call_count);
8403}
8404
8405THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss1) {
8406 int interceptor_call_count = 0;
8407 v8::HandleScope scope;
8408 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
8409 v8::Handle<v8::FunctionTemplate> method_templ =
8410 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
8411 v8_str("method_data"),
8412 v8::Signature::New(fun_templ));
8413 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
8414 proto_templ->Set(v8_str("method"), method_templ);
8415 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
8416 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
8417 NULL, NULL, NULL, NULL,
8418 v8::External::Wrap(&interceptor_call_count));
8419 LocalContext context;
8420 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
8421 GenerateSomeGarbage();
8422 context->Global()->Set(v8_str("o"), fun->NewInstance());
8423 v8::Handle<Value> value = CompileRun(
8424 "o.foo = 17;"
8425 "var receiver = {};"
8426 "receiver.__proto__ = o;"
8427 "var result = 0;"
8428 "var saved_result = 0;"
8429 "for (var i = 0; i < 100; i++) {"
8430 " result = receiver.method(41);"
8431 " if (i == 50) {"
8432 " saved_result = result;"
8433 " receiver = {method: function(x) { return x - 1 }};"
8434 " }"
8435 "}");
8436 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
8437 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
8438 CHECK_GE(interceptor_call_count, 50);
8439}
8440
8441THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss2) {
8442 int interceptor_call_count = 0;
8443 v8::HandleScope scope;
8444 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
8445 v8::Handle<v8::FunctionTemplate> method_templ =
8446 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
8447 v8_str("method_data"),
8448 v8::Signature::New(fun_templ));
8449 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
8450 proto_templ->Set(v8_str("method"), method_templ);
8451 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
8452 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
8453 NULL, NULL, NULL, NULL,
8454 v8::External::Wrap(&interceptor_call_count));
8455 LocalContext context;
8456 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
8457 GenerateSomeGarbage();
8458 context->Global()->Set(v8_str("o"), fun->NewInstance());
8459 v8::Handle<Value> value = CompileRun(
8460 "o.foo = 17;"
8461 "var receiver = {};"
8462 "receiver.__proto__ = o;"
8463 "var result = 0;"
8464 "var saved_result = 0;"
8465 "for (var i = 0; i < 100; i++) {"
8466 " result = receiver.method(41);"
8467 " if (i == 50) {"
8468 " saved_result = result;"
8469 " o.method = function(x) { return x - 1 };"
8470 " }"
8471 "}");
8472 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
8473 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
8474 CHECK_GE(interceptor_call_count, 50);
8475}
8476
Steve Block6ded16b2010-05-10 14:33:55 +01008477THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss3) {
8478 int interceptor_call_count = 0;
8479 v8::HandleScope scope;
8480 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
8481 v8::Handle<v8::FunctionTemplate> method_templ =
8482 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
8483 v8_str("method_data"),
8484 v8::Signature::New(fun_templ));
8485 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
8486 proto_templ->Set(v8_str("method"), method_templ);
8487 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
8488 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
8489 NULL, NULL, NULL, NULL,
8490 v8::External::Wrap(&interceptor_call_count));
8491 LocalContext context;
8492 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
8493 GenerateSomeGarbage();
8494 context->Global()->Set(v8_str("o"), fun->NewInstance());
8495 v8::TryCatch try_catch;
8496 v8::Handle<Value> value = CompileRun(
8497 "o.foo = 17;"
8498 "var receiver = {};"
8499 "receiver.__proto__ = o;"
8500 "var result = 0;"
8501 "var saved_result = 0;"
8502 "for (var i = 0; i < 100; i++) {"
8503 " result = receiver.method(41);"
8504 " if (i == 50) {"
8505 " saved_result = result;"
8506 " receiver = 333;"
8507 " }"
8508 "}");
8509 CHECK(try_catch.HasCaught());
8510 CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"),
8511 try_catch.Exception()->ToString());
8512 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
8513 CHECK_GE(interceptor_call_count, 50);
8514}
8515
Andrei Popescu402d9372010-02-26 13:31:12 +00008516THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_TypeError) {
8517 int interceptor_call_count = 0;
8518 v8::HandleScope scope;
8519 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
8520 v8::Handle<v8::FunctionTemplate> method_templ =
8521 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
8522 v8_str("method_data"),
8523 v8::Signature::New(fun_templ));
8524 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
8525 proto_templ->Set(v8_str("method"), method_templ);
8526 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
8527 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
8528 NULL, NULL, NULL, NULL,
8529 v8::External::Wrap(&interceptor_call_count));
8530 LocalContext context;
8531 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
8532 GenerateSomeGarbage();
8533 context->Global()->Set(v8_str("o"), fun->NewInstance());
8534 v8::TryCatch try_catch;
8535 v8::Handle<Value> value = CompileRun(
8536 "o.foo = 17;"
8537 "var receiver = {};"
8538 "receiver.__proto__ = o;"
8539 "var result = 0;"
8540 "var saved_result = 0;"
8541 "for (var i = 0; i < 100; i++) {"
8542 " result = receiver.method(41);"
8543 " if (i == 50) {"
8544 " saved_result = result;"
8545 " receiver = {method: receiver.method};"
8546 " }"
8547 "}");
8548 CHECK(try_catch.HasCaught());
8549 CHECK_EQ(v8_str("TypeError: Illegal invocation"),
8550 try_catch.Exception()->ToString());
8551 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
8552 CHECK_GE(interceptor_call_count, 50);
8553}
8554
8555THREADED_TEST(CallICFastApi_TrivialSignature) {
8556 v8::HandleScope scope;
8557 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
8558 v8::Handle<v8::FunctionTemplate> method_templ =
8559 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature,
8560 v8_str("method_data"),
8561 v8::Handle<v8::Signature>());
8562 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
8563 proto_templ->Set(v8_str("method"), method_templ);
8564 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
8565 LocalContext context;
8566 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
8567 GenerateSomeGarbage();
8568 context->Global()->Set(v8_str("o"), fun->NewInstance());
8569 v8::Handle<Value> value = CompileRun(
8570 "var result = 0;"
8571 "for (var i = 0; i < 100; i++) {"
8572 " result = o.method(41);"
8573 "}");
8574
8575 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
8576}
8577
8578THREADED_TEST(CallICFastApi_SimpleSignature) {
8579 v8::HandleScope scope;
8580 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
8581 v8::Handle<v8::FunctionTemplate> method_templ =
8582 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
8583 v8_str("method_data"),
8584 v8::Signature::New(fun_templ));
8585 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
8586 proto_templ->Set(v8_str("method"), method_templ);
8587 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
8588 LocalContext context;
8589 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
8590 GenerateSomeGarbage();
8591 context->Global()->Set(v8_str("o"), fun->NewInstance());
8592 v8::Handle<Value> value = CompileRun(
8593 "o.foo = 17;"
8594 "var receiver = {};"
8595 "receiver.__proto__ = o;"
8596 "var result = 0;"
8597 "for (var i = 0; i < 100; i++) {"
8598 " result = receiver.method(41);"
8599 "}");
8600
8601 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
8602}
8603
Steve Block6ded16b2010-05-10 14:33:55 +01008604THREADED_TEST(CallICFastApi_SimpleSignature_Miss1) {
Andrei Popescu402d9372010-02-26 13:31:12 +00008605 v8::HandleScope scope;
8606 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
8607 v8::Handle<v8::FunctionTemplate> method_templ =
8608 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
8609 v8_str("method_data"),
8610 v8::Signature::New(fun_templ));
8611 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
8612 proto_templ->Set(v8_str("method"), method_templ);
8613 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
8614 LocalContext context;
8615 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
8616 GenerateSomeGarbage();
8617 context->Global()->Set(v8_str("o"), fun->NewInstance());
8618 v8::Handle<Value> value = CompileRun(
8619 "o.foo = 17;"
8620 "var receiver = {};"
8621 "receiver.__proto__ = o;"
8622 "var result = 0;"
8623 "var saved_result = 0;"
8624 "for (var i = 0; i < 100; i++) {"
8625 " result = receiver.method(41);"
8626 " if (i == 50) {"
8627 " saved_result = result;"
8628 " receiver = {method: function(x) { return x - 1 }};"
8629 " }"
8630 "}");
8631 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
8632 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
8633}
8634
Steve Block6ded16b2010-05-10 14:33:55 +01008635THREADED_TEST(CallICFastApi_SimpleSignature_Miss2) {
8636 v8::HandleScope scope;
8637 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
8638 v8::Handle<v8::FunctionTemplate> method_templ =
8639 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
8640 v8_str("method_data"),
8641 v8::Signature::New(fun_templ));
8642 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
8643 proto_templ->Set(v8_str("method"), method_templ);
8644 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
8645 LocalContext context;
8646 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
8647 GenerateSomeGarbage();
8648 context->Global()->Set(v8_str("o"), fun->NewInstance());
8649 v8::TryCatch try_catch;
8650 v8::Handle<Value> value = CompileRun(
8651 "o.foo = 17;"
8652 "var receiver = {};"
8653 "receiver.__proto__ = o;"
8654 "var result = 0;"
8655 "var saved_result = 0;"
8656 "for (var i = 0; i < 100; i++) {"
8657 " result = receiver.method(41);"
8658 " if (i == 50) {"
8659 " saved_result = result;"
8660 " receiver = 333;"
8661 " }"
8662 "}");
8663 CHECK(try_catch.HasCaught());
8664 CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"),
8665 try_catch.Exception()->ToString());
8666 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
8667}
8668
Leon Clarke4515c472010-02-03 11:58:03 +00008669
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008670v8::Handle<Value> keyed_call_ic_function;
8671
8672static v8::Handle<Value> InterceptorKeyedCallICGetter(
8673 Local<String> name, const AccessorInfo& info) {
8674 ApiTestFuzzer::Fuzz();
8675 if (v8_str("x")->Equals(name)) {
8676 return keyed_call_ic_function;
8677 }
8678 return v8::Handle<Value>();
8679}
8680
8681
8682// Test the case when we stored cacheable lookup into
8683// a stub, but the function name changed (to another cacheable function).
8684THREADED_TEST(InterceptorKeyedCallICKeyChange1) {
8685 v8::HandleScope scope;
8686 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
8687 templ->SetNamedPropertyHandler(NoBlockGetterX);
8688 LocalContext context;
8689 context->Global()->Set(v8_str("o"), templ->NewInstance());
8690 v8::Handle<Value> value = CompileRun(
8691 "proto = new Object();"
8692 "proto.y = function(x) { return x + 1; };"
8693 "proto.z = function(x) { return x - 1; };"
8694 "o.__proto__ = proto;"
8695 "var result = 0;"
8696 "var method = 'y';"
8697 "for (var i = 0; i < 10; i++) {"
8698 " if (i == 5) { method = 'z'; };"
8699 " result += o[method](41);"
8700 "}");
8701 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
8702}
8703
8704
8705// Test the case when we stored cacheable lookup into
8706// a stub, but the function name changed (and the new function is present
8707// both before and after the interceptor in the prototype chain).
8708THREADED_TEST(InterceptorKeyedCallICKeyChange2) {
8709 v8::HandleScope scope;
8710 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
8711 templ->SetNamedPropertyHandler(InterceptorKeyedCallICGetter);
8712 LocalContext context;
8713 context->Global()->Set(v8_str("proto1"), templ->NewInstance());
8714 keyed_call_ic_function =
8715 v8_compile("function f(x) { return x - 1; }; f")->Run();
8716 v8::Handle<Value> value = CompileRun(
8717 "o = new Object();"
8718 "proto2 = new Object();"
8719 "o.y = function(x) { return x + 1; };"
8720 "proto2.y = function(x) { return x + 2; };"
8721 "o.__proto__ = proto1;"
8722 "proto1.__proto__ = proto2;"
8723 "var result = 0;"
8724 "var method = 'x';"
8725 "for (var i = 0; i < 10; i++) {"
8726 " if (i == 5) { method = 'y'; };"
8727 " result += o[method](41);"
8728 "}");
8729 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
8730}
8731
8732
8733// Same as InterceptorKeyedCallICKeyChange1 only the cacheable function sit
8734// on the global object.
8735THREADED_TEST(InterceptorKeyedCallICKeyChangeOnGlobal) {
8736 v8::HandleScope scope;
8737 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
8738 templ->SetNamedPropertyHandler(NoBlockGetterX);
8739 LocalContext context;
8740 context->Global()->Set(v8_str("o"), templ->NewInstance());
8741 v8::Handle<Value> value = CompileRun(
8742 "function inc(x) { return x + 1; };"
8743 "inc(1);"
8744 "function dec(x) { return x - 1; };"
8745 "dec(1);"
8746 "o.__proto__ = this;"
8747 "this.__proto__.x = inc;"
8748 "this.__proto__.y = dec;"
8749 "var result = 0;"
8750 "var method = 'x';"
8751 "for (var i = 0; i < 10; i++) {"
8752 " if (i == 5) { method = 'y'; };"
8753 " result += o[method](41);"
8754 "}");
8755 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
8756}
8757
8758
8759// Test the case when actual function to call sits on global object.
8760THREADED_TEST(InterceptorKeyedCallICFromGlobal) {
8761 v8::HandleScope scope;
8762 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
8763 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
8764 LocalContext context;
8765 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
8766
8767 v8::Handle<Value> value = CompileRun(
8768 "function len(x) { return x.length; };"
8769 "o.__proto__ = this;"
8770 "var m = 'parseFloat';"
8771 "var result = 0;"
8772 "for (var i = 0; i < 10; i++) {"
8773 " if (i == 5) {"
8774 " m = 'len';"
8775 " saved_result = result;"
8776 " };"
8777 " result = o[m]('239');"
8778 "}");
8779 CHECK_EQ(3, context->Global()->Get(v8_str("result"))->Int32Value());
8780 CHECK_EQ(239, context->Global()->Get(v8_str("saved_result"))->Int32Value());
8781}
8782
8783// Test the map transition before the interceptor.
8784THREADED_TEST(InterceptorKeyedCallICMapChangeBefore) {
8785 v8::HandleScope scope;
8786 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
8787 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
8788 LocalContext context;
8789 context->Global()->Set(v8_str("proto"), templ_o->NewInstance());
8790
8791 v8::Handle<Value> value = CompileRun(
8792 "var o = new Object();"
8793 "o.__proto__ = proto;"
8794 "o.method = function(x) { return x + 1; };"
8795 "var m = 'method';"
8796 "var result = 0;"
8797 "for (var i = 0; i < 10; i++) {"
8798 " if (i == 5) { o.method = function(x) { return x - 1; }; };"
8799 " result += o[m](41);"
8800 "}");
8801 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
8802}
8803
8804
8805// Test the map transition after the interceptor.
8806THREADED_TEST(InterceptorKeyedCallICMapChangeAfter) {
8807 v8::HandleScope scope;
8808 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
8809 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
8810 LocalContext context;
8811 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
8812
8813 v8::Handle<Value> value = CompileRun(
8814 "var proto = new Object();"
8815 "o.__proto__ = proto;"
8816 "proto.method = function(x) { return x + 1; };"
8817 "var m = 'method';"
8818 "var result = 0;"
8819 "for (var i = 0; i < 10; i++) {"
8820 " if (i == 5) { proto.method = function(x) { return x - 1; }; };"
8821 " result += o[m](41);"
8822 "}");
8823 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
8824}
8825
8826
Steve Blocka7e24c12009-10-30 11:49:00 +00008827static int interceptor_call_count = 0;
8828
8829static v8::Handle<Value> InterceptorICRefErrorGetter(Local<String> name,
8830 const AccessorInfo& info) {
8831 ApiTestFuzzer::Fuzz();
8832 if (v8_str("x")->Equals(name) && interceptor_call_count++ < 20) {
8833 return call_ic_function2;
8834 }
8835 return v8::Handle<Value>();
8836}
8837
8838
8839// This test should hit load and call ICs for the interceptor case.
8840// Once in a while, the interceptor will reply that a property was not
8841// found in which case we should get a reference error.
8842THREADED_TEST(InterceptorICReferenceErrors) {
8843 v8::HandleScope scope;
8844 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
8845 templ->SetNamedPropertyHandler(InterceptorICRefErrorGetter);
8846 LocalContext context(0, templ, v8::Handle<Value>());
8847 call_ic_function2 = v8_compile("function h(x) { return x; }; h")->Run();
8848 v8::Handle<Value> value = CompileRun(
8849 "function f() {"
8850 " for (var i = 0; i < 1000; i++) {"
8851 " try { x; } catch(e) { return true; }"
8852 " }"
8853 " return false;"
8854 "};"
8855 "f();");
8856 CHECK_EQ(true, value->BooleanValue());
8857 interceptor_call_count = 0;
8858 value = CompileRun(
8859 "function g() {"
8860 " for (var i = 0; i < 1000; i++) {"
8861 " try { x(42); } catch(e) { return true; }"
8862 " }"
8863 " return false;"
8864 "};"
8865 "g();");
8866 CHECK_EQ(true, value->BooleanValue());
8867}
8868
8869
8870static int interceptor_ic_exception_get_count = 0;
8871
8872static v8::Handle<Value> InterceptorICExceptionGetter(
8873 Local<String> name,
8874 const AccessorInfo& info) {
8875 ApiTestFuzzer::Fuzz();
8876 if (v8_str("x")->Equals(name) && ++interceptor_ic_exception_get_count < 20) {
8877 return call_ic_function3;
8878 }
8879 if (interceptor_ic_exception_get_count == 20) {
8880 return v8::ThrowException(v8_num(42));
8881 }
8882 // Do not handle get for properties other than x.
8883 return v8::Handle<Value>();
8884}
8885
8886// Test interceptor load/call IC where the interceptor throws an
8887// exception once in a while.
8888THREADED_TEST(InterceptorICGetterExceptions) {
8889 interceptor_ic_exception_get_count = 0;
8890 v8::HandleScope scope;
8891 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
8892 templ->SetNamedPropertyHandler(InterceptorICExceptionGetter);
8893 LocalContext context(0, templ, v8::Handle<Value>());
8894 call_ic_function3 = v8_compile("function h(x) { return x; }; h")->Run();
8895 v8::Handle<Value> value = CompileRun(
8896 "function f() {"
8897 " for (var i = 0; i < 100; i++) {"
8898 " try { x; } catch(e) { return true; }"
8899 " }"
8900 " return false;"
8901 "};"
8902 "f();");
8903 CHECK_EQ(true, value->BooleanValue());
8904 interceptor_ic_exception_get_count = 0;
8905 value = CompileRun(
8906 "function f() {"
8907 " for (var i = 0; i < 100; i++) {"
8908 " try { x(42); } catch(e) { return true; }"
8909 " }"
8910 " return false;"
8911 "};"
8912 "f();");
8913 CHECK_EQ(true, value->BooleanValue());
8914}
8915
8916
8917static int interceptor_ic_exception_set_count = 0;
8918
8919static v8::Handle<Value> InterceptorICExceptionSetter(
8920 Local<String> key, Local<Value> value, const AccessorInfo&) {
8921 ApiTestFuzzer::Fuzz();
8922 if (++interceptor_ic_exception_set_count > 20) {
8923 return v8::ThrowException(v8_num(42));
8924 }
8925 // Do not actually handle setting.
8926 return v8::Handle<Value>();
8927}
8928
8929// Test interceptor store IC where the interceptor throws an exception
8930// once in a while.
8931THREADED_TEST(InterceptorICSetterExceptions) {
8932 interceptor_ic_exception_set_count = 0;
8933 v8::HandleScope scope;
8934 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
8935 templ->SetNamedPropertyHandler(0, InterceptorICExceptionSetter);
8936 LocalContext context(0, templ, v8::Handle<Value>());
8937 v8::Handle<Value> value = CompileRun(
8938 "function f() {"
8939 " for (var i = 0; i < 100; i++) {"
8940 " try { x = 42; } catch(e) { return true; }"
8941 " }"
8942 " return false;"
8943 "};"
8944 "f();");
8945 CHECK_EQ(true, value->BooleanValue());
8946}
8947
8948
8949// Test that we ignore null interceptors.
8950THREADED_TEST(NullNamedInterceptor) {
8951 v8::HandleScope scope;
8952 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
8953 templ->SetNamedPropertyHandler(0);
8954 LocalContext context;
8955 templ->Set("x", v8_num(42));
8956 v8::Handle<v8::Object> obj = templ->NewInstance();
8957 context->Global()->Set(v8_str("obj"), obj);
8958 v8::Handle<Value> value = CompileRun("obj.x");
8959 CHECK(value->IsInt32());
8960 CHECK_EQ(42, value->Int32Value());
8961}
8962
8963
8964// Test that we ignore null interceptors.
8965THREADED_TEST(NullIndexedInterceptor) {
8966 v8::HandleScope scope;
8967 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
8968 templ->SetIndexedPropertyHandler(0);
8969 LocalContext context;
8970 templ->Set("42", v8_num(42));
8971 v8::Handle<v8::Object> obj = templ->NewInstance();
8972 context->Global()->Set(v8_str("obj"), obj);
8973 v8::Handle<Value> value = CompileRun("obj[42]");
8974 CHECK(value->IsInt32());
8975 CHECK_EQ(42, value->Int32Value());
8976}
8977
8978
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008979THREADED_TEST(NamedPropertyHandlerGetterAttributes) {
8980 v8::HandleScope scope;
8981 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
8982 templ->InstanceTemplate()->SetNamedPropertyHandler(InterceptorLoadXICGetter);
8983 LocalContext env;
8984 env->Global()->Set(v8_str("obj"),
8985 templ->GetFunction()->NewInstance());
8986 ExpectTrue("obj.x === 42");
8987 ExpectTrue("!obj.propertyIsEnumerable('x')");
8988}
8989
8990
Ben Murdoch8b112d22011-06-08 16:22:53 +01008991static Handle<Value> ThrowingGetter(Local<String> name,
8992 const AccessorInfo& info) {
8993 ApiTestFuzzer::Fuzz();
8994 ThrowException(Handle<Value>());
8995 return Undefined();
8996}
8997
8998
8999THREADED_TEST(VariousGetPropertiesAndThrowingCallbacks) {
9000 HandleScope scope;
9001 LocalContext context;
9002
9003 Local<FunctionTemplate> templ = FunctionTemplate::New();
9004 Local<ObjectTemplate> instance_templ = templ->InstanceTemplate();
9005 instance_templ->SetAccessor(v8_str("f"), ThrowingGetter);
9006
9007 Local<Object> instance = templ->GetFunction()->NewInstance();
9008
9009 Local<Object> another = Object::New();
9010 another->SetPrototype(instance);
9011
9012 Local<Object> with_js_getter = CompileRun(
9013 "o = {};\n"
9014 "o.__defineGetter__('f', function() { throw undefined; });\n"
9015 "o\n").As<Object>();
9016 CHECK(!with_js_getter.IsEmpty());
9017
9018 TryCatch try_catch;
9019
9020 Local<Value> result = instance->GetRealNamedProperty(v8_str("f"));
9021 CHECK(try_catch.HasCaught());
9022 try_catch.Reset();
9023 CHECK(result.IsEmpty());
9024
9025 result = another->GetRealNamedProperty(v8_str("f"));
9026 CHECK(try_catch.HasCaught());
9027 try_catch.Reset();
9028 CHECK(result.IsEmpty());
9029
9030 result = another->GetRealNamedPropertyInPrototypeChain(v8_str("f"));
9031 CHECK(try_catch.HasCaught());
9032 try_catch.Reset();
9033 CHECK(result.IsEmpty());
9034
9035 result = another->Get(v8_str("f"));
9036 CHECK(try_catch.HasCaught());
9037 try_catch.Reset();
9038 CHECK(result.IsEmpty());
9039
9040 result = with_js_getter->GetRealNamedProperty(v8_str("f"));
9041 CHECK(try_catch.HasCaught());
9042 try_catch.Reset();
9043 CHECK(result.IsEmpty());
9044
9045 result = with_js_getter->Get(v8_str("f"));
9046 CHECK(try_catch.HasCaught());
9047 try_catch.Reset();
9048 CHECK(result.IsEmpty());
9049}
9050
9051
9052static Handle<Value> ThrowingCallbackWithTryCatch(const Arguments& args) {
9053 TryCatch try_catch;
9054 // Verboseness is important: it triggers message delivery which can call into
9055 // external code.
9056 try_catch.SetVerbose(true);
9057 CompileRun("throw 'from JS';");
9058 CHECK(try_catch.HasCaught());
9059 CHECK(!i::Isolate::Current()->has_pending_exception());
9060 CHECK(!i::Isolate::Current()->has_scheduled_exception());
9061 return Undefined();
9062}
9063
9064
9065static int call_depth;
9066
9067
9068static void WithTryCatch(Handle<Message> message, Handle<Value> data) {
9069 TryCatch try_catch;
9070}
9071
9072
9073static void ThrowFromJS(Handle<Message> message, Handle<Value> data) {
9074 if (--call_depth) CompileRun("throw 'ThrowInJS';");
9075}
9076
9077
9078static void ThrowViaApi(Handle<Message> message, Handle<Value> data) {
9079 if (--call_depth) ThrowException(v8_str("ThrowViaApi"));
9080}
9081
9082
9083static void WebKitLike(Handle<Message> message, Handle<Value> data) {
9084 Handle<String> errorMessageString = message->Get();
9085 CHECK(!errorMessageString.IsEmpty());
9086 message->GetStackTrace();
9087 message->GetScriptResourceName();
9088}
9089
9090THREADED_TEST(ExceptionsDoNotPropagatePastTryCatch) {
9091 HandleScope scope;
9092 LocalContext context;
9093
9094 Local<Function> func =
9095 FunctionTemplate::New(ThrowingCallbackWithTryCatch)->GetFunction();
9096 context->Global()->Set(v8_str("func"), func);
9097
9098 MessageCallback callbacks[] =
9099 { NULL, WebKitLike, ThrowViaApi, ThrowFromJS, WithTryCatch };
9100 for (unsigned i = 0; i < sizeof(callbacks)/sizeof(callbacks[0]); i++) {
9101 MessageCallback callback = callbacks[i];
9102 if (callback != NULL) {
9103 V8::AddMessageListener(callback);
9104 }
Ben Murdoch257744e2011-11-30 15:57:28 +00009105 // Some small number to control number of times message handler should
9106 // throw an exception.
Ben Murdoch8b112d22011-06-08 16:22:53 +01009107 call_depth = 5;
9108 ExpectFalse(
9109 "var thrown = false;\n"
9110 "try { func(); } catch(e) { thrown = true; }\n"
9111 "thrown\n");
9112 if (callback != NULL) {
9113 V8::RemoveMessageListeners(callback);
9114 }
9115 }
9116}
9117
9118
Steve Blocka7e24c12009-10-30 11:49:00 +00009119static v8::Handle<Value> ParentGetter(Local<String> name,
9120 const AccessorInfo& info) {
9121 ApiTestFuzzer::Fuzz();
9122 return v8_num(1);
9123}
9124
9125
9126static v8::Handle<Value> ChildGetter(Local<String> name,
9127 const AccessorInfo& info) {
9128 ApiTestFuzzer::Fuzz();
9129 return v8_num(42);
9130}
9131
9132
9133THREADED_TEST(Overriding) {
9134 v8::HandleScope scope;
9135 LocalContext context;
9136
9137 // Parent template.
9138 Local<v8::FunctionTemplate> parent_templ = v8::FunctionTemplate::New();
9139 Local<ObjectTemplate> parent_instance_templ =
9140 parent_templ->InstanceTemplate();
9141 parent_instance_templ->SetAccessor(v8_str("f"), ParentGetter);
9142
9143 // Template that inherits from the parent template.
9144 Local<v8::FunctionTemplate> child_templ = v8::FunctionTemplate::New();
9145 Local<ObjectTemplate> child_instance_templ =
9146 child_templ->InstanceTemplate();
9147 child_templ->Inherit(parent_templ);
9148 // Override 'f'. The child version of 'f' should get called for child
9149 // instances.
9150 child_instance_templ->SetAccessor(v8_str("f"), ChildGetter);
9151 // Add 'g' twice. The 'g' added last should get called for instances.
9152 child_instance_templ->SetAccessor(v8_str("g"), ParentGetter);
9153 child_instance_templ->SetAccessor(v8_str("g"), ChildGetter);
9154
9155 // Add 'h' as an accessor to the proto template with ReadOnly attributes
9156 // so 'h' can be shadowed on the instance object.
9157 Local<ObjectTemplate> child_proto_templ = child_templ->PrototypeTemplate();
9158 child_proto_templ->SetAccessor(v8_str("h"), ParentGetter, 0,
9159 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly);
9160
9161 // Add 'i' as an accessor to the instance template with ReadOnly attributes
9162 // but the attribute does not have effect because it is duplicated with
9163 // NULL setter.
9164 child_instance_templ->SetAccessor(v8_str("i"), ChildGetter, 0,
9165 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly);
9166
9167
9168
9169 // Instantiate the child template.
9170 Local<v8::Object> instance = child_templ->GetFunction()->NewInstance();
9171
9172 // Check that the child function overrides the parent one.
9173 context->Global()->Set(v8_str("o"), instance);
9174 Local<Value> value = v8_compile("o.f")->Run();
9175 // Check that the 'g' that was added last is hit.
9176 CHECK_EQ(42, value->Int32Value());
9177 value = v8_compile("o.g")->Run();
9178 CHECK_EQ(42, value->Int32Value());
9179
9180 // Check 'h' can be shadowed.
9181 value = v8_compile("o.h = 3; o.h")->Run();
9182 CHECK_EQ(3, value->Int32Value());
9183
9184 // Check 'i' is cannot be shadowed or changed.
9185 value = v8_compile("o.i = 3; o.i")->Run();
9186 CHECK_EQ(42, value->Int32Value());
9187}
9188
9189
9190static v8::Handle<Value> IsConstructHandler(const v8::Arguments& args) {
9191 ApiTestFuzzer::Fuzz();
9192 if (args.IsConstructCall()) {
9193 return v8::Boolean::New(true);
9194 }
9195 return v8::Boolean::New(false);
9196}
9197
9198
9199THREADED_TEST(IsConstructCall) {
9200 v8::HandleScope scope;
9201
9202 // Function template with call handler.
9203 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
9204 templ->SetCallHandler(IsConstructHandler);
9205
9206 LocalContext context;
9207
9208 context->Global()->Set(v8_str("f"), templ->GetFunction());
9209 Local<Value> value = v8_compile("f()")->Run();
9210 CHECK(!value->BooleanValue());
9211 value = v8_compile("new f()")->Run();
9212 CHECK(value->BooleanValue());
9213}
9214
9215
9216THREADED_TEST(ObjectProtoToString) {
9217 v8::HandleScope scope;
9218 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
9219 templ->SetClassName(v8_str("MyClass"));
9220
9221 LocalContext context;
9222
9223 Local<String> customized_tostring = v8_str("customized toString");
9224
9225 // Replace Object.prototype.toString
9226 v8_compile("Object.prototype.toString = function() {"
9227 " return 'customized toString';"
9228 "}")->Run();
9229
9230 // Normal ToString call should call replaced Object.prototype.toString
9231 Local<v8::Object> instance = templ->GetFunction()->NewInstance();
9232 Local<String> value = instance->ToString();
9233 CHECK(value->IsString() && value->Equals(customized_tostring));
9234
9235 // ObjectProtoToString should not call replace toString function.
9236 value = instance->ObjectProtoToString();
9237 CHECK(value->IsString() && value->Equals(v8_str("[object MyClass]")));
9238
9239 // Check global
9240 value = context->Global()->ObjectProtoToString();
9241 CHECK(value->IsString() && value->Equals(v8_str("[object global]")));
9242
9243 // Check ordinary object
9244 Local<Value> object = v8_compile("new Object()")->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01009245 value = object.As<v8::Object>()->ObjectProtoToString();
Steve Blocka7e24c12009-10-30 11:49:00 +00009246 CHECK(value->IsString() && value->Equals(v8_str("[object Object]")));
9247}
9248
9249
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -08009250THREADED_TEST(ObjectGetConstructorName) {
9251 v8::HandleScope scope;
9252 LocalContext context;
9253 v8_compile("function Parent() {};"
9254 "function Child() {};"
9255 "Child.prototype = new Parent();"
9256 "var outer = { inner: function() { } };"
9257 "var p = new Parent();"
9258 "var c = new Child();"
9259 "var x = new outer.inner();")->Run();
9260
9261 Local<v8::Value> p = context->Global()->Get(v8_str("p"));
9262 CHECK(p->IsObject() && p->ToObject()->GetConstructorName()->Equals(
9263 v8_str("Parent")));
9264
9265 Local<v8::Value> c = context->Global()->Get(v8_str("c"));
9266 CHECK(c->IsObject() && c->ToObject()->GetConstructorName()->Equals(
9267 v8_str("Child")));
9268
9269 Local<v8::Value> x = context->Global()->Get(v8_str("x"));
9270 CHECK(x->IsObject() && x->ToObject()->GetConstructorName()->Equals(
9271 v8_str("outer.inner")));
9272}
9273
9274
Steve Blocka7e24c12009-10-30 11:49:00 +00009275bool ApiTestFuzzer::fuzzing_ = false;
Steve Block8defd9f2010-07-08 12:39:36 +01009276i::Semaphore* ApiTestFuzzer::all_tests_done_=
9277 i::OS::CreateSemaphore(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00009278int ApiTestFuzzer::active_tests_;
9279int ApiTestFuzzer::tests_being_run_;
9280int ApiTestFuzzer::current_;
9281
9282
9283// We are in a callback and want to switch to another thread (if we
9284// are currently running the thread fuzzing test).
9285void ApiTestFuzzer::Fuzz() {
9286 if (!fuzzing_) return;
9287 ApiTestFuzzer* test = RegisterThreadedTest::nth(current_)->fuzzer_;
9288 test->ContextSwitch();
9289}
9290
9291
9292// Let the next thread go. Since it is also waiting on the V8 lock it may
9293// not start immediately.
9294bool ApiTestFuzzer::NextThread() {
9295 int test_position = GetNextTestNumber();
Steve Blockd0582a62009-12-15 09:54:21 +00009296 const char* test_name = RegisterThreadedTest::nth(current_)->name();
Steve Blocka7e24c12009-10-30 11:49:00 +00009297 if (test_position == current_) {
Steve Blockd0582a62009-12-15 09:54:21 +00009298 if (kLogThreading)
9299 printf("Stay with %s\n", test_name);
Steve Blocka7e24c12009-10-30 11:49:00 +00009300 return false;
9301 }
Steve Blockd0582a62009-12-15 09:54:21 +00009302 if (kLogThreading) {
9303 printf("Switch from %s to %s\n",
9304 test_name,
9305 RegisterThreadedTest::nth(test_position)->name());
9306 }
Steve Blocka7e24c12009-10-30 11:49:00 +00009307 current_ = test_position;
9308 RegisterThreadedTest::nth(current_)->fuzzer_->gate_->Signal();
9309 return true;
9310}
9311
9312
9313void ApiTestFuzzer::Run() {
9314 // When it is our turn...
9315 gate_->Wait();
9316 {
9317 // ... get the V8 lock and start running the test.
9318 v8::Locker locker;
9319 CallTest();
9320 }
9321 // This test finished.
9322 active_ = false;
9323 active_tests_--;
9324 // If it was the last then signal that fact.
9325 if (active_tests_ == 0) {
9326 all_tests_done_->Signal();
9327 } else {
9328 // Otherwise select a new test and start that.
9329 NextThread();
9330 }
9331}
9332
9333
9334static unsigned linear_congruential_generator;
9335
9336
9337void ApiTestFuzzer::Setup(PartOfTest part) {
9338 linear_congruential_generator = i::FLAG_testing_prng_seed;
9339 fuzzing_ = true;
Ben Murdoch257744e2011-11-30 15:57:28 +00009340 int count = RegisterThreadedTest::count();
9341 int start = count * part / (LAST_PART + 1);
9342 int end = (count * (part + 1) / (LAST_PART + 1)) - 1;
9343 active_tests_ = tests_being_run_ = end - start + 1;
Steve Blocka7e24c12009-10-30 11:49:00 +00009344 for (int i = 0; i < tests_being_run_; i++) {
Steve Block44f0eee2011-05-26 01:26:41 +01009345 RegisterThreadedTest::nth(i)->fuzzer_ = new ApiTestFuzzer(
9346 i::Isolate::Current(), i + start);
Steve Blocka7e24c12009-10-30 11:49:00 +00009347 }
9348 for (int i = 0; i < active_tests_; i++) {
9349 RegisterThreadedTest::nth(i)->fuzzer_->Start();
9350 }
9351}
9352
9353
9354static void CallTestNumber(int test_number) {
9355 (RegisterThreadedTest::nth(test_number)->callback())();
9356}
9357
9358
9359void ApiTestFuzzer::RunAllTests() {
9360 // Set off the first test.
9361 current_ = -1;
9362 NextThread();
9363 // Wait till they are all done.
9364 all_tests_done_->Wait();
9365}
9366
9367
9368int ApiTestFuzzer::GetNextTestNumber() {
9369 int next_test;
9370 do {
9371 next_test = (linear_congruential_generator >> 16) % tests_being_run_;
9372 linear_congruential_generator *= 1664525u;
9373 linear_congruential_generator += 1013904223u;
9374 } while (!RegisterThreadedTest::nth(next_test)->fuzzer_->active_);
9375 return next_test;
9376}
9377
9378
9379void ApiTestFuzzer::ContextSwitch() {
9380 // If the new thread is the same as the current thread there is nothing to do.
9381 if (NextThread()) {
9382 // Now it can start.
9383 v8::Unlocker unlocker;
9384 // Wait till someone starts us again.
9385 gate_->Wait();
9386 // And we're off.
9387 }
9388}
9389
9390
9391void ApiTestFuzzer::TearDown() {
9392 fuzzing_ = false;
9393 for (int i = 0; i < RegisterThreadedTest::count(); i++) {
9394 ApiTestFuzzer *fuzzer = RegisterThreadedTest::nth(i)->fuzzer_;
9395 if (fuzzer != NULL) fuzzer->Join();
9396 }
9397}
9398
9399
9400// Lets not be needlessly self-referential.
9401TEST(Threading) {
9402 ApiTestFuzzer::Setup(ApiTestFuzzer::FIRST_PART);
9403 ApiTestFuzzer::RunAllTests();
9404 ApiTestFuzzer::TearDown();
9405}
9406
9407TEST(Threading2) {
9408 ApiTestFuzzer::Setup(ApiTestFuzzer::SECOND_PART);
9409 ApiTestFuzzer::RunAllTests();
9410 ApiTestFuzzer::TearDown();
9411}
9412
Ben Murdoch257744e2011-11-30 15:57:28 +00009413TEST(Threading3) {
9414 ApiTestFuzzer::Setup(ApiTestFuzzer::THIRD_PART);
9415 ApiTestFuzzer::RunAllTests();
9416 ApiTestFuzzer::TearDown();
9417}
9418
9419TEST(Threading4) {
9420 ApiTestFuzzer::Setup(ApiTestFuzzer::FOURTH_PART);
9421 ApiTestFuzzer::RunAllTests();
9422 ApiTestFuzzer::TearDown();
9423}
Steve Blocka7e24c12009-10-30 11:49:00 +00009424
9425void ApiTestFuzzer::CallTest() {
Steve Blockd0582a62009-12-15 09:54:21 +00009426 if (kLogThreading)
9427 printf("Start test %d\n", test_number_);
Steve Blocka7e24c12009-10-30 11:49:00 +00009428 CallTestNumber(test_number_);
Steve Blockd0582a62009-12-15 09:54:21 +00009429 if (kLogThreading)
9430 printf("End test %d\n", test_number_);
Steve Blocka7e24c12009-10-30 11:49:00 +00009431}
9432
9433
9434static v8::Handle<Value> ThrowInJS(const v8::Arguments& args) {
9435 CHECK(v8::Locker::IsLocked());
9436 ApiTestFuzzer::Fuzz();
9437 v8::Unlocker unlocker;
9438 const char* code = "throw 7;";
9439 {
9440 v8::Locker nested_locker;
9441 v8::HandleScope scope;
9442 v8::Handle<Value> exception;
9443 { v8::TryCatch try_catch;
9444 v8::Handle<Value> value = CompileRun(code);
9445 CHECK(value.IsEmpty());
9446 CHECK(try_catch.HasCaught());
9447 // Make sure to wrap the exception in a new handle because
9448 // the handle returned from the TryCatch is destroyed
9449 // when the TryCatch is destroyed.
9450 exception = Local<Value>::New(try_catch.Exception());
9451 }
9452 return v8::ThrowException(exception);
9453 }
9454}
9455
9456
9457static v8::Handle<Value> ThrowInJSNoCatch(const v8::Arguments& args) {
9458 CHECK(v8::Locker::IsLocked());
9459 ApiTestFuzzer::Fuzz();
9460 v8::Unlocker unlocker;
9461 const char* code = "throw 7;";
9462 {
9463 v8::Locker nested_locker;
9464 v8::HandleScope scope;
9465 v8::Handle<Value> value = CompileRun(code);
9466 CHECK(value.IsEmpty());
9467 return v8_str("foo");
9468 }
9469}
9470
9471
9472// These are locking tests that don't need to be run again
9473// as part of the locking aggregation tests.
9474TEST(NestedLockers) {
9475 v8::Locker locker;
9476 CHECK(v8::Locker::IsLocked());
9477 v8::HandleScope scope;
9478 LocalContext env;
9479 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(ThrowInJS);
9480 Local<Function> fun = fun_templ->GetFunction();
9481 env->Global()->Set(v8_str("throw_in_js"), fun);
9482 Local<Script> script = v8_compile("(function () {"
9483 " try {"
9484 " throw_in_js();"
9485 " return 42;"
9486 " } catch (e) {"
9487 " return e * 13;"
9488 " }"
9489 "})();");
9490 CHECK_EQ(91, script->Run()->Int32Value());
9491}
9492
9493
9494// These are locking tests that don't need to be run again
9495// as part of the locking aggregation tests.
9496TEST(NestedLockersNoTryCatch) {
9497 v8::Locker locker;
9498 v8::HandleScope scope;
9499 LocalContext env;
9500 Local<v8::FunctionTemplate> fun_templ =
9501 v8::FunctionTemplate::New(ThrowInJSNoCatch);
9502 Local<Function> fun = fun_templ->GetFunction();
9503 env->Global()->Set(v8_str("throw_in_js"), fun);
9504 Local<Script> script = v8_compile("(function () {"
9505 " try {"
9506 " throw_in_js();"
9507 " return 42;"
9508 " } catch (e) {"
9509 " return e * 13;"
9510 " }"
9511 "})();");
9512 CHECK_EQ(91, script->Run()->Int32Value());
9513}
9514
9515
9516THREADED_TEST(RecursiveLocking) {
9517 v8::Locker locker;
9518 {
9519 v8::Locker locker2;
9520 CHECK(v8::Locker::IsLocked());
9521 }
9522}
9523
9524
9525static v8::Handle<Value> UnlockForAMoment(const v8::Arguments& args) {
9526 ApiTestFuzzer::Fuzz();
9527 v8::Unlocker unlocker;
9528 return v8::Undefined();
9529}
9530
9531
9532THREADED_TEST(LockUnlockLock) {
9533 {
9534 v8::Locker locker;
9535 v8::HandleScope scope;
9536 LocalContext env;
9537 Local<v8::FunctionTemplate> fun_templ =
9538 v8::FunctionTemplate::New(UnlockForAMoment);
9539 Local<Function> fun = fun_templ->GetFunction();
9540 env->Global()->Set(v8_str("unlock_for_a_moment"), fun);
9541 Local<Script> script = v8_compile("(function () {"
9542 " unlock_for_a_moment();"
9543 " return 42;"
9544 "})();");
9545 CHECK_EQ(42, script->Run()->Int32Value());
9546 }
9547 {
9548 v8::Locker locker;
9549 v8::HandleScope scope;
9550 LocalContext env;
9551 Local<v8::FunctionTemplate> fun_templ =
9552 v8::FunctionTemplate::New(UnlockForAMoment);
9553 Local<Function> fun = fun_templ->GetFunction();
9554 env->Global()->Set(v8_str("unlock_for_a_moment"), fun);
9555 Local<Script> script = v8_compile("(function () {"
9556 " unlock_for_a_moment();"
9557 " return 42;"
9558 "})();");
9559 CHECK_EQ(42, script->Run()->Int32Value());
9560 }
9561}
9562
9563
Leon Clarked91b9f72010-01-27 17:25:45 +00009564static int GetGlobalObjectsCount() {
Leon Clarkeeab96aa2010-01-27 16:31:12 +00009565 int count = 0;
Steve Block8defd9f2010-07-08 12:39:36 +01009566 i::HeapIterator it;
Leon Clarked91b9f72010-01-27 17:25:45 +00009567 for (i::HeapObject* object = it.next(); object != NULL; object = it.next())
9568 if (object->IsJSGlobalObject()) count++;
9569 return count;
9570}
9571
9572
Ben Murdochf87a2032010-10-22 12:50:53 +01009573static void CheckSurvivingGlobalObjectsCount(int expected) {
Steve Blocka7e24c12009-10-30 11:49:00 +00009574 // We need to collect all garbage twice to be sure that everything
9575 // has been collected. This is because inline caches are cleared in
9576 // the first garbage collection but some of the maps have already
9577 // been marked at that point. Therefore some of the maps are not
9578 // collected until the second garbage collection.
Steve Block44f0eee2011-05-26 01:26:41 +01009579 HEAP->CollectAllGarbage(false);
9580 HEAP->CollectAllGarbage(false);
Leon Clarked91b9f72010-01-27 17:25:45 +00009581 int count = GetGlobalObjectsCount();
Steve Blocka7e24c12009-10-30 11:49:00 +00009582#ifdef DEBUG
Steve Block44f0eee2011-05-26 01:26:41 +01009583 if (count != expected) HEAP->TracePathToGlobal();
Steve Blocka7e24c12009-10-30 11:49:00 +00009584#endif
Ben Murdochf87a2032010-10-22 12:50:53 +01009585 CHECK_EQ(expected, count);
Steve Blocka7e24c12009-10-30 11:49:00 +00009586}
9587
9588
9589TEST(DontLeakGlobalObjects) {
9590 // Regression test for issues 1139850 and 1174891.
9591
9592 v8::V8::Initialize();
9593
Steve Blocka7e24c12009-10-30 11:49:00 +00009594 for (int i = 0; i < 5; i++) {
9595 { v8::HandleScope scope;
9596 LocalContext context;
9597 }
Ben Murdochf87a2032010-10-22 12:50:53 +01009598 CheckSurvivingGlobalObjectsCount(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00009599
9600 { v8::HandleScope scope;
9601 LocalContext context;
9602 v8_compile("Date")->Run();
9603 }
Ben Murdochf87a2032010-10-22 12:50:53 +01009604 CheckSurvivingGlobalObjectsCount(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00009605
9606 { v8::HandleScope scope;
9607 LocalContext context;
9608 v8_compile("/aaa/")->Run();
9609 }
Ben Murdochf87a2032010-10-22 12:50:53 +01009610 CheckSurvivingGlobalObjectsCount(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00009611
9612 { v8::HandleScope scope;
9613 const char* extension_list[] = { "v8/gc" };
9614 v8::ExtensionConfiguration extensions(1, extension_list);
9615 LocalContext context(&extensions);
9616 v8_compile("gc();")->Run();
9617 }
Ben Murdochf87a2032010-10-22 12:50:53 +01009618 CheckSurvivingGlobalObjectsCount(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00009619 }
9620}
9621
9622
9623v8::Persistent<v8::Object> some_object;
9624v8::Persistent<v8::Object> bad_handle;
9625
Kristian Monsen50ef84f2010-07-29 15:18:00 +01009626void NewPersistentHandleCallback(v8::Persistent<v8::Value> handle, void*) {
Steve Blocka7e24c12009-10-30 11:49:00 +00009627 v8::HandleScope scope;
9628 bad_handle = v8::Persistent<v8::Object>::New(some_object);
Kristian Monsen50ef84f2010-07-29 15:18:00 +01009629 handle.Dispose();
Steve Blocka7e24c12009-10-30 11:49:00 +00009630}
9631
9632
9633THREADED_TEST(NewPersistentHandleFromWeakCallback) {
9634 LocalContext context;
9635
9636 v8::Persistent<v8::Object> handle1, handle2;
9637 {
9638 v8::HandleScope scope;
9639 some_object = v8::Persistent<v8::Object>::New(v8::Object::New());
9640 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
9641 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
9642 }
9643 // Note: order is implementation dependent alas: currently
9644 // global handle nodes are processed by PostGarbageCollectionProcessing
9645 // in reverse allocation order, so if second allocated handle is deleted,
9646 // weak callback of the first handle would be able to 'reallocate' it.
9647 handle1.MakeWeak(NULL, NewPersistentHandleCallback);
9648 handle2.Dispose();
Steve Block44f0eee2011-05-26 01:26:41 +01009649 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +00009650}
9651
9652
9653v8::Persistent<v8::Object> to_be_disposed;
9654
9655void DisposeAndForceGcCallback(v8::Persistent<v8::Value> handle, void*) {
9656 to_be_disposed.Dispose();
Steve Block44f0eee2011-05-26 01:26:41 +01009657 HEAP->CollectAllGarbage(false);
Kristian Monsen50ef84f2010-07-29 15:18:00 +01009658 handle.Dispose();
Steve Blocka7e24c12009-10-30 11:49:00 +00009659}
9660
9661
9662THREADED_TEST(DoNotUseDeletedNodesInSecondLevelGc) {
9663 LocalContext context;
9664
9665 v8::Persistent<v8::Object> handle1, handle2;
9666 {
9667 v8::HandleScope scope;
9668 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
9669 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
9670 }
9671 handle1.MakeWeak(NULL, DisposeAndForceGcCallback);
9672 to_be_disposed = handle2;
Steve Block44f0eee2011-05-26 01:26:41 +01009673 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +00009674}
9675
Steve Blockd0582a62009-12-15 09:54:21 +00009676void DisposingCallback(v8::Persistent<v8::Value> handle, void*) {
9677 handle.Dispose();
9678}
9679
9680void HandleCreatingCallback(v8::Persistent<v8::Value> handle, void*) {
9681 v8::HandleScope scope;
9682 v8::Persistent<v8::Object>::New(v8::Object::New());
Kristian Monsen50ef84f2010-07-29 15:18:00 +01009683 handle.Dispose();
Steve Blockd0582a62009-12-15 09:54:21 +00009684}
9685
9686
9687THREADED_TEST(NoGlobalHandlesOrphaningDueToWeakCallback) {
9688 LocalContext context;
9689
9690 v8::Persistent<v8::Object> handle1, handle2, handle3;
9691 {
9692 v8::HandleScope scope;
9693 handle3 = v8::Persistent<v8::Object>::New(v8::Object::New());
9694 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
9695 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
9696 }
9697 handle2.MakeWeak(NULL, DisposingCallback);
9698 handle3.MakeWeak(NULL, HandleCreatingCallback);
Steve Block44f0eee2011-05-26 01:26:41 +01009699 HEAP->CollectAllGarbage(false);
Steve Blockd0582a62009-12-15 09:54:21 +00009700}
9701
Steve Blocka7e24c12009-10-30 11:49:00 +00009702
9703THREADED_TEST(CheckForCrossContextObjectLiterals) {
9704 v8::V8::Initialize();
9705
9706 const int nof = 2;
9707 const char* sources[nof] = {
9708 "try { [ 2, 3, 4 ].forEach(5); } catch(e) { e.toString(); }",
9709 "Object()"
9710 };
9711
9712 for (int i = 0; i < nof; i++) {
9713 const char* source = sources[i];
9714 { v8::HandleScope scope;
9715 LocalContext context;
9716 CompileRun(source);
9717 }
9718 { v8::HandleScope scope;
9719 LocalContext context;
9720 CompileRun(source);
9721 }
9722 }
9723}
9724
9725
9726static v8::Handle<Value> NestedScope(v8::Persistent<Context> env) {
9727 v8::HandleScope inner;
9728 env->Enter();
9729 v8::Handle<Value> three = v8_num(3);
9730 v8::Handle<Value> value = inner.Close(three);
9731 env->Exit();
9732 return value;
9733}
9734
9735
9736THREADED_TEST(NestedHandleScopeAndContexts) {
9737 v8::HandleScope outer;
9738 v8::Persistent<Context> env = Context::New();
9739 env->Enter();
9740 v8::Handle<Value> value = NestedScope(env);
9741 v8::Handle<String> str = value->ToString();
9742 env->Exit();
9743 env.Dispose();
9744}
9745
9746
9747THREADED_TEST(ExternalAllocatedMemory) {
9748 v8::HandleScope outer;
9749 v8::Persistent<Context> env = Context::New();
9750 const int kSize = 1024*1024;
9751 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(kSize), kSize);
9752 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(-kSize), 0);
9753}
9754
9755
9756THREADED_TEST(DisposeEnteredContext) {
9757 v8::HandleScope scope;
9758 LocalContext outer;
9759 { v8::Persistent<v8::Context> inner = v8::Context::New();
9760 inner->Enter();
9761 inner.Dispose();
9762 inner.Clear();
9763 inner->Exit();
9764 }
9765}
9766
9767
9768// Regression test for issue 54, object templates with internal fields
9769// but no accessors or interceptors did not get their internal field
9770// count set on instances.
9771THREADED_TEST(Regress54) {
9772 v8::HandleScope outer;
9773 LocalContext context;
9774 static v8::Persistent<v8::ObjectTemplate> templ;
9775 if (templ.IsEmpty()) {
9776 v8::HandleScope inner;
9777 v8::Handle<v8::ObjectTemplate> local = v8::ObjectTemplate::New();
9778 local->SetInternalFieldCount(1);
9779 templ = v8::Persistent<v8::ObjectTemplate>::New(inner.Close(local));
9780 }
9781 v8::Handle<v8::Object> result = templ->NewInstance();
9782 CHECK_EQ(1, result->InternalFieldCount());
9783}
9784
9785
9786// If part of the threaded tests, this test makes ThreadingTest fail
9787// on mac.
9788TEST(CatchStackOverflow) {
9789 v8::HandleScope scope;
9790 LocalContext context;
9791 v8::TryCatch try_catch;
9792 v8::Handle<v8::Script> script = v8::Script::Compile(v8::String::New(
9793 "function f() {"
9794 " return f();"
9795 "}"
9796 ""
9797 "f();"));
9798 v8::Handle<v8::Value> result = script->Run();
9799 CHECK(result.IsEmpty());
9800}
9801
9802
9803static void CheckTryCatchSourceInfo(v8::Handle<v8::Script> script,
9804 const char* resource_name,
9805 int line_offset) {
9806 v8::HandleScope scope;
9807 v8::TryCatch try_catch;
9808 v8::Handle<v8::Value> result = script->Run();
9809 CHECK(result.IsEmpty());
9810 CHECK(try_catch.HasCaught());
9811 v8::Handle<v8::Message> message = try_catch.Message();
9812 CHECK(!message.IsEmpty());
9813 CHECK_EQ(10 + line_offset, message->GetLineNumber());
9814 CHECK_EQ(91, message->GetStartPosition());
9815 CHECK_EQ(92, message->GetEndPosition());
9816 CHECK_EQ(2, message->GetStartColumn());
9817 CHECK_EQ(3, message->GetEndColumn());
9818 v8::String::AsciiValue line(message->GetSourceLine());
9819 CHECK_EQ(" throw 'nirk';", *line);
9820 v8::String::AsciiValue name(message->GetScriptResourceName());
9821 CHECK_EQ(resource_name, *name);
9822}
9823
9824
9825THREADED_TEST(TryCatchSourceInfo) {
9826 v8::HandleScope scope;
9827 LocalContext context;
9828 v8::Handle<v8::String> source = v8::String::New(
9829 "function Foo() {\n"
9830 " return Bar();\n"
9831 "}\n"
9832 "\n"
9833 "function Bar() {\n"
9834 " return Baz();\n"
9835 "}\n"
9836 "\n"
9837 "function Baz() {\n"
9838 " throw 'nirk';\n"
9839 "}\n"
9840 "\n"
9841 "Foo();\n");
9842
9843 const char* resource_name;
9844 v8::Handle<v8::Script> script;
9845 resource_name = "test.js";
9846 script = v8::Script::Compile(source, v8::String::New(resource_name));
9847 CheckTryCatchSourceInfo(script, resource_name, 0);
9848
9849 resource_name = "test1.js";
9850 v8::ScriptOrigin origin1(v8::String::New(resource_name));
9851 script = v8::Script::Compile(source, &origin1);
9852 CheckTryCatchSourceInfo(script, resource_name, 0);
9853
9854 resource_name = "test2.js";
9855 v8::ScriptOrigin origin2(v8::String::New(resource_name), v8::Integer::New(7));
9856 script = v8::Script::Compile(source, &origin2);
9857 CheckTryCatchSourceInfo(script, resource_name, 7);
9858}
9859
9860
9861THREADED_TEST(CompilationCache) {
9862 v8::HandleScope scope;
9863 LocalContext context;
9864 v8::Handle<v8::String> source0 = v8::String::New("1234");
9865 v8::Handle<v8::String> source1 = v8::String::New("1234");
9866 v8::Handle<v8::Script> script0 =
9867 v8::Script::Compile(source0, v8::String::New("test.js"));
9868 v8::Handle<v8::Script> script1 =
9869 v8::Script::Compile(source1, v8::String::New("test.js"));
9870 v8::Handle<v8::Script> script2 =
9871 v8::Script::Compile(source0); // different origin
9872 CHECK_EQ(1234, script0->Run()->Int32Value());
9873 CHECK_EQ(1234, script1->Run()->Int32Value());
9874 CHECK_EQ(1234, script2->Run()->Int32Value());
9875}
9876
9877
9878static v8::Handle<Value> FunctionNameCallback(const v8::Arguments& args) {
9879 ApiTestFuzzer::Fuzz();
9880 return v8_num(42);
9881}
9882
9883
9884THREADED_TEST(CallbackFunctionName) {
9885 v8::HandleScope scope;
9886 LocalContext context;
9887 Local<ObjectTemplate> t = ObjectTemplate::New();
9888 t->Set(v8_str("asdf"), v8::FunctionTemplate::New(FunctionNameCallback));
9889 context->Global()->Set(v8_str("obj"), t->NewInstance());
9890 v8::Handle<v8::Value> value = CompileRun("obj.asdf.name");
9891 CHECK(value->IsString());
9892 v8::String::AsciiValue name(value);
9893 CHECK_EQ("asdf", *name);
9894}
9895
9896
9897THREADED_TEST(DateAccess) {
9898 v8::HandleScope scope;
9899 LocalContext context;
9900 v8::Handle<v8::Value> date = v8::Date::New(1224744689038.0);
9901 CHECK(date->IsDate());
Steve Block6ded16b2010-05-10 14:33:55 +01009902 CHECK_EQ(1224744689038.0, date.As<v8::Date>()->NumberValue());
Steve Blocka7e24c12009-10-30 11:49:00 +00009903}
9904
9905
9906void CheckProperties(v8::Handle<v8::Value> val, int elmc, const char* elmv[]) {
Steve Block6ded16b2010-05-10 14:33:55 +01009907 v8::Handle<v8::Object> obj = val.As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00009908 v8::Handle<v8::Array> props = obj->GetPropertyNames();
9909 CHECK_EQ(elmc, props->Length());
9910 for (int i = 0; i < elmc; i++) {
9911 v8::String::Utf8Value elm(props->Get(v8::Integer::New(i)));
9912 CHECK_EQ(elmv[i], *elm);
9913 }
9914}
9915
9916
9917THREADED_TEST(PropertyEnumeration) {
9918 v8::HandleScope scope;
9919 LocalContext context;
9920 v8::Handle<v8::Value> obj = v8::Script::Compile(v8::String::New(
9921 "var result = [];"
9922 "result[0] = {};"
9923 "result[1] = {a: 1, b: 2};"
9924 "result[2] = [1, 2, 3];"
9925 "var proto = {x: 1, y: 2, z: 3};"
9926 "var x = { __proto__: proto, w: 0, z: 1 };"
9927 "result[3] = x;"
9928 "result;"))->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01009929 v8::Handle<v8::Array> elms = obj.As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00009930 CHECK_EQ(4, elms->Length());
9931 int elmc0 = 0;
9932 const char** elmv0 = NULL;
9933 CheckProperties(elms->Get(v8::Integer::New(0)), elmc0, elmv0);
9934 int elmc1 = 2;
9935 const char* elmv1[] = {"a", "b"};
9936 CheckProperties(elms->Get(v8::Integer::New(1)), elmc1, elmv1);
9937 int elmc2 = 3;
9938 const char* elmv2[] = {"0", "1", "2"};
9939 CheckProperties(elms->Get(v8::Integer::New(2)), elmc2, elmv2);
9940 int elmc3 = 4;
9941 const char* elmv3[] = {"w", "z", "x", "y"};
9942 CheckProperties(elms->Get(v8::Integer::New(3)), elmc3, elmv3);
9943}
9944
Steve Block44f0eee2011-05-26 01:26:41 +01009945THREADED_TEST(PropertyEnumeration2) {
9946 v8::HandleScope scope;
9947 LocalContext context;
9948 v8::Handle<v8::Value> obj = v8::Script::Compile(v8::String::New(
9949 "var result = [];"
9950 "result[0] = {};"
9951 "result[1] = {a: 1, b: 2};"
9952 "result[2] = [1, 2, 3];"
9953 "var proto = {x: 1, y: 2, z: 3};"
9954 "var x = { __proto__: proto, w: 0, z: 1 };"
9955 "result[3] = x;"
9956 "result;"))->Run();
9957 v8::Handle<v8::Array> elms = obj.As<v8::Array>();
9958 CHECK_EQ(4, elms->Length());
9959 int elmc0 = 0;
9960 const char** elmv0 = NULL;
9961 CheckProperties(elms->Get(v8::Integer::New(0)), elmc0, elmv0);
9962
9963 v8::Handle<v8::Value> val = elms->Get(v8::Integer::New(0));
9964 v8::Handle<v8::Array> props = val.As<v8::Object>()->GetPropertyNames();
9965 CHECK_EQ(0, props->Length());
9966 for (uint32_t i = 0; i < props->Length(); i++) {
9967 printf("p[%d]\n", i);
9968 }
9969}
Steve Blocka7e24c12009-10-30 11:49:00 +00009970
Steve Blocka7e24c12009-10-30 11:49:00 +00009971static bool NamedSetAccessBlocker(Local<v8::Object> obj,
9972 Local<Value> name,
9973 v8::AccessType type,
9974 Local<Value> data) {
9975 return type != v8::ACCESS_SET;
9976}
9977
9978
9979static bool IndexedSetAccessBlocker(Local<v8::Object> obj,
9980 uint32_t key,
9981 v8::AccessType type,
9982 Local<Value> data) {
9983 return type != v8::ACCESS_SET;
9984}
9985
9986
9987THREADED_TEST(DisableAccessChecksWhileConfiguring) {
9988 v8::HandleScope scope;
9989 LocalContext context;
9990 Local<ObjectTemplate> templ = ObjectTemplate::New();
9991 templ->SetAccessCheckCallbacks(NamedSetAccessBlocker,
9992 IndexedSetAccessBlocker);
9993 templ->Set(v8_str("x"), v8::True());
9994 Local<v8::Object> instance = templ->NewInstance();
9995 context->Global()->Set(v8_str("obj"), instance);
9996 Local<Value> value = CompileRun("obj.x");
9997 CHECK(value->BooleanValue());
9998}
9999
10000
10001static bool NamedGetAccessBlocker(Local<v8::Object> obj,
10002 Local<Value> name,
10003 v8::AccessType type,
10004 Local<Value> data) {
10005 return false;
10006}
10007
10008
10009static bool IndexedGetAccessBlocker(Local<v8::Object> obj,
10010 uint32_t key,
10011 v8::AccessType type,
10012 Local<Value> data) {
10013 return false;
10014}
10015
10016
10017
10018THREADED_TEST(AccessChecksReenabledCorrectly) {
10019 v8::HandleScope scope;
10020 LocalContext context;
10021 Local<ObjectTemplate> templ = ObjectTemplate::New();
10022 templ->SetAccessCheckCallbacks(NamedGetAccessBlocker,
10023 IndexedGetAccessBlocker);
10024 templ->Set(v8_str("a"), v8_str("a"));
10025 // Add more than 8 (see kMaxFastProperties) properties
10026 // so that the constructor will force copying map.
10027 // Cannot sprintf, gcc complains unsafety.
10028 char buf[4];
10029 for (char i = '0'; i <= '9' ; i++) {
10030 buf[0] = i;
10031 for (char j = '0'; j <= '9'; j++) {
10032 buf[1] = j;
10033 for (char k = '0'; k <= '9'; k++) {
10034 buf[2] = k;
10035 buf[3] = 0;
10036 templ->Set(v8_str(buf), v8::Number::New(k));
10037 }
10038 }
10039 }
10040
10041 Local<v8::Object> instance_1 = templ->NewInstance();
10042 context->Global()->Set(v8_str("obj_1"), instance_1);
10043
10044 Local<Value> value_1 = CompileRun("obj_1.a");
10045 CHECK(value_1->IsUndefined());
10046
10047 Local<v8::Object> instance_2 = templ->NewInstance();
10048 context->Global()->Set(v8_str("obj_2"), instance_2);
10049
10050 Local<Value> value_2 = CompileRun("obj_2.a");
10051 CHECK(value_2->IsUndefined());
10052}
10053
10054
10055// This tests that access check information remains on the global
10056// object template when creating contexts.
10057THREADED_TEST(AccessControlRepeatedContextCreation) {
10058 v8::HandleScope handle_scope;
10059 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
10060 global_template->SetAccessCheckCallbacks(NamedSetAccessBlocker,
10061 IndexedSetAccessBlocker);
10062 i::Handle<i::ObjectTemplateInfo> internal_template =
10063 v8::Utils::OpenHandle(*global_template);
10064 CHECK(!internal_template->constructor()->IsUndefined());
10065 i::Handle<i::FunctionTemplateInfo> constructor(
10066 i::FunctionTemplateInfo::cast(internal_template->constructor()));
10067 CHECK(!constructor->access_check_info()->IsUndefined());
10068 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
10069 CHECK(!constructor->access_check_info()->IsUndefined());
10070}
10071
10072
10073THREADED_TEST(TurnOnAccessCheck) {
10074 v8::HandleScope handle_scope;
10075
10076 // Create an environment with access check to the global object disabled by
10077 // default.
10078 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
10079 global_template->SetAccessCheckCallbacks(NamedGetAccessBlocker,
10080 IndexedGetAccessBlocker,
10081 v8::Handle<v8::Value>(),
10082 false);
10083 v8::Persistent<Context> context = Context::New(NULL, global_template);
10084 Context::Scope context_scope(context);
10085
10086 // Set up a property and a number of functions.
10087 context->Global()->Set(v8_str("a"), v8_num(1));
10088 CompileRun("function f1() {return a;}"
10089 "function f2() {return a;}"
10090 "function g1() {return h();}"
10091 "function g2() {return h();}"
10092 "function h() {return 1;}");
10093 Local<Function> f1 =
10094 Local<Function>::Cast(context->Global()->Get(v8_str("f1")));
10095 Local<Function> f2 =
10096 Local<Function>::Cast(context->Global()->Get(v8_str("f2")));
10097 Local<Function> g1 =
10098 Local<Function>::Cast(context->Global()->Get(v8_str("g1")));
10099 Local<Function> g2 =
10100 Local<Function>::Cast(context->Global()->Get(v8_str("g2")));
10101 Local<Function> h =
10102 Local<Function>::Cast(context->Global()->Get(v8_str("h")));
10103
10104 // Get the global object.
10105 v8::Handle<v8::Object> global = context->Global();
10106
10107 // Call f1 one time and f2 a number of times. This will ensure that f1 still
10108 // uses the runtime system to retreive property a whereas f2 uses global load
10109 // inline cache.
10110 CHECK(f1->Call(global, 0, NULL)->Equals(v8_num(1)));
10111 for (int i = 0; i < 4; i++) {
10112 CHECK(f2->Call(global, 0, NULL)->Equals(v8_num(1)));
10113 }
10114
10115 // Same for g1 and g2.
10116 CHECK(g1->Call(global, 0, NULL)->Equals(v8_num(1)));
10117 for (int i = 0; i < 4; i++) {
10118 CHECK(g2->Call(global, 0, NULL)->Equals(v8_num(1)));
10119 }
10120
10121 // Detach the global and turn on access check.
10122 context->DetachGlobal();
10123 context->Global()->TurnOnAccessCheck();
10124
10125 // Failing access check to property get results in undefined.
10126 CHECK(f1->Call(global, 0, NULL)->IsUndefined());
10127 CHECK(f2->Call(global, 0, NULL)->IsUndefined());
10128
10129 // Failing access check to function call results in exception.
10130 CHECK(g1->Call(global, 0, NULL).IsEmpty());
10131 CHECK(g2->Call(global, 0, NULL).IsEmpty());
10132
10133 // No failing access check when just returning a constant.
10134 CHECK(h->Call(global, 0, NULL)->Equals(v8_num(1)));
10135}
10136
10137
Ben Murdochb0fe1622011-05-05 13:52:32 +010010138v8::Handle<v8::String> a;
10139v8::Handle<v8::String> h;
10140
10141static bool NamedGetAccessBlockAandH(Local<v8::Object> obj,
10142 Local<Value> name,
10143 v8::AccessType type,
10144 Local<Value> data) {
10145 return !(name->Equals(a) || name->Equals(h));
10146}
10147
10148
10149THREADED_TEST(TurnOnAccessCheckAndRecompile) {
10150 v8::HandleScope handle_scope;
10151
10152 // Create an environment with access check to the global object disabled by
10153 // default. When the registered access checker will block access to properties
10154 // a and h
10155 a = v8_str("a");
10156 h = v8_str("h");
10157 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
10158 global_template->SetAccessCheckCallbacks(NamedGetAccessBlockAandH,
10159 IndexedGetAccessBlocker,
10160 v8::Handle<v8::Value>(),
10161 false);
10162 v8::Persistent<Context> context = Context::New(NULL, global_template);
10163 Context::Scope context_scope(context);
10164
10165 // Set up a property and a number of functions.
10166 context->Global()->Set(v8_str("a"), v8_num(1));
10167 static const char* source = "function f1() {return a;}"
10168 "function f2() {return a;}"
10169 "function g1() {return h();}"
10170 "function g2() {return h();}"
10171 "function h() {return 1;}";
10172
10173 CompileRun(source);
10174 Local<Function> f1;
10175 Local<Function> f2;
10176 Local<Function> g1;
10177 Local<Function> g2;
10178 Local<Function> h;
10179 f1 = Local<Function>::Cast(context->Global()->Get(v8_str("f1")));
10180 f2 = Local<Function>::Cast(context->Global()->Get(v8_str("f2")));
10181 g1 = Local<Function>::Cast(context->Global()->Get(v8_str("g1")));
10182 g2 = Local<Function>::Cast(context->Global()->Get(v8_str("g2")));
10183 h = Local<Function>::Cast(context->Global()->Get(v8_str("h")));
10184
10185 // Get the global object.
10186 v8::Handle<v8::Object> global = context->Global();
10187
10188 // Call f1 one time and f2 a number of times. This will ensure that f1 still
10189 // uses the runtime system to retreive property a whereas f2 uses global load
10190 // inline cache.
10191 CHECK(f1->Call(global, 0, NULL)->Equals(v8_num(1)));
10192 for (int i = 0; i < 4; i++) {
10193 CHECK(f2->Call(global, 0, NULL)->Equals(v8_num(1)));
10194 }
10195
10196 // Same for g1 and g2.
10197 CHECK(g1->Call(global, 0, NULL)->Equals(v8_num(1)));
10198 for (int i = 0; i < 4; i++) {
10199 CHECK(g2->Call(global, 0, NULL)->Equals(v8_num(1)));
10200 }
10201
10202 // Detach the global and turn on access check now blocking access to property
10203 // a and function h.
10204 context->DetachGlobal();
10205 context->Global()->TurnOnAccessCheck();
10206
10207 // Failing access check to property get results in undefined.
10208 CHECK(f1->Call(global, 0, NULL)->IsUndefined());
10209 CHECK(f2->Call(global, 0, NULL)->IsUndefined());
10210
10211 // Failing access check to function call results in exception.
10212 CHECK(g1->Call(global, 0, NULL).IsEmpty());
10213 CHECK(g2->Call(global, 0, NULL).IsEmpty());
10214
10215 // No failing access check when just returning a constant.
10216 CHECK(h->Call(global, 0, NULL)->Equals(v8_num(1)));
10217
10218 // Now compile the source again. And get the newly compiled functions, except
10219 // for h for which access is blocked.
10220 CompileRun(source);
10221 f1 = Local<Function>::Cast(context->Global()->Get(v8_str("f1")));
10222 f2 = Local<Function>::Cast(context->Global()->Get(v8_str("f2")));
10223 g1 = Local<Function>::Cast(context->Global()->Get(v8_str("g1")));
10224 g2 = Local<Function>::Cast(context->Global()->Get(v8_str("g2")));
10225 CHECK(context->Global()->Get(v8_str("h"))->IsUndefined());
10226
10227 // Failing access check to property get results in undefined.
10228 CHECK(f1->Call(global, 0, NULL)->IsUndefined());
10229 CHECK(f2->Call(global, 0, NULL)->IsUndefined());
10230
10231 // Failing access check to function call results in exception.
10232 CHECK(g1->Call(global, 0, NULL).IsEmpty());
10233 CHECK(g2->Call(global, 0, NULL).IsEmpty());
10234}
10235
10236
Steve Blocka7e24c12009-10-30 11:49:00 +000010237// This test verifies that pre-compilation (aka preparsing) can be called
10238// without initializing the whole VM. Thus we cannot run this test in a
10239// multi-threaded setup.
10240TEST(PreCompile) {
10241 // TODO(155): This test would break without the initialization of V8. This is
10242 // a workaround for now to make this test not fail.
10243 v8::V8::Initialize();
Leon Clarkef7060e22010-06-03 12:02:55 +010010244 const char* script = "function foo(a) { return a+1; }";
10245 v8::ScriptData* sd =
Steve Blockd0582a62009-12-15 09:54:21 +000010246 v8::ScriptData::PreCompile(script, i::StrLength(script));
Steve Blocka7e24c12009-10-30 11:49:00 +000010247 CHECK_NE(sd->Length(), 0);
10248 CHECK_NE(sd->Data(), NULL);
Leon Clarkee46be812010-01-19 14:06:41 +000010249 CHECK(!sd->HasError());
10250 delete sd;
10251}
10252
10253
10254TEST(PreCompileWithError) {
10255 v8::V8::Initialize();
Leon Clarkef7060e22010-06-03 12:02:55 +010010256 const char* script = "function foo(a) { return 1 * * 2; }";
10257 v8::ScriptData* sd =
Leon Clarkee46be812010-01-19 14:06:41 +000010258 v8::ScriptData::PreCompile(script, i::StrLength(script));
10259 CHECK(sd->HasError());
10260 delete sd;
10261}
10262
10263
10264TEST(Regress31661) {
10265 v8::V8::Initialize();
Leon Clarkef7060e22010-06-03 12:02:55 +010010266 const char* script = " The Definintive Guide";
10267 v8::ScriptData* sd =
Leon Clarkee46be812010-01-19 14:06:41 +000010268 v8::ScriptData::PreCompile(script, i::StrLength(script));
10269 CHECK(sd->HasError());
Steve Blocka7e24c12009-10-30 11:49:00 +000010270 delete sd;
10271}
10272
10273
Leon Clarkef7060e22010-06-03 12:02:55 +010010274// Tests that ScriptData can be serialized and deserialized.
10275TEST(PreCompileSerialization) {
10276 v8::V8::Initialize();
10277 const char* script = "function foo(a) { return a+1; }";
10278 v8::ScriptData* sd =
10279 v8::ScriptData::PreCompile(script, i::StrLength(script));
10280
10281 // Serialize.
10282 int serialized_data_length = sd->Length();
10283 char* serialized_data = i::NewArray<char>(serialized_data_length);
10284 memcpy(serialized_data, sd->Data(), serialized_data_length);
10285
10286 // Deserialize.
10287 v8::ScriptData* deserialized_sd =
10288 v8::ScriptData::New(serialized_data, serialized_data_length);
10289
10290 // Verify that the original is the same as the deserialized.
10291 CHECK_EQ(sd->Length(), deserialized_sd->Length());
10292 CHECK_EQ(0, memcmp(sd->Data(), deserialized_sd->Data(), sd->Length()));
10293 CHECK_EQ(sd->HasError(), deserialized_sd->HasError());
10294
10295 delete sd;
10296 delete deserialized_sd;
10297}
10298
10299
10300// Attempts to deserialize bad data.
10301TEST(PreCompileDeserializationError) {
10302 v8::V8::Initialize();
10303 const char* data = "DONT CARE";
10304 int invalid_size = 3;
10305 v8::ScriptData* sd = v8::ScriptData::New(data, invalid_size);
10306
10307 CHECK_EQ(0, sd->Length());
10308
10309 delete sd;
10310}
10311
10312
Leon Clarkeac952652010-07-15 11:15:24 +010010313// Attempts to deserialize bad data.
10314TEST(PreCompileInvalidPreparseDataError) {
10315 v8::V8::Initialize();
10316 v8::HandleScope scope;
10317 LocalContext context;
10318
10319 const char* script = "function foo(){ return 5;}\n"
10320 "function bar(){ return 6 + 7;} foo();";
10321 v8::ScriptData* sd =
10322 v8::ScriptData::PreCompile(script, i::StrLength(script));
10323 CHECK(!sd->HasError());
10324 // ScriptDataImpl private implementation details
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -080010325 const int kHeaderSize = i::PreparseDataConstants::kHeaderSize;
Iain Merrick9ac36c92010-09-13 15:29:50 +010010326 const int kFunctionEntrySize = i::FunctionEntry::kSize;
Leon Clarkeac952652010-07-15 11:15:24 +010010327 const int kFunctionEntryStartOffset = 0;
10328 const int kFunctionEntryEndOffset = 1;
10329 unsigned* sd_data =
10330 reinterpret_cast<unsigned*>(const_cast<char*>(sd->Data()));
Leon Clarkeac952652010-07-15 11:15:24 +010010331
10332 // Overwrite function bar's end position with 0.
10333 sd_data[kHeaderSize + 1 * kFunctionEntrySize + kFunctionEntryEndOffset] = 0;
10334 v8::TryCatch try_catch;
10335
10336 Local<String> source = String::New(script);
10337 Local<Script> compiled_script = Script::New(source, NULL, sd);
10338 CHECK(try_catch.HasCaught());
10339 String::AsciiValue exception_value(try_catch.Message()->Get());
10340 CHECK_EQ("Uncaught SyntaxError: Invalid preparser data for function bar",
10341 *exception_value);
10342
10343 try_catch.Reset();
10344 // Overwrite function bar's start position with 200. The function entry
10345 // will not be found when searching for it by position.
Kristian Monsen80d68ea2010-09-08 11:05:35 +010010346 sd = v8::ScriptData::PreCompile(script, i::StrLength(script));
10347 sd_data = reinterpret_cast<unsigned*>(const_cast<char*>(sd->Data()));
Leon Clarkeac952652010-07-15 11:15:24 +010010348 sd_data[kHeaderSize + 1 * kFunctionEntrySize + kFunctionEntryStartOffset] =
10349 200;
10350 compiled_script = Script::New(source, NULL, sd);
10351 CHECK(try_catch.HasCaught());
10352 String::AsciiValue second_exception_value(try_catch.Message()->Get());
10353 CHECK_EQ("Uncaught SyntaxError: Invalid preparser data for function bar",
10354 *second_exception_value);
10355
10356 delete sd;
10357}
10358
10359
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +010010360// Verifies that the Handle<String> and const char* versions of the API produce
10361// the same results (at least for one trivial case).
10362TEST(PreCompileAPIVariationsAreSame) {
10363 v8::V8::Initialize();
10364 v8::HandleScope scope;
10365
10366 const char* cstring = "function foo(a) { return a+1; }";
Ben Murdoch3bec4d22010-07-22 14:51:16 +010010367
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +010010368 v8::ScriptData* sd_from_cstring =
10369 v8::ScriptData::PreCompile(cstring, i::StrLength(cstring));
10370
10371 TestAsciiResource* resource = new TestAsciiResource(cstring);
Ben Murdoch3bec4d22010-07-22 14:51:16 +010010372 v8::ScriptData* sd_from_external_string = v8::ScriptData::PreCompile(
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +010010373 v8::String::NewExternal(resource));
10374
Ben Murdoch3bec4d22010-07-22 14:51:16 +010010375 v8::ScriptData* sd_from_string = v8::ScriptData::PreCompile(
10376 v8::String::New(cstring));
10377
10378 CHECK_EQ(sd_from_cstring->Length(), sd_from_external_string->Length());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +010010379 CHECK_EQ(0, memcmp(sd_from_cstring->Data(),
Ben Murdoch3bec4d22010-07-22 14:51:16 +010010380 sd_from_external_string->Data(),
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +010010381 sd_from_cstring->Length()));
10382
Ben Murdoch3bec4d22010-07-22 14:51:16 +010010383 CHECK_EQ(sd_from_cstring->Length(), sd_from_string->Length());
10384 CHECK_EQ(0, memcmp(sd_from_cstring->Data(),
10385 sd_from_string->Data(),
10386 sd_from_cstring->Length()));
10387
10388
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +010010389 delete sd_from_cstring;
Ben Murdoch3bec4d22010-07-22 14:51:16 +010010390 delete sd_from_external_string;
10391 delete sd_from_string;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +010010392}
10393
10394
Steve Blocka7e24c12009-10-30 11:49:00 +000010395// This tests that we do not allow dictionary load/call inline caches
10396// to use functions that have not yet been compiled. The potential
10397// problem of loading a function that has not yet been compiled can
10398// arise because we share code between contexts via the compilation
10399// cache.
10400THREADED_TEST(DictionaryICLoadedFunction) {
10401 v8::HandleScope scope;
10402 // Test LoadIC.
10403 for (int i = 0; i < 2; i++) {
10404 LocalContext context;
10405 context->Global()->Set(v8_str("tmp"), v8::True());
10406 context->Global()->Delete(v8_str("tmp"));
10407 CompileRun("for (var j = 0; j < 10; j++) new RegExp('');");
10408 }
10409 // Test CallIC.
10410 for (int i = 0; i < 2; i++) {
10411 LocalContext context;
10412 context->Global()->Set(v8_str("tmp"), v8::True());
10413 context->Global()->Delete(v8_str("tmp"));
10414 CompileRun("for (var j = 0; j < 10; j++) RegExp('')");
10415 }
10416}
10417
10418
10419// Test that cross-context new calls use the context of the callee to
10420// create the new JavaScript object.
10421THREADED_TEST(CrossContextNew) {
10422 v8::HandleScope scope;
10423 v8::Persistent<Context> context0 = Context::New();
10424 v8::Persistent<Context> context1 = Context::New();
10425
10426 // Allow cross-domain access.
10427 Local<String> token = v8_str("<security token>");
10428 context0->SetSecurityToken(token);
10429 context1->SetSecurityToken(token);
10430
10431 // Set an 'x' property on the Object prototype and define a
10432 // constructor function in context0.
10433 context0->Enter();
10434 CompileRun("Object.prototype.x = 42; function C() {};");
10435 context0->Exit();
10436
10437 // Call the constructor function from context0 and check that the
10438 // result has the 'x' property.
10439 context1->Enter();
10440 context1->Global()->Set(v8_str("other"), context0->Global());
10441 Local<Value> value = CompileRun("var instance = new other.C(); instance.x");
10442 CHECK(value->IsInt32());
10443 CHECK_EQ(42, value->Int32Value());
10444 context1->Exit();
10445
10446 // Dispose the contexts to allow them to be garbage collected.
10447 context0.Dispose();
10448 context1.Dispose();
10449}
10450
10451
10452class RegExpInterruptTest {
10453 public:
10454 RegExpInterruptTest() : block_(NULL) {}
10455 ~RegExpInterruptTest() { delete block_; }
10456 void RunTest() {
10457 block_ = i::OS::CreateSemaphore(0);
10458 gc_count_ = 0;
10459 gc_during_regexp_ = 0;
10460 regexp_success_ = false;
10461 gc_success_ = false;
Steve Block44f0eee2011-05-26 01:26:41 +010010462 GCThread gc_thread(i::Isolate::Current(), this);
Steve Blocka7e24c12009-10-30 11:49:00 +000010463 gc_thread.Start();
10464 v8::Locker::StartPreemption(1);
10465
10466 LongRunningRegExp();
10467 {
10468 v8::Unlocker unlock;
10469 gc_thread.Join();
10470 }
10471 v8::Locker::StopPreemption();
10472 CHECK(regexp_success_);
10473 CHECK(gc_success_);
10474 }
10475 private:
10476 // Number of garbage collections required.
10477 static const int kRequiredGCs = 5;
10478
10479 class GCThread : public i::Thread {
10480 public:
Steve Block44f0eee2011-05-26 01:26:41 +010010481 explicit GCThread(i::Isolate* isolate, RegExpInterruptTest* test)
10482 : Thread(isolate, "GCThread"), test_(test) {}
Steve Blocka7e24c12009-10-30 11:49:00 +000010483 virtual void Run() {
10484 test_->CollectGarbage();
10485 }
10486 private:
10487 RegExpInterruptTest* test_;
10488 };
10489
10490 void CollectGarbage() {
10491 block_->Wait();
10492 while (gc_during_regexp_ < kRequiredGCs) {
10493 {
10494 v8::Locker lock;
10495 // TODO(lrn): Perhaps create some garbage before collecting.
Steve Block44f0eee2011-05-26 01:26:41 +010010496 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +000010497 gc_count_++;
10498 }
10499 i::OS::Sleep(1);
10500 }
10501 gc_success_ = true;
10502 }
10503
10504 void LongRunningRegExp() {
10505 block_->Signal(); // Enable garbage collection thread on next preemption.
10506 int rounds = 0;
10507 while (gc_during_regexp_ < kRequiredGCs) {
10508 int gc_before = gc_count_;
10509 {
10510 // Match 15-30 "a"'s against 14 and a "b".
10511 const char* c_source =
10512 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
10513 ".exec('aaaaaaaaaaaaaaab') === null";
10514 Local<String> source = String::New(c_source);
10515 Local<Script> script = Script::Compile(source);
10516 Local<Value> result = script->Run();
10517 if (!result->BooleanValue()) {
10518 gc_during_regexp_ = kRequiredGCs; // Allow gc thread to exit.
10519 return;
10520 }
10521 }
10522 {
10523 // Match 15-30 "a"'s against 15 and a "b".
10524 const char* c_source =
10525 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
10526 ".exec('aaaaaaaaaaaaaaaab')[0] === 'aaaaaaaaaaaaaaaa'";
10527 Local<String> source = String::New(c_source);
10528 Local<Script> script = Script::Compile(source);
10529 Local<Value> result = script->Run();
10530 if (!result->BooleanValue()) {
10531 gc_during_regexp_ = kRequiredGCs;
10532 return;
10533 }
10534 }
10535 int gc_after = gc_count_;
10536 gc_during_regexp_ += gc_after - gc_before;
10537 rounds++;
10538 i::OS::Sleep(1);
10539 }
10540 regexp_success_ = true;
10541 }
10542
10543 i::Semaphore* block_;
10544 int gc_count_;
10545 int gc_during_regexp_;
10546 bool regexp_success_;
10547 bool gc_success_;
10548};
10549
10550
10551// Test that a regular expression execution can be interrupted and
10552// survive a garbage collection.
10553TEST(RegExpInterruption) {
10554 v8::Locker lock;
10555 v8::V8::Initialize();
10556 v8::HandleScope scope;
10557 Local<Context> local_env;
10558 {
10559 LocalContext env;
10560 local_env = env.local();
10561 }
10562
10563 // Local context should still be live.
10564 CHECK(!local_env.IsEmpty());
10565 local_env->Enter();
10566
10567 // Should complete without problems.
10568 RegExpInterruptTest().RunTest();
10569
10570 local_env->Exit();
10571}
10572
10573
10574class ApplyInterruptTest {
10575 public:
10576 ApplyInterruptTest() : block_(NULL) {}
10577 ~ApplyInterruptTest() { delete block_; }
10578 void RunTest() {
10579 block_ = i::OS::CreateSemaphore(0);
10580 gc_count_ = 0;
10581 gc_during_apply_ = 0;
10582 apply_success_ = false;
10583 gc_success_ = false;
Steve Block44f0eee2011-05-26 01:26:41 +010010584 GCThread gc_thread(i::Isolate::Current(), this);
Steve Blocka7e24c12009-10-30 11:49:00 +000010585 gc_thread.Start();
10586 v8::Locker::StartPreemption(1);
10587
10588 LongRunningApply();
10589 {
10590 v8::Unlocker unlock;
10591 gc_thread.Join();
10592 }
10593 v8::Locker::StopPreemption();
10594 CHECK(apply_success_);
10595 CHECK(gc_success_);
10596 }
10597 private:
10598 // Number of garbage collections required.
10599 static const int kRequiredGCs = 2;
10600
10601 class GCThread : public i::Thread {
10602 public:
Steve Block44f0eee2011-05-26 01:26:41 +010010603 explicit GCThread(i::Isolate* isolate, ApplyInterruptTest* test)
10604 : Thread(isolate, "GCThread"), test_(test) {}
Steve Blocka7e24c12009-10-30 11:49:00 +000010605 virtual void Run() {
10606 test_->CollectGarbage();
10607 }
10608 private:
10609 ApplyInterruptTest* test_;
10610 };
10611
10612 void CollectGarbage() {
10613 block_->Wait();
10614 while (gc_during_apply_ < kRequiredGCs) {
10615 {
10616 v8::Locker lock;
Steve Block44f0eee2011-05-26 01:26:41 +010010617 HEAP->CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +000010618 gc_count_++;
10619 }
10620 i::OS::Sleep(1);
10621 }
10622 gc_success_ = true;
10623 }
10624
10625 void LongRunningApply() {
10626 block_->Signal();
10627 int rounds = 0;
10628 while (gc_during_apply_ < kRequiredGCs) {
10629 int gc_before = gc_count_;
10630 {
10631 const char* c_source =
10632 "function do_very_little(bar) {"
10633 " this.foo = bar;"
10634 "}"
10635 "for (var i = 0; i < 100000; i++) {"
10636 " do_very_little.apply(this, ['bar']);"
10637 "}";
10638 Local<String> source = String::New(c_source);
10639 Local<Script> script = Script::Compile(source);
10640 Local<Value> result = script->Run();
10641 // Check that no exception was thrown.
10642 CHECK(!result.IsEmpty());
10643 }
10644 int gc_after = gc_count_;
10645 gc_during_apply_ += gc_after - gc_before;
10646 rounds++;
10647 }
10648 apply_success_ = true;
10649 }
10650
10651 i::Semaphore* block_;
10652 int gc_count_;
10653 int gc_during_apply_;
10654 bool apply_success_;
10655 bool gc_success_;
10656};
10657
10658
10659// Test that nothing bad happens if we get a preemption just when we were
10660// about to do an apply().
10661TEST(ApplyInterruption) {
10662 v8::Locker lock;
10663 v8::V8::Initialize();
10664 v8::HandleScope scope;
10665 Local<Context> local_env;
10666 {
10667 LocalContext env;
10668 local_env = env.local();
10669 }
10670
10671 // Local context should still be live.
10672 CHECK(!local_env.IsEmpty());
10673 local_env->Enter();
10674
10675 // Should complete without problems.
10676 ApplyInterruptTest().RunTest();
10677
10678 local_env->Exit();
10679}
10680
10681
10682// Verify that we can clone an object
10683TEST(ObjectClone) {
10684 v8::HandleScope scope;
10685 LocalContext env;
10686
10687 const char* sample =
10688 "var rv = {};" \
10689 "rv.alpha = 'hello';" \
10690 "rv.beta = 123;" \
10691 "rv;";
10692
10693 // Create an object, verify basics.
10694 Local<Value> val = CompileRun(sample);
10695 CHECK(val->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +010010696 Local<v8::Object> obj = val.As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +000010697 obj->Set(v8_str("gamma"), v8_str("cloneme"));
10698
10699 CHECK_EQ(v8_str("hello"), obj->Get(v8_str("alpha")));
10700 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta")));
10701 CHECK_EQ(v8_str("cloneme"), obj->Get(v8_str("gamma")));
10702
10703 // Clone it.
10704 Local<v8::Object> clone = obj->Clone();
10705 CHECK_EQ(v8_str("hello"), clone->Get(v8_str("alpha")));
10706 CHECK_EQ(v8::Integer::New(123), clone->Get(v8_str("beta")));
10707 CHECK_EQ(v8_str("cloneme"), clone->Get(v8_str("gamma")));
10708
10709 // Set a property on the clone, verify each object.
10710 clone->Set(v8_str("beta"), v8::Integer::New(456));
10711 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta")));
10712 CHECK_EQ(v8::Integer::New(456), clone->Get(v8_str("beta")));
10713}
10714
10715
10716class AsciiVectorResource : public v8::String::ExternalAsciiStringResource {
10717 public:
10718 explicit AsciiVectorResource(i::Vector<const char> vector)
10719 : data_(vector) {}
10720 virtual ~AsciiVectorResource() {}
10721 virtual size_t length() const { return data_.length(); }
10722 virtual const char* data() const { return data_.start(); }
10723 private:
10724 i::Vector<const char> data_;
10725};
10726
10727
10728class UC16VectorResource : public v8::String::ExternalStringResource {
10729 public:
10730 explicit UC16VectorResource(i::Vector<const i::uc16> vector)
10731 : data_(vector) {}
10732 virtual ~UC16VectorResource() {}
10733 virtual size_t length() const { return data_.length(); }
10734 virtual const i::uc16* data() const { return data_.start(); }
10735 private:
10736 i::Vector<const i::uc16> data_;
10737};
10738
10739
10740static void MorphAString(i::String* string,
10741 AsciiVectorResource* ascii_resource,
10742 UC16VectorResource* uc16_resource) {
10743 CHECK(i::StringShape(string).IsExternal());
10744 if (string->IsAsciiRepresentation()) {
10745 // Check old map is not symbol or long.
Steve Block44f0eee2011-05-26 01:26:41 +010010746 CHECK(string->map() == HEAP->external_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +000010747 // Morph external string to be TwoByte string.
Steve Block44f0eee2011-05-26 01:26:41 +010010748 string->set_map(HEAP->external_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +000010749 i::ExternalTwoByteString* morphed =
10750 i::ExternalTwoByteString::cast(string);
10751 morphed->set_resource(uc16_resource);
10752 } else {
10753 // Check old map is not symbol or long.
Steve Block44f0eee2011-05-26 01:26:41 +010010754 CHECK(string->map() == HEAP->external_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +000010755 // Morph external string to be ASCII string.
Steve Block44f0eee2011-05-26 01:26:41 +010010756 string->set_map(HEAP->external_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +000010757 i::ExternalAsciiString* morphed =
10758 i::ExternalAsciiString::cast(string);
10759 morphed->set_resource(ascii_resource);
10760 }
10761}
10762
10763
10764// Test that we can still flatten a string if the components it is built up
10765// from have been turned into 16 bit strings in the mean time.
10766THREADED_TEST(MorphCompositeStringTest) {
10767 const char* c_string = "Now is the time for all good men"
10768 " to come to the aid of the party";
10769 uint16_t* two_byte_string = AsciiToTwoByteString(c_string);
10770 {
10771 v8::HandleScope scope;
10772 LocalContext env;
10773 AsciiVectorResource ascii_resource(
Steve Blockd0582a62009-12-15 09:54:21 +000010774 i::Vector<const char>(c_string, i::StrLength(c_string)));
Steve Blocka7e24c12009-10-30 11:49:00 +000010775 UC16VectorResource uc16_resource(
Steve Blockd0582a62009-12-15 09:54:21 +000010776 i::Vector<const uint16_t>(two_byte_string,
10777 i::StrLength(c_string)));
Steve Blocka7e24c12009-10-30 11:49:00 +000010778
10779 Local<String> lhs(v8::Utils::ToLocal(
Steve Block44f0eee2011-05-26 01:26:41 +010010780 FACTORY->NewExternalStringFromAscii(&ascii_resource)));
Steve Blocka7e24c12009-10-30 11:49:00 +000010781 Local<String> rhs(v8::Utils::ToLocal(
Steve Block44f0eee2011-05-26 01:26:41 +010010782 FACTORY->NewExternalStringFromAscii(&ascii_resource)));
Steve Blocka7e24c12009-10-30 11:49:00 +000010783
10784 env->Global()->Set(v8_str("lhs"), lhs);
10785 env->Global()->Set(v8_str("rhs"), rhs);
10786
10787 CompileRun(
10788 "var cons = lhs + rhs;"
10789 "var slice = lhs.substring(1, lhs.length - 1);"
10790 "var slice_on_cons = (lhs + rhs).substring(1, lhs.length *2 - 1);");
10791
10792 MorphAString(*v8::Utils::OpenHandle(*lhs), &ascii_resource, &uc16_resource);
10793 MorphAString(*v8::Utils::OpenHandle(*rhs), &ascii_resource, &uc16_resource);
10794
10795 // Now do some stuff to make sure the strings are flattened, etc.
10796 CompileRun(
10797 "/[^a-z]/.test(cons);"
10798 "/[^a-z]/.test(slice);"
10799 "/[^a-z]/.test(slice_on_cons);");
10800 const char* expected_cons =
10801 "Now is the time for all good men to come to the aid of the party"
10802 "Now is the time for all good men to come to the aid of the party";
10803 const char* expected_slice =
10804 "ow is the time for all good men to come to the aid of the part";
10805 const char* expected_slice_on_cons =
10806 "ow is the time for all good men to come to the aid of the party"
10807 "Now is the time for all good men to come to the aid of the part";
10808 CHECK_EQ(String::New(expected_cons),
10809 env->Global()->Get(v8_str("cons")));
10810 CHECK_EQ(String::New(expected_slice),
10811 env->Global()->Get(v8_str("slice")));
10812 CHECK_EQ(String::New(expected_slice_on_cons),
10813 env->Global()->Get(v8_str("slice_on_cons")));
10814 }
Ben Murdoch3bec4d22010-07-22 14:51:16 +010010815 i::DeleteArray(two_byte_string);
Steve Blocka7e24c12009-10-30 11:49:00 +000010816}
10817
10818
10819TEST(CompileExternalTwoByteSource) {
10820 v8::HandleScope scope;
10821 LocalContext context;
10822
10823 // This is a very short list of sources, which currently is to check for a
10824 // regression caused by r2703.
10825 const char* ascii_sources[] = {
10826 "0.5",
10827 "-0.5", // This mainly testes PushBack in the Scanner.
10828 "--0.5", // This mainly testes PushBack in the Scanner.
10829 NULL
10830 };
10831
10832 // Compile the sources as external two byte strings.
10833 for (int i = 0; ascii_sources[i] != NULL; i++) {
10834 uint16_t* two_byte_string = AsciiToTwoByteString(ascii_sources[i]);
10835 UC16VectorResource uc16_resource(
Steve Blockd0582a62009-12-15 09:54:21 +000010836 i::Vector<const uint16_t>(two_byte_string,
10837 i::StrLength(ascii_sources[i])));
Steve Blocka7e24c12009-10-30 11:49:00 +000010838 v8::Local<v8::String> source = v8::String::NewExternal(&uc16_resource);
10839 v8::Script::Compile(source);
Ben Murdoch3bec4d22010-07-22 14:51:16 +010010840 i::DeleteArray(two_byte_string);
Steve Blocka7e24c12009-10-30 11:49:00 +000010841 }
10842}
10843
10844
10845class RegExpStringModificationTest {
10846 public:
10847 RegExpStringModificationTest()
10848 : block_(i::OS::CreateSemaphore(0)),
10849 morphs_(0),
10850 morphs_during_regexp_(0),
10851 ascii_resource_(i::Vector<const char>("aaaaaaaaaaaaaab", 15)),
10852 uc16_resource_(i::Vector<const uint16_t>(two_byte_content_, 15)) {}
10853 ~RegExpStringModificationTest() { delete block_; }
10854 void RunTest() {
10855 regexp_success_ = false;
10856 morph_success_ = false;
10857
10858 // Initialize the contents of two_byte_content_ to be a uc16 representation
10859 // of "aaaaaaaaaaaaaab".
10860 for (int i = 0; i < 14; i++) {
10861 two_byte_content_[i] = 'a';
10862 }
10863 two_byte_content_[14] = 'b';
10864
10865 // Create the input string for the regexp - the one we are going to change
10866 // properties of.
Steve Block44f0eee2011-05-26 01:26:41 +010010867 input_ = FACTORY->NewExternalStringFromAscii(&ascii_resource_);
Steve Blocka7e24c12009-10-30 11:49:00 +000010868
10869 // Inject the input as a global variable.
10870 i::Handle<i::String> input_name =
Steve Block44f0eee2011-05-26 01:26:41 +010010871 FACTORY->NewStringFromAscii(i::Vector<const char>("input", 5));
10872 i::Isolate::Current()->global_context()->global()->SetProperty(
Ben Murdoche0cee9b2011-05-25 10:26:03 +010010873 *input_name,
10874 *input_,
10875 NONE,
10876 i::kNonStrictMode)->ToObjectChecked();
Steve Blocka7e24c12009-10-30 11:49:00 +000010877
Steve Block44f0eee2011-05-26 01:26:41 +010010878 MorphThread morph_thread(i::Isolate::Current(), this);
Steve Blocka7e24c12009-10-30 11:49:00 +000010879 morph_thread.Start();
10880 v8::Locker::StartPreemption(1);
10881 LongRunningRegExp();
10882 {
10883 v8::Unlocker unlock;
10884 morph_thread.Join();
10885 }
10886 v8::Locker::StopPreemption();
10887 CHECK(regexp_success_);
10888 CHECK(morph_success_);
10889 }
10890 private:
10891
10892 // Number of string modifications required.
10893 static const int kRequiredModifications = 5;
10894 static const int kMaxModifications = 100;
10895
10896 class MorphThread : public i::Thread {
10897 public:
Steve Block44f0eee2011-05-26 01:26:41 +010010898 explicit MorphThread(i::Isolate* isolate,
10899 RegExpStringModificationTest* test)
10900 : Thread(isolate, "MorphThread"), test_(test) {}
Steve Blocka7e24c12009-10-30 11:49:00 +000010901 virtual void Run() {
10902 test_->MorphString();
10903 }
10904 private:
10905 RegExpStringModificationTest* test_;
10906 };
10907
10908 void MorphString() {
10909 block_->Wait();
10910 while (morphs_during_regexp_ < kRequiredModifications &&
10911 morphs_ < kMaxModifications) {
10912 {
10913 v8::Locker lock;
10914 // Swap string between ascii and two-byte representation.
10915 i::String* string = *input_;
10916 MorphAString(string, &ascii_resource_, &uc16_resource_);
10917 morphs_++;
10918 }
10919 i::OS::Sleep(1);
10920 }
10921 morph_success_ = true;
10922 }
10923
10924 void LongRunningRegExp() {
10925 block_->Signal(); // Enable morphing thread on next preemption.
10926 while (morphs_during_regexp_ < kRequiredModifications &&
10927 morphs_ < kMaxModifications) {
10928 int morphs_before = morphs_;
10929 {
Steve Block791712a2010-08-27 10:21:07 +010010930 v8::HandleScope scope;
Steve Blocka7e24c12009-10-30 11:49:00 +000010931 // Match 15-30 "a"'s against 14 and a "b".
10932 const char* c_source =
10933 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
10934 ".exec(input) === null";
10935 Local<String> source = String::New(c_source);
10936 Local<Script> script = Script::Compile(source);
10937 Local<Value> result = script->Run();
10938 CHECK(result->IsTrue());
10939 }
10940 int morphs_after = morphs_;
10941 morphs_during_regexp_ += morphs_after - morphs_before;
10942 }
10943 regexp_success_ = true;
10944 }
10945
10946 i::uc16 two_byte_content_[15];
10947 i::Semaphore* block_;
10948 int morphs_;
10949 int morphs_during_regexp_;
10950 bool regexp_success_;
10951 bool morph_success_;
10952 i::Handle<i::String> input_;
10953 AsciiVectorResource ascii_resource_;
10954 UC16VectorResource uc16_resource_;
10955};
10956
10957
10958// Test that a regular expression execution can be interrupted and
10959// the string changed without failing.
10960TEST(RegExpStringModification) {
10961 v8::Locker lock;
10962 v8::V8::Initialize();
10963 v8::HandleScope scope;
10964 Local<Context> local_env;
10965 {
10966 LocalContext env;
10967 local_env = env.local();
10968 }
10969
10970 // Local context should still be live.
10971 CHECK(!local_env.IsEmpty());
10972 local_env->Enter();
10973
10974 // Should complete without problems.
10975 RegExpStringModificationTest().RunTest();
10976
10977 local_env->Exit();
10978}
10979
10980
10981// Test that we can set a property on the global object even if there
10982// is a read-only property in the prototype chain.
10983TEST(ReadOnlyPropertyInGlobalProto) {
10984 v8::HandleScope scope;
10985 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
10986 LocalContext context(0, templ);
10987 v8::Handle<v8::Object> global = context->Global();
10988 v8::Handle<v8::Object> global_proto =
10989 v8::Handle<v8::Object>::Cast(global->Get(v8_str("__proto__")));
10990 global_proto->Set(v8_str("x"), v8::Integer::New(0), v8::ReadOnly);
10991 global_proto->Set(v8_str("y"), v8::Integer::New(0), v8::ReadOnly);
10992 // Check without 'eval' or 'with'.
10993 v8::Handle<v8::Value> res =
10994 CompileRun("function f() { x = 42; return x; }; f()");
10995 // Check with 'eval'.
10996 res = CompileRun("function f() { eval('1'); y = 42; return y; }; f()");
10997 CHECK_EQ(v8::Integer::New(42), res);
10998 // Check with 'with'.
10999 res = CompileRun("function f() { with (this) { y = 42 }; return y; }; f()");
11000 CHECK_EQ(v8::Integer::New(42), res);
11001}
11002
11003static int force_set_set_count = 0;
11004static int force_set_get_count = 0;
11005bool pass_on_get = false;
11006
11007static v8::Handle<v8::Value> ForceSetGetter(v8::Local<v8::String> name,
11008 const v8::AccessorInfo& info) {
11009 force_set_get_count++;
11010 if (pass_on_get) {
11011 return v8::Handle<v8::Value>();
11012 } else {
11013 return v8::Int32::New(3);
11014 }
11015}
11016
11017static void ForceSetSetter(v8::Local<v8::String> name,
11018 v8::Local<v8::Value> value,
11019 const v8::AccessorInfo& info) {
11020 force_set_set_count++;
11021}
11022
11023static v8::Handle<v8::Value> ForceSetInterceptSetter(
11024 v8::Local<v8::String> name,
11025 v8::Local<v8::Value> value,
11026 const v8::AccessorInfo& info) {
11027 force_set_set_count++;
11028 return v8::Undefined();
11029}
11030
11031TEST(ForceSet) {
11032 force_set_get_count = 0;
11033 force_set_set_count = 0;
11034 pass_on_get = false;
11035
11036 v8::HandleScope scope;
11037 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
11038 v8::Handle<v8::String> access_property = v8::String::New("a");
11039 templ->SetAccessor(access_property, ForceSetGetter, ForceSetSetter);
11040 LocalContext context(NULL, templ);
11041 v8::Handle<v8::Object> global = context->Global();
11042
11043 // Ordinary properties
11044 v8::Handle<v8::String> simple_property = v8::String::New("p");
11045 global->Set(simple_property, v8::Int32::New(4), v8::ReadOnly);
11046 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
11047 // This should fail because the property is read-only
11048 global->Set(simple_property, v8::Int32::New(5));
11049 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
11050 // This should succeed even though the property is read-only
11051 global->ForceSet(simple_property, v8::Int32::New(6));
11052 CHECK_EQ(6, global->Get(simple_property)->Int32Value());
11053
11054 // Accessors
11055 CHECK_EQ(0, force_set_set_count);
11056 CHECK_EQ(0, force_set_get_count);
11057 CHECK_EQ(3, global->Get(access_property)->Int32Value());
11058 // CHECK_EQ the property shouldn't override it, just call the setter
11059 // which in this case does nothing.
11060 global->Set(access_property, v8::Int32::New(7));
11061 CHECK_EQ(3, global->Get(access_property)->Int32Value());
11062 CHECK_EQ(1, force_set_set_count);
11063 CHECK_EQ(2, force_set_get_count);
11064 // Forcing the property to be set should override the accessor without
11065 // calling it
11066 global->ForceSet(access_property, v8::Int32::New(8));
11067 CHECK_EQ(8, global->Get(access_property)->Int32Value());
11068 CHECK_EQ(1, force_set_set_count);
11069 CHECK_EQ(2, force_set_get_count);
11070}
11071
11072TEST(ForceSetWithInterceptor) {
11073 force_set_get_count = 0;
11074 force_set_set_count = 0;
11075 pass_on_get = false;
11076
11077 v8::HandleScope scope;
11078 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
11079 templ->SetNamedPropertyHandler(ForceSetGetter, ForceSetInterceptSetter);
11080 LocalContext context(NULL, templ);
11081 v8::Handle<v8::Object> global = context->Global();
11082
11083 v8::Handle<v8::String> some_property = v8::String::New("a");
11084 CHECK_EQ(0, force_set_set_count);
11085 CHECK_EQ(0, force_set_get_count);
11086 CHECK_EQ(3, global->Get(some_property)->Int32Value());
11087 // Setting the property shouldn't override it, just call the setter
11088 // which in this case does nothing.
11089 global->Set(some_property, v8::Int32::New(7));
11090 CHECK_EQ(3, global->Get(some_property)->Int32Value());
11091 CHECK_EQ(1, force_set_set_count);
11092 CHECK_EQ(2, force_set_get_count);
11093 // Getting the property when the interceptor returns an empty handle
11094 // should yield undefined, since the property isn't present on the
11095 // object itself yet.
11096 pass_on_get = true;
11097 CHECK(global->Get(some_property)->IsUndefined());
11098 CHECK_EQ(1, force_set_set_count);
11099 CHECK_EQ(3, force_set_get_count);
11100 // Forcing the property to be set should cause the value to be
11101 // set locally without calling the interceptor.
11102 global->ForceSet(some_property, v8::Int32::New(8));
11103 CHECK_EQ(8, global->Get(some_property)->Int32Value());
11104 CHECK_EQ(1, force_set_set_count);
11105 CHECK_EQ(4, force_set_get_count);
11106 // Reenabling the interceptor should cause it to take precedence over
11107 // the property
11108 pass_on_get = false;
11109 CHECK_EQ(3, global->Get(some_property)->Int32Value());
11110 CHECK_EQ(1, force_set_set_count);
11111 CHECK_EQ(5, force_set_get_count);
11112 // The interceptor should also work for other properties
11113 CHECK_EQ(3, global->Get(v8::String::New("b"))->Int32Value());
11114 CHECK_EQ(1, force_set_set_count);
11115 CHECK_EQ(6, force_set_get_count);
11116}
11117
11118
11119THREADED_TEST(ForceDelete) {
11120 v8::HandleScope scope;
11121 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
11122 LocalContext context(NULL, templ);
11123 v8::Handle<v8::Object> global = context->Global();
11124
11125 // Ordinary properties
11126 v8::Handle<v8::String> simple_property = v8::String::New("p");
11127 global->Set(simple_property, v8::Int32::New(4), v8::DontDelete);
11128 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
11129 // This should fail because the property is dont-delete.
11130 CHECK(!global->Delete(simple_property));
11131 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
11132 // This should succeed even though the property is dont-delete.
11133 CHECK(global->ForceDelete(simple_property));
11134 CHECK(global->Get(simple_property)->IsUndefined());
11135}
11136
11137
11138static int force_delete_interceptor_count = 0;
11139static bool pass_on_delete = false;
11140
11141
11142static v8::Handle<v8::Boolean> ForceDeleteDeleter(
11143 v8::Local<v8::String> name,
11144 const v8::AccessorInfo& info) {
11145 force_delete_interceptor_count++;
11146 if (pass_on_delete) {
11147 return v8::Handle<v8::Boolean>();
11148 } else {
11149 return v8::True();
11150 }
11151}
11152
11153
11154THREADED_TEST(ForceDeleteWithInterceptor) {
11155 force_delete_interceptor_count = 0;
11156 pass_on_delete = false;
11157
11158 v8::HandleScope scope;
11159 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
11160 templ->SetNamedPropertyHandler(0, 0, 0, ForceDeleteDeleter);
11161 LocalContext context(NULL, templ);
11162 v8::Handle<v8::Object> global = context->Global();
11163
11164 v8::Handle<v8::String> some_property = v8::String::New("a");
11165 global->Set(some_property, v8::Integer::New(42), v8::DontDelete);
11166
11167 // Deleting a property should get intercepted and nothing should
11168 // happen.
11169 CHECK_EQ(0, force_delete_interceptor_count);
11170 CHECK(global->Delete(some_property));
11171 CHECK_EQ(1, force_delete_interceptor_count);
11172 CHECK_EQ(42, global->Get(some_property)->Int32Value());
11173 // Deleting the property when the interceptor returns an empty
11174 // handle should not delete the property since it is DontDelete.
11175 pass_on_delete = true;
11176 CHECK(!global->Delete(some_property));
11177 CHECK_EQ(2, force_delete_interceptor_count);
11178 CHECK_EQ(42, global->Get(some_property)->Int32Value());
11179 // Forcing the property to be deleted should delete the value
11180 // without calling the interceptor.
11181 CHECK(global->ForceDelete(some_property));
11182 CHECK(global->Get(some_property)->IsUndefined());
11183 CHECK_EQ(2, force_delete_interceptor_count);
11184}
11185
11186
11187// Make sure that forcing a delete invalidates any IC stubs, so we
11188// don't read the hole value.
11189THREADED_TEST(ForceDeleteIC) {
11190 v8::HandleScope scope;
11191 LocalContext context;
11192 // Create a DontDelete variable on the global object.
11193 CompileRun("this.__proto__ = { foo: 'horse' };"
11194 "var foo = 'fish';"
11195 "function f() { return foo.length; }");
11196 // Initialize the IC for foo in f.
11197 CompileRun("for (var i = 0; i < 4; i++) f();");
11198 // Make sure the value of foo is correct before the deletion.
11199 CHECK_EQ(4, CompileRun("f()")->Int32Value());
11200 // Force the deletion of foo.
11201 CHECK(context->Global()->ForceDelete(v8_str("foo")));
11202 // Make sure the value for foo is read from the prototype, and that
11203 // we don't get in trouble with reading the deleted cell value
11204 // sentinel.
11205 CHECK_EQ(5, CompileRun("f()")->Int32Value());
11206}
11207
11208
11209v8::Persistent<Context> calling_context0;
11210v8::Persistent<Context> calling_context1;
11211v8::Persistent<Context> calling_context2;
11212
11213
11214// Check that the call to the callback is initiated in
11215// calling_context2, the directly calling context is calling_context1
11216// and the callback itself is in calling_context0.
11217static v8::Handle<Value> GetCallingContextCallback(const v8::Arguments& args) {
11218 ApiTestFuzzer::Fuzz();
11219 CHECK(Context::GetCurrent() == calling_context0);
11220 CHECK(Context::GetCalling() == calling_context1);
11221 CHECK(Context::GetEntered() == calling_context2);
11222 return v8::Integer::New(42);
11223}
11224
11225
11226THREADED_TEST(GetCallingContext) {
11227 v8::HandleScope scope;
11228
11229 calling_context0 = Context::New();
11230 calling_context1 = Context::New();
11231 calling_context2 = Context::New();
11232
11233 // Allow cross-domain access.
11234 Local<String> token = v8_str("<security token>");
11235 calling_context0->SetSecurityToken(token);
11236 calling_context1->SetSecurityToken(token);
11237 calling_context2->SetSecurityToken(token);
11238
11239 // Create an object with a C++ callback in context0.
11240 calling_context0->Enter();
11241 Local<v8::FunctionTemplate> callback_templ =
11242 v8::FunctionTemplate::New(GetCallingContextCallback);
11243 calling_context0->Global()->Set(v8_str("callback"),
11244 callback_templ->GetFunction());
11245 calling_context0->Exit();
11246
11247 // Expose context0 in context1 and setup a function that calls the
11248 // callback function.
11249 calling_context1->Enter();
11250 calling_context1->Global()->Set(v8_str("context0"),
11251 calling_context0->Global());
11252 CompileRun("function f() { context0.callback() }");
11253 calling_context1->Exit();
11254
11255 // Expose context1 in context2 and call the callback function in
11256 // context0 indirectly through f in context1.
11257 calling_context2->Enter();
11258 calling_context2->Global()->Set(v8_str("context1"),
11259 calling_context1->Global());
11260 CompileRun("context1.f()");
11261 calling_context2->Exit();
11262
11263 // Dispose the contexts to allow them to be garbage collected.
11264 calling_context0.Dispose();
11265 calling_context1.Dispose();
11266 calling_context2.Dispose();
11267 calling_context0.Clear();
11268 calling_context1.Clear();
11269 calling_context2.Clear();
11270}
11271
11272
11273// Check that a variable declaration with no explicit initialization
11274// value does not shadow an existing property in the prototype chain.
11275//
11276// This is consistent with Firefox and Safari.
11277//
11278// See http://crbug.com/12548.
11279THREADED_TEST(InitGlobalVarInProtoChain) {
11280 v8::HandleScope scope;
11281 LocalContext context;
11282 // Introduce a variable in the prototype chain.
11283 CompileRun("__proto__.x = 42");
11284 v8::Handle<v8::Value> result = CompileRun("var x; x");
11285 CHECK(!result->IsUndefined());
11286 CHECK_EQ(42, result->Int32Value());
11287}
11288
11289
11290// Regression test for issue 398.
11291// If a function is added to an object, creating a constant function
11292// field, and the result is cloned, replacing the constant function on the
11293// original should not affect the clone.
11294// See http://code.google.com/p/v8/issues/detail?id=398
11295THREADED_TEST(ReplaceConstantFunction) {
11296 v8::HandleScope scope;
11297 LocalContext context;
11298 v8::Handle<v8::Object> obj = v8::Object::New();
11299 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New();
11300 v8::Handle<v8::String> foo_string = v8::String::New("foo");
11301 obj->Set(foo_string, func_templ->GetFunction());
11302 v8::Handle<v8::Object> obj_clone = obj->Clone();
11303 obj_clone->Set(foo_string, v8::String::New("Hello"));
11304 CHECK(!obj->Get(foo_string)->IsUndefined());
11305}
11306
11307
11308// Regression test for http://crbug.com/16276.
11309THREADED_TEST(Regress16276) {
11310 v8::HandleScope scope;
11311 LocalContext context;
11312 // Force the IC in f to be a dictionary load IC.
11313 CompileRun("function f(obj) { return obj.x; }\n"
11314 "var obj = { x: { foo: 42 }, y: 87 };\n"
11315 "var x = obj.x;\n"
11316 "delete obj.y;\n"
11317 "for (var i = 0; i < 5; i++) f(obj);");
11318 // Detach the global object to make 'this' refer directly to the
11319 // global object (not the proxy), and make sure that the dictionary
11320 // load IC doesn't mess up loading directly from the global object.
11321 context->DetachGlobal();
11322 CHECK_EQ(42, CompileRun("f(this).foo")->Int32Value());
11323}
11324
11325
11326THREADED_TEST(PixelArray) {
11327 v8::HandleScope scope;
11328 LocalContext context;
Steve Blockd0582a62009-12-15 09:54:21 +000011329 const int kElementCount = 260;
Steve Blocka7e24c12009-10-30 11:49:00 +000011330 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(kElementCount));
Steve Block44f0eee2011-05-26 01:26:41 +010011331 i::Handle<i::ExternalPixelArray> pixels =
11332 i::Handle<i::ExternalPixelArray>::cast(
11333 FACTORY->NewExternalArray(kElementCount,
11334 v8::kExternalPixelArray,
11335 pixel_data));
11336 HEAP->CollectAllGarbage(false); // Force GC to trigger verification.
Steve Blocka7e24c12009-10-30 11:49:00 +000011337 for (int i = 0; i < kElementCount; i++) {
Steve Blockd0582a62009-12-15 09:54:21 +000011338 pixels->set(i, i % 256);
Steve Blocka7e24c12009-10-30 11:49:00 +000011339 }
Steve Block44f0eee2011-05-26 01:26:41 +010011340 HEAP->CollectAllGarbage(false); // Force GC to trigger verification.
Steve Blocka7e24c12009-10-30 11:49:00 +000011341 for (int i = 0; i < kElementCount; i++) {
Steve Blockd0582a62009-12-15 09:54:21 +000011342 CHECK_EQ(i % 256, pixels->get(i));
11343 CHECK_EQ(i % 256, pixel_data[i]);
Steve Blocka7e24c12009-10-30 11:49:00 +000011344 }
11345
11346 v8::Handle<v8::Object> obj = v8::Object::New();
11347 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj);
11348 // Set the elements to be the pixels.
11349 // jsobj->set_elements(*pixels);
11350 obj->SetIndexedPropertiesToPixelData(pixel_data, kElementCount);
John Reck59135872010-11-02 12:39:01 -070011351 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000011352 obj->Set(v8_str("field"), v8::Int32::New(1503));
11353 context->Global()->Set(v8_str("pixels"), obj);
11354 v8::Handle<v8::Value> result = CompileRun("pixels.field");
11355 CHECK_EQ(1503, result->Int32Value());
11356 result = CompileRun("pixels[1]");
11357 CHECK_EQ(1, result->Int32Value());
11358
11359 result = CompileRun("var sum = 0;"
11360 "for (var i = 0; i < 8; i++) {"
11361 " sum += pixels[i] = pixels[i] = -i;"
11362 "}"
11363 "sum;");
11364 CHECK_EQ(-28, result->Int32Value());
11365
11366 result = CompileRun("var sum = 0;"
11367 "for (var i = 0; i < 8; i++) {"
11368 " sum += pixels[i] = pixels[i] = 0;"
11369 "}"
11370 "sum;");
11371 CHECK_EQ(0, result->Int32Value());
11372
11373 result = CompileRun("var sum = 0;"
11374 "for (var i = 0; i < 8; i++) {"
11375 " sum += pixels[i] = pixels[i] = 255;"
11376 "}"
11377 "sum;");
11378 CHECK_EQ(8 * 255, result->Int32Value());
11379
11380 result = CompileRun("var sum = 0;"
11381 "for (var i = 0; i < 8; i++) {"
11382 " sum += pixels[i] = pixels[i] = 256 + i;"
11383 "}"
11384 "sum;");
11385 CHECK_EQ(2076, result->Int32Value());
11386
11387 result = CompileRun("var sum = 0;"
11388 "for (var i = 0; i < 8; i++) {"
11389 " sum += pixels[i] = pixels[i] = i;"
11390 "}"
11391 "sum;");
11392 CHECK_EQ(28, result->Int32Value());
11393
11394 result = CompileRun("var sum = 0;"
11395 "for (var i = 0; i < 8; i++) {"
11396 " sum += pixels[i];"
11397 "}"
11398 "sum;");
11399 CHECK_EQ(28, result->Int32Value());
11400
11401 i::Handle<i::Smi> value(i::Smi::FromInt(2));
Ben Murdoche0cee9b2011-05-25 10:26:03 +010011402 i::SetElement(jsobj, 1, value, i::kNonStrictMode);
John Reck59135872010-11-02 12:39:01 -070011403 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000011404 *value.location() = i::Smi::FromInt(256);
Ben Murdoche0cee9b2011-05-25 10:26:03 +010011405 i::SetElement(jsobj, 1, value, i::kNonStrictMode);
John Reck59135872010-11-02 12:39:01 -070011406 CHECK_EQ(255,
11407 i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000011408 *value.location() = i::Smi::FromInt(-1);
Ben Murdoche0cee9b2011-05-25 10:26:03 +010011409 i::SetElement(jsobj, 1, value, i::kNonStrictMode);
John Reck59135872010-11-02 12:39:01 -070011410 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000011411
11412 result = CompileRun("for (var i = 0; i < 8; i++) {"
11413 " pixels[i] = (i * 65) - 109;"
11414 "}"
11415 "pixels[1] + pixels[6];");
11416 CHECK_EQ(255, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070011417 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0)->ToObjectChecked())->value());
11418 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value());
11419 CHECK_EQ(21,
11420 i::Smi::cast(jsobj->GetElement(2)->ToObjectChecked())->value());
11421 CHECK_EQ(86,
11422 i::Smi::cast(jsobj->GetElement(3)->ToObjectChecked())->value());
11423 CHECK_EQ(151,
11424 i::Smi::cast(jsobj->GetElement(4)->ToObjectChecked())->value());
11425 CHECK_EQ(216,
11426 i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value());
11427 CHECK_EQ(255,
11428 i::Smi::cast(jsobj->GetElement(6)->ToObjectChecked())->value());
11429 CHECK_EQ(255,
11430 i::Smi::cast(jsobj->GetElement(7)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000011431 result = CompileRun("var sum = 0;"
11432 "for (var i = 0; i < 8; i++) {"
11433 " sum += pixels[i];"
11434 "}"
11435 "sum;");
11436 CHECK_EQ(984, result->Int32Value());
11437
11438 result = CompileRun("for (var i = 0; i < 8; i++) {"
11439 " pixels[i] = (i * 1.1);"
11440 "}"
11441 "pixels[1] + pixels[6];");
11442 CHECK_EQ(8, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070011443 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0)->ToObjectChecked())->value());
11444 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value());
11445 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(2)->ToObjectChecked())->value());
11446 CHECK_EQ(3, i::Smi::cast(jsobj->GetElement(3)->ToObjectChecked())->value());
11447 CHECK_EQ(4, i::Smi::cast(jsobj->GetElement(4)->ToObjectChecked())->value());
11448 CHECK_EQ(6, i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value());
11449 CHECK_EQ(7, i::Smi::cast(jsobj->GetElement(6)->ToObjectChecked())->value());
11450 CHECK_EQ(8, i::Smi::cast(jsobj->GetElement(7)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000011451
11452 result = CompileRun("for (var i = 0; i < 8; i++) {"
11453 " pixels[7] = undefined;"
11454 "}"
11455 "pixels[7];");
11456 CHECK_EQ(0, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070011457 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(7)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000011458
11459 result = CompileRun("for (var i = 0; i < 8; i++) {"
11460 " pixels[6] = '2.3';"
11461 "}"
11462 "pixels[6];");
11463 CHECK_EQ(2, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070011464 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(6)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000011465
11466 result = CompileRun("for (var i = 0; i < 8; i++) {"
11467 " pixels[5] = NaN;"
11468 "}"
11469 "pixels[5];");
11470 CHECK_EQ(0, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070011471 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000011472
11473 result = CompileRun("for (var i = 0; i < 8; i++) {"
11474 " pixels[8] = Infinity;"
11475 "}"
11476 "pixels[8];");
11477 CHECK_EQ(255, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070011478 CHECK_EQ(255,
11479 i::Smi::cast(jsobj->GetElement(8)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000011480
11481 result = CompileRun("for (var i = 0; i < 8; i++) {"
11482 " pixels[9] = -Infinity;"
11483 "}"
11484 "pixels[9];");
11485 CHECK_EQ(0, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070011486 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(9)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000011487
11488 result = CompileRun("pixels[3] = 33;"
11489 "delete pixels[3];"
11490 "pixels[3];");
11491 CHECK_EQ(33, result->Int32Value());
11492
11493 result = CompileRun("pixels[0] = 10; pixels[1] = 11;"
11494 "pixels[2] = 12; pixels[3] = 13;"
11495 "pixels.__defineGetter__('2',"
11496 "function() { return 120; });"
11497 "pixels[2];");
11498 CHECK_EQ(12, result->Int32Value());
11499
11500 result = CompileRun("var js_array = new Array(40);"
11501 "js_array[0] = 77;"
11502 "js_array;");
11503 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
11504
11505 result = CompileRun("pixels[1] = 23;"
11506 "pixels.__proto__ = [];"
11507 "js_array.__proto__ = pixels;"
11508 "js_array.concat(pixels);");
11509 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
11510 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value());
11511
11512 result = CompileRun("pixels[1] = 23;");
11513 CHECK_EQ(23, result->Int32Value());
11514
Steve Blockd0582a62009-12-15 09:54:21 +000011515 // Test for index greater than 255. Regression test for:
11516 // http://code.google.com/p/chromium/issues/detail?id=26337.
11517 result = CompileRun("pixels[256] = 255;");
11518 CHECK_EQ(255, result->Int32Value());
11519 result = CompileRun("var i = 0;"
11520 "for (var j = 0; j < 8; j++) { i = pixels[256]; }"
11521 "i");
11522 CHECK_EQ(255, result->Int32Value());
11523
Steve Block1e0659c2011-05-24 12:43:12 +010011524 // Make sure that pixel array ICs recognize when a non-pixel array
11525 // is passed to it.
11526 result = CompileRun("function pa_load(p) {"
11527 " var sum = 0;"
11528 " for (var j = 0; j < 256; j++) { sum += p[j]; }"
11529 " return sum;"
11530 "}"
11531 "for (var i = 0; i < 256; ++i) { pixels[i] = i; }"
11532 "for (var i = 0; i < 10; ++i) { pa_load(pixels); }"
11533 "just_ints = new Object();"
11534 "for (var i = 0; i < 256; ++i) { just_ints[i] = i; }"
11535 "for (var i = 0; i < 10; ++i) {"
11536 " result = pa_load(just_ints);"
11537 "}"
11538 "result");
11539 CHECK_EQ(32640, result->Int32Value());
11540
11541 // Make sure that pixel array ICs recognize out-of-bound accesses.
11542 result = CompileRun("function pa_load(p, start) {"
11543 " var sum = 0;"
11544 " for (var j = start; j < 256; j++) { sum += p[j]; }"
11545 " return sum;"
11546 "}"
11547 "for (var i = 0; i < 256; ++i) { pixels[i] = i; }"
11548 "for (var i = 0; i < 10; ++i) { pa_load(pixels,0); }"
11549 "for (var i = 0; i < 10; ++i) {"
11550 " result = pa_load(pixels,-10);"
11551 "}"
11552 "result");
11553 CHECK_EQ(0, result->Int32Value());
11554
11555 // Make sure that generic ICs properly handles a pixel array.
11556 result = CompileRun("function pa_load(p) {"
11557 " var sum = 0;"
11558 " for (var j = 0; j < 256; j++) { sum += p[j]; }"
11559 " return sum;"
11560 "}"
11561 "for (var i = 0; i < 256; ++i) { pixels[i] = i; }"
11562 "just_ints = new Object();"
11563 "for (var i = 0; i < 256; ++i) { just_ints[i] = i; }"
11564 "for (var i = 0; i < 10; ++i) { pa_load(just_ints); }"
11565 "for (var i = 0; i < 10; ++i) {"
11566 " result = pa_load(pixels);"
11567 "}"
11568 "result");
11569 CHECK_EQ(32640, result->Int32Value());
11570
11571 // Make sure that generic load ICs recognize out-of-bound accesses in
11572 // pixel arrays.
11573 result = CompileRun("function pa_load(p, start) {"
11574 " var sum = 0;"
11575 " for (var j = start; j < 256; j++) { sum += p[j]; }"
11576 " return sum;"
11577 "}"
11578 "for (var i = 0; i < 256; ++i) { pixels[i] = i; }"
11579 "just_ints = new Object();"
11580 "for (var i = 0; i < 256; ++i) { just_ints[i] = i; }"
11581 "for (var i = 0; i < 10; ++i) { pa_load(just_ints,0); }"
11582 "for (var i = 0; i < 10; ++i) { pa_load(pixels,0); }"
11583 "for (var i = 0; i < 10; ++i) {"
11584 " result = pa_load(pixels,-10);"
11585 "}"
11586 "result");
11587 CHECK_EQ(0, result->Int32Value());
11588
11589 // Make sure that generic ICs properly handles other types than pixel
11590 // arrays (that the inlined fast pixel array test leaves the right information
11591 // in the right registers).
11592 result = CompileRun("function pa_load(p) {"
11593 " var sum = 0;"
11594 " for (var j = 0; j < 256; j++) { sum += p[j]; }"
11595 " return sum;"
11596 "}"
11597 "for (var i = 0; i < 256; ++i) { pixels[i] = i; }"
11598 "just_ints = new Object();"
11599 "for (var i = 0; i < 256; ++i) { just_ints[i] = i; }"
11600 "for (var i = 0; i < 10; ++i) { pa_load(just_ints); }"
11601 "for (var i = 0; i < 10; ++i) { pa_load(pixels); }"
11602 "sparse_array = new Object();"
11603 "for (var i = 0; i < 256; ++i) { sparse_array[i] = i; }"
11604 "sparse_array[1000000] = 3;"
11605 "for (var i = 0; i < 10; ++i) {"
11606 " result = pa_load(sparse_array);"
11607 "}"
11608 "result");
11609 CHECK_EQ(32640, result->Int32Value());
11610
Ben Murdoche0cee9b2011-05-25 10:26:03 +010011611 // Make sure that pixel array store ICs clamp values correctly.
11612 result = CompileRun("function pa_store(p) {"
11613 " for (var j = 0; j < 256; j++) { p[j] = j * 2; }"
11614 "}"
11615 "pa_store(pixels);"
11616 "var sum = 0;"
11617 "for (var j = 0; j < 256; j++) { sum += pixels[j]; }"
11618 "sum");
11619 CHECK_EQ(48896, result->Int32Value());
11620
11621 // Make sure that pixel array stores correctly handle accesses outside
11622 // of the pixel array..
11623 result = CompileRun("function pa_store(p,start) {"
11624 " for (var j = 0; j < 256; j++) {"
11625 " p[j+start] = j * 2;"
11626 " }"
11627 "}"
11628 "pa_store(pixels,0);"
11629 "pa_store(pixels,-128);"
11630 "var sum = 0;"
11631 "for (var j = 0; j < 256; j++) { sum += pixels[j]; }"
11632 "sum");
11633 CHECK_EQ(65280, result->Int32Value());
11634
11635 // Make sure that the generic store stub correctly handle accesses outside
11636 // of the pixel array..
11637 result = CompileRun("function pa_store(p,start) {"
11638 " for (var j = 0; j < 256; j++) {"
11639 " p[j+start] = j * 2;"
11640 " }"
11641 "}"
11642 "pa_store(pixels,0);"
11643 "just_ints = new Object();"
11644 "for (var i = 0; i < 256; ++i) { just_ints[i] = i; }"
11645 "pa_store(just_ints, 0);"
11646 "pa_store(pixels,-128);"
11647 "var sum = 0;"
11648 "for (var j = 0; j < 256; j++) { sum += pixels[j]; }"
11649 "sum");
11650 CHECK_EQ(65280, result->Int32Value());
11651
11652 // Make sure that the generic keyed store stub clamps pixel array values
11653 // correctly.
11654 result = CompileRun("function pa_store(p) {"
11655 " for (var j = 0; j < 256; j++) { p[j] = j * 2; }"
11656 "}"
11657 "pa_store(pixels);"
11658 "just_ints = new Object();"
11659 "pa_store(just_ints);"
11660 "pa_store(pixels);"
11661 "var sum = 0;"
11662 "for (var j = 0; j < 256; j++) { sum += pixels[j]; }"
11663 "sum");
11664 CHECK_EQ(48896, result->Int32Value());
11665
11666 // Make sure that pixel array loads are optimized by crankshaft.
Steve Block1e0659c2011-05-24 12:43:12 +010011667 result = CompileRun("function pa_load(p) {"
11668 " var sum = 0;"
11669 " for (var i=0; i<256; ++i) {"
11670 " sum += p[i];"
11671 " }"
11672 " return sum; "
11673 "}"
11674 "for (var i = 0; i < 256; ++i) { pixels[i] = i; }"
Steve Block44f0eee2011-05-26 01:26:41 +010011675 "for (var i = 0; i < 5000; ++i) {"
Steve Block1e0659c2011-05-24 12:43:12 +010011676 " result = pa_load(pixels);"
11677 "}"
11678 "result");
11679 CHECK_EQ(32640, result->Int32Value());
11680
Ben Murdoche0cee9b2011-05-25 10:26:03 +010011681 // Make sure that pixel array stores are optimized by crankshaft.
11682 result = CompileRun("function pa_init(p) {"
11683 "for (var i = 0; i < 256; ++i) { p[i] = i; }"
11684 "}"
11685 "function pa_load(p) {"
11686 " var sum = 0;"
11687 " for (var i=0; i<256; ++i) {"
11688 " sum += p[i];"
11689 " }"
11690 " return sum; "
11691 "}"
Steve Block44f0eee2011-05-26 01:26:41 +010011692 "for (var i = 0; i < 5000; ++i) {"
Ben Murdoche0cee9b2011-05-25 10:26:03 +010011693 " pa_init(pixels);"
11694 "}"
11695 "result = pa_load(pixels);"
11696 "result");
11697 CHECK_EQ(32640, result->Int32Value());
11698
Steve Blocka7e24c12009-10-30 11:49:00 +000011699 free(pixel_data);
11700}
11701
11702
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010011703THREADED_TEST(PixelArrayInfo) {
11704 v8::HandleScope scope;
11705 LocalContext context;
11706 for (int size = 0; size < 100; size += 10) {
11707 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(size));
11708 v8::Handle<v8::Object> obj = v8::Object::New();
11709 obj->SetIndexedPropertiesToPixelData(pixel_data, size);
11710 CHECK(obj->HasIndexedPropertiesInPixelData());
11711 CHECK_EQ(pixel_data, obj->GetIndexedPropertiesPixelData());
11712 CHECK_EQ(size, obj->GetIndexedPropertiesPixelDataLength());
11713 free(pixel_data);
11714 }
11715}
11716
11717
Ben Murdoche0cee9b2011-05-25 10:26:03 +010011718static v8::Handle<Value> NotHandledIndexedPropertyGetter(
11719 uint32_t index,
11720 const AccessorInfo& info) {
11721 ApiTestFuzzer::Fuzz();
11722 return v8::Handle<Value>();
11723}
11724
11725
11726static v8::Handle<Value> NotHandledIndexedPropertySetter(
11727 uint32_t index,
11728 Local<Value> value,
11729 const AccessorInfo& info) {
11730 ApiTestFuzzer::Fuzz();
11731 return v8::Handle<Value>();
11732}
11733
11734
11735THREADED_TEST(PixelArrayWithInterceptor) {
11736 v8::HandleScope scope;
11737 LocalContext context;
11738 const int kElementCount = 260;
11739 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(kElementCount));
Steve Block44f0eee2011-05-26 01:26:41 +010011740 i::Handle<i::ExternalPixelArray> pixels =
11741 i::Handle<i::ExternalPixelArray>::cast(
11742 FACTORY->NewExternalArray(kElementCount,
11743 v8::kExternalPixelArray,
11744 pixel_data));
Ben Murdoche0cee9b2011-05-25 10:26:03 +010011745 for (int i = 0; i < kElementCount; i++) {
11746 pixels->set(i, i % 256);
11747 }
11748 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
11749 templ->SetIndexedPropertyHandler(NotHandledIndexedPropertyGetter,
11750 NotHandledIndexedPropertySetter);
11751 v8::Handle<v8::Object> obj = templ->NewInstance();
11752 obj->SetIndexedPropertiesToPixelData(pixel_data, kElementCount);
11753 context->Global()->Set(v8_str("pixels"), obj);
11754 v8::Handle<v8::Value> result = CompileRun("pixels[1]");
11755 CHECK_EQ(1, result->Int32Value());
11756 result = CompileRun("var sum = 0;"
11757 "for (var i = 0; i < 8; i++) {"
11758 " sum += pixels[i] = pixels[i] = -i;"
11759 "}"
11760 "sum;");
11761 CHECK_EQ(-28, result->Int32Value());
11762 result = CompileRun("pixels.hasOwnProperty('1')");
11763 CHECK(result->BooleanValue());
11764 free(pixel_data);
11765}
11766
11767
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010011768static int ExternalArrayElementSize(v8::ExternalArrayType array_type) {
11769 switch (array_type) {
11770 case v8::kExternalByteArray:
11771 case v8::kExternalUnsignedByteArray:
Steve Block44f0eee2011-05-26 01:26:41 +010011772 case v8::kExternalPixelArray:
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010011773 return 1;
11774 break;
11775 case v8::kExternalShortArray:
11776 case v8::kExternalUnsignedShortArray:
11777 return 2;
11778 break;
11779 case v8::kExternalIntArray:
11780 case v8::kExternalUnsignedIntArray:
11781 case v8::kExternalFloatArray:
11782 return 4;
11783 break;
Ben Murdoch257744e2011-11-30 15:57:28 +000011784 case v8::kExternalDoubleArray:
11785 return 8;
11786 break;
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010011787 default:
11788 UNREACHABLE();
11789 return -1;
11790 }
11791 UNREACHABLE();
11792 return -1;
11793}
11794
11795
Steve Block3ce2e202009-11-05 08:53:23 +000011796template <class ExternalArrayClass, class ElementType>
11797static void ExternalArrayTestHelper(v8::ExternalArrayType array_type,
11798 int64_t low,
11799 int64_t high) {
11800 v8::HandleScope scope;
11801 LocalContext context;
11802 const int kElementCount = 40;
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010011803 int element_size = ExternalArrayElementSize(array_type);
Steve Block3ce2e202009-11-05 08:53:23 +000011804 ElementType* array_data =
11805 static_cast<ElementType*>(malloc(kElementCount * element_size));
11806 i::Handle<ExternalArrayClass> array =
11807 i::Handle<ExternalArrayClass>::cast(
Steve Block44f0eee2011-05-26 01:26:41 +010011808 FACTORY->NewExternalArray(kElementCount, array_type, array_data));
11809 HEAP->CollectAllGarbage(false); // Force GC to trigger verification.
Steve Block3ce2e202009-11-05 08:53:23 +000011810 for (int i = 0; i < kElementCount; i++) {
11811 array->set(i, static_cast<ElementType>(i));
11812 }
Steve Block44f0eee2011-05-26 01:26:41 +010011813 HEAP->CollectAllGarbage(false); // Force GC to trigger verification.
Steve Block3ce2e202009-11-05 08:53:23 +000011814 for (int i = 0; i < kElementCount; i++) {
11815 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array->get(i)));
11816 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array_data[i]));
11817 }
11818
11819 v8::Handle<v8::Object> obj = v8::Object::New();
11820 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj);
11821 // Set the elements to be the external array.
11822 obj->SetIndexedPropertiesToExternalArrayData(array_data,
11823 array_type,
11824 kElementCount);
John Reck59135872010-11-02 12:39:01 -070011825 CHECK_EQ(
11826 1, static_cast<int>(jsobj->GetElement(1)->ToObjectChecked()->Number()));
Steve Block3ce2e202009-11-05 08:53:23 +000011827 obj->Set(v8_str("field"), v8::Int32::New(1503));
11828 context->Global()->Set(v8_str("ext_array"), obj);
11829 v8::Handle<v8::Value> result = CompileRun("ext_array.field");
11830 CHECK_EQ(1503, result->Int32Value());
11831 result = CompileRun("ext_array[1]");
11832 CHECK_EQ(1, result->Int32Value());
11833
11834 // Check pass through of assigned smis
11835 result = CompileRun("var sum = 0;"
11836 "for (var i = 0; i < 8; i++) {"
11837 " sum += ext_array[i] = ext_array[i] = -i;"
11838 "}"
11839 "sum;");
11840 CHECK_EQ(-28, result->Int32Value());
11841
11842 // Check assigned smis
11843 result = CompileRun("for (var i = 0; i < 8; i++) {"
11844 " ext_array[i] = i;"
11845 "}"
11846 "var sum = 0;"
11847 "for (var i = 0; i < 8; i++) {"
11848 " sum += ext_array[i];"
11849 "}"
11850 "sum;");
11851 CHECK_EQ(28, result->Int32Value());
11852
11853 // Check assigned smis in reverse order
11854 result = CompileRun("for (var i = 8; --i >= 0; ) {"
11855 " ext_array[i] = i;"
11856 "}"
11857 "var sum = 0;"
11858 "for (var i = 0; i < 8; i++) {"
11859 " sum += ext_array[i];"
11860 "}"
11861 "sum;");
11862 CHECK_EQ(28, result->Int32Value());
11863
11864 // Check pass through of assigned HeapNumbers
11865 result = CompileRun("var sum = 0;"
11866 "for (var i = 0; i < 16; i+=2) {"
11867 " sum += ext_array[i] = ext_array[i] = (-i * 0.5);"
11868 "}"
11869 "sum;");
11870 CHECK_EQ(-28, result->Int32Value());
11871
11872 // Check assigned HeapNumbers
11873 result = CompileRun("for (var i = 0; i < 16; i+=2) {"
11874 " ext_array[i] = (i * 0.5);"
11875 "}"
11876 "var sum = 0;"
11877 "for (var i = 0; i < 16; i+=2) {"
11878 " sum += ext_array[i];"
11879 "}"
11880 "sum;");
11881 CHECK_EQ(28, result->Int32Value());
11882
11883 // Check assigned HeapNumbers in reverse order
11884 result = CompileRun("for (var i = 14; i >= 0; i-=2) {"
11885 " ext_array[i] = (i * 0.5);"
11886 "}"
11887 "var sum = 0;"
11888 "for (var i = 0; i < 16; i+=2) {"
11889 " sum += ext_array[i];"
11890 "}"
11891 "sum;");
11892 CHECK_EQ(28, result->Int32Value());
11893
11894 i::ScopedVector<char> test_buf(1024);
11895
11896 // Check legal boundary conditions.
11897 // The repeated loads and stores ensure the ICs are exercised.
11898 const char* boundary_program =
11899 "var res = 0;"
11900 "for (var i = 0; i < 16; i++) {"
11901 " ext_array[i] = %lld;"
11902 " if (i > 8) {"
11903 " res = ext_array[i];"
11904 " }"
11905 "}"
11906 "res;";
11907 i::OS::SNPrintF(test_buf,
11908 boundary_program,
11909 low);
11910 result = CompileRun(test_buf.start());
11911 CHECK_EQ(low, result->IntegerValue());
11912
11913 i::OS::SNPrintF(test_buf,
11914 boundary_program,
11915 high);
11916 result = CompileRun(test_buf.start());
11917 CHECK_EQ(high, result->IntegerValue());
11918
11919 // Check misprediction of type in IC.
11920 result = CompileRun("var tmp_array = ext_array;"
11921 "var sum = 0;"
11922 "for (var i = 0; i < 8; i++) {"
11923 " tmp_array[i] = i;"
11924 " sum += tmp_array[i];"
11925 " if (i == 4) {"
11926 " tmp_array = {};"
11927 " }"
11928 "}"
11929 "sum;");
Steve Block44f0eee2011-05-26 01:26:41 +010011930 HEAP->CollectAllGarbage(false); // Force GC to trigger verification.
Steve Block3ce2e202009-11-05 08:53:23 +000011931 CHECK_EQ(28, result->Int32Value());
11932
11933 // Make sure out-of-range loads do not throw.
11934 i::OS::SNPrintF(test_buf,
11935 "var caught_exception = false;"
11936 "try {"
11937 " ext_array[%d];"
11938 "} catch (e) {"
11939 " caught_exception = true;"
11940 "}"
11941 "caught_exception;",
11942 kElementCount);
11943 result = CompileRun(test_buf.start());
11944 CHECK_EQ(false, result->BooleanValue());
11945
11946 // Make sure out-of-range stores do not throw.
11947 i::OS::SNPrintF(test_buf,
11948 "var caught_exception = false;"
11949 "try {"
11950 " ext_array[%d] = 1;"
11951 "} catch (e) {"
11952 " caught_exception = true;"
11953 "}"
11954 "caught_exception;",
11955 kElementCount);
11956 result = CompileRun(test_buf.start());
11957 CHECK_EQ(false, result->BooleanValue());
11958
11959 // Check other boundary conditions, values and operations.
11960 result = CompileRun("for (var i = 0; i < 8; i++) {"
11961 " ext_array[7] = undefined;"
11962 "}"
11963 "ext_array[7];");
11964 CHECK_EQ(0, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070011965 CHECK_EQ(
11966 0, static_cast<int>(jsobj->GetElement(7)->ToObjectChecked()->Number()));
Steve Block3ce2e202009-11-05 08:53:23 +000011967
11968 result = CompileRun("for (var i = 0; i < 8; i++) {"
11969 " ext_array[6] = '2.3';"
11970 "}"
11971 "ext_array[6];");
11972 CHECK_EQ(2, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070011973 CHECK_EQ(
11974 2, static_cast<int>(jsobj->GetElement(6)->ToObjectChecked()->Number()));
Steve Block3ce2e202009-11-05 08:53:23 +000011975
Ben Murdoch257744e2011-11-30 15:57:28 +000011976 if (array_type != v8::kExternalFloatArray &&
11977 array_type != v8::kExternalDoubleArray) {
Steve Block3ce2e202009-11-05 08:53:23 +000011978 // Though the specification doesn't state it, be explicit about
11979 // converting NaNs and +/-Infinity to zero.
11980 result = CompileRun("for (var i = 0; i < 8; i++) {"
11981 " ext_array[i] = 5;"
11982 "}"
11983 "for (var i = 0; i < 8; i++) {"
11984 " ext_array[i] = NaN;"
11985 "}"
11986 "ext_array[5];");
11987 CHECK_EQ(0, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070011988 CHECK_EQ(0,
11989 i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value());
Steve Block3ce2e202009-11-05 08:53:23 +000011990
11991 result = CompileRun("for (var i = 0; i < 8; i++) {"
11992 " ext_array[i] = 5;"
11993 "}"
11994 "for (var i = 0; i < 8; i++) {"
11995 " ext_array[i] = Infinity;"
11996 "}"
11997 "ext_array[5];");
Steve Block44f0eee2011-05-26 01:26:41 +010011998 int expected_value =
11999 (array_type == v8::kExternalPixelArray) ? 255 : 0;
12000 CHECK_EQ(expected_value, result->Int32Value());
12001 CHECK_EQ(expected_value,
John Reck59135872010-11-02 12:39:01 -070012002 i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value());
Steve Block3ce2e202009-11-05 08:53:23 +000012003
12004 result = CompileRun("for (var i = 0; i < 8; i++) {"
12005 " ext_array[i] = 5;"
12006 "}"
12007 "for (var i = 0; i < 8; i++) {"
12008 " ext_array[i] = -Infinity;"
12009 "}"
12010 "ext_array[5];");
12011 CHECK_EQ(0, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070012012 CHECK_EQ(0,
12013 i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value());
Steve Block1e0659c2011-05-24 12:43:12 +010012014
12015 // Check truncation behavior of integral arrays.
12016 const char* unsigned_data =
12017 "var source_data = [0.6, 10.6];"
12018 "var expected_results = [0, 10];";
12019 const char* signed_data =
12020 "var source_data = [0.6, 10.6, -0.6, -10.6];"
12021 "var expected_results = [0, 10, 0, -10];";
Steve Block44f0eee2011-05-26 01:26:41 +010012022 const char* pixel_data =
12023 "var source_data = [0.6, 10.6];"
12024 "var expected_results = [1, 11];";
Steve Block1e0659c2011-05-24 12:43:12 +010012025 bool is_unsigned =
12026 (array_type == v8::kExternalUnsignedByteArray ||
12027 array_type == v8::kExternalUnsignedShortArray ||
12028 array_type == v8::kExternalUnsignedIntArray);
Steve Block44f0eee2011-05-26 01:26:41 +010012029 bool is_pixel_data = array_type == v8::kExternalPixelArray;
Steve Block1e0659c2011-05-24 12:43:12 +010012030
12031 i::OS::SNPrintF(test_buf,
12032 "%s"
12033 "var all_passed = true;"
12034 "for (var i = 0; i < source_data.length; i++) {"
12035 " for (var j = 0; j < 8; j++) {"
12036 " ext_array[j] = source_data[i];"
12037 " }"
12038 " all_passed = all_passed &&"
12039 " (ext_array[5] == expected_results[i]);"
12040 "}"
12041 "all_passed;",
Steve Block44f0eee2011-05-26 01:26:41 +010012042 (is_unsigned ?
12043 unsigned_data :
12044 (is_pixel_data ? pixel_data : signed_data)));
Steve Block1e0659c2011-05-24 12:43:12 +010012045 result = CompileRun(test_buf.start());
12046 CHECK_EQ(true, result->BooleanValue());
Steve Block3ce2e202009-11-05 08:53:23 +000012047 }
12048
Ben Murdoch8b112d22011-06-08 16:22:53 +010012049 for (int i = 0; i < kElementCount; i++) {
12050 array->set(i, static_cast<ElementType>(i));
12051 }
12052 // Test complex assignments
12053 result = CompileRun("function ee_op_test_complex_func(sum) {"
12054 " for (var i = 0; i < 40; ++i) {"
12055 " sum += (ext_array[i] += 1);"
12056 " sum += (ext_array[i] -= 1);"
12057 " } "
12058 " return sum;"
12059 "}"
12060 "sum=0;"
12061 "for (var i=0;i<10000;++i) {"
12062 " sum=ee_op_test_complex_func(sum);"
12063 "}"
12064 "sum;");
12065 CHECK_EQ(16000000, result->Int32Value());
12066
12067 // Test count operations
12068 result = CompileRun("function ee_op_test_count_func(sum) {"
12069 " for (var i = 0; i < 40; ++i) {"
12070 " sum += (++ext_array[i]);"
12071 " sum += (--ext_array[i]);"
12072 " } "
12073 " return sum;"
12074 "}"
12075 "sum=0;"
12076 "for (var i=0;i<10000;++i) {"
12077 " sum=ee_op_test_count_func(sum);"
12078 "}"
12079 "sum;");
12080 CHECK_EQ(16000000, result->Int32Value());
12081
Steve Block3ce2e202009-11-05 08:53:23 +000012082 result = CompileRun("ext_array[3] = 33;"
12083 "delete ext_array[3];"
12084 "ext_array[3];");
12085 CHECK_EQ(33, result->Int32Value());
12086
12087 result = CompileRun("ext_array[0] = 10; ext_array[1] = 11;"
12088 "ext_array[2] = 12; ext_array[3] = 13;"
12089 "ext_array.__defineGetter__('2',"
12090 "function() { return 120; });"
12091 "ext_array[2];");
12092 CHECK_EQ(12, result->Int32Value());
12093
12094 result = CompileRun("var js_array = new Array(40);"
12095 "js_array[0] = 77;"
12096 "js_array;");
12097 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
12098
12099 result = CompileRun("ext_array[1] = 23;"
12100 "ext_array.__proto__ = [];"
12101 "js_array.__proto__ = ext_array;"
12102 "js_array.concat(ext_array);");
12103 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
12104 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value());
12105
12106 result = CompileRun("ext_array[1] = 23;");
12107 CHECK_EQ(23, result->Int32Value());
12108
Steve Blockd0582a62009-12-15 09:54:21 +000012109 // Test more complex manipulations which cause eax to contain values
12110 // that won't be completely overwritten by loads from the arrays.
12111 // This catches bugs in the instructions used for the KeyedLoadIC
12112 // for byte and word types.
12113 {
12114 const int kXSize = 300;
12115 const int kYSize = 300;
12116 const int kLargeElementCount = kXSize * kYSize * 4;
12117 ElementType* large_array_data =
12118 static_cast<ElementType*>(malloc(kLargeElementCount * element_size));
12119 i::Handle<ExternalArrayClass> large_array =
12120 i::Handle<ExternalArrayClass>::cast(
Steve Block44f0eee2011-05-26 01:26:41 +010012121 FACTORY->NewExternalArray(kLargeElementCount,
Steve Blockd0582a62009-12-15 09:54:21 +000012122 array_type,
12123 array_data));
12124 v8::Handle<v8::Object> large_obj = v8::Object::New();
12125 // Set the elements to be the external array.
12126 large_obj->SetIndexedPropertiesToExternalArrayData(large_array_data,
12127 array_type,
12128 kLargeElementCount);
12129 context->Global()->Set(v8_str("large_array"), large_obj);
12130 // Initialize contents of a few rows.
12131 for (int x = 0; x < 300; x++) {
12132 int row = 0;
12133 int offset = row * 300 * 4;
12134 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
12135 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
12136 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
12137 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
12138 row = 150;
12139 offset = row * 300 * 4;
12140 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
12141 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
12142 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
12143 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
12144 row = 298;
12145 offset = row * 300 * 4;
12146 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
12147 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
12148 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
12149 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
12150 }
12151 // The goal of the code below is to make "offset" large enough
12152 // that the computation of the index (which goes into eax) has
12153 // high bits set which will not be overwritten by a byte or short
12154 // load.
12155 result = CompileRun("var failed = false;"
12156 "var offset = 0;"
12157 "for (var i = 0; i < 300; i++) {"
12158 " if (large_array[4 * i] != 127 ||"
12159 " large_array[4 * i + 1] != 0 ||"
12160 " large_array[4 * i + 2] != 0 ||"
12161 " large_array[4 * i + 3] != 127) {"
12162 " failed = true;"
12163 " }"
12164 "}"
12165 "offset = 150 * 300 * 4;"
12166 "for (var i = 0; i < 300; i++) {"
12167 " if (large_array[offset + 4 * i] != 127 ||"
12168 " large_array[offset + 4 * i + 1] != 0 ||"
12169 " large_array[offset + 4 * i + 2] != 0 ||"
12170 " large_array[offset + 4 * i + 3] != 127) {"
12171 " failed = true;"
12172 " }"
12173 "}"
12174 "offset = 298 * 300 * 4;"
12175 "for (var i = 0; i < 300; i++) {"
12176 " if (large_array[offset + 4 * i] != 127 ||"
12177 " large_array[offset + 4 * i + 1] != 0 ||"
12178 " large_array[offset + 4 * i + 2] != 0 ||"
12179 " large_array[offset + 4 * i + 3] != 127) {"
12180 " failed = true;"
12181 " }"
12182 "}"
12183 "!failed;");
12184 CHECK_EQ(true, result->BooleanValue());
12185 free(large_array_data);
12186 }
12187
Steve Block44f0eee2011-05-26 01:26:41 +010012188 // The "" property descriptor is overloaded to store information about
12189 // the external array. Ensure that setting and accessing the "" property
12190 // works (it should overwrite the information cached about the external
12191 // array in the DescriptorArray) in various situations.
12192 result = CompileRun("ext_array[''] = 23; ext_array['']");
12193 CHECK_EQ(23, result->Int32Value());
12194
12195 // Property "" set after the external array is associated with the object.
12196 {
12197 v8::Handle<v8::Object> obj2 = v8::Object::New();
12198 obj2->Set(v8_str("ee_test_field"), v8::Int32::New(256));
12199 obj2->Set(v8_str(""), v8::Int32::New(1503));
12200 // Set the elements to be the external array.
12201 obj2->SetIndexedPropertiesToExternalArrayData(array_data,
12202 array_type,
12203 kElementCount);
12204 context->Global()->Set(v8_str("ext_array"), obj2);
12205 result = CompileRun("ext_array['']");
12206 CHECK_EQ(1503, result->Int32Value());
12207 }
12208
12209 // Property "" set after the external array is associated with the object.
12210 {
12211 v8::Handle<v8::Object> obj2 = v8::Object::New();
12212 obj2->Set(v8_str("ee_test_field_2"), v8::Int32::New(256));
12213 // Set the elements to be the external array.
12214 obj2->SetIndexedPropertiesToExternalArrayData(array_data,
12215 array_type,
12216 kElementCount);
12217 obj2->Set(v8_str(""), v8::Int32::New(1503));
12218 context->Global()->Set(v8_str("ext_array"), obj2);
12219 result = CompileRun("ext_array['']");
12220 CHECK_EQ(1503, result->Int32Value());
12221 }
12222
12223 // Should reuse the map from previous test.
12224 {
12225 v8::Handle<v8::Object> obj2 = v8::Object::New();
12226 obj2->Set(v8_str("ee_test_field_2"), v8::Int32::New(256));
12227 // Set the elements to be the external array. Should re-use the map
12228 // from previous test.
12229 obj2->SetIndexedPropertiesToExternalArrayData(array_data,
12230 array_type,
12231 kElementCount);
12232 context->Global()->Set(v8_str("ext_array"), obj2);
12233 result = CompileRun("ext_array['']");
12234 }
12235
12236 // Property "" is a constant function that shouldn't not be interfered with
12237 // when an external array is set.
12238 {
12239 v8::Handle<v8::Object> obj2 = v8::Object::New();
12240 // Start
12241 obj2->Set(v8_str("ee_test_field3"), v8::Int32::New(256));
12242
12243 // Add a constant function to an object.
12244 context->Global()->Set(v8_str("ext_array"), obj2);
12245 result = CompileRun("ext_array[''] = function() {return 1503;};"
12246 "ext_array['']();");
12247
12248 // Add an external array transition to the same map that
12249 // has the constant transition.
12250 v8::Handle<v8::Object> obj3 = v8::Object::New();
12251 obj3->Set(v8_str("ee_test_field3"), v8::Int32::New(256));
12252 obj3->SetIndexedPropertiesToExternalArrayData(array_data,
12253 array_type,
12254 kElementCount);
12255 context->Global()->Set(v8_str("ext_array"), obj3);
12256 }
12257
12258 // If a external array transition is in the map, it should get clobbered
12259 // by a constant function.
12260 {
12261 // Add an external array transition.
12262 v8::Handle<v8::Object> obj3 = v8::Object::New();
12263 obj3->Set(v8_str("ee_test_field4"), v8::Int32::New(256));
12264 obj3->SetIndexedPropertiesToExternalArrayData(array_data,
12265 array_type,
12266 kElementCount);
12267
12268 // Add a constant function to the same map that just got an external array
12269 // transition.
12270 v8::Handle<v8::Object> obj2 = v8::Object::New();
12271 obj2->Set(v8_str("ee_test_field4"), v8::Int32::New(256));
12272 context->Global()->Set(v8_str("ext_array"), obj2);
12273 result = CompileRun("ext_array[''] = function() {return 1503;};"
12274 "ext_array['']();");
12275 }
12276
Steve Block3ce2e202009-11-05 08:53:23 +000012277 free(array_data);
12278}
12279
12280
12281THREADED_TEST(ExternalByteArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010012282 ExternalArrayTestHelper<i::ExternalByteArray, int8_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000012283 v8::kExternalByteArray,
12284 -128,
12285 127);
12286}
12287
12288
12289THREADED_TEST(ExternalUnsignedByteArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010012290 ExternalArrayTestHelper<i::ExternalUnsignedByteArray, uint8_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000012291 v8::kExternalUnsignedByteArray,
12292 0,
12293 255);
12294}
12295
12296
Steve Block44f0eee2011-05-26 01:26:41 +010012297THREADED_TEST(ExternalPixelArray) {
12298 ExternalArrayTestHelper<i::ExternalPixelArray, uint8_t>(
12299 v8::kExternalPixelArray,
12300 0,
12301 255);
12302}
12303
12304
Steve Block3ce2e202009-11-05 08:53:23 +000012305THREADED_TEST(ExternalShortArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010012306 ExternalArrayTestHelper<i::ExternalShortArray, int16_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000012307 v8::kExternalShortArray,
12308 -32768,
12309 32767);
12310}
12311
12312
12313THREADED_TEST(ExternalUnsignedShortArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010012314 ExternalArrayTestHelper<i::ExternalUnsignedShortArray, uint16_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000012315 v8::kExternalUnsignedShortArray,
12316 0,
12317 65535);
12318}
12319
12320
12321THREADED_TEST(ExternalIntArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010012322 ExternalArrayTestHelper<i::ExternalIntArray, int32_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000012323 v8::kExternalIntArray,
12324 INT_MIN, // -2147483648
12325 INT_MAX); // 2147483647
12326}
12327
12328
12329THREADED_TEST(ExternalUnsignedIntArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010012330 ExternalArrayTestHelper<i::ExternalUnsignedIntArray, uint32_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000012331 v8::kExternalUnsignedIntArray,
12332 0,
12333 UINT_MAX); // 4294967295
12334}
12335
12336
12337THREADED_TEST(ExternalFloatArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010012338 ExternalArrayTestHelper<i::ExternalFloatArray, float>(
Steve Block3ce2e202009-11-05 08:53:23 +000012339 v8::kExternalFloatArray,
12340 -500,
12341 500);
12342}
12343
12344
Ben Murdoch257744e2011-11-30 15:57:28 +000012345THREADED_TEST(ExternalDoubleArray) {
12346 ExternalArrayTestHelper<i::ExternalDoubleArray, double>(
12347 v8::kExternalDoubleArray,
12348 -500,
12349 500);
12350}
12351
12352
Steve Block3ce2e202009-11-05 08:53:23 +000012353THREADED_TEST(ExternalArrays) {
12354 TestExternalByteArray();
12355 TestExternalUnsignedByteArray();
12356 TestExternalShortArray();
12357 TestExternalUnsignedShortArray();
12358 TestExternalIntArray();
12359 TestExternalUnsignedIntArray();
12360 TestExternalFloatArray();
12361}
12362
12363
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010012364void ExternalArrayInfoTestHelper(v8::ExternalArrayType array_type) {
12365 v8::HandleScope scope;
12366 LocalContext context;
12367 for (int size = 0; size < 100; size += 10) {
12368 int element_size = ExternalArrayElementSize(array_type);
12369 void* external_data = malloc(size * element_size);
12370 v8::Handle<v8::Object> obj = v8::Object::New();
12371 obj->SetIndexedPropertiesToExternalArrayData(
12372 external_data, array_type, size);
12373 CHECK(obj->HasIndexedPropertiesInExternalArrayData());
12374 CHECK_EQ(external_data, obj->GetIndexedPropertiesExternalArrayData());
12375 CHECK_EQ(array_type, obj->GetIndexedPropertiesExternalArrayDataType());
12376 CHECK_EQ(size, obj->GetIndexedPropertiesExternalArrayDataLength());
12377 free(external_data);
12378 }
12379}
12380
12381
12382THREADED_TEST(ExternalArrayInfo) {
12383 ExternalArrayInfoTestHelper(v8::kExternalByteArray);
12384 ExternalArrayInfoTestHelper(v8::kExternalUnsignedByteArray);
12385 ExternalArrayInfoTestHelper(v8::kExternalShortArray);
12386 ExternalArrayInfoTestHelper(v8::kExternalUnsignedShortArray);
12387 ExternalArrayInfoTestHelper(v8::kExternalIntArray);
12388 ExternalArrayInfoTestHelper(v8::kExternalUnsignedIntArray);
12389 ExternalArrayInfoTestHelper(v8::kExternalFloatArray);
Ben Murdoch257744e2011-11-30 15:57:28 +000012390 ExternalArrayInfoTestHelper(v8::kExternalDoubleArray);
Steve Block44f0eee2011-05-26 01:26:41 +010012391 ExternalArrayInfoTestHelper(v8::kExternalPixelArray);
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010012392}
12393
12394
Steve Blocka7e24c12009-10-30 11:49:00 +000012395THREADED_TEST(ScriptContextDependence) {
12396 v8::HandleScope scope;
12397 LocalContext c1;
12398 const char *source = "foo";
12399 v8::Handle<v8::Script> dep = v8::Script::Compile(v8::String::New(source));
12400 v8::Handle<v8::Script> indep = v8::Script::New(v8::String::New(source));
12401 c1->Global()->Set(v8::String::New("foo"), v8::Integer::New(100));
12402 CHECK_EQ(dep->Run()->Int32Value(), 100);
12403 CHECK_EQ(indep->Run()->Int32Value(), 100);
12404 LocalContext c2;
12405 c2->Global()->Set(v8::String::New("foo"), v8::Integer::New(101));
12406 CHECK_EQ(dep->Run()->Int32Value(), 100);
12407 CHECK_EQ(indep->Run()->Int32Value(), 101);
12408}
12409
12410
12411THREADED_TEST(StackTrace) {
12412 v8::HandleScope scope;
12413 LocalContext context;
12414 v8::TryCatch try_catch;
12415 const char *source = "function foo() { FAIL.FAIL; }; foo();";
12416 v8::Handle<v8::String> src = v8::String::New(source);
12417 v8::Handle<v8::String> origin = v8::String::New("stack-trace-test");
12418 v8::Script::New(src, origin)->Run();
12419 CHECK(try_catch.HasCaught());
12420 v8::String::Utf8Value stack(try_catch.StackTrace());
12421 CHECK(strstr(*stack, "at foo (stack-trace-test") != NULL);
12422}
12423
12424
Kristian Monsen25f61362010-05-21 11:50:48 +010012425// Checks that a StackFrame has certain expected values.
12426void checkStackFrame(const char* expected_script_name,
12427 const char* expected_func_name, int expected_line_number,
12428 int expected_column, bool is_eval, bool is_constructor,
12429 v8::Handle<v8::StackFrame> frame) {
12430 v8::HandleScope scope;
12431 v8::String::Utf8Value func_name(frame->GetFunctionName());
12432 v8::String::Utf8Value script_name(frame->GetScriptName());
12433 if (*script_name == NULL) {
12434 // The situation where there is no associated script, like for evals.
12435 CHECK(expected_script_name == NULL);
12436 } else {
12437 CHECK(strstr(*script_name, expected_script_name) != NULL);
12438 }
12439 CHECK(strstr(*func_name, expected_func_name) != NULL);
12440 CHECK_EQ(expected_line_number, frame->GetLineNumber());
12441 CHECK_EQ(expected_column, frame->GetColumn());
12442 CHECK_EQ(is_eval, frame->IsEval());
12443 CHECK_EQ(is_constructor, frame->IsConstructor());
12444}
12445
12446
12447v8::Handle<Value> AnalyzeStackInNativeCode(const v8::Arguments& args) {
12448 v8::HandleScope scope;
12449 const char* origin = "capture-stack-trace-test";
12450 const int kOverviewTest = 1;
12451 const int kDetailedTest = 2;
12452
12453 ASSERT(args.Length() == 1);
12454
12455 int testGroup = args[0]->Int32Value();
12456 if (testGroup == kOverviewTest) {
12457 v8::Handle<v8::StackTrace> stackTrace =
12458 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kOverview);
12459 CHECK_EQ(4, stackTrace->GetFrameCount());
12460 checkStackFrame(origin, "bar", 2, 10, false, false,
12461 stackTrace->GetFrame(0));
12462 checkStackFrame(origin, "foo", 6, 3, false, false,
12463 stackTrace->GetFrame(1));
12464 checkStackFrame(NULL, "", 1, 1, false, false,
12465 stackTrace->GetFrame(2));
12466 // The last frame is an anonymous function that has the initial call.
12467 checkStackFrame(origin, "", 8, 7, false, false,
12468 stackTrace->GetFrame(3));
12469
12470 CHECK(stackTrace->AsArray()->IsArray());
12471 } else if (testGroup == kDetailedTest) {
12472 v8::Handle<v8::StackTrace> stackTrace =
12473 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kDetailed);
12474 CHECK_EQ(4, stackTrace->GetFrameCount());
12475 checkStackFrame(origin, "bat", 4, 22, false, false,
12476 stackTrace->GetFrame(0));
12477 checkStackFrame(origin, "baz", 8, 3, false, true,
12478 stackTrace->GetFrame(1));
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010012479#ifdef ENABLE_DEBUGGER_SUPPORT
12480 bool is_eval = true;
12481#else // ENABLE_DEBUGGER_SUPPORT
12482 bool is_eval = false;
12483#endif // ENABLE_DEBUGGER_SUPPORT
12484
12485 checkStackFrame(NULL, "", 1, 1, is_eval, false,
Kristian Monsen25f61362010-05-21 11:50:48 +010012486 stackTrace->GetFrame(2));
12487 // The last frame is an anonymous function that has the initial call to foo.
12488 checkStackFrame(origin, "", 10, 1, false, false,
12489 stackTrace->GetFrame(3));
12490
12491 CHECK(stackTrace->AsArray()->IsArray());
12492 }
12493 return v8::Undefined();
12494}
12495
12496
12497// Tests the C++ StackTrace API.
Ben Murdochb0fe1622011-05-05 13:52:32 +010012498// TODO(3074796): Reenable this as a THREADED_TEST once it passes.
12499// THREADED_TEST(CaptureStackTrace) {
12500TEST(CaptureStackTrace) {
Kristian Monsen25f61362010-05-21 11:50:48 +010012501 v8::HandleScope scope;
12502 v8::Handle<v8::String> origin = v8::String::New("capture-stack-trace-test");
12503 Local<ObjectTemplate> templ = ObjectTemplate::New();
12504 templ->Set(v8_str("AnalyzeStackInNativeCode"),
12505 v8::FunctionTemplate::New(AnalyzeStackInNativeCode));
12506 LocalContext context(0, templ);
12507
12508 // Test getting OVERVIEW information. Should ignore information that is not
12509 // script name, function name, line number, and column offset.
12510 const char *overview_source =
12511 "function bar() {\n"
12512 " var y; AnalyzeStackInNativeCode(1);\n"
12513 "}\n"
12514 "function foo() {\n"
12515 "\n"
12516 " bar();\n"
12517 "}\n"
12518 "var x;eval('new foo();');";
12519 v8::Handle<v8::String> overview_src = v8::String::New(overview_source);
12520 v8::Handle<Value> overview_result =
12521 v8::Script::New(overview_src, origin)->Run();
12522 ASSERT(!overview_result.IsEmpty());
12523 ASSERT(overview_result->IsObject());
12524
12525 // Test getting DETAILED information.
12526 const char *detailed_source =
12527 "function bat() {AnalyzeStackInNativeCode(2);\n"
12528 "}\n"
12529 "\n"
12530 "function baz() {\n"
12531 " bat();\n"
12532 "}\n"
12533 "eval('new baz();');";
12534 v8::Handle<v8::String> detailed_src = v8::String::New(detailed_source);
12535 // Make the script using a non-zero line and column offset.
12536 v8::Handle<v8::Integer> line_offset = v8::Integer::New(3);
12537 v8::Handle<v8::Integer> column_offset = v8::Integer::New(5);
12538 v8::ScriptOrigin detailed_origin(origin, line_offset, column_offset);
12539 v8::Handle<v8::Script> detailed_script(
12540 v8::Script::New(detailed_src, &detailed_origin));
12541 v8::Handle<Value> detailed_result = detailed_script->Run();
12542 ASSERT(!detailed_result.IsEmpty());
12543 ASSERT(detailed_result->IsObject());
12544}
12545
12546
Ben Murdoch3bec4d22010-07-22 14:51:16 +010012547static void StackTraceForUncaughtExceptionListener(
12548 v8::Handle<v8::Message> message,
12549 v8::Handle<Value>) {
12550 v8::Handle<v8::StackTrace> stack_trace = message->GetStackTrace();
12551 CHECK_EQ(2, stack_trace->GetFrameCount());
12552 checkStackFrame("origin", "foo", 2, 3, false, false,
12553 stack_trace->GetFrame(0));
12554 checkStackFrame("origin", "bar", 5, 3, false, false,
12555 stack_trace->GetFrame(1));
12556}
12557
12558TEST(CaptureStackTraceForUncaughtException) {
12559 report_count = 0;
12560 v8::HandleScope scope;
12561 LocalContext env;
12562 v8::V8::AddMessageListener(StackTraceForUncaughtExceptionListener);
12563 v8::V8::SetCaptureStackTraceForUncaughtExceptions(true);
12564
12565 Script::Compile(v8_str("function foo() {\n"
12566 " throw 1;\n"
12567 "};\n"
12568 "function bar() {\n"
12569 " foo();\n"
12570 "};"),
12571 v8_str("origin"))->Run();
12572 v8::Local<v8::Object> global = env->Global();
12573 Local<Value> trouble = global->Get(v8_str("bar"));
12574 CHECK(trouble->IsFunction());
12575 Function::Cast(*trouble)->Call(global, 0, NULL);
12576 v8::V8::SetCaptureStackTraceForUncaughtExceptions(false);
12577 v8::V8::RemoveMessageListeners(StackTraceForUncaughtExceptionListener);
12578}
12579
12580
Steve Block1e0659c2011-05-24 12:43:12 +010012581TEST(CaptureStackTraceForUncaughtExceptionAndSetters) {
12582 v8::HandleScope scope;
12583 LocalContext env;
12584 v8::V8::SetCaptureStackTraceForUncaughtExceptions(true,
12585 1024,
12586 v8::StackTrace::kDetailed);
12587
12588 CompileRun(
12589 "var setters = ['column', 'lineNumber', 'scriptName',\n"
12590 " 'scriptNameOrSourceURL', 'functionName', 'isEval',\n"
12591 " 'isConstructor'];\n"
12592 "for (var i = 0; i < setters.length; i++) {\n"
12593 " var prop = setters[i];\n"
12594 " Object.prototype.__defineSetter__(prop, function() { throw prop; });\n"
12595 "}\n");
12596 CompileRun("throw 'exception';");
12597 v8::V8::SetCaptureStackTraceForUncaughtExceptions(false);
12598}
12599
12600
Ben Murdochf87a2032010-10-22 12:50:53 +010012601v8::Handle<Value> AnalyzeStackOfEvalWithSourceURL(const v8::Arguments& args) {
12602 v8::HandleScope scope;
12603 v8::Handle<v8::StackTrace> stackTrace =
12604 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kDetailed);
12605 CHECK_EQ(5, stackTrace->GetFrameCount());
12606 v8::Handle<v8::String> url = v8_str("eval_url");
12607 for (int i = 0; i < 3; i++) {
12608 v8::Handle<v8::String> name =
12609 stackTrace->GetFrame(i)->GetScriptNameOrSourceURL();
12610 CHECK(!name.IsEmpty());
12611 CHECK_EQ(url, name);
12612 }
12613 return v8::Undefined();
12614}
12615
12616
12617TEST(SourceURLInStackTrace) {
12618 v8::HandleScope scope;
12619 Local<ObjectTemplate> templ = ObjectTemplate::New();
12620 templ->Set(v8_str("AnalyzeStackOfEvalWithSourceURL"),
12621 v8::FunctionTemplate::New(AnalyzeStackOfEvalWithSourceURL));
12622 LocalContext context(0, templ);
12623
12624 const char *source =
12625 "function outer() {\n"
12626 "function bar() {\n"
12627 " AnalyzeStackOfEvalWithSourceURL();\n"
12628 "}\n"
12629 "function foo() {\n"
12630 "\n"
12631 " bar();\n"
12632 "}\n"
12633 "foo();\n"
12634 "}\n"
12635 "eval('(' + outer +')()//@ sourceURL=eval_url');";
12636 CHECK(CompileRun(source)->IsUndefined());
12637}
12638
12639
Steve Block3ce2e202009-11-05 08:53:23 +000012640// Test that idle notification can be handled and eventually returns true.
Steve Blocka7e24c12009-10-30 11:49:00 +000012641THREADED_TEST(IdleNotification) {
Steve Block3ce2e202009-11-05 08:53:23 +000012642 bool rv = false;
12643 for (int i = 0; i < 100; i++) {
12644 rv = v8::V8::IdleNotification();
12645 if (rv)
12646 break;
12647 }
12648 CHECK(rv == true);
Steve Blocka7e24c12009-10-30 11:49:00 +000012649}
12650
12651
12652static uint32_t* stack_limit;
12653
12654static v8::Handle<Value> GetStackLimitCallback(const v8::Arguments& args) {
Steve Block44f0eee2011-05-26 01:26:41 +010012655 stack_limit = reinterpret_cast<uint32_t*>(
12656 i::Isolate::Current()->stack_guard()->real_climit());
Steve Blocka7e24c12009-10-30 11:49:00 +000012657 return v8::Undefined();
12658}
12659
12660
12661// Uses the address of a local variable to determine the stack top now.
12662// Given a size, returns an address that is that far from the current
12663// top of stack.
12664static uint32_t* ComputeStackLimit(uint32_t size) {
12665 uint32_t* answer = &size - (size / sizeof(size));
12666 // If the size is very large and the stack is very near the bottom of
12667 // memory then the calculation above may wrap around and give an address
12668 // that is above the (downwards-growing) stack. In that case we return
12669 // a very low address.
12670 if (answer > &size) return reinterpret_cast<uint32_t*>(sizeof(size));
12671 return answer;
12672}
12673
12674
12675TEST(SetResourceConstraints) {
12676 static const int K = 1024;
12677 uint32_t* set_limit = ComputeStackLimit(128 * K);
12678
12679 // Set stack limit.
12680 v8::ResourceConstraints constraints;
12681 constraints.set_stack_limit(set_limit);
12682 CHECK(v8::SetResourceConstraints(&constraints));
12683
12684 // Execute a script.
12685 v8::HandleScope scope;
12686 LocalContext env;
12687 Local<v8::FunctionTemplate> fun_templ =
12688 v8::FunctionTemplate::New(GetStackLimitCallback);
12689 Local<Function> fun = fun_templ->GetFunction();
12690 env->Global()->Set(v8_str("get_stack_limit"), fun);
12691 CompileRun("get_stack_limit();");
12692
12693 CHECK(stack_limit == set_limit);
12694}
12695
12696
12697TEST(SetResourceConstraintsInThread) {
12698 uint32_t* set_limit;
12699 {
12700 v8::Locker locker;
12701 static const int K = 1024;
12702 set_limit = ComputeStackLimit(128 * K);
12703
12704 // Set stack limit.
12705 v8::ResourceConstraints constraints;
12706 constraints.set_stack_limit(set_limit);
12707 CHECK(v8::SetResourceConstraints(&constraints));
12708
12709 // Execute a script.
12710 v8::HandleScope scope;
12711 LocalContext env;
12712 Local<v8::FunctionTemplate> fun_templ =
12713 v8::FunctionTemplate::New(GetStackLimitCallback);
12714 Local<Function> fun = fun_templ->GetFunction();
12715 env->Global()->Set(v8_str("get_stack_limit"), fun);
12716 CompileRun("get_stack_limit();");
12717
12718 CHECK(stack_limit == set_limit);
12719 }
12720 {
12721 v8::Locker locker;
12722 CHECK(stack_limit == set_limit);
12723 }
12724}
Steve Block3ce2e202009-11-05 08:53:23 +000012725
12726
12727THREADED_TEST(GetHeapStatistics) {
12728 v8::HandleScope scope;
12729 LocalContext c1;
12730 v8::HeapStatistics heap_statistics;
Steve Blockd0582a62009-12-15 09:54:21 +000012731 CHECK_EQ(static_cast<int>(heap_statistics.total_heap_size()), 0);
12732 CHECK_EQ(static_cast<int>(heap_statistics.used_heap_size()), 0);
Steve Block3ce2e202009-11-05 08:53:23 +000012733 v8::V8::GetHeapStatistics(&heap_statistics);
Steve Blockd0582a62009-12-15 09:54:21 +000012734 CHECK_NE(static_cast<int>(heap_statistics.total_heap_size()), 0);
12735 CHECK_NE(static_cast<int>(heap_statistics.used_heap_size()), 0);
12736}
12737
12738
12739static double DoubleFromBits(uint64_t value) {
12740 double target;
Steve Blockd0582a62009-12-15 09:54:21 +000012741 memcpy(&target, &value, sizeof(target));
Steve Blockd0582a62009-12-15 09:54:21 +000012742 return target;
12743}
12744
12745
12746static uint64_t DoubleToBits(double value) {
12747 uint64_t target;
Steve Blockd0582a62009-12-15 09:54:21 +000012748 memcpy(&target, &value, sizeof(target));
Steve Blockd0582a62009-12-15 09:54:21 +000012749 return target;
12750}
12751
12752
12753static double DoubleToDateTime(double input) {
12754 double date_limit = 864e13;
12755 if (IsNaN(input) || input < -date_limit || input > date_limit) {
12756 return i::OS::nan_value();
12757 }
12758 return (input < 0) ? -(floor(-input)) : floor(input);
12759}
12760
12761// We don't have a consistent way to write 64-bit constants syntactically, so we
12762// split them into two 32-bit constants and combine them programmatically.
12763static double DoubleFromBits(uint32_t high_bits, uint32_t low_bits) {
12764 return DoubleFromBits((static_cast<uint64_t>(high_bits) << 32) | low_bits);
12765}
12766
12767
12768THREADED_TEST(QuietSignalingNaNs) {
12769 v8::HandleScope scope;
12770 LocalContext context;
12771 v8::TryCatch try_catch;
12772
12773 // Special double values.
12774 double snan = DoubleFromBits(0x7ff00000, 0x00000001);
12775 double qnan = DoubleFromBits(0x7ff80000, 0x00000000);
12776 double infinity = DoubleFromBits(0x7ff00000, 0x00000000);
12777 double max_normal = DoubleFromBits(0x7fefffff, 0xffffffffu);
12778 double min_normal = DoubleFromBits(0x00100000, 0x00000000);
12779 double max_denormal = DoubleFromBits(0x000fffff, 0xffffffffu);
12780 double min_denormal = DoubleFromBits(0x00000000, 0x00000001);
12781
12782 // Date values are capped at +/-100000000 days (times 864e5 ms per day)
12783 // on either side of the epoch.
12784 double date_limit = 864e13;
12785
12786 double test_values[] = {
12787 snan,
12788 qnan,
12789 infinity,
12790 max_normal,
12791 date_limit + 1,
12792 date_limit,
12793 min_normal,
12794 max_denormal,
12795 min_denormal,
12796 0,
12797 -0,
12798 -min_denormal,
12799 -max_denormal,
12800 -min_normal,
12801 -date_limit,
12802 -date_limit - 1,
12803 -max_normal,
12804 -infinity,
12805 -qnan,
12806 -snan
12807 };
12808 int num_test_values = 20;
12809
12810 for (int i = 0; i < num_test_values; i++) {
12811 double test_value = test_values[i];
12812
12813 // Check that Number::New preserves non-NaNs and quiets SNaNs.
12814 v8::Handle<v8::Value> number = v8::Number::New(test_value);
12815 double stored_number = number->NumberValue();
12816 if (!IsNaN(test_value)) {
12817 CHECK_EQ(test_value, stored_number);
12818 } else {
12819 uint64_t stored_bits = DoubleToBits(stored_number);
12820 // Check if quiet nan (bits 51..62 all set).
12821 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff));
12822 }
12823
12824 // Check that Date::New preserves non-NaNs in the date range and
12825 // quiets SNaNs.
12826 v8::Handle<v8::Value> date = v8::Date::New(test_value);
12827 double expected_stored_date = DoubleToDateTime(test_value);
12828 double stored_date = date->NumberValue();
12829 if (!IsNaN(expected_stored_date)) {
12830 CHECK_EQ(expected_stored_date, stored_date);
12831 } else {
12832 uint64_t stored_bits = DoubleToBits(stored_date);
12833 // Check if quiet nan (bits 51..62 all set).
12834 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff));
12835 }
12836 }
12837}
12838
12839
12840static v8::Handle<Value> SpaghettiIncident(const v8::Arguments& args) {
12841 v8::HandleScope scope;
12842 v8::TryCatch tc;
12843 v8::Handle<v8::String> str = args[0]->ToString();
12844 if (tc.HasCaught())
12845 return tc.ReThrow();
12846 return v8::Undefined();
12847}
12848
12849
12850// Test that an exception can be propagated down through a spaghetti
12851// stack using ReThrow.
12852THREADED_TEST(SpaghettiStackReThrow) {
12853 v8::HandleScope scope;
12854 LocalContext context;
12855 context->Global()->Set(
12856 v8::String::New("s"),
12857 v8::FunctionTemplate::New(SpaghettiIncident)->GetFunction());
12858 v8::TryCatch try_catch;
12859 CompileRun(
12860 "var i = 0;"
12861 "var o = {"
12862 " toString: function () {"
12863 " if (i == 10) {"
12864 " throw 'Hey!';"
12865 " } else {"
12866 " i++;"
12867 " return s(o);"
12868 " }"
12869 " }"
12870 "};"
12871 "s(o);");
12872 CHECK(try_catch.HasCaught());
12873 v8::String::Utf8Value value(try_catch.Exception());
12874 CHECK_EQ(0, strcmp(*value, "Hey!"));
12875}
12876
12877
Steve Blockd0582a62009-12-15 09:54:21 +000012878TEST(Regress528) {
12879 v8::V8::Initialize();
12880
12881 v8::HandleScope scope;
12882 v8::Persistent<Context> context;
12883 v8::Persistent<Context> other_context;
12884 int gc_count;
12885
12886 // Create a context used to keep the code from aging in the compilation
12887 // cache.
12888 other_context = Context::New();
12889
12890 // Context-dependent context data creates reference from the compilation
12891 // cache to the global object.
12892 const char* source_simple = "1";
12893 context = Context::New();
12894 {
12895 v8::HandleScope scope;
12896
12897 context->Enter();
12898 Local<v8::String> obj = v8::String::New("");
12899 context->SetData(obj);
12900 CompileRun(source_simple);
12901 context->Exit();
12902 }
12903 context.Dispose();
12904 for (gc_count = 1; gc_count < 10; gc_count++) {
12905 other_context->Enter();
12906 CompileRun(source_simple);
12907 other_context->Exit();
Steve Block44f0eee2011-05-26 01:26:41 +010012908 HEAP->CollectAllGarbage(false);
Steve Blockd0582a62009-12-15 09:54:21 +000012909 if (GetGlobalObjectsCount() == 1) break;
12910 }
12911 CHECK_GE(2, gc_count);
12912 CHECK_EQ(1, GetGlobalObjectsCount());
12913
12914 // Eval in a function creates reference from the compilation cache to the
12915 // global object.
12916 const char* source_eval = "function f(){eval('1')}; f()";
12917 context = Context::New();
12918 {
12919 v8::HandleScope scope;
12920
12921 context->Enter();
12922 CompileRun(source_eval);
12923 context->Exit();
12924 }
12925 context.Dispose();
12926 for (gc_count = 1; gc_count < 10; gc_count++) {
12927 other_context->Enter();
12928 CompileRun(source_eval);
12929 other_context->Exit();
Steve Block44f0eee2011-05-26 01:26:41 +010012930 HEAP->CollectAllGarbage(false);
Steve Blockd0582a62009-12-15 09:54:21 +000012931 if (GetGlobalObjectsCount() == 1) break;
12932 }
12933 CHECK_GE(2, gc_count);
12934 CHECK_EQ(1, GetGlobalObjectsCount());
12935
12936 // Looking up the line number for an exception creates reference from the
12937 // compilation cache to the global object.
12938 const char* source_exception = "function f(){throw 1;} f()";
12939 context = Context::New();
12940 {
12941 v8::HandleScope scope;
12942
12943 context->Enter();
12944 v8::TryCatch try_catch;
12945 CompileRun(source_exception);
12946 CHECK(try_catch.HasCaught());
12947 v8::Handle<v8::Message> message = try_catch.Message();
12948 CHECK(!message.IsEmpty());
12949 CHECK_EQ(1, message->GetLineNumber());
12950 context->Exit();
12951 }
12952 context.Dispose();
12953 for (gc_count = 1; gc_count < 10; gc_count++) {
12954 other_context->Enter();
12955 CompileRun(source_exception);
12956 other_context->Exit();
Steve Block44f0eee2011-05-26 01:26:41 +010012957 HEAP->CollectAllGarbage(false);
Steve Blockd0582a62009-12-15 09:54:21 +000012958 if (GetGlobalObjectsCount() == 1) break;
12959 }
12960 CHECK_GE(2, gc_count);
12961 CHECK_EQ(1, GetGlobalObjectsCount());
12962
12963 other_context.Dispose();
Steve Block3ce2e202009-11-05 08:53:23 +000012964}
Andrei Popescu402d9372010-02-26 13:31:12 +000012965
12966
12967THREADED_TEST(ScriptOrigin) {
12968 v8::HandleScope scope;
12969 LocalContext env;
12970 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test"));
12971 v8::Handle<v8::String> script = v8::String::New(
12972 "function f() {}\n\nfunction g() {}");
12973 v8::Script::Compile(script, &origin)->Run();
12974 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(
12975 env->Global()->Get(v8::String::New("f")));
12976 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast(
12977 env->Global()->Get(v8::String::New("g")));
12978
12979 v8::ScriptOrigin script_origin_f = f->GetScriptOrigin();
12980 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_f.ResourceName()));
12981 CHECK_EQ(0, script_origin_f.ResourceLineOffset()->Int32Value());
12982
12983 v8::ScriptOrigin script_origin_g = g->GetScriptOrigin();
12984 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_g.ResourceName()));
12985 CHECK_EQ(0, script_origin_g.ResourceLineOffset()->Int32Value());
12986}
12987
12988
12989THREADED_TEST(ScriptLineNumber) {
12990 v8::HandleScope scope;
12991 LocalContext env;
12992 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test"));
12993 v8::Handle<v8::String> script = v8::String::New(
12994 "function f() {}\n\nfunction g() {}");
12995 v8::Script::Compile(script, &origin)->Run();
12996 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(
12997 env->Global()->Get(v8::String::New("f")));
12998 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast(
12999 env->Global()->Get(v8::String::New("g")));
13000 CHECK_EQ(0, f->GetScriptLineNumber());
13001 CHECK_EQ(2, g->GetScriptLineNumber());
13002}
13003
13004
13005static v8::Handle<Value> GetterWhichReturns42(Local<String> name,
13006 const AccessorInfo& info) {
13007 return v8_num(42);
13008}
13009
13010
13011static void SetterWhichSetsYOnThisTo23(Local<String> name,
13012 Local<Value> value,
13013 const AccessorInfo& info) {
13014 info.This()->Set(v8_str("y"), v8_num(23));
13015}
13016
13017
Steve Block6ded16b2010-05-10 14:33:55 +010013018TEST(SetterOnConstructorPrototype) {
Andrei Popescu402d9372010-02-26 13:31:12 +000013019 v8::HandleScope scope;
13020 Local<ObjectTemplate> templ = ObjectTemplate::New();
13021 templ->SetAccessor(v8_str("x"),
13022 GetterWhichReturns42,
13023 SetterWhichSetsYOnThisTo23);
13024 LocalContext context;
13025 context->Global()->Set(v8_str("P"), templ->NewInstance());
13026 CompileRun("function C1() {"
13027 " this.x = 23;"
13028 "};"
13029 "C1.prototype = P;"
13030 "function C2() {"
13031 " this.x = 23"
13032 "};"
13033 "C2.prototype = { };"
13034 "C2.prototype.__proto__ = P;");
13035
13036 v8::Local<v8::Script> script;
13037 script = v8::Script::Compile(v8_str("new C1();"));
13038 for (int i = 0; i < 10; i++) {
13039 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
13040 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value());
13041 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value());
13042 }
13043
13044 script = v8::Script::Compile(v8_str("new C2();"));
13045 for (int i = 0; i < 10; i++) {
13046 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run());
13047 CHECK_EQ(42, c2->Get(v8_str("x"))->Int32Value());
13048 CHECK_EQ(23, c2->Get(v8_str("y"))->Int32Value());
13049 }
13050}
13051
13052
13053static v8::Handle<Value> NamedPropertyGetterWhichReturns42(
13054 Local<String> name, const AccessorInfo& info) {
13055 return v8_num(42);
13056}
13057
13058
13059static v8::Handle<Value> NamedPropertySetterWhichSetsYOnThisTo23(
13060 Local<String> name, Local<Value> value, const AccessorInfo& info) {
13061 if (name->Equals(v8_str("x"))) {
13062 info.This()->Set(v8_str("y"), v8_num(23));
13063 }
13064 return v8::Handle<Value>();
13065}
13066
13067
13068THREADED_TEST(InterceptorOnConstructorPrototype) {
13069 v8::HandleScope scope;
13070 Local<ObjectTemplate> templ = ObjectTemplate::New();
13071 templ->SetNamedPropertyHandler(NamedPropertyGetterWhichReturns42,
13072 NamedPropertySetterWhichSetsYOnThisTo23);
13073 LocalContext context;
13074 context->Global()->Set(v8_str("P"), templ->NewInstance());
13075 CompileRun("function C1() {"
13076 " this.x = 23;"
13077 "};"
13078 "C1.prototype = P;"
13079 "function C2() {"
13080 " this.x = 23"
13081 "};"
13082 "C2.prototype = { };"
13083 "C2.prototype.__proto__ = P;");
13084
13085 v8::Local<v8::Script> script;
13086 script = v8::Script::Compile(v8_str("new C1();"));
13087 for (int i = 0; i < 10; i++) {
13088 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
13089 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value());
13090 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value());
13091 }
13092
13093 script = v8::Script::Compile(v8_str("new C2();"));
13094 for (int i = 0; i < 10; i++) {
13095 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run());
13096 CHECK_EQ(23, c2->Get(v8_str("x"))->Int32Value());
13097 CHECK_EQ(42, c2->Get(v8_str("y"))->Int32Value());
13098 }
13099}
Steve Block6ded16b2010-05-10 14:33:55 +010013100
13101
13102TEST(Bug618) {
13103 const char* source = "function C1() {"
13104 " this.x = 23;"
13105 "};"
13106 "C1.prototype = P;";
13107
13108 v8::HandleScope scope;
13109 LocalContext context;
13110 v8::Local<v8::Script> script;
13111
13112 // Use a simple object as prototype.
13113 v8::Local<v8::Object> prototype = v8::Object::New();
13114 prototype->Set(v8_str("y"), v8_num(42));
13115 context->Global()->Set(v8_str("P"), prototype);
13116
13117 // This compile will add the code to the compilation cache.
13118 CompileRun(source);
13119
13120 script = v8::Script::Compile(v8_str("new C1();"));
Kristian Monsen0d5e1162010-09-30 15:31:59 +010013121 // Allow enough iterations for the inobject slack tracking logic
13122 // to finalize instance size and install the fast construct stub.
13123 for (int i = 0; i < 256; i++) {
Steve Block6ded16b2010-05-10 14:33:55 +010013124 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
13125 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value());
13126 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value());
13127 }
13128
13129 // Use an API object with accessors as prototype.
13130 Local<ObjectTemplate> templ = ObjectTemplate::New();
13131 templ->SetAccessor(v8_str("x"),
13132 GetterWhichReturns42,
13133 SetterWhichSetsYOnThisTo23);
13134 context->Global()->Set(v8_str("P"), templ->NewInstance());
13135
13136 // This compile will get the code from the compilation cache.
13137 CompileRun(source);
13138
13139 script = v8::Script::Compile(v8_str("new C1();"));
13140 for (int i = 0; i < 10; i++) {
13141 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
13142 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value());
13143 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value());
13144 }
13145}
13146
13147int prologue_call_count = 0;
13148int epilogue_call_count = 0;
13149int prologue_call_count_second = 0;
13150int epilogue_call_count_second = 0;
13151
13152void PrologueCallback(v8::GCType, v8::GCCallbackFlags) {
13153 ++prologue_call_count;
13154}
13155
13156void EpilogueCallback(v8::GCType, v8::GCCallbackFlags) {
13157 ++epilogue_call_count;
13158}
13159
13160void PrologueCallbackSecond(v8::GCType, v8::GCCallbackFlags) {
13161 ++prologue_call_count_second;
13162}
13163
13164void EpilogueCallbackSecond(v8::GCType, v8::GCCallbackFlags) {
13165 ++epilogue_call_count_second;
13166}
13167
13168TEST(GCCallbacks) {
13169 LocalContext context;
13170
13171 v8::V8::AddGCPrologueCallback(PrologueCallback);
13172 v8::V8::AddGCEpilogueCallback(EpilogueCallback);
13173 CHECK_EQ(0, prologue_call_count);
13174 CHECK_EQ(0, epilogue_call_count);
Steve Block44f0eee2011-05-26 01:26:41 +010013175 HEAP->CollectAllGarbage(false);
Steve Block6ded16b2010-05-10 14:33:55 +010013176 CHECK_EQ(1, prologue_call_count);
13177 CHECK_EQ(1, epilogue_call_count);
13178 v8::V8::AddGCPrologueCallback(PrologueCallbackSecond);
13179 v8::V8::AddGCEpilogueCallback(EpilogueCallbackSecond);
Steve Block44f0eee2011-05-26 01:26:41 +010013180 HEAP->CollectAllGarbage(false);
Steve Block6ded16b2010-05-10 14:33:55 +010013181 CHECK_EQ(2, prologue_call_count);
13182 CHECK_EQ(2, epilogue_call_count);
13183 CHECK_EQ(1, prologue_call_count_second);
13184 CHECK_EQ(1, epilogue_call_count_second);
13185 v8::V8::RemoveGCPrologueCallback(PrologueCallback);
13186 v8::V8::RemoveGCEpilogueCallback(EpilogueCallback);
Steve Block44f0eee2011-05-26 01:26:41 +010013187 HEAP->CollectAllGarbage(false);
Steve Block6ded16b2010-05-10 14:33:55 +010013188 CHECK_EQ(2, prologue_call_count);
13189 CHECK_EQ(2, epilogue_call_count);
13190 CHECK_EQ(2, prologue_call_count_second);
13191 CHECK_EQ(2, epilogue_call_count_second);
13192 v8::V8::RemoveGCPrologueCallback(PrologueCallbackSecond);
13193 v8::V8::RemoveGCEpilogueCallback(EpilogueCallbackSecond);
Steve Block44f0eee2011-05-26 01:26:41 +010013194 HEAP->CollectAllGarbage(false);
Steve Block6ded16b2010-05-10 14:33:55 +010013195 CHECK_EQ(2, prologue_call_count);
13196 CHECK_EQ(2, epilogue_call_count);
13197 CHECK_EQ(2, prologue_call_count_second);
13198 CHECK_EQ(2, epilogue_call_count_second);
13199}
Kristian Monsen25f61362010-05-21 11:50:48 +010013200
13201
13202THREADED_TEST(AddToJSFunctionResultCache) {
13203 i::FLAG_allow_natives_syntax = true;
13204 v8::HandleScope scope;
13205
13206 LocalContext context;
13207
13208 const char* code =
13209 "(function() {"
13210 " var key0 = 'a';"
13211 " var key1 = 'b';"
13212 " var r0 = %_GetFromCache(0, key0);"
13213 " var r1 = %_GetFromCache(0, key1);"
13214 " var r0_ = %_GetFromCache(0, key0);"
13215 " if (r0 !== r0_)"
13216 " return 'Different results for ' + key0 + ': ' + r0 + ' vs. ' + r0_;"
13217 " var r1_ = %_GetFromCache(0, key1);"
13218 " if (r1 !== r1_)"
13219 " return 'Different results for ' + key1 + ': ' + r1 + ' vs. ' + r1_;"
13220 " return 'PASSED';"
13221 "})()";
Steve Block44f0eee2011-05-26 01:26:41 +010013222 HEAP->ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010013223 ExpectString(code, "PASSED");
13224}
13225
13226
13227static const int k0CacheSize = 16;
13228
13229THREADED_TEST(FillJSFunctionResultCache) {
13230 i::FLAG_allow_natives_syntax = true;
13231 v8::HandleScope scope;
13232
13233 LocalContext context;
13234
13235 const char* code =
13236 "(function() {"
13237 " var k = 'a';"
13238 " var r = %_GetFromCache(0, k);"
13239 " for (var i = 0; i < 16; i++) {"
13240 " %_GetFromCache(0, 'a' + i);"
13241 " };"
13242 " if (r === %_GetFromCache(0, k))"
13243 " return 'FAILED: k0CacheSize is too small';"
13244 " return 'PASSED';"
13245 "})()";
Steve Block44f0eee2011-05-26 01:26:41 +010013246 HEAP->ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010013247 ExpectString(code, "PASSED");
13248}
13249
13250
13251THREADED_TEST(RoundRobinGetFromCache) {
13252 i::FLAG_allow_natives_syntax = true;
13253 v8::HandleScope scope;
13254
13255 LocalContext context;
13256
13257 const char* code =
13258 "(function() {"
13259 " var keys = [];"
13260 " for (var i = 0; i < 16; i++) keys.push(i);"
13261 " var values = [];"
13262 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);"
13263 " for (var i = 0; i < 16; i++) {"
13264 " var v = %_GetFromCache(0, keys[i]);"
13265 " if (v !== values[i])"
13266 " return 'Wrong value for ' + "
13267 " keys[i] + ': ' + v + ' vs. ' + values[i];"
13268 " };"
13269 " return 'PASSED';"
13270 "})()";
Steve Block44f0eee2011-05-26 01:26:41 +010013271 HEAP->ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010013272 ExpectString(code, "PASSED");
13273}
13274
13275
13276THREADED_TEST(ReverseGetFromCache) {
13277 i::FLAG_allow_natives_syntax = true;
13278 v8::HandleScope scope;
13279
13280 LocalContext context;
13281
13282 const char* code =
13283 "(function() {"
13284 " var keys = [];"
13285 " for (var i = 0; i < 16; i++) keys.push(i);"
13286 " var values = [];"
13287 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);"
13288 " for (var i = 15; i >= 16; i--) {"
13289 " var v = %_GetFromCache(0, keys[i]);"
13290 " if (v !== values[i])"
13291 " return 'Wrong value for ' + "
13292 " keys[i] + ': ' + v + ' vs. ' + values[i];"
13293 " };"
13294 " return 'PASSED';"
13295 "})()";
Steve Block44f0eee2011-05-26 01:26:41 +010013296 HEAP->ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010013297 ExpectString(code, "PASSED");
13298}
13299
13300
13301THREADED_TEST(TestEviction) {
13302 i::FLAG_allow_natives_syntax = true;
13303 v8::HandleScope scope;
13304
13305 LocalContext context;
13306
13307 const char* code =
13308 "(function() {"
13309 " for (var i = 0; i < 2*16; i++) {"
13310 " %_GetFromCache(0, 'a' + i);"
13311 " };"
13312 " return 'PASSED';"
13313 "})()";
Steve Block44f0eee2011-05-26 01:26:41 +010013314 HEAP->ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010013315 ExpectString(code, "PASSED");
13316}
Steve Block8defd9f2010-07-08 12:39:36 +010013317
13318
13319THREADED_TEST(TwoByteStringInAsciiCons) {
13320 // See Chromium issue 47824.
13321 v8::HandleScope scope;
13322
13323 LocalContext context;
13324 const char* init_code =
13325 "var str1 = 'abelspendabel';"
13326 "var str2 = str1 + str1 + str1;"
13327 "str2;";
13328 Local<Value> result = CompileRun(init_code);
13329
13330 CHECK(result->IsString());
13331 i::Handle<i::String> string = v8::Utils::OpenHandle(String::Cast(*result));
13332 int length = string->length();
13333 CHECK(string->IsAsciiRepresentation());
13334
13335 FlattenString(string);
13336 i::Handle<i::String> flat_string = FlattenGetString(string);
13337
13338 CHECK(string->IsAsciiRepresentation());
13339 CHECK(flat_string->IsAsciiRepresentation());
13340
13341 // Create external resource.
13342 uint16_t* uc16_buffer = new uint16_t[length + 1];
13343
13344 i::String::WriteToFlat(*flat_string, uc16_buffer, 0, length);
13345 uc16_buffer[length] = 0;
13346
13347 TestResource resource(uc16_buffer);
13348
13349 flat_string->MakeExternal(&resource);
13350
13351 CHECK(flat_string->IsTwoByteRepresentation());
13352
13353 // At this point, we should have a Cons string which is flat and ASCII,
13354 // with a first half that is a two-byte string (although it only contains
13355 // ASCII characters). This is a valid sequence of steps, and it can happen
13356 // in real pages.
13357
13358 CHECK(string->IsAsciiRepresentation());
13359 i::ConsString* cons = i::ConsString::cast(*string);
13360 CHECK_EQ(0, cons->second()->length());
13361 CHECK(cons->first()->IsTwoByteRepresentation());
13362
13363 // Check that some string operations work.
13364
13365 // Atom RegExp.
13366 Local<Value> reresult = CompileRun("str2.match(/abel/g).length;");
13367 CHECK_EQ(6, reresult->Int32Value());
13368
13369 // Nonatom RegExp.
13370 reresult = CompileRun("str2.match(/abe./g).length;");
13371 CHECK_EQ(6, reresult->Int32Value());
13372
13373 reresult = CompileRun("str2.search(/bel/g);");
13374 CHECK_EQ(1, reresult->Int32Value());
13375
13376 reresult = CompileRun("str2.search(/be./g);");
13377 CHECK_EQ(1, reresult->Int32Value());
13378
13379 ExpectTrue("/bel/g.test(str2);");
13380
13381 ExpectTrue("/be./g.test(str2);");
13382
13383 reresult = CompileRun("/bel/g.exec(str2);");
13384 CHECK(!reresult->IsNull());
13385
13386 reresult = CompileRun("/be./g.exec(str2);");
13387 CHECK(!reresult->IsNull());
13388
13389 ExpectString("str2.substring(2, 10);", "elspenda");
13390
13391 ExpectString("str2.substring(2, 20);", "elspendabelabelspe");
13392
13393 ExpectString("str2.charAt(2);", "e");
13394
13395 reresult = CompileRun("str2.charCodeAt(2);");
13396 CHECK_EQ(static_cast<int32_t>('e'), reresult->Int32Value());
13397}
Iain Merrick75681382010-08-19 15:07:18 +010013398
13399
13400// Failed access check callback that performs a GC on each invocation.
13401void FailedAccessCheckCallbackGC(Local<v8::Object> target,
13402 v8::AccessType type,
13403 Local<v8::Value> data) {
Steve Block44f0eee2011-05-26 01:26:41 +010013404 HEAP->CollectAllGarbage(true);
Iain Merrick75681382010-08-19 15:07:18 +010013405}
13406
13407
13408TEST(GCInFailedAccessCheckCallback) {
13409 // Install a failed access check callback that performs a GC on each
13410 // invocation. Then force the callback to be called from va
13411
13412 v8::V8::Initialize();
13413 v8::V8::SetFailedAccessCheckCallbackFunction(&FailedAccessCheckCallbackGC);
13414
13415 v8::HandleScope scope;
13416
13417 // Create an ObjectTemplate for global objects and install access
13418 // check callbacks that will block access.
13419 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
13420 global_template->SetAccessCheckCallbacks(NamedGetAccessBlocker,
13421 IndexedGetAccessBlocker,
13422 v8::Handle<v8::Value>(),
13423 false);
13424
13425 // Create a context and set an x property on it's global object.
13426 LocalContext context0(NULL, global_template);
13427 context0->Global()->Set(v8_str("x"), v8_num(42));
13428 v8::Handle<v8::Object> global0 = context0->Global();
13429
13430 // Create a context with a different security token so that the
13431 // failed access check callback will be called on each access.
13432 LocalContext context1(NULL, global_template);
13433 context1->Global()->Set(v8_str("other"), global0);
13434
13435 // Get property with failed access check.
13436 ExpectUndefined("other.x");
13437
13438 // Get element with failed access check.
13439 ExpectUndefined("other[0]");
13440
13441 // Set property with failed access check.
13442 v8::Handle<v8::Value> result = CompileRun("other.x = new Object()");
13443 CHECK(result->IsObject());
13444
13445 // Set element with failed access check.
13446 result = CompileRun("other[0] = new Object()");
13447 CHECK(result->IsObject());
13448
13449 // Get property attribute with failed access check.
13450 ExpectFalse("\'x\' in other");
13451
13452 // Get property attribute for element with failed access check.
13453 ExpectFalse("0 in other");
13454
13455 // Delete property.
13456 ExpectFalse("delete other.x");
13457
13458 // Delete element.
13459 CHECK_EQ(false, global0->Delete(0));
13460
13461 // DefineAccessor.
13462 CHECK_EQ(false,
13463 global0->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("x")));
13464
13465 // Define JavaScript accessor.
13466 ExpectUndefined("Object.prototype.__defineGetter__.call("
13467 " other, \'x\', function() { return 42; })");
13468
13469 // LookupAccessor.
13470 ExpectUndefined("Object.prototype.__lookupGetter__.call("
13471 " other, \'x\')");
13472
13473 // HasLocalElement.
13474 ExpectFalse("Object.prototype.hasOwnProperty.call(other, \'0\')");
13475
13476 CHECK_EQ(false, global0->HasRealIndexedProperty(0));
13477 CHECK_EQ(false, global0->HasRealNamedProperty(v8_str("x")));
13478 CHECK_EQ(false, global0->HasRealNamedCallbackProperty(v8_str("x")));
13479
13480 // Reset the failed access check callback so it does not influence
13481 // the other tests.
13482 v8::V8::SetFailedAccessCheckCallbackFunction(NULL);
13483}
Kristian Monsen0d5e1162010-09-30 15:31:59 +010013484
Steve Block44f0eee2011-05-26 01:26:41 +010013485TEST(DefaultIsolateGetCurrent) {
13486 CHECK(v8::Isolate::GetCurrent() != NULL);
13487 v8::Isolate* isolate = v8::Isolate::GetCurrent();
13488 CHECK(reinterpret_cast<i::Isolate*>(isolate)->IsDefaultIsolate());
13489 printf("*** %s\n", "DefaultIsolateGetCurrent success");
13490}
13491
13492TEST(IsolateNewDispose) {
13493 v8::Isolate* current_isolate = v8::Isolate::GetCurrent();
13494 v8::Isolate* isolate = v8::Isolate::New();
13495 CHECK(isolate != NULL);
13496 CHECK(!reinterpret_cast<i::Isolate*>(isolate)->IsDefaultIsolate());
13497 CHECK(current_isolate != isolate);
13498 CHECK(current_isolate == v8::Isolate::GetCurrent());
13499
13500 v8::V8::SetFatalErrorHandler(StoringErrorCallback);
13501 last_location = last_message = NULL;
13502 isolate->Dispose();
13503 CHECK_EQ(last_location, NULL);
13504 CHECK_EQ(last_message, NULL);
13505}
13506
13507TEST(IsolateEnterExitDefault) {
13508 v8::HandleScope scope;
13509 LocalContext context;
13510 v8::Isolate* current_isolate = v8::Isolate::GetCurrent();
13511 CHECK(current_isolate != NULL); // Default isolate.
13512 ExpectString("'hello'", "hello");
13513 current_isolate->Enter();
13514 ExpectString("'still working'", "still working");
13515 current_isolate->Exit();
13516 ExpectString("'still working 2'", "still working 2");
13517 current_isolate->Exit();
13518 // Default isolate is always, well, 'default current'.
13519 CHECK_EQ(v8::Isolate::GetCurrent(), current_isolate);
13520 // Still working since default isolate is auto-entering any thread
13521 // that has no isolate and attempts to execute V8 APIs.
13522 ExpectString("'still working 3'", "still working 3");
13523}
13524
13525TEST(DisposeDefaultIsolate) {
13526 v8::V8::SetFatalErrorHandler(StoringErrorCallback);
13527
13528 // Run some V8 code to trigger default isolate to become 'current'.
13529 v8::HandleScope scope;
13530 LocalContext context;
13531 ExpectString("'run some V8'", "run some V8");
13532
13533 v8::Isolate* isolate = v8::Isolate::GetCurrent();
13534 CHECK(reinterpret_cast<i::Isolate*>(isolate)->IsDefaultIsolate());
13535 last_location = last_message = NULL;
13536 isolate->Dispose();
13537 // It is not possible to dispose default isolate via Isolate API.
13538 CHECK_NE(last_location, NULL);
13539 CHECK_NE(last_message, NULL);
13540}
13541
13542TEST(RunDefaultAndAnotherIsolate) {
13543 v8::HandleScope scope;
13544 LocalContext context;
13545
13546 // Enter new isolate.
13547 v8::Isolate* isolate = v8::Isolate::New();
13548 CHECK(isolate);
13549 isolate->Enter();
13550 { // Need this block because subsequent Exit() will deallocate Heap,
13551 // so we need all scope objects to be deconstructed when it happens.
13552 v8::HandleScope scope_new;
13553 LocalContext context_new;
13554
13555 // Run something in new isolate.
13556 CompileRun("var foo = 153;");
13557 ExpectTrue("function f() { return foo == 153; }; f()");
13558 }
13559 isolate->Exit();
13560
13561 // This runs automatically in default isolate.
13562 // Variables in another isolate should be not available.
13563 ExpectTrue("function f() {"
13564 " try {"
13565 " foo;"
13566 " return false;"
13567 " } catch(e) {"
13568 " return true;"
13569 " }"
13570 "};"
13571 "var bar = 371;"
13572 "f()");
13573
13574 v8::V8::SetFatalErrorHandler(StoringErrorCallback);
13575 last_location = last_message = NULL;
13576 isolate->Dispose();
13577 CHECK_EQ(last_location, NULL);
13578 CHECK_EQ(last_message, NULL);
13579
13580 // Check that default isolate still runs.
13581 ExpectTrue("function f() { return bar == 371; }; f()");
13582}
13583
13584TEST(DisposeIsolateWhenInUse) {
13585 v8::Isolate* isolate = v8::Isolate::New();
13586 CHECK(isolate);
13587 isolate->Enter();
13588 v8::HandleScope scope;
13589 LocalContext context;
13590 // Run something in this isolate.
13591 ExpectTrue("true");
13592 v8::V8::SetFatalErrorHandler(StoringErrorCallback);
13593 last_location = last_message = NULL;
13594 // Still entered, should fail.
13595 isolate->Dispose();
13596 CHECK_NE(last_location, NULL);
13597 CHECK_NE(last_message, NULL);
13598}
13599
13600TEST(RunTwoIsolatesOnSingleThread) {
13601 // Run isolate 1.
13602 v8::Isolate* isolate1 = v8::Isolate::New();
13603 isolate1->Enter();
13604 v8::Persistent<v8::Context> context1 = v8::Context::New();
13605
13606 {
13607 v8::Context::Scope cscope(context1);
13608 v8::HandleScope scope;
13609 // Run something in new isolate.
13610 CompileRun("var foo = 'isolate 1';");
13611 ExpectString("function f() { return foo; }; f()", "isolate 1");
13612 }
13613
13614 // Run isolate 2.
13615 v8::Isolate* isolate2 = v8::Isolate::New();
13616 v8::Persistent<v8::Context> context2;
13617
13618 {
13619 v8::Isolate::Scope iscope(isolate2);
13620 context2 = v8::Context::New();
13621 v8::Context::Scope cscope(context2);
13622 v8::HandleScope scope;
13623
13624 // Run something in new isolate.
13625 CompileRun("var foo = 'isolate 2';");
13626 ExpectString("function f() { return foo; }; f()", "isolate 2");
13627 }
13628
13629 {
13630 v8::Context::Scope cscope(context1);
13631 v8::HandleScope scope;
13632 // Now again in isolate 1
13633 ExpectString("function f() { return foo; }; f()", "isolate 1");
13634 }
13635
13636 isolate1->Exit();
13637
13638 // Run some stuff in default isolate.
13639 v8::Persistent<v8::Context> context_default = v8::Context::New();
13640
13641 {
13642 v8::Context::Scope cscope(context_default);
13643 v8::HandleScope scope;
13644 // Variables in other isolates should be not available, verify there
13645 // is an exception.
13646 ExpectTrue("function f() {"
13647 " try {"
13648 " foo;"
13649 " return false;"
13650 " } catch(e) {"
13651 " return true;"
13652 " }"
13653 "};"
13654 "var isDefaultIsolate = true;"
13655 "f()");
13656 }
13657
13658 isolate1->Enter();
13659
13660 {
13661 v8::Isolate::Scope iscope(isolate2);
13662 v8::Context::Scope cscope(context2);
13663 v8::HandleScope scope;
13664 ExpectString("function f() { return foo; }; f()", "isolate 2");
13665 }
13666
13667 {
13668 v8::Context::Scope cscope(context1);
13669 v8::HandleScope scope;
13670 ExpectString("function f() { return foo; }; f()", "isolate 1");
13671 }
13672
13673 {
13674 v8::Isolate::Scope iscope(isolate2);
13675 context2.Dispose();
13676 }
13677
13678 context1.Dispose();
13679 isolate1->Exit();
13680
13681 v8::V8::SetFatalErrorHandler(StoringErrorCallback);
13682 last_location = last_message = NULL;
13683
13684 isolate1->Dispose();
13685 CHECK_EQ(last_location, NULL);
13686 CHECK_EQ(last_message, NULL);
13687
13688 isolate2->Dispose();
13689 CHECK_EQ(last_location, NULL);
13690 CHECK_EQ(last_message, NULL);
13691
13692 // Check that default isolate still runs.
13693 {
13694 v8::Context::Scope cscope(context_default);
13695 v8::HandleScope scope;
13696 ExpectTrue("function f() { return isDefaultIsolate; }; f()");
13697 }
13698}
13699
13700static int CalcFibonacci(v8::Isolate* isolate, int limit) {
13701 v8::Isolate::Scope isolate_scope(isolate);
13702 v8::HandleScope scope;
13703 LocalContext context;
13704 i::ScopedVector<char> code(1024);
13705 i::OS::SNPrintF(code, "function fib(n) {"
13706 " if (n <= 2) return 1;"
13707 " return fib(n-1) + fib(n-2);"
13708 "}"
13709 "fib(%d)", limit);
13710 Local<Value> value = CompileRun(code.start());
13711 CHECK(value->IsNumber());
13712 return static_cast<int>(value->NumberValue());
13713}
13714
13715class IsolateThread : public v8::internal::Thread {
13716 public:
13717 explicit IsolateThread(v8::Isolate* isolate, int fib_limit)
13718 : Thread(NULL, "IsolateThread"),
13719 isolate_(isolate),
13720 fib_limit_(fib_limit),
13721 result_(0) { }
13722
13723 void Run() {
13724 result_ = CalcFibonacci(isolate_, fib_limit_);
13725 }
13726
13727 int result() { return result_; }
13728
13729 private:
13730 v8::Isolate* isolate_;
13731 int fib_limit_;
13732 int result_;
13733};
13734
13735TEST(MultipleIsolatesOnIndividualThreads) {
13736 v8::Isolate* isolate1 = v8::Isolate::New();
13737 v8::Isolate* isolate2 = v8::Isolate::New();
13738
13739 IsolateThread thread1(isolate1, 21);
13740 IsolateThread thread2(isolate2, 12);
13741
13742 // Compute some fibonacci numbers on 3 threads in 3 isolates.
13743 thread1.Start();
13744 thread2.Start();
13745
13746 int result1 = CalcFibonacci(v8::Isolate::GetCurrent(), 21);
13747 int result2 = CalcFibonacci(v8::Isolate::GetCurrent(), 12);
13748
13749 thread1.Join();
13750 thread2.Join();
13751
13752 // Compare results. The actual fibonacci numbers for 12 and 21 are taken
13753 // (I'm lazy!) from http://en.wikipedia.org/wiki/Fibonacci_number
13754 CHECK_EQ(result1, 10946);
13755 CHECK_EQ(result2, 144);
13756 CHECK_EQ(result1, thread1.result());
13757 CHECK_EQ(result2, thread2.result());
13758
13759 isolate1->Dispose();
13760 isolate2->Dispose();
13761}
13762
Ben Murdoch257744e2011-11-30 15:57:28 +000013763TEST(IsolateDifferentContexts) {
13764 v8::Isolate* isolate = v8::Isolate::New();
13765 Persistent<v8::Context> context;
13766 {
13767 v8::Isolate::Scope isolate_scope(isolate);
13768 v8::HandleScope handle_scope;
13769 context = v8::Context::New();
13770 v8::Context::Scope context_scope(context);
13771 Local<Value> v = CompileRun("2");
13772 CHECK(v->IsNumber());
13773 CHECK_EQ(2, static_cast<int>(v->NumberValue()));
13774 }
13775 {
13776 v8::Isolate::Scope isolate_scope(isolate);
13777 v8::HandleScope handle_scope;
13778 context = v8::Context::New();
13779 v8::Context::Scope context_scope(context);
13780 Local<Value> v = CompileRun("22");
13781 CHECK(v->IsNumber());
13782 CHECK_EQ(22, static_cast<int>(v->NumberValue()));
13783 }
13784}
Steve Block44f0eee2011-05-26 01:26:41 +010013785
13786class InitDefaultIsolateThread : public v8::internal::Thread {
13787 public:
13788 enum TestCase {
13789 IgnoreOOM,
13790 SetResourceConstraints,
13791 SetFatalHandler,
13792 SetCounterFunction,
13793 SetCreateHistogramFunction,
13794 SetAddHistogramSampleFunction
13795 };
13796
13797 explicit InitDefaultIsolateThread(TestCase testCase)
13798 : Thread(NULL, "InitDefaultIsolateThread"),
13799 testCase_(testCase),
13800 result_(false) { }
13801
13802 void Run() {
13803 switch (testCase_) {
13804 case IgnoreOOM:
13805 v8::V8::IgnoreOutOfMemoryException();
13806 break;
13807
13808 case SetResourceConstraints: {
13809 static const int K = 1024;
13810 v8::ResourceConstraints constraints;
13811 constraints.set_max_young_space_size(256 * K);
13812 constraints.set_max_old_space_size(4 * K * K);
13813 v8::SetResourceConstraints(&constraints);
13814 break;
13815 }
13816
13817 case SetFatalHandler:
13818 v8::V8::SetFatalErrorHandler(NULL);
13819 break;
13820
13821 case SetCounterFunction:
13822 v8::V8::SetCounterFunction(NULL);
13823 break;
13824
13825 case SetCreateHistogramFunction:
13826 v8::V8::SetCreateHistogramFunction(NULL);
13827 break;
13828
13829 case SetAddHistogramSampleFunction:
13830 v8::V8::SetAddHistogramSampleFunction(NULL);
13831 break;
13832 }
13833 result_ = true;
13834 }
13835
13836 bool result() { return result_; }
13837
13838 private:
13839 TestCase testCase_;
13840 bool result_;
13841};
13842
13843
13844static void InitializeTestHelper(InitDefaultIsolateThread::TestCase testCase) {
13845 InitDefaultIsolateThread thread(testCase);
13846 thread.Start();
13847 thread.Join();
13848 CHECK_EQ(thread.result(), true);
13849}
13850
13851TEST(InitializeDefaultIsolateOnSecondaryThread1) {
13852 InitializeTestHelper(InitDefaultIsolateThread::IgnoreOOM);
13853}
13854
13855TEST(InitializeDefaultIsolateOnSecondaryThread2) {
13856 InitializeTestHelper(InitDefaultIsolateThread::SetResourceConstraints);
13857}
13858
13859TEST(InitializeDefaultIsolateOnSecondaryThread3) {
13860 InitializeTestHelper(InitDefaultIsolateThread::SetFatalHandler);
13861}
13862
13863TEST(InitializeDefaultIsolateOnSecondaryThread4) {
13864 InitializeTestHelper(InitDefaultIsolateThread::SetCounterFunction);
13865}
13866
13867TEST(InitializeDefaultIsolateOnSecondaryThread5) {
13868 InitializeTestHelper(InitDefaultIsolateThread::SetCreateHistogramFunction);
13869}
13870
13871TEST(InitializeDefaultIsolateOnSecondaryThread6) {
13872 InitializeTestHelper(InitDefaultIsolateThread::SetAddHistogramSampleFunction);
13873}
13874
Kristian Monsen0d5e1162010-09-30 15:31:59 +010013875
13876TEST(StringCheckMultipleContexts) {
13877 const char* code =
13878 "(function() { return \"a\".charAt(0); })()";
13879
13880 {
13881 // Run the code twice in the first context to initialize the call IC.
13882 v8::HandleScope scope;
13883 LocalContext context1;
13884 ExpectString(code, "a");
13885 ExpectString(code, "a");
13886 }
13887
13888 {
13889 // Change the String.prototype in the second context and check
13890 // that the right function gets called.
13891 v8::HandleScope scope;
13892 LocalContext context2;
13893 CompileRun("String.prototype.charAt = function() { return \"not a\"; }");
13894 ExpectString(code, "not a");
13895 }
13896}
13897
13898
13899TEST(NumberCheckMultipleContexts) {
13900 const char* code =
13901 "(function() { return (42).toString(); })()";
13902
13903 {
13904 // Run the code twice in the first context to initialize the call IC.
13905 v8::HandleScope scope;
13906 LocalContext context1;
13907 ExpectString(code, "42");
13908 ExpectString(code, "42");
13909 }
13910
13911 {
13912 // Change the Number.prototype in the second context and check
13913 // that the right function gets called.
13914 v8::HandleScope scope;
13915 LocalContext context2;
13916 CompileRun("Number.prototype.toString = function() { return \"not 42\"; }");
13917 ExpectString(code, "not 42");
13918 }
13919}
13920
13921
13922TEST(BooleanCheckMultipleContexts) {
13923 const char* code =
13924 "(function() { return true.toString(); })()";
13925
13926 {
13927 // Run the code twice in the first context to initialize the call IC.
13928 v8::HandleScope scope;
13929 LocalContext context1;
13930 ExpectString(code, "true");
13931 ExpectString(code, "true");
13932 }
13933
13934 {
13935 // Change the Boolean.prototype in the second context and check
13936 // that the right function gets called.
13937 v8::HandleScope scope;
13938 LocalContext context2;
13939 CompileRun("Boolean.prototype.toString = function() { return \"\"; }");
13940 ExpectString(code, "");
13941 }
13942}
Ben Murdochf87a2032010-10-22 12:50:53 +010013943
13944
13945TEST(DontDeleteCellLoadIC) {
13946 const char* function_code =
13947 "function readCell() { while (true) { return cell; } }";
13948
13949 {
13950 // Run the code twice in the first context to initialize the load
13951 // IC for a don't delete cell.
13952 v8::HandleScope scope;
13953 LocalContext context1;
13954 CompileRun("var cell = \"first\";");
13955 ExpectBoolean("delete cell", false);
13956 CompileRun(function_code);
13957 ExpectString("readCell()", "first");
13958 ExpectString("readCell()", "first");
13959 }
13960
13961 {
13962 // Use a deletable cell in the second context.
13963 v8::HandleScope scope;
13964 LocalContext context2;
13965 CompileRun("cell = \"second\";");
13966 CompileRun(function_code);
13967 ExpectString("readCell()", "second");
13968 ExpectBoolean("delete cell", true);
13969 ExpectString("(function() {"
13970 " try {"
13971 " return readCell();"
13972 " } catch(e) {"
13973 " return e.toString();"
13974 " }"
13975 "})()",
13976 "ReferenceError: cell is not defined");
13977 CompileRun("cell = \"new_second\";");
Steve Block44f0eee2011-05-26 01:26:41 +010013978 HEAP->CollectAllGarbage(true);
Ben Murdochf87a2032010-10-22 12:50:53 +010013979 ExpectString("readCell()", "new_second");
13980 ExpectString("readCell()", "new_second");
13981 }
13982}
13983
13984
13985TEST(DontDeleteCellLoadICForceDelete) {
13986 const char* function_code =
13987 "function readCell() { while (true) { return cell; } }";
13988
13989 // Run the code twice to initialize the load IC for a don't delete
13990 // cell.
13991 v8::HandleScope scope;
13992 LocalContext context;
13993 CompileRun("var cell = \"value\";");
13994 ExpectBoolean("delete cell", false);
13995 CompileRun(function_code);
13996 ExpectString("readCell()", "value");
13997 ExpectString("readCell()", "value");
13998
13999 // Delete the cell using the API and check the inlined code works
14000 // correctly.
14001 CHECK(context->Global()->ForceDelete(v8_str("cell")));
14002 ExpectString("(function() {"
14003 " try {"
14004 " return readCell();"
14005 " } catch(e) {"
14006 " return e.toString();"
14007 " }"
14008 "})()",
14009 "ReferenceError: cell is not defined");
14010}
14011
14012
14013TEST(DontDeleteCellLoadICAPI) {
14014 const char* function_code =
14015 "function readCell() { while (true) { return cell; } }";
14016
14017 // Run the code twice to initialize the load IC for a don't delete
14018 // cell created using the API.
14019 v8::HandleScope scope;
14020 LocalContext context;
14021 context->Global()->Set(v8_str("cell"), v8_str("value"), v8::DontDelete);
14022 ExpectBoolean("delete cell", false);
14023 CompileRun(function_code);
14024 ExpectString("readCell()", "value");
14025 ExpectString("readCell()", "value");
14026
14027 // Delete the cell using the API and check the inlined code works
14028 // correctly.
14029 CHECK(context->Global()->ForceDelete(v8_str("cell")));
14030 ExpectString("(function() {"
14031 " try {"
14032 " return readCell();"
14033 " } catch(e) {"
14034 " return e.toString();"
14035 " }"
14036 "})()",
14037 "ReferenceError: cell is not defined");
14038}
14039
14040
14041TEST(GlobalLoadICGC) {
14042 const char* function_code =
14043 "function readCell() { while (true) { return cell; } }";
14044
14045 // Check inline load code for a don't delete cell is cleared during
14046 // GC.
14047 {
14048 v8::HandleScope scope;
14049 LocalContext context;
14050 CompileRun("var cell = \"value\";");
14051 ExpectBoolean("delete cell", false);
14052 CompileRun(function_code);
14053 ExpectString("readCell()", "value");
14054 ExpectString("readCell()", "value");
14055 }
14056 {
14057 v8::HandleScope scope;
14058 LocalContext context2;
14059 // Hold the code object in the second context.
14060 CompileRun(function_code);
14061 CheckSurvivingGlobalObjectsCount(1);
14062 }
14063
14064 // Check inline load code for a deletable cell is cleared during GC.
14065 {
14066 v8::HandleScope scope;
14067 LocalContext context;
14068 CompileRun("cell = \"value\";");
14069 CompileRun(function_code);
14070 ExpectString("readCell()", "value");
14071 ExpectString("readCell()", "value");
14072 }
14073 {
14074 v8::HandleScope scope;
14075 LocalContext context2;
14076 // Hold the code object in the second context.
14077 CompileRun(function_code);
14078 CheckSurvivingGlobalObjectsCount(1);
14079 }
14080}
14081
14082
14083TEST(RegExp) {
14084 v8::HandleScope scope;
14085 LocalContext context;
14086
14087 v8::Handle<v8::RegExp> re = v8::RegExp::New(v8_str("foo"), v8::RegExp::kNone);
14088 CHECK(re->IsRegExp());
14089 CHECK(re->GetSource()->Equals(v8_str("foo")));
14090 CHECK_EQ(re->GetFlags(), v8::RegExp::kNone);
14091
14092 re = v8::RegExp::New(v8_str("bar"),
14093 static_cast<v8::RegExp::Flags>(v8::RegExp::kIgnoreCase |
14094 v8::RegExp::kGlobal));
14095 CHECK(re->IsRegExp());
14096 CHECK(re->GetSource()->Equals(v8_str("bar")));
14097 CHECK_EQ(static_cast<int>(re->GetFlags()),
14098 v8::RegExp::kIgnoreCase | v8::RegExp::kGlobal);
14099
14100 re = v8::RegExp::New(v8_str("baz"),
14101 static_cast<v8::RegExp::Flags>(v8::RegExp::kIgnoreCase |
14102 v8::RegExp::kMultiline));
14103 CHECK(re->IsRegExp());
14104 CHECK(re->GetSource()->Equals(v8_str("baz")));
14105 CHECK_EQ(static_cast<int>(re->GetFlags()),
14106 v8::RegExp::kIgnoreCase | v8::RegExp::kMultiline);
14107
14108 re = CompileRun("/quux/").As<v8::RegExp>();
14109 CHECK(re->IsRegExp());
14110 CHECK(re->GetSource()->Equals(v8_str("quux")));
14111 CHECK_EQ(re->GetFlags(), v8::RegExp::kNone);
14112
14113 re = CompileRun("/quux/gm").As<v8::RegExp>();
14114 CHECK(re->IsRegExp());
14115 CHECK(re->GetSource()->Equals(v8_str("quux")));
14116 CHECK_EQ(static_cast<int>(re->GetFlags()),
14117 v8::RegExp::kGlobal | v8::RegExp::kMultiline);
14118
14119 // Override the RegExp constructor and check the API constructor
14120 // still works.
14121 CompileRun("RegExp = function() {}");
14122
14123 re = v8::RegExp::New(v8_str("foobar"), v8::RegExp::kNone);
14124 CHECK(re->IsRegExp());
14125 CHECK(re->GetSource()->Equals(v8_str("foobar")));
14126 CHECK_EQ(re->GetFlags(), v8::RegExp::kNone);
14127
14128 re = v8::RegExp::New(v8_str("foobarbaz"),
14129 static_cast<v8::RegExp::Flags>(v8::RegExp::kIgnoreCase |
14130 v8::RegExp::kMultiline));
14131 CHECK(re->IsRegExp());
14132 CHECK(re->GetSource()->Equals(v8_str("foobarbaz")));
14133 CHECK_EQ(static_cast<int>(re->GetFlags()),
14134 v8::RegExp::kIgnoreCase | v8::RegExp::kMultiline);
14135
14136 context->Global()->Set(v8_str("re"), re);
14137 ExpectTrue("re.test('FoobarbaZ')");
14138
Ben Murdoch257744e2011-11-30 15:57:28 +000014139 // RegExps are objects on which you can set properties.
14140 re->Set(v8_str("property"), v8::Integer::New(32));
14141 v8::Handle<v8::Value> value = CompileRun("re.property");
14142 ASSERT_EQ(32, value->Int32Value());
14143
Ben Murdochf87a2032010-10-22 12:50:53 +010014144 v8::TryCatch try_catch;
14145 re = v8::RegExp::New(v8_str("foo["), v8::RegExp::kNone);
14146 CHECK(re.IsEmpty());
14147 CHECK(try_catch.HasCaught());
14148 context->Global()->Set(v8_str("ex"), try_catch.Exception());
14149 ExpectTrue("ex instanceof SyntaxError");
14150}
14151
14152
Steve Block1e0659c2011-05-24 12:43:12 +010014153THREADED_TEST(Equals) {
14154 v8::HandleScope handleScope;
14155 LocalContext localContext;
14156
14157 v8::Handle<v8::Object> globalProxy = localContext->Global();
14158 v8::Handle<Value> global = globalProxy->GetPrototype();
14159
14160 CHECK(global->StrictEquals(global));
14161 CHECK(!global->StrictEquals(globalProxy));
14162 CHECK(!globalProxy->StrictEquals(global));
14163 CHECK(globalProxy->StrictEquals(globalProxy));
14164
14165 CHECK(global->Equals(global));
14166 CHECK(!global->Equals(globalProxy));
14167 CHECK(!globalProxy->Equals(global));
14168 CHECK(globalProxy->Equals(globalProxy));
14169}
14170
14171
Ben Murdochf87a2032010-10-22 12:50:53 +010014172static v8::Handle<v8::Value> Getter(v8::Local<v8::String> property,
14173 const v8::AccessorInfo& info ) {
14174 return v8_str("42!");
14175}
14176
14177
14178static v8::Handle<v8::Array> Enumerator(const v8::AccessorInfo& info) {
14179 v8::Handle<v8::Array> result = v8::Array::New();
14180 result->Set(0, v8_str("universalAnswer"));
14181 return result;
14182}
14183
14184
14185TEST(NamedEnumeratorAndForIn) {
14186 v8::HandleScope handle_scope;
14187 LocalContext context;
14188 v8::Context::Scope context_scope(context.local());
14189
14190 v8::Handle<v8::ObjectTemplate> tmpl = v8::ObjectTemplate::New();
14191 tmpl->SetNamedPropertyHandler(Getter, NULL, NULL, NULL, Enumerator);
14192 context->Global()->Set(v8_str("o"), tmpl->NewInstance());
14193 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun(
14194 "var result = []; for (var k in o) result.push(k); result"));
14195 CHECK_EQ(1, result->Length());
14196 CHECK_EQ(v8_str("universalAnswer"), result->Get(0));
14197}
Steve Block1e0659c2011-05-24 12:43:12 +010014198
14199
14200TEST(DefinePropertyPostDetach) {
14201 v8::HandleScope scope;
14202 LocalContext context;
14203 v8::Handle<v8::Object> proxy = context->Global();
14204 v8::Handle<v8::Function> define_property =
14205 CompileRun("(function() {"
14206 " Object.defineProperty("
14207 " this,"
14208 " 1,"
14209 " { configurable: true, enumerable: true, value: 3 });"
14210 "})").As<Function>();
14211 context->DetachGlobal();
14212 define_property->Call(proxy, 0, NULL);
14213}
Ben Murdoch8b112d22011-06-08 16:22:53 +010014214
14215
14216static void InstallContextId(v8::Handle<Context> context, int id) {
14217 Context::Scope scope(context);
14218 CompileRun("Object.prototype").As<Object>()->
14219 Set(v8_str("context_id"), v8::Integer::New(id));
14220}
14221
14222
14223static void CheckContextId(v8::Handle<Object> object, int expected) {
14224 CHECK_EQ(expected, object->Get(v8_str("context_id"))->Int32Value());
14225}
14226
14227
14228THREADED_TEST(CreationContext) {
14229 HandleScope handle_scope;
14230 Persistent<Context> context1 = Context::New();
14231 InstallContextId(context1, 1);
14232 Persistent<Context> context2 = Context::New();
14233 InstallContextId(context2, 2);
14234 Persistent<Context> context3 = Context::New();
14235 InstallContextId(context3, 3);
14236
14237 Local<v8::FunctionTemplate> tmpl = v8::FunctionTemplate::New();
14238
14239 Local<Object> object1;
14240 Local<Function> func1;
14241 {
14242 Context::Scope scope(context1);
14243 object1 = Object::New();
14244 func1 = tmpl->GetFunction();
14245 }
14246
14247 Local<Object> object2;
14248 Local<Function> func2;
14249 {
14250 Context::Scope scope(context2);
14251 object2 = Object::New();
14252 func2 = tmpl->GetFunction();
14253 }
14254
14255 Local<Object> instance1;
14256 Local<Object> instance2;
14257
14258 {
14259 Context::Scope scope(context3);
14260 instance1 = func1->NewInstance();
14261 instance2 = func2->NewInstance();
14262 }
14263
14264 CHECK(object1->CreationContext() == context1);
14265 CheckContextId(object1, 1);
14266 CHECK(func1->CreationContext() == context1);
14267 CheckContextId(func1, 1);
14268 CHECK(instance1->CreationContext() == context1);
14269 CheckContextId(instance1, 1);
14270 CHECK(object2->CreationContext() == context2);
14271 CheckContextId(object2, 2);
14272 CHECK(func2->CreationContext() == context2);
14273 CheckContextId(func2, 2);
14274 CHECK(instance2->CreationContext() == context2);
14275 CheckContextId(instance2, 2);
14276
14277 {
14278 Context::Scope scope(context1);
14279 CHECK(object1->CreationContext() == context1);
14280 CheckContextId(object1, 1);
14281 CHECK(func1->CreationContext() == context1);
14282 CheckContextId(func1, 1);
14283 CHECK(instance1->CreationContext() == context1);
14284 CheckContextId(instance1, 1);
14285 CHECK(object2->CreationContext() == context2);
14286 CheckContextId(object2, 2);
14287 CHECK(func2->CreationContext() == context2);
14288 CheckContextId(func2, 2);
14289 CHECK(instance2->CreationContext() == context2);
14290 CheckContextId(instance2, 2);
14291 }
14292
14293 {
14294 Context::Scope scope(context2);
14295 CHECK(object1->CreationContext() == context1);
14296 CheckContextId(object1, 1);
14297 CHECK(func1->CreationContext() == context1);
14298 CheckContextId(func1, 1);
14299 CHECK(instance1->CreationContext() == context1);
14300 CheckContextId(instance1, 1);
14301 CHECK(object2->CreationContext() == context2);
14302 CheckContextId(object2, 2);
14303 CHECK(func2->CreationContext() == context2);
14304 CheckContextId(func2, 2);
14305 CHECK(instance2->CreationContext() == context2);
14306 CheckContextId(instance2, 2);
14307 }
14308
14309 context1.Dispose();
14310 context2.Dispose();
14311 context3.Dispose();
14312}
Ben Murdoch257744e2011-11-30 15:57:28 +000014313
14314
14315Handle<Value> HasOwnPropertyIndexedPropertyGetter(uint32_t index,
14316 const AccessorInfo& info) {
14317 if (index == 42) return v8_str("yes");
14318 return Handle<v8::Integer>();
14319}
14320
14321
14322Handle<Value> HasOwnPropertyNamedPropertyGetter(Local<String> property,
14323 const AccessorInfo& info) {
14324 if (property->Equals(v8_str("foo"))) return v8_str("yes");
14325 return Handle<Value>();
14326}
14327
14328
14329Handle<v8::Integer> HasOwnPropertyIndexedPropertyQuery(
14330 uint32_t index, const AccessorInfo& info) {
14331 if (index == 42) return v8_num(1).As<v8::Integer>();
14332 return Handle<v8::Integer>();
14333}
14334
14335
14336Handle<v8::Integer> HasOwnPropertyNamedPropertyQuery(
14337 Local<String> property, const AccessorInfo& info) {
14338 if (property->Equals(v8_str("foo"))) return v8_num(1).As<v8::Integer>();
14339 return Handle<v8::Integer>();
14340}
14341
14342
14343Handle<v8::Integer> HasOwnPropertyNamedPropertyQuery2(
14344 Local<String> property, const AccessorInfo& info) {
14345 if (property->Equals(v8_str("bar"))) return v8_num(1).As<v8::Integer>();
14346 return Handle<v8::Integer>();
14347}
14348
14349
14350Handle<Value> HasOwnPropertyAccessorGetter(Local<String> property,
14351 const AccessorInfo& info) {
14352 return v8_str("yes");
14353}
14354
14355
14356TEST(HasOwnProperty) {
14357 v8::HandleScope scope;
14358 LocalContext env;
14359 { // Check normal properties and defined getters.
14360 Handle<Value> value = CompileRun(
14361 "function Foo() {"
14362 " this.foo = 11;"
14363 " this.__defineGetter__('baz', function() { return 1; });"
14364 "};"
14365 "function Bar() { "
14366 " this.bar = 13;"
14367 " this.__defineGetter__('bla', function() { return 2; });"
14368 "};"
14369 "Bar.prototype = new Foo();"
14370 "new Bar();");
14371 CHECK(value->IsObject());
14372 Handle<Object> object = value->ToObject();
14373 CHECK(object->Has(v8_str("foo")));
14374 CHECK(!object->HasOwnProperty(v8_str("foo")));
14375 CHECK(object->HasOwnProperty(v8_str("bar")));
14376 CHECK(object->Has(v8_str("baz")));
14377 CHECK(!object->HasOwnProperty(v8_str("baz")));
14378 CHECK(object->HasOwnProperty(v8_str("bla")));
14379 }
14380 { // Check named getter interceptors.
14381 Handle<ObjectTemplate> templ = ObjectTemplate::New();
14382 templ->SetNamedPropertyHandler(HasOwnPropertyNamedPropertyGetter);
14383 Handle<Object> instance = templ->NewInstance();
14384 CHECK(!instance->HasOwnProperty(v8_str("42")));
14385 CHECK(instance->HasOwnProperty(v8_str("foo")));
14386 CHECK(!instance->HasOwnProperty(v8_str("bar")));
14387 }
14388 { // Check indexed getter interceptors.
14389 Handle<ObjectTemplate> templ = ObjectTemplate::New();
14390 templ->SetIndexedPropertyHandler(HasOwnPropertyIndexedPropertyGetter);
14391 Handle<Object> instance = templ->NewInstance();
14392 CHECK(instance->HasOwnProperty(v8_str("42")));
14393 CHECK(!instance->HasOwnProperty(v8_str("43")));
14394 CHECK(!instance->HasOwnProperty(v8_str("foo")));
14395 }
14396 { // Check named query interceptors.
14397 Handle<ObjectTemplate> templ = ObjectTemplate::New();
14398 templ->SetNamedPropertyHandler(0, 0, HasOwnPropertyNamedPropertyQuery);
14399 Handle<Object> instance = templ->NewInstance();
14400 CHECK(instance->HasOwnProperty(v8_str("foo")));
14401 CHECK(!instance->HasOwnProperty(v8_str("bar")));
14402 }
14403 { // Check indexed query interceptors.
14404 Handle<ObjectTemplate> templ = ObjectTemplate::New();
14405 templ->SetIndexedPropertyHandler(0, 0, HasOwnPropertyIndexedPropertyQuery);
14406 Handle<Object> instance = templ->NewInstance();
14407 CHECK(instance->HasOwnProperty(v8_str("42")));
14408 CHECK(!instance->HasOwnProperty(v8_str("41")));
14409 }
14410 { // Check callbacks.
14411 Handle<ObjectTemplate> templ = ObjectTemplate::New();
14412 templ->SetAccessor(v8_str("foo"), HasOwnPropertyAccessorGetter);
14413 Handle<Object> instance = templ->NewInstance();
14414 CHECK(instance->HasOwnProperty(v8_str("foo")));
14415 CHECK(!instance->HasOwnProperty(v8_str("bar")));
14416 }
14417 { // Check that query wins on disagreement.
14418 Handle<ObjectTemplate> templ = ObjectTemplate::New();
14419 templ->SetNamedPropertyHandler(HasOwnPropertyNamedPropertyGetter,
14420 0,
14421 HasOwnPropertyNamedPropertyQuery2);
14422 Handle<Object> instance = templ->NewInstance();
14423 CHECK(!instance->HasOwnProperty(v8_str("foo")));
14424 CHECK(instance->HasOwnProperty(v8_str("bar")));
14425 }
14426}
14427
14428
14429void CheckCodeGenerationAllowed() {
14430 Handle<Value> result = CompileRun("eval('42')");
14431 CHECK_EQ(42, result->Int32Value());
14432 result = CompileRun("(function(e) { return e('42'); })(eval)");
14433 CHECK_EQ(42, result->Int32Value());
14434 result = CompileRun("var f = new Function('return 42'); f()");
14435 CHECK_EQ(42, result->Int32Value());
14436}
14437
14438
14439void CheckCodeGenerationDisallowed() {
14440 TryCatch try_catch;
14441
14442 Handle<Value> result = CompileRun("eval('42')");
14443 CHECK(result.IsEmpty());
14444 CHECK(try_catch.HasCaught());
14445 try_catch.Reset();
14446
14447 result = CompileRun("(function(e) { return e('42'); })(eval)");
14448 CHECK(result.IsEmpty());
14449 CHECK(try_catch.HasCaught());
14450 try_catch.Reset();
14451
14452 result = CompileRun("var f = new Function('return 42'); f()");
14453 CHECK(result.IsEmpty());
14454 CHECK(try_catch.HasCaught());
14455}
14456
14457
14458bool CodeGenerationAllowed(Local<Context> context) {
14459 ApiTestFuzzer::Fuzz();
14460 return true;
14461}
14462
14463
14464bool CodeGenerationDisallowed(Local<Context> context) {
14465 ApiTestFuzzer::Fuzz();
14466 return false;
14467}
14468
14469
14470THREADED_TEST(AllowCodeGenFromStrings) {
14471 v8::HandleScope scope;
14472 LocalContext context;
14473
14474 // eval and the Function constructor allowed by default.
14475 CheckCodeGenerationAllowed();
14476
14477 // Disallow eval and the Function constructor.
14478 context->AllowCodeGenerationFromStrings(false);
14479 CheckCodeGenerationDisallowed();
14480
14481 // Allow again.
14482 context->AllowCodeGenerationFromStrings(true);
14483 CheckCodeGenerationAllowed();
14484
14485 // Disallow but setting a global callback that will allow the calls.
14486 context->AllowCodeGenerationFromStrings(false);
14487 V8::SetAllowCodeGenerationFromStringsCallback(&CodeGenerationAllowed);
14488 CheckCodeGenerationAllowed();
14489
14490 // Set a callback that disallows the code generation.
14491 V8::SetAllowCodeGenerationFromStringsCallback(&CodeGenerationDisallowed);
14492 CheckCodeGenerationDisallowed();
14493}
14494
14495
14496static v8::Handle<Value> NonObjectThis(const v8::Arguments& args) {
14497 return v8::Undefined();
14498}
14499
14500
14501THREADED_TEST(CallAPIFunctionOnNonObject) {
14502 v8::HandleScope scope;
14503 LocalContext context;
14504 Handle<FunctionTemplate> templ = v8::FunctionTemplate::New(NonObjectThis);
14505 Handle<Function> function = templ->GetFunction();
14506 context->Global()->Set(v8_str("f"), function);
14507 TryCatch try_catch;
14508 CompileRun("f.call(2)");
14509}