Chakra TypedArray代码实现笔记

发布者:Ox9A82
发布于:2017-08-08 18:26

ArrayBuffer.cpp阅读

对象继承关系

JavascriptArrayBuffer:
ArrayBuffer:
ArrayBufferBase:
DynamicObject:
RecyclableObject:
FinalizableObject
template<> TypedArray<Typed>
TypedArray
TypedArrayBase
ArrayBufferParent
ArrayObject
DynamicObject
RecyclableObject
FinalizableObject

FinalizableObject类

抽象类FinalizableObject,所有Object类的基类

class FinalizableObject
{
public:
    virtual void Finalize(bool isShutdown) = 0;
    virtual void Dispose(bool isShutdown) = 0;
    virtual void Mark(Recycler * recycler) = 0;
    virtual void OnMark() {}
};

RecyclableObject类

//TO DO
RecyclableObject类涉及到内存管理相关的操作

   class RecyclableObject : public FinalizableObject
    {
        
        friend class JavascriptOperators;

    protected:
        Field(Type *) type;
        DEFINE_VTABLE_CTOR_NOBASE(RecyclableObject);

        virtual RecyclableObject* GetPrototypeSpecial();

    public:
        static bool Is(Var aValue);//static方法,传入地址判断是否为RecyclableObject对象
        static RecyclableObject* FromVar(Var varValue);//static方法,传入地址返回对象
        RecyclableObject(Type * type);//构造函数

        ScriptContext* GetScriptContext() const;//返回ScriptContext
        TypeId GetTypeId() const;//返回TypeId
        RecyclableObject* GetPrototype() const;//return prototype;
        JavascriptMethod GetEntryPoint() const;//return entryPoint;
        JavascriptLibrary* GetLibrary() const;// return javascriptLibrary;
        Recycler* GetRecycler() const;// return recycler;
        void SetIsPrototype();

        // Is this object known to have only writable data properties
        // (i.e. no accessors or non-writable properties)?
        bool HasOnlyWritableDataProperties();//查询访问属性

        void ClearWritableDataOnlyDetectionBit();//删除只读属性  propertyTypes &= ~PropertyTypesWritableDataOnlyDetection;
        bool IsWritableDataOnlyDetectionBitSet();

        inline Type * GetType() const { return type; }

        // 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.
        static Var DefaultEntryPoint(RecyclableObject* function, CallInfo callInfo, ...);

        BOOL HasItem(uint32 index);
        BOOL HasProperty(PropertyId propertyId);
        BOOL GetProperty(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext);
        BOOL GetProperty(Var originalInstance, JavascriptString* propertyNameString, Var* value, PropertyValueInfo* info, ScriptContext* requestContext);
        BOOL GetPropertyReference(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext);
        BOOL GetItem(Var originalInstance, uint32 index, Var* value, ScriptContext * requestContext);
        BOOL GetItemReference(Var originalInstance, uint32 index, Var* value, ScriptContext * requestContext);

        virtual PropertyId GetPropertyId(PropertyIndex index) { return Constants::NoProperty; }
        virtual PropertyId GetPropertyId(BigPropertyIndex index) { return Constants::NoProperty; }
        virtual PropertyIndex GetPropertyIndex(PropertyId propertyId) { return Constants::NoSlot; }
        virtual int GetPropertyCount() { return 0; }
        virtual PropertyQueryFlags HasPropertyQuery(PropertyId propertyId);
        virtual BOOL HasOwnProperty( PropertyId propertyId);
        virtual BOOL HasOwnPropertyNoHostObject( PropertyId propertyId);
        virtual BOOL HasOwnPropertyCheckNoRedecl( PropertyId propertyId) { Assert(FALSE); return FALSE; }
        virtual BOOL UseDynamicObjectForNoHostObjectAccess() { return FALSE; }
        virtual DescriptorFlags GetSetter(PropertyId propertyId, Var* setterValue, PropertyValueInfo* info, ScriptContext* requestContext) { return None; }
        virtual DescriptorFlags GetSetter(JavascriptString* propertyNameString, Var* setterValue, PropertyValueInfo* info, ScriptContext* requestContext) { return None; }
        virtual PropertyQueryFlags GetPropertyQuery(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext);
        virtual PropertyQueryFlags GetPropertyQuery(Var originalInstance, JavascriptString* propertyNameString, Var* value, PropertyValueInfo* info, ScriptContext* requestContext);
        virtual BOOL GetInternalProperty(Var instance, PropertyId internalPropertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext);
        virtual BOOL GetAccessors(PropertyId propertyId, Var* getter, Var* setter, ScriptContext * requestContext);
        virtual PropertyQueryFlags GetPropertyReferenceQuery(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext);
        virtual BOOL SetProperty(PropertyId propertyId, Var value, PropertyOperationFlags flags, PropertyValueInfo* info);
        virtual BOOL SetProperty(JavascriptString* propertyNameString, Var value, PropertyOperationFlags flags, PropertyValueInfo* info);
        virtual BOOL SetInternalProperty(PropertyId internalPropertyId, Var value, PropertyOperationFlags flags, PropertyValueInfo* info);
        virtual BOOL InitProperty(PropertyId propertyId, Var value, PropertyOperationFlags flags = PropertyOperation_None, PropertyValueInfo* info = NULL);
        virtual BOOL EnsureProperty(PropertyId propertyId);
        virtual BOOL EnsureNoRedeclProperty(PropertyId propertyId);
        virtual BOOL SetPropertyWithAttributes(PropertyId propertyId, Var value, PropertyAttributes attributes, PropertyValueInfo* info, PropertyOperationFlags flags = PropertyOperation_None, SideEffects possibleSideEffects = SideEffects_Any);
        virtual BOOL InitPropertyScoped(PropertyId propertyId, Var value);
        virtual BOOL InitFuncScoped(PropertyId propertyId, Var value);
        virtual BOOL DeleteProperty(PropertyId propertyId, PropertyOperationFlags flags);
        virtual BOOL DeleteProperty(JavascriptString *propertyNameString, PropertyOperationFlags flags);
        virtual BOOL IsFixedProperty(PropertyId propertyId);
        virtual PropertyQueryFlags HasItemQuery(uint32 index);
        virtual BOOL HasOwnItem(uint32 index);
        virtual PropertyQueryFlags GetItemQuery(Var originalInstance, uint32 index, Var* value, ScriptContext * requestContext);
        virtual PropertyQueryFlags GetItemReferenceQuery(Var originalInstance, uint32 index, Var* value, ScriptContext * requestContext);
        virtual DescriptorFlags GetItemSetter(uint32 index, Var* setterValue, ScriptContext* requestContext) { return None; }
        virtual BOOL SetItem(uint32 index, Var value, PropertyOperationFlags flags);
        virtual BOOL DeleteItem(uint32 index, PropertyOperationFlags flags);
        virtual BOOL GetEnumerator(JavascriptStaticEnumerator * enumerator, EnumeratorFlags flags, ScriptContext* requestContext, ForInCache * forInCache = nullptr);
        virtual BOOL ToPrimitive(JavascriptHint hint, Var* value, ScriptContext * requestContext);
        virtual BOOL SetAccessors(PropertyId propertyId, Var getter, Var setter, PropertyOperationFlags flags = PropertyOperation_None);
        virtual BOOL Equals(__in Var other, __out BOOL* value, ScriptContext* requestContext);
        virtual BOOL StrictEquals(__in Var other, __out BOOL* value, ScriptContext* requestContext);
        virtual BOOL IsWritable(PropertyId propertyId) { return false; }
        virtual BOOL IsConfigurable(PropertyId propertyId) { return false; }
        virtual BOOL IsEnumerable(PropertyId propertyId) { return false; }
        virtual BOOL IsExtensible() { return false; }
        virtual BOOL IsProtoImmutable() const { return false; }
        virtual BOOL PreventExtensions() { return false; };     // Sets [[Extensible]] flag of instance to false
        virtual void ThrowIfCannotDefineProperty(PropertyId propId, const PropertyDescriptor& descriptor);
        virtual void ThrowIfCannotGetOwnPropertyDescriptor(PropertyId propId) {}
        virtual BOOL GetDefaultPropertyDescriptor(PropertyDescriptor& descriptor);
        virtual BOOL Seal() { return false; }                   // Seals the instance, no additional property can be added or deleted
        virtual BOOL Freeze() { return false; }                 // Freezes the instance, no additional property can be added or deleted or written
        virtual BOOL IsSealed() { return false; }
        virtual BOOL IsFrozen() { return false; }
        virtual BOOL SetWritable(PropertyId propertyId, BOOL value) { return false; }
        virtual BOOL SetConfigurable(PropertyId propertyId, BOOL value) { return false; }
        virtual BOOL SetEnumerable(PropertyId propertyId, BOOL value) { return false; }
        virtual BOOL SetAttributes(PropertyId propertyId, PropertyAttributes attributes) { return false; }

        virtual BOOL GetSpecialPropertyName(uint32 index, JavascriptString ** propertyName, ScriptContext * requestContext) { return false; }
        virtual uint GetSpecialPropertyCount() const { return 0; }
        virtual PropertyId const * GetSpecialPropertyIds() const { return nullptr; }
        virtual RecyclableObject* GetThisObjectOrUnWrap(); // Due to the withScope object there are times we need to unwrap

        virtual BOOL HasInstance(Var instance, ScriptContext* scriptContext, IsInstInlineCache* inlineCache = NULL);

        BOOL SkipsPrototype() const;
        BOOL CanHaveInterceptors() const;
        BOOL IsExternal() const;
        // Used only in JsVarToExtension where it may be during dispose and the type is not available
        virtual BOOL IsExternalVirtual() const { return FALSE; }

        virtual RecyclableObject* GetConfigurablePrototype(ScriptContext * requestContext) { return GetPrototype(); }
        virtual Js::JavascriptString* GetClassName(ScriptContext * requestContext);
        virtual RecyclableObject* GetProxiedObjectForHeapEnum();

        virtual void RemoveFromPrototype(ScriptContext * requestContext) { AssertMsg(false, "Shouldn't call this implementation."); }
        virtual void AddToPrototype(ScriptContext * requestContext) { AssertMsg(false, "Shouldn't call this implementation."); }
        virtual void SetPrototype(RecyclableObject* newPrototype) { AssertMsg(false, "Shouldn't call this implementation."); }

        virtual BOOL ToString(Js::Var* value, Js::ScriptContext* scriptContext) { AssertMsg(FALSE, "Do not use this function."); return false; }

        // don't need cross-site: in HostDispatch it's IDispatchEx based; in CustomExternalObject we have marshalling code explicitly.
        virtual Var GetNamespaceParent(Js::Var aChild) { return nullptr; }
        virtual HRESULT QueryObjectInterface(REFIID riid, void **ppvObj);

        virtual BOOL GetDiagValueString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext);
        virtual BOOL GetDiagTypeString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext);
        virtual RecyclableObject* ToObject(ScriptContext * requestContext);
        virtual Var GetTypeOfString(ScriptContext* requestContext);

        // don't need cross-site: only supported in HostDispatch.
        virtual Var InvokePut(Arguments args);
        virtual BOOL GetRemoteTypeId(TypeId* typeId);

        // Only implemented by the HostDispatch object for cross-thread support
        // Only supports a subset of entry points to be called remotely.
        // For a list of supported entry points see the BuiltInOperation enum defined in JscriptInfo.idl
        virtual BOOL InvokeBuiltInOperationRemotely(JavascriptMethod entryPoint, Arguments args, Var* result) { return FALSE; };

        // don't need cross-site: only supported in HostDispatch.
        virtual DynamicObject* GetRemoteObject();

        // don't need cross-site: get the HostDispatch for global object/module root. don't need marshalling.
        virtual Var GetHostDispatchVar();

        virtual RecyclableObject * CloneToScriptContext(ScriptContext* requestContext);

        // If dtor is called, that means that OOM happened (mostly), then the vtable might not be initialized
        // to the base class', so we can't assert.
        virtual void Finalize(bool isShutdown) override {

        }
        virtual void Dispose(bool isShutdown) override {

        }
        virtual void Mark(Recycler *recycler) override { AssertMsg(false, "Mark called on object that isn't TrackableObject"); }

        static uint32 GetOffsetOfType() { return offsetof(RecyclableObject, type); }

        virtual void InvalidateCachedScope() { return; }
        virtual BOOL HasDeferredTypeHandler() const { return false; }

    private:

        bool dtorCalled;
