• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2015 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef V8_WASM_DECODER_H_
6 #define V8_WASM_DECODER_H_
7 
8 #include "src/base/smart-pointers.h"
9 #include "src/flags.h"
10 #include "src/signature.h"
11 #include "src/wasm/wasm-result.h"
12 #include "src/zone-containers.h"
13 
14 namespace v8 {
15 namespace internal {
16 namespace wasm {
17 
18 #if DEBUG
19 #define TRACE(...)                                    \
20   do {                                                \
21     if (FLAG_trace_wasm_decoder) PrintF(__VA_ARGS__); \
22   } while (false)
23 #else
24 #define TRACE(...)
25 #endif
26 
27 // A helper utility to decode bytes, integers, fields, varints, etc, from
28 // a buffer of bytes.
29 class Decoder {
30  public:
Decoder(const byte * start,const byte * end)31   Decoder(const byte* start, const byte* end)
32       : start_(start),
33         pc_(start),
34         limit_(end),
35         error_pc_(nullptr),
36         error_pt_(nullptr) {}
37 
~Decoder()38   virtual ~Decoder() {}
39 
40   // Reads a 8-bit unsigned integer (byte) and advances {pc_}.
41   uint8_t u8(const char* name = nullptr) {
42     TRACE("  +%d  %-20s: ", static_cast<int>(pc_ - start_),
43           name ? name : "uint8_t");
44     if (checkAvailable(1)) {
45       byte val = *(pc_++);
46       TRACE("%02x = %d\n", val, val);
47       return val;
48     } else {
49       error("expected 1 byte, but fell off end");
50       return traceOffEnd<uint8_t>();
51     }
52   }
53 
54   // Reads a 16-bit unsigned integer (little endian) and advances {pc_}.
55   uint16_t u16(const char* name = nullptr) {
56     TRACE("  +%d  %-20s: ", static_cast<int>(pc_ - start_),
57           name ? name : "uint16_t");
58     if (checkAvailable(2)) {
59 #ifdef V8_TARGET_LITTLE_ENDIAN
60       byte b0 = pc_[0];
61       byte b1 = pc_[1];
62 #else
63       byte b1 = pc_[0];
64       byte b0 = pc_[1];
65 #endif
66       uint16_t val = static_cast<uint16_t>(b1 << 8) | b0;
67       TRACE("%02x %02x = %d\n", pc_[0], pc_[1], val);
68       pc_ += 2;
69       return val;
70     } else {
71       error("expected 2 bytes, but fell off end");
72       return traceOffEnd<uint16_t>();
73     }
74   }
75 
76   // Reads a single 32-bit unsigned integer (little endian) and advances {pc_}.
77   uint32_t u32(const char* name = nullptr) {
78     TRACE("  +%d  %-20s: ", static_cast<int>(pc_ - start_),
79           name ? name : "uint32_t");
80     if (checkAvailable(4)) {
81 #ifdef V8_TARGET_LITTLE_ENDIAN
82       byte b0 = pc_[0];
83       byte b1 = pc_[1];
84       byte b2 = pc_[2];
85       byte b3 = pc_[3];
86 #else
87       byte b3 = pc_[0];
88       byte b2 = pc_[1];
89       byte b1 = pc_[2];
90       byte b0 = pc_[3];
91 #endif
92       uint32_t val = static_cast<uint32_t>(b3 << 24) |
93                      static_cast<uint32_t>(b2 << 16) |
94                      static_cast<uint32_t>(b1 << 8) | b0;
95       TRACE("%02x %02x %02x %02x = %u\n", pc_[0], pc_[1], pc_[2], pc_[3], val);
96       pc_ += 4;
97       return val;
98     } else {
99       error("expected 4 bytes, but fell off end");
100       return traceOffEnd<uint32_t>();
101     }
102   }
103 
104   // Reads a LEB128 variable-length 32-bit integer and advances {pc_}.
105   uint32_t u32v(int* length, const char* name = nullptr) {
106     TRACE("  +%d  %-20s: ", static_cast<int>(pc_ - start_),
107           name ? name : "varint");
108 
109     if (!checkAvailable(1)) {
110       error("expected at least 1 byte, but fell off end");
111       return traceOffEnd<uint32_t>();
112     }
113 
114     const byte* pos = pc_;
115     const byte* end = pc_ + 5;
116     if (end > limit_) end = limit_;
117 
118     uint32_t result = 0;
119     int shift = 0;
120     byte b = 0;
121     while (pc_ < end) {
122       b = *pc_++;
123       TRACE("%02x ", b);
124       result = result | ((b & 0x7F) << shift);
125       if ((b & 0x80) == 0) break;
126       shift += 7;
127     }
128 
129     *length = static_cast<int>(pc_ - pos);
130     if (pc_ == end && (b & 0x80)) {
131       error(pc_ - 1, "varint too large");
132     } else {
133       TRACE("= %u\n", result);
134     }
135     return result;
136   }
137 
138   // Check that at least {size} bytes exist between {pc_} and {limit_}.
checkAvailable(int size)139   bool checkAvailable(int size) {
140     if (pc_ < start_ || (pc_ + size) > limit_) {
141       error(pc_, nullptr, "expected %d bytes, fell off end", size);
142       return false;
143     } else {
144       return true;
145     }
146   }
147 
error(const char * msg)148   void error(const char* msg) { error(pc_, nullptr, msg); }
149 
error(const byte * pc,const char * msg)150   void error(const byte* pc, const char* msg) { error(pc, nullptr, msg); }
151 
152   // Sets internal error state.
error(const byte * pc,const byte * pt,const char * format,...)153   void error(const byte* pc, const byte* pt, const char* format, ...) {
154     if (ok()) {
155 #if DEBUG
156       if (FLAG_wasm_break_on_decoder_error) {
157         base::OS::DebugBreak();
158       }
159 #endif
160       const int kMaxErrorMsg = 256;
161       char* buffer = new char[kMaxErrorMsg];
162       va_list arguments;
163       va_start(arguments, format);
164       base::OS::VSNPrintF(buffer, kMaxErrorMsg - 1, format, arguments);
165       va_end(arguments);
166       error_msg_.Reset(buffer);
167       error_pc_ = pc;
168       error_pt_ = pt;
169       onFirstError();
170     }
171   }
172 
173   // Behavior triggered on first error, overridden in subclasses.
onFirstError()174   virtual void onFirstError() {}
175 
176   // Debugging helper to print bytes up to the end.
177   template <typename T>
traceOffEnd()178   T traceOffEnd() {
179     T t = 0;
180     for (const byte* ptr = pc_; ptr < limit_; ptr++) {
181       TRACE("%02x ", *ptr);
182     }
183     TRACE("<end>\n");
184     pc_ = limit_;
185     return t;
186   }
187 
188   // Converts the given value to a {Result}, copying the error if necessary.
189   template <typename T>
toResult(T val)190   Result<T> toResult(T val) {
191     Result<T> result;
192     if (error_pc_) {
193       result.error_code = kError;
194       result.start = start_;
195       result.error_pc = error_pc_;
196       result.error_pt = error_pt_;
197       result.error_msg = error_msg_;
198       error_msg_.Reset(nullptr);
199     } else {
200       result.error_code = kSuccess;
201     }
202     result.val = val;
203     return result;
204   }
205 
206   // Resets the boundaries of this decoder.
Reset(const byte * start,const byte * end)207   void Reset(const byte* start, const byte* end) {
208     start_ = start;
209     pc_ = start;
210     limit_ = end;
211     error_pc_ = nullptr;
212     error_pt_ = nullptr;
213     error_msg_.Reset(nullptr);
214   }
215 
ok()216   bool ok() const { return error_pc_ == nullptr; }
failed()217   bool failed() const { return error_pc_ != nullptr; }
218 
219  protected:
220   const byte* start_;
221   const byte* pc_;
222   const byte* limit_;
223   const byte* error_pc_;
224   const byte* error_pt_;
225   base::SmartArrayPointer<char> error_msg_;
226 };
227 
228 #undef TRACE
229 }  // namespace wasm
230 }  // namespace internal
231 }  // namespace v8
232 
233 #endif  // V8_WASM_DECODER_H_
234