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 37using ::v8::ObjectTemplate; 38using ::v8::Value; 39using ::v8::Context; 40using ::v8::Local; 41using ::v8::String; 42using ::v8::Script; 43using ::v8::Function; 44using ::v8::AccessorInfo; 45using ::v8::Extension; 46 47namespace i = ::v8::internal; 48 49static v8::Handle<Value> handle_property(Local<String> name, 50 const AccessorInfo&) { 51 ApiTestFuzzer::Fuzz(); 52 return v8_num(900); 53} 54 55 56THREADED_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 70static 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 79static 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 87int foo, bar, baz; 88 89THREADED_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 114static int x_register = 0; 115static v8::Handle<v8::Object> x_receiver; 116static v8::Handle<v8::Object> x_holder; 117 118 119static 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 127static 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 136THREADED_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 161static v8::Handle<Value> AccessorProhibitsOverwritingGetter( 162 Local<String> name, 163 const AccessorInfo& info) { 164 ApiTestFuzzer::Fuzz(); 165 return v8::True(); 166} 167 168 169THREADED_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 208template <int C> 209static 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 218THREADED_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 239static 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 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 252THREADED_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 270static 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 278THREADED_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 293THREADED_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 327static v8::Handle<Value> ThrowingGetAccessor(Local<String> name, 328 const AccessorInfo& info) { 329 ApiTestFuzzer::Fuzz(); 330 return v8::ThrowException(v8_str("g")); 331} 332 333 334static void ThrowingSetAccessor(Local<String> name, 335 Local<Value> value, 336 const AccessorInfo& info) { 337 v8::ThrowException(value); 338} 339 340 341THREADED_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 371static v8::Handle<Value> AllocGetter(Local<String> name, 372 const AccessorInfo& info) { 373 ApiTestFuzzer::Fuzz(); 374 return v8::Array::New(1000); 375} 376 377 378THREADED_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 394static 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 i::Code* code = frame->LookupCode(); 401 CHECK(code->IsCode()); 402 i::Address pc = frame->pc(); 403 CHECK(code->contains(pc)); 404 iter.Advance(); 405 } 406 return v8::Undefined(); 407} 408 409 410THREADED_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 427static 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 436THREADED_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