#endif
        friend class LowererMD;
        friend class LowererMDArch;
        friend struct InlineCache;

    private:
        UINT m_heapEnumValidationCookie;
    public:
        void SetHeapEnumValidationCookie(int cookie ) { m_heapEnumValidationCookie = cookie; }
        int GetHeapEnumValidationCookie() { return m_heapEnumValidationCookie; }
#endif
    };

ArrayBufferBase类

ArrayBufferBase类包含一系列关于ArrayBuffer的重要操作,但是这是个抽象类,方法并未实现。
GetByteLength //获取buffer size
GetBuffer //获取buffer地址
IsArrayBuffer //判断类型
IsSharedArrayBuffer //判断类型

比较重要的是提供了static方法AllocWrapper,负责分配VirtualBuffer内存

class ArrayBufferBase : public DynamicObject
    {
    protected:
        #define MAX_ASMJS_ARRAYBUFFER_LENGTH 0x100000000 // 4GB
       
        //这个static函数会以函数指针作为参数传递,与malloc做选择
        template<size_t MaxVirtualSize = MAX_ASMJS_ARRAYBUFFER_LENGTH>
        static void* __cdecl AllocWrapper(DECLSPEC_GUARD_OVERFLOW size_t length);
        //VirtualFree
        static void FreeMemAlloc(Var ptr);

    public:
        virtual void MarshalToScriptContext(Js::ScriptContext * scriptContext) = 0;//pure virtual
        //构造函数,设置isDetached=0
        ArrayBufferBase(DynamicType *type) : DynamicObject(type), isDetached(false) { }
        bool IsDetached() { return isDetached; }

        virtual bool IsArrayBuffer() = 0;//pure virtual
        virtual bool IsSharedArrayBuffer() = 0;//pure virtual
        virtual bool IsWebAssemblyArrayBuffer() { return false; }
        virtual ArrayBuffer * GetAsArrayBuffer() = 0;//pure virtual
        virtual SharedArrayBuffer * GetAsSharedArrayBuffer() { return nullptr; }
        virtual void AddParent(ArrayBufferParent* parent) { }//not defined
        virtual uint32 GetByteLength() const = 0;//pure virtual
        virtual BYTE* GetBuffer() const = 0;//pure virtual
        virtual bool IsValidVirtualBufferLength(uint length) const { return false; };//说明Base类是基类,不可直接使用
        
        //通用的static接口
        static bool Is(Var value);//static 判断类型
        static ArrayBufferBase* FromVar(Var value);//static 类型转换
        // #define offsetof(s,m) ((size_t)&reinterpret_cast<char const volatile&>((((s*)0)->m)))
        static int GetIsDetachedOffset() { return offsetof(ArrayBufferBase, isDetached); }

    protected:
        Field(bool) isDetached;
    };

