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