1/*
2 * Copyright (C) 2016 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "WasmValidate.h"
28
29#if ENABLE(WEBASSEMBLY)
30
31#include "WasmFunctionParser.h"
32#include <wtf/text/StringBuilder.h>
33
34namespace JSC { namespace Wasm {
35
36class Validate {
37public:
38 class ControlData {
39 public:
40 ControlData(BlockType type, Type signature)
41 : m_blockType(type)
42 , m_signature(signature)
43 {
44 }
45
46 ControlData()
47 {
48 }
49
50 void dump(PrintStream& out) const
51 {
52 switch (type()) {
53 case BlockType::If:
54 out.print("If: ");
55 break;
56 case BlockType::Block:
57 out.print("Block: ");
58 break;
59 case BlockType::Loop:
60 out.print("Loop: ");
61 break;
62 }
63 }
64
65 BlockType type() const { return m_blockType; }
66 Type signature() const { return m_signature; }
67 private:
68 BlockType m_blockType;
69 Type m_signature;
70 };
71 typedef Type ExpressionType;
72 typedef ControlData ControlType;
73 typedef Vector<ExpressionType, 1> ExpressionList;
74 static const ExpressionType emptyExpression = Void;
75
76 bool WARN_UNUSED_RETURN addArguments(const Vector<Type>&);
77 bool WARN_UNUSED_RETURN addLocal(Type, uint32_t);
78 ExpressionType addConstant(Type type, uint64_t) { return type; }
79
80 // Locals
81 bool WARN_UNUSED_RETURN getLocal(uint32_t index, ExpressionType& result);
82 bool WARN_UNUSED_RETURN setLocal(uint32_t index, ExpressionType value);
83
84 // Memory
85 bool WARN_UNUSED_RETURN load(LoadOpType, ExpressionType pointer, ExpressionType& result, uint32_t offset);
86 bool WARN_UNUSED_RETURN store(StoreOpType, ExpressionType pointer, ExpressionType value, uint32_t offset);
87
88 // Basic operators
89 bool WARN_UNUSED_RETURN binaryOp(BinaryOpType, ExpressionType left, ExpressionType right, ExpressionType& result);
90 bool WARN_UNUSED_RETURN unaryOp(UnaryOpType, ExpressionType arg, ExpressionType& result);
91
92 // Control flow
93 ControlData WARN_UNUSED_RETURN addBlock(Type signature);
94 ControlData WARN_UNUSED_RETURN addLoop(Type signature);
95 bool WARN_UNUSED_RETURN addIf(ExpressionType condition, Type signature, ControlData& result);
96 bool WARN_UNUSED_RETURN addElse(ControlData&, const ExpressionList&);
97
98 bool WARN_UNUSED_RETURN addReturn(const ExpressionList& returnValues);
99 bool WARN_UNUSED_RETURN addBranch(ControlData&, ExpressionType condition, const ExpressionList& returnValues);
100 bool WARN_UNUSED_RETURN endBlock(ControlData&, ExpressionList& expressionStack);
101
102 bool WARN_UNUSED_RETURN addCall(unsigned calleeIndex, const FunctionInformation&, const Vector<ExpressionType>& args, ExpressionType& result);
103 bool WARN_UNUSED_RETURN isContinuationReachable(ControlData&) { return true; }
104
105 void dump(const Vector<ControlType>& controlStack, const ExpressionList& expressionStack);
106
107 String errorMessage() const { return m_errorMessage; }
108 Validate(ExpressionType returnType)
109 : m_returnType(returnType)
110 {
111 }
112
113private:
114 bool unify(Type, Type);
115 bool unify(const ExpressionList&, const ControlData&);
116
117 ExpressionType m_returnType;
118 Vector<Type> m_locals;
119 String m_errorMessage;
120};
121
122bool Validate::addArguments(const Vector<Type>& args)
123{
124 for (Type arg : args) {
125 if (!addLocal(arg, 1))
126 return false;
127 }
128 return true;
129}
130
131bool Validate::addLocal(Type type, uint32_t count)
132{
133 if (!m_locals.tryReserveCapacity(m_locals.size() + count))
134 return false;
135
136 for (uint32_t i = 0; i < count; ++i)
137 m_locals.uncheckedAppend(type);
138 return true;
139}
140
141bool Validate::getLocal(uint32_t index, ExpressionType& result)
142{
143 if (index < m_locals.size()) {
144 result = m_locals[index];
145 return true;
146 }
147 m_errorMessage = ASCIILiteral("Attempt to use unknown local.");
148 return false;
149}
150
151bool Validate::setLocal(uint32_t index, ExpressionType value)
152{
153 ExpressionType localType;
154 if (!getLocal(index, localType))
155 return false;
156
157 if (localType == value)
158 return true;
159
160 m_errorMessage = makeString("Attempt to set local with type: ", toString(localType), " with a variable of type: ", toString(value));
161 return false;
162}
163
164Validate::ControlType Validate::addBlock(Type signature)
165{
166 return ControlData(BlockType::Block, signature);
167}
168
169Validate::ControlType Validate::addLoop(Type signature)
170{
171 return ControlData(BlockType::Loop, signature);
172}
173
174bool Validate::addIf(ExpressionType condition, Type signature, ControlType& result)
175{
176 if (condition != I32) {
177 m_errorMessage = makeString("Attempting to use ", toString(condition), " as the condition for an if block");
178 return false;
179 }
180 result = ControlData(BlockType::If, signature);
181 return true;
182}
183
184bool Validate::addElse(ControlType& current, const ExpressionList& values)
185{
186 if (current.type() != BlockType::If) {
187 m_errorMessage = makeString("Attempting to add else block to something other than an if");
188 return false;
189 }
190
191 if (!unify(values, current)) {
192 ASSERT(errorMessage());
193 return false;
194 }
195
196 current = ControlData(BlockType::Block, current.signature());
197 return true;
198}
199
200bool Validate::addReturn(const ExpressionList& returnValues)
201{
202 if (m_returnType == Void)
203 return true;
204 ASSERT(returnValues.size() == 1);
205
206 if (m_returnType == returnValues[0])
207 return true;
208
209 m_errorMessage = makeString("Attempting to add return with type: ", toString(returnValues[0]), " but function expects return with type: ", toString(m_returnType));
210 return false;
211}
212
213bool Validate::addBranch(ControlType& target, ExpressionType condition, const ExpressionList& stack)
214{
215 // Void means this is not a conditional branch.
216 if (condition != Void && condition != I32) {
217 m_errorMessage = makeString("Attempting to add a conditional branch with condition type: ", toString(condition), " but expected i32.");
218 return false;
219 }
220
221 if (target.type() == BlockType::If)
222 return true;
223
224 if (target.signature() == Void)
225 return true;
226
227 ASSERT(stack.size() == 1);
228 if (target.signature() == stack[0])
229 return true;
230
231 m_errorMessage = makeString("Attempting to branch to block with expected type: ", toString(target.signature()), " but branching with type: ", toString(target.signature()));
232 return false;
233}
234
235bool Validate::endBlock(ControlType& block, ExpressionList& stack)
236{
237 if (block.signature() == Void)
238 return true;
239
240
241 ASSERT(stack.size() == 1);
242 if (block.signature() == stack[0])
243 return true;
244
245 m_errorMessage = makeString("Block fallthrough has expected type: ", toString(block.signature()), " but produced type: ", toString(block.signature()));
246 return false;
247}
248
249bool Validate::addCall(unsigned, const FunctionInformation& info, const Vector<ExpressionType>& args, ExpressionType& result)
250{
251 if (info.signature->arguments.size() != args.size()) {
252 StringBuilder builder;
253 builder.append("Arity mismatch in call, expected: ");
254 builder.appendNumber(info.signature->arguments.size());
255 builder.append(" but got: ");
256 builder.appendNumber(args.size());
257 m_errorMessage = builder.toString();
258 return false;
259 }
260
261 for (unsigned i = 0; i < args.size(); ++i) {
262 if (args[i] != info.signature->arguments[i]) {
263 m_errorMessage = makeString("Expected argument type: ", toString(info.signature->arguments[i]), " does not match passed argument type: ", toString(args[i]));
264 return false;
265 }
266 }
267
268 result = info.signature->returnType;
269 return true;
270}
271
272bool Validate::unify(const ExpressionList& values, const ControlType& block)
273{
274 ASSERT(values.size() <= 1);
275 if (block.signature() == Void)
276 return true;
277
278 if (!values.size()) {
279 m_errorMessage = makeString("Block has non-void signature but has no stack entries on exit");
280 return false;
281 }
282
283 if (values[0] == block.signature())
284 return true;
285
286 m_errorMessage = makeString("Expected control flow to return value with type: ", toString(block.signature()), " but got value with type: ", toString(values[0]));
287 return false;
288}
289
290void Validate::dump(const Vector<ControlType>&, const ExpressionList&)
291{
292 dataLogLn("Validating");
293}
294
295String validateFunction(const uint8_t* source, size_t length, const Signature* signature, const Vector<FunctionInformation>& functions)
296{
297 Validate context(signature->returnType);
298 FunctionParser<Validate> validator(context, source, length, signature, functions);
299 if (!validator.parse()) {
300 // FIXME: add better location information here. see: https://bugs.webkit.org/show_bug.cgi?id=164288
301 return context.errorMessage();
302 }
303
304 return String();
305}
306
307} } // namespace JSC::Wasm
308
309#include "WasmValidateInlines.h"
310
311#endif // ENABLE(WEBASSEMBLY)