001 /*
002 * Copyright 2010-2013 JetBrains s.r.o.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017 package org.jetbrains.jet.codegen.signature;
018
019 import com.intellij.util.containers.Stack;
020 import org.jetbrains.annotations.NotNull;
021 import org.jetbrains.annotations.Nullable;
022 import org.jetbrains.asm4.Type;
023 import org.jetbrains.asm4.commons.Method;
024 import org.jetbrains.asm4.signature.SignatureVisitor;
025 import org.jetbrains.asm4.signature.SignatureWriter;
026 import org.jetbrains.asm4.util.CheckSignatureAdapter;
027 import org.jetbrains.jet.lang.resolve.java.AsmTypeConstants;
028 import org.jetbrains.jet.lang.resolve.name.Name;
029 import org.jetbrains.jet.lang.types.Variance;
030
031 import java.util.ArrayList;
032 import java.util.List;
033
034 public class BothSignatureWriter {
035 public enum Mode {
036 METHOD(CheckSignatureAdapter.METHOD_SIGNATURE),
037 CLASS(CheckSignatureAdapter.CLASS_SIGNATURE),
038 TYPE(CheckSignatureAdapter.TYPE_SIGNATURE);
039
040 private final int asmType;
041
042 Mode(int asmType) {
043 this.asmType = asmType;
044 }
045 }
046
047 private final SignatureWriter signatureWriter = new SignatureWriter();
048 private final SignatureVisitor signatureVisitor;
049
050 private final List<JvmMethodParameterSignature> kotlinParameterTypes = new ArrayList<JvmMethodParameterSignature>();
051
052 private int jvmCurrentTypeArrayLevel;
053 private Type jvmCurrentType;
054 private Type jvmReturnType;
055
056 private JvmMethodParameterKind currentParameterKind;
057
058 private final boolean needGenerics;
059
060 private boolean generic = false;
061
062 public BothSignatureWriter(Mode mode, boolean needGenerics) {
063 this.needGenerics = needGenerics;
064
065 this.signatureVisitor = new CheckSignatureAdapter(mode.asmType, signatureWriter);
066 }
067
068 private final Stack<SignatureVisitor> visitors = new Stack<SignatureVisitor>();
069
070 private void push(SignatureVisitor visitor) {
071 visitors.push(visitor);
072 }
073
074 private void pop() {
075 visitors.pop();
076 }
077
078 private SignatureVisitor signatureVisitor() {
079 return !visitors.isEmpty() ? visitors.peek() : signatureVisitor;
080 }
081
082 /**
083 * Shortcut
084 */
085 public void writeAsmType(Type asmType) {
086 switch (asmType.getSort()) {
087 case Type.OBJECT:
088 writeClassBegin(asmType);
089 writeClassEnd();
090 return;
091 case Type.ARRAY:
092 writeArrayType();
093 writeAsmType(asmType.getElementType());
094 writeArrayEnd();
095 return;
096 default:
097 signatureVisitor().visitBaseType(asmType.getDescriptor().charAt(0));
098 writeAsmType0(asmType);
099 }
100 }
101
102 public void writeNothing() {
103 signatureVisitor().visitBaseType('V');
104 writeAsmType0(Type.VOID_TYPE);
105 }
106
107 public void writeNullableNothing() {
108 signatureVisitor().visitClassType("java/lang/Object");
109 signatureVisitor().visitEnd();
110 writeAsmType0(AsmTypeConstants.OBJECT_TYPE);
111 }
112
113 private String makeArrayPrefix() {
114 StringBuilder sb = new StringBuilder();
115 for (int i = 0; i < jvmCurrentTypeArrayLevel; ++i) {
116 sb.append('[');
117 }
118 return sb.toString();
119 }
120
121 private void writeAsmType0(Type type) {
122 if (jvmCurrentType == null) {
123 jvmCurrentType = Type.getType(makeArrayPrefix() + type.getDescriptor());
124 }
125 }
126
127 public void writeClassBegin(Type asmType) {
128 signatureVisitor().visitClassType(asmType.getInternalName());
129 writeAsmType0(asmType);
130 }
131
132 public void writeClassEnd() {
133 signatureVisitor().visitEnd();
134 }
135
136 public void writeArrayType() {
137 push(signatureVisitor().visitArrayType());
138 if (jvmCurrentType == null) {
139 ++jvmCurrentTypeArrayLevel;
140 }
141 }
142
143 public void writeArrayEnd() {
144 pop();
145 }
146
147 private static char toJvmVariance(@NotNull Variance variance) {
148 switch (variance) {
149 case INVARIANT: return '=';
150 case IN_VARIANCE: return '-';
151 case OUT_VARIANCE: return '+';
152 default: throw new IllegalStateException("Unknown variance: " + variance);
153 }
154 }
155
156 public void writeTypeArgument(@NotNull Variance projectionKind) {
157 push(signatureVisitor().visitTypeArgument(toJvmVariance(projectionKind)));
158
159 generic = true;
160 }
161
162 public void writeTypeArgumentEnd() {
163 pop();
164 }
165
166 public void writeTypeVariable(Name name, Type asmType) {
167 signatureVisitor().visitTypeVariable(name.asString());
168 generic = true;
169 writeAsmType0(asmType);
170 }
171
172 public void writeFormalTypeParameter(String name) {
173 signatureVisitor().visitFormalTypeParameter(name);
174
175 generic = true;
176 }
177
178 public void writeClassBound() {
179 push(signatureVisitor().visitClassBound());
180 }
181
182 public void writeClassBoundEnd() {
183 pop();
184 }
185
186 public void writeInterfaceBound() {
187 push(signatureVisitor().visitInterfaceBound());
188 }
189
190 public void writeInterfaceBoundEnd() {
191 pop();
192 }
193
194 public void writeParametersStart() {
195 // hacks
196 jvmCurrentType = null;
197 jvmCurrentTypeArrayLevel = 0;
198 }
199
200 public void writeParameterType(JvmMethodParameterKind parameterKind) {
201 // This magic mimics the behavior of javac that enum constructor have these synthetic parameters in erased signature, but doesn't
202 // have them in generic signature. IDEA relies on this behavior.
203 if (parameterKind == JvmMethodParameterKind.ENUM_NAME || parameterKind == JvmMethodParameterKind.ENUM_ORDINAL) {
204 generic = true;
205
206 // pushing dummy visitor, because we don't want these parameters to appear in generic JVM signature
207 push(new SignatureWriter());
208 }
209 else {
210 push(signatureVisitor().visitParameterType());
211 }
212
213 this.currentParameterKind = parameterKind;
214 }
215
216 public void writeParameterTypeEnd() {
217 pop();
218
219 kotlinParameterTypes.add(new JvmMethodParameterSignature(jvmCurrentType, currentParameterKind));
220
221 currentParameterKind = null;
222 jvmCurrentType = null;
223 jvmCurrentTypeArrayLevel = 0;
224 }
225
226 public void writeReturnType() {
227 push(signatureVisitor().visitReturnType());
228 }
229
230 public void writeReturnTypeEnd() {
231 pop();
232
233 jvmReturnType = jvmCurrentType;
234 jvmCurrentType = null;
235 jvmCurrentTypeArrayLevel = 0;
236 }
237
238 public void writeSuperclass() {
239 push(signatureVisitor().visitSuperclass());
240 }
241
242 public void writeSuperclassEnd() {
243 pop();
244 }
245
246 public void writeInterface() {
247 push(signatureVisitor().visitInterface());
248 }
249
250 public void writeInterfaceEnd() {
251 pop();
252 }
253
254
255 @NotNull
256 private Method makeAsmMethod(String name) {
257 List<Type> jvmParameterTypes = new ArrayList<Type>(kotlinParameterTypes.size());
258 for (JvmMethodParameterSignature p : kotlinParameterTypes) {
259 jvmParameterTypes.add(p.getAsmType());
260 }
261 return new Method(name, jvmReturnType, jvmParameterTypes.toArray(new Type[jvmParameterTypes.size()]));
262 }
263
264 @Nullable
265 public String makeJavaGenericSignature() {
266 return generic ? signatureWriter.toString() : null;
267 }
268
269 @NotNull
270 public JvmMethodSignature makeJvmMethodSignature(String name) {
271 return new JvmMethodSignature(
272 makeAsmMethod(name),
273 needGenerics ? makeJavaGenericSignature() : null,
274 kotlinParameterTypes
275 );
276 }
277 }
278