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