Chakra TypedArray程式碼實現筆記
Ox9A82發表於2017-08-08
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();
};
相關文章
- Chakra除錯筆記 TypedArray2017-08-08除錯筆記
- KgCaptcha驗證碼實現筆記2023-04-19GCAPT筆記
- Chakra GC記憶體管理(未完)2017-08-08GC記憶體
- Android TypedArray 原始碼詳解2016-09-23Android原始碼
- Android TypedArray原始碼詳解2016-09-14Android原始碼
- Promise in Chakra2017-08-29Promise
- Type in Chakra2017-08-14
- 資源 | 數十種TensorFlow實現案例彙集:程式碼+筆記2017-05-11筆記
- CTR學習筆記&程式碼實現5-深度ctr模型 DeepCrossing -> DCN2020-05-15筆記模型ROS
- Node.js 程式碼閱讀筆記系列(0)Timer 的實現2017-03-23Node.js筆記
- LearnVIORB程式碼框架筆記2019-04-10ORB框架筆記
- 【筆記】堆及其實現2017-10-29筆記
- ClickHouse原始碼筆記2:聚合流程的實現2020-07-17原始碼筆記
- ClickHouse原始碼筆記1:聚合函式的實現2020-06-02原始碼筆記函式
- Node.js 程式碼閱讀筆記系列 — process.nextTick() 的實現2017-03-30Node.js筆記
- 【深度學習】TensorFlow實現線性迴歸,程式碼演示。全md文件筆記(程式碼文件已分享)2024-02-27深度學習筆記
- YYModel程式碼分析筆記2016-06-07筆記
- 【筆記】oracle 陣列實現2009-01-14筆記Oracle陣列
- C#滑動拼圖驗證碼實現筆記2023-05-06C#筆記
- TypedArray.name 屬性2017-03-13
- ClickHouse原始碼筆記3:函式呼叫的向量化實現2021-02-22原始碼筆記函式
- Vue.js 滑動拼圖驗證碼實現筆記2023-03-31Vue.js筆記
- CTR學習筆記&程式碼實現6-深度ctr模型 後浪 xDeepFM/FiBiNET2020-06-01筆記模型
- shell指令碼程式設計筆記2020-08-26指令碼程式設計筆記
- Python學習筆記—程式碼2019-02-16Python筆記
- 讀書筆記-乾淨程式碼2019-02-08筆記
- JS匯出Excel 程式碼筆記2016-01-04JSExcel筆記
- 常用程式碼筆記-持續更新2012-08-17筆記
- 程式碼大全2閱讀筆記2024-10-30筆記
- 【筆記】樹的表示與實現2017-10-29筆記
- Java實現Kafka讀寫筆記2017-04-18JavaKafka筆記
- 《自我實現的人》讀書筆記2010-08-03筆記
- Promise 程式碼實現2019-01-01Promise
- 人工智慧實踐:Tensorflow筆記:程式碼總結(2)2020-09-30人工智慧筆記
- 實現彩色二維碼程式碼實2017-03-22
- Locust 程式碼指令碼實現2024-03-16指令碼
- 【Java筆記】十分鐘搞定常用的八種排序演算法與程式碼實現2020-06-29Java筆記排序演算法
- spring原始碼學習筆記之容器的基本實現(一)2021-02-05Spring原始碼筆記