package railo.transformer.bytecode.util;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.GeneratorAdapter;
import org.objectweb.asm.commons.Method;
import railo.aprint;
import railo.commons.digest.MD5;
import railo.commons.io.IOUtil;
import railo.commons.io.res.Resource;
import railo.commons.lang.StringUtil;
import railo.runtime.component.Property;
import railo.runtime.exp.PageException;
import railo.runtime.net.rpc.AxisCaster;
import railo.runtime.op.Caster;
import railo.runtime.op.Decision;
import railo.runtime.type.dt.TimeSpanImpl;
import railo.runtime.type.util.ArrayUtil;
import railo.runtime.type.util.ListUtil;
import railo.transformer.bytecode.Body;
import railo.transformer.bytecode.BytecodeContext;
import railo.transformer.bytecode.BytecodeException;
import railo.transformer.bytecode.Literal;
import railo.transformer.bytecode.Page;
import railo.transformer.bytecode.Position;
import railo.transformer.bytecode.ScriptBody;
import railo.transformer.bytecode.Statement;
import railo.transformer.bytecode.cast.Cast;
import railo.transformer.bytecode.cast.CastBoolean;
import railo.transformer.bytecode.cast.CastDouble;
import railo.transformer.bytecode.cast.CastString;
import railo.transformer.bytecode.expression.ExprDouble;
import railo.transformer.bytecode.expression.ExprString;
import railo.transformer.bytecode.expression.Expression;
import railo.transformer.bytecode.expression.var.Argument;
import railo.transformer.bytecode.expression.var.BIF;
import railo.transformer.bytecode.expression.var.DataMember;
import railo.transformer.bytecode.expression.var.Member;
import railo.transformer.bytecode.expression.var.NullExpression;
import railo.transformer.bytecode.expression.var.Variable;
import railo.transformer.bytecode.expression.var.VariableString;
import railo.transformer.bytecode.literal.Identifier;
import railo.transformer.bytecode.literal.LitBoolean;
import railo.transformer.bytecode.literal.LitDouble;
import railo.transformer.bytecode.literal.LitString;
import railo.transformer.bytecode.statement.FlowControl;
import railo.transformer.bytecode.statement.FlowControlBreak;
import railo.transformer.bytecode.statement.FlowControlContinue;
import railo.transformer.bytecode.statement.FlowControlFinal;
import railo.transformer.bytecode.statement.FlowControlRetry;
import railo.transformer.bytecode.statement.PrintOut;
import railo.transformer.bytecode.statement.TryCatchFinally;
import railo.transformer.bytecode.statement.tag.Attribute;
import railo.transformer.bytecode.statement.tag.Tag;
import railo.transformer.bytecode.statement.tag.TagComponent;
import railo.transformer.bytecode.statement.tag.TagTry;
import railo.transformer.cfml.evaluator.EvaluatorException;
public final class ASMUtil {
//private static final int VERSION_2=1;
//private static final int VERSION_3=2;
public static final short TYPE_ALL=0;
public static final short TYPE_BOOLEAN=1;
public static final short TYPE_NUMERIC=2;
public static final short TYPE_STRING=4;
//private static int version=0;
private final static Method CONSTRUCTOR_OBJECT = Method.getMethod("void <init> ()");
private static final Method _SRC_NAME = new Method("_srcName",
Types.STRING,
new Type[]{}
);;
//private static final String VERSION_MESSAGE = "you use an invalid version of the ASM Jar, please update your jar files";
private static long id=0;
/**
* Gibt zurueck ob das direkt uebergeordnete Tag mit dem uebergebenen Full-Name (Namespace und Name) existiert.
* @param el Startelement, von wo aus gesucht werden soll.
* @param fullName Name des gesuchten Tags.
* @return Existiert ein solches Tag oder nicht.
*/
public static boolean hasAncestorTag(Tag tag, String fullName) {
return getAncestorTag(tag, fullName)!=null;
}
/**
* Gibt das uebergeordnete CFXD Tag Element zurueck, falls dies nicht existiert wird null zurueckgegeben.
* @param el Element von dem das parent Element zurueckgegeben werden soll.
* @return uebergeordnete CFXD Tag Element
*/
public static Tag getParentTag(Tag tag) {
Statement p=tag.getParent();
if(p==null)return null;
p=p.getParent();
if(p instanceof Tag) return (Tag) p;
return null;
}
public static boolean isParentTag(Tag tag,String fullName) {
Tag p = getParentTag(tag);
if(p==null) return false;
return p.getFullname().equalsIgnoreCase(fullName);
}
public static boolean isParentTag(Tag tag,Class clazz) {
Tag p = getParentTag(tag);
if(p==null) return false;
return p.getClass()==clazz;
}
public static boolean hasAncestorRetryFCStatement(Statement stat,String label) {
return getAncestorRetryFCStatement(stat,null,label)!=null;
}
public static boolean hasAncestorBreakFCStatement(Statement stat,String label) {
return getAncestorBreakFCStatement(stat,null,label)!=null;
}
public static boolean hasAncestorContinueFCStatement(Statement stat,String label) {
return getAncestorContinueFCStatement(stat,null,label)!=null;
}
public static FlowControlRetry getAncestorRetryFCStatement(Statement stat, List<FlowControlFinal> finallyLabels, String label) {
return (FlowControlRetry) getAncestorFCStatement(stat, finallyLabels, FlowControl.RETRY,label);
}
public static FlowControlBreak getAncestorBreakFCStatement(Statement stat, List<FlowControlFinal> finallyLabels, String label) {
return (FlowControlBreak) getAncestorFCStatement(stat, finallyLabels, FlowControl.BREAK,label);
}
public static FlowControlContinue getAncestorContinueFCStatement(Statement stat, List<FlowControlFinal> finallyLabels, String label) {
return (FlowControlContinue) getAncestorFCStatement(stat, finallyLabels, FlowControl.CONTINUE,label);
}
private static FlowControl getAncestorFCStatement(Statement stat, List<FlowControlFinal> finallyLabels, int flowType, String label) {
Statement parent = stat;
FlowControlFinal fcf;
while(true) {
parent=parent.getParent();
if(parent==null)return null;
if(
((flowType==FlowControl.RETRY && parent instanceof FlowControlRetry) ||
(flowType==FlowControl.CONTINUE && parent instanceof FlowControlContinue) ||
(flowType==FlowControl.BREAK && parent instanceof FlowControlBreak))
&&
labelMatch((FlowControl)parent,label)) {
if(parent instanceof ScriptBody){
List<FlowControlFinal> _finallyLabels=finallyLabels==null?null:new ArrayList<FlowControlFinal>();
FlowControl scriptBodyParent = getAncestorFCStatement(parent,_finallyLabels,flowType,label);
if(scriptBodyParent!=null) {
if(finallyLabels!=null){
Iterator<FlowControlFinal> it = _finallyLabels.iterator();
while(it.hasNext()){
finallyLabels.add(it.next());
}
}
return scriptBodyParent;
}
return (FlowControl)parent;
}
return (FlowControl) parent;
}
// only if not last
if(finallyLabels!=null){
fcf = parent.getFlowControlFinal();
if(fcf!=null){
finallyLabels.add(fcf);
}
}
}
}
private static boolean labelMatch(FlowControl fc, String label) {
if(StringUtil.isEmpty(label,true)) return true;
String fcl = fc.getLabel();
if(StringUtil.isEmpty(fcl,true)) return false;
return label.trim().equalsIgnoreCase(fcl.trim());
}
public static void leadFlow(BytecodeContext bc,Statement stat, int flowType, String label) throws BytecodeException {
List<FlowControlFinal> finallyLabels=new ArrayList<FlowControlFinal>();
FlowControl fc;
String name;
if(FlowControl.BREAK==flowType) {
fc=ASMUtil.getAncestorBreakFCStatement(stat,finallyLabels,label);
name="break";
}
else if(FlowControl.CONTINUE==flowType) {
fc=ASMUtil.getAncestorContinueFCStatement(stat,finallyLabels,label);
name="continue";
}
else {
fc=ASMUtil.getAncestorRetryFCStatement(stat,finallyLabels,label);
name="retry";
}
if(fc==null)
throw new BytecodeException(name+" must be inside a loop (for,while,do-while,<cfloop>,<cfwhile> ...)",stat.getStart());
GeneratorAdapter adapter = bc.getAdapter();
Label end;
if(FlowControl.BREAK==flowType) end=((FlowControlBreak)fc).getBreakLabel();
else if(FlowControl.CONTINUE==flowType) end=((FlowControlContinue)fc).getContinueLabel();
else end=((FlowControlRetry)fc).getRetryLabel();
// first jump to all final labels
FlowControlFinal[] arr = finallyLabels.toArray(new FlowControlFinal[finallyLabels.size()]);
if(arr.length>0) {
FlowControlFinal fcf;
for(int i=0;i<arr.length;i++){
fcf=arr[i];
// first
if(i==0) {
adapter.visitJumpInsn(Opcodes.GOTO, fcf.getFinalEntryLabel());
}
// last
if(arr.length==i+1) fcf.setAfterFinalGOTOLabel(end);
else fcf.setAfterFinalGOTOLabel(arr[i+1].getFinalEntryLabel());
}
}
else bc.getAdapter().visitJumpInsn(Opcodes.GOTO, end);
}
public static boolean hasAncestorTryStatement(Statement stat) {
return getAncestorTryStatement(stat)!=null;
}
public static Statement getAncestorTryStatement(Statement stat) {
Statement parent = stat;
while(true) {
parent=parent.getParent();
if(parent==null)return null;
if(parent instanceof TagTry) {
return parent;
}
else if(parent instanceof TryCatchFinally) {
return parent;
}
}
}
/**
* Gibt ein uebergeordnetes Tag mit dem uebergebenen Full-Name (Namespace und Name) zurueck,
* falls ein solches existiert, andernfalls wird null zurueckgegeben.
* @param el Startelement, von wo aus gesucht werden soll.
* @param fullName Name des gesuchten Tags.
* @return �bergeornetes Element oder null.
*/
public static Tag getAncestorTag(Tag tag, String fullName) {
Statement parent=tag;
while(true) {
parent=parent.getParent();
if(parent==null)return null;
if(parent instanceof Tag) {
tag=(Tag) parent;
if(tag.getFullname().equalsIgnoreCase(fullName))
return tag;
}
}
}
/**
* extract the content of a attribut
* @param cfxdTag
* @param attrName
* @return attribute value
* @throws EvaluatorException
*/
public static Boolean getAttributeBoolean(Tag tag,String attrName) throws EvaluatorException {
Boolean b= getAttributeLiteral(tag, attrName).getBoolean(null);
if(b==null)throw new EvaluatorException("attribute ["+attrName+"] must be a constant boolean value");
return b;
}
/**
* extract the content of a attribut
* @param cfxdTag
* @param attrName
* @return attribute value
* @throws EvaluatorException
*/
public static Boolean getAttributeBoolean(Tag tag,String attrName, Boolean defaultValue) {
Literal lit=getAttributeLiteral(tag, attrName,null);
if(lit==null) return defaultValue;
return lit.getBoolean(defaultValue);
}
/**
* extract the content of a attribut
* @param cfxdTag
* @param attrName
* @return attribute value
* @throws EvaluatorException
*/
public static String getAttributeString(Tag tag,String attrName) throws EvaluatorException {
return getAttributeLiteral(tag, attrName).getString();
}
/**
* extract the content of a attribut
* @param cfxdTag
* @param attrName
* @return attribute value
* @throws EvaluatorException
*/
public static String getAttributeString(Tag tag,String attrName, String defaultValue) {
Literal lit=getAttributeLiteral(tag, attrName,null);
if(lit==null) return defaultValue;
return lit.getString();
}
/**
* extract the content of a attribut
* @param cfxdTag
* @param attrName
* @return attribute value
* @throws EvaluatorException
*/
public static Literal getAttributeLiteral(Tag tag,String attrName) throws EvaluatorException {
Attribute attr = tag.getAttribute(attrName);
if(attr!=null && attr.getValue() instanceof Literal) return ((Literal)attr.getValue());
throw new EvaluatorException("attribute ["+attrName+"] must be a constant value");
}
/**
* extract the content of a attribut
* @param cfxdTag
* @param attrName
* @return attribute value
* @throws EvaluatorException
*/
public static Literal getAttributeLiteral(Tag tag,String attrName, Literal defaultValue) {
Attribute attr = tag.getAttribute(attrName);
if(attr!=null && attr.getValue() instanceof Literal) return ((Literal)attr.getValue());
return defaultValue;
}
/**
* Prueft ob das das angegebene Tag in der gleichen Ebene nach dem angegebenen Tag vorkommt.
* @param tag Ausgangspunkt, nach diesem tag darf das angegebene nicht vorkommen.
* @param nameToFind Tag Name der nicht vorkommen darf
* @return kommt das Tag vor.
*/
public static boolean hasSisterTagAfter(Tag tag, String nameToFind) {
Body body=(Body) tag.getParent();
List<Statement> stats = body.getStatements();
Iterator<Statement> it = stats.iterator();
Statement other;
boolean isAfter=false;
while(it.hasNext()) {
other=it.next();
if(other instanceof Tag) {
if(isAfter) {
if(((Tag) other).getTagLibTag().getName().equals(nameToFind))
return true;
}
else if(other == tag) isAfter=true;
}
}
return false;
}
/**
* Prueft ob das angegebene Tag innerhalb seiner Ebene einmalig ist oder nicht.
* @param tag Ausgangspunkt, nach diesem tag darf das angegebene nicht vorkommen.
* @return kommt das Tag vor.
*/
public static boolean hasSisterTagWithSameName(Tag tag) {
Body body=(Body) tag.getParent();
List<Statement> stats = body.getStatements();
Iterator<Statement> it = stats.iterator();
Statement other;
String name=tag.getTagLibTag().getName();
while(it.hasNext()) {
other=it.next();
if(other != tag && other instanceof Tag && ((Tag) other).getTagLibTag().getName().equals(name))
return true;
}
return false;
}
/**
* remove this tag from his parent body
* @param tag
*/
public static void remove(Tag tag) {
Body body=(Body) tag.getParent();
body.getStatements().remove(tag);
}
/**
* replace src with trg
* @param src
* @param trg
*/
public static void replace(Tag src, Tag trg, boolean moveBody) {
trg.setParent(src.getParent());
Body p=(Body) src.getParent();
List<Statement> stats = p.getStatements();
Iterator<Statement> it = stats.iterator();
Statement stat;
int count=0;
while(it.hasNext()) {
stat=it.next();
if(stat==src) {
if(moveBody && src.getBody()!=null)src.getBody().setParent(trg);
stats.set(count, trg);
break;
}
count++;
}
}
public static Page getAncestorPage(Statement stat) throws BytecodeException {
Statement parent=stat;
while(true) {
parent=parent.getParent();
if(parent==null) {
throw new BytecodeException("missing parent Statement of Statement",stat.getStart());
//return null;
}
if(parent instanceof Page) return (Page) parent;
}
}
public static Page getAncestorPage(Statement stat, Page defaultValue) {
Statement parent=stat;
while(true) {
parent=parent.getParent();
if(parent==null) {
return defaultValue;
}
if(parent instanceof Page) return (Page) parent;
}
}
public static void listAncestor(Statement stat) {
Statement parent=stat;
aprint.o(stat);
while(true) {
parent=parent.getParent();
if(parent instanceof Page)aprint.o("page-> "+ ((Page)parent).getSource());
else aprint.o("parent-> "+ parent);
if(parent==null) break;
}
}
public static Tag getAncestorComponent(Statement stat) throws BytecodeException {
//print.ln("getAncestorPage:"+stat);
Statement parent=stat;
while(true) {
parent=parent.getParent();
//print.ln(" - "+parent);
if(parent==null) {
throw new BytecodeException("missing parent Statement of Statement",stat.getStart());
//return null;
}
if(parent instanceof TagComponent)
//if(parent instanceof Tag && "component".equals(((Tag)parent).getTagLibTag().getName()))
return (Tag) parent;
}
}
public static Statement getRoot(Statement stat) {
while(true) {
if(isRoot(stat)) {
return stat;
}
stat=stat.getParent();
}
}
public static boolean isRoot(Statement statement) {
//return statement instanceof Page || (statement instanceof Tag && "component".equals(((Tag)statement).getTagLibTag().getName()));
return statement instanceof Page || statement instanceof TagComponent;
}
public static void invokeMethod(GeneratorAdapter adapter, Type type, Method method) {
if(type.getClass().isInterface())
adapter.invokeInterface(type, method);
else
adapter.invokeVirtual(type, method);
}
public static byte[] createPojo(String className, ASMProperty[] properties,Class parent,Class[] interfaces, String srcName) throws PageException {
className=className.replace('.', '/');
className=className.replace('\\', '/');
className=ListUtil.trim(className, "/");
String[] inter=null;
if(interfaces!=null){
inter=new String[interfaces.length];
for(int i=0;i<inter.length;i++){
inter[i]=interfaces[i].getName().replace('.', '/');
}
}
// CREATE CLASS
//ClassWriter cw = new ClassWriter(true);
ClassWriter cw = ASMUtil.getClassWriter();
cw.visit(Opcodes.V1_2, Opcodes.ACC_PUBLIC, className, null, parent.getName().replace('.', '/'), inter);
String md5;
try{
md5=createMD5(properties);
}
catch(Throwable t){
md5="";
t.printStackTrace();
}
FieldVisitor fv = cw.visitField(Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_STATIC, "_md5_", "Ljava/lang/String;", null, md5);
fv.visitEnd();
// Constructor
GeneratorAdapter adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC,CONSTRUCTOR_OBJECT,null,null,cw);
adapter.loadThis();
adapter.invokeConstructor(toType(parent,true), CONSTRUCTOR_OBJECT);
adapter.returnValue();
adapter.endMethod();
// properties
for(int i=0;i<properties.length;i++){
createProperty(cw,className,properties[i]);
}
// complexType src
if(!StringUtil.isEmpty(srcName)) {
GeneratorAdapter _adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL+ Opcodes.ACC_STATIC , _SRC_NAME, null, null, cw);
_adapter.push(srcName);
_adapter.returnValue();
_adapter.endMethod();
}
cw.visitEnd();
return cw.toByteArray();
}
private static void createProperty(ClassWriter cw,String classType, ASMProperty property) throws PageException {
String name = property.getName();
Type type = property.getASMType();
Class clazz = property.getClazz();
cw.visitField(Opcodes.ACC_PRIVATE, name, type.toString(), null, null).visitEnd();
int load=loadFor(type);
//int sizeOf=sizeOf(type);
// get<PropertyName>():<type>
Type[] types=new Type[0];
Method method = new Method((clazz==boolean.class?"get":"get")+StringUtil.ucFirst(name),type,types);
GeneratorAdapter adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC , method, null, null, cw);
Label start = new Label();
adapter.visitLabel(start);
adapter.visitVarInsn(Opcodes.ALOAD, 0);
adapter.visitFieldInsn(Opcodes.GETFIELD, classType, name, type.toString());
adapter.returnValue();
Label end = new Label();
adapter.visitLabel(end);
adapter.visitLocalVariable("this", "L"+classType+";", null, start, end, 0);
adapter.visitEnd();
adapter.endMethod();
// set<PropertyName>(object):void
types=new Type[]{type};
method = new Method("set"+StringUtil.ucFirst(name),Types.VOID,types);
adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC , method, null, null, cw);
start = new Label();
adapter.visitLabel(start);
adapter.visitVarInsn(Opcodes.ALOAD, 0);
adapter.visitVarInsn(load, 1);
adapter.visitFieldInsn(Opcodes.PUTFIELD, classType, name, type.toString());
adapter.visitInsn(Opcodes.RETURN);
end = new Label();
adapter.visitLabel(end);
adapter.visitLocalVariable("this", "L"+classType+";", null, start, end, 0);
adapter.visitLocalVariable(name, type.toString(), null, start, end, 1);
//adapter.visitMaxs(0, 0);//.visitMaxs(sizeOf+1, sizeOf+1);// hansx
adapter.visitEnd();
adapter.endMethod();
}
public static int loadFor(Type type) {
if(type.equals(Types.BOOLEAN_VALUE) || type.equals(Types.INT_VALUE) || type.equals(Types.CHAR) || type.equals(Types.SHORT_VALUE))
return Opcodes.ILOAD;
if(type.equals(Types.FLOAT_VALUE))
return Opcodes.FLOAD;
if(type.equals(Types.LONG_VALUE))
return Opcodes.LLOAD;
if(type.equals(Types.DOUBLE_VALUE))
return Opcodes.DLOAD;
return Opcodes.ALOAD;
}
public static int sizeOf(Type type) {
if(type.equals(Types.LONG_VALUE) || type.equals(Types.DOUBLE_VALUE))
return 2;
return 1;
}
/**
* translate a string cfml type definition to a Type Object
* @param cfType
* @param axistype
* @return
* @throws PageException
*/
public static Type toType(String cfType, boolean axistype) throws PageException {
return toType(Caster.cfTypeToClass(cfType), axistype);
}
/**
* translate a string cfml type definition to a Type Object
* @param cfType
* @param axistype
* @return
* @throws PageException
*/
public static Type toType(Class type, boolean axistype) {
if(axistype)type=AxisCaster.toAxisTypeClass(type);
return Type.getType(type);
}
public static String createMD5(ASMProperty[] props) {
StringBuffer sb=new StringBuffer();
for(int i=0;i<props.length;i++){
sb.append("name:"+props[i].getName()+";");
if(props[i] instanceof Property){
sb.append("type:"+((Property)props[i]).getType()+";");
}
else {
try {
sb.append("type:"+props[i].getASMType()+";");
}
catch (PageException e) {}
}
}
try {
return MD5.getDigestAsString(sb.toString());
} catch (IOException e) {
return "";
}
}
public static void removeLiterlChildren(Tag tag, boolean recursive) {
Body body=tag.getBody();
if(body!=null) {
List<Statement> list = body.getStatements();
Statement[] stats = list.toArray(new Statement[list.size()]);
PrintOut po;
Tag t;
for(int i=0;i<stats.length;i++) {
if(stats[i] instanceof PrintOut) {
po=(PrintOut) stats[i];
if(po.getExpr() instanceof Literal) {
body.getStatements().remove(po);
}
}
else if(recursive && stats[i] instanceof Tag) {
t=(Tag) stats[i];
if(t.getTagLibTag().isAllowRemovingLiteral()) {
removeLiterlChildren(t, recursive);
}
}
}
}
}
public synchronized static String getId() {
if(id<0)id=0;
return StringUtil.addZeros(++id,6);
}
public static boolean isEmpty(Body body) {
return body==null || body.isEmpty();
}
/**
* @param adapter
* @param expr
* @param mode
*/
public static void pop(GeneratorAdapter adapter, Expression expr,int mode) {
if(mode==Expression.MODE_VALUE && (expr instanceof ExprDouble))adapter.pop2();
else adapter.pop();
}
public static void pop(GeneratorAdapter adapter, Type type) {
if(type.equals(Types.DOUBLE_VALUE))adapter.pop2();
else if(type.equals(Types.VOID));
else adapter.pop();
}
public static ClassWriter getClassWriter() {
return new ClassWriter(ClassWriter.COMPUTE_MAXS|ClassWriter.COMPUTE_FRAMES);
/*if(true) return new ClassWriter(ClassWriter.COMPUTE_MAXS);
if(version==VERSION_2)
return new ClassWriter(ClassWriter.COMPUTE_MAXS+ClassWriter.COMPUTE_FRAMES);
try{
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
version=VERSION_2;
return cw;
}
catch(NoSuchMethodError err){
if(version==0){
version=VERSION_3;
}
PrintWriter ew = ThreadLocalPageContext.getConfig().getErrWriter();
SystemOut.printDate(ew, VERSION_MESSAGE);
try {
return ClassWriter.class.getConstructor(new Class[]{boolean.class}).newInstance(new Object[]{Boolean.TRUE});
}
catch (Exception e) {
throw new RuntimeException(Caster.toPageException(e));
}
}*/
}
/*
* For 3.1
*
* public static ClassWriter getClassWriter() {
if(version==VERSION_3)
return new ClassWriter(ClassWriter.COMPUTE_MAXS);
try{
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
version=VERSION_3;
return cw;
}
catch(NoSuchMethodError err){
if(version==0){
version=VERSION_2;
throw new RuntimeException(new ApplicationException(VERSION_MESSAGE+
", after reload this version will work as well, but please update to newer version"));
}
PrintWriter ew = ThreadLocalPageContext.getConfig().getErrWriter();
SystemOut.printDate(ew, VERSION_MESSAGE);
//err.printStackTrace(ew);
try {
return (ClassWriter) ClassWriter.class.getConstructor(new Class[]{boolean.class}).newInstance(new Object[]{Boolean.TRUE});
}
catch (Exception e) {
throw new RuntimeException(Caster.toPageException(e));
}
}
}*/
public static String createOverfowMethod(String prefix, int id) { // pattern is used in function callstackget
if(StringUtil.isEmpty(prefix)) prefix="call";
return prefix+"_"+StringUtil.addZeros(id,6);
}
public static boolean isOverfowMethod(String name) {
return name.length()>6 && Decision.isNumeric(name.substring(name.length()-6,name.length()));
//return name.startsWith("_call") && name.length()>=11;
}
public static boolean isDotKey(ExprString expr) {
return expr instanceof LitString && !((LitString)expr).fromBracket();
}
public static String toString(Expression exp,String defaultValue) {
try {
return toString(exp);
} catch (BytecodeException e) {
return defaultValue;
}
}
public static String toString(Expression exp) throws BytecodeException {
if(exp instanceof Variable) {
return toString(VariableString.toExprString(exp));
}
else if(exp instanceof VariableString) {
return ((VariableString)exp).castToString();
}
else if(exp instanceof Literal) {
return ((Literal)exp).toString();
}
return null;
}
public static Boolean toBoolean(Attribute attr, Position start) throws BytecodeException {
if(attr==null)
throw new BytecodeException("attribute does not exist",start);
if(attr.getValue() instanceof Literal){
Boolean b=((Literal)attr.getValue()).getBoolean(null);
if(b!=null) return b;
}
throw new BytecodeException("attribute ["+attr.getName()+"] must be a constant boolean value",start);
}
public static Boolean toBoolean(Attribute attr, int line, Boolean defaultValue) {
if(attr==null)
return defaultValue;
if(attr.getValue() instanceof Literal){
Boolean b=((Literal)attr.getValue()).getBoolean(null);
if(b!=null) return b;
}
return defaultValue;
}
public static boolean isCFC(Statement s) {
Statement p;
while((p=s.getParent())!=null){
s=p;
}
return true;
}
public static boolean isLiteralAttribute(Tag tag, String attrName, short type,boolean required,boolean throwWhenNot) throws EvaluatorException {
return isLiteralAttribute(tag,tag.getAttribute(attrName), type, required, throwWhenNot);
}
public static boolean isLiteralAttribute(Tag tag,Attribute attr, short type,boolean required,boolean throwWhenNot) throws EvaluatorException {
String strType="/constant";
if(attr!=null && !isNull(attr.getValue())) {
switch(type){
case TYPE_ALL:
if(attr.getValue() instanceof Literal) return true;
break;
case TYPE_BOOLEAN:
if(CastBoolean.toExprBoolean(attr.getValue()) instanceof LitBoolean) return true;
strType=" boolean";
break;
case TYPE_NUMERIC:
if(CastDouble.toExprDouble(attr.getValue()) instanceof LitDouble) return true;
strType=" numeric";
break;
case TYPE_STRING:
if(CastString.toExprString(attr.getValue()) instanceof LitString) return true;
strType=" string";
break;
}
if(!throwWhenNot) return false;
throw new EvaluatorException("Attribute ["+attr.getName()+"] of the Tag ["+tag.getFullname()+"] must be a literal"+strType+" value. "+
"attributes java class type "+attr.getValue().getClass().getName());
}
if(required){
if(!throwWhenNot) return false;
throw new EvaluatorException("Attribute ["+attr.getName()+"] of the Tag ["+tag.getFullname()+"] is required");
}
return false;
}
public static boolean isNull(Expression expr) {
if(expr instanceof NullExpression) return true;
if(expr instanceof Cast) {
return isNull(((Cast)expr).getExpr());
}
return false;
}
public static boolean isRefType(Type type) {
return
!(type==Types.BYTE_VALUE || type==Types.BOOLEAN_VALUE || type==Types.CHAR || type==Types.DOUBLE_VALUE ||
type==Types.FLOAT_VALUE || type==Types.INT_VALUE || type==Types.LONG_VALUE || type==Types.SHORT_VALUE);
}
public static Type toRefType(Type type) {
if(type==Types.BYTE_VALUE) return Types.BYTE;
if(type==Types.BOOLEAN_VALUE) return Types.BOOLEAN;
if(type==Types.CHAR) return Types.CHARACTER;
if(type==Types.DOUBLE_VALUE) return Types.DOUBLE;
if(type==Types.FLOAT_VALUE) return Types.FLOAT;
if(type==Types.INT_VALUE) return Types.INTEGER;
if(type==Types.LONG_VALUE) return Types.LONG;
if(type==Types.SHORT_VALUE) return Types.SHORT;
return type;
}
/**
* return value type only when there is one
* @param type
* @return
*/
public static Type toValueType(Type type) {
if(type==Types.BYTE) return Types.BYTE_VALUE;
if(type==Types.BOOLEAN) return Types.BOOLEAN_VALUE;
if(type==Types.CHARACTER) return Types.CHAR;
if(type==Types.DOUBLE) return Types.DOUBLE_VALUE;
if(type==Types.FLOAT) return Types.FLOAT_VALUE;
if(type==Types.INTEGER) return Types.INT_VALUE;
if(type==Types.LONG) return Types.LONG_VALUE;
if(type==Types.SHORT) return Types.SHORT_VALUE;
return type;
}
public static Class getValueTypeClass(Type type, Class defaultValue) {
if(type==Types.BYTE_VALUE) return byte.class;
if(type==Types.BOOLEAN_VALUE) return boolean.class;
if(type==Types.CHAR) return char.class;
if(type==Types.DOUBLE_VALUE) return double.class;
if(type==Types.FLOAT_VALUE) return float.class;
if(type==Types.INT_VALUE) return int.class;
if(type==Types.LONG_VALUE) return long.class;
if(type==Types.SHORT_VALUE) return short.class;
return defaultValue;
}
public static ASMProperty[] toASMProperties(Property[] properties) {
ASMProperty[] asmp=new ASMProperty[properties.length];
for(int i=0;i<asmp.length;i++){
asmp[i]=(ASMProperty) properties[i];
}
return asmp;
}
public static boolean containsComponent(Body body) {
if(body==null) return false;
Iterator<Statement> it = body.getStatements().iterator();
while(it.hasNext()){
if(it.next() instanceof TagComponent)return true;
}
return false;
}
public static void dummy1(BytecodeContext bc) {
bc.getAdapter().visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J");
bc.getAdapter().visitInsn(Opcodes.POP2);
}
public static void dummy2(BytecodeContext bc) {
bc.getAdapter().visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/System", "nanoTime", "()J");
bc.getAdapter().visitInsn(Opcodes.POP2);
}
/**
* convert a clas array to a type array
* @param classes
* @return
*/
public static Type[] toTypes(Class<?>[] classes) {
if(classes==null || classes.length==0)
return new Type[0];
Type[] types=new Type[classes.length];
for(int i=0;i<classes.length;i++) {
types[i]=Type.getType(classes[i]);
}
return types;
}
public static String display(ExprString name) {
if(name instanceof Literal) {
if(name instanceof Identifier)
return ((Identifier)name).getRaw();
return ((Literal)name).getString();
}
return name.toString();
}
public static long timeSpanToLong(Expression val) throws EvaluatorException {
if(val instanceof Literal) {
Double d = ((Literal)val).getDouble(null);
if(d==null) throw cacheWithinException();
return TimeSpanImpl.fromDays(d.doubleValue()).getMillis();
}
// createTimespan
else if(val instanceof Variable) {
Variable var=(Variable)val;
if(var.getMembers().size()==1) {
Member first = var.getFirstMember();
if(first instanceof BIF) {
BIF bif=(BIF) first;
if("createTimeSpan".equalsIgnoreCase(bif.getFlf().getName())) {
Argument[] args = bif.getArguments();
int len=ArrayUtil.size(args);
if(len>=4 && len<=5) {
double days=toDouble(args[0].getValue());
double hours=toDouble(args[1].getValue());
double minutes=toDouble(args[2].getValue());
double seconds=toDouble(args[3].getValue());
double millis=len==5?toDouble(args[4].getValue()):0;
return new TimeSpanImpl((int)days,(int)hours,(int)minutes,(int)seconds,(int)millis).getMillis();
}
}
}
}
}
throw cacheWithinException();
}
private static EvaluatorException cacheWithinException() {
return new EvaluatorException("value of cachedWithin must be a literal timespan, like 0.1 or createTimespan(1,2,3,4)");
}
private static double toDouble(Expression e) throws EvaluatorException {
if(!(e instanceof Literal))
throw new EvaluatorException("Paremeters of the function createTimeSpan have to be literal numeric values in this context");
Double d = ((Literal)e).getDouble(null);
if(d==null)
throw new EvaluatorException("Paremeters of the function createTimeSpan have to be literal numeric values in this context");
return d.doubleValue();
}
public static void visitLabel(GeneratorAdapter ga, Label label) {
if(label!=null) ga.visitLabel(label);
}
public static String getClassName(Resource res) throws IOException{
byte[] src=IOUtil.toBytes(res);
ClassReader cr = new ClassReader(src);
return cr.getClassName();
}
public static String getClassName(byte[] barr){
return new ClassReader(barr).getClassName();
}
public static String getSourceName(Class clazz) throws IOException {
return SourceNameClassVisitor.getSourceName(clazz);
}
public static boolean hasOnlyDataMembers(Variable var) {
Iterator<Member> it = var.getMembers().iterator();
Member m;
while(it.hasNext()){
m = it.next();
if(!(m instanceof DataMember)) return false;
}
return true;
}
}