/*
* 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.types.traits;
import com.jpexs.decompiler.flash.abc.ABC;
import com.jpexs.decompiler.flash.abc.avm2.parser.script.AbcIndexing;
import com.jpexs.decompiler.flash.abc.types.ClassInfo;
import com.jpexs.decompiler.flash.abc.types.ConvertData;
import com.jpexs.decompiler.flash.abc.types.InstanceInfo;
import com.jpexs.decompiler.flash.abc.types.MethodBody;
import com.jpexs.decompiler.flash.abc.types.Multiname;
import com.jpexs.decompiler.flash.abc.types.Namespace;
import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode;
import com.jpexs.decompiler.flash.exporters.script.Dependency;
import com.jpexs.decompiler.flash.exporters.script.DependencyParser;
import com.jpexs.decompiler.flash.exporters.script.DependencyType;
import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
import com.jpexs.decompiler.flash.helpers.NulWriter;
import com.jpexs.decompiler.flash.helpers.hilight.HighlightSpecialType;
import com.jpexs.decompiler.graph.DottedChain;
import com.jpexs.decompiler.graph.ScopeStack;
import com.jpexs.decompiler.graph.TypeItem;
import com.jpexs.helpers.Helper;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author JPEXS
*/
public class TraitClass extends Trait implements TraitWithSlot {
public int slot_id;
public int class_info;
private boolean classInitializerIsEmpty;
@Override
public void delete(ABC abc, boolean d) {
abc.deleteClass(class_info, d);
abc.constants.getMultiname(name_index).deleted = d;
}
@Override
public int getSlotIndex() {
return slot_id;
}
@Override
public String toString(ABC abc, List<DottedChain> fullyQualifiedNames) {
return "Class " + abc.constants.getMultiname(name_index).toString(abc.constants, fullyQualifiedNames) + " slot=" + slot_id + " class_info=" + class_info + " metadata=" + Helper.intArrToString(metadata);
}
@Override
public void getDependencies(String customNs, ABC abc, List<Dependency> dependencies, List<String> uses, DottedChain ignorePackage, List<DottedChain> fullyQualifiedNames) {
super.getDependencies(customNs, abc, dependencies, uses, ignorePackage == null ? getPackage(abc) : ignorePackage, fullyQualifiedNames);
ClassInfo classInfo = abc.class_info.get(class_info);
InstanceInfo instanceInfo = abc.instance_info.get(class_info);
DottedChain packageName = instanceInfo.getName(abc.constants).getNamespace(abc.constants).getName(abc.constants); //assume not null name
//DependencyParser.parseDependenciesFromMultiname(customNs, abc, dependencies, uses, abc.constants.getMultiname(instanceInfo.name_index), packageName, fullyQualifiedNames);
if (instanceInfo.super_index > 0) {
DependencyParser.parseDependenciesFromMultiname(customNs, abc, dependencies, uses, abc.constants.getMultiname(instanceInfo.super_index), packageName, fullyQualifiedNames, DependencyType.INHERITANCE);
}
for (int i : instanceInfo.interfaces) {
DependencyParser.parseDependenciesFromMultiname(customNs, abc, dependencies, uses, abc.constants.getMultiname(i), packageName, fullyQualifiedNames, DependencyType.INHERITANCE);
}
//static
classInfo.static_traits.getDependencies(customNs, abc, dependencies, uses, packageName, fullyQualifiedNames);
//static initializer
DependencyParser.parseDependenciesFromMethodInfo(customNs, abc, classInfo.cinit_index, dependencies, uses, packageName, fullyQualifiedNames, new ArrayList<>());
//instance
instanceInfo.instance_traits.getDependencies(customNs, abc, dependencies, uses, packageName, fullyQualifiedNames);
//instance initializer
DependencyParser.parseDependenciesFromMethodInfo(customNs, abc, instanceInfo.iinit_index, dependencies, uses, packageName, fullyQualifiedNames, new ArrayList<>());
}
@Override
public GraphTextWriter toStringHeader(Trait parent, ConvertData convertData, String path, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, GraphTextWriter writer, List<DottedChain> fullyQualifiedNames, boolean parallel) {
abc.instance_info.get(class_info).getClassHeaderStr(writer, abc, fullyQualifiedNames, false);
return writer;
}
@Override
public void convertHeader(Trait parent, ConvertData convertData, String path, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, NulWriter writer, List<DottedChain> fullyQualifiedNames, boolean parallel) {
}
@Override
public GraphTextWriter toString(Trait parent, ConvertData convertData, String path, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, GraphTextWriter writer, List<DottedChain> fullyQualifiedNames, boolean parallel) throws InterruptedException {
InstanceInfo instanceInfo = abc.instance_info.get(class_info);
Multiname instanceInfoMultiname = instanceInfo.getName(abc.constants);
DottedChain packageName = instanceInfoMultiname.getNamespace(abc.constants).getName(abc.constants); //assume not null name
fullyQualifiedNames = new ArrayList<>();
writeImportsUsages(abc, writer, packageName, fullyQualifiedNames);
String instanceInfoName = instanceInfoMultiname.getName(abc.constants, fullyQualifiedNames, false, true);
writer.startClass(class_info);
getMetaData(parent, convertData, abc, writer);
//class header
instanceInfo.getClassHeaderStr(writer, abc, fullyQualifiedNames, false);
writer.startBlock();
//static variables & constants
ClassInfo classInfo = abc.class_info.get(class_info);
classInfo.static_traits.toString(new Class[]{TraitSlotConst.class}, this, convertData, path +/*packageName +*/ "/" + instanceInfoName, abc, true, exportMode, false, scriptIndex, class_info, writer, fullyQualifiedNames, parallel);
//static initializer
int bodyIndex = abc.findBodyIndex(classInfo.cinit_index);
if (bodyIndex != -1) {
writer.startTrait(GraphTextWriter.TRAIT_CLASS_INITIALIZER);
writer.startMethod(classInfo.cinit_index);
if (exportMode != ScriptExportMode.AS_METHOD_STUBS) {
if (!classInitializerIsEmpty) {
writer.startBlock();
abc.bodies.get(bodyIndex).toString(path +/*packageName +*/ "/" + instanceInfoName + ".staticinitializer", exportMode, abc, this, writer, fullyQualifiedNames);
writer.endBlock();
} else {
//Note: There must be trait/method highlight even if the initializer is empty to TraitList in GUI to work correctly
//TODO: handle this better in GUI(?)
writer.append(" ").newLine();
}
}
writer.endMethod();
writer.endTrait();
if (!classInitializerIsEmpty) {
writer.newLine();
}
} else {
//"/*classInitializer*/";
}
//instance variables
instanceInfo.instance_traits.toString(new Class[]{TraitSlotConst.class}, this, convertData, path +/*packageName +*/ "/" + instanceInfoName, abc, false, exportMode, false, scriptIndex, class_info, writer, fullyQualifiedNames, parallel);
//instance initializer - constructor
if (!instanceInfo.isInterface()) {
String modifier = "";
Multiname m = abc.constants.getMultiname(instanceInfo.name_index);
Namespace ns = m.getNamespace(abc.constants);
if (ns != null) {
modifier = ns.getPrefix(abc) + " ";
if (modifier.equals(" ")) {
modifier = "";
}
if (modifier.startsWith("private")) { //cannot have private constuctor
modifier = "";
}
}
writer.newLine();
writer.startTrait(GraphTextWriter.TRAIT_INSTANCE_INITIALIZER);
writer.startMethod(instanceInfo.iinit_index);
writer.appendNoHilight(modifier);
writer.appendNoHilight("function ");
writer.appendNoHilight(m.getName(abc.constants, null/*do not want full names here*/, false, true));
writer.appendNoHilight("(");
bodyIndex = abc.findBodyIndex(instanceInfo.iinit_index);
MethodBody body = bodyIndex == -1 ? null : abc.bodies.get(bodyIndex);
abc.method_info.get(instanceInfo.iinit_index).getParamStr(writer, abc.constants, body, abc, fullyQualifiedNames);
writer.appendNoHilight(")").startBlock();
if (exportMode != ScriptExportMode.AS_METHOD_STUBS) {
if (body != null) {
body.toString(path +/*packageName +*/ "/" + instanceInfoName + ".initializer", exportMode, abc, this, writer, fullyQualifiedNames);
}
}
writer.endBlock().newLine();
writer.endMethod();
writer.endTrait();
}
//static methods
classInfo.static_traits.toString(new Class[]{TraitClass.class, TraitFunction.class, TraitMethodGetterSetter.class}, this, convertData, path +/*packageName +*/ "/" + instanceInfoName, abc, true, exportMode, false, scriptIndex, class_info, writer, fullyQualifiedNames, parallel);
//instance methods
instanceInfo.instance_traits.toString(new Class[]{TraitClass.class, TraitFunction.class, TraitMethodGetterSetter.class}, this, convertData, path +/*packageName +*/ "/" + instanceInfoName, abc, false, exportMode, false, scriptIndex, class_info, writer, fullyQualifiedNames, parallel);
writer.endBlock(); // class
writer.endClass();
writer.newLine();
return writer;
}
@Override
public void convert(Trait parent, ConvertData convertData, String path, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, NulWriter writer, List<DottedChain> fullyQualifiedNames, boolean parallel) throws InterruptedException {
fullyQualifiedNames = new ArrayList<>();
InstanceInfo instanceInfo = abc.instance_info.get(class_info);
String instanceInfoName = instanceInfo.getName(abc.constants).getName(abc.constants, fullyQualifiedNames, false, true);
ClassInfo classInfo = abc.class_info.get(class_info);
AbcIndexing index = new AbcIndexing(abc.getSwf());
//for simplification of String(this)
convertData.thisHasDefaultToPrimitive = null == index.findProperty(new AbcIndexing.PropertyDef("toString", new TypeItem(instanceInfo.getName(abc.constants).getNameWithNamespace(abc.constants, true)), abc, abc.constants.getNamespaceId(Namespace.KIND_PACKAGE, DottedChain.TOPLEVEL, abc.constants.getStringId("", true), true)), false, true);
//class initializer
int bodyIndex = abc.findBodyIndex(classInfo.cinit_index);
if (bodyIndex != -1) {
writer.mark();
List<Traits> ts = new ArrayList<>();
ts.add(classInfo.static_traits);
abc.bodies.get(bodyIndex).convert(convertData, path +/*packageName +*/ "/" + instanceInfoName + ".staticinitializer", exportMode, true, classInfo.cinit_index, scriptIndex, class_info, abc, this, new ScopeStack(), GraphTextWriter.TRAIT_CLASS_INITIALIZER, writer, fullyQualifiedNames, ts, true);
classInitializerIsEmpty = !writer.getMark();
}
//constructor - instance initializer
if (!instanceInfo.isInterface()) {
bodyIndex = abc.findBodyIndex(instanceInfo.iinit_index);
if (bodyIndex != -1) {
List<Traits> ts = new ArrayList<>();
ts.add(instanceInfo.instance_traits);
abc.bodies.get(bodyIndex).convert(convertData, path +/*packageName +*/ "/" + instanceInfoName + ".initializer", exportMode, false, instanceInfo.iinit_index, scriptIndex, class_info, abc, this, new ScopeStack(), GraphTextWriter.TRAIT_INSTANCE_INITIALIZER, writer, fullyQualifiedNames, ts, true);
}
}
//static variables,constants & methods
classInfo.static_traits.convert(this, convertData, path +/*packageName +*/ "/" + instanceInfoName, abc, true, exportMode, false, scriptIndex, class_info, writer, fullyQualifiedNames, parallel);
instanceInfo.instance_traits.convert(this, convertData, path +/*packageName +*/ "/" + instanceInfoName, abc, false, exportMode, false, scriptIndex, class_info, writer, fullyQualifiedNames, parallel);
}
@Override
public int removeTraps(int scriptIndex, int classIndex, boolean isStatic, ABC abc, String path) throws InterruptedException {
ClassInfo classInfo = abc.class_info.get(class_info);
InstanceInfo instanceInfo = abc.instance_info.get(class_info);
int iInitializer = abc.findBodyIndex(instanceInfo.iinit_index);
int ret = 0;
if (iInitializer != -1) {
ret += abc.bodies.get(iInitializer).removeTraps(abc, this, scriptIndex, class_info, false, path);
}
int sInitializer = abc.findBodyIndex(classInfo.cinit_index);
if (sInitializer != -1) {
ret += abc.bodies.get(sInitializer).removeTraps(abc, this, scriptIndex, class_info, true, path);
}
ret += instanceInfo.instance_traits.removeTraps(scriptIndex, class_info, false, abc, path);
ret += classInfo.static_traits.removeTraps(scriptIndex, class_info, true, abc, path);
return ret;
}
@Override
public TraitClass clone() {
TraitClass ret = (TraitClass) super.clone();
return ret;
}
@Override
public GraphTextWriter convertTraitHeader(ABC abc, GraphTextWriter writer) {
convertCommonHeaderFlags("class", abc, writer);
writer.appendNoHilight(" slotid ");
writer.hilightSpecial(Integer.toString(slot_id), HighlightSpecialType.SLOT_ID);
writer.newLine();
return writer;
}
}