Projects >> railo >>5dd1ac987286e26d179b01bec9f0e647040a5931

Chunk
Conflicting content
<<<<<<< HEAD
package railo.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 org.objectweb.asm.ClassAdapter;
import org.objectweb.asm.ClassReader;
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;

import railo.commons.io.res.Resource;
import railo.commons.lang.NumberUtil;
import railo.commons.lang.StringUtil;
import railo.runtime.component.ImportDefintion;
import railo.runtime.component.ImportDefintionImpl;
import railo.runtime.exp.TemplateException;
import railo.runtime.type.KeyImpl;
import railo.runtime.type.StructImpl;
import railo.runtime.type.UDF;
import railo.runtime.type.scope.Undefined;
import railo.runtime.type.util.KeyConstants;
import railo.transformer.bytecode.expression.Expression;
import railo.transformer.bytecode.extern.StringExternalizerWriter;
import railo.transformer.bytecode.literal.LitString;
import railo.transformer.bytecode.statement.Argument;
import railo.transformer.bytecode.statement.HasBodies;
import railo.transformer.bytecode.statement.HasBody;
import railo.transformer.bytecode.statement.IFunction;
import railo.transformer.bytecode.statement.NativeSwitch;
import railo.transformer.bytecode.statement.tag.Attribute;
import railo.transformer.bytecode.statement.tag.Tag;
import railo.transformer.bytecode.statement.tag.TagImport;
import railo.transformer.bytecode.statement.tag.TagThread;
import railo.transformer.bytecode.statement.udf.Function;
import railo.transformer.bytecode.statement.udf.FunctionImpl;
import railo.transformer.bytecode.util.ASMConstants;
import railo.transformer.bytecode.util.ASMUtil;
import railo.transformer.bytecode.util.ExpressionUtil;
import railo.transformer.bytecode.util.Types;
import railo.transformer.bytecode.visitor.ArrayVisitor;
import railo.transformer.bytecode.visitor.ConditionVisitor;
	        }
			}
			new Type[]{Types.PAGE_CONTEXT}
    		);

			Types.INT_VALUE,
		});
			new Type[]{}
    // int getVersion()
    		);