ArrayBufferBase内存结构,此对象只有一个isDetached成员

Js::ArrayBufferBase
{
    Js::DynamicObject
    isDetached bool
}

ArrayBuffer类

ArrayBuffer类是直接对应于javascript中ArrayBuffer对象

ArrayBuffer方法

ArrayBuffer.isView(arg)
    如果参数是ArrayBuffer的视图实例就返回true,例如 typed array objects 或 DataView。否则返回false。
ArrayBuffer.transfer(oldBuffer [, newByteLength])
    返回一个新的ArrayBuffer,其内容取自oldBuffer的数据,并且根据 newByteLength 的大小来对数据进行截取或者以0扩展。
ArrayBuffer.prototype.slice()
    返回一个新的 ArrayBuffer ,它的内容是这个 ArrayBuffer 的字节副本,从begin(包括),到end(不包括)。如果begin或end是负数,则指的是从数组末尾开始的索引,而不是从头开始。
ArrayBuffer.slice()
    和 ArrayBuffer.prototype.slice()功能一样
    
ArrayBuffer属性
    
ArrayBuffer.length
    ArrayBuffer构造函数的length属性,它的值是1
ArrayBuffer.prototype.constructor
    指定函数,它创建一个对象的原型。其初始值是标准ArrayBuffer内置构造函数。
ArrayBuffer.prototype.byteLength 只读
    数组的字节大小。在数组创建时确定,并且不可变更。只读
内存结构
可以看到对于ArrayBuffer来说,重要的结构在ArrayBuffer对象中都已给出

