/* * Copyright (C) 2010-2016 JPEXS, All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3.0 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. */ package com.jpexs.decompiler.flash.action.swf4; import com.jpexs.decompiler.flash.BaseLocalData; import com.jpexs.decompiler.flash.EndOfStreamException; import com.jpexs.decompiler.flash.SWFInputStream; import com.jpexs.decompiler.flash.SWFOutputStream; import com.jpexs.decompiler.flash.action.Action; import com.jpexs.decompiler.flash.action.ActionList; import com.jpexs.decompiler.flash.action.LocalDataArea; import com.jpexs.decompiler.flash.action.model.DirectValueActionItem; import com.jpexs.decompiler.flash.action.model.TemporaryRegister; import com.jpexs.decompiler.flash.action.parser.ActionParseException; import com.jpexs.decompiler.flash.action.parser.pcode.ASMParsedSymbol; import com.jpexs.decompiler.flash.action.parser.pcode.FlasmLexer; import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.dumpview.DumpInfo; import com.jpexs.decompiler.flash.ecma.Null; import com.jpexs.decompiler.flash.ecma.Undefined; import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; import com.jpexs.decompiler.flash.helpers.HighlightedTextWriter; import com.jpexs.decompiler.flash.types.annotations.SWFVersion; import com.jpexs.decompiler.graph.GraphSourceItem; import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.TranslateStack; import com.jpexs.decompiler.graph.model.FalseItem; import com.jpexs.decompiler.graph.model.TrueItem; import com.jpexs.helpers.Helper; import com.jpexs.helpers.utf8.Utf8Helper; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Set; /** * * @author JPEXS */ @SWFVersion(from = 4) public class ActionPush extends Action { public List<Object> values; public List<Object> replacement; public List<String> constantPool; public ActionPush(int actionLength, SWFInputStream sis, int version) throws IOException { super(0x96, actionLength); int type; values = new ArrayList<>(); DumpInfo di = sis.dumpInfo; sis = sis.getLimitedStream(actionLength); sis.dumpInfo = di; try { while (sis.available() > 0) { type = sis.readUI8("type"); switch (type) { case 0: values.add(sis.readString("string")); break; case 1: values.add(sis.readFLOAT("float")); break; case 2: values.add(Null.INSTANCE); break; case 3: values.add(Undefined.INSTANCE); break; case 4: values.add(new RegisterNumber(sis.readUI8("registerNumber"))); break; case 5: int b = sis.readUI8("boolean"); if (b == 0) { values.add((Boolean) false); } else { values.add((Boolean) true); } break; case 6: values.add(sis.readDOUBLE("double")); break; case 7: long el = sis.readSI32("integer"); values.add((Long) el); break; case 8: values.add(new ConstantIndex(sis.readUI8("constantIndex"))); break; case 9: values.add(new ConstantIndex(sis.readUI16("constantIndex"))); break; } } } catch (EndOfStreamException ex) { } } @Override protected void getContentBytes(SWFOutputStream sos) throws IOException { for (Object o : values) { if (o instanceof String) { sos.writeUI8(0); sos.writeString((String) o); } else if (o instanceof Float) { sos.writeUI8(1); sos.writeFLOAT((Float) o); } else if (o == Null.INSTANCE) { sos.writeUI8(2); } else if (o == Undefined.INSTANCE) { sos.writeUI8(3); } else if (o instanceof RegisterNumber) { sos.writeUI8(4); sos.writeUI8(((RegisterNumber) o).number); } else if (o instanceof Boolean) { sos.writeUI8(5); sos.writeUI8((Boolean) o ? 1 : 0); } else if (o instanceof Number) { if (o instanceof Long) { long l = (Long) o; if (l < -0x80000000 || l > 0x7fffffff) { o = (double) l; } } if (o instanceof Double || o instanceof Float) { sos.writeUI8(6); sos.writeDOUBLE(((Number) o).doubleValue()); } else if (o instanceof Long || o instanceof Integer || o instanceof Short || o instanceof Byte) { sos.writeUI8(7); sos.writeSI32(((Number) o).longValue()); } } else if (o instanceof ConstantIndex) { int cIndex = ((ConstantIndex) o).index; if (cIndex < 256) { sos.writeUI8(8); sos.writeUI8(cIndex); } else { sos.writeUI8(9); sos.writeUI16(cIndex); } } } } /** * Gets the length of action converted to bytes * * @return Length */ @Override protected int getContentBytesLength() { int res = 0; for (Object o : values) { if (o instanceof String) { res += Utf8Helper.getBytesLength((String) o) + 2; } else if (o instanceof Float) { res += 5; } else if (o == Null.INSTANCE) { res++; } else if (o == Undefined.INSTANCE) { res++; } else if (o instanceof RegisterNumber) { res += 2; } else if (o instanceof Boolean) { res += 2; } else if (o instanceof Number) { if (o instanceof Long) { long l = (Long) o; if (l < -0x80000000 || l > 0x7fffffff) { o = (double) l; } } if (o instanceof Double || o instanceof Float) { res += 9; } else if (o instanceof Long || o instanceof Integer || o instanceof Short || o instanceof Byte) { res += 5; } } else if (o instanceof ConstantIndex) { int cIndex = ((ConstantIndex) o).index; if (cIndex < 256) { res += 2; } else { res += 3; } } } return res; } public static boolean isValidValue(Object value) { if (value instanceof String) { for (char ch : ((String) value).toCharArray()) { if (ch == 0) { return false; } } } if (value instanceof Long) { long l = (Long) value; if (l < -0x80000000 || l > 0x7fffffff) { return false; } } return true; } public ActionPush(Object value) { super(0x96, 0); this.values = new ArrayList<>(); this.values.add(value); updateLength(); } public ActionPush(Object[] values) { super(0x96, 0); this.values = new ArrayList<>(); this.values.addAll(Arrays.asList(values)); updateLength(); } public ActionPush(FlasmLexer lexer, List<String> constantPool) throws IOException, ActionParseException { super(0x96, 0); this.constantPool = constantPool; values = new ArrayList<>(); int count = 0; loop: while (true) { ASMParsedSymbol symb = lexer.yylex(); switch (symb.type) { case ASMParsedSymbol.TYPE_STRING: count++; if (constantPool.contains((String) symb.value)) { values.add(new ConstantIndex(constantPool.indexOf(symb.value))); } else { values.add(symb.value); } break; case ASMParsedSymbol.TYPE_FLOAT: case ASMParsedSymbol.TYPE_NULL: case ASMParsedSymbol.TYPE_UNDEFINED: case ASMParsedSymbol.TYPE_REGISTER: case ASMParsedSymbol.TYPE_BOOLEAN: case ASMParsedSymbol.TYPE_INTEGER: case ASMParsedSymbol.TYPE_CONSTANT: count++; values.add(symb.value); break; case ASMParsedSymbol.TYPE_EOL: case ASMParsedSymbol.TYPE_EOF: if (count == 0) { throw new ActionParseException("Arguments expected", lexer.yyline()); } else { break loop; } case ASMParsedSymbol.TYPE_COMMENT: break; default: throw new ActionParseException("Arguments expected, " + symb.type + " " + symb.value + " found", lexer.yyline()); } } } @Override public GraphTextWriter getASMSourceReplaced(ActionList container, Set<Long> knownAddreses, ScriptExportMode exportMode, GraphTextWriter writer) { if (replacement == null || replacement.size() < values.size()) { return toString(writer); } List<Object> oldVal = values; values = replacement; toString(writer); values = oldVal; return writer; } public GraphTextWriter paramsToStringReplaced(List<? extends GraphSourceItem> container, Set<Long> knownAddreses, ScriptExportMode exportMode, GraphTextWriter writer) { if (replacement == null || replacement.size() < values.size()) { return paramsToString(writer); } List<Object> oldVal = values; values = replacement; paramsToString(writer); values = oldVal; return writer; } public String toStringNoQ(int i) { String ret; Object value = values.get(i); if (value instanceof ConstantIndex) { ret = ((ConstantIndex) value).toStringNoQ(constantPool, Configuration.resolveConstants.get()); } else if (value instanceof String) { ret = (String) value; } else if (value instanceof RegisterNumber) { ret = ((RegisterNumber) value).toStringNoName(); } else { ret = value.toString(); } return ret; } public String toString(int i) { String ret; Object value = values.get(i); if (value instanceof ConstantIndex) { ret = ((ConstantIndex) value).toString(constantPool, Configuration.resolveConstants.get()); } else if (value instanceof String) { ret = "\"" + Helper.escapeActionScriptString((String) value) + "\""; } else if (value instanceof RegisterNumber) { ret = ((RegisterNumber) value).toStringNoName(); } else { ret = value.toString(); } return ret; } public GraphTextWriter paramsToString(GraphTextWriter writer) { int pos = 0; for (int i = 0; i < values.size(); i++) { if (pos > 0) { writer.appendNoHilight(" "); } writer.append(toString(i), getAddress() + pos + 1, getFileOffset()); pos++; } return writer; } @Override public String toString() { HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), false); toString(writer); return writer.toString(); } public GraphTextWriter toString(GraphTextWriter writer) { writer.appendNoHilight("Push "); paramsToString(writer); return writer; } @Override public boolean execute(LocalDataArea lda) { for (Object value : values) { if (value instanceof ConstantIndex) { ConstantIndex constantIndex = (ConstantIndex) value; List<String> cPool = lda.constantPool != null ? lda.constantPool : constantPool; lda.stack.push(constantIndex.toStringNoQ(cPool, true)); } else if (value instanceof RegisterNumber) { int rn = ((RegisterNumber) value).number; if (lda.localRegisters.containsKey(rn)) { lda.stack.push(lda.localRegisters.get(rn)); } else { lda.stack.push(Undefined.INSTANCE); } } else { lda.stack.push(value); } } return true; } @Override public void translate(GraphSourceItem lineStartAction, TranslateStack stack, List<GraphTargetItem> output, HashMap<Integer, String> regNames, HashMap<String, GraphTargetItem> variables, HashMap<String, GraphTargetItem> functions, int staticOperation, String path) { int pos = 0; for (Object o : values) { if (o instanceof ConstantIndex) { if ((constantPool == null) || (((ConstantIndex) o).index >= constantPool.size())) { o = "\u00A7\u00A7constant" + ((ConstantIndex) o).index; } else { o = constantPool.get(((ConstantIndex) o).index); } } /*if (o instanceof RegisterNumber) { if (regNames.containsKey(((RegisterNumber) o).number)) { ((RegisterNumber) o).name = regNames.get(((RegisterNumber) o).number); } else if (output.size() >= 2) { //chained assignments:, ignore for class prototype assignment GraphTargetItem last = output.get(output.size() - 1); GraphTargetItem prev = output.get(output.size() - 2); if (last instanceof SetTypeActionItem) { if (prev instanceof StoreRegisterActionItem) { StoreRegisterActionItem str = (StoreRegisterActionItem) prev; if (str.register.number == ((RegisterNumber) o).number) { SetTypeActionItem stt = (SetTypeActionItem) last; stt.setTempRegister(((RegisterNumber) o).number); if ((stt.getValue() instanceof IncrementActionItem) && (((IncrementActionItem) stt.getValue()).object.equals(stt.getObject()))) { stack.push(new PreIncrementActionItem(this, lineStartAction, stt.getObject())); } else if ((stt.getValue() instanceof DecrementActionItem) && (((DecrementActionItem) stt.getValue()).object.equals(stt.getObject()))) { stack.push(new PreDecrementActionItem(this, lineStartAction, stt.getObject())); } else { //stack.push(last); continue; } output.remove(output.size() - 1); output.remove(output.size() - 1); pos++; continue; } } } } }*/ if (o instanceof Boolean) { Boolean b = (Boolean) o; if (b) { stack.push(new TrueItem(this, lineStartAction)); } else { stack.push(new FalseItem(this, lineStartAction)); } } else { DirectValueActionItem dvt = new DirectValueActionItem(this, lineStartAction, pos, o, constantPool); if (o instanceof RegisterNumber) {//TemporaryRegister dvt.computedRegValue = variables.get("__register" + ((RegisterNumber) o).number); if (regNames.containsKey(((RegisterNumber) o).number)) { ((RegisterNumber) o).name = regNames.get(((RegisterNumber) o).number); } } if (dvt.computedRegValue instanceof TemporaryRegister) { stack.push(new TemporaryRegister(((RegisterNumber) o).number, ((TemporaryRegister) dvt.computedRegValue).value)); } else { stack.push(dvt); } } pos++; } } @Override public int getStackPushCount(BaseLocalData localData, TranslateStack stack) { return values.size(); } }