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 writeClassEnd() { 122 signatureVisitor().visitEnd(); 123 } 124 125 public void writeArrayType() { 126 push(signatureVisitor().visitArrayType()); 127 if (jvmCurrentType == null) { 128 ++jvmCurrentTypeArrayLevel; 129 } 130 } 131 132 public void writeArrayEnd() { 133 pop(); 134 } 135 136 private static char toJvmVariance(@NotNull Variance variance) { 137 switch (variance) { 138 case INVARIANT: return '='; 139 case IN_VARIANCE: return '-'; 140 case OUT_VARIANCE: return '+'; 141 default: throw new IllegalStateException("Unknown variance: " + variance); 142 } 143 } 144 145 public void writeTypeArgument(@NotNull Variance projectionKind) { 146 push(signatureVisitor().visitTypeArgument(toJvmVariance(projectionKind))); 147 148 generic = true; 149 } 150 151 public void writeUnboundedWildcard() { 152 signatureVisitor().visitTypeArgument(); 153 154 generic = true; 155 } 156 157 public void writeTypeArgumentEnd() { 158 pop(); 159 } 160 161 public void writeTypeVariable(Name name, Type asmType) { 162 signatureVisitor().visitTypeVariable(name.asString()); 163 generic = true; 164 writeAsmType0(asmType); 165 } 166 167 public void writeFormalTypeParameter(String name) { 168 signatureVisitor().visitFormalTypeParameter(name); 169 170 generic = true; 171 } 172 173 public void writeClassBound() { 174 push(signatureVisitor().visitClassBound()); 175 } 176 177 public void writeClassBoundEnd() { 178 pop(); 179 } 180 181 public void writeInterfaceBound() { 182 push(signatureVisitor().visitInterfaceBound()); 183 } 184 185 public void writeInterfaceBoundEnd() { 186 pop(); 187 } 188 189 public void writeParametersStart() { 190 // hacks 191 jvmCurrentType = null; 192 jvmCurrentTypeArrayLevel = 0; 193 } 194 195 public void writeParameterType(JvmMethodParameterKind parameterKind) { 196 // This magic mimics the behavior of javac that enum constructor have these synthetic parameters in erased signature, but doesn't 197 // have them in generic signature. IDEA, javac and their friends rely on this behavior. 198 if (parameterKind.isSkippedInGenericSignature()) { 199 generic = true; 200 201 // pushing dummy visitor, because we don't want these parameters to appear in generic JVM signature 202 push(new SignatureWriter()); 203 } 204 else { 205 push(signatureVisitor().visitParameterType()); 206 } 207 208 this.currentParameterKind = parameterKind; 209 } 210 211 public void writeParameterTypeEnd() { 212 pop(); 213 214 kotlinParameterTypes.add(new JvmMethodParameterSignature(jvmCurrentType, currentParameterKind)); 215 currentSignatureSize += jvmCurrentType.getSize(); 216 217 currentParameterKind = null; 218 jvmCurrentType = null; 219 jvmCurrentTypeArrayLevel = 0; 220 } 221 222 public void writeReturnType() { 223 push(signatureVisitor().visitReturnType()); 224 } 225 226 public void writeReturnTypeEnd() { 227 pop(); 228 229 jvmReturnType = jvmCurrentType; 230 jvmCurrentType = null; 231 jvmCurrentTypeArrayLevel = 0; 232 } 233 234 public void writeSuperclass() { 235 push(signatureVisitor().visitSuperclass()); 236 } 237 238 public void writeSuperclassEnd() { 239 pop(); 240 } 241 242 public void writeInterface() { 243 push(signatureVisitor().visitInterface()); 244 } 245 246 public void writeInterfaceEnd() { 247 pop(); 248 } 249 250 251 @Nullable 252 public String makeJavaGenericSignature() { 253 return generic ? signatureWriter.toString() : null; 254 } 255 256 @NotNull 257 public JvmMethodSignature makeJvmMethodSignature(@NotNull String name) { 258 List<Type> types = new ArrayList<Type>(kotlinParameterTypes.size()); 259 for (JvmMethodParameterSignature parameter : kotlinParameterTypes) { 260 types.add(parameter.getAsmType()); 261 } 262 Method asmMethod = new Method(name, jvmReturnType, types.toArray(new Type[types.size()])); 263 return new JvmMethodSignature(asmMethod, makeJavaGenericSignature(), kotlinParameterTypes); 264 } 265 266 public int getCurrentSignatureSize() { 267 return currentSignatureSize; 268 } 269 270 @Override 271 public String toString() { 272 return signatureWriter.toString(); 273 } 274 } 275