Files
avmplus/eval/eval-parse.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

1392 lines
56 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
{
/*
static const struct TokenMapping {
unsigned isOpAssign:1;
unsigned isMultiplicative:1;
unsigned isAdditive:1;
unsigned isRelational:1;
unsigned isEquality:1;
unsigned isShift:1;
unsigned unaryOp:8;
unsigned binaryOp:8;
};
*/
const Parser::TokenMapping Parser::tokenMapping[] = {
{ 0, 0, 0, 1, 0, 0, 0, OPR_as }, // T_As
{ 0, 0, 0, 0, 0, 0, 0, OPR_assign }, // T_Assign,
{ 0, 0, 0, 0, 0, 0, 0, OPR_bitwiseAnd }, // T_BitwiseAnd,
{ 1, 0, 0, 0, 0, 0, 0, OPR_bitwiseAnd }, // T_BitwiseAndAssign,
{ 0, 0, 0, 0, 0, 0, OPR_bitwiseNot, 0 }, // T_BitwiseNot,
{ 0, 0, 0, 0, 0, 0, 0, OPR_bitwiseOr }, // T_BitwiseOr,
{ 1, 0, 0, 0, 0, 0, 0, OPR_bitwiseOr }, // T_BitwiseOrAssign,
{ 0, 0, 0, 0, 0, 0, 0, OPR_bitwiseXor }, // T_BitwiseXor,
{ 1, 0, 0, 0, 0, 0, 0, OPR_bitwiseXor }, // T_BitwiseXorAssign,
{ 0, 0, 0, 0, 0, 0, OPR_delete, 0 }, // T_Delete,
{ 0, 1, 0, 0, 0, 0, 0, OPR_divide }, // T_Divide,
{ 1, 0, 0, 0, 0, 0, 0, OPR_divide }, // T_DivideAssign,
{ 0, 0, 0, 0, 1, 0, 0, OPR_equal }, // T_Equal,
{ 0, 0, 0, 1, 0, 0, 0, OPR_greater }, // T_GreaterThan,
{ 0, 0, 0, 1, 0, 0, 0, OPR_greaterOrEqual }, // T_GreaterThanOrEqual,
{ 0, 0, 0, 1, 0, 0, 0, OPR_in }, // T_In,
{ 0, 0, 0, 1, 0, 0, 0, OPR_instanceof }, // T_InstanceOf,
{ 0, 0, 0, 1, 0, 0, 0, OPR_is }, // T_Is,
{ 0, 0, 0, 0, 0, 1, 0, OPR_leftShift }, // T_LeftShift,
{ 1, 0, 0, 0, 0, 0, 0, OPR_leftShift }, // T_LeftShiftAssign,
{ 0, 0, 0, 1, 0, 0, 0, OPR_less }, // T_LessThan,
{ 0, 0, 0, 1, 0, 0, 0, OPR_lessOrEqual }, // T_LessThanOrEqual,
{ 0, 0, 0, 0, 0, 0, 0, OPR_logicalAnd }, // T_LogicalAnd,
{ 1, 0, 0, 0, 0, 0, 0, OPR_logicalAnd }, // T_LogicalAndAssign,
{ 0, 0, 0, 0, 0, 0, 0, OPR_logicalOr }, // T_LogicalOr,
{ 1, 0, 0, 0, 0, 0, 0, OPR_logicalOr }, // T_LogicalOrAssign,
{ 0, 0, 1, 0, 0, 0, OPR_unminus, OPR_minus }, // T_Minus,
{ 1, 0, 0, 0, 0, 0, 0, OPR_minus }, // T_MinusAssign,
{ 0, 0, 0, 0, 0, 0, OPR_preDecr, OPR_postDecr }, // T_MinusMinus, note binop slot encodes postfix form
{ 0, 1, 0, 0, 0, 0, 0, OPR_multiply }, // T_Multiply,
{ 1, 0, 0, 0, 0, 0, 0, OPR_multiply }, // T_MultiplyAssign,
{ 0, 0, 0, 0, 0, 0, OPR_not, 0 }, // T_Not,
{ 0, 0, 0, 0, 1, 0, 0, OPR_notEqual }, // T_NotEqual,
{ 0, 0, 1, 0, 0, 0, OPR_unplus, OPR_plus }, // T_Plus,
{ 1, 0, 0, 0, 0, 0, 0, OPR_plus }, // T_PlusAssign,
{ 0, 0, 0, 0, 0, 0, OPR_preIncr, OPR_postIncr }, // T_PlusPlus, note binop slot encodes postfix form
{ 0, 1, 0, 0, 0, 0, 0, OPR_remainder }, // T_Remainder,
{ 1, 0, 0, 0, 0, 0, 0, OPR_remainder }, // T_RemainderAssign,
{ 0, 0, 0, 0, 0, 1, 0, OPR_rightShift }, // T_RightShift,
{ 1, 0, 0, 0, 0, 0, 0, OPR_rightShift }, // T_RightShiftAssign,
{ 0, 0, 0, 0, 1, 0, 0, OPR_strictEqual }, // T_StrictEqual,
{ 0, 0, 0, 0, 1, 0, 0, OPR_strictNotEqual }, // T_StrictNotEqual,
{ 0, 0, 0, 0, 0, 0, OPR_typeof, 0 }, // T_TypeOf,
{ 0, 0, 0, 0, 1, 0, 0, OPR_rightShiftUnsigned }, // T_UnsignedRightShift,
{ 1, 0, 0, 0, 0, 0, 0, OPR_rightShiftUnsigned }, // T_UnsignedRightShiftAssign,
{ 0, 0, 0, 0, 0, 0, OPR_void, 0 } // T_Void,
};
Parser::BindingRib::BindingRib(Allocator* allocator, BindingRib* next, RibType tag)
: tag(tag)
, signature(NULL)
, body(allocator)
, next(next)
{
}
Parser::Parser(Compiler* compiler, Lexer* lexer, bool public_by_default, uint32_t first_line)
: compiler(compiler)
, allocator(compiler->allocator)
, public_by_default(public_by_default)
, line_offset(first_line-1)
, internalNS(ALLOC(BuiltinNamespace, (T_Internal)))
, privateNS(ALLOC(BuiltinNamespace, (T_Private)))
, protectedNS(ALLOC(BuiltinNamespace, (T_Protected)))
, publicNS(ALLOC(BuiltinNamespace, (T_Public)))
, topRib(NULL)
, classes(allocator)
, configNamespaces(NULL)
, configBindings(NULL)
, lexerStack(NULL)
, lexer(lexer)
, T0(T_LAST)
, T1(T_LAST)
, LP(0)
, L0(0)
, L1(0)
, included_input(NULL)
{
}
Program* Parser::parse()
{
start();
return program();
}
/*
* <program> ::= <package>* <directive>*
*/
Program* Parser::program()
{
addConfigNamespace(compiler->SYM_CONFIG);
pushBindingRib(RIB_Program);
addNamespaceBinding(defaultNamespace(), compiler->SYM_AS3, ALLOC(LiteralString, (compiler->intern("http://adobe.com/AS3/2006/builtin"), 0)));
while (hd() == T_Package)
package();
directives(SFLAG_Toplevel);
Program* prog = ALLOC(Program, (topRib->body, classes.get()));
popBindingRib();
return prog;
}
// FIXME: must open the package namespaces inside the package
// FIXME: must record the package name as a compound name prefix inside the package
// FIXME: must record that we're inside a package so that namespace qualifiers can be checked
void Parser::package()
{
SeqBuilder<Str*> name(allocator);
eat(T_Package);
while (hd() == T_Identifier) {
name.addAtEnd(identValue());
eat(T_Identifier);
if (!match(T_Dot))
break;
}
eat(T_LeftBrace);
directives(SFLAG_Package);
eat(T_RightBrace);
}
void Parser::directives(int flags)
{
SeqBuilder<Stmt*>* instanceStmts = &(topRib->body.stmts);
SeqBuilder<Stmt*>* classStmts = (flags & SFLAG_Class) ? &(topRib->next->body.stmts) : NULL;
// Directive parser machine state.
SeqBuilder<Expr*> metadatas(allocator); // Metadata expressions, in order
QualifiedName* configname; // Non-NULL if we have a config name, will also appear in metadatas
Qualifier qual; // ns / native / static / prototype / dynamic / override / committed metadata
bool metadata; // We're still looking at metadata and have not moved to qualifiers
bool committed; // We're committed to parsing a directive
// Working storage
Expr* e; // Pending expression on entry to expr_statement
next_directive:
metadatas.clear();
configname = NULL;
qual = Qualifier();
metadata = true;
committed = false;
e = NULL;
switch (hd()) {
//case T_Import: goto import_directive;
case T_Include: goto include_directive;
//case T_Use: goto use_directive;
//case T_Package: goto package_directive;
case T_Identifier: {
if (identValue() == compiler->SYM_config && hd2() == T_Namespace && L0 == L1)
goto config_namespace_directive;
goto continue_directive;
}
default: goto continue_directive;
}
continue_directive:
if (hd() == T_RightBrace || hd() == T_EOS) {
if (committed)
compiler->syntaxError(position(), SYNTAXERR_DIRECTIVE_REQUIRED);
addExprStatements((flags & SFLAG_Class) ? classStmts : instanceStmts, metadatas.get());
goto finish;
}
switch (hd())
{
case T_LeftBracket: goto array_metadata;
case T_BreakLeftAngle: goto xml_metadata;
case T_Private: goto special_namespace_qualifier;
case T_Protected: goto special_namespace_qualifier;
case T_Public: goto special_namespace_qualifier;
case T_Internal: goto special_namespace_qualifier;
case T_Dynamic: goto dynamic_qualifier;
case T_Final: goto final_qualifier;
case T_Native: goto native_qualifier;
case T_Override: goto override_qualifier;
case T_Static: goto static_qualifier;
case T_Namespace: goto namespace_directive;
case T_Const: goto const_or_var_directive;
case T_Var: goto const_or_var_directive;
case T_Function: goto function_directive;
case T_Class: goto class_directive;
case T_Interface: goto interface_directive;
case T_Identifier: goto configname_or_namespacename_or_statement;
default: goto statement;
}
xml_metadata: {
// Could be xml-literal metadata or expression statement.
//
// Must parse full expression here in case it's not metadata, eg,
// <foo></foo> + "bar"
// is a valid statement.
if (!metadata)
compiler->syntaxError(position(), SYNTAXERR_METADATA_NOT_ALLOWED);
e = commaExpression(0);
if (e->tag() == TAG_literalXml) {
committed = true;
metadatas.addAtEnd(e);
e = NULL;
goto continue_directive;
}
// Not metadata after all
goto expr_statement;
}
array_metadata: {
// Could be array-literal metadata or expression statement.
//
// Must parse full expression here in case it's not metadata, eg,
// [native(a,b,c)] + " = " + x
// or
// [a,b,c] = 10
// are valid statements (or will be).
if (!metadata)
compiler->syntaxError(position(), SYNTAXERR_METADATA_NOT_ALLOWED);
e = commaExpression(0);
if (e->tag() == TAG_literalArray) {
committed = true;
metadatas.addAtEnd(e);
e = NULL;
goto continue_directive;
}
// Not metadata after all
goto expr_statement;
}
configname_or_namespacename_or_statement: {
// Here we're looking at some identifier. It can be the start of: a config name
// reference (always of the form NS::v), a namespace reference (anything from a
// simple unqualified name through a string of namespace qualifiers and finally a
// name), a labeled statement, or an expression statement. The special names
// private, public, protected, and internal do not appear.
//
// If it's followed by a colon then it's a label. Otherwise we must parse it as
// a full comma expression and then start disambiguating.
//
// In the AST, a simple unqualified name is a QualifiedName with a NULL qualifier
// and a SimpleName for a name.
if (hd2() == T_Colon)
goto statement; // labeled statement
e = commaExpression(0);
if (e->tag() != TAG_qualifiedName)
goto expr_statement;
if (newline() || hd() == T_Semicolon || hd() == T_RightBrace)
goto configname_or_statement;
if (isConfigReference(e))
goto configname;
goto namespace_or_statement;
configname_or_statement:
if (!isConfigReference(e))
goto expr_statement;
goto configname;
configname:
if (!metadata)
compiler->syntaxError(position(), SYNTAXERR_DIRECTIVE_REQUIRED);
if (configname != NULL) {
// Flush up to and including previous configname
while (!metadatas.isEmpty()) {
Expr* p = metadatas.dequeue();
if (p == NULL)
break; // Oops - really an error
((flags & SFLAG_Class) ? classStmts : instanceStmts)->addAtEnd(ALLOC(ExprStmt, (e->pos, e)));
if (e == configname)
break;
}
}
configname = (QualifiedName*)e;
metadatas.addAtEnd(e);
e = NULL;
if (!newline())
committed = true;
goto continue_directive;
expr_statement:
// When we get here there must be an expr in e that is
// an expression statement. We must not be committed,
// but there can be metadatas - those are also expression
// statements that were newline-terminated. We have not
// consumed any semicolon at the end of the current
// expression statement.
AvmAssert(e != NULL);
if (committed)
compiler->syntaxError(position(), SYNTAXERR_DIRECTIVE_REQUIRED);
semicolon();
metadatas.addAtEnd(e);
addExprStatements((flags & SFLAG_Class) ? classStmts : instanceStmts, metadatas.get());
goto next_directive;
namespace_or_statement:
AvmAssert(!newline());
if (!isNamespaceReference(e))
goto expr_statement;
if (qual.tag != QUAL_none)
compiler->syntaxError(position(), SYNTAXERR_DUPLICATE_QUALIFIER);
qual.tag = QUAL_name;
qual.name = (QualifiedName*)e;
committed = true;
metadata = false;
e = NULL;
goto continue_directive;
}
special_namespace_qualifier: {
// The namespace qualifiers public, private, protected, and internal can be used
// either by themselves as qualifiers or as part of a qualified name, eg,
// 'internal::foo'. In the latter case, we also risk looking at an expression
// statement, eg, 'internal::foo() + fnord'. However, the namespace qualifiers
// are not themselves valid expressions, so we can't blithely parse an expression
// here. We must instead look ahead: if we're seeing '::' next then we can parse
// an expression and then disambiguate between a qualified namespace name or
// an expression statement, otherwise we take the keyword to be a qualifier.
//
// Configuration names do not appear here because private, public, protected, and
// internal never denote a configuration namespace.
Token tok = hd();
QualifierTag t = QUAL_none;
if (hd2() != T_DoubleColon) {
next();
switch (tok) {
case T_Private: t = QUAL_private; break;
case T_Public: t = QUAL_public; break;
case T_Protected: t = QUAL_protected; break;
case T_Internal: t = QUAL_internal; break;
}
goto consumed_special_namespace_qualifier;
}
{
Expr* e = commaExpression(0);
if (e->tag() != TAG_qualifiedName || newline()) {
if (committed)
compiler->syntaxError(position(), SYNTAXERR_DIRECTIVE_REQUIRED);
// Expression statement
semicolon();
metadatas.addAtEnd(e);
addExprStatements((flags & SFLAG_Class) ? classStmts : instanceStmts, metadatas.get());
goto next_directive;
}
t = QUAL_name;
qual.name = (QualifiedName*)e;
goto consumed_special_namespace_qualifier;
}
consumed_special_namespace_qualifier:
if (qual.tag != QUAL_none)
compiler->syntaxError(position(), SYNTAXERR_KWD_NOT_ALLOWED, "private");
qual.tag = t;
committed = true;
metadata = false;
goto continue_directive;
}
dynamic_qualifier: {
qual.is_dynamic++;
goto consume_simple_qualifier;
}
final_qualifier: {
qual.is_final++;
goto consume_simple_qualifier;
}
native_qualifier: {
qual.is_native++;
goto consume_simple_qualifier;
}
override_qualifier: {
qual.is_override++;
goto consume_simple_qualifier;
}
static_qualifier: {
qual.is_static++;
goto consume_simple_qualifier;
}
consume_simple_qualifier: {
next();
committed = true;
metadata = false;
goto continue_directive;
}
config_namespace_directive: {
// No attributes or metadata will have been parsed
eat(T_Identifier);
eat(T_Namespace);
if (!(flags & SFLAG_Toplevel))
compiler->syntaxError(position(), SYNTAXERR_CONFIG_NAMESPACE_NOT_ALLOWED);
configNamespaceDefinition(flags, evaluateConfigReference(configname));
goto next_directive;
}
include_directive: {
// No attributes or metadata will have been parsed
includeDirective();
goto next_directive;
}
namespace_directive: {
checkSimpleAttributes(&qual);
if (!(flags & (SFLAG_Function|SFLAG_Toplevel|SFLAG_Package|SFLAG_Class)))
compiler->syntaxError(position(), SYNTAXERR_ILLEGAL_NAMESPACE);
if (qual.is_dynamic || qual.is_final || qual.is_native || qual.is_override || qual.is_static)
compiler->syntaxError(position(), SYNTAXERR_ILLEGAL_NAMESPACE);
namespaceDefinition(evaluateConfigReference(configname), flags, &qual);
goto next_directive;
}
const_or_var_directive: {
checkSimpleAttributes(&qual);
if (flags & SFLAG_Interface)
compiler->syntaxError(position(), SYNTAXERR_ILLEGAL_IN_INTERFACE);
if (qual.is_dynamic || qual.is_final || qual.is_native || qual.is_override ||
(qual.is_static && !(flags & SFLAG_Class)) ||
(qual.tag != QUAL_none && !(flags & (SFLAG_Toplevel|SFLAG_Package|SFLAG_Class))))
compiler->syntaxError(position(), SYNTAXERR_QUALIFIER_NOT_ALLOWED);
// FIXME: if const, and at the top level, and the namespace is a config namespace, evaluate and define a config const
((flags & SFLAG_Class) && qual.is_static ? classStmts : instanceStmts)->addAtEnd(variableDefinition(evaluateConfigReference(configname), &qual));
goto next_directive;
}
function_directive: {
checkSimpleAttributes(&qual);
if (qual.is_dynamic ||
((qual.is_final || qual.is_override || qual.is_static) && !(flags & SFLAG_Class)) ||
(qual.tag != QUAL_none && !(flags & (SFLAG_Toplevel|SFLAG_Package|SFLAG_Class))))
compiler->syntaxError(position(), SYNTAXERR_QUALIFIER_NOT_ALLOWED);
functionDefinition(evaluateConfigReference(configname), &qual, (flags & SFLAG_Class) != 0, (flags & SFLAG_Interface) == 0);
goto next_directive;
}
class_directive: {
checkSimpleAttributes(&qual);
if (!(flags & (SFLAG_Toplevel|SFLAG_Package)))
compiler->syntaxError(position(), SYNTAXERR_CLASS_NOT_ALLOWED);
if (qual.is_native || qual.is_override || qual.is_static)
compiler->syntaxError(position(), SYNTAXERR_QUALIFIER_NOT_ALLOWED);
classDefinition(evaluateConfigReference(configname), flags, &qual);
goto next_directive;
}
interface_directive: {
checkSimpleAttributes(&qual);
if (!(flags & (SFLAG_Toplevel|SFLAG_Package)))
compiler->syntaxError(position(), SYNTAXERR_INTERFACE_NOT_ALLOWED);
if (qual.is_dynamic || qual.is_final || qual.is_native || qual.is_override || qual.is_static ||
(qual.tag != QUAL_none && qual.tag != QUAL_public))
compiler->syntaxError(position(), SYNTAXERR_QUALIFIER_NOT_ALLOWED);
interfaceDefinition(evaluateConfigReference(configname), flags, &qual);
goto next_directive;
}
statement: {
AvmAssert(e == NULL);
if (committed)
compiler->syntaxError(position(), SYNTAXERR_DIRECTIVE_REQUIRED);
addExprStatements((flags & SFLAG_Class) ? classStmts : instanceStmts, metadatas.get());
if (flags & SFLAG_Interface)
compiler->syntaxError(position(), SYNTAXERR_STMT_IN_INTERFACE);
// FIXME: is this test right?
if (qual.tag != QUAL_none)
compiler->syntaxError(position(), SYNTAXERR_ILLEGAL_STMT);
if (configname != NULL && hd() != T_LeftBrace)
compiler->syntaxError(position(), SYNTAXERR_CONFIG_PROHIBITED);
((flags & SFLAG_Class) ? classStmts : instanceStmts)->addAtEnd(statement(evaluateConfigReference(configname)));
goto next_directive;
}
finish: ;
}
void Parser::checkSimpleAttributes(Qualifier* qual)
{
// FIXME: if the defining keyword is not const, and a namespace is present,
// then that namespace must not be a config namespace.
if ((qual->is_dynamic > 1 || qual->is_final > 1 || qual->is_native > 1 || qual->is_override > 1 || qual->is_static > 1) ||
(qual->is_static + qual->is_final > 1) ||
(qual->is_static + qual->is_override > 1) ||
(qual->is_static + qual->is_dynamic > 1))
compiler->syntaxError(position(), SYNTAXERR_QUALIFIER_NOT_ALLOWED);
}
// FIXME: This is incorrect because QualifiedName currently does not admit a::b::c
bool Parser::isNamespaceReference(Expr* e)
{
if (e->tag() != TAG_qualifiedName)
return false;
QualifiedName* qn = (QualifiedName*)e;
if (qn->name->tag() != TAG_simpleName)
return false;
if (qn->qualifier != NULL && qn->qualifier->tag() != TAG_simpleName)
return false;
return true;
}
void Parser::addExprStatements(SeqBuilder<Stmt*>* stmts, Seq<Expr*>* exprs)
{
while (exprs != NULL) {
Expr* e = exprs->hd;
exprs = exprs->tl;
stmts->addAtEnd(ALLOC(ExprStmt, (e->pos, e)));
}
}
// Qualifiers are known to be appropriate for 'class'.
void Parser::classDefinition(bool config, int /*flags*/, Qualifier* qual)
{
(void)config;
eat(T_Class);
uint32_t pos = position();
Str* name = identifier();
checkNoShadowingOfConfigNamespaces(pos, name);
Str* extends = NULL;
SeqBuilder<Str*> implements(allocator);
if (hd() == T_Identifier && identValue() == compiler->SYM_extends) {
next();
extends = identifier();
}
if (hd() == T_Identifier && identValue() == compiler->SYM_implements) {
next();
do {
implements.addAtEnd(identifier());
} while (match(T_Comma));
}
eat(T_LeftBrace);
pushBindingRib(RIB_Class);
pushBindingRib(RIB_Instance);
directives(SFLAG_Class);
// The initializing statements of the class constructor are hoisted
// and become cls_init1, these are executed in an outer scope. Any other
// statements are part of the constructor and executed after the super stmt,
// and are within the constructor body. This means that during parsing we
// must distinguish between initializing stores and other statements. We hack:
// just filter the statements after the fact, variable definition stmts are
// distinguishable from other statements.
//
// FIXME: the hack is somehow wrong, consider
//
// class C {
// L1: var c = v;
// }
//
// where "c = v" is part of the instance initializer. Now put it in a (static) loop.
// It should not be executed by that loop, but it is.
BodyInfo* cls = &topRib->next->body;
Seq<FunctionDefn*>* cls_methods = cls->functionDefinitions.get();
Seq<Binding*>* cls_vars = bindingsToVars(cls->bindings.get());
Seq<NamespaceDefn*>* cls_namespaces = cls->namespaces.get();
Seq<Stmt*>* static_stmts = NULL;
Seq<Stmt*>* cls_init_stmts = filterStatements(cls->stmts.get(), &static_stmts);
FunctionDefn* cls_init = constructClassConstructor(pos, name, cls, cls_init_stmts, static_stmts);
// For the instance constructor only the initializing stores appear in the
// statements list; this list becomes init1, whereas the constructor (user-provided
// or hand-written) becomes init2.
BodyInfo* instance = &topRib->body;
FunctionDefn* constructor = NULL;
Seq<FunctionDefn*>* inst_methods = filterConstructor(name, instance->functionDefinitions.get(), &constructor);
Seq<Binding*>* inst_vars = bindingsToVars(instance->bindings.get());
Seq<Stmt*>* inst_init_stmts = instance->stmts.get();
FunctionDefn* inst_init = constructInstanceConstructor(pos, name, inst_init_stmts, constructor);
ClassDefn* clsobj = ALLOC(ClassDefn, (pos,
qualToNamespace(qual), name,
qual->is_final > 0, qual->is_dynamic > 0,
extends, implements.get(),
cls_methods, cls_vars, cls_namespaces, cls_init,
inst_methods, inst_vars, inst_init));
popBindingRib();
popBindingRib();
eat(T_RightBrace);
addClass(clsobj);
}
ClassDefn::ClassDefn(uint32_t pos,
NameComponent* ns, Str* name, bool is_final, bool is_dynamic, Str* extends, Seq<Str*>* implements,
Seq<FunctionDefn*>* cls_methods, Seq<Binding*>* cls_vars, Seq<NamespaceDefn*>* cls_namespaces,
FunctionDefn* cls_init,
Seq<FunctionDefn*>* inst_methods, Seq<Binding*>* inst_vars,
FunctionDefn* inst_init)
: TypeDefn(pos, ns, name, false)
, is_final(is_final)
, is_dynamic(is_dynamic)
, extends(extends)
, implements(implements)
, cls_methods(cls_methods)
, cls_vars(cls_vars)
, cls_namespaces(cls_namespaces)
, cls_init(cls_init)
, inst_methods(inst_methods)
, inst_vars(inst_vars)
, inst_init(inst_init)
{
}
FunctionDefn* Parser::constructClassConstructor(uint32_t pos, Str* name, BodyInfo* old_body, Seq<Stmt*>* init_stmts, Seq<Stmt*>* stmts)
{
SignatureInfo signature(allocator);
BodyInfo body(allocator);
StringBuilder fnname(compiler);
fnname.append(name);
fnname.append("$cinit");
signature.ns = publicNS;
signature.name = fnname.str();
body.uses_finally = old_body->uses_finally;
body.uses_catch = old_body->uses_catch;
body.uses_goto = old_body->uses_goto;
body.uses_dxns = old_body->uses_dxns;
while (stmts != NULL) {
body.stmts.addAtEnd(stmts->hd);
stmts = stmts->tl;
}
body.stmts.addAtEnd(ALLOC(SuperStmt, (pos, NULL)));
return ALLOC(FunctionDefn, (signature, body, init_stmts));
}
// For the instance init we want a function derived from the real constructor method,
// it is called CLASSNAME$iinit. If there's no constructor method one is generated.
// Initializer statements are inserted at the beginning of the constructor body; these
// statements are the statements in the "inst" body. Finally, if there's no super
// call in the constructor method then one gets inserted at the beginning.
//
// The spec says:
// - initializers are evaluated *in the instance scope*, not in the scope of the
// constructor. Thus they cannot simply be added to the start of the constructor
// body, somehow the code generator needs to be in on the joke.
// - super is called at the beginning of the constructor body, after var init,
// if not present.
FunctionDefn* Parser::constructInstanceConstructor(uint32_t pos, Str* name, Seq<Stmt*>* init_stmts, FunctionDefn* constructor)
{
if (constructor) {
if (!constructor->uses_super)
constructor->stmts = ALLOC(Seq<Stmt*>, (ALLOC(SuperStmt, (pos, NULL)), constructor->stmts));
constructor->inits = init_stmts;
return constructor;
}
else {
SignatureInfo signature(allocator);
BodyInfo body(allocator);
StringBuilder fnname(compiler);
fnname.append(name);
fnname.append("$iinit");
signature.ns = publicNS;
signature.name = fnname.str();
body.stmts.addAtEnd(ALLOC(SuperStmt, (pos, NULL)));
return ALLOC(FunctionDefn, (signature, body, init_stmts));
}
}
Seq<Stmt*>* Parser::filterStatements(Seq<Stmt*>* stmts, Seq<Stmt*>** nonDefinitionStmts)
{
SeqBuilder<Stmt*> defStmts(allocator);
SeqBuilder<Stmt*> nodefStmts(allocator);
while (stmts != NULL) {
if (stmts->hd->isVardefStmt())
defStmts.addAtEnd(stmts->hd);
else
nodefStmts.addAtEnd(stmts->hd);
stmts = stmts->tl;
}
*nonDefinitionStmts = nodefStmts.get();
return defStmts.get();
}
Seq<FunctionDefn*>* Parser::filterConstructor(Str* name, Seq<FunctionDefn*>* fns, FunctionDefn** constructor)
{
SeqBuilder<FunctionDefn*> out(allocator);
*constructor = NULL;
while (fns != NULL) {
if (fns->hd->name == name) {
if (*constructor != NULL)
compiler->internalError(0, "Multiple constructors in list??");
*constructor = fns->hd;
}
else
out.addAtEnd(fns->hd);
fns = fns->tl;
}
return out.get();
}
Seq<Binding*>* Parser::bindingsToVars(Seq<Binding*>* bindings)
{
SeqBuilder<Binding*> l(allocator);
while (bindings != NULL) {
if (bindings->hd->kind == TAG_varBinding || bindings->hd->kind == TAG_constBinding)
l.addAtEnd(bindings->hd);
bindings = bindings->tl;
}
return l.get();
}
void Parser::interfaceDefinition(bool config, int /*flags*/, Qualifier* qual)
{
(void)config;
eat(T_Interface);
uint32_t pos = position();
Str* name = identifier();
checkNoShadowingOfConfigNamespaces(pos, name);
SeqBuilder<Str*> extends(allocator);
if (hd() == T_Identifier && identValue() == compiler->SYM_extends) {
next();
do {
extends.addAtEnd(identifier());
} while (match(T_Comma));
}
eat(T_LeftBrace);
pushBindingRib(RIB_Instance);
directives(SFLAG_Interface);
BodyInfo* instance = &topRib->body;
FunctionDefn* constructor = NULL;
Seq<FunctionDefn*>* inst_methods = filterConstructor(name, instance->functionDefinitions.get(), &constructor);
if (constructor != NULL)
compiler->syntaxError(pos, SYNTAXERR_REDUNDANT_METHOD);
InterfaceDefn* iface = ALLOC(InterfaceDefn, (pos, qualToNamespace(qual), name, extends.get(), inst_methods));
popBindingRib();
eat(T_RightBrace);
addInterface(iface);
}
// Namespaces are:
// - allowed at the top level of classes
// - allowed in functions and blocks
// - hoisted to the function level and scoped to the entire function
// - initialized on entry to the function (so defining them in blocks is pretty silly)
void Parser::namespaceDefinition(bool config, int flags, Qualifier* qual)
{
(void)flags;
uint32_t pos = position();
eat(T_Namespace);
Str* name = identifier();
checkNoShadowingOfConfigNamespaces(pos, name);
Expr* value = NULL;
if (match(T_Assign)) {
if (hd() != T_Identifier && hd() != T_StringLiteral)
compiler->syntaxError(pos, SYNTAXERR_ILLEGAL_NAMESPACE);
value = primaryExpression();
}
semicolon();
if (config)
addNamespaceBinding(qualToNamespace(qual), name, value);
}
void Parser::configNamespaceDefinition(int flags, bool config)
{
(void)flags;
(void)config;
}
void Parser::includeDirective()
{
eat(T_Include);
Str* newFile = stringValue();
uint32_t pos = position();
eat(T_StringLiteral);
semicolon();
if (!compiler->origin_is_file)
compiler->syntaxError(pos, SYNTAXERR_INCLUDE_ORIGIN);
// The current lexer state - including the state variables that are a part of the
// parser object - gets pushed onto a stack, a new lexer is created for the new
// input, and is installed. Then we return, the caller must continue parsing as
// if nothing had happened. When the current lexer sees EOS it calls onEOS()
// on the parser, which pops the lexer stack and generates a token from the popped
// lexer. The normal token processing path does not slow down at all and the
// machinery is almost entirely transparent to the parser.
pushLexerState();
T0 = T1 = T_LAST;
LP = L0 = L1 = 0;
uint32_t inputlen;
const wchar* input = compiler->context->readFileForEval(compiler->filename, newFile->s, &inputlen);
if (input == NULL)
compiler->syntaxError(pos, SYNTAXERR_INCLUDE_INACCESSIBLE);
included_input = input; // freed in onEOS below
#ifdef DEBUG
bool doTrace = lexer->getTrace();
#endif
lexer = ALLOC(Lexer, (compiler, input, inputlen));
#ifdef DEBUG
if (doTrace) lexer->trace();
#endif
start();
}
Token Parser::onEOS(uint32_t* linep, TokenValue* valuep)
{
if (lexerStack == NULL)
return T_EOS;
compiler->context->freeInput(included_input);
popLexerState();
if (T0 == T_LAST)
next();
*linep = L0;
*valuep = V0;
return T0;
}
void Parser::pushLexerState()
{
LexerState* s = ALLOC(LexerState, ());
s->lexer = lexer;
s->next = lexerStack;
s->T0 = T0;
s->T1 = T1;
s->V0 = V0;
s->LP = LP;
s->L0 = L0;
s->L1 = L1;
s->included_input = included_input;
lexerStack = s;
}
void Parser::popLexerState()
{
T0 = lexerStack->T0;
T1 = lexerStack->T1;
V0 = lexerStack->V0;
V1 = lexerStack->V1;
LP = lexerStack->LP;
L0 = lexerStack->L0;
L1 = lexerStack->L1;
included_input = lexerStack->included_input;
lexer = lexerStack->lexer;
lexerStack = lexerStack->next;
}
void Parser::pushBindingRib(RibType tag)
{
topRib = ALLOC(BindingRib, (allocator, topRib, tag));
}
void Parser::popBindingRib()
{
topRib = topRib->next;
}
// Only makes sense in the context of a scope; compares name components only
static bool sameName(NameComponent* n1, NameComponent* n2)
{
if (n1 == NULL && n2 == NULL)
return true;
if (n1 == NULL || n2 == NULL)
return false;
if (n1->tag() != n2->tag())
return false;
switch (n1->tag()) {
case TAG_simpleName:
return ((SimpleName*)n1)->name == ((SimpleName*)n2)->name; // Str is interned, so pointer comparison works
case TAG_wildcardName:
return true;
case TAG_computedName:
return false;
case TAG_builtinNamespace:
return ((BuiltinNamespace*)n1)->t == ((BuiltinNamespace*)n2)->t; // tokens are integer values
default:
return false;
}
}
static bool sameName(QualifiedName* n1, QualifiedName* n2)
{
// ActionScript allows eg 'var n:Number; var n;".
if (n1 == NULL || n2 == NULL)
return true;
return sameName(n1->qualifier, n2->qualifier) && sameName(n1->name, n2->name);
}
static bool sameType(Type* t1, Type* t2)
{
// Iffy, why NULL?
if (t1 == NULL || t2 == NULL)
return true;
if (t1->tag() != t2->tag())
return false;
switch (t1->tag())
{
case TAG_simpleType: {
return sameName(((SimpleType*)t1)->name, ((SimpleType*)t2)->name);
case TAG_instantiatedType: {
InstantiatedType* it1 = (InstantiatedType*)t1;
InstantiatedType* it2 = (InstantiatedType*)t2;
return sameName(it1->basename, it2->basename) && sameType(it1->tparam, it2->tparam);
}
default:
AvmAssert(!"Should not happen");
return false;
}
}
}
Binding* Parser::findBinding(NameComponent* ns, Str* name, BindingKind kind, BindingRib* rib)
{
if (rib == NULL)
rib = topRib;
for ( Seq<Binding*>* bindings = rib->body.bindings.get() ; bindings != NULL ; bindings = bindings->tl ) {
if (sameName(bindings->hd->ns, ns) && bindings->hd->name == name) {
if (bindings->hd->kind != kind)
compiler->syntaxError(0, SYNTAXERR_REDEFINITION);
return bindings->hd;
}
}
return NULL;
}
void Parser::addClass(ClassDefn* cls)
{
Binding* b = findBinding(cls->ns, cls->name, TAG_classBinding);
if (!b)
topRib->body.bindings.addAtEnd(ALLOC(Binding, (cls->ns, cls->name, NULL, TAG_classBinding)));
else
compiler->syntaxError(0, SYNTAXERR_REDEFINITION_TYPE);
classes.addAtEnd(cls);
}
void Parser::addInterface(InterfaceDefn* iface)
{
Binding* b = findBinding(iface->ns, iface->name, TAG_interfaceBinding);
if (!b)
topRib->body.bindings.addAtEnd(ALLOC(Binding, (iface->ns, iface->name, NULL, TAG_interfaceBinding)));
else
compiler->syntaxError(0, SYNTAXERR_REDEFINITION_TYPE);
classes.addAtEnd(iface);
}
void Parser::addFunctionBinding(NameComponent* ns, FunctionDefn* fn)
{
addVarBinding(ns, fn->name, NULL);
topRib->body.functionDefinitions.addAtEnd(fn);
}
void Parser::addVarBinding(NameComponent* ns, Str* name, Type* type_name, BindingRib* rib)
{
if (rib == NULL)
rib = topRib;
Binding* b = findBinding(ns, name, TAG_varBinding, rib);
if (!b)
rib->body.bindings.addAtEnd(ALLOC(Binding, (ns, name, type_name, TAG_varBinding)));
else if (!sameType(b->type_name, type_name))
compiler->syntaxError(0, SYNTAXERR_REDEFINITION_TYPE);
}
void Parser::addConstBinding(NameComponent* ns, Str* name, Type* type_name, BindingRib* rib)
{
if (rib == NULL)
rib = topRib;
if (findBinding(ns, name, TAG_constBinding, rib))
compiler->syntaxError(0, SYNTAXERR_REDUNDANT_CONST);
rib->body.bindings.addAtEnd(ALLOC(Binding, (ns, name, type_name, TAG_constBinding)));
}
void Parser::addMethodBinding(NameComponent* ns, FunctionDefn* fn, BindingRib* rib)
{
if (findBinding(ns, fn->name, TAG_methodBinding, rib))
compiler->syntaxError(0, SYNTAXERR_REDUNDANT_METHOD);
rib->body.bindings.addAtEnd(ALLOC(Binding, (ns, fn->name, NULL, TAG_methodBinding)));
rib->body.functionDefinitions.addAtEnd(fn);
}
void Parser::addNamespaceBinding(NameComponent* ns, Str* name, Expr* expr)
{
if (findBinding(ns, name, TAG_namespaceBinding))
compiler->syntaxError(0, SYNTAXERR_REDUNDANT_NAMESPACE);
topRib->body.bindings.addAtEnd(ALLOC(Binding, (ns, name, NULL, TAG_namespaceBinding))); // FIXME: type for 'Namespace'
topRib->body.namespaces.addAtEnd(ALLOC(NamespaceDefn, (ns, name, expr)));
}
void Parser::addQualifiedImport(Seq<Str*>* name)
{
(void)name;
// Nothing we care about at this time
}
void Parser::addUnqualifiedImport(Seq<Str*>* name)
{
(void)name;
// Nothing we care about at this time
}
void Parser::addOpenNamespace(Namespace* ns)
{
topRib->body.openNamespaces.addAtEnd(ns);
}
void Parser::setUsesFinally()
{
// FIXME: Far from ideal
if (topRib->tag == RIB_Instance && topRib->next != NULL && topRib->next->tag == RIB_Class)
topRib->next->body.uses_finally = true;
else
topRib->body.uses_finally = true;
}
void Parser::setUsesCatch()
{
// FIXME: Far from ideal
if (topRib->tag == RIB_Instance && topRib->next != NULL && topRib->next->tag == RIB_Class)
topRib->next->body.uses_catch = true;
else
topRib->body.uses_catch = true;
}
void Parser::setUsesGoto()
{
// FIXME: Far from ideal
if (topRib->tag == RIB_Instance && topRib->next != NULL && topRib->next->tag == RIB_Class)
topRib->next->body.uses_goto = true;
else
topRib->body.uses_goto = true;
}
void Parser::setUsesSuper()
{
topRib->body.uses_super = true;
}
void Parser::functionDefinition(bool config, Qualifier* qual, bool getters_and_setters, bool require_body)
{
(void)config;
uint32_t pos = position();
FunctionDefn* fn = functionGuts(qual, true, getters_and_setters, require_body);
// FIXME: if an interface method it's always public, plus functionGuts() will have computed a namespace
// in signature.ns and we should use that.
NameComponent* ns = qualToNamespace(qual);
checkNoShadowingOfConfigNamespaces(pos, fn->name);
if (topRib->tag == RIB_Instance) {
// Class or interface. If interface then is_static will never be true because it's
// filtered during attribute parsing.
if (qual->is_static) {
AvmAssert(topRib->next->tag == RIB_Class);
addMethodBinding(ns, fn, topRib->next);
}
else
addMethodBinding(ns, fn, topRib);
}
else
addFunctionBinding(ns, fn);
}
Stmt* Parser::variableDefinition(bool config, Qualifier* qual)
{
// FIXME: do not ignore the config value
// FIXME: discards qualifiers on variable definitions
// FIXME: check that the name we're defining is not shadowing a config NS
(void)config;
Stmt* stmt = varStatement(qualToNamespace(qual), hd() == T_Const, qual->is_static > 0);
semicolon();
return stmt;
}
FunctionDefn* Parser::functionGuts(Qualifier* qual, bool require_name, bool getters_and_setters, bool require_body)
{
if (qual->is_native)
compiler->syntaxError(position(), SYNTAXERR_NATIVE_NOT_SUPPORTED);
eat(T_Function);
uint32_t pos = position();
SignatureInfo signature(allocator);
if (require_name || hd() == T_Identifier)
signature.name = identifier();
if (signature.name != NULL && hd() == T_Identifier && getters_and_setters) {
signature.isGetter = (signature.name == compiler->SYM_get);
signature.isSetter = (signature.name == compiler->SYM_set);
signature.name = identifier();
}
if (signature.name != NULL)
signature.ns = qualToNamespace(qual);
pushBindingRib(RIB_Function);
eat(T_LeftParen);
if (hd() != T_RightParen) {
for (;;)
{
if (hd() == T_TripleDot) {
Str* rest_name = NULL;
Type* rest_type_name = NULL;
eat(T_TripleDot);
rest_name = identifier();
if (match(T_Colon))
rest_type_name = typeExpression();
addVarBinding(defaultNamespace(), rest_name, rest_type_name);
signature.rest_param = ALLOC(FunctionParam, (rest_name, rest_type_name, NULL));
break;
}
++signature.numparams;
Str* param_name = identifier();
Type* param_type_name = NULL;
Expr* param_default_value = NULL;
if (match(T_Colon))
param_type_name = typeExpression();
if (match(T_Assign)) {
signature.optional_arguments = true;
param_default_value = assignmentExpression(0);
}
else if (signature.optional_arguments)
compiler->syntaxError(pos, SYNTAXERR_DEFAULT_VALUE_REQD);
addVarBinding(defaultNamespace(), param_name, param_type_name);
signature.params.addAtEnd(ALLOC(FunctionParam, (param_name, param_type_name, param_default_value)));
if (hd() == T_RightParen)
break;
eat(T_Comma);
}
}
eat(T_RightParen);
topRib->signature = &signature;
if (match(T_Colon)) {
if (match(T_Void))
signature.isVoid = true;
else
signature.return_type_name = typeExpression();
}
if (require_body)
{
eat(T_LeftBrace);
directives(SFLAG_Function);
eat(T_RightBrace);
// Rest takes precedence over 'arguments'
if (topRib->body.uses_arguments) {
if (signature.rest_param == NULL)
addVarBinding(defaultNamespace(), compiler->SYM_arguments, NULL);
else
topRib->body.uses_arguments = false;
}
}
else {
topRib->body.empty_body = true;
semicolon();
}
BindingRib* theRib = topRib;
popBindingRib();
return ALLOC(FunctionDefn, (signature, theRib->body));
}
BuiltinNamespace* Parser::internalNamespace()
{
return internalNS;
}
BuiltinNamespace* Parser::defaultNamespace()
{
if (public_by_default)
return publicNS;
return internalNS;
}
NameComponent* Parser::qualToNamespace(Qualifier* qual)
{
switch (qual->tag) {
case QUAL_none:
return defaultNamespace();
case QUAL_internal:
return internalNS;
case QUAL_private:
return privateNS;
case QUAL_protected:
return protectedNS;
case QUAL_public:
return publicNS;
case QUAL_name:
compiler->internalError(position(), "Defintions in user-defined namespaces not implemented");
/*NOTREACHED*/
default:
return NULL;
}
}
// Token queue abstractions
// BEGIN
// Token queue
//
// T0 is the current token, L0 its line number
// T1 is the next token, L1 its line number
// LP is the line number of the previous token
//
// The line number of a token is the 1-based line number of
// the last character of the token.
//
// Invariants:
// T0 is LAST only before start() and when the stream is exhausted.
// T1 is LAST whenever it's invalid
// L1 is invalid iff T1 is invalid
// LP, L0, and L1 are 0 if there is "no information"
// The scanner never returns Eol
//
// Every token passes through this interface so we try to save time
// when possible.
//
// Current optimizations:
// * avoid function calls
// - Lexer::lex() is shallow
// - Token::tokenKind and Token::tokenText have been in-lined
// * avoid allocations
// - Communicate with lexer via multiple variables rather than
// using multiple return values
void Parser::start()
{
T0 = lexer->lex(&L0, &V0);
}
Token Parser::divideOperator()
{
AvmAssert( T0 == T_BreakSlash && T1 == T_LAST );
T0 = lexer->divideOperator(&L0);
return hd();
}
Token Parser::regexp()
{
AvmAssert( T0 == T_BreakSlash && T1 == T_LAST );
T0 = lexer->regexp(&L0, &V0);
return hd();
}
Token Parser::rightAngle()
{
AvmAssert( T0 == T_BreakRightAngle && T1 == T_LAST );
T0 = lexer->rightAngle(&L0);
return hd();
}
Token Parser::leftAngle()
{
AvmAssert( T0 == T_BreakLeftAngle && T1 == T_LAST );
T0 = lexer->leftAngle(&L0);
return hd();
}
Token Parser::leftShiftOrRelationalOperator()
{
AvmAssert( T0 == T_BreakLeftAngle && T1 == T_LAST );
T0 = lexer->leftShiftOrRelationalOperator(&L0);
return hd();
}
Token Parser::rightShiftOrRelationalOperator()
{
AvmAssert( T0 == T_BreakRightAngle && T1 == T_LAST );
T0 = lexer->rightShiftOrRelationalOperator(&L0);
return hd();
}
Token Parser::hd2()
{
if (T1 == T_LAST)
T1 = lexer->lex(&L1, &V1);
return T1;
}
void Parser::next()
{
LP = L0;
T0 = T1;
L0 = L1;
V0 = V1;
T1 = T_LAST;
if (T0 == T_LAST)
T0 = lexer->lex(&L0, &V0);
}
// Token queue abstractions
// END
// If the current token is not tc then throw an error.
// Otherwise consume the token.
void Parser::eat(Token tc)
{
Token tk = hd();
if (tk != tc) {
#ifdef DEBUG
compiler->syntaxError(position(), SYNTAXERR_WRONG_TOKEN, tc, tk);
#else
compiler->syntaxError(position(), SYNTAXERR_WRONG_TOKEN); // FIXME: not adequate
#endif
}
next();
}
bool Parser::match (Token tc)
{
Token tk = hd();
if (tk != tc)
return false;
next();
return true;
}
Str* Parser::identifier()
{
if (hd() != T_Identifier)
compiler->syntaxError(position(), SYNTAXERR_EXPECTED_IDENT);
Str* name = V0.s;
next();
return name;
}
Str* Parser::doubleToStr(double d)
{
char buf[380];
compiler->context->doubleToString(d, buf, sizeof(buf));
return compiler->intern(buf);
}
// FIXME: this duplicates Lexer::parseDouble()
double Parser::strToDouble(Str* s)
{
double n;
StringBuilder sb(compiler);
sb.append(s);
DEBUG_ONLY(bool flag =) compiler->context->stringToDouble(sb.chardata(), &n);
AvmAssert(flag);
return n;
}
CodeBlock::~CodeBlock()
{
}
}
}
#endif // VMCFG_EVAL