WebAssembly: ModuleInformation should be a ref counted thing that can be shared acros...
[WebKit-https.git] / Source / JavaScriptCore / wasm / WasmParser.h
1 /*
2  * Copyright (C) 2016 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #pragma once
27
28 #if ENABLE(WEBASSEMBLY)
29
30 #include "B3Compilation.h"
31 #include "B3Procedure.h"
32 #include "WasmFormat.h"
33 #include "WasmModuleInformation.h"
34 #include "WasmOps.h"
35 #include "WasmSections.h"
36 #include <type_traits>
37 #include <wtf/Expected.h>
38 #include <wtf/LEBDecoder.h>
39 #include <wtf/StdLibExtras.h>
40 #include <wtf/text/WTFString.h>
41 #include <wtf/unicode/UTF8.h>
42
43 namespace JSC { namespace Wasm {
44
45 namespace FailureHelper {
46 // FIXME We should move this to makeString. It's in its own namespace to enable C++ Argument Dependent Lookup à la std::swap: user code can deblare its own "boxFailure" and the fail() helper will find it.
47 static inline auto makeString(const char *failure) { return ASCIILiteral(failure); }
48 template <typename Int, typename = typename std::enable_if<std::is_integral<Int>::value>::type>
49 static inline auto makeString(Int failure) { return String::number(failure); }
50 }
51
52 template<typename SuccessType>
53 class Parser {
54 public:
55     typedef String ErrorType;
56     typedef UnexpectedType<ErrorType> UnexpectedResult;
57     typedef Expected<void, ErrorType> PartialResult;
58     typedef Expected<SuccessType, ErrorType> Result;
59
60 protected:
61     Parser(const uint8_t*, size_t);
62
63     bool WARN_UNUSED_RETURN consumeCharacter(char);
64     bool WARN_UNUSED_RETURN consumeString(const char*);
65     bool WARN_UNUSED_RETURN consumeUTF8String(Vector<LChar>&, size_t);
66
67     bool WARN_UNUSED_RETURN parseVarUInt1(uint8_t&);
68     bool WARN_UNUSED_RETURN parseInt7(int8_t&);
69     bool WARN_UNUSED_RETURN parseUInt7(uint8_t&);
70     bool WARN_UNUSED_RETURN parseUInt8(uint8_t&);
71     bool WARN_UNUSED_RETURN parseUInt32(uint32_t&);
72     bool WARN_UNUSED_RETURN parseUInt64(uint64_t&);
73     bool WARN_UNUSED_RETURN parseVarUInt32(uint32_t&);
74     bool WARN_UNUSED_RETURN parseVarUInt64(uint64_t&);
75
76     bool WARN_UNUSED_RETURN parseVarInt32(int32_t&);
77     bool WARN_UNUSED_RETURN parseVarInt64(int64_t&);
78
79     bool WARN_UNUSED_RETURN parseResultType(Type&);
80     bool WARN_UNUSED_RETURN parseValueType(Type&);
81     bool WARN_UNUSED_RETURN parseExternalKind(ExternalKind&);
82
83     const uint8_t* source() const { return m_source; }
84     size_t length() const { return m_sourceLength; }
85
86     size_t m_offset = 0;
87
88     template <typename ...Args>
89     NEVER_INLINE UnexpectedResult WARN_UNUSED_RETURN fail(Args... args) const
90     {
91         using namespace FailureHelper; // See ADL comment in namespace above.
92         return UnexpectedResult(makeString(ASCIILiteral("WebAssembly.Module doesn't parse at byte "), String::number(m_offset), ASCIILiteral(" / "), String::number(m_sourceLength), ASCIILiteral(": "), makeString(args)...));
93     }
94 #define WASM_PARSER_FAIL_IF(condition, ...) do { \
95     if (UNLIKELY(condition))                     \
96         return fail(__VA_ARGS__);                \
97     } while (0)
98
99 #define WASM_FAIL_IF_HELPER_FAILS(helper) do {   \
100         auto helperResult = helper;              \
101         if (UNLIKELY(!helperResult))             \
102             return helperResult.getUnexpected(); \
103     } while (0)
104
105 private:
106     const uint8_t* m_source;
107     size_t m_sourceLength;
108 };
109
110 template<typename SuccessType>
111 ALWAYS_INLINE Parser<SuccessType>::Parser(const uint8_t* sourceBuffer, size_t sourceLength)
112     : m_source(sourceBuffer)
113     , m_sourceLength(sourceLength)
114 {
115 }
116
117 template<typename SuccessType>
118 ALWAYS_INLINE bool Parser<SuccessType>::consumeCharacter(char c)
119 {
120     if (m_offset >= length())
121         return false;
122     if (c == source()[m_offset]) {
123         m_offset++;
124         return true;
125     }
126     return false;
127 }
128
129 template<typename SuccessType>
130 ALWAYS_INLINE bool Parser<SuccessType>::consumeString(const char* str)
131 {
132     unsigned start = m_offset;
133     if (m_offset >= length())
134         return false;
135     for (size_t i = 0; str[i]; i++) {
136         if (!consumeCharacter(str[i])) {
137             m_offset = start;
138             return false;
139         }
140     }
141     return true;
142 }
143
144 template<typename SuccessType>
145 ALWAYS_INLINE bool Parser<SuccessType>::consumeUTF8String(Vector<LChar>& result, size_t stringLength)
146 {
147     if (length() < stringLength || m_offset > length() - stringLength)
148         return false;
149     if (!result.tryReserveCapacity(stringLength))
150         return false;
151
152     const uint8_t* stringStart = source() + m_offset;
153
154     // We don't cache the UTF-16 characters since it seems likely the string is ascii.
155     if (UNLIKELY(!charactersAreAllASCII(stringStart, stringLength))) {
156         Vector<UChar, 1024> buffer(stringLength);
157         UChar* bufferStart = buffer.data();
158
159         UChar* bufferCurrent = bufferStart;
160         const char* stringCurrent = reinterpret_cast<const char*>(stringStart);
161         if (WTF::Unicode::convertUTF8ToUTF16(&stringCurrent, reinterpret_cast<const char *>(stringStart + stringLength), &bufferCurrent, bufferCurrent + buffer.size()) != WTF::Unicode::conversionOK)
162             return false;
163     }
164
165     result.grow(stringLength);
166     memcpy(result.data(), stringStart, stringLength);
167     m_offset += stringLength;
168     return true;
169 }
170
171 template<typename SuccessType>
172 ALWAYS_INLINE bool Parser<SuccessType>::parseVarUInt32(uint32_t& result)
173 {
174     return WTF::LEBDecoder::decodeUInt32(m_source, m_sourceLength, m_offset, result);
175 }
176
177 template<typename SuccessType>
178 ALWAYS_INLINE bool Parser<SuccessType>::parseVarUInt64(uint64_t& result)
179 {
180     return WTF::LEBDecoder::decodeUInt64(m_source, m_sourceLength, m_offset, result);
181 }
182
183 template<typename SuccessType>
184 ALWAYS_INLINE bool Parser<SuccessType>::parseVarInt32(int32_t& result)
185 {
186     return WTF::LEBDecoder::decodeInt32(m_source, m_sourceLength, m_offset, result);
187 }
188
189 template<typename SuccessType>
190 ALWAYS_INLINE bool Parser<SuccessType>::parseVarInt64(int64_t& result)
191 {
192     return WTF::LEBDecoder::decodeInt64(m_source, m_sourceLength, m_offset, result);
193 }
194
195 template<typename SuccessType>
196 ALWAYS_INLINE bool Parser<SuccessType>::parseUInt32(uint32_t& result)
197 {
198     if (length() < 4 || m_offset > length() - 4)
199         return false;
200     result = *reinterpret_cast<const uint32_t*>(source() + m_offset);
201     m_offset += 4;
202     return true;
203 }
204
205 template<typename SuccessType>
206 ALWAYS_INLINE bool Parser<SuccessType>::parseUInt64(uint64_t& result)
207 {
208     if (length() < 8 || m_offset > length() - 8)
209         return false;
210     result = *reinterpret_cast<const uint64_t*>(source() + m_offset);
211     m_offset += 8;
212     return true;
213 }
214
215 template<typename SuccessType>
216 ALWAYS_INLINE bool Parser<SuccessType>::parseUInt8(uint8_t& result)
217 {
218     if (m_offset >= length())
219         return false;
220     result = source()[m_offset++];
221     return true;
222 }
223
224 template<typename SuccessType>
225 ALWAYS_INLINE bool Parser<SuccessType>::parseInt7(int8_t& result)
226 {
227     if (m_offset >= length())
228         return false;
229     uint8_t v = source()[m_offset++];
230     result = (v & 0x40) ? WTF::bitwise_cast<int8_t>(uint8_t(v | 0x80)) : v;
231     return (v & 0x80) == 0;
232 }
233
234 template<typename SuccessType>
235 ALWAYS_INLINE bool Parser<SuccessType>::parseUInt7(uint8_t& result)
236 {
237     if (m_offset >= length())
238         return false;
239     result = source()[m_offset++];
240     return result < 0x80;
241 }
242
243 template<typename SuccessType>
244 ALWAYS_INLINE bool Parser<SuccessType>::parseVarUInt1(uint8_t& result)
245 {
246     uint32_t temp;
247     if (!parseVarUInt32(temp))
248         return false;
249     if (temp > 1)
250         return false;
251     result = static_cast<uint8_t>(temp);
252     return true;
253 }
254
255 template<typename SuccessType>
256 ALWAYS_INLINE bool Parser<SuccessType>::parseResultType(Type& result)
257 {
258     int8_t value;
259     if (!parseInt7(value))
260         return false;
261     if (!isValidType(value))
262         return false;
263     result = static_cast<Type>(value);
264     return true;
265 }
266
267 template<typename SuccessType>
268 ALWAYS_INLINE bool Parser<SuccessType>::parseValueType(Type& result)
269 {
270     return parseResultType(result) && isValueType(result);
271 }
272
273 template<typename SuccessType>
274 ALWAYS_INLINE bool Parser<SuccessType>::parseExternalKind(ExternalKind& result)
275 {
276     uint8_t value;
277     if (!parseUInt7(value))
278         return false;
279     if (!isValidExternalKind(value))
280         return false;
281     result = static_cast<ExternalKind>(value);
282     return true;
283 }
284
285 } } // namespace JSC::Wasm
286
287 #endif // ENABLE(WEBASSEMBLY)