Skip to content

Commit 446b086

Browse files
committed
[MERGE chakra-core#2032 @Cellule] WASM - api testing + bugfixes
Merge pull request chakra-core#2032 from Cellule:wasm/api This is a pretty big change as I had to do some refactoring to fix some issues with the `WebAssembly.Module.imports` api and a bug in reusing the same module for multiple instance with imported globals. I took the opportunity to cleanup the global/imports code as well as the module environment in the instance. Fixes chakra-core#2033 Overview of the changes I made: - Throw a different error message if the element in the function table is null on call. - Throw a `WebAssembly.RuntimeError ` for exception thrown from the jitted code for Wasm functions - Throw in `WebAssembly::ReadBufferSource` if the buffer is empty and/or null since we assume the buffer is valid after that - Created the class `WebAssemblyEnvironment` to handle get/set elements in the module environment. This allows to do additional checks to avoid writing past the buffer limit. - The `WasmGlobal` objects are now immutable. This allows to re-instantiate the module with imported globals. The globals needs to be read from the `WebAssemblyEnvironment` for init_expr and local references - Some of the apis we use in `WebAssemblyInstance::CreateInstance` can throw a `WasmCompilationException`, for now I converted that exception to a TypeError. Related chakra-core#2035 - Insert all the imports type in the imports array in `WebAssemblyModule`. Treat all imports similarly to how we do exports in instantiate. Fixes chakra-core#2034
2 parents a1032cf + cc369f4 commit 446b086

44 files changed

Lines changed: 1526 additions & 604 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

lib/Backend/JnHelperMethodList.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,7 @@ HELPERCALL(Op_Throw, Js::JavascriptExceptionOperators::OP_Throw, AttrCanThrow)
354354
HELPERCALL(Op_RuntimeTypeError, Js::JavascriptExceptionOperators::OP_RuntimeTypeError, AttrCanThrow)
355355
HELPERCALL(Op_RuntimeRangeError, Js::JavascriptExceptionOperators::OP_RuntimeRangeError, AttrCanThrow)
356356
HELPERCALL(Op_RuntimeReferenceError, Js::JavascriptExceptionOperators::OP_RuntimeReferenceError, AttrCanThrow)
357+
HELPERCALL(Op_WebAssemblyRuntimeError, Js::JavascriptExceptionOperators::OP_WebAssemblyRuntimeError, AttrCanThrow)
357358
HELPERCALL(Op_OutOfMemoryError, Js::Throw::OutOfMemory, AttrCanThrow)
358359
HELPERCALL(Op_FatalInternalError, Js::Throw::FatalInternalError, AttrCanThrow)
359360

lib/Backend/Lower.cpp

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8288,16 +8288,18 @@ Lowerer::LowerLdWasmFunc(IR::Instr* instr)
82888288
funcIndirOpnd->SetScale(scale);
82898289
}
82908290

8291-
IR::LabelInstr * trapLabel = InsertLabel(true, instr);
8291+
IR::LabelInstr * trapOutOfBoundsLabel = InsertLabel(true, instr);
8292+
IR::LabelInstr * trapLabel = InsertLabel(true, trapOutOfBoundsLabel);
82928293
IR::LabelInstr * doneLabel = InsertLabel(false, instr->m_next);
8293-
InsertCompareBranch(indexOpnd, lengthOpnd, Js::OpCode::BrGe_A, true, trapLabel, trapLabel);
8294+
InsertCompareBranch(indexOpnd, lengthOpnd, Js::OpCode::BrGe_A, true, trapOutOfBoundsLabel, trapLabel);
82948295
InsertMove(valuesRegOpnd, valuesIndirOpnd, trapLabel);
82958296

82968297
InsertMove(dst, funcIndirOpnd, trapLabel);
82978298

82988299
InsertCompareBranch(dst, IR::IntConstOpnd::New(0, TyMachPtr, m_func), Js::OpCode::BrEq_A, trapLabel, trapLabel);
82998300
InsertBranch(Js::OpCode::Br, doneLabel, trapLabel);
83008301

