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