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.java.JetSignatureUtils;
029    import org.jetbrains.jet.lang.resolve.name.Name;
030    import org.jetbrains.jet.lang.types.Variance;
031    import org.jetbrains.jet.rt.signature.JetSignatureAdapter;
032    import org.jetbrains.jet.rt.signature.JetSignatureReader;
033    import org.jetbrains.jet.rt.signature.JetSignatureVariance;
034    import org.jetbrains.jet.rt.signature.JetSignatureWriter;
035    
036    import java.util.ArrayList;
037    import java.util.List;
038    
039    
040    public class BothSignatureWriter {
041    
042        private static final boolean DEBUG_SIGNATURE_WRITER = true;
043    
044        public enum Mode {
045            METHOD(CheckSignatureAdapter.METHOD_SIGNATURE),
046            CLASS(CheckSignatureAdapter.CLASS_SIGNATURE),
047            TYPE(CheckSignatureAdapter.TYPE_SIGNATURE);
048    
049            private final int asmType;
050    
051            Mode(int asmType) {
052                this.asmType = asmType;
053            }
054        }
055    
056        private enum State {
057            START,
058            TYPE_PARAMETERS,
059    
060            PARAMETERS,
061            PARAMETER,
062            RETURN_TYPE,
063            METHOD_END,
064    
065            FIELD,
066            FIELD_END,
067    
068            SUPERS,
069            CLASS_END,
070        }
071    
072        private final SignatureWriter signatureWriter = new SignatureWriter();
073        private final SignatureVisitor signatureVisitor;
074    
075        private JetSignatureWriter jetSignatureWriter;
076    
077        private String kotlinClassParameters;
078        private String kotlinClassSignature;
079    
080        private final List<JvmMethodParameterSignature> kotlinParameterTypes = new ArrayList<JvmMethodParameterSignature>();
081        private String kotlinReturnType;
082    
083        private int jvmCurrentTypeArrayLevel;
084        private Type jvmCurrentType;
085        private Type jvmReturnType;
086    
087        private JvmMethodParameterKind currentParameterKind;
088    
089        private final Mode mode;
090        private final boolean needGenerics;
091    
092        private State state = State.START;
093    
094        private boolean generic = false;
095    
096        public BothSignatureWriter(Mode mode, boolean needGenerics) {
097            this.mode = mode;
098            this.needGenerics = needGenerics;
099    
100            if (DEBUG_SIGNATURE_WRITER) {
101                signatureVisitor = new CheckSignatureAdapter(mode.asmType, signatureWriter);
102            }
103            else {
104                signatureVisitor = signatureWriter;
105            }
106        }
107    
108        // TODO: ignore when debugging is disabled
109        private final Stack<SignatureVisitor> visitors = new Stack<SignatureVisitor>();
110    
111        private void push(SignatureVisitor visitor) {
112            visitors.push(visitor);
113        }
114    
115        private void pop() {
116            visitors.pop();
117        }
118    
119    
120        private SignatureVisitor signatureVisitor() {
121            return !visitors.isEmpty() ? visitors.peek() : signatureVisitor;
122        }
123    
124        private void checkTopLevel() {
125            if (DEBUG_SIGNATURE_WRITER) {
126                if (!visitors.isEmpty()) {
127                    throw new IllegalStateException();
128                }
129            }
130        }
131    
132        private void checkMode(Mode mode) {
133            if (DEBUG_SIGNATURE_WRITER) {
134                if (mode != this.mode) {
135                    throw new IllegalStateException();
136                }
137            }
138        }
139    
140        private void checkState(State state) {
141            if (DEBUG_SIGNATURE_WRITER) {
142                if (state != this.state) {
143                    throw new IllegalStateException();
144                }
145                if (jetSignatureWriter != null) {
146                    throw new IllegalStateException();
147                }
148                checkTopLevel();
149            }
150        }
151    
152        private void transitionState(State from, State to) {
153            checkState(from);
154            state = to;
155        }
156    
157        public void writeAsmType(Type asmType, boolean nullable) {
158            writeAsmType(asmType, nullable, null);
159        }
160    
161        /**
162         * Shortcut
163         */
164        public void writeAsmType(Type asmType, boolean nullable, @Nullable String kotlinTypeName) {
165            switch (asmType.getSort()) {
166                case Type.OBJECT:
167                    writeClassBegin(asmType.getInternalName(), nullable, false, kotlinTypeName);
168                    writeClassEnd();
169                    return;
170                case Type.ARRAY:
171                    writeArrayType(nullable, Variance.INVARIANT);
172                    writeAsmType(asmType.getElementType(), false, kotlinTypeName);
173                    writeArrayEnd();
174                    return;
175                default:
176                    String descriptor = asmType.getDescriptor();
177                    if (descriptor.length() != 1) {
178                        throw new IllegalStateException();
179                    }
180                    writeBaseType(descriptor.charAt(0), nullable);
181            }
182        }
183    
184        private void writeBaseType(char c, boolean nullable) {
185            if (nullable) {
186                throw new IllegalStateException();
187            }
188            signatureVisitor().visitBaseType(c);
189            jetSignatureWriter.visitBaseType(c, nullable);
190            writeAsmType0(Type.getType(String.valueOf(c)));
191        }
192    
193        public void writeNothing(boolean nullable) {
194            if (nullable) {
195                signatureVisitor().visitClassType("java/lang/Object");
196                signatureVisitor().visitEnd();
197            }
198            else {
199                signatureVisitor().visitBaseType('V');
200            }
201            jetSignatureWriter.visitClassType("jet/Nothing", nullable, false);
202            jetSignatureWriter.visitEnd();
203            if (nullable) {
204                writeAsmType0(AsmTypeConstants.OBJECT_TYPE);
205            }
206            else {
207                writeAsmType0(Type.VOID_TYPE);
208            }
209        }
210    
211        private String makeArrayPrefix() {
212            StringBuilder sb = new StringBuilder();
213            for (int i = 0; i < jvmCurrentTypeArrayLevel; ++i) {
214                sb.append('[');
215            }
216            return sb.toString();
217        }
218    
219        private void writeAsmType0(Type type) {
220            if (jvmCurrentType == null) {
221                jvmCurrentType = Type.getType(makeArrayPrefix() + type.getDescriptor());
222            }
223        }
224    
225        public void writeClassBegin(String internalName, boolean nullable, boolean real) {
226            writeClassBegin(internalName, nullable, real, null);
227        }
228    
229        public void writeClassBegin(String internalName, boolean nullable, boolean real, @Nullable String kotlinTypeName) {
230            signatureVisitor().visitClassType(internalName);
231            jetSignatureWriter.visitClassType(kotlinTypeName == null ? internalName : kotlinTypeName, nullable, real);
232            writeAsmType0(Type.getObjectType(internalName));
233        }
234    
235        public void writeClassEnd() {
236            signatureVisitor().visitEnd();
237            jetSignatureWriter.visitEnd();
238        }
239    
240        public void writeArrayType(boolean nullable, Variance projectionKind) {
241            push(signatureVisitor().visitArrayType());
242            jetSignatureWriter.visitArrayType(nullable, toJetSignatureVariance(projectionKind));
243            if (jvmCurrentType == null) {
244                ++jvmCurrentTypeArrayLevel;
245            }
246        }
247    
248        public void writeArrayEnd() {
249            pop();
250        }
251    
252        private static JetSignatureVariance toJetSignatureVariance(Variance variance) {
253            switch (variance) {
254                case INVARIANT:
255                    return JetSignatureVariance.INVARIANT;
256                case IN_VARIANCE:
257                    return JetSignatureVariance.IN;
258                case OUT_VARIANCE:
259                    return JetSignatureVariance.OUT;
260                default:
261                    throw new IllegalStateException();
262            }
263        }
264    
265        public void writeTypeArgument(Variance projectionKindForKotlin, Variance projectionKindForJava) {
266            push(signatureVisitor().visitTypeArgument(
267                    toJetSignatureVariance(projectionKindForJava).getChar()
268            ));
269    
270            jetSignatureWriter.visitTypeArgument(toJetSignatureVariance(projectionKindForKotlin));
271            generic = true;
272        }
273    
274        public void writeTypeArgumentEnd() {
275            pop();
276        }
277    
278        public void writeTypeVariable(Name name, boolean nullable, Type asmType) {
279            signatureVisitor().visitTypeVariable(name.asString());
280            jetSignatureWriter.visitTypeVariable(name.asString(), nullable);
281            generic = true;
282            writeAsmType0(asmType);
283        }
284    
285        public void writeFormalTypeParameter(String name, Variance variance, boolean reified) {
286            checkTopLevel();
287    
288            signatureVisitor().visitFormalTypeParameter(name);
289            jetSignatureWriter.visitFormalTypeParameter(name, JetSignatureUtils.translateVariance(variance), reified);
290    
291            generic = true;
292        }
293    
294        public void writeFormalTypeParameterEnd() {
295            jetSignatureWriter.visitFormalTypeParameterEnd();
296        }
297    
298        public void writeFormalTypeParametersStart() {
299            checkTopLevel();
300            transitionState(State.START, State.TYPE_PARAMETERS);
301            jetSignatureWriter = new JetSignatureWriter();
302        }
303    
304        public void writeFormalTypeParametersEnd() {
305            jetSignatureWriter.visitSuperclass(); // just to call endFormals
306    
307            kotlinClassParameters = jetSignatureWriter.toString();
308    
309            jetSignatureWriter = null;
310    
311            if (DEBUG_SIGNATURE_WRITER) {
312                new JetSignatureReader(kotlinClassParameters).acceptFormalTypeParametersOnly(new JetSignatureAdapter());
313            }
314    
315            checkState(State.TYPE_PARAMETERS);
316        }
317    
318        public void writeClassBound() {
319            push(signatureVisitor().visitClassBound());
320            jetSignatureWriter.visitClassBound();
321        }
322    
323        public void writeClassBoundEnd() {
324            pop();
325        }
326    
327        public void writeInterfaceBound() {
328            push(signatureVisitor().visitInterfaceBound());
329            jetSignatureWriter.visitInterfaceBound();
330        }
331    
332        public void writeInterfaceBoundEnd() {
333            pop();
334        }
335    
336        public void writeParametersStart() {
337            transitionState(State.TYPE_PARAMETERS, State.PARAMETERS);
338    
339            // hacks
340            jvmCurrentType = null;
341            jvmCurrentTypeArrayLevel = 0;
342        }
343    
344        public void writeParametersEnd() {
345            checkState(State.PARAMETERS);
346        }
347    
348        public void writeFieldTypeStart() {
349            transitionState(State.START, State.FIELD);
350            jetSignatureWriter = new JetSignatureWriter();
351        }
352    
353        public void writeFieldTypeEnd() {
354            jetSignatureWriter = null;
355            transitionState(State.FIELD, State.FIELD_END);
356        }
357    
358        public void writeParameterType(JvmMethodParameterKind parameterKind) {
359            transitionState(State.PARAMETERS, State.PARAMETER);
360    
361            // This magic mimics the behavior of javac that enum constructor have these synthetic parameters in erased signature, but doesn't
362            // have them in generic signature. IDEA relies on this behavior.
363            if (parameterKind == JvmMethodParameterKind.ENUM_NAME || parameterKind == JvmMethodParameterKind.ENUM_ORDINAL) {
364                generic = true;
365    
366                // pushing dummy visitor, because we don't want these parameters to appear in generic JVM signature
367                push(new SignatureWriter());
368            }
369            else {
370                push(signatureVisitor().visitParameterType());
371            }
372    
373            jetSignatureWriter = new JetSignatureWriter();
374            if (jvmCurrentType != null || jvmCurrentTypeArrayLevel != 0) {
375                throw new IllegalStateException();
376            }
377    
378            if (currentParameterKind != null) {
379                throw new IllegalStateException();
380            }
381            this.currentParameterKind = parameterKind;
382    
383            //jetSignatureWriter.visitParameterType();
384        }
385    
386        public void writeParameterTypeEnd() {
387            pop();
388    
389            if (jvmCurrentType == null) {
390                throw new IllegalStateException();
391            }
392    
393            String signature = jetSignatureWriter.toString();
394            kotlinParameterTypes.add(new JvmMethodParameterSignature(jvmCurrentType, signature, currentParameterKind));
395    
396            if (DEBUG_SIGNATURE_WRITER) {
397                new JetSignatureReader(signature).acceptTypeOnly(new JetSignatureAdapter());
398            }
399    
400            currentParameterKind = null;
401            jvmCurrentType = null;
402            jvmCurrentTypeArrayLevel = 0;
403    
404            jetSignatureWriter = null;
405            transitionState(State.PARAMETER, State.PARAMETERS);
406        }
407    
408        public void writeReturnType() {
409            transitionState(State.PARAMETERS, State.RETURN_TYPE);
410    
411            jetSignatureWriter = new JetSignatureWriter();
412    
413            if (jvmCurrentType != null) {
414                throw new IllegalStateException();
415            }
416    
417            push(signatureVisitor().visitReturnType());
418            //jetSignatureWriter.visitReturnType();
419        }
420    
421        public void writeReturnTypeEnd() {
422            pop();
423    
424            kotlinReturnType = jetSignatureWriter.toString();
425    
426            if (jvmCurrentType == null) {
427                throw new IllegalStateException();
428            }
429    
430            jvmReturnType = jvmCurrentType;
431            jvmCurrentType = null;
432            jvmCurrentTypeArrayLevel = 0;
433    
434            if (DEBUG_SIGNATURE_WRITER) {
435                new JetSignatureReader(kotlinReturnType).acceptTypeOnly(new JetSignatureAdapter());
436            }
437    
438            jetSignatureWriter = null;
439            transitionState(State.RETURN_TYPE, State.METHOD_END);
440        }
441    
442        public void writeVoidReturn() {
443            writeReturnType();
444            writeAsmType(Type.VOID_TYPE, false);
445            writeReturnTypeEnd();
446        }
447    
448        public void writeSupersStart() {
449            transitionState(State.TYPE_PARAMETERS, State.SUPERS);
450            jetSignatureWriter = new JetSignatureWriter();
451        }
452    
453        public void writeSupersEnd() {
454            kotlinClassSignature = jetSignatureWriter.toString();
455            jetSignatureWriter = null;
456    
457            if (DEBUG_SIGNATURE_WRITER) {
458                new JetSignatureReader(kotlinClassSignature).accept(new JetSignatureAdapter());
459            }
460    
461            transitionState(State.SUPERS, State.CLASS_END);
462        }
463    
464        public void writeSuperclass() {
465            push(signatureVisitor().visitSuperclass());
466            jetSignatureWriter.visitSuperclass();
467        }
468    
469        public void writeSuperclassEnd() {
470            pop();
471            if (!visitors.isEmpty()) {
472                throw new IllegalStateException();
473            }
474        }
475    
476        public void writeInterface() {
477            checkTopLevel();
478            checkMode(Mode.CLASS);
479    
480            push(signatureVisitor().visitInterface());
481            jetSignatureWriter.visitInterface();
482        }
483    
484        public void writeInterfaceEnd() {
485            pop();
486            if (!visitors.isEmpty()) {
487                throw new IllegalStateException();
488            }
489        }
490    
491    
492        @NotNull
493        protected Method makeAsmMethod(String name) {
494            List<Type> jvmParameterTypes = new ArrayList<Type>(kotlinParameterTypes.size());
495            for (JvmMethodParameterSignature p : kotlinParameterTypes) {
496                jvmParameterTypes.add(p.getAsmType());
497            }
498            return new Method(name, jvmReturnType, jvmParameterTypes.toArray(new Type[jvmParameterTypes.size()]));
499        }
500    
501        @Nullable
502        public String makeJavaGenericSignature() {
503            if (state != State.METHOD_END && state != State.CLASS_END && state != State.FIELD_END) {
504                throw new IllegalStateException();
505            }
506            checkTopLevel();
507            return generic ? signatureWriter.toString() : null;
508        }
509    
510        @NotNull
511        protected List<JvmMethodParameterSignature> makeKotlinParameterTypes() {
512            checkState(State.METHOD_END);
513            // TODO: return nulls if equal to #makeJavaString
514            return kotlinParameterTypes;
515        }
516    
517        @NotNull
518        protected String makeKotlinReturnTypeSignature() {
519            checkState(State.METHOD_END);
520            return kotlinReturnType;
521        }
522    
523        protected String makeKotlinMethodTypeParameters() {
524            checkState(State.METHOD_END);
525            return kotlinClassParameters;
526        }
527    
528        @NotNull
529        public String makeKotlinClassSignature() {
530            checkState(State.CLASS_END);
531            if (kotlinClassParameters == null) {
532                throw new IllegalStateException();
533            }
534            if (kotlinClassSignature == null) {
535                throw new IllegalStateException();
536            }
537            return kotlinClassParameters + kotlinClassSignature;
538        }
539    
540        @NotNull
541        public JvmMethodSignature makeJvmMethodSignature(String name) {
542            return new JvmMethodSignature(
543                    makeAsmMethod(name),
544                    needGenerics ? makeJavaGenericSignature() : null,
545                    needGenerics ? makeKotlinMethodTypeParameters() : null,
546                    makeKotlinParameterTypes(),
547                    makeKotlinReturnTypeSignature(),
548                    needGenerics
549            );
550        }
551    
552        @NotNull
553        public JvmPropertyAccessorSignature makeJvmPropertyAccessorSignature(String name, boolean isGetter) {
554            return new JvmPropertyAccessorSignature(
555                    makeAsmMethod(name),
556                    needGenerics ? makeJavaGenericSignature() : null,
557                    needGenerics ? makeKotlinMethodTypeParameters() : null,
558                    makeKotlinParameterTypes(),
559                    makeKotlinReturnTypeSignature(),
560                    needGenerics,
561                    isGetter
562            );
563        }
564    }
565