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