/* * 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.action.parser.script; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.SourceGeneratorLocalData; import com.jpexs.decompiler.flash.action.Action; import com.jpexs.decompiler.flash.action.model.DirectValueActionItem; import com.jpexs.decompiler.flash.action.model.FunctionActionItem; import com.jpexs.decompiler.flash.action.model.GetMemberActionItem; import com.jpexs.decompiler.flash.action.model.GetVariableActionItem; import com.jpexs.decompiler.flash.action.model.operations.Inverted; import com.jpexs.decompiler.flash.action.swf4.ActionGetVariable; import com.jpexs.decompiler.flash.action.swf4.ActionIf; import com.jpexs.decompiler.flash.action.swf4.ActionJump; import com.jpexs.decompiler.flash.action.swf4.ActionNot; import com.jpexs.decompiler.flash.action.swf4.ActionPop; import com.jpexs.decompiler.flash.action.swf4.ActionPush; import com.jpexs.decompiler.flash.action.swf4.ActionSetVariable; import com.jpexs.decompiler.flash.action.swf4.ConstantIndex; import com.jpexs.decompiler.flash.action.swf4.RegisterNumber; import com.jpexs.decompiler.flash.action.swf5.ActionCallFunction; import com.jpexs.decompiler.flash.action.swf5.ActionDefineFunction; import com.jpexs.decompiler.flash.action.swf5.ActionGetMember; import com.jpexs.decompiler.flash.action.swf5.ActionNewObject; import com.jpexs.decompiler.flash.action.swf5.ActionPushDuplicate; import com.jpexs.decompiler.flash.action.swf5.ActionSetMember; import com.jpexs.decompiler.flash.action.swf5.ActionStoreRegister; import com.jpexs.decompiler.flash.action.swf6.ActionStrictEquals; import com.jpexs.decompiler.flash.action.swf7.ActionExtends; import com.jpexs.decompiler.flash.action.swf7.ActionImplementsOp; import com.jpexs.decompiler.flash.ecma.Null; import com.jpexs.decompiler.flash.helpers.collections.MyEntry; import com.jpexs.decompiler.graph.CompilationException; import com.jpexs.decompiler.graph.GraphSourceItem; import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.SourceGenerator; import com.jpexs.decompiler.graph.TypeItem; import com.jpexs.decompiler.graph.model.AndItem; 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.FalseItem; 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.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.TrueItem; import com.jpexs.decompiler.graph.model.WhileItem; import com.jpexs.helpers.Helper; import java.util.ArrayList; import java.util.HashMap; import java.util.List; /** * * @author JPEXS */ public class ActionSourceGenerator implements SourceGenerator { @Override public List<GraphSourceItem> generate(SourceGeneratorLocalData localData, FalseItem item) throws CompilationException { return GraphTargetItem.toSourceMerge(localData, this, new ActionPush(Boolean.FALSE)); } @Override public List<GraphSourceItem> generate(SourceGeneratorLocalData localData, TrueItem item) throws CompilationException { return GraphTargetItem.toSourceMerge(localData, this, new ActionPush(Boolean.TRUE)); } @Override public List<GraphSourceItem> generate(SourceGeneratorLocalData localData, AndItem item) throws CompilationException { List<GraphSourceItem> ret = new ArrayList<>(); ret.addAll(generateToActionList(localData, item.leftSide)); ret.add(new ActionPushDuplicate()); ret.add(new ActionNot()); List<Action> andExpr = generateToActionList(localData, item.rightSide); andExpr.add(0, new ActionPop()); int andExprLen = Action.actionsToBytes(andExpr, false, SWF.DEFAULT_VERSION).length; ret.add(new ActionIf(andExprLen)); ret.addAll(andExpr); return ret; } @Override public List<GraphSourceItem> generate(SourceGeneratorLocalData localData, OrItem item) throws CompilationException { List<GraphSourceItem> ret = new ArrayList<>(); ret.addAll(generateToActionList(localData, item.leftSide)); ret.add(new ActionPushDuplicate()); List<Action> orExpr = generateToActionList(localData, item.rightSide); orExpr.add(0, new ActionPop()); int orExprLen = Action.actionsToBytes(orExpr, false, SWF.DEFAULT_VERSION).length; ret.add(new ActionIf(orExprLen)); ret.addAll(orExpr); return ret; } public List<Action> toActionList(List<GraphSourceItem> items) { List<Action> ret = new ArrayList<>(); for (GraphSourceItem s : items) { if (s instanceof Action) { ret.add((Action) s); } } return ret; } private List<Action> nonempty(List<Action> list) { if (list == null) { return new ArrayList<>(); } return list; } private List<GraphSourceItem> generateIf(SourceGeneratorLocalData localData, GraphTargetItem expression, List<GraphTargetItem> onTrueCmds, List<GraphTargetItem> onFalseCmds, boolean ternar) throws CompilationException { List<GraphSourceItem> ret = new ArrayList<>(); if (expression instanceof Inverted) { ret.addAll(((Inverted) expression).invert(null).toSource(localData, this)); } else { ret.addAll(expression.toSource(localData, this)); ret.add(new ActionNot()); } List<Action> onTrue; List<Action> onFalse = null; if (ternar) { onTrue = toActionList(onTrueCmds.get(0).toSource(localData, this)); } else { onTrue = generateToActionList(localData, onTrueCmds); } if (onFalseCmds != null && !onFalseCmds.isEmpty()) { if (ternar) { onFalse = toActionList(onFalseCmds.get(0).toSource(localData, this)); } else { onFalse = generateToActionList(localData, onFalseCmds); } } byte[] onTrueBytes = Action.actionsToBytes(onTrue, false, SWF.DEFAULT_VERSION); int onTrueLen = onTrueBytes.length; ActionIf ifaif = new ActionIf(0); ret.add(ifaif); ret.addAll(onTrue); ifaif.setJumpOffset(onTrueLen); ActionJump ajmp = null; if (onFalse != null) { if (!((!nonempty(onTrue).isEmpty()) && (onTrue.get(onTrue.size() - 1) instanceof ActionJump) && ((((ActionJump) onTrue.get(onTrue.size() - 1)).isContinue) || (((ActionJump) onTrue.get(onTrue.size() - 1)).isBreak)))) { ajmp = new ActionJump(0); ret.add(ajmp); onTrueLen += ajmp.getTotalActionLength(); } ifaif.setJumpOffset(onTrueLen); byte[] onFalseBytes = Action.actionsToBytes(onFalse, false, SWF.DEFAULT_VERSION); int onFalseLen = onFalseBytes.length; if (ajmp != null) { ajmp.setJumpOffset(onFalseLen); } ret.addAll(onFalse); } return ret; } @Override public List<GraphSourceItem> generate(SourceGeneratorLocalData localData, IfItem item) throws CompilationException { return generateIf(localData, item.expression, item.onTrue, item.onFalse, false); } private void fixLoop(List<Action> code, int breakOffset) { fixLoop(code, breakOffset, Integer.MAX_VALUE); } private void fixLoop(List<Action> code, int breakOffset, int continueOffset) { int pos = 0; for (Action a : code) { pos += a.getTotalActionLength(); if (a instanceof ActionJump) { ActionJump aj = (ActionJump) a; if (aj.isContinue && (continueOffset != Integer.MAX_VALUE)) { aj.setJumpOffset(-pos + continueOffset); aj.isContinue = false; } if (aj.isBreak) { aj.setJumpOffset(-pos + breakOffset); aj.isBreak = false; } } } } @Override public List<GraphSourceItem> generate(SourceGeneratorLocalData localData, TernarOpItem item) throws CompilationException { List<GraphTargetItem> onTrue = new ArrayList<>(); onTrue.add(item.onTrue); List<GraphTargetItem> onFalse = new ArrayList<>(); onFalse.add(item.onFalse); return generateIf(localData, item.expression, onTrue, onFalse, true); } @Override public List<GraphSourceItem> generate(SourceGeneratorLocalData localData, WhileItem item) throws CompilationException { List<GraphSourceItem> ret = new ArrayList<>(); List<Action> whileExpr = new ArrayList<>(); List<GraphTargetItem> ex = new ArrayList<>(item.expression); if (!ex.isEmpty()) { GraphTargetItem lastItem = ex.remove(ex.size() - 1); whileExpr.addAll(generateToActionList(localData, ex)); whileExpr.addAll(toActionList(lastItem.toSource(localData, this))); //Want result } List<Action> whileBody = generateToActionList(localData, item.commands); whileExpr.add(new ActionNot()); ActionIf whileaif = new ActionIf(0); whileExpr.add(whileaif); ActionJump whileajmp = new ActionJump(0); whileBody.add(whileajmp); int whileExprLen = Action.actionsToBytes(whileExpr, false, SWF.DEFAULT_VERSION).length; int whileBodyLen = Action.actionsToBytes(whileBody, false, SWF.DEFAULT_VERSION).length; whileajmp.setJumpOffset(-(whileExprLen + whileBodyLen)); whileaif.setJumpOffset(whileBodyLen); ret.addAll(whileExpr); fixLoop(whileBody, whileBodyLen, -whileExprLen); ret.addAll(whileBody); return ret; } @Override public List<GraphSourceItem> generate(SourceGeneratorLocalData localData, DoWhileItem item) throws CompilationException { List<GraphSourceItem> ret = new ArrayList<>(); List<Action> doExpr = generateToActionList(localData, item.expression); List<Action> doBody = generateToActionList(localData, item.commands); int doBodyLen = Action.actionsToBytes(doBody, false, SWF.DEFAULT_VERSION).length; int doExprLen = Action.actionsToBytes(doExpr, false, SWF.DEFAULT_VERSION).length; ret.addAll(doBody); ret.addAll(doExpr); ActionIf doif = new ActionIf(0); ret.add(doif); int offset = doBodyLen + doExprLen + doif.getTotalActionLength(); doif.setJumpOffset(-offset); fixLoop(doBody, offset, doBodyLen); return ret; } @Override public List<GraphSourceItem> generate(SourceGeneratorLocalData localData, ForItem item) throws CompilationException { List<GraphSourceItem> ret = new ArrayList<>(); List<Action> forExpr = generateToActionList(localData, item.expression); List<Action> forBody = generateToActionList(localData, item.commands); List<Action> forFinalCommands = generateToActionList(localData, item.finalCommands); forExpr.add(new ActionNot()); ActionIf foraif = new ActionIf(0); forExpr.add(foraif); ActionJump forajmp = new ActionJump(0); int forajmpLen = forajmp.getTotalActionLength(); int forExprLen = Action.actionsToBytes(forExpr, false, SWF.DEFAULT_VERSION).length; int forBodyLen = Action.actionsToBytes(forBody, false, SWF.DEFAULT_VERSION).length; int forFinalLen = Action.actionsToBytes(forFinalCommands, false, SWF.DEFAULT_VERSION).length; forajmp.setJumpOffset(-(forExprLen + forBodyLen + forFinalLen + forajmpLen)); foraif.setJumpOffset(forBodyLen + forFinalLen + forajmpLen); ret.addAll(forExpr); ret.addAll(forBody); ret.addAll(forFinalCommands); ret.add(forajmp); fixLoop(forBody, forBodyLen + forFinalLen + forajmpLen, forBodyLen); return ret; } private long uniqLast = 0; public String uniqId() { uniqLast++; return "" + uniqLast; } @Override public List<GraphSourceItem> generate(SourceGeneratorLocalData localData, SwitchItem item) throws CompilationException { List<GraphSourceItem> ret = new ArrayList<>(); HashMap<String, Integer> registerVars = getRegisterVars(localData); int exprReg = 0; for (int i = 0; i < 256; i++) { if (!registerVars.containsValue(i)) { registerVars.put("__switch" + uniqId(), i); exprReg = i; break; } } ret.addAll(toActionList(item.switchedObject.toSource(localData, this))); boolean firstCase = true; List<List<ActionIf>> caseIfs = new ArrayList<>(); List<List<Action>> caseCmds = new ArrayList<>(); List<List<List<Action>>> caseExprsAll = new ArrayList<>(); int defaultPos = -1; loopm: for (int m = 0; m < item.caseValues.size(); m++) { List<List<Action>> caseExprs = new ArrayList<>(); List<ActionIf> caseIfsOne = new ArrayList<>(); int mapping = item.valuesMapping.get(m); for (; m < item.caseValues.size(); m++) { int newmapping = item.valuesMapping.get(m); if (newmapping != mapping) { m--; break; } if (item.caseValues.get(m) instanceof DefaultItem) { defaultPos = caseIfs.size(); } else { List<Action> curCaseExpr = generateToActionList(localData, item.caseValues.get(m)); caseExprs.add(curCaseExpr); if (firstCase) { curCaseExpr.add(0, new ActionStoreRegister(exprReg)); } else { curCaseExpr.add(0, new ActionPush(new RegisterNumber(exprReg))); } curCaseExpr.add(new ActionStrictEquals()); ActionIf aif = new ActionIf(0); caseIfsOne.add(aif); curCaseExpr.add(aif); ret.addAll(curCaseExpr); } firstCase = false; } caseExprsAll.add(caseExprs); caseIfs.add(caseIfsOne); List<Action> caseCmd = generateToActionList(localData, item.caseCommands.get(mapping)); caseCmds.add(caseCmd); } ActionJump defJump = new ActionJump(0); ret.add(defJump); for (List<Action> caseCmd : caseCmds) { ret.addAll(caseCmd); } List<List<Integer>> exprLengths = new ArrayList<>(); for (List<List<Action>> caseExprs : caseExprsAll) { List<Integer> lengths = new ArrayList<>(); for (List<Action> caseExpr : caseExprs) { lengths.add(Action.actionsToBytes(caseExpr, false, SWF.DEFAULT_VERSION).length); } exprLengths.add(lengths); } List<Integer> caseLengths = new ArrayList<>(); for (List<Action> caseCmd : caseCmds) { caseLengths.add(Action.actionsToBytes(caseCmd, false, SWF.DEFAULT_VERSION).length); } for (int i = 0; i < caseIfs.size(); i++) { for (int c = 0; c < caseIfs.get(i).size(); c++) { int jmpPos = 0; for (int j = c + 1; j < caseIfs.get(i).size(); j++) { jmpPos += exprLengths.get(i).get(j); } for (int k = i + 1; k < caseIfs.size(); k++) { for (int m = 0; m < caseIfs.get(k).size(); m++) { jmpPos += exprLengths.get(k).get(m); } } jmpPos += defJump.getTotalActionLength(); for (int n = 0; n < i; n++) { jmpPos += caseLengths.get(n); } caseIfs.get(i).get(c).setJumpOffset(jmpPos); } } int defJmpPos = 0; for (int i = 0; i < caseIfs.size(); i++) { if (defaultPos == -1 || i < defaultPos) { defJmpPos += caseLengths.get(i); } } defJump.setJumpOffset(defJmpPos); List<Action> caseCmdsAll = new ArrayList<>(); int breakOffset = 0; for (int i = 0; i < caseCmds.size(); i++) { caseCmdsAll.addAll(caseCmds.get(i)); breakOffset += caseLengths.get(i); } fixLoop(caseCmdsAll, breakOffset); return ret; } @Override public List<GraphSourceItem> generate(SourceGeneratorLocalData localData, NotItem item) throws CompilationException { if (item.getOriginal() instanceof Inverted) { GraphTargetItem norig = ((Inverted) item).invert(null); return norig.toSource(localData, this); } List<GraphSourceItem> ret = new ArrayList<>(); ret.addAll(item.getOriginal().toSource(localData, this)); ret.add(new ActionNot()); return ret; } @Override public List<GraphSourceItem> generate(SourceGeneratorLocalData localData, DuplicateItem item) { List<GraphSourceItem> ret = new ArrayList<>(); ret.add(new ActionPushDuplicate()); return ret; } @Override public List<GraphSourceItem> generate(SourceGeneratorLocalData localData, BreakItem item) { List<GraphSourceItem> ret = new ArrayList<>(); ActionJump abreak = new ActionJump(0); abreak.isBreak = true; ret.add(abreak); return ret; } @Override public List<GraphSourceItem> generate(SourceGeneratorLocalData localData, ContinueItem item) { List<GraphSourceItem> ret = new ArrayList<>(); ActionJump acontinue = new ActionJump(0); acontinue.isContinue = true; ret.add(acontinue); return ret; } private List<Action> generateToActionList(SourceGeneratorLocalData localData, List<GraphTargetItem> commands) throws CompilationException { return toActionList(generate(localData, commands)); } private List<Action> generateToActionList(SourceGeneratorLocalData localData, GraphTargetItem command) throws CompilationException { return toActionList(command.toSource(localData, this)); } @Override public List<GraphSourceItem> generate(SourceGeneratorLocalData localData, List<GraphTargetItem> commands) throws CompilationException { List<GraphSourceItem> ret = new ArrayList<>(); for (GraphTargetItem item : commands) { ret.addAll(item.toSourceIgnoreReturnValue(localData, this)); } return ret; } public HashMap<String, Integer> getRegisterVars(SourceGeneratorLocalData localData) { return localData.registerVars; } public void setRegisterVars(SourceGeneratorLocalData localData, HashMap<String, Integer> value) { localData.registerVars = value; } public void setInFunction(SourceGeneratorLocalData localData, int value) { localData.inFunction = value; } public int isInFunction(SourceGeneratorLocalData localData) { return localData.inFunction; } public boolean isInMethod(SourceGeneratorLocalData localData) { return localData.inMethod; } public void setInMethod(SourceGeneratorLocalData localData, boolean value) { localData.inMethod = value; } public int getForInLevel(SourceGeneratorLocalData localData) { return localData.forInLevel; } public void setForInLevel(SourceGeneratorLocalData localData, int value) { localData.forInLevel = value; } public int getTempRegister(SourceGeneratorLocalData localData) { HashMap<String, Integer> registerVars = getRegisterVars(localData); for (int tmpReg = 0; tmpReg < 256; tmpReg++) { if (!registerVars.containsValue(tmpReg)) { registerVars.put("__temp" + tmpReg, tmpReg); return tmpReg; } } return 0; //? } public void releaseTempRegister(SourceGeneratorLocalData localData, int tmp) { HashMap<String, Integer> registerVars = getRegisterVars(localData); registerVars.remove("__temp" + tmp); } private String getName(GraphTargetItem item) { if (item instanceof VariableActionItem) { return ((VariableActionItem) item).getVariableName(); } if (item instanceof DirectValueActionItem) { DirectValueActionItem dv = (DirectValueActionItem) item; return (String) dv.getResult(); } if (item instanceof GetVariableActionItem) { GetVariableActionItem gv = (GetVariableActionItem) item; return getName(gv.name); } if (item instanceof GetMemberActionItem) { GetMemberActionItem mem = (GetMemberActionItem) item; return getName(mem.memberName); } return null; } private List<String> getVarParts(GraphTargetItem item) { List<String> ret = new ArrayList<>(); do { if (item instanceof GetMemberActionItem) { GetMemberActionItem mem = (GetMemberActionItem) item; ret.add(0, getName(mem)); item = mem.object; } } while (item instanceof GetMemberActionItem); String f = getName(item); if (f != null) { ret.add(0, f); } return ret; } private int getVarLength(GraphTargetItem item) { int len = 1; do { if (item instanceof GetMemberActionItem) { GetMemberActionItem mem = (GetMemberActionItem) item; item = mem.object; len++; } } while (item instanceof GetMemberActionItem); return len; } private GraphTargetItem removeVarLast(GraphTargetItem item, int cnt) { item = Helper.deepCopy(item); for (int i = 0; i < cnt; i++) { if (item instanceof GetMemberActionItem) { GetMemberActionItem mem = (GetMemberActionItem) item; item = mem.object; } } return item; } private GraphTargetItem addGlobalPrefix(GraphTargetItem item) { item = Helper.deepCopy(item); GraphTargetItem first = item; GetMemberActionItem mem = null; do { if (item instanceof GetMemberActionItem) { mem = (GetMemberActionItem) item; item = mem.object; } } while (item instanceof GetMemberActionItem); if (item instanceof GetVariableActionItem) { GetVariableActionItem v = (GetVariableActionItem) item; item = new GetMemberActionItem(null, null, new GetVariableActionItem(null, null, new DirectValueActionItem(null, null, 0, "_global", new ArrayList<>())), v.name); if (mem != null) { mem.object = item; } } return first; } private List<Action> typeToActions(List<String> type, List<Action> value) { List<Action> ret = new ArrayList<>(); if (type.isEmpty()) { return ret; } ret.add(pushConst(type.get(0))); if (type.size() == 1 && (value != null)) { ret.addAll(value); ret.add(new ActionSetVariable()); } else { ret.add(new ActionGetVariable()); } for (int i = 1; i < type.size(); i++) { ret.add(pushConst(type.get(i))); if ((i == type.size() - 1) && (value != null)) { ret.addAll(value); ret.add(new ActionSetMember()); } else { ret.add(new ActionGetMember()); } } return ret; } private final List<String> constantPool; private final int swfVersion; public int getSwfVersion() { return swfVersion; } public ActionSourceGenerator(int swfVersion, List<String> constantPool) { this.constantPool = constantPool; this.swfVersion = swfVersion; } public List<String> getConstantPool() { return constantPool; } public DirectValueActionItem pushConstTargetItem(String s) { int index = constantPool.indexOf(s); if (index == -1) { constantPool.add(s); index = constantPool.indexOf(s); } return new DirectValueActionItem(null, null, 0, new ConstantIndex(index), constantPool); } public ActionPush pushConst(String s) { int index = constantPool.indexOf(s); if (index == -1) { constantPool.add(s); index = constantPool.indexOf(s); } return new ActionPush(new ConstantIndex(index)); } public List<GraphSourceItem> generateTraits(SourceGeneratorLocalData localData, boolean isInterface, GraphTargetItem name, GraphTargetItem extendsVal, List<GraphTargetItem> implementsStr, GraphTargetItem constructor, List<GraphTargetItem> functions, List<MyEntry<GraphTargetItem, GraphTargetItem>> vars, List<GraphTargetItem> staticFunctions, List<MyEntry<GraphTargetItem, GraphTargetItem>> staticVars) throws CompilationException { List<String> extendsStr = getVarParts(extendsVal); List<GraphSourceItem> ret = new ArrayList<>(); List<String> nameStr = getVarParts(name); for (int i = 0; i < nameStr.size() - 1; i++) { List<Action> notBody = new ArrayList<>(); List<String> globalClassTypeStr = new ArrayList<>(); globalClassTypeStr.add("_global"); for (int j = 0; j <= i; j++) { globalClassTypeStr.add(nameStr.get(j)); } List<Action> val = new ArrayList<>(); val.add(new ActionPush((Long) 0L)); val.add(pushConst("Object")); val.add(new ActionNewObject()); notBody.addAll(typeToActions(globalClassTypeStr, val)); ret.addAll(typeToActions(globalClassTypeStr, null)); ret.add(new ActionNot()); ret.add(new ActionNot()); ret.add(new ActionIf(Action.actionsToBytes(notBody, false, SWF.DEFAULT_VERSION).length)); ret.addAll(notBody); } List<Action> ifbody = new ArrayList<>(); List<String> globalClassTypeStr = new ArrayList<>(); globalClassTypeStr.add("_global"); globalClassTypeStr.addAll(nameStr); ParsedSymbol s = null; List<Action> constr = new ArrayList<>(); if (constructor == null) { List<Action> val = new ArrayList<>(); val.add(new ActionDefineFunction("", new ArrayList<>(), 0, SWF.DEFAULT_VERSION)); if (!isInterface) { val.add(new ActionStoreRegister(1)); } constr.addAll(typeToActions(globalClassTypeStr, val)); } else { constr.addAll(toActionList(((FunctionActionItem) constructor).toSource(localData, this))); constr.add(new ActionStoreRegister(1)); constr = (typeToActions(globalClassTypeStr, constr)); } if (!isInterface) { for (GraphTargetItem f : staticFunctions) { FunctionActionItem fi = (FunctionActionItem) f; ifbody.add(new ActionPush(new RegisterNumber(1/*static*/))); ifbody.add(new ActionPush(getName(fi.calculatedFunctionName))); ifbody.addAll(toActionList(fi.toSource(localData, this))); ifbody.add(new ActionSetMember()); } for (GraphTargetItem f : functions) { FunctionActionItem fi = (FunctionActionItem) f; ifbody.add(new ActionPush(new RegisterNumber(2/*instance*/))); ifbody.add(new ActionPush(getName(fi.calculatedFunctionName))); ifbody.addAll(toActionList(fi.toSource(localData, this))); ifbody.add(new ActionSetMember()); } for (MyEntry<GraphTargetItem, GraphTargetItem> en : staticVars) { ifbody.add(new ActionPush(new RegisterNumber(1/*static*/))); ifbody.add(new ActionPush(getName(en.getKey()))); ifbody.addAll(toActionList(en.getValue().toSource(localData, this))); ifbody.add(new ActionSetMember()); } for (MyEntry<GraphTargetItem, GraphTargetItem> en : vars) { ifbody.add(new ActionPush(new RegisterNumber(2/*instance*/))); ifbody.add(new ActionPush(getName(en.getKey()))); ifbody.addAll(toActionList(en.getValue().toSource(localData, this))); ifbody.add(new ActionSetMember()); } } if (!isInterface) { ifbody.add(new ActionPush((Long) 1L)); ifbody.add(new ActionPush(Null.INSTANCE)); ifbody.addAll(typeToActions(globalClassTypeStr, null)); ifbody.add(pushConst("prototype")); ifbody.add(new ActionGetMember()); ifbody.add(new ActionPush((Long) 3L)); ifbody.add(pushConst("ASSetPropFlags")); ifbody.add(new ActionCallFunction()); } if (constr.isEmpty()) { List<Action> val = new ArrayList<>(); val.add(new ActionDefineFunction("", new ArrayList<>(), 0, SWF.DEFAULT_VERSION)); if (!isInterface) { val.add(new ActionStoreRegister(1)); } constr.addAll(typeToActions(globalClassTypeStr, val)); } if (!extendsStr.isEmpty()) { constr.addAll(typeToActions(globalClassTypeStr, null)); constr.addAll(typeToActions(extendsStr, null)); constr.add(new ActionExtends()); } if (!isInterface) { constr.add(new ActionPush(new RegisterNumber(1))); constr.add(pushConst("prototype")); constr.add(new ActionGetMember()); constr.add(new ActionStoreRegister(2)); constr.add(new ActionPop()); } if (!implementsStr.isEmpty()) { for (GraphTargetItem imp : implementsStr) { List<String> impList = getVarParts(imp); List<String> globImp = new ArrayList<>(); globImp.add("_global"); globImp.addAll(impList); constr.addAll(typeToActions(globImp, null)); } constr.add(new ActionPush((long) implementsStr.size())); constr.addAll(typeToActions(globalClassTypeStr, null)); constr.add(new ActionImplementsOp()); } ifbody.addAll(0, constr); ret.addAll(typeToActions(globalClassTypeStr, null)); ret.add(new ActionNot()); ret.add(new ActionNot()); ret.add(new ActionIf(Action.actionsToBytes(ifbody, false, SWF.DEFAULT_VERSION).length)); ret.addAll(ifbody); ret.add(new ActionPop()); return ret; } @Override public List<GraphSourceItem> generate(SourceGeneratorLocalData localData, CommaExpressionItem item) throws CompilationException { if (item.commands.isEmpty()) { return new ArrayList<>(); } //We need to handle commands and last expression separately, otherwise last expression result will be popped List<GraphTargetItem> cmds = new ArrayList<>(item.commands); GraphTargetItem lastExpr = cmds.remove(cmds.size() - 1); List<GraphSourceItem> ret = new ArrayList<>(); ret.addAll(generate(localData, cmds)); ret.addAll(lastExpr.toSource(localData, this)); return ret; } @Override public List<GraphSourceItem> generate(SourceGeneratorLocalData localData, TypeItem item) throws CompilationException { //Unsupported in AS1/2 return new ArrayList<>(); } @Override public List<GraphSourceItem> generateDiscardValue(SourceGeneratorLocalData localData, GraphTargetItem item) throws CompilationException { List<GraphSourceItem> ret = item.toSource(localData, this); ret.add(new ActionPop()); return ret; } @Override public List<GraphSourceItem> generate(SourceGeneratorLocalData localData, PushItem item) throws CompilationException { return item.value.toSource(localData, this); } @Override public List<GraphSourceItem> generate(SourceGeneratorLocalData localData, PopItem item) throws CompilationException { List<GraphSourceItem> ret = new ArrayList<>(); return ret; } }