Js::ArrayBuffer
{
    Js::ArrayBufferBase Js::ArrayBufferBase
    primaryParent   Recycler*
    otherParents    OtherParents*
    buffer          byte*
    bufferLength    uint32
}
class ArrayBuffer : public ArrayBufferBase
    {
    public:
        // we need to install cross-site thunk on the nested array buffer when marshaling
        // typed array.
        DEFINE_VTABLE_CTOR_ABSTRACT(ArrayBuffer, ArrayBufferBase);
    private:
        void DetachBufferFromParent(ArrayBufferParent* parent);
    public:
    
        template <typename FreeFN>
        class ArrayBufferDetachedState : public ArrayBufferDetachedStateBase
        {
        public:
            FreeFN* freeFunction;

            ArrayBufferDetachedState(BYTE* buffer, uint32 bufferLength, FreeFN* freeFunction, ArrayBufferAllocationType allocationType)
                : ArrayBufferDetachedStateBase(TypeIds_ArrayBuffer, buffer, bufferLength, allocationType),
                freeFunction(freeFunction)
            {}

            virtual void ClearSelfOnly() override
            {
                HeapDelete(this);
            }

            virtual void DiscardState() override
            {
                if (this->buffer != nullptr)
                {
                    freeFunction(this->buffer);
                    this->buffer = nullptr;
                }
                this->bufferLength = 0;
            }

            virtual void Discard() override
            {
                ClearSelfOnly();
            }
        };


        class EntryInfo
        {
        public:
            static FunctionInfo NewInstance;
            static FunctionInfo Slice;
            static FunctionInfo IsView;
            static FunctionInfo GetterByteLength;
            static FunctionInfo GetterSymbolSpecies;
            static FunctionInfo Transfer;
        };

        //newArr = scriptContext->GetLibrary()->CreateArrayBuffer(byteLength);
        static Var NewInstance(RecyclableObject* function, CallInfo callInfo, ...);
        
        //ArrayBuffer.prototype.slice()
        static Var EntrySlice(RecyclableObject* function, CallInfo callInfo, ...);
        
        //ArrayBuffer.isView()
        //https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer/isView
        static Var EntryIsView(RecyclableObject* function, CallInfo callInfo, ...);
        
        //ArrayBuffer.prototype.byteLength
        //https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer/byteLength
        static Var EntryGetterByteLength(RecyclableObject* function, CallInfo callInfo, ...);
        
        static Var EntryGetterSymbolSpecies(RecyclableObject* function, CallInfo callInfo, ...);
        
        //ArrayBuffer.transfer()
        //https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer/transfer
        static Var EntryTransfer(RecyclableObject* function, CallInfo callInfo, ...);
        
        //判定类型
        static bool Is(Var aValue);
        //
        static ArrayBuffer* NewFromDetachedState(DetachedStateBase* state, JavascriptLibrary *library);
        //类型转换
        static ArrayBuffer* FromVar(Var aValue);
    
        //Detach
        virtual ArrayBufferDetachedStateBase* DetachAndGetState();
        virtual uint32 GetByteLength() const override { return bufferLength; }
        virtual BYTE* GetBuffer() const override { return buffer; }

        static int GetByteLengthOffset() { return offsetof(ArrayBuffer, bufferLength); }//ArrayBuffer中bufferLength的offset
        static int GetBufferOffset() { return offsetof(ArrayBuffer, buffer); }//ArrayBuffer中buffer的offset
        
        //设定parent
        virtual void AddParent(ArrayBufferParent* parent) override;
        
        //maximum 2G -1  for amd64
        static const uint32 MaxArrayBufferLength = 0x7FFFFFFF;

        static const uint32 ParentsCleanupThreshold = 1000;

        virtual bool IsValidAsmJsBufferLength(uint length, bool forceCheck = false) { return false; }
        virtual bool IsArrayBuffer() override { return true; }
        virtual bool IsSharedArrayBuffer() override { return false; }
        virtual ArrayBuffer * GetAsArrayBuffer() override { return ArrayBuffer::FromVar(this); }

        static uint32 ToIndex(Var value, int32 errorCode, ScriptContext *scriptContext, uint32 MaxAllowedLength, bool checkSameValueZero = true);

        virtual ArrayBuffer * TransferInternal(DECLSPEC_GUARD_OVERFLOW uint32 newBufferLength) = 0;
    
    protected:
    
        typedef void __cdecl FreeFn(void* ptr);
        
        virtual ArrayBufferDetachedStateBase* CreateDetachedState(BYTE* buffer, DECLSPEC_GUARD_OVERFLOW uint32 bufferLength) = 0;

        static uint32 GetIndexFromVar(Js::Var arg, uint32 length, ScriptContext* scriptContext);

        //In most cases, the ArrayBuffer will only have one parent
        Field(RecyclerWeakReference<ArrayBufferParent>*) primaryParent;

        struct OtherParents :public SList<RecyclerWeakReference<ArrayBufferParent>*, Recycler>
        {
            OtherParents(Recycler* recycler)
                :SList<RecyclerWeakReference<ArrayBufferParent>*, Recycler>(recycler), increasedCount(0)
            {
            }
            Field(uint) increasedCount;
        };

        Field(OtherParents*) otherParents;

        FieldNoBarrier(BYTE*) buffer;      // Points to a heap allocated RGBA buffer, can be null
        Field(uint32) bufferLength;       // Number of bytes allocated
    };

TypedArrayBase

对于TypedArray的常见操作都已经给出,但是是纯虚函数

