| /* |
| * Copyright (C) 2016-2019 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #pragma once |
| |
| #if ENABLE(WEBASSEMBLY) |
| |
| #include "WasmParser.h" |
| #include "WasmSignatureInlines.h" |
| #include <wtf/DataLog.h> |
| |
| namespace JSC { namespace Wasm { |
| |
| enum class BlockType { |
| If, |
| Block, |
| Loop, |
| TopLevel |
| }; |
| |
| template<typename Stack> |
| Stack splitStack(BlockSignature signature, Stack& stack) |
| { |
| Stack result; |
| result.reserveInitialCapacity(signature->argumentCount()); |
| ASSERT(stack.size() >= signature->argumentCount()); |
| unsigned offset = stack.size() - signature->argumentCount(); |
| for (unsigned i = 0; i < signature->argumentCount(); ++i) |
| result.uncheckedAppend(stack.at(i + offset)); |
| stack.shrink(offset); |
| return result; |
| } |
| |
| template<typename Context> |
| class FunctionParser : public Parser<void> { |
| public: |
| using ExpressionType = typename Context::ExpressionType; |
| using ControlType = typename Context::ControlType; |
| using ExpressionList = typename Context::ExpressionList; |
| using Stack = typename Context::Stack; |
| using ResultList = typename Context::ResultList; |
| |
| FunctionParser(Context&, const uint8_t* functionStart, size_t functionLength, const Signature&, const ModuleInformation&); |
| |
| Result WARN_UNUSED_RETURN parse(); |
| |
| struct ControlEntry { |
| Stack enclosedExpressionStack; |
| Stack elseBlockStack; |
| ControlType controlData; |
| }; |
| |
| OpType currentOpcode() const { return m_currentOpcode; } |
| size_t currentOpcodeStartingOffset() const { return m_currentOpcodeStartingOffset; } |
| const Signature& signature() const { return m_signature; } |
| |
| Vector<ControlEntry>& controlStack() { return m_controlStack; } |
| |
| private: |
| static constexpr bool verbose = false; |
| |
| PartialResult WARN_UNUSED_RETURN parseBody(); |
| PartialResult WARN_UNUSED_RETURN parseExpression(); |
| PartialResult WARN_UNUSED_RETURN parseUnreachableExpression(); |
| PartialResult WARN_UNUSED_RETURN unifyControl(Vector<ExpressionType>&, unsigned level); |
| |
| #define WASM_TRY_POP_EXPRESSION_STACK_INTO(result, what) do { \ |
| WASM_PARSER_FAIL_IF(m_expressionStack.isEmpty(), "can't pop empty stack in " what); \ |
| result = m_expressionStack.takeLast(); \ |
| } while (0) |
| |
| template<OpType> |
| PartialResult WARN_UNUSED_RETURN unaryCase(); |
| |
| template<OpType> |
| PartialResult WARN_UNUSED_RETURN binaryCase(); |
| |
| #define WASM_TRY_ADD_TO_CONTEXT(add_expression) WASM_FAIL_IF_HELPER_FAILS(m_context.add_expression) |
| |
| // FIXME add a macro as above for WASM_TRY_APPEND_TO_CONTROL_STACK https://bugs.webkit.org/show_bug.cgi?id=165862 |
| |
| Context& m_context; |
| Stack m_expressionStack; |
| Vector<ControlEntry> m_controlStack; |
| const Signature& m_signature; |
| const ModuleInformation& m_info; |
| |
| OpType m_currentOpcode; |
| size_t m_currentOpcodeStartingOffset { 0 }; |
| |
| unsigned m_unreachableBlocks { 0 }; |
| unsigned m_loopIndex { 0 }; |
| }; |
| |
| template<typename Context> |
| FunctionParser<Context>::FunctionParser(Context& context, const uint8_t* functionStart, size_t functionLength, const Signature& signature, const ModuleInformation& info) |
| : Parser(functionStart, functionLength) |
| , m_context(context) |
| , m_signature(signature) |
| , m_info(info) |
| { |
| if (verbose) |
| dataLogLn("Parsing function starting at: ", (uintptr_t)functionStart, " of length: ", functionLength, " with signature: ", signature); |
| m_context.setParser(this); |
| } |
| |
| template<typename Context> |
| auto FunctionParser<Context>::parse() -> Result |
| { |
| uint32_t localGroupsCount; |
| |
| WASM_PARSER_FAIL_IF(!m_context.addArguments(m_signature), "can't add ", m_signature.argumentCount(), " arguments to Function"); |
| WASM_PARSER_FAIL_IF(!parseVarUInt32(localGroupsCount), "can't get local groups count"); |
| |
| uint64_t totalNumberOfLocals = m_signature.argumentCount(); |
| for (uint32_t i = 0; i < localGroupsCount; ++i) { |
| uint32_t numberOfLocals; |
| Type typeOfLocal; |
| |
| WASM_PARSER_FAIL_IF(!parseVarUInt32(numberOfLocals), "can't get Function's number of locals in group ", i); |
| totalNumberOfLocals += numberOfLocals; |
| WASM_PARSER_FAIL_IF(totalNumberOfLocals > maxFunctionLocals, "Function's number of locals is too big ", totalNumberOfLocals, " maximum ", maxFunctionLocals); |
| WASM_PARSER_FAIL_IF(!parseValueType(typeOfLocal), "can't get Function local's type in group ", i); |
| WASM_TRY_ADD_TO_CONTEXT(addLocal(typeOfLocal, numberOfLocals)); |
| } |
| |
| WASM_FAIL_IF_HELPER_FAILS(parseBody()); |
| |
| return { }; |
| } |
| |
| template<typename Context> |
| auto FunctionParser<Context>::parseBody() -> PartialResult |
| { |
| m_controlStack.append({ { }, { }, m_context.addTopLevel(&m_signature) }); |
| uint8_t op; |
| while (m_controlStack.size()) { |
| m_currentOpcodeStartingOffset = m_offset; |
| WASM_PARSER_FAIL_IF(!parseUInt8(op), "can't decode opcode"); |
| WASM_PARSER_FAIL_IF(!isValidOpType(op), "invalid opcode ", op); |
| |
| m_currentOpcode = static_cast<OpType>(op); |
| |
| if (verbose) { |
| dataLogLn("processing op (", m_unreachableBlocks, "): ", RawPointer(reinterpret_cast<void*>(op)), ", ", makeString(static_cast<OpType>(op)), " at offset: ", RawPointer(reinterpret_cast<void*>(m_offset))); |
| m_context.dump(m_controlStack, &m_expressionStack); |
| } |
| |
| if (m_unreachableBlocks) |
| WASM_FAIL_IF_HELPER_FAILS(parseUnreachableExpression()); |
| else { |
| WASM_FAIL_IF_HELPER_FAILS(parseExpression()); |
| } |
| } |
| |
| ASSERT(op == OpType::End); |
| return { }; |
| } |
| |
| template<typename Context> |
| template<OpType op> |
| auto FunctionParser<Context>::binaryCase() -> PartialResult |
| { |
| ExpressionType right; |
| ExpressionType left; |
| ExpressionType result; |
| |
| WASM_TRY_POP_EXPRESSION_STACK_INTO(right, "binary right"); |
| WASM_TRY_POP_EXPRESSION_STACK_INTO(left, "binary left"); |
| WASM_TRY_ADD_TO_CONTEXT(template addOp<op>(left, right, result)); |
| |
| m_expressionStack.append(result); |
| return { }; |
| } |
| |
| template<typename Context> |
| template<OpType op> |
| auto FunctionParser<Context>::unaryCase() -> PartialResult |
| { |
| ExpressionType value; |
| ExpressionType result; |
| |
| WASM_TRY_POP_EXPRESSION_STACK_INTO(value, "unary"); |
| WASM_TRY_ADD_TO_CONTEXT(template addOp<op>(value, result)); |
| |
| m_expressionStack.append(result); |
| return { }; |
| } |
| |
| template<typename Context> |
| auto FunctionParser<Context>::parseExpression() -> PartialResult |
| { |
| switch (m_currentOpcode) { |
| #define CREATE_CASE(name, id, b3op, inc) case OpType::name: return binaryCase<OpType::name>(); |
| FOR_EACH_WASM_BINARY_OP(CREATE_CASE) |
| #undef CREATE_CASE |
| |
| #define CREATE_CASE(name, id, b3op, inc) case OpType::name: return unaryCase<OpType::name>(); |
| FOR_EACH_WASM_UNARY_OP(CREATE_CASE) |
| #undef CREATE_CASE |
| |
| case Select: { |
| ExpressionType condition; |
| ExpressionType zero; |
| ExpressionType nonZero; |
| |
| WASM_TRY_POP_EXPRESSION_STACK_INTO(condition, "select condition"); |
| WASM_TRY_POP_EXPRESSION_STACK_INTO(zero, "select zero"); |
| WASM_TRY_POP_EXPRESSION_STACK_INTO(nonZero, "select non-zero"); |
| |
| ExpressionType result; |
| WASM_TRY_ADD_TO_CONTEXT(addSelect(condition, nonZero, zero, result)); |
| |
| m_expressionStack.append(result); |
| return { }; |
| } |
| |
| #define CREATE_CASE(name, id, b3op, inc) case OpType::name: |
| FOR_EACH_WASM_MEMORY_LOAD_OP(CREATE_CASE) { |
| uint32_t alignment; |
| uint32_t offset; |
| ExpressionType pointer; |
| ExpressionType result; |
| WASM_PARSER_FAIL_IF(!parseVarUInt32(alignment), "can't get load alignment"); |
| WASM_PARSER_FAIL_IF(alignment > memoryLog2Alignment(m_currentOpcode), "byte alignment ", 1ull << alignment, " exceeds load's natural alignment ", 1ull << memoryLog2Alignment(m_currentOpcode)); |
| WASM_PARSER_FAIL_IF(!parseVarUInt32(offset), "can't get load offset"); |
| WASM_TRY_POP_EXPRESSION_STACK_INTO(pointer, "load pointer"); |
| WASM_TRY_ADD_TO_CONTEXT(load(static_cast<LoadOpType>(m_currentOpcode), pointer, result, offset)); |
| m_expressionStack.append(result); |
| return { }; |
| } |
| |
| FOR_EACH_WASM_MEMORY_STORE_OP(CREATE_CASE) { |
| uint32_t alignment; |
| uint32_t offset; |
| ExpressionType value; |
| ExpressionType pointer; |
| WASM_PARSER_FAIL_IF(!parseVarUInt32(alignment), "can't get store alignment"); |
| WASM_PARSER_FAIL_IF(alignment > memoryLog2Alignment(m_currentOpcode), "byte alignment ", 1ull << alignment, " exceeds store's natural alignment ", 1ull << memoryLog2Alignment(m_currentOpcode)); |
| WASM_PARSER_FAIL_IF(!parseVarUInt32(offset), "can't get store offset"); |
| WASM_TRY_POP_EXPRESSION_STACK_INTO(value, "store value"); |
| WASM_TRY_POP_EXPRESSION_STACK_INTO(pointer, "store pointer"); |
| WASM_TRY_ADD_TO_CONTEXT(store(static_cast<StoreOpType>(m_currentOpcode), pointer, value, offset)); |
| return { }; |
| } |
| #undef CREATE_CASE |
| |
| case F32Const: { |
| uint32_t constant; |
| WASM_PARSER_FAIL_IF(!parseUInt32(constant), "can't parse 32-bit floating-point constant"); |
| m_expressionStack.append(m_context.addConstant(F32, constant)); |
| return { }; |
| } |
| |
| case I32Const: { |
| int32_t constant; |
| WASM_PARSER_FAIL_IF(!parseVarInt32(constant), "can't parse 32-bit constant"); |
| m_expressionStack.append(m_context.addConstant(I32, constant)); |
| return { }; |
| } |
| |
| case F64Const: { |
| uint64_t constant; |
| WASM_PARSER_FAIL_IF(!parseUInt64(constant), "can't parse 64-bit floating-point constant"); |
| m_expressionStack.append(m_context.addConstant(F64, constant)); |
| return { }; |
| } |
| |
| case I64Const: { |
| int64_t constant; |
| WASM_PARSER_FAIL_IF(!parseVarInt64(constant), "can't parse 64-bit constant"); |
| m_expressionStack.append(m_context.addConstant(I64, constant)); |
| return { }; |
| } |
| |
| case TableGet: { |
| WASM_PARSER_FAIL_IF(!Options::useWebAssemblyReferences(), "references are not enabled"); |
| unsigned tableIndex; |
| WASM_PARSER_FAIL_IF(!parseVarUInt32(tableIndex), "can't parse table index"); |
| ExpressionType result, index; |
| WASM_TRY_POP_EXPRESSION_STACK_INTO(index, "table.get"); |
| WASM_TRY_ADD_TO_CONTEXT(addTableGet(tableIndex, index, result)); |
| m_expressionStack.append(result); |
| return { }; |
| } |
| |
| case TableSet: { |
| WASM_PARSER_FAIL_IF(!Options::useWebAssemblyReferences(), "references are not enabled"); |
| unsigned tableIndex; |
| WASM_PARSER_FAIL_IF(!parseVarUInt32(tableIndex), "can't parse table index"); |
| ExpressionType val, index; |
| WASM_TRY_POP_EXPRESSION_STACK_INTO(val, "table.set"); |
| WASM_TRY_POP_EXPRESSION_STACK_INTO(index, "table.set"); |
| WASM_TRY_ADD_TO_CONTEXT(addTableSet(tableIndex, index, val)); |
| return { }; |
| } |
| |
| case ExtTable: { |
| WASM_PARSER_FAIL_IF(!Options::useWebAssemblyReferences(), "references are not enabled"); |
| uint8_t extOp; |
| WASM_PARSER_FAIL_IF(!parseUInt8(extOp), "can't parse table extended opcode"); |
| unsigned tableIndex; |
| WASM_PARSER_FAIL_IF(!parseVarUInt32(tableIndex), "can't parse table index"); |
| |
| switch (static_cast<ExtTableOpType>(extOp)) { |
| case ExtTableOpType::TableSize: { |
| ExpressionType result; |
| WASM_TRY_ADD_TO_CONTEXT(addTableSize(tableIndex, result)); |
| m_expressionStack.append(result); |
| break; |
| } |
| case ExtTableOpType::TableGrow: { |
| ExpressionType fill, delta, result; |
| WASM_TRY_POP_EXPRESSION_STACK_INTO(delta, "table.grow"); |
| WASM_TRY_POP_EXPRESSION_STACK_INTO(fill, "table.grow"); |
| WASM_TRY_ADD_TO_CONTEXT(addTableGrow(tableIndex, fill, delta, result)); |
| m_expressionStack.append(result); |
| break; |
| } |
| case ExtTableOpType::TableFill: { |
| ExpressionType offset, fill, count; |
| WASM_TRY_POP_EXPRESSION_STACK_INTO(count, "table.fill"); |
| WASM_TRY_POP_EXPRESSION_STACK_INTO(fill, "table.fill"); |
| WASM_TRY_POP_EXPRESSION_STACK_INTO(offset, "table.fill"); |
| WASM_TRY_ADD_TO_CONTEXT(addTableFill(tableIndex, offset, fill, count)); |
| break; |
| } |
| default: |
| WASM_PARSER_FAIL_IF(true, "invalid extended table op ", extOp); |
| break; |
| } |
| return { }; |
| } |
| |
| case RefNull: { |
| WASM_PARSER_FAIL_IF(!Options::useWebAssemblyReferences(), "references are not enabled"); |
| m_expressionStack.append(m_context.addConstant(Funcref, JSValue::encode(jsNull()))); |
| return { }; |
| } |
| |
| case RefIsNull: { |
| WASM_PARSER_FAIL_IF(!Options::useWebAssemblyReferences(), "references are not enabled"); |
| ExpressionType result, value; |
| WASM_TRY_POP_EXPRESSION_STACK_INTO(value, "ref.is_null"); |
| WASM_TRY_ADD_TO_CONTEXT(addRefIsNull(value, result)); |
| m_expressionStack.append(result); |
| return { }; |
| } |
| |
| case RefFunc: { |
| uint32_t index; |
| ExpressionType result; |
| WASM_PARSER_FAIL_IF(!Options::useWebAssemblyReferences(), "references are not enabled"); |
| WASM_PARSER_FAIL_IF(!parseVarUInt32(index), "can't get index for ref.func"); |
| |
| WASM_TRY_ADD_TO_CONTEXT(addRefFunc(index, result)); |
| m_expressionStack.append(result); |
| return { }; |
| } |
| |
| case GetLocal: { |
| uint32_t index; |
| ExpressionType result; |
| WASM_PARSER_FAIL_IF(!parseVarUInt32(index), "can't get index for get_local"); |
| WASM_TRY_ADD_TO_CONTEXT(getLocal(index, result)); |
| m_expressionStack.append(result); |
| return { }; |
| } |
| |
| case SetLocal: { |
| uint32_t index; |
| ExpressionType value; |
| WASM_PARSER_FAIL_IF(!parseVarUInt32(index), "can't get index for set_local"); |
| WASM_TRY_POP_EXPRESSION_STACK_INTO(value, "set_local"); |
| WASM_TRY_ADD_TO_CONTEXT(setLocal(index, value)); |
| return { }; |
| } |
| |
| case TeeLocal: { |
| uint32_t index; |
| WASM_PARSER_FAIL_IF(!parseVarUInt32(index), "can't get index for tee_local"); |
| WASM_PARSER_FAIL_IF(m_expressionStack.isEmpty(), "can't tee_local on empty expression stack"); |
| WASM_TRY_ADD_TO_CONTEXT(setLocal(index, m_expressionStack.last())); |
| return { }; |
| } |
| |
| case GetGlobal: { |
| uint32_t index; |
| ExpressionType result; |
| WASM_PARSER_FAIL_IF(!parseVarUInt32(index), "can't get get_global's index"); |
| WASM_TRY_ADD_TO_CONTEXT(getGlobal(index, result)); |
| m_expressionStack.append(result); |
| return { }; |
| } |
| |
| case SetGlobal: { |
| uint32_t index; |
| ExpressionType value; |
| WASM_PARSER_FAIL_IF(!parseVarUInt32(index), "can't get set_global's index"); |
| WASM_TRY_POP_EXPRESSION_STACK_INTO(value, "set_global value"); |
| WASM_TRY_ADD_TO_CONTEXT(setGlobal(index, value)); |
| return { }; |
| } |
| |
| case Call: { |
| uint32_t functionIndex; |
| WASM_PARSER_FAIL_IF(!parseVarUInt32(functionIndex), "can't parse call's function index"); |
| WASM_PARSER_FAIL_IF(functionIndex >= m_info.functionIndexSpaceSize(), "call function index ", functionIndex, " exceeds function index space ", m_info.functionIndexSpaceSize()); |
| |
| SignatureIndex calleeSignatureIndex = m_info.signatureIndexFromFunctionIndexSpace(functionIndex); |
| const Signature& calleeSignature = SignatureInformation::get(calleeSignatureIndex); |
| WASM_PARSER_FAIL_IF(calleeSignature.argumentCount() > m_expressionStack.size(), "call function index ", functionIndex, " has ", calleeSignature.argumentCount(), " arguments, but the expression stack currently holds ", m_expressionStack.size(), " values"); |
| |
| size_t firstArgumentIndex = m_expressionStack.size() - calleeSignature.argumentCount(); |
| Vector<ExpressionType> args; |
| WASM_PARSER_FAIL_IF(!args.tryReserveCapacity(calleeSignature.argumentCount()), "can't allocate enough memory for call's ", calleeSignature.argumentCount(), " arguments"); |
| for (size_t i = firstArgumentIndex; i < m_expressionStack.size(); ++i) |
| args.uncheckedAppend(m_expressionStack.at(i)); |
| m_expressionStack.shrink(firstArgumentIndex); |
| |
| ResultList results; |
| WASM_TRY_ADD_TO_CONTEXT(addCall(functionIndex, calleeSignature, args, results)); |
| |
| m_expressionStack.appendVector(results); |
| |
| return { }; |
| } |
| |
| case CallIndirect: { |
| uint32_t signatureIndex; |
| uint32_t tableIndex; |
| WASM_PARSER_FAIL_IF(!m_info.tableCount(), "call_indirect is only valid when a table is defined or imported"); |
| WASM_PARSER_FAIL_IF(!parseVarUInt32(signatureIndex), "can't get call_indirect's signature index"); |
| WASM_PARSER_FAIL_IF(!parseVarUInt32(tableIndex), "can't get call_indirect's table index"); |
| WASM_PARSER_FAIL_IF(tableIndex >= m_info.tableCount(), "call_indirect's table index ", tableIndex, " invalid, limit is ", m_info.tableCount()); |
| WASM_PARSER_FAIL_IF(m_info.usedSignatures.size() <= signatureIndex, "call_indirect's signature index ", signatureIndex, " exceeds known signatures ", m_info.usedSignatures.size()); |
| WASM_PARSER_FAIL_IF(m_info.tables[tableIndex].type() != TableElementType::Funcref, "call_indirect is only valid when a table has type funcref"); |
| |
| const Signature& calleeSignature = m_info.usedSignatures[signatureIndex].get(); |
| size_t argumentCount = calleeSignature.argumentCount() + 1; // Add the callee's index. |
| WASM_PARSER_FAIL_IF(argumentCount > m_expressionStack.size(), "call_indirect expects ", argumentCount, " arguments, but the expression stack currently holds ", m_expressionStack.size(), " values"); |
| |
| Vector<ExpressionType> args; |
| WASM_PARSER_FAIL_IF(!args.tryReserveCapacity(argumentCount), "can't allocate enough memory for ", argumentCount, " call_indirect arguments"); |
| size_t firstArgumentIndex = m_expressionStack.size() - argumentCount; |
| for (size_t i = firstArgumentIndex; i < m_expressionStack.size(); ++i) |
| args.uncheckedAppend(m_expressionStack.at(i)); |
| m_expressionStack.shrink(firstArgumentIndex); |
| |
| ResultList results; |
| WASM_TRY_ADD_TO_CONTEXT(addCallIndirect(tableIndex, calleeSignature, args, results)); |
| m_expressionStack.appendVector(results); |
| |
| return { }; |
| } |
| |
| case Block: { |
| BlockSignature inlineSignature; |
| WASM_PARSER_FAIL_IF(!parseBlockSignature(m_info, inlineSignature), "can't get block's signature"); |
| |
| int64_t oldSize = m_expressionStack.size(); |
| Stack newStack; |
| ControlType block; |
| WASM_TRY_ADD_TO_CONTEXT(addBlock(inlineSignature, m_expressionStack, block, newStack)); |
| ASSERT_UNUSED(oldSize, oldSize - m_expressionStack.size() == inlineSignature->argumentCount()); |
| ASSERT(newStack.size() == inlineSignature->argumentCount()); |
| |
| m_controlStack.append({ WTFMove(m_expressionStack), { }, WTFMove(block) }); |
| m_expressionStack = WTFMove(newStack); |
| return { }; |
| } |
| |
| case Loop: { |
| BlockSignature inlineSignature; |
| WASM_PARSER_FAIL_IF(!parseBlockSignature(m_info, inlineSignature), "can't get loop's signature"); |
| |
| int64_t oldSize = m_expressionStack.size(); |
| Stack newStack; |
| ControlType loop; |
| WASM_TRY_ADD_TO_CONTEXT(addLoop(inlineSignature, m_expressionStack, loop, newStack, m_loopIndex++)); |
| ASSERT_UNUSED(oldSize, oldSize - m_expressionStack.size() == inlineSignature->argumentCount()); |
| ASSERT(newStack.size() == inlineSignature->argumentCount()); |
| |
| m_controlStack.append({ WTFMove(m_expressionStack), { }, WTFMove(loop) }); |
| m_expressionStack = WTFMove(newStack); |
| return { }; |
| } |
| |
| case If: { |
| BlockSignature inlineSignature; |
| ExpressionType condition; |
| WASM_PARSER_FAIL_IF(!parseBlockSignature(m_info, inlineSignature), "can't get if's signature"); |
| WASM_TRY_POP_EXPRESSION_STACK_INTO(condition, "if condition"); |
| |
| int64_t oldSize = m_expressionStack.size(); |
| Stack newStack; |
| ControlType control; |
| WASM_TRY_ADD_TO_CONTEXT(addIf(condition, inlineSignature, m_expressionStack, control, newStack)); |
| ASSERT_UNUSED(oldSize, oldSize - m_expressionStack.size() == inlineSignature->argumentCount()); |
| ASSERT(newStack.size() == inlineSignature->argumentCount()); |
| |
| m_controlStack.append({ WTFMove(m_expressionStack), newStack, WTFMove(control) }); |
| m_expressionStack = WTFMove(newStack); |
| return { }; |
| } |
| |
| case Else: { |
| WASM_PARSER_FAIL_IF(m_controlStack.size() == 1, "can't use else block at the top-level of a function"); |
| WASM_TRY_ADD_TO_CONTEXT(addElse(m_controlStack.last().controlData, m_expressionStack)); |
| m_expressionStack = WTFMove(m_controlStack.last().elseBlockStack); |
| return { }; |
| } |
| |
| case Br: |
| case BrIf: { |
| uint32_t target; |
| ExpressionType condition = Context::emptyExpression(); |
| WASM_PARSER_FAIL_IF(!parseVarUInt32(target), "can't get br / br_if's target"); |
| WASM_PARSER_FAIL_IF(target >= m_controlStack.size(), "br / br_if's target ", target, " exceeds control stack size ", m_controlStack.size()); |
| if (m_currentOpcode == BrIf) |
| WASM_TRY_POP_EXPRESSION_STACK_INTO(condition, "br / br_if condition"); |
| else |
| m_unreachableBlocks = 1; |
| |
| ControlType& data = m_controlStack[m_controlStack.size() - 1 - target].controlData; |
| |
| WASM_TRY_ADD_TO_CONTEXT(addBranch(data, condition, m_expressionStack)); |
| return { }; |
| } |
| |
| case BrTable: { |
| uint32_t numberOfTargets; |
| uint32_t defaultTarget; |
| ExpressionType condition; |
| Vector<ControlType*> targets; |
| |
| WASM_PARSER_FAIL_IF(!parseVarUInt32(numberOfTargets), "can't get the number of targets for br_table"); |
| WASM_PARSER_FAIL_IF(numberOfTargets == std::numeric_limits<uint32_t>::max(), "br_table's number of targets is too big ", numberOfTargets); |
| |
| WASM_PARSER_FAIL_IF(!targets.tryReserveCapacity(numberOfTargets), "can't allocate memory for ", numberOfTargets, " br_table targets"); |
| for (uint32_t i = 0; i < numberOfTargets; ++i) { |
| uint32_t target; |
| WASM_PARSER_FAIL_IF(!parseVarUInt32(target), "can't get ", i, "th target for br_table"); |
| WASM_PARSER_FAIL_IF(target >= m_controlStack.size(), "br_table's ", i, "th target ", target, " exceeds control stack size ", m_controlStack.size()); |
| targets.uncheckedAppend(&m_controlStack[m_controlStack.size() - 1 - target].controlData); |
| } |
| |
| WASM_PARSER_FAIL_IF(!parseVarUInt32(defaultTarget), "can't get default target for br_table"); |
| WASM_PARSER_FAIL_IF(defaultTarget >= m_controlStack.size(), "br_table's default target ", defaultTarget, " exceeds control stack size ", m_controlStack.size()); |
| |
| WASM_TRY_POP_EXPRESSION_STACK_INTO(condition, "br_table condition"); |
| WASM_TRY_ADD_TO_CONTEXT(addSwitch(condition, targets, m_controlStack[m_controlStack.size() - 1 - defaultTarget].controlData, m_expressionStack)); |
| |
| m_unreachableBlocks = 1; |
| return { }; |
| } |
| |
| case Return: { |
| WASM_TRY_ADD_TO_CONTEXT(addReturn(m_controlStack[0].controlData, m_expressionStack)); |
| m_unreachableBlocks = 1; |
| return { }; |
| } |
| |
| case End: { |
| ControlEntry data = m_controlStack.takeLast(); |
| if (data.controlData.blockType() == BlockType::If) { |
| WASM_TRY_ADD_TO_CONTEXT(addElse(data.controlData, m_expressionStack)); |
| m_expressionStack = WTFMove(data.elseBlockStack); |
| } |
| // FIXME: This is a little weird in that it will modify the expressionStack for the result of the block. |
| // That's a little too effectful for me but I don't have a better API right now. |
| // see: https://bugs.webkit.org/show_bug.cgi?id=164353 |
| WASM_TRY_ADD_TO_CONTEXT(endBlock(data, m_expressionStack)); |
| m_expressionStack.swap(data.enclosedExpressionStack); |
| return { }; |
| } |
| |
| case Unreachable: { |
| WASM_TRY_ADD_TO_CONTEXT(addUnreachable()); |
| m_unreachableBlocks = 1; |
| return { }; |
| } |
| |
| case Drop: { |
| WASM_PARSER_FAIL_IF(!m_expressionStack.size(), "can't drop on empty stack"); |
| m_expressionStack.takeLast(); |
| return { }; |
| } |
| |
| case Nop: { |
| return { }; |
| } |
| |
| case GrowMemory: { |
| WASM_PARSER_FAIL_IF(!m_info.memory, "grow_memory is only valid if a memory is defined or imported"); |
| |
| uint8_t reserved; |
| WASM_PARSER_FAIL_IF(!parseVarUInt1(reserved), "can't parse reserved varUint1 for grow_memory"); |
| WASM_PARSER_FAIL_IF(reserved != 0, "reserved varUint1 for grow_memory must be zero"); |
| |
| ExpressionType delta; |
| WASM_TRY_POP_EXPRESSION_STACK_INTO(delta, "expect an i32 argument to grow_memory on the stack"); |
| |
| ExpressionType result; |
| WASM_TRY_ADD_TO_CONTEXT(addGrowMemory(delta, result)); |
| m_expressionStack.append(result); |
| |
| return { }; |
| } |
| |
| case CurrentMemory: { |
| WASM_PARSER_FAIL_IF(!m_info.memory, "current_memory is only valid if a memory is defined or imported"); |
| |
| uint8_t reserved; |
| WASM_PARSER_FAIL_IF(!parseVarUInt1(reserved), "can't parse reserved varUint1 for current_memory"); |
| WASM_PARSER_FAIL_IF(reserved != 0, "reserved varUint1 for current_memory must be zero"); |
| |
| ExpressionType result; |
| WASM_TRY_ADD_TO_CONTEXT(addCurrentMemory(result)); |
| m_expressionStack.append(result); |
| |
| return { }; |
| } |
| } |
| |
| ASSERT_NOT_REACHED(); |
| return { }; |
| } |
| |
| // FIXME: We should try to use the same decoder function for both unreachable and reachable code. https://bugs.webkit.org/show_bug.cgi?id=165965 |
| template<typename Context> |
| auto FunctionParser<Context>::parseUnreachableExpression() -> PartialResult |
| { |
| ASSERT(m_unreachableBlocks); |
| #define CREATE_CASE(name, id, b3op, inc) case OpType::name: |
| switch (m_currentOpcode) { |
| case Else: { |
| if (m_unreachableBlocks > 1) |
| return { }; |
| |
| ControlEntry& data = m_controlStack.last(); |
| m_unreachableBlocks = 0; |
| WASM_TRY_ADD_TO_CONTEXT(addElseToUnreachable(data.controlData)); |
| m_expressionStack = WTFMove(data.elseBlockStack); |
| return { }; |
| } |
| |
| case End: { |
| if (m_unreachableBlocks == 1) { |
| ControlEntry data = m_controlStack.takeLast(); |
| if (data.controlData.blockType() == BlockType::If) { |
| WASM_TRY_ADD_TO_CONTEXT(addElseToUnreachable(data.controlData)); |
| m_expressionStack = WTFMove(data.elseBlockStack); |
| WASM_TRY_ADD_TO_CONTEXT(endBlock(data, m_expressionStack)); |
| } else |
| WASM_TRY_ADD_TO_CONTEXT(addEndToUnreachable(data)); |
| |
| m_expressionStack.swap(data.enclosedExpressionStack); |
| } |
| m_unreachableBlocks--; |
| return { }; |
| } |
| |
| case Loop: |
| case If: |
| case Block: { |
| m_unreachableBlocks++; |
| BlockSignature unused; |
| WASM_PARSER_FAIL_IF(!parseBlockSignature(m_info, unused), "can't get inline type for ", m_currentOpcode, " in unreachable context"); |
| return { }; |
| } |
| |
| case BrTable: { |
| uint32_t numberOfTargets; |
| uint32_t unused; |
| WASM_PARSER_FAIL_IF(!parseVarUInt32(numberOfTargets), "can't get the number of targets for br_table in unreachable context"); |
| WASM_PARSER_FAIL_IF(numberOfTargets == std::numeric_limits<uint32_t>::max(), "br_table's number of targets is too big ", numberOfTargets); |
| |
| for (uint32_t i = 0; i < numberOfTargets; ++i) |
| WASM_PARSER_FAIL_IF(!parseVarUInt32(unused), "can't get ", i, "th target for br_table in unreachable context"); |
| |
| WASM_PARSER_FAIL_IF(!parseVarUInt32(unused), "can't get default target for br_table in unreachable context"); |
| return { }; |
| } |
| |
| case CallIndirect: { |
| uint32_t unused; |
| uint32_t unused2; |
| WASM_PARSER_FAIL_IF(!parseVarUInt32(unused), "can't get call_indirect's signature index in unreachable context"); |
| WASM_PARSER_FAIL_IF(!parseVarUInt32(unused2), "can't get call_indirect's reserved byte in unreachable context"); |
| return { }; |
| } |
| |
| case F32Const: { |
| uint32_t unused; |
| WASM_PARSER_FAIL_IF(!parseUInt32(unused), "can't parse 32-bit floating-point constant"); |
| return { }; |
| } |
| |
| case F64Const: { |
| uint64_t constant; |
| WASM_PARSER_FAIL_IF(!parseUInt64(constant), "can't parse 64-bit floating-point constant"); |
| return { }; |
| } |
| |
| // two immediate cases |
| FOR_EACH_WASM_MEMORY_LOAD_OP(CREATE_CASE) |
| FOR_EACH_WASM_MEMORY_STORE_OP(CREATE_CASE) { |
| uint32_t unused; |
| WASM_PARSER_FAIL_IF(!parseVarUInt32(unused), "can't get first immediate for ", m_currentOpcode, " in unreachable context"); |
| WASM_PARSER_FAIL_IF(!parseVarUInt32(unused), "can't get second immediate for ", m_currentOpcode, " in unreachable context"); |
| return { }; |
| } |
| |
| // one immediate cases |
| case SetLocal: |
| case GetLocal: |
| case TeeLocal: |
| case GetGlobal: |
| case SetGlobal: |
| case Br: |
| case BrIf: |
| case Call: { |
| uint32_t unused; |
| WASM_PARSER_FAIL_IF(!parseVarUInt32(unused), "can't get immediate for ", m_currentOpcode, " in unreachable context"); |
| return { }; |
| } |
| |
| case I32Const: { |
| int32_t unused; |
| WASM_PARSER_FAIL_IF(!parseVarInt32(unused), "can't get immediate for ", m_currentOpcode, " in unreachable context"); |
| return { }; |
| } |
| |
| case I64Const: { |
| int64_t unused; |
| WASM_PARSER_FAIL_IF(!parseVarInt64(unused), "can't get immediate for ", m_currentOpcode, " in unreachable context"); |
| return { }; |
| } |
| |
| case ExtTable: |
| case TableGet: |
| case TableSet: { |
| unsigned tableIndex; |
| WASM_PARSER_FAIL_IF(!parseVarUInt32(tableIndex), "can't parse table index"); |
| FALLTHROUGH; |
| } |
| case RefIsNull: |
| case RefNull: { |
| WASM_PARSER_FAIL_IF(!Options::useWebAssemblyReferences(), "references are not enabled"); |
| return { }; |
| } |
| |
| case RefFunc: { |
| uint32_t unused; |
| WASM_PARSER_FAIL_IF(!parseVarUInt32(unused), "can't get immediate for ", m_currentOpcode, " in unreachable context"); |
| WASM_PARSER_FAIL_IF(!Options::useWebAssemblyReferences(), "references are not enabled"); |
| return { }; |
| } |
| |
| case GrowMemory: |
| case CurrentMemory: { |
| uint8_t reserved; |
| WASM_PARSER_FAIL_IF(!parseVarUInt1(reserved), "can't parse reserved varUint1 for grow_memory/current_memory"); |
| return { }; |
| } |
| |
| // no immediate cases |
| FOR_EACH_WASM_BINARY_OP(CREATE_CASE) |
| FOR_EACH_WASM_UNARY_OP(CREATE_CASE) |
| case Unreachable: |
| case Nop: |
| case Return: |
| case Select: |
| case Drop: { |
| return { }; |
| } |
| } |
| #undef CREATE_CASE |
| RELEASE_ASSERT_NOT_REACHED(); |
| } |
| |
| } } // namespace JSC::Wasm |
| |
| #endif // ENABLE(WEBASSEMBLY) |