001/**
002 *
003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either 
008 * version 2.1 of the License, or (at your option) any later version.
009 * 
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013 * Lesser General Public License for more details.
014 * 
015 * You should have received a copy of the GNU Lesser General Public 
016 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
017 * 
018 **/
019package lucee.transformer.bytecode;
020
021import java.io.IOException;
022import java.util.ArrayList;
023import java.util.Iterator;
024import java.util.List;
025import java.util.Map;
026import java.util.Map.Entry;
027
028import lucee.commons.io.CharsetUtil;
029import lucee.commons.io.IOUtil;
030import lucee.commons.io.res.Resource;
031import lucee.commons.lang.StringUtil;
032import lucee.runtime.Mapping;
033import lucee.runtime.PageSource;
034import lucee.runtime.component.ImportDefintion;
035import lucee.runtime.component.ImportDefintionImpl;
036import lucee.runtime.config.Config;
037import lucee.runtime.engine.ThreadLocalPageContext;
038import lucee.runtime.exp.TemplateException;
039import lucee.runtime.type.KeyImpl;
040import lucee.runtime.type.UDF;
041import lucee.runtime.type.scope.Undefined;
042import lucee.runtime.type.util.KeyConstants;
043import lucee.transformer.bytecode.expression.Expression;
044import lucee.transformer.bytecode.literal.LitString;
045import lucee.transformer.bytecode.literal.LitString.Range;
046import lucee.transformer.bytecode.statement.Argument;
047import lucee.transformer.bytecode.statement.HasBodies;
048import lucee.transformer.bytecode.statement.HasBody;
049import lucee.transformer.bytecode.statement.IFunction;
050import lucee.transformer.bytecode.statement.NativeSwitch;
051import lucee.transformer.bytecode.statement.tag.Attribute;
052import lucee.transformer.bytecode.statement.tag.Tag;
053import lucee.transformer.bytecode.statement.tag.TagImport;
054import lucee.transformer.bytecode.statement.tag.TagThread;
055import lucee.transformer.bytecode.statement.udf.Function;
056import lucee.transformer.bytecode.statement.udf.FunctionImpl;
057import lucee.transformer.bytecode.util.ASMConstants;
058import lucee.transformer.bytecode.util.ASMUtil;
059import lucee.transformer.bytecode.util.ExpressionUtil;
060import lucee.transformer.bytecode.util.Types;
061import lucee.transformer.bytecode.visitor.ArrayVisitor;
062import lucee.transformer.bytecode.visitor.ConditionVisitor;
063import lucee.transformer.bytecode.visitor.DecisionIntVisitor;
064import lucee.transformer.bytecode.visitor.OnFinally;
065import lucee.transformer.bytecode.visitor.TryCatchFinallyVisitor;
066
067import org.objectweb.asm.ClassReader;
068import org.objectweb.asm.ClassVisitor;
069import org.objectweb.asm.ClassWriter;
070import org.objectweb.asm.FieldVisitor;
071import org.objectweb.asm.Label;
072import org.objectweb.asm.MethodVisitor;
073import org.objectweb.asm.Opcodes;
074import org.objectweb.asm.Type;
075import org.objectweb.asm.commons.GeneratorAdapter;
076import org.objectweb.asm.commons.Method;
077
078/**
079 * represent a single Page like "index.cfm"
080 */
081public final class Page extends BodyBase {
082
083
084        public void doFinalize(BytecodeContext bc) {
085                ExpressionUtil.visitLine(bc, getEnd());
086        }
087        
088        public static final Type NULL = Type.getType(lucee.runtime.type.Null.class);
089        public static final Type KEY_IMPL = Type.getType(KeyImpl.class);
090        public static final Type KEY_CONSTANTS = Type.getType(KeyConstants.class);
091        public static final Method KEY_INIT = new Method(
092                        "init",
093                        Types.COLLECTION_KEY,
094                        new Type[]{Types.STRING}
095                );
096        public static final Method KEY_INTERN = new Method(
097                        "intern",
098                        Types.COLLECTION_KEY,
099                        new Type[]{Types.STRING}
100                );
101        
102        // public static ImportDefintion getInstance(String fullname,ImportDefintion defaultValue)
103        private static final Method ID_GET_INSTANCE = new Method(
104                        "getInstance",
105                        Types.IMPORT_DEFINITIONS,
106                        new Type[]{Types.STRING,Types.IMPORT_DEFINITIONS}
107                );
108
109        public final static Method STATIC_CONSTRUCTOR = Method.getMethod("void <clinit> ()V");
110        //public final static Method CONSTRUCTOR = Method.getMethod("void <init> ()V");
111
112        private static final Method CONSTRUCTOR = new Method(
113                        "<init>",
114                        Types.VOID,
115                        new Type[]{}//
116                );
117        private static final Method CONSTRUCTOR_PS = new Method(
118                        "<init>",
119                        Types.VOID,
120                        new Type[]{Types.PAGE_SOURCE}//
121                );
122
123    public static final Method INIT_STRUCT_IMPL = new Method(
124                        "<init>",
125                        Types.VOID,
126                        new Type[]{}
127                );
128
129        
130    // void call (lucee.runtime.PageContext)
131    private final static Method CALL = new Method(
132                        "call",
133                        Types.VOID,
134                        new Type[]{Types.PAGE_CONTEXT}
135                );
136    
137    /*/ void _try ()
138    private final static Method TRY = new Method(
139                        "_try",
140                        Types.VOID,
141                        new Type[]{}
142                );*/
143        
144    // int getVersion()
145    private final static Method VERSION = new Method(
146                        "getVersion",
147                        Types.INT_VALUE,
148                        new Type[]{}
149                );
150    // void _init()
151    private final static Method _INIT = new Method(
152                        "initKeys",
153                        Types.VOID,
154                        new Type[]{}
155                );
156    
157    private final static Method SET_PAGE_SOURCE = new Method(
158                        "setPageSource",
159                        Types.VOID,
160                        new Type[]{Types.PAGE_SOURCE}
161                );
162    
163    // public ImportDefintion[] getImportDefintions()
164    private final static Method GET_IMPORT_DEFINITIONS = new Method(
165                        "getImportDefintions",
166                        Types.IMPORT_DEFINITIONS_ARRAY,
167                        new Type[]{}
168                );
169    
170    // long getSourceLastModified()
171    private final static Method LAST_MOD = new Method(
172                        "getSourceLastModified",
173                        Types.LONG_VALUE,
174                        new Type[]{}
175                );
176    
177    private final static Method COMPILE_TIME = new Method(
178                        "getCompileTime",
179                        Types.LONG_VALUE,
180                        new Type[]{}
181                );
182
183    private static final Type USER_DEFINED_FUNCTION = Type.getType(UDF.class);
184    private static final Method UDF_CALL = new Method(
185                        "udfCall",
186                        Types.OBJECT,
187                        new Type[]{Types.PAGE_CONTEXT, USER_DEFINED_FUNCTION, Types.INT_VALUE}
188                        );
189    
190
191        private static final Method THREAD_CALL = new Method(
192                        "threadCall",
193                        Types.VOID,
194                        new Type[]{Types.PAGE_CONTEXT, Types.INT_VALUE}
195                        );
196
197        /*private static final Method UDF_DEFAULT_VALUE = new Method(
198                        "udfDefaultValue",
199                        Types.OBJECT,
200                        new Type[]{Types.PAGE_CONTEXT, Types.INT_VALUE, Types.INT_VALUE}
201                        );*/
202        
203        private static final Method UDF_DEFAULT_VALUE = new Method(
204                        "udfDefaultValue",
205                        Types.OBJECT,
206                        new Type[]{Types.PAGE_CONTEXT, Types.INT_VALUE, Types.INT_VALUE,Types.OBJECT}
207                        );
208
209        private static final Method NEW_COMPONENT_IMPL_INSTANCE = new Method(
210                        "newInstance",
211                        Types.COMPONENT_IMPL,
212                        new Type[]{Types.PAGE_CONTEXT,Types.STRING,Types.BOOLEAN_VALUE}
213                );
214        
215        private static final Method NEW_INTERFACE_IMPL_INSTANCE = new Method(
216                        "newInstance",
217                        Types.INTERFACE_IMPL,
218                        new Type[]{Types.STRING,Types.BOOLEAN_VALUE,Types.MAP}
219                );
220        
221        
222
223        
224
225        // void init(PageContext pc,Component Impl c) throws PageException
226        private static final Method INIT_COMPONENT = new Method(
227                        "initComponent",
228                        Types.VOID,
229                        new Type[]{Types.PAGE_CONTEXT,Types.COMPONENT_IMPL}
230                );
231        private static final Method INIT_INTERFACE = new Method(
232                        "initInterface",
233                        Types.VOID,
234                        new Type[]{Types.INTERFACE_IMPL}
235                );
236        
237
238        // public boolean setMode(int mode) {
239        private static final Method SET_MODE = new Method(
240                        "setMode",
241                        Types.INT_VALUE,
242                        new Type[]{Types.INT_VALUE}
243                );
244        
245        
246
247
248        
249        
250
251
252        private static final Method CONSTR_INTERFACE_IMPL = new Method(
253                        "<init>",
254                        Types.VOID,
255                        new Type[]{
256                                        Types.INTERFACE_PAGE,
257                                                Types.STRING, // extends
258                                                Types.STRING, // hind
259                                                Types.STRING, // display
260                                                Types.STRING, // callpath
261                                                Types.BOOLEAN_VALUE, // relpath
262                                                Types.MAP, //interfaceudfs
263                                                Types.MAP // meta
264                                        }
265                );
266        
267        
268        //void init(PageContext pageContext,ComponentPage componentPage)
269        private static final Method INIT = new Method(
270                        "init",
271                        Types.VOID,
272                        new Type[]{Types.PAGE_CONTEXT,Types.COMPONENT_PAGE}
273                );
274        
275        private static final Method CHECK_INTERFACE = new Method(
276                        "checkInterface",
277                        Types.VOID,
278                        new Type[]{Types.PAGE_CONTEXT,Types.COMPONENT_PAGE}
279                );
280        
281        
282
283        // boolean getOutput()
284        private static final Method GET_OUTPUT = new Method(
285                        "getOutput",
286                        Types.BOOLEAN_VALUE,
287                        new Type[]{}
288                );
289
290
291        private static final Method PUSH_BODY = new Method(
292                        "pushBody",
293                        Types.BODY_CONTENT,
294                        new Type[]{}
295                );
296        
297        /*/ boolean setSilent()
298        private static final Method SET_SILENT = new Method(
299                        "setSilent",
300                        Types.BOOLEAN_VALUE,
301                        new Type[]{}
302                );
303*/
304        // Scope beforeCall(PageContext pc)
305        private static final Method BEFORE_CALL = new Method(
306                        "beforeCall",
307                        Types.VARIABLES,
308                        new Type[]{Types.PAGE_CONTEXT}
309                );
310
311        private static final Method TO_PAGE_EXCEPTION = new Method(
312                        "toPageException",
313                        Types.PAGE_EXCEPTION,
314                        new Type[]{Types.THROWABLE});
315        
316        
317        // boolean unsetSilent()
318        /*private static final Method UNSET_SILENT = new Method(
319                        "unsetSilent",
320                        Types.BOOLEAN_VALUE,
321                        new Type[]{}
322                );*/
323
324        // void afterCall(PageContext pc, Scope parent)
325        private static final Method AFTER_CALL = new Method(
326                        "afterConstructor",
327                        Types.VOID,
328                        new Type[]{Types.PAGE_CONTEXT,Types.VARIABLES}
329                );
330
331        // ComponentImpl(ComponentPage,boolean, String, String, String) NS==No Style
332        
333        
334        // Component Impl(ComponentPage,boolean, String, String, String, String) WS==With Style
335        private static final Method CONSTR_COMPONENT_IMPL = new Method(
336                        "<init>",
337                        Types.VOID,
338                        new Type[]{
339                                        Types.COMPONENT_PAGE,
340                                        Types.BOOLEAN,
341                                        Types.BOOLEAN_VALUE,
342                                        Types.STRING,
343                                        Types.STRING,
344                                        Types.STRING,
345                                        Types.STRING,
346                                        Types.STRING,
347                                        Types.BOOLEAN_VALUE,
348                                        Types.STRING,
349                                        Types.BOOLEAN_VALUE,
350                                        Types.BOOLEAN_VALUE,
351                                        Types.STRUCT_IMPL
352                                }
353                );
354        private static final Method SET_EL = new Method(
355                        "setEL",
356                        Types.OBJECT,
357                        new Type[]{Types.STRING,Types.OBJECT}
358                );
359        private static final Method UNDEFINED_SCOPE = new Method(
360                        "us",
361                        Types.UNDEFINED,
362                        new Type[]{}
363                );
364        private static final Method FLUSH_AND_POP = new Method(
365                        "flushAndPop",
366                        Types.VOID,
367                        new Type[]{Types.PAGE_CONTEXT,Types.BODY_CONTENT}
368                );
369        private static final Method CLEAR_AND_POP = new Method(
370                        "clearAndPop",
371                        Types.VOID,
372                        new Type[]{Types.PAGE_CONTEXT,Types.BODY_CONTENT}
373                );
374        public static final byte CF = (byte)207;
375        public static final byte _33 = (byte)51;
376        //private static final boolean ADD_C33 = false;
377        //private static final String SUB_CALL_UDF = "udfCall";
378        private static final String SUB_CALL_UDF = "_";
379        private static final int DEFAULT_VALUE = 3;
380                
381    private final int version;
382    private final long lastModifed;
383    private final boolean _writeLog;
384        private final String name;
385        private final boolean suppressWSbeforeArg;
386        private final boolean output;
387        private final PageSource pageSource;
388        
389        private boolean isComponent;
390        private boolean isInterface;
391
392        private ArrayList<IFunction> functions=new ArrayList<IFunction>();
393        private ArrayList<TagThread> threads=new ArrayList<TagThread>();
394        private Resource staticTextLocation;
395        private  int off;
396        private int methodCount=0;
397        //private final Config config;
398        private boolean splitIfNecessary;
399    
400        
401        
402    public Page(PageSource pageSource,Resource source,String name,int version, long lastModifed, boolean writeLog, boolean suppressWSbeforeArg, boolean output) {
403        name=name.replace('.', '/');
404        this.name=name;
405        this.version=version;
406        this.lastModifed=lastModifed;
407        
408        this._writeLog=writeLog;
409        this.suppressWSbeforeArg=suppressWSbeforeArg;
410        this.output=output;
411        this.pageSource=pageSource;
412    }
413    
414    /**
415     * result byte code as binary array
416     * @param classFile 
417     * @return byte code
418     * @throws IOException 
419     * @throws TemplateException 
420     */
421    public byte[] execute(PageSource source,Resource classFile) throws BytecodeException {
422        /*
423        // this is done that the Page can be executed more than once
424        if(initFunctions==null)
425                initFunctions=(ArrayList<IFunction>) functions.clone();
426        else
427                functions=initFunctions;
428        if(initThreads==null)
429                initThreads=(ArrayList<TagThread>) threads.clone();
430        else
431                threads=initThreads;
432        methodCount=0;
433        off=0;
434        staticTextLocation=null;
435        
436        
437        print.e(this.functions);
438        print.e(this.threads);*/
439        
440        
441        Resource p = classFile.getParentResource().getRealResource(classFile.getName()+".txt");
442        
443        List<LitString> keys=new ArrayList<LitString>();
444        ClassWriter cw = ASMUtil.getClassWriter(); 
445        
446        ArrayList<String> imports = new ArrayList<String>();
447        getImports(imports, this); 
448        
449        // parent
450        String parent="lucee/runtime/PagePlus";
451        if(isComponent()) parent="lucee/runtime/ComponentPage";
452        else if(isInterface()) parent="lucee/runtime/InterfacePage";
453        
454        cw.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL, name, null, parent, null);
455        Mapping m = source!=null?source.getMapping():null;
456        Config config = m!=null?m.getConfig():null;
457        if(config==null) config=ThreadLocalPageContext.getConfig();
458        String path=config.allowRequestTimeout()?
459                        pageSource.getFullRealpath():pageSource.getPhyscalFile().getAbsolutePath();
460        
461        cw.visitSource(path,null); // when adding more use ; as delimiter
462        
463        // static constructor
464        //GeneratorAdapter statConstrAdapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC,STATIC_CONSTRUCTOR,null,null,cw);
465                BytecodeContext statConstr = null;//new BytecodeContext(null,null,this,externalizer,keys,cw,name,statConstrAdapter,STATIC_CONSTRUCTOR,writeLog(),suppressWSbeforeArg);
466                
467        // constructor
468            GeneratorAdapter constrAdapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC,CONSTRUCTOR_PS,null,null,cw);
469                BytecodeContext constr = new BytecodeContext(source,null,null,this,keys,cw,name,constrAdapter,CONSTRUCTOR_PS,writeLog(),suppressWSbeforeArg,output);
470                constrAdapter.loadThis();
471        Type t=Types.PAGE_PLUS;
472        if(isComponent())t=Types.COMPONENT_PAGE;
473        else if(isInterface())t=Types.INTERFACE_PAGE;
474        
475        constrAdapter.invokeConstructor(t, CONSTRUCTOR);
476        
477     // call _init()
478        constrAdapter.visitVarInsn(Opcodes.ALOAD, 0);
479        constrAdapter.visitMethodInsn(Opcodes.INVOKEVIRTUAL, constr.getClassName(), "initKeys", "()V");
480
481     // private static  ImportDefintion[] test=new ImportDefintion[]{...};
482            {
483                        FieldVisitor fv = cw.visitField(Opcodes.ACC_PRIVATE + Opcodes.ACC_FINAL, 
484                                        "imports", "[Llucee/runtime/component/ImportDefintion;", null, null);
485                        fv.visitEnd();
486                
487                        constrAdapter.visitVarInsn(Opcodes.ALOAD, 0);
488                        ArrayVisitor av=new ArrayVisitor();
489                        av.visitBegin(constrAdapter,Types.IMPORT_DEFINITIONS,imports.size());
490                        int index=0;
491                        Iterator<String> it = imports.iterator();
492                        while(it.hasNext()){
493                                av.visitBeginItem(constrAdapter,index++);
494                                constrAdapter.push(it.next());
495                                ASMConstants.NULL(constrAdapter);
496                                constrAdapter.invokeStatic(Types.IMPORT_DEFINITIONS_IMPL, ID_GET_INSTANCE);
497                                av.visitEndItem(constrAdapter);
498                        }
499                        av.visitEnd();
500                        constrAdapter.visitFieldInsn(Opcodes.PUTFIELD, name, "imports", "[Llucee/runtime/component/ImportDefintion;");
501                                
502                }
503        
504        
505        
506        
507     // getVersion
508         GeneratorAdapter adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL , VERSION, null, null, cw);
509         adapter.push(version);
510         adapter.returnValue();
511         adapter.endMethod();
512         
513         
514    // public ImportDefintion[] getImportDefintions()
515         if(imports.size()>0){
516                 adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL , GET_IMPORT_DEFINITIONS, null, null, cw);
517                 adapter.visitVarInsn(Opcodes.ALOAD, 0);
518             adapter.visitFieldInsn(Opcodes.GETFIELD, name, "imports", "[Llucee/runtime/component/ImportDefintion;");
519                 adapter.returnValue();
520             adapter.endMethod();
521         }
522         else {
523                 adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL , GET_IMPORT_DEFINITIONS, null, null, cw);
524                 adapter.visitInsn(Opcodes.ICONST_0);
525                 adapter.visitTypeInsn(Opcodes.ANEWARRAY, "lucee/runtime/component/ImportDefintion");
526                 adapter.returnValue();
527             adapter.endMethod();
528         }
529
530         
531    // getSourceLastModified
532        adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL , LAST_MOD, null, null, cw);
533        adapter.push(lastModifed);
534        adapter.returnValue();
535        adapter.endMethod();
536        
537    // getCompileTime
538        adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL , COMPILE_TIME, null, null, cw);
539        adapter.push(System.currentTimeMillis());
540        adapter.returnValue();
541        adapter.endMethod();
542   
543    // newInstance/initComponent/call
544        if(isComponent()) {
545                Tag component=getComponent();
546                writeOutNewComponent(statConstr,constr,keys,cw,component);
547                writeOutInitComponent(statConstr,constr,keys,cw,component);
548        }
549        else if(isInterface()) {
550                Tag interf=getInterface();
551                writeOutNewInterface(statConstr,constr,keys,cw,interf);
552                writeOutInitInterface(statConstr,constr,keys,cw,interf);
553        }
554        else {
555                writeOutCall(statConstr,constr,keys,cw);
556        }
557        
558// udfCall     
559        Function[] functions=getFunctions();
560        ConditionVisitor cv;
561        DecisionIntVisitor div;
562    // less/equal than 10 functions
563        if(isInterface()){}
564        else if(functions.length<=10) {
565                adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL , UDF_CALL, null, new Type[]{Types.THROWABLE}, cw);
566            BytecodeContext bc = new BytecodeContext(source,statConstr,constr,this,keys,cw,name,adapter,UDF_CALL,writeLog(),suppressWSbeforeArg,output);
567            if(functions.length==0){}
568            else if(functions.length==1){
569                        ExpressionUtil.visitLine(bc,functions[0].getStart());
570                        functions[0].getBody().writeOut(bc);
571                        ExpressionUtil.visitLine(bc,functions[0].getEnd());
572                }
573                else writeOutUdfCallInner(bc,functions,0,functions.length);
574            adapter.visitInsn(Opcodes.ACONST_NULL);
575            adapter.returnValue();
576            adapter.endMethod(); 
577        }
578   // more than 10 functions
579        else {
580                adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL , UDF_CALL, null, new Type[]{Types.THROWABLE}, cw);
581                BytecodeContext bc = new BytecodeContext(source,statConstr,constr,this,keys,cw,name,adapter,UDF_CALL,writeLog(),suppressWSbeforeArg,output);
582                        cv = new ConditionVisitor();
583                        cv.visitBefore();
584                        int count=0;
585                        for(int i=0;i<functions.length;i+=10) {
586                                cv.visitWhenBeforeExpr();
587                                        div=new DecisionIntVisitor();
588                                                div.visitBegin();
589                                                        adapter.loadArg(2);
590                                                div.visitLT();
591                                                        adapter.push(i+10);
592                                                div.visitEnd(bc);
593                                cv.visitWhenAfterExprBeforeBody(bc);
594                                        
595                                        adapter.visitVarInsn(Opcodes.ALOAD, 0);
596                                        adapter.visitVarInsn(Opcodes.ALOAD, 1);
597                                        adapter.visitVarInsn(Opcodes.ALOAD, 2);
598                                        adapter.visitVarInsn(Opcodes.ILOAD, 3);
599                                        adapter.visitMethodInsn(Opcodes.INVOKEVIRTUAL, name, createFunctionName(++count), "(Llucee/runtime/PageContext;Llucee/runtime/type/UDF;I)Ljava/lang/Object;");
600                                        adapter.visitInsn(Opcodes.ARETURN);//adapter.returnValue();
601                                cv.visitWhenAfterBody(bc);
602                        }
603                        cv.visitAfter(bc);
604                
605                adapter.visitInsn(Opcodes.ACONST_NULL);
606                adapter.returnValue();
607                adapter.endMethod();
608                
609                count=0;
610                Method innerCall;
611                for(int i=0;i<functions.length;i+=10) {
612                        innerCall = new Method(createFunctionName(++count),Types.OBJECT,new Type[]{Types.PAGE_CONTEXT, USER_DEFINED_FUNCTION, Types.INT_VALUE});
613                        
614                        adapter = new GeneratorAdapter(Opcodes.ACC_PRIVATE+Opcodes.ACC_FINAL , innerCall, null, new Type[]{Types.THROWABLE}, cw);
615                        writeOutUdfCallInner(new BytecodeContext(source,statConstr,constr,this,keys,cw,name,adapter,innerCall,writeLog(),suppressWSbeforeArg,output), functions, i, i+10>functions.length?functions.length:i+10);
616                        
617                        adapter.visitInsn(Opcodes.ACONST_NULL);
618                        adapter.returnValue();
619                        adapter.endMethod();
620                }
621        }
622        
623
624     // threadCall
625         TagThread[] threads=getThreads();
626         if(true) {
627                adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL , THREAD_CALL, null, new Type[]{Types.THROWABLE}, cw);
628                if(threads.length>0) writeOutThreadCallInner(new BytecodeContext(source,statConstr,constr,this,keys,cw,name,adapter,THREAD_CALL,writeLog(),suppressWSbeforeArg,output),threads,0,threads.length);
629                //adapter.visitInsn(Opcodes.ACONST_NULL);
630                adapter.returnValue();
631                adapter.endMethod();
632         }
633        
634
635        
636                
637// udfDefaultValue
638    // less/equal than 10 functions
639        if(isInterface()) {}
640        else if(functions.length<=10) {
641                adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL , UDF_DEFAULT_VALUE, null, new Type[]{Types.PAGE_EXCEPTION}, cw);
642                if(functions.length>0)
643                        writeUdfDefaultValueInner(new BytecodeContext(source,statConstr,constr,this,keys,cw,name,adapter,UDF_DEFAULT_VALUE,writeLog(),suppressWSbeforeArg,output),functions,0,functions.length);
644            
645            adapter.loadArg(DEFAULT_VALUE);
646                adapter.returnValue();
647            adapter.endMethod();
648        }
649        else {
650                adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL , UDF_DEFAULT_VALUE, null, new Type[]{Types.PAGE_EXCEPTION}, cw);
651            BytecodeContext bc = new BytecodeContext(source,statConstr,constr,this,keys,cw,name,adapter,UDF_DEFAULT_VALUE,writeLog(),suppressWSbeforeArg,output);
652                cv = new ConditionVisitor();
653                cv.visitBefore();
654                int count=0;
655                for(int i=0;i<functions.length;i+=10) {
656                        cv.visitWhenBeforeExpr();
657                                div=new DecisionIntVisitor();
658                                        div.visitBegin();
659                                                adapter.loadArg(1);
660                                        div.visitLT();
661                                                adapter.push(i+10);
662                                        div.visitEnd(bc);
663                        cv.visitWhenAfterExprBeforeBody(bc);
664                                
665                                adapter.visitVarInsn(Opcodes.ALOAD, 0);
666                                adapter.visitVarInsn(Opcodes.ALOAD, 1);
667                                adapter.visitVarInsn(Opcodes.ILOAD, 2);
668                                adapter.visitVarInsn(Opcodes.ILOAD, 3);
669                                adapter.visitVarInsn(Opcodes.ALOAD, 4);
670                                
671                                adapter.visitMethodInsn(Opcodes.INVOKEVIRTUAL, name, "udfDefaultValue"+(++count), "(Llucee/runtime/PageContext;IILjava/lang/Object;)Ljava/lang/Object;");
672                                adapter.visitInsn(Opcodes.ARETURN);//adapter.returnValue();
673                                
674                        cv.visitWhenAfterBody(bc);
675                }
676                cv.visitAfter(bc);
677        
678        adapter.visitInsn(Opcodes.ACONST_NULL);
679        adapter.returnValue();
680        adapter.endMethod();
681        
682        count=0;
683        Method innerDefaultValue;
684        for(int i=0;i<functions.length;i+=10) {
685                innerDefaultValue = new Method("udfDefaultValue"+(++count),Types.OBJECT,new Type[]{Types.PAGE_CONTEXT, Types.INT_VALUE, Types.INT_VALUE,Types.OBJECT});
686                adapter = new GeneratorAdapter(Opcodes.ACC_PRIVATE+Opcodes.ACC_FINAL , innerDefaultValue, null, new Type[]{Types.PAGE_EXCEPTION}, cw);
687                writeUdfDefaultValueInner(new BytecodeContext(source,statConstr,constr,this,keys,cw,name,adapter,innerDefaultValue,writeLog(),suppressWSbeforeArg,output), functions, i, i+10>functions.length?functions.length:i+10);
688                
689                adapter.loadArg(DEFAULT_VALUE);
690                //adapter.visitInsn(Opcodes.ACONST_NULL);
691                adapter.returnValue();
692                adapter.endMethod();
693        }
694                
695        }
696        
697        
698        // register fields
699        {
700                GeneratorAdapter aInit = new GeneratorAdapter(Opcodes.ACC_PRIVATE+Opcodes.ACC_FINAL , _INIT, null, null, cw);
701            BytecodeContext bcInit = new BytecodeContext(source,statConstr,constr,this,keys,cw,name,aInit,_INIT,writeLog(),suppressWSbeforeArg,output);
702                registerFields(bcInit,keys);
703                aInit.returnValue();
704                aInit.endMethod();
705        }
706
707        //setPageSource(pageSource);
708        constrAdapter.visitVarInsn(Opcodes.ALOAD, 0);
709        constrAdapter.visitVarInsn(Opcodes.ALOAD, 1);
710        constrAdapter.invokeVirtual(t, SET_PAGE_SOURCE);
711        
712     
713        constrAdapter.returnValue();
714        constrAdapter.endMethod();
715        
716        return cw.toByteArray();
717        
718    }
719    
720
721
722
723        private String createFunctionName(int i) {
724                return "udfCall"+Integer.toString(i, Character.MAX_RADIX);
725        }
726
727        private boolean writeLog() {
728                return _writeLog && !isInterface();
729        }
730
731        public static void registerFields(BytecodeContext bc, List<LitString> keys) throws BytecodeException {
732                //if(keys.size()==0) return;
733                GeneratorAdapter ga = bc.getAdapter();
734                
735                FieldVisitor fv = bc.getClassWriter().visitField(Opcodes.ACC_PRIVATE ,
736                                "keys", Types.COLLECTION_KEY_ARRAY.toString(), null, null);
737                fv.visitEnd();
738                
739                int index=0;
740                LitString value;
741                Iterator<LitString> it = keys.iterator();
742                ga.visitVarInsn(Opcodes.ALOAD, 0);
743                ga.push(keys.size());
744                ga.newArray(Types.COLLECTION_KEY);
745                while(it.hasNext()) {
746                        value=it.next();
747                        ga.dup();
748                        ga.push(index++);
749                        //value.setExternalize(false);
750                        ExpressionUtil.writeOutSilent(value,bc, Expression.MODE_REF);
751                        ga.invokeStatic(KEY_IMPL, KEY_INTERN);
752                        ga.visitInsn(Opcodes.AASTORE);
753                }
754                ga.visitFieldInsn(Opcodes.PUTFIELD, bc.getClassName(), "keys", Types.COLLECTION_KEY_ARRAY.toString());
755        }
756
757        private void writeUdfDefaultValueInner(BytecodeContext bc, Function[] functions, int offset, int length) throws BytecodeException {
758                GeneratorAdapter adapter = bc.getAdapter();
759                ConditionVisitor cv = new ConditionVisitor();
760                DecisionIntVisitor div;
761        cv.visitBefore();
762        for(int i=offset;i<length;i++) {
763                cv.visitWhenBeforeExpr();
764                        div = new DecisionIntVisitor();
765                                div.visitBegin();
766                                        adapter.loadArg(1);
767                                div.visitEQ();
768                                        adapter.push(i);
769                                div.visitEnd(bc);
770                cv.visitWhenAfterExprBeforeBody(bc);
771                        writeOutFunctionDefaultValueInnerInner(bc, functions[i]);
772                cv.visitWhenAfterBody(bc);
773        }
774        cv.visitAfter(bc);
775        }
776
777
778        private void writeOutUdfCallInnerIf(BytecodeContext bc,Function[] functions, int offset, int length) throws BytecodeException {
779                GeneratorAdapter adapter = bc.getAdapter();
780                ConditionVisitor cv=new ConditionVisitor();
781        DecisionIntVisitor div;
782        cv.visitBefore();
783        for(int i=offset;i<length;i++) {
784                cv.visitWhenBeforeExpr();
785                        div=new DecisionIntVisitor();
786                                div.visitBegin();
787                                        adapter.loadArg(2);
788                                div.visitEQ();
789                                        adapter.push(i);
790                                div.visitEnd(bc);
791                cv.visitWhenAfterExprBeforeBody(bc);
792                        ExpressionUtil.visitLine(bc, functions[i].getStart());
793                        functions[i].getBody().writeOut(bc);
794                        ExpressionUtil.visitLine(bc, functions[i].getEnd());
795                cv.visitWhenAfterBody(bc);
796        }
797        cv.visitAfter(bc);
798        }
799
800        private void writeOutUdfCallInner(BytecodeContext bc,Function[] functions, int offset, int length) throws BytecodeException {
801                NativeSwitch ns=new NativeSwitch(2,NativeSwitch.ARG_REF,null,null);
802                
803                for(int i=offset;i<length;i++) {
804                ns.addCase(i, functions[i].getBody(),functions[i].getStart(),functions[i].getEnd(),true);
805        }
806        ns._writeOut(bc);
807        }
808        
809        
810
811        private void writeOutThreadCallInner(BytecodeContext bc,TagThread[] threads, int offset, int length) throws BytecodeException {
812                GeneratorAdapter adapter = bc.getAdapter();
813                ConditionVisitor cv=new ConditionVisitor();
814        DecisionIntVisitor div;
815        cv.visitBefore();
816        //print.ln("functions:"+functions.length);
817                for(int i=offset;i<length;i++) {
818                        cv.visitWhenBeforeExpr();
819                                div=new DecisionIntVisitor();
820                                        div.visitBegin();
821                                                adapter.loadArg(1);
822                                        div.visitEQ();
823                                                adapter.push(i);
824                                        div.visitEnd(bc);
825                        cv.visitWhenAfterExprBeforeBody(bc);
826                                Body body = threads[i].getRealBody();
827                                if(body!=null)body.writeOut(bc);
828                        cv.visitWhenAfterBody(bc);
829                }
830        cv.visitAfter(bc);
831        }
832
833        private void writeOutInitComponent(BytecodeContext statConstr,BytecodeContext constr,List<LitString> keys, ClassWriter cw, Tag component) throws BytecodeException {
834                final GeneratorAdapter adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL , INIT_COMPONENT, null, new Type[]{Types.PAGE_EXCEPTION}, cw);
835        BytecodeContext bc=new BytecodeContext(null,statConstr, constr,this,keys,cw,name,adapter,INIT_COMPONENT,writeLog(),suppressWSbeforeArg,output);
836                Label methodBegin=new Label();
837        Label methodEnd=new Label();
838
839                adapter.visitLocalVariable("this", "L"+name+";", null, methodBegin, methodEnd, 0);
840        adapter.visitLabel(methodBegin);
841        
842                // Scope oldData=null;
843                final int oldData=adapter.newLocal(Types.VARIABLES);
844                ASMConstants.NULL(adapter);
845                adapter.storeLocal(oldData);
846                
847                int localBC=adapter.newLocal(Types.BODY_CONTENT);
848                ConditionVisitor cv=new ConditionVisitor();
849                cv.visitBefore();
850                        cv.visitWhenBeforeExpr();
851                                adapter.loadArg(1);
852                                adapter.invokeVirtual(Types.COMPONENT_IMPL, GET_OUTPUT);
853                        cv.visitWhenAfterExprBeforeBody(bc);
854                                ASMConstants.NULL(adapter);
855                        cv.visitWhenAfterBody(bc);
856
857                        cv.visitOtherviseBeforeBody();
858                                adapter.loadArg(0);
859                                adapter.invokeVirtual(Types.PAGE_CONTEXT, PUSH_BODY);
860                        cv.visitOtherviseAfterBody();
861                cv.visitAfter(bc);
862                adapter.storeLocal(localBC);
863                
864                // c.init(pc,this);
865                adapter.loadArg(1);
866                adapter.loadArg(0);
867                adapter.visitVarInsn(Opcodes.ALOAD, 0);
868                adapter.invokeVirtual(Types.COMPONENT_IMPL, INIT);
869                        
870                        
871                //int oldCheckArgs=     pc.undefinedScope().setMode(Undefined.MODE_NO_LOCAL_AND_ARGUMENTS);
872                        final int oldCheckArgs = adapter.newLocal(Types.INT_VALUE);
873                        adapter.loadArg(0);
874                        adapter.invokeVirtual(Types.PAGE_CONTEXT, UNDEFINED_SCOPE);
875                        adapter.push(Undefined.MODE_NO_LOCAL_AND_ARGUMENTS);
876                        adapter.invokeInterface(Types.UNDEFINED, SET_MODE);
877                        adapter.storeLocal(oldCheckArgs);
878                
879                        
880                TryCatchFinallyVisitor tcf=new TryCatchFinallyVisitor(new OnFinally() {
881                        
882                        public void _writeOut(BytecodeContext bc) {
883
884                                // undefined.setMode(oldMode);
885                                adapter.loadArg(0);
886                                adapter.invokeVirtual(Types.PAGE_CONTEXT, UNDEFINED_SCOPE);
887                                adapter.loadLocal(oldCheckArgs,Types.INT_VALUE);
888                                adapter.invokeInterface(Types.UNDEFINED, SET_MODE);
889                                adapter.pop();
890                                
891                                        // c.afterCall(pc,_oldData);
892                                        adapter.loadArg(1);
893                                        adapter.loadArg(0);
894                                        adapter.loadLocal(oldData);
895                                        adapter.invokeVirtual(Types.COMPONENT_IMPL, AFTER_CALL);
896                                
897                                
898                        }
899                },null);
900                tcf.visitTryBegin(bc);
901                        // oldData=c.beforeCall(pc);
902                        adapter.loadArg(1);
903                        adapter.loadArg(0);
904                        adapter.invokeVirtual(Types.COMPONENT_IMPL, BEFORE_CALL);
905                        adapter.storeLocal(oldData);
906                        ExpressionUtil.visitLine(bc, component.getStart());
907                        writeOutCallBody(bc,component.getBody(),IFunction.PAGE_TYPE_COMPONENT);
908                        ExpressionUtil.visitLine(bc, component.getEnd());
909                int t = tcf.visitTryEndCatchBeging(bc);
910                        // BodyContentUtil.flushAndPop(pc,bc);
911                        adapter.loadArg(0);
912                        adapter.loadLocal(localBC);
913                        adapter.invokeStatic(Types.BODY_CONTENT_UTIL, FLUSH_AND_POP);
914                
915                        // throw Caster.toPageException(t);
916                        adapter.loadLocal(t);
917                        adapter.invokeStatic(Types.CASTER, TO_PAGE_EXCEPTION);
918                        adapter.throwException();
919                tcf.visitCatchEnd(bc);
920                
921                adapter.loadArg(0);
922                adapter.loadLocal(localBC);
923                adapter.invokeStatic(Types.BODY_CONTENT_UTIL, CLEAR_AND_POP);
924        
925        adapter.returnValue();
926            adapter.visitLabel(methodEnd);
927            
928            adapter.endMethod();
929        
930        }
931
932        private void writeOutInitInterface(BytecodeContext statConstr,BytecodeContext constr,List<LitString> keys, ClassWriter cw, Tag interf) throws BytecodeException {
933                GeneratorAdapter adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL , INIT_INTERFACE, null, new Type[]{Types.PAGE_EXCEPTION}, cw);
934        BytecodeContext bc=new BytecodeContext(null,statConstr, constr,this,keys,cw,name,adapter,INIT_INTERFACE,writeLog(),suppressWSbeforeArg,output);
935                Label methodBegin=new Label();
936        Label methodEnd=new Label();
937
938                adapter.visitLocalVariable("this", "L"+name+";", null, methodBegin, methodEnd, 0);
939        adapter.visitLabel(methodBegin);
940        
941        ExpressionUtil.visitLine(bc, interf.getStart());
942                writeOutCallBody(bc,interf.getBody(),IFunction.PAGE_TYPE_INTERFACE);
943                ExpressionUtil.visitLine(bc, interf.getEnd());
944                
945        adapter.returnValue();
946            adapter.visitLabel(methodEnd);
947            
948            adapter.endMethod();
949        
950        }
951
952        private Tag getComponent() throws BytecodeException {
953                Iterator it = getStatements().iterator();
954                Statement s;
955                Tag t;
956        while(it.hasNext()) {
957                s=(Statement)it.next();
958                if(s instanceof Tag) {
959                        t=(Tag)s;
960                        if(t.getTagLibTag().getTagClassName().equals("lucee.runtime.tag.Component"))return t;
961                }
962        }
963                throw new BytecodeException("missing component",getStart());
964        }
965        private Tag getInterface() throws BytecodeException {
966                Iterator it = getStatements().iterator();
967                Statement s;
968                Tag t;
969        while(it.hasNext()) {
970                s=(Statement)it.next();
971                if(s instanceof Tag) {
972                        t=(Tag)s;
973                        if(t.getTagLibTag().getTagClassName().equals("lucee.runtime.tag.Interface"))return t;
974                }
975        }
976                throw new BytecodeException("missing interface",getStart());
977        }
978
979        private void writeOutFunctionDefaultValueInnerInner(BytecodeContext bc, Function function) throws BytecodeException {
980                GeneratorAdapter adapter = bc.getAdapter();
981                
982                List<Argument> args = function.getArguments();
983                
984                if(args.size()==0) {
985                        adapter.loadArg(DEFAULT_VALUE);
986                        adapter.returnValue();
987                        return;
988                }
989                        
990                Iterator<Argument> it = args.iterator();
991                Argument arg;
992                ConditionVisitor cv=new ConditionVisitor();
993                DecisionIntVisitor div;
994                cv.visitBefore();
995                int count=0;
996                while(it.hasNext()) {
997                        arg=it.next();
998                        cv.visitWhenBeforeExpr();
999                                div=new DecisionIntVisitor();
1000                                div.visitBegin();
1001                                        adapter.loadArg(2);
1002                                div.visitEQ();
1003                                        adapter.push(count++);
1004                                div.visitEnd(bc);
1005                        cv.visitWhenAfterExprBeforeBody(bc);
1006                                Expression defaultValue = arg.getDefaultValue();
1007                                if(defaultValue!=null) {
1008                                        /*if(defaultValue instanceof Null) {
1009                                                adapter.invokeStatic(NULL, GET_INSTANCE);
1010                                        }
1011                                        else*/ 
1012                                        defaultValue.writeOut(bc, Expression.MODE_REF);
1013                                }
1014                                else 
1015                                        adapter.loadArg(DEFAULT_VALUE);
1016                                        //adapter.visitInsn(Opcodes.ACONST_NULL);
1017                                adapter.returnValue();
1018                        cv.visitWhenAfterBody(bc);
1019                }
1020                cv.visitOtherviseBeforeBody();
1021                        //adapter.visitInsn(ACONST_NULL);
1022                        //adapter.returnValue();
1023                cv.visitOtherviseAfterBody();
1024                cv.visitAfter(bc);
1025        }
1026
1027        private Function[] getFunctions() {
1028                Function[] funcs=new Function[functions.size()];
1029                Iterator it = functions.iterator();
1030                int count=0;
1031                while(it.hasNext()) {
1032                        funcs[count++]=(Function) it.next();
1033                }
1034                return funcs;
1035        }
1036        
1037        private TagThread[] getThreads() {
1038                TagThread[] threads=new TagThread[this.threads.size()];
1039                Iterator it = this.threads.iterator();
1040                int count=0;
1041                while(it.hasNext()) {
1042                        threads[count++]=(TagThread) it.next();
1043                }
1044                return threads;
1045        }
1046        
1047        
1048        public void _writeOut(BytecodeContext bc) throws BytecodeException {
1049                
1050        }
1051
1052        private void writeOutNewComponent(BytecodeContext statConstr,BytecodeContext constr,List<LitString> keys,ClassWriter cw, Tag component) throws BytecodeException {
1053                
1054                GeneratorAdapter adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL , NEW_COMPONENT_IMPL_INSTANCE, null, new Type[]{Types.PAGE_EXCEPTION}, cw);
1055        BytecodeContext bc=new BytecodeContext(null,statConstr, constr,this,keys,cw,name,adapter,NEW_COMPONENT_IMPL_INSTANCE,writeLog(),suppressWSbeforeArg,output);
1056        Label methodBegin=new Label();
1057        Label methodEnd=new Label();
1058        
1059                adapter.visitLocalVariable("this", "L"+name+";", null, methodBegin, methodEnd, 0);
1060                ExpressionUtil.visitLine(bc, component.getStart());
1061        adapter.visitLabel(methodBegin);
1062
1063        
1064                int comp=adapter.newLocal(Types.COMPONENT_IMPL);
1065                adapter.newInstance(Types.COMPONENT_IMPL);
1066                adapter.dup();
1067                
1068                Attribute attr;
1069                // ComponentPage
1070                adapter.visitVarInsn(Opcodes.ALOAD, 0);
1071                adapter.checkCast(Types.COMPONENT_PAGE);
1072
1073                // !!! also check CFMLScriptTransformer.addMetaData if you do any change here !!!
1074                
1075                // Output
1076                attr = component.removeAttribute("output");
1077                if(attr!=null) {
1078                        ExpressionUtil.writeOutSilent(attr.getValue(),bc, Expression.MODE_REF);
1079                }
1080                else ASMConstants.NULL(adapter);
1081
1082                // synchronized 
1083                attr = component.removeAttribute("synchronized");
1084                if(attr!=null) ExpressionUtil.writeOutSilent(attr.getValue(),bc, Expression.MODE_VALUE);
1085                else adapter.push(false);
1086
1087                // extends
1088                attr = component.removeAttribute("extends");
1089                if(attr!=null) ExpressionUtil.writeOutSilent(attr.getValue(),bc, Expression.MODE_REF);
1090                else adapter.push("");
1091 
1092                // implements
1093                attr = component.removeAttribute("implements");
1094                if(attr!=null) ExpressionUtil.writeOutSilent(attr.getValue(),bc, Expression.MODE_REF);
1095                else adapter.push("");
1096                
1097                // hint
1098                attr = component.removeAttribute("hint");
1099                if(attr!=null) {
1100                        Expression value = attr.getValue();
1101                        if(!(value instanceof Literal)){
1102                                value=LitString.toExprString("[runtime expression]");
1103                        }
1104                        ExpressionUtil.writeOutSilent(value,bc, Expression.MODE_REF);
1105                }
1106                else adapter.push("");
1107                
1108                // dspName
1109                attr = component.removeAttribute("displayname");
1110                if(attr==null) attr=component.getAttribute("display");
1111                if(attr!=null) ExpressionUtil.writeOutSilent(attr.getValue(),bc, Expression.MODE_REF);
1112                else adapter.push("");
1113                                
1114                // callpath
1115                adapter.visitVarInsn(Opcodes.ALOAD, 2);
1116                // relpath
1117                adapter.visitVarInsn(Opcodes.ILOAD, 3);
1118                
1119
1120                // style
1121                attr = component.removeAttribute("style");
1122                if(attr!=null) ExpressionUtil.writeOutSilent(attr.getValue(),bc, Expression.MODE_REF);
1123                else adapter.push("");
1124
1125                // persistent
1126                attr = component.removeAttribute("persistent");
1127                boolean persistent=false;
1128                if(attr!=null) {
1129                        persistent=ASMUtil.toBoolean(attr,component.getStart()).booleanValue();
1130                }
1131                
1132                // persistent
1133                attr = component.removeAttribute("accessors");
1134                boolean accessors=false;
1135                if(attr!=null) {
1136                        accessors=ASMUtil.toBoolean(attr,component.getStart()).booleanValue();
1137                }
1138
1139                adapter.push(persistent);
1140                adapter.push(accessors);
1141                //ExpressionUtil.writeOutSilent(attr.getValue(),bc, Expression.MODE_VALUE);
1142                
1143                //adapter.visitVarInsn(Opcodes.ALOAD, 4);
1144                createMetaDataStruct(bc,component.getAttributes(),component.getMetaData());
1145                
1146                adapter.invokeConstructor(Types.COMPONENT_IMPL, CONSTR_COMPONENT_IMPL);
1147                
1148                adapter.storeLocal(comp);
1149                
1150                //Component Impl(ComponentPage componentPage,boolean output, String extend, String hint, String dspName)
1151                
1152                
1153                // initComponent(pc,c);
1154                adapter.visitVarInsn(Opcodes.ALOAD, 0);
1155                adapter.loadArg(0);
1156                adapter.loadLocal(comp);
1157                adapter.invokeVirtual(Types.COMPONENT_PAGE, INIT_COMPONENT);
1158                
1159        adapter.visitLabel(methodEnd);
1160        
1161        // return component;
1162        adapter.loadLocal(comp);
1163
1164        adapter.returnValue();
1165        //ExpressionUtil.visitLine(adapter, component.getEndLine());
1166        adapter.endMethod();
1167                
1168        
1169        }
1170        
1171        private void writeOutNewInterface(BytecodeContext statConstr,BytecodeContext constr,List<LitString> keys,ClassWriter cw, Tag interf) throws BytecodeException {
1172                GeneratorAdapter adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL , NEW_INTERFACE_IMPL_INSTANCE, null, new Type[]{Types.PAGE_EXCEPTION}, cw);
1173        BytecodeContext bc=new BytecodeContext(null,statConstr, constr,this,keys,cw,name,adapter,NEW_INTERFACE_IMPL_INSTANCE,writeLog(),suppressWSbeforeArg,output);
1174        Label methodBegin=new Label();
1175        Label methodEnd=new Label();
1176
1177        
1178                adapter.visitLocalVariable("this", "L"+name+";", null, methodBegin, methodEnd, 0);
1179                ExpressionUtil.visitLine(bc, interf.getStart());
1180        adapter.visitLabel(methodBegin);
1181
1182        //ExpressionUtil.visitLine(adapter, interf.getStartLine());
1183        
1184                int comp=adapter.newLocal(Types.INTERFACE_IMPL);
1185                
1186                
1187                adapter.newInstance(Types.INTERFACE_IMPL);
1188                adapter.dup();
1189                
1190                
1191                Attribute attr;
1192                // Interface Page
1193                adapter.visitVarInsn(Opcodes.ALOAD, 0);
1194                adapter.checkCast(Types.INTERFACE_PAGE);
1195
1196                // extened
1197                attr = interf.removeAttribute("extends");
1198                if(attr!=null) ExpressionUtil.writeOutSilent(attr.getValue(),bc, Expression.MODE_REF);
1199                else adapter.push("");
1200                
1201                // hint
1202                attr = interf.removeAttribute("hint");
1203                if(attr!=null) ExpressionUtil.writeOutSilent(attr.getValue(),bc, Expression.MODE_REF);
1204                else adapter.push("");
1205                
1206                // dspName
1207                attr = interf.removeAttribute("displayname");
1208                if(attr==null) attr=interf.getAttribute("display");
1209                if(attr!=null) ExpressionUtil.writeOutSilent(attr.getValue(),bc, Expression.MODE_REF);
1210                else adapter.push("");
1211                
1212                
1213                // callpath
1214                adapter.visitVarInsn(Opcodes.ALOAD, 1);
1215                // relpath
1216                adapter.visitVarInsn(Opcodes.ILOAD, 2);
1217                
1218                // interface udfs
1219                adapter.visitVarInsn(Opcodes.ALOAD, 3);
1220                
1221                createMetaDataStruct(bc,interf.getAttributes(),interf.getMetaData());
1222                
1223                
1224                adapter.invokeConstructor(Types.INTERFACE_IMPL, CONSTR_INTERFACE_IMPL);
1225                
1226                adapter.storeLocal(comp);
1227                
1228                
1229                
1230                // initInterface(pc,c);
1231                adapter.visitVarInsn(Opcodes.ALOAD, 0);
1232                //adapter.loadArg(0);
1233                adapter.loadLocal(comp);
1234                adapter.invokeVirtual(Types.INTERFACE_PAGE, INIT_INTERFACE);
1235                
1236                adapter.visitLabel(methodEnd);
1237        
1238        
1239        // return interface;
1240        adapter.loadLocal(comp);
1241
1242        adapter.returnValue();
1243        //ExpressionUtil.visitLine(adapter, interf.getEndLine());
1244        adapter.endMethod();
1245                
1246        
1247        }
1248        
1249        public static boolean hasMetaDataStruct(Map attrs, Map meta) {
1250                if((attrs==null || attrs.size()==0) && (meta==null || meta.size()==0)){
1251                        return false;
1252                }
1253                return true;
1254        }
1255        
1256        public static void createMetaDataStruct(BytecodeContext bc, Map attrs, Map meta) throws BytecodeException {
1257                
1258                
1259                GeneratorAdapter adapter = bc.getAdapter();
1260                if((attrs==null || attrs.size()==0) && (meta==null || meta.size()==0)){
1261                        ASMConstants.NULL(bc.getAdapter());
1262                        bc.getAdapter().cast(Types.OBJECT,Types.STRUCT_IMPL);
1263                        return;
1264                }
1265                
1266                int sct=adapter.newLocal(Types.STRUCT_IMPL);
1267                adapter.newInstance(Types.STRUCT_IMPL);
1268                adapter.dup();
1269                adapter.invokeConstructor(Types.STRUCT_IMPL, INIT_STRUCT_IMPL);
1270                adapter.storeLocal(sct);
1271                if(meta!=null) {
1272                        _createMetaDataStruct(bc,adapter,sct,meta);
1273                }
1274                if(attrs!=null) {
1275                        _createMetaDataStruct(bc,adapter,sct,attrs);
1276                }
1277                
1278                
1279                adapter.loadLocal(sct);
1280        }
1281
1282        private static void _createMetaDataStruct(BytecodeContext bc, GeneratorAdapter adapter, int sct, Map attrs) throws BytecodeException {
1283                Attribute attr;
1284                Iterator it = attrs.entrySet().iterator();
1285                Entry entry;
1286                while(it.hasNext()){
1287                        entry = (Map.Entry)it.next();
1288                        attr=(Attribute) entry.getValue();
1289                        adapter.loadLocal(sct);
1290                        adapter.push(attr.getName());
1291                        if(attr.getValue() instanceof Literal)
1292                                ExpressionUtil.writeOutSilent(attr.getValue(),bc,Expression.MODE_REF);
1293                        else
1294                                adapter.push("[runtime expression]");
1295                        
1296                        adapter.invokeVirtual(Types.STRUCT_IMPL, SET_EL);
1297                        adapter.pop();
1298                }
1299        }
1300
1301        private void writeOutCall(BytecodeContext statConstr,BytecodeContext constr,List<LitString> keys,ClassWriter cw) throws BytecodeException {
1302                //GeneratorAdapter adapter = bc.getAdapter();
1303                GeneratorAdapter adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL , CALL, null, new Type[]{Types.THROWABLE}, cw);
1304                Label methodBegin=new Label();
1305                Label methodEnd=new Label();
1306        
1307                        adapter.visitLocalVariable("this", "L"+name+";", null, methodBegin, methodEnd, 0);
1308                adapter.visitLabel(methodBegin);
1309
1310                                writeOutCallBody(new BytecodeContext(null,statConstr, constr,this,keys,cw,name,adapter,CALL,writeLog(),suppressWSbeforeArg,output),this,IFunction.PAGE_TYPE_REGULAR);
1311                
1312                adapter.visitLabel(methodEnd);
1313                adapter.returnValue();
1314        adapter.endMethod();
1315    }
1316
1317
1318        private void writeOutCallBody(BytecodeContext bc,Body body, int pageType) throws BytecodeException {
1319                // Other
1320                List<IFunction> functions=new ArrayList<IFunction>();
1321                getFunctions(functions,bc,body,pageType);
1322                
1323                String className = Types.UDF_PROPERTIES_ARRAY.toString();
1324                //FieldVisitor fv = bc.getClassWriter().visitField(Opcodes.ACC_PRIVATE , "udfs",className , null, null);
1325                //fv.visitEnd();
1326                
1327                BytecodeContext constr = bc.getConstructor();
1328                GeneratorAdapter cga = constr.getAdapter();
1329                
1330                cga.visitVarInsn(Opcodes.ALOAD, 0);
1331                cga.push(functions.size());
1332                //cga.visitTypeInsn(Opcodes.ANEWARRAY, Types.UDF_PROPERTIES.toString());
1333                cga.newArray(Types.UDF_PROPERTIES);
1334                cga.visitFieldInsn(Opcodes.PUTFIELD, bc.getClassName(), "udfs", className);
1335                
1336                
1337                Iterator<IFunction> it = functions.iterator();
1338                while(it.hasNext()){
1339                        it.next().writeOut(bc, pageType);
1340                }
1341                
1342                if(pageType==IFunction.PAGE_TYPE_COMPONENT) {
1343                        GeneratorAdapter adapter = bc.getAdapter();
1344                        adapter.loadArg(1);
1345                        adapter.loadArg(0);
1346                        adapter.visitVarInsn(Opcodes.ALOAD, 0);
1347                        adapter.invokeVirtual(Types.COMPONENT_IMPL, CHECK_INTERFACE);
1348
1349                }
1350                if(pageType!=IFunction.PAGE_TYPE_INTERFACE){
1351                        //BodyBase.writeOut(bc.getStaticConstructor(),bc.getConstructor(),bc.getKeys(),body.getStatements(), bc);
1352                        BodyBase.writeOut(bc,body);
1353                }
1354        }
1355
1356        private static void getImports(List<String> list,Body body) throws BytecodeException {
1357                if(ASMUtil.isEmpty(body)) return;
1358                Statement stat;
1359                List stats = body.getStatements();
1360        int len=stats.size();
1361        for(int i=0;i<len;i++) {
1362                stat = (Statement)stats.get(i);
1363                
1364                // IFunction
1365                if(stat instanceof TagImport && !StringUtil.isEmpty(((TagImport)stat).getPath(),true)) {
1366                        ImportDefintion id = ImportDefintionImpl.getInstance(((TagImport) stat).getPath(), null);
1367                        if(id!=null && (!list.contains(id.toString()) && !list.contains(id.getPackage()+".*"))){
1368                                list.add(id.toString());
1369                        }
1370                        stats.remove(i);
1371                        len--;
1372                        i--;
1373                        
1374                }
1375                else if(stat instanceof HasBody) getImports(list, ((HasBody)stat).getBody());
1376                else if(stat instanceof HasBodies) {
1377                        Body[] bodies=((HasBodies)stat).getBodies();
1378                        for(int y=0;y<bodies.length;y++) {
1379                                getImports(list,bodies[y]);
1380                        }
1381                }
1382        }
1383        }
1384        
1385        
1386        private static void getFunctions(List<IFunction> functions,BytecodeContext bc, Body body, int pageType) throws BytecodeException {
1387                //writeOutImports(bc, body, pageType);
1388                if(ASMUtil.isEmpty(body)) return;
1389                Statement stat;
1390                List stats = body.getStatements();
1391        int len=stats.size();
1392        for(int i=0;i<len;i++) {
1393                stat = (Statement)stats.get(i);
1394                
1395                // IFunction
1396                if(stat instanceof IFunction) {
1397                        functions.add((IFunction)stat);
1398                        //((IFunction)stat).writeOut(bc,pageType);
1399                        stats.remove(i);
1400                        len--;
1401                        i--;
1402                }
1403                else if(stat instanceof HasBody) getFunctions(functions,bc, ((HasBody)stat).getBody(), pageType);
1404                else if(stat instanceof HasBodies) {
1405                        Body[] bodies=((HasBodies)stat).getBodies();
1406                        for(int y=0;y<bodies.length;y++) {
1407                                getFunctions(functions,bc,bodies[y] , pageType);
1408                        }
1409                        
1410                }
1411        }
1412        }
1413
1414        /**
1415         * @return if it is a component
1416         */
1417        public boolean isComponent() {
1418                return isComponent;
1419        }
1420        
1421        /**
1422         * set if the page is a component or not
1423         * @param cfc 
1424         */
1425        public void setIsComponent(boolean isComponent) {
1426                this.isComponent = isComponent;
1427        }
1428
1429        /**
1430         * @return if it is a component
1431         */
1432        public boolean isInterface() {
1433                return isInterface;
1434        }
1435        
1436
1437        public boolean isPage() {
1438                return !isInterface && !isComponent;
1439        }
1440        
1441        /**
1442         * set if the page is a component or not
1443         * @param cfc 
1444         */
1445        public void setIsInterface(boolean isInterface) {
1446                this.isInterface = isInterface;
1447        }
1448        /**
1449         * @return the lastModifed
1450         */
1451        public long getLastModifed() {
1452                return lastModifed;
1453        }
1454
1455
1456        public int[] addFunction(IFunction function) {
1457                int[] indexes=new int[2];
1458                Iterator<IFunction> it = functions.iterator();
1459                while(it.hasNext()){
1460                        if(it.next() instanceof FunctionImpl)indexes[IFunction.ARRAY_INDEX]++;
1461                }
1462                indexes[IFunction.VALUE_INDEX]=functions.size();
1463                functions.add(function);
1464                return indexes;
1465        }
1466        
1467        public int addThread(TagThread thread) {
1468                threads.add(thread);
1469                return threads.size()-1;
1470        }
1471
1472        public static byte[] setSourceLastModified(byte[] barr,  long lastModified) {
1473                ClassReader cr = new ClassReader(barr);
1474                ClassWriter cw = ASMUtil.getClassWriter();
1475                ClassVisitor ca = new SourceLastModifiedClassAdapter(cw,lastModified);
1476                cr.accept(ca, 0);
1477                return cw.toByteArray();
1478        }
1479
1480        public Range registerString(BytecodeContext bc, String str) throws IOException {
1481                boolean append=true;
1482                
1483                if(staticTextLocation==null) {
1484                        PageSource ps = bc.getPageSource();
1485                        Mapping m = ps.getMapping();
1486                        staticTextLocation=m.getClassRootDirectory();
1487                        
1488                        staticTextLocation.mkdirs();
1489                        staticTextLocation=staticTextLocation.getRealResource(ps.getJavaName()+".txt");
1490                        if(staticTextLocation.exists()) append=false;
1491                        else staticTextLocation.createFile(true);
1492                        
1493                        off=0;
1494                }
1495                
1496                
1497                IOUtil.write(staticTextLocation, str, CharsetUtil.UTF8, append);
1498                Range r = new Range(off,str.length());
1499                off+=str.length();
1500                return r;
1501        }
1502
1503        public int getMethodCount() {
1504                return ++methodCount;
1505        }
1506
1507        public PageSource getPageSource() {
1508                return pageSource;
1509        }
1510
1511        public void setSplitIfNecessary(boolean splitIfNecessary) {
1512                this.splitIfNecessary=splitIfNecessary;
1513        }
1514        
1515        public boolean getSplitIfNecessary() {
1516                return splitIfNecessary;
1517        }
1518        
1519
1520
1521}
1522        class SourceLastModifiedClassAdapter extends ClassVisitor {
1523
1524                private long lastModified;
1525                public SourceLastModifiedClassAdapter(ClassWriter cw, long lastModified) {
1526                        super(Opcodes.ASM4,cw);
1527                        this.lastModified=lastModified;
1528                }
1529                public MethodVisitor visitMethod(int access,String name, String desc,  String signature, String[] exceptions) {
1530                        
1531                        if(!name.equals("getSourceLastModified"))return super.visitMethod(access,name, desc, signature, exceptions);
1532                        
1533                        MethodVisitor mv = cv.visitMethod(access,name, desc, signature, exceptions);
1534                        mv.visitCode();
1535                        mv.visitLdcInsn(Long.valueOf(lastModified));
1536                        mv.visitInsn(Opcodes.LRETURN);
1537                        mv.visitEnd();
1538                        return mv;
1539                }
1540
1541        }