Browse Source

swb: wasm write barrier fixes

This change fixes wasm related write barrier issues (enabled/introduced
recently).

GrowingArray: Change SWB annotations and use CopyArray.

ArenaAllocator: Tag cacheBlockEnd to avoid GC false positive. Refactor
related code to make TaggedPointer (renamed from GC_MARKED_OBJECT)
available.

HeapBlock::PrintVerifyMarkFailure: Fix previously missed addresses and
ignore target internal address false positives.

Wasm: Add annotations, and specifically remove bad casts that bypass SWB
(Get/SetVarElement).

runtests.py: Decode "byte" output so that when printed to console "\n"
works.
Jianchun Xu 9 năm trước cách đây
mục cha
commit
9299610bfe

+ 10 - 11
lib/Common/DataStructures/GrowingArray.h

@@ -14,10 +14,11 @@ extern int listFreeAmount;
 
 namespace JsUtil
 {
-    template <class TValue, class TAllocator>
+    template <class ValueType, class TAllocator>
     class GrowingArray
     {
     public:
+        typedef Field(ValueType, TAllocator) TValue;
         typedef typename AllocatorInfo<TAllocator, TValue>::AllocatorType AllocatorType;
         static GrowingArray* Create(uint32 _length);
 
@@ -77,18 +78,17 @@ namespace JsUtil
                     TRACK_ALLOC_INFO(alloc, TValue, AllocatorType, 0, length),
                     TypeAllocatorFunc<AllocatorType, TValue>::GetAllocFunc(),
                     length);
-                const size_t byteSize = UInt32Math::Mul(length, sizeof(TValue));
-                js_memcpy_s(pNewArray->buffer, byteSize, buffer, byteSize);
+                CopyArray<TValue, TValue, TAllocator>(pNewArray->buffer, length, buffer, length);
             }
 
             return pNewArray;
         }
-    private:
 
-        TValue* buffer;
-        uint32 count;
-        uint32 length;
-        AllocatorType* alloc;
+    private:
+        Field(TValue*, TAllocator) buffer;
+        Field(uint32) count;
+        Field(uint32) length;
+        FieldNoBarrier(AllocatorType*) alloc;
 
         void EnsureArray()
         {
@@ -107,14 +107,13 @@ namespace JsUtil
                     TRACK_ALLOC_INFO(alloc, TValue, AllocatorType, 0, newLength),
                     TypeAllocatorFunc<AllocatorType, TValue>::GetAllocFunc(),
                     newLength);
-                const size_t lengthByteSize = UInt32Math::Mul(length, sizeof(TValue));
-                const size_t newLengthByteSize = UInt32Math::Mul(newLength, sizeof(TValue));
-                js_memcpy_s(newbuffer, newLengthByteSize, buffer, lengthByteSize);
+                CopyArray<TValue, TValue, TAllocator>(newbuffer, newLength, buffer, length);
 #ifdef DIAG_MEM
                 listFreeAmount += length;
 #endif
                 if (length != 0)
                 {
+                    const size_t lengthByteSize = UInt32Math::Mul(length, sizeof(TValue));
                     AllocatorFree(alloc, (TypeAllocatorFunc<AllocatorType, int>::GetFreeFunc()), buffer, lengthByteSize);
                 }
                 length = newLength;

+ 0 - 7
lib/Common/Memory/ArenaAllocator.cpp

@@ -70,13 +70,6 @@ ArenaAllocatorBase<TFreeListPolicy, ObjectAlignmentBitShiftArg, RequireObjectAli
     ArenaMemoryTracking::ReportFreeAll(this);
     ArenaMemoryTracking::ArenaDestroyed(this);
 
