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
015package org.apache.tapestry5.internal.plastic;
016
017import java.util.Map;
018
019import org.apache.tapestry5.internal.plastic.asm.Label;
020import org.apache.tapestry5.internal.plastic.asm.MethodVisitor;
021import org.apache.tapestry5.internal.plastic.asm.Opcodes;
022import org.apache.tapestry5.plastic.LocalVariable;
023import 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 */
029public 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}