// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following // disclaimer in the documentation and/or other materials provided // with the distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "v8.h" #include "accessors.h" #include "compiler.h" #include "contexts.h" #include "deoptimizer.h" #include "execution.h" #include "factory.h" #include "frames-inl.h" #include "isolate.h" #include "list-inl.h" #include "property-details.h" namespace v8 { namespace internal { template static C* FindInstanceOf(Isolate* isolate, Object* obj) { for (Object* cur = obj; !cur->IsNull(); cur = cur->GetPrototype(isolate)) { if (Is(cur)) return C::cast(cur); } return NULL; } // Entry point that never should be called. MaybeObject* Accessors::IllegalSetter(Isolate* isolate, JSObject*, Object*, void*) { UNREACHABLE(); return NULL; } Object* Accessors::IllegalGetAccessor(Isolate* isolate, Object* object, void*) { UNREACHABLE(); return object; } MaybeObject* Accessors::ReadOnlySetAccessor(Isolate* isolate, JSObject*, Object* value, void*) { // According to ECMA-262, section 8.6.2.2, page 28, setting // read-only properties must be silently ignored. return value; } static V8_INLINE bool CheckForName(Handle name, String* property_name, int offset, int* object_offset) { if (name->Equals(property_name)) { *object_offset = offset; return true; } return false; } // Returns true for properties that are accessors to object fields. // If true, *object_offset contains offset of object field. template bool Accessors::IsJSObjectFieldAccessor(typename T::TypeHandle type, Handle name, int* object_offset) { Isolate* isolate = name->GetIsolate(); if (type->Is(T::String())) { return CheckForName(name, isolate->heap()->length_string(), String::kLengthOffset, object_offset); } if (!type->IsClass()) return false; Handle map = type->AsClass(); switch (map->instance_type()) { case JS_ARRAY_TYPE: return CheckForName(name, isolate->heap()->length_string(), JSArray::kLengthOffset, object_offset); case JS_TYPED_ARRAY_TYPE: return CheckForName(name, isolate->heap()->length_string(), JSTypedArray::kLengthOffset, object_offset) || CheckForName(name, isolate->heap()->byte_length_string(), JSTypedArray::kByteLengthOffset, object_offset) || CheckForName(name, isolate->heap()->byte_offset_string(), JSTypedArray::kByteOffsetOffset, object_offset) || CheckForName(name, isolate->heap()->buffer_string(), JSTypedArray::kBufferOffset, object_offset); case JS_ARRAY_BUFFER_TYPE: return CheckForName(name, isolate->heap()->byte_length_string(), JSArrayBuffer::kByteLengthOffset, object_offset); case JS_DATA_VIEW_TYPE: return CheckForName(name, isolate->heap()->byte_length_string(), JSDataView::kByteLengthOffset, object_offset) || CheckForName(name, isolate->heap()->byte_offset_string(), JSDataView::kByteOffsetOffset, object_offset) || CheckForName(name, isolate->heap()->buffer_string(), JSDataView::kBufferOffset, object_offset); default: return false; } } template bool Accessors::IsJSObjectFieldAccessor(Type* type, Handle name, int* object_offset); template bool Accessors::IsJSObjectFieldAccessor(Handle type, Handle name, int* object_offset); // // Accessors::ArrayLength // MaybeObject* Accessors::ArrayGetLength(Isolate* isolate, Object* object, void*) { // Traverse the prototype chain until we reach an array. JSArray* holder = FindInstanceOf(isolate, object); return holder == NULL ? Smi::FromInt(0) : holder->length(); } // The helper function will 'flatten' Number objects. Handle Accessors::FlattenNumber(Isolate* isolate, Handle value) { if (value->IsNumber() || !value->IsJSValue()) return value; Handle wrapper = Handle::cast(value); ASSERT(wrapper->GetIsolate()->context()->native_context()->number_function()-> has_initial_map()); if (wrapper->map() == isolate->context()->native_context()->number_function()->initial_map()) { return handle(wrapper->value(), isolate); } return value; } MaybeObject* Accessors::ArraySetLength(Isolate* isolate, JSObject* object_raw, Object* value_raw, void*) { HandleScope scope(isolate); Handle object(object_raw, isolate); Handle value(value_raw, isolate); // This means one of the object's prototypes is a JSArray and the // object does not have a 'length' property. Calling SetProperty // causes an infinite loop. if (!object->IsJSArray()) { Handle result = JSObject::SetLocalPropertyIgnoreAttributes(object, isolate->factory()->length_string(), value, NONE); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } value = FlattenNumber(isolate, value); Handle array_handle = Handle::cast(object); bool has_exception; Handle uint32_v = Execution::ToUint32(isolate, value, &has_exception); if (has_exception) return Failure::Exception(); Handle number_v = Execution::ToNumber(isolate, value, &has_exception); if (has_exception) return Failure::Exception(); if (uint32_v->Number() == number_v->Number()) { Handle result = JSArray::SetElementsLength(array_handle, uint32_v); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } return isolate->Throw( *isolate->factory()->NewRangeError("invalid_array_length", HandleVector(NULL, 0))); } const AccessorDescriptor Accessors::ArrayLength = { ArrayGetLength, ArraySetLength, 0 }; // // Accessors::StringLength // MaybeObject* Accessors::StringGetLength(Isolate* isolate, Object* object, void*) { Object* value = object; if (object->IsJSValue()) value = JSValue::cast(object)->value(); if (value->IsString()) return Smi::FromInt(String::cast(value)->length()); // If object is not a string we return 0 to be compatible with WebKit. // Note: Firefox returns the length of ToString(object). return Smi::FromInt(0); } const AccessorDescriptor Accessors::StringLength = { StringGetLength, IllegalSetter, 0 }; // // Accessors::ScriptSource // MaybeObject* Accessors::ScriptGetSource(Isolate* isolate, Object* object, void*) { Object* script = JSValue::cast(object)->value(); return Script::cast(script)->source(); } const AccessorDescriptor Accessors::ScriptSource = { ScriptGetSource, IllegalSetter, 0 }; // // Accessors::ScriptName // MaybeObject* Accessors::ScriptGetName(Isolate* isolate, Object* object, void*) { Object* script = JSValue::cast(object)->value(); return Script::cast(script)->name(); } const AccessorDescriptor Accessors::ScriptName = { ScriptGetName, IllegalSetter, 0 }; // // Accessors::ScriptId // MaybeObject* Accessors::ScriptGetId(Isolate* isolate, Object* object, void*) { Object* script = JSValue::cast(object)->value(); return Script::cast(script)->id(); } const AccessorDescriptor Accessors::ScriptId = { ScriptGetId, IllegalSetter, 0 }; // // Accessors::ScriptLineOffset // MaybeObject* Accessors::ScriptGetLineOffset(Isolate* isolate, Object* object, void*) { Object* script = JSValue::cast(object)->value(); return Script::cast(script)->line_offset(); } const AccessorDescriptor Accessors::ScriptLineOffset = { ScriptGetLineOffset, IllegalSetter, 0 }; // // Accessors::ScriptColumnOffset // MaybeObject* Accessors::ScriptGetColumnOffset(Isolate* isolate, Object* object, void*) { Object* script = JSValue::cast(object)->value(); return Script::cast(script)->column_offset(); } const AccessorDescriptor Accessors::ScriptColumnOffset = { ScriptGetColumnOffset, IllegalSetter, 0 }; // // Accessors::ScriptType // MaybeObject* Accessors::ScriptGetType(Isolate* isolate, Object* object, void*) { Object* script = JSValue::cast(object)->value(); return Script::cast(script)->type(); } const AccessorDescriptor Accessors::ScriptType = { ScriptGetType, IllegalSetter, 0 }; // // Accessors::ScriptCompilationType // MaybeObject* Accessors::ScriptGetCompilationType(Isolate* isolate, Object* object, void*) { Object* script = JSValue::cast(object)->value(); return Smi::FromInt(Script::cast(script)->compilation_type()); } const AccessorDescriptor Accessors::ScriptCompilationType = { ScriptGetCompilationType, IllegalSetter, 0 }; // // Accessors::ScriptGetLineEnds // MaybeObject* Accessors::ScriptGetLineEnds(Isolate* isolate, Object* object, void*) { JSValue* wrapper = JSValue::cast(object); HandleScope scope(isolate); Handle