/*
* 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.model;
import com.jpexs.decompiler.flash.ecma.EcmaScript;
import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
import com.jpexs.decompiler.graph.GraphSourceItem;
import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.decompiler.graph.TypeItem;
import com.jpexs.decompiler.graph.model.Callable;
import com.jpexs.decompiler.graph.model.LocalData;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
*
* @author JPEXS
*/
public class CallAVM2Item extends AVM2Item {
public GraphTargetItem receiver;
public GraphTargetItem function;
public List<GraphTargetItem> arguments;
private static abstract class Func implements Callable {
@Override
public Object call(String methodName, List<Object> args) {
return call(args);
}
}
private static Map<String, Func> bundledFunctions = new HashMap<>();
static {
bundledFunctions.put("parseInt", new Func() {
@Override
public Object call(List<Object> args) {
Object v = args.get(0);
Object r = args.size() > 1 ? args.get(0) : 0L;
return EcmaScript.parseInt(v, r);
}
});
bundledFunctions.put("parseFloat", new Func() {
@Override
public Object call(List<Object> args) {
return EcmaScript.parseFloat(args.get(0));
}
});
bundledFunctions.put("Number", new Func() {
@Override
public Object call(List<Object> args) {
return EcmaScript.toNumber(args.get(0));
}
});
bundledFunctions.put("isNaN", new Func() {
@Override
public Object call(List<Object> args) {
return EcmaScript.isNaN(args.get(0));
}
});
bundledFunctions.put("int", new Func() {
@Override
public Object call(List<Object> args) {
return EcmaScript.toInt32(args.get(0));
}
});
bundledFunctions.put("uint", new Func() {
@Override
public Object call(List<Object> args) {
return EcmaScript.toUint32(args.get(0));
}
});
bundledFunctions.put("encodeURI", new Func() {
@Override
public Object call(List<Object> args) {
return EcmaScript.encodeUri(args.get(0));
}
});
bundledFunctions.put("decodeURI", new Func() {
@Override
public Object call(List<Object> args) {
return EcmaScript.decodeUri(args.get(0));
}
});
bundledFunctions.put("encodeURIComponent", new Func() {
@Override
public Object call(List<Object> args) {
return EcmaScript.encodeUriComponent(args.get(0));
}
});
bundledFunctions.put("decodeURIComponent", new Func() {
@Override
public Object call(List<Object> args) {
return EcmaScript.decodeUriComponent(args.get(0));
}
});
bundledFunctions.put("escape", new Func() {
@Override
public Object call(List<Object> args) {
return EcmaScript.escape(args.get(0));
}
});
bundledFunctions.put("unescape", new Func() {
@Override
public Object call(List<Object> args) {
return EcmaScript.unescape(args.get(0));
}
});
}
public CallAVM2Item(GraphSourceItem instruction, GraphSourceItem lineStartIns, GraphTargetItem receiver, GraphTargetItem function, List<GraphTargetItem> arguments) {
super(instruction, lineStartIns, PRECEDENCE_PRIMARY);
this.receiver = receiver;
this.function = function;
this.arguments = arguments;
}
@Override
public Object getResult() {
if (!isCompileTime()) {
return null;
}
List<Object> oargs = new ArrayList<>();
for (GraphTargetItem ot : arguments) {
Object r = ot.getResult();
if (r == null) {
return false;
}
oargs.add(r);
}
if (function instanceof GetLexAVM2Item) {
String propName = ((GetLexAVM2Item) function).getRawPropertyName();
if (bundledFunctions.containsKey(propName)) {
return bundledFunctions.get(propName).call(oargs);
}
} else if (function instanceof Callable) {
return ((Callable) function).call(oargs);
}
return null;
}
@Override
public boolean isCompileTime(Set<GraphTargetItem> dependencies) {
for (GraphTargetItem a : arguments) {
if (!a.isCompileTime(dependencies)) {
return false;
}
}
//TODO: receiver?
if ((function instanceof Callable) && (function.isCompileTime())) {
return true;
} else if (function instanceof GetLexAVM2Item) {
String propName = ((GetLexAVM2Item) function).getRawPropertyName();
if (bundledFunctions.containsKey(propName)) {
return true;
}
}
return false;
}
@Override
public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException {
/*String recPart = ""; receiver.toString(constants, localRegNames) + writer.append(".");
if (receiver instanceof NewActivationAVM2Item) {
recPart = "";
}
if (receiver instanceof ThisAVM2Item) {
recPart = "";
}*/
if (function.getPrecedence() > precedence) {
writer.append("(");
function.toString(writer, localData);
writer.append(")");
} else {
function.toString(writer, localData);
}
writer.spaceBeforeCallParenthesies(arguments.size());
writer.append("(");
for (int a = 0; a < arguments.size(); a++) {
if (a > 0) {
writer.append(",");
}
arguments.get(a).toString(writer, localData);
}
return writer.append(")");
}
@Override
public GraphTargetItem returnType() {
return TypeItem.UNBOUNDED;
}
@Override
public boolean hasReturnValue() {
return true;
}
}