/*
* 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.parser.script;
import com.jpexs.decompiler.flash.SourceGeneratorLocalData;
import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction;
import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instructions;
import com.jpexs.decompiler.flash.abc.avm2.model.InitVectorAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.IntegerValueAVM2Item;
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.UndefinedAVM2Item;
import com.jpexs.decompiler.flash.abc.types.MethodBody;
import com.jpexs.decompiler.flash.abc.types.Namespace;
import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
import com.jpexs.decompiler.graph.CompilationException;
import com.jpexs.decompiler.graph.DottedChain;
import com.jpexs.decompiler.graph.GraphSourceItem;
import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.decompiler.graph.SourceGenerator;
import com.jpexs.decompiler.graph.TypeItem;
import com.jpexs.decompiler.graph.model.LocalData;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author JPEXS
*/
public class UnresolvedAVM2Item extends AssignableAVM2Item {
private DottedChain name;
private int nsKind = -1;
public List<NamespaceItem> openedNamespaces;
public int line;
public GraphTargetItem type;
//private GraphTargetItem ns = null;
public GraphTargetItem resolved;
public GraphTargetItem resolvedRoot;
private final boolean mustBeType;
public List<DottedChain> importedClasses;
public List<GraphTargetItem> scopeStack = new ArrayList<>();
public List<GraphTargetItem> subtypes;
@Override
public AssignableAVM2Item copy() {
UnresolvedAVM2Item c = new UnresolvedAVM2Item(subtypes, importedClasses, mustBeType, type, line, name, assignedValue, openedNamespaces);
//c.setNs(ns);
c.nsKind = nsKind;
c.resolved = resolved;
return c;
}
public void setSlotScope(int slotScope) {
if (resolved instanceof NameAVM2Item) {
((NameAVM2Item) resolved).setSlotScope(slotScope);
}
}
public int getSlotScope() {
if (resolved instanceof NameAVM2Item) {
return ((NameAVM2Item) resolved).getSlotScope();
}
return -1;
}
/*public void setNs(GraphTargetItem ns) {
this.ns = ns;
}*/
public void setRegNumber(int regNumber) {
if (resolved instanceof NameAVM2Item) {
((NameAVM2Item) resolved).setRegNumber(regNumber);
}
}
public int getSlotNumber() {
if (resolved instanceof NameAVM2Item) {
return ((NameAVM2Item) resolved).getSlotNumber();
}
return -1;
}
public void setSlotNumber(int slotNumber) {
if (resolved instanceof NameAVM2Item) {
((NameAVM2Item) resolved).setSlotNumber(slotNumber);
}
}
public int getRegNumber() {
if (resolved instanceof NameAVM2Item) {
return ((NameAVM2Item) resolved).getRegNumber();
}
return -1;
}
/*
public GraphTargetItem getNs() {
return ns;
}
*/
public void appendName(String name) {
this.name = this.name.addWithSuffix(name);
}
public void setDefinition(boolean definition) {
if (resolved instanceof NameAVM2Item) {
((NameAVM2Item) resolved).setDefinition(definition);
}
}
public void setNsKind(int nsKind) {
this.nsKind = nsKind;
}
public int getNsKind() {
return nsKind;
}
@Override
public void setAssignedValue(GraphTargetItem storeValue) {
this.assignedValue = storeValue;
}
public DottedChain getVariableName() {
return name;
}
public void setVariableName(DottedChain name) {
this.name = name;
}
public UnresolvedAVM2Item(List<GraphTargetItem> subtypes, List<DottedChain> importedClasses, boolean mustBeType, GraphTargetItem type, int line, DottedChain name, GraphTargetItem storeValue, List<NamespaceItem> openedNamespaces) {
super(storeValue);
this.name = name;
this.assignedValue = storeValue;
this.line = line;
this.type = type;
this.openedNamespaces = openedNamespaces;
this.mustBeType = mustBeType;
this.importedClasses = importedClasses;
this.subtypes = subtypes;
}
public boolean isDefinition() {
if (resolved instanceof NameAVM2Item) {
return ((NameAVM2Item) resolved).isDefinition();
}
return false;
}
public GraphTargetItem getStoreValue() {
return assignedValue;
}
@Override
public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException {
return writer;
}
public static GraphTargetItem getDefaultValue(String type) {
switch (type) {
case "*":
return new UndefinedAVM2Item(null, null);
case "int":
return new IntegerValueAVM2Item(null, null, 0L);
case "Number":
return new NanAVM2Item(null, null);
default:
return new NullAVM2Item(null, null);
}
}
public static AVM2Instruction generateCoerce(SourceGeneratorLocalData localData, SourceGenerator generator, GraphTargetItem type) throws CompilationException {
AVM2Instruction ins;
switch (type.toString()) {
case "int":
ins = ins(AVM2Instructions.ConvertI);
break;
case "*":
ins = ins(AVM2Instructions.CoerceA);
break;
case "String":
ins = ins(AVM2Instructions.CoerceS);
break;
default:
int type_index = AVM2SourceGenerator.resolveType(localData, type, ((AVM2SourceGenerator) generator).abcIndex);
ins = ins(AVM2Instructions.Coerce, type_index);
break;
}
return ins;
}
@Override
public List<GraphSourceItem> toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException {
if (resolved == null) {
throw new RuntimeException("Unresolved");
}
return resolved.toSource(localData, generator);
}
@Override
public List<GraphSourceItem> toSourceIgnoreReturnValue(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException {
if (resolved == null) {
throw new RuntimeException("Unresolved");
}
return resolved.toSourceIgnoreReturnValue(localData, generator);
}
@Override
public boolean hasReturnValue() {
if (resolved != null) {
return resolved.hasReturnValue();
}
return true;
}
@Override
public boolean needsSemicolon() {
if (resolved != null) {
return resolved.needsSemicolon();
}
return false;
}
@Override
public String toString() {
if (resolved != null) {
return resolved.toString();
}
return name.toRawString();
}
@Override
public GraphTargetItem returnType() {
if (resolved != null) {
return resolved.returnType();
}
if (type == null) {
return TypeItem.UNBOUNDED;
}
return type;
}
@Override
public List<GraphSourceItem> toSourceChange(SourceGeneratorLocalData localData, SourceGenerator generator, boolean post, boolean decrement, boolean needsReturn) throws CompilationException {
if (resolved == null) {
throw new RuntimeException("Unresolved");
}
if (resolved instanceof AssignableAVM2Item) {
return ((AssignableAVM2Item) resolved).toSourceChange(localData, generator, post, decrement, needsReturn);
}
throw new RuntimeException("Cannot assign");
}
public GraphTargetItem resolve(GraphTargetItem thisType, List<GraphTargetItem> paramTypes, List<String> paramNames, AbcIndexing abc, List<MethodBody> callStack, List<AssignableAVM2Item> variables) throws CompilationException {
if (scopeStack.isEmpty()) { //Everything is multiname property in with command
//search for variable
for (AssignableAVM2Item a : variables) {
if (a instanceof NameAVM2Item) {
NameAVM2Item n = (NameAVM2Item) a;
if (n.isDefinition() && name.get(0).equals(n.getVariableName())) {
NameAVM2Item ret = new NameAVM2Item(n.type, n.line, name.get(0), null, false, openedNamespaces);
ret.setSlotScope(n.getSlotScope());
ret.setSlotNumber(n.getSlotNumber());
ret.setRegNumber(n.getRegNumber());
resolved = ret;
for (int i = 1; i < name.size(); i++) {
resolved = new PropertyAVM2Item(resolved, name.get(i), abc, openedNamespaces, new ArrayList<>());
if (i == name.size() - 1) {
((PropertyAVM2Item) resolved).assignedValue = assignedValue;
}
}
if (name.size() == 1) {
ret.setAssignedValue(assignedValue);
}
ret.setNs(n.getNs());
return resolvedRoot = ret;
}
}
}
}
//Search for types in imported classes
for (DottedChain imp : importedClasses) {
String impName = imp.getLast();
if (impName.equals(name.get(0))) {
TypeItem ret = new TypeItem(imp);
resolved = ret;
for (int i = 1; i < name.size(); i++) {
resolved = new PropertyAVM2Item(resolved, name.get(i), abc, openedNamespaces, new ArrayList<>());
if (i == name.size() - 1) {
((PropertyAVM2Item) resolved).assignedValue = assignedValue;
}
}
if (name.size() == 1 && assignedValue != null) {
throw new CompilationException("Cannot assign type", line);
}
return resolvedRoot = ret;
}
}
//Search all fully qualitfied types
/*List<ABC> allAbcs = new ArrayList<>();
allAbcs.add(abc);
allAbcs.addAll(otherAbcs);*/
for (int i = 0; i < name.size(); i++) {
DottedChain fname = name.subChain(i + 1);
AbcIndexing.ClassIndex ci = abc.findClass(new TypeItem(fname));
if (ci != null) {
if (!subtypes.isEmpty() && name.size() > i + 1) {
continue;
}
TypeItem ret = new TypeItem(fname);
resolved = ret;
for (int j = i + 1; j < name.size(); j++) {
resolved = new PropertyAVM2Item(resolved, name.get(j), abc, openedNamespaces, new ArrayList<>());
if (j == name.size() - 1) {
((PropertyAVM2Item) resolved).assignedValue = assignedValue;
}
}
if (name.size() == i + 1 && assignedValue != null) {
throw new CompilationException("Cannot assign type", line);
}
return resolvedRoot = ret;
}
}
//Search for types in opened namespaces
for (NamespaceItem n : openedNamespaces) {
Namespace ons = abc.getSelectedAbc().constants.getNamespace(n.getCpoolIndex(abc));
TypeItem ti = new TypeItem(ons.getName(abc.getSelectedAbc().constants).addWithSuffix(name.get(0)));
AbcIndexing.ClassIndex ci = abc.findClass(ti);
if (ci != null) {
if (!subtypes.isEmpty() && name.size() > 1) {
continue;
}
TypeItem ret = ti;
resolved = ret;
for (int i = 1; i < name.size(); i++) {
resolved = new PropertyAVM2Item(resolved, name.get(i), abc, openedNamespaces, new ArrayList<>());
if (i == name.size() - 1) {
((PropertyAVM2Item) resolved).assignedValue = assignedValue;
}
}
if (name.size() == 1 && assignedValue != null) {
throw new CompilationException("Cannot assign type", line);
}
return resolvedRoot = ret;
}
}
if (name.get(
0).equals("this") || name.get(0).equals("super")) {
if (thisType == null) {
throw new CompilationException("Cannot use this in that context", line);
}
boolean isSuper = name.get(0).equals("super");
GraphTargetItem ntype = thisType;
if (isSuper) {
AbcIndexing.ClassIndex ci = abc.findClass(thisType);
if (ci == null) {
throw new CompilationException("This class not found", line);
}
ci = ci.parent;
if (ci == null) {
ntype = new TypeItem("Object");
} else {
ntype = new TypeItem(ci.abc.instance_info.get(ci.index).getName(ci.abc.constants).getNameWithNamespace(ci.abc.constants, true));
}
}
NameAVM2Item ret = new NameAVM2Item(ntype, line, name.get(0), null, false, openedNamespaces);
resolved = ret;
for (int i = 1; i < name.size(); i++) {
resolved = new PropertyAVM2Item(resolved, name.get(i), abc, openedNamespaces, new ArrayList<>());
if (i == name.size() - 1) {
((PropertyAVM2Item) resolved).assignedValue = assignedValue;
}
}
if (name.size() == 1) {
ret.setAssignedValue(assignedValue);
}
return resolvedRoot = ret;
}
if (paramNames.contains(name.get(0)) || name.get(0).equals("arguments")) {
int ind = paramNames.indexOf(name.get(0));
GraphTargetItem t = TypeItem.UNBOUNDED;
if (ind == -1) {
} else if (ind < paramTypes.size()) {
t = paramTypes.get(ind);
} //else rest parameter
GraphTargetItem ret = new NameAVM2Item(t, line, name.get(0), null, false, openedNamespaces);
resolved = ret;
for (int i = 1; i < name.size(); i++) {
resolved = new PropertyAVM2Item(resolved, name.get(i), abc, openedNamespaces, new ArrayList<>());
if (i == name.size() - 1) {
((PropertyAVM2Item) resolved).assignedValue = assignedValue;
}
}
if (name.size() == 1) {
((NameAVM2Item) ret).setAssignedValue(assignedValue);
}
return resolvedRoot = ret;
}
if (/*!subtypes.isEmpty() && */name.size()
== 1 && name.get(0).equals("Vector")) {
TypeItem ret = new TypeItem(InitVectorAVM2Item.VECTOR_FQN);
/*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;
return resolvedRoot = ret;
}
if (mustBeType) {
throw new CompilationException("Not a type", line);
}
resolved = null;
GraphTargetItem ret = null;
for (int i = 0;
i < name.size();
i++) {
resolved = new PropertyAVM2Item(resolved, name.get(i), abc, openedNamespaces, callStack);
if (ret == null) {
((PropertyAVM2Item) resolved).scopeStack = scopeStack;
ret = resolved;
}
if (i == name.size() - 1) {
((PropertyAVM2Item) resolved).setAssignedValue(assignedValue);
}
}
return resolvedRoot = ret;
}
}