diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java index 3c8698a18..58ba8a284 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java @@ -1,2809 +1,2821 @@ -/* - * Copyright (C) 2010-2015 JPEXS, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -package com.jpexs.decompiler.flash; - -import SevenZip.Compression.LZMA.Decoder; -import SevenZip.Compression.LZMA.Encoder; -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.CachedDecompilation; -import com.jpexs.decompiler.flash.abc.ClassPath; -import com.jpexs.decompiler.flash.abc.RenameType; -import com.jpexs.decompiler.flash.abc.ScriptPack; -import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; -import com.jpexs.decompiler.flash.abc.types.MethodBody; -import com.jpexs.decompiler.flash.abc.types.ScriptInfo; -import com.jpexs.decompiler.flash.action.Action; -import com.jpexs.decompiler.flash.action.ActionGraphSource; -import com.jpexs.decompiler.flash.action.ActionList; -import com.jpexs.decompiler.flash.action.ActionLocalData; -import com.jpexs.decompiler.flash.action.CachedScript; -import com.jpexs.decompiler.flash.action.model.ConstantPool; -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.clauses.ClassActionItem; -import com.jpexs.decompiler.flash.action.model.clauses.InterfaceActionItem; -import com.jpexs.decompiler.flash.action.swf4.ActionEquals; -import com.jpexs.decompiler.flash.action.swf4.ActionGetVariable; -import com.jpexs.decompiler.flash.action.swf4.ActionIf; -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.swf5.ActionCallFunction; -import com.jpexs.decompiler.flash.action.swf5.ActionCallMethod; -import com.jpexs.decompiler.flash.action.swf5.ActionConstantPool; -import com.jpexs.decompiler.flash.action.swf5.ActionDefineFunction; -import com.jpexs.decompiler.flash.action.swf5.ActionDefineLocal; -import com.jpexs.decompiler.flash.action.swf5.ActionDefineLocal2; -import com.jpexs.decompiler.flash.action.swf5.ActionEquals2; -import com.jpexs.decompiler.flash.action.swf5.ActionGetMember; -import com.jpexs.decompiler.flash.action.swf5.ActionNewMethod; -import com.jpexs.decompiler.flash.action.swf5.ActionNewObject; -import com.jpexs.decompiler.flash.action.swf5.ActionSetMember; -import com.jpexs.decompiler.flash.action.swf7.ActionDefineFunction2; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.dumpview.DumpInfo; -import com.jpexs.decompiler.flash.dumpview.DumpInfoSwfNode; -import com.jpexs.decompiler.flash.ecma.Null; -import com.jpexs.decompiler.flash.exporters.commonshape.ExportRectangle; -import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; -import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter; -import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; -import com.jpexs.decompiler.flash.exporters.script.AS2ScriptExporter; -import com.jpexs.decompiler.flash.exporters.script.AS3ScriptExporter; -import com.jpexs.decompiler.flash.exporters.settings.ScriptExportSettings; -import com.jpexs.decompiler.flash.helpers.HighlightedText; -import com.jpexs.decompiler.flash.helpers.HighlightedTextWriter; -import com.jpexs.decompiler.flash.helpers.ImageHelper; -import com.jpexs.decompiler.flash.helpers.SWFDecompilerPlugin; -import com.jpexs.decompiler.flash.helpers.collections.MyEntry; -import com.jpexs.decompiler.flash.helpers.hilight.Highlighting; -import com.jpexs.decompiler.flash.tags.ABCContainerTag; -import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag; -import com.jpexs.decompiler.flash.tags.DefineButton2Tag; -import com.jpexs.decompiler.flash.tags.DefineButtonTag; -import com.jpexs.decompiler.flash.tags.DefineSpriteTag; -import com.jpexs.decompiler.flash.tags.DoInitActionTag; -import com.jpexs.decompiler.flash.tags.EndTag; -import com.jpexs.decompiler.flash.tags.ExportAssetsTag; -import com.jpexs.decompiler.flash.tags.FileAttributesTag; -import com.jpexs.decompiler.flash.tags.JPEGTablesTag; -import com.jpexs.decompiler.flash.tags.ShowFrameTag; -import com.jpexs.decompiler.flash.tags.SymbolClassTag; -import com.jpexs.decompiler.flash.tags.Tag; -import com.jpexs.decompiler.flash.tags.TagStub; -import com.jpexs.decompiler.flash.tags.VideoFrameTag; -import com.jpexs.decompiler.flash.tags.base.ASMSource; -import com.jpexs.decompiler.flash.tags.base.ASMSourceContainer; -import com.jpexs.decompiler.flash.tags.base.BoundedTag; -import com.jpexs.decompiler.flash.tags.base.ButtonTag; -import com.jpexs.decompiler.flash.tags.base.CharacterIdTag; -import com.jpexs.decompiler.flash.tags.base.CharacterTag; -import com.jpexs.decompiler.flash.tags.base.DrawableTag; -import com.jpexs.decompiler.flash.tags.base.Exportable; -import com.jpexs.decompiler.flash.tags.base.FontTag; -import com.jpexs.decompiler.flash.tags.base.ImageTag; -import com.jpexs.decompiler.flash.tags.base.MorphShapeTag; -import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; -import com.jpexs.decompiler.flash.tags.base.RemoveTag; -import com.jpexs.decompiler.flash.tags.base.RenderContext; -import com.jpexs.decompiler.flash.tags.base.ShapeTag; -import com.jpexs.decompiler.flash.tags.base.SoundTag; -import com.jpexs.decompiler.flash.tags.base.TextTag; -import com.jpexs.decompiler.flash.tags.enums.ImageFormat; -import com.jpexs.decompiler.flash.timeline.AS2Package; -import com.jpexs.decompiler.flash.timeline.Clip; -import com.jpexs.decompiler.flash.timeline.DepthState; -import com.jpexs.decompiler.flash.timeline.Frame; -import com.jpexs.decompiler.flash.timeline.FrameScript; -import com.jpexs.decompiler.flash.timeline.SvgClip; -import com.jpexs.decompiler.flash.timeline.TagScript; -import com.jpexs.decompiler.flash.timeline.Timeline; -import com.jpexs.decompiler.flash.timeline.Timelined; -import com.jpexs.decompiler.flash.treeitems.SWFList; -import com.jpexs.decompiler.flash.treeitems.TreeItem; -import com.jpexs.decompiler.flash.types.ColorTransform; -import com.jpexs.decompiler.flash.types.MATRIX; -import com.jpexs.decompiler.flash.types.RECT; -import com.jpexs.decompiler.flash.types.annotations.Internal; -import com.jpexs.decompiler.flash.types.filters.BlendComposite; -import com.jpexs.decompiler.flash.types.filters.FILTER; -import com.jpexs.decompiler.flash.xfl.FLAVersion; -import com.jpexs.decompiler.flash.xfl.XFLConverter; -import com.jpexs.decompiler.graph.Graph; -import com.jpexs.decompiler.graph.GraphSourceItem; -import com.jpexs.decompiler.graph.GraphSourceItemContainer; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.TranslateStack; -import com.jpexs.decompiler.graph.model.LocalData; -import com.jpexs.helpers.Cache; -import com.jpexs.helpers.Helper; -import com.jpexs.helpers.NulStream; -import com.jpexs.helpers.ProgressListener; -import com.jpexs.helpers.SerializableImage; -import com.jpexs.helpers.utf8.Utf8Helper; -import java.awt.AlphaComposite; -import java.awt.Color; -import java.awt.Graphics2D; -import java.awt.Rectangle; -import java.awt.RenderingHints; -import java.awt.Shape; -import java.awt.geom.AffineTransform; -import java.awt.image.BufferedImage; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.EmptyStackException; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.Stack; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.zip.DeflaterOutputStream; -import java.util.zip.InflaterInputStream; - -/** - * Class representing SWF file - * - * @author JPEXS - */ -public final class SWF implements SWFContainerItem, Timelined { - - // big object for testing cleanup - //BigObject bigObj = new BigObject(); - /** - * Default version of SWF file format - */ - public static final int DEFAULT_VERSION = 10; - - /** - * Maximum SWF file format version - * Needs to be fixed when SWF versions reaches this value - */ - public static final int MAX_VERSION = 30; - - /** - * Tags inside of file - */ - public List tags = new ArrayList<>(); - - @Internal - public boolean hasEndTag = true; - - /** - * ExportRectangle for the display - */ - public RECT displayRect; - - /** - * Movie frame rate - */ - public int frameRate; - - /** - * Number of frames in movie - */ - public int frameCount; - - /** - * Version of SWF - */ - public int version; - - /** - * Uncompressed size of the file - */ - @Internal - public long fileSize; - - /** - * Used compression mode - */ - public SWFCompression compression = SWFCompression.NONE; - - /** - * Compressed size of the file (LZMA) - */ - @Internal - public long compressedSize; - - /** - * LZMA Properties - */ - public byte[] lzmaProperties; - - @Internal - public byte[] uncompressedData; - - @Internal - public byte[] originalUncompressedData; - - /** - * ScaleForm GFx - */ - public boolean gfx = false; - - @Internal - public SWFList swfList; - - @Internal - private String file; - - @Internal - private String fileTitle; - - @Internal - private Map characters; - - @Internal - private List abcList; - - @Internal - private JPEGTablesTag jtt; - - @Internal - public Map sourceFontNamesMap = new HashMap<>(); - - public static final double unitDivisor = 20; - - private static final Logger logger = Logger.getLogger(SWF.class.getName()); - - @Internal - private Timeline timeline; - - @Internal - public DumpInfoSwfNode dumpInfo; - - @Internal - public DefineBinaryDataTag binaryData; - - @Internal - private final HashMap deobfuscated = new HashMap<>(); - - @Internal - private final IdentifiersDeobfuscation deobfuscation = new IdentifiersDeobfuscation(); - - @Internal - private Cache frameCache = Cache.getInstance(false, false, "frame"); - - @Internal - private Cache soundCache = Cache.getInstance(false, false, "sound"); - - @Internal - private final Cache as2Cache = Cache.getInstance(true, false, "as2"); - - @Internal - private final Cache as3Cache = Cache.getInstance(true, false, "as3"); - - public void updateCharacters() { - characters = null; - } - - public void clearTagSwfs() { - resetTimelines(this); - updateCharacters(); - - for (Tag tag : tags) { - if (tag instanceof DefineSpriteTag) { - DefineSpriteTag spriteTag = (DefineSpriteTag) tag; - for (Tag tag1 : spriteTag.subTags) { - tag1.setSwf(null); - } - - spriteTag.subTags.clear(); - } - - if (tag instanceof DefineBinaryDataTag) { - DefineBinaryDataTag binaryTag = (DefineBinaryDataTag) tag; - if (binaryTag.innerSwf != null) { - binaryTag.innerSwf.clearTagSwfs(); - } - } - - tag.setSwf(null); - } - - tags.clear(); - if (abcList != null) { - abcList.clear(); - } - - if (swfList != null) { - swfList.swfs.clear(); - } - - as2Cache.clear(); - as3Cache.clear(); - frameCache.clear(); - soundCache.clear(); - - timeline = null; - clearDumpInfo(dumpInfo); - dumpInfo = null; - jtt = null; - binaryData = null; - } - - private void clearDumpInfo(DumpInfo di) { - for (DumpInfo childInfo : di.getChildInfos()) { - clearDumpInfo(childInfo); - } - - di.getChildInfos().clear(); - } - - public Map getCharacters() { - if (characters == null) { - synchronized (this) { - if (characters == null) { - Map chars = new HashMap<>(); - parseCharacters(tags, chars); - characters = Collections.unmodifiableMap(chars); - } - } - } - - return characters; - } - - public CharacterTag getCharacter(int characterId) { - return getCharacters().get(characterId); - } - - public String getExportName(int characterId) { - CharacterTag characterTag = getCharacters().get(characterId); - String exportName = characterTag != null ? characterTag.getExportName() : null; - return exportName; - } - - public FontTag getFont(int fontId) { - CharacterTag characterTag = getCharacters().get(fontId); - if (characterTag instanceof FontTag) { - return (FontTag) characterTag; - } - - if (characterTag != null) { - logger.log(Level.SEVERE, "CharacterTag should be a FontTag. characterId: {0}", fontId); - } - - return null; - } - - public ImageTag getImage(int imageId) { - CharacterTag characterTag = getCharacters().get(imageId); - if (characterTag instanceof ImageTag) { - return (ImageTag) characterTag; - } - - if (characterTag != null) { - logger.log(Level.SEVERE, "CharacterTag should be an ImageTag. characterId: {0}", imageId); - } - - return null; - } - - public TextTag getText(int textId) { - CharacterTag characterTag = getCharacters().get(textId); - if (characterTag instanceof TextTag) { - return (TextTag) characterTag; - } - - if (characterTag != null) { - logger.log(Level.SEVERE, "CharacterTag should be a TextTag. characterId: {0}", textId); - } - - return null; - } - - public List getAbcList() { - if (abcList == null) { - synchronized (this) { - if (abcList == null) { - ArrayList newAbcList = new ArrayList<>(); - getAbcTags(tags, newAbcList); - abcList = newAbcList; - } - } - } - - return abcList; - } - - public boolean isAS3() { - FileAttributesTag fileAttributes = getFileAttributes(); - return (fileAttributes != null && fileAttributes.actionScript3) || (fileAttributes == null && !getAbcList().isEmpty()); - } - - public FileAttributesTag getFileAttributes() { - for (Tag t : tags) { - if (t instanceof FileAttributesTag) { - return (FileAttributesTag) t; - } - } - - return null; - } - - public int getNextCharacterId() { - int max = -1; - for (int characterId : getCharacters().keySet()) { - if (characterId > max) { - max = characterId; - } - } - - return max + 1; - } - - public synchronized JPEGTablesTag getJtt() { - if (jtt == null) { - synchronized (this) { - if (jtt == null) { - for (Tag t : tags) { - if (t instanceof JPEGTablesTag) { - jtt = (JPEGTablesTag) t; - break; - } - } - } - } - } - - return jtt; - } - - public String getDocumentClass() { - for (Tag t : tags) { - if (t instanceof SymbolClassTag) { - SymbolClassTag sc = (SymbolClassTag) t; - for (int i = 0; i < sc.tags.size(); i++) { - if (sc.tags.get(i) == 0) { - return sc.names.get(i); - } - } - } - } - - return null; - } - - public void fixCharactersOrder(boolean checkAll) { - Set addedCharacterIds = new HashSet<>(); - Set movedTags = new HashSet<>(); - for (int i = 0; i < tags.size(); i++) { - Tag tag = tags.get(i); - if (checkAll || tag.isModified()) { - Set needed = new HashSet<>(); - tag.getNeededCharacters(needed); - if (tag instanceof CharacterTag) { - CharacterTag characterTag = (CharacterTag) tag; - needed.remove(characterTag.getCharacterId()); - } - boolean moved = false; - for (Integer id : needed) { - if (!addedCharacterIds.contains(id)) { - CharacterTag neededCharacter = getCharacter(id); - if (neededCharacter == null) { - continue; - } - - if (movedTags.contains(neededCharacter)) { - logger.log(Level.SEVERE, "Fixing characters order failed, recursion detected."); - return; - } - - // move the needed character to the current position - tags.remove(neededCharacter); - tags.add(i, neededCharacter); - movedTags.add(neededCharacter); - moved = true; - } - } - - if (moved) { - i--; - continue; - } - } - if (tag instanceof CharacterTag) { - addedCharacterIds.add(((CharacterTag) tag).getCharacterId()); - } - } - } - - public void resetTimelines(Timelined timelined) { - timelined.resetTimeline(); - if (timelined instanceof SWF) { - for (Tag t : ((SWF) timelined).tags) { - if (t instanceof Timelined) { - resetTimelines((Timelined) t); - } - } - } - } - - private void parseCharacters(List list, Map characters) { - for (Tag t : list) { - if (t instanceof CharacterTag) { - int characterId = ((CharacterTag) t).getCharacterId(); - if (characters.containsKey(characterId)) { - logger.log(Level.SEVERE, "SWF already contains characterId={0}", characterId); - } - - if (characterId != 0) { - characters.put(characterId, (CharacterTag) t); - } - } - if (t instanceof DefineSpriteTag) { - parseCharacters(((DefineSpriteTag) t).getSubTags(), characters); - } - } - } - - /** - * Unresolve recursive sprites - */ - private void checkInvalidSprites() { - for (int i = 0; i < tags.size(); i++) { - Tag t = tags.get(i); - if (t instanceof DefineSpriteTag) { - if (!isSpriteValid((DefineSpriteTag) t, new ArrayList())) { - tags.set(i, new TagStub(this, t.getId(), "InvalidSprite", t.getOriginalRange(), null)); - } - } - } - } - - private boolean isSpriteValid(DefineSpriteTag sprite, List path) { - if (path.contains(sprite.spriteId)) { - return false; - } - path.add(sprite.spriteId); - for (Tag t : sprite.subTags) { - if (t instanceof DefineSpriteTag) { - if (!isSpriteValid((DefineSpriteTag) t, path)) { - return false; - } - } - } - path.remove((Integer) sprite.spriteId); - return true; - } - - @Override - public Timeline getTimeline() { - if (timeline == null) { - timeline = new Timeline(this); - } - return timeline; - } - - @Override - public void resetTimeline() { - if (timeline != null) { - timeline.reset(this); - } - } - - /** - * Gets all tags with specified id - * - * @param tagId Identificator of tag type - * @return List of tags - */ - public List getTagData(int tagId) { - List ret = new ArrayList<>(); - for (Tag tag : tags) { - if (tag.getId() == tagId) { - ret.add(tag); - } - } - return ret; - } - - /** - * Saves this SWF into new file - * - * @param os OutputStream to save SWF in - * @throws IOException - */ - public void saveTo(OutputStream os) throws IOException { - saveTo(os, compression); - } - - public String getHeaderBytes() { - return getHeaderBytes(compression, gfx); - } - - private String getHeaderBytes(SWFCompression compression, boolean gfx) { - if (compression == SWFCompression.LZMA_ABC) { - return "ABC"; - } - - String ret = ""; - if (compression == SWFCompression.LZMA) { - ret += 'Z'; - } else if (compression == SWFCompression.ZLIB) { - ret += 'C'; - } else { - if (gfx) { - ret += 'G'; - } else { - ret += 'F'; - } - } - if (gfx) { - ret += 'F'; - ret += 'X'; - } else { - ret += 'W'; - ret += 'S'; - } - return ret; - } - - /** - * Saves this SWF into new file - * - * @param os OutputStream to save SWF in - * @param compression - * @throws IOException - */ - public void saveTo(OutputStream os, SWFCompression compression) throws IOException { - try { - fixCharactersOrder(false); - - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - SWFOutputStream sos = new SWFOutputStream(baos, version); - sos.writeRECT(displayRect); - sos.writeUI8(0); - sos.writeUI8(frameRate); - sos.writeUI16(frameCount); - - sos.writeTags(tags); - if (hasEndTag) { - sos.writeUI16(0); - } - - sos.close(); - os.write(Utf8Helper.getBytes(getHeaderBytes(compression, gfx))); - os.write(version); - byte[] data = baos.toByteArray(); - sos = new SWFOutputStream(os, version); - sos.writeUI32(data.length + 8); - - if (compression == SWFCompression.LZMA || compression == SWFCompression.LZMA_ABC) { - long uncompressedLength = data.length; - Encoder enc = new Encoder(); - int val = lzmaProperties[0] & 0xFF; - int lc = val % 9; - int remainder = val / 9; - int lp = remainder % 5; - int pb = remainder / 5; - int dictionarySize = 0; - for (int i = 0; i < 4; i++) { - dictionarySize += ((int) (lzmaProperties[1 + i]) & 0xFF) << (i * 8); - } - if (Configuration.lzmaFastBytes.get() > 0) { - enc.SetNumFastBytes(Configuration.lzmaFastBytes.get()); - } - enc.SetDictionarySize(dictionarySize); - enc.SetLcLpPb(lc, lp, pb); - baos = new ByteArrayOutputStream(); - enc.SetEndMarkerMode(true); - enc.Code(new ByteArrayInputStream(data), baos, -1, -1, null); - data = baos.toByteArray(); - if (compression == SWFCompression.LZMA) { - byte[] udata = new byte[4]; - udata[0] = (byte) (data.length & 0xFF); - udata[1] = (byte) ((data.length >> 8) & 0xFF); - udata[2] = (byte) ((data.length >> 16) & 0xFF); - udata[3] = (byte) ((data.length >> 24) & 0xFF); - os.write(udata); - } - enc.WriteCoderProperties(os); - if (compression == SWFCompression.LZMA_ABC) { - byte[] udata = new byte[8]; - udata[0] = (byte) (uncompressedLength & 0xFF); - udata[1] = (byte) ((uncompressedLength >> 8) & 0xFF); - udata[2] = (byte) ((uncompressedLength >> 16) & 0xFF); - udata[3] = (byte) ((uncompressedLength >> 24) & 0xFF); - udata[4] = (byte) ((uncompressedLength >> 32) & 0xFF); - udata[5] = (byte) ((uncompressedLength >> 40) & 0xFF); - udata[6] = (byte) ((uncompressedLength >> 48) & 0xFF); - udata[7] = (byte) ((uncompressedLength >> 56) & 0xFF); - os.write(udata); - } - } else if (compression == SWFCompression.ZLIB) { - os = new DeflaterOutputStream(os); - } - - os.write(data); - } finally { - if (os != null) { - os.close(); - } - } - } - - public boolean isModified() { - for (Tag tag : tags) { - if (tag.isModified()) { - return true; - } - } - return false; - } - - public void clearModified() { - for (Tag tag : tags) { - if (tag.isModified()) { - tag.createOriginalData(); - tag.setModified(false); - } - } - - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - try { - saveTo(baos, SWFCompression.NONE); - byte[] swfData = baos.toByteArray(); - uncompressedData = swfData; - } catch (IOException ex) { - logger.log(Level.SEVERE, "Cannot save SWF", ex); - } - } - - /** - * Constructs an empty SWF - */ - public SWF() { - - } - - /** - * Construct SWF from stream - * - * @param is Stream to read SWF from - * @param parallelRead Use parallel threads? - * @throws IOException - * @throws java.lang.InterruptedException - */ - public SWF(InputStream is, boolean parallelRead) throws IOException, InterruptedException { - this(is, null, null, null, parallelRead, false, true); - } - - /** - * Construct SWF from stream - * - * @param is Stream to read SWF from - * @param parallelRead Use parallel threads? - * @param lazy - * @throws IOException - * @throws java.lang.InterruptedException - */ - public SWF(InputStream is, boolean parallelRead, boolean lazy) throws IOException, InterruptedException { - this(is, null, null, null, parallelRead, false, lazy); - } - - /** - * Construct SWF from stream - * - * @param is Stream to read SWF from - * @param file Path to the file - * @param fileTitle Title of the SWF - * @param parallelRead Use parallel threads? - * @throws IOException - * @throws java.lang.InterruptedException - */ - public SWF(InputStream is, String file, String fileTitle, boolean parallelRead) throws IOException, InterruptedException { - this(is, file, fileTitle, null, parallelRead, false, true); - } - - /** - * Construct SWF from stream - * - * @param is Stream to read SWF from - * @param listener - * @param parallelRead Use parallel threads? - * @throws IOException - * @throws java.lang.InterruptedException - */ - public SWF(InputStream is, ProgressListener listener, boolean parallelRead) throws IOException, InterruptedException { - this(is, null, null, listener, parallelRead, false, true); - } - - /** - * Construct SWF from stream - * - * @param is Stream to read SWF from - * @param file Path to the file - * @param fileTitle Title of the SWF - * @param listener - * @param parallelRead Use parallel threads? - * @throws IOException - * @throws java.lang.InterruptedException - */ - public SWF(InputStream is, String file, String fileTitle, ProgressListener listener, boolean parallelRead) throws IOException, InterruptedException { - this(is, file, fileTitle, listener, parallelRead, false, true); - } - - /** - * Faster constructor to check SWF only - * - * @param is - * @throws java.io.IOException - */ - public SWF(InputStream is) throws IOException { - decompress(is, new NulStream(), true); - } - - /** - * Construct SWF from stream - * - * @param is Stream to read SWF from - * @param file Path to the file - * @param fileTitle Title of the SWF - * @param listener - * @param parallelRead Use parallel threads? - * @param checkOnly Check only file validity - * @param lazy - * @throws IOException - * @throws java.lang.InterruptedException - */ - public SWF(InputStream is, String file, String fileTitle, ProgressListener listener, boolean parallelRead, boolean checkOnly, boolean lazy) throws IOException, InterruptedException { - this.file = file; - this.fileTitle = fileTitle; - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - SWFHeader header = decompress(is, baos, true); - gfx = header.gfx; - compression = header.compression; - lzmaProperties = header.lzmaProperties; - uncompressedData = baos.toByteArray(); - originalUncompressedData = uncompressedData; - - SWFInputStream sis = new SWFInputStream(this, uncompressedData); - dumpInfo = new DumpInfoSwfNode(this, "rootswf", "", null, 0, 0); - sis.dumpInfo = dumpInfo; - sis.readBytesEx(3, "signature"); // skip siganture - version = sis.readUI8("version"); - fileSize = sis.readUI32("fileSize"); - dumpInfo.lengthBytes = fileSize; - if (listener != null) { - sis.addPercentListener(listener); - } - sis.setPercentMax(fileSize); - displayRect = sis.readRECT("displayRect"); - // FIXED8 (16 bit fixed point) frameRate - sis.readUI8("tmpFirstByetOfFrameRate"); // tmpFirstByetOfFrameRate - frameRate = sis.readUI8("frameRate"); - frameCount = sis.readUI16("frameCount"); - List tags = sis.readTagList(this, 0, parallelRead, true, !checkOnly, lazy); - if (tags.size() > 0 && tags.get(tags.size() - 1).getId() == EndTag.ID) { - tags.remove(tags.size() - 1); - } else { - hasEndTag = false; - } - this.tags = tags; - if (!checkOnly) { - checkInvalidSprites(); - updateCharacters(); - assignExportNamesToSymbols(); - assignClassesToSymbols(); - SWFDecompilerPlugin.fireSwfParsed(this); - } else { - boolean hasNonUnknownTag = false; - for (Tag tag : tags) { - if (tag.getOriginalDataLength() > 0 && Tag.getRequiredTags().contains(tag.getId())) { - hasNonUnknownTag = true; - } - } - if (!hasNonUnknownTag) { - throw new IOException("Invalid SWF file. No known tag found."); - } - } - - /*preload shape tags - for (Tag tag : tags) { - if (tag instanceof ShapeTag) { - ((ShapeTag) tag).getShapes(); - } - }*/ - } - - @Override - public SWF getSwf() { - return this; - } - - public SWF getRootSwf() { - SWF result = this; - while (result.binaryData != null) { - result = result.binaryData.getSwf(); - } - - return result; - } - - public String getFile() { - return file; - } - - /** - * Get title of the file - * - * @return file title - */ - public String getFileTitle() { - if (fileTitle != null) { - return fileTitle; - } - return file; - } - - public String getShortFileName() { - String title = getFileTitle(); - if (title == null) { - return ""; - } - return new File(title).getName(); - } - - public void setFile(String file) { - this.file = file; - fileTitle = null; - } - - public Date getFileModificationDate() { - try { - if (swfList != null && swfList.sourceInfo != null) { - String fileName = swfList.sourceInfo.getFile(); - if (fileName != null) { - long lastModified = new File(fileName).lastModified(); - if (lastModified > 0) { - return new Date(lastModified); - } - } - } - } catch (SecurityException sex) { - } - - return new Date(); - } - - private static void getAbcTags(List list, List actionScripts) { - for (Tag t : list) { - if (t instanceof DefineSpriteTag) { - getAbcTags(((DefineSpriteTag) t).getSubTags(), actionScripts); - } - if (t instanceof ABCContainerTag) { - actionScripts.add((ABCContainerTag) t); - } - } - } - - public void assignExportNamesToSymbols() { - HashMap exportNames = new HashMap<>(); - for (Tag t : tags) { - if (t instanceof ExportAssetsTag) { - ExportAssetsTag eat = (ExportAssetsTag) t; - for (int i = 0; i < eat.tags.size(); i++) { - Integer tagId = eat.tags.get(i); - String name = eat.names.get(i); - if ((!exportNames.containsKey(tagId)) && (!exportNames.containsValue(name))) { - exportNames.put(tagId, name); - } - } - } - } - for (Tag t : tags) { - if (t instanceof CharacterTag) { - CharacterTag ct = (CharacterTag) t; - if (exportNames.containsKey(ct.getCharacterId())) { - ct.setExportName(exportNames.get(ct.getCharacterId())); - } - } - } - } - - public void assignClassesToSymbols() { - HashMap classes = new HashMap<>(); - for (Tag t : tags) { - if (t instanceof SymbolClassTag) { - SymbolClassTag sct = (SymbolClassTag) t; - for (int i = 0; i < sct.tags.size(); i++) { - if ((!classes.containsKey(sct.tags.get(i))) && (!classes.containsValue(sct.names.get(i)))) { - classes.put(sct.tags.get(i), sct.names.get(i)); - } - } - } - } - for (Tag t : tags) { - if (t instanceof CharacterTag) { - CharacterTag ct = (CharacterTag) t; - if (classes.containsKey(ct.getCharacterId())) { - ct.setClassName(classes.get(ct.getCharacterId())); - } - } - } - } - - /** - * Compress SWF file - * - * @param fis Input stream - * @param fos Output stream - * @return True on success - */ - public static boolean fws2cws(InputStream fis, OutputStream fos) { - try { - byte[] swfHead = new byte[8]; - fis.read(swfHead); - - if (swfHead[0] != 'F') { - fis.close(); - return false; - } - swfHead[0] = 'C'; - fos.write(swfHead); - fos = new DeflaterOutputStream(fos); - int i; - while ((i = fis.read()) != -1) { - fos.write(i); - } - - fis.close(); - fos.close(); - } catch (IOException ex) { - return false; - } - return true; - } - - public static boolean decompress(InputStream fis, OutputStream fos) { - try { - decompress(fis, fos, false); - return true; - } catch (IOException ex) { - return false; - } - } - - private static void decodeLZMAStream(InputStream is, OutputStream os, byte[] lzmaProperties, long fileSize) throws IOException { - Decoder decoder = new Decoder(); - if (!decoder.SetDecoderProperties(lzmaProperties)) { - throw new IOException("LZMA:Incorrect stream properties"); - } - if (!decoder.Code(is, os, fileSize - 8)) { - throw new IOException("LZMA:Error in data stream"); - } - } - - private static SWFHeader decompress(InputStream is, OutputStream os, boolean allowUncompressed) throws IOException { - byte[] hdr = new byte[8]; - - // SWFheader: signature, version and fileSize - if (is.read(hdr) != 8) { - throw new SwfOpenException("SWF header is too short"); - } - - String signature = new String(hdr, 0, 3, Utf8Helper.charset); - if (!Arrays.asList( - "FWS", // Uncompressed Flash - "CWS", // ZLib compressed Flash - "ZWS", // LZMA compressed Flash - "GFX", // Uncompressed ScaleForm GFx - "CFX", // Compressed ScaleForm GFx - "ABC" // Non-standard LZMA compressed Flash - ).contains(signature)) { - throw new SwfOpenException("Invalid SWF file, wrong signature."); - } - - int version = hdr[3]; - SWFInputStream sis = new SWFInputStream(null, Arrays.copyOfRange(hdr, 4, 8), 4, 4); - long fileSize = sis.readUI32("fileSize"); - SWFHeader header = new SWFHeader(); - header.version = version; - header.fileSize = fileSize; - - if (hdr[1] == 'F' && hdr[2] == 'X') { - header.gfx = true; - } - - try (SWFOutputStream sos = new SWFOutputStream(os, version)) { - sos.write(Utf8Helper.getBytes(header.gfx ? "GFX" : "FWS")); - sos.writeUI8(version); - sos.writeUI32(fileSize); - - switch (hdr[0]) { - case 'C': { // CWS, CFX - Helper.copyStream(new InflaterInputStream(is), os, fileSize - 8); - header.compression = SWFCompression.ZLIB; - break; - } - case 'Z': { // ZWS - byte[] lzmaprop = new byte[9]; - is.read(lzmaprop); - sis = new SWFInputStream(null, lzmaprop); - sis.readUI32("LZMAsize"); // compressed LZMA data size = compressed SWF - 17 byte, - // where 17 = 8 byte header + this 4 byte + 5 bytes decoder properties - int propertiesSize = 5; - byte[] lzmaProperties = sis.readBytes(propertiesSize, "lzmaproperties"); - if (lzmaProperties.length != propertiesSize) { - throw new IOException("LZMA:input .lzma file is too short"); - } - - decodeLZMAStream(is, os, lzmaProperties, fileSize); - - header.compression = SWFCompression.LZMA; - header.lzmaProperties = lzmaProperties; - break; - } - case 'A': { // ABC - byte[] lzmaProperties = new byte[5]; - is.read(lzmaProperties); - byte[] uncompressedLength = new byte[8]; - is.read(uncompressedLength); - - decodeLZMAStream(is, os, lzmaProperties, fileSize); - - header.compression = SWFCompression.LZMA_ABC; - header.lzmaProperties = lzmaProperties; - break; - } - default: { // FWS, GFX - if (allowUncompressed) { - Helper.copyStream(is, os, fileSize - 8); - } else { - throw new IOException("SWF is not compressed"); - } - } - } - - return header; - } - } - - public static boolean renameInvalidIdentifiers(RenameType renameType, InputStream fis, OutputStream fos) { - try { - SWF swf = new SWF(fis, Configuration.parallelSpeedUp.get()); - int cnt = swf.deobfuscateIdentifiers(renameType); - swf.assignClassesToSymbols(); - System.out.println(cnt + " identifiers renamed."); - swf.saveTo(fos); - } catch (InterruptedException ex) { - return false; - } catch (IOException ex) { - return false; - } - return true; - } - - public boolean exportAS3Class(String className, String outdir, ScriptExportSettings exportSettings, boolean parallel, EventListener evl) throws Exception { - boolean exported = false; - - List abcList = getAbcList(); - for (int i = 0; i < abcList.size(); i++) { - ABC abc = abcList.get(i).getABC(); - List scrs = abc.findScriptPacksByPath(className); - for (int j = 0; j < scrs.size(); j++) { - ScriptPack scr = scrs.get(j); - String cnt = ""; - if (scrs.size() > 1) { - cnt = "script " + (j + 1) + "/" + scrs.size() + " "; - } - String eventData = cnt + scr.getPath() + " ..."; - evl.handleExportingEvent("tag", i + 1, abcList.size(), eventData); - scr.export(outdir, exportSettings, parallel); - evl.handleExportedEvent("tag", i + 1, abcList.size(), eventData); - exported = true; - } - } - return exported; - } - - private List uniqueAS3Packs(List packs) { - List ret = new ArrayList<>(); - Set classPaths = new HashSet<>(); - for (ScriptPack item : packs) { - ClassPath key = item.getClassPath(); - if (classPaths.contains(key)) { - logger.log(Level.SEVERE, "Duplicate pack path found (" + key + ")!"); - } else { - classPaths.add(key); - ret.add(item); - } - } - return ret; - } - - public List getAS3Packs() { - List packs = new ArrayList<>(); - for (ABCContainerTag abcTag : getAbcList()) { - packs.addAll(abcTag.getABC().getScriptPacks(null)); - } - return uniqueAS3Packs(packs); - } - - @Override - public RECT getRect() { - return displayRect; - } - - @Override - public RECT getRect(Set added) { - return displayRect; - } - - public EventListener getExportEventListener() { - EventListener evl = new EventListener() { - @Override - public void handleExportingEvent(String type, int index, int count, Object data) { - for (EventListener listener : listeners) { - listener.handleExportingEvent(type, index, count, data); - } - } - - @Override - public void handleExportedEvent(String type, int index, int count, Object data) { - for (EventListener listener : listeners) { - listener.handleExportedEvent(type, index, count, data); - } - } - - @Override - public void handleEvent(String event, Object data) { - informListeners(event, data); - } - }; - - return evl; - } - - public List exportActionScript(AbortRetryIgnoreHandler handler, String outdir, ScriptExportSettings exportSettings, boolean parallel, EventListener evl) throws IOException { - List ret = new ArrayList<>(); - - if (isAS3()) { - ret.addAll(new AS3ScriptExporter().exportActionScript3(this, handler, outdir, exportSettings, parallel, evl)); - } else { - ret.addAll(new AS2ScriptExporter().exportAS2ScriptsTimeout(handler, outdir, getASMs(true), exportSettings, evl)); - } - return ret; - } - - public Map getASMs(boolean exportFileNames) { - return getASMs(exportFileNames, new ArrayList(), true); - } - - public Map getASMs(boolean exportFileNames, List nodesToExport, boolean exportAll) { - Map asmsToExport = new HashMap<>(); - for (TreeItem treeItem : getFirstLevelASMNodes(null)) { - getASMs(exportFileNames, treeItem, nodesToExport, exportAll, asmsToExport, File.separator + getASMPath(exportFileNames, treeItem)); - } - - return asmsToExport; - } - - private void getASMs(boolean exportFileNames, TreeItem treeItem, List nodesToExport, boolean exportAll, Map asmsToExport, String path) { - boolean exportNode = nodesToExport.contains(treeItem); - TreeItem realItem = treeItem instanceof TagScript ? ((TagScript) treeItem).getTag() : treeItem; - if (realItem instanceof ASMSource && (exportAll || exportNode)) { - String npath = path; - int ppos = 1; - while (asmsToExport.containsKey(npath)) { - ppos++; - npath = path + (exportFileNames ? "[" + ppos + "]" : "_" + ppos); - } - asmsToExport.put(npath, (ASMSource) realItem); - } - - if (treeItem instanceof TagScript) { - TagScript tagScript = (TagScript) treeItem; - for (TreeItem subItem : tagScript.getFrames()) { - getASMs(exportFileNames, subItem, nodesToExport, exportAll, asmsToExport, path + File.separator + getASMPath(exportFileNames, subItem)); - } - } else if (treeItem instanceof FrameScript) { - FrameScript frameScript = (FrameScript) treeItem; - Frame parentFrame = frameScript.getFrame(); - for (TreeItem subItem : parentFrame.actionContainers) { - getASMs(exportFileNames, getASMWrapToTagScript(subItem), nodesToExport, exportAll || exportNode, asmsToExport, path + File.separator + getASMPath(exportFileNames, subItem)); - } - for (TreeItem subItem : parentFrame.actions) { - getASMs(exportFileNames, getASMWrapToTagScript(subItem), nodesToExport, exportAll || exportNode, asmsToExport, path + File.separator + getASMPath(exportFileNames, subItem)); - } - } else if (treeItem instanceof AS2Package) { - AS2Package as2Package = (AS2Package) treeItem; - for (TreeItem subItem : as2Package.subPackages.values()) { - getASMs(exportFileNames, subItem, nodesToExport, exportAll, asmsToExport, path + File.separator + getASMPath(exportFileNames, subItem)); - } - for (TreeItem subItem : as2Package.scripts.values()) { - getASMs(exportFileNames, subItem, nodesToExport, exportAll, asmsToExport, path + File.separator + getASMPath(exportFileNames, subItem)); - } - } - } - - private String getASMPath(boolean exportFileName, TreeItem treeItem) { - if (!exportFileName) { - return treeItem.toString(); - } - - String result; - if (treeItem instanceof Exportable) { - result = ((Exportable) treeItem).getExportFileName(); - } else { - result = treeItem.toString(); - } - - return Helper.makeFileName(result); - } - - private TreeItem getASMWrapToTagScript(TreeItem treeItem) { - if (treeItem instanceof Tag) { - Tag resultTag = (Tag) treeItem; - List subNodes = new ArrayList<>(); - if (treeItem instanceof ASMSourceContainer) { - for (ASMSource item : ((ASMSourceContainer) treeItem).getSubItems()) { - subNodes.add(item); - } - } - - TagScript tagScript = new TagScript(treeItem.getSwf(), resultTag, subNodes); - return tagScript; - } - - return treeItem; - } - - public List getFirstLevelASMNodes(Map tagScriptCache) { - Timeline timeline = getTimeline(); - List subNodes = new ArrayList<>(); - List subFrames = new ArrayList<>(); - subNodes.addAll(timeline.getAS2RootPackage().subPackages.values()); - subNodes.addAll(timeline.getAS2RootPackage().scripts.values()); - - for (Tag tag : timeline.otherTags) { - boolean hasInnerFrames = false; - List tagSubNodes = new ArrayList<>(); - if (tag instanceof Timelined) { - Timeline timeline2 = ((Timelined) tag).getTimeline(); - for (Frame frame : timeline2.getFrames()) { - if (!frame.actions.isEmpty() || !frame.actionContainers.isEmpty()) { - FrameScript frameScript = new FrameScript(this, frame); - tagSubNodes.add(frameScript); - hasInnerFrames = true; - } - } - } - - if (tag instanceof ASMSourceContainer) { - for (ASMSource asm : ((ASMSourceContainer) tag).getSubItems()) { - tagSubNodes.add(asm); - } - } - - if (!tagSubNodes.isEmpty()) { - TagScript ts = new TagScript(this, tag, tagSubNodes); - if (tagScriptCache != null) { - tagScriptCache.put(tag, ts); - } - if (hasInnerFrames) { - subFrames.add(ts); - } else { - subNodes.add(ts); - } - } - } - - subNodes.addAll(subFrames); - for (Frame frame : timeline.getFrames()) { - if (!frame.actions.isEmpty() || !frame.actionContainers.isEmpty()) { - FrameScript frameScript = new FrameScript(this, frame); - subNodes.add(frameScript); - } - } - - return subNodes; - } - - private final HashSet listeners = new HashSet<>(); - - public final void addEventListener(EventListener listener) { - listeners.add(listener); - for (Tag t : tags) { - if (t instanceof ABCContainerTag) { - (((ABCContainerTag) t).getABC()).addEventListener(listener); - } - } - } - - public final void removeEventListener(EventListener listener) { - listeners.remove(listener); - for (Tag t : tags) { - if (t instanceof ABCContainerTag) { - (((ABCContainerTag) t).getABC()).removeEventListener(listener); - } - } - } - - protected void informListeners(String event, Object data) { - for (EventListener listener : listeners) { - listener.handleEvent(event, data); - } - } - - public static void populateVideoFrames(int streamId, List tags, HashMap output) { - for (Tag t : tags) { - if (t instanceof VideoFrameTag) { - output.put(((VideoFrameTag) t).frameNum, (VideoFrameTag) t); - } - if (t instanceof DefineSpriteTag) { - populateVideoFrames(streamId, ((DefineSpriteTag) t).getSubTags(), output); - } - } - } - - private static void writeLE(OutputStream os, long val, int size) throws IOException { - for (int i = 0; i < size; i++) { - os.write((int) (val & 0xff)); - val >>= 8; - } - } - - public static void createWavFromPcmData(OutputStream fos, int soundRateHz, boolean soundSize, boolean soundType, byte[] data) throws IOException { - ByteArrayOutputStream subChunk1Data = new ByteArrayOutputStream(); - int audioFormat = 1; // PCM - writeLE(subChunk1Data, audioFormat, 2); - int numChannels = soundType ? 2 : 1; - writeLE(subChunk1Data, numChannels, 2); - int[] rateMap = {5512, 11025, 22050, 44100}; - int sampleRate = soundRateHz; // rateMap[soundRate]; - writeLE(subChunk1Data, sampleRate, 4); - int bitsPerSample = soundSize ? 16 : 8; - int byteRate = sampleRate * numChannels * bitsPerSample / 8; - writeLE(subChunk1Data, byteRate, 4); - int blockAlign = numChannels * bitsPerSample / 8; - writeLE(subChunk1Data, blockAlign, 2); - writeLE(subChunk1Data, bitsPerSample, 2); - - ByteArrayOutputStream chunks = new ByteArrayOutputStream(); - chunks.write(Utf8Helper.getBytes("fmt ")); - byte[] subChunk1DataBytes = subChunk1Data.toByteArray(); - writeLE(chunks, subChunk1DataBytes.length, 4); - chunks.write(subChunk1DataBytes); - - chunks.write(Utf8Helper.getBytes("data")); - writeLE(chunks, data.length, 4); - chunks.write(data); - - fos.write(Utf8Helper.getBytes("RIFF")); - byte[] chunkBytes = chunks.toByteArray(); - writeLE(fos, 4 + chunkBytes.length, 4); - fos.write(Utf8Helper.getBytes("WAVE")); - fos.write(chunkBytes); - } - - public static String getTypePrefix(CharacterTag c) { - if (c instanceof ShapeTag) { - return "shape"; - } - if (c instanceof MorphShapeTag) { - return "morphshape"; - } - if (c instanceof DefineSpriteTag) { - return "sprite"; - } - if (c instanceof TextTag) { - return "text"; - } - if (c instanceof ButtonTag) { - return "button"; - } - if (c instanceof FontTag) { - return "font"; - } - if (c instanceof ImageTag) { - return "image"; - } - return "character"; - } - - public static void writeLibrary(SWF fswf, Set library, OutputStream fos) throws IOException { - for (int c : library) { - CharacterTag ch = fswf.getCharacter(c); - if (ch instanceof FontTag) { - fos.write(Utf8Helper.getBytes("function " + getTypePrefix(ch) + c + "(ctx,ch,textColor){\r\n")); - fos.write(Utf8Helper.getBytes(((FontTag) ch).toHtmlCanvas(1))); - fos.write(Utf8Helper.getBytes("}\r\n\r\n")); - } else { - if (ch instanceof ImageTag) { - ImageTag image = (ImageTag) ch; - ImageFormat format = image.getImageFormat(); - InputStream imageStream = image.getImageData(); - byte[] imageData; - if (imageStream != null) { - imageData = Helper.readStream(image.getImageData()); - } else { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - ImageHelper.write(image.getImage().getBufferedImage(), format, baos); - imageData = baos.toByteArray(); - } - String base64ImgData = Helper.byteArrayToBase64String(imageData); - fos.write(Utf8Helper.getBytes("var imageObj" + c + " = document.createElement(\"img\");\r\nimageObj" + c + ".src=\"data:image/" + format + ";base64," + base64ImgData + "\";\r\n")); - } - fos.write(Utf8Helper.getBytes("function " + getTypePrefix(ch) + c + "(ctx,ctrans,frame,ratio,time){\r\n")); - if (ch instanceof DrawableTag) { - fos.write(Utf8Helper.getBytes(((DrawableTag) ch).toHtmlCanvas(1))); - } - fos.write(Utf8Helper.getBytes("}\r\n\r\n")); - } - } - } - - private static void getVariables(ConstantPool constantPool, BaseLocalData localData, TranslateStack stack, List output, ActionGraphSource code, int ip, List> variables, List functions, HashMap strings, List visited, HashMap usageTypes, String path) throws InterruptedException { - boolean debugMode = false; - while ((ip > -1) && ip < code.size()) { - if (visited.contains(ip)) { - break; - } - GraphSourceItem ins = code.get(ip); - - if (debugMode) { - System.err.println("Visit " + ip + ": ofs" + Helper.formatAddress(((Action) ins).getAddress()) + ":" + ((Action) ins).getASMSource(new ActionList(), new HashSet(), ScriptExportMode.PCODE) + " stack:" + Helper.stackToString(stack, LocalData.create(new ConstantPool()))); - } - if (ins.isExit()) { - break; - } - if (ins.isIgnored()) { - ip++; - continue; - } - - String usageType = "name"; - GraphTargetItem name = null; - if ((ins instanceof ActionGetVariable) - || (ins instanceof ActionGetMember) - || (ins instanceof ActionDefineLocal2) - || (ins instanceof ActionNewMethod) - || (ins instanceof ActionNewObject) - || (ins instanceof ActionCallMethod) - || (ins instanceof ActionCallFunction)) { - if (stack.isEmpty()) { - break; - } - name = stack.peek(); - } - - if ((ins instanceof ActionGetVariable) || (ins instanceof ActionDefineLocal2)) { - usageType = "variable"; - } - if (ins instanceof ActionGetMember) { - usageType = "member"; - } - if ((ins instanceof ActionNewMethod) || (ins instanceof ActionNewObject)) { - usageType = "class"; - } - if (ins instanceof ActionCallMethod) { - usageType = "function"; // can there be method? - } - if (ins instanceof ActionCallFunction) { - usageType = "function"; - } - - if ((ins instanceof ActionDefineFunction) || (ins instanceof ActionDefineFunction2)) { - functions.add(ins); - } - - if (ins instanceof GraphSourceItemContainer) { - GraphSourceItemContainer cnt = (GraphSourceItemContainer) ins; - List cntSizes = cnt.getContainerSizes(); - long addr = code.pos2adr(ip + 1); - ip = code.adr2pos(addr); - String cntName = cnt.getName(); - for (Long size : cntSizes) { - if (size == 0) { - continue; - } - ip = code.adr2pos(addr); - addr += size; - int nextip = code.adr2pos(addr); - getVariables(variables, functions, strings, usageTypes, new ActionGraphSource(code.getActions().subList(ip, nextip), code.version, new HashMap(), new HashMap(), new HashMap()), 0, path + (cntName == null ? "" : "/" + cntName)); - ip = nextip; - } - List> r = new ArrayList<>(); - r.add(new ArrayList()); - r.add(new ArrayList()); - r.add(new ArrayList()); - try { - ((GraphSourceItemContainer) ins).translateContainer(r, stack, output, new HashMap(), new HashMap(), new HashMap()); - } catch (EmptyStackException ex) { - } - - continue; - } - - if ((ins instanceof ActionSetVariable) || (ins instanceof ActionSetMember) || (ins instanceof ActionDefineLocal)) { - if (stack.size() < 2) { - break; - } - name = stack.get(stack.size() - 2); - } - - if ((ins instanceof ActionSetVariable) || (ins instanceof ActionDefineLocal)) { - usageType = "variable"; - } - - if (ins instanceof ActionSetMember) { - usageType = "member"; - } - - if (name instanceof DirectValueActionItem) { - variables.add(new MyEntry<>((DirectValueActionItem) name, constantPool)); - usageTypes.put((DirectValueActionItem) name, usageType); - } - - // for..in return - if (((ins instanceof ActionEquals) || (ins instanceof ActionEquals2)) && (stack.size() == 1) && (stack.peek() instanceof DirectValueActionItem)) { - stack.push(new DirectValueActionItem(null, 0, new Null(), new ArrayList())); - } - - if (ins instanceof ActionConstantPool) { - constantPool = new ConstantPool(((ActionConstantPool) ins).constantPool); - } - int staticOperation = Graph.SOP_USE_STATIC; //(Boolean) Configuration.getConfig("autoDeobfuscate", true) ? Graph.SOP_SKIP_STATIC : Graph.SOP_USE_STATIC; - - try { - ins.translate(localData, stack, output, staticOperation, path); - } catch (EmptyStackException ex) { - // probably obfucated code, never executed branch - break; - } - if (ins.isExit()) { - break; - } - - if (ins instanceof ActionPush) { - if (!stack.isEmpty()) { - GraphTargetItem top = stack.peek(); - if (top instanceof DirectValueActionItem) { - DirectValueActionItem dvt = (DirectValueActionItem) top; - if ((dvt.value instanceof String) || (dvt.value instanceof ConstantIndex)) { - if (constantPool == null) { - constantPool = new ConstantPool(dvt.constants); - } - strings.put(dvt, constantPool); - } - } - } - } - - if (ins.isBranch() || ins.isJump()) { - if (ins instanceof ActionIf) { - if (stack.isEmpty()) { - break; - } - stack.pop(); - } - visited.add(ip); - List branches = ins.getBranches(code); - for (int b : branches) { - TranslateStack brStack = (TranslateStack) stack.clone(); - if (b >= 0) { - getVariables(constantPool, localData, brStack, output, code, b, variables, functions, strings, visited, usageTypes, path); - } else { - if (debugMode) { - System.out.println("Negative branch:" + b); - } - } - } - // } - break; - } - ip++; - }; - } - - private static void getVariables(List> variables, List functions, HashMap strings, HashMap usageTypes, ActionGraphSource code, int addr, String path) throws InterruptedException { - ActionLocalData localData = new ActionLocalData(); - getVariables(null, localData, new TranslateStack(), new ArrayList(), code, code.adr2pos(addr), variables, functions, strings, new ArrayList(), usageTypes, path); - } - - private List> getVariables(List> variables, HashMap actionsMap, List functions, HashMap strings, HashMap usageTypes, ASMSource src, String path) throws InterruptedException { - List> ret = new ArrayList<>(); - ActionList actions = src.getActions(); - actionsMap.put(src, actions); - getVariables(variables, functions, strings, usageTypes, new ActionGraphSource(actions, version, new HashMap(), new HashMap(), new HashMap()), 0, path); - return ret; - } - - private void getVariables(List tags, String path, List> variables, HashMap actionsMap, List functions, HashMap strings, HashMap usageTypes) throws InterruptedException { - List processed = new ArrayList<>(); - for (Tag t : tags) { - String subPath = path + "/" + t.toString(); - if (t instanceof ASMSource) { - addVariable((ASMSource) t, subPath, processed, variables, actionsMap, functions, strings, usageTypes); - } - if (t instanceof ASMSourceContainer) { - List processed2 = new ArrayList<>(); - for (ASMSource asm : ((ASMSourceContainer) t).getSubItems()) { - addVariable(asm, subPath + "/" + asm.toString(), processed2, variables, actionsMap, functions, strings, usageTypes); - } - } - if (t instanceof DefineSpriteTag) { - getVariables(((DefineSpriteTag) t).getSubTags(), path + "/" + t.toString(), variables, actionsMap, functions, strings, usageTypes); - } - } - } - - private void addVariable(ASMSource asm, String path, List processed, List> variables, HashMap actionsMap, List functions, HashMap strings, HashMap usageTypes) throws InterruptedException { - int pos = 1; - String infPath2 = path; - while (processed.contains(infPath2)) { - pos++; - infPath2 = path + "[" + pos + "]"; - } - processed.add(infPath2); - informListeners("getVariables", infPath2); - getVariables(variables, actionsMap, functions, strings, usageTypes, asm, path); - } - - public void fixAS3Code() { - for (ABCContainerTag abcTag : getAbcList()) { - ABC abc = abcTag.getABC(); - for (MethodBody body : abc.bodies) { - AVM2Code code = body.getCode(); - } - - ((Tag) abcTag).setModified(true); - } - } - - public int deobfuscateAS3Identifiers(RenameType renameType) { - for (Tag tag : tags) { - if (tag instanceof ABCContainerTag) { - ((ABCContainerTag) tag).getABC().deobfuscateIdentifiers(deobfuscated, renameType, true); - tag.setModified(true); - } - } - for (Tag tag : tags) { - if (tag instanceof ABCContainerTag) { - ((ABCContainerTag) tag).getABC().deobfuscateIdentifiers(deobfuscated, renameType, false); - tag.setModified(true); - } - } - for (Tag tag : tags) { - if (tag instanceof SymbolClassTag) { - SymbolClassTag sc = (SymbolClassTag) tag; - for (int i = 0; i < sc.names.size(); i++) { - String newname = deobfuscation.deobfuscateNameWithPackage(true, sc.names.get(i), deobfuscated, renameType, deobfuscated); - if (newname != null) { - sc.names.set(i, newname); - } - } - sc.setModified(true); - } - } - deobfuscation.deobfuscateInstanceNames(true, deobfuscated, renameType, tags, new HashMap()); - return deobfuscated.size(); - } - - public int deobfuscateIdentifiers(RenameType renameType) throws InterruptedException { - FileAttributesTag fileAttributes = getFileAttributes(); - if (fileAttributes == null) { - int cnt = 0; - cnt += deobfuscateAS2Identifiers(renameType); - cnt += deobfuscateAS3Identifiers(renameType); - return cnt; - } else { - if (fileAttributes.actionScript3) { - return deobfuscateAS3Identifiers(renameType); - } else { - return deobfuscateAS2Identifiers(renameType); - } - } - } - - public void renameAS2Identifier(String identifier, String newname) throws InterruptedException { - Map selected = new HashMap<>(); - selected.put(identifier, newname); - renameAS2Identifiers(null, selected); - } - - private int deobfuscateAS2Identifiers(RenameType renameType) throws InterruptedException { - return renameAS2Identifiers(renameType, null); - } - - private int renameAS2Identifiers(RenameType renameType, Map selected) throws InterruptedException { - HashMap actionsMap = new HashMap<>(); - List allFunctions = new ArrayList<>(); - List> allVariableNames = new ArrayList<>(); - HashMap allStrings = new HashMap<>(); - HashMap usageTypes = new HashMap<>(); - - int ret = 0; - getVariables(tags, "", allVariableNames, actionsMap, allFunctions, allStrings, usageTypes); - informListeners("rename", ""); - int fc = 0; - for (MyEntry it : allVariableNames) { - String name = it.getKey().toStringNoH(it.getValue()); - deobfuscation.allVariableNamesStr.add(name); - } - - informListeners("rename", "classes"); - int classCount = 0; - for (Tag t : tags) { - if (t instanceof DoInitActionTag) { - classCount++; - } - } - int cnt = 0; - for (Tag t : tags) { - if (t instanceof DoInitActionTag) { - cnt++; - informListeners("rename", "class " + cnt + "/" + classCount); - DoInitActionTag dia = (DoInitActionTag) t; - String exportName = getExportName(dia.spriteId); - exportName = exportName != null ? exportName : "_unk_"; - final String pkgPrefix = "__Packages."; - String[] classNameParts = null; - if (exportName.startsWith(pkgPrefix)) { - String className = exportName.substring(pkgPrefix.length()); - if (className.contains(".")) { - classNameParts = className.split("\\."); - } else { - classNameParts = new String[]{className}; - } - } - int staticOperation = Graph.SOP_USE_STATIC; //(Boolean) Configuration.getConfig("autoDeobfuscate", true) ? Graph.SOP_SKIP_STATIC : Graph.SOP_USE_STATIC; - List dec; - try { - dec = Action.actionsToTree(dia.getActions(), version, staticOperation, ""/*FIXME*/); - } catch (EmptyStackException ex) { - continue; - } - GraphTargetItem name = null; - for (GraphTargetItem it : dec) { - if (it instanceof ClassActionItem) { - ClassActionItem cti = (ClassActionItem) it; - List methods = new ArrayList<>(); - methods.addAll(cti.functions); - methods.addAll(cti.staticFunctions); - - for (GraphTargetItem gti : methods) { - if (gti instanceof FunctionActionItem) { - FunctionActionItem fun = (FunctionActionItem) gti; - if (fun.calculatedFunctionName instanceof DirectValueActionItem) { - DirectValueActionItem dvf = (DirectValueActionItem) fun.calculatedFunctionName; - String fname = dvf.toStringNoH(null); - String changed = deobfuscation.deobfuscateName(false, fname, false, "method", deobfuscated, renameType, selected); - if (changed != null) { - deobfuscated.put(fname, changed); - } - } - } - } - - List vars = new ArrayList<>(); - for (MyEntry item : cti.vars) { - vars.add(item.getKey()); - } - for (MyEntry item : cti.staticVars) { - vars.add(item.getKey()); - } - for (GraphTargetItem gti : vars) { - if (gti instanceof DirectValueActionItem) { - DirectValueActionItem dvf = (DirectValueActionItem) gti; - String vname = dvf.toStringNoH(null); - String changed = deobfuscation.deobfuscateName(false, vname, false, "attribute", deobfuscated, renameType, selected); - if (changed != null) { - deobfuscated.put(vname, changed); - } - } - } - - name = cti.className; - break; - } - if (it instanceof InterfaceActionItem) { - InterfaceActionItem ift = (InterfaceActionItem) it; - name = ift.name; - } - } - - if (name != null) { - int pos = 0; - while (name instanceof GetMemberActionItem) { - GetMemberActionItem mem = (GetMemberActionItem) name; - GraphTargetItem memberName = mem.memberName; - if (memberName instanceof DirectValueActionItem) { - DirectValueActionItem dvt = (DirectValueActionItem) memberName; - String nameStr = dvt.toStringNoH(null); - if (classNameParts != null) { - if (classNameParts.length - 1 - pos < 0) { - break; - } - } - String changedNameStr = nameStr; - if (classNameParts != null) { - changedNameStr = classNameParts[classNameParts.length - 1 - pos]; - } - String changedNameStr2 = deobfuscation.deobfuscateName(false, changedNameStr, pos == 0, pos == 0 ? "class" : "package", deobfuscated, renameType, selected); - if (changedNameStr2 != null) { - changedNameStr = changedNameStr2; - } - ret++; - deobfuscated.put(nameStr, changedNameStr); - pos++; - } - name = mem.object; - } - if (name instanceof GetVariableActionItem) { - GetVariableActionItem var = (GetVariableActionItem) name; - if (var.name instanceof DirectValueActionItem) { - DirectValueActionItem dvt = (DirectValueActionItem) var.name; - String nameStr = dvt.toStringNoH(null); - if (classNameParts != null) { - if (classNameParts.length - 1 - pos < 0) { - break; - } - } - String changedNameStr = nameStr; - if (classNameParts != null) { - changedNameStr = classNameParts[classNameParts.length - 1 - pos]; - } - String changedNameStr2 = deobfuscation.deobfuscateName(false, changedNameStr, pos == 0, pos == 0 ? "class" : "package", deobfuscated, renameType, selected); - if (changedNameStr2 != null) { - changedNameStr = changedNameStr2; - } - ret++; - deobfuscated.put(nameStr, changedNameStr); - pos++; - } - } - } - t.setModified(true); - } - } - - for (GraphSourceItem fun : allFunctions) { - fc++; - informListeners("rename", "function " + fc + "/" + allFunctions.size()); - if (fun instanceof ActionDefineFunction) { - ActionDefineFunction f = (ActionDefineFunction) fun; - if (f.functionName.isEmpty()) { // anonymous function, leave as is - continue; - } - String changed = deobfuscation.deobfuscateName(false, f.functionName, false, "function", deobfuscated, renameType, selected); - if (changed != null) { - f.replacedFunctionName = changed; - ret++; - } - } - if (fun instanceof ActionDefineFunction2) { - ActionDefineFunction2 f = (ActionDefineFunction2) fun; - if (f.functionName.isEmpty()) { // anonymous function, leave as is - continue; - } - String changed = deobfuscation.deobfuscateName(false, f.functionName, false, "function", deobfuscated, renameType, selected); - if (changed != null) { - f.replacedFunctionName = changed; - ret++; - } - } - } - - HashSet stringsNoVarH = new HashSet<>(); - List allVariableNamesDv = new ArrayList<>(); - for (MyEntry it : allVariableNames) { - allVariableNamesDv.add(it.getKey()); - } - for (DirectValueActionItem ti : allStrings.keySet()) { - if (!allVariableNamesDv.contains(ti)) { - stringsNoVarH.add(System.identityHashCode(allStrings.get(ti)) + "_" + ti.toStringNoH(allStrings.get(ti))); - } - } - - int vc = 0; - for (MyEntry it : allVariableNames) { - vc++; - String name = it.getKey().toStringNoH(it.getValue()); - String changed = deobfuscation.deobfuscateName(false, name, false, usageTypes.get(it.getKey()), deobfuscated, renameType, selected); - if (changed != null) { - boolean addNew = false; - String h = System.identityHashCode(it.getKey()) + "_" + name; - if (stringsNoVarH.contains(h)) { - addNew = true; - } - ActionPush pu = (ActionPush) it.getKey().src; - if (pu.replacement == null) { - pu.replacement = new ArrayList<>(); - pu.replacement.addAll(pu.values); - } - if (pu.replacement.get(it.getKey().pos) instanceof ConstantIndex) { - ConstantIndex ci = (ConstantIndex) pu.replacement.get(it.getKey().pos); - ConstantPool pool = it.getValue(); - if (pool == null) { - continue; - } - if (pool.constants == null) { - continue; - } - if (addNew) { - pool.constants.add(changed); - ci.index = pool.constants.size() - 1; - } else { - pool.constants.set(ci.index, changed); - } - } else { - pu.replacement.set(it.getKey().pos, changed); - } - ret++; - } - } - - for (ASMSource src : actionsMap.keySet()) { - actionsMap.get(src).removeNops(); - src.setActions(actionsMap.get(src)); - src.setModified(); - } - - deobfuscation.deobfuscateInstanceNames(false, deobfuscated, renameType, tags, selected); - return ret; - } - - public void exportFla(AbortRetryIgnoreHandler handler, String outfile, String swfName, String generator, String generatorVerName, String generatorVersion, boolean parallel, FLAVersion version) throws IOException { - XFLConverter.convertSWF(handler, this, swfName, outfile, true, generator, generatorVerName, generatorVersion, parallel, version); - clearAllCache(); - } - - public void exportXfl(AbortRetryIgnoreHandler handler, String outfile, String swfName, String generator, String generatorVerName, String generatorVersion, boolean parallel, FLAVersion version) throws IOException { - XFLConverter.convertSWF(handler, this, swfName, outfile, false, generator, generatorVerName, generatorVersion, parallel, version); - clearAllCache(); - } - - public static AffineTransform matrixToTransform(MATRIX mat) { - return new AffineTransform(mat.getScaleXFloat(), mat.getRotateSkew0Float(), - mat.getRotateSkew1Float(), mat.getScaleYFloat(), - mat.translateX, mat.translateY); - } - - public SerializableImage getFromCache(String key) { - if (frameCache.contains(key)) { - return frameCache.get(key); - } - return null; - } - - public byte[] getFromCache(SoundTag soundTag) { - if (soundCache.contains(soundTag)) { - return soundCache.get(soundTag); - } - return null; - } - - public void putToCache(String key, SerializableImage img) { - if (Configuration.useFrameCache.get()) { - frameCache.put(key, img); - } - } - - public void putToCache(SoundTag soundTag, byte[] data) { - soundCache.put(soundTag, data); - } - - public void clearImageCache() { - frameCache.clear(); - DefineSpriteTag.clearCache(); - DefineButtonTag.clearCache(); - DefineButton2Tag.clearCache(); - for (Tag tag : tags) { - if (tag instanceof ImageTag) { - ((ImageTag) tag).clearCache(); - } - } - } - - public void clearScriptCache() { - as2Cache.clear(); - as3Cache.clear(); - } - - public void clearAllCache() { - characters = null; - abcList = null; - timeline = null; - clearImageCache(); - clearScriptCache(); - Cache.clearAll(); - Helper.clearShapeCache(); - System.gc(); - } - - public static void uncache(ASMSource src) { - if (src != null) { - src.getSwf().as2Cache.remove(src); - } - } - - public static void uncache(ScriptPack pack) { - if (pack != null) { - pack.getSwf().as3Cache.remove(pack); - } - } - - public static boolean isCached(ASMSource src) { - return src.getSwf().as2Cache.contains(src); - } - - public static boolean isCached(ScriptPack pack) { - return pack.getSwf().as3Cache.contains(pack); - } - - public static CachedScript getCached(ASMSource src, ActionList actions) throws InterruptedException { - SWF swf = src.getSwf(); - if (swf.as2Cache.contains(src)) { - return swf.as2Cache.get(src); - } - - if (actions == null) { - actions = src.getActions(); - } - - HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), true); - Action.actionsToSource(src, actions, src.toString()/*FIXME?*/, writer); - List hilights = writer.instructionHilights; - String srcNoHex = writer.toString(); - CachedScript res = new CachedScript(srcNoHex, hilights); - swf.as2Cache.put(src, res); - return res; - } - - public static CachedDecompilation getCached(ScriptPack pack) throws InterruptedException { - SWF swf = pack.getSwf(); - if (swf.as3Cache.contains(pack)) { - return swf.as3Cache.get(pack); - } - - int scriptIndex = pack.scriptIndex; - ScriptInfo script = null; - if (scriptIndex > -1) { - script = pack.abc.script_info.get(scriptIndex); - } - boolean parallel = Configuration.parallelSpeedUp.get(); - HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), true); - pack.toSource(writer, script.traits.traits, ScriptExportMode.AS, parallel); - HighlightedText hilightedCode = new HighlightedText(writer); - CachedDecompilation res = new CachedDecompilation(hilightedCode); - swf.as3Cache.put(pack, res); - - return res; - } - - public static RECT fixRect(RECT rect) { - RECT ret = new RECT(); - ret.Xmin = rect.Xmin; - ret.Xmax = rect.Xmax; - ret.Ymin = rect.Ymin; - ret.Ymax = rect.Ymax; - - if (ret.Xmax <= 0) { - ret.Xmax = ret.getWidth(); - ret.Xmin = 0; - } - if (ret.Ymax <= 0) { - ret.Ymax = ret.getHeight(); - ret.Ymin = 0; - } - if (ret.Xmin < 0) { - ret.Xmax += (-ret.Xmin); - ret.Xmin = 0; - } - if (ret.Ymin < 0) { - ret.Ymax += (-ret.Ymin); - ret.Ymin = 0; - } - - if (ret.getWidth() < 1 || ret.getHeight() < 1) { - ret.Xmin = 0; - ret.Ymin = 0; - ret.Xmax = 20; - ret.Ymax = 20; - } - return ret; - } - - public static void frameToSvg(Timeline timeline, int frame, int time, DepthState stateUnderCursor, int mouseButton, SVGExporter exporter, ColorTransform colorTransform, int level, double zoom) throws IOException { - if (timeline.getFrameCount() <= frame) { - return; - } - Frame frameObj = timeline.getFrame(frame); - List clips = new ArrayList<>(); - List prevClips = new ArrayList<>(); - - int maxDepth = timeline.getMaxDepth(); - for (int i = 1; i <= maxDepth; i++) { - for (int c = 0; c < clips.size(); c++) { - if (clips.get(c).depth == i) { - exporter.setClip(prevClips.get(c)); - prevClips.remove(c); - clips.remove(c); - } - } - if (!frameObj.layers.containsKey(i)) { - continue; - } - DepthState layer = frameObj.layers.get(i); - if (!timeline.swf.getCharacters().containsKey(layer.characterId)) { - continue; - } - if (!layer.isVisible) { - continue; - } - - CharacterTag character = timeline.swf.getCharacter(layer.characterId); - if (colorTransform == null) { - colorTransform = new ColorTransform(); - } - - ColorTransform clrTrans = colorTransform.clone(); - if (layer.colorTransForm != null && layer.blendMode <= 1) { // Normal blend mode - clrTrans = colorTransform.merge(layer.colorTransForm); - } - - if (character instanceof DrawableTag) { - DrawableTag drawable = (DrawableTag) character; - - String assetName; - Tag drawableTag = (Tag) drawable; - RECT boundRect = drawable.getRect(); - if (exporter.exportedTags.containsKey(drawableTag)) { - assetName = exporter.exportedTags.get(drawableTag); - } else { - assetName = getTagIdPrefix(drawableTag, exporter); - exporter.exportedTags.put(drawableTag, assetName); - exporter.createDefGroup(new ExportRectangle(boundRect), assetName); - drawable.toSVG(exporter, layer.ratio, clrTrans, level + 1, zoom); - exporter.endGroup(); - } - ExportRectangle rect = new ExportRectangle(boundRect); - - // TODO: if (layer.filters != null) - // TODO: if (layer.blendMode > 1) - if (layer.clipDepth > -1) { - String clipName = exporter.getUniqueId("clipPath"); - exporter.createClipPath(new Matrix(), clipName); - SvgClip clip = new SvgClip(clipName, layer.clipDepth); - clips.add(clip); - prevClips.add(exporter.getClip()); - Matrix mat = Matrix.getTranslateInstance(rect.xMin, rect.yMin).preConcatenate(new Matrix(layer.matrix)); - exporter.addUse(mat, boundRect, assetName); - exporter.setClip(clip.shape); - exporter.endGroup(); - } else { - Matrix mat = Matrix.getTranslateInstance(rect.xMin, rect.yMin).preConcatenate(new Matrix(layer.matrix)); - exporter.addUse(mat, boundRect, assetName); - } - } - } - } - - private static String getTagIdPrefix(Tag tag, SVGExporter exporter) { - if (tag instanceof ShapeTag) { - return exporter.getUniqueId("shape"); - } - if (tag instanceof MorphShapeTag) { - return exporter.getUniqueId("morphshape"); - } - if (tag instanceof DefineSpriteTag) { - return exporter.getUniqueId("sprite"); - } - if (tag instanceof TextTag) { - return exporter.getUniqueId("text"); - } - if (tag instanceof ButtonTag) { - return exporter.getUniqueId("button"); - } - return exporter.getUniqueId("tag"); - } - - public static SerializableImage frameToImageGet(Timeline timeline, int frame, int time, DepthState stateUnderCursor, int mouseButton, RECT displayRect, Matrix transformation, ColorTransform colorTransform, Color backGroundColor, boolean useCache, double zoom) { - SWF swf = timeline.swf; - String key = "frame_" + frame + "_" + timeline.id + "_" + swf.hashCode() + "_" + zoom; - SerializableImage image; - if (useCache) { - image = swf.getFromCache(key); - if (image != null) { - return image; - } - } - - if (timeline.getFrameCount() == 0) { - return new SerializableImage(1, 1, SerializableImage.TYPE_INT_ARGB); - } - - RECT rect = displayRect; - image = new SerializableImage((int) (rect.getWidth() * zoom / SWF.unitDivisor) + 1, - (int) (rect.getHeight() * zoom / SWF.unitDivisor) + 1, SerializableImage.TYPE_INT_ARGB); - if (backGroundColor == null) { - image.fillTransparent(); - } else { - Graphics2D g = (Graphics2D) image.getBufferedImage().getGraphics(); - g.setComposite(AlphaComposite.Src); - g.setColor(backGroundColor); - g.fill(new Rectangle(image.getWidth(), image.getHeight())); - } - - Matrix m = transformation.clone(); - m.translate(-rect.Xmin * zoom, -rect.Ymin * zoom); - m.scale(zoom); - RenderContext renderContext = new RenderContext(); - renderContext.stateUnderCursor = stateUnderCursor; - renderContext.mouseButton = mouseButton; - frameToImage(timeline, frame, time, renderContext, image, m, colorTransform); - if (useCache) { - swf.putToCache(key, image); - } - - return image; - } - - public static void framesToImage(Timeline timeline, List ret, int startFrame, int stopFrame, RenderContext renderContext, RECT displayRect, int totalFrameCount, Stack visited, Matrix transformation, ColorTransform colorTransform, double zoom) { - RECT rect = displayRect; - for (int f = 0; f < timeline.getFrameCount(); f++) { - SerializableImage image = new SerializableImage((int) (rect.getWidth() / SWF.unitDivisor) + 1, - (int) (rect.getHeight() / SWF.unitDivisor) + 1, SerializableImage.TYPE_INT_ARGB); - image.fillTransparent(); - Matrix m = new Matrix(); - m.translate(-rect.Xmin, -rect.Ymin); - frameToImage(timeline, f, 0, renderContext, image, m, colorTransform); - ret.add(image); - } - } - - public static void frameToImage(Timeline timeline, int frame, int time, RenderContext renderContext, SerializableImage image, Matrix transformation, ColorTransform colorTransform) { - double unzoom = SWF.unitDivisor; - if (timeline.getFrameCount() <= frame) { - return; - } - Frame frameObj = timeline.getFrame(frame); - Graphics2D g = (Graphics2D) image.getGraphics(); - g.setPaint(frameObj.backgroundColor.toColor()); - g.fill(new Rectangle(image.getWidth(), image.getHeight())); - g.setTransform(transformation.toTransform()); - List clips = new ArrayList<>(); - List prevClips = new ArrayList<>(); - - int maxDepth = timeline.getMaxDepth(); - for (int i = 1; i <= maxDepth; i++) { - for (int c = 0; c < clips.size(); c++) { - if (clips.get(c).depth == i) { - g.setClip(prevClips.get(c)); - prevClips.remove(c); - clips.remove(c); - } - } - if (!frameObj.layers.containsKey(i)) { - continue; - } - DepthState layer = frameObj.layers.get(i); - if (!timeline.swf.getCharacters().containsKey(layer.characterId)) { - continue; - } - if (!layer.isVisible) { - continue; - } - - CharacterTag character = timeline.swf.getCharacter(layer.characterId); - Matrix mat = new Matrix(layer.matrix); - mat = mat.preConcatenate(transformation); - - if (colorTransform == null) { - colorTransform = new ColorTransform(); - } - - ColorTransform clrTrans = colorTransform.clone(); - if (layer.colorTransForm != null && layer.blendMode <= 1) { // Normal blend mode - clrTrans = colorTransform.merge(layer.colorTransForm); - } - - boolean showPlaceholder = false; - if (character instanceof DrawableTag) { - DrawableTag drawable = (DrawableTag) character; - Matrix drawMatrix = new Matrix(); - int drawableFrameCount = drawable.getNumFrames(); - if (drawableFrameCount == 0) { - drawableFrameCount = 1; - } - - int dframe; - if (timeline.fontFrameNum != -1) { - dframe = timeline.fontFrameNum; - } else { - dframe = (time + layer.time) % drawableFrameCount; - } - - if (character instanceof ButtonTag) { - dframe = ButtonTag.FRAME_UP; - if (renderContext.stateUnderCursor == layer) { - if (renderContext.mouseButton > 0) { - dframe = ButtonTag.FRAME_DOWN; - } else { - dframe = ButtonTag.FRAME_OVER; - } - } - } - - RECT boundRect = drawable.getRect(); - ExportRectangle rect = new ExportRectangle(boundRect); - rect = mat.transform(rect); - Matrix m = mat.clone(); - if (layer.filters != null && layer.filters.size() > 0) { - // calculate size after applying the filters - double deltaXMax = 0; - double deltaYMax = 0; - for (FILTER filter : layer.filters) { - double x = filter.getDeltaX(); - double y = filter.getDeltaY(); - deltaXMax = Math.max(x, deltaXMax); - deltaYMax = Math.max(y, deltaYMax); - } - rect.xMin -= deltaXMax * unzoom; - rect.xMax += deltaXMax * unzoom; - rect.yMin -= deltaYMax * unzoom; - rect.yMax += deltaYMax * unzoom; - } - - rect.xMin -= 1 * unzoom; - rect.yMin -= 1 * unzoom; - rect.xMin = Math.max(0, rect.xMin); - rect.yMin = Math.max(0, rect.yMin); - - int newWidth = (int) (rect.getWidth() / unzoom); - int newHeight = (int) (rect.getHeight() / unzoom); - int deltaX = (int) (rect.xMin / unzoom); - int deltaY = (int) (rect.yMin / unzoom); - newWidth = Math.min(image.getWidth() - deltaX, newWidth) + 1; - newHeight = Math.min(image.getHeight() - deltaY, newHeight) + 1; - - if (newWidth <= 0 || newHeight <= 0) { - continue; - } - - m.translate(-rect.xMin, -rect.yMin); - drawMatrix.translate(rect.xMin, rect.yMin); - - SerializableImage img = null; - String cacheKey = null; - if (drawable instanceof ShapeTag) { - cacheKey = ((ShapeTag) drawable).getCharacterId() + m.toString() + clrTrans.toString(); - img = renderContext.shapeCache.get(cacheKey); - } - - if (img == null) { - img = new SerializableImage(newWidth, newHeight, SerializableImage.TYPE_INT_ARGB); - img.fillTransparent(); - - drawable.toImage(dframe, layer.time + time, layer.ratio, renderContext, img, m, clrTrans); - - if (cacheKey != null) { - renderContext.shapeCache.put(cacheKey, img); - } - } - - /*//if (renderContext.stateUnderCursor == layer) { - if (true) { - BufferedImage bi = img.getBufferedImage(); - ColorModel cm = bi.getColorModel(); - boolean isAlphaPremultiplied = cm.isAlphaPremultiplied(); - WritableRaster raster = bi.copyData(null); - img = new SerializableImage(new BufferedImage(cm, raster, isAlphaPremultiplied, null)); - Graphics2D gg = (Graphics2D) img.getGraphics(); - gg.setStroke(new BasicStroke(3)); - gg.setPaint(Color.red); - gg.setTransform(AffineTransform.getTranslateInstance(0, 0)); - gg.draw(SHAPERECORD.twipToPixelShape(drawable.getOutline(dframe, layer.time + time, layer.ratio, renderContext, m))); - }*/ - if (layer.filters != null) { - for (FILTER filter : layer.filters) { - img = filter.apply(img); - } - } - if (layer.blendMode > 1) { - if (layer.colorTransForm != null) { - img = layer.colorTransForm.apply(img); - } - } - - drawMatrix.translateX /= unzoom; - drawMatrix.translateY /= unzoom; - AffineTransform trans = drawMatrix.toTransform(); - - switch (layer.blendMode) { - case 0: - case 1: - g.setComposite(AlphaComposite.SrcOver); - break; - case 2: // Layer - g.setComposite(AlphaComposite.SrcOver); - break; - case 3: - g.setComposite(BlendComposite.Multiply); - break; - case 4: - g.setComposite(BlendComposite.Screen); - break; - case 5: - g.setComposite(BlendComposite.Lighten); - break; - case 6: - g.setComposite(BlendComposite.Darken); - break; - case 7: - g.setComposite(BlendComposite.Difference); - break; - case 8: - g.setComposite(BlendComposite.Add); - break; - case 9: - g.setComposite(BlendComposite.Subtract); - break; - case 10: - g.setComposite(BlendComposite.Invert); - break; - case 11: - g.setComposite(BlendComposite.Alpha); - break; - case 12: - g.setComposite(BlendComposite.Erase); - break; - case 13: - g.setComposite(BlendComposite.Overlay); - break; - case 14: - g.setComposite(BlendComposite.HardLight); - break; - default: // Not implemented - g.setComposite(AlphaComposite.SrcOver); - break; - } - - if (layer.clipDepth > -1) { - BufferedImage mask = new BufferedImage(image.getWidth(), image.getHeight(), image.getType()); - Graphics2D gm = (Graphics2D) mask.getGraphics(); - gm.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); - gm.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); - gm.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - gm.setComposite(AlphaComposite.Src); - gm.setColor(new Color(0, 0, 0, 0f)); - gm.fillRect(0, 0, image.getWidth(), image.getHeight()); - gm.setTransform(trans); - gm.drawImage(img.getBufferedImage(), 0, 0, null); - Clip clip = new Clip(Helper.imageToShape(mask), layer.clipDepth); // Maybe we can get current outline instead converting from image (?) - clips.add(clip); - prevClips.add(g.getClip()); - g.setTransform(AffineTransform.getTranslateInstance(0, 0)); - g.setClip(clip.shape); - } else { - g.setTransform(trans); - g.drawImage(img.getBufferedImage(), 0, 0, null); - } - } else if (character instanceof BoundedTag) { - showPlaceholder = true; - } - - if (showPlaceholder) { - mat.translateX /= unzoom; - mat.translateY /= unzoom; - AffineTransform trans = mat.toTransform(); - g.setTransform(trans); - BoundedTag b = (BoundedTag) character; - g.setPaint(new Color(255, 255, 255, 128)); - g.setComposite(BlendComposite.Invert); - RECT r = b.getRect(); - int div = (int) unzoom; - g.drawString(character.toString(), r.Xmin / div + 3, r.Ymin / div + 15); - g.draw(new Rectangle(r.Xmin / div, r.Ymin / div, r.getWidth() / div, r.getHeight() / div)); - g.drawLine(r.Xmin / div, r.Ymin / div, r.Xmax / div, r.Ymax / div); - g.drawLine(r.Xmax / div, r.Ymin / div, r.Xmin / div, r.Ymax / div); - g.setComposite(AlphaComposite.Dst); - } - } - - g.setTransform(AffineTransform.getScaleInstance(1, 1)); - } - - private void removeTagWithDependenciesFromTimeline(Tag toRemove, Timeline timeline) { - Map stage = new HashMap<>(); - Set dependingChars = new HashSet<>(); - if (toRemove instanceof CharacterTag) { - int characterId = ((CharacterTag) toRemove).getCharacterId(); - - if (characterId != 0) { - dependingChars.add(characterId); - for (int i = 0; i < timeline.tags.size(); i++) { - Tag t = timeline.tags.get(i); - if (t instanceof CharacterIdTag) { - CharacterIdTag c = (CharacterIdTag) t; - Set needed = new HashSet<>(); - t.getNeededCharacters(needed); - if (needed.contains(characterId)) { - dependingChars.add(c.getCharacterId()); - } - } - } - } - } - - for (int i = 0; i < timeline.tags.size(); i++) { - Tag t = timeline.tags.get(i); - if (t instanceof RemoveTag) { - RemoveTag rt = (RemoveTag) t; - int depth = rt.getDepth(); - if (stage.containsKey(depth)) { - int currentCharId = stage.get(depth); - stage.remove(depth); - if (dependingChars.contains(currentCharId)) { - timeline.tags.remove(i); - i--; - continue; - } - } - } - if (t instanceof PlaceObjectTypeTag) { - PlaceObjectTypeTag po = (PlaceObjectTypeTag) t; - int placeCharId = po.getCharacterId(); - int depth = po.getDepth(); - if (placeCharId != 0) { - stage.put(depth, placeCharId); - if (dependingChars.contains(placeCharId)) { - timeline.tags.remove(i); - i--; - continue; - } - } - } - if (t instanceof CharacterIdTag) { - CharacterIdTag c = (CharacterIdTag) t; - if (dependingChars.contains(c.getCharacterId())) { - timeline.tags.remove(i); - i--; - continue; - } - } - Set needed = new HashSet<>(); - t.getNeededCharacters(needed); - for (int dep : dependingChars) { - if (needed.contains(dep)) { - timeline.tags.remove(i); - i--; - continue; - } - } - if (t == toRemove) { - timeline.tags.remove(i); - i--; - continue; - } - if (t instanceof Timelined) { - removeTagWithDependenciesFromTimeline(toRemove, ((Timelined) t).getTimeline()); - } - } - } - - private boolean removeTagFromTimeline(Tag toRemove, Timeline timeline) { - boolean modified = false; - int characterId = -1; - if (toRemove instanceof CharacterTag) { - characterId = ((CharacterTag) toRemove).getCharacterId(); - modified = timeline.removeCharacter(characterId); - } - for (int i = 0; i < timeline.tags.size(); i++) { - Tag t = timeline.tags.get(i); - if (t == toRemove) { - timeline.tags.remove(t); - i--; - continue; - } - - if (toRemove instanceof CharacterTag) { - if (t.removeCharacter(characterId)) { - modified = true; - i = -1; - continue; - } - } - - if (t instanceof DefineSpriteTag) { - DefineSpriteTag spr = (DefineSpriteTag) t; - boolean sprModified = removeTagFromTimeline(toRemove, spr.getTimeline()); - if (sprModified) { - spr.setModified(true); - } - modified |= sprModified; - } - } - return modified; - } - - public void removeTags(Collection tags, boolean removeDependencies) { - Set timelineds = new HashSet<>(); - for (Tag tag : tags) { - Timelined timelined = tag.getTimelined(); - timelineds.add(timelined); - removeTagInternal(timelined, tag, removeDependencies); - } - - for (Timelined timelined : timelineds) { - resetTimelines(timelined); - } - - updateCharacters(); - clearImageCache(); - } - - public void removeTag(Tag tag, boolean removeDependencies) { - Timelined timelined = tag.getTimelined(); - removeTagInternal(timelined, tag, removeDependencies); - resetTimelines(timelined); - updateCharacters(); - clearImageCache(); - } - - private void removeTagInternal(Timelined timelined, Tag tag, boolean removeDependencies) { - if (tag instanceof ShowFrameTag || ShowFrameTag.isNestedTagType(tag.getId())) { - List tags; - if (timelined instanceof DefineSpriteTag) { - DefineSpriteTag sprite = (DefineSpriteTag) timelined; - tags = sprite.getSubTags(); - } else { - tags = this.tags; - } - tags.remove(tag); - if (timelined instanceof DefineSpriteTag) { - DefineSpriteTag sprite = (DefineSpriteTag) timelined; - sprite.setModified(true); - } - timelined.resetTimeline(); - } else { - // timeline should be always the swf here - if (removeDependencies) { - removeTagWithDependenciesFromTimeline(tag, timelined.getTimeline()); - if (timelined instanceof DefineSpriteTag) { - DefineSpriteTag sprite = (DefineSpriteTag) timelined; - sprite.setModified(true); - } - } else { - removeTagFromTimeline(tag, timeline); - } - } - } - - @Override - public String toString() { - return getShortFileName(); - } -} +/* + * Copyright (C) 2010-2015 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash; + +import SevenZip.Compression.LZMA.Decoder; +import SevenZip.Compression.LZMA.Encoder; +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.CachedDecompilation; +import com.jpexs.decompiler.flash.abc.ClassPath; +import com.jpexs.decompiler.flash.abc.RenameType; +import com.jpexs.decompiler.flash.abc.ScriptPack; +import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; +import com.jpexs.decompiler.flash.abc.types.MethodBody; +import com.jpexs.decompiler.flash.abc.types.ScriptInfo; +import com.jpexs.decompiler.flash.action.Action; +import com.jpexs.decompiler.flash.action.ActionGraphSource; +import com.jpexs.decompiler.flash.action.ActionList; +import com.jpexs.decompiler.flash.action.ActionLocalData; +import com.jpexs.decompiler.flash.action.CachedScript; +import com.jpexs.decompiler.flash.action.model.ConstantPool; +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.clauses.ClassActionItem; +import com.jpexs.decompiler.flash.action.model.clauses.InterfaceActionItem; +import com.jpexs.decompiler.flash.action.swf4.ActionEquals; +import com.jpexs.decompiler.flash.action.swf4.ActionGetVariable; +import com.jpexs.decompiler.flash.action.swf4.ActionIf; +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.swf5.ActionCallFunction; +import com.jpexs.decompiler.flash.action.swf5.ActionCallMethod; +import com.jpexs.decompiler.flash.action.swf5.ActionConstantPool; +import com.jpexs.decompiler.flash.action.swf5.ActionDefineFunction; +import com.jpexs.decompiler.flash.action.swf5.ActionDefineLocal; +import com.jpexs.decompiler.flash.action.swf5.ActionDefineLocal2; +import com.jpexs.decompiler.flash.action.swf5.ActionEquals2; +import com.jpexs.decompiler.flash.action.swf5.ActionGetMember; +import com.jpexs.decompiler.flash.action.swf5.ActionNewMethod; +import com.jpexs.decompiler.flash.action.swf5.ActionNewObject; +import com.jpexs.decompiler.flash.action.swf5.ActionSetMember; +import com.jpexs.decompiler.flash.action.swf7.ActionDefineFunction2; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.dumpview.DumpInfo; +import com.jpexs.decompiler.flash.dumpview.DumpInfoSwfNode; +import com.jpexs.decompiler.flash.ecma.Null; +import com.jpexs.decompiler.flash.exporters.commonshape.ExportRectangle; +import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; +import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter; +import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; +import com.jpexs.decompiler.flash.exporters.script.AS2ScriptExporter; +import com.jpexs.decompiler.flash.exporters.script.AS3ScriptExporter; +import com.jpexs.decompiler.flash.exporters.settings.ScriptExportSettings; +import com.jpexs.decompiler.flash.helpers.HighlightedText; +import com.jpexs.decompiler.flash.helpers.HighlightedTextWriter; +import com.jpexs.decompiler.flash.helpers.ImageHelper; +import com.jpexs.decompiler.flash.helpers.SWFDecompilerPlugin; +import com.jpexs.decompiler.flash.helpers.collections.MyEntry; +import com.jpexs.decompiler.flash.helpers.hilight.Highlighting; +import com.jpexs.decompiler.flash.tags.ABCContainerTag; +import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag; +import com.jpexs.decompiler.flash.tags.DefineButton2Tag; +import com.jpexs.decompiler.flash.tags.DefineButtonTag; +import com.jpexs.decompiler.flash.tags.DefineSpriteTag; +import com.jpexs.decompiler.flash.tags.DoInitActionTag; +import com.jpexs.decompiler.flash.tags.EndTag; +import com.jpexs.decompiler.flash.tags.ExportAssetsTag; +import com.jpexs.decompiler.flash.tags.FileAttributesTag; +import com.jpexs.decompiler.flash.tags.JPEGTablesTag; +import com.jpexs.decompiler.flash.tags.ShowFrameTag; +import com.jpexs.decompiler.flash.tags.SymbolClassTag; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.TagStub; +import com.jpexs.decompiler.flash.tags.VideoFrameTag; +import com.jpexs.decompiler.flash.tags.base.ASMSource; +import com.jpexs.decompiler.flash.tags.base.ASMSourceContainer; +import com.jpexs.decompiler.flash.tags.base.BoundedTag; +import com.jpexs.decompiler.flash.tags.base.ButtonTag; +import com.jpexs.decompiler.flash.tags.base.CharacterIdTag; +import com.jpexs.decompiler.flash.tags.base.CharacterTag; +import com.jpexs.decompiler.flash.tags.base.DrawableTag; +import com.jpexs.decompiler.flash.tags.base.Exportable; +import com.jpexs.decompiler.flash.tags.base.FontTag; +import com.jpexs.decompiler.flash.tags.base.ImageTag; +import com.jpexs.decompiler.flash.tags.base.MorphShapeTag; +import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; +import com.jpexs.decompiler.flash.tags.base.RemoveTag; +import com.jpexs.decompiler.flash.tags.base.RenderContext; +import com.jpexs.decompiler.flash.tags.base.ShapeTag; +import com.jpexs.decompiler.flash.tags.base.SoundTag; +import com.jpexs.decompiler.flash.tags.base.TextTag; +import com.jpexs.decompiler.flash.tags.enums.ImageFormat; +import com.jpexs.decompiler.flash.timeline.AS2Package; +import com.jpexs.decompiler.flash.timeline.Clip; +import com.jpexs.decompiler.flash.timeline.DepthState; +import com.jpexs.decompiler.flash.timeline.Frame; +import com.jpexs.decompiler.flash.timeline.FrameScript; +import com.jpexs.decompiler.flash.timeline.SvgClip; +import com.jpexs.decompiler.flash.timeline.TagScript; +import com.jpexs.decompiler.flash.timeline.Timeline; +import com.jpexs.decompiler.flash.timeline.Timelined; +import com.jpexs.decompiler.flash.treeitems.SWFList; +import com.jpexs.decompiler.flash.treeitems.TreeItem; +import com.jpexs.decompiler.flash.types.ColorTransform; +import com.jpexs.decompiler.flash.types.MATRIX; +import com.jpexs.decompiler.flash.types.RECT; +import com.jpexs.decompiler.flash.types.annotations.Internal; +import com.jpexs.decompiler.flash.types.filters.BlendComposite; +import com.jpexs.decompiler.flash.types.filters.FILTER; +import com.jpexs.decompiler.flash.xfl.FLAVersion; +import com.jpexs.decompiler.flash.xfl.XFLConverter; +import com.jpexs.decompiler.graph.Graph; +import com.jpexs.decompiler.graph.GraphSourceItem; +import com.jpexs.decompiler.graph.GraphSourceItemContainer; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.TranslateStack; +import com.jpexs.decompiler.graph.model.LocalData; +import com.jpexs.helpers.Cache; +import com.jpexs.helpers.Helper; +import com.jpexs.helpers.NulStream; +import com.jpexs.helpers.ProgressListener; +import com.jpexs.helpers.SerializableImage; +import com.jpexs.helpers.utf8.Utf8Helper; +import java.awt.AlphaComposite; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.Shape; +import java.awt.geom.AffineTransform; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.EmptyStackException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Stack; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.zip.DeflaterOutputStream; +import java.util.zip.InflaterInputStream; + +/** + * Class representing SWF file + * + * @author JPEXS + */ +public final class SWF implements SWFContainerItem, Timelined { + + // big object for testing cleanup + //BigObject bigObj = new BigObject(); + /** + * Default version of SWF file format + */ + public static final int DEFAULT_VERSION = 10; + + /** + * Maximum SWF file format version Needs to be fixed when SWF versions + * reaches this value + */ + public static final int MAX_VERSION = 30; + + /** + * Tags inside of file + */ + public List tags = new ArrayList<>(); + + @Internal + public boolean hasEndTag = true; + + /** + * ExportRectangle for the display + */ + public RECT displayRect; + + /** + * Movie frame rate + */ + public int frameRate; + + /** + * Number of frames in movie + */ + public int frameCount; + + /** + * Version of SWF + */ + public int version; + + /** + * Uncompressed size of the file + */ + @Internal + public long fileSize; + + /** + * Used compression mode + */ + public SWFCompression compression = SWFCompression.NONE; + + /** + * Compressed size of the file (LZMA) + */ + @Internal + public long compressedSize; + + /** + * LZMA Properties + */ + public byte[] lzmaProperties; + + @Internal + public byte[] uncompressedData; + + @Internal + public byte[] originalUncompressedData; + + /** + * ScaleForm GFx + */ + public boolean gfx = false; + + @Internal + public SWFList swfList; + + @Internal + private String file; + + @Internal + private String fileTitle; + + @Internal + private Map characters; + + @Internal + private List abcList; + + @Internal + private JPEGTablesTag jtt; + + @Internal + public Map sourceFontNamesMap = new HashMap<>(); + + public static final double unitDivisor = 20; + + private static final Logger logger = Logger.getLogger(SWF.class.getName()); + + @Internal + private Timeline timeline; + + @Internal + public DumpInfoSwfNode dumpInfo; + + @Internal + public DefineBinaryDataTag binaryData; + + @Internal + private final HashMap deobfuscated = new HashMap<>(); + + @Internal + private final IdentifiersDeobfuscation deobfuscation = new IdentifiersDeobfuscation(); + + @Internal + private Cache frameCache = Cache.getInstance(false, false, "frame"); + + @Internal + private Cache soundCache = Cache.getInstance(false, false, "sound"); + + @Internal + private final Cache as2Cache = Cache.getInstance(true, false, "as2"); + + @Internal + private final Cache as3Cache = Cache.getInstance(true, false, "as3"); + + public void updateCharacters() { + characters = null; + } + + public void clearTagSwfs() { + resetTimelines(this); + updateCharacters(); + + for (Tag tag : tags) { + if (tag instanceof DefineSpriteTag) { + DefineSpriteTag spriteTag = (DefineSpriteTag) tag; + for (Tag tag1 : spriteTag.subTags) { + tag1.setSwf(null); + } + + spriteTag.subTags.clear(); + } + + if (tag instanceof DefineBinaryDataTag) { + DefineBinaryDataTag binaryTag = (DefineBinaryDataTag) tag; + if (binaryTag.innerSwf != null) { + binaryTag.innerSwf.clearTagSwfs(); + } + } + + tag.setSwf(null); + } + + tags.clear(); + if (abcList != null) { + abcList.clear(); + } + + if (swfList != null) { + swfList.swfs.clear(); + } + + as2Cache.clear(); + as3Cache.clear(); + frameCache.clear(); + soundCache.clear(); + + timeline = null; + clearDumpInfo(dumpInfo); + dumpInfo = null; + jtt = null; + binaryData = null; + } + + private void clearDumpInfo(DumpInfo di) { + for (DumpInfo childInfo : di.getChildInfos()) { + clearDumpInfo(childInfo); + } + + di.getChildInfos().clear(); + } + + public Map getCharacters() { + if (characters == null) { + synchronized (this) { + if (characters == null) { + Map chars = new HashMap<>(); + parseCharacters(tags, chars); + characters = Collections.unmodifiableMap(chars); + } + } + } + + return characters; + } + + public CharacterTag getCharacter(int characterId) { + return getCharacters().get(characterId); + } + + public String getExportName(int characterId) { + CharacterTag characterTag = getCharacters().get(characterId); + String exportName = characterTag != null ? characterTag.getExportName() : null; + return exportName; + } + + public FontTag getFont(int fontId) { + CharacterTag characterTag = getCharacters().get(fontId); + if (characterTag instanceof FontTag) { + return (FontTag) characterTag; + } + + if (characterTag != null) { + logger.log(Level.SEVERE, "CharacterTag should be a FontTag. characterId: {0}", fontId); + } + + return null; + } + + public ImageTag getImage(int imageId) { + CharacterTag characterTag = getCharacters().get(imageId); + if (characterTag instanceof ImageTag) { + return (ImageTag) characterTag; + } + + if (characterTag != null) { + logger.log(Level.SEVERE, "CharacterTag should be an ImageTag. characterId: {0}", imageId); + } + + return null; + } + + public TextTag getText(int textId) { + CharacterTag characterTag = getCharacters().get(textId); + if (characterTag instanceof TextTag) { + return (TextTag) characterTag; + } + + if (characterTag != null) { + logger.log(Level.SEVERE, "CharacterTag should be a TextTag. characterId: {0}", textId); + } + + return null; + } + + public List getAbcList() { + if (abcList == null) { + synchronized (this) { + if (abcList == null) { + ArrayList newAbcList = new ArrayList<>(); + getAbcTags(tags, newAbcList); + abcList = newAbcList; + } + } + } + + return abcList; + } + + public boolean isAS3() { + FileAttributesTag fileAttributes = getFileAttributes(); + return (fileAttributes != null && fileAttributes.actionScript3) || (fileAttributes == null && !getAbcList().isEmpty()); + } + + public FileAttributesTag getFileAttributes() { + for (Tag t : tags) { + if (t instanceof FileAttributesTag) { + return (FileAttributesTag) t; + } + } + + return null; + } + + public int getNextCharacterId() { + int max = -1; + for (int characterId : getCharacters().keySet()) { + if (characterId > max) { + max = characterId; + } + } + + return max + 1; + } + + public synchronized JPEGTablesTag getJtt() { + if (jtt == null) { + synchronized (this) { + if (jtt == null) { + for (Tag t : tags) { + if (t instanceof JPEGTablesTag) { + jtt = (JPEGTablesTag) t; + break; + } + } + } + } + } + + return jtt; + } + + public String getDocumentClass() { + for (Tag t : tags) { + if (t instanceof SymbolClassTag) { + SymbolClassTag sc = (SymbolClassTag) t; + for (int i = 0; i < sc.tags.size(); i++) { + if (sc.tags.get(i) == 0) { + return sc.names.get(i); + } + } + } + } + + return null; + } + + public void fixCharactersOrder(boolean checkAll) { + Set addedCharacterIds = new HashSet<>(); + Set movedTags = new HashSet<>(); + for (int i = 0; i < tags.size(); i++) { + Tag tag = tags.get(i); + if (checkAll || tag.isModified()) { + Set needed = new HashSet<>(); + tag.getNeededCharacters(needed); + if (tag instanceof CharacterTag) { + CharacterTag characterTag = (CharacterTag) tag; + needed.remove(characterTag.getCharacterId()); + } + boolean moved = false; + for (Integer id : needed) { + if (!addedCharacterIds.contains(id)) { + CharacterTag neededCharacter = getCharacter(id); + if (neededCharacter == null) { + continue; + } + + if (movedTags.contains(neededCharacter)) { + logger.log(Level.SEVERE, "Fixing characters order failed, recursion detected."); + return; + } + + // move the needed character to the current position + tags.remove(neededCharacter); + tags.add(i, neededCharacter); + movedTags.add(neededCharacter); + moved = true; + } + } + + if (moved) { + i--; + continue; + } + } + if (tag instanceof CharacterTag) { + addedCharacterIds.add(((CharacterTag) tag).getCharacterId()); + } + } + } + + public void resetTimelines(Timelined timelined) { + timelined.resetTimeline(); + if (timelined instanceof SWF) { + for (Tag t : ((SWF) timelined).tags) { + if (t instanceof Timelined) { + resetTimelines((Timelined) t); + } + } + } + } + + private void parseCharacters(List list, Map characters) { + for (Tag t : list) { + if (t instanceof CharacterTag) { + int characterId = ((CharacterTag) t).getCharacterId(); + if (characters.containsKey(characterId)) { + logger.log(Level.SEVERE, "SWF already contains characterId={0}", characterId); + } + + if (characterId != 0) { + characters.put(characterId, (CharacterTag) t); + } + } + if (t instanceof DefineSpriteTag) { + parseCharacters(((DefineSpriteTag) t).getSubTags(), characters); + } + } + } + + /** + * Unresolve recursive sprites + */ + private void checkInvalidSprites() { + for (int i = 0; i < tags.size(); i++) { + Tag t = tags.get(i); + if (t instanceof DefineSpriteTag) { + if (!isSpriteValid((DefineSpriteTag) t, new ArrayList())) { + tags.set(i, new TagStub(this, t.getId(), "InvalidSprite", t.getOriginalRange(), null)); + } + } + } + } + + private boolean isSpriteValid(DefineSpriteTag sprite, List path) { + if (path.contains(sprite.spriteId)) { + return false; + } + path.add(sprite.spriteId); + for (Tag t : sprite.subTags) { + if (t instanceof DefineSpriteTag) { + if (!isSpriteValid((DefineSpriteTag) t, path)) { + return false; + } + } + } + path.remove((Integer) sprite.spriteId); + return true; + } + + @Override + public Timeline getTimeline() { + if (timeline == null) { + timeline = new Timeline(this); + } + return timeline; + } + + @Override + public void resetTimeline() { + if (timeline != null) { + timeline.reset(this); + } + } + + /** + * Gets all tags with specified id + * + * @param tagId Identificator of tag type + * @return List of tags + */ + public List getTagData(int tagId) { + List ret = new ArrayList<>(); + for (Tag tag : tags) { + if (tag.getId() == tagId) { + ret.add(tag); + } + } + return ret; + } + + /** + * Saves this SWF into new file + * + * @param os OutputStream to save SWF in + * @throws IOException + */ + public void saveTo(OutputStream os) throws IOException { + saveTo(os, compression); + } + + public String getHeaderBytes() { + return getHeaderBytes(compression, gfx); + } + + private String getHeaderBytes(SWFCompression compression, boolean gfx) { + if (compression == SWFCompression.LZMA_ABC) { + return "ABC"; + } + + String ret = ""; + if (compression == SWFCompression.LZMA) { + ret += 'Z'; + } else if (compression == SWFCompression.ZLIB) { + ret += 'C'; + } else { + if (gfx) { + ret += 'G'; + } else { + ret += 'F'; + } + } + if (gfx) { + ret += 'F'; + ret += 'X'; + } else { + ret += 'W'; + ret += 'S'; + } + return ret; + } + + /** + * Saves this SWF into new file + * + * @param os OutputStream to save SWF in + * @param compression + * @throws IOException + */ + public void saveTo(OutputStream os, SWFCompression compression) throws IOException { + try { + fixCharactersOrder(false); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + SWFOutputStream sos = new SWFOutputStream(baos, version); + sos.writeRECT(displayRect); + sos.writeUI8(0); + sos.writeUI8(frameRate); + sos.writeUI16(frameCount); + + sos.writeTags(tags); + if (hasEndTag) { + sos.writeUI16(0); + } + + sos.close(); + os.write(Utf8Helper.getBytes(getHeaderBytes(compression, gfx))); + os.write(version); + byte[] data = baos.toByteArray(); + sos = new SWFOutputStream(os, version); + sos.writeUI32(data.length + 8); + + if (compression == SWFCompression.LZMA || compression == SWFCompression.LZMA_ABC) { + long uncompressedLength = data.length; + Encoder enc = new Encoder(); + int val = lzmaProperties[0] & 0xFF; + int lc = val % 9; + int remainder = val / 9; + int lp = remainder % 5; + int pb = remainder / 5; + int dictionarySize = 0; + for (int i = 0; i < 4; i++) { + dictionarySize += ((int) (lzmaProperties[1 + i]) & 0xFF) << (i * 8); + } + if (Configuration.lzmaFastBytes.get() > 0) { + enc.SetNumFastBytes(Configuration.lzmaFastBytes.get()); + } + enc.SetDictionarySize(dictionarySize); + enc.SetLcLpPb(lc, lp, pb); + baos = new ByteArrayOutputStream(); + enc.SetEndMarkerMode(true); + enc.Code(new ByteArrayInputStream(data), baos, -1, -1, null); + data = baos.toByteArray(); + if (compression == SWFCompression.LZMA) { + byte[] udata = new byte[4]; + udata[0] = (byte) (data.length & 0xFF); + udata[1] = (byte) ((data.length >> 8) & 0xFF); + udata[2] = (byte) ((data.length >> 16) & 0xFF); + udata[3] = (byte) ((data.length >> 24) & 0xFF); + os.write(udata); + } + enc.WriteCoderProperties(os); + if (compression == SWFCompression.LZMA_ABC) { + byte[] udata = new byte[8]; + udata[0] = (byte) (uncompressedLength & 0xFF); + udata[1] = (byte) ((uncompressedLength >> 8) & 0xFF); + udata[2] = (byte) ((uncompressedLength >> 16) & 0xFF); + udata[3] = (byte) ((uncompressedLength >> 24) & 0xFF); + udata[4] = (byte) ((uncompressedLength >> 32) & 0xFF); + udata[5] = (byte) ((uncompressedLength >> 40) & 0xFF); + udata[6] = (byte) ((uncompressedLength >> 48) & 0xFF); + udata[7] = (byte) ((uncompressedLength >> 56) & 0xFF); + os.write(udata); + } + } else if (compression == SWFCompression.ZLIB) { + os = new DeflaterOutputStream(os); + } + + os.write(data); + } finally { + if (os != null) { + os.close(); + } + } + } + + public boolean isModified() { + for (Tag tag : tags) { + if (tag.isModified()) { + return true; + } + } + return false; + } + + public void clearModified() { + for (Tag tag : tags) { + if (tag.isModified()) { + tag.createOriginalData(); + tag.setModified(false); + } + } + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + saveTo(baos, SWFCompression.NONE); + byte[] swfData = baos.toByteArray(); + uncompressedData = swfData; + } catch (IOException ex) { + logger.log(Level.SEVERE, "Cannot save SWF", ex); + } + } + + /** + * Constructs an empty SWF + */ + public SWF() { + + } + + /** + * Construct SWF from stream + * + * @param is Stream to read SWF from + * @param parallelRead Use parallel threads? + * @throws IOException + * @throws java.lang.InterruptedException + */ + public SWF(InputStream is, boolean parallelRead) throws IOException, InterruptedException { + this(is, null, null, null, parallelRead, false, true); + } + + /** + * Construct SWF from stream + * + * @param is Stream to read SWF from + * @param parallelRead Use parallel threads? + * @param lazy + * @throws IOException + * @throws java.lang.InterruptedException + */ + public SWF(InputStream is, boolean parallelRead, boolean lazy) throws IOException, InterruptedException { + this(is, null, null, null, parallelRead, false, lazy); + } + + /** + * Construct SWF from stream + * + * @param is Stream to read SWF from + * @param file Path to the file + * @param fileTitle Title of the SWF + * @param parallelRead Use parallel threads? + * @throws IOException + * @throws java.lang.InterruptedException + */ + public SWF(InputStream is, String file, String fileTitle, boolean parallelRead) throws IOException, InterruptedException { + this(is, file, fileTitle, null, parallelRead, false, true); + } + + /** + * Construct SWF from stream + * + * @param is Stream to read SWF from + * @param listener + * @param parallelRead Use parallel threads? + * @throws IOException + * @throws java.lang.InterruptedException + */ + public SWF(InputStream is, ProgressListener listener, boolean parallelRead) throws IOException, InterruptedException { + this(is, null, null, listener, parallelRead, false, true); + } + + /** + * Construct SWF from stream + * + * @param is Stream to read SWF from + * @param file Path to the file + * @param fileTitle Title of the SWF + * @param listener + * @param parallelRead Use parallel threads? + * @throws IOException + * @throws java.lang.InterruptedException + */ + public SWF(InputStream is, String file, String fileTitle, ProgressListener listener, boolean parallelRead) throws IOException, InterruptedException { + this(is, file, fileTitle, listener, parallelRead, false, true); + } + + /** + * Faster constructor to check SWF only + * + * @param is + * @throws java.io.IOException + */ + public SWF(InputStream is) throws IOException { + decompress(is, new NulStream(), true); + } + + /** + * Construct SWF from stream + * + * @param is Stream to read SWF from + * @param file Path to the file + * @param fileTitle Title of the SWF + * @param listener + * @param parallelRead Use parallel threads? + * @param checkOnly Check only file validity + * @param lazy + * @throws IOException + * @throws java.lang.InterruptedException + */ + public SWF(InputStream is, String file, String fileTitle, ProgressListener listener, boolean parallelRead, boolean checkOnly, boolean lazy) throws IOException, InterruptedException { + this.file = file; + this.fileTitle = fileTitle; + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + SWFHeader header = decompress(is, baos, true); + gfx = header.gfx; + compression = header.compression; + lzmaProperties = header.lzmaProperties; + uncompressedData = baos.toByteArray(); + originalUncompressedData = uncompressedData; + + SWFInputStream sis = new SWFInputStream(this, uncompressedData); + dumpInfo = new DumpInfoSwfNode(this, "rootswf", "", null, 0, 0); + sis.dumpInfo = dumpInfo; + sis.readBytesEx(3, "signature"); // skip siganture + version = sis.readUI8("version"); + fileSize = sis.readUI32("fileSize"); + dumpInfo.lengthBytes = fileSize; + if (listener != null) { + sis.addPercentListener(listener); + } + sis.setPercentMax(fileSize); + displayRect = sis.readRECT("displayRect"); + // FIXED8 (16 bit fixed point) frameRate + sis.readUI8("tmpFirstByetOfFrameRate"); // tmpFirstByetOfFrameRate + frameRate = sis.readUI8("frameRate"); + frameCount = sis.readUI16("frameCount"); + List tags = sis.readTagList(this, 0, parallelRead, true, !checkOnly, lazy); + if (tags.size() > 0 && tags.get(tags.size() - 1).getId() == EndTag.ID) { + tags.remove(tags.size() - 1); + } else { + hasEndTag = false; + } + this.tags = tags; + if (!checkOnly) { + checkInvalidSprites(); + updateCharacters(); + assignExportNamesToSymbols(); + assignClassesToSymbols(); + SWFDecompilerPlugin.fireSwfParsed(this); + } else { + boolean hasNonUnknownTag = false; + for (Tag tag : tags) { + if (tag.getOriginalDataLength() > 0 && Tag.getRequiredTags().contains(tag.getId())) { + hasNonUnknownTag = true; + } + } + if (!hasNonUnknownTag) { + throw new IOException("Invalid SWF file. No known tag found."); + } + } + + /*preload shape tags + for (Tag tag : tags) { + if (tag instanceof ShapeTag) { + ((ShapeTag) tag).getShapes(); + } + }*/ + } + + @Override + public SWF getSwf() { + return this; + } + + public SWF getRootSwf() { + SWF result = this; + while (result.binaryData != null) { + result = result.binaryData.getSwf(); + } + + return result; + } + + public String getFile() { + return file; + } + + /** + * Get title of the file + * + * @return file title + */ + public String getFileTitle() { + if (fileTitle != null) { + return fileTitle; + } + return file; + } + + public String getShortFileName() { + String title = getFileTitle(); + if (title == null) { + return ""; + } + return new File(title).getName(); + } + + public void setFile(String file) { + this.file = file; + fileTitle = null; + } + + public Date getFileModificationDate() { + try { + if (swfList != null && swfList.sourceInfo != null) { + String fileName = swfList.sourceInfo.getFile(); + if (fileName != null) { + long lastModified = new File(fileName).lastModified(); + if (lastModified > 0) { + return new Date(lastModified); + } + } + } + } catch (SecurityException sex) { + } + + return new Date(); + } + + private static void getAbcTags(List list, List actionScripts) { + for (Tag t : list) { + if (t instanceof DefineSpriteTag) { + getAbcTags(((DefineSpriteTag) t).getSubTags(), actionScripts); + } + if (t instanceof ABCContainerTag) { + actionScripts.add((ABCContainerTag) t); + } + } + } + + public void assignExportNamesToSymbols() { + HashMap exportNames = new HashMap<>(); + for (Tag t : tags) { + if (t instanceof ExportAssetsTag) { + ExportAssetsTag eat = (ExportAssetsTag) t; + for (int i = 0; i < eat.tags.size(); i++) { + Integer tagId = eat.tags.get(i); + String name = eat.names.get(i); + if ((!exportNames.containsKey(tagId)) && (!exportNames.containsValue(name))) { + exportNames.put(tagId, name); + } + } + } + } + for (Tag t : tags) { + if (t instanceof CharacterTag) { + CharacterTag ct = (CharacterTag) t; + if (exportNames.containsKey(ct.getCharacterId())) { + ct.setExportName(exportNames.get(ct.getCharacterId())); + } + } + } + } + + public void assignClassesToSymbols() { + HashMap classes = new HashMap<>(); + for (Tag t : tags) { + if (t instanceof SymbolClassTag) { + SymbolClassTag sct = (SymbolClassTag) t; + for (int i = 0; i < sct.tags.size(); i++) { + if ((!classes.containsKey(sct.tags.get(i))) && (!classes.containsValue(sct.names.get(i)))) { + classes.put(sct.tags.get(i), sct.names.get(i)); + } + } + } + } + for (Tag t : tags) { + if (t instanceof CharacterTag) { + CharacterTag ct = (CharacterTag) t; + if (classes.containsKey(ct.getCharacterId())) { + ct.setClassName(classes.get(ct.getCharacterId())); + } + } + } + } + + /** + * Compress SWF file + * + * @param fis Input stream + * @param fos Output stream + * @return True on success + */ + public static boolean fws2cws(InputStream fis, OutputStream fos) { + try { + byte[] swfHead = new byte[8]; + fis.read(swfHead); + + if (swfHead[0] != 'F') { + fis.close(); + return false; + } + swfHead[0] = 'C'; + fos.write(swfHead); + fos = new DeflaterOutputStream(fos); + int i; + while ((i = fis.read()) != -1) { + fos.write(i); + } + + fis.close(); + fos.close(); + } catch (IOException ex) { + return false; + } + return true; + } + + public static boolean decompress(InputStream fis, OutputStream fos) { + try { + decompress(fis, fos, false); + return true; + } catch (IOException ex) { + return false; + } + } + + private static void decodeLZMAStream(InputStream is, OutputStream os, byte[] lzmaProperties, long fileSize) throws IOException { + Decoder decoder = new Decoder(); + if (!decoder.SetDecoderProperties(lzmaProperties)) { + throw new IOException("LZMA:Incorrect stream properties"); + } + if (!decoder.Code(is, os, fileSize - 8)) { + throw new IOException("LZMA:Error in data stream"); + } + } + + private static SWFHeader decompress(InputStream is, OutputStream os, boolean allowUncompressed) throws IOException { + byte[] hdr = new byte[8]; + + // SWFheader: signature, version and fileSize + if (is.read(hdr) != 8) { + throw new SwfOpenException("SWF header is too short"); + } + + String signature = new String(hdr, 0, 3, Utf8Helper.charset); + if (!Arrays.asList( + "FWS", // Uncompressed Flash + "CWS", // ZLib compressed Flash + "ZWS", // LZMA compressed Flash + "GFX", // Uncompressed ScaleForm GFx + "CFX", // Compressed ScaleForm GFx + "ABC" // Non-standard LZMA compressed Flash + ).contains(signature)) { + throw new SwfOpenException("Invalid SWF file, wrong signature."); + } + + int version = hdr[3]; + SWFInputStream sis = new SWFInputStream(null, Arrays.copyOfRange(hdr, 4, 8), 4, 4); + long fileSize = sis.readUI32("fileSize"); + SWFHeader header = new SWFHeader(); + header.version = version; + header.fileSize = fileSize; + + if (hdr[1] == 'F' && hdr[2] == 'X') { + header.gfx = true; + } + + try (SWFOutputStream sos = new SWFOutputStream(os, version)) { + sos.write(Utf8Helper.getBytes(header.gfx ? "GFX" : "FWS")); + sos.writeUI8(version); + sos.writeUI32(fileSize); + + switch (hdr[0]) { + case 'C': { // CWS, CFX + Helper.copyStream(new InflaterInputStream(is), os, fileSize - 8); + header.compression = SWFCompression.ZLIB; + break; + } + case 'Z': { // ZWS + byte[] lzmaprop = new byte[9]; + is.read(lzmaprop); + sis = new SWFInputStream(null, lzmaprop); + sis.readUI32("LZMAsize"); // compressed LZMA data size = compressed SWF - 17 byte, + // where 17 = 8 byte header + this 4 byte + 5 bytes decoder properties + int propertiesSize = 5; + byte[] lzmaProperties = sis.readBytes(propertiesSize, "lzmaproperties"); + if (lzmaProperties.length != propertiesSize) { + throw new IOException("LZMA:input .lzma file is too short"); + } + + decodeLZMAStream(is, os, lzmaProperties, fileSize); + + header.compression = SWFCompression.LZMA; + header.lzmaProperties = lzmaProperties; + break; + } + case 'A': { // ABC + byte[] lzmaProperties = new byte[5]; + is.read(lzmaProperties); + byte[] uncompressedLength = new byte[8]; + is.read(uncompressedLength); + + decodeLZMAStream(is, os, lzmaProperties, fileSize); + + header.compression = SWFCompression.LZMA_ABC; + header.lzmaProperties = lzmaProperties; + break; + } + default: { // FWS, GFX + if (allowUncompressed) { + Helper.copyStream(is, os, fileSize - 8); + } else { + throw new IOException("SWF is not compressed"); + } + } + } + + return header; + } + } + + public static boolean renameInvalidIdentifiers(RenameType renameType, InputStream fis, OutputStream fos) { + try { + SWF swf = new SWF(fis, Configuration.parallelSpeedUp.get()); + int cnt = swf.deobfuscateIdentifiers(renameType); + swf.assignClassesToSymbols(); + System.out.println(cnt + " identifiers renamed."); + swf.saveTo(fos); + } catch (InterruptedException ex) { + return false; + } catch (IOException ex) { + return false; + } + return true; + } + + public boolean exportAS3Class(String className, String outdir, ScriptExportSettings exportSettings, boolean parallel, EventListener evl) throws Exception { + boolean exported = false; + + List abcList = getAbcList(); + List allAbcList = new ArrayList<>(); + for (int i = 0; i < abcList.size(); i++) { + allAbcList.add(abcList.get(i).getABC()); + } + + for (int i = 0; i < abcList.size(); i++) { + ABC abc = abcList.get(i).getABC(); + List scrs = abc.findScriptPacksByPath(className, allAbcList); + for (int j = 0; j < scrs.size(); j++) { + ScriptPack scr = scrs.get(j); + String cnt = ""; + if (scrs.size() > 1) { + cnt = "script " + (j + 1) + "/" + scrs.size() + " "; + } + String eventData = cnt + scr.getPath() + " ..."; + evl.handleExportingEvent("tag", i + 1, abcList.size(), eventData); + scr.export(outdir, exportSettings, parallel); + evl.handleExportedEvent("tag", i + 1, abcList.size(), eventData); + exported = true; + } + } + return exported; + } + + private List uniqueAS3Packs(List packs) { + List ret = new ArrayList<>(); + Set classPaths = new HashSet<>(); + for (ScriptPack item : packs) { + ClassPath key = item.getClassPath(); + if (classPaths.contains(key)) { + logger.log(Level.SEVERE, "Duplicate pack path found (" + key + ")!"); + } else { + classPaths.add(key); + ret.add(item); + } + } + return ret; + } + + public List getAS3Packs() { + List packs = new ArrayList<>(); + + List abcList = getAbcList(); + List allAbcList = new ArrayList<>(); + for (int i = 0; i < abcList.size(); i++) { + allAbcList.add(abcList.get(i).getABC()); + } + + for (ABCContainerTag abcTag : abcList) { + packs.addAll(abcTag.getABC().getScriptPacks(null, allAbcList)); + } + return uniqueAS3Packs(packs); + } + + @Override + public RECT getRect() { + return displayRect; + } + + @Override + public RECT getRect(Set added) { + return displayRect; + } + + public EventListener getExportEventListener() { + EventListener evl = new EventListener() { + @Override + public void handleExportingEvent(String type, int index, int count, Object data) { + for (EventListener listener : listeners) { + listener.handleExportingEvent(type, index, count, data); + } + } + + @Override + public void handleExportedEvent(String type, int index, int count, Object data) { + for (EventListener listener : listeners) { + listener.handleExportedEvent(type, index, count, data); + } + } + + @Override + public void handleEvent(String event, Object data) { + informListeners(event, data); + } + }; + + return evl; + } + + public List exportActionScript(AbortRetryIgnoreHandler handler, String outdir, ScriptExportSettings exportSettings, boolean parallel, EventListener evl) throws IOException { + List ret = new ArrayList<>(); + + if (isAS3()) { + ret.addAll(new AS3ScriptExporter().exportActionScript3(this, handler, outdir, exportSettings, parallel, evl)); + } else { + ret.addAll(new AS2ScriptExporter().exportAS2ScriptsTimeout(handler, outdir, getASMs(true), exportSettings, evl)); + } + return ret; + } + + public Map getASMs(boolean exportFileNames) { + return getASMs(exportFileNames, new ArrayList(), true); + } + + public Map getASMs(boolean exportFileNames, List nodesToExport, boolean exportAll) { + Map asmsToExport = new HashMap<>(); + for (TreeItem treeItem : getFirstLevelASMNodes(null)) { + getASMs(exportFileNames, treeItem, nodesToExport, exportAll, asmsToExport, File.separator + getASMPath(exportFileNames, treeItem)); + } + + return asmsToExport; + } + + private void getASMs(boolean exportFileNames, TreeItem treeItem, List nodesToExport, boolean exportAll, Map asmsToExport, String path) { + boolean exportNode = nodesToExport.contains(treeItem); + TreeItem realItem = treeItem instanceof TagScript ? ((TagScript) treeItem).getTag() : treeItem; + if (realItem instanceof ASMSource && (exportAll || exportNode)) { + String npath = path; + int ppos = 1; + while (asmsToExport.containsKey(npath)) { + ppos++; + npath = path + (exportFileNames ? "[" + ppos + "]" : "_" + ppos); + } + asmsToExport.put(npath, (ASMSource) realItem); + } + + if (treeItem instanceof TagScript) { + TagScript tagScript = (TagScript) treeItem; + for (TreeItem subItem : tagScript.getFrames()) { + getASMs(exportFileNames, subItem, nodesToExport, exportAll, asmsToExport, path + File.separator + getASMPath(exportFileNames, subItem)); + } + } else if (treeItem instanceof FrameScript) { + FrameScript frameScript = (FrameScript) treeItem; + Frame parentFrame = frameScript.getFrame(); + for (TreeItem subItem : parentFrame.actionContainers) { + getASMs(exportFileNames, getASMWrapToTagScript(subItem), nodesToExport, exportAll || exportNode, asmsToExport, path + File.separator + getASMPath(exportFileNames, subItem)); + } + for (TreeItem subItem : parentFrame.actions) { + getASMs(exportFileNames, getASMWrapToTagScript(subItem), nodesToExport, exportAll || exportNode, asmsToExport, path + File.separator + getASMPath(exportFileNames, subItem)); + } + } else if (treeItem instanceof AS2Package) { + AS2Package as2Package = (AS2Package) treeItem; + for (TreeItem subItem : as2Package.subPackages.values()) { + getASMs(exportFileNames, subItem, nodesToExport, exportAll, asmsToExport, path + File.separator + getASMPath(exportFileNames, subItem)); + } + for (TreeItem subItem : as2Package.scripts.values()) { + getASMs(exportFileNames, subItem, nodesToExport, exportAll, asmsToExport, path + File.separator + getASMPath(exportFileNames, subItem)); + } + } + } + + private String getASMPath(boolean exportFileName, TreeItem treeItem) { + if (!exportFileName) { + return treeItem.toString(); + } + + String result; + if (treeItem instanceof Exportable) { + result = ((Exportable) treeItem).getExportFileName(); + } else { + result = treeItem.toString(); + } + + return Helper.makeFileName(result); + } + + private TreeItem getASMWrapToTagScript(TreeItem treeItem) { + if (treeItem instanceof Tag) { + Tag resultTag = (Tag) treeItem; + List subNodes = new ArrayList<>(); + if (treeItem instanceof ASMSourceContainer) { + for (ASMSource item : ((ASMSourceContainer) treeItem).getSubItems()) { + subNodes.add(item); + } + } + + TagScript tagScript = new TagScript(treeItem.getSwf(), resultTag, subNodes); + return tagScript; + } + + return treeItem; + } + + public List getFirstLevelASMNodes(Map tagScriptCache) { + Timeline timeline = getTimeline(); + List subNodes = new ArrayList<>(); + List subFrames = new ArrayList<>(); + subNodes.addAll(timeline.getAS2RootPackage().subPackages.values()); + subNodes.addAll(timeline.getAS2RootPackage().scripts.values()); + + for (Tag tag : timeline.otherTags) { + boolean hasInnerFrames = false; + List tagSubNodes = new ArrayList<>(); + if (tag instanceof Timelined) { + Timeline timeline2 = ((Timelined) tag).getTimeline(); + for (Frame frame : timeline2.getFrames()) { + if (!frame.actions.isEmpty() || !frame.actionContainers.isEmpty()) { + FrameScript frameScript = new FrameScript(this, frame); + tagSubNodes.add(frameScript); + hasInnerFrames = true; + } + } + } + + if (tag instanceof ASMSourceContainer) { + for (ASMSource asm : ((ASMSourceContainer) tag).getSubItems()) { + tagSubNodes.add(asm); + } + } + + if (!tagSubNodes.isEmpty()) { + TagScript ts = new TagScript(this, tag, tagSubNodes); + if (tagScriptCache != null) { + tagScriptCache.put(tag, ts); + } + if (hasInnerFrames) { + subFrames.add(ts); + } else { + subNodes.add(ts); + } + } + } + + subNodes.addAll(subFrames); + for (Frame frame : timeline.getFrames()) { + if (!frame.actions.isEmpty() || !frame.actionContainers.isEmpty()) { + FrameScript frameScript = new FrameScript(this, frame); + subNodes.add(frameScript); + } + } + + return subNodes; + } + + private final HashSet listeners = new HashSet<>(); + + public final void addEventListener(EventListener listener) { + listeners.add(listener); + for (Tag t : tags) { + if (t instanceof ABCContainerTag) { + (((ABCContainerTag) t).getABC()).addEventListener(listener); + } + } + } + + public final void removeEventListener(EventListener listener) { + listeners.remove(listener); + for (Tag t : tags) { + if (t instanceof ABCContainerTag) { + (((ABCContainerTag) t).getABC()).removeEventListener(listener); + } + } + } + + protected void informListeners(String event, Object data) { + for (EventListener listener : listeners) { + listener.handleEvent(event, data); + } + } + + public static void populateVideoFrames(int streamId, List tags, HashMap output) { + for (Tag t : tags) { + if (t instanceof VideoFrameTag) { + output.put(((VideoFrameTag) t).frameNum, (VideoFrameTag) t); + } + if (t instanceof DefineSpriteTag) { + populateVideoFrames(streamId, ((DefineSpriteTag) t).getSubTags(), output); + } + } + } + + private static void writeLE(OutputStream os, long val, int size) throws IOException { + for (int i = 0; i < size; i++) { + os.write((int) (val & 0xff)); + val >>= 8; + } + } + + public static void createWavFromPcmData(OutputStream fos, int soundRateHz, boolean soundSize, boolean soundType, byte[] data) throws IOException { + ByteArrayOutputStream subChunk1Data = new ByteArrayOutputStream(); + int audioFormat = 1; // PCM + writeLE(subChunk1Data, audioFormat, 2); + int numChannels = soundType ? 2 : 1; + writeLE(subChunk1Data, numChannels, 2); + int[] rateMap = {5512, 11025, 22050, 44100}; + int sampleRate = soundRateHz; // rateMap[soundRate]; + writeLE(subChunk1Data, sampleRate, 4); + int bitsPerSample = soundSize ? 16 : 8; + int byteRate = sampleRate * numChannels * bitsPerSample / 8; + writeLE(subChunk1Data, byteRate, 4); + int blockAlign = numChannels * bitsPerSample / 8; + writeLE(subChunk1Data, blockAlign, 2); + writeLE(subChunk1Data, bitsPerSample, 2); + + ByteArrayOutputStream chunks = new ByteArrayOutputStream(); + chunks.write(Utf8Helper.getBytes("fmt ")); + byte[] subChunk1DataBytes = subChunk1Data.toByteArray(); + writeLE(chunks, subChunk1DataBytes.length, 4); + chunks.write(subChunk1DataBytes); + + chunks.write(Utf8Helper.getBytes("data")); + writeLE(chunks, data.length, 4); + chunks.write(data); + + fos.write(Utf8Helper.getBytes("RIFF")); + byte[] chunkBytes = chunks.toByteArray(); + writeLE(fos, 4 + chunkBytes.length, 4); + fos.write(Utf8Helper.getBytes("WAVE")); + fos.write(chunkBytes); + } + + public static String getTypePrefix(CharacterTag c) { + if (c instanceof ShapeTag) { + return "shape"; + } + if (c instanceof MorphShapeTag) { + return "morphshape"; + } + if (c instanceof DefineSpriteTag) { + return "sprite"; + } + if (c instanceof TextTag) { + return "text"; + } + if (c instanceof ButtonTag) { + return "button"; + } + if (c instanceof FontTag) { + return "font"; + } + if (c instanceof ImageTag) { + return "image"; + } + return "character"; + } + + public static void writeLibrary(SWF fswf, Set library, OutputStream fos) throws IOException { + for (int c : library) { + CharacterTag ch = fswf.getCharacter(c); + if (ch instanceof FontTag) { + fos.write(Utf8Helper.getBytes("function " + getTypePrefix(ch) + c + "(ctx,ch,textColor){\r\n")); + fos.write(Utf8Helper.getBytes(((FontTag) ch).toHtmlCanvas(1))); + fos.write(Utf8Helper.getBytes("}\r\n\r\n")); + } else { + if (ch instanceof ImageTag) { + ImageTag image = (ImageTag) ch; + ImageFormat format = image.getImageFormat(); + InputStream imageStream = image.getImageData(); + byte[] imageData; + if (imageStream != null) { + imageData = Helper.readStream(image.getImageData()); + } else { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ImageHelper.write(image.getImage().getBufferedImage(), format, baos); + imageData = baos.toByteArray(); + } + String base64ImgData = Helper.byteArrayToBase64String(imageData); + fos.write(Utf8Helper.getBytes("var imageObj" + c + " = document.createElement(\"img\");\r\nimageObj" + c + ".src=\"data:image/" + format + ";base64," + base64ImgData + "\";\r\n")); + } + fos.write(Utf8Helper.getBytes("function " + getTypePrefix(ch) + c + "(ctx,ctrans,frame,ratio,time){\r\n")); + if (ch instanceof DrawableTag) { + fos.write(Utf8Helper.getBytes(((DrawableTag) ch).toHtmlCanvas(1))); + } + fos.write(Utf8Helper.getBytes("}\r\n\r\n")); + } + } + } + + private static void getVariables(ConstantPool constantPool, BaseLocalData localData, TranslateStack stack, List output, ActionGraphSource code, int ip, List> variables, List functions, HashMap strings, List visited, HashMap usageTypes, String path) throws InterruptedException { + boolean debugMode = false; + while ((ip > -1) && ip < code.size()) { + if (visited.contains(ip)) { + break; + } + GraphSourceItem ins = code.get(ip); + + if (debugMode) { + System.err.println("Visit " + ip + ": ofs" + Helper.formatAddress(((Action) ins).getAddress()) + ":" + ((Action) ins).getASMSource(new ActionList(), new HashSet(), ScriptExportMode.PCODE) + " stack:" + Helper.stackToString(stack, LocalData.create(new ConstantPool()))); + } + if (ins.isExit()) { + break; + } + if (ins.isIgnored()) { + ip++; + continue; + } + + String usageType = "name"; + GraphTargetItem name = null; + if ((ins instanceof ActionGetVariable) + || (ins instanceof ActionGetMember) + || (ins instanceof ActionDefineLocal2) + || (ins instanceof ActionNewMethod) + || (ins instanceof ActionNewObject) + || (ins instanceof ActionCallMethod) + || (ins instanceof ActionCallFunction)) { + if (stack.isEmpty()) { + break; + } + name = stack.peek(); + } + + if ((ins instanceof ActionGetVariable) || (ins instanceof ActionDefineLocal2)) { + usageType = "variable"; + } + if (ins instanceof ActionGetMember) { + usageType = "member"; + } + if ((ins instanceof ActionNewMethod) || (ins instanceof ActionNewObject)) { + usageType = "class"; + } + if (ins instanceof ActionCallMethod) { + usageType = "function"; // can there be method? + } + if (ins instanceof ActionCallFunction) { + usageType = "function"; + } + + if ((ins instanceof ActionDefineFunction) || (ins instanceof ActionDefineFunction2)) { + functions.add(ins); + } + + if (ins instanceof GraphSourceItemContainer) { + GraphSourceItemContainer cnt = (GraphSourceItemContainer) ins; + List cntSizes = cnt.getContainerSizes(); + long addr = code.pos2adr(ip + 1); + ip = code.adr2pos(addr); + String cntName = cnt.getName(); + for (Long size : cntSizes) { + if (size == 0) { + continue; + } + ip = code.adr2pos(addr); + addr += size; + int nextip = code.adr2pos(addr); + getVariables(variables, functions, strings, usageTypes, new ActionGraphSource(code.getActions().subList(ip, nextip), code.version, new HashMap(), new HashMap(), new HashMap()), 0, path + (cntName == null ? "" : "/" + cntName)); + ip = nextip; + } + List> r = new ArrayList<>(); + r.add(new ArrayList()); + r.add(new ArrayList()); + r.add(new ArrayList()); + try { + ((GraphSourceItemContainer) ins).translateContainer(r, stack, output, new HashMap(), new HashMap(), new HashMap()); + } catch (EmptyStackException ex) { + } + + continue; + } + + if ((ins instanceof ActionSetVariable) || (ins instanceof ActionSetMember) || (ins instanceof ActionDefineLocal)) { + if (stack.size() < 2) { + break; + } + name = stack.get(stack.size() - 2); + } + + if ((ins instanceof ActionSetVariable) || (ins instanceof ActionDefineLocal)) { + usageType = "variable"; + } + + if (ins instanceof ActionSetMember) { + usageType = "member"; + } + + if (name instanceof DirectValueActionItem) { + variables.add(new MyEntry<>((DirectValueActionItem) name, constantPool)); + usageTypes.put((DirectValueActionItem) name, usageType); + } + + // for..in return + if (((ins instanceof ActionEquals) || (ins instanceof ActionEquals2)) && (stack.size() == 1) && (stack.peek() instanceof DirectValueActionItem)) { + stack.push(new DirectValueActionItem(null, 0, new Null(), new ArrayList())); + } + + if (ins instanceof ActionConstantPool) { + constantPool = new ConstantPool(((ActionConstantPool) ins).constantPool); + } + int staticOperation = Graph.SOP_USE_STATIC; //(Boolean) Configuration.getConfig("autoDeobfuscate", true) ? Graph.SOP_SKIP_STATIC : Graph.SOP_USE_STATIC; + + try { + ins.translate(localData, stack, output, staticOperation, path); + } catch (EmptyStackException ex) { + // probably obfucated code, never executed branch + break; + } + if (ins.isExit()) { + break; + } + + if (ins instanceof ActionPush) { + if (!stack.isEmpty()) { + GraphTargetItem top = stack.peek(); + if (top instanceof DirectValueActionItem) { + DirectValueActionItem dvt = (DirectValueActionItem) top; + if ((dvt.value instanceof String) || (dvt.value instanceof ConstantIndex)) { + if (constantPool == null) { + constantPool = new ConstantPool(dvt.constants); + } + strings.put(dvt, constantPool); + } + } + } + } + + if (ins.isBranch() || ins.isJump()) { + if (ins instanceof ActionIf) { + if (stack.isEmpty()) { + break; + } + stack.pop(); + } + visited.add(ip); + List branches = ins.getBranches(code); + for (int b : branches) { + TranslateStack brStack = (TranslateStack) stack.clone(); + if (b >= 0) { + getVariables(constantPool, localData, brStack, output, code, b, variables, functions, strings, visited, usageTypes, path); + } else { + if (debugMode) { + System.out.println("Negative branch:" + b); + } + } + } + // } + break; + } + ip++; + }; + } + + private static void getVariables(List> variables, List functions, HashMap strings, HashMap usageTypes, ActionGraphSource code, int addr, String path) throws InterruptedException { + ActionLocalData localData = new ActionLocalData(); + getVariables(null, localData, new TranslateStack(), new ArrayList(), code, code.adr2pos(addr), variables, functions, strings, new ArrayList(), usageTypes, path); + } + + private List> getVariables(List> variables, HashMap actionsMap, List functions, HashMap strings, HashMap usageTypes, ASMSource src, String path) throws InterruptedException { + List> ret = new ArrayList<>(); + ActionList actions = src.getActions(); + actionsMap.put(src, actions); + getVariables(variables, functions, strings, usageTypes, new ActionGraphSource(actions, version, new HashMap(), new HashMap(), new HashMap()), 0, path); + return ret; + } + + private void getVariables(List tags, String path, List> variables, HashMap actionsMap, List functions, HashMap strings, HashMap usageTypes) throws InterruptedException { + List processed = new ArrayList<>(); + for (Tag t : tags) { + String subPath = path + "/" + t.toString(); + if (t instanceof ASMSource) { + addVariable((ASMSource) t, subPath, processed, variables, actionsMap, functions, strings, usageTypes); + } + if (t instanceof ASMSourceContainer) { + List processed2 = new ArrayList<>(); + for (ASMSource asm : ((ASMSourceContainer) t).getSubItems()) { + addVariable(asm, subPath + "/" + asm.toString(), processed2, variables, actionsMap, functions, strings, usageTypes); + } + } + if (t instanceof DefineSpriteTag) { + getVariables(((DefineSpriteTag) t).getSubTags(), path + "/" + t.toString(), variables, actionsMap, functions, strings, usageTypes); + } + } + } + + private void addVariable(ASMSource asm, String path, List processed, List> variables, HashMap actionsMap, List functions, HashMap strings, HashMap usageTypes) throws InterruptedException { + int pos = 1; + String infPath2 = path; + while (processed.contains(infPath2)) { + pos++; + infPath2 = path + "[" + pos + "]"; + } + processed.add(infPath2); + informListeners("getVariables", infPath2); + getVariables(variables, actionsMap, functions, strings, usageTypes, asm, path); + } + + public void fixAS3Code() { + for (ABCContainerTag abcTag : getAbcList()) { + ABC abc = abcTag.getABC(); + for (MethodBody body : abc.bodies) { + AVM2Code code = body.getCode(); + } + + ((Tag) abcTag).setModified(true); + } + } + + public int deobfuscateAS3Identifiers(RenameType renameType) { + for (Tag tag : tags) { + if (tag instanceof ABCContainerTag) { + ((ABCContainerTag) tag).getABC().deobfuscateIdentifiers(deobfuscated, renameType, true); + tag.setModified(true); + } + } + for (Tag tag : tags) { + if (tag instanceof ABCContainerTag) { + ((ABCContainerTag) tag).getABC().deobfuscateIdentifiers(deobfuscated, renameType, false); + tag.setModified(true); + } + } + for (Tag tag : tags) { + if (tag instanceof SymbolClassTag) { + SymbolClassTag sc = (SymbolClassTag) tag; + for (int i = 0; i < sc.names.size(); i++) { + String newname = deobfuscation.deobfuscateNameWithPackage(true, sc.names.get(i), deobfuscated, renameType, deobfuscated); + if (newname != null) { + sc.names.set(i, newname); + } + } + sc.setModified(true); + } + } + deobfuscation.deobfuscateInstanceNames(true, deobfuscated, renameType, tags, new HashMap()); + return deobfuscated.size(); + } + + public int deobfuscateIdentifiers(RenameType renameType) throws InterruptedException { + FileAttributesTag fileAttributes = getFileAttributes(); + if (fileAttributes == null) { + int cnt = 0; + cnt += deobfuscateAS2Identifiers(renameType); + cnt += deobfuscateAS3Identifiers(renameType); + return cnt; + } else { + if (fileAttributes.actionScript3) { + return deobfuscateAS3Identifiers(renameType); + } else { + return deobfuscateAS2Identifiers(renameType); + } + } + } + + public void renameAS2Identifier(String identifier, String newname) throws InterruptedException { + Map selected = new HashMap<>(); + selected.put(identifier, newname); + renameAS2Identifiers(null, selected); + } + + private int deobfuscateAS2Identifiers(RenameType renameType) throws InterruptedException { + return renameAS2Identifiers(renameType, null); + } + + private int renameAS2Identifiers(RenameType renameType, Map selected) throws InterruptedException { + HashMap actionsMap = new HashMap<>(); + List allFunctions = new ArrayList<>(); + List> allVariableNames = new ArrayList<>(); + HashMap allStrings = new HashMap<>(); + HashMap usageTypes = new HashMap<>(); + + int ret = 0; + getVariables(tags, "", allVariableNames, actionsMap, allFunctions, allStrings, usageTypes); + informListeners("rename", ""); + int fc = 0; + for (MyEntry it : allVariableNames) { + String name = it.getKey().toStringNoH(it.getValue()); + deobfuscation.allVariableNamesStr.add(name); + } + + informListeners("rename", "classes"); + int classCount = 0; + for (Tag t : tags) { + if (t instanceof DoInitActionTag) { + classCount++; + } + } + int cnt = 0; + for (Tag t : tags) { + if (t instanceof DoInitActionTag) { + cnt++; + informListeners("rename", "class " + cnt + "/" + classCount); + DoInitActionTag dia = (DoInitActionTag) t; + String exportName = getExportName(dia.spriteId); + exportName = exportName != null ? exportName : "_unk_"; + final String pkgPrefix = "__Packages."; + String[] classNameParts = null; + if (exportName.startsWith(pkgPrefix)) { + String className = exportName.substring(pkgPrefix.length()); + if (className.contains(".")) { + classNameParts = className.split("\\."); + } else { + classNameParts = new String[]{className}; + } + } + int staticOperation = Graph.SOP_USE_STATIC; //(Boolean) Configuration.getConfig("autoDeobfuscate", true) ? Graph.SOP_SKIP_STATIC : Graph.SOP_USE_STATIC; + List dec; + try { + dec = Action.actionsToTree(dia.getActions(), version, staticOperation, ""/*FIXME*/); + } catch (EmptyStackException ex) { + continue; + } + GraphTargetItem name = null; + for (GraphTargetItem it : dec) { + if (it instanceof ClassActionItem) { + ClassActionItem cti = (ClassActionItem) it; + List methods = new ArrayList<>(); + methods.addAll(cti.functions); + methods.addAll(cti.staticFunctions); + + for (GraphTargetItem gti : methods) { + if (gti instanceof FunctionActionItem) { + FunctionActionItem fun = (FunctionActionItem) gti; + if (fun.calculatedFunctionName instanceof DirectValueActionItem) { + DirectValueActionItem dvf = (DirectValueActionItem) fun.calculatedFunctionName; + String fname = dvf.toStringNoH(null); + String changed = deobfuscation.deobfuscateName(false, fname, false, "method", deobfuscated, renameType, selected); + if (changed != null) { + deobfuscated.put(fname, changed); + } + } + } + } + + List vars = new ArrayList<>(); + for (MyEntry item : cti.vars) { + vars.add(item.getKey()); + } + for (MyEntry item : cti.staticVars) { + vars.add(item.getKey()); + } + for (GraphTargetItem gti : vars) { + if (gti instanceof DirectValueActionItem) { + DirectValueActionItem dvf = (DirectValueActionItem) gti; + String vname = dvf.toStringNoH(null); + String changed = deobfuscation.deobfuscateName(false, vname, false, "attribute", deobfuscated, renameType, selected); + if (changed != null) { + deobfuscated.put(vname, changed); + } + } + } + + name = cti.className; + break; + } + if (it instanceof InterfaceActionItem) { + InterfaceActionItem ift = (InterfaceActionItem) it; + name = ift.name; + } + } + + if (name != null) { + int pos = 0; + while (name instanceof GetMemberActionItem) { + GetMemberActionItem mem = (GetMemberActionItem) name; + GraphTargetItem memberName = mem.memberName; + if (memberName instanceof DirectValueActionItem) { + DirectValueActionItem dvt = (DirectValueActionItem) memberName; + String nameStr = dvt.toStringNoH(null); + if (classNameParts != null) { + if (classNameParts.length - 1 - pos < 0) { + break; + } + } + String changedNameStr = nameStr; + if (classNameParts != null) { + changedNameStr = classNameParts[classNameParts.length - 1 - pos]; + } + String changedNameStr2 = deobfuscation.deobfuscateName(false, changedNameStr, pos == 0, pos == 0 ? "class" : "package", deobfuscated, renameType, selected); + if (changedNameStr2 != null) { + changedNameStr = changedNameStr2; + } + ret++; + deobfuscated.put(nameStr, changedNameStr); + pos++; + } + name = mem.object; + } + if (name instanceof GetVariableActionItem) { + GetVariableActionItem var = (GetVariableActionItem) name; + if (var.name instanceof DirectValueActionItem) { + DirectValueActionItem dvt = (DirectValueActionItem) var.name; + String nameStr = dvt.toStringNoH(null); + if (classNameParts != null) { + if (classNameParts.length - 1 - pos < 0) { + break; + } + } + String changedNameStr = nameStr; + if (classNameParts != null) { + changedNameStr = classNameParts[classNameParts.length - 1 - pos]; + } + String changedNameStr2 = deobfuscation.deobfuscateName(false, changedNameStr, pos == 0, pos == 0 ? "class" : "package", deobfuscated, renameType, selected); + if (changedNameStr2 != null) { + changedNameStr = changedNameStr2; + } + ret++; + deobfuscated.put(nameStr, changedNameStr); + pos++; + } + } + } + t.setModified(true); + } + } + + for (GraphSourceItem fun : allFunctions) { + fc++; + informListeners("rename", "function " + fc + "/" + allFunctions.size()); + if (fun instanceof ActionDefineFunction) { + ActionDefineFunction f = (ActionDefineFunction) fun; + if (f.functionName.isEmpty()) { // anonymous function, leave as is + continue; + } + String changed = deobfuscation.deobfuscateName(false, f.functionName, false, "function", deobfuscated, renameType, selected); + if (changed != null) { + f.replacedFunctionName = changed; + ret++; + } + } + if (fun instanceof ActionDefineFunction2) { + ActionDefineFunction2 f = (ActionDefineFunction2) fun; + if (f.functionName.isEmpty()) { // anonymous function, leave as is + continue; + } + String changed = deobfuscation.deobfuscateName(false, f.functionName, false, "function", deobfuscated, renameType, selected); + if (changed != null) { + f.replacedFunctionName = changed; + ret++; + } + } + } + + HashSet stringsNoVarH = new HashSet<>(); + List allVariableNamesDv = new ArrayList<>(); + for (MyEntry it : allVariableNames) { + allVariableNamesDv.add(it.getKey()); + } + for (DirectValueActionItem ti : allStrings.keySet()) { + if (!allVariableNamesDv.contains(ti)) { + stringsNoVarH.add(System.identityHashCode(allStrings.get(ti)) + "_" + ti.toStringNoH(allStrings.get(ti))); + } + } + + int vc = 0; + for (MyEntry it : allVariableNames) { + vc++; + String name = it.getKey().toStringNoH(it.getValue()); + String changed = deobfuscation.deobfuscateName(false, name, false, usageTypes.get(it.getKey()), deobfuscated, renameType, selected); + if (changed != null) { + boolean addNew = false; + String h = System.identityHashCode(it.getKey()) + "_" + name; + if (stringsNoVarH.contains(h)) { + addNew = true; + } + ActionPush pu = (ActionPush) it.getKey().src; + if (pu.replacement == null) { + pu.replacement = new ArrayList<>(); + pu.replacement.addAll(pu.values); + } + if (pu.replacement.get(it.getKey().pos) instanceof ConstantIndex) { + ConstantIndex ci = (ConstantIndex) pu.replacement.get(it.getKey().pos); + ConstantPool pool = it.getValue(); + if (pool == null) { + continue; + } + if (pool.constants == null) { + continue; + } + if (addNew) { + pool.constants.add(changed); + ci.index = pool.constants.size() - 1; + } else { + pool.constants.set(ci.index, changed); + } + } else { + pu.replacement.set(it.getKey().pos, changed); + } + ret++; + } + } + + for (ASMSource src : actionsMap.keySet()) { + actionsMap.get(src).removeNops(); + src.setActions(actionsMap.get(src)); + src.setModified(); + } + + deobfuscation.deobfuscateInstanceNames(false, deobfuscated, renameType, tags, selected); + return ret; + } + + public void exportFla(AbortRetryIgnoreHandler handler, String outfile, String swfName, String generator, String generatorVerName, String generatorVersion, boolean parallel, FLAVersion version) throws IOException { + XFLConverter.convertSWF(handler, this, swfName, outfile, true, generator, generatorVerName, generatorVersion, parallel, version); + clearAllCache(); + } + + public void exportXfl(AbortRetryIgnoreHandler handler, String outfile, String swfName, String generator, String generatorVerName, String generatorVersion, boolean parallel, FLAVersion version) throws IOException { + XFLConverter.convertSWF(handler, this, swfName, outfile, false, generator, generatorVerName, generatorVersion, parallel, version); + clearAllCache(); + } + + public static AffineTransform matrixToTransform(MATRIX mat) { + return new AffineTransform(mat.getScaleXFloat(), mat.getRotateSkew0Float(), + mat.getRotateSkew1Float(), mat.getScaleYFloat(), + mat.translateX, mat.translateY); + } + + public SerializableImage getFromCache(String key) { + if (frameCache.contains(key)) { + return frameCache.get(key); + } + return null; + } + + public byte[] getFromCache(SoundTag soundTag) { + if (soundCache.contains(soundTag)) { + return soundCache.get(soundTag); + } + return null; + } + + public void putToCache(String key, SerializableImage img) { + if (Configuration.useFrameCache.get()) { + frameCache.put(key, img); + } + } + + public void putToCache(SoundTag soundTag, byte[] data) { + soundCache.put(soundTag, data); + } + + public void clearImageCache() { + frameCache.clear(); + DefineSpriteTag.clearCache(); + DefineButtonTag.clearCache(); + DefineButton2Tag.clearCache(); + for (Tag tag : tags) { + if (tag instanceof ImageTag) { + ((ImageTag) tag).clearCache(); + } + } + } + + public void clearScriptCache() { + as2Cache.clear(); + as3Cache.clear(); + } + + public void clearAllCache() { + characters = null; + abcList = null; + timeline = null; + clearImageCache(); + clearScriptCache(); + Cache.clearAll(); + Helper.clearShapeCache(); + System.gc(); + } + + public static void uncache(ASMSource src) { + if (src != null) { + src.getSwf().as2Cache.remove(src); + } + } + + public static void uncache(ScriptPack pack) { + if (pack != null) { + pack.getSwf().as3Cache.remove(pack); + } + } + + public static boolean isCached(ASMSource src) { + return src.getSwf().as2Cache.contains(src); + } + + public static boolean isCached(ScriptPack pack) { + return pack.getSwf().as3Cache.contains(pack); + } + + public static CachedScript getCached(ASMSource src, ActionList actions) throws InterruptedException { + SWF swf = src.getSwf(); + if (swf.as2Cache.contains(src)) { + return swf.as2Cache.get(src); + } + + if (actions == null) { + actions = src.getActions(); + } + + HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), true); + Action.actionsToSource(src, actions, src.toString()/*FIXME?*/, writer); + List hilights = writer.instructionHilights; + String srcNoHex = writer.toString(); + CachedScript res = new CachedScript(srcNoHex, hilights); + swf.as2Cache.put(src, res); + return res; + } + + public static CachedDecompilation getCached(ScriptPack pack) throws InterruptedException { + SWF swf = pack.getSwf(); + if (swf.as3Cache.contains(pack)) { + return swf.as3Cache.get(pack); + } + + int scriptIndex = pack.scriptIndex; + ScriptInfo script = null; + if (scriptIndex > -1) { + script = pack.abc.script_info.get(scriptIndex); + } + boolean parallel = Configuration.parallelSpeedUp.get(); + HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), true); + pack.toSource(writer, script.traits.traits, ScriptExportMode.AS, parallel); + HighlightedText hilightedCode = new HighlightedText(writer); + CachedDecompilation res = new CachedDecompilation(hilightedCode); + swf.as3Cache.put(pack, res); + + return res; + } + + public static RECT fixRect(RECT rect) { + RECT ret = new RECT(); + ret.Xmin = rect.Xmin; + ret.Xmax = rect.Xmax; + ret.Ymin = rect.Ymin; + ret.Ymax = rect.Ymax; + + if (ret.Xmax <= 0) { + ret.Xmax = ret.getWidth(); + ret.Xmin = 0; + } + if (ret.Ymax <= 0) { + ret.Ymax = ret.getHeight(); + ret.Ymin = 0; + } + if (ret.Xmin < 0) { + ret.Xmax += (-ret.Xmin); + ret.Xmin = 0; + } + if (ret.Ymin < 0) { + ret.Ymax += (-ret.Ymin); + ret.Ymin = 0; + } + + if (ret.getWidth() < 1 || ret.getHeight() < 1) { + ret.Xmin = 0; + ret.Ymin = 0; + ret.Xmax = 20; + ret.Ymax = 20; + } + return ret; + } + + public static void frameToSvg(Timeline timeline, int frame, int time, DepthState stateUnderCursor, int mouseButton, SVGExporter exporter, ColorTransform colorTransform, int level, double zoom) throws IOException { + if (timeline.getFrameCount() <= frame) { + return; + } + Frame frameObj = timeline.getFrame(frame); + List clips = new ArrayList<>(); + List prevClips = new ArrayList<>(); + + int maxDepth = timeline.getMaxDepth(); + for (int i = 1; i <= maxDepth; i++) { + for (int c = 0; c < clips.size(); c++) { + if (clips.get(c).depth == i) { + exporter.setClip(prevClips.get(c)); + prevClips.remove(c); + clips.remove(c); + } + } + if (!frameObj.layers.containsKey(i)) { + continue; + } + DepthState layer = frameObj.layers.get(i); + if (!timeline.swf.getCharacters().containsKey(layer.characterId)) { + continue; + } + if (!layer.isVisible) { + continue; + } + + CharacterTag character = timeline.swf.getCharacter(layer.characterId); + if (colorTransform == null) { + colorTransform = new ColorTransform(); + } + + ColorTransform clrTrans = colorTransform.clone(); + if (layer.colorTransForm != null && layer.blendMode <= 1) { // Normal blend mode + clrTrans = colorTransform.merge(layer.colorTransForm); + } + + if (character instanceof DrawableTag) { + DrawableTag drawable = (DrawableTag) character; + + String assetName; + Tag drawableTag = (Tag) drawable; + RECT boundRect = drawable.getRect(); + if (exporter.exportedTags.containsKey(drawableTag)) { + assetName = exporter.exportedTags.get(drawableTag); + } else { + assetName = getTagIdPrefix(drawableTag, exporter); + exporter.exportedTags.put(drawableTag, assetName); + exporter.createDefGroup(new ExportRectangle(boundRect), assetName); + drawable.toSVG(exporter, layer.ratio, clrTrans, level + 1, zoom); + exporter.endGroup(); + } + ExportRectangle rect = new ExportRectangle(boundRect); + + // TODO: if (layer.filters != null) + // TODO: if (layer.blendMode > 1) + if (layer.clipDepth > -1) { + String clipName = exporter.getUniqueId("clipPath"); + exporter.createClipPath(new Matrix(), clipName); + SvgClip clip = new SvgClip(clipName, layer.clipDepth); + clips.add(clip); + prevClips.add(exporter.getClip()); + Matrix mat = Matrix.getTranslateInstance(rect.xMin, rect.yMin).preConcatenate(new Matrix(layer.matrix)); + exporter.addUse(mat, boundRect, assetName); + exporter.setClip(clip.shape); + exporter.endGroup(); + } else { + Matrix mat = Matrix.getTranslateInstance(rect.xMin, rect.yMin).preConcatenate(new Matrix(layer.matrix)); + exporter.addUse(mat, boundRect, assetName); + } + } + } + } + + private static String getTagIdPrefix(Tag tag, SVGExporter exporter) { + if (tag instanceof ShapeTag) { + return exporter.getUniqueId("shape"); + } + if (tag instanceof MorphShapeTag) { + return exporter.getUniqueId("morphshape"); + } + if (tag instanceof DefineSpriteTag) { + return exporter.getUniqueId("sprite"); + } + if (tag instanceof TextTag) { + return exporter.getUniqueId("text"); + } + if (tag instanceof ButtonTag) { + return exporter.getUniqueId("button"); + } + return exporter.getUniqueId("tag"); + } + + public static SerializableImage frameToImageGet(Timeline timeline, int frame, int time, DepthState stateUnderCursor, int mouseButton, RECT displayRect, Matrix transformation, ColorTransform colorTransform, Color backGroundColor, boolean useCache, double zoom) { + SWF swf = timeline.swf; + String key = "frame_" + frame + "_" + timeline.id + "_" + swf.hashCode() + "_" + zoom; + SerializableImage image; + if (useCache) { + image = swf.getFromCache(key); + if (image != null) { + return image; + } + } + + if (timeline.getFrameCount() == 0) { + return new SerializableImage(1, 1, SerializableImage.TYPE_INT_ARGB); + } + + RECT rect = displayRect; + image = new SerializableImage((int) (rect.getWidth() * zoom / SWF.unitDivisor) + 1, + (int) (rect.getHeight() * zoom / SWF.unitDivisor) + 1, SerializableImage.TYPE_INT_ARGB); + if (backGroundColor == null) { + image.fillTransparent(); + } else { + Graphics2D g = (Graphics2D) image.getBufferedImage().getGraphics(); + g.setComposite(AlphaComposite.Src); + g.setColor(backGroundColor); + g.fill(new Rectangle(image.getWidth(), image.getHeight())); + } + + Matrix m = transformation.clone(); + m.translate(-rect.Xmin * zoom, -rect.Ymin * zoom); + m.scale(zoom); + RenderContext renderContext = new RenderContext(); + renderContext.stateUnderCursor = stateUnderCursor; + renderContext.mouseButton = mouseButton; + frameToImage(timeline, frame, time, renderContext, image, m, colorTransform); + if (useCache) { + swf.putToCache(key, image); + } + + return image; + } + + public static void framesToImage(Timeline timeline, List ret, int startFrame, int stopFrame, RenderContext renderContext, RECT displayRect, int totalFrameCount, Stack visited, Matrix transformation, ColorTransform colorTransform, double zoom) { + RECT rect = displayRect; + for (int f = 0; f < timeline.getFrameCount(); f++) { + SerializableImage image = new SerializableImage((int) (rect.getWidth() / SWF.unitDivisor) + 1, + (int) (rect.getHeight() / SWF.unitDivisor) + 1, SerializableImage.TYPE_INT_ARGB); + image.fillTransparent(); + Matrix m = new Matrix(); + m.translate(-rect.Xmin, -rect.Ymin); + frameToImage(timeline, f, 0, renderContext, image, m, colorTransform); + ret.add(image); + } + } + + public static void frameToImage(Timeline timeline, int frame, int time, RenderContext renderContext, SerializableImage image, Matrix transformation, ColorTransform colorTransform) { + double unzoom = SWF.unitDivisor; + if (timeline.getFrameCount() <= frame) { + return; + } + Frame frameObj = timeline.getFrame(frame); + Graphics2D g = (Graphics2D) image.getGraphics(); + g.setPaint(frameObj.backgroundColor.toColor()); + g.fill(new Rectangle(image.getWidth(), image.getHeight())); + g.setTransform(transformation.toTransform()); + List clips = new ArrayList<>(); + List prevClips = new ArrayList<>(); + + int maxDepth = timeline.getMaxDepth(); + for (int i = 1; i <= maxDepth; i++) { + for (int c = 0; c < clips.size(); c++) { + if (clips.get(c).depth == i) { + g.setClip(prevClips.get(c)); + prevClips.remove(c); + clips.remove(c); + } + } + if (!frameObj.layers.containsKey(i)) { + continue; + } + DepthState layer = frameObj.layers.get(i); + if (!timeline.swf.getCharacters().containsKey(layer.characterId)) { + continue; + } + if (!layer.isVisible) { + continue; + } + + CharacterTag character = timeline.swf.getCharacter(layer.characterId); + Matrix mat = new Matrix(layer.matrix); + mat = mat.preConcatenate(transformation); + + if (colorTransform == null) { + colorTransform = new ColorTransform(); + } + + ColorTransform clrTrans = colorTransform.clone(); + if (layer.colorTransForm != null && layer.blendMode <= 1) { // Normal blend mode + clrTrans = colorTransform.merge(layer.colorTransForm); + } + + boolean showPlaceholder = false; + if (character instanceof DrawableTag) { + DrawableTag drawable = (DrawableTag) character; + Matrix drawMatrix = new Matrix(); + int drawableFrameCount = drawable.getNumFrames(); + if (drawableFrameCount == 0) { + drawableFrameCount = 1; + } + + int dframe; + if (timeline.fontFrameNum != -1) { + dframe = timeline.fontFrameNum; + } else { + dframe = (time + layer.time) % drawableFrameCount; + } + + if (character instanceof ButtonTag) { + dframe = ButtonTag.FRAME_UP; + if (renderContext.stateUnderCursor == layer) { + if (renderContext.mouseButton > 0) { + dframe = ButtonTag.FRAME_DOWN; + } else { + dframe = ButtonTag.FRAME_OVER; + } + } + } + + RECT boundRect = drawable.getRect(); + ExportRectangle rect = new ExportRectangle(boundRect); + rect = mat.transform(rect); + Matrix m = mat.clone(); + if (layer.filters != null && layer.filters.size() > 0) { + // calculate size after applying the filters + double deltaXMax = 0; + double deltaYMax = 0; + for (FILTER filter : layer.filters) { + double x = filter.getDeltaX(); + double y = filter.getDeltaY(); + deltaXMax = Math.max(x, deltaXMax); + deltaYMax = Math.max(y, deltaYMax); + } + rect.xMin -= deltaXMax * unzoom; + rect.xMax += deltaXMax * unzoom; + rect.yMin -= deltaYMax * unzoom; + rect.yMax += deltaYMax * unzoom; + } + + rect.xMin -= 1 * unzoom; + rect.yMin -= 1 * unzoom; + rect.xMin = Math.max(0, rect.xMin); + rect.yMin = Math.max(0, rect.yMin); + + int newWidth = (int) (rect.getWidth() / unzoom); + int newHeight = (int) (rect.getHeight() / unzoom); + int deltaX = (int) (rect.xMin / unzoom); + int deltaY = (int) (rect.yMin / unzoom); + newWidth = Math.min(image.getWidth() - deltaX, newWidth) + 1; + newHeight = Math.min(image.getHeight() - deltaY, newHeight) + 1; + + if (newWidth <= 0 || newHeight <= 0) { + continue; + } + + m.translate(-rect.xMin, -rect.yMin); + drawMatrix.translate(rect.xMin, rect.yMin); + + SerializableImage img = null; + String cacheKey = null; + if (drawable instanceof ShapeTag) { + cacheKey = ((ShapeTag) drawable).getCharacterId() + m.toString() + clrTrans.toString(); + img = renderContext.shapeCache.get(cacheKey); + } + + if (img == null) { + img = new SerializableImage(newWidth, newHeight, SerializableImage.TYPE_INT_ARGB); + img.fillTransparent(); + + drawable.toImage(dframe, layer.time + time, layer.ratio, renderContext, img, m, clrTrans); + + if (cacheKey != null) { + renderContext.shapeCache.put(cacheKey, img); + } + } + + /*//if (renderContext.stateUnderCursor == layer) { + if (true) { + BufferedImage bi = img.getBufferedImage(); + ColorModel cm = bi.getColorModel(); + boolean isAlphaPremultiplied = cm.isAlphaPremultiplied(); + WritableRaster raster = bi.copyData(null); + img = new SerializableImage(new BufferedImage(cm, raster, isAlphaPremultiplied, null)); + Graphics2D gg = (Graphics2D) img.getGraphics(); + gg.setStroke(new BasicStroke(3)); + gg.setPaint(Color.red); + gg.setTransform(AffineTransform.getTranslateInstance(0, 0)); + gg.draw(SHAPERECORD.twipToPixelShape(drawable.getOutline(dframe, layer.time + time, layer.ratio, renderContext, m))); + }*/ + if (layer.filters != null) { + for (FILTER filter : layer.filters) { + img = filter.apply(img); + } + } + if (layer.blendMode > 1) { + if (layer.colorTransForm != null) { + img = layer.colorTransForm.apply(img); + } + } + + drawMatrix.translateX /= unzoom; + drawMatrix.translateY /= unzoom; + AffineTransform trans = drawMatrix.toTransform(); + + switch (layer.blendMode) { + case 0: + case 1: + g.setComposite(AlphaComposite.SrcOver); + break; + case 2: // Layer + g.setComposite(AlphaComposite.SrcOver); + break; + case 3: + g.setComposite(BlendComposite.Multiply); + break; + case 4: + g.setComposite(BlendComposite.Screen); + break; + case 5: + g.setComposite(BlendComposite.Lighten); + break; + case 6: + g.setComposite(BlendComposite.Darken); + break; + case 7: + g.setComposite(BlendComposite.Difference); + break; + case 8: + g.setComposite(BlendComposite.Add); + break; + case 9: + g.setComposite(BlendComposite.Subtract); + break; + case 10: + g.setComposite(BlendComposite.Invert); + break; + case 11: + g.setComposite(BlendComposite.Alpha); + break; + case 12: + g.setComposite(BlendComposite.Erase); + break; + case 13: + g.setComposite(BlendComposite.Overlay); + break; + case 14: + g.setComposite(BlendComposite.HardLight); + break; + default: // Not implemented + g.setComposite(AlphaComposite.SrcOver); + break; + } + + if (layer.clipDepth > -1) { + BufferedImage mask = new BufferedImage(image.getWidth(), image.getHeight(), image.getType()); + Graphics2D gm = (Graphics2D) mask.getGraphics(); + gm.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); + gm.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + gm.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + gm.setComposite(AlphaComposite.Src); + gm.setColor(new Color(0, 0, 0, 0f)); + gm.fillRect(0, 0, image.getWidth(), image.getHeight()); + gm.setTransform(trans); + gm.drawImage(img.getBufferedImage(), 0, 0, null); + Clip clip = new Clip(Helper.imageToShape(mask), layer.clipDepth); // Maybe we can get current outline instead converting from image (?) + clips.add(clip); + prevClips.add(g.getClip()); + g.setTransform(AffineTransform.getTranslateInstance(0, 0)); + g.setClip(clip.shape); + } else { + g.setTransform(trans); + g.drawImage(img.getBufferedImage(), 0, 0, null); + } + } else if (character instanceof BoundedTag) { + showPlaceholder = true; + } + + if (showPlaceholder) { + mat.translateX /= unzoom; + mat.translateY /= unzoom; + AffineTransform trans = mat.toTransform(); + g.setTransform(trans); + BoundedTag b = (BoundedTag) character; + g.setPaint(new Color(255, 255, 255, 128)); + g.setComposite(BlendComposite.Invert); + RECT r = b.getRect(); + int div = (int) unzoom; + g.drawString(character.toString(), r.Xmin / div + 3, r.Ymin / div + 15); + g.draw(new Rectangle(r.Xmin / div, r.Ymin / div, r.getWidth() / div, r.getHeight() / div)); + g.drawLine(r.Xmin / div, r.Ymin / div, r.Xmax / div, r.Ymax / div); + g.drawLine(r.Xmax / div, r.Ymin / div, r.Xmin / div, r.Ymax / div); + g.setComposite(AlphaComposite.Dst); + } + } + + g.setTransform(AffineTransform.getScaleInstance(1, 1)); + } + + private void removeTagWithDependenciesFromTimeline(Tag toRemove, Timeline timeline) { + Map stage = new HashMap<>(); + Set dependingChars = new HashSet<>(); + if (toRemove instanceof CharacterTag) { + int characterId = ((CharacterTag) toRemove).getCharacterId(); + + if (characterId != 0) { + dependingChars.add(characterId); + for (int i = 0; i < timeline.tags.size(); i++) { + Tag t = timeline.tags.get(i); + if (t instanceof CharacterIdTag) { + CharacterIdTag c = (CharacterIdTag) t; + Set needed = new HashSet<>(); + t.getNeededCharacters(needed); + if (needed.contains(characterId)) { + dependingChars.add(c.getCharacterId()); + } + } + } + } + } + + for (int i = 0; i < timeline.tags.size(); i++) { + Tag t = timeline.tags.get(i); + if (t instanceof RemoveTag) { + RemoveTag rt = (RemoveTag) t; + int depth = rt.getDepth(); + if (stage.containsKey(depth)) { + int currentCharId = stage.get(depth); + stage.remove(depth); + if (dependingChars.contains(currentCharId)) { + timeline.tags.remove(i); + i--; + continue; + } + } + } + if (t instanceof PlaceObjectTypeTag) { + PlaceObjectTypeTag po = (PlaceObjectTypeTag) t; + int placeCharId = po.getCharacterId(); + int depth = po.getDepth(); + if (placeCharId != 0) { + stage.put(depth, placeCharId); + if (dependingChars.contains(placeCharId)) { + timeline.tags.remove(i); + i--; + continue; + } + } + } + if (t instanceof CharacterIdTag) { + CharacterIdTag c = (CharacterIdTag) t; + if (dependingChars.contains(c.getCharacterId())) { + timeline.tags.remove(i); + i--; + continue; + } + } + Set needed = new HashSet<>(); + t.getNeededCharacters(needed); + for (int dep : dependingChars) { + if (needed.contains(dep)) { + timeline.tags.remove(i); + i--; + continue; + } + } + if (t == toRemove) { + timeline.tags.remove(i); + i--; + continue; + } + if (t instanceof Timelined) { + removeTagWithDependenciesFromTimeline(toRemove, ((Timelined) t).getTimeline()); + } + } + } + + private boolean removeTagFromTimeline(Tag toRemove, Timeline timeline) { + boolean modified = false; + int characterId = -1; + if (toRemove instanceof CharacterTag) { + characterId = ((CharacterTag) toRemove).getCharacterId(); + modified = timeline.removeCharacter(characterId); + } + for (int i = 0; i < timeline.tags.size(); i++) { + Tag t = timeline.tags.get(i); + if (t == toRemove) { + timeline.tags.remove(t); + i--; + continue; + } + + if (toRemove instanceof CharacterTag) { + if (t.removeCharacter(characterId)) { + modified = true; + i = -1; + continue; + } + } + + if (t instanceof DefineSpriteTag) { + DefineSpriteTag spr = (DefineSpriteTag) t; + boolean sprModified = removeTagFromTimeline(toRemove, spr.getTimeline()); + if (sprModified) { + spr.setModified(true); + } + modified |= sprModified; + } + } + return modified; + } + + public void removeTags(Collection tags, boolean removeDependencies) { + Set timelineds = new HashSet<>(); + for (Tag tag : tags) { + Timelined timelined = tag.getTimelined(); + timelineds.add(timelined); + removeTagInternal(timelined, tag, removeDependencies); + } + + for (Timelined timelined : timelineds) { + resetTimelines(timelined); + } + + updateCharacters(); + clearImageCache(); + } + + public void removeTag(Tag tag, boolean removeDependencies) { + Timelined timelined = tag.getTimelined(); + removeTagInternal(timelined, tag, removeDependencies); + resetTimelines(timelined); + updateCharacters(); + clearImageCache(); + } + + private void removeTagInternal(Timelined timelined, Tag tag, boolean removeDependencies) { + if (tag instanceof ShowFrameTag || ShowFrameTag.isNestedTagType(tag.getId())) { + List tags; + if (timelined instanceof DefineSpriteTag) { + DefineSpriteTag sprite = (DefineSpriteTag) timelined; + tags = sprite.getSubTags(); + } else { + tags = this.tags; + } + tags.remove(tag); + if (timelined instanceof DefineSpriteTag) { + DefineSpriteTag sprite = (DefineSpriteTag) timelined; + sprite.setModified(true); + } + timelined.resetTimeline(); + } else { + // timeline should be always the swf here + if (removeDependencies) { + removeTagWithDependenciesFromTimeline(tag, timelined.getTimeline()); + if (timelined instanceof DefineSpriteTag) { + DefineSpriteTag sprite = (DefineSpriteTag) timelined; + sprite.setModified(true); + } + } else { + removeTagFromTimeline(tag, timeline); + } + } + } + + @Override + public String toString() { + return getShortFileName(); + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ABC.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ABC.java index 4692f7eb3..10a27d85e 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ABC.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ABC.java @@ -1,1271 +1,1273 @@ -/* - * Copyright (C) 2010-2015 JPEXS, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -package com.jpexs.decompiler.flash.abc; - -import com.jpexs.decompiler.flash.EventListener; -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; -import com.jpexs.decompiler.flash.abc.avm2.AVM2ConstantPool; -import com.jpexs.decompiler.flash.abc.avm2.AVM2Deobfuscation; -import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; -import com.jpexs.decompiler.flash.abc.avm2.instructions.executing.CallPropertyIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushStringIns; -import com.jpexs.decompiler.flash.abc.avm2.parser.AVM2ParseException; -import com.jpexs.decompiler.flash.abc.avm2.parser.script.ActionScriptParser; -import com.jpexs.decompiler.flash.abc.types.ABCException; -import com.jpexs.decompiler.flash.abc.types.ClassInfo; -import com.jpexs.decompiler.flash.abc.types.InstanceInfo; -import com.jpexs.decompiler.flash.abc.types.MetadataInfo; -import com.jpexs.decompiler.flash.abc.types.MethodBody; -import com.jpexs.decompiler.flash.abc.types.MethodInfo; -import com.jpexs.decompiler.flash.abc.types.Multiname; -import com.jpexs.decompiler.flash.abc.types.Namespace; -import com.jpexs.decompiler.flash.abc.types.NamespaceSet; -import com.jpexs.decompiler.flash.abc.types.ScriptInfo; -import com.jpexs.decompiler.flash.abc.types.traits.Trait; -import com.jpexs.decompiler.flash.abc.types.traits.TraitClass; -import com.jpexs.decompiler.flash.abc.types.traits.TraitFunction; -import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter; -import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst; -import com.jpexs.decompiler.flash.abc.types.traits.Traits; -import com.jpexs.decompiler.flash.abc.usages.ClassNameMultinameUsage; -import com.jpexs.decompiler.flash.abc.usages.ConstVarNameMultinameUsage; -import com.jpexs.decompiler.flash.abc.usages.ConstVarTypeMultinameUsage; -import com.jpexs.decompiler.flash.abc.usages.DefinitionUsage; -import com.jpexs.decompiler.flash.abc.usages.ExtendsMultinameUsage; -import com.jpexs.decompiler.flash.abc.usages.ImplementsMultinameUsage; -import com.jpexs.decompiler.flash.abc.usages.MethodBodyMultinameUsage; -import com.jpexs.decompiler.flash.abc.usages.MethodNameMultinameUsage; -import com.jpexs.decompiler.flash.abc.usages.MethodParamsMultinameUsage; -import com.jpexs.decompiler.flash.abc.usages.MethodReturnTypeMultinameUsage; -import com.jpexs.decompiler.flash.abc.usages.MultinameUsage; -import com.jpexs.decompiler.flash.abc.usages.TypeNameMultinameUsage; -import com.jpexs.decompiler.flash.helpers.SWFDecompilerPlugin; -import com.jpexs.decompiler.flash.tags.ABCContainerTag; -import com.jpexs.decompiler.flash.tags.Tag; -import com.jpexs.decompiler.flash.types.annotations.Internal; -import com.jpexs.decompiler.graph.CompilationException; -import com.jpexs.helpers.utf8.Utf8PrintWriter; -import java.io.IOException; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; - -public class ABC { - - public int major_version = 46; - - public int minor_version = 16; - - public AVM2ConstantPool constants = new AVM2ConstantPool(); - - public List method_info = new ArrayList<>(); - - public List metadata_info = new ArrayList<>(); - - public List instance_info = new ArrayList<>(); - - public List class_info = new ArrayList<>(); - - public List script_info = new ArrayList<>(); - - public List bodies = new ArrayList<>(); - - private Map bodyIdxFromMethodIdx; - - private long[] stringOffsets; - - public static final int MINORwithDECIMAL = 17; - - protected Set listeners = new HashSet<>(); - - private static final Logger logger = Logger.getLogger(ABC.class.getName()); - - private AVM2Deobfuscation deobfuscation; - - @Internal - public ABCContainerTag parentTag; - - /* Map from multiname index of namespace value to namespace name**/ - private Map namespaceMap; - - public ABC(ABCContainerTag tag) { - this.parentTag = tag; - this.deobfuscation = null; - constants.constant_double.add(null); - constants.constant_int.add(null); - constants.constant_uint.add(null); - constants.constant_string.add(null); - constants.constant_multiname.add(null); - constants.constant_namespace.add(null); - constants.constant_namespace_set.add(null); - } - - public SWF getSwf() { - return parentTag.getSwf(); - } - - public List getAbcTags() { - return getSwf().getAbcList(); - } - - public int addMethodBody(MethodBody body) { - bodies.add(body); - bodyIdxFromMethodIdx = null; - return bodies.size() - 1; - } - - public int addMethodInfo(MethodInfo mi) { - method_info.add(mi); - return method_info.size() - 1; - } - - public void addEventListener(EventListener listener) { - listeners.add(listener); - } - - public void removeEventListener(EventListener listener) { - listeners.remove(listener); - } - - protected void informListeners(String event, Object data) { - for (EventListener listener : listeners) { - listener.handleEvent(event, data); - } - } - - public int removeTraps() throws InterruptedException { - int rem = 0; - for (int s = 0; s < script_info.size(); s++) { - rem += script_info.get(s).removeTraps(s, this, ""); - } - return rem; - } - - public int removeDeadCode() throws InterruptedException { - int rem = 0; - for (MethodBody body : bodies) { - rem += body.removeDeadCode(constants, null/*FIXME*/, method_info.get(body.method_info)); - } - return rem; - } - - public void restoreControlFlow() throws InterruptedException { - for (MethodBody body : bodies) { - body.restoreControlFlow(constants, null/*FIXME*/, method_info.get(body.method_info)); - } - } - - public Set getNsStringUsages() { - Set ret = new HashSet<>(); - for (int n = 1; n < constants.getNamespaceCount(); n++) { - ret.add(constants.getNamespace(n).name_index); - } - return ret; - } - - public Set getStringUsages() { - Set ret = new HashSet<>(); - for (MethodBody body : bodies) { - for (AVM2Instruction ins : body.getCode().code) { - for (int i = 0; i < ins.definition.operands.length; i++) { - if (ins.definition.operands[i] == AVM2Code.DAT_STRING_INDEX) { - ret.add(ins.operands[i]); - } - } - } - } - return ret; - } - - private void setStringUsageType(Map ret, int strIndex, String usageType) { - if (ret.containsKey(strIndex)) { - if (!"name".equals(usageType)) { - if (!ret.get(strIndex).equals(usageType)) { - ret.put(strIndex, "name"); - } - } - } else { - ret.put(strIndex, usageType); - } - } - - private void getStringUsageTypes(Map ret, Traits traits, boolean classesOnly) { - for (Trait t : traits.traits) { - int strIndex = constants.getMultiname(t.name_index).name_index; - String usageType = ""; - if (t instanceof TraitClass) { - TraitClass tc = (TraitClass) t; - getStringUsageTypes(ret, class_info.get(tc.class_info).static_traits, classesOnly); - getStringUsageTypes(ret, instance_info.get(tc.class_info).instance_traits, classesOnly); - - if (instance_info.get(tc.class_info).name_index != 0) { - setStringUsageType(ret, constants.getMultiname(instance_info.get(tc.class_info).name_index).name_index, "class"); - } - if (instance_info.get(tc.class_info).super_index != 0) { - setStringUsageType(ret, constants.getMultiname(instance_info.get(tc.class_info).super_index).name_index, "class"); - } - - usageType = "class"; - } - if (t instanceof TraitMethodGetterSetter) { - TraitMethodGetterSetter tm = (TraitMethodGetterSetter) t; - usageType = "method"; - MethodBody body = findBody(tm.method_info); - if (body != null) { - getStringUsageTypes(ret, body.traits, classesOnly); - } - } - if (t instanceof TraitFunction) { - TraitFunction tf = (TraitFunction) t; - MethodBody body = findBody(tf.method_info); - if (body != null) { - getStringUsageTypes(ret, body.traits, classesOnly); - } - usageType = "function"; - } - if (t instanceof TraitSlotConst) { - TraitSlotConst ts = (TraitSlotConst) t; - if (ts.isVar()) { - usageType = "var"; - } - if (ts.isConst()) { - usageType = "const"; - } - } - if (usageType.equals("class") || (!classesOnly)) { - setStringUsageType(ret, strIndex, usageType); - } - } - } - - public void getStringUsageTypes(Map ret, boolean classesOnly) { - for (ScriptInfo script : script_info) { - getStringUsageTypes(ret, script.traits, classesOnly); - } - } - - public void renameMultiname(int multinameIndex, String newname) { - if (multinameIndex <= 0 || multinameIndex >= constants.getMultinameCount()) { - throw new IllegalArgumentException("Multiname with index " + multinameIndex + " does not exist"); - } - Set stringUsages = getStringUsages(); - Set namespaceUsages = getNsStringUsages(); - int strIndex = constants.getMultiname(multinameIndex).name_index; - if (stringUsages.contains(strIndex) || namespaceUsages.contains(strIndex)) { // name is used elsewhere as string literal - strIndex = constants.getStringId(newname, true); - constants.getMultiname(multinameIndex).name_index = strIndex; - } else { - constants.setString(strIndex, newname); - } - } - - public void deobfuscateIdentifiers(HashMap namesMap, RenameType renameType, boolean classesOnly) { - Set stringUsages = getStringUsages(); - Set namespaceUsages = getNsStringUsages(); - Map stringUsageTypes = new HashMap<>(); - informListeners("deobfuscate", "Getting usage types..."); - getStringUsageTypes(stringUsageTypes, classesOnly); - AVM2Deobfuscation deobfuscation = getDeobfuscation(); - for (int i = 0; i < instance_info.size(); i++) { - informListeners("deobfuscate", "class " + i + "/" + instance_info.size()); - InstanceInfo insti = instance_info.get(i); - if (insti.name_index != 0) { - constants.getMultiname(insti.name_index).name_index = deobfuscation.deobfuscateName(stringUsageTypes, stringUsages, namespaceUsages, namesMap, constants.getMultiname(insti.name_index).name_index, true, renameType); - if (constants.getMultiname(insti.name_index).namespace_index != 0) { - constants.getNamespace(constants.getMultiname(insti.name_index).namespace_index).name_index - = deobfuscation.deobfuscatePackageName(stringUsageTypes, stringUsages, namesMap, constants.getNamespace(constants.getMultiname(insti.name_index).namespace_index).name_index, renameType); - } - } - if (insti.super_index != 0) { - constants.getMultiname(insti.super_index).name_index = deobfuscation.deobfuscateName(stringUsageTypes, stringUsages, namespaceUsages, namesMap, constants.getMultiname(insti.super_index).name_index, true, renameType); - } - } - if (classesOnly) { - return; - } - for (int i = 1; i < constants.getMultinameCount(); i++) { - informListeners("deobfuscate", "name " + i + "/" + constants.getMultinameCount()); - constants.getMultiname(i).name_index = deobfuscation.deobfuscateName(stringUsageTypes, stringUsages, namespaceUsages, namesMap, constants.getMultiname(i).name_index, false, renameType); - } - for (int i = 1; i < constants.getNamespaceCount(); i++) { - informListeners("deobfuscate", "namespace " + i + "/" + constants.getNamespaceCount()); - if (constants.getNamespace(i).kind != Namespace.KIND_PACKAGE) { // only packages - continue; - } - constants.getNamespace(i).name_index = deobfuscation.deobfuscatePackageName(stringUsageTypes, stringUsages, namesMap, constants.getNamespace(i).name_index, renameType); - } - - // process reflection using getDefinitionByName too - for (MethodBody body : bodies) { - for (int ip = 0; ip < body.getCode().code.size(); ip++) { - if (body.getCode().code.get(ip).definition instanceof CallPropertyIns) { - int mIndex = body.getCode().code.get(ip).operands[0]; - if (mIndex > 0) { - Multiname m = constants.getMultiname(mIndex); - if (m.getNameWithNamespace(constants, true).equals("flash.utils.getDefinitionByName")) { - if (ip > 0) { - if (body.getCode().code.get(ip - 1).definition instanceof PushStringIns) { - int strIndex = body.getCode().code.get(ip - 1).operands[0]; - String fullname = constants.getString(strIndex); - String pkg = ""; - String name = fullname; - if (fullname.contains(".")) { - pkg = fullname.substring(0, fullname.lastIndexOf('.')); - name = fullname.substring(fullname.lastIndexOf('.') + 1); - } - if (!pkg.isEmpty()) { - int pkgStrIndex = constants.getStringId(pkg, true); - pkgStrIndex = deobfuscation.deobfuscatePackageName(stringUsageTypes, stringUsages, namesMap, pkgStrIndex, renameType); - pkg = constants.getString(pkgStrIndex); - } - int nameStrIndex = constants.getStringId(name, true); - nameStrIndex = deobfuscation.deobfuscateName(stringUsageTypes, stringUsages, namespaceUsages, namesMap, nameStrIndex, true, renameType); - name = constants.getString(nameStrIndex); - String fullChanged = ""; - if (!pkg.isEmpty()) { - fullChanged = pkg + "."; - } - fullChanged += name; - strIndex = constants.getStringId(fullChanged, true); - body.getCode().code.get(ip - 1).operands[0] = strIndex; - } - } - } - } - } - } - } - } - - public ABC(ABCInputStream ais, SWF swf, ABCContainerTag tag) throws IOException { - this.parentTag = tag; - minor_version = ais.readU16("minor_version"); - major_version = ais.readU16("major_version"); - logger.log(Level.FINE, "ABC minor_version: {0}, major_version: {1}", new Object[]{minor_version, major_version}); - - constants = new AVM2ConstantPool(); - ais.newDumpLevel("constant_pool", "cpool_info"); - - // constant integers - int constant_int_pool_count = ais.readU30("int_count"); - constants.constant_int = new ArrayList<>(constant_int_pool_count); - if (constant_int_pool_count > 0) { - constants.addInt(0); - } - if (constant_int_pool_count > 1) { - ais.newDumpLevel("integers", "integer[]"); - for (int i = 1; i < constant_int_pool_count; i++) { // index 0 not used. Values 1..n-1 - constants.addInt(ais.readS32("int")); - } - ais.endDumpLevel(); - } - - // constant unsigned integers - int constant_uint_pool_count = ais.readU30("uint_count"); - constants.constant_uint = new ArrayList<>(constant_uint_pool_count); - if (constant_uint_pool_count > 0) { - constants.addUInt(0); - } - if (constant_uint_pool_count > 1) { - ais.newDumpLevel("uintegers", "uinteger[]"); - for (int i = 1; i < constant_uint_pool_count; i++) { // index 0 not used. Values 1..n-1 - constants.addUInt(ais.readU32("uint")); - } - ais.endDumpLevel(); - } - - // constant double - int constant_double_pool_count = ais.readU30("double_count"); - constants.constant_double = new ArrayList<>(constant_double_pool_count); - if (constant_double_pool_count > 0) { - constants.addDouble(0); - } - if (constant_double_pool_count > 1) { - ais.newDumpLevel("doubles", "double[]"); - for (int i = 1; i < constant_double_pool_count; i++) { // index 0 not used. Values 1..n-1 - constants.addDouble(ais.readDouble("double")); - } - ais.endDumpLevel(); - } - - // constant decimal - if (minor_version >= MINORwithDECIMAL) { - int constant_decimal_pool_count = ais.readU30("decimal_count"); - constants.constant_decimal = new ArrayList<>(constant_decimal_pool_count); - if (constant_decimal_pool_count > 0) { - constants.addDecimal(null); - } - if (constant_decimal_pool_count > 1) { - ais.newDumpLevel("decimals", "decimal[]"); - for (int i = 1; i < constant_decimal_pool_count; i++) { // index 0 not used. Values 1..n-1 - constants.addDecimal(ais.readDecimal("decimal")); - } - ais.endDumpLevel(); - } - } else { - constants.constant_decimal = new ArrayList<>(0); - } - - // constant string - int constant_string_pool_count = ais.readU30("string_count"); - constants.constant_string = new ArrayList<>(constant_string_pool_count); - stringOffsets = new long[constant_string_pool_count]; - if (constant_string_pool_count > 0) { - constants.addString(""); - } - if (constant_string_pool_count > 1) { - ais.newDumpLevel("strings", "string[]"); - for (int i = 1; i < constant_string_pool_count; i++) { // index 0 not used. Values 1..n-1 - long pos = ais.getPosition(); - constants.addString(ais.readString("string")); - stringOffsets[i] = pos; - } - ais.endDumpLevel(); - } - - // constant namespace - int constant_namespace_pool_count = ais.readU30("namespace_count"); - constants.constant_namespace = new ArrayList<>(constant_namespace_pool_count); - if (constant_namespace_pool_count > 0) { - constants.addNamespace(null); - } - if (constant_namespace_pool_count > 1) { - ais.newDumpLevel("namespaces", "namespace[]"); - for (int i = 1; i < constant_namespace_pool_count; i++) { // index 0 not used. Values 1..n-1 - constants.addNamespace(ais.readNamespace("namespace")); - } - ais.endDumpLevel(); - } - - // constant namespace set - int constant_namespace_set_pool_count = ais.readU30("ns_set_count"); - constants.constant_namespace_set = new ArrayList<>(constant_namespace_set_pool_count); - if (constant_namespace_set_pool_count > 0) { - constants.addNamespaceSet(null); - } - if (constant_namespace_set_pool_count > 1) { - ais.newDumpLevel("ns_sets", "ns_set[]"); - for (int i = 1; i < constant_namespace_set_pool_count; i++) { // index 0 not used. Values 1..n-1 - ais.newDumpLevel("ns_set_infos", "ns_set_info[]"); - constants.addNamespaceSet(new NamespaceSet()); - int namespace_count = ais.readU30("count"); - constants.getNamespaceSet(i).namespaces = new int[namespace_count]; - for (int j = 0; j < namespace_count; j++) { - constants.getNamespaceSet(i).namespaces[j] = ais.readU30("ns"); - } - ais.endDumpLevel(); - } - ais.endDumpLevel(); - } - - // constant multiname - int constant_multiname_pool_count = ais.readU30("multiname_count"); - constants.constant_multiname = new ArrayList<>(constant_multiname_pool_count); - if (constant_multiname_pool_count > 0) { - constants.addMultiname(null); - } - if (constant_multiname_pool_count > 1) { - ais.newDumpLevel("multiname", "multinames[]"); - for (int i = 1; i < constant_multiname_pool_count; i++) { // index 0 not used. Values 1..n-1 - constants.addMultiname(ais.readMultiname("multiname")); - } - ais.endDumpLevel(); - } - - ais.endDumpLevel(); // cpool_info - - // method info - int methods_count = ais.readU30("methods_count"); - method_info = new ArrayList<>(methods_count); // MethodInfo[methods_count]; - for (int i = 0; i < methods_count; i++) { - method_info.add(ais.readMethodInfo("method")); - } - - // metadata info - int metadata_count = ais.readU30("metadata_count"); - metadata_info = new ArrayList<>(metadata_count); - for (int i = 0; i < metadata_count; i++) { - int name_index = ais.readU30("name_index"); - int values_count = ais.readU30("values_count"); - int[] keys = new int[values_count]; - for (int v = 0; v < values_count; v++) { - keys[v] = ais.readU30("key"); - } - int[] values = new int[values_count]; - for (int v = 0; v < values_count; v++) { - values[v] = ais.readU30("value"); - } - metadata_info.add(new MetadataInfo(name_index, keys, values)); - } - - int class_count = ais.readU30("class_count"); - instance_info = new ArrayList<>(class_count); - for (int i = 0; i < class_count; i++) { - instance_info.add(ais.readInstanceInfo("instance")); - } - class_info = new ArrayList<>(class_count); - for (int i = 0; i < class_count; i++) { - ais.newDumpLevel("class", "class_info"); - ClassInfo ci = new ClassInfo(null); // do not create Traits in constructor - ci.cinit_index = ais.readU30("cinit_index"); - ci.static_traits = ais.readTraits("static_traits"); - class_info.add(ci); - ais.endDumpLevel(); - } - int script_count = ais.readU30("script_count"); - script_info = new ArrayList<>(script_count); - for (int i = 0; i < script_count; i++) { - ais.newDumpLevel("script", "script_info"); - ScriptInfo si = new ScriptInfo(null); // do not create Traits in constructor - si.init_index = ais.readU30("init_index"); - si.traits = ais.readTraits("traits"); - script_info.add(si); - ais.endDumpLevel(); - si.setModified(false); - } - - int bodies_count = ais.readU30("bodies_count"); - bodies = new ArrayList<>(bodies_count); - for (int i = 0; i < bodies_count; i++) { - ais.newDumpLevel("method_body", "method_body_info"); - MethodBody mb = new MethodBody(null, null, null); // do not create Traits in constructor - mb.method_info = ais.readU30("method_info"); - mb.max_stack = ais.readU30("max_stack"); - mb.max_regs = ais.readU30("max_regs"); - mb.init_scope_depth = ais.readU30("init_scope_depth"); - mb.max_scope_depth = ais.readU30("max_scope_depth"); - int code_length = ais.readU30("code_length"); - mb.setCodeBytes(ais.readBytes(code_length, "code")); - int ex_count = ais.readU30("ex_count"); - mb.exceptions = new ABCException[ex_count]; - for (int j = 0; j < ex_count; j++) { - ABCException abce = new ABCException(); - abce.start = ais.readU30("start"); - abce.end = ais.readU30("end"); - abce.target = ais.readU30("target"); - abce.type_index = ais.readU30("type_index"); - abce.name_index = ais.readU30("name_index"); - mb.exceptions[j] = abce; - } - mb.traits = ais.readTraits("traits"); - bodies.add(mb); - method_info.get(mb.method_info).setBody(mb); - ais.endDumpLevel(); - - SWFDecompilerPlugin.fireMethodBodyParsed(mb, swf); - } - - /*for(int i=0;i()); - } catch (InterruptedException ex) { - Logger.getLogger(ABC.class.getName()).log(Level.SEVERE, null, ex); - } - System.out.println(""+t.toString()); - } - //System.exit(0);*/ - SWFDecompilerPlugin.fireAbcParsed(this, swf); - } - - public void saveToStream(OutputStream os) throws IOException { - ABCOutputStream aos = new ABCOutputStream(os); - aos.writeU16(minor_version); - aos.writeU16(major_version); - - aos.writeU30(constants.getIntCount()); - for (int i = 1; i < constants.getIntCount(); i++) { - aos.writeS32(constants.getInt(i)); - } - aos.writeU30(constants.getUIntCount()); - for (int i = 1; i < constants.getUIntCount(); i++) { - aos.writeU32(constants.getUInt(i)); - } - - aos.writeU30(constants.getDoubleCount()); - for (int i = 1; i < constants.getDoubleCount(); i++) { - aos.writeDouble(constants.getDouble(i)); - } - - if (minor_version >= MINORwithDECIMAL) { - aos.writeU30(constants.getDecimalCount()); - for (int i = 1; i < constants.getDecimalCount(); i++) { - aos.writeDecimal(constants.getDecimal(i)); - } - } - - aos.writeU30(constants.getStringCount()); - for (int i = 1; i < constants.getStringCount(); i++) { - aos.writeString(constants.getString(i)); - } - - aos.writeU30(constants.getNamespaceCount()); - for (int i = 1; i < constants.getNamespaceCount(); i++) { - aos.writeNamespace(constants.getNamespace(i)); - } - - aos.writeU30(constants.getNamespaceSetCount()); - for (int i = 1; i < constants.getNamespaceSetCount(); i++) { - aos.writeU30(constants.getNamespaceSet(i).namespaces.length); - for (int j = 0; j < constants.getNamespaceSet(i).namespaces.length; j++) { - aos.writeU30(constants.getNamespaceSet(i).namespaces[j]); - } - } - - aos.writeU30(constants.getMultinameCount()); - for (int i = 1; i < constants.getMultinameCount(); i++) { - aos.writeMultiname(constants.getMultiname(i)); - } - - aos.writeU30(method_info.size()); - for (MethodInfo mi : method_info) { - aos.writeMethodInfo(mi); - } - - aos.writeU30(metadata_info.size()); - for (MetadataInfo mi : metadata_info) { - aos.writeU30(mi.name_index); - aos.writeU30(mi.values.length); - for (int j = 0; j < mi.values.length; j++) { - aos.writeU30(mi.keys[j]); - } - for (int j = 0; j < mi.values.length; j++) { - aos.writeU30(mi.values[j]); - } - } - - aos.writeU30(class_info.size()); - for (InstanceInfo ii : instance_info) { - aos.writeInstanceInfo(ii); - } - for (ClassInfo ci : class_info) { - aos.writeU30(ci.cinit_index); - aos.writeTraits(ci.static_traits); - } - aos.writeU30(script_info.size()); - for (ScriptInfo si : script_info) { - aos.writeU30(si.init_index); - aos.writeTraits(si.traits); - } - - aos.writeU30(bodies.size()); - for (MethodBody mb : bodies) { - aos.writeU30(mb.method_info); - aos.writeU30(mb.max_stack); - aos.writeU30(mb.max_regs); - aos.writeU30(mb.init_scope_depth); - aos.writeU30(mb.max_scope_depth); - byte[] codeBytes = mb.getCodeBytes(); - aos.writeU30(codeBytes.length); - aos.write(codeBytes); - aos.writeU30(mb.exceptions.length); - for (int j = 0; j < mb.exceptions.length; j++) { - aos.writeU30(mb.exceptions[j].start); - aos.writeU30(mb.exceptions[j].end); - aos.writeU30(mb.exceptions[j].target); - aos.writeU30(mb.exceptions[j].type_index); - aos.writeU30(mb.exceptions[j].name_index); - } - aos.writeTraits(mb.traits); - } - } - - public MethodBody findBody(int methodInfo) { - if (methodInfo < 0) { - return null; - } - return method_info.get(methodInfo).getBody(); - } - - public int findBodyIndex(int methodInfo) { - if (methodInfo == -1) { - return -1; - } - - Integer result = getBodyIdxFromMethodIdx().get(methodInfo); - if (result == null) { - return -1; - } - - return result; - } - - public MethodBody findBodyByClassAndName(String className, String methodName) { - for (int i = 0; i < instance_info.size(); i++) { - if (className.equals(constants.getMultiname(instance_info.get(i).name_index).getName(constants, new ArrayList(), true))) { - for (Trait t : instance_info.get(i).instance_traits.traits) { - if (t instanceof TraitMethodGetterSetter) { - TraitMethodGetterSetter t2 = (TraitMethodGetterSetter) t; - if (methodName.equals(t2.getName(this).getName(constants, new ArrayList(), true))) { - for (MethodBody body : bodies) { - if (body.method_info == t2.method_info) { - return body; - } - } - } - } - } - //break; - } - } - for (int i = 0; i < class_info.size(); i++) { - if (className.equals(constants.getMultiname(instance_info.get(i).name_index).getName(constants, new ArrayList(), true))) { - for (Trait t : class_info.get(i).static_traits.traits) { - if (t instanceof TraitMethodGetterSetter) { - TraitMethodGetterSetter t2 = (TraitMethodGetterSetter) t; - if (methodName.equals(t2.getName(this).getName(constants, new ArrayList(), true))) { - for (MethodBody body : bodies) { - if (body.method_info == t2.method_info) { - return body; - } - } - } - } - } - //break; - } - } - - return null; - } - - public boolean isStaticTraitId(int classIndex, int traitId) { - if (traitId < class_info.get(classIndex).static_traits.traits.size()) { - return true; - } else if (traitId < class_info.get(classIndex).static_traits.traits.size() + instance_info.get(classIndex).instance_traits.traits.size()) { - return false; - } else { - return true; // Can be class or instance initializer - } - } - - public Trait findTraitByTraitId(int classIndex, int traitId) { - if (classIndex == -1) { - return null; - } - List staticTraits = class_info.get(classIndex).static_traits.traits; - if (traitId < staticTraits.size()) { - return staticTraits.get(traitId); - } else { - List instanceTraits = instance_info.get(classIndex).instance_traits.traits; - if (traitId < staticTraits.size() + instanceTraits.size()) { - traitId -= staticTraits.size(); - return instanceTraits.get(traitId); - } else { - return null; // Can be class or instance initializer - } - } - } - - public int findMethodIdByTraitId(int classIndex, int traitId) { - if (classIndex == -1) { - return -1; - } - List staticTraits = class_info.get(classIndex).static_traits.traits; - if (traitId < staticTraits.size()) { - if (staticTraits.get(traitId) instanceof TraitMethodGetterSetter) { - return ((TraitMethodGetterSetter) staticTraits.get(traitId)).method_info; - } else { - return -1; - } - } else { - List instanceTraits = instance_info.get(classIndex).instance_traits.traits; - if (traitId < staticTraits.size() + instanceTraits.size()) { - traitId -= staticTraits.size(); - if (instanceTraits.get(traitId) instanceof TraitMethodGetterSetter) { - return ((TraitMethodGetterSetter) instanceTraits.get(traitId)).method_info; - } else { - return -1; - } - } else { - traitId -= staticTraits.size() + instanceTraits.size(); - if (traitId == 0) { - return instance_info.get(classIndex).iinit_index; - } else if (traitId == 1) { - return class_info.get(classIndex).cinit_index; - } else { - return -1; - } - } - } - } - - private Map getNamespaceMap() { - if (namespaceMap == null) { - Map map = new HashMap<>(); - for (ScriptInfo si : script_info) { - for (Trait t : si.traits.traits) { - if (t instanceof TraitSlotConst) { - TraitSlotConst s = ((TraitSlotConst) t); - if (s.isNamespace()) { - String key = constants.getNamespace(s.value_index).getName(constants, true); // assume not null - String val = constants.getMultiname(s.name_index).getNameWithNamespace(constants, true); - map.put(key, val); - } - } - } - } - namespaceMap = map; - } - - return namespaceMap; - } - - private AVM2Deobfuscation getDeobfuscation() { - if (deobfuscation == null) { - deobfuscation = new AVM2Deobfuscation(constants); - } - - return deobfuscation; - } - - private Map getBodyIdxFromMethodIdx() { - if (bodyIdxFromMethodIdx == null) { - Map map = new HashMap<>(bodies.size()); - for (int i = 0; i < bodies.size(); i++) { - MethodBody mb = bodies.get(i); - map.put(mb.method_info, i); - } - - bodyIdxFromMethodIdx = map; - } - - return bodyIdxFromMethodIdx; - } - - public String nsValueToName(String value) { - if (getNamespaceMap().containsKey(value)) { - return getNamespaceMap().get(value); - } else { - String ns = getDeobfuscation().builtInNs(value); - if (ns == null) { - return ""; - } else { - return ns; - } - } - } - - public List getScriptPacks(String packagePrefix) { - List ret = new ArrayList<>(); - for (int i = 0; i < script_info.size(); i++) { - ret.addAll(script_info.get(i).getPacks(this, i, packagePrefix)); - } - return ret; - } - - public void dump(OutputStream os) { - Utf8PrintWriter output; - output = new Utf8PrintWriter(os); - constants.dump(output); - for (int i = 0; i < method_info.size(); i++) { - output.println("MethodInfo[" + i + "]:" + method_info.get(i).toString(constants, new ArrayList())); - } - for (int i = 0; i < metadata_info.size(); i++) { - output.println("MetadataInfo[" + i + "]:" + metadata_info.get(i).toString(constants)); - } - for (int i = 0; i < instance_info.size(); i++) { - output.println("InstanceInfo[" + i + "]:" + instance_info.get(i).toString(this, new ArrayList())); - } - for (int i = 0; i < class_info.size(); i++) { - output.println("ClassInfo[" + i + "]:" + class_info.get(i).toString(this, new ArrayList())); - } - for (int i = 0; i < script_info.size(); i++) { - output.println("ScriptInfo[" + i + "]:" + script_info.get(i).toString(this, new ArrayList())); - } - for (int i = 0; i < bodies.size(); i++) { - output.println("MethodBody[" + i + "]:"); //+ bodies[i].toString(this, constants, method_info)); - } - } - - private void checkMultinameUsedInMethod(int multinameIndex, int methodInfo, List ret, int classIndex, int traitIndex, boolean isStatic, boolean isInitializer, Traits traits, int parentTraitIndex) { - for (int p = 0; p < method_info.get(methodInfo).param_types.length; p++) { - if (method_info.get(methodInfo).param_types[p] == multinameIndex) { - ret.add(new MethodParamsMultinameUsage(this, multinameIndex, classIndex, traitIndex, isStatic, isInitializer, traits, parentTraitIndex)); - break; - } - } - if (method_info.get(methodInfo).ret_type == multinameIndex) { - ret.add(new MethodReturnTypeMultinameUsage(this, multinameIndex, classIndex, traitIndex, isStatic, isInitializer, traits, parentTraitIndex)); - } - MethodBody body = findBody(methodInfo); - if (body != null) { - findMultinameUsageInTraits(body.traits, multinameIndex, isStatic, classIndex, ret, traitIndex); - for (ABCException e : body.exceptions) { - if ((e.name_index == multinameIndex) || (e.type_index == multinameIndex)) { - ret.add(new MethodBodyMultinameUsage(this, multinameIndex, classIndex, traitIndex, isStatic, isInitializer, traits, parentTraitIndex)); - return; - } - } - for (AVM2Instruction ins : body.getCode().code) { - for (int o = 0; o < ins.definition.operands.length; o++) { - if (ins.definition.operands[o] == AVM2Code.DAT_MULTINAME_INDEX) { - if (ins.operands[o] == multinameIndex) { - ret.add(new MethodBodyMultinameUsage(this, multinameIndex, classIndex, traitIndex, isStatic, isInitializer, traits, parentTraitIndex)); - return; - } - } - } - } - } - } - - private void findMultinameUsageInTraits(Traits traits, int multinameIndex, boolean isStatic, int classIndex, List ret, int parentTraitIndex) { - for (int t = 0; t < traits.traits.size(); t++) { - if (traits.traits.get(t) instanceof TraitSlotConst) { - TraitSlotConst tsc = (TraitSlotConst) traits.traits.get(t); - if (tsc.name_index == multinameIndex) { - ret.add(new ConstVarNameMultinameUsage(this, multinameIndex, classIndex, t, isStatic, traits, parentTraitIndex)); - } - if (tsc.type_index == multinameIndex) { - ret.add(new ConstVarTypeMultinameUsage(this, multinameIndex, classIndex, t, isStatic, traits, parentTraitIndex)); - } - } - if (traits.traits.get(t) instanceof TraitMethodGetterSetter) { - TraitMethodGetterSetter tmgs = (TraitMethodGetterSetter) traits.traits.get(t); - if (tmgs.name_index == multinameIndex) { - ret.add(new MethodNameMultinameUsage(this, multinameIndex, classIndex, t, isStatic, false, traits, parentTraitIndex)); - } - checkMultinameUsedInMethod(multinameIndex, tmgs.method_info, ret, classIndex, t, isStatic, false, traits, parentTraitIndex); - } - } - } - - public List findMultinameDefinition(int multinameIndex) { - List usages = findMultinameUsage(multinameIndex); - List ret = new ArrayList<>(); - for (MultinameUsage u : usages) { - if (u instanceof DefinitionUsage) { - ret.add(u); - } - } - return ret; - } - - public List findMultinameUsage(int multinameIndex) { - List ret = new ArrayList<>(); - if (multinameIndex == 0) { - return ret; - } - for (int c = 0; c < instance_info.size(); c++) { - if (instance_info.get(c).name_index == multinameIndex) { - ret.add(new ClassNameMultinameUsage(this, multinameIndex, c)); - } - if (instance_info.get(c).super_index == multinameIndex) { - ret.add(new ExtendsMultinameUsage(this, multinameIndex, c)); - } - for (int i = 0; i < instance_info.get(c).interfaces.length; i++) { - if (instance_info.get(c).interfaces[i] == multinameIndex) { - ret.add(new ImplementsMultinameUsage(this, multinameIndex, c)); - } - } - checkMultinameUsedInMethod(multinameIndex, instance_info.get(c).iinit_index, ret, c, 0, false, true, null, -1); - checkMultinameUsedInMethod(multinameIndex, class_info.get(c).cinit_index, ret, c, 0, true, true, null, -1); - findMultinameUsageInTraits(instance_info.get(c).instance_traits, multinameIndex, false, c, ret, -1); - findMultinameUsageInTraits(class_info.get(c).static_traits, multinameIndex, true, c, ret, -1); - } - loopm: - for (int m = 1; m < constants.getMultinameCount(); m++) { - if (constants.getMultiname(m).kind == Multiname.TYPENAME) { - if (constants.getMultiname(m).qname_index == multinameIndex) { - ret.add(new TypeNameMultinameUsage(this, m)); - continue; - } - for (int mp : constants.getMultiname(m).params) { - if (mp == multinameIndex) { - ret.add(new TypeNameMultinameUsage(this, m)); - continue loopm; - } - } - } - } - return ret; - } - - public int findMethodInfoByName(int classId, String methodName) { - if (classId > -1) { - for (Trait t : instance_info.get(classId).instance_traits.traits) { - if (t instanceof TraitMethodGetterSetter) { - if (t.getName(this).getName(constants, new ArrayList(), true).equals(methodName)) { - return ((TraitMethodGetterSetter) t).method_info; - } - } - } - } - return -1; - } - - public int findMethodBodyByName(int classId, String methodName) { - if (classId > -1) { - for (Trait t : instance_info.get(classId).instance_traits.traits) { - if (t instanceof TraitMethodGetterSetter) { - if (t.getName(this).getName(constants, new ArrayList(), true).equals(methodName)) { - return findBodyIndex(((TraitMethodGetterSetter) t).method_info); - } - } - } - } - return -1; - } - - public int findMethodBodyByName(String className, String methodName) { - int classId = findClassByName(className); - return findMethodBodyByName(classId, methodName); - } - - public int findClassByName(String name) { - for (int c = 0; c < instance_info.size(); c++) { - String s = constants.getMultiname(instance_info.get(c).name_index).getNameWithNamespace(constants, true); - if (name.equals(s)) { - return c; - } - } - return -1; - } - - public List findScriptPacksByPath(String name) { - List ret = new ArrayList<>(); - List allPacks = getScriptPacks(null); // todo: honfika: use filter parameter - if (name.endsWith(".**") || name.equals("**") || name.endsWith(".++") || name.equals("++")) { - name = name.substring(0, name.length() - 2); - - for (ScriptPack en : allPacks) { - if (en.getClassPath().toString().startsWith(name)) { - ret.add(en); - } - } - } else if (name.endsWith(".*") || name.equals("*") || name.endsWith(".+") || name.equals("+")) { - name = name.substring(0, name.length() - 1); - for (ScriptPack en : allPacks) { - String classPathStr = en.getClassPath().toString(); - if (classPathStr.startsWith(name)) { - String rem = name.isEmpty() ? classPathStr : classPathStr.substring(name.length()); - if (!rem.contains(".")) { - ret.add(en); - } - } - } - } else { - ScriptPack p = findScriptPackByPath(name); - if (p != null) { - ret.add(p); - } - } - return ret; - - } - - public ScriptPack findScriptPackByPath(String name) { - List packs = getScriptPacks(null); - for (ScriptPack en : packs) { - if (en.getClassPath().toString().equals(name)) { - return en; - } - } - return null; - } - - private void removeClassFromTraits(Traits traits, int index) { - for (Trait t : traits.traits) { - if (t instanceof TraitClass) { - TraitClass tc = (TraitClass) t; - removeClassFromTraits(instance_info.get(tc.class_info).instance_traits, index); - removeClassFromTraits(class_info.get(tc.class_info).static_traits, index); - if (tc.class_info > index) { - tc.class_info--; - } - } - } - } - - public void addClass(ClassInfo ci, InstanceInfo ii, int index) { - for (MethodBody b : bodies) { - for (AVM2Instruction ins : b.getCode().code) { - for (int i = 0; i < ins.definition.operands.length; i++) { - if (ins.definition.operands[i] == AVM2Code.DAT_CLASS_INDEX) { - if (ins.operands[i] >= index) { - ins.operands[i]++; - } - } - } - } - } - for (ScriptInfo si : script_info) { - addClassInTraits(si.traits, index); - } - for (MethodBody b : bodies) { - addClassInTraits(b.traits, index); - } - instance_info.add(index, ii); - class_info.add(index, ci); - } - - private void addClassInTraits(Traits traits, int index) { - for (Trait t : traits.traits) { - if (t instanceof TraitClass) { - TraitClass tc = (TraitClass) t; - addClassInTraits(instance_info.get(tc.class_info).instance_traits, index); - addClassInTraits(class_info.get(tc.class_info).static_traits, index); - if (tc.class_info >= index) { - tc.class_info++; - } - } - } - } - - public void removeClass(int index) { - for (MethodBody b : bodies) { - for (AVM2Instruction ins : b.getCode().code) { - for (int i = 0; i < ins.definition.operands.length; i++) { - if (ins.definition.operands[i] == AVM2Code.DAT_CLASS_INDEX) { - if (ins.operands[i] > index) { - ins.operands[i]--; - } - } - } - } - } - for (ScriptInfo si : script_info) { - removeClassFromTraits(si.traits, index); - } - for (MethodBody b : bodies) { - removeClassFromTraits(b.traits, index); - } - instance_info.remove(index); - class_info.remove(index); - } - - private void removeMethodFromTraits(Traits traits, int index) { - for (Trait t : traits.traits) { - if (t instanceof TraitClass) { - TraitClass tc = (TraitClass) t; - removeMethodFromTraits(instance_info.get(tc.class_info).instance_traits, index); - removeMethodFromTraits(class_info.get(tc.class_info).static_traits, index); - } - if (t instanceof TraitMethodGetterSetter) { - TraitMethodGetterSetter tmgs = (TraitMethodGetterSetter) t; - if (tmgs.method_info > index) { - tmgs.method_info--; - } - } - if (t instanceof TraitFunction) { - TraitFunction tf = (TraitFunction) t; - if (tf.method_info > index) { - tf.method_info--; - } - } - } - } - - public void removeMethod(int index) { - - int bindex = -1; - for (int b = 0; b < bodies.size(); b++) { - if (bodies.get(b).method_info == index) { - bodies.remove(b); - bindex = b; - b--; - } - } - - for (MethodBody b : bodies) { - if (b.method_info > index) { - b.method_info--; - } - for (AVM2Instruction ins : b.getCode().code) { - for (int i = 0; i < ins.definition.operands.length; i++) { - if (ins.definition.operands[i] == AVM2Code.DAT_METHOD_INDEX) { - if (ins.operands[i] > index) { - ins.operands[i]--; - } - } - } - } - removeMethodFromTraits(b.traits, index); - } - - for (int c = 0; c < instance_info.size(); c++) { - InstanceInfo ii = instance_info.get(c); - if (ii.iinit_index > index) { - ii.iinit_index--; - } - ClassInfo ci = class_info.get(c); - if (ci.cinit_index > index) { - ci.cinit_index--; - } - } - - for (ScriptInfo si : script_info) { - if (si.init_index > index) { - si.init_index--; - } - removeMethodFromTraits(si.traits, index); - } - - bodyIdxFromMethodIdx = null; - - method_info.remove(index); - } - - public void replaceScriptPack(ScriptPack pack, String as) throws AVM2ParseException, CompilationException, IOException, InterruptedException { - String scriptName = pack.getPathScriptName() + ".as"; - int oldIndex = pack.scriptIndex; - int newIndex = script_info.size(); - String documentClass = getSwf().getDocumentClass(); - boolean isDocumentClass = documentClass != null && documentClass.equals(pack.getClassPath().toString()); - - ScriptInfo si = script_info.get(oldIndex); - si.delete(this, true); - int newClassIndex = instance_info.size(); - for (Trait t : si.traits.traits) { - if (t instanceof TraitClass) { - TraitClass tc = (TraitClass) t; - newClassIndex = tc.class_info + 1; - } - } - ActionScriptParser.compile(as, this, new ArrayList(), isDocumentClass, scriptName, newClassIndex); - // Move newly added script to its position - script_info.set(oldIndex, script_info.get(newIndex)); - script_info.remove(newIndex); - script_info.get(oldIndex).setModified(true); - pack(); // removes old classes/methods - ((Tag) parentTag).setModified(true); - } - - public void pack() { - for (int c = 0; c < instance_info.size(); c++) { - if (instance_info.get(c).deleted) { - removeClass(c); - c--; - } - } - for (int m = 0; m < method_info.size(); m++) { - if (method_info.get(m).deleted) { - removeMethod(m); - m--; - } - } - } -} +/* + * Copyright (C) 2010-2015 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash.abc; + +import com.jpexs.decompiler.flash.EventListener; +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; +import com.jpexs.decompiler.flash.abc.avm2.AVM2ConstantPool; +import com.jpexs.decompiler.flash.abc.avm2.AVM2Deobfuscation; +import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; +import com.jpexs.decompiler.flash.abc.avm2.instructions.executing.CallPropertyIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushStringIns; +import com.jpexs.decompiler.flash.abc.avm2.parser.AVM2ParseException; +import com.jpexs.decompiler.flash.abc.avm2.parser.script.ActionScriptParser; +import com.jpexs.decompiler.flash.abc.types.ABCException; +import com.jpexs.decompiler.flash.abc.types.ClassInfo; +import com.jpexs.decompiler.flash.abc.types.InstanceInfo; +import com.jpexs.decompiler.flash.abc.types.MetadataInfo; +import com.jpexs.decompiler.flash.abc.types.MethodBody; +import com.jpexs.decompiler.flash.abc.types.MethodInfo; +import com.jpexs.decompiler.flash.abc.types.Multiname; +import com.jpexs.decompiler.flash.abc.types.Namespace; +import com.jpexs.decompiler.flash.abc.types.NamespaceSet; +import com.jpexs.decompiler.flash.abc.types.ScriptInfo; +import com.jpexs.decompiler.flash.abc.types.traits.Trait; +import com.jpexs.decompiler.flash.abc.types.traits.TraitClass; +import com.jpexs.decompiler.flash.abc.types.traits.TraitFunction; +import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter; +import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst; +import com.jpexs.decompiler.flash.abc.types.traits.Traits; +import com.jpexs.decompiler.flash.abc.usages.ClassNameMultinameUsage; +import com.jpexs.decompiler.flash.abc.usages.ConstVarNameMultinameUsage; +import com.jpexs.decompiler.flash.abc.usages.ConstVarTypeMultinameUsage; +import com.jpexs.decompiler.flash.abc.usages.DefinitionUsage; +import com.jpexs.decompiler.flash.abc.usages.ExtendsMultinameUsage; +import com.jpexs.decompiler.flash.abc.usages.ImplementsMultinameUsage; +import com.jpexs.decompiler.flash.abc.usages.MethodBodyMultinameUsage; +import com.jpexs.decompiler.flash.abc.usages.MethodNameMultinameUsage; +import com.jpexs.decompiler.flash.abc.usages.MethodParamsMultinameUsage; +import com.jpexs.decompiler.flash.abc.usages.MethodReturnTypeMultinameUsage; +import com.jpexs.decompiler.flash.abc.usages.MultinameUsage; +import com.jpexs.decompiler.flash.abc.usages.TypeNameMultinameUsage; +import com.jpexs.decompiler.flash.helpers.SWFDecompilerPlugin; +import com.jpexs.decompiler.flash.tags.ABCContainerTag; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.types.annotations.Internal; +import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.helpers.utf8.Utf8PrintWriter; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class ABC { + + public int major_version = 46; + + public int minor_version = 16; + + public AVM2ConstantPool constants = new AVM2ConstantPool(); + + public List method_info = new ArrayList<>(); + + public List metadata_info = new ArrayList<>(); + + public List instance_info = new ArrayList<>(); + + public List class_info = new ArrayList<>(); + + public List script_info = new ArrayList<>(); + + public List bodies = new ArrayList<>(); + + private Map bodyIdxFromMethodIdx; + + private long[] stringOffsets; + + public static final int MINORwithDECIMAL = 17; + + protected Set listeners = new HashSet<>(); + + private static final Logger logger = Logger.getLogger(ABC.class.getName()); + + private AVM2Deobfuscation deobfuscation; + + @Internal + public ABCContainerTag parentTag; + + /* Map from multiname index of namespace value to namespace name**/ + private Map namespaceMap; + + public ABC(ABCContainerTag tag) { + this.parentTag = tag; + this.deobfuscation = null; + constants.constant_double.add(null); + constants.constant_int.add(null); + constants.constant_uint.add(null); + constants.constant_string.add(null); + constants.constant_multiname.add(null); + constants.constant_namespace.add(null); + constants.constant_namespace_set.add(null); + } + + public SWF getSwf() { + return parentTag.getSwf(); + } + + public List getAbcTags() { + return getSwf().getAbcList(); + } + + public int addMethodBody(MethodBody body) { + bodies.add(body); + bodyIdxFromMethodIdx = null; + return bodies.size() - 1; + } + + public int addMethodInfo(MethodInfo mi) { + method_info.add(mi); + return method_info.size() - 1; + } + + public void addEventListener(EventListener listener) { + listeners.add(listener); + } + + public void removeEventListener(EventListener listener) { + listeners.remove(listener); + } + + protected void informListeners(String event, Object data) { + for (EventListener listener : listeners) { + listener.handleEvent(event, data); + } + } + + public int removeTraps() throws InterruptedException { + int rem = 0; + for (int s = 0; s < script_info.size(); s++) { + rem += script_info.get(s).removeTraps(s, this, ""); + } + return rem; + } + + public int removeDeadCode() throws InterruptedException { + int rem = 0; + for (MethodBody body : bodies) { + rem += body.removeDeadCode(constants, null/*FIXME*/, method_info.get(body.method_info)); + } + return rem; + } + + public void restoreControlFlow() throws InterruptedException { + for (MethodBody body : bodies) { + body.restoreControlFlow(constants, null/*FIXME*/, method_info.get(body.method_info)); + } + } + + public Set getNsStringUsages() { + Set ret = new HashSet<>(); + for (int n = 1; n < constants.getNamespaceCount(); n++) { + ret.add(constants.getNamespace(n).name_index); + } + return ret; + } + + public Set getStringUsages() { + Set ret = new HashSet<>(); + for (MethodBody body : bodies) { + for (AVM2Instruction ins : body.getCode().code) { + for (int i = 0; i < ins.definition.operands.length; i++) { + if (ins.definition.operands[i] == AVM2Code.DAT_STRING_INDEX) { + ret.add(ins.operands[i]); + } + } + } + } + return ret; + } + + private void setStringUsageType(Map ret, int strIndex, String usageType) { + if (ret.containsKey(strIndex)) { + if (!"name".equals(usageType)) { + if (!ret.get(strIndex).equals(usageType)) { + ret.put(strIndex, "name"); + } + } + } else { + ret.put(strIndex, usageType); + } + } + + private void getStringUsageTypes(Map ret, Traits traits, boolean classesOnly) { + for (Trait t : traits.traits) { + int strIndex = constants.getMultiname(t.name_index).name_index; + String usageType = ""; + if (t instanceof TraitClass) { + TraitClass tc = (TraitClass) t; + getStringUsageTypes(ret, class_info.get(tc.class_info).static_traits, classesOnly); + getStringUsageTypes(ret, instance_info.get(tc.class_info).instance_traits, classesOnly); + + if (instance_info.get(tc.class_info).name_index != 0) { + setStringUsageType(ret, constants.getMultiname(instance_info.get(tc.class_info).name_index).name_index, "class"); + } + if (instance_info.get(tc.class_info).super_index != 0) { + setStringUsageType(ret, constants.getMultiname(instance_info.get(tc.class_info).super_index).name_index, "class"); + } + + usageType = "class"; + } + if (t instanceof TraitMethodGetterSetter) { + TraitMethodGetterSetter tm = (TraitMethodGetterSetter) t; + usageType = "method"; + MethodBody body = findBody(tm.method_info); + if (body != null) { + getStringUsageTypes(ret, body.traits, classesOnly); + } + } + if (t instanceof TraitFunction) { + TraitFunction tf = (TraitFunction) t; + MethodBody body = findBody(tf.method_info); + if (body != null) { + getStringUsageTypes(ret, body.traits, classesOnly); + } + usageType = "function"; + } + if (t instanceof TraitSlotConst) { + TraitSlotConst ts = (TraitSlotConst) t; + if (ts.isVar()) { + usageType = "var"; + } + if (ts.isConst()) { + usageType = "const"; + } + } + if (usageType.equals("class") || (!classesOnly)) { + setStringUsageType(ret, strIndex, usageType); + } + } + } + + public void getStringUsageTypes(Map ret, boolean classesOnly) { + for (ScriptInfo script : script_info) { + getStringUsageTypes(ret, script.traits, classesOnly); + } + } + + public void renameMultiname(int multinameIndex, String newname) { + if (multinameIndex <= 0 || multinameIndex >= constants.getMultinameCount()) { + throw new IllegalArgumentException("Multiname with index " + multinameIndex + " does not exist"); + } + Set stringUsages = getStringUsages(); + Set namespaceUsages = getNsStringUsages(); + int strIndex = constants.getMultiname(multinameIndex).name_index; + if (stringUsages.contains(strIndex) || namespaceUsages.contains(strIndex)) { // name is used elsewhere as string literal + strIndex = constants.getStringId(newname, true); + constants.getMultiname(multinameIndex).name_index = strIndex; + } else { + constants.setString(strIndex, newname); + } + } + + public void deobfuscateIdentifiers(HashMap namesMap, RenameType renameType, boolean classesOnly) { + Set stringUsages = getStringUsages(); + Set namespaceUsages = getNsStringUsages(); + Map stringUsageTypes = new HashMap<>(); + informListeners("deobfuscate", "Getting usage types..."); + getStringUsageTypes(stringUsageTypes, classesOnly); + AVM2Deobfuscation deobfuscation = getDeobfuscation(); + for (int i = 0; i < instance_info.size(); i++) { + informListeners("deobfuscate", "class " + i + "/" + instance_info.size()); + InstanceInfo insti = instance_info.get(i); + if (insti.name_index != 0) { + constants.getMultiname(insti.name_index).name_index = deobfuscation.deobfuscateName(stringUsageTypes, stringUsages, namespaceUsages, namesMap, constants.getMultiname(insti.name_index).name_index, true, renameType); + if (constants.getMultiname(insti.name_index).namespace_index != 0) { + constants.getNamespace(constants.getMultiname(insti.name_index).namespace_index).name_index + = deobfuscation.deobfuscatePackageName(stringUsageTypes, stringUsages, namesMap, constants.getNamespace(constants.getMultiname(insti.name_index).namespace_index).name_index, renameType); + } + } + if (insti.super_index != 0) { + constants.getMultiname(insti.super_index).name_index = deobfuscation.deobfuscateName(stringUsageTypes, stringUsages, namespaceUsages, namesMap, constants.getMultiname(insti.super_index).name_index, true, renameType); + } + } + if (classesOnly) { + return; + } + for (int i = 1; i < constants.getMultinameCount(); i++) { + informListeners("deobfuscate", "name " + i + "/" + constants.getMultinameCount()); + constants.getMultiname(i).name_index = deobfuscation.deobfuscateName(stringUsageTypes, stringUsages, namespaceUsages, namesMap, constants.getMultiname(i).name_index, false, renameType); + } + for (int i = 1; i < constants.getNamespaceCount(); i++) { + informListeners("deobfuscate", "namespace " + i + "/" + constants.getNamespaceCount()); + if (constants.getNamespace(i).kind != Namespace.KIND_PACKAGE) { // only packages + continue; + } + constants.getNamespace(i).name_index = deobfuscation.deobfuscatePackageName(stringUsageTypes, stringUsages, namesMap, constants.getNamespace(i).name_index, renameType); + } + + // process reflection using getDefinitionByName too + for (MethodBody body : bodies) { + for (int ip = 0; ip < body.getCode().code.size(); ip++) { + if (body.getCode().code.get(ip).definition instanceof CallPropertyIns) { + int mIndex = body.getCode().code.get(ip).operands[0]; + if (mIndex > 0) { + Multiname m = constants.getMultiname(mIndex); + if (m.getNameWithNamespace(constants, true).equals("flash.utils.getDefinitionByName")) { + if (ip > 0) { + if (body.getCode().code.get(ip - 1).definition instanceof PushStringIns) { + int strIndex = body.getCode().code.get(ip - 1).operands[0]; + String fullname = constants.getString(strIndex); + String pkg = ""; + String name = fullname; + if (fullname.contains(".")) { + pkg = fullname.substring(0, fullname.lastIndexOf('.')); + name = fullname.substring(fullname.lastIndexOf('.') + 1); + } + if (!pkg.isEmpty()) { + int pkgStrIndex = constants.getStringId(pkg, true); + pkgStrIndex = deobfuscation.deobfuscatePackageName(stringUsageTypes, stringUsages, namesMap, pkgStrIndex, renameType); + pkg = constants.getString(pkgStrIndex); + } + int nameStrIndex = constants.getStringId(name, true); + nameStrIndex = deobfuscation.deobfuscateName(stringUsageTypes, stringUsages, namespaceUsages, namesMap, nameStrIndex, true, renameType); + name = constants.getString(nameStrIndex); + String fullChanged = ""; + if (!pkg.isEmpty()) { + fullChanged = pkg + "."; + } + fullChanged += name; + strIndex = constants.getStringId(fullChanged, true); + body.getCode().code.get(ip - 1).operands[0] = strIndex; + } + } + } + } + } + } + } + } + + public ABC(ABCInputStream ais, SWF swf, ABCContainerTag tag) throws IOException { + this.parentTag = tag; + minor_version = ais.readU16("minor_version"); + major_version = ais.readU16("major_version"); + logger.log(Level.FINE, "ABC minor_version: {0}, major_version: {1}", new Object[]{minor_version, major_version}); + + constants = new AVM2ConstantPool(); + ais.newDumpLevel("constant_pool", "cpool_info"); + + // constant integers + int constant_int_pool_count = ais.readU30("int_count"); + constants.constant_int = new ArrayList<>(constant_int_pool_count); + if (constant_int_pool_count > 0) { + constants.addInt(0); + } + if (constant_int_pool_count > 1) { + ais.newDumpLevel("integers", "integer[]"); + for (int i = 1; i < constant_int_pool_count; i++) { // index 0 not used. Values 1..n-1 + constants.addInt(ais.readS32("int")); + } + ais.endDumpLevel(); + } + + // constant unsigned integers + int constant_uint_pool_count = ais.readU30("uint_count"); + constants.constant_uint = new ArrayList<>(constant_uint_pool_count); + if (constant_uint_pool_count > 0) { + constants.addUInt(0); + } + if (constant_uint_pool_count > 1) { + ais.newDumpLevel("uintegers", "uinteger[]"); + for (int i = 1; i < constant_uint_pool_count; i++) { // index 0 not used. Values 1..n-1 + constants.addUInt(ais.readU32("uint")); + } + ais.endDumpLevel(); + } + + // constant double + int constant_double_pool_count = ais.readU30("double_count"); + constants.constant_double = new ArrayList<>(constant_double_pool_count); + if (constant_double_pool_count > 0) { + constants.addDouble(0); + } + if (constant_double_pool_count > 1) { + ais.newDumpLevel("doubles", "double[]"); + for (int i = 1; i < constant_double_pool_count; i++) { // index 0 not used. Values 1..n-1 + constants.addDouble(ais.readDouble("double")); + } + ais.endDumpLevel(); + } + + // constant decimal + if (minor_version >= MINORwithDECIMAL) { + int constant_decimal_pool_count = ais.readU30("decimal_count"); + constants.constant_decimal = new ArrayList<>(constant_decimal_pool_count); + if (constant_decimal_pool_count > 0) { + constants.addDecimal(null); + } + if (constant_decimal_pool_count > 1) { + ais.newDumpLevel("decimals", "decimal[]"); + for (int i = 1; i < constant_decimal_pool_count; i++) { // index 0 not used. Values 1..n-1 + constants.addDecimal(ais.readDecimal("decimal")); + } + ais.endDumpLevel(); + } + } else { + constants.constant_decimal = new ArrayList<>(0); + } + + // constant string + int constant_string_pool_count = ais.readU30("string_count"); + constants.constant_string = new ArrayList<>(constant_string_pool_count); + stringOffsets = new long[constant_string_pool_count]; + if (constant_string_pool_count > 0) { + constants.addString(""); + } + if (constant_string_pool_count > 1) { + ais.newDumpLevel("strings", "string[]"); + for (int i = 1; i < constant_string_pool_count; i++) { // index 0 not used. Values 1..n-1 + long pos = ais.getPosition(); + constants.addString(ais.readString("string")); + stringOffsets[i] = pos; + } + ais.endDumpLevel(); + } + + // constant namespace + int constant_namespace_pool_count = ais.readU30("namespace_count"); + constants.constant_namespace = new ArrayList<>(constant_namespace_pool_count); + if (constant_namespace_pool_count > 0) { + constants.addNamespace(null); + } + if (constant_namespace_pool_count > 1) { + ais.newDumpLevel("namespaces", "namespace[]"); + for (int i = 1; i < constant_namespace_pool_count; i++) { // index 0 not used. Values 1..n-1 + constants.addNamespace(ais.readNamespace("namespace")); + } + ais.endDumpLevel(); + } + + // constant namespace set + int constant_namespace_set_pool_count = ais.readU30("ns_set_count"); + constants.constant_namespace_set = new ArrayList<>(constant_namespace_set_pool_count); + if (constant_namespace_set_pool_count > 0) { + constants.addNamespaceSet(null); + } + if (constant_namespace_set_pool_count > 1) { + ais.newDumpLevel("ns_sets", "ns_set[]"); + for (int i = 1; i < constant_namespace_set_pool_count; i++) { // index 0 not used. Values 1..n-1 + ais.newDumpLevel("ns_set_infos", "ns_set_info[]"); + constants.addNamespaceSet(new NamespaceSet()); + int namespace_count = ais.readU30("count"); + constants.getNamespaceSet(i).namespaces = new int[namespace_count]; + for (int j = 0; j < namespace_count; j++) { + constants.getNamespaceSet(i).namespaces[j] = ais.readU30("ns"); + } + ais.endDumpLevel(); + } + ais.endDumpLevel(); + } + + // constant multiname + int constant_multiname_pool_count = ais.readU30("multiname_count"); + constants.constant_multiname = new ArrayList<>(constant_multiname_pool_count); + if (constant_multiname_pool_count > 0) { + constants.addMultiname(null); + } + if (constant_multiname_pool_count > 1) { + ais.newDumpLevel("multiname", "multinames[]"); + for (int i = 1; i < constant_multiname_pool_count; i++) { // index 0 not used. Values 1..n-1 + constants.addMultiname(ais.readMultiname("multiname")); + } + ais.endDumpLevel(); + } + + ais.endDumpLevel(); // cpool_info + + // method info + int methods_count = ais.readU30("methods_count"); + method_info = new ArrayList<>(methods_count); // MethodInfo[methods_count]; + for (int i = 0; i < methods_count; i++) { + method_info.add(ais.readMethodInfo("method")); + } + + // metadata info + int metadata_count = ais.readU30("metadata_count"); + metadata_info = new ArrayList<>(metadata_count); + for (int i = 0; i < metadata_count; i++) { + int name_index = ais.readU30("name_index"); + int values_count = ais.readU30("values_count"); + int[] keys = new int[values_count]; + for (int v = 0; v < values_count; v++) { + keys[v] = ais.readU30("key"); + } + int[] values = new int[values_count]; + for (int v = 0; v < values_count; v++) { + values[v] = ais.readU30("value"); + } + metadata_info.add(new MetadataInfo(name_index, keys, values)); + } + + int class_count = ais.readU30("class_count"); + instance_info = new ArrayList<>(class_count); + for (int i = 0; i < class_count; i++) { + instance_info.add(ais.readInstanceInfo("instance")); + } + class_info = new ArrayList<>(class_count); + for (int i = 0; i < class_count; i++) { + ais.newDumpLevel("class", "class_info"); + ClassInfo ci = new ClassInfo(null); // do not create Traits in constructor + ci.cinit_index = ais.readU30("cinit_index"); + ci.static_traits = ais.readTraits("static_traits"); + class_info.add(ci); + ais.endDumpLevel(); + } + int script_count = ais.readU30("script_count"); + script_info = new ArrayList<>(script_count); + for (int i = 0; i < script_count; i++) { + ais.newDumpLevel("script", "script_info"); + ScriptInfo si = new ScriptInfo(null); // do not create Traits in constructor + si.init_index = ais.readU30("init_index"); + si.traits = ais.readTraits("traits"); + script_info.add(si); + ais.endDumpLevel(); + si.setModified(false); + } + + int bodies_count = ais.readU30("bodies_count"); + bodies = new ArrayList<>(bodies_count); + for (int i = 0; i < bodies_count; i++) { + ais.newDumpLevel("method_body", "method_body_info"); + MethodBody mb = new MethodBody(null, null, null); // do not create Traits in constructor + mb.method_info = ais.readU30("method_info"); + mb.max_stack = ais.readU30("max_stack"); + mb.max_regs = ais.readU30("max_regs"); + mb.init_scope_depth = ais.readU30("init_scope_depth"); + mb.max_scope_depth = ais.readU30("max_scope_depth"); + int code_length = ais.readU30("code_length"); + mb.setCodeBytes(ais.readBytes(code_length, "code")); + int ex_count = ais.readU30("ex_count"); + mb.exceptions = new ABCException[ex_count]; + for (int j = 0; j < ex_count; j++) { + ABCException abce = new ABCException(); + abce.start = ais.readU30("start"); + abce.end = ais.readU30("end"); + abce.target = ais.readU30("target"); + abce.type_index = ais.readU30("type_index"); + abce.name_index = ais.readU30("name_index"); + mb.exceptions[j] = abce; + } + mb.traits = ais.readTraits("traits"); + bodies.add(mb); + method_info.get(mb.method_info).setBody(mb); + ais.endDumpLevel(); + + SWFDecompilerPlugin.fireMethodBodyParsed(mb, swf); + } + + /*for(int i=0;i()); + } catch (InterruptedException ex) { + Logger.getLogger(ABC.class.getName()).log(Level.SEVERE, null, ex); + } + System.out.println(""+t.toString()); + } + //System.exit(0);*/ + SWFDecompilerPlugin.fireAbcParsed(this, swf); + } + + public void saveToStream(OutputStream os) throws IOException { + ABCOutputStream aos = new ABCOutputStream(os); + aos.writeU16(minor_version); + aos.writeU16(major_version); + + aos.writeU30(constants.getIntCount()); + for (int i = 1; i < constants.getIntCount(); i++) { + aos.writeS32(constants.getInt(i)); + } + aos.writeU30(constants.getUIntCount()); + for (int i = 1; i < constants.getUIntCount(); i++) { + aos.writeU32(constants.getUInt(i)); + } + + aos.writeU30(constants.getDoubleCount()); + for (int i = 1; i < constants.getDoubleCount(); i++) { + aos.writeDouble(constants.getDouble(i)); + } + + if (minor_version >= MINORwithDECIMAL) { + aos.writeU30(constants.getDecimalCount()); + for (int i = 1; i < constants.getDecimalCount(); i++) { + aos.writeDecimal(constants.getDecimal(i)); + } + } + + aos.writeU30(constants.getStringCount()); + for (int i = 1; i < constants.getStringCount(); i++) { + aos.writeString(constants.getString(i)); + } + + aos.writeU30(constants.getNamespaceCount()); + for (int i = 1; i < constants.getNamespaceCount(); i++) { + aos.writeNamespace(constants.getNamespace(i)); + } + + aos.writeU30(constants.getNamespaceSetCount()); + for (int i = 1; i < constants.getNamespaceSetCount(); i++) { + aos.writeU30(constants.getNamespaceSet(i).namespaces.length); + for (int j = 0; j < constants.getNamespaceSet(i).namespaces.length; j++) { + aos.writeU30(constants.getNamespaceSet(i).namespaces[j]); + } + } + + aos.writeU30(constants.getMultinameCount()); + for (int i = 1; i < constants.getMultinameCount(); i++) { + aos.writeMultiname(constants.getMultiname(i)); + } + + aos.writeU30(method_info.size()); + for (MethodInfo mi : method_info) { + aos.writeMethodInfo(mi); + } + + aos.writeU30(metadata_info.size()); + for (MetadataInfo mi : metadata_info) { + aos.writeU30(mi.name_index); + aos.writeU30(mi.values.length); + for (int j = 0; j < mi.values.length; j++) { + aos.writeU30(mi.keys[j]); + } + for (int j = 0; j < mi.values.length; j++) { + aos.writeU30(mi.values[j]); + } + } + + aos.writeU30(class_info.size()); + for (InstanceInfo ii : instance_info) { + aos.writeInstanceInfo(ii); + } + for (ClassInfo ci : class_info) { + aos.writeU30(ci.cinit_index); + aos.writeTraits(ci.static_traits); + } + aos.writeU30(script_info.size()); + for (ScriptInfo si : script_info) { + aos.writeU30(si.init_index); + aos.writeTraits(si.traits); + } + + aos.writeU30(bodies.size()); + for (MethodBody mb : bodies) { + aos.writeU30(mb.method_info); + aos.writeU30(mb.max_stack); + aos.writeU30(mb.max_regs); + aos.writeU30(mb.init_scope_depth); + aos.writeU30(mb.max_scope_depth); + byte[] codeBytes = mb.getCodeBytes(); + aos.writeU30(codeBytes.length); + aos.write(codeBytes); + aos.writeU30(mb.exceptions.length); + for (int j = 0; j < mb.exceptions.length; j++) { + aos.writeU30(mb.exceptions[j].start); + aos.writeU30(mb.exceptions[j].end); + aos.writeU30(mb.exceptions[j].target); + aos.writeU30(mb.exceptions[j].type_index); + aos.writeU30(mb.exceptions[j].name_index); + } + aos.writeTraits(mb.traits); + } + } + + public MethodBody findBody(int methodInfo) { + if (methodInfo < 0) { + return null; + } + return method_info.get(methodInfo).getBody(); + } + + public int findBodyIndex(int methodInfo) { + if (methodInfo == -1) { + return -1; + } + + Integer result = getBodyIdxFromMethodIdx().get(methodInfo); + if (result == null) { + return -1; + } + + return result; + } + + public MethodBody findBodyByClassAndName(String className, String methodName) { + for (int i = 0; i < instance_info.size(); i++) { + if (className.equals(constants.getMultiname(instance_info.get(i).name_index).getName(constants, new ArrayList(), true))) { + for (Trait t : instance_info.get(i).instance_traits.traits) { + if (t instanceof TraitMethodGetterSetter) { + TraitMethodGetterSetter t2 = (TraitMethodGetterSetter) t; + if (methodName.equals(t2.getName(this).getName(constants, new ArrayList(), true))) { + for (MethodBody body : bodies) { + if (body.method_info == t2.method_info) { + return body; + } + } + } + } + } + //break; + } + } + for (int i = 0; i < class_info.size(); i++) { + if (className.equals(constants.getMultiname(instance_info.get(i).name_index).getName(constants, new ArrayList(), true))) { + for (Trait t : class_info.get(i).static_traits.traits) { + if (t instanceof TraitMethodGetterSetter) { + TraitMethodGetterSetter t2 = (TraitMethodGetterSetter) t; + if (methodName.equals(t2.getName(this).getName(constants, new ArrayList(), true))) { + for (MethodBody body : bodies) { + if (body.method_info == t2.method_info) { + return body; + } + } + } + } + } + //break; + } + } + + return null; + } + + public boolean isStaticTraitId(int classIndex, int traitId) { + if (traitId < class_info.get(classIndex).static_traits.traits.size()) { + return true; + } else if (traitId < class_info.get(classIndex).static_traits.traits.size() + instance_info.get(classIndex).instance_traits.traits.size()) { + return false; + } else { + return true; // Can be class or instance initializer + } + } + + public Trait findTraitByTraitId(int classIndex, int traitId) { + if (classIndex == -1) { + return null; + } + List staticTraits = class_info.get(classIndex).static_traits.traits; + if (traitId < staticTraits.size()) { + return staticTraits.get(traitId); + } else { + List instanceTraits = instance_info.get(classIndex).instance_traits.traits; + if (traitId < staticTraits.size() + instanceTraits.size()) { + traitId -= staticTraits.size(); + return instanceTraits.get(traitId); + } else { + return null; // Can be class or instance initializer + } + } + } + + public int findMethodIdByTraitId(int classIndex, int traitId) { + if (classIndex == -1) { + return -1; + } + List staticTraits = class_info.get(classIndex).static_traits.traits; + if (traitId < staticTraits.size()) { + if (staticTraits.get(traitId) instanceof TraitMethodGetterSetter) { + return ((TraitMethodGetterSetter) staticTraits.get(traitId)).method_info; + } else { + return -1; + } + } else { + List instanceTraits = instance_info.get(classIndex).instance_traits.traits; + if (traitId < staticTraits.size() + instanceTraits.size()) { + traitId -= staticTraits.size(); + if (instanceTraits.get(traitId) instanceof TraitMethodGetterSetter) { + return ((TraitMethodGetterSetter) instanceTraits.get(traitId)).method_info; + } else { + return -1; + } + } else { + traitId -= staticTraits.size() + instanceTraits.size(); + if (traitId == 0) { + return instance_info.get(classIndex).iinit_index; + } else if (traitId == 1) { + return class_info.get(classIndex).cinit_index; + } else { + return -1; + } + } + } + } + + private Map getNamespaceMap() { + if (namespaceMap == null) { + Map map = new HashMap<>(); + for (ScriptInfo si : script_info) { + for (Trait t : si.traits.traits) { + if (t instanceof TraitSlotConst) { + TraitSlotConst s = ((TraitSlotConst) t); + if (s.isNamespace()) { + String key = constants.getNamespace(s.value_index).getName(constants, true); // assume not null + String val = constants.getMultiname(s.name_index).getNameWithNamespace(constants, true); + map.put(key, val); + } + } + } + } + namespaceMap = map; + } + + return namespaceMap; + } + + private AVM2Deobfuscation getDeobfuscation() { + if (deobfuscation == null) { + deobfuscation = new AVM2Deobfuscation(constants); + } + + return deobfuscation; + } + + private Map getBodyIdxFromMethodIdx() { + if (bodyIdxFromMethodIdx == null) { + Map map = new HashMap<>(bodies.size()); + for (int i = 0; i < bodies.size(); i++) { + MethodBody mb = bodies.get(i); + map.put(mb.method_info, i); + } + + bodyIdxFromMethodIdx = map; + } + + return bodyIdxFromMethodIdx; + } + + public String nsValueToName(String value) { + if (getNamespaceMap().containsKey(value)) { + return getNamespaceMap().get(value); + } else { + String ns = getDeobfuscation().builtInNs(value); + if (ns == null) { + return ""; + } else { + return ns; + } + } + } + + public List getScriptPacks(String packagePrefix, List allAbcs) { + List ret = new ArrayList<>(); + for (int i = 0; i < script_info.size(); i++) { + ret.addAll(script_info.get(i).getPacks(this, i, packagePrefix, allAbcs)); + } + return ret; + } + + public void dump(OutputStream os) { + Utf8PrintWriter output; + output = new Utf8PrintWriter(os); + constants.dump(output); + for (int i = 0; i < method_info.size(); i++) { + output.println("MethodInfo[" + i + "]:" + method_info.get(i).toString(constants, new ArrayList())); + } + for (int i = 0; i < metadata_info.size(); i++) { + output.println("MetadataInfo[" + i + "]:" + metadata_info.get(i).toString(constants)); + } + for (int i = 0; i < instance_info.size(); i++) { + output.println("InstanceInfo[" + i + "]:" + instance_info.get(i).toString(this, new ArrayList())); + } + for (int i = 0; i < class_info.size(); i++) { + output.println("ClassInfo[" + i + "]:" + class_info.get(i).toString(this, new ArrayList())); + } + for (int i = 0; i < script_info.size(); i++) { + output.println("ScriptInfo[" + i + "]:" + script_info.get(i).toString(this, new ArrayList())); + } + for (int i = 0; i < bodies.size(); i++) { + output.println("MethodBody[" + i + "]:"); //+ bodies[i].toString(this, constants, method_info)); + } + } + + private void checkMultinameUsedInMethod(int multinameIndex, int methodInfo, List ret, int classIndex, int traitIndex, boolean isStatic, boolean isInitializer, Traits traits, int parentTraitIndex) { + for (int p = 0; p < method_info.get(methodInfo).param_types.length; p++) { + if (method_info.get(methodInfo).param_types[p] == multinameIndex) { + ret.add(new MethodParamsMultinameUsage(this, multinameIndex, classIndex, traitIndex, isStatic, isInitializer, traits, parentTraitIndex)); + break; + } + } + if (method_info.get(methodInfo).ret_type == multinameIndex) { + ret.add(new MethodReturnTypeMultinameUsage(this, multinameIndex, classIndex, traitIndex, isStatic, isInitializer, traits, parentTraitIndex)); + } + MethodBody body = findBody(methodInfo); + if (body != null) { + findMultinameUsageInTraits(body.traits, multinameIndex, isStatic, classIndex, ret, traitIndex); + for (ABCException e : body.exceptions) { + if ((e.name_index == multinameIndex) || (e.type_index == multinameIndex)) { + ret.add(new MethodBodyMultinameUsage(this, multinameIndex, classIndex, traitIndex, isStatic, isInitializer, traits, parentTraitIndex)); + return; + } + } + for (AVM2Instruction ins : body.getCode().code) { + for (int o = 0; o < ins.definition.operands.length; o++) { + if (ins.definition.operands[o] == AVM2Code.DAT_MULTINAME_INDEX) { + if (ins.operands[o] == multinameIndex) { + ret.add(new MethodBodyMultinameUsage(this, multinameIndex, classIndex, traitIndex, isStatic, isInitializer, traits, parentTraitIndex)); + return; + } + } + } + } + } + } + + private void findMultinameUsageInTraits(Traits traits, int multinameIndex, boolean isStatic, int classIndex, List ret, int parentTraitIndex) { + for (int t = 0; t < traits.traits.size(); t++) { + if (traits.traits.get(t) instanceof TraitSlotConst) { + TraitSlotConst tsc = (TraitSlotConst) traits.traits.get(t); + if (tsc.name_index == multinameIndex) { + ret.add(new ConstVarNameMultinameUsage(this, multinameIndex, classIndex, t, isStatic, traits, parentTraitIndex)); + } + if (tsc.type_index == multinameIndex) { + ret.add(new ConstVarTypeMultinameUsage(this, multinameIndex, classIndex, t, isStatic, traits, parentTraitIndex)); + } + } + if (traits.traits.get(t) instanceof TraitMethodGetterSetter) { + TraitMethodGetterSetter tmgs = (TraitMethodGetterSetter) traits.traits.get(t); + if (tmgs.name_index == multinameIndex) { + ret.add(new MethodNameMultinameUsage(this, multinameIndex, classIndex, t, isStatic, false, traits, parentTraitIndex)); + } + checkMultinameUsedInMethod(multinameIndex, tmgs.method_info, ret, classIndex, t, isStatic, false, traits, parentTraitIndex); + } + } + } + + public List findMultinameDefinition(int multinameIndex) { + List usages = findMultinameUsage(multinameIndex); + List ret = new ArrayList<>(); + for (MultinameUsage u : usages) { + if (u instanceof DefinitionUsage) { + ret.add(u); + } + } + return ret; + } + + public List findMultinameUsage(int multinameIndex) { + List ret = new ArrayList<>(); + if (multinameIndex == 0) { + return ret; + } + for (int c = 0; c < instance_info.size(); c++) { + if (instance_info.get(c).name_index == multinameIndex) { + ret.add(new ClassNameMultinameUsage(this, multinameIndex, c)); + } + if (instance_info.get(c).super_index == multinameIndex) { + ret.add(new ExtendsMultinameUsage(this, multinameIndex, c)); + } + for (int i = 0; i < instance_info.get(c).interfaces.length; i++) { + if (instance_info.get(c).interfaces[i] == multinameIndex) { + ret.add(new ImplementsMultinameUsage(this, multinameIndex, c)); + } + } + checkMultinameUsedInMethod(multinameIndex, instance_info.get(c).iinit_index, ret, c, 0, false, true, null, -1); + checkMultinameUsedInMethod(multinameIndex, class_info.get(c).cinit_index, ret, c, 0, true, true, null, -1); + findMultinameUsageInTraits(instance_info.get(c).instance_traits, multinameIndex, false, c, ret, -1); + findMultinameUsageInTraits(class_info.get(c).static_traits, multinameIndex, true, c, ret, -1); + } + loopm: + for (int m = 1; m < constants.getMultinameCount(); m++) { + if (constants.getMultiname(m).kind == Multiname.TYPENAME) { + if (constants.getMultiname(m).qname_index == multinameIndex) { + ret.add(new TypeNameMultinameUsage(this, m)); + continue; + } + for (int mp : constants.getMultiname(m).params) { + if (mp == multinameIndex) { + ret.add(new TypeNameMultinameUsage(this, m)); + continue loopm; + } + } + } + } + return ret; + } + + public int findMethodInfoByName(int classId, String methodName) { + if (classId > -1) { + for (Trait t : instance_info.get(classId).instance_traits.traits) { + if (t instanceof TraitMethodGetterSetter) { + if (t.getName(this).getName(constants, new ArrayList(), true).equals(methodName)) { + return ((TraitMethodGetterSetter) t).method_info; + } + } + } + } + return -1; + } + + public int findMethodBodyByName(int classId, String methodName) { + if (classId > -1) { + for (Trait t : instance_info.get(classId).instance_traits.traits) { + if (t instanceof TraitMethodGetterSetter) { + if (t.getName(this).getName(constants, new ArrayList(), true).equals(methodName)) { + return findBodyIndex(((TraitMethodGetterSetter) t).method_info); + } + } + } + } + return -1; + } + + public int findMethodBodyByName(String className, String methodName) { + int classId = findClassByName(className); + return findMethodBodyByName(classId, methodName); + } + + public int findClassByName(String name) { + for (int c = 0; c < instance_info.size(); c++) { + String s = constants.getMultiname(instance_info.get(c).name_index).getNameWithNamespace(constants, true); + if (name.equals(s)) { + return c; + } + } + return -1; + } + + public List findScriptPacksByPath(String name, List allAbcs) { + List ret = new ArrayList<>(); + List allPacks = getScriptPacks(null, allAbcs); // todo: honfika: use filter parameter + if (name.endsWith(".**") || name.equals("**") || name.endsWith(".++") || name.equals("++")) { + name = name.substring(0, name.length() - 2); + + for (ScriptPack en : allPacks) { + if (en.getClassPath().toString().startsWith(name)) { + ret.add(en); + } + } + } else if (name.endsWith(".*") || name.equals("*") || name.endsWith(".+") || name.equals("+")) { + name = name.substring(0, name.length() - 1); + for (ScriptPack en : allPacks) { + String classPathStr = en.getClassPath().toString(); + if (classPathStr.startsWith(name)) { + String rem = name.isEmpty() ? classPathStr : classPathStr.substring(name.length()); + if (!rem.contains(".")) { + ret.add(en); + } + } + } + } else { + ScriptPack p = findScriptPackByPath(name, allAbcs); + if (p != null) { + ret.add(p); + } + } + return ret; + + } + + public ScriptPack findScriptPackByPath(String name, List allAbcs) { + List packs = getScriptPacks(null, allAbcs); + for (ScriptPack en : packs) { + if (en.getClassPath().toString().equals(name)) { + return en; + } + } + return null; + } + + private void removeClassFromTraits(Traits traits, int index) { + for (Trait t : traits.traits) { + if (t instanceof TraitClass) { + TraitClass tc = (TraitClass) t; + removeClassFromTraits(instance_info.get(tc.class_info).instance_traits, index); + removeClassFromTraits(class_info.get(tc.class_info).static_traits, index); + if (tc.class_info > index) { + tc.class_info--; + } + } + } + } + + public void addClass(ClassInfo ci, InstanceInfo ii, int index) { + for (MethodBody b : bodies) { + for (AVM2Instruction ins : b.getCode().code) { + for (int i = 0; i < ins.definition.operands.length; i++) { + if (ins.definition.operands[i] == AVM2Code.DAT_CLASS_INDEX) { + if (ins.operands[i] >= index) { + ins.operands[i]++; + } + } + } + } + } + for (ScriptInfo si : script_info) { + addClassInTraits(si.traits, index); + } + for (MethodBody b : bodies) { + addClassInTraits(b.traits, index); + } + instance_info.add(index, ii); + class_info.add(index, ci); + } + + private void addClassInTraits(Traits traits, int index) { + for (Trait t : traits.traits) { + if (t instanceof TraitClass) { + TraitClass tc = (TraitClass) t; + addClassInTraits(instance_info.get(tc.class_info).instance_traits, index); + addClassInTraits(class_info.get(tc.class_info).static_traits, index); + if (tc.class_info >= index) { + tc.class_info++; + } + } + } + } + + public void removeClass(int index) { + for (MethodBody b : bodies) { + for (AVM2Instruction ins : b.getCode().code) { + for (int i = 0; i < ins.definition.operands.length; i++) { + if (ins.definition.operands[i] == AVM2Code.DAT_CLASS_INDEX) { + if (ins.operands[i] > index) { + ins.operands[i]--; + } + } + } + } + } + for (ScriptInfo si : script_info) { + removeClassFromTraits(si.traits, index); + } + for (MethodBody b : bodies) { + removeClassFromTraits(b.traits, index); + } + instance_info.remove(index); + class_info.remove(index); + } + + private void removeMethodFromTraits(Traits traits, int index) { + for (Trait t : traits.traits) { + if (t instanceof TraitClass) { + TraitClass tc = (TraitClass) t; + removeMethodFromTraits(instance_info.get(tc.class_info).instance_traits, index); + removeMethodFromTraits(class_info.get(tc.class_info).static_traits, index); + } + if (t instanceof TraitMethodGetterSetter) { + TraitMethodGetterSetter tmgs = (TraitMethodGetterSetter) t; + if (tmgs.method_info > index) { + tmgs.method_info--; + } + } + if (t instanceof TraitFunction) { + TraitFunction tf = (TraitFunction) t; + if (tf.method_info > index) { + tf.method_info--; + } + } + } + } + + public void removeMethod(int index) { + + int bindex = -1; + for (int b = 0; b < bodies.size(); b++) { + if (bodies.get(b).method_info == index) { + bodies.remove(b); + bindex = b; + b--; + } + } + + for (MethodBody b : bodies) { + if (b.method_info > index) { + b.method_info--; + } + for (AVM2Instruction ins : b.getCode().code) { + for (int i = 0; i < ins.definition.operands.length; i++) { + if (ins.definition.operands[i] == AVM2Code.DAT_METHOD_INDEX) { + if (ins.operands[i] > index) { + ins.operands[i]--; + } + } + } + } + removeMethodFromTraits(b.traits, index); + } + + for (int c = 0; c < instance_info.size(); c++) { + InstanceInfo ii = instance_info.get(c); + if (ii.iinit_index > index) { + ii.iinit_index--; + } + ClassInfo ci = class_info.get(c); + if (ci.cinit_index > index) { + ci.cinit_index--; + } + } + + for (ScriptInfo si : script_info) { + if (si.init_index > index) { + si.init_index--; + } + removeMethodFromTraits(si.traits, index); + } + + bodyIdxFromMethodIdx = null; + + method_info.remove(index); + } + + public void replaceScriptPack(ScriptPack pack, String as) throws AVM2ParseException, CompilationException, IOException, InterruptedException { + String scriptName = pack.getPathScriptName() + ".as"; + int oldIndex = pack.scriptIndex; + int newIndex = script_info.size(); + String documentClass = getSwf().getDocumentClass(); + boolean isDocumentClass = documentClass != null && documentClass.equals(pack.getClassPath().toString()); + + ScriptInfo si = script_info.get(oldIndex); + si.delete(this, true); + int newClassIndex = instance_info.size(); + for (Trait t : si.traits.traits) { + if (t instanceof TraitClass) { + TraitClass tc = (TraitClass) t; + newClassIndex = tc.class_info + 1; + } + } + List otherAbcs = new ArrayList<>(pack.allABCs); + otherAbcs.remove(this); + ActionScriptParser.compile(as, this, otherAbcs, isDocumentClass, scriptName, newClassIndex); + // Move newly added script to its position + script_info.set(oldIndex, script_info.get(newIndex)); + script_info.remove(newIndex); + script_info.get(oldIndex).setModified(true); + pack(); // removes old classes/methods + ((Tag) parentTag).setModified(true); + } + + public void pack() { + for (int c = 0; c < instance_info.size(); c++) { + if (instance_info.get(c).deleted) { + removeClass(c); + c--; + } + } + for (int m = 0; m < method_info.size(); m++) { + if (method_info.get(m).deleted) { + removeMethod(m); + m--; + } + } + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ScriptPack.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ScriptPack.java index 4bf8ff906..9e5485e48 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ScriptPack.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ScriptPack.java @@ -1,253 +1,256 @@ -/* - * Copyright (C) 2010-2015 JPEXS, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -package com.jpexs.decompiler.flash.abc; - -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.abc.types.Multiname; -import com.jpexs.decompiler.flash.abc.types.Namespace; -import com.jpexs.decompiler.flash.abc.types.traits.Trait; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; -import com.jpexs.decompiler.flash.exporters.settings.ScriptExportSettings; -import com.jpexs.decompiler.flash.helpers.FileTextWriter; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.flash.helpers.NulWriter; -import com.jpexs.decompiler.flash.treeitems.AS3ClassTreeItem; -import com.jpexs.helpers.CancellableWorker; -import com.jpexs.helpers.Helper; -import com.jpexs.helpers.Path; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * - * @author JPEXS - */ -public class ScriptPack extends AS3ClassTreeItem { - - private static final Logger logger = Logger.getLogger(ScriptPack.class.getName()); - - public final ABC abc; - - public final int scriptIndex; - - public final List traitIndices; - - private final ClassPath path; - - @Override - public SWF getSwf() { - return abc.getSwf(); - } - - public ClassPath getClassPath() { - return path; - } - - public ScriptPack(ClassPath path, ABC abc, int scriptIndex, List traitIndices) { - super(path.className, path); - this.abc = abc; - this.scriptIndex = scriptIndex; - this.traitIndices = traitIndices; - this.path = path; - } - - public String getPathPackage() { - String packageName = ""; - for (int t : traitIndices) { - Multiname name = abc.script_info.get(scriptIndex).traits.traits.get(t).getName(abc); - Namespace ns = name.getNamespace(abc.constants); - if ((ns.kind == Namespace.KIND_PACKAGE) || (ns.kind == Namespace.KIND_PACKAGE_INTERNAL)) { - packageName = ns.getName(abc.constants, false); // assume not null - } - } - return packageName; - } - - public String getPathScriptName() { - String scriptName = ""; - for (int t : traitIndices) { - Multiname name = abc.script_info.get(scriptIndex).traits.traits.get(t).getName(abc); - Namespace ns = name.getNamespace(abc.constants); - if ((ns.kind == Namespace.KIND_PACKAGE) || (ns.kind == Namespace.KIND_PACKAGE_INTERNAL)) { - scriptName = name.getName(abc.constants, new ArrayList(), false); - } - } - return scriptName; - } - - /*public String getPath() { - String packageName = ""; - String scriptName = ""; - for (int t : traitIndices) { - Multiname name = abc.script_info[scriptIndex].traits.traits.get(t).getName(abc); - Namespace ns = name.getNamespace(abc.constants); - if ((ns.kind == Namespace.KIND_PACKAGE) || (ns.kind == Namespace.KIND_PACKAGE_INTERNAL)) { - packageName = ns.getName(abc.constants); - scriptName = name.getName(abc.constants, new ArrayList()); - } - } - return packageName.equals("") ? scriptName : packageName + "." + scriptName; - }*/ - private static String makeDirPath(String packageName) { - if (packageName.isEmpty()) { - return ""; - } - String[] pathParts; - if (packageName.contains(".")) { - pathParts = packageName.split("\\."); - } else { - pathParts = new String[]{packageName}; - } - for (int i = 0; i < pathParts.length; i++) { - pathParts[i] = Helper.makeFileName(pathParts[i]); - } - return Helper.joinStrings(pathParts, File.separator); - } - - public void convert(final NulWriter writer, final List traits, final ScriptExportMode exportMode, final boolean parallel) throws InterruptedException { - for (int t : traitIndices) { - Trait trait = traits.get(t); - Multiname name = trait.getName(abc); - Namespace ns = name.getNamespace(abc.constants); - if ((ns.kind == Namespace.KIND_PACKAGE) || (ns.kind == Namespace.KIND_PACKAGE_INTERNAL)) { - trait.convertPackaged(null, "", abc, false, exportMode, scriptIndex, -1, writer, new ArrayList(), parallel); - } else { - trait.convert(null, "", abc, false, exportMode, scriptIndex, -1, writer, new ArrayList(), parallel); - } - } - } - - private void appendTo(GraphTextWriter writer, List traits, ScriptExportMode exportMode, boolean parallel) throws InterruptedException { - boolean first = true; - for (int t : traitIndices) { - if (!first) { - writer.newLine(); - } - - Trait trait = traits.get(t); - Multiname name = trait.getName(abc); - Namespace ns = name.getNamespace(abc.constants); - if ((ns.kind == Namespace.KIND_PACKAGE) || (ns.kind == Namespace.KIND_PACKAGE_INTERNAL)) { - trait.toStringPackaged(null, "", abc, false, exportMode, scriptIndex, -1, writer, new ArrayList(), parallel); - } else { - trait.toString(null, "", abc, false, exportMode, scriptIndex, -1, writer, new ArrayList(), parallel); - } - - first = false; - } - } - - public void toSource(GraphTextWriter writer, final List traits, final ScriptExportMode exportMode, final boolean parallel) throws InterruptedException { - writer.suspendMeasure(); - int timeout = Configuration.decompilationTimeoutFile.get(); - try { - CancellableWorker.call(new Callable() { - @Override - public Void call() throws Exception { - convert(new NulWriter(), traits, exportMode, parallel); - return null; - } - }, timeout, TimeUnit.SECONDS); - } catch (TimeoutException ex) { - writer.continueMeasure(); - logger.log(Level.SEVERE, "Decompilation error", ex); - Helper.appendTimeoutComment(writer, timeout); - return; - } catch (ExecutionException ex) { - writer.continueMeasure(); - logger.log(Level.SEVERE, "Decompilation error", ex); - Helper.appendErrorComment(writer, ex); - return; - } - writer.continueMeasure(); - - appendTo(writer, traits, exportMode, parallel); - } - - public File export(String directory, ScriptExportSettings exportSettings, boolean parallel) throws IOException { - File file = null; - - if (!exportSettings.singleFile) { - String scriptName = getPathScriptName(); - String packageName = getPathPackage(); - File outDir = new File(directory + File.separatorChar + makeDirPath(packageName)); - Path.createDirectorySafe(outDir); - String fileName = outDir.toString() + File.separator + Helper.makeFileName(scriptName) + exportSettings.getFileExtension(); - file = new File(fileName); - } - - try (FileTextWriter writer = exportSettings.singleFile ? null : new FileTextWriter(Configuration.getCodeFormatting(), new FileOutputStream(file))) { - try { - FileTextWriter writer2 = exportSettings.singleFile ? exportSettings.singleFileWriter : writer; - toSource(writer2, abc.script_info.get(scriptIndex).traits.traits, exportSettings.mode, parallel); - } catch (InterruptedException ex) { - logger.log(Level.SEVERE, null, ex); - } - } catch (FileNotFoundException ex) { - logger.log(Level.SEVERE, "The file path is probably too long", ex); - } - - return file; - } - - @Override - public int hashCode() { - int hash = 7; - hash = 79 * hash + Objects.hashCode(this.abc); - hash = 79 * hash + this.scriptIndex; - hash = 79 * hash + Objects.hashCode(this.path); - return hash; - } - - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final ScriptPack other = (ScriptPack) obj; - if (!Objects.equals(this.abc, other.abc)) { - return false; - } - if (this.scriptIndex != other.scriptIndex) { - return false; - } - if (!Objects.equals(this.path, other.path)) { - return false; - } - return true; - } - - @Override - public boolean isModified() { - return abc.script_info.get(scriptIndex).isModified(); - } -} +/* + * Copyright (C) 2010-2015 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash.abc; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.abc.types.Multiname; +import com.jpexs.decompiler.flash.abc.types.Namespace; +import com.jpexs.decompiler.flash.abc.types.traits.Trait; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; +import com.jpexs.decompiler.flash.exporters.settings.ScriptExportSettings; +import com.jpexs.decompiler.flash.helpers.FileTextWriter; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.flash.helpers.NulWriter; +import com.jpexs.decompiler.flash.treeitems.AS3ClassTreeItem; +import com.jpexs.helpers.CancellableWorker; +import com.jpexs.helpers.Helper; +import com.jpexs.helpers.Path; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * + * @author JPEXS + */ +public class ScriptPack extends AS3ClassTreeItem { + + private static final Logger logger = Logger.getLogger(ScriptPack.class.getName()); + + public final ABC abc; + + public List allABCs; + + public final int scriptIndex; + + public final List traitIndices; + + private final ClassPath path; + + @Override + public SWF getSwf() { + return abc.getSwf(); + } + + public ClassPath getClassPath() { + return path; + } + + public ScriptPack(ClassPath path, ABC abc, List allAbcs, int scriptIndex, List traitIndices) { + super(path.className, path); + this.abc = abc; + this.scriptIndex = scriptIndex; + this.traitIndices = traitIndices; + this.path = path; + this.allABCs = allAbcs; + } + + public String getPathPackage() { + String packageName = ""; + for (int t : traitIndices) { + Multiname name = abc.script_info.get(scriptIndex).traits.traits.get(t).getName(abc); + Namespace ns = name.getNamespace(abc.constants); + if ((ns.kind == Namespace.KIND_PACKAGE) || (ns.kind == Namespace.KIND_PACKAGE_INTERNAL)) { + packageName = ns.getName(abc.constants, false); // assume not null + } + } + return packageName; + } + + public String getPathScriptName() { + String scriptName = ""; + for (int t : traitIndices) { + Multiname name = abc.script_info.get(scriptIndex).traits.traits.get(t).getName(abc); + Namespace ns = name.getNamespace(abc.constants); + if ((ns.kind == Namespace.KIND_PACKAGE) || (ns.kind == Namespace.KIND_PACKAGE_INTERNAL)) { + scriptName = name.getName(abc.constants, new ArrayList(), false); + } + } + return scriptName; + } + + /*public String getPath() { + String packageName = ""; + String scriptName = ""; + for (int t : traitIndices) { + Multiname name = abc.script_info[scriptIndex].traits.traits.get(t).getName(abc); + Namespace ns = name.getNamespace(abc.constants); + if ((ns.kind == Namespace.KIND_PACKAGE) || (ns.kind == Namespace.KIND_PACKAGE_INTERNAL)) { + packageName = ns.getName(abc.constants); + scriptName = name.getName(abc.constants, new ArrayList()); + } + } + return packageName.equals("") ? scriptName : packageName + "." + scriptName; + }*/ + private static String makeDirPath(String packageName) { + if (packageName.isEmpty()) { + return ""; + } + String[] pathParts; + if (packageName.contains(".")) { + pathParts = packageName.split("\\."); + } else { + pathParts = new String[]{packageName}; + } + for (int i = 0; i < pathParts.length; i++) { + pathParts[i] = Helper.makeFileName(pathParts[i]); + } + return Helper.joinStrings(pathParts, File.separator); + } + + public void convert(final NulWriter writer, final List traits, final ScriptExportMode exportMode, final boolean parallel) throws InterruptedException { + for (int t : traitIndices) { + Trait trait = traits.get(t); + Multiname name = trait.getName(abc); + Namespace ns = name.getNamespace(abc.constants); + if ((ns.kind == Namespace.KIND_PACKAGE) || (ns.kind == Namespace.KIND_PACKAGE_INTERNAL)) { + trait.convertPackaged(null, "", abc, false, exportMode, scriptIndex, -1, writer, new ArrayList(), parallel); + } else { + trait.convert(null, "", abc, false, exportMode, scriptIndex, -1, writer, new ArrayList(), parallel); + } + } + } + + private void appendTo(GraphTextWriter writer, List traits, ScriptExportMode exportMode, boolean parallel) throws InterruptedException { + boolean first = true; + for (int t : traitIndices) { + if (!first) { + writer.newLine(); + } + + Trait trait = traits.get(t); + Multiname name = trait.getName(abc); + Namespace ns = name.getNamespace(abc.constants); + if ((ns.kind == Namespace.KIND_PACKAGE) || (ns.kind == Namespace.KIND_PACKAGE_INTERNAL)) { + trait.toStringPackaged(null, "", abc, false, exportMode, scriptIndex, -1, writer, new ArrayList(), parallel); + } else { + trait.toString(null, "", abc, false, exportMode, scriptIndex, -1, writer, new ArrayList(), parallel); + } + + first = false; + } + } + + public void toSource(GraphTextWriter writer, final List traits, final ScriptExportMode exportMode, final boolean parallel) throws InterruptedException { + writer.suspendMeasure(); + int timeout = Configuration.decompilationTimeoutFile.get(); + try { + CancellableWorker.call(new Callable() { + @Override + public Void call() throws Exception { + convert(new NulWriter(), traits, exportMode, parallel); + return null; + } + }, timeout, TimeUnit.SECONDS); + } catch (TimeoutException ex) { + writer.continueMeasure(); + logger.log(Level.SEVERE, "Decompilation error", ex); + Helper.appendTimeoutComment(writer, timeout); + return; + } catch (ExecutionException ex) { + writer.continueMeasure(); + logger.log(Level.SEVERE, "Decompilation error", ex); + Helper.appendErrorComment(writer, ex); + return; + } + writer.continueMeasure(); + + appendTo(writer, traits, exportMode, parallel); + } + + public File export(String directory, ScriptExportSettings exportSettings, boolean parallel) throws IOException { + File file = null; + + if (!exportSettings.singleFile) { + String scriptName = getPathScriptName(); + String packageName = getPathPackage(); + File outDir = new File(directory + File.separatorChar + makeDirPath(packageName)); + Path.createDirectorySafe(outDir); + String fileName = outDir.toString() + File.separator + Helper.makeFileName(scriptName) + exportSettings.getFileExtension(); + file = new File(fileName); + } + + try (FileTextWriter writer = exportSettings.singleFile ? null : new FileTextWriter(Configuration.getCodeFormatting(), new FileOutputStream(file))) { + try { + FileTextWriter writer2 = exportSettings.singleFile ? exportSettings.singleFileWriter : writer; + toSource(writer2, abc.script_info.get(scriptIndex).traits.traits, exportSettings.mode, parallel); + } catch (InterruptedException ex) { + logger.log(Level.SEVERE, null, ex); + } + } catch (FileNotFoundException ex) { + logger.log(Level.SEVERE, "The file path is probably too long", ex); + } + + return file; + } + + @Override + public int hashCode() { + int hash = 7; + hash = 79 * hash + Objects.hashCode(this.abc); + hash = 79 * hash + this.scriptIndex; + hash = 79 * hash + Objects.hashCode(this.path); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final ScriptPack other = (ScriptPack) obj; + if (!Objects.equals(this.abc, other.abc)) { + return false; + } + if (this.scriptIndex != other.scriptIndex) { + return false; + } + if (!Objects.equals(this.path, other.path)) { + return false; + } + return true; + } + + @Override + public boolean isModified() { + return abc.script_info.get(scriptIndex).isModified(); + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/AVM2SourceGenerator.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/AVM2SourceGenerator.java index 594f1e793..7a41c4ff4 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/AVM2SourceGenerator.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/AVM2SourceGenerator.java @@ -1,2499 +1,2499 @@ -/* - * Copyright (C) 2010-2015 JPEXS, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -package com.jpexs.decompiler.flash.abc.avm2.parser.script; - -import com.jpexs.decompiler.flash.SourceGeneratorLocalData; -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; -import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; -import com.jpexs.decompiler.flash.abc.avm2.instructions.InstructionDefinition; -import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.NotIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.ConstructIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.ConstructSuperIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.NewActivationIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.NewCatchIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.NewClassIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.NewFunctionIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.NewObjectIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfFalseIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfStrictNeIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfTrueIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.JumpIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.LookupSwitchIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.GetLocal0Ins; -import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.KillIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.FindPropertyIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.FindPropertyStrictIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetDescendantsIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetGlobalScopeIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetLexIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetScopeObjectIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetSlotIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.HasNext2Ins; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.InitPropertyIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.LabelIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.NextNameIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.NextValueIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ReturnValueIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ReturnVoidIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.SetPropertyIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.SetSlotIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ThrowIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.DupIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PopIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PopScopeIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushByteIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushNullIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushScopeIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushStringIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushUndefinedIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushWithIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.SwapIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.types.CoerceAIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ConvertBIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.xml.CheckFilterIns; -import com.jpexs.decompiler.flash.abc.avm2.model.AVM2Item; -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.FloatValueAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.GetDescendantsAVM2Item; -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.NanAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.NullAVM2Item; -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.WithObjectAVM2Item; -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.IfCondition; -import com.jpexs.decompiler.flash.abc.avm2.parser.AVM2ParseException; -import com.jpexs.decompiler.flash.abc.types.ABCException; -import com.jpexs.decompiler.flash.abc.types.ClassInfo; -import com.jpexs.decompiler.flash.abc.types.InstanceInfo; -import com.jpexs.decompiler.flash.abc.types.MethodBody; -import com.jpexs.decompiler.flash.abc.types.MethodInfo; -import com.jpexs.decompiler.flash.abc.types.Multiname; -import com.jpexs.decompiler.flash.abc.types.Namespace; -import com.jpexs.decompiler.flash.abc.types.NamespaceSet; -import com.jpexs.decompiler.flash.abc.types.ScriptInfo; -import com.jpexs.decompiler.flash.abc.types.ValueKind; -import com.jpexs.decompiler.flash.abc.types.traits.Trait; -import com.jpexs.decompiler.flash.abc.types.traits.TraitClass; -import com.jpexs.decompiler.flash.abc.types.traits.TraitFunction; -import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter; -import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst; -import com.jpexs.decompiler.flash.abc.types.traits.Traits; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.graph.CompilationException; -import com.jpexs.decompiler.graph.GraphSourceItem; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.Loop; -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.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.LocalData; -import com.jpexs.decompiler.graph.model.NotItem; -import com.jpexs.decompiler.graph.model.OrItem; -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 java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * - * @author JPEXS - */ -public class AVM2SourceGenerator implements SourceGenerator { - - public final ABC abc; - - public List allABCs; - - public static final int MARK_E_START = 0; - - public static final int MARK_E_END = 1; - - public static final int MARK_E_TARGET = 2; - - public static final int MARK_E_FINALLYPART = 3; - - private AVM2Instruction ins(InstructionDefinition def, int... operands) { - return new AVM2Instruction(0, def, operands); - } - - public List generate(SourceGeneratorLocalData localData, GetDescendantsAVM2Item item) throws CompilationException { - int[] nssa = new int[item.openedNamespaces.size()]; - for (int i = 0; i < item.openedNamespaces.size(); i++) { - nssa[i] = item.openedNamespaces.get(i); - } - int nsset = abc.constants.getNamespaceSetId(new NamespaceSet(nssa), true); - - return GraphTargetItem.toSourceMerge(localData, this, - item.object, - ins(new GetDescendantsIns(), abc.constants.getMultinameId(new Multiname(Multiname.MULTINAME, abc.constants.getStringId(item.nameStr, true), 0, nsset, 0, new ArrayList()), true)) - ); - } - - @Override - public List generate(SourceGeneratorLocalData localData, AndItem item) throws CompilationException { - List ret = new ArrayList<>(); - ret.addAll(generateToActionList(localData, item.leftSide)); - if (!("" + item.leftSide.returnType()).equals("Boolean")) { - ret.add(ins(new ConvertBIns())); - } - ret.add(ins(new DupIns())); - List andExpr = generateToActionList(localData, item.rightSide); - andExpr.add(0, ins(new PopIns())); - int andExprLen = insToBytes(andExpr).length; - ret.add(ins(new IfFalseIns(), andExprLen)); - ret.addAll(andExpr); - return ret; - - } - - private byte[] insToBytes(List code) { - try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - for (AVM2Instruction instruction : code) { - - baos.write(instruction.getBytes()); - - } - return baos.toByteArray(); - } catch (IOException ex) { - Logger.getLogger(AVM2SourceGenerator.class.getName()).log(Level.SEVERE, null, ex); - } - return new byte[0]; - } - - @Override - public List generate(SourceGeneratorLocalData localData, OrItem item) throws CompilationException { - List ret = new ArrayList<>(); - ret.addAll(generateToActionList(localData, item.leftSide)); - if (!("" + item.leftSide.returnType()).equals("Boolean")) { - ret.add(ins(new ConvertBIns())); - } - ret.add(ins(new DupIns())); - List orExpr = generateToActionList(localData, item.rightSide); - orExpr.add(0, ins(new PopIns())); - int orExprLen = insToBytes(orExpr).length; - ret.add(ins(new IfTrueIns(), orExprLen)); - ret.addAll(orExpr); - return ret; - } - - public List toInsList(List items) { - List ret = new ArrayList<>(); - for (GraphSourceItem s : items) { - if (s instanceof AVM2Instruction) { - ret.add((AVM2Instruction) s); - } - } - return ret; - } - - private List nonempty(List list) { - if (list == null) { - return new ArrayList<>(); - } - return list; - } - - private List condition(SourceGeneratorLocalData localData, GraphTargetItem t, int offset) throws CompilationException { - if (t instanceof IfCondition) { - IfCondition ic = (IfCondition) t; - return GraphTargetItem.toSourceMerge(localData, this, ic.getLeftSide(), ic.getRightSide(), ins(ic.getIfDefinition(), offset)); - } - return GraphTargetItem.toSourceMerge(localData, this, t, ins(new IfTrueIns(), offset)); - } - - private List notCondition(SourceGeneratorLocalData localData, GraphTargetItem t, int offset) throws CompilationException { - if (t instanceof IfCondition) { - IfCondition ic = (IfCondition) t; - return GraphTargetItem.toSourceMerge(localData, this, ic.getLeftSide(), ic.getRightSide(), ins(ic.getIfNotDefinition(), offset)); - } - return GraphTargetItem.toSourceMerge(localData, this, t, ins(new IfFalseIns(), offset)); - } - - private List generateIf(SourceGeneratorLocalData localData, GraphTargetItem expression, List onTrueCmds, List onFalseCmds, boolean ternar) throws CompilationException { - List ret = new ArrayList<>(); - //ret.addAll(notCondition(localData, expression)); - List onTrue = null; - List onFalse = null; - if (ternar) { - onTrue = toInsList(onTrueCmds.get(0).toSource(localData, this)); - } else { - onTrue = generateToInsList(localData, onTrueCmds); - } - - if (onFalseCmds != null && !onFalseCmds.isEmpty()) { - if (ternar) { - onFalse = toInsList(onFalseCmds.get(0).toSource(localData, this)); - } else { - onFalse = generateToInsList(localData, onFalseCmds); - } - } - AVM2Instruction ajmp = null; - if (onFalse != null) { - if (!((!nonempty(onTrue).isEmpty()) - && ((onTrue.get(onTrue.size() - 1).definition instanceof ContinueJumpIns) - || ((onTrue.get(onTrue.size() - 1).definition instanceof BreakJumpIns))))) { - ajmp = ins(new JumpIns(), 0); - onTrue.add(ajmp); - } - } - - byte[] onTrueBytes = insToBytes(onTrue); - int onTrueLen = onTrueBytes.length; - - ret.addAll(notCondition(localData, expression, onTrueLen)); - ret.addAll(onTrue); - - if (onFalse != null) { - byte[] onFalseBytes = insToBytes(onFalse); - int onFalseLen = onFalseBytes.length; - if (ajmp != null) { - ajmp.operands[0] = onFalseLen; - } - ret.addAll(onFalse); - } - return ret; - } - - public List generate(SourceGeneratorLocalData localData, XMLFilterAVM2Item item) throws CompilationException { - List ret = new ArrayList<>(); - final Reference counterReg = new Reference<>(0); - final Reference collectionReg = new Reference<>(0); - final Reference xmlListReg = new Reference<>(0); - List xmlListSetTemp = AssignableAVM2Item.setTemp(localData, this, xmlListReg); - ret.addAll(GraphTargetItem.toSourceMerge(localData, this, - ins(new PushByteIns(), 0), - AssignableAVM2Item.setTemp(localData, this, counterReg), - item.object, - ins(new CheckFilterIns()), - NameAVM2Item.generateCoerce(localData, this, TypeItem.UNBOUNDED), - AssignableAVM2Item.setTemp(localData, this, collectionReg), - ins(new GetLexIns(), abc.constants.getMultinameId(new Multiname(Multiname.QNAME, abc.constants.getStringId("XMLList", true), abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.getStringId("", true)), 0, true), 0, 0, new ArrayList()), true)), - ins(new PushStringIns(), abc.constants.getStringId("", true)), - ins(new ConstructIns(), 1), - xmlListSetTemp - )); - final Reference tempVal1 = new Reference<>(0); - final Reference tempVal2 = new Reference<>(0); - - List forBody = toInsList(GraphTargetItem.toSourceMerge(localData, this, - ins(new LabelIns()), - AssignableAVM2Item.getTemp(localData, this, collectionReg), - AssignableAVM2Item.getTemp(localData, this, counterReg), - ins(new NextValueIns()), - AssignableAVM2Item.dupSetTemp(localData, this, tempVal1), - AssignableAVM2Item.dupSetTemp(localData, this, tempVal2), - ins(new PushWithIns()) - )); - localData.scopeStack.add(new LocalRegAVM2Item(null, tempVal2.getVal(), null)); - forBody.addAll(toInsList(item.value.toSource(localData, this))); - List trueBody = new ArrayList<>(); - trueBody.addAll(toInsList(AssignableAVM2Item.getTemp(localData, this, xmlListReg))); - trueBody.addAll(toInsList(AssignableAVM2Item.getTemp(localData, this, counterReg))); - trueBody.addAll(toInsList(AssignableAVM2Item.getTemp(localData, this, tempVal1))); - int[] nss = new int[item.openedNamespaces.size()]; - for (int i = 0; i < item.openedNamespaces.size(); i++) { - nss[i] = item.openedNamespaces.get(i); - } - trueBody.add(ins(new SetPropertyIns(), abc.constants.getMultinameId(new Multiname(Multiname.MULTINAMEL, 0, 0, abc.constants.getNamespaceSetId(new NamespaceSet(nss), true), 0, new ArrayList()), true))); - forBody.add(ins(new IfFalseIns(), insToBytes(trueBody).length)); - forBody.addAll(trueBody); - forBody.add(ins(new PopScopeIns())); - localData.scopeStack.remove(localData.scopeStack.size() - 1); - forBody.addAll(toInsList(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(tempVal2, tempVal1)))); - - int forBodyLen = insToBytes(forBody).length; - AVM2Instruction forwardJump = ins(new JumpIns(), forBodyLen); - ret.add(forwardJump); - - List expr = new ArrayList<>(); - expr.add(ins(new HasNext2Ins(), collectionReg.getVal(), counterReg.getVal())); - AVM2Instruction backIf = ins(new IfTrueIns(), 0); - expr.add(backIf); - - int exprLen = insToBytes(expr).length; - backIf.operands[0] = -(exprLen + forBodyLen); - - ret.addAll(forBody); - ret.addAll(expr); - ret.addAll(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(collectionReg, counterReg))); - ret.addAll(AssignableAVM2Item.getTemp(localData, this, xmlListReg)); - ret.addAll(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(xmlListReg))); - return ret; - } - - @Override - public List generate(SourceGeneratorLocalData localData, IfItem item) throws CompilationException { - return generateIf(localData, item.expression, item.onTrue, item.onFalse, false); - } - - private void fixSwitch(List code, int breakOffset, long loopId) { - fixLoop(code, breakOffset, Integer.MAX_VALUE, loopId); - } - - private void fixLoop(List code, int breakOffset, int continueOffset, long loopId) { - int pos = 0; - for (int a = 0; a < code.size(); a++) { - AVM2Instruction ins = code.get(a); - pos += ins.getBytes().length; - if (ins.definition instanceof JumpIns) { - if (ins.definition instanceof ContinueJumpIns) { - if (continueOffset != Integer.MAX_VALUE) { - ins.operands[0] = (-pos + continueOffset); - code.get(a).definition = new JumpIns(); - } - } - if (ins.definition instanceof BreakJumpIns) { - ins.operands[0] = (-pos + breakOffset); - code.get(a).definition = new JumpIns(); - } - } - } - } - - @Override - public List generate(SourceGeneratorLocalData localData, TernarOpItem item) throws CompilationException { - List onTrue = new ArrayList<>(); - onTrue.add(item.onTrue); - List onFalse = new ArrayList<>(); - onFalse.add(item.onFalse); - return generateIf(localData, item.expression, onTrue, onFalse, true); - } - - @Override - public List generate(SourceGeneratorLocalData localData, WhileItem item) throws CompilationException { - List ret = new ArrayList<>(); - List whileExpr = new ArrayList<>(); - - List ex = new ArrayList<>(item.expression); - GraphTargetItem lastItem = null; - if (!ex.isEmpty()) { - lastItem = ex.remove(ex.size() - 1); - while (lastItem instanceof CommaExpressionItem) { - CommaExpressionItem cei = (CommaExpressionItem) lastItem; - ex.addAll(cei.commands); - lastItem = ex.remove(ex.size() - 1); - } - whileExpr.addAll(generateToInsList(localData, ex)); - } - List whileBody = generateToInsList(localData, item.commands); - AVM2Instruction forwardJump = ins(new JumpIns(), 0); - ret.add(forwardJump); - whileBody.add(0, ins(new LabelIns())); - ret.addAll(whileBody); - int whileBodyLen = insToBytes(whileBody).length; - forwardJump.operands[0] = whileBodyLen; - whileExpr.addAll(toInsList(condition(localData, lastItem, 0))); - int whileExprLen = insToBytes(whileExpr).length; - whileExpr.get(whileExpr.size() - 1).operands[0] = -(whileExprLen + whileBodyLen); //Assuming last is if instruction - ret.addAll(whileExpr); - fixLoop(whileBody, whileBodyLen + whileExprLen, whileBodyLen, item.loop.id); - return ret; - } - - public List generate(SourceGeneratorLocalData localData, ForEachInAVM2Item item) throws CompilationException { - return generateForIn(localData, item.loop, item.expression.collection, (AssignableAVM2Item) item.expression.object, item.commands, true); - } - - public List generate(SourceGeneratorLocalData localData, ForInAVM2Item item) throws CompilationException { - return generateForIn(localData, item.loop, item.expression.collection, (AssignableAVM2Item) item.expression.object, item.commands, false); - } - - public List generateForIn(SourceGeneratorLocalData localData, Loop loop, GraphTargetItem collection, AssignableAVM2Item assignable, List commands, final boolean each) throws CompilationException { - List ret = new ArrayList<>(); - final Reference counterReg = new Reference<>(0); - final Reference collectionReg = new Reference<>(0); - - if (assignable instanceof UnresolvedAVM2Item) { - assignable = (AssignableAVM2Item) ((UnresolvedAVM2Item) assignable).resolved; - } - - ret.addAll(GraphTargetItem.toSourceMerge(localData, this, - ins(new PushByteIns(), 0), - AssignableAVM2Item.setTemp(localData, this, counterReg), - collection, - NameAVM2Item.generateCoerce(localData, this, TypeItem.UNBOUNDED), - AssignableAVM2Item.setTemp(localData, this, collectionReg) - )); - - GraphTargetItem assigned = new GraphTargetItem() { - - @Override - public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { - return null; - } - - @Override - public boolean hasReturnValue() { - return true; - } - - @Override - public GraphTargetItem returnType() { - return TypeItem.UNBOUNDED; - } - - @Override - public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { - return toSourceMerge(localData, generator, - AssignableAVM2Item.getTemp(localData, generator, collectionReg), - AssignableAVM2Item.getTemp(localData, generator, counterReg), - ins(each ? new NextValueIns() : new NextNameIns()) - ); - } - }; - assignable.setAssignedValue(assigned); - - List forBody = toInsList(GraphTargetItem.toSourceMerge(localData, this, - ins(new LabelIns()), - assignable.toSourceIgnoreReturnValue(localData, this) - )); - - forBody.addAll(generateToInsList(localData, commands)); - int forBodyLen = insToBytes(forBody).length; - - AVM2Instruction forwardJump = ins(new JumpIns(), forBodyLen); - ret.add(forwardJump); - - List expr = new ArrayList<>(); - expr.add(ins(new HasNext2Ins(), collectionReg.getVal(), counterReg.getVal())); - AVM2Instruction backIf = ins(new IfTrueIns(), 0); - expr.add(backIf); - - int exprLen = insToBytes(expr).length; - backIf.operands[0] = -(exprLen + forBodyLen); - - fixLoop(forBody, forBodyLen + exprLen, forBodyLen, loop.id); - ret.addAll(forBody); - ret.addAll(expr); - ret.addAll(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(collectionReg, counterReg))); - return ret; - } - - @Override - public List generate(SourceGeneratorLocalData localData, DoWhileItem item) throws CompilationException { - List ret = new ArrayList<>(); - List whileExpr = new ArrayList<>(); - - List ex = new ArrayList<>(item.expression); - GraphTargetItem lastItem = null; - if (!ex.isEmpty()) { - lastItem = ex.remove(ex.size() - 1); - while (lastItem instanceof CommaExpressionItem) { - CommaExpressionItem cei = (CommaExpressionItem) lastItem; - ex.addAll(cei.commands); - lastItem = ex.remove(ex.size() - 1); - } - whileExpr.addAll(generateToInsList(localData, ex)); - } - List dowhileBody = generateToInsList(localData, item.commands); - List labelBody = new ArrayList<>(); - labelBody.add(ins(new LabelIns())); - int labelBodyLen = insToBytes(labelBody).length; - - AVM2Instruction forwardJump = ins(new JumpIns(), labelBodyLen); - ret.add(forwardJump); - ret.addAll(labelBody); - ret.addAll(dowhileBody); - int dowhileBodyLen = insToBytes(dowhileBody).length; - whileExpr.addAll(toInsList(condition(localData, lastItem, 0))); - int dowhileExprLen = insToBytes(whileExpr).length; - whileExpr.get(whileExpr.size() - 1).operands[0] = -(dowhileExprLen + dowhileBodyLen + labelBodyLen); //Assuming last is if instruction - ret.addAll(whileExpr); - fixLoop(dowhileBody, dowhileBodyLen + dowhileExprLen, dowhileBodyLen, item.loop.id); - return ret; - } - - public List generate(SourceGeneratorLocalData localData, WithAVM2Item item) throws CompilationException { - - List ret = new ArrayList<>(); - ret.addAll(item.scope.toSource(localData, this)); - Reference tempReg = new Reference<>(0); - ret.addAll(AssignableAVM2Item.dupSetTemp(localData, this, tempReg)); - localData.scopeStack.add(new WithObjectAVM2Item(null, new LocalRegAVM2Item(null, tempReg.getVal(), null))); - ret.add(ins(new PushWithIns())); - ret.addAll(generate(localData, item.items)); - ret.add(ins(new PopScopeIns())); - ret.addAll(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(tempReg))); - localData.scopeStack.remove(localData.scopeStack.size() - 1); - return ret; - } - - @Override - public List generate(SourceGeneratorLocalData localData, ForItem item) throws CompilationException { - List ret = new ArrayList<>(); - List forExpr = new ArrayList<>(); - - List ex = new ArrayList<>(); - ex.add(item.expression); - - GraphTargetItem lastItem = null; - if (!ex.isEmpty()) { - lastItem = ex.remove(ex.size() - 1); - while (lastItem instanceof CommaExpressionItem) { - CommaExpressionItem cei = (CommaExpressionItem) lastItem; - ex.addAll(cei.commands); - lastItem = ex.remove(ex.size() - 1); - } - forExpr.addAll(generateToInsList(localData, ex)); - } - List forBody = generateToInsList(localData, item.commands); - List forFinalCommands = generateToInsList(localData, item.finalCommands); - - ret.addAll(generateToInsList(localData, item.firstCommands)); - - AVM2Instruction forwardJump = ins(new JumpIns(), 0); - ret.add(forwardJump); - forBody.add(0, ins(new LabelIns())); - ret.addAll(forBody); - ret.addAll(forFinalCommands); - int forBodyLen = insToBytes(forBody).length; - int forFinalCLen = insToBytes(forFinalCommands).length; - forwardJump.operands[0] = forBodyLen + forFinalCLen; - forExpr.addAll(toInsList(condition(localData, lastItem, 0))); - int forExprLen = insToBytes(forExpr).length; - forExpr.get(forExpr.size() - 1).operands[0] = -(forExprLen + forBodyLen + forFinalCLen); //Assuming last is if instruction - ret.addAll(forExpr); - fixLoop(forBody, forBodyLen + forFinalCLen + forExprLen, forBodyLen, item.loop.id); - return ret; - } - - private long uniqLast = 0; - - public String uniqId() { - uniqLast++; - return "" + uniqLast; - } - - @Override - public List generate(SourceGeneratorLocalData localData, SwitchItem item) throws CompilationException { - List ret = new ArrayList<>(); - Reference switchedReg = new Reference<>(0); - AVM2Instruction forwardJump = ins(new JumpIns(), 0); - ret.add(forwardJump); - - List cases = new ArrayList<>(); - cases.addAll(toInsList(new IntegerValueAVM2Item(null, (long) item.caseValues.size()).toSource(localData, this))); - int cLen = insToBytes(cases).length; - List caseLast = new ArrayList<>(); - caseLast.add(0, ins(new JumpIns(), cLen)); - caseLast.addAll(0, toInsList(new IntegerValueAVM2Item(null, (long) item.caseValues.size()).toSource(localData, this))); - int cLastLen = insToBytes(caseLast).length; - caseLast.add(0, ins(new JumpIns(), cLastLen)); - cases.addAll(0, caseLast); - - List preCases = new ArrayList<>(); - preCases.addAll(toInsList(item.switchedObject.toSource(localData, this))); - preCases.addAll(toInsList(AssignableAVM2Item.setTemp(localData, this, switchedReg))); - - for (int i = item.caseValues.size() - 1; i >= 0; i--) { - List sub = new ArrayList<>(); - sub.addAll(toInsList(new IntegerValueAVM2Item(null, (long) i).toSource(localData, this))); - sub.add(ins(new JumpIns(), insToBytes(cases).length)); - int subLen = insToBytes(sub).length; - - cases.addAll(0, sub); - cases.add(0, ins(new IfStrictNeIns(), subLen)); - cases.addAll(0, toInsList(AssignableAVM2Item.getTemp(localData, this, switchedReg))); - cases.addAll(0, toInsList(item.caseValues.get(i).toSource(localData, this))); - } - cases.addAll(0, preCases); - - AVM2Instruction lookupOp = new AVM2Instruction(0, new LookupSwitchIns(), new int[item.caseValues.size() + 1 + 1 + 1]); - cases.addAll(toInsList(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(switchedReg)))); - List bodies = new ArrayList<>(); - List bodiesOffsets = new ArrayList<>(); - int defOffset; - int casesLen = insToBytes(cases).length; - bodies.addAll(generateToInsList(localData, item.defaultCommands)); - bodies.add(0, ins(new LabelIns())); - bodies.add(ins(new BreakJumpIns(item.loop.id), 0)); //There could be two breaks when default clause ends with break, but official compiler does this too, so who cares... - defOffset = -(insToBytes(bodies).length + casesLen); - for (int i = item.caseCommands.size() - 1; i >= 0; i--) { - bodies.addAll(0, generateToInsList(localData, item.caseCommands.get(i))); - bodies.add(0, ins(new LabelIns())); - bodiesOffsets.add(0, -(insToBytes(bodies).length + casesLen)); - } - lookupOp.operands[0] = defOffset; - lookupOp.operands[1] = item.valuesMapping.size(); - lookupOp.operands[2 + item.caseValues.size()] = defOffset; - for (int i = 0; i < item.valuesMapping.size(); i++) { - lookupOp.operands[2 + i] = bodiesOffsets.get(item.valuesMapping.get(i)); - } - - forwardJump.operands[0] = insToBytes(bodies).length; - ret.addAll(bodies); - ret.addAll(cases); - ret.add(lookupOp); - fixSwitch(toInsList(ret), insToBytes(toInsList(ret)).length, uniqLast); - return ret; - } - - @Override - public List generate(SourceGeneratorLocalData localData, NotItem item) throws CompilationException { - /*if (item.getOriginal() instanceof Inverted) { - GraphTargetItem norig = ((Inverted) item).invert(); - return norig.toSource(localData, this); - }*/ - List ret = new ArrayList<>(); - ret.addAll(item.getOriginal().toSource(localData, this)); - ret.add(ins(new NotIns())); - return ret; - } - - @Override - public List generate(SourceGeneratorLocalData localData, DuplicateItem item) { - List ret = new ArrayList<>(); - ret.add(ins(new DupIns())); - return ret; - } - - @Override - public List generate(SourceGeneratorLocalData localData, BreakItem item) { - List ret = new ArrayList<>(); - AVM2Instruction abreak = ins(new BreakJumpIns(item.loopId), 0); - ret.add(abreak); - return ret; - } - - public List generate(SourceGeneratorLocalData localData, FunctionAVM2Item item) throws CompilationException { - List ret = new ArrayList<>(); - int scope = 0; - if (!item.functionName.isEmpty()) { - ret.add(ins(new NewObjectIns(), 0)); - ret.add(ins(new PushWithIns())); - scope = localData.scopeStack.size(); - localData.scopeStack.add(new PropertyAVM2Item(null, item.functionName, abc, allABCs, new ArrayList(), localData.callStack)); - } - ret.add(ins(new NewFunctionIns(), method(true, false, localData.callStack, localData.pkg, item.needsActivation, item.subvariables, 0 /*Set later*/, item.hasRest, item.line, localData.currentClass, null, false, localData, item.paramTypes, item.paramNames, item.paramValues, item.body, item.retType))); - if (!item.functionName.isEmpty()) { - ret.add(ins(new DupIns())); - ret.add(ins(new GetScopeObjectIns(), scope)); - ret.add(ins(new SwapIns())); - ret.add(ins(new SetPropertyIns(), abc.constants.getMultinameId(new Multiname(Multiname.QNAME, abc.constants.getStringId(item.functionName, true), abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.getStringId(localData.pkg, true)), 0, true), 0, 0, new ArrayList()), true))); - ret.add(ins(new PopScopeIns())); - localData.scopeStack.remove(localData.scopeStack.size() - 1); - } - return ret; - } - - private static int currentFinId = 1; - - private static int finId() { - return currentFinId++; - } - - public List generate(SourceGeneratorLocalData localData, TryAVM2Item item) throws CompilationException { - List ret = new ArrayList<>(); - - boolean newFinallyReg = false; - List newex = new ArrayList<>(); - int aloneFinallyEx = -1; - int finallyEx = -1; - for (NameAVM2Item e : item.catchExceptions2) { - ABCException aex = new ABCException(); - aex.name_index = abc.constants.getMultinameId(new Multiname(Multiname.QNAME, abc.constants.getStringId(e.getVariableName(), true), abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.getStringId("", true)), 0, true), 0, 0, new ArrayList()), true); - aex.type_index = typeName(localData, e.type); - newex.add(aex); - } - int finId = 0; - if (item.finallyCommands != null) { - if (item.catchExceptions2.isEmpty()) { - ABCException aex = new ABCException(); - aex.name_index = 0; - aex.type_index = 0; - newex.add(aex); - aloneFinallyEx = newex.size() - 1; - } - ABCException aex = new ABCException(); - aex.name_index = 0; - aex.type_index = 0; - newex.add(aex); - finallyEx = newex.size() - 1; - if (localData.finallyRegister == -1) { - localData.finallyRegister = getFreeRegister(localData); - killRegister(localData, localData.finallyRegister); //reuse for catches - newFinallyReg = true; - } - finId = finId(); - } - - if (finallyEx > -1) { - localData.finallyCatches.add(finId); - } - List tryCmds = generateToInsList(localData, item.tryCommands); - - //int i = firstId + item.catchCommands.size() - 1; - List catches = new ArrayList<>(); - Reference tempReg = new Reference<>(0); - - List currentExceptionIds = new ArrayList<>(); - List> catchCmds = new ArrayList<>(); - for (int c = 0; c < item.catchCommands.size(); c++) { - int i = localData.exceptions.size(); - localData.exceptions.add(newex.get(c)); - - currentExceptionIds.add(i); - - //Reference tempReg=new Reference<>(0); - List catchCmd = new ArrayList<>(); - catchCmd.add(ins(new NewCatchIns(), i)); - catchCmd.addAll(toInsList(AssignableAVM2Item.dupSetTemp(localData, this, tempReg))); - catchCmd.add(ins(new DupIns())); - catchCmd.add(ins(new PushScopeIns())); - catchCmd.add(ins(new SwapIns())); - catchCmd.add(ins(new SetSlotIns(), 1)); - - for (AssignableAVM2Item a : item.catchVariables.get(c)) { - GraphTargetItem r = a; - if (r instanceof UnresolvedAVM2Item) { - r = ((UnresolvedAVM2Item) r).resolvedRoot; - } - if (r instanceof NameAVM2Item) { - NameAVM2Item n = (NameAVM2Item) r; - if (item.catchExceptions2.get(c).getVariableName().equals(n.getVariableName())) { - n.setSlotScope(localData.scopeStack.size()); - } - } - } - localData.scopeStack.add(new LocalRegAVM2Item(null, tempReg.getVal(), null)); - catchCmd.addAll(generateToInsList(localData, item.catchCommands.get(c))); - localData.scopeStack.remove(localData.scopeStack.size() - 1); - catchCmd.add(ins(new PopScopeIns())); - catchCmd.addAll(toInsList(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(tempReg)))); - catchCmds.add(catchCmd); - } - for (int c = item.catchCommands.size() - 1; c >= 0; c--) { - List preCatches = new ArrayList<>(); - /*preCatches.add(ins(new GetLocal0Ins())); - preCatches.add(ins(new PushScopeIns())); - preCatches.add(AssignableAVM2Item.generateGetLoc(localData.activationReg)); - preCatches.add(ins(new PushScopeIns()));*/ - for (GraphTargetItem s : localData.scopeStack) { - preCatches.addAll(toInsList(s.toSource(localData, this))); - if (s instanceof WithObjectAVM2Item) { - preCatches.add(ins(new PushWithIns())); - } else { - preCatches.add(ins(new PushScopeIns())); - } - } - - //catchCmds.add(catchCmd); - preCatches.addAll(catchCmds.get(c)); - catches.addAll(0, preCatches); - catches.add(0, new ExceptionMarkAVM2Instruction(currentExceptionIds.get(c), MARK_E_TARGET)); - catches.add(0, ins(new JumpIns(), insToBytes(catches).length)); - } - - if (aloneFinallyEx > -1) { - localData.exceptions.add(newex.get(aloneFinallyEx)); - aloneFinallyEx = localData.exceptions.size() - 1; - - } - if (finallyEx > -1) { - localData.exceptions.add(newex.get(finallyEx)); - finallyEx = localData.exceptions.size() - 1; - } - - for (int i : currentExceptionIds) { - ret.add(new ExceptionMarkAVM2Instruction(i, MARK_E_START)); - } - if (aloneFinallyEx > -1) { - ret.add(new ExceptionMarkAVM2Instruction(aloneFinallyEx, MARK_E_START)); - } - if (finallyEx > -1) { - ret.add(new ExceptionMarkAVM2Instruction(finallyEx, MARK_E_START)); - } - - ret.addAll(tryCmds); - - for (int i : currentExceptionIds) { - ret.add(new ExceptionMarkAVM2Instruction(i, MARK_E_END)); - } - if (aloneFinallyEx > -1) { - ret.add(new ExceptionMarkAVM2Instruction(aloneFinallyEx, MARK_E_END)); - } - - if (aloneFinallyEx > -1) { - List preCatches = new ArrayList<>(); - for (GraphTargetItem s : localData.scopeStack) { - preCatches.addAll(toInsList(s.toSource(localData, this))); - if (s instanceof WithObjectAVM2Item) { - preCatches.add(ins(new PushWithIns())); - } else { - preCatches.add(ins(new PushScopeIns())); - } - } - preCatches.add(ins(new NewCatchIns(), aloneFinallyEx)); - preCatches.addAll(toInsList(AssignableAVM2Item.dupSetTemp(localData, this, tempReg))); - preCatches.add(ins(new PushScopeIns())); - preCatches.add(ins(new ThrowIns())); - preCatches.add(ins(new PopScopeIns())); - preCatches.addAll(toInsList(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(tempReg)))); - catches.add(ins(new JumpIns(), insToBytes(preCatches).length)); - catches.add(new ExceptionMarkAVM2Instruction(aloneFinallyEx, MARK_E_TARGET)); - catches.addAll(preCatches); - } - AVM2Instruction finSwitch = null; - AVM2Instruction pushDefIns = ins(new PushByteIns(), 0); - - int defPos = 0; - if (finallyEx > -1) { - List preCatches = new ArrayList<>(); - preCatches.add(0, new ExceptionMarkAVM2Instruction(finallyEx, MARK_E_TARGET)); - for (GraphTargetItem s : localData.scopeStack) { - preCatches.addAll(toInsList(s.toSource(localData, this))); - if (s instanceof WithObjectAVM2Item) { - preCatches.add(ins(new PushWithIns())); - } else { - preCatches.add(ins(new PushScopeIns())); - } - } - preCatches.add(ins(new NewCatchIns(), finallyEx)); - preCatches.addAll(toInsList(AssignableAVM2Item.dupSetTemp(localData, this, tempReg))); - preCatches.add(ins(new PushScopeIns())); - preCatches.add(ins(new PopScopeIns())); - Reference tempReg2 = new Reference<>(0); - preCatches.add(ins(new KillIns(), tempReg.getVal())); - preCatches.add(ins(new CoerceAIns())); - preCatches.addAll(toInsList(AssignableAVM2Item.setTemp(localData, this, tempReg2))); - preCatches.add(pushDefIns); - - List finallySwitchCmds = new ArrayList<>(); - - finSwitch = new AVM2Instruction(0, new LookupSwitchIns(), new int[1 + 1 + 1]); - finSwitch.operands[0] = finSwitch.getBytes().length; - finSwitch.operands[1] = 0; //switch cnt - - List preFinallySwitch = new ArrayList<>(); - preFinallySwitch.add(ins(new LabelIns())); - preFinallySwitch.add(ins(new PopIns())); - int preFinallySwitchLen = insToBytes(preFinallySwitch).length; - - finallySwitchCmds.add(ins(new LabelIns())); - finallySwitchCmds.addAll(toInsList(AssignableAVM2Item.getTemp(localData, this, tempReg2))); - finallySwitchCmds.add(ins(new KillIns(), tempReg2.getVal())); - finallySwitchCmds.add(ins(new ThrowIns())); - finallySwitchCmds.add(ins(new PushByteIns(), 255)); - finallySwitchCmds.add(ins(new PopScopeIns())); - finallySwitchCmds.add(ins(new KillIns(), tempReg.getVal())); - - int finSwitchLen = insToBytes(finallySwitchCmds).length; - - preCatches.add(ins(new JumpIns(), preFinallySwitchLen + finSwitchLen)); - AVM2Instruction fjump = ins(new JumpIns(), 0); - fjump.operands[0] = insToBytes(preCatches).length + preFinallySwitchLen + finSwitchLen; - - preCatches.add(0, fjump); - preCatches.add(0, new ExceptionMarkAVM2Instruction(finallyEx, MARK_E_END)); - preCatches.add(0, ins(new PushByteIns(), 255)); - - finallySwitchCmds.add(new ExceptionMarkAVM2Instruction(finallyEx, MARK_E_FINALLYPART)); - - int oldReg = localData.finallyRegister; - localData.finallyRegister = getFreeRegister(localData); - Integer cnt = localData.finallyCounter.get(finId); - if (cnt == null) { - cnt = -1; - } - defPos = cnt; - cnt++; //Skip default clause (throw) - localData.finallyCounter.put(finId, cnt); - finallySwitchCmds.addAll(generateToInsList(localData, item.finallyCommands)); - killRegister(localData, localData.finallyRegister); - localData.finallyRegister = oldReg; - finSwitchLen = insToBytes(finallySwitchCmds).length; - - finSwitch.operands[2] = -finSwitchLen; - preCatches.addAll(preFinallySwitch); - preCatches.addAll(finallySwitchCmds); - preCatches.add(finSwitch); - - catches.addAll(preCatches); - AssignableAVM2Item.killTemp(localData, this, Arrays.asList(tempReg, tempReg2)); - } - - ret.addAll(catches); - //localData.exceptions.addAll(newex); - - if (finallyEx > -1) { - localData.finallyCatches.remove(localData.finallyCatches.size() - 1); - } - if (newFinallyReg) { - localData.finallyRegister = -1; - killRegister(localData, localData.finallyRegister); - } - int pos = 0; - int finallyPos = 0; - int switchPos = 0; - for (int s = 0; s < ret.size(); s++) { - GraphSourceItem src = ret.get(s); - if (src == finSwitch) { - switchPos = pos; - } - if (src instanceof AVM2Instruction) { - AVM2Instruction ins = (AVM2Instruction) src; - if (ins instanceof ExceptionMarkAVM2Instruction) { - ExceptionMarkAVM2Instruction em = (ExceptionMarkAVM2Instruction) ins; - if (em.exceptionId == finallyEx && em.markType == MARK_E_FINALLYPART) { - finallyPos = pos; - ret.remove(s); - s--; - continue; - } - } - pos += ins.getBytes().length; - } - - } - - if (finSwitch != null) { - pos = 0; - int defLoc = finSwitch.operands[2]; - List switchLoc = new ArrayList<>(); - boolean wasDef = false; - for (int s = 0; s < ret.size(); s++) { - GraphSourceItem src = ret.get(s); - if (src instanceof AVM2Instruction) { - AVM2Instruction ins = (AVM2Instruction) src; - if (ins.definition instanceof FinallyJumpIns) { - FinallyJumpIns fji = (FinallyJumpIns) ins.definition; - if (fji.getClauseId() == finId) { - List bet = new ArrayList<>(); - bet.add(ins(new LabelIns())); - bet.add(ins(new PopIns())); - int betLen = insToBytes(bet).length; - if (wasDef) { - ins.operands[0] = 0; - } else { - ins.operands[0] = finallyPos - (pos + ins.getBytes().length); - } - ins.definition = new JumpIns(); - switchLoc.add(pos + ins.getBytes().length + betLen - switchPos); - } - } - pos += ins.getBytes().length; - } - if (defPos == switchLoc.size() - 1) { - switchLoc.add(defLoc); - wasDef = true; - } - } - finSwitch.operands = new int[1 + 1 + switchLoc.size()]; - pushDefIns.operands[0] = defPos + 1; - int afterLoc = finSwitch.getBytes().length; - finSwitch.operands[0] = afterLoc; - finSwitch.operands[1] = switchLoc.size() - 1; - for (int j = 0; j < switchLoc.size(); j++) { - finSwitch.operands[2 + j] = switchLoc.get(j); - } - } - - return ret; - } - - @Override - public List generate(SourceGeneratorLocalData localData, ContinueItem item) { - List ret = new ArrayList<>(); - AVM2Instruction acontinue = ins(new ContinueJumpIns(item.loopId), 0); - ret.add(acontinue); - return ret; - } - - public List generate(SourceGeneratorLocalData localData, ReturnValueAVM2Item item) throws CompilationException { - List ret = new ArrayList<>(); - ret.addAll(item.value.toSource(localData, this)); - if (!localData.finallyCatches.isEmpty()) { - ret.add(ins(new CoerceAIns())); - ret.add(AssignableAVM2Item.generateSetLoc(localData.finallyRegister)); - for (int i = localData.finallyCatches.size() - 1; i >= 0; i--) { - if (i < localData.finallyCatches.size() - 1) { - ret.add(ins(new LabelIns())); - } - int clauseId = localData.finallyCatches.get(i); - Integer cnt = localData.finallyCounter.get(clauseId); - if (cnt == null) { - cnt = -1; - } - cnt++; - localData.finallyCounter.put(clauseId, cnt); - ret.addAll(new IntegerValueAVM2Item(null, (long) cnt).toSource(localData, this)); - ret.add(ins(new FinallyJumpIns(clauseId), 0)); - ret.add(ins(new LabelIns())); - ret.add(ins(new PopIns())); - } - ret.add(ins(new LabelIns())); - ret.add(AssignableAVM2Item.generateGetLoc(localData.finallyRegister)); - ret.add(ins(new KillIns(), localData.finallyRegister)); - } - ret.add(ins(new ReturnValueIns())); - return ret; - } - - public List generate(SourceGeneratorLocalData localData, ReturnVoidAVM2Item item) throws CompilationException { - List ret = new ArrayList<>(); - if (!localData.finallyCatches.isEmpty()) { - - for (int i = 0; i < localData.finallyCatches.size(); i++) { - if (i > 0) { - ret.add(ins(new LabelIns())); - } - int clauseId = localData.finallyCatches.get(i); - Integer cnt = localData.finallyCounter.get(clauseId); - if (cnt == null) { - cnt = -1; - } - cnt++; - localData.finallyCounter.put(clauseId, cnt); - ret.addAll(new IntegerValueAVM2Item(null, (long) cnt).toSource(localData, this)); - ret.add(ins(new FinallyJumpIns(clauseId), 0)); - ret.add(ins(new LabelIns())); - ret.add(ins(new PopIns())); - } - ret.add(ins(new LabelIns())); - } - ret.add(ins(new ReturnVoidIns())); - return ret; - } - - public List generate(SourceGeneratorLocalData localData, ThrowAVM2Item item) throws CompilationException { - List ret = new ArrayList<>(); - ret.addAll(item.value.toSource(localData, this)); - ret.add(ins(new ThrowIns())); - return ret; - } - - private List generateToInsList(SourceGeneratorLocalData localData, List commands) throws CompilationException { - return toInsList(generate(localData, commands)); - } - - private List generateToActionList(SourceGeneratorLocalData localData, GraphTargetItem command) throws CompilationException { - return toInsList(command.toSource(localData, this)); - } - - @Override - public List generate(SourceGeneratorLocalData localData, List commands) throws CompilationException { - List ret = new ArrayList<>(); - for (GraphTargetItem item : commands) { - ret.addAll(item.toSourceIgnoreReturnValue(localData, this)); - } - return ret; - } - - public HashMap getRegisterVars(SourceGeneratorLocalData localData) { - return localData.registerVars; - } - - public void setRegisterVars(SourceGeneratorLocalData localData, HashMap 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 registerVars = getRegisterVars(localData); - int tmpReg = 0; - for (int i = 0; i < 256; i++) { - if (!registerVars.containsValue(i)) { - tmpReg = i; - break; - } - } - return tmpReg; - } - - public AVM2SourceGenerator(ABC abc, List allABCs) { - this.abc = abc; - this.allABCs = allABCs; - } - - public ABC getABC() { - return abc; - } - - public void generateClass(List importedClasses, List sinitVariables, boolean staticNeedsActivation, List staticInit, List openedNamespaces, int namespace, int initScope, String pkg, ClassInfo classInfo, InstanceInfo instanceInfo, SourceGeneratorLocalData localData, boolean isInterface, String name, String superName, GraphTargetItem extendsVal, List implementsStr, GraphTargetItem constructor, List traitItems, Reference class_index) throws AVM2ParseException, CompilationException { - localData.currentClass = name; - localData.pkg = pkg; - List ret = new ArrayList<>(); - if (extendsVal == null && !isInterface) { - extendsVal = new TypeItem("Object"); - } - ParsedSymbol s = null; - - instanceInfo.name_index = traitName(namespace, name); - - Trait[] it = generateTraitsPhase1(name, superName, false, localData, traitItems, instanceInfo.instance_traits, class_index); - Trait[] st = generateTraitsPhase1(name, superName, true, localData, traitItems, classInfo.static_traits, class_index); - generateTraitsPhase2(importedClasses, pkg, traitItems, it, openedNamespaces, localData); - generateTraitsPhase2(importedClasses, pkg, traitItems, st, openedNamespaces, localData); - generateTraitsPhase3(initScope, isInterface, name, superName, false, localData, traitItems, instanceInfo.instance_traits, it, new HashMap(), class_index); - generateTraitsPhase3(initScope, isInterface, name, superName, true, localData, traitItems, classInfo.static_traits, st, new HashMap(), class_index); - int init = 0; - if (constructor == null || isInterface) { - instanceInfo.iinit_index = init = method(false, isInterface, new ArrayList(), pkg, false, new ArrayList(), initScope + 1, false, 0, isInterface ? null : name, extendsVal != null ? extendsVal.toString() : null, true, localData, new ArrayList(), new ArrayList(), new ArrayList(), new ArrayList(), TypeItem.UNBOUNDED/*?? FIXME*/); - } else { - MethodAVM2Item m = (MethodAVM2Item) constructor; - instanceInfo.iinit_index = init = method(false, false, new ArrayList(), pkg, m.needsActivation, m.subvariables, initScope + 1, m.hasRest, m.line, name, extendsVal != null ? extendsVal.toString() : null, true, localData, m.paramTypes, m.paramNames, m.paramValues, m.body, TypeItem.UNBOUNDED/*?? FIXME*/); - } - - //Class initializer - int staticMi = method(false, false, new ArrayList(), pkg, staticNeedsActivation, sinitVariables, initScope + (implementsStr.isEmpty() ? 0 : 1), false, 0, isInterface ? null : name, superName, false, localData, new ArrayList(), new ArrayList(), new ArrayList(), staticInit, TypeItem.UNBOUNDED); - MethodBody sinitBody = abc.findBody(staticMi); - - List sinitcode = new ArrayList<>(); - List initcode = new ArrayList<>(); - for (GraphTargetItem ti : traitItems) { - if ((ti instanceof SlotAVM2Item) || (ti instanceof ConstAVM2Item)) { - GraphTargetItem val = null; - boolean isStatic = false; - int ns = -1; - String tname = null; - boolean isConst = false; - if (ti instanceof SlotAVM2Item) { - val = ((SlotAVM2Item) ti).value; - isStatic = ((SlotAVM2Item) ti).isStatic(); - ns = ((SlotAVM2Item) ti).getNamespace(); - tname = ((SlotAVM2Item) ti).var; - } - if (ti instanceof ConstAVM2Item) { - val = ((ConstAVM2Item) ti).value; - isStatic = ((ConstAVM2Item) ti).isStatic(); - ns = ((ConstAVM2Item) ti).getNamespace(); - tname = ((ConstAVM2Item) ti).var; - isConst = true; - } - if (isStatic && val != null) { - sinitcode.add(ins(new FindPropertyIns(), traitName(ns, tname))); - sinitcode.addAll(toInsList(val.toSource(localData, this))); - sinitcode.add(ins(isConst ? new InitPropertyIns() : new SetPropertyIns(), traitName(ns, tname))); - } - if (!isStatic && val != null) { - //do not init basic values, that can be stored in trait - if (!(val instanceof IntegerValueAVM2Item) && !(val instanceof StringAVM2Item) && !(val instanceof BooleanAVM2Item) && !(val instanceof NullAVM2Item) && !(val instanceof UndefinedAVM2Item)) { - initcode.add(ins(new GetLocal0Ins())); - initcode.addAll(toInsList(val.toSource(localData, this))); - initcode.add(ins(isConst ? new InitPropertyIns() : new SetPropertyIns(), traitName(ns, tname))); - } - } - } - } - MethodBody initBody = null; - if (!isInterface) { - initBody = abc.findBody(init); - initBody.getCode().code.addAll(constructor == null ? 0 : 2, initcode);//after getlocal0,pushscope - - if (sinitBody.getCode().code.get(sinitBody.getCode().code.size() - 1).definition instanceof ReturnVoidIns) { - sinitBody.getCode().code.addAll(2, sinitcode); //after getlocal0,pushscope - } - } - sinitBody.markOffsets(); - sinitBody.autoFillStats(abc, initScope + (implementsStr.isEmpty() ? 0 : 1), true); - - classInfo.cinit_index = staticMi; - if (!isInterface) { - initBody.autoFillStats(abc, initScope + 1, true); - } - instanceInfo.interfaces = new int[implementsStr.size()]; - for (int i = 0; i < implementsStr.size(); i++) { - instanceInfo.interfaces[i] = superIntName(localData, implementsStr.get(i)); - } - } - - @Override - public List 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 cmds = new ArrayList<>(item.commands); - GraphTargetItem lastExpr = cmds.remove(cmds.size() - 1); - List ret = new ArrayList<>(); - ret.addAll(generate(localData, cmds)); - ret.addAll(lastExpr.toSource(localData, this)); - return ret; - } - - public int generateClass(int namespace, ClassInfo ci, InstanceInfo ii, int initScope, String pkg, SourceGeneratorLocalData localData, AVM2Item cls, Reference class_index) throws AVM2ParseException, CompilationException { - /*ClassInfo ci = new ClassInfo(); - InstanceInfo ii = new InstanceInfo(); - abc.class_info.add(ci); - abc.instance_info.add(ii); - */ - if (cls instanceof ClassAVM2Item) { - ClassAVM2Item cai = (ClassAVM2Item) cls; - generateClass(cai.importedClasses, cai.sinitVariables, cai.staticInitActivation, cai.staticInit, cai.openedNamespaces, namespace, initScope, pkg, ci, ii, localData, false, cai.className, cai.extendsOp == null ? "Object" : cai.extendsOp.toString(), cai.extendsOp, cai.implementsOp, cai.constructor, cai.traits, class_index); - if (!cai.isDynamic) { - ii.flags |= InstanceInfo.CLASS_SEALED; - } - if (cai.isFinal) { - ii.flags |= InstanceInfo.CLASS_FINAL; - } - ii.flags |= InstanceInfo.CLASS_PROTECTEDNS; - ii.protectedNS = cai.protectedNs; - } - if (cls instanceof InterfaceAVM2Item) { - InterfaceAVM2Item iai = (InterfaceAVM2Item) cls; - ii.flags |= InstanceInfo.CLASS_INTERFACE; - ii.flags |= InstanceInfo.CLASS_SEALED; - generateClass(iai.importedClasses, new ArrayList(), false, new ArrayList(), iai.openedNamespaces, namespace, initScope, pkg, ci, ii, localData, true, iai.name, null, null, iai.superInterfaces, null, iai.methods, class_index); - } - - return abc.instance_info.size() - 1; - } - - public int traitName(int namespace, String var) { - return abc.constants.getMultinameId(new Multiname(Multiname.QNAME, str(var), namespace, 0, 0, new ArrayList()), true); - } - - public int typeName(SourceGeneratorLocalData localData, GraphTargetItem type) throws CompilationException { - if (type instanceof UnboundedTypeItem) { - return 0; - } - if (("" + type).equals("*")) { - return 0; - } - - return resolveType(localData, type, abc, allABCs); - /* - TypeItem nameItem = (TypeItem) type; - name = nameItem.fullTypeName; - if (name.contains(".")) { - pkg = name.substring(0, name.lastIndexOf('.')); - name = name.substring(name.lastIndexOf('.') + 1); - } - if (!nameItem.subtypes.isEmpty()) { //It's vector => TypeName - List params = new ArrayList<>(); - for (GraphTargetItem p : nameItem.subtypes) { - params.add(typeName(localData, p));//abc.constants.getMultinameId(new Multiname(Multiname.QNAME, str(p), namespace(Namespace.KIND_PACKAGE, ppkg), 0, 0, new ArrayList()), true)); - } - int qname = abc.constants.getMultinameId(new Multiname(Multiname.QNAME, str(name), namespace(Namespace.KIND_PACKAGE, pkg), 0, 0, new ArrayList()), true); - return abc.constants.getMultinameId(new Multiname(Multiname.TYPENAME, 0, 0, 0, qname, params), true); - } else { - return abc.constants.getMultinameId(new Multiname(Multiname.QNAME, str(name), namespace(Namespace.KIND_PACKAGE, pkg), 0, 0, new ArrayList()), true); - }*/ - } - - public int ident(GraphTargetItem name) { - if (name instanceof NameAVM2Item) { - return str(((NameAVM2Item) name).getVariableName()); - } - throw new RuntimeException("no ident"); //FIXME - } - - public int namespace(int nsKind, String name) { - return abc.constants.getNamespaceId(new Namespace(nsKind, str(name)), 0, true); - } - - public int str(String name) { - return abc.constants.getStringId(name, true); - } - - public int propertyName(GraphTargetItem name) { - if (name instanceof NameAVM2Item) { - NameAVM2Item va = (NameAVM2Item) name; - return abc.constants.getMultinameId(new Multiname(Multiname.QNAME, str(va.getVariableName()), namespace(Namespace.KIND_PACKAGE, ""), 0, 0, new ArrayList()), true); - } - throw new RuntimeException("no prop"); //FIXME - } - - public int getFreeRegister(SourceGeneratorLocalData localData) { - for (int i = 0;; i++) { - if (!localData.registerVars.containsValue(i)) { - localData.registerVars.put("__TEMP__" + i, i); - return i; - } - } - } - - public boolean killRegister(SourceGeneratorLocalData localData, int i) { - String key = null; - for (String k : localData.registerVars.keySet()) { - if (localData.registerVars.get(k) == i) { - key = k; - break; - } - } - if (key != null) { - localData.registerVars.remove(key); - return true; - } - return false; - } - - public int method(boolean subMethod, boolean isInterface, List callStack, String pkg, boolean needsActivation, List subvariables, int initScope, boolean hasRest, int line, String className, String superType, boolean constructor, SourceGeneratorLocalData localData, List paramTypes, List paramNames, List paramValues, List body, GraphTargetItem retType) throws CompilationException { - //Reference hasArgs = new Reference<>(Boolean.FALSE); - //calcRegisters(localData,needsActivation,paramNames,subvariables,body, hasArgs); - SourceGeneratorLocalData newlocalData = new SourceGeneratorLocalData(new HashMap(), 1, true, 0); - newlocalData.currentClass = className; - newlocalData.pkg = localData.pkg; - newlocalData.callStack.addAll(localData.callStack); - newlocalData.traitUsages = localData.traitUsages; - newlocalData.currentScript = localData.currentScript; - newlocalData.documentClass = localData.documentClass; - newlocalData.subMethod = subMethod; - localData = newlocalData; - - localData.activationReg = 0; - - for (int i = 0; i < subvariables.size(); i++) { - AssignableAVM2Item an = subvariables.get(i); - if (an instanceof UnresolvedAVM2Item) { - UnresolvedAVM2Item n = (UnresolvedAVM2Item) an; - if (n.resolved == null) { - String fullClass = localData.getFullClass(); - GraphTargetItem res = n.resolve(new TypeItem(fullClass), paramTypes, paramNames, abc, allABCs, callStack, subvariables); - if (res instanceof AssignableAVM2Item) { - subvariables.set(i, (AssignableAVM2Item) res); - } else { - subvariables.remove(i); - i--; - } - } - } - } - - for (int t = 0; t < paramTypes.size(); t++) { - GraphTargetItem an = paramTypes.get(t); - if (an instanceof UnresolvedAVM2Item) { - UnresolvedAVM2Item n = (UnresolvedAVM2Item) an; - if (n.resolved == null) { - String fullClass = localData.getFullClass(); - GraphTargetItem res = n.resolve(new TypeItem(fullClass), paramTypes, paramNames, abc, allABCs, callStack, subvariables); - paramTypes.set(t, res); - } - } - } - - boolean hasArguments = false; - List slotNames = new ArrayList<>(); - List slotTypes = new ArrayList<>(); - slotNames.add("--first"); - slotTypes.add("-"); - - List registerNames = new ArrayList<>(); - List registerTypes = new ArrayList<>(); - if (className != null) { - String fullClassName = pkg == null || pkg.isEmpty() ? className : pkg + "." + className; - registerTypes.add(fullClassName); - localData.scopeStack.add(new LocalRegAVM2Item(null, registerNames.size(), null)); - registerNames.add("this"); - - } else { - registerTypes.add("global"); - registerNames.add("this"); - } - for (GraphTargetItem t : paramTypes) { - registerTypes.add(t.toString()); - slotTypes.add(t.toString()); - } - registerNames.addAll(paramNames); - slotNames.addAll(paramNames); - /*for (GraphTargetItem p : paramTypes) { - slotTypes.add("" + p); - }*/ - if (hasRest) { - slotTypes.add("*"); - } - localData.registerVars.clear(); - for (AssignableAVM2Item an : subvariables) { - if (an instanceof NameAVM2Item) { - NameAVM2Item n = (NameAVM2Item) an; - if (n.getVariableName().equals("arguments") & !n.isDefinition()) { - registerNames.add("arguments"); - registerTypes.add("Object"); - hasArguments = true; - break; - } - } - } - int paramRegCount = registerNames.size(); - - if (needsActivation) { - registerNames.add("+$activation"); - localData.activationReg = registerNames.size() - 1; - registerTypes.add("Object"); - localData.scopeStack.add(new LocalRegAVM2Item(null, localData.activationReg, null)); - } - - String mask = Configuration.registerNameFormat.get(); - mask = mask.replace("%d", "([0-9]+)"); - Pattern pat = Pattern.compile(mask); - - //Two rounds - for (int round = 1; round <= 2; round++) { - for (AssignableAVM2Item an : subvariables) { - if (an instanceof NameAVM2Item) { - NameAVM2Item n = (NameAVM2Item) an; - if (n.isDefinition()) { - if (!needsActivation || (n.getSlotScope() <= 0)) { - String varName = n.getVariableName(); - Matcher m = pat.matcher(varName); - //In first round, make all register that match standard loc_xx register - if ((round == 1) && (m.matches())) { - String regIndexStr = m.group(1); - int regIndex = Integer.parseInt(regIndexStr); - while (registerNames.size() <= regIndex + 1) { - String standardName = String.format(mask, registerNames.size() - 1); - registerNames.add(standardName); - registerTypes.add("*"); - slotNames.add(standardName); - slotTypes.add("*"); - } - registerNames.set(regIndex, varName); - registerTypes.set(regIndex, varName); - slotNames.set(regIndex, varName); - slotTypes.set(regIndex, varName); - } //in second round the rest - else if (round == 2 && !m.matches()) { - registerNames.add(n.getVariableName()); - registerTypes.add(n.type.toString()); - slotNames.add(n.getVariableName()); - slotTypes.add(n.type.toString()); - } - } - } - } - } - } - - int slotScope = subMethod ? 0 : 1; - - for (AssignableAVM2Item an : subvariables) { - if (an instanceof NameAVM2Item) { - NameAVM2Item n = (NameAVM2Item) an; - String variableName = n.getVariableName(); - if (variableName != null) { - boolean isThisOrSuper = variableName.equals("this") || variableName.equals("super"); - if (!isThisOrSuper && needsActivation) { - if (n.getSlotNumber() <= 0) { - n.setSlotNumber(slotNames.indexOf(variableName)); - n.setSlotScope(slotScope); - } - } else { - if (isThisOrSuper) { - n.setRegNumber(0); - } else { - n.setRegNumber(registerNames.indexOf(variableName)); - } - } - } - } - } - - for (int i = 0; i < registerNames.size(); i++) { - if (needsActivation && i > localData.activationReg) { - break; - } - localData.registerVars.put(registerNames.get(i), i); - } - List declarations = new ArrayList<>(); - loopn: - for (AssignableAVM2Item an : subvariables) { - if (an instanceof NameAVM2Item) { - NameAVM2Item n = (NameAVM2Item) an; - - if (needsActivation) { - if (n.getSlotScope() != slotScope) { - continue; - } else { - if (n.getSlotNumber() < paramRegCount) { - continue; - } - } - } - for (NameAVM2Item d : declarations) { - if (n.getVariableName() != null && n.getVariableName().equals(d.getVariableName())) { - continue loopn; - } - } - - for (GraphTargetItem it : body) { //search first level of commands - if (it instanceof NameAVM2Item) { - NameAVM2Item n2 = (NameAVM2Item) it; - if (n2.isDefinition() && n2.getAssignedValue() != null && n2.getVariableName().equals(n.getVariableName())) { - continue loopn; - } - if (!n2.isDefinition() && n2.getVariableName() != null && n2.getVariableName().equals(n.getVariableName())) { //used earlier than defined - break; - } - } - } - if (n.unresolved) { - continue; - } - if (n.redirect != null) { - continue; - } - if (n.getNs() != null) { - continue; - } - - String variableName = n.getVariableName(); - if ("this".equals(variableName) || "super".equals(variableName) || paramNames.contains(variableName) || "arguments".equals(variableName)) { - continue; - } - - NameAVM2Item d = new NameAVM2Item(n.type, n.line, n.getVariableName(), NameAVM2Item.getDefaultValue("" + n.type), true, n.openedNamespaces); - //no index - if (needsActivation) { - if (d.getSlotNumber() <= 0) { - d.setSlotNumber(n.getSlotNumber()); - d.setSlotScope(n.getSlotScope()); - } - } else { - d.setRegNumber(n.getRegNumber()); - } - declarations.add(d); - } - } - - int[] param_types = new int[paramTypes.size()]; - ValueKind[] optional = new ValueKind[paramValues.size()]; - //int[] param_names = new int[paramNames.size()]; - for (int i = 0; i < paramTypes.size(); i++) { - param_types[i] = typeName(localData, paramTypes.get(i)); - //param_names[i] = str(paramNames.get(i)); - } - - for (int i = 0; i < paramValues.size(); i++) { - optional[i] = getValueKind(Namespace.KIND_NAMESPACE/*FIXME*/, paramTypes.get(paramTypes.size() - paramValues.size() + i), paramValues.get(i)); - if (optional[i] == null) { - throw new CompilationException("Default value must be compiletime constant", line); - } - } - - MethodInfo mi = new MethodInfo(param_types, constructor ? 0 : typeName(localData, retType), 0/*name_index*/, 0, optional, new int[0]/*no param_names*/); - if (hasArguments) { - mi.setFlagNeed_Arguments(); - } - //No param names like in official - /* - if (!paramNames.isEmpty()) { - mi.setFlagHas_paramnames(); - }*/ - if (!paramValues.isEmpty()) { - mi.setFlagHas_optional(); - } - if (hasRest) { - mi.setFlagNeed_rest(); - } - - int mindex; - if (!isInterface) { - MethodBody mbody = new MethodBody(); - - if (needsActivation) { - int slotId = 1; - for (int i = 1; i < slotNames.size(); i++) { - TraitSlotConst tsc = new TraitSlotConst(); - tsc.slot_id = slotId++; - tsc.name_index = abc.constants.getMultinameId(new Multiname(Multiname.QNAME, abc.constants.getStringId(slotNames.get(i), true), abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE_INTERNAL, abc.constants.getStringId(pkg, true)), 0, true), 0, 0, new ArrayList()), true); - tsc.type_index = typeName(localData, new TypeItem(slotTypes.get(i))); - mbody.traits.traits.add(tsc); - } - for (int i = 1; i < paramRegCount; i++) { - NameAVM2Item param = new NameAVM2Item(new TypeItem(registerTypes.get(i)), 0, registerNames.get(i), null, false, new ArrayList()); - param.setRegNumber(i); - NameAVM2Item d = new NameAVM2Item(new TypeItem(registerTypes.get(i)), 0, registerNames.get(i), param, true, new ArrayList()); - d.setSlotScope(slotScope); - d.setSlotNumber(slotNames.indexOf(registerNames.get(i))); - declarations.add(d); - } - } - if (body != null) { - body.addAll(0, declarations); - } - - localData.exceptions = new ArrayList<>(); - localData.callStack.add(mbody); - List src = body == null ? new ArrayList() : generate(localData, body); - - mbody.method_info = abc.addMethodInfo(mi); - mi.setBody(mbody); - List mbodyCode = toInsList(src); - mbody.setCode(new AVM2Code()); - mbody.getCode().code = mbodyCode; - - if (needsActivation) { - if (localData.traitUsages.containsKey(mbody)) { - List usages = localData.traitUsages.get(mbody); - for (int i = 0; i < mbody.traits.traits.size(); i++) { - if (usages.contains(i)) { - TraitSlotConst tsc = (TraitSlotConst) mbody.traits.traits.get(i); - GraphTargetItem type = TypeItem.UNBOUNDED; - if (tsc.type_index > 0) { - type = new TypeItem(abc.constants.constant_multiname.get(tsc.type_index).getNameWithNamespace(abc.constants, true)); - } - NameAVM2Item d = new NameAVM2Item(type, 0, tsc.getName(abc).getName(abc.constants, new ArrayList(), true), NameAVM2Item.getDefaultValue("" + type), true, new ArrayList()); - d.setSlotNumber(tsc.slot_id); - d.setSlotScope(slotScope); - mbodyCode.addAll(0, toInsList(d.toSourceIgnoreReturnValue(localData, this))); - } - } - } - - List acts = new ArrayList<>(); - acts.add(ins(new NewActivationIns())); - acts.add(ins(new DupIns())); - acts.add(AssignableAVM2Item.generateSetLoc(localData.activationReg)); - acts.add(ins(new PushScopeIns())); - - mbodyCode.addAll(0, acts); - } - - if (constructor) { - List abcs = new ArrayList<>(); - abcs.add(abc); - abcs.addAll(allABCs); - - int parentConstMinAC = 0; - - for (ABC a : abcs) { - int ci = a.findClassByName(superType); - if (ci > -1) { - MethodInfo pmi = a.method_info.get(a.instance_info.get(ci).iinit_index); - parentConstMinAC = pmi.param_types.length; - if (pmi.flagHas_optional()) { - parentConstMinAC -= pmi.optional.length; - } - - } - } - int ac = -1; - for (AVM2Instruction ins : mbodyCode) { - if (ins.definition instanceof ConstructSuperIns) { - ac = ins.operands[0]; - if (parentConstMinAC > ac) { - throw new CompilationException("Parent constructor call requires different number of arguments", line); - } - - } - } - if (ac == -1) { - if (parentConstMinAC == 0) { - mbodyCode.add(0, new AVM2Instruction(0, new GetLocal0Ins(), null)); - mbodyCode.add(1, new AVM2Instruction(0, new ConstructSuperIns(), new int[]{0})); - - } else { - throw new CompilationException("Parent constructor must be called", line); - } - } - } - if (className != null && !subMethod) { - mbodyCode.add(0, new AVM2Instruction(0, new GetLocal0Ins(), null)); - mbodyCode.add(1, new AVM2Instruction(0, new PushScopeIns(), null)); - } - boolean addRet = false; - if (!mbodyCode.isEmpty()) { - InstructionDefinition lastDef = mbodyCode.get(mbodyCode.size() - 1).definition; - if (!((lastDef instanceof ReturnVoidIns) || (lastDef instanceof ReturnValueIns))) { - addRet = true; - } - } else { - addRet = true; - } - if (addRet) { - if (retType.toString().equals("*") || retType.toString().equals("void") || constructor) { - mbodyCode.add(new AVM2Instruction(0, new ReturnVoidIns(), null)); - } else { - mbodyCode.add(new AVM2Instruction(0, new PushUndefinedIns(), null)); - mbodyCode.add(new AVM2Instruction(0, new ReturnValueIns(), null)); - } - } - mbody.exceptions = localData.exceptions.toArray(new ABCException[localData.exceptions.size()]); - int offset = 0; - for (int i = 0; i < mbodyCode.size(); i++) { - AVM2Instruction ins = mbodyCode.get(i); - if (ins instanceof ExceptionMarkAVM2Instruction) { - ExceptionMarkAVM2Instruction m = (ExceptionMarkAVM2Instruction) ins; - switch (m.markType) { - case MARK_E_START: - mbody.exceptions[m.exceptionId].start = offset; - break; - case MARK_E_END: - mbody.exceptions[m.exceptionId].end = offset; - break; - case MARK_E_TARGET: - mbody.exceptions[m.exceptionId].target = offset; - break; - } - mbodyCode.remove(i); - i--; - continue; - } - offset += ins.getBytes().length; - } - - mbody.markOffsets(); - mbody.autoFillStats(abc, initScope, className != null); - abc.addMethodBody(mbody); - mindex = mbody.method_info; - } else { - mindex = abc.addMethodInfo(mi); - } - - return mindex; - } - - public ValueKind getValueKind(int ns, GraphTargetItem type, GraphTargetItem val) { - - if (val instanceof BooleanAVM2Item) { - BooleanAVM2Item bi = (BooleanAVM2Item) val; - if (bi.value) { - return new ValueKind(0, ValueKind.CONSTANT_True); - } else { - return new ValueKind(0, ValueKind.CONSTANT_False); - } - } - - boolean isNs = false; - if (type instanceof NameAVM2Item) { - if (((NameAVM2Item) type).getVariableName().equals("namespace")) { - isNs = true; - } - } - - if ((type instanceof TypeItem) && (((TypeItem) type).fullTypeName.equals("Namespace"))) { - isNs = true; - } - - if (val instanceof StringAVM2Item) { - StringAVM2Item sval = (StringAVM2Item) val; - if (isNs) { - return new ValueKind(namespace(Namespace.KIND_NAMESPACE, sval.value), ValueKind.CONSTANT_Namespace); - } else { - return new ValueKind(str(sval.value), ValueKind.CONSTANT_Utf8); - } - } - if (val instanceof IntegerValueAVM2Item) { - return new ValueKind(abc.constants.getIntId(((IntegerValueAVM2Item) val).value, true), ValueKind.CONSTANT_Int); - } - if (val instanceof FloatValueAVM2Item) { - return new ValueKind(abc.constants.getDoubleId(((FloatValueAVM2Item) val).value, true), ValueKind.CONSTANT_Double); - } - if (val instanceof NanAVM2Item) { - return new ValueKind(abc.constants.getDoubleId(Double.NaN, true), ValueKind.CONSTANT_Double); - } - if (val instanceof NullAVM2Item) { - return new ValueKind(0, ValueKind.CONSTANT_Null); - } - if (val instanceof UndefinedAVM2Item) { - return new ValueKind(0, ValueKind.CONSTANT_Undefined); - } - return null; - } - - private int genNs(List importedClasses, String pkg, String custom, int namespace, List openedNamespaces, SourceGeneratorLocalData localData, int line) throws CompilationException { - if (custom != null) { - PropertyAVM2Item prop = new PropertyAVM2Item(null, custom, abc, allABCs, openedNamespaces, new ArrayList()); - Reference value = new Reference<>(null); - prop.resolve(localData, new Reference(null), new Reference(null), new Reference<>(0), value); - boolean resolved = true; - if (value.getVal() == null) { - resolved = false; - } - if (!resolved) { - - String customPkg = ""; - String fullCustom = ""; - for (String imp : importedClasses) { - if (imp.endsWith("." + custom)) { - customPkg = imp.substring(0, imp.lastIndexOf('.')); - fullCustom = imp; - break; - } - } - - List aas = new ArrayList<>(); - aas.add(abc); - aas.addAll(allABCs); - for (ABC a : aas) { - for (ScriptInfo si : a.script_info) { - for (Trait t : si.traits.traits) { - Multiname m = t.getName(a); - if (fullCustom.equals(m.getNameWithNamespace(a.constants, true))) { - if (t instanceof TraitSlotConst) { - if (((TraitSlotConst) t).isNamespace()) { - Namespace ns = a.constants.getNamespace(((TraitSlotConst) t).value_index); - return abc.constants.getNamespaceId(new Namespace(ns.kind, abc.constants.getStringId(ns.getName(a.constants, true), true)), 0, true); - } - } - } - } - } - } - - throw new CompilationException("Namespace not defined", line); - } - namespace = value.getVal().value_index; - } - return namespace; - } - - public void generateTraitsPhase2(List importedClasses, String pkg, List items, Trait[] traits, List openedNamespaces, SourceGeneratorLocalData localData) throws CompilationException { - for (int k = 0; k < items.size(); k++) { - GraphTargetItem item = items.get(k); - if (traits[k] == null) { - continue; - } else if (item instanceof InterfaceAVM2Item) { - traits[k].name_index = traitName(((InterfaceAVM2Item) item).namespace, ((InterfaceAVM2Item) item).name); - } else if (item instanceof ClassAVM2Item) { - traits[k].name_index = traitName(((ClassAVM2Item) item).namespace, ((ClassAVM2Item) item).className); - } else if ((item instanceof MethodAVM2Item) || (item instanceof GetterAVM2Item) || (item instanceof SetterAVM2Item)) { - traits[k].name_index = traitName(genNs(importedClasses, pkg, ((MethodAVM2Item) item).customNamespace, ((MethodAVM2Item) item).namespace, openedNamespaces, localData, ((MethodAVM2Item) item).line), ((MethodAVM2Item) item).functionName); - } else if (item instanceof FunctionAVM2Item) { - traits[k].name_index = traitName(((FunctionAVM2Item) item).namespace, ((FunctionAVM2Item) item).functionName); - } else if (item instanceof ConstAVM2Item) { - traits[k].name_index = traitName(genNs(importedClasses, pkg, ((ConstAVM2Item) item).customNamespace, ((ConstAVM2Item) item).getNamespace(), openedNamespaces, localData, ((ConstAVM2Item) item).line), ((ConstAVM2Item) item).var); - } else if (item instanceof SlotAVM2Item) { - traits[k].name_index = traitName(genNs(importedClasses, pkg, ((SlotAVM2Item) item).customNamespace, ((SlotAVM2Item) item).getNamespace(), openedNamespaces, localData, ((SlotAVM2Item) item).line), ((SlotAVM2Item) item).var); - } - } - - for (int k = 0; k < items.size(); k++) { - GraphTargetItem item = items.get(k); - if (traits[k] == null) { - continue; - } - if (item instanceof ClassAVM2Item) { - InstanceInfo instanceInfo = abc.instance_info.get(((TraitClass) traits[k]).class_info); - instanceInfo.name_index = abc.constants.getMultinameId(new Multiname(Multiname.QNAME, abc.constants.getStringId(((ClassAVM2Item) item).className, true), - abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.getStringId(pkg, true)), 0, true), 0, 0, new ArrayList()), true); - - if (((ClassAVM2Item) item).extendsOp != null) { - instanceInfo.super_index = typeName(localData, ((ClassAVM2Item) item).extendsOp); - } else { - instanceInfo.super_index = abc.constants.getMultinameId(new Multiname(Multiname.QNAME, str("Object"), namespace(Namespace.KIND_PACKAGE, ""), 0, 0, new ArrayList()), true); - } - instanceInfo.interfaces = new int[((ClassAVM2Item) item).implementsOp.size()]; - for (int i = 0; i < ((ClassAVM2Item) item).implementsOp.size(); i++) { - instanceInfo.interfaces[i] = superIntName(localData, ((ClassAVM2Item) item).implementsOp.get(i)); - } - } - if (item instanceof InterfaceAVM2Item) { - InstanceInfo instanceInfo = abc.instance_info.get(((TraitClass) traits[k]).class_info); - instanceInfo.name_index = abc.constants.getMultinameId(new Multiname(Multiname.QNAME, abc.constants.getStringId(((InterfaceAVM2Item) item).name, true), - abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.getStringId(pkg, true)), 0, true), 0, 0, new ArrayList()), true); - - instanceInfo.interfaces = new int[((InterfaceAVM2Item) item).superInterfaces.size()]; - for (int i = 0; i < ((InterfaceAVM2Item) item).superInterfaces.size(); i++) { - GraphTargetItem un = ((InterfaceAVM2Item) item).superInterfaces.get(i); - instanceInfo.interfaces[i] = superIntName(localData, un); - } - } - } - } - - public int superIntName(SourceGeneratorLocalData localData, GraphTargetItem un) throws CompilationException { - if (un instanceof UnresolvedAVM2Item) { - ((UnresolvedAVM2Item) un).resolve(null, new ArrayList(), new ArrayList(), abc, allABCs, new ArrayList(), new ArrayList()); - un = ((UnresolvedAVM2Item) un).resolved; - } - if (!(un instanceof TypeItem)) { //not applyType - throw new CompilationException("Invalid type", 0); - } - TypeItem sup = (TypeItem) un; - int propId = resolveType(localData, sup, abc, allABCs); - int[] nss = new int[]{abc.constants.constant_multiname.get(propId).namespace_index}; - return abc.constants.getMultinameId(new Multiname(Multiname.MULTINAME, abc.constants.constant_multiname.get(propId).name_index, 0, abc.constants.getNamespaceSetId(new NamespaceSet(nss), true), 0, new ArrayList()), true); - - } - - public void generateTraitsPhase3(int methodInitScope, boolean isInterface, String className, String superName, boolean generateStatic, SourceGeneratorLocalData localData, List items, Traits ts, Trait[] traits, Map initScopes, Reference class_index) throws AVM2ParseException, CompilationException { - //Note: Names must be generated first before accesed in inner subs - for (int k = 0; k < items.size(); k++) { - GraphTargetItem item = items.get(k); - if (traits[k] == null) { - continue; - } - if (item instanceof InterfaceAVM2Item) { - generateClass(((InterfaceAVM2Item) item).namespace, abc.class_info.get(((TraitClass) traits[k]).class_info), abc.instance_info.get(((TraitClass) traits[k]).class_info), initScopes.get(traits[k]), ((InterfaceAVM2Item) item).pkg, localData, (InterfaceAVM2Item) item, class_index); - } - - if (item instanceof ClassAVM2Item) { - generateClass(((ClassAVM2Item) item).namespace, abc.class_info.get(((TraitClass) traits[k]).class_info), abc.instance_info.get(((TraitClass) traits[k]).class_info), initScopes.get(traits[k]), ((ClassAVM2Item) item).pkg, localData, (ClassAVM2Item) item, class_index); - } - if ((item instanceof MethodAVM2Item) || (item instanceof GetterAVM2Item) || (item instanceof SetterAVM2Item)) { - MethodAVM2Item mai = (MethodAVM2Item) item; - if (mai.isStatic() != generateStatic) { - continue; - } - ((TraitMethodGetterSetter) traits[k]).method_info = method(false, isInterface, new ArrayList(), mai.pkg, mai.needsActivation, mai.subvariables, methodInitScope + (mai.isStatic() ? 0 : 1), mai.hasRest, mai.line, className, superName, false, localData, mai.paramTypes, mai.paramNames, mai.paramValues, mai.body, mai.retType); - } else if (item instanceof FunctionAVM2Item) { - FunctionAVM2Item fai = (FunctionAVM2Item) item; - ((TraitFunction) traits[k]).method_info = method(false, isInterface, new ArrayList(), fai.pkg, fai.needsActivation, fai.subvariables, methodInitScope, fai.hasRest, fai.line, className, superName, false, localData, fai.paramTypes, fai.paramNames, fai.paramValues, fai.body, fai.retType); - } - } - } - - public Trait[] generateTraitsPhase1(String className, String superName, boolean generateStatic, SourceGeneratorLocalData localData, List items, Traits ts, Reference classIndex) throws AVM2ParseException, CompilationException { - Trait[] traits = new Trait[items.size()]; - int slot_id = 1; - int disp_id = 3; //1 and 2 are for constructor - for (int k = 0; k < items.size(); k++) { - GraphTargetItem item = items.get(k); - if (item instanceof InterfaceAVM2Item) { - TraitClass tc = new TraitClass(); - ClassInfo ci = new ClassInfo(); - InstanceInfo ii = new InstanceInfo(); - /*abc.class_info.add(ci); - abc.instance_info.add(ii);*/ - tc.class_info = classIndex.getVal(); - abc.addClass(ci, ii, classIndex.getVal()); - classIndex.setVal(classIndex.getVal() + 1); - ii.flags |= InstanceInfo.CLASS_INTERFACE; - //tc.class_info = abc.instance_info.size() - 1; - tc.kindType = Trait.TRAIT_CLASS; - //tc.name_index = traitName(((InterfaceAVM2Item) item).namespace, ((InterfaceAVM2Item) item).name); - tc.slot_id = 0; //? - ts.traits.add(tc); - traits[k] = tc; - } - - if (item instanceof ClassAVM2Item) { - TraitClass tc = new TraitClass(); - ClassInfo ci = new ClassInfo(); - InstanceInfo ii = new InstanceInfo(); - /*abc.class_info.add(ci); - abc.instance_info.add(instanceInfo);*/ - tc.class_info = classIndex.getVal(); - abc.addClass(ci, ii, classIndex.getVal()); - classIndex.setVal(classIndex.getVal() + 1); - //tc.class_info = abc.instance_info.size() - 1; - - /*instanceInfo.name_index = abc.constants.addMultiname(new Multiname(Multiname.QNAME, abc.constants.getStringId(((ClassAVM2Item) item).className, true), - abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.getStringId(pkg.packageName, true)), 0, true), 0, 0, new ArrayList())); - */ - - /*if (((ClassAVM2Item) item).extendsOp != null) { - instanceInfo.super_index = typeName(localData, ((ClassAVM2Item) item).extendsOp); - } else { - instanceInfo.super_index = abc.constants.getMultinameId(new Multiname(Multiname.QNAME, str("Object"), namespace(Namespace.KIND_PACKAGE, ""), 0, 0, new ArrayList()), true); - }*/ - tc.kindType = Trait.TRAIT_CLASS; - // tc.name_index = traitName(((ClassAVM2Item) item).namespace, ((ClassAVM2Item) item).className); - tc.slot_id = slot_id++; - ts.traits.add(tc); - traits[k] = tc; - - } - if ((item instanceof SlotAVM2Item) || (item instanceof ConstAVM2Item)) { - TraitSlotConst tsc = new TraitSlotConst(); - tsc.kindType = (item instanceof SlotAVM2Item) ? Trait.TRAIT_SLOT : Trait.TRAIT_CONST; - String var = null; - GraphTargetItem val = null; - GraphTargetItem type = null; - boolean isNamespace = false; - int namespace = 0; - boolean isStatic = false; - if (item instanceof SlotAVM2Item) { - SlotAVM2Item sai = (SlotAVM2Item) item; - if (sai.isStatic() != generateStatic) { - continue; - } - var = sai.var; - val = sai.value; - type = sai.type; - isStatic = sai.isStatic(); - namespace = sai.getNamespace(); - } - if (item instanceof ConstAVM2Item) { - ConstAVM2Item cai = (ConstAVM2Item) item; - if (cai.isStatic() != generateStatic) { - continue; - } - var = cai.var; - val = cai.value; - type = cai.type; - namespace = cai.getNamespace(); - isNamespace = type.toString().equals("Namespace"); - isStatic = cai.isStatic(); - } - if (isNamespace) { - tsc.name_index = traitName(namespace, var); - } - tsc.type_index = isNamespace ? 0 : (type == null ? 0 : typeName(localData, type)); - - ValueKind vk = getValueKind(namespace, type, val); - if (vk == null) { - tsc.value_kind = ValueKind.CONSTANT_Undefined; - } else { - tsc.value_kind = vk.value_kind; - tsc.value_index = vk.value_index; - } - tsc.slot_id = isStatic ? slot_id++ : 0; - ts.traits.add(tsc); - traits[k] = tsc; - } - if ((item instanceof MethodAVM2Item) || (item instanceof GetterAVM2Item) || (item instanceof SetterAVM2Item)) { - MethodAVM2Item mai = (MethodAVM2Item) item; - if (mai.isStatic() != generateStatic) { - continue; - } - TraitMethodGetterSetter tmgs = new TraitMethodGetterSetter(); - tmgs.kindType = (item instanceof GetterAVM2Item) ? Trait.TRAIT_GETTER : ((item instanceof SetterAVM2Item) ? Trait.TRAIT_SETTER : Trait.TRAIT_METHOD); - //tmgs.name_index = traitName(((MethodAVM2Item) item).namespace, ((MethodAVM2Item) item).functionName); - tmgs.disp_id = mai.isStatic() ? disp_id++ : 0; //For a reason, there is disp_id only for static methods (or not?) - if (mai.isFinal() || mai.isStatic()) { - tmgs.kindFlags |= Trait.ATTR_Final; - } - if (mai.isOverride()) { - tmgs.kindFlags |= Trait.ATTR_Override; - } - ts.traits.add(tmgs); - - traits[k] = tmgs; - } else if (item instanceof FunctionAVM2Item) { - TraitFunction tf = new TraitFunction(); - tf.slot_id = slot_id++; - tf.kindType = Trait.TRAIT_FUNCTION; - //tf.name_index = traitName(((FunctionAVM2Item) item).namespace, ((FunctionAVM2Item) item).functionName); - ts.traits.add(tf); - traits[k] = tf; - } - } - - return traits; - } - - public ScriptInfo generateScriptInfo(SourceGeneratorLocalData localData, List commands, int classPos) throws AVM2ParseException, CompilationException { - Reference class_index = new Reference<>(classPos); - ScriptInfo si = new ScriptInfo(); - localData.currentScript = si; - Trait[] traitArr = generateTraitsPhase1(null, null, true, localData, commands, si.traits, class_index); - generateTraitsPhase2(new ArrayList(), null/*FIXME*/, commands, traitArr, new ArrayList(), localData); - MethodInfo mi = new MethodInfo(new int[0], 0, 0, 0, new ValueKind[0], new int[0]); - MethodBody mb = new MethodBody(); - mb.method_info = abc.addMethodInfo(mi); - mb.setCode(new AVM2Code()); - List mbCode = mb.getCode().code; - mbCode.add(ins(new GetLocal0Ins())); - mbCode.add(ins(new PushScopeIns())); - - int traitScope = 2; - - Map initScopes = new HashMap<>(); - - for (Trait t : si.traits.traits) { - if (t instanceof TraitClass) { - TraitClass tc = (TraitClass) t; - List parents = new ArrayList<>(); - if (localData.documentClass) { - mbCode.add(ins(new GetScopeObjectIns(), 0)); - traitScope++; - } else { - NamespaceSet nsset = new NamespaceSet(new int[]{abc.constants.constant_multiname.get(tc.name_index).namespace_index}); - mbCode.add(ins(new FindPropertyStrictIns(), abc.constants.getMultinameId(new Multiname(Multiname.MULTINAME, abc.constants.constant_multiname.get(tc.name_index).name_index, 0, abc.constants.getNamespaceSetId(nsset, true), 0, new ArrayList()), true))); - } - if (abc.instance_info.get(tc.class_info).isInterface()) { - mbCode.add(ins(new PushNullIns())); - } else { - - parentNamesAddNames(abc, allABCs, abc.instance_info.get(tc.class_info).name_index, parents, new ArrayList(), new ArrayList()); - for (int i = parents.size() - 1; i >= 1; i--) { - mbCode.add(ins(new GetLexIns(), parents.get(i))); - mbCode.add(ins(new PushScopeIns())); - traitScope++; - } - mbCode.add(ins(new GetLexIns(), parents.get(1))); - } - mbCode.add(ins(new NewClassIns(), tc.class_info)); - if (!abc.instance_info.get(tc.class_info).isInterface()) { - for (int i = parents.size() - 1; i >= 1; i--) { - mbCode.add(ins(new PopScopeIns())); - } - } - mbCode.add(ins(new InitPropertyIns(), tc.name_index)); - initScopes.put(t, traitScope); - traitScope = 1; - } - } - - mbCode.add(ins(new ReturnVoidIns())); - mb.autoFillStats(abc, 1, false); - abc.addMethodBody(mb); - si.init_index = mb.method_info; - localData.pkg = null; //FIXME: pkg.packageName; - generateTraitsPhase3(1/*??*/, false, null, null, true, localData, commands, si.traits, traitArr, initScopes, class_index); - return si; - } - - public static void parentNamesAddNames(ABC abc, List allABCs, int name_index, List indices, List names, List namespaces) { - List cindices = new ArrayList<>(); - - List outABCs = new ArrayList<>(); - parentNames(abc, allABCs, name_index, cindices, names, namespaces, outABCs); - for (int i = 0; i < cindices.size(); i++) { - ABC a = outABCs.get(i); - int m = cindices.get(i); - if (a == abc) { - indices.add(m); - continue; - } - Multiname superName = a.constants.constant_multiname.get(m); - indices.add( - abc.constants.getMultinameId( - new Multiname(Multiname.QNAME, - abc.constants.getStringId(superName.getName(a.constants, new ArrayList(), true), true), - abc.constants.getNamespaceId(new Namespace(superName.getNamespace(a.constants).kind, abc.constants.getStringId(superName.getNamespace(a.constants).getName(a.constants, true), true)), 0, true), 0, 0, new ArrayList()), true) - ); - } - } - - public static GraphTargetItem getTraitReturnType(ABC abc, Trait t) { - if (t instanceof TraitSlotConst) { - TraitSlotConst tsc = (TraitSlotConst) t; - if (tsc.type_index == 0) { - return TypeItem.UNBOUNDED; - } - return PropertyAVM2Item.multinameToType(tsc.type_index, abc.constants); - } - if (t instanceof TraitMethodGetterSetter) { - TraitMethodGetterSetter tmgs = (TraitMethodGetterSetter) t; - if (tmgs.kindType == Trait.TRAIT_GETTER) { - return PropertyAVM2Item.multinameToType(abc.method_info.get(tmgs.method_info).ret_type, abc.constants); - } - if (tmgs.kindType == Trait.TRAIT_SETTER) { - if (abc.method_info.get(tmgs.method_info).param_types.length > 0) { - return PropertyAVM2Item.multinameToType(abc.method_info.get(tmgs.method_info).param_types[0], abc.constants); - } else { - return TypeItem.UNBOUNDED; - } - } - } - if (t instanceof TraitFunction) { - return new TypeItem("Function"); - } - return TypeItem.UNBOUNDED; - } - - private static boolean eq(Object a, Object b) { - if (a == null && b == null) { - return true; - } - if (a == null) { - return false; - } - if (b == null) { - return false; - } - return a.equals(b); - } - - public static boolean searchPrototypeChain(boolean instanceOnly, List abcs, String pkg, String obj, String propertyName, Reference outName, Reference outNs, Reference outPropNs, Reference outPropNsKind, Reference outPropNsIndex, Reference outPropType, Reference outPropValue) { - - for (ABC abc : abcs) { - if (!instanceOnly) { - for (ScriptInfo ii : abc.script_info) { - if (ii.deleted) { - continue; - } - for (Trait t : ii.traits.traits) { - if (eq(pkg, t.getName(abc).getNamespace(abc.constants).getName(abc.constants, true))) { - if (propertyName.equals(t.getName(abc).getName(abc.constants, new ArrayList(), true))) { - outName.setVal(obj); - outNs.setVal(pkg); - outPropNs.setVal(t.getName(abc).getNamespace(abc.constants).getName(abc.constants, true)); - outPropNsKind.setVal(t.getName(abc).getNamespace(abc.constants).kind); - outPropNsIndex.setVal(abc.constants.getNamespaceSubIndex(t.getName(abc).namespace_index)); - outPropType.setVal(getTraitReturnType(abc, t)); - if (t instanceof TraitSlotConst) { - TraitSlotConst tsc = (TraitSlotConst) t; - outPropValue.setVal(new ValueKind(tsc.value_index, tsc.value_kind)); - } - return true; - } - } - } - } - } - for (int i = 0; i < abc.instance_info.size(); i++) { - InstanceInfo ii = abc.instance_info.get(i); - if (ii.deleted) { - continue; - } - Multiname clsName = ii.getName(abc.constants); - if (obj.equals(clsName.getName(abc.constants, new ArrayList(), true))) { - if (eq(pkg, clsName.getNamespace(abc.constants).getName(abc.constants, true))) { - //class found - - for (Trait t : ii.instance_traits.traits) { - if (t.getName(abc) == null) { //in traits phase 2 - continue; - } - if (propertyName.equals(t.getName(abc).getName(abc.constants, new ArrayList(), true))) { - outName.setVal(obj); - outNs.setVal(pkg); - outPropNs.setVal(t.getName(abc).getNamespace(abc.constants).getName(abc.constants, true)); - outPropNsKind.setVal(t.getName(abc).getNamespace(abc.constants).kind); - outPropNsIndex.setVal(abc.constants.getNamespaceSubIndex(t.getName(abc).namespace_index)); - outPropType.setVal(getTraitReturnType(abc, t)); - if (t instanceof TraitSlotConst) { - TraitSlotConst tsc = (TraitSlotConst) t; - outPropValue.setVal(new ValueKind(tsc.value_index, tsc.value_kind)); - } - return true; - } - } - - if (!instanceOnly) { - for (Trait t : abc.class_info.get(i).static_traits.traits) { - if (t.getName(abc) == null) { //in traits phase 2 - continue; - } - if (propertyName.equals(t.getName(abc).getName(abc.constants, new ArrayList(), true))) { - outName.setVal(obj); - outNs.setVal(pkg); - outPropNs.setVal(t.getName(abc).getNamespace(abc.constants).getName(abc.constants, true)); - outPropNsKind.setVal(t.getName(abc).getNamespace(abc.constants).kind); - outPropNsIndex.setVal(abc.constants.getNamespaceSubIndex(t.getName(abc).namespace_index)); - outPropType.setVal(getTraitReturnType(abc, t)); - if (t instanceof TraitSlotConst) { - TraitSlotConst tsc = (TraitSlotConst) t; - outPropValue.setVal(new ValueKind(tsc.value_index, tsc.value_kind)); - } - return true; - } - } - } - - Multiname superName = abc.constants.constant_multiname.get(ii.super_index); - if (superName != null) { - return searchPrototypeChain(instanceOnly, abcs, superName.getNamespace(abc.constants).getName(abc.constants, true), superName.getName(abc.constants, new ArrayList(), true), propertyName, outName, outNs, outPropNs, outPropNsKind, outPropNsIndex, outPropType, outPropValue); - } else { - return false; - } - } - } - } - } - return false; - } - - public static void parentNames(ABC abc, List allABCs, int name_index, List indices, List names, List namespaces, List outABCs) { - indices.add(name_index); - names.add(abc.constants.constant_multiname.get(name_index).getName(abc.constants, new ArrayList(), true)); - namespaces.add(abc.constants.constant_multiname.get(name_index).getNamespace(abc.constants).getName(abc.constants, true)); - Multiname mname = abc.constants.constant_multiname.get(name_index); - - outABCs.add(abc); - - List abcs = new ArrayList<>(); - abcs.add(abc); - abcs.addAll(allABCs); - - for (ABC a : abcs) { - for (int i = 0; i < a.instance_info.size(); i++) { - Multiname m = a.constants.constant_multiname.get(a.instance_info.get(i).name_index); - if (m.getName(a.constants, new ArrayList(), true).equals(mname.getName(abc.constants, new ArrayList(), true))) { - - if (m.getNamespace(a.constants).hasName(mname.getNamespace(abc.constants).getName(abc.constants, true), a.constants)) { - //Multiname superName = a.constants.constant_multiname.get(a.instance_info.get(i).super_index); - abcs.remove(a); - if (a.instance_info.get(i).super_index != 0) { - parentNames(a, abcs, a.instance_info.get(i).super_index, indices, names, namespaces, outABCs); - } - /*parentNames(abc,allABCs,abc.constants.getMultinameId( - new Multiname(superName.kind, - abc.constants.getStringId(superName.getName(a.constants, new ArrayList()),true), - abc.constants.getNamespaceId(new Namespace(superName.getNamespace(a.constants).kind, abc.constants.getStringId(superName.getNamespace(a.constants).getName(a.constants),true)),0,true), 0, 0, new ArrayList()), true),indices,names,namespaces,outABCs);*/ - return; - } - } - } - } - } - - /* public void calcRegisters(Reference activationReg, SourceGeneratorLocalData localData, boolean needsActivation, List funParamNames,List funSubVariables,List funBody, Reference hasArguments) throws ParseException { - - }*/ - /*public int resolveType(String objType) { - if (objType.equals("*")) { - return 0; - } - List abcs = new ArrayList<>(); - abcs.add(abc); - abcs.addAll(allABCs); - for (ABC a : abcs) { - int ci = a.findClassByName(objType); - if (ci != -1) { - Multiname tname = a.instance_info.get(ci).getName(a.constants); - return abc.constants.getMultinameId(new Multiname(tname.kind, - abc.constants.getStringId(tname.getName(a.constants, new ArrayList()), true), - abc.constants.getNamespaceId(new Namespace(tname.getNamespace(a.constants).kind, abc.constants.getStringId(tname.getNamespace(a.constants).getName(a.constants), true)), 0, true), 0, 0, new ArrayList()), true); - } - } - return 0; - }*/ - @Override - public List generate(SourceGeneratorLocalData localData, TypeItem item) throws CompilationException { - String currentFullClassName = localData.getFullClass(); - - if (localData.documentClass && item.toString().equals(currentFullClassName)) { - int slotId = 0; - int c = abc.findClassByName(currentFullClassName); - for (Trait t : localData.currentScript.traits.traits) { - if (t instanceof TraitClass) { - TraitClass tc = (TraitClass) t; - if (tc.class_info == c) { - slotId = tc.slot_id; - break; - } - } - } - return GraphTargetItem.toSourceMerge(localData, this, ins(new GetGlobalScopeIns()), ins(new GetSlotIns(), slotId)); - } else { - return GraphTargetItem.toSourceMerge(localData, this, ins(new GetLexIns(), resolveType(localData, item, abc, allABCs))); - } - } - - public static int resolveType(SourceGeneratorLocalData localData, GraphTargetItem item, ABC abc, List allABCs) throws CompilationException { - int name_index = 0; - GraphTargetItem typeItem = null; - - if (item instanceof UnresolvedAVM2Item) { - String fullClass = localData.getFullClass(); - item = ((UnresolvedAVM2Item) item).resolve(new TypeItem(fullClass), new ArrayList(), new ArrayList(), abc, allABCs, new ArrayList(), new ArrayList()); - } - if (item instanceof TypeItem) { - typeItem = item; - } else if (item instanceof ApplyTypeAVM2Item) { - typeItem = ((ApplyTypeAVM2Item) item).object; - } else { - throw new CompilationException("Invalid type:" + item.getClass().getName(), 0/*??*/); - } - if (typeItem instanceof UnresolvedAVM2Item) { - String fullClass = localData.getFullClass(); - typeItem = ((UnresolvedAVM2Item) typeItem).resolve(new TypeItem(fullClass), new ArrayList(), new ArrayList(), abc, allABCs, new ArrayList(), new ArrayList()); - } - - if (!(typeItem instanceof TypeItem)) { - throw new CompilationException("Invalid type", 0/*??*/); - } - - TypeItem type = (TypeItem) typeItem; - - String name = type.fullTypeName; - String pkg = ""; - if (name.contains(".")) { - pkg = name.substring(0, name.lastIndexOf('.')); - name = name.substring(name.lastIndexOf('.') + 1); - } - for (InstanceInfo ii : abc.instance_info) { - Multiname mname = abc.constants.constant_multiname.get(ii.name_index); - if (mname != null && name.equals(mname.getName(abc.constants, new ArrayList(), true))) { - if (mname.getNamespace(abc.constants).hasName(pkg, abc.constants)) { - name_index = ii.name_index; - break; - } - } - } - for (int i = 1; i < abc.constants.constant_multiname.size(); i++) { - Multiname mname = abc.constants.constant_multiname.get(i); - if (mname != null && name.equals(mname.getName(abc.constants, new ArrayList(), true))) { - if (mname.getNamespace(abc.constants) != null && pkg.equals(mname.getNamespace(abc.constants).getName(abc.constants, true))) { - name_index = i; - break; - } - } - } - if (name_index == 0) { - if (pkg.isEmpty() && localData.currentScript != null /*FIXME!*/) { - for (Trait t : localData.currentScript.traits.traits) { - if (t.getName(abc).getName(abc.constants, new ArrayList(), true).equals(name)) { - name_index = t.name_index; - break; - } - } - } - if (name_index == 0) { - name_index = abc.constants.getMultinameId(new Multiname(Multiname.QNAME, abc.constants.getStringId(name, true), abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.getStringId(pkg, true)), 0, true), 0, 0, new ArrayList()), true); - } - } - - if (item instanceof ApplyTypeAVM2Item) { - ApplyTypeAVM2Item atype = (ApplyTypeAVM2Item) item; - List params = new ArrayList<>(); - for (GraphTargetItem s : atype.params) { - params.add(resolveType(localData, s, abc, allABCs)); - } - return abc.constants.getMultinameId(new Multiname(Multiname.TYPENAME, 0, 0, 0, name_index, params), true); - } - - return name_index; - } - - @Override - public List generateDiscardValue(SourceGeneratorLocalData localData, GraphTargetItem item) throws CompilationException { - List ret = item.toSource(localData, this); - ret.add(ins(new PopIns())); - return ret; - } -} +/* + * Copyright (C) 2010-2015 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash.abc.avm2.parser.script; + +import com.jpexs.decompiler.flash.SourceGeneratorLocalData; +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; +import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; +import com.jpexs.decompiler.flash.abc.avm2.instructions.InstructionDefinition; +import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.NotIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.ConstructIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.ConstructSuperIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.NewActivationIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.NewCatchIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.NewClassIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.NewFunctionIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.NewObjectIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfFalseIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfStrictNeIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfTrueIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.JumpIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.LookupSwitchIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.GetLocal0Ins; +import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.KillIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.FindPropertyIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.FindPropertyStrictIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetDescendantsIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetGlobalScopeIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetLexIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetScopeObjectIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetSlotIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.HasNext2Ins; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.InitPropertyIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.LabelIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.NextNameIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.NextValueIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ReturnValueIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ReturnVoidIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.SetPropertyIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.SetSlotIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ThrowIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.DupIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PopIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PopScopeIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushByteIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushNullIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushScopeIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushStringIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushUndefinedIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushWithIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.SwapIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.types.CoerceAIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ConvertBIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.xml.CheckFilterIns; +import com.jpexs.decompiler.flash.abc.avm2.model.AVM2Item; +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.FloatValueAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.GetDescendantsAVM2Item; +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.NanAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.NullAVM2Item; +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.WithObjectAVM2Item; +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.IfCondition; +import com.jpexs.decompiler.flash.abc.avm2.parser.AVM2ParseException; +import com.jpexs.decompiler.flash.abc.types.ABCException; +import com.jpexs.decompiler.flash.abc.types.ClassInfo; +import com.jpexs.decompiler.flash.abc.types.InstanceInfo; +import com.jpexs.decompiler.flash.abc.types.MethodBody; +import com.jpexs.decompiler.flash.abc.types.MethodInfo; +import com.jpexs.decompiler.flash.abc.types.Multiname; +import com.jpexs.decompiler.flash.abc.types.Namespace; +import com.jpexs.decompiler.flash.abc.types.NamespaceSet; +import com.jpexs.decompiler.flash.abc.types.ScriptInfo; +import com.jpexs.decompiler.flash.abc.types.ValueKind; +import com.jpexs.decompiler.flash.abc.types.traits.Trait; +import com.jpexs.decompiler.flash.abc.types.traits.TraitClass; +import com.jpexs.decompiler.flash.abc.types.traits.TraitFunction; +import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter; +import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst; +import com.jpexs.decompiler.flash.abc.types.traits.Traits; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.GraphSourceItem; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.Loop; +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.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.LocalData; +import com.jpexs.decompiler.graph.model.NotItem; +import com.jpexs.decompiler.graph.model.OrItem; +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 java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * + * @author JPEXS + */ +public class AVM2SourceGenerator implements SourceGenerator { + + public final ABC abc; + + public List allABCs; + + public static final int MARK_E_START = 0; + + public static final int MARK_E_END = 1; + + public static final int MARK_E_TARGET = 2; + + public static final int MARK_E_FINALLYPART = 3; + + private AVM2Instruction ins(InstructionDefinition def, int... operands) { + return new AVM2Instruction(0, def, operands); + } + + public List generate(SourceGeneratorLocalData localData, GetDescendantsAVM2Item item) throws CompilationException { + int[] nssa = new int[item.openedNamespaces.size()]; + for (int i = 0; i < item.openedNamespaces.size(); i++) { + nssa[i] = item.openedNamespaces.get(i); + } + int nsset = abc.constants.getNamespaceSetId(new NamespaceSet(nssa), true); + + return GraphTargetItem.toSourceMerge(localData, this, + item.object, + ins(new GetDescendantsIns(), abc.constants.getMultinameId(new Multiname(Multiname.MULTINAME, abc.constants.getStringId(item.nameStr, true), 0, nsset, 0, new ArrayList()), true)) + ); + } + + @Override + public List generate(SourceGeneratorLocalData localData, AndItem item) throws CompilationException { + List ret = new ArrayList<>(); + ret.addAll(generateToActionList(localData, item.leftSide)); + if (!("" + item.leftSide.returnType()).equals("Boolean")) { + ret.add(ins(new ConvertBIns())); + } + ret.add(ins(new DupIns())); + List andExpr = generateToActionList(localData, item.rightSide); + andExpr.add(0, ins(new PopIns())); + int andExprLen = insToBytes(andExpr).length; + ret.add(ins(new IfFalseIns(), andExprLen)); + ret.addAll(andExpr); + return ret; + + } + + private byte[] insToBytes(List code) { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + for (AVM2Instruction instruction : code) { + + baos.write(instruction.getBytes()); + + } + return baos.toByteArray(); + } catch (IOException ex) { + Logger.getLogger(AVM2SourceGenerator.class.getName()).log(Level.SEVERE, null, ex); + } + return new byte[0]; + } + + @Override + public List generate(SourceGeneratorLocalData localData, OrItem item) throws CompilationException { + List ret = new ArrayList<>(); + ret.addAll(generateToActionList(localData, item.leftSide)); + if (!("" + item.leftSide.returnType()).equals("Boolean")) { + ret.add(ins(new ConvertBIns())); + } + ret.add(ins(new DupIns())); + List orExpr = generateToActionList(localData, item.rightSide); + orExpr.add(0, ins(new PopIns())); + int orExprLen = insToBytes(orExpr).length; + ret.add(ins(new IfTrueIns(), orExprLen)); + ret.addAll(orExpr); + return ret; + } + + public List toInsList(List items) { + List ret = new ArrayList<>(); + for (GraphSourceItem s : items) { + if (s instanceof AVM2Instruction) { + ret.add((AVM2Instruction) s); + } + } + return ret; + } + + private List nonempty(List list) { + if (list == null) { + return new ArrayList<>(); + } + return list; + } + + private List condition(SourceGeneratorLocalData localData, GraphTargetItem t, int offset) throws CompilationException { + if (t instanceof IfCondition) { + IfCondition ic = (IfCondition) t; + return GraphTargetItem.toSourceMerge(localData, this, ic.getLeftSide(), ic.getRightSide(), ins(ic.getIfDefinition(), offset)); + } + return GraphTargetItem.toSourceMerge(localData, this, t, ins(new IfTrueIns(), offset)); + } + + private List notCondition(SourceGeneratorLocalData localData, GraphTargetItem t, int offset) throws CompilationException { + if (t instanceof IfCondition) { + IfCondition ic = (IfCondition) t; + return GraphTargetItem.toSourceMerge(localData, this, ic.getLeftSide(), ic.getRightSide(), ins(ic.getIfNotDefinition(), offset)); + } + return GraphTargetItem.toSourceMerge(localData, this, t, ins(new IfFalseIns(), offset)); + } + + private List generateIf(SourceGeneratorLocalData localData, GraphTargetItem expression, List onTrueCmds, List onFalseCmds, boolean ternar) throws CompilationException { + List ret = new ArrayList<>(); + //ret.addAll(notCondition(localData, expression)); + List onTrue = null; + List onFalse = null; + if (ternar) { + onTrue = toInsList(onTrueCmds.get(0).toSource(localData, this)); + } else { + onTrue = generateToInsList(localData, onTrueCmds); + } + + if (onFalseCmds != null && !onFalseCmds.isEmpty()) { + if (ternar) { + onFalse = toInsList(onFalseCmds.get(0).toSource(localData, this)); + } else { + onFalse = generateToInsList(localData, onFalseCmds); + } + } + AVM2Instruction ajmp = null; + if (onFalse != null) { + if (!((!nonempty(onTrue).isEmpty()) + && ((onTrue.get(onTrue.size() - 1).definition instanceof ContinueJumpIns) + || ((onTrue.get(onTrue.size() - 1).definition instanceof BreakJumpIns))))) { + ajmp = ins(new JumpIns(), 0); + onTrue.add(ajmp); + } + } + + byte[] onTrueBytes = insToBytes(onTrue); + int onTrueLen = onTrueBytes.length; + + ret.addAll(notCondition(localData, expression, onTrueLen)); + ret.addAll(onTrue); + + if (onFalse != null) { + byte[] onFalseBytes = insToBytes(onFalse); + int onFalseLen = onFalseBytes.length; + if (ajmp != null) { + ajmp.operands[0] = onFalseLen; + } + ret.addAll(onFalse); + } + return ret; + } + + public List generate(SourceGeneratorLocalData localData, XMLFilterAVM2Item item) throws CompilationException { + List ret = new ArrayList<>(); + final Reference counterReg = new Reference<>(0); + final Reference collectionReg = new Reference<>(0); + final Reference xmlListReg = new Reference<>(0); + List xmlListSetTemp = AssignableAVM2Item.setTemp(localData, this, xmlListReg); + ret.addAll(GraphTargetItem.toSourceMerge(localData, this, + ins(new PushByteIns(), 0), + AssignableAVM2Item.setTemp(localData, this, counterReg), + item.object, + ins(new CheckFilterIns()), + NameAVM2Item.generateCoerce(localData, this, TypeItem.UNBOUNDED), + AssignableAVM2Item.setTemp(localData, this, collectionReg), + ins(new GetLexIns(), abc.constants.getMultinameId(new Multiname(Multiname.QNAME, abc.constants.getStringId("XMLList", true), abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.getStringId("", true)), 0, true), 0, 0, new ArrayList()), true)), + ins(new PushStringIns(), abc.constants.getStringId("", true)), + ins(new ConstructIns(), 1), + xmlListSetTemp + )); + final Reference tempVal1 = new Reference<>(0); + final Reference tempVal2 = new Reference<>(0); + + List forBody = toInsList(GraphTargetItem.toSourceMerge(localData, this, + ins(new LabelIns()), + AssignableAVM2Item.getTemp(localData, this, collectionReg), + AssignableAVM2Item.getTemp(localData, this, counterReg), + ins(new NextValueIns()), + AssignableAVM2Item.dupSetTemp(localData, this, tempVal1), + AssignableAVM2Item.dupSetTemp(localData, this, tempVal2), + ins(new PushWithIns()) + )); + localData.scopeStack.add(new LocalRegAVM2Item(null, tempVal2.getVal(), null)); + forBody.addAll(toInsList(item.value.toSource(localData, this))); + List trueBody = new ArrayList<>(); + trueBody.addAll(toInsList(AssignableAVM2Item.getTemp(localData, this, xmlListReg))); + trueBody.addAll(toInsList(AssignableAVM2Item.getTemp(localData, this, counterReg))); + trueBody.addAll(toInsList(AssignableAVM2Item.getTemp(localData, this, tempVal1))); + int[] nss = new int[item.openedNamespaces.size()]; + for (int i = 0; i < item.openedNamespaces.size(); i++) { + nss[i] = item.openedNamespaces.get(i); + } + trueBody.add(ins(new SetPropertyIns(), abc.constants.getMultinameId(new Multiname(Multiname.MULTINAMEL, 0, 0, abc.constants.getNamespaceSetId(new NamespaceSet(nss), true), 0, new ArrayList()), true))); + forBody.add(ins(new IfFalseIns(), insToBytes(trueBody).length)); + forBody.addAll(trueBody); + forBody.add(ins(new PopScopeIns())); + localData.scopeStack.remove(localData.scopeStack.size() - 1); + forBody.addAll(toInsList(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(tempVal2, tempVal1)))); + + int forBodyLen = insToBytes(forBody).length; + AVM2Instruction forwardJump = ins(new JumpIns(), forBodyLen); + ret.add(forwardJump); + + List expr = new ArrayList<>(); + expr.add(ins(new HasNext2Ins(), collectionReg.getVal(), counterReg.getVal())); + AVM2Instruction backIf = ins(new IfTrueIns(), 0); + expr.add(backIf); + + int exprLen = insToBytes(expr).length; + backIf.operands[0] = -(exprLen + forBodyLen); + + ret.addAll(forBody); + ret.addAll(expr); + ret.addAll(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(collectionReg, counterReg))); + ret.addAll(AssignableAVM2Item.getTemp(localData, this, xmlListReg)); + ret.addAll(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(xmlListReg))); + return ret; + } + + @Override + public List generate(SourceGeneratorLocalData localData, IfItem item) throws CompilationException { + return generateIf(localData, item.expression, item.onTrue, item.onFalse, false); + } + + private void fixSwitch(List code, int breakOffset, long loopId) { + fixLoop(code, breakOffset, Integer.MAX_VALUE, loopId); + } + + private void fixLoop(List code, int breakOffset, int continueOffset, long loopId) { + int pos = 0; + for (int a = 0; a < code.size(); a++) { + AVM2Instruction ins = code.get(a); + pos += ins.getBytes().length; + if (ins.definition instanceof JumpIns) { + if (ins.definition instanceof ContinueJumpIns) { + if (continueOffset != Integer.MAX_VALUE) { + ins.operands[0] = (-pos + continueOffset); + code.get(a).definition = new JumpIns(); + } + } + if (ins.definition instanceof BreakJumpIns) { + ins.operands[0] = (-pos + breakOffset); + code.get(a).definition = new JumpIns(); + } + } + } + } + + @Override + public List generate(SourceGeneratorLocalData localData, TernarOpItem item) throws CompilationException { + List onTrue = new ArrayList<>(); + onTrue.add(item.onTrue); + List onFalse = new ArrayList<>(); + onFalse.add(item.onFalse); + return generateIf(localData, item.expression, onTrue, onFalse, true); + } + + @Override + public List generate(SourceGeneratorLocalData localData, WhileItem item) throws CompilationException { + List ret = new ArrayList<>(); + List whileExpr = new ArrayList<>(); + + List ex = new ArrayList<>(item.expression); + GraphTargetItem lastItem = null; + if (!ex.isEmpty()) { + lastItem = ex.remove(ex.size() - 1); + while (lastItem instanceof CommaExpressionItem) { + CommaExpressionItem cei = (CommaExpressionItem) lastItem; + ex.addAll(cei.commands); + lastItem = ex.remove(ex.size() - 1); + } + whileExpr.addAll(generateToInsList(localData, ex)); + } + List whileBody = generateToInsList(localData, item.commands); + AVM2Instruction forwardJump = ins(new JumpIns(), 0); + ret.add(forwardJump); + whileBody.add(0, ins(new LabelIns())); + ret.addAll(whileBody); + int whileBodyLen = insToBytes(whileBody).length; + forwardJump.operands[0] = whileBodyLen; + whileExpr.addAll(toInsList(condition(localData, lastItem, 0))); + int whileExprLen = insToBytes(whileExpr).length; + whileExpr.get(whileExpr.size() - 1).operands[0] = -(whileExprLen + whileBodyLen); //Assuming last is if instruction + ret.addAll(whileExpr); + fixLoop(whileBody, whileBodyLen + whileExprLen, whileBodyLen, item.loop.id); + return ret; + } + + public List generate(SourceGeneratorLocalData localData, ForEachInAVM2Item item) throws CompilationException { + return generateForIn(localData, item.loop, item.expression.collection, (AssignableAVM2Item) item.expression.object, item.commands, true); + } + + public List generate(SourceGeneratorLocalData localData, ForInAVM2Item item) throws CompilationException { + return generateForIn(localData, item.loop, item.expression.collection, (AssignableAVM2Item) item.expression.object, item.commands, false); + } + + public List generateForIn(SourceGeneratorLocalData localData, Loop loop, GraphTargetItem collection, AssignableAVM2Item assignable, List commands, final boolean each) throws CompilationException { + List ret = new ArrayList<>(); + final Reference counterReg = new Reference<>(0); + final Reference collectionReg = new Reference<>(0); + + if (assignable instanceof UnresolvedAVM2Item) { + assignable = (AssignableAVM2Item) ((UnresolvedAVM2Item) assignable).resolved; + } + + ret.addAll(GraphTargetItem.toSourceMerge(localData, this, + ins(new PushByteIns(), 0), + AssignableAVM2Item.setTemp(localData, this, counterReg), + collection, + NameAVM2Item.generateCoerce(localData, this, TypeItem.UNBOUNDED), + AssignableAVM2Item.setTemp(localData, this, collectionReg) + )); + + GraphTargetItem assigned = new GraphTargetItem() { + + @Override + public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { + return null; + } + + @Override + public boolean hasReturnValue() { + return true; + } + + @Override + public GraphTargetItem returnType() { + return TypeItem.UNBOUNDED; + } + + @Override + public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { + return toSourceMerge(localData, generator, + AssignableAVM2Item.getTemp(localData, generator, collectionReg), + AssignableAVM2Item.getTemp(localData, generator, counterReg), + ins(each ? new NextValueIns() : new NextNameIns()) + ); + } + }; + assignable.setAssignedValue(assigned); + + List forBody = toInsList(GraphTargetItem.toSourceMerge(localData, this, + ins(new LabelIns()), + assignable.toSourceIgnoreReturnValue(localData, this) + )); + + forBody.addAll(generateToInsList(localData, commands)); + int forBodyLen = insToBytes(forBody).length; + + AVM2Instruction forwardJump = ins(new JumpIns(), forBodyLen); + ret.add(forwardJump); + + List expr = new ArrayList<>(); + expr.add(ins(new HasNext2Ins(), collectionReg.getVal(), counterReg.getVal())); + AVM2Instruction backIf = ins(new IfTrueIns(), 0); + expr.add(backIf); + + int exprLen = insToBytes(expr).length; + backIf.operands[0] = -(exprLen + forBodyLen); + + fixLoop(forBody, forBodyLen + exprLen, forBodyLen, loop.id); + ret.addAll(forBody); + ret.addAll(expr); + ret.addAll(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(collectionReg, counterReg))); + return ret; + } + + @Override + public List generate(SourceGeneratorLocalData localData, DoWhileItem item) throws CompilationException { + List ret = new ArrayList<>(); + List whileExpr = new ArrayList<>(); + + List ex = new ArrayList<>(item.expression); + GraphTargetItem lastItem = null; + if (!ex.isEmpty()) { + lastItem = ex.remove(ex.size() - 1); + while (lastItem instanceof CommaExpressionItem) { + CommaExpressionItem cei = (CommaExpressionItem) lastItem; + ex.addAll(cei.commands); + lastItem = ex.remove(ex.size() - 1); + } + whileExpr.addAll(generateToInsList(localData, ex)); + } + List dowhileBody = generateToInsList(localData, item.commands); + List labelBody = new ArrayList<>(); + labelBody.add(ins(new LabelIns())); + int labelBodyLen = insToBytes(labelBody).length; + + AVM2Instruction forwardJump = ins(new JumpIns(), labelBodyLen); + ret.add(forwardJump); + ret.addAll(labelBody); + ret.addAll(dowhileBody); + int dowhileBodyLen = insToBytes(dowhileBody).length; + whileExpr.addAll(toInsList(condition(localData, lastItem, 0))); + int dowhileExprLen = insToBytes(whileExpr).length; + whileExpr.get(whileExpr.size() - 1).operands[0] = -(dowhileExprLen + dowhileBodyLen + labelBodyLen); //Assuming last is if instruction + ret.addAll(whileExpr); + fixLoop(dowhileBody, dowhileBodyLen + dowhileExprLen, dowhileBodyLen, item.loop.id); + return ret; + } + + public List generate(SourceGeneratorLocalData localData, WithAVM2Item item) throws CompilationException { + + List ret = new ArrayList<>(); + ret.addAll(item.scope.toSource(localData, this)); + Reference tempReg = new Reference<>(0); + ret.addAll(AssignableAVM2Item.dupSetTemp(localData, this, tempReg)); + localData.scopeStack.add(new WithObjectAVM2Item(null, new LocalRegAVM2Item(null, tempReg.getVal(), null))); + ret.add(ins(new PushWithIns())); + ret.addAll(generate(localData, item.items)); + ret.add(ins(new PopScopeIns())); + ret.addAll(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(tempReg))); + localData.scopeStack.remove(localData.scopeStack.size() - 1); + return ret; + } + + @Override + public List generate(SourceGeneratorLocalData localData, ForItem item) throws CompilationException { + List ret = new ArrayList<>(); + List forExpr = new ArrayList<>(); + + List ex = new ArrayList<>(); + ex.add(item.expression); + + GraphTargetItem lastItem = null; + if (!ex.isEmpty()) { + lastItem = ex.remove(ex.size() - 1); + while (lastItem instanceof CommaExpressionItem) { + CommaExpressionItem cei = (CommaExpressionItem) lastItem; + ex.addAll(cei.commands); + lastItem = ex.remove(ex.size() - 1); + } + forExpr.addAll(generateToInsList(localData, ex)); + } + List forBody = generateToInsList(localData, item.commands); + List forFinalCommands = generateToInsList(localData, item.finalCommands); + + ret.addAll(generateToInsList(localData, item.firstCommands)); + + AVM2Instruction forwardJump = ins(new JumpIns(), 0); + ret.add(forwardJump); + forBody.add(0, ins(new LabelIns())); + ret.addAll(forBody); + ret.addAll(forFinalCommands); + int forBodyLen = insToBytes(forBody).length; + int forFinalCLen = insToBytes(forFinalCommands).length; + forwardJump.operands[0] = forBodyLen + forFinalCLen; + forExpr.addAll(toInsList(condition(localData, lastItem, 0))); + int forExprLen = insToBytes(forExpr).length; + forExpr.get(forExpr.size() - 1).operands[0] = -(forExprLen + forBodyLen + forFinalCLen); //Assuming last is if instruction + ret.addAll(forExpr); + fixLoop(forBody, forBodyLen + forFinalCLen + forExprLen, forBodyLen, item.loop.id); + return ret; + } + + private long uniqLast = 0; + + public String uniqId() { + uniqLast++; + return "" + uniqLast; + } + + @Override + public List generate(SourceGeneratorLocalData localData, SwitchItem item) throws CompilationException { + List ret = new ArrayList<>(); + Reference switchedReg = new Reference<>(0); + AVM2Instruction forwardJump = ins(new JumpIns(), 0); + ret.add(forwardJump); + + List cases = new ArrayList<>(); + cases.addAll(toInsList(new IntegerValueAVM2Item(null, (long) item.caseValues.size()).toSource(localData, this))); + int cLen = insToBytes(cases).length; + List caseLast = new ArrayList<>(); + caseLast.add(0, ins(new JumpIns(), cLen)); + caseLast.addAll(0, toInsList(new IntegerValueAVM2Item(null, (long) item.caseValues.size()).toSource(localData, this))); + int cLastLen = insToBytes(caseLast).length; + caseLast.add(0, ins(new JumpIns(), cLastLen)); + cases.addAll(0, caseLast); + + List preCases = new ArrayList<>(); + preCases.addAll(toInsList(item.switchedObject.toSource(localData, this))); + preCases.addAll(toInsList(AssignableAVM2Item.setTemp(localData, this, switchedReg))); + + for (int i = item.caseValues.size() - 1; i >= 0; i--) { + List sub = new ArrayList<>(); + sub.addAll(toInsList(new IntegerValueAVM2Item(null, (long) i).toSource(localData, this))); + sub.add(ins(new JumpIns(), insToBytes(cases).length)); + int subLen = insToBytes(sub).length; + + cases.addAll(0, sub); + cases.add(0, ins(new IfStrictNeIns(), subLen)); + cases.addAll(0, toInsList(AssignableAVM2Item.getTemp(localData, this, switchedReg))); + cases.addAll(0, toInsList(item.caseValues.get(i).toSource(localData, this))); + } + cases.addAll(0, preCases); + + AVM2Instruction lookupOp = new AVM2Instruction(0, new LookupSwitchIns(), new int[item.caseValues.size() + 1 + 1 + 1]); + cases.addAll(toInsList(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(switchedReg)))); + List bodies = new ArrayList<>(); + List bodiesOffsets = new ArrayList<>(); + int defOffset; + int casesLen = insToBytes(cases).length; + bodies.addAll(generateToInsList(localData, item.defaultCommands)); + bodies.add(0, ins(new LabelIns())); + bodies.add(ins(new BreakJumpIns(item.loop.id), 0)); //There could be two breaks when default clause ends with break, but official compiler does this too, so who cares... + defOffset = -(insToBytes(bodies).length + casesLen); + for (int i = item.caseCommands.size() - 1; i >= 0; i--) { + bodies.addAll(0, generateToInsList(localData, item.caseCommands.get(i))); + bodies.add(0, ins(new LabelIns())); + bodiesOffsets.add(0, -(insToBytes(bodies).length + casesLen)); + } + lookupOp.operands[0] = defOffset; + lookupOp.operands[1] = item.valuesMapping.size(); + lookupOp.operands[2 + item.caseValues.size()] = defOffset; + for (int i = 0; i < item.valuesMapping.size(); i++) { + lookupOp.operands[2 + i] = bodiesOffsets.get(item.valuesMapping.get(i)); + } + + forwardJump.operands[0] = insToBytes(bodies).length; + ret.addAll(bodies); + ret.addAll(cases); + ret.add(lookupOp); + fixSwitch(toInsList(ret), insToBytes(toInsList(ret)).length, uniqLast); + return ret; + } + + @Override + public List generate(SourceGeneratorLocalData localData, NotItem item) throws CompilationException { + /*if (item.getOriginal() instanceof Inverted) { + GraphTargetItem norig = ((Inverted) item).invert(); + return norig.toSource(localData, this); + }*/ + List ret = new ArrayList<>(); + ret.addAll(item.getOriginal().toSource(localData, this)); + ret.add(ins(new NotIns())); + return ret; + } + + @Override + public List generate(SourceGeneratorLocalData localData, DuplicateItem item) { + List ret = new ArrayList<>(); + ret.add(ins(new DupIns())); + return ret; + } + + @Override + public List generate(SourceGeneratorLocalData localData, BreakItem item) { + List ret = new ArrayList<>(); + AVM2Instruction abreak = ins(new BreakJumpIns(item.loopId), 0); + ret.add(abreak); + return ret; + } + + public List generate(SourceGeneratorLocalData localData, FunctionAVM2Item item) throws CompilationException { + List ret = new ArrayList<>(); + int scope = 0; + if (!item.functionName.isEmpty()) { + ret.add(ins(new NewObjectIns(), 0)); + ret.add(ins(new PushWithIns())); + scope = localData.scopeStack.size(); + localData.scopeStack.add(new PropertyAVM2Item(null, item.functionName, abc, allABCs, new ArrayList(), localData.callStack)); + } + ret.add(ins(new NewFunctionIns(), method(true, false, localData.callStack, localData.pkg, item.needsActivation, item.subvariables, 0 /*Set later*/, item.hasRest, item.line, localData.currentClass, null, false, localData, item.paramTypes, item.paramNames, item.paramValues, item.body, item.retType))); + if (!item.functionName.isEmpty()) { + ret.add(ins(new DupIns())); + ret.add(ins(new GetScopeObjectIns(), scope)); + ret.add(ins(new SwapIns())); + ret.add(ins(new SetPropertyIns(), abc.constants.getMultinameId(new Multiname(Multiname.QNAME, abc.constants.getStringId(item.functionName, true), abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.getStringId(localData.pkg, true)), 0, true), 0, 0, new ArrayList()), true))); + ret.add(ins(new PopScopeIns())); + localData.scopeStack.remove(localData.scopeStack.size() - 1); + } + return ret; + } + + private static int currentFinId = 1; + + private static int finId() { + return currentFinId++; + } + + public List generate(SourceGeneratorLocalData localData, TryAVM2Item item) throws CompilationException { + List ret = new ArrayList<>(); + + boolean newFinallyReg = false; + List newex = new ArrayList<>(); + int aloneFinallyEx = -1; + int finallyEx = -1; + for (NameAVM2Item e : item.catchExceptions2) { + ABCException aex = new ABCException(); + aex.name_index = abc.constants.getMultinameId(new Multiname(Multiname.QNAME, abc.constants.getStringId(e.getVariableName(), true), abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.getStringId("", true)), 0, true), 0, 0, new ArrayList()), true); + aex.type_index = typeName(localData, e.type); + newex.add(aex); + } + int finId = 0; + if (item.finallyCommands != null) { + if (item.catchExceptions2.isEmpty()) { + ABCException aex = new ABCException(); + aex.name_index = 0; + aex.type_index = 0; + newex.add(aex); + aloneFinallyEx = newex.size() - 1; + } + ABCException aex = new ABCException(); + aex.name_index = 0; + aex.type_index = 0; + newex.add(aex); + finallyEx = newex.size() - 1; + if (localData.finallyRegister == -1) { + localData.finallyRegister = getFreeRegister(localData); + killRegister(localData, localData.finallyRegister); //reuse for catches + newFinallyReg = true; + } + finId = finId(); + } + + if (finallyEx > -1) { + localData.finallyCatches.add(finId); + } + List tryCmds = generateToInsList(localData, item.tryCommands); + + //int i = firstId + item.catchCommands.size() - 1; + List catches = new ArrayList<>(); + Reference tempReg = new Reference<>(0); + + List currentExceptionIds = new ArrayList<>(); + List> catchCmds = new ArrayList<>(); + for (int c = 0; c < item.catchCommands.size(); c++) { + int i = localData.exceptions.size(); + localData.exceptions.add(newex.get(c)); + + currentExceptionIds.add(i); + + //Reference tempReg=new Reference<>(0); + List catchCmd = new ArrayList<>(); + catchCmd.add(ins(new NewCatchIns(), i)); + catchCmd.addAll(toInsList(AssignableAVM2Item.dupSetTemp(localData, this, tempReg))); + catchCmd.add(ins(new DupIns())); + catchCmd.add(ins(new PushScopeIns())); + catchCmd.add(ins(new SwapIns())); + catchCmd.add(ins(new SetSlotIns(), 1)); + + for (AssignableAVM2Item a : item.catchVariables.get(c)) { + GraphTargetItem r = a; + if (r instanceof UnresolvedAVM2Item) { + r = ((UnresolvedAVM2Item) r).resolvedRoot; + } + if (r instanceof NameAVM2Item) { + NameAVM2Item n = (NameAVM2Item) r; + if (item.catchExceptions2.get(c).getVariableName().equals(n.getVariableName())) { + n.setSlotScope(localData.scopeStack.size()); + } + } + } + localData.scopeStack.add(new LocalRegAVM2Item(null, tempReg.getVal(), null)); + catchCmd.addAll(generateToInsList(localData, item.catchCommands.get(c))); + localData.scopeStack.remove(localData.scopeStack.size() - 1); + catchCmd.add(ins(new PopScopeIns())); + catchCmd.addAll(toInsList(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(tempReg)))); + catchCmds.add(catchCmd); + } + for (int c = item.catchCommands.size() - 1; c >= 0; c--) { + List preCatches = new ArrayList<>(); + /*preCatches.add(ins(new GetLocal0Ins())); + preCatches.add(ins(new PushScopeIns())); + preCatches.add(AssignableAVM2Item.generateGetLoc(localData.activationReg)); + preCatches.add(ins(new PushScopeIns()));*/ + for (GraphTargetItem s : localData.scopeStack) { + preCatches.addAll(toInsList(s.toSource(localData, this))); + if (s instanceof WithObjectAVM2Item) { + preCatches.add(ins(new PushWithIns())); + } else { + preCatches.add(ins(new PushScopeIns())); + } + } + + //catchCmds.add(catchCmd); + preCatches.addAll(catchCmds.get(c)); + catches.addAll(0, preCatches); + catches.add(0, new ExceptionMarkAVM2Instruction(currentExceptionIds.get(c), MARK_E_TARGET)); + catches.add(0, ins(new JumpIns(), insToBytes(catches).length)); + } + + if (aloneFinallyEx > -1) { + localData.exceptions.add(newex.get(aloneFinallyEx)); + aloneFinallyEx = localData.exceptions.size() - 1; + + } + if (finallyEx > -1) { + localData.exceptions.add(newex.get(finallyEx)); + finallyEx = localData.exceptions.size() - 1; + } + + for (int i : currentExceptionIds) { + ret.add(new ExceptionMarkAVM2Instruction(i, MARK_E_START)); + } + if (aloneFinallyEx > -1) { + ret.add(new ExceptionMarkAVM2Instruction(aloneFinallyEx, MARK_E_START)); + } + if (finallyEx > -1) { + ret.add(new ExceptionMarkAVM2Instruction(finallyEx, MARK_E_START)); + } + + ret.addAll(tryCmds); + + for (int i : currentExceptionIds) { + ret.add(new ExceptionMarkAVM2Instruction(i, MARK_E_END)); + } + if (aloneFinallyEx > -1) { + ret.add(new ExceptionMarkAVM2Instruction(aloneFinallyEx, MARK_E_END)); + } + + if (aloneFinallyEx > -1) { + List preCatches = new ArrayList<>(); + for (GraphTargetItem s : localData.scopeStack) { + preCatches.addAll(toInsList(s.toSource(localData, this))); + if (s instanceof WithObjectAVM2Item) { + preCatches.add(ins(new PushWithIns())); + } else { + preCatches.add(ins(new PushScopeIns())); + } + } + preCatches.add(ins(new NewCatchIns(), aloneFinallyEx)); + preCatches.addAll(toInsList(AssignableAVM2Item.dupSetTemp(localData, this, tempReg))); + preCatches.add(ins(new PushScopeIns())); + preCatches.add(ins(new ThrowIns())); + preCatches.add(ins(new PopScopeIns())); + preCatches.addAll(toInsList(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(tempReg)))); + catches.add(ins(new JumpIns(), insToBytes(preCatches).length)); + catches.add(new ExceptionMarkAVM2Instruction(aloneFinallyEx, MARK_E_TARGET)); + catches.addAll(preCatches); + } + AVM2Instruction finSwitch = null; + AVM2Instruction pushDefIns = ins(new PushByteIns(), 0); + + int defPos = 0; + if (finallyEx > -1) { + List preCatches = new ArrayList<>(); + preCatches.add(0, new ExceptionMarkAVM2Instruction(finallyEx, MARK_E_TARGET)); + for (GraphTargetItem s : localData.scopeStack) { + preCatches.addAll(toInsList(s.toSource(localData, this))); + if (s instanceof WithObjectAVM2Item) { + preCatches.add(ins(new PushWithIns())); + } else { + preCatches.add(ins(new PushScopeIns())); + } + } + preCatches.add(ins(new NewCatchIns(), finallyEx)); + preCatches.addAll(toInsList(AssignableAVM2Item.dupSetTemp(localData, this, tempReg))); + preCatches.add(ins(new PushScopeIns())); + preCatches.add(ins(new PopScopeIns())); + Reference tempReg2 = new Reference<>(0); + preCatches.add(ins(new KillIns(), tempReg.getVal())); + preCatches.add(ins(new CoerceAIns())); + preCatches.addAll(toInsList(AssignableAVM2Item.setTemp(localData, this, tempReg2))); + preCatches.add(pushDefIns); + + List finallySwitchCmds = new ArrayList<>(); + + finSwitch = new AVM2Instruction(0, new LookupSwitchIns(), new int[1 + 1 + 1]); + finSwitch.operands[0] = finSwitch.getBytes().length; + finSwitch.operands[1] = 0; //switch cnt + + List preFinallySwitch = new ArrayList<>(); + preFinallySwitch.add(ins(new LabelIns())); + preFinallySwitch.add(ins(new PopIns())); + int preFinallySwitchLen = insToBytes(preFinallySwitch).length; + + finallySwitchCmds.add(ins(new LabelIns())); + finallySwitchCmds.addAll(toInsList(AssignableAVM2Item.getTemp(localData, this, tempReg2))); + finallySwitchCmds.add(ins(new KillIns(), tempReg2.getVal())); + finallySwitchCmds.add(ins(new ThrowIns())); + finallySwitchCmds.add(ins(new PushByteIns(), 255)); + finallySwitchCmds.add(ins(new PopScopeIns())); + finallySwitchCmds.add(ins(new KillIns(), tempReg.getVal())); + + int finSwitchLen = insToBytes(finallySwitchCmds).length; + + preCatches.add(ins(new JumpIns(), preFinallySwitchLen + finSwitchLen)); + AVM2Instruction fjump = ins(new JumpIns(), 0); + fjump.operands[0] = insToBytes(preCatches).length + preFinallySwitchLen + finSwitchLen; + + preCatches.add(0, fjump); + preCatches.add(0, new ExceptionMarkAVM2Instruction(finallyEx, MARK_E_END)); + preCatches.add(0, ins(new PushByteIns(), 255)); + + finallySwitchCmds.add(new ExceptionMarkAVM2Instruction(finallyEx, MARK_E_FINALLYPART)); + + int oldReg = localData.finallyRegister; + localData.finallyRegister = getFreeRegister(localData); + Integer cnt = localData.finallyCounter.get(finId); + if (cnt == null) { + cnt = -1; + } + defPos = cnt; + cnt++; //Skip default clause (throw) + localData.finallyCounter.put(finId, cnt); + finallySwitchCmds.addAll(generateToInsList(localData, item.finallyCommands)); + killRegister(localData, localData.finallyRegister); + localData.finallyRegister = oldReg; + finSwitchLen = insToBytes(finallySwitchCmds).length; + + finSwitch.operands[2] = -finSwitchLen; + preCatches.addAll(preFinallySwitch); + preCatches.addAll(finallySwitchCmds); + preCatches.add(finSwitch); + + catches.addAll(preCatches); + AssignableAVM2Item.killTemp(localData, this, Arrays.asList(tempReg, tempReg2)); + } + + ret.addAll(catches); + //localData.exceptions.addAll(newex); + + if (finallyEx > -1) { + localData.finallyCatches.remove(localData.finallyCatches.size() - 1); + } + if (newFinallyReg) { + localData.finallyRegister = -1; + killRegister(localData, localData.finallyRegister); + } + int pos = 0; + int finallyPos = 0; + int switchPos = 0; + for (int s = 0; s < ret.size(); s++) { + GraphSourceItem src = ret.get(s); + if (src == finSwitch) { + switchPos = pos; + } + if (src instanceof AVM2Instruction) { + AVM2Instruction ins = (AVM2Instruction) src; + if (ins instanceof ExceptionMarkAVM2Instruction) { + ExceptionMarkAVM2Instruction em = (ExceptionMarkAVM2Instruction) ins; + if (em.exceptionId == finallyEx && em.markType == MARK_E_FINALLYPART) { + finallyPos = pos; + ret.remove(s); + s--; + continue; + } + } + pos += ins.getBytes().length; + } + + } + + if (finSwitch != null) { + pos = 0; + int defLoc = finSwitch.operands[2]; + List switchLoc = new ArrayList<>(); + boolean wasDef = false; + for (int s = 0; s < ret.size(); s++) { + GraphSourceItem src = ret.get(s); + if (src instanceof AVM2Instruction) { + AVM2Instruction ins = (AVM2Instruction) src; + if (ins.definition instanceof FinallyJumpIns) { + FinallyJumpIns fji = (FinallyJumpIns) ins.definition; + if (fji.getClauseId() == finId) { + List bet = new ArrayList<>(); + bet.add(ins(new LabelIns())); + bet.add(ins(new PopIns())); + int betLen = insToBytes(bet).length; + if (wasDef) { + ins.operands[0] = 0; + } else { + ins.operands[0] = finallyPos - (pos + ins.getBytes().length); + } + ins.definition = new JumpIns(); + switchLoc.add(pos + ins.getBytes().length + betLen - switchPos); + } + } + pos += ins.getBytes().length; + } + if (defPos == switchLoc.size() - 1) { + switchLoc.add(defLoc); + wasDef = true; + } + } + finSwitch.operands = new int[1 + 1 + switchLoc.size()]; + pushDefIns.operands[0] = defPos + 1; + int afterLoc = finSwitch.getBytes().length; + finSwitch.operands[0] = afterLoc; + finSwitch.operands[1] = switchLoc.size() - 1; + for (int j = 0; j < switchLoc.size(); j++) { + finSwitch.operands[2 + j] = switchLoc.get(j); + } + } + + return ret; + } + + @Override + public List generate(SourceGeneratorLocalData localData, ContinueItem item) { + List ret = new ArrayList<>(); + AVM2Instruction acontinue = ins(new ContinueJumpIns(item.loopId), 0); + ret.add(acontinue); + return ret; + } + + public List generate(SourceGeneratorLocalData localData, ReturnValueAVM2Item item) throws CompilationException { + List ret = new ArrayList<>(); + ret.addAll(item.value.toSource(localData, this)); + if (!localData.finallyCatches.isEmpty()) { + ret.add(ins(new CoerceAIns())); + ret.add(AssignableAVM2Item.generateSetLoc(localData.finallyRegister)); + for (int i = localData.finallyCatches.size() - 1; i >= 0; i--) { + if (i < localData.finallyCatches.size() - 1) { + ret.add(ins(new LabelIns())); + } + int clauseId = localData.finallyCatches.get(i); + Integer cnt = localData.finallyCounter.get(clauseId); + if (cnt == null) { + cnt = -1; + } + cnt++; + localData.finallyCounter.put(clauseId, cnt); + ret.addAll(new IntegerValueAVM2Item(null, (long) cnt).toSource(localData, this)); + ret.add(ins(new FinallyJumpIns(clauseId), 0)); + ret.add(ins(new LabelIns())); + ret.add(ins(new PopIns())); + } + ret.add(ins(new LabelIns())); + ret.add(AssignableAVM2Item.generateGetLoc(localData.finallyRegister)); + ret.add(ins(new KillIns(), localData.finallyRegister)); + } + ret.add(ins(new ReturnValueIns())); + return ret; + } + + public List generate(SourceGeneratorLocalData localData, ReturnVoidAVM2Item item) throws CompilationException { + List ret = new ArrayList<>(); + if (!localData.finallyCatches.isEmpty()) { + + for (int i = 0; i < localData.finallyCatches.size(); i++) { + if (i > 0) { + ret.add(ins(new LabelIns())); + } + int clauseId = localData.finallyCatches.get(i); + Integer cnt = localData.finallyCounter.get(clauseId); + if (cnt == null) { + cnt = -1; + } + cnt++; + localData.finallyCounter.put(clauseId, cnt); + ret.addAll(new IntegerValueAVM2Item(null, (long) cnt).toSource(localData, this)); + ret.add(ins(new FinallyJumpIns(clauseId), 0)); + ret.add(ins(new LabelIns())); + ret.add(ins(new PopIns())); + } + ret.add(ins(new LabelIns())); + } + ret.add(ins(new ReturnVoidIns())); + return ret; + } + + public List generate(SourceGeneratorLocalData localData, ThrowAVM2Item item) throws CompilationException { + List ret = new ArrayList<>(); + ret.addAll(item.value.toSource(localData, this)); + ret.add(ins(new ThrowIns())); + return ret; + } + + private List generateToInsList(SourceGeneratorLocalData localData, List commands) throws CompilationException { + return toInsList(generate(localData, commands)); + } + + private List generateToActionList(SourceGeneratorLocalData localData, GraphTargetItem command) throws CompilationException { + return toInsList(command.toSource(localData, this)); + } + + @Override + public List generate(SourceGeneratorLocalData localData, List commands) throws CompilationException { + List ret = new ArrayList<>(); + for (GraphTargetItem item : commands) { + ret.addAll(item.toSourceIgnoreReturnValue(localData, this)); + } + return ret; + } + + public HashMap getRegisterVars(SourceGeneratorLocalData localData) { + return localData.registerVars; + } + + public void setRegisterVars(SourceGeneratorLocalData localData, HashMap 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 registerVars = getRegisterVars(localData); + int tmpReg = 0; + for (int i = 0; i < 256; i++) { + if (!registerVars.containsValue(i)) { + tmpReg = i; + break; + } + } + return tmpReg; + } + + public AVM2SourceGenerator(ABC abc, List allABCs) { + this.abc = abc; + this.allABCs = allABCs; + } + + public ABC getABC() { + return abc; + } + + public void generateClass(List importedClasses, List sinitVariables, boolean staticNeedsActivation, List staticInit, List openedNamespaces, int namespace, int initScope, String pkg, ClassInfo classInfo, InstanceInfo instanceInfo, SourceGeneratorLocalData localData, boolean isInterface, String name, String superName, GraphTargetItem extendsVal, List implementsStr, GraphTargetItem constructor, List traitItems, Reference class_index) throws AVM2ParseException, CompilationException { + localData.currentClass = name; + localData.pkg = pkg; + List ret = new ArrayList<>(); + if (extendsVal == null && !isInterface) { + extendsVal = new TypeItem("Object"); + } + ParsedSymbol s = null; + + instanceInfo.name_index = traitName(namespace, name); + + Trait[] it = generateTraitsPhase1(name, superName, false, localData, traitItems, instanceInfo.instance_traits, class_index); + Trait[] st = generateTraitsPhase1(name, superName, true, localData, traitItems, classInfo.static_traits, class_index); + generateTraitsPhase2(importedClasses, pkg, traitItems, it, openedNamespaces, localData); + generateTraitsPhase2(importedClasses, pkg, traitItems, st, openedNamespaces, localData); + generateTraitsPhase3(initScope, isInterface, name, superName, false, localData, traitItems, instanceInfo.instance_traits, it, new HashMap(), class_index); + generateTraitsPhase3(initScope, isInterface, name, superName, true, localData, traitItems, classInfo.static_traits, st, new HashMap(), class_index); + int init = 0; + if (constructor == null || isInterface) { + instanceInfo.iinit_index = init = method(false, isInterface, new ArrayList(), pkg, false, new ArrayList(), initScope + 1, false, 0, isInterface ? null : name, extendsVal != null ? extendsVal.toString() : null, true, localData, new ArrayList(), new ArrayList(), new ArrayList(), new ArrayList(), TypeItem.UNBOUNDED/*?? FIXME*/); + } else { + MethodAVM2Item m = (MethodAVM2Item) constructor; + instanceInfo.iinit_index = init = method(false, false, new ArrayList(), pkg, m.needsActivation, m.subvariables, initScope + 1, m.hasRest, m.line, name, extendsVal != null ? extendsVal.toString() : null, true, localData, m.paramTypes, m.paramNames, m.paramValues, m.body, TypeItem.UNBOUNDED/*?? FIXME*/); + } + + //Class initializer + int staticMi = method(false, false, new ArrayList(), pkg, staticNeedsActivation, sinitVariables, initScope + (implementsStr.isEmpty() ? 0 : 1), false, 0, isInterface ? null : name, superName, false, localData, new ArrayList(), new ArrayList(), new ArrayList(), staticInit, TypeItem.UNBOUNDED); + MethodBody sinitBody = abc.findBody(staticMi); + + List sinitcode = new ArrayList<>(); + List initcode = new ArrayList<>(); + for (GraphTargetItem ti : traitItems) { + if ((ti instanceof SlotAVM2Item) || (ti instanceof ConstAVM2Item)) { + GraphTargetItem val = null; + boolean isStatic = false; + int ns = -1; + String tname = null; + boolean isConst = false; + if (ti instanceof SlotAVM2Item) { + val = ((SlotAVM2Item) ti).value; + isStatic = ((SlotAVM2Item) ti).isStatic(); + ns = ((SlotAVM2Item) ti).getNamespace(); + tname = ((SlotAVM2Item) ti).var; + } + if (ti instanceof ConstAVM2Item) { + val = ((ConstAVM2Item) ti).value; + isStatic = ((ConstAVM2Item) ti).isStatic(); + ns = ((ConstAVM2Item) ti).getNamespace(); + tname = ((ConstAVM2Item) ti).var; + isConst = true; + } + if (isStatic && val != null) { + sinitcode.add(ins(new FindPropertyIns(), traitName(ns, tname))); + sinitcode.addAll(toInsList(val.toSource(localData, this))); + sinitcode.add(ins(isConst ? new InitPropertyIns() : new SetPropertyIns(), traitName(ns, tname))); + } + if (!isStatic && val != null) { + //do not init basic values, that can be stored in trait + if (!(val instanceof IntegerValueAVM2Item) && !(val instanceof StringAVM2Item) && !(val instanceof BooleanAVM2Item) && !(val instanceof NullAVM2Item) && !(val instanceof UndefinedAVM2Item)) { + initcode.add(ins(new GetLocal0Ins())); + initcode.addAll(toInsList(val.toSource(localData, this))); + initcode.add(ins(isConst ? new InitPropertyIns() : new SetPropertyIns(), traitName(ns, tname))); + } + } + } + } + MethodBody initBody = null; + if (!isInterface) { + initBody = abc.findBody(init); + initBody.getCode().code.addAll(constructor == null ? 0 : 2, initcode);//after getlocal0,pushscope + + if (sinitBody.getCode().code.get(sinitBody.getCode().code.size() - 1).definition instanceof ReturnVoidIns) { + sinitBody.getCode().code.addAll(2, sinitcode); //after getlocal0,pushscope + } + } + sinitBody.markOffsets(); + sinitBody.autoFillStats(abc, initScope + (implementsStr.isEmpty() ? 0 : 1), true); + + classInfo.cinit_index = staticMi; + if (!isInterface) { + initBody.autoFillStats(abc, initScope + 1, true); + } + instanceInfo.interfaces = new int[implementsStr.size()]; + for (int i = 0; i < implementsStr.size(); i++) { + instanceInfo.interfaces[i] = superIntName(localData, implementsStr.get(i)); + } + } + + @Override + public List 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 cmds = new ArrayList<>(item.commands); + GraphTargetItem lastExpr = cmds.remove(cmds.size() - 1); + List ret = new ArrayList<>(); + ret.addAll(generate(localData, cmds)); + ret.addAll(lastExpr.toSource(localData, this)); + return ret; + } + + public int generateClass(int namespace, ClassInfo ci, InstanceInfo ii, int initScope, String pkg, SourceGeneratorLocalData localData, AVM2Item cls, Reference class_index) throws AVM2ParseException, CompilationException { + /*ClassInfo ci = new ClassInfo(); + InstanceInfo ii = new InstanceInfo(); + abc.class_info.add(ci); + abc.instance_info.add(ii); + */ + if (cls instanceof ClassAVM2Item) { + ClassAVM2Item cai = (ClassAVM2Item) cls; + generateClass(cai.importedClasses, cai.sinitVariables, cai.staticInitActivation, cai.staticInit, cai.openedNamespaces, namespace, initScope, pkg, ci, ii, localData, false, cai.className, cai.extendsOp == null ? "Object" : cai.extendsOp.toString(), cai.extendsOp, cai.implementsOp, cai.constructor, cai.traits, class_index); + if (!cai.isDynamic) { + ii.flags |= InstanceInfo.CLASS_SEALED; + } + if (cai.isFinal) { + ii.flags |= InstanceInfo.CLASS_FINAL; + } + ii.flags |= InstanceInfo.CLASS_PROTECTEDNS; + ii.protectedNS = cai.protectedNs; + } + if (cls instanceof InterfaceAVM2Item) { + InterfaceAVM2Item iai = (InterfaceAVM2Item) cls; + ii.flags |= InstanceInfo.CLASS_INTERFACE; + ii.flags |= InstanceInfo.CLASS_SEALED; + generateClass(iai.importedClasses, new ArrayList(), false, new ArrayList(), iai.openedNamespaces, namespace, initScope, pkg, ci, ii, localData, true, iai.name, null, null, iai.superInterfaces, null, iai.methods, class_index); + } + + return abc.instance_info.size() - 1; + } + + public int traitName(int namespace, String var) { + return abc.constants.getMultinameId(new Multiname(Multiname.QNAME, str(var), namespace, 0, 0, new ArrayList()), true); + } + + public int typeName(SourceGeneratorLocalData localData, GraphTargetItem type) throws CompilationException { + if (type instanceof UnboundedTypeItem) { + return 0; + } + if (("" + type).equals("*")) { + return 0; + } + + return resolveType(localData, type, abc, allABCs); + /* + TypeItem nameItem = (TypeItem) type; + name = nameItem.fullTypeName; + if (name.contains(".")) { + pkg = name.substring(0, name.lastIndexOf('.')); + name = name.substring(name.lastIndexOf('.') + 1); + } + if (!nameItem.subtypes.isEmpty()) { //It's vector => TypeName + List params = new ArrayList<>(); + for (GraphTargetItem p : nameItem.subtypes) { + params.add(typeName(localData, p));//abc.constants.getMultinameId(new Multiname(Multiname.QNAME, str(p), namespace(Namespace.KIND_PACKAGE, ppkg), 0, 0, new ArrayList()), true)); + } + int qname = abc.constants.getMultinameId(new Multiname(Multiname.QNAME, str(name), namespace(Namespace.KIND_PACKAGE, pkg), 0, 0, new ArrayList()), true); + return abc.constants.getMultinameId(new Multiname(Multiname.TYPENAME, 0, 0, 0, qname, params), true); + } else { + return abc.constants.getMultinameId(new Multiname(Multiname.QNAME, str(name), namespace(Namespace.KIND_PACKAGE, pkg), 0, 0, new ArrayList()), true); + }*/ + } + + public int ident(GraphTargetItem name) { + if (name instanceof NameAVM2Item) { + return str(((NameAVM2Item) name).getVariableName()); + } + throw new RuntimeException("no ident"); //FIXME + } + + public int namespace(int nsKind, String name) { + return abc.constants.getNamespaceId(new Namespace(nsKind, str(name)), 0, true); + } + + public int str(String name) { + return abc.constants.getStringId(name, true); + } + + public int propertyName(GraphTargetItem name) { + if (name instanceof NameAVM2Item) { + NameAVM2Item va = (NameAVM2Item) name; + return abc.constants.getMultinameId(new Multiname(Multiname.QNAME, str(va.getVariableName()), namespace(Namespace.KIND_PACKAGE, ""), 0, 0, new ArrayList()), true); + } + throw new RuntimeException("no prop"); //FIXME + } + + public int getFreeRegister(SourceGeneratorLocalData localData) { + for (int i = 0;; i++) { + if (!localData.registerVars.containsValue(i)) { + localData.registerVars.put("__TEMP__" + i, i); + return i; + } + } + } + + public boolean killRegister(SourceGeneratorLocalData localData, int i) { + String key = null; + for (String k : localData.registerVars.keySet()) { + if (localData.registerVars.get(k) == i) { + key = k; + break; + } + } + if (key != null) { + localData.registerVars.remove(key); + return true; + } + return false; + } + + public int method(boolean subMethod, boolean isInterface, List callStack, String pkg, boolean needsActivation, List subvariables, int initScope, boolean hasRest, int line, String className, String superType, boolean constructor, SourceGeneratorLocalData localData, List paramTypes, List paramNames, List paramValues, List body, GraphTargetItem retType) throws CompilationException { + //Reference hasArgs = new Reference<>(Boolean.FALSE); + //calcRegisters(localData,needsActivation,paramNames,subvariables,body, hasArgs); + SourceGeneratorLocalData newlocalData = new SourceGeneratorLocalData(new HashMap(), 1, true, 0); + newlocalData.currentClass = className; + newlocalData.pkg = localData.pkg; + newlocalData.callStack.addAll(localData.callStack); + newlocalData.traitUsages = localData.traitUsages; + newlocalData.currentScript = localData.currentScript; + newlocalData.documentClass = localData.documentClass; + newlocalData.subMethod = subMethod; + localData = newlocalData; + + localData.activationReg = 0; + + for (int i = 0; i < subvariables.size(); i++) { + AssignableAVM2Item an = subvariables.get(i); + if (an instanceof UnresolvedAVM2Item) { + UnresolvedAVM2Item n = (UnresolvedAVM2Item) an; + if (n.resolved == null) { + String fullClass = localData.getFullClass(); + GraphTargetItem res = n.resolve(new TypeItem(fullClass), paramTypes, paramNames, abc, allABCs, callStack, subvariables); + if (res instanceof AssignableAVM2Item) { + subvariables.set(i, (AssignableAVM2Item) res); + } else { + subvariables.remove(i); + i--; + } + } + } + } + + for (int t = 0; t < paramTypes.size(); t++) { + GraphTargetItem an = paramTypes.get(t); + if (an instanceof UnresolvedAVM2Item) { + UnresolvedAVM2Item n = (UnresolvedAVM2Item) an; + if (n.resolved == null) { + String fullClass = localData.getFullClass(); + GraphTargetItem res = n.resolve(new TypeItem(fullClass), paramTypes, paramNames, abc, allABCs, callStack, subvariables); + paramTypes.set(t, res); + } + } + } + + boolean hasArguments = false; + List slotNames = new ArrayList<>(); + List slotTypes = new ArrayList<>(); + slotNames.add("--first"); + slotTypes.add("-"); + + List registerNames = new ArrayList<>(); + List registerTypes = new ArrayList<>(); + if (className != null) { + String fullClassName = pkg == null || pkg.isEmpty() ? className : pkg + "." + className; + registerTypes.add(fullClassName); + localData.scopeStack.add(new LocalRegAVM2Item(null, registerNames.size(), null)); + registerNames.add("this"); + + } else { + registerTypes.add("global"); + registerNames.add("this"); + } + for (GraphTargetItem t : paramTypes) { + registerTypes.add(t.toString()); + slotTypes.add(t.toString()); + } + registerNames.addAll(paramNames); + slotNames.addAll(paramNames); + /*for (GraphTargetItem p : paramTypes) { + slotTypes.add("" + p); + }*/ + if (hasRest) { + slotTypes.add("*"); + } + localData.registerVars.clear(); + for (AssignableAVM2Item an : subvariables) { + if (an instanceof NameAVM2Item) { + NameAVM2Item n = (NameAVM2Item) an; + if (n.getVariableName().equals("arguments") & !n.isDefinition()) { + registerNames.add("arguments"); + registerTypes.add("Object"); + hasArguments = true; + break; + } + } + } + int paramRegCount = registerNames.size(); + + if (needsActivation) { + registerNames.add("+$activation"); + localData.activationReg = registerNames.size() - 1; + registerTypes.add("Object"); + localData.scopeStack.add(new LocalRegAVM2Item(null, localData.activationReg, null)); + } + + String mask = Configuration.registerNameFormat.get(); + mask = mask.replace("%d", "([0-9]+)"); + Pattern pat = Pattern.compile(mask); + + //Two rounds + for (int round = 1; round <= 2; round++) { + for (AssignableAVM2Item an : subvariables) { + if (an instanceof NameAVM2Item) { + NameAVM2Item n = (NameAVM2Item) an; + if (n.isDefinition()) { + if (!needsActivation || (n.getSlotScope() <= 0)) { + String varName = n.getVariableName(); + Matcher m = pat.matcher(varName); + //In first round, make all register that match standard loc_xx register + if ((round == 1) && (m.matches())) { + String regIndexStr = m.group(1); + int regIndex = Integer.parseInt(regIndexStr); + while (registerNames.size() <= regIndex + 1) { + String standardName = String.format(mask, registerNames.size() - 1); + registerNames.add(standardName); + registerTypes.add("*"); + slotNames.add(standardName); + slotTypes.add("*"); + } + registerNames.set(regIndex, varName); + registerTypes.set(regIndex, varName); + slotNames.set(regIndex, varName); + slotTypes.set(regIndex, varName); + } //in second round the rest + else if (round == 2 && !m.matches()) { + registerNames.add(n.getVariableName()); + registerTypes.add(n.type.toString()); + slotNames.add(n.getVariableName()); + slotTypes.add(n.type.toString()); + } + } + } + } + } + } + + int slotScope = subMethod ? 0 : 1; + + for (AssignableAVM2Item an : subvariables) { + if (an instanceof NameAVM2Item) { + NameAVM2Item n = (NameAVM2Item) an; + String variableName = n.getVariableName(); + if (variableName != null) { + boolean isThisOrSuper = variableName.equals("this") || variableName.equals("super"); + if (!isThisOrSuper && needsActivation) { + if (n.getSlotNumber() <= 0) { + n.setSlotNumber(slotNames.indexOf(variableName)); + n.setSlotScope(slotScope); + } + } else { + if (isThisOrSuper) { + n.setRegNumber(0); + } else { + n.setRegNumber(registerNames.indexOf(variableName)); + } + } + } + } + } + + for (int i = 0; i < registerNames.size(); i++) { + if (needsActivation && i > localData.activationReg) { + break; + } + localData.registerVars.put(registerNames.get(i), i); + } + List declarations = new ArrayList<>(); + loopn: + for (AssignableAVM2Item an : subvariables) { + if (an instanceof NameAVM2Item) { + NameAVM2Item n = (NameAVM2Item) an; + + if (needsActivation) { + if (n.getSlotScope() != slotScope) { + continue; + } else { + if (n.getSlotNumber() < paramRegCount) { + continue; + } + } + } + for (NameAVM2Item d : declarations) { + if (n.getVariableName() != null && n.getVariableName().equals(d.getVariableName())) { + continue loopn; + } + } + + for (GraphTargetItem it : body) { //search first level of commands + if (it instanceof NameAVM2Item) { + NameAVM2Item n2 = (NameAVM2Item) it; + if (n2.isDefinition() && n2.getAssignedValue() != null && n2.getVariableName().equals(n.getVariableName())) { + continue loopn; + } + if (!n2.isDefinition() && n2.getVariableName() != null && n2.getVariableName().equals(n.getVariableName())) { //used earlier than defined + break; + } + } + } + if (n.unresolved) { + continue; + } + if (n.redirect != null) { + continue; + } + if (n.getNs() != null) { + continue; + } + + String variableName = n.getVariableName(); + if ("this".equals(variableName) || "super".equals(variableName) || paramNames.contains(variableName) || "arguments".equals(variableName)) { + continue; + } + + NameAVM2Item d = new NameAVM2Item(n.type, n.line, n.getVariableName(), NameAVM2Item.getDefaultValue("" + n.type), true, n.openedNamespaces); + //no index + if (needsActivation) { + if (d.getSlotNumber() <= 0) { + d.setSlotNumber(n.getSlotNumber()); + d.setSlotScope(n.getSlotScope()); + } + } else { + d.setRegNumber(n.getRegNumber()); + } + declarations.add(d); + } + } + + int[] param_types = new int[paramTypes.size()]; + ValueKind[] optional = new ValueKind[paramValues.size()]; + //int[] param_names = new int[paramNames.size()]; + for (int i = 0; i < paramTypes.size(); i++) { + param_types[i] = typeName(localData, paramTypes.get(i)); + //param_names[i] = str(paramNames.get(i)); + } + + for (int i = 0; i < paramValues.size(); i++) { + optional[i] = getValueKind(Namespace.KIND_NAMESPACE/*FIXME*/, paramTypes.get(paramTypes.size() - paramValues.size() + i), paramValues.get(i)); + if (optional[i] == null) { + throw new CompilationException("Default value must be compiletime constant", line); + } + } + + MethodInfo mi = new MethodInfo(param_types, constructor ? 0 : typeName(localData, retType), 0/*name_index*/, 0, optional, new int[0]/*no param_names*/); + if (hasArguments) { + mi.setFlagNeed_Arguments(); + } + //No param names like in official + /* + if (!paramNames.isEmpty()) { + mi.setFlagHas_paramnames(); + }*/ + if (!paramValues.isEmpty()) { + mi.setFlagHas_optional(); + } + if (hasRest) { + mi.setFlagNeed_rest(); + } + + int mindex; + if (!isInterface) { + MethodBody mbody = new MethodBody(); + + if (needsActivation) { + int slotId = 1; + for (int i = 1; i < slotNames.size(); i++) { + TraitSlotConst tsc = new TraitSlotConst(); + tsc.slot_id = slotId++; + tsc.name_index = abc.constants.getMultinameId(new Multiname(Multiname.QNAME, abc.constants.getStringId(slotNames.get(i), true), abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE_INTERNAL, abc.constants.getStringId(pkg, true)), 0, true), 0, 0, new ArrayList()), true); + tsc.type_index = typeName(localData, new TypeItem(slotTypes.get(i))); + mbody.traits.traits.add(tsc); + } + for (int i = 1; i < paramRegCount; i++) { + NameAVM2Item param = new NameAVM2Item(new TypeItem(registerTypes.get(i)), 0, registerNames.get(i), null, false, new ArrayList()); + param.setRegNumber(i); + NameAVM2Item d = new NameAVM2Item(new TypeItem(registerTypes.get(i)), 0, registerNames.get(i), param, true, new ArrayList()); + d.setSlotScope(slotScope); + d.setSlotNumber(slotNames.indexOf(registerNames.get(i))); + declarations.add(d); + } + } + if (body != null) { + body.addAll(0, declarations); + } + + localData.exceptions = new ArrayList<>(); + localData.callStack.add(mbody); + List src = body == null ? new ArrayList() : generate(localData, body); + + mbody.method_info = abc.addMethodInfo(mi); + mi.setBody(mbody); + List mbodyCode = toInsList(src); + mbody.setCode(new AVM2Code()); + mbody.getCode().code = mbodyCode; + + if (needsActivation) { + if (localData.traitUsages.containsKey(mbody)) { + List usages = localData.traitUsages.get(mbody); + for (int i = 0; i < mbody.traits.traits.size(); i++) { + if (usages.contains(i)) { + TraitSlotConst tsc = (TraitSlotConst) mbody.traits.traits.get(i); + GraphTargetItem type = TypeItem.UNBOUNDED; + if (tsc.type_index > 0) { + type = new TypeItem(abc.constants.constant_multiname.get(tsc.type_index).getNameWithNamespace(abc.constants, true)); + } + NameAVM2Item d = new NameAVM2Item(type, 0, tsc.getName(abc).getName(abc.constants, new ArrayList(), true), NameAVM2Item.getDefaultValue("" + type), true, new ArrayList()); + d.setSlotNumber(tsc.slot_id); + d.setSlotScope(slotScope); + mbodyCode.addAll(0, toInsList(d.toSourceIgnoreReturnValue(localData, this))); + } + } + } + + List acts = new ArrayList<>(); + acts.add(ins(new NewActivationIns())); + acts.add(ins(new DupIns())); + acts.add(AssignableAVM2Item.generateSetLoc(localData.activationReg)); + acts.add(ins(new PushScopeIns())); + + mbodyCode.addAll(0, acts); + } + + if (constructor) { + List abcs = new ArrayList<>(); + abcs.add(abc); + abcs.addAll(allABCs); + + int parentConstMinAC = 0; + + for (ABC a : abcs) { + int ci = a.findClassByName(superType); + if (ci > -1) { + MethodInfo pmi = a.method_info.get(a.instance_info.get(ci).iinit_index); + parentConstMinAC = pmi.param_types.length; + if (pmi.flagHas_optional()) { + parentConstMinAC -= pmi.optional.length; + } + + } + } + int ac = -1; + for (AVM2Instruction ins : mbodyCode) { + if (ins.definition instanceof ConstructSuperIns) { + ac = ins.operands[0]; + if (parentConstMinAC > ac) { + throw new CompilationException("Parent constructor call requires different number of arguments", line); + } + + } + } + if (ac == -1) { + if (parentConstMinAC == 0) { + mbodyCode.add(0, new AVM2Instruction(0, new GetLocal0Ins(), null)); + mbodyCode.add(1, new AVM2Instruction(0, new ConstructSuperIns(), new int[]{0})); + + } else { + throw new CompilationException("Parent constructor must be called", line); + } + } + } + if (className != null && !subMethod) { + mbodyCode.add(0, new AVM2Instruction(0, new GetLocal0Ins(), null)); + mbodyCode.add(1, new AVM2Instruction(0, new PushScopeIns(), null)); + } + boolean addRet = false; + if (!mbodyCode.isEmpty()) { + InstructionDefinition lastDef = mbodyCode.get(mbodyCode.size() - 1).definition; + if (!((lastDef instanceof ReturnVoidIns) || (lastDef instanceof ReturnValueIns))) { + addRet = true; + } + } else { + addRet = true; + } + if (addRet) { + if (retType.toString().equals("*") || retType.toString().equals("void") || constructor) { + mbodyCode.add(new AVM2Instruction(0, new ReturnVoidIns(), null)); + } else { + mbodyCode.add(new AVM2Instruction(0, new PushUndefinedIns(), null)); + mbodyCode.add(new AVM2Instruction(0, new ReturnValueIns(), null)); + } + } + mbody.exceptions = localData.exceptions.toArray(new ABCException[localData.exceptions.size()]); + int offset = 0; + for (int i = 0; i < mbodyCode.size(); i++) { + AVM2Instruction ins = mbodyCode.get(i); + if (ins instanceof ExceptionMarkAVM2Instruction) { + ExceptionMarkAVM2Instruction m = (ExceptionMarkAVM2Instruction) ins; + switch (m.markType) { + case MARK_E_START: + mbody.exceptions[m.exceptionId].start = offset; + break; + case MARK_E_END: + mbody.exceptions[m.exceptionId].end = offset; + break; + case MARK_E_TARGET: + mbody.exceptions[m.exceptionId].target = offset; + break; + } + mbodyCode.remove(i); + i--; + continue; + } + offset += ins.getBytes().length; + } + + mbody.markOffsets(); + mbody.autoFillStats(abc, initScope, className != null); + abc.addMethodBody(mbody); + mindex = mbody.method_info; + } else { + mindex = abc.addMethodInfo(mi); + } + + return mindex; + } + + public ValueKind getValueKind(int ns, GraphTargetItem type, GraphTargetItem val) { + + if (val instanceof BooleanAVM2Item) { + BooleanAVM2Item bi = (BooleanAVM2Item) val; + if (bi.value) { + return new ValueKind(0, ValueKind.CONSTANT_True); + } else { + return new ValueKind(0, ValueKind.CONSTANT_False); + } + } + + boolean isNs = false; + if (type instanceof NameAVM2Item) { + if (((NameAVM2Item) type).getVariableName().equals("namespace")) { + isNs = true; + } + } + + if ((type instanceof TypeItem) && (((TypeItem) type).fullTypeName.equals("Namespace"))) { + isNs = true; + } + + if (val instanceof StringAVM2Item) { + StringAVM2Item sval = (StringAVM2Item) val; + if (isNs) { + return new ValueKind(namespace(Namespace.KIND_NAMESPACE, sval.value), ValueKind.CONSTANT_Namespace); + } else { + return new ValueKind(str(sval.value), ValueKind.CONSTANT_Utf8); + } + } + if (val instanceof IntegerValueAVM2Item) { + return new ValueKind(abc.constants.getIntId(((IntegerValueAVM2Item) val).value, true), ValueKind.CONSTANT_Int); + } + if (val instanceof FloatValueAVM2Item) { + return new ValueKind(abc.constants.getDoubleId(((FloatValueAVM2Item) val).value, true), ValueKind.CONSTANT_Double); + } + if (val instanceof NanAVM2Item) { + return new ValueKind(abc.constants.getDoubleId(Double.NaN, true), ValueKind.CONSTANT_Double); + } + if (val instanceof NullAVM2Item) { + return new ValueKind(0, ValueKind.CONSTANT_Null); + } + if (val instanceof UndefinedAVM2Item) { + return new ValueKind(0, ValueKind.CONSTANT_Undefined); + } + return null; + } + + private int genNs(List importedClasses, String pkg, String custom, int namespace, List openedNamespaces, SourceGeneratorLocalData localData, int line) throws CompilationException { + if (custom != null) { + PropertyAVM2Item prop = new PropertyAVM2Item(null, custom, abc, allABCs, openedNamespaces, new ArrayList()); + Reference value = new Reference<>(null); + prop.resolve(localData, new Reference(null), new Reference(null), new Reference<>(0), value); + boolean resolved = true; + if (value.getVal() == null) { + resolved = false; + } + if (!resolved) { + + String customPkg = ""; + String fullCustom = ""; + for (String imp : importedClasses) { + if (imp.endsWith("." + custom)) { + customPkg = imp.substring(0, imp.lastIndexOf('.')); + fullCustom = imp; + break; + } + } + + List aas = new ArrayList<>(); + aas.add(abc); + aas.addAll(allABCs); + for (ABC a : aas) { + for (ScriptInfo si : a.script_info) { + for (Trait t : si.traits.traits) { + Multiname m = t.getName(a); + if (fullCustom.equals(m.getNameWithNamespace(a.constants, true))) { + if (t instanceof TraitSlotConst) { + if (((TraitSlotConst) t).isNamespace()) { + Namespace ns = a.constants.getNamespace(((TraitSlotConst) t).value_index); + return abc.constants.getNamespaceId(new Namespace(ns.kind, abc.constants.getStringId(ns.getName(a.constants, true), true)), 0, true); + } + } + } + } + } + } + + throw new CompilationException("Namespace not defined", line); + } + namespace = value.getVal().value_index; + } + return namespace; + } + + public void generateTraitsPhase2(List importedClasses, String pkg, List items, Trait[] traits, List openedNamespaces, SourceGeneratorLocalData localData) throws CompilationException { + for (int k = 0; k < items.size(); k++) { + GraphTargetItem item = items.get(k); + if (traits[k] == null) { + continue; + } else if (item instanceof InterfaceAVM2Item) { + traits[k].name_index = traitName(((InterfaceAVM2Item) item).namespace, ((InterfaceAVM2Item) item).name); + } else if (item instanceof ClassAVM2Item) { + traits[k].name_index = traitName(((ClassAVM2Item) item).namespace, ((ClassAVM2Item) item).className); + } else if ((item instanceof MethodAVM2Item) || (item instanceof GetterAVM2Item) || (item instanceof SetterAVM2Item)) { + traits[k].name_index = traitName(genNs(importedClasses, pkg, ((MethodAVM2Item) item).customNamespace, ((MethodAVM2Item) item).namespace, openedNamespaces, localData, ((MethodAVM2Item) item).line), ((MethodAVM2Item) item).functionName); + } else if (item instanceof FunctionAVM2Item) { + traits[k].name_index = traitName(((FunctionAVM2Item) item).namespace, ((FunctionAVM2Item) item).functionName); + } else if (item instanceof ConstAVM2Item) { + traits[k].name_index = traitName(genNs(importedClasses, pkg, ((ConstAVM2Item) item).customNamespace, ((ConstAVM2Item) item).getNamespace(), openedNamespaces, localData, ((ConstAVM2Item) item).line), ((ConstAVM2Item) item).var); + } else if (item instanceof SlotAVM2Item) { + traits[k].name_index = traitName(genNs(importedClasses, pkg, ((SlotAVM2Item) item).customNamespace, ((SlotAVM2Item) item).getNamespace(), openedNamespaces, localData, ((SlotAVM2Item) item).line), ((SlotAVM2Item) item).var); + } + } + + for (int k = 0; k < items.size(); k++) { + GraphTargetItem item = items.get(k); + if (traits[k] == null) { + continue; + } + if (item instanceof ClassAVM2Item) { + InstanceInfo instanceInfo = abc.instance_info.get(((TraitClass) traits[k]).class_info); + instanceInfo.name_index = abc.constants.getMultinameId(new Multiname(Multiname.QNAME, abc.constants.getStringId(((ClassAVM2Item) item).className, true), + abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.getStringId(pkg, true)), 0, true), 0, 0, new ArrayList()), true); + + if (((ClassAVM2Item) item).extendsOp != null) { + instanceInfo.super_index = typeName(localData, ((ClassAVM2Item) item).extendsOp); + } else { + instanceInfo.super_index = abc.constants.getMultinameId(new Multiname(Multiname.QNAME, str("Object"), namespace(Namespace.KIND_PACKAGE, ""), 0, 0, new ArrayList()), true); + } + instanceInfo.interfaces = new int[((ClassAVM2Item) item).implementsOp.size()]; + for (int i = 0; i < ((ClassAVM2Item) item).implementsOp.size(); i++) { + instanceInfo.interfaces[i] = superIntName(localData, ((ClassAVM2Item) item).implementsOp.get(i)); + } + } + if (item instanceof InterfaceAVM2Item) { + InstanceInfo instanceInfo = abc.instance_info.get(((TraitClass) traits[k]).class_info); + instanceInfo.name_index = abc.constants.getMultinameId(new Multiname(Multiname.QNAME, abc.constants.getStringId(((InterfaceAVM2Item) item).name, true), + abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.getStringId(pkg, true)), 0, true), 0, 0, new ArrayList()), true); + + instanceInfo.interfaces = new int[((InterfaceAVM2Item) item).superInterfaces.size()]; + for (int i = 0; i < ((InterfaceAVM2Item) item).superInterfaces.size(); i++) { + GraphTargetItem un = ((InterfaceAVM2Item) item).superInterfaces.get(i); + instanceInfo.interfaces[i] = superIntName(localData, un); + } + } + } + } + + public int superIntName(SourceGeneratorLocalData localData, GraphTargetItem un) throws CompilationException { + if (un instanceof UnresolvedAVM2Item) { + ((UnresolvedAVM2Item) un).resolve(null, new ArrayList(), new ArrayList(), abc, allABCs, new ArrayList(), new ArrayList()); + un = ((UnresolvedAVM2Item) un).resolved; + } + if (!(un instanceof TypeItem)) { //not applyType + throw new CompilationException("Invalid type", 0); + } + TypeItem sup = (TypeItem) un; + int propId = resolveType(localData, sup, abc, allABCs); + int[] nss = new int[]{abc.constants.constant_multiname.get(propId).namespace_index}; + return abc.constants.getMultinameId(new Multiname(Multiname.MULTINAME, abc.constants.constant_multiname.get(propId).name_index, 0, abc.constants.getNamespaceSetId(new NamespaceSet(nss), true), 0, new ArrayList()), true); + + } + + public void generateTraitsPhase3(int methodInitScope, boolean isInterface, String className, String superName, boolean generateStatic, SourceGeneratorLocalData localData, List items, Traits ts, Trait[] traits, Map initScopes, Reference class_index) throws AVM2ParseException, CompilationException { + //Note: Names must be generated first before accesed in inner subs + for (int k = 0; k < items.size(); k++) { + GraphTargetItem item = items.get(k); + if (traits[k] == null) { + continue; + } + if (item instanceof InterfaceAVM2Item) { + generateClass(((InterfaceAVM2Item) item).namespace, abc.class_info.get(((TraitClass) traits[k]).class_info), abc.instance_info.get(((TraitClass) traits[k]).class_info), initScopes.get(traits[k]), ((InterfaceAVM2Item) item).pkg, localData, (InterfaceAVM2Item) item, class_index); + } + + if (item instanceof ClassAVM2Item) { + generateClass(((ClassAVM2Item) item).namespace, abc.class_info.get(((TraitClass) traits[k]).class_info), abc.instance_info.get(((TraitClass) traits[k]).class_info), initScopes.get(traits[k]), ((ClassAVM2Item) item).pkg, localData, (ClassAVM2Item) item, class_index); + } + if ((item instanceof MethodAVM2Item) || (item instanceof GetterAVM2Item) || (item instanceof SetterAVM2Item)) { + MethodAVM2Item mai = (MethodAVM2Item) item; + if (mai.isStatic() != generateStatic) { + continue; + } + ((TraitMethodGetterSetter) traits[k]).method_info = method(false, isInterface, new ArrayList(), mai.pkg, mai.needsActivation, mai.subvariables, methodInitScope + (mai.isStatic() ? 0 : 1), mai.hasRest, mai.line, className, superName, false, localData, mai.paramTypes, mai.paramNames, mai.paramValues, mai.body, mai.retType); + } else if (item instanceof FunctionAVM2Item) { + FunctionAVM2Item fai = (FunctionAVM2Item) item; + ((TraitFunction) traits[k]).method_info = method(false, isInterface, new ArrayList(), fai.pkg, fai.needsActivation, fai.subvariables, methodInitScope, fai.hasRest, fai.line, className, superName, false, localData, fai.paramTypes, fai.paramNames, fai.paramValues, fai.body, fai.retType); + } + } + } + + public Trait[] generateTraitsPhase1(String className, String superName, boolean generateStatic, SourceGeneratorLocalData localData, List items, Traits ts, Reference classIndex) throws AVM2ParseException, CompilationException { + Trait[] traits = new Trait[items.size()]; + int slot_id = 1; + int disp_id = 3; //1 and 2 are for constructor + for (int k = 0; k < items.size(); k++) { + GraphTargetItem item = items.get(k); + if (item instanceof InterfaceAVM2Item) { + TraitClass tc = new TraitClass(); + ClassInfo ci = new ClassInfo(); + InstanceInfo ii = new InstanceInfo(); + /*abc.class_info.add(ci); + abc.instance_info.add(ii);*/ + tc.class_info = classIndex.getVal(); + abc.addClass(ci, ii, classIndex.getVal()); + classIndex.setVal(classIndex.getVal() + 1); + ii.flags |= InstanceInfo.CLASS_INTERFACE; + //tc.class_info = abc.instance_info.size() - 1; + tc.kindType = Trait.TRAIT_CLASS; + //tc.name_index = traitName(((InterfaceAVM2Item) item).namespace, ((InterfaceAVM2Item) item).name); + tc.slot_id = 0; //? + ts.traits.add(tc); + traits[k] = tc; + } + + if (item instanceof ClassAVM2Item) { + TraitClass tc = new TraitClass(); + ClassInfo ci = new ClassInfo(); + InstanceInfo ii = new InstanceInfo(); + /*abc.class_info.add(ci); + abc.instance_info.add(instanceInfo);*/ + tc.class_info = classIndex.getVal(); + abc.addClass(ci, ii, classIndex.getVal()); + classIndex.setVal(classIndex.getVal() + 1); + //tc.class_info = abc.instance_info.size() - 1; + + /*instanceInfo.name_index = abc.constants.addMultiname(new Multiname(Multiname.QNAME, abc.constants.getStringId(((ClassAVM2Item) item).className, true), + abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.getStringId(pkg.packageName, true)), 0, true), 0, 0, new ArrayList())); + */ + + /*if (((ClassAVM2Item) item).extendsOp != null) { + instanceInfo.super_index = typeName(localData, ((ClassAVM2Item) item).extendsOp); + } else { + instanceInfo.super_index = abc.constants.getMultinameId(new Multiname(Multiname.QNAME, str("Object"), namespace(Namespace.KIND_PACKAGE, ""), 0, 0, new ArrayList()), true); + }*/ + tc.kindType = Trait.TRAIT_CLASS; + // tc.name_index = traitName(((ClassAVM2Item) item).namespace, ((ClassAVM2Item) item).className); + tc.slot_id = slot_id++; + ts.traits.add(tc); + traits[k] = tc; + + } + if ((item instanceof SlotAVM2Item) || (item instanceof ConstAVM2Item)) { + TraitSlotConst tsc = new TraitSlotConst(); + tsc.kindType = (item instanceof SlotAVM2Item) ? Trait.TRAIT_SLOT : Trait.TRAIT_CONST; + String var = null; + GraphTargetItem val = null; + GraphTargetItem type = null; + boolean isNamespace = false; + int namespace = 0; + boolean isStatic = false; + if (item instanceof SlotAVM2Item) { + SlotAVM2Item sai = (SlotAVM2Item) item; + if (sai.isStatic() != generateStatic) { + continue; + } + var = sai.var; + val = sai.value; + type = sai.type; + isStatic = sai.isStatic(); + namespace = sai.getNamespace(); + } + if (item instanceof ConstAVM2Item) { + ConstAVM2Item cai = (ConstAVM2Item) item; + if (cai.isStatic() != generateStatic) { + continue; + } + var = cai.var; + val = cai.value; + type = cai.type; + namespace = cai.getNamespace(); + isNamespace = type.toString().equals("Namespace"); + isStatic = cai.isStatic(); + } + if (isNamespace) { + tsc.name_index = traitName(namespace, var); + } + tsc.type_index = isNamespace ? 0 : (type == null ? 0 : typeName(localData, type)); + + ValueKind vk = getValueKind(namespace, type, val); + if (vk == null) { + tsc.value_kind = ValueKind.CONSTANT_Undefined; + } else { + tsc.value_kind = vk.value_kind; + tsc.value_index = vk.value_index; + } + tsc.slot_id = isStatic ? slot_id++ : 0; + ts.traits.add(tsc); + traits[k] = tsc; + } + if ((item instanceof MethodAVM2Item) || (item instanceof GetterAVM2Item) || (item instanceof SetterAVM2Item)) { + MethodAVM2Item mai = (MethodAVM2Item) item; + if (mai.isStatic() != generateStatic) { + continue; + } + TraitMethodGetterSetter tmgs = new TraitMethodGetterSetter(); + tmgs.kindType = (item instanceof GetterAVM2Item) ? Trait.TRAIT_GETTER : ((item instanceof SetterAVM2Item) ? Trait.TRAIT_SETTER : Trait.TRAIT_METHOD); + //tmgs.name_index = traitName(((MethodAVM2Item) item).namespace, ((MethodAVM2Item) item).functionName); + tmgs.disp_id = mai.isStatic() ? disp_id++ : 0; //For a reason, there is disp_id only for static methods (or not?) + if (mai.isFinal() || mai.isStatic()) { + tmgs.kindFlags |= Trait.ATTR_Final; + } + if (mai.isOverride()) { + tmgs.kindFlags |= Trait.ATTR_Override; + } + ts.traits.add(tmgs); + + traits[k] = tmgs; + } else if (item instanceof FunctionAVM2Item) { + TraitFunction tf = new TraitFunction(); + tf.slot_id = slot_id++; + tf.kindType = Trait.TRAIT_FUNCTION; + //tf.name_index = traitName(((FunctionAVM2Item) item).namespace, ((FunctionAVM2Item) item).functionName); + ts.traits.add(tf); + traits[k] = tf; + } + } + + return traits; + } + + public ScriptInfo generateScriptInfo(SourceGeneratorLocalData localData, List commands, int classPos) throws AVM2ParseException, CompilationException { + Reference class_index = new Reference<>(classPos); + ScriptInfo si = new ScriptInfo(); + localData.currentScript = si; + Trait[] traitArr = generateTraitsPhase1(null, null, true, localData, commands, si.traits, class_index); + generateTraitsPhase2(new ArrayList(), null/*FIXME*/, commands, traitArr, new ArrayList(), localData); + MethodInfo mi = new MethodInfo(new int[0], 0, 0, 0, new ValueKind[0], new int[0]); + MethodBody mb = new MethodBody(); + mb.method_info = abc.addMethodInfo(mi); + mb.setCode(new AVM2Code()); + List mbCode = mb.getCode().code; + mbCode.add(ins(new GetLocal0Ins())); + mbCode.add(ins(new PushScopeIns())); + + int traitScope = 2; + + Map initScopes = new HashMap<>(); + + for (Trait t : si.traits.traits) { + if (t instanceof TraitClass) { + TraitClass tc = (TraitClass) t; + List parents = new ArrayList<>(); + if (localData.documentClass) { + mbCode.add(ins(new GetScopeObjectIns(), 0)); + traitScope++; + } else { + NamespaceSet nsset = new NamespaceSet(new int[]{abc.constants.constant_multiname.get(tc.name_index).namespace_index}); + mbCode.add(ins(new FindPropertyStrictIns(), abc.constants.getMultinameId(new Multiname(Multiname.MULTINAME, abc.constants.constant_multiname.get(tc.name_index).name_index, 0, abc.constants.getNamespaceSetId(nsset, true), 0, new ArrayList()), true))); + } + if (abc.instance_info.get(tc.class_info).isInterface()) { + mbCode.add(ins(new PushNullIns())); + } else { + + parentNamesAddNames(abc, allABCs, abc.instance_info.get(tc.class_info).name_index, parents, new ArrayList(), new ArrayList()); + for (int i = parents.size() - 1; i >= 1; i--) { + mbCode.add(ins(new GetLexIns(), parents.get(i))); + mbCode.add(ins(new PushScopeIns())); + traitScope++; + } + mbCode.add(ins(new GetLexIns(), parents.get(1))); + } + mbCode.add(ins(new NewClassIns(), tc.class_info)); + if (!abc.instance_info.get(tc.class_info).isInterface()) { + for (int i = parents.size() - 1; i >= 1; i--) { + mbCode.add(ins(new PopScopeIns())); + } + } + mbCode.add(ins(new InitPropertyIns(), tc.name_index)); + initScopes.put(t, traitScope); + traitScope = 1; + } + } + + mbCode.add(ins(new ReturnVoidIns())); + mb.autoFillStats(abc, 1, false); + abc.addMethodBody(mb); + si.init_index = mb.method_info; + localData.pkg = null; //FIXME: pkg.packageName; + generateTraitsPhase3(1/*??*/, false, null, null, true, localData, commands, si.traits, traitArr, initScopes, class_index); + return si; + } + + public static void parentNamesAddNames(ABC abc, List allABCs, int name_index, List indices, List names, List namespaces) { + List cindices = new ArrayList<>(); + + List outABCs = new ArrayList<>(); + parentNames(abc, allABCs, name_index, cindices, names, namespaces, outABCs); + for (int i = 0; i < cindices.size(); i++) { + ABC a = outABCs.get(i); + int m = cindices.get(i); + if (a == abc) { + indices.add(m); + continue; + } + Multiname superName = a.constants.constant_multiname.get(m); + indices.add( + abc.constants.getMultinameId( + new Multiname(Multiname.QNAME, + abc.constants.getStringId(superName.getName(a.constants, new ArrayList(), true), true), + abc.constants.getNamespaceId(new Namespace(superName.getNamespace(a.constants).kind, abc.constants.getStringId(superName.getNamespace(a.constants).getName(a.constants, true), true)), 0, true), 0, 0, new ArrayList()), true) + ); + } + } + + public static GraphTargetItem getTraitReturnType(ABC abc, Trait t) { + if (t instanceof TraitSlotConst) { + TraitSlotConst tsc = (TraitSlotConst) t; + if (tsc.type_index == 0) { + return TypeItem.UNBOUNDED; + } + return PropertyAVM2Item.multinameToType(tsc.type_index, abc.constants); + } + if (t instanceof TraitMethodGetterSetter) { + TraitMethodGetterSetter tmgs = (TraitMethodGetterSetter) t; + if (tmgs.kindType == Trait.TRAIT_GETTER) { + return PropertyAVM2Item.multinameToType(abc.method_info.get(tmgs.method_info).ret_type, abc.constants); + } + if (tmgs.kindType == Trait.TRAIT_SETTER) { + if (abc.method_info.get(tmgs.method_info).param_types.length > 0) { + return PropertyAVM2Item.multinameToType(abc.method_info.get(tmgs.method_info).param_types[0], abc.constants); + } else { + return TypeItem.UNBOUNDED; + } + } + } + if (t instanceof TraitFunction) { + return new TypeItem("Function"); + } + return TypeItem.UNBOUNDED; + } + + private static boolean eq(Object a, Object b) { + if (a == null && b == null) { + return true; + } + if (a == null) { + return false; + } + if (b == null) { + return false; + } + return a.equals(b); + } + + public static boolean searchPrototypeChain(boolean instanceOnly, List abcs, String pkg, String obj, String propertyName, Reference outName, Reference outNs, Reference outPropNs, Reference outPropNsKind, Reference outPropNsIndex, Reference outPropType, Reference outPropValue) { + + for (ABC abc : abcs) { + if (!instanceOnly) { + for (ScriptInfo ii : abc.script_info) { + if (ii.deleted) { + continue; + } + for (Trait t : ii.traits.traits) { + if (eq(pkg, t.getName(abc).getNamespace(abc.constants).getName(abc.constants, true))) { + if (propertyName.equals(t.getName(abc).getName(abc.constants, new ArrayList(), true))) { + outName.setVal(obj); + outNs.setVal(pkg); + outPropNs.setVal(t.getName(abc).getNamespace(abc.constants).getName(abc.constants, true)); + outPropNsKind.setVal(t.getName(abc).getNamespace(abc.constants).kind); + outPropNsIndex.setVal(abc.constants.getNamespaceSubIndex(t.getName(abc).namespace_index)); + outPropType.setVal(getTraitReturnType(abc, t)); + if (t instanceof TraitSlotConst) { + TraitSlotConst tsc = (TraitSlotConst) t; + outPropValue.setVal(new ValueKind(tsc.value_index, tsc.value_kind)); + } + return true; + } + } + } + } + } + for (int i = 0; i < abc.instance_info.size(); i++) { + InstanceInfo ii = abc.instance_info.get(i); + if (ii.deleted) { + continue; + } + Multiname clsName = ii.getName(abc.constants); + if (obj.equals(clsName.getName(abc.constants, new ArrayList(), true))) { + if (eq(pkg, clsName.getNamespace(abc.constants).getName(abc.constants, true))) { + //class found + + for (Trait t : ii.instance_traits.traits) { + if (t.getName(abc) == null) { //in traits phase 2 + continue; + } + if (propertyName.equals(t.getName(abc).getName(abc.constants, new ArrayList(), true))) { + outName.setVal(obj); + outNs.setVal(pkg); + outPropNs.setVal(t.getName(abc).getNamespace(abc.constants).getName(abc.constants, true)); + outPropNsKind.setVal(t.getName(abc).getNamespace(abc.constants).kind); + outPropNsIndex.setVal(abc.constants.getNamespaceSubIndex(t.getName(abc).namespace_index)); + outPropType.setVal(getTraitReturnType(abc, t)); + if (t instanceof TraitSlotConst) { + TraitSlotConst tsc = (TraitSlotConst) t; + outPropValue.setVal(new ValueKind(tsc.value_index, tsc.value_kind)); + } + return true; + } + } + + if (!instanceOnly) { + for (Trait t : abc.class_info.get(i).static_traits.traits) { + if (t.getName(abc) == null) { //in traits phase 2 + continue; + } + if (propertyName.equals(t.getName(abc).getName(abc.constants, new ArrayList(), true))) { + outName.setVal(obj); + outNs.setVal(pkg); + outPropNs.setVal(t.getName(abc).getNamespace(abc.constants).getName(abc.constants, true)); + outPropNsKind.setVal(t.getName(abc).getNamespace(abc.constants).kind); + outPropNsIndex.setVal(abc.constants.getNamespaceSubIndex(t.getName(abc).namespace_index)); + outPropType.setVal(getTraitReturnType(abc, t)); + if (t instanceof TraitSlotConst) { + TraitSlotConst tsc = (TraitSlotConst) t; + outPropValue.setVal(new ValueKind(tsc.value_index, tsc.value_kind)); + } + return true; + } + } + } + + Multiname superName = abc.constants.constant_multiname.get(ii.super_index); + if (superName != null) { + return searchPrototypeChain(instanceOnly, abcs, superName.getNamespace(abc.constants).getName(abc.constants, true), superName.getName(abc.constants, new ArrayList(), true), propertyName, outName, outNs, outPropNs, outPropNsKind, outPropNsIndex, outPropType, outPropValue); + } else { + return false; + } + } + } + } + } + return false; + } + + public static void parentNames(ABC abc, List allABCs, int name_index, List indices, List names, List namespaces, List outABCs) { + indices.add(name_index); + names.add(abc.constants.constant_multiname.get(name_index).getName(abc.constants, new ArrayList(), true)); + namespaces.add(abc.constants.constant_multiname.get(name_index).getNamespace(abc.constants).getName(abc.constants, true)); + Multiname mname = abc.constants.constant_multiname.get(name_index); + + outABCs.add(abc); + + List abcs = new ArrayList<>(); + abcs.add(abc); + abcs.addAll(allABCs); + + for (ABC a : abcs) { + for (int i = 0; i < a.instance_info.size(); i++) { + Multiname m = a.constants.constant_multiname.get(a.instance_info.get(i).name_index); + if (m.getName(a.constants, new ArrayList(), true).equals(mname.getName(abc.constants, new ArrayList(), true))) { + + if (m.getNamespace(a.constants).hasName(mname.getNamespace(abc.constants).getName(abc.constants, true), a.constants)) { + //Multiname superName = a.constants.constant_multiname.get(a.instance_info.get(i).super_index); + abcs.remove(a); + if (a.instance_info.get(i).super_index != 0) { + parentNames(a, abcs, a.instance_info.get(i).super_index, indices, names, namespaces, outABCs); + } + /*parentNames(abc,allABCs,abc.constants.getMultinameId( + new Multiname(superName.kind, + abc.constants.getStringId(superName.getName(a.constants, new ArrayList()),true), + abc.constants.getNamespaceId(new Namespace(superName.getNamespace(a.constants).kind, abc.constants.getStringId(superName.getNamespace(a.constants).getName(a.constants),true)),0,true), 0, 0, new ArrayList()), true),indices,names,namespaces,outABCs);*/ + return; + } + } + } + } + } + + /* public void calcRegisters(Reference activationReg, SourceGeneratorLocalData localData, boolean needsActivation, List funParamNames,List funSubVariables,List funBody, Reference hasArguments) throws ParseException { + + }*/ + /*public int resolveType(String objType) { + if (objType.equals("*")) { + return 0; + } + List abcs = new ArrayList<>(); + abcs.add(abc); + abcs.addAll(allABCs); + for (ABC a : abcs) { + int ci = a.findClassByName(objType); + if (ci != -1) { + Multiname tname = a.instance_info.get(ci).getName(a.constants); + return abc.constants.getMultinameId(new Multiname(tname.kind, + abc.constants.getStringId(tname.getName(a.constants, new ArrayList()), true), + abc.constants.getNamespaceId(new Namespace(tname.getNamespace(a.constants).kind, abc.constants.getStringId(tname.getNamespace(a.constants).getName(a.constants), true)), 0, true), 0, 0, new ArrayList()), true); + } + } + return 0; + }*/ + @Override + public List generate(SourceGeneratorLocalData localData, TypeItem item) throws CompilationException { + String currentFullClassName = localData.getFullClass(); + + if (localData.documentClass && item.toString().equals(currentFullClassName)) { + int slotId = 0; + int c = abc.findClassByName(currentFullClassName); + for (Trait t : localData.currentScript.traits.traits) { + if (t instanceof TraitClass) { + TraitClass tc = (TraitClass) t; + if (tc.class_info == c) { + slotId = tc.slot_id; + break; + } + } + } + return GraphTargetItem.toSourceMerge(localData, this, ins(new GetGlobalScopeIns()), ins(new GetSlotIns(), slotId)); + } else { + return GraphTargetItem.toSourceMerge(localData, this, ins(new GetLexIns(), resolveType(localData, item, abc, allABCs))); + } + } + + public static int resolveType(SourceGeneratorLocalData localData, GraphTargetItem item, ABC abc, List allABCs) throws CompilationException { + int name_index = 0; + GraphTargetItem typeItem = null; + + if (item instanceof UnresolvedAVM2Item) { + String fullClass = localData.getFullClass(); + item = ((UnresolvedAVM2Item) item).resolve(new TypeItem(fullClass), new ArrayList(), new ArrayList(), abc, allABCs, new ArrayList(), new ArrayList()); + } + if (item instanceof TypeItem) { + typeItem = item; + } else if (item instanceof ApplyTypeAVM2Item) { + typeItem = ((ApplyTypeAVM2Item) item).object; + } else { + throw new CompilationException("Invalid type:" + item + " (" + item.getClass().getName() + ")", 0/*??*/); + } + if (typeItem instanceof UnresolvedAVM2Item) { + String fullClass = localData.getFullClass(); + typeItem = ((UnresolvedAVM2Item) typeItem).resolve(new TypeItem(fullClass), new ArrayList(), new ArrayList(), abc, allABCs, new ArrayList(), new ArrayList()); + } + + if (!(typeItem instanceof TypeItem)) { + throw new CompilationException("Invalid type", 0/*??*/); + } + + TypeItem type = (TypeItem) typeItem; + + String name = type.fullTypeName; + String pkg = ""; + if (name.contains(".")) { + pkg = name.substring(0, name.lastIndexOf('.')); + name = name.substring(name.lastIndexOf('.') + 1); + } + for (InstanceInfo ii : abc.instance_info) { + Multiname mname = abc.constants.constant_multiname.get(ii.name_index); + if (mname != null && name.equals(mname.getName(abc.constants, new ArrayList(), true))) { + if (mname.getNamespace(abc.constants).hasName(pkg, abc.constants)) { + name_index = ii.name_index; + break; + } + } + } + for (int i = 1; i < abc.constants.constant_multiname.size(); i++) { + Multiname mname = abc.constants.constant_multiname.get(i); + if (mname != null && name.equals(mname.getName(abc.constants, new ArrayList(), true))) { + if (mname.getNamespace(abc.constants) != null && pkg.equals(mname.getNamespace(abc.constants).getName(abc.constants, true))) { + name_index = i; + break; + } + } + } + if (name_index == 0) { + if (pkg.isEmpty() && localData.currentScript != null /*FIXME!*/) { + for (Trait t : localData.currentScript.traits.traits) { + if (t.getName(abc).getName(abc.constants, new ArrayList(), true).equals(name)) { + name_index = t.name_index; + break; + } + } + } + if (name_index == 0) { + name_index = abc.constants.getMultinameId(new Multiname(Multiname.QNAME, abc.constants.getStringId(name, true), abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.getStringId(pkg, true)), 0, true), 0, 0, new ArrayList()), true); + } + } + + if (item instanceof ApplyTypeAVM2Item) { + ApplyTypeAVM2Item atype = (ApplyTypeAVM2Item) item; + List params = new ArrayList<>(); + for (GraphTargetItem s : atype.params) { + params.add(resolveType(localData, s, abc, allABCs)); + } + return abc.constants.getMultinameId(new Multiname(Multiname.TYPENAME, 0, 0, 0, name_index, params), true); + } + + return name_index; + } + + @Override + public List generateDiscardValue(SourceGeneratorLocalData localData, GraphTargetItem item) throws CompilationException { + List ret = item.toSource(localData, this); + ret.add(ins(new PopIns())); + return ret; + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ActionScriptParser.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ActionScriptParser.java index df357b0d8..9c0abb716 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ActionScriptParser.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ActionScriptParser.java @@ -2221,7 +2221,7 @@ public class ActionScriptParser { expectedType(SymbolType.PARENT_OPEN); ret = new ConstructSomethingAVM2Item(lexer.yyline(), openedNamespaces, newvar, call(thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, variables)); } - + allowMemberOrCall = true; break; case IDENTIFIER: case THIS: diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/UnresolvedAVM2Item.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/UnresolvedAVM2Item.java index eea4f3b21..2548eaf0e 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/UnresolvedAVM2Item.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/UnresolvedAVM2Item.java @@ -365,15 +365,6 @@ public class UnresolvedAVM2Item extends AssignableAVM2Item { continue; } TypeItem ret = new TypeItem(fname); - /*for (String s : subtypes) { - UnresolvedAVM2Item su = new UnresolvedAVM2Item(new ArrayList(), importedClasses, true, null, line, s, null, openedNamespaces); - su.resolve(thisType, paramTypes, paramNames, abc, otherAbcs, callStack, variables); - if (!(su.resolved instanceof TypeItem)) { - throw new CompilationException("Not a type", line); - } - TypeItem st = (TypeItem) su.resolved; - ret.subtypes.add(st.fullTypeName); - }*/ resolved = ret; for (int j = i + 1; j < parts.size(); j++) { resolved = new PropertyAVM2Item(resolved, parts.get(j), abc, otherAbcs, openedNamespaces, new ArrayList()); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/ScriptInfo.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/ScriptInfo.java index da02dad95..6a96ba792 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/ScriptInfo.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/ScriptInfo.java @@ -1,112 +1,112 @@ -/* - * Copyright (C) 2010-2015 JPEXS, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -package com.jpexs.decompiler.flash.abc.types; - -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.ClassPath; -import com.jpexs.decompiler.flash.abc.ScriptPack; -import com.jpexs.decompiler.flash.abc.types.traits.Trait; -import com.jpexs.decompiler.flash.abc.types.traits.Traits; -import com.jpexs.decompiler.flash.types.annotations.Internal; -import java.util.ArrayList; -import java.util.List; - -public class ScriptInfo { - - @Internal - private boolean modified = true; - - public void setModified(boolean modified) { - this.modified = modified; - } - - public boolean isModified() { - return modified; - } - - @Internal - public boolean deleted; - - public int init_index; //MethodInfo - - public Traits traits; - - public ScriptInfo() { - traits = new Traits(); - } - - public ScriptInfo(Traits traits) { - this.traits = traits; - } - - public List getPacks(ABC abc, int scriptIndex, String packagePrefix) { - List ret = new ArrayList<>(); - - List otherTraits = new ArrayList<>(); - for (int j = 0; j < traits.traits.size(); j++) { - Trait t = traits.traits.get(j); - Multiname name = t.getName(abc); - Namespace ns = name.getNamespace(abc.constants); - if (!((ns.kind == Namespace.KIND_PACKAGE_INTERNAL) - || (ns.kind == Namespace.KIND_PACKAGE))) { - otherTraits.add(j); - } - } - for (int j = 0; j < traits.traits.size(); j++) { - Trait t = traits.traits.get(j); - Multiname name = t.getName(abc); - Namespace ns = name.getNamespace(abc.constants); - if ((ns.kind == Namespace.KIND_PACKAGE_INTERNAL) - || (ns.kind == Namespace.KIND_PACKAGE)) { - String packageName = ns.getName(abc.constants, false); // assume not null package - String objectName = name.getName(abc.constants, null, false); - List traitIndices = new ArrayList<>(); - - traitIndices.add(j); - if (!otherTraits.isEmpty()) { - traitIndices.addAll(otherTraits); - otherTraits.clear(); - } - - if (packagePrefix == null || packageName.startsWith(packagePrefix)) { - ClassPath cp = new ClassPath(packageName, objectName); - ret.add(new ScriptPack(cp, abc, scriptIndex, traitIndices)); - } - } - } - return ret; - } - - public int removeTraps(int scriptIndex, ABC abc, String path) throws InterruptedException { - return traits.removeTraps(scriptIndex, -1, true, abc, path); - } - - @Override - public String toString() { - return "method_index=" + init_index + "\r\n" + traits.toString(); - } - - public String toString(ABC abc, List fullyQualifiedNames) { - return "method_index=" + init_index + "\r\n" + traits.toString(abc, fullyQualifiedNames); - } - - public void delete(ABC abc, boolean d) { - deleted = d; - abc.method_info.get(init_index).delete(abc, d); - traits.delete(abc, d); - } -} +/* + * Copyright (C) 2010-2015 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash.abc.types; + +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.ClassPath; +import com.jpexs.decompiler.flash.abc.ScriptPack; +import com.jpexs.decompiler.flash.abc.types.traits.Trait; +import com.jpexs.decompiler.flash.abc.types.traits.Traits; +import com.jpexs.decompiler.flash.types.annotations.Internal; +import java.util.ArrayList; +import java.util.List; + +public class ScriptInfo { + + @Internal + private boolean modified = true; + + public void setModified(boolean modified) { + this.modified = modified; + } + + public boolean isModified() { + return modified; + } + + @Internal + public boolean deleted; + + public int init_index; //MethodInfo + + public Traits traits; + + public ScriptInfo() { + traits = new Traits(); + } + + public ScriptInfo(Traits traits) { + this.traits = traits; + } + + public List getPacks(ABC abc, int scriptIndex, String packagePrefix, List allAbcs) { + List ret = new ArrayList<>(); + + List otherTraits = new ArrayList<>(); + for (int j = 0; j < traits.traits.size(); j++) { + Trait t = traits.traits.get(j); + Multiname name = t.getName(abc); + Namespace ns = name.getNamespace(abc.constants); + if (!((ns.kind == Namespace.KIND_PACKAGE_INTERNAL) + || (ns.kind == Namespace.KIND_PACKAGE))) { + otherTraits.add(j); + } + } + for (int j = 0; j < traits.traits.size(); j++) { + Trait t = traits.traits.get(j); + Multiname name = t.getName(abc); + Namespace ns = name.getNamespace(abc.constants); + if ((ns.kind == Namespace.KIND_PACKAGE_INTERNAL) + || (ns.kind == Namespace.KIND_PACKAGE)) { + String packageName = ns.getName(abc.constants, false); // assume not null package + String objectName = name.getName(abc.constants, null, false); + List traitIndices = new ArrayList<>(); + + traitIndices.add(j); + if (!otherTraits.isEmpty()) { + traitIndices.addAll(otherTraits); + otherTraits.clear(); + } + + if (packagePrefix == null || packageName.startsWith(packagePrefix)) { + ClassPath cp = new ClassPath(packageName, objectName); + ret.add(new ScriptPack(cp, abc, allAbcs, scriptIndex, traitIndices)); + } + } + } + return ret; + } + + public int removeTraps(int scriptIndex, ABC abc, String path) throws InterruptedException { + return traits.removeTraps(scriptIndex, -1, true, abc, path); + } + + @Override + public String toString() { + return "method_index=" + init_index + "\r\n" + traits.toString(); + } + + public String toString(ABC abc, List fullyQualifiedNames) { + return "method_index=" + init_index + "\r\n" + traits.toString(abc, fullyQualifiedNames); + } + + public void delete(ABC abc, boolean d) { + deleted = d; + abc.method_info.get(init_index).delete(abc, d); + traits.delete(abc, d); + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java b/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java index 412b193d6..88deb8023 100644 --- a/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java @@ -1,886 +1,887 @@ -/* - * Copyright (C) 2010-2015 JPEXS - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.jpexs.decompiler.flash.gui.abc; - -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.ClassPath; -import com.jpexs.decompiler.flash.abc.ScriptPack; -import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; -import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; -import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.GetLocal0Ins; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ReturnVoidIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushScopeIns; -import com.jpexs.decompiler.flash.abc.avm2.parser.AVM2ParseException; -import com.jpexs.decompiler.flash.abc.avm2.parser.script.Reference; -import com.jpexs.decompiler.flash.abc.types.ABCException; -import com.jpexs.decompiler.flash.abc.types.MethodBody; -import com.jpexs.decompiler.flash.abc.types.MethodInfo; -import com.jpexs.decompiler.flash.abc.types.Multiname; -import com.jpexs.decompiler.flash.abc.types.Namespace; -import com.jpexs.decompiler.flash.abc.types.ValueKind; -import com.jpexs.decompiler.flash.abc.types.traits.Trait; -import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter; -import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst; -import com.jpexs.decompiler.flash.abc.types.traits.Traits; -import com.jpexs.decompiler.flash.abc.usages.MultinameUsage; -import com.jpexs.decompiler.flash.abc.usages.TraitMultinameUsage; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.gui.AppDialog; -import com.jpexs.decompiler.flash.gui.AppStrings; -import com.jpexs.decompiler.flash.gui.HeaderLabel; -import com.jpexs.decompiler.flash.gui.Main; -import com.jpexs.decompiler.flash.gui.MainPanel; -import com.jpexs.decompiler.flash.gui.SearchListener; -import com.jpexs.decompiler.flash.gui.SearchPanel; -import com.jpexs.decompiler.flash.gui.SearchResultsDialog; -import com.jpexs.decompiler.flash.gui.TagEditorPanel; -import com.jpexs.decompiler.flash.gui.View; -import com.jpexs.decompiler.flash.gui.abc.tablemodels.DecimalTableModel; -import com.jpexs.decompiler.flash.gui.abc.tablemodels.DoubleTableModel; -import com.jpexs.decompiler.flash.gui.abc.tablemodels.IntTableModel; -import com.jpexs.decompiler.flash.gui.abc.tablemodels.MultinameTableModel; -import com.jpexs.decompiler.flash.gui.abc.tablemodels.NamespaceSetTableModel; -import com.jpexs.decompiler.flash.gui.abc.tablemodels.NamespaceTableModel; -import com.jpexs.decompiler.flash.gui.abc.tablemodels.StringTableModel; -import com.jpexs.decompiler.flash.gui.abc.tablemodels.UIntTableModel; -import com.jpexs.decompiler.flash.gui.controls.JPersistentSplitPane; -import com.jpexs.decompiler.flash.gui.editor.LinkHandler; -import com.jpexs.decompiler.flash.gui.tagtree.TagTreeModel; -import com.jpexs.decompiler.flash.tags.ABCContainerTag; -import com.jpexs.decompiler.flash.treeitems.TreeItem; -import com.jpexs.decompiler.graph.CompilationException; -import com.jpexs.helpers.CancellableWorker; -import java.awt.BorderLayout; -import java.awt.Cursor; -import java.awt.FlowLayout; -import java.awt.Font; -import java.awt.Insets; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.ItemEvent; -import java.awt.event.ItemListener; -import java.awt.event.KeyAdapter; -import java.awt.event.KeyEvent; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; -import java.awt.event.MouseMotionListener; -import java.io.File; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ExecutionException; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.regex.Pattern; -import javax.swing.AbstractAction; -import javax.swing.BoxLayout; -import javax.swing.JButton; -import javax.swing.JComboBox; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JSplitPane; -import javax.swing.JTabbedPane; -import javax.swing.JTable; -import javax.swing.JToggleButton; -import javax.swing.SwingConstants; -import javax.swing.border.BevelBorder; -import javax.swing.table.DefaultTableModel; -import javax.swing.text.Highlighter; -import javax.swing.tree.TreePath; -import jsyntaxpane.SyntaxDocument; -import jsyntaxpane.Token; -import jsyntaxpane.TokenType; - -/** - * - * @author JPEXS - */ -public class ABCPanel extends JPanel implements ItemListener, SearchListener, TagEditorPanel { - - private final MainPanel mainPanel; - - public final TraitsList navigator; - - public ABC abc; - - public final DecompiledEditorPane decompiledTextArea; - - public final JScrollPane decompiledScrollPane; - - private final JPersistentSplitPane splitPane; - - private final JTable constantTable; - - public JComboBox constantTypeList; - - public JLabel asmLabel = new HeaderLabel(AppStrings.translate("panel.disassembled")); - - public JLabel decLabel = new HeaderLabel(AppStrings.translate("panel.decompiled")); - - public final DetailPanel detailPanel; - - private final JPanel navPanel; - - public final JTabbedPane tabbedPane; - - private final SearchPanel searchPanel; - - private NewTraitDialog newTraitDialog; - - public final JLabel scriptNameLabel; - - private final JLabel experimentalLabel = new JLabel(AppStrings.translate("action.edit.experimental")); - - private final JButton editDecompiledButton = new JButton(AppStrings.translate("button.edit"), View.getIcon("edit16")); - - private final JButton saveDecompiledButton = new JButton(AppStrings.translate("button.save"), View.getIcon("save16")); - - private final JButton cancelDecompiledButton = new JButton(AppStrings.translate("button.cancel"), View.getIcon("cancel16")); - - private String lastDecompiled = null; - - public MainPanel getMainPanel() { - return mainPanel; - } - - public boolean search(final String txt, boolean ignoreCase, boolean regexp) { - if ((txt != null) && (!txt.isEmpty())) { - searchPanel.setOptions(ignoreCase, regexp); - TagTreeModel ttm = (TagTreeModel) mainPanel.tagTree.getModel(); - TreeItem scriptsNode = ttm.getScriptsNode(mainPanel.getCurrentSwf()); - final List found = new ArrayList<>(); - if (scriptsNode instanceof ClassesListTreeModel) { - ClassesListTreeModel clModel = (ClassesListTreeModel) scriptsNode; - List allpacks = clModel.getList(); - final Pattern pat = regexp - ? Pattern.compile(txt, ignoreCase ? (Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE) : 0) - : Pattern.compile(Pattern.quote(txt), ignoreCase ? (Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE) : 0); - int pos = 0; - for (final ScriptPack pack : allpacks) { - pos++; - String workText = AppStrings.translate("work.searching"); - String decAdd = ""; - if (!SWF.isCached(pack)) { - decAdd = ", " + AppStrings.translate("work.decompiling"); - } - - try { - CancellableWorker worker = new CancellableWorker() { - - @Override - public Void doInBackground() throws Exception { - if (pat.matcher(SWF.getCached(pack).text).find()) { - ABCPanelSearchResult searchResult = new ABCPanelSearchResult(); - searchResult.scriptPack = pack; - found.add(searchResult); - } - return null; - } - }; - worker.execute(); - Main.startWork(workText + " \"" + txt + "\"" + decAdd + " - (" + pos + "/" + allpacks.size() + ") " + pack.getClassPath().toString() + "... ", worker); - worker.get(); - } catch (InterruptedException ex) { - break; - } catch (ExecutionException ex) { - Logger.getLogger(ABCPanel.class.getName()).log(Level.SEVERE, null, ex); - } - } - } - - Main.stopWork(); - - searchPanel.setSearchText(txt); - - View.execInEventDispatch(() -> { - SearchResultsDialog sr = new SearchResultsDialog<>(ABCPanel.this.mainPanel.getMainFrame().getWindow(), txt, ABCPanel.this); - sr.setResults(found); - sr.setVisible(true); - }); - - return true; - - //return searchPanel.setResults(found); - } - return false; - } - - public void setAbc(ABC abc) { - this.abc = abc; - setDecompiledEditMode(false); - navigator.setAbc(abc); - updateConstList(); - } - - public void updateConstList() { - switch (constantTypeList.getSelectedIndex()) { - case 0: - View.autoResizeColWidth(constantTable, new UIntTableModel(abc)); - break; - case 1: - View.autoResizeColWidth(constantTable, new IntTableModel(abc)); - break; - case 2: - View.autoResizeColWidth(constantTable, new DoubleTableModel(abc)); - break; - case 3: - View.autoResizeColWidth(constantTable, new DecimalTableModel(abc)); - break; - case 4: - View.autoResizeColWidth(constantTable, new StringTableModel(abc)); - break; - case 5: - View.autoResizeColWidth(constantTable, new NamespaceTableModel(abc)); - break; - case 6: - View.autoResizeColWidth(constantTable, new NamespaceSetTableModel(abc)); - break; - case 7: - View.autoResizeColWidth(constantTable, new MultinameTableModel(abc)); - break; - } - //DefaultTableColumnModel colModel = (DefaultTableColumnModel) constantTable.getColumnModel(); - //colModel.getColumn(0).setMaxWidth(50); - } - - public SWF getSwf() { - return abc == null ? null : abc.getSwf(); - } - - public List getAbcList() { - SWF swf = getSwf(); - return swf == null ? null : swf.getAbcList(); - } - - public void clearSwf() { - this.abc = null; - constantTable.setModel(new DefaultTableModel()); - navigator.clearAbc(); - decompiledTextArea.clearScript(); - } - - public ABCPanel(MainPanel mainPanel) { - - this.mainPanel = mainPanel; - setLayout(new BorderLayout()); - - decompiledTextArea = new DecompiledEditorPane(this); - decompiledTextArea.addTextChangedListener(this::decompiledTextAreaTextChanged); - - decompiledTextArea.setLinkHandler(new LinkHandler() { - - @Override - public boolean isLink(Token token) { - return hasDeclaration(token.length == 1 ? token.start : token.start + 1); - } - - @Override - public void handleLink(Token token) { - gotoDeclaration(token.length == 1 ? token.start : token.start + 1); - } - - @Override - public Highlighter.HighlightPainter linkPainter() { - return decompiledTextArea.linkPainter(); - } - }); - - searchPanel = new SearchPanel<>(new FlowLayout(), this); - - decompiledScrollPane = new JScrollPane(decompiledTextArea); - - JPanel iconDecPanel = new JPanel(); - iconDecPanel.setLayout(new BoxLayout(iconDecPanel, BoxLayout.Y_AXIS)); - JPanel iconsPanel = new JPanel(); - iconsPanel.setLayout(new BoxLayout(iconsPanel, BoxLayout.X_AXIS)); - - JButton newTraitButton = new JButton(View.getIcon("traitadd16")); - newTraitButton.setMargin(new Insets(5, 5, 5, 5)); - newTraitButton.addActionListener(this::addTraitButtonActionPerformed); - newTraitButton.setToolTipText(AppStrings.translate("button.addtrait")); - iconsPanel.add(newTraitButton); - - scriptNameLabel = new JLabel("-"); - scriptNameLabel.setAlignmentX(0); - iconsPanel.setAlignmentX(0); - decompiledScrollPane.setAlignmentX(0); - iconDecPanel.add(scriptNameLabel); - iconDecPanel.add(iconsPanel); - iconDecPanel.add(decompiledScrollPane); - - JPanel decButtonsPan = new JPanel(new FlowLayout()); - decButtonsPan.setBorder(new BevelBorder(BevelBorder.RAISED)); - decButtonsPan.add(editDecompiledButton); - decButtonsPan.add(experimentalLabel); - decButtonsPan.add(saveDecompiledButton); - decButtonsPan.add(cancelDecompiledButton); - - editDecompiledButton.setMargin(new Insets(3, 3, 3, 10)); - saveDecompiledButton.setMargin(new Insets(3, 3, 3, 10)); - cancelDecompiledButton.setMargin(new Insets(3, 3, 3, 10)); - - saveDecompiledButton.addActionListener(this::saveDecompiledButtonActionPerformed); - editDecompiledButton.addActionListener(this::editDecompiledButtonActionPerformed); - cancelDecompiledButton.addActionListener(this::cancelDecompiledButtonActionPerformed); - - saveDecompiledButton.setVisible(false); - cancelDecompiledButton.setVisible(false); - decButtonsPan.setAlignmentX(0); - - JPanel decPanel = new JPanel(new BorderLayout()); - decPanel.add(searchPanel, BorderLayout.NORTH); - decPanel.add(iconDecPanel, BorderLayout.CENTER); - decPanel.add(decButtonsPan, BorderLayout.SOUTH); - detailPanel = new DetailPanel(this); - JPanel panB = new JPanel(); - panB.setLayout(new BorderLayout()); - panB.add(decPanel, BorderLayout.CENTER); - panB.add(decLabel, BorderLayout.NORTH); - decLabel.setHorizontalAlignment(SwingConstants.CENTER); - //decLabel.setBorder(new BevelBorder(BevelBorder.RAISED)); - splitPane = new JPersistentSplitPane(JSplitPane.HORIZONTAL_SPLIT, panB, detailPanel, Configuration.guiAvm2SplitPaneDividerLocationPercent); - splitPane.setContinuousLayout(true); - - decompiledTextArea.changeContentType("text/actionscript"); - decompiledTextArea.setFont(new Font("Monospaced", Font.PLAIN, decompiledTextArea.getFont().getSize())); - - View.addEditorAction(decompiledTextArea, new AbstractAction() { - @Override - public void actionPerformed(ActionEvent e) { - int multinameIndex = decompiledTextArea.getMultinameUnderCaret(); - if (multinameIndex > -1) { - UsageFrame usageFrame = new UsageFrame(abc, multinameIndex, ABCPanel.this, false); - usageFrame.setVisible(true); - } - } - }, "find-usages", AppStrings.translate("abc.action.find-usages"), "control U"); - - View.addEditorAction(decompiledTextArea, new AbstractAction() { - @Override - public void actionPerformed(ActionEvent e) { - gotoDeclaration(decompiledTextArea.getCaretPosition()); - } - }, "find-declaration", AppStrings.translate("abc.action.find-declaration"), "control B"); - - CtrlClickHandler cch = new CtrlClickHandler(); - decompiledTextArea.addKeyListener(cch); - decompiledTextArea.addMouseListener(cch); - decompiledTextArea.addMouseMotionListener(cch); - - navigator = new TraitsList(this); - - navPanel = new JPanel(new BorderLayout()); - JPanel navIconsPanel = new JPanel(); - navIconsPanel.setLayout(new BoxLayout(navIconsPanel, BoxLayout.X_AXIS)); - final JToggleButton sortButton = new JToggleButton(View.getIcon("sort16")); - sortButton.setMargin(new Insets(3, 3, 3, 3)); - navIconsPanel.add(sortButton); - navPanel.add(navIconsPanel, BorderLayout.SOUTH); - navPanel.add(new JScrollPane(navigator), BorderLayout.CENTER); - sortButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - navigator.setSorted(sortButton.isSelected()); - navigator.updateUI(); - } - }); - - tabbedPane = new JTabbedPane(); - tabbedPane.addTab(AppStrings.translate("traits"), navPanel); - add(splitPane, BorderLayout.CENTER); - - JPanel panConstants = new JPanel(); - panConstants.setLayout(new BorderLayout()); - - constantTypeList = new JComboBox<>(new String[]{"UINT", "INT", "DOUBLE", "DECIMAL", "STRING", "NAMESPACE", "NAMESPACESET", "MULTINAME"}); - constantTable = new JTable(); - if (abc != null) { - View.autoResizeColWidth(constantTable, new UIntTableModel(abc)); - } - constantTable.setAutoCreateRowSorter(true); - - final ABCPanel t = this; - constantTable.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - if (e.getClickCount() == 2) { - if (constantTypeList.getSelectedIndex() == 7) { //MULTINAME - int rowIndex = constantTable.getSelectedRow(); - if (rowIndex == -1) { - return; - } - int multinameIndex = constantTable.convertRowIndexToModel(rowIndex); - if (multinameIndex > 0) { - UsageFrame usageFrame = new UsageFrame(abc, multinameIndex, t, false); - usageFrame.setVisible(true); - } - } - } - } - }); - constantTypeList.addItemListener(this); - panConstants.add(constantTypeList, BorderLayout.NORTH); - panConstants.add(new JScrollPane(constantTable), BorderLayout.CENTER); - tabbedPane.addTab(AppStrings.translate("constants"), panConstants); - } - - private void decompiledTextAreaTextChanged() { - setModified(true); - } - - private boolean isModified() { - return saveDecompiledButton.isVisible() && saveDecompiledButton.isEnabled(); - } - - private void setModified(boolean value) { - saveDecompiledButton.setEnabled(value); - cancelDecompiledButton.setEnabled(value); - } - - private boolean hasDeclaration(int pos) { - - SyntaxDocument sd = (SyntaxDocument) decompiledTextArea.getDocument(); - Token t = sd.getTokenAt(pos); - if (t == null || (t.type != TokenType.IDENTIFIER && t.type != TokenType.KEYWORD && t.type != TokenType.REGEX)) { - return false; - } - Reference abcIndex = new Reference<>(0); - Reference classIndex = new Reference<>(0); - Reference traitIndex = new Reference<>(0); - Reference multinameIndexRef = new Reference<>(0); - Reference classTrait = new Reference<>(false); - - if (decompiledTextArea.getPropertyTypeAtPos(pos, abcIndex, classIndex, traitIndex, classTrait, multinameIndexRef)) { - return true; - } - int multinameIndex = decompiledTextArea.getMultinameAtPos(pos); - if (multinameIndex > -1) { - if (multinameIndex == 0) { - return false; - } - List usages = abc.findMultinameDefinition(multinameIndex); - - Multiname m = abc.constants.constant_multiname.get(multinameIndex); - //search other ABC tags if this is not private multiname - if (m.namespace_index > 0 && abc.constants.constant_namespace.get(m.namespace_index).kind != Namespace.KIND_PRIVATE) { - for (ABCContainerTag at : getAbcList()) { - ABC a = at.getABC(); - if (a == abc) { - continue; - } - int mid = a.constants.getMultinameId(m, false); - if (mid > 0) { - usages.addAll(a.findMultinameDefinition(mid)); - } - } - } - - //more than one? display list - if (!usages.isEmpty()) { - return true; - } - } - - return decompiledTextArea.getLocalDeclarationOfPos(pos, new Reference<>("")) != -1; - } - - private void gotoDeclaration(int pos) { - Reference abcIndex = new Reference<>(0); - Reference classIndex = new Reference<>(0); - Reference traitIndex = new Reference<>(0); - Reference classTrait = new Reference<>(false); - Reference multinameIndexRef = new Reference<>(0); - - if (decompiledTextArea.getPropertyTypeAtPos(pos, abcIndex, classIndex, traitIndex, classTrait, multinameIndexRef)) { - UsageFrame.gotoUsage(ABCPanel.this, new TraitMultinameUsage(getAbcList().get(abcIndex.getVal()).getABC(), multinameIndexRef.getVal(), classIndex.getVal(), traitIndex.getVal(), classTrait.getVal(), null, -1) { - }); - return; - } - int multinameIndex = decompiledTextArea.getMultinameAtPos(pos); - if (multinameIndex > -1) { - List usages = abc.findMultinameDefinition(multinameIndex); - - Multiname m = abc.constants.constant_multiname.get(multinameIndex); - //search other ABC tags if this is not private multiname - if (m.namespace_index > 0 && abc.constants.constant_namespace.get(m.namespace_index).kind != Namespace.KIND_PRIVATE) { - for (ABCContainerTag at : getAbcList()) { - ABC a = at.getABC(); - if (a == abc) { - continue; - } - int mid = a.constants.getMultinameId(m, false); - if (mid > 0) { - usages.addAll(a.findMultinameDefinition(mid)); - } - } - } - - //more than one? display list - if (usages.size() > 1) { - UsageFrame usageFrame = new UsageFrame(abc, multinameIndex, ABCPanel.this, true); - usageFrame.setVisible(true); - return; - } else if (!usages.isEmpty()) { //one - UsageFrame.gotoUsage(ABCPanel.this, usages.get(0)); - return; - } - } - - int dpos = decompiledTextArea.getLocalDeclarationOfPos(pos, new Reference<>("")); - if (dpos > -1) { - decompiledTextArea.setCaretPosition(dpos); - } - - } - - private class CtrlClickHandler extends KeyAdapter implements MouseListener, MouseMotionListener { - - private boolean ctrlDown = false; - - @Override - public void keyPressed(KeyEvent e) { - if (e.getKeyCode() == 17 && !decompiledTextArea.isEditable()) { - ctrlDown = true; - //decompiledTextArea.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - } - } - - @Override - public void keyReleased(KeyEvent e) { - if (e.getKeyCode() == 17) { - ctrlDown = false; - //decompiledTextArea.setCursor(Cursor.getDefaultCursor()); - } - } - - @Override - public void mouseClicked(MouseEvent e) { - if (ctrlDown && e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() == 1 && !decompiledTextArea.isEditable()) { - ctrlDown = false; - //decompiledTextArea.setCursor(Cursor.getDefaultCursor()); - //gotoDeclaration(); - } - } - - @Override - public void mousePressed(MouseEvent e) { - - } - - @Override - public void mouseReleased(MouseEvent e) { - } - - @Override - public void mouseEntered(MouseEvent e) { - } - - @Override - public void mouseExited(MouseEvent e) { - ctrlDown = false; - decompiledTextArea.setCursor(Cursor.getDefaultCursor()); - } - - @Override - public void mouseDragged(MouseEvent e) { - } - - @Override - public void mouseMoved(MouseEvent e) { - if (ctrlDown && decompiledTextArea.isEditable()) { - ctrlDown = false; - decompiledTextArea.setCursor(Cursor.getDefaultCursor()); - } - } - } - - public void reload() { - lastDecompiled = ""; - SWF swf = getSwf(); - if (swf != null) { - swf.clearScriptCache(); - } - - decompiledTextArea.reloadClass(); - detailPanel.methodTraitPanel.methodCodePanel.clear(); - } - - @Override - public void itemStateChanged(ItemEvent e) { - if (e.getSource() == constantTypeList) { - int index = ((JComboBox) e.getSource()).getSelectedIndex(); - if (index == -1) { - return; - } - updateConstList(); - } - } - - public void display() { - setVisible(true); - } - - public void hilightScript(SWF swf, String name) { - TagTreeModel ttm = (TagTreeModel) mainPanel.tagTree.getModel(); - TreeItem scriptsNode = ttm.getScriptsNode(swf); - if (scriptsNode instanceof ClassesListTreeModel) { - ClassesListTreeModel clModel = (ClassesListTreeModel) scriptsNode; - ScriptPack pack = null; - for (ScriptPack item : clModel.getList()) { - ClassPath classPath = item.getClassPath(); - - // first check the className to avoid calling unnecessary toString - if (name.endsWith(classPath.className) && classPath.toString().equals(name)) { - pack = item; - break; - } - } - if (pack != null) { - hilightScript(pack); - } - } - } - - public void hilightScript(ScriptPack pack) { - TagTreeModel ttm = (TagTreeModel) mainPanel.tagTree.getModel(); - final TreePath tp = ttm.getTreePath(pack); - View.execInEventDispatchLater(() -> { - mainPanel.tagTree.setSelectionPath(tp); - mainPanel.tagTree.scrollPathToVisible(tp); - }); - - } - - @Override - public void updateSearchPos(ABCPanelSearchResult item) { - ScriptPack pack = item.scriptPack; - setAbc(pack.abc); - decompiledTextArea.setScript(pack); - hilightScript(pack); - decompiledTextArea.setCaretPosition(0); - - View.execInEventDispatchLater(() -> { - searchPanel.showQuickFindDialog(decompiledTextArea); - }); - - } - - public boolean isDirectEditing() { - return saveDecompiledButton.isVisible() && saveDecompiledButton.isEnabled(); - } - - public void setDecompiledEditMode(boolean val) { - if (val) { - lastDecompiled = decompiledTextArea.getText(); - } else { - decompiledTextArea.setText(lastDecompiled); - } - - decompiledTextArea.setEditable(val); - saveDecompiledButton.setVisible(val); - saveDecompiledButton.setEnabled(false); - editDecompiledButton.setVisible(!val); - experimentalLabel.setVisible(!val); - cancelDecompiledButton.setVisible(val); - decompiledTextArea.getCaret().setVisible(true); - decLabel.setIcon(val ? View.getIcon("editing16") : null); - detailPanel.setVisible(!val); - - decompiledTextArea.ignoreCarret = val; - decompiledTextArea.requestFocusInWindow(); - } - - private void editDecompiledButtonActionPerformed(ActionEvent evt) { - File swc = Configuration.getPlayerSWC(); - final String adobePage = "http://www.adobe.com/support/flashplayer/downloads.html"; - if (swc == null) { - if (View.showConfirmDialog(this, AppStrings.translate("message.action.playerglobal.needed").replace("%adobehomepage%", adobePage), AppStrings.translate("message.action.playerglobal.title"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE) == JOptionPane.OK_OPTION) { - - View.navigateUrl(adobePage); - - int ret; - do { - ret = View.showConfirmDialog(this, AppStrings.translate("message.action.playerglobal.place").replace("%libpath%", Configuration.getFlashLibPath().getAbsolutePath()), AppStrings.translate("message.action.playerglobal.title"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE); - swc = Configuration.getPlayerSWC(); - } while (ret == JOptionPane.OK_OPTION && swc == null); - } - } - if (swc != null) { - if (View.showConfirmDialog(null, AppStrings.translate("message.confirm.experimental.function"), AppStrings.translate("message.warning"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE, Configuration.warningExperimentalAS3Edit, JOptionPane.OK_OPTION) == JOptionPane.OK_OPTION) { - setDecompiledEditMode(true); - } - } - } - - private void cancelDecompiledButtonActionPerformed(ActionEvent evt) { - setDecompiledEditMode(false); - } - - private void saveDecompiledButtonActionPerformed(ActionEvent evt) { - ScriptPack pack = decompiledTextArea.getScriptLeaf(); - int oldIndex = pack.scriptIndex; - SWF.uncache(pack); - - try { - String oldSp = null; - List packs = abc.script_info.get(oldIndex).getPacks(abc, oldIndex, null); - if (!packs.isEmpty()) { - oldSp = packs.get(0).getClassPath().toString(); - } - - String as = decompiledTextArea.getText(); - abc.replaceScriptPack(pack, as); - lastDecompiled = as; - setDecompiledEditMode(false); - mainPanel.updateClassesList(); - - if (oldSp != null) { - hilightScript(getSwf(), oldSp); - } - - reload(); - View.showMessageDialog(this, AppStrings.translate("message.action.saved"), AppStrings.translate("dialog.message.title"), JOptionPane.INFORMATION_MESSAGE, Configuration.showCodeSavedMessage); - } catch (AVM2ParseException ex) { - abc.script_info.get(oldIndex).delete(abc, false); - decompiledTextArea.gotoLine((int) ex.line); - decompiledTextArea.markError(); - View.showMessageDialog(this, AppStrings.translate("error.action.save").replace("%error%", ex.text).replace("%line%", Long.toString(ex.line)), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); - } catch (CompilationException ex) { - abc.script_info.get(oldIndex).delete(abc, false); - decompiledTextArea.gotoLine((int) ex.line); - decompiledTextArea.markError(); - View.showMessageDialog(this, AppStrings.translate("error.action.save").replace("%error%", ex.text).replace("%line%", Long.toString(ex.line)), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); - } catch (Throwable ex) { - Logger.getLogger(ABCPanel.class.getName()).log(Level.SEVERE, null, ex); - } - } - - private void addTraitButtonActionPerformed(ActionEvent evt) { - int class_index = decompiledTextArea.getClassIndex(); - if (class_index < 0) { - return; - } - if (newTraitDialog == null) { - newTraitDialog = new NewTraitDialog(); - } - int void_type = abc.constants.getPublicQnameId("void", true);//abc.constants.forceGetMultinameId(new Multiname(Multiname.QNAME, abc.constants.forceGetStringId("void"), abc.constants.forceGetNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.forceGetStringId("")), 0), -1, -1, new ArrayList())); - int int_type = abc.constants.getPublicQnameId("int", true); //abc.constants.forceGetMultinameId(new Multiname(Multiname.QNAME, abc.constants.forceGetStringId("int"), abc.constants.forceGetNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.forceGetStringId("")), 0), -1, -1, new ArrayList())); - - Trait t = null; - int kind; - int nskind; - String name = null; - boolean isStatic; - Multiname m; - - boolean again = false; - loopm: - do { - if (again) { - View.showMessageDialog(null, AppStrings.translate("error.trait.exists").replace("%name%", name), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); - } - again = false; - if (newTraitDialog.showDialog() != AppDialog.OK_OPTION) { - return; - } - kind = newTraitDialog.getTraitType(); - nskind = newTraitDialog.getNamespaceKind(); - name = newTraitDialog.getTraitName(); - isStatic = newTraitDialog.getStatic(); - m = new Multiname(Multiname.QNAME, abc.constants.getStringId(name, true), abc.constants.getNamespaceId(new Namespace(nskind, abc.constants.getStringId("", true)), 0, true), 0, 0, new ArrayList<>()); - int mid = abc.constants.getMultinameId(m); - if (mid == 0) { - break; - } - for (Trait tr : abc.class_info.get(class_index).static_traits.traits) { - if (tr.name_index == mid) { - again = true; - break; - } - } - - for (Trait tr : abc.instance_info.get(class_index).instance_traits.traits) { - if (tr.name_index == mid) { - again = true; - break; - } - } - } while (again); - switch (kind) { - case Trait.TRAIT_GETTER: - case Trait.TRAIT_SETTER: - case Trait.TRAIT_METHOD: - TraitMethodGetterSetter tm = new TraitMethodGetterSetter(); - MethodInfo mi = new MethodInfo(new int[0], void_type, abc.constants.getStringId(name, true), 0, new ValueKind[0], new int[0]); - int method_info = abc.addMethodInfo(mi); - tm.method_info = method_info; - MethodBody body = new MethodBody(); - body.method_info = method_info; - body.init_scope_depth = 1; - body.max_regs = 1; - body.max_scope_depth = 1; - body.max_stack = 1; - body.exceptions = new ABCException[0]; - AVM2Code code = new AVM2Code(); - code.code.add(new AVM2Instruction(0, new GetLocal0Ins(), new int[0])); - code.code.add(new AVM2Instruction(0, new PushScopeIns(), new int[0])); - code.code.add(new AVM2Instruction(0, new ReturnVoidIns(), new int[0])); - body.setCode(code); - Traits traits = new Traits(); - traits.traits = new ArrayList<>(); - body.traits = traits; - abc.addMethodBody(body); - mi.setBody(body); - t = tm; - break; - case Trait.TRAIT_SLOT: - case Trait.TRAIT_CONST: - TraitSlotConst ts = new TraitSlotConst(); - ts.type_index = int_type; - ts.value_kind = ValueKind.CONSTANT_Int; - ts.value_index = abc.constants.getIntId(0, true); - t = ts; - break; - } - if (t != null) { - t.kindType = kind; - t.name_index = abc.constants.getMultinameId(m, true); - int traitId; - if (isStatic) { - traitId = abc.class_info.get(class_index).static_traits.addTrait(t); - } else { - traitId = abc.class_info.get(class_index).static_traits.traits.size() + abc.instance_info.get(class_index).instance_traits.addTrait(t); - } - reload(); - decompiledTextArea.gotoTrait(traitId); - } - } - - @Override - public boolean tryAutoSave() { - // todo: implement - return false; - } - - @Override - public boolean isEditing() { - return detailPanel.isEditing() || isModified(); - } -} +/* + * Copyright (C) 2010-2015 JPEXS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.jpexs.decompiler.flash.gui.abc; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.ClassPath; +import com.jpexs.decompiler.flash.abc.ScriptPack; +import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; +import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; +import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.GetLocal0Ins; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ReturnVoidIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushScopeIns; +import com.jpexs.decompiler.flash.abc.avm2.parser.AVM2ParseException; +import com.jpexs.decompiler.flash.abc.avm2.parser.script.Reference; +import com.jpexs.decompiler.flash.abc.types.ABCException; +import com.jpexs.decompiler.flash.abc.types.MethodBody; +import com.jpexs.decompiler.flash.abc.types.MethodInfo; +import com.jpexs.decompiler.flash.abc.types.Multiname; +import com.jpexs.decompiler.flash.abc.types.Namespace; +import com.jpexs.decompiler.flash.abc.types.ValueKind; +import com.jpexs.decompiler.flash.abc.types.traits.Trait; +import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter; +import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst; +import com.jpexs.decompiler.flash.abc.types.traits.Traits; +import com.jpexs.decompiler.flash.abc.usages.MultinameUsage; +import com.jpexs.decompiler.flash.abc.usages.TraitMultinameUsage; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.gui.AppDialog; +import com.jpexs.decompiler.flash.gui.AppStrings; +import com.jpexs.decompiler.flash.gui.HeaderLabel; +import com.jpexs.decompiler.flash.gui.Main; +import com.jpexs.decompiler.flash.gui.MainPanel; +import com.jpexs.decompiler.flash.gui.SearchListener; +import com.jpexs.decompiler.flash.gui.SearchPanel; +import com.jpexs.decompiler.flash.gui.SearchResultsDialog; +import com.jpexs.decompiler.flash.gui.TagEditorPanel; +import com.jpexs.decompiler.flash.gui.View; +import com.jpexs.decompiler.flash.gui.abc.tablemodels.DecimalTableModel; +import com.jpexs.decompiler.flash.gui.abc.tablemodels.DoubleTableModel; +import com.jpexs.decompiler.flash.gui.abc.tablemodels.IntTableModel; +import com.jpexs.decompiler.flash.gui.abc.tablemodels.MultinameTableModel; +import com.jpexs.decompiler.flash.gui.abc.tablemodels.NamespaceSetTableModel; +import com.jpexs.decompiler.flash.gui.abc.tablemodels.NamespaceTableModel; +import com.jpexs.decompiler.flash.gui.abc.tablemodels.StringTableModel; +import com.jpexs.decompiler.flash.gui.abc.tablemodels.UIntTableModel; +import com.jpexs.decompiler.flash.gui.controls.JPersistentSplitPane; +import com.jpexs.decompiler.flash.gui.editor.LinkHandler; +import com.jpexs.decompiler.flash.gui.tagtree.TagTreeModel; +import com.jpexs.decompiler.flash.tags.ABCContainerTag; +import com.jpexs.decompiler.flash.treeitems.TreeItem; +import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.helpers.CancellableWorker; +import java.awt.BorderLayout; +import java.awt.Cursor; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Pattern; +import javax.swing.AbstractAction; +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.JTabbedPane; +import javax.swing.JTable; +import javax.swing.JToggleButton; +import javax.swing.SwingConstants; +import javax.swing.border.BevelBorder; +import javax.swing.table.DefaultTableModel; +import javax.swing.text.Highlighter; +import javax.swing.tree.TreePath; +import jsyntaxpane.SyntaxDocument; +import jsyntaxpane.Token; +import jsyntaxpane.TokenType; + +/** + * + * @author JPEXS + */ +public class ABCPanel extends JPanel implements ItemListener, SearchListener, TagEditorPanel { + + private final MainPanel mainPanel; + + public final TraitsList navigator; + + public ABC abc; + + public final DecompiledEditorPane decompiledTextArea; + + public final JScrollPane decompiledScrollPane; + + private final JPersistentSplitPane splitPane; + + private final JTable constantTable; + + public JComboBox constantTypeList; + + public JLabel asmLabel = new HeaderLabel(AppStrings.translate("panel.disassembled")); + + public JLabel decLabel = new HeaderLabel(AppStrings.translate("panel.decompiled")); + + public final DetailPanel detailPanel; + + private final JPanel navPanel; + + public final JTabbedPane tabbedPane; + + private final SearchPanel searchPanel; + + private NewTraitDialog newTraitDialog; + + public final JLabel scriptNameLabel; + + private final JLabel experimentalLabel = new JLabel(AppStrings.translate("action.edit.experimental")); + + private final JButton editDecompiledButton = new JButton(AppStrings.translate("button.edit"), View.getIcon("edit16")); + + private final JButton saveDecompiledButton = new JButton(AppStrings.translate("button.save"), View.getIcon("save16")); + + private final JButton cancelDecompiledButton = new JButton(AppStrings.translate("button.cancel"), View.getIcon("cancel16")); + + private String lastDecompiled = null; + + public MainPanel getMainPanel() { + return mainPanel; + } + + public boolean search(final String txt, boolean ignoreCase, boolean regexp) { + if ((txt != null) && (!txt.isEmpty())) { + searchPanel.setOptions(ignoreCase, regexp); + TagTreeModel ttm = (TagTreeModel) mainPanel.tagTree.getModel(); + TreeItem scriptsNode = ttm.getScriptsNode(mainPanel.getCurrentSwf()); + final List found = new ArrayList<>(); + if (scriptsNode instanceof ClassesListTreeModel) { + ClassesListTreeModel clModel = (ClassesListTreeModel) scriptsNode; + List allpacks = clModel.getList(); + final Pattern pat = regexp + ? Pattern.compile(txt, ignoreCase ? (Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE) : 0) + : Pattern.compile(Pattern.quote(txt), ignoreCase ? (Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE) : 0); + int pos = 0; + for (final ScriptPack pack : allpacks) { + pos++; + String workText = AppStrings.translate("work.searching"); + String decAdd = ""; + if (!SWF.isCached(pack)) { + decAdd = ", " + AppStrings.translate("work.decompiling"); + } + + try { + CancellableWorker worker = new CancellableWorker() { + + @Override + public Void doInBackground() throws Exception { + if (pat.matcher(SWF.getCached(pack).text).find()) { + ABCPanelSearchResult searchResult = new ABCPanelSearchResult(); + searchResult.scriptPack = pack; + found.add(searchResult); + } + return null; + } + }; + worker.execute(); + Main.startWork(workText + " \"" + txt + "\"" + decAdd + " - (" + pos + "/" + allpacks.size() + ") " + pack.getClassPath().toString() + "... ", worker); + worker.get(); + } catch (InterruptedException ex) { + break; + } catch (ExecutionException ex) { + Logger.getLogger(ABCPanel.class.getName()).log(Level.SEVERE, null, ex); + } + } + } + + Main.stopWork(); + + searchPanel.setSearchText(txt); + + View.execInEventDispatch(() -> { + SearchResultsDialog sr = new SearchResultsDialog<>(ABCPanel.this.mainPanel.getMainFrame().getWindow(), txt, ABCPanel.this); + sr.setResults(found); + sr.setVisible(true); + }); + + return true; + + //return searchPanel.setResults(found); + } + return false; + } + + public void setAbc(ABC abc) { + this.abc = abc; + setDecompiledEditMode(false); + navigator.setAbc(abc); + updateConstList(); + } + + public void updateConstList() { + switch (constantTypeList.getSelectedIndex()) { + case 0: + View.autoResizeColWidth(constantTable, new UIntTableModel(abc)); + break; + case 1: + View.autoResizeColWidth(constantTable, new IntTableModel(abc)); + break; + case 2: + View.autoResizeColWidth(constantTable, new DoubleTableModel(abc)); + break; + case 3: + View.autoResizeColWidth(constantTable, new DecimalTableModel(abc)); + break; + case 4: + View.autoResizeColWidth(constantTable, new StringTableModel(abc)); + break; + case 5: + View.autoResizeColWidth(constantTable, new NamespaceTableModel(abc)); + break; + case 6: + View.autoResizeColWidth(constantTable, new NamespaceSetTableModel(abc)); + break; + case 7: + View.autoResizeColWidth(constantTable, new MultinameTableModel(abc)); + break; + } + //DefaultTableColumnModel colModel = (DefaultTableColumnModel) constantTable.getColumnModel(); + //colModel.getColumn(0).setMaxWidth(50); + } + + public SWF getSwf() { + return abc == null ? null : abc.getSwf(); + } + + public List getAbcList() { + SWF swf = getSwf(); + return swf == null ? null : swf.getAbcList(); + } + + public void clearSwf() { + this.abc = null; + constantTable.setModel(new DefaultTableModel()); + navigator.clearAbc(); + decompiledTextArea.clearScript(); + } + + public ABCPanel(MainPanel mainPanel) { + + this.mainPanel = mainPanel; + setLayout(new BorderLayout()); + + decompiledTextArea = new DecompiledEditorPane(this); + decompiledTextArea.addTextChangedListener(this::decompiledTextAreaTextChanged); + + decompiledTextArea.setLinkHandler(new LinkHandler() { + + @Override + public boolean isLink(Token token) { + return hasDeclaration(token.length == 1 ? token.start : token.start + 1); + } + + @Override + public void handleLink(Token token) { + gotoDeclaration(token.length == 1 ? token.start : token.start + 1); + } + + @Override + public Highlighter.HighlightPainter linkPainter() { + return decompiledTextArea.linkPainter(); + } + }); + + searchPanel = new SearchPanel<>(new FlowLayout(), this); + + decompiledScrollPane = new JScrollPane(decompiledTextArea); + + JPanel iconDecPanel = new JPanel(); + iconDecPanel.setLayout(new BoxLayout(iconDecPanel, BoxLayout.Y_AXIS)); + JPanel iconsPanel = new JPanel(); + iconsPanel.setLayout(new BoxLayout(iconsPanel, BoxLayout.X_AXIS)); + + JButton newTraitButton = new JButton(View.getIcon("traitadd16")); + newTraitButton.setMargin(new Insets(5, 5, 5, 5)); + newTraitButton.addActionListener(this::addTraitButtonActionPerformed); + newTraitButton.setToolTipText(AppStrings.translate("button.addtrait")); + iconsPanel.add(newTraitButton); + + scriptNameLabel = new JLabel("-"); + scriptNameLabel.setAlignmentX(0); + iconsPanel.setAlignmentX(0); + decompiledScrollPane.setAlignmentX(0); + iconDecPanel.add(scriptNameLabel); + iconDecPanel.add(iconsPanel); + iconDecPanel.add(decompiledScrollPane); + + JPanel decButtonsPan = new JPanel(new FlowLayout()); + decButtonsPan.setBorder(new BevelBorder(BevelBorder.RAISED)); + decButtonsPan.add(editDecompiledButton); + decButtonsPan.add(experimentalLabel); + decButtonsPan.add(saveDecompiledButton); + decButtonsPan.add(cancelDecompiledButton); + + editDecompiledButton.setMargin(new Insets(3, 3, 3, 10)); + saveDecompiledButton.setMargin(new Insets(3, 3, 3, 10)); + cancelDecompiledButton.setMargin(new Insets(3, 3, 3, 10)); + + saveDecompiledButton.addActionListener(this::saveDecompiledButtonActionPerformed); + editDecompiledButton.addActionListener(this::editDecompiledButtonActionPerformed); + cancelDecompiledButton.addActionListener(this::cancelDecompiledButtonActionPerformed); + + saveDecompiledButton.setVisible(false); + cancelDecompiledButton.setVisible(false); + decButtonsPan.setAlignmentX(0); + + JPanel decPanel = new JPanel(new BorderLayout()); + decPanel.add(searchPanel, BorderLayout.NORTH); + decPanel.add(iconDecPanel, BorderLayout.CENTER); + decPanel.add(decButtonsPan, BorderLayout.SOUTH); + detailPanel = new DetailPanel(this); + JPanel panB = new JPanel(); + panB.setLayout(new BorderLayout()); + panB.add(decPanel, BorderLayout.CENTER); + panB.add(decLabel, BorderLayout.NORTH); + decLabel.setHorizontalAlignment(SwingConstants.CENTER); + //decLabel.setBorder(new BevelBorder(BevelBorder.RAISED)); + splitPane = new JPersistentSplitPane(JSplitPane.HORIZONTAL_SPLIT, panB, detailPanel, Configuration.guiAvm2SplitPaneDividerLocationPercent); + splitPane.setContinuousLayout(true); + + decompiledTextArea.changeContentType("text/actionscript"); + decompiledTextArea.setFont(new Font("Monospaced", Font.PLAIN, decompiledTextArea.getFont().getSize())); + + View.addEditorAction(decompiledTextArea, new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + int multinameIndex = decompiledTextArea.getMultinameUnderCaret(); + if (multinameIndex > -1) { + UsageFrame usageFrame = new UsageFrame(abc, multinameIndex, ABCPanel.this, false); + usageFrame.setVisible(true); + } + } + }, "find-usages", AppStrings.translate("abc.action.find-usages"), "control U"); + + View.addEditorAction(decompiledTextArea, new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + gotoDeclaration(decompiledTextArea.getCaretPosition()); + } + }, "find-declaration", AppStrings.translate("abc.action.find-declaration"), "control B"); + + CtrlClickHandler cch = new CtrlClickHandler(); + decompiledTextArea.addKeyListener(cch); + decompiledTextArea.addMouseListener(cch); + decompiledTextArea.addMouseMotionListener(cch); + + navigator = new TraitsList(this); + + navPanel = new JPanel(new BorderLayout()); + JPanel navIconsPanel = new JPanel(); + navIconsPanel.setLayout(new BoxLayout(navIconsPanel, BoxLayout.X_AXIS)); + final JToggleButton sortButton = new JToggleButton(View.getIcon("sort16")); + sortButton.setMargin(new Insets(3, 3, 3, 3)); + navIconsPanel.add(sortButton); + navPanel.add(navIconsPanel, BorderLayout.SOUTH); + navPanel.add(new JScrollPane(navigator), BorderLayout.CENTER); + sortButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + navigator.setSorted(sortButton.isSelected()); + navigator.updateUI(); + } + }); + + tabbedPane = new JTabbedPane(); + tabbedPane.addTab(AppStrings.translate("traits"), navPanel); + add(splitPane, BorderLayout.CENTER); + + JPanel panConstants = new JPanel(); + panConstants.setLayout(new BorderLayout()); + + constantTypeList = new JComboBox<>(new String[]{"UINT", "INT", "DOUBLE", "DECIMAL", "STRING", "NAMESPACE", "NAMESPACESET", "MULTINAME"}); + constantTable = new JTable(); + if (abc != null) { + View.autoResizeColWidth(constantTable, new UIntTableModel(abc)); + } + constantTable.setAutoCreateRowSorter(true); + + final ABCPanel t = this; + constantTable.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (e.getClickCount() == 2) { + if (constantTypeList.getSelectedIndex() == 7) { //MULTINAME + int rowIndex = constantTable.getSelectedRow(); + if (rowIndex == -1) { + return; + } + int multinameIndex = constantTable.convertRowIndexToModel(rowIndex); + if (multinameIndex > 0) { + UsageFrame usageFrame = new UsageFrame(abc, multinameIndex, t, false); + usageFrame.setVisible(true); + } + } + } + } + }); + constantTypeList.addItemListener(this); + panConstants.add(constantTypeList, BorderLayout.NORTH); + panConstants.add(new JScrollPane(constantTable), BorderLayout.CENTER); + tabbedPane.addTab(AppStrings.translate("constants"), panConstants); + } + + private void decompiledTextAreaTextChanged() { + setModified(true); + } + + private boolean isModified() { + return saveDecompiledButton.isVisible() && saveDecompiledButton.isEnabled(); + } + + private void setModified(boolean value) { + saveDecompiledButton.setEnabled(value); + cancelDecompiledButton.setEnabled(value); + } + + private boolean hasDeclaration(int pos) { + + SyntaxDocument sd = (SyntaxDocument) decompiledTextArea.getDocument(); + Token t = sd.getTokenAt(pos); + if (t == null || (t.type != TokenType.IDENTIFIER && t.type != TokenType.KEYWORD && t.type != TokenType.REGEX)) { + return false; + } + Reference abcIndex = new Reference<>(0); + Reference classIndex = new Reference<>(0); + Reference traitIndex = new Reference<>(0); + Reference multinameIndexRef = new Reference<>(0); + Reference classTrait = new Reference<>(false); + + if (decompiledTextArea.getPropertyTypeAtPos(pos, abcIndex, classIndex, traitIndex, classTrait, multinameIndexRef)) { + return true; + } + int multinameIndex = decompiledTextArea.getMultinameAtPos(pos); + if (multinameIndex > -1) { + if (multinameIndex == 0) { + return false; + } + List usages = abc.findMultinameDefinition(multinameIndex); + + Multiname m = abc.constants.constant_multiname.get(multinameIndex); + //search other ABC tags if this is not private multiname + if (m.namespace_index > 0 && abc.constants.constant_namespace.get(m.namespace_index).kind != Namespace.KIND_PRIVATE) { + for (ABCContainerTag at : getAbcList()) { + ABC a = at.getABC(); + if (a == abc) { + continue; + } + int mid = a.constants.getMultinameId(m, false); + if (mid > 0) { + usages.addAll(a.findMultinameDefinition(mid)); + } + } + } + + //more than one? display list + if (!usages.isEmpty()) { + return true; + } + } + + return decompiledTextArea.getLocalDeclarationOfPos(pos, new Reference<>("")) != -1; + } + + private void gotoDeclaration(int pos) { + Reference abcIndex = new Reference<>(0); + Reference classIndex = new Reference<>(0); + Reference traitIndex = new Reference<>(0); + Reference classTrait = new Reference<>(false); + Reference multinameIndexRef = new Reference<>(0); + + if (decompiledTextArea.getPropertyTypeAtPos(pos, abcIndex, classIndex, traitIndex, classTrait, multinameIndexRef)) { + UsageFrame.gotoUsage(ABCPanel.this, new TraitMultinameUsage(getAbcList().get(abcIndex.getVal()).getABC(), multinameIndexRef.getVal(), classIndex.getVal(), traitIndex.getVal(), classTrait.getVal(), null, -1) { + }); + return; + } + int multinameIndex = decompiledTextArea.getMultinameAtPos(pos); + if (multinameIndex > -1) { + List usages = abc.findMultinameDefinition(multinameIndex); + + Multiname m = abc.constants.constant_multiname.get(multinameIndex); + //search other ABC tags if this is not private multiname + if (m.namespace_index > 0 && abc.constants.constant_namespace.get(m.namespace_index).kind != Namespace.KIND_PRIVATE) { + for (ABCContainerTag at : getAbcList()) { + ABC a = at.getABC(); + if (a == abc) { + continue; + } + int mid = a.constants.getMultinameId(m, false); + if (mid > 0) { + usages.addAll(a.findMultinameDefinition(mid)); + } + } + } + + //more than one? display list + if (usages.size() > 1) { + UsageFrame usageFrame = new UsageFrame(abc, multinameIndex, ABCPanel.this, true); + usageFrame.setVisible(true); + return; + } else if (!usages.isEmpty()) { //one + UsageFrame.gotoUsage(ABCPanel.this, usages.get(0)); + return; + } + } + + int dpos = decompiledTextArea.getLocalDeclarationOfPos(pos, new Reference<>("")); + if (dpos > -1) { + decompiledTextArea.setCaretPosition(dpos); + } + + } + + private class CtrlClickHandler extends KeyAdapter implements MouseListener, MouseMotionListener { + + private boolean ctrlDown = false; + + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == 17 && !decompiledTextArea.isEditable()) { + ctrlDown = true; + //decompiledTextArea.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + } + } + + @Override + public void keyReleased(KeyEvent e) { + if (e.getKeyCode() == 17) { + ctrlDown = false; + //decompiledTextArea.setCursor(Cursor.getDefaultCursor()); + } + } + + @Override + public void mouseClicked(MouseEvent e) { + if (ctrlDown && e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() == 1 && !decompiledTextArea.isEditable()) { + ctrlDown = false; + //decompiledTextArea.setCursor(Cursor.getDefaultCursor()); + //gotoDeclaration(); + } + } + + @Override + public void mousePressed(MouseEvent e) { + + } + + @Override + public void mouseReleased(MouseEvent e) { + } + + @Override + public void mouseEntered(MouseEvent e) { + } + + @Override + public void mouseExited(MouseEvent e) { + ctrlDown = false; + decompiledTextArea.setCursor(Cursor.getDefaultCursor()); + } + + @Override + public void mouseDragged(MouseEvent e) { + } + + @Override + public void mouseMoved(MouseEvent e) { + if (ctrlDown && decompiledTextArea.isEditable()) { + ctrlDown = false; + decompiledTextArea.setCursor(Cursor.getDefaultCursor()); + } + } + } + + public void reload() { + lastDecompiled = ""; + SWF swf = getSwf(); + if (swf != null) { + swf.clearScriptCache(); + } + + decompiledTextArea.reloadClass(); + detailPanel.methodTraitPanel.methodCodePanel.clear(); + } + + @Override + public void itemStateChanged(ItemEvent e) { + if (e.getSource() == constantTypeList) { + int index = ((JComboBox) e.getSource()).getSelectedIndex(); + if (index == -1) { + return; + } + updateConstList(); + } + } + + public void display() { + setVisible(true); + } + + public void hilightScript(SWF swf, String name) { + TagTreeModel ttm = (TagTreeModel) mainPanel.tagTree.getModel(); + TreeItem scriptsNode = ttm.getScriptsNode(swf); + if (scriptsNode instanceof ClassesListTreeModel) { + ClassesListTreeModel clModel = (ClassesListTreeModel) scriptsNode; + ScriptPack pack = null; + for (ScriptPack item : clModel.getList()) { + ClassPath classPath = item.getClassPath(); + + // first check the className to avoid calling unnecessary toString + if (name.endsWith(classPath.className) && classPath.toString().equals(name)) { + pack = item; + break; + } + } + if (pack != null) { + hilightScript(pack); + } + } + } + + public void hilightScript(ScriptPack pack) { + TagTreeModel ttm = (TagTreeModel) mainPanel.tagTree.getModel(); + final TreePath tp = ttm.getTreePath(pack); + View.execInEventDispatchLater(() -> { + mainPanel.tagTree.setSelectionPath(tp); + mainPanel.tagTree.scrollPathToVisible(tp); + }); + + } + + @Override + public void updateSearchPos(ABCPanelSearchResult item) { + ScriptPack pack = item.scriptPack; + setAbc(pack.abc); + decompiledTextArea.setScript(pack); + hilightScript(pack); + decompiledTextArea.setCaretPosition(0); + + View.execInEventDispatchLater(() -> { + searchPanel.showQuickFindDialog(decompiledTextArea); + }); + + } + + public boolean isDirectEditing() { + return saveDecompiledButton.isVisible() && saveDecompiledButton.isEnabled(); + } + + public void setDecompiledEditMode(boolean val) { + if (val) { + lastDecompiled = decompiledTextArea.getText(); + } else { + decompiledTextArea.setText(lastDecompiled); + } + + decompiledTextArea.setEditable(val); + saveDecompiledButton.setVisible(val); + saveDecompiledButton.setEnabled(false); + editDecompiledButton.setVisible(!val); + experimentalLabel.setVisible(!val); + cancelDecompiledButton.setVisible(val); + decompiledTextArea.getCaret().setVisible(true); + decLabel.setIcon(val ? View.getIcon("editing16") : null); + detailPanel.setVisible(!val); + + decompiledTextArea.ignoreCarret = val; + decompiledTextArea.requestFocusInWindow(); + } + + private void editDecompiledButtonActionPerformed(ActionEvent evt) { + File swc = Configuration.getPlayerSWC(); + final String adobePage = "http://www.adobe.com/support/flashplayer/downloads.html"; + if (swc == null) { + if (View.showConfirmDialog(this, AppStrings.translate("message.action.playerglobal.needed").replace("%adobehomepage%", adobePage), AppStrings.translate("message.action.playerglobal.title"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE) == JOptionPane.OK_OPTION) { + + View.navigateUrl(adobePage); + + int ret; + do { + ret = View.showConfirmDialog(this, AppStrings.translate("message.action.playerglobal.place").replace("%libpath%", Configuration.getFlashLibPath().getAbsolutePath()), AppStrings.translate("message.action.playerglobal.title"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE); + swc = Configuration.getPlayerSWC(); + } while (ret == JOptionPane.OK_OPTION && swc == null); + } + } + if (swc != null) { + if (View.showConfirmDialog(null, AppStrings.translate("message.confirm.experimental.function"), AppStrings.translate("message.warning"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE, Configuration.warningExperimentalAS3Edit, JOptionPane.OK_OPTION) == JOptionPane.OK_OPTION) { + setDecompiledEditMode(true); + } + } + } + + private void cancelDecompiledButtonActionPerformed(ActionEvent evt) { + setDecompiledEditMode(false); + } + + private void saveDecompiledButtonActionPerformed(ActionEvent evt) { + ScriptPack pack = decompiledTextArea.getScriptLeaf(); + int oldIndex = pack.scriptIndex; + SWF.uncache(pack); + + try { + String oldSp = null; + List packs = abc.script_info.get(oldIndex).getPacks(abc, oldIndex, null, pack.allABCs); + if (!packs.isEmpty()) { + oldSp = packs.get(0).getClassPath().toString(); + } + + String as = decompiledTextArea.getText(); + abc.replaceScriptPack(pack, as); + lastDecompiled = as; + setDecompiledEditMode(false); + mainPanel.updateClassesList(); + + if (oldSp != null) { + hilightScript(getSwf(), oldSp); + } + + reload(); + View.showMessageDialog(this, AppStrings.translate("message.action.saved"), AppStrings.translate("dialog.message.title"), JOptionPane.INFORMATION_MESSAGE, Configuration.showCodeSavedMessage); + } catch (AVM2ParseException ex) { + abc.script_info.get(oldIndex).delete(abc, false); + decompiledTextArea.gotoLine((int) ex.line); + decompiledTextArea.markError(); + View.showMessageDialog(this, AppStrings.translate("error.action.save").replace("%error%", ex.text).replace("%line%", Long.toString(ex.line)), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); + } catch (CompilationException ex) { + abc.script_info.get(oldIndex).delete(abc, false); + decompiledTextArea.gotoLine((int) ex.line); + decompiledTextArea.markError(); + ex.printStackTrace(); + View.showMessageDialog(this, AppStrings.translate("error.action.save").replace("%error%", ex.text).replace("%line%", Long.toString(ex.line)), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); + } catch (Throwable ex) { + Logger.getLogger(ABCPanel.class.getName()).log(Level.SEVERE, null, ex); + } + } + + private void addTraitButtonActionPerformed(ActionEvent evt) { + int class_index = decompiledTextArea.getClassIndex(); + if (class_index < 0) { + return; + } + if (newTraitDialog == null) { + newTraitDialog = new NewTraitDialog(); + } + int void_type = abc.constants.getPublicQnameId("void", true);//abc.constants.forceGetMultinameId(new Multiname(Multiname.QNAME, abc.constants.forceGetStringId("void"), abc.constants.forceGetNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.forceGetStringId("")), 0), -1, -1, new ArrayList())); + int int_type = abc.constants.getPublicQnameId("int", true); //abc.constants.forceGetMultinameId(new Multiname(Multiname.QNAME, abc.constants.forceGetStringId("int"), abc.constants.forceGetNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.forceGetStringId("")), 0), -1, -1, new ArrayList())); + + Trait t = null; + int kind; + int nskind; + String name = null; + boolean isStatic; + Multiname m; + + boolean again = false; + loopm: + do { + if (again) { + View.showMessageDialog(null, AppStrings.translate("error.trait.exists").replace("%name%", name), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); + } + again = false; + if (newTraitDialog.showDialog() != AppDialog.OK_OPTION) { + return; + } + kind = newTraitDialog.getTraitType(); + nskind = newTraitDialog.getNamespaceKind(); + name = newTraitDialog.getTraitName(); + isStatic = newTraitDialog.getStatic(); + m = new Multiname(Multiname.QNAME, abc.constants.getStringId(name, true), abc.constants.getNamespaceId(new Namespace(nskind, abc.constants.getStringId("", true)), 0, true), 0, 0, new ArrayList<>()); + int mid = abc.constants.getMultinameId(m); + if (mid == 0) { + break; + } + for (Trait tr : abc.class_info.get(class_index).static_traits.traits) { + if (tr.name_index == mid) { + again = true; + break; + } + } + + for (Trait tr : abc.instance_info.get(class_index).instance_traits.traits) { + if (tr.name_index == mid) { + again = true; + break; + } + } + } while (again); + switch (kind) { + case Trait.TRAIT_GETTER: + case Trait.TRAIT_SETTER: + case Trait.TRAIT_METHOD: + TraitMethodGetterSetter tm = new TraitMethodGetterSetter(); + MethodInfo mi = new MethodInfo(new int[0], void_type, abc.constants.getStringId(name, true), 0, new ValueKind[0], new int[0]); + int method_info = abc.addMethodInfo(mi); + tm.method_info = method_info; + MethodBody body = new MethodBody(); + body.method_info = method_info; + body.init_scope_depth = 1; + body.max_regs = 1; + body.max_scope_depth = 1; + body.max_stack = 1; + body.exceptions = new ABCException[0]; + AVM2Code code = new AVM2Code(); + code.code.add(new AVM2Instruction(0, new GetLocal0Ins(), new int[0])); + code.code.add(new AVM2Instruction(0, new PushScopeIns(), new int[0])); + code.code.add(new AVM2Instruction(0, new ReturnVoidIns(), new int[0])); + body.setCode(code); + Traits traits = new Traits(); + traits.traits = new ArrayList<>(); + body.traits = traits; + abc.addMethodBody(body); + mi.setBody(body); + t = tm; + break; + case Trait.TRAIT_SLOT: + case Trait.TRAIT_CONST: + TraitSlotConst ts = new TraitSlotConst(); + ts.type_index = int_type; + ts.value_kind = ValueKind.CONSTANT_Int; + ts.value_index = abc.constants.getIntId(0, true); + t = ts; + break; + } + if (t != null) { + t.kindType = kind; + t.name_index = abc.constants.getMultinameId(m, true); + int traitId; + if (isStatic) { + traitId = abc.class_info.get(class_index).static_traits.addTrait(t); + } else { + traitId = abc.class_info.get(class_index).static_traits.traits.size() + abc.instance_info.get(class_index).instance_traits.addTrait(t); + } + reload(); + decompiledTextArea.gotoTrait(traitId); + } + } + + @Override + public boolean tryAutoSave() { + // todo: implement + return false; + } + + @Override + public boolean isEditing() { + return detailPanel.isEditing() || isModified(); + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/debugger/DebuggerTools.java b/src/com/jpexs/decompiler/flash/gui/debugger/DebuggerTools.java index 2e6928d29..d519d6808 100644 --- a/src/com/jpexs/decompiler/flash/gui/debugger/DebuggerTools.java +++ b/src/com/jpexs/decompiler/flash/gui/debugger/DebuggerTools.java @@ -1,190 +1,196 @@ -/* - * Copyright (C) 2010-2015 JPEXS - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.jpexs.decompiler.flash.gui.debugger; - -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.ScriptPack; -import com.jpexs.decompiler.flash.abc.types.Multiname; -import com.jpexs.decompiler.flash.abc.types.Namespace; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.gui.DebugLogDialog; -import com.jpexs.decompiler.flash.gui.Main; -import com.jpexs.decompiler.flash.tags.ABCContainerTag; -import com.jpexs.decompiler.flash.tags.Tag; -import com.jpexs.helpers.Helper; -import java.util.Random; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.regex.Pattern; - -/** - * - * @author JPEXS - */ -public class DebuggerTools { - - private static final Logger logger = Logger.getLogger(DebuggerTools.class.getName()); - - public static final String DEBUGGER_PACKAGE = "com.jpexs.decompiler.flash.debugger"; - - private static Debugger debugger; - - private static ScriptPack getDebuggerScriptPack(SWF swf) { - for (ABCContainerTag ac : swf.getAbcList()) { - ABC a = ac.getABC(); - for (ScriptPack m : a.getScriptPacks(DEBUGGER_PACKAGE)) { - if (isDebuggerClass(m.getClassPath().packageStr, null)) { - return m; - } - } - } - return null; - } - - public static boolean hasDebugger(SWF swf) { - return getDebuggerScriptPack(swf) != null; - } - - private static boolean isDebuggerClass(String tested, String cls) { - if (tested == null) { - return false; - } - - // fast check, because dynamic regex compile and match is expensive - if (!tested.startsWith(DEBUGGER_PACKAGE)) { - return false; - } - - if (cls == null) { - cls = ""; - } else { - cls = "\\." + Pattern.quote(cls); - } - - return tested.matches(Pattern.quote(DEBUGGER_PACKAGE) + "(\\.pkg[a-f0-9]+)?" + cls); - } - - public static void replaceTraceCalls(SWF swf, String fname) { - if (hasDebugger(swf)) { - String debuggerPkg = getDebuggerScriptPack(swf).getClassPath().packageStr; - //change trace to fname - for (ABCContainerTag ct : swf.getAbcList()) { - ABC a = ct.getABC(); - for (int i = 1; i < a.constants.constant_multiname.size(); i++) { - Multiname m = a.constants.constant_multiname.get(i); - if ("trace".equals(m.getNameWithNamespace(a.constants, true))) { - m.namespace_index = a.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, a.constants.getStringId(debuggerPkg, true)), 0, true); - m.name_index = a.constants.getStringId(fname, true); - ((Tag) ct).setModified(true); - } - } - } - } - } - - public static void switchDebugger(SWF swf) { - int port = Configuration.debuggerPort.get(); - ScriptPack found = getDebuggerScriptPack(swf); - if (found != null) { - ABCContainerTag tag = found.abc.parentTag; - swf.tags.remove((Tag) tag); - swf.getAbcList().remove(tag); - - //Change all debugger calls to normal trace - for (ABCContainerTag ct : swf.getAbcList()) { - ABC a = ct.getABC(); - for (int i = 1; i < a.constants.constant_multiname.size(); i++) { - Multiname m = a.constants.constant_multiname.get(i); - String packageStr = m.getNameWithNamespace(a.constants, true); - if (isDebuggerClass(packageStr, "debugTrace") - || isDebuggerClass(packageStr, "debugAlert") - || isDebuggerClass(packageStr, "debugSocket") - || isDebuggerClass(packageStr, "debugConsole")) { - m.name_index = a.constants.getStringId("trace", true); - m.namespace_index = a.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, a.constants.getStringId("", true)), 0, true); - ((Tag) ct).setModified(true); - } - } - } - } else { - Random rnd = new Random(); - byte rb[] = new byte[16]; - rnd.nextBytes(rb); - String rhex = Helper.byteArrayToHex(rb); - try { - //load debug swf - SWF debugSWF = new SWF(Main.class.getClassLoader().getResourceAsStream("com/jpexs/decompiler/flash/gui/debugger/debug.swf"), false); - - ABCContainerTag firstAbc = swf.getAbcList().get(0); - String newdebuggerpkg = DEBUGGER_PACKAGE; - - if (Configuration.randomDebuggerPackage.get()) { - newdebuggerpkg += ".pkg" + rhex; - } - - //add debug ABC tags to main SWF - for (ABCContainerTag ds : debugSWF.getAbcList()) { - ABC a = ds.getABC(); - //Append random hex to Debugger package name - for (int i = 1; i < a.constants.constant_namespace.size(); i++) { - if (a.constants.constant_namespace.get(i).hasName(DEBUGGER_PACKAGE, a.constants)) { - a.constants.constant_namespace.get(i).name_index = a.constants.getStringId(newdebuggerpkg, true); - } - } - //Set debugger port to actually set port - for (int i = 0; i < a.constants.constant_int.size(); i++) { - if (a.constants.constant_int.get(i) == 123456L) { - a.constants.constant_int.set(i, (long) port); - } - } - //Add to target SWF - ((Tag) ds).setSwf(swf); - swf.tags.add(swf.tags.indexOf(firstAbc), (Tag) ds); - swf.getAbcList().add(swf.getAbcList().indexOf(firstAbc), ds); - ((Tag) ds).setModified(true); - } - - } catch (Exception ex) { - logger.log(Level.SEVERE, "Error while attaching debugger", ex); - //ignore - } - - } - initDebugger(); - } - - private static void initDebugger() { - if (debugger == null) { - synchronized (Main.class) { - if (debugger == null) { - Debugger dbg = new Debugger(Configuration.debuggerPort.get()); - dbg.start(); - debugger = dbg; - } - } - } - } - - public static void debuggerShowLog() { - initDebugger(); - if (Main.debugDialog == null) { - Main.debugDialog = new DebugLogDialog(debugger); - } - Main.debugDialog.setVisible(true); - } -} +/* + * Copyright (C) 2010-2015 JPEXS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.jpexs.decompiler.flash.gui.debugger; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.ScriptPack; +import com.jpexs.decompiler.flash.abc.types.Multiname; +import com.jpexs.decompiler.flash.abc.types.Namespace; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.gui.DebugLogDialog; +import com.jpexs.decompiler.flash.gui.Main; +import com.jpexs.decompiler.flash.tags.ABCContainerTag; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.helpers.Helper; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Pattern; + +/** + * + * @author JPEXS + */ +public class DebuggerTools { + + private static final Logger logger = Logger.getLogger(DebuggerTools.class.getName()); + + public static final String DEBUGGER_PACKAGE = "com.jpexs.decompiler.flash.debugger"; + + private static Debugger debugger; + + private static ScriptPack getDebuggerScriptPack(SWF swf) { + List allAbcList = new ArrayList<>(); + for (ABCContainerTag ac : swf.getAbcList()) { + allAbcList.add(ac.getABC()); + } + for (ABCContainerTag ac : swf.getAbcList()) { + ABC a = ac.getABC(); + for (ScriptPack m : a.getScriptPacks(DEBUGGER_PACKAGE, allAbcList)) { + if (isDebuggerClass(m.getClassPath().packageStr, null)) { + return m; + } + } + } + return null; + } + + public static boolean hasDebugger(SWF swf) { + return getDebuggerScriptPack(swf) != null; + } + + private static boolean isDebuggerClass(String tested, String cls) { + if (tested == null) { + return false; + } + + // fast check, because dynamic regex compile and match is expensive + if (!tested.startsWith(DEBUGGER_PACKAGE)) { + return false; + } + + if (cls == null) { + cls = ""; + } else { + cls = "\\." + Pattern.quote(cls); + } + + return tested.matches(Pattern.quote(DEBUGGER_PACKAGE) + "(\\.pkg[a-f0-9]+)?" + cls); + } + + public static void replaceTraceCalls(SWF swf, String fname) { + if (hasDebugger(swf)) { + String debuggerPkg = getDebuggerScriptPack(swf).getClassPath().packageStr; + //change trace to fname + for (ABCContainerTag ct : swf.getAbcList()) { + ABC a = ct.getABC(); + for (int i = 1; i < a.constants.constant_multiname.size(); i++) { + Multiname m = a.constants.constant_multiname.get(i); + if ("trace".equals(m.getNameWithNamespace(a.constants, true))) { + m.namespace_index = a.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, a.constants.getStringId(debuggerPkg, true)), 0, true); + m.name_index = a.constants.getStringId(fname, true); + ((Tag) ct).setModified(true); + } + } + } + } + } + + public static void switchDebugger(SWF swf) { + int port = Configuration.debuggerPort.get(); + ScriptPack found = getDebuggerScriptPack(swf); + if (found != null) { + ABCContainerTag tag = found.abc.parentTag; + swf.tags.remove((Tag) tag); + swf.getAbcList().remove(tag); + + //Change all debugger calls to normal trace + for (ABCContainerTag ct : swf.getAbcList()) { + ABC a = ct.getABC(); + for (int i = 1; i < a.constants.constant_multiname.size(); i++) { + Multiname m = a.constants.constant_multiname.get(i); + String packageStr = m.getNameWithNamespace(a.constants, true); + if (isDebuggerClass(packageStr, "debugTrace") + || isDebuggerClass(packageStr, "debugAlert") + || isDebuggerClass(packageStr, "debugSocket") + || isDebuggerClass(packageStr, "debugConsole")) { + m.name_index = a.constants.getStringId("trace", true); + m.namespace_index = a.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, a.constants.getStringId("", true)), 0, true); + ((Tag) ct).setModified(true); + } + } + } + } else { + Random rnd = new Random(); + byte rb[] = new byte[16]; + rnd.nextBytes(rb); + String rhex = Helper.byteArrayToHex(rb); + try { + //load debug swf + SWF debugSWF = new SWF(Main.class.getClassLoader().getResourceAsStream("com/jpexs/decompiler/flash/gui/debugger/debug.swf"), false); + + ABCContainerTag firstAbc = swf.getAbcList().get(0); + String newdebuggerpkg = DEBUGGER_PACKAGE; + + if (Configuration.randomDebuggerPackage.get()) { + newdebuggerpkg += ".pkg" + rhex; + } + + //add debug ABC tags to main SWF + for (ABCContainerTag ds : debugSWF.getAbcList()) { + ABC a = ds.getABC(); + //Append random hex to Debugger package name + for (int i = 1; i < a.constants.constant_namespace.size(); i++) { + if (a.constants.constant_namespace.get(i).hasName(DEBUGGER_PACKAGE, a.constants)) { + a.constants.constant_namespace.get(i).name_index = a.constants.getStringId(newdebuggerpkg, true); + } + } + //Set debugger port to actually set port + for (int i = 0; i < a.constants.constant_int.size(); i++) { + if (a.constants.constant_int.get(i) == 123456L) { + a.constants.constant_int.set(i, (long) port); + } + } + //Add to target SWF + ((Tag) ds).setSwf(swf); + swf.tags.add(swf.tags.indexOf(firstAbc), (Tag) ds); + swf.getAbcList().add(swf.getAbcList().indexOf(firstAbc), ds); + ((Tag) ds).setModified(true); + } + + } catch (Exception ex) { + logger.log(Level.SEVERE, "Error while attaching debugger", ex); + //ignore + } + + } + initDebugger(); + } + + private static void initDebugger() { + if (debugger == null) { + synchronized (Main.class) { + if (debugger == null) { + Debugger dbg = new Debugger(Configuration.debuggerPort.get()); + dbg.start(); + debugger = dbg; + } + } + } + } + + public static void debuggerShowLog() { + initDebugger(); + if (Main.debugDialog == null) { + Main.debugDialog = new DebugLogDialog(debugger); + } + Main.debugDialog.setVisible(true); + } +}