accessors.cc revision 4515c472dc3e5ed2448a564600976759e569a0a8
1// Copyright 2006-2008 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 "v8.h" 29 30#include "accessors.h" 31#include "execution.h" 32#include "factory.h" 33#include "scopeinfo.h" 34#include "top.h" 35#include "zone-inl.h" 36 37namespace v8 { 38namespace internal { 39 40 41template <class C> 42static C* FindInPrototypeChain(Object* obj, bool* found_it) { 43 ASSERT(!*found_it); 44 while (!Is<C>(obj)) { 45 if (obj == Heap::null_value()) return NULL; 46 obj = obj->GetPrototype(); 47 } 48 *found_it = true; 49 return C::cast(obj); 50} 51 52 53// Entry point that never should be called. 54Object* Accessors::IllegalSetter(JSObject*, Object*, void*) { 55 UNREACHABLE(); 56 return NULL; 57} 58 59 60Object* Accessors::IllegalGetAccessor(Object* object, void*) { 61 UNREACHABLE(); 62 return object; 63} 64 65 66Object* Accessors::ReadOnlySetAccessor(JSObject*, Object* value, void*) { 67 // According to ECMA-262, section 8.6.2.2, page 28, setting 68 // read-only properties must be silently ignored. 69 return value; 70} 71 72 73// 74// Accessors::ArrayLength 75// 76 77 78Object* Accessors::ArrayGetLength(Object* object, void*) { 79 // Traverse the prototype chain until we reach an array. 80 bool found_it = false; 81 JSArray* holder = FindInPrototypeChain<JSArray>(object, &found_it); 82 if (!found_it) return Smi::FromInt(0); 83 return holder->length(); 84} 85 86 87// The helper function will 'flatten' Number objects. 88Object* Accessors::FlattenNumber(Object* value) { 89 if (value->IsNumber() || !value->IsJSValue()) return value; 90 JSValue* wrapper = JSValue::cast(value); 91 ASSERT( 92 Top::context()->global_context()->number_function()->has_initial_map()); 93 Map* number_map = 94 Top::context()->global_context()->number_function()->initial_map(); 95 if (wrapper->map() == number_map) return wrapper->value(); 96 return value; 97} 98 99 100Object* Accessors::ArraySetLength(JSObject* object, Object* value, void*) { 101 value = FlattenNumber(value); 102 103 // Need to call methods that may trigger GC. 104 HandleScope scope; 105 106 // Protect raw pointers. 107 Handle<JSObject> object_handle(object); 108 Handle<Object> value_handle(value); 109 110 bool has_exception; 111 Handle<Object> uint32_v = Execution::ToUint32(value_handle, &has_exception); 112 if (has_exception) return Failure::Exception(); 113 Handle<Object> number_v = Execution::ToNumber(value_handle, &has_exception); 114 if (has_exception) return Failure::Exception(); 115 116 // Restore raw pointers, 117 object = *object_handle; 118 value = *value_handle; 119 120 if (uint32_v->Number() == number_v->Number()) { 121 if (object->IsJSArray()) { 122 return JSArray::cast(object)->SetElementsLength(*uint32_v); 123 } else { 124 // This means one of the object's prototypes is a JSArray and 125 // the object does not have a 'length' property. 126 // Calling SetProperty causes an infinite loop. 127 return object->IgnoreAttributesAndSetLocalProperty(Heap::length_symbol(), 128 value, NONE); 129 } 130 } 131 return Top::Throw(*Factory::NewRangeError("invalid_array_length", 132 HandleVector<Object>(NULL, 0))); 133} 134 135 136const AccessorDescriptor Accessors::ArrayLength = { 137 ArrayGetLength, 138 ArraySetLength, 139 0 140}; 141 142 143// 144// Accessors::StringLength 145// 146 147 148Object* Accessors::StringGetLength(Object* object, void*) { 149 Object* value = object; 150 if (object->IsJSValue()) value = JSValue::cast(object)->value(); 151 if (value->IsString()) return Smi::FromInt(String::cast(value)->length()); 152 // If object is not a string we return 0 to be compatible with WebKit. 153 // Note: Firefox returns the length of ToString(object). 154 return Smi::FromInt(0); 155} 156 157 158const AccessorDescriptor Accessors::StringLength = { 159 StringGetLength, 160 IllegalSetter, 161 0 162}; 163 164 165// 166// Accessors::ScriptSource 167// 168 169 170Object* Accessors::ScriptGetSource(Object* object, void*) { 171 Object* script = JSValue::cast(object)->value(); 172 return Script::cast(script)->source(); 173} 174 175 176const AccessorDescriptor Accessors::ScriptSource = { 177 ScriptGetSource, 178 IllegalSetter, 179 0 180}; 181 182 183// 184// Accessors::ScriptName 185// 186 187 188Object* Accessors::ScriptGetName(Object* object, void*) { 189 Object* script = JSValue::cast(object)->value(); 190 return Script::cast(script)->name(); 191} 192 193 194const AccessorDescriptor Accessors::ScriptName = { 195 ScriptGetName, 196 IllegalSetter, 197 0 198}; 199 200 201// 202// Accessors::ScriptId 203// 204 205 206Object* Accessors::ScriptGetId(Object* object, void*) { 207 Object* script = JSValue::cast(object)->value(); 208 return Script::cast(script)->id(); 209} 210 211 212const AccessorDescriptor Accessors::ScriptId = { 213 ScriptGetId, 214 IllegalSetter, 215 0 216}; 217 218 219// 220// Accessors::ScriptLineOffset 221// 222 223 224Object* Accessors::ScriptGetLineOffset(Object* object, void*) { 225 Object* script = JSValue::cast(object)->value(); 226 return Script::cast(script)->line_offset(); 227} 228 229 230const AccessorDescriptor Accessors::ScriptLineOffset = { 231 ScriptGetLineOffset, 232 IllegalSetter, 233 0 234}; 235 236 237// 238// Accessors::ScriptColumnOffset 239// 240 241 242Object* Accessors::ScriptGetColumnOffset(Object* object, void*) { 243 Object* script = JSValue::cast(object)->value(); 244 return Script::cast(script)->column_offset(); 245} 246 247 248const AccessorDescriptor Accessors::ScriptColumnOffset = { 249 ScriptGetColumnOffset, 250 IllegalSetter, 251 0 252}; 253 254 255// 256// Accessors::ScriptData 257// 258 259 260Object* Accessors::ScriptGetData(Object* object, void*) { 261 Object* script = JSValue::cast(object)->value(); 262 return Script::cast(script)->data(); 263} 264 265 266const AccessorDescriptor Accessors::ScriptData = { 267 ScriptGetData, 268 IllegalSetter, 269 0 270}; 271 272 273// 274// Accessors::ScriptType 275// 276 277 278Object* Accessors::ScriptGetType(Object* object, void*) { 279 Object* script = JSValue::cast(object)->value(); 280 return Script::cast(script)->type(); 281} 282 283 284const AccessorDescriptor Accessors::ScriptType = { 285 ScriptGetType, 286 IllegalSetter, 287 0 288}; 289 290 291// 292// Accessors::ScriptCompilationType 293// 294 295 296Object* Accessors::ScriptGetCompilationType(Object* object, void*) { 297 Object* script = JSValue::cast(object)->value(); 298 return Script::cast(script)->compilation_type(); 299} 300 301 302const AccessorDescriptor Accessors::ScriptCompilationType = { 303 ScriptGetCompilationType, 304 IllegalSetter, 305 0 306}; 307 308 309// 310// Accessors::ScriptGetLineEnds 311// 312 313 314Object* Accessors::ScriptGetLineEnds(Object* object, void*) { 315 HandleScope scope; 316 Handle<Script> script(Script::cast(JSValue::cast(object)->value())); 317 InitScriptLineEnds(script); 318 ASSERT(script->line_ends()->IsFixedArray()); 319 Handle<FixedArray> line_ends(FixedArray::cast(script->line_ends())); 320 Handle<FixedArray> copy = Factory::CopyFixedArray(line_ends); 321 Handle<JSArray> js_array = Factory::NewJSArrayWithElements(copy); 322 return *js_array; 323} 324 325 326const AccessorDescriptor Accessors::ScriptLineEnds = { 327 ScriptGetLineEnds, 328 IllegalSetter, 329 0 330}; 331 332 333// 334// Accessors::ScriptGetContextData 335// 336 337 338Object* Accessors::ScriptGetContextData(Object* object, void*) { 339 Object* script = JSValue::cast(object)->value(); 340 return Script::cast(script)->context_data(); 341} 342 343 344const AccessorDescriptor Accessors::ScriptContextData = { 345 ScriptGetContextData, 346 IllegalSetter, 347 0 348}; 349 350 351// 352// Accessors::ScriptGetEvalFromScript 353// 354 355 356Object* Accessors::ScriptGetEvalFromScript(Object* object, void*) { 357 Object* script = JSValue::cast(object)->value(); 358 if (!Script::cast(script)->eval_from_shared()->IsUndefined()) { 359 Handle<SharedFunctionInfo> eval_from_shared( 360 SharedFunctionInfo::cast(Script::cast(script)->eval_from_shared())); 361 362 if (eval_from_shared->script()->IsScript()) { 363 Handle<Script> eval_from_script(Script::cast(eval_from_shared->script())); 364 return *GetScriptWrapper(eval_from_script); 365 } 366 } 367 return Heap::undefined_value(); 368} 369 370 371const AccessorDescriptor Accessors::ScriptEvalFromScript = { 372 ScriptGetEvalFromScript, 373 IllegalSetter, 374 0 375}; 376 377 378// 379// Accessors::ScriptGetEvalFromScriptPosition 380// 381 382 383Object* Accessors::ScriptGetEvalFromScriptPosition(Object* object, void*) { 384 HandleScope scope; 385 Handle<Script> script(Script::cast(JSValue::cast(object)->value())); 386 387 // If this is not a script compiled through eval there is no eval position. 388 int compilation_type = Smi::cast(script->compilation_type())->value(); 389 if (compilation_type != Script::COMPILATION_TYPE_EVAL) { 390 return Heap::undefined_value(); 391 } 392 393 // Get the function from where eval was called and find the source position 394 // from the instruction offset. 395 Handle<Code> code(SharedFunctionInfo::cast( 396 script->eval_from_shared())->code()); 397 return Smi::FromInt(code->SourcePosition(code->instruction_start() + 398 script->eval_from_instructions_offset()->value())); 399} 400 401 402const AccessorDescriptor Accessors::ScriptEvalFromScriptPosition = { 403 ScriptGetEvalFromScriptPosition, 404 IllegalSetter, 405 0 406}; 407 408 409// 410// Accessors::ScriptGetEvalFromFunctionName 411// 412 413 414Object* Accessors::ScriptGetEvalFromFunctionName(Object* object, void*) { 415 Object* script = JSValue::cast(object)->value(); 416 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast( 417 Script::cast(script)->eval_from_shared())); 418 419 420 // Find the name of the function calling eval. 421 if (!shared->name()->IsUndefined()) { 422 return shared->name(); 423 } else { 424 return shared->inferred_name(); 425 } 426} 427 428 429const AccessorDescriptor Accessors::ScriptEvalFromFunctionName = { 430 ScriptGetEvalFromFunctionName, 431 IllegalSetter, 432 0 433}; 434 435 436// 437// Accessors::FunctionPrototype 438// 439 440 441Object* Accessors::FunctionGetPrototype(Object* object, void*) { 442 bool found_it = false; 443 JSFunction* function = FindInPrototypeChain<JSFunction>(object, &found_it); 444 if (!found_it) return Heap::undefined_value(); 445 if (!function->has_prototype()) { 446 Object* prototype = Heap::AllocateFunctionPrototype(function); 447 if (prototype->IsFailure()) return prototype; 448 Object* result = function->SetPrototype(prototype); 449 if (result->IsFailure()) return result; 450 } 451 return function->prototype(); 452} 453 454 455Object* Accessors::FunctionSetPrototype(JSObject* object, 456 Object* value, 457 void*) { 458 bool found_it = false; 459 JSFunction* function = FindInPrototypeChain<JSFunction>(object, &found_it); 460 if (!found_it) return Heap::undefined_value(); 461 if (function->has_initial_map()) { 462 // If the function has allocated the initial map 463 // replace it with a copy containing the new prototype. 464 Object* new_map = function->initial_map()->CopyDropTransitions(); 465 if (new_map->IsFailure()) return new_map; 466 function->set_initial_map(Map::cast(new_map)); 467 } 468 Object* prototype = function->SetPrototype(value); 469 if (prototype->IsFailure()) return prototype; 470 ASSERT(function->prototype() == value); 471 return function; 472} 473 474 475const AccessorDescriptor Accessors::FunctionPrototype = { 476 FunctionGetPrototype, 477 FunctionSetPrototype, 478 0 479}; 480 481 482// 483// Accessors::FunctionLength 484// 485 486 487Object* Accessors::FunctionGetLength(Object* object, void*) { 488 bool found_it = false; 489 JSFunction* function = FindInPrototypeChain<JSFunction>(object, &found_it); 490 if (!found_it) return Smi::FromInt(0); 491 // Check if already compiled. 492 if (!function->is_compiled()) { 493 // If the function isn't compiled yet, the length is not computed 494 // correctly yet. Compile it now and return the right length. 495 HandleScope scope; 496 Handle<SharedFunctionInfo> shared(function->shared()); 497 if (!CompileLazyShared(shared, KEEP_EXCEPTION)) { 498 return Failure::Exception(); 499 } 500 return Smi::FromInt(shared->length()); 501 } else { 502 return Smi::FromInt(function->shared()->length()); 503 } 504} 505 506 507const AccessorDescriptor Accessors::FunctionLength = { 508 FunctionGetLength, 509 ReadOnlySetAccessor, 510 0 511}; 512 513 514// 515// Accessors::FunctionName 516// 517 518 519Object* Accessors::FunctionGetName(Object* object, void*) { 520 bool found_it = false; 521 JSFunction* holder = FindInPrototypeChain<JSFunction>(object, &found_it); 522 if (!found_it) return Heap::undefined_value(); 523 return holder->shared()->name(); 524} 525 526 527const AccessorDescriptor Accessors::FunctionName = { 528 FunctionGetName, 529 ReadOnlySetAccessor, 530 0 531}; 532 533 534// 535// Accessors::FunctionArguments 536// 537 538 539Object* Accessors::FunctionGetArguments(Object* object, void*) { 540 HandleScope scope; 541 bool found_it = false; 542 JSFunction* holder = FindInPrototypeChain<JSFunction>(object, &found_it); 543 if (!found_it) return Heap::undefined_value(); 544 Handle<JSFunction> function(holder); 545 546 // Find the top invocation of the function by traversing frames. 547 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) { 548 // Skip all frames that aren't invocations of the given function. 549 JavaScriptFrame* frame = it.frame(); 550 if (frame->function() != *function) continue; 551 552 // If there is an arguments variable in the stack, we return that. 553 int index = ScopeInfo<>::StackSlotIndex(frame->code(), 554 Heap::arguments_symbol()); 555 if (index >= 0) { 556 Handle<Object> arguments = Handle<Object>(frame->GetExpression(index)); 557 if (!arguments->IsTheHole()) return *arguments; 558 } 559 560 // If there isn't an arguments variable in the stack, we need to 561 // find the frame that holds the actual arguments passed to the 562 // function on the stack. 563 it.AdvanceToArgumentsFrame(); 564 frame = it.frame(); 565 566 // Get the number of arguments and construct an arguments object 567 // mirror for the right frame. 568 const int length = frame->GetProvidedParametersCount(); 569 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length); 570 Handle<FixedArray> array = Factory::NewFixedArray(length); 571 572 // Copy the parameters to the arguments object. 573 ASSERT(array->length() == length); 574 for (int i = 0; i < length; i++) array->set(i, frame->GetParameter(i)); 575 arguments->set_elements(*array); 576 577 // Return the freshly allocated arguments object. 578 return *arguments; 579 } 580 581 // No frame corresponding to the given function found. Return null. 582 return Heap::null_value(); 583} 584 585 586const AccessorDescriptor Accessors::FunctionArguments = { 587 FunctionGetArguments, 588 ReadOnlySetAccessor, 589 0 590}; 591 592 593// 594// Accessors::FunctionCaller 595// 596 597 598Object* Accessors::FunctionGetCaller(Object* object, void*) { 599 HandleScope scope; 600 bool found_it = false; 601 JSFunction* holder = FindInPrototypeChain<JSFunction>(object, &found_it); 602 if (!found_it) return Heap::undefined_value(); 603 Handle<JSFunction> function(holder); 604 605 // Find the top invocation of the function by traversing frames. 606 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) { 607 // Skip all frames that aren't invocations of the given function. 608 if (it.frame()->function() != *function) continue; 609 // Once we have found the frame, we need to go to the caller 610 // frame. This may require skipping through a number of top-level 611 // frames, e.g. frames for scripts not functions. 612 while (true) { 613 it.Advance(); 614 if (it.done()) return Heap::null_value(); 615 JSFunction* caller = JSFunction::cast(it.frame()->function()); 616 if (!caller->shared()->is_toplevel()) return caller; 617 } 618 } 619 620 // No frame corresponding to the given function found. Return null. 621 return Heap::null_value(); 622} 623 624 625const AccessorDescriptor Accessors::FunctionCaller = { 626 FunctionGetCaller, 627 ReadOnlySetAccessor, 628 0 629}; 630 631 632// 633// Accessors::ObjectPrototype 634// 635 636 637Object* Accessors::ObjectGetPrototype(Object* receiver, void*) { 638 Object* current = receiver->GetPrototype(); 639 while (current->IsJSObject() && 640 JSObject::cast(current)->map()->is_hidden_prototype()) { 641 current = current->GetPrototype(); 642 } 643 return current; 644} 645 646 647Object* Accessors::ObjectSetPrototype(JSObject* receiver, 648 Object* value, 649 void*) { 650 // Before we can set the prototype we need to be sure 651 // prototype cycles are prevented. 652 // It is sufficient to validate that the receiver is not in the new prototype 653 // chain. 654 655 // Silently ignore the change if value is not a JSObject or null. 656 // SpiderMonkey behaves this way. 657 if (!value->IsJSObject() && !value->IsNull()) return value; 658 659 for (Object* pt = value; pt != Heap::null_value(); pt = pt->GetPrototype()) { 660 if (JSObject::cast(pt) == receiver) { 661 // Cycle detected. 662 HandleScope scope; 663 return Top::Throw(*Factory::NewError("cyclic_proto", 664 HandleVector<Object>(NULL, 0))); 665 } 666 } 667 668 // Find the first object in the chain whose prototype object is not 669 // hidden and set the new prototype on that object. 670 JSObject* current = receiver; 671 Object* current_proto = receiver->GetPrototype(); 672 while (current_proto->IsJSObject() && 673 JSObject::cast(current_proto)->map()->is_hidden_prototype()) { 674 current = JSObject::cast(current_proto); 675 current_proto = current_proto->GetPrototype(); 676 } 677 678 // Set the new prototype of the object. 679 Object* new_map = current->map()->CopyDropTransitions(); 680 if (new_map->IsFailure()) return new_map; 681 Map::cast(new_map)->set_prototype(value); 682 current->set_map(Map::cast(new_map)); 683 684 // To be consistent with other Set functions, return the value. 685 return value; 686} 687 688 689const AccessorDescriptor Accessors::ObjectPrototype = { 690 ObjectGetPrototype, 691 ObjectSetPrototype, 692 0 693}; 694 695} } // namespace v8::internal 696