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