/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.codegen.signature;
import com.intellij.util.containers.
Stack;
import org.jetbrains.annotations.
NotNull;
import org.jetbrains.annotations.
Nullable;
import org.jetbrains.kotlin.name.
Name;
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.
JvmMethodParameterKind;
import org.jetbrains.kotlin.types.
Variance;
import org.jetbrains.org.objectweb.asm.
Type;
import org.jetbrains.org.objectweb.asm.signature.
SignatureVisitor;
import org.jetbrains.org.objectweb.asm.signature.
SignatureWriter;
import org.jetbrains.org.objectweb.asm.util.
CheckSignatureAdapter;
public class
BothSignatureWriter extends
JvmSignatureWriter {
public enum
Mode {
METHOD(
CheckSignatureAdapter.
METHOD_SIGNATURE),
CLASS(
CheckSignatureAdapter.
CLASS_SIGNATURE),
TYPE(
CheckSignatureAdapter.
TYPE_SIGNATURE);
private final int
asmType;
Mode(int
asmType) {
this.
asmType =
asmType;
}
}
private final
SignatureWriter signatureWriter = new
SignatureWriter();
private final
SignatureVisitor signatureVisitor;
private boolean
generic = false;
public
BothSignatureWriter(@
NotNull Mode mode) {
this.
signatureVisitor = new
CheckSignatureAdapter(
mode.
asmType,
signatureWriter);
}
private final
Stack<
SignatureVisitor>
visitors = new
Stack<>();
private void
push(
SignatureVisitor visitor) {
visitors.
push(
visitor);
}
private void
pop() {
visitors.
pop();
}
private
SignatureVisitor signatureVisitor() {
return !
visitors.
isEmpty() ?
visitors.
peek() :
signatureVisitor;
}
/**
* Shortcut
*/
@
Override
public void
writeAsmType(@
NotNull Type asmType) {
if (
asmType.
getSort() !=
Type.
OBJECT &&
asmType.
getSort() !=
Type.
ARRAY) {
signatureVisitor().
visitBaseType(
asmType.
getDescriptor().
charAt(0));
}
super.writeAsmType(
asmType);
}
@
Override
public void
writeClassBegin(
Type asmType) {
signatureVisitor().
visitClassType(
asmType.
getInternalName());
super.writeClassBegin(
asmType);
}
@
Override
public void
writeOuterClassBegin(
Type resultingAsmType,
String outerInternalName) {
signatureVisitor().
visitClassType(
outerInternalName);
super.writeOuterClassBegin(
resultingAsmType,
outerInternalName);
}
@
Override
public void
writeInnerClass(
String name) {
signatureVisitor().
visitInnerClassType(
name);
super.writeInnerClass(
name);
}
@
Override
public void
writeClassEnd() {
signatureVisitor().
visitEnd();
super.writeClassEnd();
}
@
Override
public void
writeArrayType() {
push(
signatureVisitor().
visitArrayType());
super.writeArrayType();
}
@
Override
public void
writeArrayEnd() {
pop();
super.writeArrayEnd();
}
private static char
toJvmVariance(@
NotNull Variance variance) {
switch (
variance) {
case
INVARIANT: return '=';
case
IN_VARIANCE: return '-';
case
OUT_VARIANCE: return '+';
default: throw new
IllegalStateException("Unknown variance: " +
variance);
}
}
@
Override
public void
writeTypeArgument(@
NotNull Variance projectionKind) {
push(
signatureVisitor().
visitTypeArgument(
toJvmVariance(
projectionKind)));
generic = true;
super.writeTypeArgument(
projectionKind);
}
@
Override
public void
writeUnboundedWildcard() {
signatureVisitor().
visitTypeArgument();
generic = true;
super.writeUnboundedWildcard();
}
@
Override
public void
writeTypeArgumentEnd() {
pop();
super.writeTypeArgumentEnd();
}
@
Override
public void
writeTypeVariable(@
NotNull Name name, @
NotNull Type asmType) {
signatureVisitor().
visitTypeVariable(
name.
asString());
generic = true;
super.writeTypeVariable(
name,
asmType);
}
@
Override
public void
writeFormalTypeParameter(
String name) {
signatureVisitor().
visitFormalTypeParameter(
name);
generic = true;
super.writeFormalTypeParameter(
name);
}
@
Override
public void
writeClassBound() {
push(
signatureVisitor().
visitClassBound());
super.writeClassBound();
}
@
Override
public void
writeClassBoundEnd() {
pop();
super.writeClassBoundEnd();
}
@
Override
public void
writeInterfaceBound() {
push(
signatureVisitor().
visitInterfaceBound());
super.writeInterfaceBound();
}
@
Override
public void
writeInterfaceBoundEnd() {
pop();
super.writeInterfaceBoundEnd();
}
@
Override
public void
writeParametersStart() {
super.writeParametersStart();
}
@
Override
public void
writeParameterType(
JvmMethodParameterKind parameterKind) {
// This magic mimics the behavior of javac that enum constructor have these synthetic parameters in erased signature, but doesn't
// have them in generic signature. IDEA, javac and their friends rely on this behavior.
if (
parameterKind.
isSkippedInGenericSignature()) {
generic = true;
// pushing dummy visitor, because we don't want these parameters to appear in generic JVM signature
push(new
SignatureWriter());
}
else {
push(
signatureVisitor().
visitParameterType());
}
super.writeParameterType(
parameterKind);
}
@
Override
public void
writeParameterTypeEnd() {
pop();
super.writeParameterTypeEnd();
}
@
Override
public void
writeReturnType() {
push(
signatureVisitor().
visitReturnType());
super.writeReturnType();
}
@
Override
public void
writeReturnTypeEnd() {
pop();
super.writeReturnTypeEnd();
}
@
Override
public void
writeSuperclass() {
push(
signatureVisitor().
visitSuperclass());
super.writeSuperclass();
}
@
Override
public void
writeSuperclassEnd() {
pop();
super.writeSuperclassEnd();
}
@
Override
public void
writeInterface() {
push(
signatureVisitor().
visitInterface());
super.writeInterface();
}
@
Override
public void
writeInterfaceEnd() {
pop();
super.writeInterfaceEnd();
}
@
Override
@
Nullable
public
String makeJavaGenericSignature() {
return
generic ?
signatureWriter.
toString() : null;
}
@
Override
public boolean
skipGenericSignature() {
return false;
}
@
Override
public
String toString() {
return
signatureWriter.
toString();
}
}