-#if DBG
-    // tag the fields in case the address is reused in recycler and create a false positive
-    this->cacheBlockEnd = (char*)((intptr_t)this->cacheBlockEnd | 1);
-#else
-    this->cacheBlockEnd = nullptr;
-#endif
-
     if (!pageAllocator->IsClosed())
     {
         ReleasePageMemory();

+ 4 - 1
lib/Common/Memory/ArenaAllocator.h

@@ -139,7 +139,10 @@ template <class TFreeListPolicy, size_t ObjectAlignmentBitShiftArg = 3, bool Req
 class ArenaAllocatorBase : public Allocator, public ArenaData
 {
 private:
-    char * cacheBlockEnd;
+    // cacheBlockEnd may be the start address of actual GC memory, tag it to
+    // avoid GC false positive
+    TaggedPointer<char> cacheBlockEnd;
+
     size_t largestHole;
     uint blockState;        // 0 = no block, 1 = one big block, other more then one big block or have malloc blocks
 

+ 20 - 14
lib/Common/Memory/HeapBlock.cpp

@@ -788,12 +788,14 @@ SmallHeapBlockT<TBlockAttributes>::ClearExplicitFreeBitForObject(void* objectAdd
 #if DBG
 void HeapBlock::PrintVerifyMarkFailure(Recycler* recycler, char* objectAddress, char* target)
 {
-    HeapBlock* block = recycler->FindHeapBlock(objectAddress);
+    // Due to possible GC mark optimization, the pointers may point to object
+    // internal and "unaligned". Align them then FindHeapBlock.
+    HeapBlock* block = recycler->FindHeapBlock(HeapInfo::GetAlignedAddress(objectAddress));
     if (block == nullptr)
     {
         return;
     }
-    HeapBlock* targetBlock = recycler->FindHeapBlock(target);
+    HeapBlock* targetBlock = recycler->FindHeapBlock(HeapInfo::GetAlignedAddress(target));
     if (targetBlock == nullptr)
     {
         return;
@@ -811,6 +813,21 @@ void HeapBlock::PrintVerifyMarkFailure(Recycler* recycler, char* objectAddress,
     byte barrier = RecyclerWriteBarrierManager::GetWriteBarrier(objectAddress);
     Unused(barrier);
 
+    if (targetBlock->IsLargeHeapBlock())
+    {
+        targetOffset = (uint)(target - (char*)((LargeHeapBlock*)targetBlock)->GetRealAddressFromInterior(target));
+    }
+    else
+    {
+        targetOffset = (uint)(target - targetBlock->GetAddress()) % targetBlock->GetObjectSize(nullptr);
+    }
+
+    if (targetOffset != 0)
+    {
+        // "target" points to internal of an object. This is not a GC pointer.
+        return;
+    }
+
     if (Recycler::DoProfileAllocTracker())
     {
         // need CheckMemoryLeak or KeepRecyclerTrackData flag to have the tracker data and show following detailed info
@@ -918,21 +935,10 @@ void HeapBlock::PrintVerifyMarkFailure(Recycler* recycler, char* objectAddress,
             }
         }        
 
-        if (targetBlock->IsLargeHeapBlock())
-        {
-            targetOffset = (uint)(target - (char*)((LargeHeapBlock*)targetBlock)->GetRealAddressFromInterior(target));
-        }
-        else
-        {
-            targetOffset = (uint)(target - targetBlock->GetAddress()) % targetBlock->GetObjectSize(nullptr);
-        }
+
         targetStartAddress = target - targetOffset;
         targetTrackerData = (Recycler::TrackerData*)targetBlock->GetTrackerData(targetStartAddress);
 
-        if (targetOffset != 0)
-        {
-            Output::Print(_u("Target is not aligned with it's bucket, this indicate it's likely a false positive\n"));
-        }
 
         if (targetTrackerData)
         {

+ 1 - 0
lib/Common/Memory/HeapInfo.h

@@ -165,6 +165,7 @@ public:
 #endif
 
     static BOOL IsAlignedAddress(void * address) { return (0 == (((size_t)address) & HeapInfo::ObjectAlignmentMask)); }
+    static void * GetAlignedAddress(void * address) { return (void*)((uintptr_t)address & ~(uintptr_t)HeapInfo::ObjectAlignmentMask); }
 private:
     template <ObjectInfoBits attributes>
     typename SmallHeapBlockType<attributes, SmallAllocationBlockAttributes>::BucketType& GetBucket(size_t sizeCat);

+ 34 - 1
lib/Common/Memory/RecyclerPointers.h

@@ -4,6 +4,8 @@
 //-------------------------------------------------------------------------------------------------------
 #pragma once
 
+#include "WriteBarrierMacros.h"
+
 namespace Memory
 {
 class Recycler;
@@ -596,4 +598,35 @@ void *  __cdecl memset(_Out_writes_bytes_all_(_Size) WriteBarrierPtr<T> * _Dst,
     CompileAssert(false);
 }
 
-#include <Memory/WriteBarrierMacros.h>
+
+// This class abstract a pointer value with its last 2 bits set to avoid conservative GC tracking.
+template <class T>
+class TaggedPointer
+{
+public:
+    operator T*()          const { return GetPointerValue(); }
+    bool operator!= (T* p) const { return GetPointerValue() != p; }
+    bool operator== (T* p) const { return GetPointerValue() == p; }
+    T* operator-> ()       const { return GetPointerValue(); }
+    TaggedPointer<T>& operator= (T* inPtr)
+    {
+        SetPointerValue(inPtr);
+        return (*this);
+    }
+    TaggedPointer(T* inPtr) : ptr(inPtr)
+    {
+        SetPointerValue(inPtr);
+    }
+
+    TaggedPointer() : ptr(NULL) {};
+private:
+    T * GetPointerValue() const { return reinterpret_cast<T*>(reinterpret_cast<ULONG_PTR>(ptr) & ~3); }
+    T * SetPointerValue(T* inPtr)
+    {
+        AssertMsg((reinterpret_cast<ULONG_PTR>(inPtr) & 3) == 0, "Invalid pointer value, 2 least significant bits must be zero");
+        ptr = reinterpret_cast<T*>((reinterpret_cast<ULONG_PTR>(inPtr) | 3));
+        return ptr;
+    }
+
+    FieldNoBarrier(T*) ptr;
+};

+ 2 - 34
lib/Jsrt/JsrtContext.h

@@ -6,38 +6,6 @@
 
 #include "JsrtRuntime.h"
 
-// This class abstract a pointer value with its last 2 bits set to avoid conservative GC tracking.
-template <class T>
-class GC_MARKED_OBJECT
-{
-public:
-    operator T*()          const { return GetPointerValue(); }
-    bool operator!= (T* p) const { return GetPointerValue() != p; }
-    bool operator== (T* p) const { return GetPointerValue() == p; }
-    T* operator-> ()       const { return GetPointerValue(); }
-    GC_MARKED_OBJECT<T>& operator= (T* inPtr)
-    {
-        SetPointerValue(inPtr);
-        return (*this);
-    }
-    GC_MARKED_OBJECT(T* inPtr) : ptr(inPtr)
-    {
-        SetPointerValue(inPtr);
-    }
-
-    GC_MARKED_OBJECT() : ptr(NULL) {};
-private:
-    T * GetPointerValue() const { return reinterpret_cast<T*>(reinterpret_cast<ULONG_PTR>(ptr) & ~3); }
-    T * SetPointerValue(T* inPtr)
-    {
-        AssertMsg((reinterpret_cast<ULONG_PTR>(inPtr) & 3) == 0, "Invalid pointer value, 2 least significant bits must be zero");
-        ptr = reinterpret_cast<T*>((reinterpret_cast<ULONG_PTR>(inPtr) | 3));
-        return ptr;
-    }
-
-    FieldNoBarrier(T*) ptr;
-};
-
 class JsrtContext : public FinalizableObject
 {
 public:
@@ -73,6 +41,6 @@ private:
 
     Field(JsrtRuntime *) runtime;
     Field(void*) externalData = nullptr;
-    Field(GC_MARKED_OBJECT<JsrtContext>) previous;
-    Field(GC_MARKED_OBJECT<JsrtContext>) next;
+    Field(TaggedPointer<JsrtContext>) previous;
+    Field(TaggedPointer<JsrtContext>) next;
 };

+ 3 - 3
lib/Runtime/Language/AsmJsTypes.h

@@ -869,9 +869,9 @@ namespace Js
         Field(ArgSlot) mArgByteSize;
         Field(AsmJsRetType) mReturnType;
 #ifdef ENABLE_WASM
-        Wasm::WasmSignature * mSignature;
-        Wasm::WasmReaderInfo* mWasmReaderInfo;
-        WebAssemblyModule* mWasmModule;
+        Field(Wasm::WasmSignature *) mSignature;
+        Field(Wasm::WasmReaderInfo*) mWasmReaderInfo;
+        Field(WebAssemblyModule*) mWasmModule;
 #endif
         Field(bool) mIsHeapBufferConst;
         Field(bool) mUsesHeapBuffer;

+ 1 - 1
lib/Runtime/Library/ArrayBuffer.h

@@ -154,7 +154,7 @@ namespace Js
                 :SList<RecyclerWeakReference<ArrayBufferParent>*, Recycler>(recycler), increasedCount(0)
             {
             }
-            uint increasedCount;
+            Field(uint) increasedCount;
         };
 
         Field(OtherParents*) otherParents;

+ 13 - 13
lib/Runtime/Library/WebAssemblyEnvironment.cpp

@@ -47,14 +47,14 @@ void Js::WebAssemblyEnvironment::CheckPtrIsValid(intptr_t ptr) const
 }
 
 template<typename T>
-T* Js::WebAssemblyEnvironment::GetVarElement(Var* ptr, uint32 index, uint32 maxCount) const
+T* Js::WebAssemblyEnvironment::GetVarElement(Field(Var)* ptr, uint32 index, uint32 maxCount) const
 {
     if (index >= maxCount)
     {
         Js::Throw::InternalError();
     }
 
-    Var* functionPtr = ptr + index;
+    Field(Var)* functionPtr = ptr + index;
     CheckPtrIsValid<T*>((intptr_t)functionPtr);
     Var varFunc = *functionPtr;
     if (varFunc)
@@ -69,7 +69,7 @@ T* Js::WebAssemblyEnvironment::GetVarElement(Var* ptr, uint32 index, uint32 maxC
 }
 
 template<typename T>
-void Js::WebAssemblyEnvironment::SetVarElement(Var* ptr, T* val, uint32 index, uint32 maxCount)
+void Js::WebAssemblyEnvironment::SetVarElement(Field(Var)* ptr, T* val, uint32 index, uint32 maxCount)
 {
     if (index >= maxCount ||
         !T::Is(val))
@@ -77,7 +77,7 @@ void Js::WebAssemblyEnvironment::SetVarElement(Var* ptr, T* val, uint32 index, u
         Js::Throw::InternalError();
     }
 
-    Var* dst = ptr + index;
+    Field(Var)* dst = ptr + index;
     CheckPtrIsValid<Var>((intptr_t)dst);
     AssertMsg(*(T**)dst == nullptr, "We shouln't overwrite anything on the environment once it is set");
     *dst = val;
@@ -90,7 +90,7 @@ AsmJsScriptFunction* WebAssemblyEnvironment::GetWasmFunction(uint32 index) const
     {
         Js::Throw::InternalError();
     }
-    return GetVarElement<AsmJsScriptFunction>((Var*)PointerValue(functions), index, module->GetWasmFunctionCount());
+    return GetVarElement<AsmJsScriptFunction>(functions, index, module->GetWasmFunctionCount());
 }
 
 void WebAssemblyEnvironment::SetWasmFunction(uint32 index, AsmJsScriptFunction* func)
@@ -101,38 +101,38 @@ void WebAssemblyEnvironment::SetWasmFunction(uint32 index, AsmJsScriptFunction*
     {
         Js::Throw::InternalError();
     }
-    SetVarElement<AsmJsScriptFunction>((Var*)PointerValue(functions), func, index, module->GetWasmFunctionCount());
+    SetVarElement<AsmJsScriptFunction>(functions, func, index, module->GetWasmFunctionCount());
 }
 
 void WebAssemblyEnvironment::SetImportedFunction(uint32 index, Var importedFunc)
 {
-    SetVarElement<JavascriptFunction>((Var*)PointerValue(imports), (JavascriptFunction*)importedFunc, index, module->GetWasmFunctionCount());
+    SetVarElement<JavascriptFunction>(imports, (JavascriptFunction*)importedFunc, index, module->GetWasmFunctionCount());
 }
 
 Js::WebAssemblyTable* WebAssemblyEnvironment::GetTable(uint32 index) const
 {
-    return GetVarElement<WebAssemblyTable>((Var*)PointerValue(table), index, 1);
+    return GetVarElement<WebAssemblyTable>(table, index, 1);
 }
 
 void WebAssemblyEnvironment::SetTable(uint32 index, WebAssemblyTable* table)
 {
-    SetVarElement<WebAssemblyTable>((Var*)PointerValue(this->table), table, index, 1);
+    SetVarElement<WebAssemblyTable>(this->table, table, index, 1);
 }
 
 WebAssemblyMemory* WebAssemblyEnvironment::GetMemory(uint32 index) const
 {
-    return GetVarElement<WebAssemblyMemory>((Var*)PointerValue(memory), index, 1);
+    return GetVarElement<WebAssemblyMemory>(memory, index, 1);
 }
 
 void WebAssemblyEnvironment::SetMemory(uint32 index, WebAssemblyMemory* mem)
 {
-    SetVarElement<WebAssemblyMemory>((Var*)PointerValue(this->memory), mem, index, 1);
+    SetVarElement<WebAssemblyMemory>(this->memory, mem, index, 1);
 }
 
 template<typename T>
 T WebAssemblyEnvironment::GetGlobalInternal(uint32 offset) const
 {
-    T* ptr = (T*)PointerValue(start) + offset;
+    Field(T)* ptr = (Field(T)*)PointerValue(start) + offset;
     CheckPtrIsValid<T>((intptr_t)ptr);
     return *ptr;
 }
@@ -140,7 +140,7 @@ T WebAssemblyEnvironment::GetGlobalInternal(uint32 offset) const
 template<typename T>
 void WebAssemblyEnvironment::SetGlobalInternal(uint32 offset, T val)
 {
-    Field(T*) ptr = (T*)PointerValue(start) + offset;
+    Field(T)* ptr = (Field(T)*)PointerValue(start) + offset;
     CheckPtrIsValid<T>((intptr_t)PointerValue(ptr));
     AssertMsg(*ptr == 0, "We shouln't overwrite anything on the environment once it is set");
     *ptr = val;

+ 2 - 2
lib/Runtime/Library/WebAssemblyEnvironment.h

@@ -45,8 +45,8 @@ namespace Js
 
     private:
         template<typename T> void CheckPtrIsValid(intptr_t ptr) const;
-        template<typename T> T* GetVarElement(Var* ptr, uint32 index, uint32 maxCount) const;
-        template<typename T> void SetVarElement(Var* ptr, T* val, uint32 index, uint32 maxCount);
+        template<typename T> T* GetVarElement(Field(Var)* ptr, uint32 index, uint32 maxCount) const;
+        template<typename T> void SetVarElement(Field(Var)* ptr, T* val, uint32 index, uint32 maxCount);
         template<typename T> T GetGlobalInternal(uint32 offset) const;
         template<typename T> void SetGlobalInternal(uint32 offset, T val);
     };

+ 1 - 1
lib/Runtime/Library/WebAssemblyInstance.h

@@ -32,7 +32,7 @@ namespace Js
         static void LoadGlobals(WebAssemblyModule * wasmModule, ScriptContext* ctx, WebAssemblyEnvironment* env);
         static void LoadIndirectFunctionTable(WebAssemblyModule * wasmModule, ScriptContext* ctx, WebAssemblyEnvironment* env);
 
-        WebAssemblyModule * m_module;
+        Field(WebAssemblyModule *) m_module;
     };
 
 } // namespace Js

+ 3 - 3
lib/Runtime/Library/WebAssemblyMemory.h

@@ -43,10 +43,10 @@ namespace Js
     private:
         WebAssemblyMemory(ArrayBuffer * buffer, uint32 initial, uint32 maximum, DynamicType * type);
 
-        ArrayBuffer * m_buffer;
+        Field(ArrayBuffer *) m_buffer;
 
-        uint m_initial;
-        uint m_maximum;
+        Field(uint) m_initial;
+        Field(uint) m_maximum;
 #endif
     };
 

+ 28 - 28
lib/Runtime/Library/WebAssemblyModule.h

@@ -145,43 +145,43 @@ public:
 private:
     static JavascriptString * GetExternalKindString(ScriptContext * scriptContext, Wasm::ExternalKinds::ExternalKind kind);
 
-    bool m_hasTable;
-    bool m_hasMemory;
+    Field(bool) m_hasTable;
+    Field(bool) m_hasMemory;
     // The binary buffer is recycler allocated, tied the lifetime of the buffer to the module
-    const byte* m_binaryBuffer;
-    uint32 m_memoryInitSize;
-    uint32 m_memoryMaxSize;
-    uint32 m_tableInitSize;
-    uint32 m_tableMaxSize;
-    Wasm::WasmSignature* m_signatures;
-    uint32* m_indirectfuncs;
-    Wasm::WasmElementSegment** m_elementsegs;
+    Field(const byte*) m_binaryBuffer;
+    Field(uint32) m_memoryInitSize;
+    Field(uint32) m_memoryMaxSize;
+    Field(uint32) m_tableInitSize;
+    Field(uint32) m_tableMaxSize;
+    Field(Wasm::WasmSignature*) m_signatures;
+    Field(uint32*) m_indirectfuncs;
+    Field(Wasm::WasmElementSegment**) m_elementsegs;
     typedef JsUtil::List<Wasm::WasmFunctionInfo*, Recycler> WasmFunctionInfosList;
-    WasmFunctionInfosList* m_functionsInfo;
-    Wasm::WasmExport* m_exports;
+    Field(WasmFunctionInfosList*) m_functionsInfo;
+    Field(Wasm::WasmExport*) m_exports;
     typedef JsUtil::List<Wasm::WasmImport*, ArenaAllocator> WasmImportsList;
-    WasmImportsList* m_imports;
-    Wasm::WasmImport* m_memImport;
-    Wasm::WasmImport* m_tableImport;
-    uint32 m_importedFunctionCount;
-    Wasm::WasmDataSegment** m_datasegs;
-    Wasm::WasmBinaryReader* m_reader;
-    uint32* m_equivalentSignatureMap;
+    Field(WasmImportsList*) m_imports;
+    Field(Wasm::WasmImport*) m_memImport;
+    Field(Wasm::WasmImport*) m_tableImport;
+    Field(uint32) m_importedFunctionCount;
+    Field(Wasm::WasmDataSegment**) m_datasegs;
+    Field(Wasm::WasmBinaryReader*) m_reader;
+    Field(uint32*) m_equivalentSignatureMap;
     typedef JsUtil::List<Wasm::CustomSection, ArenaAllocator> CustomSectionsList;
-    CustomSectionsList* m_customSections;
+    Field(CustomSectionsList*) m_customSections;
 
-    uint m_globalCounts[Wasm::WasmTypes::Limit];
+    Field(uint) m_globalCounts[Wasm::WasmTypes::Limit];
     typedef JsUtil::List<Wasm::WasmGlobal*, ArenaAllocator> WasmGlobalsList;
-    WasmGlobalsList * m_globals;
+    Field(WasmGlobalsList *) m_globals;
 
-    uint m_signaturesCount;
-    uint m_exportCount;
-    uint32 m_datasegCount;
-    uint32 m_elementsegCount;
+    Field(uint) m_signaturesCount;
+    Field(uint) m_exportCount;
+    Field(uint32) m_datasegCount;
+    Field(uint32) m_elementsegCount;
 
-    uint32 m_startFuncIndex;
+    Field(uint32) m_startFuncIndex;
 
-    ArenaAllocator m_alloc;
+    FieldNoBarrier(ArenaAllocator) m_alloc;
 };
 
 } // namespace Js

+ 3 - 3
lib/WasmReader/WasmByteCodeGenerator.h

@@ -73,9 +73,9 @@ namespace Wasm
 
     struct WasmReaderInfo
     {
-        WasmFunctionInfo* m_funcInfo;
-        Js::WebAssemblyModule* m_module;
-        Js::Var m_bufferSrc;
+        Field(WasmFunctionInfo*) m_funcInfo;
+        Field(Js::WebAssemblyModule*) m_module;
+        Field(Js::Var) m_bufferSrc;
     };
 
     class WasmModuleGenerator

+ 11 - 11
lib/WasmReader/WasmFunctionInfo.h

@@ -35,21 +35,21 @@ namespace Wasm
         WasmReaderBase* GetCustomReader() const { return m_customReader; }
         void SetCustomReader(WasmReaderBase* customReader) { m_customReader = customReader; }
 #if DBG_DUMP
-        WasmImport* importedFunctionReference;
+        FieldNoBarrier(WasmImport*) importedFunctionReference;
 #endif
 
-        FunctionBodyReaderInfo m_readerInfo;
+        Field(FunctionBodyReaderInfo) m_readerInfo;
     private:
 
-        ArenaAllocator* m_alloc;
+        FieldNoBarrier(ArenaAllocator*) m_alloc;
         typedef JsUtil::GrowingArray<Local, ArenaAllocator> WasmTypeArray;
-        WasmTypeArray m_locals;
-        Js::FunctionBody* m_body;
-        WasmSignature* m_signature;
-        Js::ByteCodeLabel m_ExitLabel;
-        WasmReaderBase* m_customReader;
-        const char16* m_name;
-        uint32 m_nameLength;
-        uint32 m_number;
+        Field(WasmTypeArray) m_locals;
+        Field(Js::FunctionBody*) m_body;
+        Field(WasmSignature*) m_signature;
+        Field(Js::ByteCodeLabel) m_ExitLabel;
+        Field(WasmReaderBase*) m_customReader;
+        Field(const char16*) m_name;
+        Field(uint32) m_nameLength;
+        Field(uint32) m_number;
     };
 } // namespace Wasm

