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 47static v8::Handle<Value> handle_property(Local<String> name, 48 const AccessorInfo&) { 49 ApiTestFuzzer::Fuzz(); 50 return v8_num(900); 51} 52 53 54THREADED_TEST(PropertyHandler) { 55 v8::HandleScope scope; 56 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 57 fun_templ->InstanceTemplate()->SetAccessor(v8_str("foo"), handle_property); 58 LocalContext env; 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 68static v8::Handle<Value> GetIntValue(Local<String> property, 69 const AccessorInfo& info) { 70 ApiTestFuzzer::Fuzz(); 71 int* value = 72 static_cast<int*>(v8::Handle<v8::External>::Cast(info.Data())->Value()); 73 return v8_num(*value); 74} 75 76 77static void SetIntValue(Local<String> property, 78 Local<Value> value, 79 const AccessorInfo& info) { 80 int* field = 81 static_cast<int*>(v8::Handle<v8::External>::Cast(info.Data())->Value()); 82 *field = value->Int32Value(); 83} 84 85int foo, bar, baz; 86 87THREADED_TEST(GlobalVariableAccess) { 88 foo = 0; 89 bar = -4; 90 baz = 10; 91 v8::HandleScope scope; 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 112static int x_register = 0; 113static v8::Handle<v8::Object> x_receiver; 114static v8::Handle<v8::Object> x_holder; 115 116 117static v8::Handle<Value> XGetter(Local<String> name, const AccessorInfo& info) { 118 ApiTestFuzzer::Fuzz(); 119 CHECK_EQ(x_receiver, info.This()); 120 CHECK_EQ(x_holder, info.Holder()); 121 return v8_num(x_register); 122} 123 124 125static void XSetter(Local<String> name, 126 Local<Value> value, 127 const AccessorInfo& info) { 128 CHECK_EQ(x_holder, info.This()); 129 CHECK_EQ(x_holder, info.Holder()); 130 x_register = value->Int32Value(); 131} 132 133 134THREADED_TEST(AccessorIC) { 135 v8::HandleScope scope; 136 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 137 obj->SetAccessor(v8_str("x"), XGetter, XSetter); 138 LocalContext context; 139 x_holder = obj->NewInstance(); 140 context->Global()->Set(v8_str("holder"), x_holder); 141 x_receiver = v8::Object::New(); 142 context->Global()->Set(v8_str("obj"), x_receiver); 143 v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(CompileRun( 144 "obj.__proto__ = holder;" 145 "var result = [];" 146 "for (var i = 0; i < 10; i++) {" 147 " holder.x = i;" 148 " result.push(obj.x);" 149 "}" 150 "result")); 151 CHECK_EQ(10, array->Length()); 152 for (int i = 0; i < 10; i++) { 153 v8::Handle<Value> entry = array->Get(v8::Integer::New(i)); 154 CHECK_EQ(v8::Integer::New(i), entry); 155 } 156} 157 158 159static v8::Handle<Value> AccessorProhibitsOverwritingGetter( 160 Local<String> name, 161 const AccessorInfo& info) { 162 ApiTestFuzzer::Fuzz(); 163 return v8::True(); 164} 165 166 167THREADED_TEST(AccessorProhibitsOverwriting) { 168 v8::HandleScope scope; 169 LocalContext context; 170 Local<ObjectTemplate> templ = ObjectTemplate::New(); 171 templ->SetAccessor(v8_str("x"), 172 AccessorProhibitsOverwritingGetter, 173 0, 174 v8::Handle<Value>(), 175 v8::PROHIBITS_OVERWRITING, 176 v8::ReadOnly); 177 Local<v8::Object> instance = templ->NewInstance(); 178 context->Global()->Set(v8_str("obj"), instance); 179 Local<Value> value = CompileRun( 180 "obj.__defineGetter__('x', function() { return false; });" 181 "obj.x"); 182 CHECK(value->BooleanValue()); 183 value = CompileRun( 184 "var setter_called = false;" 185 "obj.__defineSetter__('x', function() { setter_called = true; });" 186 "obj.x = 42;" 187 "setter_called"); 188 CHECK(!value->BooleanValue()); 189 value = CompileRun( 190 "obj2 = {};" 191 "obj2.__proto__ = obj;" 192 "obj2.__defineGetter__('x', function() { return false; });" 193 "obj2.x"); 194 CHECK(value->BooleanValue()); 195 value = CompileRun( 196 "var setter_called = false;" 197 "obj2 = {};" 198 "obj2.__proto__ = obj;" 199 "obj2.__defineSetter__('x', function() { setter_called = true; });" 200 "obj2.x = 42;" 201 "setter_called"); 202 CHECK(!value->BooleanValue()); 203} 204 205 206template <int C> 207static v8::Handle<Value> HandleAllocatingGetter(Local<String> name, 208 const AccessorInfo& info) { 209 ApiTestFuzzer::Fuzz(); 210 for (int i = 0; i < C; i++) 211 v8::String::New("foo"); 212 return v8::String::New("foo"); 213} 214 215 216THREADED_TEST(HandleScopePop) { 217 v8::HandleScope scope; 218 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 219 obj->SetAccessor(v8_str("one"), HandleAllocatingGetter<1>); 220 obj->SetAccessor(v8_str("many"), HandleAllocatingGetter<1024>); 221 LocalContext context; 222 v8::Handle<v8::Object> inst = obj->NewInstance(); 223 context->Global()->Set(v8::String::New("obj"), inst); 224 int count_before = i::HandleScope::NumberOfHandles(); 225 { 226 v8::HandleScope scope; 227 CompileRun( 228 "for (var i = 0; i < 1000; i++) {" 229 " obj.one;" 230 " obj.many;" 231 "}"); 232 } 233 int count_after = i::HandleScope::NumberOfHandles(); 234 CHECK_EQ(count_before, count_after); 235} 236 237static v8::Handle<Value> CheckAccessorArgsCorrect(Local<String> name, 238 const AccessorInfo& info) { 239 CHECK(info.This() == info.Holder()); 240 CHECK(info.Data()->Equals(v8::String::New("data"))); 241 ApiTestFuzzer::Fuzz(); 242 CHECK(info.This() == info.Holder()); 243 CHECK(info.Data()->Equals(v8::String::New("data"))); 244 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 245 CHECK(info.This() == info.Holder()); 246 CHECK(info.Data()->Equals(v8::String::New("data"))); 247 return v8::Integer::New(17); 248} 249 250THREADED_TEST(DirectCall) { 251 v8::HandleScope scope; 252 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 253 obj->SetAccessor(v8_str("xxx"), 254 CheckAccessorArgsCorrect, 255 NULL, 256 v8::String::New("data")); 257 LocalContext context; 258 v8::Handle<v8::Object> inst = obj->NewInstance(); 259 context->Global()->Set(v8::String::New("obj"), inst); 260 Local<Script> scr = v8::Script::Compile(v8::String::New("obj.xxx")); 261 for (int i = 0; i < 10; i++) { 262 Local<Value> result = scr->Run(); 263 CHECK(!result.IsEmpty()); 264 CHECK_EQ(17, result->Int32Value()); 265 } 266} 267 268static v8::Handle<Value> EmptyGetter(Local<String> name, 269 const AccessorInfo& info) { 270 CheckAccessorArgsCorrect(name, info); 271 ApiTestFuzzer::Fuzz(); 272 CheckAccessorArgsCorrect(name, info); 273 return v8::Handle<v8::Value>(); 274} 275 276THREADED_TEST(EmptyResult) { 277 v8::HandleScope scope; 278 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 279 obj->SetAccessor(v8_str("xxx"), EmptyGetter, NULL, v8::String::New("data")); 280 LocalContext context; 281 v8::Handle<v8::Object> inst = obj->NewInstance(); 282 context->Global()->Set(v8::String::New("obj"), inst); 283 Local<Script> scr = v8::Script::Compile(v8::String::New("obj.xxx")); 284 for (int i = 0; i < 10; i++) { 285 Local<Value> result = scr->Run(); 286 CHECK(result == v8::Undefined()); 287 } 288} 289 290 291THREADED_TEST(NoReuseRegress) { 292 // Check that the IC generated for the one test doesn't get reused 293 // for the other. 294 v8::HandleScope scope; 295 { 296 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 297 obj->SetAccessor(v8_str("xxx"), EmptyGetter, NULL, v8::String::New("data")); 298 LocalContext context; 299 v8::Handle<v8::Object> inst = obj->NewInstance(); 300 context->Global()->Set(v8::String::New("obj"), inst); 301 Local<Script> scr = v8::Script::Compile(v8::String::New("obj.xxx")); 302 for (int i = 0; i < 2; i++) { 303 Local<Value> result = scr->Run(); 304 CHECK(result == v8::Undefined()); 305 } 306 } 307 { 308 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 309 obj->SetAccessor(v8_str("xxx"), 310 CheckAccessorArgsCorrect, 311 NULL, 312 v8::String::New("data")); 313 LocalContext context; 314 v8::Handle<v8::Object> inst = obj->NewInstance(); 315 context->Global()->Set(v8::String::New("obj"), inst); 316 Local<Script> scr = v8::Script::Compile(v8::String::New("obj.xxx")); 317 for (int i = 0; i < 10; i++) { 318 Local<Value> result = scr->Run(); 319 CHECK(!result.IsEmpty()); 320 CHECK_EQ(17, result->Int32Value()); 321 } 322 } 323} 324 325static v8::Handle<Value> ThrowingGetAccessor(Local<String> name, 326 const AccessorInfo& info) { 327 ApiTestFuzzer::Fuzz(); 328 return v8::ThrowException(v8_str("g")); 329} 330 331 332static void ThrowingSetAccessor(Local<String> name, 333 Local<Value> value, 334 const AccessorInfo& info) { 335 v8::ThrowException(value); 336} 337 338 339THREADED_TEST(Regress1054726) { 340 v8::HandleScope scope; 341 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 342 obj->SetAccessor(v8_str("x"), 343 ThrowingGetAccessor, 344 ThrowingSetAccessor, 345 Local<Value>()); 346 347 LocalContext env; 348 env->Global()->Set(v8_str("obj"), obj->NewInstance()); 349 350 // Use the throwing property setter/getter in a loop to force 351 // the accessor ICs to be initialized. 352 v8::Handle<Value> result; 353 result = Script::Compile(v8_str( 354 "var result = '';" 355 "for (var i = 0; i < 5; i++) {" 356 " try { obj.x; } catch (e) { result += e; }" 357 "}; result"))->Run(); 358 CHECK_EQ(v8_str("ggggg"), result); 359 360 result = Script::Compile(String::New( 361 "var result = '';" 362 "for (var i = 0; i < 5; i++) {" 363 " try { obj.x = i; } catch (e) { result += e; }" 364 "}; result"))->Run(); 365 CHECK_EQ(v8_str("01234"), result); 366} 367 368 369static v8::Handle<Value> AllocGetter(Local<String> name, 370 const AccessorInfo& info) { 371 ApiTestFuzzer::Fuzz(); 372 return v8::Array::New(1000); 373} 374 375 376THREADED_TEST(Gc) { 377 v8::HandleScope scope; 378 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 379 obj->SetAccessor(v8_str("xxx"), AllocGetter); 380 LocalContext env; 381 env->Global()->Set(v8_str("obj"), obj->NewInstance()); 382 Script::Compile(String::New( 383 "var last = [];" 384 "for (var i = 0; i < 2048; i++) {" 385 " var result = obj.xxx;" 386 " result[0] = last;" 387 " last = result;" 388 "}"))->Run(); 389} 390 391 392static v8::Handle<Value> StackCheck(Local<String> name, 393 const AccessorInfo& info) { 394 i::StackFrameIterator iter; 395 for (int i = 0; !iter.done(); i++) { 396 i::StackFrame* frame = iter.frame(); 397 CHECK(i != 0 || (frame->type() == i::StackFrame::EXIT)); 398 i::Code* code = frame->LookupCode(); 399 CHECK(code->IsCode()); 400 i::Address pc = frame->pc(); 401 CHECK(code->contains(pc)); 402 iter.Advance(); 403 } 404 return v8::Undefined(); 405} 406 407 408THREADED_TEST(StackIteration) { 409 v8::HandleScope scope; 410 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 411 i::StringStream::ClearMentionedObjectCache(); 412 obj->SetAccessor(v8_str("xxx"), StackCheck); 413 LocalContext env; 414 env->Global()->Set(v8_str("obj"), obj->NewInstance()); 415 Script::Compile(String::New( 416 "function foo() {" 417 " return obj.xxx;" 418 "}" 419 "for (var i = 0; i < 100; i++) {" 420 " foo();" 421 "}"))->Run(); 422} 423 424 425static v8::Handle<Value> AllocateHandles(Local<String> name, 426 const AccessorInfo& info) { 427 for (int i = 0; i < i::kHandleBlockSize + 1; i++) { 428 v8::Local<v8::Value>::New(name); 429 } 430 return v8::Integer::New(100); 431} 432 433 434THREADED_TEST(HandleScopeSegment) { 435 // Check that we can return values past popping of handle scope 436 // segments. 437 v8::HandleScope scope; 438 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 439 obj->SetAccessor(v8_str("xxx"), AllocateHandles); 440 LocalContext env; 441 env->Global()->Set(v8_str("obj"), obj->NewInstance()); 442 v8::Handle<v8::Value> result = Script::Compile(String::New( 443 "var result;" 444 "for (var i = 0; i < 4; i++)" 445 " result = obj.xxx;" 446 "result;"))->Run(); 447 CHECK_EQ(100, result->Int32Value()); 448} 449