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