Files
palemoon27/js/src/asmjs/WasmText.cpp
T
roytam1 cc394d8cae import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1253094, part 8 - Stop using DebugOnly for class/struct members in uriloader/. r=bz (15566e1146)
- Bug 1253094, part 9 - Stop using DebugOnly for class/struct members in xpcom/. r=froydnj (9fb881be79)
- Bug 1253094, part 10 - Stop using DebugOnly for class/struct members in memory/. r=njn (5fd563e632)
- Bug 1248843 - Make it clearer that DebugOnly uses up space even in optimized, non-DEBUG builds. r=Waldo (8e5e6e6a01)
- Bug 1253094, part 11 - Make DebugOnly a MOZ_STACK_CLASS. r=Waldo (7cee0c3c03)
- Bug 1246116 - BaldrMonkey: Wasm validation for block and loop. r=luke (0da84fb8fe)
- Bug 1252498 - Baldr: add Wasm object behind pref, default off (r=jorendorff) (b554912a96)
- Bug 1256988 - Fix #endif comments for MOZ_WIDGET_GTK. r=chmanchester (28928d1d58)
- Bug 724538 - Regenerate Unicode property data with updated script. r=emk (50e43bb897)
- Bug 1232665 - initialize class members: mLastPrefLang and mLastPrefFirstFont. r=jfkthame (8a62f92809)
- Bug 1248248 - Don't break glyph run for orientation mismatch before a cluster-extender. r=xidorn (f114f65903)
- Bug 1252432 part 1 - Implement wasm i32.wrap. r=luke (54d1e634b6)
- Bug 1252432 part 2 - Implement wasm i64.extend_s and i64.extend_u. r=bbouvier (d673455188)
- Bug 1252432 part 3 - Implement wasm i64.trunc_s and i64.trunc_u. r=sunfish (41dd8d7272)
- Bug 1253115 - BaldrMonkey: Convert AsmJSHeapAccess offsets to unsigned. r=luke (5cb02e4832)
- Bug 1253115 - BaldrMonkey: Refactor AsmJS load/store infrastructure. r=luke (ef75bae281)
- Bug 1243583 - ensure transition events are dispatched to all the relevant subdocuments, r=dholbert (05026b75bb)
- Bug 1240985 - IPC fuzzer (r=gabor) (e825e77187)
- Bug 1248750 - Eliminate intentional IPC crashes (r=dvander) (0ace690c3b)
- Bug 1242609 - Implement PeekMessage to get some messages earlier. r=billm (4985fc8394)
- Bug 1257314 - Properly lock in IPC PeekMessages. r=dvander a=topcrash (6fe1db48f4)
- Bug 1242609 - Use PeekMessages to get the most recent DisplayPort request. r=kats (12374eafba)
- Bug 1254471 - Fix MessageChannel.cpp error unused variable transaction. r=billm (6a74186673)
- Bug 1251482 - Remove remaining references to MOZILLA_XPCOMRT_API from xpcom. r=froydnj (b691ca31f4)
- Bug 1251473 - Remove libxpcomrt library. r=froydnj (faed80b0ed)
- Bug 1249787 - BaldrMonkey: Add the testcase, which was mistakenly omitted from the main push. r=luke (1ef533365a)
- Bug 1250556: Require Store value expression to have the opcode's type; r=sunfish (b8363b4fc6)
- Bug 1250955: Guard against unimplemented i64 opcodes; r=jandem (98689ea7da)
- Bug 1253137 - Baldr: update version uint32 to match BinaryEncoding.md (r=sunfish) (c75d60370a)
- Bug 1253681 - BaldrMonkey: Update to the current official opcode encodings. r=luke (2e69d5780b)
- Bug 1252019: Don't patch profiling entries for the BadIndirectCall exit; r=luke (6f336d796c)
- Bug 1253137 - Baldr: update section header structure to match BinaryEncoding.md, part 1 (r=sunfish) (cd3e204373)
- Bug 1246116 - BaldrMonkey: Wasm validation for block and loop. r=luke (c594d15189)
- Bug 1253137 - Baldr: update memory exports to match BinaryEncoding.md (r=sunfish) (73fd37ee3b)
2024-02-12 09:50:49 +08:00

