My favorites | Sign in
v8
Project Home Downloads Wiki Issues Source Code Search
Checkout   Browse   Changes  
Changes to /trunk/src/accessors.cc
r0 vs. r9 Compare: vs.  Format:
Revision r9
Go to: 
Project members, sign in to write a code review
/trunk/src/accessors.cc /trunk/src/accessors.cc   r9
  1 // Copyright 2006-2008 Google Inc. 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 { namespace internal {
  38
  39
  40 template <class C>
  41 static C* FindInPrototypeChain(Object* obj, bool* found_it) {
  42 ASSERT(!*found_it);
  43 while (!Is<C>(obj)) {
  44 if (obj == Heap::null_value()) return NULL;
  45 obj = obj->GetPrototype();
  46 }
  47 *found_it = true;
  48 return C::cast(obj);
  49 }
  50
  51
  52 // Entry point that never should be called.
  53 Object* Accessors::IllegalSetter(JSObject*, Object*, void*) {
  54 UNREACHABLE();
  55 return NULL;
  56 }
  57
  58
  59 Object* Accessors::IllegalGetAccessor(Object* object, void*) {
  60 UNREACHABLE();
  61 return object;
  62 }
  63
  64
  65 Object* Accessors::ReadOnlySetAccessor(JSObject*, Object* value, void*) {
  66 // According to ECMA-262, section 8.6.2.2, page 28, setting
  67 // read-only properties must be silently ignored.
  68 return value;
  69 }
  70
  71
  72 //
  73 // Accessors::ArrayLength
  74 //
  75
  76
  77 Object* Accessors::ArrayGetLength(Object* object, void*) {
  78 // Traverse the prototype chain until we reach an array.
  79 bool found_it = false;
  80 JSArray* holder = FindInPrototypeChain<JSArray>(object, &found_it);
  81 if (!found_it) return Smi::FromInt(0);
  82 return holder->length();
  83 }
  84
  85
  86 // The helper function will 'flatten' Number objects.
  87 Object* Accessors::FlattenNumber(Object* value) {
  88 if (value->IsNumber() || !value->IsJSValue()) return value;
  89 JSValue* wrapper = JSValue::cast(value);
  90 ASSERT(
  91 Top::context()->global_context()->number_function()->has_initial_map());
  92 Map* number_map =
  93 Top::context()->global_context()->number_function()->initial_map();
  94 if (wrapper->map() == number_map) return wrapper->value();
  95 return value;
  96 }
  97
  98
  99 Object* Accessors::ArraySetLength(JSObject* object, Object* value, void*) {
  100 value = FlattenNumber(value);
  101
  102 // Need to call methods that may trigger GC.
  103 HandleScope scope;
  104
  105 // Protect raw pointers.
  106 Handle<JSObject> object_handle(object);
  107 Handle<Object> value_handle(value);
  108
  109 bool has_exception;
  110 Handle<Object> uint32_v = Execution::ToUint32(value_handle, &has_exception);
  111 if (has_exception) return Failure::Exception();
  112 Handle<Object> number_v = Execution::ToNumber(value_handle, &has_exception);
  113 if (has_exception) return Failure::Exception();
  114
  115 // Restore raw pointers,
  116 object = *object_handle;
  117 value = *value_handle;
  118
  119 if (uint32_v->Number() == number_v->Number()) {
  120 if (object->IsJSArray()) {
  121 return JSArray::cast(object)->SetElementsLength(*uint32_v);
  122 } else {
  123 // This means one of the object's prototypes is a JSArray and
  124 // the object does not have a 'length' property.
  125 return object->AddProperty(Heap::length_symbol(), value, NONE);
  126 }
  127 }
  128
  129 return Top::Throw(*Factory::NewRangeError("invalid_array_length",
  130 HandleVector<Object>(NULL, 0)));
  131 }
  132
  133
  134 const AccessorDescriptor Accessors::ArrayLength = {
  135 ArrayGetLength,
  136 ArraySetLength,
  137 0
  138 };
  139
  140
  141 //
  142 // Accessors::StringLength
  143 //
  144
  145
  146 Object* Accessors::StringGetLength(Object* object, void*) {
  147 Object* value = object;
  148 if (object->IsJSValue()) value = JSValue::cast(object)->value();
  149 if (value->IsString()) return Smi::FromInt(String::cast(value)->length());
  150 // If object is not a string we return 0 to be compatible with WebKit.
  151 // Note: Firefox returns the length of ToString(object).
  152 return Smi::FromInt(0);
  153 }
  154
  155
  156 const AccessorDescriptor Accessors::StringLength = {
  157 StringGetLength,
  158 IllegalSetter,
  159 0
  160 };
  161
  162
  163 //
  164 // Accessors::ScriptSource
  165 //
  166
  167
  168 Object* Accessors::ScriptGetSource(Object* object, void*) {
  169 Object* script = JSValue::cast(object)->value();
  170 return Script::cast(script)->source();
  171 }
  172
  173
  174 const AccessorDescriptor Accessors::ScriptSource = {
  175 ScriptGetSource,
  176 IllegalSetter,
  177 0
  178 };
  179
  180
  181 //
  182 // Accessors::ScriptName
  183 //
  184
  185
  186 Object* Accessors::ScriptGetName(Object* object, void*) {
  187 Object* script = JSValue::cast(object)->value();
  188 return Script::cast(script)->name();
  189 }
  190
  191
  192 const AccessorDescriptor Accessors::ScriptName = {
  193 ScriptGetName,
  194 IllegalSetter,
  195 0
  196 };
  197
  198
  199 //
  200 // Accessors::ScriptLineOffset
  201 //
  202
  203
  204 Object* Accessors::ScriptGetLineOffset(Object* object, void*) {
  205 Object* script = JSValue::cast(object)->value();
  206 return Script::cast(script)->line_offset();
  207 }
  208
  209
  210 const AccessorDescriptor Accessors::ScriptLineOffset = {
  211 ScriptGetLineOffset,
  212 IllegalSetter,
  213 0
  214 };
  215
  216
  217 //
  218 // Accessors::ScriptColumnOffset
  219 //
  220
  221
  222 Object* Accessors::ScriptGetColumnOffset(Object* object, void*) {
  223 Object* script = JSValue::cast(object)->value();
  224 return Script::cast(script)->column_offset();
  225 }
  226
  227
  228 const AccessorDescriptor Accessors::ScriptColumnOffset = {
  229 ScriptGetColumnOffset,
  230 IllegalSetter,
  231 0
  232 };
  233
  234
  235 //
  236 // Accessors::ScriptType
  237 //
  238
  239
  240 Object* Accessors::ScriptGetType(Object* object, void*) {
  241 Object* script = JSValue::cast(object)->value();
  242 return Script::cast(script)->type();
  243 }
  244
  245
  246 const AccessorDescriptor Accessors::ScriptType = {
  247 ScriptGetType,
  248 IllegalSetter,
  249 0
  250 };
  251
  252
  253 //
  254 // Accessors::FunctionPrototype
  255 //
  256
  257
  258 Object* Accessors::FunctionGetPrototype(Object* object, void*) {
  259 bool found_it = false;
  260 JSFunction* function = FindInPrototypeChain<JSFunction>(object, &found_it);
  261 if (!found_it) return Heap::undefined_value();
  262 if (!function->has_prototype()) {
  263 Object* prototype = Heap::AllocateFunctionPrototype(function);
  264 if (prototype->IsFailure()) return prototype;
  265 Object* result = function->SetPrototype(prototype);
  266 if (result->IsFailure()) return result;
  267 }
  268 return function->prototype();
  269 }
  270
  271
  272 Object* Accessors::FunctionSetPrototype(JSObject* object,
  273 Object* value,
  274 void*) {
  275 bool found_it = false;
  276 JSFunction* function = FindInPrototypeChain<JSFunction>(object, &found_it);
  277 if (!found_it) return Heap::undefined_value();
  278 if (function->has_initial_map()) {
  279 // If the function has allocated the initial map
  280 // replace it with a copy containing the new prototype.
  281 Object* new_map = function->initial_map()->Copy();
  282 if (new_map->IsFailure()) return new_map;
  283 Object* result = Map::cast(new_map)->EnsureNoMapTransitions();
  284 if (result->IsFailure()) return result;
  285 function->set_initial_map(Map::cast(new_map));
  286 }
  287 Object* prototype = function->SetPrototype(value);
  288 if (prototype->IsFailure()) return prototype;
  289 ASSERT(function->prototype() == value);
  290 return function;
  291 }
  292
  293
  294 const AccessorDescriptor Accessors::FunctionPrototype = {
  295 FunctionGetPrototype,
  296 FunctionSetPrototype,
  297 0
  298 };
  299
  300
  301 //
  302 // Accessors::FunctionLength
  303 //
  304
  305
  306 Object* Accessors::FunctionGetLength(Object* object, void*) {
  307 bool found_it = false;
  308 JSFunction* function = FindInPrototypeChain<JSFunction>(object, &found_it);
  309 if (!found_it) return Smi::FromInt(0);
  310 // Check if already compiled.
  311 if (!function->is_compiled()) {
  312 // If the function isn't compiled yet, the length is not computed
  313 // correctly yet. Compile it now and return the right length.
  314 HandleScope scope;
  315 Handle<JSFunction> function_handle(function);
  316 if (!CompileLazy(function_handle, KEEP_EXCEPTION)) {
  317 return Failure::Exception();
  318 }
  319 return Smi::FromInt(function_handle->shared()->length());
  320 } else {
  321 return Smi::FromInt(function->shared()->length());
  322 }
  323 }
  324
  325
  326 const AccessorDescriptor Accessors::FunctionLength = {
  327 FunctionGetLength,
  328 ReadOnlySetAccessor,
  329 0
  330 };
  331
  332
  333 //
  334 // Accessors::FunctionName
  335 //
  336
  337
  338 Object* Accessors::FunctionGetName(Object* object, void*) {
  339 bool found_it = false;
  340 JSFunction* holder = FindInPrototypeChain<JSFunction>(object, &found_it);
  341 if (!found_it) return Heap::undefined_value();
  342 return holder->shared()->name();
  343 }
  344
  345
  346 const AccessorDescriptor Accessors::FunctionName = {
  347 FunctionGetName,
  348 ReadOnlySetAccessor,
  349 0
  350 };
  351
  352
  353 //
  354 // Accessors::FunctionArguments
  355 //
  356
  357
  358 Object* Accessors::FunctionGetArguments(Object* object, void*) {
  359 HandleScope scope;
  360 bool found_it = false;
  361 JSFunction* holder = FindInPrototypeChain<JSFunction>(object, &found_it);
  362 if (!found_it) return Heap::undefined_value();
  363 Handle<JSFunction> function(holder);
  364
  365 // Find the top invocation of the function by traversing frames.
  366 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) {
  367 // Skip all frames that aren't invocations of the given function.
  368 JavaScriptFrame* frame = it.frame();
  369 if (frame->function() != *function) continue;
  370
  371 // If there is an arguments variable in the stack, we return that.
  372 int index = ScopeInfo<>::StackSlotIndex(frame->FindCode(),
  373 Heap::arguments_symbol());
  374 if (index >= 0) return frame->GetExpression(index);
  375
  376 // If there isn't an arguments variable in the stack, we need to
  377 // find the frame that holds the actual arguments passed to the
  378 // function on the stack.
  379 it.AdvanceToArgumentsFrame();
  380 frame = it.frame();
  381
  382 // Get the number of arguments and construct an arguments object
  383 // mirror for the right frame.
  384 const int length = frame->GetProvidedParametersCount();
  385 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
  386
  387 // Copy the parameters to the arguments object.
  388 FixedArray* array = FixedArray::cast(arguments->elements());
  389 ASSERT(array->length() == length);
  390 for (int i = 0; i < length; i++) array->set(i, frame->GetParameter(i));
  391
  392 // Return the freshly allocated arguments object.
  393 return *arguments;
  394 }
  395
  396 // No frame corresponding to the given function found. Return null.
  397 return Heap::null_value();
  398 }
  399
  400
  401 const AccessorDescriptor Accessors::FunctionArguments = {
  402 FunctionGetArguments,
  403 ReadOnlySetAccessor,
  404 0
  405 };
  406
  407
  408 //
  409 // Accessors::FunctionCaller
  410 //
  411
  412
  413 Object* Accessors::FunctionGetCaller(Object* object, void*) {
  414 HandleScope scope;
  415 bool found_it = false;
  416 JSFunction* holder = FindInPrototypeChain<JSFunction>(object, &found_it);
  417 if (!found_it) return Heap::undefined_value();
  418 Handle<JSFunction> function(holder);
  419
  420 // Find the top invocation of the function by traversing frames.
  421 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) {
  422 // Skip all frames that aren't invocations of the given function.
  423 if (it.frame()->function() != *function) continue;
  424 // Once we have found the frame, we need to go to the caller
  425 // frame. This may require skipping through a number of top-level
  426 // frames, e.g. frames for scripts not functions.
  427 while (true) {
  428 it.Advance();
  429 if (it.done()) return Heap::null_value();
  430 JSFunction* caller = JSFunction::cast(it.frame()->function());
  431 if (!caller->shared()->is_toplevel()) return caller;
  432 }
  433 }
  434
  435 // No frame corresponding to the given function found. Return null.
  436 return Heap::null_value();
  437 }
  438
  439
  440 const AccessorDescriptor Accessors::FunctionCaller = {
  441 FunctionGetCaller,
  442 ReadOnlySetAccessor,
  443 0
  444 };
  445
  446
  447 //
  448 // Accessors::ObjectPrototype
  449 //
  450
  451
  452 Object* Accessors::ObjectGetPrototype(Object* receiver, void*) {
  453 Object* current = receiver->GetPrototype();
  454 while (current->IsJSObject() &&
  455 JSObject::cast(current)->map()->is_hidden_prototype()) {
  456 current = current->GetPrototype();
  457 }
  458 return current;
  459 }
  460
  461
  462 Object* Accessors::ObjectSetPrototype(JSObject* receiver,
  463 Object* value,
  464 void*) {
  465 // Before we can set the prototype we need to be sure
  466 // prototype cycles are prevented.
  467 // It is suficient to validate the receiver is not in the new prototype chain.
  468
  469 // Silently ignore the change if value is not a JSObject or null.
  470 // SpiderMonkey behaves this way.
  471 if (!value->IsJSObject() && !value->IsNull()) return value;
  472
  473 for (Object* pt = value; pt != Heap::null_value(); pt = pt->GetPrototype()) {
  474 if (JSObject::cast(pt) == receiver) {
  475 // Cycle detected.
  476 HandleScope scope;
  477 return Top::Throw(*Factory::NewError("cyclic_proto",
  478 HandleVector<Object>(NULL, 0)));
  479 }
  480 }
  481
  482 // Find the first object in the chain whose prototype object is not
  483 // hidden and set the new prototype on that object.
  484 JSObject* current = receiver;
  485 Object* current_proto = receiver->GetPrototype();
  486 while (current_proto->IsJSObject() &&
  487 JSObject::cast(current_proto)->map()->is_hidden_prototype()) {
  488 current = JSObject::cast(current_proto);
  489 current_proto = current_proto->GetPrototype();
  490 }
  491
  492 // Set the new prototype of the object.
  493 Object* new_map = current->map()->Copy();
  494 if (new_map->IsFailure()) return new_map;
  495 Object* result = Map::cast(new_map)->EnsureNoMapTransitions();
  496 if (result->IsFailure()) return result;
  497 Map::cast(new_map)->set_prototype(value);
  498 current->set_map(Map::cast(new_map));
  499
  500 // To be consistant with other Set functions, return the value.
  501 return value;
  502 }
  503
  504
  505 const AccessorDescriptor Accessors::ObjectPrototype = {
  506 ObjectGetPrototype,
  507 ObjectSetPrototype,
  508 0
  509 };
  510
  511 } } // namespace v8::internal
Powered by Google Project Hosting