+ 3 - 3
lib/WasmReader/WasmReaderBase.h

@@ -9,9 +9,9 @@ namespace Wasm
 {
     struct FunctionBodyReaderInfo
     {
-        uint32 index;
-        uint32 size;
-        intptr_t startOffset;
+        Field(uint32) index;
+        Field(uint32) size;
+        Field(intptr_t) startOffset;
     };
 
     class WasmReaderBase

+ 6 - 6
lib/WasmReader/WasmSignature.h

@@ -33,12 +33,12 @@ public:
 
     void Dump();
 private:
-    WasmTypes::WasmType m_resultType;
-    uint32 m_id;
-    uint32 m_paramSize;
-    uint32 m_paramsCount;
-    size_t m_shortSig;
-    Local* m_params;
+    Field(WasmTypes::WasmType) m_resultType;
+    Field(uint32) m_id;
+    Field(uint32) m_paramSize;
+    Field(uint32) m_paramsCount;
+    Field(size_t) m_shortSig;
+    Field(Local*) m_params;
 };
 
 } // namespace Wasm

+ 1 - 1
test/runtests.py

@@ -340,7 +340,7 @@ class TestVariant(object):
         if expected_output == None or timedout:
             self._print("\nOutput:")
             self._print("----------------------------")
-            self._print(output)
+            self._print(output.decode('utf-8'))
             self._print("----------------------------")
         else:
             lst_output = output.split(b'\n')