summaryrefslogtreecommitdiff
path: root/deps/v8/src/wasm/decoder.h
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/wasm/decoder.h')
-rw-r--r--deps/v8/src/wasm/decoder.h293
1 files changed, 170 insertions, 123 deletions
diff --git a/deps/v8/src/wasm/decoder.h b/deps/v8/src/wasm/decoder.h
index 8e9830b447..3bfb9a1a91 100644
--- a/deps/v8/src/wasm/decoder.h
+++ b/deps/v8/src/wasm/decoder.h
@@ -38,6 +38,48 @@ namespace wasm {
// A {DecodeResult} only stores the failure / success status, but no data.
using DecodeResult = VoidResult;
+struct WasmFunction;
+
+class ITracer {
+ public:
+ static constexpr ITracer* NoTrace = nullptr;
+
+ // Hooks for extracting byte offsets of things.
+ virtual void TypeOffset(uint32_t offset) = 0;
+ virtual void ImportOffset(uint32_t offset) = 0;
+ virtual void ImportsDone() = 0;
+ virtual void TableOffset(uint32_t offset) = 0;
+ virtual void MemoryOffset(uint32_t offset) = 0;
+ virtual void TagOffset(uint32_t offset) = 0;
+ virtual void GlobalOffset(uint32_t offset) = 0;
+ virtual void StartOffset(uint32_t offset) = 0;
+ virtual void ElementOffset(uint32_t offset) = 0;
+ virtual void DataOffset(uint32_t offset) = 0;
+
+ // Hooks for annotated hex dumps.
+ virtual void Bytes(const byte* start, uint32_t count) = 0;
+
+ virtual void Description(const char* desc) = 0;
+ virtual void Description(const char* desc, size_t length) = 0;
+ virtual void Description(uint32_t number) = 0;
+ virtual void Description(ValueType type) = 0;
+ virtual void Description(HeapType type) = 0;
+ virtual void Description(const FunctionSig* sig) = 0;
+
+ virtual void NextLine() = 0;
+ virtual void NextLineIfFull() = 0;
+ virtual void NextLineIfNonEmpty() = 0;
+
+ virtual void InitializerExpression(const byte* start, const byte* end,
+ ValueType expected_type) = 0;
+ virtual void FunctionBody(const WasmFunction* func, const byte* start) = 0;
+ virtual void FunctionName(uint32_t func_index) = 0;
+ virtual void NameSection(const byte* start, const byte* end,
+ uint32_t offset) = 0;
+
+ virtual ~ITracer() = default;
+};
+
// A helper utility to decode bytes, integers, fields, varints, etc, from
// a buffer of bytes.
class Decoder {
@@ -117,82 +159,81 @@ class Decoder {
return read_little_endian<uint64_t, ValidationTag>(pc, msg);
}
- // Reads a variable-length unsigned integer (little endian).
+ // Reads a variable-length unsigned integer (little endian). Returns the read
+ // value and the number of bytes read.
template <typename ValidationTag>
- uint32_t read_u32v(const byte* pc, uint32_t* length,
- Name<ValidationTag> name = "LEB32") {
- return read_leb<uint32_t, ValidationTag, kNoTrace>(pc, length, name);
+ std::pair<uint32_t, uint32_t> read_u32v(const byte* pc,
+ Name<ValidationTag> name = "LEB32") {
+ return read_leb<uint32_t, ValidationTag, kNoTrace>(pc, name);
}
- // Reads a variable-length signed integer (little endian).
+ // Reads a variable-length signed integer (little endian). Returns the read
+ // value and the number of bytes read.
template <typename ValidationTag>
- int32_t read_i32v(const byte* pc, uint32_t* length,
- Name<ValidationTag> name = "signed LEB32") {
- return read_leb<int32_t, ValidationTag, kNoTrace>(pc, length, name);
+ std::pair<int32_t, uint32_t> read_i32v(
+ const byte* pc, Name<ValidationTag> name = "signed LEB32") {
+ return read_leb<int32_t, ValidationTag, kNoTrace>(pc, name);
}
- // Reads a variable-length unsigned integer (little endian).
+ // Reads a variable-length unsigned integer (little endian). Returns the read
+ // value and the number of bytes read.
template <typename ValidationTag>
- uint64_t read_u64v(const byte* pc, uint32_t* length,
- Name<ValidationTag> name = "LEB64") {
- return read_leb<uint64_t, ValidationTag, kNoTrace>(pc, length, name);
+ std::pair<uint64_t, uint32_t> read_u64v(const byte* pc,
+ Name<ValidationTag> name = "LEB64") {
+ return read_leb<uint64_t, ValidationTag, kNoTrace>(pc, name);
}
- // Reads a variable-length signed integer (little endian).
+ // Reads a variable-length signed integer (little endian). Returns the read
+ // value and the number of bytes read.
template <typename ValidationTag>
- int64_t read_i64v(const byte* pc, uint32_t* length,
- Name<ValidationTag> name = "signed LEB64") {
- return read_leb<int64_t, ValidationTag, kNoTrace>(pc, length, name);
+ std::pair<int64_t, uint32_t> read_i64v(
+ const byte* pc, Name<ValidationTag> name = "signed LEB64") {
+ return read_leb<int64_t, ValidationTag, kNoTrace>(pc, name);
}
- // Reads a variable-length 33-bit signed integer (little endian).
+ // Reads a variable-length 33-bit signed integer (little endian). Returns the
+ // read value and the number of bytes read.
template <typename ValidationTag>
- int64_t read_i33v(const byte* pc, uint32_t* length,
- Name<ValidationTag> name = "signed LEB33") {
- return read_leb<int64_t, ValidationTag, kNoTrace, 33>(pc, length, name);
- }
-
- // Convenient overload for callers who don't care about length.
- template <typename ValidationTag>
- WasmOpcode read_prefixed_opcode(const byte* pc) {
- uint32_t len;
- return read_prefixed_opcode<ValidationTag>(pc, &len);
+ std::pair<int64_t, uint32_t> read_i33v(
+ const byte* pc, Name<ValidationTag> name = "signed LEB33") {
+ return read_leb<int64_t, ValidationTag, kNoTrace, 33>(pc, name);
}
// Reads a prefixed-opcode, possibly with variable-length index.
- // `length` is set to the number of bytes that make up this opcode,
+ // Returns the read opcode and the number of bytes that make up this opcode,
// *including* the prefix byte. For most opcodes, it will be 2.
template <typename ValidationTag>
- WasmOpcode read_prefixed_opcode(
- const byte* pc, uint32_t* length,
- Name<ValidationTag> name = "prefixed opcode") {
- uint32_t index;
-
+ std::pair<WasmOpcode, uint32_t> read_prefixed_opcode(
+ const byte* pc, Name<ValidationTag> name = "prefixed opcode") {
// Prefixed opcodes all use LEB128 encoding.
- index = read_u32v<ValidationTag>(pc + 1, length, "prefixed opcode index");
- *length += 1; // Prefix byte.
+ auto [index, index_length] =
+ read_u32v<ValidationTag>(pc + 1, "prefixed opcode index");
+ uint32_t length = index_length + 1; // 1 for prefix byte.
// Only support opcodes that go up to 0xFFF (when decoded). Anything
// bigger will need more than 2 bytes, and the '<< 12' below will be wrong.
if (ValidationTag::validate && V8_UNLIKELY(index > 0xfff)) {
errorf(pc, "Invalid prefixed opcode %d", index);
- // If size validation fails.
- index = 0;
- *length = 0;
+ // On validation failure we return "unreachable" (opcode 0).
+ static_assert(kExprUnreachable == 0);
+ return {kExprUnreachable, 0};
}
- if (index > 0xff) return static_cast<WasmOpcode>((*pc) << 12 | index);
+ if (index > 0xff) {
+ return {static_cast<WasmOpcode>((*pc) << 12 | index), length};
+ }
- return static_cast<WasmOpcode>((*pc) << 8 | index);
+ return {static_cast<WasmOpcode>((*pc) << 8 | index), length};
}
// Reads a 8-bit unsigned integer (byte) and advances {pc_}.
uint8_t consume_u8(const char* name = "uint8_t") {
return consume_little_endian<uint8_t, kTrace>(name);
}
- template <class Tracer>
- uint8_t consume_u8(const char* name, Tracer& tracer) {
- tracer.Bytes(pc_, sizeof(uint8_t));
- tracer.Description(name);
+ uint8_t consume_u8(const char* name, ITracer* tracer) {
+ if (tracer) {
+ tracer->Bytes(pc_, sizeof(uint8_t));
+ tracer->Description(name);
+ }
return consume_little_endian<uint8_t, kNoTrace>(name);
}
@@ -202,58 +243,56 @@ class Decoder {
}
// Reads a single 32-bit unsigned integer (little endian) and advances {pc_}.
- template <class Tracer>
- uint32_t consume_u32(const char* name, Tracer& tracer) {
- tracer.Bytes(pc_, sizeof(uint32_t));
- tracer.Description(name);
+ uint32_t consume_u32(const char* name, ITracer* tracer) {
+ if (tracer) {
+ tracer->Bytes(pc_, sizeof(uint32_t));
+ tracer->Description(name);
+ }
return consume_little_endian<uint32_t, kNoTrace>(name);
}
// Reads a LEB128 variable-length unsigned 32-bit integer and advances {pc_}.
uint32_t consume_u32v(const char* name = "var_uint32") {
- uint32_t length = 0;
- uint32_t result =
- read_leb<uint32_t, FullValidationTag, kTrace>(pc_, &length, name);
+ auto [result, length] =
+ read_leb<uint32_t, FullValidationTag, kTrace>(pc_, name);
pc_ += length;
return result;
}
- template <class Tracer>
- uint32_t consume_u32v(const char* name, Tracer& tracer) {
- uint32_t length = 0;
- uint32_t result =
- read_leb<uint32_t, FullValidationTag, kNoTrace>(pc_, &length, name);
- tracer.Bytes(pc_, length);
- tracer.Description(name);
+ uint32_t consume_u32v(const char* name, ITracer* tracer) {
+ auto [result, length] =
+ read_leb<uint32_t, FullValidationTag, kNoTrace>(pc_, name);
+ if (tracer) {
+ tracer->Bytes(pc_, length);
+ tracer->Description(name);
+ }
pc_ += length;
return result;
}
// Reads a LEB128 variable-length signed 32-bit integer and advances {pc_}.
int32_t consume_i32v(const char* name = "var_int32") {
- uint32_t length = 0;
- int32_t result =
- read_leb<int32_t, FullValidationTag, kTrace>(pc_, &length, name);
+ auto [result, length] =
+ read_leb<int32_t, FullValidationTag, kTrace>(pc_, name);
pc_ += length;
return result;
}
// Reads a LEB128 variable-length unsigned 64-bit integer and advances {pc_}.
- template <class Tracer>
- uint64_t consume_u64v(const char* name, Tracer& tracer) {
- uint32_t length = 0;
- uint64_t result =
- read_leb<uint64_t, FullValidationTag, kNoTrace>(pc_, &length, name);
- tracer.Bytes(pc_, length);
- tracer.Description(name);
+ uint64_t consume_u64v(const char* name, ITracer* tracer) {
+ auto [result, length] =
+ read_leb<uint64_t, FullValidationTag, kNoTrace>(pc_, name);
+ if (tracer) {
+ tracer->Bytes(pc_, length);
+ tracer->Description(name);
+ }
pc_ += length;
return result;
}
// Reads a LEB128 variable-length signed 64-bit integer and advances {pc_}.
int64_t consume_i64v(const char* name = "var_int64") {
- uint32_t length = 0;
- int64_t result =
- read_leb<int64_t, FullValidationTag, kTrace>(pc_, &length, name);
+ auto [result, length] =
+ read_leb<int64_t, FullValidationTag, kTrace>(pc_, name);
pc_ += length;
return result;
}
@@ -268,10 +307,11 @@ class Decoder {
pc_ = end_;
}
}
- template <class Tracer>
- void consume_bytes(uint32_t size, const char* name, Tracer& tracer) {
- tracer.Bytes(pc_, size);
- tracer.Description(name);
+ void consume_bytes(uint32_t size, const char* name, ITracer* tracer) {
+ if (tracer) {
+ tracer->Bytes(pc_, size);
+ tracer->Description(name);
+ }
consume_bytes(size, nullptr);
}
@@ -292,7 +332,7 @@ class Decoder {
// Use this for "boolean validation", i.e. if the error message is not used
// anyway.
- void V8_NOINLINE MarkError() {
+ void V8_NOINLINE V8_PRESERVE_MOST MarkError() {
if (!ok()) return;
error_ = {0, "validation failed"};
onFirstError();
@@ -300,35 +340,34 @@ class Decoder {
// Do not inline error methods. This has measurable impact on validation time,
// see https://crbug.com/910432.
- void V8_NOINLINE error(const char* msg) { errorf(pc_offset(), "%s", msg); }
- void V8_NOINLINE error(const uint8_t* pc, const char* msg) {
+ void V8_NOINLINE V8_PRESERVE_MOST error(const char* msg) {
+ errorf(pc_offset(), "%s", msg);
+ }
+ void V8_NOINLINE V8_PRESERVE_MOST error(const uint8_t* pc, const char* msg) {
errorf(pc_offset(pc), "%s", msg);
}
- void V8_NOINLINE error(uint32_t offset, const char* msg) {
+ void V8_NOINLINE V8_PRESERVE_MOST error(uint32_t offset, const char* msg) {
errorf(offset, "%s", msg);
}
- void V8_NOINLINE PRINTF_FORMAT(2, 3) errorf(const char* format, ...) {
- va_list args;
- va_start(args, format);
- verrorf(pc_offset(), format, args);
- va_end(args);
+ template <typename... Args>
+ void V8_NOINLINE V8_PRESERVE_MOST errorf(const char* format, Args... args) {
+ errorf(pc_offset(), format, args...);
}
- void V8_NOINLINE PRINTF_FORMAT(3, 4)
- errorf(uint32_t offset, const char* format, ...) {
- va_list args;
- va_start(args, format);
- verrorf(offset, format, args);
- va_end(args);
+ template <typename... Args>
+ void V8_NOINLINE V8_PRESERVE_MOST errorf(const uint8_t* pc,
+ const char* format, Args... args) {
+ errorf(pc_offset(pc), format, args...);
}
- void V8_NOINLINE PRINTF_FORMAT(3, 4)
- errorf(const uint8_t* pc, const char* format, ...) {
- va_list args;
- va_start(args, format);
- verrorf(pc_offset(pc), format, args);
- va_end(args);
+ template <typename... Args>
+ void V8_NOINLINE V8_PRESERVE_MOST errorf(uint32_t offset, const char* format,
+ Args... args) {
+ static_assert(
+ sizeof...(Args) > 0,
+ "Use error instead of errorf if the format string has no placeholders");
+ verrorf(offset, format, args...);
}
// Behavior triggered on first error, overridden in subclasses.
@@ -371,8 +410,8 @@ class Decoder {
Reset(bytes.begin(), bytes.end(), buffer_offset);
}
- bool ok() const { return error_.empty(); }
- bool failed() const { return !ok(); }
+ bool ok() const { return !failed(); }
+ bool failed() const { return error_.has_error(); }
bool more() const { return pc_ < end_; }
const WasmError& error() const { return error_; }
@@ -413,12 +452,16 @@ class Decoder {
WasmError error_;
private:
- void verrorf(uint32_t offset, const char* format, va_list args) {
+ void V8_NOINLINE PRINTF_FORMAT(3, 4)
+ verrorf(uint32_t offset, const char* format, ...) {
// Only report the first error.
if (!ok()) return;
constexpr int kMaxErrorMsg = 256;
base::EmbeddedVector<char, kMaxErrorMsg> buffer;
+ va_list args;
+ va_start(args, format);
int len = base::VSNPrintF(buffer, format, args);
+ va_end(args);
CHECK_LT(0, len);
error_ = {offset, {buffer.begin(), static_cast<size_t>(len)}};
onFirstError();
@@ -457,18 +500,19 @@ class Decoder {
return val;
}
+ // The implementation of LEB-decoding; returns the value and the number of
+ // bytes read.
template <typename IntType, typename ValidationTag, TraceFlag trace,
size_t size_in_bits = 8 * sizeof(IntType)>
- V8_INLINE IntType read_leb(const byte* pc, uint32_t* length,
- Name<ValidationTag> name = "varint") {
+ V8_INLINE std::pair<IntType, uint32_t> read_leb(
+ const byte* pc, Name<ValidationTag> name = "varint") {
static_assert(size_in_bits <= 8 * sizeof(IntType),
"leb does not fit in type");
TRACE_IF(trace, " +%u %-20s: ", pc_offset(),
implicit_cast<const char*>(name));
// Fast path for single-byte integers.
- if ((!ValidationTag::validate || V8_LIKELY(pc < end_)) && !(*pc & 0x80)) {
+ if (V8_LIKELY((!ValidationTag::validate || pc < end_) && !(*pc & 0x80))) {
TRACE_IF(trace, "%02x ", *pc);
- *length = 1;
IntType result = *pc;
if (std::is_signed<IntType>::value) {
// Perform sign extension.
@@ -478,25 +522,29 @@ class Decoder {
} else {
TRACE_IF(trace, "= %" PRIu64 "\n", static_cast<uint64_t>(result));
}
- return result;
+ return {result, 1};
}
- return read_leb_slowpath<IntType, ValidationTag, trace, size_in_bits>(
- pc, length, name);
+ auto [result, length] =
+ read_leb_slowpath<IntType, ValidationTag, trace, size_in_bits>(pc,
+ name);
+ V8_ASSUME(length >= 0 && length <= (size_in_bits + 6) / 7);
+ V8_ASSUME(ValidationTag::validate || length >= 1);
+ return {result, length};
}
template <typename IntType, typename ValidationTag, TraceFlag trace,
size_t size_in_bits = 8 * sizeof(IntType)>
- V8_NOINLINE IntType read_leb_slowpath(const byte* pc, uint32_t* length,
- Name<ValidationTag> name) {
+ V8_NOINLINE V8_PRESERVE_MOST std::pair<IntType, uint32_t> read_leb_slowpath(
+ const byte* pc, Name<ValidationTag> name) {
// Create an unrolled LEB decoding function per integer type.
return read_leb_tail<IntType, ValidationTag, trace, size_in_bits, 0>(
- pc, length, name, 0);
+ pc, name, 0);
}
template <typename IntType, typename ValidationTag, TraceFlag trace,
size_t size_in_bits, int byte_index>
- V8_INLINE IntType read_leb_tail(const byte* pc, uint32_t* length,
- Name<ValidationTag> name, IntType result) {
+ V8_INLINE std::pair<IntType, uint32_t> read_leb_tail(
+ const byte* pc, Name<ValidationTag> name, IntType intermediate_result) {
constexpr bool is_signed = std::is_signed<IntType>::value;
constexpr int kMaxLength = (size_in_bits + 6) / 7;
static_assert(byte_index < kMaxLength, "invalid template instantiation");
@@ -509,8 +557,8 @@ class Decoder {
b = *pc;
TRACE_IF(trace, "%02x ", b);
using Unsigned = typename std::make_unsigned<IntType>::type;
- result = result |
- (static_cast<Unsigned>(static_cast<IntType>(b) & 0x7f) << shift);
+ intermediate_result |=
+ (static_cast<Unsigned>(static_cast<IntType>(b) & 0x7f) << shift);
}
if (!is_last_byte && (b & 0x80)) {
// Make sure that we only instantiate the template for valid byte indexes.
@@ -518,9 +566,8 @@ class Decoder {
// following call is unreachable if is_last_byte is false.
constexpr int next_byte_index = byte_index + (is_last_byte ? 0 : 1);
return read_leb_tail<IntType, ValidationTag, trace, size_in_bits,
- next_byte_index>(pc + 1, length, name, result);
+ next_byte_index>(pc + 1, name, intermediate_result);
}
- *length = byte_index + (at_end ? 0 : 1);
if (ValidationTag::validate && V8_UNLIKELY(at_end || (b & 0x80))) {
TRACE_IF(trace, at_end ? "<end> " : "<length overflow> ");
if constexpr (ValidationTag::full_validation) {
@@ -528,8 +575,7 @@ class Decoder {
} else {
MarkError();
}
- result = 0;
- *length = 0;
+ return {0, 0};
}
if constexpr (is_last_byte) {
// A signed-LEB128 must sign-extend the final byte, excluding its
@@ -553,20 +599,21 @@ class Decoder {
} else {
MarkError();
}
- result = 0;
- *length = 0;
+ return {0, 0};
}
}
constexpr int sign_ext_shift =
is_signed ? std::max(0, int{8 * sizeof(IntType)} - shift - 7) : 0;
// Perform sign extension.
- result = (result << sign_ext_shift) >> sign_ext_shift;
+ intermediate_result =
+ (intermediate_result << sign_ext_shift) >> sign_ext_shift;
if (trace && is_signed) {
- TRACE("= %" PRIi64 "\n", static_cast<int64_t>(result));
+ TRACE("= %" PRIi64 "\n", static_cast<int64_t>(intermediate_result));
} else if (trace) {
- TRACE("= %" PRIu64 "\n", static_cast<uint64_t>(result));
+ TRACE("= %" PRIu64 "\n", static_cast<uint64_t>(intermediate_result));
}
- return result;
+ const uint32_t length = byte_index + 1;
+ return {intermediate_result, length};
}
};