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.org.objectweb.asm.Type;
023 import org.jetbrains.org.objectweb.asm.commons.Method;
024 import org.jetbrains.org.objectweb.asm.signature.SignatureVisitor;
025 import org.jetbrains.org.objectweb.asm.signature.SignatureWriter;
026 import org.jetbrains.org.objectweb.asm.util.CheckSignatureAdapter;
027 import org.jetbrains.jet.lang.resolve.name.Name;
028 import org.jetbrains.jet.lang.types.Variance;
029
030 import java.util.ArrayList;
031 import java.util.List;
032
033 public class BothSignatureWriter {
034 public enum Mode {
035 METHOD(CheckSignatureAdapter.METHOD_SIGNATURE),
036 CLASS(CheckSignatureAdapter.CLASS_SIGNATURE),
037 TYPE(CheckSignatureAdapter.TYPE_SIGNATURE);
038
039 private final int asmType;
040
041 Mode(int asmType) {
042 this.asmType = asmType;
043 }
044 }
045
046 private final SignatureWriter signatureWriter = new SignatureWriter();
047 private final SignatureVisitor signatureVisitor;
048
049 private final List<JvmMethodParameterSignature> kotlinParameterTypes = new ArrayList<JvmMethodParameterSignature>();
050
051 private int jvmCurrentTypeArrayLevel;
052 private Type jvmCurrentType;
053 private Type jvmReturnType;
054
055 private JvmMethodParameterKind currentParameterKind;
056
057 private boolean generic = false;
058
059 public BothSignatureWriter(@NotNull Mode mode) {
060 this.signatureVisitor = new CheckSignatureAdapter(mode.asmType, signatureWriter);
061 }
062
063 private final Stack<SignatureVisitor> visitors = new Stack<SignatureVisitor>();
064
065 private void push(SignatureVisitor visitor) {
066 visitors.push(visitor);
067 }
068
069 private void pop() {
070 visitors.pop();
071 }
072
073 private SignatureVisitor signatureVisitor() {
074 return !visitors.isEmpty() ? visitors.peek() : signatureVisitor;
075 }
076
077 /**
078 * Shortcut
079 */
080 public void writeAsmType(Type asmType) {
081 switch (asmType.getSort()) {
082 case Type.OBJECT:
083 writeClassBegin(asmType);
084 writeClassEnd();
085 return;
086 case Type.ARRAY:
087 writeArrayType();
088 writeAsmType(asmType.getElementType());
089 writeArrayEnd();
090 return;
091 default:
092 signatureVisitor().visitBaseType(asmType.getDescriptor().charAt(0));
093 writeAsmType0(asmType);
094 }
095 }
096
097 private String makeArrayPrefix() {
098 StringBuilder sb = new StringBuilder();
099 for (int i = 0; i < jvmCurrentTypeArrayLevel; ++i) {
100 sb.append('[');
101 }
102 return sb.toString();
103 }
104
105 private void writeAsmType0(Type type) {
106 if (jvmCurrentType == null) {
107 jvmCurrentType = Type.getType(makeArrayPrefix() + type.getDescriptor());
108 }
109 }
110
111 public void writeClassBegin(Type asmType) {
112 signatureVisitor().visitClassType(asmType.getInternalName());
113 writeAsmType0(asmType);
114 }
115
116 public void writeClassEnd() {
117 signatureVisitor().visitEnd();
118 }
119
120 public void writeArrayType() {
121 push(signatureVisitor().visitArrayType());
122 if (jvmCurrentType == null) {
123 ++jvmCurrentTypeArrayLevel;
124 }
125 }
126
127 public void writeArrayEnd() {
128 pop();
129 }
130
131 private static char toJvmVariance(@NotNull Variance variance) {
132 switch (variance) {
133 case INVARIANT: return '=';
134 case IN_VARIANCE: return '-';
135 case OUT_VARIANCE: return '+';
136 default: throw new IllegalStateException("Unknown variance: " + variance);
137 }
138 }
139
140 public void writeTypeArgument(@NotNull Variance projectionKind) {
141 push(signatureVisitor().visitTypeArgument(toJvmVariance(projectionKind)));
142
143 generic = true;
144 }
145
146 public void writeTypeArgumentEnd() {
147 pop();
148 }
149
150 public void writeTypeVariable(Name name, Type asmType) {
151 signatureVisitor().visitTypeVariable(name.asString());
152 generic = true;
153 writeAsmType0(asmType);
154 }
155
156 public void writeFormalTypeParameter(String name) {
157 signatureVisitor().visitFormalTypeParameter(name);
158
159 generic = true;
160 }
161
162 public void writeClassBound() {
163 push(signatureVisitor().visitClassBound());
164 }
165
166 public void writeClassBoundEnd() {
167 pop();
168 }
169
170 public void writeInterfaceBound() {
171 push(signatureVisitor().visitInterfaceBound());
172 }
173
174 public void writeInterfaceBoundEnd() {
175 pop();
176 }
177
178 public void writeParametersStart() {
179 // hacks
180 jvmCurrentType = null;
181 jvmCurrentTypeArrayLevel = 0;
182 }
183
184 public void writeParameterType(JvmMethodParameterKind parameterKind) {
185 // This magic mimics the behavior of javac that enum constructor have these synthetic parameters in erased signature, but doesn't
186 // have them in generic signature. IDEA, javac and their friends rely on this behavior.
187 if (parameterKind.isSkippedInGenericSignature()) {
188 generic = true;
189
190 // pushing dummy visitor, because we don't want these parameters to appear in generic JVM signature
191 push(new SignatureWriter());
192 }
193 else {
194 push(signatureVisitor().visitParameterType());
195 }
196
197 this.currentParameterKind = parameterKind;
198 }
199
200 public void writeParameterTypeEnd() {
201 pop();
202
203 kotlinParameterTypes.add(new JvmMethodParameterSignature(jvmCurrentType, currentParameterKind));
204
205 currentParameterKind = null;
206 jvmCurrentType = null;
207 jvmCurrentTypeArrayLevel = 0;
208 }
209
210 public void writeReturnType() {
211 push(signatureVisitor().visitReturnType());
212 }
213
214 public void writeReturnTypeEnd() {
215 pop();
216
217 jvmReturnType = jvmCurrentType;
218 jvmCurrentType = null;
219 jvmCurrentTypeArrayLevel = 0;
220 }
221
222 public void writeSuperclass() {
223 push(signatureVisitor().visitSuperclass());
224 }
225
226 public void writeSuperclassEnd() {
227 pop();
228 }
229
230 public void writeInterface() {
231 push(signatureVisitor().visitInterface());
232 }
233
234 public void writeInterfaceEnd() {
235 pop();
236 }
237
238
239 @Nullable
240 public String makeJavaGenericSignature() {
241 return generic ? signatureWriter.toString() : null;
242 }
243
244 @NotNull
245 public JvmMethodSignature makeJvmMethodSignature(@NotNull String name) {
246 List<Type> types = new ArrayList<Type>(kotlinParameterTypes.size());
247 for (JvmMethodParameterSignature parameter : kotlinParameterTypes) {
248 types.add(parameter.getAsmType());
249 }
250 Method asmMethod = new Method(name, jvmReturnType, types.toArray(new Type[types.size()]));
251 return new JvmMethodSignature(asmMethod, makeJavaGenericSignature(), kotlinParameterTypes);
252 }
253
254
255 @Override
256 public String toString() {
257 return signatureWriter.toString();
258 }
259 }
260