• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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>
FindInPrototypeChain(Object * obj,bool * found_it)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.
IllegalSetter(JSObject *,Object *,void *)54 Object* Accessors::IllegalSetter(JSObject*, Object*, void*) {
55   UNREACHABLE();
56   return NULL;
57 }
58 
59 
IllegalGetAccessor(Object * object,void *)60 Object* Accessors::IllegalGetAccessor(Object* object, void*) {
61   UNREACHABLE();
62   return object;
63 }
64 
65 
ReadOnlySetAccessor(JSObject *,Object * value,void *)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 
ArrayGetLength(Object * object,void *)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.
FlattenNumber(Object * value)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 
ArraySetLength(JSObject * object,Object * value,void *)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 
StringGetLength(Object * object,void *)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 
ScriptGetSource(Object * object,void *)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 
ScriptGetName(Object * object,void *)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 
ScriptGetId(Object * object,void *)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 
ScriptGetLineOffset(Object * object,void *)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 
ScriptGetColumnOffset(Object * object,void *)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 
ScriptGetData(Object * object,void *)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 
ScriptGetType(Object * object,void *)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 
ScriptGetCompilationType(Object * object,void *)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 
ScriptGetLineEnds(Object * object,void *)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 
ScriptGetContextData(Object * object,void *)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 
ScriptGetEvalFromScript(Object * object,void *)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 
ScriptGetEvalFromScriptPosition(Object * object,void *)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 
ScriptGetEvalFromFunctionName(Object * object,void *)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 
FunctionGetPrototype(Object * object,void *)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 
FunctionSetPrototype(JSObject * object,Object * value,void *)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 
FunctionGetLength(Object * object,void *)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 
FunctionGetName(Object * object,void *)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 
FunctionGetArguments(Object * object,void *)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 
FunctionGetCaller(Object * object,void *)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 
ObjectGetPrototype(Object * receiver,void *)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 
ObjectSetPrototype(JSObject * receiver,Object * value,void *)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