blob: ed0bbd7a1ca595bd7a24d1a0b75e54403b49cae0 [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"
Steve Blocka7e24c12009-10-30 11:49:00 +000035
36namespace v8 {
37namespace internal {
38
39
40template <class C>
41static 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.
53Object* Accessors::IllegalSetter(JSObject*, Object*, void*) {
54 UNREACHABLE();
55 return NULL;
56}
57
58
59Object* Accessors::IllegalGetAccessor(Object* object, void*) {
60 UNREACHABLE();
61 return object;
62}
63
64
65Object* 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
77Object* 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.
87Object* 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
99Object* 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 // Calling SetProperty causes an infinite loop.
126 return object->IgnoreAttributesAndSetLocalProperty(Heap::length_symbol(),
127 value, NONE);
128 }
129 }
130 return Top::Throw(*Factory::NewRangeError("invalid_array_length",
131 HandleVector<Object>(NULL, 0)));
132}
133
134
135const AccessorDescriptor Accessors::ArrayLength = {
136 ArrayGetLength,
137 ArraySetLength,
138 0
139};
140
141
142//
143// Accessors::StringLength
144//
145
146
147Object* Accessors::StringGetLength(Object* object, void*) {
148 Object* value = object;
149 if (object->IsJSValue()) value = JSValue::cast(object)->value();
150 if (value->IsString()) return Smi::FromInt(String::cast(value)->length());
151 // If object is not a string we return 0 to be compatible with WebKit.
152 // Note: Firefox returns the length of ToString(object).
153 return Smi::FromInt(0);
154}
155
156
157const AccessorDescriptor Accessors::StringLength = {
158 StringGetLength,
159 IllegalSetter,
160 0
161};
162
163
164//
165// Accessors::ScriptSource
166//
167
168
169Object* Accessors::ScriptGetSource(Object* object, void*) {
170 Object* script = JSValue::cast(object)->value();
171 return Script::cast(script)->source();
172}
173
174
175const AccessorDescriptor Accessors::ScriptSource = {
176 ScriptGetSource,
177 IllegalSetter,
178 0
179};
180
181
182//
183// Accessors::ScriptName
184//
185
186
187Object* Accessors::ScriptGetName(Object* object, void*) {
188 Object* script = JSValue::cast(object)->value();
189 return Script::cast(script)->name();
190}
191
192
193const AccessorDescriptor Accessors::ScriptName = {
194 ScriptGetName,
195 IllegalSetter,
196 0
197};
198
199
200//
201// Accessors::ScriptId
202//
203
204
205Object* Accessors::ScriptGetId(Object* object, void*) {
206 Object* script = JSValue::cast(object)->value();
207 return Script::cast(script)->id();
208}
209
210
211const AccessorDescriptor Accessors::ScriptId = {
212 ScriptGetId,
213 IllegalSetter,
214 0
215};
216
217
218//
219// Accessors::ScriptLineOffset
220//
221
222
223Object* Accessors::ScriptGetLineOffset(Object* object, void*) {
224 Object* script = JSValue::cast(object)->value();
225 return Script::cast(script)->line_offset();
226}
227
228
229const AccessorDescriptor Accessors::ScriptLineOffset = {
230 ScriptGetLineOffset,
231 IllegalSetter,
232 0
233};
234
235
236//
237// Accessors::ScriptColumnOffset
238//
239
240
241Object* Accessors::ScriptGetColumnOffset(Object* object, void*) {
242 Object* script = JSValue::cast(object)->value();
243 return Script::cast(script)->column_offset();
244}
245
246
247const AccessorDescriptor Accessors::ScriptColumnOffset = {
248 ScriptGetColumnOffset,
249 IllegalSetter,
250 0
251};
252
253
254//
255// Accessors::ScriptData
256//
257
258
259Object* Accessors::ScriptGetData(Object* object, void*) {
260 Object* script = JSValue::cast(object)->value();
261 return Script::cast(script)->data();
262}
263
264
265const AccessorDescriptor Accessors::ScriptData = {
266 ScriptGetData,
267 IllegalSetter,
268 0
269};
270
271
272//
273// Accessors::ScriptType
274//
275
276
277Object* Accessors::ScriptGetType(Object* object, void*) {
278 Object* script = JSValue::cast(object)->value();
279 return Script::cast(script)->type();
280}
281
282
283const AccessorDescriptor Accessors::ScriptType = {
284 ScriptGetType,
285 IllegalSetter,
286 0
287};
288
289
290//
291// Accessors::ScriptCompilationType
292//
293
294
295Object* Accessors::ScriptGetCompilationType(Object* object, void*) {
296 Object* script = JSValue::cast(object)->value();
297 return Script::cast(script)->compilation_type();
298}
299
300
301const AccessorDescriptor Accessors::ScriptCompilationType = {
302 ScriptGetCompilationType,
303 IllegalSetter,
304 0
305};
306
307
308//
309// Accessors::ScriptGetLineEnds
310//
311
312
313Object* Accessors::ScriptGetLineEnds(Object* object, void*) {
314 HandleScope scope;
315 Handle<Script> script(Script::cast(JSValue::cast(object)->value()));
316 InitScriptLineEnds(script);
Steve Blockd0582a62009-12-15 09:54:21 +0000317 ASSERT(script->line_ends()->IsFixedArray());
318 Handle<FixedArray> line_ends(FixedArray::cast(script->line_ends()));
319 Handle<FixedArray> copy = Factory::CopyFixedArray(line_ends);
320 Handle<JSArray> js_array = Factory::NewJSArrayWithElements(copy);
321 return *js_array;
Steve Blocka7e24c12009-10-30 11:49:00 +0000322}
323
324
325const AccessorDescriptor Accessors::ScriptLineEnds = {
326 ScriptGetLineEnds,
327 IllegalSetter,
328 0
329};
330
331
332//
333// Accessors::ScriptGetContextData
334//
335
336
337Object* Accessors::ScriptGetContextData(Object* object, void*) {
338 Object* script = JSValue::cast(object)->value();
339 return Script::cast(script)->context_data();
340}
341
342
343const AccessorDescriptor Accessors::ScriptContextData = {
344 ScriptGetContextData,
345 IllegalSetter,
346 0
347};
348
349
350//
Steve Blockd0582a62009-12-15 09:54:21 +0000351// Accessors::ScriptGetEvalFromScript
Steve Blocka7e24c12009-10-30 11:49:00 +0000352//
353
354
Steve Blockd0582a62009-12-15 09:54:21 +0000355Object* Accessors::ScriptGetEvalFromScript(Object* object, void*) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000356 Object* script = JSValue::cast(object)->value();
Steve Blockd0582a62009-12-15 09:54:21 +0000357 if (!Script::cast(script)->eval_from_shared()->IsUndefined()) {
358 Handle<SharedFunctionInfo> eval_from_shared(
359 SharedFunctionInfo::cast(Script::cast(script)->eval_from_shared()));
360
361 if (eval_from_shared->script()->IsScript()) {
362 Handle<Script> eval_from_script(Script::cast(eval_from_shared->script()));
363 return *GetScriptWrapper(eval_from_script);
364 }
365 }
366 return Heap::undefined_value();
Steve Blocka7e24c12009-10-30 11:49:00 +0000367}
368
369
Steve Blockd0582a62009-12-15 09:54:21 +0000370const AccessorDescriptor Accessors::ScriptEvalFromScript = {
371 ScriptGetEvalFromScript,
Steve Blocka7e24c12009-10-30 11:49:00 +0000372 IllegalSetter,
373 0
374};
375
376
377//
Steve Blockd0582a62009-12-15 09:54:21 +0000378// Accessors::ScriptGetEvalFromScriptPosition
Steve Blocka7e24c12009-10-30 11:49:00 +0000379//
380
381
Steve Blockd0582a62009-12-15 09:54:21 +0000382Object* Accessors::ScriptGetEvalFromScriptPosition(Object* object, void*) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000383 HandleScope scope;
384 Handle<Script> script(Script::cast(JSValue::cast(object)->value()));
385
386 // If this is not a script compiled through eval there is no eval position.
387 int compilation_type = Smi::cast(script->compilation_type())->value();
388 if (compilation_type != Script::COMPILATION_TYPE_EVAL) {
389 return Heap::undefined_value();
390 }
391
392 // Get the function from where eval was called and find the source position
393 // from the instruction offset.
Steve Blockd0582a62009-12-15 09:54:21 +0000394 Handle<Code> code(SharedFunctionInfo::cast(
395 script->eval_from_shared())->code());
Steve Blocka7e24c12009-10-30 11:49:00 +0000396 return Smi::FromInt(code->SourcePosition(code->instruction_start() +
397 script->eval_from_instructions_offset()->value()));
398}
399
400
Steve Blockd0582a62009-12-15 09:54:21 +0000401const AccessorDescriptor Accessors::ScriptEvalFromScriptPosition = {
402 ScriptGetEvalFromScriptPosition,
403 IllegalSetter,
404 0
405};
406
407
408//
409// Accessors::ScriptGetEvalFromFunctionName
410//
411
412
413Object* Accessors::ScriptGetEvalFromFunctionName(Object* object, void*) {
414 Object* script = JSValue::cast(object)->value();
415 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(
416 Script::cast(script)->eval_from_shared()));
417
418
419 // Find the name of the function calling eval.
420 if (!shared->name()->IsUndefined()) {
421 return shared->name();
422 } else {
423 return shared->inferred_name();
424 }
425}
426
427
428const AccessorDescriptor Accessors::ScriptEvalFromFunctionName = {
429 ScriptGetEvalFromFunctionName,
Steve Blocka7e24c12009-10-30 11:49:00 +0000430 IllegalSetter,
431 0
432};
433
434
435//
436// Accessors::FunctionPrototype
437//
438
439
440Object* Accessors::FunctionGetPrototype(Object* object, void*) {
441 bool found_it = false;
442 JSFunction* function = FindInPrototypeChain<JSFunction>(object, &found_it);
443 if (!found_it) return Heap::undefined_value();
444 if (!function->has_prototype()) {
445 Object* prototype = Heap::AllocateFunctionPrototype(function);
446 if (prototype->IsFailure()) return prototype;
447 Object* result = function->SetPrototype(prototype);
448 if (result->IsFailure()) return result;
449 }
450 return function->prototype();
451}
452
453
454Object* Accessors::FunctionSetPrototype(JSObject* object,
455 Object* value,
456 void*) {
457 bool found_it = false;
458 JSFunction* function = FindInPrototypeChain<JSFunction>(object, &found_it);
459 if (!found_it) return Heap::undefined_value();
460 if (function->has_initial_map()) {
461 // If the function has allocated the initial map
462 // replace it with a copy containing the new prototype.
463 Object* new_map = function->initial_map()->CopyDropTransitions();
464 if (new_map->IsFailure()) return new_map;
465 function->set_initial_map(Map::cast(new_map));
466 }
467 Object* prototype = function->SetPrototype(value);
468 if (prototype->IsFailure()) return prototype;
469 ASSERT(function->prototype() == value);
470 return function;
471}
472
473
474const AccessorDescriptor Accessors::FunctionPrototype = {
475 FunctionGetPrototype,
476 FunctionSetPrototype,
477 0
478};
479
480
481//
482// Accessors::FunctionLength
483//
484
485
486Object* Accessors::FunctionGetLength(Object* object, void*) {
487 bool found_it = false;
488 JSFunction* function = FindInPrototypeChain<JSFunction>(object, &found_it);
489 if (!found_it) return Smi::FromInt(0);
490 // Check if already compiled.
491 if (!function->is_compiled()) {
492 // If the function isn't compiled yet, the length is not computed
493 // correctly yet. Compile it now and return the right length.
494 HandleScope scope;
Leon Clarke4515c472010-02-03 11:58:03 +0000495 Handle<SharedFunctionInfo> shared(function->shared());
496 if (!CompileLazyShared(shared, KEEP_EXCEPTION)) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000497 return Failure::Exception();
498 }
Leon Clarke4515c472010-02-03 11:58:03 +0000499 return Smi::FromInt(shared->length());
Steve Blocka7e24c12009-10-30 11:49:00 +0000500 } else {
501 return Smi::FromInt(function->shared()->length());
502 }
503}
504
505
506const AccessorDescriptor Accessors::FunctionLength = {
507 FunctionGetLength,
508 ReadOnlySetAccessor,
509 0
510};
511
512
513//
514// Accessors::FunctionName
515//
516
517
518Object* Accessors::FunctionGetName(Object* object, void*) {
519 bool found_it = false;
520 JSFunction* holder = FindInPrototypeChain<JSFunction>(object, &found_it);
521 if (!found_it) return Heap::undefined_value();
522 return holder->shared()->name();
523}
524
525
526const AccessorDescriptor Accessors::FunctionName = {
527 FunctionGetName,
528 ReadOnlySetAccessor,
529 0
530};
531
532
533//
534// Accessors::FunctionArguments
535//
536
537
538Object* Accessors::FunctionGetArguments(Object* object, void*) {
539 HandleScope scope;
540 bool found_it = false;
541 JSFunction* holder = FindInPrototypeChain<JSFunction>(object, &found_it);
542 if (!found_it) return Heap::undefined_value();
543 Handle<JSFunction> function(holder);
544
545 // Find the top invocation of the function by traversing frames.
546 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) {
547 // Skip all frames that aren't invocations of the given function.
548 JavaScriptFrame* frame = it.frame();
549 if (frame->function() != *function) continue;
550
551 // If there is an arguments variable in the stack, we return that.
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100552 int index = function->shared()->scope_info()->
553 StackSlotIndex(Heap::arguments_symbol());
Steve Blocka7e24c12009-10-30 11:49:00 +0000554 if (index >= 0) {
555 Handle<Object> arguments = Handle<Object>(frame->GetExpression(index));
556 if (!arguments->IsTheHole()) return *arguments;
557 }
558
559 // If there isn't an arguments variable in the stack, we need to
560 // find the frame that holds the actual arguments passed to the
561 // function on the stack.
562 it.AdvanceToArgumentsFrame();
563 frame = it.frame();
564
565 // Get the number of arguments and construct an arguments object
566 // mirror for the right frame.
567 const int length = frame->GetProvidedParametersCount();
568 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
569 Handle<FixedArray> array = Factory::NewFixedArray(length);
570
571 // Copy the parameters to the arguments object.
572 ASSERT(array->length() == length);
573 for (int i = 0; i < length; i++) array->set(i, frame->GetParameter(i));
574 arguments->set_elements(*array);
575
576 // Return the freshly allocated arguments object.
577 return *arguments;
578 }
579
580 // No frame corresponding to the given function found. Return null.
581 return Heap::null_value();
582}
583
584
585const AccessorDescriptor Accessors::FunctionArguments = {
586 FunctionGetArguments,
587 ReadOnlySetAccessor,
588 0
589};
590
591
592//
593// Accessors::FunctionCaller
594//
595
596
597Object* Accessors::FunctionGetCaller(Object* object, void*) {
598 HandleScope scope;
599 bool found_it = false;
600 JSFunction* holder = FindInPrototypeChain<JSFunction>(object, &found_it);
601 if (!found_it) return Heap::undefined_value();
602 Handle<JSFunction> function(holder);
603
604 // Find the top invocation of the function by traversing frames.
605 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) {
606 // Skip all frames that aren't invocations of the given function.
607 if (it.frame()->function() != *function) continue;
608 // Once we have found the frame, we need to go to the caller
609 // frame. This may require skipping through a number of top-level
610 // frames, e.g. frames for scripts not functions.
611 while (true) {
612 it.Advance();
613 if (it.done()) return Heap::null_value();
614 JSFunction* caller = JSFunction::cast(it.frame()->function());
615 if (!caller->shared()->is_toplevel()) return caller;
616 }
617 }
618
619 // No frame corresponding to the given function found. Return null.
620 return Heap::null_value();
621}
622
623
624const AccessorDescriptor Accessors::FunctionCaller = {
625 FunctionGetCaller,
626 ReadOnlySetAccessor,
627 0
628};
629
630
631//
632// Accessors::ObjectPrototype
633//
634
635
636Object* Accessors::ObjectGetPrototype(Object* receiver, void*) {
637 Object* current = receiver->GetPrototype();
638 while (current->IsJSObject() &&
639 JSObject::cast(current)->map()->is_hidden_prototype()) {
640 current = current->GetPrototype();
641 }
642 return current;
643}
644
645
646Object* Accessors::ObjectSetPrototype(JSObject* receiver,
647 Object* value,
648 void*) {
Andrei Popescu402d9372010-02-26 13:31:12 +0000649 const bool skip_hidden_prototypes = true;
Steve Blocka7e24c12009-10-30 11:49:00 +0000650 // To be consistent with other Set functions, return the value.
Andrei Popescu402d9372010-02-26 13:31:12 +0000651 return receiver->SetPrototype(value, skip_hidden_prototypes);
Steve Blocka7e24c12009-10-30 11:49:00 +0000652}
653
654
655const AccessorDescriptor Accessors::ObjectPrototype = {
656 ObjectGetPrototype,
657 ObjectSetPrototype,
658 0
659};
660
661} } // namespace v8::internal