blob: 698919d6a024eea189aa6efbf0a660cd14d716b6 [file] [log] [blame]
// Copyright 2015 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_DECODER_H_
#define V8_WASM_DECODER_H_
#include "src/base/smart-pointers.h"
#include "src/flags.h"
#include "src/signature.h"
#include "src/wasm/wasm-result.h"
#include "src/zone-containers.h"
namespace v8 {
namespace internal {
namespace wasm {
#if DEBUG
#define TRACE(...) \
do { \
if (FLAG_trace_wasm_decoder) PrintF(__VA_ARGS__); \
} while (false)
#else
#define TRACE(...)
#endif
// A helper utility to decode bytes, integers, fields, varints, etc, from
// a buffer of bytes.
class Decoder {
public:
Decoder(const byte* start, const byte* end)
: start_(start),
pc_(start),
limit_(end),
error_pc_(nullptr),
error_pt_(nullptr) {}
virtual ~Decoder() {}
// Reads a 8-bit unsigned integer (byte) and advances {pc_}.
uint8_t u8(const char* name = nullptr) {
TRACE(" +%d %-20s: ", static_cast<int>(pc_ - start_),
name ? name : "uint8_t");
if (checkAvailable(1)) {
byte val = *(pc_++);
TRACE("%02x = %d\n", val, val);
return val;
} else {
error("expected 1 byte, but fell off end");
return traceOffEnd<uint8_t>();
}
}
// Reads a 16-bit unsigned integer (little endian) and advances {pc_}.
uint16_t u16(const char* name = nullptr) {
TRACE(" +%d %-20s: ", static_cast<int>(pc_ - start_),
name ? name : "uint16_t");
if (checkAvailable(2)) {
#ifdef V8_TARGET_LITTLE_ENDIAN
byte b0 = pc_[0];
byte b1 = pc_[1];
#else
byte b1 = pc_[0];
byte b0 = pc_[1];
#endif
uint16_t val = static_cast<uint16_t>(b1 << 8) | b0;
TRACE("%02x %02x = %d\n", pc_[0], pc_[1], val);
pc_ += 2;
return val;
} else {
error("expected 2 bytes, but fell off end");
return traceOffEnd<uint16_t>();
}
}
// Reads a single 32-bit unsigned integer (little endian) and advances {pc_}.
uint32_t u32(const char* name = nullptr) {
TRACE(" +%d %-20s: ", static_cast<int>(pc_ - start_),
name ? name : "uint32_t");
if (checkAvailable(4)) {
#ifdef V8_TARGET_LITTLE_ENDIAN
byte b0 = pc_[0];
byte b1 = pc_[1];
byte b2 = pc_[2];
byte b3 = pc_[3];
#else
byte b3 = pc_[0];
byte b2 = pc_[1];
byte b1 = pc_[2];
byte b0 = pc_[3];
#endif
uint32_t val = static_cast<uint32_t>(b3 << 24) |
static_cast<uint32_t>(b2 << 16) |
static_cast<uint32_t>(b1 << 8) | b0;
TRACE("%02x %02x %02x %02x = %u\n", pc_[0], pc_[1], pc_[2], pc_[3], val);
pc_ += 4;
return val;
} else {
error("expected 4 bytes, but fell off end");
return traceOffEnd<uint32_t>();
}
}
// Reads a LEB128 variable-length 32-bit integer and advances {pc_}.
uint32_t u32v(int* length, const char* name = nullptr) {
TRACE(" +%d %-20s: ", static_cast<int>(pc_ - start_),
name ? name : "varint");
if (!checkAvailable(1)) {
error("expected at least 1 byte, but fell off end");
return traceOffEnd<uint32_t>();
}
const byte* pos = pc_;
const byte* end = pc_ + 5;
if (end > limit_) end = limit_;
uint32_t result = 0;
int shift = 0;
byte b = 0;
while (pc_ < end) {
b = *pc_++;
TRACE("%02x ", b);
result = result | ((b & 0x7F) << shift);
if ((b & 0x80) == 0) break;
shift += 7;
}
*length = static_cast<int>(pc_ - pos);
if (pc_ == end && (b & 0x80)) {
error(pc_ - 1, "varint too large");
} else {
TRACE("= %u\n", result);
}
return result;
}
// Check that at least {size} bytes exist between {pc_} and {limit_}.
bool checkAvailable(int size) {
if (pc_ < start_ || (pc_ + size) > limit_) {
error(pc_, nullptr, "expected %d bytes, fell off end", size);
return false;
} else {
return true;
}
}
void error(const char* msg) { error(pc_, nullptr, msg); }
void error(const byte* pc, const char* msg) { error(pc, nullptr, msg); }
// Sets internal error state.
void error(const byte* pc, const byte* pt, const char* format, ...) {
if (ok()) {
#if DEBUG
if (FLAG_wasm_break_on_decoder_error) {
base::OS::DebugBreak();
}
#endif
const int kMaxErrorMsg = 256;
char* buffer = new char[kMaxErrorMsg];
va_list arguments;
va_start(arguments, format);
base::OS::VSNPrintF(buffer, kMaxErrorMsg - 1, format, arguments);
va_end(arguments);
error_msg_.Reset(buffer);
error_pc_ = pc;
error_pt_ = pt;
onFirstError();
}
}
// Behavior triggered on first error, overridden in subclasses.
virtual void onFirstError() {}
// Debugging helper to print bytes up to the end.
template <typename T>
T traceOffEnd() {
T t = 0;
for (const byte* ptr = pc_; ptr < limit_; ptr++) {
TRACE("%02x ", *ptr);
}
TRACE("<end>\n");
pc_ = limit_;
return t;
}
// Converts the given value to a {Result}, copying the error if necessary.
template <typename T>
Result<T> toResult(T val) {
Result<T> result;
if (error_pc_) {
result.error_code = kError;
result.start = start_;
result.error_pc = error_pc_;
result.error_pt = error_pt_;
result.error_msg = error_msg_;
error_msg_.Reset(nullptr);
} else {
result.error_code = kSuccess;
}
result.val = val;
return result;
}
// Resets the boundaries of this decoder.
void Reset(const byte* start, const byte* end) {
start_ = start;
pc_ = start;
limit_ = end;
error_pc_ = nullptr;
error_pt_ = nullptr;
error_msg_.Reset(nullptr);
}
bool ok() const { return error_pc_ == nullptr; }
bool failed() const { return error_pc_ != nullptr; }
protected:
const byte* start_;
const byte* pc_;
const byte* limit_;
const byte* error_pc_;
const byte* error_pt_;
base::SmartArrayPointer<char> error_msg_;
};
#undef TRACE
} // namespace wasm
} // namespace internal
} // namespace v8
#endif // V8_WASM_DECODER_H_