    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    }