4078 lines
116 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
*
* Copyright 2015 Mozilla Foundation
*
* 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 "asmjs/WasmText.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/MathAlgorithms.h"
#include "mozilla/Maybe.h"
#include "jsdtoa.h"
#include "jsnum.h"
#include "jsprf.h"
#include "jsstr.h"
#include "asmjs/WasmBinary.h"
#include "ds/LifoAlloc.h"
#include "js/CharacterEncoding.h"
#include "js/HashTable.h"
using namespace js;
using namespace js::wasm;
using mozilla::BitwiseCast;
using mozilla::CountLeadingZeroes32;
using mozilla::CheckedInt;
using mozilla::FloatingPoint;
using mozilla::Maybe;
using mozilla::PositiveInfinity;
using mozilla::SpecificNaN;
static const unsigned AST_LIFO_DEFAULT_CHUNK_SIZE = 4096;
static const uint32_t WasmNoIndex = UINT32_MAX;
/*****************************************************************************/
// wasm AST
namespace {
class WasmAstExpr;
template <class T>
using WasmAstVector = mozilla::Vector<T, 0, LifoAllocPolicy<Fallible>>;
template <class K, class V, class HP>
using WasmAstHashMap = HashMap<K, V, HP, LifoAllocPolicy<Fallible>>;
class WasmName
{
const char16_t* begin_;
const char16_t* end_;
public:
WasmName(const char16_t* begin, size_t length) : begin_(begin), end_(begin + length) {}
WasmName() : begin_(nullptr), end_(nullptr) {}
const char16_t* begin() const { return begin_; }
const char16_t* end() const { return end_; }
size_t length() const { return end_ - begin_; }
bool empty() const { return begin_ == nullptr; }
bool operator==(WasmName rhs) const {
if (length() != rhs.length())
return false;
if (begin() == rhs.begin())
return true;
return EqualChars(begin(), rhs.begin(), length());
}
bool operator!=(WasmName rhs) const {
return !(*this == rhs);
}
};
class WasmRef
{
WasmName name_;
uint32_t index_;
public:
WasmRef()
: index_(WasmNoIndex)
{
MOZ_ASSERT(isInvalid());
}
WasmRef(WasmName name, uint32_t index)
: name_(name), index_(index)
{
MOZ_ASSERT(name.empty() ^ (index == WasmNoIndex));
MOZ_ASSERT(!isInvalid());
}
bool isInvalid() const {
return name_.empty() && index_ == WasmNoIndex;
}
WasmName name() const {
return name_;
}
size_t index() const {
MOZ_ASSERT(index_ != WasmNoIndex);
return index_;
}
void setIndex(uint32_t index) {
MOZ_ASSERT(index_ == WasmNoIndex);
index_ = index;
}
};
struct WasmNameHasher
{
typedef const WasmName Lookup;
static js::HashNumber hash(Lookup l) {
return mozilla::HashString(l.begin(), l.length());
}
static bool match(const WasmName key, Lookup lookup) {
return key == lookup;
}
};
using WasmNameMap = WasmAstHashMap<WasmName, uint32_t, WasmNameHasher>;
typedef WasmAstVector<ValType> WasmAstValTypeVector;
typedef WasmAstVector<WasmAstExpr*> WasmAstExprVector;
typedef WasmAstVector<WasmName> WasmNameVector;
typedef WasmAstVector<WasmRef> WasmRefVector;
struct WasmAstBase
{
void* operator new(size_t numBytes, LifoAlloc& astLifo) throw() {
return astLifo.alloc(numBytes);
}
};
class WasmAstSig : public WasmAstBase
{
WasmName name_;
WasmAstValTypeVector args_;
ExprType ret_;
public:
explicit WasmAstSig(LifoAlloc& lifo)
: args_(lifo),
ret_(ExprType::Void)
{}
WasmAstSig(WasmAstValTypeVector&& args, ExprType ret)
: args_(Move(args)),
ret_(ret)
{}
WasmAstSig(WasmName name, WasmAstSig&& rhs)
: name_(name),
args_(Move(rhs.args_)),
ret_(rhs.ret_)
{}
void operator=(WasmAstSig&& rhs) {
args_ = Move(rhs.args_);
ret_ = rhs.ret_;
}
const WasmAstValTypeVector& args() const {
return args_;
}
ExprType ret() const {
return ret_;
}
WasmName name() const {
return name_;
}
bool operator==(const WasmAstSig& rhs) const {
return ret() == rhs.ret() && EqualContainers(args(), rhs.args());
}
typedef const WasmAstSig& Lookup;
static HashNumber hash(Lookup sig) {
return AddContainerToHash(sig.args(), HashNumber(sig.ret()));
}
static bool match(const WasmAstSig* lhs, Lookup rhs) {
return *lhs == rhs;
}
};
class WasmAstNode : public WasmAstBase
{};
enum class WasmAstExprKind
{
BinaryOperator,
Block,
Branch,
BranchTable,
Call,
CallIndirect,
ComparisonOperator,
Const,
ConversionOperator,
GetLocal,
IfElse,
Load,
Nop,
Return,
SetLocal,
Store,
UnaryOperator,
};
class WasmAstExpr : public WasmAstNode
{
const WasmAstExprKind kind_;
protected:
explicit WasmAstExpr(WasmAstExprKind kind)
: kind_(kind)
{}
public:
WasmAstExprKind kind() const { return kind_; }
template <class T>
T& as() {
MOZ_ASSERT(kind() == T::Kind);
return static_cast<T&>(*this);
}
};
struct WasmAstNop : WasmAstExpr
{
WasmAstNop()
: WasmAstExpr(WasmAstExprKind::Nop)
{}
};
class WasmAstConst : public WasmAstExpr
{
const Val val_;
public:
static const WasmAstExprKind Kind = WasmAstExprKind::Const;
explicit WasmAstConst(Val val)
: WasmAstExpr(Kind),
val_(val)
{}
Val val() const { return val_; }
};
class WasmAstGetLocal : public WasmAstExpr
{
WasmRef local_;
public:
static const WasmAstExprKind Kind = WasmAstExprKind::GetLocal;
explicit WasmAstGetLocal(WasmRef local)
: WasmAstExpr(Kind),
local_(local)
{}
WasmRef& local() {
return local_;
}
};
class WasmAstSetLocal : public WasmAstExpr
{
WasmRef local_;
WasmAstExpr& value_;
public:
static const WasmAstExprKind Kind = WasmAstExprKind::SetLocal;
WasmAstSetLocal(WasmRef local, WasmAstExpr& value)
: WasmAstExpr(Kind),
local_(local),
value_(value)
{}
WasmRef& local() {
return local_;
}
WasmAstExpr& value() const {
return value_;
}
};
class WasmAstBlock : public WasmAstExpr
{
Expr expr_;
WasmName breakName_;
WasmName continueName_;
WasmAstExprVector exprs_;
public:
static const WasmAstExprKind Kind = WasmAstExprKind::Block;
explicit WasmAstBlock(Expr expr, WasmName breakName, WasmName continueName,
WasmAstExprVector&& exprs)
: WasmAstExpr(Kind),
expr_(expr),
breakName_(breakName),
continueName_(continueName),
exprs_(Move(exprs))
{}
Expr expr() const { return expr_; }
WasmName breakName() const { return breakName_; }
WasmName continueName() const { return continueName_; }
const WasmAstExprVector& exprs() const { return exprs_; }
};
class WasmAstBranch : public WasmAstExpr
{
Expr expr_;
WasmAstExpr* cond_;
WasmRef target_;
public:
static const WasmAstExprKind Kind = WasmAstExprKind::Branch;
explicit WasmAstBranch(Expr expr, WasmAstExpr* cond, WasmRef target)
: WasmAstExpr(Kind),
expr_(expr),
cond_(cond),
target_(target)
{}
Expr expr() const { return expr_; }
WasmRef& target() { return target_; }
WasmAstExpr& cond() const { MOZ_ASSERT(cond_); return *cond_; }
};
class WasmAstCall : public WasmAstExpr
{
Expr expr_;
WasmRef func_;
WasmAstExprVector args_;
public:
static const WasmAstExprKind Kind = WasmAstExprKind::Call;
WasmAstCall(Expr expr, WasmRef func, WasmAstExprVector&& args)
: WasmAstExpr(Kind), expr_(expr), func_(func), args_(Move(args))
{}
Expr expr() const { return expr_; }
WasmRef& func() { return func_; }
const WasmAstExprVector& args() const { return args_; }
};
class WasmAstCallIndirect : public WasmAstExpr
{
WasmRef sig_;
WasmAstExpr* index_;
WasmAstExprVector args_;
public:
static const WasmAstExprKind Kind = WasmAstExprKind::CallIndirect;
WasmAstCallIndirect(WasmRef sig, WasmAstExpr* index, WasmAstExprVector&& args)
: WasmAstExpr(Kind), sig_(sig), index_(index), args_(Move(args))
{}
WasmRef& sig() { return sig_; }
WasmAstExpr* index() const { return index_; }
const WasmAstExprVector& args() const { return args_; }
};
class WasmAstReturn : public WasmAstExpr
{
WasmAstExpr* maybeExpr_;
public:
static const WasmAstExprKind Kind = WasmAstExprKind::Return;
explicit WasmAstReturn(WasmAstExpr* maybeExpr)
: WasmAstExpr(Kind),
maybeExpr_(maybeExpr)
{}
WasmAstExpr* maybeExpr() const { return maybeExpr_; }
};
class WasmAstIfElse : public WasmAstExpr
{
Expr expr_;
WasmAstExpr* cond_;
WasmAstExpr* ifBody_;
WasmAstExpr* elseBody_;
public:
static const WasmAstExprKind Kind = WasmAstExprKind::IfElse;
WasmAstIfElse(Expr expr, WasmAstExpr* cond, WasmAstExpr* ifBody,
WasmAstExpr* elseBody = nullptr)
: WasmAstExpr(Kind),
expr_(expr),
cond_(cond),
ifBody_(ifBody),
elseBody_(elseBody)
{}
bool hasElse() const { return expr_ == Expr::IfElse; }
Expr expr() const { return expr_; }
WasmAstExpr& cond() const { return *cond_; }
WasmAstExpr& ifBody() const { return *ifBody_; }
WasmAstExpr& elseBody() const { return *elseBody_; }
};
class WasmAstLoadStoreAddress
{
WasmAstExpr* base_;
int32_t offset_;
int32_t align_;
public:
explicit WasmAstLoadStoreAddress(WasmAstExpr* base, int32_t offset,
int32_t align)
: base_(base),
offset_(offset),
align_(align)
{}
WasmAstExpr& base() const { return *base_; }
int32_t offset() const { return offset_; }
int32_t align() const { return align_; }
};
class WasmAstLoad : public WasmAstExpr
{
Expr expr_;
WasmAstLoadStoreAddress address_;
public:
static const WasmAstExprKind Kind = WasmAstExprKind::Load;
explicit WasmAstLoad(Expr expr, const WasmAstLoadStoreAddress &address)
: WasmAstExpr(Kind),
expr_(expr),
address_(address)
{}
Expr expr() const { return expr_; }
const WasmAstLoadStoreAddress& address() const { return address_; }
};
class WasmAstStore : public WasmAstExpr
{
Expr expr_;
WasmAstLoadStoreAddress address_;
WasmAstExpr* value_;
public:
static const WasmAstExprKind Kind = WasmAstExprKind::Store;
explicit WasmAstStore(Expr expr, const WasmAstLoadStoreAddress &address,
WasmAstExpr* value)
: WasmAstExpr(Kind),
expr_(expr),
address_(address),
value_(value)
{}
Expr expr() const { return expr_; }
const WasmAstLoadStoreAddress& address() const { return address_; }
WasmAstExpr& value() const { return *value_; }
};
class WasmAstBranchTable : public WasmAstExpr
{
WasmAstExpr& index_;
WasmRef default_;
WasmRefVector table_;
public:
static const WasmAstExprKind Kind = WasmAstExprKind::BranchTable;
explicit WasmAstBranchTable(WasmAstExpr& index, WasmRef def, WasmRefVector&& table)
: WasmAstExpr(Kind),
index_(index),
default_(def),
table_(Move(table))
{}
WasmAstExpr& index() const { return index_; }
WasmRef& def() { return default_; }
WasmRefVector& table() { return table_; }
};
class WasmAstFunc : public WasmAstNode
{
WasmName name_;
WasmRef sig_;
WasmAstValTypeVector vars_;
WasmNameVector localNames_;
WasmAstExprVector body_;
public:
WasmAstFunc(WasmName name, WasmRef sig, WasmAstValTypeVector&& vars,
WasmNameVector&& locals, WasmAstExprVector&& body)
: name_(name),
sig_(sig),
vars_(Move(vars)),
localNames_(Move(locals)),
body_(Move(body))
{}
WasmRef& sig() { return sig_; }
const WasmAstValTypeVector& vars() const { return vars_; }
const WasmNameVector& locals() const { return localNames_; }
const WasmAstExprVector& body() const { return body_; }
WasmName name() const { return name_; }
};
class WasmAstImport : public WasmAstNode
{
WasmName name_;
WasmName module_;
WasmName func_;
uint32_t sigIndex_;
public:
WasmAstImport(WasmName name, WasmName module, WasmName func, uint32_t sigIndex)
: name_(name), module_(module), func_(func), sigIndex_(sigIndex)
{}
WasmName name() const { return name_; }
WasmName module() const { return module_; }
WasmName func() const { return func_; }
uint32_t sigIndex() const { return sigIndex_; }
};
enum class WasmAstExportKind { Func, Memory };
class WasmAstExport : public WasmAstNode
{
WasmName name_;
WasmAstExportKind kind_;
WasmRef func_;
public:
WasmAstExport(WasmName name, WasmRef func)
: name_(name), kind_(WasmAstExportKind::Func), func_(func)
{}
explicit WasmAstExport(WasmName name)
: name_(name), kind_(WasmAstExportKind::Memory)
{}
WasmName name() const { return name_; }
WasmAstExportKind kind() const { return kind_; }
WasmRef& func() { return func_; }
};
typedef WasmAstVector<WasmRef> WasmAstTableElemVector;
class WasmAstTable : public WasmAstNode
{
WasmAstTableElemVector elems_;
public:
explicit WasmAstTable(WasmAstTableElemVector&& elems) : elems_(Move(elems)) {}
WasmAstTableElemVector& elems() { return elems_; }
};
class WasmAstSegment : public WasmAstNode
{
uint32_t offset_;
WasmName text_;
public:
WasmAstSegment(uint32_t offset, WasmName text)
: offset_(offset), text_(text)
{}
uint32_t offset() const { return offset_; }
WasmName text() const { return text_; }
};
typedef WasmAstVector<WasmAstSegment*> WasmAstSegmentVector;
class WasmAstMemory : public WasmAstNode
{
uint32_t initialSize_;
Maybe<uint32_t> maxSize_;
WasmAstSegmentVector segments_;
public:
explicit WasmAstMemory(uint32_t initialSize, Maybe<uint32_t> maxSize,
WasmAstSegmentVector&& segments)
: initialSize_(initialSize),
maxSize_(maxSize),
segments_(Move(segments))
{}
uint32_t initialSize() const { return initialSize_; }
const Maybe<uint32_t>& maxSize() const { return maxSize_; }
const WasmAstSegmentVector& segments() const { return segments_; }
};
class WasmAstModule : public WasmAstNode
{
typedef WasmAstVector<WasmAstFunc*> FuncVector;
typedef WasmAstVector<WasmAstImport*> ImportVector;
typedef WasmAstVector<WasmAstExport*> ExportVector;
typedef WasmAstVector<WasmAstSig*> SigVector;
typedef WasmAstHashMap<WasmAstSig*, uint32_t, WasmAstSig> SigMap;
LifoAlloc& lifo_;
WasmAstMemory* memory_;
SigVector sigs_;
SigMap sigMap_;
ImportVector imports_;
ExportVector exports_;
WasmAstTable* table_;
FuncVector funcs_;
public:
explicit WasmAstModule(LifoAlloc& lifo)
: lifo_(lifo),
memory_(nullptr),
sigs_(lifo),
sigMap_(lifo),
imports_(lifo),
exports_(lifo),
table_(nullptr),
funcs_(lifo)
{}
bool init() {
return sigMap_.init();
}
bool setMemory(WasmAstMemory* memory) {
if (memory_)
return false;
memory_ = memory;
return true;
}
WasmAstMemory* maybeMemory() const {
return memory_;
}
bool declare(WasmAstSig&& sig, uint32_t* sigIndex) {
SigMap::AddPtr p = sigMap_.lookupForAdd(sig);
if (p) {
*sigIndex = p->value();
return true;
}
*sigIndex = sigs_.length();
return sigs_.append(new (lifo_) WasmAstSig(WasmName(), Move(sig))) &&
sigMap_.add(p, sigs_.back(), *sigIndex);
}
bool append(WasmAstSig* sig) {
uint32_t sigIndex = sigs_.length();
if (!sigs_.append(sig))
return false;
SigMap::AddPtr p = sigMap_.lookupForAdd(*sig);
return p || sigMap_.add(p, sig, sigIndex);
}
const SigVector& sigs() const {
return sigs_;
}
bool append(WasmAstFunc* func) {
return funcs_.append(func);
}
const FuncVector& funcs() const {
return funcs_;
}
const ImportVector& imports() const {
return imports_;
}
bool append(WasmAstImport* imp) {
return imports_.append(imp);
}
bool append(WasmAstExport* exp) {
return exports_.append(exp);
}
const ExportVector& exports() const {
return exports_;
}
bool initTable(WasmAstTable* table) {
if (table_)
return false;
table_ = table;
return true;
}
WasmAstTable* maybeTable() const {
return table_;
}
};
class WasmAstUnaryOperator final : public WasmAstExpr
{
Expr expr_;
WasmAstExpr* op_;
public:
static const WasmAstExprKind Kind = WasmAstExprKind::UnaryOperator;
explicit WasmAstUnaryOperator(Expr expr, WasmAstExpr* op)
: WasmAstExpr(Kind),
expr_(expr), op_(op)
{}
Expr expr() const { return expr_; }
WasmAstExpr* op() const { return op_; }
};
class WasmAstBinaryOperator final : public WasmAstExpr
{
Expr expr_;
WasmAstExpr* lhs_;
WasmAstExpr* rhs_;
public:
static const WasmAstExprKind Kind = WasmAstExprKind::BinaryOperator;
explicit WasmAstBinaryOperator(Expr expr, WasmAstExpr* lhs, WasmAstExpr* rhs)
: WasmAstExpr(Kind),
expr_(expr), lhs_(lhs), rhs_(rhs)
{}
Expr expr() const { return expr_; }
WasmAstExpr* lhs() const { return lhs_; }
WasmAstExpr* rhs() const { return rhs_; }
};
class WasmAstComparisonOperator final : public WasmAstExpr
{
Expr expr_;
WasmAstExpr* lhs_;
WasmAstExpr* rhs_;
public:
static const WasmAstExprKind Kind = WasmAstExprKind::ComparisonOperator;
explicit WasmAstComparisonOperator(Expr expr, WasmAstExpr* lhs, WasmAstExpr* rhs)
: WasmAstExpr(Kind),
expr_(expr), lhs_(lhs), rhs_(rhs)
{}
Expr expr() const { return expr_; }
WasmAstExpr* lhs() const { return lhs_; }
WasmAstExpr* rhs() const { return rhs_; }
};
class WasmAstConversionOperator final : public WasmAstExpr
{
Expr expr_;
WasmAstExpr* op_;
public:
static const WasmAstExprKind Kind = WasmAstExprKind::ConversionOperator;
explicit WasmAstConversionOperator(Expr expr, WasmAstExpr* op)
: WasmAstExpr(Kind),
expr_(expr), op_(op)
{}
Expr expr() const { return expr_; }
WasmAstExpr* op() const { return op_; }
};
} // end anonymous namespace
/*****************************************************************************/
// wasm text token stream
namespace {
class WasmToken
{
public:
enum FloatLiteralKind
{
HexNumber,
DecNumber,
Infinity,
NaN
};
enum Kind
{
Align,
BinaryOpcode,
Block,
Br,
BrIf,
BrTable,
Call,
CallImport,
CallIndirect,
CloseParen,
ComparisonOpcode,
Const,
ConversionOpcode,
EndOfFile,
Equal,
Error,
Export,
Float,
Func,
GetLocal,
If,
IfElse,
Import,
Index,
UnsignedInteger,
SignedInteger,
Memory,
Load,
Local,
Loop,
Module,
Name,
Nop,
Offset,
OpenParen,
Param,
Result,
Return,
Segment,
SetLocal,
Store,
Table,
Text,
Type,
UnaryOpcode,
ValueType
};
private:
Kind kind_;
const char16_t* begin_;
const char16_t* end_;
union {
uint32_t index_;
uint64_t uint_;
int64_t sint_;
FloatLiteralKind floatLiteralKind_;
ValType valueType_;
Expr expr_;
} u;
public:
explicit WasmToken() = default;
WasmToken(Kind kind, const char16_t* begin, const char16_t* end)
: kind_(kind),
begin_(begin),
end_(end)
{
MOZ_ASSERT(kind_ != Error);
MOZ_ASSERT((kind == EndOfFile) == (begin == end));
}
explicit WasmToken(uint32_t index, const char16_t* begin, const char16_t* end)
: kind_(Index),
begin_(begin),
end_(end)
{
MOZ_ASSERT(begin != end);
u.index_ = index;
}
explicit WasmToken(uint64_t uint, const char16_t* begin, const char16_t* end)
: kind_(UnsignedInteger),
begin_(begin),
end_(end)
{
MOZ_ASSERT(begin != end);
u.uint_ = uint;
}
explicit WasmToken(int64_t sint, const char16_t* begin, const char16_t* end)
: kind_(SignedInteger),
begin_(begin),
end_(end)
{
MOZ_ASSERT(begin != end);
u.sint_ = sint;
}
explicit WasmToken(FloatLiteralKind floatLiteralKind,
const char16_t* begin, const char16_t* end)
: kind_(Float),
begin_(begin),
end_(end)
{
MOZ_ASSERT(begin != end);
u.floatLiteralKind_ = floatLiteralKind;
}
explicit WasmToken(Kind kind, ValType valueType, const char16_t* begin, const char16_t* end)
: kind_(kind),
begin_(begin),
end_(end)
{
MOZ_ASSERT(begin != end);
MOZ_ASSERT(kind_ == ValueType || kind_ == Const);
u.valueType_ = valueType;
}
explicit WasmToken(Kind kind, Expr expr, const char16_t* begin, const char16_t* end)
: kind_(kind),
begin_(begin),
end_(end)
{
MOZ_ASSERT(begin != end);
MOZ_ASSERT(kind_ == UnaryOpcode || kind_ == BinaryOpcode || kind_ == ComparisonOpcode ||
kind_ == ConversionOpcode || kind_ == Load || kind_ == Store);
u.expr_ = expr;
}
explicit WasmToken(const char16_t* begin)
: kind_(Error),
begin_(begin),
end_(begin)
{}
Kind kind() const {
return kind_;
}
const char16_t* begin() const {
return begin_;
}
const char16_t* end() const {
return end_;
}
WasmName text() const {
MOZ_ASSERT(kind_ == Text);
MOZ_ASSERT(begin_[0] == '"');
MOZ_ASSERT(end_[-1] == '"');
MOZ_ASSERT(end_ - begin_ >= 2);
return WasmName(begin_ + 1, end_ - begin_ - 2);
}
WasmName name() const {
return WasmName(begin_, end_ - begin_);
}
uint32_t index() const {
MOZ_ASSERT(kind_ == Index);
return u.index_;
}
uint64_t uint() const {
MOZ_ASSERT(kind_ == UnsignedInteger);
return u.uint_;
}
int64_t sint() const {
MOZ_ASSERT(kind_ == SignedInteger);
return u.sint_;
}
FloatLiteralKind floatLiteralKind() const {
MOZ_ASSERT(kind_ == Float);
return u.floatLiteralKind_;
}
ValType valueType() const {
MOZ_ASSERT(kind_ == ValueType || kind_ == Const);
return u.valueType_;
}
Expr expr() const {
MOZ_ASSERT(kind_ == UnaryOpcode || kind_ == BinaryOpcode || kind_ == ComparisonOpcode ||
kind_ == ConversionOpcode || kind_ == Load || kind_ == Store);
return u.expr_;
}
};
} // end anonymous namespace
static bool
IsWasmNewLine(char16_t c)
{
return c == '\n';
}
static bool
IsWasmSpace(char16_t c)
{
switch (c) {
case ' ':
case '\n':
case '\r':
case '\t':
case '\v':
case '\f':
return true;
default:
return false;
}
}
static bool
IsWasmDigit(char16_t c)
{
return c >= '0' && c <= '9';
}
static bool
IsWasmLetter(char16_t c)
{
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
}
static bool
IsNameAfterDollar(char16_t c)
{
return IsWasmLetter(c) || IsWasmDigit(c) || c == '_' || c == '$' || c == '-';
}
static bool
IsHexDigit(char c, uint8_t* value)
{
if (c >= '0' && c <= '9') {
*value = c - '0';
return true;
}
if (c >= 'a' && c <= 'f') {
*value = 10 + (c - 'a');
return true;
}
if (c >= 'A' && c <= 'F') {
*value = 10 + (c - 'A');
return true;
}
return false;
}
static WasmToken
LexHexFloatLiteral(const char16_t* begin, const char16_t* end, const char16_t** curp)
{
const char16_t* cur = begin;
if (cur != end && (*cur == '-' || *cur == '+'))
cur++;
MOZ_ASSERT(cur != end && *cur == '0');
cur++;
MOZ_ASSERT(cur != end && *cur == 'x');
cur++;
uint8_t digit;
while (cur != end && IsHexDigit(*cur, &digit))
cur++;
if (cur != end && *cur == '.')
cur++;
while (cur != end && IsHexDigit(*cur, &digit))
cur++;
if (cur != end && *cur == 'p') {
cur++;
if (cur != end && (*cur == '-' || *cur == '+'))
cur++;
while (cur != end && IsWasmDigit(*cur))
cur++;
}
*curp = cur;
return WasmToken(WasmToken::HexNumber, begin, cur);
}
static WasmToken
LexDecFloatLiteral(const char16_t* begin, const char16_t* end, const char16_t** curp)
{
const char16_t* cur = begin;
if (cur != end && (*cur == '-' || *cur == '+'))
cur++;
while (cur != end && IsWasmDigit(*cur))
cur++;
if (cur != end && *cur == '.')
cur++;
while (cur != end && IsWasmDigit(*cur))
cur++;
if (cur != end && *cur == 'e') {
cur++;
if (cur != end && (*cur == '-' || *cur == '+'))
cur++;
while (cur != end && IsWasmDigit(*cur))
cur++;
}
*curp = cur;
return WasmToken(WasmToken::DecNumber, begin, cur);
}
static bool
ConsumeTextByte(const char16_t** curp, const char16_t* end, uint8_t *byte = nullptr)
{
const char16_t*& cur = *curp;
MOZ_ASSERT(cur != end);
if (*cur != '\\') {
if (byte)
*byte = *cur;
cur++;
return true;
}
if (++cur == end)
return false;
uint8_t u8;
switch (*cur) {
case 'n': u8 = '\n'; break;
case 't': u8 = '\t'; break;
case '\\': u8 = '\\'; break;
case '\"': u8 = '\"'; break;
case '\'': u8 = '\''; break;
default: {
uint8_t highNibble;
if (!IsHexDigit(*cur, &highNibble))
return false;
if (++cur == end)
return false;
uint8_t lowNibble;
if (!IsHexDigit(*cur, &lowNibble))
return false;
u8 = lowNibble | (highNibble << 4);
break;
}
}
if (byte)
*byte = u8;
cur++;
return true;
}
namespace {
class WasmTokenStream
{
static const uint32_t LookaheadSize = 2;
const char16_t* cur_;
const char16_t* const end_;
const char16_t* lineStart_;
unsigned line_;
uint32_t lookaheadIndex_;
uint32_t lookaheadDepth_;
WasmToken lookahead_[LookaheadSize];
bool consume(const char16_t* match) {
const char16_t* p = cur_;
for (; *match; p++, match++) {
if (p == end_ || *p != *match)
return false;
}
cur_ = p;
return true;
}
WasmToken fail(const char16_t* begin) const {
return WasmToken(begin);
}
WasmToken nan(const char16_t* begin);
WasmToken literal(const char16_t* begin);
WasmToken next();
public:
WasmTokenStream(const char16_t* text, UniqueChars* error)
: cur_(text),
end_(text + js_strlen(text)),
lineStart_(text),
line_(1),
lookaheadIndex_(0),
lookaheadDepth_(0)
{}
void generateError(WasmToken token, UniqueChars* error) {
unsigned column = token.begin() - lineStart_ + 1;
error->reset(JS_smprintf("parsing wasm text at %u:%u", line_, column));
}
WasmToken peek() {
if (!lookaheadDepth_) {
lookahead_[lookaheadIndex_] = next();
lookaheadDepth_ = 1;
}
return lookahead_[lookaheadIndex_];
}
WasmToken get() {
static_assert(LookaheadSize == 2, "can just flip");
if (lookaheadDepth_) {
lookaheadDepth_--;
WasmToken ret = lookahead_[lookaheadIndex_];
lookaheadIndex_ ^= 1;
return ret;
}
return next();
}
void unget(WasmToken token) {
static_assert(LookaheadSize == 2, "can just flip");
lookaheadDepth_++;
lookaheadIndex_ ^= 1;
lookahead_[lookaheadIndex_] = token;
}
// Helpers:
bool getIf(WasmToken::Kind kind, WasmToken* token) {
if (peek().kind() == kind) {
*token = get();
return true;
}
return false;
}
bool getIf(WasmToken::Kind kind) {
WasmToken token;
if (getIf(kind, &token))
return true;
return false;
}
WasmName getIfName() {
WasmToken token;
if (getIf(WasmToken::Name, &token))
return token.name();
return WasmName();
}
bool getIfRef(WasmRef* ref) {
WasmToken token = peek();
if (token.kind() == WasmToken::Name || token.kind() == WasmToken::Index)
return matchRef(ref, nullptr);
return false;
}
bool match(WasmToken::Kind expect, WasmToken* token, UniqueChars* error) {
*token = get();
if (token->kind() == expect)
return true;
generateError(*token, error);
return false;
}
bool match(WasmToken::Kind expect, UniqueChars* error) {
WasmToken token;
return match(expect, &token, error);
}
bool matchRef(WasmRef* ref, UniqueChars* error) {
WasmToken token = get();
switch (token.kind()) {
case WasmToken::Name:
*ref = WasmRef(token.name(), WasmNoIndex);
break;
case WasmToken::Index:
*ref = WasmRef(WasmName(), token.index());
break;
default:
generateError(token, error);
return false;
}
return true;
}
};
} // end anonymous namespace
WasmToken
WasmTokenStream::nan(const char16_t* begin)
{
if (consume(MOZ_UTF16(":"))) {
if (!consume(MOZ_UTF16("0x")))
return fail(begin);
uint8_t digit;
while (cur_ != end_ && IsHexDigit(*cur_, &digit))
cur_++;
}
return WasmToken(WasmToken::NaN, begin, cur_);
}
WasmToken
WasmTokenStream::literal(const char16_t* begin)
{
CheckedInt<uint64_t> u = 0;
if (consume(MOZ_UTF16("0x"))) {
if (cur_ == end_)
return fail(begin);
do {
if (*cur_ == '.' || *cur_ == 'p')
return LexHexFloatLiteral(begin, end_, &cur_);
uint8_t digit;
if (!IsHexDigit(*cur_, &digit))
break;
u *= 16;
u += digit;
if (!u.isValid())
return LexHexFloatLiteral(begin, end_, &cur_);
cur_++;
} while (cur_ != end_);
if (*begin == '-') {
uint64_t value = u.value();
if (value > uint64_t(INT64_MIN))
return LexHexFloatLiteral(begin, end_, &cur_);
value = -value;
return WasmToken(int64_t(value), begin, cur_);
}
} else {
while (cur_ != end_) {
if (*cur_ == '.' || *cur_ == 'e')
return LexDecFloatLiteral(begin, end_, &cur_);
if (!IsWasmDigit(*cur_))
break;
u *= 10;
u += *cur_ - '0';
if (!u.isValid())
return LexDecFloatLiteral(begin, end_, &cur_);
cur_++;
}
if (*begin == '-') {
uint64_t value = u.value();
if (value > uint64_t(INT64_MIN))
return LexDecFloatLiteral(begin, end_, &cur_);
value = -value;
return WasmToken(int64_t(value), begin, cur_);
}
}
CheckedInt<uint32_t> index = u.value();
if (index.isValid())
return WasmToken(index.value(), begin, cur_);
return WasmToken(u.value(), begin, cur_);
}
WasmToken
WasmTokenStream::next()
{
while (cur_ != end_ && IsWasmSpace(*cur_)) {
if (IsWasmNewLine(*cur_++)) {
lineStart_ = cur_;
line_++;
}
}
if (cur_ == end_)
return WasmToken(WasmToken::EndOfFile, cur_, cur_);
const char16_t* begin = cur_;
switch (*begin) {
case '"':
cur_++;
while (true) {
if (cur_ == end_)
return fail(begin);
if (*cur_ == '"')
break;
if (!ConsumeTextByte(&cur_, end_))
return fail(begin);
}
cur_++;
return WasmToken(WasmToken::Text, begin, cur_);
case '$':
cur_++;
while (cur_ != end_ && IsNameAfterDollar(*cur_))
cur_++;
return WasmToken(WasmToken::Name, begin, cur_);
case '(':
cur_++;
return WasmToken(WasmToken::OpenParen, begin, cur_);
case ')':
cur_++;
return WasmToken(WasmToken::CloseParen, begin, cur_);
case '=':
cur_++;
return WasmToken(WasmToken::Equal, begin, cur_);
case '+': case '-':
cur_++;
if (consume(MOZ_UTF16("infinity")))
return WasmToken(WasmToken::Infinity, begin, cur_);
if (consume(MOZ_UTF16("nan")))
return nan(begin);
if (!IsWasmDigit(*cur_))
break;
MOZ_FALLTHROUGH;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
return literal(begin);
case 'a':
if (consume(MOZ_UTF16("align")))
return WasmToken(WasmToken::Align, begin, cur_);
break;
case 'b':
if (consume(MOZ_UTF16("block")))
return WasmToken(WasmToken::Block, begin, cur_);
if (consume(MOZ_UTF16("br"))) {
if (consume(MOZ_UTF16("_table")))
return WasmToken(WasmToken::BrTable, begin, cur_);
if (consume(MOZ_UTF16("_if")))
return WasmToken(WasmToken::BrIf, begin, cur_);
return WasmToken(WasmToken::Br, begin, cur_);
}
break;
case 'c':
if (consume(MOZ_UTF16("call"))) {
if (consume(MOZ_UTF16("_indirect")))
return WasmToken(WasmToken::CallIndirect, begin, cur_);
if (consume(MOZ_UTF16("_import")))
return WasmToken(WasmToken::CallImport, begin, cur_);
return WasmToken(WasmToken::Call, begin, cur_);
}
break;
case 'e':
if (consume(MOZ_UTF16("export")))
return WasmToken(WasmToken::Export, begin, cur_);
break;
case 'f':
if (consume(MOZ_UTF16("func")))
return WasmToken(WasmToken::Func, begin, cur_);
if (consume(MOZ_UTF16("f32"))) {
if (!consume(MOZ_UTF16(".")))
return WasmToken(WasmToken::ValueType, ValType::F32, begin, cur_);
switch (*cur_) {
case 'a':
if (consume(MOZ_UTF16("abs")))
return WasmToken(WasmToken::UnaryOpcode, Expr::F32Abs, begin, cur_);
if (consume(MOZ_UTF16("add")))
return WasmToken(WasmToken::BinaryOpcode, Expr::F32Add, begin, cur_);
break;
case 'c':
if (consume(MOZ_UTF16("ceil")))
return WasmToken(WasmToken::UnaryOpcode, Expr::F32Ceil, begin, cur_);
if (consume(MOZ_UTF16("const")))
return WasmToken(WasmToken::Const, ValType::F32, begin, cur_);
if (consume(MOZ_UTF16("convert_s/i32")))
return WasmToken(WasmToken::ConversionOpcode, Expr::F32ConvertSI32,
begin, cur_);
if (consume(MOZ_UTF16("convert_u/i32")))
return WasmToken(WasmToken::ConversionOpcode, Expr::F32ConvertUI32,
begin, cur_);
if (consume(MOZ_UTF16("copysign")))
return WasmToken(WasmToken::BinaryOpcode, Expr::F32CopySign, begin, cur_);
break;
case 'd':
if (consume(MOZ_UTF16("demote/f64")))
return WasmToken(WasmToken::ConversionOpcode, Expr::F32DemoteF64,
begin, cur_);
if (consume(MOZ_UTF16("div")))
return WasmToken(WasmToken::BinaryOpcode, Expr::F32Div, begin, cur_);
break;
case 'e':
if (consume(MOZ_UTF16("eq")))
return WasmToken(WasmToken::ComparisonOpcode, Expr::F32Eq, begin, cur_);
break;
case 'f':
if (consume(MOZ_UTF16("floor")))
return WasmToken(WasmToken::UnaryOpcode, Expr::F32Floor, begin, cur_);
break;
case 'g':
if (consume(MOZ_UTF16("ge")))
return WasmToken(WasmToken::ComparisonOpcode, Expr::F32Ge, begin, cur_);
if (consume(MOZ_UTF16("gt")))
return WasmToken(WasmToken::ComparisonOpcode, Expr::F32Gt, begin, cur_);
break;
case 'l':
if (consume(MOZ_UTF16("le")))
return WasmToken(WasmToken::ComparisonOpcode, Expr::F32Le, begin, cur_);
if (consume(MOZ_UTF16("lt")))
return WasmToken(WasmToken::ComparisonOpcode, Expr::F32Lt, begin, cur_);
if (consume(MOZ_UTF16("load")))
return WasmToken(WasmToken::Load, Expr::F32LoadMem, begin, cur_);
break;
case 'm':
if (consume(MOZ_UTF16("max")))
return WasmToken(WasmToken::BinaryOpcode, Expr::F32Max, begin, cur_);
if (consume(MOZ_UTF16("min")))
return WasmToken(WasmToken::BinaryOpcode, Expr::F32Min, begin, cur_);
if (consume(MOZ_UTF16("mul")))
return WasmToken(WasmToken::BinaryOpcode, Expr::F32Mul, begin, cur_);
break;
case 'n':
if (consume(MOZ_UTF16("nearest")))
return WasmToken(WasmToken::UnaryOpcode, Expr::F32Nearest, begin, cur_);
if (consume(MOZ_UTF16("neg")))
return WasmToken(WasmToken::UnaryOpcode, Expr::F32Neg, begin, cur_);
if (consume(MOZ_UTF16("ne")))
return WasmToken(WasmToken::ComparisonOpcode, Expr::F32Ne, begin, cur_);
break;
case 'r':
if (consume(MOZ_UTF16("reinterpret/i32")))
return WasmToken(WasmToken::ConversionOpcode, Expr::F32ReinterpretI32,
begin, cur_);
break;
case 's':
if (consume(MOZ_UTF16("sqrt")))
return WasmToken(WasmToken::UnaryOpcode, Expr::F32Sqrt, begin, cur_);
if (consume(MOZ_UTF16("sub")))
return WasmToken(WasmToken::BinaryOpcode, Expr::F32Sub, begin, cur_);
if (consume(MOZ_UTF16("store")))
return WasmToken(WasmToken::Store, Expr::F32StoreMem, begin, cur_);
break;
case 't':
if (consume(MOZ_UTF16("trunc")))
return WasmToken(WasmToken::UnaryOpcode, Expr::F32Trunc, begin, cur_);
break;
}
break;
}
if (consume(MOZ_UTF16("f64"))) {
if (!consume(MOZ_UTF16(".")))
return WasmToken(WasmToken::ValueType, ValType::F64, begin, cur_);
switch (*cur_) {
case 'a':
if (consume(MOZ_UTF16("abs")))
return WasmToken(WasmToken::UnaryOpcode, Expr::F64Abs, begin, cur_);
if (consume(MOZ_UTF16("add")))
return WasmToken(WasmToken::BinaryOpcode, Expr::F64Add, begin, cur_);
break;
case 'c':
if (consume(MOZ_UTF16("ceil")))
return WasmToken(WasmToken::UnaryOpcode, Expr::F64Ceil, begin, cur_);
if (consume(MOZ_UTF16("const")))
return WasmToken(WasmToken::Const, ValType::F64, begin, cur_);
if (consume(MOZ_UTF16("convert_s/i32")))
return WasmToken(WasmToken::ConversionOpcode, Expr::F64ConvertSI32,
begin, cur_);
if (consume(MOZ_UTF16("convert_u/i32")))
return WasmToken(WasmToken::ConversionOpcode, Expr::F64ConvertUI32,
begin, cur_);
if (consume(MOZ_UTF16("copysign")))
return WasmToken(WasmToken::BinaryOpcode, Expr::F64CopySign, begin, cur_);
break;
case 'd':
if (consume(MOZ_UTF16("div")))
return WasmToken(WasmToken::BinaryOpcode, Expr::F64Div, begin, cur_);
break;
case 'e':
if (consume(MOZ_UTF16("eq")))
return WasmToken(WasmToken::ComparisonOpcode, Expr::F64Eq, begin, cur_);
break;
case 'f':
if (consume(MOZ_UTF16("floor")))
return WasmToken(WasmToken::UnaryOpcode, Expr::F64Floor, begin, cur_);
break;
case 'g':
if (consume(MOZ_UTF16("ge")))
return WasmToken(WasmToken::ComparisonOpcode, Expr::F64Ge, begin, cur_);
if (consume(MOZ_UTF16("gt")))
return WasmToken(WasmToken::ComparisonOpcode, Expr::F64Gt, begin, cur_);
break;
case 'l':
if (consume(MOZ_UTF16("le")))
return WasmToken(WasmToken::ComparisonOpcode, Expr::F64Le, begin, cur_);
if (consume(MOZ_UTF16("lt")))
return WasmToken(WasmToken::ComparisonOpcode, Expr::F64Lt, begin, cur_);
if (consume(MOZ_UTF16("load")))
return WasmToken(WasmToken::Load, Expr::F64LoadMem, begin, cur_);
break;
case 'm':
if (consume(MOZ_UTF16("max")))
return WasmToken(WasmToken::BinaryOpcode, Expr::F64Max, begin, cur_);
if (consume(MOZ_UTF16("min")))
return WasmToken(WasmToken::BinaryOpcode, Expr::F64Min, begin, cur_);
if (consume(MOZ_UTF16("mul")))
return WasmToken(WasmToken::BinaryOpcode, Expr::F64Mul, begin, cur_);
break;
case 'n':
if (consume(MOZ_UTF16("nearest")))
return WasmToken(WasmToken::UnaryOpcode, Expr::F64Nearest, begin, cur_);
if (consume(MOZ_UTF16("neg")))
return WasmToken(WasmToken::UnaryOpcode, Expr::F64Neg, begin, cur_);
if (consume(MOZ_UTF16("ne")))
return WasmToken(WasmToken::ComparisonOpcode, Expr::F64Ne, begin, cur_);
break;
case 'p':
if (consume(MOZ_UTF16("promote/f32")))
return WasmToken(WasmToken::ConversionOpcode, Expr::F64PromoteF32,
begin, cur_);
break;
case 's':
if (consume(MOZ_UTF16("sqrt")))
return WasmToken(WasmToken::UnaryOpcode, Expr::F64Sqrt, begin, cur_);
if (consume(MOZ_UTF16("sub")))
return WasmToken(WasmToken::BinaryOpcode, Expr::F64Sub, begin, cur_);
if (consume(MOZ_UTF16("store")))
return WasmToken(WasmToken::Store, Expr::F64StoreMem, begin, cur_);
break;
case 't':
if (consume(MOZ_UTF16("trunc")))
return WasmToken(WasmToken::UnaryOpcode, Expr::F64Trunc, begin, cur_);
break;
}
break;
}
break;
case 'g':
if (consume(MOZ_UTF16("get_local")))
return WasmToken(WasmToken::GetLocal, begin, cur_);
break;
case 'i':
if (consume(MOZ_UTF16("i32"))) {
if (!consume(MOZ_UTF16(".")))
return WasmToken(WasmToken::ValueType, ValType::I32, begin, cur_);
switch (*cur_) {
case 'a':
if (consume(MOZ_UTF16("add")))
return WasmToken(WasmToken::BinaryOpcode, Expr::I32Add, begin, cur_);
if (consume(MOZ_UTF16("and")))
return WasmToken(WasmToken::BinaryOpcode, Expr::I32And, begin, cur_);
break;
case 'c':
if (consume(MOZ_UTF16("const")))
return WasmToken(WasmToken::Const, ValType::I32, begin, cur_);
if (consume(MOZ_UTF16("clz")))
return WasmToken(WasmToken::UnaryOpcode, Expr::I32Clz, begin, cur_);
if (consume(MOZ_UTF16("ctz")))
return WasmToken(WasmToken::UnaryOpcode, Expr::I32Ctz, begin, cur_);
break;
case 'd':
if (consume(MOZ_UTF16("div_s")))
return WasmToken(WasmToken::BinaryOpcode, Expr::I32DivS, begin, cur_);
if (consume(MOZ_UTF16("div_u")))
return WasmToken(WasmToken::BinaryOpcode, Expr::I32DivU, begin, cur_);
break;
case 'e':
if (consume(MOZ_UTF16("eq")))
return WasmToken(WasmToken::ComparisonOpcode, Expr::I32Eq, begin, cur_);
break;
case 'g':
if (consume(MOZ_UTF16("ge_s")))
return WasmToken(WasmToken::ComparisonOpcode, Expr::I32GeS, begin, cur_);
if (consume(MOZ_UTF16("ge_u")))
return WasmToken(WasmToken::ComparisonOpcode, Expr::I32GeU, begin, cur_);
if (consume(MOZ_UTF16("gt_s")))
return WasmToken(WasmToken::ComparisonOpcode, Expr::I32GtS, begin, cur_);
if (consume(MOZ_UTF16("gt_u")))
return WasmToken(WasmToken::ComparisonOpcode, Expr::I32GtU, begin, cur_);
break;
case 'l':
if (consume(MOZ_UTF16("le_s")))
return WasmToken(WasmToken::ComparisonOpcode, Expr::I32LeS, begin, cur_);
if (consume(MOZ_UTF16("le_u")))
return WasmToken(WasmToken::ComparisonOpcode, Expr::I32LeU, begin, cur_);
if (consume(MOZ_UTF16("lt_s")))
return WasmToken(WasmToken::ComparisonOpcode, Expr::I32LtS, begin, cur_);
if (consume(MOZ_UTF16("lt_u")))
return WasmToken(WasmToken::ComparisonOpcode, Expr::I32LtU, begin, cur_);
if (consume(MOZ_UTF16("load"))) {
if (IsWasmSpace(*cur_))
return WasmToken(WasmToken::Load, Expr::I32LoadMem, begin, cur_);
if (consume(MOZ_UTF16("8_s")))
return WasmToken(WasmToken::Load, Expr::I32LoadMem8S, begin, cur_);
if (consume(MOZ_UTF16("8_u")))
return WasmToken(WasmToken::Load, Expr::I32LoadMem8U, begin, cur_);
if (consume(MOZ_UTF16("16_s")))
return WasmToken(WasmToken::Load, Expr::I32LoadMem16S, begin, cur_);
if (consume(MOZ_UTF16("16_u")))
return WasmToken(WasmToken::Load, Expr::I32LoadMem16U, begin, cur_);
break;
}
break;
case 'm':
if (consume(MOZ_UTF16("mul")))
return WasmToken(WasmToken::BinaryOpcode, Expr::I32Mul, begin, cur_);
break;
case 'n':
if (consume(MOZ_UTF16("ne")))
return WasmToken(WasmToken::ComparisonOpcode, Expr::I32Ne, begin, cur_);
break;
case 'o':
if (consume(MOZ_UTF16("or")))
return WasmToken(WasmToken::BinaryOpcode, Expr::I32Or, begin, cur_);
break;
case 'p':
if (consume(MOZ_UTF16("popcnt")))
return WasmToken(WasmToken::UnaryOpcode, Expr::I32Popcnt, begin, cur_);
break;
case 'r':
if (consume(MOZ_UTF16("reinterpret/f32")))
return WasmToken(WasmToken::UnaryOpcode, Expr::I32ReinterpretF32,
begin, cur_);
if (consume(MOZ_UTF16("rem_s")))
return WasmToken(WasmToken::BinaryOpcode, Expr::I32RemS, begin, cur_);
if (consume(MOZ_UTF16("rem_u")))
return WasmToken(WasmToken::BinaryOpcode, Expr::I32RemU, begin, cur_);
break;
case 's':
if (consume(MOZ_UTF16("sub")))
return WasmToken(WasmToken::BinaryOpcode, Expr::I32Sub, begin, cur_);
if (consume(MOZ_UTF16("shl")))
return WasmToken(WasmToken::BinaryOpcode, Expr::I32Shl, begin, cur_);
if (consume(MOZ_UTF16("shr_s")))
return WasmToken(WasmToken::BinaryOpcode, Expr::I32ShrS, begin, cur_);
if (consume(MOZ_UTF16("shr_u")))
return WasmToken(WasmToken::BinaryOpcode, Expr::I32ShrU, begin, cur_);
if (consume(MOZ_UTF16("store"))) {
if (IsWasmSpace(*cur_))
return WasmToken(WasmToken::Store, Expr::I32StoreMem, begin, cur_);
if (consume(MOZ_UTF16("8")))
return WasmToken(WasmToken::Store, Expr::I32StoreMem8, begin, cur_);
if (consume(MOZ_UTF16("16")))
return WasmToken(WasmToken::Store, Expr::I32StoreMem16, begin, cur_);
break;
}
break;
case 't':
if (consume(MOZ_UTF16("trunc_s/f32")))
return WasmToken(WasmToken::ConversionOpcode, Expr::I32TruncSF32,
begin, cur_);
if (consume(MOZ_UTF16("trunc_s/f64")))
return WasmToken(WasmToken::ConversionOpcode, Expr::I32TruncSF64,
begin, cur_);
if (consume(MOZ_UTF16("trunc_u/f32")))
return WasmToken(WasmToken::ConversionOpcode, Expr::I32TruncUF32,
begin, cur_);
if (consume(MOZ_UTF16("trunc_u/f64")))
return WasmToken(WasmToken::ConversionOpcode, Expr::I32TruncUF64,
begin, cur_);
break;
case 'w':
if (consume(MOZ_UTF16("wrap/i64")))
return WasmToken(WasmToken::ConversionOpcode, Expr::I32WrapI64,
begin, cur_);
break;
case 'x':
if (consume(MOZ_UTF16("xor")))
return WasmToken(WasmToken::BinaryOpcode, Expr::I32Xor, begin, cur_);
break;
}
break;
}
if (consume(MOZ_UTF16("i64"))) {
if (!consume(MOZ_UTF16(".")))
return WasmToken(WasmToken::ValueType, ValType::I64, begin, cur_);
switch (*cur_) {
case 'a':
if (consume(MOZ_UTF16("add")))
return WasmToken(WasmToken::BinaryOpcode, Expr::I64Add, begin, cur_);
if (consume(MOZ_UTF16("and")))
return WasmToken(WasmToken::BinaryOpcode, Expr::I64And, begin, cur_);
break;
case 'c':
if (consume(MOZ_UTF16("const")))
return WasmToken(WasmToken::Const, ValType::I64, begin, cur_);
if (consume(MOZ_UTF16("clz")))
return WasmToken(WasmToken::UnaryOpcode, Expr::I64Clz, begin, cur_);
if (consume(MOZ_UTF16("ctz")))
return WasmToken(WasmToken::UnaryOpcode, Expr::I64Ctz, begin, cur_);
break;
case 'd':
if (consume(MOZ_UTF16("div_s")))
return WasmToken(WasmToken::BinaryOpcode, Expr::I64DivS, begin, cur_);
if (consume(MOZ_UTF16("div_u")))
return WasmToken(WasmToken::BinaryOpcode, Expr::I64DivU, begin, cur_);
break;
case 'e':
if (consume(MOZ_UTF16("eq")))
return WasmToken(WasmToken::ComparisonOpcode, Expr::I64Eq, begin, cur_);
if (consume(MOZ_UTF16("extend_s/i32")))
return WasmToken(WasmToken::ConversionOpcode, Expr::I64ExtendSI32,
begin, cur_);
if (consume(MOZ_UTF16("extend_u/i32")))
return WasmToken(WasmToken::ConversionOpcode, Expr::I64ExtendUI32,
begin, cur_);
break;
case 'g':
if (consume(MOZ_UTF16("ge_s")))
return WasmToken(WasmToken::ComparisonOpcode, Expr::I64GeS, begin, cur_);
if (consume(MOZ_UTF16("ge_u")))
return WasmToken(WasmToken::ComparisonOpcode, Expr::I64GeU, begin, cur_);
if (consume(MOZ_UTF16("gt_s")))
return WasmToken(WasmToken::ComparisonOpcode, Expr::I64GtS, begin, cur_);
if (consume(MOZ_UTF16("gt_u")))
return WasmToken(WasmToken::ComparisonOpcode, Expr::I64GtU, begin, cur_);
break;
case 'l':
if (consume(MOZ_UTF16("le_s")))
return WasmToken(WasmToken::ComparisonOpcode, Expr::I64LeS, begin, cur_);
if (consume(MOZ_UTF16("le_u")))
return WasmToken(WasmToken::ComparisonOpcode, Expr::I64LeU, begin, cur_);
if (consume(MOZ_UTF16("lt_s")))
return WasmToken(WasmToken::ComparisonOpcode, Expr::I64LtS, begin, cur_);
if (consume(MOZ_UTF16("lt_u")))
return WasmToken(WasmToken::ComparisonOpcode, Expr::I64LtU, begin, cur_);
if (consume(MOZ_UTF16("load"))) {
if (IsWasmSpace(*cur_))
return WasmToken(WasmToken::Load, Expr::I64LoadMem, begin, cur_);
if (consume(MOZ_UTF16("8_s")))
return WasmToken(WasmToken::Load, Expr::I64LoadMem8S, begin, cur_);
if (consume(MOZ_UTF16("8_u")))
return WasmToken(WasmToken::Load, Expr::I64LoadMem8U, begin, cur_);
if (consume(MOZ_UTF16("16_s")))
return WasmToken(WasmToken::Load, Expr::I64LoadMem16S, begin, cur_);
if (consume(MOZ_UTF16("16_u")))
return WasmToken(WasmToken::Load, Expr::I64LoadMem16U, begin, cur_);
if (consume(MOZ_UTF16("32_s")))
return WasmToken(WasmToken::Load, Expr::I64LoadMem32S, begin, cur_);
if (consume(MOZ_UTF16("32_u")))
return WasmToken(WasmToken::Load, Expr::I64LoadMem32U, begin, cur_);
break;
}
break;
case 'm':
if (consume(MOZ_UTF16("mul")))
return WasmToken(WasmToken::BinaryOpcode, Expr::I64Mul, begin, cur_);
break;
case 'n':
if (consume(MOZ_UTF16("ne")))
return WasmToken(WasmToken::ComparisonOpcode, Expr::I64Ne, begin, cur_);
break;
case 'o':
if (consume(MOZ_UTF16("or")))
return WasmToken(WasmToken::BinaryOpcode, Expr::I64Or, begin, cur_);
break;
case 'p':
if (consume(MOZ_UTF16("popcnt")))
return WasmToken(WasmToken::UnaryOpcode, Expr::I64Popcnt, begin, cur_);
break;
case 'r':
if (consume(MOZ_UTF16("reinterpret/f64")))
return WasmToken(WasmToken::UnaryOpcode, Expr::I64ReinterpretF64,
begin, cur_);
if (consume(MOZ_UTF16("rem_s")))
return WasmToken(WasmToken::BinaryOpcode, Expr::I64RemS, begin, cur_);
if (consume(MOZ_UTF16("rem_u")))
return WasmToken(WasmToken::BinaryOpcode, Expr::I64RemU, begin, cur_);
break;
case 's':
if (consume(MOZ_UTF16("sub")))
return WasmToken(WasmToken::BinaryOpcode, Expr::I64Sub, begin, cur_);
if (consume(MOZ_UTF16("shl")))
return WasmToken(WasmToken::BinaryOpcode, Expr::I64Shl, begin, cur_);
if (consume(MOZ_UTF16("shr_s")))
return WasmToken(WasmToken::BinaryOpcode, Expr::I64ShrS, begin, cur_);
if (consume(MOZ_UTF16("shr_u")))
return WasmToken(WasmToken::BinaryOpcode, Expr::I64ShrU, begin, cur_);
if (consume(MOZ_UTF16("store"))) {
if (IsWasmSpace(*cur_))
return WasmToken(WasmToken::Store, Expr::I64StoreMem, begin, cur_);
if (consume(MOZ_UTF16("8")))
return WasmToken(WasmToken::Store, Expr::I64StoreMem8, begin, cur_);
if (consume(MOZ_UTF16("16")))
return WasmToken(WasmToken::Store, Expr::I64StoreMem16, begin, cur_);
if (consume(MOZ_UTF16("32")))
return WasmToken(WasmToken::Store, Expr::I64StoreMem32, begin, cur_);
break;
}
break;
case 't':
if (consume(MOZ_UTF16("trunc_s/f32")))
return WasmToken(WasmToken::ConversionOpcode, Expr::I64TruncSF32,
begin, cur_);
if (consume(MOZ_UTF16("trunc_s/f64")))
return WasmToken(WasmToken::ConversionOpcode, Expr::I64TruncSF64,
begin, cur_);
if (consume(MOZ_UTF16("trunc_u/f32")))
return WasmToken(WasmToken::ConversionOpcode, Expr::I64TruncUF32,
begin, cur_);
if (consume(MOZ_UTF16("trunc_u/f64")))
return WasmToken(WasmToken::ConversionOpcode, Expr::I64TruncUF64,
begin, cur_);
break;
case 'x':
if (consume(MOZ_UTF16("xor")))
return WasmToken(WasmToken::BinaryOpcode, Expr::I64Xor, begin, cur_);
break;
}
break;
}
if (consume(MOZ_UTF16("import")))
return WasmToken(WasmToken::Import, begin, cur_);
if (consume(MOZ_UTF16("infinity")))
return WasmToken(WasmToken::Infinity, begin, cur_);
if (consume(MOZ_UTF16("if"))) {
if (consume(MOZ_UTF16("_else")))
return WasmToken(WasmToken::IfElse, begin, cur_);
return WasmToken(WasmToken::If, begin, cur_);
}
break;
case 'l':
if (consume(MOZ_UTF16("local")))
return WasmToken(WasmToken::Local, begin, cur_);
if (consume(MOZ_UTF16("loop")))
return WasmToken(WasmToken::Loop, begin, cur_);
break;
case 'm':
if (consume(MOZ_UTF16("module")))
return WasmToken(WasmToken::Module, begin, cur_);
if (consume(MOZ_UTF16("memory")))
return WasmToken(WasmToken::Memory, begin, cur_);
break;
case 'n':
if (consume(MOZ_UTF16("nan")))
return nan(begin);
if (consume(MOZ_UTF16("nop")))
return WasmToken(WasmToken::Nop, begin, cur_);
break;
case 'o':
if (consume(MOZ_UTF16("offset")))
return WasmToken(WasmToken::Offset, begin, cur_);
break;
case 'p':
if (consume(MOZ_UTF16("param")))
return WasmToken(WasmToken::Param, begin, cur_);
break;
case 'r':
if (consume(MOZ_UTF16("result")))
return WasmToken(WasmToken::Result, begin, cur_);
if (consume(MOZ_UTF16("return")))
return WasmToken(WasmToken::Return, begin, cur_);
break;
case 's':
if (consume(MOZ_UTF16("set_local")))
return WasmToken(WasmToken::SetLocal, begin, cur_);
if (consume(MOZ_UTF16("segment")))
return WasmToken(WasmToken::Segment, begin, cur_);
break;
case 't':
if (consume(MOZ_UTF16("table")))
return WasmToken(WasmToken::Table, begin, cur_);
if (consume(MOZ_UTF16("type")))
return WasmToken(WasmToken::Type, begin, cur_);
break;
default:
break;
}
return fail(begin);
}
/*****************************************************************************/
// wasm text format parser
namespace {
struct WasmParseContext
{
WasmTokenStream ts;
LifoAlloc& lifo;
UniqueChars* error;
DtoaState* dtoaState;
WasmParseContext(const char16_t* text, LifoAlloc& lifo, UniqueChars* error)
: ts(text, error),
lifo(lifo),
error(error),
dtoaState(NewDtoaState())
{}
bool fail(const char* message) {
error->reset(JS_smprintf(message));
return false;
}
~WasmParseContext() {
DestroyDtoaState(dtoaState);
}
};
} // end anonymous namespace
static WasmAstExpr*
ParseExprInsideParens(WasmParseContext& c);
static WasmAstExpr*
ParseExpr(WasmParseContext& c)
{
if (!c.ts.match(WasmToken::OpenParen, c.error))
return nullptr;
WasmAstExpr* expr = ParseExprInsideParens(c);
if (!expr)
return nullptr;
if (!c.ts.match(WasmToken::CloseParen, c.error))
return nullptr;
return expr;
}
static WasmAstBlock*
ParseBlock(WasmParseContext& c, Expr expr)
{
WasmAstExprVector exprs(c.lifo);
WasmName breakName = c.ts.getIfName();
WasmName continueName;
if (expr == Expr::Loop)
continueName = c.ts.getIfName();
while (c.ts.getIf(WasmToken::OpenParen)) {
WasmAstExpr* expr = ParseExprInsideParens(c);
if (!expr || !exprs.append(expr))
return nullptr;
if (!c.ts.match(WasmToken::CloseParen, c.error))
return nullptr;
}
return new(c.lifo) WasmAstBlock(expr, breakName, continueName, Move(exprs));
}
static WasmAstBranch*
ParseBranch(WasmParseContext& c, Expr expr)
{
MOZ_ASSERT(expr == Expr::Br || expr == Expr::BrIf);
WasmRef target;
if (!c.ts.matchRef(&target, c.error))
return nullptr;
WasmAstExpr* cond = nullptr;
if (expr == Expr::BrIf) {
cond = ParseExpr(c);
if (!cond)
return nullptr;
}
return new(c.lifo) WasmAstBranch(expr, cond, target);
}
static bool
ParseArgs(WasmParseContext& c, WasmAstExprVector* args)
{
while (c.ts.getIf(WasmToken::OpenParen)) {
WasmAstExpr* arg = ParseExprInsideParens(c);
if (!arg || !args->append(arg))
return false;
if (!c.ts.match(WasmToken::CloseParen, c.error))
return false;
}
return true;
}
static WasmAstCall*
ParseCall(WasmParseContext& c, Expr expr)
{
MOZ_ASSERT(expr == Expr::Call || expr == Expr::CallImport);
WasmRef func;
if (!c.ts.matchRef(&func, c.error))
return nullptr;
WasmAstExprVector args(c.lifo);
if (!ParseArgs(c, &args))
return nullptr;
return new(c.lifo) WasmAstCall(expr, func, Move(args));
}
static WasmAstCallIndirect*
ParseCallIndirect(WasmParseContext& c)
{
WasmRef sig;
if (!c.ts.matchRef(&sig, c.error))
return nullptr;
WasmAstExpr* index = ParseExpr(c);
if (!index)
return nullptr;
WasmAstExprVector args(c.lifo);
if (!ParseArgs(c, &args))
return nullptr;
return new(c.lifo) WasmAstCallIndirect(sig, index, Move(args));
}
static uint_fast8_t
CountLeadingZeroes4(uint8_t x)
{
MOZ_ASSERT((x & -0x10) == 0);
return CountLeadingZeroes32(x) - 28;
}
template <typename T>
static T
ushl(T lhs, unsigned rhs)
{
return rhs < sizeof(T) * CHAR_BIT ? (lhs << rhs) : 0;
}
template <typename T>
static T
ushr(T lhs, unsigned rhs)
{
return rhs < sizeof(T) * CHAR_BIT ? (lhs >> rhs) : 0;
}
template<typename Float>
static bool
ParseNaNLiteral(const char16_t* cur, const char16_t* end, Float* result)
{
MOZ_ALWAYS_TRUE(*cur++ == 'n' && *cur++ == 'a' && *cur++ == 'n');
typedef FloatingPoint<Float> Traits;
typedef typename Traits::Bits Bits;
if (cur != end) {
MOZ_ALWAYS_TRUE(*cur++ == ':' && *cur++ == '0' && *cur++ == 'x');
if (cur == end)
return false;
CheckedInt<Bits> u = 0;
do {
uint8_t digit;
MOZ_ALWAYS_TRUE(IsHexDigit(*cur, &digit));
u *= 16;
u += digit;
cur++;
} while (cur != end);
if (!u.isValid())
return false;
Bits value = u.value();
if ((value & ~Traits::kSignificandBits) != 0)
return false;
// NaN payloads must contain at least one set bit.
if (value == 0)
return false;
*result = SpecificNaN<Float>(0, value);
} else {
// Produce the spec's default NaN.
*result = SpecificNaN<Float>(0, (Traits::kSignificandBits + 1) >> 1);
}
return true;
}
template <typename Float>
static bool
ParseHexFloatLiteral(const char16_t* cur, const char16_t* end, Float* result)
{
MOZ_ALWAYS_TRUE(*cur++ == '0' && *cur++ == 'x');
typedef FloatingPoint<Float> Traits;
typedef typename Traits::Bits Bits;
static const unsigned numBits = sizeof(Float) * CHAR_BIT;
static const Bits allOnes = ~Bits(0);
static const Bits mostSignificantBit = ~(allOnes >> 1);
// Significand part.
Bits significand = 0;
CheckedInt<int32_t> exponent = 0;
bool sawFirstNonZero = false;
bool discardedExtraNonZero = false;
const char16_t* dot = nullptr;
int significandPos;
for (; cur != end; cur++) {
if (*cur == '.') {
MOZ_ASSERT(!dot);
dot = cur;
continue;
}
uint8_t digit;
if (!IsHexDigit(*cur, &digit))
break;
if (!sawFirstNonZero) {
if (digit == 0)
continue;
// We've located the first non-zero digit; we can now determine the
// initial exponent. If we're after the dot, count the number of
// zeros from the dot to here, and adjust for the number of leading
// zero bits in the digit. Set up significandPos to put the first
// nonzero at the most significant bit.
int_fast8_t lz = CountLeadingZeroes4(digit);
ptrdiff_t zeroAdjustValue = !dot ? 1 : dot + 1 - cur;
CheckedInt<ptrdiff_t> zeroAdjust = zeroAdjustValue;
zeroAdjust *= 4;
zeroAdjust -= lz + 1;
if (!zeroAdjust.isValid())
return false;
exponent = zeroAdjust.value();
significandPos = numBits - (4 - lz);
sawFirstNonZero = true;
} else {
// We've already seen a non-zero; just take 4 more bits.
if (!dot)
exponent += 4;
if (significandPos > -4)
significandPos -= 4;
}
// Or the newly parsed digit into significand at signicandPos.
if (significandPos >= 0) {
significand |= ushl(Bits(digit), significandPos);
} else if (significandPos > -4) {
significand |= ushr(digit, 4 - significandPos);
discardedExtraNonZero = (digit & ~ushl(allOnes, 4 - significandPos)) != 0;
} else if (digit != 0) {
discardedExtraNonZero = true;
}
}
// Exponent part.
if (cur != end) {
MOZ_ALWAYS_TRUE(*cur++ == 'p');
bool isNegated = false;
if (cur != end && (*cur == '-' || *cur == '+'))
isNegated = *cur++ == '-';
CheckedInt<int32_t> parsedExponent = 0;
while (cur != end && IsWasmDigit(*cur))
parsedExponent = parsedExponent * 10 + (*cur++ - '0');
if (isNegated)
parsedExponent = -parsedExponent;
exponent += parsedExponent;
}
MOZ_ASSERT(cur == end);
if (!exponent.isValid())
return false;
// Create preliminary exponent and significand encodings of the results.
Bits encodedExponent, encodedSignificand, discardedSignificandBits;
if (significand == 0) {
// Zero. The exponent is encoded non-biased.
encodedExponent = 0;
encodedSignificand = 0;
discardedSignificandBits = 0;
} else if (MOZ_UNLIKELY(exponent.value() <= int32_t(-Traits::kExponentBias))) {
// Underflow to subnormal or zero.
encodedExponent = 0;
encodedSignificand = ushr(significand,
numBits - Traits::kExponentShift -
exponent.value() - Traits::kExponentBias);
discardedSignificandBits =
ushl(significand,
Traits::kExponentShift + exponent.value() + Traits::kExponentBias);
} else if (MOZ_LIKELY(exponent.value() <= int32_t(Traits::kExponentBias))) {
// Normal (non-zero). The significand's leading 1 is encoded implicitly.
encodedExponent = (Bits(exponent.value()) + Traits::kExponentBias) <<
Traits::kExponentShift;
MOZ_ASSERT(significand & mostSignificantBit);
encodedSignificand = ushr(significand, numBits - Traits::kExponentShift - 1) &
Traits::kSignificandBits;
discardedSignificandBits = ushl(significand, Traits::kExponentShift + 1);
} else {
// Overflow to infinity.
encodedExponent = Traits::kExponentBits;
encodedSignificand = 0;
discardedSignificandBits = 0;
}
MOZ_ASSERT((encodedExponent & ~Traits::kExponentBits) == 0);
MOZ_ASSERT((encodedSignificand & ~Traits::kSignificandBits) == 0);
MOZ_ASSERT(encodedExponent != Traits::kExponentBits || encodedSignificand == 0);
Bits bits = encodedExponent | encodedSignificand;
// Apply rounding. If this overflows the significand, it carries into the
// exponent bit according to the magic of the IEEE 754 encoding.
bits += (discardedSignificandBits & mostSignificantBit) &&
((discardedSignificandBits & ~mostSignificantBit) ||
discardedExtraNonZero ||
// ties to even
(encodedSignificand & 1));
*result = BitwiseCast<Float>(bits);
return true;
}
template <typename Float>
static bool
ParseFloatLiteral(WasmParseContext& c, WasmToken token, Float* result)
{
switch (token.kind()) {
case WasmToken::Index:
*result = token.index();
return true;
case WasmToken::UnsignedInteger:
*result = token.uint();
return true;
case WasmToken::SignedInteger:
*result = token.sint();
return true;
case WasmToken::Float:
break;
default:
c.ts.generateError(token, c.error);
return false;
}
const char16_t* begin = token.begin();
const char16_t* end = token.end();
const char16_t* cur = begin;
bool isNegated = false;
if (*cur == '-' || *cur == '+')
isNegated = *cur++ == '-';
switch (token.floatLiteralKind()) {
case WasmToken::Infinity:
*result = PositiveInfinity<Float>();
break;
case WasmToken::NaN:
if (!ParseNaNLiteral(cur, end, result)) {
c.ts.generateError(token, c.error);
return false;
}
break;
case WasmToken::HexNumber:
if (!ParseHexFloatLiteral(cur, end, result)) {
c.ts.generateError(token, c.error);
return false;
}
break;
case WasmToken::DecNumber: {
// Call into JS' strtod. Tokenization has already required that the
// string is well-behaved.
LifoAlloc::Mark mark = c.lifo.mark();
char* buffer = c.lifo.newArray<char>(end - begin + 1);
if (!buffer)
return false;
for (ptrdiff_t i = 0; i < end - cur; ++i)
buffer[i] = char(cur[i]);
char* strtod_end;
int err;
Float d = (Float)js_strtod_harder(c.dtoaState, buffer, &strtod_end, &err);
if (err != 0 || strtod_end == buffer) {
c.lifo.release(mark);
c.ts.generateError(token, c.error);
return false;
}
c.lifo.release(mark);
*result = d;
break;
}
}
if (isNegated)
*result = -*result;
return true;
}
static WasmAstConst*
ParseConst(WasmParseContext& c, WasmToken constToken)
{
WasmToken val = c.ts.get();
switch (constToken.valueType()) {
case ValType::I32: {
switch (val.kind()) {
case WasmToken::Index:
return new(c.lifo) WasmAstConst(Val(val.index()));
case WasmToken::SignedInteger: {
CheckedInt<int32_t> sint = val.sint();
if (!sint.isValid())
break;
return new(c.lifo) WasmAstConst(Val(uint32_t(sint.value())));
}
default:
break;
}
break;
}
case ValType::I64: {
switch (val.kind()) {
case WasmToken::Index:
return new(c.lifo) WasmAstConst(Val(uint64_t(val.index())));
case WasmToken::UnsignedInteger:
return new(c.lifo) WasmAstConst(Val(val.uint()));
case WasmToken::SignedInteger:
return new(c.lifo) WasmAstConst(Val(uint64_t(val.sint())));
default:
break;
}
break;
}
case ValType::F32: {
float result;
if (!ParseFloatLiteral(c, val, &result))
break;
return new(c.lifo) WasmAstConst(Val(result));
}
case ValType::F64: {
double result;
if (!ParseFloatLiteral(c, val, &result))
break;
return new(c.lifo) WasmAstConst(Val(result));
}
default:
break;
}
c.ts.generateError(constToken, c.error);
return nullptr;
}
static WasmAstGetLocal*
ParseGetLocal(WasmParseContext& c)
{
WasmRef local;
if (!c.ts.matchRef(&local, c.error))
return nullptr;
return new(c.lifo) WasmAstGetLocal(local);
}
static WasmAstSetLocal*
ParseSetLocal(WasmParseContext& c)
{
WasmRef local;
if (!c.ts.matchRef(&local, c.error))
return nullptr;
WasmAstExpr* value = ParseExpr(c);
if (!value)
return nullptr;
return new(c.lifo) WasmAstSetLocal(local, *value);
}
static WasmAstReturn*
ParseReturn(WasmParseContext& c)
{
WasmAstExpr* maybeExpr = nullptr;
if (c.ts.peek().kind() != WasmToken::CloseParen) {
maybeExpr = ParseExpr(c);
if (!maybeExpr)
return nullptr;
}
return new(c.lifo) WasmAstReturn(maybeExpr);
}
static WasmAstUnaryOperator*
ParseUnaryOperator(WasmParseContext& c, Expr expr)
{
WasmAstExpr* op = ParseExpr(c);
if (!op)
return nullptr;
return new(c.lifo) WasmAstUnaryOperator(expr, op);
}
static WasmAstBinaryOperator*
ParseBinaryOperator(WasmParseContext& c, Expr expr)
{
WasmAstExpr* lhs = ParseExpr(c);
if (!lhs)
return nullptr;
WasmAstExpr* rhs = ParseExpr(c);
if (!rhs)
return nullptr;
return new(c.lifo) WasmAstBinaryOperator(expr, lhs, rhs);
}
static WasmAstComparisonOperator*
ParseComparisonOperator(WasmParseContext& c, Expr expr)
{
WasmAstExpr* lhs = ParseExpr(c);
if (!lhs)
return nullptr;
WasmAstExpr* rhs = ParseExpr(c);
if (!rhs)
return nullptr;
return new(c.lifo) WasmAstComparisonOperator(expr, lhs, rhs);
}
static WasmAstConversionOperator*
ParseConversionOperator(WasmParseContext& c, Expr expr)
{
WasmAstExpr* op = ParseExpr(c);
if (!op)
return nullptr;
return new(c.lifo) WasmAstConversionOperator(expr, op);
}
static WasmAstIfElse*
ParseIfElse(WasmParseContext& c, Expr expr)
{
WasmAstExpr* cond = ParseExpr(c);
if (!cond)
return nullptr;
WasmAstExpr* ifBody = ParseExpr(c);
if (!ifBody)
return nullptr;
WasmAstExpr* elseBody = nullptr;
if (expr == Expr::IfElse) {
elseBody = ParseExpr(c);
if (!elseBody)
return nullptr;
}
return new(c.lifo) WasmAstIfElse(expr, cond, ifBody, elseBody);
}
static bool
ParseLoadStoreAddress(WasmParseContext& c, int32_t* offset, int32_t* align, WasmAstExpr** base)
{
*offset = 0;
if (c.ts.getIf(WasmToken::Offset)) {
if (!c.ts.match(WasmToken::Equal, c.error))
return false;
WasmToken val = c.ts.get();
switch (val.kind()) {
case WasmToken::Index:
*offset = val.index();
break;
default:
c.ts.generateError(val, c.error);
return false;
}
}
*align = 0;
if (c.ts.getIf(WasmToken::Align)) {
if (!c.ts.match(WasmToken::Equal, c.error))
return false;
WasmToken val = c.ts.get();
switch (val.kind()) {
case WasmToken::Index:
*align = val.index();
break;
default:
c.ts.generateError(val, c.error);
return false;
}
}
*base = ParseExpr(c);
if (!*base)
return false;
return true;
}
static WasmAstLoad*
ParseLoad(WasmParseContext& c, Expr expr)
{
int32_t offset;
int32_t align;
WasmAstExpr* base;
if (!ParseLoadStoreAddress(c, &offset, &align, &base))
return nullptr;
if (align == 0) {
switch (expr) {
case Expr::I32LoadMem8S:
case Expr::I32LoadMem8U:
case Expr::I64LoadMem8S:
case Expr::I64LoadMem8U:
align = 1;
break;
case Expr::I32LoadMem16S:
case Expr::I32LoadMem16U:
case Expr::I64LoadMem16S:
case Expr::I64LoadMem16U:
align = 2;
break;
case Expr::I32LoadMem:
case Expr::F32LoadMem:
case Expr::I64LoadMem32S:
case Expr::I64LoadMem32U:
align = 4;
break;
case Expr::I64LoadMem:
case Expr::F64LoadMem:
align = 8;
break;
default:
MOZ_CRASH("Bad load expr");
}
}
return new(c.lifo) WasmAstLoad(expr, WasmAstLoadStoreAddress(base, offset, align));
}
static WasmAstStore*
ParseStore(WasmParseContext& c, Expr expr)
{
int32_t offset;
int32_t align;
WasmAstExpr* base;
if (!ParseLoadStoreAddress(c, &offset, &align, &base))
return nullptr;
if (align == 0) {
switch (expr) {
case Expr::I32StoreMem8:
case Expr::I64StoreMem8:
align = 1;
break;
case Expr::I32StoreMem16:
case Expr::I64StoreMem16:
align = 2;
break;
case Expr::I32StoreMem:
case Expr::F32StoreMem:
case Expr::I64StoreMem32:
align = 4;
break;
case Expr::I64StoreMem:
case Expr::F64StoreMem:
align = 8;
break;
default:
MOZ_CRASH("Bad load expr");
}
}
WasmAstExpr* value = ParseExpr(c);
if (!value)
return nullptr;
return new(c.lifo) WasmAstStore(expr, WasmAstLoadStoreAddress(base, offset, align), value);
}
static WasmAstBranchTable*
ParseBranchTable(WasmParseContext& c)
{
WasmAstExpr* index = ParseExpr(c);
if (!index)
return nullptr;
if (!c.ts.match(WasmToken::OpenParen, c.error))
return nullptr;
if (!c.ts.match(WasmToken::Table, c.error))
return nullptr;
WasmRefVector table(c.lifo);
while (c.ts.getIf(WasmToken::OpenParen)) {
if (!c.ts.match(WasmToken::Br, c.error))
return nullptr;
WasmRef target;
if (!c.ts.matchRef(&target, c.error))
return nullptr;
if (!table.append(target))
return nullptr;
if (!c.ts.match(WasmToken::CloseParen, c.error))
return nullptr;
}
if (!c.ts.match(WasmToken::CloseParen, c.error))
return nullptr;
if (!c.ts.match(WasmToken::OpenParen, c.error))
return nullptr;
if (!c.ts.match(WasmToken::Br, c.error))
return nullptr;
WasmRef def;
if (!c.ts.matchRef(&def, c.error))
return nullptr;
if (!c.ts.match(WasmToken::CloseParen, c.error))
return nullptr;
return new(c.lifo) WasmAstBranchTable(*index, def, Move(table));
}
static WasmAstExpr*
ParseExprInsideParens(WasmParseContext& c)
{
WasmToken token = c.ts.get();
switch (token.kind()) {
case WasmToken::Nop:
return new(c.lifo) WasmAstNop;
case WasmToken::BinaryOpcode:
return ParseBinaryOperator(c, token.expr());
case WasmToken::Block:
return ParseBlock(c, Expr::Block);
case WasmToken::Br:
return ParseBranch(c, Expr::Br);
case WasmToken::BrIf:
return ParseBranch(c, Expr::BrIf);
case WasmToken::BrTable:
return ParseBranchTable(c);
case WasmToken::Call:
return ParseCall(c, Expr::Call);
case WasmToken::CallImport:
return ParseCall(c, Expr::CallImport);
case WasmToken::CallIndirect:
return ParseCallIndirect(c);
case WasmToken::ComparisonOpcode:
return ParseComparisonOperator(c, token.expr());
case WasmToken::Const:
return ParseConst(c, token);
case WasmToken::ConversionOpcode:
return ParseConversionOperator(c, token.expr());
case WasmToken::If:
return ParseIfElse(c, Expr::If);
case WasmToken::IfElse:
return ParseIfElse(c, Expr::IfElse);
case WasmToken::GetLocal:
return ParseGetLocal(c);
case WasmToken::Load:
return ParseLoad(c, token.expr());
case WasmToken::Loop:
return ParseBlock(c, Expr::Loop);
case WasmToken::Return:
return ParseReturn(c);
case WasmToken::SetLocal:
return ParseSetLocal(c);
case WasmToken::Store:
return ParseStore(c, token.expr());
case WasmToken::UnaryOpcode:
return ParseUnaryOperator(c, token.expr());
default:
c.ts.generateError(token, c.error);
return nullptr;
}
}
static bool
ParseValueType(WasmParseContext& c, WasmAstValTypeVector* vec)
{
WasmToken token;
return c.ts.match(WasmToken::ValueType, &token, c.error) &&
vec->append(token.valueType());
}
static bool
ParseValueTypeList(WasmParseContext& c, WasmAstValTypeVector* vec)
{
WasmToken token;
while (c.ts.getIf(WasmToken::ValueType, &token)) {
if (!vec->append(token.valueType()))
return false;
}
return true;
}
static bool
ParseResult(WasmParseContext& c, ExprType* result)
{
if (*result != ExprType::Void) {
c.ts.generateError(c.ts.peek(), c.error);
return false;
}
WasmToken token;
if (!c.ts.match(WasmToken::ValueType, &token, c.error))
return false;
*result = ToExprType(token.valueType());
return true;
}
static bool
ParseLocal(WasmParseContext& c, WasmNameVector* locals, WasmAstValTypeVector* localTypes)
{
return locals->append(c.ts.getIfName()) &&
ParseValueType(c, localTypes);
}
static bool
ParseParam(WasmParseContext& c, WasmNameVector* locals, WasmAstValTypeVector* args)
{
if (c.ts.peek().kind() == WasmToken::Name)
return ParseLocal(c, locals, args);
return locals->append(WasmName()) &&
ParseValueTypeList(c, args);
}
static WasmAstFunc*
ParseFunc(WasmParseContext& c, WasmAstModule* module)
{
WasmAstValTypeVector vars(c.lifo);
WasmAstValTypeVector args(c.lifo);
WasmNameVector locals(c.lifo);
WasmName funcName = c.ts.getIfName();
WasmRef sig;
WasmToken openParen;
if (c.ts.getIf(WasmToken::OpenParen, &openParen)) {
if (c.ts.getIf(WasmToken::Type)) {
if (!c.ts.matchRef(&sig, c.error))
return nullptr;
if (!c.ts.match(WasmToken::CloseParen, c.error))
return nullptr;
} else {
c.ts.unget(openParen);
}
}
WasmAstExprVector body(c.lifo);
ExprType result = ExprType::Void;
while (c.ts.getIf(WasmToken::OpenParen)) {
WasmToken token = c.ts.get();
switch (token.kind()) {
case WasmToken::Local:
if (!ParseLocal(c, &locals, &vars))
return nullptr;
break;
case WasmToken::Param:
if (!vars.empty()) {
c.ts.generateError(token, c.error);
return nullptr;
}
if (!ParseParam(c, &locals, &args))
return nullptr;
break;
case WasmToken::Result:
if (!ParseResult(c, &result))
return nullptr;
break;
default:
c.ts.unget(token);
WasmAstExpr* expr = ParseExprInsideParens(c);
if (!expr || !body.append(expr))
return nullptr;
break;
}
if (!c.ts.match(WasmToken::CloseParen, c.error))
return nullptr;
}
if (sig.isInvalid()) {
uint32_t sigIndex;
if (!module->declare(WasmAstSig(Move(args), result), &sigIndex))
return nullptr;
sig.setIndex(sigIndex);
}
return new(c.lifo) WasmAstFunc(funcName, sig, Move(vars), Move(locals), Move(body));
}
static bool
ParseFuncType(WasmParseContext& c, WasmAstSig* sig)
{
WasmAstValTypeVector args(c.lifo);
ExprType result = ExprType::Void;
while (c.ts.getIf(WasmToken::OpenParen)) {
WasmToken token = c.ts.get();
switch (token.kind()) {
case WasmToken::Param:
if (!ParseValueTypeList(c, &args))
return false;
break;
case WasmToken::Result:
if (!ParseResult(c, &result))
return false;
break;
default:
c.ts.generateError(token, c.error);
return false;
}
if (!c.ts.match(WasmToken::CloseParen, c.error))
return false;
}
*sig = WasmAstSig(Move(args), result);
return true;
}
static WasmAstSig*
ParseTypeDef(WasmParseContext& c)
{
WasmName name = c.ts.getIfName();
if (!c.ts.match(WasmToken::OpenParen, c.error))
return nullptr;
if (!c.ts.match(WasmToken::Func, c.error))
return nullptr;
WasmAstSig sig(c.lifo);
if (!ParseFuncType(c, &sig))
return nullptr;
if (!c.ts.match(WasmToken::CloseParen, c.error))
return nullptr;
return new(c.lifo) WasmAstSig(name, Move(sig));
}
static WasmAstSegment*
ParseSegment(WasmParseContext& c)
{
if (!c.ts.match(WasmToken::Segment, c.error))
return nullptr;
WasmToken dstOffset;
if (!c.ts.match(WasmToken::Index, &dstOffset, c.error))
return nullptr;
WasmToken text;
if (!c.ts.match(WasmToken::Text, &text, c.error))
return nullptr;
return new(c.lifo) WasmAstSegment(dstOffset.index(), text.text());
}
static WasmAstMemory*
ParseMemory(WasmParseContext& c)
{
WasmToken initialSize;
if (!c.ts.match(WasmToken::Index, &initialSize, c.error))
return nullptr;
Maybe<uint32_t> maxSize;
WasmToken token;
if (c.ts.getIf(WasmToken::Index, &token))
maxSize.emplace(token.index());
WasmAstSegmentVector segments(c.lifo);
while (c.ts.getIf(WasmToken::OpenParen)) {
WasmAstSegment* segment = ParseSegment(c);
if (!segment || !segments.append(segment))
return nullptr;
if (!c.ts.match(WasmToken::CloseParen, c.error))
return nullptr;
}
return new(c.lifo) WasmAstMemory(initialSize.index(), maxSize, Move(segments));
}
static WasmAstImport*
ParseImport(WasmParseContext& c, WasmAstModule* module)
{
WasmName name = c.ts.getIfName();
WasmToken moduleName;
if (!c.ts.match(WasmToken::Text, &moduleName, c.error))
return nullptr;
WasmToken funcName;
if (!c.ts.match(WasmToken::Text, &funcName, c.error))
return nullptr;
WasmAstSig sig(c.lifo);
if (!ParseFuncType(c, &sig))
return nullptr;
uint32_t sigIndex;
if (!module->declare(Move(sig), &sigIndex))
return nullptr;
return new(c.lifo) WasmAstImport(name, moduleName.text(), funcName.text(), sigIndex);
}
static WasmAstExport*
ParseExport(WasmParseContext& c)
{
WasmToken name;
if (!c.ts.match(WasmToken::Text, &name, c.error))
return nullptr;
WasmToken exportee = c.ts.get();
switch (exportee.kind()) {
case WasmToken::Index:
return new(c.lifo) WasmAstExport(name.text(), WasmRef(WasmName(), exportee.index()));
case WasmToken::Name:
return new(c.lifo) WasmAstExport(name.text(), WasmRef(exportee.name(), WasmNoIndex));
case WasmToken::Memory:
if (name.text() != WasmName(MOZ_UTF16("memory"), 6)) {
c.ts.generateError(exportee, c.error);
return nullptr;
}
return new(c.lifo) WasmAstExport(name.text());
default:
break;
}
c.ts.generateError(exportee, c.error);
return nullptr;
}
static WasmAstTable*
ParseTable(WasmParseContext& c)
{
WasmAstTableElemVector elems(c.lifo);
WasmRef elem;
while (c.ts.getIfRef(&elem)) {
if (!elems.append(elem))
return nullptr;
}
return new(c.lifo) WasmAstTable(Move(elems));
}
static WasmAstModule*
ParseModule(const char16_t* text, LifoAlloc& lifo, UniqueChars* error)
{
WasmParseContext c(text, lifo, error);
if (!c.ts.match(WasmToken::OpenParen, c.error))
return nullptr;
if (!c.ts.match(WasmToken::Module, c.error))
return nullptr;
auto module = new(c.lifo) WasmAstModule(c.lifo);
if (!module || !module->init())
return nullptr;
while (c.ts.getIf(WasmToken::OpenParen)) {
WasmToken section = c.ts.get();
switch (section.kind()) {
case WasmToken::Type: {
WasmAstSig* sig = ParseTypeDef(c);
if (!sig || !module->append(sig))
return nullptr;
break;
}
case WasmToken::Memory: {
WasmAstMemory* memory = ParseMemory(c);
if (!memory)
return nullptr;
if (!module->setMemory(memory)) {
c.ts.generateError(section, c.error);
return nullptr;
}
break;
}
case WasmToken::Import: {
WasmAstImport* imp = ParseImport(c, module);
if (!imp || !module->append(imp))
return nullptr;
break;
}
case WasmToken::Export: {
WasmAstExport* exp = ParseExport(c);
if (!exp || !module->append(exp))
return nullptr;
break;
}
case WasmToken::Table: {
WasmAstTable* table = ParseTable(c);
if (!table)
return nullptr;
if (!module->initTable(table)) {
c.ts.generateError(section, c.error);
return nullptr;
}
break;
}
case WasmToken::Func: {
WasmAstFunc* func = ParseFunc(c, module);
if (!func || !module->append(func))
return nullptr;
break;
}
default:
c.ts.generateError(section, c.error);
return nullptr;
}
if (!c.ts.match(WasmToken::CloseParen, c.error))
return nullptr;
}
if (!c.ts.match(WasmToken::CloseParen, c.error))
return nullptr;
if (!c.ts.match(WasmToken::EndOfFile, c.error))
return nullptr;
return module;
}
/*****************************************************************************/
// wasm name resolution
namespace {
class Resolver
{
UniqueChars* error_;
WasmNameMap varMap_;
WasmNameMap sigMap_;
WasmNameMap funcMap_;
WasmNameMap importMap_;
WasmNameVector targetStack_;
bool registerName(WasmNameMap& map, WasmName name, size_t index) {
WasmNameMap::AddPtr p = map.lookupForAdd(name);
if (!p) {
if (!map.add(p, name, index))
return false;
} else {
return false;
}
return true;
}
bool resolveName(WasmNameMap& map, WasmName name, size_t* index) {
WasmNameMap::Ptr p = map.lookup(name);
if (p) {
*index = p->value();
return true;
}
return false;
}
bool resolveRef(WasmNameMap& map, WasmRef& ref) {
WasmNameMap::Ptr p = map.lookup(ref.name());
if (p) {
ref.setIndex(p->value());
return true;
}
return false;
}
public:
explicit Resolver(LifoAlloc& lifo, UniqueChars* error)
: error_(error),
varMap_(lifo),
sigMap_(lifo),
funcMap_(lifo),
importMap_(lifo),
targetStack_(lifo)
{}
bool init() {
return sigMap_.init() && funcMap_.init() && importMap_.init() && varMap_.init();
}
void beginFunc() {
varMap_.clear();
MOZ_ASSERT(targetStack_.empty());
}
bool registerSigName(WasmName name, size_t index) {
return name.empty() || registerName(sigMap_, name, index);
}
bool registerFuncName(WasmName name, size_t index) {
return name.empty() || registerName(funcMap_, name, index);
}
bool registerImportName(WasmName name, size_t index) {
return name.empty() || registerName(importMap_, name, index);
}
bool registerVarName(WasmName name, size_t index) {
return name.empty() || registerName(varMap_, name, index);
}
bool pushTarget(WasmName name) {
return targetStack_.append(name);
}
void popTarget(WasmName name) {
MOZ_ASSERT(targetStack_.back() == name);
targetStack_.popBack();
}
bool resolveSigRef(WasmRef& ref) {
return ref.name().empty() || resolveRef(sigMap_, ref);
}
bool resolveFuncRef(WasmRef& ref) {
return ref.name().empty() || resolveRef(funcMap_, ref);
}
bool resolveImportRef(WasmRef& ref) {
return ref.name().empty() || resolveRef(importMap_, ref);
}
bool resolveVarRef(WasmRef& ref) {
return ref.name().empty() || resolveRef(varMap_, ref);
}
bool resolveTarget(WasmRef& ref) {
for (size_t i = targetStack_.length(); i > 0; i--) {
uint32_t targetIndex = i - 1;
if (targetStack_[targetIndex] == ref.name()) {
ref.setIndex(targetIndex);
return true;
}
}
return false;
}
bool fail(const char*message) {
error_->reset(JS_smprintf("%s", message));
return false;
}
};
} // end anonymous namespace
static bool
ResolveExpr(Resolver& r, WasmAstExpr& expr);
static bool
ResolveBlock(Resolver& r, WasmAstBlock& b)
{
if (!r.pushTarget(b.breakName()))
return false;
if (b.expr() == Expr::Loop) {
if (!r.pushTarget(b.continueName()))
return false;
}
size_t numExprs = b.exprs().length();
for (size_t i = 0; i < numExprs; i++) {
if (!ResolveExpr(r, *b.exprs()[i]))
return false;
}
if (b.expr() == Expr::Loop)
r.popTarget(b.continueName());
r.popTarget(b.breakName());
return true;
}
static bool
ResolveBranch(Resolver& r, WasmAstBranch& br)
{
if (!br.target().name().empty() && !r.resolveTarget(br.target()))
return r.fail("label not found");
if (br.expr() == Expr::BrIf) {
if (!ResolveExpr(r, br.cond()))
return false;
}
return true;
}
static bool
ResolveArgs(Resolver& r, const WasmAstExprVector& args)
{
for (WasmAstExpr* arg : args) {
if (!ResolveExpr(r, *arg))
return false;
}
return true;
}
static bool
ResolveCall(Resolver& r, WasmAstCall& c)
{
if (!ResolveArgs(r, c.args()))
return false;
if (c.expr() == Expr::Call) {
if (!r.resolveFuncRef(c.func()))
return r.fail("function not found");
} else {
MOZ_ASSERT(c.expr() == Expr::CallImport);
if (!r.resolveImportRef(c.func()))
return r.fail("import not found");
}
return true;
}
static bool
ResolveCallIndirect(Resolver& r, WasmAstCallIndirect& c)
{
if (!ResolveExpr(r, *c.index()))
return false;
if (!ResolveArgs(r, c.args()))
return false;
if (!r.resolveSigRef(c.sig()))
return r.fail("signature not found");
return true;
}
static bool
ResolveGetLocal(Resolver& r, WasmAstGetLocal& gl)
{
if (!r.resolveVarRef(gl.local()))
return r.fail("local not found");
return true;
}
static bool
ResolveSetLocal(Resolver& r, WasmAstSetLocal& sl)
{
if (!ResolveExpr(r, sl.value()))
return false;
if (!r.resolveVarRef(sl.local()))
return r.fail("local not found");
return true;
}
static bool
ResolveUnaryOperator(Resolver& r, WasmAstUnaryOperator& b)
{
return ResolveExpr(r, *b.op());
}
static bool
ResolveBinaryOperator(Resolver& r, WasmAstBinaryOperator& b)
{
return ResolveExpr(r, *b.lhs()) &&
ResolveExpr(r, *b.rhs());
}
static bool
ResolveComparisonOperator(Resolver& r, WasmAstComparisonOperator& b)
{
return ResolveExpr(r, *b.lhs()) &&
ResolveExpr(r, *b.rhs());
}
static bool
ResolveConversionOperator(Resolver& r, WasmAstConversionOperator& b)
{
return ResolveExpr(r, *b.op());
}
static bool
ResolveIfElse(Resolver& r, WasmAstIfElse& ie)
{
return ResolveExpr(r, ie.cond()) &&
ResolveExpr(r, ie.ifBody()) &&
(!ie.hasElse() || ResolveExpr(r, ie.elseBody()));
}
static bool
ResolveLoadStoreAddress(Resolver& r, const WasmAstLoadStoreAddress &address)
{
return ResolveExpr(r, address.base());
}
static bool
ResolveLoad(Resolver& r, WasmAstLoad& l)
{
return ResolveLoadStoreAddress(r, l.address());
}
static bool
ResolveStore(Resolver& r, WasmAstStore& s)
{
return ResolveLoadStoreAddress(r, s.address()) &&
ResolveExpr(r, s.value());
}
static bool
ResolveReturn(Resolver& r, WasmAstReturn& ret)
{
return !ret.maybeExpr() || ResolveExpr(r, *ret.maybeExpr());
}
static bool
ResolveBranchTable(Resolver& r, WasmAstBranchTable& bt)
{
if (!bt.def().name().empty() && !r.resolveTarget(bt.def()))
return r.fail("switch default not found");
for (WasmRef& elem : bt.table()) {
if (!elem.name().empty() && !r.resolveTarget(elem))
return r.fail("switch element not found");
}
return ResolveExpr(r, bt.index());
}
static bool
ResolveExpr(Resolver& r, WasmAstExpr& expr)
{
switch (expr.kind()) {
case WasmAstExprKind::Nop:
return true;
case WasmAstExprKind::BinaryOperator:
return ResolveBinaryOperator(r, expr.as<WasmAstBinaryOperator>());
case WasmAstExprKind::Block:
return ResolveBlock(r, expr.as<WasmAstBlock>());
case WasmAstExprKind::Branch:
return ResolveBranch(r, expr.as<WasmAstBranch>());
case WasmAstExprKind::Call:
return ResolveCall(r, expr.as<WasmAstCall>());
case WasmAstExprKind::CallIndirect:
return ResolveCallIndirect(r, expr.as<WasmAstCallIndirect>());
case WasmAstExprKind::ComparisonOperator:
return ResolveComparisonOperator(r, expr.as<WasmAstComparisonOperator>());
case WasmAstExprKind::Const:
return true;
case WasmAstExprKind::ConversionOperator:
return ResolveConversionOperator(r, expr.as<WasmAstConversionOperator>());
case WasmAstExprKind::GetLocal:
return ResolveGetLocal(r, expr.as<WasmAstGetLocal>());
case WasmAstExprKind::IfElse:
return ResolveIfElse(r, expr.as<WasmAstIfElse>());
case WasmAstExprKind::Load:
return ResolveLoad(r, expr.as<WasmAstLoad>());
case WasmAstExprKind::Return:
return ResolveReturn(r, expr.as<WasmAstReturn>());
case WasmAstExprKind::SetLocal:
return ResolveSetLocal(r, expr.as<WasmAstSetLocal>());
case WasmAstExprKind::Store:
return ResolveStore(r, expr.as<WasmAstStore>());
case WasmAstExprKind::BranchTable:
return ResolveBranchTable(r, expr.as<WasmAstBranchTable>());
case WasmAstExprKind::UnaryOperator:
return ResolveUnaryOperator(r, expr.as<WasmAstUnaryOperator>());
}
MOZ_CRASH("Bad expr kind");
}
static bool
ResolveFunc(Resolver& r, WasmAstFunc& func)
{
r.beginFunc();
size_t numVars = func.locals().length();
for (size_t i = 0; i < numVars; i++) {
if (!r.registerVarName(func.locals()[i], i))
return r.fail("duplicate var");
}
for (WasmAstExpr* expr : func.body()) {
if (!ResolveExpr(r, *expr))
return false;
}
return true;
}
static bool
ResolveModule(LifoAlloc& lifo, WasmAstModule* module, UniqueChars* error)
{
Resolver r(lifo, error);
if (!r.init())
return false;
size_t numSigs = module->sigs().length();
for (size_t i = 0; i < numSigs; i++) {
WasmAstSig* sig = module->sigs()[i];
if (!r.registerSigName(sig->name(), i))
return r.fail("duplicate signature");
}
size_t numFuncs = module->funcs().length();
for (size_t i = 0; i < numFuncs; i++) {
WasmAstFunc* func = module->funcs()[i];
if (!r.resolveSigRef(func->sig()))
return r.fail("signature not found");
if (!r.registerFuncName(func->name(), i))
return r.fail("duplicate function");
}
if (module->maybeTable()) {
for (WasmRef& ref : module->maybeTable()->elems()) {
if (!r.resolveFuncRef(ref))
return r.fail("function not found");
}
}
size_t numImports = module->imports().length();
for (size_t i = 0; i < numImports; i++) {
WasmAstImport* imp = module->imports()[i];
if (!r.registerImportName(imp->name(), i))
return r.fail("duplicate import");
}
for (WasmAstExport* export_ : module->exports()) {
if (export_->kind() != WasmAstExportKind::Func)
continue;
if (!r.resolveFuncRef(export_->func()))
return r.fail("function not found");
}
for (WasmAstFunc* func : module->funcs()) {
if (!ResolveFunc(r, *func))
return false;
}
return true;
}
/*****************************************************************************/
// wasm function body serialization
static bool
EncodeExpr(Encoder& e, WasmAstExpr& expr);
static bool
EncodeBlock(Encoder& e, WasmAstBlock& b)
{
if (!e.writeExpr(b.expr()))
return false;
size_t numExprs = b.exprs().length();
if (!e.writeVarU32(numExprs))
return false;
for (size_t i = 0; i < numExprs; i++) {
if (!EncodeExpr(e, *b.exprs()[i]))
return false;
}
return true;
}
static bool
EncodeBranch(Encoder& e, WasmAstBranch& br)
{
if (!e.writeExpr(br.expr()))
return false;
if (!e.writeVarU32(br.target().index()))
return false;
if (br.expr() == Expr::BrIf) {
if (!EncodeExpr(e, br.cond()))
return false;
}
return true;
}
static bool
EncodeArgs(Encoder& e, const WasmAstExprVector& args)
{
for (WasmAstExpr* arg : args) {
if (!EncodeExpr(e, *arg))
return false;
}
return true;
}
static bool
EncodeCall(Encoder& e, WasmAstCall& c)
{
if (!e.writeExpr(c.expr()))
return false;
if (!e.writeVarU32(c.func().index()))
return false;
if (!EncodeArgs(e, c.args()))
return false;
return true;
}
static bool
EncodeCallIndirect(Encoder& e, WasmAstCallIndirect& c)
{
if (!e.writeExpr(Expr::CallIndirect))
return false;
if (!e.writeVarU32(c.sig().index()))
return false;
if (!EncodeExpr(e, *c.index()))
return false;
if (!EncodeArgs(e, c.args()))
return false;
return true;
}
static bool
EncodeConst(Encoder& e, WasmAstConst& c)
{
switch (c.val().type()) {
case ValType::I32:
return e.writeExpr(Expr::I32Const) &&
e.writeVarU32(c.val().i32());
case ValType::I64:
return e.writeExpr(Expr::I64Const) &&
e.writeVarU64(c.val().i64());
case ValType::F32:
return e.writeExpr(Expr::F32Const) &&
e.writeFixedF32(c.val().f32());
case ValType::F64:
return e.writeExpr(Expr::F64Const) &&
e.writeFixedF64(c.val().f64());
default:
break;
}
MOZ_CRASH("Bad value type");
}
static bool
EncodeGetLocal(Encoder& e, WasmAstGetLocal& gl)
{
return e.writeExpr(Expr::GetLocal) &&
e.writeVarU32(gl.local().index());
}
static bool
EncodeSetLocal(Encoder& e, WasmAstSetLocal& sl)
{
return e.writeExpr(Expr::SetLocal) &&
e.writeVarU32(sl.local().index()) &&
EncodeExpr(e, sl.value());
}
static bool
EncodeUnaryOperator(Encoder& e, WasmAstUnaryOperator& b)
{
return e.writeExpr(b.expr()) &&
EncodeExpr(e, *b.op());
}
static bool
EncodeBinaryOperator(Encoder& e, WasmAstBinaryOperator& b)
{
return e.writeExpr(b.expr()) &&
EncodeExpr(e, *b.lhs()) &&
EncodeExpr(e, *b.rhs());
}
static bool
EncodeComparisonOperator(Encoder& e, WasmAstComparisonOperator& b)
{
return e.writeExpr(b.expr()) &&
EncodeExpr(e, *b.lhs()) &&
EncodeExpr(e, *b.rhs());
}
static bool
EncodeConversionOperator(Encoder& e, WasmAstConversionOperator& b)
{
return e.writeExpr(b.expr()) &&
EncodeExpr(e, *b.op());
}
static bool
EncodeIfElse(Encoder& e, WasmAstIfElse& ie)
{
return e.writeExpr(ie.expr()) &&
EncodeExpr(e, ie.cond()) &&
EncodeExpr(e, ie.ifBody()) &&
(!ie.hasElse() || EncodeExpr(e, ie.elseBody()));
}
static bool
EncodeLoadStoreAddress(Encoder &e, const WasmAstLoadStoreAddress &address)
{
return e.writeVarU32(address.offset()) &&
e.writeVarU32(address.align()) &&
EncodeExpr(e, address.base());
}
static bool
EncodeLoad(Encoder& e, WasmAstLoad& l)
{
return e.writeExpr(l.expr()) &&
EncodeLoadStoreAddress(e, l.address());
}
static bool
EncodeStore(Encoder& e, WasmAstStore& s)
{
return e.writeExpr(s.expr()) &&
EncodeLoadStoreAddress(e, s.address()) &&
EncodeExpr(e, s.value());
}
static bool
EncodeReturn(Encoder& e, WasmAstReturn& r)
{
return e.writeExpr(Expr::Return) &&
(!r.maybeExpr() || EncodeExpr(e, *r.maybeExpr()));
}
static bool
EncodeBranchTable(Encoder& e, WasmAstBranchTable& bt)
{
if (!e.writeExpr(Expr::BrTable))
return false;
if (!e.writeVarU32(bt.def().index()))
return false;
if (!e.writeVarU32(bt.table().length()))
return false;
for (const WasmRef& elem : bt.table()) {
if (!e.writeVarU32(elem.index()))
return false;
}
return EncodeExpr(e, bt.index());
}
static bool
EncodeExpr(Encoder& e, WasmAstExpr& expr)
{
switch (expr.kind()) {
case WasmAstExprKind::Nop:
return e.writeExpr(Expr::Nop);
case WasmAstExprKind::BinaryOperator:
return EncodeBinaryOperator(e, expr.as<WasmAstBinaryOperator>());
case WasmAstExprKind::Block:
return EncodeBlock(e, expr.as<WasmAstBlock>());
case WasmAstExprKind::Branch:
return EncodeBranch(e, expr.as<WasmAstBranch>());
case WasmAstExprKind::Call:
return EncodeCall(e, expr.as<WasmAstCall>());
case WasmAstExprKind::CallIndirect:
return EncodeCallIndirect(e, expr.as<WasmAstCallIndirect>());
case WasmAstExprKind::ComparisonOperator:
return EncodeComparisonOperator(e, expr.as<WasmAstComparisonOperator>());
case WasmAstExprKind::Const:
return EncodeConst(e, expr.as<WasmAstConst>());
case WasmAstExprKind::ConversionOperator:
return EncodeConversionOperator(e, expr.as<WasmAstConversionOperator>());
case WasmAstExprKind::GetLocal:
return EncodeGetLocal(e, expr.as<WasmAstGetLocal>());
case WasmAstExprKind::IfElse:
return EncodeIfElse(e, expr.as<WasmAstIfElse>());
case WasmAstExprKind::Load:
return EncodeLoad(e, expr.as<WasmAstLoad>());
case WasmAstExprKind::Return:
return EncodeReturn(e, expr.as<WasmAstReturn>());
case WasmAstExprKind::SetLocal:
return EncodeSetLocal(e, expr.as<WasmAstSetLocal>());
case WasmAstExprKind::Store:
return EncodeStore(e, expr.as<WasmAstStore>());
case WasmAstExprKind::BranchTable:
return EncodeBranchTable(e, expr.as<WasmAstBranchTable>());
case WasmAstExprKind::UnaryOperator:
return EncodeUnaryOperator(e, expr.as<WasmAstUnaryOperator>());
}
MOZ_CRASH("Bad expr kind");
}
/*****************************************************************************/
// wasm AST binary serialization
static bool
EncodeSignatureSection(Encoder& e, WasmAstModule& module)
{
if (module.sigs().empty())
return true;
size_t offset;
if (!e.startSection(SigLabel, &offset))
return false;
if (!e.writeVarU32(module.sigs().length()))
return false;
for (WasmAstSig* sig : module.sigs()) {
if (!e.writeVarU32(sig->args().length()))
return false;
if (!e.writeExprType(sig->ret()))
return false;
for (ValType t : sig->args()) {
if (!e.writeValType(t))
return false;
}
}
e.finishSection(offset);
return true;
}
static bool
EncodeDeclarationSection(Encoder& e, WasmAstModule& module)
{
if (module.funcs().empty())
return true;
size_t offset;
if (!e.startSection(DeclLabel, &offset))
return false;
if (!e.writeVarU32(module.funcs().length()))
return false;
for (WasmAstFunc* func : module.funcs()) {
if (!e.writeVarU32(func->sig().index()))
return false;
}
e.finishSection(offset);
return true;
}
static bool
EncodeCString(Encoder& e, WasmName wasmName)
{
TwoByteChars range(wasmName.begin(), wasmName.length());
UniqueChars utf8(JS::CharsToNewUTF8CharsZ(nullptr, range).c_str());
return utf8 && e.writeCString(utf8.get());
}
static bool
EncodeImport(Encoder& e, WasmAstImport& imp)
{
if (!e.writeVarU32(imp.sigIndex()))
return false;
if (!EncodeCString(e, imp.module()))
return false;
if (!EncodeCString(e, imp.func()))
return false;
return true;
}
static bool
EncodeImportSection(Encoder& e, WasmAstModule& module)
{
if (module.imports().empty())
return true;
size_t offset;
if (!e.startSection(ImportLabel, &offset))
return false;
for (WasmAstImport* imp : module.imports()) {
if (!e.writeCString(FuncLabel))
return false;
if (!EncodeImport(e, *imp))
return false;
}
if (!e.writeCString(EndLabel))
return false;
e.finishSection(offset);
return true;
}
static bool
EncodeMemorySection(Encoder& e, WasmAstModule& module)
{
if (!module.maybeMemory())
return true;
size_t offset;
if (!e.startSection(MemoryLabel, &offset))
return false;
WasmAstMemory& memory = *module.maybeMemory();
if (!e.writeVarU32(memory.initialSize()))
return false;
uint32_t maxSize = memory.maxSize() ? *memory.maxSize() : memory.initialSize();
if (!e.writeVarU32(maxSize))
return false;
uint8_t exported = 0;
for (WasmAstExport* exp : module.exports()) {
if (exp->kind() == WasmAstExportKind::Memory) {
exported = 1;
break;
}
}
if (!e.writeU8(exported))
return false;
e.finishSection(offset);
return true;
}
static bool
EncodeFunctionExport(Encoder& e, WasmAstExport& exp)
{
if (!e.writeVarU32(exp.func().index()))
return false;
if (!EncodeCString(e, exp.name()))
return false;
return true;
}
static bool
EncodeExportSection(Encoder& e, WasmAstModule& module)
{
uint32_t numFuncExports = 0;
for (WasmAstExport* exp : module.exports()) {
if (exp->kind() == WasmAstExportKind::Func)
numFuncExports++;
}
if (!numFuncExports)
return true;
size_t offset;
if (!e.startSection(ExportLabel, &offset))
return false;
if (!e.writeVarU32(numFuncExports))
return false;
for (WasmAstExport* exp : module.exports()) {
switch (exp->kind()) {
case WasmAstExportKind::Func:
if (!EncodeFunctionExport(e, *exp))
return false;
break;
case WasmAstExportKind::Memory:
continue;
}
}
e.finishSection(offset);
return true;
}
static bool
EncodeTableSection(Encoder& e, WasmAstModule& module)
{
if (!module.maybeTable())
return true;
size_t offset;
if (!e.startSection(TableLabel, &offset))
return false;
if (!e.writeVarU32(module.maybeTable()->elems().length()))
return false;
for (WasmRef& ref : module.maybeTable()->elems()) {
if (!e.writeVarU32(ref.index()))
return false;
}
e.finishSection(offset);
return true;
}
static bool
EncodeFunctionBody(Encoder& e, WasmAstFunc& func)
{
if (!e.writeVarU32(func.vars().length()))
return false;
for (ValType type : func.vars()) {
if (!e.writeValType(type))
return false;
}
if (!e.writeVarU32(func.body().length()))
return false;
for (WasmAstExpr* expr : func.body()) {
if (!EncodeExpr(e, *expr))
return false;
}
return true;
}
static bool
EncodeFunctionBodiesSection(Encoder& e, WasmAstModule& module)
{
if (module.funcs().empty())
return true;
size_t offset;
if (!e.startSection(FuncLabel, &offset))
return false;
for (WasmAstFunc* func : module.funcs()) {
if (!EncodeFunctionBody(e, *func))
return false;
}
e.finishSection(offset);
return true;
}
static bool
EncodeDataSegment(Encoder& e, WasmAstSegment& segment)
{
if (!e.writeVarU32(segment.offset()))
return false;
WasmName text = segment.text();
Vector<uint8_t, 0, SystemAllocPolicy> bytes;
if (!bytes.reserve(text.length()))
return false;
const char16_t* cur = text.begin();
const char16_t* end = text.end();
while (cur != end) {
uint8_t byte;
MOZ_ALWAYS_TRUE(ConsumeTextByte(&cur, end, &byte));
bytes.infallibleAppend(byte);
}
if (!e.writeVarU32(bytes.length()))
return false;
if (!e.writeRawData(bytes.begin(), bytes.length()))
return false;
return true;
}
static bool
EncodeDataSection(Encoder& e, WasmAstModule& module)
{
if (!module.maybeMemory() || module.maybeMemory()->segments().empty())
return true;
const WasmAstSegmentVector& segments = module.maybeMemory()->segments();
size_t offset;
if (!e.startSection(DataLabel, &offset))
return false;
for (WasmAstSegment* segment : segments) {
if (!e.writeCString(SegmentLabel))
return false;
if (!EncodeDataSegment(e, *segment))
return false;
}
if (!e.writeCString(EndLabel))
return false;
e.finishSection(offset);
return true;
}
static UniqueBytecode
EncodeModule(WasmAstModule& module)
{
UniqueBytecode bytecode = MakeUnique<Bytecode>();
if (!bytecode)
return nullptr;
Encoder e(*bytecode);
if (!e.writeFixedU32(MagicNumber))
return nullptr;
if (!e.writeFixedU32(EncodingVersion))
return nullptr;
if (!EncodeSignatureSection(e, module))
return nullptr;
if (!EncodeImportSection(e, module))
return nullptr;
if (!EncodeDeclarationSection(e, module))
return nullptr;
if (!EncodeTableSection(e, module))
return nullptr;
if (!EncodeMemorySection(e, module))
return nullptr;
if (!EncodeExportSection(e, module))
return nullptr;
if (!EncodeFunctionBodiesSection(e, module))
return nullptr;
if (!EncodeDataSection(e, module))
return nullptr;
if (!e.writeCString(EndLabel))
return nullptr;
return Move(bytecode);
}
/*****************************************************************************/
UniqueBytecode
wasm::TextToBinary(const char16_t* text, UniqueChars* error)
{
LifoAlloc lifo(AST_LIFO_DEFAULT_CHUNK_SIZE);
WasmAstModule* module = ParseModule(text, lifo, error);
if (!module)
return nullptr;
if (!ResolveModule(lifo, module, error))
return nullptr;
return EncodeModule(*module);
}