فهرست منبع

Fix PAL implementation of _vsnwprintf & _vsnprintf with regards to truncation.
Fix some unit tests to be able to run in xplat builds (mostly problems with filename casing)
Use HeapNew & HeapDelete for exception message in Wasm.
Make test regress.js more generic to be able to add more regression tests there.

Michael Ferris 8 سال پیش
والد
کامیت
ffa75cb98d

+ 51 - 0
lib/Common/Memory/HeapAllocator.h

@@ -93,6 +93,57 @@ struct HeapAllocatorData
 
 struct HeapAllocator
 {
+    template<typename T>
+    struct AutoFree
+    {
+    private:
+        T * obj = nullptr;
+    public:
+        ~AutoFree()
+        {
+            HeapDelete(obj);
+        }
+        void Replace(T* obj)
+        {
+            this->obj = obj;
+        }
+        void Release()
+        {
+            this->obj = nullptr;
+        }
+        T* Get() const
+        {
+            return this->obj;
+        }
+    };
+
+    template<typename T>
+    struct AutoFreeArray
+    {
+    private:
+        T* obj = nullptr;
+        size_t count = 0;
+    public:
+        ~AutoFreeArray()
+        {
+            HeapDeleteArray(count, obj);
+        }
+        void Replace(__ecount(count) T* obj, size_t count)
+        {
+            this->obj = obj;
+            this->count = count;
+        }
+        void Release()
+        {
+            this->obj = nullptr;
+            this->count = 0;
+        }
+        T* Get() const
+        {
+            return this->obj;
+        }
+    };
+
     static const bool FakeZeroLengthArray = false;
 
     char * Alloc(DECLSPEC_GUARD_OVERFLOW size_t byteSize)

+ 1 - 1
lib/Runtime/Library/WasmLibrary.cpp

@@ -107,7 +107,7 @@ Js::JavascriptMethod Js::WasmLibrary::WasmDeferredParseEntryPoint(Js::AsmJsScrip
         }
         catch (Wasm::WasmCompilationException& ex)
         {
-            AutoCleanStr autoCleanExceptionMessage;
+            AutoFreeExceptionMessage autoCleanExceptionMessage;
             char16* exceptionMessage = WebAssemblyModule::FormatExceptionMessage(&ex, &autoCleanExceptionMessage, readerInfo->m_module, body);
 
             JavascriptLibrary *library = scriptContext->GetLibrary();

+ 10 - 21
lib/Runtime/Library/WebAssemblyModule.cpp

@@ -218,7 +218,7 @@ WebAssemblyModule::CreateModule(
     Wasm::WasmReaderInfo * readerInfo = nullptr;
     Js::FunctionBody * currentBody = nullptr;
     char16* exceptionMessage = nullptr;
-    AutoCleanStr autoCleanExceptionMessage;
+    AutoFreeExceptionMessage autoCleanExceptionMessage;
     try
     {
         Wasm::WasmModuleGenerator bytecodeGen(scriptContext, src);
@@ -818,42 +818,31 @@ WebAssemblyModule::GetModuleEnvironmentSize() const
     return size;
 }
 
-char16* WebAssemblyModule::FormatExceptionMessage(Wasm::WasmCompilationException* ex, AutoCleanStr* autoClean, WebAssemblyModule* wasmModule, FunctionBody* body)
+char16* WebAssemblyModule::FormatExceptionMessage(Wasm::WasmCompilationException* ex, AutoFreeExceptionMessage* autoFree, WebAssemblyModule* wasmModule, FunctionBody* body)
 {
     char16* originalExceptionMessage = ex->GetTempErrorMessageRef();
     if (!wasmModule || !body)
     {
         size_t len = wcslen(originalExceptionMessage) + 1;
-        autoClean->str = new char16[len];
-        js_memcpy_s(autoClean->str, len * sizeof(char16), originalExceptionMessage, len * sizeof(char16));
-        return autoClean->str;
+        char16* buf = HeapNewArray(char16, len);
+        autoFree->Replace(buf, len);
+        js_memcpy_s(buf, len * sizeof(char16), originalExceptionMessage, len * sizeof(char16));
+        return buf;
     }
 
     Wasm::BinaryLocation location = wasmModule->GetReader()->GetCurrentLocation();
 
     const char16* format = _u("function %s at offset %u/%u (0x%x/0x%x): %s");
     const char16* funcName = body->GetDisplayName();
+    char16* buf = HeapNewArray(char16, 2048);
+    autoFree->Replace(buf, 2048);
 
-    uint size = (uint)_scwprintf(format,
+    _snwprintf_s(buf, 2048, _TRUNCATE, format,
         funcName,
         location.offset, location.size,
         location.offset, location.size,
         originalExceptionMessage);
-
-    if (size > 2048)
-    {
-        // Do not allocate too much for the exception message, just truncate the message past 2048 characters
-        size = 2047;
-    }
-    ++size; // Null terminate character
-    autoClean->str = new char16[size];
-    int written = _snwprintf_s(autoClean->str, size, _TRUNCATE, format,
-        funcName,
-        location.offset, location.size,
-        location.offset, location.size,
-        originalExceptionMessage);
-    Assert((uint)written == size - 1);
-    return autoClean->str;
+    return buf;
 }
 
 void

+ 2 - 11
lib/Runtime/Library/WebAssemblyModule.h

@@ -23,16 +23,7 @@ namespace Wasm
 
 namespace Js
 {
-
-struct AutoCleanStr
-{
-    char16* str = nullptr;
-    ~AutoCleanStr()
-    {
-        delete[] str;
-    }
-};
-
+typedef HeapAllocator::AutoFreeArray<char16> AutoFreeExceptionMessage;
 
 class WebAssemblyModule : public DynamicObject
 {
@@ -160,7 +151,7 @@ public:
 
     Wasm::WasmBinaryReader* GetReader() const { return m_reader; }
 
-    static char16* FormatExceptionMessage(Wasm::WasmCompilationException* ex, AutoCleanStr* autoClean, WebAssemblyModule* wasmModule = nullptr, FunctionBody* body = nullptr);
+    static char16* FormatExceptionMessage(Wasm::WasmCompilationException* ex, AutoFreeExceptionMessage* autoClean, WebAssemblyModule* wasmModule = nullptr, FunctionBody* body = nullptr);
 
     virtual void Finalize(bool isShutdown) override;
     virtual void Dispose(bool isShutdown) override;

+ 2 - 2
pal/inc/rt/palrt.h

@@ -912,7 +912,7 @@ inline int __cdecl _scwprintf_unsafe(const WCHAR *_Format, ...)
 
 inline int __cdecl _vsnwprintf_unsafe(WCHAR *_Dst, size_t _SizeInWords, size_t _Count, const WCHAR *_Format, va_list _ArgList)
 {
-    if (_Count == _TRUNCATE) _Count = _SizeInWords - 1;
+    if (_Count == _TRUNCATE) _Count = _SizeInWords;
     int ret = _vsnwprintf(_Dst, _Count, _Format, _ArgList);
     _Dst[_SizeInWords - 1] = L'\0';
     if (ret < 0 && errno == 0)
@@ -934,7 +934,7 @@ inline int __cdecl _snwprintf_unsafe(WCHAR *_Dst, size_t _SizeInWords, size_t _C
 
 inline int __cdecl _vsnprintf_unsafe(char *_Dst, size_t _SizeInWords, size_t _Count, const char *_Format, va_list _ArgList)
 {
-    if (_Count == _TRUNCATE) _Count = _SizeInWords - 1;
+    if (_Count == _TRUNCATE) _Count = _SizeInWords;
     int ret = _vsnprintf(_Dst, _Count, _Format, _ArgList);
     _Dst[_SizeInWords - 1] = L'\0';
     if (ret < 0 && errno == 0)

+ 2 - 2
test/wasm/binary.js

@@ -4,7 +4,7 @@
 //-------------------------------------------------------------------------------------------------------
 
 /* global assert,testRunner */ // eslint rule
-WScript.LoadScriptFile("../UnitTestFrameWork/UnitTestFrameWork.js");
+WScript.LoadScriptFile("../UnitTestFramework/UnitTestFramework.js");
 WScript.LoadScriptFile("../WasmSpec/testsuite/harness/wasm-constants.js");
 WScript.LoadScriptFile("../WasmSpec/testsuite/harness/wasm-module-builder.js");
 WScript.Flag("-off:wasmdeferred");
@@ -34,7 +34,7 @@ const tests = [
   makeReservedTest("call_indirect reserved", [kExprCallIndirect, 1], "call_indirect reserved value must be 0"),
 ];
 
-WScript.LoadScriptFile("../UnitTestFrameWork/yargs.js");
+WScript.LoadScriptFile("../UnitTestFramework/yargs.js");
 const argv = yargsParse(WScript.Arguments, {
   boolean: ["verbose"],
   number: ["start", "end"],

+ 2 - 2
test/wasm/i64.js

@@ -5,11 +5,11 @@
 
 /* global assert,testRunner,yargsParse,i64ToString */ // eslint rule
 WScript.Flag("-wasmI64");
-WScript.LoadScriptFile("../UnitTestFrameWork/UnitTestFrameWork.js");
+WScript.LoadScriptFile("../UnitTestFramework/UnitTestFramework.js");
 WScript.LoadScriptFile("wasmutils.js");
 WScript.LoadScriptFile("../WasmSpec/testsuite/harness/wasm-constants.js");
 WScript.LoadScriptFile("../WasmSpec/testsuite/harness/wasm-module-builder.js");
-WScript.LoadScriptFile("../UnitTestFrameWork/yargs.js");
+WScript.LoadScriptFile("../UnitTestFramework/yargs.js");
 const argv = yargsParse(WScript.Arguments, {
   number: ["start", "end", "verbose"],
   default: {

+ 116 - 42
test/wasm/regress.js

@@ -3,49 +3,123 @@
 // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
 //-------------------------------------------------------------------------------------------------------
 
-const toValidate = {
-  "stack-polymorphic-yield": `
-(module
-  (func (result i32)
-    (block (result i32)
-      (loop (br 0))
-      unreachable
-      drop
-    )
-  )
-)`,
-  "stack-polymorphic-br_table": `
-(module
-  (func (result i32)
-    (block (result i32)
-      unreachable
-      drop
-      (br_table 0 0)
-    )
-  )
-)`,
-  "stack-polymorphic-br": `
-(module
-  (func (result i32)
-    unreachable
-    drop
-    br 0
-  )
-)`,
-  "stack-polymorphic-return": `
-(module
-  (func (result i32)
-    unreachable
-    drop
-    return
-  )
-)`,
-};
+WScript.LoadScriptFile("../UnitTestFramework/UnitTestFramework.js");
+WScript.LoadScriptFile("../WasmSpec/testsuite/harness/wasm-constants.js");
+WScript.LoadScriptFile("../WasmSpec/testsuite/harness/wasm-module-builder.js");
 
-for (const key in toValidate) {
-  if (!WebAssembly.validate(WebAssembly.wabt.convertWast2Wasm(toValidate[key]))) {
-    print(`Module ${key} should be valid`);
+const tests = [{
+  name: "polymorphic operators regression",
+  usesWabt: true,
+  body() {
+    const toValidate = {
+      "stack-polymorphic-yield": `
+    (module
+      (func (result i32)
+        (block (result i32)
+          (loop (br 0))
+          unreachable
+          drop
+        )
+      )
+    )`,
+      "stack-polymorphic-br_table": `
+    (module
+      (func (result i32)
+        (block (result i32)
+          unreachable
+          drop
+          (br_table 0 0)
+        )
+      )
+    )`,
+      "stack-polymorphic-br": `
+    (module
+      (func (result i32)
+        unreachable
+        drop
+        br 0
+      )
+    )`,
+      "stack-polymorphic-return": `
+    (module
+      (func (result i32)
+        unreachable
+        drop
+        return
+      )
+    )`,
+    };
+    for (const key in toValidate) {
+      assert.isTrue(WebAssembly.validate(WebAssembly.wabt.convertWast2Wasm(toValidate[key])), `Module ${key} should be valid`);
+    }
   }
+}, {
+  name: "tracing regression test",
+  usesWabt: false,
+  body() {
+    const tracingPrefix = 0xf0;
+    const builder = new WasmModuleBuilder();
+    const typeIndex = builder.addType(kSig_v_v);
+    builder.addFunction("foo", typeIndex).addBody([
+      tracingPrefix, 0x0,
+    ]).exportFunc();
+
+    try {
+      const {exports: {foo}} = new WebAssembly.Instance(new WebAssembly.Module(builder.toBuffer()));
+      foo();
+      assert.fail("Failed: Tracing prefix should not be accepted");
+    } catch (e) {
+      if (!e.message.includes("Tracing opcodes not allowed")) {
+        assert.fail("Wrong error: " + e);
+      }
+    }
+  }
+}, {
+  name: "Error message truncation test",
+  usesWabt: false,
+  body() {
+    const tracingPrefix = 0xf0;
+    const builder = new WasmModuleBuilder();
+    const typeIndex = builder.addType(kSig_v_v);
+    const exportedName = Array(5000).fill("").map((_, i) => i.toString()).join("");
+    builder.addFunction(exportedName, typeIndex).addBody([
+      // Use the same body as the test above to trigger an exception
+      tracingPrefix, 0x0,
+    ]).exportFunc();
+    try {
+      const {exports} = new WebAssembly.Instance(new WebAssembly.Module(builder.toBuffer()));
+      exports[exportedName]();
+      assert.fail("Failed: Compilation error expected");
+    } catch (e) {
+      // Make sure all platforms correctly truncate the error message to the expected length
+      const marker = "function ";
+      const index = e.message.indexOf(marker);
+      assert.isTrue(index !== -1);
+      assert.isTrue(e.message.length - index === 2047);
+      const sub = e.message.substring(index + marker.length, e.message.length);
+      const funcNameLengthUsed = 2047 - marker.length;
+      const funcNameUsed = exportedName.substring(0, funcNameLengthUsed);
+      assert.areEqual(funcNameUsed, sub, "Error message truncation unexpected result");
+    }
+  }
+}];
+
+WScript.LoadScriptFile("../UnitTestFramework/yargs.js");
+const argv = yargsParse(WScript.Arguments, {
+  boolean: ["verbose", "wabt"],
+  number: ["start", "end"],
+  default: {
+    verbose: true,
+    wabt: true,
+    start: 0,
+    end: tests.length
+  }
+}).argv;
+
+let todoTests = tests
+  .slice(argv.start, argv.end);
+if (!argv.wabt) {
+  todoTests = todoTests.filter(t => !t.usesWabt);
 }
 
-print("pass");
+testRunner.run(todoTests, {verbose: argv.verbose});

+ 8 - 6
test/wasm/rlexe.xml

@@ -3,10 +3,17 @@
 <test>
   <default>
     <files>regress.js</files>
-    <compile-flags>-wasm</compile-flags>
+    <compile-flags>-wasm -args --no-verbose -endargs</compile-flags>
     <tags>exclude_jshost,exclude_win7</tags>
   </default>
 </test>
+<test>
+  <default>
+    <files>regress.js</files>
+    <!-- Variant running without tests using wabt for jshost -->
+    <compile-flags>-wasm -args --no-verbose --no-wabt -endargs</compile-flags>
+  </default>
+</test>
 <test>
   <default>
     <files>rot.js</files>
@@ -395,9 +402,4 @@
     <tags>exclude_win7,exclude_xplat</tags>
   </default>
 </test>
-<test>
-  <default>
-    <files>tracing_regress.js</files>
-  </default>
-</test>
 </regress-exe>

+ 0 - 26
test/wasm/tracing_regress.js

@@ -1,26 +0,0 @@
-//-------------------------------------------------------------------------------------------------------
-// Copyright (C) Microsoft Corporation and contributors. All rights reserved.
-// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
-//-------------------------------------------------------------------------------------------------------
-
-WScript.LoadScriptFile("../WasmSpec/testsuite/harness/wasm-constants.js");
-WScript.LoadScriptFile("../WasmSpec/testsuite/harness/wasm-module-builder.js");
-
-const tracingPrefix = 0xf0;
-const builder = new WasmModuleBuilder();
-const typeIndex = builder.addType(kSig_v_v);
-builder.addFunction("foo", typeIndex).addBody([
-  tracingPrefix, 0x0,
-]).exportFunc();
-
-try {
-  const {exports: {foo}} = new WebAssembly.Instance(new WebAssembly.Module(builder.toBuffer()));
-  foo();
-  console.log("Failed: Tracing prefix should not be accepted");
-} catch (e) {
-  if (e.message.includes("Tracing opcodes not allowed")) {
-    console.log("pass");
-  } else {
-    console.log("Wrong error: " + e);
-  }
-}