blob: 56cf135981a08828a1f847dfd5c4536bf58697de [file] [log] [blame]
Steve Blocka7e24c12009-10-30 11:49:00 +00001// 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);
Steve Blockd0582a62009-12-15 09:54:21 +0000318 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;
Steve Blocka7e24c12009-10-30 11:49:00 +0000323}
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//
Steve Blockd0582a62009-12-15 09:54:21 +0000352// Accessors::ScriptGetEvalFromScript
Steve Blocka7e24c12009-10-30 11:49:00 +0000353//
354
355
Steve Blockd0582a62009-12-15 09:54:21 +0000356Object* Accessors::ScriptGetEvalFromScript(Object* object, void*) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000357 Object* script = JSValue::cast(object)->value();
Steve Blockd0582a62009-12-15 09:54:21 +0000358 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();
Steve Blocka7e24c12009-10-30 11:49:00 +0000368}
369
370
Steve Blockd0582a62009-12-15 09:54:21 +0000371const AccessorDescriptor Accessors::ScriptEvalFromScript = {
372 ScriptGetEvalFromScript,
Steve Blocka7e24c12009-10-30 11:49:00 +0000373 IllegalSetter,
374 0
375};
376
377
378//
Steve Blockd0582a62009-12-15 09:54:21 +0000379// Accessors::ScriptGetEvalFromScriptPosition
Steve Blocka7e24c12009-10-30 11:49:00 +0000380//
381
382
Steve Blockd0582a62009-12-15 09:54:21 +0000383Object* Accessors::ScriptGetEvalFromScriptPosition(Object* object, void*) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000384 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.
Steve Blockd0582a62009-12-15 09:54:21 +0000395 Handle<Code> code(SharedFunctionInfo::cast(
396 script->eval_from_shared())->code());
Steve Blocka7e24c12009-10-30 11:49:00 +0000397 return Smi::FromInt(code->SourcePosition(code->instruction_start() +
398 script->eval_from_instructions_offset()->value()));
399}
400
401
Steve Blockd0582a62009-12-15 09:54:21 +0000402const 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,
Steve Blocka7e24c12009-10-30 11:49:00 +0000431 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<JSFunction> function_handle(function);
497 if (!CompileLazy(function_handle, KEEP_EXCEPTION)) {
498 return Failure::Exception();
499 }
500 return Smi::FromInt(function_handle->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