OLD | NEW |
(Empty) | |
| 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 } |
OLD | NEW |