Make JetStream 2
[WebKit-https.git] / PerformanceTests / JetStream2 / WSL / Evaluator.js
1 /*
2  * Copyright (C) 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 "use strict";
26
27 // This is a combined LHS/RHS evaluator that passes around EPtr's to everything.
28 class Evaluator extends Visitor {
29     constructor(program)
30     {
31         super();
32         this._program = program;
33     }
34     
35     // You must snapshot if you use a value in rvalue context. For example, a call expression will
36     // snapshot all of its arguments immedaitely upon executing them. In general, it should not be
37     // possible for a pointer returned from a visit method in rvalue context to live across any effects.
38     _snapshot(type, dstPtr, srcPtr)
39     {
40         let size = type.size;
41         if (size == null)
42             throw new Error("Cannot get size of type: " + type + " (size = " + size + ", constructor = " + type.constructor.name + ")");
43         if (!dstPtr)
44             dstPtr = new EPtr(new EBuffer(size), 0);
45         dstPtr.copyFrom(srcPtr, size);
46         return dstPtr;
47     }
48     
49     runFunc(func)
50     {
51         return EBuffer.disallowAllocation(
52             () => this._runBody(func.returnType, func.returnEPtr, func.body));
53     }
54     
55     _runBody(type, ptr, block)
56     {
57         if (!ptr)
58             throw new Error("Null ptr");
59         try {
60             block.visit(this);
61             // FIXME: We should have a check that there is no way to drop out of a function without
62             // returning unless the function returns void.
63             return null;
64         } catch (e) {
65             if (e == BreakException || e == ContinueException)
66                 throw new Error("Should not see break/continue at function scope");
67             if (e instanceof ReturnException) {
68                 let result = this._snapshot(type, ptr, e.value);
69                 return result;
70             }
71             throw e;
72         }
73     }
74     
75     visitFunctionLikeBlock(node)
76     {
77         for (let i = 0; i < node.argumentList.length; ++i) {
78             node.parameters[i].ePtr.copyFrom(
79                 node.argumentList[i].visit(this),
80                 node.parameters[i].type.size);
81         }
82         let result = this._runBody(node.returnType, node.returnEPtr, node.body);
83         return result;
84     }
85     
86     visitReturn(node)
87     {
88         throw new ReturnException(node.value ? node.value.visit(this) : null);
89     }
90     
91     visitVariableDecl(node)
92     {
93         if (!node.ePtr.buffer)
94             throw new Error("eptr without buffer in " + node);
95         node.type.populateDefaultValue(node.ePtr.buffer, node.ePtr.offset);
96         if (node.initializer)
97             node.ePtr.copyFrom(node.initializer.visit(this), node.type.size);
98     }
99     
100     visitAssignment(node)
101     {
102         let target = node.lhs.visit(this);
103         let source = node.rhs.visit(this);
104         target.copyFrom(source, node.type.size);
105         return target;
106     }
107     
108     visitIdentityExpression(node)
109     {
110         return node.target.visit(this);
111     }
112     
113     visitDereferenceExpression(node)
114     {
115         let ptr = node.ptr.visit(this).loadValue();
116         if (!ptr)
117             throw new WTrapError(node.origin.originString, "Null dereference");
118         return ptr;
119     }
120     
121     visitMakePtrExpression(node)
122     {
123         let ptr = node.lValue.visit(this);
124         return node.ePtr.box(ptr);
125     }
126     
127     visitMakeArrayRefExpression(node)
128     {
129         return node.ePtr.box(new EArrayRef(node.lValue.visit(this), node.numElements.visit(this).loadValue()));
130     }
131     
132     visitConvertPtrToArrayRefExpression(node)
133     {
134         return node.ePtr.box(new EArrayRef(node.lValue.visit(this).loadValue(), 1));
135     }
136     
137     visitCommaExpression(node)
138     {
139         let result;
140         for (let expression of node.list)
141             result = expression.visit(this);
142         // This should almost snapshot, except that tail-returning a pointer is totally OK.
143         return result;
144     }
145     
146     visitVariableRef(node)
147     {
148         return node.variable.ePtr;
149     }
150     
151     visitGenericLiteral(node)
152     {
153         return node.ePtr.box(node.valueForSelectedType);
154     }
155     
156     visitNullLiteral(node)
157     {
158         return node.ePtr.box(null);
159     }
160     
161     visitBoolLiteral(node)
162     {
163         return node.ePtr.box(node.value);
164     }
165     
166     visitEnumLiteral(node)
167     {
168         return node.ePtr.box(node.member.value.unifyNode.valueForSelectedType);
169     }
170
171     visitLogicalNot(node)
172     {
173         let result = !node.operand.visit(this).loadValue();
174         return node.ePtr.box(result);
175     }
176
177     visitLogicalExpression(node)
178     {
179         let lhs = node.left.visit(this).loadValue();
180         let rhs = node.right.visit(this).loadValue();
181         let result;
182         switch (node.text) {
183         case "&&":
184             result = lhs && rhs;
185             break;
186         case "||":
187             result = lhs || rhs;
188             break;
189         default:
190             throw new Error("Unknown type of logical expression");
191         }
192         return node.ePtr.box(result);
193     }
194
195     visitIfStatement(node)
196     {
197         if (node.conditional.visit(this).loadValue())
198             return node.body.visit(this);
199         else if (node.elseBody)
200             return node.elseBody.visit(this);
201     }
202
203     visitWhileLoop(node)
204     {
205         while (node.conditional.visit(this).loadValue()) {
206             try {
207                 node.body.visit(this);
208             } catch (e) {
209                 if (e == BreakException)
210                     break;
211                 if (e == ContinueException)
212                     continue;
213                 throw e;
214             }
215         }
216     }
217
218     visitDoWhileLoop(node)
219     {
220         do {
221             try {
222                 node.body.visit(this);
223             } catch (e) {
224                 if (e == BreakException)
225                     break;
226                 if (e == ContinueException)
227                     continue;
228                 throw e;
229             }
230         } while (node.conditional.visit(this).loadValue());
231     }
232
233     visitForLoop(node)
234     {
235         for (node.initialization ? node.initialization.visit(this) : true;
236             node.condition ? node.condition.visit(this).loadValue() : true;
237             node.increment ? node.increment.visit(this) : true) {
238             try {
239                 node.body.visit(this);
240             } catch (e) {
241                 if (e == BreakException)
242                     break;
243                 if (e == ContinueException)
244                     continue;
245                 throw e;
246             }
247         }
248     }
249     
250     visitSwitchStatement(node)
251     {
252         let findAndRunCast = predicate => {
253             for (let i = 0; i < node.switchCases.length; ++i) {
254                 let switchCase = node.switchCases[i];
255                 if (predicate(switchCase)) {
256                     try {
257                         for (let j = i; j < node.switchCases.length; ++j)
258                             node.switchCases[j].visit(this);
259                     } catch (e) {
260                         if (e != BreakException)
261                             throw e;
262                     }
263                     return true;
264                 }
265             }
266             return false;
267         };
268         
269         let value = node.value.visit(this).loadValue();
270         
271         let found = findAndRunCast(switchCase => {
272             if (switchCase.isDefault)
273                 return false;
274             return node.type.unifyNode.valuesEqual(
275                 value, switchCase.value.unifyNode.valueForSelectedType);
276         });
277         if (found)
278             return;
279         
280         found = findAndRunCast(switchCase => switchCase.isDefault);
281         if (!found)
282             throw new Error("Switch statement did not find case");
283     }
284
285     visitBreak(node)
286     {
287         throw BreakException;
288     }
289
290     visitContinue(node)
291     {
292         throw ContinueException;
293     }
294
295     visitTrapStatement(node)
296     {
297         throw new WTrapError(node.origin.originString, "Trap statement");
298     }
299     
300     visitAnonymousVariable(node)
301     {
302         node.type.populateDefaultValue(node.ePtr.buffer, node.ePtr.offset);
303     }
304     
305     visitCallExpression(node)
306     {
307         // We evaluate inlined ASTs, so this can only be a native call.
308         let callArguments = [];
309         for (let i = 0; i < node.argumentList.length; ++i) {
310             let argument = node.argumentList[i];
311             let type = node.nativeFuncInstance.parameterTypes[i];
312             if (!type || !argument)
313                 throw new Error("Cannot get type or argument; i = " + i + ", argument = " + argument + ", type = " + type + "; in " + node);
314             let argumentValue = argument.visit(this);
315             if (!argumentValue)
316                 throw new Error("Null argument value, i = " + i + ", node = " + node);
317             callArguments.push(() => {
318                 let result = this._snapshot(type, null, argumentValue);
319                 return result;
320             });
321         }
322         
323         // For simplicity, we allow intrinsics to just allocate new buffers, and we allocate new
324         // buffers when snapshotting their arguments. This is not observable to the user, so it's OK.
325         let result = EBuffer.allowAllocation(
326             () => node.func.implementation(callArguments.map(thunk => thunk()), node));
327         
328         result = this._snapshot(node.nativeFuncInstance.returnType, node.resultEPtr, result);
329         return result;
330     }
331 }
332