class TypedArrayBase : public ArrayBufferParent
    {
        friend ArrayBuffer;
        friend ArrayBufferBase;

    public:
        static Var GetDefaultConstructor(Var object, ScriptContext* scriptContext);

        class EntryInfo
        {
        public:
            static FunctionInfo NewInstance;
            static FunctionInfo Set;
            static FunctionInfo Subarray;

            static FunctionInfo From;
            static FunctionInfo Of;
            static FunctionInfo CopyWithin;
            static FunctionInfo Entries;
            static FunctionInfo Every;
            static FunctionInfo Fill;
            static FunctionInfo Filter;
            static FunctionInfo Find;
            static FunctionInfo FindIndex;
            static FunctionInfo ForEach;
            static FunctionInfo IndexOf;
            static FunctionInfo Includes;
            static FunctionInfo Join;
            static FunctionInfo Keys;
            static FunctionInfo LastIndexOf;
            static FunctionInfo Map;
            static FunctionInfo Reduce;
            static FunctionInfo ReduceRight;
            static FunctionInfo Reverse;
            static FunctionInfo Slice;
            static FunctionInfo Some;
            static FunctionInfo Sort;
            static FunctionInfo Values;

            static FunctionInfo GetterBuffer;
            static FunctionInfo GetterByteLength;
            static FunctionInfo GetterByteOffset;
            static FunctionInfo GetterLength;
            static FunctionInfo GetterSymbolToStringTag;
            static FunctionInfo GetterSymbolSpecies;
        };

        TypedArrayBase(ArrayBufferBase* arrayBuffer, uint byteOffset, uint mappedLength, uint elementSize, DynamicType* type);
        
        //创建函数
        static Var NewInstance(RecyclableObject* function, CallInfo callInfo, ...);

        static Var EntrySet(RecyclableObject* function, CallInfo callInfo, ...);
        static Var EntrySubarray(RecyclableObject* function, CallInfo callInfo, ...);
        static Var EntryFrom(RecyclableObject* function, CallInfo callInfo, ...);
        static Var EntryOf(RecyclableObject* function, CallInfo callInfo, ...);
        static Var EntryCopyWithin(RecyclableObject* function, CallInfo callInfo, ...);
        static Var EntryEntries(RecyclableObject* function, CallInfo callInfo, ...);
        static Var EntryEvery(RecyclableObject* function, CallInfo callInfo, ...);
        static Var EntryFill(RecyclableObject* function, CallInfo callInfo, ...);
        static Var EntryFilter(RecyclableObject* function, CallInfo callInfo, ...);
        static Var EntryFind(RecyclableObject* function, CallInfo callInfo, ...);
        static Var EntryFindIndex(RecyclableObject* function, CallInfo callInfo, ...);
        static Var EntryForEach(RecyclableObject* function, CallInfo callInfo, ...);
        static Var EntryIndexOf(RecyclableObject* function, CallInfo callInfo, ...);
        static Var EntryIncludes(RecyclableObject* function, CallInfo callInfo, ...);
        static Var EntryJoin(RecyclableObject* function, CallInfo callInfo, ...);
        static Var EntryKeys(RecyclableObject* function, CallInfo callInfo, ...);
        static Var EntryLastIndexOf(RecyclableObject* function, CallInfo callInfo, ...);
        static Var EntryMap(RecyclableObject* function, CallInfo callInfo, ...);
        static Var EntryReduce(RecyclableObject* function, CallInfo callInfo, ...);
        static Var EntryReduceRight(RecyclableObject* function, CallInfo callInfo, ...);
        static Var EntryReverse(RecyclableObject* function, CallInfo callInfo, ...);
        static Var EntrySlice(RecyclableObject* function, CallInfo callInfo, ...);
        static Var EntrySome(RecyclableObject* function, CallInfo callInfo, ...);
        static Var EntrySort(RecyclableObject* function, CallInfo callInfo, ...);
        static Var EntryValues(RecyclableObject* function, CallInfo callInfo, ...);

        static Var EntryGetterBuffer(RecyclableObject* function, CallInfo callInfo, ...);
        static Var EntryGetterByteLength(RecyclableObject* function, CallInfo callInfo, ...);
        static Var EntryGetterByteOffset(RecyclableObject* function, CallInfo callInfo, ...);
        static Var EntryGetterLength(RecyclableObject* function, CallInfo callInfo, ...);
        static Var EntryGetterSymbolToStringTag(RecyclableObject* function, CallInfo callInfo, ...);
        static Var EntryGetterSymbolSpecies(RecyclableObject* function, CallInfo callInfo, ...);

        virtual DescriptorFlags GetSetter(PropertyId propertyId, Var *setterValue, PropertyValueInfo* info, ScriptContext* requestContext) override;
        virtual DescriptorFlags GetSetter(JavascriptString* propertyNameString, Var *setterValue, PropertyValueInfo* info, ScriptContext* requestContext) override;
        virtual DescriptorFlags GetItemSetter(uint32 index, Var* setterValue, ScriptContext* requestContext) override;
        virtual PropertyQueryFlags HasPropertyQuery(Js::PropertyId propertyId) override;
        virtual BOOL HasOwnProperty(Js::PropertyId propertyId) override;
        virtual PropertyQueryFlags GetPropertyQuery(Js::Var originalInstance, Js::PropertyId propertyId, Js::Var* value, Js::PropertyValueInfo* info, Js::ScriptContext* requestContext) override;
        virtual PropertyQueryFlags GetPropertyQuery(Js::Var originalInstance, Js::JavascriptString* propertyNameString, Js::Var* value, Js::PropertyValueInfo* info, Js::ScriptContext* requestContext) override;
        virtual PropertyQueryFlags GetPropertyReferenceQuery(Js::Var originalInstance, Js::PropertyId propertyId, Js::Var* value, Js::PropertyValueInfo* info, Js::ScriptContext* requestContext) override;
        virtual PropertyQueryFlags HasItemQuery(uint32 index) override;
        virtual BOOL DeleteItem(uint32 index, Js::PropertyOperationFlags flags) override { return false; }
        virtual PropertyQueryFlags GetItemQuery(Js::Var originalInstance, uint32 index, Js::Var* value, Js::ScriptContext * requestContext) override;
        virtual BOOL SetItem(uint32 index, Js::Var value, Js::PropertyOperationFlags flags = PropertyOperation_None) override;
        virtual BOOL SetProperty(Js::PropertyId propertyId, Js::Var value, Js::PropertyOperationFlags flags, Js::PropertyValueInfo* info) override;
        virtual BOOL SetProperty(Js::JavascriptString* propertyNameString, Js::Var value, Js::PropertyOperationFlags flags, Js::PropertyValueInfo* info) override;
        virtual BOOL DeleteProperty(Js::PropertyId propertyId, Js::PropertyOperationFlags flags) override;
        virtual BOOL DeleteProperty(JavascriptString *propertyNameString, Js::PropertyOperationFlags flags) override;
        virtual PropertyQueryFlags GetItemReferenceQuery(Js::Var originalInstance, uint32 index, Js::Var* value, Js::ScriptContext * requestContext) override;
        virtual BOOL GetEnumerator(JavascriptStaticEnumerator * enumerator, EnumeratorFlags flags, ScriptContext* requestContext, ForInCache * forInCache = nullptr) override;
        virtual JavascriptEnumerator * GetIndexEnumerator(EnumeratorFlags flags, ScriptContext * requestContext) override;

        virtual BOOL IsEnumerable(PropertyId propertyId)  override;
        virtual BOOL IsConfigurable(PropertyId propertyId)  override;
        virtual BOOL IsWritable(PropertyId propertyId)  override;
        virtual BOOL SetEnumerable(PropertyId propertyId, BOOL value) override;
        virtual BOOL SetWritable(PropertyId propertyId, BOOL value) override;
        virtual BOOL SetConfigurable(PropertyId propertyId, BOOL value) override;
        virtual BOOL SetAttributes(PropertyId propertyId, PropertyAttributes attributes) override;
        virtual BOOL SetAccessors(PropertyId propertyId, Var getter, Var setter, PropertyOperationFlags flags) override;

        virtual BOOL InitProperty(Js::PropertyId propertyId, Js::Var value, PropertyOperationFlags flags = PropertyOperation_None, Js::PropertyValueInfo* info = NULL) override;
        virtual BOOL SetPropertyWithAttributes(PropertyId propertyId, Var value, PropertyAttributes attributes, PropertyValueInfo* info, PropertyOperationFlags flags = PropertyOperation_None, SideEffects possibleSideEffects = SideEffects_Any) override;
        static BOOL Is(Var aValue);
        static BOOL Is(TypeId typeId);
        static TypedArrayBase* FromVar(Var aValue);
        // Returns false if this is not a TypedArray or it's not detached
        static BOOL IsDetachedTypedArray(Var aValue);
        static HRESULT GetBuffer(Var aValue, ArrayBuffer** outBuffer, uint32* outOffset, uint32* outLength);
        static TypedArrayBase * ValidateTypedArray(Var aValue, ScriptContext *scriptContext, LPCWSTR apiName);
        static TypedArrayBase * ValidateTypedArray(Arguments &args, ScriptContext *scriptContext, LPCWSTR apiName);
        static Var TypedArrayCreate(Var constructor, Arguments *args, uint32 length, ScriptContext *scriptContext);

        //写入操作 pure virtual
        virtual BOOL DirectSetItem(__in uint32 index, __in Js::Var value) = 0;
        virtual BOOL DirectSetItemNoSet(__in uint32 index, __in Js::Var value) = 0;
        virtual Var  DirectGetItem(__in uint32 index) = 0;
        virtual BOOL DirectSetItemNoDetachCheck(__in uint32 index, __in Js::Var value) = 0;
        virtual Var  DirectGetItemNoDetachCheck(__in uint32 index) = 0;

        virtual Var TypedAdd(__in uint32 index, Var second) = 0;
        virtual Var TypedAnd(__in uint32 index, Var second) = 0;
        virtual Var TypedLoad(__in uint32 index) = 0;
        virtual Var TypedOr(__in uint32 index, Var second) = 0;
        virtual Var TypedStore(__in uint32 index, Var second) = 0;
        virtual Var TypedSub(__in uint32 index, Var second) = 0;
        virtual Var TypedXor(__in uint32 index, Var second) = 0;
        virtual Var TypedExchange(__in uint32 index, Var second) = 0;
        virtual Var TypedCompareExchange(__in uint32 index, Var comparand, Var replacementValue) = 0;
        
        //TypedArray.prototype.byteLength
        uint32 GetByteLength() const { return length * BYTES_PER_ELEMENT; }
        //TypedArray.prototype.byteOffset
        uint32 GetByteOffset() const { return byteOffset; }
        uint32 GetBytesPerElement() const { return BYTES_PER_ELEMENT; }
        //TypedArray.prototype.buffer
        byte*  GetByteBuffer() const { return buffer; };
        
        bool IsDetachedBuffer() const { return this->GetArrayBuffer()->IsDetached(); }
        void ClearLengthAndBufferOnDetach();
        static Var CommonSet(Arguments& args);
        static Var CommonSubarray(Arguments& args);

        void SetObject(RecyclableObject* arraySource, uint32 targetLength, uint32 offset = 0);
        void SetObjectNoDetachCheck(RecyclableObject* arraySource, uint32 targetLength, uint32 offset = 0);
        void Set(TypedArrayBase* typedArraySource, uint32 offset = 0);

        virtual BOOL GetDiagValueString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext) override;
        virtual BOOL GetDiagTypeString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext) override;

        static bool TryGetLengthForOptimizedTypedArray(const Var var, uint32 *const lengthRef, TypeId *const typeIdRef);
        BOOL ValidateIndexAndDirectSetItem(__in Js::Var index, __in Js::Var value, __out bool * isNumericIndex);
        uint32 ValidateAndReturnIndex(__in Js::Var index, __out bool * skipOperation, __out bool * isNumericIndex);

        // objectArray support
        virtual BOOL SetItemWithAttributes(uint32 index, Var value, PropertyAttributes attributes) override;

        Var FindMinOrMax(Js::ScriptContext * scriptContext, TypeId typeId, bool findMax);
        template<typename T, bool checkNaNAndNegZero> Var FindMinOrMax(Js::ScriptContext * scriptContext, bool findMax);

        static Var GetKeysEntriesValuesHelper(Arguments& args, ScriptContext *scriptContext, LPCWSTR apiName, JavascriptArrayIteratorKind kind);

        static uint32 GetFromIndex(Var arg, uint32 length, ScriptContext *scriptContext);

    private:
        uint32 GetSourceLength(RecyclableObject* arraySource, uint32 targetLength, uint32 offset);

    protected:
        static Var CreateNewInstanceFromIterator(RecyclableObject *iterator, ScriptContext *scriptContext, uint32 elementSize, PFNCreateTypedArray pfnCreateTypedArray);
        static Var CreateNewInstance(Arguments& args, ScriptContext* scriptContext, uint32 elementSize, PFNCreateTypedArray pfnCreateTypedArray );
        static bool ArrayIteratorPrototypeHasUserDefinedNext(ScriptContext *scriptContext);
        static BOOL CanonicalNumericIndexString(PropertyId propertyId, ScriptContext *scriptContext);
        static BOOL CanonicalNumericIndexString(JavascriptString *propertyString, ScriptContext *scriptContext);

        typedef int(__cdecl* CompareElementsFunction)(void*, const void*, const void*);
        virtual CompareElementsFunction GetCompareElementsFunction() = 0;

        virtual Var Subarray(uint32 begin, uint32 end) = 0;
        Field(int32) BYTES_PER_ELEMENT;
        Field(uint32) byteOffset;
        FieldNoBarrier(BYTE*) buffer;   // beginning of mapped array.

    public:
        static uint32 GetOffsetOfBuffer()  { return offsetof(TypedArrayBase, buffer); }
        static uint32 GetOffsetOfLength()  { return offsetof(TypedArrayBase, length); }

    };

