File: | platform/mac/avmshell/../../../eval/eval-parse-config.cpp |
Location: | line 154, column 47 |
Description: | Access to field 'pos' results in a dereference of a null pointer (loaded from variable 'expr') |
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 | ||
5 | * | ||
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/ | ||
10 | * | ||
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. | ||
15 | * | ||
16 | * The Original Code is [Open Source Virtual Machine.]. | ||
17 | * | ||
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. | ||
22 | * | ||
23 | * Contributor(s): | ||
24 | * Adobe AS3 Team | ||
25 | * | ||
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. | ||
37 | * | ||
38 | * ***** END LICENSE BLOCK ***** */ | ||
39 | |||
40 | #include "avmplus.h" | ||
41 | |||
42 | #ifdef VMCFG_EVAL | ||
43 | |||
44 | #include "eval.h" | ||
45 | |||
46 | namespace avmplus | ||
47 | { | ||
48 | namespace RTC | ||
49 | { | ||
50 | Parser::ConfigBinding::ConfigBinding(Str* ns, Str* name, Expr* value) | ||
51 | : ns(ns) | ||
52 | , name(name) | ||
53 | , value(value) | ||
54 | { | ||
55 | } | ||
56 | |||
57 | void Parser::addConfigNamespace(Str* ns) | ||
58 | { | ||
59 | configNamespaces = ALLOC(Seq<Str*>, (ns, configNamespaces))::new (allocator->alloc(sizeof(Seq<Str*>))) Seq<Str *> (ns, configNamespaces); | ||
60 | } | ||
61 | |||
62 | void Parser::checkNoShadowingOfConfigNamespaces(uint32_t pos, Str* s) | ||
63 | { | ||
64 | Seq<Str*>* nss = configNamespaces; | ||
65 | while (nss != NULL__null) { | ||
66 | if (s == nss->hd) | ||
67 | compiler->syntaxError(pos, SYNTAXERR_CONFIG_NAMESPACE_SHADOWING); | ||
68 | nss = nss->tl; | ||
69 | } | ||
70 | } | ||
71 | |||
72 | void Parser::addConfigBinding(Str* ns, Str* name, Expr* value) | ||
73 | { | ||
74 | configBindings = ALLOC(Seq<ConfigBinding*>, (ALLOC(ConfigBinding, (ns, name, value)), configBindings))::new (allocator->alloc(sizeof(Seq<ConfigBinding*>)) ) Seq<ConfigBinding*> (::new (allocator->alloc(sizeof (ConfigBinding))) ConfigBinding (ns, name, value), configBindings ); | ||
75 | } | ||
76 | |||
77 | boolbool Parser::findConfigNamespace(Str* ns) | ||
78 | { | ||
79 | Seq<Str*>* nss = configNamespaces; | ||
80 | while (nss != NULL__null) { | ||
81 | if (nss->hd == ns) | ||
82 | return truetrue; | ||
83 | nss = nss->tl; | ||
84 | } | ||
85 | return falsefalse; | ||
86 | } | ||
87 | |||
88 | Expr* Parser::findConfigBinding(Str* ns, Str* name) | ||
89 | { | ||
90 | Seq<ConfigBinding*>* bs = configBindings; | ||
91 | while (bs != NULL__null) { | ||
92 | if (bs->hd->ns == ns && bs->hd->name == name) | ||
93 | return bs->hd->value; | ||
94 | bs = bs->tl; | ||
95 | } | ||
96 | return NULL__null; | ||
97 | } | ||
98 | |||
99 | boolbool Parser::isConfigReference(Expr* e) | ||
100 | { | ||
101 | if (e->tag() != TAG_qualifiedName) | ||
102 | return falsefalse; | ||
103 | QualifiedName* qn = (QualifiedName*)e; | ||
104 | if (qn->qualifier == NULL__null || qn->qualifier->tag() != TAG_simpleName || qn->name->tag() != TAG_simpleName) | ||
105 | return falsefalse; | ||
106 | SimpleName* sn = (SimpleName*)(qn->qualifier); | ||
107 | return findConfigNamespace(sn->name); | ||
108 | } | ||
109 | |||
110 | boolbool Parser::evaluateConfigReference(QualifiedName* qname) | ||
111 | { | ||
112 | if (qname == NULL__null) | ||
113 | return truetrue; | ||
114 | |||
115 | AvmAssert(isConfigReference(qname))do { } while (0); | ||
116 | |||
117 | Str* ns = ((SimpleName*)qname->qualifier)->name; | ||
118 | Str* name = ((SimpleName*)qname->name)->name; | ||
119 | Expr* value = findConfigBinding(ns, name); | ||
120 | if (value != NULL__null) | ||
121 | return evaluateToBoolean(value); | ||
122 | |||
123 | compiler->syntaxError(qname->pos, SYNTAXERR_UNBOUND_CONST_NAME); | ||
124 | /*NOTREACHED*/ | ||
125 | return falsefalse; | ||
126 | } | ||
127 | |||
128 | // POSSIBLE EXTENSION: structured data and field access | ||
129 | // | ||
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? | ||
136 | |||
137 | Expr* Parser::evaluateConfigDefinition(Str* ns, Expr* e) | ||
138 | { | ||
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. | ||
141 | |||
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__null) | ||
| |||
154 | compiler->syntaxError(expr->pos, SYNTAXERR_UNBOUND_CONST_NAME); | ||
| |||
155 | return expr; | ||
156 | } | ||
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__null) | ||
165 | compiler->syntaxError(e->pos, SYNTAXERR_UNBOUND_CONST_NAME); | ||
166 | return value; | ||
167 | } | ||
168 | case TAG_binaryExpr: { | ||
169 | // CLARIFICATION: no short-circuiting | ||
170 | // | ||
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()); | ||
183 | } | ||
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 ? falsefalse : truetrue); | ||
212 | } | ||
213 | case OPR_greater: { | ||
214 | int r = evaluateRelational(rhs, lhs); | ||
215 | return boxBoolean(r == -1 || r == 0 ? falsefalse : truetrue); | ||
216 | } | ||
217 | case OPR_lessOrEqual: { | ||
218 | int r = evaluateRelational(rhs, lhs); | ||
219 | return boxBoolean(r == -1 || r == 1 ? falsefalse : truetrue); | ||
220 | } | ||
221 | case OPR_greaterOrEqual: { | ||
222 | int r = evaluateRelational(lhs, rhs); | ||
223 | return boxBoolean(r == -1 || r == 1 ? falsefalse : truetrue); | ||
224 | } | ||
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)); | ||
233 | |||
234 | boolbool 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; | ||
239 | |||
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(truetrue == 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); | ||
250 | } | ||
251 | return boxBoolean(falsefalse == equality); | ||
252 | } | ||
253 | if (lhs->tag() == TAG_literalUndefined || lhs->tag() == TAG_literalNull) | ||
254 | return boxBoolean(truetrue == 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; | ||
264 | } | ||
265 | default: | ||
266 | // "as", "is", "in", ",", "=" | ||
267 | compiler->syntaxError(position(), SYNTAXERR_ILLEGAL_OP_IN_CONSTEXPR); | ||
268 | /*NOTREACHED*/ | ||
269 | break; | ||
270 | } | ||
271 | /*NOTREACHED*/ | ||
272 | break; | ||
273 | } | ||
274 | case TAG_unaryExpr: { | ||
275 | // EXTENSION: typeof | ||
276 | // | ||
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). | ||
281 | // | ||
282 | // EXTENSION: void | ||
283 | // | ||
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__null; | ||
301 | } | ||
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; | ||
312 | } | ||
313 | /*NOTREACHED*/ | ||
314 | break; | ||
315 | } | ||
316 | case TAG_conditionalExpr: { | ||
317 | // EXTENSION: conditional operator | ||
318 | // | ||
319 | // It seems totally sensible to support "... ? ... : ...", though | ||
320 | // it's not mentioned in the conditional compilation spec. | ||
321 | // | ||
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; | ||
329 | } | ||
330 | default: | ||
331 | // Property references, 'new', 'call' - lots of things | ||
332 | compiler->syntaxError(position(), SYNTAXERR_ILLEGAL_OP_IN_CONSTEXPR); | ||
333 | /*NOTREACHED*/ | ||
334 | break; | ||
335 | } | ||
336 | /*NOTREACHED*/ | ||
337 | return NULL__null; | ||
338 | } | ||
339 | |||
340 | Expr* Parser::boxDouble(double n) { return ALLOC(LiteralDouble, (n, 0))::new (allocator->alloc(sizeof(LiteralDouble))) LiteralDouble (n, 0); } | ||
341 | Expr* Parser::boxUInt(uint32_t n) { return ALLOC(LiteralUInt, (n, 0))::new (allocator->alloc(sizeof(LiteralUInt))) LiteralUInt ( n, 0); } | ||
342 | Expr* Parser::boxInt(int32_t n) { return ALLOC(LiteralInt, (n, 0))::new (allocator->alloc(sizeof(LiteralInt))) LiteralInt (n , 0); } | ||
343 | Expr* Parser::boxBoolean(boolbool b) { return ALLOC(LiteralBoolean, (b, 0))::new (allocator->alloc(sizeof(LiteralBoolean))) LiteralBoolean (b, 0); } | ||
344 | Expr* Parser::boxString(const char* s) { return ALLOC(LiteralString, (compiler->intern(s), 0))::new (allocator->alloc(sizeof(LiteralString))) LiteralString (compiler->intern(s), 0); } | ||
345 | Expr* Parser::boxString(Str* s) { return ALLOC(LiteralString, (s, 0))::new (allocator->alloc(sizeof(LiteralString))) LiteralString (s, 0); } | ||
346 | Expr* Parser::boxUndefined() { return ALLOC(LiteralUndefined, (0))::new (allocator->alloc(sizeof(LiteralUndefined))) LiteralUndefined (0); } | ||
347 | |||
348 | uint32_t Parser::evaluateToUInt32(Expr* e) | ||
349 | { | ||
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); | ||
358 | } | ||
359 | |||
360 | int32_t Parser::evaluateToInt32(Expr* e) | ||
361 | { | ||
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); | ||
373 | } | ||
374 | |||
375 | double Parser::evaluateToNumber(Expr* e) | ||
376 | { | ||
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; | ||
388 | } | ||
389 | } | ||
390 | |||
391 | boolbool Parser::evaluateToBoolean(Expr* e) | ||
392 | { | ||
393 | switch (e->tag()) { | ||
394 | case TAG_literalUndefined: return falsefalse; | ||
395 | case TAG_literalNull: return falsefalse; | ||
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; | ||
404 | } | ||
405 | } | ||
406 | |||
407 | Str* Parser::evaluateToString(Expr* e) | ||
408 | { | ||
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; | ||
420 | } | ||
421 | } | ||
422 | |||
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. | ||
425 | |||
426 | int Parser::evaluateRelational(Expr* lhs, Expr* rhs) | ||
427 | { | ||
428 | if (lhs->tag() == TAG_literalString && rhs->tag() == TAG_literalString) | ||
429 | return (((LiteralString*)lhs)->value)->compareTo(((LiteralString*)rhs)->value) < 0 ? 1 : 0; | ||
430 | |||
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; | ||
436 | } | ||
437 | |||
438 | void Parser::failNonConstant(Expr* e) | ||
439 | { | ||
440 | compiler->internalError(position(), "Non-constant value in expression evaluation, tag=%d", int(e->tag())); | ||
441 | } | ||
442 | } | ||
443 | } | ||
444 | |||
445 | #endif // VMCFG_EVAL |