/* * Copyright (C) 2010-2016 JPEXS, All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3.0 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. */ package com.jpexs.decompiler.flash.abc.avm2.parser.script; import com.jpexs.decompiler.flash.SWC; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.SourceGeneratorLocalData; import com.jpexs.decompiler.flash.abc.ABC; import com.jpexs.decompiler.flash.abc.avm2.model.ApplyTypeAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.BooleanAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.CoerceAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.ConstructSuperAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.DefaultXMLNamespace; import com.jpexs.decompiler.flash.abc.avm2.model.EscapeXAttrAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.EscapeXElemAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.FloatValueAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.GetDescendantsAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.GetPropertyAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.HasNextAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.InAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.InitVectorAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.IntegerValueAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.LocalRegAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.NameValuePair; import com.jpexs.decompiler.flash.abc.avm2.model.NanAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.NewActivationAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.NewArrayAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.NewObjectAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.NextNameAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.NullAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.PostDecrementAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.PostIncrementAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.RegExpAvm2Item; import com.jpexs.decompiler.flash.abc.avm2.model.ReturnValueAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.ReturnVoidAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.StringAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.ThrowAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.UndefinedAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.WithAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.clauses.ExceptionAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.clauses.ForEachInAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.clauses.ForInAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.clauses.TryAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.operations.AddAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.operations.AsTypeAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.operations.BitAndAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.operations.BitOrAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.operations.BitXorAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.operations.DeletePropertyAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.operations.DivideAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.operations.EqAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.operations.GeAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.operations.GtAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.operations.InstanceOfAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.operations.IsTypeAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.operations.LShiftAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.operations.LeAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.operations.LtAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.operations.ModuloAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.operations.MultiplyAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.operations.NegAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.operations.NeqAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.operations.PreDecrementAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.operations.PreIncrementAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.operations.RShiftAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.operations.StrictEqAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.operations.StrictNeqAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.operations.SubtractAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.operations.TypeOfAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.operations.URShiftAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.parser.AVM2ParseException; import com.jpexs.decompiler.flash.abc.types.Namespace; import com.jpexs.decompiler.flash.action.swf4.ActionIf; import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.tags.ABCContainerTag; import com.jpexs.decompiler.flash.tags.Tag; import com.jpexs.decompiler.graph.CompilationException; import com.jpexs.decompiler.graph.DottedChain; import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.Loop; import com.jpexs.decompiler.graph.TypeItem; import com.jpexs.decompiler.graph.model.AndItem; import com.jpexs.decompiler.graph.model.BlockItem; import com.jpexs.decompiler.graph.model.BreakItem; import com.jpexs.decompiler.graph.model.CommaExpressionItem; import com.jpexs.decompiler.graph.model.ContinueItem; import com.jpexs.decompiler.graph.model.DefaultItem; import com.jpexs.decompiler.graph.model.DoWhileItem; import com.jpexs.decompiler.graph.model.DuplicateItem; import com.jpexs.decompiler.graph.model.ForItem; import com.jpexs.decompiler.graph.model.IfItem; import com.jpexs.decompiler.graph.model.NotItem; import com.jpexs.decompiler.graph.model.OrItem; import com.jpexs.decompiler.graph.model.ParenthesisItem; import com.jpexs.decompiler.graph.model.PopItem; import com.jpexs.decompiler.graph.model.PushItem; import com.jpexs.decompiler.graph.model.SwitchItem; import com.jpexs.decompiler.graph.model.TernarOpItem; import com.jpexs.decompiler.graph.model.UnboundedTypeItem; import com.jpexs.decompiler.graph.model.WhileItem; import com.jpexs.helpers.Helper; import com.jpexs.helpers.utf8.Utf8Helper; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.AbstractMap; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Stack; import java.util.logging.Level; import java.util.logging.Logger; /** * * @author JPEXS */ public class ActionScript3Parser { private long uniqLast = 0; private final boolean debugMode = false; private static final String AS3_NAMESPACE = "http://adobe.com/AS3/2006/builtin"; private final AbcIndexing abcIndex; // private final AbcIndexing otherABCs; //private static final List<ABC> playerABCs = new ArrayList<>(); private static AbcIndexing playerGlobalAbcIndex; private long uniqId() { uniqLast++; return uniqLast; } private List<GraphTargetItem> commands(List<List<NamespaceItem>> allOpenedNamespaces, TypeItem thisType, NamespaceItem pkg, Reference<Boolean> needsActivation, List<DottedChain> importedClasses, List<NamespaceItem> openedNamespaces, Stack<Loop> loops, Map<Loop, String> loopLabels, HashMap<String, Integer> registerVars, boolean inFunction, boolean inMethod, int forinlevel, List<AssignableAVM2Item> variables) throws IOException, AVM2ParseException { List<GraphTargetItem> ret = new ArrayList<>(); if (debugMode) { System.out.println("commands:"); } GraphTargetItem cmd; while ((cmd = command(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, true, variables)) != null) { ret.add(cmd); } if (debugMode) { System.out.println("/commands"); } return ret; } private GraphTargetItem type(List<List<NamespaceItem>> allOpenedNamespaces, TypeItem thisType, NamespaceItem pkg, Reference<Boolean> needsActivation, List<DottedChain> importedClasses, List<NamespaceItem> openedNamespaces, List<AssignableAVM2Item> variables) throws IOException, AVM2ParseException { ParsedSymbol s = lex(); if (s.type == SymbolType.MULTIPLY) { return new UnboundedTypeItem(); } else if (s.type == SymbolType.VOID) { return new TypeItem(DottedChain.VOID); } else { lexer.pushback(s); } GraphTargetItem t = name(allOpenedNamespaces, thisType, pkg, needsActivation, true, openedNamespaces, null, false, false, variables, importedClasses); t = applyType(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, t, new HashMap<>(), false, false, variables); return t; } private GraphTargetItem memberOrCall(List<List<NamespaceItem>> allOpenedNamespaces, TypeItem thisType, NamespaceItem pkg, Reference<Boolean> needsActivation, List<DottedChain> importedClasses, List<NamespaceItem> openedNamespaces, GraphTargetItem newcmds, HashMap<String, Integer> registerVars, boolean inFunction, boolean inMethod, List<AssignableAVM2Item> variables) throws IOException, AVM2ParseException { if (debugMode) { System.out.println("memberOrCall:"); } ParsedSymbol s = lex(); GraphTargetItem ret = newcmds; while (s.isType(SymbolType.DOT, SymbolType.PARENT_OPEN, SymbolType.BRACKET_OPEN, SymbolType.TYPENAME, SymbolType.FILTER, SymbolType.DESCENDANTS)) { switch (s.type) { case BRACKET_OPEN: case DOT: case TYPENAME: lexer.pushback(s); ret = member(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, ret, registerVars, inFunction, inMethod, variables); break; case FILTER: needsActivation.setVal(true); ret = new XMLFilterAVM2Item(ret, expression(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, inMethod, variables), openedNamespaces); expectedType(SymbolType.PARENT_CLOSE); break; case PARENT_OPEN: ret = new CallAVM2Item(openedNamespaces, lexer.yyline(), ret, call(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, variables)); break; case DESCENDANTS: s = lex(); expected(s, lexer.yyline(), SymbolGroup.IDENTIFIER, SymbolType.MULTIPLY); ret = new GetDescendantsAVM2Item(ret, s.type == SymbolType.MULTIPLY ? null : s.value.toString(), openedNamespaces); break; } s = lex(); } if (s.type == SymbolType.INCREMENT) { if (!isNameOrProp(ret)) { throw new AVM2ParseException("Invalid assignment", lexer.yyline()); } ret = new PostIncrementAVM2Item(null, null, ret); s = lex(); } else if (s.type == SymbolType.DECREMENT) { if (!isNameOrProp(ret)) { throw new AVM2ParseException("Invalid assignment", lexer.yyline()); } ret = new PostDecrementAVM2Item(null, null, ret); s = lex(); } lexer.pushback(s); if (debugMode) { System.out.println("/memberOrCall"); } return ret; } private GraphTargetItem applyType(List<List<NamespaceItem>> allOpenedNamespaces, TypeItem thisType, NamespaceItem pkg, Reference<Boolean> needsActivation, List<DottedChain> importedClasses, List<NamespaceItem> openedNamespaces, GraphTargetItem obj, HashMap<String, Integer> registerVars, boolean inFunction, boolean inMethod, List<AssignableAVM2Item> variables) throws IOException, AVM2ParseException { GraphTargetItem ret = obj; ParsedSymbol s = lex(); if (s.type == SymbolType.TYPENAME) { List<GraphTargetItem> params = new ArrayList<>(); do { s = lex(); if (s.isType(SymbolType.MULTIPLY)) { params.add(new NullAVM2Item(null, null)); } else { lexer.pushback(s); params.add(expressionPrimary(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, false, registerVars, inFunction, inMethod, false, variables)); } s = lex(); } while (s.type == SymbolType.COMMA); if (s.type == SymbolType.USHIFT_RIGHT) { s = new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.GREATER_THAN); lexer.pushback(s); lexer.pushback(s); } if (s.type == SymbolType.SHIFT_RIGHT) { s = new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.GREATER_THAN); lexer.pushback(s); } expected(s, lexer.yyline(), SymbolType.GREATER_THAN); ret = new ApplyTypeAVM2Item(null, null, ret, params); } else { lexer.pushback(s); } return ret; } private GraphTargetItem member(List<List<NamespaceItem>> allOpenedNamespaces, TypeItem thisType, NamespaceItem pkg, Reference<Boolean> needsActivation, List<DottedChain> importedClasses, List<NamespaceItem> openedNamespaces, GraphTargetItem obj, HashMap<String, Integer> registerVars, boolean inFunction, boolean inMethod, List<AssignableAVM2Item> variables) throws IOException, AVM2ParseException { if (debugMode) { System.out.println("member:"); } GraphTargetItem ret = obj; ParsedSymbol s = lex(); while (s.isType(SymbolType.DOT, SymbolType.BRACKET_OPEN, SymbolType.TYPENAME)) { ParsedSymbol s2 = lex(); boolean attr = false; if (s.type == SymbolType.DOT) { if (s2.type == SymbolType.ATTRIBUTE) { attr = true; } else { lexer.pushback(s2); } } else { lexer.pushback(s2); } if (s.type == SymbolType.TYPENAME) { lexer.pushback(s); ret = applyType(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, ret, registerVars, inFunction, inMethod, variables); s = lex(); } else if (s.type == SymbolType.BRACKET_OPEN) { GraphTargetItem index = expression(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); expectedType(SymbolType.BRACKET_CLOSE); ret = new IndexAVM2Item(attr, ret, index, null, openedNamespaces); s = lex(); } else { s = lex(); expected(s, lexer.yyline(), SymbolGroup.IDENTIFIER, SymbolType.MULTIPLY); String propName = s.value.toString(); //Can be * GraphTargetItem propItem = null; s = lex(); GraphTargetItem ns = null; if (s.type == SymbolType.NAMESPACE_OP) { ns = new UnresolvedAVM2Item(new ArrayList<>(), importedClasses, false, null, lexer.yyline(), new DottedChain(new String[]{propName}, "" /*FIXME ???*/), null, openedNamespaces); variables.add((UnresolvedAVM2Item) ns); s = lex(); if (s.type == SymbolType.BRACKET_OPEN) { propItem = expression(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); expectedType(SymbolType.BRACKET_CLOSE); propName = null; } else { expected(s, lexer.yyline(), SymbolGroup.IDENTIFIER); propName = s.value.toString(); propItem = null; } } else { lexer.pushback(s); } if (ns != null) { ret = new NamespacedAVM2Item(ns, propName, propItem, ret, attr, openedNamespaces, null); } else { ret = new PropertyAVM2Item(ret, (attr ? "@" : "") + propName, abcIndex, openedNamespaces, new ArrayList<>()); } s = lex(); } } lexer.pushback(s); if (debugMode) { System.out.println("/member"); } return ret; } private GraphTargetItem name(List<List<NamespaceItem>> allOpenedNamespaces, TypeItem thisType, NamespaceItem pkg, Reference<Boolean> needsActivation, boolean typeOnly, List<NamespaceItem> openedNamespaces, HashMap<String, Integer> registerVars, boolean inFunction, boolean inMethod, List<AssignableAVM2Item> variables, List<DottedChain> importedClasses) throws IOException, AVM2ParseException { ParsedSymbol s = lex(); DottedChain name = new DottedChain(new String[]{}, ""); String name2 = ""; if (s.type == SymbolType.ATTRIBUTE) { name2 += "@"; s = lex(); } expected(s, lexer.yyline(), SymbolGroup.IDENTIFIER, SymbolType.THIS, SymbolType.SUPER, SymbolType.STRING_OP); name2 += s.value.toString(); s = lex(); boolean attrBracket = false; name = name.addWithSuffix(name2); while (s.isType(SymbolType.DOT)) { //name += s.value.toString(); //. or :: s = lex(); name2 = ""; if (s.type == SymbolType.ATTRIBUTE) { name2 += "@"; s = lex(); if (s.type == SymbolType.MULTIPLY) { name2 += s.value.toString(); } else if (s.group == SymbolGroup.IDENTIFIER) { name2 += s.value.toString(); } else { if (s.type != SymbolType.BRACKET_OPEN) { throw new AVM2ParseException("Attribute identifier or bracket expected", lexer.yyline()); } attrBracket = true; continue; } } else { expected(s, lexer.yyline(), SymbolGroup.IDENTIFIER, SymbolType.NAMESPACE, SymbolType.MULTIPLY); name2 += s.value.toString(); } name = name.addWithSuffix(name2); s = lex(); } String nsname = null; String nsprop = null; GraphTargetItem nspropItem = null; if (s.type == SymbolType.NAMESPACE_OP) { nsname = name.getLast(); s = lex(); if (s.group == SymbolGroup.IDENTIFIER) { nsprop = s.value.toString(); } else if (s.type == SymbolType.BRACKET_OPEN) { nspropItem = expression(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); expectedType(SymbolType.BRACKET_CLOSE); } name = name.getWithoutLast(); s = lex(); } GraphTargetItem ret = null; if (!name.isEmpty()) { UnresolvedAVM2Item unr = new UnresolvedAVM2Item(new ArrayList<>(), importedClasses, typeOnly, null, lexer.yyline(), name, null, openedNamespaces); //unr.setIndex(index); variables.add(unr); ret = unr; } if (nsname != null) { boolean attr = nsname.startsWith("@"); if (attr) { nsname = nsname.substring(1); } UnresolvedAVM2Item ns = new UnresolvedAVM2Item(new ArrayList<>(), importedClasses, typeOnly, null, lexer.yyline(), new DottedChain(new String[]{nsname}, ""), null, openedNamespaces); variables.add(ns); ret = new NamespacedAVM2Item(ns, nsprop, nspropItem, ret, attr, openedNamespaces, null); } if (s.type == SymbolType.BRACKET_OPEN) { lexer.pushback(s); if (attrBracket) { lexer.pushback(new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.ATTRIBUTE, "@")); lexer.pushback(new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.DOT, ".")); } ret = member(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, ret, registerVars, inFunction, inMethod, variables); } else { lexer.pushback(s); } return ret; } private void expected(ParsedSymbol symb, int line, Object... expected) throws IOException, AVM2ParseException { boolean found = false; for (Object t : expected) { if (symb.type == t) { found = true; } if (symb.group == t) { found = true; } } if (!found) { String expStr = ""; boolean first = true; for (Object e : expected) { if (!first) { expStr += " or "; } expStr += e; first = false; } throw new AVM2ParseException("" + expStr + " expected but " + symb.type + " found", line); } } private ParsedSymbol expectedType(Object... type) throws IOException, AVM2ParseException { ParsedSymbol symb = lex(); expected(symb, lexer.yyline(), type); return symb; } private ParsedSymbol lex() throws IOException, AVM2ParseException { ParsedSymbol ret = lexer.lex(); if (debugMode) { System.out.println(ret); } return ret; } private List<GraphTargetItem> call(List<List<NamespaceItem>> allOpenedNamespaces, TypeItem thisType, NamespaceItem pkg, Reference<Boolean> needsActivation, List<DottedChain> importedClasses, List<NamespaceItem> openedNamespaces, HashMap<String, Integer> registerVars, boolean inFunction, boolean inMethod, List<AssignableAVM2Item> variables) throws IOException, AVM2ParseException { List<GraphTargetItem> ret = new ArrayList<>(); //expected(SymbolType.PARENT_OPEN); //MUST BE HANDLED BY CALLER ParsedSymbol s = lex(); while (s.type != SymbolType.PARENT_CLOSE) { if (s.type != SymbolType.COMMA) { lexer.pushback(s); } ret.add(expression(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables)); s = lex(); expected(s, lexer.yyline(), SymbolType.COMMA, SymbolType.PARENT_CLOSE); } return ret; } private MethodAVM2Item method(List<List<NamespaceItem>> allOpenedNamespaces, boolean outsidePackage, boolean isPrivate, List<Map.Entry<String, Map<String, String>>> metadata, NamespaceItem pkg, boolean isInterface, String customAccess, Reference<Boolean> needsActivation, List<DottedChain> importedClasses, boolean override, boolean isFinal, TypeItem thisType, List<NamespaceItem> openedNamespaces, boolean isStatic, String functionName, boolean isMethod, List<AssignableAVM2Item> variables) throws IOException, AVM2ParseException { FunctionAVM2Item f = function(allOpenedNamespaces, metadata, pkg, isInterface, needsActivation, importedClasses, thisType, openedNamespaces, functionName, isMethod, variables); return new MethodAVM2Item(allOpenedNamespaces, outsidePackage, isPrivate, f.metadata, f.pkg, f.isInterface, customAccess, f.needsActivation, f.hasRest, f.line, override, isFinal, isStatic, functionName, f.paramTypes, f.paramNames, f.paramValues, f.body, f.subvariables, f.retType); } private FunctionAVM2Item function(List<List<NamespaceItem>> allOpenedNamespaces, List<Map.Entry<String, Map<String, String>>> metadata, NamespaceItem pkg, boolean isInterface, Reference<Boolean> needsActivation, List<DottedChain> importedClasses, TypeItem thisType, List<NamespaceItem> openedNamespaces, String functionName, boolean isMethod, List<AssignableAVM2Item> variables) throws IOException, AVM2ParseException { openedNamespaces = new ArrayList<>(openedNamespaces); //local copy allOpenedNamespaces.add(openedNamespaces); int line = lexer.yyline(); ParsedSymbol s; expectedType(SymbolType.PARENT_OPEN); s = lex(); List<String> paramNames = new ArrayList<>(); List<GraphTargetItem> paramTypes = new ArrayList<>(); List<GraphTargetItem> paramValues = new ArrayList<>(); boolean hasRest = false; while (s.type != SymbolType.PARENT_CLOSE) { if (s.type != SymbolType.COMMA) { lexer.pushback(s); } s = lex(); if (s.type == SymbolType.REST) { hasRest = true; s = lex(); } expected(s, lexer.yyline(), SymbolGroup.IDENTIFIER); paramNames.add(s.value.toString()); s = lex(); if (!hasRest) { if (s.type == SymbolType.COLON) { paramTypes.add(type(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, variables)); s = lex(); } else { paramTypes.add(new UnboundedTypeItem()); } if (s.type == SymbolType.ASSIGN) { paramValues.add(expression(allOpenedNamespaces, thisType, pkg, new Reference<>(false), importedClasses, openedNamespaces, null, isMethod, isMethod, isMethod, variables)); s = lex(); } else if (!paramValues.isEmpty()) { throw new AVM2ParseException("Some of parameters do not have default values", lexer.yyline()); } } if (!s.isType(SymbolType.COMMA, SymbolType.PARENT_CLOSE)) { expected(s, lexer.yyline(), SymbolType.COMMA, SymbolType.PARENT_CLOSE); } if (hasRest) { expected(s, lexer.yyline(), SymbolType.PARENT_CLOSE); } } s = lex(); GraphTargetItem retType; if (s.type == SymbolType.COLON) { retType = type(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, variables); } else { retType = new UnboundedTypeItem(); lexer.pushback(s); } List<GraphTargetItem> body = null; List<AssignableAVM2Item> subvariables = new ArrayList<>(); subvariables.add(new NameAVM2Item(thisType, lexer.yyline(), "this", null, true, openedNamespaces)); for (int i = 0; i < paramNames.size() - (hasRest ? 1 : 0); i++) { subvariables.add(new NameAVM2Item(paramTypes.get(i), lexer.yyline(), paramNames.get(i), null, true, openedNamespaces)); } if (hasRest) { subvariables.add(new NameAVM2Item(TypeItem.UNBOUNDED, lexer.yyline(), paramNames.get(paramNames.size() - 1), null, true, openedNamespaces)); } subvariables.add(new NameAVM2Item(thisType, lexer.yyline(), "arguments", null, true, openedNamespaces)); int parCnt = subvariables.size(); Reference<Boolean> needsActivation2 = new Reference<>(false); if (!isInterface) { expectedType(SymbolType.CURLY_OPEN); body = commands(allOpenedNamespaces, thisType, pkg, needsActivation2, importedClasses, openedNamespaces, new Stack<>(), new HashMap<>(), new HashMap<>(), true, isMethod, 0, subvariables); expectedType(SymbolType.CURLY_CLOSE); } else { expectedType(SymbolType.SEMICOLON); } for (int i = 0; i < parCnt; i++) { subvariables.remove(0); } return new FunctionAVM2Item(metadata, pkg, isInterface, needsActivation2.getVal(), hasRest, line, functionName, paramTypes, paramNames, paramValues, body, subvariables, retType); } private List<Map.Entry<String, Map<String, String>>> parseMetadata() throws IOException, AVM2ParseException { List<Map.Entry<String, Map<String, String>>> metadata = new ArrayList<>(); ParsedSymbol s = lex(); while (s.isType(SymbolType.BRACKET_OPEN)) { s = lex(); expected(s, lexer.yyline(), SymbolGroup.IDENTIFIER); String name = s.value.toString(); Map.Entry<String, Map<String, String>> en = new AbstractMap.SimpleEntry<>(name, new HashMap<String, String>()); s = lex(); if (s.isType(SymbolType.PARENT_OPEN)) { s = lex(); if (s.isType(SymbolGroup.STRING)) { en.getValue().put("", s.value.toString()); s = lex(); } else { lexer.pushback(s); do { s = lex(); expected(s, lexer.yyline(), SymbolGroup.IDENTIFIER); String key = s.value.toString(); expectedType(SymbolType.ASSIGN); s = lex(); expected(s, lexer.yyline(), SymbolGroup.STRING); String value = s.value.toString(); en.getValue().put(key, value); s = lex(); } while (s.isType(SymbolType.COMMA)); } expected(s, lexer.yyline(), SymbolType.PARENT_CLOSE); s = lex(); } metadata.add(en); expected(s, lexer.yyline(), SymbolType.BRACKET_CLOSE); s = lex(); } lexer.pushback(s); return metadata; } private void classTraits(List<List<NamespaceItem>> allOpenedNamespaces, boolean outsidePackage, List<AssignableAVM2Item> cinitVariables, Reference<Boolean> cinitNeedsActivation, List<GraphTargetItem> cinit, List<DottedChain> importedClasses, List<NamespaceItem> openedNamespaces, NamespaceItem pkg, String classNameStr, boolean isInterface, List<GraphTargetItem> traits, List<AssignableAVM2Item> iinitVariables, Reference<Boolean> iinitNeedsActivation, Reference<GraphTargetItem> iinit) throws AVM2ParseException, IOException, CompilationException { NamespaceItem publicNs = new NamespaceItem("", Namespace.KIND_PACKAGE); NamespaceItem privateNs = new NamespaceItem(pkg.name.toRawString() + ":" + classNameStr, Namespace.KIND_PRIVATE); NamespaceItem protectedNs = new NamespaceItem(pkg.name.toRawString() + ":" + classNameStr, Namespace.KIND_PROTECTED); NamespaceItem staticProtectedNs = new NamespaceItem(pkg.name.toRawString() + ":" + classNameStr, Namespace.KIND_STATIC_PROTECTED); NamespaceItem packageInternalNs = new NamespaceItem(pkg.name, Namespace.KIND_PACKAGE_INTERNAL); openedNamespaces = new ArrayList<>(openedNamespaces); allOpenedNamespaces.add(openedNamespaces); for (List<NamespaceItem> ln : allOpenedNamespaces) { if (!ln.contains(publicNs)) { ln.add(publicNs); } } openedNamespaces.add(privateNs); openedNamespaces.add(protectedNs); openedNamespaces.add(staticProtectedNs); looptraits: while (true) { TypeItem thisType = new TypeItem(pkg.name.addWithSuffix(classNameStr)); boolean isGetter = false; boolean isSetter = false; boolean isOverride = false; boolean isStatic = false; boolean isFinal = false; boolean isPrivate = false; String customNs = null; NamespaceItem namespace = null; ParsedSymbol s = lex(); //static class initializer if (s.type == SymbolType.CURLY_OPEN) { cinit.addAll(commands(allOpenedNamespaces, thisType, pkg, cinitNeedsActivation, importedClasses, openedNamespaces, new Stack<>(), new HashMap<>(), new HashMap<>(), true, false, 0, cinitVariables)); expectedType(SymbolType.CURLY_CLOSE); } else { lexer.pushback(s); } List<Map.Entry<String, Map<String, String>>> metadata = parseMetadata(); s = lex(); while (s.isType(SymbolType.STATIC, SymbolType.PUBLIC, SymbolType.PRIVATE, SymbolType.PROTECTED, SymbolType.OVERRIDE, SymbolType.FINAL, SymbolType.DYNAMIC, SymbolGroup.IDENTIFIER)) { if (s.type == SymbolType.FINAL) { if (isFinal) { throw new AVM2ParseException("Only one final keyword allowed", lexer.yyline()); } isFinal = true; } else if (s.type == SymbolType.OVERRIDE) { if (isOverride) { throw new AVM2ParseException("Only one override keyword allowed", lexer.yyline()); } isOverride = true; } else if (s.type == SymbolType.STATIC) { if (isInterface) { throw new AVM2ParseException("Interface cannot have static traits", lexer.yyline()); } if (classNameStr == null) { throw new AVM2ParseException("No static keyword allowed here", lexer.yyline()); } if (isStatic) { throw new AVM2ParseException("Only one static keyword allowed", lexer.yyline()); } isStatic = true; } else if (s.type == SymbolType.NAMESPACE) { break; } else if (s.type == SymbolType.NATIVE) { throw new AVM2ParseException("Cannot compile native code", lexer.yyline()); } else if (s.group == SymbolGroup.IDENTIFIER) { customNs = s.value.toString(); } else if (namespace != null) { throw new AVM2ParseException("Only one access identifier allowed", lexer.yyline()); } switch (s.type) { case PUBLIC: namespace = publicNs; if (isInterface) { throw new AVM2ParseException("Interface cannot have public, private or protected modifier", lexer.yyline()); } break; case PRIVATE: isPrivate = true; namespace = privateNs; if (isInterface) { throw new AVM2ParseException("Interface cannot have public, private or protected modifier", lexer.yyline()); } break; case PROTECTED: namespace = protectedNs; if (isInterface) { throw new AVM2ParseException("Interface cannot have public, private or protected modifier", lexer.yyline()); } break; } s = lex(); } if (namespace == null && customNs == null) { namespace = packageInternalNs; } if (namespace == protectedNs && isStatic) { namespace = staticProtectedNs; } if (namespace == null && customNs != null) { //Special: it will be resolved later: namespace = new NamespaceItem(customNs, Namespace.KIND_NAMESPACE); } switch (s.type) { case FUNCTION: s = lex(); if (s.type == SymbolType.GET) { isGetter = true; s = lex(); } else if (s.type == SymbolType.SET) { isSetter = true; s = lex(); } expected(s, lexer.yyline(), SymbolGroup.IDENTIFIER, SymbolType.PARENT_OPEN); String fname = null; //fix for methods with name "get" or "set" - they are not getters/setters! if (s.isType(SymbolType.PARENT_OPEN)) { lexer.pushback(s); if (isGetter) { fname = "get"; isGetter = false; } else if (isSetter) { fname = "set"; isSetter = false; } else { throw new AVM2ParseException("Missing method name", lexer.yyline()); } } else { fname = s.value.toString(); } if (fname.equals(classNameStr)) { //constructor if (isStatic) { throw new AVM2ParseException("Constructor cannot be static", lexer.yyline()); } if (isOverride) { throw new AVM2ParseException("Override flag not allowed for constructor", lexer.yyline()); } if (isFinal) { throw new AVM2ParseException("Final flag not allowed for constructor", lexer.yyline()); } if (isInterface) { throw new AVM2ParseException("Interface cannot have constructor", lexer.yyline()); } iinit.setVal(method(allOpenedNamespaces, outsidePackage, isPrivate, metadata, pkg, false, customNs, iinitNeedsActivation, importedClasses, false, false, thisType, openedNamespaces, false, "", true, iinitVariables)); } else { GraphTargetItem t; if (classNameStr == null) { isStatic = true; } { MethodAVM2Item ft = method(allOpenedNamespaces, outsidePackage, isPrivate, metadata, namespace, isInterface, customNs, new Reference<>(false), importedClasses, isOverride, isFinal, thisType, openedNamespaces, isStatic, fname, true, new ArrayList<>()); if (isGetter) { if (!ft.paramTypes.isEmpty()) { throw new AVM2ParseException("Getter can't have any parameters", lexer.yyline()); } } if (isSetter) { if (ft.paramTypes.size() != 1) { throw new AVM2ParseException("Getter must have exactly one parameter", lexer.yyline()); } } if (isStatic && isInterface) { if (isInterface) { throw new AVM2ParseException("Interface cannot have static fields", lexer.yyline()); } } if (isGetter) { GetterAVM2Item g = new GetterAVM2Item(allOpenedNamespaces, outsidePackage, ft.isPrivate(), ft.metadata, ft.pkg, isInterface, customNs, ft.needsActivation, ft.hasRest, ft.line, ft.isOverride(), ft.isFinal(), isStatic, ft.functionName, ft.paramTypes, ft.paramNames, ft.paramValues, ft.body, ft.subvariables, ft.retType); t = g; } else if (isSetter) { SetterAVM2Item st = new SetterAVM2Item(allOpenedNamespaces, outsidePackage, ft.isPrivate(), ft.metadata, ft.pkg, isInterface, customNs, ft.needsActivation, ft.hasRest, ft.line, ft.isOverride(), ft.isFinal(), isStatic, ft.functionName, ft.paramTypes, ft.paramNames, ft.paramValues, ft.body, ft.subvariables, ft.retType); t = st; } else { t = ft; } } //NOTE: Looks like TraitFunction does not work in FlashPlayer - use MethodTrait instead /*else { t = function(metadata, pkg, isInterface, new Reference<Boolean>(false), importedClasses, namespace, thisType, openedNamespaces, fname, false, new ArrayList<AssignableAVM2Item>()); }*/ traits.add(t); } //} break; case NAMESPACE: if (isInterface) { throw new AVM2ParseException("Interface cannot have namespace fields", lexer.yyline()); } s = lex(); expected(s, lexer.yyline(), SymbolGroup.IDENTIFIER); String nname = s.value.toString(); String nval; s = lex(); if (s.type == SymbolType.ASSIGN) { s = lex(); expected(s, lexer.yyline(), SymbolType.STRING); nval = s.value.toString(); s = lex(); } else { nval = (pkg.name.toRawString() + ":" + classNameStr) + "/" + nname; } if (s.type != SymbolType.SEMICOLON) { lexer.pushback(s); } ConstAVM2Item ns = new ConstAVM2Item(metadata, namespace, customNs, true, nname, new TypeItem(DottedChain.NAMESPACE), new StringAVM2Item(null, null, nval), lexer.yyline()); traits.add(ns); break; case CONST: case VAR: boolean isConst = s.type == SymbolType.CONST; if (isOverride) { throw new AVM2ParseException("Override flag not allowed for " + (isConst ? "consts" : "vars"), lexer.yyline()); } if (isFinal) { throw new AVM2ParseException("Final flag not allowed for " + (isConst ? "consts" : "vars"), lexer.yyline()); } if (isInterface) { throw new AVM2ParseException("Interface cannot have variable/const fields", lexer.yyline()); } s = lex(); expected(s, lexer.yyline(), SymbolGroup.IDENTIFIER); String vcname = s.value.toString(); s = lex(); GraphTargetItem type; if (s.type == SymbolType.COLON) { type = type(allOpenedNamespaces, thisType, pkg, new Reference<>(false), importedClasses, openedNamespaces, new ArrayList<>()); s = lex(); } else { type = TypeItem.UNBOUNDED; } GraphTargetItem value = null; if (s.type == SymbolType.ASSIGN) { value = expression(allOpenedNamespaces, thisType, pkg, new Reference<>(false), importedClasses, openedNamespaces, new HashMap<>(), false, false, true, isStatic || isConst ? cinitVariables : iinitVariables); s = lex(); } GraphTargetItem tar; if (isConst) { tar = new ConstAVM2Item(metadata, namespace, customNs, isStatic, vcname, type, value, lexer.yyline()); } else { tar = new SlotAVM2Item(metadata, namespace, customNs, isStatic, vcname, type, value, lexer.yyline()); } traits.add(tar); if (s.type != SymbolType.SEMICOLON) { lexer.pushback(s); } break; default: lexer.pushback(s); break looptraits; } } } private void scriptTraits(List<List<NamespaceItem>> allOpenedNamespaces, int scriptIndex, String scriptName, List<GraphTargetItem> traits) throws AVM2ParseException, IOException, CompilationException { while (scriptTraitsBlock(allOpenedNamespaces, scriptIndex, scriptName, traits)) { //empty } } private boolean scriptTraitsBlock(List<List<NamespaceItem>> allOpenedNamespaces, int scriptIndex, String scriptName, List<GraphTargetItem> traits) throws AVM2ParseException, IOException, CompilationException { ParsedSymbol s; boolean inPackage = false; s = lex(); List<AssignableAVM2Item> sinitVariables = new ArrayList<>(); NamespaceItem publicNs; NamespaceItem packageInternalNs; if (s.type == SymbolType.PACKAGE) { DottedChain pkgName = DottedChain.TOPLEVEL; s = lex(); if (s.type != SymbolType.CURLY_OPEN) { expected(s, lexer.yyline(), SymbolGroup.IDENTIFIER); pkgName = pkgName.addWithSuffix(s.value.toString()); s = lex(); } while (s.type == SymbolType.DOT) { s = lex(); expected(s, lexer.yyline(), SymbolGroup.IDENTIFIER); pkgName = pkgName.addWithSuffix(s.value.toString()); s = lex(); } expected(s, lexer.yyline(), SymbolType.CURLY_OPEN); publicNs = new NamespaceItem(pkgName, Namespace.KIND_PACKAGE); packageInternalNs = new NamespaceItem(pkgName, Namespace.KIND_PACKAGE_INTERNAL); s = lex(); inPackage = true; } else { publicNs = null; packageInternalNs = new NamespaceItem(scriptName + "$" + scriptIndex, Namespace.KIND_PRIVATE); } lexer.pushback(s); List<NamespaceItem> openedNamespaces = new ArrayList<>(); allOpenedNamespaces.add(openedNamespaces); NamespaceItem emptyNs = new NamespaceItem("", Namespace.KIND_PACKAGE); openedNamespaces.add(emptyNs); NamespaceItem as3Ns = new NamespaceItem(AS3_NAMESPACE, Namespace.KIND_NAMESPACE); as3Ns.forceResolve(abcIndex); openedNamespaces.add(as3Ns); for (List<NamespaceItem> ln : allOpenedNamespaces) { if (publicNs != null && !ln.contains(publicNs)) { ln.add(publicNs); } if (!ln.contains(packageInternalNs)) { ln.add(packageInternalNs); } } List<DottedChain> importedClasses = parseImportsUsages(openedNamespaces); boolean isEmpty = true; looptrait: while (true) { List<Map.Entry<String, Map<String, String>>> metadata = parseMetadata(); s = lex(); boolean isFinal = false; boolean isDynamic = false; boolean isPublic = false; NamespaceItem ns = packageInternalNs; while (s.isType(SymbolType.FINAL, SymbolType.DYNAMIC, SymbolType.PUBLIC)) { if (s.type == SymbolType.FINAL) { if (isFinal) { throw new AVM2ParseException("Only one final keyword allowed", lexer.yyline()); } isFinal = true; } if (s.type == SymbolType.PUBLIC) { if (!inPackage) { throw new AVM2ParseException("public only allowed inside package", lexer.yyline()); } if (isPublic) { throw new AVM2ParseException("Only one public keyword allowed", lexer.yyline()); } isPublic = true; ns = publicNs; } if (s.type == SymbolType.DYNAMIC) { if (isDynamic) { throw new AVM2ParseException("Only one dynamic keyword allowed", lexer.yyline()); } isDynamic = true; } s = lex(); } switch (s.type) { case CLASS: case INTERFACE: isEmpty = false; List<NamespaceItem> subOpenedNamespaces = new ArrayList<>(openedNamespaces); boolean isInterface = false; if (s.type == SymbolType.INTERFACE) { isInterface = true; } GraphTargetItem extendsTypeStr = null; List<GraphTargetItem> interfaces = new ArrayList<>(); String subNameStr; s = lex(); expected(s, lexer.yyline(), SymbolGroup.IDENTIFIER); subNameStr = s.value.toString(); s = lex(); if (!isInterface) { if (s.type == SymbolType.EXTENDS) { extendsTypeStr = type(allOpenedNamespaces, null, ns, new Reference<>(false), importedClasses, openedNamespaces, new ArrayList<>()); s = lex(); } if (s.type == SymbolType.IMPLEMENTS) { do { GraphTargetItem implementsTypeStr = type(allOpenedNamespaces, null, ns, new Reference<>(false), importedClasses, openedNamespaces, new ArrayList<>()); interfaces.add(implementsTypeStr); s = lex(); } while (s.type == SymbolType.COMMA); } expected(s, lexer.yyline(), SymbolType.CURLY_OPEN); } else { if (s.type == SymbolType.EXTENDS) { do { GraphTargetItem intExtendsTypeStr = type(allOpenedNamespaces, null, ns, new Reference<>(false), importedClasses, openedNamespaces, new ArrayList<>()); interfaces.add(intExtendsTypeStr); s = lex(); } while (s.type == SymbolType.COMMA); } expected(s, lexer.yyline(), SymbolType.CURLY_OPEN); } if (extendsTypeStr != null) { List<Integer> indices = new ArrayList<>(); List<String> names = new ArrayList<>(); List<String> namespaces = new ArrayList<>(); //FIXME for Private classes in script (?) AVM2SourceGenerator.parentNamesAddNames(abcIndex, AVM2SourceGenerator.resolveType(new SourceGeneratorLocalData(new HashMap<>(), 0, false, 0), ((TypeItem) ((UnresolvedAVM2Item) extendsTypeStr).resolve(null, new ArrayList<>(), new ArrayList<>(), abcIndex, new ArrayList<>(), new ArrayList<>())), abcIndex), indices, names, namespaces); for (int i = 0; i < names.size(); i++) { if (namespaces.get(i) == null || namespaces.get(i).isEmpty()) { continue; } subOpenedNamespaces.add(new NamespaceItem(namespaces.get(i) + ":" + names.get(i), Namespace.KIND_STATIC_PROTECTED)); } } List<GraphTargetItem> cinit = new ArrayList<>(); Reference<Boolean> cinitNeedsActivation = new Reference<>(false); Reference<GraphTargetItem> iinit = new Reference<>(null); List<AssignableAVM2Item> cinitVariables = new ArrayList<>(); List<AssignableAVM2Item> iinitVariables = new ArrayList<>(); Reference<Boolean> iinitNeedsActivation = new Reference<>(false); List<GraphTargetItem> subTraits = new ArrayList<>(); classTraits(allOpenedNamespaces, !inPackage, cinitVariables, cinitNeedsActivation, cinit, importedClasses, subOpenedNamespaces, ns, subNameStr, isInterface, subTraits, iinitVariables, iinitNeedsActivation, iinit); if (isInterface) { traits.add(new InterfaceAVM2Item(metadata, importedClasses, ns, subOpenedNamespaces, isFinal, subNameStr, interfaces, subTraits)); } else { traits.add(new ClassAVM2Item(metadata, importedClasses, ns, subOpenedNamespaces, isFinal, isDynamic, subNameStr, extendsTypeStr, interfaces, cinit, cinitNeedsActivation.getVal(), cinitVariables, iinit.getVal(), iinitVariables, subTraits, iinitNeedsActivation.getVal())); } expectedType(SymbolType.CURLY_CLOSE); break; case FUNCTION: isEmpty = false; s = lex(); expected(s, lexer.yyline(), SymbolGroup.IDENTIFIER); String fname = s.value.toString(); traits.add(method(allOpenedNamespaces, !inPackage, false, metadata, ns, false, null, new Reference<>(false), importedClasses, false, isFinal, null, openedNamespaces, true, fname, true, new ArrayList<>())); break; case CONST: case VAR: isEmpty = false; boolean isConst = s.type == SymbolType.CONST; if (isFinal) { throw new AVM2ParseException("Final flag not allowed for " + (isConst ? "consts" : "vars"), lexer.yyline()); } s = lex(); expected(s, lexer.yyline(), SymbolGroup.IDENTIFIER); String vcname = s.value.toString(); s = lex(); GraphTargetItem type; if (s.type == SymbolType.COLON) { type = type(allOpenedNamespaces, null, ns, new Reference<>(false), importedClasses, openedNamespaces, new ArrayList<>()); s = lex(); } else { type = TypeItem.UNBOUNDED; } GraphTargetItem value = null; if (s.type == SymbolType.ASSIGN) { value = expression(allOpenedNamespaces, null, ns, new Reference<>(false), importedClasses, openedNamespaces, new HashMap<>(), false, false, true, sinitVariables); s = lex(); } GraphTargetItem tar; if (isConst) { tar = new ConstAVM2Item(metadata, ns, null, false, vcname, type, value, lexer.yyline()); } else { tar = new SlotAVM2Item(metadata, ns, null, false, vcname, type, value, lexer.yyline()); } traits.add(tar); if (s.type != SymbolType.SEMICOLON) { lexer.pushback(s); } break; case NAMESPACE: isEmpty = false; if (isFinal) { throw new AVM2ParseException("Final flag not allowed for namespaces", lexer.yyline()); } s = lex(); expected(s, lexer.yyline(), SymbolGroup.IDENTIFIER); String nname = s.value.toString(); String nval; s = lex(); if (s.type == SymbolType.ASSIGN) { s = lex(); expected(s, lexer.yyline(), SymbolType.STRING); nval = s.value.toString(); s = lex(); } else { nval = ns + "/" + nname; } if (s.type != SymbolType.SEMICOLON) { lexer.pushback(s); } traits.add(new ConstAVM2Item(metadata, ns, null, true, nname, new TypeItem(DottedChain.NAMESPACE), new StringAVM2Item(null, null, nval), lexer.yyline())); break; default: lexer.pushback(s); break looptrait; } } if (inPackage) { expectedType(SymbolType.CURLY_CLOSE); } return !isEmpty; } private GraphTargetItem expressionCommands(ParsedSymbol s, HashMap<String, Integer> registerVars, boolean inFunction, boolean inMethod, int forinlevel, List<AssignableAVM2Item> variables) throws IOException, AVM2ParseException { GraphTargetItem ret = null; switch (s.type) { /*case INT: expectedType(SymbolType.PARENT_OPEN); ret = new ToIntegerAVM2Item(null, null, expression(allOpenedNamespaces, thisType,pkg,needsActivation, importedClasses, openedNamespaces,openedNamespacesKinds,registerVars, inFunction, inMethod, true, variables)); expectedType(SymbolType.PARENT_CLOSE); break; case NUMBER_OP: s = lex(); if (s.type == SymbolType.DOT) { VariableAVM2Item vi = new VariableAVM2Item(s.value.toString(), null, false); variables.add(vi); ret = memberOrCall(allOpenedNamespaces, thisType,vi, registerVars, inFunction, inMethod, variables); } else { expected(s, lexer.yyline(), SymbolType.PARENT_OPEN); ret = new ToNumberAVM2Item(null, null, expression(allOpenedNamespaces, thisType,pkg,needsActivation, importedClasses, openedNamespaces,openedNamespacesKinds,registerVars, inFunction, inMethod, true, variables)); expectedType(SymbolType.PARENT_CLOSE); } break; case STRING_OP: ParsedSymbol sop = s; s = lex(); if (s.type == SymbolType.DOT) { lexer.pushback(s); VariableAVM2Item vi2 = new VariableAVM2Item(sop.value.toString(), null, false); variables.add(vi2); ret = memberOrCall(allOpenedNamespaces, thisType,vi2, registerVars, inFunction, inMethod, variables); } else { expected(s, lexer.yyline(), SymbolType.PARENT_OPEN); ret = new ToStringAVM2Item(null, null, expression(allOpenedNamespaces, thisType,pkg,needsActivation, importedClasses, openedNamespaces,openedNamespacesKinds,registerVars, inFunction, inMethod, true, variables)); expectedType(SymbolType.PARENT_CLOSE); ret = memberOrCall(allOpenedNamespaces, thisType,ret, registerVars, inFunction, inMethod, variables); } break;*/ default: return null; } //return ret; } private GraphTargetItem add(Object a) { if (a instanceof List) { List l = (List) a; if (l.isEmpty()) { return null; } GraphTargetItem o = add(l.get(0)); for (int i = 1; i < l.size(); i++) { o = add(o, l.get(i)); } return o; } if (a instanceof StringBuilder) { if (((StringBuilder) a).length() == 0) { return null; } GraphTargetItem ret = new StringAVM2Item(null, null, a.toString()); ((StringBuilder) a).setLength(0); return ret; } if (a instanceof String) { return new StringAVM2Item(null, null, (String) a); } if (a instanceof GraphTargetItem) { return (GraphTargetItem) a; } return null; } private GraphTargetItem add(Object a, Object b) { GraphTargetItem ta = add(a); GraphTargetItem tb = add(b); if (ta == null && tb == null) { return null; } if (ta == null) { return tb; } if (tb == null) { return ta; } return new AddAVM2Item(null, null, ta, tb); } private void addS(List<GraphTargetItem> rets, StringBuilder sb) { if (sb.length() > 0) { if (!rets.isEmpty() && (rets.get(rets.size() - 1) instanceof StringAVM2Item)) { StringAVM2Item stringItem = ((StringAVM2Item) rets.get(rets.size() - 1)); stringItem.setValue(stringItem.getValue() + sb.toString()); } else { rets.add(new StringAVM2Item(null, null, sb.toString())); } sb.setLength(0); } } private List<GraphTargetItem> xmltag(List<List<NamespaceItem>> allOpenedNamespaces, TypeItem thisType, NamespaceItem pkg, Reference<Boolean> usesVars, List<String> openedTags, Reference<Integer> closedVarTags, Reference<Boolean> needsActivation, List<DottedChain> importedClasses, List<NamespaceItem> openedNamespaces, HashMap<String, Integer> registerVars, boolean inFunction, boolean inMethod, List<AssignableAVM2Item> variables) throws IOException, AVM2ParseException { ParsedSymbol s; List<GraphTargetItem> rets = new ArrayList<>(); //GraphTargetItem ret = null; StringBuilder sb = new StringBuilder(); loop: do { s = lex(); List<String> sub = new ArrayList<>(); Reference<Integer> subclose = new Reference<>(0); Reference<Boolean> subusesvars = new Reference<>(false); switch (s.type) { case XML_ATTRNAMEVAR_BEGIN: //add usesVars.setVal(true); addS(rets, sb); rets.add(expression(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables)); expectedType(SymbolType.CURLY_CLOSE); expectedType(SymbolType.ASSIGN); sb.append("="); lexer.yybegin(ActionScriptLexer.XMLOPENTAGATTRIB); break; case XML_ATTRVALVAR_BEGIN: //esc_xattr usesVars.setVal(true); sb.append("\""); addS(rets, sb); rets.add(new EscapeXAttrAVM2Item(null, null, expression(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables))); sb.append("\""); expectedType(SymbolType.CURLY_CLOSE); lexer.yybegin(ActionScriptLexer.XMLOPENTAG); break; case XML_INSTRATTRNAMEVAR_BEGIN: //add usesVars.setVal(true); addS(rets, sb); rets.add(expression(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables)); expectedType(SymbolType.CURLY_CLOSE); expectedType(SymbolType.ASSIGN); sb.append("="); lexer.yybegin(ActionScriptLexer.XMLOPENTAGATTRIB); break; case XML_INSTRATTRVALVAR_BEGIN: //esc_xattr usesVars.setVal(true); sb.append("\""); addS(rets, sb); rets.add(new EscapeXAttrAVM2Item(null, null, expression(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables))); sb.append("\""); expectedType(SymbolType.CURLY_CLOSE); lexer.yybegin(ActionScriptLexer.XMLOPENTAG); break; case XML_VAR_BEGIN: //esc_xelem usesVars.setVal(true); addS(rets, sb); rets.add(new EscapeXElemAVM2Item(null, null, expression(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables))); expectedType(SymbolType.CURLY_CLOSE); lexer.yybegin(ActionScriptLexer.XML); break; case XML_FINISHVARTAG_BEGIN: //add usesVars.setVal(true); closedVarTags.setVal(closedVarTags.getVal() + 1); sb.append("</"); addS(rets, sb); rets.add(expression(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables)); expectedType(SymbolType.CURLY_CLOSE); expectedType(SymbolType.GREATER_THAN); sb.append(">"); addS(rets, sb); lexer.yybegin(ActionScriptLexer.XML); break; case XML_STARTVARTAG_BEGIN: //add //openedTags.add("*"); //ret = add(ret, ); GraphTargetItem ex = expression(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); expectedType(SymbolType.CURLY_CLOSE); lexer.yybegin(ActionScriptLexer.XMLOPENTAG); sub.add("*"); sb.append("<"); addS(rets, sb); rets.add(ex); rets.addAll(xmltag(allOpenedNamespaces, thisType, pkg, subusesvars, sub, subclose, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, variables)); closedVarTags.setVal(subclose.getVal() + subclose.getVal()); break; case XML_INSTRVARTAG_BEGIN: //add usesVars.setVal(true); addS(rets, sb); sb.append("<?"); addS(rets, sb); rets.add(expression(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables)); expectedType(SymbolType.CURLY_CLOSE); lexer.yybegin(ActionScriptLexer.XMLINSTROPENTAG); break; case XML_STARTTAG_BEGIN: sub.add(s.value.toString().trim().substring(1)); //remove < from beginning List<GraphTargetItem> st = xmltag(allOpenedNamespaces, thisType, pkg, subusesvars, sub, closedVarTags, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, variables); sb.append(s.value.toString()); addS(rets, sb); rets.addAll(st); closedVarTags.setVal(subclose.getVal() + subclose.getVal()); break; /*case XML_STARTTAG_END: sb.append(s.value.toString()); ret = addstr(ret,sb); break;*/ case XML_FINISHTAG: String tname = s.value.toString().substring(2, s.value.toString().length() - 1).trim(); if (openedTags.contains(tname)) { openedTags.remove(tname); } else if (openedTags.contains("*")) { openedTags.remove("*"); } else { throw new AVM2ParseException("XML : Closing unopened tag", lexer.yyline()); } sb.append(s.value.toString()); break; case XML_STARTFINISHTAG_END: openedTags.remove(openedTags.size() - 1); //close last tag sb.append(s.value.toString()); break; case EOF: throw new AVM2ParseException("End of file before XML finish", lexer.yyline()); default: sb.append(s.value.toString()); break; } } while (!(openedTags.isEmpty() || closedVarTags.getVal() >= openedTags.size())); addS(rets, sb); return rets; } private GraphTargetItem xml(List<List<NamespaceItem>> allOpenedNamespaces, TypeItem thisType, NamespaceItem pkg, Reference<Boolean> needsActivation, List<DottedChain> importedClasses, List<NamespaceItem> openedNamespaces, HashMap<String, Integer> registerVars, boolean inFunction, boolean inMethod, List<AssignableAVM2Item> variables) throws IOException, AVM2ParseException { List<String> openedTags = new ArrayList<>(); int closedVarTags = 0; GraphTargetItem ret = add(xmltag(allOpenedNamespaces, thisType, pkg, new Reference<>(false), openedTags, new Reference<>(closedVarTags), needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, variables)); ret = new XMLAVM2Item(ret); lexer.yybegin(ActionScriptLexer.YYINITIAL); //TODO: Order of additions as in official compiler return ret; } private GraphTargetItem command(List<List<NamespaceItem>> allOpenedNamespaces, TypeItem thisType, NamespaceItem pkg, Reference<Boolean> needsActivation, List<DottedChain> importedClasses, List<NamespaceItem> openedNamespaces, Stack<Loop> loops, Map<Loop, String> loopLabels, HashMap<String, Integer> registerVars, boolean inFunction, boolean inMethod, int forinlevel, boolean mustBeCommand, List<AssignableAVM2Item> variables) throws IOException, AVM2ParseException { LexBufferer buf = new LexBufferer(); lexer.addListener(buf); GraphTargetItem ret = null; if (debugMode) { System.out.println("command:"); } ParsedSymbol s = lex(); if (s.type == SymbolType.EOF) { return null; } String loopLabel = null; if (s.group == SymbolGroup.IDENTIFIER) { ParsedSymbol sc = lex(); if (sc.type == SymbolType.COLON) { loopLabel = s.value.toString(); s = lex(); } else { lexer.pushback(sc); } } if (s.type == SymbolType.DEFAULT) { ParsedSymbol sx = lex(); if (sx.group != SymbolGroup.IDENTIFIER) { lexer.pushback(sx); } else if (!sx.value.equals("xml")) { lexer.pushback(sx); } else { expectedType(SymbolType.NAMESPACE); expectedType(SymbolType.ASSIGN); GraphTargetItem ns = expression(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); ret = new DefaultXMLNamespace(null, null, ns); //TODO: use dxns for attribute namespaces instead of dxnslate } } if (ret == null) { switch (s.type) { case USE: expectedType(SymbolType.NAMESPACE); GraphTargetItem ns = type(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, variables); openedNamespaces.add(new NamespaceItem(ns.toString(), Namespace.KIND_PACKAGE /*FIXME?*/)); break; case WITH: needsActivation.setVal(true); expectedType(SymbolType.PARENT_OPEN); GraphTargetItem wvar = expression(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables);//(name(allOpenedNamespaces, thisType,false, openedNamespaces, registerVars, inFunction, inMethod, variables)); if (!isNameOrProp(wvar)) { throw new AVM2ParseException("Not a property or name", lexer.yyline()); } expectedType(SymbolType.PARENT_CLOSE); expectedType(SymbolType.CURLY_OPEN); List<AssignableAVM2Item> withVars = new ArrayList<>(); List<GraphTargetItem> wcmd = commands(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, withVars); variables.addAll(withVars); for (AssignableAVM2Item a : withVars) { if (a instanceof UnresolvedAVM2Item) { UnresolvedAVM2Item ua = (UnresolvedAVM2Item) a; ua.scopeStack.add(0, wvar); } } expectedType(SymbolType.CURLY_CLOSE); ret = new WithAVM2Item(null, null, wvar, wcmd); ((WithAVM2Item) ret).subvariables = withVars; break; /*case DELETE: GraphTargetItem varDel = expression(allOpenedNamespaces, thisType,pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables);//name(allOpenedNamespaces, thisType,false, openedNamespaces, registerVars, inFunction, inMethod, variables); if(!isNameOrProp(varDel)){ throw new ParseException("Not a property or name", lexer.yyline()); } if (varDel instanceof GetPropertyAVM2Item) { GetPropertyAVM2Item gm = (GetPropertyAVM2Item) varDel; ret = new DeletePropertyAVM2Item(null, null, gm.object, gm.propertyName); } else if (varDel instanceof NameAVM2Item) { variables.remove(varDel); ret = new DeletePropertyAVM2Item(null, null, null, (NameAVM2Item) varDel); } else { throw new ParseException("Not a property", lexer.yyline()); } break;*/ case FUNCTION: s = lexer.lex(); expected(s, lexer.yyline(), SymbolGroup.IDENTIFIER); needsActivation.setVal(true); ret = (function(allOpenedNamespaces, new ArrayList<>(), pkg, false, needsActivation, importedClasses, thisType, openedNamespaces, s.value.toString(), false, variables)); break; case VAR: s = lex(); expected(s, lexer.yyline(), SymbolGroup.IDENTIFIER); String varIdentifier = s.value.toString(); s = lex(); GraphTargetItem type; if (s.type == SymbolType.COLON) { type = type(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, variables); s = lex(); } else { type = new UnboundedTypeItem(); } if (s.type == SymbolType.ASSIGN) { GraphTargetItem varval = (expression(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables)); ret = new NameAVM2Item(type, lexer.yyline(), varIdentifier, varval, true, openedNamespaces); variables.add((NameAVM2Item) ret); } else { ret = new NameAVM2Item(type, lexer.yyline(), varIdentifier, null, true, openedNamespaces); variables.add((NameAVM2Item) ret); lexer.pushback(s); } break; case CURLY_OPEN: ret = new BlockItem(null, null, commands(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, variables)); expectedType(SymbolType.CURLY_CLOSE); break; /*case INCREMENT: //preincrement case DECREMENT: //predecrement GraphTargetItem varincdec = expression(allOpenedNamespaces, thisType,pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables);//name(allOpenedNamespaces, thisType,false, openedNamespaces, registerVars, inFunction, inMethod, variables); if(!isNameOrProp(varincdec)){ throw new ParseException("Not a property or name", lexer.yyline()); } if (s.type == SymbolType.INCREMENT) { ret = new PreIncrementAVM2Item(null, null, varincdec); } else if (s.type == SymbolType.DECREMENT) { ret = new PreDecrementAVM2Item(null, null, varincdec); } break;*/ case SUPER: //constructor call ParsedSymbol ss2 = lex(); if (ss2.type == SymbolType.PARENT_OPEN) { List<GraphTargetItem> args = call(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, variables); ret = new ConstructSuperAVM2Item(null, null, new LocalRegAVM2Item(null, null, 0, null), args); } else {//no costructor call, but it could be calling parent methods... => handle in expression lexer.pushback(ss2); lexer.pushback(s); } break; case IF: expectedType(SymbolType.PARENT_OPEN); GraphTargetItem ifExpr = (expression(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables)); expectedType(SymbolType.PARENT_CLOSE); GraphTargetItem onTrue = command(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, true, variables); List<GraphTargetItem> onTrueList = new ArrayList<>(); onTrueList.add(onTrue); s = lex(); List<GraphTargetItem> onFalseList = null; if (s.type == SymbolType.ELSE) { onFalseList = new ArrayList<>(); onFalseList.add(command(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, true, variables)); } else { lexer.pushback(s); } ret = new IfItem(null, null, ifExpr, onTrueList, onFalseList); break; case WHILE: expectedType(SymbolType.PARENT_OPEN); List<GraphTargetItem> whileExpr = new ArrayList<>(); whileExpr.add(commaExpression(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, variables)); expectedType(SymbolType.PARENT_CLOSE); List<GraphTargetItem> whileBody = new ArrayList<>(); Loop wloop = new Loop(uniqId(), null, null); if (loopLabel != null) { loopLabels.put(wloop, loopLabel); } loops.push(wloop); whileBody.add(command(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, true, variables)); ret = new WhileItem(null, null, wloop, whileExpr, whileBody); break; case DO: List<GraphTargetItem> doBody = new ArrayList<>(); Loop dloop = new Loop(uniqId(), null, null); loops.push(dloop); if (loopLabel != null) { loopLabels.put(dloop, loopLabel); } doBody.add(command(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, true, variables)); expectedType(SymbolType.WHILE); expectedType(SymbolType.PARENT_OPEN); List<GraphTargetItem> doExpr = new ArrayList<>(); doExpr.add(commaExpression(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, variables)); expectedType(SymbolType.PARENT_CLOSE); ret = new DoWhileItem(null, null, dloop, doBody, doExpr); break; case FOR: s = lex(); boolean forin = false; boolean each = false; GraphTargetItem collection = null; if (s.type == SymbolType.EACH) { each = true; forin = true; s = lex(); } expected(s, lexer.yyline(), SymbolType.PARENT_OPEN); GraphTargetItem firstCommand = command(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, false, variables); if (firstCommand instanceof NameAVM2Item) { NameAVM2Item nai = (NameAVM2Item) firstCommand; if (nai.isDefinition() && nai.getAssignedValue() == null) { //Declared value in for..in firstCommand = expression1(allOpenedNamespaces, firstCommand, GraphTargetItem.NOPRECEDENCE, thisType, pkg, needsActivation, importedClasses, openedNamespaces, true, registerVars, inFunction, inMethod, true, variables); } } InAVM2Item inexpr = null; if (firstCommand instanceof InAVM2Item) { forin = true; inexpr = (InAVM2Item) firstCommand; } else if (forin) { throw new AVM2ParseException("In expression required", lexer.yyline()); } Loop floop = new Loop(uniqId(), null, null); loops.push(floop); if (loopLabel != null) { loopLabels.put(floop, loopLabel); } List<GraphTargetItem> forFinalCommands = new ArrayList<>(); GraphTargetItem forExpr = null; List<GraphTargetItem> forFirstCommands = new ArrayList<>(); if (!forin) { s = lex(); if (firstCommand != null) { //can be empty command forFirstCommands.add(firstCommand); } while (s.isType(SymbolType.COMMA)) { forFirstCommands.add(command(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, false, variables)); s = lex(); } lexer.pushback(s); //GraphTargetItem firstCommand = command(thisType,pkg,needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, true, variables); forExpr = (expression(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables)); expectedType(SymbolType.SEMICOLON); GraphTargetItem fcom = command(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, true, variables); if (fcom != null) { forFinalCommands.add(fcom); } } expectedType(SymbolType.PARENT_CLOSE); List<GraphTargetItem> forBody = new ArrayList<>(); forBody.add(command(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forin ? forinlevel + 1 : forinlevel, true, variables)); if (forin) { if (each) { ret = new ForEachInAVM2Item(null, null, floop, inexpr, forBody); } else { ret = new ForInAVM2Item(null, null, floop, inexpr, forBody); } } else { ret = new ForItem(null, null, floop, forFirstCommands, forExpr, forFinalCommands, forBody); } break; case SWITCH: Loop sloop = new Loop(-uniqId(), null, null); //negative id marks switch = no continue loops.push(sloop); if (loopLabel != null) { loopLabels.put(sloop, loopLabel); } expectedType(SymbolType.PARENT_OPEN); GraphTargetItem switchExpr = expression(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); expectedType(SymbolType.PARENT_CLOSE); expectedType(SymbolType.CURLY_OPEN); s = lex(); //ret.addAll(switchExpr); int exprReg = 0; for (int i = 0; i < 256; i++) { if (!registerVars.containsValue(i)) { registerVars.put("__switch" + uniqId(), i); exprReg = i; break; } } List<List<ActionIf>> caseIfs = new ArrayList<>(); List<List<GraphTargetItem>> caseCmds = new ArrayList<>(); List<GraphTargetItem> caseExprsAll = new ArrayList<>(); List<Integer> valueMapping = new ArrayList<>(); int pos = 0; while (s.type == SymbolType.CASE || s.type == SymbolType.DEFAULT) { while (s.type == SymbolType.CASE || s.type == SymbolType.DEFAULT) { GraphTargetItem curCaseExpr = s.type == SymbolType.DEFAULT ? new DefaultItem() : expression(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); expectedType(SymbolType.COLON); s = lex(); caseExprsAll.add(curCaseExpr); valueMapping.add(pos); } pos++; lexer.pushback(s); List<GraphTargetItem> caseCmd = commands(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, variables); caseCmds.add(caseCmd); s = lex(); } expected(s, lexer.yyline(), SymbolType.CURLY_CLOSE); ret = new SwitchItem(null, null, sloop, switchExpr, caseExprsAll, caseCmds, valueMapping); break; case BREAK: s = lex(); long bloopId = 0; if (loops.isEmpty()) { throw new AVM2ParseException("No loop to break", lexer.yyline()); } if (s.group == SymbolGroup.IDENTIFIER) { String breakLabel = s.value.toString(); for (Loop l : loops) { if (breakLabel.equals(loopLabels.get(l))) { bloopId = l.id; break; } } if (bloopId == 0) { throw new AVM2ParseException("Identifier of loop expected", lexer.yyline()); } } else { lexer.pushback(s); bloopId = loops.peek().id; } ret = new BreakItem(null, null, bloopId); break; case CONTINUE: s = lex(); long cloopId = 0; if (loops.isEmpty()) { throw new AVM2ParseException("No loop to continue", lexer.yyline()); } if (s.group == SymbolGroup.IDENTIFIER) { String continueLabel = s.value.toString(); for (Loop l : loops) { if (l.id < 0) { //negative id marks switch => no continue continue; } if (continueLabel.equals(loopLabels.get(l))) { cloopId = l.id; break; } } if (cloopId == -1) { throw new AVM2ParseException("Identifier of loop expected", lexer.yyline()); } } else { lexer.pushback(s); for (int i = loops.size() - 1; i >= 0; i--) { if (loops.get(i).id >= 0) {//no switches cloopId = loops.get(i).id; break; } } if (cloopId <= 0) { throw new AVM2ParseException("No loop to continue", lexer.yyline()); } } //TODO: handle switch ret = new ContinueItem(null, null, cloopId); break; case RETURN: GraphTargetItem retexpr = expression(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, true, registerVars, inFunction, inMethod, true, variables); if (retexpr == null) { ret = new ReturnVoidAVM2Item(null, null); } else { ret = new ReturnValueAVM2Item(null, null, retexpr); } break; case TRY: needsActivation.setVal(true); List<GraphTargetItem> tryCommands = new ArrayList<>(); tryCommands.add(command(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, true, variables)); s = lex(); boolean found = false; List<List<GraphTargetItem>> catchCommands = new ArrayList<>(); List<NameAVM2Item> catchExceptions = new ArrayList<>(); int varCnt = variables.size(); List<List<AssignableAVM2Item>> catchesVars = new ArrayList<>(); while (s.type == SymbolType.CATCH) { expectedType(SymbolType.PARENT_OPEN); s = lex(); expected(s, lexer.yyline(), SymbolGroup.IDENTIFIER, SymbolType.THIS, SymbolType.SUPER, SymbolType.STRING_OP); String enamestr = s.value.toString(); expectedType(SymbolType.COLON); GraphTargetItem etype = type(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, variables); NameAVM2Item e = new NameAVM2Item(etype, lexer.yyline(), enamestr, new ExceptionAVM2Item(null)/*?*/, true/*?*/, openedNamespaces); variables.add(e); catchExceptions.add(e); e.setSlotNumber(1); e.setSlotScope(Integer.MAX_VALUE); //will be changed later expectedType(SymbolType.PARENT_CLOSE); List<GraphTargetItem> cc = new ArrayList<>(); List<AssignableAVM2Item> catchVars = new ArrayList<>(); cc.add(command(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, true, catchVars)); catchesVars.add(catchVars); variables.addAll(catchVars); for (AssignableAVM2Item a : catchVars) { if (a instanceof UnresolvedAVM2Item) { UnresolvedAVM2Item ui = (UnresolvedAVM2Item) a; if (ui.getVariableName().equals(DottedChain.parseWithSuffix(e.getVariableName()))) { try { ui.resolve(null, new ArrayList<>(), new ArrayList<>(), abcIndex, new ArrayList<>(), variables); } catch (CompilationException ex) { // ignore } ui.setSlotNumber(e.getSlotNumber()); ui.setSlotScope(e.getSlotScope()); } } } catchCommands.add(cc); s = lex(); found = true; } //TODO: for (int i = varCnt; i < variables.size(); i++) { AssignableAVM2Item av = variables.get(i); if (av instanceof UnresolvedAVM2Item) { UnresolvedAVM2Item ui = (UnresolvedAVM2Item) av; for (NameAVM2Item e : catchExceptions) { if (ui.getVariableName().equals(DottedChain.parseWithSuffix(e.getVariableName()))) { try { ui.resolve(null, new ArrayList<>(), new ArrayList<>(), abcIndex, new ArrayList<>(), variables); } catch (CompilationException ex) { // ignore } ui.setSlotNumber(e.getSlotNumber()); ui.setSlotScope(e.getSlotScope()); } } } } List<GraphTargetItem> finallyCommands = null; if (s.type == SymbolType.FINALLY) { finallyCommands = new ArrayList<>(); finallyCommands.add(command(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, true, variables)); found = true; s = lex(); } if (!found) { expected(s, lexer.yyline(), SymbolType.CATCH, SymbolType.FINALLY); } lexer.pushback(s); TryAVM2Item tai = new TryAVM2Item(tryCommands, null, catchCommands, finallyCommands, ""); tai.catchVariables = catchesVars; tai.catchExceptions2 = catchExceptions; ret = tai; break; case THROW: ret = new ThrowAVM2Item(null, null, expression(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables)); break; default: GraphTargetItem valcmd = expressionCommands(s, registerVars, inFunction, inMethod, forinlevel, variables); if (valcmd != null) { ret = valcmd; break; } if (s.type == SymbolType.SEMICOLON) { return null; } lexer.pushback(s); ret = expression(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); if (debugMode) { System.out.println("/command"); } } } if (debugMode) { System.out.println("/command"); } lexer.removeListener(buf); if (ret == null) { //can be popped expression buf.pushAllBack(lexer); ret = expression(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); } s = lex(); if ((s != null) && (s.type != SymbolType.SEMICOLON)) { lexer.pushback(s); } return ret; } private GraphTargetItem expression(List<List<NamespaceItem>> allOpenedNamespaces, TypeItem thisType, NamespaceItem pkg, Reference<Boolean> needsActivation, List<DottedChain> importedClasses, List<NamespaceItem> openedNamespaces, HashMap<String, Integer> registerVars, boolean inFunction, boolean inMethod, boolean allowRemainder, List<AssignableAVM2Item> variables) throws IOException, AVM2ParseException { return expression(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, false, registerVars, inFunction, inMethod, allowRemainder, variables); } /*private GraphTargetItem expressionRemainder(TypeItem thisType, String pkg, Reference<Boolean> needsActivation, List<NamespaceItem> openedNamespaces, GraphTargetItem expr, HashMap<String, Integer> registerVars, boolean inFunction, boolean inMethod, boolean allowRemainder, List<AssignableAVM2Item> variables, List<DottedChain> importedClasses) throws IOException, AVM2ParseException { GraphTargetItem ret = null; ParsedSymbol s = lex(); ret = fixPrecedence(ret); return ret; }*/ private boolean isNameOrProp(GraphTargetItem item) { if (item instanceof UnresolvedAVM2Item) { return true; //we don't know yet } if (item instanceof NameAVM2Item) { return true; } if (item instanceof PropertyAVM2Item) { return true; } return (item instanceof IndexAVM2Item); } private boolean isType(GraphTargetItem item) { if (item == null) { return false; } while (item instanceof GetPropertyAVM2Item) { item = ((GetPropertyAVM2Item) item).object; } return (item instanceof NameAVM2Item); } private int brackets(List<List<NamespaceItem>> allOpenedNamespaces, TypeItem thisType, NamespaceItem pkg, Reference<Boolean> needsActivation, List<DottedChain> importedClasses, List<NamespaceItem> openedNamespaces, List<GraphTargetItem> ret, HashMap<String, Integer> registerVars, boolean inFunction, boolean inMethod, List<AssignableAVM2Item> variables) throws IOException, AVM2ParseException { ParsedSymbol s = lex(); int arrCnt = 0; if (s.type == SymbolType.BRACKET_OPEN) { s = lex(); while (s.type != SymbolType.BRACKET_CLOSE) { if (s.type != SymbolType.COMMA) { lexer.pushback(s); } arrCnt++; ret.add(expression(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables)); s = lex(); if (!s.isType(SymbolType.COMMA, SymbolType.BRACKET_CLOSE)) { expected(s, lexer.yyline(), SymbolType.COMMA, SymbolType.BRACKET_CLOSE); } } } else { lexer.pushback(s); return -1; } return arrCnt; } private GraphTargetItem commaExpression(List<List<NamespaceItem>> allOpenedNamespaces, TypeItem thisType, NamespaceItem pkg, Reference<Boolean> needsActivation, List<DottedChain> importedClasses, List<NamespaceItem> openedNamespaces, Stack<Loop> loops, Map<Loop, String> loopLabels, HashMap<String, Integer> registerVars, boolean inFunction, boolean inMethod, int forInLevel, List<AssignableAVM2Item> variables) throws IOException, AVM2ParseException { GraphTargetItem cmd = null; List<GraphTargetItem> expr = new ArrayList<>(); ParsedSymbol s; do { cmd = command(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forInLevel, false, variables); if (cmd != null) { expr.add(cmd); } s = lex(); } while (s.type == SymbolType.COMMA && cmd != null); lexer.pushback(s); if (cmd == null) { expr.add(expression(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables)); } else if (!cmd.hasReturnValue()) { throw new AVM2ParseException("Expression expected", lexer.yyline()); } return new CommaExpressionItem(null, null, expr); } private GraphTargetItem expression(List<List<NamespaceItem>> allOpenedNamespaces, TypeItem thisType, NamespaceItem pkg, Reference<Boolean> needsActivation, List<DottedChain> importedClasses, List<NamespaceItem> openedNamespaces, boolean allowEmpty, HashMap<String, Integer> registerVars, boolean inFunction, boolean inMethod, boolean allowRemainder, List<AssignableAVM2Item> variables) throws IOException, AVM2ParseException { GraphTargetItem prim = expressionPrimary(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, allowEmpty, registerVars, inFunction, inMethod, allowRemainder, variables); if (prim == null) { return null; } return expression1(allOpenedNamespaces, prim, GraphTargetItem.NOPRECEDENCE, thisType, pkg, needsActivation, importedClasses, openedNamespaces, allowEmpty, registerVars, inFunction, inMethod, allowRemainder, variables); } /** * Lexer can return XML opentags instead of greater. In expression, we need * greater sign only * * @param symb */ private void xmlToLowerThanFix(ParsedSymbol symb) { if (symb.isType(SymbolType.XML_STARTVARTAG_BEGIN, SymbolType.XML_STARTTAG_BEGIN)) { lexer.yypushbackstr(symb.value.toString().substring(1)); //parse again as LOWER_THAN String pb = symb.value.toString().substring(1); symb.type = SymbolType.LOWER_THAN; symb.group = SymbolGroup.OPERATOR; symb.value = "<"; if (pb.charAt(0) == '=') { symb.type = SymbolType.LOWER_EQUAL; symb.value = "<="; pb = pb.substring(1); } lexer.yypushbackstr(pb); //parse again as LOWER_THAN } } private void regexpToDivideFix(ParsedSymbol symb) { if (symb.isType(SymbolType.REGEXP)) { String pb = symb.value.toString().substring(1); symb.type = SymbolType.DIVIDE; symb.group = SymbolGroup.OPERATOR; symb.value = "/"; if (pb.charAt(0) == '=') { symb.type = SymbolType.ASSIGN_DIVIDE; symb.value = "/="; pb = pb.substring(1); } lexer.yypushbackstr(pb); //parse again as DIVIDE } } private ParsedSymbol peekExprToken() throws IOException, AVM2ParseException { ParsedSymbol lookahead = lex(); xmlToLowerThanFix(lookahead); regexpToDivideFix(lookahead); lexer.pushback(lookahead); return lookahead; } private GraphTargetItem expression1(List<List<NamespaceItem>> allOpenedNamespaces, GraphTargetItem lhs, int min_precedence, TypeItem thisType, NamespaceItem pkg, Reference<Boolean> needsActivation, List<DottedChain> importedClasses, List<NamespaceItem> openedNamespaces, boolean allowEmpty, HashMap<String, Integer> registerVars, boolean inFunction, boolean inMethod, boolean allowRemainder, List<AssignableAVM2Item> variables) throws IOException, AVM2ParseException { if (debugMode) { System.out.println("expression1:"); } ParsedSymbol lookahead = peekExprToken(); ParsedSymbol op; GraphTargetItem rhs; GraphTargetItem mhs = null; //Note: algorithm from http://en.wikipedia.org/wiki/Operator-precedence_parser //with relation operators reversed as we have precedence in reverse order while (lookahead.type.isBinary() && lookahead.type.getPrecedence() <= /* >= on wiki */ min_precedence) { op = lookahead; lex(); //Note: Handle ternar operator as Binary //http://stackoverflow.com/questions/13681293/how-can-i-incorporate-ternary-operators-into-a-precedence-climbing-algorithm if (op.type == SymbolType.TERNAR) { if (debugMode) { System.out.println("ternar-middle:"); } mhs = expression(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, allowRemainder, variables); expectedType(SymbolType.COLON); if (debugMode) { System.out.println("/ternar-middle"); } } rhs = expressionPrimary(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, allowEmpty, registerVars, inFunction, inMethod, allowRemainder, variables); if (rhs == null) { lexer.pushback(op); break; } lookahead = peekExprToken(); while ((lookahead.type.isBinary() && lookahead.type.getPrecedence() < /* > on wiki */ op.type.getPrecedence()) || (lookahead.type.isRightAssociative() && lookahead.type.getPrecedence() == op.type.getPrecedence())) { rhs = expression1(allOpenedNamespaces, rhs, lookahead.type.getPrecedence(), thisType, pkg, needsActivation, importedClasses, openedNamespaces, allowEmpty, registerVars, inFunction, inMethod, allowRemainder, variables); lookahead = peekExprToken(); } switch (op.type) { case AS: //GraphTargetItem type = type(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, variables); lhs = new AsTypeAVM2Item(null, null, lhs, rhs); //??? allowRemainder = false; break; case IN: lhs = new InAVM2Item(null, null, lhs, rhs); break; case TERNAR: //??? lhs = new TernarOpItem(null, null, lhs, mhs, rhs); break; case SHIFT_LEFT: lhs = new LShiftAVM2Item(null, null, lhs, rhs); break; case SHIFT_RIGHT: lhs = new RShiftAVM2Item(null, null, lhs, rhs); break; case USHIFT_RIGHT: lhs = new URShiftAVM2Item(null, null, lhs, rhs); break; case BITAND: lhs = new BitAndAVM2Item(null, null, lhs, rhs); break; case BITOR: lhs = new BitOrAVM2Item(null, null, lhs, rhs); break; case DIVIDE: lhs = new DivideAVM2Item(null, null, lhs, rhs); break; case MODULO: lhs = new ModuloAVM2Item(null, null, lhs, rhs); break; case EQUALS: lhs = new EqAVM2Item(null, null, lhs, rhs); break; case STRICT_EQUALS: lhs = new StrictEqAVM2Item(null, null, lhs, rhs); break; case NOT_EQUAL: lhs = new NeqAVM2Item(null, null, lhs, rhs); break; case STRICT_NOT_EQUAL: lhs = new StrictNeqAVM2Item(null, null, lhs, rhs); break; case LOWER_THAN: lhs = new LtAVM2Item(null, null, lhs, rhs); break; case LOWER_EQUAL: lhs = new LeAVM2Item(null, null, lhs, rhs); break; case GREATER_THAN: lhs = new GtAVM2Item(null, null, lhs, rhs); break; case GREATER_EQUAL: lhs = new GeAVM2Item(null, null, lhs, rhs); break; case AND: lhs = new AndItem(null, null, lhs, rhs); break; case OR: lhs = new OrItem(null, null, lhs, rhs); break; case MINUS: lhs = new SubtractAVM2Item(null, null, lhs, rhs); break; case MULTIPLY: lhs = new MultiplyAVM2Item(null, null, lhs, rhs); break; case PLUS: lhs = new AddAVM2Item(null, null, lhs, rhs); break; case XOR: lhs = new BitXorAVM2Item(null, null, lhs, rhs); break; case INSTANCEOF: lhs = new InstanceOfAVM2Item(null, null, lhs, rhs); break; case IS: GraphTargetItem istype = rhs;//type(allOpenedNamespaces, thisType,pkg,needsActivation, importedClasses, openedNamespaces, variables); lhs = new IsTypeAVM2Item(null, null, lhs, istype); break; case ASSIGN: case ASSIGN_BITAND: case ASSIGN_BITOR: case ASSIGN_DIVIDE: case ASSIGN_MINUS: case ASSIGN_MODULO: case ASSIGN_MULTIPLY: case ASSIGN_PLUS: case ASSIGN_SHIFT_LEFT: case ASSIGN_SHIFT_RIGHT: case ASSIGN_USHIFT_RIGHT: case ASSIGN_XOR: GraphTargetItem assigned = rhs; switch (op.type) { case ASSIGN: //assigned = assigned; break; case ASSIGN_BITAND: assigned = new BitAndAVM2Item(null, null, lhs, assigned); break; case ASSIGN_BITOR: assigned = new BitOrAVM2Item(null, null, lhs, assigned); break; case ASSIGN_DIVIDE: assigned = new DivideAVM2Item(null, null, lhs, assigned); break; case ASSIGN_MINUS: assigned = new SubtractAVM2Item(null, null, lhs, assigned); break; case ASSIGN_MODULO: assigned = new ModuloAVM2Item(null, null, lhs, assigned); break; case ASSIGN_MULTIPLY: assigned = new MultiplyAVM2Item(null, null, lhs, assigned); break; case ASSIGN_PLUS: assigned = new AddAVM2Item(null, null, lhs, assigned); break; case ASSIGN_SHIFT_LEFT: assigned = new LShiftAVM2Item(null, null, lhs, assigned); break; case ASSIGN_SHIFT_RIGHT: assigned = new RShiftAVM2Item(null, null, lhs, assigned); break; case ASSIGN_USHIFT_RIGHT: assigned = new URShiftAVM2Item(null, null, lhs, assigned); break; case ASSIGN_XOR: assigned = new BitXorAVM2Item(null, null, lhs, assigned); break; } if (!(lhs instanceof AssignableAVM2Item)) { throw new AVM2ParseException("Invalid assignment", lexer.yyline()); } AssignableAVM2Item as = ((AssignableAVM2Item) lhs).copy(); if ((as instanceof UnresolvedAVM2Item) || (as instanceof NameAVM2Item)) { variables.add(as); } as.setAssignedValue(assigned); if (lhs instanceof NameAVM2Item) { ((NameAVM2Item) lhs).setDefinition(false); } lhs = as; break; case DESCENDANTS: expected(lookahead, lexer.yyline(), SymbolGroup.IDENTIFIER, SymbolType.MULTIPLY); lookahead = lex(); lhs = new GetDescendantsAVM2Item(lhs, lookahead.type == SymbolType.MULTIPLY ? null : lookahead.value.toString(), openedNamespaces); allowRemainder = true; break; } } if (lhs instanceof ParenthesisItem) { GraphTargetItem coerced = expression(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, allowRemainder, variables); if (coerced != null && isType(((ParenthesisItem) lhs).value)) { lhs = new CoerceAVM2Item(null, null, ((ParenthesisItem) lhs).value, coerced); } } if (debugMode) { System.out.println("/expression1"); } return lhs; } private GraphTargetItem expressionPrimary(List<List<NamespaceItem>> allOpenedNamespaces, TypeItem thisType, NamespaceItem pkg, Reference<Boolean> needsActivation, List<DottedChain> importedClasses, List<NamespaceItem> openedNamespaces, boolean allowEmpty, HashMap<String, Integer> registerVars, boolean inFunction, boolean inMethod, boolean allowRemainder, List<AssignableAVM2Item> variables) throws IOException, AVM2ParseException { if (debugMode) { System.out.println("primary:"); } GraphTargetItem ret = null; ParsedSymbol s = lex(); boolean allowMemberOrCall = false; switch (s.type) { case PREPROCESSOR: expectedType(SymbolType.PARENT_OPEN); switch ("" + s.value) { //AS3 case "hasnext": GraphTargetItem hnIndex = expression(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); expectedType(SymbolType.COMMA); GraphTargetItem hnObject = expression(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); ret = new HasNextAVM2Item(null, null, hnIndex, hnObject); break; case "newactivation": ret = new NewActivationAVM2Item(null, null); break; case "nextname": GraphTargetItem nnIndex = expression(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); expectedType(SymbolType.COMMA); GraphTargetItem nnObject = expression(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); ret = new NextNameAVM2Item(null, null, nnIndex, nnObject); allowMemberOrCall = true; break; case "nextvalue": GraphTargetItem nvIndex = expression(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); expectedType(SymbolType.COMMA); GraphTargetItem nvObject = expression(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); ret = new NextNameAVM2Item(null, null, nvIndex, nvObject); allowMemberOrCall = true; break; //Both ASs case "dup": ret = new DuplicateItem(null, null, expression(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables)); break; case "push": ret = new PushItem(expression(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables)); break; case "pop": ret = new PopItem(null, null); break; case "goto": //TODO case "multiname": throw new AVM2ParseException("Compiling §§" + s.value + " is not available, sorry", lexer.yyline()); default: throw new AVM2ParseException("Unknown preprocessor instruction: §§" + s.value, lexer.yyline()); } expectedType(SymbolType.PARENT_CLOSE); break; case REGEXP: String p = (String) s.value; p = p.substring(1); int spos = p.lastIndexOf('/'); String mod = p.substring(spos + 1); p = p.substring(0, spos); p = p.replace("\\/", "/"); ret = new RegExpAvm2Item(p, mod, null, null); allowMemberOrCall = true; break; case XML_STARTTAG_BEGIN: lexer.pushback(s); ret = xml(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, variables); break; case STRING: ret = new StringAVM2Item(null, null, s.value.toString()); allowMemberOrCall = true; break; case NEGATE: ret = expressionPrimary(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, false, registerVars, inFunction, inMethod, false, variables); ret = new NegAVM2Item(null, null, ret); break; case MINUS: s = lex(); if (s.isType(SymbolType.DOUBLE)) { ret = new FloatValueAVM2Item(null, null, -(Double) s.value); } else if (s.isType(SymbolType.INTEGER)) { ret = new IntegerValueAVM2Item(null, null, -(Long) s.value); } else { lexer.pushback(s); GraphTargetItem num = expressionPrimary(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, false, registerVars, inFunction, inMethod, true, variables); if (num instanceof IntegerValueAVM2Item) { ((IntegerValueAVM2Item) num).value = -((IntegerValueAVM2Item) num).value; ret = num; } else if (num instanceof FloatValueAVM2Item) { Double d = ((FloatValueAVM2Item) num).value; if (d.isInfinite()) { ((FloatValueAVM2Item) num).value = Double.NEGATIVE_INFINITY; } else { ((FloatValueAVM2Item) num).value = -d; } ret = (num); } else { ret = (new SubtractAVM2Item(null, null, new IntegerValueAVM2Item(null, null, 0L), num)); } } break; case TYPEOF: ret = new TypeOfAVM2Item(null, null, expressionPrimary(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, false, registerVars, inFunction, inMethod, false, variables)); break; case TRUE: ret = new BooleanAVM2Item(null, null, true); break; case NULL: ret = new NullAVM2Item(null, null); break; case UNDEFINED: ret = new UndefinedAVM2Item(null, null); break; case FALSE: ret = new BooleanAVM2Item(null, null, false); break; case CURLY_OPEN: //Object literal s = lex(); List<NameValuePair> nvs = new ArrayList<>(); while (s.type != SymbolType.CURLY_CLOSE) { if (s.type != SymbolType.COMMA) { lexer.pushback(s); } s = lex(); expected(s, lexer.yyline(), SymbolGroup.IDENTIFIER, SymbolType.STRING, SymbolType.INTEGER, SymbolType.DOUBLE); GraphTargetItem n = new StringAVM2Item(null, null, s.value.toString()); //expression(allOpenedNamespaces, thisType,pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, allowRemainder, variables); expectedType(SymbolType.COLON); GraphTargetItem v = expression(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, allowRemainder, variables); NameValuePair nv = new NameValuePair(n, v); nvs.add(nv); s = lex(); if (!s.isType(SymbolType.COMMA, SymbolType.CURLY_CLOSE)) { expected(s, lexer.yyline(), SymbolType.COMMA, SymbolType.CURLY_CLOSE); } } ret = new NewObjectAVM2Item(null, null, nvs); allowMemberOrCall = true; break; case BRACKET_OPEN: //Array literal or just brackets lexer.pushback(s); List<GraphTargetItem> inBrackets = new ArrayList<>(); int arrCnt = brackets(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, inBrackets, registerVars, inFunction, inMethod, variables); ret = new NewArrayAVM2Item(null, null, inBrackets); allowMemberOrCall = true; break; case FUNCTION: s = lexer.lex(); String fname = ""; if (s.isType(SymbolGroup.IDENTIFIER)) { fname = s.value.toString(); } else { lexer.pushback(s); } needsActivation.setVal(true); ret = function(allOpenedNamespaces, new ArrayList<>(), pkg, false, needsActivation, importedClasses, thisType, openedNamespaces, fname, false, variables); allowMemberOrCall = true; break; case NAN: ret = new NanAVM2Item(null, null); break; case INFINITY: ret = new FloatValueAVM2Item(null, null, Double.POSITIVE_INFINITY); break; case INTEGER: ret = new IntegerValueAVM2Item(null, null, (Long) s.value); break; case DOUBLE: ret = new FloatValueAVM2Item(null, null, (Double) s.value); break; case DELETE: GraphTargetItem varDel = expressionPrimary(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, false, registerVars, inFunction, inMethod, true, variables);//name(allOpenedNamespaces, thisType,false, openedNamespaces, registerVars, inFunction, inMethod, variables); if (!isNameOrProp(varDel)) { throw new AVM2ParseException("Not a property or name", lexer.yyline()); } ret = new DeletePropertyAVM2Item(varDel, lexer.yyline()); break; case INCREMENT: case DECREMENT: //preincrement GraphTargetItem varincdec = expressionPrimary(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, false, registerVars, inFunction, inMethod, false/*?*/, variables);//name(allOpenedNamespaces, thisType,false, openedNamespaces, registerVars, inFunction, inMethod, variables); if (!isNameOrProp(varincdec)) { throw new AVM2ParseException("Not a property or name", lexer.yyline()); } if (s.type == SymbolType.INCREMENT) { ret = new PreIncrementAVM2Item(null, null, varincdec); } if (s.type == SymbolType.DECREMENT) { ret = new PreDecrementAVM2Item(null, null, varincdec); } break; case NOT: ret = new NotItem(null, null, expressionPrimary(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, false, registerVars, inFunction, inMethod, false, variables)); break; case PARENT_OPEN: ret = new ParenthesisItem(null, null, expression(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables)); expectedType(SymbolType.PARENT_CLOSE); if (ret.value == null) { throw new AVM2ParseException("Expression in parenthesis expected", lexer.yyline()); } allowMemberOrCall = true; break; case NEW: s = lex(); if (s.type == SymbolType.XML_STARTTAG_BEGIN) { lexer.yypushbackstr(s.value.toString().substring(1), ActionScriptLexer.YYINITIAL); s = new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.LOWER_THAN); } if (s.type == SymbolType.FUNCTION) { s = lexer.lex(); String ffname = ""; if (s.isType(SymbolGroup.IDENTIFIER)) { ffname = s.value.toString(); } else { lexer.pushback(s); } needsActivation.setVal(true); ret = function(allOpenedNamespaces, new ArrayList<>(), pkg, false, needsActivation, importedClasses, thisType, openedNamespaces, ffname, false, variables); } else if (s.type == SymbolType.LOWER_THAN) { GraphTargetItem subtype = type(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, variables); expectedType(SymbolType.GREATER_THAN); s = lex(); expected(s, lexer.yyline(), SymbolType.BRACKET_OPEN); lexer.pushback(s); List<GraphTargetItem> params = new ArrayList<>(); brackets(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, params, registerVars, inFunction, inMethod, variables); ret = new InitVectorAVM2Item(subtype, params, openedNamespaces); } else if (s.type == SymbolType.PARENT_OPEN) { GraphTargetItem newvar = expression(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); newvar = applyType(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, newvar, registerVars, inFunction, inMethod, variables); expectedType(SymbolType.PARENT_CLOSE); expectedType(SymbolType.PARENT_OPEN); ret = new ConstructSomethingAVM2Item(lexer.yyline(), openedNamespaces, newvar, call(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, variables)); } else { lexer.pushback(s); GraphTargetItem newvar = name(allOpenedNamespaces, thisType, pkg, needsActivation, false /*?*/, openedNamespaces, registerVars, inFunction, inMethod, variables, importedClasses); newvar = applyType(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, newvar, registerVars, inFunction, inMethod, variables); expectedType(SymbolType.PARENT_OPEN); ret = new ConstructSomethingAVM2Item(lexer.yyline(), openedNamespaces, newvar, call(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, variables)); } allowMemberOrCall = true; break; case IDENTIFIER: case THIS: case SUPER: case ATTRIBUTE: lexer.pushback(s); ret = name(allOpenedNamespaces, thisType, pkg, needsActivation, false, openedNamespaces, registerVars, inFunction, inMethod, variables, importedClasses); allowMemberOrCall = true; //var = memberOrCall(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, var, registerVars, inFunction, inMethod, variables); //ret = var; break; default: GraphTargetItem excmd = expressionCommands(s, registerVars, inFunction, inMethod, -1, variables); if (excmd != null) { //? ret = excmd; allowMemberOrCall = true; //? break; } lexer.pushback(s); } if (allowMemberOrCall && ret != null) { ret = memberOrCall(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, ret, registerVars, inFunction, inMethod, variables); } if (debugMode) { System.out.println("/primary"); } return ret; } private ActionScriptLexer lexer = null; private List<String> constantPool; private List<DottedChain> parseImportsUsages(List<NamespaceItem> openedNamespaces) throws IOException, AVM2ParseException { ParsedSymbol s; List<DottedChain> importedClasses = new ArrayList<>(); s = lex(); while (s.isType(SymbolType.IMPORT, SymbolType.USE)) { boolean all = false; boolean isUse = s.type == SymbolType.USE; if (isUse) { expectedType(SymbolType.NAMESPACE); } s = lex(); expected(s, lexer.yyline(), SymbolGroup.IDENTIFIER); DottedChain fullName = new DottedChain(new String[]{}, ""); fullName = fullName.addWithSuffix(s.value.toString()); s = lex(); boolean isStar = false; while (s.type == SymbolType.DOT) { s = lex(); if (s.type == SymbolType.MULTIPLY && !isUse) { isStar = true; s = lex(); break; } expected(s, lexer.yyline(), SymbolGroup.IDENTIFIER); fullName = fullName.addWithSuffix(s.value.toString()); s = lex(); } if (isStar) { openedNamespaces.add(new NamespaceItem(fullName, Namespace.KIND_PACKAGE)); } else if (isUse) { //Note: in this case, fullName attribute will be changed to real NS insude NamespaceItem openedNamespaces.add(new NamespaceItem(fullName, Namespace.KIND_NAMESPACE)); } else { importedClasses.add(fullName); } expected(s, lexer.yyline(), SymbolType.SEMICOLON); s = lex(); } lexer.pushback(s); return importedClasses; } private List<GraphTargetItem> parseScript(List<List<NamespaceItem>> allOpenedNamespaces, int scriptIndex, String fileName) throws IOException, AVM2ParseException, CompilationException { //int scriptPrivateNs; if (fileName.contains("/")) { fileName = fileName.substring(fileName.lastIndexOf('/') + 1); } if (fileName.contains("\\")) { fileName = fileName.substring(fileName.lastIndexOf('\\') + 1); } List<GraphTargetItem> items = new ArrayList<>(); scriptTraits(allOpenedNamespaces, scriptIndex, fileName, items); return items; } public List<GraphTargetItem> scriptTraitsFromString(List<List<NamespaceItem>> allOpenedNamespaces, String str, String fileName, int scriptIndex) throws AVM2ParseException, IOException, CompilationException { lexer = new ActionScriptLexer(str); List<GraphTargetItem> ret = parseScript(allOpenedNamespaces, scriptIndex, fileName); if (lexer.lex().type != SymbolType.EOF) { throw new AVM2ParseException("Parsing finisned before end of the file", lexer.yyline()); } return ret; } public void addScriptFromTree(List<List<NamespaceItem>> allOpenedNamespaces, List<GraphTargetItem> items, boolean documentClass, int classPos) throws AVM2ParseException, CompilationException { AVM2SourceGenerator gen = new AVM2SourceGenerator(abcIndex); SourceGeneratorLocalData localData = new SourceGeneratorLocalData( new HashMap<>(), 0, Boolean.FALSE, 0); localData.documentClass = documentClass; abcIndex.getSelectedAbc().script_info.add(gen.generateScriptInfo(allOpenedNamespaces, localData, items, classPos)); } public void addScript(String s, boolean documentClass, String fileName, int classPos, int scriptIndex) throws AVM2ParseException, IOException, CompilationException { List<List<NamespaceItem>> allOpenedNamespaces = new ArrayList<>(); List<GraphTargetItem> traits = scriptTraitsFromString(allOpenedNamespaces, s, fileName, scriptIndex); addScriptFromTree(allOpenedNamespaces, traits, documentClass, classPos); } public ActionScript3Parser(ABC abc, List<ABC> otherAbcs) throws IOException, InterruptedException { initPlayer(); abcIndex = new AbcIndexing(playerGlobalAbcIndex); for (ABC a : otherAbcs) { abcIndex.addAbc(a); } abcIndex.addAbc(abc); } private static void initPlayer() throws IOException, InterruptedException { if (playerGlobalAbcIndex == null) { if (Configuration.getPlayerSWC() == null) { throw new IOException("Player SWC library not found, please place it to " + Configuration.getFlashLibPath()); } SWC swc = new SWC(new FileInputStream(Configuration.getPlayerSWC())); SWF swf = new SWF(swc.getSWF("library.swf"), true); playerGlobalAbcIndex = new AbcIndexing(swf); } } public static void compile(String src, ABC abc, List<ABC> otherABCs, boolean documentClass, String fileName, int classPos, int scriptIndex) throws AVM2ParseException, IOException, InterruptedException, CompilationException { //List<ABC> parABCs = new ArrayList<>(); initPlayer(); ActionScript3Parser parser = new ActionScript3Parser(abc, otherABCs); boolean success = false; ABC originalAbc = ((ABCContainerTag) ((Tag) abc.parentTag).cloneTag()).getABC(); try { parser.addScript(src, documentClass, fileName, classPos, scriptIndex); success = true; } finally { if (!success) { // restore original constant pool and other lists abc.constants = originalAbc.constants; abc.method_info = originalAbc.method_info; abc.metadata_info = originalAbc.metadata_info; abc.instance_info = originalAbc.instance_info; abc.class_info = originalAbc.class_info; abc.script_info = originalAbc.script_info; abc.bodies = originalAbc.bodies; abc.getMethodIndexing(); } } } public static void compile(SWF swf, String src, String dst, int classPos, int scriptIndex) { System.err.println("WARNING: AS3 compiler is not finished yet. This is only used for debuggging!"); try { initPlayer(); ABC abc = new ABC(null); ActionScript3Parser parser = new ActionScript3Parser(abc, new ArrayList<>()); parser.addScript(new String(Helper.readFile(src), Utf8Helper.charset), true, src, classPos, scriptIndex); try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(new File(dst)))) { abc.saveToStream(fos); } } catch (Exception ex) { Logger.getLogger(ActionScript3Parser.class .getName()).log(Level.SEVERE, null, ex); } System.exit(0); } }