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.statement;
020
021import java.util.ArrayList;
022import java.util.Iterator;
023import java.util.List;
024
025import lucee.transformer.bytecode.Body;
026import lucee.transformer.bytecode.BytecodeContext;
027import lucee.transformer.bytecode.BytecodeException;
028import lucee.transformer.bytecode.Position;
029import lucee.transformer.bytecode.Statement;
030import lucee.transformer.bytecode.util.ExpressionUtil;
031
032import org.objectweb.asm.Label;
033import org.objectweb.asm.commons.GeneratorAdapter;
034
035public final class NativeSwitch extends StatementBaseNoFinal implements FlowControlBreak,FlowControlContinue,HasBodies {
036
037        public static final short LOCAL_REF=0;
038        public static final short ARG_REF=1;
039        public static final short PRIMITIVE=1;
040        
041        
042        private int value;
043        private Label end;
044        private Statement defaultCase;
045        List<Case> cases=new ArrayList<Case>();
046        private Label[] labels=new Label[0];
047        private int[] values=new int[0];
048        private short type;
049        
050        public NativeSwitch(int value, short type, Position start, Position end) {
051                super(start, end);
052                this.value=value;
053                this.type=type;
054        }
055
056        public void _writeOut(BytecodeContext bc) throws BytecodeException {
057                end = new Label();
058                GeneratorAdapter adapter = bc.getAdapter();
059
060                if(type==LOCAL_REF) adapter.loadLocal(value);
061                else if(type==ARG_REF) adapter.loadArg(value);
062                else adapter.push(value);
063                
064                Label beforeDefault = new Label();
065                adapter.visitLookupSwitchInsn(beforeDefault, values, labels);
066                
067                Iterator<Case> it = cases.iterator();
068                Case c;
069                while(it.hasNext()) {
070                        c= it.next();
071                        adapter.visitLabel(c.label);
072                        ExpressionUtil.visitLine(bc, c.startPos);
073                        c.body.writeOut(bc);
074                        ExpressionUtil.visitLine(bc, c.endPos);
075                        if(c.doBreak){
076                                adapter.goTo(end);
077                        }
078                }
079                
080                
081                adapter.visitLabel(beforeDefault);
082                if(defaultCase!=null)defaultCase.writeOut(bc);
083                adapter.visitLabel(end);
084
085        }
086        
087        public void addCase(int value, Statement body,Position start,Position end,boolean doBreak) {
088                
089                Case nc = new Case(value,body,start,end,doBreak);
090
091                Label[] labelsTmp = new Label[cases.size()+1];
092                int[] valuesTmp = new int[cases.size()+1];
093                
094                int count=0;
095                boolean hasAdd=false;
096                for(int i=0;i<labels.length;i++) {
097                        if(!hasAdd && nc.value<values[i]) {
098                                labelsTmp[count]=nc.label;
099                                valuesTmp[count]=nc.value;
100                                count++;
101                                hasAdd=true;
102                        }
103                        labelsTmp[count]=labels[i];
104                        valuesTmp[count]=values[i];
105                        count++;
106                }
107                if(!hasAdd) {
108                        labelsTmp[labels.length]=nc.label;
109                        valuesTmp[values.length]=nc.value;
110                }
111                labels=labelsTmp;
112                values=valuesTmp;
113                
114                
115                cases.add(nc);
116        }
117        
118        public void addDefaultCase(Statement defaultStatement) {
119                this.defaultCase=defaultStatement;
120        }
121        
122        class Case {
123
124                public boolean doBreak;
125                private int value;
126                private Statement body;
127                private Label label=new Label();
128                private Position startPos;
129                private Position endPos;
130
131                public Case(int value, Statement body,Position startline,Position endline, boolean doBreak) {
132                        this.value=value;
133                        this.body=body;
134                        this.startPos=startline;
135                        this.endPos=endline;
136                        this.doBreak=doBreak;
137                }
138
139        }
140
141        /**
142         *
143         * @see lucee.transformer.bytecode.statement.FlowControl#getBreakLabel()
144         */
145        public Label getBreakLabel() {
146                return end;
147        }
148
149        /**
150         *
151         * @see lucee.transformer.bytecode.statement.FlowControl#getContinueLabel()
152         */
153        public Label getContinueLabel() {
154                return end;
155        }
156
157        /**
158         * @see lucee.transformer.bytecode.statement.HasBodies#getBodies()
159         */
160        public Body[] getBodies() {
161                if(cases==null) {
162                        if(defaultCase!=null) return new Body[]{(Body) defaultCase};
163                        return new Body[]{};
164                }
165                
166                int len=cases.size(),count=0;
167                if(defaultCase!=null)len++;
168                Body[] bodies=new Body[len];
169                Case c;
170                Iterator<Case> it = cases.iterator();
171                while(it.hasNext()) {
172                        c=it.next();
173                        bodies[count++]=(Body) c.body;
174                }
175                if(defaultCase!=null)bodies[count++]=(Body) defaultCase;
176                
177                return bodies;
178        }
179
180        @Override
181        public String getLabel() {
182                return null;
183        }
184}