diff --git a/lib/Backend/AsmJsJITInfo.cpp b/lib/Backend/AsmJsJITInfo.cpp index bed0679ad41..50d223edeb8 100644 --- a/lib/Backend/AsmJsJITInfo.cpp +++ b/lib/Backend/AsmJsJITInfo.cpp @@ -64,6 +64,22 @@ AsmJsJITInfo::GetArgType(Js::ArgSlot argNum) const return GetArgTypeArray()[argNum]; } +#ifdef ENABLE_WASM +Wasm::WasmSignature * +AsmJsJITInfo::GetWasmSignature(uint index) const +{ + Assert(index < m_data.wasmSignatureCount); + return Wasm::WasmSignature::FromIDL(&m_data.wasmSignatures[index]); +} + +intptr_t +AsmJsJITInfo::GetWasmSignatureAddr(uint index) const +{ + Assert(index < m_data.wasmSignatureCount); + return m_data.wasmSignaturesBaseAddr + index * sizeof(Wasm::WasmSignature); +} +#endif + bool AsmJsJITInfo::IsHeapBufferConst() const { diff --git a/lib/Backend/AsmJsJITInfo.h b/lib/Backend/AsmJsJITInfo.h index 11c5acaab95..2b03a2cc196 100644 --- a/lib/Backend/AsmJsJITInfo.h +++ b/lib/Backend/AsmJsJITInfo.h @@ -31,6 +31,11 @@ class AsmJsJITInfo Js::AsmJsVarType::Which * GetArgTypeArray() const; Js::AsmJsVarType::Which GetArgType(Js::ArgSlot argNum) const; +#ifdef ENABLE_WASM + Wasm::WasmSignature * GetWasmSignature(uint index) const; + intptr_t GetWasmSignatureAddr(uint index) const; +#endif + bool IsHeapBufferConst() const; bool UsesHeapBuffer() const; bool AccessNeedsBoundCheck(uint offset) const; diff --git a/lib/Backend/IRBuilderAsmJs.cpp b/lib/Backend/IRBuilderAsmJs.cpp index 402bb9145b4..47c1d4f013b 100644 --- a/lib/Backend/IRBuilderAsmJs.cpp +++ b/lib/Backend/IRBuilderAsmJs.cpp @@ -1202,7 +1202,7 @@ IRBuilderAsmJs::BuildElementSlot(Js::OpCodeAsmJs newOpcode, uint32 offset, int32 { Assert(OpCodeAttrAsmJs::HasMultiSizeLayout(newOpcode)); - Assert(instance == 1 || newOpcode == Js::OpCodeAsmJs::LdArr_Func); + Assert(instance == 1 || newOpcode == Js::OpCodeAsmJs::LdArr_Func || newOpcode == Js::OpCodeAsmJs::LdArr_WasmFunc); Js::RegSlot valueRegSlot; IR::Opnd * slotOpnd; @@ -1238,13 +1238,22 @@ IRBuilderAsmJs::BuildElementSlot(Js::OpCodeAsmJs newOpcode, uint32 offset, int32 { IR::RegOpnd * baseOpnd = BuildSrcOpnd(GetRegSlotFromVarReg(instance), TyVar); IR::RegOpnd * indexOpnd = BuildSrcOpnd(GetRegSlotFromIntReg(slotIndex), TyUint32); + IR::IndirOpnd * indirOpnd = IR::IndirOpnd::New(baseOpnd, indexOpnd, TyVar, m_func); regOpnd = BuildDstOpnd(GetRegSlotFromVarReg(value), TyVar); instr = IR::Instr::New(Js::OpCode::LdAsmJsFunc, regOpnd, indirOpnd, m_func); break; } + case Js::OpCodeAsmJs::LdArr_WasmFunc: + { + IR::RegOpnd * baseOpnd = BuildSrcOpnd(GetRegSlotFromVarReg(instance), TyVar); + IR::RegOpnd * indexOpnd = BuildSrcOpnd(GetRegSlotFromIntReg(slotIndex), TyUint32); + regOpnd = BuildDstOpnd(GetRegSlotFromVarReg(value), TyVar); + instr = IR::Instr::New(Js::OpCode::LdWasmFunc, regOpnd, baseOpnd, indexOpnd, m_func); + break; + } case Js::OpCodeAsmJs::StSlot_Int: case Js::OpCodeAsmJs::LdSlot_Int: type = WAsmJs::INT32; @@ -2179,6 +2188,22 @@ IRBuilderAsmJs::BuildInt1Const1(Js::OpCodeAsmJs newOpcode, uint32 offset, Js::Re AddInstr(instr, offset); } +void +IRBuilderAsmJs::BuildReg1IntConst1(Js::OpCodeAsmJs newOpcode, uint32 offset, Js::RegSlot reg1, int constInt) +{ + Assert(newOpcode == Js::OpCodeAsmJs::CheckSignature); + + IR::RegOpnd * funcReg = BuildSrcOpnd(reg1, TyMachPtr); + + IR::IntConstOpnd * sigIndex = IR::IntConstOpnd::New(constInt, TyInt32, m_func); + + IR::Instr * instr = IR::Instr::New(Js::OpCode::CheckWasmSignature, m_func); + instr->SetSrc1(funcReg); + instr->SetSrc2(sigIndex); + + AddInstr(instr, offset); +} + void IRBuilderAsmJs::BuildFloat1Const1(Js::OpCodeAsmJs newOpcode, uint32 offset, Js::RegSlot dstRegSlot, float constVal) { diff --git a/lib/Backend/JITTimeFunctionBody.cpp b/lib/Backend/JITTimeFunctionBody.cpp index 25fd022fb4e..33fdf42898a 100644 --- a/lib/Backend/JITTimeFunctionBody.cpp +++ b/lib/Backend/JITTimeFunctionBody.cpp @@ -272,6 +272,15 @@ JITTimeFunctionBody::InitializeJITFunctionData( jitBody->asmJsData->isHeapBufferConst = asmFuncInfo->IsHeapBufferConst(); jitBody->asmJsData->usesHeapBuffer = asmFuncInfo->UsesHeapBuffer(); jitBody->asmJsData->totalSizeInBytes = asmFuncInfo->GetTotalSizeinBytes(); + +#ifdef ENABLE_WASM + if (functionBody->IsWasmFunction()) + { + jitBody->asmJsData->wasmSignatureCount = asmFuncInfo->GetWebAssemblyModule()->GetSignatureCount(); + jitBody->asmJsData->wasmSignaturesBaseAddr = (intptr_t)asmFuncInfo->GetWebAssemblyModule()->GetSignatures(); + jitBody->asmJsData->wasmSignatures = (WasmSignatureIDL*)asmFuncInfo->GetWebAssemblyModule()->GetSignatures(); + } +#endif } #endif } diff --git a/lib/Backend/JnHelperMethodList.h b/lib/Backend/JnHelperMethodList.h index 1245def0f33..3698d044ad4 100644 --- a/lib/Backend/JnHelperMethodList.h +++ b/lib/Backend/JnHelperMethodList.h @@ -84,6 +84,10 @@ HELPERCALL(Op_TypeofElem_Int32, Js::JavascriptOperators::TypeofElem_Int32, 0) HELPERCALL(Op_TypeofPropertyScoped, Js::JavascriptOperators::OP_TypeofPropertyScoped, 0) HELPERCALL(Op_Rem_Double, Js::NumberUtilities::Modulus, 0) +#ifdef ENABLE_WASM +HELPERCALL(Op_CheckWasmSignature, Js::WebAssembly::CheckSignature, AttrCanThrow) +#endif + HELPERCALL_FULL_OR_INPLACE_MATH(Op_Increment, Js::JavascriptMath::Increment, Js::SSE2::JavascriptMath::Increment, AttrCanThrow) HELPERCALL_FULL_OR_INPLACE_MATH(Op_Decrement, Js::JavascriptMath::Decrement, Js::SSE2::JavascriptMath::Decrement, AttrCanThrow) HELPERCALL_FULL_OR_INPLACE_MATH(Op_Negate, Js::JavascriptMath::Negate, Js::SSE2::JavascriptMath::Negate, AttrCanThrow) diff --git a/lib/Backend/Lower.cpp b/lib/Backend/Lower.cpp index 76ebca5c514..19061ab20cb 100644 --- a/lib/Backend/Lower.cpp +++ b/lib/Backend/Lower.cpp @@ -1809,6 +1809,14 @@ Lowerer::LowerRange(IR::Instr *instrStart, IR::Instr *instrEnd, bool defaultDoFa this->LowerUnaryHelper(instr, IR::HelperOp_UnwrapWithObj); break; +#ifdef ENABLE_WASM + case Js::OpCode::CheckWasmSignature: + this->LowerCheckWasmSignature(instr); + break; + case Js::OpCode::LdWasmFunc: + instrPrev = this->LowerLdWasmFunc(instr); + break; +#endif case Js::OpCode::LdAsmJsFunc: if (instr->GetSrc1()->IsIndirOpnd()) { @@ -1831,7 +1839,7 @@ Lowerer::LowerRange(IR::Instr *instrStart, IR::Instr *instrEnd, bool defaultDoFa indir->SetScale(scale); } } - // Fallthrough + //fallthrough case Js::OpCode::Ld_I4: case Js::OpCode::Ld_A: case Js::OpCode::InitConst: @@ -8179,6 +8187,100 @@ Lowerer::LoadArgumentsFromFrame(IR::Instr *const instr) } } +#ifdef ENABLE_WASM +IR::Instr * +Lowerer::LowerCheckWasmSignature(IR::Instr * instr) +{ + Assert(m_func->GetJITFunctionBody()->IsWasmFunction()); + Assert(instr->GetSrc1()); + Assert(instr->GetSrc2()->IsIntConstOpnd()); + + int sigId = instr->UnlinkSrc2()->AsIntConstOpnd()->AsInt32(); + + IR::Instr *instrPrev = instr->m_prev; + + IR::IndirOpnd * actualSig = IR::IndirOpnd::New(instr->UnlinkSrc1()->AsRegOpnd(), Js::AsmJsScriptFunction::GetOffsetOfSignature(), TyMachReg, m_func); + + Wasm::WasmSignature * expectedSig = m_func->GetJITFunctionBody()->GetAsmJsInfo()->GetWasmSignature(sigId); + if (expectedSig->GetShortSig() == Js::Constants::InvalidSignature) + { + intptr_t sigAddr = m_func->GetJITFunctionBody()->GetAsmJsInfo()->GetWasmSignatureAddr(sigId); + IR::AddrOpnd * expectedOpnd = IR::AddrOpnd::New(sigAddr, IR::AddrOpndKindConstantAddress, m_func); + m_lowererMD.LoadHelperArgument(instr, expectedOpnd); + m_lowererMD.LoadHelperArgument(instr, actualSig); + + LoadScriptContext(instr); + + m_lowererMD.ChangeToHelperCall(instr, IR::HelperOp_CheckWasmSignature); + } + else + { + IR::LabelInstr * trapLabel = InsertLabel(true, instr); + IR::LabelInstr * labelFallThrough = InsertLabel(false, instr->m_next); + IR::RegOpnd * actualRegOpnd = IR::RegOpnd::New(TyMachReg, m_func); + InsertMove(actualRegOpnd, actualSig, trapLabel); + + IR::IndirOpnd * shortSigIndir = IR::IndirOpnd::New(actualRegOpnd, Wasm::WasmSignature::GetOffsetOfShortSig(), TyMachReg, m_func); + InsertCompareBranch(shortSigIndir, IR::IntConstOpnd::New(expectedSig->GetShortSig(), TyMachReg, m_func), Js::OpCode::BrNeq_A, trapLabel, trapLabel); + + InsertBranch(Js::OpCode::Br, labelFallThrough, trapLabel); + + GenerateThrow(IR::IntConstOpnd::NewFromType(SCODE_CODE(WASMERR_SignatureMismatch), TyInt32, m_func), instr); + + instr->Remove(); + + } + + + return instrPrev; +} + +IR::Instr * +Lowerer::LowerLdWasmFunc(IR::Instr* instr) +{ + IR::Instr * prev = instr->m_prev; + + IR::RegOpnd * tableReg = instr->UnlinkSrc1()->AsRegOpnd(); + + IR::Opnd * indexOpnd = instr->UnlinkSrc2(); + IR::Opnd * dst = instr->UnlinkDst(); + + IR::IndirOpnd * lengthOpnd = IR::IndirOpnd::New(tableReg, Js::WebAssemblyTable::GetOffsetOfCurrentLength(), TyUint32, m_func); + IR::IndirOpnd * valuesIndirOpnd = IR::IndirOpnd::New(tableReg, Js::WebAssemblyTable::GetOffsetOfValues(), TyMachPtr, m_func); + IR::RegOpnd * valuesRegOpnd = IR::RegOpnd::New(TyMachPtr, m_func); + + byte scale = m_lowererMD.GetDefaultIndirScale(); + IR::IndirOpnd * funcIndirOpnd; + if (indexOpnd->IsIntConstOpnd()) + { + funcIndirOpnd = IR::IndirOpnd::New(valuesRegOpnd, indexOpnd->AsIntConstOpnd()->AsInt32() << scale, TyMachPtr, m_func); + } + else + { + Assert(indexOpnd->IsRegOpnd()); + funcIndirOpnd = IR::IndirOpnd::New(valuesRegOpnd, indexOpnd->AsRegOpnd(), TyMachPtr, m_func); + funcIndirOpnd->SetScale(scale); + } + + IR::LabelInstr * trapLabel = InsertLabel(true, instr); + IR::LabelInstr * doneLabel = InsertLabel(false, instr->m_next); + InsertCompareBranch(indexOpnd, lengthOpnd, Js::OpCode::BrGe_A, trapLabel, trapLabel); + InsertMove(valuesRegOpnd, valuesIndirOpnd, trapLabel); + + InsertMove(dst, funcIndirOpnd, trapLabel); + + InsertCompareBranch(dst, IR::IntConstOpnd::New(0, TyMachPtr, m_func), Js::OpCode::BrEq_A, trapLabel, trapLabel); + InsertBranch(Js::OpCode::Br, doneLabel, trapLabel); + + GenerateThrow(IR::IntConstOpnd::NewFromType(SCODE_CODE(WASMERR_TableIndexOutOfRange), TyInt32, m_func), instr); + + instr->Remove(); + + return prev; +} + +#endif + IR::Instr * Lowerer::LowerUnaryHelper(IR::Instr *instr, IR::JnHelperMethod helperMethod, IR::Opnd* opndBailoutArg) { diff --git a/lib/Backend/Lower.h b/lib/Backend/Lower.h index 1554cc57ee5..cd36b6e37da 100644 --- a/lib/Backend/Lower.h +++ b/lib/Backend/Lower.h @@ -210,6 +210,10 @@ class Lowerer IR::JnHelperMethod helperMethodWithTemp, IR::JnHelperMethod helperMethodLeftDead); IR::Instr * LowerAddLeftDeadForString(IR::Instr *instr); IR::Instr * LowerBinaryHelper(IR::Instr *instr, IR::JnHelperMethod helperMethod); +#ifdef ENABLE_WASM + IR::Instr * LowerCheckWasmSignature(IR::Instr * instr); + IR::Instr * LowerLdWasmFunc(IR::Instr* instr); +#endif IR::Instr * LowerInitCachedScope(IR::Instr * instr); IR::Instr * LowerBrBReturn(IR::Instr * instr, IR::JnHelperMethod helperMethod, bool isHelper); IR::Instr * LowerBrBMem(IR::Instr *instr, IR::JnHelperMethod helperMethod); diff --git a/lib/JITIDL/JITTypes.h b/lib/JITIDL/JITTypes.h index 9dab475ae74..d39b4e6761e 100644 --- a/lib/JITIDL/JITTypes.h +++ b/lib/JITIDL/JITTypes.h @@ -368,6 +368,28 @@ typedef struct StatementMapIDL IDL_PAD2(0) } StatementMapIDL; +typedef struct WasmSignatureIDL +{ + int resultType; + unsigned int id; + unsigned int paramSize; + unsigned int paramsCount; + CHAKRA_PTR shortSig; + IDL_DEF([size_is(paramsCount)]) int * params; +} WasmSignatureIDL; + +typedef struct TypedSlotInfo +{ + boolean isValidType; + IDL_PAD1(0) + IDL_PAD2(1) + unsigned int constCount; + unsigned int varCount; + unsigned int tmpCount; + unsigned int byteOffset; + unsigned int constSrcByteOffset; +} TypedSlotInfo; + typedef struct AsmJsDataIDL { boolean isHeapBufferConst; @@ -376,16 +398,12 @@ typedef struct AsmJsDataIDL unsigned short argCount; IDL_PAD2(0) int retType; - struct TypedSlotInfo - { - unsigned int constCount; - unsigned int varCount; - unsigned int tmpCount; - unsigned int byteOffset; - unsigned int constSrcByteOffset; - boolean isValidType; - } typedSlotInfos[5]; int totalSizeInBytes; + unsigned int wasmSignatureCount; + X64_PAD4(1) + TypedSlotInfo typedSlotInfos[5]; + CHAKRA_PTR wasmSignaturesBaseAddr; + IDL_DEF([size_is(wasmSignatureCount)]) WasmSignatureIDL * wasmSignatures; IDL_DEF([size_is(argCount)]) byte * argTypeArray; } AsmJsDataIDL; @@ -467,18 +485,12 @@ typedef struct FunctionBodyDataIDL boolean hasFinally; boolean usesArgumentsObject; boolean doScopeObjectCreation; -#if defined(_M_IX86) || defined(_M_ARM) - IDL_PAD1(0) -#else - IDL_PAD1(0) - IDL_PAD2(1) -#endif unsigned short envDepth; unsigned short inParamCount; unsigned short argUsedForBranch; unsigned short profiledCallSiteCount; - + IDL_PAD2(0) unsigned int funcNumber; unsigned int sourceContextId; unsigned int nestedCount; diff --git a/lib/Parser/rterrors.h b/lib/Parser/rterrors.h index 96b959c128a..643705403de 100755 --- a/lib/Parser/rterrors.h +++ b/lib/Parser/rterrors.h @@ -374,3 +374,9 @@ RT_ERROR_MSG(WASMERR_InvalidGlobalRef, 5673, "", "Global initialization does not RT_ERROR_MSG(WASMERR_NeedMemoryObject, 5674, "%s is not a WebAssembly.Memory", "WebAssembly.Memory object expected", kjstTypeError, 0) RT_ERROR_MSG(WASMERR_InvalidTypeConversion, 5675, "Invalid WebAssembly type conversion %s to %s", "Invalid WebAssembly type conversion", kjstTypeError, 0) RT_ERROR_MSG(WASMERR_DivideByZero, 5676, "", "Division by zero", kjstError, 0) +RT_ERROR_MSG(WASMERR_ExpectedAnyFunc, 5677, "%s is not AnyFunc", "AnyFunc expected", kjstTypeError, 0) +RT_ERROR_MSG(WASMERR_NeedTableObject, 5678, "%s is not a WebAssembly.Table", "WebAssembly.Table object expected", kjstTypeError, 0) +RT_ERROR_MSG(WASMERR_NeedWebAssemblyFunc, 5679, "%s is not a WebAssembly exported function", "WebAssembly exported function expected", kjstTypeError, 0) +RT_ERROR_MSG(WASMERR_SignatureMismatch, 5680, "%s called with invalid signature", "Function called with invalid signature", kjstWebAssemblyRuntimeError, 0) +RT_ERROR_MSG(WASMERR_ElementSegOutOfRange, 5681, "", "Element segment is out of range", kjstTypeError, 0) +RT_ERROR_MSG(WASMERR_TableIndexOutOfRange, 5682, "", "Table index is out of range", kjstWebAssemblyRuntimeError, 0) diff --git a/lib/Runtime/Base/Constants.h b/lib/Runtime/Base/Constants.h index 910c5db82bc..e4a6ccd22f5 100644 --- a/lib/Runtime/Base/Constants.h +++ b/lib/Runtime/Base/Constants.h @@ -46,6 +46,7 @@ namespace Js static const uint UninitializedValue = (uint)-1; static const ArgSlot InvalidArgSlot = (ArgSlot)-1; static const uint32 InvalidSymID = (uint32)-1; + static const size_t InvalidSignature = (size_t)-1; static const uint64 ExponentMask; static const uint64 MantissaMask; diff --git a/lib/Runtime/ByteCode/AsmJsByteCodeDumper.cpp b/lib/Runtime/ByteCode/AsmJsByteCodeDumper.cpp index f83ed710ddb..402dc122101 100644 --- a/lib/Runtime/ByteCode/AsmJsByteCodeDumper.cpp +++ b/lib/Runtime/ByteCode/AsmJsByteCodeDumper.cpp @@ -404,6 +404,7 @@ namespace Js break; } case OpCodeAsmJs::LdArr_Func: + case OpCodeAsmJs::LdArr_WasmFunc: Output::Print(_u(" R%d = R%d[I%d]"), data->Value, data->Instance, data->SlotIndex); break; case OpCodeAsmJs::StSlot_Int: diff --git a/lib/Runtime/ByteCode/AsmJsByteCodeWriter.cpp b/lib/Runtime/ByteCode/AsmJsByteCodeWriter.cpp index ac8d4dac07f..3c5687428c9 100644 --- a/lib/Runtime/ByteCode/AsmJsByteCodeWriter.cpp +++ b/lib/Runtime/ByteCode/AsmJsByteCodeWriter.cpp @@ -239,6 +239,18 @@ namespace Js return false; } + template + bool AsmJsByteCodeWriter::TryWriteReg1IntConst1(OpCodeAsmJs op, RegSlot R0, int C1) + { + OpLayoutT_Reg1IntConst1 layout; + if (SizePolicy::Assign(layout.R0, R0) && SizePolicy::Assign(layout.C1, C1)) + { + m_byteCodeData.EncodeT(op, &layout, sizeof(layout), this); + return true; + } + return false; + } + template bool AsmJsByteCodeWriter::TryWriteLong1Const1(OpCodeAsmJs op, RegSlot R0, int64 C1) { @@ -387,6 +399,11 @@ namespace Js MULTISIZE_LAYOUT_WRITE(Int1Const1, op, R0, C1); } + void AsmJsByteCodeWriter::AsmReg1IntConst1(OpCodeAsmJs op, RegSlot R0, int C1) + { + MULTISIZE_LAYOUT_WRITE(Reg1IntConst1, op, R0, C1); + } + void AsmJsByteCodeWriter::AsmLong1Const1(OpCodeAsmJs op, RegSlot R0, int64 C1) { MULTISIZE_LAYOUT_WRITE(Long1Const1, op, R0, C1); diff --git a/lib/Runtime/ByteCode/AsmJsByteCodeWriter.h b/lib/Runtime/ByteCode/AsmJsByteCodeWriter.h index e18df20b18a..d4ef1c7dd95 100644 --- a/lib/Runtime/ByteCode/AsmJsByteCodeWriter.h +++ b/lib/Runtime/ByteCode/AsmJsByteCodeWriter.h @@ -18,6 +18,7 @@ namespace Js void EmptyAsm ( OpCodeAsmJs op ); void Conv ( OpCodeAsmJs op, RegSlot R0, RegSlot R1 ); void AsmInt1Const1 ( OpCodeAsmJs op, RegSlot R0, int C1 ); + void AsmReg1IntConst1( OpCodeAsmJs op, RegSlot R0, int C1 ); void AsmLong1Const1 ( OpCodeAsmJs op, RegSlot R0, int64 C1 ); void AsmFloat1Const1 ( OpCodeAsmJs op, RegSlot R0, float C1 ); void AsmDouble1Const1( OpCodeAsmJs op, RegSlot R0, double C1 ); @@ -72,6 +73,7 @@ namespace Js RegSlot R9, RegSlot R10, RegSlot R11, RegSlot R12, RegSlot R13, RegSlot R14, RegSlot R15, RegSlot R16, RegSlot R17, RegSlot R18); template bool TryWriteInt1Const1 ( OpCodeAsmJs op, RegSlot R0, int C1 ); + template bool TryWriteReg1IntConst1 ( OpCodeAsmJs op, RegSlot R0, int C1 ); template bool TryWriteLong1Const1 ( OpCodeAsmJs op, RegSlot R0, int64 C1 ); template bool TryWriteFloat1Const1 ( OpCodeAsmJs op, RegSlot R0, float C1 ); template bool TryWriteDouble1Const1 ( OpCodeAsmJs op, RegSlot R0, double C1 ); diff --git a/lib/Runtime/ByteCode/LayoutTypesAsmJs.h b/lib/Runtime/ByteCode/LayoutTypesAsmJs.h index d7efc161f6c..9c35363042b 100644 --- a/lib/Runtime/ByteCode/LayoutTypesAsmJs.h +++ b/lib/Runtime/ByteCode/LayoutTypesAsmJs.h @@ -145,6 +145,7 @@ LAYOUT_TYPE_WMS_REG2 ( Reg1Double1 , Reg, Double) // 1 var register and 1 dou LAYOUT_TYPE_WMS_REG2 ( Reg1Float1 , Reg, Float) // 1 var register and 1 Float register LAYOUT_TYPE_WMS_REG2 ( Reg1Int1 , Reg, Int) // 1 var register and 1 int register LAYOUT_TYPE_WMS_REG2 ( Int1Const1 , Int, IntConst) // 1 int register and 1 const int value +LAYOUT_TYPE_WMS_REG2 ( Reg1IntConst1 , Reg, IntConst) // 1 int register and 1 const int value LAYOUT_TYPE_WMS_REG3 ( Int1Double2 , Int, Double, Double) // 1 int register and 2 double register ( double comparisons ) LAYOUT_TYPE_WMS_REG3 ( Int1Float2 , Int, Float, Float) // 1 int register and 2 float register ( float comparisons ) LAYOUT_TYPE_WMS_REG2 ( Int2 , Int, Int) // 2 int register diff --git a/lib/Runtime/ByteCode/OpCodes.h b/lib/Runtime/ByteCode/OpCodes.h index ed846af8b05..8c84a987bd7 100755 --- a/lib/Runtime/ByteCode/OpCodes.h +++ b/lib/Runtime/ByteCode/OpCodes.h @@ -144,6 +144,7 @@ MACRO_BACKEND_ONLY( Call, Reg1, OpSideEffect|OpUseAl MACRO_BACKEND_ONLY( AsmJsCallI, Reg1, OpSideEffect|OpUseAllFields|OpCallInstr) // call from asm.js to asm.js MACRO_BACKEND_ONLY( AsmJsCallE, Reg1, OpSideEffect|OpUseAllFields|OpCallInstr) // call from asm.js to javascript + // CallI through CallIExtendedFlags need to stay in this order since all the ProfiledCall* opcodes are calculated based on this order MACRO_WMS( CallI, CallI, OpSideEffect|OpUseAllFields|OpCallInstr|OpInlineCallInstr) // Return <- Call (indirect) Function(ArgCount) MACRO_WMS( CallIFlags, CallIFlags, OpSideEffect|OpUseAllFields|OpCallInstr|OpInlineCallInstr) // Return <- Call (indirect) Function(ArgCount) @@ -411,6 +412,10 @@ MACRO_EXTEND_WMS( StInnerObjSlotChkUndecl, ElementSlotI2, OpSideEffect MACRO_EXTEND_WMS( StEnvObjSlotChkUndecl, ElementSlotI2, OpSideEffect) MACRO_EXTEND_WMS( StModuleSlot, ElementSlotI2, OpSideEffect) MACRO_BACKEND_ONLY( LdAsmJsFunc, ElementSlot, OpTempNumberSources|OpCanCSE) +MACRO_BACKEND_ONLY( LdWasmFunc, ElementSlot, OpSideEffect) + +MACRO_BACKEND_ONLY( CheckWasmSignature, Reg2, OpSideEffect) + #ifndef FLOAT_VAR MACRO_BACKEND_ONLY( StSlotBoxTemp, Empty, OpSideEffect|OpTempNumberSources) #endif @@ -687,7 +692,7 @@ MACRO_BACKEND_ONLY( CheckPropertyGuardAndLoadType, Empty, OpFastFl MACRO_BACKEND_ONLY( CheckObjType, Empty, OpFastFldInstr|OpTempObjectSources|OpCanCSE) MACRO_BACKEND_ONLY( AdjustObjType, Empty, OpSideEffect) -// Edge inline built-ins + // Edge inline built-ins #ifdef ENABLE_DOM_FAST_PATH MACRO_BACKEND_ONLY( DOMFastPathGetter, Empty, OpCanCSE) // unlike other builtins, we don't know the return type MACRO_BACKEND_ONLY( DOMFastPathSetter, Empty, OpSideEffect) diff --git a/lib/Runtime/ByteCode/OpCodesAsmJs.h b/lib/Runtime/ByteCode/OpCodesAsmJs.h index 9c92eedebd7..28c48053619 100755 --- a/lib/Runtime/ByteCode/OpCodesAsmJs.h +++ b/lib/Runtime/ByteCode/OpCodesAsmJs.h @@ -115,6 +115,8 @@ MACRO_WMS ( StSlot_Int , ElementSlot , None MACRO_WMS ( StSlot_Long , ElementSlot , None ) // Sets an Int64 in the Module MACRO_WMS ( StSlot_Flt , ElementSlot , None ) // Sets an Int in the Module MACRO_WMS ( LdArr_Func , ElementSlot , None ) // opcode to load func from function tables +MACRO_WMS ( LdArr_WasmFunc , ElementSlot , None ) // opcode to load wasm func from function table +MACRO_WMS ( CheckSignature , Reg1IntConst1 , None ) // opcode to check signature for wasm indirect call // Array Buffer manipulations MACRO_WMS ( LdArrWasm , AsmTypedArr , None ) diff --git a/lib/Runtime/Language/AsmJsTypes.h b/lib/Runtime/Language/AsmJsTypes.h index c8b9f46e571..03e29ee1a84 100644 --- a/lib/Runtime/Language/AsmJsTypes.h +++ b/lib/Runtime/Language/AsmJsTypes.h @@ -868,12 +868,15 @@ namespace Js uint * mArgSizes; ArgSlot mArgByteSize; AsmJsRetType mReturnType; - +#ifdef ENABLE_WASM + Wasm::WasmSignature * mSignature; + Wasm::WasmReaderInfo* mWasmReaderInfo; + WebAssemblyModule* mWasmModule; +#endif bool mIsHeapBufferConst; bool mUsesHeapBuffer; FunctionBody* asmJsModuleFunctionBody; - Wasm::WasmReaderInfo* mWasmReaderInfo; Js::JavascriptError * mLazyError; public: AsmJsFunctionInfo() : mArgCount(0), @@ -882,11 +885,15 @@ namespace Js mArgByteSize(0), asmJsModuleFunctionBody(nullptr), mTJBeginAddress(nullptr), +#ifdef ENABLE_WASM + mWasmReaderInfo(nullptr), + mSignature(nullptr), + mWasmModule(nullptr), +#endif mUsesHeapBuffer(false), mIsHeapBufferConst(false), mArgType(nullptr), - mArgSizes(nullptr), - mWasmReaderInfo(nullptr) {} + mArgSizes(nullptr) {} // the key is the bytecode address typedef JsUtil::BaseDictionary ByteCodeToTJMap; ByteCodeToTJMap* mbyteCodeTJMap; @@ -961,10 +968,22 @@ namespace Js { mArgType = val; } +#ifdef ENABLE_WASM + Wasm::WasmSignature * GetWasmSignature() + { + return mSignature; + } + void SetWasmSignature(Wasm::WasmSignature * sig) + { + mSignature = sig; + } Wasm::WasmReaderInfo* GetWasmReaderInfo() const {return mWasmReaderInfo;} void SetWasmReaderInfo(Wasm::WasmReaderInfo* reader) {mWasmReaderInfo = reader;} + WebAssemblyModule* GetWebAssemblyModule() const { return mWasmModule; } + void SetWebAssemblyModule(WebAssemblyModule * module) { mWasmModule= module; } bool IsWasmDeferredParse() const { return mWasmReaderInfo != nullptr; } +#endif }; // The asm.js spec recognizes this set of builtin SIMD functions. diff --git a/lib/Runtime/Language/InterpreterHandlerAsmJs.inl b/lib/Runtime/Language/InterpreterHandlerAsmJs.inl index 2bef3548b67..6a3f349e3d4 100755 --- a/lib/Runtime/Language/InterpreterHandlerAsmJs.inl +++ b/lib/Runtime/Language/InterpreterHandlerAsmJs.inl @@ -42,6 +42,8 @@ EXDEF2 (NOPASMJS , InvalidOpCode, Empty DEF3_WMS( CUSTOM_ASMJS , Conv_VTL , OP_InvalidWasmTypeConversion , Long1Reg1 ) // convert var to int64 DEF3_WMS( CUSTOM_ASMJS , LdArr_Func , OP_LdArrFunc , ElementSlot ) + DEF3_WMS( CUSTOM_ASMJS , LdArr_WasmFunc,OP_LdArrWasmFunc , ElementSlot ) + DEF3_WMS( CUSTOM_ASMJS , CheckSignature,OP_CheckSignature , Reg1IntConst1 ) DEF4_WMS( TEMPLATE_ASMJS , LdSlot_Db , OP_LdSlotPrimitive , ElementSlot, double ) DEF4_WMS( TEMPLATE_ASMJS , LdSlot_Int , OP_LdSlotPrimitive , ElementSlot, int ) DEF4_WMS( TEMPLATE_ASMJS , LdSlot_Long , OP_LdSlotPrimitive , ElementSlot, int64 ) diff --git a/lib/Runtime/Language/InterpreterStackFrame.cpp b/lib/Runtime/Language/InterpreterStackFrame.cpp index 3590d329480..9c2d5f85db1 100644 --- a/lib/Runtime/Language/InterpreterStackFrame.cpp +++ b/lib/Runtime/Language/InterpreterStackFrame.cpp @@ -3103,6 +3103,8 @@ namespace Js val = (Var*)((BYTE*)wasmMem + WebAssemblyMemory::GetOffsetOfArrayBuffer()); } m_localSlots[AsmJsFunctionMemory::ArrayBufferRegister] = val; + + m_signatures = func->GetFunctionBody()->GetAsmJsFunctionInfo()->GetWebAssemblyModule()->GetSignatures(); } else #endif @@ -3110,7 +3112,6 @@ namespace Js m_localSlots[AsmJsFunctionMemory::ArrayBufferRegister] = (Var*)frame->GetItem(0) + AsmJsModuleMemory::MemoryTableBeginOffset; } - m_localSlots[AsmJsFunctionMemory::ArraySizeRegister] = 0; // do not cache ArraySize in the interpreter m_localSlots[AsmJsFunctionMemory::ScriptContextBufferRegister] = functionBody->GetScriptContext(); @@ -8392,6 +8393,50 @@ const byte * InterpreterStackFrame::OP_ProfiledLoopBodyStart(const byte * ip) m_localSlots[playout->Value] = arr[index]; } + template + void InterpreterStackFrame::OP_LdArrWasmFunc(const unaligned T* playout) + { +#ifdef ENABLE_WASM + WebAssemblyTable * table = WebAssemblyTable::FromVar(GetNonVarReg(playout->Instance)); + const uint32 index = (uint32)GetRegRawInt(playout->SlotIndex); + if (index >= table->GetCurrentLength()) + { + JavascriptError::ThrowWebAssemblyRuntimeError(GetScriptContext(), WASMERR_TableIndexOutOfRange); + } + Var func = table->DirectGetValue(index); + if (!func) + { + JavascriptError::ThrowWebAssemblyRuntimeError(GetScriptContext(), WASMERR_TableIndexOutOfRange); + } + m_localSlots[playout->Value] = func; +#endif + } + + template + void InterpreterStackFrame::OP_CheckSignature(const unaligned T* playout) + { +#ifdef ENABLE_WASM + ScriptFunction * func = ScriptFunction::FromVar(GetNonVarReg(playout->R0)); + int sigIndex = playout->C1; + Wasm::WasmSignature * expected = &m_signatures[sigIndex]; + if (func->GetFunctionInfo()->IsDeferredParseFunction()) + { + // TODO: should be able to assert this once imports are converted to wasm functions + JavascriptError::ThrowWebAssemblyRuntimeError(GetScriptContext(), WASMERR_NeedWebAssemblyFunc, func->GetDisplayName()); + } + AsmJsFunctionInfo * asmInfo = func->GetFunctionBody()->GetAsmJsFunctionInfo(); + if (!asmInfo) + { + // TODO: should be able to assert this once imports are converted to wasm functions + JavascriptError::ThrowWebAssemblyRuntimeError(GetScriptContext(), WASMERR_NeedWebAssemblyFunc, func->GetDisplayName()); + } + if (!expected->IsEquivalent(asmInfo->GetWasmSignature())) + { + JavascriptError::ThrowWebAssemblyRuntimeError(GetScriptContext(), WASMERR_SignatureMismatch, func->GetDisplayName()); + } +#endif + } + #ifdef ASMJS_PLAT template void InterpreterStackFrame::OP_LdArr(uint32 index, RegSlot regSlot) diff --git a/lib/Runtime/Language/InterpreterStackFrame.h b/lib/Runtime/Language/InterpreterStackFrame.h index 7144d58ba7f..122e7000d4e 100644 --- a/lib/Runtime/Language/InterpreterStackFrame.h +++ b/lib/Runtime/Language/InterpreterStackFrame.h @@ -140,6 +140,9 @@ namespace Js double* m_localDoubleSlots; float* m_localFloatSlots; +#ifdef ENABLE_WASM + Wasm::WasmSignature* m_signatures; +#endif _SIMDValue* m_localSimdSlots; EHBailoutData * ehBailoutData; @@ -610,6 +613,8 @@ namespace Js template inline void OP_InitClassMemberSetComputedName(const unaligned T * playout); template inline void OP_LdArr( uint32 index, RegSlot value ); template inline void OP_LdArrFunc(const unaligned T* playout); + template inline void OP_LdArrWasmFunc(const unaligned T* playout); + template inline void OP_CheckSignature(const unaligned T* playout); template inline void OP_ReturnDb(const unaligned T* playout); template T GetArrayViewOverflowVal(); template inline void OP_StArr( uint32 index, RegSlot value ); diff --git a/lib/Runtime/Library/Chakra.Runtime.Library.vcxproj b/lib/Runtime/Library/Chakra.Runtime.Library.vcxproj index f6165076dc2..08a04689040 100644 --- a/lib/Runtime/Library/Chakra.Runtime.Library.vcxproj +++ b/lib/Runtime/Library/Chakra.Runtime.Library.vcxproj @@ -160,6 +160,7 @@ + @@ -292,6 +293,7 @@ + diff --git a/lib/Runtime/Library/Chakra.Runtime.Library.vcxproj.filters b/lib/Runtime/Library/Chakra.Runtime.Library.vcxproj.filters index 916b7a301cf..55e5bb139ce 100644 --- a/lib/Runtime/Library/Chakra.Runtime.Library.vcxproj.filters +++ b/lib/Runtime/Library/Chakra.Runtime.Library.vcxproj.filters @@ -116,6 +116,7 @@ + @@ -248,6 +249,7 @@ + diff --git a/lib/Runtime/Library/JavascriptBuiltInFunctionList.h b/lib/Runtime/Library/JavascriptBuiltInFunctionList.h index afdab3e2f55..8ae60f7f0e7 100644 --- a/lib/Runtime/Library/JavascriptBuiltInFunctionList.h +++ b/lib/Runtime/Library/JavascriptBuiltInFunctionList.h @@ -310,6 +310,11 @@ BUILTIN(WebAssemblyInstance, NewInstance, NewInstance, FunctionInfo::SkipDefault BUILTIN(WebAssemblyMemory, NewInstance, NewInstance, FunctionInfo::SkipDefaultNewObject) BUILTIN(WebAssemblyMemory, Grow, EntryGrow, FunctionInfo::ErrorOnNew) BUILTIN(WebAssemblyMemory, GetterBuffer, EntryGetterBuffer, FunctionInfo::ErrorOnNew | FunctionInfo::HasNoSideEffect) +BUILTIN(WebAssemblyTable, NewInstance, NewInstance, FunctionInfo::SkipDefaultNewObject) +BUILTIN(WebAssemblyTable, GetterLength, EntryGetterLength, FunctionInfo::ErrorOnNew | FunctionInfo::HasNoSideEffect) +BUILTIN(WebAssemblyTable, Grow, EntryGrow, FunctionInfo::ErrorOnNew) +BUILTIN(WebAssemblyTable, Get, EntryGet, FunctionInfo::ErrorOnNew) +BUILTIN(WebAssemblyTable, Set, EntrySet, FunctionInfo::ErrorOnNew) #if ENABLE_DEBUG_CONFIG_OPTIONS BUILTIN(WebAssembly, NativeTypeCallTest, EntryNativeTypeCallTest, FunctionInfo::ErrorOnNew) #endif diff --git a/lib/Runtime/Library/JavascriptLibrary.cpp b/lib/Runtime/Library/JavascriptLibrary.cpp index 7661ce79a84..eb5874a8a0e 100644 --- a/lib/Runtime/Library/JavascriptLibrary.cpp +++ b/lib/Runtime/Library/JavascriptLibrary.cpp @@ -376,6 +376,11 @@ namespace Js webAssemblyInstancePrototype = DynamicObject::New(recycler, DynamicType::New(scriptContext, TypeIds_Object, objectPrototype, nullptr, DeferredTypeHandler::GetDefaultInstance())); + + webAssemblyTablePrototype = DynamicObject::New(recycler, + DynamicType::New(scriptContext, TypeIds_Object, objectPrototype, nullptr, + DeferredTypeHandler::GetDefaultInstance())); + } #endif @@ -612,6 +617,7 @@ namespace Js webAssemblyModuleType = DynamicType::New(scriptContext, TypeIds_WebAssemblyModule, webAssemblyModulePrototype, nullptr, NullTypeHandler::GetDefaultInstance(), true, true); webAssemblyInstanceType = DynamicType::New(scriptContext, TypeIds_WebAssemblyInstance, webAssemblyInstancePrototype, nullptr, NullTypeHandler::GetDefaultInstance(), true, true); webAssemblyMemoryType = DynamicType::New(scriptContext, TypeIds_WebAssemblyMemory, webAssemblyMemoryPrototype, nullptr, NullTypeHandler::GetDefaultInstance(), true, true); + webAssemblyTableType = DynamicType::New(scriptContext, TypeIds_WebAssemblyTable, webAssemblyTablePrototype, nullptr, NullTypeHandler::GetDefaultInstance(), true, true); } #endif // Initialize Object types @@ -1526,6 +1532,9 @@ namespace Js webAssemblyMemoryConstructor = CreateBuiltinConstructor(&WebAssemblyMemory::EntryInfo::NewInstance, DeferredTypeHandler::GetDefaultInstance(), webAssemblyMemoryConstructor); + + webAssemblyTableConstructor = CreateBuiltinConstructor(&WebAssemblyTable::EntryInfo::NewInstance, + DeferredTypeHandler::GetDefaultInstance(), webAssemblyTableConstructor); } #endif } @@ -2682,6 +2691,46 @@ namespace Js #ifdef ENABLE_WASM + void JavascriptLibrary::InitializeWebAssemblyTablePrototype(DynamicObject* prototype, DeferredTypeHandlerBase * typeHandler, DeferredInitializeMode mode) + { + typeHandler->Convert(prototype, mode, 6); + + JavascriptLibrary* library = prototype->GetLibrary(); + ScriptContext* scriptContext = prototype->GetScriptContext(); + + library->AddMember(prototype, PropertyIds::constructor, library->webAssemblyTableConstructor); + if (scriptContext->GetConfig()->IsES6ToStringTagEnabled()) + { + library->AddMember(prototype, PropertyIds::_symbolToStringTag, library->CreateStringFromCppLiteral(_u("WebAssemblyTable")), PropertyConfigurable); + } + scriptContext->SetBuiltInLibraryFunction(WebAssemblyTable::EntryInfo::Grow.GetOriginalEntryPoint(), + library->AddFunctionToLibraryObject(prototype, PropertyIds::grow, &WebAssemblyTable::EntryInfo::Grow, PropertyEnumerable)); + + scriptContext->SetBuiltInLibraryFunction(WebAssemblyTable::EntryInfo::Get.GetOriginalEntryPoint(), + library->AddFunctionToLibraryObject(prototype, PropertyIds::get, &WebAssemblyTable::EntryInfo::Get, PropertyEnumerable)); + + scriptContext->SetBuiltInLibraryFunction(WebAssemblyTable::EntryInfo::Set.GetOriginalEntryPoint(), + library->AddFunctionToLibraryObject(prototype, PropertyIds::set, &WebAssemblyTable::EntryInfo::Set, PropertyEnumerable)); + + library->AddAccessorsToLibraryObject(prototype, PropertyIds::length, &WebAssemblyTable::EntryInfo::GetterLength, nullptr); + + prototype->SetHasNoEnumerableProperties(true); + } + + void JavascriptLibrary::InitializeWebAssemblyTableConstructor(DynamicObject* constructor, DeferredTypeHandlerBase * typeHandler, DeferredInitializeMode mode) + { + typeHandler->Convert(constructor, mode, 3); + JavascriptLibrary* library = constructor->GetLibrary(); + ScriptContext* scriptContext = constructor->GetScriptContext(); + library->AddMember(constructor, PropertyIds::length, TaggedInt::ToVarUnchecked(1), PropertyConfigurable); + library->AddMember(constructor, PropertyIds::prototype, library->webAssemblyTablePrototype, PropertyNone); + if (scriptContext->GetConfig()->IsES6FunctionNameEnabled()) + { + library->AddMember(constructor, PropertyIds::name, library->CreateStringFromCppLiteral(_u("WebAssemblyTable")), PropertyConfigurable); + } + constructor->SetHasNoEnumerableProperties(true); + } + void JavascriptLibrary::InitializeWebAssemblyMemoryPrototype(DynamicObject* prototype, DeferredTypeHandlerBase * typeHandler, DeferredInitializeMode mode) { typeHandler->Convert(prototype, mode, 4); @@ -2788,6 +2837,7 @@ namespace Js library->AddFunction(webAssemblyObject, PropertyIds::CompileError, library->webAssemblyCompileErrorConstructor); library->AddFunction(webAssemblyObject, PropertyIds::RuntimeError, library->webAssemblyRuntimeErrorConstructor); library->AddFunction(webAssemblyObject, PropertyIds::Memory, library->webAssemblyMemoryConstructor); + library->AddFunction(webAssemblyObject, PropertyIds::Table, library->webAssemblyTableConstructor); #if ENABLE_DEBUG_CONFIG_OPTIONS if (PHASE_ON1(WasmNativeTypeCallTestPhase)) { diff --git a/lib/Runtime/Library/JavascriptLibrary.h b/lib/Runtime/Library/JavascriptLibrary.h index ff9f83d44a8..a9797d15616 100644 --- a/lib/Runtime/Library/JavascriptLibrary.h +++ b/lib/Runtime/Library/JavascriptLibrary.h @@ -267,6 +267,7 @@ namespace Js DynamicType * webAssemblyModuleType; DynamicType * webAssemblyInstanceType; DynamicType * webAssemblyMemoryType; + DynamicType * webAssemblyTableType; // SIMD_JS DynamicType * simdBool8x16TypeDynamic; @@ -430,6 +431,8 @@ namespace Js RuntimeFunction* webAssemblyModuleConstructor; DynamicObject* webAssemblyInstancePrototype; RuntimeFunction* webAssemblyInstanceConstructor; + DynamicObject* webAssemblyTablePrototype; + RuntimeFunction* webAssemblyTableConstructor; int regexConstructorSlotIndex; int regexExecSlotIndex; @@ -751,6 +754,7 @@ namespace Js DynamicType * GetWebAssemblyModuleType() const { return webAssemblyModuleType; } DynamicType * GetWebAssemblyInstanceType() const { return webAssemblyInstanceType; } DynamicType * GetWebAssemblyMemoryType() const { return webAssemblyMemoryType; } + DynamicType * GetWebAssemblyTableType() const { return webAssemblyTableType; } // SIMD_JS DynamicType * GetSIMDBool8x16TypeDynamic() const { return simdBool8x16TypeDynamic; } @@ -1216,6 +1220,7 @@ namespace Js STANDARD_INIT(WebAssemblyMemory); STANDARD_INIT(WebAssemblyModule); STANDARD_INIT(WebAssemblyInstance); + STANDARD_INIT(WebAssemblyTable); #undef STANDARD_INIT diff --git a/lib/Runtime/Library/RuntimeLibraryPch.h b/lib/Runtime/Library/RuntimeLibraryPch.h index 25b2fcd1677..8673be95274 100644 --- a/lib/Runtime/Library/RuntimeLibraryPch.h +++ b/lib/Runtime/Library/RuntimeLibraryPch.h @@ -85,7 +85,6 @@ #include "Library/SimdLib.h" #include "Language/SimdOps.h" -#include "Library/WebAssembly.h" #include "Library/WebAssemblyInstance.h" #include "Language/JavascriptStackWalker.h" diff --git a/lib/Runtime/Library/ScriptFunction.h b/lib/Runtime/Library/ScriptFunction.h index 9f3f84a2e85..5e2970ae843 100644 --- a/lib/Runtime/Library/ScriptFunction.h +++ b/lib/Runtime/Library/ScriptFunction.h @@ -122,6 +122,11 @@ namespace Js void SetModuleMemory(Var* mem) { m_moduleMemory = mem; } Var * GetModuleMemory() const { return m_moduleMemory; } +#ifdef ENABLE_WASM + void SetSignature(Wasm::WasmSignature * sig) { m_signature = sig; } + Wasm::WasmSignature * GetSignature() const { return m_signature; } + static uint32 GetOffsetOfSignature() { return offsetof(AsmJsScriptFunction, m_signature); } +#endif static uint32 GetOffsetOfModuleMemory() { return offsetof(AsmJsScriptFunction, m_moduleMemory); } protected: AsmJsScriptFunction(DynamicType * type); @@ -130,6 +135,7 @@ namespace Js private: Var * m_moduleMemory; + Wasm::WasmSignature * m_signature; }; class ScriptFunctionWithInlineCache : public ScriptFunction diff --git a/lib/Runtime/Library/WebAssembly.cpp b/lib/Runtime/Library/WebAssembly.cpp index 08bbbf5d0ce..db858f3892a 100644 --- a/lib/Runtime/Library/WebAssembly.cpp +++ b/lib/Runtime/Library/WebAssembly.cpp @@ -178,5 +178,20 @@ WebAssembly::ReadBufferSource(Var val, ScriptContext * ctx, _Out_ BYTE** buffer, } } +void +WebAssembly::CheckSignature(ScriptContext * scriptContext, Wasm::WasmSignature * sig1, Wasm::WasmSignature * sig2) +{ + if (!sig1->IsEquivalent(sig2)) + { + JavascriptError::ThrowWebAssemblyRuntimeError(scriptContext, WASMERR_SignatureMismatch); + } +} + +uint +WebAssembly::GetSignatureSize() +{ + return sizeof(Wasm::WasmSignature); +} + } #endif // ENABLE_WASM \ No newline at end of file diff --git a/lib/Runtime/Library/WebAssembly.h b/lib/Runtime/Library/WebAssembly.h index 810f6eedb5b..54b110cb06f 100644 --- a/lib/Runtime/Library/WebAssembly.h +++ b/lib/Runtime/Library/WebAssembly.h @@ -27,6 +27,8 @@ class WebAssembly static uint32 ToNonWrappingUint32(Var val, ScriptContext * ctx); static void ReadBufferSource(Var val, ScriptContext * ctx, _Out_ BYTE** buffer, _Out_ uint *byteLength); + static void CheckSignature(ScriptContext * scriptContext, Wasm::WasmSignature * sig1, Wasm::WasmSignature * sig2); + static uint GetSignatureSize(); #endif }; diff --git a/lib/Runtime/Library/WebAssemblyInstance.cpp b/lib/Runtime/Library/WebAssemblyInstance.cpp index 761f07930a7..cebd2e1a85f 100644 --- a/lib/Runtime/Library/WebAssemblyInstance.cpp +++ b/lib/Runtime/Library/WebAssemblyInstance.cpp @@ -87,21 +87,22 @@ WebAssemblyInstance::CreateInstance(WebAssemblyModule * module, Var importObject { ScriptContext * scriptContext = module->GetScriptContext(); Var* moduleEnvironmentPtr = RecyclerNewArrayZ(scriptContext->GetRecycler(), Var, module->GetModuleEnvironmentSize()); - Var* memory = moduleEnvironmentPtr + module->GetHeapOffset(); + Var* memory = moduleEnvironmentPtr + WebAssemblyModule::GetMemoryOffset(); + WebAssemblyInstance * newInstance = RecyclerNewZ(scriptContext->GetRecycler(), WebAssemblyInstance, module, scriptContext->GetLibrary()->GetWebAssemblyInstanceType()); + WebAssemblyTable** table = (WebAssemblyTable**)(moduleEnvironmentPtr + module->GetTableEnvironmentOffset()); Var* localModuleFunctions = moduleEnvironmentPtr + module->GetFuncOffset(); Var* importFunctions = moduleEnvironmentPtr + module->GetImportFuncOffset(); - LoadGlobals(module, scriptContext, moduleEnvironmentPtr, importObject); - LoadImports(module, scriptContext, importFunctions, localModuleFunctions, importObject, memory); + LoadImports(module, scriptContext, importFunctions, localModuleFunctions, importObject, memory, table); LoadFunctions(module, scriptContext, moduleEnvironmentPtr, localModuleFunctions); LoadDataSegs(module, memory, scriptContext); Js::Var exportsNamespace = JavascriptOperators::NewJavascriptObjectNoArg(scriptContext); - BuildObject(module, scriptContext, exportsNamespace, memory, newInstance, localModuleFunctions, importFunctions); + LoadIndirectFunctionTable(module, scriptContext, table, localModuleFunctions, importFunctions); + + BuildObject(module, scriptContext, exportsNamespace, memory, table, newInstance, localModuleFunctions, importFunctions); - Var** indirectFunctionTables = (Var**)(moduleEnvironmentPtr + module->GetTableEnvironmentOffset()); - LoadIndirectFunctionTables(module, scriptContext, indirectFunctionTables, localModuleFunctions, importFunctions); uint32 startFuncIdx = module->GetStartFunction(); if (startFuncIdx != Js::Constants::UninitializedValue) { @@ -131,14 +132,15 @@ void WebAssemblyInstance::LoadFunctions(WebAssemblyModule * wasmModule, ScriptCo continue; } AsmJsScriptFunction * funcObj = ctx->GetLibrary()->CreateAsmJsScriptFunction(wasmModule->GetWasmFunctionInfo(i)->GetBody()); + FunctionBody* body = funcObj->GetFunctionBody(); funcObj->SetModuleMemory(moduleMemoryPtr); + funcObj->SetSignature(body->GetAsmJsFunctionInfo()->GetWasmSignature()); FunctionEntryPointInfo * entypointInfo = (FunctionEntryPointInfo*)funcObj->GetEntryPointInfo(); entypointInfo->SetIsAsmJSFunction(true); entypointInfo->SetModuleAddress((uintptr_t)moduleMemoryPtr); funcObj->SetEnvironment(frameDisplay); localModuleFunctions[i] = funcObj; - FunctionBody* body = funcObj->GetFunctionBody(); if (!PHASE_OFF(WasmDeferredPhase, body)) { // if we still have WasmReaderInfo we haven't yet parsed @@ -171,20 +173,16 @@ void WebAssemblyInstance::LoadFunctions(WebAssemblyModule * wasmModule, ScriptCo void WebAssemblyInstance::LoadDataSegs(WebAssemblyModule * wasmModule, Var* memoryObject, ScriptContext* ctx) { - if (*memoryObject == nullptr) - { - *memoryObject = wasmModule->GetMemory(); - } - else if (wasmModule->GetMemory()) + if (wasmModule->HasMemoryImport()) { - // can only have 1 memory now - JavascriptError::ThrowTypeError(ctx, WASMERR_InvalidImport); + if (*memoryObject == nullptr) + { + JavascriptError::ThrowTypeError(ctx, WASMERR_NeedMemoryObject); + } } - if (*memoryObject == nullptr) + else { - // if there is no import or defined memory, create one with size 0 - // REVIEW: this makes code better as we don't have to do existence check, but ensure that this is spec behavior - *memoryObject = WebAssemblyMemory::CreateMemoryObject(0, 0, ctx); + *memoryObject = wasmModule->CreateMemory(); } WebAssemblyMemory * mem = WebAssemblyMemory::FromVar(*memoryObject); @@ -213,15 +211,8 @@ void WebAssemblyInstance::LoadDataSegs(WebAssemblyModule * wasmModule, Var* memo } } -void WebAssemblyInstance::BuildObject(WebAssemblyModule * wasmModule, ScriptContext* ctx, Var exportsNamespace, Var* memory, Var exportObj, Var* localModuleFunctions, Var* importFunctions) +void WebAssemblyInstance::BuildObject(WebAssemblyModule * wasmModule, ScriptContext* ctx, Var exportsNamespace, Var* memory, WebAssemblyTable** table, Var exportObj, Var* localModuleFunctions, Var* importFunctions) { - if (wasmModule->IsMemoryExported()) - { - PropertyRecord const * propertyRecord = nullptr; - ctx->GetOrAddPropertyRecord(_u("memory"), lstrlen(_u("memory")), &propertyRecord); - JavascriptOperators::OP_SetProperty(exportsNamespace, propertyRecord->GetPropertyId(), *memory, ctx); - } - PropertyRecord const * exportsPropertyRecord = nullptr; ctx->GetOrAddPropertyRecord(_u("exports"), lstrlen(_u("exports")), &exportsPropertyRecord); JavascriptOperators::OP_SetProperty(exportObj, exportsPropertyRecord->GetPropertyId(), exportsNamespace, ctx); @@ -237,6 +228,12 @@ void WebAssemblyInstance::BuildObject(WebAssemblyModule * wasmModule, ScriptCont Var obj = ctx->GetLibrary()->GetUndefined(); switch (funcExport->kind) { + case Wasm::ExternalKinds::Table: + obj = *table; + break; + case Wasm::ExternalKinds::Memory: + obj = *memory; + break; case Wasm::ExternalKinds::Function: obj = GetFunctionObjFromFunctionIndex(wasmModule, ctx, funcExport->funcIndex, localModuleFunctions, importFunctions); @@ -290,7 +287,7 @@ static Var GetImportVariable(Wasm::WasmImport* wi, ScriptContext* ctx, Var ffi) PropertyRecord const * propertyRecord = nullptr; ctx->GetOrAddPropertyRecord(name, nameLen, &propertyRecord); - if (!JavascriptObject::Is(modProp)) + if (!RecyclableObject::Is(modProp)) { JavascriptError::ThrowTypeError(ctx, WASMERR_InvalidImport); } @@ -312,39 +309,56 @@ void static SetGlobalValue(Var moduleEnv, uint offset, T val) *slot = val; } -void WebAssemblyInstance::LoadImports(WebAssemblyModule * wasmModule, ScriptContext* ctx, Var* importFunctions, Var* localModuleFunctions, Var ffi, Var* memoryObject) +void WebAssemblyInstance::LoadImports(WebAssemblyModule * wasmModule, ScriptContext* ctx, Var* importFunctions, Var* localModuleFunctions, Var ffi, Var* memoryObject, WebAssemblyTable ** tableObject) { const uint32 importCount = wasmModule->GetImportCount(); - if (importCount > 0 && (!ffi || !JavascriptObject::Is(ffi))) + if (importCount > 0 && (!ffi || !RecyclableObject::Is(ffi))) { JavascriptError::ThrowTypeError(ctx, WASMERR_InvalidImport); } for (uint32 i = 0; i < importCount; ++i) { Var prop = GetImportVariable(wasmModule->GetFunctionImport(i), ctx, ffi); - if (WebAssemblyMemory::Is(prop)) + if (!JavascriptFunction::Is(prop)) { - // only support 1 memory currently - if (*memoryObject != nullptr) - { - JavascriptError::ThrowTypeError(ctx, WASMERR_InvalidImport); - } - *memoryObject = prop; + JavascriptError::ThrowTypeError(ctx, JSERR_Property_NeedFunction); } - else if (JavascriptFunction::Is(prop)) + importFunctions[i] = prop; + if (AsmJsScriptFunction::IsWasmScriptFunction(prop)) { - importFunctions[i] = prop; - if (AsmJsScriptFunction::IsWasmScriptFunction(prop)) - { - Assert(localModuleFunctions[i] == nullptr); - // Imported Wasm functions can be called directly - localModuleFunctions[i] = prop; - } + Assert(localModuleFunctions[i] == nullptr); + // Imported Wasm functions can be called directly + localModuleFunctions[i] = prop; } - else + } + if (wasmModule->HasMemoryImport()) + { + Var prop = GetImportVariable(wasmModule->GetMemoryImport(), ctx, ffi); + if (!WebAssemblyMemory::Is(prop)) { - JavascriptError::ThrowTypeError(ctx, WASMERR_InvalidImport); + JavascriptError::ThrowTypeError(ctx, WASMERR_NeedMemoryObject); + } + WebAssemblyMemory * mem = WebAssemblyMemory::FromVar(prop); + if (!wasmModule->IsValidMemoryImport(mem)) + { + JavascriptError::ThrowTypeError(ctx, WASMERR_NeedMemoryObject); + } + *memoryObject = mem; + } + if (wasmModule->HasTableImport()) + { + Var prop = GetImportVariable(wasmModule->GetTableImport(), ctx, ffi); + if (!WebAssemblyTable::Is(prop)) + { + JavascriptError::ThrowTypeError(ctx, WASMERR_NeedTableObject); + } + WebAssemblyTable * table = WebAssemblyTable::FromVar(prop); + + if (!wasmModule->IsValidTableImport(table)) + { + JavascriptError::ThrowTypeError(ctx, WASMERR_NeedTableObject); } + *tableObject = table; } } @@ -449,32 +463,39 @@ void WebAssemblyInstance::LoadGlobals(WebAssemblyModule * wasmModule, ScriptCont } } -void WebAssemblyInstance::LoadIndirectFunctionTables(WebAssemblyModule * wasmModule, ScriptContext* ctx, Var** indirectFunctionTables, Var* localModuleFunctions, Var* importFunctions) +void WebAssemblyInstance::LoadIndirectFunctionTable(WebAssemblyModule * wasmModule, ScriptContext* ctx, WebAssemblyTable** tableObject, Var* localModuleFunctions, Var* importFunctions) { - // Globals can be imported, thus the offset must be resolved at at the last possible moment before instantiating the table. - // TODO: this must be fixed for multiple instance support - wasmModule->ResolveTableElementOffsets(); - for (uint i = 0; i < wasmModule->GetTableSize(); ++i) + if (wasmModule->HasTableImport()) { - uint funcIndex = wasmModule->GetTableValue(i); - if (funcIndex == Js::Constants::UninitializedValue) + if (*tableObject == nullptr) { - // todo:: are we suppose to invalidate here, or do a runtime error if this is accessed? - continue; + JavascriptError::ThrowTypeError(ctx, WASMERR_NeedTableObject); } + } + else + { + *tableObject = wasmModule->CreateTable(); + } - uint sigId = wasmModule->GetFunctionSignature(funcIndex)->GetSignatureId(); - sigId = wasmModule->GetEquivalentSignatureId(sigId); - if (!indirectFunctionTables[sigId]) - { - // TODO: initialize all indexes to "Js::Throw::RuntimeError" or similar type thing - // now, indirect func call to invalid type will give nullptr deref - indirectFunctionTables[sigId] = RecyclerNewArrayZ(ctx->GetRecycler(), Js::Var, wasmModule->GetTableSize()); - } - Var funcObj = GetFunctionObjFromFunctionIndex(wasmModule, ctx, funcIndex, localModuleFunctions, importFunctions); - if (funcObj) + WebAssemblyTable * table = *tableObject; + + for (uint elementsIndex = 0; elementsIndex < wasmModule->GetElementSegCount(); ++elementsIndex) + { + Wasm::WasmElementSegment* eSeg = wasmModule->GetElementSeg(elementsIndex); + + if (eSeg->GetNumElements() > 0) { - indirectFunctionTables[sigId][i] = funcObj; + uint offset = eSeg->GetDestAddr(wasmModule); + if (UInt32Math::Add(offset, eSeg->GetNumElements()) > table->GetCurrentLength()) + { + JavascriptError::ThrowTypeError(wasmModule->GetScriptContext(), WASMERR_ElementSegOutOfRange); + } + for (uint segIndex = 0; segIndex < eSeg->GetNumElements(); ++segIndex) + { + uint funcIndex = eSeg->GetElement(segIndex); + Var funcObj = GetFunctionObjFromFunctionIndex(wasmModule, ctx, funcIndex, localModuleFunctions, importFunctions); + table->DirectSetValue(segIndex + offset, funcObj); + } } } } diff --git a/lib/Runtime/Library/WebAssemblyInstance.h b/lib/Runtime/Library/WebAssemblyInstance.h index 36acb5dbc4f..a120dea18bf 100644 --- a/lib/Runtime/Library/WebAssemblyInstance.h +++ b/lib/Runtime/Library/WebAssemblyInstance.h @@ -27,10 +27,10 @@ namespace Js static void LoadDataSegs(WebAssemblyModule * wasmModule, Var* memory, ScriptContext* ctx); static void LoadFunctions(WebAssemblyModule * wasmModule, ScriptContext* ctx, Var* moduleMemoryPtr, Var* localModuleFunctions); - static void BuildObject(WebAssemblyModule * wasmModule, ScriptContext* ctx, Var exportsNamespace, Var* memory, Var exportObj, Var* localModuleFunctions, Var* importFunctions); - static void LoadImports(WebAssemblyModule * wasmModule, ScriptContext* ctx, Var* importFunctions, Var* localModuleFunctions, Var ffi, Var* memoryObject); + static void BuildObject(WebAssemblyModule * wasmModule, ScriptContext* ctx, Var exportsNamespace, Var* memory, WebAssemblyTable** table, Var exportObj, Var* localModuleFunctions, Var* importFunctions); + static void LoadImports(WebAssemblyModule * wasmModule, ScriptContext* ctx, Var* importFunctions, Var* localModuleFunctions, Var ffi, Var* memoryObject, WebAssemblyTable ** tableObject); static void LoadGlobals(WebAssemblyModule * wasmModule, ScriptContext* ctx, Var moduleEnv, Var ffi); - static void LoadIndirectFunctionTables(WebAssemblyModule * wasmModule, ScriptContext* ctx, Var** indirectFunctionTables, Var* localModuleFunctions, Var* importFunctions); + static void LoadIndirectFunctionTable(WebAssemblyModule * wasmModule, ScriptContext* ctx, WebAssemblyTable** indirectFunctionTables, Var* localModuleFunctions, Var* importFunctions); static Var GetFunctionObjFromFunctionIndex(WebAssemblyModule * wasmModule, ScriptContext* ctx, uint32 funcIndex, Var* localModuleFunctions, Var* importFunctions); diff --git a/lib/Runtime/Library/WebAssemblyMemory.cpp b/lib/Runtime/Library/WebAssemblyMemory.cpp index 94af95bdcf0..530f3c9af8a 100644 --- a/lib/Runtime/Library/WebAssemblyMemory.cpp +++ b/lib/Runtime/Library/WebAssemblyMemory.cpp @@ -169,5 +169,17 @@ WebAssemblyMemory::GetBuffer() const return m_buffer; } +uint +WebAssemblyMemory::GetInitialLength() const +{ + return m_initial; +} + +uint +WebAssemblyMemory::GetMaximumLength() const +{ + return m_maximum; +} + } // namespace Js #endif // ENABLE_WASM diff --git a/lib/Runtime/Library/WebAssemblyMemory.h b/lib/Runtime/Library/WebAssemblyMemory.h index 2a41f01a383..d5b6a655db7 100644 --- a/lib/Runtime/Library/WebAssemblyMemory.h +++ b/lib/Runtime/Library/WebAssemblyMemory.h @@ -30,6 +30,8 @@ namespace Js static WebAssemblyMemory * CreateMemoryObject(uint32 initial, uint32 maximum, ScriptContext * scriptContext); ArrayBuffer * GetBuffer() const; + uint GetInitialLength() const; + uint GetMaximumLength() const; static int GetOffsetOfArrayBuffer() { return offsetof(WebAssemblyMemory, m_buffer); } private: diff --git a/lib/Runtime/Library/WebAssemblyModule.cpp b/lib/Runtime/Library/WebAssemblyModule.cpp index ebafe17a172..15c43ec5cfc 100644 --- a/lib/Runtime/Library/WebAssemblyModule.cpp +++ b/lib/Runtime/Library/WebAssemblyModule.cpp @@ -13,10 +13,16 @@ namespace Js { WebAssemblyModule::WebAssemblyModule(Js::ScriptContext* scriptContext, const byte* binaryBuffer, uint binaryBufferLength, DynamicType * type) : DynamicObject(type), - m_memory(nullptr), + m_hasMemory(false), + m_hasTable(false), + m_memImport(nullptr), + m_tableImport(nullptr), + m_memoryInitSize(0), + m_memoryMaxSize(0), + m_tableInitSize(0), + m_tableMaxSize(0), m_alloc(_u("WebAssemblyModule"), scriptContext->GetThreadContext()->GetPageAllocator(), Js::Throw::OutOfMemory), m_indirectfuncs(nullptr), - m_indirectFuncCount(0), m_exports(nullptr), m_exportCount(0), m_datasegCount(0), @@ -25,7 +31,6 @@ WebAssemblyModule::WebAssemblyModule(Js::ScriptContext* scriptContext, const byt m_signatures(nullptr), m_signaturesCount(0), m_startFuncIndex(Js::Constants::UninitializedValue), - isMemExported(false), m_binaryBuffer(binaryBuffer) { //the first elm is the number of Vars in front of I32; makes for a nicer offset computation @@ -232,7 +237,7 @@ WebAssemblyModule::GetFunctionIndexType(uint32 funcIndex) const void WebAssemblyModule::InitializeMemory(uint32 minPage, uint32 maxPage) { - if (m_memory != nullptr) + if (m_hasMemory) { throw Wasm::WasmCompilationException(_u("Memory already allocated")); } @@ -241,22 +246,27 @@ WebAssemblyModule::InitializeMemory(uint32 minPage, uint32 maxPage) { throw Wasm::WasmCompilationException(_u("Memory: MaxPage (%d) must be greater than MinPage (%d)"), maxPage, minPage); } - - m_memory = WebAssemblyMemory::CreateMemoryObject(minPage, maxPage, GetScriptContext()); + m_hasMemory = true; + m_memoryInitSize = minPage; + m_memoryMaxSize = maxPage; } WebAssemblyMemory * -WebAssemblyModule::GetMemory() const +WebAssemblyModule::CreateMemory() const { - return m_memory; + return WebAssemblyMemory::CreateMemoryObject(m_memoryInitSize, m_memoryMaxSize, GetScriptContext()); } -void -WebAssemblyModule::SetSignature(uint32 index, Wasm::WasmSignature * signature) +bool +WebAssemblyModule::IsValidMemoryImport(const WebAssemblyMemory * memory) const +{ + return m_memImport && memory->GetInitialLength() >= m_memoryInitSize && memory->GetMaximumLength() <= m_memoryMaxSize; +} + +Wasm::WasmSignature * +WebAssemblyModule::GetSignatures() const { - Assert(index < GetSignatureCount()); - signature->SetSignatureId(index); - m_signatures[index] = signature; + return m_signatures; } Wasm::WasmSignature * @@ -267,7 +277,7 @@ WebAssemblyModule::GetSignature(uint32 index) const throw Wasm::WasmCompilationException(_u("Invalid signature index %u"), index); } - return m_signatures[index]; + return &m_signatures[index]; } uint32 @@ -276,37 +286,6 @@ WebAssemblyModule::GetSignatureCount() const return m_signaturesCount; } -void -WebAssemblyModule::CalculateEquivalentSignatures() -{ - Assert(m_equivalentSignatureMap == nullptr); - uint32 sigCount = GetSignatureCount(); - m_equivalentSignatureMap = AnewArray(&m_alloc, uint32, sigCount); - memset(m_equivalentSignatureMap, -1, sigCount * sizeof(uint32)); - - const auto IsEquivalentSignatureSet = [this](uint32 sigId) - { - return m_equivalentSignatureMap[sigId] != (uint32)-1; - }; - - for (uint32 iSig = 0; iSig < sigCount; iSig++) - { - if (!IsEquivalentSignatureSet(iSig)) - { - m_equivalentSignatureMap[iSig] = iSig; - Wasm::WasmSignature* sig = GetSignature(iSig); - // todo:: Find a better way than O(n^2) algo here - for (uint32 iSig2 = iSig + 1; iSig2 < sigCount; iSig2++) - { - if (!IsEquivalentSignatureSet(iSig2) && sig->IsEquivalent(GetSignature(iSig2))) - { - m_equivalentSignatureMap[iSig2] = iSig; - } - } - } - } -} - uint32 WebAssemblyModule::GetEquivalentSignatureId(uint32 sigId) const { @@ -319,55 +298,32 @@ WebAssemblyModule::GetEquivalentSignatureId(uint32 sigId) const } void -WebAssemblyModule::SetTableSize(uint32 entries) +WebAssemblyModule::InitializeTable(uint32 minEntries, uint32 maxEntries) { - m_indirectFuncCount = entries; -} - -void -WebAssemblyModule::SetTableValues(Wasm::WasmElementSegment* seg, uint32 index) -{ - if (index < m_elementsegCount) + if (m_hasTable) { - SetElementSeg(seg, index); + throw Wasm::WasmCompilationException(_u("Table already allocated")); } -} -void -WebAssemblyModule::ResolveTableElementOffsets() -{ - for (uint i = 0; i < GetElementSegCount(); ++i) + if (maxEntries < minEntries) { - Wasm::WasmElementSegment* eSeg = GetElementSeg(i); - eSeg->ResolveOffsets(*this); + throw Wasm::WasmCompilationException(_u("Table: max entries (%d) is less than min entries (%d)"), maxEntries, minEntries); } + m_hasTable = true; + m_tableInitSize = minEntries; + m_tableMaxSize = maxEntries; } -uint32 -WebAssemblyModule::GetTableValue(uint32 tableIndex) const +WebAssemblyTable * +WebAssemblyModule::CreateTable() const { - if (tableIndex >= GetTableSize()) - { - return Js::Constants::InvalidSourceIndex; - } - uint32 value = Js::Constants::UninitializedValue; - - for (uint32 i = 0; i < GetElementSegCount(); ++i) - { - Wasm::WasmElementSegment* eSeg = GetElementSeg(i); - value = eSeg->GetElement(tableIndex); - if (value != Js::Constants::UninitializedValue) - { - return value; - } - } - return value; + return WebAssemblyTable::Create(m_tableInitSize, m_tableMaxSize, GetScriptContext()); } -uint32 -WebAssemblyModule::GetTableSize() const +bool +WebAssemblyModule::IsValidTableImport(const WebAssemblyTable * table) const { - return m_indirectFuncCount; + return m_tableImport && table->GetInitialLength() >= m_tableInitSize && table->GetMaximumLength() <= m_tableMaxSize; } uint32 @@ -479,7 +435,7 @@ WebAssemblyModule::GetFunctionImport(uint32 i) const } void -WebAssemblyModule::AddGlobalImport(const char16* modName, uint32 modNameLen, const char16* fnName, uint32 fnNameLen, Wasm::ExternalKinds::ExternalKind kind, Wasm::WasmGlobal* importedGlobal) +WebAssemblyModule::AddGlobalImport(const char16* modName, uint32 modNameLen, const char16* fnName, uint32 fnNameLen, Wasm::WasmGlobal* importedGlobal) { Wasm::WasmImport* wi = Anew(&m_alloc, Wasm::WasmImport); wi->sigId = 0; @@ -493,6 +449,30 @@ WebAssemblyModule::AddGlobalImport(const char16* modName, uint32 modNameLen, con globals->Add(importedGlobal); } +void +WebAssemblyModule::AddMemoryImport(const char16* modName, uint32 modNameLen, const char16* importName, uint32 importNameLen) +{ + Wasm::WasmImport* wi = Anew(&m_alloc, Wasm::WasmImport); + wi->sigId = 0; + wi->fnName = importName; + wi->fnNameLen = importNameLen; + wi->modName = modName; + wi->modNameLen = modNameLen; + m_memImport = wi; +} + +void +WebAssemblyModule::AddTableImport(const char16* modName, uint32 modNameLen, const char16* importName, uint32 importNameLen) +{ + Wasm::WasmImport* wi = Anew(&m_alloc, Wasm::WasmImport); + wi->sigId = 0; + wi->fnName = importName; + wi->fnNameLen = importNameLen; + wi->modName = modName; + wi->modNameLen = modNameLen; + m_tableImport = wi; +} + uint WebAssemblyModule::GetOffsetFromInit(const Wasm::WasmNode& initExpr) const { @@ -530,15 +510,11 @@ WebAssemblyModule::AllocateDataSegs(uint32 count) m_datasegs = AnewArray(&m_alloc, Wasm::WasmDataSegment*, count); } -bool -WebAssemblyModule::AddDataSeg(Wasm::WasmDataSegment* seg, uint32 index) +void +WebAssemblyModule::SetDataSeg(Wasm::WasmDataSegment* seg, uint32 index) { - if (index >= m_datasegCount) - { - return false; - } + Assert(index < m_datasegCount); m_datasegs[index] = seg; - return true; } Wasm::WasmDataSegment* @@ -597,17 +573,15 @@ void WebAssemblyModule::SetSignatureCount(uint32 count) { Assert(m_signaturesCount == 0 && m_signatures == nullptr); m_signaturesCount = count; - m_signatures = AnewArray(&m_alloc, Wasm::WasmSignature*, count); + m_signatures = RecyclerNewArrayZ(GetRecycler(), Wasm::WasmSignature, count); } uint32 WebAssemblyModule::GetModuleEnvironmentSize() const { static const uint DOUBLE_SIZE_IN_INTS = sizeof(double) / sizeof(int); - // 1 for the heap - uint32 size = 1; + // 1 each for memory, table, and signatures + uint32 size = 3; size = UInt32Math::Add(size, GetWasmFunctionCount()); - // reserve space for as many function tables as there are signatures, though we won't fill them all - size = UInt32Math::Add(size, GetSignatureCount()); size = UInt32Math::Add(size, GetImportCount()); size = UInt32Math::Add(size, WAsmJs::ConvertToJsVarOffset(GetGlobalsByteSize())); return size; diff --git a/lib/Runtime/Library/WebAssemblyModule.h b/lib/Runtime/Library/WebAssemblyModule.h index 3fd51499aed..98474f989c8 100644 --- a/lib/Runtime/Library/WebAssemblyModule.h +++ b/lib/Runtime/Library/WebAssemblyModule.h @@ -68,22 +68,23 @@ class WebAssemblyModule : public DynamicObject Wasm::FunctionIndexTypes::Type GetFunctionIndexType(uint32 funcIndex) const; void InitializeMemory(uint32 minSize, uint32 maxSize); - void SetMemoryExported() { isMemExported = true; } - bool IsMemoryExported() const { return isMemExported; } - WebAssemblyMemory * GetMemory() const; + WebAssemblyMemory * CreateMemory() const; + bool HasMemory() const { return m_hasMemory; } + bool HasMemoryImport() const { return m_memImport != nullptr; } + bool IsValidMemoryImport(const WebAssemblyMemory * memory) const; - void SetSignature(uint32 index, Wasm::WasmSignature * signature); + Wasm::WasmSignature * GetSignatures() const; Wasm::WasmSignature* GetSignature(uint32 index) const; void SetSignatureCount(uint32 count); uint32 GetSignatureCount() const; - void CalculateEquivalentSignatures(); uint32 GetEquivalentSignatureId(uint32 sigId) const; - void SetTableSize(uint32 entries); - void SetTableValues(Wasm::WasmElementSegment* seg, uint32 index); - uint32 GetTableValue(uint32 indirTableIndex) const; - uint32 GetTableSize() const; + void InitializeTable(uint32 minEntries, uint32 maxEntries); + WebAssemblyTable * CreateTable() const; + bool HasTable() const { return m_hasTable; } + bool HasTableImport() const { return m_tableImport != nullptr; } + bool IsValidTableImport(const WebAssemblyTable * table) const; uint GetWasmFunctionCount() const; Wasm::WasmFunctionInfo* AddWasmFunctionInfo(Wasm::WasmSignature* funsig); @@ -97,18 +98,21 @@ class WebAssemblyModule : public DynamicObject uint32 GetImportCount() const; void AddFunctionImport(uint32 sigId, const char16* modName, uint32 modNameLen, const char16* fnName, uint32 fnNameLen); Wasm::WasmImport* GetFunctionImport(uint32 i) const; - void AddGlobalImport(const char16* modName, uint32 modNameLen, const char16* fnName, uint32 fnNameLen, Wasm::ExternalKinds::ExternalKind kind, Wasm::WasmGlobal* importedGlobal); + void AddGlobalImport(const char16* modName, uint32 modNameLen, const char16* fnName, uint32 fnNameLen, Wasm::WasmGlobal* importedGlobal); + void AddMemoryImport(const char16* modName, uint32 modNameLen, const char16* importName, uint32 importNameLen); + void AddTableImport(const char16* modName, uint32 modNameLen, const char16* importName, uint32 importNameLen); + Wasm::WasmImport * GetMemoryImport() const { return m_memImport; } + Wasm::WasmImport * GetTableImport() const { return m_tableImport; } uint GetOffsetFromInit(const Wasm::WasmNode& initexpr) const; void AllocateDataSegs(uint32 count); - bool AddDataSeg(Wasm::WasmDataSegment* seg, uint32 index); + void SetDataSeg(Wasm::WasmDataSegment* seg, uint32 index); Wasm::WasmDataSegment* GetDataSeg(uint32 index) const; uint32 GetDataSegCount() const { return m_datasegCount; } void AllocateElementSegs(uint32 count); void SetElementSeg(Wasm::WasmElementSegment* seg, uint32 index); - void ResolveTableElementOffsets(); Wasm::WasmElementSegment* GetElementSeg(uint32 index) const; uint32 GetElementSegCount() const { return m_elementsegCount; } @@ -117,11 +121,14 @@ class WebAssemblyModule : public DynamicObject uint32 GetModuleEnvironmentSize() const; - uint GetHeapOffset() const { return 0; } - uint GetImportFuncOffset() const { return GetHeapOffset() + 1; } + // elements at known offsets + static uint GetMemoryOffset() { return 0; } + static uint GetImportFuncOffset() { return GetMemoryOffset() + 1; } + + // elements at instance dependent offsets uint GetFuncOffset() const { return GetImportFuncOffset() + GetImportCount(); } uint GetTableEnvironmentOffset() const { return GetFuncOffset() + GetWasmFunctionCount(); } - uint GetGlobalOffset() const { return GetTableEnvironmentOffset() + GetSignatureCount(); } + uint GetGlobalOffset() const { return GetTableEnvironmentOffset() + 1; } uint GetOffsetForGlobal(Wasm::WasmGlobal* global); uint AddGlobalByteSizeToOffset(Wasm::WasmTypes::WasmType type, uint32 offset) const; uint GetGlobalsByteSize() const; @@ -137,10 +144,15 @@ class WebAssemblyModule : public DynamicObject WasmGlobalsList * globals; private: + bool m_hasTable; + bool m_hasMemory; // The binary buffer is recycler allocated, tied the lifetime of the buffer to the module const byte* m_binaryBuffer; - WebAssemblyMemory * m_memory; - Wasm::WasmSignature** m_signatures; + uint32 m_memoryInitSize; + uint32 m_memoryMaxSize; + uint32 m_tableInitSize; + uint32 m_tableMaxSize; + Wasm::WasmSignature* m_signatures; uint32* m_indirectfuncs; Wasm::WasmElementSegment** m_elementsegs; typedef JsUtil::List WasmFunctionInfosList; @@ -148,12 +160,13 @@ class WebAssemblyModule : public DynamicObject Wasm::WasmExport* m_exports; typedef JsUtil::List WasmImportsList; WasmImportsList* m_imports; + Wasm::WasmImport* m_memImport; + Wasm::WasmImport* m_tableImport; Wasm::WasmDataSegment** m_datasegs; Wasm::WasmBinaryReader* m_reader; uint32* m_equivalentSignatureMap; uint m_signaturesCount; - uint m_indirectFuncCount; uint m_exportCount; uint32 m_datasegCount; uint32 m_elementsegCount; @@ -161,8 +174,6 @@ class WebAssemblyModule : public DynamicObject uint32 m_startFuncIndex; ArenaAllocator m_alloc; - - bool isMemExported; }; } // namespace Js diff --git a/lib/Runtime/Library/WebAssemblyTable.cpp b/lib/Runtime/Library/WebAssemblyTable.cpp new file mode 100644 index 00000000000..a527b3172fb --- /dev/null +++ b/lib/Runtime/Library/WebAssemblyTable.cpp @@ -0,0 +1,273 @@ +//------------------------------------------------------------------------------------------------------- +// Copyright (C) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. +//------------------------------------------------------------------------------------------------------- + +#include "RuntimeLibraryPch.h" + +#ifdef ENABLE_WASM + +namespace Js +{ + +WebAssemblyTable::WebAssemblyTable(Var * values, uint32 currentLength, uint32 initialLength, uint32 maxLength, DynamicType * type) : + DynamicObject(type), + m_values(values), + m_currentLength(currentLength), + m_initialLength(initialLength), + m_maxLength(maxLength) +{ +} + +/* static */ +bool +WebAssemblyTable::Is(Var value) +{ + return JavascriptOperators::GetTypeId(value) == TypeIds_WebAssemblyTable; +} + +/* static */ +WebAssemblyTable * +WebAssemblyTable::FromVar(Var value) +{ + Assert(WebAssemblyTable::Is(value)); + return static_cast(value); +} + +Var +WebAssemblyTable::NewInstance(RecyclableObject* function, CallInfo callInfo, ...) +{ + PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); + + ARGUMENTS(args, callInfo); + ScriptContext* scriptContext = function->GetScriptContext(); + + AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'"); + + Var newTarget = callInfo.Flags & CallFlags_NewTarget ? args.Values[args.Info.Count] : args[0]; + bool isCtorSuperCall = (callInfo.Flags & CallFlags_New) && newTarget != nullptr && !JavascriptOperators::IsUndefined(newTarget); + Assert(isCtorSuperCall || !(callInfo.Flags & CallFlags_New) || args[0] == nullptr); + + if (!(callInfo.Flags & CallFlags_New) || (newTarget && JavascriptOperators::IsUndefinedObject(newTarget))) + { + JavascriptError::ThrowTypeError(scriptContext, JSERR_ClassConstructorCannotBeCalledWithoutNew); + } + + if (args.Info.Count < 2 || !JavascriptOperators::IsObject(args[1])) + { + JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedObject); + } + DynamicObject * memoryDescriptor = JavascriptObject::FromVar(args[1]); + + PropertyRecord const * elementPropRecord = nullptr; + scriptContext->GetOrAddPropertyRecord(_u("element"), lstrlen(_u("element")), &elementPropRecord); + Var elementVar = JavascriptOperators::OP_GetProperty(memoryDescriptor, elementPropRecord->GetPropertyId(), scriptContext); + if (!JavascriptOperators::StrictEqualString(elementVar, scriptContext->GetLibrary()->CreateStringFromCppLiteral(_u("anyfunc")))) + { + JavascriptError::ThrowTypeError(scriptContext, WASMERR_ExpectedAnyFunc); + } + + PropertyRecord const * initPropRecord = nullptr; + scriptContext->GetOrAddPropertyRecord(_u("initial"), lstrlen(_u("initial")), &initPropRecord); + Var initVar = JavascriptOperators::OP_GetProperty(memoryDescriptor, initPropRecord->GetPropertyId(), scriptContext); + uint32 initial = WebAssembly::ToNonWrappingUint32(initVar, scriptContext); + + PropertyRecord const * maxPropRecord = nullptr; + scriptContext->GetOrAddPropertyRecord(_u("maximum"), lstrlen(_u("maximum")), &maxPropRecord); + uint32 maximum = UINT_MAX; + if (JavascriptOperators::OP_HasProperty(memoryDescriptor, maxPropRecord->GetPropertyId(), scriptContext)) + { + Var maxVar = JavascriptOperators::OP_GetProperty(memoryDescriptor, initPropRecord->GetPropertyId(), scriptContext); + maximum = WebAssembly::ToNonWrappingUint32(maxVar, scriptContext); + } + return Create(initial, maximum, scriptContext); +} + +Var +WebAssemblyTable::EntryGetterLength(RecyclableObject* function, CallInfo callInfo, ...) +{ + PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); + + ARGUMENTS(args, callInfo); + ScriptContext* scriptContext = function->GetScriptContext(); + + Assert(!(callInfo.Flags & CallFlags_New)); + + if (args.Info.Count == 0 || !WebAssemblyTable::Is(args[0])) + { + JavascriptError::ThrowTypeError(scriptContext, WASMERR_NeedTableObject); + } + WebAssemblyTable * table = WebAssemblyTable::FromVar(args[0]); + return JavascriptNumber::ToVar(table->m_currentLength, scriptContext); +} + +Var +WebAssemblyTable::EntryGrow(RecyclableObject* function, CallInfo callInfo, ...) +{ + PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); + + ARGUMENTS(args, callInfo); + ScriptContext* scriptContext = function->GetScriptContext(); + + Assert(!(callInfo.Flags & CallFlags_New)); + + if (args.Info.Count == 0 || !WebAssemblyTable::Is(args[0])) + { + JavascriptError::ThrowTypeError(scriptContext, WASMERR_NeedTableObject); + } + WebAssemblyTable * table = WebAssemblyTable::FromVar(args[0]); + + Var deltaVar = scriptContext->GetLibrary()->GetUndefined(); + if (args.Info.Count >= 2) + { + deltaVar = args[1]; + } + uint32 delta = WebAssembly::ToNonWrappingUint32(deltaVar, scriptContext); + if ((uint64)table->m_currentLength + delta > (uint64)table->m_maxLength) + { + JavascriptError::ThrowRangeError(scriptContext, JSERR_ArgumentOutOfRange); + } + CompileAssert(sizeof(m_maxLength) == sizeof(uint32)); + + uint32 newLength = table->m_currentLength + delta; + Var * newValues = RecyclerNewArrayZ(scriptContext->GetRecycler(), Var, newLength); + memcpy_s(newValues, newLength, table->m_values, table->m_currentLength); + + table->m_values = newValues; + table->m_currentLength = newLength; + + return scriptContext->GetLibrary()->GetUndefined(); +} + +Var +WebAssemblyTable::EntryGet(RecyclableObject* function, CallInfo callInfo, ...) +{ + PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); + + ARGUMENTS(args, callInfo); + ScriptContext* scriptContext = function->GetScriptContext(); + + Assert(!(callInfo.Flags & CallFlags_New)); + + if (args.Info.Count == 0 || !WebAssemblyTable::Is(args[0])) + { + JavascriptError::ThrowTypeError(scriptContext, WASMERR_NeedTableObject); + } + WebAssemblyTable * table = WebAssemblyTable::FromVar(args[0]); + + Var indexVar = scriptContext->GetLibrary()->GetUndefined(); + if (args.Info.Count >= 2) + { + indexVar = args[1]; + } + uint32 index = WebAssembly::ToNonWrappingUint32(indexVar, scriptContext); + if (index > table->m_currentLength) + { + JavascriptError::ThrowRangeError(scriptContext, JSERR_ArgumentOutOfRange); + } + if (!table->m_values[index]) + { + return scriptContext->GetLibrary()->GetNull(); + } + + return table->m_values[index]; +} + +Var +WebAssemblyTable::EntrySet(RecyclableObject* function, CallInfo callInfo, ...) +{ + PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); + + ARGUMENTS(args, callInfo); + ScriptContext* scriptContext = function->GetScriptContext(); + + Assert(!(callInfo.Flags & CallFlags_New)); + + if (args.Info.Count == 0 || !WebAssemblyTable::Is(args[0])) + { + JavascriptError::ThrowTypeError(scriptContext, WASMERR_NeedTableObject); + } + WebAssemblyTable * table = WebAssemblyTable::FromVar(args[0]); + + if (args.Info.Count < 3) + { + JavascriptError::ThrowTypeError(scriptContext, WASMERR_NeedWebAssemblyFunc); + } + Var indexVar = args[1]; + Var value = args[2]; + + if (JavascriptOperators::IsNull(value)) + { + value = nullptr; + } + else if (!AsmJsScriptFunction::IsWasmScriptFunction(args[2])) + { + JavascriptError::ThrowTypeError(scriptContext, WASMERR_NeedWebAssemblyFunc); + } + + uint32 index = WebAssembly::ToNonWrappingUint32(indexVar, scriptContext); + if (index > table->m_currentLength) + { + JavascriptError::ThrowRangeError(scriptContext, JSERR_ArgumentOutOfRange); + } + + table->m_values[index] = value; + + return scriptContext->GetLibrary()->GetUndefined(); +} + +WebAssemblyTable * +WebAssemblyTable::Create(uint32 initial, uint32 maximum, ScriptContext * scriptContext) +{ + Var * values = nullptr; + if (initial > 0) + { + values = RecyclerNewArrayZ(scriptContext->GetRecycler(), Var, initial); + } + return RecyclerNew(scriptContext->GetRecycler(), WebAssemblyTable, values, initial, initial, maximum, scriptContext->GetLibrary()->GetWebAssemblyTableType()); +} + +void +WebAssemblyTable::DirectSetValue(uint index, Var val) +{ + Assert(index < m_currentLength); + Assert(!val || AsmJsScriptFunction::Is(val)); + m_values[index] = val; +} + +Var +WebAssemblyTable::DirectGetValue(uint index) const +{ + Assert(index < m_currentLength); + Var val = m_values[index]; + Assert(!val || AsmJsScriptFunction::Is(val)); + return val; +} + +Var * +WebAssemblyTable::GetValues() const +{ + return m_values; +} + +uint32 +WebAssemblyTable::GetCurrentLength() const +{ + return m_currentLength; +} + +uint32 +WebAssemblyTable::GetInitialLength() const +{ + return m_initialLength; +} + +uint32 +WebAssemblyTable::GetMaximumLength() const +{ + return m_maxLength; +} + +} // namespace Js + +#endif // ENABLE_WASM diff --git a/lib/Runtime/Library/WebAssemblyTable.h b/lib/Runtime/Library/WebAssemblyTable.h new file mode 100644 index 00000000000..cdcf78b9756 --- /dev/null +++ b/lib/Runtime/Library/WebAssemblyTable.h @@ -0,0 +1,54 @@ +//------------------------------------------------------------------------------------------------------- +// Copyright (C) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. +//------------------------------------------------------------------------------------------------------- + +#pragma once + +namespace Js +{ + class WebAssemblyTable : public DynamicObject + { +#ifdef ENABLE_WASM + public: + class EntryInfo + { + public: + static FunctionInfo NewInstance; + static FunctionInfo GetterLength; + static FunctionInfo Grow; + static FunctionInfo Get; + static FunctionInfo Set; + }; + WebAssemblyTable(Var * values, uint32 currentLength, uint32 initialLength, uint32 maxLength, DynamicType * type); + static Var NewInstance(RecyclableObject* function, CallInfo callInfo, ...); + static Var EntryGetterLength(RecyclableObject* function, CallInfo callInfo, ...); + static Var EntryGrow(RecyclableObject* function, CallInfo callInfo, ...); + static Var EntryGet(RecyclableObject* function, CallInfo callInfo, ...); + static Var EntrySet(RecyclableObject* function, CallInfo callInfo, ...); + + static bool Is(Var aValue); + static WebAssemblyTable * FromVar(Var aValue); + + static WebAssemblyTable * Create(uint32 initial, uint32 maximum, ScriptContext * scriptContext); + + uint32 GetCurrentLength() const; + uint32 GetInitialLength() const; + uint32 GetMaximumLength() const; + + Var * GetValues() const; + + void DirectSetValue(uint index, Var val); + Var DirectGetValue(uint index) const; + + static uint32 GetOffsetOfValues() { return offsetof(WebAssemblyTable, m_values); } + static uint32 GetOffsetOfCurrentLength() { return offsetof(WebAssemblyTable, m_currentLength); } + private: + uint32 m_initialLength; + uint32 m_maxLength; + + uint32 m_currentLength; + Var * m_values; +#endif + }; +} diff --git a/lib/Runtime/Runtime.h b/lib/Runtime/Runtime.h index 5633883724c..ffef36c263f 100644 --- a/lib/Runtime/Runtime.h +++ b/lib/Runtime/Runtime.h @@ -515,8 +515,10 @@ enum tagDEBUG_EVENT_INFO_TYPE #include "Library/SharedArrayBuffer.h" #include "Library/TypedArray.h" #include "Library/JavascriptBoolean.h" +#include "Library/WebAssemblyTable.h" #include "Library/WebAssemblyMemory.h" #include "Library/WebAssemblyModule.h" +#include "Library/WebAssembly.h" #include "Language/ModuleRecordBase.h" #include "Language/SourceTextModuleRecord.h" @@ -541,6 +543,8 @@ enum tagDEBUG_EVENT_INFO_TYPE #include "Debug/TTEventLog.h" #endif +#include "../WasmReader/WasmReader.h" + // // .inl files // diff --git a/lib/WasmReader/WasmBinaryReader.cpp b/lib/WasmReader/WasmBinaryReader.cpp index 602252ad581..e0adc4276c9 100644 --- a/lib/WasmReader/WasmBinaryReader.cpp +++ b/lib/WasmReader/WasmBinaryReader.cpp @@ -148,7 +148,7 @@ WasmBinaryReader::ProcessCurrentSection() switch (m_currentSection.code) { case bSectMemory: - ReadMemorySection(); + ReadMemorySection(false); break; case bSectSignatures: ReadSignatures(); @@ -172,7 +172,7 @@ WasmBinaryReader::ProcessCurrentSection() ReadDataSegments(); break; case bSectIndirectFunctionTable: - ReadTableSection(); + ReadTableSection(false); break; case bSectElement: ReadElementSection(); @@ -608,10 +608,18 @@ WasmBinaryReader::EndOfModule() // readers void -WasmBinaryReader::ReadMemorySection() +WasmBinaryReader::ReadMemorySection(bool isImportSection) { UINT length = 0; - UINT32 count = LEB128(length); + UINT32 count; + if (isImportSection) + { + count = 1; + } + else + { + count = LEB128(length); + } if (count > 1) { ThrowDecodingError(_u("Maximum of 1 memory allowed")); @@ -621,7 +629,7 @@ WasmBinaryReader::ReadMemorySection() { uint32 flags = LEB128(length); uint32 minPage = LEB128(length); - uint32 maxPage = minPage; + uint32 maxPage = UINT32_MAX; if (flags & 0x1) { maxPage = LEB128(length); @@ -640,8 +648,9 @@ WasmBinaryReader::ReadSignatures() for (UINT32 i = 0; i < count; i++) { TRACE_WASM_DECODER(_u("Signature #%u"), i); - WasmSignature * sig = Anew(m_alloc, WasmSignature, m_alloc); + WasmSignature * sig = m_module->GetSignature(i); + sig->SetSignatureId(i); int8 form = ReadConst(); if (form != LanguageTypes::func) { @@ -649,7 +658,7 @@ WasmBinaryReader::ReadSignatures() } UINT32 paramCount = LEB128(len); WasmTypes::WasmType type; - sig->AllocateParams(paramCount); + sig->AllocateParams(paramCount, m_module->GetRecycler()); for (UINT32 j = 0; j < paramCount; j++) { @@ -667,7 +676,7 @@ WasmBinaryReader::ReadSignatures() type = ReadWasmType(len); sig->SetResultType(type); } - m_module->SetSignature(i, sig); + sig->FinalizeSignature(); } } @@ -728,19 +737,15 @@ void WasmBinaryReader::ReadExportTable() break; } case ExternalKinds::Memory: + case ExternalKinds::Table: + if (index != 0) { - if (index != 0) - { - ThrowDecodingError(_u("Invalid memory index %s"), index); - } - m_module->SetMemoryExported(); - break; + ThrowDecodingError(_u("Invalid index %s"), index); } + // fallthrough case ExternalKinds::Global: m_module->SetExport(iExport, index, exportName, nameLength, kind); break; - case ExternalKinds::Table: - ThrowDecodingError(_u("Exported Kind Table, NYI")); default: ThrowDecodingError(_u("Exported Kind %d, NYI"), kind); break; @@ -749,16 +754,24 @@ void WasmBinaryReader::ReadExportTable() } } -void WasmBinaryReader::ReadTableSection() +void WasmBinaryReader::ReadTableSection(bool isImportSection) { uint32 length; - uint32 entries = LEB128(length); + uint32 entries; + if (isImportSection) + { + entries = 1; + } + else + { + entries = LEB128(length); + } if (entries > 1) { ThrowDecodingError(_u("Maximum of one table allowed")); } - if (entries > 0) + if (entries == 1) { int8 elementType = ReadConst(); if (elementType != LanguageTypes::anyfunc) @@ -767,16 +780,13 @@ void WasmBinaryReader::ReadTableSection() } uint32 flags = LEB128(length); uint32 initialLength = LEB128(length); + uint32 maximumLength = UINT32_MAX; if (flags & 0x1) { - uint32 maximumLength = LEB128(length); - - // Allocate maximum length for now until resizing supported - initialLength = maximumLength; + maximumLength = LEB128(length); } - m_module->SetTableSize(initialLength); - m_module->CalculateEquivalentSignatures(); - TRACE_WASM_DECODER(_u("Indirect table: %u entries"), initialLength); + m_module->InitializeTable(initialLength, maximumLength); + TRACE_WASM_DECODER(_u("Indirect table: %u to %u entries"), initialLength, maximumLength); } } @@ -814,7 +824,7 @@ WasmBinaryReader::ReadElementSection() } eSeg->AddElement(elem, *m_module); } - m_module->SetTableValues(eSeg, i); + m_module->SetElementSeg(eSeg, i); } } @@ -843,7 +853,7 @@ WasmBinaryReader::ReadDataSegments() WasmDataSegment *dseg = Anew(m_alloc, WasmDataSegment, m_alloc, initExpr, dataByteLen, m_pc); CheckBytesLeft(dataByteLen); m_pc += dataByteLen; - m_module->AddDataSeg(dseg, i); + m_module->SetDataSeg(dseg, i); } } @@ -960,13 +970,18 @@ WasmBinaryReader::ReadImportEntries() { ThrowDecodingError(_u("I64 Globals, NYI")); } - m_module->AddGlobalImport(modName, modNameLen, fnName, fnNameLen, kind, importedGlobal); + m_module->AddGlobalImport(modName, modNameLen, fnName, fnNameLen, importedGlobal); break; } case ExternalKinds::Table: - ThrowDecodingError(_u("Imported Kind Table, NYI")); + ReadTableSection(true); + m_module->AddTableImport(modName, modNameLen, fnName, fnNameLen); + break; case ExternalKinds::Memory: - ThrowDecodingError(_u("Imported Kind Memory, NYI")); + ReadMemorySection(true); + m_module->AddMemoryImport(modName, modNameLen, fnName, fnNameLen); + + break; default: ThrowDecodingError(_u("Imported Kind %d, NYI"), kind); break; diff --git a/lib/WasmReader/WasmBinaryReader.h b/lib/WasmReader/WasmBinaryReader.h index c6bdfb1a6bd..d0261703144 100644 --- a/lib/WasmReader/WasmBinaryReader.h +++ b/lib/WasmReader/WasmBinaryReader.h @@ -64,12 +64,12 @@ namespace Wasm // Module readers void ValidateModuleHeader(); SectionHeader ReadSectionHeader(); - void ReadMemorySection(); + void ReadMemorySection(bool isImportSection); void ReadSignatures(); void ReadFunctionsSignatures(); void ReadFunctionHeaders(); void ReadExportTable(); - void ReadTableSection(); + void ReadTableSection(bool isImportSection); void ReadDataSegments(); void ReadImportEntries(); void ReadStartFunction(); diff --git a/lib/WasmReader/WasmByteCodeGenerator.cpp b/lib/WasmReader/WasmByteCodeGenerator.cpp index 57751fe9bd6..1c96a87f189 100644 --- a/lib/WasmReader/WasmByteCodeGenerator.cpp +++ b/lib/WasmReader/WasmByteCodeGenerator.cpp @@ -208,6 +208,7 @@ WasmModuleGenerator::GenerateFunctionHeader(uint32 index) Js::AsmJsFunctionInfo* info = body->GetAsmJsFunctionInfo(); info->SetWasmReaderInfo(readerInfo); + info->SetWebAssemblyModule(m_module); if (wasmInfo->GetParamCount() >= Js::Constants::InvalidArgSlot) { @@ -215,7 +216,7 @@ WasmModuleGenerator::GenerateFunctionHeader(uint32 index) } Js::ArgSlot paramCount = (Js::ArgSlot)wasmInfo->GetParamCount(); info->SetArgCount(paramCount); - + info->SetWasmSignature(wasmInfo->GetSignature()); Js::ArgSlot argSizeLength = max(paramCount, 3ui16); info->SetArgSizeArrayLength(argSizeLength); uint* argSizeArray = RecyclerNewArrayLeafZ(m_recycler, uint, argSizeLength); @@ -804,7 +805,6 @@ WasmBytecodeGenerator::EmitCall() case wbCallIndirect: indirectIndexInfo = PopEvalStack(); signatureId = GetReader()->m_currentNode.call.num; - signatureId = m_module->GetEquivalentSignatureId(signatureId); calleeSignature = m_module->GetSignature(signatureId); ReleaseLocation(&indirectIndexInfo); break; @@ -905,9 +905,9 @@ WasmBytecodeGenerator::EmitCall() { throw WasmCompilationException(_u("Indirect call index must be int type")); } - // todo:: Add bounds check. Asm.js doesn't need it because there has to be an & operator - m_writer.AsmSlot(Js::OpCodeAsmJs::LdSlotArr, 0, 1, calleeSignature->GetSignatureId() + m_module->GetTableEnvironmentOffset()); - m_writer.AsmSlot(Js::OpCodeAsmJs::LdArr_Func, 0, 0, indirectIndexInfo.location); + m_writer.AsmSlot(Js::OpCodeAsmJs::LdSlotArr, 0, 1, m_module->GetTableEnvironmentOffset()); + m_writer.AsmSlot(Js::OpCodeAsmJs::LdArr_WasmFunc, 0, 0, indirectIndexInfo.location); + m_writer.AsmReg1IntConst1(Js::OpCodeAsmJs::CheckSignature, 0, calleeSignature->GetSignatureId()); break; default: Assume(UNREACHED); diff --git a/lib/WasmReader/WasmElementSegment.cpp b/lib/WasmReader/WasmElementSegment.cpp index afeabf7b6c4..c85da1e13df 100644 --- a/lib/WasmReader/WasmElementSegment.cpp +++ b/lib/WasmReader/WasmElementSegment.cpp @@ -15,9 +15,7 @@ namespace Wasm m_offsetExpr(initExpr), m_numElem(numElem), m_offset(0), - m_limit(0), m_elemIdx(0), - m_isOffsetResolved(false), m_elems(nullptr) {} @@ -29,22 +27,6 @@ namespace Wasm memset(m_elems, Js::Constants::UninitializedValue, m_numElem * sizeof(UINT32)); } - void WasmElementSegment::ResolveOffsets(const Js::WebAssemblyModule& module) - { - if (m_elems == nullptr) - { - return; - } - m_offset = module.GetOffsetFromInit(m_offsetExpr); // i32 or global (i32) - m_limit = UInt32Math::Add(m_offset, m_numElem); - - if (m_limit > module.GetTableSize()) - { - throw WasmCompilationException(_u("Out of bounds element in Table[%d][%d], max index: %d"), m_index, m_limit - 1, module.GetTableSize() - 1); - } - m_isOffsetResolved = true; - } - void WasmElementSegment::AddElement(const UINT32 funcIndex, const Js::WebAssemblyModule& module) { @@ -56,24 +38,16 @@ namespace Wasm m_elems[m_elemIdx++] = funcIndex; } - inline bool - WasmElementSegment::IsOffsetResolved() const + UINT32 + WasmElementSegment::GetElement(const UINT32 tableIndex) const { - return m_isOffsetResolved; + Assert(m_elems != nullptr); + return m_elems[tableIndex]; } - UINT32 - WasmElementSegment::GetElement(const UINT32 tableIndex) const + uint32 WasmElementSegment::GetDestAddr(Js::WebAssemblyModule* module) const { - if (!IsOffsetResolved()) - { - throw WasmCompilationException(_u("Offset not resolved: Cannot access table elements.")); - } - if (m_offset > tableIndex || tableIndex >= m_limit) - { - return Js::Constants::UninitializedValue; - } - return m_elems[tableIndex - m_offset]; + return module->GetOffsetFromInit(m_offsetExpr); } } // namespace Wasm diff --git a/lib/WasmReader/WasmElementSegment.h b/lib/WasmReader/WasmElementSegment.h index 721a1c29f1d..a66c8d93d8b 100644 --- a/lib/WasmReader/WasmElementSegment.h +++ b/lib/WasmReader/WasmElementSegment.h @@ -15,7 +15,8 @@ namespace Wasm void AddElement(const UINT32 funcIndex, const Js::WebAssemblyModule& module); UINT32 GetElement(const UINT32 tableIndex) const; UINT32 GetNumElements() const { return m_numElem; } - void ResolveOffsets(const Js::WebAssemblyModule& module); + + uint32 GetDestAddr(Js::WebAssemblyModule* module) const; private: ArenaAllocator* m_alloc; @@ -23,12 +24,9 @@ namespace Wasm const WasmNode m_offsetExpr; UINT32 m_numElem; UINT32 m_offset; - UINT32 m_limit; UINT32 m_elemIdx; - bool m_isOffsetResolved; UINT32* m_elems; void Init(const Js::WebAssemblyModule& module); - bool IsOffsetResolved() const; }; } // Namespace Wasm diff --git a/lib/WasmReader/WasmSections.h b/lib/WasmReader/WasmSections.h index 95b32376826..e187dbc2f67 100644 --- a/lib/WasmReader/WasmSections.h +++ b/lib/WasmReader/WasmSections.h @@ -12,9 +12,9 @@ WASM_SECTION(Memory , "memory" , fSectNone , Invalid WASM_SECTION(Global , "global" , fSectNone , Invalid ) WASM_SECTION(ExportTable , "export" , fSectNone , Invalid ) WASM_SECTION(StartFunction , "start" , fSectNone , Signatures ) -WASM_SECTION(Element , "element" , fSectNone , IndirectFunctionTable) +WASM_SECTION(Element , "element" , fSectNone , Invalid ) WASM_SECTION(FunctionBodies , "code" , fSectNone , FunctionSignatures) -WASM_SECTION(DataSegments , "data" , fSectNone , Memory ) +WASM_SECTION(DataSegments , "data" , fSectNone , Invalid ) WASM_SECTION(Names , "name" , fSectIgnore, Signatures ) WASM_SECTION(User , "user" , fSectIgnore, Invalid ) #undef WASM_SECTION diff --git a/lib/WasmReader/WasmSignature.cpp b/lib/WasmReader/WasmSignature.cpp index ec9175d2ee8..79c29eed304 100644 --- a/lib/WasmReader/WasmSignature.cpp +++ b/lib/WasmReader/WasmSignature.cpp @@ -10,20 +10,23 @@ namespace Wasm { -WasmSignature::WasmSignature(ArenaAllocator * alloc) : - m_alloc(alloc), +WasmSignature::WasmSignature() : m_resultType(WasmTypes::Void), m_id(Js::Constants::UninitializedValue), m_paramSize(Js::Constants::UninitializedValue), m_params(nullptr), - m_paramsCount(0) + m_paramsCount(0), + m_shortSig(Js::Constants::InvalidSignature) { } void -WasmSignature::AllocateParams(uint32 count) +WasmSignature::AllocateParams(uint32 count, Recycler * recycler) { - m_params = AnewArrayZ(m_alloc, Local, count); + if (count > 0) + { + m_params = RecyclerNewArrayLeafZ(recycler, Local, count); + } m_paramsCount = count; } @@ -79,9 +82,19 @@ WasmSignature::GetSignatureId() const return m_id; } +size_t +WasmSignature::GetShortSig() const +{ + return m_shortSig; +} + bool -WasmSignature::IsEquivalent(WasmSignature* sig) const +WasmSignature::IsEquivalent(const WasmSignature* sig) const { + if (m_shortSig != Js::Constants::InvalidSignature) + { + return sig->GetShortSig() == m_shortSig; + } if (GetResultType() == sig->GetResultType() && GetParamCount() == sig->GetParamCount() && GetParamsSize() == sig->GetParamsSize()) @@ -117,23 +130,57 @@ uint32 WasmSignature::GetParamSize(uint index) const } } -uint32 -WasmSignature::GetParamsSize() const +void +WasmSignature::FinalizeSignature() { - if (m_paramSize != Js::Constants::UninitializedValue) - { - return m_paramSize; - } + Assert(m_paramSize == Js::Constants::UninitializedValue); + Assert(m_shortSig == Js::Constants::InvalidSignature); - uint32 m_paramSize = 0; + m_paramSize = 0; for (uint32 i = 0; i < GetParamCount(); ++i) { m_paramSize += GetParamSize(i); } + CompileAssert(Local::Limit - 1 <= 4); + CompileAssert(Local::Void == 0); + + // 3 bits for result type, 2 for each arg + // we don't need to reserve a sentinel bit because there is no result type with value of 7 + int sigSize = 3 + 2 * GetParamCount(); + if (sigSize <= sizeof(m_shortSig) << 3) + { + m_shortSig = (m_shortSig << 3) | m_resultType; + for (uint32 i = 0; i < GetParamCount(); ++i) + { + // we can use 2 bits per arg by dropping void + m_shortSig = (m_shortSig << 2) | (m_params[i] - 1); + } + } +} + +uint32 +WasmSignature::GetParamsSize() const +{ return m_paramSize; } +WasmSignature * +WasmSignature::FromIDL(WasmSignatureIDL* sig) +{ + // must update WasmSignatureIDL when changing WasmSignature + CompileAssert(sizeof(Wasm::WasmSignature) == sizeof(WasmSignatureIDL)); + CompileAssert(offsetof(Wasm::WasmSignature, m_resultType) == offsetof(WasmSignatureIDL, resultType)); + CompileAssert(offsetof(Wasm::WasmSignature, m_id) == offsetof(WasmSignatureIDL, id)); + CompileAssert(offsetof(Wasm::WasmSignature, m_paramSize) == offsetof(WasmSignatureIDL, paramSize)); + CompileAssert(offsetof(Wasm::WasmSignature, m_paramsCount) == offsetof(WasmSignatureIDL, paramsCount)); + CompileAssert(offsetof(Wasm::WasmSignature, m_params) == offsetof(WasmSignatureIDL, params)); + CompileAssert(offsetof(Wasm::WasmSignature, m_shortSig) == offsetof(WasmSignatureIDL, shortSig)); + CompileAssert(sizeof(Local) == sizeof(int)); + + return reinterpret_cast(sig); +} + } // namespace Wasm #endif // ENABLE_WASM diff --git a/lib/WasmReader/WasmSignature.h b/lib/WasmReader/WasmSignature.h index 6dfed3c6bb6..e0ee94a3a3c 100644 --- a/lib/WasmReader/WasmSignature.h +++ b/lib/WasmReader/WasmSignature.h @@ -10,9 +10,9 @@ namespace Wasm class WasmSignature { public: - WasmSignature(ArenaAllocator * alloc); + WasmSignature(); - void AllocateParams(uint32 count); + void AllocateParams(uint32 count, Recycler * recycler); void SetParam(WasmTypes::WasmType type, uint32 index); void SetResultType(WasmTypes::WasmType type); void SetSignatureId(uint32 id); @@ -22,16 +22,21 @@ class WasmSignature uint32 GetParamCount() const; uint32 GetParamSize(uint index) const; uint32 GetParamsSize() const; + void FinalizeSignature(); uint32 GetSignatureId() const; + size_t GetShortSig() const; - bool IsEquivalent(WasmSignature* sig) const; + bool IsEquivalent(const WasmSignature* sig) const; + static WasmSignature * FromIDL(WasmSignatureIDL* sig); + + static uint GetOffsetOfShortSig() { return offsetof(WasmSignature, m_shortSig); } private: - ArenaAllocator* m_alloc; WasmTypes::WasmType m_resultType; - Local* m_params; - uint32 m_paramsCount; uint32 m_id; uint32 m_paramSize; + uint32 m_paramsCount; + size_t m_shortSig; + Local* m_params; }; } // namespace Wasm diff --git a/test/WasmSpec/baselines/imports.baseline b/test/WasmSpec/baselines/imports.baseline index 2b53df9224b..4eec5247bb2 100644 --- a/test/WasmSpec/baselines/imports.baseline +++ b/test/WasmSpec/baselines/imports.baseline @@ -1,76 +1,21 @@ -Unexpected Error while compiling imports.0.wasm -WebAssemblyCompileError: Compiling wasm failed: Exported Kind Table, NYI 13 14 42 13 13 13 -Unexpected Error while compiling imports.2.wasm -TypeError: Import is invalid -Unexpected Error while compiling imports.3.wasm -TypeError: Import is invalid -Unexpected Error while compiling imports.4.wasm -TypeError: Import is invalid -Unexpected Error while compiling imports.5.wasm -TypeError: Import is invalid -Unexpected Error while compiling imports.6.wasm -TypeError: Import is invalid -Unexpected Error while compiling imports.7.wasm -TypeError: Import is invalid Unexpected Error while compiling imports.8.wasm WebAssemblyCompileError: Compiling wasm failed: I64 Globals, NYI -Unexpected Error while compiling imports.9.wasm -TypeError: Import is invalid -Unexpected Error while compiling imports.10.wasm -TypeError: Import is invalid -Unexpected Error while compiling imports.11.wasm -WebAssemblyCompileError: Compiling wasm failed: Imported Kind Table, NYI -Unexpected Error while compiling imports.12.wasm -WebAssemblyCompileError: Compiling wasm failed: Imported Kind Table, NYI -Unexpected Error while compiling imports.13.wasm -WebAssemblyCompileError: Compiling wasm failed: Imported Kind Table, NYI -Unexpected Error while compiling imports.14.wasm -WebAssemblyCompileError: Compiling wasm failed: Imported Kind Table, NYI -Unexpected Error while compiling imports.15.wasm -WebAssemblyCompileError: Compiling wasm failed: Imported Kind Table, NYI -Unexpected Error while compiling imports.16.wasm -WebAssemblyCompileError: Compiling wasm failed: Imported Kind Table, NYI -Unexpected Error while compiling imports.17.wasm -WebAssemblyCompileError: Compiling wasm failed: Imported Kind Table, NYI -Unexpected Error while compiling imports.18.wasm -WebAssemblyCompileError: Compiling wasm failed: Imported Kind Table, NYI -Unexpected Error while compiling imports.19.wasm -WebAssemblyCompileError: Compiling wasm failed: Imported Kind Table, NYI -Unexpected Error while compiling imports.20.wasm -WebAssemblyCompileError: Compiling wasm failed: Imported Kind Table, NYI -Unexpected Error while compiling imports.21.wasm -WebAssemblyCompileError: Compiling wasm failed: Imported Kind Table, NYI -Unexpected Error while compiling imports.22.wasm -WebAssemblyCompileError: Compiling wasm failed: Imported Kind Table, NYI -Unexpected Error while compiling imports.23.wasm -WebAssemblyCompileError: Compiling wasm failed: Imported Kind Table, NYI -Unexpected Error while compiling imports.24.wasm -WebAssemblyCompileError: Compiling wasm failed: Imported Kind Memory, NYI -Unexpected Error while compiling imports.25.wasm -WebAssemblyCompileError: Compiling wasm failed: Imported Kind Memory, NYI -Unexpected Error while compiling imports.26.wasm -WebAssemblyCompileError: Compiling wasm failed: Imported Kind Memory, NYI -Unexpected Error while compiling imports.27.wasm -WebAssemblyCompileError: Compiling wasm failed: Imported Kind Memory, NYI -Unexpected Error while compiling imports.28.wasm -WebAssemblyCompileError: Compiling wasm failed: Imported Kind Memory, NYI -Unexpected Error while compiling imports.29.wasm -WebAssemblyCompileError: Compiling wasm failed: Imported Kind Memory, NYI -Unexpected Error while compiling imports.30.wasm -WebAssemblyCompileError: Compiling wasm failed: Imported Kind Memory, NYI -Unexpected Error while compiling imports.31.wasm -WebAssemblyCompileError: Compiling wasm failed: Imported Kind Memory, NYI -Unexpected Error while compiling imports.32.wasm -WebAssemblyCompileError: Compiling wasm failed: Imported Kind Memory, NYI -Unexpected Error while compiling imports.33.wasm -WebAssemblyCompileError: Compiling wasm failed: Imported Kind Memory, NYI -Unexpected Error while compiling imports.34.wasm -WebAssemblyCompileError: Compiling wasm failed: Imported Kind Memory, NYI -Unexpected Error while compiling imports.35.wasm -WebAssemblyCompileError: Compiling wasm failed: Imported Kind Memory, NYI -1/1 tests passed. +imports.wast:245: $assert_trap_0 failed, runtime trap NYI +imports.wast:248: $assert_trap_3 failed, runtime trap NYI +imports.wast:249: $assert_trap_4 failed, runtime trap NYI +imports.wast:262: $assert_trap_0 failed, runtime trap NYI +imports.wast:265: $assert_trap_3 failed, runtime trap NYI +imports.wast:266: $assert_trap_4 failed, runtime trap NYI +imports.wast:351: $assert_trap_3 failed, runtime trap NYI +imports.wast:362: $assert_trap_3 failed, runtime trap NYI +imports.wast:451: $assert_return_0 unexpectedly threw: WebAssemblyCompileError: Compiling wasm failed: function grow[0] at offset 5/6: Operator GrowMemory NYI +imports.wast:452: $assert_return_1 unexpectedly threw: WebAssemblyCompileError: Compiling wasm failed: function grow[0] at offset 5/6: Operator GrowMemory NYI +imports.wast:453: $assert_return_2 unexpectedly threw: WebAssemblyCompileError: Compiling wasm failed: function grow[0] at offset 5/6: Operator GrowMemory NYI +imports.wast:454: $assert_return_3 unexpectedly threw: WebAssemblyCompileError: Compiling wasm failed: function grow[0] at offset 5/6: Operator GrowMemory NYI +imports.wast:455: $assert_return_4 unexpectedly threw: WebAssemblyCompileError: Compiling wasm failed: function grow[0] at offset 5/6: Operator GrowMemory NYI +11/24 tests passed. diff --git a/test/WasmSpec/spec.js b/test/WasmSpec/spec.js index 8bf867bb41a..5639f0c6e76 100644 --- a/test/WasmSpec/spec.js +++ b/test/WasmSpec/spec.js @@ -73,7 +73,11 @@ function run(inPath, iStart, iEnd) { var jsonData = JSON.parse(data); var iTest = 0; - var registry = Object.assign({spectest: {print: print, global : 666}}, IMPORTS_FROM_OTHER_SCRIPT); + var registry = Object.assign({spectest: { + print: print, + global : 666, + table: new WebAssembly.Table({initial: 10, maximum: 20, element: 'anyfunc'}), memory: new WebAssembly.Memory({initial: 1, maximum: 2}) + }}, IMPORTS_FROM_OTHER_SCRIPT); for (var i = 0; i < jsonData.modules.length; ++i) { var module = jsonData.modules[i] || {}; try { diff --git a/test/WasmSpec/testsuite-bin/imports.json b/test/WasmSpec/testsuite-bin/imports.json index d229ad423b0..d939eca7b2f 100644 --- a/test/WasmSpec/testsuite-bin/imports.json +++ b/test/WasmSpec/testsuite-bin/imports.json @@ -1,7 +1,7 @@ { "modules": [{ "filename": "imports.0.wasm", - "commands": [] + "commands": [{"type": "register", "name": "test", "file": "testsuite/imports.wast", "line": 18}] }, { "filename": "imports.1.wasm", "commands": [{