| /* |
| * Copyright 2015 WebAssembly Community Group participants |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| // |
| // Parses WebAssembly code in S-Expression format, as in .wast files |
| // such as are in the spec test suite. |
| // |
| |
| #ifndef wasm_wasm_s_parser_h |
| #define wasm_wasm_s_parser_h |
| |
| #include "mixed_arena.h" |
| #include "parsing.h" // for UniqueNameMapper. TODO: move dependency to cpp file? |
| #include "wasm-builder.h" |
| #include "wasm.h" |
| |
| namespace wasm { |
| |
| class SourceLocation { |
| public: |
| cashew::IString filename; |
| uint32_t line; |
| uint32_t column; |
| SourceLocation(cashew::IString filename_, |
| uint32_t line_, |
| uint32_t column_ = 0) |
| : filename(filename_), line(line_), column(column_) {} |
| }; |
| |
| // |
| // An element in an S-Expression: a list or a string |
| // |
| class Element { |
| typedef ArenaVector<Element*> List; |
| |
| bool isList_ = true; |
| List list_; |
| cashew::IString str_; |
| bool dollared_; |
| bool quoted_; |
| |
| public: |
| Element(MixedArena& allocator) : list_(allocator) {} |
| |
| bool isList() const { return isList_; } |
| bool isStr() const { return !isList_; } |
| bool dollared() const { return isStr() && dollared_; } |
| bool quoted() const { return isStr() && quoted_; } |
| |
| size_t line = -1; |
| size_t col = -1; |
| // original locations at the start/end of the S-Expression list |
| SourceLocation* startLoc = nullptr; |
| SourceLocation* endLoc = nullptr; |
| |
| // list methods |
| List& list(); |
| Element* operator[](unsigned i); |
| size_t size() { return list().size(); } |
| List::Iterator begin() { return list().begin(); } |
| List::Iterator end() { return list().end(); } |
| |
| // string methods |
| cashew::IString str() const; |
| const char* c_str() const; |
| Element* setString(cashew::IString str__, bool dollared__, bool quoted__); |
| Element* setMetadata(size_t line_, size_t col_, SourceLocation* startLoc_); |
| |
| // comparisons |
| bool operator==(Name name) { return isStr() && str() == name; } |
| |
| template<typename T> bool operator!=(T t) { return !(*this == t); } |
| |
| // printing |
| friend std::ostream& operator<<(std::ostream& o, Element& e); |
| void dump(); |
| }; |
| |
| // |
| // Generic S-Expression parsing into lists |
| // |
| class SExpressionParser { |
| char* input; |
| size_t line; |
| char* lineStart; |
| SourceLocation* loc = nullptr; |
| |
| MixedArena allocator; |
| |
| public: |
| // Assumes control of and modifies the input. |
| SExpressionParser(char* input); |
| Element* root; |
| |
| private: |
| Element* parse(); |
| void skipWhitespace(); |
| void parseDebugLocation(); |
| Element* parseString(); |
| }; |
| |
| // |
| // SExpressions => WebAssembly module |
| // |
| class SExpressionWasmBuilder { |
| Module& wasm; |
| MixedArena& allocator; |
| IRProfile profile; |
| |
| // The main list of types declared in the module |
| std::vector<HeapType> types; |
| std::unordered_map<std::string, size_t> typeIndices; |
| |
| std::vector<Name> functionNames; |
| std::vector<Name> tableNames; |
| std::vector<Name> memoryNames; |
| std::vector<Name> globalNames; |
| std::vector<Name> tagNames; |
| int functionCounter = 0; |
| int globalCounter = 0; |
| int tagCounter = 0; |
| int tableCounter = 0; |
| int elemCounter = 0; |
| int memoryCounter = 0; |
| int dataCounter = 0; |
| // we need to know function return types before we parse their contents |
| std::map<Name, HeapType> functionTypes; |
| std::unordered_map<cashew::IString, Index> debugInfoFileIndices; |
| |
| // Maps type indexes to a mapping of field index => name. This is not the same |
| // as the field names stored on the wasm object, as that maps types after |
| // their canonicalization. Canonicalization loses information, which means |
| // that structurally identical types cannot have different names. However, |
| // while parsing the text format we keep this mapping of type indexes to names |
| // which does allow reading such content. |
| std::unordered_map<size_t, std::unordered_map<Index, Name>> fieldNames; |
| |
| public: |
| // Assumes control of and modifies the input. |
| SExpressionWasmBuilder(Module& wasm, Element& module, IRProfile profile); |
| |
| private: |
| void preParseHeapTypes(Element& module); |
| // pre-parse types and function definitions, so we know function return types |
| // before parsing their contents |
| void preParseFunctionType(Element& s); |
| bool isImport(Element& curr); |
| void preParseImports(Element& curr); |
| void preParseMemory(Element& curr); |
| void parseModuleElement(Element& curr); |
| |
| // function parsing state |
| std::unique_ptr<Function> currFunction; |
| bool brokeToAutoBlock; |
| |
| UniqueNameMapper nameMapper; |
| |
| Name getFunctionName(Element& s); |
| Name getTableName(Element& s); |
| Name getMemoryName(Element& s); |
| Name getGlobalName(Element& s); |
| Name getTagName(Element& s); |
| void parseStart(Element& s) { wasm.addStart(getFunctionName(*s[1])); } |
| |
| Name getMemoryNameAtIdx(Index i); |
| bool isMemory64(Name memoryName); |
| bool hasMemoryIdx(Element& s, Index defaultSize, Index i); |
| |
| // returns the next index in s |
| size_t parseFunctionNames(Element& s, Name& name, Name& exportName); |
| void parseFunction(Element& s, bool preParseImport = false); |
| |
| Type stringToType(cashew::IString str, |
| bool allowError = false, |
| bool prefix = false) { |
| return stringToType(str.str, allowError, prefix); |
| } |
| Type |
| stringToType(const char* str, bool allowError = false, bool prefix = false); |
| HeapType stringToHeapType(cashew::IString str, bool prefix = false) { |
| return stringToHeapType(str.str, prefix); |
| } |
| HeapType stringToHeapType(const char* str, bool prefix = false); |
| Type elementToType(Element& s); |
| Type stringToLaneType(const char* str); |
| bool isType(cashew::IString str) { |
| return stringToType(str, true) != Type::none; |
| } |
| HeapType getFunctionType(Name name, Element& s); |
| |
| public: |
| Expression* parseExpression(Element* s) { return parseExpression(*s); } |
| Expression* parseExpression(Element& s); |
| |
| Module& getModule() { return wasm; } |
| |
| private: |
| Expression* makeExpression(Element& s); |
| Expression* makeUnreachable(); |
| Expression* makeNop(); |
| Expression* makeBinary(Element& s, BinaryOp op); |
| Expression* makeUnary(Element& s, UnaryOp op); |
| Expression* makeSelect(Element& s); |
| Expression* makeDrop(Element& s); |
| Expression* makeMemorySize(Element& s); |
| Expression* makeMemoryGrow(Element& s); |
| Index getLocalIndex(Element& s); |
| Expression* makeLocalGet(Element& s); |
| Expression* makeLocalTee(Element& s); |
| Expression* makeLocalSet(Element& s); |
| Expression* makeGlobalGet(Element& s); |
| Expression* makeGlobalSet(Element& s); |
| Expression* makeBlock(Element& s); |
| Expression* makeThenOrElse(Element& s); |
| Expression* makeConst(Element& s, Type type); |
| Expression* makeLoad(Element& s, Type type, bool isAtomic); |
| Expression* makeStore(Element& s, Type type, bool isAtomic); |
| Expression* makeAtomicRMWOrCmpxchg(Element& s, Type type); |
| Expression* |
| makeAtomicRMW(Element& s, Type type, uint8_t bytes, const char* extra); |
| Expression* |
| makeAtomicCmpxchg(Element& s, Type type, uint8_t bytes, const char* extra); |
| Expression* makeAtomicWait(Element& s, Type type); |
| Expression* makeAtomicNotify(Element& s); |
| Expression* makeAtomicFence(Element& s); |
| Expression* makeSIMDExtract(Element& s, SIMDExtractOp op, size_t lanes); |
| Expression* makeSIMDReplace(Element& s, SIMDReplaceOp op, size_t lanes); |
| Expression* makeSIMDShuffle(Element& s); |
| Expression* makeSIMDTernary(Element& s, SIMDTernaryOp op); |
| Expression* makeSIMDShift(Element& s, SIMDShiftOp op); |
| Expression* makeSIMDLoad(Element& s, SIMDLoadOp op); |
| Expression* makeSIMDLoadStoreLane(Element& s, SIMDLoadStoreLaneOp op); |
| Expression* makeMemoryInit(Element& s); |
| Expression* makeDataDrop(Element& s); |
| Expression* makeMemoryCopy(Element& s); |
| Expression* makeMemoryFill(Element& s); |
| Expression* makePush(Element& s); |
| Expression* makePop(Element& s); |
| Expression* makeIf(Element& s); |
| Expression* makeMaybeBlock(Element& s, size_t i, Type type); |
| Expression* makeLoop(Element& s); |
| Expression* makeCall(Element& s, bool isReturn); |
| Expression* makeCallIndirect(Element& s, bool isReturn); |
| template<class T> void parseOperands(Element& s, Index i, Index j, T& list) { |
| while (i < j) { |
| list.push_back(parseExpression(s[i])); |
| i++; |
| } |
| } |
| template<class T> |
| void parseCallOperands(Element& s, Index i, Index j, T* call) { |
| parseOperands(s, i, j, call->operands); |
| } |
| enum class LabelType { Break, Exception }; |
| Name getLabel(Element& s, LabelType labelType = LabelType::Break); |
| Expression* makeBreak(Element& s); |
| Expression* makeBreakTable(Element& s); |
| Expression* makeReturn(Element& s); |
| Expression* makeRefNull(Element& s); |
| Expression* makeRefIs(Element& s, RefIsOp op); |
| Expression* makeRefFunc(Element& s); |
| Expression* makeRefEq(Element& s); |
| Expression* makeTableGet(Element& s); |
| Expression* makeTableSet(Element& s); |
| Expression* makeTableSize(Element& s); |
| Expression* makeTableGrow(Element& s); |
| Expression* makeTry(Element& s); |
| Expression* makeTryOrCatchBody(Element& s, Type type, bool isTry); |
| Expression* makeThrow(Element& s); |
| Expression* makeRethrow(Element& s); |
| Expression* makeTupleMake(Element& s); |
| Expression* makeTupleExtract(Element& s); |
| Expression* makeCallRef(Element& s, bool isReturn); |
| Expression* makeI31New(Element& s); |
| Expression* makeI31Get(Element& s, bool signed_); |
| Expression* makeRefTestStatic(Element& s); |
| Expression* makeRefCastStatic(Element& s); |
| Expression* makeRefCastNopStatic(Element& s); |
| Expression* makeBrOn(Element& s, BrOnOp op); |
| Expression* makeBrOnStatic(Element& s, BrOnOp op); |
| Expression* makeStructNewStatic(Element& s, bool default_); |
| Index getStructIndex(Element& type, Element& field); |
| Expression* makeStructGet(Element& s, bool signed_ = false); |
| Expression* makeStructSet(Element& s); |
| Expression* makeArrayNewStatic(Element& s, bool default_); |
| Expression* makeArrayInitStatic(Element& s); |
| Expression* makeArrayGet(Element& s, bool signed_ = false); |
| Expression* makeArraySet(Element& s); |
| Expression* makeArrayLen(Element& s); |
| Expression* makeArrayCopy(Element& s); |
| Expression* makeRefAs(Element& s, RefAsOp op); |
| Expression* makeStringNew(Element& s, StringNewOp op); |
| Expression* makeStringConst(Element& s); |
| Expression* makeStringMeasure(Element& s, StringMeasureOp op); |
| Expression* makeStringEncode(Element& s, StringEncodeOp op); |
| Expression* makeStringConcat(Element& s); |
| Expression* makeStringEq(Element& s); |
| Expression* makeStringAs(Element& s, StringAsOp op); |
| Expression* makeStringWTF8Advance(Element& s); |
| Expression* makeStringWTF16Get(Element& s); |
| Expression* makeStringIterNext(Element& s); |
| Expression* makeStringIterMove(Element& s, StringIterMoveOp op); |
| Expression* makeStringSliceWTF(Element& s, StringSliceWTFOp op); |
| Expression* makeStringSliceIter(Element& s); |
| |
| // Helper functions |
| Type parseOptionalResultType(Element& s, Index& i); |
| Index parseMemoryLimits(Element& s, Index i, std::unique_ptr<Memory>& memory); |
| Index parseMemoryIndex(Element& s, Index i, std::unique_ptr<Memory>& memory); |
| Index parseMemoryForInstruction(const std::string& instrName, |
| Memory& memory, |
| Element& s, |
| Index i); |
| std::vector<Type> parseParamOrLocal(Element& s); |
| std::vector<NameType> parseParamOrLocal(Element& s, size_t& localIndex); |
| std::vector<Type> parseResults(Element& s); |
| HeapType parseTypeRef(Element& s); |
| size_t parseTypeUse(Element& s, |
| size_t startPos, |
| HeapType& functionType, |
| std::vector<NameType>& namedParams); |
| size_t parseTypeUse(Element& s, size_t startPos, HeapType& functionType); |
| |
| void stringToBinary(const char* input, size_t size, std::vector<char>& data); |
| void parseMemory(Element& s, bool preParseImport = false); |
| void parseData(Element& s); |
| void parseInnerData(Element& s, Index i, std::unique_ptr<DataSegment>& seg); |
| void parseExport(Element& s); |
| void parseImport(Element& s); |
| void parseGlobal(Element& s, bool preParseImport = false); |
| void parseTable(Element& s, bool preParseImport = false); |
| void parseElem(Element& s, Table* table = nullptr); |
| ElementSegment* parseElemFinish(Element& s, |
| std::unique_ptr<ElementSegment>& segment, |
| Index i = 1, |
| bool usesExpressions = false); |
| |
| // Parses something like (func ..), (array ..), (struct) |
| HeapType parseHeapType(Element& s); |
| |
| void parseTag(Element& s, bool preParseImport = false); |
| |
| Function::DebugLocation getDebugLocation(const SourceLocation& loc); |
| |
| // Struct/Array instructions have an unnecessary heap type that is just for |
| // validation (except for the case of unreachability, but that's not a problem |
| // anyhow, we can ignore it there). That is, we also have a reference typed |
| // child from which we can infer the type anyhow, and we just need to check |
| // that type is the same. |
| void |
| validateHeapTypeUsingChild(Expression* child, HeapType heapType, Element& s); |
| }; |
| |
| } // namespace wasm |
| |
| #endif // wasm_wasm_s_parser_h |