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
017package org.jetbrains.jet.codegen.signature;
018
019import com.intellij.util.containers.Stack;
020import org.jetbrains.annotations.NotNull;
021import org.jetbrains.annotations.Nullable;
022import org.jetbrains.asm4.Type;
023import org.jetbrains.asm4.commons.Method;
024import org.jetbrains.asm4.signature.SignatureVisitor;
025import org.jetbrains.asm4.signature.SignatureWriter;
026import org.jetbrains.asm4.util.CheckSignatureAdapter;
027import org.jetbrains.jet.lang.resolve.java.AsmTypeConstants;
028import org.jetbrains.jet.lang.resolve.java.JetSignatureUtils;
029import org.jetbrains.jet.lang.resolve.name.Name;
030import org.jetbrains.jet.lang.types.Variance;
031import org.jetbrains.jet.rt.signature.JetSignatureAdapter;
032import org.jetbrains.jet.rt.signature.JetSignatureReader;
033import org.jetbrains.jet.rt.signature.JetSignatureVariance;
034import org.jetbrains.jet.rt.signature.JetSignatureWriter;
035
036import java.util.ArrayList;
037import java.util.List;
038
039
040public 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