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 | |
34 | namespace JSC { namespace Wasm { |
35 | |
36 | enum class BlockType { |
37 | If, |
38 | Block, |
39 | Loop, |
40 | TopLevel |
41 | }; |
42 | |
43 | template<typename Stack> |
44 | Stack 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 | |
56 | template<typename Context> |
57 | class FunctionParser : public Parser<void> { |
58 | public: |
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 | |
81 | private: |
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 | |
117 | template<typename Context> |
118 | FunctionParser<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 | |
129 | template<typename Context> |
130 | auto 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 | |
156 | template<typename Context> |
157 | auto 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 | |
184 | template<typename Context> |
185 | template<OpType op> |
186 | auto 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 | |
200 | template<typename Context> |
201 | template<OpType op> |
202 | auto 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 | |
214 | template<typename Context> |
215 | auto 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 |
655 | template<typename Context> |
656 | auto 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 | |