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 37 namespace v8 { 38 namespace internal { 39 40 41 template <class C> 42 static 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. 54 Object* Accessors::IllegalSetter(JSObject*, Object*, void*) { 55 UNREACHABLE(); 56 return NULL; 57 } 58 59 60 Object* Accessors::IllegalGetAccessor(Object* object, void*) { 61 UNREACHABLE(); 62 return object; 63 } 64 65 66 Object* 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 78 Object* 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. 88 Object* 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 100 Object* 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 136 const AccessorDescriptor Accessors::ArrayLength = { 137 ArrayGetLength, 138 ArraySetLength, 139 0 140 }; 141 142 143 // 144 // Accessors::StringLength 145 // 146 147 148 Object* 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 158 const AccessorDescriptor Accessors::StringLength = { 159 StringGetLength, 160 IllegalSetter, 161 0 162 }; 163 164 165 // 166 // Accessors::ScriptSource 167 // 168 169 170 Object* Accessors::ScriptGetSource(Object* object, void*) { 171 Object* script = JSValue::cast(object)->value(); 172 return Script::cast(script)->source(); 173 } 174 175 176 const AccessorDescriptor Accessors::ScriptSource = { 177 ScriptGetSource, 178 IllegalSetter, 179 0 180 }; 181 182 183 // 184 // Accessors::ScriptName 185 // 186 187 188 Object* Accessors::ScriptGetName(Object* object, void*) { 189 Object* script = JSValue::cast(object)->value(); 190 return Script::cast(script)->name(); 191 } 192 193 194 const AccessorDescriptor Accessors::ScriptName = { 195 ScriptGetName, 196 IllegalSetter, 197 0 198 }; 199 200 201 // 202 // Accessors::ScriptId 203 // 204 205 206 Object* Accessors::ScriptGetId(Object* object, void*) { 207 Object* script = JSValue::cast(object)->value(); 208 return Script::cast(script)->id(); 209 } 210 211 212 const AccessorDescriptor Accessors::ScriptId = { 213 ScriptGetId, 214 IllegalSetter, 215 0 216 }; 217 218 219 // 220 // Accessors::ScriptLineOffset 221 // 222 223 224 Object* Accessors::ScriptGetLineOffset(Object* object, void*) { 225 Object* script = JSValue::cast(object)->value(); 226 return Script::cast(script)->line_offset(); 227 } 228 229 230 const AccessorDescriptor Accessors::ScriptLineOffset = { 231 ScriptGetLineOffset, 232 IllegalSetter, 233 0 234 }; 235 236 237 // 238 // Accessors::ScriptColumnOffset 239 // 240 241 242 Object* Accessors::ScriptGetColumnOffset(Object* object, void*) { 243 Object* script = JSValue::cast(object)->value(); 244 return Script::cast(script)->column_offset(); 245 } 246 247 248 const AccessorDescriptor Accessors::ScriptColumnOffset = { 249 ScriptGetColumnOffset, 250 IllegalSetter, 251 0 252 }; 253 254 255 // 256 // Accessors::ScriptData 257 // 258 259 260 Object* Accessors::ScriptGetData(Object* object, void*) { 261 Object* script = JSValue::cast(object)->value(); 262 return Script::cast(script)->data(); 263 } 264 265 266 const AccessorDescriptor Accessors::ScriptData = { 267 ScriptGetData, 268 IllegalSetter, 269 0 270 }; 271 272 273 // 274 // Accessors::ScriptType 275 // 276 277 278 Object* Accessors::ScriptGetType(Object* object, void*) { 279 Object* script = JSValue::cast(object)->value(); 280 return Script::cast(script)->type(); 281 } 282 283 284 const AccessorDescriptor Accessors::ScriptType = { 285 ScriptGetType, 286 IllegalSetter, 287 0 288 }; 289 290 291 // 292 // Accessors::ScriptCompilationType 293 // 294 295 296 Object* Accessors::ScriptGetCompilationType(Object* object, void*) { 297 Object* script = JSValue::cast(object)->value(); 298 return Script::cast(script)->compilation_type(); 299 } 300 301 302 const AccessorDescriptor Accessors::ScriptCompilationType = { 303 ScriptGetCompilationType, 304 IllegalSetter, 305 0 306 }; 307 308 309 // 310 // Accessors::ScriptGetLineEnds 311 // 312 313 314 Object* 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 326 const AccessorDescriptor Accessors::ScriptLineEnds = { 327 ScriptGetLineEnds, 328 IllegalSetter, 329 0 330 }; 331 332 333 // 334 // Accessors::ScriptGetContextData 335 // 336 337 338 Object* Accessors::ScriptGetContextData(Object* object, void*) { 339 Object* script = JSValue::cast(object)->value(); 340 return Script::cast(script)->context_data(); 341 } 342 343 344 const AccessorDescriptor Accessors::ScriptContextData = { 345 ScriptGetContextData, 346 IllegalSetter, 347 0 348 }; 349 350 351 // 352 // Accessors::ScriptGetEvalFromScript 353 // 354 355 356 Object* 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 371 const AccessorDescriptor Accessors::ScriptEvalFromScript = { 372 ScriptGetEvalFromScript, 373 IllegalSetter, 374 0 375 }; 376 377 378 // 379 // Accessors::ScriptGetEvalFromScriptPosition 380 // 381 382 383 Object* 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 402 const AccessorDescriptor Accessors::ScriptEvalFromScriptPosition = { 403 ScriptGetEvalFromScriptPosition, 404 IllegalSetter, 405 0 406 }; 407 408 409 // 410 // Accessors::ScriptGetEvalFromFunctionName 411 // 412 413 414 Object* 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 429 const AccessorDescriptor Accessors::ScriptEvalFromFunctionName = { 430 ScriptGetEvalFromFunctionName, 431 IllegalSetter, 432 0 433 }; 434 435 436 // 437 // Accessors::FunctionPrototype 438 // 439 440 441 Object* 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 455 Object* 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 475 const AccessorDescriptor Accessors::FunctionPrototype = { 476 FunctionGetPrototype, 477 FunctionSetPrototype, 478 0 479 }; 480 481 482 // 483 // Accessors::FunctionLength 484 // 485 486 487 Object* 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 507 const AccessorDescriptor Accessors::FunctionLength = { 508 FunctionGetLength, 509 ReadOnlySetAccessor, 510 0 511 }; 512 513 514 // 515 // Accessors::FunctionName 516 // 517 518 519 Object* 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 527 const AccessorDescriptor Accessors::FunctionName = { 528 FunctionGetName, 529 ReadOnlySetAccessor, 530 0 531 }; 532 533 534 // 535 // Accessors::FunctionArguments 536 // 537 538 539 Object* 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 586 const AccessorDescriptor Accessors::FunctionArguments = { 587 FunctionGetArguments, 588 ReadOnlySetAccessor, 589 0 590 }; 591 592 593 // 594 // Accessors::FunctionCaller 595 // 596 597 598 Object* 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 625 const AccessorDescriptor Accessors::FunctionCaller = { 626 FunctionGetCaller, 627 ReadOnlySetAccessor, 628 0 629 }; 630 631 632 // 633 // Accessors::ObjectPrototype 634 // 635 636 637 Object* 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 647 Object* Accessors::ObjectSetPrototype(JSObject* receiver, 648 Object* value, 649 void*) { 650 const bool skip_hidden_prototypes = true; 651 // To be consistent with other Set functions, return the value. 652 return receiver->SetPrototype(value, skip_hidden_prototypes); 653 } 654 655 656 const AccessorDescriptor Accessors::ObjectPrototype = { 657 ObjectGetPrototype, 658 ObjectSetPrototype, 659 0 660 }; 661 662 } } // namespace v8::internal 663