diff options
Diffstat (limited to 'src/wasm/wasm-s-parser.cpp')
-rw-r--r-- | src/wasm/wasm-s-parser.cpp | 4124 |
1 files changed, 0 insertions, 4124 deletions
diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp deleted file mode 100644 index f9eae14b8..000000000 --- a/src/wasm/wasm-s-parser.cpp +++ /dev/null @@ -1,4124 +0,0 @@ -/* - * 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. - */ - -#include "wasm-s-parser.h" - -#include <cctype> -#include <cmath> -#include <limits> - -#include "ir/branch-utils.h" -#include "ir/table-utils.h" -#include "shared-constants.h" -#include "support/string.h" -#include "wasm-binary.h" -#include "wasm-builder.h" - -using namespace std::string_literals; - -#define abort_on(str) \ - { throw ParseException(std::string("abort_on ") + str); } -#define element_assert(condition) \ - assert((condition) ? true : (std::cerr << "on: " << *this << '\n' && 0)); - -namespace { -int unhex(char c) { - if (c >= '0' && c <= '9') { - return c - '0'; - } - if (c >= 'a' && c <= 'f') { - return c - 'a' + 10; - } - if (c >= 'A' && c <= 'F') { - return c - 'A' + 10; - } - throw wasm::ParseException("invalid hexadecimal"); -} -} // namespace - -namespace wasm { - -// Similar to ParseException but built from an Element. -struct SParseException : public ParseException { - // Receive an element and report its contents, line and column. - SParseException(std::string text, const Element& s) - : ParseException(text + ": " + s.forceString(), s.line, s.col) {} - - // Receive a parent and child element. We print out the full parent for - // context, but report the child line and column inside it. - SParseException(std::string text, const Element& parent, const Element& child) - : ParseException( - text + ": " + parent.forceString(), child.line, child.col) {} -}; - -static Name STRUCT("struct"), FIELD("field"), ARRAY("array"), REC("rec"), - I8("i8"), I16("i16"), DECLARE("declare"), ITEM("item"), OFFSET("offset"), - SUB("sub"), FINAL("final"); - -static Address getAddress(const Element* s) { - return std::stoll(s->toString()); -} - -static void -checkAddress(Address a, const char* errorText, const Element* errorElem) { - if (a > std::numeric_limits<Address::address32_t>::max()) { - throw SParseException(errorText, *errorElem); - } -} - -static bool elementStartsWith(Element& s, IString str) { - return s.isList() && s.size() > 0 && s[0]->isStr() && s[0]->str() == str; -} - -static bool elementStartsWith(Element* s, IString str) { - return elementStartsWith(*s, str); -} - -Element::List& Element::list() { - if (!isList()) { - throw SParseException("expected list", *this); - } - return list_; -} - -Element* Element::operator[](unsigned i) { - if (!isList()) { - throw SParseException("expected list", *this); - } - if (i >= list().size()) { - throw SParseException("expected more elements in list", *this); - } - return list()[i]; -} - -IString Element::str() const { - if (!isStr()) { - throw SParseException("expected string", *this); - } - return str_; -} - -std::string Element::toString() const { - if (!isStr()) { - throw SParseException("expected string", *this); - } - return str_.toString(); -} - -std::string Element::forceString() const { - std::stringstream ss; - ss << *this; - // Limit the size to something reasonable for printing out. - return ss.str().substr(0, 80); -} - -Element* Element::setString(IString str__, bool dollared__, bool quoted__) { - isList_ = false; - str_ = str__; - dollared_ = dollared__; - quoted_ = quoted__; - return this; -} - -Element* -Element::setMetadata(size_t line_, size_t col_, SourceLocation* startLoc_) { - line = line_; - col = col_; - startLoc = startLoc_; - return this; -} - -std::ostream& operator<<(std::ostream& o, const Element& e) { - if (e.isList_) { - o << '('; - for (auto item : e.list_) { - o << ' ' << *item; - } - o << " )"; - } else { - if (e.dollared()) { - o << '$'; - } - o << e.str_.str; - } - return o; -} - -void Element::dump() { - std::cout << "dumping " << this << " : " << *this << ".\n"; -} - -SExpressionParser::SExpressionParser(char const* input) : input(input) { - root = nullptr; - line = 1; - lineStart = input; - while (!root) { // keep parsing until we pass an initial comment - root = parse(); - } -} - -Element* SExpressionParser::parse() { - std::vector<Element*> stack; - std::vector<SourceLocation*> stackLocs; - Element* curr = allocator.alloc<Element>(); - while (1) { - skipWhitespace(); - if (input[0] == 0) { - break; - } - if (input[0] == '(') { - input++; - stack.push_back(curr); - curr = allocator.alloc<Element>()->setMetadata( - line, input - lineStart - 1, loc); - stackLocs.push_back(loc); - assert(stack.size() == stackLocs.size()); - } else if (input[0] == ')') { - input++; - curr->endLoc = loc; - auto last = curr; - if (stack.empty()) { - throw ParseException("s-expr stack empty"); - } - curr = stack.back(); - assert(stack.size() == stackLocs.size()); - stack.pop_back(); - loc = stackLocs.back(); - stackLocs.pop_back(); - curr->list().push_back(last); - } else { - curr->list().push_back(parseString()); - } - } - if (stack.size() != 0) { - throw SParseException("stack is not empty", *curr); - } - return curr; -} - -void SExpressionParser::parseDebugLocation() { - // Extracting debug location (if valid) - char const* debugLoc = input + 3; // skipping ";;@" - while (debugLoc[0] && debugLoc[0] == ' ') { - debugLoc++; - } - char const* debugLocEnd = debugLoc; - while (debugLocEnd[0] && debugLocEnd[0] != '\n') { - debugLocEnd++; - } - if (debugLocEnd == debugLoc) { - loc = nullptr; - return; - } - char const* pos = debugLoc; - while (pos < debugLocEnd && pos[0] != ':') { - pos++; - } - if (pos >= debugLocEnd) { - return; // no line number - } - std::string name(debugLoc, pos); - char const* lineStart = ++pos; - while (pos < debugLocEnd && pos[0] != ':') { - pos++; - } - std::string lineStr(lineStart, pos); - if (pos >= debugLocEnd) { - return; // no column number - } - std::string colStr(++pos, debugLocEnd); - void* buf = - allocator.allocSpace(sizeof(SourceLocation), alignof(SourceLocation)); - loc = new (buf) SourceLocation( - IString(name.c_str(), false), atoi(lineStr.c_str()), atoi(colStr.c_str())); -} - -void SExpressionParser::skipWhitespace() { - while (1) { - while (isspace(input[0])) { - if (input[0] == '\n') { - line++; - lineStart = input + 1; - } - input++; - } - if (input[0] == ';' && input[1] == ';') { - if (input[2] == '@') { - parseDebugLocation(); - } - while (input[0] && input[0] != '\n') { - input++; - } - line++; - if (!input[0]) { - return; - } - lineStart = ++input; - } else if (input[0] == '(' && input[1] == ';') { - // Skip nested block comments. - input += 2; - int depth = 1; - while (1) { - if (!input[0]) { - return; - } - if (input[0] == '(' && input[1] == ';') { - input += 2; - depth++; - } else if (input[0] == ';' && input[1] == ')') { - input += 2; - --depth; - if (depth == 0) { - break; - } - } else if (input[0] == '\n') { - line++; - lineStart = input; - input++; - } else { - input++; - } - } - } else { - return; - } - } -} - -Element* SExpressionParser::parseString() { - bool dollared = false; - if (input[0] == '$') { - input++; - dollared = true; - } - char const* start = input; - if (input[0] == '"') { - // parse escaping \", but leave code escaped - we'll handle escaping in - // memory segments specifically - input++; - std::string str; - while (1) { - if (input[0] == 0) { - throw ParseException("unterminated string", line, start - lineStart); - } - if (input[0] == '"') { - break; - } - if (input[0] == '\\') { - str += input[0]; - if (input[1] == 0) { - throw ParseException( - "unterminated string escape", line, start - lineStart); - } - str += input[1]; - input += 2; - continue; - } - str += input[0]; - input++; - } - input++; - return allocator.alloc<Element>() - ->setString(IString(str.c_str(), false), dollared, true) - ->setMetadata(line, start - lineStart, loc); - } - while (input[0] && !isspace(input[0]) && input[0] != ')' && input[0] != '(' && - input[0] != ';') { - input++; - } - if (start == input) { - throw ParseException("expected string", line, input - lineStart); - } - - std::string temp; - temp.assign(start, input - start); - - auto ret = allocator.alloc<Element>() - ->setString(IString(temp.c_str(), false), dollared, false) - ->setMetadata(line, start - lineStart, loc); - - return ret; -} - -SExpressionWasmBuilder::SExpressionWasmBuilder(Module& wasm, - Element& module, - IRProfile profile) - : wasm(wasm), allocator(wasm.allocator), profile(profile) { - if (module.size() == 0) { - throw ParseException("empty toplevel, expected module"); - } - if (module[0]->str() != MODULE) { - throw ParseException("toplevel does not start with module"); - } - if (module.size() == 1) { - return; - } - Index i = 1; - if (module[i]->dollared()) { - wasm.name = module[i]->str(); - if (module.size() == 2) { - return; - } - i++; - } - - // spec tests have a `binary` keyword after the optional module name. Skip it - Name BINARY("binary"); - if (module[i]->isStr() && module[i]->str() == BINARY && - !module[i]->quoted()) { - i++; - } - - if (i < module.size() && module[i]->isStr()) { - // these s-expressions contain a binary module, actually - std::vector<char> data; - for (; i < module.size(); ++i) { - stringToBinary(*module[i], module[i]->str().str, data); - } - // TODO: support applying features here - WasmBinaryReader binaryBuilder(wasm, FeatureSet::MVP, data); - binaryBuilder.read(); - return; - } - - preParseHeapTypes(module); - - Index implementedFunctions = 0; - functionCounter = 0; - for (unsigned j = i; j < module.size(); j++) { - auto& s = *module[j]; - preParseFunctionType(s); - preParseImports(s); - preParseMemory(s); - if (elementStartsWith(s, FUNC) && !isImport(s)) { - implementedFunctions++; - } - } - // we go through the functions again, now parsing them, and the counter begins - // from where imports ended - functionCounter -= implementedFunctions; - for (unsigned j = i; j < module.size(); j++) { - parseModuleElement(*module[j]); - } -} - -bool SExpressionWasmBuilder::isImport(Element& curr) { - for (Index i = 0; i < curr.size(); i++) { - auto& x = *curr[i]; - if (elementStartsWith(x, IMPORT)) { - return true; - } - } - return false; -} - -void SExpressionWasmBuilder::preParseImports(Element& curr) { - IString id = curr[0]->str(); - if (id == IMPORT) { - parseImport(curr); - } - if (isImport(curr)) { - if (id == FUNC) { - parseFunction(curr, true /* preParseImport */); - } else if (id == GLOBAL) { - parseGlobal(curr, true /* preParseImport */); - } else if (id == TABLE) { - parseTable(curr, true /* preParseImport */); - } else if (id == MEMORY) { - parseMemory(curr, true /* preParseImport */); - } else if (id == TAG) { - parseTag(curr, true /* preParseImport */); - } else { - throw SParseException("fancy import we don't support yet", curr); - } - } -} - -void SExpressionWasmBuilder::preParseMemory(Element& curr) { - IString id = curr[0]->str(); - if (id == MEMORY && !isImport(curr)) { - parseMemory(curr); - } -} - -void SExpressionWasmBuilder::parseModuleElement(Element& curr) { - if (isImport(curr)) { - return; // already done - } - IString id = curr[0]->str(); - if (id == MEMORY) { - return; // already done - } - if (id == START) { - return parseStart(curr); - } - if (id == FUNC) { - return parseFunction(curr); - } - if (id == DATA) { - return parseData(curr); - } - if (id == EXPORT) { - return parseExport(curr); - } - if (id == IMPORT) { - return; // already done - } - if (id == GLOBAL) { - return parseGlobal(curr); - } - if (id == TABLE) { - return parseTable(curr); - } - if (id == ELEM) { - return parseElem(curr); - } - if (id == TYPE) { - return; // already done - } - if (id == REC) { - return; // already done - } - if (id == TAG) { - return parseTag(curr); - } - std::cerr << "bad module element " << id.str << '\n'; - throw SParseException("unknown module element", curr); -} - -int SExpressionWasmBuilder::parseIndex(Element& s) { - try { - return std::stoi(s.toString()); - } catch (...) { - throw SParseException("expected integer", s); - } -} - -Name SExpressionWasmBuilder::getFunctionName(Element& s) { - if (s.dollared()) { - return s.str(); - } else { - // index - size_t offset = parseIndex(s); - if (offset >= functionNames.size()) { - throw SParseException("unknown function in getFunctionName", s); - } - return functionNames[offset]; - } -} - -Name SExpressionWasmBuilder::getTableName(Element& s) { - if (s.dollared()) { - return s.str(); - } else { - // index - size_t offset = parseIndex(s); - if (offset >= tableNames.size()) { - throw SParseException("unknown table in getTableName", s); - } - return tableNames[offset]; - } -} - -Name SExpressionWasmBuilder::getElemSegmentName(Element& s) { - if (s.dollared()) { - return s.str(); - } else { - // index - size_t offset = parseIndex(s); - if (offset >= elemSegmentNames.size()) { - throw SParseException("unknown elem segment", s); - } - return elemSegmentNames[offset]; - } -} - -bool SExpressionWasmBuilder::isMemory64(Name memoryName) { - auto* memory = wasm.getMemoryOrNull(memoryName); - if (!memory) { - throw ParseException("invalid memory name in isMemory64: "s + - memoryName.toString()); - } - return memory->is64(); -} - -Name SExpressionWasmBuilder::getMemoryNameAtIdx(Index i) { - if (i >= memoryNames.size()) { - throw ParseException("unknown memory in getMemoryName: "s + - std::to_string(i)); - } - return memoryNames[i]; -} - -Name SExpressionWasmBuilder::getMemoryName(Element& s) { - if (s.dollared()) { - return s.str(); - } else { - // index - size_t offset = parseIndex(s); - return getMemoryNameAtIdx(offset); - } -} - -Name SExpressionWasmBuilder::getDataSegmentName(Element& s) { - if (s.dollared()) { - return s.str(); - } else { - // index - size_t offset = parseIndex(s); - if (offset >= dataSegmentNames.size()) { - throw SParseException("unknown data segment", s); - } - return dataSegmentNames[offset]; - } -} - -Name SExpressionWasmBuilder::getGlobalName(Element& s) { - if (s.dollared()) { - return s.str(); - } else { - // index - size_t offset = parseIndex(s); - if (offset >= globalNames.size()) { - throw SParseException("unknown global in getGlobalName", s); - } - return globalNames[offset]; - } -} - -Name SExpressionWasmBuilder::getTagName(Element& s) { - if (s.dollared()) { - return s.str(); - } else { - // index - size_t offset = parseIndex(s); - if (offset >= tagNames.size()) { - throw SParseException("unknown tag in getTagName", s); - } - return tagNames[offset]; - } -} - -// Parse various forms of (param ...) or (local ...) element. This ignores all -// parameter or local names when specified. -std::vector<Type> SExpressionWasmBuilder::parseParamOrLocal(Element& s) { - size_t fakeIndex = 0; - std::vector<NameType> namedParams = parseParamOrLocal(s, fakeIndex); - std::vector<Type> params; - for (auto& p : namedParams) { - params.push_back(p.type); - } - return params; -} - -// Parses various forms of (param ...) or (local ...) element: -// (param $name type) (e.g. (param $a i32)) -// (param type+) (e.g. (param i32 f64)) -// (local $name type) (e.g. (local $a i32)) -// (local type+) (e.g. (local i32 f64)) -// If the name is unspecified, it will create one using localIndex. -std::vector<NameType> -SExpressionWasmBuilder::parseParamOrLocal(Element& s, size_t& localIndex) { - assert(elementStartsWith(s, PARAM) || elementStartsWith(s, LOCAL)); - std::vector<NameType> namedParams; - if (s.size() == 1) { // (param) or (local) - return namedParams; - } - - for (size_t i = 1; i < s.size(); i++) { - IString name; - if (s[i]->dollared()) { - if (i != 1) { - throw SParseException("invalid wasm type", *s[i]); - } - if (i + 1 >= s.size()) { - throw SParseException("invalid param entry", s); - } - name = s[i]->str(); - i++; - } else { - name = Name::fromInt(localIndex); - } - localIndex++; - Type type; - type = elementToType(*s[i]); - if (elementStartsWith(s, PARAM) && type.isTuple()) { - throw SParseException("params may not have tuple types", *s[i]); - } - namedParams.emplace_back(name, type); - } - return namedParams; -} - -// Parses (result type) element. (e.g. (result i32)) -std::vector<Type> SExpressionWasmBuilder::parseResults(Element& s) { - assert(elementStartsWith(s, RESULT)); - std::vector<Type> types; - for (size_t i = 1; i < s.size(); i++) { - types.push_back(elementToType(*s[i])); - } - return types; -} - -// Parses an element that references an entry in the type section. The element -// should be in the form of (type name) or (type index). -// (e.g. (type $a), (type 0)) -HeapType SExpressionWasmBuilder::parseTypeRef(Element& s) { - assert(elementStartsWith(s, TYPE)); - if (s.size() != 2) { - throw SParseException("invalid type reference", s); - } - auto heapType = parseHeapType(*s[1]); - if (!heapType.isSignature()) { - throw SParseException("expected signature type", s); - } - return heapType; -} - -// Parses typeuse, a reference to a type definition. It is in the form of either -// (type index) or (type name), possibly augmented by inlined (param) and -// (result) nodes. (type) node can be omitted as well. Outputs are returned by -// parameter references. -// typeuse ::= (type index|name)+ | -// (type index|name)+ (param ..)* (result ..)* | -// (param ..)* (result ..)* -size_t -SExpressionWasmBuilder::parseTypeUse(Element& s, - size_t startPos, - HeapType& functionType, - std::vector<NameType>& namedParams) { - std::vector<Type> params, results; - size_t i = startPos; - - bool typeExists = false, paramsOrResultsExist = false; - if (i < s.size() && elementStartsWith(*s[i], TYPE)) { - typeExists = true; - functionType = parseTypeRef(*s[i++]); - } - - size_t paramPos = i; - size_t localIndex = 0; - - while (i < s.size() && elementStartsWith(*s[i], PARAM)) { - paramsOrResultsExist = true; - auto newParams = parseParamOrLocal(*s[i++], localIndex); - namedParams.insert(namedParams.end(), newParams.begin(), newParams.end()); - for (auto p : newParams) { - params.push_back(p.type); - } - } - - while (i < s.size() && elementStartsWith(*s[i], RESULT)) { - paramsOrResultsExist = true; - auto newResults = parseResults(*s[i++]); - results.insert(results.end(), newResults.begin(), newResults.end()); - } - - auto inlineSig = Signature(Type(params), Type(results)); - - // If none of type/param/result exists, this is equivalent to a type that does - // not have parameters and returns nothing. - if (!typeExists && !paramsOrResultsExist) { - paramsOrResultsExist = true; - } - - if (!typeExists) { - functionType = inlineSig; - } else if (paramsOrResultsExist) { - // verify that (type) and (params)/(result) match - if (inlineSig != functionType.getSignature()) { - throw SParseException("type and param/result don't match", *s[paramPos]); - } - } - - // Add implicitly defined type to global list so it has an index - if (std::find(types.begin(), types.end(), functionType) == types.end()) { - types.push_back(functionType); - } - - // If only (type) is specified, populate `namedParams` - if (!paramsOrResultsExist) { - size_t index = 0; - assert(functionType.isSignature()); - Signature sig = functionType.getSignature(); - for (const auto& param : sig.params) { - namedParams.emplace_back(Name::fromInt(index++), param); - } - } - - return i; -} - -// Parses a typeuse. Use this when only FunctionType* is needed. -size_t SExpressionWasmBuilder::parseTypeUse(Element& s, - size_t startPos, - HeapType& functionType) { - std::vector<NameType> params; - return parseTypeUse(s, startPos, functionType, params); -} - -void SExpressionWasmBuilder::preParseHeapTypes(Element& module) { - // Iterate through each individual type definition, calling `f` with the - // definition and its recursion group number. - auto forEachType = [&](auto f) { - size_t groupNumber = 0; - for (auto* elemPtr : module) { - auto& elem = *elemPtr; - if (elementStartsWith(elem, TYPE)) { - f(elem, groupNumber++); - } else if (elementStartsWith(elem, REC)) { - for (auto* innerPtr : elem) { - auto& inner = *innerPtr; - if (elementStartsWith(inner, TYPE)) { - f(inner, groupNumber); - } - } - ++groupNumber; - } - } - }; - - // Map type names to indices - size_t numTypes = 0; - forEachType([&](Element& elem, size_t) { - if (elem[1]->dollared()) { - std::string name = elem[1]->toString(); - if (!typeIndices.insert({name, numTypes}).second) { - throw SParseException("duplicate function type", elem); - } - } - ++numTypes; - }); - - TypeBuilder builder(numTypes); - - // Create recursion groups - size_t currGroup = 0, groupStart = 0, groupLength = 0; - auto finishGroup = [&]() { - builder.createRecGroup(groupStart, groupLength); - groupStart = groupStart + groupLength; - groupLength = 0; - }; - forEachType([&](Element&, size_t group) { - if (group != currGroup) { - finishGroup(); - currGroup = group; - } - ++groupLength; - }); - finishGroup(); - - auto parseHeapType = [&](Element& elem) -> HeapType { - auto name = elem.toString(); - if (elem.dollared()) { - auto it = typeIndices.find(name); - if (it == typeIndices.end()) { - throw SParseException("invalid type name", elem); - } else { - return builder[it->second]; - } - } else if (String::isNumber(name)) { - size_t index = parseIndex(elem); - if (index >= numTypes) { - throw SParseException("invalid type index", elem); - } - return builder[index]; - } else { - return stringToHeapType(elem.str()); - } - }; - - auto parseRefType = [&](Element& elem) -> Type { - // '(' 'ref' 'null'? ht ')' - auto nullable = - elem[1]->isStr() && *elem[1] == NULL_ ? Nullable : NonNullable; - auto& referent = nullable ? *elem[2] : *elem[1]; - auto ht = parseHeapType(referent); - if (ht.isBasic()) { - return Type(ht, nullable); - } else { - return builder.getTempRefType(ht, nullable); - } - }; - - auto parseValType = [&](Element& elem) { - if (elem.isStr()) { - return stringToType(elem.str()); - } else if (*elem[0] == REF) { - return parseRefType(elem); - } else { - throw SParseException("unknown valtype kind", elem); - } - }; - - auto parseParams = [&](Element& elem) { - auto it = ++elem.begin(); - if (it != elem.end() && (*it)->dollared()) { - ++it; - } - std::vector<Type> params; - for (auto end = elem.end(); it != end; ++it) { - params.push_back(parseValType(**it)); - } - return params; - }; - - auto parseResults = [&](Element& elem) { - std::vector<Type> results; - for (auto it = ++elem.begin(); it != elem.end(); ++it) { - results.push_back(parseValType(**it)); - } - return results; - }; - - auto parseSignatureDef = [&](Element& elem, bool nominal) { - // '(' 'func' vec(param) vec(result) ')' - // param ::= '(' 'param' id? valtype ')' - // result ::= '(' 'result' valtype ')' - std::vector<Type> params, results; - auto end = elem.end() - (nominal ? 1 : 0); - for (auto it = ++elem.begin(); it != end; ++it) { - Element& curr = **it; - if (elementStartsWith(curr, PARAM)) { - auto newParams = parseParams(curr); - params.insert(params.end(), newParams.begin(), newParams.end()); - } else if (elementStartsWith(curr, RESULT)) { - auto newResults = parseResults(curr); - results.insert(results.end(), newResults.begin(), newResults.end()); - } - } - return Signature(builder.getTempTupleType(params), - builder.getTempTupleType(results)); - }; - - auto parseContinuationDef = [&](Element& elem) { - // '(' 'cont' index ')' | '(' 'cont' name ')' - HeapType ft = parseHeapType(*elem[1]); - if (!ft.isSignature()) { - throw ParseException( - "cont type must be created from func type", elem.line, elem.col); - } - return Continuation(ft); - }; - - // Parses a field, and notes the name if one is found. - auto parseField = [&](Element* elem, Name& name) { - Mutability mutable_ = Immutable; - // elem is a list, containing either - // TYPE - // or - // (field TYPE) - // or - // (field $name TYPE) - if (elementStartsWith(elem, FIELD)) { - if (elem->size() == 3) { - name = (*elem)[1]->str(); - } - elem = (*elem)[elem->size() - 1]; - } - // The element may also be (mut (..)). - if (elementStartsWith(elem, MUT)) { - mutable_ = Mutable; - elem = (*elem)[1]; - } - if (elem->isStr()) { - // elem is a simple string name like "i32". It can be a normal wasm - // type, or one of the special types only available in fields. - if (*elem == I8) { - return Field(Field::i8, mutable_); - } else if (*elem == I16) { - return Field(Field::i16, mutable_); - } - } - // Otherwise it's an arbitrary type. - return Field(parseValType(*elem), mutable_); - }; - - auto parseStructDef = [&](Element& elem, size_t typeIndex, bool nominal) { - FieldList fields; - Index end = elem.size() - (nominal ? 1 : 0); - for (Index i = 1; i < end; i++) { - Name name; - fields.emplace_back(parseField(elem[i], name)); - if (name.is()) { - // Only add the name to the map if it exists. - fieldNames[typeIndex][i - 1] = name; - } - } - return Struct(fields); - }; - - auto parseArrayDef = [&](Element& elem) { - Name unused; - return Array(parseField(elem[1], unused)); - }; - - size_t index = 0; - forEachType([&](Element& elem, size_t) { - Element& def = elem[1]->dollared() ? *elem[2] : *elem[1]; - Element& kind = *def[0]; - Element* super = nullptr; - if (kind == SUB) { - Index i = 1; - if (*def[i] == FINAL) { - ++i; - } else { - builder[index].setOpen(); - } - if (def[i]->dollared()) { - super = def[i]; - ++i; - } - Element& subtype = *def[i++]; - if (i != def.size()) { - throw SParseException("invalid 'sub' form", kind); - } - if (!subtype.isList() || subtype.size() < 1) { - throw SParseException("invalid subtype definition", subtype); - } - Element& subtypeKind = *subtype[0]; - if (subtypeKind == FUNC) { - builder[index] = parseSignatureDef(subtype, 0); - } else if (kind == CONT) { - builder[index] = parseContinuationDef(subtype); - } else if (subtypeKind == STRUCT) { - builder[index] = parseStructDef(subtype, index, 0); - } else if (subtypeKind == ARRAY) { - builder[index] = parseArrayDef(subtype); - } else { - throw SParseException("unknown subtype kind", subtypeKind); - } - } else { - if (kind == FUNC) { - builder[index] = parseSignatureDef(def, 0); - } else if (kind == CONT) { - builder[index] = parseContinuationDef(def); - } else if (kind == STRUCT) { - builder[index] = parseStructDef(def, index, 0); - } else if (kind == ARRAY) { - builder[index] = parseArrayDef(def); - } else { - throw SParseException("unknown heaptype kind", kind); - } - } - if (super) { - auto it = typeIndices.find(super->toString()); - if (!super->dollared() || it == typeIndices.end()) { - throw SParseException("unknown supertype", elem, *super); - } - builder[index].subTypeOf(builder[it->second]); - } - ++index; - }); - - auto result = builder.build(); - if (auto* err = result.getError()) { - // Find the name to provide a better error message. - std::stringstream msg; - msg << "Invalid type: " << err->reason; - for (auto& [name, index] : typeIndices) { - if (index == err->index) { - Fatal() << msg.str() << " at type $" << name; - } - } - // No name, just report the index. - Fatal() << msg.str() << " at index " << err->index; - } - types = *result; - - for (auto& [name, index] : typeIndices) { - auto type = types[index]; - // A type may appear in the type section more than once, but we canonicalize - // types internally, so there will be a single name chosen for that type. Do - // so determistically. - if (wasm.typeNames.count(type) && wasm.typeNames[type].name.str < name) { - continue; - } - auto& currTypeNames = wasm.typeNames[type]; - currTypeNames.name = name; - if (type.isStruct()) { - currTypeNames.fieldNames = fieldNames[index]; - } - } -} - -void SExpressionWasmBuilder::preParseFunctionType(Element& s) { - IString id = s[0]->str(); - if (id != FUNC) { - return; - } - size_t i = 1; - Name name, exportName; - i = parseFunctionNames(s, name, exportName); - if (!name.is()) { - // unnamed, use an index - name = Name::fromInt(functionCounter); - } - functionNames.push_back(name); - functionCounter++; - parseTypeUse(s, i, functionTypes[name]); -} - -size_t SExpressionWasmBuilder::parseFunctionNames(Element& s, - Name& name, - Name& exportName) { - size_t i = 1; - while (i < s.size() && i < 3 && s[i]->isStr()) { - if (s[i]->dollared()) { - name = s[i]->str(); - i++; - } else { - break; - } - } - if (i < s.size() && s[i]->isList()) { - auto& inner = *s[i]; - if (elementStartsWith(inner, EXPORT)) { - exportName = inner[1]->str(); - i++; - } - } -#if 0 - if (exportName.is() && !name.is()) { - name = exportName; // useful for debugging - } -#endif - return i; -} - -void SExpressionWasmBuilder::parseFunction(Element& s, bool preParseImport) { - brokeToAutoBlock = false; - - Name name, exportName; - size_t i = parseFunctionNames(s, name, exportName); - bool hasExplicitName = name.is(); - if (!preParseImport) { - if (!name.is()) { - // unnamed, use an index - name = Name::fromInt(functionCounter); - } - functionCounter++; - } else { - // just preparsing, functionCounter was incremented by preParseFunctionType - if (!name.is()) { - // unnamed, use an index - name = functionNames[functionCounter - 1]; - } - } - if (exportName.is()) { - auto ex = std::make_unique<Export>(); - ex->name = exportName; - ex->value = name; - ex->kind = ExternalKind::Function; - if (wasm.getExportOrNull(ex->name)) { - throw SParseException("duplicate export", s); - } - wasm.addExport(ex.release()); - } - - // parse import - Name importModule, importBase; - if (i < s.size() && elementStartsWith(*s[i], IMPORT)) { - Element& curr = *s[i]; - importModule = curr[1]->str(); - importBase = curr[2]->str(); - i++; - } - - // parse typeuse: type/param/result - HeapType type; - std::vector<NameType> params; - i = parseTypeUse(s, i, type, params); - - // when (import) is inside a (func) element, this is not a function definition - // but an import. - if (importModule.is()) { - if (!importBase.size()) { - throw SParseException("module but no base for import", s); - } - if (!preParseImport) { - throw SParseException("!preParseImport in func", s); - } - auto im = std::make_unique<Function>(); - im->setName(name, hasExplicitName); - im->module = importModule; - im->base = importBase; - im->type = type; - functionTypes[name] = type; - if (wasm.getFunctionOrNull(im->name)) { - throw SParseException("duplicate import", s); - } - wasm.addFunction(std::move(im)); - if (currFunction) { - throw SParseException("import module inside function dec", s); - } - nameMapper.clear(); - return; - } - // at this point this not an import but a real function definition. - if (preParseImport) { - throw SParseException("preParseImport in func", s); - } - - size_t localIndex = params.size(); // local index for params and locals - - // parse locals - std::vector<NameType> vars; - while (i < s.size() && elementStartsWith(*s[i], LOCAL)) { - auto newVars = parseParamOrLocal(*s[i++], localIndex); - vars.insert(vars.end(), newVars.begin(), newVars.end()); - } - - // make a new function - currFunction = std::unique_ptr<Function>( - Builder(wasm).makeFunction(name, std::move(params), type, std::move(vars))); - currFunction->hasExplicitName = hasExplicitName; - currFunction->profile = profile; - - // parse body - Block* autoBlock = nullptr; // may need to add a block for the very top level - auto ensureAutoBlock = [&]() { - if (!autoBlock) { - autoBlock = allocator.alloc<Block>(); - autoBlock->list.push_back(currFunction->body); - currFunction->body = autoBlock; - } - }; - while (i < s.size()) { - Expression* ex = parseExpression(*s[i++]); - if (!currFunction->body) { - currFunction->body = ex; - } else { - ensureAutoBlock(); - autoBlock->list.push_back(ex); - } - } - - if (brokeToAutoBlock) { - ensureAutoBlock(); - autoBlock->name = FAKE_RETURN; - } - if (autoBlock) { - autoBlock->finalize(type.getSignature().results); - } - if (!currFunction->body) { - currFunction->body = allocator.alloc<Nop>(); - } - if (s.startLoc) { - currFunction->prologLocation.insert(getDebugLocation(*s.startLoc)); - } - if (s.endLoc) { - currFunction->epilogLocation.insert(getDebugLocation(*s.endLoc)); - } - if (wasm.getFunctionOrNull(currFunction->name)) { - throw SParseException("duplicate function", s); - } - wasm.addFunction(currFunction.release()); - nameMapper.clear(); -} - -Type SExpressionWasmBuilder::stringToType(std::string_view str, - bool allowError, - bool prefix) { - if (str.size() >= 3) { - if (str[0] == 'i') { - if (str[1] == '3' && str[2] == '2' && (prefix || str.size() == 3)) { - return Type::i32; - } - if (str[1] == '6' && str[2] == '4' && (prefix || str.size() == 3)) { - return Type::i64; - } - } - if (str[0] == 'f') { - if (str[1] == '3' && str[2] == '2' && (prefix || str.size() == 3)) { - return Type::f32; - } - if (str[1] == '6' && str[2] == '4' && (prefix || str.size() == 3)) { - return Type::f64; - } - } - } - if (str.size() >= 4) { - if (str[0] == 'v') { - if (str[1] == '1' && str[2] == '2' && str[3] == '8' && - (prefix || str.size() == 4)) { - return Type::v128; - } - } - } - if (str.substr(0, 7) == "funcref" && (prefix || str.size() == 7)) { - return Type(HeapType::func, Nullable); - } - if (str.substr(0, 7) == "contref" && (prefix || str.size() == 7)) { - return Type(HeapType::cont, Nullable); - } - if (str.substr(0, 9) == "externref" && (prefix || str.size() == 9)) { - return Type(HeapType::ext, Nullable); - } - if (str.substr(0, 6) == "anyref" && (prefix || str.size() == 6)) { - return Type(HeapType::any, Nullable); - } - if (str.substr(0, 5) == "eqref" && (prefix || str.size() == 5)) { - return Type(HeapType::eq, Nullable); - } - if (str.substr(0, 6) == "i31ref" && (prefix || str.size() == 6)) { - return Type(HeapType::i31, Nullable); - } - if (str.substr(0, 9) == "structref" && (prefix || str.size() == 9)) { - return Type(HeapType::struct_, Nullable); - } - if (str.substr(0, 8) == "arrayref" && (prefix || str.size() == 8)) { - return Type(HeapType::array, Nullable); - } - if (str.substr(0, 6) == "exnref" && (prefix || str.size() == 6)) { - return Type(HeapType::exn, Nullable); - } - if (str.substr(0, 9) == "stringref" && (prefix || str.size() == 9)) { - return Type(HeapType::string, Nullable); - } - if (str.substr(0, 7) == "nullref" && (prefix || str.size() == 7)) { - return Type(HeapType::none, Nullable); - } - if (str.substr(0, 13) == "nullexternref" && (prefix || str.size() == 13)) { - return Type(HeapType::noext, Nullable); - } - if (str.substr(0, 11) == "nullfuncref" && (prefix || str.size() == 11)) { - return Type(HeapType::nofunc, Nullable); - } - if (str.substr(0, 10) == "nullexnref" && (prefix || str.size() == 10)) { - return Type(HeapType::noexn, Nullable); - } - if (str.substr(0, 11) == "nullcontref" && (prefix || str.size() == 11)) { - return Type(HeapType::nocont, Nullable); - } - if (allowError) { - return Type::none; - } - throw ParseException(std::string("invalid wasm type: ") + - std::string(str.data(), str.size())); -} - -HeapType SExpressionWasmBuilder::stringToHeapType(std::string_view str, - bool prefix) { - if (str.substr(0, 4) == "func" && (prefix || str.size() == 4)) { - return HeapType::func; - } - if (str.substr(0, 4) == "cont" && (prefix || str.size() == 4)) { - return HeapType::cont; - } - if (str.substr(0, 2) == "eq" && (prefix || str.size() == 2)) { - return HeapType::eq; - } - if (str.substr(0, 6) == "extern" && (prefix || str.size() == 6)) { - return HeapType::ext; - } - if (str.substr(0, 3) == "any" && (prefix || str.size() == 3)) { - return HeapType::any; - } - if (str.substr(0, 3) == "i31" && (prefix || str.size() == 3)) { - return HeapType::i31; - } - if (str.substr(0, 6) == "struct" && (prefix || str.size() == 6)) { - return HeapType::struct_; - } - if (str.substr(0, 5) == "array" && (prefix || str.size() == 5)) { - return HeapType::array; - } - if (str.substr(0, 3) == "exn" && (prefix || str.size() == 3)) { - return HeapType::exn; - } - if (str.substr(0, 6) == "string" && (prefix || str.size() == 6)) { - return HeapType::string; - } - if (str.substr(0, 4) == "none" && (prefix || str.size() == 4)) { - return HeapType::none; - } - if (str.substr(0, 8) == "noextern" && (prefix || str.size() == 8)) { - return HeapType::noext; - } - if (str.substr(0, 6) == "nofunc" && (prefix || str.size() == 6)) { - return HeapType::nofunc; - } - if (str.substr(0, 6) == "nofunc" && (prefix || str.size() == 6)) { - return HeapType::nofunc; - } - if (str.substr(0, 5) == "noexn" && (prefix || str.size() == 5)) { - return HeapType::noexn; - } - if (str.substr(0, 6) == "nocont" && (prefix || str.size() == 6)) { - return HeapType::nocont; - } - throw ParseException(std::string("invalid wasm heap type: ") + - std::string(str.data(), str.size())); -} - -Type SExpressionWasmBuilder::elementToType(Element& s) { - if (s.isStr()) { - return stringToType(s.str()); - } - auto& list = s.list(); - auto size = list.size(); - if (elementStartsWith(s, REF)) { - // It's a reference. It should be in the form - // (ref $name) - // or - // (ref null $name) - // and also $name can be the expanded structure of the type and not a name, - // so something like (ref (func (result i32))), etc. - if (size != 2 && size != 3) { - throw SParseException(std::string("invalid reference type size"), s); - } - if (size == 3 && *list[1] != NULL_) { - throw SParseException(std::string("invalid reference type qualifier"), s); - } - Nullability nullable = NonNullable; - size_t i = 1; - if (size == 3) { - nullable = Nullable; - i++; - } - return Type(parseHeapType(*s[i]), nullable); - } - if (elementStartsWith(s, TUPLE)) { - // It's a tuple. - std::vector<Type> types; - for (size_t i = 1; i < s.size(); ++i) { - types.push_back(elementToType(*list[i])); - } - return Type(types); - } - throw SParseException(std::string("expected type, got list"), s); -} - -Type SExpressionWasmBuilder::stringToLaneType(const char* str) { - if (strcmp(str, "i8x16") == 0) { - return Type::i32; - } - if (strcmp(str, "i16x8") == 0) { - return Type::i32; - } - if (strcmp(str, "i32x4") == 0) { - return Type::i32; - } - if (strcmp(str, "i64x2") == 0) { - return Type::i64; - } - if (strcmp(str, "f32x4") == 0) { - return Type::f32; - } - if (strcmp(str, "f64x2") == 0) { - return Type::f64; - } - return Type::none; -} - -HeapType SExpressionWasmBuilder::getFunctionType(Name name, Element& s) { - auto iter = functionTypes.find(name); - if (iter == functionTypes.end()) { - throw SParseException("invalid call target: " + std::string(name.str), s); - } - return iter->second; -} - -Function::DebugLocation -SExpressionWasmBuilder::getDebugLocation(const SourceLocation& loc) { - IString file = loc.filename; - auto& debugInfoFileNames = wasm.debugInfoFileNames; - auto iter = debugInfoFileIndices.find(file); - if (iter == debugInfoFileIndices.end()) { - Index index = debugInfoFileNames.size(); - debugInfoFileNames.push_back(file.toString()); - debugInfoFileIndices[file] = index; - } - uint32_t fileIndex = debugInfoFileIndices[file]; - return {fileIndex, loc.line, loc.column}; -} - -Expression* SExpressionWasmBuilder::parseExpression(Element& s) { - Expression* result = makeExpression(s); - if (s.startLoc && currFunction) { - currFunction->debugLocations[result] = getDebugLocation(*s.startLoc); - } - return result; -} - -Expression* SExpressionWasmBuilder::makeExpression(Element& s){ -#define INSTRUCTION_PARSER -#include "gen-s-parser.inc" -} - -Expression* SExpressionWasmBuilder::makeUnreachable() { - return allocator.alloc<Unreachable>(); -} - -Expression* SExpressionWasmBuilder::makeNop() { return allocator.alloc<Nop>(); } - -Expression* SExpressionWasmBuilder::makeBinary(Element& s, BinaryOp op) { - auto ret = allocator.alloc<Binary>(); - ret->op = op; - ret->left = parseExpression(s[1]); - ret->right = parseExpression(s[2]); - ret->finalize(); - return ret; -} - -Expression* SExpressionWasmBuilder::makeUnary(Element& s, UnaryOp op) { - auto ret = allocator.alloc<Unary>(); - ret->op = op; - ret->value = parseExpression(s[1]); - ret->finalize(); - return ret; -} - -Expression* SExpressionWasmBuilder::makeSelect(Element& s) { - auto ret = allocator.alloc<Select>(); - Index i = 1; - Type type = parseBlockType(s, i); - ret->ifTrue = parseExpression(s[i++]); - ret->ifFalse = parseExpression(s[i++]); - ret->condition = parseExpression(s[i]); - if (type.isConcrete()) { - ret->finalize(type); - } else { - ret->finalize(); - } - return ret; -} - -Expression* SExpressionWasmBuilder::makeDrop(Element& s) { - auto ret = allocator.alloc<Drop>(); - ret->value = parseExpression(s[1]); - if (ret->value->type.isTuple()) { - throw SParseException("expected tuple.drop, found drop", s, *s[0]); - } - ret->finalize(); - return ret; -} - -Expression* SExpressionWasmBuilder::makeMemorySize(Element& s) { - auto ret = allocator.alloc<MemorySize>(); - Index i = 1; - Name memory; - if (s.size() > 1) { - memory = getMemoryName(*s[i++]); - } else { - memory = getMemoryNameAtIdx(0); - } - ret->memory = memory; - if (isMemory64(memory)) { - ret->type = Type::i64; - } - ret->finalize(); - return ret; -} - -Expression* SExpressionWasmBuilder::makeMemoryGrow(Element& s) { - auto ret = allocator.alloc<MemoryGrow>(); - Index i = 1; - Name memory; - if (s.size() > 2) { - memory = getMemoryName(*s[i++]); - } else { - memory = getMemoryNameAtIdx(0); - } - ret->memory = memory; - if (isMemory64(memory)) { - ret->type = Type::i64; - } - ret->delta = parseExpression(s[i]); - ret->finalize(); - return ret; -} - -Index SExpressionWasmBuilder::getLocalIndex(Element& s) { - if (!currFunction) { - throw SParseException("local access in non-function scope", s); - } - if (s.dollared()) { - auto ret = s.str(); - if (currFunction->localIndices.count(ret) == 0) { - throw SParseException("bad local name", s); - } - return currFunction->getLocalIndex(ret); - } - // this is a numeric index - Index ret = parseIndex(s); - if (ret >= currFunction->getNumLocals()) { - throw SParseException("bad local index", s); - } - return ret; -} - -Expression* SExpressionWasmBuilder::makeLocalGet(Element& s) { - auto ret = allocator.alloc<LocalGet>(); - ret->index = getLocalIndex(*s[1]); - ret->type = currFunction->getLocalType(ret->index); - return ret; -} - -Expression* SExpressionWasmBuilder::makeLocalTee(Element& s) { - auto ret = allocator.alloc<LocalSet>(); - ret->index = getLocalIndex(*s[1]); - ret->value = parseExpression(s[2]); - ret->makeTee(currFunction->getLocalType(ret->index)); - ret->finalize(); - return ret; -} - -Expression* SExpressionWasmBuilder::makeLocalSet(Element& s) { - auto ret = allocator.alloc<LocalSet>(); - ret->index = getLocalIndex(*s[1]); - ret->value = parseExpression(s[2]); - ret->makeSet(); - ret->finalize(); - return ret; -} - -Expression* SExpressionWasmBuilder::makeGlobalGet(Element& s) { - auto ret = allocator.alloc<GlobalGet>(); - ret->name = getGlobalName(*s[1]); - auto* global = wasm.getGlobalOrNull(ret->name); - if (!global) { - throw SParseException("bad global.get name", s); - } - ret->type = global->type; - return ret; -} - -Expression* SExpressionWasmBuilder::makeGlobalSet(Element& s) { - auto ret = allocator.alloc<GlobalSet>(); - ret->name = getGlobalName(*s[1]); - if (wasm.getGlobalOrNull(ret->name) && - !wasm.getGlobalOrNull(ret->name)->mutable_) { - throw SParseException("global.set of immutable", s); - } - ret->value = parseExpression(s[2]); - ret->finalize(); - return ret; -} - -Expression* SExpressionWasmBuilder::makeBlock(Element& s) { - if (!currFunction) { - throw SParseException("block is unallowed outside of functions", s); - } - // special-case Block, because Block nesting (in their first element) can be - // incredibly deep - auto curr = allocator.alloc<Block>(); - auto* sp = &s; - // The information we need for the stack of blocks here is the element we are - // converting, the block we are converting it to, and whether it originally - // had a name or not (which will be useful later). - struct Info { - Element* element; - Block* block; - bool hadName; - }; - std::vector<Info> stack; - while (1) { - auto& s = *sp; - Index i = 1; - Name sName; - bool hadName = false; - if (i < s.size() && s[i]->isStr()) { - // could be a name or a type - if (s[i]->dollared() || - stringToType(s[i]->str(), true /* allowError */) == Type::none) { - sName = s[i++]->str(); - hadName = true; - } else { - sName = "block"; - } - } else { - sName = "block"; - } - stack.emplace_back(Info{sp, curr, hadName}); - curr->name = nameMapper.pushLabelName(sName); - // block signature - curr->type = parseBlockType(s, i); - if (i >= s.size()) { - break; // empty block - } - auto& first = *s[i]; - if (elementStartsWith(first, BLOCK)) { - // recurse - curr = allocator.alloc<Block>(); - if (first.startLoc) { - currFunction->debugLocations[curr] = getDebugLocation(*first.startLoc); - } - sp = &first; - continue; - } - break; - } - // we now have a stack of Blocks, with their labels, but no contents yet - for (int t = int(stack.size()) - 1; t >= 0; t--) { - auto* sp = stack[t].element; - auto* curr = stack[t].block; - auto hadName = stack[t].hadName; - auto& s = *sp; - size_t i = 1; - if (i < s.size()) { - while (i < s.size() && s[i]->isStr()) { - i++; - } - while (i < s.size() && (elementStartsWith(*s[i], RESULT) || - elementStartsWith(*s[i], TYPE))) { - i++; - } - if (t < int(stack.size()) - 1) { - // first child is one of our recursions - curr->list.push_back(stack[t + 1].block); - i++; - } - for (; i < s.size(); i++) { - curr->list.push_back(parseExpression(s[i])); - } - } - nameMapper.popLabelName(curr->name); - curr->finalize(curr->type); - // If the block never had a name, and one was not needed in practice (even - // if one did not exist, perhaps a break targeted it by index), then we can - // remove the name. Note that we only do this if it never had a name: if it - // did, we don't want to change anything; we just want to be the same as - // the code we are loading - if there was no name before, we don't want one - // now, so that we roundtrip text precisely. - if (!hadName && !BranchUtils::BranchSeeker::has(curr, curr->name)) { - curr->name = Name(); - } - } - return stack[0].block; -} - -// Similar to block, but the label is handled by the enclosing if (since there -// might not be a then or else, ick) -Expression* SExpressionWasmBuilder::makeThenOrElse(Element& s) { - if (s.size() == 2) { - return parseExpression(s[1]); - } - auto ret = allocator.alloc<Block>(); - for (size_t i = 1; i < s.size(); i++) { - ret->list.push_back(parseExpression(s[i])); - } - ret->finalize(); - return ret; -} - -static Expression* parseConst(IString s, Type type, MixedArena& allocator) { - const char* str = s.str.data(); - auto ret = allocator.alloc<Const>(); - ret->type = type; - if (type.isFloat()) { - if (s == _INFINITY) { - switch (type.getBasic()) { - case Type::f32: - ret->value = Literal(std::numeric_limits<float>::infinity()); - break; - case Type::f64: - ret->value = Literal(std::numeric_limits<double>::infinity()); - break; - default: - return nullptr; - } - return ret; - } - if (s == NEG_INFINITY) { - switch (type.getBasic()) { - case Type::f32: - ret->value = Literal(-std::numeric_limits<float>::infinity()); - break; - case Type::f64: - ret->value = Literal(-std::numeric_limits<double>::infinity()); - break; - default: - return nullptr; - } - return ret; - } - if (s == _NAN) { - switch (type.getBasic()) { - case Type::f32: - ret->value = Literal(float(std::nan(""))); - break; - case Type::f64: - ret->value = Literal(double(std::nan(""))); - break; - default: - return nullptr; - } - return ret; - } - bool negative = str[0] == '-'; - const char* positive = negative ? str + 1 : str; - if (!negative) { - if (positive[0] == '+') { - positive++; - } - } - if (positive[0] == 'n' && positive[1] == 'a' && positive[2] == 'n') { - const char* modifier = positive[3] == ':' ? positive + 4 : nullptr; - if (!(modifier ? positive[4] == '0' && positive[5] == 'x' : 1)) { - throw ParseException("bad nan input: "s + str); - } - switch (type.getBasic()) { - case Type::f32: { - uint32_t pattern; - if (modifier) { - std::istringstream istr(modifier); - istr >> std::hex >> pattern; - if (istr.fail()) { - throw ParseException("invalid f32 format: "s + str); - } - pattern |= 0x7f800000U; - } else { - pattern = 0x7fc00000U; - } - if (negative) { - pattern |= 0x80000000U; - } - if (!std::isnan(bit_cast<float>(pattern))) { - pattern |= 1U; - } - ret->value = Literal(pattern).castToF32(); - break; - } - case Type::f64: { - uint64_t pattern; - if (modifier) { - std::istringstream istr(modifier); - istr >> std::hex >> pattern; - if (istr.fail()) { - throw ParseException("invalid f64 format: "s + str); - } - pattern |= 0x7ff0000000000000ULL; - } else { - pattern = 0x7ff8000000000000UL; - } - if (negative) { - pattern |= 0x8000000000000000ULL; - } - if (!std::isnan(bit_cast<double>(pattern))) { - pattern |= 1ULL; - } - ret->value = Literal(pattern).castToF64(); - break; - } - default: - return nullptr; - } - // std::cerr << "make constant " << str << " ==> " << ret->value << '\n'; - return ret; - } - if (s == NEG_NAN) { - switch (type.getBasic()) { - case Type::f32: - ret->value = Literal(float(-std::nan(""))); - break; - case Type::f64: - ret->value = Literal(double(-std::nan(""))); - break; - default: - return nullptr; - } - // std::cerr << "make constant " << str << " ==> " << ret->value << '\n'; - return ret; - } - } - switch (type.getBasic()) { - case Type::i32: { - if ((str[0] == '0' && str[1] == 'x') || - (str[0] == '-' && str[1] == '0' && str[2] == 'x')) { - bool negative = str[0] == '-'; - if (negative) { - str++; - } - std::istringstream istr(str); - uint32_t temp; - istr >> std::hex >> temp; - if (istr.fail()) { - throw ParseException("invalid i32 format: "s + str); - } - ret->value = Literal(negative ? -temp : temp); - } else { - std::istringstream istr(str[0] == '-' ? str + 1 : str); - uint32_t temp; - istr >> temp; - if (istr.fail()) { - throw ParseException("invalid i32 format: "s + str); - } - ret->value = Literal(str[0] == '-' ? -temp : temp); - } - break; - } - case Type::i64: { - if ((str[0] == '0' && str[1] == 'x') || - (str[0] == '-' && str[1] == '0' && str[2] == 'x')) { - bool negative = str[0] == '-'; - if (negative) { - str++; - } - std::istringstream istr(str); - uint64_t temp; - istr >> std::hex >> temp; - if (istr.fail()) { - throw ParseException("invalid i64 format: "s + str); - } - ret->value = Literal(negative ? -temp : temp); - } else { - std::istringstream istr(str[0] == '-' ? str + 1 : str); - uint64_t temp; - istr >> temp; - if (istr.fail()) { - throw ParseException("invalid i64 format: "s + str); - } - ret->value = Literal(str[0] == '-' ? -temp : temp); - } - break; - } - case Type::f32: { - char* end; - ret->value = Literal(strtof(str, &end)); - break; - } - case Type::f64: { - char* end; - ret->value = Literal(strtod(str, &end)); - break; - } - case Type::v128: - WASM_UNREACHABLE("unexpected const type"); - case Type::none: - case Type::unreachable: { - return nullptr; - } - } - if (ret->value.type != type) { - throw ParseException("parsed type does not match expected type: "s + str); - } - return ret; -} - -template<int Lanes> -static Literal makeLanes(Element& s, MixedArena& allocator, Type lane_t) { - std::array<Literal, Lanes> lanes; - for (size_t i = 0; i < Lanes; ++i) { - Expression* lane = parseConst(s[i + 2]->str(), lane_t, allocator); - if (lane) { - lanes[i] = lane->cast<Const>()->value; - } else { - throw SParseException("Could not parse v128 lane", s, *s[i + 2]); - } - } - return Literal(lanes); -} - -Expression* SExpressionWasmBuilder::makeConst(Element& s, Type type) { - if (type != Type::v128) { - auto ret = parseConst(s[1]->str(), type, allocator); - if (!ret) { - throw SParseException("bad const", s, *s[1]); - } - return ret; - } - - auto ret = allocator.alloc<Const>(); - Type lane_t = stringToLaneType(s[1]->str().str.data()); - size_t lanes = s.size() - 2; - switch (lanes) { - case 2: { - if (lane_t != Type::i64 && lane_t != Type::f64) { - throw SParseException("Unexpected v128 literal lane type", s); - } - ret->value = makeLanes<2>(s, allocator, lane_t); - break; - } - case 4: { - if (lane_t != Type::i32 && lane_t != Type::f32) { - throw SParseException("Unexpected v128 literal lane type", s); - } - ret->value = makeLanes<4>(s, allocator, lane_t); - break; - } - case 8: { - if (lane_t != Type::i32) { - throw SParseException("Unexpected v128 literal lane type", s); - } - ret->value = makeLanes<8>(s, allocator, lane_t); - break; - } - case 16: { - if (lane_t != Type::i32) { - throw SParseException("Unexpected v128 literal lane type", s); - } - ret->value = makeLanes<16>(s, allocator, lane_t); - break; - } - default: - throw SParseException("Unexpected number of lanes in v128 literal", s); - } - ret->finalize(); - return ret; -} - -static size_t parseMemAttributes(size_t i, - Element& s, - Address& offset, - Address& align, - bool memory64) { - // Parse "align=X" and "offset=X" arguments, bailing out on anything else. - while (!s[i]->isList()) { - const char* str = s[i]->str().str.data(); - if (strncmp(str, "align", 5) != 0 && strncmp(str, "offset", 6) != 0) { - return i; - } - const char* eq = strchr(str, '='); - if (!eq) { - throw SParseException("missing = in memory attribute", s); - } - eq++; - if (*eq == 0) { - throw SParseException("missing value in memory attribute", s); - } - char* endptr; - uint64_t value = strtoll(eq, &endptr, 10); - if (*endptr != 0) { - throw SParseException("bad memory attribute immediate", s); - } - if (str[0] == 'a') { - if (value > std::numeric_limits<uint32_t>::max()) { - throw SParseException("bad align", s); - } - align = value; - } else if (str[0] == 'o') { - if (!memory64 && value > std::numeric_limits<uint32_t>::max()) { - throw SParseException("bad offset", s); - } - offset = value; - } else { - throw SParseException("bad memory attribute", s); - } - i++; - } - return i; -} - -bool SExpressionWasmBuilder::hasMemoryIdx(Element& s, - Index defaultSize, - Index i) { - if (s.size() > defaultSize && !s[i]->isList() && - strncmp(s[i]->str().str.data(), "align", 5) != 0 && - strncmp(s[i]->str().str.data(), "offset", 6) != 0) { - return true; - } - return false; -} - -Expression* SExpressionWasmBuilder::makeLoad( - Element& s, Type type, bool signed_, int bytes, bool isAtomic) { - auto* ret = allocator.alloc<Load>(); - ret->type = type; - ret->bytes = bytes; - ret->signed_ = signed_; - ret->offset = 0; - ret->align = bytes; - ret->isAtomic = isAtomic; - Index i = 1; - Name memory; - // Check to make sure there are more than the default args & this str isn't - // the mem attributes - if (hasMemoryIdx(s, 2, i)) { - memory = getMemoryName(*s[i++]); - } else { - memory = getMemoryNameAtIdx(0); - } - ret->memory = memory; - i = parseMemAttributes(i, s, ret->offset, ret->align, isMemory64(memory)); - ret->ptr = parseExpression(s[i]); - ret->finalize(); - return ret; -} - -Expression* SExpressionWasmBuilder::makeStore(Element& s, - Type type, - int bytes, - bool isAtomic) { - auto ret = allocator.alloc<Store>(); - ret->bytes = bytes; - ret->offset = 0; - ret->align = bytes; - ret->isAtomic = isAtomic; - ret->valueType = type; - Index i = 1; - Name memory; - // Check to make sure there are more than the default args & this str isn't - // the mem attributes - if (hasMemoryIdx(s, 3, i)) { - memory = getMemoryName(*s[i++]); - } else { - memory = getMemoryNameAtIdx(0); - } - ret->memory = memory; - i = parseMemAttributes(i, s, ret->offset, ret->align, isMemory64(memory)); - ret->ptr = parseExpression(s[i]); - ret->value = parseExpression(s[i + 1]); - ret->finalize(); - return ret; -} - -Expression* SExpressionWasmBuilder::makeAtomicRMW(Element& s, - AtomicRMWOp op, - Type type, - uint8_t bytes) { - auto ret = allocator.alloc<AtomicRMW>(); - ret->type = type; - ret->op = op; - ret->bytes = bytes; - ret->offset = 0; - Index i = 1; - Name memory; - // Check to make sure there are more than the default args & this str isn't - // the mem attributes - if (hasMemoryIdx(s, 3, i)) { - memory = getMemoryName(*s[i++]); - } else { - memory = getMemoryNameAtIdx(0); - } - ret->memory = memory; - Address align = bytes; - i = parseMemAttributes(i, s, ret->offset, align, isMemory64(memory)); - if (align != ret->bytes) { - throw SParseException("Align of Atomic RMW must match size", s); - } - ret->ptr = parseExpression(s[i]); - ret->value = parseExpression(s[i + 1]); - ret->finalize(); - return ret; -} - -Expression* SExpressionWasmBuilder::makeAtomicCmpxchg(Element& s, - Type type, - uint8_t bytes) { - auto ret = allocator.alloc<AtomicCmpxchg>(); - ret->type = type; - ret->bytes = bytes; - ret->offset = 0; - Index i = 1; - Name memory; - // Check to make sure there are more than the default args & this str isn't - // the mem attributes - if (hasMemoryIdx(s, 4, i)) { - memory = getMemoryName(*s[i++]); - } else { - memory = getMemoryNameAtIdx(0); - } - ret->memory = memory; - Address align = ret->bytes; - i = parseMemAttributes(i, s, ret->offset, align, isMemory64(memory)); - if (align != ret->bytes) { - throw SParseException("Align of Atomic Cmpxchg must match size", s); - } - ret->ptr = parseExpression(s[i]); - ret->expected = parseExpression(s[i + 1]); - ret->replacement = parseExpression(s[i + 2]); - ret->finalize(); - return ret; -} - -Expression* SExpressionWasmBuilder::makeAtomicWait(Element& s, Type type) { - auto ret = allocator.alloc<AtomicWait>(); - ret->type = Type::i32; - ret->offset = 0; - ret->expectedType = type; - Index i = 1; - Name memory; - // Check to make sure there are more than the default args & this str isn't - // the mem attributes - if (hasMemoryIdx(s, 4, i)) { - memory = getMemoryName(*s[i++]); - } else { - memory = getMemoryNameAtIdx(0); - } - ret->memory = memory; - Address expectedAlign = type == Type::i64 ? 8 : 4; - Address align = expectedAlign; - i = parseMemAttributes(i, s, ret->offset, align, isMemory64(memory)); - if (align != expectedAlign) { - throw SParseException("Align of memory.atomic.wait must match size", s); - } - ret->ptr = parseExpression(s[i]); - ret->expected = parseExpression(s[i + 1]); - ret->timeout = parseExpression(s[i + 2]); - ret->finalize(); - return ret; -} - -Expression* SExpressionWasmBuilder::makeAtomicNotify(Element& s) { - auto ret = allocator.alloc<AtomicNotify>(); - ret->type = Type::i32; - ret->offset = 0; - Index i = 1; - Name memory; - // Check to make sure there are more than the default args & this str isn't - // the mem attributes - if (hasMemoryIdx(s, 3, i)) { - memory = getMemoryName(*s[i++]); - } else { - memory = getMemoryNameAtIdx(0); - } - ret->memory = memory; - Address align = 4; - i = parseMemAttributes(i, s, ret->offset, align, isMemory64(memory)); - if (align != 4) { - throw SParseException("Align of memory.atomic.notify must be 4", s); - } - ret->ptr = parseExpression(s[i]); - ret->notifyCount = parseExpression(s[i + 1]); - ret->finalize(); - return ret; -} - -Expression* SExpressionWasmBuilder::makeAtomicFence(Element& s) { - return allocator.alloc<AtomicFence>(); -} - -static uint8_t parseLaneIndex(const Element* s, size_t lanes) { - const char* str = s->str().str.data(); - char* end; - auto n = static_cast<unsigned long long>(strtoll(str, &end, 10)); - if (end == str || *end != '\0') { - throw SParseException("Expected lane index", *s); - } - if (n > lanes) { - throw SParseException( - "lane index must be less than " + std::to_string(lanes), *s); - } - return uint8_t(n); -} - -Expression* SExpressionWasmBuilder::makeSIMDExtract(Element& s, - SIMDExtractOp op, - size_t lanes) { - auto ret = allocator.alloc<SIMDExtract>(); - ret->op = op; - ret->index = parseLaneIndex(s[1], lanes); - ret->vec = parseExpression(s[2]); - ret->finalize(); - return ret; -} - -Expression* SExpressionWasmBuilder::makeSIMDReplace(Element& s, - SIMDReplaceOp op, - size_t lanes) { - auto ret = allocator.alloc<SIMDReplace>(); - ret->op = op; - ret->index = parseLaneIndex(s[1], lanes); - ret->vec = parseExpression(s[2]); - ret->value = parseExpression(s[3]); - ret->finalize(); - return ret; -} - -Expression* SExpressionWasmBuilder::makeSIMDShuffle(Element& s) { - auto ret = allocator.alloc<SIMDShuffle>(); - for (size_t i = 0; i < 16; ++i) { - ret->mask[i] = parseLaneIndex(s[i + 1], 32); - } - ret->left = parseExpression(s[17]); - ret->right = parseExpression(s[18]); - ret->finalize(); - return ret; -} - -Expression* SExpressionWasmBuilder::makeSIMDTernary(Element& s, - SIMDTernaryOp op) { - auto ret = allocator.alloc<SIMDTernary>(); - ret->op = op; - ret->a = parseExpression(s[1]); - ret->b = parseExpression(s[2]); - ret->c = parseExpression(s[3]); - ret->finalize(); - return ret; -} - -Expression* SExpressionWasmBuilder::makeSIMDShift(Element& s, SIMDShiftOp op) { - auto ret = allocator.alloc<SIMDShift>(); - ret->op = op; - ret->vec = parseExpression(s[1]); - ret->shift = parseExpression(s[2]); - ret->finalize(); - return ret; -} - -Expression* -SExpressionWasmBuilder::makeSIMDLoad(Element& s, SIMDLoadOp op, int bytes) { - auto ret = allocator.alloc<SIMDLoad>(); - ret->op = op; - ret->offset = 0; - ret->align = bytes; - Index i = 1; - Name memory; - // Check to make sure there are more than the default args & this str isn't - // the mem attributes - if (hasMemoryIdx(s, 2, i)) { - memory = getMemoryName(*s[i++]); - } else { - memory = getMemoryNameAtIdx(0); - } - ret->memory = memory; - i = parseMemAttributes(i, s, ret->offset, ret->align, isMemory64(memory)); - ret->ptr = parseExpression(s[i]); - ret->finalize(); - return ret; -} - -Expression* SExpressionWasmBuilder::makeSIMDLoadStoreLane( - Element& s, SIMDLoadStoreLaneOp op, int bytes) { - auto* ret = allocator.alloc<SIMDLoadStoreLane>(); - ret->op = op; - ret->offset = 0; - ret->align = bytes; - size_t lanes; - switch (op) { - case Load8LaneVec128: - case Store8LaneVec128: - lanes = 16; - break; - case Load16LaneVec128: - case Store16LaneVec128: - lanes = 8; - break; - case Load32LaneVec128: - case Store32LaneVec128: - lanes = 4; - break; - case Load64LaneVec128: - case Store64LaneVec128: - lanes = 2; - break; - default: - WASM_UNREACHABLE("Unexpected SIMDLoadStoreLane op"); - } - Index i = 1; - Name memory; - // Check to make sure there are more than the default args & this str isn't - // the mem attributes - if (hasMemoryIdx(s, 4, i)) { - memory = getMemoryName(*s[i++]); - } else { - memory = getMemoryNameAtIdx(0); - } - ret->memory = memory; - i = parseMemAttributes(i, s, ret->offset, ret->align, isMemory64(memory)); - ret->index = parseLaneIndex(s[i++], lanes); - ret->ptr = parseExpression(s[i++]); - ret->vec = parseExpression(s[i]); - ret->finalize(); - return ret; -} - -Expression* SExpressionWasmBuilder::makeMemoryInit(Element& s) { - auto ret = allocator.alloc<MemoryInit>(); - Index i = 1; - Name memory; - if (s.size() > 5) { - memory = getMemoryName(*s[i++]); - } else { - memory = getMemoryNameAtIdx(0); - } - ret->memory = memory; - ret->segment = getDataSegmentName(*s[i++]); - ret->dest = parseExpression(s[i++]); - ret->offset = parseExpression(s[i++]); - ret->size = parseExpression(s[i]); - ret->finalize(); - return ret; -} - -Expression* SExpressionWasmBuilder::makeDataDrop(Element& s) { - auto ret = allocator.alloc<DataDrop>(); - ret->segment = getDataSegmentName(*s[1]); - ret->finalize(); - return ret; -} - -Expression* SExpressionWasmBuilder::makeMemoryCopy(Element& s) { - auto ret = allocator.alloc<MemoryCopy>(); - Index i = 1; - Name destMemory; - Name sourceMemory; - if (s.size() > 4) { - destMemory = getMemoryName(*s[i++]); - sourceMemory = getMemoryName(*s[i++]); - } else { - destMemory = getMemoryNameAtIdx(0); - sourceMemory = getMemoryNameAtIdx(0); - } - ret->destMemory = destMemory; - ret->sourceMemory = sourceMemory; - ret->dest = parseExpression(s[i++]); - ret->source = parseExpression(s[i++]); - ret->size = parseExpression(s[i]); - ret->finalize(); - return ret; -} - -Expression* SExpressionWasmBuilder::makeMemoryFill(Element& s) { - auto ret = allocator.alloc<MemoryFill>(); - Index i = 1; - Name memory; - if (s.size() > 4) { - memory = getMemoryName(*s[i++]); - } else { - memory = getMemoryNameAtIdx(0); - } - ret->memory = memory; - ret->dest = parseExpression(s[i++]); - ret->value = parseExpression(s[i++]); - ret->size = parseExpression(s[i]); - ret->finalize(); - return ret; -} - -Expression* SExpressionWasmBuilder::makePop(Element& s) { - auto ret = allocator.alloc<Pop>(); - if (s.size() != 2) { - throw SParseException("expected 'pop <valtype>'", s); - } - ret->type = elementToType(*s[1]); - ret->finalize(); - return ret; -} - -Expression* SExpressionWasmBuilder::makeIf(Element& s) { - auto ret = allocator.alloc<If>(); - Index i = 1; - Name sName; - if (s[i]->dollared()) { - // the if is labeled - sName = s[i++]->str(); - } else { - sName = "if"; - } - auto label = nameMapper.pushLabelName(sName); - // if signature - Type type = parseBlockType(s, i); - ret->condition = parseExpression(s[i++]); - if (!elementStartsWith(*s[i], "then")) { - throw SParseException("expected 'then'", *s[i]); - } - ret->ifTrue = parseExpression(*s[i++]); - if (i < s.size()) { - if (!elementStartsWith(*s[i], "else")) { - throw SParseException("expected 'else'", *s[i]); - } - ret->ifFalse = parseExpression(*s[i++]); - } - ret->finalize(type); - nameMapper.popLabelName(label); - // create a break target if we must - if (BranchUtils::BranchSeeker::has(ret, label)) { - auto* block = allocator.alloc<Block>(); - block->name = label; - block->list.push_back(ret); - block->finalize(type); - return block; - } - return ret; -} - -Expression* -SExpressionWasmBuilder::makeMaybeBlock(Element& s, size_t i, Type type) { - Index stopAt = -1; - if (s.size() == i) { - return allocator.alloc<Nop>(); - } - if (s.size() == i + 1) { - return parseExpression(s[i]); - } - auto ret = allocator.alloc<Block>(); - for (; i < s.size() && i < stopAt; i++) { - ret->list.push_back(parseExpression(s[i])); - } - ret->finalize(type); - // Note that we do not name these implicit/synthetic blocks. They - // are the effects of syntactic sugar, and nothing can branch to - // them anyhow. - return ret; -} - -Type SExpressionWasmBuilder::parseBlockType(Element& s, Index& i) { - if (s.size() == i) { - return Type::none; - } - - // TODO(sbc): Remove support for old result syntax (bare streing) once the - // spec tests are updated. - if (s[i]->isStr()) { - return stringToType(s[i++]->str()); - } - - Element* results = s[i]; - IString id = (*results)[0]->str(); - std::optional<Signature> usedType; - if (id == TYPE) { - auto type = parseHeapType(*(*results)[1]); - if (!type.isSignature()) { - throw SParseException("unexpected non-function type", s); - } - usedType = type.getSignature(); - if (usedType->params != Type::none) { - throw SParseException("block input values are not yet supported", s); - } - i++; - results = s[i]; - id = (*results)[0]->str(); - } - - if (id == RESULT) { - i++; - auto type = Type(parseResults(*results)); - if (usedType && usedType->results != type) { - throw SParseException("results do not match type", s); - } - return type; - } - - if (usedType && usedType->results != Type::none) { - throw SParseException("results do not match type", s); - } - return Type::none; -} - -Expression* SExpressionWasmBuilder::makeLoop(Element& s) { - auto ret = allocator.alloc<Loop>(); - Index i = 1; - Name sName; - if (s.size() > i && s[i]->dollared()) { - sName = s[i++]->str(); - } else { - sName = "loop-in"; - } - ret->name = nameMapper.pushLabelName(sName); - ret->type = parseBlockType(s, i); - ret->body = makeMaybeBlock(s, i, ret->type); - nameMapper.popLabelName(ret->name); - ret->finalize(ret->type); - return ret; -} - -Expression* SExpressionWasmBuilder::makeCall(Element& s, bool isReturn) { - auto target = getFunctionName(*s[1]); - auto ret = allocator.alloc<Call>(); - ret->target = target; - ret->type = getFunctionType(ret->target, s).getSignature().results; - parseCallOperands(s, 2, s.size(), ret); - ret->isReturn = isReturn; - ret->finalize(); - return ret; -} - -Expression* SExpressionWasmBuilder::makeCallIndirect(Element& s, - bool isReturn) { - if (wasm.tables.empty()) { - throw SParseException("no tables", s); - } - Index i = 1; - auto ret = allocator.alloc<CallIndirect>(); - if (s[i]->isStr()) { - ret->table = s[i++]->str(); - } else { - ret->table = wasm.tables.front()->name; - } - HeapType callType; - i = parseTypeUse(s, i, callType); - ret->heapType = callType; - parseCallOperands(s, i, s.size() - 1, ret); - ret->target = parseExpression(s[s.size() - 1]); - ret->isReturn = isReturn; - ret->finalize(); - return ret; -} - -Name SExpressionWasmBuilder::getLabel(Element& s, LabelType labelType) { - if (s.dollared()) { - return nameMapper.sourceToUnique(s.str()); - } else { - // offset, break to nth outside label - uint64_t offset; - try { - offset = std::stoll(s.toString(), nullptr, 0); - } catch (std::invalid_argument&) { - throw SParseException("invalid break offset", s); - } catch (std::out_of_range&) { - throw SParseException("out of range break offset", s); - } - if (offset > nameMapper.labelStack.size()) { - throw SParseException("invalid label", s); - } - if (offset == nameMapper.labelStack.size()) { - if (labelType == LabelType::Break) { - // a break to the function's scope. this means we need an automatic - // block, with a name - brokeToAutoBlock = true; - return FAKE_RETURN; - } - // This is a delegate that delegates to the caller - return DELEGATE_CALLER_TARGET; - } - return nameMapper.labelStack[nameMapper.labelStack.size() - 1 - offset]; - } -} - -Expression* SExpressionWasmBuilder::makeBreak(Element& s, bool isConditional) { - auto ret = allocator.alloc<Break>(); - size_t i = 1; - ret->name = getLabel(*s[i]); - i++; - if (i == s.size()) { - return ret; - } - if (isConditional) { - if (i + 1 < s.size()) { - ret->value = parseExpression(s[i]); - i++; - } - ret->condition = parseExpression(s[i]); - } else { - ret->value = parseExpression(s[i]); - } - ret->finalize(); - return ret; -} - -Expression* SExpressionWasmBuilder::makeBreakTable(Element& s) { - auto ret = allocator.alloc<Switch>(); - size_t i = 1; - while (!s[i]->isList()) { - ret->targets.push_back(getLabel(*s[i++])); - } - if (ret->targets.size() == 0) { - throw SParseException("switch with no targets", s); - } - ret->default_ = ret->targets.back(); - ret->targets.pop_back(); - ret->condition = parseExpression(s[i++]); - if (i < s.size()) { - ret->value = ret->condition; - ret->condition = parseExpression(s[i++]); - } - return ret; -} - -Expression* SExpressionWasmBuilder::makeReturn(Element& s) { - auto ret = allocator.alloc<Return>(); - if (s.size() >= 2) { - ret->value = parseExpression(s[1]); - } - return ret; -} - -Expression* SExpressionWasmBuilder::makeRefNull(Element& s) { - if (s.size() != 2) { - throw SParseException("invalid heap type reference", s); - } - auto ret = allocator.alloc<RefNull>(); - // The heap type may be just "func", that is, the whole thing is just - // (ref.null func), or it may be the name of a defined type, such as - // (ref.null $struct.FOO) - if (s[1]->dollared()) { - ret->finalize(parseHeapType(*s[1]).getBottom()); - } else { - ret->finalize(stringToHeapType(s[1]->str()).getBottom()); - } - return ret; -} - -Expression* SExpressionWasmBuilder::makeRefIsNull(Element& s) { - auto ret = allocator.alloc<RefIsNull>(); - ret->value = parseExpression(s[1]); - ret->finalize(); - return ret; -} - -Expression* SExpressionWasmBuilder::makeRefFunc(Element& s) { - auto func = getFunctionName(*s[1]); - auto ret = allocator.alloc<RefFunc>(); - ret->func = func; - // To support typed function refs, we give the reference not just a general - // funcref, but a specific subtype with the actual signature. - ret->finalize(Type(getFunctionType(func, s), NonNullable)); - return ret; -} - -Expression* SExpressionWasmBuilder::makeRefEq(Element& s) { - auto ret = allocator.alloc<RefEq>(); - ret->left = parseExpression(s[1]); - ret->right = parseExpression(s[2]); - ret->finalize(); - return ret; -} - -Expression* SExpressionWasmBuilder::makeTableGet(Element& s) { - auto tableName = s[1]->str(); - auto* index = parseExpression(s[2]); - auto* table = wasm.getTableOrNull(tableName); - if (!table) { - throw SParseException("invalid table name in table.get", s); - } - return Builder(wasm).makeTableGet(tableName, index, table->type); -} - -Expression* SExpressionWasmBuilder::makeTableSet(Element& s) { - auto tableName = s[1]->str(); - auto* table = wasm.getTableOrNull(tableName); - if (!table) { - throw SParseException("invalid table name in table.set", s); - } - auto* index = parseExpression(s[2]); - auto* value = parseExpression(s[3]); - return Builder(wasm).makeTableSet(tableName, index, value); -} - -Expression* SExpressionWasmBuilder::makeTableSize(Element& s) { - auto tableName = s[1]->str(); - auto* table = wasm.getTableOrNull(tableName); - if (!table) { - throw SParseException("invalid table name in table.size", s); - } - return Builder(wasm).makeTableSize(tableName); -} - -Expression* SExpressionWasmBuilder::makeTableGrow(Element& s) { - auto tableName = s[1]->str(); - auto* table = wasm.getTableOrNull(tableName); - if (!table) { - throw SParseException("invalid table name in table.grow", s); - } - auto* value = parseExpression(s[2]); - if (!value->type.isRef()) { - throw SParseException("only reference types are valid for tables", s); - } - auto* delta = parseExpression(s[3]); - return Builder(wasm).makeTableGrow(tableName, value, delta); -} - -Expression* SExpressionWasmBuilder::makeTableFill(Element& s) { - auto tableName = s[1]->str(); - auto* table = wasm.getTableOrNull(tableName); - if (!table) { - throw SParseException("invalid table name in table.fill", s); - } - auto* dest = parseExpression(s[2]); - auto* value = parseExpression(s[3]); - auto* size = parseExpression(s[4]); - return Builder(wasm).makeTableFill(tableName, dest, value, size); -} - -Expression* SExpressionWasmBuilder::makeTableCopy(Element& s) { - auto destTableName = s[1]->str(); - auto* destTable = wasm.getTableOrNull(destTableName); - if (!destTable) { - throw SParseException("invalid dest table name in table.copy", s); - } - auto sourceTableName = s[2]->str(); - auto* sourceTable = wasm.getTableOrNull(sourceTableName); - if (!sourceTable) { - throw SParseException("invalid source table name in table.copy", s); - } - auto* dest = parseExpression(s[3]); - auto* source = parseExpression(s[4]); - auto* size = parseExpression(s[5]); - return Builder(wasm).makeTableCopy( - dest, source, size, destTableName, sourceTableName); -} - -// try can be either in the form of try-catch or try-delegate. -// try-catch is written in the folded wast format as -// (try -// (do -// ... -// ) -// (catch $e -// ... -// ) -// ... -// (catch_all -// ... -// ) -// ) -// Any number of catch blocks can exist, including none. Zero or one catch_all -// block can exist, and if it does, it should be at the end. There should be at -// least one catch or catch_all body per try. -// -// try-delegate is written in the folded format as -// (try -// (do -// ... -// ) -// (delegate $label) -// ) -Expression* SExpressionWasmBuilder::makeTry(Element& s) { - auto ret = allocator.alloc<Try>(); - Index i = 1; - Name sName; - if (s[i]->dollared()) { - // the try is labeled - sName = s[i++]->str(); - } else { - sName = "try"; - } - ret->name = nameMapper.pushLabelName(sName); - Type type = parseBlockType(s, i); // signature - - if (!elementStartsWith(*s[i], "do")) { - throw SParseException("try body should start with 'do'", s, *s[i]); - } - ret->body = makeMaybeBlock(*s[i++], 1, type); - - while (i < s.size() && elementStartsWith(*s[i], "catch")) { - Element& inner = *s[i++]; - if (inner.size() < 2) { - throw SParseException("invalid catch block", s, inner); - } - Name tag = getTagName(*inner[1]); - if (!wasm.getTagOrNull(tag)) { - throw SParseException("bad tag name", s, inner); - } - ret->catchTags.push_back(tag); - ret->catchBodies.push_back(makeMaybeBlock(inner, 2, type)); - } - - if (i < s.size() && elementStartsWith(*s[i], "catch_all")) { - ret->catchBodies.push_back(makeMaybeBlock(*s[i++], 1, type)); - } - - // 'delegate' cannot target its own try. So we pop the name here. - nameMapper.popLabelName(ret->name); - - if (i < s.size() && elementStartsWith(*s[i], "delegate")) { - Element& inner = *s[i++]; - if (inner.size() != 2) { - throw SParseException("invalid delegate", s, inner); - } - ret->delegateTarget = getLabel(*inner[1], LabelType::Exception); - } - - if (i != s.size()) { - throw SParseException( - "there should be at most one catch_all block at the end", s); - } - - ret->finalize(type); - - // create a break target if we must - if (BranchUtils::BranchSeeker::has(ret, ret->name)) { - auto* block = allocator.alloc<Block>(); - // We create a different name for the wrapping block, because try's name can - // be used by internal delegates - block->name = nameMapper.pushLabelName(sName); - // For simplicity, try's name can only be targeted by delegates and - // rethrows. Make the branches target the new wrapping block instead. - BranchUtils::replaceBranchTargets(ret, ret->name, block->name); - block->list.push_back(ret); - nameMapper.popLabelName(block->name); - block->finalize(type); - return block; - } - return ret; -} - -Expression* SExpressionWasmBuilder::makeTryTable(Element& s) { - auto ret = allocator.alloc<TryTable>(); - Index i = 1; - Name sName; - if (s.size() > i && s[i]->dollared()) { - // the try_table is labeled - sName = s[i++]->str(); - } else { - sName = "try_table"; - } - auto label = nameMapper.pushLabelName(sName); - Type type = parseBlockType(s, i); // signature - - while (i < s.size()) { - Element& inner = *s[i]; - - if (elementStartsWith(inner, "catch") || - elementStartsWith(inner, "catch_ref")) { - bool isRef = elementStartsWith(inner, "catch_ref"); - if (inner.size() < 3) { - throw SParseException("invalid catch/catch_ref block", s, inner); - } - Name tag = getTagName(*inner[1]); - if (!wasm.getTagOrNull(tag)) { - throw SParseException("bad tag name", s, inner); - } - ret->catchTags.push_back(tag); - ret->catchDests.push_back(getLabel(*inner[2])); - ret->catchRefs.push_back(isRef); - } else if (elementStartsWith(inner, "catch_all") || - elementStartsWith(inner, "catch_all_ref")) { - bool isRef = elementStartsWith(inner, "catch_all_ref"); - if (inner.size() < 2) { - throw SParseException( - "invalid catch_all/catch_all_ref block", s, inner); - } - ret->catchTags.push_back(Name()); - ret->catchDests.push_back(getLabel(*inner[1])); - ret->catchRefs.push_back(isRef); - } else { - break; - } - i++; - } - - ret->body = makeMaybeBlock(s, i, type); - ret->finalize(type, &wasm); - nameMapper.popLabelName(label); - // create a break target if we must - if (BranchUtils::BranchSeeker::has(ret, label)) { - auto* block = allocator.alloc<Block>(); - block->name = label; - block->list.push_back(ret); - block->finalize(type); - return block; - } - return ret; -} - -Expression* SExpressionWasmBuilder::makeThrow(Element& s) { - auto ret = allocator.alloc<Throw>(); - Index i = 1; - - ret->tag = getTagName(*s[i++]); - if (!wasm.getTagOrNull(ret->tag)) { - throw SParseException("bad tag name", s, *s[i]); - } - for (; i < s.size(); i++) { - ret->operands.push_back(parseExpression(s[i])); - } - ret->finalize(); - return ret; -} - -Expression* SExpressionWasmBuilder::makeRethrow(Element& s) { - auto ret = allocator.alloc<Rethrow>(); - ret->target = getLabel(*s[1], LabelType::Exception); - ret->finalize(); - return ret; -} - -Expression* SExpressionWasmBuilder::makeThrowRef(Element& s) { - auto ret = allocator.alloc<ThrowRef>(); - ret->exnref = parseExpression(s[1]); - ret->finalize(); - return ret; -} - -Expression* SExpressionWasmBuilder::makeTupleMake(Element& s) { - auto ret = allocator.alloc<TupleMake>(); - size_t arity = std::stoll(s[1]->toString()); - if (arity != s.size() - 2) { - throw SParseException("unexpected number of elements", s, *s[1]); - } - parseCallOperands(s, 2, s.size(), ret); - ret->finalize(); - return ret; -} - -Expression* SExpressionWasmBuilder::makeTupleExtract(Element& s) { - auto ret = allocator.alloc<TupleExtract>(); - size_t arity = std::stoll(s[1]->toString()); - ret->index = parseIndex(*s[2]); - ret->tuple = parseExpression(s[3]); - if (ret->tuple->type != Type::unreachable) { - if (arity != ret->tuple->type.size()) { - throw SParseException("Unexpected tuple.extract arity", s, *s[1]); - } - if (ret->index >= ret->tuple->type.size()) { - throw SParseException("Bad index on tuple.extract", s, *s[2]); - } - } - ret->finalize(); - return ret; -} - -Expression* SExpressionWasmBuilder::makeTupleDrop(Element& s) { - size_t arity = std::stoll(s[1]->toString()); - auto ret = allocator.alloc<Drop>(); - ret->value = parseExpression(s[2]); - if (ret->value->type != Type::unreachable && - ret->value->type.size() != arity) { - throw SParseException("unexpected tuple.drop arity", s, *s[1]); - } - ret->finalize(); - return ret; -} - -Expression* SExpressionWasmBuilder::makeCallRef(Element& s, bool isReturn) { - HeapType sigType = parseHeapType(*s[1]); - std::vector<Expression*> operands; - parseOperands(s, 2, s.size() - 1, operands); - auto* target = parseExpression(s[s.size() - 1]); - - if (!sigType.isSignature()) { - throw SParseException( - std::string(isReturn ? "return_call_ref" : "call_ref") + - " type annotation should be a signature", - s); - } - if (!Type::isSubType(target->type, Type(sigType, Nullable))) { - throw SParseException( - std::string(isReturn ? "return_call_ref" : "call_ref") + - " target should match expected type", - s); - } - return Builder(wasm).makeCallRef( - target, operands, sigType.getSignature().results, isReturn); -} - -Expression* SExpressionWasmBuilder::makeContBind(Element& s) { - auto ret = allocator.alloc<ContBind>(); - - ret->contTypeBefore = parseHeapType(*s[1]); - if (!ret->contTypeBefore.isContinuation()) { - throw ParseException("expected continuation type", s[1]->line, s[1]->col); - } - ret->contTypeAfter = parseHeapType(*s[2]); - if (!ret->contTypeAfter.isContinuation()) { - throw ParseException("expected continuation type", s[2]->line, s[2]->col); - } - - Index i = 3; - while (i < s.size() - 1) { - ret->operands.push_back(parseExpression(s[i++])); - } - - ret->cont = parseExpression(s[i]); - - ret->finalize(); - return ret; -} - -Expression* SExpressionWasmBuilder::makeContNew(Element& s) { - auto ret = allocator.alloc<ContNew>(); - - ret->contType = parseHeapType(*s[1]); - if (!ret->contType.isContinuation()) { - throw ParseException("expected continuation type", s[1]->line, s[1]->col); - } - - ret->func = parseExpression(s[2]); - - ret->finalize(); - return ret; -} - -Expression* SExpressionWasmBuilder::makeResume(Element& s) { - auto ret = allocator.alloc<Resume>(); - - ret->contType = parseHeapType(*s[1]); - if (!ret->contType.isContinuation()) { - throw ParseException("expected continuation type", s[1]->line, s[1]->col); - } - - Index i = 2; - while (i < s.size() && elementStartsWith(*s[i], "tag")) { - Element& inner = *s[i++]; - if (inner.size() < 3) { - throw ParseException("invalid tag block", inner.line, inner.col); - } - Name tag = getTagName(*inner[1]); - if (!wasm.getTagOrNull(tag)) { - throw ParseException("bad tag name", inner[1]->line, inner[1]->col); - } - ret->handlerTags.push_back(tag); - ret->handlerBlocks.push_back(getLabel(*inner[2])); - } - - while (i < s.size() - 1) { - ret->operands.push_back(parseExpression(s[i++])); - } - - ret->cont = parseExpression(s[i]); - - ret->finalize(&wasm); - return ret; -} - -Expression* SExpressionWasmBuilder::makeSuspend(Element& s) { - auto ret = allocator.alloc<Suspend>(); - - ret->tag = getTagName(*s[1]); - - Index i = 2; - while (i < s.size()) { - ret->operands.push_back(parseExpression(s[i++])); - } - - ret->finalize(&wasm); - return ret; -} - -Expression* SExpressionWasmBuilder::makeRefI31(Element& s) { - auto ret = allocator.alloc<RefI31>(); - ret->value = parseExpression(s[1]); - ret->finalize(); - return ret; -} - -Expression* SExpressionWasmBuilder::makeI31Get(Element& s, bool signed_) { - auto ret = allocator.alloc<I31Get>(); - ret->i31 = parseExpression(s[1]); - ret->signed_ = signed_; - ret->finalize(); - return ret; -} - -Expression* SExpressionWasmBuilder::makeRefTest(Element& s) { - Type castType = elementToType(*s[1]); - auto* ref = parseExpression(*s[2]); - return Builder(wasm).makeRefTest(ref, castType); -} - -Expression* SExpressionWasmBuilder::makeRefCast(Element& s) { - Type castType = elementToType(*s[1]); - auto* ref = parseExpression(*s[2]); - return Builder(wasm).makeRefCast(ref, castType); -} - -Expression* SExpressionWasmBuilder::makeBrOnNull(Element& s, bool onFail) { - int i = 1; - auto name = getLabel(*s[i++]); - auto* ref = parseExpression(*s[i]); - auto op = onFail ? BrOnNonNull : BrOnNull; - return Builder(wasm).makeBrOn(op, name, ref); -} - -Expression* SExpressionWasmBuilder::makeBrOnCast(Element& s, bool onFail) { - int i = 1; - auto name = getLabel(*s[i++]); - auto inputType = elementToType(*s[i++]); - auto castType = elementToType(*s[i++]); - if (!Type::isSubType(castType, inputType)) { - throw SParseException( - "br_on_cast* cast type must be a subtype of its input type", s); - } - auto* ref = parseExpression(*s[i]); - if (!Type::isSubType(ref->type, inputType)) { - throw SParseException("br_on_cast* ref type does not match expected type", - s); - } - auto op = onFail ? BrOnCastFail : BrOnCast; - return Builder(wasm).makeBrOn(op, name, ref, castType); -} - -Expression* SExpressionWasmBuilder::makeStructNew(Element& s, bool default_) { - auto heapType = parseHeapType(*s[1]); - auto numOperands = s.size() - 2; - if (default_ && numOperands > 0) { - throw SParseException("arguments provided for struct.new", s); - } - std::vector<Expression*> operands; - operands.resize(numOperands); - for (Index i = 0; i < numOperands; i++) { - operands[i] = parseExpression(*s[i + 2]); - } - return Builder(wasm).makeStructNew(heapType, operands); -} - -Index SExpressionWasmBuilder::getStructIndex(Element& type, Element& field) { - if (field.dollared()) { - auto name = field.str(); - auto index = typeIndices[type.toString()]; - auto struct_ = types[index].getStruct(); - auto& fields = struct_.fields; - const auto& names = fieldNames[index]; - for (Index i = 0; i < fields.size(); i++) { - auto it = names.find(i); - if (it != names.end() && it->second == name) { - return i; - } - } - throw SParseException("bad struct field name", field); - } - // this is a numeric index - return parseIndex(field); -} - -Expression* SExpressionWasmBuilder::makeStructGet(Element& s, bool signed_) { - auto heapType = parseHeapType(*s[1]); - if (!heapType.isStruct()) { - throw SParseException("bad struct heap type", s); - } - auto index = getStructIndex(*s[1], *s[2]); - auto type = heapType.getStruct().fields[index].type; - auto ref = parseExpression(*s[3]); - validateHeapTypeUsingChild(ref, heapType, s); - return Builder(wasm).makeStructGet(index, ref, type, signed_); -} - -Expression* SExpressionWasmBuilder::makeStructSet(Element& s) { - auto heapType = parseHeapType(*s[1]); - if (!heapType.isStruct()) { - throw SParseException("bad struct heap type", s); - } - auto index = getStructIndex(*s[1], *s[2]); - auto ref = parseExpression(*s[3]); - validateHeapTypeUsingChild(ref, heapType, s); - auto value = parseExpression(*s[4]); - return Builder(wasm).makeStructSet(index, ref, value); -} - -Expression* SExpressionWasmBuilder::makeArrayNew(Element& s, bool default_) { - auto heapType = parseHeapType(*s[1]); - Expression* init = nullptr; - size_t i = 2; - if (!default_) { - init = parseExpression(*s[i++]); - } - auto* size = parseExpression(*s[i++]); - return Builder(wasm).makeArrayNew(heapType, size, init); -} - -Expression* SExpressionWasmBuilder::makeArrayNewData(Element& s) { - auto heapType = parseHeapType(*s[1]); - Name seg = getDataSegmentName(*s[2]); - Expression* offset = parseExpression(*s[3]); - Expression* size = parseExpression(*s[4]); - return Builder(wasm).makeArrayNewData(heapType, seg, offset, size); -} - -Expression* SExpressionWasmBuilder::makeArrayNewElem(Element& s) { - auto heapType = parseHeapType(*s[1]); - Name seg = getElemSegmentName(*s[2]); - Expression* offset = parseExpression(*s[3]); - Expression* size = parseExpression(*s[4]); - return Builder(wasm).makeArrayNewElem(heapType, seg, offset, size); -} - -Expression* SExpressionWasmBuilder::makeArrayNewFixed(Element& s) { - auto heapType = parseHeapType(*s[1]); - if ((size_t)parseIndex(*s[2]) != s.size() - 3) { - throw SParseException("wrong number of elements in array", s); - } - std::vector<Expression*> values; - for (size_t i = 3; i < s.size(); ++i) { - values.push_back(parseExpression(*s[i])); - } - return Builder(wasm).makeArrayNewFixed(heapType, values); -} - -Expression* SExpressionWasmBuilder::makeArrayGet(Element& s, bool signed_) { - auto heapType = parseHeapType(*s[1]); - if (!heapType.isArray()) { - throw SParseException("bad array heap type", s); - } - auto ref = parseExpression(*s[2]); - auto type = heapType.getArray().element.type; - validateHeapTypeUsingChild(ref, heapType, s); - auto index = parseExpression(*s[3]); - return Builder(wasm).makeArrayGet(ref, index, type, signed_); -} - -Expression* SExpressionWasmBuilder::makeArraySet(Element& s) { - auto heapType = parseHeapType(*s[1]); - auto ref = parseExpression(*s[2]); - validateHeapTypeUsingChild(ref, heapType, s); - auto index = parseExpression(*s[3]); - auto value = parseExpression(*s[4]); - return Builder(wasm).makeArraySet(ref, index, value); -} - -Expression* SExpressionWasmBuilder::makeArrayLen(Element& s) { - auto ref = parseExpression(*s[1]); - return Builder(wasm).makeArrayLen(ref); -} - -Expression* SExpressionWasmBuilder::makeArrayCopy(Element& s) { - auto destHeapType = parseHeapType(*s[1]); - auto srcHeapType = parseHeapType(*s[2]); - auto destRef = parseExpression(*s[3]); - validateHeapTypeUsingChild(destRef, destHeapType, s); - auto destIndex = parseExpression(*s[4]); - auto srcRef = parseExpression(*s[5]); - validateHeapTypeUsingChild(srcRef, srcHeapType, s); - auto srcIndex = parseExpression(*s[6]); - auto length = parseExpression(*s[7]); - return Builder(wasm).makeArrayCopy( - destRef, destIndex, srcRef, srcIndex, length); -} - -Expression* SExpressionWasmBuilder::makeArrayFill(Element& s) { - auto heapType = parseHeapType(*s[1]); - auto ref = parseExpression(*s[2]); - validateHeapTypeUsingChild(ref, heapType, s); - auto index = parseExpression(*s[3]); - auto value = parseExpression(*s[4]); - auto size = parseExpression(*s[5]); - return Builder(wasm).makeArrayFill(ref, index, value, size); -} - -Expression* SExpressionWasmBuilder::makeArrayInitData(Element& s) { - auto heapType = parseHeapType(*s[1]); - auto seg = getDataSegmentName(*s[2]); - auto ref = parseExpression(*s[3]); - validateHeapTypeUsingChild(ref, heapType, s); - auto index = parseExpression(*s[4]); - auto offset = parseExpression(*s[5]); - auto size = parseExpression(*s[6]); - return Builder(wasm).makeArrayInitData(seg, ref, index, offset, size); -} - -Expression* SExpressionWasmBuilder::makeArrayInitElem(Element& s) { - auto heapType = parseHeapType(*s[1]); - auto seg = getElemSegmentName(*s[2]); - auto ref = parseExpression(*s[3]); - validateHeapTypeUsingChild(ref, heapType, s); - auto index = parseExpression(*s[4]); - auto offset = parseExpression(*s[5]); - auto size = parseExpression(*s[6]); - return Builder(wasm).makeArrayInitElem(seg, ref, index, offset, size); -} - -Expression* SExpressionWasmBuilder::makeRefAs(Element& s, RefAsOp op) { - auto* value = parseExpression(s[1]); - if (!value->type.isRef() && value->type != Type::unreachable) { - throw SParseException("ref.as child must be a ref", s); - } - return Builder(wasm).makeRefAs(op, value); -} - -Expression* SExpressionWasmBuilder::makeStringNew(Element& s, StringNewOp op) { - if (op == StringNewLossyUTF8Array || op == StringNewWTF16Array) { - auto* start = parseExpression(s[2]); - auto* end = parseExpression(s[3]); - return Builder(wasm).makeStringNew(op, parseExpression(s[1]), start, end); - } else if (op == StringNewFromCodePoint) { - return Builder(wasm).makeStringNew(op, parseExpression(s[1])); - } else { - throw SParseException("bad string.new op", s); - } -} - -Expression* SExpressionWasmBuilder::makeStringConst(Element& s) { - std::vector<char> data; - stringToBinary(*s[1], s[1]->str().str, data); - Name str = std::string_view(data.data(), data.size()); - // Re-encode from WTF-8 to WTF-16. - std::stringstream wtf16; - if (!String::convertWTF8ToWTF16(wtf16, str.str)) { - throw SParseException("invalid string constant", s); - } - // TODO: Use wtf16.view() once we have C++20. - return Builder(wasm).makeStringConst(wtf16.str()); -} - -Expression* SExpressionWasmBuilder::makeStringMeasure(Element& s, - StringMeasureOp op) { - return Builder(wasm).makeStringMeasure(op, parseExpression(s[1])); -} - -Expression* SExpressionWasmBuilder::makeStringEncode(Element& s, - StringEncodeOp op) { - - return Builder(wasm).makeStringEncode( - op, parseExpression(s[1]), parseExpression(s[2]), parseExpression(s[3])); -} - -Expression* SExpressionWasmBuilder::makeStringConcat(Element& s) { - return Builder(wasm).makeStringConcat(parseExpression(s[1]), - parseExpression(s[2])); -} - -Expression* SExpressionWasmBuilder::makeStringEq(Element& s, StringEqOp op) { - return Builder(wasm).makeStringEq( - op, parseExpression(s[1]), parseExpression(s[2])); -} - -Expression* SExpressionWasmBuilder::makeStringWTF16Get(Element& s) { - return Builder(wasm).makeStringWTF16Get(parseExpression(s[1]), - parseExpression(s[2])); -} - -Expression* SExpressionWasmBuilder::makeStringSliceWTF(Element& s) { - return Builder(wasm).makeStringSliceWTF( - parseExpression(s[1]), parseExpression(s[2]), parseExpression(s[3])); -} - -// converts an s-expression string representing binary data into an output -// sequence of raw bytes this appends to data, which may already contain -// content. -void SExpressionWasmBuilder::stringToBinary(Element& s, - std::string_view str, - std::vector<char>& data) { - auto originalSize = data.size(); - data.resize(originalSize + str.size()); - char* write = data.data() + originalSize; - const char* end = str.data() + str.size(); - for (const char* input = str.data(); input < end;) { - if (input[0] == '\\') { - if (input + 1 >= end) { - throw SParseException("Unterminated escape sequence", s); - } - if (input[1] == 't') { - *write++ = '\t'; - input += 2; - continue; - } else if (input[1] == 'n') { - *write++ = '\n'; - input += 2; - continue; - } else if (input[1] == 'r') { - *write++ = '\r'; - input += 2; - continue; - } else if (input[1] == '"') { - *write++ = '"'; - input += 2; - continue; - } else if (input[1] == '\'') { - *write++ = '\''; - input += 2; - continue; - } else if (input[1] == '\\') { - *write++ = '\\'; - input += 2; - continue; - } else { - if (input + 2 >= end) { - throw SParseException("Unterminated escape sequence", s); - } - *write++ = (char)(unhex(input[1]) * 16 + unhex(input[2])); - input += 3; - continue; - } - } - *write++ = input[0]; - input++; - } - assert(write >= data.data()); - size_t actual = write - data.data(); - assert(actual <= data.size()); - data.resize(actual); -} - -Index SExpressionWasmBuilder::parseMemoryIndex( - Element& s, Index i, std::unique_ptr<Memory>& memory) { - if (i < s.size() && s[i]->isStr()) { - if (s[i]->str() == "i64") { - i++; - memory->indexType = Type::i64; - } else if (s[i]->str() == "i32") { - i++; - memory->indexType = Type::i32; - } - } - return i; -} - -Index SExpressionWasmBuilder::parseMemoryLimits( - Element& s, Index i, std::unique_ptr<Memory>& memory) { - i = parseMemoryIndex(s, i, memory); - if (i == s.size()) { - throw SParseException("missing memory limits", s); - } - auto initElem = s[i++]; - memory->initial = getAddress(initElem); - if (!memory->is64()) { - checkAddress(memory->initial, "excessive memory init", initElem); - } - if (i == s.size()) { - memory->max = Memory::kUnlimitedSize; - } else { - auto maxElem = s[i++]; - memory->max = getAddress(maxElem); - if (!memory->is64() && memory->max > Memory::kMaxSize32) { - throw SParseException("total memory must be <= 4GB", s, *maxElem); - } - } - return i; -} - -void SExpressionWasmBuilder::parseMemory(Element& s, bool preParseImport) { - auto memory = std::make_unique<Memory>(); - memory->shared = *s[s.size() - 1] == SHARED; - Index i = 1; - if (s[i]->dollared()) { - memory->setExplicitName(s[i++]->str()); - } else { - memory->name = Name::fromInt(memoryCounter++); - } - memoryNames.push_back(memory->name); - - i = parseMemoryIndex(s, i, memory); - Name importModule, importBase; - if (s[i]->isList()) { - auto& inner = *s[i]; - if (elementStartsWith(inner, EXPORT)) { - auto ex = std::make_unique<Export>(); - ex->name = inner[1]->str(); - ex->value = memory->name; - ex->kind = ExternalKind::Memory; - if (wasm.getExportOrNull(ex->name)) { - throw SParseException("duplicate export", inner); - } - wasm.addExport(ex.release()); - i++; - } else if (elementStartsWith(inner, IMPORT)) { - memory->module = inner[1]->str(); - memory->base = inner[2]->str(); - i++; - } else { - if (!(inner.size() > 0 ? inner[0]->str() != IMPORT : true)) { - throw SParseException("bad import ending", inner); - } - // (memory (data ..)) format - auto j = parseMemoryIndex(inner, 1, memory); - auto offset = allocator.alloc<Const>(); - if (memory->is64()) { - offset->set(Literal(int64_t(0))); - } else { - offset->set(Literal(int32_t(0))); - } - auto segName = Name::fromInt(dataCounter++); - auto seg = Builder::makeDataSegment(segName, memory->name, false, offset); - dataSegmentNames.push_back(segName); - parseInnerData(inner, j, seg); - memory->initial = seg->data.size(); - wasm.addDataSegment(std::move(seg)); - wasm.addMemory(std::move(memory)); - return; - } - } - i = parseMemoryLimits(s, i, memory); - if (i + int(memory->shared) != s.size()) { - throw SParseException("expected end of memory", *s[i]); - } - wasm.addMemory(std::move(memory)); -} - -void SExpressionWasmBuilder::parseData(Element& s) { - Index i = 1; - Name name = Name::fromInt(dataCounter++); - bool hasExplicitName = false; - Name memory; - bool isPassive = true; - Expression* offset = nullptr; - - if (s[i]->isStr() && s[i]->dollared()) { - name = s[i++]->str(); - hasExplicitName = true; - } - dataSegmentNames.push_back(name); - - if (s[i]->isList()) { - // Optional (memory <memoryidx>) - if (elementStartsWith(s[i], MEMORY)) { - auto& inner = *s[i++]; - memory = getMemoryName(*inner[1]); - } else { - memory = getMemoryNameAtIdx(0); - } - // Offset expression (offset (<expr>)) | (<expr>) - auto& inner = *s[i++]; - if (elementStartsWith(inner, OFFSET)) { - offset = parseExpression(inner[1]); - } else { - offset = parseExpression(inner); - } - isPassive = false; - } - - auto seg = Builder::makeDataSegment(name, memory, isPassive, offset); - seg->hasExplicitName = hasExplicitName; - parseInnerData(s, i, seg); - wasm.addDataSegment(std::move(seg)); -} - -void SExpressionWasmBuilder::parseInnerData(Element& s, - Index i, - std::unique_ptr<DataSegment>& seg) { - std::vector<char> data; - while (i < s.size()) { - std::string_view input = s[i++]->str().str; - stringToBinary(s, input, data); - } - seg->data.resize(data.size()); - std::copy_n(data.data(), data.size(), seg->data.begin()); -} - -void SExpressionWasmBuilder::parseExport(Element& s) { - std::unique_ptr<Export> ex = std::make_unique<Export>(); - std::vector<char> nameBytes; - stringToBinary(*s[1], s[1]->str().str, nameBytes); - ex->name = std::string(nameBytes.data(), nameBytes.size()); - if (s[2]->isList()) { - auto& inner = *s[2]; - if (elementStartsWith(inner, FUNC)) { - ex->kind = ExternalKind::Function; - ex->value = getFunctionName(*inner[1]); - } else if (elementStartsWith(inner, MEMORY)) { - ex->kind = ExternalKind::Memory; - ex->value = inner[1]->str(); - } else if (elementStartsWith(inner, TABLE)) { - ex->kind = ExternalKind::Table; - ex->value = getTableName(*inner[1]); - } else if (elementStartsWith(inner, GLOBAL)) { - ex->kind = ExternalKind::Global; - ex->value = getGlobalName(*inner[1]); - } else if (inner[0]->str() == TAG) { - ex->kind = ExternalKind::Tag; - ex->value = getTagName(*inner[1]); - } else { - throw SParseException("invalid export", inner); - } - } else { - // function - ex->value = s[2]->str(); - ex->kind = ExternalKind::Function; - } - if (wasm.getExportOrNull(ex->name)) { - throw SParseException("duplicate export", s); - } - wasm.addExport(ex.release()); -} - -void SExpressionWasmBuilder::parseImport(Element& s) { - size_t i = 1; - // (import "env" "STACKTOP" (global $stackTop i32)) - bool newStyle = s.size() == 4 && s[3]->isList(); - auto kind = ExternalKind::Invalid; - if (newStyle) { - if (elementStartsWith(*s[3], FUNC)) { - kind = ExternalKind::Function; - } else if (elementStartsWith(*s[3], MEMORY)) { - kind = ExternalKind::Memory; - } else if (elementStartsWith(*s[3], TABLE)) { - kind = ExternalKind::Table; - } else if (elementStartsWith(*s[3], GLOBAL)) { - kind = ExternalKind::Global; - } else if ((*s[3])[0]->str() == TAG) { - kind = ExternalKind::Tag; - } else { - newStyle = false; // either (param..) or (result..) - } - } - Index newStyleInner = 1; - Name name; - if (s.size() > 3 && s[3]->isStr()) { - name = s[i++]->str(); - } else if (newStyle && newStyleInner < s[3]->size() && - (*s[3])[newStyleInner]->dollared()) { - name = (*s[3])[newStyleInner++]->str(); - } - bool hasExplicitName = name.is(); - if (!hasExplicitName) { - if (kind == ExternalKind::Function) { - name = Name("fimport$" + std::to_string(functionCounter++)); - functionNames.push_back(name); - } else if (kind == ExternalKind::Global) { - // Handled in `parseGlobal`. - } else if (kind == ExternalKind::Memory) { - name = Name("mimport$" + std::to_string(memoryCounter++)); - } else if (kind == ExternalKind::Table) { - name = Name("timport$" + std::to_string(tableCounter++)); - } else if (kind == ExternalKind::Tag) { - name = Name("eimport$" + std::to_string(tagCounter++)); - tagNames.push_back(name); - } else { - throw SParseException("invalid import", s); - } - } - if (!newStyle) { - kind = ExternalKind::Function; - } - std::vector<char> moduleBytes; - stringToBinary(*s[i], s[i]->str().str, moduleBytes); - Name module = std::string(moduleBytes.data(), moduleBytes.size()); - i++; - - if (!s[i]->isStr()) { - throw SParseException("no name for import", s, *s[i]); - } - - std::vector<char> baseBytes; - stringToBinary(*s[i], s[i]->str().str, baseBytes); - Name base = std::string(baseBytes.data(), baseBytes.size()); - i++; - - // parse internals - Element& inner = newStyle ? *s[3] : s; - Index j = newStyle ? newStyleInner : i; - if (kind == ExternalKind::Function) { - auto func = std::make_unique<Function>(); - - j = parseTypeUse(inner, j, func->type); - func->setName(name, hasExplicitName); - func->module = module; - func->base = base; - functionTypes[name] = func->type; - wasm.addFunction(func.release()); - } else if (kind == ExternalKind::Global) { - parseGlobal(inner, true); - j++; - auto& global = wasm.globals.back(); - global->module = module; - global->base = base; - } else if (kind == ExternalKind::Table) { - auto table = std::make_unique<Table>(); - table->setName(name, hasExplicitName); - table->module = module; - table->base = base; - tableNames.push_back(name); - - if (j < inner.size() - 1) { - auto initElem = inner[j++]; - table->initial = getAddress(initElem); - checkAddress(table->initial, "excessive table init size", initElem); - } - if (j < inner.size() - 1) { - auto maxElem = inner[j++]; - table->max = getAddress(maxElem); - checkAddress(table->max, "excessive table max size", maxElem); - } else { - table->max = Table::kUnlimitedSize; - } - - // ends with the table element type - table->type = elementToType(*inner[j++]); - if (!table->type.isRef()) { - throw SParseException("Only reference types are valid for tables", s); - } - - wasm.addTable(std::move(table)); - } else if (kind == ExternalKind::Memory) { - auto memory = std::make_unique<Memory>(); - memory->setName(name, hasExplicitName); - memory->module = module; - memory->base = base; - memoryNames.push_back(name); - - j = parseMemoryLimits(inner, j, memory); - if (j != inner.size() && *inner[j] == SHARED) { - memory->shared = true; - j++; - } - - wasm.addMemory(std::move(memory)); - } else if (kind == ExternalKind::Tag) { - auto tag = std::make_unique<Tag>(); - HeapType tagType; - j = parseTypeUse(inner, j, tagType); - tag->sig = tagType.getSignature(); - tag->setName(name, hasExplicitName); - tag->module = module; - tag->base = base; - wasm.addTag(tag.release()); - } - // If there are more elements, they are invalid - if (j < inner.size()) { - throw SParseException("invalid element", inner, *inner[j]); - } -} - -void SExpressionWasmBuilder::parseGlobal(Element& s, bool preParseImport) { - std::unique_ptr<Global> global = std::make_unique<Global>(); - size_t i = 1; - if (s[i]->dollared()) { - global->setExplicitName(s[i++]->str()); - } else if (preParseImport) { - global->name = Name("gimport$" + std::to_string(globalCounter)); - } else { - global->name = Name::fromInt(globalCounter); - } - globalCounter++; - globalNames.push_back(global->name); - bool mutable_ = false; - Type type = Type::none; - Name importModule, importBase; - while (i < s.size() && s[i]->isList()) { - auto& inner = *s[i++]; - if (elementStartsWith(inner, EXPORT)) { - auto ex = std::make_unique<Export>(); - ex->name = inner[1]->str(); - ex->value = global->name; - ex->kind = ExternalKind::Global; - if (wasm.getExportOrNull(ex->name)) { - throw SParseException("duplicate export", s); - } - wasm.addExport(ex.release()); - } else if (elementStartsWith(inner, IMPORT)) { - importModule = inner[1]->str(); - importBase = inner[2]->str(); - } else if (elementStartsWith(inner, MUT)) { - mutable_ = true; - type = elementToType(*inner[1]); - break; - } else { - type = elementToType(inner); - break; - } - } - if (type == Type::none) { - type = stringToType(s[i++]->str()); - } - if (importModule.is()) { - // this is an import, actually - if (!importBase.size()) { - throw SParseException("module but no base for import", s); - } - if (!preParseImport) { - throw SParseException("!preParseImport in global", s); - } - auto im = std::make_unique<Global>(); - im->name = global->name; - im->module = importModule; - im->base = importBase; - im->type = type; - im->mutable_ = mutable_; - if (wasm.getGlobalOrNull(im->name)) { - throw SParseException("duplicate import", s); - } - wasm.addGlobal(im.release()); - return; - } - global->type = type; - if (i < s.size()) { - global->init = parseExpression(s[i++]); - } else if (!preParseImport) { - throw SParseException("global without init", s); - } - global->mutable_ = mutable_; - if (i != s.size()) { - throw SParseException("extra import elements", s); - } - if (wasm.getGlobalOrNull(global->name)) { - throw SParseException("duplicate import", s); - } - wasm.addGlobal(global.release()); -} - -void SExpressionWasmBuilder::parseTable(Element& s, bool preParseImport) { - std::unique_ptr<Table> table = std::make_unique<Table>(); - Index i = 1; - if (s[i]->dollared()) { - table->setExplicitName(s[i++]->str()); - } else { - table->name = Name::fromInt(tableCounter++); - } - tableNames.push_back(table->name); - - Name importModule, importBase; - if (s[i]->isList()) { - auto& inner = *s[i]; - if (elementStartsWith(inner, EXPORT)) { - auto ex = std::make_unique<Export>(); - ex->name = inner[1]->str(); - ex->value = table->name; - ex->kind = ExternalKind::Table; - if (wasm.getExportOrNull(ex->name)) { - throw SParseException("duplicate export", inner); - } - wasm.addExport(ex.release()); - i++; - } else if (elementStartsWith(inner, IMPORT)) { - if (!preParseImport) { - throw SParseException("!preParseImport in table", inner); - } - table->module = inner[1]->str(); - table->base = inner[2]->str(); - i++; - } else if (!elementStartsWith(inner, REF)) { - throw SParseException("invalid table", inner); - } - } - - bool hasExplicitLimit = false; - - if (s[i]->isStr() && String::isNumber(s[i]->toString())) { - table->initial = parseIndex(*s[i++]); - hasExplicitLimit = true; - } - if (s[i]->isStr() && String::isNumber(s[i]->toString())) { - table->max = parseIndex(*s[i++]); - } - - table->type = elementToType(*s[i++]); - if (!table->type.isRef()) { - throw SParseException("Only reference types are valid for tables", s); - } - - if (i < s.size() && s[i]->isList()) { - if (hasExplicitLimit) { - throw SParseException( - "Table cannot have both explicit limits and an inline (elem ...)", s); - } - // (table type (elem ..)) - parseElem(*s[i], table.get()); - auto it = std::find_if(wasm.elementSegments.begin(), - wasm.elementSegments.end(), - [&](std::unique_ptr<ElementSegment>& segment) { - return segment->table == table->name; - }); - if (it != wasm.elementSegments.end()) { - table->initial = table->max = it->get()->data.size(); - } else { - table->initial = table->max = 0; - } - } - - wasm.addTable(std::move(table)); -} - -// parses an elem segment -// elem ::= (elem (table tableidx)? (offset (expr)) reftype vec(item (expr))) -// | (elem reftype vec(item (expr))) -// | (elem declare reftype vec(item (expr))) -// -// abbreviation: -// (offset (expr)) ≡ (expr) -// (item (expr)) ≡ (expr) -// ϵ ≡ (table 0) -// -// funcref vec(ref.func) ≡ func vec(funcidx) -// (elem (expr) vec(funcidx)) ≡ (elem (table 0) (offset (expr)) func -// vec(funcidx)) -// -void SExpressionWasmBuilder::parseElem(Element& s, Table* table) { - Index i = 1; - Name name = Name::fromInt(elemCounter++); - bool hasExplicitName = false; - bool isPassive = true; - bool usesExpressions = false; - - if (table) { - Expression* offset = allocator.alloc<Const>()->set(Literal(int32_t(0))); - auto segment = std::make_unique<ElementSegment>(table->name, offset); - segment->setName(name, hasExplicitName); - elemSegmentNames.push_back(name); - parseElemFinish(s, segment, i, s[i]->isList()); - return; - } - - if (s[i]->isStr() && s[i]->dollared()) { - name = s[i++]->str(); - hasExplicitName = true; - } - elemSegmentNames.push_back(name); - if (s[i]->isStr() && s[i]->str() == DECLARE) { - // We don't store declared segments in the IR - return; - } - - auto segment = std::make_unique<ElementSegment>(); - segment->setName(name, hasExplicitName); - - if (s[i]->isList() && !elementStartsWith(s[i], REF)) { - // Optional (table <tableidx>) - if (elementStartsWith(s[i], TABLE)) { - auto& inner = *s[i++]; - segment->table = getTableName(*inner[1]); - } - - // Offset expression (offset (<expr>)) | (<expr>) - auto& inner = *s[i++]; - if (elementStartsWith(inner, OFFSET)) { - if (inner.size() > 2) { - throw SParseException( - "Invalid offset for an element segment.", s, *s[i]); - } - segment->offset = parseExpression(inner[1]); - } else { - segment->offset = parseExpression(inner); - } - isPassive = false; - } - - if (i < s.size()) { - if (s[i]->isStr() && s[i]->dollared()) { - usesExpressions = false; - } else if (s[i]->isStr() && s[i]->str() == FUNC) { - usesExpressions = false; - i += 1; - } else { - segment->type = elementToType(*s[i]); - usesExpressions = true; - i += 1; - } - } - - if (!isPassive && segment->table.isNull()) { - if (wasm.tables.empty()) { - throw SParseException("active element without table", s); - } - table = wasm.tables.front().get(); - segment->table = table->name; - } - - // We may be post-MVP also due to type reasons or otherwise, as detected by - // the utility function for Binaryen IR. - usesExpressions = - usesExpressions || TableUtils::usesExpressions(segment.get(), &wasm); - - parseElemFinish(s, segment, i, usesExpressions); -} - -ElementSegment* SExpressionWasmBuilder::parseElemFinish( - Element& s, - std::unique_ptr<ElementSegment>& segment, - Index i, - bool usesExpressions) { - - for (; i < s.size(); i++) { - if (!s[i]->isList()) { - // An MVP-style declaration: just a function name. - auto func = getFunctionName(*s[i]); - segment->data.push_back( - Builder(wasm).makeRefFunc(func, functionTypes[func])); - continue; - } - if (!usesExpressions) { - throw SParseException("expected an MVP-style $funcname in elem.", s); - } - auto& inner = *s[i]; - if (elementStartsWith(inner, ITEM)) { - if (inner[1]->isList()) { - // (item (ref.func $f)) - segment->data.push_back(parseExpression(inner[1])); - } else { - // (item ref.func $f) - inner.list().removeAt(0); - segment->data.push_back(parseExpression(inner)); - } - } else { - segment->data.push_back(parseExpression(inner)); - } - } - return wasm.addElementSegment(std::move(segment)); -} - -HeapType SExpressionWasmBuilder::parseHeapType(Element& s) { - if (s.isStr()) { - // It's a string. - if (s.dollared()) { - auto it = typeIndices.find(s.toString()); - if (it == typeIndices.end()) { - throw SParseException("unknown dollared function type", s); - } - return types[it->second]; - } else { - // It may be a numerical index, or it may be a built-in type name like - // "i31". - auto str = s.toString(); - if (String::isNumber(str)) { - size_t offset = parseIndex(s); - if (offset >= types.size()) { - throw SParseException("unknown indexed function type", s); - } - return types[offset]; - } - return stringToHeapType(s.str(), /* prefix = */ false); - } - } - throw SParseException("invalid heap type", s); -} - -void SExpressionWasmBuilder::parseTag(Element& s, bool preParseImport) { - auto tag = std::make_unique<Tag>(); - size_t i = 1; - - // Parse name - if (s[i]->isStr() && s[i]->dollared()) { - auto& inner = *s[i++]; - tag->setExplicitName(inner.str()); - if (wasm.getTagOrNull(tag->name)) { - throw SParseException("duplicate tag", inner); - } - } else { - tag->name = Name::fromInt(tagCounter); - assert(!wasm.getTagOrNull(tag->name)); - } - tagCounter++; - tagNames.push_back(tag->name); - - // Parse import, if any - if (i < s.size() && elementStartsWith(*s[i], IMPORT)) { - assert(preParseImport && "import element in non-preParseImport mode"); - auto& importElem = *s[i++]; - if (importElem.size() != 3) { - throw SParseException("invalid import", importElem); - } - if (!importElem[1]->isStr() || importElem[1]->dollared()) { - throw SParseException("invalid import module name", importElem); - } - if (!importElem[2]->isStr() || importElem[2]->dollared()) { - throw SParseException("invalid import base name", importElem); - } - tag->module = importElem[1]->str(); - tag->base = importElem[2]->str(); - } - - // Parse export, if any - if (i < s.size() && elementStartsWith(*s[i], EXPORT)) { - auto& exportElem = *s[i++]; - if (tag->module.is()) { - throw SParseException("import and export cannot be specified together", - exportElem); - } - if (exportElem.size() != 2) { - throw SParseException("invalid export", exportElem); - } - if (!exportElem[1]->isStr() || exportElem[1]->dollared()) { - throw SParseException("invalid export name", exportElem); - } - auto ex = std::make_unique<Export>(); - ex->name = exportElem[1]->str(); - if (wasm.getExportOrNull(ex->name)) { - throw SParseException("duplicate export", exportElem); - } - ex->value = tag->name; - ex->kind = ExternalKind::Tag; - wasm.addExport(ex.release()); - } - - // Parse typeuse - HeapType tagType; - i = parseTypeUse(s, i, tagType); - tag->sig = tagType.getSignature(); - - // If there are more elements, they are invalid - if (i < s.size()) { - throw SParseException("invalid element", *s[i]); - } - - wasm.addTag(tag.release()); -} - -void SExpressionWasmBuilder::validateHeapTypeUsingChild(Expression* child, - HeapType heapType, - Element& s) { - if (child->type == Type::unreachable) { - return; - } - if (!child->type.isRef() || - !HeapType::isSubType(child->type.getHeapType(), heapType)) { - throw SParseException("bad heap type: expected " + heapType.toString() + - " but found " + child->type.toString(), - s); - } -} - -} // namespace wasm |