Merge remote-tracking branch 'redux/master' into sh4-pool
[tamarin-stm.git] / eval / eval-parse-config.cpp
blobaeaf9352ac15ecd96ff37e3f025afc0685dfcc3d
1 /* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
2 /* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
16 * The Original Code is [Open Source Virtual Machine.].
18 * The Initial Developer of the Original Code is
19 * Adobe System Incorporated.
20 * Portions created by the Initial Developer are Copyright (C) 2010
21 * the Initial Developer. All Rights Reserved.
23 * Contributor(s):
24 * Adobe AS3 Team
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 #include "avmplus.h"
42 #ifdef VMCFG_EVAL
44 #include "eval.h"
46 namespace avmplus
48 namespace RTC
50 Parser::ConfigBinding::ConfigBinding(Str* ns, Str* name, Expr* value)
51 : ns(ns)
52 , name(name)
53 , value(value)
57 void Parser::addConfigNamespace(Str* ns)
59 configNamespaces = ALLOC(Seq<Str*>, (ns, configNamespaces));
62 void Parser::checkNoShadowingOfConfigNamespaces(uint32_t pos, Str* s)
64 Seq<Str*>* nss = configNamespaces;
65 while (nss != NULL) {
66 if (s == nss->hd)
67 compiler->syntaxError(pos, SYNTAXERR_CONFIG_NAMESPACE_SHADOWING);
68 nss = nss->tl;
72 void Parser::addConfigBinding(Str* ns, Str* name, Expr* value)
74 configBindings = ALLOC(Seq<ConfigBinding*>, (ALLOC(ConfigBinding, (ns, name, value)), configBindings));
77 bool Parser::findConfigNamespace(Str* ns)
79 Seq<Str*>* nss = configNamespaces;
80 while (nss != NULL) {
81 if (nss->hd == ns)
82 return true;
83 nss = nss->tl;
85 return false;
88 Expr* Parser::findConfigBinding(Str* ns, Str* name)
90 Seq<ConfigBinding*>* bs = configBindings;
91 while (bs != NULL) {
92 if (bs->hd->ns == ns && bs->hd->name == name)
93 return bs->hd->value;
94 bs = bs->tl;
96 return NULL;
99 bool Parser::isConfigReference(Expr* e)
101 if (e->tag() != TAG_qualifiedName)
102 return false;
103 QualifiedName* qn = (QualifiedName*)e;
104 if (qn->qualifier == NULL || qn->qualifier->tag() != TAG_simpleName || qn->name->tag() != TAG_simpleName)
105 return false;
106 SimpleName* sn = (SimpleName*)(qn->qualifier);
107 return findConfigNamespace(sn->name);
110 bool Parser::evaluateConfigReference(QualifiedName* qname)
112 if (qname == NULL)
113 return true;
115 AvmAssert(isConfigReference(qname));
117 Str* ns = ((SimpleName*)qname->qualifier)->name;
118 Str* name = ((SimpleName*)qname->name)->name;
119 Expr* value = findConfigBinding(ns, name);
120 if (value != NULL)
121 return evaluateToBoolean(value);
123 compiler->syntaxError(qname->pos, SYNTAXERR_UNBOUND_CONST_NAME);
124 /*NOTREACHED*/
125 return false;
128 // POSSIBLE EXTENSION: structured data and field access
130 // It would be possible to support array, object, xml, and vector initializers
131 // whose initializing subexpressions are either literal values or references
132 // to config variables, as well as field selections on those initializers.
133 // It's possible to imagine some use cases. We need some clean semantics for
134 // when those initializers escape into non-config code - do we want a single
135 // copy or multiple copies?
137 Expr* Parser::evaluateConfigDefinition(Str* ns, Expr* e)
139 // e is evaluated in an environment containing only config bindings.
140 // ns is the default namespace: it will be used to qualify any unqualified name.
142 switch (e->tag()) {
143 case TAG_literalUndefined:
144 case TAG_literalString:
145 case TAG_literalNull:
146 case TAG_literalUInt:
147 case TAG_literalInt:
148 case TAG_literalDouble:
149 case TAG_literalBoolean:
150 return e;
151 case TAG_simpleName: {
152 Expr* expr = findConfigBinding(ns, ((SimpleName*)e)->name);
153 if (expr == NULL)
154 compiler->syntaxError(expr->pos, SYNTAXERR_UNBOUND_CONST_NAME);
155 return expr;
157 case TAG_qualifiedName: {
158 QualifiedName* qname = (QualifiedName*)e;
159 if (qname->qualifier->tag() != TAG_simpleName || qname->name->tag() != TAG_simpleName)
160 compiler->syntaxError(e->pos, SYNTAXERR_UNBOUND_CONST_NAME); // Specifically an illegal const name
161 Str* ns = ((SimpleName*)qname->qualifier)->name;
162 Str* name = ((SimpleName*)qname->name)->name;
163 Expr* value = findConfigBinding(ns, name);
164 if (value == NULL)
165 compiler->syntaxError(e->pos, SYNTAXERR_UNBOUND_CONST_NAME);
166 return value;
168 case TAG_binaryExpr: {
169 // CLARIFICATION: no short-circuiting
171 // We evaluate both sides of && and || in order to uncover
172 // any undefined variables lurking in non-taken branches.
173 BinaryExpr* binary = (BinaryExpr*)e;
174 Expr* lhs = evaluateConfigDefinition(ns, binary->lhs);
175 Expr* rhs = evaluateConfigDefinition(ns, binary->rhs);
176 switch (binary->op) {
177 case OPR_plus:
178 if (lhs->tag() == TAG_literalString || rhs->tag() == TAG_literalString) {
179 StringBuilder b(compiler);
180 b.append(((LiteralString*)lhs)->value);
181 b.append(((LiteralString*)rhs)->value);
182 return boxString(b.str());
184 return boxDouble(evaluateToNumber(lhs) + evaluateToNumber(rhs));
185 case OPR_minus:
186 return boxDouble(evaluateToNumber(lhs) - evaluateToNumber(rhs));
187 case OPR_multiply:
188 return boxDouble(evaluateToNumber(lhs) * evaluateToNumber(rhs));
189 case OPR_divide:
190 return boxDouble(evaluateToNumber(lhs) / evaluateToNumber(rhs));
191 case OPR_remainder:
192 return boxDouble(fmod(evaluateToNumber(lhs), evaluateToNumber(rhs)));
193 case OPR_leftShift:
194 return boxInt(evaluateToInt32(lhs) << (evaluateToUInt32(rhs) & 0x1F));
195 case OPR_rightShift:
196 return boxInt(evaluateToInt32(lhs) >> (evaluateToUInt32(rhs) & 0x1F));
197 case OPR_rightShiftUnsigned:
198 return boxUInt(evaluateToUInt32(lhs) >> (evaluateToUInt32(rhs) & 0x1F));
199 case OPR_bitwiseAnd:
200 return boxInt(evaluateToInt32(lhs) & evaluateToInt32(rhs));
201 case OPR_bitwiseOr:
202 return boxInt(evaluateToInt32(lhs) | evaluateToInt32(rhs));
203 case OPR_bitwiseXor:
204 return boxInt(evaluateToInt32(lhs) ^ evaluateToInt32(rhs));
205 case OPR_logicalAnd:
206 return boxBoolean(int(evaluateToBoolean(lhs)) + int(evaluateToBoolean(rhs)) == 2);
207 case OPR_logicalOr:
208 return boxBoolean(int(evaluateToBoolean(lhs)) + int(evaluateToBoolean(rhs)) != 0);
209 case OPR_less: {
210 int r = evaluateRelational(lhs, rhs);
211 return boxBoolean(r == -1 || r == 0 ? false : true);
213 case OPR_greater: {
214 int r = evaluateRelational(rhs, lhs);
215 return boxBoolean(r == -1 || r == 0 ? false : true);
217 case OPR_lessOrEqual: {
218 int r = evaluateRelational(rhs, lhs);
219 return boxBoolean(r == -1 || r == 1 ? false : true);
221 case OPR_greaterOrEqual: {
222 int r = evaluateRelational(lhs, rhs);
223 return boxBoolean(r == -1 || r == 1 ? false : true);
225 case OPR_equal:
226 case OPR_notEqual:
227 case OPR_strictEqual:
228 case OPR_strictNotEqual: {
229 if (lhs->tag() == TAG_literalInt || lhs->tag() == TAG_literalUInt)
230 lhs = boxDouble(evaluateToNumber(lhs));
231 if (rhs->tag() == TAG_literalInt || rhs->tag() == TAG_literalUInt)
232 rhs = boxDouble(evaluateToNumber(rhs));
234 bool equality;
235 if (binary->op == OPR_equal || binary->op == OPR_notEqual)
236 equality = binary->op == OPR_equal;
237 else
238 equality = binary->op == OPR_strictEqual;
240 if (lhs->tag() != rhs->tag()) {
241 if (binary->op == OPR_equal || binary->op == OPR_notEqual) {
242 if ((lhs->tag() == TAG_literalUndefined && rhs->tag() == TAG_literalNull) ||
243 (lhs->tag() == TAG_literalNull && rhs->tag() == TAG_literalUndefined))
244 return boxBoolean(true == equality);
245 if ((lhs->tag() == TAG_literalString && rhs->tag() == TAG_literalDouble) ||
246 (lhs->tag() == TAG_literalDouble && rhs->tag() == TAG_literalString))
247 return boxBoolean((evaluateToNumber(lhs) == evaluateToNumber(rhs)) == equality);
248 if (lhs->tag() == TAG_literalBoolean || rhs->tag() == TAG_literalBoolean)
249 return boxBoolean((evaluateToBoolean(lhs) == evaluateToBoolean(rhs)) == equality);
251 return boxBoolean(false == equality);
253 if (lhs->tag() == TAG_literalUndefined || lhs->tag() == TAG_literalNull)
254 return boxBoolean(true == equality);
255 if (lhs->tag() == TAG_literalDouble)
256 return boxBoolean((evaluateToNumber(lhs) == evaluateToNumber(rhs)) == equality);
257 if (lhs->tag() == TAG_literalBoolean)
258 return boxBoolean((evaluateToBoolean(lhs) == evaluateToBoolean(rhs)) == equality);
259 if (lhs->tag() == TAG_literalString)
260 return boxBoolean((evaluateToString(lhs) == evaluateToString(rhs)) == equality);
261 failNonConstant(lhs);
262 /*NOTREACHED*/
263 break;
265 default:
266 // "as", "is", "in", ",", "="
267 compiler->syntaxError(position(), SYNTAXERR_ILLEGAL_OP_IN_CONSTEXPR);
268 /*NOTREACHED*/
269 break;
271 /*NOTREACHED*/
272 break;
274 case TAG_unaryExpr: {
275 // EXTENSION: typeof
277 // Supporting "typeof" makes some sort of sense (for example, the
278 // operand of typeof can be a config constant that can take on
279 // various values, and computing the name of the type into the
280 // program can be useful).
282 // EXTENSION: void
284 // Supporting "void" probably does not make a lot of sense, but it
285 // seems benign.
286 UnaryExpr* unary = (UnaryExpr*)e;
287 Expr* opd = evaluateConfigDefinition(ns, unary->expr);
288 switch (unary->op) {
289 case OPR_typeof:
290 switch (opd->tag()) {
291 case TAG_literalUndefined: return boxString("undefined");
292 case TAG_literalString: return boxString("string");
293 case TAG_literalNull: return boxString("object");
294 case TAG_literalUInt:
295 case TAG_literalInt:
296 case TAG_literalDouble: return boxString("number");
297 case TAG_literalBoolean: return boxString("boolean");
298 default:
299 failNonConstant(opd);
300 return NULL;
302 case OPR_bitwiseNot: return boxUInt(~evaluateToUInt32(opd));
303 case OPR_unminus: return boxDouble(-evaluateToNumber(opd));
304 case OPR_unplus: return boxDouble(evaluateToNumber(opd));
305 case OPR_not: return boxBoolean(!evaluateToBoolean(opd));
306 case OPR_void: return boxUndefined();
307 default:
308 // "delete", "++", "--"
309 compiler->syntaxError(position(), SYNTAXERR_ILLEGAL_OP_IN_CONSTEXPR);
310 /*NOTREACHED*/
311 break;
313 /*NOTREACHED*/
314 break;
316 case TAG_conditionalExpr: {
317 // EXTENSION: conditional operator
319 // It seems totally sensible to support "... ? ... : ...", though
320 // it's not mentioned in the conditional compilation spec.
322 // We evaluate both arms in order to uncover references to undefined
323 // configuration variables, same as for && and ||.
324 ConditionalExpr* cond = (ConditionalExpr*)e;
325 Expr* e1 = evaluateConfigDefinition(ns, cond->e1);
326 Expr* e2 = evaluateConfigDefinition(ns, cond->e2);
327 Expr* e3 = evaluateConfigDefinition(ns, cond->e3);
328 return evaluateToBoolean(e1) ? e2 : e3;
330 default:
331 // Property references, 'new', 'call' - lots of things
332 compiler->syntaxError(position(), SYNTAXERR_ILLEGAL_OP_IN_CONSTEXPR);
333 /*NOTREACHED*/
334 break;
336 /*NOTREACHED*/
337 return NULL;
340 Expr* Parser::boxDouble(double n) { return ALLOC(LiteralDouble, (n, 0)); }
341 Expr* Parser::boxUInt(uint32_t n) { return ALLOC(LiteralUInt, (n, 0)); }
342 Expr* Parser::boxInt(int32_t n) { return ALLOC(LiteralInt, (n, 0)); }
343 Expr* Parser::boxBoolean(bool b) { return ALLOC(LiteralBoolean, (b, 0)); }
344 Expr* Parser::boxString(const char* s) { return ALLOC(LiteralString, (compiler->intern(s), 0)); }
345 Expr* Parser::boxString(Str* s) { return ALLOC(LiteralString, (s, 0)); }
346 Expr* Parser::boxUndefined() { return ALLOC(LiteralUndefined, (0)); }
348 uint32_t Parser::evaluateToUInt32(Expr* e)
350 if (e->tag() == TAG_literalUInt)
351 return ((LiteralUInt*)e)->value;
352 double d = evaluateToNumber(e);
353 if (d == 0 || MathUtils::isNaN(d) || MathUtils::isInfinite(d))
354 return 0;
355 d = (d < 0 ? -1 : 1) * floor(fabs(d));
356 d = fmod(d, 4294967296.0);
357 return uint32_t(d);
360 int32_t Parser::evaluateToInt32(Expr* e)
362 if (e->tag() == TAG_literalInt)
363 return ((LiteralInt*)e)->value;
364 double d = evaluateToNumber(e);
365 if (d == 0 || MathUtils::isNaN(d) || MathUtils::isInfinite(d))
366 return 0;
367 d = (d < 0 ? -1 : 1) * floor(fabs(d));
368 d = fmod(d, 4294967296.0);
369 if (d >= 2147483648.0)
370 return int32_t(d - 4294967296.0);
371 else
372 return int32_t(d);
375 double Parser::evaluateToNumber(Expr* e)
377 switch (e->tag()) {
378 case TAG_literalUndefined: return MathUtils::kNaN;
379 case TAG_literalNull: return 0.0;
380 case TAG_literalBoolean: return ((LiteralBoolean*)e)->value ? 1.0 : 0.0;
381 case TAG_literalDouble: return ((LiteralDouble*)e)->value;
382 case TAG_literalInt: return (double)(((LiteralInt*)e)->value);
383 case TAG_literalUInt: return (double)(((LiteralUInt*)e)->value);
384 case TAG_literalString: return strToDouble(((LiteralString*)e)->value);
385 default:
386 failNonConstant(e);
387 return 0;
391 bool Parser::evaluateToBoolean(Expr* e)
393 switch (e->tag()) {
394 case TAG_literalUndefined: return false;
395 case TAG_literalNull: return false;
396 case TAG_literalBoolean: return ((LiteralBoolean*)e)->value;
397 case TAG_literalDouble: { double v = ((LiteralDouble*)e)->value; return !MathUtils::isNaN(v) && v != 0.0; }
398 case TAG_literalInt: return ((LiteralInt*)e)->value != 0;
399 case TAG_literalUInt: return ((LiteralUInt*)e)->value != 0;
400 case TAG_literalString: return ((LiteralString*)e)->value->length > 0;
401 default:
402 failNonConstant(e);
403 return 0;
407 Str* Parser::evaluateToString(Expr* e)
409 switch (e->tag()) {
410 case TAG_literalUndefined: return compiler->intern("undefined");
411 case TAG_literalNull: return compiler->intern("null");
412 case TAG_literalBoolean: return ((LiteralBoolean*)e)->value ? compiler->intern("true") : compiler->intern("false");
413 case TAG_literalDouble: return doubleToStr(((LiteralDouble*)e)->value);
414 case TAG_literalInt: return doubleToStr(((LiteralInt*)e)->value);
415 case TAG_literalUInt: return doubleToStr(((LiteralUInt*)e)->value);
416 case TAG_literalString: return ((LiteralString*)e)->value;
417 default:
418 failNonConstant(e);
419 return 0;
423 // Returns -1 for undefined, 0 for false, 1 for true. Generally true means "x < y",
424 // false means "!(x < y)" and undefined means x and y are not comparable.
426 int Parser::evaluateRelational(Expr* lhs, Expr* rhs)
428 if (lhs->tag() == TAG_literalString && rhs->tag() == TAG_literalString)
429 return (((LiteralString*)lhs)->value)->compareTo(((LiteralString*)rhs)->value) < 0 ? 1 : 0;
431 double l = evaluateToNumber(lhs);
432 double r = evaluateToNumber(rhs);
433 if (MathUtils::isNaN(l) || MathUtils::isNaN(r))
434 return -1;
435 return l < r ? 1 : 0;
438 void Parser::failNonConstant(Expr* e)
440 compiler->internalError(position(), "Non-constant value in expression evaluation, tag=%d", int(e->tag()));
445 #endif // VMCFG_EVAL