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