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.util;
020
021import java.io.IOException;
022import java.lang.reflect.Field;
023import java.util.ArrayList;
024import java.util.Iterator;
025import java.util.List;
026
027import lucee.aprint;
028import lucee.commons.digest.MD5;
029import lucee.commons.io.IOUtil;
030import lucee.commons.io.res.Resource;
031import lucee.commons.lang.ExceptionUtil;
032import lucee.commons.lang.StringUtil;
033import lucee.runtime.component.Property;
034import lucee.runtime.config.Config;
035import lucee.runtime.exp.PageException;
036import lucee.runtime.net.rpc.AxisCaster;
037import lucee.runtime.op.Caster;
038import lucee.runtime.op.Decision;
039import lucee.runtime.type.dt.TimeSpanImpl;
040import lucee.runtime.type.util.ArrayUtil;
041import lucee.runtime.type.util.ListUtil;
042import lucee.transformer.bytecode.Body;
043import lucee.transformer.bytecode.BytecodeContext;
044import lucee.transformer.bytecode.BytecodeException;
045import lucee.transformer.bytecode.Literal;
046import lucee.transformer.bytecode.Page;
047import lucee.transformer.bytecode.Position;
048import lucee.transformer.bytecode.ScriptBody;
049import lucee.transformer.bytecode.Statement;
050import lucee.transformer.bytecode.cast.Cast;
051import lucee.transformer.bytecode.cast.CastBoolean;
052import lucee.transformer.bytecode.cast.CastDouble;
053import lucee.transformer.bytecode.cast.CastString;
054import lucee.transformer.bytecode.expression.ExprDouble;
055import lucee.transformer.bytecode.expression.ExprString;
056import lucee.transformer.bytecode.expression.Expression;
057import lucee.transformer.bytecode.expression.var.Argument;
058import lucee.transformer.bytecode.expression.var.BIF;
059import lucee.transformer.bytecode.expression.var.DataMember;
060import lucee.transformer.bytecode.expression.var.Member;
061import lucee.transformer.bytecode.expression.var.NullExpression;
062import lucee.transformer.bytecode.expression.var.Variable;
063import lucee.transformer.bytecode.expression.var.VariableString;
064import lucee.transformer.bytecode.literal.Identifier;
065import lucee.transformer.bytecode.literal.LitBoolean;
066import lucee.transformer.bytecode.literal.LitDouble;
067import lucee.transformer.bytecode.literal.LitLong;
068import lucee.transformer.bytecode.literal.LitString;
069import lucee.transformer.bytecode.statement.FlowControl;
070import lucee.transformer.bytecode.statement.FlowControlBreak;
071import lucee.transformer.bytecode.statement.FlowControlContinue;
072import lucee.transformer.bytecode.statement.FlowControlFinal;
073import lucee.transformer.bytecode.statement.FlowControlRetry;
074import lucee.transformer.bytecode.statement.HasBody;
075import lucee.transformer.bytecode.statement.PrintOut;
076import lucee.transformer.bytecode.statement.Switch;
077import lucee.transformer.bytecode.statement.TryCatchFinally;
078import lucee.transformer.bytecode.statement.tag.Attribute;
079import lucee.transformer.bytecode.statement.tag.Tag;
080import lucee.transformer.bytecode.statement.tag.TagComponent;
081import lucee.transformer.bytecode.statement.tag.TagTry;
082import lucee.transformer.bytecode.util.SourceNameClassVisitor.SourceInfo;
083import lucee.transformer.cfml.evaluator.EvaluatorException;
084
085import org.objectweb.asm.ClassReader;
086import org.objectweb.asm.ClassWriter;
087import org.objectweb.asm.FieldVisitor;
088import org.objectweb.asm.Label;
089import org.objectweb.asm.MethodVisitor;
090import org.objectweb.asm.Opcodes;
091import org.objectweb.asm.Type;
092import org.objectweb.asm.commons.GeneratorAdapter;
093import org.objectweb.asm.commons.Method;
094
095public final class ASMUtil {
096        
097        //private static final int VERSION_2=1;
098        //private static final int VERSION_3=2;
099
100        public static final short TYPE_ALL=0;
101        public static final short TYPE_BOOLEAN=1;
102        public static final short TYPE_NUMERIC=2;
103        public static final short TYPE_STRING=4;
104        
105        
106        
107        
108        //private static int version=0;
109        
110        private final static Method CONSTRUCTOR_OBJECT = Method.getMethod("void <init> ()");
111        private static final Method _SRC_NAME = new Method("_srcName",
112                                Types.STRING,
113                                new Type[]{}
114                        );
115        //private static final String VERSION_MESSAGE = "you use an invalid version of the ASM Jar, please update your jar files";
116        private static long id=0;
117                
118        /**
119         * Gibt zurueck ob das direkt uebergeordnete Tag mit dem uebergebenen Full-Name (Namespace und Name) existiert.
120         * @param el Startelement, von wo aus gesucht werden soll.
121         * @param fullName Name des gesuchten Tags.
122         * @return Existiert ein solches Tag oder nicht.
123         */
124        public static boolean hasAncestorTag(Tag tag, String fullName) {
125            return getAncestorTag(tag, fullName)!=null;
126        }
127        
128
129        /**
130         * Gibt das uebergeordnete CFXD Tag Element zurueck, falls dies nicht existiert wird null zurueckgegeben.
131         * @param el Element von dem das parent Element zurueckgegeben werden soll.
132         * @return uebergeordnete CFXD Tag Element
133         */
134        public static Tag getParentTag(Tag tag) {
135                Statement p=tag.getParent();
136                if(p==null)return null;
137                p=p.getParent();
138                if(p instanceof Tag) return (Tag) p;
139                return null;
140        }
141
142        public static boolean isParentTag(Tag tag,String fullName)      {
143                Tag p = getParentTag(tag);
144                if(p==null) return false;
145                return p.getFullname().equalsIgnoreCase(fullName);
146                
147        }
148        public static boolean isParentTag(Tag tag,Class clazz)  {
149                Tag p = getParentTag(tag);
150                if(p==null) return false;
151                return p.getClass()==clazz;
152                
153        }
154        
155        public static boolean hasAncestorRetryFCStatement(Statement stat,String label) {
156                return getAncestorRetryFCStatement(stat,null,label)!=null;
157        }
158        
159        public static boolean hasAncestorBreakFCStatement(Statement stat,String label) {
160                return getAncestorBreakFCStatement(stat,null,label)!=null;
161        }
162        
163        public static boolean hasAncestorContinueFCStatement(Statement stat,String label) {
164                return getAncestorContinueFCStatement(stat,null,label)!=null;
165        }
166        
167        
168        
169        public static FlowControlRetry getAncestorRetryFCStatement(Statement stat, List<FlowControlFinal> finallyLabels, String label) {
170                return (FlowControlRetry) getAncestorFCStatement(stat, finallyLabels, FlowControl.RETRY,label);
171        }
172        public static FlowControlBreak getAncestorBreakFCStatement(Statement stat, List<FlowControlFinal> finallyLabels, String label) {
173                return (FlowControlBreak) getAncestorFCStatement(stat, finallyLabels, FlowControl.BREAK,label);
174        }
175        
176        public static FlowControlContinue getAncestorContinueFCStatement(Statement stat, List<FlowControlFinal> finallyLabels, String label) {
177                return (FlowControlContinue) getAncestorFCStatement(stat, finallyLabels, FlowControl.CONTINUE,label);
178        }
179
180        private static FlowControl getAncestorFCStatement(Statement stat, List<FlowControlFinal> finallyLabels, int flowType, String label) {
181                Statement parent = stat;
182                FlowControlFinal fcf;
183                while(true)     {
184                        parent=parent.getParent();
185                        if(parent==null)return null;
186                        if(
187                           ((flowType==FlowControl.RETRY && parent instanceof FlowControlRetry) || 
188                           (flowType==FlowControl.CONTINUE && parent instanceof FlowControlContinue) || 
189                           (flowType==FlowControl.BREAK && parent instanceof FlowControlBreak))
190                           &&
191                                labelMatch((FlowControl)parent,label))  {
192                                if(parent instanceof ScriptBody){
193                                        List<FlowControlFinal> _finallyLabels=finallyLabels==null?null:new ArrayList<FlowControlFinal>();
194                                        
195                                        FlowControl scriptBodyParent = getAncestorFCStatement(parent,_finallyLabels,flowType,label);
196                                        if(scriptBodyParent!=null) {
197                                                if(finallyLabels!=null){
198                                                        Iterator<FlowControlFinal> it = _finallyLabels.iterator();
199                                                        while(it.hasNext()){
200                                                                finallyLabels.add(it.next());
201                                                        }
202                                                }
203                                                return scriptBodyParent;
204                                        }
205                                        return (FlowControl)parent;
206                                }
207                                return (FlowControl) parent;
208                        }
209                        
210                        // only if not last
211                        if(finallyLabels!=null){
212                                fcf = parent.getFlowControlFinal();
213                                if(fcf!=null){
214                                        finallyLabels.add(fcf);
215                                }
216                        }
217                        
218                }
219        }
220        
221        private static boolean labelMatch(FlowControl fc, String label) {
222                if(StringUtil.isEmpty(label,true)) return true;
223                String fcl = fc.getLabel();
224                if(StringUtil.isEmpty(fcl,true)) return false;
225                
226                return label.trim().equalsIgnoreCase(fcl.trim());
227        }
228
229
230        public static void leadFlow(BytecodeContext bc,Statement stat, int flowType, String label) throws BytecodeException {
231                List<FlowControlFinal> finallyLabels=new ArrayList<FlowControlFinal>();
232                
233                FlowControl fc;
234                String name;
235                if(FlowControl.BREAK==flowType) {
236                        fc=ASMUtil.getAncestorBreakFCStatement(stat,finallyLabels,label);
237                        name="break";
238                }
239                else if(FlowControl.CONTINUE==flowType) {
240                        fc=ASMUtil.getAncestorContinueFCStatement(stat,finallyLabels,label);
241                        name="continue";
242                }
243                else  {
244                        fc=ASMUtil.getAncestorRetryFCStatement(stat,finallyLabels,label);
245                        name="retry";
246                }
247                
248                if(fc==null)
249                        throw new BytecodeException(name+" must be inside a loop (for,while,do-while,<cfloop>,<cfwhile> ...)",stat.getStart());
250                
251                GeneratorAdapter adapter = bc.getAdapter();
252                
253                Label end;
254                if(FlowControl.BREAK==flowType) end=((FlowControlBreak)fc).getBreakLabel();
255                else if(FlowControl.CONTINUE==flowType) end=((FlowControlContinue)fc).getContinueLabel();
256                else  end=((FlowControlRetry)fc).getRetryLabel();
257
258                // first jump to all final labels
259                FlowControlFinal[] arr = finallyLabels.toArray(new FlowControlFinal[finallyLabels.size()]);
260                if(arr.length>0) {
261                        FlowControlFinal fcf;
262                        for(int i=0;i<arr.length;i++){
263                                fcf=arr[i];
264                                
265                                // first
266                                if(i==0) {
267                                        adapter.visitJumpInsn(Opcodes.GOTO, fcf.getFinalEntryLabel());
268                                }
269                                
270                                // last
271                                if(arr.length==i+1) fcf.setAfterFinalGOTOLabel(end);
272                                else fcf.setAfterFinalGOTOLabel(arr[i+1].getFinalEntryLabel());
273                        }
274                        
275                }
276                else bc.getAdapter().visitJumpInsn(Opcodes.GOTO, end);
277        }
278        
279        
280        
281        public static boolean hasAncestorTryStatement(Statement stat) {
282                return getAncestorTryStatement(stat)!=null;
283        }
284        
285        public static Statement getAncestorTryStatement(Statement stat) {
286                Statement parent = stat;
287                while(true)     {
288                        parent=parent.getParent();
289                        if(parent==null)return null;
290                        
291                        if(parent instanceof TagTry)    {
292                                return parent;
293                        }
294                        else if(parent instanceof TryCatchFinally)      {
295                                return parent;
296                        }
297                }
298        }
299        
300
301
302        
303        /**
304         * Gibt ein uebergeordnetes Tag mit dem uebergebenen Full-Name (Namespace und Name) zurueck, 
305         * falls ein solches existiert, andernfalls wird null zurueckgegeben.
306         * @param el Startelement, von wo aus gesucht werden soll.
307         * @param fullName Name des gesuchten Tags.
308         * @return  uebergeornetes Element oder null.
309         */
310        public static Tag getAncestorTag(Tag tag, String fullName) {
311                Statement parent=tag;
312                while(true)     {
313                        parent=parent.getParent();
314                        if(parent==null)return null;
315                        if(parent instanceof Tag)       {
316                                tag=(Tag) parent;
317                                if(tag.getFullname().equalsIgnoreCase(fullName))
318                                        return tag;
319                        }
320                }
321        }
322        
323        
324
325    /**
326     * extract the content of a attribut
327     * @param cfxdTag
328     * @param attrName
329     * @return attribute value
330     * @throws EvaluatorException
331     */
332        public static Boolean getAttributeBoolean(Tag tag,String attrName) throws EvaluatorException {
333                Boolean b= getAttributeLiteral(tag, attrName).getBoolean(null);
334                if(b==null)throw new EvaluatorException("attribute ["+attrName+"] must be a constant boolean value");
335                return b;
336    }
337    
338    /**
339     * extract the content of a attribut
340     * @param cfxdTag
341     * @param attrName
342     * @return attribute value
343     * @throws EvaluatorException
344     */
345        public static Boolean getAttributeBoolean(Tag tag,String attrName, Boolean defaultValue) {
346                Literal lit=getAttributeLiteral(tag, attrName,null);
347                if(lit==null) return defaultValue;
348                return lit.getBoolean(defaultValue); 
349    }
350
351
352    /**
353     * extract the content of a attribut
354     * @param cfxdTag
355     * @param attrName
356     * @return attribute value
357     * @throws EvaluatorException
358     */
359        public static String getAttributeString(Tag tag,String attrName) throws EvaluatorException {
360                return getAttributeLiteral(tag, attrName).getString();
361    }
362    
363    /**
364     * extract the content of a attribut
365     * @param cfxdTag
366     * @param attrName
367     * @return attribute value
368     * @throws EvaluatorException
369     */
370        public static String getAttributeString(Tag tag,String attrName, String defaultValue) {
371                Literal lit=getAttributeLiteral(tag, attrName,null);
372                if(lit==null) return defaultValue;
373                return lit.getString(); 
374    }
375        
376        /**
377     * extract the content of a attribut
378     * @param cfxdTag
379     * @param attrName
380     * @return attribute value
381     * @throws EvaluatorException
382     */
383        public static Literal getAttributeLiteral(Tag tag,String attrName) throws EvaluatorException {
384                Attribute attr = tag.getAttribute(attrName);
385                if(attr!=null && attr.getValue() instanceof Literal) return ((Literal)attr.getValue());
386        throw new EvaluatorException("attribute ["+attrName+"] must be a constant value");
387    }
388        
389        
390    
391    /**
392     * extract the content of a attribut
393     * @param cfxdTag
394     * @param attrName
395     * @return attribute value
396     * @throws EvaluatorException
397     */
398        public static Literal getAttributeLiteral(Tag tag,String attrName, Literal defaultValue) {
399                Attribute attr = tag.getAttribute(attrName);
400                if(attr!=null && attr.getValue() instanceof Literal) return ((Literal)attr.getValue());
401        return defaultValue; 
402    }
403        
404        
405        
406
407        /**
408         * Prueft ob das das angegebene Tag in der gleichen Ebene nach dem angegebenen Tag vorkommt.
409         * @param tag Ausgangspunkt, nach diesem tag darf das angegebene nicht vorkommen.
410         * @param nameToFind Tag Name der nicht vorkommen darf
411         * @return kommt das Tag vor.
412         */
413        public static boolean hasSisterTagAfter(Tag tag, String nameToFind) {
414                Body body=(Body) tag.getParent();
415                List<Statement> stats = body.getStatements();
416                Iterator<Statement> it = stats.iterator();
417                Statement other;
418                
419                boolean isAfter=false;
420                while(it.hasNext()) {
421                        other=it.next();
422                        
423                        if(other instanceof Tag) {
424                                if(isAfter) {
425                                        if(((Tag) other).getTagLibTag().getName().equals(nameToFind))
426                                        return true;
427                                }
428                                else if(other == tag) isAfter=true;
429                        }
430                }
431                return false;
432        }
433        
434        /**
435         * Prueft ob das angegebene Tag innerhalb seiner Ebene einmalig ist oder nicht.
436         * @param tag Ausgangspunkt, nach diesem tag darf das angegebene nicht vorkommen.
437         * @return kommt das Tag vor.
438         */
439        public static boolean hasSisterTagWithSameName(Tag tag) {
440                
441                Body body=(Body) tag.getParent();
442                List<Statement> stats = body.getStatements();
443                Iterator<Statement> it = stats.iterator();
444                Statement other;
445                String name=tag.getTagLibTag().getName();
446                
447                while(it.hasNext()) {
448                        other=it.next();
449                        if(other != tag && other instanceof Tag && ((Tag) other).getTagLibTag().getName().equals(name))
450                                        return true;
451                        
452                }
453                return false;
454        }
455
456        /**
457         * remove this tag from his parent body
458         * @param tag
459         */
460        public static void remove(Tag tag) {
461                Body body=(Body) tag.getParent();
462                body.getStatements().remove(tag);
463        }
464
465        /**
466         * replace src with trg
467         * @param src
468         * @param trg
469         */
470        public static void replace(Tag src, Tag trg, boolean moveBody) {
471                trg.setParent(src.getParent());
472                
473                Body p=(Body) src.getParent();
474                List<Statement> stats = p.getStatements();
475                Iterator<Statement> it = stats.iterator();
476                Statement stat;
477                int count=0;
478                
479                while(it.hasNext()) {
480                        stat=it.next();
481                        if(stat==src) {
482                                if(moveBody && src.getBody()!=null)src.getBody().setParent(trg);
483                                stats.set(count, trg);
484                                break;
485                        }
486                        count++;
487                }
488        }
489        
490        public static Page getAncestorPage(Statement stat) throws BytecodeException {
491                Statement parent=stat;
492                while(true)     {
493                        parent=parent.getParent();
494                        if(parent==null) {
495                                throw new BytecodeException("missing parent Statement of Statement",stat.getStart());
496                                //return null;
497                        }
498                        if(parent instanceof Page)      return (Page) parent;
499                }
500        }
501        
502        public static Page getAncestorPage(Statement stat, Page defaultValue) {
503                Statement parent=stat;
504                while(true)     {
505                        parent=parent.getParent();
506                        if(parent==null) {
507                                return defaultValue;
508                        }
509                        if(parent instanceof Page)      return (Page) parent;
510                }
511        }
512        
513        public static void listAncestor(Statement stat) {
514                Statement parent=stat;
515                aprint.o(stat);
516                while(true)     {
517                        parent=parent.getParent();
518                        if(parent instanceof Page)aprint.o("page-> "+ ((Page)parent).getPageSource().getDisplayPath());
519                        else aprint.o("parent-> "+ parent);
520                        if(parent==null) break;
521                }
522        }
523        
524        
525        public static Tag getAncestorComponent(Statement stat) throws BytecodeException {
526                //print.ln("getAncestorPage:"+stat);
527                Statement parent=stat;
528                while(true)     {
529                        parent=parent.getParent();
530                        //print.ln(" - "+parent);
531                        if(parent==null) {
532                                throw new BytecodeException("missing parent Statement of Statement",stat.getStart());
533                                //return null;
534                        }
535                        if(parent instanceof TagComponent)
536                        //if(parent instanceof Tag && "component".equals(((Tag)parent).getTagLibTag().getName()))       
537                                return (Tag) parent;
538                }
539        }
540        
541        public static Statement getRoot(Statement stat) {
542                while(true)     {
543                        if(isRoot(stat))        {
544                                return stat;
545                        }
546                        stat=stat.getParent();
547                }
548        }
549
550
551
552    public static boolean isRoot(Statement statement) { 
553        //return statement instanceof Page || (statement instanceof Tag && "component".equals(((Tag)statement).getTagLibTag().getName()));
554        return statement instanceof Page || statement instanceof TagComponent;
555    }
556        
557        public static void invokeMethod(GeneratorAdapter adapter, Type type, Method method) {
558                if(type.getClass().isInterface())
559                        adapter.invokeInterface(type, method);
560                else
561                        adapter.invokeVirtual(type, method);
562        }
563
564    public static byte[] createPojo(String className, ASMProperty[] properties,Class parent,Class[] interfaces, String srcName) throws PageException {
565        className=className.replace('.', '/');
566        className=className.replace('\\', '/');
567        className=ListUtil.trim(className, "/");
568        String[] inter=null;
569        if(interfaces!=null){
570                inter=new String[interfaces.length];
571                for(int i=0;i<inter.length;i++){
572                        inter[i]=interfaces[i].getName().replace('.', '/');
573                }
574        }
575    // CREATE CLASS     
576                ClassWriter cw = ASMUtil.getClassWriter();
577        cw.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC, className, null, parent.getName().replace('.', '/'), inter);
578        String md5;
579        try{
580                md5=createMD5(properties);
581        }
582        catch(Throwable t){
583                        ExceptionUtil.rethrowIfNecessary(t);
584                md5="";
585                t.printStackTrace();
586        }
587        
588        FieldVisitor fv = cw.visitField(Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_STATIC, "_md5_", "Ljava/lang/String;", null, md5);
589        fv.visitEnd();
590        
591        
592    // Constructor
593        GeneratorAdapter adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC,CONSTRUCTOR_OBJECT,null,null,cw);
594        adapter.loadThis();
595        adapter.invokeConstructor(toType(parent,true), CONSTRUCTOR_OBJECT);
596        adapter.returnValue();
597        adapter.endMethod();
598    
599        // properties
600        for(int i=0;i<properties.length;i++){
601                createProperty(cw,className,properties[i]);
602        }
603        
604        // complexType src
605        if(!StringUtil.isEmpty(srcName)) {
606                GeneratorAdapter _adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL+ Opcodes.ACC_STATIC , _SRC_NAME, null, null, cw);
607                _adapter.push(srcName);
608                _adapter.returnValue();
609                _adapter.endMethod();
610        }
611        
612        cw.visitEnd();
613        return cw.toByteArray();
614    }
615    
616    private static void createProperty(ClassWriter cw,String classType, ASMProperty property) throws PageException {
617                String name = property.getName();
618                Type type = property.getASMType();
619                Class clazz = property.getClazz();
620                
621                cw.visitField(Opcodes.ACC_PRIVATE, name, type.toString(), null, null).visitEnd();
622                
623                int load=loadFor(type);
624                //int sizeOf=sizeOf(type);
625                
626        // get<PropertyName>():<type>
627                Type[] types=new Type[0];
628                Method method = new Method((clazz==boolean.class?"get":"get")+(name),type,types);
629            GeneratorAdapter adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC , method, null, null, cw);
630            
631            Label start = new Label();
632            adapter.visitLabel(start);
633            
634            adapter.visitVarInsn(Opcodes.ALOAD, 0);
635                        adapter.visitFieldInsn(Opcodes.GETFIELD, classType, name, type.toString());
636                        adapter.returnValue();
637                        
638                        Label end = new Label();
639                        adapter.visitLabel(end);
640                        adapter.visitLocalVariable("this", "L"+classType+";", null, start, end, 0);
641                        adapter.visitEnd();
642                        
643                        adapter.endMethod();
644                        
645                        
646                        
647                        
648                
649                // set<PropertyName>(object):void
650                        types=new Type[]{type};
651                        method = new Method("set"+(name),Types.VOID,types);
652            adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC , method, null, null, cw);
653            
654            start = new Label();
655            adapter.visitLabel(start);
656            adapter.visitVarInsn(Opcodes.ALOAD, 0);
657            adapter.visitVarInsn(load, 1);
658            adapter.visitFieldInsn(Opcodes.PUTFIELD, classType, name, type.toString());
659                        
660                        adapter.visitInsn(Opcodes.RETURN);
661                        end = new Label();
662                        adapter.visitLabel(end);
663                        adapter.visitLocalVariable("this", "L"+classType+";", null, start, end, 0);
664                        adapter.visitLocalVariable(name, type.toString(), null, start, end, 1);
665                        //adapter.visitMaxs(0, 0);//.visitMaxs(sizeOf+1, sizeOf+1);// hansx
666                        adapter.visitEnd();
667        
668                        adapter.endMethod();
669                        
670                        
671                        
672                        
673        }
674
675    public static int loadFor(Type type) {
676        if(type.equals(Types.BOOLEAN_VALUE) || type.equals(Types.INT_VALUE) || type.equals(Types.CHAR) || type.equals(Types.SHORT_VALUE))
677                return Opcodes.ILOAD;
678        if(type.equals(Types.FLOAT_VALUE))
679                return Opcodes.FLOAD;
680        if(type.equals(Types.LONG_VALUE))
681                return Opcodes.LLOAD;
682        if(type.equals(Types.DOUBLE_VALUE))
683                return Opcodes.DLOAD;
684        return Opcodes.ALOAD;
685        }
686
687    public static int sizeOf(Type type) {
688        if(type.equals(Types.LONG_VALUE) || type.equals(Types.DOUBLE_VALUE))
689                return 2;
690        return 1;
691        }
692
693
694        /**
695     * translate a string cfml type definition to a Type Object
696     * @param cfType
697     * @param axistype
698     * @return
699     * @throws PageException
700     */
701    public static Type toType(String cfType, boolean axistype) throws PageException {
702                return toType(Caster.cfTypeToClass(cfType), axistype);
703        }
704
705    /**
706     * translate a string cfml type definition to a Type Object
707     * @param cfType
708     * @param axistype
709     * @return
710     * @throws PageException
711     */
712    public static Type toType(Class type, boolean axistype) {
713                if(axistype)type=AxisCaster.toAxisTypeClass(type);
714                return Type.getType(type);      
715        }
716    
717
718        public static String createMD5(ASMProperty[] props) {
719                
720                StringBuffer sb=new StringBuffer();
721                for(int i=0;i<props.length;i++){
722                        sb.append("name:"+props[i].getName()+";");
723                        if(props[i] instanceof Property){
724                                sb.append("type:"+((Property)props[i]).getType()+";");
725                        }
726                        else {
727                                try {
728                                        sb.append("type:"+props[i].getASMType()+";");
729                                        
730                                } 
731                                catch (PageException e) {}
732                        }
733                }
734                try {
735                        return MD5.getDigestAsString(sb.toString());
736                } catch (IOException e) {
737                        return "";
738                }
739        }
740
741
742
743        public static void removeLiterlChildren(Tag tag, boolean recursive) {
744                Body body=tag.getBody();
745                if(body!=null) {
746                List<Statement> list = body.getStatements();
747                Statement[] stats = list.toArray(new Statement[list.size()]);
748                PrintOut po;
749                Tag t;
750                for(int i=0;i<stats.length;i++) {
751                if(stats[i] instanceof PrintOut) {
752                        po=(PrintOut) stats[i];
753                        if(po.getExpr() instanceof Literal) {
754                                body.getStatements().remove(po);
755                        }
756                }
757                else if(recursive && stats[i] instanceof Tag) {
758                        t=(Tag) stats[i];
759                        if(t.getTagLibTag().isAllowRemovingLiteral()) {
760                                removeLiterlChildren(t, recursive);
761                        }
762                }
763            }
764        }
765        }
766
767
768        public synchronized static String getId() {
769                if(id<0)id=0;
770                return StringUtil.addZeros(++id,6);
771        }
772
773
774        public static boolean isEmpty(Body body) {
775                return body==null || body.isEmpty();
776        }
777
778
779        /**
780         * @param adapter
781         * @param expr
782         * @param mode
783         */
784        public static void pop(GeneratorAdapter adapter, Expression expr,int mode) {
785                if(mode==Expression.MODE_VALUE && (expr instanceof ExprDouble))adapter.pop2();
786                else adapter.pop();
787        }
788        public static void pop(GeneratorAdapter adapter, Type type) {
789                if(type.equals(Types.DOUBLE_VALUE))adapter.pop2();
790                else if(type.equals(Types.VOID));
791                else adapter.pop();
792        }
793
794
795        public static ClassWriter getClassWriter() {
796                return new ClassWriter(ClassWriter.COMPUTE_MAXS);//|ClassWriter.COMPUTE_FRAMES);
797                
798        }
799
800
801        public static String createOverfowMethod(String prefix, int id) { // pattern is used in function callstackget
802                if(StringUtil.isEmpty(prefix)) prefix="call";
803                return prefix+"_"+StringUtil.addZeros(id,6);
804        }
805        
806        public static boolean isOverfowMethod(String name) {
807                return name.length()>6 && Decision.isNumeric(name.substring(name.length()-6,name.length()));
808                //return name.startsWith("_call") && name.length()>=11;
809        }
810
811
812        public static boolean isDotKey(ExprString expr) {
813                return expr instanceof LitString && !((LitString)expr).fromBracket();
814        }
815
816        public static String toString(Expression exp,String defaultValue) {
817                try {
818                        return toString(exp);
819                } catch (BytecodeException e) {
820                        return defaultValue;
821                }
822        }
823        public static String toString(Expression exp) throws BytecodeException {
824                if(exp instanceof Variable) {
825                        return toString(VariableString.toExprString(exp));
826                }
827                else if(exp instanceof VariableString) {
828                        return ((VariableString)exp).castToString();
829                }
830                else if(exp instanceof Literal) {
831                        return ((Literal)exp).toString();
832                }
833                return null;
834        }
835
836
837        public static Boolean toBoolean(Attribute attr, Position start) throws BytecodeException {
838                if(attr==null)
839                        throw new BytecodeException("attribute does not exist",start);
840                
841                if(attr.getValue() instanceof Literal){
842                        Boolean b=((Literal)attr.getValue()).getBoolean(null);
843                        if(b!=null) return b; 
844                }
845                throw new BytecodeException("attribute ["+attr.getName()+"] must be a constant boolean value",start);
846                
847                
848        }
849        public static Boolean toBoolean(Attribute attr, int line, Boolean defaultValue) {
850                if(attr==null)
851                        return defaultValue;
852                
853                if(attr.getValue() instanceof Literal){
854                        Boolean b=((Literal)attr.getValue()).getBoolean(null);
855                        if(b!=null) return b; 
856                }
857                return defaultValue;    
858        }
859
860
861        public static boolean isCFC(Statement s) {
862                Statement p;
863                while((p=s.getParent())!=null){
864                        s=p;
865                }
866                
867                return true;
868        }
869
870        public static boolean isLiteralAttribute(Tag tag, String attrName, short type,boolean required,boolean throwWhenNot) throws EvaluatorException {
871                return isLiteralAttribute(tag,tag.getAttribute(attrName), type, required, throwWhenNot);
872        }
873        
874        
875        public static boolean isLiteralAttribute(Tag tag,Attribute attr, short type,boolean required,boolean throwWhenNot) throws EvaluatorException {
876                String strType="/constant";
877                if(attr!=null && !isNull(attr.getValue())) {
878                        
879                        switch(type){
880                        case TYPE_ALL:
881                                if(attr.getValue() instanceof Literal) return true;
882                        break;
883                        case TYPE_BOOLEAN:
884                                if(CastBoolean.toExprBoolean(attr.getValue()) instanceof LitBoolean) return true;
885                                strType=" boolean";
886                        break;
887                        case TYPE_NUMERIC:
888                                if(CastDouble.toExprDouble(attr.getValue()) instanceof LitDouble) return true;
889                                strType=" numeric";
890                        break;
891                        case TYPE_STRING:
892                                if(CastString.toExprString(attr.getValue()) instanceof LitString) return true;
893                                strType=" string";
894                        break;
895                        }
896                        if(!throwWhenNot) return false;
897                        throw new EvaluatorException("Attribute ["+attr.getName()+"] of the Tag ["+tag.getFullname()+"] must be a literal"+strType+" value. "+
898                                        "attributes java class type "+attr.getValue().getClass().getName());
899                }
900                if(required){
901                        if(!throwWhenNot) return false;
902                        throw new EvaluatorException("Attribute ["+attr.getName()+"] of the Tag ["+tag.getFullname()+"] is required");
903                }
904                return false;
905        }
906
907
908        public static boolean isNull(Expression expr) {
909                if(expr instanceof NullExpression) return true;
910                if(expr instanceof Cast) {
911                        return isNull(((Cast)expr).getExpr());
912                }
913                return false;
914        }
915
916
917        public static boolean isRefType(Type type) {
918                return 
919                !(type==Types.BYTE_VALUE || type==Types.BOOLEAN_VALUE || type==Types.CHAR || type==Types.DOUBLE_VALUE || 
920                type==Types.FLOAT_VALUE || type==Types.INT_VALUE || type==Types.LONG_VALUE || type==Types.SHORT_VALUE);
921        }
922
923
924        public static Type toRefType(Type type) {
925                if(type==Types.BYTE_VALUE) return Types.BYTE;
926                if(type==Types.BOOLEAN_VALUE) return Types.BOOLEAN;
927                if(type==Types.CHAR) return Types.CHARACTER;
928                if(type==Types.DOUBLE_VALUE) return Types.DOUBLE;
929                if(type==Types.FLOAT_VALUE) return Types.FLOAT;
930                if(type==Types.INT_VALUE) return Types.INTEGER;
931                if(type==Types.LONG_VALUE) return Types.LONG;
932                if(type==Types.SHORT_VALUE) return Types.SHORT;
933                
934                return type;
935        }
936        
937        /**
938         * return value type only when there is one
939         * @param type
940         * @return
941         */
942        public static Type toValueType(Type type) {
943                if(type==Types.BYTE) return Types.BYTE_VALUE;
944                if(type==Types.BOOLEAN) return Types.BOOLEAN_VALUE;
945                if(type==Types.CHARACTER) return Types.CHAR;
946                if(type==Types.DOUBLE) return Types.DOUBLE_VALUE;
947                if(type==Types.FLOAT) return Types.FLOAT_VALUE;
948                if(type==Types.INTEGER) return Types.INT_VALUE;
949                if(type==Types.LONG) return Types.LONG_VALUE;
950                if(type==Types.SHORT) return Types.SHORT_VALUE;
951                
952                return type;
953        }
954
955
956        public static Class getValueTypeClass(Type type, Class defaultValue) {
957
958                if(type==Types.BYTE_VALUE) return byte.class;
959                if(type==Types.BOOLEAN_VALUE) return boolean.class;
960                if(type==Types.CHAR) return char.class;
961                if(type==Types.DOUBLE_VALUE) return double.class;
962                if(type==Types.FLOAT_VALUE) return float.class;
963                if(type==Types.INT_VALUE) return int.class;
964                if(type==Types.LONG_VALUE) return long.class;
965                if(type==Types.SHORT_VALUE) return short.class;
966                
967                return defaultValue;
968        }
969
970        public static ASMProperty[] toASMProperties(Property[] properties) {
971                ASMProperty[] asmp=new ASMProperty[properties.length];
972                for(int i=0;i<asmp.length;i++){
973                        asmp[i]=(ASMProperty) properties[i];
974                }
975                return asmp;
976        }
977        
978
979        public static boolean containsComponent(Body body) {
980                if(body==null) return false;
981                
982                Iterator<Statement> it = body.getStatements().iterator();
983                while(it.hasNext()){
984                        if(it.next() instanceof TagComponent)return true;
985                }
986                return false;
987        }
988
989
990        public static void dummy1(BytecodeContext bc) {
991                bc.getAdapter().visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J");
992                bc.getAdapter().visitInsn(Opcodes.POP2);
993        }
994        public static void dummy2(BytecodeContext bc) {
995                bc.getAdapter().visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/System", "nanoTime", "()J");
996                bc.getAdapter().visitInsn(Opcodes.POP2);
997        }
998
999
1000        /**
1001         * convert a clas array to a type array
1002         * @param classes
1003         * @return
1004         */
1005        public static Type[] toTypes(Class<?>[] classes) {
1006                if(classes==null || classes.length==0) 
1007                        return new Type[0];
1008                
1009                Type[] types=new Type[classes.length];
1010                for(int i=0;i<classes.length;i++)    {
1011                        types[i]=Type.getType(classes[i]);
1012                }
1013                return types;
1014        }
1015
1016
1017        public static String display(ExprString name) {
1018                if(name instanceof Literal) {
1019                        if(name instanceof Identifier) 
1020                                return ((Identifier)name).getRaw();
1021                        return ((Literal)name).getString();
1022                        
1023                }
1024                return name.toString();
1025        }
1026
1027
1028        public static Literal cachedWithinValue(Expression val) throws EvaluatorException {
1029                if(val instanceof Literal) {
1030                        Literal l=(Literal)val;
1031                        
1032                        // double == days
1033                        Double d = l.getDouble(null);
1034                        if(d!=null) {
1035                                return new LitLong(TimeSpanImpl.fromDays(d.doubleValue()).getMillis(), null, null);
1036                        }
1037                        
1038                        // request
1039                        String str=l.getString();
1040                        if(str!=null && "request".equalsIgnoreCase(str.trim()))
1041                                return new LitString("request",null,null);
1042                        
1043                        throw cacheWithinException();
1044                }
1045                // createTimespan
1046                else if(val instanceof Variable) {
1047                        Variable var=(Variable)val;
1048                        if(var.getMembers().size()==1) {
1049                                Member first = var.getFirstMember();
1050                                if(first instanceof BIF) {
1051                                        BIF bif=(BIF) first;
1052                                        if("createTimeSpan".equalsIgnoreCase(bif.getFlf().getName())) {
1053                                                Argument[] args = bif.getArguments();
1054                                                int len=ArrayUtil.size(args);
1055                                                if(len>=4 && len<=5) {
1056                                                        double days=toDouble(args[0].getValue());
1057                                                        double hours=toDouble(args[1].getValue());
1058                                                        double minutes=toDouble(args[2].getValue());
1059                                                        double seconds=toDouble(args[3].getValue());
1060                                                        double millis=len==5?toDouble(args[4].getValue()):0;
1061                                                        return new LitLong(new TimeSpanImpl((int)days,(int)hours,(int)minutes,(int)seconds,(int)millis).getMillis(),null,null);
1062                                                }
1063                                        }
1064                                }
1065                        }
1066                }
1067                throw cacheWithinException();
1068        }
1069
1070
1071
1072        private static EvaluatorException cacheWithinException() {
1073                return new EvaluatorException("value of cachedWithin must be a literal timespan, like 0.1 or createTimespan(1,2,3,4) or the string \"request\" or the string \"smart\"");
1074        }
1075
1076
1077        private static double toDouble(Expression e) throws EvaluatorException {
1078                if(!(e instanceof Literal)) 
1079                        throw new EvaluatorException("Paremeters of the function createTimeSpan have to be literal numeric values in this context");
1080                Double d = ((Literal)e).getDouble(null);
1081                if(d==null)
1082                        throw new EvaluatorException("Paremeters of the function createTimeSpan have to be literal numeric values in this context");
1083                
1084                return d.doubleValue();
1085        }
1086
1087        public static void visitLabel(GeneratorAdapter ga, Label label) {
1088                if(label!=null) ga.visitLabel(label);
1089        }
1090        
1091
1092        
1093        public static String getClassName(Resource res) throws IOException{
1094                byte[] src=IOUtil.toBytes(res);
1095                ClassReader cr = new ClassReader(src);
1096                return cr.getClassName();
1097        }
1098        
1099        public static String getClassName(byte[] barr){
1100                return new ClassReader(barr).getClassName();
1101        }
1102
1103
1104        public static SourceInfo getSourceInfo(Config config,Class clazz, boolean onlyCFC) throws IOException {
1105                return SourceNameClassVisitor.getSourceInfo(config,clazz, onlyCFC);
1106        }
1107
1108        public static boolean hasOnlyDataMembers(Variable var) {
1109                Iterator<Member> it = var.getMembers().iterator();
1110                Member m;
1111                while(it.hasNext()){
1112                        m = it.next();
1113                        if(!(m instanceof DataMember)) return false;
1114                }
1115                return true;
1116        }
1117
1118
1119        public static int count(List<Statement> statements, boolean recursive) {
1120                if(statements==null) return 0;
1121                int count=0;
1122                Iterator<Statement> it = statements.iterator();
1123                while(it.hasNext()){
1124                        count+=count(it.next(),recursive);
1125                }
1126                return count;
1127        }
1128
1129
1130        public static int count(Statement s, boolean recursive) {
1131                int count=1;
1132                if(recursive && s instanceof HasBody) {
1133                        Body b = ((HasBody) s).getBody();
1134                        if(b!=null) count+=count(b.getStatements(),recursive);
1135                }
1136                return count;
1137        }
1138
1139        public static void dump(Statement s, int level) {
1140                
1141                for(int i=0;i<level;i++)System.err.print("-");
1142                aprint.e(s.getClass().getName());
1143                
1144                if(s instanceof HasBody) {
1145                        Body b = ((HasBody) s).getBody();
1146                        if(b!=null) {
1147                                Iterator<Statement> it = b.getStatements().iterator();
1148                                while(it.hasNext()){
1149                                        dump(it.next(),level+1);
1150                                }
1151                        }
1152                }
1153        }
1154        
1155
1156
1157        public static void size(ClassWriter cw) {
1158                try {
1159                        MethodVisitor mw=null;
1160                        
1161                        Field[] fields = cw.getClass().getDeclaredFields();
1162                        Field f;
1163                        for(int i=0;i<fields.length;i++){
1164                                f=fields[i];
1165                                if(f.getType().getName().equals("org.objectweb.asm.MethodWriter")) {
1166                                        f.setAccessible(true);
1167                                        mw = (MethodVisitor) f.get(cw);
1168                                        break;
1169                                }
1170                        }
1171                }
1172                catch (Throwable t) {
1173                        ExceptionUtil.rethrowIfNecessary(t);
1174                        // TODO Auto-generated catch block
1175                        t.printStackTrace();
1176                }
1177        }
1178
1179
1180        public static void createEmptyStruct(GeneratorAdapter adapter) {
1181                adapter.newInstance(Types.STRUCT_IMPL);
1182                adapter.dup();
1183                adapter.invokeConstructor(Types.STRUCT_IMPL, Page.INIT_STRUCT_IMPL);
1184        }
1185        
1186        public static void createEmptyArray(GeneratorAdapter adapter) {
1187                adapter.newInstance(Types.ARRAY_IMPL);
1188                adapter.dup();
1189                adapter.invokeConstructor(Types.ARRAY_IMPL, Switch.INIT);
1190        }
1191}