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