blob: 26aa97b7888dd911045359338ffd8265a97917da [file]
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Copyright (c) ChakraCore Project Contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#pragma once
namespace Js
{
typedef void (*JsTraceCallback)(void * data);
typedef void (*JsFinalizeCallback)(void * data);
typedef struct _JsGetterSetterInterceptor {
Field(void *) getTrap;
Field(void *) setTrap;
Field(void *) deletePropertyTrap;
Field(void *) enumerateTrap;
Field(void *) ownKeysTrap;
Field(void *) hasTrap;
Field(void *) getOwnPropertyDescriptorTrap;
Field(void *) definePropertyTrap;
Field(void *) initializeTrap;
} JsGetterSetterInterceptor;
class CustomExternalWrapperType sealed : public DynamicType
{
public:
CustomExternalWrapperType(CustomExternalWrapperType * type) : DynamicType(type), jsTraceCallback(type->jsTraceCallback), jsFinalizeCallback(type->jsFinalizeCallback), jsGetterSetterInterceptor(type->jsGetterSetterInterceptor) {}
CustomExternalWrapperType(ScriptContext * scriptContext, JsTraceCallback traceCallback, JsFinalizeCallback finalizeCallback, RecyclableObject * prototype);
JsTraceCallback GetJsTraceCallback() const { return this->jsTraceCallback; }
JsFinalizeCallback GetJsFinalizeCallback() const { return this->jsFinalizeCallback; }
JsGetterSetterInterceptor * GetJsGetterSetterInterceptor() const { return this->jsGetterSetterInterceptor; }
private:
FieldNoBarrier(JsTraceCallback const) jsTraceCallback;
FieldNoBarrier(JsFinalizeCallback const) jsFinalizeCallback;
FieldNoBarrier(JsGetterSetterInterceptor *) jsGetterSetterInterceptor;
};
class CustomExternalWrapperObject : public DynamicObject
{
protected:
DEFINE_VTABLE_CTOR(CustomExternalWrapperObject, DynamicObject);
DEFINE_MARSHAL_OBJECT_TO_SCRIPT_CONTEXT(CustomExternalWrapperObject);
public:
typedef enum SetPropertyTrapKind {
SetItemOnTaggedNumberKind,
SetPropertyOnTaggedNumberKind,
SetPropertyKind,
SetItemKind,
SetPropertyWPCacheKind,
} SetPropertyTrapKind;
enum KeysTrapKind {
GetOwnEnumerablePropertyNamesKind,
GetOwnPropertyNamesKind,
GetOwnPropertySymbolKind,
EnumerableKeysKind,
KeysKind
};
CustomExternalWrapperObject(CustomExternalWrapperType * type, void *data, uint inlineSlotSize);
CustomExternalWrapperObject(CustomExternalWrapperObject* instance, bool deepCopy);
BOOL IsObjectAlive();
BOOL VerifyObjectAlive();
static CustomExternalWrapperObject * Create(void *data, uint inlineSlotSize, JsTraceCallback traceCallback, JsFinalizeCallback finalizeCallback, JsGetterSetterInterceptor ** getterSetterInterceptor, RecyclableObject * prototype, ScriptContext *scriptContext);
virtual CustomExternalWrapperObject* Copy(bool deepCopy) override;
static BOOL GetOwnPropertyDescriptor(RecyclableObject * obj, PropertyId propertyId, ScriptContext* requestContext, PropertyDescriptor* propertyDescriptor);
static BOOL DefineOwnPropertyDescriptor(RecyclableObject * obj, PropertyId propId, const PropertyDescriptor& descriptor, bool throwOnError, ScriptContext* requestContext);
CustomExternalWrapperType * GetExternalType() const { return (CustomExternalWrapperType *)this->GetType(); }
BOOL EnsureInitialized(ScriptContext* requestContext);
void Mark(Recycler * recycler) override;
void Finalize(bool isShutdown) override;
void Dispose(bool isShutdown) override;
bool HasReadOnlyPropertiesInvisibleToTypeHandler() override { return true; }
bool IsInitialized() const { return this->initialized; }
virtual PropertyQueryFlags GetPropertyQuery(Var originalInstance, PropertyId propertyId, Var * value, PropertyValueInfo * info, ScriptContext * requestContext) override;
virtual PropertyQueryFlags GetPropertyQuery(Var originalInstance, JavascriptString* propertyNameString, Var * value, PropertyValueInfo * info, ScriptContext * requestContext) override;
virtual PropertyQueryFlags GetPropertyReferenceQuery(Var originalInstance, PropertyId propertyId, Var * value, PropertyValueInfo * info, ScriptContext * requestContext) override;
virtual PropertyQueryFlags HasPropertyQuery(PropertyId propertyId, _Inout_opt_ PropertyValueInfo* info) override;
virtual BOOL SetProperty(PropertyId propertyId, Var value, PropertyOperationFlags flags, PropertyValueInfo * info) override;
virtual BOOL SetProperty(JavascriptString* propertyNameString, Var value, PropertyOperationFlags flags, PropertyValueInfo * info) override;
virtual BOOL EnsureNoRedeclProperty(PropertyId propertyId) override;
virtual BOOL InitProperty(PropertyId propertyId, Var value, PropertyOperationFlags flags = PropertyOperation_None, PropertyValueInfo* info = nullptr) override;
virtual BOOL DeleteProperty(PropertyId propertyId, PropertyOperationFlags flags) override;
virtual BOOL DeleteProperty(JavascriptString *propertyNameString, PropertyOperationFlags flags) override;
virtual PropertyQueryFlags HasItemQuery(uint32 index) override;
virtual BOOL HasOwnItem(uint32 index) override;
virtual PropertyQueryFlags GetItemQuery(Var originalInstance, uint32 index, Var * value, ScriptContext * requestContext) override;
virtual PropertyQueryFlags GetItemReferenceQuery(Var originalInstance, uint32 index, Var * value, ScriptContext * requestContext) override;
virtual BOOL SetItem(uint32 index, Var value, PropertyOperationFlags flags) override;
virtual BOOL DeleteItem(uint32 index, PropertyOperationFlags flags) override;
virtual BOOL GetEnumerator(JavascriptStaticEnumerator * enumerator, EnumeratorFlags flags, ScriptContext * requestContext, EnumeratorCache * enumeratorCache = nullptr) override;
virtual BOOL Equals(_In_ Var other, _Out_ BOOL* value, ScriptContext* requestContext) override;
virtual BOOL StrictEquals(_In_ Var other, _Out_ BOOL* value, ScriptContext* requestContext) override;
virtual DynamicType* DuplicateType() override;
virtual void SetPrototype(RecyclableObject* newPrototype) override;
void PropertyIdFromInt(uint32 index, PropertyRecord const** propertyRecord);
template <class Fn, class GetPropertyNameFunc>
BOOL SetPropertyTrap(Var receiver, SetPropertyTrapKind setPropertyTrapKind, GetPropertyNameFunc getPropertyName, Var newValue, ScriptContext * requestContext, PropertyOperationFlags propertyOperationFlags, BOOL skipPrototypeCheck, Fn fn, Js::PropertyValueInfo* info);
BOOL SetPropertyTrap(Var receiver, SetPropertyTrapKind setPropertyTrapKind, JavascriptString * propertyString, Var newValue, ScriptContext * requestContext, PropertyOperationFlags propertyOperationFlags, Js::PropertyValueInfo* info);
void * GetSlotData() const;
void SetSlotData(void * data);
int GetInlineSlotSize() const;
void* GetInlineSlots() const;
virtual PropertyId GetPropertyId(PropertyIndex index) override { if (!EnsureInitialized(GetScriptContext())) { return Constants::NoProperty; } return DynamicObject::GetPropertyId(index); }
virtual PropertyId GetPropertyId(BigPropertyIndex index) override { if (!EnsureInitialized(GetScriptContext())) { return Constants::NoProperty; } return DynamicObject::GetPropertyId(index); }
virtual BOOL HasOwnProperty(PropertyId propertyId) override { if (!EnsureInitialized(GetScriptContext())) { return FALSE; } return DynamicObject::HasOwnProperty(propertyId); }
virtual DescriptorFlags GetSetter(PropertyId propertyId, Var *setterValue, PropertyValueInfo* info, ScriptContext* requestContext) override { if (!EnsureInitialized(GetScriptContext())) { return None; } return DynamicObject::GetSetter(propertyId, setterValue, info, requestContext); }
virtual DescriptorFlags GetSetter(JavascriptString* propertyNameString, Var *setterValue, PropertyValueInfo* info, ScriptContext* requestContext) override { if (!EnsureInitialized(GetScriptContext())) { return None; } return DynamicObject::GetSetter(propertyNameString, setterValue, info, requestContext); }
virtual BOOL SetPropertyWithAttributes(PropertyId propertyId, Var value, PropertyAttributes attributes, PropertyValueInfo* info, PropertyOperationFlags flags = PropertyOperation_None, SideEffects possibleSideEffects = SideEffects_Any) override { if (!EnsureInitialized(GetScriptContext())) { return FALSE; } return DynamicObject::SetPropertyWithAttributes(propertyId, value, attributes, info, flags, possibleSideEffects); }
#if ENABLE_FIXED_FIELDS
virtual BOOL IsFixedProperty(PropertyId propertyId) override { if (!EnsureInitialized(GetScriptContext())) { return FALSE; } return DynamicObject::IsFixedProperty(propertyId); }
#endif
virtual DescriptorFlags GetItemSetter(uint32 index, Var* setterValue, ScriptContext* requestContext) override { if (!EnsureInitialized(GetScriptContext())) { return None; } return DynamicObject::GetItemSetter(index, setterValue, requestContext); }
virtual BOOL ToPrimitive(JavascriptHint hint, Var* result, ScriptContext * requestContext) override { if (!EnsureInitialized(GetScriptContext())) { return FALSE; } return DynamicObject::ToPrimitive(hint, result, requestContext); }
virtual BOOL SetAccessors(PropertyId propertyId, Var getter, Var setter, PropertyOperationFlags flags = PropertyOperation_None) override { if (!EnsureInitialized(GetScriptContext())) { return FALSE; } return DynamicObject::SetAccessors(propertyId, getter, setter, flags); }
_Check_return_ _Success_(return) virtual BOOL GetAccessors(PropertyId propertyId, _Outptr_result_maybenull_ Var* getter, _Outptr_result_maybenull_ Var* setter, ScriptContext* requestContext) override { if (!EnsureInitialized(GetScriptContext())) { return FALSE; } return DynamicObject::GetAccessors(propertyId, getter, setter, requestContext); }
virtual BOOL IsWritable(PropertyId propertyId) override { if (!EnsureInitialized(GetScriptContext())) { return FALSE; } return DynamicObject::IsWritable(propertyId); }
virtual BOOL IsConfigurable(PropertyId propertyId) override { if (!EnsureInitialized(GetScriptContext())) { return FALSE; } return DynamicObject::IsConfigurable(propertyId); }
virtual BOOL IsEnumerable(PropertyId propertyId) override { if (!EnsureInitialized(GetScriptContext())) { return FALSE; } return DynamicObject::IsEnumerable(propertyId); }
virtual BOOL SetEnumerable(PropertyId propertyId, BOOL value) override { if (!EnsureInitialized(GetScriptContext())) { return FALSE; } return DynamicObject::SetEnumerable(propertyId, value); }
virtual BOOL SetWritable(PropertyId propertyId, BOOL value) override { if (!EnsureInitialized(GetScriptContext())) { return FALSE; } return DynamicObject::SetWritable(propertyId, value); }
virtual BOOL SetConfigurable(PropertyId propertyId, BOOL value) override { if (!EnsureInitialized(GetScriptContext())) { return FALSE; } return DynamicObject::SetConfigurable(propertyId, value); }
virtual BOOL SetAttributes(PropertyId propertyId, PropertyAttributes attributes) override { if (!EnsureInitialized(GetScriptContext())) { return FALSE; } return DynamicObject::SetAttributes(propertyId, attributes); }
virtual BOOL Seal() override { if (!EnsureInitialized(GetScriptContext())) { return FALSE; } return DynamicObject::Seal(); }
virtual BOOL Freeze() override { if (!EnsureInitialized(GetScriptContext())) { return FALSE; } return DynamicObject::Freeze(); }
#if DBG
virtual bool CanStorePropertyValueDirectly(PropertyId propertyId, bool allowLetConst) override { if (!EnsureInitialized(GetScriptContext())) { return FALSE; } return DynamicObject::CanStorePropertyValueDirectly(propertyId, allowLetConst); }
#endif
virtual void RemoveFromPrototype(ScriptContext * requestContext, bool * allProtoCachesInvalidated) override { if (!EnsureInitialized(GetScriptContext())) { return; } return DynamicObject::RemoveFromPrototype(requestContext, allProtoCachesInvalidated); }
virtual void AddToPrototype(ScriptContext * requestContext, bool * allProtoCachesInvalidated) override { if (!EnsureInitialized(GetScriptContext())) { return; } return DynamicObject::AddToPrototype(requestContext, allProtoCachesInvalidated); }
virtual bool ClearProtoCachesWereInvalidated() override { if (!EnsureInitialized(GetScriptContext())) { return FALSE; } return DynamicObject::ClearProtoCachesWereInvalidated(); }
private:
enum class SlotType {
Inline,
External
};
Field(bool) initialized = false;
Field(SlotType) slotType;
union SlotInfo
{
Field(void *) slot;
Field(uint) inlineSlotSize;
SlotInfo()
{
memset((void*)this, 0, sizeof(SlotInfo));
}
};
Field(SlotInfo) u;
Var GetValueFromDescriptor(Var instance, PropertyDescriptor propertyDescriptor, ScriptContext * requestContext);
Var GetName(ScriptContext* requestContext, PropertyId propertyId, Var * isPropertyNameNumeric, Var * propertyNameNumericValue);
BOOL GetPropertyDescriptorTrap(PropertyId propertyId, PropertyDescriptor * resultDescriptor, ScriptContext * requestContext);
template <class Fn, class GetPropertyNameFunc>
BOOL GetPropertyTrap(Var instance, PropertyDescriptor * propertyDescriptor, Fn fn, GetPropertyNameFunc getPropertyName, ScriptContext * requestContext, Js::PropertyValueInfo* info);
template <class Fn, class GetPropertyNameFunc>
BOOL HasPropertyTrap(Fn fn, GetPropertyNameFunc getPropertyName, Js::PropertyValueInfo* info);
template <class Fn>
void GetOwnPropertyKeysHelper(ScriptContext * scriptContext, RecyclableObject * trapResultArray, uint32 len, JavascriptArray * trapResult,
JsUtil::BaseDictionary<PropertyId, bool, Memory::ArenaAllocator>& targetToTrapResultMap, Fn fn)
{
Var element = nullptr;
const PropertyRecord* propertyRecord;
uint32 trapResultIndex = 0;
PropertyId propertyId;
for (uint32 i = 0; i < len; i++)
{
if (!JavascriptOperators::GetItem(trapResultArray, i, &element, scriptContext) || // missing
!(VarIs<JavascriptString>(element) || VarIs<JavascriptSymbol>(element))) // neither String nor Symbol
{
JavascriptError::ThrowTypeError(scriptContext, JSERR_InconsistentTrapResult, _u("ownKeys"));
}
JavascriptConversion::ToPropertyKey(element, scriptContext, &propertyRecord, nullptr);
propertyId = propertyRecord->GetPropertyId();
if (propertyId != Constants::NoProperty)
{
if (targetToTrapResultMap.AddNew(propertyId, true) == -1)
{
JavascriptError::ThrowTypeError(scriptContext, JSERR_InconsistentTrapResult);
}
}
if (fn(propertyRecord))
{
trapResult->DirectSetItemAt(trapResultIndex++, element);
}
}
}
#if ENABLE_TTD
public:
virtual TTD::NSSnapObjects::SnapObjectType GetSnapTag_TTD() const override;
virtual void ExtractSnapObjectDataInto(TTD::NSSnapObjects::SnapObject* objData, TTD::SlabAllocator& alloc) override;
#endif
};
template <> inline bool VarIsImpl<CustomExternalWrapperObject>(RecyclableObject* obj)
{
return (VirtualTableInfo<CustomExternalWrapperObject>::HasVirtualTable(obj)) ||
(VirtualTableInfo<Js::CrossSiteObject<CustomExternalWrapperObject>>::HasVirtualTable(obj));
}
}
#ifndef __APPLE__ // TODO: for some reason on OSX builds this initialization happens before PAL initialization
AUTO_REGISTER_RECYCLER_OBJECT_DUMPER(Js::CustomExternalWrapperType, &Js::Type::DumpObjectFunction);
AUTO_REGISTER_RECYCLER_OBJECT_DUMPER(Js::CustomExternalWrapperObject, &Js::RecyclableObject::DumpObjectFunction);
#endif