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