Files
avmplus/eval/eval-parse-config.cpp
dmcallis 65a0592776 Initial source code drop
Source code drop for the Actionscript virtual machine in the Adobe
Flash Player for December 2013
2013-12-02 16:04:04 -08:00

427 lines
21 KiB
C++

/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "avmplus.h"
#ifdef VMCFG_EVAL
#include "eval.h"
namespace avmplus
{
namespace RTC
{
Parser::ConfigBinding::ConfigBinding(Str* ns, Str* name, Expr* value)
: ns(ns)
, name(name)
, value(value)
{
}
void Parser::addConfigNamespace(Str* ns)
{
configNamespaces = ALLOC(Seq<Str*>, (ns, configNamespaces));
}
void Parser::checkNoShadowingOfConfigNamespaces(uint32_t pos, Str* s)
{
Seq<Str*>* nss = configNamespaces;
while (nss != NULL) {
if (s == nss->hd)
compiler->syntaxError(pos, SYNTAXERR_CONFIG_NAMESPACE_SHADOWING);
nss = nss->tl;
}
}
void Parser::addConfigBinding(Str* ns, Str* name, Expr* value)
{
configBindings = ALLOC(Seq<ConfigBinding*>, (ALLOC(ConfigBinding, (ns, name, value)), configBindings));
}
bool Parser::findConfigNamespace(Str* ns)
{
Seq<Str*>* nss = configNamespaces;
while (nss != NULL) {
if (nss->hd == ns)
return true;
nss = nss->tl;
}
return false;
}
Expr* Parser::findConfigBinding(Str* ns, Str* name)
{
Seq<ConfigBinding*>* bs = configBindings;
while (bs != NULL) {
if (bs->hd->ns == ns && bs->hd->name == name)
return bs->hd->value;
bs = bs->tl;
}
return NULL;
}
bool Parser::isConfigReference(Expr* e)
{
if (e->tag() != TAG_qualifiedName)
return false;
QualifiedName* qn = (QualifiedName*)e;
if (qn->qualifier == NULL || qn->qualifier->tag() != TAG_simpleName || qn->name->tag() != TAG_simpleName)
return false;
SimpleName* sn = (SimpleName*)(qn->qualifier);
return findConfigNamespace(sn->name);
}
bool Parser::evaluateConfigReference(QualifiedName* qname)
{
if (qname == NULL)
return true;
AvmAssert(isConfigReference(qname));
Str* ns = ((SimpleName*)qname->qualifier)->name;
Str* name = ((SimpleName*)qname->name)->name;
Expr* value = findConfigBinding(ns, name);
if (value != NULL)
return evaluateToBoolean(value);
compiler->syntaxError(qname->pos, SYNTAXERR_UNBOUND_CONST_NAME);
/*NOTREACHED*/
return false;
}
// POSSIBLE EXTENSION: structured data and field access
//
// It would be possible to support array, object, xml, and vector initializers
// whose initializing subexpressions are either literal values or references
// to config variables, as well as field selections on those initializers.
// It's possible to imagine some use cases. We need some clean semantics for
// when those initializers escape into non-config code - do we want a single
// copy or multiple copies?
Expr* Parser::evaluateConfigDefinition(Str* ns, Expr* e)
{
// e is evaluated in an environment containing only config bindings.
// ns is the default namespace: it will be used to qualify any unqualified name.
switch (e->tag()) {
case TAG_literalUndefined:
case TAG_literalString:
case TAG_literalNull:
case TAG_literalUInt:
case TAG_literalInt:
case TAG_literalDouble:
case TAG_literalFloat:
case TAG_literalBoolean:
return e;
case TAG_simpleName: {
Expr* expr = findConfigBinding(ns, ((SimpleName*)e)->name);
if (expr == NULL)
compiler->syntaxError(expr->pos, SYNTAXERR_UNBOUND_CONST_NAME);
return expr;
}
case TAG_qualifiedName: {
QualifiedName* qname = (QualifiedName*)e;
if (qname->qualifier->tag() != TAG_simpleName || qname->name->tag() != TAG_simpleName)
compiler->syntaxError(e->pos, SYNTAXERR_UNBOUND_CONST_NAME); // Specifically an illegal const name
Str* ns = ((SimpleName*)qname->qualifier)->name;
Str* name = ((SimpleName*)qname->name)->name;
Expr* value = findConfigBinding(ns, name);
if (value == NULL)
compiler->syntaxError(e->pos, SYNTAXERR_UNBOUND_CONST_NAME);
return value;
}
case TAG_binaryExpr: {
// CLARIFICATION: no short-circuiting
//
// We evaluate both sides of && and || in order to uncover
// any undefined variables lurking in non-taken branches.
BinaryExpr* binary = (BinaryExpr*)e;
Expr* lhs = evaluateConfigDefinition(ns, binary->lhs);
Expr* rhs = evaluateConfigDefinition(ns, binary->rhs);
switch (binary->op) {
case OPR_plus:
if (lhs->tag() == TAG_literalString || rhs->tag() == TAG_literalString) {
StringBuilder b(compiler);
b.append(((LiteralString*)lhs)->value);
b.append(((LiteralString*)rhs)->value);
return boxString(b.str());
}
return boxDouble(evaluateToNumber(lhs) + evaluateToNumber(rhs));
case OPR_minus:
return boxDouble(evaluateToNumber(lhs) - evaluateToNumber(rhs));
case OPR_multiply:
return boxDouble(evaluateToNumber(lhs) * evaluateToNumber(rhs));
case OPR_divide:
return boxDouble(evaluateToNumber(lhs) / evaluateToNumber(rhs));
case OPR_remainder:
return boxDouble(fmod(evaluateToNumber(lhs), evaluateToNumber(rhs)));
case OPR_leftShift:
return boxInt(evaluateToInt32(lhs) << (evaluateToUInt32(rhs) & 0x1F));
case OPR_rightShift:
return boxInt(evaluateToInt32(lhs) >> (evaluateToUInt32(rhs) & 0x1F));
case OPR_rightShiftUnsigned:
return boxUInt(evaluateToUInt32(lhs) >> (evaluateToUInt32(rhs) & 0x1F));
case OPR_bitwiseAnd:
return boxInt(evaluateToInt32(lhs) & evaluateToInt32(rhs));
case OPR_bitwiseOr:
return boxInt(evaluateToInt32(lhs) | evaluateToInt32(rhs));
case OPR_bitwiseXor:
return boxInt(evaluateToInt32(lhs) ^ evaluateToInt32(rhs));
case OPR_logicalAnd:
return boxBoolean(int(evaluateToBoolean(lhs)) + int(evaluateToBoolean(rhs)) == 2);
case OPR_logicalOr:
return boxBoolean(int(evaluateToBoolean(lhs)) + int(evaluateToBoolean(rhs)) != 0);
case OPR_less: {
int r = evaluateRelational(lhs, rhs);
return boxBoolean(r == -1 || r == 0 ? false : true);
}
case OPR_greater: {
int r = evaluateRelational(rhs, lhs);
return boxBoolean(r == -1 || r == 0 ? false : true);
}
case OPR_lessOrEqual: {
int r = evaluateRelational(rhs, lhs);
return boxBoolean(r == -1 || r == 1 ? false : true);
}
case OPR_greaterOrEqual: {
int r = evaluateRelational(lhs, rhs);
return boxBoolean(r == -1 || r == 1 ? false : true);
}
case OPR_equal:
case OPR_notEqual:
case OPR_strictEqual:
case OPR_strictNotEqual: {
if (lhs->tag() == TAG_literalInt || lhs->tag() == TAG_literalUInt)
lhs = boxDouble(evaluateToNumber(lhs));
if (rhs->tag() == TAG_literalInt || rhs->tag() == TAG_literalUInt)
rhs = boxDouble(evaluateToNumber(rhs));
bool equality;
if (binary->op == OPR_equal || binary->op == OPR_notEqual)
equality = binary->op == OPR_equal;
else
equality = binary->op == OPR_strictEqual;
if (lhs->tag() != rhs->tag()) {
if (binary->op == OPR_equal || binary->op == OPR_notEqual) {
if ((lhs->tag() == TAG_literalUndefined && rhs->tag() == TAG_literalNull) ||
(lhs->tag() == TAG_literalNull && rhs->tag() == TAG_literalUndefined))
return boxBoolean(true == equality);
if ((lhs->tag() == TAG_literalString && rhs->tag() == TAG_literalDouble) ||
(lhs->tag() == TAG_literalDouble && rhs->tag() == TAG_literalString))
return boxBoolean((evaluateToNumber(lhs) == evaluateToNumber(rhs)) == equality);
if (lhs->tag() == TAG_literalBoolean || rhs->tag() == TAG_literalBoolean)
return boxBoolean((evaluateToBoolean(lhs) == evaluateToBoolean(rhs)) == equality);
}
return boxBoolean(false == equality);
}
if (lhs->tag() == TAG_literalUndefined || lhs->tag() == TAG_literalNull)
return boxBoolean(true == equality);
if (lhs->tag() == TAG_literalDouble)
return boxBoolean((evaluateToNumber(lhs) == evaluateToNumber(rhs)) == equality);
if (lhs->tag() == TAG_literalBoolean)
return boxBoolean((evaluateToBoolean(lhs) == evaluateToBoolean(rhs)) == equality);
if (lhs->tag() == TAG_literalString)
return boxBoolean((evaluateToString(lhs) == evaluateToString(rhs)) == equality);
failNonConstant(lhs);
/*NOTREACHED*/
break;
}
default:
// "as", "is", "in", ",", "="
compiler->syntaxError(position(), SYNTAXERR_ILLEGAL_OP_IN_CONSTEXPR);
/*NOTREACHED*/
break;
}
/*NOTREACHED*/
break;
}
case TAG_unaryExpr: {
// EXTENSION: typeof
//
// Supporting "typeof" makes some sort of sense (for example, the
// operand of typeof can be a config constant that can take on
// various values, and computing the name of the type into the
// program can be useful).
//
// EXTENSION: void
//
// Supporting "void" probably does not make a lot of sense, but it
// seems benign.
UnaryExpr* unary = (UnaryExpr*)e;
Expr* opd = evaluateConfigDefinition(ns, unary->expr);
switch (unary->op) {
case OPR_typeof:
switch (opd->tag()) {
case TAG_literalUndefined: return boxString("undefined");
case TAG_literalString: return boxString("string");
case TAG_literalNull: return boxString("object");
case TAG_literalUInt:
case TAG_literalInt:
case TAG_literalDouble: return boxString("number");
case TAG_literalFloat: return boxString("number");
case TAG_literalBoolean: return boxString("boolean");
default:
failNonConstant(opd);
return NULL;
}
case OPR_bitwiseNot: return boxUInt(~evaluateToUInt32(opd));
case OPR_unminus: return boxDouble(-evaluateToNumber(opd));
case OPR_unplus: return boxDouble(evaluateToNumber(opd));
case OPR_not: return boxBoolean(!evaluateToBoolean(opd));
case OPR_void: return boxUndefined();
default:
// "delete", "++", "--"
compiler->syntaxError(position(), SYNTAXERR_ILLEGAL_OP_IN_CONSTEXPR);
/*NOTREACHED*/
break;
}
/*NOTREACHED*/
break;
}
case TAG_conditionalExpr: {
// EXTENSION: conditional operator
//
// It seems totally sensible to support "... ? ... : ...", though
// it's not mentioned in the conditional compilation spec.
//
// We evaluate both arms in order to uncover references to undefined
// configuration variables, same as for && and ||.
ConditionalExpr* cond = (ConditionalExpr*)e;
Expr* e1 = evaluateConfigDefinition(ns, cond->e1);
Expr* e2 = evaluateConfigDefinition(ns, cond->e2);
Expr* e3 = evaluateConfigDefinition(ns, cond->e3);
return evaluateToBoolean(e1) ? e2 : e3;
}
default:
// Property references, 'new', 'call' - lots of things
compiler->syntaxError(position(), SYNTAXERR_ILLEGAL_OP_IN_CONSTEXPR);
/*NOTREACHED*/
break;
}
/*NOTREACHED*/
return NULL;
}
Expr* Parser::boxDouble(double n) { return ALLOC(LiteralDouble, (n, 0)); }
#ifdef VMCFG_FLOAT
Expr* Parser::boxFloat(float n) { return ALLOC(LiteralFloat, (n, 0)); }
#endif
Expr* Parser::boxUInt(uint32_t n) { return ALLOC(LiteralUInt, (n, 0)); }
Expr* Parser::boxInt(int32_t n) { return ALLOC(LiteralInt, (n, 0)); }
Expr* Parser::boxBoolean(bool b) { return ALLOC(LiteralBoolean, (b, 0)); }
Expr* Parser::boxString(const char* s) { return ALLOC(LiteralString, (compiler->intern(s), 0)); }
Expr* Parser::boxString(Str* s) { return ALLOC(LiteralString, (s, 0)); }
Expr* Parser::boxUndefined() { return ALLOC(LiteralUndefined, (0)); }
uint32_t Parser::evaluateToUInt32(Expr* e)
{
if (e->tag() == TAG_literalUInt)
return ((LiteralUInt*)e)->value;
double d = evaluateToNumber(e);
if (d == 0 || MathUtils::isNaN(d) || MathUtils::isInfinite(d))
return 0;
d = (d < 0 ? -1 : 1) * floor(fabs(d));
d = fmod(d, 4294967296.0);
return uint32_t(d);
}
int32_t Parser::evaluateToInt32(Expr* e)
{
if (e->tag() == TAG_literalInt)
return ((LiteralInt*)e)->value;
double d = evaluateToNumber(e);
if (d == 0 || MathUtils::isNaN(d) || MathUtils::isInfinite(d))
return 0;
d = (d < 0 ? -1 : 1) * floor(fabs(d));
d = fmod(d, 4294967296.0);
if (d >= 2147483648.0)
return int32_t(d - 4294967296.0);
else
return int32_t(d);
}
double Parser::evaluateToNumber(Expr* e)
{
switch (e->tag()) {
case TAG_literalUndefined: return MathUtils::kNaN;
case TAG_literalNull: return 0.0;
case TAG_literalBoolean: return ((LiteralBoolean*)e)->value ? 1.0 : 0.0;
case TAG_literalDouble: return ((LiteralDouble*)e)->value;
#ifdef VMCFG_FLOAT
case TAG_literalFloat: return ((LiteralFloat*)e)->value; // FIXME?
#endif
case TAG_literalInt: return (double)(((LiteralInt*)e)->value);
case TAG_literalUInt: return (double)(((LiteralUInt*)e)->value);
case TAG_literalString: return strToDouble(((LiteralString*)e)->value);
default:
failNonConstant(e);
return 0;
}
}
bool Parser::evaluateToBoolean(Expr* e)
{
switch (e->tag()) {
case TAG_literalUndefined: return false;
case TAG_literalNull: return false;
case TAG_literalBoolean: return ((LiteralBoolean*)e)->value;
case TAG_literalDouble: { double v = ((LiteralDouble*)e)->value; return !MathUtils::isNaN(v) && v != 0.0; }
#ifdef VMCFG_FLOAT
case TAG_literalFloat: { float v = ((LiteralFloat*)e)->value; return !MathUtils::isNaNf(v) && v != 0.0f; }
#endif
case TAG_literalInt: return ((LiteralInt*)e)->value != 0;
case TAG_literalUInt: return ((LiteralUInt*)e)->value != 0;
case TAG_literalString: return ((LiteralString*)e)->value->length > 0;
default:
failNonConstant(e);
return 0;
}
}
Str* Parser::evaluateToString(Expr* e)
{
switch (e->tag()) {
case TAG_literalUndefined: return compiler->intern("undefined");
case TAG_literalNull: return compiler->intern("null");
case TAG_literalBoolean: return ((LiteralBoolean*)e)->value ? compiler->intern("true") : compiler->intern("false");
case TAG_literalDouble: return doubleToStr(((LiteralDouble*)e)->value);
#ifdef VMCFG_FLOAT
case TAG_literalFloat: return doubleToStr(((LiteralFloat*)e)->value);
#endif
case TAG_literalInt: return doubleToStr(((LiteralInt*)e)->value);
case TAG_literalUInt: return doubleToStr(((LiteralUInt*)e)->value);
case TAG_literalString: return ((LiteralString*)e)->value;
default:
failNonConstant(e);
return 0;
}
}
// Returns -1 for undefined, 0 for false, 1 for true. Generally true means "x < y",
// false means "!(x < y)" and undefined means x and y are not comparable.
int Parser::evaluateRelational(Expr* lhs, Expr* rhs)
{
if (lhs->tag() == TAG_literalString && rhs->tag() == TAG_literalString)
return (((LiteralString*)lhs)->value)->compareTo(((LiteralString*)rhs)->value) < 0 ? 1 : 0;
double l = evaluateToNumber(lhs);
double r = evaluateToNumber(rhs);
if (MathUtils::isNaN(l) || MathUtils::isNaN(r))
return -1;
return l < r ? 1 : 0;
}
void Parser::failNonConstant(Expr* e)
{
compiler->internalError(position(), "Non-constant value in expression evaluation, tag=%d", int(e->tag()));
}
}
}
#endif // VMCFG_EVAL