blob: bcaa01850f5eb754663bfa3c3ff5a11dff34f996 [file] [log] [blame]
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#include "RuntimeTypePch.h"
#include "Library/JavascriptSymbolObject.h"
DEFINE_VALIDATE_HAS_VTABLE_CTOR(Js::RecyclableObject);
namespace Js
{
void PropertyValueInfo::SetCacheInfo(PropertyValueInfo* info, InlineCache *const inlineCache)
{
Assert(info);
Assert(inlineCache);
info->functionBody = nullptr;
info->inlineCache = inlineCache;
info->polymorphicInlineCache = nullptr;
info->inlineCacheIndex = Js::Constants::NoInlineCacheIndex;
info->allowResizingPolymorphicInlineCache = false;
}
void PropertyValueInfo::SetCacheInfo(
_Out_ PropertyValueInfo* info,
_In_opt_ RecyclableObject * prop,
_In_opt_ PropertyRecordUsageCache *const propertyRecordUsageCache,
_In_ PolymorphicInlineCache *const polymorphicInlineCache,
bool allowResizing)
{
Assert(info);
// Make sure the given prop and usage cache match
Assert(
prop == nullptr && propertyRecordUsageCache == nullptr ||
JavascriptSymbol::Is(prop) && JavascriptSymbol::UnsafeFromVar(prop)->GetPropertyRecordUsageCache() == propertyRecordUsageCache ||
PropertyString::Is(prop) && PropertyString::UnsafeFromVar(prop)->GetPropertyRecordUsageCache() == propertyRecordUsageCache);
info->prop = prop;
info->propertyRecordUsageCache = propertyRecordUsageCache;
SetCacheInfo(info, polymorphicInlineCache, allowResizing);
if (propertyRecordUsageCache && propertyRecordUsageCache->ShouldDisableWriteCache())
{
info->ClearInfoFlag(CacheInfoFlag::enableStoreFieldCacheFlag);
}
}
void PropertyValueInfo::SetCacheInfo(_Out_ PropertyValueInfo* info, _In_ PolymorphicInlineCache *const polymorphicInlineCache, bool allowResizing)
{
Assert(info);
Assert(polymorphicInlineCache);
info->functionBody = nullptr;
info->inlineCache = nullptr;
info->polymorphicInlineCache = polymorphicInlineCache;
info->inlineCacheIndex = Js::Constants::NoInlineCacheIndex;
info->allowResizingPolymorphicInlineCache = allowResizing;
}
void PropertyValueInfo::SetCacheInfo(
PropertyValueInfo* info,
FunctionBody *const functionBody,
InlineCache *const inlineCache,
const InlineCacheIndex inlineCacheIndex,
const bool allowResizingPolymorphicInlineCache)
{
Assert(info);
Assert(functionBody);
Assert(inlineCache);
Assert(inlineCacheIndex < functionBody->GetInlineCacheCount());
info->functionBody = functionBody;
info->inlineCache = inlineCache;
info->polymorphicInlineCache = nullptr;
info->inlineCacheIndex = inlineCacheIndex;
info->allowResizingPolymorphicInlineCache = allowResizingPolymorphicInlineCache;
}
void PropertyValueInfo::SetCacheInfo(
PropertyValueInfo* info,
FunctionBody *const functionBody,
PolymorphicInlineCache *const polymorphicInlineCache,
const InlineCacheIndex inlineCacheIndex,
const bool allowResizingPolymorphicInlineCache)
{
Assert(info);
Assert(functionBody);
Assert(polymorphicInlineCache);
Assert(inlineCacheIndex < functionBody->GetInlineCacheCount());
info->functionBody = functionBody;
info->inlineCache = nullptr;
info->polymorphicInlineCache = polymorphicInlineCache;
info->inlineCacheIndex = inlineCacheIndex;
info->allowResizingPolymorphicInlineCache = allowResizingPolymorphicInlineCache;
}
void PropertyValueInfo::ClearCacheInfo(PropertyValueInfo* info)
{
if (info != NULL)
{
info->functionBody = nullptr;
info->inlineCache = nullptr;
info->polymorphicInlineCache = nullptr;
info->prop = nullptr;
info->propertyRecordUsageCache = nullptr;
info->inlineCacheIndex = Constants::NoInlineCacheIndex;
info->allowResizingPolymorphicInlineCache = true;
}
}
#if DBG || defined(PROFILE_TYPES)
// Used only by the GlobalObject, because it's typeHandler can't be fully initialized
// with the globalobject which is currently being created.
RecyclableObject::RecyclableObject(DynamicType * type, ScriptContext * scriptContext) : type(type)
{
#if DBG_EXTRAFIELD
dtorCalled = false;
#ifdef HEAP_ENUMERATION_VALIDATION
m_heapEnumValidationCookie = 0;
#endif
#endif
Assert(type->GetTypeId() == TypeIds_GlobalObject);
RecordAllocation(scriptContext);
}
void RecyclableObject::RecordAllocation(ScriptContext * scriptContext)
{
#ifdef PROFILE_TYPES
TypeId typeId = this->GetType()->GetTypeId();
if (typeId < sizeof(scriptContext->instanceCount)/sizeof(int))
{
scriptContext->instanceCount[typeId]++;
}
#endif
}
#endif
RecyclableObject::RecyclableObject(Type * type) : type(type)
{
#if DBG_EXTRAFIELD
dtorCalled = false;
#ifdef HEAP_ENUMERATION_VALIDATION
m_heapEnumValidationCookie = 0;
#endif
#endif
#if DBG || defined(PROFILE_TYPES)
#if ENABLE_NATIVE_CODEGEN
if (!JITManager::GetJITManager()->IsOOPJITEnabled())
#endif
{
RecordAllocation(type->GetScriptContext());
}
#endif
}
RecyclableObject* RecyclableObject::GetPrototype() const
{
Type* type = GetType();
if (!type->HasSpecialPrototype())
{
return type->GetPrototype();
}
return const_cast<RecyclableObject*>(this)->GetPrototypeSpecial();
}
RecyclableObject* RecyclableObject::GetPrototypeSpecial()
{
AssertMsg(GetType()->GetTypeId() == TypeIds_Null, "Do not use this function.");
return nullptr;
}
JavascriptMethod RecyclableObject::GetEntryPoint() const
{
return this->GetType()->GetEntryPoint();
}
Recycler* RecyclableObject::GetRecycler() const
{
return this->GetLibrary()->GetRecycler();
}
void RecyclableObject::SetIsPrototype()
{
if (DynamicType::Is(this->GetTypeId()))
{
DynamicObject* dynamicThis = DynamicObject::UnsafeFromVar(this);
dynamicThis->SetIsPrototype(); // Call the DynamicObject::SetIsPrototype
}
}
bool RecyclableObject::HasOnlyWritableDataProperties()
{
if (DynamicType::Is(this->GetTypeId()))
{
DynamicObject* obj = DynamicObject::UnsafeFromVar(this);
return obj->GetTypeHandler()->GetHasOnlyWritableDataProperties() &&
(!obj->HasObjectArray() || obj->GetObjectArrayOrFlagsAsArray()->HasOnlyWritableDataProperties());
}
return true;
}
bool RecyclableObject::HasAnySpecialProperties()
{
if (DynamicType::Is(this->GetTypeId()))
{
DynamicObject* obj = DynamicObject::UnsafeFromVar(this);
return obj->GetTypeHandler()->GetHasSpecialProperties() ||
(obj->HasObjectArray() && obj->GetObjectArrayOrFlagsAsArray()->HasAnySpecialProperties());
}
return true;
}
void RecyclableObject::ClearWritableDataOnlyDetectionBit()
{
if (DynamicType::Is(this->GetTypeId()))
{
DynamicObject* obj = DynamicObject::UnsafeFromVar(this);
obj->GetTypeHandler()->ClearWritableDataOnlyDetectionBit();
if (obj->HasObjectArray())
{
obj->GetObjectArrayOrFlagsAsArray()->ClearWritableDataOnlyDetectionBit();
}
}
}
bool RecyclableObject::IsWritableDataOnlyDetectionBitSet()
{
if (DynamicType::Is(this->GetTypeId()))
{
DynamicObject* obj = DynamicObject::UnsafeFromVar(this);
return obj->GetTypeHandler()->IsWritableDataOnlyDetectionBitSet() ||
(obj->HasObjectArray() && obj->GetObjectArrayOrFlagsAsArray()->IsWritableDataOnlyDetectionBitSet());
}
return false;
}
RecyclableObject* RecyclableObject::GetProxiedObjectForHeapEnum()
{
Assert(this->GetScriptContext()->IsHeapEnumInProgress());
return NULL;
}
BOOL RecyclableObject::SkipsPrototype() const
{
Assert(this->DbgSkipsPrototype() == this->GetType()->SkipsPrototype());
return this->GetType()->SkipsPrototype();
}
RecyclableObject * RecyclableObject::CloneToScriptContext(ScriptContext* requestContext)
{
switch (JavascriptOperators::GetTypeId(this))
{
case TypeIds_Undefined:
return requestContext->GetLibrary()->GetUndefined();
case TypeIds_Null:
return requestContext->GetLibrary()->GetNull();
case TypeIds_Number:
return RecyclableObject::FromVar(JavascriptNumber::CloneToScriptContext(this, requestContext));
default:
AssertMsg(FALSE, "shouldn't clone for other types");
Js::JavascriptError::ThrowError(requestContext, VBSERR_InternalError);
}
}
#if defined(PROFILE_RECYCLER_ALLOC) && defined(RECYCLER_DUMP_OBJECT_GRAPH)
bool RecyclableObject::DumpObjectFunction(type_info const * typeinfo, bool isArray, void * objectAddress)
{
if (isArray)
{
// Don't deal with array
return false;
}
Output::Print(_u("%S{%x} %p"), typeinfo->name(), ((RecyclableObject *)objectAddress)->GetTypeId(), objectAddress);
return true;
}
#endif
#if ENABLE_TTD
TTD::NSSnapObjects::SnapObjectType RecyclableObject::GetSnapTag_TTD() const
{
return TTD::NSSnapObjects::SnapObjectType::Invalid;
}
void RecyclableObject::ExtractSnapObjectDataInto(TTD::NSSnapObjects::SnapObject* objData, TTD::SlabAllocator& alloc)
{
TTDAssert(false, "Missing subtype implementation.");
}
#endif
BOOL RecyclableObject::SetPropertyWithAttributes(PropertyId propertyId, Var value, PropertyAttributes attributes, PropertyValueInfo* info, PropertyOperationFlags flags, SideEffects possibleSideEffects)
{
// TODO: It appears as though this is never called. Some types (such as JavascriptNumber) don't override this, but they
// also don't expect properties to be set on them. Need to review this and see if we can make this pure virtual or
// Assert(false) here. In any case, this should be SetProperty, not InitProperty.
Assert(false);
bool isForce = (flags & PropertyOperation_Force) != 0;
bool throwIfNotExtensible = (flags & PropertyOperation_ThrowIfNotExtensible) != 0;
if (!isForce)
{
// throwIfNotExtensible is only relevant to DynamicObjects
Assert(!throwIfNotExtensible);
}
return
this->InitProperty(propertyId, value, flags) &&
this->SetAttributes(propertyId, attributes);
}
void RecyclableObject::ThrowIfCannotDefineProperty(PropertyId propId, const PropertyDescriptor& descriptor)
{
// Do nothing
}
HRESULT RecyclableObject::QueryObjectInterface(REFIID riid, void **ppvObj)
{
Assert(!this->GetScriptContext()->GetThreadContext()->IsScriptActive());
return E_NOINTERFACE;
}
RecyclableObject* RecyclableObject::GetThisObjectOrUnWrap()
{
if (UnscopablesWrapperObject::Is(this))
{
return UnscopablesWrapperObject::FromVar(this)->GetWrappedObject();
}
return this;
}
// In order to avoid a branch, every object has an entry point if it gets called like a
// function - however, if it can't be called like a function, it's set to DefaultEntryPoint
// which will emit an error.
Var RecyclableObject::DefaultEntryPoint(RecyclableObject* function, CallInfo callInfo, ...)
{
ARGUMENTS(args, callInfo);
TypeId typeId = function->GetTypeId();
rtErrors err = typeId == TypeIds_Undefined || typeId == TypeIds_Null ? JSERR_NeedObject : JSERR_NeedFunction;
JavascriptError::ThrowTypeError(function->GetScriptContext(), err
/* TODO-ERROR: args.Info.Count > 0? args[0] : nullptr); */);
}
PropertyQueryFlags RecyclableObject::HasPropertyQuery(PropertyId propertyId, _Inout_opt_ PropertyValueInfo* info)
{
return PropertyQueryFlags::Property_NotFound;
}
BOOL RecyclableObject::HasOwnProperty(PropertyId propertyId)
{
return false;
}
BOOL RecyclableObject::HasOwnPropertyNoHostObject(PropertyId propertyId)
{
return HasOwnProperty(propertyId);
}
PropertyQueryFlags RecyclableObject::GetPropertyQuery(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
{
return PropertyQueryFlags::Property_NotFound;
}
PropertyQueryFlags RecyclableObject::GetPropertyQuery(Var originalInstance, JavascriptString* propertyNameString, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
{
return PropertyQueryFlags::Property_NotFound;
}
BOOL RecyclableObject::GetInternalProperty(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
{
return false;
}
PropertyQueryFlags RecyclableObject::GetPropertyReferenceQuery(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
{
return PropertyQueryFlags::Property_NotFound;
}
BOOL RecyclableObject::SetProperty(PropertyId propertyId, Var value, PropertyOperationFlags flags, PropertyValueInfo* info)
{
return false;
}
BOOL RecyclableObject::SetProperty(JavascriptString* propertyNameString, Var value, PropertyOperationFlags flags, PropertyValueInfo* info)
{
return false;
}
BOOL RecyclableObject::SetInternalProperty(PropertyId propertyId, Var value, PropertyOperationFlags flags, PropertyValueInfo* info)
{
return false;
}
BOOL RecyclableObject::InitProperty(PropertyId propertyId, Var value, PropertyOperationFlags flags, PropertyValueInfo* info)
{
return false;
}
BOOL RecyclableObject::InitPropertyInEval(PropertyId propertyId, Var value, PropertyOperationFlags flags, PropertyValueInfo* info)
{
return false;
}
BOOL RecyclableObject::InitPropertyScoped(PropertyId propertyId, Var value)
{
return false;
}
BOOL RecyclableObject::InitFuncScoped(PropertyId propertyId, Var value)
{
return false;
}
BOOL RecyclableObject::EnsureProperty(PropertyId propertyId)
{
return false;
}
BOOL RecyclableObject::EnsureNoRedeclProperty(PropertyId propertyId)
{
return false;
}
BOOL RecyclableObject::DeleteProperty(PropertyId propertyId, PropertyOperationFlags flags)
{
return true;
}
BOOL RecyclableObject::DeleteProperty(JavascriptString *propertyNameString, PropertyOperationFlags flags)
{
return true;
}
#if ENABLE_FIXED_FIELDS
BOOL RecyclableObject::IsFixedProperty(PropertyId propertyId)
{
return false;
}
#endif
PropertyQueryFlags RecyclableObject::HasItemQuery(uint32 index)
{
return PropertyQueryFlags::Property_NotFound;
}
BOOL RecyclableObject::HasOwnItem(uint32 index)
{
return false;
}
PropertyQueryFlags RecyclableObject::GetItemQuery(Var originalInstance, uint32 index, Var* value, ScriptContext * requestContext)
{
return PropertyQueryFlags::Property_NotFound;
}
PropertyQueryFlags RecyclableObject::GetItemReferenceQuery(Var originalInstance, uint32 index, Var* value, ScriptContext * requestContext)
{
return PropertyQueryFlags::Property_NotFound;
}
BOOL RecyclableObject::SetItem(uint32 index, Var value, PropertyOperationFlags flags)
{
return false;
}
BOOL RecyclableObject::DeleteItem(uint32 index, PropertyOperationFlags flags)
{
return true;
}
BOOL RecyclableObject::GetEnumerator(JavascriptStaticEnumerator * enumerator, EnumeratorFlags flags, ScriptContext* requestContext, EnumeratorCache * enumeratorCache)
{
return false;
}
BOOL RecyclableObject::ToPrimitive(JavascriptHint hint, Var* value, ScriptContext * scriptContext)
{
*value = NULL;
return false;
}
BOOL RecyclableObject::SetAccessors(PropertyId propertyId, Var getter, Var setter, PropertyOperationFlags flags)
{
return false;
}
_Check_return_ _Success_(return) BOOL RecyclableObject::GetAccessors(PropertyId propertyId, _Outptr_result_maybenull_ Var* getter, _Outptr_result_maybenull_ Var* setter, ScriptContext * requestContext)
{
return false;
}
BOOL RecyclableObject::StrictEquals(__in Var aRight, __out BOOL* value, ScriptContext * requestContext)
{
*value = false;
//StrictEquals is handled in JavascriptOperators::StrictEqual
Throw::InternalError();
}
#pragma fenv_access (on)
BOOL RecyclableObject::Equals(__in Var aRight, __out BOOL* value, ScriptContext * requestContext)
{
Var aLeft = this;
if (aLeft == aRight)
{
//In ES5 mode strict equals (===) on same instance of object type VariantDate succeeds.
//Hence equals needs to succeed.
//goto ReturnTrue;
*value = TRUE;
return TRUE;
}
double dblLeft, dblRight;
TypeId leftType = this->GetTypeId();
TypeId rightType = JavascriptOperators::GetTypeId(aRight);
int redoCount = 0;
Redo:
if (redoCount == 2)
{
goto ReturnFalse;
}
switch (leftType)
{
case TypeIds_Undefined:
case TypeIds_Null:
switch (rightType)
{
case TypeIds_Integer:
case TypeIds_Number:
case TypeIds_Symbol:
goto ReturnFalse;
case TypeIds_Undefined:
case TypeIds_Null:
goto ReturnTrue;
default:
// Falsy objects are == null and == undefined.
*value = RecyclableObject::FromVar(aRight)->GetType()->IsFalsy();
return TRUE;
}
case TypeIds_Integer:
switch (rightType)
{
case TypeIds_Undefined:
case TypeIds_Null:
case TypeIds_Symbol:
goto ReturnFalse;
case TypeIds_Integer:
// We already did a check to see if aLeft == aRight above, but we need to check again in case there was a redo.
*value = aLeft == aRight;
return TRUE;
case TypeIds_Int64Number:
{
int leftValue = TaggedInt::ToInt32(aLeft);
__int64 rightValue = JavascriptInt64Number::FromVar(aRight)->GetValue();
*value = leftValue == rightValue;
Assert(!(*value)); // currently it cannot be true. more for future extension if we allow arithmetic calculation
return TRUE;
}
case TypeIds_UInt64Number:
{
__int64 leftValue = TaggedInt::ToInt32(aLeft);
unsigned __int64 rightValue = JavascriptInt64Number::FromVar(aRight)->GetValue();
// TODO: yongqu to review whether we need to check for neg value
*value = (/*leftValue >= 0 && */(unsigned __int64)leftValue == rightValue);
Assert(!(*value)); // currently it cannot be true. more for future extension if we allow arithmetic calculation
return TRUE;
}
case TypeIds_Number:
dblLeft = TaggedInt::ToDouble(aLeft);
dblRight = JavascriptNumber::GetValue(aRight);
goto CompareDoubles;
case TypeIds_Boolean:
case TypeIds_String:
dblLeft = TaggedInt::ToDouble(aLeft);
dblRight = JavascriptConversion::ToNumber(aRight, requestContext);
goto CompareDoubles;
default:
goto RedoRight;
}
break;
case TypeIds_Int64Number:
switch (rightType)
{
case TypeIds_Integer:
{
__int64 leftValue = JavascriptInt64Number::FromVar(aLeft)->GetValue();
int rightValue = TaggedInt::ToInt32(aRight);
*value = leftValue == rightValue;
Assert(!(*value)); // currently it cannot be true. more for future extension if we allow arithmetic calculation
return TRUE;
}
case TypeIds_Number:
dblLeft = (double)JavascriptInt64Number::FromVar(aLeft)->GetValue();
dblRight = JavascriptNumber::GetValue(aRight);
goto CompareDoubles;
case TypeIds_Int64Number:
{
__int64 leftValue = JavascriptInt64Number::FromVar(aLeft)->GetValue();
__int64 rightValue = JavascriptInt64Number::FromVar(aRight)->GetValue();
*value = leftValue == rightValue;
return TRUE;
}
case TypeIds_UInt64Number:
{
__int64 leftValue = JavascriptInt64Number::FromVar(aLeft)->GetValue();
unsigned __int64 rightValue = JavascriptInt64Number::FromVar(aRight)->GetValue();
// TODO: yongqu to review whether we need to check for neg value
*value = (/* leftValue >= 0 && */(unsigned __int64)leftValue == rightValue);
return TRUE;
}
}
break;
case TypeIds_UInt64Number:
switch (rightType)
{
case TypeIds_Integer:
{
unsigned __int64 leftValue = JavascriptUInt64Number::FromVar(aLeft)->GetValue();
__int64 rightValue = TaggedInt::ToInt32(aRight);
// TODO: yongqu to review whether we need to check for neg value
*value = rightValue >= 0 && leftValue == (unsigned __int64)rightValue;
Assert(!(*value)); // currently it cannot be true. more for future extension if we allow arithmetic calculation
return TRUE;
}
case TypeIds_Number:
dblLeft = (double)JavascriptUInt64Number::FromVar(aLeft)->GetValue();
dblRight = JavascriptNumber::GetValue(aRight);
goto CompareDoubles;
case TypeIds_Int64Number:
{
unsigned __int64 leftValue = JavascriptUInt64Number::FromVar(aLeft)->GetValue();
__int64 rightValue = JavascriptInt64Number::FromVar(aRight)->GetValue();
// TODO: yongqu to review whether we need to check for neg value
*value = (/* rightValue >= 0 && */leftValue == (unsigned __int64)rightValue);
return TRUE;
}
case TypeIds_UInt64Number:
{
unsigned __int64 leftValue = JavascriptUInt64Number::FromVar(aLeft)->GetValue();
unsigned __int64 rightValue = JavascriptInt64Number::FromVar(aRight)->GetValue();
*value = leftValue == rightValue;
return TRUE;
}
}
break;
case TypeIds_Number:
switch (rightType)
{
case TypeIds_Undefined:
case TypeIds_Null:
case TypeIds_Symbol:
goto ReturnFalse;
case TypeIds_Integer:
dblLeft = JavascriptNumber::GetValue(aLeft);
dblRight = TaggedInt::ToDouble(aRight);
goto CompareDoubles;
case TypeIds_Number:
dblLeft = JavascriptNumber::GetValue(aLeft);
dblRight = JavascriptNumber::GetValue(aRight);
goto CompareDoubles;
case TypeIds_Boolean:
case TypeIds_String:
dblLeft = JavascriptNumber::GetValue(aLeft);
dblRight = JavascriptConversion::ToNumber(aRight, requestContext);
goto CompareDoubles;
default:
goto RedoRight;
}
break;
case TypeIds_String:
switch (rightType)
{
case TypeIds_Undefined:
case TypeIds_Null:
case TypeIds_Symbol:
goto ReturnFalse;
case TypeIds_String:
*value = JavascriptString::Equals(JavascriptString::UnsafeFromVar(aLeft), JavascriptString::UnsafeFromVar(aRight));
return TRUE;
case TypeIds_Number:
case TypeIds_Integer:
case TypeIds_Boolean:
dblLeft = JavascriptConversion::ToNumber(aLeft, requestContext);
dblRight = JavascriptConversion::ToNumber(aRight, requestContext);
goto CompareDoubles;
default:
goto RedoRight;
}
case TypeIds_Boolean:
switch (rightType)
{
case TypeIds_Undefined:
case TypeIds_Null:
case TypeIds_Symbol:
goto ReturnFalse;
case TypeIds_Boolean:
*value = JavascriptBoolean::FromVar(aLeft)->GetValue() == JavascriptBoolean::FromVar(aRight)->GetValue();
return TRUE;
case TypeIds_Number:
case TypeIds_Integer:
case TypeIds_String:
dblLeft = JavascriptConversion::ToNumber(aLeft, requestContext);
dblRight = JavascriptConversion::ToNumber(aRight, requestContext);
goto CompareDoubles;
default:
goto RedoRight;
}
break;
case TypeIds_Symbol:
switch (rightType)
{
case TypeIds_Undefined:
case TypeIds_Null:
case TypeIds_Number:
case TypeIds_Integer:
case TypeIds_String:
case TypeIds_Boolean:
goto ReturnFalse;
case TypeIds_Symbol:
*value = (aLeft == aRight);
Assert((JavascriptSymbol::UnsafeFromVar(aLeft)->GetValue() == JavascriptSymbol::UnsafeFromVar(aRight)->GetValue()) == *value);
return TRUE;
case TypeIds_SymbolObject:
*value = (aLeft == JavascriptSymbolObject::UnsafeFromVar(aRight)->Unwrap());
Assert((JavascriptSymbol::UnsafeFromVar(aLeft)->GetValue() == JavascriptSymbolObject::UnsafeFromVar(aRight)->GetValue()) == *value);
return TRUE;
default:
goto RedoRight;
}
break;
case TypeIds_Function:
if (rightType == TypeIds_Function)
{
goto ReturnFalse;
}
// Fall through to do normal object comparison on function object.
default:
switch (rightType)
{
case TypeIds_Undefined:
case TypeIds_Null:
// Falsy objects are == null and == undefined.
*value = this->type->IsFalsy();
return TRUE;
case TypeIds_Boolean:
case TypeIds_Integer:
case TypeIds_Number:
case TypeIds_String:
case TypeIds_Symbol:
goto RedoLeft;
default:
goto ReturnFalse;
}
}
RedoLeft:
aLeft = JavascriptConversion::ToPrimitive<JavascriptHint::None>(aLeft, requestContext);
leftType = JavascriptOperators::GetTypeId(aLeft);
redoCount++;
goto Redo;
RedoRight:
aRight = JavascriptConversion::ToPrimitive<JavascriptHint::None>(aRight, requestContext);
rightType = JavascriptOperators::GetTypeId(aRight);
redoCount++;
goto Redo;
CompareDoubles:
*value = dblLeft == dblRight;
return TRUE;
ReturnFalse:
*value = FALSE;
return TRUE;
ReturnTrue:
*value = TRUE;
return TRUE;
}
RecyclableObject* RecyclableObject::ToObject(ScriptContext * requestContext)
{
AssertMsg(JavascriptOperators::IsObject(this), "bad type object in conversion ToObject");
Assert(!CrossSite::NeedMarshalVar(this, requestContext));
return this;
}
Var RecyclableObject::GetTypeOfString(ScriptContext * requestContext)
{
return requestContext->GetLibrary()->GetUnknownDisplayString();
}
BOOL RecyclableObject::GetRemoteTypeId(TypeId * typeId)
{
return FALSE;
}
DynamicObject* RecyclableObject::GetRemoteObject()
{
return NULL;
}
Var RecyclableObject::GetHostDispatchVar()
{
Assert(FALSE);
return this->GetLibrary()->GetUndefined();
}
JavascriptString* RecyclableObject::GetClassName(ScriptContext * requestContext)
{
// we don't need this when not handling fastDOM.
Assert(0);
return NULL;
}
BOOL RecyclableObject::HasInstance(Var instance, ScriptContext* scriptContext, IsInstInlineCache* inlineCache)
{
JavascriptError::ThrowTypeError(scriptContext, JSERR_Operand_Invalid_NeedFunction, _u("instanceof") /* TODO-ERROR: get arg name - aClass */);
}
} // namespace Js