/* * Copyright (C) 2010-2016 JPEXS, All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3.0 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. */ package com.jpexs.decompiler.flash.abc.avm2.instructions; import com.jpexs.decompiler.flash.abc.ABC; import com.jpexs.decompiler.flash.abc.AVM2LocalData; import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; import com.jpexs.decompiler.flash.abc.avm2.AVM2ConstantPool; import com.jpexs.decompiler.flash.abc.avm2.LocalDataArea; import com.jpexs.decompiler.flash.abc.avm2.exceptions.AVM2ExecutionException; import com.jpexs.decompiler.flash.abc.avm2.exceptions.AVM2VerifyErrorException; import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.DecLocalIIns; import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.DecLocalIns; import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.IncLocalIIns; import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.IncLocalIns; import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.SetLocalTypeIns; import com.jpexs.decompiler.flash.abc.avm2.model.AVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.FullMultinameAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.parser.script.Reference; import com.jpexs.decompiler.flash.abc.types.MethodBody; import com.jpexs.decompiler.flash.abc.types.Multiname; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; import com.jpexs.decompiler.graph.DottedChain; import com.jpexs.decompiler.graph.GraphSourceItem; import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.ScopeStack; import com.jpexs.decompiler.graph.TranslateStack; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.Stack; /** * * @author JPEXS */ public abstract class InstructionDefinition implements Serializable { public static final long serialVersionUID = 1L; public int[] operands; public String instructionName = ""; public int instructionCode = 0; public boolean canThrow; public AVM2InstructionFlag[] flags; public InstructionDefinition(int instructionCode, String instructionName, int[] operands, boolean canThrow, AVM2InstructionFlag... flags) { this.instructionCode = instructionCode; this.instructionName = instructionName; this.operands = operands; this.canThrow = canThrow; this.flags = flags; } public boolean hasFlag(AVM2InstructionFlag flag) { for (AVM2InstructionFlag f : flags) { if (f == flag) { return true; } } return false; } @Override public String toString() { StringBuilder s = new StringBuilder(); s.append(instructionName); for (int i = 0; i < operands.length; i++) { s.append(AVM2Code.operandTypeToString(operands[i], true)); } return s.toString(); } public void verify(LocalDataArea lda, AVM2ConstantPool constants, AVM2Instruction ins) throws AVM2VerifyErrorException { for (int i = 0; i < operands.length; i++) { int operand = operands[i]; if (operand == AVM2Code.DAT_MULTINAME_INDEX) { int idx = ins.operands[i]; if (idx <= 0 || idx >= constants.getMultinameCount()) { throw new AVM2VerifyErrorException(AVM2VerifyErrorException.CPOOL_INDEX_OUT_OF_RANGE, lda.isDebug(), new Object[]{idx, constants.getMultinameCount()}); } } else if (operand == AVM2Code.DAT_DOUBLE_INDEX) { int idx = ins.operands[i]; if (idx <= 0 || idx >= constants.getDoubleCount()) { throw new AVM2VerifyErrorException(AVM2VerifyErrorException.CPOOL_INDEX_OUT_OF_RANGE, lda.isDebug(), new Object[]{idx, constants.getDoubleCount()}); } } else if (operand == AVM2Code.DAT_INT_INDEX) { int idx = ins.operands[i]; if (idx <= 0 || idx >= constants.getIntCount()) { throw new AVM2VerifyErrorException(AVM2VerifyErrorException.CPOOL_INDEX_OUT_OF_RANGE, lda.isDebug(), new Object[]{idx, constants.getIntCount()}); } } else if (operand == AVM2Code.DAT_UINT_INDEX) { int idx = ins.operands[i]; if (idx <= 0 || idx >= constants.getUIntCount()) { throw new AVM2VerifyErrorException(AVM2VerifyErrorException.CPOOL_INDEX_OUT_OF_RANGE, lda.isDebug(), new Object[]{idx, constants.getUIntCount()}); } } else if (operand == AVM2Code.DAT_STRING_INDEX) { int idx = ins.operands[i]; if (idx <= 0 || idx >= constants.getStringCount()) { throw new AVM2VerifyErrorException(AVM2VerifyErrorException.CPOOL_INDEX_OUT_OF_RANGE, lda.isDebug(), new Object[]{idx, constants.getStringCount()}); } } else if (operand == AVM2Code.DAT_NAMESPACE_INDEX) { int idx = ins.operands[i]; if (idx <= 0 || idx >= constants.getNamespaceCount()) { throw new AVM2VerifyErrorException(AVM2VerifyErrorException.CPOOL_INDEX_OUT_OF_RANGE, lda.isDebug(), new Object[]{idx, constants.getStringCount()}); } } else if (operand == AVM2Code.DAT_FLOAT_INDEX) { int idx = ins.operands[i]; if (idx <= 0 || idx >= constants.getFloatCount()) { throw new AVM2VerifyErrorException(AVM2VerifyErrorException.CPOOL_INDEX_OUT_OF_RANGE, lda.isDebug(), new Object[]{idx, constants.getStringCount()}); } } else if (operand == AVM2Code.DAT_FLOAT4_INDEX) { int idx = ins.operands[i]; if (idx <= 0 || idx >= constants.getFloat4Count()) { throw new AVM2VerifyErrorException(AVM2VerifyErrorException.CPOOL_INDEX_OUT_OF_RANGE, lda.isDebug(), new Object[]{idx, constants.getStringCount()}); } } else if (operand == AVM2Code.DAT_DECIMAL_INDEX) { int idx = ins.operands[i]; if (idx <= 0 || idx >= constants.getDecimalCount()) { throw new AVM2VerifyErrorException(AVM2VerifyErrorException.CPOOL_INDEX_OUT_OF_RANGE, lda.isDebug(), new Object[]{idx, constants.getStringCount()}); } } } } public boolean isNotCompileTimeSupported() { return false; } public boolean execute(LocalDataArea lda, AVM2ConstantPool constants, AVM2Instruction ins) throws AVM2ExecutionException { //throw new UnsupportedOperationException("Instruction " + instructionName + " not implemented"); return false; } protected void illegalOpCode(LocalDataArea lda, AVM2Instruction ins) throws AVM2VerifyErrorException { throw new AVM2VerifyErrorException(AVM2VerifyErrorException.ILLEGAL_OPCODE, lda.isDebug(), new Object[]{lda.methodName, instructionCode, ins.getAddress()}); } public void translate(AVM2LocalData localData, TranslateStack stack, AVM2Instruction ins, List<GraphTargetItem> output, String path) throws InterruptedException { } public void translate(Reference<GraphSourceItem> lineStartItem, boolean isStatic, int scriptIndex, int classIndex, HashMap<Integer, GraphTargetItem> localRegs, TranslateStack stack, ScopeStack scopeStack, AVM2Instruction ins, List<GraphTargetItem> output, MethodBody body, ABC abc, HashMap<Integer, String> localRegNames, List<DottedChain> fullyQualifiedNames, String path, HashMap<Integer, Integer> localRegsAssignmentIps, int ip, HashMap<Integer, List<Integer>> refs, AVM2Code code, boolean thisHasDefaultToPrimitive) throws InterruptedException { AVM2LocalData localData = new AVM2LocalData(); localData.isStatic = isStatic; localData.scriptIndex = scriptIndex; localData.classIndex = classIndex; localData.lineStartInstruction = lineStartItem.getVal(); localData.localRegs = localRegs; localData.scopeStack = scopeStack; localData.methodBody = body; localData.abc = abc; localData.localRegNames = localRegNames; localData.fullyQualifiedNames = fullyQualifiedNames; localData.localRegAssignmentIps = localRegsAssignmentIps; localData.ip = ip; localData.refs = refs; localData.code = code; localData.thisHasDefaultToPrimitive = thisHasDefaultToPrimitive; translate(localData, stack, ins, output, path); lineStartItem.setVal(localData.lineStartInstruction); } public int getStackPopCount(AVM2Instruction ins, ABC abc) { return 0; } public int getStackPushCount(AVM2Instruction ins, ABC abc) { return 0; } protected void resolveMultiname(LocalDataArea localData, AVM2ConstantPool constants, int multinameIndex) { if (multinameIndex > 0 && multinameIndex < constants.getMultinameCount()) { Multiname multiname = constants.getMultiname(multinameIndex); if (multiname.needsName()) { Object name = localData.operandStack.pop(); } if (multiname.needsNs()) { Object ns = localData.operandStack.pop(); } } } protected FullMultinameAVM2Item resolveMultiname(AVM2LocalData localData, boolean property, TranslateStack stack, AVM2ConstantPool constants, int multinameIndex, AVM2Instruction ins) { GraphTargetItem ns = null; GraphTargetItem name = null; if (multinameIndex > 0 && multinameIndex < constants.getMultinameCount()) { Multiname multiname = constants.getMultiname(multinameIndex); if (multiname.needsName()) { name = stack.pop(); } if (multiname.needsNs()) { ns = stack.pop(); } } return new FullMultinameAVM2Item(property, ins, localData.lineStartInstruction, multinameIndex, localData.abc.constants.getMultiname(multinameIndex).getName(localData.getConstants(), new ArrayList<>(), true, true), name, ns); } protected int getMultinameRequiredStackSize(AVM2ConstantPool constants, int multinameIndex) { int res = 0; if (multinameIndex > 0 && multinameIndex < constants.getMultinameCount()) { //Note: In official compiler, the stack can be wrong(greater) for some MULTINAMEL/A, e.g. increments /* var arr=[1,2,3]; return arr[2]++; */ if (constants.getMultiname(multinameIndex).needsName()) { res++; } if (constants.getMultiname(multinameIndex).needsNs()) { res++; } } return res; } protected int resolvedCount(AVM2ConstantPool constants, int multinameIndex) { int pos = 0; if (constants.getMultiname(multinameIndex).needsNs()) { pos++; } if (constants.getMultiname(multinameIndex).needsName()) { pos++; } return pos; } protected String resolveMultinameNoPop(int pos, Stack<AVM2Item> stack, AVM2ConstantPool constants, int multinameIndex, AVM2Instruction ins, List<DottedChain> fullyQualifiedNames) { String ns = ""; String name; if (constants.getMultiname(multinameIndex).needsNs()) { ns = "[" + stack.get(pos) + "]"; pos++; } if (constants.getMultiname(multinameIndex).needsName()) { name = stack.get(pos).toString(); } else { name = GraphTextWriter.hilighOffset(constants.getMultiname(multinameIndex).getName(constants, fullyQualifiedNames, false, true), ins.getAddress()); } return name + ns; } public int getStackDelta(AVM2Instruction ins, ABC abc) { return getStackPushCount(ins, abc) - getStackPopCount(ins, abc); } public int getScopeStackDelta(AVM2Instruction ins, ABC abc) { return 0; } protected boolean isRegisterCompileTime(int regId, int ip, HashMap<Integer, List<Integer>> refs, AVM2Code code) { Set<Integer> previous = new HashSet<>(); AVM2Code.getPreviousReachableIps(ip, refs, previous, new HashSet<>()); for (int p : previous) { if (p < 0) { continue; } if (p >= code.code.size()) { continue; } AVM2Instruction sins = code.code.get(p); if (code.code.get(p).definition instanceof SetLocalTypeIns) { SetLocalTypeIns sl = (SetLocalTypeIns) sins.definition; if (sl.getRegisterId(sins) == regId) { if (!AVM2Code.isDirectAncestor(ip, p, refs)) { return false; } } } if ((code.code.get(p).definition instanceof IncLocalIns) || (code.code.get(p).definition instanceof IncLocalIIns) || (code.code.get(p).definition instanceof DecLocalIns) || (code.code.get(p).definition instanceof DecLocalIIns)) { if (sins.operands[0] == regId) { if (!AVM2Code.isDirectAncestor(ip, p, refs)) { return false; } } } } return true; } public boolean isExitInstruction() { return false; } }