TypedArray

内存结构

Js::TypedArray
{
    Js::TypedArrayBase
    {
        Js::ArrayBufferParent
        {
            Js::ArrayObject
            arrayBuffer         Js::JavascriptArrayBuffer*
        }
        BYTES_PER_ELEMENT       int
        byteOffset      unsigned int
        buffer          unsigned char *
    }
}

TypedArray类复写实现了虚函数和实际的操作方法
并不存在单独的TypedArray类型的类,都是针对模版进行的特例化

    template <typename TypeName, bool clamped = false, bool virtualAllocated = false>
    class TypedArray : public TypedArrayBase
    {
    protected:
        DEFINE_VTABLE_CTOR(TypedArray, TypedArrayBase);
        virtual void MarshalToScriptContext(Js::ScriptContext * scriptContext)
        {
            Assert(this->GetScriptContext() != scriptContext);
            AssertMsg(VirtualTableInfo<TypedArray>::HasVirtualTable(this), "Derived class need to define marshal to script context");

            VirtualTableInfo<Js::CrossSiteObject<TypedArray<TypeName, clamped, virtualAllocated>>>::SetVirtualTable(this);
            ArrayBufferBase* arrayBuffer = this->GetArrayBuffer();
            if (arrayBuffer && !arrayBuffer->IsCrossSiteObject())
            {
                arrayBuffer->MarshalToScriptContext(scriptContext);
            }
        }

        TypedArray(DynamicType *type): TypedArrayBase(nullptr, 0, 0, sizeof(TypeName), type) { buffer = nullptr; }

    public:
        class EntryInfo
        {
        public:
            static FunctionInfo NewInstance;
            static FunctionInfo Set;
        };

        TypedArray(ArrayBufferBase* arrayBuffer, uint32 byteOffset, uint32 mappedLength, DynamicType* type);

        static Var Create(ArrayBufferBase* arrayBuffer, uint32 byteOffSet, uint32 mappedLength, JavascriptLibrary* javascriptLibrary);
        static Var NewInstance(RecyclableObject* function, CallInfo callInfo, ...);

        static Var EntrySet(RecyclableObject* function, CallInfo callInfo, ...);

        Var Subarray(uint32 begin, uint32 end);

        static BOOL Is(Var aValue);
        static TypedArray<TypeName, clamped, virtualAllocated>* FromVar(Var aValue);

        inline Var BaseTypedDirectGetItem(__in uint32 index)
        {
            if (this->IsDetachedBuffer()) // 9.4.5.8 IntegerIndexedElementGet
            {
                JavascriptError::ThrowTypeError(GetScriptContext(), JSERR_DetachedTypedArray);
            }

            if (index < GetLength())
            {
                Assert((index + 1)* sizeof(TypeName)+GetByteOffset() <= GetArrayBuffer()->GetByteLength());
                TypeName* typedBuffer = (TypeName*)buffer;
                return JavascriptNumber::ToVar(typedBuffer[index], GetScriptContext());
            }
            return GetLibrary()->GetUndefined();
        }

        inline Var TypedDirectGetItemWithCheck(__in uint32 index)
        {
            if (this->IsDetachedBuffer()) // 9.4.5.8 IntegerIndexedElementGet
            {
                JavascriptError::ThrowTypeError(GetScriptContext(), JSERR_DetachedTypedArray);
            }

            if (index < GetLength())
            {
                Assert((index + 1)* sizeof(TypeName)+GetByteOffset() <= GetArrayBuffer()->GetByteLength());
                TypeName* typedBuffer = (TypeName*)buffer;
                return JavascriptNumber::ToVarWithCheck(typedBuffer[index], GetScriptContext());
            }
            return GetLibrary()->GetUndefined();
        }

        inline Var BaseTypedDirectGetItemNoDetachCheck(__in uint32 index)
        {
            Assert(!IsDetachedBuffer());
            Assert(index < GetLength());
            Assert((index + 1)* sizeof(TypeName) + GetByteOffset() <= GetArrayBuffer()->GetByteLength());
            TypeName* typedBuffer = (TypeName*)buffer;
            return JavascriptNumber::ToVar(typedBuffer[index], GetScriptContext());
        }

        inline Var DirectGetItemVarCheckNoDetachCheck(__in uint32 index)
        {
            Assert(!IsDetachedBuffer());
            Assert(index < GetLength());
            Assert((index + 1)* sizeof(TypeName) + GetByteOffset() <= GetArrayBuffer()->GetByteLength());
            TypeName* typedBuffer = (TypeName*)buffer;
            return JavascriptNumber::ToVarWithCheck(typedBuffer[index], GetScriptContext());
        }

        inline BOOL DirectSetItemAtRange(TypedArray *fromArray, __in int32 iSrcStart, __in int32 iDstStart, __in uint32 length, TypeName(*convFunc)(Var value, ScriptContext* scriptContext))
        {
            TypeName* dstBuffer = (TypeName*)buffer;
            TypeName* srcBuffer = (TypeName*)fromArray->buffer;
            Assert(srcBuffer && dstBuffer);
            Assert(length <= ArrayBuffer::MaxArrayBufferLength / sizeof(TypeName));
            // caller checks that src and dst index are the same
            Assert(iSrcStart == iDstStart);

            if (this->IsDetachedBuffer() || fromArray->IsDetachedBuffer())
            {
                JavascriptError::ThrowTypeError(GetScriptContext(), JSERR_DetachedTypedArray);
            }

            // Fixup destination start in case it's negative
            uint32 start = iDstStart;
            if (iDstStart < 0)
            {
                if ((int64)(length) + iDstStart < 0)
                {
                    // nothing to do, all index are no-op
                    return true;
                }

                length += iDstStart;
                start = 0;
            }

            uint32 dstLength = UInt32Math::Add(start, length) < GetLength() ? length : GetLength() > start ? GetLength() - start : 0;
            uint32 srcLength = start + length < fromArray->GetLength() ? length : (fromArray->GetLength() > start ? fromArray->GetLength() - start : 0);

            // length is the minimum of length, srcLength and dstLength
            length = length < srcLength ? (length < dstLength ? length : dstLength) : (srcLength < dstLength ? srcLength : dstLength);

            const size_t byteSize = sizeof(TypeName) * length;
            Assert(byteSize >= length); // check for overflow
            js_memcpy_s(dstBuffer + start, dstLength * sizeof(TypeName), srcBuffer + start, byteSize);

            if (dstLength > length)
            {
                TypeName undefinedValue = convFunc(GetLibrary()->GetUndefined(), GetScriptContext());
                for (uint32 i = length; i < dstLength; i++)
                {
                    dstBuffer[i] = undefinedValue;
                }
            }

            return true;
        }

        inline BOOL DirectSetItemAtRange(__in int32 start, __in uint32 length, __in Js::Var value, TypeName(*convFunc)(Var value, ScriptContext* scriptContext))
        {
            if (CrossSite::IsCrossSiteObjectTyped(this))
            {
                return false;
            }
            TypeName typedValue = convFunc(value, GetScriptContext());

            if (this->IsDetachedBuffer()) // 9.4.5.9 IntegerIndexedElementSet
            {
                JavascriptError::ThrowTypeError(GetScriptContext(), JSERR_DetachedTypedArray);
            }
            uint32 newStart = start, newLength = length;

            if (start < 0)
            {
                if ((int64)(length) + start < 0)
                {
                    // nothing to do, all index are no-op
                    return true;
                }
                newStart = 0;
                // fixup the length with the change
                newLength += start;
            }
            if (newStart >= GetLength())
            {
                // If we want to start copying past the length of the array, all index are no-op
                return true;
            }
            if (UInt32Math::Add(newStart, newLength) > GetLength())
            {
                newLength = GetLength() - newStart;
            }

            TypeName* typedBuffer = (TypeName*)buffer;

            if (typedValue == 0 || sizeof(TypeName) == 1)
            {
                const size_t byteSize = sizeof(TypeName) * newLength;
                Assert(byteSize >= newLength); // check for overflow
                memset(typedBuffer + newStart, (int)typedValue, byteSize);
            }
            else
            {
                for (uint32 i = 0; i < newLength; i++)
                {
                    typedBuffer[newStart + i] = typedValue;
                }
            }

            return TRUE;
        }

        inline BOOL BaseTypedDirectSetItem(__in uint32 index, __in Js::Var value, TypeName (*convFunc)(Var value, ScriptContext* scriptContext))
        {
            // This call can potentially invoke user code, and may end up detaching the underlying array (this).
            // Therefore it was brought out and above the IsDetached check
            TypeName typedValue = convFunc(value, GetScriptContext());

            if (this->IsDetachedBuffer()) // 9.4.5.9 IntegerIndexedElementSet
            {
                JavascriptError::ThrowTypeError(GetScriptContext(), JSERR_DetachedTypedArray);
            }

            if (index >= GetLength())
            {
                return FALSE;
            }

            AssertMsg(index < GetLength(), "Trying to set out of bound index for typed array.");
            Assert((index + 1)* sizeof(TypeName) + GetByteOffset() <= GetArrayBuffer()->GetByteLength());
            TypeName* typedBuffer = (TypeName*)buffer;

            typedBuffer[index] = typedValue;

            return TRUE;
        }

        inline BOOL BaseTypedDirectSetItemNoSet(__in uint32 index, __in Js::Var value, TypeName (*convFunc)(Var value, ScriptContext* scriptContext))
        {
            // This call can potentially invoke user code, and may end up detaching the underlying array (this).
            // Therefore it was brought out and above the IsDetached check
            convFunc(value, GetScriptContext());

            if (this->IsDetachedBuffer()) // 9.4.5.9 IntegerIndexedElementSet
            {
                JavascriptError::ThrowTypeError(GetScriptContext(), JSERR_DetachedTypedArray);
            }

            return FALSE;
        }

        inline BOOL BaseTypedDirectSetItemNoDetachCheck(__in uint32 index, __in Js::Var value, TypeName(*convFunc)(Var value, ScriptContext* scriptContext))
        {
            TypeName typedValue = convFunc(value, GetScriptContext());

            // The caller of the function made sure that no IsDetached check required.
            // The caller of the function also made sure that no length check required.

            Assert(!IsDetachedBuffer());
            AssertMsg(index < GetLength(), "Trying to set out of bound index for typed array.");
            Assert((index + 1)* sizeof(TypeName) + GetByteOffset() <= GetArrayBuffer()->GetByteLength());
            TypeName* typedBuffer = (TypeName*)buffer;

            typedBuffer[index] = typedValue;

            return TRUE;
        }


        virtual BOOL DirectSetItem(__in uint32 index, __in Js::Var value) override sealed;
        virtual BOOL DirectSetItemNoSet(__in uint32 index, __in Js::Var value) override sealed;
        virtual Var  DirectGetItem(__in uint32 index) override sealed;
        virtual BOOL DirectSetItemNoDetachCheck(__in uint32 index, __in Js::Var value) override sealed;
        virtual Var  DirectGetItemNoDetachCheck(__in uint32 index) override sealed;
        virtual Var TypedAdd(__in uint32 index, Var second) override;
        virtual Var TypedAnd(__in uint32 index, Var second) override;
        virtual Var TypedLoad(__in uint32 index) override;
        virtual Var TypedOr(__in uint32 index, Var second) override;
        virtual Var TypedStore(__in uint32 index, Var second) override;
        virtual Var TypedSub(__in uint32 index, Var second) override;
        virtual Var TypedXor(__in uint32 index, Var second) override;
        virtual Var TypedExchange(__in uint32 index, Var second) override;
        virtual Var TypedCompareExchange(__in uint32 index, Var comparand, Var replacementValue) override;

        static BOOL DirectSetItem(__in TypedArray* arr, __in uint32 index, __in Js::Var value)
        {
            AssertMsg(arr != nullptr, "Array shouldn't be nullptr.");

            return arr->DirectSetItem(index, value);
        }

    protected:
        CompareElementsFunction GetCompareElementsFunction()
        {
            return &TypedArrayCompareElementsHelper<TypeName>;
        }

    public:
        virtual VTableValue DummyVirtualFunctionToHinderLinkerICF();
    };

声明:该文观点仅代表作者本人,转载请注明来自看雪