2020-03-07 19:42:11 +01:00
/*
* Copyright ( c ) 2020 , Andreas Kling < kling @ serenityos . org >
* All rights reserved .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions are met :
*
* 1. Redistributions of source code must retain the above copyright notice , this
* list of conditions and the following disclaimer .
*
* 2. 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 .
*
* 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 HOLDER 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 <AK/String.h>
2020-03-16 14:20:30 +01:00
# include <LibJS/Heap/Heap.h>
2020-03-15 15:25:43 +01:00
# include <LibJS/Interpreter.h>
2020-03-20 20:51:59 +01:00
# include <LibJS/Runtime/Array.h>
2020-04-09 22:15:26 +02:00
# include <LibJS/Runtime/Error.h>
2020-04-08 11:05:38 +02:00
# include <LibJS/Runtime/GlobalObject.h>
2020-03-16 14:20:30 +01:00
# include <LibJS/Runtime/NativeFunction.h>
# include <LibJS/Runtime/NativeProperty.h>
# include <LibJS/Runtime/Object.h>
2020-04-02 19:32:21 +02:00
# include <LibJS/Runtime/Shape.h>
2020-04-28 19:19:31 -07:00
# include <LibJS/Runtime/StringObject.h>
2020-03-16 14:20:30 +01:00
# include <LibJS/Runtime/Value.h>
2020-03-07 19:42:11 +01:00
namespace JS {
2020-04-18 13:18:06 +02:00
Object * Object : : create_empty ( Interpreter & , GlobalObject & global_object )
2020-04-18 10:27:57 +02:00
{
2020-04-18 13:18:06 +02:00
return global_object . heap ( ) . allocate < Object > ( global_object . object_prototype ( ) ) ;
2020-04-18 10:27:57 +02:00
}
Object : : Object ( Object * prototype )
2020-03-08 19:23:58 +01:00
{
2020-04-18 13:56:13 +02:00
if ( prototype ) {
m_shape = interpreter ( ) . global_object ( ) . empty_object_shape ( ) ;
set_prototype ( prototype ) ;
} else {
m_shape = interpreter ( ) . heap ( ) . allocate < Shape > ( ) ;
}
2020-03-08 19:23:58 +01:00
}
Object : : ~ Object ( )
{
}
2020-04-02 19:32:21 +02:00
Object * Object : : prototype ( )
{
return shape ( ) . prototype ( ) ;
}
const Object * Object : : prototype ( ) const
{
return shape ( ) . prototype ( ) ;
}
void Object : : set_prototype ( Object * new_prototype )
{
2020-04-18 10:27:57 +02:00
if ( prototype ( ) = = new_prototype )
return ;
2020-04-26 13:53:40 +02:00
if ( shape ( ) . is_unique ( ) ) {
shape ( ) . set_prototype_without_transition ( new_prototype ) ;
return ;
}
2020-04-02 19:32:21 +02:00
m_shape = m_shape - > create_prototype_transition ( new_prototype ) ;
}
2020-03-28 19:48:12 +01:00
bool Object : : has_prototype ( const Object * prototype ) const
{
2020-04-02 19:32:21 +02:00
for ( auto * object = this - > prototype ( ) ; object ; object = object - > prototype ( ) ) {
2020-03-28 19:48:12 +01:00
if ( object = = prototype )
return true ;
}
return false ;
}
2020-04-25 18:43:34 +02:00
Value Object : : get_own_property ( const Object & this_object , const FlyString & property_name ) const
2020-03-21 14:37:34 +01:00
{
2020-04-02 19:32:21 +02:00
auto metadata = shape ( ) . lookup ( property_name ) ;
if ( ! metadata . has_value ( ) )
return { } ;
auto value_here = m_storage [ metadata . value ( ) . offset ] ;
2020-04-06 20:24:45 +02:00
ASSERT ( ! value_here . is_empty ( ) ) ;
2020-04-02 19:32:21 +02:00
if ( value_here . is_object ( ) & & value_here . as_object ( ) . is_native_property ( ) ) {
auto & native_property = static_cast < const NativeProperty & > ( value_here . as_object ( ) ) ;
2020-03-29 00:37:33 +01:00
auto & interpreter = const_cast < Object * > ( this ) - > interpreter ( ) ;
auto & call_frame = interpreter . push_call_frame ( ) ;
call_frame . this_value = const_cast < Object * > ( & this_object ) ;
auto result = native_property . get ( interpreter ) ;
interpreter . pop_call_frame ( ) ;
return result ;
}
2020-03-21 14:37:34 +01:00
return value_here ;
}
2020-05-01 11:06:27 +01:00
Value Object : : get_own_properties ( const Object & this_object , GetOwnPropertyMode kind , u8 attributes ) const
2020-04-29 18:59:23 -07:00
{
auto * properties_array = Array : : create ( interpreter ( ) . global_object ( ) ) ;
// FIXME: Support generic iterables
if ( this_object . is_string_object ( ) ) {
auto str = static_cast < const StringObject & > ( this_object ) . primitive_string ( ) . string ( ) ;
for ( size_t i = 0 ; i < str . length ( ) ; + + i ) {
if ( kind = = GetOwnPropertyMode : : Key ) {
properties_array - > put_by_index ( i , js_string ( interpreter ( ) , String : : number ( i ) ) ) ;
} else if ( kind = = GetOwnPropertyMode : : Value ) {
properties_array - > put_by_index ( i , js_string ( interpreter ( ) , String : : format ( " %c " , str [ i ] ) ) ) ;
} else {
auto * entry_array = Array : : create ( interpreter ( ) . global_object ( ) ) ;
entry_array - > put_by_index ( 0 , js_string ( interpreter ( ) , String : : number ( i ) ) ) ;
entry_array - > put_by_index ( 1 , js_string ( interpreter ( ) , String : : format ( " %c " , str [ i ] ) ) ) ;
properties_array - > put_by_index ( i , entry_array ) ;
}
}
return properties_array ;
}
size_t property_index = 0 ;
for ( size_t i = 0 ; i < m_elements . size ( ) ; + + i ) {
if ( m_elements . at ( i ) . is_empty ( ) )
continue ;
if ( kind = = GetOwnPropertyMode : : Key ) {
properties_array - > put_by_index ( property_index , js_string ( interpreter ( ) , String : : number ( i ) ) ) ;
} else if ( kind = = GetOwnPropertyMode : : Value ) {
properties_array - > put_by_index ( property_index , m_elements . at ( i ) ) ;
} else {
auto * entry_array = Array : : create ( interpreter ( ) . global_object ( ) ) ;
entry_array - > put_by_index ( 0 , js_string ( interpreter ( ) , String : : number ( i ) ) ) ;
entry_array - > put_by_index ( 1 , m_elements . at ( i ) ) ;
properties_array - > put_by_index ( property_index , entry_array ) ;
}
+ + property_index ;
}
for ( auto & it : this_object . shape ( ) . property_table_ordered ( ) ) {
2020-05-01 11:06:27 +01:00
if ( it . value . attributes & attributes ) {
2020-04-29 18:59:23 -07:00
size_t offset = it . value . offset + property_index ;
if ( kind = = GetOwnPropertyMode : : Key ) {
properties_array - > put_by_index ( offset , js_string ( interpreter ( ) , it . key ) ) ;
} else if ( kind = = GetOwnPropertyMode : : Value ) {
properties_array - > put_by_index ( offset , this_object . get ( it . key ) ) ;
} else {
auto * entry_array = Array : : create ( interpreter ( ) . global_object ( ) ) ;
entry_array - > put_by_index ( 0 , js_string ( interpreter ( ) , it . key ) ) ;
entry_array - > put_by_index ( 1 , this_object . get ( it . key ) ) ;
properties_array - > put_by_index ( offset , entry_array ) ;
}
}
}
return properties_array ;
}
2020-05-01 11:06:27 +01:00
Value Object : : get_own_property_descriptor ( const FlyString & property_name ) const
{
auto metadata = shape ( ) . lookup ( property_name ) ;
if ( ! metadata . has_value ( ) )
return js_undefined ( ) ;
auto value = get ( property_name ) ;
if ( interpreter ( ) . exception ( ) )
return { } ;
auto * descriptor = Object : : create_empty ( interpreter ( ) , interpreter ( ) . global_object ( ) ) ;
descriptor - > put ( " value " , value . value_or ( js_undefined ( ) ) ) ;
descriptor - > put ( " writable " , Value ( ! ! ( metadata . value ( ) . attributes & Attribute : : Writable ) ) ) ;
descriptor - > put ( " enumerable " , Value ( ! ! ( metadata . value ( ) . attributes & Attribute : : Enumerable ) ) ) ;
descriptor - > put ( " configurable " , Value ( ! ! ( metadata . value ( ) . attributes & Attribute : : Configurable ) ) ) ;
return descriptor ;
}
2020-04-02 19:32:21 +02:00
void Object : : set_shape ( Shape & new_shape )
{
m_storage . resize ( new_shape . property_count ( ) ) ;
m_shape = & new_shape ;
}
2020-05-01 11:06:27 +01:00
bool Object : : define_property ( const FlyString & property_name , const Object & descriptor , bool throw_exceptions )
{
auto value = descriptor . get ( " value " ) ;
u8 configurable = descriptor . get ( " configurable " ) . value_or ( Value ( false ) ) . to_boolean ( ) * Attribute : : Configurable ;
u8 enumerable = descriptor . get ( " enumerable " ) . value_or ( Value ( false ) ) . to_boolean ( ) * Attribute : : Enumerable ;
u8 writable = descriptor . get ( " writable " ) . value_or ( Value ( false ) ) . to_boolean ( ) * Attribute : : Writable ;
u8 attributes = configurable | enumerable | writable ;
dbg ( ) < < " Defining new property " < < property_name < < " with descriptor { " < < configurable < < " , " < < enumerable < < " , " < < writable < < " , attributes= " < < attributes < < " } " ;
return put_own_property ( * this , property_name , attributes , value , PutOwnPropertyMode : : DefineProperty , throw_exceptions ) ;
}
bool Object : : put_own_property ( Object & this_object , const FlyString & property_name , u8 attributes , Value value , PutOwnPropertyMode mode , bool throw_exceptions )
2020-03-21 14:37:34 +01:00
{
2020-04-02 19:32:21 +02:00
auto metadata = shape ( ) . lookup ( property_name ) ;
2020-04-27 23:05:02 -07:00
bool new_property = ! metadata . has_value ( ) ;
if ( new_property ) {
2020-05-05 18:48:30 +02:00
if ( ! m_shape - > is_unique ( ) & & shape ( ) . property_count ( ) > 100 ) {
// If you add more than 100 properties to an object, let's stop doing
// transitions to avoid filling up the heap with shapes.
ensure_shape_is_unique ( ) ;
}
2020-04-26 13:53:40 +02:00
if ( m_shape - > is_unique ( ) ) {
m_shape - > add_property_to_unique_shape ( property_name , attributes ) ;
2020-04-26 19:03:23 +02:00
m_storage . resize ( m_shape - > property_count ( ) ) ;
2020-04-26 13:53:40 +02:00
} else {
set_shape ( * m_shape - > create_put_transition ( property_name , attributes ) ) ;
}
2020-04-02 19:32:21 +02:00
metadata = shape ( ) . lookup ( property_name ) ;
ASSERT ( metadata . has_value ( ) ) ;
2020-04-09 22:15:26 +02:00
}
2020-04-09 22:55:17 +02:00
2020-04-27 23:05:02 -07:00
if ( ! new_property & & mode = = PutOwnPropertyMode : : DefineProperty & & ! ( metadata . value ( ) . attributes & Attribute : : Configurable ) & & attributes ! = metadata . value ( ) . attributes ) {
2020-04-09 22:15:26 +02:00
dbg ( ) < < " Disallow reconfig of non-configurable property " ;
2020-05-01 11:06:27 +01:00
if ( throw_exceptions )
interpreter ( ) . throw_exception < TypeError > ( String : : format ( " Cannot redefine property '%s' " , property_name . characters ( ) ) ) ;
2020-04-30 20:01:54 +01:00
return false ;
2020-04-02 19:32:21 +02:00
}
2020-04-09 22:55:17 +02:00
if ( mode = = PutOwnPropertyMode : : DefineProperty & & attributes ! = metadata . value ( ) . attributes ) {
2020-04-26 13:53:40 +02:00
if ( m_shape - > is_unique ( ) ) {
m_shape - > reconfigure_property_in_unique_shape ( property_name , attributes ) ;
} else {
set_shape ( * m_shape - > create_configure_transition ( property_name , attributes ) ) ;
}
2020-04-09 22:55:17 +02:00
metadata = shape ( ) . lookup ( property_name ) ;
dbg ( ) < < " Reconfigured property " < < property_name < < " , new shape says offset is " < < metadata . value ( ) . offset < < " and my storage capacity is " < < m_storage . size ( ) ;
}
2020-04-27 23:05:02 -07:00
if ( ! new_property & & mode = = PutOwnPropertyMode : : Put & & ! ( metadata . value ( ) . attributes & Attribute : : Writable ) ) {
2020-04-09 22:55:17 +02:00
dbg ( ) < < " Disallow write to non-writable property " ;
2020-04-30 20:01:54 +01:00
return false ;
2020-04-09 22:55:17 +02:00
}
if ( value . is_empty ( ) )
2020-04-30 20:01:54 +01:00
return true ;
2020-04-09 22:55:17 +02:00
2020-04-02 19:32:21 +02:00
auto value_here = m_storage [ metadata . value ( ) . offset ] ;
if ( value_here . is_object ( ) & & value_here . as_object ( ) . is_native_property ( ) ) {
auto & native_property = static_cast < NativeProperty & > ( value_here . as_object ( ) ) ;
2020-03-29 00:37:33 +01:00
auto & interpreter = const_cast < Object * > ( this ) - > interpreter ( ) ;
auto & call_frame = interpreter . push_call_frame ( ) ;
call_frame . this_value = & this_object ;
native_property . set ( interpreter , value ) ;
interpreter . pop_call_frame ( ) ;
2020-03-21 14:37:34 +01:00
} else {
2020-04-02 19:32:21 +02:00
m_storage [ metadata . value ( ) . offset ] = value ;
2020-03-21 14:37:34 +01:00
}
2020-04-30 20:01:54 +01:00
return true ;
2020-03-21 14:37:34 +01:00
}
2020-04-26 13:53:40 +02:00
Value Object : : delete_property ( PropertyName property_name )
{
ASSERT ( property_name . is_valid ( ) ) ;
if ( property_name . is_number ( ) ) {
if ( property_name . as_number ( ) < static_cast < i32 > ( elements ( ) . size ( ) ) ) {
elements ( ) [ property_name . as_number ( ) ] = { } ;
return Value ( true ) ;
}
return Value ( true ) ;
}
auto metadata = shape ( ) . lookup ( property_name . as_string ( ) ) ;
if ( ! metadata . has_value ( ) )
return Value ( true ) ;
if ( ! ( metadata . value ( ) . attributes & Attribute : : Configurable ) )
return Value ( false ) ;
size_t deleted_offset = metadata . value ( ) . offset ;
ensure_shape_is_unique ( ) ;
shape ( ) . remove_property_from_unique_shape ( property_name . as_string ( ) , deleted_offset ) ;
m_storage . remove ( deleted_offset ) ;
return Value ( true ) ;
}
void Object : : ensure_shape_is_unique ( )
{
if ( shape ( ) . is_unique ( ) )
return ;
m_shape = m_shape - > create_unique_clone ( ) ;
}
2020-04-25 18:43:34 +02:00
Value Object : : get_by_index ( i32 property_index ) const
2020-04-06 16:53:02 +02:00
{
if ( property_index < 0 )
return get ( String : : number ( property_index ) ) ;
const Object * object = this ;
while ( object ) {
2020-05-01 13:20:47 +01:00
if ( is_string_object ( ) ) {
auto & string = static_cast < const StringObject * > ( this ) - > primitive_string ( ) . string ( ) ;
if ( property_index < ( i32 ) string . length ( ) )
return js_string ( heap ( ) , string . substring ( property_index , 1 ) ) ;
return js_undefined ( ) ;
}
2020-04-06 20:24:45 +02:00
if ( static_cast < size_t > ( property_index ) < object - > m_elements . size ( ) ) {
auto value = object - > m_elements [ property_index ] ;
if ( value . is_empty ( ) )
return { } ;
return value ;
}
2020-04-06 16:53:02 +02:00
object = object - > prototype ( ) ;
}
return { } ;
}
2020-04-25 18:43:34 +02:00
Value Object : : get ( const FlyString & property_name ) const
2020-03-07 19:42:11 +01:00
{
2020-04-06 16:53:02 +02:00
bool ok ;
i32 property_index = property_name . to_int ( ok ) ;
if ( ok & & property_index > = 0 )
return get_by_index ( property_index ) ;
2020-03-15 15:01:10 +01:00
const Object * object = this ;
while ( object ) {
2020-03-24 14:46:05 +01:00
auto value = object - > get_own_property ( * this , property_name ) ;
2020-04-25 18:43:34 +02:00
if ( ! value . is_empty ( ) )
return value ;
2020-03-15 15:01:10 +01:00
object = object - > prototype ( ) ;
}
2020-03-27 12:54:18 +01:00
return { } ;
2020-03-07 19:42:11 +01:00
}
2020-04-25 18:43:34 +02:00
Value Object : : get ( PropertyName property_name ) const
2020-04-06 17:08:23 +02:00
{
if ( property_name . is_number ( ) )
return get_by_index ( property_name . as_number ( ) ) ;
return get ( property_name . as_string ( ) ) ;
}
2020-04-30 20:01:54 +01:00
bool Object : : put_by_index ( i32 property_index , Value value , u8 attributes )
2020-04-06 16:53:02 +02:00
{
2020-04-06 20:24:45 +02:00
ASSERT ( ! value . is_empty ( ) ) ;
2020-04-06 16:53:02 +02:00
if ( property_index < 0 )
2020-04-27 23:05:02 -07:00
return put ( String : : number ( property_index ) , value , attributes ) ;
2020-04-06 16:53:02 +02:00
// FIXME: Implement some kind of sparse storage for arrays with huge indices.
2020-04-27 23:05:02 -07:00
// Also: Take attributes into account here
2020-04-06 16:53:02 +02:00
if ( static_cast < size_t > ( property_index ) > = m_elements . size ( ) )
m_elements . resize ( property_index + 1 ) ;
m_elements [ property_index ] = value ;
2020-04-30 20:01:54 +01:00
return true ;
2020-04-06 16:53:02 +02:00
}
2020-04-30 20:01:54 +01:00
bool Object : : put ( const FlyString & property_name , Value value , u8 attributes )
2020-03-07 19:42:11 +01:00
{
2020-04-06 20:24:45 +02:00
ASSERT ( ! value . is_empty ( ) ) ;
2020-04-06 16:53:02 +02:00
bool ok ;
i32 property_index = property_name . to_int ( ok ) ;
if ( ok & & property_index > = 0 )
2020-04-27 23:05:02 -07:00
return put_by_index ( property_index , value , attributes ) ;
2020-04-06 16:53:02 +02:00
2020-04-02 21:34:31 +02:00
// If there's a setter in the prototype chain, we go to the setter.
// Otherwise, it goes in the own property storage.
2020-03-19 17:39:13 +01:00
Object * object = this ;
while ( object ) {
2020-04-02 19:32:21 +02:00
auto metadata = object - > shape ( ) . lookup ( property_name ) ;
if ( metadata . has_value ( ) ) {
auto value_here = object - > m_storage [ metadata . value ( ) . offset ] ;
if ( value_here . is_object ( ) & & value_here . as_object ( ) . is_native_property ( ) ) {
auto & native_property = static_cast < NativeProperty & > ( value_here . as_object ( ) ) ;
2020-03-29 00:37:33 +01:00
auto & interpreter = const_cast < Object * > ( this ) - > interpreter ( ) ;
auto & call_frame = interpreter . push_call_frame ( ) ;
call_frame . this_value = this ;
native_property . set ( interpreter , value ) ;
interpreter . pop_call_frame ( ) ;
2020-04-30 20:01:54 +01:00
return true ;
2020-03-19 17:39:13 +01:00
}
}
object = object - > prototype ( ) ;
}
2020-04-30 20:01:54 +01:00
return put_own_property ( * this , property_name , attributes , value , PutOwnPropertyMode : : Put ) ;
2020-03-07 19:42:11 +01:00
}
2020-04-30 20:01:54 +01:00
bool Object : : put ( PropertyName property_name , Value value , u8 attributes )
2020-04-06 17:08:23 +02:00
{
if ( property_name . is_number ( ) )
2020-04-27 23:05:02 -07:00
return put_by_index ( property_name . as_number ( ) , value , attributes ) ;
return put ( property_name . as_string ( ) , value , attributes ) ;
2020-04-06 17:08:23 +02:00
}
2020-04-30 20:01:54 +01:00
bool Object : : put_native_function ( const FlyString & property_name , AK : : Function < Value ( Interpreter & ) > native_function , i32 length , u8 attributes )
2020-03-13 11:06:32 +01:00
{
2020-04-17 19:59:32 +02:00
auto * function = NativeFunction : : create ( interpreter ( ) , interpreter ( ) . global_object ( ) , property_name , move ( native_function ) ) ;
2020-04-27 23:05:02 -07:00
function - > put ( " length " , Value ( length ) , Attribute : : Configurable ) ;
2020-05-02 19:18:55 +01:00
function - > put ( " name " , js_string ( heap ( ) , property_name ) , Attribute : : Configurable ) ;
2020-04-30 20:01:54 +01:00
return put ( property_name , function , attributes ) ;
2020-03-13 11:06:32 +01:00
}
2020-04-30 20:01:54 +01:00
bool Object : : put_native_property ( const FlyString & property_name , AK : : Function < Value ( Interpreter & ) > getter , AK : : Function < void ( Interpreter & , Value ) > setter , u8 attributes )
2020-03-15 18:15:44 +01:00
{
2020-04-30 20:01:54 +01:00
return put ( property_name , heap ( ) . allocate < NativeProperty > ( move ( getter ) , move ( setter ) ) , attributes ) ;
2020-03-15 18:15:44 +01:00
}
2020-03-09 22:11:22 +01:00
void Object : : visit_children ( Cell : : Visitor & visitor )
2020-03-08 19:23:58 +01:00
{
2020-03-09 22:11:22 +01:00
Cell : : visit_children ( visitor ) ;
2020-04-02 19:32:21 +02:00
visitor . visit ( m_shape ) ;
2020-04-06 12:33:27 +02:00
for ( auto & value : m_storage )
visitor . visit ( value ) ;
2020-04-06 16:53:02 +02:00
for ( auto & value : m_elements )
visitor . visit ( value ) ;
2020-03-08 19:23:58 +01:00
}
2020-04-30 20:03:40 +01:00
bool Object : : has_property ( const FlyString & property_name ) const
{
const Object * object = this ;
while ( object ) {
if ( object - > has_own_property ( property_name ) )
return true ;
object = object - > prototype ( ) ;
}
return false ;
}
2020-03-22 11:07:55 +01:00
bool Object : : has_own_property ( const FlyString & property_name ) const
2020-03-15 15:25:43 +01:00
{
2020-04-06 16:53:02 +02:00
bool ok ;
i32 property_index = property_name . to_int ( ok ) ;
2020-04-06 20:24:45 +02:00
if ( ok & & property_index > = 0 ) {
2020-05-01 13:20:47 +01:00
if ( is_string_object ( ) )
2020-05-04 13:30:40 +02:00
return property_index < ( i32 ) static_cast < const StringObject * > ( this ) - > primitive_string ( ) . string ( ) . length ( ) ;
2020-04-06 20:24:45 +02:00
if ( static_cast < size_t > ( property_index ) > = m_elements . size ( ) )
return false ;
return ! m_elements [ property_index ] . is_empty ( ) ;
}
2020-04-02 19:32:21 +02:00
return shape ( ) . lookup ( property_name ) . has_value ( ) ;
2020-03-15 15:25:43 +01:00
}
2020-03-16 00:19:41 +02:00
Value Object : : to_primitive ( PreferredType preferred_type ) const
{
Value result = js_undefined ( ) ;
switch ( preferred_type ) {
case PreferredType : : Default :
case PreferredType : : Number : {
result = value_of ( ) ;
if ( result . is_object ( ) ) {
result = to_string ( ) ;
}
break ;
}
case PreferredType : : String : {
result = to_string ( ) ;
if ( result . is_object ( ) )
result = value_of ( ) ;
break ;
}
}
ASSERT ( ! result . is_object ( ) ) ;
return result ;
}
Value Object : : to_string ( ) const
{
2020-04-05 18:18:24 +02:00
auto to_string_property = get ( " toString " ) ;
2020-05-06 16:40:08 +02:00
if ( to_string_property . is_function ( ) ) {
2020-05-06 11:52:53 +01:00
auto & to_string_function = to_string_property . as_function ( ) ;
2020-04-29 16:29:26 +01:00
auto & interpreter = const_cast < Object * > ( this ) - > interpreter ( ) ;
2020-04-29 16:36:38 +01:00
auto to_string_result = interpreter . call ( to_string_function , const_cast < Object * > ( this ) ) ;
if ( to_string_result . is_object ( ) )
2020-04-29 16:29:26 +01:00
interpreter . throw_exception < TypeError > ( " Cannot convert object to string " ) ;
if ( interpreter . exception ( ) )
return { } ;
2020-05-15 13:39:24 +02:00
auto * string = to_string_result . to_primitive_string ( interpreter ) ;
if ( interpreter . exception ( ) )
return { } ;
return string ;
2020-04-05 18:18:24 +02:00
}
2020-03-16 00:19:41 +02:00
return js_string ( heap ( ) , String : : format ( " [object %s] " , class_name ( ) ) ) ;
}
2020-04-05 18:18:24 +02:00
2020-03-07 19:42:11 +01:00
}