diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ActionScriptParser.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ActionScriptParser.java index 700a8854b..2ca857a79 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ActionScriptParser.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ActionScriptParser.java @@ -1,2442 +1,2438 @@ -/* - * Copyright (C) 2010-2015 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.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.NewArrayAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.NewObjectAVM2Item; -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.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.MethodBody; -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.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.BinaryOp; -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.DoWhileItem; -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.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 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.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 ActionScriptParser { - - private long uniqLast = 0; - - private final boolean debugMode = false; - - private static final String AS3_NAMESPACE = "http://adobe.com/AS3/2006/builtin"; - - private final ABC abc; - - private final List otherABCs; - - private static final List playerABCs = new ArrayList<>(); - - private long uniqId() { - uniqLast++; - return uniqLast; - } - - private List commands(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, Stack loops, Map loopLabels, HashMap registerVars, boolean inFunction, boolean inMethod, int forinlevel, List variables) throws IOException, AVM2ParseException { - List ret = new ArrayList<>(); - if (debugMode) { - System.out.println("commands:"); - } - GraphTargetItem cmd = null; - while ((cmd = command(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(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, List variables) throws IOException, AVM2ParseException { - ParsedSymbol s = lex(); - if (s.type == SymbolType.MULTIPLY) { - return new UnboundedTypeItem(); - } else if (s.type == SymbolType.VOID) { - return new TypeItem("void"); - } else { - lexer.pushback(s); - } - - GraphTargetItem t = name(thisType, pkg, needsActivation, true, openedNamespaces, null, false, false, variables, importedClasses); - t = applyType(thisType, pkg, needsActivation, importedClasses, openedNamespaces, t, new HashMap(), false, false, variables); - return t; - } - - private GraphTargetItem memberOrCall(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, GraphTargetItem newcmds, HashMap registerVars, boolean inFunction, boolean inMethod, List 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)) { - switch (s.type) { - case BRACKET_OPEN: - case DOT: - case TYPENAME: - lexer.pushback(s); - ret = member(thisType, pkg, needsActivation, importedClasses, openedNamespaces, ret, registerVars, inFunction, inMethod, variables); - break; - case FILTER: - needsActivation.setVal(true); - ret = new XMLFilterAVM2Item(ret, expression(thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, inMethod, variables), openedNamespaces); - expectedType(SymbolType.PARENT_CLOSE); - break; - case PARENT_OPEN: - ret = new CallAVM2Item(lexer.yyline(), ret, call(thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, variables)); - break; - - } - s = lex(); - } - if (s.type == SymbolType.INCREMENT) { - if (!isNameOrProp(ret)) { - throw new AVM2ParseException("Invalid assignment", lexer.yyline()); - } - ret = new PostIncrementAVM2Item(null, ret); - s = lex(); - - } else if (s.type == SymbolType.DECREMENT) { - if (!isNameOrProp(ret)) { - throw new AVM2ParseException("Invalid assignment", lexer.yyline()); - } - ret = new PostDecrementAVM2Item(null, ret); - s = lex(); - } - - lexer.pushback(s); - - if (debugMode) { - System.out.println("/memberOrCall"); - } - return ret; - } - - private GraphTargetItem applyType(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, GraphTargetItem obj, HashMap registerVars, boolean inFunction, boolean inMethod, List variables) throws IOException, AVM2ParseException { - GraphTargetItem ret = obj; - ParsedSymbol s = lex(); - if (s.type == SymbolType.TYPENAME) { - List params = new ArrayList<>(); - do { - params.add(expressionPrimary(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, ret, params); - } else { - lexer.pushback(s); - } - return ret; - } - - private GraphTargetItem member(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, GraphTargetItem obj, HashMap registerVars, boolean inFunction, boolean inMethod, List 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(thisType, pkg, needsActivation, importedClasses, openedNamespaces, ret, registerVars, inFunction, inMethod, variables); - s = lex(); - } else if (s.type == SymbolType.BRACKET_OPEN) { - GraphTargetItem index = expression(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); - String propName = s.value.toString(); - GraphTargetItem propItem = null; - s = lex(); - GraphTargetItem ns = null; - if (s.type == SymbolType.NAMESPACE_OP) { - ns = new UnresolvedAVM2Item(new ArrayList(), importedClasses, false, null, lexer.yyline(), propName, null, openedNamespaces); - variables.add((UnresolvedAVM2Item) ns); - s = lex(); - if (s.type == SymbolType.BRACKET_OPEN) { - propItem = expression(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, abc, otherABCs, openedNamespaces, new ArrayList()); - } - s = lex(); - } - } - if (s.type.getPrecedence() == GraphTargetItem.PRECEDENCE_ASSIGMENT) { - ret = expression1(ret, s.type.getPrecedence(), thisType, pkg, needsActivation, importedClasses, openedNamespaces, inMethod, registerVars, inFunction, inMethod, false, variables); - } else { - lexer.pushback(s); - } - - if (debugMode) { - System.out.println("/member"); - } - return ret; - } - - private GraphTargetItem name(TypeItem thisType, String pkg, Reference needsActivation, boolean typeOnly, List openedNamespaces, HashMap registerVars, boolean inFunction, boolean inMethod, List variables, List importedClasses) throws IOException, AVM2ParseException { - ParsedSymbol s = lex(); - String name = ""; - if (s.type == SymbolType.ATTRIBUTE) { - name += "@"; - s = lex(); - } - expected(s, lexer.yyline(), SymbolGroup.IDENTIFIER, SymbolType.THIS, SymbolType.SUPER, SymbolType.STRING_OP); - name += s.value.toString(); - s = lex(); - boolean attrBracket = false; - - while (s.isType(SymbolType.DOT)) { - name += s.value.toString(); //. or :: - s = lex(); - if (s.type == SymbolType.ATTRIBUTE) { - name += "@"; - s = lex(); - if (s.type == SymbolType.MULTIPLY) { - name += s.value.toString(); - } else if (s.group == SymbolGroup.IDENTIFIER) { - name += 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); - name += s.value.toString(); - } - s = lex(); - } - String nsname = null; - String nsprop = null; - GraphTargetItem nspropItem = null; - if (s.type == SymbolType.NAMESPACE_OP) { - if (name.contains(".")) { - nsname = name.substring(name.lastIndexOf('.') + 1); - } else { - nsname = name; - } - s = lex(); - if (s.group == SymbolGroup.IDENTIFIER) { - nsprop = s.value.toString(); - } else if (s.type == SymbolType.BRACKET_OPEN) { - nspropItem = expression(thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); - expectedType(SymbolType.BRACKET_CLOSE); - } - if (name.contains(".")) { - name = name.substring(0, name.lastIndexOf('.')); - } else { - name = null; - } - s = lex(); - } - - GraphTargetItem ret = null; - if (name != null) { - 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(), 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(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 call(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, HashMap registerVars, boolean inFunction, boolean inMethod, List variables) throws IOException, AVM2ParseException { - List 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(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(String pkg, boolean isInterface, String customAccess, Reference needsActivation, List importedClasses, boolean override, boolean isFinal, TypeItem thisType, List openedNamespaces, boolean isStatic, int namespace, String functionName, boolean isMethod, List variables) throws IOException, AVM2ParseException { - FunctionAVM2Item f = function(pkg, isInterface, needsActivation, importedClasses, namespace, thisType, openedNamespaces, functionName, isMethod, variables); - return new MethodAVM2Item(f.pkg, f.isInterface, customAccess, f.needsActivation, f.hasRest, f.line, override, isFinal, isStatic, f.namespace, functionName, f.paramTypes, f.paramNames, f.paramValues, f.body, f.subvariables, f.retType); - } - - private FunctionAVM2Item function(String pkg, boolean isInterface, Reference needsActivation, List importedClasses, int namespace, TypeItem thisType, List openedNamespaces, String functionName, boolean isMethod, List variables) throws IOException, AVM2ParseException { - openedNamespaces = new ArrayList<>(openedNamespaces); //local copy - int line = lexer.yyline(); - ParsedSymbol s; - expectedType(SymbolType.PARENT_OPEN); - s = lex(); - List paramNames = new ArrayList<>(); - List paramTypes = new ArrayList<>(); - List 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(thisType, pkg, needsActivation, importedClasses, openedNamespaces, variables)); - s = lex(); - } else { - paramTypes.add(new UnboundedTypeItem()); - } - if (s.type == SymbolType.ASSIGN) { - paramValues.add(expression(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(thisType, pkg, needsActivation, importedClasses, openedNamespaces, variables); - } else { - retType = new UnboundedTypeItem(); - lexer.pushback(s); - } - List body = null; - List 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 needsActivation2 = new Reference<>(false); - if (!isInterface) { - expectedType(SymbolType.CURLY_OPEN); - body = commands(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(pkg, isInterface, needsActivation2.getVal(), namespace, hasRest, line, functionName, paramTypes, paramNames, paramValues, body, subvariables, retType); - } - - private GraphTargetItem traits(String scriptName, boolean scriptTraits, List sinitVariables, Reference sinitNeedsActivation, List staticInitializer, List importedClasses, int privateNs, int protectedNs, int publicNs, int packageInternalNs, int protectedStaticNs, List openedNamespaces, String pkg, String classNameStr, boolean isInterface, List traits) throws AVM2ParseException, IOException, CompilationException { - ParsedSymbol s; - GraphTargetItem constr = null; - TypeItem thisType = pkg == null && classNameStr == null ? null : new TypeItem(pkg == null || "".equals(pkg) ? classNameStr : pkg + "." + classNameStr); - List constrVariables = new ArrayList<>(); - List originalOpenedNamespaces = openedNamespaces; - int originalPrivateNs = privateNs; - boolean inPkg = pkg != null; - looptrait: - while (true) { - s = lex(); - boolean isStatic = false; - int namespace = -1; - boolean isGetter = false; - boolean isSetter = false; - boolean isOverride = false; - boolean isFinal = false; - boolean isDynamic = false; - String customAccess = null; - - if (scriptTraits && s.type == SymbolType.PACKAGE) { - if (inPkg) { - throw new AVM2ParseException("No subpackages allowed", lexer.yyline()); - } - openedNamespaces = new ArrayList<>(); - lexer.pushback(s); - PackageAVM2Item p = parsePackage(openedNamespaces); - pkg = p.packageName; - inPkg = true; - publicNs = p.publicNs; - importedClasses = p.importedClasses; - s = lex(); - } - if (inPkg || classNameStr != null) { - if (s.type == SymbolType.CURLY_OPEN) { - staticInitializer.addAll(commands(thisType, pkg, sinitNeedsActivation, importedClasses, openedNamespaces, new Stack(), new HashMap(), new HashMap(), true, false, 0, sinitVariables)); - expectedType(SymbolType.CURLY_CLOSE); - 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.DYNAMIC) { - if (isDynamic) { - throw new AVM2ParseException("Only one dynamic keyword allowed", lexer.yyline()); - } - isDynamic = 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) { - customAccess = s.value.toString(); - namespace = -2; - } else { - if (namespace != -1) { - 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: - 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(); - } - } else { - namespace = privateNs; - } - if (namespace == -1) { - if (isInterface) { - namespace = abc.constants.getNamespaceId(new Namespace(Namespace.KIND_NAMESPACE, abc.constants.getStringId(pkg == null || pkg.isEmpty() ? classNameStr : pkg + ":" + classNameStr, true)), 0, true); - } else { - namespace = packageInternalNs; - } - } - if (namespace == protectedNs && isStatic) { - namespace = protectedStaticNs; - } - switch (s.type) { - /*case PACKAGE: - lexer.pushback(s); - traits.add(parsePackage(openedNamespaces)); - break;*/ - case CLASS: - List subNamespaces = new ArrayList<>(openedNamespaces); - if (classNameStr != null) { - throw new AVM2ParseException("Nested classes not supported", lexer.yyline()); - } - if (isOverride) { - throw new AVM2ParseException("Override flag not allowed for classes", lexer.yyline()); - } - - //GraphTargetItem classTypeStr = type(thisType,pkg,needsActivation, importedClasses, openedNamespaces, variables); - s = lex(); - expected(s, lexer.yyline(), SymbolGroup.IDENTIFIER); - String classTypeStr = s.value.toString(); - GraphTargetItem extendsTypeStr = null; - s = lex(); - if (s.type == SymbolType.EXTENDS) { - extendsTypeStr = type(thisType, pkg, new Reference<>(false), importedClasses, subNamespaces, new ArrayList()); - s = lex(); - } - List implementsTypeStrs = new ArrayList<>(); - if (s.type == SymbolType.IMPLEMENTS) { - do { - GraphTargetItem implementsTypeStr = type(thisType, pkg, new Reference<>(false), importedClasses, subNamespaces, new ArrayList()); - implementsTypeStrs.add(implementsTypeStr); - s = lex(); - } while (s.type == SymbolType.COMMA); - } - expected(s, lexer.yyline(), SymbolType.CURLY_OPEN); - if (customAccess != null) { - throw new AVM2ParseException("Class cannot have custom namespace", lexer.yyline()); - } - traits.add((classTraits(scriptName, publicNs, pkg, importedClasses, isDynamic, isFinal, subNamespaces, pkg, namespace, false, classTypeStr, extendsTypeStr, implementsTypeStrs, new ArrayList()))); - expectedType(SymbolType.CURLY_CLOSE); - break; - case INTERFACE: - if (classNameStr != null) { - throw new AVM2ParseException("Nested interfaces not supported", lexer.yyline()); - } - if (isOverride) { - throw new AVM2ParseException("Override flag not allowed for interfaces", lexer.yyline()); - } - if (isFinal) { - throw new AVM2ParseException("Final flag not allowed for interfaces", lexer.yyline()); - } - if (isDynamic) { - throw new AVM2ParseException("Dynamic flag not allowed for interfaces", lexer.yyline()); - } - //GraphTargetItem interfaceTypeStr = type(thisType,pkg,needsActivation, importedClasses, openedNamespaces, variables); - s = lex(); - expected(s, lexer.yyline(), SymbolGroup.IDENTIFIER); - String intTypeStr = s.value.toString(); - s = lex(); - List intExtendsTypeStrs = new ArrayList<>(); - - if (s.type == SymbolType.EXTENDS) { - do { - GraphTargetItem intExtendsTypeStr = type(thisType, pkg, new Reference<>(false), importedClasses, openedNamespaces, new ArrayList()); - intExtendsTypeStrs.add(intExtendsTypeStr); - s = lex(); - } while (s.type == SymbolType.COMMA); - } - expected(s, lexer.yyline(), SymbolType.CURLY_OPEN); - if (customAccess != null) { - throw new AVM2ParseException("Interface cannot have custom namespace", lexer.yyline()); - } - traits.add((classTraits(scriptName, publicNs, pkg, importedClasses, false, isFinal, openedNamespaces, pkg, namespace, true, intTypeStr, null, intExtendsTypeStrs, new ArrayList()))); - expectedType(SymbolType.CURLY_CLOSE); - break; - - case FUNCTION: - - if (isDynamic) { - throw new AVM2ParseException("Dynamic flag not allowed for methods", lexer.yyline()); - } - s = lex(); - if (s.type == SymbolType.GET) { - if (classNameStr == null) { - throw new AVM2ParseException("No get keyword allowed here", lexer.yyline()); - } - isGetter = true; - s = lex(); - } else if (s.type == SymbolType.SET) { - if (classNameStr == null) { - throw new AVM2ParseException("No set keyword allowed here", lexer.yyline()); - } - isSetter = true; - s = lex(); - } - - expected(s, lexer.yyline(), SymbolGroup.IDENTIFIER); - String fname = s.value.toString(); - if (classNameStr != null && fname.equals(classNameStr)) { //constructor - if (isStatic) { - throw new AVM2ParseException("Constructor cannot be static", lexer.yyline()); - } - 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()); - } - constr = (method(pkg, false, customAccess, new Reference<>(false), importedClasses, false, false, thisType, openedNamespaces, false, namespace, "", true, constrVariables)); - } else { - MethodAVM2Item ft = method(pkg, isInterface, customAccess, new Reference<>(false), importedClasses, isOverride, isFinal, thisType, openedNamespaces, isStatic, namespace, 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()); - } - } - GraphTargetItem t; - if (isGetter) { - GetterAVM2Item g = new GetterAVM2Item(ft.pkg, isInterface, customAccess, ft.needsActivation, ft.hasRest, ft.line, ft.isOverride(), ft.isFinal(), isStatic, ft.namespace, ft.functionName, ft.paramTypes, ft.paramNames, ft.paramValues, ft.body, ft.subvariables, ft.retType); - t = g; - } else if (isSetter) { - SetterAVM2Item st = new SetterAVM2Item(ft.pkg, isInterface, customAccess, ft.needsActivation, ft.hasRest, ft.line, ft.isOverride(), ft.isFinal(), isStatic, ft.namespace, ft.functionName, ft.paramTypes, ft.paramNames, ft.paramValues, ft.body, ft.subvariables, ft.retType); - t = st; - } else { - t = ft; - } - - 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 == null || pkg.isEmpty() ? classNameStr : pkg + ":" + classNameStr) + "/" + nname; - } - if (s.type != SymbolType.SEMICOLON) { - lexer.pushback(s); - } - - ConstAVM2Item ns = new ConstAVM2Item(pkg, customAccess, true, namespace, nname, new TypeItem("Namespace"), new StringAVM2Item(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 (isDynamic) { - throw new AVM2ParseException("Dynamic 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 = null; - if (s.type == SymbolType.COLON) { - type = type(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(thisType, pkg, new Reference<>(false), importedClasses, openedNamespaces, new HashMap(), false, false, true, isStatic || isConst ? sinitVariables : constrVariables); - s = lex(); - } - GraphTargetItem tar; - if (isConst) { - tar = new ConstAVM2Item(pkg, customAccess, isStatic, namespace, vcname, type, value, lexer.yyline()); - } else { - tar = new SlotAVM2Item(pkg, customAccess, isStatic, namespace, vcname, type, value, lexer.yyline()); - } - traits.add(tar); - if (s.type != SymbolType.SEMICOLON) { - lexer.pushback(s); - } - break; - default: - if (s.type == SymbolType.CURLY_CLOSE && inPkg && classNameStr == null) { - inPkg = false; - pkg = null; - openedNamespaces = originalOpenedNamespaces; - privateNs = originalPrivateNs; - } else { - lexer.pushback(s); - break looptrait; - } - - } - } - return constr; - } - - private GraphTargetItem classTraits(String scriptName, int gpublicNs, String pkg, List importedClasses, boolean isDynamic, boolean isFinal, List openedNamespaces, String packageName, int namespace, boolean isInterface, String nameStr, GraphTargetItem extendsStr, List implementsStr, List variables) throws IOException, AVM2ParseException, CompilationException { - - GraphTargetItem ret = null; - - ParsedSymbol s = null; - List traits = new ArrayList<>(); - - String classNameStr = nameStr; - - openedNamespaces = new ArrayList<>(openedNamespaces); - - int publicNs = 0; - int privateNs = 0; - int packageInternalNs = 0; - if (pkg != null) { - openedNamespaces.add(packageInternalNs = abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE_INTERNAL, abc.constants.getStringId(pkg, true)), 0, true)); - } - if (pkg != null && !pkg.isEmpty()) { - openedNamespaces.add(publicNs = abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.getStringId("", true)), 0, true)); - } else { - publicNs = gpublicNs; - } - - openedNamespaces.add(privateNs = abc.constants.addNamespace(new Namespace(Namespace.KIND_PRIVATE, 0))); //abc.constants.getStringId(fileName + "$", true) - - openedNamespaces.add(abc.constants.getNamespaceId(new Namespace(Namespace.KIND_NAMESPACE, abc.constants.getStringId(AS3_NAMESPACE, true)), 0, true)); - - //int privateNs = 0; - int protectedNs = 0; - //int publicNs = namespace; - int protectedStaticNs = 0; - - openedNamespaces.add(protectedNs = abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PROTECTED, abc.constants.getStringId(packageName == null ? (scriptName + "$0:"/*FIXME?*/ + classNameStr) : packageName.isEmpty() ? classNameStr : packageName + ":" + classNameStr, true)), 0, true)); - openedNamespaces.add(protectedStaticNs = abc.constants.getNamespaceId(new Namespace(Namespace.KIND_STATIC_PROTECTED, abc.constants.getStringId(packageName == null || packageName.isEmpty() ? classNameStr : packageName + ":" + classNameStr, true)), 0, true)); - - if (extendsStr != null) { - List indices = new ArrayList<>(); - List names = new ArrayList<>(); - List namespaces = new ArrayList<>(); - //FIXME for Private classes in script!!! - AVM2SourceGenerator.parentNamesAddNames(abc, otherABCs, AVM2SourceGenerator.resolveType(new SourceGeneratorLocalData(new HashMap(), 0, false, 0), ((TypeItem) ((UnresolvedAVM2Item) extendsStr).resolve(null, new ArrayList(), new ArrayList(), abc, otherABCs, new ArrayList(), new ArrayList())), abc, otherABCs), indices, names, namespaces); - for (int i = 0; i < names.size(); i++) { - if (namespaces.get(i).isEmpty()) { - continue; - } - openedNamespaces.add(abc.constants.getNamespaceId(new Namespace(Namespace.KIND_STATIC_PROTECTED, abc.constants.getStringId(namespaces.get(i) + ":" + names.get(i), true)), 0, true)); - } - } - - Reference staticNeedsActivation = new Reference<>(false); - List staticInit = new ArrayList<>(); - List sinitVariables = new ArrayList<>(); - GraphTargetItem constr = traits(scriptName, false, sinitVariables, staticNeedsActivation, staticInit, importedClasses, privateNs, protectedNs, publicNs, packageInternalNs, protectedStaticNs, openedNamespaces, packageName, classNameStr, isInterface, traits); - - if (isInterface) { - return new InterfaceAVM2Item(importedClasses, packageName, openedNamespaces, isFinal, namespace, classNameStr, implementsStr, traits); - } else { - return new ClassAVM2Item(importedClasses, packageName, openedNamespaces, protectedNs, isDynamic, isFinal, namespace, classNameStr, extendsStr, implementsStr, staticInit, staticNeedsActivation.getVal(), sinitVariables, constr, traits); - } - } - - private GraphTargetItem expressionCommands(ParsedSymbol s, HashMap registerVars, boolean inFunction, boolean inMethod, int forinlevel, List variables) throws IOException, AVM2ParseException { - GraphTargetItem ret = null; - switch (s.type) { - /*case INT: - expectedType(SymbolType.PARENT_OPEN); - ret = new ToIntegerAVM2Item(null, expression(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(thisType,vi, registerVars, inFunction, inMethod, variables); - } else { - expected(s, lexer.yyline(), SymbolType.PARENT_OPEN); - ret = new ToNumberAVM2Item(null, expression(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(thisType,vi2, registerVars, inFunction, inMethod, variables); - } else { - expected(s, lexer.yyline(), SymbolType.PARENT_OPEN); - ret = new ToStringAVM2Item(null, expression(thisType,pkg,needsActivation, importedClasses, openedNamespaces,openedNamespacesKinds,registerVars, inFunction, inMethod, true, variables)); - expectedType(SymbolType.PARENT_CLOSE); - ret = memberOrCall(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, a.toString()); - ((StringBuilder) a).setLength(0); - return ret; - } - if (a instanceof String) { - return new StringAVM2Item(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, ta, tb); - } - - private void addS(List rets, StringBuilder sb) { - if (sb.length() > 0) { - if (!rets.isEmpty() && (rets.get(rets.size() - 1) instanceof StringAVM2Item)) { - ((StringAVM2Item) rets.get(rets.size() - 1)).value += sb.toString(); - } else { - rets.add(new StringAVM2Item(null, sb.toString())); - } - sb.setLength(0); - } - } - - private List xmltag(TypeItem thisType, String pkg, Reference usesVars, List openedTags, Reference closedVarTags, Reference needsActivation, List importedClasses, List openedNamespaces, HashMap registerVars, boolean inFunction, boolean inMethod, List variables) throws IOException, AVM2ParseException { - ParsedSymbol s = null; - List rets = new ArrayList<>(); - //GraphTargetItem ret = null; - StringBuilder sb = new StringBuilder(); - loop: - do { - s = lex(); - List sub = new ArrayList<>(); - Reference subclose = new Reference<>(0); - Reference subusesvars = new Reference<>(false); - switch (s.type) { - case XML_ATTRNAMEVAR_BEGIN: //add - usesVars.setVal(true); - addS(rets, sb); - rets.add(expression(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, expression(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(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, expression(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, expression(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); - lexer.yybegin(ActionScriptLexer.XML); - break; - case XML_STARTVARTAG_BEGIN: //add - //openedTags.add("*"); - - //ret = add(ret, ); - GraphTargetItem ex = expression(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(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(" st = xmltag(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(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, HashMap registerVars, boolean inFunction, boolean inMethod, List variables) throws IOException, AVM2ParseException { - List openedTags = new ArrayList<>(); - int closedVarTags = 0; - - GraphTargetItem ret = add(xmltag(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(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, Stack loops, Map loopLabels, HashMap registerVars, boolean inFunction, boolean inMethod, int forinlevel, boolean mustBeCommand, List 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(thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); - ret = new DefaultXMLNamespace(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(thisType, pkg, needsActivation, importedClasses, openedNamespaces, variables); - openedNamespaces.add(abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE /*FIXME?*/, abc.constants.getStringId(ns.toString(), true)), 0, true)); - break; - case WITH: - needsActivation.setVal(true); - expectedType(SymbolType.PARENT_OPEN); - GraphTargetItem wvar = expression(thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables);//(name(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 withVars = new ArrayList<>(); - List wcmd = commands(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, wvar, wcmd); - ((WithAVM2Item) ret).subvariables = withVars; - break; - /*case DELETE: - GraphTargetItem varDel = expression(thisType,pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables);//name(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, gm.object, gm.propertyName); - } else if (varDel instanceof NameAVM2Item) { - variables.remove(varDel); - ret = new DeletePropertyAVM2Item(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(pkg, false, needsActivation, importedClasses, 0/*?*/, 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(thisType, pkg, needsActivation, importedClasses, openedNamespaces, variables); - s = lex(); - } else { - type = new UnboundedTypeItem(); - } - - if (s.type == SymbolType.ASSIGN) { - GraphTargetItem varval = (expression(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, commands(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(thisType,pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables);//name(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, varincdec); - } else if (s.type == SymbolType.DECREMENT) { - ret = new PreDecrementAVM2Item(null, varincdec); - } - break;*/ - case SUPER: //constructor call - ParsedSymbol ss2 = lex(); - if (ss2.type == SymbolType.PARENT_OPEN) { - List args = call(thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, variables); - ret = new ConstructSuperAVM2Item(null, new LocalRegAVM2Item(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(thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables)); - expectedType(SymbolType.PARENT_CLOSE); - GraphTargetItem onTrue = command(thisType, pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, true, variables); - List onTrueList = new ArrayList<>(); - onTrueList.add(onTrue); - s = lex(); - List onFalseList = null; - if (s.type == SymbolType.ELSE) { - onFalseList = new ArrayList<>(); - onFalseList.add(command(thisType, pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, true, variables)); - } else { - lexer.pushback(s); - } - ret = new IfItem(null, ifExpr, onTrueList, onFalseList); - break; - case WHILE: - expectedType(SymbolType.PARENT_OPEN); - List whileExpr = new ArrayList<>(); - whileExpr.add(commaExpression(thisType, pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, variables)); - expectedType(SymbolType.PARENT_CLOSE); - List whileBody = new ArrayList<>(); - Loop wloop = new Loop(uniqId(), null, null); - if (loopLabel != null) { - loopLabels.put(wloop, loopLabel); - } - loops.push(wloop); - whileBody.add(command(thisType, pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, true, variables)); - ret = new WhileItem(null, wloop, whileExpr, whileBody); - break; - case DO: - List doBody = new ArrayList<>(); - Loop dloop = new Loop(uniqId(), null, null); - loops.push(dloop); - if (loopLabel != null) { - loopLabels.put(dloop, loopLabel); - } - doBody.add(command(thisType, pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, true, variables)); - expectedType(SymbolType.WHILE); - expectedType(SymbolType.PARENT_OPEN); - List doExpr = new ArrayList<>(); - doExpr.add(commaExpression(thisType, pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, variables)); - expectedType(SymbolType.PARENT_CLOSE); - ret = new DoWhileItem(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(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) { //??? WUT - firstCommand = expression1(firstCommand, firstCommand.getPrecedence(), 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 forFinalCommands = new ArrayList<>(); - GraphTargetItem forExpr = null; - List forFirstCommands = new ArrayList<>(); - if (!forin) { - //GraphTargetItem firstCommand = command(thisType,pkg,needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, true, variables); - if (firstCommand != null) { //can be empty command - forFirstCommands.add(firstCommand); - } - forExpr = (expression(thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables)); - expectedType(SymbolType.SEMICOLON); - forFinalCommands.add(command(thisType, pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, true, variables)); - } - expectedType(SymbolType.PARENT_CLOSE); - List forBody = new ArrayList<>(); - forBody.add(command(thisType, pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forin ? forinlevel + 1 : forinlevel, true, variables)); - if (forin) { - if (each) { - ret = new ForEachInAVM2Item(null, floop, inexpr, forBody); - } else { - - ret = new ForInAVM2Item(null, floop, inexpr, forBody); - } - } else { - ret = new ForItem(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(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> caseIfs = new ArrayList<>(); - List> caseCmds = new ArrayList<>(); - List caseExprsAll = new ArrayList<>(); - List valueMapping = new ArrayList<>(); - int pos = 0; - while (s.type == SymbolType.CASE) { - List caseExprs = new ArrayList<>(); - while (s.type == SymbolType.CASE) { - GraphTargetItem curCaseExpr = expression(thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); - caseExprs.add(curCaseExpr); - expectedType(SymbolType.COLON); - s = lex(); - caseExprsAll.add(curCaseExpr); - valueMapping.add(pos); - } - pos++; - lexer.pushback(s); - List caseCmd = commands(thisType, pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, variables); - caseCmds.add(caseCmd); - s = lex(); - } - List defCmd = new ArrayList<>(); - if (s.type == SymbolType.DEFAULT) { - expectedType(SymbolType.COLON); - defCmd = commands(thisType, pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, variables); - s = lexer.lex(); - } - expected(s, lexer.yyline(), SymbolType.CURLY_CLOSE); - ret = new SwitchItem(null, sloop, switchExpr, caseExprsAll, caseCmds, defCmd, 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, 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, cloopId); - break; - case RETURN: - GraphTargetItem retexpr = expression(thisType, pkg, needsActivation, importedClasses, openedNamespaces, true, registerVars, inFunction, inMethod, true, variables); - if (retexpr == null) { - ret = new ReturnVoidAVM2Item(null); - } else { - ret = new ReturnValueAVM2Item(null, retexpr); - } - break; - case TRY: - needsActivation.setVal(true); - List tryCommands = new ArrayList<>(); - tryCommands.add(command(thisType, pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, true, variables)); - s = lex(); - boolean found = false; - List> catchCommands = new ArrayList<>(); - List catchExceptions = new ArrayList<>(); - int varCnt = variables.size(); - List> 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(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 cc = new ArrayList<>(); - List catchVars = new ArrayList<>(); - cc.add(command(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(e.getVariableName())) { - try { - ui.resolve(null, new ArrayList(), new ArrayList(), abc, otherABCs, 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(e.getVariableName())) { - try { - ui.resolve(null, new ArrayList(), new ArrayList(), abc, otherABCs, new ArrayList(), variables); - } catch (CompilationException ex) { - // ignore - } - ui.setSlotNumber(e.getSlotNumber()); - ui.setSlotScope(e.getSlotScope()); - } - } - } - } - - List finallyCommands = null; - if (s.type == SymbolType.FINALLY) { - finallyCommands = new ArrayList<>(); - finallyCommands.add(command(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, expression(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(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(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(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, HashMap registerVars, boolean inFunction, boolean inMethod, boolean allowRemainder, List variables) throws IOException, AVM2ParseException { - return expression(thisType, pkg, needsActivation, importedClasses, openedNamespaces, false, registerVars, inFunction, inMethod, allowRemainder, variables); - } - - private GraphTargetItem fixPrecedence(GraphTargetItem expr) { - System.out.println("Fixing " + expr); - GraphTargetItem ret = expr; - - /* - fix > : - a || b > c => a || (b > c) - - a < 0 || (b > c) + 1 - - */ - if (expr instanceof BinaryOp) { - BinaryOp bo = (BinaryOp) expr; - GraphTargetItem left = bo.getLeftSide(); - GraphTargetItem right = bo.getRightSide(); - if (left.getPrecedence() > bo.getPrecedence()) { - if (left instanceof BinaryOp) { - BinaryOp leftBo = (BinaryOp) left; - bo.setLeftSide(leftBo.getRightSide()); - leftBo.setRightSide(expr); - System.out.println("fixed"); - return left; - } - } - } - return ret; - } - - /*private GraphTargetItem expressionRemainder(TypeItem thisType, String pkg, Reference needsActivation, List openedNamespaces, GraphTargetItem expr, HashMap registerVars, boolean inFunction, boolean inMethod, boolean allowRemainder, List variables, List 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; - } - if (item instanceof IndexAVM2Item) { - return true; - } - return false; - } - - private boolean isType(GraphTargetItem item) { - if (item == null) { - return false; - } - while (item instanceof GetPropertyAVM2Item) { - item = ((GetPropertyAVM2Item) item).object; - } - if (item instanceof NameAVM2Item) { - return true; - } - return false; - } - - private int brackets(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, List ret, HashMap registerVars, boolean inFunction, boolean inMethod, List 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(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(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, Stack loops, Map loopLabels, HashMap registerVars, boolean inFunction, boolean inMethod, int forInLevel, List variables) throws IOException, AVM2ParseException { - GraphTargetItem cmd = null; - List expr = new ArrayList<>(); - ParsedSymbol s; - do { - cmd = command(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(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, expr); - } - - private GraphTargetItem expression(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, boolean allowEmpty, HashMap registerVars, boolean inFunction, boolean inMethod, boolean allowRemainder, List variables) throws IOException, AVM2ParseException { - GraphTargetItem prim = expressionPrimary(thisType, pkg, needsActivation, importedClasses, openedNamespaces, allowEmpty, registerVars, inFunction, inMethod, allowRemainder, variables); - if (prim == null) { - return null; - } - return expression1(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 xmlToGreaterFix(ParsedSymbol symb) { - if (symb.isType(SymbolType.XML_STARTVARTAG_BEGIN, SymbolType.XML_STARTTAG_BEGIN)) { - lexer.yypushbackstr(symb.value.toString().substring(1)); //parse again as GREATER_THAN - symb.type = SymbolType.GREATER_THAN; - symb.group = SymbolGroup.OPERATOR; - } - } - - private ParsedSymbol peekExprToken() throws IOException, AVM2ParseException { - ParsedSymbol lookahead = lex(); - xmlToGreaterFix(lookahead); - - lexer.pushback(lookahead); - return lookahead; - } - - private GraphTargetItem expression1(GraphTargetItem lhs, int min_precedence, TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, boolean allowEmpty, HashMap registerVars, boolean inFunction, boolean inMethod, boolean allowRemainder, List 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(thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, allowRemainder, variables); - expectedType(SymbolType.COLON); - if (debugMode) { - System.out.println("/ternar-middle"); - } - } - - rhs = expressionPrimary(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(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(thisType, pkg, needsActivation, importedClasses, openedNamespaces, variables); - - lhs = new AsTypeAVM2Item(null, lhs, rhs); //??? - allowRemainder = false; - break; - - case IN: - lhs = new InAVM2Item(null, lhs, rhs); - break; - - case TERNAR: //??? - lhs = new TernarOpItem(null, lhs, mhs, rhs); - break; - case SHIFT_LEFT: - lhs = new LShiftAVM2Item(null, lhs, rhs); - break; - case SHIFT_RIGHT: - lhs = new RShiftAVM2Item(null, lhs, rhs); - break; - case USHIFT_RIGHT: - lhs = new URShiftAVM2Item(null, lhs, rhs); - break; - case BITAND: - lhs = new BitAndAVM2Item(null, lhs, rhs); - break; - case BITOR: - lhs = new BitOrAVM2Item(null, lhs, rhs); - break; - case DIVIDE: - lhs = new DivideAVM2Item(null, lhs, rhs); - break; - case MODULO: - lhs = new ModuloAVM2Item(null, lhs, rhs); - break; - case EQUALS: - lhs = new EqAVM2Item(null, lhs, rhs); - break; - case STRICT_EQUALS: - lhs = new StrictEqAVM2Item(null, lhs, rhs); - break; - case NOT_EQUAL: - lhs = new NeqAVM2Item(null, lhs, rhs); - break; - case STRICT_NOT_EQUAL: - lhs = new StrictNeqAVM2Item(null, lhs, rhs); - break; - case LOWER_THAN: - lhs = new LtAVM2Item(null, lhs, rhs); - break; - case LOWER_EQUAL: - lhs = new LeAVM2Item(null, lhs, rhs); - break; - case GREATER_THAN: - lhs = new GtAVM2Item(null, lhs, rhs); - break; - case GREATER_EQUAL: - lhs = new GeAVM2Item(null, lhs, rhs); - break; - case AND: - lhs = new AndItem(null, lhs, rhs); - break; - case OR: - lhs = new OrItem(null, lhs, rhs); - break; - case MINUS: - lhs = new SubtractAVM2Item(null, lhs, rhs); - break; - case MULTIPLY: - lhs = new MultiplyAVM2Item(null, lhs, rhs); - break; - case PLUS: - lhs = new AddAVM2Item(null, lhs, rhs); - break; - case XOR: - lhs = new BitXorAVM2Item(null, lhs, rhs); - break; - case INSTANCEOF: - lhs = new InstanceOfAVM2Item(null, lhs, rhs); - break; - case IS: - GraphTargetItem istype = rhs;//type(thisType,pkg,needsActivation, importedClasses, openedNamespaces, variables); - lhs = new IsTypeAVM2Item(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, lhs, assigned); - break; - case ASSIGN_BITOR: - assigned = new BitOrAVM2Item(null, lhs, assigned); - break; - case ASSIGN_DIVIDE: - assigned = new DivideAVM2Item(null, lhs, assigned); - break; - case ASSIGN_MINUS: - assigned = new SubtractAVM2Item(null, lhs, assigned); - break; - case ASSIGN_MODULO: - assigned = new ModuloAVM2Item(null, lhs, assigned); - break; - case ASSIGN_MULTIPLY: - assigned = new MultiplyAVM2Item(null, lhs, assigned); - break; - case ASSIGN_PLUS: - assigned = new AddAVM2Item(null, lhs, assigned); - break; - case ASSIGN_SHIFT_LEFT: - assigned = new LShiftAVM2Item(null, lhs, assigned); - break; - case ASSIGN_SHIFT_RIGHT: - assigned = new RShiftAVM2Item(null, lhs, assigned); - break; - case ASSIGN_USHIFT_RIGHT: - assigned = new URShiftAVM2Item(null, lhs, assigned); - break; - case ASSIGN_XOR: - assigned = new BitXorAVM2Item(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; - } - } - - switch (lookahead.type) { - case DOT: //member - case BRACKET_OPEN: //member - case PARENT_OPEN: //function call - case FILTER: - lhs = memberOrCall(thisType, pkg, needsActivation, importedClasses, openedNamespaces, lhs, registerVars, inFunction, inMethod, variables); - break; - - default: - if (lhs instanceof ParenthesisItem) { - GraphTargetItem coerced = expression(thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, allowRemainder, variables); - if (coerced != null && isType(((ParenthesisItem) lhs).value)) { - lhs = new CoerceAVM2Item(null, ((ParenthesisItem) lhs).value, coerced); - } - } - } - - if (debugMode) { - System.out.println("/expression1"); - } - return lhs; - } - - private GraphTargetItem expressionPrimary(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, boolean allowEmpty, HashMap registerVars, boolean inFunction, boolean inMethod, boolean allowRemainder, List variables) throws IOException, AVM2ParseException { - if (debugMode) { - System.out.println("primary:"); - } - GraphTargetItem ret = null; - ParsedSymbol s = lex(); - switch (s.type) { - case XML_STARTTAG_BEGIN: - lexer.pushback(s); - ret = xml(thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, variables); - - break; - case STRING: - ret = new StringAVM2Item(null, s.value.toString()); - - break; - case NEGATE: - ret = expression(thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables); - ret = new NegAVM2Item(null, ret); - - break; - case MINUS: - s = lex(); - if (s.isType(SymbolType.DOUBLE)) { - ret = new FloatValueAVM2Item(null, -(Double) s.value); - - } else if (s.isType(SymbolType.INTEGER)) { - ret = new IntegerValueAVM2Item(null, -(Long) s.value); - - } else { - lexer.pushback(s); - GraphTargetItem num = expression(thisType, pkg, needsActivation, importedClasses, openedNamespaces, 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, new IntegerValueAVM2Item(null, 0L), num)); - } - } - break; - case TYPEOF: - ret = new TypeOfAVM2Item(null, expression(thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); - - break; - case TRUE: - ret = new BooleanAVM2Item(null, true); - - break; - case NULL: - ret = new NullAVM2Item(null); - - break; - case UNDEFINED: - ret = new UndefinedAVM2Item(null); - break; - case FALSE: - ret = new BooleanAVM2Item(null, false); - - break; - case CURLY_OPEN: //Object literal - s = lex(); - List 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); - - GraphTargetItem n = new StringAVM2Item(null, s.value.toString()); -//expression(thisType,pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, allowRemainder, variables); - expectedType(SymbolType.COLON); - GraphTargetItem v = expression(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, nvs); - ret = memberOrCall(thisType, pkg, needsActivation, importedClasses, openedNamespaces, ret, registerVars, inFunction, inMethod, variables); - break; - case BRACKET_OPEN: //Array literal or just brackets - lexer.pushback(s); - List inBrackets = new ArrayList<>(); - int arrCnt = brackets(thisType, pkg, needsActivation, importedClasses, openedNamespaces, inBrackets, registerVars, inFunction, inMethod, variables); - ret = new NewArrayAVM2Item(null, inBrackets); - ret = memberOrCall(thisType, pkg, needsActivation, importedClasses, openedNamespaces, ret, registerVars, inFunction, inMethod, variables); - - 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(pkg, false, needsActivation, importedClasses, 0/*?*/, thisType, openedNamespaces, fname, false, variables); - ret = memberOrCall(thisType, pkg, needsActivation, importedClasses, openedNamespaces, ret, registerVars, inFunction, inMethod, variables); - break; - case NAN: - ret = new NanAVM2Item(null); - - break; - case INFINITY: - ret = new FloatValueAVM2Item(null, Double.POSITIVE_INFINITY); - - break; - case INTEGER: - ret = new IntegerValueAVM2Item(null, (Long) s.value); - - break; - case DOUBLE: - ret = new FloatValueAVM2Item(null, (Double) s.value); - - break; - case DELETE: - GraphTargetItem varDel = expression(thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables);//name(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(thisType, pkg, needsActivation, importedClasses, openedNamespaces, false, registerVars, inFunction, inMethod, false/*?*/, variables);//name(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, varincdec); - } - if (s.type == SymbolType.DECREMENT) { - ret = new PreDecrementAVM2Item(null, varincdec); - } - - break; - case NOT: - ret = new NotItem(null, expression(thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); - - break; - case PARENT_OPEN: - ret = new ParenthesisItem(null, expression(thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables)); - expectedType(SymbolType.PARENT_CLOSE); - ret = memberOrCall(thisType, pkg, needsActivation, importedClasses, openedNamespaces, ret, registerVars, inFunction, inMethod, variables); - - 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(pkg, false, needsActivation, importedClasses, 0/*?*/, thisType, openedNamespaces, ffname, false, variables); - } else if (s.type == SymbolType.LOWER_THAN) { - GraphTargetItem subtype = type(thisType, pkg, needsActivation, importedClasses, openedNamespaces, variables); - expectedType(SymbolType.GREATER_THAN); - s = lex(); - expected(s, lexer.yyline(), SymbolType.BRACKET_OPEN); - lexer.pushback(s); - List params = new ArrayList<>(); - brackets(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(thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); - newvar = applyType(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(thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, variables)); - - } else { - lexer.pushback(s); - GraphTargetItem newvar = name(thisType, pkg, needsActivation, false /*?*/, openedNamespaces, registerVars, inFunction, inMethod, variables, importedClasses); - newvar = applyType(thisType, pkg, needsActivation, importedClasses, openedNamespaces, newvar, registerVars, inFunction, inMethod, variables); - expectedType(SymbolType.PARENT_OPEN); - ret = new ConstructSomethingAVM2Item(lexer.yyline(), openedNamespaces, newvar, call(thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, variables)); - } - - break; - case IDENTIFIER: - case THIS: - case SUPER: - case ATTRIBUTE: - lexer.pushback(s); - GraphTargetItem var = name(thisType, pkg, needsActivation, false, openedNamespaces, registerVars, inFunction, inMethod, variables, importedClasses); - var = memberOrCall(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; - break; - } - lexer.pushback(s); - } - /*if (allowRemainder && existsRemainder) { - GraphTargetItem rem = ret; - do { - rem = expressionRemainder(thisType, pkg, needsActivation, openedNamespaces, rem, registerVars, inFunction, inMethod, assocRight, variables, importedClasses); - if (rem != null) { - ret = rem; - } - } while ((!assocRight) && (rem != null)); - ret = fixPrecedence(ret); - }*/ - if (debugMode) { - System.out.println("/primary"); - } - return ret; - } - - private ActionScriptLexer lexer = null; - - private List constantPool; - - private PackageAVM2Item parsePackage(List openedNamespaces) throws IOException, AVM2ParseException, CompilationException { - List items = new ArrayList<>(); - expectedType(SymbolType.PACKAGE); - String name = ""; - ParsedSymbol s = lex(); - if (s.type != SymbolType.CURLY_OPEN) { - expected(s, lexer.yyline(), SymbolGroup.IDENTIFIER); - name = s.value.toString(); - s = lex(); - } - while (s.type != SymbolType.CURLY_OPEN) { - expected(s, lexer.yyline(), SymbolType.DOT); - s = lex(); - expected(s, lexer.yyline(), SymbolGroup.IDENTIFIER); - name += "." + s.value.toString(); - s = lex(); - } - - List importedClasses = new ArrayList<>(); - - s = lex(); - while (s.type == SymbolType.IMPORT) { - String impPackage = ""; - String impName = null; - boolean all = false; - s = lex(); - expected(s, lexer.yyline(), SymbolGroup.IDENTIFIER); - impName = s.value.toString(); - s = lex(); - while (s.type == SymbolType.DOT) { - if (!"".equals(impPackage)) { - impPackage += "."; - } - impPackage += impName; - - s = lex(); - if (s.type == SymbolType.MULTIPLY) { - impName = null; - s = lex(); - break; - } - expected(s, lexer.yyline(), SymbolGroup.IDENTIFIER); - - impName = s.value.toString(); - s = lex(); - } - - if (impName == null) { - openedNamespaces.add(abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.getStringId(impPackage, true)), 0, true)); - } else { - importedClasses.add(impPackage + "." + impName); - } - - expected(s, lexer.yyline(), SymbolType.SEMICOLON); - s = lex(); - } - lexer.pushback(s); - - int publicNs; - openedNamespaces.add(publicNs = abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.getStringId(name, true)), 0, true)); - - //traits(false, new ArrayList(), new Reference(false), new ArrayList(), importedClasses, privateNs, 0, publicNs, packageInternalNs, 0, openedNamespaces, name, null, false, items); - //expectedType(SymbolType.CURLY_CLOSE); - return new PackageAVM2Item(publicNs, importedClasses, name, items); - } - - private List parseScript(String fileName) throws IOException, AVM2ParseException, CompilationException { - - List openedNamespaces = new ArrayList<>(); - - int scriptPrivateNs = 0; - - if (fileName.contains("/")) { - fileName = fileName.substring(fileName.lastIndexOf('/') + 1); - } - if (fileName.contains("\\")) { - fileName = fileName.substring(fileName.lastIndexOf('\\') + 1); - } - String className = fileName; - if (className.endsWith(".as")) { - className = className.substring(0, className.length() - 3); - } - openedNamespaces.add(scriptPrivateNs = abc.constants.addNamespace(new Namespace(Namespace.KIND_PRIVATE, 0))); //abc.constants.getStringId(name + ":" + className, true) - - int publicNs; - openedNamespaces.add(publicNs = abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.getStringId("", true)), 0, true)); - - List items = new ArrayList<>(); - traits(fileName, true, new ArrayList(), new Reference<>(false), new ArrayList(), new ArrayList(), scriptPrivateNs, 0, publicNs, 0, 0, openedNamespaces, null, null, false, items); - return items; - } - - public List scriptTraitsFromString(String str, String fileName) throws AVM2ParseException, IOException, CompilationException { - lexer = new ActionScriptLexer(str); - - List ret = parseScript(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 items, boolean documentClass, int classPos) throws AVM2ParseException, CompilationException { - AVM2SourceGenerator gen = new AVM2SourceGenerator(abc, otherABCs); - SourceGeneratorLocalData localData = new SourceGeneratorLocalData( - new HashMap(), 0, Boolean.FALSE, 0); - localData.documentClass = documentClass; - abc.script_info.add(gen.generateScriptInfo(localData, items, classPos)); - } - - public void addScript(String s, boolean documentClass, String fileName, int classPos) throws AVM2ParseException, IOException, CompilationException { - List traits = scriptTraitsFromString(s, fileName); - addScriptFromTree(traits, documentClass, classPos); - } - - public ActionScriptParser(ABC abc, List otherABCs) { - this.abc = abc; - this.otherABCs = otherABCs; - } - - private static void initPlayer() throws IOException, InterruptedException { - if (playerABCs.isEmpty()) { - 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); - for (Tag t : swf.tags) { - if (t instanceof ABCContainerTag) { - playerABCs.add(((ABCContainerTag) t).getABC()); - } - } - } - } - - public static void compile(String src, ABC abc, List otherABCs, boolean documentClass, String fileName, int classPos) throws AVM2ParseException, IOException, InterruptedException, CompilationException { - List parABCs = new ArrayList<>(); - initPlayer(); - parABCs.addAll(playerABCs); - parABCs.addAll(otherABCs); - ActionScriptParser parser = new ActionScriptParser(abc, parABCs); - parser.addScript(src, documentClass, fileName, classPos); - } - - public static void compile(SWF swf, String src, String dst, int classPos) { - System.err.println("WARNING: AS3 compiler is not finished yet. This is only used for debuggging!"); - try { - initPlayer(); - ABC abc = new ABC(null); - ActionScriptParser parser = new ActionScriptParser(abc, playerABCs); - parser.addScript(new String(Helper.readFile(src), "UTF-8"), true, src, classPos); - try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(new File(dst)))) { - abc.saveToStream(fos); - } - } catch (Exception ex) { - Logger.getLogger(ActionScriptParser.class.getName()).log(Level.SEVERE, null, ex); - } - System.exit(0); - } -} +/* + * Copyright (C) 2010-2015 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.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.NewArrayAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.NewObjectAVM2Item; +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.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.MethodBody; +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.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.BinaryOp; +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.DoWhileItem; +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.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 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.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 ActionScriptParser { + + private long uniqLast = 0; + + private final boolean debugMode = false; + + private static final String AS3_NAMESPACE = "http://adobe.com/AS3/2006/builtin"; + + private final ABC abc; + + private final List otherABCs; + + private static final List playerABCs = new ArrayList<>(); + + private long uniqId() { + uniqLast++; + return uniqLast; + } + + private List commands(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, Stack loops, Map loopLabels, HashMap registerVars, boolean inFunction, boolean inMethod, int forinlevel, List variables) throws IOException, AVM2ParseException { + List ret = new ArrayList<>(); + if (debugMode) { + System.out.println("commands:"); + } + GraphTargetItem cmd = null; + while ((cmd = command(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(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, List variables) throws IOException, AVM2ParseException { + ParsedSymbol s = lex(); + if (s.type == SymbolType.MULTIPLY) { + return new UnboundedTypeItem(); + } else if (s.type == SymbolType.VOID) { + return new TypeItem("void"); + } else { + lexer.pushback(s); + } + + GraphTargetItem t = name(thisType, pkg, needsActivation, true, openedNamespaces, null, false, false, variables, importedClasses); + t = applyType(thisType, pkg, needsActivation, importedClasses, openedNamespaces, t, new HashMap(), false, false, variables); + return t; + } + + private GraphTargetItem memberOrCall(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, GraphTargetItem newcmds, HashMap registerVars, boolean inFunction, boolean inMethod, List 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)) { + switch (s.type) { + case BRACKET_OPEN: + case DOT: + case TYPENAME: + lexer.pushback(s); + ret = member(thisType, pkg, needsActivation, importedClasses, openedNamespaces, ret, registerVars, inFunction, inMethod, variables); + break; + case FILTER: + needsActivation.setVal(true); + ret = new XMLFilterAVM2Item(ret, expression(thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, inMethod, variables), openedNamespaces); + expectedType(SymbolType.PARENT_CLOSE); + break; + case PARENT_OPEN: + ret = new CallAVM2Item(lexer.yyline(), ret, call(thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, variables)); + break; + + } + s = lex(); + } + if (s.type == SymbolType.INCREMENT) { + if (!isNameOrProp(ret)) { + throw new AVM2ParseException("Invalid assignment", lexer.yyline()); + } + ret = new PostIncrementAVM2Item(null, ret); + s = lex(); + + } else if (s.type == SymbolType.DECREMENT) { + if (!isNameOrProp(ret)) { + throw new AVM2ParseException("Invalid assignment", lexer.yyline()); + } + ret = new PostDecrementAVM2Item(null, ret); + s = lex(); + } + + lexer.pushback(s); + + if (debugMode) { + System.out.println("/memberOrCall"); + } + return ret; + } + + private GraphTargetItem applyType(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, GraphTargetItem obj, HashMap registerVars, boolean inFunction, boolean inMethod, List variables) throws IOException, AVM2ParseException { + GraphTargetItem ret = obj; + ParsedSymbol s = lex(); + if (s.type == SymbolType.TYPENAME) { + List params = new ArrayList<>(); + do { + params.add(expressionPrimary(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, ret, params); + } else { + lexer.pushback(s); + } + return ret; + } + + private GraphTargetItem member(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, GraphTargetItem obj, HashMap registerVars, boolean inFunction, boolean inMethod, List 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(thisType, pkg, needsActivation, importedClasses, openedNamespaces, ret, registerVars, inFunction, inMethod, variables); + s = lex(); + } else if (s.type == SymbolType.BRACKET_OPEN) { + GraphTargetItem index = expression(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); + String propName = s.value.toString(); + GraphTargetItem propItem = null; + s = lex(); + GraphTargetItem ns = null; + if (s.type == SymbolType.NAMESPACE_OP) { + ns = new UnresolvedAVM2Item(new ArrayList(), importedClasses, false, null, lexer.yyline(), propName, null, openedNamespaces); + variables.add((UnresolvedAVM2Item) ns); + s = lex(); + if (s.type == SymbolType.BRACKET_OPEN) { + propItem = expression(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, abc, otherABCs, openedNamespaces, new ArrayList()); + } + s = lex(); + } + } + lexer.pushback(s); + + if (debugMode) { + System.out.println("/member"); + } + return ret; + } + + private GraphTargetItem name(TypeItem thisType, String pkg, Reference needsActivation, boolean typeOnly, List openedNamespaces, HashMap registerVars, boolean inFunction, boolean inMethod, List variables, List importedClasses) throws IOException, AVM2ParseException { + ParsedSymbol s = lex(); + String name = ""; + if (s.type == SymbolType.ATTRIBUTE) { + name += "@"; + s = lex(); + } + expected(s, lexer.yyline(), SymbolGroup.IDENTIFIER, SymbolType.THIS, SymbolType.SUPER, SymbolType.STRING_OP); + name += s.value.toString(); + s = lex(); + boolean attrBracket = false; + + while (s.isType(SymbolType.DOT)) { + name += s.value.toString(); //. or :: + s = lex(); + if (s.type == SymbolType.ATTRIBUTE) { + name += "@"; + s = lex(); + if (s.type == SymbolType.MULTIPLY) { + name += s.value.toString(); + } else if (s.group == SymbolGroup.IDENTIFIER) { + name += 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); + name += s.value.toString(); + } + s = lex(); + } + String nsname = null; + String nsprop = null; + GraphTargetItem nspropItem = null; + if (s.type == SymbolType.NAMESPACE_OP) { + if (name.contains(".")) { + nsname = name.substring(name.lastIndexOf('.') + 1); + } else { + nsname = name; + } + s = lex(); + if (s.group == SymbolGroup.IDENTIFIER) { + nsprop = s.value.toString(); + } else if (s.type == SymbolType.BRACKET_OPEN) { + nspropItem = expression(thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); + expectedType(SymbolType.BRACKET_CLOSE); + } + if (name.contains(".")) { + name = name.substring(0, name.lastIndexOf('.')); + } else { + name = null; + } + s = lex(); + } + + GraphTargetItem ret = null; + if (name != null) { + 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(), 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(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 call(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, HashMap registerVars, boolean inFunction, boolean inMethod, List variables) throws IOException, AVM2ParseException { + List 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(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(String pkg, boolean isInterface, String customAccess, Reference needsActivation, List importedClasses, boolean override, boolean isFinal, TypeItem thisType, List openedNamespaces, boolean isStatic, int namespace, String functionName, boolean isMethod, List variables) throws IOException, AVM2ParseException { + FunctionAVM2Item f = function(pkg, isInterface, needsActivation, importedClasses, namespace, thisType, openedNamespaces, functionName, isMethod, variables); + return new MethodAVM2Item(f.pkg, f.isInterface, customAccess, f.needsActivation, f.hasRest, f.line, override, isFinal, isStatic, f.namespace, functionName, f.paramTypes, f.paramNames, f.paramValues, f.body, f.subvariables, f.retType); + } + + private FunctionAVM2Item function(String pkg, boolean isInterface, Reference needsActivation, List importedClasses, int namespace, TypeItem thisType, List openedNamespaces, String functionName, boolean isMethod, List variables) throws IOException, AVM2ParseException { + openedNamespaces = new ArrayList<>(openedNamespaces); //local copy + int line = lexer.yyline(); + ParsedSymbol s; + expectedType(SymbolType.PARENT_OPEN); + s = lex(); + List paramNames = new ArrayList<>(); + List paramTypes = new ArrayList<>(); + List 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(thisType, pkg, needsActivation, importedClasses, openedNamespaces, variables)); + s = lex(); + } else { + paramTypes.add(new UnboundedTypeItem()); + } + if (s.type == SymbolType.ASSIGN) { + paramValues.add(expression(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(thisType, pkg, needsActivation, importedClasses, openedNamespaces, variables); + } else { + retType = new UnboundedTypeItem(); + lexer.pushback(s); + } + List body = null; + List 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 needsActivation2 = new Reference<>(false); + if (!isInterface) { + expectedType(SymbolType.CURLY_OPEN); + body = commands(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(pkg, isInterface, needsActivation2.getVal(), namespace, hasRest, line, functionName, paramTypes, paramNames, paramValues, body, subvariables, retType); + } + + private GraphTargetItem traits(String scriptName, boolean scriptTraits, List sinitVariables, Reference sinitNeedsActivation, List staticInitializer, List importedClasses, int privateNs, int protectedNs, int publicNs, int packageInternalNs, int protectedStaticNs, List openedNamespaces, String pkg, String classNameStr, boolean isInterface, List traits) throws AVM2ParseException, IOException, CompilationException { + ParsedSymbol s; + GraphTargetItem constr = null; + TypeItem thisType = pkg == null && classNameStr == null ? null : new TypeItem(pkg == null || "".equals(pkg) ? classNameStr : pkg + "." + classNameStr); + List constrVariables = new ArrayList<>(); + List originalOpenedNamespaces = openedNamespaces; + int originalPrivateNs = privateNs; + boolean inPkg = pkg != null; + looptrait: + while (true) { + s = lex(); + boolean isStatic = false; + int namespace = -1; + boolean isGetter = false; + boolean isSetter = false; + boolean isOverride = false; + boolean isFinal = false; + boolean isDynamic = false; + String customAccess = null; + + if (scriptTraits && s.type == SymbolType.PACKAGE) { + if (inPkg) { + throw new AVM2ParseException("No subpackages allowed", lexer.yyline()); + } + openedNamespaces = new ArrayList<>(); + lexer.pushback(s); + PackageAVM2Item p = parsePackage(openedNamespaces); + pkg = p.packageName; + inPkg = true; + publicNs = p.publicNs; + importedClasses = p.importedClasses; + s = lex(); + } + if (inPkg || classNameStr != null) { + if (s.type == SymbolType.CURLY_OPEN) { + staticInitializer.addAll(commands(thisType, pkg, sinitNeedsActivation, importedClasses, openedNamespaces, new Stack(), new HashMap(), new HashMap(), true, false, 0, sinitVariables)); + expectedType(SymbolType.CURLY_CLOSE); + 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.DYNAMIC) { + if (isDynamic) { + throw new AVM2ParseException("Only one dynamic keyword allowed", lexer.yyline()); + } + isDynamic = 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) { + customAccess = s.value.toString(); + namespace = -2; + } else { + if (namespace != -1) { + 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: + 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(); + } + } else { + namespace = privateNs; + } + if (namespace == -1) { + if (isInterface) { + namespace = abc.constants.getNamespaceId(new Namespace(Namespace.KIND_NAMESPACE, abc.constants.getStringId(pkg == null || pkg.isEmpty() ? classNameStr : pkg + ":" + classNameStr, true)), 0, true); + } else { + namespace = packageInternalNs; + } + } + if (namespace == protectedNs && isStatic) { + namespace = protectedStaticNs; + } + switch (s.type) { + /*case PACKAGE: + lexer.pushback(s); + traits.add(parsePackage(openedNamespaces)); + break;*/ + case CLASS: + List subNamespaces = new ArrayList<>(openedNamespaces); + if (classNameStr != null) { + throw new AVM2ParseException("Nested classes not supported", lexer.yyline()); + } + if (isOverride) { + throw new AVM2ParseException("Override flag not allowed for classes", lexer.yyline()); + } + + //GraphTargetItem classTypeStr = type(thisType,pkg,needsActivation, importedClasses, openedNamespaces, variables); + s = lex(); + expected(s, lexer.yyline(), SymbolGroup.IDENTIFIER); + String classTypeStr = s.value.toString(); + GraphTargetItem extendsTypeStr = null; + s = lex(); + if (s.type == SymbolType.EXTENDS) { + extendsTypeStr = type(thisType, pkg, new Reference<>(false), importedClasses, subNamespaces, new ArrayList()); + s = lex(); + } + List implementsTypeStrs = new ArrayList<>(); + if (s.type == SymbolType.IMPLEMENTS) { + do { + GraphTargetItem implementsTypeStr = type(thisType, pkg, new Reference<>(false), importedClasses, subNamespaces, new ArrayList()); + implementsTypeStrs.add(implementsTypeStr); + s = lex(); + } while (s.type == SymbolType.COMMA); + } + expected(s, lexer.yyline(), SymbolType.CURLY_OPEN); + if (customAccess != null) { + throw new AVM2ParseException("Class cannot have custom namespace", lexer.yyline()); + } + traits.add((classTraits(scriptName, publicNs, pkg, importedClasses, isDynamic, isFinal, subNamespaces, pkg, namespace, false, classTypeStr, extendsTypeStr, implementsTypeStrs, new ArrayList()))); + expectedType(SymbolType.CURLY_CLOSE); + break; + case INTERFACE: + if (classNameStr != null) { + throw new AVM2ParseException("Nested interfaces not supported", lexer.yyline()); + } + if (isOverride) { + throw new AVM2ParseException("Override flag not allowed for interfaces", lexer.yyline()); + } + if (isFinal) { + throw new AVM2ParseException("Final flag not allowed for interfaces", lexer.yyline()); + } + if (isDynamic) { + throw new AVM2ParseException("Dynamic flag not allowed for interfaces", lexer.yyline()); + } + //GraphTargetItem interfaceTypeStr = type(thisType,pkg,needsActivation, importedClasses, openedNamespaces, variables); + s = lex(); + expected(s, lexer.yyline(), SymbolGroup.IDENTIFIER); + String intTypeStr = s.value.toString(); + s = lex(); + List intExtendsTypeStrs = new ArrayList<>(); + + if (s.type == SymbolType.EXTENDS) { + do { + GraphTargetItem intExtendsTypeStr = type(thisType, pkg, new Reference<>(false), importedClasses, openedNamespaces, new ArrayList()); + intExtendsTypeStrs.add(intExtendsTypeStr); + s = lex(); + } while (s.type == SymbolType.COMMA); + } + expected(s, lexer.yyline(), SymbolType.CURLY_OPEN); + if (customAccess != null) { + throw new AVM2ParseException("Interface cannot have custom namespace", lexer.yyline()); + } + traits.add((classTraits(scriptName, publicNs, pkg, importedClasses, false, isFinal, openedNamespaces, pkg, namespace, true, intTypeStr, null, intExtendsTypeStrs, new ArrayList()))); + expectedType(SymbolType.CURLY_CLOSE); + break; + + case FUNCTION: + + if (isDynamic) { + throw new AVM2ParseException("Dynamic flag not allowed for methods", lexer.yyline()); + } + s = lex(); + if (s.type == SymbolType.GET) { + if (classNameStr == null) { + throw new AVM2ParseException("No get keyword allowed here", lexer.yyline()); + } + isGetter = true; + s = lex(); + } else if (s.type == SymbolType.SET) { + if (classNameStr == null) { + throw new AVM2ParseException("No set keyword allowed here", lexer.yyline()); + } + isSetter = true; + s = lex(); + } + + expected(s, lexer.yyline(), SymbolGroup.IDENTIFIER); + String fname = s.value.toString(); + if (classNameStr != null && fname.equals(classNameStr)) { //constructor + if (isStatic) { + throw new AVM2ParseException("Constructor cannot be static", lexer.yyline()); + } + 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()); + } + constr = (method(pkg, false, customAccess, new Reference<>(false), importedClasses, false, false, thisType, openedNamespaces, false, namespace, "", true, constrVariables)); + } else { + MethodAVM2Item ft = method(pkg, isInterface, customAccess, new Reference<>(false), importedClasses, isOverride, isFinal, thisType, openedNamespaces, isStatic, namespace, 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()); + } + } + GraphTargetItem t; + if (isGetter) { + GetterAVM2Item g = new GetterAVM2Item(ft.pkg, isInterface, customAccess, ft.needsActivation, ft.hasRest, ft.line, ft.isOverride(), ft.isFinal(), isStatic, ft.namespace, ft.functionName, ft.paramTypes, ft.paramNames, ft.paramValues, ft.body, ft.subvariables, ft.retType); + t = g; + } else if (isSetter) { + SetterAVM2Item st = new SetterAVM2Item(ft.pkg, isInterface, customAccess, ft.needsActivation, ft.hasRest, ft.line, ft.isOverride(), ft.isFinal(), isStatic, ft.namespace, ft.functionName, ft.paramTypes, ft.paramNames, ft.paramValues, ft.body, ft.subvariables, ft.retType); + t = st; + } else { + t = ft; + } + + 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 == null || pkg.isEmpty() ? classNameStr : pkg + ":" + classNameStr) + "/" + nname; + } + if (s.type != SymbolType.SEMICOLON) { + lexer.pushback(s); + } + + ConstAVM2Item ns = new ConstAVM2Item(pkg, customAccess, true, namespace, nname, new TypeItem("Namespace"), new StringAVM2Item(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 (isDynamic) { + throw new AVM2ParseException("Dynamic 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 = null; + if (s.type == SymbolType.COLON) { + type = type(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(thisType, pkg, new Reference<>(false), importedClasses, openedNamespaces, new HashMap(), false, false, true, isStatic || isConst ? sinitVariables : constrVariables); + s = lex(); + } + GraphTargetItem tar; + if (isConst) { + tar = new ConstAVM2Item(pkg, customAccess, isStatic, namespace, vcname, type, value, lexer.yyline()); + } else { + tar = new SlotAVM2Item(pkg, customAccess, isStatic, namespace, vcname, type, value, lexer.yyline()); + } + traits.add(tar); + if (s.type != SymbolType.SEMICOLON) { + lexer.pushback(s); + } + break; + default: + if (s.type == SymbolType.CURLY_CLOSE && inPkg && classNameStr == null) { + inPkg = false; + pkg = null; + openedNamespaces = originalOpenedNamespaces; + privateNs = originalPrivateNs; + } else { + lexer.pushback(s); + break looptrait; + } + + } + } + return constr; + } + + private GraphTargetItem classTraits(String scriptName, int gpublicNs, String pkg, List importedClasses, boolean isDynamic, boolean isFinal, List openedNamespaces, String packageName, int namespace, boolean isInterface, String nameStr, GraphTargetItem extendsStr, List implementsStr, List variables) throws IOException, AVM2ParseException, CompilationException { + + GraphTargetItem ret = null; + + ParsedSymbol s = null; + List traits = new ArrayList<>(); + + String classNameStr = nameStr; + + openedNamespaces = new ArrayList<>(openedNamespaces); + + int publicNs = 0; + int privateNs = 0; + int packageInternalNs = 0; + if (pkg != null) { + openedNamespaces.add(packageInternalNs = abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE_INTERNAL, abc.constants.getStringId(pkg, true)), 0, true)); + } + if (pkg != null && !pkg.isEmpty()) { + openedNamespaces.add(publicNs = abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.getStringId("", true)), 0, true)); + } else { + publicNs = gpublicNs; + } + + openedNamespaces.add(privateNs = abc.constants.addNamespace(new Namespace(Namespace.KIND_PRIVATE, 0))); //abc.constants.getStringId(fileName + "$", true) + + openedNamespaces.add(abc.constants.getNamespaceId(new Namespace(Namespace.KIND_NAMESPACE, abc.constants.getStringId(AS3_NAMESPACE, true)), 0, true)); + + //int privateNs = 0; + int protectedNs = 0; + //int publicNs = namespace; + int protectedStaticNs = 0; + + openedNamespaces.add(protectedNs = abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PROTECTED, abc.constants.getStringId(packageName == null ? (scriptName + "$0:"/*FIXME?*/ + classNameStr) : packageName.isEmpty() ? classNameStr : packageName + ":" + classNameStr, true)), 0, true)); + openedNamespaces.add(protectedStaticNs = abc.constants.getNamespaceId(new Namespace(Namespace.KIND_STATIC_PROTECTED, abc.constants.getStringId(packageName == null || packageName.isEmpty() ? classNameStr : packageName + ":" + classNameStr, true)), 0, true)); + + if (extendsStr != null) { + List indices = new ArrayList<>(); + List names = new ArrayList<>(); + List namespaces = new ArrayList<>(); + //FIXME for Private classes in script!!! + AVM2SourceGenerator.parentNamesAddNames(abc, otherABCs, AVM2SourceGenerator.resolveType(new SourceGeneratorLocalData(new HashMap(), 0, false, 0), ((TypeItem) ((UnresolvedAVM2Item) extendsStr).resolve(null, new ArrayList(), new ArrayList(), abc, otherABCs, new ArrayList(), new ArrayList())), abc, otherABCs), indices, names, namespaces); + for (int i = 0; i < names.size(); i++) { + if (namespaces.get(i).isEmpty()) { + continue; + } + openedNamespaces.add(abc.constants.getNamespaceId(new Namespace(Namespace.KIND_STATIC_PROTECTED, abc.constants.getStringId(namespaces.get(i) + ":" + names.get(i), true)), 0, true)); + } + } + + Reference staticNeedsActivation = new Reference<>(false); + List staticInit = new ArrayList<>(); + List sinitVariables = new ArrayList<>(); + GraphTargetItem constr = traits(scriptName, false, sinitVariables, staticNeedsActivation, staticInit, importedClasses, privateNs, protectedNs, publicNs, packageInternalNs, protectedStaticNs, openedNamespaces, packageName, classNameStr, isInterface, traits); + + if (isInterface) { + return new InterfaceAVM2Item(importedClasses, packageName, openedNamespaces, isFinal, namespace, classNameStr, implementsStr, traits); + } else { + return new ClassAVM2Item(importedClasses, packageName, openedNamespaces, protectedNs, isDynamic, isFinal, namespace, classNameStr, extendsStr, implementsStr, staticInit, staticNeedsActivation.getVal(), sinitVariables, constr, traits); + } + } + + private GraphTargetItem expressionCommands(ParsedSymbol s, HashMap registerVars, boolean inFunction, boolean inMethod, int forinlevel, List variables) throws IOException, AVM2ParseException { + GraphTargetItem ret = null; + switch (s.type) { + /*case INT: + expectedType(SymbolType.PARENT_OPEN); + ret = new ToIntegerAVM2Item(null, expression(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(thisType,vi, registerVars, inFunction, inMethod, variables); + } else { + expected(s, lexer.yyline(), SymbolType.PARENT_OPEN); + ret = new ToNumberAVM2Item(null, expression(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(thisType,vi2, registerVars, inFunction, inMethod, variables); + } else { + expected(s, lexer.yyline(), SymbolType.PARENT_OPEN); + ret = new ToStringAVM2Item(null, expression(thisType,pkg,needsActivation, importedClasses, openedNamespaces,openedNamespacesKinds,registerVars, inFunction, inMethod, true, variables)); + expectedType(SymbolType.PARENT_CLOSE); + ret = memberOrCall(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, a.toString()); + ((StringBuilder) a).setLength(0); + return ret; + } + if (a instanceof String) { + return new StringAVM2Item(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, ta, tb); + } + + private void addS(List rets, StringBuilder sb) { + if (sb.length() > 0) { + if (!rets.isEmpty() && (rets.get(rets.size() - 1) instanceof StringAVM2Item)) { + ((StringAVM2Item) rets.get(rets.size() - 1)).value += sb.toString(); + } else { + rets.add(new StringAVM2Item(null, sb.toString())); + } + sb.setLength(0); + } + } + + private List xmltag(TypeItem thisType, String pkg, Reference usesVars, List openedTags, Reference closedVarTags, Reference needsActivation, List importedClasses, List openedNamespaces, HashMap registerVars, boolean inFunction, boolean inMethod, List variables) throws IOException, AVM2ParseException { + ParsedSymbol s = null; + List rets = new ArrayList<>(); + //GraphTargetItem ret = null; + StringBuilder sb = new StringBuilder(); + loop: + do { + s = lex(); + List sub = new ArrayList<>(); + Reference subclose = new Reference<>(0); + Reference subusesvars = new Reference<>(false); + switch (s.type) { + case XML_ATTRNAMEVAR_BEGIN: //add + usesVars.setVal(true); + addS(rets, sb); + rets.add(expression(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, expression(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(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, expression(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, expression(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); + lexer.yybegin(ActionScriptLexer.XML); + break; + case XML_STARTVARTAG_BEGIN: //add + //openedTags.add("*"); + + //ret = add(ret, ); + GraphTargetItem ex = expression(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(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(" st = xmltag(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(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, HashMap registerVars, boolean inFunction, boolean inMethod, List variables) throws IOException, AVM2ParseException { + List openedTags = new ArrayList<>(); + int closedVarTags = 0; + + GraphTargetItem ret = add(xmltag(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(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, Stack loops, Map loopLabels, HashMap registerVars, boolean inFunction, boolean inMethod, int forinlevel, boolean mustBeCommand, List 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(thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); + ret = new DefaultXMLNamespace(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(thisType, pkg, needsActivation, importedClasses, openedNamespaces, variables); + openedNamespaces.add(abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE /*FIXME?*/, abc.constants.getStringId(ns.toString(), true)), 0, true)); + break; + case WITH: + needsActivation.setVal(true); + expectedType(SymbolType.PARENT_OPEN); + GraphTargetItem wvar = expression(thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables);//(name(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 withVars = new ArrayList<>(); + List wcmd = commands(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, wvar, wcmd); + ((WithAVM2Item) ret).subvariables = withVars; + break; + /*case DELETE: + GraphTargetItem varDel = expression(thisType,pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables);//name(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, gm.object, gm.propertyName); + } else if (varDel instanceof NameAVM2Item) { + variables.remove(varDel); + ret = new DeletePropertyAVM2Item(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(pkg, false, needsActivation, importedClasses, 0/*?*/, 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(thisType, pkg, needsActivation, importedClasses, openedNamespaces, variables); + s = lex(); + } else { + type = new UnboundedTypeItem(); + } + + if (s.type == SymbolType.ASSIGN) { + GraphTargetItem varval = (expression(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, commands(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(thisType,pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables);//name(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, varincdec); + } else if (s.type == SymbolType.DECREMENT) { + ret = new PreDecrementAVM2Item(null, varincdec); + } + break;*/ + case SUPER: //constructor call + ParsedSymbol ss2 = lex(); + if (ss2.type == SymbolType.PARENT_OPEN) { + List args = call(thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, variables); + ret = new ConstructSuperAVM2Item(null, new LocalRegAVM2Item(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(thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables)); + expectedType(SymbolType.PARENT_CLOSE); + GraphTargetItem onTrue = command(thisType, pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, true, variables); + List onTrueList = new ArrayList<>(); + onTrueList.add(onTrue); + s = lex(); + List onFalseList = null; + if (s.type == SymbolType.ELSE) { + onFalseList = new ArrayList<>(); + onFalseList.add(command(thisType, pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, true, variables)); + } else { + lexer.pushback(s); + } + ret = new IfItem(null, ifExpr, onTrueList, onFalseList); + break; + case WHILE: + expectedType(SymbolType.PARENT_OPEN); + List whileExpr = new ArrayList<>(); + whileExpr.add(commaExpression(thisType, pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, variables)); + expectedType(SymbolType.PARENT_CLOSE); + List whileBody = new ArrayList<>(); + Loop wloop = new Loop(uniqId(), null, null); + if (loopLabel != null) { + loopLabels.put(wloop, loopLabel); + } + loops.push(wloop); + whileBody.add(command(thisType, pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, true, variables)); + ret = new WhileItem(null, wloop, whileExpr, whileBody); + break; + case DO: + List doBody = new ArrayList<>(); + Loop dloop = new Loop(uniqId(), null, null); + loops.push(dloop); + if (loopLabel != null) { + loopLabels.put(dloop, loopLabel); + } + doBody.add(command(thisType, pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, true, variables)); + expectedType(SymbolType.WHILE); + expectedType(SymbolType.PARENT_OPEN); + List doExpr = new ArrayList<>(); + doExpr.add(commaExpression(thisType, pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, variables)); + expectedType(SymbolType.PARENT_CLOSE); + ret = new DoWhileItem(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(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) { //??? WUT + firstCommand = expression1(firstCommand, firstCommand.getPrecedence(), 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 forFinalCommands = new ArrayList<>(); + GraphTargetItem forExpr = null; + List forFirstCommands = new ArrayList<>(); + if (!forin) { + //GraphTargetItem firstCommand = command(thisType,pkg,needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, true, variables); + if (firstCommand != null) { //can be empty command + forFirstCommands.add(firstCommand); + } + forExpr = (expression(thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables)); + expectedType(SymbolType.SEMICOLON); + forFinalCommands.add(command(thisType, pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, true, variables)); + } + expectedType(SymbolType.PARENT_CLOSE); + List forBody = new ArrayList<>(); + forBody.add(command(thisType, pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forin ? forinlevel + 1 : forinlevel, true, variables)); + if (forin) { + if (each) { + ret = new ForEachInAVM2Item(null, floop, inexpr, forBody); + } else { + + ret = new ForInAVM2Item(null, floop, inexpr, forBody); + } + } else { + ret = new ForItem(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(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> caseIfs = new ArrayList<>(); + List> caseCmds = new ArrayList<>(); + List caseExprsAll = new ArrayList<>(); + List valueMapping = new ArrayList<>(); + int pos = 0; + while (s.type == SymbolType.CASE) { + List caseExprs = new ArrayList<>(); + while (s.type == SymbolType.CASE) { + GraphTargetItem curCaseExpr = expression(thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); + caseExprs.add(curCaseExpr); + expectedType(SymbolType.COLON); + s = lex(); + caseExprsAll.add(curCaseExpr); + valueMapping.add(pos); + } + pos++; + lexer.pushback(s); + List caseCmd = commands(thisType, pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, variables); + caseCmds.add(caseCmd); + s = lex(); + } + List defCmd = new ArrayList<>(); + if (s.type == SymbolType.DEFAULT) { + expectedType(SymbolType.COLON); + defCmd = commands(thisType, pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, variables); + s = lexer.lex(); + } + expected(s, lexer.yyline(), SymbolType.CURLY_CLOSE); + ret = new SwitchItem(null, sloop, switchExpr, caseExprsAll, caseCmds, defCmd, 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, 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, cloopId); + break; + case RETURN: + GraphTargetItem retexpr = expression(thisType, pkg, needsActivation, importedClasses, openedNamespaces, true, registerVars, inFunction, inMethod, true, variables); + if (retexpr == null) { + ret = new ReturnVoidAVM2Item(null); + } else { + ret = new ReturnValueAVM2Item(null, retexpr); + } + break; + case TRY: + needsActivation.setVal(true); + List tryCommands = new ArrayList<>(); + tryCommands.add(command(thisType, pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, true, variables)); + s = lex(); + boolean found = false; + List> catchCommands = new ArrayList<>(); + List catchExceptions = new ArrayList<>(); + int varCnt = variables.size(); + List> 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(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 cc = new ArrayList<>(); + List catchVars = new ArrayList<>(); + cc.add(command(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(e.getVariableName())) { + try { + ui.resolve(null, new ArrayList(), new ArrayList(), abc, otherABCs, 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(e.getVariableName())) { + try { + ui.resolve(null, new ArrayList(), new ArrayList(), abc, otherABCs, new ArrayList(), variables); + } catch (CompilationException ex) { + // ignore + } + ui.setSlotNumber(e.getSlotNumber()); + ui.setSlotScope(e.getSlotScope()); + } + } + } + } + + List finallyCommands = null; + if (s.type == SymbolType.FINALLY) { + finallyCommands = new ArrayList<>(); + finallyCommands.add(command(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, expression(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(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(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(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, HashMap registerVars, boolean inFunction, boolean inMethod, boolean allowRemainder, List variables) throws IOException, AVM2ParseException { + return expression(thisType, pkg, needsActivation, importedClasses, openedNamespaces, false, registerVars, inFunction, inMethod, allowRemainder, variables); + } + + private GraphTargetItem fixPrecedence(GraphTargetItem expr) { + System.out.println("Fixing " + expr); + GraphTargetItem ret = expr; + + /* + fix > : + a || b > c => a || (b > c) + + a < 0 || (b > c) + 1 + + */ + if (expr instanceof BinaryOp) { + BinaryOp bo = (BinaryOp) expr; + GraphTargetItem left = bo.getLeftSide(); + GraphTargetItem right = bo.getRightSide(); + if (left.getPrecedence() > bo.getPrecedence()) { + if (left instanceof BinaryOp) { + BinaryOp leftBo = (BinaryOp) left; + bo.setLeftSide(leftBo.getRightSide()); + leftBo.setRightSide(expr); + System.out.println("fixed"); + return left; + } + } + } + return ret; + } + + /*private GraphTargetItem expressionRemainder(TypeItem thisType, String pkg, Reference needsActivation, List openedNamespaces, GraphTargetItem expr, HashMap registerVars, boolean inFunction, boolean inMethod, boolean allowRemainder, List variables, List 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; + } + if (item instanceof IndexAVM2Item) { + return true; + } + return false; + } + + private boolean isType(GraphTargetItem item) { + if (item == null) { + return false; + } + while (item instanceof GetPropertyAVM2Item) { + item = ((GetPropertyAVM2Item) item).object; + } + if (item instanceof NameAVM2Item) { + return true; + } + return false; + } + + private int brackets(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, List ret, HashMap registerVars, boolean inFunction, boolean inMethod, List 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(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(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, Stack loops, Map loopLabels, HashMap registerVars, boolean inFunction, boolean inMethod, int forInLevel, List variables) throws IOException, AVM2ParseException { + GraphTargetItem cmd = null; + List expr = new ArrayList<>(); + ParsedSymbol s; + do { + cmd = command(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(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, expr); + } + + private GraphTargetItem expression(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, boolean allowEmpty, HashMap registerVars, boolean inFunction, boolean inMethod, boolean allowRemainder, List variables) throws IOException, AVM2ParseException { + GraphTargetItem prim = expressionPrimary(thisType, pkg, needsActivation, importedClasses, openedNamespaces, allowEmpty, registerVars, inFunction, inMethod, allowRemainder, variables); + if (prim == null) { + return null; + } + return expression1(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 xmlToGreaterFix(ParsedSymbol symb) { + if (symb.isType(SymbolType.XML_STARTVARTAG_BEGIN, SymbolType.XML_STARTTAG_BEGIN)) { + lexer.yypushbackstr(symb.value.toString().substring(1)); //parse again as GREATER_THAN + symb.type = SymbolType.GREATER_THAN; + symb.group = SymbolGroup.OPERATOR; + } + } + + private ParsedSymbol peekExprToken() throws IOException, AVM2ParseException { + ParsedSymbol lookahead = lex(); + xmlToGreaterFix(lookahead); + + lexer.pushback(lookahead); + return lookahead; + } + + private GraphTargetItem expression1(GraphTargetItem lhs, int min_precedence, TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, boolean allowEmpty, HashMap registerVars, boolean inFunction, boolean inMethod, boolean allowRemainder, List 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(thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, allowRemainder, variables); + expectedType(SymbolType.COLON); + if (debugMode) { + System.out.println("/ternar-middle"); + } + } + + rhs = expressionPrimary(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(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(thisType, pkg, needsActivation, importedClasses, openedNamespaces, variables); + + lhs = new AsTypeAVM2Item(null, lhs, rhs); //??? + allowRemainder = false; + break; + + case IN: + lhs = new InAVM2Item(null, lhs, rhs); + break; + + case TERNAR: //??? + lhs = new TernarOpItem(null, lhs, mhs, rhs); + break; + case SHIFT_LEFT: + lhs = new LShiftAVM2Item(null, lhs, rhs); + break; + case SHIFT_RIGHT: + lhs = new RShiftAVM2Item(null, lhs, rhs); + break; + case USHIFT_RIGHT: + lhs = new URShiftAVM2Item(null, lhs, rhs); + break; + case BITAND: + lhs = new BitAndAVM2Item(null, lhs, rhs); + break; + case BITOR: + lhs = new BitOrAVM2Item(null, lhs, rhs); + break; + case DIVIDE: + lhs = new DivideAVM2Item(null, lhs, rhs); + break; + case MODULO: + lhs = new ModuloAVM2Item(null, lhs, rhs); + break; + case EQUALS: + lhs = new EqAVM2Item(null, lhs, rhs); + break; + case STRICT_EQUALS: + lhs = new StrictEqAVM2Item(null, lhs, rhs); + break; + case NOT_EQUAL: + lhs = new NeqAVM2Item(null, lhs, rhs); + break; + case STRICT_NOT_EQUAL: + lhs = new StrictNeqAVM2Item(null, lhs, rhs); + break; + case LOWER_THAN: + lhs = new LtAVM2Item(null, lhs, rhs); + break; + case LOWER_EQUAL: + lhs = new LeAVM2Item(null, lhs, rhs); + break; + case GREATER_THAN: + lhs = new GtAVM2Item(null, lhs, rhs); + break; + case GREATER_EQUAL: + lhs = new GeAVM2Item(null, lhs, rhs); + break; + case AND: + lhs = new AndItem(null, lhs, rhs); + break; + case OR: + lhs = new OrItem(null, lhs, rhs); + break; + case MINUS: + lhs = new SubtractAVM2Item(null, lhs, rhs); + break; + case MULTIPLY: + lhs = new MultiplyAVM2Item(null, lhs, rhs); + break; + case PLUS: + lhs = new AddAVM2Item(null, lhs, rhs); + break; + case XOR: + lhs = new BitXorAVM2Item(null, lhs, rhs); + break; + case INSTANCEOF: + lhs = new InstanceOfAVM2Item(null, lhs, rhs); + break; + case IS: + GraphTargetItem istype = rhs;//type(thisType,pkg,needsActivation, importedClasses, openedNamespaces, variables); + lhs = new IsTypeAVM2Item(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, lhs, assigned); + break; + case ASSIGN_BITOR: + assigned = new BitOrAVM2Item(null, lhs, assigned); + break; + case ASSIGN_DIVIDE: + assigned = new DivideAVM2Item(null, lhs, assigned); + break; + case ASSIGN_MINUS: + assigned = new SubtractAVM2Item(null, lhs, assigned); + break; + case ASSIGN_MODULO: + assigned = new ModuloAVM2Item(null, lhs, assigned); + break; + case ASSIGN_MULTIPLY: + assigned = new MultiplyAVM2Item(null, lhs, assigned); + break; + case ASSIGN_PLUS: + assigned = new AddAVM2Item(null, lhs, assigned); + break; + case ASSIGN_SHIFT_LEFT: + assigned = new LShiftAVM2Item(null, lhs, assigned); + break; + case ASSIGN_SHIFT_RIGHT: + assigned = new RShiftAVM2Item(null, lhs, assigned); + break; + case ASSIGN_USHIFT_RIGHT: + assigned = new URShiftAVM2Item(null, lhs, assigned); + break; + case ASSIGN_XOR: + assigned = new BitXorAVM2Item(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; + } + } + + switch (lookahead.type) { + case DOT: //member + case BRACKET_OPEN: //member + case PARENT_OPEN: //function call + case FILTER: + lhs = memberOrCall(thisType, pkg, needsActivation, importedClasses, openedNamespaces, lhs, registerVars, inFunction, inMethod, variables); + break; + + default: + if (lhs instanceof ParenthesisItem) { + GraphTargetItem coerced = expression(thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, allowRemainder, variables); + if (coerced != null && isType(((ParenthesisItem) lhs).value)) { + lhs = new CoerceAVM2Item(null, ((ParenthesisItem) lhs).value, coerced); + } + } + } + + if (debugMode) { + System.out.println("/expression1"); + } + return lhs; + } + + private GraphTargetItem expressionPrimary(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, boolean allowEmpty, HashMap registerVars, boolean inFunction, boolean inMethod, boolean allowRemainder, List variables) throws IOException, AVM2ParseException { + if (debugMode) { + System.out.println("primary:"); + } + GraphTargetItem ret = null; + ParsedSymbol s = lex(); + switch (s.type) { + case XML_STARTTAG_BEGIN: + lexer.pushback(s); + ret = xml(thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, variables); + + break; + case STRING: + ret = new StringAVM2Item(null, s.value.toString()); + + break; + case NEGATE: + ret = expression(thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables); + ret = new NegAVM2Item(null, ret); + + break; + case MINUS: + s = lex(); + if (s.isType(SymbolType.DOUBLE)) { + ret = new FloatValueAVM2Item(null, -(Double) s.value); + + } else if (s.isType(SymbolType.INTEGER)) { + ret = new IntegerValueAVM2Item(null, -(Long) s.value); + + } else { + lexer.pushback(s); + GraphTargetItem num = expression(thisType, pkg, needsActivation, importedClasses, openedNamespaces, 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, new IntegerValueAVM2Item(null, 0L), num)); + } + } + break; + case TYPEOF: + ret = new TypeOfAVM2Item(null, expression(thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); + + break; + case TRUE: + ret = new BooleanAVM2Item(null, true); + + break; + case NULL: + ret = new NullAVM2Item(null); + + break; + case UNDEFINED: + ret = new UndefinedAVM2Item(null); + break; + case FALSE: + ret = new BooleanAVM2Item(null, false); + + break; + case CURLY_OPEN: //Object literal + s = lex(); + List 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); + + GraphTargetItem n = new StringAVM2Item(null, s.value.toString()); +//expression(thisType,pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, allowRemainder, variables); + expectedType(SymbolType.COLON); + GraphTargetItem v = expression(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, nvs); + ret = memberOrCall(thisType, pkg, needsActivation, importedClasses, openedNamespaces, ret, registerVars, inFunction, inMethod, variables); + break; + case BRACKET_OPEN: //Array literal or just brackets + lexer.pushback(s); + List inBrackets = new ArrayList<>(); + int arrCnt = brackets(thisType, pkg, needsActivation, importedClasses, openedNamespaces, inBrackets, registerVars, inFunction, inMethod, variables); + ret = new NewArrayAVM2Item(null, inBrackets); + ret = memberOrCall(thisType, pkg, needsActivation, importedClasses, openedNamespaces, ret, registerVars, inFunction, inMethod, variables); + + 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(pkg, false, needsActivation, importedClasses, 0/*?*/, thisType, openedNamespaces, fname, false, variables); + ret = memberOrCall(thisType, pkg, needsActivation, importedClasses, openedNamespaces, ret, registerVars, inFunction, inMethod, variables); + break; + case NAN: + ret = new NanAVM2Item(null); + + break; + case INFINITY: + ret = new FloatValueAVM2Item(null, Double.POSITIVE_INFINITY); + + break; + case INTEGER: + ret = new IntegerValueAVM2Item(null, (Long) s.value); + + break; + case DOUBLE: + ret = new FloatValueAVM2Item(null, (Double) s.value); + + break; + case DELETE: + GraphTargetItem varDel = expression(thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables);//name(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(thisType, pkg, needsActivation, importedClasses, openedNamespaces, false, registerVars, inFunction, inMethod, false/*?*/, variables);//name(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, varincdec); + } + if (s.type == SymbolType.DECREMENT) { + ret = new PreDecrementAVM2Item(null, varincdec); + } + + break; + case NOT: + ret = new NotItem(null, expression(thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); + + break; + case PARENT_OPEN: + ret = new ParenthesisItem(null, expression(thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables)); + expectedType(SymbolType.PARENT_CLOSE); + ret = memberOrCall(thisType, pkg, needsActivation, importedClasses, openedNamespaces, ret, registerVars, inFunction, inMethod, variables); + + 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(pkg, false, needsActivation, importedClasses, 0/*?*/, thisType, openedNamespaces, ffname, false, variables); + } else if (s.type == SymbolType.LOWER_THAN) { + GraphTargetItem subtype = type(thisType, pkg, needsActivation, importedClasses, openedNamespaces, variables); + expectedType(SymbolType.GREATER_THAN); + s = lex(); + expected(s, lexer.yyline(), SymbolType.BRACKET_OPEN); + lexer.pushback(s); + List params = new ArrayList<>(); + brackets(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(thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); + newvar = applyType(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(thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, variables)); + + } else { + lexer.pushback(s); + GraphTargetItem newvar = name(thisType, pkg, needsActivation, false /*?*/, openedNamespaces, registerVars, inFunction, inMethod, variables, importedClasses); + newvar = applyType(thisType, pkg, needsActivation, importedClasses, openedNamespaces, newvar, registerVars, inFunction, inMethod, variables); + expectedType(SymbolType.PARENT_OPEN); + ret = new ConstructSomethingAVM2Item(lexer.yyline(), openedNamespaces, newvar, call(thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, variables)); + } + + break; + case IDENTIFIER: + case THIS: + case SUPER: + case ATTRIBUTE: + lexer.pushback(s); + GraphTargetItem var = name(thisType, pkg, needsActivation, false, openedNamespaces, registerVars, inFunction, inMethod, variables, importedClasses); + var = memberOrCall(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; + break; + } + lexer.pushback(s); + } + /*if (allowRemainder && existsRemainder) { + GraphTargetItem rem = ret; + do { + rem = expressionRemainder(thisType, pkg, needsActivation, openedNamespaces, rem, registerVars, inFunction, inMethod, assocRight, variables, importedClasses); + if (rem != null) { + ret = rem; + } + } while ((!assocRight) && (rem != null)); + ret = fixPrecedence(ret); + }*/ + if (debugMode) { + System.out.println("/primary"); + } + return ret; + } + + private ActionScriptLexer lexer = null; + + private List constantPool; + + private PackageAVM2Item parsePackage(List openedNamespaces) throws IOException, AVM2ParseException, CompilationException { + List items = new ArrayList<>(); + expectedType(SymbolType.PACKAGE); + String name = ""; + ParsedSymbol s = lex(); + if (s.type != SymbolType.CURLY_OPEN) { + expected(s, lexer.yyline(), SymbolGroup.IDENTIFIER); + name = s.value.toString(); + s = lex(); + } + while (s.type != SymbolType.CURLY_OPEN) { + expected(s, lexer.yyline(), SymbolType.DOT); + s = lex(); + expected(s, lexer.yyline(), SymbolGroup.IDENTIFIER); + name += "." + s.value.toString(); + s = lex(); + } + + List importedClasses = new ArrayList<>(); + + s = lex(); + while (s.type == SymbolType.IMPORT) { + String impPackage = ""; + String impName = null; + boolean all = false; + s = lex(); + expected(s, lexer.yyline(), SymbolGroup.IDENTIFIER); + impName = s.value.toString(); + s = lex(); + while (s.type == SymbolType.DOT) { + if (!"".equals(impPackage)) { + impPackage += "."; + } + impPackage += impName; + + s = lex(); + if (s.type == SymbolType.MULTIPLY) { + impName = null; + s = lex(); + break; + } + expected(s, lexer.yyline(), SymbolGroup.IDENTIFIER); + + impName = s.value.toString(); + s = lex(); + } + + if (impName == null) { + openedNamespaces.add(abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.getStringId(impPackage, true)), 0, true)); + } else { + importedClasses.add(impPackage + "." + impName); + } + + expected(s, lexer.yyline(), SymbolType.SEMICOLON); + s = lex(); + } + lexer.pushback(s); + + int publicNs; + openedNamespaces.add(publicNs = abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.getStringId(name, true)), 0, true)); + + //traits(false, new ArrayList(), new Reference(false), new ArrayList(), importedClasses, privateNs, 0, publicNs, packageInternalNs, 0, openedNamespaces, name, null, false, items); + //expectedType(SymbolType.CURLY_CLOSE); + return new PackageAVM2Item(publicNs, importedClasses, name, items); + } + + private List parseScript(String fileName) throws IOException, AVM2ParseException, CompilationException { + + List openedNamespaces = new ArrayList<>(); + + int scriptPrivateNs = 0; + + if (fileName.contains("/")) { + fileName = fileName.substring(fileName.lastIndexOf('/') + 1); + } + if (fileName.contains("\\")) { + fileName = fileName.substring(fileName.lastIndexOf('\\') + 1); + } + String className = fileName; + if (className.endsWith(".as")) { + className = className.substring(0, className.length() - 3); + } + openedNamespaces.add(scriptPrivateNs = abc.constants.addNamespace(new Namespace(Namespace.KIND_PRIVATE, 0))); //abc.constants.getStringId(name + ":" + className, true) + + int publicNs; + openedNamespaces.add(publicNs = abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.getStringId("", true)), 0, true)); + + List items = new ArrayList<>(); + traits(fileName, true, new ArrayList(), new Reference<>(false), new ArrayList(), new ArrayList(), scriptPrivateNs, 0, publicNs, 0, 0, openedNamespaces, null, null, false, items); + return items; + } + + public List scriptTraitsFromString(String str, String fileName) throws AVM2ParseException, IOException, CompilationException { + lexer = new ActionScriptLexer(str); + + List ret = parseScript(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 items, boolean documentClass, int classPos) throws AVM2ParseException, CompilationException { + AVM2SourceGenerator gen = new AVM2SourceGenerator(abc, otherABCs); + SourceGeneratorLocalData localData = new SourceGeneratorLocalData( + new HashMap(), 0, Boolean.FALSE, 0); + localData.documentClass = documentClass; + abc.script_info.add(gen.generateScriptInfo(localData, items, classPos)); + } + + public void addScript(String s, boolean documentClass, String fileName, int classPos) throws AVM2ParseException, IOException, CompilationException { + List traits = scriptTraitsFromString(s, fileName); + addScriptFromTree(traits, documentClass, classPos); + } + + public ActionScriptParser(ABC abc, List otherABCs) { + this.abc = abc; + this.otherABCs = otherABCs; + } + + private static void initPlayer() throws IOException, InterruptedException { + if (playerABCs.isEmpty()) { + 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); + for (Tag t : swf.tags) { + if (t instanceof ABCContainerTag) { + playerABCs.add(((ABCContainerTag) t).getABC()); + } + } + } + } + + public static void compile(String src, ABC abc, List otherABCs, boolean documentClass, String fileName, int classPos) throws AVM2ParseException, IOException, InterruptedException, CompilationException { + List parABCs = new ArrayList<>(); + initPlayer(); + parABCs.addAll(playerABCs); + parABCs.addAll(otherABCs); + ActionScriptParser parser = new ActionScriptParser(abc, parABCs); + parser.addScript(src, documentClass, fileName, classPos); + } + + public static void compile(SWF swf, String src, String dst, int classPos) { + System.err.println("WARNING: AS3 compiler is not finished yet. This is only used for debuggging!"); + try { + initPlayer(); + ABC abc = new ABC(null); + ActionScriptParser parser = new ActionScriptParser(abc, playerABCs); + parser.addScript(new String(Helper.readFile(src), "UTF-8"), true, src, classPos); + try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(new File(dst)))) { + abc.saveToStream(fos); + } + } catch (Exception ex) { + Logger.getLogger(ActionScriptParser.class.getName()).log(Level.SEVERE, null, ex); + } + System.exit(0); + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/parser/script/ActionScriptParser.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/parser/script/ActionScriptParser.java index bb077f27f..5b78b1f0b 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/parser/script/ActionScriptParser.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/parser/script/ActionScriptParser.java @@ -1,1910 +1,1907 @@ -/* - * Copyright (C) 2010-2015 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.action.parser.script; - -import com.jpexs.decompiler.flash.SourceGeneratorLocalData; -import com.jpexs.decompiler.flash.action.Action; -import com.jpexs.decompiler.flash.action.model.AsciiToCharActionItem; -import com.jpexs.decompiler.flash.action.model.CallActionItem; -import com.jpexs.decompiler.flash.action.model.CallFunctionActionItem; -import com.jpexs.decompiler.flash.action.model.CallMethodActionItem; -import com.jpexs.decompiler.flash.action.model.CastOpActionItem; -import com.jpexs.decompiler.flash.action.model.CharToAsciiActionItem; -import com.jpexs.decompiler.flash.action.model.CloneSpriteActionItem; -import com.jpexs.decompiler.flash.action.model.DefineLocalActionItem; -import com.jpexs.decompiler.flash.action.model.DeleteActionItem; -import com.jpexs.decompiler.flash.action.model.DirectValueActionItem; -import com.jpexs.decompiler.flash.action.model.EvalActionItem; -import com.jpexs.decompiler.flash.action.model.FSCommandActionItem; -import com.jpexs.decompiler.flash.action.model.FunctionActionItem; -import com.jpexs.decompiler.flash.action.model.GetMemberActionItem; -import com.jpexs.decompiler.flash.action.model.GetTimeActionItem; -import com.jpexs.decompiler.flash.action.model.GetURL2ActionItem; -import com.jpexs.decompiler.flash.action.model.GetVariableActionItem; -import com.jpexs.decompiler.flash.action.model.GetVersionActionItem; -import com.jpexs.decompiler.flash.action.model.GotoFrame2ActionItem; -import com.jpexs.decompiler.flash.action.model.InitArrayActionItem; -import com.jpexs.decompiler.flash.action.model.InitObjectActionItem; -import com.jpexs.decompiler.flash.action.model.LoadMovieActionItem; -import com.jpexs.decompiler.flash.action.model.LoadMovieNumActionItem; -import com.jpexs.decompiler.flash.action.model.LoadVariablesActionItem; -import com.jpexs.decompiler.flash.action.model.LoadVariablesNumActionItem; -import com.jpexs.decompiler.flash.action.model.MBAsciiToCharActionItem; -import com.jpexs.decompiler.flash.action.model.MBCharToAsciiActionItem; -import com.jpexs.decompiler.flash.action.model.MBStringExtractActionItem; -import com.jpexs.decompiler.flash.action.model.MBStringLengthActionItem; -import com.jpexs.decompiler.flash.action.model.NewMethodActionItem; -import com.jpexs.decompiler.flash.action.model.NewObjectActionItem; -import com.jpexs.decompiler.flash.action.model.NextFrameActionItem; -import com.jpexs.decompiler.flash.action.model.PlayActionItem; -import com.jpexs.decompiler.flash.action.model.PostDecrementActionItem; -import com.jpexs.decompiler.flash.action.model.PostIncrementActionItem; -import com.jpexs.decompiler.flash.action.model.PrevFrameActionItem; -import com.jpexs.decompiler.flash.action.model.PrintActionItem; -import com.jpexs.decompiler.flash.action.model.PrintAsBitmapActionItem; -import com.jpexs.decompiler.flash.action.model.PrintAsBitmapNumActionItem; -import com.jpexs.decompiler.flash.action.model.PrintNumActionItem; -import com.jpexs.decompiler.flash.action.model.RandomNumberActionItem; -import com.jpexs.decompiler.flash.action.model.RemoveSpriteActionItem; -import com.jpexs.decompiler.flash.action.model.ReturnActionItem; -import com.jpexs.decompiler.flash.action.model.SetMemberActionItem; -import com.jpexs.decompiler.flash.action.model.SetVariableActionItem; -import com.jpexs.decompiler.flash.action.model.StartDragActionItem; -import com.jpexs.decompiler.flash.action.model.StopActionItem; -import com.jpexs.decompiler.flash.action.model.StopAllSoundsActionItem; -import com.jpexs.decompiler.flash.action.model.StopDragActionItem; -import com.jpexs.decompiler.flash.action.model.StringExtractActionItem; -import com.jpexs.decompiler.flash.action.model.StringLengthActionItem; -import com.jpexs.decompiler.flash.action.model.TargetPathActionItem; -import com.jpexs.decompiler.flash.action.model.ThrowActionItem; -import com.jpexs.decompiler.flash.action.model.ToIntegerActionItem; -import com.jpexs.decompiler.flash.action.model.ToNumberActionItem; -import com.jpexs.decompiler.flash.action.model.ToStringActionItem; -import com.jpexs.decompiler.flash.action.model.ToggleHighQualityActionItem; -import com.jpexs.decompiler.flash.action.model.TraceActionItem; -import com.jpexs.decompiler.flash.action.model.TypeOfActionItem; -import com.jpexs.decompiler.flash.action.model.UnLoadMovieActionItem; -import com.jpexs.decompiler.flash.action.model.UnLoadMovieNumActionItem; -import com.jpexs.decompiler.flash.action.model.clauses.ClassActionItem; -import com.jpexs.decompiler.flash.action.model.clauses.ForInActionItem; -import com.jpexs.decompiler.flash.action.model.clauses.IfFrameLoadedActionItem; -import com.jpexs.decompiler.flash.action.model.clauses.InterfaceActionItem; -import com.jpexs.decompiler.flash.action.model.clauses.TellTargetActionItem; -import com.jpexs.decompiler.flash.action.model.clauses.TryActionItem; -import com.jpexs.decompiler.flash.action.model.clauses.WithActionItem; -import com.jpexs.decompiler.flash.action.model.operations.AddActionItem; -import com.jpexs.decompiler.flash.action.model.operations.AndActionItem; -import com.jpexs.decompiler.flash.action.model.operations.BitAndActionItem; -import com.jpexs.decompiler.flash.action.model.operations.BitOrActionItem; -import com.jpexs.decompiler.flash.action.model.operations.BitXorActionItem; -import com.jpexs.decompiler.flash.action.model.operations.DivideActionItem; -import com.jpexs.decompiler.flash.action.model.operations.EqActionItem; -import com.jpexs.decompiler.flash.action.model.operations.GeActionItem; -import com.jpexs.decompiler.flash.action.model.operations.GtActionItem; -import com.jpexs.decompiler.flash.action.model.operations.InstanceOfActionItem; -import com.jpexs.decompiler.flash.action.model.operations.LShiftActionItem; -import com.jpexs.decompiler.flash.action.model.operations.LeActionItem; -import com.jpexs.decompiler.flash.action.model.operations.LtActionItem; -import com.jpexs.decompiler.flash.action.model.operations.ModuloActionItem; -import com.jpexs.decompiler.flash.action.model.operations.MultiplyActionItem; -import com.jpexs.decompiler.flash.action.model.operations.NeqActionItem; -import com.jpexs.decompiler.flash.action.model.operations.OrActionItem; -import com.jpexs.decompiler.flash.action.model.operations.PreDecrementActionItem; -import com.jpexs.decompiler.flash.action.model.operations.PreIncrementActionItem; -import com.jpexs.decompiler.flash.action.model.operations.RShiftActionItem; -import com.jpexs.decompiler.flash.action.model.operations.StrictEqActionItem; -import com.jpexs.decompiler.flash.action.model.operations.StrictNeqActionItem; -import com.jpexs.decompiler.flash.action.model.operations.StringAddActionItem; -import com.jpexs.decompiler.flash.action.model.operations.StringEqActionItem; -import com.jpexs.decompiler.flash.action.model.operations.StringGeActionItem; -import com.jpexs.decompiler.flash.action.model.operations.StringGtActionItem; -import com.jpexs.decompiler.flash.action.model.operations.StringLeActionItem; -import com.jpexs.decompiler.flash.action.model.operations.StringLtActionItem; -import com.jpexs.decompiler.flash.action.model.operations.StringNeActionItem; -import com.jpexs.decompiler.flash.action.model.operations.SubtractActionItem; -import com.jpexs.decompiler.flash.action.model.operations.URShiftActionItem; -import com.jpexs.decompiler.flash.action.parser.ActionParseException; -import com.jpexs.decompiler.flash.action.swf4.ActionIf; -import com.jpexs.decompiler.flash.action.swf4.ConstantIndex; -import com.jpexs.decompiler.flash.action.swf5.ActionConstantPool; -import com.jpexs.decompiler.flash.ecma.Null; -import com.jpexs.decompiler.flash.ecma.Undefined; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.flash.helpers.collections.MyEntry; -import com.jpexs.decompiler.graph.CompilationException; -import com.jpexs.decompiler.graph.GraphSourceItem; -import com.jpexs.decompiler.graph.GraphTargetItem; -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.DoWhileItem; -import com.jpexs.decompiler.graph.model.ForItem; -import com.jpexs.decompiler.graph.model.IfItem; -import com.jpexs.decompiler.graph.model.LocalData; -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.SwitchItem; -import com.jpexs.decompiler.graph.model.TernarOpItem; -import com.jpexs.decompiler.graph.model.WhileItem; -import java.io.IOException; -import java.io.StringReader; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; - -/** - * - * @author JPEXS - */ -public class ActionScriptParser { - - private final int swfVersion; - - public ActionScriptParser(int swfVersion) { - this.swfVersion = swfVersion; - } - - private long uniqLast = 0; - - private final boolean debugMode = false; - - private String uniqId() { - uniqLast++; - return "" + uniqLast; - } - - private List commands(boolean inFunction, boolean inMethod, int forinlevel, List variables) throws IOException, ActionParseException { - List ret = new ArrayList<>(); - if (debugMode) { - System.out.println("commands:"); - } - GraphTargetItem cmd = null; - while ((cmd = command(inFunction, inMethod, forinlevel, true, variables)) != null) { - ret.add(cmd); - } - if (debugMode) { - System.out.println("/commands"); - } - return ret; - } - - private GraphTargetItem type(List variables) throws IOException, ActionParseException { - GraphTargetItem ret = null; - - ParsedSymbol s = lex(); - expected(s, lexer.yyline(), SymbolType.IDENTIFIER, SymbolType.STRING_OP); - ret = new VariableActionItem(s.value.toString(), null, false); - variables.add((VariableActionItem) ret); - s = lex(); - while (s.type == SymbolType.DOT) { - s = lex(); - expected(s, lexer.yyline(), SymbolType.IDENTIFIER, SymbolType.STRING_OP); - ret = new GetMemberActionItem(null, ret, pushConst(s.value.toString())); - s = lex(); - } - lexer.pushback(s); - return ret; - } - - private GraphTargetItem memberOrCall(GraphTargetItem newcmds, boolean inFunction, boolean inMethod, List variables) throws IOException, ActionParseException { - ParsedSymbol s = lex(); - GraphTargetItem ret = newcmds; - while (s.isType(SymbolType.DOT, SymbolType.BRACKET_OPEN, SymbolType.PARENT_OPEN)) { - switch (s.type) { - case DOT: - case BRACKET_OPEN: - lexer.pushback(s); - ret = member(ret, inFunction, inMethod, variables); - break; - case PARENT_OPEN: - if (ret instanceof GetMemberActionItem) { - GetMemberActionItem mem = (GetMemberActionItem) ret; - ret = new CallMethodActionItem(null, mem.object, mem.memberName, call(inFunction, inMethod, variables)); - } else if (ret instanceof VariableActionItem) { - VariableActionItem var = (VariableActionItem) ret; - ret = new CallFunctionActionItem(null, pushConst(var.getVariableName()), call(inFunction, inMethod, variables)); - } else { - ret = new CallFunctionActionItem(null, ret, call(inFunction, inMethod, variables)); - } - break; - } - s = lex(); - } - lexer.pushback(s); - return ret; - } - - private GraphTargetItem member(GraphTargetItem obj, boolean inFunction, boolean inMethod, List variables) throws IOException, ActionParseException { - GraphTargetItem ret = obj; - ParsedSymbol s = lex(); - while (s.isType(SymbolType.DOT, SymbolType.BRACKET_OPEN)) { - if (s.type == SymbolType.BRACKET_OPEN) { - ret = new GetMemberActionItem(null, ret, expression(inFunction, inMethod, true, variables)); - expectedType(SymbolType.BRACKET_CLOSE); - s = lex(); - continue; - } - s = lex(); - expected(s, lexer.yyline(), SymbolType.IDENTIFIER, SymbolType.THIS, SymbolType.SUPER, SymbolGroup.GLOBALFUNC, SymbolGroup.GLOBALCONST); - //TODO: Handle properties (?) - if (false) {//GraphTargetItem.propertyNamesList.contains(s.value.toString())) { - //ret.add(new ActionPush((Long) (long) (int) GraphTargetItem.propertyNamesList.indexOf(s.value.toString()))); - //ret.add(new ActionGetProperty()); - } else { - ret = new GetMemberActionItem(null, ret, pushConst(s.value.toString())); - } - s = lex(); - } - lexer.pushback(s); - if (s.type.getPrecedence() == GraphTargetItem.PRECEDENCE_ASSIGMENT) { - ret = expression1(ret, s.type.getPrecedence(), inFunction, inMethod, inMethod, variables); - } - - if (debugMode) { - System.out.println("/member"); - } - return ret; - } - - private GraphTargetItem variable(boolean inFunction, boolean inMethod, List variables) throws IOException, ActionParseException { - GraphTargetItem ret = null; - ParsedSymbol s = lex(); - expected(s, lexer.yyline(), SymbolType.IDENTIFIER, SymbolType.THIS, SymbolType.SUPER, SymbolType.STRING_OP); - ret = new VariableActionItem(s.value.toString(), null, false); - variables.add((VariableActionItem) ret); - ret = (member(ret, inFunction, inMethod, variables)); - return ret; - } - - private void expected(ParsedSymbol symb, int line, Object... expected) throws IOException, ActionParseException { - 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 ActionParseException("" + expStr + " expected but " + symb.type + " found", line); - } - } - - private ParsedSymbol expectedType(Object... type) throws IOException, ActionParseException { - ParsedSymbol symb = lex(); - expected(symb, lexer.yyline(), type); - return symb; - } - - private ParsedSymbol lex() throws IOException, ActionParseException { - ParsedSymbol ret = lexer.lex(); - if (debugMode) { - System.out.println(ret); - } - return ret; - } - - private List call(boolean inFunction, boolean inMethod, List variables) throws IOException, ActionParseException { - List 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(inFunction, inMethod, true, variables)); - s = lex(); - expected(s, lexer.yyline(), SymbolType.COMMA, SymbolType.PARENT_CLOSE); - } - return ret; - } - - private FunctionActionItem function(boolean withBody, String functionName, boolean isMethod, List variables) throws IOException, ActionParseException { - GraphTargetItem ret = null; - ParsedSymbol s = null; - expectedType(SymbolType.PARENT_OPEN); - s = lex(); - List paramNames = new ArrayList<>(); - - while (s.type != SymbolType.PARENT_CLOSE) { - if (s.type != SymbolType.COMMA) { - lexer.pushback(s); - } - s = lex(); - expected(s, lexer.yyline(), SymbolType.IDENTIFIER); - paramNames.add(s.value.toString()); - s = lex(); - if (s.type == SymbolType.COLON) { - type(variables); - s = lex(); - } - - if (!s.isType(SymbolType.COMMA, SymbolType.PARENT_CLOSE)) { - expected(s, lexer.yyline(), SymbolType.COMMA, SymbolType.PARENT_CLOSE); - } - } - List body = null; - List subvariables = new ArrayList<>(); - if (withBody) { - expectedType(SymbolType.CURLY_OPEN); - - body = commands(true, isMethod, 0, subvariables); - expectedType(SymbolType.CURLY_CLOSE); - } - - return new FunctionActionItem(null, functionName, paramNames, body, constantPool, -1, subvariables); - } - - private GraphTargetItem traits(boolean isInterface, GraphTargetItem nameStr, GraphTargetItem extendsStr, List implementsStr, List variables) throws IOException, ActionParseException { - - GraphTargetItem ret = null; - /*for (int i = 0; i < nameStr.size() - 1; i++) { - List notBody = new ArrayList<>(); - List globalClassTypeStr = new ArrayList<>(); - globalClassTypeStr.add("_global"); - for (int j = 0; j <= i; j++) { - globalClassTypeStr.add(nameStr.get(j)); - } - - List val = new ArrayList<>(); - val.add(new ActionPush((Long) 0L)); - val.add(pushConst("Object")); - val.add(new ActionNewObject()); - notBody.addAll(typeToActions(globalClassTypeStr, val)); - ret.addAll(typeToActions(globalClassTypeStr, null)); - ret.add(new ActionNot()); - ret.add(new ActionNot()); - ret.add(new ActionIf(GraphTargetItem.actionsToBytes(notBody, false, SWF.DEFAULT_VERSION).length)); - ret.addAll(notBody); - } - List ifbody = new ArrayList<>(); - List globalClassTypeStr = new ArrayList<>(); - globalClassTypeStr.add("_global"); - globalClassTypeStr.addAll(nameStr);*/ - - ParsedSymbol s = null; - FunctionActionItem constr = null; - List staticFunctions = new ArrayList<>(); - List> staticVars = new ArrayList<>(); - List functions = new ArrayList<>(); - List> vars = new ArrayList<>(); - - String classNameStr = ""; - if (nameStr instanceof GetMemberActionItem) { - GetMemberActionItem mem = (GetMemberActionItem) nameStr; - if (mem.memberName instanceof VariableActionItem) { - classNameStr = ((VariableActionItem) mem.memberName).getVariableName(); - } else if (mem.memberName instanceof DirectValueActionItem) { - classNameStr = ((DirectValueActionItem) mem.memberName).toStringNoQuotes(LocalData.empty); - } - } else if (nameStr instanceof VariableActionItem) { - VariableActionItem var = (VariableActionItem) nameStr; - classNameStr = var.getVariableName(); - } - - looptrait: - while (true) { - s = lex(); - boolean isStatic = false; - while (s.isType(SymbolType.STATIC, SymbolType.PUBLIC, SymbolType.PRIVATE)) { - if (s.type == SymbolType.STATIC) { - isStatic = true; - } - s = lex(); - } - switch (s.type) { - case FUNCTION: - s = lex(); - expected(s, lexer.yyline(), SymbolType.IDENTIFIER, SymbolGroup.GLOBALFUNC); - String fname = s.value.toString(); - if (fname.equals(classNameStr)) { //constructor - constr = (function(!isInterface, "", true, variables)); - } else { - if (!isInterface) { - if (isStatic) { - FunctionActionItem ft = function(!isInterface, "", true, variables); - ft.calculatedFunctionName = pushConst(fname); - staticFunctions.add(ft); - } else { - FunctionActionItem ft = function(!isInterface, "", true, variables); - ft.calculatedFunctionName = pushConst(fname); - functions.add(ft); - } - } - } - break; - case VAR: - s = lex(); - expected(s, lexer.yyline(), SymbolType.IDENTIFIER); - String ident = s.value.toString(); - s = lex(); - if (s.type == SymbolType.COLON) { - type(variables); - s = lex(); - } - if (s.type == SymbolType.ASSIGN) { - if (isStatic) { - staticVars.add(new MyEntry(pushConst(ident), expression(false, false, true, variables))); - } else { - vars.add(new MyEntry(pushConst(ident), expression(false, false, true, variables))); - } - s = lex(); - } - if (s.type != SymbolType.SEMICOLON) { - lexer.pushback(s); - } - break; - default: - lexer.pushback(s); - break looptrait; - - } - } - - if (isInterface) { - return new InterfaceActionItem(nameStr, implementsStr); - } else { - return new ClassActionItem(nameStr, extendsStr, implementsStr, constr, functions, vars, staticFunctions, staticVars); - } - } - - private GraphTargetItem expressionCommands(ParsedSymbol s, boolean inFunction, boolean inMethod, int forinlevel, List variables) throws IOException, ActionParseException { - if (debugMode) { - System.out.println("expressionCommands:"); - } - GraphTargetItem ret = null; - switch (s.type) { - case GETVERSION: - expectedType(SymbolType.PARENT_OPEN); - ret = new GetVersionActionItem(null); - expectedType(SymbolType.PARENT_CLOSE); - break; - case MBORD: - expectedType(SymbolType.PARENT_OPEN); - ret = new MBCharToAsciiActionItem(null, expression(inFunction, inMethod, true, variables)); - expectedType(SymbolType.PARENT_CLOSE); - break; - case MBCHR: - expectedType(SymbolType.PARENT_OPEN); - ret = new MBAsciiToCharActionItem(null, expression(inFunction, inMethod, true, variables)); - expectedType(SymbolType.PARENT_CLOSE); - break; - case MBLENGTH: - expectedType(SymbolType.PARENT_OPEN); - ret = new MBStringLengthActionItem(null, expression(inFunction, inMethod, true, variables)); - expectedType(SymbolType.PARENT_CLOSE); - break; - case MBSUBSTRING: - expectedType(SymbolType.PARENT_OPEN); - GraphTargetItem val1 = (expression(inFunction, inMethod, true, variables)); - expectedType(SymbolType.COMMA); - GraphTargetItem index1 = (expression(inFunction, inMethod, true, variables)); - expectedType(SymbolType.COMMA); - GraphTargetItem len1 = (expression(inFunction, inMethod, true, variables)); - expectedType(SymbolType.PARENT_CLOSE); - ret = new MBStringExtractActionItem(null, val1, index1, len1); - break; - case SUBSTR: - expectedType(SymbolType.PARENT_OPEN); - GraphTargetItem val2 = (expression(inFunction, inMethod, true, variables)); - expectedType(SymbolType.COMMA); - GraphTargetItem index2 = (expression(inFunction, inMethod, true, variables)); - expectedType(SymbolType.COMMA); - GraphTargetItem len2 = (expression(inFunction, inMethod, true, variables)); - expectedType(SymbolType.PARENT_CLOSE); - ret = new StringExtractActionItem(null, val2, index2, len2); - break; - case LENGTH: - expectedType(SymbolType.PARENT_OPEN); - ret = new StringLengthActionItem(null, expression(inFunction, inMethod, true, variables)); - expectedType(SymbolType.PARENT_CLOSE); - break; - case RANDOM: - expectedType(SymbolType.PARENT_OPEN); - ret = new RandomNumberActionItem(null, expression(inFunction, inMethod, true, variables)); - expectedType(SymbolType.PARENT_CLOSE); - break; - case INT: - expectedType(SymbolType.PARENT_OPEN); - ret = new ToIntegerActionItem(null, expression(inFunction, inMethod, true, variables)); - expectedType(SymbolType.PARENT_CLOSE); - break; - case NUMBER_OP: - s = lex(); - if (s.type == SymbolType.DOT) { - VariableActionItem vi = new VariableActionItem(s.value.toString(), null, false); - variables.add(vi); - ret = memberOrCall(vi, inFunction, inMethod, variables); - } else { - expected(s, lexer.yyline(), SymbolType.PARENT_OPEN); - ret = new ToNumberActionItem(null, expression(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); - VariableActionItem vi2 = new VariableActionItem(sop.value.toString(), null, false); - variables.add(vi2); - ret = memberOrCall(vi2, inFunction, inMethod, variables); - } else { - expected(s, lexer.yyline(), SymbolType.PARENT_OPEN); - ret = new ToStringActionItem(null, expression(inFunction, inMethod, true, variables)); - expectedType(SymbolType.PARENT_CLOSE); - ret = memberOrCall(ret, inFunction, inMethod, variables); - } - break; - case ORD: - expectedType(SymbolType.PARENT_OPEN); - ret = new CharToAsciiActionItem(null, expression(inFunction, inMethod, true, variables)); - expectedType(SymbolType.PARENT_CLOSE); - break; - case CHR: - expectedType(SymbolType.PARENT_OPEN); - ret = new AsciiToCharActionItem(null, expression(inFunction, inMethod, true, variables)); - expectedType(SymbolType.PARENT_CLOSE); - break; - case DUPLICATEMOVIECLIP: - expectedType(SymbolType.PARENT_OPEN); - GraphTargetItem src3 = (expression(inFunction, inMethod, true, variables)); - expectedType(SymbolType.COMMA); - GraphTargetItem tar3 = (expression(inFunction, inMethod, true, variables)); - expectedType(SymbolType.COMMA); - GraphTargetItem dep3 = (expression(inFunction, inMethod, true, variables)); - expectedType(SymbolType.PARENT_CLOSE); - ret = new CloneSpriteActionItem(null, src3, tar3, dep3); - break; - case GETTIMER: - expectedType(SymbolType.PARENT_OPEN); - expectedType(SymbolType.PARENT_CLOSE); - ret = new GetTimeActionItem(null); - break; - default: - return null; - } - if (debugMode) { - System.out.println("/expressionCommands"); - } - return ret; - } - - private GraphTargetItem command(boolean inFunction, boolean inMethod, int forinlevel, boolean mustBeCommand, List variables) throws IOException, ActionParseException { - 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; - } - switch (s.type) { - case FSCOMMAND: - expectedType(SymbolType.PARENT_OPEN); - ret = new FSCommandActionItem(null, expression(inFunction, inMethod, true, variables)); - expectedType(SymbolType.PARENT_CLOSE); - break; - case CALL: - expectedType(SymbolType.PARENT_OPEN); - ret = new CallActionItem(null, (expression(inFunction, inMethod, true, variables))); - expectedType(SymbolType.PARENT_CLOSE); - break; - case LENGTH: - expectedType(SymbolType.PARENT_OPEN); - ret = new StringLengthActionItem(null, (expression(inFunction, inMethod, true, variables))); - expectedType(SymbolType.PARENT_CLOSE); - break; - case MBLENGTH: - expectedType(SymbolType.PARENT_OPEN); - ret = new MBStringLengthActionItem(null, (expression(inFunction, inMethod, true, variables))); - expectedType(SymbolType.PARENT_CLOSE); - break; - case SET: - expectedType(SymbolType.PARENT_OPEN); - GraphTargetItem name1 = (expression(inFunction, inMethod, true, variables)); - expectedType(SymbolType.COMMA); - GraphTargetItem value1 = (expression(inFunction, inMethod, true, variables)); - expectedType(SymbolType.PARENT_CLOSE); - ret = new SetVariableActionItem(null, name1, value1); - break; - case WITH: - expectedType(SymbolType.PARENT_OPEN); - GraphTargetItem wvar = (variable(inFunction, inMethod, variables)); - expectedType(SymbolType.PARENT_CLOSE); - expectedType(SymbolType.CURLY_OPEN); - List wcmd = commands(inFunction, inMethod, forinlevel, variables); - expectedType(SymbolType.CURLY_CLOSE); - ret = new WithActionItem(null, wvar, wcmd); - break; - case DELETE: - GraphTargetItem varDel = variable(inFunction, inMethod, variables); - if (varDel instanceof GetMemberActionItem) { - GetMemberActionItem gm = (GetMemberActionItem) varDel; - ret = new DeleteActionItem(null, gm.object, gm.memberName); - } else if (varDel instanceof VariableActionItem) { - variables.remove(varDel); - ret = new DeleteActionItem(null, null, pushConst(((VariableActionItem) varDel).getVariableName())); - } else { - throw new ActionParseException("Not a property", lexer.yyline()); - } - break; - case TRACE: - expectedType(SymbolType.PARENT_OPEN); - ret = new TraceActionItem(null, (expression(inFunction, inMethod, true, variables))); - expectedType(SymbolType.PARENT_CLOSE); - break; - - case GETURL: - expectedType(SymbolType.PARENT_OPEN); - GraphTargetItem url = (expression(inFunction, inMethod, true, variables)); - s = lex(); - expected(s, lexer.yyline(), SymbolType.PARENT_CLOSE, SymbolType.COMMA); - int getuMethod = 1; - GraphTargetItem target = null; - if (s.type == SymbolType.COMMA) { - target = (expression(inFunction, inMethod, true, variables)); - s = lex(); - if (s.type == SymbolType.COMMA) { - s = lex(); - expected(s, lexer.yyline(), SymbolType.STRING); - if (s.value.equals("GET")) { - getuMethod = 1; - } else if (s.value.equals("POST")) { - getuMethod = 2; - } else { - throw new ActionParseException("Invalid method, \"GET\" or \"POST\" expected.", lexer.yyline()); - } - } else { - lexer.pushback(s); - } - } else { - lexer.pushback(s); - target = new DirectValueActionItem(null, 0, "", new ArrayList()); - } - expectedType(SymbolType.PARENT_CLOSE); - ret = new GetURL2ActionItem(null, url, target, getuMethod); - break; - case GOTOANDSTOP: - expectedType(SymbolType.PARENT_OPEN); - GraphTargetItem gtsFrame = expression(inFunction, inMethod, true, variables); - s = lex(); - if (s.type == SymbolType.COMMA) { //Handle scene? - s = lex(); - gtsFrame = expression(inFunction, inMethod, true, variables); - } else { - lexer.pushback(s); - } - ret = new GotoFrame2ActionItem(null, gtsFrame, false, false, 0); - expectedType(SymbolType.PARENT_CLOSE); - break; - case NEXTFRAME: - expectedType(SymbolType.PARENT_OPEN); - expectedType(SymbolType.PARENT_CLOSE); - ret = new NextFrameActionItem(null); - break; - case PLAY: - expectedType(SymbolType.PARENT_OPEN); - expectedType(SymbolType.PARENT_CLOSE); - ret = new PlayActionItem(null); - break; - case PREVFRAME: - expectedType(SymbolType.PARENT_OPEN); - expectedType(SymbolType.PARENT_CLOSE); - ret = new PrevFrameActionItem(null); - break; - case TELLTARGET: - expectedType(SymbolType.PARENT_OPEN); - GraphTargetItem tellTarget = expression(inFunction, inMethod, true, variables); - expectedType(SymbolType.PARENT_CLOSE); - expectedType(SymbolType.CURLY_OPEN); - List tellcmds = commands(inFunction, inMethod, forinlevel, variables); - expectedType(SymbolType.CURLY_CLOSE); - ret = new TellTargetActionItem(null, tellTarget, tellcmds); - break; - case STOP: - expectedType(SymbolType.PARENT_OPEN); - expectedType(SymbolType.PARENT_CLOSE); - ret = new StopActionItem(null); - break; - case STOPALLSOUNDS: - expectedType(SymbolType.PARENT_OPEN); - expectedType(SymbolType.PARENT_CLOSE); - ret = new StopAllSoundsActionItem(null); - break; - case TOGGLEHIGHQUALITY: - expectedType(SymbolType.PARENT_OPEN); - expectedType(SymbolType.PARENT_CLOSE); - ret = new ToggleHighQualityActionItem(null); - break; - - case STOPDRAG: - expectedType(SymbolType.PARENT_OPEN); - expectedType(SymbolType.PARENT_CLOSE); - ret = new StopDragActionItem(null); - break; - - case TARGETPATH: - expectedType(SymbolType.PARENT_OPEN); - ret = new TargetPathActionItem(null, (expression(inFunction, inMethod, true, variables))); - expectedType(SymbolType.PARENT_CLOSE); - break; - - case UNLOADMOVIE: - case UNLOADMOVIENUM: - SymbolType unloadType = s.type; - expectedType(SymbolType.PARENT_OPEN); - GraphTargetItem unTargetOrNum = expression(inFunction, inMethod, true, variables); - expectedType(SymbolType.PARENT_CLOSE); - if (unloadType == SymbolType.UNLOADMOVIE) { - ret = new UnLoadMovieActionItem(null, unTargetOrNum); - } - if (unloadType == SymbolType.UNLOADMOVIENUM) { - ret = new UnLoadMovieNumActionItem(null, unTargetOrNum); - } - break; - case PRINT: - case PRINTASBITMAP: - case PRINTASBITMAPNUM: - case PRINTNUM: - SymbolType printType = s.type; - expectedType(SymbolType.PARENT_OPEN); - GraphTargetItem printTarget = (expression(inFunction, inMethod, true, variables)); - expectedType(SymbolType.COMMA); - GraphTargetItem printBBox = (expression(inFunction, inMethod, true, variables)); - expectedType(SymbolType.PARENT_CLOSE); - - switch (printType) { - case PRINT: - ret = new PrintActionItem(null, printTarget, printBBox); - break; - case PRINTNUM: - ret = new PrintNumActionItem(null, printTarget, printBBox); - break; - case PRINTASBITMAP: - ret = new PrintAsBitmapActionItem(null, printTarget, printBBox); - break; - case PRINTASBITMAPNUM: - ret = new PrintAsBitmapNumActionItem(null, printTarget, printBBox); - break; - } - break; - case LOADVARIABLES: - case LOADMOVIE: - case LOADVARIABLESNUM: - case LOADMOVIENUM: - SymbolType loadType = s.type; - expectedType(SymbolType.PARENT_OPEN); - GraphTargetItem url2 = (expression(inFunction, inMethod, true, variables)); - expectedType(SymbolType.COMMA); - GraphTargetItem targetOrNum = (expression(inFunction, inMethod, true, variables)); - - s = lex(); - expected(s, lexer.yyline(), SymbolType.PARENT_CLOSE, SymbolType.COMMA); - int lvmethod = 1; - if (s.type == SymbolType.COMMA) { - s = lex(); - expected(s, lexer.yyline(), SymbolType.STRING); - if (s.value.equals("POST")) { - lvmethod = 2; - } else if (s.value.equals("GET")) { - lvmethod = 1; - } else { - throw new ActionParseException("Invalid method, \"GET\" or \"POST\" expected.", lexer.yyline()); - } - } else { - lexer.pushback(s); - } - expectedType(SymbolType.PARENT_CLOSE); - switch (loadType) { - case LOADVARIABLES: - ret = new LoadVariablesActionItem(null, url2, targetOrNum, lvmethod); - break; - case LOADMOVIE: - ret = new LoadMovieActionItem(null, url2, targetOrNum, lvmethod); - break; - case LOADVARIABLESNUM: - ret = new LoadVariablesNumActionItem(null, url2, targetOrNum, lvmethod); - break; - case LOADMOVIENUM: - ret = new LoadMovieNumActionItem(null, url2, targetOrNum, lvmethod); - break; - } - break; - case GOTOANDPLAY: - expectedType(SymbolType.PARENT_OPEN); - GraphTargetItem gtpFrame = expression(inFunction, inMethod, true, variables); - s = lex(); - if (s.type == SymbolType.COMMA) { //Handle scene? - s = lex(); - gtpFrame = expression(inFunction, inMethod, true, variables); - } else { - lexer.pushback(s); - } - ret = new GotoFrame2ActionItem(null, gtpFrame, false, true, 0); - expectedType(SymbolType.PARENT_CLOSE); - break; - - case REMOVEMOVIECLIP: - expectedType(SymbolType.PARENT_OPEN); - ret = new RemoveSpriteActionItem(null, (expression(inFunction, inMethod, true, variables))); - expectedType(SymbolType.PARENT_CLOSE); - break; - case STARTDRAG: - expectedType(SymbolType.PARENT_OPEN); - GraphTargetItem dragTarget = (expression(inFunction, inMethod, true, variables)); - GraphTargetItem lockCenter = null; - GraphTargetItem constrain = null; - GraphTargetItem x1 = null; - GraphTargetItem y1 = null; - GraphTargetItem x2 = null; - GraphTargetItem y2 = null; - s = lex(); - if (s.type == SymbolType.COMMA) { - lockCenter = (expression(inFunction, inMethod, true, variables)); - s = lex(); - if (s.type == SymbolType.COMMA) { - constrain = new DirectValueActionItem(null, 0, 1L, new ArrayList()); - x1 = (expression(inFunction, inMethod, true, variables)); - s = lex(); - if (s.type == SymbolType.COMMA) { - y1 = (expression(inFunction, inMethod, true, variables)); - s = lex(); - if (s.type == SymbolType.COMMA) { - x2 = (expression(inFunction, inMethod, true, variables)); - s = lex(); - if (s.type == SymbolType.COMMA) { - y2 = (expression(inFunction, inMethod, true, variables)); - } else { - lexer.pushback(s); - y2 = new DirectValueActionItem(null, 0, 0L, new ArrayList()); - } - } else { - lexer.pushback(s); - x2 = new DirectValueActionItem(null, 0, 0L, new ArrayList()); - y2 = new DirectValueActionItem(null, 0, 0L, new ArrayList()); - } - } else { - lexer.pushback(s); - x2 = new DirectValueActionItem(null, 0, 0L, new ArrayList()); - y2 = new DirectValueActionItem(null, 0, 0L, new ArrayList()); - y1 = new DirectValueActionItem(null, 0, 0L, new ArrayList()); - - } - } else { - lexer.pushback(s); - constrain = new DirectValueActionItem(null, 0, 0L, new ArrayList()); - //ret.add(new ActionPush(Boolean.FALSE)); - } - } else { - lockCenter = new DirectValueActionItem(null, 0, 0L, new ArrayList()); - constrain = new DirectValueActionItem(null, 0, 0L, new ArrayList()); - lexer.pushback(s); - } - expectedType(SymbolType.PARENT_CLOSE); - ret = new StartDragActionItem(null, dragTarget, lockCenter, constrain, x1, y1, x2, y2); - break; - - case IFFRAMELOADED: - expectedType(SymbolType.PARENT_OPEN); - GraphTargetItem iflExpr = (expression(inFunction, inMethod, true, variables)); - expectedType(SymbolType.PARENT_CLOSE); - expectedType(SymbolType.CURLY_OPEN); - List iflComs = commands(inFunction, inMethod, forinlevel, variables); - expectedType(SymbolType.CURLY_CLOSE); - ret = new IfFrameLoadedActionItem(iflExpr, iflComs, null); - break; - case CLASS: - GraphTargetItem classTypeStr = type(variables); - s = lex(); - GraphTargetItem extendsTypeStr = null; - if (s.type == SymbolType.EXTENDS) { - extendsTypeStr = type(variables); - s = lex(); - } - List implementsTypeStrs = new ArrayList<>(); - if (s.type == SymbolType.IMPLEMENTS) { - do { - GraphTargetItem implementsTypeStr = type(variables); - implementsTypeStrs.add(implementsTypeStr); - s = lex(); - } while (s.type == SymbolType.COMMA); - } - expected(s, lexer.yyline(), SymbolType.CURLY_OPEN); - ret = (traits(false, classTypeStr, extendsTypeStr, implementsTypeStrs, variables)); - expectedType(SymbolType.CURLY_CLOSE); - break; - case INTERFACE: - GraphTargetItem interfaceTypeStr = type(variables); - s = lex(); - List intExtendsTypeStrs = new ArrayList<>(); - - if (s.type == SymbolType.EXTENDS) { - do { - GraphTargetItem intExtendsTypeStr = type(variables); - intExtendsTypeStrs.add(intExtendsTypeStr); - s = lex(); - } while (s.type == SymbolType.COMMA); - } - expected(s, lexer.yyline(), SymbolType.CURLY_OPEN); - ret = (traits(true, interfaceTypeStr, null, intExtendsTypeStrs, variables)); - expectedType(SymbolType.CURLY_CLOSE); - break; - case FUNCTION: - s = lexer.lex(); - expected(s, lexer.yyline(), SymbolType.IDENTIFIER, SymbolGroup.GLOBALFUNC); - ret = (function(true, s.value.toString(), false, variables)); - break; - case VAR: - s = lex(); - expected(s, lexer.yyline(), SymbolType.IDENTIFIER); - String varIdentifier = s.value.toString(); - s = lex(); - if (s.type == SymbolType.COLON) { - type(variables); - s = lex(); - //TODO: handle value type - } - - if (s.type == SymbolType.ASSIGN) { - GraphTargetItem varval = (expression(inFunction, inMethod, true, variables)); - ret = new VariableActionItem(varIdentifier, varval, true); - variables.add((VariableActionItem) ret); - } else { - ret = new VariableActionItem(varIdentifier, null, true); - variables.add((VariableActionItem) ret); - lexer.pushback(s); - } - break; - case CURLY_OPEN: - ret = new BlockItem(null, commands(inFunction, inMethod, forinlevel, variables)); - expectedType(SymbolType.CURLY_CLOSE); - break; - case INCREMENT: //preincrement - case DECREMENT: //predecrement - GraphTargetItem varincdec = variable(inFunction, inMethod, variables); - if (s.type == SymbolType.INCREMENT) { - ret = new PreIncrementActionItem(null, varincdec); - } else if (s.type == SymbolType.DECREMENT) { - ret = new PreDecrementActionItem(null, varincdec); - } - break; - case SUPER: //constructor call - ParsedSymbol ss2 = lex(); - if (ss2.type == SymbolType.PARENT_OPEN) { - List args = call(inFunction, inMethod, variables); - VariableActionItem supItem = new VariableActionItem(s.value.toString(), null, false); - variables.add(supItem); - ret = new CallMethodActionItem(null, supItem, new DirectValueActionItem(null, 0, new Undefined(), constantPool), 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(inFunction, inMethod, true, variables)); - expectedType(SymbolType.PARENT_CLOSE); - GraphTargetItem onTrue = command(inFunction, inMethod, forinlevel, true, variables); - List onTrueList = new ArrayList<>(); - onTrueList.add(onTrue); - s = lex(); - List onFalseList = null; - if (s.type == SymbolType.ELSE) { - onFalseList = new ArrayList<>(); - onFalseList.add(command(inFunction, inMethod, forinlevel, true, variables)); - } else { - lexer.pushback(s); - } - ret = new IfItem(null, ifExpr, onTrueList, onFalseList); - break; - case WHILE: - expectedType(SymbolType.PARENT_OPEN); - List whileExpr = new ArrayList<>(); - whileExpr.add(commaExpression(inFunction, inMethod, forinlevel, variables)); - expectedType(SymbolType.PARENT_CLOSE); - List whileBody = new ArrayList<>(); - whileBody.add(command(inFunction, inMethod, forinlevel, true, variables)); - ret = new WhileItem(null, null, whileExpr, whileBody); - break; - case DO: - List doBody = new ArrayList<>(); - doBody.add(command(inFunction, inMethod, forinlevel, true, variables)); - expectedType(SymbolType.WHILE); - expectedType(SymbolType.PARENT_OPEN); - List doExpr = new ArrayList<>(); - doExpr.add(commaExpression(inFunction, inMethod, forinlevel, variables)); - expectedType(SymbolType.PARENT_CLOSE); - ret = new DoWhileItem(null, null, doBody, doExpr); - break; - case FOR: - expectedType(SymbolType.PARENT_OPEN); - s = lex(); - boolean forin = false; - GraphTargetItem collection = null; - String objIdent = null; - VariableActionItem item = null; - int innerExprReg = 0; - boolean define = false; - if (s.type == SymbolType.VAR || s.type == SymbolType.IDENTIFIER) { - ParsedSymbol s2 = null; - ParsedSymbol ssel = s; - if (s.type == SymbolType.VAR) { - s2 = lex(); - ssel = s2; - define = true; - } - - if (ssel.type == SymbolType.IDENTIFIER) { - objIdent = ssel.value.toString(); - - ParsedSymbol s3 = lex(); - if (s3.type == SymbolType.IN) { - if (inFunction) { - /*for (int i = 0; i < 256; i++) { - if (!registerVars.containsValue(i)) { - registerVars.put(objIdent, i); - innerExprReg = i; - break; - } - }*/ - } - - item = new VariableActionItem(objIdent, null, define); - - item.setStoreValue(new GraphTargetItem() { - - @Override - public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { - return writer; - } - - @Override - public boolean hasReturnValue() { - return false; - } - - @Override - public GraphTargetItem returnType() { - return TypeItem.UNBOUNDED; - } - - //toSource is Empty - }); - - variables.add(item); - - collection = expression(inFunction, inMethod, true, variables); - forin = true; - } else { - lexer.pushback(s3); - if (s2 != null) { - lexer.pushback(s2); - } - lexer.pushback(s); - } - } else { - if (s2 != null) { - lexer.pushback(s2); - } - lexer.pushback(s); - } - } else { - lexer.pushback(s); - } - List forFinalCommands = new ArrayList<>(); - GraphTargetItem forExpr = null; - List forFirstCommands = new ArrayList<>(); - if (!forin) { - GraphTargetItem fc = command(inFunction, inMethod, forinlevel, true, variables); - if (fc != null) { //can be empty command - forFirstCommands.add(fc); - } - forExpr = (expression(inFunction, inMethod, true, variables)); - expectedType(SymbolType.SEMICOLON); - forFinalCommands.add(command(inFunction, inMethod, forinlevel, true, variables)); - } - expectedType(SymbolType.PARENT_CLOSE); - List forBody = new ArrayList<>(); - forBody.add(command(inFunction, inMethod, forin ? forinlevel + 1 : forinlevel, true, variables)); - if (forin) { - ret = new ForInActionItem(null, null, item, collection, forBody); - } else { - ret = new ForItem(null, null, forFirstCommands, forExpr, forFinalCommands, forBody); - } - break; - case SWITCH: - expectedType(SymbolType.PARENT_OPEN); - GraphTargetItem switchExpr = expression(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> caseIfs = new ArrayList<>(); - List> caseCmds = new ArrayList<>(); - List caseExprsAll = new ArrayList<>(); - List valueMapping = new ArrayList<>(); - int pos = 0; - while (s.type == SymbolType.CASE) { - List caseExprs = new ArrayList<>(); - while (s.type == SymbolType.CASE) { - GraphTargetItem curCaseExpr = expression(inFunction, inMethod, true, variables); - caseExprs.add(curCaseExpr); - expectedType(SymbolType.COLON); - s = lex(); - caseExprsAll.add(curCaseExpr); - valueMapping.add(pos); - } - pos++; - lexer.pushback(s); - List caseCmd = commands(inFunction, inMethod, forinlevel, variables); - caseCmds.add(caseCmd); - s = lex(); - } - List defCmd = new ArrayList<>(); - if (s.type == SymbolType.DEFAULT) { - expectedType(SymbolType.COLON); - defCmd = commands(inFunction, inMethod, forinlevel, variables); - s = lexer.lex(); - } - expected(s, lexer.yyline(), SymbolType.CURLY_CLOSE); - ret = new SwitchItem(null, null, switchExpr, caseExprsAll, caseCmds, defCmd, valueMapping); - break; - case BREAK: - ret = new BreakItem(null, 0); //? There is no more than 1 level continue/break in AS1/2 - break; - case CONTINUE: - ret = new ContinueItem(null, 0); //? There is no more than 1 level continue/break in AS1/2 - break; - case RETURN: - GraphTargetItem retexpr = expression(inFunction, inMethod, true, variables); - if (retexpr == null) { - retexpr = new DirectValueActionItem(null, 0, new Undefined(), new ArrayList()); - } - ret = new ReturnActionItem(null, retexpr); - break; - case TRY: - List tryCommands = new ArrayList<>(); - tryCommands.add(command(inFunction, inMethod, forinlevel, true, variables)); - s = lex(); - boolean found = false; - List> catchCommands = null; - List catchExceptions = new ArrayList<>(); - if (s.type == SymbolType.CATCH) { - expectedType(SymbolType.PARENT_OPEN); - s = lex(); - expected(s, lexer.yyline(), SymbolType.IDENTIFIER, SymbolType.STRING); - catchExceptions.add(pushConst((String) s.value)); - expectedType(SymbolType.PARENT_CLOSE); - catchCommands = new ArrayList<>(); - List cc = new ArrayList<>(); - cc.add(command(inFunction, inMethod, forinlevel, true, variables)); - catchCommands.add(cc); - s = lex(); - found = true; - } - List finallyCommands = null; - if (s.type == SymbolType.FINALLY) { - finallyCommands = new ArrayList<>(); - finallyCommands.add(command(inFunction, inMethod, forinlevel, true, variables)); - found = true; - s = lex(); - } - if (!found) { - expected(s, lexer.yyline(), SymbolType.CATCH, SymbolType.FINALLY); - } - lexer.pushback(s); - ret = new TryActionItem(tryCommands, catchExceptions, catchCommands, finallyCommands); - break; - case THROW: - ret = new ThrowActionItem(null, expression(inFunction, inMethod, true, variables)); - break; - case SEMICOLON: //empty command - if (debugMode) { - System.out.println("/command"); - } - return null; - default: - GraphTargetItem valcmd = expressionCommands(s, inFunction, inMethod, forinlevel, variables); - if (valcmd != null) { - ret = valcmd; - break; - } - lexer.pushback(s); - ret = expression(inFunction, inMethod, true, variables); - } - if (debugMode) { - System.out.println("/command"); - } - lexer.removeListener(buf); - if (ret == null) { //can be popped expression - buf.pushAllBack(lexer); - ret = expression(inFunction, inMethod, true, variables); - } - s = lex(); - if ((s != null) && (s.type != SymbolType.SEMICOLON)) { - lexer.pushback(s); - } - - return ret; - - } - - private GraphTargetItem expression(boolean inFunction, boolean inMethod, boolean allowRemainder, List variables) throws IOException, ActionParseException { - if (debugMode) { - System.out.println("expression:"); - } - GraphTargetItem prim = expressionPrimary(false, inFunction, inMethod, allowRemainder, variables); - if (prim == null) { - return null; - } - GraphTargetItem ret = expression1(prim, GraphTargetItem.NOPRECEDENCE, inFunction, inMethod, allowRemainder, variables); - if (debugMode) { - System.out.println("/expression"); - } - return ret; - } - - private ParsedSymbol peekLex() throws IOException, ActionParseException { - ParsedSymbol lookahead = lex(); - lexer.pushback(lookahead); - return lookahead; - } - - private static final String[] operatorIdentifiers = new String[]{"add", "eq", "ne", "lt", "ge", "gt", "le"}; - - private boolean isBinaryOperator(ParsedSymbol s) { - if (s.type == SymbolType.IDENTIFIER && Arrays.asList(operatorIdentifiers).contains(s.value.toString())) { - return true; - } - return s.type.isBinary(); - } - - private int getSymbPrecedence(ParsedSymbol s) { - if (s.type == SymbolType.IDENTIFIER && Arrays.asList(operatorIdentifiers).contains(s.value.toString())) { - switch (s.value.toString()) { - case "add": - return GraphTargetItem.PRECEDENCE_ADDITIVE; - case "eq": - case "ne": - return GraphTargetItem.PRECEDENCE_EQUALITY; - case "lt": - case "ge": - case "gt": - case "le": - return GraphTargetItem.PRECEDENCE_RELATIONAL; - } - } - return s.type.getPrecedence(); - } - - private GraphTargetItem expression1(GraphTargetItem lhs, int min_precedence, boolean inFunction, boolean inMethod, boolean allowRemainder, List variables) throws IOException, ActionParseException { - ParsedSymbol op; - GraphTargetItem rhs; - GraphTargetItem mhs = null; - ParsedSymbol lookahead = peekLex(); - if (debugMode) { - System.out.println("expression1:"); - } - //Note: algorithm from http://en.wikipedia.org/wiki/Operator-precedence_parser - //with relation operators reversed as we have precedence in reverse order - while (isBinaryOperator(lookahead) && getSymbPrecedence(lookahead) <= /* >= 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(inFunction, inMethod, allowRemainder, variables); - expectedType(SymbolType.COLON); - if (debugMode) { - System.out.println("/ternar-middle"); - } - } - - rhs = expressionPrimary(allowRemainder, inFunction, inMethod, allowRemainder, variables); - if (rhs == null) { - lexer.pushback(op); - break; - } - - lookahead = peekLex(); - while ((isBinaryOperator(lookahead) && getSymbPrecedence(lookahead) < /* > on wiki */ getSymbPrecedence(op)) - || (lookahead.type.isRightAssociative() && getSymbPrecedence(lookahead) == getSymbPrecedence(op))) { - rhs = expression1(rhs, getSymbPrecedence(lookahead), inFunction, inMethod, allowRemainder, variables); - lookahead = peekLex(); - } - - switch (op.type) { - case TERNAR: - lhs = new TernarOpItem(null, lhs, mhs, rhs); - break; - case SHIFT_LEFT: - lhs = new LShiftActionItem(null, lhs, rhs); - break; - case SHIFT_RIGHT: - lhs = new RShiftActionItem(null, lhs, rhs); - break; - case USHIFT_RIGHT: - lhs = new URShiftActionItem(null, lhs, rhs); - break; - case BITAND: - lhs = new BitAndActionItem(null, lhs, rhs); - break; - case BITOR: - lhs = new BitOrActionItem(null, lhs, rhs); - break; - case DIVIDE: - lhs = new DivideActionItem(null, lhs, rhs); - break; - case MODULO: - lhs = new ModuloActionItem(null, lhs, rhs); - break; - case EQUALS: - lhs = new EqActionItem(null, lhs, rhs, true/*FIXME SWF version?*/); - break; - case STRICT_EQUALS: - lhs = new StrictEqActionItem(null, lhs, rhs); - break; - case NOT_EQUAL: - lhs = new NeqActionItem(null, lhs, rhs, true/*FIXME SWF version?*/); - break; - case STRICT_NOT_EQUAL: - lhs = new StrictNeqActionItem(null, lhs, rhs); - break; - case LOWER_THAN: - lhs = new LtActionItem(null, lhs, rhs, true/*FIXME SWF version?*/); - break; - case LOWER_EQUAL: - lhs = new LeActionItem(null, lhs, rhs); - break; - case GREATER_THAN: - lhs = new GtActionItem(null, lhs, rhs); - break; - case GREATER_EQUAL: - lhs = new GeActionItem(null, lhs, rhs, true/*FIXME SWF version?*/); - break; - case AND: - lhs = new AndItem(null, lhs, rhs); - break; - case OR: - lhs = new OrItem(null, lhs, rhs); - break; - case FULLAND: - lhs = new AndActionItem(null, lhs, rhs); - break; - case FULLOR: - lhs = new OrActionItem(null, lhs, rhs); - break; - case MINUS: - lhs = new SubtractActionItem(null, lhs, rhs); - break; - case MULTIPLY: - lhs = new MultiplyActionItem(null, lhs, rhs); - break; - case PLUS: - lhs = new AddActionItem(null, lhs, rhs, true); - break; - case XOR: - lhs = new BitXorActionItem(null, lhs, rhs); - break; - case AS: - - break; - case INSTANCEOF: - lhs = new InstanceOfActionItem(null, lhs, rhs); - break; - case IS: - - 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 (lookahead.type) { - case ASSIGN: - //assigned = assigned; - break; - case ASSIGN_BITAND: - assigned = new BitAndActionItem(null, lhs, assigned); - break; - case ASSIGN_BITOR: - assigned = new BitOrActionItem(null, lhs, assigned); - break; - case ASSIGN_DIVIDE: - assigned = new DivideActionItem(null, lhs, assigned); - break; - case ASSIGN_MINUS: - assigned = new SubtractActionItem(null, lhs, assigned); - break; - case ASSIGN_MODULO: - assigned = new ModuloActionItem(null, lhs, assigned); - break; - case ASSIGN_MULTIPLY: - assigned = new MultiplyActionItem(null, lhs, assigned); - break; - case ASSIGN_PLUS: - assigned = new AddActionItem(null, lhs, assigned, true/*TODO:SWF version?*/); - break; - case ASSIGN_SHIFT_LEFT: - assigned = new LShiftActionItem(null, lhs, assigned); - break; - case ASSIGN_SHIFT_RIGHT: - assigned = new RShiftActionItem(null, lhs, assigned); - break; - case ASSIGN_USHIFT_RIGHT: - assigned = new URShiftActionItem(null, lhs, assigned); - break; - case ASSIGN_XOR: - assigned = new BitXorActionItem(null, lhs, assigned); - break; - } - if (lhs instanceof VariableActionItem) { - ((VariableActionItem) lhs).setStoreValue(assigned); - ((VariableActionItem) lhs).setDefinition(false); - lhs = lhs; - } else if (lhs instanceof GetMemberActionItem) { - lhs = new SetMemberActionItem(null, ((GetMemberActionItem) lhs).object, ((GetMemberActionItem) lhs).memberName, assigned); - } else { - throw new ActionParseException("Invalid assignment", lexer.yyline()); - } - break; - case IDENTIFIER: - switch (lookahead.value.toString()) { - case "add": - lhs = new StringAddActionItem(null, lhs, rhs); - break; - case "eq": - lhs = new StringEqActionItem(null, lhs, rhs); - break; - case "ne": - lhs = new StringNeActionItem(null, lhs, rhs); - break; - case "lt": - lhs = new StringLtActionItem(null, lhs, rhs); - break; - case "ge": - lhs = new StringGeActionItem(null, lhs, rhs); - break; - case "gt": - lhs = new StringGtActionItem(null, lhs, rhs); - break; - case "le": - lhs = new StringLeActionItem(null, lhs, rhs); - break; - } - break; - } - } - - switch (lookahead.type) { - case INCREMENT: //postincrement - lex(); - if (!(lhs instanceof VariableActionItem) && !(lhs instanceof GetMemberActionItem)) { - throw new ActionParseException("Invalid assignment", lexer.yyline()); - } - lhs = new PostIncrementActionItem(null, lhs); - break; - case DECREMENT: //postdecrement - lex(); - if (!(lhs instanceof VariableActionItem) && !(lhs instanceof GetMemberActionItem)) { - throw new ActionParseException("Invalid assignment", lexer.yyline()); - } - lhs = new PostDecrementActionItem(null, lhs); - break; - case DOT: //member - case BRACKET_OPEN: //member - case PARENT_OPEN: //function call - lhs = memberOrCall(lhs, inFunction, inMethod, variables); - break; - default: - if (lhs instanceof ParenthesisItem) { - if (isType(((ParenthesisItem) lhs).value)) { - GraphTargetItem expr2 = expression(inFunction, inMethod, true, variables); - if (expr2 != null) { - lhs = new CastOpActionItem(null, ((ParenthesisItem) lhs).value, expr2); - } - } - } - } - if (debugMode) { - System.out.println("/expression1"); - } - return lhs; - } - - private boolean isType(GraphTargetItem item) { - if (item == null) { - return false; - } - while (item instanceof GetMemberActionItem) { - item = ((GetMemberActionItem) item).object; - } - if (item instanceof VariableActionItem) { - return true; - } - return false; - } - - private int brackets(List ret, boolean inFunction, boolean inMethod, List variables) throws IOException, ActionParseException { - 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(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(boolean inFunction, boolean inMethod, int forInLevel, List variables) throws IOException, ActionParseException { - GraphTargetItem cmd = null; - List expr = new ArrayList<>(); - ParsedSymbol s; - do { - cmd = command(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(inFunction, inMethod, true, variables)); - } else { - if (!cmd.hasReturnValue()) { - throw new ActionParseException("Expression expected", lexer.yyline()); - } - } - return new CommaExpressionItem(null, expr); - } - - private GraphTargetItem expressionPrimary(boolean allowEmpty, boolean inFunction, boolean inMethod, boolean allowRemainder, List variables) throws IOException, ActionParseException { - if (debugMode) { - System.out.println("primary:"); - } - GraphTargetItem ret = null; - ParsedSymbol s = lex(); - - switch (s.type) { - case NEGATE: - versionRequired(s, 5); - ret = expression(inFunction, inMethod, false, variables); - ret = new BitXorActionItem(null, ret, new DirectValueActionItem(4.294967295E9)); - - break; - case MINUS: - s = lex(); - if (s.isType(SymbolType.DOUBLE)) { - ret = new DirectValueActionItem(null, 0, -(double) (Double) s.value, new ArrayList()); - - } else if (s.isType(SymbolType.INTEGER)) { - ret = new DirectValueActionItem(null, 0, -(long) (Long) s.value, new ArrayList()); - - } else { - lexer.pushback(s); - GraphTargetItem num = expression(inFunction, inMethod, true, variables); - if ((num instanceof DirectValueActionItem) - && (((DirectValueActionItem) num).value instanceof Long)) { - ((DirectValueActionItem) num).value = -(Long) ((DirectValueActionItem) num).value; - ret = num; - } else if ((num instanceof DirectValueActionItem) - && (((DirectValueActionItem) num).value instanceof Double)) { - Double d = (Double) ((DirectValueActionItem) num).value; - if (d.isInfinite()) { - ((DirectValueActionItem) num).value = Double.NEGATIVE_INFINITY; - } else { - ((DirectValueActionItem) num).value = -d; - } - ret = (num); - } else if ((num instanceof DirectValueActionItem) - && (((DirectValueActionItem) num).value instanceof Float)) { - ((DirectValueActionItem) num).value = -(Float) ((DirectValueActionItem) num).value; - ret = (num); - } else {; - ret = (new SubtractActionItem(null, new DirectValueActionItem(null, 0, (Long) 0L, new ArrayList()), num)); - } - } - break; - case TYPEOF: - ret = new TypeOfActionItem(null, expression(inFunction, inMethod, false, variables)); - - break; - case TRUE: - ret = new DirectValueActionItem(null, 0, Boolean.TRUE, new ArrayList()); - - break; - case NULL: - ret = new DirectValueActionItem(null, 0, new Null(), new ArrayList()); - - break; - case UNDEFINED: - ret = new DirectValueActionItem(null, 0, new Undefined(), new ArrayList()); - break; - case FALSE: - ret = new DirectValueActionItem(null, 0, Boolean.FALSE, new ArrayList()); - - break; - case CURLY_OPEN: //Object literal - s = lex(); - List objectNames = new ArrayList<>(); - List objectValues = new ArrayList<>(); - while (s.type != SymbolType.CURLY_CLOSE) { - if (s.type != SymbolType.COMMA) { - lexer.pushback(s); - } - s = lex(); - expected(s, lexer.yyline(), SymbolType.IDENTIFIER); - objectNames.add(0, pushConst((String) s.value)); - expectedType(SymbolType.COLON); - objectValues.add(0, expression(inFunction, inMethod, true, variables)); - s = lex(); - if (!s.isType(SymbolType.COMMA, SymbolType.CURLY_CLOSE)) { - expected(s, lexer.yyline(), SymbolType.COMMA, SymbolType.CURLY_CLOSE); - } - } - ret = new InitObjectActionItem(null, objectNames, objectValues); - break; - case BRACKET_OPEN: //Array literal or just brackets - lexer.pushback(s); - List inBrackets = new ArrayList<>(); - int arrCnt = brackets(inBrackets, inFunction, inMethod, variables); - ret = new InitArrayActionItem(null, inBrackets); - break; - case FUNCTION: - s = lexer.lex(); - String fname = ""; - if (s.isType(SymbolType.IDENTIFIER, SymbolGroup.GLOBALFUNC)) { - fname = s.value.toString(); - } else { - lexer.pushback(s); - } - ret = function(true, fname, false, variables); - break; - case STRING: - ret = pushConst(s.value.toString()); - ret = memberOrCall(ret, inFunction, inMethod, variables); - - break; - case NEWLINE: - ret = new DirectValueActionItem(null, 0, "\r", new ArrayList()); - - break; - case NAN: - ret = new DirectValueActionItem(null, 0, Double.NaN, new ArrayList()); - - break; - case INFINITY: - ret = new DirectValueActionItem(null, 0, Double.POSITIVE_INFINITY, new ArrayList()); - - break; - case INTEGER: - case DOUBLE: - ret = new DirectValueActionItem(null, 0, s.value, new ArrayList()); - - break; - case DELETE: - GraphTargetItem varDel = variable(inFunction, inMethod, variables); - if (varDel instanceof GetMemberActionItem) { - GetMemberActionItem gm = (GetMemberActionItem) varDel; - ret = new DeleteActionItem(null, gm.object, gm.memberName); - } else { - throw new ActionParseException("Not a property", lexer.yyline()); - } - break; - case INCREMENT: - case DECREMENT: //preincrement - GraphTargetItem prevar = variable(inFunction, inMethod, variables); - if (s.type == SymbolType.INCREMENT) { - ret = new PreIncrementActionItem(null, prevar); - } - if (s.type == SymbolType.DECREMENT) { - ret = new PreDecrementActionItem(null, prevar); - } - - break; - case NOT: - ret = new NotItem(null, expression(inFunction, inMethod, false, variables)); - - break; - case PARENT_OPEN: - ret = new ParenthesisItem(null, expression(inFunction, inMethod, true, variables)); - expectedType(SymbolType.PARENT_CLOSE); - ret = memberOrCall(ret, inFunction, inMethod, variables); - - break; - case NEW: - GraphTargetItem newvar = variable(inFunction, inMethod, variables); - expectedType(SymbolType.PARENT_OPEN); - if (newvar instanceof GetMemberActionItem) { - GetMemberActionItem mem = (GetMemberActionItem) newvar; - ret = new NewMethodActionItem(null, mem.object, mem.memberName, call(inFunction, inMethod, variables)); - } else if (newvar instanceof VariableActionItem) { - ret = new NewObjectActionItem(null, new DirectValueActionItem(((VariableActionItem) newvar).getVariableName()), call(inFunction, inMethod, variables)); - } else { - throw new ActionParseException("Invalid new item", lexer.yyline()); - } - - break; - case EVAL: - expectedType(SymbolType.PARENT_OPEN); - GraphTargetItem evar = new EvalActionItem(null, expression(inFunction, inMethod, true, variables)); - expectedType(SymbolType.PARENT_CLOSE); - evar = memberOrCall(evar, inFunction, inMethod, variables); - ret = evar; - - break; - case IDENTIFIER: - case THIS: - case SUPER: - if (s.value.equals("not")) { - ret = new NotItem(null, expression(inFunction, inMethod, false, variables)); - } else { - lexer.pushback(s); - GraphTargetItem var = variable(inFunction, inMethod, variables); - var = memberOrCall(var, inFunction, inMethod, variables); - ret = var; - } - - break; - default: - GraphTargetItem excmd = expressionCommands(s, inFunction, inMethod, -1, variables); - if (excmd != null) { - //? - ret = excmd; - break; - } - lexer.pushback(s); - } - - if (debugMode) { - System.out.println("/primary"); - } - return ret; - } - - private DirectValueActionItem pushConst(String s) throws IOException, ActionParseException { - int index = constantPool.indexOf(s); - if (index == -1) { - constantPool.add(s); - index = constantPool.indexOf(s); - } - return new DirectValueActionItem(null, 0, new ConstantIndex(index), constantPool); - } - - private ActionScriptLexer lexer = null; - - private List constantPool; - - public List treeFromString(String str, List constantPool) throws ActionParseException, IOException { - List retTree = new ArrayList<>(); - this.constantPool = constantPool; - lexer = new ActionScriptLexer(new StringReader(str)); - - List vars = new ArrayList<>(); - retTree.addAll(commands(false, false, 0, vars)); - for (VariableActionItem v : vars) { - String varName = v.getVariableName(); - GraphTargetItem stored = v.getStoreValue(); - if (v.isDefinition()) { - v.setBoxedValue(new DefineLocalActionItem(null, pushConst(varName), stored)); - } else { - if (stored != null) { - v.setBoxedValue(new SetVariableActionItem(null, pushConst(varName), stored)); - } else { - v.setBoxedValue(new GetVariableActionItem(null, pushConst(varName))); - } - } - } - if (lexer.lex().type != SymbolType.EOF) { - throw new ActionParseException("Parsing finished before end of the file", lexer.yyline()); - } - return retTree; - } - - public List actionsFromTree(List tree, List constantPool) throws CompilationException { - ActionSourceGenerator gen = new ActionSourceGenerator(swfVersion, constantPool); - List ret = new ArrayList<>(); - SourceGeneratorLocalData localData = new SourceGeneratorLocalData( - new HashMap(), 0, Boolean.FALSE, 0); - List srcList = gen.generate(localData, tree); - for (GraphSourceItem s : srcList) { - if (s instanceof Action) { - ret.add((Action) s); - } - } - ret.add(0, new ActionConstantPool(constantPool)); - return ret; - } - - public List actionsFromString(String s) throws ActionParseException, IOException, CompilationException { - List constantPool = new ArrayList<>(); - List tree = treeFromString(s, constantPool); - return actionsFromTree(tree, constantPool); - } - - private void versionRequired(ParsedSymbol s, int min) throws ActionParseException { - versionRequired(s.value.toString(), min, Integer.MAX_VALUE); - } - - private void versionRequired(ParsedSymbol s, int min, int max) throws ActionParseException { - versionRequired(s.value.toString(), min, max); - } - - private void versionRequired(String type, int min, int max) throws ActionParseException { - if (min == max && swfVersion != min) { - throw new ActionParseException(type + " requires SWF version " + min, lexer.yyline()); - } - if (swfVersion < min) { - throw new ActionParseException(type + " requires at least SWF version " + min, lexer.yyline()); - } - if (swfVersion > max) { - throw new ActionParseException(type + " requires SWF version lower than " + max, lexer.yyline()); - } - } -} +/* + * Copyright (C) 2010-2015 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.action.parser.script; + +import com.jpexs.decompiler.flash.SourceGeneratorLocalData; +import com.jpexs.decompiler.flash.action.Action; +import com.jpexs.decompiler.flash.action.model.AsciiToCharActionItem; +import com.jpexs.decompiler.flash.action.model.CallActionItem; +import com.jpexs.decompiler.flash.action.model.CallFunctionActionItem; +import com.jpexs.decompiler.flash.action.model.CallMethodActionItem; +import com.jpexs.decompiler.flash.action.model.CastOpActionItem; +import com.jpexs.decompiler.flash.action.model.CharToAsciiActionItem; +import com.jpexs.decompiler.flash.action.model.CloneSpriteActionItem; +import com.jpexs.decompiler.flash.action.model.DefineLocalActionItem; +import com.jpexs.decompiler.flash.action.model.DeleteActionItem; +import com.jpexs.decompiler.flash.action.model.DirectValueActionItem; +import com.jpexs.decompiler.flash.action.model.EvalActionItem; +import com.jpexs.decompiler.flash.action.model.FSCommandActionItem; +import com.jpexs.decompiler.flash.action.model.FunctionActionItem; +import com.jpexs.decompiler.flash.action.model.GetMemberActionItem; +import com.jpexs.decompiler.flash.action.model.GetTimeActionItem; +import com.jpexs.decompiler.flash.action.model.GetURL2ActionItem; +import com.jpexs.decompiler.flash.action.model.GetVariableActionItem; +import com.jpexs.decompiler.flash.action.model.GetVersionActionItem; +import com.jpexs.decompiler.flash.action.model.GotoFrame2ActionItem; +import com.jpexs.decompiler.flash.action.model.InitArrayActionItem; +import com.jpexs.decompiler.flash.action.model.InitObjectActionItem; +import com.jpexs.decompiler.flash.action.model.LoadMovieActionItem; +import com.jpexs.decompiler.flash.action.model.LoadMovieNumActionItem; +import com.jpexs.decompiler.flash.action.model.LoadVariablesActionItem; +import com.jpexs.decompiler.flash.action.model.LoadVariablesNumActionItem; +import com.jpexs.decompiler.flash.action.model.MBAsciiToCharActionItem; +import com.jpexs.decompiler.flash.action.model.MBCharToAsciiActionItem; +import com.jpexs.decompiler.flash.action.model.MBStringExtractActionItem; +import com.jpexs.decompiler.flash.action.model.MBStringLengthActionItem; +import com.jpexs.decompiler.flash.action.model.NewMethodActionItem; +import com.jpexs.decompiler.flash.action.model.NewObjectActionItem; +import com.jpexs.decompiler.flash.action.model.NextFrameActionItem; +import com.jpexs.decompiler.flash.action.model.PlayActionItem; +import com.jpexs.decompiler.flash.action.model.PostDecrementActionItem; +import com.jpexs.decompiler.flash.action.model.PostIncrementActionItem; +import com.jpexs.decompiler.flash.action.model.PrevFrameActionItem; +import com.jpexs.decompiler.flash.action.model.PrintActionItem; +import com.jpexs.decompiler.flash.action.model.PrintAsBitmapActionItem; +import com.jpexs.decompiler.flash.action.model.PrintAsBitmapNumActionItem; +import com.jpexs.decompiler.flash.action.model.PrintNumActionItem; +import com.jpexs.decompiler.flash.action.model.RandomNumberActionItem; +import com.jpexs.decompiler.flash.action.model.RemoveSpriteActionItem; +import com.jpexs.decompiler.flash.action.model.ReturnActionItem; +import com.jpexs.decompiler.flash.action.model.SetMemberActionItem; +import com.jpexs.decompiler.flash.action.model.SetVariableActionItem; +import com.jpexs.decompiler.flash.action.model.StartDragActionItem; +import com.jpexs.decompiler.flash.action.model.StopActionItem; +import com.jpexs.decompiler.flash.action.model.StopAllSoundsActionItem; +import com.jpexs.decompiler.flash.action.model.StopDragActionItem; +import com.jpexs.decompiler.flash.action.model.StringExtractActionItem; +import com.jpexs.decompiler.flash.action.model.StringLengthActionItem; +import com.jpexs.decompiler.flash.action.model.TargetPathActionItem; +import com.jpexs.decompiler.flash.action.model.ThrowActionItem; +import com.jpexs.decompiler.flash.action.model.ToIntegerActionItem; +import com.jpexs.decompiler.flash.action.model.ToNumberActionItem; +import com.jpexs.decompiler.flash.action.model.ToStringActionItem; +import com.jpexs.decompiler.flash.action.model.ToggleHighQualityActionItem; +import com.jpexs.decompiler.flash.action.model.TraceActionItem; +import com.jpexs.decompiler.flash.action.model.TypeOfActionItem; +import com.jpexs.decompiler.flash.action.model.UnLoadMovieActionItem; +import com.jpexs.decompiler.flash.action.model.UnLoadMovieNumActionItem; +import com.jpexs.decompiler.flash.action.model.clauses.ClassActionItem; +import com.jpexs.decompiler.flash.action.model.clauses.ForInActionItem; +import com.jpexs.decompiler.flash.action.model.clauses.IfFrameLoadedActionItem; +import com.jpexs.decompiler.flash.action.model.clauses.InterfaceActionItem; +import com.jpexs.decompiler.flash.action.model.clauses.TellTargetActionItem; +import com.jpexs.decompiler.flash.action.model.clauses.TryActionItem; +import com.jpexs.decompiler.flash.action.model.clauses.WithActionItem; +import com.jpexs.decompiler.flash.action.model.operations.AddActionItem; +import com.jpexs.decompiler.flash.action.model.operations.AndActionItem; +import com.jpexs.decompiler.flash.action.model.operations.BitAndActionItem; +import com.jpexs.decompiler.flash.action.model.operations.BitOrActionItem; +import com.jpexs.decompiler.flash.action.model.operations.BitXorActionItem; +import com.jpexs.decompiler.flash.action.model.operations.DivideActionItem; +import com.jpexs.decompiler.flash.action.model.operations.EqActionItem; +import com.jpexs.decompiler.flash.action.model.operations.GeActionItem; +import com.jpexs.decompiler.flash.action.model.operations.GtActionItem; +import com.jpexs.decompiler.flash.action.model.operations.InstanceOfActionItem; +import com.jpexs.decompiler.flash.action.model.operations.LShiftActionItem; +import com.jpexs.decompiler.flash.action.model.operations.LeActionItem; +import com.jpexs.decompiler.flash.action.model.operations.LtActionItem; +import com.jpexs.decompiler.flash.action.model.operations.ModuloActionItem; +import com.jpexs.decompiler.flash.action.model.operations.MultiplyActionItem; +import com.jpexs.decompiler.flash.action.model.operations.NeqActionItem; +import com.jpexs.decompiler.flash.action.model.operations.OrActionItem; +import com.jpexs.decompiler.flash.action.model.operations.PreDecrementActionItem; +import com.jpexs.decompiler.flash.action.model.operations.PreIncrementActionItem; +import com.jpexs.decompiler.flash.action.model.operations.RShiftActionItem; +import com.jpexs.decompiler.flash.action.model.operations.StrictEqActionItem; +import com.jpexs.decompiler.flash.action.model.operations.StrictNeqActionItem; +import com.jpexs.decompiler.flash.action.model.operations.StringAddActionItem; +import com.jpexs.decompiler.flash.action.model.operations.StringEqActionItem; +import com.jpexs.decompiler.flash.action.model.operations.StringGeActionItem; +import com.jpexs.decompiler.flash.action.model.operations.StringGtActionItem; +import com.jpexs.decompiler.flash.action.model.operations.StringLeActionItem; +import com.jpexs.decompiler.flash.action.model.operations.StringLtActionItem; +import com.jpexs.decompiler.flash.action.model.operations.StringNeActionItem; +import com.jpexs.decompiler.flash.action.model.operations.SubtractActionItem; +import com.jpexs.decompiler.flash.action.model.operations.URShiftActionItem; +import com.jpexs.decompiler.flash.action.parser.ActionParseException; +import com.jpexs.decompiler.flash.action.swf4.ActionIf; +import com.jpexs.decompiler.flash.action.swf4.ConstantIndex; +import com.jpexs.decompiler.flash.action.swf5.ActionConstantPool; +import com.jpexs.decompiler.flash.ecma.Null; +import com.jpexs.decompiler.flash.ecma.Undefined; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.flash.helpers.collections.MyEntry; +import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.GraphSourceItem; +import com.jpexs.decompiler.graph.GraphTargetItem; +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.DoWhileItem; +import com.jpexs.decompiler.graph.model.ForItem; +import com.jpexs.decompiler.graph.model.IfItem; +import com.jpexs.decompiler.graph.model.LocalData; +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.SwitchItem; +import com.jpexs.decompiler.graph.model.TernarOpItem; +import com.jpexs.decompiler.graph.model.WhileItem; +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; + +/** + * + * @author JPEXS + */ +public class ActionScriptParser { + + private final int swfVersion; + + public ActionScriptParser(int swfVersion) { + this.swfVersion = swfVersion; + } + + private long uniqLast = 0; + + private final boolean debugMode = false; + + private String uniqId() { + uniqLast++; + return "" + uniqLast; + } + + private List commands(boolean inFunction, boolean inMethod, int forinlevel, List variables) throws IOException, ActionParseException { + List ret = new ArrayList<>(); + if (debugMode) { + System.out.println("commands:"); + } + GraphTargetItem cmd = null; + while ((cmd = command(inFunction, inMethod, forinlevel, true, variables)) != null) { + ret.add(cmd); + } + if (debugMode) { + System.out.println("/commands"); + } + return ret; + } + + private GraphTargetItem type(List variables) throws IOException, ActionParseException { + GraphTargetItem ret = null; + + ParsedSymbol s = lex(); + expected(s, lexer.yyline(), SymbolType.IDENTIFIER, SymbolType.STRING_OP); + ret = new VariableActionItem(s.value.toString(), null, false); + variables.add((VariableActionItem) ret); + s = lex(); + while (s.type == SymbolType.DOT) { + s = lex(); + expected(s, lexer.yyline(), SymbolType.IDENTIFIER, SymbolType.STRING_OP); + ret = new GetMemberActionItem(null, ret, pushConst(s.value.toString())); + s = lex(); + } + lexer.pushback(s); + return ret; + } + + private GraphTargetItem memberOrCall(GraphTargetItem newcmds, boolean inFunction, boolean inMethod, List variables) throws IOException, ActionParseException { + ParsedSymbol s = lex(); + GraphTargetItem ret = newcmds; + while (s.isType(SymbolType.DOT, SymbolType.BRACKET_OPEN, SymbolType.PARENT_OPEN)) { + switch (s.type) { + case DOT: + case BRACKET_OPEN: + lexer.pushback(s); + ret = member(ret, inFunction, inMethod, variables); + break; + case PARENT_OPEN: + if (ret instanceof GetMemberActionItem) { + GetMemberActionItem mem = (GetMemberActionItem) ret; + ret = new CallMethodActionItem(null, mem.object, mem.memberName, call(inFunction, inMethod, variables)); + } else if (ret instanceof VariableActionItem) { + VariableActionItem var = (VariableActionItem) ret; + ret = new CallFunctionActionItem(null, pushConst(var.getVariableName()), call(inFunction, inMethod, variables)); + } else { + ret = new CallFunctionActionItem(null, ret, call(inFunction, inMethod, variables)); + } + break; + } + s = lex(); + } + lexer.pushback(s); + return ret; + } + + private GraphTargetItem member(GraphTargetItem obj, boolean inFunction, boolean inMethod, List variables) throws IOException, ActionParseException { + GraphTargetItem ret = obj; + ParsedSymbol s = lex(); + while (s.isType(SymbolType.DOT, SymbolType.BRACKET_OPEN)) { + if (s.type == SymbolType.BRACKET_OPEN) { + ret = new GetMemberActionItem(null, ret, expression(inFunction, inMethod, true, variables)); + expectedType(SymbolType.BRACKET_CLOSE); + s = lex(); + continue; + } + s = lex(); + expected(s, lexer.yyline(), SymbolType.IDENTIFIER, SymbolType.THIS, SymbolType.SUPER, SymbolGroup.GLOBALFUNC, SymbolGroup.GLOBALCONST); + //TODO: Handle properties (?) + if (false) {//GraphTargetItem.propertyNamesList.contains(s.value.toString())) { + //ret.add(new ActionPush((Long) (long) (int) GraphTargetItem.propertyNamesList.indexOf(s.value.toString()))); + //ret.add(new ActionGetProperty()); + } else { + ret = new GetMemberActionItem(null, ret, pushConst(s.value.toString())); + } + s = lex(); + } + lexer.pushback(s); + + if (debugMode) { + System.out.println("/member"); + } + return ret; + } + + private GraphTargetItem variable(boolean inFunction, boolean inMethod, List variables) throws IOException, ActionParseException { + GraphTargetItem ret = null; + ParsedSymbol s = lex(); + expected(s, lexer.yyline(), SymbolType.IDENTIFIER, SymbolType.THIS, SymbolType.SUPER, SymbolType.STRING_OP); + ret = new VariableActionItem(s.value.toString(), null, false); + variables.add((VariableActionItem) ret); + ret = (member(ret, inFunction, inMethod, variables)); + return ret; + } + + private void expected(ParsedSymbol symb, int line, Object... expected) throws IOException, ActionParseException { + 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 ActionParseException("" + expStr + " expected but " + symb.type + " found", line); + } + } + + private ParsedSymbol expectedType(Object... type) throws IOException, ActionParseException { + ParsedSymbol symb = lex(); + expected(symb, lexer.yyline(), type); + return symb; + } + + private ParsedSymbol lex() throws IOException, ActionParseException { + ParsedSymbol ret = lexer.lex(); + if (debugMode) { + System.out.println(ret); + } + return ret; + } + + private List call(boolean inFunction, boolean inMethod, List variables) throws IOException, ActionParseException { + List 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(inFunction, inMethod, true, variables)); + s = lex(); + expected(s, lexer.yyline(), SymbolType.COMMA, SymbolType.PARENT_CLOSE); + } + return ret; + } + + private FunctionActionItem function(boolean withBody, String functionName, boolean isMethod, List variables) throws IOException, ActionParseException { + GraphTargetItem ret = null; + ParsedSymbol s = null; + expectedType(SymbolType.PARENT_OPEN); + s = lex(); + List paramNames = new ArrayList<>(); + + while (s.type != SymbolType.PARENT_CLOSE) { + if (s.type != SymbolType.COMMA) { + lexer.pushback(s); + } + s = lex(); + expected(s, lexer.yyline(), SymbolType.IDENTIFIER); + paramNames.add(s.value.toString()); + s = lex(); + if (s.type == SymbolType.COLON) { + type(variables); + s = lex(); + } + + if (!s.isType(SymbolType.COMMA, SymbolType.PARENT_CLOSE)) { + expected(s, lexer.yyline(), SymbolType.COMMA, SymbolType.PARENT_CLOSE); + } + } + List body = null; + List subvariables = new ArrayList<>(); + if (withBody) { + expectedType(SymbolType.CURLY_OPEN); + + body = commands(true, isMethod, 0, subvariables); + expectedType(SymbolType.CURLY_CLOSE); + } + + return new FunctionActionItem(null, functionName, paramNames, body, constantPool, -1, subvariables); + } + + private GraphTargetItem traits(boolean isInterface, GraphTargetItem nameStr, GraphTargetItem extendsStr, List implementsStr, List variables) throws IOException, ActionParseException { + + GraphTargetItem ret = null; + /*for (int i = 0; i < nameStr.size() - 1; i++) { + List notBody = new ArrayList<>(); + List globalClassTypeStr = new ArrayList<>(); + globalClassTypeStr.add("_global"); + for (int j = 0; j <= i; j++) { + globalClassTypeStr.add(nameStr.get(j)); + } + + List val = new ArrayList<>(); + val.add(new ActionPush((Long) 0L)); + val.add(pushConst("Object")); + val.add(new ActionNewObject()); + notBody.addAll(typeToActions(globalClassTypeStr, val)); + ret.addAll(typeToActions(globalClassTypeStr, null)); + ret.add(new ActionNot()); + ret.add(new ActionNot()); + ret.add(new ActionIf(GraphTargetItem.actionsToBytes(notBody, false, SWF.DEFAULT_VERSION).length)); + ret.addAll(notBody); + } + List ifbody = new ArrayList<>(); + List globalClassTypeStr = new ArrayList<>(); + globalClassTypeStr.add("_global"); + globalClassTypeStr.addAll(nameStr);*/ + + ParsedSymbol s = null; + FunctionActionItem constr = null; + List staticFunctions = new ArrayList<>(); + List> staticVars = new ArrayList<>(); + List functions = new ArrayList<>(); + List> vars = new ArrayList<>(); + + String classNameStr = ""; + if (nameStr instanceof GetMemberActionItem) { + GetMemberActionItem mem = (GetMemberActionItem) nameStr; + if (mem.memberName instanceof VariableActionItem) { + classNameStr = ((VariableActionItem) mem.memberName).getVariableName(); + } else if (mem.memberName instanceof DirectValueActionItem) { + classNameStr = ((DirectValueActionItem) mem.memberName).toStringNoQuotes(LocalData.empty); + } + } else if (nameStr instanceof VariableActionItem) { + VariableActionItem var = (VariableActionItem) nameStr; + classNameStr = var.getVariableName(); + } + + looptrait: + while (true) { + s = lex(); + boolean isStatic = false; + while (s.isType(SymbolType.STATIC, SymbolType.PUBLIC, SymbolType.PRIVATE)) { + if (s.type == SymbolType.STATIC) { + isStatic = true; + } + s = lex(); + } + switch (s.type) { + case FUNCTION: + s = lex(); + expected(s, lexer.yyline(), SymbolType.IDENTIFIER, SymbolGroup.GLOBALFUNC); + String fname = s.value.toString(); + if (fname.equals(classNameStr)) { //constructor + constr = (function(!isInterface, "", true, variables)); + } else { + if (!isInterface) { + if (isStatic) { + FunctionActionItem ft = function(!isInterface, "", true, variables); + ft.calculatedFunctionName = pushConst(fname); + staticFunctions.add(ft); + } else { + FunctionActionItem ft = function(!isInterface, "", true, variables); + ft.calculatedFunctionName = pushConst(fname); + functions.add(ft); + } + } + } + break; + case VAR: + s = lex(); + expected(s, lexer.yyline(), SymbolType.IDENTIFIER); + String ident = s.value.toString(); + s = lex(); + if (s.type == SymbolType.COLON) { + type(variables); + s = lex(); + } + if (s.type == SymbolType.ASSIGN) { + if (isStatic) { + staticVars.add(new MyEntry(pushConst(ident), expression(false, false, true, variables))); + } else { + vars.add(new MyEntry(pushConst(ident), expression(false, false, true, variables))); + } + s = lex(); + } + if (s.type != SymbolType.SEMICOLON) { + lexer.pushback(s); + } + break; + default: + lexer.pushback(s); + break looptrait; + + } + } + + if (isInterface) { + return new InterfaceActionItem(nameStr, implementsStr); + } else { + return new ClassActionItem(nameStr, extendsStr, implementsStr, constr, functions, vars, staticFunctions, staticVars); + } + } + + private GraphTargetItem expressionCommands(ParsedSymbol s, boolean inFunction, boolean inMethod, int forinlevel, List variables) throws IOException, ActionParseException { + if (debugMode) { + System.out.println("expressionCommands:"); + } + GraphTargetItem ret = null; + switch (s.type) { + case GETVERSION: + expectedType(SymbolType.PARENT_OPEN); + ret = new GetVersionActionItem(null); + expectedType(SymbolType.PARENT_CLOSE); + break; + case MBORD: + expectedType(SymbolType.PARENT_OPEN); + ret = new MBCharToAsciiActionItem(null, expression(inFunction, inMethod, true, variables)); + expectedType(SymbolType.PARENT_CLOSE); + break; + case MBCHR: + expectedType(SymbolType.PARENT_OPEN); + ret = new MBAsciiToCharActionItem(null, expression(inFunction, inMethod, true, variables)); + expectedType(SymbolType.PARENT_CLOSE); + break; + case MBLENGTH: + expectedType(SymbolType.PARENT_OPEN); + ret = new MBStringLengthActionItem(null, expression(inFunction, inMethod, true, variables)); + expectedType(SymbolType.PARENT_CLOSE); + break; + case MBSUBSTRING: + expectedType(SymbolType.PARENT_OPEN); + GraphTargetItem val1 = (expression(inFunction, inMethod, true, variables)); + expectedType(SymbolType.COMMA); + GraphTargetItem index1 = (expression(inFunction, inMethod, true, variables)); + expectedType(SymbolType.COMMA); + GraphTargetItem len1 = (expression(inFunction, inMethod, true, variables)); + expectedType(SymbolType.PARENT_CLOSE); + ret = new MBStringExtractActionItem(null, val1, index1, len1); + break; + case SUBSTR: + expectedType(SymbolType.PARENT_OPEN); + GraphTargetItem val2 = (expression(inFunction, inMethod, true, variables)); + expectedType(SymbolType.COMMA); + GraphTargetItem index2 = (expression(inFunction, inMethod, true, variables)); + expectedType(SymbolType.COMMA); + GraphTargetItem len2 = (expression(inFunction, inMethod, true, variables)); + expectedType(SymbolType.PARENT_CLOSE); + ret = new StringExtractActionItem(null, val2, index2, len2); + break; + case LENGTH: + expectedType(SymbolType.PARENT_OPEN); + ret = new StringLengthActionItem(null, expression(inFunction, inMethod, true, variables)); + expectedType(SymbolType.PARENT_CLOSE); + break; + case RANDOM: + expectedType(SymbolType.PARENT_OPEN); + ret = new RandomNumberActionItem(null, expression(inFunction, inMethod, true, variables)); + expectedType(SymbolType.PARENT_CLOSE); + break; + case INT: + expectedType(SymbolType.PARENT_OPEN); + ret = new ToIntegerActionItem(null, expression(inFunction, inMethod, true, variables)); + expectedType(SymbolType.PARENT_CLOSE); + break; + case NUMBER_OP: + s = lex(); + if (s.type == SymbolType.DOT) { + VariableActionItem vi = new VariableActionItem(s.value.toString(), null, false); + variables.add(vi); + ret = memberOrCall(vi, inFunction, inMethod, variables); + } else { + expected(s, lexer.yyline(), SymbolType.PARENT_OPEN); + ret = new ToNumberActionItem(null, expression(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); + VariableActionItem vi2 = new VariableActionItem(sop.value.toString(), null, false); + variables.add(vi2); + ret = memberOrCall(vi2, inFunction, inMethod, variables); + } else { + expected(s, lexer.yyline(), SymbolType.PARENT_OPEN); + ret = new ToStringActionItem(null, expression(inFunction, inMethod, true, variables)); + expectedType(SymbolType.PARENT_CLOSE); + ret = memberOrCall(ret, inFunction, inMethod, variables); + } + break; + case ORD: + expectedType(SymbolType.PARENT_OPEN); + ret = new CharToAsciiActionItem(null, expression(inFunction, inMethod, true, variables)); + expectedType(SymbolType.PARENT_CLOSE); + break; + case CHR: + expectedType(SymbolType.PARENT_OPEN); + ret = new AsciiToCharActionItem(null, expression(inFunction, inMethod, true, variables)); + expectedType(SymbolType.PARENT_CLOSE); + break; + case DUPLICATEMOVIECLIP: + expectedType(SymbolType.PARENT_OPEN); + GraphTargetItem src3 = (expression(inFunction, inMethod, true, variables)); + expectedType(SymbolType.COMMA); + GraphTargetItem tar3 = (expression(inFunction, inMethod, true, variables)); + expectedType(SymbolType.COMMA); + GraphTargetItem dep3 = (expression(inFunction, inMethod, true, variables)); + expectedType(SymbolType.PARENT_CLOSE); + ret = new CloneSpriteActionItem(null, src3, tar3, dep3); + break; + case GETTIMER: + expectedType(SymbolType.PARENT_OPEN); + expectedType(SymbolType.PARENT_CLOSE); + ret = new GetTimeActionItem(null); + break; + default: + return null; + } + if (debugMode) { + System.out.println("/expressionCommands"); + } + return ret; + } + + private GraphTargetItem command(boolean inFunction, boolean inMethod, int forinlevel, boolean mustBeCommand, List variables) throws IOException, ActionParseException { + 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; + } + switch (s.type) { + case FSCOMMAND: + expectedType(SymbolType.PARENT_OPEN); + ret = new FSCommandActionItem(null, expression(inFunction, inMethod, true, variables)); + expectedType(SymbolType.PARENT_CLOSE); + break; + case CALL: + expectedType(SymbolType.PARENT_OPEN); + ret = new CallActionItem(null, (expression(inFunction, inMethod, true, variables))); + expectedType(SymbolType.PARENT_CLOSE); + break; + case LENGTH: + expectedType(SymbolType.PARENT_OPEN); + ret = new StringLengthActionItem(null, (expression(inFunction, inMethod, true, variables))); + expectedType(SymbolType.PARENT_CLOSE); + break; + case MBLENGTH: + expectedType(SymbolType.PARENT_OPEN); + ret = new MBStringLengthActionItem(null, (expression(inFunction, inMethod, true, variables))); + expectedType(SymbolType.PARENT_CLOSE); + break; + case SET: + expectedType(SymbolType.PARENT_OPEN); + GraphTargetItem name1 = (expression(inFunction, inMethod, true, variables)); + expectedType(SymbolType.COMMA); + GraphTargetItem value1 = (expression(inFunction, inMethod, true, variables)); + expectedType(SymbolType.PARENT_CLOSE); + ret = new SetVariableActionItem(null, name1, value1); + break; + case WITH: + expectedType(SymbolType.PARENT_OPEN); + GraphTargetItem wvar = (variable(inFunction, inMethod, variables)); + expectedType(SymbolType.PARENT_CLOSE); + expectedType(SymbolType.CURLY_OPEN); + List wcmd = commands(inFunction, inMethod, forinlevel, variables); + expectedType(SymbolType.CURLY_CLOSE); + ret = new WithActionItem(null, wvar, wcmd); + break; + case DELETE: + GraphTargetItem varDel = variable(inFunction, inMethod, variables); + if (varDel instanceof GetMemberActionItem) { + GetMemberActionItem gm = (GetMemberActionItem) varDel; + ret = new DeleteActionItem(null, gm.object, gm.memberName); + } else if (varDel instanceof VariableActionItem) { + variables.remove(varDel); + ret = new DeleteActionItem(null, null, pushConst(((VariableActionItem) varDel).getVariableName())); + } else { + throw new ActionParseException("Not a property", lexer.yyline()); + } + break; + case TRACE: + expectedType(SymbolType.PARENT_OPEN); + ret = new TraceActionItem(null, (expression(inFunction, inMethod, true, variables))); + expectedType(SymbolType.PARENT_CLOSE); + break; + + case GETURL: + expectedType(SymbolType.PARENT_OPEN); + GraphTargetItem url = (expression(inFunction, inMethod, true, variables)); + s = lex(); + expected(s, lexer.yyline(), SymbolType.PARENT_CLOSE, SymbolType.COMMA); + int getuMethod = 1; + GraphTargetItem target = null; + if (s.type == SymbolType.COMMA) { + target = (expression(inFunction, inMethod, true, variables)); + s = lex(); + if (s.type == SymbolType.COMMA) { + s = lex(); + expected(s, lexer.yyline(), SymbolType.STRING); + if (s.value.equals("GET")) { + getuMethod = 1; + } else if (s.value.equals("POST")) { + getuMethod = 2; + } else { + throw new ActionParseException("Invalid method, \"GET\" or \"POST\" expected.", lexer.yyline()); + } + } else { + lexer.pushback(s); + } + } else { + lexer.pushback(s); + target = new DirectValueActionItem(null, 0, "", new ArrayList()); + } + expectedType(SymbolType.PARENT_CLOSE); + ret = new GetURL2ActionItem(null, url, target, getuMethod); + break; + case GOTOANDSTOP: + expectedType(SymbolType.PARENT_OPEN); + GraphTargetItem gtsFrame = expression(inFunction, inMethod, true, variables); + s = lex(); + if (s.type == SymbolType.COMMA) { //Handle scene? + s = lex(); + gtsFrame = expression(inFunction, inMethod, true, variables); + } else { + lexer.pushback(s); + } + ret = new GotoFrame2ActionItem(null, gtsFrame, false, false, 0); + expectedType(SymbolType.PARENT_CLOSE); + break; + case NEXTFRAME: + expectedType(SymbolType.PARENT_OPEN); + expectedType(SymbolType.PARENT_CLOSE); + ret = new NextFrameActionItem(null); + break; + case PLAY: + expectedType(SymbolType.PARENT_OPEN); + expectedType(SymbolType.PARENT_CLOSE); + ret = new PlayActionItem(null); + break; + case PREVFRAME: + expectedType(SymbolType.PARENT_OPEN); + expectedType(SymbolType.PARENT_CLOSE); + ret = new PrevFrameActionItem(null); + break; + case TELLTARGET: + expectedType(SymbolType.PARENT_OPEN); + GraphTargetItem tellTarget = expression(inFunction, inMethod, true, variables); + expectedType(SymbolType.PARENT_CLOSE); + expectedType(SymbolType.CURLY_OPEN); + List tellcmds = commands(inFunction, inMethod, forinlevel, variables); + expectedType(SymbolType.CURLY_CLOSE); + ret = new TellTargetActionItem(null, tellTarget, tellcmds); + break; + case STOP: + expectedType(SymbolType.PARENT_OPEN); + expectedType(SymbolType.PARENT_CLOSE); + ret = new StopActionItem(null); + break; + case STOPALLSOUNDS: + expectedType(SymbolType.PARENT_OPEN); + expectedType(SymbolType.PARENT_CLOSE); + ret = new StopAllSoundsActionItem(null); + break; + case TOGGLEHIGHQUALITY: + expectedType(SymbolType.PARENT_OPEN); + expectedType(SymbolType.PARENT_CLOSE); + ret = new ToggleHighQualityActionItem(null); + break; + + case STOPDRAG: + expectedType(SymbolType.PARENT_OPEN); + expectedType(SymbolType.PARENT_CLOSE); + ret = new StopDragActionItem(null); + break; + + case TARGETPATH: + expectedType(SymbolType.PARENT_OPEN); + ret = new TargetPathActionItem(null, (expression(inFunction, inMethod, true, variables))); + expectedType(SymbolType.PARENT_CLOSE); + break; + + case UNLOADMOVIE: + case UNLOADMOVIENUM: + SymbolType unloadType = s.type; + expectedType(SymbolType.PARENT_OPEN); + GraphTargetItem unTargetOrNum = expression(inFunction, inMethod, true, variables); + expectedType(SymbolType.PARENT_CLOSE); + if (unloadType == SymbolType.UNLOADMOVIE) { + ret = new UnLoadMovieActionItem(null, unTargetOrNum); + } + if (unloadType == SymbolType.UNLOADMOVIENUM) { + ret = new UnLoadMovieNumActionItem(null, unTargetOrNum); + } + break; + case PRINT: + case PRINTASBITMAP: + case PRINTASBITMAPNUM: + case PRINTNUM: + SymbolType printType = s.type; + expectedType(SymbolType.PARENT_OPEN); + GraphTargetItem printTarget = (expression(inFunction, inMethod, true, variables)); + expectedType(SymbolType.COMMA); + GraphTargetItem printBBox = (expression(inFunction, inMethod, true, variables)); + expectedType(SymbolType.PARENT_CLOSE); + + switch (printType) { + case PRINT: + ret = new PrintActionItem(null, printTarget, printBBox); + break; + case PRINTNUM: + ret = new PrintNumActionItem(null, printTarget, printBBox); + break; + case PRINTASBITMAP: + ret = new PrintAsBitmapActionItem(null, printTarget, printBBox); + break; + case PRINTASBITMAPNUM: + ret = new PrintAsBitmapNumActionItem(null, printTarget, printBBox); + break; + } + break; + case LOADVARIABLES: + case LOADMOVIE: + case LOADVARIABLESNUM: + case LOADMOVIENUM: + SymbolType loadType = s.type; + expectedType(SymbolType.PARENT_OPEN); + GraphTargetItem url2 = (expression(inFunction, inMethod, true, variables)); + expectedType(SymbolType.COMMA); + GraphTargetItem targetOrNum = (expression(inFunction, inMethod, true, variables)); + + s = lex(); + expected(s, lexer.yyline(), SymbolType.PARENT_CLOSE, SymbolType.COMMA); + int lvmethod = 1; + if (s.type == SymbolType.COMMA) { + s = lex(); + expected(s, lexer.yyline(), SymbolType.STRING); + if (s.value.equals("POST")) { + lvmethod = 2; + } else if (s.value.equals("GET")) { + lvmethod = 1; + } else { + throw new ActionParseException("Invalid method, \"GET\" or \"POST\" expected.", lexer.yyline()); + } + } else { + lexer.pushback(s); + } + expectedType(SymbolType.PARENT_CLOSE); + switch (loadType) { + case LOADVARIABLES: + ret = new LoadVariablesActionItem(null, url2, targetOrNum, lvmethod); + break; + case LOADMOVIE: + ret = new LoadMovieActionItem(null, url2, targetOrNum, lvmethod); + break; + case LOADVARIABLESNUM: + ret = new LoadVariablesNumActionItem(null, url2, targetOrNum, lvmethod); + break; + case LOADMOVIENUM: + ret = new LoadMovieNumActionItem(null, url2, targetOrNum, lvmethod); + break; + } + break; + case GOTOANDPLAY: + expectedType(SymbolType.PARENT_OPEN); + GraphTargetItem gtpFrame = expression(inFunction, inMethod, true, variables); + s = lex(); + if (s.type == SymbolType.COMMA) { //Handle scene? + s = lex(); + gtpFrame = expression(inFunction, inMethod, true, variables); + } else { + lexer.pushback(s); + } + ret = new GotoFrame2ActionItem(null, gtpFrame, false, true, 0); + expectedType(SymbolType.PARENT_CLOSE); + break; + + case REMOVEMOVIECLIP: + expectedType(SymbolType.PARENT_OPEN); + ret = new RemoveSpriteActionItem(null, (expression(inFunction, inMethod, true, variables))); + expectedType(SymbolType.PARENT_CLOSE); + break; + case STARTDRAG: + expectedType(SymbolType.PARENT_OPEN); + GraphTargetItem dragTarget = (expression(inFunction, inMethod, true, variables)); + GraphTargetItem lockCenter = null; + GraphTargetItem constrain = null; + GraphTargetItem x1 = null; + GraphTargetItem y1 = null; + GraphTargetItem x2 = null; + GraphTargetItem y2 = null; + s = lex(); + if (s.type == SymbolType.COMMA) { + lockCenter = (expression(inFunction, inMethod, true, variables)); + s = lex(); + if (s.type == SymbolType.COMMA) { + constrain = new DirectValueActionItem(null, 0, 1L, new ArrayList()); + x1 = (expression(inFunction, inMethod, true, variables)); + s = lex(); + if (s.type == SymbolType.COMMA) { + y1 = (expression(inFunction, inMethod, true, variables)); + s = lex(); + if (s.type == SymbolType.COMMA) { + x2 = (expression(inFunction, inMethod, true, variables)); + s = lex(); + if (s.type == SymbolType.COMMA) { + y2 = (expression(inFunction, inMethod, true, variables)); + } else { + lexer.pushback(s); + y2 = new DirectValueActionItem(null, 0, 0L, new ArrayList()); + } + } else { + lexer.pushback(s); + x2 = new DirectValueActionItem(null, 0, 0L, new ArrayList()); + y2 = new DirectValueActionItem(null, 0, 0L, new ArrayList()); + } + } else { + lexer.pushback(s); + x2 = new DirectValueActionItem(null, 0, 0L, new ArrayList()); + y2 = new DirectValueActionItem(null, 0, 0L, new ArrayList()); + y1 = new DirectValueActionItem(null, 0, 0L, new ArrayList()); + + } + } else { + lexer.pushback(s); + constrain = new DirectValueActionItem(null, 0, 0L, new ArrayList()); + //ret.add(new ActionPush(Boolean.FALSE)); + } + } else { + lockCenter = new DirectValueActionItem(null, 0, 0L, new ArrayList()); + constrain = new DirectValueActionItem(null, 0, 0L, new ArrayList()); + lexer.pushback(s); + } + expectedType(SymbolType.PARENT_CLOSE); + ret = new StartDragActionItem(null, dragTarget, lockCenter, constrain, x1, y1, x2, y2); + break; + + case IFFRAMELOADED: + expectedType(SymbolType.PARENT_OPEN); + GraphTargetItem iflExpr = (expression(inFunction, inMethod, true, variables)); + expectedType(SymbolType.PARENT_CLOSE); + expectedType(SymbolType.CURLY_OPEN); + List iflComs = commands(inFunction, inMethod, forinlevel, variables); + expectedType(SymbolType.CURLY_CLOSE); + ret = new IfFrameLoadedActionItem(iflExpr, iflComs, null); + break; + case CLASS: + GraphTargetItem classTypeStr = type(variables); + s = lex(); + GraphTargetItem extendsTypeStr = null; + if (s.type == SymbolType.EXTENDS) { + extendsTypeStr = type(variables); + s = lex(); + } + List implementsTypeStrs = new ArrayList<>(); + if (s.type == SymbolType.IMPLEMENTS) { + do { + GraphTargetItem implementsTypeStr = type(variables); + implementsTypeStrs.add(implementsTypeStr); + s = lex(); + } while (s.type == SymbolType.COMMA); + } + expected(s, lexer.yyline(), SymbolType.CURLY_OPEN); + ret = (traits(false, classTypeStr, extendsTypeStr, implementsTypeStrs, variables)); + expectedType(SymbolType.CURLY_CLOSE); + break; + case INTERFACE: + GraphTargetItem interfaceTypeStr = type(variables); + s = lex(); + List intExtendsTypeStrs = new ArrayList<>(); + + if (s.type == SymbolType.EXTENDS) { + do { + GraphTargetItem intExtendsTypeStr = type(variables); + intExtendsTypeStrs.add(intExtendsTypeStr); + s = lex(); + } while (s.type == SymbolType.COMMA); + } + expected(s, lexer.yyline(), SymbolType.CURLY_OPEN); + ret = (traits(true, interfaceTypeStr, null, intExtendsTypeStrs, variables)); + expectedType(SymbolType.CURLY_CLOSE); + break; + case FUNCTION: + s = lexer.lex(); + expected(s, lexer.yyline(), SymbolType.IDENTIFIER, SymbolGroup.GLOBALFUNC); + ret = (function(true, s.value.toString(), false, variables)); + break; + case VAR: + s = lex(); + expected(s, lexer.yyline(), SymbolType.IDENTIFIER); + String varIdentifier = s.value.toString(); + s = lex(); + if (s.type == SymbolType.COLON) { + type(variables); + s = lex(); + //TODO: handle value type + } + + if (s.type == SymbolType.ASSIGN) { + GraphTargetItem varval = (expression(inFunction, inMethod, true, variables)); + ret = new VariableActionItem(varIdentifier, varval, true); + variables.add((VariableActionItem) ret); + } else { + ret = new VariableActionItem(varIdentifier, null, true); + variables.add((VariableActionItem) ret); + lexer.pushback(s); + } + break; + case CURLY_OPEN: + ret = new BlockItem(null, commands(inFunction, inMethod, forinlevel, variables)); + expectedType(SymbolType.CURLY_CLOSE); + break; + case INCREMENT: //preincrement + case DECREMENT: //predecrement + GraphTargetItem varincdec = variable(inFunction, inMethod, variables); + if (s.type == SymbolType.INCREMENT) { + ret = new PreIncrementActionItem(null, varincdec); + } else if (s.type == SymbolType.DECREMENT) { + ret = new PreDecrementActionItem(null, varincdec); + } + break; + case SUPER: //constructor call + ParsedSymbol ss2 = lex(); + if (ss2.type == SymbolType.PARENT_OPEN) { + List args = call(inFunction, inMethod, variables); + VariableActionItem supItem = new VariableActionItem(s.value.toString(), null, false); + variables.add(supItem); + ret = new CallMethodActionItem(null, supItem, new DirectValueActionItem(null, 0, new Undefined(), constantPool), 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(inFunction, inMethod, true, variables)); + expectedType(SymbolType.PARENT_CLOSE); + GraphTargetItem onTrue = command(inFunction, inMethod, forinlevel, true, variables); + List onTrueList = new ArrayList<>(); + onTrueList.add(onTrue); + s = lex(); + List onFalseList = null; + if (s.type == SymbolType.ELSE) { + onFalseList = new ArrayList<>(); + onFalseList.add(command(inFunction, inMethod, forinlevel, true, variables)); + } else { + lexer.pushback(s); + } + ret = new IfItem(null, ifExpr, onTrueList, onFalseList); + break; + case WHILE: + expectedType(SymbolType.PARENT_OPEN); + List whileExpr = new ArrayList<>(); + whileExpr.add(commaExpression(inFunction, inMethod, forinlevel, variables)); + expectedType(SymbolType.PARENT_CLOSE); + List whileBody = new ArrayList<>(); + whileBody.add(command(inFunction, inMethod, forinlevel, true, variables)); + ret = new WhileItem(null, null, whileExpr, whileBody); + break; + case DO: + List doBody = new ArrayList<>(); + doBody.add(command(inFunction, inMethod, forinlevel, true, variables)); + expectedType(SymbolType.WHILE); + expectedType(SymbolType.PARENT_OPEN); + List doExpr = new ArrayList<>(); + doExpr.add(commaExpression(inFunction, inMethod, forinlevel, variables)); + expectedType(SymbolType.PARENT_CLOSE); + ret = new DoWhileItem(null, null, doBody, doExpr); + break; + case FOR: + expectedType(SymbolType.PARENT_OPEN); + s = lex(); + boolean forin = false; + GraphTargetItem collection = null; + String objIdent = null; + VariableActionItem item = null; + int innerExprReg = 0; + boolean define = false; + if (s.type == SymbolType.VAR || s.type == SymbolType.IDENTIFIER) { + ParsedSymbol s2 = null; + ParsedSymbol ssel = s; + if (s.type == SymbolType.VAR) { + s2 = lex(); + ssel = s2; + define = true; + } + + if (ssel.type == SymbolType.IDENTIFIER) { + objIdent = ssel.value.toString(); + + ParsedSymbol s3 = lex(); + if (s3.type == SymbolType.IN) { + if (inFunction) { + /*for (int i = 0; i < 256; i++) { + if (!registerVars.containsValue(i)) { + registerVars.put(objIdent, i); + innerExprReg = i; + break; + } + }*/ + } + + item = new VariableActionItem(objIdent, null, define); + + item.setStoreValue(new GraphTargetItem() { + + @Override + public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { + return writer; + } + + @Override + public boolean hasReturnValue() { + return false; + } + + @Override + public GraphTargetItem returnType() { + return TypeItem.UNBOUNDED; + } + + //toSource is Empty + }); + + variables.add(item); + + collection = expression(inFunction, inMethod, true, variables); + forin = true; + } else { + lexer.pushback(s3); + if (s2 != null) { + lexer.pushback(s2); + } + lexer.pushback(s); + } + } else { + if (s2 != null) { + lexer.pushback(s2); + } + lexer.pushback(s); + } + } else { + lexer.pushback(s); + } + List forFinalCommands = new ArrayList<>(); + GraphTargetItem forExpr = null; + List forFirstCommands = new ArrayList<>(); + if (!forin) { + GraphTargetItem fc = command(inFunction, inMethod, forinlevel, true, variables); + if (fc != null) { //can be empty command + forFirstCommands.add(fc); + } + forExpr = (expression(inFunction, inMethod, true, variables)); + expectedType(SymbolType.SEMICOLON); + forFinalCommands.add(command(inFunction, inMethod, forinlevel, true, variables)); + } + expectedType(SymbolType.PARENT_CLOSE); + List forBody = new ArrayList<>(); + forBody.add(command(inFunction, inMethod, forin ? forinlevel + 1 : forinlevel, true, variables)); + if (forin) { + ret = new ForInActionItem(null, null, item, collection, forBody); + } else { + ret = new ForItem(null, null, forFirstCommands, forExpr, forFinalCommands, forBody); + } + break; + case SWITCH: + expectedType(SymbolType.PARENT_OPEN); + GraphTargetItem switchExpr = expression(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> caseIfs = new ArrayList<>(); + List> caseCmds = new ArrayList<>(); + List caseExprsAll = new ArrayList<>(); + List valueMapping = new ArrayList<>(); + int pos = 0; + while (s.type == SymbolType.CASE) { + List caseExprs = new ArrayList<>(); + while (s.type == SymbolType.CASE) { + GraphTargetItem curCaseExpr = expression(inFunction, inMethod, true, variables); + caseExprs.add(curCaseExpr); + expectedType(SymbolType.COLON); + s = lex(); + caseExprsAll.add(curCaseExpr); + valueMapping.add(pos); + } + pos++; + lexer.pushback(s); + List caseCmd = commands(inFunction, inMethod, forinlevel, variables); + caseCmds.add(caseCmd); + s = lex(); + } + List defCmd = new ArrayList<>(); + if (s.type == SymbolType.DEFAULT) { + expectedType(SymbolType.COLON); + defCmd = commands(inFunction, inMethod, forinlevel, variables); + s = lexer.lex(); + } + expected(s, lexer.yyline(), SymbolType.CURLY_CLOSE); + ret = new SwitchItem(null, null, switchExpr, caseExprsAll, caseCmds, defCmd, valueMapping); + break; + case BREAK: + ret = new BreakItem(null, 0); //? There is no more than 1 level continue/break in AS1/2 + break; + case CONTINUE: + ret = new ContinueItem(null, 0); //? There is no more than 1 level continue/break in AS1/2 + break; + case RETURN: + GraphTargetItem retexpr = expression(inFunction, inMethod, true, variables); + if (retexpr == null) { + retexpr = new DirectValueActionItem(null, 0, new Undefined(), new ArrayList()); + } + ret = new ReturnActionItem(null, retexpr); + break; + case TRY: + List tryCommands = new ArrayList<>(); + tryCommands.add(command(inFunction, inMethod, forinlevel, true, variables)); + s = lex(); + boolean found = false; + List> catchCommands = null; + List catchExceptions = new ArrayList<>(); + if (s.type == SymbolType.CATCH) { + expectedType(SymbolType.PARENT_OPEN); + s = lex(); + expected(s, lexer.yyline(), SymbolType.IDENTIFIER, SymbolType.STRING); + catchExceptions.add(pushConst((String) s.value)); + expectedType(SymbolType.PARENT_CLOSE); + catchCommands = new ArrayList<>(); + List cc = new ArrayList<>(); + cc.add(command(inFunction, inMethod, forinlevel, true, variables)); + catchCommands.add(cc); + s = lex(); + found = true; + } + List finallyCommands = null; + if (s.type == SymbolType.FINALLY) { + finallyCommands = new ArrayList<>(); + finallyCommands.add(command(inFunction, inMethod, forinlevel, true, variables)); + found = true; + s = lex(); + } + if (!found) { + expected(s, lexer.yyline(), SymbolType.CATCH, SymbolType.FINALLY); + } + lexer.pushback(s); + ret = new TryActionItem(tryCommands, catchExceptions, catchCommands, finallyCommands); + break; + case THROW: + ret = new ThrowActionItem(null, expression(inFunction, inMethod, true, variables)); + break; + case SEMICOLON: //empty command + if (debugMode) { + System.out.println("/command"); + } + return null; + default: + GraphTargetItem valcmd = expressionCommands(s, inFunction, inMethod, forinlevel, variables); + if (valcmd != null) { + ret = valcmd; + break; + } + lexer.pushback(s); + ret = expression(inFunction, inMethod, true, variables); + } + if (debugMode) { + System.out.println("/command"); + } + lexer.removeListener(buf); + if (ret == null) { //can be popped expression + buf.pushAllBack(lexer); + ret = expression(inFunction, inMethod, true, variables); + } + s = lex(); + if ((s != null) && (s.type != SymbolType.SEMICOLON)) { + lexer.pushback(s); + } + + return ret; + + } + + private GraphTargetItem expression(boolean inFunction, boolean inMethod, boolean allowRemainder, List variables) throws IOException, ActionParseException { + if (debugMode) { + System.out.println("expression:"); + } + GraphTargetItem prim = expressionPrimary(false, inFunction, inMethod, allowRemainder, variables); + if (prim == null) { + return null; + } + GraphTargetItem ret = expression1(prim, GraphTargetItem.NOPRECEDENCE, inFunction, inMethod, allowRemainder, variables); + if (debugMode) { + System.out.println("/expression"); + } + return ret; + } + + private ParsedSymbol peekLex() throws IOException, ActionParseException { + ParsedSymbol lookahead = lex(); + lexer.pushback(lookahead); + return lookahead; + } + + private static final String[] operatorIdentifiers = new String[]{"add", "eq", "ne", "lt", "ge", "gt", "le"}; + + private boolean isBinaryOperator(ParsedSymbol s) { + if (s.type == SymbolType.IDENTIFIER && Arrays.asList(operatorIdentifiers).contains(s.value.toString())) { + return true; + } + return s.type.isBinary(); + } + + private int getSymbPrecedence(ParsedSymbol s) { + if (s.type == SymbolType.IDENTIFIER && Arrays.asList(operatorIdentifiers).contains(s.value.toString())) { + switch (s.value.toString()) { + case "add": + return GraphTargetItem.PRECEDENCE_ADDITIVE; + case "eq": + case "ne": + return GraphTargetItem.PRECEDENCE_EQUALITY; + case "lt": + case "ge": + case "gt": + case "le": + return GraphTargetItem.PRECEDENCE_RELATIONAL; + } + } + return s.type.getPrecedence(); + } + + private GraphTargetItem expression1(GraphTargetItem lhs, int min_precedence, boolean inFunction, boolean inMethod, boolean allowRemainder, List variables) throws IOException, ActionParseException { + ParsedSymbol op; + GraphTargetItem rhs; + GraphTargetItem mhs = null; + ParsedSymbol lookahead = peekLex(); + if (debugMode) { + System.out.println("expression1:"); + } + //Note: algorithm from http://en.wikipedia.org/wiki/Operator-precedence_parser + //with relation operators reversed as we have precedence in reverse order + while (isBinaryOperator(lookahead) && getSymbPrecedence(lookahead) <= /* >= 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(inFunction, inMethod, allowRemainder, variables); + expectedType(SymbolType.COLON); + if (debugMode) { + System.out.println("/ternar-middle"); + } + } + + rhs = expressionPrimary(allowRemainder, inFunction, inMethod, allowRemainder, variables); + if (rhs == null) { + lexer.pushback(op); + break; + } + + lookahead = peekLex(); + while ((isBinaryOperator(lookahead) && getSymbPrecedence(lookahead) < /* > on wiki */ getSymbPrecedence(op)) + || (lookahead.type.isRightAssociative() && getSymbPrecedence(lookahead) == getSymbPrecedence(op))) { + rhs = expression1(rhs, getSymbPrecedence(lookahead), inFunction, inMethod, allowRemainder, variables); + lookahead = peekLex(); + } + + switch (op.type) { + case TERNAR: + lhs = new TernarOpItem(null, lhs, mhs, rhs); + break; + case SHIFT_LEFT: + lhs = new LShiftActionItem(null, lhs, rhs); + break; + case SHIFT_RIGHT: + lhs = new RShiftActionItem(null, lhs, rhs); + break; + case USHIFT_RIGHT: + lhs = new URShiftActionItem(null, lhs, rhs); + break; + case BITAND: + lhs = new BitAndActionItem(null, lhs, rhs); + break; + case BITOR: + lhs = new BitOrActionItem(null, lhs, rhs); + break; + case DIVIDE: + lhs = new DivideActionItem(null, lhs, rhs); + break; + case MODULO: + lhs = new ModuloActionItem(null, lhs, rhs); + break; + case EQUALS: + lhs = new EqActionItem(null, lhs, rhs, true/*FIXME SWF version?*/); + break; + case STRICT_EQUALS: + lhs = new StrictEqActionItem(null, lhs, rhs); + break; + case NOT_EQUAL: + lhs = new NeqActionItem(null, lhs, rhs, true/*FIXME SWF version?*/); + break; + case STRICT_NOT_EQUAL: + lhs = new StrictNeqActionItem(null, lhs, rhs); + break; + case LOWER_THAN: + lhs = new LtActionItem(null, lhs, rhs, true/*FIXME SWF version?*/); + break; + case LOWER_EQUAL: + lhs = new LeActionItem(null, lhs, rhs); + break; + case GREATER_THAN: + lhs = new GtActionItem(null, lhs, rhs); + break; + case GREATER_EQUAL: + lhs = new GeActionItem(null, lhs, rhs, true/*FIXME SWF version?*/); + break; + case AND: + lhs = new AndItem(null, lhs, rhs); + break; + case OR: + lhs = new OrItem(null, lhs, rhs); + break; + case FULLAND: + lhs = new AndActionItem(null, lhs, rhs); + break; + case FULLOR: + lhs = new OrActionItem(null, lhs, rhs); + break; + case MINUS: + lhs = new SubtractActionItem(null, lhs, rhs); + break; + case MULTIPLY: + lhs = new MultiplyActionItem(null, lhs, rhs); + break; + case PLUS: + lhs = new AddActionItem(null, lhs, rhs, true); + break; + case XOR: + lhs = new BitXorActionItem(null, lhs, rhs); + break; + case AS: + + break; + case INSTANCEOF: + lhs = new InstanceOfActionItem(null, lhs, rhs); + break; + case IS: + + 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 (lookahead.type) { + case ASSIGN: + //assigned = assigned; + break; + case ASSIGN_BITAND: + assigned = new BitAndActionItem(null, lhs, assigned); + break; + case ASSIGN_BITOR: + assigned = new BitOrActionItem(null, lhs, assigned); + break; + case ASSIGN_DIVIDE: + assigned = new DivideActionItem(null, lhs, assigned); + break; + case ASSIGN_MINUS: + assigned = new SubtractActionItem(null, lhs, assigned); + break; + case ASSIGN_MODULO: + assigned = new ModuloActionItem(null, lhs, assigned); + break; + case ASSIGN_MULTIPLY: + assigned = new MultiplyActionItem(null, lhs, assigned); + break; + case ASSIGN_PLUS: + assigned = new AddActionItem(null, lhs, assigned, true/*TODO:SWF version?*/); + break; + case ASSIGN_SHIFT_LEFT: + assigned = new LShiftActionItem(null, lhs, assigned); + break; + case ASSIGN_SHIFT_RIGHT: + assigned = new RShiftActionItem(null, lhs, assigned); + break; + case ASSIGN_USHIFT_RIGHT: + assigned = new URShiftActionItem(null, lhs, assigned); + break; + case ASSIGN_XOR: + assigned = new BitXorActionItem(null, lhs, assigned); + break; + } + if (lhs instanceof VariableActionItem) { + ((VariableActionItem) lhs).setStoreValue(assigned); + ((VariableActionItem) lhs).setDefinition(false); + lhs = lhs; + } else if (lhs instanceof GetMemberActionItem) { + lhs = new SetMemberActionItem(null, ((GetMemberActionItem) lhs).object, ((GetMemberActionItem) lhs).memberName, assigned); + } else { + throw new ActionParseException("Invalid assignment", lexer.yyline()); + } + break; + case IDENTIFIER: + switch (lookahead.value.toString()) { + case "add": + lhs = new StringAddActionItem(null, lhs, rhs); + break; + case "eq": + lhs = new StringEqActionItem(null, lhs, rhs); + break; + case "ne": + lhs = new StringNeActionItem(null, lhs, rhs); + break; + case "lt": + lhs = new StringLtActionItem(null, lhs, rhs); + break; + case "ge": + lhs = new StringGeActionItem(null, lhs, rhs); + break; + case "gt": + lhs = new StringGtActionItem(null, lhs, rhs); + break; + case "le": + lhs = new StringLeActionItem(null, lhs, rhs); + break; + } + break; + } + } + + switch (lookahead.type) { + case INCREMENT: //postincrement + lex(); + if (!(lhs instanceof VariableActionItem) && !(lhs instanceof GetMemberActionItem)) { + throw new ActionParseException("Invalid assignment", lexer.yyline()); + } + lhs = new PostIncrementActionItem(null, lhs); + break; + case DECREMENT: //postdecrement + lex(); + if (!(lhs instanceof VariableActionItem) && !(lhs instanceof GetMemberActionItem)) { + throw new ActionParseException("Invalid assignment", lexer.yyline()); + } + lhs = new PostDecrementActionItem(null, lhs); + break; + case DOT: //member + case BRACKET_OPEN: //member + case PARENT_OPEN: //function call + lhs = memberOrCall(lhs, inFunction, inMethod, variables); + break; + default: + if (lhs instanceof ParenthesisItem) { + if (isType(((ParenthesisItem) lhs).value)) { + GraphTargetItem expr2 = expression(inFunction, inMethod, true, variables); + if (expr2 != null) { + lhs = new CastOpActionItem(null, ((ParenthesisItem) lhs).value, expr2); + } + } + } + } + if (debugMode) { + System.out.println("/expression1"); + } + return lhs; + } + + private boolean isType(GraphTargetItem item) { + if (item == null) { + return false; + } + while (item instanceof GetMemberActionItem) { + item = ((GetMemberActionItem) item).object; + } + if (item instanceof VariableActionItem) { + return true; + } + return false; + } + + private int brackets(List ret, boolean inFunction, boolean inMethod, List variables) throws IOException, ActionParseException { + 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(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(boolean inFunction, boolean inMethod, int forInLevel, List variables) throws IOException, ActionParseException { + GraphTargetItem cmd = null; + List expr = new ArrayList<>(); + ParsedSymbol s; + do { + cmd = command(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(inFunction, inMethod, true, variables)); + } else { + if (!cmd.hasReturnValue()) { + throw new ActionParseException("Expression expected", lexer.yyline()); + } + } + return new CommaExpressionItem(null, expr); + } + + private GraphTargetItem expressionPrimary(boolean allowEmpty, boolean inFunction, boolean inMethod, boolean allowRemainder, List variables) throws IOException, ActionParseException { + if (debugMode) { + System.out.println("primary:"); + } + GraphTargetItem ret = null; + ParsedSymbol s = lex(); + + switch (s.type) { + case NEGATE: + versionRequired(s, 5); + ret = expression(inFunction, inMethod, false, variables); + ret = new BitXorActionItem(null, ret, new DirectValueActionItem(4.294967295E9)); + + break; + case MINUS: + s = lex(); + if (s.isType(SymbolType.DOUBLE)) { + ret = new DirectValueActionItem(null, 0, -(double) (Double) s.value, new ArrayList()); + + } else if (s.isType(SymbolType.INTEGER)) { + ret = new DirectValueActionItem(null, 0, -(long) (Long) s.value, new ArrayList()); + + } else { + lexer.pushback(s); + GraphTargetItem num = expression(inFunction, inMethod, true, variables); + if ((num instanceof DirectValueActionItem) + && (((DirectValueActionItem) num).value instanceof Long)) { + ((DirectValueActionItem) num).value = -(Long) ((DirectValueActionItem) num).value; + ret = num; + } else if ((num instanceof DirectValueActionItem) + && (((DirectValueActionItem) num).value instanceof Double)) { + Double d = (Double) ((DirectValueActionItem) num).value; + if (d.isInfinite()) { + ((DirectValueActionItem) num).value = Double.NEGATIVE_INFINITY; + } else { + ((DirectValueActionItem) num).value = -d; + } + ret = (num); + } else if ((num instanceof DirectValueActionItem) + && (((DirectValueActionItem) num).value instanceof Float)) { + ((DirectValueActionItem) num).value = -(Float) ((DirectValueActionItem) num).value; + ret = (num); + } else {; + ret = (new SubtractActionItem(null, new DirectValueActionItem(null, 0, (Long) 0L, new ArrayList()), num)); + } + } + break; + case TYPEOF: + ret = new TypeOfActionItem(null, expression(inFunction, inMethod, false, variables)); + + break; + case TRUE: + ret = new DirectValueActionItem(null, 0, Boolean.TRUE, new ArrayList()); + + break; + case NULL: + ret = new DirectValueActionItem(null, 0, new Null(), new ArrayList()); + + break; + case UNDEFINED: + ret = new DirectValueActionItem(null, 0, new Undefined(), new ArrayList()); + break; + case FALSE: + ret = new DirectValueActionItem(null, 0, Boolean.FALSE, new ArrayList()); + + break; + case CURLY_OPEN: //Object literal + s = lex(); + List objectNames = new ArrayList<>(); + List objectValues = new ArrayList<>(); + while (s.type != SymbolType.CURLY_CLOSE) { + if (s.type != SymbolType.COMMA) { + lexer.pushback(s); + } + s = lex(); + expected(s, lexer.yyline(), SymbolType.IDENTIFIER); + objectNames.add(0, pushConst((String) s.value)); + expectedType(SymbolType.COLON); + objectValues.add(0, expression(inFunction, inMethod, true, variables)); + s = lex(); + if (!s.isType(SymbolType.COMMA, SymbolType.CURLY_CLOSE)) { + expected(s, lexer.yyline(), SymbolType.COMMA, SymbolType.CURLY_CLOSE); + } + } + ret = new InitObjectActionItem(null, objectNames, objectValues); + break; + case BRACKET_OPEN: //Array literal or just brackets + lexer.pushback(s); + List inBrackets = new ArrayList<>(); + int arrCnt = brackets(inBrackets, inFunction, inMethod, variables); + ret = new InitArrayActionItem(null, inBrackets); + break; + case FUNCTION: + s = lexer.lex(); + String fname = ""; + if (s.isType(SymbolType.IDENTIFIER, SymbolGroup.GLOBALFUNC)) { + fname = s.value.toString(); + } else { + lexer.pushback(s); + } + ret = function(true, fname, false, variables); + break; + case STRING: + ret = pushConst(s.value.toString()); + ret = memberOrCall(ret, inFunction, inMethod, variables); + + break; + case NEWLINE: + ret = new DirectValueActionItem(null, 0, "\r", new ArrayList()); + + break; + case NAN: + ret = new DirectValueActionItem(null, 0, Double.NaN, new ArrayList()); + + break; + case INFINITY: + ret = new DirectValueActionItem(null, 0, Double.POSITIVE_INFINITY, new ArrayList()); + + break; + case INTEGER: + case DOUBLE: + ret = new DirectValueActionItem(null, 0, s.value, new ArrayList()); + + break; + case DELETE: + GraphTargetItem varDel = variable(inFunction, inMethod, variables); + if (varDel instanceof GetMemberActionItem) { + GetMemberActionItem gm = (GetMemberActionItem) varDel; + ret = new DeleteActionItem(null, gm.object, gm.memberName); + } else { + throw new ActionParseException("Not a property", lexer.yyline()); + } + break; + case INCREMENT: + case DECREMENT: //preincrement + GraphTargetItem prevar = variable(inFunction, inMethod, variables); + if (s.type == SymbolType.INCREMENT) { + ret = new PreIncrementActionItem(null, prevar); + } + if (s.type == SymbolType.DECREMENT) { + ret = new PreDecrementActionItem(null, prevar); + } + + break; + case NOT: + ret = new NotItem(null, expression(inFunction, inMethod, false, variables)); + + break; + case PARENT_OPEN: + ret = new ParenthesisItem(null, expression(inFunction, inMethod, true, variables)); + expectedType(SymbolType.PARENT_CLOSE); + ret = memberOrCall(ret, inFunction, inMethod, variables); + + break; + case NEW: + GraphTargetItem newvar = variable(inFunction, inMethod, variables); + expectedType(SymbolType.PARENT_OPEN); + if (newvar instanceof GetMemberActionItem) { + GetMemberActionItem mem = (GetMemberActionItem) newvar; + ret = new NewMethodActionItem(null, mem.object, mem.memberName, call(inFunction, inMethod, variables)); + } else if (newvar instanceof VariableActionItem) { + ret = new NewObjectActionItem(null, new DirectValueActionItem(((VariableActionItem) newvar).getVariableName()), call(inFunction, inMethod, variables)); + } else { + throw new ActionParseException("Invalid new item", lexer.yyline()); + } + + break; + case EVAL: + expectedType(SymbolType.PARENT_OPEN); + GraphTargetItem evar = new EvalActionItem(null, expression(inFunction, inMethod, true, variables)); + expectedType(SymbolType.PARENT_CLOSE); + evar = memberOrCall(evar, inFunction, inMethod, variables); + ret = evar; + + break; + case IDENTIFIER: + case THIS: + case SUPER: + if (s.value.equals("not")) { + ret = new NotItem(null, expression(inFunction, inMethod, false, variables)); + } else { + lexer.pushback(s); + GraphTargetItem var = variable(inFunction, inMethod, variables); + var = memberOrCall(var, inFunction, inMethod, variables); + ret = var; + } + + break; + default: + GraphTargetItem excmd = expressionCommands(s, inFunction, inMethod, -1, variables); + if (excmd != null) { + //? + ret = excmd; + break; + } + lexer.pushback(s); + } + + if (debugMode) { + System.out.println("/primary"); + } + return ret; + } + + private DirectValueActionItem pushConst(String s) throws IOException, ActionParseException { + int index = constantPool.indexOf(s); + if (index == -1) { + constantPool.add(s); + index = constantPool.indexOf(s); + } + return new DirectValueActionItem(null, 0, new ConstantIndex(index), constantPool); + } + + private ActionScriptLexer lexer = null; + + private List constantPool; + + public List treeFromString(String str, List constantPool) throws ActionParseException, IOException { + List retTree = new ArrayList<>(); + this.constantPool = constantPool; + lexer = new ActionScriptLexer(new StringReader(str)); + + List vars = new ArrayList<>(); + retTree.addAll(commands(false, false, 0, vars)); + for (VariableActionItem v : vars) { + String varName = v.getVariableName(); + GraphTargetItem stored = v.getStoreValue(); + if (v.isDefinition()) { + v.setBoxedValue(new DefineLocalActionItem(null, pushConst(varName), stored)); + } else { + if (stored != null) { + v.setBoxedValue(new SetVariableActionItem(null, pushConst(varName), stored)); + } else { + v.setBoxedValue(new GetVariableActionItem(null, pushConst(varName))); + } + } + } + if (lexer.lex().type != SymbolType.EOF) { + throw new ActionParseException("Parsing finished before end of the file", lexer.yyline()); + } + return retTree; + } + + public List actionsFromTree(List tree, List constantPool) throws CompilationException { + ActionSourceGenerator gen = new ActionSourceGenerator(swfVersion, constantPool); + List ret = new ArrayList<>(); + SourceGeneratorLocalData localData = new SourceGeneratorLocalData( + new HashMap(), 0, Boolean.FALSE, 0); + List srcList = gen.generate(localData, tree); + for (GraphSourceItem s : srcList) { + if (s instanceof Action) { + ret.add((Action) s); + } + } + ret.add(0, new ActionConstantPool(constantPool)); + return ret; + } + + public List actionsFromString(String s) throws ActionParseException, IOException, CompilationException { + List constantPool = new ArrayList<>(); + List tree = treeFromString(s, constantPool); + return actionsFromTree(tree, constantPool); + } + + private void versionRequired(ParsedSymbol s, int min) throws ActionParseException { + versionRequired(s.value.toString(), min, Integer.MAX_VALUE); + } + + private void versionRequired(ParsedSymbol s, int min, int max) throws ActionParseException { + versionRequired(s.value.toString(), min, max); + } + + private void versionRequired(String type, int min, int max) throws ActionParseException { + if (min == max && swfVersion != min) { + throw new ActionParseException(type + " requires SWF version " + min, lexer.yyline()); + } + if (swfVersion < min) { + throw new ActionParseException(type + " requires at least SWF version " + min, lexer.yyline()); + } + if (swfVersion > max) { + throw new ActionParseException(type + " requires SWF version lower than " + max, lexer.yyline()); + } + } +}