/* -*- 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* 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 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* instanceStmts = &(topRib->body.stmts); SeqBuilder* classStmts = (flags & SFLAG_Class) ? &(topRib->next->body.stmts) : NULL; // Directive parser machine state. SeqBuilder 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, // + "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* stmts, Seq* 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 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* cls_methods = cls->functionDefinitions.get(); Seq* cls_vars = bindingsToVars(cls->bindings.get()); Seq* cls_namespaces = cls->namespaces.get(); Seq* static_stmts = NULL; Seq* 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* inst_methods = filterConstructor(name, instance->functionDefinitions.get(), &constructor); Seq* inst_vars = bindingsToVars(instance->bindings.get()); Seq* 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* implements, Seq* cls_methods, Seq* cls_vars, Seq* cls_namespaces, FunctionDefn* cls_init, Seq* inst_methods, Seq* 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* init_stmts, Seq* 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* init_stmts, FunctionDefn* constructor) { if (constructor) { if (!constructor->uses_super) constructor->stmts = ALLOC(Seq, (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* Parser::filterStatements(Seq* stmts, Seq** nonDefinitionStmts) { SeqBuilder defStmts(allocator); SeqBuilder 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* Parser::filterConstructor(Str* name, Seq* fns, FunctionDefn** constructor) { SeqBuilder 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* Parser::bindingsToVars(Seq* bindings) { SeqBuilder 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 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* 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* 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* name) { (void)name; // Nothing we care about at this time } void Parser::addUnqualifiedImport(Seq* 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