// newInstance/initComponent/call
				div.visitBegin();
        if(isComponent()) {
        	Tag component=getComponent();
    private final static Method VERSION = new Method(
			"getVersion",
import railo.transformer.bytecode.visitor.DecisionIntVisitor;
import railo.transformer.bytecode.visitor.OnFinally;
import railo.transformer.bytecode.visitor.TryCatchFinallyVisitor;

/**
 * represent a single Page like "index.cfm"
 */
public final class Page extends BodyBase {


	public void doFinalize(BytecodeContext bc) {
		ExpressionUtil.visitLine(bc, getEndLine()+1);
	}

	private static final Type KEY_IMPL = Type.getType(KeyImpl.class);
	private static final Type KEY_CONSTANTS = Type.getType(KeyConstants.class);
	private static final Method KEY_INIT = new Method(
			"init",
			Types.COLLECTION_KEY,
			new Type[]{Types.STRING}
    		);
	private 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  ()V");
	//public final static Method CONSTRUCTOR = Method.getMethod("void  ()V");

	private static final Method CONSTRUCTOR = new Method(
			"",
			Types.VOID,
			new Type[]{}//
    		);
	private static final Method CONSTRUCTOR_PS = new Method(
			"",
			Types.VOID,
			new Type[]{Types.PAGE_SOURCE}//
    		);

    public static final Type STRUCT_IMPL = Type.getType(StructImpl.class);
	private static final Method INIT_STRUCT_IMPL = new Method(
			"",
			Types.VOID,
			new Type[]{}
    		);

	
    // void call (railo.runtime.PageContext)
    private final static Method CALL = new Method(
			"call",
			Types.VOID,
			new Type[]{Types.PAGE_CONTEXT}
    		);
    
    /*/ void _try ()
    private final static Method TRY = new Method(
			"_try",
			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[]{}
    		);
    
    // 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}
			);

	private static final Method NEW_COMPONENT_IMPL_INSTANCE = new Method(
			"newInstance",
			Types.COMPONENT_IMPL,
			new Type[]{Types.PAGE_CONTEXT,Types.STRING,Types.BOOLEAN_VALUE}
    		);
	
	private static final Method NEW_INTERFACE_IMPL_INSTANCE = new Method(
			"newInstance",
			Types.INTERFACE_IMPL,
			new Type[]{Types.STRING,Types.BOOLEAN_VALUE,Types.MAP}
    		);
	
	

	

	// void init(PageContext pc,Component Impl c) throws PageException
	private static final Method INIT_COMPONENT = new Method(
			"initComponent",
			Types.VOID,
			new Type[]{Types.PAGE_CONTEXT,Types.COMPONENT_IMPL}
    		);
	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_IMPL = new Method(
			"",
			Types.VOID,
			new Type[]{
					Types.INTERFACE_PAGE,
						Types.STRING, // extends
						Types.STRING, // hind
						Types.STRING, // display
						Types.STRING, // callpath
						Types.BOOLEAN_VALUE, // realpath
						Types.MAP, //interfaceudfs
						Types.MAP // meta
					}
    		);
	
	
	//void init(PageContext pageContext,ComponentPage componentPage)
	private static final Method INIT = new Method(
			"init",
			Types.VOID,
			new Type[]{Types.PAGE_CONTEXT,Types.COMPONENT_PAGE}
    		);
	
	private static final Method CHECK_INTERFACE = new Method(
			"checkInterface",
			Types.VOID,
			new Type[]{Types.PAGE_CONTEXT,Types.COMPONENT_PAGE}
    		);
	
	

	// 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,
	private static final Method TO_PAGE_EXCEPTION = new Method(
			"toPageException",
			Types.PAGE_EXCEPTION,
			new Type[]{Types.THROWABLE});
	
	
	// boolean unsetSilent()
	/*private static final Method UNSET_SILENT = new Method(
			"unsetSilent",
			Types.BOOLEAN_VALUE,
			new Type[]{}
    		);*/

	// void afterCall(PageContext pc, Scope parent)
	private static final Method AFTER_CALL = new Method(
			"afterConstructor",
			Types.VOID,
			new Type[]{Types.PAGE_CONTEXT,Types.VARIABLES}
    		);

	// ComponentImpl(ComponentPage,boolean, String, String, String) NS==No Style
	
	
	// Component Impl(ComponentPage,boolean, String, String, String, String) WS==With Style
	private static final Method CONSTR_COMPONENT_IMPL = new Method(
			"",
			Types.VOID,
			new Type[]{
					Types.COMPONENT_PAGE,
					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,
					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 int version;
    private long lastModifed;
    private String name;
    
    //private Body body=new Body();
	private Resource source;
	private final String path;
	private boolean isComponent;
	private boolean isInterface;

	private List functions=new ArrayList();
	private List threads=new ArrayList();
	private boolean _writeLog;
	private StringExternalizerWriter externalizer;
    
	
	
    public Page(Resource source,String name,int version, long lastModifed, boolean writeLog) {
    	name=name.replace('.', '/');
    	//body.setParent(this);
        this.name=name;
        this.version=version;
        this.lastModifed=lastModifed;
        this.source=source;
        this.path=source.getAbsolutePath();
        
        this._writeLog=writeLog;
    }
    
    /**
     * result byte code as binary array
     * @param classFile 
     * @return byte code
     * @throws IOException 
     * @throws TemplateException 
     */
    public byte[] execute(Resource classFile) throws BytecodeException {
    	
        
 
        
    	Resource p = classFile.getParentResource().getRealResource(classFile.getName()+".txt");
        this.externalizer=new StringExternalizerWriter(p);
		
    	List keys=new ArrayList();
    	ClassWriter cw = ASMUtil.getClassWriter(); 
    	//ClassWriter cw = new ClassWriter(true);
    	
    	ArrayList list = new ArrayList();
        getImports(list, this); 
    	
    	// parent
    	String parent="railo/runtime/Page";
    	if(isComponent()) parent="railo/runtime/ComponentPage";
    	else if(isInterface()) parent="railo/runtime/InterfacePage";
    	
    	cw.visit(Opcodes.V1_2, Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL, name, null, parent, null);
        cw.visitSource(this.path, null);

        // static constructor
        GeneratorAdapter ga = new GeneratorAdapter(Opcodes.ACC_PUBLIC,STATIC_CONSTRUCTOR,null,null,cw);
		BytecodeContext statConstr = new BytecodeContext(null,null,this,externalizer,keys,cw,name,ga,STATIC_CONSTRUCTOR,writeLog());
		
		// private static  ImportDefintion[] test=new ImportDefintion[]{...};
	    if(list.size()>0){
			FieldVisitor fv = cw.visitField(Opcodes.ACC_PRIVATE + Opcodes.ACC_FINAL + Opcodes.ACC_STATIC, 
					"imports", "[Lrailo/runtime/component/ImportDefintion;", null, null);
			fv.visitEnd();
		
			GeneratorAdapter adapter = statConstr.getAdapter();
			ArrayVisitor av=new ArrayVisitor();
			av.visitBegin(adapter,Types.IMPORT_DEFINITIONS,list.size());
			int index=0;
			Iterator it = list.iterator();
			while(it.hasNext()){
				av.visitBeginItem(adapter,index++);
				adapter.push(it.next());
				ASMConstants.NULL(adapter);
				adapter.invokeStatic(Types.IMPORT_DEFINITIONS_IMPL, ID_GET_INSTANCE);
				av.visitEndItem(adapter);
			}
			av.visitEnd();
			adapter.visitFieldInsn(Opcodes.PUTSTATIC, name, "imports", "[Lrailo/runtime/component/ImportDefintion;");
				
		}
		
		
        // constructor
        ga = new GeneratorAdapter(Opcodes.ACC_PUBLIC,CONSTRUCTOR_PS,null,null,cw);
		BytecodeContext constr = new BytecodeContext(null,null,this,externalizer,keys,cw,name,ga,CONSTRUCTOR_PS,writeLog());
		ga.loadThis();
        Type t=Types.PAGE;
        if(isComponent())t=Types.COMPONENT_PAGE;
        else if(isInterface())t=Types.INTERFACE_PAGE;
        
        ga.invokeConstructor(t, CONSTRUCTOR);

        
        //setPageSource(pageSource);
        ga.visitVarInsn(Opcodes.ALOAD, 0);
        ga.visitVarInsn(Opcodes.ALOAD, 1);
        ga.invokeVirtual(t, SET_PAGE_SOURCE);
        
        
     // 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(list.size()>0){
        	 adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL , GET_IMPORT_DEFINITIONS, null, null, cw);
             adapter.visitFieldInsn(Opcodes.GETSTATIC, name, "imports", "[Lrailo/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();
        
        adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL , COMPILE_TIME, null, null, cw);
        adapter.push(System.currentTimeMillis());
        adapter.returnValue();
        adapter.endMethod();
   
	        adapter.visitInsn(Opcodes.ACONST_NULL);
   	        writeOutNewComponent(statConstr,constr,keys,cw,component);
	        writeOutInitComponent(statConstr,constr,keys,cw,component);
        }
        else if(isInterface()) {
        	Tag interf=getInterface();
   	        writeOutNewInterface(statConstr,constr,keys,cw,interf);
	        writeOutInitInterface(statConstr,constr,keys,cw,interf);
        }
        else {
	        writeOutCall(statConstr,constr,keys,cw);
        }
        
// udfCall     
        Function[] functions=getFunctions();
        ConditionVisitor cv;
        DecisionIntVisitor div;
    // less/equal than 10 functions
        if(functions.length==0 || 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(statConstr,constr,this,externalizer,keys,cw,name,adapter,UDF_CALL,writeLog());
            if(functions.length==0){}
            else if(functions.length==1){
        		ExpressionUtil.visitLine(bc,functions[0].getStartLine());
        		functions[0].getBody().writeOut(bc);
        		ExpressionUtil.visitLine(bc,functions[0].getEndLine());
        	}
        	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(statConstr,constr,this,externalizer,keys,cw,name,adapter,UDF_CALL,writeLog());
		        cv = new ConditionVisitor();
		        cv.visitBefore();
		        int count=0;
		        for(int i=0;ifunctions.length?functions.length:i+10);
	        	
	        	adapter.visitInsn(Opcodes.ACONST_NULL);
		        adapter.returnValue();
		        adapter.endMethod();
	        }
        }
        

     // threadCall
             TagThread[] threads=getThreads();
             if(threads.length>0) {
             	adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL , THREAD_CALL, null, new Type[]{Types.THROWABLE}, cw);
         			writeOutThreadCallInner(new BytecodeContext(statConstr,constr,this,externalizer,keys,cw,name,adapter,THREAD_CALL,writeLog()),threads,0,threads.length);
         		//adapter.visitInsn(Opcodes.ACONST_NULL);
         		adapter.returnValue();
    	
	        	barr[i+2]=bLastMod[i];
         		adapter.endMethod();
             }
        

        
                
// udfDefaultValue
    // less/equal than 10 functions
        if(functions.length==0 || 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);
            writeUdfDefaultValueInner(new BytecodeContext(statConstr,constr,this,externalizer,keys,cw,name,adapter,UDF_DEFAULT_VALUE,writeLog()),functions,0,functions.length);
            
            ASMConstants.NULL(adapter);
        	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(statConstr,constr,this,externalizer,keys,cw,name,adapter,UDF_DEFAULT_VALUE,writeLog());
        	cv = new ConditionVisitor();
	        cv.visitBefore();
	        int count=0;
	        for(int i=0;ifunctions.length?functions.length:i+10);
        	
        	adapter.visitInsn(Opcodes.ACONST_NULL);
	        adapter.returnValue();
	        adapter.endMethod();
        }
        	
        
        
        registerFields(statConstr,keys);
        
        

        statConstr.getAdapter().returnValue();
        statConstr.getAdapter().endMethod();
        
        
        adapter = constr.getAdapter();//new GeneratorAdapter(Opcodes.ACC_PUBLIC,CONSTRUCTOR,null,null,cw);
        
        adapter.returnValue();
        adapter.endMethod();
    	
        try {
			if(externalizer!=null)externalizer.writeOut();
		} catch (IOException e) {
			throw new BytecodeException(e.getMessage(), -1);
		}
        
        
        if(ADD_C33) {
        	byte[] tmp = cw.toByteArray();
	        byte[] bLastMod=NumberUtil.longToByteArray(lastModifed);
	        byte[] barr = new byte[tmp.length+10];
	        // Magic Number
	        barr[0]=CF; // CF
	        barr[1]=_33; // 33
	        
	        // Last Modified
	        for(int i=0;i<8;i++){
	        for(int i=0;i functions=new ArrayList();
		getFunctions(functions,bc,body,pageType);
		
		String className = Types.UDF_PROPERTIES_ARRAY.toString();
		//FieldVisitor fv = bc.getClassWriter().visitField(Opcodes.ACC_PRIVATE , "udfs",className , null, null);
		//fv.visitEnd();
		
		BytecodeContext constr = bc.getConstructor();
		GeneratorAdapter cga = constr.getAdapter();
		
		cga.visitVarInsn(Opcodes.ALOAD, 0);
		cga.push(functions.size());
		//cga.visitTypeInsn(Opcodes.ANEWARRAY, Types.UDF_PROPERTIES.toString());
		cga.newArray(Types.UDF_PROPERTIES);
		cga.visitFieldInsn(Opcodes.PUTFIELD, bc.getClassName(), "udfs", className);
		
		
		Iterator it = functions.iterator();
		while(it.hasNext()){
			it.next().writeOut(bc, 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){
			BodyBase.writeOut(bc.getStaticConstructor(),bc.getConstructor(),bc.getKeys(),body.getStatements(), bc);
		}
	}

	private static void getImports(List list,Body body) throws BytecodeException {
		if(ASMUtil.isEmpty(body)) return;
		Statement stat;
		List stats = body.getStatements();
    	int len=stats.size();
        for(int i=0;i functions,BytecodeContext bc, Body body, int pageType) throws BytecodeException {
		//writeOutImports(bc, body, pageType);
		if(ASMUtil.isEmpty(body)) return;
		Statement stat;
		List stats = body.getStatements();
    	int len=stats.size();
        for(int i=0;i 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();
		ClassAdapter ca = new SourceLastModifiedClassAdapter(cw,lastModified);
		cr.accept(ca, ClassReader.SKIP_DEBUG);
		return cw.toByteArray();
	}
	


}
	class SourceLastModifiedClassAdapter extends ClassAdapter {

		private long lastModified;
		public SourceLastModifiedClassAdapter(ClassWriter cw, long lastModified) {
			super(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;
		}

	}
=======
package railo.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 org.objectweb.asm.ClassAdapter;
import org.objectweb.asm.ClassReader;
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;
	//void init(PageContext pageContext,ComponentPage componentPage)
import org.objectweb.asm.commons.Method;

import railo.commons.io.res.Resource;
import railo.commons.lang.NumberUtil;
import railo.commons.lang.StringUtil;
import railo.runtime.component.ImportDefintion;
import railo.runtime.exp.TemplateException;
import railo.runtime.type.KeyImpl;
import railo.runtime.type.StructImpl;
import railo.runtime.type.UDF;
import railo.runtime.type.scope.Undefined;
import railo.runtime.type.util.KeyConstants;
import railo.transformer.bytecode.expression.Expression;
import railo.transformer.bytecode.extern.StringExternalizerWriter;
import railo.transformer.bytecode.literal.LitString;
import railo.transformer.bytecode.statement.Argument;
import railo.transformer.bytecode.statement.Function;
import railo.transformer.bytecode.statement.HasBodies;
import railo.transformer.bytecode.statement.HasBody;
import railo.transformer.bytecode.statement.IFunction;
import railo.transformer.bytecode.statement.NativeSwitch;
import railo.transformer.bytecode.statement.tag.Attribute;
import railo.transformer.bytecode.statement.tag.Tag;
import railo.transformer.bytecode.statement.tag.TagImport;
import railo.transformer.bytecode.statement.tag.TagThread;
import railo.transformer.bytecode.util.ASMConstants;
import railo.transformer.bytecode.util.ASMUtil;
import railo.transformer.bytecode.util.ExpressionUtil;
import railo.transformer.bytecode.util.Types;
import railo.transformer.bytecode.visitor.ArrayVisitor;
import railo.transformer.bytecode.visitor.ConditionVisitor;
import railo.transformer.bytecode.visitor.DecisionIntVisitor;
import railo.transformer.bytecode.visitor.TryCatchFinallyVisitor;

/**
 * represent a single Page like "index.cfm"
 */
public final class Page extends BodyBase {


	public void doFinalize(BytecodeContext bc) {
		ExpressionUtil.visitLine(bc, getEndLine()+1);
	}

	private static final Type KEY_IMPL = Type.getType(KeyImpl.class);
	private static final Type KEY_CONSTANTS = Type.getType(KeyConstants.class);
	private static final Method KEY_INIT = new Method(
			"init",
			Types.COLLECTION_KEY,
			new Type[]{Types.STRING}
    		);
	private 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  ()V");
	//public final static Method CONSTRUCTOR = Method.getMethod("void  ()V");

	private static final Method CONSTRUCTOR = new Method(
			"",
			Types.VOID,
			new Type[]{}//
    		);
	private static final Method CONSTRUCTOR_PS = new Method(
			"",
			Types.VOID,
			new Type[]{Types.PAGE_SOURCE}//
    		);

    public static final Type STRUCT_IMPL = Type.getType(StructImpl.class);
	private static final Method INIT_STRUCT_IMPL = new Method(
			"",
			Types.VOID,
			new Type[]{}
    		);

	
    // void call (railo.runtime.PageContext)
    private final static Method CALL = new Method(
			"call",
			Types.VOID,
			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.INT_VALUE,
			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[]{}
    		);
    
    // 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}
			);

	private static final Method NEW_COMPONENT_IMPL_INSTANCE = new Method(
			"newInstance",
			Types.COMPONENT_IMPL,
			new Type[]{Types.PAGE_CONTEXT,Types.STRING,Types.BOOLEAN_VALUE}
    		);
	
	private static final Method NEW_INTERFACE_IMPL_INSTANCE = new Method(
			"newInstance",
			Types.INTERFACE_IMPL,
			new Type[]{Types.STRING,Types.BOOLEAN_VALUE,Types.MAP}
    		);
	
	

	

	// void init(PageContext pc,Component Impl c) throws PageException
	private static final Method INIT_COMPONENT = new Method(
			"initComponent",
			Types.VOID,
			new Type[]{Types.PAGE_CONTEXT,Types.COMPONENT_IMPL}
    		);
	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_IMPL = new Method(
			"",
			Types.VOID,
			new Type[]{
					Types.INTERFACE_PAGE,
						Types.STRING, // extends
						Types.STRING, // hind
						Types.STRING, // display
						Types.STRING, // callpath
						Types.BOOLEAN_VALUE, // realpath
						Types.MAP, //interfaceudfs
						Types.MAP // meta
					}
    		);
	
	
	private static final Method INIT = new Method(
        ga.visitVarInsn(Opcodes.ALOAD, 1);
		LitString value;
	}
			"init",
			Types.VOID,
			new Type[]{Types.PAGE_CONTEXT,Types.COMPONENT_PAGE}
    		);
	
	private static final Method CHECK_INTERFACE = new Method(
			"checkInterface",
			Types.VOID,
			new Type[]{Types.PAGE_CONTEXT,Types.COMPONENT_PAGE}
    		);
	
	

	// 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});
	
	
	// boolean unsetSilent()
	/*private static final Method UNSET_SILENT = new Method(
			"unsetSilent",
			Types.BOOLEAN_VALUE,
			new Type[]{}
    		);*/

	// void afterCall(PageContext pc, Scope parent)
	private static final Method AFTER_CALL = new Method(
			"afterConstructor",
			Types.VOID,
			new Type[]{Types.PAGE_CONTEXT,Types.VARIABLES}
    		);

	// ComponentImpl(ComponentPage,boolean, String, String, String) NS==No Style
	
	
	// Component Impl(ComponentPage,boolean, String, String, String, String) WS==With Style
	private static final Method CONSTR_COMPONENT_IMPL = new Method(
			"",
			Types.VOID,
			new Type[]{
					Types.COMPONENT_PAGE,
					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,
					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 int version;
    private long lastModifed;
    private String name;
    
    //private Body body=new Body();
	private Resource source;
	private final String path;
	private boolean isComponent;
	private boolean isInterface;

	private List functions=new ArrayList();
	private List threads=new ArrayList();
	private boolean _writeLog;
	private StringExternalizerWriter externalizer;
    
	
	
    public Page(Resource source,String name,int version, long lastModifed, boolean writeLog) {
    	name=name.replace('.', '/');
    	//body.setParent(this);
        this.name=name;
        this.version=version;
        this.lastModifed=lastModifed;
        this.source=source;
        this.path=source.getAbsolutePath();
        
        this._writeLog=writeLog;
    }
    
    /**
     * result byte code as binary array
     * @param classFile 
     * @return byte code
     * @throws IOException 
     * @throws TemplateException 
     */
    public byte[] execute(Resource classFile) throws BytecodeException {
    	
    	try {
    		Resource p = classFile.getParentResource().getRealResource(classFile.getName()+".txt");
            this.externalizer=new StringExternalizerWriter(p);
		} catch (IOException e) {}
    	
    	List keys=new ArrayList();
    	ClassWriter cw = ASMUtil.getClassWriter(); 
    	//ClassWriter cw = new ClassWriter(true);
    	
    	ArrayList list = new ArrayList();
        getImports(list, this); 
    	
    	// parent
    	String parent="railo/runtime/PagePlus";// FUTURE use Page instead of PagePlus
    	if(isComponent()) parent="railo/runtime/ComponentPage";
    	else if(isInterface()) parent="railo/runtime/InterfacePage";
    	
    	cw.visit(Opcodes.V1_2, Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL, name, null, parent, null);
        cw.visitSource(this.path, null);

        // static constructor
        GeneratorAdapter ga = new GeneratorAdapter(Opcodes.ACC_PUBLIC,STATIC_CONSTRUCTOR,null,null,cw);
		BytecodeContext statConstr = new BytecodeContext(null,null,externalizer,keys,cw,name,ga,STATIC_CONSTRUCTOR,writeLog());
		
		// private static  ImportDefintion[] test=new ImportDefintion[]{...};
	    if(list.size()>0){
			FieldVisitor fv = cw.visitField(Opcodes.ACC_PRIVATE + Opcodes.ACC_FINAL + Opcodes.ACC_STATIC, 
					"imports", "[Lrailo/runtime/component/ImportDefintion;", null, null);
			fv.visitEnd();
		
			GeneratorAdapter adapter = statConstr.getAdapter();
			ArrayVisitor av=new ArrayVisitor();
			av.visitBegin(adapter,Types.IMPORT_DEFINITIONS,list.size());
			int index=0;
			Iterator it = list.iterator();
			while(it.hasNext()){
				av.visitBeginItem(adapter,index++);
				adapter.push(it.next());
				ASMConstants.NULL(adapter);
				adapter.invokeStatic(Types.IMPORT_DEFINITIONS, ID_GET_INSTANCE);
				av.visitEndItem(adapter);
			}
			av.visitEnd();
			adapter.visitFieldInsn(Opcodes.PUTSTATIC, name, "imports", "[Lrailo/runtime/component/ImportDefintion;");
				
		}
		
		
        // constructor
        ga = new GeneratorAdapter(Opcodes.ACC_PUBLIC,CONSTRUCTOR_PS,null,null,cw);
		BytecodeContext constr = new BytecodeContext(null,null,externalizer,keys,cw,name,ga,CONSTRUCTOR_PS,writeLog());
		ga.loadThis();
        Type t=Types.PAGE_PLUS;
        if(isComponent())t=Types.COMPONENT_PAGE;
        else if(isInterface())t=Types.INTERFACE_PAGE;
        
        ga.invokeConstructor(t, CONSTRUCTOR);

        
        //setPageSource(pageSource);
        ga.visitVarInsn(Opcodes.ALOAD, 0);
        ga.invokeVirtual(t, SET_PAGE_SOURCE);
       // mv.visitMethodInsn(INVOKEVIRTUAL, "railo/runtime/PagePlus", "setPageSource", "(Lrailo/runtime/PageSource;)V");

        
        
        
 
        
     // 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(list.size()>0){
        	 adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL , GET_IMPORT_DEFINITIONS, null, null, cw);
             adapter.visitFieldInsn(Opcodes.GETSTATIC, name, "imports", "[Lrailo/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();
        
        adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL , COMPILE_TIME, null, null, cw);
        adapter.push(System.currentTimeMillis());
        adapter.returnValue();
        adapter.endMethod();
   
// newInstance/initComponent/call
        if(isComponent()) {
        	Tag component=getComponent();
   	        writeOutNewComponent(statConstr,constr,keys,cw,component);
	        writeOutInitComponent(statConstr,constr,keys,cw,component);
        }
        else if(isInterface()) {
        	Tag interf=getInterface();
   	        writeOutNewInterface(statConstr,constr,keys,cw,interf);
	        writeOutInitInterface(statConstr,constr,keys,cw,interf);
        }
        else {
	        writeOutCall(statConstr,constr,keys,cw);
        }
        
// udfCall     
        Function[] functions=getFunctions();
        
        ConditionVisitor cv;
        DecisionIntVisitor div;
    // less/equal than 10 functions
        if(functions.length==0 || 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(statConstr,constr,externalizer,keys,cw,name,adapter,UDF_CALL,writeLog());
            if(functions.length==0){}
            else if(functions.length==1){
        		ExpressionUtil.visitLine(bc,functions[0].getStartLine());
        		functions[0].getBody().writeOut(bc);
	}
        		ExpressionUtil.visitLine(bc,functions[0].getEndLine());
        	}
        	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(statConstr,constr,externalizer,keys,cw,name,adapter,UDF_CALL,writeLog());
		        cv = new ConditionVisitor();
		        cv.visitBefore();
		        int count=0;
		        for(int i=0;ifunctions.length?functions.length:i+10);
	        	
	        	adapter.visitInsn(Opcodes.ACONST_NULL);
		        adapter.returnValue();
		        adapter.endMethod();
	        }
        }
        

     // threadCall
             TagThread[] threads=getThreads();
             if(threads.length>0) {
             	adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL , THREAD_CALL, null, new Type[]{Types.THROWABLE}, cw);
         			writeOutThreadCallInner(new BytecodeContext(statConstr,constr,externalizer,keys,cw,name,adapter,THREAD_CALL,writeLog()),threads,0,threads.length);
         		//adapter.visitInsn(Opcodes.ACONST_NULL);
         		adapter.returnValue();
         		adapter.endMethod();
             }
        

        
                
// udfDefaultValue
    // less/equal than 10 functions
        if(functions.length==0 || 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);
            writeUdfDefaultValueInner(new BytecodeContext(statConstr,constr,externalizer,keys,cw,name,adapter,UDF_DEFAULT_VALUE,writeLog()),functions,0,functions.length);
            
            ASMConstants.NULL(adapter);
        	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(statConstr,constr,externalizer,keys,cw,name,adapter,UDF_DEFAULT_VALUE,writeLog());
        	cv = new ConditionVisitor();
	        cv.visitBefore();
	        int count=0;
	        for(int i=0;ifunctions.length?functions.length:i+10);
        	
        	adapter.visitInsn(Opcodes.ACONST_NULL);
	        adapter.returnValue();
	        adapter.endMethod();
        }
        	
        }
        
        
        registerFields(statConstr,keys);
        
        

        statConstr.getAdapter().returnValue();
        statConstr.getAdapter().endMethod();
        
        
        adapter = constr.getAdapter();//new GeneratorAdapter(Opcodes.ACC_PUBLIC,CONSTRUCTOR,null,null,cw);
        
        adapter.returnValue();
        adapter.endMethod();
    	
        try {
			if(externalizer!=null)externalizer.writeOut();
		} catch (IOException e) {
			throw new BytecodeException(e.getMessage(), -1);
		}
        
        
        if(ADD_C33) {
        	byte[] tmp = cw.toByteArray();
	        byte[] bLastMod=NumberUtil.longToByteArray(lastModifed);
	        byte[] barr = new byte[tmp.length+10];
	        // Magic Number
	        barr[0]=CF; // CF
	        barr[1]=_33; // 33
	        
	        // Last Modified
	        for(int i=0;i<8;i++){
	        	barr[i+2]=bLastMod[i];
	        }
	        for(int i=0;i functions=new ArrayList();
		getFunctions(functions,bc,body,pageType);
		
		String className = Types.UDF_PROPERTIES_ARRAY.toString();
		//FieldVisitor fv = bc.getClassWriter().visitField(Opcodes.ACC_PRIVATE , "udfs",className , null, null);
		//fv.visitEnd();
		
		BytecodeContext constr = bc.getConstructor();
		GeneratorAdapter cga = constr.getAdapter();
		
		cga.visitVarInsn(Opcodes.ALOAD, 0);
		cga.push(functions.size());
		//cga.visitTypeInsn(Opcodes.ANEWARRAY, Types.UDF_PROPERTIES.toString());
		cga.newArray(Types.UDF_PROPERTIES);
		cga.visitFieldInsn(Opcodes.PUTFIELD, bc.getClassName(), "udfs", className);
		
		
		Iterator it = functions.iterator();
		while(it.hasNext()){
			it.next().writeOut(bc, 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){
			BodyBase.writeOut(bc.getStaticConstructor(),bc.getConstructor(),bc.getKeys(),body.getStatements(), bc);
		}
	}

	private static void getImports(List list,Body body) throws BytecodeException {
		if(ASMUtil.isEmpty(body)) return;
		Statement stat;
		List stats = body.getStatements();
    	int len=stats.size();
        for(int i=0;i functions,BytecodeContext bc, Body body, int pageType) throws BytecodeException {
		//writeOutImports(bc, body, pageType);
		if(ASMUtil.isEmpty(body)) return;
		Statement stat;
		List stats = body.getStatements();
    	int len=stats.size();
        for(int i=0;i>>>>>> 6874c434a7a0fdf4e9b3ef495c5db191b50f57af
Solution content
package railo.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 org.objectweb.asm.ClassAdapter;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Label;
			Types.VOID,
			new Type[]{}
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;

import railo.commons.io.res.Resource;
import railo.commons.lang.NumberUtil;
import railo.commons.lang.StringUtil;
import railo.runtime.component.ImportDefintion;
import railo.runtime.component.ImportDefintionImpl;
import railo.runtime.exp.TemplateException;
import railo.runtime.type.KeyImpl;
import railo.runtime.type.StructImpl;
import railo.runtime.type.UDF;
import railo.runtime.type.scope.Undefined;
import railo.runtime.type.util.KeyConstants;
import railo.transformer.bytecode.expression.Expression;
import railo.transformer.bytecode.extern.StringExternalizerWriter;
import railo.transformer.bytecode.literal.LitString;
import railo.transformer.bytecode.statement.Argument;
import railo.transformer.bytecode.statement.HasBodies;
import railo.transformer.bytecode.statement.HasBody;
import railo.transformer.bytecode.statement.IFunction;
import railo.transformer.bytecode.statement.NativeSwitch;
import railo.transformer.bytecode.statement.tag.Attribute;
import railo.transformer.bytecode.statement.tag.Tag;
import railo.transformer.bytecode.statement.tag.TagImport;
import railo.transformer.bytecode.statement.tag.TagThread;
import railo.transformer.bytecode.statement.udf.Function;
import railo.transformer.bytecode.statement.udf.FunctionImpl;
import railo.transformer.bytecode.util.ASMConstants;
import railo.transformer.bytecode.util.ASMUtil;
import railo.transformer.bytecode.util.ExpressionUtil;
import railo.transformer.bytecode.util.Types;
import railo.transformer.bytecode.visitor.ArrayVisitor;
import railo.transformer.bytecode.visitor.ConditionVisitor;
import railo.transformer.bytecode.visitor.DecisionIntVisitor;
import railo.transformer.bytecode.visitor.OnFinally;
import railo.transformer.bytecode.visitor.TryCatchFinallyVisitor;

/**
 * represent a single Page like "index.cfm"
 */
public final class Page extends BodyBase {


	public void doFinalize(BytecodeContext bc) {
		ExpressionUtil.visitLine(bc, getEndLine()+1);
	}

	private static final Type KEY_IMPL = Type.getType(KeyImpl.class);
	private static final Type KEY_CONSTANTS = Type.getType(KeyConstants.class);
	private static final Method KEY_INIT = new Method(
			"init",
			Types.COLLECTION_KEY,
			new Type[]{Types.STRING}
    		);
	private 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  ()V");
	//public final static Method CONSTRUCTOR = Method.getMethod("void  ()V");

	private static final Method CONSTRUCTOR = new Method(
			"",
			Types.VOID,
			new Type[]{}//
    		);
	private static final Method CONSTRUCTOR_PS = new Method(
			"",
			Types.VOID,
			new Type[]{Types.PAGE_SOURCE}//
    		);

    public static final Type STRUCT_IMPL = Type.getType(StructImpl.class);
	private static final Method INIT_STRUCT_IMPL = new Method(
			"",
			Types.VOID,
    		);

	
    // void call (railo.runtime.PageContext)
    private final static Method CALL = new Method(
			"call",
			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.INT_VALUE,
			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[]{}
    		);
    
    // 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}
			);

	private static final Method NEW_COMPONENT_IMPL_INSTANCE = new Method(
			"newInstance",
			Types.COMPONENT_IMPL,
			new Type[]{Types.PAGE_CONTEXT,Types.STRING,Types.BOOLEAN_VALUE}
    		);
	
	private static final Method NEW_INTERFACE_IMPL_INSTANCE = new Method(
			"newInstance",
			Types.INTERFACE_IMPL,
			new Type[]{Types.STRING,Types.BOOLEAN_VALUE,Types.MAP}
    		);
	
	

	

	// void init(PageContext pc,Component Impl c) throws PageException
	private static final Method INIT_COMPONENT = new Method(
			"initComponent",
			Types.VOID,
			new Type[]{Types.PAGE_CONTEXT,Types.COMPONENT_IMPL}
    		);
	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_IMPL = new Method(
			"",
			Types.VOID,
			new Type[]{
					Types.INTERFACE_PAGE,
						Types.STRING, // extends
						Types.STRING, // hind
						Types.STRING, // display
						Types.STRING, // callpath
						Types.BOOLEAN_VALUE, // realpath
						Types.MAP, //interfaceudfs
						Types.MAP // meta
					}
    		);
	
	
	//void init(PageContext pageContext,ComponentPage componentPage)
	private static final Method INIT = new Method(
			"init",
			Types.VOID,
			new Type[]{Types.PAGE_CONTEXT,Types.COMPONENT_PAGE}
    		);
	
	private static final Method CHECK_INTERFACE = new Method(
			"checkInterface",
			Types.VOID,
			new Type[]{Types.PAGE_CONTEXT,Types.COMPONENT_PAGE}
    		);
	
	

	// 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});
	
	
	// boolean unsetSilent()
	/*private static final Method UNSET_SILENT = new Method(
			"unsetSilent",
			Types.BOOLEAN_VALUE,
			new Type[]{}
    		);*/

	// void afterCall(PageContext pc, Scope parent)
	private static final Method AFTER_CALL = new Method(
			"afterConstructor",
			Types.VOID,
			new Type[]{Types.PAGE_CONTEXT,Types.VARIABLES}
    		);

	// ComponentImpl(ComponentPage,boolean, String, String, String) NS==No Style
	
	
	// Component Impl(ComponentPage,boolean, String, String, String, String) WS==With Style
	private static final Method CONSTR_COMPONENT_IMPL = new Method(
			"",
			Types.VOID,
			new Type[]{
					Types.COMPONENT_PAGE,
					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,
					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,
		if(attrs!=null) {
			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 int version;
    private long lastModifed;
    private String name;
    
    //private Body body=new Body();
	private Resource source;
	private final String path;
	private boolean isComponent;
	private boolean isInterface;

	private List functions=new ArrayList();
	private List threads=new ArrayList();
	private boolean _writeLog;
	private StringExternalizerWriter externalizer;
    
	
	
    public Page(Resource source,String name,int version, long lastModifed, boolean writeLog) {
    	name=name.replace('.', '/');
    	//body.setParent(this);
        this.name=name;
        this.version=version;
        this.lastModifed=lastModifed;
        this.source=source;
        this.path=source.getAbsolutePath();
        
        this._writeLog=writeLog;
    }
    
    /**
     * result byte code as binary array
     * @param classFile 
     * @return byte code
     * @throws IOException 
     * @throws TemplateException 
     */
    public byte[] execute(Resource classFile) throws BytecodeException {
    	
    	Resource p = classFile.getParentResource().getRealResource(classFile.getName()+".txt");
        this.externalizer=new StringExternalizerWriter(p);
		
    	List keys=new ArrayList();
    	ClassWriter cw = ASMUtil.getClassWriter(); 
    	//ClassWriter cw = new ClassWriter(true);
    	
    	ArrayList list = new ArrayList();
        getImports(list, this); 
    	
    	// parent
    	String parent="railo/runtime/Page";
    	if(isComponent()) parent="railo/runtime/ComponentPage";
    	else if(isInterface()) parent="railo/runtime/InterfacePage";
    	
    	cw.visit(Opcodes.V1_2, Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL, name, null, parent, null);
        cw.visitSource(this.path, null);

        // static constructor
        GeneratorAdapter ga = new GeneratorAdapter(Opcodes.ACC_PUBLIC,STATIC_CONSTRUCTOR,null,null,cw);
		BytecodeContext statConstr = new BytecodeContext(null,null,this,externalizer,keys,cw,name,ga,STATIC_CONSTRUCTOR,writeLog());
		
		// private static  ImportDefintion[] test=new ImportDefintion[]{...};
	    if(list.size()>0){
			FieldVisitor fv = cw.visitField(Opcodes.ACC_PRIVATE + Opcodes.ACC_FINAL + Opcodes.ACC_STATIC, 
					"imports", "[Lrailo/runtime/component/ImportDefintion;", null, null);
			fv.visitEnd();
		
			GeneratorAdapter adapter = statConstr.getAdapter();
			ArrayVisitor av=new ArrayVisitor();
			av.visitBegin(adapter,Types.IMPORT_DEFINITIONS,list.size());
			int index=0;
			Iterator it = list.iterator();
			while(it.hasNext()){
				av.visitBeginItem(adapter,index++);
				adapter.push(it.next());
				ASMConstants.NULL(adapter);
				adapter.invokeStatic(Types.IMPORT_DEFINITIONS_IMPL, ID_GET_INSTANCE);
				av.visitEndItem(adapter);
			}
			av.visitEnd();
			adapter.visitFieldInsn(Opcodes.PUTSTATIC, name, "imports", "[Lrailo/runtime/component/ImportDefintion;");
				
		}
		
		
        // constructor
        ga = new GeneratorAdapter(Opcodes.ACC_PUBLIC,CONSTRUCTOR_PS,null,null,cw);
		BytecodeContext constr = new BytecodeContext(null,null,this,externalizer,keys,cw,name,ga,CONSTRUCTOR_PS,writeLog());
		ga.loadThis();
        Type t=Types.PAGE;
        		i--;
        if(isComponent())t=Types.COMPONENT_PAGE;
		        		
        else if(isInterface())t=Types.INTERFACE_PAGE;
        
        ga.invokeConstructor(t, CONSTRUCTOR);

        
        //setPageSource(pageSource);
        ga.visitVarInsn(Opcodes.ALOAD, 0);
        ga.visitVarInsn(Opcodes.ALOAD, 1);
        ga.invokeVirtual(t, SET_PAGE_SOURCE);
        
        
        
 
        
     // 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(list.size()>0){
        	 adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL , GET_IMPORT_DEFINITIONS, null, null, cw);
             adapter.visitFieldInsn(Opcodes.GETSTATIC, name, "imports", "[Lrailo/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();
        
        adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL , COMPILE_TIME, null, null, cw);
        adapter.push(System.currentTimeMillis());
        adapter.returnValue();
        adapter.endMethod();
   
// newInstance/initComponent/call
        if(isComponent()) {
        	Tag component=getComponent();
   	        writeOutNewComponent(statConstr,constr,keys,cw,component);
	        writeOutInitComponent(statConstr,constr,keys,cw,component);
        }
        else if(isInterface()) {
        	Tag interf=getInterface();
   	        writeOutNewInterface(statConstr,constr,keys,cw,interf);
	        writeOutInitInterface(statConstr,constr,keys,cw,interf);
        }
        else {
	        writeOutCall(statConstr,constr,keys,cw);
        }
        
// udfCall     
        Function[] functions=getFunctions();
        ConditionVisitor cv;
        DecisionIntVisitor div;
    // less/equal than 10 functions
        if(functions.length==0 || 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(statConstr,constr,this,externalizer,keys,cw,name,adapter,UDF_CALL,writeLog());
            if(functions.length==0){}
            else if(functions.length==1){
        		ExpressionUtil.visitLine(bc,functions[0].getStartLine());
        		functions[0].getBody().writeOut(bc);
        		ExpressionUtil.visitLine(bc,functions[0].getEndLine());
        	}
        	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(statConstr,constr,this,externalizer,keys,cw,name,adapter,UDF_CALL,writeLog());
		        cv = new ConditionVisitor();
		        cv.visitBefore();
		        int count=0;
		        for(int i=0;ifunctions.length?functions.length:i+10);
	        	
	        	adapter.visitInsn(Opcodes.ACONST_NULL);
		        adapter.returnValue();
		        adapter.endMethod();
	        }
        }
        

     // threadCall
             TagThread[] threads=getThreads();
             if(threads.length>0) {
             	adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL , THREAD_CALL, null, new Type[]{Types.THROWABLE}, cw);
         			writeOutThreadCallInner(new BytecodeContext(statConstr,constr,this,externalizer,keys,cw,name,adapter,THREAD_CALL,writeLog()),threads,0,threads.length);
         		//adapter.visitInsn(Opcodes.ACONST_NULL);
         		adapter.returnValue();
         		adapter.endMethod();
             }
        

        
                
// udfDefaultValue
    // less/equal than 10 functions
        if(functions.length==0 || 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);
            writeUdfDefaultValueInner(new BytecodeContext(statConstr,constr,this,externalizer,keys,cw,name,adapter,UDF_DEFAULT_VALUE,writeLog()),functions,0,functions.length);
            
            ASMConstants.NULL(adapter);
        	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(statConstr,constr,this,externalizer,keys,cw,name,adapter,UDF_DEFAULT_VALUE,writeLog());
        	cv = new ConditionVisitor();
	        cv.visitBefore();
	        int count=0;
	        for(int i=0;ifunctions.length?functions.length:i+10);
        	
        	adapter.visitInsn(Opcodes.ACONST_NULL);
	        adapter.returnValue();
	        adapter.endMethod();
        }
        	
        }
        
        
        registerFields(statConstr,keys);
        
        

        statConstr.getAdapter().returnValue();
        statConstr.getAdapter().endMethod();
        
        
        adapter = constr.getAdapter();//new GeneratorAdapter(Opcodes.ACC_PUBLIC,CONSTRUCTOR,null,null,cw);
        
        adapter.returnValue();
        adapter.endMethod();
    	
        try {
			if(externalizer!=null)externalizer.writeOut();
		} catch (IOException e) {
			throw new BytecodeException(e.getMessage(), -1);
		}
        
        
        if(ADD_C33) {
        	byte[] tmp = cw.toByteArray();
	        byte[] bLastMod=NumberUtil.longToByteArray(lastModifed);
	        byte[] barr = new byte[tmp.length+10];
	        // Magic Number
	        barr[0]=CF; // CF
	        barr[1]=_33; // 33
	        
	        // Last Modified
	        for(int i=0;i<8;i++){
	        	barr[i+2]=bLastMod[i];
	        }
	        for(int i=0;i functions=new ArrayList();
		getFunctions(functions,bc,body,pageType);
		
		String className = Types.UDF_PROPERTIES_ARRAY.toString();
		//FieldVisitor fv = bc.getClassWriter().visitField(Opcodes.ACC_PRIVATE , "udfs",className , null, null);
		//fv.visitEnd();
		
		BytecodeContext constr = bc.getConstructor();
		GeneratorAdapter cga = constr.getAdapter();
		
		cga.visitVarInsn(Opcodes.ALOAD, 0);
		cga.push(functions.size());
		//cga.visitTypeInsn(Opcodes.ANEWARRAY, Types.UDF_PROPERTIES.toString());
		cga.newArray(Types.UDF_PROPERTIES);
		cga.visitFieldInsn(Opcodes.PUTFIELD, bc.getClassName(), "udfs", className);
		
		
		Iterator it = functions.iterator();
		while(it.hasNext()){
			it.next().writeOut(bc, 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){
			BodyBase.writeOut(bc.getStaticConstructor(),bc.getConstructor(),bc.getKeys(),body.getStatements(), bc);
		}
	}

	private static void getImports(List list,Body body) throws BytecodeException {
		if(ASMUtil.isEmpty(body)) return;
		Statement stat;
		List stats = body.getStatements();
    	int len=stats.size();
        for(int i=0;i functions,BytecodeContext bc, Body body, int pageType) throws BytecodeException {
		//writeOutImports(bc, body, pageType);
		if(ASMUtil.isEmpty(body)) return;
		Statement stat;
		List stats = body.getStatements();
    	int len=stats.size();
        for(int i=0;i 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();
		ClassAdapter ca = new SourceLastModifiedClassAdapter(cw,lastModified);
		cr.accept(ca, ClassReader.SKIP_DEBUG);
		return cw.toByteArray();
	}
	


}
	class SourceLastModifiedClassAdapter extends ClassAdapter {

		private long lastModified;
		public SourceLastModifiedClassAdapter(ClassWriter cw, long lastModified) {
			super(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;
		}

	}
File
Page.java
Developer's decision
Combination
Kind of conflict
Class declaration
Comment
Import
Package declaration