001    // Copyright 2011 The Apache Software Foundation
002    //
003    // Licensed under the Apache License, Version 2.0 (the "License");
004    // you may not use this file except in compliance with the License.
005    // You may obtain a copy of the License at
006    //
007    // http://www.apache.org/licenses/LICENSE-2.0
008    //
009    // Unless required by applicable law or agreed to in writing, software
010    // distributed under the License is distributed on an "AS IS" BASIS,
011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012    // See the License for the specific language governing permissions and
013    // limitations under the License.
014    
015    package org.apache.tapestry5.internal.plastic;
016    
017    import java.util.Map;
018    
019    import org.apache.tapestry5.internal.plastic.asm.Label;
020    import org.apache.tapestry5.internal.plastic.asm.MethodVisitor;
021    import org.apache.tapestry5.internal.plastic.asm.Opcodes;
022    import org.apache.tapestry5.plastic.LocalVariable;
023    import org.apache.tapestry5.plastic.MethodDescription;
024    
025    /**
026     * Stores information about the method whose instructions are being constructed, to make it easier
027     * to share data across multiple instances.
028     */
029    public class InstructionBuilderState implements Opcodes
030    {
031        final MethodDescription description;
032    
033        final MethodVisitor visitor;
034    
035        final NameCache nameCache;
036    
037        int localIndex;
038    
039        int varSuffix;
040    
041        static class LVInfo
042        {
043            final int width, offset, loadOpcode, storeOpcode;
044    
045            final Label end;
046    
047            public LVInfo(int width, int offset, int loadOpcode, int storeOpcode, Label end)
048            {
049                this.width = width;
050                this.offset = offset;
051                this.loadOpcode = loadOpcode;
052                this.storeOpcode = storeOpcode;
053                this.end = end;
054            }
055        }
056    
057        /** Map from LocalVariable to Integer offset. */
058        final Map<LocalVariable, LVInfo> locals = PlasticInternalUtils.newMap();
059    
060        /** Index for argument (0 is first true argument); allows for double-width primitive types. */
061        final int[] argumentIndex;
062    
063        /** Opcode used to load argument (0 is first true argument). */
064        final int[] argumentLoadOpcode;
065    
066        protected InstructionBuilderState(MethodDescription description, MethodVisitor visitor, NameCache nameCache)
067        {
068            this.description = description;
069            this.visitor = visitor;
070            this.nameCache = nameCache;
071    
072            // TODO: Account for static methods?
073    
074            int argCount = description.argumentTypes.length;
075    
076            argumentIndex = new int[argCount];
077            argumentLoadOpcode = new int[argCount];
078    
079            // first argument index is for "this"
080    
081            int offset = 1;
082    
083            for (int i = 0; i < argCount; i++)
084            {
085                PrimitiveType type = PrimitiveType.getByName(description.argumentTypes[i]);
086    
087                argumentIndex[i] = offset++;
088                argumentLoadOpcode[i] = type == null ? ALOAD : type.loadOpcode;
089    
090                if (type != null && type.isWide())
091                    offset++;
092            }
093    
094            localIndex = offset;
095        }
096    
097        /** Creates a new Label and adds it to the method. */
098        Label newLabel()
099        {
100            Label result = new Label();
101    
102            visitor.visitLabel(result);
103    
104            return result;
105        }
106    
107        LocalVariable startVariable(String type)
108        {
109            Label start = newLabel();
110            Label end = new Label();
111    
112            PrimitiveType ptype = PrimitiveType.getByName(type);
113    
114            int width = (ptype != null && ptype.isWide()) ? 2 : 1;
115    
116            int loadOpcode = ptype == null ? ALOAD : ptype.loadOpcode;
117            int storeOpcode = ptype == null ? ASTORE : ptype.storeOpcode;
118    
119            LVInfo info = new LVInfo(width, localIndex, loadOpcode, storeOpcode, end);
120    
121            localIndex += width;
122    
123            LocalVariable var = new LocalVariableImpl(type);
124    
125            locals.put(var, info);
126    
127            visitor.visitLocalVariable(nextVarName(), nameCache.toDesc(type), null, start, end, info.offset);
128    
129            return var;
130        }
131    
132        void load(LocalVariable var)
133        {
134            LVInfo info = locals.get(var);
135    
136            visitor.visitVarInsn(info.loadOpcode, info.offset);
137        }
138    
139        void store(LocalVariable var)
140        {
141            LVInfo info = locals.get(var);
142    
143            visitor.visitVarInsn(info.storeOpcode, info.offset);
144        }
145    
146        void stopVariable(LocalVariable variable)
147        {
148            LVInfo info = locals.get(variable);
149    
150            visitor.visitLabel(info.end);
151    
152            locals.remove(variable);
153    
154            localIndex -= info.width;
155        }
156    
157        private String nextVarName()
158        {
159            return "var" + varSuffix++;
160        }
161    }