/**
* Copyright (c) 2014, the Railo Company Ltd.
* Copyright (c) 2015, Lucee Assosication Switzerland
*
* 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 2.1 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. If not, see <http://www.gnu.org/licenses/>.
*
*/
package lucee.transformer.bytecode;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import lucee.commons.io.CharsetUtil;
import lucee.commons.io.IOUtil;
import lucee.commons.io.res.Resource;
import lucee.commons.lang.StringUtil;
import lucee.loader.engine.CFMLEngine;
import lucee.runtime.Component;
import lucee.runtime.ComponentPageImpl;
import lucee.runtime.InterfacePageImpl;
import lucee.runtime.Mapping;
import lucee.runtime.PageSource;
import lucee.runtime.component.ImportDefintion;
import lucee.runtime.component.ImportDefintionImpl;
import lucee.runtime.config.Config;
import lucee.runtime.config.Constants;
import lucee.runtime.type.KeyImpl;
import lucee.runtime.type.UDF;
import lucee.runtime.type.scope.Undefined;
import lucee.runtime.type.util.ArrayUtil;
import lucee.runtime.type.util.ComponentUtil;
import lucee.runtime.type.util.KeyConstants;
import lucee.transformer.Factory;
import lucee.transformer.TransformerException;
import lucee.transformer.bytecode.ConstrBytecodeContext.Data;
import lucee.transformer.bytecode.statement.Argument;
import lucee.transformer.bytecode.statement.HasBodies;
import lucee.transformer.bytecode.statement.HasBody;
import lucee.transformer.bytecode.statement.IFunction;
import lucee.transformer.bytecode.statement.NativeSwitch;
import lucee.transformer.bytecode.statement.tag.Attribute;
import lucee.transformer.bytecode.statement.tag.Tag;
import lucee.transformer.bytecode.statement.tag.TagCIObject;
import lucee.transformer.bytecode.statement.tag.TagComponent;
import lucee.transformer.bytecode.statement.tag.TagImport;
import lucee.transformer.bytecode.statement.tag.TagInterface;
import lucee.transformer.bytecode.statement.tag.TagThread;
import lucee.transformer.bytecode.statement.udf.Function;
import lucee.transformer.bytecode.statement.udf.FunctionImpl;
import lucee.transformer.bytecode.util.ASMConstants;
import lucee.transformer.bytecode.util.ASMUtil;
import lucee.transformer.bytecode.util.ExpressionUtil;
import lucee.transformer.bytecode.util.Types;
import lucee.transformer.bytecode.visitor.ArrayVisitor;
import lucee.transformer.bytecode.visitor.ConditionVisitor;
import lucee.transformer.bytecode.visitor.DecisionIntVisitor;
import lucee.transformer.bytecode.visitor.OnFinally;
import lucee.transformer.bytecode.visitor.TryCatchFinallyVisitor;
import lucee.transformer.expression.Expression;
import lucee.transformer.expression.literal.LitString;
import lucee.transformer.expression.literal.Literal;
import lucee.transformer.util.PageSourceCode;
import lucee.transformer.util.SourceCode;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.GeneratorAdapter;
import org.objectweb.asm.commons.Method;
/**
* represent a single Page
*/
public final class Page extends BodyBase implements Root {
public static final Type NULL = Type.getType(lucee.runtime.type.Null.class);
public static final Type KEY_IMPL = Type.getType(KeyImpl.class);
public static final Type KEY_CONSTANTS = Type.getType(KeyConstants.class);
public static final Method KEY_INIT = new Method(
"init",
Types.COLLECTION_KEY,
new Type[]{Types.STRING}
);
public static final Method KEY_INTERN = new Method(
"intern",
Types.COLLECTION_KEY,
new Type[]{Types.STRING}
);
// public static ImportDefintion getInstance(String fullname,ImportDefintion defaultValue)
private static final Method ID_GET_INSTANCE = new Method(
"getInstance",
Types.IMPORT_DEFINITIONS,
new Type[]{Types.STRING,Types.IMPORT_DEFINITIONS}
);
public final static Method STATIC_CONSTRUCTOR = Method.getMethod("void <clinit> ()V");
//public final static Method CONSTRUCTOR = Method.getMethod("void <init> ()V");
private static final Method CONSTRUCTOR = new Method(
"<init>",
Types.VOID,
new Type[]{}//
);
/*private static final Method CONSTRUCTOR_STR = new Method(
"<init>",
Types.VOID,
new Type[]{Types.STRING}//
);*/
private static final Method CONSTRUCTOR_PS = new Method(
"<init>",
Types.VOID,
new Type[]{Types.PAGE_SOURCE}//
);
//public static final Type STRUCT_IMPL = Type.getType(StructImpl.class);
public static final Method INIT_STRUCT_IMPL = new Method(
"<init>",
Types.VOID,
new Type[]{}
);
// void call (lucee.runtime.PageContext)
private final static Method CALL1 = new Method(
"call",
Types.OBJECT,
new Type[]{Types.PAGE_CONTEXT}
);
/*/ void _try ()
private final static Method TRY = new Method(
"_try",
Types.VOID,
new Type[]{}
);*/
// int getVersion()
private final static Method VERSION = new Method(
"getVersion",
Types.LONG_VALUE,
new Type[]{}
);
// void _init()
private final static Method INIT_KEYS = new Method(
"initKeys",
Types.VOID,
new Type[]{}
);
private final static Method SET_PAGE_SOURCE = new Method(
"setPageSource",
Types.VOID,
new Type[]{Types.PAGE_SOURCE}
);
// public ImportDefintion[] getImportDefintions()
private final static Method GET_IMPORT_DEFINITIONS = new Method(
"getImportDefintions",
Types.IMPORT_DEFINITIONS_ARRAY,
new Type[]{}
);
private final static Method GET_SUB_PAGES = new Method(
"getSubPages",
Types.CI_PAGE_ARRAY,
new Type[]{}
);
// long getSourceLastModified()
private final static Method LAST_MOD = new Method(
"getSourceLastModified",
Types.LONG_VALUE,
new Type[]{}
);
private final static Method COMPILE_TIME = new Method(
"getCompileTime",
Types.LONG_VALUE,
new Type[]{}
);
private static final Type USER_DEFINED_FUNCTION = Type.getType(UDF.class);
private static final Method UDF_CALL = new Method(
"udfCall",
Types.OBJECT,
new Type[]{Types.PAGE_CONTEXT, USER_DEFINED_FUNCTION, Types.INT_VALUE}
);
private static final Method THREAD_CALL = new Method(
"threadCall",
Types.VOID,
new Type[]{Types.PAGE_CONTEXT, Types.INT_VALUE}
);
private static final Method UDF_DEFAULT_VALUE = new Method(
"udfDefaultValue",
Types.OBJECT,
new Type[]{Types.PAGE_CONTEXT, Types.INT_VALUE, Types.INT_VALUE,Types.OBJECT}
);
private static final Method NEW_COMPONENT_IMPL_INSTANCE = new Method(
"newInstance",
Types.COMPONENT_IMPL,
new Type[]{Types.PAGE_CONTEXT,Types.STRING,Types.BOOLEAN_VALUE,Types.BOOLEAN_VALUE,Types.BOOLEAN_VALUE}
);
private static final Method NEW_INTERFACE_IMPL_INSTANCE = new Method(
"newInstance",
Types.INTERFACE_IMPL,
new Type[]{Types.PAGE_CONTEXT,Types.STRING,Types.BOOLEAN_VALUE}
);
private static final Method STATIC_COMPONENT_CONSTR = new Method(
"staticConstructor",
Types.VOID,
new Type[]{Types.PAGE_CONTEXT,Types.COMPONENT_IMPL}
);
// void init(PageContext pc,Component Impl c) throws PageException
private static final Method INIT_COMPONENT3 = new Method(
"initComponent",
Types.VOID,
new Type[]{Types.PAGE_CONTEXT,Types.COMPONENT_IMPL,Types.BOOLEAN_VALUE}
);
private static final Method INIT_INTERFACE = new Method(
"initInterface",
Types.VOID,
new Type[]{Types.INTERFACE_IMPL}
);
// public boolean setMode(int mode) {
private static final Method SET_MODE = new Method(
"setMode",
Types.INT_VALUE,
new Type[]{Types.INT_VALUE}
);
private static final Method CONSTR_INTERFACE_IMPL8 = new Method(
"<init>",
Types.VOID,
new Type[]{
Types.PAGE_CONTEXT,
Types.INTERFACE_PAGE_IMPL,
Types.STRING, // extends
Types.STRING, // hind
Types.STRING, // display
Types.STRING, // callpath
Types.BOOLEAN_VALUE, // realpath
Types.MAP // meta
}
);
//void init(PageContext pageContext,ComponentPage componentPage)
private static final Method INIT_COMPONENT = new Method(
"init",
Types.VOID,
new Type[]{Types.PAGE_CONTEXT,Types.COMPONENT_PAGE_IMPL,Types.BOOLEAN_VALUE}
);
private static final Method CHECK_INTERFACE = new Method(
"checkInterface",
Types.VOID,
new Type[]{Types.PAGE_CONTEXT,Types.COMPONENT_PAGE_IMPL}
);
// boolean getOutput()
private static final Method GET_OUTPUT = new Method(
"getOutput",
Types.BOOLEAN_VALUE,
new Type[]{}
);
private static final Method PUSH_BODY = new Method(
"pushBody",
Types.BODY_CONTENT,
new Type[]{}
);
/*/ boolean setSilent()
private static final Method SET_SILENT = new Method(
"setSilent",
Types.BOOLEAN_VALUE,
new Type[]{}
);
*/
// Scope beforeCall(PageContext pc)
private static final Method BEFORE_CALL = new Method(
"beforeCall",
Types.VARIABLES,
new Type[]{Types.PAGE_CONTEXT}
);
private static final Method TO_PAGE_EXCEPTION = new Method(
"toPageException",
Types.PAGE_EXCEPTION,
new Type[]{Types.THROWABLE});
// void afterCall(PageContext pc, Scope parent)
private static final Method AFTER_CALL = new Method(
"afterConstructor",
Types.VOID,
new Type[]{Types.PAGE_CONTEXT,Types.VARIABLES}
);
private static final Method AFTER_STATIC_CONSTR = new Method(
"afterStaticConstructor",
Types.VOID,
new Type[]{Types.PAGE_CONTEXT,Types.VARIABLES}
);
private static final Method BEFORE_STATIC_CONSTR = new Method(
"beforeStaticConstructor",
Types.VARIABLES,
new Type[]{Types.PAGE_CONTEXT}
);
private static final org.objectweb.asm.commons.Method CONSTRUCTOR_EMPTY =
new org.objectweb.asm.commons.Method("<init>",Types.VOID,new Type[]{});
// Component Impl(ComponentPage,boolean, String, String, String, String) WS==With Style
private static final Method CONSTR_COMPONENT_IMPL15 = new Method(
"<init>",
Types.VOID,
new Type[]{
Types.COMPONENT_PAGE_IMPL,
Types.BOOLEAN,
Types.BOOLEAN_VALUE,
Types.STRING,
Types.STRING,
Types.STRING,
Types.STRING,
Types.STRING,
Types.BOOLEAN_VALUE,
Types.STRING,
Types.BOOLEAN_VALUE,
Types.BOOLEAN_VALUE,
Types.INT_VALUE,
Types.BOOLEAN_VALUE,
Types.STRUCT_IMPL
}
);
private static final Method SET_EL = new Method(
"setEL",
Types.OBJECT,
new Type[]{Types.STRING,Types.OBJECT}
);
private static final Method UNDEFINED_SCOPE = new Method(
"us",
Types.UNDEFINED,
new Type[]{}
);
private static final Method FLUSH_AND_POP = new Method(
"flushAndPop",
Types.VOID,
new Type[]{Types.PAGE_CONTEXT,Types.BODY_CONTENT}
);
private static final Method CLEAR_AND_POP = new Method(
"clearAndPop",
Types.VOID,
new Type[]{Types.PAGE_CONTEXT,Types.BODY_CONTENT}
);
public static final byte CF = (byte)207;
public static final byte _33 = (byte)51;
//private static final boolean ADD_C33 = false;
//private static final String SUB_CALL_UDF = "udfCall";
private static final String SUB_CALL_UDF = "_";
private static final int DEFAULT_VALUE = 3;
private final long version;
private final long lastModifed;
private final boolean _writeLog;
private final boolean suppressWSbeforeArg;
private final boolean output;
private final boolean returnValue;
public final boolean ignoreScopes;
//private final PageSource pageSource;
//private boolean isComponent;
//private boolean isInterface;
private ArrayList<IFunction> functions=new ArrayList<IFunction>();
private ArrayList<TagThread> threads=new ArrayList<TagThread>();
private Resource staticTextLocation;
private int off;
private int methodCount=0;
//private final Config config;
private boolean splitIfNecessary;
private TagCIObject _comp;
private String className; // following the pattern "or/susi/Sorglos"
private Config config;
private SourceCode sourceCode;
/**
* @param factory
* @param config
* @param sc SourceCode for this Page
* @param className name of the class produced (pattern: org.whatever.Susi)
* @param tc
* @param version
* @param lastModifed
* @param writeLog
* @param suppressWSbeforeArg
* @param dotNotationUpperCase
*/
public Page(Factory factory,Config config,SourceCode sc, TagCIObject tc,long version, long lastModifed, boolean writeLog, boolean suppressWSbeforeArg, boolean output,boolean returnValue, boolean ignoreScopes) {
super(factory);
this._comp=tc;
this.version=version;
this.lastModifed=lastModifed;
this._writeLog=writeLog;
this.suppressWSbeforeArg=suppressWSbeforeArg;
this.returnValue=returnValue;
this.ignoreScopes=ignoreScopes;
this.output=output;
//this.pageSource=ps;
this.config=config;
this.sourceCode=sc;
}
/**
* convert the Page Object to java bytecode
* @param className name of the genrated class (only necessary when Page object has no PageSource reference)
* @return
* @throws TransformerException
*/
public byte[] execute(String className) throws TransformerException {
// not exists in any case, so every usage must have a plan b for not existence
PageSource optionalPS=sourceCode instanceof PageSourceCode?((PageSourceCode)sourceCode).getPageSource():null;
List<LitString> keys=new ArrayList<LitString>();
ClassWriter cw = ASMUtil.getClassWriter();
ArrayList<String> imports = new ArrayList<String>();
getImports(imports, this);
// look for component if necessary
TagCIObject comp =getTagCFObject(null);
// in case we have a sub component
if(className==null) {
if(optionalPS==null) throw new IllegalArgumentException("when Page object has no PageSource, a className is necessary");
className=optionalPS.getClassName();
}
if(comp!=null) className=createSubClass(className,comp.getName(),sourceCode.getDialect());
className=className.replace('.', '/');
this.className=className;
// parent
String parent=lucee.runtime.Page.class.getName();//"lucee/runtime/Page";
if(isComponent(comp)) parent=ComponentPageImpl.class.getName();//"lucee/runtime/ComponentPage";
else if(isInterface(comp)) parent=InterfacePageImpl.class.getName();//"lucee/runtime/InterfacePage";
parent=parent.replace('.', '/');
cw.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL, className, null, parent, null);
if(optionalPS!=null) {
// we use full path when FD is enabled
String path=config.allowRequestTimeout()?
optionalPS.getRealpathWithVirtual():optionalPS.getPhyscalFile().getAbsolutePath();
cw.visitSource(path,null); // when adding more use ; as delimiter
//cw.visitSource(optionalPS.getPhyscalFile().getAbsolutePath(),
// "rel:"+optionalPS.getRealpathWithVirtual()); // when adding more use ; as delimiter
}
else {
//cw.visitSource("","rel:");
}
// static constructor
//GeneratorAdapter statConstrAdapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC,STATIC_CONSTRUCTOR,null,null,cw);
//StaticConstrBytecodeContext statConstr = null;//new BytecodeContext(null,null,this,externalizer,keys,cw,name,statConstrAdapter,STATIC_CONSTRUCTOR,writeLog(),suppressWSbeforeArg);
// constructor
GeneratorAdapter constrAdapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC,CONSTRUCTOR_PS,null,null,cw);
ConstrBytecodeContext constr = new ConstrBytecodeContext(optionalPS,this,keys,cw,className,constrAdapter,CONSTRUCTOR_PS,writeLog(),suppressWSbeforeArg,output,returnValue);
constrAdapter.loadThis();
Type t;
if(isComponent(comp)) {
t=Types.COMPONENT_PAGE_IMPL;
// extends
//Attribute attr = comp.getAttribute("extends");
//if(attr!=null) ExpressionUtil.writeOutSilent(attr.getValue(),constr, Expression.MODE_REF);
//else constrAdapter.push("");
constrAdapter.invokeConstructor(t, CONSTRUCTOR);
}
else if(isInterface(comp)){
t=Types.INTERFACE_PAGE_IMPL;
constrAdapter.invokeConstructor(t, CONSTRUCTOR);
}
else {
t=Types.PAGE;
constrAdapter.invokeConstructor(t, CONSTRUCTOR);
}
// call _init()
constrAdapter.visitVarInsn(Opcodes.ALOAD, 0);
constrAdapter.visitMethodInsn(Opcodes.INVOKEVIRTUAL, constr.getClassName(), "initKeys", "()V");
// private static ImportDefintion[] test=new ImportDefintion[]{...};
{
FieldVisitor fv = cw.visitField(Opcodes.ACC_PRIVATE + Opcodes.ACC_FINAL,
"imports", "[Llucee/runtime/component/ImportDefintion;", null, null);
fv.visitEnd();
constrAdapter.visitVarInsn(Opcodes.ALOAD, 0);
ArrayVisitor av=new ArrayVisitor();
av.visitBegin(constrAdapter,Types.IMPORT_DEFINITIONS,imports.size());
int index=0;
Iterator<String> it = imports.iterator();
while(it.hasNext()){
av.visitBeginItem(constrAdapter,index++);
constrAdapter.push(it.next());
ASMConstants.NULL(constrAdapter);
constrAdapter.invokeStatic(Types.IMPORT_DEFINITIONS_IMPL, ID_GET_INSTANCE);
av.visitEndItem(constrAdapter);
}
av.visitEnd();
constrAdapter.visitFieldInsn(Opcodes.PUTFIELD, className, "imports", "[Llucee/runtime/component/ImportDefintion;");
}
// getVersion
GeneratorAdapter adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL , VERSION, null, null, cw);
adapter.push(version);
adapter.returnValue();
adapter.endMethod();
// public ImportDefintion[] getImportDefintions()
if(imports.size()>0){
adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL , GET_IMPORT_DEFINITIONS, null, null, cw);
adapter.visitVarInsn(Opcodes.ALOAD, 0);
adapter.visitFieldInsn(Opcodes.GETFIELD, className, "imports", "[Llucee/runtime/component/ImportDefintion;");
adapter.returnValue();
adapter.endMethod();
}
else {
adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL , GET_IMPORT_DEFINITIONS, null, null, cw);
adapter.visitInsn(Opcodes.ICONST_0);
adapter.visitTypeInsn(Opcodes.ANEWARRAY, "lucee/runtime/component/ImportDefintion");
adapter.returnValue();
adapter.endMethod();
}
// getSourceLastModified
adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL , LAST_MOD, null, null, cw);
adapter.push(lastModifed);
adapter.returnValue();
adapter.endMethod();
// getCompileTime
adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL , COMPILE_TIME, null, null, cw);
adapter.push(System.currentTimeMillis());
adapter.returnValue();
adapter.endMethod();
// static consructor for component/interface
if(comp!=null) {
writeOutStaticConstructor(constr,keys,cw,comp,className);
}
// newInstance/initComponent/call
if(isComponent()) {
writeOutNewComponent(constr,keys,cw,comp,className);
writeOutInitComponent(constr,keys,cw,comp,className);
}
else if(isInterface()) {
writeOutNewInterface(constr,keys,cw,comp,className);
writeOutInitInterface(constr,keys,cw,comp,className);
}
else {
writeOutCall(constr,keys,cw,className);
}
// write UDFProperties to constructor
//writeUDFProperties(bc,funcs,pageType);
// udfCall
Function[] functions=getFunctions();
ConditionVisitor cv;
DecisionIntVisitor div;
// less/equal than 10 functions
if(isInterface()){}
else if(functions.length<=10) {
adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL , UDF_CALL, null, new Type[]{Types.THROWABLE}, cw);
BytecodeContext bc = new BytecodeContext(optionalPS,constr,this,keys,cw,className,adapter,UDF_CALL,writeLog(),suppressWSbeforeArg,output,returnValue);
if(functions.length==0){}
else if(functions.length==1){
ExpressionUtil.visitLine(bc,functions[0].getStart());
functions[0].getBody().writeOut(bc);
ExpressionUtil.visitLine(bc,functions[0].getEnd());
}
else writeOutUdfCallInner(bc,functions,0,functions.length);
adapter.visitInsn(Opcodes.ACONST_NULL);
adapter.returnValue();
adapter.endMethod();
}
// more than 10 functions
else {
adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL , UDF_CALL, null, new Type[]{Types.THROWABLE}, cw);
BytecodeContext bc = new BytecodeContext(optionalPS,constr,this,keys,cw,className,adapter,UDF_CALL,writeLog(),suppressWSbeforeArg,output,returnValue);
cv = new ConditionVisitor();
cv.visitBefore();
int count=0;
for(int i=0;i<functions.length;i+=10) {
cv.visitWhenBeforeExpr();
div=new DecisionIntVisitor();
div.visitBegin();
adapter.loadArg(2);
div.visitLT();
adapter.push(i+10);
div.visitEnd(bc);
cv.visitWhenAfterExprBeforeBody(bc);
adapter.visitVarInsn(Opcodes.ALOAD, 0);
adapter.visitVarInsn(Opcodes.ALOAD, 1);
adapter.visitVarInsn(Opcodes.ALOAD, 2);
adapter.visitVarInsn(Opcodes.ILOAD, 3);
adapter.visitMethodInsn(Opcodes.INVOKEVIRTUAL, className, createFunctionName(++count), "(Llucee/runtime/PageContext;Llucee/runtime/type/UDF;I)Ljava/lang/Object;");
adapter.visitInsn(Opcodes.ARETURN);//adapter.returnValue();
cv.visitWhenAfterBody(bc);
}
cv.visitAfter(bc);
adapter.visitInsn(Opcodes.ACONST_NULL);
adapter.returnValue();
adapter.endMethod();
count=0;
Method innerCall;
for(int i=0;i<functions.length;i+=10) {
innerCall = new Method(createFunctionName(++count),Types.OBJECT,new Type[]{Types.PAGE_CONTEXT, USER_DEFINED_FUNCTION, Types.INT_VALUE});
adapter = new GeneratorAdapter(Opcodes.ACC_PRIVATE+Opcodes.ACC_FINAL , innerCall, null, new Type[]{Types.THROWABLE}, cw);
writeOutUdfCallInner(new BytecodeContext(optionalPS,constr,this,keys,cw,className,adapter,innerCall,writeLog(),suppressWSbeforeArg,output,returnValue), functions, i, i+10>functions.length?functions.length:i+10);
adapter.visitInsn(Opcodes.ACONST_NULL);
adapter.returnValue();
adapter.endMethod();
}
}
// threadCall
TagThread[] threads=getThreads();
if(true) {
adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL , THREAD_CALL, null, new Type[]{Types.THROWABLE}, cw);
if(threads.length>0) writeOutThreadCallInner(new BytecodeContext(optionalPS,constr,this,keys,cw,className,adapter,THREAD_CALL,writeLog(),suppressWSbeforeArg,output,returnValue),threads,0,threads.length);
//adapter.visitInsn(Opcodes.ACONST_NULL);
adapter.returnValue();
adapter.endMethod();
}
// udfDefaultValue
// less/equal than 10 functions
if(isInterface()) {}
else if(functions.length<=10) {
adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL , UDF_DEFAULT_VALUE, null, new Type[]{Types.PAGE_EXCEPTION}, cw);
if(functions.length>0)
writeUdfDefaultValueInner(new BytecodeContext(optionalPS,constr,this,keys,cw,className,adapter,UDF_DEFAULT_VALUE,writeLog(),suppressWSbeforeArg,output,returnValue),functions,0,functions.length);
adapter.loadArg(DEFAULT_VALUE);
adapter.returnValue();
adapter.endMethod();
}
else {
adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL , UDF_DEFAULT_VALUE, null, new Type[]{Types.PAGE_EXCEPTION}, cw);
BytecodeContext bc = new BytecodeContext(optionalPS,constr,this,keys,cw,className,adapter,UDF_DEFAULT_VALUE,writeLog(),suppressWSbeforeArg,output,returnValue);
cv = new ConditionVisitor();
cv.visitBefore();
int count=0;
for(int i=0;i<functions.length;i+=10) {
cv.visitWhenBeforeExpr();
div=new DecisionIntVisitor();
div.visitBegin();
adapter.loadArg(1);
div.visitLT();
adapter.push(i+10);
div.visitEnd(bc);
cv.visitWhenAfterExprBeforeBody(bc);
adapter.visitVarInsn(Opcodes.ALOAD, 0);
adapter.visitVarInsn(Opcodes.ALOAD, 1);
adapter.visitVarInsn(Opcodes.ILOAD, 2);
adapter.visitVarInsn(Opcodes.ILOAD, 3);
adapter.visitVarInsn(Opcodes.ALOAD, 4);
adapter.visitMethodInsn(Opcodes.INVOKEVIRTUAL, className, "udfDefaultValue"+(++count), "(Llucee/runtime/PageContext;IILjava/lang/Object;)Ljava/lang/Object;");
adapter.visitInsn(Opcodes.ARETURN);//adapter.returnValue();
cv.visitWhenAfterBody(bc);
}
cv.visitAfter(bc);
adapter.visitInsn(Opcodes.ACONST_NULL);
adapter.returnValue();
adapter.endMethod();
count=0;
Method innerDefaultValue;
for(int i=0;i<functions.length;i+=10) {
innerDefaultValue = new Method("udfDefaultValue"+(++count),Types.OBJECT,new Type[]{Types.PAGE_CONTEXT, Types.INT_VALUE, Types.INT_VALUE,Types.OBJECT});
adapter = new GeneratorAdapter(Opcodes.ACC_PRIVATE+Opcodes.ACC_FINAL , innerDefaultValue, null, new Type[]{Types.PAGE_EXCEPTION}, cw);
writeUdfDefaultValueInner(new BytecodeContext(optionalPS,constr,this,keys,cw,className,adapter,innerDefaultValue,writeLog(),suppressWSbeforeArg,output,returnValue), functions, i, i+10>functions.length?functions.length:i+10);
adapter.loadArg(DEFAULT_VALUE);
//adapter.visitInsn(Opcodes.ACONST_NULL);
adapter.returnValue();
adapter.endMethod();
}
}
// CONSTRUCTOR
List<Data> udfProperties = constr.getUDFProperties();
Iterator<Data> it = udfProperties.iterator();
String udfpropsClassName = Types.UDF_PROPERTIES_ARRAY.toString();
// new UDFProperties Array
constrAdapter.visitVarInsn(Opcodes.ALOAD, 0);
constrAdapter.push(udfProperties.size());
constrAdapter.newArray(Types.UDF_PROPERTIES);
constrAdapter.visitFieldInsn(Opcodes.PUTFIELD, getClassName(), "udfs", udfpropsClassName);
// set item
Data data;
while(it.hasNext()) {
data = it.next();
constrAdapter.visitVarInsn(Opcodes.ALOAD, 0);
constrAdapter.visitFieldInsn(Opcodes.GETFIELD, constr.getClassName(), "udfs", Types.UDF_PROPERTIES_ARRAY.toString());
constrAdapter.push(data.arrayIndex);
data.function.createUDFProperties(constr,data.valueIndex,data.type);
constrAdapter.visitInsn(Opcodes.AASTORE);
}
//setPageSource(pageSource);
constrAdapter.visitVarInsn(Opcodes.ALOAD, 0);
constrAdapter.visitVarInsn(Opcodes.ALOAD, 1);
constrAdapter.invokeVirtual(t, SET_PAGE_SOURCE);
constrAdapter.returnValue();
constrAdapter.endMethod();
// INIT KEYS
{
GeneratorAdapter aInit = new GeneratorAdapter(Opcodes.ACC_PRIVATE+Opcodes.ACC_FINAL , INIT_KEYS, null, null, cw);
BytecodeContext bcInit = new BytecodeContext(optionalPS,constr,this,keys,cw,className,aInit,INIT_KEYS,writeLog(),suppressWSbeforeArg,output,returnValue);
registerFields(bcInit,keys);
aInit.returnValue();
aInit.endMethod();
}
// set field subs
FieldVisitor fv = cw.visitField(
Opcodes.ACC_PRIVATE + Opcodes.ACC_FINAL, "subs", "[Llucee/runtime/CIPage;", null, null);
fv.visitEnd();
// create sub components/interfaces
if(comp!=null && comp.isMain()) {
List<TagCIObject> subs = getSubs(null);
if(!ArrayUtil.isEmpty(subs)) {
Iterator<TagCIObject> _it = subs.iterator();
TagCIObject tc;
while(_it.hasNext()){
tc=_it.next();
tc.writeOut(this);
}
writeGetSubPages(cw,className,subs,sourceCode.getDialect());
}
}
return cw.toByteArray();
}
public static String createSubClass(String name, String subName, int dialect) {
if(!StringUtil.isEmpty(subName)) {
String suffix=(dialect==CFMLEngine.DIALECT_CFML?Constants.CFML_CLASS_SUFFIX:Constants.LUCEE_CLASS_SUFFIX);
subName=subName.toLowerCase();
if(name.endsWith(suffix)) name=name.substring(0,name.length()-3)+"$"+subName+suffix;
else name+="$"+subName;
}
return name;
}
private void writeGetSubPages(ClassWriter cw, String name, List<TagCIObject> subs, int dialect) {
//pageSource.getFullClassName().replace('.', '/');
GeneratorAdapter adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL , GET_SUB_PAGES, null, null, cw);
Label endIF = new Label();
//adapter.visitVarInsn(Opcodes.ALOAD, 0);
//adapter.visitFieldInsn(Opcodes.GETFIELD, name, "subs", "[Llucee/runtime/CIPage;");
//adapter.visitJumpInsn(Opcodes.IFNONNULL, endIF);
adapter.visitVarInsn(Opcodes.ALOAD, 0);
ArrayVisitor av=new ArrayVisitor();
av.visitBegin(adapter, Types.CI_PAGE, subs.size());
Iterator<TagCIObject> it = subs.iterator();
String className;
int index=0;
while(it.hasNext()){
TagCIObject ci = it.next();
av.visitBeginItem(adapter, index++);
className=createSubClass(name,ci.getName(),dialect);
//ASMConstants.NULL(adapter);
adapter.visitTypeInsn(Opcodes.NEW, className);
adapter.visitInsn(Opcodes.DUP);
adapter.visitVarInsn(Opcodes.ALOAD, 0);
adapter.visitMethodInsn(Opcodes.INVOKEVIRTUAL, name, "getPageSource", "()Llucee/runtime/PageSource;");
adapter.visitMethodInsn(Opcodes.INVOKESPECIAL, className, "<init>", "(Llucee/runtime/PageSource;)V");
av.visitEndItem(adapter);
}
av.visitEnd();
adapter.visitFieldInsn(Opcodes.PUTFIELD, name, "subs", "[Llucee/runtime/CIPage;");
//adapter.visitLabel(endIF);
adapter.visitVarInsn(Opcodes.ALOAD, 0);
adapter.visitFieldInsn(Opcodes.GETFIELD, name, "subs", "[Llucee/runtime/CIPage;");
adapter.returnValue();
adapter.endMethod();
}
public String getClassName() {
return className;
}
/**
* get the main component/interface from the Page
* @return
* @throws TransformerException
*/
private TagCIObject getTagCFObject(TagCIObject defaultValue) {
if(_comp!=null) return _comp;
// first look for main
Iterator<Statement> it = getStatements().iterator();
Statement s;
TagCIObject t,sub=null;
while(it.hasNext()) {
s=it.next();
if(s instanceof TagCIObject) {
t=(TagCIObject)s;
if(t.isMain()) return _comp=t;
else if(sub==null) sub=t;
}
}
if(sub!=null) return _comp=sub;
return defaultValue;
}
private List<TagCIObject> getSubs(TagCIObject[] defaultValue) {
Iterator<Statement> it = getStatements().iterator();
Statement s;
TagCIObject t;
List<TagCIObject> subs=null;
while(it.hasNext()) {
s=it.next();
if(s instanceof TagCIObject) {
t=(TagCIObject)s;
if(!t.isMain()) {
if(subs==null) subs=new ArrayList<TagCIObject>();
subs.add(t);
}
}
}
return subs;
}
private String createFunctionName(int i) {
return "udfCall"+Integer.toString(i, Character.MAX_RADIX);
}
public boolean writeLog() {
return _writeLog && !isInterface();
}
public static void registerFields(BytecodeContext bc, List<LitString> keys) throws TransformerException {
//if(keys.size()==0) return;
GeneratorAdapter ga = bc.getAdapter();
FieldVisitor fv = bc.getClassWriter().visitField(Opcodes.ACC_PRIVATE ,
"keys", Types.COLLECTION_KEY_ARRAY.toString(), null, null);
fv.visitEnd();
int index=0;
LitString value;
Iterator<LitString> it = keys.iterator();
ga.visitVarInsn(Opcodes.ALOAD, 0);
ga.push(keys.size());
ga.newArray(Types.COLLECTION_KEY);
while(it.hasNext()) {
value=it.next();
ga.dup();
ga.push(index++);
//value.setExternalize(false);
ExpressionUtil.writeOutSilent(value,bc, Expression.MODE_REF);
ga.invokeStatic(KEY_IMPL, KEY_INTERN);
ga.visitInsn(Opcodes.AASTORE);
}
ga.visitFieldInsn(Opcodes.PUTFIELD, bc.getClassName(), "keys", Types.COLLECTION_KEY_ARRAY.toString());
}
private void writeUdfDefaultValueInner(BytecodeContext bc, Function[] functions, int offset, int length) throws TransformerException {
GeneratorAdapter adapter = bc.getAdapter();
ConditionVisitor cv = new ConditionVisitor();
DecisionIntVisitor div;
cv.visitBefore();
for(int i=offset;i<length;i++) {
cv.visitWhenBeforeExpr();
div = new DecisionIntVisitor();
div.visitBegin();
adapter.loadArg(1);
div.visitEQ();
adapter.push(i);
div.visitEnd(bc);
cv.visitWhenAfterExprBeforeBody(bc);
writeOutFunctionDefaultValueInnerInner(bc, functions[i]);
cv.visitWhenAfterBody(bc);
}
cv.visitAfter(bc);
}
private void writeOutUdfCallInnerIf(BytecodeContext bc,Function[] functions, int offset, int length) throws TransformerException {
GeneratorAdapter adapter = bc.getAdapter();
ConditionVisitor cv=new ConditionVisitor();
DecisionIntVisitor div;
cv.visitBefore();
for(int i=offset;i<length;i++) {
cv.visitWhenBeforeExpr();
div=new DecisionIntVisitor();
div.visitBegin();
adapter.loadArg(2);
div.visitEQ();
adapter.push(i);
div.visitEnd(bc);
cv.visitWhenAfterExprBeforeBody(bc);
ExpressionUtil.visitLine(bc, functions[i].getStart());
functions[i].getBody().writeOut(bc);
ExpressionUtil.visitLine(bc, functions[i].getEnd());
cv.visitWhenAfterBody(bc);
}
cv.visitAfter(bc);
}
private void writeOutUdfCallInner(BytecodeContext bc,Function[] functions, int offset, int length) throws TransformerException {
NativeSwitch ns=new NativeSwitch(bc.getFactory(),2,NativeSwitch.ARG_REF,null,null);
for(int i=offset;i<length;i++) {
ns.addCase(i, functions[i].getBody(),functions[i].getStart(),functions[i].getEnd(),true);
}
ns._writeOut(bc);
}
private void writeOutThreadCallInner(BytecodeContext bc,TagThread[] threads, int offset, int length) throws TransformerException {
GeneratorAdapter adapter = bc.getAdapter();
ConditionVisitor cv=new ConditionVisitor();
DecisionIntVisitor div;
cv.visitBefore();
for(int i=offset;i<length;i++) {
cv.visitWhenBeforeExpr();
div=new DecisionIntVisitor();
div.visitBegin();
adapter.loadArg(1);
div.visitEQ();
adapter.push(i);
div.visitEnd(bc);
cv.visitWhenAfterExprBeforeBody(bc);
Body body = threads[i].getRealBody();
if(body!=null)body.writeOut(bc);
cv.visitWhenAfterBody(bc);
}
cv.visitAfter(bc);
}
private void writeOutStaticConstructor(ConstrBytecodeContext constr,List<LitString> keys, ClassWriter cw, TagCIObject component,String name) throws TransformerException {
//if(true) return;
final GeneratorAdapter adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL , STATIC_COMPONENT_CONSTR, null, new Type[]{Types.PAGE_EXCEPTION}, cw);
BytecodeContext bc=new BytecodeContext(null, constr,this,keys,cw,name,adapter,STATIC_COMPONENT_CONSTR,writeLog(),suppressWSbeforeArg,output,returnValue);
Label methodBegin=new Label();
Label methodEnd=new Label();
// Scope oldData=null;
final int oldData=adapter.newLocal(Types.VARIABLES);
ASMConstants.NULL(adapter);
adapter.storeLocal(oldData);
// push body
int localBC=adapter.newLocal(Types.BODY_CONTENT);
adapter.loadArg(0);
adapter.invokeVirtual(Types.PAGE_CONTEXT, PUSH_BODY);
adapter.storeLocal(localBC);
//int oldCheckArgs= pc.undefinedScope().setMode(Undefined.MODE_NO_LOCAL_AND_ARGUMENTS);
final int oldCheckArgs = adapter.newLocal(Types.INT_VALUE);
adapter.loadArg(0);
adapter.invokeVirtual(Types.PAGE_CONTEXT, UNDEFINED_SCOPE);
adapter.push(Undefined.MODE_NO_LOCAL_AND_ARGUMENTS);
adapter.invokeInterface(Types.UNDEFINED, SET_MODE);
adapter.storeLocal(oldCheckArgs);
TryCatchFinallyVisitor tcf=new TryCatchFinallyVisitor(new OnFinally() {
public void _writeOut(BytecodeContext bc) {
// undefined.setMode(oldMode);
adapter.loadArg(0);
adapter.invokeVirtual(Types.PAGE_CONTEXT, UNDEFINED_SCOPE);
adapter.loadLocal(oldCheckArgs,Types.INT_VALUE);
adapter.invokeInterface(Types.UNDEFINED, SET_MODE);
adapter.pop();
// c.afterCall(pc,_oldData);
//adapter.loadThis();
adapter.loadArg(1);
adapter.loadArg(0);
adapter.loadLocal(oldData); // old variables scope
adapter.invokeVirtual(Types.COMPONENT_IMPL, AFTER_STATIC_CONSTR);
}
},null);
tcf.visitTryBegin(bc);
// oldData=c.beforeCall(pc);
adapter.loadArg(1);
adapter.loadArg(0);
adapter.invokeVirtual(Types.COMPONENT_IMPL, BEFORE_STATIC_CONSTR);
adapter.storeLocal(oldData);
//ExpressionUtil.visitLine(bc, component.getStart());
List<StaticBody> list = component.getStaticBodies();
if(list!=null) {
writeOutConstrBody(bc,list,IFunction.PAGE_TYPE_COMPONENT);
}
//ExpressionUtil.visitLine(bc, component.getEnd());
int t = tcf.visitTryEndCatchBeging(bc);
// BodyContentUtil.flushAndPop(pc,bc);
adapter.loadArg(0);
adapter.loadLocal(localBC);
adapter.invokeStatic(Types.BODY_CONTENT_UTIL, FLUSH_AND_POP);
// throw Caster.toPageException(t);
adapter.loadLocal(t);
adapter.invokeStatic(Types.CASTER, TO_PAGE_EXCEPTION);
adapter.throwException();
tcf.visitCatchEnd(bc);
adapter.loadArg(0);
adapter.loadLocal(localBC);
adapter.invokeStatic(Types.BODY_CONTENT_UTIL, FLUSH_AND_POP);// TODO why does the body constuctor call clear and it works?
adapter.returnValue();
adapter.visitLabel(methodEnd);
adapter.endMethod();
}
private void writeOutConstrBody(BytecodeContext bc, List<StaticBody> bodies, int pageType) throws TransformerException {
// get and remove all functions from body
List<IFunction> funcs=new ArrayList<IFunction>();
Iterator<StaticBody> it = bodies.iterator();
while(it.hasNext()){
extractFunctions(bc,it.next(),funcs,pageType);
}
writeUDFProperties(bc,funcs,pageType);
it = bodies.iterator();
while(it.hasNext()){
BodyBase.writeOut(bc,it.next());
}
}
private void writeOutInitComponent(ConstrBytecodeContext constr,List<LitString> keys, ClassWriter cw, Tag component,String name) throws TransformerException {
final GeneratorAdapter adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL , INIT_COMPONENT3, null, new Type[]{Types.PAGE_EXCEPTION}, cw);
BytecodeContext bc=new BytecodeContext(null, constr,this,keys,cw,name,adapter,INIT_COMPONENT3,writeLog(),suppressWSbeforeArg,output,returnValue);
Label methodBegin=new Label();
Label methodEnd=new Label();
adapter.visitLocalVariable("this", "L"+name+";", null, methodBegin, methodEnd, 0);
adapter.visitLabel(methodBegin);
// Scope oldData=null;
final int oldData=adapter.newLocal(Types.VARIABLES);
ASMConstants.NULL(adapter);
adapter.storeLocal(oldData);
int localBC=adapter.newLocal(Types.BODY_CONTENT);
ConditionVisitor cv=new ConditionVisitor();
cv.visitBefore();
cv.visitWhenBeforeExpr();
adapter.loadArg(1);
adapter.invokeVirtual(Types.COMPONENT_IMPL, GET_OUTPUT);
cv.visitWhenAfterExprBeforeBody(bc);
ASMConstants.NULL(adapter);
cv.visitWhenAfterBody(bc);
cv.visitOtherviseBeforeBody();
adapter.loadArg(0);
adapter.invokeVirtual(Types.PAGE_CONTEXT, PUSH_BODY);
cv.visitOtherviseAfterBody();
cv.visitAfter(bc);
adapter.storeLocal(localBC);
// c.init(pc,this);
adapter.loadArg(1);
adapter.loadArg(0);
adapter.loadThis();
adapter.loadArg(2);
//adapter.visitVarInsn(Opcodes.ALOAD, 0);
adapter.invokeVirtual(Types.COMPONENT_IMPL, INIT_COMPONENT);
// return when executeConstr is false
adapter.loadArg(2);
Label afterIf = new Label();
adapter.visitJumpInsn(Opcodes.IFNE, afterIf);
adapter.loadArg(0);
adapter.loadLocal(localBC);
adapter.invokeStatic(Types.BODY_CONTENT_UTIL, CLEAR_AND_POP);
adapter.visitInsn(Opcodes.RETURN);
adapter.visitLabel(afterIf);
//int oldCheckArgs= pc.undefinedScope().setMode(Undefined.MODE_NO_LOCAL_AND_ARGUMENTS);
final int oldCheckArgs = adapter.newLocal(Types.INT_VALUE);
adapter.loadArg(0);
adapter.invokeVirtual(Types.PAGE_CONTEXT, UNDEFINED_SCOPE);
adapter.push(Undefined.MODE_NO_LOCAL_AND_ARGUMENTS);
adapter.invokeInterface(Types.UNDEFINED, SET_MODE);
adapter.storeLocal(oldCheckArgs);
TryCatchFinallyVisitor tcf=new TryCatchFinallyVisitor(new OnFinally() {
public void _writeOut(BytecodeContext bc) {
// undefined.setMode(oldMode);
adapter.loadArg(0);
adapter.invokeVirtual(Types.PAGE_CONTEXT, UNDEFINED_SCOPE);
adapter.loadLocal(oldCheckArgs,Types.INT_VALUE);
adapter.invokeInterface(Types.UNDEFINED, SET_MODE);
adapter.pop();
// c.afterCall(pc,_oldData);
adapter.loadArg(1);
adapter.loadArg(0);
adapter.loadLocal(oldData);
adapter.invokeVirtual(Types.COMPONENT_IMPL, AFTER_CALL);
}
},null);
tcf.visitTryBegin(bc);
// oldData=c.beforeCall(pc);
adapter.loadArg(1);
adapter.loadArg(0);
adapter.invokeVirtual(Types.COMPONENT_IMPL, BEFORE_CALL);
adapter.storeLocal(oldData);
ExpressionUtil.visitLine(bc, component.getStart());
writeOutCallBody(bc,component.getBody(),IFunction.PAGE_TYPE_COMPONENT);
ExpressionUtil.visitLine(bc, component.getEnd());
int t = tcf.visitTryEndCatchBeging(bc);
// BodyContentUtil.flushAndPop(pc,bc);
adapter.loadArg(0);
adapter.loadLocal(localBC);
adapter.invokeStatic(Types.BODY_CONTENT_UTIL, FLUSH_AND_POP);
// throw Caster.toPageException(t);
adapter.loadLocal(t);
adapter.invokeStatic(Types.CASTER, TO_PAGE_EXCEPTION);
adapter.throwException();
tcf.visitCatchEnd(bc);
adapter.loadArg(0);
adapter.loadLocal(localBC);
adapter.invokeStatic(Types.BODY_CONTENT_UTIL, CLEAR_AND_POP);
adapter.returnValue();
adapter.visitLabel(methodEnd);
adapter.endMethod();
}
private void writeOutInitInterface(ConstrBytecodeContext constr,List<LitString> keys, ClassWriter cw, Tag interf, String name) throws TransformerException {
GeneratorAdapter adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL , INIT_INTERFACE, null, new Type[]{Types.PAGE_EXCEPTION}, cw);
BytecodeContext bc=new BytecodeContext(null,constr,this,keys,cw,name,adapter,INIT_INTERFACE,writeLog(),suppressWSbeforeArg,output,returnValue);
Label methodBegin=new Label();
Label methodEnd=new Label();
adapter.visitLocalVariable("this", "L"+name+";", null, methodBegin, methodEnd, 0);
adapter.visitLabel(methodBegin);
ExpressionUtil.visitLine(bc, interf.getStart());
writeOutCallBody(bc,interf.getBody(),IFunction.PAGE_TYPE_INTERFACE);
ExpressionUtil.visitLine(bc, interf.getEnd());
adapter.returnValue();
adapter.visitLabel(methodEnd);
adapter.endMethod();
}
private void writeOutFunctionDefaultValueInnerInner(BytecodeContext bc, Function function) throws TransformerException {
GeneratorAdapter adapter = bc.getAdapter();
List<Argument> args = function.getArguments();
if(args.size()==0) {
adapter.loadArg(DEFAULT_VALUE);
adapter.returnValue();
return;
}
Iterator<Argument> it = args.iterator();
Argument arg;
ConditionVisitor cv=new ConditionVisitor();
DecisionIntVisitor div;
cv.visitBefore();
int count=0;
while(it.hasNext()) {
arg=it.next();
cv.visitWhenBeforeExpr();
div=new DecisionIntVisitor();
div.visitBegin();
adapter.loadArg(2);
div.visitEQ();
adapter.push(count++);
div.visitEnd(bc);
cv.visitWhenAfterExprBeforeBody(bc);
Expression defaultValue = arg.getDefaultValue();
if(defaultValue!=null) {
/*if(defaultValue instanceof Null) {
adapter.invokeStatic(NULL, GET_INSTANCE);
}
else*/
defaultValue.writeOut(bc, Expression.MODE_REF);
}
else
adapter.loadArg(DEFAULT_VALUE);
//adapter.visitInsn(Opcodes.ACONST_NULL);
adapter.returnValue();
cv.visitWhenAfterBody(bc);
}
cv.visitOtherviseBeforeBody();
//adapter.visitInsn(ACONST_NULL);
//adapter.returnValue();
cv.visitOtherviseAfterBody();
cv.visitAfter(bc);
}
private Function[] getFunctions() {
Function[] funcs=new Function[functions.size()];
Iterator it = functions.iterator();
int count=0;
while(it.hasNext()) {
funcs[count++]=(Function) it.next();
}
return funcs;
}
private TagThread[] getThreads() {
TagThread[] threads=new TagThread[this.threads.size()];
Iterator it = this.threads.iterator();
int count=0;
while(it.hasNext()) {
threads[count++]=(TagThread) it.next();
}
return threads;
}
public void _writeOut(BytecodeContext bc) throws TransformerException {
}
private void writeOutNewComponent(ConstrBytecodeContext constr, List<LitString> keys,ClassWriter cw, Tag component,String name) throws TransformerException {
GeneratorAdapter adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL , NEW_COMPONENT_IMPL_INSTANCE, null, new Type[]{Types.PAGE_EXCEPTION}, cw);
BytecodeContext bc=new BytecodeContext(null,constr,this,keys,cw,name,adapter,NEW_COMPONENT_IMPL_INSTANCE,writeLog(),suppressWSbeforeArg,output,returnValue);
Label methodBegin=new Label();
Label methodEnd=new Label();
adapter.visitLocalVariable("this", "L"+name+";", null, methodBegin, methodEnd, 0);
ExpressionUtil.visitLine(bc, component.getStart());
adapter.visitLabel(methodBegin);
int comp=adapter.newLocal(Types.COMPONENT_IMPL);
adapter.newInstance(Types.COMPONENT_IMPL);
adapter.dup();
Attribute attr;
// ComponentPage
adapter.visitVarInsn(Opcodes.ALOAD, 0);
adapter.checkCast(Types.COMPONENT_PAGE_IMPL);
// !!! also check CFMLScriptTransformer.addMetaData if you do any change here !!!
// Output
attr = component.removeAttribute("output");
if(attr!=null) {
ExpressionUtil.writeOutSilent(attr.getValue(),bc, Expression.MODE_REF);
}
else ASMConstants.NULL(adapter);
// synchronized
attr = component.removeAttribute("synchronized");
if(attr!=null) ExpressionUtil.writeOutSilent(attr.getValue(),bc, Expression.MODE_VALUE);
else adapter.push(false);
// extends
attr = component.removeAttribute("extends");
if(attr!=null) ExpressionUtil.writeOutSilent(attr.getValue(),bc, Expression.MODE_REF);
else adapter.push("");
// implements
attr = component.removeAttribute("implements");
if(attr!=null) ExpressionUtil.writeOutSilent(attr.getValue(),bc, Expression.MODE_REF);
else adapter.push("");
// hint
attr = component.removeAttribute("hint");
if(attr!=null) {
Expression value = attr.getValue();
if(!(value instanceof Literal)){
value=bc.getFactory().createLitString("[runtime expression]");
}
ExpressionUtil.writeOutSilent(value,bc, Expression.MODE_REF);
}
else adapter.push("");
// dspName
attr = component.removeAttribute("displayname");
if(attr==null) attr=component.getAttribute("display");
if(attr!=null) ExpressionUtil.writeOutSilent(attr.getValue(),bc, Expression.MODE_REF);
else adapter.push("");
// callpath
adapter.visitVarInsn(Opcodes.ALOAD, 2);
// realpath
adapter.visitVarInsn(Opcodes.ILOAD, 3);
// style
attr = component.removeAttribute("style");
if(attr!=null) ExpressionUtil.writeOutSilent(attr.getValue(),bc, Expression.MODE_REF);
else adapter.push("");
// persistent
attr = component.removeAttribute("persistent");
boolean persistent=false;
if(attr!=null) {
persistent=ASMUtil.toBoolean(attr,component.getStart()).booleanValue();
}
// accessors
attr = component.removeAttribute("accessors");
boolean accessors=false;
if(attr!=null) {
accessors=ASMUtil.toBoolean(attr,component.getStart()).booleanValue();
}
// modifier
attr = component.removeAttribute("modifier");
int modifiers=Component.MODIFIER_NONE;
if(attr!=null) {
// type already evaluated in evaluator
LitString ls=(LitString) component.getFactory().toExprString(attr.getValue());
modifiers = ComponentUtil.toModifier(ls.getString(),lucee.runtime.Component.MODIFIER_NONE,lucee.runtime.Component.MODIFIER_NONE);
}
adapter.push(persistent);
adapter.push(accessors);
adapter.push(modifiers);
adapter.visitVarInsn(Opcodes.ILOAD, 4);
//adapter.visitVarInsn(Opcodes.ALOAD, 4);
createMetaDataStruct(bc,component.getAttributes(),component.getMetaData());
adapter.invokeConstructor(Types.COMPONENT_IMPL, CONSTR_COMPONENT_IMPL15);
adapter.storeLocal(comp);
//Component Impl(ComponentPage componentPage,boolean output, String extend, String hint, String dspName)
// initComponent(pc,c);
adapter.visitVarInsn(Opcodes.ALOAD, 0);
adapter.loadArg(0);
adapter.loadLocal(comp);
adapter.loadArg(4);
adapter.invokeVirtual(Types.COMPONENT_PAGE_IMPL, INIT_COMPONENT3);
adapter.visitLabel(methodEnd);
// return component;
adapter.loadLocal(comp);
adapter.returnValue();
//ExpressionUtil.visitLine(adapter, component.getEndLine());
adapter.endMethod();
}
private void writeOutNewInterface(ConstrBytecodeContext constr,List<LitString> keys,ClassWriter cw, Tag interf, String name) throws TransformerException {
GeneratorAdapter adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL , NEW_INTERFACE_IMPL_INSTANCE, null, new Type[]{Types.PAGE_EXCEPTION}, cw);
BytecodeContext bc=new BytecodeContext(null,constr,this,keys,cw,name,adapter,NEW_INTERFACE_IMPL_INSTANCE,writeLog(),suppressWSbeforeArg,output,returnValue);
Label methodBegin=new Label();
Label methodEnd=new Label();
adapter.visitLocalVariable("this", "L"+name+";", null, methodBegin, methodEnd, 0);
ExpressionUtil.visitLine(bc, interf.getStart());
adapter.visitLabel(methodBegin);
//ExpressionUtil.visitLine(adapter, interf.getStartLine());
int comp=adapter.newLocal(Types.INTERFACE_IMPL);
adapter.newInstance(Types.INTERFACE_IMPL);
adapter.dup();
// PageContext
adapter.visitVarInsn(Opcodes.ALOAD, 1);
// Interface Page
adapter.visitVarInsn(Opcodes.ALOAD, 0);
adapter.checkCast(Types.INTERFACE_PAGE_IMPL);
// extened
Attribute attr = interf.removeAttribute("extends");
if(attr!=null) ExpressionUtil.writeOutSilent(attr.getValue(),bc, Expression.MODE_REF);
else adapter.push("");
// hint
attr = interf.removeAttribute("hint");
if(attr!=null) ExpressionUtil.writeOutSilent(attr.getValue(),bc, Expression.MODE_REF);
else adapter.push("");
// dspName
attr = interf.removeAttribute("displayname");
if(attr==null) attr=interf.getAttribute("display");
if(attr!=null) ExpressionUtil.writeOutSilent(attr.getValue(),bc, Expression.MODE_REF);
else adapter.push("");
// callpath
adapter.visitVarInsn(Opcodes.ALOAD, 2);
// relpath
adapter.visitVarInsn(Opcodes.ILOAD, 3);
// interface udfs
//adapter.visitVarInsn(Opcodes.ALOAD, 3);
createMetaDataStruct(bc,interf.getAttributes(),interf.getMetaData());
adapter.invokeConstructor(Types.INTERFACE_IMPL, CONSTR_INTERFACE_IMPL8);
adapter.storeLocal(comp);
// initInterface(pc,c);
adapter.visitVarInsn(Opcodes.ALOAD, 0);
//adapter.loadArg(0);
adapter.loadLocal(comp);
adapter.invokeVirtual(Types.INTERFACE_PAGE_IMPL, INIT_INTERFACE);
adapter.visitLabel(methodEnd);
// return interface;
adapter.loadLocal(comp);
adapter.returnValue();
//ExpressionUtil.visitLine(adapter, interf.getEndLine());
adapter.endMethod();
}
public static boolean hasMetaDataStruct(Map attrs, Map meta) {
if((attrs==null || attrs.size()==0) && (meta==null || meta.size()==0)){
return false;
}
return true;
}
public static void createMetaDataStruct(BytecodeContext bc, Map attrs, Map meta) throws TransformerException {
GeneratorAdapter adapter = bc.getAdapter();
if((attrs==null || attrs.size()==0) && (meta==null || meta.size()==0)){
ASMConstants.NULL(bc.getAdapter());
bc.getAdapter().cast(Types.OBJECT,Types.STRUCT_IMPL);
return;
}
int sct=adapter.newLocal(Types.STRUCT_IMPL);
adapter.newInstance(Types.STRUCT_IMPL);
adapter.dup();
adapter.invokeConstructor(Types.STRUCT_IMPL, INIT_STRUCT_IMPL);
adapter.storeLocal(sct);
if(meta!=null) {
_createMetaDataStruct(bc,adapter,sct,meta);
}
if(attrs!=null) {
_createMetaDataStruct(bc,adapter,sct,attrs);
}
adapter.loadLocal(sct);
}
private static void _createMetaDataStruct(BytecodeContext bc, GeneratorAdapter adapter, int sct, Map attrs) throws TransformerException {
Attribute attr;
Iterator it = attrs.entrySet().iterator();
Entry entry;
while(it.hasNext()){
entry = (Map.Entry)it.next();
attr=(Attribute) entry.getValue();
adapter.loadLocal(sct);
adapter.push(attr.getName());
if(attr.getValue() instanceof Literal)
ExpressionUtil.writeOutSilent(attr.getValue(),bc,Expression.MODE_REF);
else
adapter.push("[runtime expression]");
adapter.invokeVirtual(Types.STRUCT_IMPL, SET_EL);
adapter.pop();
}
}
private void writeOutCall(ConstrBytecodeContext constr,List<LitString> keys,ClassWriter cw, String name) throws TransformerException {
//GeneratorAdapter adapter = bc.getAdapter();
GeneratorAdapter adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL , CALL1, null, new Type[]{Types.THROWABLE}, cw);
Label methodBegin=new Label();
Label methodEnd=new Label();
adapter.visitLocalVariable("this", "L"+name+";", null, methodBegin, methodEnd, 0);
adapter.visitLabel(methodBegin);
writeOutCallBody(
new BytecodeContext(null, constr,this,keys,cw,name,adapter,CALL1,writeLog(),suppressWSbeforeArg,output,returnValue)
,this
,IFunction.PAGE_TYPE_REGULAR);
adapter.visitLabel(methodEnd);
adapter.returnValue();
adapter.endMethod();
}
private void writeOutCallBody(BytecodeContext bc,Body body,int pageType) throws TransformerException {
List<IFunction> funcs=new ArrayList<IFunction>();
extractFunctions(bc,body,funcs,pageType);
writeUDFProperties(bc,funcs,pageType);
if(pageType==IFunction.PAGE_TYPE_COMPONENT) {
GeneratorAdapter adapter = bc.getAdapter();
adapter.loadArg(1);
adapter.loadArg(0);
adapter.visitVarInsn(Opcodes.ALOAD, 0);
adapter.invokeVirtual(Types.COMPONENT_IMPL, CHECK_INTERFACE);
}
if(pageType!=IFunction.PAGE_TYPE_INTERFACE){
int rtn=-1;
if(bc.returnValue()) {
rtn = bc.getAdapter().newLocal(Types.OBJECT);
bc.setReturn(rtn);
// make sure we have a value
ASMConstants.NULL(bc.getAdapter());
bc.getAdapter().storeLocal(rtn);
}
BodyBase.writeOut(bc,body);
if(rtn!=-1)bc.getAdapter().loadLocal(rtn);
else ASMConstants.NULL(bc.getAdapter());
}
}
private void writeUDFProperties(BytecodeContext bc, List<IFunction> funcs, int pageType) throws TransformerException {
/*String className = Types.UDF_PROPERTIES_ARRAY.toString();
BytecodeContext constr = bc.getConstructor();
GeneratorAdapter cga = constr.getAdapter();
// new UDFProperties Array
cga.visitVarInsn(Opcodes.ALOAD, 0);
cga.push(1111);
cga.newArray(Types.UDF_PROPERTIES);
cga.visitFieldInsn(Opcodes.PUTFIELD, bc.getClassName(), "udfs", className);
*/
// set items
Iterator<IFunction> it = funcs.iterator();
while(it.hasNext()){
it.next().writeOut(bc, pageType);
}
}
private static void getImports(List<String> list,Body body) throws TransformerException {
if(ASMUtil.isEmpty(body)) return;
Statement stat;
List stats = body.getStatements();
int len=stats.size();
for(int i=0;i<len;i++) {
stat = (Statement)stats.get(i);
// IFunction
if(stat instanceof TagImport && !StringUtil.isEmpty(((TagImport)stat).getPath(),true)) {
ImportDefintion id = ImportDefintionImpl.getInstance(((TagImport) stat).getPath(), null);
if(id!=null && (!list.contains(id.toString()) && !list.contains(id.getPackage()+".*"))){
list.add(id.toString());
}
stats.remove(i);
len--;
i--;
}
else if(stat instanceof HasBody) getImports(list, ((HasBody)stat).getBody());
else if(stat instanceof HasBodies) {
Body[] bodies=((HasBodies)stat).getBodies();
for(int y=0;y<bodies.length;y++) {
getImports(list,bodies[y]);
}
}
}
}
private static void extractFunctions(BytecodeContext bc, Body body, List<IFunction> funcs, int pageType) throws TransformerException {
if(ASMUtil.isEmpty(body)) return ;
Statement stat;
List<Statement> stats = body.getStatements();
int len=stats.size();
for(int i=0;i<len;i++) {
stat = stats.get(i);
// IFunction
if(stat instanceof IFunction) {
funcs.add((IFunction) stat);
//((IFunction)stat).writeOut(bc,pageType);
stats.remove(i);
len--;
i--;
}
else if(stat instanceof HasBody) extractFunctions(bc, ((HasBody)stat).getBody(), funcs, pageType);
else if(stat instanceof HasBodies) {
Body[] bodies=((HasBodies)stat).getBodies();
for(int y=0;y<bodies.length;y++) {
extractFunctions(bc,bodies[y] , funcs, pageType);
}
}
}
}
/**
* @return if it is a component
*/
public boolean isComponent() {
return isComponent(null);
/*TagCFObject comp = getTagCFObject(null);
if(comp!=null && comp.getTagLibTag().getTagClassName().equals("lucee.runtime.tag.Component")) return true;
return false;
*/
}
/**
* @return if it is a interface
*/
public boolean isInterface() {
return isInterface(null);
/*TagCFObject comp = getTagCFObject(null);
if(comp!=null && comp.getTagLibTag().getTagClassName().equals("lucee.runtime.tag.Interface")) return true;
return false;*/
}
public boolean isComponent(TagCIObject cio) {
if(cio==null)cio=getTagCFObject(null);
return cio instanceof TagComponent;
}
/**
* @return if it is a interface
*/
public boolean isInterface(TagCIObject cio) {
if(cio==null)cio=getTagCFObject(null);
return cio instanceof TagInterface;
}
public boolean isPage() {
return getTagCFObject(null)==null;
}
/**
* @return the lastModifed
*/
public long getLastModifed() {
return lastModifed;
}
public int[] addFunction(IFunction function) {
int[] indexes=new int[2];
Iterator<IFunction> it = functions.iterator();
while(it.hasNext()){
if(it.next() instanceof FunctionImpl)indexes[IFunction.ARRAY_INDEX]++;
}
indexes[IFunction.VALUE_INDEX]=functions.size();
functions.add(function);
return indexes;
}
public int addThread(TagThread thread) {
threads.add(thread);
return threads.size()-1;
}
public static byte[] setSourceLastModified(byte[] barr, long lastModified) {
ClassReader cr = new ClassReader(barr);
ClassWriter cw = ASMUtil.getClassWriter();
ClassVisitor ca = new SourceLastModifiedClassAdapter(cw,lastModified);
cr.accept(ca, 0);
return cw.toByteArray();
}
/**
* return null if not possible to register
* @param bc
* @param str
* @return
* @throws IOException
*/
public Range registerString(BytecodeContext bc, String str) throws IOException {
boolean append=true;
if(staticTextLocation==null) {
if(bc.getPageSource()==null) return null;
PageSource ps = bc.getPageSource();
Mapping m = ps.getMapping();
staticTextLocation=m.getClassRootDirectory();
staticTextLocation.mkdirs();
staticTextLocation=staticTextLocation.getRealResource(ps.getClassName().replace('.', '/')+".txt");
if(staticTextLocation.exists()) append=false;
else staticTextLocation.createFile(true);
off=0;
}
IOUtil.write(staticTextLocation, str, CharsetUtil.UTF8, append);
Range r = new Range(off,str.length());
off+=str.length();
return r;
}
public int getMethodCount() {
return ++methodCount;
}
public SourceCode getSourceCode() {
return sourceCode;
}
public void setSplitIfNecessary(boolean splitIfNecessary) {
this.splitIfNecessary=splitIfNecessary;
}
public boolean getSplitIfNecessary() {
return splitIfNecessary;
}
public boolean getSupressWSbeforeArg() {
return suppressWSbeforeArg;
}
public boolean getOutput() {
return output;
}
public boolean returnValue() {
return returnValue;
}
public Config getConfig() {
return config;
}
public void doFinalize(BytecodeContext bc) {
ExpressionUtil.visitLine(bc, getEnd());
}
}
class SourceLastModifiedClassAdapter extends ClassVisitor {
private long lastModified;
public SourceLastModifiedClassAdapter(ClassWriter cw, long lastModified) {
super(Opcodes.ASM4,cw);
this.lastModified=lastModified;
}
public MethodVisitor visitMethod(int access,String name, String desc, String signature, String[] exceptions) {
if(!name.equals("getSourceLastModified"))return super.visitMethod(access,name, desc, signature, exceptions);
MethodVisitor mv = cv.visitMethod(access,name, desc, signature, exceptions);
mv.visitCode();
mv.visitLdcInsn(Long.valueOf(lastModified));
mv.visitInsn(Opcodes.LRETURN);
mv.visitEnd();
return mv;
}
}