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 return script->line_ends();
319 }
320
321
322 const AccessorDescriptor Accessors::ScriptLineEnds = {
323 ScriptGetLineEnds,
324 IllegalSetter,
325 0
326 };
327
328
329 //
330 // Accessors::ScriptGetContextData
331 //
332
333
ScriptGetContextData(Object * object,void *)334 Object* Accessors::ScriptGetContextData(Object* object, void*) {
335 Object* script = JSValue::cast(object)->value();
336 return Script::cast(script)->context_data();
337 }
338
339
340 const AccessorDescriptor Accessors::ScriptContextData = {
341 ScriptGetContextData,
342 IllegalSetter,
343 0
344 };
345
346
347 //
348 // Accessors::ScriptGetEvalFromFunction
349 //
350
351
ScriptGetEvalFromFunction(Object * object,void *)352 Object* Accessors::ScriptGetEvalFromFunction(Object* object, void*) {
353 Object* script = JSValue::cast(object)->value();
354 return Script::cast(script)->eval_from_function();
355 }
356
357
358 const AccessorDescriptor Accessors::ScriptEvalFromFunction = {
359 ScriptGetEvalFromFunction,
360 IllegalSetter,
361 0
362 };
363
364
365 //
366 // Accessors::ScriptGetEvalFromPosition
367 //
368
369
ScriptGetEvalFromPosition(Object * object,void *)370 Object* Accessors::ScriptGetEvalFromPosition(Object* object, void*) {
371 HandleScope scope;
372 Handle<Script> script(Script::cast(JSValue::cast(object)->value()));
373
374 // If this is not a script compiled through eval there is no eval position.
375 int compilation_type = Smi::cast(script->compilation_type())->value();
376 if (compilation_type != Script::COMPILATION_TYPE_EVAL) {
377 return Heap::undefined_value();
378 }
379
380 // Get the function from where eval was called and find the source position
381 // from the instruction offset.
382 Handle<Code> code(JSFunction::cast(script->eval_from_function())->code());
383 return Smi::FromInt(code->SourcePosition(code->instruction_start() +
384 script->eval_from_instructions_offset()->value()));
385 }
386
387
388 const AccessorDescriptor Accessors::ScriptEvalFromPosition = {
389 ScriptGetEvalFromPosition,
390 IllegalSetter,
391 0
392 };
393
394
395 //
396 // Accessors::FunctionPrototype
397 //
398
399
FunctionGetPrototype(Object * object,void *)400 Object* Accessors::FunctionGetPrototype(Object* object, void*) {
401 bool found_it = false;
402 JSFunction* function = FindInPrototypeChain<JSFunction>(object, &found_it);
403 if (!found_it) return Heap::undefined_value();
404 if (!function->has_prototype()) {
405 Object* prototype = Heap::AllocateFunctionPrototype(function);
406 if (prototype->IsFailure()) return prototype;
407 Object* result = function->SetPrototype(prototype);
408 if (result->IsFailure()) return result;
409 }
410 return function->prototype();
411 }
412
413
FunctionSetPrototype(JSObject * object,Object * value,void *)414 Object* Accessors::FunctionSetPrototype(JSObject* object,
415 Object* value,
416 void*) {
417 bool found_it = false;
418 JSFunction* function = FindInPrototypeChain<JSFunction>(object, &found_it);
419 if (!found_it) return Heap::undefined_value();
420 if (function->has_initial_map()) {
421 // If the function has allocated the initial map
422 // replace it with a copy containing the new prototype.
423 Object* new_map = function->initial_map()->CopyDropTransitions();
424 if (new_map->IsFailure()) return new_map;
425 function->set_initial_map(Map::cast(new_map));
426 }
427 Object* prototype = function->SetPrototype(value);
428 if (prototype->IsFailure()) return prototype;
429 ASSERT(function->prototype() == value);
430 return function;
431 }
432
433
434 const AccessorDescriptor Accessors::FunctionPrototype = {
435 FunctionGetPrototype,
436 FunctionSetPrototype,
437 0
438 };
439
440
441 //
442 // Accessors::FunctionLength
443 //
444
445
FunctionGetLength(Object * object,void *)446 Object* Accessors::FunctionGetLength(Object* object, void*) {
447 bool found_it = false;
448 JSFunction* function = FindInPrototypeChain<JSFunction>(object, &found_it);
449 if (!found_it) return Smi::FromInt(0);
450 // Check if already compiled.
451 if (!function->is_compiled()) {
452 // If the function isn't compiled yet, the length is not computed
453 // correctly yet. Compile it now and return the right length.
454 HandleScope scope;
455 Handle<JSFunction> function_handle(function);
456 if (!CompileLazy(function_handle, KEEP_EXCEPTION)) {
457 return Failure::Exception();
458 }
459 return Smi::FromInt(function_handle->shared()->length());
460 } else {
461 return Smi::FromInt(function->shared()->length());
462 }
463 }
464
465
466 const AccessorDescriptor Accessors::FunctionLength = {
467 FunctionGetLength,
468 ReadOnlySetAccessor,
469 0
470 };
471
472
473 //
474 // Accessors::FunctionName
475 //
476
477
FunctionGetName(Object * object,void *)478 Object* Accessors::FunctionGetName(Object* object, void*) {
479 bool found_it = false;
480 JSFunction* holder = FindInPrototypeChain<JSFunction>(object, &found_it);
481 if (!found_it) return Heap::undefined_value();
482 return holder->shared()->name();
483 }
484
485
486 const AccessorDescriptor Accessors::FunctionName = {
487 FunctionGetName,
488 ReadOnlySetAccessor,
489 0
490 };
491
492
493 //
494 // Accessors::FunctionArguments
495 //
496
497
FunctionGetArguments(Object * object,void *)498 Object* Accessors::FunctionGetArguments(Object* object, void*) {
499 HandleScope scope;
500 bool found_it = false;
501 JSFunction* holder = FindInPrototypeChain<JSFunction>(object, &found_it);
502 if (!found_it) return Heap::undefined_value();
503 Handle<JSFunction> function(holder);
504
505 // Find the top invocation of the function by traversing frames.
506 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) {
507 // Skip all frames that aren't invocations of the given function.
508 JavaScriptFrame* frame = it.frame();
509 if (frame->function() != *function) continue;
510
511 // If there is an arguments variable in the stack, we return that.
512 int index = ScopeInfo<>::StackSlotIndex(frame->code(),
513 Heap::arguments_symbol());
514 if (index >= 0) {
515 Handle<Object> arguments = Handle<Object>(frame->GetExpression(index));
516 if (!arguments->IsTheHole()) return *arguments;
517 }
518
519 // If there isn't an arguments variable in the stack, we need to
520 // find the frame that holds the actual arguments passed to the
521 // function on the stack.
522 it.AdvanceToArgumentsFrame();
523 frame = it.frame();
524
525 // Get the number of arguments and construct an arguments object
526 // mirror for the right frame.
527 const int length = frame->GetProvidedParametersCount();
528 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
529 Handle<FixedArray> array = Factory::NewFixedArray(length);
530
531 // Copy the parameters to the arguments object.
532 ASSERT(array->length() == length);
533 for (int i = 0; i < length; i++) array->set(i, frame->GetParameter(i));
534 arguments->set_elements(*array);
535
536 // Return the freshly allocated arguments object.
537 return *arguments;
538 }
539
540 // No frame corresponding to the given function found. Return null.
541 return Heap::null_value();
542 }
543
544
545 const AccessorDescriptor Accessors::FunctionArguments = {
546 FunctionGetArguments,
547 ReadOnlySetAccessor,
548 0
549 };
550
551
552 //
553 // Accessors::FunctionCaller
554 //
555
556
FunctionGetCaller(Object * object,void *)557 Object* Accessors::FunctionGetCaller(Object* object, void*) {
558 HandleScope scope;
559 bool found_it = false;
560 JSFunction* holder = FindInPrototypeChain<JSFunction>(object, &found_it);
561 if (!found_it) return Heap::undefined_value();
562 Handle<JSFunction> function(holder);
563
564 // Find the top invocation of the function by traversing frames.
565 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) {
566 // Skip all frames that aren't invocations of the given function.
567 if (it.frame()->function() != *function) continue;
568 // Once we have found the frame, we need to go to the caller
569 // frame. This may require skipping through a number of top-level
570 // frames, e.g. frames for scripts not functions.
571 while (true) {
572 it.Advance();
573 if (it.done()) return Heap::null_value();
574 JSFunction* caller = JSFunction::cast(it.frame()->function());
575 if (!caller->shared()->is_toplevel()) return caller;
576 }
577 }
578
579 // No frame corresponding to the given function found. Return null.
580 return Heap::null_value();
581 }
582
583
584 const AccessorDescriptor Accessors::FunctionCaller = {
585 FunctionGetCaller,
586 ReadOnlySetAccessor,
587 0
588 };
589
590
591 //
592 // Accessors::ObjectPrototype
593 //
594
595
ObjectGetPrototype(Object * receiver,void *)596 Object* Accessors::ObjectGetPrototype(Object* receiver, void*) {
597 Object* current = receiver->GetPrototype();
598 while (current->IsJSObject() &&
599 JSObject::cast(current)->map()->is_hidden_prototype()) {
600 current = current->GetPrototype();
601 }
602 return current;
603 }
604
605
ObjectSetPrototype(JSObject * receiver,Object * value,void *)606 Object* Accessors::ObjectSetPrototype(JSObject* receiver,
607 Object* value,
608 void*) {
609 // Before we can set the prototype we need to be sure
610 // prototype cycles are prevented.
611 // It is sufficient to validate that the receiver is not in the new prototype
612 // chain.
613
614 // Silently ignore the change if value is not a JSObject or null.
615 // SpiderMonkey behaves this way.
616 if (!value->IsJSObject() && !value->IsNull()) return value;
617
618 for (Object* pt = value; pt != Heap::null_value(); pt = pt->GetPrototype()) {
619 if (JSObject::cast(pt) == receiver) {
620 // Cycle detected.
621 HandleScope scope;
622 return Top::Throw(*Factory::NewError("cyclic_proto",
623 HandleVector<Object>(NULL, 0)));
624 }
625 }
626
627 // Find the first object in the chain whose prototype object is not
628 // hidden and set the new prototype on that object.
629 JSObject* current = receiver;
630 Object* current_proto = receiver->GetPrototype();
631 while (current_proto->IsJSObject() &&
632 JSObject::cast(current_proto)->map()->is_hidden_prototype()) {
633 current = JSObject::cast(current_proto);
634 current_proto = current_proto->GetPrototype();
635 }
636
637 // Set the new prototype of the object.
638 Object* new_map = current->map()->CopyDropTransitions();
639 if (new_map->IsFailure()) return new_map;
640 Map::cast(new_map)->set_prototype(value);
641 current->set_map(Map::cast(new_map));
642
643 // To be consistent with other Set functions, return the value.
644 return value;
645 }
646
647
648 const AccessorDescriptor Accessors::ObjectPrototype = {
649 ObjectGetPrototype,
650 ObjectSetPrototype,
651 0
652 };
653
654 } } // namespace v8::internal
655