8302+
GenerateThrow(IR::IntConstOpnd::NewFromType(SCODE_CODE(WASMERR_NeedWebAssemblyFunc), TyInt32, m_func), trapOutOfBoundsLabel);
83018303
GenerateThrow(IR::IntConstOpnd::NewFromType(SCODE_CODE(WASMERR_TableIndexOutOfRange), TyInt32, m_func), instr);
83028304

83038305
instr->Remove();
@@ -23106,9 +23108,7 @@ Lowerer::LowerDivI4Common(IR::Instr * instr)
2310623108

2310723109
if (divideByZeroInstr)
2310823110
{
23109-
IR::Opnd* errReg = IR::RegOpnd::New(TyInt32, m_func);
23110-
InsertMove(errReg, IR::IntConstOpnd::NewFromType(SCODE_CODE(WASMERR_DivideByZero), TyInt32, m_func), divLabel);
23111-
GenerateThrow(errReg, divLabel);
23111+
GenerateThrow(IR::IntConstOpnd::NewFromType(SCODE_CODE(WASMERR_DivideByZero), TyInt32, m_func), divLabel);
2311223112
}
2311323113
else
2311423114
{
@@ -23161,9 +23161,7 @@ Lowerer::LowerDivI4Common(IR::Instr * instr)
2316123161

2316223162
if (overflowReg3)
2316323163
{
23164-
IR::Opnd* errReg = IR::RegOpnd::New(TyInt32, m_func);
23165-
InsertMove(errReg, IR::IntConstOpnd::NewFromType(SCODE_CODE(VBSERR_Overflow), TyInt32, m_func), divLabel);
23166-
GenerateThrow(errReg, divLabel);
23164+
GenerateThrow( IR::IntConstOpnd::NewFromType(SCODE_CODE(VBSERR_Overflow), TyInt32, m_func), divLabel);
2316723165
}
2316823166
else
2316923167
{
@@ -23199,7 +23197,8 @@ Lowerer::GenerateThrow(IR::Opnd* errorCode, IR::Instr * instr) const
2319923197
instr->InsertBefore(throwInstr);
2320023198
Lowerer* lw = const_cast<Lowerer*> (this); //LowerUnaryHelperMem unlinks src1 of throwInstr,
2320123199
//local and self-contained mutation
23202-
lw->LowerUnaryHelperMem(throwInstr, IR::HelperOp_RuntimeTypeError);
23200+
const bool isWasm = m_func->GetJITFunctionBody() && m_func->GetJITFunctionBody()->IsWasmFunction();
23201+
lw->LowerUnaryHelperMem(throwInstr, isWasm ? IR::HelperOp_WebAssemblyRuntimeError : IR::HelperOp_RuntimeTypeError);
2320323202
}
2320423203

2320523204
void

lib/Runtime/ByteCode/AsmJsByteCodeDumper.cpp

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -388,14 +388,7 @@ namespace Js
388388
if (wasmInfo)
389389
{
390390
uint index = (uint)data->SlotIndex;
391-
if (index - wasmInfo->m_module->GetImportFuncOffset() < wasmInfo->m_module->GetImportCount())
392-
{
393-
uint importIndex = data->SlotIndex - wasmInfo->m_module->GetImportFuncOffset();
394-
auto loadedImport = wasmInfo->m_module->GetFunctionImport(importIndex);
395-
Output::Print(_u(" R%d = %s.%s[%u]"), data->Value, loadedImport->modName, loadedImport->fnName, importIndex);
396-
break;
397-
}
398-
else if (index - wasmInfo->m_module->GetFuncOffset() < wasmInfo->m_module->GetWasmFunctionCount())
391+
if (index - wasmInfo->m_module->GetFuncOffset() < wasmInfo->m_module->GetWasmFunctionCount())
399392
{
400393
uint funcIndex = data->SlotIndex - wasmInfo->m_module->GetFuncOffset();
401394
auto loadedFunc = wasmInfo->m_module->GetWasmFunctionInfo(funcIndex);

lib/Runtime/Language/InterpreterStackFrame.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8383,7 +8383,7 @@ const byte * InterpreterStackFrame::OP_ProfiledLoopBodyStart(const byte * ip)
83838383
Var func = table->DirectGetValue(index);
83848384
if (!func)
83858385
{
8386-
JavascriptError::ThrowWebAssemblyRuntimeError(GetScriptContext(), WASMERR_TableIndexOutOfRange);
8386+
JavascriptError::ThrowWebAssemblyRuntimeError(GetScriptContext(), WASMERR_NeedWebAssemblyFunc);
83878387
}
83888388
m_localSlots[playout->Value] = func;
83898389
#endif
@@ -8399,17 +8399,17 @@ const byte * InterpreterStackFrame::OP_ProfiledLoopBodyStart(const byte * ip)
83998399
if (func->GetFunctionInfo()->IsDeferredParseFunction())
84008400
{
84018401
// TODO: should be able to assert this once imports are converted to wasm functions
8402-
JavascriptError::ThrowWebAssemblyRuntimeError(GetScriptContext(), WASMERR_NeedWebAssemblyFunc, func->GetDisplayName());
8402+
JavascriptError::ThrowWebAssemblyRuntimeError(GetScriptContext(), WASMERR_NeedWebAssemblyFunc);
84038403
}
84048404
AsmJsFunctionInfo * asmInfo = func->GetFunctionBody()->GetAsmJsFunctionInfo();
84058405
if (!asmInfo)
84068406
{
84078407
// TODO: should be able to assert this once imports are converted to wasm functions
8408-
JavascriptError::ThrowWebAssemblyRuntimeError(GetScriptContext(), WASMERR_NeedWebAssemblyFunc, func->GetDisplayName());
8408+
JavascriptError::ThrowWebAssemblyRuntimeError(GetScriptContext(), WASMERR_NeedWebAssemblyFunc);
84098409
}
84108410
if (!expected->IsEquivalent(asmInfo->GetWasmSignature()))
84118411
{
8412-
JavascriptError::ThrowWebAssemblyRuntimeError(GetScriptContext(), WASMERR_SignatureMismatch, func->GetDisplayName());
8412+
JavascriptError::ThrowWebAssemblyRuntimeError(GetScriptContext(), WASMERR_SignatureMismatch);
84138413
}
84148414
#endif
84158415
}

lib/Runtime/Language/JavascriptExceptionOperators.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1048,6 +1048,11 @@ namespace Js
10481048
JavascriptError::ThrowReferenceError(scriptContext, MAKE_HR(messageId));
10491049
}
10501050

1051+
Var JavascriptExceptionOperators::OP_WebAssemblyRuntimeError(MessageId messageId, ScriptContext* scriptContext)
1052+
{
1053+
JavascriptError::ThrowWebAssemblyRuntimeError(scriptContext, MAKE_HR(messageId));
1054+
}
1055+
10511056
// Throw type error on access 'arguments', 'callee' or 'caller' when in a restricted context
10521057
Var JavascriptExceptionOperators::ThrowTypeErrorRestrictedPropertyAccessor(RecyclableObject* function, CallInfo callInfo, ...)
10531058
{

lib/Runtime/Language/JavascriptExceptionOperators.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ namespace Js
6969
static Var OP_RuntimeTypeError(MessageId messageId, ScriptContext* scriptContext);
7070
static Var OP_RuntimeRangeError(MessageId messageId, ScriptContext* scriptContext);
7171
static Var OP_RuntimeReferenceError(MessageId messageId, ScriptContext* scriptContext);
72+
static Var OP_WebAssemblyRuntimeError(MessageId messageId, ScriptContext* scriptContext);
7273
static void __declspec(noreturn) ThrowOutOfMemory(ScriptContext* scriptContext);
7374
static void __declspec(noreturn) ThrowStackOverflow(ScriptContext* scriptContext, PVOID returnAddress);
7475

lib/Runtime/Library/Chakra.Runtime.Library.vcxproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@
161161
<ClCompile Include="$(MSBuildThisFileDirectory)WebAssemblyMemory.cpp" />
162162
<ClCompile Include="$(MSBuildThisFileDirectory)WebAssemblyModule.cpp" />
163163
<ClCompile Include="$(MSBuildThisFileDirectory)WebAssemblyTable.cpp" />
164+
<ClCompile Include="$(MSBuildThisFileDirectory)WebAssemblyEnvironment.cpp" />
164165
</ItemGroup>
165166
<ItemGroup>
166167
<ClInclude Include="..\InternalPropertyList.h" />
@@ -290,6 +291,7 @@
290291
<ClInclude Include="UriHelper.h" />
291292
<ClInclude Include="WasmLibrary.h" />
292293
<ClInclude Include="WebAssembly.h" />
294+
<ClInclude Include="WebAssemblyEnvironment.h" />
293295
<ClInclude Include="WebAssemblyInstance.h" />
294296
<ClInclude Include="WebAssemblyMemory.h" />
295297
<ClInclude Include="WebAssemblyModule.h" />

lib/Runtime/Library/Chakra.Runtime.Library.vcxproj.filters

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@
117117
<ClCompile Include="$(MSBuildThisFileDirectory)WebAssemblyMemory.cpp" />
118118
<ClCompile Include="$(MSBuildThisFileDirectory)WebAssemblyModule.cpp" />
119119
<ClCompile Include="$(MSBuildThisFileDirectory)WebAssemblyTable.cpp" />
120+
<ClCompile Include="$(MSBuildThisFileDirectory)WebAssemblyEnvironment.cpp" />
120121
</ItemGroup>
121122
<ItemGroup>
122123
<ClInclude Include="..\InternalPropertyList.h" />
@@ -250,6 +251,7 @@
250251
<ClInclude Include="WebAssembly.h" />
251252
<ClInclude Include="WebAssemblyMemory.h" />
252253
<ClInclude Include="WebAssemblyTable.h" />
254+
<ClInclude Include="WebAssemblyEnvironment.h" />
253255
</ItemGroup>
254256
<ItemGroup>
255257
<None Include="ConcatString.inl" />

lib/Runtime/Library/WebAssembly.cpp

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -212,25 +212,26 @@ WebAssembly::ReadBufferSource(Var val, ScriptContext * ctx, _Out_ BYTE** buffer,
212212
const BOOL isTypedArray = Js::TypedArrayBase::Is(val);
213213
const BOOL isArrayBuffer = Js::ArrayBuffer::Is(val);
214214

215-
if (!isTypedArray && !isArrayBuffer)
216-
{
217-
*buffer = nullptr;
218-
*byteLength = 0;
219-
JavascriptError::ThrowTypeError(ctx, WASMERR_NeedBufferSource);
220-
}
215+
*buffer = nullptr;
216+
*byteLength = 0;
221217

222218
if (isTypedArray)
223219
{
224220
Js::TypedArrayBase* array = Js::TypedArrayBase::FromVar(val);
225221
*buffer = array->GetByteBuffer();
226222
*byteLength = array->GetByteLength();
227223
}
228-
else
224+
else if (isArrayBuffer)
229225
{
230226
Js::ArrayBuffer* arrayBuffer = Js::ArrayBuffer::FromVar(val);
231227
*buffer = arrayBuffer->GetBuffer();
232228
*byteLength = arrayBuffer->GetByteLength();
233229
}
230+
231+
if (*buffer == nullptr || *byteLength == 0)
232+
{
233+
JavascriptError::ThrowTypeError(ctx, WASMERR_NeedBufferSource);
234+
}
234235
}
235236

236237
void
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
//-------------------------------------------------------------------------------------------------------
2+
// Copyright (C) Microsoft. All rights reserved.
3+
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
4+
//-------------------------------------------------------------------------------------------------------
5+
6+
#include "RuntimeLibraryPch.h"
7+
8+
#ifdef ENABLE_WASM
9+
#include "../WasmReader/WasmReaderPch.h"
10+
namespace Js
11+
{
12+
13+
WebAssemblyEnvironment::WebAssemblyEnvironment(WebAssemblyModule* module)
14+
{
15+
this->module = module;
16+
ScriptContext* scriptContext = module->GetScriptContext();
17+
uint32 size = module->GetModuleEnvironmentSize();
18+
this->start = RecyclerNewArrayZ(scriptContext->GetRecycler(), Var, size);
19+
this->end = start + size;
20+
Assert(start < end);
21+
this->memory = this->start + module->GetMemoryOffset();
22+
this->imports = this->start + module->GetImportFuncOffset();
23+
this->functions = this->start + module->GetFuncOffset();
24+
this->table = this->start + module->GetTableEnvironmentOffset();
25+
this->globals = this->start + module->GetGlobalOffset();
26+
27+
uint32 globalsSize = WAsmJs::ConvertToJsVarOffset<byte>(module->GetGlobalsByteSize());
28+
// Assumes globals are last
29+
Assert(globals > table && globals > functions && globals > imports && globals > memory);
30+
if (globals < start ||
31+
(globals + globalsSize) > end)
32+
{
33+
// Something went wrong in the allocation and/or offset calculation failfast
34+
JavascriptError::ThrowOutOfMemoryError(scriptContext);
35+
}
36+
AssertMsg(globals + globalsSize + 0x10 > end, "We don't expect to allocate much more memory than what's needed");
37+
}
38+
39+
40+
template<typename T>
41+
void Js::WebAssemblyEnvironment::CheckPtrIsValid(intptr_t ptr) const
42+
{
43+
if (ptr < (intptr_t)start || (intptr_t)(ptr + sizeof(T)) > (intptr_t)end)
44+
{
45+
Js::Throw::InternalError();
46+
}
47+
}
48+
49+
template<typename T>
50+
T* Js::WebAssemblyEnvironment::GetVarElement(Var* ptr, uint32 index, uint32 maxCount) const
51+
{
52+
if (index >= maxCount)
53+
{
54+
Js::Throw::InternalError();
55+
}
56+
57+
Var* functionPtr = ptr + index;
58+
CheckPtrIsValid<T*>((intptr_t)functionPtr);
59+
Var varFunc = *functionPtr;
60+
if (varFunc)
61+
{
62+
if (!T::Is(varFunc))
63+
{
64+
Js::Throw::InternalError();
65+
}
66+
return T::FromVar(varFunc);
67+
}
68+
return nullptr;
69+
}
70+
71+
template<typename T>
72+
void Js::WebAssemblyEnvironment::SetVarElement(Var* ptr, T* val, uint32 index, uint32 maxCount)
73+
{
74+
if (index >= maxCount ||
75+
!T::Is(val))
76+
{
77+
Js::Throw::InternalError();
78+
}
79+
80+
Var* dst = ptr + index;
81+
CheckPtrIsValid<Var>((intptr_t)dst);
82+
AssertMsg(*(T**)dst == nullptr, "We shouln't overwrite anything on the environment once it is set");
83+
*dst = val;
84+
}
85+
86+
AsmJsScriptFunction* WebAssemblyEnvironment::GetWasmFunction(uint32 index) const
87+
{
88+
if (!(module->GetFunctionIndexType(index) == Wasm::FunctionIndexTypes::Function ||
89+
module->GetFunctionIndexType(index) == Wasm::FunctionIndexTypes::ImportThunk))
90+
{
91+
Js::Throw::InternalError();
92+
}
93+
return GetVarElement<AsmJsScriptFunction>(functions, index, module->GetWasmFunctionCount());
94+
}
95+
96+
void WebAssemblyEnvironment::SetWasmFunction(uint32 index, AsmJsScriptFunction* func)
97+
{
98+
if (!(module->GetFunctionIndexType(index) == Wasm::FunctionIndexTypes::Function ||
99+
module->GetFunctionIndexType(index) == Wasm::FunctionIndexTypes::ImportThunk) ||
100+
!AsmJsScriptFunction::IsWasmScriptFunction(func))
101+
{
102+
Js::Throw::InternalError();
103+
}
104+
SetVarElement<AsmJsScriptFunction>(functions, func, index, module->GetWasmFunctionCount());
105+
}
106+
107+
void WebAssemblyEnvironment::SetImportedFunction(uint32 index, Var importedFunc)
108+
{
109+
SetVarElement<JavascriptFunction>(imports, (JavascriptFunction*)importedFunc, index, module->GetWasmFunctionCount());
110+
}
111+
112+
Js::WebAssemblyTable* WebAssemblyEnvironment::GetTable(uint32 index) const
113+
{
114+
return GetVarElement<WebAssemblyTable>(table, index, 1);
115+
}
116+
117+
void WebAssemblyEnvironment::SetTable(uint32 index, WebAssemblyTable* table)
118+
{
119+
SetVarElement<WebAssemblyTable>(this->table, table, index, 1);
120+
}
121+
122+
WebAssemblyMemory* WebAssemblyEnvironment::GetMemory(uint32 index) const
123+
{
124+
return GetVarElement<WebAssemblyMemory>(memory, index, 1);
125+
}
126+
127+
void WebAssemblyEnvironment::SetMemory(uint32 index, WebAssemblyMemory* mem)
128+
{
129+
SetVarElement<WebAssemblyMemory>(this->memory, mem, index, 1);
130+
}
131+
132+
template<typename T>
133+
T WebAssemblyEnvironment::GetGlobalInternal(uint32 offset) const
134+
{
135+
T* ptr = (T*)start + offset;
136+
CheckPtrIsValid<T>((intptr_t)ptr);
137+
return *ptr;
138+
}
139+
140+
template<typename T>
141+
void WebAssemblyEnvironment::SetGlobalInternal(uint32 offset, T val)
142+
{
143+
T* ptr = (T*)start + offset;
144+
CheckPtrIsValid<T>((intptr_t)ptr);
145+
AssertMsg(*ptr == 0, "We shouln't overwrite anything on the environment once it is set");
146+
*ptr = val;
147+
}
148+
149+
Wasm::WasmConstLitNode WebAssemblyEnvironment::GetGlobalValue(Wasm::WasmGlobal* global) const
150+
{
151+
if (!global)
152+
{
153+
Js::Throw::InternalError();
154+
}
155+
Wasm::WasmConstLitNode cnst;
156+
uint32 offset = module->GetOffsetForGlobal(global);
157+
158+
switch (global->GetType())
159+
{
160+
case Wasm::WasmTypes::I32: cnst.i32 = GetGlobalInternal<int>(offset); break;
161+
case Wasm::WasmTypes::I64: cnst.i64 = GetGlobalInternal<int64>(offset); break;
162+
case Wasm::WasmTypes::F32: cnst.f32 = GetGlobalInternal<float>(offset); break;
163+
case Wasm::WasmTypes::F64: cnst.f64 = GetGlobalInternal<double>(offset); break;
164+
default:
165+
Js::Throw::InternalError();
166+
}
167+
return cnst;
168+
}
169+
170+
void WebAssemblyEnvironment::SetGlobalValue(Wasm::WasmGlobal* global, Wasm::WasmConstLitNode cnst)
171+
{
172+
if (!global)
173+
{
174+
Js::Throw::InternalError();
175+
}
176+
uint32 offset = module->GetOffsetForGlobal(global);
177+
178+
switch (global->GetType())
179+
{
180+
case Wasm::WasmTypes::I32: SetGlobalInternal<int>(offset, cnst.i32); break;
181+
case Wasm::WasmTypes::I64: SetGlobalInternal<int64>(offset, cnst.i64); break;
182+
case Wasm::WasmTypes::F32: SetGlobalInternal<float>(offset, cnst.f32); break;
183+
case Wasm::WasmTypes::F64: SetGlobalInternal<double>(offset, cnst.f64); break;
184+
default:
185+
Js::Throw::InternalError();
186+
}
187+
}
188+
189+
} // namespace Js
190+
#endif // ENABLE_WASM

0 commit comments

Comments
 (0)