1 | /* |
2 | * Copyright (C) 2016-2017 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 "WasmParser.h" |
31 | #include "WasmSignatureInlines.h" |
32 | #include <wtf/DataLog.h> |
33 | |
34 | namespace JSC { namespace Wasm { |
35 | |
36 | enum class BlockType { |
37 | If, |
38 | Block, |
39 | Loop, |
40 | TopLevel |
41 | }; |
42 | |
43 | template<typename Context> |
44 | class FunctionParser : public Parser<void> { |
45 | public: |
46 | typedef typename Context::ExpressionType ExpressionType; |
47 | typedef typename Context::ControlType ControlType; |
48 | typedef typename Context::ExpressionList ExpressionList; |
49 | |
50 | FunctionParser(Context&, const uint8_t* functionStart, size_t functionLength, const Signature&, const ModuleInformation&); |
51 | |
52 | Result WARN_UNUSED_RETURN parse(); |
53 | |
54 | struct ControlEntry { |
55 | ExpressionList enclosedExpressionStack; |
56 | ControlType controlData; |
57 | }; |
58 | |
59 | OpType currentOpcode() const { return m_currentOpcode; } |
60 | size_t currentOpcodeStartingOffset() const { return m_currentOpcodeStartingOffset; } |
61 | |
62 | private: |
63 | static const bool verbose = false; |
64 | |
65 | PartialResult WARN_UNUSED_RETURN parseBody(); |
66 | PartialResult WARN_UNUSED_RETURN parseExpression(); |
67 | PartialResult WARN_UNUSED_RETURN parseUnreachableExpression(); |
68 | PartialResult WARN_UNUSED_RETURN unifyControl(Vector<ExpressionType>&, unsigned level); |
69 | |
70 | #define WASM_TRY_POP_EXPRESSION_STACK_INTO(result, what) do { \ |
71 | WASM_PARSER_FAIL_IF(m_expressionStack.isEmpty(), "can't pop empty stack in " what); \ |
72 | result = m_expressionStack.takeLast(); \ |
73 | m_toKillAfterExpression.append(result); \ |
74 | } while (0) |
75 | |
76 | template<OpType> |
77 | PartialResult WARN_UNUSED_RETURN unaryCase(); |
78 | |
79 | template<OpType> |
80 | PartialResult WARN_UNUSED_RETURN binaryCase(); |
81 | |
82 | #define WASM_TRY_ADD_TO_CONTEXT(add_expression) WASM_FAIL_IF_HELPER_FAILS(m_context.add_expression) |
83 | |
84 | // FIXME add a macro as above for WASM_TRY_APPEND_TO_CONTROL_STACK https://bugs.webkit.org/show_bug.cgi?id=165862 |
85 | |
86 | Context& m_context; |
87 | ExpressionList m_expressionStack; |
88 | Vector<ControlEntry> m_controlStack; |
89 | const Signature& m_signature; |
90 | const ModuleInformation& m_info; |
91 | |
92 | OpType m_currentOpcode; |
93 | size_t m_currentOpcodeStartingOffset { 0 }; |
94 | |
95 | Vector<ExpressionType, 8> m_toKillAfterExpression; |
96 | |
97 | unsigned m_unreachableBlocks { 0 }; |
98 | }; |
99 | |
100 | template<typename Context> |
101 | FunctionParser<Context>::FunctionParser(Context& context, const uint8_t* functionStart, size_t functionLength, const Signature& signature, const ModuleInformation& info) |
102 | : Parser(functionStart, functionLength) |
103 | , m_context(context) |
104 | , m_signature(signature) |
105 | , m_info(info) |
106 | { |
107 | if (verbose) |
108 | dataLogLn("Parsing function starting at: " , (uintptr_t)functionStart, " of length: " , functionLength, " with signature: " , signature); |
109 | m_context.setParser(this); |
110 | } |
111 | |
112 | template<typename Context> |
113 | auto FunctionParser<Context>::parse() -> Result |
114 | { |
115 | uint32_t localCount; |
116 | |
117 | WASM_PARSER_FAIL_IF(!m_context.addArguments(m_signature), "can't add " , m_signature.argumentCount(), " arguments to Function" ); |
118 | WASM_PARSER_FAIL_IF(!parseVarUInt32(localCount), "can't get local count" ); |
119 | WASM_PARSER_FAIL_IF(localCount > maxFunctionLocals, "Function section's local count is too big " , localCount, " maximum " , maxFunctionLocals); |
120 | |
121 | for (uint32_t i = 0; i < localCount; ++i) { |
122 | uint32_t numberOfLocals; |
123 | Type typeOfLocal; |
124 | |
125 | WASM_PARSER_FAIL_IF(!parseVarUInt32(numberOfLocals), "can't get Function's number of locals in group " , i); |
126 | WASM_PARSER_FAIL_IF(numberOfLocals > maxFunctionLocals, "Function section's " , i, "th local group count is too big " , numberOfLocals, " maximum " , maxFunctionLocals); |
127 | WASM_PARSER_FAIL_IF(!parseValueType(typeOfLocal), "can't get Function local's type in group " , i); |
128 | WASM_TRY_ADD_TO_CONTEXT(addLocal(typeOfLocal, numberOfLocals)); |
129 | } |
130 | |
131 | WASM_FAIL_IF_HELPER_FAILS(parseBody()); |
132 | |
133 | return { }; |
134 | } |
135 | |
136 | template<typename Context> |
137 | auto FunctionParser<Context>::parseBody() -> PartialResult |
138 | { |
139 | m_controlStack.append({ ExpressionList(), m_context.addTopLevel(m_signature.returnType()) }); |
140 | uint8_t op; |
141 | while (m_controlStack.size()) { |
142 | ASSERT(m_toKillAfterExpression.isEmpty()); |
143 | |
144 | m_currentOpcodeStartingOffset = m_offset; |
145 | WASM_PARSER_FAIL_IF(!parseUInt8(op), "can't decode opcode" ); |
146 | WASM_PARSER_FAIL_IF(!isValidOpType(op), "invalid opcode " , op); |
147 | |
148 | m_currentOpcode = static_cast<OpType>(op); |
149 | |
150 | if (verbose) { |
151 | dataLogLn("processing op (" , m_unreachableBlocks, "): " , RawPointer(reinterpret_cast<void*>(op)), ", " , makeString(static_cast<OpType>(op)), " at offset: " , RawPointer(reinterpret_cast<void*>(m_offset))); |
152 | m_context.dump(m_controlStack, &m_expressionStack); |
153 | } |
154 | |
155 | if (m_unreachableBlocks) |
156 | WASM_FAIL_IF_HELPER_FAILS(parseUnreachableExpression()); |
157 | else { |
158 | WASM_FAIL_IF_HELPER_FAILS(parseExpression()); |
159 | while (m_toKillAfterExpression.size()) |
160 | m_context.didKill(m_toKillAfterExpression.takeLast()); |
161 | } |
162 | } |
163 | |
164 | ASSERT(op == OpType::End); |
165 | return { }; |
166 | } |
167 | |
168 | template<typename Context> |
169 | template<OpType op> |
170 | auto FunctionParser<Context>::binaryCase() -> PartialResult |
171 | { |
172 | ExpressionType right; |
173 | ExpressionType left; |
174 | ExpressionType result; |
175 | |
176 | WASM_TRY_POP_EXPRESSION_STACK_INTO(right, "binary right" ); |
177 | WASM_TRY_POP_EXPRESSION_STACK_INTO(left, "binary left" ); |
178 | WASM_TRY_ADD_TO_CONTEXT(template addOp<op>(left, right, result)); |
179 | |
180 | m_expressionStack.append(result); |
181 | return { }; |
182 | } |
183 | |
184 | template<typename Context> |
185 | template<OpType op> |
186 | auto FunctionParser<Context>::unaryCase() -> PartialResult |
187 | { |
188 | ExpressionType value; |
189 | ExpressionType result; |
190 | |
191 | WASM_TRY_POP_EXPRESSION_STACK_INTO(value, "unary" ); |
192 | WASM_TRY_ADD_TO_CONTEXT(template addOp<op>(value, result)); |
193 | |
194 | m_expressionStack.append(result); |
195 | return { }; |
196 | } |
197 | |
198 | template<typename Context> |
199 | auto FunctionParser<Context>::parseExpression() -> PartialResult |
200 | { |
201 | switch (m_currentOpcode) { |
202 | #define CREATE_CASE(name, id, b3op, inc) case OpType::name: return binaryCase<OpType::name>(); |
203 | FOR_EACH_WASM_BINARY_OP(CREATE_CASE) |
204 | #undef CREATE_CASE |
205 | |
206 | #define CREATE_CASE(name, id, b3op, inc) case OpType::name: return unaryCase<OpType::name>(); |
207 | FOR_EACH_WASM_UNARY_OP(CREATE_CASE) |
208 | #undef CREATE_CASE |
209 | |
210 | case Select: { |
211 | ExpressionType condition; |
212 | ExpressionType zero; |
213 | ExpressionType nonZero; |
214 | |
215 | WASM_TRY_POP_EXPRESSION_STACK_INTO(condition, "select condition" ); |
216 | WASM_TRY_POP_EXPRESSION_STACK_INTO(zero, "select zero" ); |
217 | WASM_TRY_POP_EXPRESSION_STACK_INTO(nonZero, "select non-zero" ); |
218 | |
219 | ExpressionType result; |
220 | WASM_TRY_ADD_TO_CONTEXT(addSelect(condition, nonZero, zero, result)); |
221 | |
222 | m_expressionStack.append(result); |
223 | return { }; |
224 | } |
225 | |
226 | #define CREATE_CASE(name, id, b3op, inc) case OpType::name: |
227 | FOR_EACH_WASM_MEMORY_LOAD_OP(CREATE_CASE) { |
228 | uint32_t alignment; |
229 | uint32_t offset; |
230 | ExpressionType pointer; |
231 | ExpressionType result; |
232 | WASM_PARSER_FAIL_IF(!parseVarUInt32(alignment), "can't get load alignment" ); |
233 | WASM_PARSER_FAIL_IF(alignment > memoryLog2Alignment(m_currentOpcode), "byte alignment " , 1ull << alignment, " exceeds load's natural alignment " , 1ull << memoryLog2Alignment(m_currentOpcode)); |
234 | WASM_PARSER_FAIL_IF(!parseVarUInt32(offset), "can't get load offset" ); |
235 | WASM_TRY_POP_EXPRESSION_STACK_INTO(pointer, "load pointer" ); |
236 | WASM_TRY_ADD_TO_CONTEXT(load(static_cast<LoadOpType>(m_currentOpcode), pointer, result, offset)); |
237 | m_expressionStack.append(result); |
238 | return { }; |
239 | } |
240 | |
241 | FOR_EACH_WASM_MEMORY_STORE_OP(CREATE_CASE) { |
242 | uint32_t alignment; |
243 | uint32_t offset; |
244 | ExpressionType value; |
245 | ExpressionType pointer; |
246 | WASM_PARSER_FAIL_IF(!parseVarUInt32(alignment), "can't get store alignment" ); |
247 | WASM_PARSER_FAIL_IF(alignment > memoryLog2Alignment(m_currentOpcode), "byte alignment " , 1ull << alignment, " exceeds store's natural alignment " , 1ull << memoryLog2Alignment(m_currentOpcode)); |
248 | WASM_PARSER_FAIL_IF(!parseVarUInt32(offset), "can't get store offset" ); |
249 | WASM_TRY_POP_EXPRESSION_STACK_INTO(value, "store value" ); |
250 | WASM_TRY_POP_EXPRESSION_STACK_INTO(pointer, "store pointer" ); |
251 | WASM_TRY_ADD_TO_CONTEXT(store(static_cast<StoreOpType>(m_currentOpcode), pointer, value, offset)); |
252 | return { }; |
253 | } |
254 | #undef CREATE_CASE |
255 | |
256 | case F32Const: { |
257 | uint32_t constant; |
258 | WASM_PARSER_FAIL_IF(!parseUInt32(constant), "can't parse 32-bit floating-point constant" ); |
259 | m_expressionStack.append(m_context.addConstant(F32, constant)); |
260 | return { }; |
261 | } |
262 | |
263 | case I32Const: { |
264 | int32_t constant; |
265 | WASM_PARSER_FAIL_IF(!parseVarInt32(constant), "can't parse 32-bit constant" ); |
266 | m_expressionStack.append(m_context.addConstant(I32, constant)); |
267 | return { }; |
268 | } |
269 | |
270 | case F64Const: { |
271 | uint64_t constant; |
272 | WASM_PARSER_FAIL_IF(!parseUInt64(constant), "can't parse 64-bit floating-point constant" ); |
273 | m_expressionStack.append(m_context.addConstant(F64, constant)); |
274 | return { }; |
275 | } |
276 | |
277 | case I64Const: { |
278 | int64_t constant; |
279 | WASM_PARSER_FAIL_IF(!parseVarInt64(constant), "can't parse 64-bit constant" ); |
280 | m_expressionStack.append(m_context.addConstant(I64, constant)); |
281 | return { }; |
282 | } |
283 | |
284 | case TableGet: { |
285 | WASM_PARSER_FAIL_IF(!Options::useWebAssemblyReferences(), "references are not enabled" ); |
286 | ExpressionType result, idx; |
287 | WASM_TRY_POP_EXPRESSION_STACK_INTO(idx, "table.get" ); |
288 | WASM_TRY_ADD_TO_CONTEXT(addTableGet(idx, result)); |
289 | m_expressionStack.append(result); |
290 | return { }; |
291 | } |
292 | |
293 | case TableSet: { |
294 | WASM_PARSER_FAIL_IF(!Options::useWebAssemblyReferences(), "references are not enabled" ); |
295 | ExpressionType val, idx; |
296 | WASM_TRY_POP_EXPRESSION_STACK_INTO(val, "table.set" ); |
297 | WASM_TRY_POP_EXPRESSION_STACK_INTO(idx, "table.set" ); |
298 | WASM_TRY_ADD_TO_CONTEXT(addTableSet(idx, val)); |
299 | return { }; |
300 | } |
301 | |
302 | case RefNull: { |
303 | WASM_PARSER_FAIL_IF(!Options::useWebAssemblyReferences(), "references are not enabled" ); |
304 | m_expressionStack.append(m_context.addConstant(Anyref, JSValue::encode(jsNull()))); |
305 | return { }; |
306 | } |
307 | |
308 | case RefIsNull: { |
309 | WASM_PARSER_FAIL_IF(!Options::useWebAssemblyReferences(), "references are not enabled" ); |
310 | ExpressionType result, value; |
311 | WASM_TRY_POP_EXPRESSION_STACK_INTO(value, "ref.is_null" ); |
312 | WASM_TRY_ADD_TO_CONTEXT(addRefIsNull(value, result)); |
313 | m_expressionStack.append(result); |
314 | return { }; |
315 | } |
316 | |
317 | case GetLocal: { |
318 | uint32_t index; |
319 | ExpressionType result; |
320 | WASM_PARSER_FAIL_IF(!parseVarUInt32(index), "can't get index for get_local" ); |
321 | WASM_TRY_ADD_TO_CONTEXT(getLocal(index, result)); |
322 | m_expressionStack.append(result); |
323 | return { }; |
324 | } |
325 | |
326 | case SetLocal: { |
327 | uint32_t index; |
328 | ExpressionType value; |
329 | WASM_PARSER_FAIL_IF(!parseVarUInt32(index), "can't get index for set_local" ); |
330 | WASM_TRY_POP_EXPRESSION_STACK_INTO(value, "set_local" ); |
331 | WASM_TRY_ADD_TO_CONTEXT(setLocal(index, value)); |
332 | return { }; |
333 | } |
334 | |
335 | case TeeLocal: { |
336 | uint32_t index; |
337 | WASM_PARSER_FAIL_IF(!parseVarUInt32(index), "can't get index for tee_local" ); |
338 | WASM_PARSER_FAIL_IF(m_expressionStack.isEmpty(), "can't tee_local on empty expression stack" ); |
339 | WASM_TRY_ADD_TO_CONTEXT(setLocal(index, m_expressionStack.last())); |
340 | return { }; |
341 | } |
342 | |
343 | case GetGlobal: { |
344 | uint32_t index; |
345 | ExpressionType result; |
346 | WASM_PARSER_FAIL_IF(!parseVarUInt32(index), "can't get get_global's index" ); |
347 | WASM_TRY_ADD_TO_CONTEXT(getGlobal(index, result)); |
348 | m_expressionStack.append(result); |
349 | return { }; |
350 | } |
351 | |
352 | case SetGlobal: { |
353 | uint32_t index; |
354 | ExpressionType value; |
355 | WASM_PARSER_FAIL_IF(!parseVarUInt32(index), "can't get set_global's index" ); |
356 | WASM_TRY_POP_EXPRESSION_STACK_INTO(value, "set_global value" ); |
357 | WASM_TRY_ADD_TO_CONTEXT(setGlobal(index, value)); |
358 | return { }; |
359 | } |
360 | |
361 | case Call: { |
362 | uint32_t functionIndex; |
363 | WASM_PARSER_FAIL_IF(!parseVarUInt32(functionIndex), "can't parse call's function index" ); |
364 | WASM_PARSER_FAIL_IF(functionIndex >= m_info.functionIndexSpaceSize(), "call function index " , functionIndex, " exceeds function index space " , m_info.functionIndexSpaceSize()); |
365 | |
366 | SignatureIndex calleeSignatureIndex = m_info.signatureIndexFromFunctionIndexSpace(functionIndex); |
367 | const Signature& calleeSignature = SignatureInformation::get(calleeSignatureIndex); |
368 | WASM_PARSER_FAIL_IF(calleeSignature.argumentCount() > m_expressionStack.size(), "call function index " , functionIndex, " has " , calleeSignature.argumentCount(), " arguments, but the expression stack currently holds " , m_expressionStack.size(), " values" ); |
369 | |
370 | size_t firstArgumentIndex = m_expressionStack.size() - calleeSignature.argumentCount(); |
371 | Vector<ExpressionType> args; |
372 | WASM_PARSER_FAIL_IF(!args.tryReserveCapacity(calleeSignature.argumentCount()), "can't allocate enough memory for call's " , calleeSignature.argumentCount(), " arguments" ); |
373 | for (size_t i = firstArgumentIndex; i < m_expressionStack.size(); ++i) |
374 | args.uncheckedAppend(m_expressionStack[i]); |
375 | m_expressionStack.shrink(firstArgumentIndex); |
376 | |
377 | ExpressionType result = Context::emptyExpression(); |
378 | WASM_TRY_ADD_TO_CONTEXT(addCall(functionIndex, calleeSignature, args, result)); |
379 | |
380 | if (result != Context::emptyExpression()) |
381 | m_expressionStack.append(result); |
382 | |
383 | return { }; |
384 | } |
385 | |
386 | case CallIndirect: { |
387 | uint32_t signatureIndex; |
388 | uint8_t reserved; |
389 | WASM_PARSER_FAIL_IF(!m_info.tableInformation, "call_indirect is only valid when a table is defined or imported" ); |
390 | WASM_PARSER_FAIL_IF(!parseVarUInt32(signatureIndex), "can't get call_indirect's signature index" ); |
391 | WASM_PARSER_FAIL_IF(!parseVarUInt1(reserved), "can't get call_indirect's reserved byte" ); |
392 | WASM_PARSER_FAIL_IF(reserved, "call_indirect's 'reserved' varuint1 must be 0x0" ); |
393 | WASM_PARSER_FAIL_IF(m_info.usedSignatures.size() <= signatureIndex, "call_indirect's signature index " , signatureIndex, " exceeds known signatures " , m_info.usedSignatures.size()); |
394 | WASM_PARSER_FAIL_IF(m_info.tableInformation.type() != TableElementType::Funcref, "call_indirect is only valid when a table has type anyfunc" ); |
395 | |
396 | const Signature& calleeSignature = m_info.usedSignatures[signatureIndex].get(); |
397 | size_t argumentCount = calleeSignature.argumentCount() + 1; // Add the callee's index. |
398 | WASM_PARSER_FAIL_IF(argumentCount > m_expressionStack.size(), "call_indirect expects " , argumentCount, " arguments, but the expression stack currently holds " , m_expressionStack.size(), " values" ); |
399 | |
400 | Vector<ExpressionType> args; |
401 | WASM_PARSER_FAIL_IF(!args.tryReserveCapacity(argumentCount), "can't allocate enough memory for " , argumentCount, " call_indirect arguments" ); |
402 | size_t firstArgumentIndex = m_expressionStack.size() - argumentCount; |
403 | for (size_t i = firstArgumentIndex; i < m_expressionStack.size(); ++i) |
404 | args.uncheckedAppend(m_expressionStack[i]); |
405 | m_expressionStack.shrink(firstArgumentIndex); |
406 | |
407 | ExpressionType result = Context::emptyExpression(); |
408 | WASM_TRY_ADD_TO_CONTEXT(addCallIndirect(calleeSignature, args, result)); |
409 | |
410 | if (result != Context::emptyExpression()) |
411 | m_expressionStack.append(result); |
412 | |
413 | return { }; |
414 | } |
415 | |
416 | case Block: { |
417 | Type inlineSignature; |
418 | WASM_PARSER_FAIL_IF(!parseResultType(inlineSignature), "can't get block's inline signature" ); |
419 | m_controlStack.append({ WTFMove(m_expressionStack), m_context.addBlock(inlineSignature) }); |
420 | m_expressionStack = ExpressionList(); |
421 | return { }; |
422 | } |
423 | |
424 | case Loop: { |
425 | Type inlineSignature; |
426 | WASM_PARSER_FAIL_IF(!parseResultType(inlineSignature), "can't get loop's inline signature" ); |
427 | m_controlStack.append({ WTFMove(m_expressionStack), m_context.addLoop(inlineSignature) }); |
428 | m_expressionStack = ExpressionList(); |
429 | return { }; |
430 | } |
431 | |
432 | case If: { |
433 | Type inlineSignature; |
434 | ExpressionType condition; |
435 | ControlType control; |
436 | WASM_PARSER_FAIL_IF(!parseResultType(inlineSignature), "can't get if's inline signature" ); |
437 | WASM_TRY_POP_EXPRESSION_STACK_INTO(condition, "if condition" ); |
438 | WASM_TRY_ADD_TO_CONTEXT(addIf(condition, inlineSignature, control)); |
439 | m_controlStack.append({ WTFMove(m_expressionStack), control }); |
440 | m_expressionStack = ExpressionList(); |
441 | return { }; |
442 | } |
443 | |
444 | case Else: { |
445 | WASM_PARSER_FAIL_IF(m_controlStack.size() == 1, "can't use else block at the top-level of a function" ); |
446 | WASM_TRY_ADD_TO_CONTEXT(addElse(m_controlStack.last().controlData, m_expressionStack)); |
447 | m_expressionStack.shrink(0); |
448 | return { }; |
449 | } |
450 | |
451 | case Br: |
452 | case BrIf: { |
453 | uint32_t target; |
454 | ExpressionType condition = Context::emptyExpression(); |
455 | WASM_PARSER_FAIL_IF(!parseVarUInt32(target), "can't get br / br_if's target" ); |
456 | WASM_PARSER_FAIL_IF(target >= m_controlStack.size(), "br / br_if's target " , target, " exceeds control stack size " , m_controlStack.size()); |
457 | if (m_currentOpcode == BrIf) |
458 | WASM_TRY_POP_EXPRESSION_STACK_INTO(condition, "br / br_if condition" ); |
459 | else |
460 | m_unreachableBlocks = 1; |
461 | |
462 | ControlType& data = m_controlStack[m_controlStack.size() - 1 - target].controlData; |
463 | |
464 | WASM_TRY_ADD_TO_CONTEXT(addBranch(data, condition, m_expressionStack)); |
465 | return { }; |
466 | } |
467 | |
468 | case BrTable: { |
469 | uint32_t numberOfTargets; |
470 | uint32_t defaultTarget; |
471 | ExpressionType condition; |
472 | Vector<ControlType*> targets; |
473 | |
474 | WASM_PARSER_FAIL_IF(!parseVarUInt32(numberOfTargets), "can't get the number of targets for br_table" ); |
475 | WASM_PARSER_FAIL_IF(numberOfTargets == std::numeric_limits<uint32_t>::max(), "br_table's number of targets is too big " , numberOfTargets); |
476 | |
477 | WASM_PARSER_FAIL_IF(!targets.tryReserveCapacity(numberOfTargets), "can't allocate memory for " , numberOfTargets, " br_table targets" ); |
478 | for (uint32_t i = 0; i < numberOfTargets; ++i) { |
479 | uint32_t target; |
480 | WASM_PARSER_FAIL_IF(!parseVarUInt32(target), "can't get " , i, "th target for br_table" ); |
481 | WASM_PARSER_FAIL_IF(target >= m_controlStack.size(), "br_table's " , i, "th target " , target, " exceeds control stack size " , m_controlStack.size()); |
482 | targets.uncheckedAppend(&m_controlStack[m_controlStack.size() - 1 - target].controlData); |
483 | } |
484 | |
485 | WASM_PARSER_FAIL_IF(!parseVarUInt32(defaultTarget), "can't get default target for br_table" ); |
486 | WASM_PARSER_FAIL_IF(defaultTarget >= m_controlStack.size(), "br_table's default target " , defaultTarget, " exceeds control stack size " , m_controlStack.size()); |
487 | |
488 | WASM_TRY_POP_EXPRESSION_STACK_INTO(condition, "br_table condition" ); |
489 | WASM_TRY_ADD_TO_CONTEXT(addSwitch(condition, targets, m_controlStack[m_controlStack.size() - 1 - defaultTarget].controlData, m_expressionStack)); |
490 | |
491 | m_unreachableBlocks = 1; |
492 | return { }; |
493 | } |
494 | |
495 | case Return: { |
496 | ExpressionList returnValues; |
497 | if (m_signature.returnType() != Void) { |
498 | ExpressionType returnValue; |
499 | WASM_TRY_POP_EXPRESSION_STACK_INTO(returnValue, "return" ); |
500 | returnValues.append(returnValue); |
501 | } |
502 | |
503 | WASM_TRY_ADD_TO_CONTEXT(addReturn(m_controlStack[0].controlData, returnValues)); |
504 | m_unreachableBlocks = 1; |
505 | return { }; |
506 | } |
507 | |
508 | case End: { |
509 | ControlEntry data = m_controlStack.takeLast(); |
510 | // FIXME: This is a little weird in that it will modify the expressionStack for the result of the block. |
511 | // That's a little too effectful for me but I don't have a better API right now. |
512 | // see: https://bugs.webkit.org/show_bug.cgi?id=164353 |
513 | WASM_TRY_ADD_TO_CONTEXT(endBlock(data, m_expressionStack)); |
514 | m_expressionStack.swap(data.enclosedExpressionStack); |
515 | return { }; |
516 | } |
517 | |
518 | case Unreachable: { |
519 | WASM_TRY_ADD_TO_CONTEXT(addUnreachable()); |
520 | m_unreachableBlocks = 1; |
521 | return { }; |
522 | } |
523 | |
524 | case Drop: { |
525 | WASM_PARSER_FAIL_IF(!m_expressionStack.size(), "can't drop on empty stack" ); |
526 | auto expression = m_expressionStack.takeLast(); |
527 | m_toKillAfterExpression.append(expression); |
528 | return { }; |
529 | } |
530 | |
531 | case Nop: { |
532 | return { }; |
533 | } |
534 | |
535 | case GrowMemory: { |
536 | WASM_PARSER_FAIL_IF(!m_info.memory, "grow_memory is only valid if a memory is defined or imported" ); |
537 | |
538 | uint8_t reserved; |
539 | WASM_PARSER_FAIL_IF(!parseVarUInt1(reserved), "can't parse reserved varUint1 for grow_memory" ); |
540 | WASM_PARSER_FAIL_IF(reserved != 0, "reserved varUint1 for grow_memory must be zero" ); |
541 | |
542 | ExpressionType delta; |
543 | WASM_TRY_POP_EXPRESSION_STACK_INTO(delta, "expect an i32 argument to grow_memory on the stack" ); |
544 | |
545 | ExpressionType result; |
546 | WASM_TRY_ADD_TO_CONTEXT(addGrowMemory(delta, result)); |
547 | m_expressionStack.append(result); |
548 | |
549 | return { }; |
550 | } |
551 | |
552 | case CurrentMemory: { |
553 | WASM_PARSER_FAIL_IF(!m_info.memory, "current_memory is only valid if a memory is defined or imported" ); |
554 | |
555 | uint8_t reserved; |
556 | WASM_PARSER_FAIL_IF(!parseVarUInt1(reserved), "can't parse reserved varUint1 for current_memory" ); |
557 | WASM_PARSER_FAIL_IF(reserved != 0, "reserved varUint1 for current_memory must be zero" ); |
558 | |
559 | ExpressionType result; |
560 | WASM_TRY_ADD_TO_CONTEXT(addCurrentMemory(result)); |
561 | m_expressionStack.append(result); |
562 | |
563 | return { }; |
564 | } |
565 | } |
566 | |
567 | ASSERT_NOT_REACHED(); |
568 | return { }; |
569 | } |
570 | |
571 | // FIXME: We should try to use the same decoder function for both unreachable and reachable code. https://bugs.webkit.org/show_bug.cgi?id=165965 |
572 | template<typename Context> |
573 | auto FunctionParser<Context>::parseUnreachableExpression() -> PartialResult |
574 | { |
575 | ASSERT(m_unreachableBlocks); |
576 | #define CREATE_CASE(name, id, b3op, inc) case OpType::name: |
577 | switch (m_currentOpcode) { |
578 | case Else: { |
579 | if (m_unreachableBlocks > 1) |
580 | return { }; |
581 | |
582 | ControlEntry& data = m_controlStack.last(); |
583 | m_unreachableBlocks = 0; |
584 | WASM_TRY_ADD_TO_CONTEXT(addElseToUnreachable(data.controlData)); |
585 | m_expressionStack.shrink(0); |
586 | return { }; |
587 | } |
588 | |
589 | case End: { |
590 | if (m_unreachableBlocks == 1) { |
591 | ControlEntry data = m_controlStack.takeLast(); |
592 | WASM_TRY_ADD_TO_CONTEXT(addEndToUnreachable(data)); |
593 | m_expressionStack.swap(data.enclosedExpressionStack); |
594 | } |
595 | m_unreachableBlocks--; |
596 | return { }; |
597 | } |
598 | |
599 | case Loop: |
600 | case If: |
601 | case Block: { |
602 | m_unreachableBlocks++; |
603 | Type unused; |
604 | WASM_PARSER_FAIL_IF(!parseResultType(unused), "can't get inline type for " , m_currentOpcode, " in unreachable context" ); |
605 | return { }; |
606 | } |
607 | |
608 | case BrTable: { |
609 | uint32_t numberOfTargets; |
610 | uint32_t unused; |
611 | WASM_PARSER_FAIL_IF(!parseVarUInt32(numberOfTargets), "can't get the number of targets for br_table in unreachable context" ); |
612 | WASM_PARSER_FAIL_IF(numberOfTargets == std::numeric_limits<uint32_t>::max(), "br_table's number of targets is too big " , numberOfTargets); |
613 | |
614 | for (uint32_t i = 0; i < numberOfTargets; ++i) |
615 | WASM_PARSER_FAIL_IF(!parseVarUInt32(unused), "can't get " , i, "th target for br_table in unreachable context" ); |
616 | |
617 | WASM_PARSER_FAIL_IF(!parseVarUInt32(unused), "can't get default target for br_table in unreachable context" ); |
618 | return { }; |
619 | } |
620 | |
621 | case CallIndirect: { |
622 | uint32_t unused; |
623 | uint8_t unused2; |
624 | WASM_PARSER_FAIL_IF(!parseVarUInt32(unused), "can't get call_indirect's signature index in unreachable context" ); |
625 | WASM_PARSER_FAIL_IF(!parseVarUInt1(unused2), "can't get call_indirect's reserved byte in unreachable context" ); |
626 | return { }; |
627 | } |
628 | |
629 | case F32Const: { |
630 | uint32_t unused; |
631 | WASM_PARSER_FAIL_IF(!parseUInt32(unused), "can't parse 32-bit floating-point constant" ); |
632 | return { }; |
633 | } |
634 | |
635 | case F64Const: { |
636 | uint64_t constant; |
637 | WASM_PARSER_FAIL_IF(!parseUInt64(constant), "can't parse 64-bit floating-point constant" ); |
638 | return { }; |
639 | } |
640 | |
641 | // two immediate cases |
642 | FOR_EACH_WASM_MEMORY_LOAD_OP(CREATE_CASE) |
643 | FOR_EACH_WASM_MEMORY_STORE_OP(CREATE_CASE) { |
644 | uint32_t unused; |
645 | WASM_PARSER_FAIL_IF(!parseVarUInt32(unused), "can't get first immediate for " , m_currentOpcode, " in unreachable context" ); |
646 | WASM_PARSER_FAIL_IF(!parseVarUInt32(unused), "can't get second immediate for " , m_currentOpcode, " in unreachable context" ); |
647 | return { }; |
648 | } |
649 | |
650 | // one immediate cases |
651 | case SetLocal: |
652 | case GetLocal: |
653 | case TeeLocal: |
654 | case GetGlobal: |
655 | case SetGlobal: |
656 | case Br: |
657 | case BrIf: |
658 | case Call: { |
659 | uint32_t unused; |
660 | WASM_PARSER_FAIL_IF(!parseVarUInt32(unused), "can't get immediate for " , m_currentOpcode, " in unreachable context" ); |
661 | return { }; |
662 | } |
663 | |
664 | case I32Const: { |
665 | int32_t unused; |
666 | WASM_PARSER_FAIL_IF(!parseVarInt32(unused), "can't get immediate for " , m_currentOpcode, " in unreachable context" ); |
667 | return { }; |
668 | } |
669 | |
670 | case I64Const: { |
671 | int64_t unused; |
672 | WASM_PARSER_FAIL_IF(!parseVarInt64(unused), "can't get immediate for " , m_currentOpcode, " in unreachable context" ); |
673 | return { }; |
674 | } |
675 | |
676 | case TableGet: |
677 | case TableSet: |
678 | case RefIsNull: |
679 | case RefNull: { |
680 | WASM_PARSER_FAIL_IF(!Options::useWebAssemblyReferences(), "references are not enabled" ); |
681 | return { }; |
682 | } |
683 | |
684 | case GrowMemory: |
685 | case CurrentMemory: { |
686 | uint8_t reserved; |
687 | WASM_PARSER_FAIL_IF(!parseVarUInt1(reserved), "can't parse reserved varUint1 for grow_memory/current_memory" ); |
688 | return { }; |
689 | } |
690 | |
691 | // no immediate cases |
692 | FOR_EACH_WASM_BINARY_OP(CREATE_CASE) |
693 | FOR_EACH_WASM_UNARY_OP(CREATE_CASE) |
694 | case Unreachable: |
695 | case Nop: |
696 | case Return: |
697 | case Select: |
698 | case Drop: { |
699 | return { }; |
700 | } |
701 | } |
702 | #undef CREATE_CASE |
703 | RELEASE_ASSERT_NOT_REACHED(); |
704 | } |
705 | |
706 | } } // namespace JSC::Wasm |
707 | |
708 | #endif // ENABLE(WEBASSEMBLY) |
709 | |