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 }