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