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