summaryrefslogtreecommitdiff
path: root/deps/v8/src/wasm
diff options
context:
space:
mode:
authorMichaël Zasso <targos@protonmail.com>2017-03-21 10:16:54 +0100
committerMichaël Zasso <targos@protonmail.com>2017-03-25 09:44:10 +0100
commitc459d8ea5d402c702948c860d9497b2230ff7e8a (patch)
tree56c282fc4d40e5cb613b47cf7be3ea0526ed5b6f /deps/v8/src/wasm
parente0bc5a7361b1d29c3ed034155fd779ce6f44fb13 (diff)
downloadandroid-node-v8-c459d8ea5d402c702948c860d9497b2230ff7e8a.tar.gz
android-node-v8-c459d8ea5d402c702948c860d9497b2230ff7e8a.tar.bz2
android-node-v8-c459d8ea5d402c702948c860d9497b2230ff7e8a.zip
deps: update V8 to 5.7.492.69
PR-URL: https://github.com/nodejs/node/pull/11752 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Franziska Hinkelmann <franziska.hinkelmann@gmail.com>
Diffstat (limited to 'deps/v8/src/wasm')
-rw-r--r--deps/v8/src/wasm/OWNERS1
-rw-r--r--deps/v8/src/wasm/decoder.h43
-rw-r--r--deps/v8/src/wasm/function-body-decoder.cc (renamed from deps/v8/src/wasm/ast-decoder.cc)936
-rw-r--r--deps/v8/src/wasm/function-body-decoder.h (renamed from deps/v8/src/wasm/ast-decoder.h)166
-rw-r--r--deps/v8/src/wasm/module-decoder.cc389
-rw-r--r--deps/v8/src/wasm/module-decoder.h24
-rw-r--r--deps/v8/src/wasm/wasm-debug.cc341
-rw-r--r--deps/v8/src/wasm/wasm-external-refs.cc13
-rw-r--r--deps/v8/src/wasm/wasm-external-refs.h6
-rw-r--r--deps/v8/src/wasm/wasm-interpreter.cc199
-rw-r--r--deps/v8/src/wasm/wasm-interpreter.h36
-rw-r--r--deps/v8/src/wasm/wasm-js.cc719
-rw-r--r--deps/v8/src/wasm/wasm-js.h11
-rw-r--r--deps/v8/src/wasm/wasm-limits.h45
-rw-r--r--deps/v8/src/wasm/wasm-macro-gen.h62
-rw-r--r--deps/v8/src/wasm/wasm-module-builder.cc118
-rw-r--r--deps/v8/src/wasm/wasm-module-builder.h30
-rw-r--r--deps/v8/src/wasm/wasm-module.cc1913
-rw-r--r--deps/v8/src/wasm/wasm-module.h234
-rw-r--r--deps/v8/src/wasm/wasm-objects.cc678
-rw-r--r--deps/v8/src/wasm/wasm-objects.h321
-rw-r--r--deps/v8/src/wasm/wasm-opcodes.cc5
-rw-r--r--deps/v8/src/wasm/wasm-opcodes.h219
-rw-r--r--deps/v8/src/wasm/wasm-result.cc11
-rw-r--r--deps/v8/src/wasm/wasm-result.h3
-rw-r--r--deps/v8/src/wasm/wasm-text.cc312
-rw-r--r--deps/v8/src/wasm/wasm-text.h38
27 files changed, 4582 insertions, 2291 deletions
diff --git a/deps/v8/src/wasm/OWNERS b/deps/v8/src/wasm/OWNERS
index 2822c29819..4f54661aeb 100644
--- a/deps/v8/src/wasm/OWNERS
+++ b/deps/v8/src/wasm/OWNERS
@@ -2,6 +2,7 @@ set noparent
ahaas@chromium.org
bradnelson@chromium.org
+clemensh@chromium.org
mtrofin@chromium.org
rossberg@chromium.org
titzer@chromium.org
diff --git a/deps/v8/src/wasm/decoder.h b/deps/v8/src/wasm/decoder.h
index fc8f110b73..afe8701779 100644
--- a/deps/v8/src/wasm/decoder.h
+++ b/deps/v8/src/wasm/decoder.h
@@ -34,7 +34,12 @@ class Decoder {
Decoder(const byte* start, const byte* end)
: start_(start),
pc_(start),
- limit_(end),
+ end_(end),
+ error_pc_(nullptr),
+ error_pt_(nullptr) {}
+ Decoder(const byte* start, const byte* pc, const byte* end)
+ : start_(start),
+ pc_(pc),
end_(end),
error_pc_(nullptr),
error_pt_(nullptr) {}
@@ -44,7 +49,7 @@ class Decoder {
inline bool check(const byte* base, unsigned offset, unsigned length,
const char* msg) {
DCHECK_GE(base, start_);
- if ((base + offset + length) > limit_) {
+ if ((base + offset + length) > end_) {
error(base, base + offset, "%s", msg);
return false;
}
@@ -185,22 +190,27 @@ class Decoder {
// Consume {size} bytes and send them to the bit bucket, advancing {pc_}.
void consume_bytes(uint32_t size, const char* name = "skip") {
- TRACE(" +%d %-20s: %d bytes\n", static_cast<int>(pc_ - start_), name,
- size);
+#if DEBUG
+ if (name) {
+ // Only trace if the name is not null.
+ TRACE(" +%d %-20s: %d bytes\n", static_cast<int>(pc_ - start_), name,
+ size);
+ }
+#endif
if (checkAvailable(size)) {
pc_ += size;
} else {
- pc_ = limit_;
+ pc_ = end_;
}
}
- // Check that at least {size} bytes exist between {pc_} and {limit_}.
+ // Check that at least {size} bytes exist between {pc_} and {end_}.
bool checkAvailable(int size) {
intptr_t pc_overflow_value = std::numeric_limits<intptr_t>::max() - size;
if (size < 0 || (intptr_t)pc_ > pc_overflow_value) {
error(pc_, nullptr, "reading %d bytes would underflow/overflow", size);
return false;
- } else if (pc_ < start_ || limit_ < (pc_ + size)) {
+ } else if (pc_ < start_ || end_ < (pc_ + size)) {
error(pc_, nullptr, "expected %d bytes, fell off end", size);
return false;
} else {
@@ -241,11 +251,11 @@ class Decoder {
template <typename T>
T traceOffEnd() {
T t = 0;
- for (const byte* ptr = pc_; ptr < limit_; ptr++) {
+ for (const byte* ptr = pc_; ptr < end_; ptr++) {
TRACE("%02x ", *ptr);
}
TRACE("<end>\n");
- pc_ = limit_;
+ pc_ = end_;
return t;
}
@@ -272,7 +282,6 @@ class Decoder {
void Reset(const byte* start, const byte* end) {
start_ = start;
pc_ = start;
- limit_ = end;
end_ = end;
error_pc_ = nullptr;
error_pt_ = nullptr;
@@ -281,16 +290,16 @@ class Decoder {
bool ok() const { return error_msg_ == nullptr; }
bool failed() const { return !ok(); }
- bool more() const { return pc_ < limit_; }
+ bool more() const { return pc_ < end_; }
- const byte* start() { return start_; }
- const byte* pc() { return pc_; }
- uint32_t pc_offset() { return static_cast<uint32_t>(pc_ - start_); }
+ const byte* start() const { return start_; }
+ const byte* pc() const { return pc_; }
+ uint32_t pc_offset() const { return static_cast<uint32_t>(pc_ - start_); }
+ const byte* end() const { return end_; }
protected:
const byte* start_;
const byte* pc_;
- const byte* limit_;
const byte* end_;
const byte* error_pc_;
const byte* error_pt_;
@@ -308,7 +317,7 @@ class Decoder {
const int kMaxLength = (sizeof(IntType) * 8 + 6) / 7;
const byte* ptr = base + offset;
const byte* end = ptr + kMaxLength;
- if (end > limit_) end = limit_;
+ if (end > end_) end = end_;
int shift = 0;
byte b = 0;
IntType result = 0;
@@ -358,7 +367,7 @@ class Decoder {
const int kMaxLength = (sizeof(IntType) * 8 + 6) / 7;
const byte* pos = pc_;
const byte* end = pc_ + kMaxLength;
- if (end > limit_) end = limit_;
+ if (end > end_) end = end_;
IntType result = 0;
int shift = 0;
diff --git a/deps/v8/src/wasm/ast-decoder.cc b/deps/v8/src/wasm/function-body-decoder.cc
index ff6af34a02..04a2806237 100644
--- a/deps/v8/src/wasm/ast-decoder.cc
+++ b/deps/v8/src/wasm/function-body-decoder.cc
@@ -9,8 +9,8 @@
#include "src/handles.h"
#include "src/zone/zone-containers.h"
-#include "src/wasm/ast-decoder.h"
#include "src/wasm/decoder.h"
+#include "src/wasm/function-body-decoder.h"
#include "src/wasm/wasm-module.h"
#include "src/wasm/wasm-opcodes.h"
@@ -31,16 +31,14 @@ namespace wasm {
#define TRACE(...)
#endif
-#define CHECK_PROTOTYPE_OPCODE(flag) \
- if (module_ && module_->origin == kAsmJsOrigin) { \
- error("Opcode not supported for asmjs modules"); \
- } \
- if (!FLAG_##flag) { \
- error("Invalid opcode (enable with --" #flag ")"); \
- break; \
+#define CHECK_PROTOTYPE_OPCODE(flag) \
+ if (module_ != nullptr && module_->origin == kAsmJsOrigin) { \
+ error("Opcode not supported for asmjs modules"); \
+ } \
+ if (!FLAG_##flag) { \
+ error("Invalid opcode (enable with --" #flag ")"); \
+ break; \
}
-// TODO(titzer): this is only for intermediate migration.
-#define IMPLICIT_FUNCTION_END 1
// An SsaEnv environment carries the current local variable renaming
// as well as the current effect and control dependency in the TF graph.
@@ -70,7 +68,7 @@ struct SsaEnv {
struct Value {
const byte* pc;
TFNode* node;
- LocalType type;
+ ValueType type;
};
struct TryInfo : public ZoneObject {
@@ -87,9 +85,9 @@ struct MergeValues {
Value first;
} vals; // Either multiple values or a single value.
- Value& first() {
- DCHECK_GT(arity, 0u);
- return arity == 1 ? vals.first : vals.array[0];
+ Value& operator[](size_t i) {
+ DCHECK_GT(arity, i);
+ return arity == 1 ? vals.first : vals.array[i];
}
};
@@ -101,11 +99,12 @@ enum ControlKind { kControlIf, kControlBlock, kControlLoop, kControlTry };
struct Control {
const byte* pc;
ControlKind kind;
- int stack_depth; // stack height at the beginning of the construct.
- SsaEnv* end_env; // end environment for the construct.
- SsaEnv* false_env; // false environment (only for if).
- TryInfo* try_info; // Information used for compiling try statements.
+ size_t stack_depth; // stack height at the beginning of the construct.
+ SsaEnv* end_env; // end environment for the construct.
+ SsaEnv* false_env; // false environment (only for if).
+ TryInfo* try_info; // Information used for compiling try statements.
int32_t previous_catch; // The previous Control (on the stack) with a catch.
+ bool unreachable; // The current block has been ended.
// Values merged into the end of this control construct.
MergeValues merge;
@@ -116,30 +115,30 @@ struct Control {
inline bool is_try() const { return kind == kControlTry; }
// Named constructors.
- static Control Block(const byte* pc, int stack_depth, SsaEnv* end_env,
+ static Control Block(const byte* pc, size_t stack_depth, SsaEnv* end_env,
int32_t previous_catch) {
- return {pc, kControlBlock, stack_depth, end_env,
- nullptr, nullptr, previous_catch, {0, {NO_VALUE}}};
+ return {pc, kControlBlock, stack_depth, end_env, nullptr,
+ nullptr, previous_catch, false, {0, {NO_VALUE}}};
}
- static Control If(const byte* pc, int stack_depth, SsaEnv* end_env,
+ static Control If(const byte* pc, size_t stack_depth, SsaEnv* end_env,
SsaEnv* false_env, int32_t previous_catch) {
- return {pc, kControlIf, stack_depth, end_env,
- false_env, nullptr, previous_catch, {0, {NO_VALUE}}};
+ return {pc, kControlIf, stack_depth, end_env, false_env,
+ nullptr, previous_catch, false, {0, {NO_VALUE}}};
}
- static Control Loop(const byte* pc, int stack_depth, SsaEnv* end_env,
+ static Control Loop(const byte* pc, size_t stack_depth, SsaEnv* end_env,
int32_t previous_catch) {
- return {pc, kControlLoop, stack_depth, end_env,
- nullptr, nullptr, previous_catch, {0, {NO_VALUE}}};
+ return {pc, kControlLoop, stack_depth, end_env, nullptr,
+ nullptr, previous_catch, false, {0, {NO_VALUE}}};
}
- static Control Try(const byte* pc, int stack_depth, SsaEnv* end_env,
+ static Control Try(const byte* pc, size_t stack_depth, SsaEnv* end_env,
Zone* zone, SsaEnv* catch_env, int32_t previous_catch) {
DCHECK_NOT_NULL(catch_env);
TryInfo* try_info = new (zone) TryInfo(catch_env);
- return {pc, kControlTry, stack_depth, end_env,
- nullptr, try_info, previous_catch, {0, {NO_VALUE}}};
+ return {pc, kControlTry, stack_depth, end_env, nullptr,
+ try_info, previous_catch, false, {0, {NO_VALUE}}};
}
};
@@ -164,24 +163,123 @@ struct LaneOperand {
// lengths, etc.
class WasmDecoder : public Decoder {
public:
- WasmDecoder(ModuleEnv* module, FunctionSig* sig, const byte* start,
+ WasmDecoder(const WasmModule* module, FunctionSig* sig, const byte* start,
const byte* end)
: Decoder(start, end),
module_(module),
sig_(sig),
- total_locals_(0),
local_types_(nullptr) {}
- ModuleEnv* module_;
+ const WasmModule* module_;
FunctionSig* sig_;
- size_t total_locals_;
- ZoneVector<LocalType>* local_types_;
+
+ ZoneVector<ValueType>* local_types_;
+
+ size_t total_locals() const {
+ return local_types_ == nullptr ? 0 : local_types_->size();
+ }
+
+ static bool DecodeLocals(Decoder* decoder, const FunctionSig* sig,
+ ZoneVector<ValueType>* type_list) {
+ DCHECK_NOT_NULL(type_list);
+ // Initialize from signature.
+ if (sig != nullptr) {
+ type_list->reserve(sig->parameter_count());
+ for (size_t i = 0; i < sig->parameter_count(); ++i) {
+ type_list->push_back(sig->GetParam(i));
+ }
+ }
+ // Decode local declarations, if any.
+ uint32_t entries = decoder->consume_u32v("local decls count");
+ if (decoder->failed()) return false;
+
+ TRACE("local decls count: %u\n", entries);
+ while (entries-- > 0 && decoder->ok() && decoder->more()) {
+ uint32_t count = decoder->consume_u32v("local count");
+ if (decoder->failed()) return false;
+
+ if ((count + type_list->size()) > kMaxNumWasmLocals) {
+ decoder->error(decoder->pc() - 1, "local count too large");
+ return false;
+ }
+ byte code = decoder->consume_u8("local type");
+ if (decoder->failed()) return false;
+
+ ValueType type;
+ switch (code) {
+ case kLocalI32:
+ type = kWasmI32;
+ break;
+ case kLocalI64:
+ type = kWasmI64;
+ break;
+ case kLocalF32:
+ type = kWasmF32;
+ break;
+ case kLocalF64:
+ type = kWasmF64;
+ break;
+ case kLocalS128:
+ type = kWasmS128;
+ break;
+ default:
+ decoder->error(decoder->pc() - 1, "invalid local type");
+ return false;
+ }
+ type_list->insert(type_list->end(), count, type);
+ }
+ DCHECK(decoder->ok());
+ return true;
+ }
+
+ static BitVector* AnalyzeLoopAssignment(Decoder* decoder, const byte* pc,
+ int locals_count, Zone* zone) {
+ if (pc >= decoder->end()) return nullptr;
+ if (*pc != kExprLoop) return nullptr;
+
+ BitVector* assigned = new (zone) BitVector(locals_count, zone);
+ int depth = 0;
+ // Iteratively process all AST nodes nested inside the loop.
+ while (pc < decoder->end() && decoder->ok()) {
+ WasmOpcode opcode = static_cast<WasmOpcode>(*pc);
+ unsigned length = 1;
+ switch (opcode) {
+ case kExprLoop:
+ case kExprIf:
+ case kExprBlock:
+ case kExprTry:
+ length = OpcodeLength(decoder, pc);
+ depth++;
+ break;
+ case kExprSetLocal: // fallthru
+ case kExprTeeLocal: {
+ LocalIndexOperand operand(decoder, pc);
+ if (assigned->length() > 0 &&
+ operand.index < static_cast<uint32_t>(assigned->length())) {
+ // Unverified code might have an out-of-bounds index.
+ assigned->Add(operand.index);
+ }
+ length = 1 + operand.length;
+ break;
+ }
+ case kExprEnd:
+ depth--;
+ break;
+ default:
+ length = OpcodeLength(decoder, pc);
+ break;
+ }
+ if (depth <= 0) break;
+ pc += length;
+ }
+ return decoder->ok() ? assigned : nullptr;
+ }
inline bool Validate(const byte* pc, LocalIndexOperand& operand) {
- if (operand.index < total_locals_) {
+ if (operand.index < total_locals()) {
if (local_types_) {
operand.type = local_types_->at(operand.index);
} else {
- operand.type = kAstStmt;
+ operand.type = kWasmStmt;
}
return true;
}
@@ -190,9 +288,8 @@ class WasmDecoder : public Decoder {
}
inline bool Validate(const byte* pc, GlobalIndexOperand& operand) {
- ModuleEnv* m = module_;
- if (m && m->module && operand.index < m->module->globals.size()) {
- operand.global = &m->module->globals[operand.index];
+ if (module_ != nullptr && operand.index < module_->globals.size()) {
+ operand.global = &module_->globals[operand.index];
operand.type = operand.global->type;
return true;
}
@@ -201,9 +298,8 @@ class WasmDecoder : public Decoder {
}
inline bool Complete(const byte* pc, CallFunctionOperand& operand) {
- ModuleEnv* m = module_;
- if (m && m->module && operand.index < m->module->functions.size()) {
- operand.sig = m->module->functions[operand.index].sig;
+ if (module_ != nullptr && operand.index < module_->functions.size()) {
+ operand.sig = module_->functions[operand.index].sig;
return true;
}
return false;
@@ -218,17 +314,15 @@ class WasmDecoder : public Decoder {
}
inline bool Complete(const byte* pc, CallIndirectOperand& operand) {
- ModuleEnv* m = module_;
- if (m && m->module && operand.index < m->module->signatures.size()) {
- operand.sig = m->module->signatures[operand.index];
+ if (module_ != nullptr && operand.index < module_->signatures.size()) {
+ operand.sig = module_->signatures[operand.index];
return true;
}
return false;
}
inline bool Validate(const byte* pc, CallIndirectOperand& operand) {
- uint32_t table_index = 0;
- if (!module_->IsValidTable(table_index)) {
+ if (module_ == nullptr || module_->function_tables.empty()) {
error("function table has to exist to execute call_indirect");
return false;
}
@@ -264,33 +358,33 @@ class WasmDecoder : public Decoder {
}
}
- unsigned OpcodeLength(const byte* pc) {
+ static unsigned OpcodeLength(Decoder* decoder, const byte* pc) {
switch (static_cast<byte>(*pc)) {
#define DECLARE_OPCODE_CASE(name, opcode, sig) case kExpr##name:
FOREACH_LOAD_MEM_OPCODE(DECLARE_OPCODE_CASE)
FOREACH_STORE_MEM_OPCODE(DECLARE_OPCODE_CASE)
#undef DECLARE_OPCODE_CASE
{
- MemoryAccessOperand operand(this, pc, UINT32_MAX);
+ MemoryAccessOperand operand(decoder, pc, UINT32_MAX);
return 1 + operand.length;
}
case kExprBr:
case kExprBrIf: {
- BreakDepthOperand operand(this, pc);
+ BreakDepthOperand operand(decoder, pc);
return 1 + operand.length;
}
case kExprSetGlobal:
case kExprGetGlobal: {
- GlobalIndexOperand operand(this, pc);
+ GlobalIndexOperand operand(decoder, pc);
return 1 + operand.length;
}
case kExprCallFunction: {
- CallFunctionOperand operand(this, pc);
+ CallFunctionOperand operand(decoder, pc);
return 1 + operand.length;
}
case kExprCallIndirect: {
- CallIndirectOperand operand(this, pc);
+ CallIndirectOperand operand(decoder, pc);
return 1 + operand.length;
}
@@ -298,7 +392,7 @@ class WasmDecoder : public Decoder {
case kExprIf: // fall thru
case kExprLoop:
case kExprBlock: {
- BlockTypeOperand operand(this, pc);
+ BlockTypeOperand operand(decoder, pc);
return 1 + operand.length;
}
@@ -306,35 +400,33 @@ class WasmDecoder : public Decoder {
case kExprTeeLocal:
case kExprGetLocal:
case kExprCatch: {
- LocalIndexOperand operand(this, pc);
+ LocalIndexOperand operand(decoder, pc);
return 1 + operand.length;
}
case kExprBrTable: {
- BranchTableOperand operand(this, pc);
- BranchTableIterator iterator(this, operand);
+ BranchTableOperand operand(decoder, pc);
+ BranchTableIterator iterator(decoder, operand);
return 1 + iterator.length();
}
case kExprI32Const: {
- ImmI32Operand operand(this, pc);
+ ImmI32Operand operand(decoder, pc);
return 1 + operand.length;
}
case kExprI64Const: {
- ImmI64Operand operand(this, pc);
+ ImmI64Operand operand(decoder, pc);
return 1 + operand.length;
}
case kExprGrowMemory:
case kExprMemorySize: {
- MemoryIndexOperand operand(this, pc);
+ MemoryIndexOperand operand(decoder, pc);
return 1 + operand.length;
}
- case kExprI8Const:
- return 2;
case kExprF32Const:
return 5;
case kExprF64Const:
return 9;
case kSimdPrefix: {
- byte simd_index = checked_read_u8(pc, 1, "simd_index");
+ byte simd_index = decoder->checked_read_u8(pc, 1, "simd_index");
WasmOpcode opcode =
static_cast<WasmOpcode>(kSimdPrefix << 8 | simd_index);
switch (opcode) {
@@ -351,7 +443,7 @@ class WasmDecoder : public Decoder {
return 3;
}
default:
- error("invalid SIMD opcode");
+ decoder->error(pc, "invalid SIMD opcode");
return 2;
}
}
@@ -363,24 +455,24 @@ class WasmDecoder : public Decoder {
static const int32_t kNullCatch = -1;
-// The full WASM decoder for bytecode. Both verifies bytecode and generates
-// a TurboFan IR graph.
+// The full WASM decoder for bytecode. Verifies bytecode and, optionally,
+// generates a TurboFan IR graph.
class WasmFullDecoder : public WasmDecoder {
public:
+ WasmFullDecoder(Zone* zone, const wasm::WasmModule* module,
+ const FunctionBody& body)
+ : WasmFullDecoder(zone, module, nullptr, body) {}
+
WasmFullDecoder(Zone* zone, TFBuilder* builder, const FunctionBody& body)
- : WasmDecoder(body.module, body.sig, body.start, body.end),
- zone_(zone),
- builder_(builder),
- base_(body.base),
- local_type_vec_(zone),
- stack_(zone),
- control_(zone),
- last_end_found_(false),
- current_catch_(kNullCatch) {
- local_types_ = &local_type_vec_;
- }
+ : WasmFullDecoder(zone, builder->module_env() == nullptr
+ ? nullptr
+ : builder->module_env()->module,
+ builder, body) {}
bool Decode() {
+ if (FLAG_wasm_code_fuzzer_gen_test) {
+ PrintRawWasmCode(start_, end_);
+ }
base::ElapsedTimer decode_timer;
if (FLAG_trace_wasm_decode_time) {
decode_timer.Start();
@@ -393,47 +485,21 @@ class WasmFullDecoder : public WasmDecoder {
return false;
}
- DecodeLocalDecls();
+ DCHECK_EQ(0, local_types_->size());
+ WasmDecoder::DecodeLocals(this, sig_, local_types_);
InitSsaEnv();
DecodeFunctionBody();
if (failed()) return TraceFailed();
-#if IMPLICIT_FUNCTION_END
- // With implicit end support (old style), the function block
- // remains on the stack. Other control blocks are an error.
- if (control_.size() > 1) {
- error(pc_, control_.back().pc, "unterminated control structure");
- return TraceFailed();
- }
-
- // Assume an implicit end to the function body block.
- if (control_.size() == 1) {
- Control* c = &control_.back();
- if (ssa_env_->go()) {
- FallThruTo(c);
- }
-
- if (c->end_env->go()) {
- // Push the end values onto the stack.
- stack_.resize(c->stack_depth);
- if (c->merge.arity == 1) {
- stack_.push_back(c->merge.vals.first);
- } else {
- for (unsigned i = 0; i < c->merge.arity; i++) {
- stack_.push_back(c->merge.vals.array[i]);
- }
- }
-
- TRACE(" @%-8d #xx:%-20s|", startrel(pc_), "ImplicitReturn");
- SetEnv("function:end", c->end_env);
- DoReturn();
- TRACE("\n");
- }
- }
-#else
if (!control_.empty()) {
- error(pc_, control_.back().pc, "unterminated control structure");
+ // Generate a better error message whether the unterminated control
+ // structure is the function body block or an innner structure.
+ if (control_.size() > 1) {
+ error(pc_, control_.back().pc, "unterminated control structure");
+ } else {
+ error("function body must end with \"end\" opcode.");
+ }
return TraceFailed();
}
@@ -441,7 +507,6 @@ class WasmFullDecoder : public WasmDecoder {
error("function body must end with \"end\" opcode.");
return false;
}
-#endif
if (FLAG_trace_wasm_decode_time) {
double ms = decode_timer.Elapsed().InMillisecondsF();
@@ -459,36 +524,21 @@ class WasmFullDecoder : public WasmDecoder {
return false;
}
- bool DecodeLocalDecls(AstLocalDecls& decls) {
- DecodeLocalDecls();
- if (failed()) return false;
- decls.decls_encoded_size = pc_offset();
- decls.local_types.reserve(local_type_vec_.size());
- for (size_t pos = 0; pos < local_type_vec_.size();) {
- uint32_t count = 0;
- LocalType type = local_type_vec_[pos];
- while (pos < local_type_vec_.size() && local_type_vec_[pos] == type) {
- pos++;
- count++;
- }
- decls.local_types.push_back(std::pair<LocalType, uint32_t>(type, count));
- }
- decls.total_local_count = static_cast<uint32_t>(local_type_vec_.size());
- return true;
- }
-
- BitVector* AnalyzeLoopAssignmentForTesting(const byte* pc,
- size_t num_locals) {
- total_locals_ = num_locals;
- local_type_vec_.reserve(num_locals);
- if (num_locals > local_type_vec_.size()) {
- local_type_vec_.insert(local_type_vec_.end(),
- num_locals - local_type_vec_.size(), kAstI32);
- }
- return AnalyzeLoopAssignment(pc);
+ private:
+ WasmFullDecoder(Zone* zone, const wasm::WasmModule* module,
+ TFBuilder* builder, const FunctionBody& body)
+ : WasmDecoder(module, body.sig, body.start, body.end),
+ zone_(zone),
+ builder_(builder),
+ base_(body.base),
+ local_type_vec_(zone),
+ stack_(zone),
+ control_(zone),
+ last_end_found_(false),
+ current_catch_(kNullCatch) {
+ local_types_ = &local_type_vec_;
}
- private:
static const size_t kErrorMsgSize = 128;
Zone* zone_;
@@ -497,7 +547,7 @@ class WasmFullDecoder : public WasmDecoder {
SsaEnv* ssa_env_;
- ZoneVector<LocalType> local_type_vec_; // types of local variables.
+ ZoneVector<ValueType> local_type_vec_; // types of local variables.
ZoneVector<Value> stack_; // stack of values.
ZoneVector<Control> control_; // stack of blocks, loops, and ifs.
bool last_end_found_;
@@ -521,11 +571,11 @@ class WasmFullDecoder : public WasmDecoder {
// Initialize local variables.
uint32_t index = 0;
while (index < sig_->parameter_count()) {
- ssa_env->locals[index] = builder_->Param(index, local_type_vec_[index]);
+ ssa_env->locals[index] = builder_->Param(index);
index++;
}
while (index < local_type_vec_.size()) {
- LocalType type = local_type_vec_[index];
+ ValueType type = local_type_vec_[index];
TFNode* node = DefaultValue(type);
while (index < local_type_vec_.size() &&
local_type_vec_[index] == type) {
@@ -533,27 +583,28 @@ class WasmFullDecoder : public WasmDecoder {
ssa_env->locals[index++] = node;
}
}
- builder_->set_module(module_);
}
ssa_env->control = start;
ssa_env->effect = start;
SetEnv("initial", ssa_env);
if (builder_) {
- builder_->StackCheck(position());
+ // The function-prologue stack check is associated with position 0, which
+ // is never a position of any instruction in the function.
+ builder_->StackCheck(0);
}
}
- TFNode* DefaultValue(LocalType type) {
+ TFNode* DefaultValue(ValueType type) {
switch (type) {
- case kAstI32:
+ case kWasmI32:
return builder_->Int32Constant(0);
- case kAstI64:
+ case kWasmI64:
return builder_->Int64Constant(0);
- case kAstF32:
+ case kWasmF32:
return builder_->Float32Constant(0);
- case kAstF64:
+ case kWasmF64:
return builder_->Float64Constant(0);
- case kAstS128:
+ case kWasmS128:
return builder_->CreateS128Value(0);
default:
UNREACHABLE();
@@ -572,58 +623,19 @@ class WasmFullDecoder : public WasmDecoder {
return bytes;
}
- // Decodes the locals declarations, if any, populating {local_type_vec_}.
- void DecodeLocalDecls() {
- DCHECK_EQ(0u, local_type_vec_.size());
- // Initialize {local_type_vec} from signature.
- if (sig_) {
- local_type_vec_.reserve(sig_->parameter_count());
- for (size_t i = 0; i < sig_->parameter_count(); ++i) {
- local_type_vec_.push_back(sig_->GetParam(i));
- }
- }
- // Decode local declarations, if any.
- uint32_t entries = consume_u32v("local decls count");
- TRACE("local decls count: %u\n", entries);
- while (entries-- > 0 && pc_ < limit_) {
- uint32_t count = consume_u32v("local count");
- if (count > kMaxNumWasmLocals) {
- error(pc_ - 1, "local count too large");
- return;
- }
- byte code = consume_u8("local type");
- LocalType type;
- switch (code) {
- case kLocalI32:
- type = kAstI32;
- break;
- case kLocalI64:
- type = kAstI64;
- break;
- case kLocalF32:
- type = kAstF32;
- break;
- case kLocalF64:
- type = kAstF64;
- break;
- case kLocalS128:
- type = kAstS128;
- break;
- default:
- error(pc_ - 1, "invalid local type");
- return;
- }
- local_type_vec_.insert(local_type_vec_.end(), count, type);
+ bool CheckHasMemory() {
+ if (!module_->has_memory) {
+ error(pc_ - 1, "memory instruction with no memory");
}
- total_locals_ = local_type_vec_.size();
+ return module_->has_memory;
}
// Decodes the body of a function.
void DecodeFunctionBody() {
TRACE("wasm-decode %p...%p (module+%d, %d bytes) %s\n",
reinterpret_cast<const void*>(start_),
- reinterpret_cast<const void*>(limit_), baserel(pc_),
- static_cast<int>(limit_ - start_), builder_ ? "graph building" : "");
+ reinterpret_cast<const void*>(end_), baserel(pc_),
+ static_cast<int>(end_ - start_), builder_ ? "graph building" : "");
{
// Set up initial function block.
@@ -643,9 +655,7 @@ class WasmFullDecoder : public WasmDecoder {
}
}
- if (pc_ >= limit_) return; // Nothing to do.
-
- while (true) { // decoding loop.
+ while (pc_ < end_) { // decoding loop.
unsigned len = 1;
WasmOpcode opcode = static_cast<WasmOpcode>(*pc_);
if (!WasmOpcodes::IsPrefixOpcode(opcode)) {
@@ -673,8 +683,12 @@ class WasmFullDecoder : public WasmDecoder {
}
case kExprThrow: {
CHECK_PROTOTYPE_OPCODE(wasm_eh_prototype);
- Value value = Pop(0, kAstI32);
+ Value value = Pop(0, kWasmI32);
BUILD(Throw, value.node);
+ // TODO(titzer): Throw should end control, but currently we build a
+ // (reachable) runtime call instead of connecting it directly to
+ // end.
+ // EndControl();
break;
}
case kExprTry: {
@@ -710,9 +724,7 @@ class WasmFullDecoder : public WasmDecoder {
break;
}
- if (ssa_env_->go()) {
- MergeValuesInto(c);
- }
+ FallThruTo(c);
stack_.resize(c->stack_depth);
DCHECK_NOT_NULL(c->try_info);
@@ -746,7 +758,7 @@ class WasmFullDecoder : public WasmDecoder {
case kExprIf: {
// Condition on top of stack. Split environments for branches.
BlockTypeOperand operand(this, pc_);
- Value cond = Pop(0, kAstI32);
+ Value cond = Pop(0, kWasmI32);
TFNode* if_true = nullptr;
TFNode* if_false = nullptr;
BUILD(BranchNoHint, cond.node, &if_true, &if_false);
@@ -776,8 +788,8 @@ class WasmFullDecoder : public WasmDecoder {
break;
}
FallThruTo(c);
- // Switch to environment for false branch.
stack_.resize(c->stack_depth);
+ // Switch to environment for false branch.
SetEnv("if_else:false", c->false_env);
c->false_env = nullptr; // record that an else is already seen
break;
@@ -791,7 +803,8 @@ class WasmFullDecoder : public WasmDecoder {
Control* c = &control_.back();
if (c->is_loop()) {
// A loop just leaves the values on the stack.
- TypeCheckLoopFallThru(c);
+ TypeCheckFallThru(c);
+ if (c->unreachable) PushEndValues(c);
PopControl();
SetEnv("loop:end", ssa_env_);
break;
@@ -800,8 +813,7 @@ class WasmFullDecoder : public WasmDecoder {
if (c->false_env != nullptr) {
// End the true branch of a one-armed if.
Goto(c->false_env, c->end_env);
- if (ssa_env_->go() &&
- static_cast<int>(stack_.size()) != c->stack_depth) {
+ if (!c->unreachable && stack_.size() != c->stack_depth) {
error("end of if expected empty stack");
stack_.resize(c->stack_depth);
}
@@ -824,23 +836,13 @@ class WasmFullDecoder : public WasmDecoder {
}
FallThruTo(c);
SetEnv(name, c->end_env);
+ PushEndValues(c);
- // Push the end values onto the stack.
- stack_.resize(c->stack_depth);
- if (c->merge.arity == 1) {
- stack_.push_back(c->merge.vals.first);
- } else {
- for (unsigned i = 0; i < c->merge.arity; i++) {
- stack_.push_back(c->merge.vals.array[i]);
- }
- }
-
- PopControl();
-
- if (control_.empty()) {
- // If the last (implicit) control was popped, check we are at end.
+ if (control_.size() == 1) {
+ // If at the last (implicit) control, check we are at end.
if (pc_ + 1 != end_) {
error(pc_, pc_ + 1, "trailing code after function end");
+ break;
}
last_end_found_ = true;
if (ssa_env_->go()) {
@@ -848,25 +850,18 @@ class WasmFullDecoder : public WasmDecoder {
TRACE(" @%-8d #xx:%-20s|", startrel(pc_), "ImplicitReturn");
DoReturn();
TRACE("\n");
+ } else {
+ TypeCheckFallThru(c);
}
- return;
}
+ PopControl();
break;
}
case kExprSelect: {
- Value cond = Pop(2, kAstI32);
+ Value cond = Pop(2, kWasmI32);
Value fval = Pop();
- Value tval = Pop();
- if (tval.type == kAstStmt || tval.type != fval.type) {
- if (tval.type != kAstEnd && fval.type != kAstEnd) {
- error("type mismatch in select");
- break;
- }
- }
+ Value tval = Pop(0, fval.type);
if (build()) {
- DCHECK(tval.type != kAstEnd);
- DCHECK(fval.type != kAstEnd);
- DCHECK(cond.type != kAstEnd);
TFNode* controls[2];
builder_->BranchNoHint(cond.node, &controls[0], &controls[1]);
TFNode* merge = builder_->Merge(2, controls);
@@ -875,7 +870,7 @@ class WasmFullDecoder : public WasmDecoder {
Push(tval.type, phi);
ssa_env_->control = merge;
} else {
- Push(tval.type, nullptr);
+ Push(tval.type == kWasmVar ? fval.type : tval.type, nullptr);
}
break;
}
@@ -890,7 +885,7 @@ class WasmFullDecoder : public WasmDecoder {
}
case kExprBrIf: {
BreakDepthOperand operand(this, pc_);
- Value cond = Pop(0, kAstI32);
+ Value cond = Pop(0, kWasmI32);
if (ok() && Validate(pc_, operand, control_)) {
SsaEnv* fenv = ssa_env_;
SsaEnv* tenv = Split(fenv);
@@ -907,7 +902,7 @@ class WasmFullDecoder : public WasmDecoder {
BranchTableOperand operand(this, pc_);
BranchTableIterator iterator(this, operand);
if (Validate(pc_, operand, control_.size())) {
- Value key = Pop(0, kAstI32);
+ Value key = Pop(0, kWasmI32);
if (failed()) break;
SsaEnv* break_env = ssa_env_;
@@ -917,6 +912,7 @@ class WasmFullDecoder : public WasmDecoder {
SsaEnv* copy = Steal(break_env);
ssa_env_ = copy;
+ MergeValues* merge = nullptr;
while (ok() && iterator.has_next()) {
uint32_t i = iterator.cur_index();
const byte* pos = iterator.pc();
@@ -930,6 +926,27 @@ class WasmFullDecoder : public WasmDecoder {
? BUILD(IfDefault, sw)
: BUILD(IfValue, i, sw);
BreakTo(target);
+
+ // Check that label types match up.
+ Control* c = &control_[control_.size() - target - 1];
+ if (i == 0) {
+ merge = &c->merge;
+ } else if (merge->arity != c->merge.arity) {
+ error(pos, pos,
+ "inconsistent arity in br_table target %d"
+ " (previous was %u, this one %u)",
+ i, merge->arity, c->merge.arity);
+ } else if (control_.back().unreachable) {
+ for (uint32_t j = 0; ok() && j < merge->arity; ++j) {
+ if ((*merge)[j].type != c->merge[j].type) {
+ error(pos, pos,
+ "type error in br_table target %d operand %d"
+ " (previous expected %s, this one %s)",
+ i, j, WasmOpcodes::TypeName((*merge)[j].type),
+ WasmOpcodes::TypeName(c->merge[j].type));
+ }
+ }
+ }
}
if (failed()) break;
} else {
@@ -946,6 +963,7 @@ class WasmFullDecoder : public WasmDecoder {
ssa_env_ = break_env;
}
len = 1 + iterator.length();
+ EndControl();
break;
}
case kExprReturn: {
@@ -957,33 +975,27 @@ class WasmFullDecoder : public WasmDecoder {
EndControl();
break;
}
- case kExprI8Const: {
- ImmI8Operand operand(this, pc_);
- Push(kAstI32, BUILD(Int32Constant, operand.value));
- len = 1 + operand.length;
- break;
- }
case kExprI32Const: {
ImmI32Operand operand(this, pc_);
- Push(kAstI32, BUILD(Int32Constant, operand.value));
+ Push(kWasmI32, BUILD(Int32Constant, operand.value));
len = 1 + operand.length;
break;
}
case kExprI64Const: {
ImmI64Operand operand(this, pc_);
- Push(kAstI64, BUILD(Int64Constant, operand.value));
+ Push(kWasmI64, BUILD(Int64Constant, operand.value));
len = 1 + operand.length;
break;
}
case kExprF32Const: {
ImmF32Operand operand(this, pc_);
- Push(kAstF32, BUILD(Float32Constant, operand.value));
+ Push(kWasmF32, BUILD(Float32Constant, operand.value));
len = 1 + operand.length;
break;
}
case kExprF64Const: {
ImmF64Operand operand(this, pc_);
- Push(kAstF64, BUILD(Float64Constant, operand.value));
+ Push(kWasmF64, BUILD(Float64Constant, operand.value));
len = 1 + operand.length;
break;
}
@@ -1045,79 +1057,81 @@ class WasmFullDecoder : public WasmDecoder {
break;
}
case kExprI32LoadMem8S:
- len = DecodeLoadMem(kAstI32, MachineType::Int8());
+ len = DecodeLoadMem(kWasmI32, MachineType::Int8());
break;
case kExprI32LoadMem8U:
- len = DecodeLoadMem(kAstI32, MachineType::Uint8());
+ len = DecodeLoadMem(kWasmI32, MachineType::Uint8());
break;
case kExprI32LoadMem16S:
- len = DecodeLoadMem(kAstI32, MachineType::Int16());
+ len = DecodeLoadMem(kWasmI32, MachineType::Int16());
break;
case kExprI32LoadMem16U:
- len = DecodeLoadMem(kAstI32, MachineType::Uint16());
+ len = DecodeLoadMem(kWasmI32, MachineType::Uint16());
break;
case kExprI32LoadMem:
- len = DecodeLoadMem(kAstI32, MachineType::Int32());
+ len = DecodeLoadMem(kWasmI32, MachineType::Int32());
break;
case kExprI64LoadMem8S:
- len = DecodeLoadMem(kAstI64, MachineType::Int8());
+ len = DecodeLoadMem(kWasmI64, MachineType::Int8());
break;
case kExprI64LoadMem8U:
- len = DecodeLoadMem(kAstI64, MachineType::Uint8());
+ len = DecodeLoadMem(kWasmI64, MachineType::Uint8());
break;
case kExprI64LoadMem16S:
- len = DecodeLoadMem(kAstI64, MachineType::Int16());
+ len = DecodeLoadMem(kWasmI64, MachineType::Int16());
break;
case kExprI64LoadMem16U:
- len = DecodeLoadMem(kAstI64, MachineType::Uint16());
+ len = DecodeLoadMem(kWasmI64, MachineType::Uint16());
break;
case kExprI64LoadMem32S:
- len = DecodeLoadMem(kAstI64, MachineType::Int32());
+ len = DecodeLoadMem(kWasmI64, MachineType::Int32());
break;
case kExprI64LoadMem32U:
- len = DecodeLoadMem(kAstI64, MachineType::Uint32());
+ len = DecodeLoadMem(kWasmI64, MachineType::Uint32());
break;
case kExprI64LoadMem:
- len = DecodeLoadMem(kAstI64, MachineType::Int64());
+ len = DecodeLoadMem(kWasmI64, MachineType::Int64());
break;
case kExprF32LoadMem:
- len = DecodeLoadMem(kAstF32, MachineType::Float32());
+ len = DecodeLoadMem(kWasmF32, MachineType::Float32());
break;
case kExprF64LoadMem:
- len = DecodeLoadMem(kAstF64, MachineType::Float64());
+ len = DecodeLoadMem(kWasmF64, MachineType::Float64());
break;
case kExprI32StoreMem8:
- len = DecodeStoreMem(kAstI32, MachineType::Int8());
+ len = DecodeStoreMem(kWasmI32, MachineType::Int8());
break;
case kExprI32StoreMem16:
- len = DecodeStoreMem(kAstI32, MachineType::Int16());
+ len = DecodeStoreMem(kWasmI32, MachineType::Int16());
break;
case kExprI32StoreMem:
- len = DecodeStoreMem(kAstI32, MachineType::Int32());
+ len = DecodeStoreMem(kWasmI32, MachineType::Int32());
break;
case kExprI64StoreMem8:
- len = DecodeStoreMem(kAstI64, MachineType::Int8());
+ len = DecodeStoreMem(kWasmI64, MachineType::Int8());
break;
case kExprI64StoreMem16:
- len = DecodeStoreMem(kAstI64, MachineType::Int16());
+ len = DecodeStoreMem(kWasmI64, MachineType::Int16());
break;
case kExprI64StoreMem32:
- len = DecodeStoreMem(kAstI64, MachineType::Int32());
+ len = DecodeStoreMem(kWasmI64, MachineType::Int32());
break;
case kExprI64StoreMem:
- len = DecodeStoreMem(kAstI64, MachineType::Int64());
+ len = DecodeStoreMem(kWasmI64, MachineType::Int64());
break;
case kExprF32StoreMem:
- len = DecodeStoreMem(kAstF32, MachineType::Float32());
+ len = DecodeStoreMem(kWasmF32, MachineType::Float32());
break;
case kExprF64StoreMem:
- len = DecodeStoreMem(kAstF64, MachineType::Float64());
+ len = DecodeStoreMem(kWasmF64, MachineType::Float64());
break;
case kExprGrowMemory: {
+ if (!CheckHasMemory()) break;
MemoryIndexOperand operand(this, pc_);
+ DCHECK_NOT_NULL(module_);
if (module_->origin != kAsmJsOrigin) {
- Value val = Pop(0, kAstI32);
- Push(kAstI32, BUILD(GrowMemory, val.node));
+ Value val = Pop(0, kWasmI32);
+ Push(kWasmI32, BUILD(GrowMemory, val.node));
} else {
error("grow_memory is not supported for asmjs modules");
}
@@ -1125,8 +1139,9 @@ class WasmFullDecoder : public WasmDecoder {
break;
}
case kExprMemorySize: {
+ if (!CheckHasMemory()) break;
MemoryIndexOperand operand(this, pc_);
- Push(kAstI32, BUILD(CurrentMemoryPages));
+ Push(kWasmI32, BUILD(CurrentMemoryPages));
len = 1 + operand.length;
break;
}
@@ -1144,7 +1159,7 @@ class WasmFullDecoder : public WasmDecoder {
case kExprCallIndirect: {
CallIndirectOperand operand(this, pc_);
if (Validate(pc_, operand)) {
- Value index = Pop(0, kAstI32);
+ Value index = Pop(0, kWasmI32);
TFNode** buffer = PopArgs(operand.sig);
if (buffer) buffer[0] = index.node;
TFNode** rets = nullptr;
@@ -1165,7 +1180,7 @@ class WasmFullDecoder : public WasmDecoder {
break;
}
case kAtomicPrefix: {
- if (!module_ || module_->origin != kAsmJsOrigin) {
+ if (module_ == nullptr || module_->origin != kAsmJsOrigin) {
error("Atomics are allowed only in AsmJs modules");
break;
}
@@ -1184,7 +1199,7 @@ class WasmFullDecoder : public WasmDecoder {
}
default: {
// Deal with special asmjs opcodes.
- if (module_ && module_->origin == kAsmJsOrigin) {
+ if (module_ != nullptr && module_->origin == kAsmJsOrigin) {
sig = WasmOpcodes::AsmjsSignature(opcode);
if (sig) {
BuildSimpleOperator(opcode, sig);
@@ -1199,6 +1214,35 @@ class WasmFullDecoder : public WasmDecoder {
#if DEBUG
if (FLAG_trace_wasm_decoder) {
+ PrintF(" ");
+ for (size_t i = 0; i < control_.size(); ++i) {
+ Control* c = &control_[i];
+ enum ControlKind {
+ kControlIf,
+ kControlBlock,
+ kControlLoop,
+ kControlTry
+ };
+ switch (c->kind) {
+ case kControlIf:
+ PrintF("I");
+ break;
+ case kControlBlock:
+ PrintF("B");
+ break;
+ case kControlLoop:
+ PrintF("L");
+ break;
+ case kControlTry:
+ PrintF("T");
+ break;
+ default:
+ break;
+ }
+ PrintF("%u", c->merge.arity);
+ if (c->unreachable) PrintF("*");
+ }
+ PrintF(" | ");
for (size_t i = 0; i < stack_.size(); ++i) {
Value& val = stack_[i];
WasmOpcode opcode = static_cast<WasmOpcode>(*val.pc);
@@ -1228,20 +1272,23 @@ class WasmFullDecoder : public WasmDecoder {
default:
break;
}
+ if (val.node == nullptr) PrintF("?");
}
PrintF("\n");
}
#endif
pc_ += len;
- if (pc_ >= limit_) {
- // End of code reached or exceeded.
- if (pc_ > limit_ && ok()) error("Beyond end of code");
- return;
- }
} // end decode loop
+ if (pc_ > end_ && ok()) error("Beyond end of code");
}
- void EndControl() { ssa_env_->Kill(SsaEnv::kControlEnd); }
+ void EndControl() {
+ ssa_env_->Kill(SsaEnv::kControlEnd);
+ if (!control_.empty()) {
+ stack_.resize(control_.back().stack_depth);
+ control_.back().unreachable = true;
+ }
+ }
void SetBlockType(Control* c, BlockTypeOperand& operand) {
c->merge.arity = operand.arity;
@@ -1273,77 +1320,96 @@ class WasmFullDecoder : public WasmDecoder {
}
}
- LocalType GetReturnType(FunctionSig* sig) {
- return sig->return_count() == 0 ? kAstStmt : sig->GetReturn();
+ ValueType GetReturnType(FunctionSig* sig) {
+ return sig->return_count() == 0 ? kWasmStmt : sig->GetReturn();
}
void PushBlock(SsaEnv* end_env) {
- const int stack_depth = static_cast<int>(stack_.size());
control_.emplace_back(
- Control::Block(pc_, stack_depth, end_env, current_catch_));
+ Control::Block(pc_, stack_.size(), end_env, current_catch_));
}
void PushLoop(SsaEnv* end_env) {
- const int stack_depth = static_cast<int>(stack_.size());
control_.emplace_back(
- Control::Loop(pc_, stack_depth, end_env, current_catch_));
+ Control::Loop(pc_, stack_.size(), end_env, current_catch_));
}
void PushIf(SsaEnv* end_env, SsaEnv* false_env) {
- const int stack_depth = static_cast<int>(stack_.size());
control_.emplace_back(
- Control::If(pc_, stack_depth, end_env, false_env, current_catch_));
+ Control::If(pc_, stack_.size(), end_env, false_env, current_catch_));
}
void PushTry(SsaEnv* end_env, SsaEnv* catch_env) {
- const int stack_depth = static_cast<int>(stack_.size());
- control_.emplace_back(Control::Try(pc_, stack_depth, end_env, zone_,
+ control_.emplace_back(Control::Try(pc_, stack_.size(), end_env, zone_,
catch_env, current_catch_));
current_catch_ = static_cast<int32_t>(control_.size() - 1);
}
void PopControl() { control_.pop_back(); }
- int DecodeLoadMem(LocalType type, MachineType mem_type) {
+ int DecodeLoadMem(ValueType type, MachineType mem_type) {
+ if (!CheckHasMemory()) return 0;
MemoryAccessOperand operand(this, pc_,
ElementSizeLog2Of(mem_type.representation()));
- Value index = Pop(0, kAstI32);
+ Value index = Pop(0, kWasmI32);
TFNode* node = BUILD(LoadMem, type, mem_type, index.node, operand.offset,
operand.alignment, position());
Push(type, node);
return 1 + operand.length;
}
- int DecodeStoreMem(LocalType type, MachineType mem_type) {
+ int DecodeStoreMem(ValueType type, MachineType mem_type) {
+ if (!CheckHasMemory()) return 0;
MemoryAccessOperand operand(this, pc_,
ElementSizeLog2Of(mem_type.representation()));
Value val = Pop(1, type);
- Value index = Pop(0, kAstI32);
+ Value index = Pop(0, kWasmI32);
BUILD(StoreMem, mem_type, index.node, operand.offset, operand.alignment,
val.node, position());
return 1 + operand.length;
}
- unsigned ExtractLane(WasmOpcode opcode, LocalType type) {
+ unsigned ExtractLane(WasmOpcode opcode, ValueType type) {
LaneOperand operand(this, pc_);
if (Validate(pc_, operand)) {
- TFNode* input = Pop(0, LocalType::kSimd128).node;
- TFNode* node = BUILD(SimdExtractLane, opcode, operand.lane, input);
+ compiler::NodeVector inputs(1, zone_);
+ inputs[0] = Pop(0, ValueType::kSimd128).node;
+ TFNode* node = BUILD(SimdLaneOp, opcode, operand.lane, inputs);
Push(type, node);
}
return operand.length;
}
+ unsigned ReplaceLane(WasmOpcode opcode, ValueType type) {
+ LaneOperand operand(this, pc_);
+ if (Validate(pc_, operand)) {
+ compiler::NodeVector inputs(2, zone_);
+ inputs[1] = Pop(1, type).node;
+ inputs[0] = Pop(0, ValueType::kSimd128).node;
+ TFNode* node = BUILD(SimdLaneOp, opcode, operand.lane, inputs);
+ Push(ValueType::kSimd128, node);
+ }
+ return operand.length;
+ }
+
unsigned DecodeSimdOpcode(WasmOpcode opcode) {
unsigned len = 0;
switch (opcode) {
case kExprI32x4ExtractLane: {
- len = ExtractLane(opcode, LocalType::kWord32);
+ len = ExtractLane(opcode, ValueType::kWord32);
break;
}
case kExprF32x4ExtractLane: {
- len = ExtractLane(opcode, LocalType::kFloat32);
+ len = ExtractLane(opcode, ValueType::kFloat32);
+ break;
+ }
+ case kExprI32x4ReplaceLane: {
+ len = ReplaceLane(opcode, ValueType::kWord32);
+ break;
+ }
+ case kExprF32x4ReplaceLane: {
+ len = ReplaceLane(opcode, ValueType::kFloat32);
break;
}
default: {
@@ -1381,12 +1447,25 @@ class WasmFullDecoder : public WasmDecoder {
EndControl();
}
- void Push(LocalType type, TFNode* node) {
- if (type != kAstStmt && type != kAstEnd) {
+ void Push(ValueType type, TFNode* node) {
+ if (type != kWasmStmt) {
stack_.push_back({pc_, node, type});
}
}
+ void PushEndValues(Control* c) {
+ DCHECK_EQ(c, &control_.back());
+ stack_.resize(c->stack_depth);
+ if (c->merge.arity == 1) {
+ stack_.push_back(c->merge.vals.first);
+ } else {
+ for (unsigned i = 0; i < c->merge.arity; i++) {
+ stack_.push_back(c->merge.vals.array[i]);
+ }
+ }
+ DCHECK_EQ(c->stack_depth + c->merge.arity, stack_.size());
+ }
+
void PushReturns(FunctionSig* sig, TFNode** rets) {
for (size_t i = 0; i < sig->return_count(); i++) {
// When verifying only, then {rets} will be null, so push null.
@@ -1399,31 +1478,24 @@ class WasmFullDecoder : public WasmDecoder {
return WasmOpcodes::ShortOpcodeName(static_cast<WasmOpcode>(*pc));
}
- Value Pop(int index, LocalType expected) {
- if (!ssa_env_->go()) {
- // Unreachable code is essentially not typechecked.
- return {pc_, nullptr, expected};
- }
+ Value Pop(int index, ValueType expected) {
Value val = Pop();
- if (val.type != expected) {
- if (val.type != kAstEnd) {
- error(pc_, val.pc, "%s[%d] expected type %s, found %s of type %s",
- SafeOpcodeNameAt(pc_), index, WasmOpcodes::TypeName(expected),
- SafeOpcodeNameAt(val.pc), WasmOpcodes::TypeName(val.type));
- }
+ if (val.type != expected && val.type != kWasmVar && expected != kWasmVar) {
+ error(pc_, val.pc, "%s[%d] expected type %s, found %s of type %s",
+ SafeOpcodeNameAt(pc_), index, WasmOpcodes::TypeName(expected),
+ SafeOpcodeNameAt(val.pc), WasmOpcodes::TypeName(val.type));
}
return val;
}
Value Pop() {
- if (!ssa_env_->go()) {
- // Unreachable code is essentially not typechecked.
- return {pc_, nullptr, kAstEnd};
- }
size_t limit = control_.empty() ? 0 : control_.back().stack_depth;
if (stack_.size() <= limit) {
- Value val = {pc_, nullptr, kAstStmt};
- error(pc_, pc_, "%s found empty stack", SafeOpcodeNameAt(pc_));
+ // Popping past the current control start in reachable code.
+ Value val = {pc_, nullptr, kWasmVar};
+ if (!control_.back().unreachable) {
+ error(pc_, pc_, "%s found empty stack", SafeOpcodeNameAt(pc_));
+ }
return val;
}
Value val = stack_.back();
@@ -1431,22 +1503,6 @@ class WasmFullDecoder : public WasmDecoder {
return val;
}
- Value PopUpTo(int stack_depth) {
- if (!ssa_env_->go()) {
- // Unreachable code is essentially not typechecked.
- return {pc_, nullptr, kAstEnd};
- }
- if (stack_depth == static_cast<int>(stack_.size())) {
- Value val = {pc_, nullptr, kAstStmt};
- return val;
- } else {
- DCHECK_LE(stack_depth, static_cast<int>(stack_.size()));
- Value val = Pop();
- stack_.resize(stack_depth);
- return val;
- }
- }
-
int baserel(const byte* ptr) {
return base_ ? static_cast<int>(ptr - base_) : 0;
}
@@ -1454,17 +1510,17 @@ class WasmFullDecoder : public WasmDecoder {
int startrel(const byte* ptr) { return static_cast<int>(ptr - start_); }
void BreakTo(unsigned depth) {
- if (!ssa_env_->go()) return;
Control* c = &control_[control_.size() - depth - 1];
if (c->is_loop()) {
// This is the inner loop block, which does not have a value.
Goto(ssa_env_, c->end_env);
} else {
// Merge the value(s) into the end of the block.
- if (c->stack_depth + c->merge.arity > stack_.size()) {
+ size_t expected = control_.back().stack_depth + c->merge.arity;
+ if (stack_.size() < expected && !control_.back().unreachable) {
error(
pc_, pc_,
- "expected at least %d values on the stack for br to @%d, found %d",
+ "expected at least %u values on the stack for br to @%d, found %d",
c->merge.arity, startrel(c->pc),
static_cast<int>(stack_.size() - c->stack_depth));
return;
@@ -1474,37 +1530,41 @@ class WasmFullDecoder : public WasmDecoder {
}
void FallThruTo(Control* c) {
- if (!ssa_env_->go()) return;
+ DCHECK_EQ(c, &control_.back());
// Merge the value(s) into the end of the block.
- int arity = static_cast<int>(c->merge.arity);
- if (c->stack_depth + arity != static_cast<int>(stack_.size())) {
- error(pc_, pc_, "expected %d elements on the stack for fallthru to @%d",
- arity, startrel(c->pc));
+ size_t expected = c->stack_depth + c->merge.arity;
+ if (stack_.size() == expected ||
+ (stack_.size() < expected && c->unreachable)) {
+ MergeValuesInto(c);
+ c->unreachable = false;
return;
}
- MergeValuesInto(c);
+ error(pc_, pc_, "expected %u elements on the stack for fallthru to @%d",
+ c->merge.arity, startrel(c->pc));
}
- inline Value& GetMergeValueFromStack(Control* c, int i) {
+ inline Value& GetMergeValueFromStack(Control* c, size_t i) {
return stack_[stack_.size() - c->merge.arity + i];
}
- void TypeCheckLoopFallThru(Control* c) {
- if (!ssa_env_->go()) return;
+ void TypeCheckFallThru(Control* c) {
+ DCHECK_EQ(c, &control_.back());
// Fallthru must match arity exactly.
int arity = static_cast<int>(c->merge.arity);
- if (c->stack_depth + arity != static_cast<int>(stack_.size())) {
+ if (c->stack_depth + arity < stack_.size() ||
+ (c->stack_depth + arity != stack_.size() && !c->unreachable)) {
error(pc_, pc_, "expected %d elements on the stack for fallthru to @%d",
arity, startrel(c->pc));
return;
}
// Typecheck the values left on the stack.
- for (unsigned i = 0; i < c->merge.arity; i++) {
+ size_t avail = stack_.size() - c->stack_depth;
+ for (size_t i = avail >= c->merge.arity ? 0 : c->merge.arity - avail;
+ i < c->merge.arity; i++) {
Value& val = GetMergeValueFromStack(c, i);
- Value& old =
- c->merge.arity == 1 ? c->merge.vals.first : c->merge.vals.array[i];
+ Value& old = c->merge[i];
if (val.type != old.type) {
- error(pc_, pc_, "type error in merge[%d] (expected %s, got %s)", i,
+ error(pc_, pc_, "type error in merge[%zu] (expected %s, got %s)", i,
WasmOpcodes::TypeName(old.type), WasmOpcodes::TypeName(val.type));
return;
}
@@ -1514,23 +1574,24 @@ class WasmFullDecoder : public WasmDecoder {
void MergeValuesInto(Control* c) {
SsaEnv* target = c->end_env;
bool first = target->state == SsaEnv::kUnreachable;
+ bool reachable = ssa_env_->go();
Goto(ssa_env_, target);
- for (unsigned i = 0; i < c->merge.arity; i++) {
+ size_t avail = stack_.size() - control_.back().stack_depth;
+ for (size_t i = avail >= c->merge.arity ? 0 : c->merge.arity - avail;
+ i < c->merge.arity; i++) {
Value& val = GetMergeValueFromStack(c, i);
- Value& old =
- c->merge.arity == 1 ? c->merge.vals.first : c->merge.vals.array[i];
- if (val.type != old.type) {
- error(pc_, pc_, "type error in merge[%d] (expected %s, got %s)", i,
+ Value& old = c->merge[i];
+ if (val.type != old.type && val.type != kWasmVar) {
+ error(pc_, pc_, "type error in merge[%zu] (expected %s, got %s)", i,
WasmOpcodes::TypeName(old.type), WasmOpcodes::TypeName(val.type));
return;
}
- if (builder_) {
+ if (builder_ && reachable) {
+ DCHECK_NOT_NULL(val.node);
old.node =
first ? val.node : CreateOrMergeIntoPhi(old.type, target->control,
old.node, val.node);
- } else {
- old.node = nullptr;
}
}
}
@@ -1555,13 +1616,13 @@ class WasmFullDecoder : public WasmDecoder {
break;
}
}
- PrintF(" env = %p, state = %c, reason = %s", static_cast<void*>(env),
+ PrintF("{set_env = %p, state = %c, reason = %s", static_cast<void*>(env),
state, reason);
if (env && env->control) {
PrintF(", control = ");
compiler::WasmGraphBuilder::PrintDebugName(env->control);
}
- PrintF("\n");
+ PrintF("}");
}
#endif
ssa_env_ = env;
@@ -1602,7 +1663,7 @@ class WasmFullDecoder : public WasmDecoder {
} else {
DCHECK_EQ(SsaEnv::kMerged, try_info->catch_env->state);
try_info->exception =
- CreateOrMergeIntoPhi(kAstI32, try_info->catch_env->control,
+ CreateOrMergeIntoPhi(kWasmI32, try_info->catch_env->control,
try_info->exception, if_exception);
}
@@ -1686,7 +1747,7 @@ class WasmFullDecoder : public WasmDecoder {
return from->Kill();
}
- TFNode* CreateOrMergeIntoPhi(LocalType type, TFNode* merge, TFNode* tnode,
+ TFNode* CreateOrMergeIntoPhi(ValueType type, TFNode* merge, TFNode* tnode,
TFNode* fnode) {
DCHECK_NOT_NULL(builder_);
if (builder_->IsPhiWithMerge(tnode, merge)) {
@@ -1710,7 +1771,8 @@ class WasmFullDecoder : public WasmDecoder {
env->effect = builder_->EffectPhi(1, &env->effect, env->control);
builder_->Terminate(env->effect, env->control);
if (FLAG_wasm_loop_assignment_analysis) {
- BitVector* assigned = AnalyzeLoopAssignment(pc);
+ BitVector* assigned = AnalyzeLoopAssignment(
+ this, pc, static_cast<int>(total_locals()), zone_);
if (failed()) return env;
if (assigned != nullptr) {
// Only introduce phis for variables assigned in this loop.
@@ -1789,52 +1851,10 @@ class WasmFullDecoder : public WasmDecoder {
}
virtual void onFirstError() {
- limit_ = start_; // Terminate decoding loop.
+ end_ = start_; // Terminate decoding loop.
builder_ = nullptr; // Don't build any more nodes.
TRACE(" !%s\n", error_msg_.get());
}
- BitVector* AnalyzeLoopAssignment(const byte* pc) {
- if (pc >= limit_) return nullptr;
- if (*pc != kExprLoop) return nullptr;
-
- BitVector* assigned =
- new (zone_) BitVector(static_cast<int>(local_type_vec_.size()), zone_);
- int depth = 0;
- // Iteratively process all AST nodes nested inside the loop.
- while (pc < limit_ && ok()) {
- WasmOpcode opcode = static_cast<WasmOpcode>(*pc);
- unsigned length = 1;
- switch (opcode) {
- case kExprLoop:
- case kExprIf:
- case kExprBlock:
- case kExprTry:
- length = OpcodeLength(pc);
- depth++;
- break;
- case kExprSetLocal: // fallthru
- case kExprTeeLocal: {
- LocalIndexOperand operand(this, pc);
- if (assigned->length() > 0 &&
- operand.index < static_cast<uint32_t>(assigned->length())) {
- // Unverified code might have an out-of-bounds index.
- assigned->Add(operand.index);
- }
- length = 1 + operand.length;
- break;
- }
- case kExprEnd:
- depth--;
- break;
- default:
- length = OpcodeLength(pc);
- break;
- }
- if (depth <= 0) break;
- pc += length;
- }
- return ok() ? assigned : nullptr;
- }
inline wasm::WasmCodePosition position() {
int offset = static_cast<int>(pc_ - start_);
@@ -1865,30 +1885,33 @@ class WasmFullDecoder : public WasmDecoder {
}
};
-bool DecodeLocalDecls(AstLocalDecls& decls, const byte* start,
+bool DecodeLocalDecls(BodyLocalDecls* decls, const byte* start,
const byte* end) {
- AccountingAllocator allocator;
- Zone tmp(&allocator, ZONE_NAME);
- FunctionBody body = {nullptr, nullptr, nullptr, start, end};
- WasmFullDecoder decoder(&tmp, nullptr, body);
- return decoder.DecodeLocalDecls(decls);
+ Decoder decoder(start, end);
+ if (WasmDecoder::DecodeLocals(&decoder, nullptr, &decls->type_list)) {
+ DCHECK(decoder.ok());
+ decls->encoded_size = decoder.pc_offset();
+ return true;
+ }
+ return false;
}
BytecodeIterator::BytecodeIterator(const byte* start, const byte* end,
- AstLocalDecls* decls)
+ BodyLocalDecls* decls)
: Decoder(start, end) {
if (decls != nullptr) {
- if (DecodeLocalDecls(*decls, start, end)) {
- pc_ += decls->decls_encoded_size;
+ if (DecodeLocalDecls(decls, start, end)) {
+ pc_ += decls->encoded_size;
if (pc_ > end_) pc_ = end_;
}
}
}
DecodeResult VerifyWasmCode(AccountingAllocator* allocator,
+ const wasm::WasmModule* module,
FunctionBody& body) {
Zone zone(allocator, ZONE_NAME);
- WasmFullDecoder decoder(&zone, nullptr, body);
+ WasmFullDecoder decoder(&zone, module, body);
decoder.Decode();
return decoder.toResult<DecodeStruct*>(nullptr);
}
@@ -1902,21 +1925,35 @@ DecodeResult BuildTFGraph(AccountingAllocator* allocator, TFBuilder* builder,
}
unsigned OpcodeLength(const byte* pc, const byte* end) {
- WasmDecoder decoder(nullptr, nullptr, pc, end);
- return decoder.OpcodeLength(pc);
+ Decoder decoder(pc, end);
+ return WasmDecoder::OpcodeLength(&decoder, pc);
}
-void PrintAstForDebugging(const byte* start, const byte* end) {
+void PrintRawWasmCode(const byte* start, const byte* end) {
AccountingAllocator allocator;
- OFStream os(stdout);
- PrintAst(&allocator, FunctionBodyForTesting(start, end), os, nullptr);
+ PrintRawWasmCode(&allocator, FunctionBodyForTesting(start, end), nullptr);
}
-bool PrintAst(AccountingAllocator* allocator, const FunctionBody& body,
- std::ostream& os,
- std::vector<std::tuple<uint32_t, int, int>>* offset_table) {
+namespace {
+const char* RawOpcodeName(WasmOpcode opcode) {
+ switch (opcode) {
+#define DECLARE_NAME_CASE(name, opcode, sig) \
+ case kExpr##name: \
+ return "kExpr" #name;
+ FOREACH_OPCODE(DECLARE_NAME_CASE)
+#undef DECLARE_NAME_CASE
+ default:
+ break;
+ }
+ return "Unknown";
+}
+} // namespace
+
+bool PrintRawWasmCode(AccountingAllocator* allocator, const FunctionBody& body,
+ const wasm::WasmModule* module) {
+ OFStream os(stdout);
Zone zone(allocator, ZONE_NAME);
- WasmFullDecoder decoder(&zone, nullptr, body);
+ WasmFullDecoder decoder(&zone, module, body);
int line_nr = 0;
// Print the function signature.
@@ -1926,14 +1963,22 @@ bool PrintAst(AccountingAllocator* allocator, const FunctionBody& body,
}
// Print the local declarations.
- AstLocalDecls decls(&zone);
+ BodyLocalDecls decls(&zone);
BytecodeIterator i(body.start, body.end, &decls);
- if (body.start != i.pc()) {
+ if (body.start != i.pc() && !FLAG_wasm_code_fuzzer_gen_test) {
os << "// locals: ";
- for (auto p : decls.local_types) {
- LocalType type = p.first;
- uint32_t count = p.second;
- os << " " << count << " " << WasmOpcodes::TypeName(type);
+ if (!decls.type_list.empty()) {
+ ValueType type = decls.type_list[0];
+ uint32_t count = 0;
+ for (size_t pos = 0; pos < decls.type_list.size(); ++pos) {
+ if (decls.type_list[pos] == type) {
+ ++count;
+ } else {
+ os << " " << count << " " << WasmOpcodes::TypeName(type);
+ type = decls.type_list[pos];
+ count = 1;
+ }
+ }
}
os << std::endl;
++line_nr;
@@ -1949,25 +1994,22 @@ bool PrintAst(AccountingAllocator* allocator, const FunctionBody& body,
++line_nr;
unsigned control_depth = 0;
for (; i.has_next(); i.next()) {
- unsigned length = decoder.OpcodeLength(i.pc());
+ unsigned length = WasmDecoder::OpcodeLength(&decoder, i.pc());
WasmOpcode opcode = i.current();
if (opcode == kExprElse) control_depth--;
int num_whitespaces = control_depth < 32 ? 2 * control_depth : 64;
- if (offset_table) {
- offset_table->push_back(
- std::make_tuple(i.pc_offset(), line_nr, num_whitespaces));
- }
// 64 whitespaces
const char* padding =
" ";
os.write(padding, num_whitespaces);
- os << "k" << WasmOpcodes::OpcodeName(opcode) << ",";
+
+ os << RawOpcodeName(opcode) << ",";
for (size_t j = 1; j < length; ++j) {
- os << " " << AsHex(i.pc()[j], 2) << ",";
+ os << " 0x" << AsHex(i.pc()[j], 2) << ",";
}
switch (opcode) {
@@ -2024,7 +2066,7 @@ bool PrintAst(AccountingAllocator* allocator, const FunctionBody& body,
}
default:
break;
- }
+ }
os << std::endl;
++line_nr;
}
@@ -2034,9 +2076,9 @@ bool PrintAst(AccountingAllocator* allocator, const FunctionBody& body,
BitVector* AnalyzeLoopAssignmentForTesting(Zone* zone, size_t num_locals,
const byte* start, const byte* end) {
- FunctionBody body = {nullptr, nullptr, nullptr, start, end};
- WasmFullDecoder decoder(zone, nullptr, body);
- return decoder.AnalyzeLoopAssignmentForTesting(start, num_locals);
+ Decoder decoder(start, end);
+ return WasmDecoder::AnalyzeLoopAssignment(&decoder, start,
+ static_cast<int>(num_locals), zone);
}
} // namespace wasm
diff --git a/deps/v8/src/wasm/ast-decoder.h b/deps/v8/src/wasm/function-body-decoder.h
index 9ce323efcb..1115b1a450 100644
--- a/deps/v8/src/wasm/ast-decoder.h
+++ b/deps/v8/src/wasm/function-body-decoder.h
@@ -2,10 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef V8_WASM_AST_DECODER_H_
-#define V8_WASM_AST_DECODER_H_
+#ifndef V8_WASM_FUNCTION_BODY_DECODER_H_
+#define V8_WASM_FUNCTION_BODY_DECODER_H_
+
+#include <iterator>
#include "src/base/compiler-specific.h"
+#include "src/base/iterator.h"
#include "src/globals.h"
#include "src/signature.h"
#include "src/wasm/decoder.h"
@@ -29,12 +32,12 @@ struct WasmGlobal;
// Helpers for decoding different kinds of operands which follow bytecodes.
struct LocalIndexOperand {
uint32_t index;
- LocalType type;
+ ValueType type;
unsigned length;
inline LocalIndexOperand(Decoder* decoder, const byte* pc) {
index = decoder->checked_read_u32v(pc, 1, &length, "local index");
- type = kAstStmt;
+ type = kWasmStmt;
}
};
@@ -67,7 +70,9 @@ struct ImmF32Operand {
float value;
unsigned length;
inline ImmF32Operand(Decoder* decoder, const byte* pc) {
- value = bit_cast<float>(decoder->checked_read_u32(pc, 1, "immf32"));
+ // Avoid bit_cast because it might not preserve the signalling bit of a NaN.
+ uint32_t tmp = decoder->checked_read_u32(pc, 1, "immf32");
+ memcpy(&value, &tmp, sizeof(value));
length = 4;
}
};
@@ -76,21 +81,23 @@ struct ImmF64Operand {
double value;
unsigned length;
inline ImmF64Operand(Decoder* decoder, const byte* pc) {
- value = bit_cast<double>(decoder->checked_read_u64(pc, 1, "immf64"));
+ // Avoid bit_cast because it might not preserve the signalling bit of a NaN.
+ uint64_t tmp = decoder->checked_read_u64(pc, 1, "immf64");
+ memcpy(&value, &tmp, sizeof(value));
length = 8;
}
};
struct GlobalIndexOperand {
uint32_t index;
- LocalType type;
+ ValueType type;
const WasmGlobal* global;
unsigned length;
inline GlobalIndexOperand(Decoder* decoder, const byte* pc) {
index = decoder->checked_read_u32v(pc, 1, &length, "global index");
global = nullptr;
- type = kAstStmt;
+ type = kWasmStmt;
}
};
@@ -101,12 +108,12 @@ struct BlockTypeOperand {
inline BlockTypeOperand(Decoder* decoder, const byte* pc) {
uint8_t val = decoder->checked_read_u8(pc, 1, "block type");
- LocalType type = kAstStmt;
+ ValueType type = kWasmStmt;
length = 1;
arity = 0;
types = nullptr;
if (decode_local_type(val, &type)) {
- arity = type == kAstStmt ? 0 : 1;
+ arity = type == kWasmStmt ? 0 : 1;
types = pc + 1;
} else {
// Handle multi-value blocks.
@@ -132,7 +139,7 @@ struct BlockTypeOperand {
uint32_t offset = 1 + 1 + len + i;
val = decoder->checked_read_u8(pc, offset, "block type");
decode_local_type(val, &type);
- if (type == kAstStmt) {
+ if (type == kWasmStmt) {
decoder->error(pc, pc + offset, "invalid block type");
return;
}
@@ -141,34 +148,34 @@ struct BlockTypeOperand {
}
// Decode a byte representing a local type. Return {false} if the encoded
// byte was invalid or {kMultivalBlock}.
- bool decode_local_type(uint8_t val, LocalType* result) {
- switch (static_cast<LocalTypeCode>(val)) {
+ bool decode_local_type(uint8_t val, ValueType* result) {
+ switch (static_cast<ValueTypeCode>(val)) {
case kLocalVoid:
- *result = kAstStmt;
+ *result = kWasmStmt;
return true;
case kLocalI32:
- *result = kAstI32;
+ *result = kWasmI32;
return true;
case kLocalI64:
- *result = kAstI64;
+ *result = kWasmI64;
return true;
case kLocalF32:
- *result = kAstF32;
+ *result = kWasmF32;
return true;
case kLocalF64:
- *result = kAstF64;
+ *result = kWasmF64;
return true;
case kLocalS128:
- *result = kAstS128;
+ *result = kWasmS128;
return true;
default:
- *result = kAstStmt;
+ *result = kWasmStmt;
return false;
}
}
- LocalType read_entry(unsigned index) {
+ ValueType read_entry(unsigned index) {
DCHECK_LT(index, arity);
- LocalType result;
+ ValueType result;
CHECK(decode_local_type(types[index], &result));
return result;
}
@@ -243,10 +250,6 @@ struct BranchTableOperand {
}
table = pc + 1 + len1;
}
- inline uint32_t read_entry(Decoder* decoder, unsigned i) {
- DCHECK(i <= table_count);
- return table ? decoder->read_u32(table + i * sizeof(uint32_t)) : 0;
- }
};
// A helper to iterate over a branch table.
@@ -309,11 +312,10 @@ struct MemoryAccessOperand {
};
typedef compiler::WasmGraphBuilder TFBuilder;
-struct ModuleEnv; // forward declaration of module interface.
+struct WasmModule; // forward declaration of module interface.
// All of the various data structures necessary to decode a function body.
struct FunctionBody {
- ModuleEnv* module; // module environment
FunctionSig* sig; // function signature
const byte* base; // base of the module bytes, for error reporting
const byte* start; // start of the function body
@@ -322,7 +324,7 @@ struct FunctionBody {
static inline FunctionBody FunctionBodyForTesting(const byte* start,
const byte* end) {
- return {nullptr, nullptr, start, start, end};
+ return {nullptr, start, start, end};
}
struct DecodeStruct {
@@ -334,48 +336,42 @@ inline std::ostream& operator<<(std::ostream& os, const DecodeStruct& tree) {
}
V8_EXPORT_PRIVATE DecodeResult VerifyWasmCode(AccountingAllocator* allocator,
+ const wasm::WasmModule* module,
FunctionBody& body);
DecodeResult BuildTFGraph(AccountingAllocator* allocator, TFBuilder* builder,
FunctionBody& body);
-bool PrintAst(AccountingAllocator* allocator, const FunctionBody& body,
- std::ostream& os,
- std::vector<std::tuple<uint32_t, int, int>>* offset_table);
+bool PrintRawWasmCode(AccountingAllocator* allocator, const FunctionBody& body,
+ const wasm::WasmModule* module);
// A simplified form of AST printing, e.g. from a debugger.
-void PrintAstForDebugging(const byte* start, const byte* end);
+void PrintRawWasmCode(const byte* start, const byte* end);
inline DecodeResult VerifyWasmCode(AccountingAllocator* allocator,
- ModuleEnv* module, FunctionSig* sig,
+ const WasmModule* module, FunctionSig* sig,
const byte* start, const byte* end) {
- FunctionBody body = {module, sig, nullptr, start, end};
- return VerifyWasmCode(allocator, body);
+ FunctionBody body = {sig, nullptr, start, end};
+ return VerifyWasmCode(allocator, module, body);
}
inline DecodeResult BuildTFGraph(AccountingAllocator* allocator,
- TFBuilder* builder, ModuleEnv* module,
- FunctionSig* sig, const byte* start,
- const byte* end) {
- FunctionBody body = {module, sig, nullptr, start, end};
+ TFBuilder* builder, FunctionSig* sig,
+ const byte* start, const byte* end) {
+ FunctionBody body = {sig, nullptr, start, end};
return BuildTFGraph(allocator, builder, body);
}
-struct AstLocalDecls {
+struct BodyLocalDecls {
// The size of the encoded declarations.
- uint32_t decls_encoded_size; // size of encoded declarations
+ uint32_t encoded_size; // size of encoded declarations
- // Total number of locals.
- uint32_t total_local_count;
-
- // List of {local type, count} pairs.
- ZoneVector<std::pair<LocalType, uint32_t>> local_types;
+ ZoneVector<ValueType> type_list;
// Constructor initializes the vector.
- explicit AstLocalDecls(Zone* zone)
- : decls_encoded_size(0), total_local_count(0), local_types(zone) {}
+ explicit BodyLocalDecls(Zone* zone) : encoded_size(0), type_list(zone) {}
};
-V8_EXPORT_PRIVATE bool DecodeLocalDecls(AstLocalDecls& decls, const byte* start,
- const byte* end);
+V8_EXPORT_PRIVATE bool DecodeLocalDecls(BodyLocalDecls* decls,
+ const byte* start, const byte* end);
V8_EXPORT_PRIVATE BitVector* AnalyzeLoopAssignmentForTesting(Zone* zone,
size_t num_locals,
const byte* start,
@@ -386,41 +382,77 @@ V8_EXPORT_PRIVATE unsigned OpcodeLength(const byte* pc, const byte* end);
// A simple forward iterator for bytecodes.
class V8_EXPORT_PRIVATE BytecodeIterator : public NON_EXPORTED_BASE(Decoder) {
- public:
- // If one wants to iterate over the bytecode without looking at {pc_offset()}.
- class iterator {
+ // Base class for both iterators defined below.
+ class iterator_base {
public:
- inline iterator& operator++() {
+ inline iterator_base& operator++() {
DCHECK_LT(ptr_, end_);
ptr_ += OpcodeLength(ptr_, end_);
return *this;
}
+ inline bool operator==(const iterator_base& that) {
+ return this->ptr_ == that.ptr_;
+ }
+ inline bool operator!=(const iterator_base& that) {
+ return this->ptr_ != that.ptr_;
+ }
+
+ protected:
+ const byte* ptr_;
+ const byte* end_;
+ iterator_base(const byte* ptr, const byte* end) : ptr_(ptr), end_(end) {}
+ };
+
+ public:
+ // If one wants to iterate over the bytecode without looking at {pc_offset()}.
+ class opcode_iterator
+ : public iterator_base,
+ public std::iterator<std::input_iterator_tag, WasmOpcode> {
+ public:
inline WasmOpcode operator*() {
DCHECK_LT(ptr_, end_);
return static_cast<WasmOpcode>(*ptr_);
}
- inline bool operator==(const iterator& that) {
- return this->ptr_ == that.ptr_;
- }
- inline bool operator!=(const iterator& that) {
- return this->ptr_ != that.ptr_;
+
+ private:
+ friend class BytecodeIterator;
+ opcode_iterator(const byte* ptr, const byte* end)
+ : iterator_base(ptr, end) {}
+ };
+ // If one wants to iterate over the instruction offsets without looking at
+ // opcodes.
+ class offset_iterator
+ : public iterator_base,
+ public std::iterator<std::input_iterator_tag, uint32_t> {
+ public:
+ inline uint32_t operator*() {
+ DCHECK_LT(ptr_, end_);
+ return static_cast<uint32_t>(ptr_ - start_);
}
private:
+ const byte* start_;
friend class BytecodeIterator;
- const byte* ptr_;
- const byte* end_;
- iterator(const byte* ptr, const byte* end) : ptr_(ptr), end_(end) {}
+ offset_iterator(const byte* start, const byte* ptr, const byte* end)
+ : iterator_base(ptr, end), start_(start) {}
};
// Create a new {BytecodeIterator}. If the {decls} pointer is non-null,
// assume the bytecode starts with local declarations and decode them.
// Otherwise, do not decode local decls.
BytecodeIterator(const byte* start, const byte* end,
- AstLocalDecls* decls = nullptr);
+ BodyLocalDecls* decls = nullptr);
- inline iterator begin() const { return iterator(pc_, end_); }
- inline iterator end() const { return iterator(end_, end_); }
+ base::iterator_range<opcode_iterator> opcodes() {
+ return base::iterator_range<opcode_iterator>(opcode_iterator(pc_, end_),
+ opcode_iterator(end_, end_));
+ }
+
+ base::iterator_range<offset_iterator> offsets() {
+ return base::iterator_range<offset_iterator>(
+ offset_iterator(start_, pc_, end_),
+ offset_iterator(start_, end_, end_));
+ }
WasmOpcode current() {
return static_cast<WasmOpcode>(
@@ -441,4 +473,4 @@ class V8_EXPORT_PRIVATE BytecodeIterator : public NON_EXPORTED_BASE(Decoder) {
} // namespace internal
} // namespace v8
-#endif // V8_WASM_AST_DECODER_H_
+#endif // V8_WASM_FUNCTION_BODY_DECODER_H_
diff --git a/deps/v8/src/wasm/module-decoder.cc b/deps/v8/src/wasm/module-decoder.cc
index c8eace3c10..056fc2f64d 100644
--- a/deps/v8/src/wasm/module-decoder.cc
+++ b/deps/v8/src/wasm/module-decoder.cc
@@ -12,6 +12,7 @@
#include "src/v8.h"
#include "src/wasm/decoder.h"
+#include "src/wasm/wasm-limits.h"
namespace v8 {
namespace internal {
@@ -31,25 +32,25 @@ namespace {
const char* kNameString = "name";
const size_t kNameStringLength = 4;
-LocalType TypeOf(const WasmModule* module, const WasmInitExpr& expr) {
+ValueType TypeOf(const WasmModule* module, const WasmInitExpr& expr) {
switch (expr.kind) {
case WasmInitExpr::kNone:
- return kAstStmt;
+ return kWasmStmt;
case WasmInitExpr::kGlobalIndex:
return expr.val.global_index < module->globals.size()
? module->globals[expr.val.global_index].type
- : kAstStmt;
+ : kWasmStmt;
case WasmInitExpr::kI32Const:
- return kAstI32;
+ return kWasmI32;
case WasmInitExpr::kI64Const:
- return kAstI64;
+ return kWasmI64;
case WasmInitExpr::kF32Const:
- return kAstF32;
+ return kWasmF32;
case WasmInitExpr::kF64Const:
- return kAstF64;
+ return kWasmF64;
default:
UNREACHABLE();
- return kAstStmt;
+ return kWasmStmt;
}
}
@@ -179,17 +180,17 @@ class ModuleDecoder : public Decoder {
ModuleOrigin origin)
: Decoder(module_start, module_end), module_zone(zone), origin_(origin) {
result_.start = start_;
- if (limit_ < start_) {
+ if (end_ < start_) {
error(start_, "end is less than start");
- limit_ = start_;
+ end_ = start_;
}
}
virtual void onFirstError() {
- pc_ = limit_; // On error, terminate section decoding loop.
+ pc_ = end_; // On error, terminate section decoding loop.
}
- static void DumpModule(WasmModule* module, const ModuleResult& result) {
+ void DumpModule(const ModuleResult& result) {
std::string path;
if (FLAG_dump_wasm_module_path) {
path = FLAG_dump_wasm_module_path;
@@ -199,7 +200,7 @@ class ModuleDecoder : public Decoder {
}
}
// File are named `HASH.{ok,failed}.wasm`.
- size_t hash = base::hash_range(module->module_start, module->module_end);
+ size_t hash = base::hash_range(start_, end_);
char buf[32] = {'\0'};
#if V8_OS_WIN && _MSC_VER < 1900
#define snprintf sprintf_s
@@ -208,17 +209,15 @@ class ModuleDecoder : public Decoder {
result.ok() ? "ok" : "failed");
std::string name(buf);
if (FILE* wasm_file = base::OS::FOpen((path + name).c_str(), "wb")) {
- fwrite(module->module_start, module->module_end - module->module_start, 1,
- wasm_file);
+ fwrite(start_, end_ - start_, 1, wasm_file);
fclose(wasm_file);
}
}
// Decodes an entire module.
- ModuleResult DecodeModule(WasmModule* module, bool verify_functions = true) {
+ ModuleResult DecodeModule(bool verify_functions = true) {
pc_ = start_;
- module->module_start = start_;
- module->module_end = limit_;
+ WasmModule* module = new WasmModule(module_zone);
module->min_mem_pages = 0;
module->max_mem_pages = 0;
module->mem_export = false;
@@ -249,8 +248,8 @@ class ModuleDecoder : public Decoder {
// ===== Type section ====================================================
if (section_iter.section_code() == kTypeSectionCode) {
- uint32_t signatures_count = consume_u32v("signatures count");
- module->signatures.reserve(SafeReserve(signatures_count));
+ uint32_t signatures_count = consume_count("types count", kV8MaxWasmTypes);
+ module->signatures.reserve(signatures_count);
for (uint32_t i = 0; ok() && i < signatures_count; ++i) {
TRACE("DecodeSignature[%d] module+%d\n", i,
static_cast<int>(pc_ - start_));
@@ -262,8 +261,9 @@ class ModuleDecoder : public Decoder {
// ===== Import section ==================================================
if (section_iter.section_code() == kImportSectionCode) {
- uint32_t import_table_count = consume_u32v("import table count");
- module->import_table.reserve(SafeReserve(import_table_count));
+ uint32_t import_table_count =
+ consume_count("imports count", kV8MaxWasmImports);
+ module->import_table.reserve(import_table_count);
for (uint32_t i = 0; ok() && i < import_table_count; ++i) {
TRACE("DecodeImportTable[%d] module+%d\n", i,
static_cast<int>(pc_ - start_));
@@ -280,9 +280,6 @@ class ModuleDecoder : public Decoder {
const byte* pos = pc_;
import->module_name_offset =
consume_string(&import->module_name_length, true);
- if (import->module_name_length == 0) {
- error(pos, "import module name cannot be NULL");
- }
import->field_name_offset =
consume_string(&import->field_name_length, true);
@@ -307,6 +304,7 @@ class ModuleDecoder : public Decoder {
}
case kExternalTable: {
// ===== Imported table ==========================================
+ if (!AddTable(module)) break;
import->index =
static_cast<uint32_t>(module->function_tables.size());
module->function_tables.push_back({0, 0, false,
@@ -314,30 +312,29 @@ class ModuleDecoder : public Decoder {
false, SignatureMap()});
expect_u8("element type", kWasmAnyFunctionTypeForm);
WasmIndirectFunctionTable* table = &module->function_tables.back();
- consume_resizable_limits(
- "element count", "elements", WasmModule::kV8MaxTableSize,
- &table->min_size, &table->has_max, WasmModule::kV8MaxTableSize,
- &table->max_size);
+ consume_resizable_limits("element count", "elements",
+ kV8MaxWasmTableSize, &table->min_size,
+ &table->has_max, kV8MaxWasmTableSize,
+ &table->max_size);
break;
}
case kExternalMemory: {
// ===== Imported memory =========================================
- bool has_max = false;
- consume_resizable_limits("memory", "pages", WasmModule::kV8MaxPages,
- &module->min_mem_pages, &has_max,
- WasmModule::kSpecMaxPages,
- &module->max_mem_pages);
- module->has_memory = true;
+ if (!AddMemory(module)) break;
+ consume_resizable_limits(
+ "memory", "pages", kV8MaxWasmMemoryPages,
+ &module->min_mem_pages, &module->has_max_mem,
+ kSpecMaxWasmMemoryPages, &module->max_mem_pages);
break;
}
case kExternalGlobal: {
// ===== Imported global =========================================
import->index = static_cast<uint32_t>(module->globals.size());
module->globals.push_back(
- {kAstStmt, false, WasmInitExpr(), 0, true, false});
+ {kWasmStmt, false, WasmInitExpr(), 0, true, false});
WasmGlobal* global = &module->globals.back();
global->type = consume_value_type();
- global->mutability = consume_u8("mutability") != 0;
+ global->mutability = consume_mutability();
if (global->mutability) {
error("mutable globals cannot be imported");
}
@@ -353,8 +350,9 @@ class ModuleDecoder : public Decoder {
// ===== Function section ================================================
if (section_iter.section_code() == kFunctionSectionCode) {
- uint32_t functions_count = consume_u32v("functions count");
- module->functions.reserve(SafeReserve(functions_count));
+ uint32_t functions_count =
+ consume_count("functions count", kV8MaxWasmFunctions);
+ module->functions.reserve(functions_count);
module->num_declared_functions = functions_count;
for (uint32_t i = 0; ok() && i < functions_count; ++i) {
uint32_t func_index = static_cast<uint32_t>(module->functions.size());
@@ -375,63 +373,47 @@ class ModuleDecoder : public Decoder {
// ===== Table section ===================================================
if (section_iter.section_code() == kTableSectionCode) {
- const byte* pos = pc_;
- uint32_t table_count = consume_u32v("table count");
- // Require at most one table for now.
- if (table_count > 1) {
- error(pos, pos, "invalid table count %d, maximum 1", table_count);
- }
- if (module->function_tables.size() < 1) {
- module->function_tables.push_back({0, 0, false, std::vector<int32_t>(),
- false, false, SignatureMap()});
- }
+ uint32_t table_count = consume_count("table count", kV8MaxWasmTables);
for (uint32_t i = 0; ok() && i < table_count; i++) {
+ if (!AddTable(module)) break;
+ module->function_tables.push_back({0, 0, false, std::vector<int32_t>(),
+ false, false, SignatureMap()});
WasmIndirectFunctionTable* table = &module->function_tables.back();
expect_u8("table type", kWasmAnyFunctionTypeForm);
- consume_resizable_limits("table elements", "elements",
- WasmModule::kV8MaxTableSize, &table->min_size,
- &table->has_max, WasmModule::kV8MaxTableSize,
- &table->max_size);
+ consume_resizable_limits(
+ "table elements", "elements", kV8MaxWasmTableSize, &table->min_size,
+ &table->has_max, kV8MaxWasmTableSize, &table->max_size);
}
section_iter.advance();
}
// ===== Memory section ==================================================
if (section_iter.section_code() == kMemorySectionCode) {
- const byte* pos = pc_;
- uint32_t memory_count = consume_u32v("memory count");
- // Require at most one memory for now.
- if (memory_count > 1) {
- error(pos, pos, "invalid memory count %d, maximum 1", memory_count);
- }
+ uint32_t memory_count = consume_count("memory count", kV8MaxWasmMemories);
for (uint32_t i = 0; ok() && i < memory_count; i++) {
- bool has_max = false;
- consume_resizable_limits(
- "memory", "pages", WasmModule::kV8MaxPages, &module->min_mem_pages,
- &has_max, WasmModule::kSpecMaxPages, &module->max_mem_pages);
+ if (!AddMemory(module)) break;
+ consume_resizable_limits("memory", "pages", kV8MaxWasmMemoryPages,
+ &module->min_mem_pages, &module->has_max_mem,
+ kSpecMaxWasmMemoryPages,
+ &module->max_mem_pages);
}
- module->has_memory = true;
section_iter.advance();
}
// ===== Global section ==================================================
if (section_iter.section_code() == kGlobalSectionCode) {
- uint32_t globals_count = consume_u32v("globals count");
+ uint32_t globals_count =
+ consume_count("globals count", kV8MaxWasmGlobals);
uint32_t imported_globals = static_cast<uint32_t>(module->globals.size());
- if (!IsWithinLimit(std::numeric_limits<int32_t>::max(), globals_count,
- imported_globals)) {
- error(pos, pos, "too many imported+defined globals: %u + %u",
- imported_globals, globals_count);
- }
- module->globals.reserve(SafeReserve(imported_globals + globals_count));
+ module->globals.reserve(imported_globals + globals_count);
for (uint32_t i = 0; ok() && i < globals_count; ++i) {
TRACE("DecodeGlobal[%d] module+%d\n", i,
static_cast<int>(pc_ - start_));
// Add an uninitialized global and pass a pointer to it.
module->globals.push_back(
- {kAstStmt, false, WasmInitExpr(), 0, false, false});
+ {kWasmStmt, false, WasmInitExpr(), 0, false, false});
WasmGlobal* global = &module->globals.back();
DecodeGlobalInModule(module, i + imported_globals, global);
}
@@ -440,8 +422,9 @@ class ModuleDecoder : public Decoder {
// ===== Export section ==================================================
if (section_iter.section_code() == kExportSectionCode) {
- uint32_t export_table_count = consume_u32v("export table count");
- module->export_table.reserve(SafeReserve(export_table_count));
+ uint32_t export_table_count =
+ consume_count("exports count", kV8MaxWasmImports);
+ module->export_table.reserve(export_table_count);
for (uint32_t i = 0; ok() && i < export_table_count; ++i) {
TRACE("DecodeExportTable[%d] module+%d\n", i,
static_cast<int>(pc_ - start_));
@@ -473,7 +456,11 @@ class ModuleDecoder : public Decoder {
}
case kExternalMemory: {
uint32_t index = consume_u32v("memory index");
- if (index != 0) error("invalid memory index != 0");
+ // TODO(titzer): This should become more regular
+ // once we support multiple memories.
+ if (!module->has_memory || index != 0) {
+ error("invalid memory index != 0");
+ }
module->mem_export = true;
break;
}
@@ -493,8 +480,8 @@ class ModuleDecoder : public Decoder {
break;
}
}
- // Check for duplicate exports.
- if (ok() && module->export_table.size() > 1) {
+ // Check for duplicate exports (except for asm.js).
+ if (ok() && origin_ != kAsmJsOrigin && module->export_table.size() > 1) {
std::vector<WasmExport> sorted_exports(module->export_table);
const byte* base = start_;
auto cmp_less = [base](const WasmExport& a, const WasmExport& b) {
@@ -538,7 +525,8 @@ class ModuleDecoder : public Decoder {
// ===== Elements section ================================================
if (section_iter.section_code() == kElementSectionCode) {
- uint32_t element_count = consume_u32v("element count");
+ uint32_t element_count =
+ consume_count("element count", kV8MaxWasmTableSize);
for (uint32_t i = 0; ok() && i < element_count; ++i) {
const byte* pos = pc();
uint32_t table_index = consume_u32v("table index");
@@ -551,19 +539,18 @@ class ModuleDecoder : public Decoder {
} else {
table = &module->function_tables[table_index];
}
- WasmInitExpr offset = consume_init_expr(module, kAstI32);
- uint32_t num_elem = consume_u32v("number of elements");
+ WasmInitExpr offset = consume_init_expr(module, kWasmI32);
+ uint32_t num_elem =
+ consume_count("number of elements", kV8MaxWasmTableEntries);
std::vector<uint32_t> vector;
module->table_inits.push_back({table_index, offset, vector});
WasmTableInit* init = &module->table_inits.back();
- init->entries.reserve(SafeReserve(num_elem));
for (uint32_t j = 0; ok() && j < num_elem; j++) {
WasmFunction* func = nullptr;
uint32_t index = consume_func_index(module, &func);
init->entries.push_back(index);
if (table && index < module->functions.size()) {
// Canonicalize signature indices during decoding.
- // TODO(titzer): suboptimal, redundant when verifying only.
table->map.FindOrInsert(module->functions[index].sig);
}
}
@@ -587,10 +574,8 @@ class ModuleDecoder : public Decoder {
function->code_start_offset = pc_offset();
function->code_end_offset = pc_offset() + size;
if (verify_functions) {
- ModuleEnv module_env;
- module_env.module = module;
- module_env.origin = module->origin;
-
+ ModuleBytesEnv module_env(module, nullptr,
+ ModuleWireBytes(start_, end_));
VerifyFunctionBody(i + module->num_imported_functions, &module_env,
function);
}
@@ -601,8 +586,9 @@ class ModuleDecoder : public Decoder {
// ===== Data section ====================================================
if (section_iter.section_code() == kDataSectionCode) {
- uint32_t data_segments_count = consume_u32v("data segments count");
- module->data_segments.reserve(SafeReserve(data_segments_count));
+ uint32_t data_segments_count =
+ consume_count("data segments count", kV8MaxWasmDataSegments);
+ module->data_segments.reserve(data_segments_count);
for (uint32_t i = 0; ok() && i < data_segments_count; ++i) {
if (!module->has_memory) {
error("cannot load data without memory");
@@ -623,22 +609,29 @@ class ModuleDecoder : public Decoder {
// ===== Name section ====================================================
if (section_iter.section_code() == kNameSectionCode) {
- uint32_t functions_count = consume_u32v("functions count");
+ // TODO(titzer): find a way to report name errors as warnings.
+ // Use an inner decoder so that errors don't fail the outer decoder.
+ Decoder inner(start_, pc_, end_);
+ uint32_t functions_count = inner.consume_u32v("functions count");
- for (uint32_t i = 0; ok() && i < functions_count; ++i) {
+ for (uint32_t i = 0; inner.ok() && i < functions_count; ++i) {
uint32_t function_name_length = 0;
- uint32_t name_offset = consume_string(&function_name_length, false);
+ uint32_t name_offset =
+ consume_string(inner, &function_name_length, false);
uint32_t func_index = i;
- if (func_index < module->functions.size()) {
+ if (inner.ok() && func_index < module->functions.size()) {
module->functions[func_index].name_offset = name_offset;
module->functions[func_index].name_length = function_name_length;
}
- uint32_t local_names_count = consume_u32v("local names count");
+ uint32_t local_names_count = inner.consume_u32v("local names count");
for (uint32_t j = 0; ok() && j < local_names_count; j++) {
- skip_string();
+ uint32_t length = inner.consume_u32v("string length");
+ inner.consume_bytes(length, "string");
}
}
+ // Skip the whole names section in the outer decoder.
+ consume_bytes(section_iter.payload_length(), nullptr);
section_iter.advance();
}
@@ -656,25 +649,19 @@ class ModuleDecoder : public Decoder {
if (verify_functions && result.ok()) {
result.MoveFrom(result_); // Copy error code and location.
}
- if (FLAG_dump_wasm_module) DumpModule(module, result);
+ if (FLAG_dump_wasm_module) DumpModule(result);
return result;
}
- uint32_t SafeReserve(uint32_t count) {
- // Avoid OOM by only reserving up to a certain size.
- const uint32_t kMaxReserve = 20000;
- return count < kMaxReserve ? count : kMaxReserve;
- }
-
// Decodes a single anonymous function starting at {start_}.
- FunctionResult DecodeSingleFunction(ModuleEnv* module_env,
+ FunctionResult DecodeSingleFunction(ModuleBytesEnv* module_env,
WasmFunction* function) {
pc_ = start_;
function->sig = consume_sig(); // read signature
function->name_offset = 0; // ---- name
function->name_length = 0; // ---- name length
function->code_start_offset = off(pc_); // ---- code start
- function->code_end_offset = off(limit_); // ---- code end
+ function->code_end_offset = off(end_); // ---- code end
if (ok()) VerifyFunctionBody(0, module_env, function);
@@ -693,7 +680,7 @@ class ModuleDecoder : public Decoder {
WasmInitExpr DecodeInitExpr(const byte* start) {
pc_ = start;
- return consume_init_expr(nullptr, kAstStmt);
+ return consume_init_expr(nullptr, kWasmStmt);
}
private:
@@ -703,13 +690,32 @@ class ModuleDecoder : public Decoder {
uint32_t off(const byte* ptr) { return static_cast<uint32_t>(ptr - start_); }
+ bool AddTable(WasmModule* module) {
+ if (module->function_tables.size() > 0) {
+ error("At most one table is supported");
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ bool AddMemory(WasmModule* module) {
+ if (module->has_memory) {
+ error("At most one memory is supported");
+ return false;
+ } else {
+ module->has_memory = true;
+ return true;
+ }
+ }
+
// Decodes a single global entry inside a module starting at {pc_}.
void DecodeGlobalInModule(WasmModule* module, uint32_t index,
WasmGlobal* global) {
global->type = consume_value_type();
- global->mutability = consume_u8("mutability") != 0;
+ global->mutability = consume_mutability();
const byte* pos = pc();
- global->init = consume_init_expr(module, kAstStmt);
+ global->init = consume_init_expr(module, kWasmStmt);
switch (global->init.kind) {
case WasmInitExpr::kGlobalIndex: {
uint32_t other_index = global->init.val.global_index;
@@ -747,12 +753,12 @@ class ModuleDecoder : public Decoder {
void DecodeDataSegmentInModule(WasmModule* module, WasmDataSegment* segment) {
const byte* start = pc_;
expect_u8("linear memory index", 0);
- segment->dest_addr = consume_init_expr(module, kAstI32);
+ segment->dest_addr = consume_init_expr(module, kWasmI32);
segment->source_size = consume_u32v("source size");
segment->source_offset = static_cast<uint32_t>(pc_ - start_);
// Validate the data is in the module.
- uint32_t module_limit = static_cast<uint32_t>(limit_ - start_);
+ uint32_t module_limit = static_cast<uint32_t>(end_ - start_);
if (!IsWithinLimit(module_limit, segment->source_offset,
segment->source_size)) {
error(start, "segment out of bounds of module");
@@ -779,17 +785,19 @@ class ModuleDecoder : public Decoder {
}
// Verifies the body (code) of a given function.
- void VerifyFunctionBody(uint32_t func_num, ModuleEnv* menv,
+ void VerifyFunctionBody(uint32_t func_num, ModuleBytesEnv* menv,
WasmFunction* function) {
if (FLAG_trace_wasm_decoder || FLAG_trace_wasm_decode_time) {
OFStream os(stdout);
os << "Verifying WASM function " << WasmFunctionName(function, menv)
<< std::endl;
}
- FunctionBody body = {menv, function->sig, start_,
+ FunctionBody body = {function->sig, start_,
start_ + function->code_start_offset,
start_ + function->code_end_offset};
- DecodeResult result = VerifyWasmCode(module_zone->allocator(), body);
+ DecodeResult result =
+ VerifyWasmCode(module_zone->allocator(),
+ menv == nullptr ? nullptr : menv->module, body);
if (result.failed()) {
// Wrap the error message from the function decoder.
std::ostringstream str;
@@ -808,27 +816,26 @@ class ModuleDecoder : public Decoder {
}
}
+ uint32_t consume_string(uint32_t* length, bool validate_utf8) {
+ return consume_string(*this, length, validate_utf8);
+ }
+
// Reads a length-prefixed string, checking that it is within bounds. Returns
// the offset of the string, and the length as an out parameter.
- uint32_t consume_string(uint32_t* length, bool validate_utf8) {
- *length = consume_u32v("string length");
- uint32_t offset = pc_offset();
- const byte* string_start = pc_;
+ uint32_t consume_string(Decoder& decoder, uint32_t* length,
+ bool validate_utf8) {
+ *length = decoder.consume_u32v("string length");
+ uint32_t offset = decoder.pc_offset();
+ const byte* string_start = decoder.pc();
// Consume bytes before validation to guarantee that the string is not oob.
- if (*length > 0) consume_bytes(*length, "string");
- if (ok() && validate_utf8 &&
+ if (*length > 0) decoder.consume_bytes(*length, "string");
+ if (decoder.ok() && validate_utf8 &&
!unibrow::Utf8::Validate(string_start, *length)) {
- error(string_start, "no valid UTF-8 string");
+ decoder.error(string_start, "no valid UTF-8 string");
}
return offset;
}
- // Skips over a length-prefixed string, but checks that it is within bounds.
- void skip_string() {
- uint32_t length = consume_u32v("string length");
- consume_bytes(length, "string");
- }
-
uint32_t consume_sig_index(WasmModule* module, FunctionSig** sig) {
const byte* pos = pc_;
uint32_t sig_index = consume_u32v("signature index");
@@ -842,6 +849,17 @@ class ModuleDecoder : public Decoder {
return sig_index;
}
+ uint32_t consume_count(const char* name, size_t maximum) {
+ const byte* p = pc_;
+ uint32_t count = consume_u32v(name);
+ if (count > maximum) {
+ error(p, p, "%s of %u exceeds internal limit of %zu", name, count,
+ maximum);
+ return static_cast<uint32_t>(maximum);
+ }
+ return count;
+ }
+
uint32_t consume_func_index(WasmModule* module, WasmFunction** func) {
return consume_index("function index", module->functions, func);
}
@@ -912,7 +930,7 @@ class ModuleDecoder : public Decoder {
return true;
}
- WasmInitExpr consume_init_expr(WasmModule* module, LocalType expected) {
+ WasmInitExpr consume_init_expr(WasmModule* module, ValueType expected) {
const byte* pos = pc();
uint8_t opcode = consume_u8("opcode");
WasmInitExpr expr;
@@ -978,7 +996,7 @@ class ModuleDecoder : public Decoder {
if (!expect_u8("end opcode", kExprEnd)) {
expr.kind = WasmInitExpr::kNone;
}
- if (expected != kAstStmt && TypeOf(module, expr) != kAstI32) {
+ if (expected != kWasmStmt && TypeOf(module, expr) != kWasmI32) {
error(pos, pos, "type error in init expression, expected %s, got %s",
WasmOpcodes::TypeName(expected),
WasmOpcodes::TypeName(TypeOf(module, expr)));
@@ -986,29 +1004,36 @@ class ModuleDecoder : public Decoder {
return expr;
}
+ // Read a mutability flag
+ bool consume_mutability() {
+ byte val = consume_u8("mutability");
+ if (val > 1) error(pc_ - 1, "invalid mutability");
+ return val != 0;
+ }
+
// Reads a single 8-bit integer, interpreting it as a local type.
- LocalType consume_value_type() {
+ ValueType consume_value_type() {
byte val = consume_u8("value type");
- LocalTypeCode t = static_cast<LocalTypeCode>(val);
+ ValueTypeCode t = static_cast<ValueTypeCode>(val);
switch (t) {
case kLocalI32:
- return kAstI32;
+ return kWasmI32;
case kLocalI64:
- return kAstI64;
+ return kWasmI64;
case kLocalF32:
- return kAstF32;
+ return kWasmF32;
case kLocalF64:
- return kAstF64;
+ return kWasmF64;
case kLocalS128:
if (origin_ != kAsmJsOrigin && FLAG_wasm_simd_prototype) {
- return kAstS128;
+ return kWasmS128;
} else {
error(pc_ - 1, "invalid local type");
- return kAstStmt;
+ return kWasmStmt;
}
default:
error(pc_ - 1, "invalid local type");
- return kAstStmt;
+ return kWasmStmt;
}
}
@@ -1016,35 +1041,32 @@ class ModuleDecoder : public Decoder {
FunctionSig* consume_sig() {
if (!expect_u8("type form", kWasmFunctionTypeForm)) return nullptr;
// parse parameter types
- uint32_t param_count = consume_u32v("param count");
- std::vector<LocalType> params;
+ uint32_t param_count =
+ consume_count("param count", kV8MaxWasmFunctionParams);
+ if (failed()) return nullptr;
+ std::vector<ValueType> params;
for (uint32_t i = 0; ok() && i < param_count; ++i) {
- LocalType param = consume_value_type();
+ ValueType param = consume_value_type();
params.push_back(param);
}
// parse return types
- const byte* pt = pc_;
- uint32_t return_count = consume_u32v("return count");
- if (return_count > kMaxReturnCount) {
- error(pt, pt, "return count of %u exceeds maximum of %u", return_count,
- kMaxReturnCount);
- return nullptr;
- }
- std::vector<LocalType> returns;
+ const size_t max_return_count = FLAG_wasm_mv_prototype
+ ? kV8MaxWasmFunctionMultiReturns
+ : kV8MaxWasmFunctionReturns;
+ uint32_t return_count = consume_count("return count", max_return_count);
+ if (failed()) return nullptr;
+ std::vector<ValueType> returns;
for (uint32_t i = 0; ok() && i < return_count; ++i) {
- LocalType ret = consume_value_type();
+ ValueType ret = consume_value_type();
returns.push_back(ret);
}
- if (failed()) {
- // Decoding failed, return void -> void
- return new (module_zone) FunctionSig(0, 0, nullptr);
- }
+ if (failed()) return nullptr;
// FunctionSig stores the return types first.
- LocalType* buffer =
- module_zone->NewArray<LocalType>(param_count + return_count);
+ ValueType* buffer =
+ module_zone->NewArray<ValueType>(param_count + return_count);
uint32_t b = 0;
for (uint32_t i = 0; i < return_count; ++i) buffer[b++] = returns[i];
for (uint32_t i = 0; i < param_count; ++i) buffer[b++] = params[i];
@@ -1113,16 +1135,16 @@ ModuleResult DecodeWasmModule(Isolate* isolate, const byte* module_start,
isolate->counters()->wasm_decode_module_time());
size_t size = module_end - module_start;
if (module_start > module_end) return ModuleError("start > end");
- if (size >= kMaxModuleSize) return ModuleError("size > maximum module size");
+ if (size >= kV8MaxWasmModuleSize)
+ return ModuleError("size > maximum module size");
// TODO(bradnelson): Improve histogram handling of size_t.
isolate->counters()->wasm_module_size_bytes()->AddSample(
static_cast<int>(size));
// Signatures are stored in zone memory, which have the same lifetime
// as the {module}.
Zone* zone = new Zone(isolate->allocator(), ZONE_NAME);
- WasmModule* module = new WasmModule(zone, module_start);
ModuleDecoder decoder(zone, module_start, module_end, origin);
- ModuleResult result = decoder.DecodeModule(module, verify_functions);
+ ModuleResult result = decoder.DecodeModule(verify_functions);
// TODO(bradnelson): Improve histogram handling of size_t.
// TODO(titzer): this isn't accurate, since it doesn't count the data
// allocated on the C++ heap.
@@ -1146,14 +1168,14 @@ WasmInitExpr DecodeWasmInitExprForTesting(const byte* start, const byte* end) {
}
FunctionResult DecodeWasmFunction(Isolate* isolate, Zone* zone,
- ModuleEnv* module_env,
+ ModuleBytesEnv* module_env,
const byte* function_start,
const byte* function_end) {
HistogramTimerScope wasm_decode_function_time_scope(
isolate->counters()->wasm_decode_function_time());
size_t size = function_end - function_start;
if (function_start > function_end) return FunctionError("start > end");
- if (size > kMaxFunctionSize)
+ if (size > kV8MaxWasmFunctionSize)
return FunctionError("size > maximum function size");
isolate->counters()->wasm_function_size_bytes()->AddSample(
static_cast<int>(size));
@@ -1208,22 +1230,31 @@ AsmJsOffsetsResult DecodeAsmJsOffsets(const byte* tables_start,
for (uint32_t i = 0; i < functions_count && decoder.ok(); ++i) {
uint32_t size = decoder.consume_u32v("table size");
if (size == 0) {
- table.push_back(std::vector<std::pair<int, int>>());
+ table.push_back(std::vector<AsmJsOffsetEntry>());
continue;
}
if (!decoder.checkAvailable(size)) {
decoder.error("illegal asm function offset table size");
}
const byte* table_end = decoder.pc() + size;
- uint32_t locals_size = decoder.consume_u32("locals size");
+ uint32_t locals_size = decoder.consume_u32v("locals size");
+ int function_start_position = decoder.consume_u32v("function start pos");
int last_byte_offset = locals_size;
- int last_asm_position = 0;
- std::vector<std::pair<int, int>> func_asm_offsets;
+ int last_asm_position = function_start_position;
+ std::vector<AsmJsOffsetEntry> func_asm_offsets;
func_asm_offsets.reserve(size / 4); // conservative estimation
+ // Add an entry for the stack check, associated with position 0.
+ func_asm_offsets.push_back(
+ {0, function_start_position, function_start_position});
while (decoder.ok() && decoder.pc() < table_end) {
last_byte_offset += decoder.consume_u32v("byte offset delta");
- last_asm_position += decoder.consume_i32v("asm position delta");
- func_asm_offsets.push_back({last_byte_offset, last_asm_position});
+ int call_position =
+ last_asm_position + decoder.consume_i32v("call position delta");
+ int to_number_position =
+ call_position + decoder.consume_i32v("to_number position delta");
+ last_asm_position = to_number_position;
+ func_asm_offsets.push_back(
+ {last_byte_offset, call_position, to_number_position});
}
if (decoder.pc() != table_end) {
decoder.error("broken asm offset table");
@@ -1235,6 +1266,36 @@ AsmJsOffsetsResult DecodeAsmJsOffsets(const byte* tables_start,
return decoder.toResult(std::move(table));
}
+std::vector<CustomSectionOffset> DecodeCustomSections(const byte* start,
+ const byte* end) {
+ Decoder decoder(start, end);
+ decoder.consume_bytes(4, "wasm magic");
+ decoder.consume_bytes(4, "wasm version");
+
+ std::vector<CustomSectionOffset> result;
+
+ while (decoder.more()) {
+ byte section_code = decoder.consume_u8("section code");
+ uint32_t section_length = decoder.consume_u32v("section length");
+ uint32_t section_start = decoder.pc_offset();
+ if (section_code != 0) {
+ // Skip known sections.
+ decoder.consume_bytes(section_length, "section bytes");
+ continue;
+ }
+ uint32_t name_length = decoder.consume_u32v("name length");
+ uint32_t name_offset = decoder.pc_offset();
+ decoder.consume_bytes(name_length, "section name");
+ uint32_t payload_offset = decoder.pc_offset();
+ uint32_t payload_length = section_length - (payload_offset - section_start);
+ decoder.consume_bytes(payload_length);
+ result.push_back({section_start, name_offset, name_length, payload_offset,
+ payload_length, section_length});
+ }
+
+ return result;
+}
+
} // namespace wasm
} // namespace internal
} // namespace v8
diff --git a/deps/v8/src/wasm/module-decoder.h b/deps/v8/src/wasm/module-decoder.h
index 7cf5cfe3c1..982fbc9189 100644
--- a/deps/v8/src/wasm/module-decoder.h
+++ b/deps/v8/src/wasm/module-decoder.h
@@ -6,7 +6,7 @@
#define V8_WASM_MODULE_DECODER_H_
#include "src/globals.h"
-#include "src/wasm/ast-decoder.h"
+#include "src/wasm/function-body-decoder.h"
#include "src/wasm/wasm-module.h"
#include "src/wasm/wasm-result.h"
@@ -18,7 +18,12 @@ typedef Result<const WasmModule*> ModuleResult;
typedef Result<WasmFunction*> FunctionResult;
typedef std::vector<std::pair<int, int>> FunctionOffsets;
typedef Result<FunctionOffsets> FunctionOffsetsResult;
-typedef std::vector<std::vector<std::pair<int, int>>> AsmJsOffsets;
+struct AsmJsOffsetEntry {
+ int byte_offset;
+ int source_position_call;
+ int source_position_number_conversion;
+};
+typedef std::vector<std::vector<AsmJsOffsetEntry>> AsmJsOffsets;
typedef Result<AsmJsOffsets> AsmJsOffsetsResult;
// Decodes the bytes of a WASM module between {module_start} and {module_end}.
@@ -37,7 +42,8 @@ V8_EXPORT_PRIVATE FunctionSig* DecodeWasmSignatureForTesting(Zone* zone,
// Decodes the bytes of a WASM function between
// {function_start} and {function_end}.
V8_EXPORT_PRIVATE FunctionResult DecodeWasmFunction(Isolate* isolate,
- Zone* zone, ModuleEnv* env,
+ Zone* zone,
+ ModuleBytesEnv* env,
const byte* function_start,
const byte* function_end);
@@ -50,6 +56,18 @@ FunctionOffsetsResult DecodeWasmFunctionOffsets(const byte* module_start,
V8_EXPORT_PRIVATE WasmInitExpr DecodeWasmInitExprForTesting(const byte* start,
const byte* end);
+struct CustomSectionOffset {
+ uint32_t section_start;
+ uint32_t name_offset;
+ uint32_t name_length;
+ uint32_t payload_offset;
+ uint32_t payload_length;
+ uint32_t section_length;
+};
+
+V8_EXPORT_PRIVATE std::vector<CustomSectionOffset> DecodeCustomSections(
+ const byte* start, const byte* end);
+
// Extracts the mapping from wasm byte offset to asm.js source position per
// function.
// Returns a vector of vectors with <byte_offset, source_position> entries, or
diff --git a/deps/v8/src/wasm/wasm-debug.cc b/deps/v8/src/wasm/wasm-debug.cc
index 11c2ef8aa5..6cb865d59c 100644
--- a/deps/v8/src/wasm/wasm-debug.cc
+++ b/deps/v8/src/wasm/wasm-debug.cc
@@ -2,144 +2,273 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "src/assembler-inl.h"
#include "src/assert-scope.h"
+#include "src/compiler/wasm-compiler.h"
#include "src/debug/debug.h"
#include "src/factory.h"
+#include "src/frames-inl.h"
#include "src/isolate.h"
#include "src/wasm/module-decoder.h"
+#include "src/wasm/wasm-interpreter.h"
+#include "src/wasm/wasm-limits.h"
#include "src/wasm/wasm-module.h"
#include "src/wasm/wasm-objects.h"
+#include "src/zone/accounting-allocator.h"
using namespace v8::internal;
using namespace v8::internal::wasm;
namespace {
-enum {
- kWasmDebugInfoWasmObj,
- kWasmDebugInfoWasmBytesHash,
- kWasmDebugInfoAsmJsOffsets,
- kWasmDebugInfoNumEntries
-};
+class InterpreterHandle {
+ AccountingAllocator allocator_;
+ WasmInstance instance_;
+ WasmInterpreter interpreter_;
-// TODO(clemensh): Move asm.js offset tables to the compiled module.
-FixedArray *GetAsmJsOffsetTables(Handle<WasmDebugInfo> debug_info,
- Isolate *isolate) {
- Object *offset_tables = debug_info->get(kWasmDebugInfoAsmJsOffsets);
- if (!offset_tables->IsUndefined(isolate)) {
- return FixedArray::cast(offset_tables);
+ public:
+ // Initialize in the right order, using helper methods to make this possible.
+ // WasmInterpreter has to be allocated in place, since it is not movable.
+ InterpreterHandle(Isolate* isolate, WasmDebugInfo* debug_info)
+ : instance_(debug_info->wasm_instance()->compiled_module()->module()),
+ interpreter_(GetBytesEnv(&instance_, debug_info), &allocator_) {
+ Handle<JSArrayBuffer> mem_buffer =
+ handle(debug_info->wasm_instance()->memory_buffer(), isolate);
+ if (mem_buffer->IsUndefined(isolate)) {
+ DCHECK_EQ(0, instance_.module->min_mem_pages);
+ instance_.mem_start = nullptr;
+ instance_.mem_size = 0;
+ } else {
+ instance_.mem_start =
+ reinterpret_cast<byte*>(mem_buffer->backing_store());
+ CHECK(mem_buffer->byte_length()->ToUint32(&instance_.mem_size));
+ }
}
- Handle<JSObject> wasm_instance(debug_info->wasm_instance(), isolate);
- Handle<WasmCompiledModule> compiled_module(GetCompiledModule(*wasm_instance),
- isolate);
- DCHECK(compiled_module->has_asm_js_offset_tables());
-
- AsmJsOffsetsResult asm_offsets;
- {
- Handle<ByteArray> asm_offset_tables =
- compiled_module->asm_js_offset_tables();
- DisallowHeapAllocation no_gc;
- const byte *bytes_start = asm_offset_tables->GetDataStartAddress();
- const byte *bytes_end = bytes_start + asm_offset_tables->length();
- asm_offsets = wasm::DecodeAsmJsOffsets(bytes_start, bytes_end);
+ static ModuleBytesEnv GetBytesEnv(WasmInstance* instance,
+ WasmDebugInfo* debug_info) {
+ // Return raw pointer into heap. The WasmInterpreter will make its own copy
+ // of this data anyway, and there is no heap allocation in-between.
+ SeqOneByteString* bytes_str =
+ debug_info->wasm_instance()->compiled_module()->module_bytes();
+ Vector<const byte> bytes(bytes_str->GetChars(), bytes_str->length());
+ return ModuleBytesEnv(instance->module, instance, bytes);
}
- // Wasm bytes must be valid and must contain asm.js offset table.
- DCHECK(asm_offsets.ok());
- DCHECK_GE(static_cast<size_t>(kMaxInt), asm_offsets.val.size());
- int num_functions = static_cast<int>(asm_offsets.val.size());
- DCHECK_EQ(
- wasm::GetNumberOfFunctions(handle(debug_info->wasm_instance())),
- static_cast<int>(num_functions +
- compiled_module->module()->num_imported_functions));
- Handle<FixedArray> all_tables =
- isolate->factory()->NewFixedArray(num_functions);
- debug_info->set(kWasmDebugInfoAsmJsOffsets, *all_tables);
- for (int func = 0; func < num_functions; ++func) {
- std::vector<std::pair<int, int>> &func_asm_offsets = asm_offsets.val[func];
- if (func_asm_offsets.empty()) continue;
- size_t array_size = 2 * kIntSize * func_asm_offsets.size();
- CHECK_LE(array_size, static_cast<size_t>(kMaxInt));
- ByteArray *arr =
- *isolate->factory()->NewByteArray(static_cast<int>(array_size));
- all_tables->set(func, arr);
- int idx = 0;
- for (std::pair<int, int> p : func_asm_offsets) {
- // Byte offsets must be strictly monotonously increasing:
- DCHECK(idx == 0 || p.first > arr->get_int(idx - 2));
- arr->set_int(idx++, p.first);
- arr->set_int(idx++, p.second);
+
+ WasmInterpreter* interpreter() { return &interpreter_; }
+ const WasmModule* module() { return instance_.module; }
+
+ void Execute(uint32_t func_index, uint8_t* arg_buffer) {
+ DCHECK_GE(module()->functions.size(), func_index);
+ FunctionSig* sig = module()->functions[func_index].sig;
+ DCHECK_GE(kMaxInt, sig->parameter_count());
+ int num_params = static_cast<int>(sig->parameter_count());
+ ScopedVector<WasmVal> wasm_args(num_params);
+ uint8_t* arg_buf_ptr = arg_buffer;
+ for (int i = 0; i < num_params; ++i) {
+ uint32_t param_size = 1 << ElementSizeLog2Of(sig->GetParam(i));
+#define CASE_ARG_TYPE(type, ctype) \
+ case type: \
+ DCHECK_EQ(param_size, sizeof(ctype)); \
+ wasm_args[i] = WasmVal(*reinterpret_cast<ctype*>(arg_buf_ptr)); \
+ break;
+ switch (sig->GetParam(i)) {
+ CASE_ARG_TYPE(kWasmI32, uint32_t)
+ CASE_ARG_TYPE(kWasmI64, uint64_t)
+ CASE_ARG_TYPE(kWasmF32, float)
+ CASE_ARG_TYPE(kWasmF64, double)
+#undef CASE_ARG_TYPE
+ default:
+ UNREACHABLE();
+ }
+ arg_buf_ptr += param_size;
}
- DCHECK_EQ(arr->length(), idx * kIntSize);
+
+ WasmInterpreter::Thread* thread = interpreter_.GetThread(0);
+ // We do not support reentering an already running interpreter at the moment
+ // (like INTERPRETER -> JS -> WASM -> INTERPRETER).
+ DCHECK(thread->state() == WasmInterpreter::STOPPED ||
+ thread->state() == WasmInterpreter::FINISHED);
+ thread->Reset();
+ thread->PushFrame(&module()->functions[func_index], wasm_args.start());
+ WasmInterpreter::State state;
+ do {
+ state = thread->Run();
+ switch (state) {
+ case WasmInterpreter::State::PAUSED: {
+ // We hit a breakpoint.
+ // TODO(clemensh): Handle this.
+ } break;
+ case WasmInterpreter::State::FINISHED:
+ // Perfect, just break the switch and exit the loop.
+ break;
+ case WasmInterpreter::State::TRAPPED:
+ // TODO(clemensh): Generate appropriate JS exception.
+ UNIMPLEMENTED();
+ break;
+ // STOPPED and RUNNING should never occur here.
+ case WasmInterpreter::State::STOPPED:
+ case WasmInterpreter::State::RUNNING:
+ default:
+ UNREACHABLE();
+ }
+ } while (state != WasmInterpreter::State::FINISHED);
+
+ // Copy back the return value
+ DCHECK_GE(kV8MaxWasmFunctionReturns, sig->return_count());
+ // TODO(wasm): Handle multi-value returns.
+ DCHECK_EQ(1, kV8MaxWasmFunctionReturns);
+ if (sig->return_count()) {
+ WasmVal ret_val = thread->GetReturnValue(0);
+#define CASE_RET_TYPE(type, ctype) \
+ case type: \
+ DCHECK_EQ(1 << ElementSizeLog2Of(sig->GetReturn(0)), sizeof(ctype)); \
+ *reinterpret_cast<ctype*>(arg_buffer) = ret_val.to<ctype>(); \
+ break;
+ switch (sig->GetReturn(0)) {
+ CASE_RET_TYPE(kWasmI32, uint32_t)
+ CASE_RET_TYPE(kWasmI64, uint64_t)
+ CASE_RET_TYPE(kWasmF32, float)
+ CASE_RET_TYPE(kWasmF64, double)
+#undef CASE_RET_TYPE
+ default:
+ UNREACHABLE();
+ }
+ }
+ }
+};
+
+InterpreterHandle* GetOrCreateInterpreterHandle(
+ Isolate* isolate, Handle<WasmDebugInfo> debug_info) {
+ Handle<Object> handle(debug_info->get(WasmDebugInfo::kInterpreterHandle),
+ isolate);
+ if (handle->IsUndefined(isolate)) {
+ InterpreterHandle* cpp_handle = new InterpreterHandle(isolate, *debug_info);
+ handle = Managed<InterpreterHandle>::New(isolate, cpp_handle);
+ debug_info->set(WasmDebugInfo::kInterpreterHandle, *handle);
}
- return *all_tables;
+
+ return Handle<Managed<InterpreterHandle>>::cast(handle)->get();
+}
+
+int GetNumFunctions(WasmInstanceObject* instance) {
+ size_t num_functions =
+ instance->compiled_module()->module()->functions.size();
+ DCHECK_GE(kMaxInt, num_functions);
+ return static_cast<int>(num_functions);
+}
+
+Handle<FixedArray> GetOrCreateInterpretedFunctions(
+ Isolate* isolate, Handle<WasmDebugInfo> debug_info) {
+ Handle<Object> obj(debug_info->get(WasmDebugInfo::kInterpretedFunctions),
+ isolate);
+ if (!obj->IsUndefined(isolate)) return Handle<FixedArray>::cast(obj);
+
+ Handle<FixedArray> new_arr = isolate->factory()->NewFixedArray(
+ GetNumFunctions(debug_info->wasm_instance()));
+ debug_info->set(WasmDebugInfo::kInterpretedFunctions, *new_arr);
+ return new_arr;
}
-} // namespace
-Handle<WasmDebugInfo> WasmDebugInfo::New(Handle<JSObject> wasm) {
- Isolate *isolate = wasm->GetIsolate();
- Factory *factory = isolate->factory();
- Handle<FixedArray> arr =
- factory->NewFixedArray(kWasmDebugInfoNumEntries, TENURED);
- arr->set(kWasmDebugInfoWasmObj, *wasm);
- int hash = 0;
- Handle<SeqOneByteString> wasm_bytes = GetWasmBytes(wasm);
- {
- DisallowHeapAllocation no_gc;
- hash = StringHasher::HashSequentialString(
- wasm_bytes->GetChars(), wasm_bytes->length(), kZeroHashSeed);
+void RedirectCallsitesInCode(Code* code, Code* old_target, Code* new_target) {
+ DisallowHeapAllocation no_gc;
+ for (RelocIterator it(code, RelocInfo::kCodeTargetMask); !it.done();
+ it.next()) {
+ DCHECK(RelocInfo::IsCodeTarget(it.rinfo()->rmode()));
+ Code* target = Code::GetCodeFromTargetAddress(it.rinfo()->target_address());
+ if (target != old_target) continue;
+ it.rinfo()->set_target_address(new_target->instruction_start());
}
- Handle<Object> hash_obj = factory->NewNumberFromInt(hash, TENURED);
- arr->set(kWasmDebugInfoWasmBytesHash, *hash_obj);
+}
+
+void RedirectCallsitesInInstance(Isolate* isolate, WasmInstanceObject* instance,
+ Code* old_target, Code* new_target) {
+ DisallowHeapAllocation no_gc;
+ // Redirect all calls in wasm functions.
+ FixedArray* code_table = instance->compiled_module()->ptr_to_code_table();
+ for (int i = 0, e = GetNumFunctions(instance); i < e; ++i) {
+ RedirectCallsitesInCode(Code::cast(code_table->get(i)), old_target,
+ new_target);
+ }
+
+ // Redirect all calls in exported functions.
+ FixedArray* weak_exported_functions =
+ instance->compiled_module()->ptr_to_weak_exported_functions();
+ for (int i = 0, e = weak_exported_functions->length(); i != e; ++i) {
+ WeakCell* weak_function = WeakCell::cast(weak_exported_functions->get(i));
+ if (weak_function->cleared()) continue;
+ Code* code = JSFunction::cast(weak_function->value())->code();
+ RedirectCallsitesInCode(code, old_target, new_target);
+ }
+}
+
+void EnsureRedirectToInterpreter(Isolate* isolate,
+ Handle<WasmDebugInfo> debug_info,
+ int func_index) {
+ Handle<FixedArray> interpreted_functions =
+ GetOrCreateInterpretedFunctions(isolate, debug_info);
+ if (!interpreted_functions->get(func_index)->IsUndefined(isolate)) return;
+
+ Handle<WasmInstanceObject> instance(debug_info->wasm_instance(), isolate);
+ Handle<Code> new_code = compiler::CompileWasmInterpreterEntry(
+ isolate, func_index,
+ instance->compiled_module()->module()->functions[func_index].sig,
+ instance);
+
+ Handle<FixedArray> code_table = instance->compiled_module()->code_table();
+ Handle<Code> old_code(Code::cast(code_table->get(func_index)), isolate);
+ interpreted_functions->set(func_index, *new_code);
+ RedirectCallsitesInInstance(isolate, *instance, *old_code, *new_code);
+}
+
+} // namespace
+
+Handle<WasmDebugInfo> WasmDebugInfo::New(Handle<WasmInstanceObject> instance) {
+ Isolate* isolate = instance->GetIsolate();
+ Factory* factory = isolate->factory();
+ Handle<FixedArray> arr = factory->NewFixedArray(kFieldCount, TENURED);
+ arr->set(kInstance, *instance);
return Handle<WasmDebugInfo>::cast(arr);
}
-bool WasmDebugInfo::IsDebugInfo(Object *object) {
+bool WasmDebugInfo::IsDebugInfo(Object* object) {
if (!object->IsFixedArray()) return false;
- FixedArray *arr = FixedArray::cast(object);
- return arr->length() == kWasmDebugInfoNumEntries &&
- IsWasmInstance(arr->get(kWasmDebugInfoWasmObj)) &&
- arr->get(kWasmDebugInfoWasmBytesHash)->IsNumber();
+ FixedArray* arr = FixedArray::cast(object);
+ if (arr->length() != kFieldCount) return false;
+ if (!IsWasmInstance(arr->get(kInstance))) return false;
+ Isolate* isolate = arr->GetIsolate();
+ if (!arr->get(kInterpreterHandle)->IsUndefined(isolate) &&
+ !arr->get(kInterpreterHandle)->IsForeign())
+ return false;
+ return true;
}
-WasmDebugInfo *WasmDebugInfo::cast(Object *object) {
+WasmDebugInfo* WasmDebugInfo::cast(Object* object) {
DCHECK(IsDebugInfo(object));
- return reinterpret_cast<WasmDebugInfo *>(object);
+ return reinterpret_cast<WasmDebugInfo*>(object);
}
-JSObject *WasmDebugInfo::wasm_instance() {
- return JSObject::cast(get(kWasmDebugInfoWasmObj));
+WasmInstanceObject* WasmDebugInfo::wasm_instance() {
+ return WasmInstanceObject::cast(get(kInstance));
}
-int WasmDebugInfo::GetAsmJsSourcePosition(Handle<WasmDebugInfo> debug_info,
- int func_index, int byte_offset) {
- Isolate *isolate = debug_info->GetIsolate();
- Handle<JSObject> instance(debug_info->wasm_instance(), isolate);
- FixedArray *offset_tables = GetAsmJsOffsetTables(debug_info, isolate);
-
- WasmCompiledModule *compiled_module = wasm::GetCompiledModule(*instance);
- int num_imported_functions =
- compiled_module->module()->num_imported_functions;
- DCHECK_LE(num_imported_functions, func_index);
- func_index -= num_imported_functions;
- DCHECK_LT(func_index, offset_tables->length());
- ByteArray *offset_table = ByteArray::cast(offset_tables->get(func_index));
-
- // Binary search for the current byte offset.
- int left = 0; // inclusive
- int right = offset_table->length() / kIntSize / 2; // exclusive
- DCHECK_LT(left, right);
- while (right - left > 1) {
- int mid = left + (right - left) / 2;
- if (offset_table->get_int(2 * mid) <= byte_offset) {
- left = mid;
- } else {
- right = mid;
- }
- }
- // There should be an entry for each position that could show up on the stack
- // trace:
- DCHECK_EQ(byte_offset, offset_table->get_int(2 * left));
- return offset_table->get_int(2 * left + 1);
+void WasmDebugInfo::SetBreakpoint(Handle<WasmDebugInfo> debug_info,
+ int func_index, int offset) {
+ Isolate* isolate = debug_info->GetIsolate();
+ InterpreterHandle* handle = GetOrCreateInterpreterHandle(isolate, debug_info);
+ WasmInterpreter* interpreter = handle->interpreter();
+ DCHECK_LE(0, func_index);
+ DCHECK_GT(handle->module()->functions.size(), func_index);
+ const WasmFunction* func = &handle->module()->functions[func_index];
+ interpreter->SetBreakpoint(func, offset, true);
+ EnsureRedirectToInterpreter(isolate, debug_info, func_index);
+}
+
+void WasmDebugInfo::RunInterpreter(Handle<WasmDebugInfo> debug_info,
+ int func_index, uint8_t* arg_buffer) {
+ DCHECK_LE(0, func_index);
+ InterpreterHandle* interp_handle =
+ GetOrCreateInterpreterHandle(debug_info->GetIsolate(), debug_info);
+ interp_handle->Execute(static_cast<uint32_t>(func_index), arg_buffer);
}
diff --git a/deps/v8/src/wasm/wasm-external-refs.cc b/deps/v8/src/wasm/wasm-external-refs.cc
index 4c4c91b29c..e982cc7f99 100644
--- a/deps/v8/src/wasm/wasm-external-refs.cc
+++ b/deps/v8/src/wasm/wasm-external-refs.cc
@@ -208,6 +208,19 @@ void float64_pow_wrapper(double* param0, double* param1) {
double y = ReadDoubleValue(param1);
WriteDoubleValue(param0, Pow(x, y));
}
+
+static WasmTrapCallbackForTesting wasm_trap_callback_for_testing = nullptr;
+
+void set_trap_callback_for_testing(WasmTrapCallbackForTesting callback) {
+ wasm_trap_callback_for_testing = callback;
+}
+
+void call_trap_callback_for_testing() {
+ if (wasm_trap_callback_for_testing) {
+ wasm_trap_callback_for_testing();
+ }
+}
+
} // namespace wasm
} // namespace internal
} // namespace v8
diff --git a/deps/v8/src/wasm/wasm-external-refs.h b/deps/v8/src/wasm/wasm-external-refs.h
index d9539ce71a..04337b99ec 100644
--- a/deps/v8/src/wasm/wasm-external-refs.h
+++ b/deps/v8/src/wasm/wasm-external-refs.h
@@ -61,6 +61,12 @@ uint32_t word64_popcnt_wrapper(uint64_t* input);
void float64_pow_wrapper(double* param0, double* param1);
+typedef void (*WasmTrapCallbackForTesting)();
+
+void set_trap_callback_for_testing(WasmTrapCallbackForTesting callback);
+
+void call_trap_callback_for_testing();
+
} // namespace wasm
} // namespace internal
} // namespace v8
diff --git a/deps/v8/src/wasm/wasm-interpreter.cc b/deps/v8/src/wasm/wasm-interpreter.cc
index 6e049ffd25..ac125caf7e 100644
--- a/deps/v8/src/wasm/wasm-interpreter.cc
+++ b/deps/v8/src/wasm/wasm-interpreter.cc
@@ -5,9 +5,10 @@
#include "src/wasm/wasm-interpreter.h"
#include "src/utils.h"
-#include "src/wasm/ast-decoder.h"
#include "src/wasm/decoder.h"
+#include "src/wasm/function-body-decoder.h"
#include "src/wasm/wasm-external-refs.h"
+#include "src/wasm/wasm-limits.h"
#include "src/wasm/wasm-module.h"
#include "src/zone/accounting-allocator.h"
@@ -62,6 +63,7 @@ namespace wasm {
V(I64GtS, int64_t, >) \
V(I64GeS, int64_t, >=) \
V(F32Add, float, +) \
+ V(F32Sub, float, -) \
V(F32Eq, float, ==) \
V(F32Ne, float, !=) \
V(F32Lt, float, <) \
@@ -69,6 +71,7 @@ namespace wasm {
V(F32Gt, float, >) \
V(F32Ge, float, >=) \
V(F64Add, double, +) \
+ V(F64Sub, double, -) \
V(F64Eq, double, ==) \
V(F64Ne, double, !=) \
V(F64Lt, double, <) \
@@ -101,13 +104,11 @@ namespace wasm {
V(I32Rol, int32_t) \
V(I64Ror, int64_t) \
V(I64Rol, int64_t) \
- V(F32Sub, float) \
V(F32Min, float) \
V(F32Max, float) \
V(F32CopySign, float) \
V(F64Min, double) \
V(F64Max, double) \
- V(F64Sub, double) \
V(F64CopySign, double) \
V(I32AsmjsDivS, int32_t) \
V(I32AsmjsDivU, uint32_t) \
@@ -158,8 +159,6 @@ namespace wasm {
V(F64UConvertI64, uint64_t) \
V(F64ConvertF32, float) \
V(F64ReinterpretI64, int64_t) \
- V(I32ReinterpretF32, float) \
- V(I64ReinterpretF64, double) \
V(I32AsmjsSConvertF32, float) \
V(I32AsmjsUConvertF32, float) \
V(I32AsmjsSConvertF64, double) \
@@ -293,41 +292,6 @@ static inline uint64_t ExecuteI64Rol(uint64_t a, uint64_t b, TrapReason* trap) {
return (a << shift) | (a >> (64 - shift));
}
-static float quiet(float a) {
- static const uint32_t kSignalingBit = 1 << 22;
- uint32_t q = bit_cast<uint32_t>(std::numeric_limits<float>::quiet_NaN());
- if ((q & kSignalingBit) != 0) {
- // On some machines, the signaling bit set indicates it's a quiet NaN.
- return bit_cast<float>(bit_cast<uint32_t>(a) | kSignalingBit);
- } else {
- // On others, the signaling bit set indicates it's a signaling NaN.
- return bit_cast<float>(bit_cast<uint32_t>(a) & ~kSignalingBit);
- }
-}
-
-static double quiet(double a) {
- static const uint64_t kSignalingBit = 1ULL << 51;
- uint64_t q = bit_cast<uint64_t>(std::numeric_limits<double>::quiet_NaN());
- if ((q & kSignalingBit) != 0) {
- // On some machines, the signaling bit set indicates it's a quiet NaN.
- return bit_cast<double>(bit_cast<uint64_t>(a) | kSignalingBit);
- } else {
- // On others, the signaling bit set indicates it's a signaling NaN.
- return bit_cast<double>(bit_cast<uint64_t>(a) & ~kSignalingBit);
- }
-}
-
-static inline float ExecuteF32Sub(float a, float b, TrapReason* trap) {
- float result = a - b;
- // Some architectures (e.g. MIPS) need extra checking to preserve the payload
- // of a NaN operand.
- if (result - result != 0) {
- if (std::isnan(a)) return quiet(a);
- if (std::isnan(b)) return quiet(b);
- }
- return result;
-}
-
static inline float ExecuteF32Min(float a, float b, TrapReason* trap) {
return JSMin(a, b);
}
@@ -340,17 +304,6 @@ static inline float ExecuteF32CopySign(float a, float b, TrapReason* trap) {
return copysignf(a, b);
}
-static inline double ExecuteF64Sub(double a, double b, TrapReason* trap) {
- double result = a - b;
- // Some architectures (e.g. MIPS) need extra checking to preserve the payload
- // of a NaN operand.
- if (result - result != 0) {
- if (std::isnan(a)) return quiet(a);
- if (std::isnan(b)) return quiet(b);
- }
- return result;
-}
-
static inline double ExecuteF64Min(double a, double b, TrapReason* trap) {
return JSMin(a, b);
}
@@ -651,19 +604,20 @@ static inline double ExecuteF64ReinterpretI64(int64_t a, TrapReason* trap) {
return bit_cast<double>(a);
}
-static inline int32_t ExecuteI32ReinterpretF32(float a, TrapReason* trap) {
- return bit_cast<int32_t>(a);
+static inline int32_t ExecuteI32ReinterpretF32(WasmVal a) {
+ return a.to_unchecked<int32_t>();
}
-static inline int64_t ExecuteI64ReinterpretF64(double a, TrapReason* trap) {
- return bit_cast<int64_t>(a);
+static inline int64_t ExecuteI64ReinterpretF64(WasmVal a) {
+ return a.to_unchecked<int64_t>();
}
static inline int32_t ExecuteGrowMemory(uint32_t delta_pages,
WasmInstance* instance) {
// TODO(ahaas): Move memory allocation to wasm-module.cc for better
// encapsulation.
- if (delta_pages > wasm::WasmModule::kV8MaxPages) {
+ if (delta_pages > wasm::kV8MaxWasmMemoryPages ||
+ delta_pages > instance->module->max_mem_pages) {
return -1;
}
uint32_t old_size = instance->mem_size;
@@ -679,8 +633,9 @@ static inline int32_t ExecuteGrowMemory(uint32_t delta_pages,
} else {
DCHECK_NOT_NULL(instance->mem_start);
new_size = old_size + delta_pages * wasm::WasmModule::kPageSize;
- if (new_size >
- wasm::WasmModule::kV8MaxPages * wasm::WasmModule::kPageSize) {
+ if (new_size / wasm::WasmModule::kPageSize > wasm::kV8MaxWasmMemoryPages ||
+ new_size / wasm::WasmModule::kPageSize >
+ instance->module->max_mem_pages) {
return -1;
}
new_mem_start = static_cast<byte*>(realloc(instance->mem_start, new_size));
@@ -721,8 +676,8 @@ class ControlTransfers : public ZoneObject {
public:
ControlTransferMap map_;
- ControlTransfers(Zone* zone, ModuleEnv* env, AstLocalDecls* locals,
- const byte* start, const byte* end)
+ ControlTransfers(Zone* zone, BodyLocalDecls* locals, const byte* start,
+ const byte* end)
: map_(zone) {
// Represents a control flow label.
struct CLabel : public ZoneObject {
@@ -872,7 +827,7 @@ class ControlTransfers : public ZoneObject {
// Code and metadata needed to execute a function.
struct InterpreterCode {
const WasmFunction* function; // wasm function
- AstLocalDecls locals; // local declarations
+ BodyLocalDecls locals; // local declarations
const byte* orig_start; // start of original code
const byte* orig_end; // end of original code
byte* start; // start of (maybe altered) code
@@ -890,14 +845,13 @@ class CodeMap {
const WasmModule* module_;
ZoneVector<InterpreterCode> interpreter_code_;
- CodeMap(const WasmModule* module, Zone* zone)
+ CodeMap(const WasmModule* module, const uint8_t* module_start, Zone* zone)
: zone_(zone), module_(module), interpreter_code_(zone) {
if (module == nullptr) return;
for (size_t i = 0; i < module->functions.size(); ++i) {
const WasmFunction* function = &module->functions[i];
- const byte* code_start =
- module->module_start + function->code_start_offset;
- const byte* code_end = module->module_start + function->code_end_offset;
+ const byte* code_start = module_start + function->code_start_offset;
+ const byte* code_end = module_start + function->code_end_offset;
AddFunction(function, code_start, code_end);
}
}
@@ -929,10 +883,9 @@ class CodeMap {
InterpreterCode* Preprocess(InterpreterCode* code) {
if (code->targets == nullptr && code->start) {
// Compute the control targets map and the local declarations.
- CHECK(DecodeLocalDecls(code->locals, code->start, code->end));
- ModuleEnv env = {module_, nullptr, kWasmOrigin};
+ CHECK(DecodeLocalDecls(&code->locals, code->start, code->end));
code->targets = new (zone_) ControlTransfers(
- zone_, &env, &code->locals, code->orig_start, code->orig_end);
+ zone_, &code->locals, code->orig_start, code->orig_end);
}
return code;
}
@@ -940,7 +893,7 @@ class CodeMap {
int AddFunction(const WasmFunction* function, const byte* code_start,
const byte* code_end) {
InterpreterCode code = {
- function, AstLocalDecls(zone_), code_start,
+ function, BodyLocalDecls(zone_), code_start,
code_end, const_cast<byte*>(code_start), const_cast<byte*>(code_end),
nullptr};
@@ -1072,7 +1025,7 @@ class ThreadImpl : public WasmInterpreter::Thread {
// Limit of parameters.
sp_t plimit() { return sp + code->function->sig->parameter_count(); }
// Limit of locals.
- sp_t llimit() { return plimit() + code->locals.total_local_count; }
+ sp_t llimit() { return plimit() + code->locals.type_list.size(); }
};
struct Block {
@@ -1121,28 +1074,28 @@ class ThreadImpl : public WasmInterpreter::Thread {
}
pc_t InitLocals(InterpreterCode* code) {
- for (auto p : code->locals.local_types) {
+ for (auto p : code->locals.type_list) {
WasmVal val;
- switch (p.first) {
- case kAstI32:
+ switch (p) {
+ case kWasmI32:
val = WasmVal(static_cast<int32_t>(0));
break;
- case kAstI64:
+ case kWasmI64:
val = WasmVal(static_cast<int64_t>(0));
break;
- case kAstF32:
+ case kWasmF32:
val = WasmVal(static_cast<float>(0));
break;
- case kAstF64:
+ case kWasmF64:
val = WasmVal(static_cast<double>(0));
break;
default:
UNREACHABLE();
break;
}
- stack_.insert(stack_.end(), p.second, val);
+ stack_.push_back(val);
}
- return code->locals.decls_encoded_size;
+ return code->locals.encoded_size;
}
void CommitPc(pc_t pc) {
@@ -1173,7 +1126,7 @@ class ThreadImpl : public WasmInterpreter::Thread {
}
bool DoReturn(InterpreterCode** code, pc_t* pc, pc_t* limit, size_t arity) {
- DCHECK_GT(frames_.size(), 0u);
+ DCHECK_GT(frames_.size(), 0);
// Pop all blocks for this frame.
while (!blocks_.empty() && blocks_.back().fp == frames_.size()) {
blocks_.pop_back();
@@ -1357,12 +1310,6 @@ class ThreadImpl : public WasmInterpreter::Thread {
blocks_.pop_back();
break;
}
- case kExprI8Const: {
- ImmI8Operand operand(&decoder, code->at(pc));
- Push(pc, WasmVal(operand.value));
- len = 1 + operand.length;
- break;
- }
case kExprI32Const: {
ImmI32Operand operand(&decoder, code->at(pc));
Push(pc, WasmVal(operand.value));
@@ -1450,15 +1397,15 @@ class ThreadImpl : public WasmInterpreter::Thread {
GlobalIndexOperand operand(&decoder, code->at(pc));
const WasmGlobal* global = &module()->globals[operand.index];
byte* ptr = instance()->globals_start + global->offset;
- LocalType type = global->type;
+ ValueType type = global->type;
WasmVal val;
- if (type == kAstI32) {
+ if (type == kWasmI32) {
val = WasmVal(*reinterpret_cast<int32_t*>(ptr));
- } else if (type == kAstI64) {
+ } else if (type == kWasmI64) {
val = WasmVal(*reinterpret_cast<int64_t*>(ptr));
- } else if (type == kAstF32) {
+ } else if (type == kWasmF32) {
val = WasmVal(*reinterpret_cast<float*>(ptr));
- } else if (type == kAstF64) {
+ } else if (type == kWasmF64) {
val = WasmVal(*reinterpret_cast<double*>(ptr));
} else {
UNREACHABLE();
@@ -1471,15 +1418,15 @@ class ThreadImpl : public WasmInterpreter::Thread {
GlobalIndexOperand operand(&decoder, code->at(pc));
const WasmGlobal* global = &module()->globals[operand.index];
byte* ptr = instance()->globals_start + global->offset;
- LocalType type = global->type;
+ ValueType type = global->type;
WasmVal val = Pop();
- if (type == kAstI32) {
+ if (type == kWasmI32) {
*reinterpret_cast<int32_t*>(ptr) = val.to<int32_t>();
- } else if (type == kAstI64) {
+ } else if (type == kWasmI64) {
*reinterpret_cast<int64_t*>(ptr) = val.to<int64_t>();
- } else if (type == kAstF32) {
+ } else if (type == kWasmF32) {
*reinterpret_cast<float*>(ptr) = val.to<float>();
- } else if (type == kAstF64) {
+ } else if (type == kWasmF64) {
*reinterpret_cast<double*>(ptr) = val.to<double>();
} else {
UNREACHABLE();
@@ -1605,6 +1552,19 @@ class ThreadImpl : public WasmInterpreter::Thread {
len = 1 + operand.length;
break;
}
+ // We need to treat kExprI32ReinterpretF32 and kExprI64ReinterpretF64
+ // specially to guarantee that the quiet bit of a NaN is preserved on
+ // ia32 by the reinterpret casts.
+ case kExprI32ReinterpretF32: {
+ WasmVal result(ExecuteI32ReinterpretF32(Pop()));
+ Push(pc, result);
+ break;
+ }
+ case kExprI64ReinterpretF64: {
+ WasmVal result(ExecuteI64ReinterpretF64(Pop()));
+ Push(pc, result);
+ break;
+ }
#define EXECUTE_SIMPLE_BINOP(name, ctype, op) \
case kExpr##name: { \
WasmVal rval = Pop(); \
@@ -1680,8 +1640,8 @@ class ThreadImpl : public WasmInterpreter::Thread {
}
WasmVal Pop() {
- DCHECK_GT(stack_.size(), 0u);
- DCHECK_GT(frames_.size(), 0u);
+ DCHECK_GT(stack_.size(), 0);
+ DCHECK_GT(frames_.size(), 0);
DCHECK_GT(stack_.size(), frames_.back().llimit()); // can't pop into locals
WasmVal val = stack_.back();
stack_.pop_back();
@@ -1689,8 +1649,8 @@ class ThreadImpl : public WasmInterpreter::Thread {
}
void PopN(int n) {
- DCHECK_GE(stack_.size(), static_cast<size_t>(n));
- DCHECK_GT(frames_.size(), 0u);
+ DCHECK_GE(stack_.size(), n);
+ DCHECK_GT(frames_.size(), 0);
size_t nsize = stack_.size() - n;
DCHECK_GE(nsize, frames_.back().llimit()); // can't pop into locals
stack_.resize(nsize);
@@ -1698,13 +1658,13 @@ class ThreadImpl : public WasmInterpreter::Thread {
WasmVal PopArity(size_t arity) {
if (arity == 0) return WasmVal();
- CHECK_EQ(1u, arity);
+ CHECK_EQ(1, arity);
return Pop();
}
void Push(pc_t pc, WasmVal val) {
// TODO(titzer): store PC as well?
- if (val.type != kAstStmt) stack_.push_back(val);
+ if (val.type != kWasmStmt) stack_.push_back(val);
}
void TraceStack(const char* phase, pc_t pc) {
@@ -1730,19 +1690,19 @@ class ThreadImpl : public WasmInterpreter::Thread {
PrintF(" s%zu:", i);
WasmVal val = stack_[i];
switch (val.type) {
- case kAstI32:
+ case kWasmI32:
PrintF("i32:%d", val.to<int32_t>());
break;
- case kAstI64:
+ case kWasmI64:
PrintF("i64:%" PRId64 "", val.to<int64_t>());
break;
- case kAstF32:
+ case kWasmF32:
PrintF("f32:%f", val.to<float>());
break;
- case kAstF64:
+ case kWasmF64:
PrintF("f64:%lf", val.to<double>());
break;
- case kAstStmt:
+ case kWasmStmt:
PrintF("void");
break;
default:
@@ -1760,14 +1720,19 @@ class ThreadImpl : public WasmInterpreter::Thread {
class WasmInterpreterInternals : public ZoneObject {
public:
WasmInstance* instance_;
+ // Create a copy of the module bytes for the interpreter, since the passed
+ // pointer might be invalidated after constructing the interpreter.
+ const ZoneVector<uint8_t> module_bytes_;
CodeMap codemap_;
ZoneVector<ThreadImpl*> threads_;
- WasmInterpreterInternals(Zone* zone, WasmInstance* instance)
- : instance_(instance),
- codemap_(instance_ ? instance_->module : nullptr, zone),
+ WasmInterpreterInternals(Zone* zone, const ModuleBytesEnv& env)
+ : instance_(env.instance),
+ module_bytes_(env.module_bytes.start(), env.module_bytes.end(), zone),
+ codemap_(env.instance ? env.instance->module : nullptr,
+ module_bytes_.data(), zone),
threads_(zone) {
- threads_.push_back(new ThreadImpl(zone, &codemap_, instance));
+ threads_.push_back(new ThreadImpl(zone, &codemap_, env.instance));
}
void Delete() {
@@ -1780,10 +1745,10 @@ class WasmInterpreterInternals : public ZoneObject {
//============================================================================
// Implementation of the public interface of the interpreter.
//============================================================================
-WasmInterpreter::WasmInterpreter(WasmInstance* instance,
+WasmInterpreter::WasmInterpreter(const ModuleBytesEnv& env,
AccountingAllocator* allocator)
: zone_(allocator, ZONE_NAME),
- internals_(new (&zone_) WasmInterpreterInternals(&zone_, instance)) {}
+ internals_(new (&zone_) WasmInterpreterInternals(&zone_, env)) {}
WasmInterpreter::~WasmInterpreter() { internals_->Delete(); }
@@ -1797,7 +1762,7 @@ bool WasmInterpreter::SetBreakpoint(const WasmFunction* function, pc_t pc,
if (!code) return false;
size_t size = static_cast<size_t>(code->end - code->start);
// Check bounds for {pc}.
- if (pc < code->locals.decls_encoded_size || pc >= size) return false;
+ if (pc < code->locals.encoded_size || pc >= size) return false;
// Make a copy of the code before enabling a breakpoint.
if (enabled && code->orig_start == code->start) {
code->start = reinterpret_cast<byte*>(zone_.New(size));
@@ -1818,7 +1783,7 @@ bool WasmInterpreter::GetBreakpoint(const WasmFunction* function, pc_t pc) {
if (!code) return false;
size_t size = static_cast<size_t>(code->end - code->start);
// Check bounds for {pc}.
- if (pc < code->locals.decls_encoded_size || pc >= size) return false;
+ if (pc < code->locals.encoded_size || pc >= size) return false;
// Check if a breakpoint is present at that place in the code.
return code->start[pc] == kInternalBreakpoint;
}
@@ -1841,14 +1806,14 @@ WasmVal WasmInterpreter::GetLocalVal(const WasmFrame* frame, int index) {
CHECK_GE(index, 0);
UNIMPLEMENTED();
WasmVal none;
- none.type = kAstStmt;
+ none.type = kWasmStmt;
return none;
}
WasmVal WasmInterpreter::GetExprVal(const WasmFrame* frame, int pc) {
UNIMPLEMENTED();
WasmVal none;
- none.type = kAstStmt;
+ none.type = kWasmStmt;
return none;
}
@@ -1885,7 +1850,7 @@ bool WasmInterpreter::SetFunctionCodeForTesting(const WasmFunction* function,
ControlTransferMap WasmInterpreter::ComputeControlTransfersForTesting(
Zone* zone, const byte* start, const byte* end) {
- ControlTransfers targets(zone, nullptr, nullptr, start, end);
+ ControlTransfers targets(zone, nullptr, start, end);
return targets.map_;
}
diff --git a/deps/v8/src/wasm/wasm-interpreter.h b/deps/v8/src/wasm/wasm-interpreter.h
index 360362b994..80e6c4ba79 100644
--- a/deps/v8/src/wasm/wasm-interpreter.h
+++ b/deps/v8/src/wasm/wasm-interpreter.h
@@ -17,8 +17,8 @@ namespace internal {
namespace wasm {
// forward declarations.
+struct ModuleBytesEnv;
struct WasmFunction;
-struct WasmInstance;
class WasmInterpreterInternals;
typedef size_t pc_t;
@@ -32,23 +32,23 @@ typedef ZoneMap<pc_t, pcdiff_t> ControlTransferMap;
// Macro for defining union members.
#define FOREACH_UNION_MEMBER(V) \
- V(i32, kAstI32, int32_t) \
- V(u32, kAstI32, uint32_t) \
- V(i64, kAstI64, int64_t) \
- V(u64, kAstI64, uint64_t) \
- V(f32, kAstF32, float) \
- V(f64, kAstF64, double)
+ V(i32, kWasmI32, int32_t) \
+ V(u32, kWasmI32, uint32_t) \
+ V(i64, kWasmI64, int64_t) \
+ V(u64, kWasmI64, uint64_t) \
+ V(f32, kWasmF32, float) \
+ V(f64, kWasmF64, double)
// Representation of values within the interpreter.
struct WasmVal {
- LocalType type;
+ ValueType type;
union {
#define DECLARE_FIELD(field, localtype, ctype) ctype field;
FOREACH_UNION_MEMBER(DECLARE_FIELD)
#undef DECLARE_FIELD
} val;
- WasmVal() : type(kAstStmt) {}
+ WasmVal() : type(kWasmStmt) {}
#define DECLARE_CONSTRUCTOR(field, localtype, ctype) \
explicit WasmVal(ctype v) : type(localtype) { val.field = v; }
@@ -56,13 +56,22 @@ struct WasmVal {
#undef DECLARE_CONSTRUCTOR
template <typename T>
- T to() {
+ inline T to() {
+ UNREACHABLE();
+ }
+
+ template <typename T>
+ inline T to_unchecked() {
UNREACHABLE();
}
};
#define DECLARE_CAST(field, localtype, ctype) \
template <> \
+ inline ctype WasmVal::to_unchecked() { \
+ return val.field; \
+ } \
+ template <> \
inline ctype WasmVal::to() { \
CHECK_EQ(localtype, type); \
return val.field; \
@@ -70,11 +79,6 @@ struct WasmVal {
FOREACH_UNION_MEMBER(DECLARE_CAST)
#undef DECLARE_CAST
-template <>
-inline void WasmVal::to() {
- CHECK_EQ(kAstStmt, type);
-}
-
// Representation of frames within the interpreter.
class WasmFrame {
public:
@@ -135,7 +139,7 @@ class V8_EXPORT_PRIVATE WasmInterpreter {
bool GetBreakpoint(const WasmFunction* function, int pc);
};
- WasmInterpreter(WasmInstance* instance, AccountingAllocator* allocator);
+ WasmInterpreter(const ModuleBytesEnv& env, AccountingAllocator* allocator);
~WasmInterpreter();
//==========================================================================
diff --git a/deps/v8/src/wasm/wasm-js.cc b/deps/v8/src/wasm/wasm-js.cc
index 0e030a28c4..b426d5bf3d 100644
--- a/deps/v8/src/wasm/wasm-js.cc
+++ b/deps/v8/src/wasm/wasm-js.cc
@@ -18,6 +18,7 @@
#include "src/wasm/module-decoder.h"
#include "src/wasm/wasm-js.h"
+#include "src/wasm/wasm-limits.h"
#include "src/wasm/wasm-module.h"
#include "src/wasm/wasm-objects.h"
#include "src/wasm/wasm-result.h"
@@ -28,13 +29,12 @@ using v8::internal::wasm::ErrorThrower;
namespace v8 {
-enum WasmMemoryObjectData {
- kWasmMemoryBuffer,
- kWasmMemoryMaximum,
- kWasmMemoryInstanceObject
-};
-
namespace {
+
+#define RANGE_ERROR_MSG \
+ "Wasm compilation exceeds internal limits in this context for the provided " \
+ "arguments"
+
i::Handle<i::String> v8_str(i::Isolate* isolate, const char* str) {
return isolate->factory()->NewStringFromAsciiChecked(str);
}
@@ -48,6 +48,100 @@ struct RawBuffer {
size_t size() { return static_cast<size_t>(end - start); }
};
+bool IsCompilationAllowed(i::Isolate* isolate, ErrorThrower* thrower,
+ v8::Local<v8::Value> source, bool is_async) {
+ // Allow caller to do one final check on thrower state, rather than
+ // one at each step. No information is lost - failure reason is captured
+ // in the thrower state.
+ if (thrower->error()) return false;
+
+ AllowWasmCompileCallback callback = isolate->allow_wasm_compile_callback();
+ if (callback != nullptr &&
+ !callback(reinterpret_cast<v8::Isolate*>(isolate), source, is_async)) {
+ thrower->RangeError(RANGE_ERROR_MSG);
+ return false;
+ }
+ return true;
+}
+
+bool IsInstantiationAllowed(i::Isolate* isolate, ErrorThrower* thrower,
+ v8::Local<v8::Value> module_or_bytes,
+ i::MaybeHandle<i::JSReceiver> ffi, bool is_async) {
+ // Allow caller to do one final check on thrower state, rather than
+ // one at each step. No information is lost - failure reason is captured
+ // in the thrower state.
+ if (thrower->error()) return false;
+ v8::MaybeLocal<v8::Value> v8_ffi;
+ if (!ffi.is_null()) {
+ v8_ffi = v8::Local<v8::Value>::Cast(Utils::ToLocal(ffi.ToHandleChecked()));
+ }
+ AllowWasmInstantiateCallback callback =
+ isolate->allow_wasm_instantiate_callback();
+ if (callback != nullptr &&
+ !callback(reinterpret_cast<v8::Isolate*>(isolate), module_or_bytes,
+ v8_ffi, is_async)) {
+ thrower->RangeError(RANGE_ERROR_MSG);
+ return false;
+ }
+ return true;
+}
+
+i::wasm::ModuleWireBytes GetFirstArgumentAsBytes(
+ const v8::FunctionCallbackInfo<v8::Value>& args, ErrorThrower* thrower) {
+ if (args.Length() < 1) {
+ thrower->TypeError("Argument 0 must be a buffer source");
+ return i::wasm::ModuleWireBytes(nullptr, nullptr);
+ }
+
+ const byte* start = nullptr;
+ size_t length = 0;
+ v8::Local<v8::Value> source = args[0];
+ if (source->IsArrayBuffer()) {
+ // A raw array buffer was passed.
+ Local<ArrayBuffer> buffer = Local<ArrayBuffer>::Cast(source);
+ ArrayBuffer::Contents contents = buffer->GetContents();
+
+ start = reinterpret_cast<const byte*>(contents.Data());
+ length = contents.ByteLength();
+ } else if (source->IsTypedArray()) {
+ // A TypedArray was passed.
+ Local<TypedArray> array = Local<TypedArray>::Cast(source);
+ Local<ArrayBuffer> buffer = array->Buffer();
+
+ ArrayBuffer::Contents contents = buffer->GetContents();
+
+ start =
+ reinterpret_cast<const byte*>(contents.Data()) + array->ByteOffset();
+ length = array->ByteLength();
+ } else {
+ thrower->TypeError("Argument 0 must be a buffer source");
+ }
+ DCHECK_IMPLIES(length, start != nullptr);
+ if (length == 0) {
+ thrower->CompileError("BufferSource argument is empty");
+ }
+ if (length > i::wasm::kV8MaxWasmModuleSize) {
+ thrower->RangeError("buffer source exceeds maximum size of %zu (is %zu)",
+ i::wasm::kV8MaxWasmModuleSize, length);
+ }
+ if (thrower->error()) return i::wasm::ModuleWireBytes(nullptr, nullptr);
+ // TODO(titzer): use the handle as well?
+ return i::wasm::ModuleWireBytes(start, start + length);
+}
+
+i::MaybeHandle<i::JSReceiver> GetSecondArgumentAsImports(
+ const v8::FunctionCallbackInfo<v8::Value>& args, ErrorThrower* thrower) {
+ if (args.Length() < 2) return {};
+ if (args[1]->IsUndefined()) return {};
+
+ if (!args[1]->IsObject()) {
+ thrower->TypeError("Argument 1 must be an object");
+ return {};
+ }
+ Local<Object> obj = Local<Object>::Cast(args[1]);
+ return i::Handle<i::JSReceiver>::cast(v8::Utils::OpenHandle(*obj));
+}
+
RawBuffer GetRawBufferSource(
v8::Local<v8::Value> source, ErrorThrower* thrower) {
const byte* start = nullptr;
@@ -61,9 +155,6 @@ RawBuffer GetRawBufferSource(
start = reinterpret_cast<const byte*>(contents.Data());
end = start + contents.ByteLength();
- if (start == nullptr || end == start) {
- thrower->CompileError("ArrayBuffer argument is empty");
- }
} else if (source->IsTypedArray()) {
// A TypedArray was passed.
Local<TypedArray> array = Local<TypedArray>::Cast(source);
@@ -75,13 +166,12 @@ RawBuffer GetRawBufferSource(
reinterpret_cast<const byte*>(contents.Data()) + array->ByteOffset();
end = start + array->ByteLength();
- if (start == nullptr || end == start) {
- thrower->TypeError("ArrayBuffer argument is empty");
- }
} else {
- thrower->TypeError("Argument 0 must be an ArrayBuffer or Uint8Array");
+ thrower->TypeError("Argument 0 must be a buffer source");
+ }
+ if (start == nullptr || end == start) {
+ thrower->CompileError("BufferSource argument is empty");
}
-
return {start, end};
}
@@ -97,7 +187,7 @@ static i::MaybeHandle<i::WasmModuleObject> CreateModuleObject(
DCHECK(source->IsArrayBuffer() || source->IsTypedArray());
return i::wasm::CreateModuleObjectFromBytes(
i_isolate, buffer.start, buffer.end, thrower, i::wasm::kWasmOrigin,
- i::Handle<i::Script>::null(), nullptr, nullptr);
+ i::Handle<i::Script>::null(), i::Vector<const byte>::empty());
}
static bool ValidateModule(v8::Isolate* isolate,
@@ -115,6 +205,17 @@ static bool ValidateModule(v8::Isolate* isolate,
i::wasm::ModuleOrigin::kWasmOrigin);
}
+// TODO(wasm): move brand check to the respective types, and don't throw
+// in it, rather, use a provided ErrorThrower, or let caller handle it.
+static bool BrandCheck(Isolate* isolate, i::Handle<i::Object> value,
+ i::Handle<i::Symbol> sym) {
+ if (!value->IsJSObject()) return false;
+ i::Handle<i::JSObject> object = i::Handle<i::JSObject>::cast(value);
+ Maybe<bool> has_brand = i::JSObject::HasOwnProperty(object, sym);
+ if (has_brand.IsNothing()) return false;
+ return has_brand.ToChecked();
+}
+
static bool BrandCheck(Isolate* isolate, i::Handle<i::Object> value,
i::Handle<i::Symbol> sym, const char* msg) {
if (value->IsJSObject()) {
@@ -130,27 +231,36 @@ static bool BrandCheck(Isolate* isolate, i::Handle<i::Object> value,
void WebAssemblyCompile(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::Isolate* isolate = args.GetIsolate();
+ i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
HandleScope scope(isolate);
ErrorThrower thrower(reinterpret_cast<i::Isolate*>(isolate),
"WebAssembly.compile()");
+ Local<Context> context = isolate->GetCurrentContext();
+ v8::Local<v8::Promise::Resolver> resolver;
+ if (!v8::Promise::Resolver::New(context).ToLocal(&resolver)) return;
+ v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
+ return_value.Set(resolver->GetPromise());
+
if (args.Length() < 1) {
thrower.TypeError("Argument 0 must be a buffer source");
+ resolver->Reject(context, Utils::ToLocal(thrower.Reify()));
+ return;
+ }
+ auto bytes = GetFirstArgumentAsBytes(args, &thrower);
+ USE(bytes);
+ if (!IsCompilationAllowed(i_isolate, &thrower, args[0], true)) {
+ resolver->Reject(context, Utils::ToLocal(thrower.Reify()));
return;
}
i::MaybeHandle<i::JSObject> module_obj =
CreateModuleObject(isolate, args[0], &thrower);
- Local<Context> context = isolate->GetCurrentContext();
- v8::Local<v8::Promise::Resolver> resolver;
- if (!v8::Promise::Resolver::New(context).ToLocal(&resolver)) return;
if (thrower.error()) {
resolver->Reject(context, Utils::ToLocal(thrower.Reify()));
} else {
resolver->Resolve(context, Utils::ToLocal(module_obj.ToHandleChecked()));
}
- v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
- return_value.Set(resolver->GetPromise());
}
void WebAssemblyValidate(const v8::FunctionCallbackInfo<v8::Value>& args) {
@@ -168,12 +278,14 @@ void WebAssemblyValidate(const v8::FunctionCallbackInfo<v8::Value>& args) {
if (ValidateModule(isolate, args[0], &thrower)) {
return_value.Set(v8::True(isolate));
} else {
+ if (thrower.wasm_error()) thrower.Reify(); // Clear error.
return_value.Set(v8::False(isolate));
}
}
void WebAssemblyModule(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::Isolate* isolate = args.GetIsolate();
+ i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
HandleScope scope(isolate);
ErrorThrower thrower(reinterpret_cast<i::Isolate*>(isolate),
"WebAssembly.Module()");
@@ -182,6 +294,10 @@ void WebAssemblyModule(const v8::FunctionCallbackInfo<v8::Value>& args) {
thrower.TypeError("Argument 0 must be a buffer source");
return;
}
+ auto bytes = GetFirstArgumentAsBytes(args, &thrower);
+ USE(bytes);
+ if (!IsCompilationAllowed(i_isolate, &thrower, args[0], false)) return;
+
i::MaybeHandle<i::JSObject> module_obj =
CreateModuleObject(isolate, args[0], &thrower);
if (module_obj.is_null()) return;
@@ -190,16 +306,49 @@ void WebAssemblyModule(const v8::FunctionCallbackInfo<v8::Value>& args) {
return_value.Set(Utils::ToLocal(module_obj.ToHandleChecked()));
}
-void WebAssemblyInstance(const v8::FunctionCallbackInfo<v8::Value>& args) {
- HandleScope scope(args.GetIsolate());
- v8::Isolate* isolate = args.GetIsolate();
- i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
+MaybeLocal<Value> InstantiateModuleImpl(
+ i::Isolate* i_isolate, i::Handle<i::WasmModuleObject> i_module_obj,
+ const v8::FunctionCallbackInfo<v8::Value>& args, ErrorThrower* thrower) {
+ // It so happens that in both the WebAssembly.instantiate, as well as
+ // WebAssembly.Instance ctor, the positions of the ffi object and memory
+ // are the same. If that changes later, we refactor the consts into
+ // parameters.
+ static const int kFfiOffset = 1;
- ErrorThrower thrower(i_isolate, "WebAssembly.Instance()");
+ MaybeLocal<Value> nothing;
+ i::Handle<i::JSReceiver> ffi = i::Handle<i::JSObject>::null();
+ // This is a first - level validation of the argument. If present, we only
+ // check its type. {Instantiate} will further check that if the module
+ // has imports, the argument must be present, as well as piecemeal
+ // import satisfaction.
+ if (args.Length() > kFfiOffset && !args[kFfiOffset]->IsUndefined()) {
+ if (!args[kFfiOffset]->IsObject()) {
+ thrower->TypeError("Argument %d must be an object", kFfiOffset);
+ return nothing;
+ }
+ Local<Object> obj = Local<Object>::Cast(args[kFfiOffset]);
+ ffi = i::Handle<i::JSReceiver>::cast(v8::Utils::OpenHandle(*obj));
+ }
+
+ i::MaybeHandle<i::JSObject> instance =
+ i::wasm::WasmModule::Instantiate(i_isolate, thrower, i_module_obj, ffi);
+ if (instance.is_null()) {
+ if (!thrower->error())
+ thrower->RuntimeError("Could not instantiate module");
+ return nothing;
+ }
+ DCHECK(!i_isolate->has_pending_exception());
+ return Utils::ToLocal(instance.ToHandleChecked());
+}
+namespace {
+i::MaybeHandle<i::WasmModuleObject> GetFirstArgumentAsModule(
+ const v8::FunctionCallbackInfo<v8::Value>& args, ErrorThrower& thrower) {
+ v8::Isolate* isolate = args.GetIsolate();
+ i::MaybeHandle<i::WasmModuleObject> nothing;
if (args.Length() < 1) {
thrower.TypeError("Argument 0 must be a WebAssembly.Module");
- return;
+ return nothing;
}
Local<Context> context = isolate->GetCurrentContext();
@@ -207,60 +356,210 @@ void WebAssemblyInstance(const v8::FunctionCallbackInfo<v8::Value>& args) {
if (!BrandCheck(isolate, Utils::OpenHandle(*args[0]),
i::Handle<i::Symbol>(i_context->wasm_module_sym()),
"Argument 0 must be a WebAssembly.Module")) {
- return;
+ return nothing;
}
- Local<Object> obj = Local<Object>::Cast(args[0]);
- i::Handle<i::JSObject> i_obj =
- i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj));
+ Local<Object> module_obj = Local<Object>::Cast(args[0]);
+ return i::Handle<i::WasmModuleObject>::cast(
+ v8::Utils::OpenHandle(*module_obj));
+}
+} // namespace
- i::Handle<i::JSReceiver> ffi = i::Handle<i::JSObject>::null();
- if (args.Length() > 1 && args[1]->IsObject()) {
- Local<Object> obj = Local<Object>::Cast(args[1]);
- ffi = i::Handle<i::JSReceiver>::cast(v8::Utils::OpenHandle(*obj));
+void WebAssemblyModuleImports(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ HandleScope scope(args.GetIsolate());
+ v8::Isolate* isolate = args.GetIsolate();
+ i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
+ ErrorThrower thrower(i_isolate, "WebAssembly.Module.imports()");
+
+ auto maybe_module = GetFirstArgumentAsModule(args, thrower);
+
+ if (!maybe_module.is_null()) {
+ auto imports =
+ i::wasm::GetImports(i_isolate, maybe_module.ToHandleChecked());
+ args.GetReturnValue().Set(Utils::ToLocal(imports));
}
+}
- i::Handle<i::JSArrayBuffer> memory = i::Handle<i::JSArrayBuffer>::null();
- if (args.Length() > 2 && args[2]->IsObject()) {
- Local<Object> obj = Local<Object>::Cast(args[2]);
- i::Handle<i::Object> mem_obj = v8::Utils::OpenHandle(*obj);
- if (i::WasmJs::IsWasmMemoryObject(i_isolate, mem_obj)) {
- memory = i::Handle<i::JSArrayBuffer>(
- i::Handle<i::WasmMemoryObject>::cast(mem_obj)->get_buffer(),
- i_isolate);
- } else {
- thrower.TypeError("Argument 2 must be a WebAssembly.Memory");
+void WebAssemblyModuleExports(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ HandleScope scope(args.GetIsolate());
+ v8::Isolate* isolate = args.GetIsolate();
+ i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
+
+ ErrorThrower thrower(i_isolate, "WebAssembly.Module.exports()");
+
+ auto maybe_module = GetFirstArgumentAsModule(args, thrower);
+
+ if (!maybe_module.is_null()) {
+ auto exports =
+ i::wasm::GetExports(i_isolate, maybe_module.ToHandleChecked());
+ args.GetReturnValue().Set(Utils::ToLocal(exports));
+ }
+}
+
+void WebAssemblyModuleCustomSections(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ HandleScope scope(args.GetIsolate());
+ v8::Isolate* isolate = args.GetIsolate();
+ i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
+
+ ErrorThrower thrower(i_isolate, "WebAssembly.Module.customSections()");
+
+ auto maybe_module = GetFirstArgumentAsModule(args, thrower);
+
+ if (args.Length() < 2) {
+ thrower.TypeError("Argument 1 must be a string");
+ return;
+ }
+
+ i::Handle<i::Object> name = Utils::OpenHandle(*args[1]);
+ if (!name->IsString()) {
+ thrower.TypeError("Argument 1 must be a string");
+ return;
+ }
+
+ if (!maybe_module.is_null()) {
+ auto custom_sections =
+ i::wasm::GetCustomSections(i_isolate, maybe_module.ToHandleChecked(),
+ i::Handle<i::String>::cast(name), &thrower);
+ if (!thrower.error()) {
+ args.GetReturnValue().Set(Utils::ToLocal(custom_sections));
}
}
- i::MaybeHandle<i::JSObject> instance =
- i::wasm::WasmModule::Instantiate(i_isolate, &thrower, i_obj, ffi, memory);
- if (instance.is_null()) {
- if (!thrower.error()) thrower.RuntimeError("Could not instantiate module");
+}
+
+void WebAssemblyInstance(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ HandleScope scope(args.GetIsolate());
+ v8::Isolate* isolate = args.GetIsolate();
+ i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
+
+ ErrorThrower thrower(i_isolate, "WebAssembly.Instance()");
+
+ auto maybe_module = GetFirstArgumentAsModule(args, thrower);
+ if (thrower.error()) return;
+ auto maybe_imports = GetSecondArgumentAsImports(args, &thrower);
+ if (!IsInstantiationAllowed(i_isolate, &thrower, args[0], maybe_imports,
+ false)) {
return;
}
- DCHECK(!i_isolate->has_pending_exception());
+ DCHECK(!thrower.error());
+
+ if (!maybe_module.is_null()) {
+ MaybeLocal<Value> instance = InstantiateModuleImpl(
+ i_isolate, maybe_module.ToHandleChecked(), args, &thrower);
+ if (instance.IsEmpty()) {
+ DCHECK(thrower.error());
+ return;
+ }
+ args.GetReturnValue().Set(instance.ToLocalChecked());
+ }
+}
+
+void WebAssemblyInstantiate(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ v8::Isolate* isolate = args.GetIsolate();
+ i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
+
+ HandleScope scope(isolate);
+ ErrorThrower thrower(i_isolate, "WebAssembly.instantiate()");
+
+ Local<Context> context = isolate->GetCurrentContext();
+ i::Handle<i::Context> i_context = Utils::OpenHandle(*context);
+
+ v8::Local<v8::Promise::Resolver> resolver;
+ if (!v8::Promise::Resolver::New(context).ToLocal(&resolver)) return;
v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
- return_value.Set(Utils::ToLocal(instance.ToHandleChecked()));
+ return_value.Set(resolver->GetPromise());
+
+ if (args.Length() < 1) {
+ thrower.TypeError(
+ "Argument 0 must be provided and must be either a buffer source or a "
+ "WebAssembly.Module object");
+ resolver->Reject(context, Utils::ToLocal(thrower.Reify()));
+ return;
+ }
+
+ i::Handle<i::Object> first_arg = Utils::OpenHandle(*args[0]);
+ if (!first_arg->IsJSObject()) {
+ thrower.TypeError(
+ "Argument 0 must be a buffer source or a WebAssembly.Module object");
+ resolver->Reject(context, Utils::ToLocal(thrower.Reify()));
+ return;
+ }
+
+ bool want_pair = !BrandCheck(
+ isolate, first_arg, i::Handle<i::Symbol>(i_context->wasm_module_sym()));
+ auto maybe_imports = GetSecondArgumentAsImports(args, &thrower);
+ if (thrower.error()) {
+ resolver->Reject(context, Utils::ToLocal(thrower.Reify()));
+ return;
+ }
+ if (!IsInstantiationAllowed(i_isolate, &thrower, args[0], maybe_imports,
+ true)) {
+ resolver->Reject(context, Utils::ToLocal(thrower.Reify()));
+ return;
+ }
+ i::Handle<i::WasmModuleObject> module_obj;
+ if (want_pair) {
+ i::MaybeHandle<i::WasmModuleObject> maybe_module_obj =
+ CreateModuleObject(isolate, args[0], &thrower);
+ if (!maybe_module_obj.ToHandle(&module_obj)) {
+ DCHECK(thrower.error());
+ resolver->Reject(context, Utils::ToLocal(thrower.Reify()));
+ return;
+ }
+ } else {
+ module_obj = i::Handle<i::WasmModuleObject>::cast(first_arg);
+ }
+ DCHECK(!module_obj.is_null());
+ MaybeLocal<Value> instance =
+ InstantiateModuleImpl(i_isolate, module_obj, args, &thrower);
+ if (instance.IsEmpty()) {
+ DCHECK(thrower.error());
+ resolver->Reject(context, Utils::ToLocal(thrower.Reify()));
+ } else {
+ DCHECK(!thrower.error());
+ Local<Value> retval;
+ if (want_pair) {
+ i::Handle<i::JSFunction> object_function = i::Handle<i::JSFunction>(
+ i_isolate->native_context()->object_function(), i_isolate);
+
+ i::Handle<i::JSObject> i_retval =
+ i_isolate->factory()->NewJSObject(object_function, i::TENURED);
+ i::Handle<i::String> module_property_name =
+ i_isolate->factory()->InternalizeUtf8String("module");
+ i::Handle<i::String> instance_property_name =
+ i_isolate->factory()->InternalizeUtf8String("instance");
+ i::JSObject::AddProperty(i_retval, module_property_name, module_obj,
+ i::NONE);
+ i::JSObject::AddProperty(i_retval, instance_property_name,
+ Utils::OpenHandle(*instance.ToLocalChecked()),
+ i::NONE);
+ retval = Utils::ToLocal(i_retval);
+ } else {
+ retval = instance.ToLocalChecked();
+ }
+ DCHECK(!retval.IsEmpty());
+ resolver->Resolve(context, retval);
+ }
}
bool GetIntegerProperty(v8::Isolate* isolate, ErrorThrower* thrower,
Local<Context> context, Local<v8::Object> object,
- Local<String> property, int* result, int lower_bound,
- int upper_bound) {
+ Local<String> property, int* result,
+ int64_t lower_bound, uint64_t upper_bound) {
v8::MaybeLocal<v8::Value> maybe = object->Get(context, property);
v8::Local<v8::Value> value;
if (maybe.ToLocal(&value)) {
int64_t number;
if (!value->IntegerValue(context).To(&number)) return false;
- if (number < static_cast<int64_t>(lower_bound)) {
+ if (number < lower_bound) {
thrower->RangeError("Property value %" PRId64
- " is below the lower bound %d",
+ " is below the lower bound %" PRIx64,
number, lower_bound);
return false;
}
if (number > static_cast<int64_t>(upper_bound)) {
thrower->RangeError("Property value %" PRId64
- " is above the upper bound %d",
+ " is above the upper bound %" PRIu64,
number, upper_bound);
return false;
}
@@ -270,8 +569,6 @@ bool GetIntegerProperty(v8::Isolate* isolate, ErrorThrower* thrower,
return false;
}
-const int max_table_size = 1 << 26;
-
void WebAssemblyTable(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::Isolate* isolate = args.GetIsolate();
HandleScope scope(isolate);
@@ -299,28 +596,23 @@ void WebAssemblyTable(const v8::FunctionCallbackInfo<v8::Value>& args) {
}
}
// The descriptor's 'initial'.
- int initial;
+ int initial = 0;
if (!GetIntegerProperty(isolate, &thrower, context, descriptor,
v8_str(isolate, "initial"), &initial, 0,
- max_table_size)) {
+ i::wasm::kV8MaxWasmTableSize)) {
return;
}
// The descriptor's 'maximum'.
- int maximum = 0;
+ int maximum = -1;
Local<String> maximum_key = v8_str(isolate, "maximum");
Maybe<bool> has_maximum = descriptor->Has(context, maximum_key);
- if (has_maximum.IsNothing()) {
- // There has been an exception, just return.
- return;
- }
- if (has_maximum.FromJust()) {
+ if (!has_maximum.IsNothing() && has_maximum.FromJust()) {
if (!GetIntegerProperty(isolate, &thrower, context, descriptor, maximum_key,
- &maximum, initial, max_table_size)) {
+ &maximum, initial,
+ i::wasm::kSpecMaxWasmTableSize)) {
return;
}
- } else {
- maximum = static_cast<int>(i::wasm::WasmModule::kV8MaxTableSize);
}
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
@@ -335,7 +627,7 @@ void WebAssemblyMemory(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::Isolate* isolate = args.GetIsolate();
HandleScope scope(isolate);
ErrorThrower thrower(reinterpret_cast<i::Isolate*>(isolate),
- "WebAssembly.Module()");
+ "WebAssembly.Memory()");
if (args.Length() < 1 || !args[0]->IsObject()) {
thrower.TypeError("Argument 0 must be a memory descriptor");
return;
@@ -343,35 +635,35 @@ void WebAssemblyMemory(const v8::FunctionCallbackInfo<v8::Value>& args) {
Local<Context> context = isolate->GetCurrentContext();
Local<v8::Object> descriptor = args[0]->ToObject(context).ToLocalChecked();
// The descriptor's 'initial'.
- int initial;
+ int initial = 0;
if (!GetIntegerProperty(isolate, &thrower, context, descriptor,
- v8_str(isolate, "initial"), &initial, 0, 65536)) {
+ v8_str(isolate, "initial"), &initial, 0,
+ i::wasm::kV8MaxWasmMemoryPages)) {
return;
}
// The descriptor's 'maximum'.
- int maximum = 0;
+ int maximum = -1;
Local<String> maximum_key = v8_str(isolate, "maximum");
Maybe<bool> has_maximum = descriptor->Has(context, maximum_key);
- if (has_maximum.IsNothing()) {
- // There has been an exception, just return.
- return;
- }
- if (has_maximum.FromJust()) {
+ if (!has_maximum.IsNothing() && has_maximum.FromJust()) {
if (!GetIntegerProperty(isolate, &thrower, context, descriptor, maximum_key,
- &maximum, initial, 65536)) {
+ &maximum, initial,
+ i::wasm::kSpecMaxWasmMemoryPages)) {
return;
}
}
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
- i::Handle<i::JSArrayBuffer> buffer =
- i_isolate->factory()->NewJSArrayBuffer(i::SharedFlag::kNotShared);
size_t size = static_cast<size_t>(i::wasm::WasmModule::kPageSize) *
static_cast<size_t>(initial);
- i::JSArrayBuffer::SetupAllocatingData(buffer, i_isolate, size);
-
- i::Handle<i::JSObject> memory_obj = i::WasmMemoryObject::New(
- i_isolate, buffer, has_maximum.FromJust() ? maximum : -1);
+ i::Handle<i::JSArrayBuffer> buffer =
+ i::wasm::NewArrayBuffer(i_isolate, size, i::FLAG_wasm_guard_pages);
+ if (buffer.is_null()) {
+ thrower.RangeError("could not allocate memory");
+ return;
+ }
+ i::Handle<i::JSObject> memory_obj =
+ i::WasmMemoryObject::New(i_isolate, buffer, maximum);
args.GetReturnValue().Set(Utils::ToLocal(memory_obj));
}
@@ -404,7 +696,7 @@ void WebAssemblyTableGrow(const v8::FunctionCallbackInfo<v8::Value>& args) {
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
auto receiver =
i::Handle<i::WasmTableObject>::cast(Utils::OpenHandle(*args.This()));
- i::Handle<i::FixedArray> old_array(receiver->get_functions(), i_isolate);
+ i::Handle<i::FixedArray> old_array(receiver->functions(), i_isolate);
int old_size = old_array->length();
int64_t new_size64 = 0;
if (args.Length() > 0 && !args[0]->IntegerValue(context).To(&new_size64)) {
@@ -412,14 +704,23 @@ void WebAssemblyTableGrow(const v8::FunctionCallbackInfo<v8::Value>& args) {
}
new_size64 += old_size;
- if (new_size64 < old_size || new_size64 > receiver->maximum_length()) {
+ int64_t max_size64 = receiver->maximum_length();
+ if (max_size64 < 0 ||
+ max_size64 > static_cast<int64_t>(i::wasm::kV8MaxWasmTableSize)) {
+ max_size64 = i::wasm::kV8MaxWasmTableSize;
+ }
+
+ if (new_size64 < old_size || new_size64 > max_size64) {
v8::Local<v8::Value> e = v8::Exception::RangeError(
v8_str(isolate, new_size64 < old_size ? "trying to shrink table"
: "maximum table size exceeded"));
isolate->ThrowException(e);
return;
}
+
int new_size = static_cast<int>(new_size64);
+ i::WasmTableObject::Grow(i_isolate, receiver,
+ static_cast<uint32_t>(new_size - old_size));
if (new_size != old_size) {
i::Handle<i::FixedArray> new_array =
@@ -430,7 +731,9 @@ void WebAssemblyTableGrow(const v8::FunctionCallbackInfo<v8::Value>& args) {
receiver->set_functions(*new_array);
}
- // TODO(titzer): update relevant instances.
+ // TODO(gdeepti): use weak links for instances
+ v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
+ return_value.Set(old_size);
}
void WebAssemblyTableGet(const v8::FunctionCallbackInfo<v8::Value>& args) {
@@ -446,7 +749,7 @@ void WebAssemblyTableGet(const v8::FunctionCallbackInfo<v8::Value>& args) {
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
auto receiver =
i::Handle<i::WasmTableObject>::cast(Utils::OpenHandle(*args.This()));
- i::Handle<i::FixedArray> array(receiver->get_functions(), i_isolate);
+ i::Handle<i::FixedArray> array(receiver->functions(), i_isolate);
int i = 0;
if (args.Length() > 0 && !args[0]->Int32Value(context).To(&i)) return;
v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
@@ -490,7 +793,7 @@ void WebAssemblyTableSet(const v8::FunctionCallbackInfo<v8::Value>& args) {
auto receiver =
i::Handle<i::WasmTableObject>::cast(Utils::OpenHandle(*args.This()));
- i::Handle<i::FixedArray> array(receiver->get_functions(), i_isolate);
+ i::Handle<i::FixedArray> array(receiver->functions(), i_isolate);
int i;
if (!args[0]->Int32Value(context).To(&i)) return;
if (i < 0 || i >= array->length()) {
@@ -500,7 +803,7 @@ void WebAssemblyTableSet(const v8::FunctionCallbackInfo<v8::Value>& args) {
return;
}
- i::Handle<i::FixedArray> dispatch_tables(receiver->get_dispatch_tables(),
+ i::Handle<i::FixedArray> dispatch_tables(receiver->dispatch_tables(),
i_isolate);
if (value->IsNull(i_isolate)) {
i::wasm::UpdateDispatchTables(i_isolate, dispatch_tables, i,
@@ -522,40 +825,40 @@ void WebAssemblyMemoryGrow(const v8::FunctionCallbackInfo<v8::Value>& args) {
"Receiver is not a WebAssembly.Memory")) {
return;
}
- if (args.Length() < 1) {
+ int64_t delta_size = 0;
+ if (args.Length() < 1 || !args[0]->IntegerValue(context).To(&delta_size)) {
v8::Local<v8::Value> e = v8::Exception::TypeError(
v8_str(isolate, "Argument 0 required, must be numeric value of pages"));
isolate->ThrowException(e);
return;
}
-
- uint32_t delta = args[0]->Uint32Value(context).FromJust();
- i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
- i::Handle<i::JSObject> receiver =
- i::Handle<i::JSObject>::cast(Utils::OpenHandle(*args.This()));
- i::Handle<i::Object> instance_object(
- receiver->GetInternalField(kWasmMemoryInstanceObject), i_isolate);
- i::Handle<i::JSObject> instance(
- i::Handle<i::JSObject>::cast(instance_object));
-
- // TODO(gdeepti) Implement growing memory when shared by different
- // instances.
- int32_t ret = internal::wasm::GrowInstanceMemory(i_isolate, instance, delta);
- if (ret == -1) {
- v8::Local<v8::Value> e = v8::Exception::Error(
- v8_str(isolate, "Unable to grow instance memory."));
+ i::Handle<i::WasmMemoryObject> receiver =
+ i::Handle<i::WasmMemoryObject>::cast(Utils::OpenHandle(*args.This()));
+ int64_t max_size64 = receiver->maximum_pages();
+ if (max_size64 < 0 ||
+ max_size64 > static_cast<int64_t>(i::wasm::kV8MaxWasmTableSize)) {
+ max_size64 = i::wasm::kV8MaxWasmMemoryPages;
+ }
+ i::Handle<i::JSArrayBuffer> old_buffer(receiver->buffer());
+ uint32_t old_size =
+ old_buffer->byte_length()->Number() / i::wasm::kSpecMaxWasmMemoryPages;
+ int64_t new_size64 = old_size + delta_size;
+ if (delta_size < 0 || max_size64 < new_size64 || new_size64 < old_size) {
+ v8::Local<v8::Value> e = v8::Exception::RangeError(v8_str(
+ isolate, new_size64 < old_size ? "trying to shrink memory"
+ : "maximum memory size exceeded"));
isolate->ThrowException(e);
return;
}
- i::MaybeHandle<i::JSArrayBuffer> buffer =
- internal::wasm::GetInstanceMemory(i_isolate, instance);
- if (buffer.is_null()) {
- v8::Local<v8::Value> e = v8::Exception::Error(
- v8_str(isolate, "WebAssembly.Memory buffer object not set."));
+ i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
+ int32_t ret = i::wasm::GrowWebAssemblyMemory(
+ i_isolate, receiver, static_cast<uint32_t>(delta_size));
+ if (ret == -1) {
+ v8::Local<v8::Value> e = v8::Exception::RangeError(
+ v8_str(isolate, "Unable to grow instance memory."));
isolate->ThrowException(e);
return;
}
- receiver->SetInternalField(kWasmMemoryBuffer, *buffer.ToHandleChecked());
v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
return_value.Set(ret);
}
@@ -571,10 +874,9 @@ void WebAssemblyMemoryGetBuffer(
return;
}
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
- i::Handle<i::JSObject> receiver =
- i::Handle<i::JSObject>::cast(Utils::OpenHandle(*args.This()));
- i::Handle<i::Object> buffer(receiver->GetInternalField(kWasmMemoryBuffer),
- i_isolate);
+ i::Handle<i::WasmMemoryObject> receiver =
+ i::Handle<i::WasmMemoryObject>::cast(Utils::OpenHandle(*args.This()));
+ i::Handle<i::Object> buffer(receiver->buffer(), i_isolate);
DCHECK(buffer->IsJSArrayBuffer());
v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
return_value.Set(Utils::ToLocal(buffer));
@@ -586,20 +888,23 @@ void WebAssemblyMemoryGetBuffer(
static i::Handle<i::FunctionTemplateInfo> NewTemplate(i::Isolate* i_isolate,
FunctionCallback func) {
Isolate* isolate = reinterpret_cast<Isolate*>(i_isolate);
- Local<FunctionTemplate> local = FunctionTemplate::New(isolate, func);
- return v8::Utils::OpenHandle(*local);
+ Local<FunctionTemplate> templ = FunctionTemplate::New(isolate, func);
+ templ->ReadOnlyPrototype();
+ return v8::Utils::OpenHandle(*templ);
}
namespace internal {
Handle<JSFunction> InstallFunc(Isolate* isolate, Handle<JSObject> object,
- const char* str, FunctionCallback func) {
+ const char* str, FunctionCallback func,
+ int length = 0) {
Handle<String> name = v8_str(isolate, str);
Handle<FunctionTemplateInfo> temp = NewTemplate(isolate, func);
Handle<JSFunction> function =
ApiNatives::InstantiateFunction(temp).ToHandleChecked();
- PropertyAttributes attributes =
- static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY);
+ JSFunction::SetName(function, name, isolate->factory()->empty_string());
+ function->shared()->set_length(length);
+ PropertyAttributes attributes = static_cast<PropertyAttributes>(DONT_ENUM);
JSObject::AddProperty(object, name, function, attributes);
return function;
}
@@ -611,27 +916,46 @@ Handle<JSFunction> InstallGetter(Isolate* isolate, Handle<JSObject> object,
Handle<JSFunction> function =
ApiNatives::InstantiateFunction(temp).ToHandleChecked();
v8::PropertyAttribute attributes =
- static_cast<v8::PropertyAttribute>(v8::DontDelete | v8::ReadOnly);
+ static_cast<v8::PropertyAttribute>(v8::DontEnum);
Utils::ToLocal(object)->SetAccessorProperty(Utils::ToLocal(name),
Utils::ToLocal(function),
Local<Function>(), attributes);
return function;
}
-void WasmJs::InstallWasmModuleSymbolIfNeeded(Isolate* isolate,
- Handle<JSGlobalObject> global,
- Handle<Context> context) {
- if (!context->get(Context::WASM_MODULE_SYM_INDEX)->IsSymbol() ||
- !context->get(Context::WASM_INSTANCE_SYM_INDEX)->IsSymbol()) {
- InstallWasmMapsIfNeeded(isolate, isolate->native_context());
- InstallWasmConstructors(isolate, isolate->global_object(),
- isolate->native_context());
- }
-}
+void WasmJs::Install(Isolate* isolate) {
+ Handle<JSGlobalObject> global = isolate->global_object();
+ Handle<Context> context(global->native_context(), isolate);
+ // TODO(titzer): once FLAG_expose_wasm is gone, this should become a DCHECK.
+ if (context->get(Context::WASM_FUNCTION_MAP_INDEX)->IsMap()) return;
+
+ // Install Maps.
+
+ // TODO(titzer): Also make one for strict mode functions?
+ Handle<Map> prev_map = Handle<Map>(context->sloppy_function_map(), isolate);
+
+ InstanceType instance_type = prev_map->instance_type();
+ int internal_fields = JSObject::GetInternalFieldCount(*prev_map);
+ CHECK_EQ(0, internal_fields);
+ int pre_allocated =
+ prev_map->GetInObjectProperties() - prev_map->unused_property_fields();
+ int instance_size = 0;
+ int in_object_properties = 0;
+ int wasm_internal_fields = internal_fields + 1 // module instance object
+ + 1 // function arity
+ + 1; // function signature
+ JSFunction::CalculateInstanceSizeHelper(instance_type, wasm_internal_fields,
+ 0, &instance_size,
+ &in_object_properties);
+
+ int unused_property_fields = in_object_properties - pre_allocated;
+ Handle<Map> map = Map::CopyInitialMap(
+ prev_map, instance_size, in_object_properties, unused_property_fields);
+
+ context->set_wasm_function_map(*map);
+
+ // Install symbols.
-void WasmJs::InstallWasmConstructors(Isolate* isolate,
- Handle<JSGlobalObject> global,
- Handle<Context> context) {
Factory* factory = isolate->factory();
// Create private symbols.
Handle<Symbol> module_sym = factory->NewPrivateSymbol();
@@ -646,7 +970,9 @@ void WasmJs::InstallWasmConstructors(Isolate* isolate,
Handle<Symbol> memory_sym = factory->NewPrivateSymbol();
context->set_wasm_memory_sym(*memory_sym);
- // Bind the WebAssembly object.
+ // Install the JS API.
+
+ // Setup WebAssembly
Handle<String> name = v8_str(isolate, "WebAssembly");
Handle<JSFunction> cons = factory->NewFunction(name);
JSFunction::SetInstancePrototype(
@@ -655,118 +981,103 @@ void WasmJs::InstallWasmConstructors(Isolate* isolate,
Handle<JSObject> webassembly = factory->NewJSObject(cons, TENURED);
PropertyAttributes attributes = static_cast<PropertyAttributes>(DONT_ENUM);
JSObject::AddProperty(global, name, webassembly, attributes);
-
- // Setup compile
- InstallFunc(isolate, webassembly, "compile", WebAssemblyCompile);
-
- // Setup compile
- InstallFunc(isolate, webassembly, "validate", WebAssemblyValidate);
+ PropertyAttributes ro_attributes =
+ static_cast<PropertyAttributes>(DONT_ENUM | READ_ONLY);
+ JSObject::AddProperty(webassembly, factory->to_string_tag_symbol(),
+ v8_str(isolate, "WebAssembly"), ro_attributes);
+ InstallFunc(isolate, webassembly, "compile", WebAssemblyCompile, 1);
+ InstallFunc(isolate, webassembly, "validate", WebAssemblyValidate, 1);
+ InstallFunc(isolate, webassembly, "instantiate", WebAssemblyInstantiate, 1);
// Setup Module
Handle<JSFunction> module_constructor =
- InstallFunc(isolate, webassembly, "Module", WebAssemblyModule);
+ InstallFunc(isolate, webassembly, "Module", WebAssemblyModule, 1);
context->set_wasm_module_constructor(*module_constructor);
Handle<JSObject> module_proto =
factory->NewJSObject(module_constructor, TENURED);
- i::Handle<i::Map> map = isolate->factory()->NewMap(
- i::JS_OBJECT_TYPE, i::JSObject::kHeaderSize +
+ i::Handle<i::Map> module_map = isolate->factory()->NewMap(
+ i::JS_API_OBJECT_TYPE, i::JSObject::kHeaderSize +
WasmModuleObject::kFieldCount * i::kPointerSize);
- JSFunction::SetInitialMap(module_constructor, map, module_proto);
+ JSFunction::SetInitialMap(module_constructor, module_map, module_proto);
+ InstallFunc(isolate, module_constructor, "imports", WebAssemblyModuleImports,
+ 1);
+ InstallFunc(isolate, module_constructor, "exports", WebAssemblyModuleExports,
+ 1);
+ InstallFunc(isolate, module_constructor, "customSections",
+ WebAssemblyModuleCustomSections, 2);
JSObject::AddProperty(module_proto, isolate->factory()->constructor_string(),
module_constructor, DONT_ENUM);
+ JSObject::AddProperty(module_proto, factory->to_string_tag_symbol(),
+ v8_str(isolate, "WebAssembly.Module"), ro_attributes);
// Setup Instance
Handle<JSFunction> instance_constructor =
- InstallFunc(isolate, webassembly, "Instance", WebAssemblyInstance);
+ InstallFunc(isolate, webassembly, "Instance", WebAssemblyInstance, 1);
context->set_wasm_instance_constructor(*instance_constructor);
+ Handle<JSObject> instance_proto =
+ factory->NewJSObject(instance_constructor, TENURED);
+ i::Handle<i::Map> instance_map = isolate->factory()->NewMap(
+ i::JS_API_OBJECT_TYPE, i::JSObject::kHeaderSize +
+ WasmInstanceObject::kFieldCount * i::kPointerSize);
+ JSFunction::SetInitialMap(instance_constructor, instance_map, instance_proto);
+ JSObject::AddProperty(instance_proto,
+ isolate->factory()->constructor_string(),
+ instance_constructor, DONT_ENUM);
+ JSObject::AddProperty(instance_proto, factory->to_string_tag_symbol(),
+ v8_str(isolate, "WebAssembly.Instance"), ro_attributes);
// Setup Table
Handle<JSFunction> table_constructor =
- InstallFunc(isolate, webassembly, "Table", WebAssemblyTable);
+ InstallFunc(isolate, webassembly, "Table", WebAssemblyTable, 1);
context->set_wasm_table_constructor(*table_constructor);
Handle<JSObject> table_proto =
factory->NewJSObject(table_constructor, TENURED);
- map = isolate->factory()->NewMap(
- i::JS_OBJECT_TYPE, i::JSObject::kHeaderSize +
+ i::Handle<i::Map> table_map = isolate->factory()->NewMap(
+ i::JS_API_OBJECT_TYPE, i::JSObject::kHeaderSize +
WasmTableObject::kFieldCount * i::kPointerSize);
- JSFunction::SetInitialMap(table_constructor, map, table_proto);
+ JSFunction::SetInitialMap(table_constructor, table_map, table_proto);
JSObject::AddProperty(table_proto, isolate->factory()->constructor_string(),
table_constructor, DONT_ENUM);
InstallGetter(isolate, table_proto, "length", WebAssemblyTableGetLength);
- InstallFunc(isolate, table_proto, "grow", WebAssemblyTableGrow);
- InstallFunc(isolate, table_proto, "get", WebAssemblyTableGet);
- InstallFunc(isolate, table_proto, "set", WebAssemblyTableSet);
+ InstallFunc(isolate, table_proto, "grow", WebAssemblyTableGrow, 1);
+ InstallFunc(isolate, table_proto, "get", WebAssemblyTableGet, 1);
+ InstallFunc(isolate, table_proto, "set", WebAssemblyTableSet, 2);
+ JSObject::AddProperty(table_proto, factory->to_string_tag_symbol(),
+ v8_str(isolate, "WebAssembly.Table"), ro_attributes);
// Setup Memory
Handle<JSFunction> memory_constructor =
- InstallFunc(isolate, webassembly, "Memory", WebAssemblyMemory);
+ InstallFunc(isolate, webassembly, "Memory", WebAssemblyMemory, 1);
context->set_wasm_memory_constructor(*memory_constructor);
Handle<JSObject> memory_proto =
factory->NewJSObject(memory_constructor, TENURED);
- map = isolate->factory()->NewMap(
- i::JS_OBJECT_TYPE, i::JSObject::kHeaderSize +
+ i::Handle<i::Map> memory_map = isolate->factory()->NewMap(
+ i::JS_API_OBJECT_TYPE, i::JSObject::kHeaderSize +
WasmMemoryObject::kFieldCount * i::kPointerSize);
- JSFunction::SetInitialMap(memory_constructor, map, memory_proto);
+ JSFunction::SetInitialMap(memory_constructor, memory_map, memory_proto);
JSObject::AddProperty(memory_proto, isolate->factory()->constructor_string(),
memory_constructor, DONT_ENUM);
- InstallFunc(isolate, memory_proto, "grow", WebAssemblyMemoryGrow);
+ InstallFunc(isolate, memory_proto, "grow", WebAssemblyMemoryGrow, 1);
InstallGetter(isolate, memory_proto, "buffer", WebAssemblyMemoryGetBuffer);
+ JSObject::AddProperty(memory_proto, factory->to_string_tag_symbol(),
+ v8_str(isolate, "WebAssembly.Memory"), ro_attributes);
// Setup errors
- attributes = static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY);
+ attributes = static_cast<PropertyAttributes>(DONT_ENUM);
Handle<JSFunction> compile_error(
isolate->native_context()->wasm_compile_error_function());
JSObject::AddProperty(webassembly, isolate->factory()->CompileError_string(),
compile_error, attributes);
+ Handle<JSFunction> link_error(
+ isolate->native_context()->wasm_link_error_function());
+ JSObject::AddProperty(webassembly, isolate->factory()->LinkError_string(),
+ link_error, attributes);
Handle<JSFunction> runtime_error(
isolate->native_context()->wasm_runtime_error_function());
JSObject::AddProperty(webassembly, isolate->factory()->RuntimeError_string(),
runtime_error, attributes);
}
-void WasmJs::Install(Isolate* isolate, Handle<JSGlobalObject> global) {
- if (!FLAG_expose_wasm && !FLAG_validate_asm) {
- return;
- }
-
- // Setup wasm function map.
- Handle<Context> context(global->native_context(), isolate);
- InstallWasmMapsIfNeeded(isolate, context);
-
- if (FLAG_expose_wasm) {
- InstallWasmConstructors(isolate, global, context);
- }
-}
-
-void WasmJs::InstallWasmMapsIfNeeded(Isolate* isolate,
- Handle<Context> context) {
- if (!context->get(Context::WASM_FUNCTION_MAP_INDEX)->IsMap()) {
- // TODO(titzer): Move this to bootstrapper.cc??
- // TODO(titzer): Also make one for strict mode functions?
- Handle<Map> prev_map = Handle<Map>(context->sloppy_function_map(), isolate);
-
- InstanceType instance_type = prev_map->instance_type();
- int internal_fields = JSObject::GetInternalFieldCount(*prev_map);
- CHECK_EQ(0, internal_fields);
- int pre_allocated =
- prev_map->GetInObjectProperties() - prev_map->unused_property_fields();
- int instance_size = 0;
- int in_object_properties = 0;
- int wasm_internal_fields = internal_fields + 1 // module instance object
- + 1 // function arity
- + 1; // function signature
- JSFunction::CalculateInstanceSizeHelper(instance_type, wasm_internal_fields,
- 0, &instance_size,
- &in_object_properties);
-
- int unused_property_fields = in_object_properties - pre_allocated;
- Handle<Map> map = Map::CopyInitialMap(
- prev_map, instance_size, in_object_properties, unused_property_fields);
-
- context->set_wasm_function_map(*map);
- }
-}
-
static bool HasBrand(i::Handle<i::Object> value, i::Handle<i::Symbol> symbol) {
if (value->IsJSObject()) {
i::Handle<i::JSObject> object = i::Handle<i::JSObject>::cast(value);
diff --git a/deps/v8/src/wasm/wasm-js.h b/deps/v8/src/wasm/wasm-js.h
index f5b9596ee2..05d5ea3061 100644
--- a/deps/v8/src/wasm/wasm-js.h
+++ b/deps/v8/src/wasm/wasm-js.h
@@ -13,16 +13,7 @@ namespace internal {
// Exposes a WASM API to JavaScript through the V8 API.
class WasmJs {
public:
- static void Install(Isolate* isolate, Handle<JSGlobalObject> global_object);
-
- V8_EXPORT_PRIVATE static void InstallWasmModuleSymbolIfNeeded(
- Isolate* isolate, Handle<JSGlobalObject> global, Handle<Context> context);
-
- V8_EXPORT_PRIVATE static void InstallWasmMapsIfNeeded(
- Isolate* isolate, Handle<Context> context);
- static void InstallWasmConstructors(Isolate* isolate,
- Handle<JSGlobalObject> global,
- Handle<Context> context);
+ V8_EXPORT_PRIVATE static void Install(Isolate* isolate);
// WebAssembly.Table.
static bool IsWasmTableObject(Isolate* isolate, Handle<Object> value);
diff --git a/deps/v8/src/wasm/wasm-limits.h b/deps/v8/src/wasm/wasm-limits.h
new file mode 100644
index 0000000000..4c7455adc5
--- /dev/null
+++ b/deps/v8/src/wasm/wasm-limits.h
@@ -0,0 +1,45 @@
+// Copyright 2016 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef V8_WASM_WASM_LIMITS_H_
+#define V8_WASM_WASM_LIMITS_H_
+
+namespace v8 {
+namespace internal {
+namespace wasm {
+
+// The following limits are imposed by V8 on WebAssembly modules.
+// The limits are agreed upon with other engines for consistency.
+const size_t kV8MaxWasmTypes = 1000000;
+const size_t kV8MaxWasmFunctions = 1000000;
+const size_t kV8MaxWasmImports = 100000;
+const size_t kV8MaxWasmExports = 100000;
+const size_t kV8MaxWasmGlobals = 1000000;
+const size_t kV8MaxWasmDataSegments = 100000;
+const size_t kV8MaxWasmMemoryPages = 16384; // = 1 GiB
+const size_t kV8MaxWasmStringSize = 100000;
+const size_t kV8MaxWasmModuleSize = 1024 * 1024 * 1024; // = 1 GiB
+const size_t kV8MaxWasmFunctionSize = 128 * 1024;
+const size_t kV8MaxWasmFunctionLocals = 50000;
+const size_t kV8MaxWasmFunctionParams = 1000;
+const size_t kV8MaxWasmFunctionMultiReturns = 1000;
+const size_t kV8MaxWasmFunctionReturns = 1;
+const size_t kV8MaxWasmTableSize = 10000000;
+const size_t kV8MaxWasmTableEntries = 10000000;
+const size_t kV8MaxWasmTables = 1;
+const size_t kV8MaxWasmMemories = 1;
+
+const size_t kSpecMaxWasmMemoryPages = 65536;
+const size_t kSpecMaxWasmTableSize = 0xFFFFFFFFu;
+
+const uint64_t kWasmMaxHeapOffset =
+ static_cast<uint64_t>(
+ std::numeric_limits<uint32_t>::max()) // maximum base value
+ + std::numeric_limits<uint32_t>::max(); // maximum index value
+
+} // namespace wasm
+} // namespace internal
+} // namespace v8
+
+#endif // V8_WASM_WASM_LIMITS_H_
diff --git a/deps/v8/src/wasm/wasm-macro-gen.h b/deps/v8/src/wasm/wasm-macro-gen.h
index ce2f843e71..1ec9ee80ff 100644
--- a/deps/v8/src/wasm/wasm-macro-gen.h
+++ b/deps/v8/src/wasm/wasm-macro-gen.h
@@ -59,6 +59,7 @@
// Control.
//------------------------------------------------------------------------------
#define WASM_NOP kExprNop
+#define WASM_END kExprEnd
#define ARITY_0 0
#define ARITY_1 1
@@ -71,13 +72,13 @@
#define WASM_BLOCK(...) kExprBlock, kLocalVoid, __VA_ARGS__, kExprEnd
#define WASM_BLOCK_T(t, ...) \
- kExprBlock, static_cast<byte>(WasmOpcodes::LocalTypeCodeFor(t)), \
+ kExprBlock, static_cast<byte>(WasmOpcodes::ValueTypeCodeFor(t)), \
__VA_ARGS__, kExprEnd
#define WASM_BLOCK_TT(t1, t2, ...) \
kExprBlock, kMultivalBlock, 0, \
- static_cast<byte>(WasmOpcodes::LocalTypeCodeFor(t1)), \
- static_cast<byte>(WasmOpcodes::LocalTypeCodeFor(t2)), __VA_ARGS__, \
+ static_cast<byte>(WasmOpcodes::ValueTypeCodeFor(t1)), \
+ static_cast<byte>(WasmOpcodes::ValueTypeCodeFor(t2)), __VA_ARGS__, \
kExprEnd
#define WASM_BLOCK_I(...) kExprBlock, kLocalI32, __VA_ARGS__, kExprEnd
@@ -99,13 +100,13 @@
cond, kExprIf, kLocalVoid, tstmt, kExprElse, fstmt, kExprEnd
#define WASM_IF_ELSE_T(t, cond, tstmt, fstmt) \
- cond, kExprIf, static_cast<byte>(WasmOpcodes::LocalTypeCodeFor(t)), tstmt, \
+ cond, kExprIf, static_cast<byte>(WasmOpcodes::ValueTypeCodeFor(t)), tstmt, \
kExprElse, fstmt, kExprEnd
#define WASM_IF_ELSE_TT(t1, t2, cond, tstmt, fstmt) \
cond, kExprIf, kMultivalBlock, 0, \
- static_cast<byte>(WasmOpcodes::LocalTypeCodeFor(t1)), \
- static_cast<byte>(WasmOpcodes::LocalTypeCodeFor(t2)), tstmt, kExprElse, \
+ static_cast<byte>(WasmOpcodes::ValueTypeCodeFor(t1)), \
+ static_cast<byte>(WasmOpcodes::ValueTypeCodeFor(t2)), tstmt, kExprElse, \
fstmt, kExprEnd
#define WASM_IF_ELSE_I(cond, tstmt, fstmt) \
@@ -140,9 +141,8 @@
// Misc expressions.
//------------------------------------------------------------------------------
#define WASM_ID(...) __VA_ARGS__
-#define WASM_ZERO kExprI8Const, 0
-#define WASM_ONE kExprI8Const, 1
-#define WASM_I8(val) kExprI8Const, static_cast<byte>(val)
+#define WASM_ZERO kExprI32Const, 0
+#define WASM_ONE kExprI32Const, 1
#define I32V_MIN(length) -(1 << (6 + (7 * ((length) - 1))))
#define I32V_MAX(length) ((1 << (6 + (7 * ((length) - 1)))) - 1)
@@ -195,7 +195,7 @@ class LocalDeclEncoder {
pos = WriteUint32v(buffer, pos, static_cast<uint32_t>(local_decls.size()));
for (size_t i = 0; i < local_decls.size(); ++i) {
pos = WriteUint32v(buffer, pos, local_decls[i].first);
- buffer[pos++] = WasmOpcodes::LocalTypeCodeFor(local_decls[i].second);
+ buffer[pos++] = WasmOpcodes::ValueTypeCodeFor(local_decls[i].second);
}
DCHECK_EQ(Size(), pos);
return pos;
@@ -203,7 +203,7 @@ class LocalDeclEncoder {
// Add locals declarations to this helper. Return the index of the newly added
// local(s), with an optional adjustment for the parameters.
- uint32_t AddLocals(uint32_t count, LocalType type) {
+ uint32_t AddLocals(uint32_t count, ValueType type) {
uint32_t result =
static_cast<uint32_t>(total + (sig ? sig->parameter_count() : 0));
total += count;
@@ -211,7 +211,7 @@ class LocalDeclEncoder {
count += local_decls.back().first;
local_decls.pop_back();
}
- local_decls.push_back(std::pair<uint32_t, LocalType>(count, type));
+ local_decls.push_back(std::pair<uint32_t, ValueType>(count, type));
return result;
}
@@ -227,7 +227,7 @@ class LocalDeclEncoder {
private:
FunctionSig* sig;
- ZoneVector<std::pair<uint32_t, LocalType>> local_decls;
+ ZoneVector<std::pair<uint32_t, ValueType>> local_decls;
size_t total;
size_t SizeofUint32v(uint32_t val) const {
@@ -447,19 +447,22 @@ class LocalDeclEncoder {
#define WASM_WHILE(x, y) \
kExprLoop, kLocalVoid, x, kExprIf, kLocalVoid, y, kExprBr, DEPTH_1, \
kExprEnd, kExprEnd
-#define WASM_INC_LOCAL(index) \
- kExprGetLocal, static_cast<byte>(index), kExprI8Const, 1, kExprI32Add, \
+#define WASM_INC_LOCAL(index) \
+ kExprGetLocal, static_cast<byte>(index), kExprI32Const, 1, kExprI32Add, \
kExprTeeLocal, static_cast<byte>(index)
#define WASM_INC_LOCAL_BYV(index, count) \
- kExprGetLocal, static_cast<byte>(index), kExprI8Const, \
+ kExprGetLocal, static_cast<byte>(index), kExprI32Const, \
static_cast<byte>(count), kExprI32Add, kExprTeeLocal, \
static_cast<byte>(index)
#define WASM_INC_LOCAL_BY(index, count) \
- kExprGetLocal, static_cast<byte>(index), kExprI8Const, \
+ kExprGetLocal, static_cast<byte>(index), kExprI32Const, \
static_cast<byte>(count), kExprI32Add, kExprSetLocal, \
static_cast<byte>(index)
#define WASM_UNOP(opcode, x) x, static_cast<byte>(opcode)
#define WASM_BINOP(opcode, x, y) x, y, static_cast<byte>(opcode)
+#define WASM_SIMD_UNOP(opcode, x) x, kSimdPrefix, static_cast<byte>(opcode)
+#define WASM_SIMD_BINOP(opcode, x, y) \
+ x, y, kSimdPrefix, static_cast<byte>(opcode)
//------------------------------------------------------------------------------
// Int32 operations
@@ -621,14 +624,31 @@ class LocalDeclEncoder {
//------------------------------------------------------------------------------
// Simd Operations.
//------------------------------------------------------------------------------
-#define WASM_SIMD_I32x4_SPLAT(x) x, kSimdPrefix, kExprI32x4Splat & 0xff
-#define WASM_SIMD_I32x4_EXTRACT_LANE(lane, x) \
- x, kSimdPrefix, kExprI32x4ExtractLane & 0xff, static_cast<byte>(lane)
-#define WASM_SIMD_I32x4_ADD(x, y) x, y, kSimdPrefix, kExprI32x4Add & 0xff
#define WASM_SIMD_F32x4_SPLAT(x) x, kSimdPrefix, kExprF32x4Splat & 0xff
#define WASM_SIMD_F32x4_EXTRACT_LANE(lane, x) \
x, kSimdPrefix, kExprF32x4ExtractLane & 0xff, static_cast<byte>(lane)
+#define WASM_SIMD_F32x4_REPLACE_LANE(lane, x, y) \
+ x, y, kSimdPrefix, kExprF32x4ReplaceLane & 0xff, static_cast<byte>(lane)
+#define WASM_SIMD_F32x4_FROM_I32x4(x) \
+ x, kSimdPrefix, kExprF32x4FromInt32x4 & 0xff
+#define WASM_SIMD_F32x4_FROM_U32x4(x) \
+ x, kSimdPrefix, kExprF32x4FromUint32x4 & 0xff
#define WASM_SIMD_F32x4_ADD(x, y) x, y, kSimdPrefix, kExprF32x4Add & 0xff
+#define WASM_SIMD_F32x4_SUB(x, y) x, y, kSimdPrefix, kExprF32x4Sub & 0xff
+
+#define WASM_SIMD_I32x4_SPLAT(x) x, kSimdPrefix, kExprI32x4Splat & 0xff
+#define WASM_SIMD_I32x4_EXTRACT_LANE(lane, x) \
+ x, kSimdPrefix, kExprI32x4ExtractLane & 0xff, static_cast<byte>(lane)
+#define WASM_SIMD_I32x4_REPLACE_LANE(lane, x, y) \
+ x, y, kSimdPrefix, kExprI32x4ReplaceLane & 0xff, static_cast<byte>(lane)
+#define WASM_SIMD_I32x4_FROM_F32x4(x) \
+ x, kSimdPrefix, kExprI32x4FromFloat32x4 & 0xff
+#define WASM_SIMD_U32x4_FROM_F32x4(x) \
+ x, kSimdPrefix, kExprUi32x4FromFloat32x4 & 0xff
+#define WASM_SIMD_S32x4_SELECT(x, y, z) \
+ x, y, z, kSimdPrefix, kExprS32x4Select & 0xff
+#define WASM_SIMD_I32x4_ADD(x, y) x, y, kSimdPrefix, kExprI32x4Add & 0xff
+#define WASM_SIMD_I32x4_SUB(x, y) x, y, kSimdPrefix, kExprI32x4Sub & 0xff
#define SIG_ENTRY_v_v kWasmFunctionTypeForm, 0, 0
#define SIZEOF_SIG_ENTRY_v_v 3
diff --git a/deps/v8/src/wasm/wasm-module-builder.cc b/deps/v8/src/wasm/wasm-module-builder.cc
index 290e98ecf8..cd83d46d3e 100644
--- a/deps/v8/src/wasm/wasm-module-builder.cc
+++ b/deps/v8/src/wasm/wasm-module-builder.cc
@@ -8,7 +8,7 @@
#include "src/v8.h"
#include "src/zone/zone-containers.h"
-#include "src/wasm/ast-decoder.h"
+#include "src/wasm/function-body-decoder.h"
#include "src/wasm/leb-helper.h"
#include "src/wasm/wasm-macro-gen.h"
#include "src/wasm/wasm-module-builder.h"
@@ -50,11 +50,10 @@ WasmFunctionBuilder::WasmFunctionBuilder(WasmModuleBuilder* builder)
: builder_(builder),
locals_(builder->zone()),
signature_index_(0),
- exported_(0),
func_index_(static_cast<uint32_t>(builder->functions_.size())),
body_(builder->zone()),
name_(builder->zone()),
- exported_name_(builder->zone()),
+ exported_names_(builder->zone()),
i32_temps_(builder->zone()),
i64_temps_(builder->zone()),
f32_temps_(builder->zone()),
@@ -77,7 +76,7 @@ void WasmFunctionBuilder::SetSignature(FunctionSig* sig) {
signature_index_ = builder_->AddSignature(sig);
}
-uint32_t WasmFunctionBuilder::AddLocal(LocalType type) {
+uint32_t WasmFunctionBuilder::AddLocal(ValueType type) {
DCHECK(locals_.has_sig());
return locals_.AddLocals(1, type);
}
@@ -123,10 +122,10 @@ void WasmFunctionBuilder::EmitWithVarInt(WasmOpcode opcode,
}
void WasmFunctionBuilder::EmitI32Const(int32_t value) {
- // TODO(titzer): variable-length signed and unsigned i32 constants.
- if (-128 <= value && value <= 127) {
- EmitWithU8(kExprI8Const, static_cast<byte>(value));
+ if (-64 <= value && value <= 63) {
+ EmitWithU8(kExprI32Const, static_cast<byte>(value & 0x7F));
} else {
+ // TODO(titzer): variable-length signed and unsigned i32 constants.
byte code[] = {WASM_I32V_5(value)};
EmitCode(code, sizeof(code));
}
@@ -141,12 +140,9 @@ void WasmFunctionBuilder::EmitDirectCallIndex(uint32_t index) {
EmitCode(code, sizeof(code));
}
-void WasmFunctionBuilder::Export() { exported_ = true; }
-
void WasmFunctionBuilder::ExportAs(Vector<const char> name) {
- exported_ = true;
- exported_name_.resize(name.length());
- memcpy(exported_name_.data(), name.start(), name.length());
+ exported_names_.push_back(ZoneVector<char>(
+ name.start(), name.start() + name.length(), builder_->zone()));
}
void WasmFunctionBuilder::SetName(Vector<const char> name) {
@@ -154,8 +150,9 @@ void WasmFunctionBuilder::SetName(Vector<const char> name) {
memcpy(name_.data(), name.start(), name.length());
}
-void WasmFunctionBuilder::AddAsmWasmOffset(int asm_position) {
- // We only want to emit one mapping per byte offset:
+void WasmFunctionBuilder::AddAsmWasmOffset(int call_position,
+ int to_number_position) {
+ // We only want to emit one mapping per byte offset.
DCHECK(asm_offsets_.size() == 0 || body_.size() > last_asm_byte_offset_);
DCHECK_LE(body_.size(), kMaxUInt32);
@@ -163,22 +160,31 @@ void WasmFunctionBuilder::AddAsmWasmOffset(int asm_position) {
asm_offsets_.write_u32v(byte_offset - last_asm_byte_offset_);
last_asm_byte_offset_ = byte_offset;
- DCHECK_GE(asm_position, 0);
- asm_offsets_.write_i32v(asm_position - last_asm_source_position_);
- last_asm_source_position_ = asm_position;
+ DCHECK_GE(call_position, 0);
+ asm_offsets_.write_i32v(call_position - last_asm_source_position_);
+
+ DCHECK_GE(to_number_position, 0);
+ asm_offsets_.write_i32v(to_number_position - call_position);
+ last_asm_source_position_ = to_number_position;
+}
+
+void WasmFunctionBuilder::SetAsmFunctionStartPosition(int position) {
+ DCHECK_EQ(0, asm_func_start_source_position_);
+ DCHECK_LE(0, position);
+ // Must be called before emitting any asm.js source position.
+ DCHECK_EQ(0, asm_offsets_.size());
+ asm_func_start_source_position_ = position;
+ last_asm_source_position_ = position;
}
void WasmFunctionBuilder::WriteSignature(ZoneBuffer& buffer) const {
buffer.write_u32v(signature_index_);
}
-void WasmFunctionBuilder::WriteExport(ZoneBuffer& buffer) const {
- if (exported_) {
- const ZoneVector<char>* exported_name =
- exported_name_.size() == 0 ? &name_ : &exported_name_;
- buffer.write_size(exported_name->size());
- buffer.write(reinterpret_cast<const byte*>(exported_name->data()),
- exported_name->size());
+void WasmFunctionBuilder::WriteExports(ZoneBuffer& buffer) const {
+ for (auto name : exported_names_) {
+ buffer.write_size(name.size());
+ buffer.write(reinterpret_cast<const byte*>(name.data()), name.size());
buffer.write_u8(kExternalFunction);
buffer.write_u32v(func_index_ +
static_cast<uint32_t>(builder_->imports_.size()));
@@ -204,14 +210,19 @@ void WasmFunctionBuilder::WriteBody(ZoneBuffer& buffer) const {
}
void WasmFunctionBuilder::WriteAsmWasmOffsetTable(ZoneBuffer& buffer) const {
- if (asm_offsets_.size() == 0) {
+ if (asm_func_start_source_position_ == 0 && asm_offsets_.size() == 0) {
buffer.write_size(0);
return;
}
- buffer.write_size(asm_offsets_.size() + kInt32Size);
+ size_t locals_enc_size = LEBHelper::sizeof_u32v(locals_.Size());
+ size_t func_start_size =
+ LEBHelper::sizeof_u32v(asm_func_start_source_position_);
+ buffer.write_size(asm_offsets_.size() + locals_enc_size + func_start_size);
// Offset of the recorded byte offsets.
DCHECK_GE(kMaxUInt32, locals_.Size());
- buffer.write_u32(static_cast<uint32_t>(locals_.Size()));
+ buffer.write_u32v(static_cast<uint32_t>(locals_.Size()));
+ // Start position of the function.
+ buffer.write_u32v(asm_func_start_source_position_);
buffer.write(asm_offsets_.begin(), asm_offsets_.size());
}
@@ -271,8 +282,15 @@ uint32_t WasmModuleBuilder::AddSignature(FunctionSig* sig) {
}
}
-void WasmModuleBuilder::AddIndirectFunction(uint32_t index) {
- indirect_functions_.push_back(index);
+uint32_t WasmModuleBuilder::AllocateIndirectFunctions(uint32_t count) {
+ uint32_t ret = static_cast<uint32_t>(indirect_functions_.size());
+ indirect_functions_.resize(indirect_functions_.size() + count);
+ return ret;
+}
+
+void WasmModuleBuilder::SetIndirectFunction(uint32_t indirect,
+ uint32_t direct) {
+ indirect_functions_[indirect] = direct;
}
uint32_t WasmModuleBuilder::AddImport(const char* name, int name_length,
@@ -285,7 +303,7 @@ void WasmModuleBuilder::MarkStartFunction(WasmFunctionBuilder* function) {
start_function_index_ = function->func_index();
}
-uint32_t WasmModuleBuilder::AddGlobal(LocalType type, bool exported,
+uint32_t WasmModuleBuilder::AddGlobal(ValueType type, bool exported,
bool mutability,
const WasmInitExpr& init) {
globals_.push_back({type, exported, mutability, init});
@@ -309,11 +327,11 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer& buffer) const {
buffer.write_u8(kWasmFunctionTypeForm);
buffer.write_size(sig->parameter_count());
for (size_t j = 0; j < sig->parameter_count(); j++) {
- buffer.write_u8(WasmOpcodes::LocalTypeCodeFor(sig->GetParam(j)));
+ buffer.write_u8(WasmOpcodes::ValueTypeCodeFor(sig->GetParam(j)));
}
buffer.write_size(sig->return_count());
for (size_t j = 0; j < sig->return_count(); j++) {
- buffer.write_u8(WasmOpcodes::LocalTypeCodeFor(sig->GetReturn(j)));
+ buffer.write_u8(WasmOpcodes::ValueTypeCodeFor(sig->GetReturn(j)));
}
}
FixupSection(buffer, start);
@@ -324,10 +342,10 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer& buffer) const {
size_t start = EmitSection(kImportSectionCode, buffer);
buffer.write_size(imports_.size());
for (auto import : imports_) {
- buffer.write_u32v(import.name_length); // module name length
- buffer.write(reinterpret_cast<const byte*>(import.name), // module name
+ buffer.write_u32v(0); // module name length
+ buffer.write_u32v(import.name_length); // field name length
+ buffer.write(reinterpret_cast<const byte*>(import.name), // field name
import.name_length);
- buffer.write_u32v(0); // field name length
buffer.write_u8(kExternalFunction);
buffer.write_u32v(import.sig_index);
}
@@ -341,7 +359,7 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer& buffer) const {
buffer.write_size(functions_.size());
for (auto function : functions_) {
function->WriteSignature(buffer);
- if (function->exported()) exports++;
+ exports += function->exported_names_.size();
if (function->name_.size() > 0) has_names = true;
}
FixupSection(buffer, start);
@@ -374,29 +392,29 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer& buffer) const {
buffer.write_size(globals_.size());
for (auto global : globals_) {
- buffer.write_u8(WasmOpcodes::LocalTypeCodeFor(global.type));
+ buffer.write_u8(WasmOpcodes::ValueTypeCodeFor(global.type));
buffer.write_u8(global.mutability ? 1 : 0);
switch (global.init.kind) {
case WasmInitExpr::kI32Const: {
- DCHECK_EQ(kAstI32, global.type);
+ DCHECK_EQ(kWasmI32, global.type);
const byte code[] = {WASM_I32V_5(global.init.val.i32_const)};
buffer.write(code, sizeof(code));
break;
}
case WasmInitExpr::kI64Const: {
- DCHECK_EQ(kAstI64, global.type);
+ DCHECK_EQ(kWasmI64, global.type);
const byte code[] = {WASM_I64V_10(global.init.val.i64_const)};
buffer.write(code, sizeof(code));
break;
}
case WasmInitExpr::kF32Const: {
- DCHECK_EQ(kAstF32, global.type);
+ DCHECK_EQ(kWasmF32, global.type);
const byte code[] = {WASM_F32(global.init.val.f32_const)};
buffer.write(code, sizeof(code));
break;
}
case WasmInitExpr::kF64Const: {
- DCHECK_EQ(kAstF64, global.type);
+ DCHECK_EQ(kWasmF64, global.type);
const byte code[] = {WASM_F64(global.init.val.f64_const)};
buffer.write(code, sizeof(code));
break;
@@ -410,22 +428,22 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer& buffer) const {
default: {
// No initializer, emit a default value.
switch (global.type) {
- case kAstI32: {
+ case kWasmI32: {
const byte code[] = {WASM_I32V_1(0)};
buffer.write(code, sizeof(code));
break;
}
- case kAstI64: {
+ case kWasmI64: {
const byte code[] = {WASM_I64V_1(0)};
buffer.write(code, sizeof(code));
break;
}
- case kAstF32: {
+ case kWasmF32: {
const byte code[] = {WASM_F32(0.0)};
buffer.write(code, sizeof(code));
break;
}
- case kAstF64: {
+ case kWasmF64: {
const byte code[] = {WASM_F64(0.0)};
buffer.write(code, sizeof(code));
break;
@@ -444,7 +462,7 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer& buffer) const {
if (exports > 0) {
size_t start = EmitSection(kExportSectionCode, buffer);
buffer.write_u32v(exports);
- for (auto function : functions_) function->WriteExport(buffer);
+ for (auto function : functions_) function->WriteExports(buffer);
FixupSection(buffer, start);
}
@@ -517,10 +535,8 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer& buffer) const {
}
for (auto function : functions_) {
buffer.write_size(function->name_.size());
- if (function->name_.size() > 0) {
- buffer.write(reinterpret_cast<const byte*>(&function->name_[0]),
- function->name_.size());
- }
+ buffer.write(reinterpret_cast<const byte*>(function->name_.data()),
+ function->name_.size());
buffer.write_u8(0);
}
FixupSection(buffer, start);
@@ -534,6 +550,8 @@ void WasmModuleBuilder::WriteAsmJsOffsetTable(ZoneBuffer& buffer) const {
for (auto function : functions_) {
function->WriteAsmWasmOffsetTable(buffer);
}
+ // Append a 0 to indicate that this is an encoded table.
+ buffer.write_u8(0);
}
} // namespace wasm
} // namespace internal
diff --git a/deps/v8/src/wasm/wasm-module-builder.h b/deps/v8/src/wasm/wasm-module-builder.h
index d35313ef47..3258f78d50 100644
--- a/deps/v8/src/wasm/wasm-module-builder.h
+++ b/deps/v8/src/wasm/wasm-module-builder.h
@@ -120,7 +120,7 @@ class V8_EXPORT_PRIVATE WasmFunctionBuilder : public ZoneObject {
public:
// Building methods.
void SetSignature(FunctionSig* sig);
- uint32_t AddLocal(LocalType type);
+ uint32_t AddLocal(ValueType type);
void EmitVarInt(uint32_t val);
void EmitCode(const byte* code, uint32_t code_size);
void Emit(WasmOpcode opcode);
@@ -132,17 +132,16 @@ class V8_EXPORT_PRIVATE WasmFunctionBuilder : public ZoneObject {
void EmitWithU8U8(WasmOpcode opcode, const byte imm1, const byte imm2);
void EmitWithVarInt(WasmOpcode opcode, uint32_t immediate);
void EmitDirectCallIndex(uint32_t index);
- void Export();
void ExportAs(Vector<const char> name);
void SetName(Vector<const char> name);
- void AddAsmWasmOffset(int asm_position);
+ void AddAsmWasmOffset(int call_position, int to_number_position);
+ void SetAsmFunctionStartPosition(int position);
void WriteSignature(ZoneBuffer& buffer) const;
- void WriteExport(ZoneBuffer& buffer) const;
+ void WriteExports(ZoneBuffer& buffer) const;
void WriteBody(ZoneBuffer& buffer) const;
void WriteAsmWasmOffsetTable(ZoneBuffer& buffer) const;
- bool exported() { return exported_; }
uint32_t func_index() { return func_index_; }
FunctionSig* signature();
@@ -159,11 +158,10 @@ class V8_EXPORT_PRIVATE WasmFunctionBuilder : public ZoneObject {
WasmModuleBuilder* builder_;
LocalDeclEncoder locals_;
uint32_t signature_index_;
- bool exported_;
uint32_t func_index_;
ZoneVector<uint8_t> body_;
ZoneVector<char> name_;
- ZoneVector<char> exported_name_;
+ ZoneVector<ZoneVector<char>> exported_names_;
ZoneVector<uint32_t> i32_temps_;
ZoneVector<uint32_t> i64_temps_;
ZoneVector<uint32_t> f32_temps_;
@@ -174,22 +172,23 @@ class V8_EXPORT_PRIVATE WasmFunctionBuilder : public ZoneObject {
ZoneBuffer asm_offsets_;
uint32_t last_asm_byte_offset_ = 0;
uint32_t last_asm_source_position_ = 0;
+ uint32_t asm_func_start_source_position_ = 0;
};
class WasmTemporary {
public:
- WasmTemporary(WasmFunctionBuilder* builder, LocalType type) {
+ WasmTemporary(WasmFunctionBuilder* builder, ValueType type) {
switch (type) {
- case kAstI32:
+ case kWasmI32:
temporary_ = &builder->i32_temps_;
break;
- case kAstI64:
+ case kWasmI64:
temporary_ = &builder->i64_temps_;
break;
- case kAstF32:
+ case kWasmF32:
temporary_ = &builder->f32_temps_;
break;
- case kAstF64:
+ case kWasmF64:
temporary_ = &builder->f64_temps_;
break;
default:
@@ -226,11 +225,12 @@ class V8_EXPORT_PRIVATE WasmModuleBuilder : public ZoneObject {
imports_[index].name_length = name_length;
}
WasmFunctionBuilder* AddFunction(FunctionSig* sig = nullptr);
- uint32_t AddGlobal(LocalType type, bool exported, bool mutability = true,
+ uint32_t AddGlobal(ValueType type, bool exported, bool mutability = true,
const WasmInitExpr& init = WasmInitExpr());
void AddDataSegment(const byte* data, uint32_t size, uint32_t dest);
uint32_t AddSignature(FunctionSig* sig);
- void AddIndirectFunction(uint32_t index);
+ uint32_t AllocateIndirectFunctions(uint32_t count);
+ void SetIndirectFunction(uint32_t indirect, uint32_t direct);
void MarkStartFunction(WasmFunctionBuilder* builder);
// Writing methods.
@@ -256,7 +256,7 @@ class V8_EXPORT_PRIVATE WasmModuleBuilder : public ZoneObject {
};
struct WasmGlobal {
- LocalType type;
+ ValueType type;
bool exported;
bool mutability;
WasmInitExpr init;
diff --git a/deps/v8/src/wasm/wasm-module.cc b/deps/v8/src/wasm/wasm-module.cc
index 79b99fe04d..60dda925fa 100644
--- a/deps/v8/src/wasm/wasm-module.cc
+++ b/deps/v8/src/wasm/wasm-module.cc
@@ -4,25 +4,26 @@
#include <memory>
+#include "src/assembler-inl.h"
+#include "src/base/adapters.h"
#include "src/base/atomic-utils.h"
#include "src/code-stubs.h"
-
-#include "src/macro-assembler.h"
+#include "src/compiler/wasm-compiler.h"
+#include "src/debug/interface-types.h"
#include "src/objects.h"
#include "src/property-descriptor.h"
#include "src/simulator.h"
#include "src/snapshot/snapshot.h"
#include "src/v8.h"
-#include "src/wasm/ast-decoder.h"
+#include "src/wasm/function-body-decoder.h"
#include "src/wasm/module-decoder.h"
#include "src/wasm/wasm-js.h"
+#include "src/wasm/wasm-limits.h"
#include "src/wasm/wasm-module.h"
#include "src/wasm/wasm-objects.h"
#include "src/wasm/wasm-result.h"
-#include "src/compiler/wasm-compiler.h"
-
using namespace v8::internal;
using namespace v8::internal::wasm;
namespace base = v8::base;
@@ -40,26 +41,11 @@ namespace base = v8::base;
namespace {
static const int kInvalidSigIndex = -1;
-static const int kPlaceholderMarker = 1000000000;
byte* raw_buffer_ptr(MaybeHandle<JSArrayBuffer> buffer, int offset) {
return static_cast<byte*>(buffer.ToHandleChecked()->backing_store()) + offset;
}
-MaybeHandle<String> ExtractStringFromModuleBytes(
- Isolate* isolate, Handle<WasmCompiledModule> compiled_module,
- uint32_t offset, uint32_t size) {
- // TODO(wasm): cache strings from modules if it's a performance win.
- Handle<SeqOneByteString> module_bytes = compiled_module->module_bytes();
- DCHECK_GE(static_cast<size_t>(module_bytes->length()), offset);
- DCHECK_GE(static_cast<size_t>(module_bytes->length() - offset), size);
- Address raw = module_bytes->GetCharsAddress() + offset;
- if (!unibrow::Utf8::Validate(reinterpret_cast<const byte*>(raw), size))
- return {}; // UTF8 decoding error for name.
- return isolate->factory()->NewStringFromUtf8SubString(
- module_bytes, static_cast<int>(offset), static_cast<int>(size));
-}
-
void ReplaceReferenceInCode(Handle<Code> code, Handle<Object> old_ref,
Handle<Object> new_ref) {
for (RelocIterator it(*code, 1 << RelocInfo::EMBEDDED_OBJECT); !it.done();
@@ -70,34 +56,74 @@ void ReplaceReferenceInCode(Handle<Code> code, Handle<Object> old_ref,
}
}
-Handle<JSArrayBuffer> NewArrayBuffer(Isolate* isolate, size_t size) {
- if (size > (WasmModule::kV8MaxPages * WasmModule::kPageSize)) {
- // TODO(titzer): lift restriction on maximum memory allocated here.
- return Handle<JSArrayBuffer>::null();
- }
- void* memory = isolate->array_buffer_allocator()->Allocate(size);
- if (memory == nullptr) {
- return Handle<JSArrayBuffer>::null();
- }
+static void MemoryFinalizer(const v8::WeakCallbackInfo<void>& data) {
+ DisallowHeapAllocation no_gc;
+ JSArrayBuffer** p = reinterpret_cast<JSArrayBuffer**>(data.GetParameter());
+ JSArrayBuffer* buffer = *p;
-#if DEBUG
- // Double check the API allocator actually zero-initialized the memory.
- const byte* bytes = reinterpret_cast<const byte*>(memory);
- for (size_t i = 0; i < size; ++i) {
- DCHECK_EQ(0, bytes[i]);
- }
+ void* memory = buffer->backing_store();
+ base::OS::Free(memory,
+ RoundUp(kWasmMaxHeapOffset, base::OS::CommitPageSize()));
+
+ data.GetIsolate()->AdjustAmountOfExternalAllocatedMemory(
+ -buffer->byte_length()->Number());
+
+ GlobalHandles::Destroy(reinterpret_cast<Object**>(p));
+}
+
+#if V8_TARGET_ARCH_64_BIT
+const bool kGuardRegionsSupported = true;
+#else
+const bool kGuardRegionsSupported = false;
#endif
- Handle<JSArrayBuffer> buffer = isolate->factory()->NewJSArrayBuffer();
- JSArrayBuffer::Setup(buffer, isolate, false, memory, static_cast<int>(size));
- buffer->set_is_neuterable(false);
- return buffer;
+bool EnableGuardRegions() {
+ return FLAG_wasm_guard_pages && kGuardRegionsSupported;
+}
+
+void* TryAllocateBackingStore(Isolate* isolate, size_t size,
+ bool enable_guard_regions, bool& is_external) {
+ is_external = false;
+ // TODO(eholk): Right now enable_guard_regions has no effect on 32-bit
+ // systems. It may be safer to fail instead, given that other code might do
+ // things that would be unsafe if they expected guard pages where there
+ // weren't any.
+ if (enable_guard_regions && kGuardRegionsSupported) {
+ // TODO(eholk): On Windows we want to make sure we don't commit the guard
+ // pages yet.
+
+ // We always allocate the largest possible offset into the heap, so the
+ // addressable memory after the guard page can be made inaccessible.
+ const size_t alloc_size =
+ RoundUp(kWasmMaxHeapOffset, base::OS::CommitPageSize());
+ DCHECK_EQ(0, size % base::OS::CommitPageSize());
+
+ // AllocateGuarded makes the whole region inaccessible by default.
+ void* memory = base::OS::AllocateGuarded(alloc_size);
+ if (memory == nullptr) {
+ return nullptr;
+ }
+
+ // Make the part we care about accessible.
+ base::OS::Unprotect(memory, size);
+
+ reinterpret_cast<v8::Isolate*>(isolate)
+ ->AdjustAmountOfExternalAllocatedMemory(size);
+
+ is_external = true;
+ return memory;
+ } else {
+ void* memory = isolate->array_buffer_allocator()->Allocate(size);
+ return memory;
+ }
}
void RelocateMemoryReferencesInCode(Handle<FixedArray> code_table,
+ uint32_t num_imported_functions,
Address old_start, Address start,
uint32_t prev_size, uint32_t new_size) {
- for (int i = 0; i < code_table->length(); ++i) {
+ for (int i = static_cast<int>(num_imported_functions);
+ i < code_table->length(); ++i) {
DCHECK(code_table->get(i)->IsCode());
Handle<Code> code = Handle<Code>(Code::cast(code_table->get(i)));
AllowDeferredHandleDereference embedding_raw_address;
@@ -123,52 +149,25 @@ void RelocateGlobals(Handle<FixedArray> code_table, Address old_start,
}
}
-Handle<Code> CreatePlaceholder(Factory* factory, uint32_t index,
- Code::Kind kind) {
- // Create a placeholder code object and encode the corresponding index in
- // the {constant_pool_offset} field of the code object.
- // TODO(titzer): instead of placeholders, use a reloc_info mode.
- static byte buffer[] = {0, 0, 0, 0}; // fake instructions.
- static CodeDesc desc = {
- buffer, arraysize(buffer), arraysize(buffer), 0, 0, nullptr, 0, nullptr};
- Handle<Code> code = factory->NewCode(desc, Code::KindField::encode(kind),
- Handle<Object>::null());
- code->set_constant_pool_offset(static_cast<int>(index) + kPlaceholderMarker);
- return code;
-}
-
-bool LinkFunction(Handle<Code> unlinked,
- std::vector<Handle<Code>>& code_table) {
- bool modified = false;
- int mode_mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET);
- AllowDeferredHandleDereference embedding_raw_address;
- for (RelocIterator it(*unlinked, mode_mask); !it.done(); it.next()) {
- RelocInfo::Mode mode = it.rinfo()->rmode();
- if (RelocInfo::IsCodeTarget(mode)) {
- Code* target =
- Code::GetCodeFromTargetAddress(it.rinfo()->target_address());
- if (target->constant_pool_offset() < kPlaceholderMarker) continue;
- switch (target->kind()) {
- case Code::WASM_FUNCTION: // fall through
- case Code::WASM_TO_JS_FUNCTION: // fall through
- case Code::JS_TO_WASM_FUNCTION: {
- // Patch direct calls to placeholder code objects.
- uint32_t index = target->constant_pool_offset() - kPlaceholderMarker;
- Handle<Code> new_target = code_table[index];
- if (target != *new_target) {
- it.rinfo()->set_target_address(new_target->instruction_start(),
- UPDATE_WRITE_BARRIER,
- SKIP_ICACHE_FLUSH);
- modified = true;
- }
- break;
- }
- default:
- break;
- }
+void RelocateTableSizeReferences(Handle<FixedArray> code_table,
+ uint32_t old_size, uint32_t new_size) {
+ for (int i = 0; i < code_table->length(); ++i) {
+ DCHECK(code_table->get(i)->IsCode());
+ Handle<Code> code = Handle<Code>(Code::cast(code_table->get(i)));
+ AllowDeferredHandleDereference embedding_raw_address;
+ int mask = 1 << RelocInfo::WASM_FUNCTION_TABLE_SIZE_REFERENCE;
+ for (RelocIterator it(*code, mask); !it.done(); it.next()) {
+ it.rinfo()->update_wasm_function_table_size_reference(old_size, new_size);
}
}
- return modified;
+}
+
+Handle<Code> CreatePlaceholder(Factory* factory, Code::Kind kind) {
+ byte buffer[] = {0, 0, 0, 0}; // fake instructions.
+ CodeDesc desc = {
+ buffer, arraysize(buffer), arraysize(buffer), 0, 0, nullptr, 0, nullptr};
+ return factory->NewCode(desc, Code::KindField::encode(kind),
+ Handle<Object>::null());
}
void FlushICache(Isolate* isolate, Handle<FixedArray> code_table) {
@@ -257,7 +256,7 @@ Address GetGlobalStartAddressFromCodeTemplate(Object* undefined,
Address old_address = nullptr;
if (instance->has_globals_buffer()) {
old_address =
- static_cast<Address>(instance->get_globals_buffer()->backing_store());
+ static_cast<Address>(instance->globals_buffer()->backing_store());
}
return old_address;
}
@@ -265,7 +264,7 @@ Address GetGlobalStartAddressFromCodeTemplate(Object* undefined,
void InitializeParallelCompilation(
Isolate* isolate, const std::vector<WasmFunction>& functions,
std::vector<compiler::WasmCompilationUnit*>& compilation_units,
- ModuleEnv& module_env, ErrorThrower* thrower) {
+ ModuleBytesEnv& module_env, ErrorThrower* thrower) {
for (uint32_t i = FLAG_skip_compiling_wasm_funcs; i < functions.size(); ++i) {
const WasmFunction* func = &functions[i];
compilation_units[i] =
@@ -329,9 +328,10 @@ void FinishCompilationUnits(
}
}
-void CompileInParallel(Isolate* isolate, const WasmModule* module,
+void CompileInParallel(Isolate* isolate, ModuleBytesEnv* module_env,
std::vector<Handle<Code>>& functions,
- ErrorThrower* thrower, ModuleEnv* module_env) {
+ ErrorThrower* thrower) {
+ const WasmModule* module = module_env->module;
// Data structures for the parallel compilation.
std::vector<compiler::WasmCompilationUnit*> compilation_units(
module->functions.size());
@@ -393,22 +393,23 @@ void CompileInParallel(Isolate* isolate, const WasmModule* module,
FinishCompilationUnits(executed_units, functions, result_mutex);
}
-void CompileSequentially(Isolate* isolate, const WasmModule* module,
+void CompileSequentially(Isolate* isolate, ModuleBytesEnv* module_env,
std::vector<Handle<Code>>& functions,
- ErrorThrower* thrower, ModuleEnv* module_env) {
+ ErrorThrower* thrower) {
DCHECK(!thrower->error());
+ const WasmModule* module = module_env->module;
for (uint32_t i = FLAG_skip_compiling_wasm_funcs;
i < module->functions.size(); ++i) {
const WasmFunction& func = module->functions[i];
if (func.imported) continue; // Imports are compiled at instantiation time.
- WasmName str = module->GetName(func.name_offset, func.name_length);
Handle<Code> code = Handle<Code>::null();
// Compile the function.
code = compiler::WasmCompilationUnit::CompileWasmFunction(
thrower, isolate, module_env, &func);
if (code.is_null()) {
+ WasmName str = module_env->GetName(&func);
thrower->CompileError("Compilation of #%d:%.*s failed.", i, str.length(),
str.start());
break;
@@ -418,36 +419,120 @@ void CompileSequentially(Isolate* isolate, const WasmModule* module,
}
}
-void PatchDirectCalls(Handle<FixedArray> old_functions,
- Handle<FixedArray> new_functions, int start) {
- DCHECK_EQ(new_functions->length(), old_functions->length());
+int ExtractDirectCallIndex(wasm::Decoder& decoder, const byte* pc) {
+ DCHECK_EQ(static_cast<int>(kExprCallFunction), static_cast<int>(*pc));
+ decoder.Reset(pc + 1, pc + 6);
+ uint32_t call_idx = decoder.consume_u32v("call index");
+ DCHECK(decoder.ok());
+ DCHECK_GE(kMaxInt, call_idx);
+ return static_cast<int>(call_idx);
+}
+
+int AdvanceSourcePositionTableIterator(SourcePositionTableIterator& iterator,
+ size_t offset_l) {
+ DCHECK_GE(kMaxInt, offset_l);
+ int offset = static_cast<int>(offset_l);
+ DCHECK(!iterator.done());
+ int byte_pos;
+ do {
+ byte_pos = iterator.source_position().ScriptOffset();
+ iterator.Advance();
+ } while (!iterator.done() && iterator.code_offset() <= offset);
+ return byte_pos;
+}
+void PatchContext(RelocIterator& it, Context* context) {
+ Object* old = it.rinfo()->target_object();
+ // The only context we use is the native context.
+ DCHECK_IMPLIES(old->IsContext(), old->IsNativeContext());
+ if (!old->IsNativeContext()) return;
+ it.rinfo()->set_target_object(context, UPDATE_WRITE_BARRIER,
+ SKIP_ICACHE_FLUSH);
+}
+
+void PatchDirectCallsAndContext(Handle<FixedArray> new_functions,
+ Handle<WasmCompiledModule> compiled_module,
+ WasmModule* module, int start) {
DisallowHeapAllocation no_gc;
- std::map<Code*, Code*> old_to_new_code;
- for (int i = 0; i < new_functions->length(); ++i) {
- old_to_new_code.insert(std::make_pair(Code::cast(old_functions->get(i)),
- Code::cast(new_functions->get(i))));
- }
- int mode_mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET);
AllowDeferredHandleDereference embedding_raw_address;
- for (int i = start; i < new_functions->length(); ++i) {
- Code* wasm_function = Code::cast(new_functions->get(i));
+ SeqOneByteString* module_bytes = compiled_module->module_bytes();
+ std::vector<WasmFunction>* wasm_functions =
+ &compiled_module->module()->functions;
+ DCHECK_EQ(wasm_functions->size() +
+ compiled_module->module()->num_exported_functions,
+ new_functions->length());
+ DCHECK_EQ(start, compiled_module->module()->num_imported_functions);
+ Context* context = compiled_module->ptr_to_native_context();
+ int mode_mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET) |
+ RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT);
+
+ // Allocate decoder outside of the loop and reuse it to decode all function
+ // indexes.
+ wasm::Decoder decoder(nullptr, nullptr);
+ int num_wasm_functions = static_cast<int>(wasm_functions->size());
+ int func_index = start;
+ // Patch all wasm functions.
+ for (; func_index < num_wasm_functions; ++func_index) {
+ Code* wasm_function = Code::cast(new_functions->get(func_index));
+ DCHECK(wasm_function->kind() == Code::WASM_FUNCTION);
+ // Iterate simultaneously over the relocation information and the source
+ // position table. For each call in the reloc info, move the source position
+ // iterator forward to that position to find the byte offset of the
+ // respective call. Then extract the call index from the module wire bytes
+ // to find the new compiled function.
+ SourcePositionTableIterator source_pos_iterator(
+ wasm_function->source_position_table());
+ const byte* func_bytes =
+ module_bytes->GetChars() +
+ compiled_module->module()->functions[func_index].code_start_offset;
for (RelocIterator it(wasm_function, mode_mask); !it.done(); it.next()) {
- Code* old_code =
- Code::GetCodeFromTargetAddress(it.rinfo()->target_address());
- if (old_code->kind() == Code::WASM_TO_JS_FUNCTION ||
- old_code->kind() == Code::WASM_FUNCTION) {
- auto found = old_to_new_code.find(old_code);
- DCHECK(found != old_to_new_code.end());
- Code* new_code = found->second;
- if (new_code != old_code) {
- it.rinfo()->set_target_address(new_code->instruction_start(),
- UPDATE_WRITE_BARRIER,
- SKIP_ICACHE_FLUSH);
- }
+ if (RelocInfo::IsEmbeddedObject(it.rinfo()->rmode())) {
+ PatchContext(it, context);
+ continue;
}
+ DCHECK(RelocInfo::IsCodeTarget(it.rinfo()->rmode()));
+ Code::Kind kind =
+ Code::GetCodeFromTargetAddress(it.rinfo()->target_address())->kind();
+ if (kind != Code::WASM_FUNCTION && kind != Code::WASM_TO_JS_FUNCTION)
+ continue;
+ size_t offset = it.rinfo()->pc() - wasm_function->instruction_start();
+ int byte_pos =
+ AdvanceSourcePositionTableIterator(source_pos_iterator, offset);
+ int called_func_index =
+ ExtractDirectCallIndex(decoder, func_bytes + byte_pos);
+ Code* new_code = Code::cast(new_functions->get(called_func_index));
+ it.rinfo()->set_target_address(new_code->instruction_start(),
+ UPDATE_WRITE_BARRIER, SKIP_ICACHE_FLUSH);
}
}
+ // Patch all exported functions.
+ for (auto exp : module->export_table) {
+ if (exp.kind != kExternalFunction) continue;
+ Code* export_wrapper = Code::cast(new_functions->get(func_index));
+ DCHECK_EQ(Code::JS_TO_WASM_FUNCTION, export_wrapper->kind());
+ // There must be exactly one call to WASM_FUNCTION or WASM_TO_JS_FUNCTION.
+ int num_wasm_calls = 0;
+ for (RelocIterator it(export_wrapper, mode_mask); !it.done(); it.next()) {
+ if (RelocInfo::IsEmbeddedObject(it.rinfo()->rmode())) {
+ PatchContext(it, context);
+ continue;
+ }
+ DCHECK(RelocInfo::IsCodeTarget(it.rinfo()->rmode()));
+ Code::Kind kind =
+ Code::GetCodeFromTargetAddress(it.rinfo()->target_address())->kind();
+ if (kind != Code::WASM_FUNCTION && kind != Code::WASM_TO_JS_FUNCTION)
+ continue;
+ ++num_wasm_calls;
+ Code* new_code = Code::cast(new_functions->get(exp.index));
+ DCHECK(new_code->kind() == Code::WASM_FUNCTION ||
+ new_code->kind() == Code::WASM_TO_JS_FUNCTION);
+ it.rinfo()->set_target_address(new_code->instruction_start(),
+ UPDATE_WRITE_BARRIER, SKIP_ICACHE_FLUSH);
+ }
+ DCHECK_EQ(1, num_wasm_calls);
+ func_index++;
+ }
+ DCHECK_EQ(new_functions->length(), func_index);
}
static void ResetCompiledModule(Isolate* isolate, WasmInstanceObject* owner,
@@ -456,7 +541,7 @@ static void ResetCompiledModule(Isolate* isolate, WasmInstanceObject* owner,
Object* undefined = *isolate->factory()->undefined_value();
uint32_t old_mem_size = compiled_module->mem_size();
uint32_t default_mem_size = compiled_module->default_mem_size();
- Object* mem_start = compiled_module->ptr_to_memory();
+ Object* mem_start = compiled_module->maybe_ptr_to_memory();
Address old_mem_address = nullptr;
Address globals_start =
GetGlobalStartAddressFromCodeTemplate(undefined, owner);
@@ -486,7 +571,8 @@ static void ResetCompiledModule(Isolate* isolate, WasmInstanceObject* owner,
if (fct_obj != nullptr && fct_obj != undefined &&
(old_mem_size > 0 || globals_start != nullptr || function_tables)) {
FixedArray* functions = FixedArray::cast(fct_obj);
- for (int i = 0; i < functions->length(); ++i) {
+ for (int i = compiled_module->num_imported_functions();
+ i < functions->length(); ++i) {
Code* code = Code::cast(functions->get(i));
bool changed = false;
for (RelocIterator it(code, mode_mask); !it.done(); it.next()) {
@@ -518,12 +604,58 @@ static void ResetCompiledModule(Isolate* isolate, WasmInstanceObject* owner,
compiled_module->reset_memory();
}
+static void MemoryInstanceFinalizer(Isolate* isolate,
+ WasmInstanceObject* instance) {
+ DisallowHeapAllocation no_gc;
+ // If the memory object is destroyed, nothing needs to be done here.
+ if (!instance->has_memory_object()) return;
+ Handle<WasmInstanceWrapper> instance_wrapper =
+ handle(instance->instance_wrapper());
+ DCHECK(WasmInstanceWrapper::IsWasmInstanceWrapper(*instance_wrapper));
+ DCHECK(instance_wrapper->has_instance());
+ bool has_prev = instance_wrapper->has_previous();
+ bool has_next = instance_wrapper->has_next();
+ Handle<WasmMemoryObject> memory_object(instance->memory_object());
+
+ if (!has_prev && !has_next) {
+ memory_object->ResetInstancesLink(isolate);
+ return;
+ } else {
+ Handle<WasmInstanceWrapper> next_wrapper, prev_wrapper;
+ if (!has_prev) {
+ Handle<WasmInstanceWrapper> next_wrapper =
+ instance_wrapper->next_wrapper();
+ next_wrapper->reset_previous_wrapper();
+ // As this is the first link in the memory object, destroying
+ // without updating memory object would corrupt the instance chain in
+ // the memory object.
+ memory_object->set_instances_link(*next_wrapper);
+ } else if (!has_next) {
+ instance_wrapper->previous_wrapper()->reset_next_wrapper();
+ } else {
+ DCHECK(has_next && has_prev);
+ Handle<WasmInstanceWrapper> prev_wrapper =
+ instance_wrapper->previous_wrapper();
+ Handle<WasmInstanceWrapper> next_wrapper =
+ instance_wrapper->next_wrapper();
+ prev_wrapper->set_next_wrapper(*next_wrapper);
+ next_wrapper->set_previous_wrapper(*prev_wrapper);
+ }
+ // Reset to avoid dangling pointers
+ instance_wrapper->reset();
+ }
+}
+
static void InstanceFinalizer(const v8::WeakCallbackInfo<void>& data) {
+ DisallowHeapAllocation no_gc;
JSObject** p = reinterpret_cast<JSObject**>(data.GetParameter());
WasmInstanceObject* owner = reinterpret_cast<WasmInstanceObject*>(*p);
- WasmCompiledModule* compiled_module = owner->get_compiled_module();
- TRACE("Finalizing %d {\n", compiled_module->instance_id());
Isolate* isolate = reinterpret_cast<Isolate*>(data.GetIsolate());
+ // If a link to shared memory instances exists, update the list of memory
+ // instances before the instance is destroyed.
+ if (owner->has_instance_wrapper()) MemoryInstanceFinalizer(isolate, owner);
+ WasmCompiledModule* compiled_module = owner->compiled_module();
+ TRACE("Finalizing %d {\n", compiled_module->instance_id());
DCHECK(compiled_module->has_weak_wasm_module());
WeakCell* weak_wasm_module = compiled_module->ptr_to_weak_wasm_module();
@@ -540,8 +672,8 @@ static void InstanceFinalizer(const v8::WeakCallbackInfo<void>& data) {
TRACE("}\n");
DCHECK(!current_template->has_weak_prev_instance());
- WeakCell* next = compiled_module->ptr_to_weak_next_instance();
- WeakCell* prev = compiled_module->ptr_to_weak_prev_instance();
+ WeakCell* next = compiled_module->maybe_ptr_to_weak_next_instance();
+ WeakCell* prev = compiled_module->maybe_ptr_to_weak_prev_instance();
if (current_template == compiled_module) {
if (next == nullptr) {
@@ -597,8 +729,81 @@ std::pair<int, int> GetFunctionOffsetAndLength(
static_cast<int>(func.code_end_offset - func.code_start_offset)};
}
+Handle<Script> CreateWasmScript(Isolate* isolate,
+ const ModuleWireBytes& wire_bytes) {
+ Handle<Script> script =
+ isolate->factory()->NewScript(isolate->factory()->empty_string());
+ script->set_type(Script::TYPE_WASM);
+
+ int hash = StringHasher::HashSequentialString(
+ reinterpret_cast<const char*>(wire_bytes.module_bytes.start()),
+ wire_bytes.module_bytes.length(), kZeroHashSeed);
+
+ const int kBufferSize = 50;
+ char buffer[kBufferSize];
+ int url_chars = SNPrintF(ArrayVector(buffer), "wasm://wasm/%08x", hash);
+ DCHECK(url_chars >= 0 && url_chars < kBufferSize);
+ MaybeHandle<String> url_str = isolate->factory()->NewStringFromOneByte(
+ Vector<const uint8_t>(reinterpret_cast<uint8_t*>(buffer), url_chars),
+ TENURED);
+ script->set_source_url(*url_str.ToHandleChecked());
+
+ int name_chars = SNPrintF(ArrayVector(buffer), "wasm-%08x", hash);
+ DCHECK(name_chars >= 0 && name_chars < kBufferSize);
+ MaybeHandle<String> name_str = isolate->factory()->NewStringFromOneByte(
+ Vector<const uint8_t>(reinterpret_cast<uint8_t*>(buffer), name_chars),
+ TENURED);
+ script->set_name(*name_str.ToHandleChecked());
+
+ return script;
+}
} // namespace
+Handle<JSArrayBuffer> wasm::NewArrayBuffer(Isolate* isolate, size_t size,
+ bool enable_guard_regions) {
+ if (size > (kV8MaxWasmMemoryPages * WasmModule::kPageSize)) {
+ // TODO(titzer): lift restriction on maximum memory allocated here.
+ return Handle<JSArrayBuffer>::null();
+ }
+
+ enable_guard_regions = enable_guard_regions && kGuardRegionsSupported;
+
+ bool is_external; // Set by TryAllocateBackingStore
+ void* memory =
+ TryAllocateBackingStore(isolate, size, enable_guard_regions, is_external);
+
+ if (memory == nullptr) {
+ return Handle<JSArrayBuffer>::null();
+ }
+
+#if DEBUG
+ // Double check the API allocator actually zero-initialized the memory.
+ const byte* bytes = reinterpret_cast<const byte*>(memory);
+ for (size_t i = 0; i < size; ++i) {
+ DCHECK_EQ(0, bytes[i]);
+ }
+#endif
+
+ Handle<JSArrayBuffer> buffer = isolate->factory()->NewJSArrayBuffer();
+ JSArrayBuffer::Setup(buffer, isolate, is_external, memory,
+ static_cast<int>(size));
+ buffer->set_is_neuterable(false);
+ buffer->set_has_guard_region(enable_guard_regions);
+
+ if (is_external) {
+ // We mark the buffer as external if we allocated it here with guard
+ // pages. That means we need to arrange for it to be freed.
+
+ // TODO(eholk): Finalizers may not run when the main thread is shutting
+ // down, which means we may leak memory here.
+ Handle<Object> global_handle = isolate->global_handles()->Create(*buffer);
+ GlobalHandles::MakeWeak(global_handle.location(), global_handle.location(),
+ &MemoryFinalizer, v8::WeakCallbackType::kFinalizer);
+ }
+
+ return buffer;
+}
+
const char* wasm::SectionName(WasmSectionCode code) {
switch (code) {
case kUnknownSectionCode:
@@ -650,15 +855,12 @@ std::ostream& wasm::operator<<(std::ostream& os, const WasmFunction& function) {
return os;
}
-std::ostream& wasm::operator<<(std::ostream& os, const WasmFunctionName& pair) {
- os << "#" << pair.function_->func_index << ":";
- if (pair.function_->name_offset > 0) {
- if (pair.module_) {
- WasmName name = pair.module_->GetName(pair.function_->name_offset,
- pair.function_->name_length);
- os.write(name.start(), name.length());
- } else {
- os << "+" << pair.function_->func_index;
+std::ostream& wasm::operator<<(std::ostream& os, const WasmFunctionName& name) {
+ os << "#" << name.function_->func_index;
+ if (name.function_->name_offset > 0) {
+ if (name.name_.start()) {
+ os << ":";
+ os.write(name.name_.start(), name.name_.length());
}
} else {
os << "?";
@@ -666,16 +868,17 @@ std::ostream& wasm::operator<<(std::ostream& os, const WasmFunctionName& pair) {
return os;
}
-Object* wasm::GetOwningWasmInstance(Code* code) {
+WasmInstanceObject* wasm::GetOwningWasmInstance(Code* code) {
DCHECK(code->kind() == Code::WASM_FUNCTION);
DisallowHeapAllocation no_gc;
FixedArray* deopt_data = code->deoptimization_data();
DCHECK_NOT_NULL(deopt_data);
- DCHECK(deopt_data->length() == 2);
+ DCHECK_EQ(2, deopt_data->length());
Object* weak_link = deopt_data->get(0);
- if (!weak_link->IsWeakCell()) return nullptr;
+ DCHECK(weak_link->IsWeakCell());
WeakCell* cell = WeakCell::cast(weak_link);
- return cell->value();
+ if (!cell->value()) return nullptr;
+ return WasmInstanceObject::cast(cell->value());
}
int wasm::GetFunctionCodeOffset(Handle<WasmCompiledModule> compiled_module,
@@ -683,69 +886,41 @@ int wasm::GetFunctionCodeOffset(Handle<WasmCompiledModule> compiled_module,
return GetFunctionOffsetAndLength(compiled_module, func_index).first;
}
-bool wasm::GetPositionInfo(Handle<WasmCompiledModule> compiled_module,
- uint32_t position, Script::PositionInfo* info) {
- std::vector<WasmFunction>& functions = compiled_module->module()->functions;
-
- // Binary search for a function containing the given position.
- int left = 0; // inclusive
- int right = static_cast<int>(functions.size()); // exclusive
- if (right == 0) return false;
- while (right - left > 1) {
- int mid = left + (right - left) / 2;
- if (functions[mid].code_start_offset <= position) {
- left = mid;
- } else {
- right = mid;
- }
- }
- // If the found entry does not contains the given position, return false.
- WasmFunction& func = functions[left];
- if (position < func.code_start_offset || position >= func.code_end_offset) {
- return false;
- }
-
- info->line = left;
- info->column = position - func.code_start_offset;
- info->line_start = func.code_start_offset;
- info->line_end = func.code_end_offset;
- return true;
-}
-
-WasmModule::WasmModule(Zone* owned, const byte* module_start)
- : owned_zone(owned),
- module_start(module_start),
- pending_tasks(new base::Semaphore(0)) {}
+WasmModule::WasmModule(Zone* owned)
+ : owned_zone(owned), pending_tasks(new base::Semaphore(0)) {}
MaybeHandle<WasmCompiledModule> WasmModule::CompileFunctions(
Isolate* isolate, Handle<WasmModuleWrapper> module_wrapper,
- ErrorThrower* thrower) const {
+ ErrorThrower* thrower, const ModuleWireBytes& wire_bytes,
+ Handle<Script> asm_js_script,
+ Vector<const byte> asm_js_offset_table_bytes) const {
Factory* factory = isolate->factory();
MaybeHandle<WasmCompiledModule> nothing;
WasmInstance temp_instance(this);
temp_instance.context = isolate->native_context();
- temp_instance.mem_size = WasmModule::kPageSize * this->min_mem_pages;
+ temp_instance.mem_size = WasmModule::kPageSize * min_mem_pages;
temp_instance.mem_start = nullptr;
temp_instance.globals_start = nullptr;
// Initialize the indirect tables with placeholders.
- int function_table_count = static_cast<int>(this->function_tables.size());
+ int function_table_count = static_cast<int>(function_tables.size());
Handle<FixedArray> function_tables =
- factory->NewFixedArray(function_table_count);
+ factory->NewFixedArray(function_table_count, TENURED);
+ Handle<FixedArray> signature_tables =
+ factory->NewFixedArray(function_table_count, TENURED);
for (int i = 0; i < function_table_count; ++i) {
- temp_instance.function_tables[i] = factory->NewFixedArray(0);
+ temp_instance.function_tables[i] = factory->NewFixedArray(1, TENURED);
+ temp_instance.signature_tables[i] = factory->NewFixedArray(1, TENURED);
function_tables->set(i, *temp_instance.function_tables[i]);
+ signature_tables->set(i, *temp_instance.signature_tables[i]);
}
HistogramTimerScope wasm_compile_module_time_scope(
isolate->counters()->wasm_compile_module_time());
- ModuleEnv module_env;
- module_env.module = this;
- module_env.instance = &temp_instance;
- module_env.origin = origin;
+ ModuleBytesEnv module_env(this, &temp_instance, wire_bytes);
// The {code_table} array contains import wrappers and functions (which
// are both included in {functions.size()}, and export wrappers.
@@ -755,12 +930,11 @@ MaybeHandle<WasmCompiledModule> WasmModule::CompileFunctions(
factory->NewFixedArray(static_cast<int>(code_table_size), TENURED);
// Initialize the code table with placeholders.
+ Handle<Code> code_placeholder =
+ CreatePlaceholder(factory, Code::WASM_FUNCTION);
for (uint32_t i = 0; i < functions.size(); ++i) {
- Code::Kind kind = Code::WASM_FUNCTION;
- if (i < num_imported_functions) kind = Code::WASM_TO_JS_FUNCTION;
- Handle<Code> placeholder = CreatePlaceholder(factory, i, kind);
- code_table->set(static_cast<int>(i), *placeholder);
- temp_instance.function_code[i] = placeholder;
+ code_table->set(static_cast<int>(i), *code_placeholder);
+ temp_instance.function_code[i] = code_placeholder;
}
isolate->counters()->wasm_functions_per_module()->AddSample(
@@ -772,14 +946,14 @@ MaybeHandle<WasmCompiledModule> WasmModule::CompileFunctions(
for (size_t i = 0; i < temp_instance.function_code.size(); ++i) {
results.push_back(temp_instance.function_code[i]);
}
- CompileInParallel(isolate, this, results, thrower, &module_env);
+ CompileInParallel(isolate, &module_env, results, thrower);
for (size_t i = 0; i < results.size(); ++i) {
temp_instance.function_code[i] = results[i];
}
} else {
- CompileSequentially(isolate, this, temp_instance.function_code, thrower,
- &module_env);
+ CompileSequentially(isolate, &module_env, temp_instance.function_code,
+ thrower);
}
if (thrower->error()) return nothing;
@@ -788,61 +962,74 @@ MaybeHandle<WasmCompiledModule> WasmModule::CompileFunctions(
i < temp_instance.function_code.size(); ++i) {
Code* code = *temp_instance.function_code[i];
code_table->set(static_cast<int>(i), code);
+ RecordStats(isolate, code);
}
- // Link the functions in the module.
- for (size_t i = FLAG_skip_compiling_wasm_funcs;
- i < temp_instance.function_code.size(); ++i) {
- Handle<Code> code = temp_instance.function_code[i];
- bool modified = LinkFunction(code, temp_instance.function_code);
- if (modified) {
- // TODO(mtrofin): do we need to flush the cache here?
- Assembler::FlushICache(isolate, code->instruction_start(),
- code->instruction_size());
- }
- }
+ // Create heap objects for script, module bytes and asm.js offset table to be
+ // stored in the shared module data.
+ Handle<Script> script;
+ Handle<ByteArray> asm_js_offset_table;
+ if (asm_js_script.is_null()) {
+ script = CreateWasmScript(isolate, wire_bytes);
+ } else {
+ script = asm_js_script;
+ asm_js_offset_table =
+ isolate->factory()->NewByteArray(asm_js_offset_table_bytes.length());
+ asm_js_offset_table->copy_in(0, asm_js_offset_table_bytes.start(),
+ asm_js_offset_table_bytes.length());
+ }
+ // TODO(wasm): only save the sections necessary to deserialize a
+ // {WasmModule}. E.g. function bodies could be omitted.
+ Handle<String> module_bytes =
+ factory->NewStringFromOneByte(wire_bytes.module_bytes, TENURED)
+ .ToHandleChecked();
+ DCHECK(module_bytes->IsSeqOneByteString());
+
+ // Create the shared module data.
+ // TODO(clemensh): For the same module (same bytes / same hash), we should
+ // only have one WasmSharedModuleData. Otherwise, we might only set
+ // breakpoints on a (potentially empty) subset of the instances.
+
+ Handle<WasmSharedModuleData> shared = WasmSharedModuleData::New(
+ isolate, module_wrapper, Handle<SeqOneByteString>::cast(module_bytes),
+ script, asm_js_offset_table);
// Create the compiled module object, and populate with compiled functions
// and information needed at instantiation time. This object needs to be
// serializable. Instantiation may occur off a deserialized version of this
// object.
- Handle<WasmCompiledModule> ret =
- WasmCompiledModule::New(isolate, module_wrapper);
+ Handle<WasmCompiledModule> ret = WasmCompiledModule::New(isolate, shared);
+ ret->set_num_imported_functions(num_imported_functions);
ret->set_code_table(code_table);
ret->set_min_mem_pages(min_mem_pages);
ret->set_max_mem_pages(max_mem_pages);
if (function_table_count > 0) {
ret->set_function_tables(function_tables);
+ ret->set_signature_tables(signature_tables);
ret->set_empty_function_tables(function_tables);
}
+ // If we created a wasm script, finish it now and make it public to the
+ // debugger.
+ if (asm_js_script.is_null()) {
+ script->set_wasm_compiled_module(*ret);
+ isolate->debug()->OnAfterCompile(script);
+ }
+
// Compile JS->WASM wrappers for exported functions.
int func_index = 0;
for (auto exp : export_table) {
if (exp.kind != kExternalFunction) continue;
Handle<Code> wasm_code =
code_table->GetValueChecked<Code>(isolate, exp.index);
- Handle<Code> wrapper_code = compiler::CompileJSToWasmWrapper(
- isolate, &module_env, wasm_code, exp.index);
+ Handle<Code> wrapper_code =
+ compiler::CompileJSToWasmWrapper(isolate, this, wasm_code, exp.index);
int export_index = static_cast<int>(functions.size() + func_index);
code_table->set(export_index, *wrapper_code);
+ RecordStats(isolate, *wrapper_code);
func_index++;
}
- {
- // TODO(wasm): only save the sections necessary to deserialize a
- // {WasmModule}. E.g. function bodies could be omitted.
- size_t module_bytes_len = module_end - module_start;
- DCHECK_LE(module_bytes_len, static_cast<size_t>(kMaxInt));
- Vector<const uint8_t> module_bytes_vec(module_start,
- static_cast<int>(module_bytes_len));
- Handle<String> module_bytes_string =
- factory->NewStringFromOneByte(module_bytes_vec, TENURED)
- .ToHandleChecked();
- DCHECK(module_bytes_string->IsSeqOneByteString());
- ret->set_module_bytes(Handle<SeqOneByteString>::cast(module_bytes_string));
- }
-
return ret;
}
@@ -876,7 +1063,7 @@ static Handle<Code> UnwrapImportWrapper(Handle<Object> target) {
code = handle(target);
}
}
- DCHECK(found == 1);
+ DCHECK_EQ(1, found);
return code;
}
@@ -884,8 +1071,8 @@ static Handle<Code> CompileImportWrapper(Isolate* isolate, int index,
FunctionSig* sig,
Handle<JSReceiver> target,
Handle<String> module_name,
- MaybeHandle<String> import_name) {
- Handle<Code> code;
+ MaybeHandle<String> import_name,
+ ModuleOrigin origin) {
WasmFunction* other_func = GetWasmFunctionForImportWrapper(isolate, target);
if (other_func) {
if (sig->Equals(other_func->sig)) {
@@ -898,7 +1085,7 @@ static Handle<Code> CompileImportWrapper(Isolate* isolate, int index,
} else {
// Signature mismatch. Compile a new wrapper for the new signature.
return compiler::CompileWasmToJSWrapper(isolate, target, sig, index,
- module_name, import_name);
+ module_name, import_name, origin);
}
}
@@ -906,11 +1093,13 @@ static void UpdateDispatchTablesInternal(Isolate* isolate,
Handle<FixedArray> dispatch_tables,
int index, WasmFunction* function,
Handle<Code> code) {
- DCHECK_EQ(0, dispatch_tables->length() % 3);
- for (int i = 0; i < dispatch_tables->length(); i += 3) {
+ DCHECK_EQ(0, dispatch_tables->length() % 4);
+ for (int i = 0; i < dispatch_tables->length(); i += 4) {
int table_index = Smi::cast(dispatch_tables->get(i + 1))->value();
- Handle<FixedArray> dispatch_table(
+ Handle<FixedArray> function_table(
FixedArray::cast(dispatch_tables->get(i + 2)), isolate);
+ Handle<FixedArray> signature_table(
+ FixedArray::cast(dispatch_tables->get(i + 3)), isolate);
if (function) {
// TODO(titzer): the signature might need to be copied to avoid
// a dangling pointer in the signature map.
@@ -919,12 +1108,12 @@ static void UpdateDispatchTablesInternal(Isolate* isolate,
int sig_index = static_cast<int>(
instance->module()->function_tables[table_index].map.FindOrInsert(
function->sig));
- dispatch_table->set(index, Smi::FromInt(sig_index));
- dispatch_table->set(index + (dispatch_table->length() / 2), *code);
+ signature_table->set(index, Smi::FromInt(sig_index));
+ function_table->set(index, *code);
} else {
Code* code = nullptr;
- dispatch_table->set(index, Smi::FromInt(-1));
- dispatch_table->set(index + (dispatch_table->length() / 2), code);
+ signature_table->set(index, Smi::FromInt(-1));
+ function_table->set(index, code);
}
}
}
@@ -949,17 +1138,27 @@ void wasm::UpdateDispatchTables(Isolate* isolate,
class WasmInstanceBuilder {
public:
WasmInstanceBuilder(Isolate* isolate, ErrorThrower* thrower,
- Handle<JSObject> module_object, Handle<JSReceiver> ffi,
- Handle<JSArrayBuffer> memory)
+ Handle<WasmModuleObject> module_object,
+ Handle<JSReceiver> ffi, Handle<JSArrayBuffer> memory)
: isolate_(isolate),
+ module_(module_object->compiled_module()->module()),
thrower_(thrower),
module_object_(module_object),
ffi_(ffi),
memory_(memory) {}
// Build an instance, in all of its glory.
- MaybeHandle<JSObject> Build() {
- MaybeHandle<JSObject> nothing;
+ MaybeHandle<WasmInstanceObject> Build() {
+ MaybeHandle<WasmInstanceObject> nothing;
+
+ // Check that an imports argument was provided, if the module requires it.
+ // No point in continuing otherwise.
+ if (!module_->import_table.empty() && ffi_.is_null()) {
+ thrower_->TypeError(
+ "Imports argument must be present and must be an object");
+ return nothing;
+ }
+
HistogramTimerScope wasm_instantiate_module_time_scope(
isolate_->counters()->wasm_instantiate_module_time());
Factory* factory = isolate_->factory();
@@ -982,8 +1181,7 @@ class WasmInstanceBuilder {
Handle<WasmCompiledModule> original;
{
DisallowHeapAllocation no_gc;
- original = handle(
- WasmCompiledModule::cast(module_object_->GetInternalField(0)));
+ original = handle(module_object_->compiled_module());
if (original->has_weak_owning_instance()) {
owner = handle(WasmInstanceObject::cast(
original->weak_owning_instance()->value()));
@@ -1032,10 +1230,8 @@ class WasmInstanceBuilder {
compiled_module_->instance_id());
}
compiled_module_->set_code_table(code_table);
+ compiled_module_->set_native_context(isolate_->native_context());
}
- module_ = reinterpret_cast<WasmModuleWrapper*>(
- *compiled_module_->module_wrapper())
- ->get();
//--------------------------------------------------------------------------
// Allocate the instance object.
@@ -1049,8 +1245,9 @@ class WasmInstanceBuilder {
MaybeHandle<JSArrayBuffer> old_globals;
uint32_t globals_size = module_->globals_size;
if (globals_size > 0) {
+ const bool enable_guard_regions = false;
Handle<JSArrayBuffer> global_buffer =
- NewArrayBuffer(isolate_, globals_size);
+ NewArrayBuffer(isolate_, globals_size, enable_guard_regions);
globals_ = global_buffer;
if (globals_.is_null()) {
thrower_->RangeError("Out of memory: wasm globals");
@@ -1072,9 +1269,9 @@ class WasmInstanceBuilder {
static_cast<int>(module_->function_tables.size());
table_instances_.reserve(module_->function_tables.size());
for (int index = 0; index < function_table_count; ++index) {
- table_instances_.push_back({Handle<WasmTableObject>::null(),
- Handle<FixedArray>::null(),
- Handle<FixedArray>::null()});
+ table_instances_.push_back(
+ {Handle<WasmTableObject>::null(), Handle<FixedArray>::null(),
+ Handle<FixedArray>::null(), Handle<FixedArray>::null()});
}
//--------------------------------------------------------------------------
@@ -1089,6 +1286,11 @@ class WasmInstanceBuilder {
InitGlobals();
//--------------------------------------------------------------------------
+ // Set up the indirect function tables for the new instance.
+ //--------------------------------------------------------------------------
+ if (function_table_count > 0) InitializeTables(code_table, instance);
+
+ //--------------------------------------------------------------------------
// Set up the memory for the new instance.
//--------------------------------------------------------------------------
MaybeHandle<JSArrayBuffer> old_memory;
@@ -1099,17 +1301,51 @@ class WasmInstanceBuilder {
if (!memory_.is_null()) {
// Set externally passed ArrayBuffer non neuterable.
memory_->set_is_neuterable(false);
+
+ DCHECK_IMPLIES(EnableGuardRegions(), module_->origin == kAsmJsOrigin ||
+ memory_->has_guard_region());
} else if (min_mem_pages > 0) {
memory_ = AllocateMemory(min_mem_pages);
if (memory_.is_null()) return nothing; // failed to allocate memory
}
+ //--------------------------------------------------------------------------
+ // Check that indirect function table segments are within bounds.
+ //--------------------------------------------------------------------------
+ for (WasmTableInit& table_init : module_->table_inits) {
+ DCHECK(table_init.table_index < table_instances_.size());
+ uint32_t base = EvalUint32InitExpr(table_init.offset);
+ uint32_t table_size =
+ table_instances_[table_init.table_index].function_table->length();
+ if (!in_bounds(base, static_cast<uint32_t>(table_init.entries.size()),
+ table_size)) {
+ thrower_->LinkError("table initializer is out of bounds");
+ return nothing;
+ }
+ }
+
+ //--------------------------------------------------------------------------
+ // Check that memory segments are within bounds.
+ //--------------------------------------------------------------------------
+ for (WasmDataSegment& seg : module_->data_segments) {
+ uint32_t base = EvalUint32InitExpr(seg.dest_addr);
+ uint32_t mem_size = memory_.is_null()
+ ? 0 : static_cast<uint32_t>(memory_->byte_length()->Number());
+ if (!in_bounds(base, seg.source_size, mem_size)) {
+ thrower_->LinkError("data segment is out of bounds");
+ return nothing;
+ }
+ }
+
+ //--------------------------------------------------------------------------
+ // Initialize memory.
+ //--------------------------------------------------------------------------
if (!memory_.is_null()) {
instance->set_memory_buffer(*memory_);
Address mem_start = static_cast<Address>(memory_->backing_store());
uint32_t mem_size =
static_cast<uint32_t>(memory_->byte_length()->Number());
- LoadDataSegments(mem_start, mem_size);
+ if (!LoadDataSegments(mem_start, mem_size)) return nothing;
uint32_t old_mem_size = compiled_module_->mem_size();
Address old_mem_start =
@@ -1117,11 +1353,10 @@ class WasmInstanceBuilder {
? static_cast<Address>(
compiled_module_->memory()->backing_store())
: nullptr;
- RelocateMemoryReferencesInCode(code_table, old_mem_start, mem_start,
- old_mem_size, mem_size);
+ RelocateMemoryReferencesInCode(
+ code_table, module_->num_imported_functions, old_mem_start, mem_start,
+ old_mem_size, mem_size);
compiled_module_->set_memory(memory_);
- } else {
- LoadDataSegments(nullptr, 0);
}
//--------------------------------------------------------------------------
@@ -1144,21 +1379,61 @@ class WasmInstanceBuilder {
//--------------------------------------------------------------------------
// Set up the exports object for the new instance.
//--------------------------------------------------------------------------
- ProcessExports(code_table, instance);
+ ProcessExports(code_table, instance, compiled_module_);
//--------------------------------------------------------------------------
- // Set up the indirect function tables for the new instance.
+ // Add instance to Memory object
//--------------------------------------------------------------------------
- if (function_table_count > 0) InitializeTables(code_table, instance);
-
- if (num_imported_functions > 0 || !owner.is_null()) {
- // If the code was cloned, or new imports were compiled, patch.
- PatchDirectCalls(old_code_table, code_table, num_imported_functions);
+ DCHECK(wasm::IsWasmInstance(*instance));
+ if (instance->has_memory_object()) {
+ instance->memory_object()->AddInstance(isolate_, instance);
}
+ //--------------------------------------------------------------------------
+ // Initialize the indirect function tables.
+ //--------------------------------------------------------------------------
+ if (function_table_count > 0) LoadTableSegments(code_table, instance);
+
+ // Patch new call sites and the context.
+ PatchDirectCallsAndContext(code_table, compiled_module_, module_,
+ num_imported_functions);
+
FlushICache(isolate_, code_table);
//--------------------------------------------------------------------------
+ // Unpack and notify signal handler of protected instructions.
+ //--------------------------------------------------------------------------
+ {
+ for (int i = 0; i < code_table->length(); ++i) {
+ Handle<Code> code = code_table->GetValueChecked<Code>(isolate_, i);
+
+ if (code->kind() != Code::WASM_FUNCTION) {
+ continue;
+ }
+
+ FixedArray* protected_instructions = code->protected_instructions();
+ DCHECK(protected_instructions != nullptr);
+ Zone zone(isolate_->allocator(), "Wasm Module");
+ ZoneVector<trap_handler::ProtectedInstructionData> unpacked(&zone);
+ for (int i = 0; i < protected_instructions->length();
+ i += Code::kTrapDataSize) {
+ trap_handler::ProtectedInstructionData data;
+ data.instr_offset =
+ protected_instructions
+ ->GetValueChecked<Smi>(isolate_, i + Code::kTrapCodeOffset)
+ ->value();
+ data.landing_offset =
+ protected_instructions
+ ->GetValueChecked<Smi>(isolate_, i + Code::kTrapLandingOffset)
+ ->value();
+ unpacked.emplace_back(data);
+ }
+ // TODO(eholk): Register the protected instruction information once the
+ // trap handler is in place.
+ }
+ }
+
+ //--------------------------------------------------------------------------
// Set up and link the new instance.
//--------------------------------------------------------------------------
{
@@ -1174,7 +1449,7 @@ class WasmInstanceBuilder {
// we want all the publishing to happen free from GC interruptions, and
// so we do it in
// one GC-free scope afterwards.
- original = handle(owner.ToHandleChecked()->get_compiled_module());
+ original = handle(owner.ToHandleChecked()->compiled_module());
link_to_original = factory->NewWeakCell(original.ToHandleChecked());
}
// Publish the new instance to the instances chain.
@@ -1194,30 +1469,20 @@ class WasmInstanceBuilder {
v8::WeakCallbackType::kFinalizer);
}
}
-
- DCHECK(wasm::IsWasmInstance(*instance));
- if (instance->has_memory_object()) {
- instance->get_memory_object()->AddInstance(*instance);
- }
-
//--------------------------------------------------------------------------
// Run the start function if one was specified.
//--------------------------------------------------------------------------
if (module_->start_function_index >= 0) {
HandleScope scope(isolate_);
- ModuleEnv module_env;
- module_env.module = module_;
- module_env.instance = nullptr;
- module_env.origin = module_->origin;
int start_index = module_->start_function_index;
Handle<Code> startup_code =
code_table->GetValueChecked<Code>(isolate_, start_index);
FunctionSig* sig = module_->functions[start_index].sig;
Handle<Code> wrapper_code = compiler::CompileJSToWasmWrapper(
- isolate_, &module_env, startup_code, start_index);
+ isolate_, module_, startup_code, start_index);
Handle<WasmExportedFunction> startup_fct = WasmExportedFunction::New(
- isolate_, instance, factory->InternalizeUtf8String("start"),
- wrapper_code, static_cast<int>(sig->parameter_count()), start_index);
+ isolate_, instance, MaybeHandle<String>(), start_index,
+ static_cast<int>(sig->parameter_count()), wrapper_code);
RecordStats(isolate_, *startup_code);
// Call the JS function.
Handle<Object> undefined = factory->undefined_value();
@@ -1237,7 +1502,7 @@ class WasmInstanceBuilder {
DCHECK(!isolate_->has_pending_exception());
TRACE("Finishing instance %d\n", compiled_module_->instance_id());
- TRACE_CHAIN(WasmCompiledModule::cast(module_object_->GetInternalField(0)));
+ TRACE_CHAIN(module_object_->compiled_module());
return instance;
}
@@ -1246,13 +1511,14 @@ class WasmInstanceBuilder {
struct TableInstance {
Handle<WasmTableObject> table_object; // WebAssembly.Table instance
Handle<FixedArray> js_wrappers; // JSFunctions exported
- Handle<FixedArray> dispatch_table; // internal (code, sig) pairs
+ Handle<FixedArray> function_table; // internal code array
+ Handle<FixedArray> signature_table; // internal sig array
};
Isolate* isolate_;
- WasmModule* module_;
+ WasmModule* const module_;
ErrorThrower* thrower_;
- Handle<JSObject> module_object_;
+ Handle<WasmModuleObject> module_object_;
Handle<JSReceiver> ffi_;
Handle<JSArrayBuffer> memory_;
Handle<JSArrayBuffer> globals_;
@@ -1260,58 +1526,49 @@ class WasmInstanceBuilder {
std::vector<TableInstance> table_instances_;
std::vector<Handle<JSFunction>> js_wrappers_;
- // Helper routine to print out errors with imports (FFI).
- MaybeHandle<JSFunction> ReportFFIError(const char* error, uint32_t index,
- Handle<String> module_name,
- MaybeHandle<String> function_name) {
- Handle<String> function_name_handle;
- if (function_name.ToHandle(&function_name_handle)) {
- thrower_->TypeError(
- "Import #%d module=\"%.*s\" function=\"%.*s\" error: %s", index,
- module_name->length(), module_name->ToCString().get(),
- function_name_handle->length(),
- function_name_handle->ToCString().get(), error);
- } else {
- thrower_->TypeError("Import #%d module=\"%.*s\" error: %s", index,
- module_name->length(), module_name->ToCString().get(),
- error);
- }
- thrower_->TypeError("Import ");
- return MaybeHandle<JSFunction>();
+ // Helper routines to print out errors with imports.
+ void ReportLinkError(const char* error, uint32_t index,
+ Handle<String> module_name, Handle<String> import_name) {
+ thrower_->LinkError(
+ "Import #%d module=\"%.*s\" function=\"%.*s\" error: %s", index,
+ module_name->length(), module_name->ToCString().get(),
+ import_name->length(), import_name->ToCString().get(), error);
+ }
+
+ MaybeHandle<Object> ReportLinkError(const char* error, uint32_t index,
+ Handle<String> module_name) {
+ thrower_->LinkError("Import #%d module=\"%.*s\" error: %s", index,
+ module_name->length(), module_name->ToCString().get(),
+ error);
+ return MaybeHandle<Object>();
}
// Look up an import value in the {ffi_} object.
MaybeHandle<Object> LookupImport(uint32_t index, Handle<String> module_name,
- MaybeHandle<String> import_name) {
- if (ffi_.is_null()) {
- return ReportFFIError("FFI is not an object", index, module_name,
- import_name);
- }
+ Handle<String> import_name) {
+ // We pre-validated in the js-api layer that the ffi object is present, and
+ // a JSObject, if the module has imports.
+ DCHECK(!ffi_.is_null());
// Look up the module first.
- MaybeHandle<Object> result = Object::GetProperty(ffi_, module_name);
+ MaybeHandle<Object> result =
+ Object::GetPropertyOrElement(ffi_, module_name);
if (result.is_null()) {
- return ReportFFIError("module not found", index, module_name,
- import_name);
+ return ReportLinkError("module not found", index, module_name);
}
Handle<Object> module = result.ToHandleChecked();
- if (!import_name.is_null()) {
- // Look up the value in the module.
- if (!module->IsJSReceiver()) {
- return ReportFFIError("module is not an object or function", index,
- module_name, import_name);
- }
+ // Look up the value in the module.
+ if (!module->IsJSReceiver()) {
+ return ReportLinkError("module is not an object or function", index,
+ module_name);
+ }
- result = Object::GetProperty(module, import_name.ToHandleChecked());
- if (result.is_null()) {
- return ReportFFIError("import not found", index, module_name,
- import_name);
- }
- } else {
- // No function specified. Use the "default export".
- result = module;
+ result = Object::GetPropertyOrElement(module, import_name);
+ if (result.is_null()) {
+ ReportLinkError("import not found", index, module_name, import_name);
+ return MaybeHandle<JSFunction>();
}
return result;
@@ -1331,26 +1588,27 @@ class WasmInstanceBuilder {
}
}
+ bool in_bounds(uint32_t offset, uint32_t size, uint32_t upper) {
+ return offset + size <= upper && offset + size >= offset;
+ }
+
// Load data segments into the memory.
- void LoadDataSegments(Address mem_addr, size_t mem_size) {
- Handle<SeqOneByteString> module_bytes = compiled_module_->module_bytes();
+ bool LoadDataSegments(Address mem_addr, size_t mem_size) {
+ Handle<SeqOneByteString> module_bytes(compiled_module_->module_bytes(),
+ isolate_);
for (const WasmDataSegment& segment : module_->data_segments) {
uint32_t source_size = segment.source_size;
// Segments of size == 0 are just nops.
if (source_size == 0) continue;
uint32_t dest_offset = EvalUint32InitExpr(segment.dest_addr);
- if (dest_offset >= mem_size || source_size >= mem_size ||
- dest_offset > (mem_size - source_size)) {
- thrower_->TypeError("data segment (start = %" PRIu32 ", size = %" PRIu32
- ") does not fit into memory (size = %" PRIuS ")",
- dest_offset, source_size, mem_size);
- return;
- }
+ DCHECK(in_bounds(dest_offset, source_size,
+ static_cast<uint32_t>(mem_size)));
byte* dest = mem_addr + dest_offset;
const byte* src = reinterpret_cast<const byte*>(
module_bytes->GetCharsAddress() + segment.source_offset);
memcpy(dest, src, source_size);
}
+ return true;
}
void WriteGlobalValue(WasmGlobal& global, Handle<Object> value) {
@@ -1365,17 +1623,17 @@ class WasmInstanceBuilder {
TRACE("init [globals+%u] = %lf, type = %s\n", global.offset, num,
WasmOpcodes::TypeName(global.type));
switch (global.type) {
- case kAstI32:
+ case kWasmI32:
*GetRawGlobalPtr<int32_t>(global) = static_cast<int32_t>(num);
break;
- case kAstI64:
+ case kWasmI64:
// TODO(titzer): initialization of imported i64 globals.
UNREACHABLE();
break;
- case kAstF32:
+ case kWasmF32:
*GetRawGlobalPtr<float>(global) = static_cast<float>(num);
break;
- case kAstF64:
+ case kWasmF64:
*GetRawGlobalPtr<double>(global) = static_cast<double>(num);
break;
default:
@@ -1393,39 +1651,43 @@ class WasmInstanceBuilder {
for (int index = 0; index < static_cast<int>(module_->import_table.size());
++index) {
WasmImport& import = module_->import_table[index];
- Handle<String> module_name =
- ExtractStringFromModuleBytes(isolate_, compiled_module_,
- import.module_name_offset,
- import.module_name_length)
- .ToHandleChecked();
- Handle<String> function_name = Handle<String>::null();
- if (import.field_name_length > 0) {
- function_name = ExtractStringFromModuleBytes(isolate_, compiled_module_,
- import.field_name_offset,
- import.field_name_length)
- .ToHandleChecked();
- }
+
+ Handle<String> module_name;
+ MaybeHandle<String> maybe_module_name =
+ WasmCompiledModule::ExtractUtf8StringFromModuleBytes(
+ isolate_, compiled_module_, import.module_name_offset,
+ import.module_name_length);
+ if (!maybe_module_name.ToHandle(&module_name)) return -1;
+
+ Handle<String> import_name;
+ MaybeHandle<String> maybe_import_name =
+ WasmCompiledModule::ExtractUtf8StringFromModuleBytes(
+ isolate_, compiled_module_, import.field_name_offset,
+ import.field_name_length);
+ if (!maybe_import_name.ToHandle(&import_name)) return -1;
MaybeHandle<Object> result =
- LookupImport(index, module_name, function_name);
+ LookupImport(index, module_name, import_name);
if (thrower_->error()) return -1;
+ Handle<Object> value = result.ToHandleChecked();
switch (import.kind) {
case kExternalFunction: {
// Function imports must be callable.
- Handle<Object> function = result.ToHandleChecked();
- if (!function->IsCallable()) {
- ReportFFIError("function import requires a callable", index,
- module_name, function_name);
+ if (!value->IsCallable()) {
+ ReportLinkError("function import requires a callable", index,
+ module_name, import_name);
return -1;
}
Handle<Code> import_wrapper = CompileImportWrapper(
isolate_, index, module_->functions[import.index].sig,
- Handle<JSReceiver>::cast(function), module_name, function_name);
+ Handle<JSReceiver>::cast(value), module_name, import_name,
+ module_->origin);
if (import_wrapper.is_null()) {
- ReportFFIError("imported function does not match the expected type",
- index, module_name, function_name);
+ ReportLinkError(
+ "imported function does not match the expected type", index,
+ module_name, import_name);
return -1;
}
code_table->set(num_imported_functions, *import_wrapper);
@@ -1434,10 +1696,9 @@ class WasmInstanceBuilder {
break;
}
case kExternalTable: {
- Handle<Object> value = result.ToHandleChecked();
if (!WasmJs::IsWasmTableObject(isolate_, value)) {
- ReportFFIError("table import requires a WebAssembly.Table", index,
- module_name, function_name);
+ ReportLinkError("table import requires a WebAssembly.Table", index,
+ module_name, import_name);
return -1;
}
WasmIndirectFunctionTable& table =
@@ -1445,23 +1706,43 @@ class WasmInstanceBuilder {
TableInstance& table_instance = table_instances_[num_imported_tables];
table_instance.table_object = Handle<WasmTableObject>::cast(value);
table_instance.js_wrappers = Handle<FixedArray>(
- table_instance.table_object->get_functions(), isolate_);
-
- // TODO(titzer): import table size must match exactly for now.
- int table_size = table_instance.js_wrappers->length();
- if (table_size != static_cast<int>(table.min_size)) {
- thrower_->TypeError(
- "table import %d is wrong size (%d), expected %u", index,
- table_size, table.min_size);
+ table_instance.table_object->functions(), isolate_);
+
+ int imported_cur_size = table_instance.js_wrappers->length();
+ if (imported_cur_size < static_cast<int>(table.min_size)) {
+ thrower_->LinkError(
+ "table import %d is smaller than minimum %d, got %u", index,
+ table.min_size, imported_cur_size);
return -1;
}
- // Allocate a new dispatch table.
- table_instance.dispatch_table =
- isolate_->factory()->NewFixedArray(table_size * 2);
- for (int i = 0; i < table_size * 2; ++i) {
- table_instance.dispatch_table->set(i,
- Smi::FromInt(kInvalidSigIndex));
+ if (table.has_max) {
+ int64_t imported_max_size =
+ table_instance.table_object->maximum_length();
+ if (imported_max_size < 0) {
+ thrower_->LinkError(
+ "table import %d has no maximum length, expected %d", index,
+ table.max_size);
+ return -1;
+ }
+ if (imported_max_size > table.max_size) {
+ thrower_->LinkError(
+ "table import %d has maximum larger than maximum %d, "
+ "got %" PRIx64,
+ index, table.max_size, imported_max_size);
+ return -1;
+ }
+ }
+
+ // Allocate a new dispatch table and signature table.
+ int table_size = imported_cur_size;
+ table_instance.function_table =
+ isolate_->factory()->NewFixedArray(table_size);
+ table_instance.signature_table =
+ isolate_->factory()->NewFixedArray(table_size);
+ for (int i = 0; i < table_size; ++i) {
+ table_instance.signature_table->set(i,
+ Smi::FromInt(kInvalidSigIndex));
}
// Initialize the dispatch table with the (foreign) JS functions
// that are already in the table.
@@ -1471,43 +1752,70 @@ class WasmInstanceBuilder {
WasmFunction* function =
GetWasmFunctionForImportWrapper(isolate_, val);
if (function == nullptr) {
- thrower_->TypeError("table import %d[%d] is not a WASM function",
+ thrower_->LinkError("table import %d[%d] is not a WASM function",
index, i);
return -1;
}
int sig_index = table.map.FindOrInsert(function->sig);
- table_instance.dispatch_table->set(i, Smi::FromInt(sig_index));
- table_instance.dispatch_table->set(i + table_size,
- *UnwrapImportWrapper(val));
+ table_instance.signature_table->set(i, Smi::FromInt(sig_index));
+ table_instance.function_table->set(i, *UnwrapImportWrapper(val));
}
num_imported_tables++;
break;
}
case kExternalMemory: {
- Handle<Object> object = result.ToHandleChecked();
- if (!WasmJs::IsWasmMemoryObject(isolate_, object)) {
- ReportFFIError("memory import must be a WebAssembly.Memory object",
- index, module_name, function_name);
+ // Validation should have failed if more than one memory object was
+ // provided.
+ DCHECK(!instance->has_memory_object());
+ if (!WasmJs::IsWasmMemoryObject(isolate_, value)) {
+ ReportLinkError("memory import must be a WebAssembly.Memory object",
+ index, module_name, import_name);
return -1;
}
- auto memory = Handle<WasmMemoryObject>::cast(object);
+ auto memory = Handle<WasmMemoryObject>::cast(value);
+ DCHECK(WasmJs::IsWasmMemoryObject(isolate_, memory));
instance->set_memory_object(*memory);
- memory_ = Handle<JSArrayBuffer>(memory->get_buffer(), isolate_);
+ memory_ = Handle<JSArrayBuffer>(memory->buffer(), isolate_);
+ uint32_t imported_cur_pages = static_cast<uint32_t>(
+ memory_->byte_length()->Number() / WasmModule::kPageSize);
+ if (imported_cur_pages < module_->min_mem_pages) {
+ thrower_->LinkError(
+ "memory import %d is smaller than maximum %u, got %u", index,
+ module_->min_mem_pages, imported_cur_pages);
+ }
+ int32_t imported_max_pages = memory->maximum_pages();
+ if (module_->has_max_mem) {
+ if (imported_max_pages < 0) {
+ thrower_->LinkError(
+ "memory import %d has no maximum limit, expected at most %u",
+ index, imported_max_pages);
+ return -1;
+ }
+ if (static_cast<uint32_t>(imported_max_pages) >
+ module_->max_mem_pages) {
+ thrower_->LinkError(
+ "memory import %d has larger maximum than maximum %u, got %d",
+ index, module_->max_mem_pages, imported_max_pages);
+ return -1;
+ }
+ }
break;
}
case kExternalGlobal: {
// Global imports are converted to numbers and written into the
// {globals_} array buffer.
- Handle<Object> object = result.ToHandleChecked();
- MaybeHandle<Object> number = Object::ToNumber(object);
- if (number.is_null()) {
- ReportFFIError("global import could not be converted to number",
- index, module_name, function_name);
+ if (module_->globals[import.index].type == kWasmI64) {
+ ReportLinkError("global import cannot have type i64", index,
+ module_name, import_name);
+ return -1;
+ }
+ if (!value->IsNumber()) {
+ ReportLinkError("global import must be a number", index,
+ module_name, import_name);
return -1;
}
- Handle<Object> val = number.ToHandleChecked();
- WriteGlobalValue(module_->globals[import.index], val);
+ WriteGlobalValue(module_->globals[import.index], value);
break;
}
default:
@@ -1546,7 +1854,7 @@ class WasmInstanceBuilder {
module_->globals[global.init.val.global_index].offset;
TRACE("init [globals+%u] = [globals+%d]\n", global.offset,
old_offset);
- size_t size = (global.type == kAstI64 || global.type == kAstF64)
+ size_t size = (global.type == kWasmI64 || global.type == kWasmF64)
? sizeof(double)
: sizeof(int32_t);
memcpy(raw_buffer_ptr(globals_, new_offset),
@@ -1565,12 +1873,13 @@ class WasmInstanceBuilder {
// Allocate memory for a module instance as a new JSArrayBuffer.
Handle<JSArrayBuffer> AllocateMemory(uint32_t min_mem_pages) {
- if (min_mem_pages > WasmModule::kV8MaxPages) {
+ if (min_mem_pages > kV8MaxWasmMemoryPages) {
thrower_->RangeError("Out of memory: wasm memory too large");
return Handle<JSArrayBuffer>::null();
}
- Handle<JSArrayBuffer> mem_buffer =
- NewArrayBuffer(isolate_, min_mem_pages * WasmModule::kPageSize);
+ const bool enable_guard_regions = EnableGuardRegions();
+ Handle<JSArrayBuffer> mem_buffer = NewArrayBuffer(
+ isolate_, min_mem_pages * WasmModule::kPageSize, enable_guard_regions);
if (mem_buffer.is_null()) {
thrower_->RangeError("Out of memory: wasm memory");
@@ -1578,31 +1887,30 @@ class WasmInstanceBuilder {
return mem_buffer;
}
- // Process the exports, creating wrappers for functions, tables, memories,
- // and globals.
- void ProcessExports(Handle<FixedArray> code_table,
- Handle<WasmInstanceObject> instance) {
- bool needs_wrappers = module_->num_exported_functions > 0;
+ bool NeedsWrappers() {
+ if (module_->num_exported_functions > 0) return true;
for (auto table_instance : table_instances_) {
- if (!table_instance.js_wrappers.is_null()) {
- needs_wrappers = true;
- break;
- }
+ if (!table_instance.js_wrappers.is_null()) return true;
}
for (auto table : module_->function_tables) {
- if (table.exported) {
- needs_wrappers = true;
- break;
- }
+ if (table.exported) return true;
}
- if (needs_wrappers) {
+ return false;
+ }
+
+ // Process the exports, creating wrappers for functions, tables, memories,
+ // and globals.
+ void ProcessExports(Handle<FixedArray> code_table,
+ Handle<WasmInstanceObject> instance,
+ Handle<WasmCompiledModule> compiled_module) {
+ if (NeedsWrappers()) {
// Fill the table to cache the exported JSFunction wrappers.
js_wrappers_.insert(js_wrappers_.begin(), module_->functions.size(),
Handle<JSFunction>::null());
}
Handle<JSObject> exports_object = instance;
- if (module_->export_table.size() > 0 && module_->origin == kWasmOrigin) {
+ if (module_->origin == kWasmOrigin) {
// Create the "exports" object.
Handle<JSFunction> object_function = Handle<JSFunction>(
isolate_->native_context()->object_function(), isolate_);
@@ -1610,38 +1918,68 @@ class WasmInstanceBuilder {
isolate_->factory()->NewJSObject(object_function, TENURED);
Handle<String> exports_name =
isolate_->factory()->InternalizeUtf8String("exports");
- JSObject::AddProperty(instance, exports_name, exports_object, READ_ONLY);
+ JSObject::AddProperty(instance, exports_name, exports_object, NONE);
}
PropertyDescriptor desc;
desc.set_writable(false);
+ desc.set_enumerable(true);
- // Process each export in the export table.
+ // Count up export indexes.
int export_index = 0;
for (auto exp : module_->export_table) {
+ if (exp.kind == kExternalFunction) {
+ ++export_index;
+ }
+ }
+
+ // Store weak references to all exported functions.
+ Handle<FixedArray> weak_exported_functions;
+ if (compiled_module->has_weak_exported_functions()) {
+ weak_exported_functions = compiled_module->weak_exported_functions();
+ } else {
+ weak_exported_functions =
+ isolate_->factory()->NewFixedArray(export_index);
+ compiled_module->set_weak_exported_functions(weak_exported_functions);
+ }
+ DCHECK_EQ(export_index, weak_exported_functions->length());
+
+ // Process each export in the export table (go in reverse so asm.js
+ // can skip duplicates).
+ for (auto exp : base::Reversed(module_->export_table)) {
Handle<String> name =
- ExtractStringFromModuleBytes(isolate_, compiled_module_,
- exp.name_offset, exp.name_length)
+ WasmCompiledModule::ExtractUtf8StringFromModuleBytes(
+ isolate_, compiled_module_, exp.name_offset, exp.name_length)
.ToHandleChecked();
switch (exp.kind) {
case kExternalFunction: {
// Wrap and export the code as a JSFunction.
WasmFunction& function = module_->functions[exp.index];
int func_index =
- static_cast<int>(module_->functions.size() + export_index);
+ static_cast<int>(module_->functions.size() + --export_index);
Handle<JSFunction> js_function = js_wrappers_[exp.index];
if (js_function.is_null()) {
// Wrap the exported code as a JSFunction.
Handle<Code> export_code =
code_table->GetValueChecked<Code>(isolate_, func_index);
+ MaybeHandle<String> func_name;
+ if (module_->origin == kAsmJsOrigin) {
+ // For modules arising from asm.js, honor the names section.
+ func_name = WasmCompiledModule::ExtractUtf8StringFromModuleBytes(
+ isolate_, compiled_module_, function.name_offset,
+ function.name_length)
+ .ToHandleChecked();
+ }
js_function = WasmExportedFunction::New(
- isolate_, instance, name, export_code,
- static_cast<int>(function.sig->parameter_count()),
- function.func_index);
+ isolate_, instance, func_name, function.func_index,
+ static_cast<int>(function.sig->parameter_count()), export_code);
js_wrappers_[exp.index] = js_function;
}
desc.set_value(js_function);
- export_index++;
+ Handle<WeakCell> weak_export =
+ isolate_->factory()->NewWeakCell(js_function);
+ DCHECK_GT(weak_exported_functions->length(), export_index);
+ weak_exported_functions->set(export_index, *weak_export);
break;
}
case kExternalTable: {
@@ -1651,7 +1989,7 @@ class WasmInstanceBuilder {
module_->function_tables[exp.index];
if (table_instance.table_object.is_null()) {
uint32_t maximum =
- table.has_max ? table.max_size : WasmModule::kV8MaxTableSize;
+ table.has_max ? table.max_size : kV8MaxWasmTableSize;
table_instance.table_object = WasmTableObject::New(
isolate_, table.min_size, maximum, &table_instance.js_wrappers);
}
@@ -1663,15 +2001,16 @@ class WasmInstanceBuilder {
Handle<WasmMemoryObject> memory_object;
if (!instance->has_memory_object()) {
// If there was no imported WebAssembly.Memory object, create one.
- Handle<JSArrayBuffer> buffer(instance->get_memory_buffer(),
- isolate_);
+ Handle<JSArrayBuffer> buffer(instance->memory_buffer(), isolate_);
memory_object = WasmMemoryObject::New(
isolate_, buffer,
(module_->max_mem_pages != 0) ? module_->max_mem_pages : -1);
instance->set_memory_object(*memory_object);
} else {
- memory_object = Handle<WasmMemoryObject>(
- instance->get_memory_object(), isolate_);
+ memory_object =
+ Handle<WasmMemoryObject>(instance->memory_object(), isolate_);
+ DCHECK(WasmJs::IsWasmMemoryObject(isolate_, memory_object));
+ memory_object->ResetInstancesLink(isolate_);
}
desc.set_value(memory_object);
@@ -1682,15 +2021,19 @@ class WasmInstanceBuilder {
WasmGlobal& global = module_->globals[exp.index];
double num = 0;
switch (global.type) {
- case kAstI32:
+ case kWasmI32:
num = *GetRawGlobalPtr<int32_t>(global);
break;
- case kAstF32:
+ case kWasmF32:
num = *GetRawGlobalPtr<float>(global);
break;
- case kAstF64:
+ case kWasmF64:
num = *GetRawGlobalPtr<double>(global);
break;
+ case kWasmI64:
+ thrower_->LinkError(
+ "export of globals of type I64 is not allowed.");
+ break;
default:
UNREACHABLE();
}
@@ -1702,42 +2045,99 @@ class WasmInstanceBuilder {
break;
}
+ // Skip duplicates for asm.js.
+ if (module_->origin == kAsmJsOrigin) {
+ v8::Maybe<bool> status =
+ JSReceiver::HasOwnProperty(exports_object, name);
+ if (status.FromMaybe(false)) {
+ continue;
+ }
+ }
v8::Maybe<bool> status = JSReceiver::DefineOwnProperty(
isolate_, exports_object, name, &desc, Object::THROW_ON_ERROR);
if (!status.IsJust()) {
- thrower_->TypeError("export of %.*s failed.", name->length(),
+ thrower_->LinkError("export of %.*s failed.", name->length(),
name->ToCString().get());
return;
}
}
+
+ if (module_->origin == kWasmOrigin) {
+ v8::Maybe<bool> success = JSReceiver::SetIntegrityLevel(
+ exports_object, FROZEN, Object::DONT_THROW);
+ DCHECK(success.FromMaybe(false));
+ USE(success);
+ }
}
void InitializeTables(Handle<FixedArray> code_table,
Handle<WasmInstanceObject> instance) {
- Handle<FixedArray> old_function_tables =
- compiled_module_->function_tables();
int function_table_count =
static_cast<int>(module_->function_tables.size());
Handle<FixedArray> new_function_tables =
isolate_->factory()->NewFixedArray(function_table_count);
+ Handle<FixedArray> new_signature_tables =
+ isolate_->factory()->NewFixedArray(function_table_count);
for (int index = 0; index < function_table_count; ++index) {
WasmIndirectFunctionTable& table = module_->function_tables[index];
TableInstance& table_instance = table_instances_[index];
int table_size = static_cast<int>(table.min_size);
- if (table_instance.dispatch_table.is_null()) {
+ if (table_instance.function_table.is_null()) {
// Create a new dispatch table if necessary.
- table_instance.dispatch_table =
- isolate_->factory()->NewFixedArray(table_size * 2);
+ table_instance.function_table =
+ isolate_->factory()->NewFixedArray(table_size);
+ table_instance.signature_table =
+ isolate_->factory()->NewFixedArray(table_size);
for (int i = 0; i < table_size; ++i) {
// Fill the table with invalid signature indexes so that
// uninitialized entries will always fail the signature check.
- table_instance.dispatch_table->set(i, Smi::FromInt(kInvalidSigIndex));
+ table_instance.signature_table->set(i,
+ Smi::FromInt(kInvalidSigIndex));
+ }
+ } else {
+ // Table is imported, patch table bounds check
+ DCHECK(table_size <= table_instance.function_table->length());
+ if (table_size < table_instance.function_table->length()) {
+ RelocateTableSizeReferences(code_table, table_size,
+ table_instance.function_table->length());
}
}
new_function_tables->set(static_cast<int>(index),
- *table_instance.dispatch_table);
+ *table_instance.function_table);
+ new_signature_tables->set(static_cast<int>(index),
+ *table_instance.signature_table);
+ }
+
+ // Patch all code that has references to the old indirect tables.
+ Handle<FixedArray> old_function_tables =
+ compiled_module_->function_tables();
+ Handle<FixedArray> old_signature_tables =
+ compiled_module_->signature_tables();
+ for (int i = 0; i < code_table->length(); ++i) {
+ if (!code_table->get(i)->IsCode()) continue;
+ Handle<Code> code(Code::cast(code_table->get(i)), isolate_);
+ for (int j = 0; j < function_table_count; ++j) {
+ ReplaceReferenceInCode(
+ code, Handle<Object>(old_function_tables->get(j), isolate_),
+ Handle<Object>(new_function_tables->get(j), isolate_));
+ ReplaceReferenceInCode(
+ code, Handle<Object>(old_signature_tables->get(j), isolate_),
+ Handle<Object>(new_signature_tables->get(j), isolate_));
+ }
+ }
+ compiled_module_->set_function_tables(new_function_tables);
+ compiled_module_->set_signature_tables(new_signature_tables);
+ }
+
+ void LoadTableSegments(Handle<FixedArray> code_table,
+ Handle<WasmInstanceObject> instance) {
+ int function_table_count =
+ static_cast<int>(module_->function_tables.size());
+ for (int index = 0; index < function_table_count; ++index) {
+ WasmIndirectFunctionTable& table = module_->function_tables[index];
+ TableInstance& table_instance = table_instances_[index];
Handle<FixedArray> all_dispatch_tables;
if (!table_instance.table_object.is_null()) {
@@ -1745,28 +2145,24 @@ class WasmInstanceBuilder {
all_dispatch_tables = WasmTableObject::AddDispatchTable(
isolate_, table_instance.table_object,
Handle<WasmInstanceObject>::null(), index,
- Handle<FixedArray>::null());
+ Handle<FixedArray>::null(), Handle<FixedArray>::null());
}
// TODO(titzer): this does redundant work if there are multiple tables,
// since initializations are not sorted by table index.
for (auto table_init : module_->table_inits) {
uint32_t base = EvalUint32InitExpr(table_init.offset);
- if (base > static_cast<uint32_t>(table_size) ||
- (base + table_init.entries.size() >
- static_cast<uint32_t>(table_size))) {
- thrower_->CompileError("table initializer is out of bounds");
- continue;
- }
+ DCHECK(in_bounds(base, static_cast<uint32_t>(table_init.entries.size()),
+ table_instance.function_table->length()));
for (int i = 0; i < static_cast<int>(table_init.entries.size()); ++i) {
uint32_t func_index = table_init.entries[i];
WasmFunction* function = &module_->functions[func_index];
int table_index = static_cast<int>(i + base);
int32_t sig_index = table.map.Find(function->sig);
DCHECK_GE(sig_index, 0);
- table_instance.dispatch_table->set(table_index,
- Smi::FromInt(sig_index));
- table_instance.dispatch_table->set(table_index + table_size,
+ table_instance.signature_table->set(table_index,
+ Smi::FromInt(sig_index));
+ table_instance.function_table->set(table_index,
code_table->get(func_index));
if (!all_dispatch_tables.is_null()) {
@@ -1783,19 +2179,22 @@ class WasmInstanceBuilder {
temp_instance.mem_start = nullptr;
temp_instance.globals_start = nullptr;
- ModuleEnv module_env;
- module_env.module = module_;
- module_env.instance = &temp_instance;
- module_env.origin = module_->origin;
-
Handle<Code> wrapper_code = compiler::CompileJSToWasmWrapper(
- isolate_, &module_env, wasm_code, func_index);
+ isolate_, module_, wasm_code, func_index);
+ MaybeHandle<String> func_name;
+ if (module_->origin == kAsmJsOrigin) {
+ // For modules arising from asm.js, honor the names section.
+ func_name =
+ WasmCompiledModule::ExtractUtf8StringFromModuleBytes(
+ isolate_, compiled_module_, function->name_offset,
+ function->name_length)
+ .ToHandleChecked();
+ }
Handle<WasmExportedFunction> js_function =
WasmExportedFunction::New(
- isolate_, instance, isolate_->factory()->empty_string(),
- wrapper_code,
+ isolate_, instance, func_name, func_index,
static_cast<int>(function->sig->parameter_count()),
- func_index);
+ wrapper_code);
js_wrappers_[func_index] = js_function;
}
table_instance.js_wrappers->set(table_index,
@@ -1814,115 +2213,57 @@ class WasmInstanceBuilder {
// Add the new dispatch table to the WebAssembly.Table object.
all_dispatch_tables = WasmTableObject::AddDispatchTable(
isolate_, table_instance.table_object, instance, index,
- table_instance.dispatch_table);
+ table_instance.function_table, table_instance.signature_table);
}
}
- // Patch all code that has references to the old indirect tables.
- for (int i = 0; i < code_table->length(); ++i) {
- if (!code_table->get(i)->IsCode()) continue;
- Handle<Code> code(Code::cast(code_table->get(i)), isolate_);
- for (int j = 0; j < function_table_count; ++j) {
- ReplaceReferenceInCode(
- code, Handle<Object>(old_function_tables->get(j), isolate_),
- Handle<Object>(new_function_tables->get(j), isolate_));
- }
- }
- compiled_module_->set_function_tables(new_function_tables);
}
};
// Instantiates a WASM module, creating a WebAssembly.Instance from a
// WebAssembly.Module.
-MaybeHandle<JSObject> WasmModule::Instantiate(Isolate* isolate,
- ErrorThrower* thrower,
- Handle<JSObject> wasm_module,
- Handle<JSReceiver> ffi,
- Handle<JSArrayBuffer> memory) {
+MaybeHandle<WasmInstanceObject> WasmModule::Instantiate(
+ Isolate* isolate, ErrorThrower* thrower,
+ Handle<WasmModuleObject> wasm_module, Handle<JSReceiver> ffi,
+ Handle<JSArrayBuffer> memory) {
WasmInstanceBuilder builder(isolate, thrower, wasm_module, ffi, memory);
return builder.Build();
}
-Handle<String> wasm::GetWasmFunctionName(Isolate* isolate,
- Handle<Object> instance_or_undef,
- uint32_t func_index) {
- if (!instance_or_undef->IsUndefined(isolate)) {
- Handle<WasmCompiledModule> compiled_module(
- Handle<WasmInstanceObject>::cast(instance_or_undef)
- ->get_compiled_module());
- MaybeHandle<String> maybe_name =
- WasmCompiledModule::GetFunctionName(compiled_module, func_index);
- if (!maybe_name.is_null()) return maybe_name.ToHandleChecked();
- }
- return isolate->factory()->NewStringFromStaticChars("<WASM UNNAMED>");
-}
-
bool wasm::IsWasmInstance(Object* object) {
return WasmInstanceObject::IsWasmInstanceObject(object);
}
-WasmCompiledModule* wasm::GetCompiledModule(Object* object) {
- return WasmInstanceObject::cast(object)->get_compiled_module();
-}
-
-bool wasm::WasmIsAsmJs(Object* instance, Isolate* isolate) {
- if (instance->IsUndefined(isolate)) return false;
- DCHECK(IsWasmInstance(instance));
- WasmCompiledModule* compiled_module =
- GetCompiledModule(JSObject::cast(instance));
- DCHECK_EQ(compiled_module->has_asm_js_offset_tables(),
- compiled_module->script()->type() == Script::TYPE_NORMAL);
- return compiled_module->has_asm_js_offset_tables();
-}
-
Handle<Script> wasm::GetScript(Handle<JSObject> instance) {
- DCHECK(IsWasmInstance(*instance));
- WasmCompiledModule* compiled_module = GetCompiledModule(*instance);
- DCHECK(compiled_module->has_script());
- return compiled_module->script();
-}
-
-int wasm::GetAsmWasmSourcePosition(Handle<JSObject> instance, int func_index,
- int byte_offset) {
- return WasmDebugInfo::GetAsmJsSourcePosition(GetDebugInfo(instance),
- func_index, byte_offset);
-}
-
-Handle<SeqOneByteString> wasm::GetWasmBytes(Handle<JSObject> object) {
- return Handle<WasmInstanceObject>::cast(object)
- ->get_compiled_module()
- ->module_bytes();
-}
-
-Handle<WasmDebugInfo> wasm::GetDebugInfo(Handle<JSObject> object) {
- auto instance = Handle<WasmInstanceObject>::cast(object);
- if (instance->has_debug_info()) {
- Handle<WasmDebugInfo> info(instance->get_debug_info(),
- instance->GetIsolate());
- return info;
- }
- Handle<WasmDebugInfo> new_info = WasmDebugInfo::New(instance);
- instance->set_debug_info(*new_info);
- return new_info;
+ WasmCompiledModule* compiled_module =
+ WasmInstanceObject::cast(*instance)->compiled_module();
+ return handle(compiled_module->script());
}
-int wasm::GetNumberOfFunctions(Handle<JSObject> object) {
- return static_cast<int>(
- Handle<WasmInstanceObject>::cast(object)->module()->functions.size());
+bool wasm::IsWasmCodegenAllowed(Isolate* isolate, Handle<Context> context) {
+ return isolate->allow_code_gen_callback() == nullptr ||
+ isolate->allow_code_gen_callback()(v8::Utils::ToLocal(context));
}
// TODO(clemensh): origin can be inferred from asm_js_script; remove it.
MaybeHandle<WasmModuleObject> wasm::CreateModuleObjectFromBytes(
Isolate* isolate, const byte* start, const byte* end, ErrorThrower* thrower,
ModuleOrigin origin, Handle<Script> asm_js_script,
- const byte* asm_js_offset_tables_start,
- const byte* asm_js_offset_tables_end) {
+ Vector<const byte> asm_js_offset_table_bytes) {
MaybeHandle<WasmModuleObject> nothing;
+
+ if (origin != kAsmJsOrigin &&
+ !IsWasmCodegenAllowed(isolate, isolate->native_context())) {
+ thrower->CompileError("Wasm code generation disallowed in this context");
+ return nothing;
+ }
+
ModuleResult result = DecodeWasmModule(isolate, start, end, false, origin);
if (result.failed()) {
if (result.val) delete result.val;
thrower->CompileFailed("Wasm decoding failed", result);
return nothing;
}
+
// The {module_wrapper} will take ownership of the {WasmModule} object,
// and it will be destroyed when the GC reclaims the wrapper object.
Handle<WasmModuleWrapper> module_wrapper =
@@ -1930,61 +2271,15 @@ MaybeHandle<WasmModuleObject> wasm::CreateModuleObjectFromBytes(
// Compile the functions of the module, producing a compiled module.
MaybeHandle<WasmCompiledModule> maybe_compiled_module =
- result.val->CompileFunctions(isolate, module_wrapper, thrower);
+ result.val->CompileFunctions(isolate, module_wrapper, thrower,
+ ModuleWireBytes(start, end), asm_js_script,
+ asm_js_offset_table_bytes);
if (maybe_compiled_module.is_null()) return nothing;
Handle<WasmCompiledModule> compiled_module =
maybe_compiled_module.ToHandleChecked();
- DCHECK_EQ(origin == kAsmJsOrigin, !asm_js_script.is_null());
- DCHECK(!compiled_module->has_script());
- DCHECK(!compiled_module->has_asm_js_offset_tables());
- if (origin == kAsmJsOrigin) {
- // Set script for the asm.js source, and the offset table mapping wasm byte
- // offsets to source positions.
- compiled_module->set_script(asm_js_script);
- size_t offset_tables_len =
- asm_js_offset_tables_end - asm_js_offset_tables_start;
- DCHECK_GE(static_cast<size_t>(kMaxInt), offset_tables_len);
- Handle<ByteArray> offset_tables =
- isolate->factory()->NewByteArray(static_cast<int>(offset_tables_len));
- memcpy(offset_tables->GetDataStartAddress(), asm_js_offset_tables_start,
- offset_tables_len);
- compiled_module->set_asm_js_offset_tables(offset_tables);
- } else {
- // Create a new Script object representing this wasm module, store it in the
- // compiled wasm module, and register it at the debugger.
- Handle<Script> script =
- isolate->factory()->NewScript(isolate->factory()->empty_string());
- script->set_type(Script::TYPE_WASM);
-
- DCHECK_GE(kMaxInt, end - start);
- int hash = StringHasher::HashSequentialString(
- reinterpret_cast<const char*>(start), static_cast<int>(end - start),
- kZeroHashSeed);
-
- const int kBufferSize = 50;
- char buffer[kBufferSize];
- int url_chars = SNPrintF(ArrayVector(buffer), "wasm://wasm/%08x", hash);
- DCHECK(url_chars >= 0 && url_chars < kBufferSize);
- MaybeHandle<String> url_str = isolate->factory()->NewStringFromOneByte(
- Vector<const uint8_t>(reinterpret_cast<uint8_t*>(buffer), url_chars),
- TENURED);
- script->set_source_url(*url_str.ToHandleChecked());
-
- int name_chars = SNPrintF(ArrayVector(buffer), "wasm-%08x", hash);
- DCHECK(name_chars >= 0 && name_chars < kBufferSize);
- MaybeHandle<String> name_str = isolate->factory()->NewStringFromOneByte(
- Vector<const uint8_t>(reinterpret_cast<uint8_t*>(buffer), name_chars),
- TENURED);
- script->set_name(*name_str.ToHandleChecked());
-
- script->set_wasm_compiled_module(*compiled_module);
- compiled_module->set_script(script);
- isolate->debug()->OnAfterCompile(script);
- }
-
return WasmModuleObject::New(isolate, compiled_module);
}
@@ -2000,24 +2295,25 @@ bool wasm::ValidateModuleBytes(Isolate* isolate, const byte* start,
return result.ok();
}
-MaybeHandle<JSArrayBuffer> wasm::GetInstanceMemory(Isolate* isolate,
- Handle<JSObject> object) {
+MaybeHandle<JSArrayBuffer> wasm::GetInstanceMemory(
+ Isolate* isolate, Handle<WasmInstanceObject> object) {
auto instance = Handle<WasmInstanceObject>::cast(object);
if (instance->has_memory_buffer()) {
- return Handle<JSArrayBuffer>(instance->get_memory_buffer(), isolate);
+ return Handle<JSArrayBuffer>(instance->memory_buffer(), isolate);
}
return MaybeHandle<JSArrayBuffer>();
}
-void SetInstanceMemory(Handle<JSObject> object, JSArrayBuffer* buffer) {
+void SetInstanceMemory(Handle<WasmInstanceObject> instance,
+ JSArrayBuffer* buffer) {
DisallowHeapAllocation no_gc;
- auto instance = Handle<WasmInstanceObject>::cast(object);
instance->set_memory_buffer(buffer);
- instance->get_compiled_module()->set_ptr_to_memory(buffer);
+ instance->compiled_module()->set_ptr_to_memory(buffer);
}
int32_t wasm::GetInstanceMemorySize(Isolate* isolate,
- Handle<JSObject> instance) {
+ Handle<WasmInstanceObject> instance) {
+ DCHECK(IsWasmInstance(*instance));
MaybeHandle<JSArrayBuffer> maybe_mem_buffer =
GetInstanceMemory(isolate, instance);
Handle<JSArrayBuffer> buffer;
@@ -2028,92 +2324,222 @@ int32_t wasm::GetInstanceMemorySize(Isolate* isolate,
}
}
-uint32_t GetMaxInstanceMemorySize(Isolate* isolate,
- Handle<WasmInstanceObject> instance) {
+uint32_t GetMaxInstanceMemoryPages(Isolate* isolate,
+ Handle<WasmInstanceObject> instance) {
if (instance->has_memory_object()) {
- Handle<WasmMemoryObject> memory_object(instance->get_memory_object(),
- isolate);
-
- int maximum = memory_object->maximum_pages();
- if (maximum > 0) return static_cast<uint32_t>(maximum);
+ Handle<WasmMemoryObject> memory_object(instance->memory_object(), isolate);
+ if (memory_object->has_maximum_pages()) {
+ uint32_t maximum = static_cast<uint32_t>(memory_object->maximum_pages());
+ if (maximum < kV8MaxWasmMemoryPages) return maximum;
+ }
}
- uint32_t compiled_max_pages =
- instance->get_compiled_module()->max_mem_pages();
+ uint32_t compiled_max_pages = instance->compiled_module()->max_mem_pages();
isolate->counters()->wasm_max_mem_pages_count()->AddSample(
compiled_max_pages);
if (compiled_max_pages != 0) return compiled_max_pages;
- return WasmModule::kV8MaxPages;
+ return kV8MaxWasmMemoryPages;
}
-int32_t wasm::GrowInstanceMemory(Isolate* isolate, Handle<JSObject> object,
- uint32_t pages) {
- if (!IsWasmInstance(*object)) return -1;
- auto instance = Handle<WasmInstanceObject>::cast(object);
- if (pages == 0) return GetInstanceMemorySize(isolate, instance);
- uint32_t max_pages = GetMaxInstanceMemorySize(isolate, instance);
-
- Address old_mem_start = nullptr;
- uint32_t old_size = 0, new_size = 0;
-
- MaybeHandle<JSArrayBuffer> maybe_mem_buffer =
- GetInstanceMemory(isolate, instance);
+Handle<JSArrayBuffer> GrowMemoryBuffer(Isolate* isolate,
+ MaybeHandle<JSArrayBuffer> buffer,
+ uint32_t pages, uint32_t max_pages) {
Handle<JSArrayBuffer> old_buffer;
- if (!maybe_mem_buffer.ToHandle(&old_buffer) ||
- old_buffer->backing_store() == nullptr) {
- // If module object does not have linear memory associated with it,
- // Allocate new array buffer of given size.
- new_size = pages * WasmModule::kPageSize;
- if (max_pages < pages) return -1;
- } else {
+ Address old_mem_start = nullptr;
+ uint32_t old_size = 0;
+ if (buffer.ToHandle(&old_buffer) && old_buffer->backing_store() != nullptr) {
old_mem_start = static_cast<Address>(old_buffer->backing_store());
- old_size = old_buffer->byte_length()->Number();
- // If the old memory was zero-sized, we should have been in the
- // "undefined" case above.
DCHECK_NOT_NULL(old_mem_start);
- DCHECK(old_size + pages * WasmModule::kPageSize <=
- std::numeric_limits<uint32_t>::max());
- new_size = old_size + pages * WasmModule::kPageSize;
+ old_size = old_buffer->byte_length()->Number();
}
-
+ DCHECK(old_size + pages * WasmModule::kPageSize <=
+ std::numeric_limits<uint32_t>::max());
+ uint32_t new_size = old_size + pages * WasmModule::kPageSize;
if (new_size <= old_size || max_pages * WasmModule::kPageSize < new_size ||
- WasmModule::kV8MaxPages * WasmModule::kPageSize < new_size) {
- return -1;
- }
- Handle<JSArrayBuffer> buffer = NewArrayBuffer(isolate, new_size);
- if (buffer.is_null()) return -1;
- Address new_mem_start = static_cast<Address>(buffer->backing_store());
- if (old_size != 0) {
- memcpy(new_mem_start, old_mem_start, old_size);
- }
- SetInstanceMemory(instance, *buffer);
- Handle<FixedArray> code_table = instance->get_compiled_module()->code_table();
- RelocateMemoryReferencesInCode(code_table, old_mem_start, new_mem_start,
- old_size, new_size);
- if (instance->has_memory_object()) {
- instance->get_memory_object()->set_buffer(*buffer);
+ kV8MaxWasmMemoryPages * WasmModule::kPageSize < new_size) {
+ return Handle<JSArrayBuffer>::null();
}
+ Handle<JSArrayBuffer> new_buffer;
+ if (!old_buffer.is_null() && old_buffer->has_guard_region()) {
+ // We don't move the backing store, we simply change the protection to make
+ // more of it accessible.
+ base::OS::Unprotect(old_buffer->backing_store(), new_size);
+ reinterpret_cast<v8::Isolate*>(isolate)
+ ->AdjustAmountOfExternalAllocatedMemory(pages * WasmModule::kPageSize);
+ Handle<Object> new_size_object =
+ isolate->factory()->NewNumberFromSize(new_size);
+ old_buffer->set_byte_length(*new_size_object);
+ new_buffer = old_buffer;
+ } else {
+ const bool enable_guard_regions = false;
+ new_buffer = NewArrayBuffer(isolate, new_size, enable_guard_regions);
+ if (new_buffer.is_null()) return new_buffer;
+ Address new_mem_start = static_cast<Address>(new_buffer->backing_store());
+ if (old_size != 0) {
+ memcpy(new_mem_start, old_mem_start, old_size);
+ }
+ }
+ return new_buffer;
+}
+
+void UncheckedUpdateInstanceMemory(Isolate* isolate,
+ Handle<WasmInstanceObject> instance,
+ Address old_mem_start, uint32_t old_size) {
+ DCHECK(instance->has_memory_buffer());
+ Handle<JSArrayBuffer> new_buffer(instance->memory_buffer());
+ uint32_t new_size = new_buffer->byte_length()->Number();
+ DCHECK(new_size <= std::numeric_limits<uint32_t>::max());
+ Address new_mem_start = static_cast<Address>(new_buffer->backing_store());
+ DCHECK_NOT_NULL(new_mem_start);
+ Handle<FixedArray> code_table = instance->compiled_module()->code_table();
+ RelocateMemoryReferencesInCode(
+ code_table, instance->compiled_module()->module()->num_imported_functions,
+ old_mem_start, new_mem_start, old_size, new_size);
+}
+
+int32_t wasm::GrowWebAssemblyMemory(Isolate* isolate,
+ Handle<WasmMemoryObject> receiver,
+ uint32_t pages) {
+ DCHECK(WasmJs::IsWasmMemoryObject(isolate, receiver));
+ Handle<WasmMemoryObject> memory_object =
+ handle(WasmMemoryObject::cast(*receiver));
+ MaybeHandle<JSArrayBuffer> memory_buffer = handle(memory_object->buffer());
+ Handle<JSArrayBuffer> old_buffer;
+ uint32_t old_size = 0;
+ Address old_mem_start = nullptr;
+ if (memory_buffer.ToHandle(&old_buffer) &&
+ old_buffer->backing_store() != nullptr) {
+ old_size = old_buffer->byte_length()->Number();
+ old_mem_start = static_cast<Address>(old_buffer->backing_store());
+ }
+ // Return current size if grow by 0
+ if (pages == 0) {
+ DCHECK(old_size % WasmModule::kPageSize == 0);
+ return (old_size / WasmModule::kPageSize);
+ }
+ Handle<JSArrayBuffer> new_buffer;
+ if (!memory_object->has_instances_link()) {
+ // Memory object does not have an instance associated with it, just grow
+ uint32_t max_pages;
+ if (memory_object->has_maximum_pages()) {
+ max_pages = static_cast<uint32_t>(memory_object->maximum_pages());
+ if (kV8MaxWasmMemoryPages < max_pages) return -1;
+ } else {
+ max_pages = kV8MaxWasmMemoryPages;
+ }
+ new_buffer = GrowMemoryBuffer(isolate, memory_buffer, pages, max_pages);
+ if (new_buffer.is_null()) return -1;
+ } else {
+ Handle<WasmInstanceWrapper> instance_wrapper(
+ memory_object->instances_link());
+ DCHECK(WasmInstanceWrapper::IsWasmInstanceWrapper(*instance_wrapper));
+ DCHECK(instance_wrapper->has_instance());
+ Handle<WasmInstanceObject> instance = instance_wrapper->instance_object();
+ DCHECK(IsWasmInstance(*instance));
+ uint32_t max_pages = GetMaxInstanceMemoryPages(isolate, instance);
+
+ // Grow memory object buffer and update instances associated with it.
+ new_buffer = GrowMemoryBuffer(isolate, memory_buffer, pages, max_pages);
+ if (new_buffer.is_null()) return -1;
+ DCHECK(!instance_wrapper->has_previous());
+ SetInstanceMemory(instance, *new_buffer);
+ UncheckedUpdateInstanceMemory(isolate, instance, old_mem_start, old_size);
+ while (instance_wrapper->has_next()) {
+ instance_wrapper = instance_wrapper->next_wrapper();
+ DCHECK(WasmInstanceWrapper::IsWasmInstanceWrapper(*instance_wrapper));
+ Handle<WasmInstanceObject> instance = instance_wrapper->instance_object();
+ DCHECK(IsWasmInstance(*instance));
+ SetInstanceMemory(instance, *new_buffer);
+ UncheckedUpdateInstanceMemory(isolate, instance, old_mem_start, old_size);
+ }
+ }
+ memory_object->set_buffer(*new_buffer);
DCHECK(old_size % WasmModule::kPageSize == 0);
return (old_size / WasmModule::kPageSize);
}
+int32_t wasm::GrowMemory(Isolate* isolate, Handle<WasmInstanceObject> instance,
+ uint32_t pages) {
+ if (!IsWasmInstance(*instance)) return -1;
+ if (pages == 0) return GetInstanceMemorySize(isolate, instance);
+ Handle<WasmInstanceObject> instance_obj(WasmInstanceObject::cast(*instance));
+ if (!instance_obj->has_memory_object()) {
+ // No other instances to grow, grow just the one.
+ MaybeHandle<JSArrayBuffer> instance_buffer =
+ GetInstanceMemory(isolate, instance);
+ Handle<JSArrayBuffer> old_buffer;
+ uint32_t old_size = 0;
+ Address old_mem_start = nullptr;
+ if (instance_buffer.ToHandle(&old_buffer) &&
+ old_buffer->backing_store() != nullptr) {
+ old_size = old_buffer->byte_length()->Number();
+ old_mem_start = static_cast<Address>(old_buffer->backing_store());
+ }
+ uint32_t max_pages = GetMaxInstanceMemoryPages(isolate, instance_obj);
+ Handle<JSArrayBuffer> buffer =
+ GrowMemoryBuffer(isolate, instance_buffer, pages, max_pages);
+ if (buffer.is_null()) return -1;
+ SetInstanceMemory(instance, *buffer);
+ UncheckedUpdateInstanceMemory(isolate, instance, old_mem_start, old_size);
+ DCHECK(old_size % WasmModule::kPageSize == 0);
+ return (old_size / WasmModule::kPageSize);
+ } else {
+ return GrowWebAssemblyMemory(isolate, handle(instance_obj->memory_object()),
+ pages);
+ }
+}
+
+void wasm::GrowDispatchTables(Isolate* isolate,
+ Handle<FixedArray> dispatch_tables,
+ uint32_t old_size, uint32_t count) {
+ DCHECK_EQ(0, dispatch_tables->length() % 4);
+ for (int i = 0; i < dispatch_tables->length(); i += 4) {
+ Handle<FixedArray> old_function_table(
+ FixedArray::cast(dispatch_tables->get(i + 2)));
+ Handle<FixedArray> old_signature_table(
+ FixedArray::cast(dispatch_tables->get(i + 3)));
+ Handle<FixedArray> new_function_table =
+ isolate->factory()->CopyFixedArrayAndGrow(old_function_table, count);
+ Handle<FixedArray> new_signature_table =
+ isolate->factory()->CopyFixedArrayAndGrow(old_signature_table, count);
+
+ // Get code table for the instance
+ Handle<WasmInstanceObject> instance(
+ WasmInstanceObject::cast(dispatch_tables->get(i)));
+ Handle<FixedArray> code_table(instance->compiled_module()->code_table());
+
+ // Relocate size references
+ RelocateTableSizeReferences(code_table, old_size, old_size + count);
+
+ // Replace references of old tables with new tables.
+ for (int j = 0; j < code_table->length(); ++j) {
+ if (!code_table->get(j)->IsCode()) continue;
+ Handle<Code> code = Handle<Code>(Code::cast(code_table->get(j)));
+ ReplaceReferenceInCode(code, old_function_table, new_function_table);
+ ReplaceReferenceInCode(code, old_signature_table, new_signature_table);
+ }
+
+ // Update dispatch tables with new function/signature tables
+ dispatch_tables->set(i + 2, *new_function_table);
+ dispatch_tables->set(i + 3, *new_signature_table);
+ }
+}
+
void testing::ValidateInstancesChain(Isolate* isolate,
- Handle<JSObject> wasm_module,
+ Handle<WasmModuleObject> module_obj,
int instance_count) {
CHECK_GE(instance_count, 0);
DisallowHeapAllocation no_gc;
- WasmCompiledModule* compiled_module =
- WasmCompiledModule::cast(wasm_module->GetInternalField(0));
+ WasmCompiledModule* compiled_module = module_obj->compiled_module();
CHECK_EQ(JSObject::cast(compiled_module->ptr_to_weak_wasm_module()->value()),
- *wasm_module);
+ *module_obj);
Object* prev = nullptr;
int found_instances = compiled_module->has_weak_owning_instance() ? 1 : 0;
WasmCompiledModule* current_instance = compiled_module;
while (current_instance->has_weak_next_instance()) {
CHECK((prev == nullptr && !current_instance->has_weak_prev_instance()) ||
current_instance->ptr_to_weak_prev_instance()->value() == prev);
- CHECK_EQ(current_instance->ptr_to_weak_wasm_module()->value(),
- *wasm_module);
+ CHECK_EQ(current_instance->ptr_to_weak_wasm_module()->value(), *module_obj);
CHECK(IsWasmInstance(
current_instance->ptr_to_weak_owning_instance()->value()));
prev = current_instance;
@@ -2126,63 +2552,222 @@ void testing::ValidateInstancesChain(Isolate* isolate,
}
void testing::ValidateModuleState(Isolate* isolate,
- Handle<JSObject> wasm_module) {
+ Handle<WasmModuleObject> module_obj) {
DisallowHeapAllocation no_gc;
- WasmCompiledModule* compiled_module =
- WasmCompiledModule::cast(wasm_module->GetInternalField(0));
+ WasmCompiledModule* compiled_module = module_obj->compiled_module();
CHECK(compiled_module->has_weak_wasm_module());
- CHECK_EQ(compiled_module->ptr_to_weak_wasm_module()->value(), *wasm_module);
+ CHECK_EQ(compiled_module->ptr_to_weak_wasm_module()->value(), *module_obj);
CHECK(!compiled_module->has_weak_prev_instance());
CHECK(!compiled_module->has_weak_next_instance());
CHECK(!compiled_module->has_weak_owning_instance());
}
void testing::ValidateOrphanedInstance(Isolate* isolate,
- Handle<JSObject> object) {
+ Handle<WasmInstanceObject> instance) {
DisallowHeapAllocation no_gc;
- WasmInstanceObject* instance = WasmInstanceObject::cast(*object);
- WasmCompiledModule* compiled_module = instance->get_compiled_module();
+ WasmCompiledModule* compiled_module = instance->compiled_module();
CHECK(compiled_module->has_weak_wasm_module());
CHECK(compiled_module->ptr_to_weak_wasm_module()->cleared());
}
-void WasmCompiledModule::RecreateModuleWrapper(Isolate* isolate,
- Handle<FixedArray> array) {
- Handle<WasmCompiledModule> compiled_module(
- reinterpret_cast<WasmCompiledModule*>(*array), isolate);
+Handle<JSArray> wasm::GetImports(Isolate* isolate,
+ Handle<WasmModuleObject> module_object) {
+ Handle<WasmCompiledModule> compiled_module(module_object->compiled_module(),
+ isolate);
+ Factory* factory = isolate->factory();
- WasmModule* module = nullptr;
+ Handle<String> module_string = factory->InternalizeUtf8String("module");
+ Handle<String> name_string = factory->InternalizeUtf8String("name");
+ Handle<String> kind_string = factory->InternalizeUtf8String("kind");
+
+ Handle<String> function_string = factory->InternalizeUtf8String("function");
+ Handle<String> table_string = factory->InternalizeUtf8String("table");
+ Handle<String> memory_string = factory->InternalizeUtf8String("memory");
+ Handle<String> global_string = factory->InternalizeUtf8String("global");
+
+ // Create the result array.
+ WasmModule* module = compiled_module->module();
+ int num_imports = static_cast<int>(module->import_table.size());
+ Handle<JSArray> array_object = factory->NewJSArray(FAST_ELEMENTS, 0, 0);
+ Handle<FixedArray> storage = factory->NewFixedArray(num_imports);
+ JSArray::SetContent(array_object, storage);
+ array_object->set_length(Smi::FromInt(num_imports));
+
+ Handle<JSFunction> object_function =
+ Handle<JSFunction>(isolate->native_context()->object_function(), isolate);
+
+ // Populate the result array.
+ for (int index = 0; index < num_imports; ++index) {
+ WasmImport& import = module->import_table[index];
+
+ Handle<JSObject> entry = factory->NewJSObject(object_function);
+
+ Handle<String> import_kind;
+ switch (import.kind) {
+ case kExternalFunction:
+ import_kind = function_string;
+ break;
+ case kExternalTable:
+ import_kind = table_string;
+ break;
+ case kExternalMemory:
+ import_kind = memory_string;
+ break;
+ case kExternalGlobal:
+ import_kind = global_string;
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ MaybeHandle<String> import_module =
+ WasmCompiledModule::ExtractUtf8StringFromModuleBytes(
+ isolate, compiled_module, import.module_name_offset,
+ import.module_name_length);
+
+ MaybeHandle<String> import_name =
+ WasmCompiledModule::ExtractUtf8StringFromModuleBytes(
+ isolate, compiled_module, import.field_name_offset,
+ import.field_name_length);
+
+ JSObject::AddProperty(entry, module_string, import_module.ToHandleChecked(),
+ NONE);
+ JSObject::AddProperty(entry, name_string, import_name.ToHandleChecked(),
+ NONE);
+ JSObject::AddProperty(entry, kind_string, import_kind, NONE);
+
+ storage->set(index, *entry);
+ }
+
+ return array_object;
+}
+
+Handle<JSArray> wasm::GetExports(Isolate* isolate,
+ Handle<WasmModuleObject> module_object) {
+ Handle<WasmCompiledModule> compiled_module(module_object->compiled_module(),
+ isolate);
+ Factory* factory = isolate->factory();
+
+ Handle<String> name_string = factory->InternalizeUtf8String("name");
+ Handle<String> kind_string = factory->InternalizeUtf8String("kind");
+
+ Handle<String> function_string = factory->InternalizeUtf8String("function");
+ Handle<String> table_string = factory->InternalizeUtf8String("table");
+ Handle<String> memory_string = factory->InternalizeUtf8String("memory");
+ Handle<String> global_string = factory->InternalizeUtf8String("global");
+
+ // Create the result array.
+ WasmModule* module = compiled_module->module();
+ int num_exports = static_cast<int>(module->export_table.size());
+ Handle<JSArray> array_object = factory->NewJSArray(FAST_ELEMENTS, 0, 0);
+ Handle<FixedArray> storage = factory->NewFixedArray(num_exports);
+ JSArray::SetContent(array_object, storage);
+ array_object->set_length(Smi::FromInt(num_exports));
+
+ Handle<JSFunction> object_function =
+ Handle<JSFunction>(isolate->native_context()->object_function(), isolate);
+
+ // Populate the result array.
+ for (int index = 0; index < num_exports; ++index) {
+ WasmExport& exp = module->export_table[index];
+
+ Handle<String> export_kind;
+ switch (exp.kind) {
+ case kExternalFunction:
+ export_kind = function_string;
+ break;
+ case kExternalTable:
+ export_kind = table_string;
+ break;
+ case kExternalMemory:
+ export_kind = memory_string;
+ break;
+ case kExternalGlobal:
+ export_kind = global_string;
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ Handle<JSObject> entry = factory->NewJSObject(object_function);
+
+ MaybeHandle<String> export_name =
+ WasmCompiledModule::ExtractUtf8StringFromModuleBytes(
+ isolate, compiled_module, exp.name_offset, exp.name_length);
+
+ JSObject::AddProperty(entry, name_string, export_name.ToHandleChecked(),
+ NONE);
+ JSObject::AddProperty(entry, kind_string, export_kind, NONE);
+
+ storage->set(index, *entry);
+ }
+
+ return array_object;
+}
+
+Handle<JSArray> wasm::GetCustomSections(Isolate* isolate,
+ Handle<WasmModuleObject> module_object,
+ Handle<String> name,
+ ErrorThrower* thrower) {
+ Handle<WasmCompiledModule> compiled_module(module_object->compiled_module(),
+ isolate);
+ Factory* factory = isolate->factory();
+
+ std::vector<CustomSectionOffset> custom_sections;
{
- Handle<SeqOneByteString> module_bytes = compiled_module->module_bytes();
- // We parse the module again directly from the module bytes, so
- // the underlying storage must not be moved meanwhile.
- DisallowHeapAllocation no_allocation;
+ DisallowHeapAllocation no_gc; // for raw access to string bytes.
+ Handle<SeqOneByteString> module_bytes(compiled_module->module_bytes(),
+ isolate);
const byte* start =
reinterpret_cast<const byte*>(module_bytes->GetCharsAddress());
const byte* end = start + module_bytes->length();
- // TODO(titzer): remember the module origin in the compiled_module
- // For now, we assume serialized modules did not originate from asm.js.
- ModuleResult result =
- DecodeWasmModule(isolate, start, end, false, kWasmOrigin);
- CHECK(result.ok());
- CHECK_NOT_NULL(result.val);
- module = const_cast<WasmModule*>(result.val);
+ custom_sections = DecodeCustomSections(start, end);
+ }
+
+ std::vector<Handle<Object>> matching_sections;
+
+ // Gather matching sections.
+ for (auto section : custom_sections) {
+ MaybeHandle<String> section_name =
+ WasmCompiledModule::ExtractUtf8StringFromModuleBytes(
+ isolate, compiled_module, section.name_offset, section.name_length);
+
+ if (!name->Equals(*section_name.ToHandleChecked())) continue;
+
+ // Make a copy of the payload data in the section.
+ bool is_external; // Set by TryAllocateBackingStore
+ void* memory = TryAllocateBackingStore(isolate, section.payload_length,
+ false, is_external);
+
+ Handle<Object> section_data = factory->undefined_value();
+ if (memory) {
+ Handle<JSArrayBuffer> buffer = isolate->factory()->NewJSArrayBuffer();
+ JSArrayBuffer::Setup(buffer, isolate, is_external, memory,
+ static_cast<int>(section.payload_length));
+ DisallowHeapAllocation no_gc; // for raw access to string bytes.
+ Handle<SeqOneByteString> module_bytes(compiled_module->module_bytes(),
+ isolate);
+ const byte* start =
+ reinterpret_cast<const byte*>(module_bytes->GetCharsAddress());
+ memcpy(memory, start + section.payload_offset, section.payload_length);
+ section_data = buffer;
+ } else {
+ thrower->RangeError("out of memory allocating custom section data");
+ return Handle<JSArray>();
+ }
+
+ matching_sections.push_back(section_data);
}
- Handle<WasmModuleWrapper> module_wrapper =
- WasmModuleWrapper::New(isolate, module);
+ int num_custom_sections = static_cast<int>(matching_sections.size());
+ Handle<JSArray> array_object = factory->NewJSArray(FAST_ELEMENTS, 0, 0);
+ Handle<FixedArray> storage = factory->NewFixedArray(num_custom_sections);
+ JSArray::SetContent(array_object, storage);
+ array_object->set_length(Smi::FromInt(num_custom_sections));
- compiled_module->set_module_wrapper(module_wrapper);
- DCHECK(WasmCompiledModule::IsWasmCompiledModule(*compiled_module));
-}
+ for (int i = 0; i < num_custom_sections; i++) {
+ storage->set(i, *matching_sections[i]);
+ }
-MaybeHandle<String> WasmCompiledModule::GetFunctionName(
- Handle<WasmCompiledModule> compiled_module, uint32_t func_index) {
- DCHECK_LT(func_index, compiled_module->module()->functions.size());
- WasmFunction& function = compiled_module->module()->functions[func_index];
- Isolate* isolate = compiled_module->GetIsolate();
- MaybeHandle<String> string = ExtractStringFromModuleBytes(
- isolate, compiled_module, function.name_offset, function.name_length);
- if (!string.is_null()) return string.ToHandleChecked();
- return {};
+ return array_object;
}
diff --git a/deps/v8/src/wasm/wasm-module.h b/deps/v8/src/wasm/wasm-module.h
index 2ad46e21b6..2f368a7391 100644
--- a/deps/v8/src/wasm/wasm-module.h
+++ b/deps/v8/src/wasm/wasm-module.h
@@ -8,6 +8,7 @@
#include <memory>
#include "src/api.h"
+#include "src/debug/debug-interface.h"
#include "src/globals.h"
#include "src/handles.h"
#include "src/parsing/preparse-data.h"
@@ -22,6 +23,8 @@ namespace internal {
class WasmCompiledModule;
class WasmDebugInfo;
class WasmModuleObject;
+class WasmInstanceObject;
+class WasmMemoryObject;
namespace compiler {
class CallDescriptor;
@@ -31,11 +34,8 @@ class WasmCompilationUnit;
namespace wasm {
class ErrorThrower;
-const size_t kMaxModuleSize = 1024 * 1024 * 1024;
-const size_t kMaxFunctionSize = 128 * 1024;
-const size_t kMaxStringSize = 256;
const uint32_t kWasmMagic = 0x6d736100;
-const uint32_t kWasmVersion = 0x0d;
+const uint32_t kWasmVersion = 0x01;
const uint8_t kWasmFunctionTypeForm = 0x60;
const uint8_t kWasmAnyFunctionTypeForm = 0x70;
@@ -63,7 +63,6 @@ inline bool IsValidSectionCode(uint8_t byte) {
const char* SectionName(WasmSectionCode code);
// Constants for fixed-size elements within a module.
-static const uint32_t kMaxReturnCount = 1;
static const uint8_t kResizableMaximumFlag = 1;
static const int32_t kInvalidFunctionIndex = -1;
@@ -118,7 +117,7 @@ struct WasmFunction {
// Static representation of a wasm global variable.
struct WasmGlobal {
- LocalType type; // type of the global.
+ ValueType type; // type of the global.
bool mutability; // {true} if mutable.
WasmInitExpr init; // the initialization expression of the global.
uint32_t offset; // offset into global memory.
@@ -170,21 +169,18 @@ struct WasmExport {
uint32_t index; // index into the respective space.
};
-enum ModuleOrigin { kWasmOrigin, kAsmJsOrigin };
+enum ModuleOrigin : uint8_t { kWasmOrigin, kAsmJsOrigin };
+struct ModuleWireBytes;
// Static representation of a module.
struct V8_EXPORT_PRIVATE WasmModule {
static const uint32_t kPageSize = 0x10000; // Page size, 64kb.
static const uint32_t kMinMemPages = 1; // Minimum memory size = 64kb
- static const size_t kV8MaxPages = 16384; // Maximum memory size = 1gb
- static const size_t kSpecMaxPages = 65536; // Maximum according to the spec
- static const size_t kV8MaxTableSize = 16 * 1024 * 1024;
Zone* owned_zone;
- const byte* module_start = nullptr; // starting address for the module bytes
- const byte* module_end = nullptr; // end address for the module bytes
uint32_t min_mem_pages = 0; // minimum size of the memory in 64k pages
uint32_t max_mem_pages = 0; // maximum size of the memory in 64k pages
+ bool has_max_mem = false; // try if a maximum memory size exists
bool has_memory = false; // true if the memory was defined or imported
bool mem_export = false; // true if the memory is exported
// TODO(wasm): reconcile start function index being an int with
@@ -214,56 +210,23 @@ struct V8_EXPORT_PRIVATE WasmModule {
// switch to libc-2.21 or higher.
std::unique_ptr<base::Semaphore> pending_tasks;
- WasmModule() : WasmModule(nullptr, nullptr) {}
- WasmModule(Zone* owned_zone, const byte* module_start);
+ WasmModule() : WasmModule(nullptr) {}
+ WasmModule(Zone* owned_zone);
~WasmModule() {
if (owned_zone) delete owned_zone;
}
- // Get a string stored in the module bytes representing a name.
- WasmName GetName(uint32_t offset, uint32_t length) const {
- if (length == 0) return {"<?>", 3}; // no name.
- CHECK(BoundsCheck(offset, offset + length));
- DCHECK_GE(static_cast<int>(length), 0);
- return {reinterpret_cast<const char*>(module_start + offset),
- static_cast<int>(length)};
- }
-
- // Get a string stored in the module bytes representing a function name.
- WasmName GetName(WasmFunction* function) const {
- return GetName(function->name_offset, function->name_length);
- }
-
- // Get a string stored in the module bytes representing a name.
- WasmName GetNameOrNull(uint32_t offset, uint32_t length) const {
- if (offset == 0 && length == 0) return {NULL, 0}; // no name.
- CHECK(BoundsCheck(offset, offset + length));
- DCHECK_GE(static_cast<int>(length), 0);
- return {reinterpret_cast<const char*>(module_start + offset),
- static_cast<int>(length)};
- }
-
- // Get a string stored in the module bytes representing a function name.
- WasmName GetNameOrNull(const WasmFunction* function) const {
- return GetNameOrNull(function->name_offset, function->name_length);
- }
-
- // Checks the given offset range is contained within the module bytes.
- bool BoundsCheck(uint32_t start, uint32_t end) const {
- size_t size = module_end - module_start;
- return start <= size && end <= size;
- }
-
// Creates a new instantiation of the module in the given isolate.
- static MaybeHandle<JSObject> Instantiate(Isolate* isolate,
- ErrorThrower* thrower,
- Handle<JSObject> wasm_module,
- Handle<JSReceiver> ffi,
- Handle<JSArrayBuffer> memory);
+ static MaybeHandle<WasmInstanceObject> Instantiate(
+ Isolate* isolate, ErrorThrower* thrower,
+ Handle<WasmModuleObject> wasm_module, Handle<JSReceiver> ffi,
+ Handle<JSArrayBuffer> memory = Handle<JSArrayBuffer>::null());
MaybeHandle<WasmCompiledModule> CompileFunctions(
Isolate* isolate, Handle<Managed<WasmModule>> module_wrapper,
- ErrorThrower* thrower) const;
+ ErrorThrower* thrower, const ModuleWireBytes& wire_bytes,
+ Handle<Script> asm_js_script,
+ Vector<const byte> asm_js_offset_table_bytes) const;
};
typedef Managed<WasmModule> WasmModuleWrapper;
@@ -272,11 +235,10 @@ typedef Managed<WasmModule> WasmModuleWrapper;
struct WasmInstance {
const WasmModule* module; // static representation of the module.
// -- Heap allocated --------------------------------------------------------
- Handle<JSObject> js_object; // JavaScript module object.
Handle<Context> context; // JavaScript native context.
- Handle<JSArrayBuffer> mem_buffer; // Handle to array buffer of memory.
- Handle<JSArrayBuffer> globals_buffer; // Handle to array buffer of globals.
std::vector<Handle<FixedArray>> function_tables; // indirect function tables.
+ std::vector<Handle<FixedArray>>
+ signature_tables; // indirect signature tables.
std::vector<Handle<Code>> function_code; // code objects for each function.
// -- raw memory ------------------------------------------------------------
byte* mem_start = nullptr; // start of linear memory.
@@ -287,15 +249,67 @@ struct WasmInstance {
explicit WasmInstance(const WasmModule* m)
: module(m),
function_tables(m->function_tables.size()),
+ signature_tables(m->function_tables.size()),
function_code(m->functions.size()) {}
};
+// Interface to the storage (wire bytes) of a wasm module.
+// It is illegal for anyone receiving a ModuleWireBytes to store pointers based
+// on module_bytes, as this storage is only guaranteed to be alive as long as
+// this struct is alive.
+struct V8_EXPORT_PRIVATE ModuleWireBytes {
+ ModuleWireBytes(Vector<const byte> module_bytes)
+ : module_bytes(module_bytes) {}
+ ModuleWireBytes(const byte* start, const byte* end)
+ : module_bytes(start, static_cast<int>(end - start)) {
+ DCHECK_GE(kMaxInt, end - start);
+ }
+
+ const Vector<const byte> module_bytes;
+
+ // Get a string stored in the module bytes representing a name.
+ WasmName GetName(uint32_t offset, uint32_t length) const {
+ if (length == 0) return {"<?>", 3}; // no name.
+ CHECK(BoundsCheck(offset, length));
+ DCHECK_GE(length, 0);
+ return Vector<const char>::cast(
+ module_bytes.SubVector(offset, offset + length));
+ }
+
+ // Get a string stored in the module bytes representing a function name.
+ WasmName GetName(const WasmFunction* function) const {
+ return GetName(function->name_offset, function->name_length);
+ }
+
+ // Get a string stored in the module bytes representing a name.
+ WasmName GetNameOrNull(uint32_t offset, uint32_t length) const {
+ if (offset == 0 && length == 0) return {NULL, 0}; // no name.
+ CHECK(BoundsCheck(offset, length));
+ DCHECK_GE(length, 0);
+ return Vector<const char>::cast(
+ module_bytes.SubVector(offset, offset + length));
+ }
+
+ // Get a string stored in the module bytes representing a function name.
+ WasmName GetNameOrNull(const WasmFunction* function) const {
+ return GetNameOrNull(function->name_offset, function->name_length);
+ }
+
+ // Checks the given offset range is contained within the module bytes.
+ bool BoundsCheck(uint32_t offset, uint32_t length) const {
+ uint32_t size = static_cast<uint32_t>(module_bytes.length());
+ return offset <= size && length <= size - offset;
+ }
+};
+
// Interface provided to the decoder/graph builder which contains only
// minimal information about the globals, functions, and function tables.
struct V8_EXPORT_PRIVATE ModuleEnv {
+ ModuleEnv(const WasmModule* module, WasmInstance* instance)
+ : module(module), instance(instance) {}
+
const WasmModule* module;
WasmInstance* instance;
- ModuleOrigin origin;
bool IsValidGlobal(uint32_t index) const {
return module && index < module->globals.size();
@@ -309,7 +323,7 @@ struct V8_EXPORT_PRIVATE ModuleEnv {
bool IsValidTable(uint32_t index) const {
return module && index < module->function_tables.size();
}
- LocalType GetGlobalType(uint32_t index) {
+ ValueType GetGlobalType(uint32_t index) {
DCHECK(IsValidGlobal(index));
return module->globals[index].type;
}
@@ -326,7 +340,7 @@ struct V8_EXPORT_PRIVATE ModuleEnv {
return &module->function_tables[index];
}
- bool asm_js() { return origin == kAsmJsOrigin; }
+ bool asm_js() { return module->origin == kAsmJsOrigin; }
Handle<Code> GetFunctionCode(uint32_t index) {
DCHECK_NOT_NULL(instance);
@@ -341,42 +355,33 @@ struct V8_EXPORT_PRIVATE ModuleEnv {
Zone* zone, compiler::CallDescriptor* descriptor);
};
+// A ModuleEnv together with ModuleWireBytes.
+struct ModuleBytesEnv : public ModuleEnv, public ModuleWireBytes {
+ ModuleBytesEnv(const WasmModule* module, WasmInstance* instance,
+ Vector<const byte> module_bytes)
+ : ModuleEnv(module, instance), ModuleWireBytes(module_bytes) {}
+ ModuleBytesEnv(const WasmModule* module, WasmInstance* instance,
+ const ModuleWireBytes& wire_bytes)
+ : ModuleEnv(module, instance), ModuleWireBytes(wire_bytes) {}
+};
+
// A helper for printing out the names of functions.
struct WasmFunctionName {
+ WasmFunctionName(const WasmFunction* function, ModuleBytesEnv* module_env)
+ : function_(function), name_(module_env->GetNameOrNull(function)) {}
+
const WasmFunction* function_;
- const WasmModule* module_;
- WasmFunctionName(const WasmFunction* function, const ModuleEnv* menv)
- : function_(function), module_(menv ? menv->module : nullptr) {}
+ WasmName name_;
};
std::ostream& operator<<(std::ostream& os, const WasmModule& module);
std::ostream& operator<<(std::ostream& os, const WasmFunction& function);
std::ostream& operator<<(std::ostream& os, const WasmFunctionName& name);
-// Extract a function name from the given wasm instance.
-// Returns "<WASM UNNAMED>" if no instance is passed, the function is unnamed or
-// the name is not a valid UTF-8 string.
-// TODO(5620): Refactor once we always get a wasm instance.
-Handle<String> GetWasmFunctionName(Isolate* isolate, Handle<Object> instance,
- uint32_t func_index);
-
-// Return the binary source bytes of a wasm module.
-Handle<SeqOneByteString> GetWasmBytes(Handle<JSObject> wasm);
-
// Get the debug info associated with the given wasm object.
// If no debug info exists yet, it is created automatically.
Handle<WasmDebugInfo> GetDebugInfo(Handle<JSObject> wasm);
-// Return the number of functions in the given wasm object.
-int GetNumberOfFunctions(Handle<JSObject> wasm);
-
-// Create and export JSFunction
-Handle<JSFunction> WrapExportCodeAsJSFunction(Isolate* isolate,
- Handle<Code> export_code,
- Handle<String> name,
- FunctionSig* sig, int func_index,
- Handle<JSObject> instance);
-
// Check whether the given object represents a WebAssembly.Instance instance.
// This checks the number and type of internal fields, so it's not 100 percent
// secure. If it turns out that we need more complete checks, we could add a
@@ -384,26 +389,26 @@ Handle<JSFunction> WrapExportCodeAsJSFunction(Isolate* isolate,
// else.
bool IsWasmInstance(Object* instance);
-// Return the compiled module object for this WASM instance.
-WasmCompiledModule* GetCompiledModule(Object* wasm_instance);
-
-// Check whether the wasm module was generated from asm.js code.
-bool WasmIsAsmJs(Object* instance, Isolate* isolate);
-
// Get the script of the wasm module. If the origin of the module is asm.js, the
// returned Script will be a JavaScript Script of Script::TYPE_NORMAL, otherwise
// it's of type TYPE_WASM.
Handle<Script> GetScript(Handle<JSObject> instance);
-// Get the asm.js source position for the given byte offset in the given
-// function.
-int GetAsmWasmSourcePosition(Handle<JSObject> instance, int func_index,
- int byte_offset);
-
V8_EXPORT_PRIVATE MaybeHandle<WasmModuleObject> CreateModuleObjectFromBytes(
Isolate* isolate, const byte* start, const byte* end, ErrorThrower* thrower,
ModuleOrigin origin, Handle<Script> asm_js_script,
- const byte* asm_offset_tables_start, const byte* asm_offset_tables_end);
+ Vector<const byte> asm_offset_table);
+
+V8_EXPORT_PRIVATE bool IsWasmCodegenAllowed(Isolate* isolate,
+ Handle<Context> context);
+
+V8_EXPORT_PRIVATE Handle<JSArray> GetImports(Isolate* isolate,
+ Handle<WasmModuleObject> module);
+V8_EXPORT_PRIVATE Handle<JSArray> GetExports(Isolate* isolate,
+ Handle<WasmModuleObject> module);
+V8_EXPORT_PRIVATE Handle<JSArray> GetCustomSections(
+ Isolate* isolate, Handle<WasmModuleObject> module, Handle<String> name,
+ ErrorThrower* thrower);
V8_EXPORT_PRIVATE bool ValidateModuleBytes(Isolate* isolate, const byte* start,
const byte* end,
@@ -414,33 +419,44 @@ V8_EXPORT_PRIVATE bool ValidateModuleBytes(Isolate* isolate, const byte* start,
int GetFunctionCodeOffset(Handle<WasmCompiledModule> compiled_module,
int func_index);
-// Translate from byte offset in the module to function number and byte offset
-// within that function, encoded as line and column in the position info.
-bool GetPositionInfo(Handle<WasmCompiledModule> compiled_module,
- uint32_t position, Script::PositionInfo* info);
-
// Assumed to be called with a code object associated to a wasm module instance.
// Intended to be called from runtime functions.
// Returns nullptr on failing to get owning instance.
-Object* GetOwningWasmInstance(Code* code);
+WasmInstanceObject* GetOwningWasmInstance(Code* code);
-MaybeHandle<JSArrayBuffer> GetInstanceMemory(Isolate* isolate,
- Handle<JSObject> instance);
+MaybeHandle<JSArrayBuffer> GetInstanceMemory(
+ Isolate* isolate, Handle<WasmInstanceObject> instance);
-int32_t GetInstanceMemorySize(Isolate* isolate, Handle<JSObject> instance);
+int32_t GetInstanceMemorySize(Isolate* isolate,
+ Handle<WasmInstanceObject> instance);
-int32_t GrowInstanceMemory(Isolate* isolate, Handle<JSObject> instance,
- uint32_t pages);
+int32_t GrowInstanceMemory(Isolate* isolate,
+ Handle<WasmInstanceObject> instance, uint32_t pages);
+
+Handle<JSArrayBuffer> NewArrayBuffer(Isolate* isolate, size_t size,
+ bool enable_guard_regions);
+
+int32_t GrowWebAssemblyMemory(Isolate* isolate,
+ Handle<WasmMemoryObject> receiver,
+ uint32_t pages);
+
+int32_t GrowMemory(Isolate* isolate, Handle<WasmInstanceObject> instance,
+ uint32_t pages);
void UpdateDispatchTables(Isolate* isolate, Handle<FixedArray> dispatch_tables,
int index, Handle<JSFunction> js_function);
+void GrowDispatchTables(Isolate* isolate, Handle<FixedArray> dispatch_tables,
+ uint32_t old_size, uint32_t count);
+
namespace testing {
-void ValidateInstancesChain(Isolate* isolate, Handle<JSObject> wasm_module,
+void ValidateInstancesChain(Isolate* isolate,
+ Handle<WasmModuleObject> module_obj,
int instance_count);
-void ValidateModuleState(Isolate* isolate, Handle<JSObject> wasm_module);
-void ValidateOrphanedInstance(Isolate* isolate, Handle<JSObject> instance);
+void ValidateModuleState(Isolate* isolate, Handle<WasmModuleObject> module_obj);
+void ValidateOrphanedInstance(Isolate* isolate,
+ Handle<WasmInstanceObject> instance);
} // namespace testing
} // namespace wasm
diff --git a/deps/v8/src/wasm/wasm-objects.cc b/deps/v8/src/wasm/wasm-objects.cc
index 68f66d246d..3f694c579f 100644
--- a/deps/v8/src/wasm/wasm-objects.cc
+++ b/deps/v8/src/wasm/wasm-objects.cc
@@ -3,7 +3,12 @@
// found in the LICENSE file.
#include "src/wasm/wasm-objects.h"
+#include "src/utils.h"
+
+#include "src/debug/debug-interface.h"
+#include "src/wasm/module-decoder.h"
#include "src/wasm/wasm-module.h"
+#include "src/wasm/wasm-text.h"
#define TRACE(...) \
do { \
@@ -18,29 +23,38 @@
using namespace v8::internal;
using namespace v8::internal::wasm;
-#define DEFINE_ACCESSORS(Container, name, field, type) \
- type* Container::get_##name() { \
- return type::cast(GetInternalField(field)); \
- } \
- void Container::set_##name(type* value) { \
- return SetInternalField(field, value); \
- }
+#define DEFINE_GETTER0(getter, Container, name, field, type) \
+ type* Container::name() { return type::cast(getter(field)); }
-#define DEFINE_OPTIONAL_ACCESSORS(Container, name, field, type) \
- bool Container::has_##name() { \
- return !GetInternalField(field)->IsUndefined(GetIsolate()); \
- } \
- type* Container::get_##name() { \
- return type::cast(GetInternalField(field)); \
- } \
- void Container::set_##name(type* value) { \
- return SetInternalField(field, value); \
- }
+#define DEFINE_ACCESSORS0(getter, setter, Container, name, field, type) \
+ DEFINE_GETTER0(getter, Container, name, field, type) \
+ void Container::set_##name(type* value) { return setter(field, value); }
-#define DEFINE_GETTER(Container, name, field, type) \
- type* Container::get_##name() { return type::cast(GetInternalField(field)); }
+#define DEFINE_OPTIONAL_ACCESSORS0(getter, setter, Container, name, field, \
+ type) \
+ DEFINE_ACCESSORS0(getter, setter, Container, name, field, type) \
+ bool Container::has_##name() { \
+ return !getter(field)->IsUndefined(GetIsolate()); \
+ }
-static uint32_t SafeUint32(Object* value) {
+#define DEFINE_OBJ_GETTER(Container, name, field, type) \
+ DEFINE_GETTER0(GetInternalField, Container, name, field, type)
+#define DEFINE_OBJ_ACCESSORS(Container, name, field, type) \
+ DEFINE_ACCESSORS0(GetInternalField, SetInternalField, Container, name, \
+ field, type)
+#define DEFINE_OPTIONAL_OBJ_ACCESSORS(Container, name, field, type) \
+ DEFINE_OPTIONAL_ACCESSORS0(GetInternalField, SetInternalField, Container, \
+ name, field, type)
+#define DEFINE_ARR_GETTER(Container, name, field, type) \
+ DEFINE_GETTER0(get, Container, name, field, type)
+#define DEFINE_ARR_ACCESSORS(Container, name, field, type) \
+ DEFINE_ACCESSORS0(get, set, Container, name, field, type)
+#define DEFINE_OPTIONAL_ARR_ACCESSORS(Container, name, field, type) \
+ DEFINE_OPTIONAL_ACCESSORS0(get, set, Container, name, field, type)
+
+namespace {
+
+uint32_t SafeUint32(Object* value) {
if (value->IsSmi()) {
int32_t val = Smi::cast(value)->value();
CHECK_GE(val, 0);
@@ -49,21 +63,23 @@ static uint32_t SafeUint32(Object* value) {
DCHECK(value->IsHeapNumber());
HeapNumber* num = HeapNumber::cast(value);
CHECK_GE(num->value(), 0.0);
- CHECK_LE(num->value(), static_cast<double>(kMaxUInt32));
+ CHECK_LE(num->value(), kMaxUInt32);
return static_cast<uint32_t>(num->value());
}
-static int32_t SafeInt32(Object* value) {
+int32_t SafeInt32(Object* value) {
if (value->IsSmi()) {
return Smi::cast(value)->value();
}
DCHECK(value->IsHeapNumber());
HeapNumber* num = HeapNumber::cast(value);
- CHECK_GE(num->value(), static_cast<double>(Smi::kMinValue));
- CHECK_LE(num->value(), static_cast<double>(Smi::kMaxValue));
+ CHECK_GE(num->value(), Smi::kMinValue);
+ CHECK_LE(num->value(), Smi::kMaxValue);
return static_cast<int32_t>(num->value());
}
+} // namespace
+
Handle<WasmModuleObject> WasmModuleObject::New(
Isolate* isolate, Handle<WasmCompiledModule> compiled_module) {
ModuleOrigin origin = compiled_module->module()->origin;
@@ -97,8 +113,16 @@ WasmModuleObject* WasmModuleObject::cast(Object* object) {
return reinterpret_cast<WasmModuleObject*>(object);
}
+bool WasmModuleObject::IsWasmModuleObject(Object* object) {
+ return object->IsJSObject() &&
+ JSObject::cast(object)->GetInternalFieldCount() == kFieldCount;
+}
+
+DEFINE_OBJ_GETTER(WasmModuleObject, compiled_module, kCompiledModule,
+ WasmCompiledModule)
+
Handle<WasmTableObject> WasmTableObject::New(Isolate* isolate, uint32_t initial,
- uint32_t maximum,
+ int64_t maximum,
Handle<FixedArray>* js_functions) {
Handle<JSFunction> table_ctor(
isolate->native_context()->wasm_table_constructor());
@@ -109,8 +133,8 @@ Handle<WasmTableObject> WasmTableObject::New(Isolate* isolate, uint32_t initial,
(*js_functions)->set(i, null);
}
table_obj->SetInternalField(kFunctions, *(*js_functions));
- table_obj->SetInternalField(kMaximum,
- static_cast<Object*>(Smi::FromInt(maximum)));
+ Handle<Object> max = isolate->factory()->NewNumber(maximum);
+ table_obj->SetInternalField(kMaximum, *max);
Handle<FixedArray> dispatch_tables = isolate->factory()->NewFixedArray(0);
table_obj->SetInternalField(kDispatchTables, *dispatch_tables);
@@ -119,27 +143,28 @@ Handle<WasmTableObject> WasmTableObject::New(Isolate* isolate, uint32_t initial,
return Handle<WasmTableObject>::cast(table_obj);
}
-DEFINE_GETTER(WasmTableObject, dispatch_tables, kDispatchTables, FixedArray)
+DEFINE_OBJ_GETTER(WasmTableObject, dispatch_tables, kDispatchTables, FixedArray)
Handle<FixedArray> WasmTableObject::AddDispatchTable(
Isolate* isolate, Handle<WasmTableObject> table_obj,
Handle<WasmInstanceObject> instance, int table_index,
- Handle<FixedArray> dispatch_table) {
+ Handle<FixedArray> function_table, Handle<FixedArray> signature_table) {
Handle<FixedArray> dispatch_tables(
FixedArray::cast(table_obj->GetInternalField(kDispatchTables)), isolate);
- DCHECK_EQ(0, dispatch_tables->length() % 3);
+ DCHECK_EQ(0, dispatch_tables->length() % 4);
if (instance.is_null()) return dispatch_tables;
// TODO(titzer): use weak cells here to avoid leaking instances.
// Grow the dispatch table and add a new triple at the end.
Handle<FixedArray> new_dispatch_tables =
- isolate->factory()->CopyFixedArrayAndGrow(dispatch_tables, 3);
+ isolate->factory()->CopyFixedArrayAndGrow(dispatch_tables, 4);
new_dispatch_tables->set(dispatch_tables->length() + 0, *instance);
new_dispatch_tables->set(dispatch_tables->length() + 1,
Smi::FromInt(table_index));
- new_dispatch_tables->set(dispatch_tables->length() + 2, *dispatch_table);
+ new_dispatch_tables->set(dispatch_tables->length() + 2, *function_table);
+ new_dispatch_tables->set(dispatch_tables->length() + 3, *signature_table);
table_obj->SetInternalField(WasmTableObject::kDispatchTables,
*new_dispatch_tables);
@@ -147,12 +172,16 @@ Handle<FixedArray> WasmTableObject::AddDispatchTable(
return new_dispatch_tables;
}
-DEFINE_ACCESSORS(WasmTableObject, functions, kFunctions, FixedArray)
+DEFINE_OBJ_ACCESSORS(WasmTableObject, functions, kFunctions, FixedArray)
+
+uint32_t WasmTableObject::current_length() { return functions()->length(); }
-uint32_t WasmTableObject::current_length() { return get_functions()->length(); }
+bool WasmTableObject::has_maximum_length() {
+ return GetInternalField(kMaximum)->Number() >= 0;
+}
-uint32_t WasmTableObject::maximum_length() {
- return SafeUint32(GetInternalField(kMaximum));
+int64_t WasmTableObject::maximum_length() {
+ return static_cast<int64_t>(GetInternalField(kMaximum)->Number());
}
WasmTableObject* WasmTableObject::cast(Object* object) {
@@ -161,28 +190,42 @@ WasmTableObject* WasmTableObject::cast(Object* object) {
return reinterpret_cast<WasmTableObject*>(object);
}
+void WasmTableObject::Grow(Isolate* isolate, Handle<WasmTableObject> table,
+ uint32_t count) {
+ Handle<FixedArray> dispatch_tables(table->dispatch_tables());
+ wasm::GrowDispatchTables(isolate, dispatch_tables,
+ table->functions()->length(), count);
+}
+
Handle<WasmMemoryObject> WasmMemoryObject::New(Isolate* isolate,
Handle<JSArrayBuffer> buffer,
- int maximum) {
+ int32_t maximum) {
Handle<JSFunction> memory_ctor(
isolate->native_context()->wasm_memory_constructor());
- Handle<JSObject> memory_obj = isolate->factory()->NewJSObject(memory_ctor);
+ Handle<JSObject> memory_obj =
+ isolate->factory()->NewJSObject(memory_ctor, TENURED);
memory_obj->SetInternalField(kArrayBuffer, *buffer);
- memory_obj->SetInternalField(kMaximum,
- static_cast<Object*>(Smi::FromInt(maximum)));
+ Handle<Object> max = isolate->factory()->NewNumber(maximum);
+ memory_obj->SetInternalField(kMaximum, *max);
Handle<Symbol> memory_sym(isolate->native_context()->wasm_memory_sym());
Object::SetProperty(memory_obj, memory_sym, memory_obj, STRICT).Check();
return Handle<WasmMemoryObject>::cast(memory_obj);
}
-DEFINE_ACCESSORS(WasmMemoryObject, buffer, kArrayBuffer, JSArrayBuffer)
+DEFINE_OBJ_ACCESSORS(WasmMemoryObject, buffer, kArrayBuffer, JSArrayBuffer)
+DEFINE_OPTIONAL_OBJ_ACCESSORS(WasmMemoryObject, instances_link, kInstancesLink,
+ WasmInstanceWrapper)
uint32_t WasmMemoryObject::current_pages() {
- return SafeUint32(get_buffer()->byte_length()) / wasm::WasmModule::kPageSize;
+ return SafeUint32(buffer()->byte_length()) / wasm::WasmModule::kPageSize;
+}
+
+bool WasmMemoryObject::has_maximum_pages() {
+ return GetInternalField(kMaximum)->Number() >= 0;
}
int32_t WasmMemoryObject::maximum_pages() {
- return SafeInt32(GetInternalField(kMaximum));
+ return static_cast<int32_t>(GetInternalField(kMaximum)->Number());
}
WasmMemoryObject* WasmMemoryObject::cast(Object* object) {
@@ -191,31 +234,50 @@ WasmMemoryObject* WasmMemoryObject::cast(Object* object) {
return reinterpret_cast<WasmMemoryObject*>(object);
}
-void WasmMemoryObject::AddInstance(WasmInstanceObject* instance) {
- // TODO(gdeepti): This should be a weak list of instance objects
- // for instances that share memory.
- SetInternalField(kInstance, instance);
+void WasmMemoryObject::AddInstance(Isolate* isolate,
+ Handle<WasmInstanceObject> instance) {
+ Handle<WasmInstanceWrapper> instance_wrapper =
+ handle(instance->instance_wrapper());
+ if (has_instances_link()) {
+ Handle<WasmInstanceWrapper> current_wrapper(instances_link());
+ DCHECK(WasmInstanceWrapper::IsWasmInstanceWrapper(*current_wrapper));
+ DCHECK(!current_wrapper->has_previous());
+ instance_wrapper->set_next_wrapper(*current_wrapper);
+ current_wrapper->set_previous_wrapper(*instance_wrapper);
+ }
+ set_instances_link(*instance_wrapper);
+}
+
+void WasmMemoryObject::ResetInstancesLink(Isolate* isolate) {
+ Handle<Object> undefined = isolate->factory()->undefined_value();
+ SetInternalField(kInstancesLink, *undefined);
}
-DEFINE_ACCESSORS(WasmInstanceObject, compiled_module, kCompiledModule,
- WasmCompiledModule)
-DEFINE_OPTIONAL_ACCESSORS(WasmInstanceObject, globals_buffer,
- kGlobalsArrayBuffer, JSArrayBuffer)
-DEFINE_OPTIONAL_ACCESSORS(WasmInstanceObject, memory_buffer, kMemoryArrayBuffer,
- JSArrayBuffer)
-DEFINE_OPTIONAL_ACCESSORS(WasmInstanceObject, memory_object, kMemoryObject,
- WasmMemoryObject)
-DEFINE_OPTIONAL_ACCESSORS(WasmInstanceObject, debug_info, kDebugInfo,
- WasmDebugInfo)
+DEFINE_OBJ_ACCESSORS(WasmInstanceObject, compiled_module, kCompiledModule,
+ WasmCompiledModule)
+DEFINE_OPTIONAL_OBJ_ACCESSORS(WasmInstanceObject, globals_buffer,
+ kGlobalsArrayBuffer, JSArrayBuffer)
+DEFINE_OPTIONAL_OBJ_ACCESSORS(WasmInstanceObject, memory_buffer,
+ kMemoryArrayBuffer, JSArrayBuffer)
+DEFINE_OPTIONAL_OBJ_ACCESSORS(WasmInstanceObject, memory_object, kMemoryObject,
+ WasmMemoryObject)
+DEFINE_OPTIONAL_OBJ_ACCESSORS(WasmInstanceObject, debug_info, kDebugInfo,
+ WasmDebugInfo)
+DEFINE_OPTIONAL_OBJ_ACCESSORS(WasmInstanceObject, instance_wrapper,
+ kWasmMemInstanceWrapper, WasmInstanceWrapper)
WasmModuleObject* WasmInstanceObject::module_object() {
- return WasmModuleObject::cast(*get_compiled_module()->wasm_module());
+ return *compiled_module()->wasm_module();
}
-WasmModule* WasmInstanceObject::module() {
- return reinterpret_cast<WasmModuleWrapper*>(
- *get_compiled_module()->module_wrapper())
- ->get();
+WasmModule* WasmInstanceObject::module() { return compiled_module()->module(); }
+
+Handle<WasmDebugInfo> WasmInstanceObject::GetOrCreateDebugInfo(
+ Handle<WasmInstanceObject> instance) {
+ if (instance->has_debug_info()) return handle(instance->debug_info());
+ Handle<WasmDebugInfo> new_info = WasmDebugInfo::New(instance);
+ instance->set_debug_info(*new_info);
+ return new_info;
}
WasmInstanceObject* WasmInstanceObject::cast(Object* object) {
@@ -224,7 +286,6 @@ WasmInstanceObject* WasmInstanceObject::cast(Object* object) {
}
bool WasmInstanceObject::IsWasmInstanceObject(Object* object) {
- if (!object->IsObject()) return false;
if (!object->IsJSObject()) return false;
JSObject* obj = JSObject::cast(object);
@@ -246,15 +307,21 @@ bool WasmInstanceObject::IsWasmInstanceObject(Object* object) {
Handle<WasmInstanceObject> WasmInstanceObject::New(
Isolate* isolate, Handle<WasmCompiledModule> compiled_module) {
- Handle<Map> map = isolate->factory()->NewMap(
- JS_OBJECT_TYPE, JSObject::kHeaderSize + kFieldCount * kPointerSize);
+ Handle<JSFunction> instance_cons(
+ isolate->native_context()->wasm_instance_constructor());
+ Handle<JSObject> instance_object =
+ isolate->factory()->NewJSObject(instance_cons, TENURED);
+ Handle<Symbol> instance_sym(isolate->native_context()->wasm_instance_sym());
+ Object::SetProperty(instance_object, instance_sym, instance_object, STRICT)
+ .Check();
Handle<WasmInstanceObject> instance(
- reinterpret_cast<WasmInstanceObject*>(
- *isolate->factory()->NewJSObjectFromMap(map, TENURED)),
- isolate);
+ reinterpret_cast<WasmInstanceObject*>(*instance_object), isolate);
instance->SetInternalField(kCompiledModule, *compiled_module);
instance->SetInternalField(kMemoryObject, isolate->heap()->undefined_value());
+ Handle<WasmInstanceWrapper> instance_wrapper =
+ WasmInstanceWrapper::New(isolate, instance);
+ instance->SetInternalField(kWasmMemInstanceWrapper, *instance_wrapper);
return instance;
}
@@ -275,8 +342,20 @@ WasmExportedFunction* WasmExportedFunction::cast(Object* object) {
}
Handle<WasmExportedFunction> WasmExportedFunction::New(
- Isolate* isolate, Handle<WasmInstanceObject> instance, Handle<String> name,
- Handle<Code> export_wrapper, int arity, int func_index) {
+ Isolate* isolate, Handle<WasmInstanceObject> instance,
+ MaybeHandle<String> maybe_name, int func_index, int arity,
+ Handle<Code> export_wrapper) {
+ Handle<String> name;
+ if (maybe_name.is_null()) {
+ EmbeddedVector<char, 16> buffer;
+ int length = SNPrintF(buffer, "%d", func_index);
+ name = isolate->factory()
+ ->NewStringFromAscii(
+ Vector<const char>::cast(buffer.SubVector(0, length)))
+ .ToHandleChecked();
+ } else {
+ name = maybe_name.ToHandleChecked();
+ }
DCHECK_EQ(Code::JS_TO_WASM_FUNCTION, export_wrapper->kind());
Handle<SharedFunctionInfo> shared =
isolate->factory()->NewSharedFunctionInfo(name, export_wrapper, false);
@@ -291,22 +370,109 @@ Handle<WasmExportedFunction> WasmExportedFunction::New(
return Handle<WasmExportedFunction>::cast(function);
}
+bool WasmSharedModuleData::IsWasmSharedModuleData(Object* object) {
+ if (!object->IsFixedArray()) return false;
+ FixedArray* arr = FixedArray::cast(object);
+ if (arr->length() != kFieldCount) return false;
+ Isolate* isolate = arr->GetIsolate();
+ if (!arr->get(kModuleWrapper)->IsForeign()) return false;
+ if (!arr->get(kModuleBytes)->IsUndefined(isolate) &&
+ !arr->get(kModuleBytes)->IsSeqOneByteString())
+ return false;
+ if (!arr->get(kScript)->IsScript()) return false;
+ if (!arr->get(kAsmJsOffsetTable)->IsUndefined(isolate) &&
+ !arr->get(kAsmJsOffsetTable)->IsByteArray())
+ return false;
+ return true;
+}
+
+WasmSharedModuleData* WasmSharedModuleData::cast(Object* object) {
+ DCHECK(IsWasmSharedModuleData(object));
+ return reinterpret_cast<WasmSharedModuleData*>(object);
+}
+
+wasm::WasmModule* WasmSharedModuleData::module() {
+ return reinterpret_cast<WasmModuleWrapper*>(get(kModuleWrapper))->get();
+}
+
+DEFINE_OPTIONAL_ARR_ACCESSORS(WasmSharedModuleData, module_bytes, kModuleBytes,
+ SeqOneByteString);
+DEFINE_ARR_GETTER(WasmSharedModuleData, script, kScript, Script);
+DEFINE_OPTIONAL_ARR_ACCESSORS(WasmSharedModuleData, asm_js_offset_table,
+ kAsmJsOffsetTable, ByteArray);
+
+Handle<WasmSharedModuleData> WasmSharedModuleData::New(
+ Isolate* isolate, Handle<Foreign> module_wrapper,
+ Handle<SeqOneByteString> module_bytes, Handle<Script> script,
+ Handle<ByteArray> asm_js_offset_table) {
+ Handle<FixedArray> arr =
+ isolate->factory()->NewFixedArray(kFieldCount, TENURED);
+
+ arr->set(kModuleWrapper, *module_wrapper);
+ if (!module_bytes.is_null()) {
+ arr->set(kModuleBytes, *module_bytes);
+ }
+ if (!script.is_null()) {
+ arr->set(kScript, *script);
+ }
+ if (!asm_js_offset_table.is_null()) {
+ arr->set(kAsmJsOffsetTable, *asm_js_offset_table);
+ }
+
+ DCHECK(WasmSharedModuleData::IsWasmSharedModuleData(*arr));
+ return Handle<WasmSharedModuleData>::cast(arr);
+}
+
+bool WasmSharedModuleData::is_asm_js() {
+ bool asm_js = module()->origin == wasm::ModuleOrigin::kAsmJsOrigin;
+ DCHECK_EQ(asm_js, script()->type() == Script::TYPE_NORMAL);
+ DCHECK_EQ(asm_js, has_asm_js_offset_table());
+ return asm_js;
+}
+
+void WasmSharedModuleData::RecreateModuleWrapper(
+ Isolate* isolate, Handle<WasmSharedModuleData> shared) {
+ DCHECK(shared->get(kModuleWrapper)->IsUndefined(isolate));
+
+ WasmModule* module = nullptr;
+ {
+ // We parse the module again directly from the module bytes, so
+ // the underlying storage must not be moved meanwhile.
+ DisallowHeapAllocation no_allocation;
+ SeqOneByteString* module_bytes = shared->module_bytes();
+ const byte* start =
+ reinterpret_cast<const byte*>(module_bytes->GetCharsAddress());
+ const byte* end = start + module_bytes->length();
+ // TODO(titzer): remember the module origin in the compiled_module
+ // For now, we assume serialized modules did not originate from asm.js.
+ ModuleResult result =
+ DecodeWasmModule(isolate, start, end, false, kWasmOrigin);
+ CHECK(result.ok());
+ CHECK_NOT_NULL(result.val);
+ module = const_cast<WasmModule*>(result.val);
+ }
+
+ Handle<WasmModuleWrapper> module_wrapper =
+ WasmModuleWrapper::New(isolate, module);
+
+ shared->set(kModuleWrapper, *module_wrapper);
+ DCHECK(WasmSharedModuleData::IsWasmSharedModuleData(*shared));
+}
+
Handle<WasmCompiledModule> WasmCompiledModule::New(
- Isolate* isolate, Handle<WasmModuleWrapper> module_wrapper) {
+ Isolate* isolate, Handle<WasmSharedModuleData> shared) {
Handle<FixedArray> ret =
isolate->factory()->NewFixedArray(PropertyIndices::Count, TENURED);
- // WasmCompiledModule::cast would fail since module bytes are not set yet.
+ // WasmCompiledModule::cast would fail since fields are not set yet.
Handle<WasmCompiledModule> compiled_module(
reinterpret_cast<WasmCompiledModule*>(*ret), isolate);
compiled_module->InitId();
- compiled_module->set_module_wrapper(module_wrapper);
+ compiled_module->set_num_imported_functions(0);
+ compiled_module->set_shared(shared);
+ compiled_module->set_native_context(isolate->native_context());
return compiled_module;
}
-wasm::WasmModule* WasmCompiledModule::module() const {
- return reinterpret_cast<WasmModuleWrapper*>(*module_wrapper())->get();
-}
-
void WasmCompiledModule::InitId() {
#if DEBUG
static uint32_t instance_id_counter = 0;
@@ -315,19 +481,39 @@ void WasmCompiledModule::InitId() {
#endif
}
+MaybeHandle<String> WasmCompiledModule::ExtractUtf8StringFromModuleBytes(
+ Isolate* isolate, Handle<WasmCompiledModule> compiled_module,
+ uint32_t offset, uint32_t size) {
+ // TODO(wasm): cache strings from modules if it's a performance win.
+ Handle<SeqOneByteString> module_bytes(compiled_module->module_bytes(),
+ isolate);
+ DCHECK_GE(module_bytes->length(), offset);
+ DCHECK_GE(module_bytes->length() - offset, size);
+ Address raw = module_bytes->GetCharsAddress() + offset;
+ if (!unibrow::Utf8::Validate(reinterpret_cast<const byte*>(raw), size))
+ return {}; // UTF8 decoding error for name.
+ DCHECK_GE(kMaxInt, offset);
+ DCHECK_GE(kMaxInt, size);
+ return isolate->factory()->NewStringFromUtf8SubString(
+ module_bytes, static_cast<int>(offset), static_cast<int>(size));
+}
+
bool WasmCompiledModule::IsWasmCompiledModule(Object* obj) {
if (!obj->IsFixedArray()) return false;
FixedArray* arr = FixedArray::cast(obj);
if (arr->length() != PropertyIndices::Count) return false;
Isolate* isolate = arr->GetIsolate();
-#define WCM_CHECK_SMALL_NUMBER(TYPE, NAME) \
- if (!arr->get(kID_##NAME)->IsSmi()) return false;
-#define WCM_CHECK_OBJECT_OR_WEAK(TYPE, NAME) \
- if (!arr->get(kID_##NAME)->IsUndefined(isolate) && \
- !arr->get(kID_##NAME)->Is##TYPE()) \
- return false;
-#define WCM_CHECK_OBJECT(TYPE, NAME) WCM_CHECK_OBJECT_OR_WEAK(TYPE, NAME)
-#define WCM_CHECK_WEAK_LINK(TYPE, NAME) WCM_CHECK_OBJECT_OR_WEAK(WeakCell, NAME)
+#define WCM_CHECK_TYPE(NAME, TYPE_CHECK) \
+ do { \
+ Object* obj = arr->get(kID_##NAME); \
+ if (!(TYPE_CHECK)) return false; \
+ } while (false);
+#define WCM_CHECK_OBJECT(TYPE, NAME) \
+ WCM_CHECK_TYPE(NAME, obj->IsUndefined(isolate) || obj->Is##TYPE())
+#define WCM_CHECK_WASM_OBJECT(TYPE, NAME) \
+ WCM_CHECK_TYPE(NAME, TYPE::Is##TYPE(obj))
+#define WCM_CHECK_WEAK_LINK(TYPE, NAME) WCM_CHECK_OBJECT(WeakCell, NAME)
+#define WCM_CHECK_SMALL_NUMBER(TYPE, NAME) WCM_CHECK_TYPE(NAME, obj->IsSmi())
#define WCM_CHECK(KIND, TYPE, NAME) WCM_CHECK_##KIND(TYPE, NAME)
WCM_PROPERTY_TABLE(WCM_CHECK)
#undef WCM_CHECK
@@ -341,7 +527,7 @@ void WasmCompiledModule::PrintInstancesChain() {
if (!FLAG_trace_wasm_instances) return;
for (WasmCompiledModule* current = this; current != nullptr;) {
PrintF("->%d", current->instance_id());
- if (current->ptr_to_weak_next_instance() == nullptr) break;
+ if (!current->has_weak_next_instance()) break;
CHECK(!current->ptr_to_weak_next_instance()->cleared());
current =
WasmCompiledModule::cast(current->ptr_to_weak_next_instance()->value());
@@ -350,6 +536,19 @@ void WasmCompiledModule::PrintInstancesChain() {
#endif
}
+void WasmCompiledModule::RecreateModuleWrapper(
+ Isolate* isolate, Handle<WasmCompiledModule> compiled_module) {
+ // This method must only be called immediately after deserialization.
+ // At this point, no module wrapper exists, so the shared module data is
+ // incomplete.
+ Handle<WasmSharedModuleData> shared(
+ static_cast<WasmSharedModuleData*>(compiled_module->get(kID_shared)),
+ isolate);
+ DCHECK(!WasmSharedModuleData::IsWasmSharedModuleData(*shared));
+ WasmSharedModuleData::RecreateModuleWrapper(isolate, shared);
+ DCHECK(WasmSharedModuleData::IsWasmSharedModuleData(*shared));
+}
+
uint32_t WasmCompiledModule::mem_size() const {
return has_memory() ? memory()->byte_length()->Number() : default_mem_size();
}
@@ -357,3 +556,310 @@ uint32_t WasmCompiledModule::mem_size() const {
uint32_t WasmCompiledModule::default_mem_size() const {
return min_mem_pages() * WasmModule::kPageSize;
}
+
+MaybeHandle<String> WasmCompiledModule::GetFunctionNameOrNull(
+ Isolate* isolate, Handle<WasmCompiledModule> compiled_module,
+ uint32_t func_index) {
+ DCHECK_LT(func_index, compiled_module->module()->functions.size());
+ WasmFunction& function = compiled_module->module()->functions[func_index];
+ return WasmCompiledModule::ExtractUtf8StringFromModuleBytes(
+ isolate, compiled_module, function.name_offset, function.name_length);
+}
+
+Handle<String> WasmCompiledModule::GetFunctionName(
+ Isolate* isolate, Handle<WasmCompiledModule> compiled_module,
+ uint32_t func_index) {
+ MaybeHandle<String> name =
+ GetFunctionNameOrNull(isolate, compiled_module, func_index);
+ if (!name.is_null()) return name.ToHandleChecked();
+ return isolate->factory()->NewStringFromStaticChars("<WASM UNNAMED>");
+}
+
+Vector<const uint8_t> WasmCompiledModule::GetRawFunctionName(
+ uint32_t func_index) {
+ DCHECK_GT(module()->functions.size(), func_index);
+ WasmFunction& function = module()->functions[func_index];
+ SeqOneByteString* bytes = module_bytes();
+ DCHECK_GE(bytes->length(), function.name_offset);
+ DCHECK_GE(bytes->length() - function.name_offset, function.name_length);
+ return Vector<const uint8_t>(bytes->GetCharsAddress() + function.name_offset,
+ function.name_length);
+}
+
+int WasmCompiledModule::GetFunctionOffset(uint32_t func_index) {
+ std::vector<WasmFunction>& functions = module()->functions;
+ if (static_cast<uint32_t>(func_index) >= functions.size()) return -1;
+ DCHECK_GE(kMaxInt, functions[func_index].code_start_offset);
+ return static_cast<int>(functions[func_index].code_start_offset);
+}
+
+int WasmCompiledModule::GetContainingFunction(uint32_t byte_offset) {
+ std::vector<WasmFunction>& functions = module()->functions;
+
+ // Binary search for a function containing the given position.
+ int left = 0; // inclusive
+ int right = static_cast<int>(functions.size()); // exclusive
+ if (right == 0) return false;
+ while (right - left > 1) {
+ int mid = left + (right - left) / 2;
+ if (functions[mid].code_start_offset <= byte_offset) {
+ left = mid;
+ } else {
+ right = mid;
+ }
+ }
+ // If the found function does not contains the given position, return -1.
+ WasmFunction& func = functions[left];
+ if (byte_offset < func.code_start_offset ||
+ byte_offset >= func.code_end_offset) {
+ return -1;
+ }
+
+ return left;
+}
+
+bool WasmCompiledModule::GetPositionInfo(uint32_t position,
+ Script::PositionInfo* info) {
+ int func_index = GetContainingFunction(position);
+ if (func_index < 0) return false;
+
+ WasmFunction& function = module()->functions[func_index];
+
+ info->line = func_index;
+ info->column = position - function.code_start_offset;
+ info->line_start = function.code_start_offset;
+ info->line_end = function.code_end_offset;
+ return true;
+}
+
+namespace {
+
+enum AsmJsOffsetTableEntryLayout {
+ kOTEByteOffset,
+ kOTECallPosition,
+ kOTENumberConvPosition,
+ kOTESize
+};
+
+Handle<ByteArray> GetDecodedAsmJsOffsetTable(
+ Handle<WasmCompiledModule> compiled_module, Isolate* isolate) {
+ DCHECK(compiled_module->is_asm_js());
+ Handle<ByteArray> offset_table(
+ compiled_module->shared()->asm_js_offset_table(), isolate);
+
+ // The last byte in the asm_js_offset_tables ByteArray tells whether it is
+ // still encoded (0) or decoded (1).
+ enum AsmJsTableType : int { Encoded = 0, Decoded = 1 };
+ int table_type = offset_table->get(offset_table->length() - 1);
+ DCHECK(table_type == Encoded || table_type == Decoded);
+ if (table_type == Decoded) return offset_table;
+
+ AsmJsOffsetsResult asm_offsets;
+ {
+ DisallowHeapAllocation no_gc;
+ const byte* bytes_start = offset_table->GetDataStartAddress();
+ const byte* bytes_end = bytes_start + offset_table->length() - 1;
+ asm_offsets = wasm::DecodeAsmJsOffsets(bytes_start, bytes_end);
+ }
+ // Wasm bytes must be valid and must contain asm.js offset table.
+ DCHECK(asm_offsets.ok());
+ DCHECK_GE(kMaxInt, asm_offsets.val.size());
+ int num_functions = static_cast<int>(asm_offsets.val.size());
+ int num_imported_functions =
+ static_cast<int>(compiled_module->module()->num_imported_functions);
+ DCHECK_EQ(compiled_module->module()->functions.size(),
+ static_cast<size_t>(num_functions) + num_imported_functions);
+ int num_entries = 0;
+ for (int func = 0; func < num_functions; ++func) {
+ size_t new_size = asm_offsets.val[func].size();
+ DCHECK_LE(new_size, static_cast<size_t>(kMaxInt) - num_entries);
+ num_entries += static_cast<int>(new_size);
+ }
+ // One byte to encode that this is a decoded table.
+ DCHECK_GE(kMaxInt,
+ 1 + static_cast<uint64_t>(num_entries) * kOTESize * kIntSize);
+ int total_size = 1 + num_entries * kOTESize * kIntSize;
+ Handle<ByteArray> decoded_table =
+ isolate->factory()->NewByteArray(total_size, TENURED);
+ decoded_table->set(total_size - 1, AsmJsTableType::Decoded);
+ compiled_module->shared()->set_asm_js_offset_table(*decoded_table);
+
+ int idx = 0;
+ std::vector<WasmFunction>& wasm_funs = compiled_module->module()->functions;
+ for (int func = 0; func < num_functions; ++func) {
+ std::vector<AsmJsOffsetEntry>& func_asm_offsets = asm_offsets.val[func];
+ if (func_asm_offsets.empty()) continue;
+ int func_offset =
+ wasm_funs[num_imported_functions + func].code_start_offset;
+ for (AsmJsOffsetEntry& e : func_asm_offsets) {
+ // Byte offsets must be strictly monotonously increasing:
+ DCHECK_IMPLIES(idx > 0, func_offset + e.byte_offset >
+ decoded_table->get_int(idx - kOTESize));
+ decoded_table->set_int(idx + kOTEByteOffset, func_offset + e.byte_offset);
+ decoded_table->set_int(idx + kOTECallPosition, e.source_position_call);
+ decoded_table->set_int(idx + kOTENumberConvPosition,
+ e.source_position_number_conversion);
+ idx += kOTESize;
+ }
+ }
+ DCHECK_EQ(total_size, idx * kIntSize + 1);
+ return decoded_table;
+}
+
+} // namespace
+
+int WasmCompiledModule::GetAsmJsSourcePosition(
+ Handle<WasmCompiledModule> compiled_module, uint32_t func_index,
+ uint32_t byte_offset, bool is_at_number_conversion) {
+ Isolate* isolate = compiled_module->GetIsolate();
+ Handle<ByteArray> offset_table =
+ GetDecodedAsmJsOffsetTable(compiled_module, isolate);
+
+ DCHECK_LT(func_index, compiled_module->module()->functions.size());
+ uint32_t func_code_offset =
+ compiled_module->module()->functions[func_index].code_start_offset;
+ uint32_t total_offset = func_code_offset + byte_offset;
+
+ // Binary search for the total byte offset.
+ int left = 0; // inclusive
+ int right = offset_table->length() / kIntSize / kOTESize; // exclusive
+ DCHECK_LT(left, right);
+ while (right - left > 1) {
+ int mid = left + (right - left) / 2;
+ int mid_entry = offset_table->get_int(kOTESize * mid);
+ DCHECK_GE(kMaxInt, mid_entry);
+ if (static_cast<uint32_t>(mid_entry) <= total_offset) {
+ left = mid;
+ } else {
+ right = mid;
+ }
+ }
+ // There should be an entry for each position that could show up on the stack
+ // trace:
+ DCHECK_EQ(total_offset, offset_table->get_int(kOTESize * left));
+ int idx = is_at_number_conversion ? kOTENumberConvPosition : kOTECallPosition;
+ return offset_table->get_int(kOTESize * left + idx);
+}
+
+v8::debug::WasmDisassembly WasmCompiledModule::DisassembleFunction(
+ int func_index) {
+ DisallowHeapAllocation no_gc;
+
+ if (func_index < 0 ||
+ static_cast<uint32_t>(func_index) >= module()->functions.size())
+ return {};
+
+ SeqOneByteString* module_bytes_str = module_bytes();
+ Vector<const byte> module_bytes(module_bytes_str->GetChars(),
+ module_bytes_str->length());
+
+ std::ostringstream disassembly_os;
+ v8::debug::WasmDisassembly::OffsetTable offset_table;
+
+ PrintWasmText(module(), module_bytes, static_cast<uint32_t>(func_index),
+ disassembly_os, &offset_table);
+
+ return {disassembly_os.str(), std::move(offset_table)};
+}
+
+bool WasmCompiledModule::GetPossibleBreakpoints(
+ const v8::debug::Location& start, const v8::debug::Location& end,
+ std::vector<v8::debug::Location>* locations) {
+ DisallowHeapAllocation no_gc;
+
+ std::vector<WasmFunction>& functions = module()->functions;
+ if (start.GetLineNumber() < 0 || start.GetColumnNumber() < 0 ||
+ (!end.IsEmpty() &&
+ (end.GetLineNumber() < 0 || end.GetColumnNumber() < 0)))
+ return false;
+
+ // start_func_index, start_offset and end_func_index is inclusive.
+ // end_offset is exclusive.
+ // start_offset and end_offset are module-relative byte offsets.
+ uint32_t start_func_index = start.GetLineNumber();
+ if (start_func_index >= functions.size()) return false;
+ int start_func_len = functions[start_func_index].code_end_offset -
+ functions[start_func_index].code_start_offset;
+ if (start.GetColumnNumber() > start_func_len) return false;
+ uint32_t start_offset =
+ functions[start_func_index].code_start_offset + start.GetColumnNumber();
+ uint32_t end_func_index;
+ uint32_t end_offset;
+ if (end.IsEmpty()) {
+ // Default: everything till the end of the Script.
+ end_func_index = static_cast<uint32_t>(functions.size() - 1);
+ end_offset = functions[end_func_index].code_end_offset;
+ } else {
+ // If end is specified: Use it and check for valid input.
+ end_func_index = static_cast<uint32_t>(end.GetLineNumber());
+
+ // Special case: Stop before the start of the next function. Change to: Stop
+ // at the end of the function before, such that we don't disassemble the
+ // next function also.
+ if (end.GetColumnNumber() == 0 && end_func_index > 0) {
+ --end_func_index;
+ end_offset = functions[end_func_index].code_end_offset;
+ } else {
+ if (end_func_index >= functions.size()) return false;
+ end_offset =
+ functions[end_func_index].code_start_offset + end.GetColumnNumber();
+ if (end_offset > functions[end_func_index].code_end_offset) return false;
+ }
+ }
+
+ AccountingAllocator alloc;
+ Zone tmp(&alloc, ZONE_NAME);
+ const byte* module_start = module_bytes()->GetChars();
+
+ for (uint32_t func_idx = start_func_index; func_idx <= end_func_index;
+ ++func_idx) {
+ WasmFunction& func = functions[func_idx];
+ if (func.code_start_offset == func.code_end_offset) continue;
+
+ BodyLocalDecls locals(&tmp);
+ BytecodeIterator iterator(module_start + func.code_start_offset,
+ module_start + func.code_end_offset, &locals);
+ DCHECK_LT(0u, locals.encoded_size);
+ for (uint32_t offset : iterator.offsets()) {
+ uint32_t total_offset = func.code_start_offset + offset;
+ if (total_offset >= end_offset) {
+ DCHECK_EQ(end_func_index, func_idx);
+ break;
+ }
+ if (total_offset < start_offset) continue;
+ locations->push_back(v8::debug::Location(func_idx, offset));
+ }
+ }
+ return true;
+}
+
+Handle<WasmInstanceWrapper> WasmInstanceWrapper::New(
+ Isolate* isolate, Handle<WasmInstanceObject> instance) {
+ Handle<FixedArray> array =
+ isolate->factory()->NewFixedArray(kWrapperPropertyCount, TENURED);
+ Handle<WasmInstanceWrapper> instance_wrapper(
+ reinterpret_cast<WasmInstanceWrapper*>(*array), isolate);
+ instance_wrapper->set_instance_object(instance, isolate);
+ return instance_wrapper;
+}
+
+bool WasmInstanceWrapper::IsWasmInstanceWrapper(Object* obj) {
+ if (!obj->IsFixedArray()) return false;
+ Handle<FixedArray> array = handle(FixedArray::cast(obj));
+ if (array->length() != kWrapperPropertyCount) return false;
+ if (!array->get(kWrapperInstanceObject)->IsWeakCell()) return false;
+ Isolate* isolate = array->GetIsolate();
+ if (!array->get(kNextInstanceWrapper)->IsUndefined(isolate) &&
+ !array->get(kNextInstanceWrapper)->IsFixedArray())
+ return false;
+ if (!array->get(kPreviousInstanceWrapper)->IsUndefined(isolate) &&
+ !array->get(kPreviousInstanceWrapper)->IsFixedArray())
+ return false;
+ return true;
+}
+
+void WasmInstanceWrapper::set_instance_object(Handle<JSObject> instance,
+ Isolate* isolate) {
+ Handle<WeakCell> cell = isolate->factory()->NewWeakCell(instance);
+ set(kWrapperInstanceObject, *cell);
+}
diff --git a/deps/v8/src/wasm/wasm-objects.h b/deps/v8/src/wasm/wasm-objects.h
index f74661f652..c478fe0419 100644
--- a/deps/v8/src/wasm/wasm-objects.h
+++ b/deps/v8/src/wasm/wasm-objects.h
@@ -5,8 +5,11 @@
#ifndef V8_WASM_OBJECTS_H_
#define V8_WASM_OBJECTS_H_
+#include "src/debug/interface-types.h"
#include "src/objects-inl.h"
+#include "src/trap-handler/trap-handler.h"
#include "src/wasm/managed.h"
+#include "src/wasm/wasm-limits.h"
namespace v8 {
namespace internal {
@@ -17,19 +20,21 @@ struct WasmModule;
class WasmCompiledModule;
class WasmDebugInfo;
class WasmInstanceObject;
+class WasmInstanceWrapper;
#define DECLARE_CASTS(name) \
static bool Is##name(Object* object); \
static name* cast(Object* object)
+#define DECLARE_GETTER(name, type) type* name()
+
#define DECLARE_ACCESSORS(name, type) \
- type* get_##name(); \
- void set_##name(type* value)
+ void set_##name(type* value); \
+ DECLARE_GETTER(name, type)
#define DECLARE_OPTIONAL_ACCESSORS(name, type) \
bool has_##name(); \
- type* get_##name(); \
- void set_##name(type* value)
+ DECLARE_ACCESSORS(name, type)
// Representation of a WebAssembly.Module JavaScript-level object.
class WasmModuleObject : public JSObject {
@@ -40,13 +45,6 @@ class WasmModuleObject : public JSObject {
DECLARE_CASTS(WasmModuleObject);
WasmCompiledModule* compiled_module();
- wasm::WasmModule* module();
- int num_functions();
- bool is_asm_js();
- int GetAsmWasmSourcePosition(int func_index, int byte_offset);
- WasmDebugInfo* debug_info();
- void set_debug_info(WasmDebugInfo* debug_info);
- MaybeHandle<String> GetFunctionName(Isolate* isolate, int func_index);
static Handle<WasmModuleObject> New(
Isolate* isolate, Handle<WasmCompiledModule> compiled_module);
@@ -61,38 +59,44 @@ class WasmTableObject : public JSObject {
DECLARE_CASTS(WasmTableObject);
DECLARE_ACCESSORS(functions, FixedArray);
- FixedArray* get_dispatch_tables();
+ FixedArray* dispatch_tables();
uint32_t current_length();
- uint32_t maximum_length();
+ bool has_maximum_length();
+ int64_t maximum_length(); // Returns < 0 if no maximum.
static Handle<WasmTableObject> New(Isolate* isolate, uint32_t initial,
- uint32_t maximum,
+ int64_t maximum,
Handle<FixedArray>* js_functions);
- static bool Grow(Handle<WasmTableObject> table, uint32_t count);
+ static void Grow(Isolate* isolate, Handle<WasmTableObject> table,
+ uint32_t count);
static Handle<FixedArray> AddDispatchTable(
Isolate* isolate, Handle<WasmTableObject> table,
Handle<WasmInstanceObject> instance, int table_index,
- Handle<FixedArray> dispatch_table);
+ Handle<FixedArray> function_table, Handle<FixedArray> signature_table);
};
// Representation of a WebAssembly.Memory JavaScript-level object.
class WasmMemoryObject : public JSObject {
public:
// TODO(titzer): add the brand as an internal field instead of a property.
- enum Fields : uint8_t { kArrayBuffer, kMaximum, kInstance, kFieldCount };
+ enum Fields : uint8_t { kArrayBuffer, kMaximum, kInstancesLink, kFieldCount };
DECLARE_CASTS(WasmMemoryObject);
DECLARE_ACCESSORS(buffer, JSArrayBuffer);
+ DECLARE_OPTIONAL_ACCESSORS(instances_link, WasmInstanceWrapper);
- void AddInstance(WasmInstanceObject* object);
+ void AddInstance(Isolate* isolate, Handle<WasmInstanceObject> object);
+ void ResetInstancesLink(Isolate* isolate);
uint32_t current_pages();
- int32_t maximum_pages(); // returns < 0 if there is no maximum
+ bool has_maximum_pages();
+ int32_t maximum_pages(); // Returns < 0 if there is no maximum.
static Handle<WasmMemoryObject> New(Isolate* isolate,
Handle<JSArrayBuffer> buffer,
- int maximum);
+ int32_t maximum);
- static bool Grow(Handle<WasmMemoryObject> memory, uint32_t count);
+ static bool Grow(Isolate* isolate, Handle<WasmMemoryObject> memory,
+ uint32_t count);
};
// Representation of a WebAssembly.Instance JavaScript-level object.
@@ -105,6 +109,7 @@ class WasmInstanceObject : public JSObject {
kMemoryArrayBuffer,
kGlobalsArrayBuffer,
kDebugInfo,
+ kWasmMemInstanceWrapper,
kFieldCount
};
@@ -115,10 +120,16 @@ class WasmInstanceObject : public JSObject {
DECLARE_OPTIONAL_ACCESSORS(memory_buffer, JSArrayBuffer);
DECLARE_OPTIONAL_ACCESSORS(memory_object, WasmMemoryObject);
DECLARE_OPTIONAL_ACCESSORS(debug_info, WasmDebugInfo);
+ DECLARE_OPTIONAL_ACCESSORS(instance_wrapper, WasmInstanceWrapper);
WasmModuleObject* module_object();
wasm::WasmModule* module();
+ // Get the debug info associated with the given wasm object.
+ // If no debug info exists yet, it is created automatically.
+ static Handle<WasmDebugInfo> GetOrCreateDebugInfo(
+ Handle<WasmInstanceObject> instance);
+
static Handle<WasmInstanceObject> New(
Isolate* isolate, Handle<WasmCompiledModule> compiled_module);
};
@@ -135,9 +146,39 @@ class WasmExportedFunction : public JSFunction {
static Handle<WasmExportedFunction> New(Isolate* isolate,
Handle<WasmInstanceObject> instance,
- Handle<String> name,
- Handle<Code> export_wrapper,
- int arity, int func_index);
+ MaybeHandle<String> maybe_name,
+ int func_index, int arity,
+ Handle<Code> export_wrapper);
+};
+
+// Information shared by all WasmCompiledModule objects for the same module.
+class WasmSharedModuleData : public FixedArray {
+ enum Fields {
+ kModuleWrapper,
+ kModuleBytes,
+ kScript,
+ kAsmJsOffsetTable,
+ kFieldCount
+ };
+
+ public:
+ DECLARE_CASTS(WasmSharedModuleData);
+
+ DECLARE_GETTER(module, wasm::WasmModule);
+ DECLARE_OPTIONAL_ACCESSORS(module_bytes, SeqOneByteString);
+ DECLARE_GETTER(script, Script);
+ DECLARE_OPTIONAL_ACCESSORS(asm_js_offset_table, ByteArray);
+
+ static Handle<WasmSharedModuleData> New(
+ Isolate* isolate, Handle<Foreign> module_wrapper,
+ Handle<SeqOneByteString> module_bytes, Handle<Script> script,
+ Handle<ByteArray> asm_js_offset_table);
+
+ // Check whether this module was generated from asm.js source.
+ bool is_asm_js();
+
+ // Recreate the ModuleWrapper from the module bytes after deserialization.
+ static void RecreateModuleWrapper(Isolate*, Handle<WasmSharedModuleData>);
};
class WasmCompiledModule : public FixedArray {
@@ -149,7 +190,7 @@ class WasmCompiledModule : public FixedArray {
return reinterpret_cast<WasmCompiledModule*>(fixed_array);
}
-#define WCM_OBJECT_OR_WEAK(TYPE, NAME, ID) \
+#define WCM_OBJECT_OR_WEAK(TYPE, NAME, ID, TYPE_CHECK) \
Handle<TYPE> NAME() const { return handle(ptr_to_##NAME()); } \
\
MaybeHandle<TYPE> maybe_##NAME() const { \
@@ -157,9 +198,15 @@ class WasmCompiledModule : public FixedArray {
return MaybeHandle<TYPE>(); \
} \
\
+ TYPE* maybe_ptr_to_##NAME() const { \
+ Object* obj = get(ID); \
+ if (!(TYPE_CHECK)) return nullptr; \
+ return TYPE::cast(obj); \
+ } \
+ \
TYPE* ptr_to_##NAME() const { \
Object* obj = get(ID); \
- if (!obj->Is##TYPE()) return nullptr; \
+ DCHECK(TYPE_CHECK); \
return TYPE::cast(obj); \
} \
\
@@ -167,11 +214,18 @@ class WasmCompiledModule : public FixedArray {
\
void set_ptr_to_##NAME(TYPE* value) { set(ID, value); } \
\
- bool has_##NAME() const { return get(ID)->Is##TYPE(); } \
+ bool has_##NAME() const { \
+ Object* obj = get(ID); \
+ return TYPE_CHECK; \
+ } \
\
void reset_##NAME() { set_undefined(ID); }
-#define WCM_OBJECT(TYPE, NAME) WCM_OBJECT_OR_WEAK(TYPE, NAME, kID_##NAME)
+#define WCM_OBJECT(TYPE, NAME) \
+ WCM_OBJECT_OR_WEAK(TYPE, NAME, kID_##NAME, obj->Is##TYPE())
+
+#define WCM_WASM_OBJECT(TYPE, NAME) \
+ WCM_OBJECT_OR_WEAK(TYPE, NAME, kID_##NAME, TYPE::Is##TYPE(obj))
#define WCM_SMALL_NUMBER(TYPE, NAME) \
TYPE NAME() const { \
@@ -179,30 +233,29 @@ class WasmCompiledModule : public FixedArray {
} \
void set_##NAME(TYPE value) { set(kID_##NAME, Smi::FromInt(value)); }
-#define WCM_WEAK_LINK(TYPE, NAME) \
- WCM_OBJECT_OR_WEAK(WeakCell, weak_##NAME, kID_##NAME); \
- \
- Handle<TYPE> NAME() const { \
- return handle(TYPE::cast(weak_##NAME()->value())); \
+#define WCM_WEAK_LINK(TYPE, NAME) \
+ WCM_OBJECT_OR_WEAK(WeakCell, weak_##NAME, kID_##NAME, obj->IsWeakCell()); \
+ \
+ Handle<TYPE> NAME() const { \
+ return handle(TYPE::cast(weak_##NAME()->value())); \
}
-#define CORE_WCM_PROPERTY_TABLE(MACRO) \
- MACRO(OBJECT, FixedArray, code_table) \
- MACRO(OBJECT, Foreign, module_wrapper) \
- /* For debugging: */ \
- MACRO(OBJECT, SeqOneByteString, module_bytes) \
- MACRO(OBJECT, Script, script) \
- MACRO(OBJECT, ByteArray, asm_js_offset_tables) \
- /* End of debugging stuff */ \
- MACRO(OBJECT, FixedArray, function_tables) \
- MACRO(OBJECT, FixedArray, empty_function_tables) \
- MACRO(OBJECT, JSArrayBuffer, memory) \
- MACRO(SMALL_NUMBER, uint32_t, min_mem_pages) \
- MACRO(SMALL_NUMBER, uint32_t, max_mem_pages) \
- MACRO(WEAK_LINK, WasmCompiledModule, next_instance) \
- MACRO(WEAK_LINK, WasmCompiledModule, prev_instance) \
- MACRO(WEAK_LINK, JSObject, owning_instance) \
- MACRO(WEAK_LINK, JSObject, wasm_module)
+#define CORE_WCM_PROPERTY_TABLE(MACRO) \
+ MACRO(WASM_OBJECT, WasmSharedModuleData, shared) \
+ MACRO(OBJECT, Context, native_context) \
+ MACRO(SMALL_NUMBER, uint32_t, num_imported_functions) \
+ MACRO(OBJECT, FixedArray, code_table) \
+ MACRO(OBJECT, FixedArray, weak_exported_functions) \
+ MACRO(OBJECT, FixedArray, function_tables) \
+ MACRO(OBJECT, FixedArray, signature_tables) \
+ MACRO(OBJECT, FixedArray, empty_function_tables) \
+ MACRO(OBJECT, JSArrayBuffer, memory) \
+ MACRO(SMALL_NUMBER, uint32_t, min_mem_pages) \
+ MACRO(SMALL_NUMBER, uint32_t, max_mem_pages) \
+ MACRO(WEAK_LINK, WasmCompiledModule, next_instance) \
+ MACRO(WEAK_LINK, WasmCompiledModule, prev_instance) \
+ MACRO(WEAK_LINK, JSObject, owning_instance) \
+ MACRO(WEAK_LINK, WasmModuleObject, wasm_module)
#if DEBUG
#define DEBUG_ONLY_TABLE(MACRO) MACRO(SMALL_NUMBER, uint32_t, instance_id)
@@ -223,8 +276,8 @@ class WasmCompiledModule : public FixedArray {
};
public:
- static Handle<WasmCompiledModule> New(
- Isolate* isolate, Handle<Managed<wasm::WasmModule>> module_wrapper);
+ static Handle<WasmCompiledModule> New(Isolate* isolate,
+ Handle<WasmSharedModuleData> shared);
static Handle<WasmCompiledModule> Clone(Isolate* isolate,
Handle<WasmCompiledModule> module) {
@@ -234,30 +287,93 @@ class WasmCompiledModule : public FixedArray {
ret->reset_weak_owning_instance();
ret->reset_weak_next_instance();
ret->reset_weak_prev_instance();
+ ret->reset_weak_exported_functions();
return ret;
}
uint32_t mem_size() const;
uint32_t default_mem_size() const;
- wasm::WasmModule* module() const;
-
#define DECLARATION(KIND, TYPE, NAME) WCM_##KIND(TYPE, NAME)
WCM_PROPERTY_TABLE(DECLARATION)
#undef DECLARATION
+// Allow to call method on WasmSharedModuleData also on this object.
+#define FORWARD_SHARED(type, name) \
+ type name() { return shared()->name(); }
+ FORWARD_SHARED(SeqOneByteString*, module_bytes)
+ FORWARD_SHARED(wasm::WasmModule*, module)
+ FORWARD_SHARED(Script*, script)
+ FORWARD_SHARED(bool, is_asm_js)
+#undef FORWARD_SHARED
+
static bool IsWasmCompiledModule(Object* obj);
void PrintInstancesChain();
+ // Recreate the ModuleWrapper from the module bytes after deserialization.
static void RecreateModuleWrapper(Isolate* isolate,
- Handle<FixedArray> compiled_module);
+ Handle<WasmCompiledModule> compiled_module);
- // Extract a function name from the given wasm instance.
+ // Get the function name of the function identified by the given index.
// Returns a null handle if the function is unnamed or the name is not a valid
// UTF-8 string.
- static MaybeHandle<String> GetFunctionName(
- Handle<WasmCompiledModule> compiled_module, uint32_t func_index);
+ static MaybeHandle<String> GetFunctionNameOrNull(
+ Isolate* isolate, Handle<WasmCompiledModule> compiled_module,
+ uint32_t func_index);
+
+ // Get the function name of the function identified by the given index.
+ // Returns "<WASM UNNAMED>" if the function is unnamed or the name is not a
+ // valid UTF-8 string.
+ static Handle<String> GetFunctionName(
+ Isolate* isolate, Handle<WasmCompiledModule> compiled_module,
+ uint32_t func_index);
+
+ // Get the raw bytes of the function name of the function identified by the
+ // given index.
+ // Meant to be used for debugging or frame printing.
+ // Does not allocate, hence gc-safe.
+ Vector<const uint8_t> GetRawFunctionName(uint32_t func_index);
+
+ // Return the byte offset of the function identified by the given index.
+ // The offset will be relative to the start of the module bytes.
+ // Returns -1 if the function index is invalid.
+ int GetFunctionOffset(uint32_t func_index);
+
+ // Returns the function containing the given byte offset.
+ // Returns -1 if the byte offset is not contained in any function of this
+ // module.
+ int GetContainingFunction(uint32_t byte_offset);
+
+ // Translate from byte offset in the module to function number and byte offset
+ // within that function, encoded as line and column in the position info.
+ // Returns true if the position is valid inside this module, false otherwise.
+ bool GetPositionInfo(uint32_t position, Script::PositionInfo* info);
+
+ // Get the asm.js source position from a byte offset.
+ // Must only be called if the associated wasm object was created from asm.js.
+ static int GetAsmJsSourcePosition(Handle<WasmCompiledModule> compiled_module,
+ uint32_t func_index, uint32_t byte_offset,
+ bool is_at_number_conversion);
+
+ // Compute the disassembly of a wasm function.
+ // Returns the disassembly string and a list of <byte_offset, line, column>
+ // entries, mapping wasm byte offsets to line and column in the disassembly.
+ // The list is guaranteed to be ordered by the byte_offset.
+ // Returns an empty string and empty vector if the function index is invalid.
+ debug::WasmDisassembly DisassembleFunction(int func_index);
+
+ // Extract a portion of the wire bytes as UTF-8 string.
+ // Returns a null handle if the respective bytes do not form a valid UTF-8
+ // string.
+ static MaybeHandle<String> ExtractUtf8StringFromModuleBytes(
+ Isolate* isolate, Handle<WasmCompiledModule> compiled_module,
+ uint32_t offset, uint32_t size);
+
+ // Get a list of all possible breakpoints within a given range of this module.
+ bool GetPossibleBreakpoints(const debug::Location& start,
+ const debug::Location& end,
+ std::vector<debug::Location>* locations);
private:
void InitId();
@@ -267,36 +383,79 @@ class WasmCompiledModule : public FixedArray {
class WasmDebugInfo : public FixedArray {
public:
- enum class Fields { kFieldCount };
-
- static Handle<WasmDebugInfo> New(Handle<JSObject> wasm);
+ enum Fields {
+ kInstance,
+ kInterpreterHandle,
+ kInterpretedFunctions,
+ kFieldCount
+ };
- static bool IsDebugInfo(Object* object);
- static WasmDebugInfo* cast(Object* object);
+ static Handle<WasmDebugInfo> New(Handle<WasmInstanceObject>);
- JSObject* wasm_instance();
+ static bool IsDebugInfo(Object*);
+ static WasmDebugInfo* cast(Object*);
- bool SetBreakPoint(int byte_offset);
+ static void SetBreakpoint(Handle<WasmDebugInfo>, int func_index, int offset);
- // Get the Script for the specified function.
- static Script* GetFunctionScript(Handle<WasmDebugInfo> debug_info,
- int func_index);
+ static void RunInterpreter(Handle<WasmDebugInfo>, int func_index,
+ uint8_t* arg_buffer);
- // Disassemble the specified function from this module.
- static Handle<String> DisassembleFunction(Handle<WasmDebugInfo> debug_info,
- int func_index);
+ DECLARE_GETTER(wasm_instance, WasmInstanceObject);
+};
- // Get the offset table for the specified function, mapping from byte offsets
- // to position in the disassembly.
- // Returns an array with three entries per instruction: byte offset, line and
- // column.
- static Handle<FixedArray> GetFunctionOffsetTable(
- Handle<WasmDebugInfo> debug_info, int func_index);
+class WasmInstanceWrapper : public FixedArray {
+ public:
+ static Handle<WasmInstanceWrapper> New(Isolate* isolate,
+ Handle<WasmInstanceObject> instance);
+ static WasmInstanceWrapper* cast(Object* fixed_array) {
+ SLOW_DCHECK(IsWasmInstanceWrapper(fixed_array));
+ return reinterpret_cast<WasmInstanceWrapper*>(fixed_array);
+ }
+ static bool IsWasmInstanceWrapper(Object* obj);
+ bool has_instance() { return get(kWrapperInstanceObject)->IsWeakCell(); }
+ Handle<WasmInstanceObject> instance_object() {
+ Object* obj = get(kWrapperInstanceObject);
+ DCHECK(obj->IsWeakCell());
+ WeakCell* cell = WeakCell::cast(obj);
+ DCHECK(cell->value()->IsJSObject());
+ return handle(WasmInstanceObject::cast(cell->value()));
+ }
+ bool has_next() { return IsWasmInstanceWrapper(get(kNextInstanceWrapper)); }
+ bool has_previous() {
+ return IsWasmInstanceWrapper(get(kPreviousInstanceWrapper));
+ }
+ void set_instance_object(Handle<JSObject> instance, Isolate* isolate);
+ void set_next_wrapper(Object* obj) {
+ DCHECK(IsWasmInstanceWrapper(obj));
+ set(kNextInstanceWrapper, obj);
+ }
+ void set_previous_wrapper(Object* obj) {
+ DCHECK(IsWasmInstanceWrapper(obj));
+ set(kPreviousInstanceWrapper, obj);
+ }
+ Handle<WasmInstanceWrapper> next_wrapper() {
+ Object* obj = get(kNextInstanceWrapper);
+ DCHECK(IsWasmInstanceWrapper(obj));
+ return handle(WasmInstanceWrapper::cast(obj));
+ }
+ Handle<WasmInstanceWrapper> previous_wrapper() {
+ Object* obj = get(kPreviousInstanceWrapper);
+ DCHECK(IsWasmInstanceWrapper(obj));
+ return handle(WasmInstanceWrapper::cast(obj));
+ }
+ void reset_next_wrapper() { set_undefined(kNextInstanceWrapper); }
+ void reset_previous_wrapper() { set_undefined(kPreviousInstanceWrapper); }
+ void reset() {
+ for (int kID = 0; kID < kWrapperPropertyCount; kID++) set_undefined(kID);
+ }
- // Get the asm.js source position from a byte offset.
- // Must only be called if the associated wasm object was created from asm.js.
- static int GetAsmJsSourcePosition(Handle<WasmDebugInfo> debug_info,
- int func_index, int byte_offset);
+ private:
+ enum {
+ kWrapperInstanceObject,
+ kNextInstanceWrapper,
+ kPreviousInstanceWrapper,
+ kWrapperPropertyCount
+ };
};
#undef DECLARE_ACCESSORS
diff --git a/deps/v8/src/wasm/wasm-opcodes.cc b/deps/v8/src/wasm/wasm-opcodes.cc
index 8f81b81a50..2a00a73cbd 100644
--- a/deps/v8/src/wasm/wasm-opcodes.cc
+++ b/deps/v8/src/wasm/wasm-opcodes.cc
@@ -4,13 +4,14 @@
#include "src/wasm/wasm-opcodes.h"
#include "src/messages.h"
+#include "src/runtime/runtime.h"
#include "src/signature.h"
namespace v8 {
namespace internal {
namespace wasm {
-typedef Signature<LocalType> FunctionSig;
+typedef Signature<ValueType> FunctionSig;
const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) {
switch (opcode) {
@@ -69,7 +70,7 @@ enum WasmOpcodeSig { FOREACH_SIGNATURE(DECLARE_SIG_ENUM) };
// TODO(titzer): not static-initializer safe. Wrap in LazyInstance.
#define DECLARE_SIG(name, ...) \
- static LocalType kTypes_##name[] = {__VA_ARGS__}; \
+ static ValueType kTypes_##name[] = {__VA_ARGS__}; \
static const FunctionSig kSig_##name( \
1, static_cast<int>(arraysize(kTypes_##name)) - 1, kTypes_##name);
diff --git a/deps/v8/src/wasm/wasm-opcodes.h b/deps/v8/src/wasm/wasm-opcodes.h
index ec22579bd7..6c231ac69b 100644
--- a/deps/v8/src/wasm/wasm-opcodes.h
+++ b/deps/v8/src/wasm/wasm-opcodes.h
@@ -7,6 +7,7 @@
#include "src/globals.h"
#include "src/machine-type.h"
+#include "src/runtime/runtime.h"
#include "src/signature.h"
namespace v8 {
@@ -14,7 +15,7 @@ namespace internal {
namespace wasm {
// Binary encoding of local types.
-enum LocalTypeCode {
+enum ValueTypeCode {
kLocalVoid = 0x40,
kLocalI32 = 0x7f,
kLocalI64 = 0x7e,
@@ -26,19 +27,18 @@ enum LocalTypeCode {
// Type code for multi-value block types.
static const uint8_t kMultivalBlock = 0x41;
-// We reuse the internal machine type to represent WebAssembly AST types.
+// We reuse the internal machine type to represent WebAssembly types.
// A typedef improves readability without adding a whole new type system.
-typedef MachineRepresentation LocalType;
-const LocalType kAstStmt = MachineRepresentation::kNone;
-const LocalType kAstI32 = MachineRepresentation::kWord32;
-const LocalType kAstI64 = MachineRepresentation::kWord64;
-const LocalType kAstF32 = MachineRepresentation::kFloat32;
-const LocalType kAstF64 = MachineRepresentation::kFloat64;
-const LocalType kAstS128 = MachineRepresentation::kSimd128;
-// We use kTagged here because kNone is already used by kAstStmt.
-const LocalType kAstEnd = MachineRepresentation::kTagged;
-
-typedef Signature<LocalType> FunctionSig;
+typedef MachineRepresentation ValueType;
+const ValueType kWasmStmt = MachineRepresentation::kNone;
+const ValueType kWasmI32 = MachineRepresentation::kWord32;
+const ValueType kWasmI64 = MachineRepresentation::kWord64;
+const ValueType kWasmF32 = MachineRepresentation::kFloat32;
+const ValueType kWasmF64 = MachineRepresentation::kFloat64;
+const ValueType kWasmS128 = MachineRepresentation::kSimd128;
+const ValueType kWasmVar = MachineRepresentation::kTagged;
+
+typedef Signature<ValueType> FunctionSig;
std::ostream& operator<<(std::ostream& os, const FunctionSig& function);
typedef Vector<const char> WasmName;
@@ -77,8 +77,7 @@ const WasmCodePosition kNoCodePosition = -1;
V(I32Const, 0x41, _) \
V(I64Const, 0x42, _) \
V(F32Const, 0x43, _) \
- V(F64Const, 0x44, _) \
- V(I8Const, 0xcb, _ /* TODO(titzer): V8 specific, remove */)
+ V(F64Const, 0x44, _)
// Load memory expressions.
#define FOREACH_LOAD_MEM_OPCODE(V) \
@@ -276,7 +275,6 @@ const WasmCodePosition kNoCodePosition = -1;
#define FOREACH_SIMD_0_OPERAND_OPCODE(V) \
V(F32x4Splat, 0xe500, s_f) \
- V(F32x4ReplaceLane, 0xe502, s_sif) \
V(F32x4Abs, 0xe503, s_s) \
V(F32x4Neg, 0xe504, s_s) \
V(F32x4Sqrt, 0xe505, s_s) \
@@ -296,13 +294,9 @@ const WasmCodePosition kNoCodePosition = -1;
V(F32x4Le, 0xe513, s_ss) \
V(F32x4Gt, 0xe514, s_ss) \
V(F32x4Ge, 0xe515, s_ss) \
- V(F32x4Select, 0xe516, s_sss) \
- V(F32x4Swizzle, 0xe517, s_s) \
- V(F32x4Shuffle, 0xe518, s_ss) \
V(F32x4FromInt32x4, 0xe519, s_s) \
V(F32x4FromUint32x4, 0xe51a, s_s) \
V(I32x4Splat, 0xe51b, s_i) \
- V(I32x4ReplaceLane, 0xe51d, s_sii) \
V(I32x4Neg, 0xe51e, s_s) \
V(I32x4Add, 0xe51f, s_ss) \
V(I32x4Sub, 0xe520, s_ss) \
@@ -330,7 +324,6 @@ const WasmCodePosition kNoCodePosition = -1;
V(I32x4Ge_u, 0xe536, s_ss) \
V(Ui32x4FromFloat32x4, 0xe537, s_s) \
V(I16x8Splat, 0xe538, s_i) \
- V(I16x8ReplaceLane, 0xe53a, s_sii) \
V(I16x8Neg, 0xe53b, s_s) \
V(I16x8Add, 0xe53c, s_ss) \
V(I16x8AddSaturate_s, 0xe53d, s_ss) \
@@ -360,7 +353,6 @@ const WasmCodePosition kNoCodePosition = -1;
V(I16x8Gt_u, 0xe555, s_ss) \
V(I16x8Ge_u, 0xe556, s_ss) \
V(I8x16Splat, 0xe557, s_i) \
- V(I8x16ReplaceLane, 0xe559, s_sii) \
V(I8x16Neg, 0xe55a, s_s) \
V(I8x16Add, 0xe55b, s_ss) \
V(I8x16AddSaturate_s, 0xe55c, s_ss) \
@@ -392,13 +384,20 @@ const WasmCodePosition kNoCodePosition = -1;
V(S128And, 0xe576, s_ss) \
V(S128Ior, 0xe577, s_ss) \
V(S128Xor, 0xe578, s_ss) \
- V(S128Not, 0xe579, s_s)
+ V(S128Not, 0xe579, s_s) \
+ V(S32x4Select, 0xe580, s_sss) \
+ V(S32x4Swizzle, 0xe581, s_s) \
+ V(S32x4Shuffle, 0xe582, s_ss)
#define FOREACH_SIMD_1_OPERAND_OPCODE(V) \
V(F32x4ExtractLane, 0xe501, _) \
+ V(F32x4ReplaceLane, 0xe502, _) \
V(I32x4ExtractLane, 0xe51c, _) \
+ V(I32x4ReplaceLane, 0xe51d, _) \
V(I16x8ExtractLane, 0xe539, _) \
- V(I8x16ExtractLane, 0xe558, _)
+ V(I16x8ReplaceLane, 0xe53a, _) \
+ V(I8x16ExtractLane, 0xe558, _) \
+ V(I8x16ReplaceLane, 0xe559, _)
#define FOREACH_ATOMIC_OPCODE(V) \
V(I32AtomicAdd8S, 0xe601, i_ii) \
@@ -451,45 +450,43 @@ const WasmCodePosition kNoCodePosition = -1;
FOREACH_ATOMIC_OPCODE(V)
// All signatures.
-#define FOREACH_SIGNATURE(V) \
- FOREACH_SIMD_SIGNATURE(V) \
- V(i_ii, kAstI32, kAstI32, kAstI32) \
- V(i_i, kAstI32, kAstI32) \
- V(i_v, kAstI32) \
- V(i_ff, kAstI32, kAstF32, kAstF32) \
- V(i_f, kAstI32, kAstF32) \
- V(i_dd, kAstI32, kAstF64, kAstF64) \
- V(i_d, kAstI32, kAstF64) \
- V(i_l, kAstI32, kAstI64) \
- V(l_ll, kAstI64, kAstI64, kAstI64) \
- V(i_ll, kAstI32, kAstI64, kAstI64) \
- V(l_l, kAstI64, kAstI64) \
- V(l_i, kAstI64, kAstI32) \
- V(l_f, kAstI64, kAstF32) \
- V(l_d, kAstI64, kAstF64) \
- V(f_ff, kAstF32, kAstF32, kAstF32) \
- V(f_f, kAstF32, kAstF32) \
- V(f_d, kAstF32, kAstF64) \
- V(f_i, kAstF32, kAstI32) \
- V(f_l, kAstF32, kAstI64) \
- V(d_dd, kAstF64, kAstF64, kAstF64) \
- V(d_d, kAstF64, kAstF64) \
- V(d_f, kAstF64, kAstF32) \
- V(d_i, kAstF64, kAstI32) \
- V(d_l, kAstF64, kAstI64) \
- V(d_id, kAstF64, kAstI32, kAstF64) \
- V(f_if, kAstF32, kAstI32, kAstF32) \
- V(l_il, kAstI64, kAstI32, kAstI64)
-
-#define FOREACH_SIMD_SIGNATURE(V) \
- V(s_s, kAstS128, kAstS128) \
- V(s_f, kAstS128, kAstF32) \
- V(s_sif, kAstS128, kAstS128, kAstI32, kAstF32) \
- V(s_ss, kAstS128, kAstS128, kAstS128) \
- V(s_sss, kAstS128, kAstS128, kAstS128, kAstS128) \
- V(s_i, kAstS128, kAstI32) \
- V(s_sii, kAstS128, kAstS128, kAstI32, kAstI32) \
- V(s_si, kAstS128, kAstS128, kAstI32)
+#define FOREACH_SIGNATURE(V) \
+ FOREACH_SIMD_SIGNATURE(V) \
+ V(i_ii, kWasmI32, kWasmI32, kWasmI32) \
+ V(i_i, kWasmI32, kWasmI32) \
+ V(i_v, kWasmI32) \
+ V(i_ff, kWasmI32, kWasmF32, kWasmF32) \
+ V(i_f, kWasmI32, kWasmF32) \
+ V(i_dd, kWasmI32, kWasmF64, kWasmF64) \
+ V(i_d, kWasmI32, kWasmF64) \
+ V(i_l, kWasmI32, kWasmI64) \
+ V(l_ll, kWasmI64, kWasmI64, kWasmI64) \
+ V(i_ll, kWasmI32, kWasmI64, kWasmI64) \
+ V(l_l, kWasmI64, kWasmI64) \
+ V(l_i, kWasmI64, kWasmI32) \
+ V(l_f, kWasmI64, kWasmF32) \
+ V(l_d, kWasmI64, kWasmF64) \
+ V(f_ff, kWasmF32, kWasmF32, kWasmF32) \
+ V(f_f, kWasmF32, kWasmF32) \
+ V(f_d, kWasmF32, kWasmF64) \
+ V(f_i, kWasmF32, kWasmI32) \
+ V(f_l, kWasmF32, kWasmI64) \
+ V(d_dd, kWasmF64, kWasmF64, kWasmF64) \
+ V(d_d, kWasmF64, kWasmF64) \
+ V(d_f, kWasmF64, kWasmF32) \
+ V(d_i, kWasmF64, kWasmI32) \
+ V(d_l, kWasmF64, kWasmI64) \
+ V(d_id, kWasmF64, kWasmI32, kWasmF64) \
+ V(f_if, kWasmF32, kWasmI32, kWasmF32) \
+ V(l_il, kWasmI64, kWasmI32, kWasmI64)
+
+#define FOREACH_SIMD_SIGNATURE(V) \
+ V(s_s, kWasmS128, kWasmS128) \
+ V(s_f, kWasmS128, kWasmF32) \
+ V(s_ss, kWasmS128, kWasmS128, kWasmS128) \
+ V(s_sss, kWasmS128, kWasmS128, kWasmS128, kWasmS128) \
+ V(s_i, kWasmS128, kWasmI32) \
+ V(s_si, kWasmS128, kWasmS128, kWasmI32)
#define FOREACH_PREFIX(V) \
V(Simd, 0xe5) \
@@ -514,8 +511,7 @@ enum WasmOpcode {
V(TrapRemByZero) \
V(TrapFloatUnrepresentable) \
V(TrapFuncInvalid) \
- V(TrapFuncSigMismatch) \
- V(TrapInvalidIndex)
+ V(TrapFuncSigMismatch)
enum TrapReason {
#define DECLARE_ENUM(name) k##name,
@@ -541,21 +537,21 @@ class V8_EXPORT_PRIVATE WasmOpcodes {
return 1 << ElementSizeLog2Of(type.representation());
}
- static byte MemSize(LocalType type) { return 1 << ElementSizeLog2Of(type); }
+ static byte MemSize(ValueType type) { return 1 << ElementSizeLog2Of(type); }
- static LocalTypeCode LocalTypeCodeFor(LocalType type) {
+ static ValueTypeCode ValueTypeCodeFor(ValueType type) {
switch (type) {
- case kAstI32:
+ case kWasmI32:
return kLocalI32;
- case kAstI64:
+ case kWasmI64:
return kLocalI64;
- case kAstF32:
+ case kWasmF32:
return kLocalF32;
- case kAstF64:
+ case kWasmF64:
return kLocalF64;
- case kAstS128:
+ case kWasmS128:
return kLocalS128;
- case kAstStmt:
+ case kWasmStmt:
return kLocalVoid;
default:
UNREACHABLE();
@@ -563,19 +559,19 @@ class V8_EXPORT_PRIVATE WasmOpcodes {
}
}
- static MachineType MachineTypeFor(LocalType type) {
+ static MachineType MachineTypeFor(ValueType type) {
switch (type) {
- case kAstI32:
+ case kWasmI32:
return MachineType::Int32();
- case kAstI64:
+ case kWasmI64:
return MachineType::Int64();
- case kAstF32:
+ case kWasmF32:
return MachineType::Float32();
- case kAstF64:
+ case kWasmF64:
return MachineType::Float64();
- case kAstS128:
+ case kWasmS128:
return MachineType::Simd128();
- case kAstStmt:
+ case kWasmStmt:
return MachineType::None();
default:
UNREACHABLE();
@@ -583,32 +579,32 @@ class V8_EXPORT_PRIVATE WasmOpcodes {
}
}
- static LocalType LocalTypeFor(MachineType type) {
+ static ValueType ValueTypeFor(MachineType type) {
if (type == MachineType::Int8()) {
- return kAstI32;
+ return kWasmI32;
} else if (type == MachineType::Uint8()) {
- return kAstI32;
+ return kWasmI32;
} else if (type == MachineType::Int16()) {
- return kAstI32;
+ return kWasmI32;
} else if (type == MachineType::Uint16()) {
- return kAstI32;
+ return kWasmI32;
} else if (type == MachineType::Int32()) {
- return kAstI32;
+ return kWasmI32;
} else if (type == MachineType::Uint32()) {
- return kAstI32;
+ return kWasmI32;
} else if (type == MachineType::Int64()) {
- return kAstI64;
+ return kWasmI64;
} else if (type == MachineType::Uint64()) {
- return kAstI64;
+ return kWasmI64;
} else if (type == MachineType::Float32()) {
- return kAstF32;
+ return kWasmF32;
} else if (type == MachineType::Float64()) {
- return kAstF64;
+ return kWasmF64;
} else if (type == MachineType::Simd128()) {
- return kAstS128;
+ return kWasmS128;
} else {
UNREACHABLE();
- return kAstI32;
+ return kWasmI32;
}
}
@@ -639,44 +635,43 @@ class V8_EXPORT_PRIVATE WasmOpcodes {
}
}
- static char ShortNameOf(LocalType type) {
+ static char ShortNameOf(ValueType type) {
switch (type) {
- case kAstI32:
+ case kWasmI32:
return 'i';
- case kAstI64:
+ case kWasmI64:
return 'l';
- case kAstF32:
+ case kWasmF32:
return 'f';
- case kAstF64:
+ case kWasmF64:
return 'd';
- case kAstS128:
+ case kWasmS128:
return 's';
- case kAstStmt:
+ case kWasmStmt:
return 'v';
- case kAstEnd:
- return 'x';
+ case kWasmVar:
+ return '*';
default:
- UNREACHABLE();
return '?';
}
}
- static const char* TypeName(LocalType type) {
+ static const char* TypeName(ValueType type) {
switch (type) {
- case kAstI32:
+ case kWasmI32:
return "i32";
- case kAstI64:
+ case kWasmI64:
return "i64";
- case kAstF32:
+ case kWasmF32:
return "f32";
- case kAstF64:
+ case kWasmF64:
return "f64";
- case kAstS128:
+ case kWasmS128:
return "s128";
- case kAstStmt:
+ case kWasmStmt:
return "<stmt>";
- case kAstEnd:
- return "<end>";
+ case kWasmVar:
+ return "<var>";
default:
return "<unknown>";
}
diff --git a/deps/v8/src/wasm/wasm-result.cc b/deps/v8/src/wasm/wasm-result.cc
index 6d535e3f57..e22f9ad442 100644
--- a/deps/v8/src/wasm/wasm-result.cc
+++ b/deps/v8/src/wasm/wasm-result.cc
@@ -64,14 +64,25 @@ void ErrorThrower::RangeError(const char* format, ...) {
void ErrorThrower::CompileError(const char* format, ...) {
if (error()) return;
+ wasm_error_ = true;
va_list arguments;
va_start(arguments, format);
Format(isolate_->wasm_compile_error_function(), format, arguments);
va_end(arguments);
}
+void ErrorThrower::LinkError(const char* format, ...) {
+ if (error()) return;
+ wasm_error_ = true;
+ va_list arguments;
+ va_start(arguments, format);
+ Format(isolate_->wasm_link_error_function(), format, arguments);
+ va_end(arguments);
+}
+
void ErrorThrower::RuntimeError(const char* format, ...) {
if (error()) return;
+ wasm_error_ = true;
va_list arguments;
va_start(arguments, format);
Format(isolate_->wasm_runtime_error_function(), format, arguments);
diff --git a/deps/v8/src/wasm/wasm-result.h b/deps/v8/src/wasm/wasm-result.h
index 53c6b8dcf9..004ac22d33 100644
--- a/deps/v8/src/wasm/wasm-result.h
+++ b/deps/v8/src/wasm/wasm-result.h
@@ -95,6 +95,7 @@ class V8_EXPORT_PRIVATE ErrorThrower {
PRINTF_FORMAT(2, 3) void TypeError(const char* fmt, ...);
PRINTF_FORMAT(2, 3) void RangeError(const char* fmt, ...);
PRINTF_FORMAT(2, 3) void CompileError(const char* fmt, ...);
+ PRINTF_FORMAT(2, 3) void LinkError(const char* fmt, ...);
PRINTF_FORMAT(2, 3) void RuntimeError(const char* fmt, ...);
template <typename T>
@@ -111,6 +112,7 @@ class V8_EXPORT_PRIVATE ErrorThrower {
}
bool error() const { return !exception_.is_null(); }
+ bool wasm_error() { return wasm_error_; }
private:
void Format(i::Handle<i::JSFunction> constructor, const char* fmt, va_list);
@@ -118,6 +120,7 @@ class V8_EXPORT_PRIVATE ErrorThrower {
i::Isolate* isolate_;
const char* context_;
i::Handle<i::Object> exception_;
+ bool wasm_error_ = false;
};
} // namespace wasm
} // namespace internal
diff --git a/deps/v8/src/wasm/wasm-text.cc b/deps/v8/src/wasm/wasm-text.cc
new file mode 100644
index 0000000000..1878095b09
--- /dev/null
+++ b/deps/v8/src/wasm/wasm-text.cc
@@ -0,0 +1,312 @@
+// Copyright 2016 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/wasm/wasm-text.h"
+
+#include "src/debug/interface-types.h"
+#include "src/ostreams.h"
+#include "src/vector.h"
+#include "src/wasm/function-body-decoder.h"
+#include "src/wasm/wasm-module.h"
+#include "src/wasm/wasm-opcodes.h"
+#include "src/zone/zone.h"
+
+using namespace v8;
+using namespace v8::internal;
+using namespace v8::internal::wasm;
+
+namespace {
+const char *GetOpName(WasmOpcode opcode) {
+#define CASE_OP(name, str) \
+ case kExpr##name: \
+ return str;
+#define CASE_I32_OP(name, str) CASE_OP(I32##name, "i32." str)
+#define CASE_I64_OP(name, str) CASE_OP(I64##name, "i64." str)
+#define CASE_F32_OP(name, str) CASE_OP(F32##name, "f32." str)
+#define CASE_F64_OP(name, str) CASE_OP(F64##name, "f64." str)
+#define CASE_INT_OP(name, str) CASE_I32_OP(name, str) CASE_I64_OP(name, str)
+#define CASE_FLOAT_OP(name, str) CASE_F32_OP(name, str) CASE_F64_OP(name, str)
+#define CASE_ALL_OP(name, str) CASE_FLOAT_OP(name, str) CASE_INT_OP(name, str)
+#define CASE_SIGN_OP(TYPE, name, str) \
+ CASE_##TYPE##_OP(name##S, str "_s") CASE_##TYPE##_OP(name##U, str "_u")
+#define CASE_ALL_SIGN_OP(name, str) \
+ CASE_FLOAT_OP(name, str) CASE_SIGN_OP(INT, name, str)
+#define CASE_CONVERT_OP(name, RES, SRC, src_suffix, str) \
+ CASE_##RES##_OP(U##name##SRC, str "_u/" src_suffix) \
+ CASE_##RES##_OP(S##name##SRC, str "_s/" src_suffix)
+
+ switch (opcode) {
+ CASE_INT_OP(Eqz, "eqz")
+ CASE_ALL_OP(Eq, "eq")
+ CASE_ALL_OP(Ne, "ne")
+ CASE_ALL_OP(Add, "add")
+ CASE_ALL_OP(Sub, "sub")
+ CASE_ALL_OP(Mul, "mul")
+ CASE_ALL_SIGN_OP(Lt, "lt")
+ CASE_ALL_SIGN_OP(Gt, "gt")
+ CASE_ALL_SIGN_OP(Le, "le")
+ CASE_ALL_SIGN_OP(Ge, "ge")
+ CASE_INT_OP(Clz, "clz")
+ CASE_INT_OP(Ctz, "ctz")
+ CASE_INT_OP(Popcnt, "popcnt")
+ CASE_ALL_SIGN_OP(Div, "div")
+ CASE_SIGN_OP(INT, Rem, "rem")
+ CASE_INT_OP(And, "and")
+ CASE_INT_OP(Ior, "or")
+ CASE_INT_OP(Xor, "xor")
+ CASE_INT_OP(Shl, "shl")
+ CASE_SIGN_OP(INT, Shr, "shr")
+ CASE_INT_OP(Rol, "rol")
+ CASE_INT_OP(Ror, "ror")
+ CASE_FLOAT_OP(Abs, "abs")
+ CASE_FLOAT_OP(Neg, "neg")
+ CASE_FLOAT_OP(Ceil, "ceil")
+ CASE_FLOAT_OP(Floor, "floor")
+ CASE_FLOAT_OP(Trunc, "trunc")
+ CASE_FLOAT_OP(NearestInt, "nearest")
+ CASE_FLOAT_OP(Sqrt, "sqrt")
+ CASE_FLOAT_OP(Min, "min")
+ CASE_FLOAT_OP(Max, "max")
+ CASE_FLOAT_OP(CopySign, "copysign")
+ CASE_I32_OP(ConvertI64, "wrap/i64")
+ CASE_CONVERT_OP(Convert, INT, F32, "f32", "trunc")
+ CASE_CONVERT_OP(Convert, INT, F64, "f64", "trunc")
+ CASE_CONVERT_OP(Convert, I64, I32, "i32", "extend")
+ CASE_CONVERT_OP(Convert, F32, I32, "i32", "convert")
+ CASE_CONVERT_OP(Convert, F32, I64, "i64", "convert")
+ CASE_F32_OP(ConvertF64, "demote/f64")
+ CASE_CONVERT_OP(Convert, F64, I32, "i32", "convert")
+ CASE_CONVERT_OP(Convert, F64, I64, "i64", "convert")
+ CASE_F64_OP(ConvertF32, "promote/f32")
+ CASE_I32_OP(ReinterpretF32, "reinterpret/f32")
+ CASE_I64_OP(ReinterpretF64, "reinterpret/f64")
+ CASE_F32_OP(ReinterpretI32, "reinterpret/i32")
+ CASE_F64_OP(ReinterpretI64, "reinterpret/i64")
+ CASE_OP(Unreachable, "unreachable")
+ CASE_OP(Nop, "nop")
+ CASE_OP(Return, "return")
+ CASE_OP(MemorySize, "current_memory")
+ CASE_OP(GrowMemory, "grow_memory")
+ CASE_OP(Loop, "loop")
+ CASE_OP(If, "if")
+ CASE_OP(Block, "block")
+ CASE_OP(Try, "try")
+ CASE_OP(Throw, "throw")
+ CASE_OP(Catch, "catch")
+ CASE_OP(Drop, "drop")
+ CASE_OP(Select, "select")
+ CASE_ALL_OP(LoadMem, "load")
+ CASE_SIGN_OP(INT, LoadMem8, "load8")
+ CASE_SIGN_OP(INT, LoadMem16, "load16")
+ CASE_SIGN_OP(I64, LoadMem32, "load32")
+ CASE_ALL_OP(StoreMem, "store")
+ CASE_INT_OP(StoreMem8, "store8")
+ CASE_INT_OP(StoreMem16, "store16")
+ CASE_I64_OP(StoreMem32, "store32")
+ CASE_OP(SetLocal, "set_local")
+ CASE_OP(GetLocal, "get_local")
+ CASE_OP(TeeLocal, "tee_local")
+ CASE_OP(GetGlobal, "get_global")
+ CASE_OP(SetGlobal, "set_global")
+ CASE_OP(Br, "br")
+ CASE_OP(BrIf, "br_if")
+ default:
+ UNREACHABLE();
+ return "";
+ }
+}
+
+bool IsValidFunctionName(const Vector<const char> &name) {
+ if (name.is_empty()) return false;
+ const char *special_chars = "_.+-*/\\^~=<>!?@#$%&|:'`";
+ for (char c : name) {
+ bool valid_char = (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') || strchr(special_chars, c);
+ if (!valid_char) return false;
+ }
+ return true;
+}
+
+} // namespace
+
+void wasm::PrintWasmText(const WasmModule *module,
+ const ModuleWireBytes &wire_bytes, uint32_t func_index,
+ std::ostream &os,
+ debug::WasmDisassembly::OffsetTable *offset_table) {
+ DCHECK_NOT_NULL(module);
+ DCHECK_GT(module->functions.size(), func_index);
+ const WasmFunction *fun = &module->functions[func_index];
+
+ AccountingAllocator allocator;
+ Zone zone(&allocator, ZONE_NAME);
+ int line_nr = 0;
+ int control_depth = 1;
+
+ // Print the function signature.
+ os << "func";
+ WasmName fun_name = wire_bytes.GetNameOrNull(fun);
+ if (IsValidFunctionName(fun_name)) {
+ os << " $";
+ os.write(fun_name.start(), fun_name.length());
+ }
+ size_t param_count = fun->sig->parameter_count();
+ if (param_count) {
+ os << " (param";
+ for (size_t i = 0; i < param_count; ++i)
+ os << ' ' << WasmOpcodes::TypeName(fun->sig->GetParam(i));
+ os << ')';
+ }
+ size_t return_count = fun->sig->return_count();
+ if (return_count) {
+ os << " (result";
+ for (size_t i = 0; i < return_count; ++i)
+ os << ' ' << WasmOpcodes::TypeName(fun->sig->GetReturn(i));
+ os << ')';
+ }
+ os << "\n";
+ ++line_nr;
+
+ // Print the local declarations.
+ BodyLocalDecls decls(&zone);
+ Vector<const byte> func_bytes = wire_bytes.module_bytes.SubVector(
+ fun->code_start_offset, fun->code_end_offset);
+ BytecodeIterator i(func_bytes.begin(), func_bytes.end(), &decls);
+ DCHECK_LT(func_bytes.begin(), i.pc());
+ if (!decls.type_list.empty()) {
+ os << "(local";
+ for (const ValueType &v : decls.type_list) {
+ os << ' ' << WasmOpcodes::TypeName(v);
+ }
+ os << ")\n";
+ ++line_nr;
+ }
+
+ for (; i.has_next(); i.next()) {
+ WasmOpcode opcode = i.current();
+ if (opcode == kExprElse || opcode == kExprEnd) --control_depth;
+
+ DCHECK_LE(0, control_depth);
+ const int kMaxIndentation = 64;
+ int indentation = std::min(kMaxIndentation, 2 * control_depth);
+ if (offset_table) {
+ offset_table->push_back(debug::WasmDisassemblyOffsetTableEntry(
+ i.pc_offset(), line_nr, indentation));
+ }
+
+ // 64 whitespaces
+ const char padding[kMaxIndentation + 1] =
+ " ";
+ os.write(padding, indentation);
+
+ switch (opcode) {
+ case kExprLoop:
+ case kExprIf:
+ case kExprBlock:
+ case kExprTry: {
+ BlockTypeOperand operand(&i, i.pc());
+ os << GetOpName(opcode);
+ for (unsigned i = 0; i < operand.arity; i++) {
+ os << " " << WasmOpcodes::TypeName(operand.read_entry(i));
+ }
+ control_depth++;
+ break;
+ }
+ case kExprBr:
+ case kExprBrIf: {
+ BreakDepthOperand operand(&i, i.pc());
+ os << GetOpName(opcode) << ' ' << operand.depth;
+ break;
+ }
+ case kExprElse:
+ os << "else";
+ control_depth++;
+ break;
+ case kExprEnd:
+ os << "end";
+ break;
+ case kExprBrTable: {
+ BranchTableOperand operand(&i, i.pc());
+ BranchTableIterator iterator(&i, operand);
+ os << "br_table";
+ while (iterator.has_next()) os << ' ' << iterator.next();
+ break;
+ }
+ case kExprCallIndirect: {
+ CallIndirectOperand operand(&i, i.pc());
+ DCHECK_EQ(0, operand.table_index);
+ os << "call_indirect " << operand.index;
+ break;
+ }
+ case kExprCallFunction: {
+ CallFunctionOperand operand(&i, i.pc());
+ os << "call " << operand.index;
+ break;
+ }
+ case kExprGetLocal:
+ case kExprSetLocal:
+ case kExprTeeLocal:
+ case kExprCatch: {
+ LocalIndexOperand operand(&i, i.pc());
+ os << GetOpName(opcode) << ' ' << operand.index;
+ break;
+ }
+ case kExprGetGlobal:
+ case kExprSetGlobal: {
+ GlobalIndexOperand operand(&i, i.pc());
+ os << GetOpName(opcode) << ' ' << operand.index;
+ break;
+ }
+#define CASE_CONST(type, str, cast_type) \
+ case kExpr##type##Const: { \
+ Imm##type##Operand operand(&i, i.pc()); \
+ os << #str ".const " << static_cast<cast_type>(operand.value); \
+ break; \
+ }
+ CASE_CONST(I32, i32, int32_t)
+ CASE_CONST(I64, i64, int64_t)
+ CASE_CONST(F32, f32, float)
+ CASE_CONST(F64, f64, double)
+
+#define CASE_OPCODE(opcode, _, __) case kExpr##opcode:
+ FOREACH_LOAD_MEM_OPCODE(CASE_OPCODE)
+ FOREACH_STORE_MEM_OPCODE(CASE_OPCODE) {
+ MemoryAccessOperand operand(&i, i.pc(), kMaxUInt32);
+ os << GetOpName(opcode) << " offset=" << operand.offset
+ << " align=" << (1ULL << operand.alignment);
+ break;
+ }
+
+ FOREACH_SIMPLE_OPCODE(CASE_OPCODE)
+ case kExprUnreachable:
+ case kExprNop:
+ case kExprReturn:
+ case kExprMemorySize:
+ case kExprGrowMemory:
+ case kExprDrop:
+ case kExprSelect:
+ case kExprThrow:
+ os << GetOpName(opcode);
+ break;
+
+ // This group is just printed by their internal opcode name, as they
+ // should never be shown to end-users.
+ FOREACH_ASMJS_COMPAT_OPCODE(CASE_OPCODE)
+ // TODO(wasm): Add correct printing for SIMD and atomic opcodes once
+ // they are publicly available.
+ FOREACH_SIMD_0_OPERAND_OPCODE(CASE_OPCODE)
+ FOREACH_SIMD_1_OPERAND_OPCODE(CASE_OPCODE)
+ FOREACH_ATOMIC_OPCODE(CASE_OPCODE)
+ os << WasmOpcodes::OpcodeName(opcode);
+ break;
+
+ default:
+ UNREACHABLE();
+ break;
+ }
+ os << '\n';
+ ++line_nr;
+ }
+ DCHECK_EQ(0, control_depth);
+ DCHECK(i.ok());
+}
diff --git a/deps/v8/src/wasm/wasm-text.h b/deps/v8/src/wasm/wasm-text.h
new file mode 100644
index 0000000000..1608ea9a2d
--- /dev/null
+++ b/deps/v8/src/wasm/wasm-text.h
@@ -0,0 +1,38 @@
+// Copyright 2016 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef V8_WASM_S_EXPR_H_
+#define V8_WASM_S_EXPR_H_
+
+#include <cstdint>
+#include <ostream>
+#include <tuple>
+#include <vector>
+
+namespace v8 {
+
+namespace debug {
+struct WasmDisassemblyOffsetTableEntry;
+} // namespace debug
+
+namespace internal {
+namespace wasm {
+
+// Forward declaration.
+struct WasmModule;
+struct ModuleWireBytes;
+
+// Generate disassembly according to official text format.
+// Output disassembly to the given output stream, and optionally return an
+// offset table of <byte offset, line, column> via the given pointer.
+void PrintWasmText(
+ const WasmModule *module, const ModuleWireBytes &wire_bytes,
+ uint32_t func_index, std::ostream &os,
+ std::vector<debug::WasmDisassemblyOffsetTableEntry> *offset_table);
+
+} // namespace wasm
+} // namespace internal
+} // namespace v8
+
+#endif // V8_WASM_S_EXPR_H_