blob: 67eec5d42e0493f8673d6f5d7f5f164358469a70 [file]
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#include "RuntimeLibraryPch.h"
namespace Js
{
ModuleRoot::ModuleRoot(DynamicType * type):
RootObjectBase(type)
{
}
void ModuleRoot::SetHostObject(ModuleID moduleID, HostObjectBase * hostObject)
{
this->moduleID = moduleID;
__super::SetHostObject(hostObject);
}
PropertyQueryFlags ModuleRoot::HasPropertyQuery(PropertyId propertyId)
{
if (JavascriptConversion::PropertyQueryFlagsToBoolean(DynamicObject::HasPropertyQuery(propertyId)))
{
return Property_Found;
}
else if (this->hostObject && JavascriptOperators::HasProperty(this->hostObject, propertyId))
{
return Property_Found;
}
return this->GetLibrary()->GetGlobalObject()->GlobalObject::HasPropertyQuery(propertyId);
}
BOOL ModuleRoot::EnsureProperty(PropertyId propertyId)
{
if (!RootObjectBase::HasOwnPropertyCheckNoRedecl(propertyId))
{
// Cannot pass the extra PropertyOperation_PreInit flag, because module root uses SetSlot directly from
// SetRootProperty. If the property is not yet initialized SetSlot will (correctly) assert.
this->InitProperty(propertyId, this->GetLibrary()->GetUndefined(), (PropertyOperationFlags)(PropertyOperation_SpecialValue | PropertyOperation_NonFixedValue));
}
return true;
}
BOOL ModuleRoot::HasRootProperty(PropertyId propertyId)
{
if (__super::HasRootProperty(propertyId))
{
return TRUE;
}
else if (this->hostObject && JavascriptOperators::HasProperty(this->hostObject, propertyId))
{
return TRUE;
}
return this->GetLibrary()->GetGlobalObject()->GlobalObject::HasRootProperty(propertyId);
}
BOOL ModuleRoot::HasOwnProperty(PropertyId propertyId)
{
return JavascriptConversion::PropertyQueryFlagsToBoolean(DynamicObject::HasPropertyQuery(propertyId));
}
PropertyQueryFlags ModuleRoot::GetPropertyQuery(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
{
PropertyIndex index = GetPropertyIndex(propertyId);
if (index != Constants::NoSlot)
{
*value = this->GetSlot(index);
if (info) // Avoid testing IsWritable if info not being queried
{
PropertyValueInfo::Set(info, this, index, IsWritable(propertyId) ? PropertyWritable : PropertyNone);
if (this->IsFixedProperty(propertyId))
{
PropertyValueInfo::DisableStoreFieldCache(info);
}
}
return Property_Found;
}
if (this->hostObject && JavascriptOperators::GetProperty(this->hostObject, propertyId, value, requestContext))
{
return Property_Found;
}
//
// Try checking the global object
// if the module root doesn't have the property and the host object also doesn't have it
//
GlobalObject* globalObj = this->GetLibrary()->GetGlobalObject();
return JavascriptConversion::BooleanToPropertyQueryFlags(globalObj->GlobalObject::GetPropertyQuery(originalInstance, propertyId, value, NULL, requestContext));
}
BOOL ModuleRoot::GetRootProperty(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
{
PropertyIndex index = GetRootPropertyIndex(propertyId);
if (index != Constants::NoSlot)
{
*value = this->GetSlot(index);
if (info) // Avoid testing IsWritable if info not being queried
{
PropertyValueInfo::Set(info, this, index, IsWritable(propertyId) ? PropertyWritable : PropertyNone);
if (this->IsFixedProperty(propertyId))
{
PropertyValueInfo::DisableStoreFieldCache(info);
}
}
return TRUE;
}
if (this->hostObject && JavascriptOperators::GetProperty(this->hostObject, propertyId, value, requestContext))
{
return TRUE;
}
//
// Try checking the global object
// if the module root doesn't have the property and the host object also doesn't have it
//
GlobalObject* globalObj = this->GetLibrary()->GetGlobalObject();
return globalObj->GlobalObject::GetRootProperty(originalInstance, propertyId, value, NULL, requestContext);
}
PropertyQueryFlags ModuleRoot::GetPropertyQuery(Var originalInstance, JavascriptString* propertyNameString, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
{
PropertyRecord const * propertyRecord;
this->GetScriptContext()->GetOrAddPropertyRecord(propertyNameString->GetString(), propertyNameString->GetLength(), &propertyRecord);
return ModuleRoot::GetPropertyQuery(originalInstance, propertyRecord->GetPropertyId(), value, info, requestContext);
}
BOOL ModuleRoot::GetAccessors(PropertyId propertyId, Var* getter, Var* setter, ScriptContext * requestContext)
{
if (DynamicObject::GetAccessors(propertyId, getter, setter, requestContext))
{
return TRUE;
}
if (this->hostObject)
{
return this->hostObject->GetAccessors(propertyId, getter, setter, requestContext);
}
// Try checking the global object
GlobalObject* globalObj = this->GetLibrary()->GetGlobalObject();
return globalObj->GlobalObject::GetAccessors(propertyId, getter, setter, requestContext);
}
PropertyQueryFlags ModuleRoot::GetPropertyReferenceQuery(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info,
ScriptContext* requestContext)
{
PropertyIndex index = GetPropertyIndex(propertyId);
if (index != Constants::NoSlot)
{
*value = this->GetSlot(index);
if (info) // Avoid testing IsWritable if info not being queried
{
PropertyValueInfo::Set(info, this, index, IsWritable(propertyId) ? PropertyWritable : PropertyNone);
if (this->IsFixedProperty(propertyId))
{
PropertyValueInfo::DisableStoreFieldCache(info);
}
}
return Property_Found;
}
if (this->hostObject && JavascriptOperators::GetPropertyReference(this->hostObject, propertyId, value, requestContext))
{
return Property_NotFound;
}
//
// Try checking the global object
// if the module root doesn't have the property and the host object also doesn't have it
//
GlobalObject* globalObj = this->GetLibrary()->GetGlobalObject();
return globalObj->GlobalObject::GetPropertyReferenceQuery(originalInstance, propertyId, value, NULL, requestContext);
}
BOOL ModuleRoot::GetRootPropertyReference(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info,
ScriptContext* requestContext)
{
PropertyIndex index = GetRootPropertyIndex(propertyId);
if (index != Constants::NoSlot)
{
*value = this->GetSlot(index);
if (info) // Avoid testing IsWritable if info not being queried
{
PropertyValueInfo::Set(info, this, index, IsWritable(propertyId) ? PropertyWritable : PropertyNone);
if (this->IsFixedProperty(propertyId))
{
PropertyValueInfo::DisableStoreFieldCache(info);
}
}
return TRUE;
}
if (this->hostObject && JavascriptOperators::GetPropertyReference(this->hostObject, propertyId, value, requestContext))
{
return TRUE;
}
//
// Try checking the global object
// if the module root doesn't have the property and the host object also doesn't have it
//
GlobalObject* globalObj = this->GetLibrary()->GetGlobalObject();
return globalObj->GlobalObject::GetRootPropertyReference(originalInstance, propertyId, value, NULL, requestContext);
}
BOOL ModuleRoot::SetProperty(PropertyId propertyId, Var value, PropertyOperationFlags flags, PropertyValueInfo* info)
{
PropertyIndex index = GetPropertyIndex(propertyId);
if (index != Constants::NoSlot)
{
if (this->IsWritable(propertyId) == FALSE)
{
JavascriptError::ThrowCantAssignIfStrictMode(flags, this->GetScriptContext());
if (!this->IsFixedProperty(propertyId))
{
PropertyValueInfo::Set(info, this, index, PropertyNone); // Try to cache property info even if not writable
}
else
{
PropertyValueInfo::SetNoCache(info, this);
}
return FALSE;
}
this->SetSlot(SetSlotArguments(propertyId, index, value));
if (!this->IsFixedProperty(propertyId))
{
PropertyValueInfo::Set(info, this, index);
}
else
{
PropertyValueInfo::SetNoCache(info, this);
}
return TRUE;
}
else if (this->hostObject && this->hostObject->HasProperty(propertyId))
{
return this->hostObject->SetProperty(propertyId, value, flags, NULL);
}
//
// Try checking the global object
// if the module root doesn't have the property and the host object also doesn't have it
//
GlobalObject* globalObj = this->GetLibrary()->GetGlobalObject();
BOOL setAttempted = TRUE;
if (globalObj->SetExistingProperty(propertyId, value, NULL, &setAttempted))
{
return TRUE;
}
//
// Set was attempted. But the set operation returned false.
// This happens, when the property is read only.
// In those scenarios, we should be setting the property with default attributes
//
if (setAttempted)
{
return FALSE;
}
return DynamicObject::SetProperty(propertyId, value, flags, info);
}
BOOL ModuleRoot::SetRootProperty(PropertyId propertyId, Var value, PropertyOperationFlags flags, PropertyValueInfo* info)
{
PropertyIndex index = GetRootPropertyIndex(propertyId);
if (index != Constants::NoSlot)
{
if (this->IsWritable(propertyId) == FALSE)
{
JavascriptError::ThrowCantAssignIfStrictMode(flags, this->GetScriptContext());
if (!this->IsFixedProperty(propertyId))
{
PropertyValueInfo::Set(info, this, index, PropertyNone); // Try to cache property info even if not writable
}
else
{
PropertyValueInfo::SetNoCache(info, this);
}
return FALSE;
}
this->SetSlot(SetSlotArgumentsRoot(propertyId, true, index, value));
if (!this->IsFixedProperty(propertyId))
{
PropertyValueInfo::Set(info, this, index);
}
else
{
PropertyValueInfo::SetNoCache(info, this);
}
return TRUE;
}
else if (this->hostObject && this->hostObject->HasProperty(propertyId))
{
return this->hostObject->SetProperty(propertyId, value, flags, NULL);
}
//
// Try checking the global object
// if the module root doesn't have the property and the host object also doesn't have it
//
GlobalObject* globalObj = this->GetLibrary()->GetGlobalObject();
BOOL setAttempted = TRUE;
if (globalObj->SetExistingRootProperty(propertyId, value, NULL, &setAttempted))
{
return TRUE;
}
//
// Set was attempted. But the set operation returned false.
// This happens, when the property is read only.
// In those scenarios, we should be setting the property with default attributes
//
if (setAttempted)
{
return FALSE;
}
return __super::SetRootProperty(propertyId, value, (PropertyOperationFlags)(flags | PropertyOperation_NonFixedValue), info);
}
BOOL ModuleRoot::SetProperty(JavascriptString* propertyNameString, Var value, PropertyOperationFlags flags, PropertyValueInfo* info)
{
PropertyRecord const * propertyRecord;
this->GetScriptContext()->GetOrAddPropertyRecord(propertyNameString->GetString(), propertyNameString->GetLength(), &propertyRecord);
return ModuleRoot::SetProperty(propertyRecord->GetPropertyId(), value, (PropertyOperationFlags)(flags | PropertyOperation_NonFixedValue), info);
}
BOOL ModuleRoot::InitPropertyScoped(PropertyId propertyId, Var value)
{
return DynamicObject::InitProperty(propertyId, value, PropertyOperation_NonFixedValue);
}
BOOL ModuleRoot::InitFuncScoped(PropertyId propertyId, Var value)
{
// Var binding of functions declared in eval are elided when conflicting
// with global scope let/const variables, so do not actually set the
// property if it exists and is a let/const variable.
bool noRedecl = false;
if (!GetTypeHandler()->HasRootProperty(this, propertyId, &noRedecl) || !noRedecl)
{
DynamicObject::InitProperty(propertyId, value, PropertyOperation_NonFixedValue);
}
return true;
}
BOOL ModuleRoot::SetAccessors(PropertyId propertyId, Var getter, Var setter, PropertyOperationFlags flags)
{
if (DynamicObject::SetAccessors(propertyId, getter, setter, flags))
{
return TRUE;
}
if (this->hostObject)
{
return this->hostObject->SetAccessors(propertyId, getter, setter, flags);
}
//
// Try checking the global object
// if the module root doesn't have the property and the host object also doesn't have it
//
GlobalObject* globalObj = GetScriptContext()->GetGlobalObject();
return globalObj->GlobalObject::SetAccessors(propertyId, getter, setter, flags);
}
BOOL ModuleRoot::DeleteProperty(PropertyId propertyId, PropertyOperationFlags flags)
{
int index = GetPropertyIndex(propertyId);
if (index != Constants::NoSlot)
{
return FALSE;
}
else if (this->hostObject && this->hostObject->HasProperty(propertyId))
{
return this->hostObject->DeleteProperty(propertyId, flags);
}
return this->GetLibrary()->GetGlobalObject()->GlobalObject::DeleteProperty(propertyId, flags);
}
BOOL ModuleRoot::DeleteProperty(JavascriptString *propertyNameString, PropertyOperationFlags flags)
{
PropertyRecord const *propertyRecord = nullptr;
if (JavascriptOperators::ShouldTryDeleteProperty(this, propertyNameString, &propertyRecord))
{
Assert(propertyRecord);
return DeleteProperty(propertyRecord->GetPropertyId(), flags);
}
return TRUE;
}
BOOL ModuleRoot::DeleteRootProperty(PropertyId propertyId, PropertyOperationFlags flags)
{
int index = GetRootPropertyIndex(propertyId);
if (index != Constants::NoSlot)
{
return FALSE;
}
else if (this->hostObject && this->hostObject->HasProperty(propertyId))
{
return this->hostObject->DeleteProperty(propertyId, flags);
}
return this->GetLibrary()->GetGlobalObject()->GlobalObject::DeleteRootProperty(propertyId, flags);
}
PropertyQueryFlags ModuleRoot::HasItemQuery(uint32 index)
{
return JavascriptConversion::BooleanToPropertyQueryFlags(JavascriptConversion::PropertyQueryFlagsToBoolean(DynamicObject::HasItemQuery(index))
|| (this->hostObject && JavascriptOperators::HasItem(this->hostObject, index)));
}
BOOL ModuleRoot::HasOwnItem(uint32 index)
{
return JavascriptConversion::PropertyQueryFlagsToBoolean(DynamicObject::HasItemQuery(index));
}
PropertyQueryFlags ModuleRoot::GetItemReferenceQuery(Var originalInstance, uint32 index, Var* value, ScriptContext * requestContext)
{
if (JavascriptConversion::PropertyQueryFlagsToBoolean(DynamicObject::GetItemReferenceQuery(originalInstance, index, value, requestContext)))
{
return Property_Found;
}
if (this->hostObject && JavascriptConversion::PropertyQueryFlagsToBoolean(this->hostObject->GetItemReferenceQuery(originalInstance, index, value, requestContext)))
{
return Property_Found;
}
*value = requestContext->GetMissingItemResult();
return Property_NotFound;
}
BOOL ModuleRoot::SetItem(uint32 index, Var value, PropertyOperationFlags flags)
{
if (DynamicObject::SetItem(index, value, flags))
{
return TRUE;
}
if (this->hostObject)
{
return this->hostObject->SetItem(index, value, flags);
}
return FALSE;
}
PropertyQueryFlags ModuleRoot::GetItemQuery(Var originalInstance, uint32 index, Var* value, ScriptContext * requestContext)
{
if (JavascriptConversion::PropertyQueryFlagsToBoolean(DynamicObject::GetItemQuery(originalInstance, index, value, requestContext)))
{
return Property_Found;
}
if (this->hostObject && this->hostObject->GetItem(originalInstance, index, value, requestContext))
{
return Property_Found;
}
*value = requestContext->GetMissingItemResult();
return Property_NotFound;
}
BOOL ModuleRoot::GetDiagValueString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext)
{
stringBuilder->AppendCppLiteral(_u("{Named Item}"));
return TRUE;
}
BOOL ModuleRoot::GetDiagTypeString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext)
{
stringBuilder->AppendCppLiteral(_u("Object, (Named Item)"));
return TRUE;
}
}