1 // Copyright 2012 the V8 project authors. All rights reserved. 2 // Redistribution and use in source and binary forms, with or without 3 // modification, are permitted provided that the following conditions are 4 // met: 5 // 6 // * Redistributions of source code must retain the above copyright 7 // notice, this list of conditions and the following disclaimer. 8 // * Redistributions in binary form must reproduce the above 9 // copyright notice, this list of conditions and the following 10 // disclaimer in the documentation and/or other materials provided 11 // with the distribution. 12 // * Neither the name of Google Inc. nor the names of its 13 // contributors may be used to endorse or promote products derived 14 // from this software without specific prior written permission. 15 // 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28 #include <stdlib.h> 29 30 #include "v8.h" 31 32 #include "api.h" 33 #include "cctest.h" 34 #include "frames-inl.h" 35 #include "string-stream.h" 36 37 using ::v8::ObjectTemplate; 38 using ::v8::Value; 39 using ::v8::Context; 40 using ::v8::Local; 41 using ::v8::String; 42 using ::v8::Script; 43 using ::v8::Function; 44 using ::v8::AccessorInfo; 45 using ::v8::Extension; 46 47 static void handle_property(Local<String> name, 48 const v8::PropertyCallbackInfo<v8::Value>& info) { 49 ApiTestFuzzer::Fuzz(); 50 info.GetReturnValue().Set(v8_num(900)); 51 } 52 53 54 THREADED_TEST(PropertyHandler) { 55 LocalContext env; 56 v8::HandleScope scope(env->GetIsolate()); 57 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 58 fun_templ->InstanceTemplate()->SetAccessor(v8_str("foo"), handle_property); 59 Local<Function> fun = fun_templ->GetFunction(); 60 env->Global()->Set(v8_str("Fun"), fun); 61 Local<Script> getter = v8_compile("var obj = new Fun(); obj.foo;"); 62 CHECK_EQ(900, getter->Run()->Int32Value()); 63 Local<Script> setter = v8_compile("obj.foo = 901;"); 64 CHECK_EQ(901, setter->Run()->Int32Value()); 65 } 66 67 68 static void GetIntValue(Local<String> property, 69 const v8::PropertyCallbackInfo<v8::Value>& info) { 70 ApiTestFuzzer::Fuzz(); 71 int* value = 72 static_cast<int*>(v8::Handle<v8::External>::Cast(info.Data())->Value()); 73 info.GetReturnValue().Set(v8_num(*value)); 74 } 75 76 77 static void SetIntValue(Local<String> property, 78 Local<Value> value, 79 const v8::PropertyCallbackInfo<void>& info) { 80 int* field = 81 static_cast<int*>(v8::Handle<v8::External>::Cast(info.Data())->Value()); 82 *field = value->Int32Value(); 83 } 84 85 int foo, bar, baz; 86 87 THREADED_TEST(GlobalVariableAccess) { 88 foo = 0; 89 bar = -4; 90 baz = 10; 91 v8::HandleScope scope(v8::Isolate::GetCurrent()); 92 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 93 templ->InstanceTemplate()->SetAccessor(v8_str("foo"), 94 GetIntValue, 95 SetIntValue, 96 v8::External::New(&foo)); 97 templ->InstanceTemplate()->SetAccessor(v8_str("bar"), 98 GetIntValue, 99 SetIntValue, 100 v8::External::New(&bar)); 101 templ->InstanceTemplate()->SetAccessor(v8_str("baz"), 102 GetIntValue, 103 SetIntValue, 104 v8::External::New(&baz)); 105 LocalContext env(0, templ->InstanceTemplate()); 106 v8_compile("foo = (++bar) + baz")->Run(); 107 CHECK_EQ(bar, -3); 108 CHECK_EQ(foo, 7); 109 } 110 111 112 static int x_register = 0; 113 static v8::Handle<v8::Object> x_receiver; 114 static v8::Handle<v8::Object> x_holder; 115 116 117 static void XGetter(Local<String> name, 118 const v8::PropertyCallbackInfo<v8::Value>& info) { 119 ApiTestFuzzer::Fuzz(); 120 v8::Isolate* isolate = v8::Isolate::GetCurrent(); 121 CHECK_EQ(isolate, info.GetIsolate()); 122 CHECK_EQ(x_receiver, info.This()); 123 CHECK_EQ(x_holder, info.Holder()); 124 info.GetReturnValue().Set(v8_num(x_register)); 125 } 126 127 128 static void XSetter(Local<String> name, 129 Local<Value> value, 130 const v8::PropertyCallbackInfo<void>& info) { 131 v8::Isolate* isolate = v8::Isolate::GetCurrent(); 132 CHECK_EQ(isolate, info.GetIsolate()); 133 CHECK_EQ(x_holder, info.This()); 134 CHECK_EQ(x_holder, info.Holder()); 135 x_register = value->Int32Value(); 136 } 137 138 139 THREADED_TEST(AccessorIC) { 140 LocalContext context; 141 v8::HandleScope scope(context->GetIsolate()); 142 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 143 obj->SetAccessor(v8_str("x"), XGetter, XSetter); 144 x_holder = obj->NewInstance(); 145 context->Global()->Set(v8_str("holder"), x_holder); 146 x_receiver = v8::Object::New(); 147 context->Global()->Set(v8_str("obj"), x_receiver); 148 v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(CompileRun( 149 "obj.__proto__ = holder;" 150 "var result = [];" 151 "for (var i = 0; i < 10; i++) {" 152 " holder.x = i;" 153 " result.push(obj.x);" 154 "}" 155 "result")); 156 CHECK_EQ(10, array->Length()); 157 for (int i = 0; i < 10; i++) { 158 v8::Handle<Value> entry = array->Get(v8::Integer::New(i)); 159 CHECK_EQ(v8::Integer::New(i), entry); 160 } 161 } 162 163 164 static void AccessorProhibitsOverwritingGetter( 165 Local<String> name, 166 const v8::PropertyCallbackInfo<v8::Value>& info) { 167 ApiTestFuzzer::Fuzz(); 168 info.GetReturnValue().Set(true); 169 } 170 171 172 THREADED_TEST(AccessorProhibitsOverwriting) { 173 LocalContext context; 174 v8::HandleScope scope(context->GetIsolate()); 175 Local<ObjectTemplate> templ = ObjectTemplate::New(); 176 templ->SetAccessor(v8_str("x"), 177 AccessorProhibitsOverwritingGetter, 178 0, 179 v8::Handle<Value>(), 180 v8::PROHIBITS_OVERWRITING, 181 v8::ReadOnly); 182 Local<v8::Object> instance = templ->NewInstance(); 183 context->Global()->Set(v8_str("obj"), instance); 184 Local<Value> value = CompileRun( 185 "obj.__defineGetter__('x', function() { return false; });" 186 "obj.x"); 187 CHECK(value->BooleanValue()); 188 value = CompileRun( 189 "var setter_called = false;" 190 "obj.__defineSetter__('x', function() { setter_called = true; });" 191 "obj.x = 42;" 192 "setter_called"); 193 CHECK(!value->BooleanValue()); 194 value = CompileRun( 195 "obj2 = {};" 196 "obj2.__proto__ = obj;" 197 "obj2.__defineGetter__('x', function() { return false; });" 198 "obj2.x"); 199 CHECK(value->BooleanValue()); 200 value = CompileRun( 201 "var setter_called = false;" 202 "obj2 = {};" 203 "obj2.__proto__ = obj;" 204 "obj2.__defineSetter__('x', function() { setter_called = true; });" 205 "obj2.x = 42;" 206 "setter_called"); 207 CHECK(!value->BooleanValue()); 208 } 209 210 211 template <int C> 212 static void HandleAllocatingGetter( 213 Local<String> name, 214 const v8::PropertyCallbackInfo<v8::Value>& info) { 215 ApiTestFuzzer::Fuzz(); 216 for (int i = 0; i < C; i++) 217 v8::String::New("foo"); 218 info.GetReturnValue().Set(v8::String::New("foo")); 219 } 220 221 222 THREADED_TEST(HandleScopePop) { 223 LocalContext context; 224 v8::HandleScope scope(context->GetIsolate()); 225 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 226 obj->SetAccessor(v8_str("one"), HandleAllocatingGetter<1>); 227 obj->SetAccessor(v8_str("many"), HandleAllocatingGetter<1024>); 228 v8::Handle<v8::Object> inst = obj->NewInstance(); 229 context->Global()->Set(v8::String::New("obj"), inst); 230 i::Isolate* isolate = i::Isolate::Current(); 231 int count_before = i::HandleScope::NumberOfHandles(isolate); 232 { 233 v8::HandleScope scope(context->GetIsolate()); 234 CompileRun( 235 "for (var i = 0; i < 1000; i++) {" 236 " obj.one;" 237 " obj.many;" 238 "}"); 239 } 240 int count_after = i::HandleScope::NumberOfHandles(isolate); 241 CHECK_EQ(count_before, count_after); 242 } 243 244 static void CheckAccessorArgsCorrect( 245 Local<String> name, 246 const v8::PropertyCallbackInfo<v8::Value>& info) { 247 CHECK(info.GetIsolate() == v8::Isolate::GetCurrent()); 248 CHECK(info.This() == info.Holder()); 249 CHECK(info.Data()->Equals(v8::String::New("data"))); 250 ApiTestFuzzer::Fuzz(); 251 CHECK(info.GetIsolate() == v8::Isolate::GetCurrent()); 252 CHECK(info.This() == info.Holder()); 253 CHECK(info.Data()->Equals(v8::String::New("data"))); 254 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 255 CHECK(info.GetIsolate() == v8::Isolate::GetCurrent()); 256 CHECK(info.This() == info.Holder()); 257 CHECK(info.Data()->Equals(v8::String::New("data"))); 258 info.GetReturnValue().Set(17); 259 } 260 261 262 THREADED_TEST(DirectCall) { 263 LocalContext context; 264 v8::HandleScope scope(context->GetIsolate()); 265 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 266 obj->SetAccessor(v8_str("xxx"), 267 CheckAccessorArgsCorrect, 268 NULL, 269 v8::String::New("data")); 270 v8::Handle<v8::Object> inst = obj->NewInstance(); 271 context->Global()->Set(v8::String::New("obj"), inst); 272 Local<Script> scr = v8::Script::Compile(v8::String::New("obj.xxx")); 273 for (int i = 0; i < 10; i++) { 274 Local<Value> result = scr->Run(); 275 CHECK(!result.IsEmpty()); 276 CHECK_EQ(17, result->Int32Value()); 277 } 278 } 279 280 static void EmptyGetter(Local<String> name, 281 const v8::PropertyCallbackInfo<v8::Value>& info) { 282 CheckAccessorArgsCorrect(name, info); 283 ApiTestFuzzer::Fuzz(); 284 CheckAccessorArgsCorrect(name, info); 285 info.GetReturnValue().Set(v8::Handle<v8::Value>()); 286 } 287 288 289 THREADED_TEST(EmptyResult) { 290 LocalContext context; 291 v8::HandleScope scope(context->GetIsolate()); 292 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 293 obj->SetAccessor(v8_str("xxx"), EmptyGetter, NULL, v8::String::New("data")); 294 v8::Handle<v8::Object> inst = obj->NewInstance(); 295 context->Global()->Set(v8::String::New("obj"), inst); 296 Local<Script> scr = v8::Script::Compile(v8::String::New("obj.xxx")); 297 for (int i = 0; i < 10; i++) { 298 Local<Value> result = scr->Run(); 299 CHECK(result == v8::Undefined()); 300 } 301 } 302 303 304 THREADED_TEST(NoReuseRegress) { 305 // Check that the IC generated for the one test doesn't get reused 306 // for the other. 307 v8::HandleScope scope(v8::Isolate::GetCurrent()); 308 { 309 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 310 obj->SetAccessor(v8_str("xxx"), EmptyGetter, NULL, v8::String::New("data")); 311 LocalContext context; 312 v8::Handle<v8::Object> inst = obj->NewInstance(); 313 context->Global()->Set(v8::String::New("obj"), inst); 314 Local<Script> scr = v8::Script::Compile(v8::String::New("obj.xxx")); 315 for (int i = 0; i < 2; i++) { 316 Local<Value> result = scr->Run(); 317 CHECK(result == v8::Undefined()); 318 } 319 } 320 { 321 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 322 obj->SetAccessor(v8_str("xxx"), 323 CheckAccessorArgsCorrect, 324 NULL, 325 v8::String::New("data")); 326 LocalContext context; 327 v8::Handle<v8::Object> inst = obj->NewInstance(); 328 context->Global()->Set(v8::String::New("obj"), inst); 329 Local<Script> scr = v8::Script::Compile(v8::String::New("obj.xxx")); 330 for (int i = 0; i < 10; i++) { 331 Local<Value> result = scr->Run(); 332 CHECK(!result.IsEmpty()); 333 CHECK_EQ(17, result->Int32Value()); 334 } 335 } 336 } 337 338 static void ThrowingGetAccessor( 339 Local<String> name, 340 const v8::PropertyCallbackInfo<v8::Value>& info) { 341 ApiTestFuzzer::Fuzz(); 342 v8::ThrowException(v8_str("g")); 343 } 344 345 346 static void ThrowingSetAccessor(Local<String> name, 347 Local<Value> value, 348 const v8::PropertyCallbackInfo<void>& info) { 349 v8::ThrowException(value); 350 } 351 352 353 THREADED_TEST(Regress1054726) { 354 LocalContext env; 355 v8::HandleScope scope(env->GetIsolate()); 356 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 357 obj->SetAccessor(v8_str("x"), 358 ThrowingGetAccessor, 359 ThrowingSetAccessor, 360 Local<Value>()); 361 362 env->Global()->Set(v8_str("obj"), obj->NewInstance()); 363 364 // Use the throwing property setter/getter in a loop to force 365 // the accessor ICs to be initialized. 366 v8::Handle<Value> result; 367 result = Script::Compile(v8_str( 368 "var result = '';" 369 "for (var i = 0; i < 5; i++) {" 370 " try { obj.x; } catch (e) { result += e; }" 371 "}; result"))->Run(); 372 CHECK_EQ(v8_str("ggggg"), result); 373 374 result = Script::Compile(String::New( 375 "var result = '';" 376 "for (var i = 0; i < 5; i++) {" 377 " try { obj.x = i; } catch (e) { result += e; }" 378 "}; result"))->Run(); 379 CHECK_EQ(v8_str("01234"), result); 380 } 381 382 383 static void AllocGetter(Local<String> name, 384 const v8::PropertyCallbackInfo<v8::Value>& info) { 385 ApiTestFuzzer::Fuzz(); 386 info.GetReturnValue().Set(v8::Array::New(1000)); 387 } 388 389 390 THREADED_TEST(Gc) { 391 LocalContext env; 392 v8::HandleScope scope(env->GetIsolate()); 393 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 394 obj->SetAccessor(v8_str("xxx"), AllocGetter); 395 env->Global()->Set(v8_str("obj"), obj->NewInstance()); 396 Script::Compile(String::New( 397 "var last = [];" 398 "for (var i = 0; i < 2048; i++) {" 399 " var result = obj.xxx;" 400 " result[0] = last;" 401 " last = result;" 402 "}"))->Run(); 403 } 404 405 406 static void StackCheck(Local<String> name, 407 const v8::PropertyCallbackInfo<v8::Value>& info) { 408 i::StackFrameIterator iter(reinterpret_cast<i::Isolate*>(info.GetIsolate())); 409 for (int i = 0; !iter.done(); i++) { 410 i::StackFrame* frame = iter.frame(); 411 CHECK(i != 0 || (frame->type() == i::StackFrame::EXIT)); 412 i::Code* code = frame->LookupCode(); 413 CHECK(code->IsCode()); 414 i::Address pc = frame->pc(); 415 CHECK(code->contains(pc)); 416 iter.Advance(); 417 } 418 } 419 420 421 THREADED_TEST(StackIteration) { 422 LocalContext env; 423 v8::HandleScope scope(env->GetIsolate()); 424 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 425 i::StringStream::ClearMentionedObjectCache(); 426 obj->SetAccessor(v8_str("xxx"), StackCheck); 427 env->Global()->Set(v8_str("obj"), obj->NewInstance()); 428 Script::Compile(String::New( 429 "function foo() {" 430 " return obj.xxx;" 431 "}" 432 "for (var i = 0; i < 100; i++) {" 433 " foo();" 434 "}"))->Run(); 435 } 436 437 438 static void AllocateHandles(Local<String> name, 439 const v8::PropertyCallbackInfo<v8::Value>& info) { 440 for (int i = 0; i < i::kHandleBlockSize + 1; i++) { 441 v8::Local<v8::Value>::New(name); 442 } 443 info.GetReturnValue().Set(v8::Integer::New(100)); 444 } 445 446 447 THREADED_TEST(HandleScopeSegment) { 448 // Check that we can return values past popping of handle scope 449 // segments. 450 LocalContext env; 451 v8::HandleScope scope(env->GetIsolate()); 452 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 453 obj->SetAccessor(v8_str("xxx"), AllocateHandles); 454 env->Global()->Set(v8_str("obj"), obj->NewInstance()); 455 v8::Handle<v8::Value> result = Script::Compile(String::New( 456 "var result;" 457 "for (var i = 0; i < 4; i++)" 458 " result = obj.xxx;" 459 "result;"))->Run(); 460 CHECK_EQ(100, result->Int32Value()); 461 } 462 463 464 void JSONStringifyEnumerator(const v8::PropertyCallbackInfo<v8::Array>& info) { 465 v8::Handle<v8::Array> array = v8::Array::New(1); 466 array->Set(0, v8_str("regress")); 467 info.GetReturnValue().Set(array); 468 } 469 470 471 void JSONStringifyGetter(Local<String> name, 472 const v8::PropertyCallbackInfo<v8::Value>& info) { 473 info.GetReturnValue().Set(v8_str("crbug-161028")); 474 } 475 476 477 THREADED_TEST(JSONStringifyNamedInterceptorObject) { 478 LocalContext env; 479 v8::HandleScope scope(env->GetIsolate()); 480 481 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 482 obj->SetNamedPropertyHandler( 483 JSONStringifyGetter, NULL, NULL, NULL, JSONStringifyEnumerator); 484 env->Global()->Set(v8_str("obj"), obj->NewInstance()); 485 v8::Handle<v8::String> expected = v8_str("{\"regress\":\"crbug-161028\"}"); 486 CHECK(CompileRun("JSON.stringify(obj)")->Equals(expected)); 487 } 488