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.jet.lang.resolve.java.jvmSignature.JvmMethodParameterKind; 023 import org.jetbrains.jet.lang.resolve.java.jvmSignature.JvmMethodParameterSignature; 024 import org.jetbrains.jet.lang.resolve.java.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.jet.lang.resolve.name.Name; 031 import org.jetbrains.jet.lang.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 writeTypeArgumentEnd() { 150 pop(); 151 } 152 153 public void writeTypeVariable(Name name, Type asmType) { 154 signatureVisitor().visitTypeVariable(name.asString()); 155 generic = true; 156 writeAsmType0(asmType); 157 } 158 159 public void writeFormalTypeParameter(String name) { 160 signatureVisitor().visitFormalTypeParameter(name); 161 162 generic = true; 163 } 164 165 public void writeClassBound() { 166 push(signatureVisitor().visitClassBound()); 167 } 168 169 public void writeClassBoundEnd() { 170 pop(); 171 } 172 173 public void writeInterfaceBound() { 174 push(signatureVisitor().visitInterfaceBound()); 175 } 176 177 public void writeInterfaceBoundEnd() { 178 pop(); 179 } 180 181 public void writeParametersStart() { 182 // hacks 183 jvmCurrentType = null; 184 jvmCurrentTypeArrayLevel = 0; 185 } 186 187 public void writeParameterType(JvmMethodParameterKind parameterKind) { 188 // This magic mimics the behavior of javac that enum constructor have these synthetic parameters in erased signature, but doesn't 189 // have them in generic signature. IDEA, javac and their friends rely on this behavior. 190 if (parameterKind.isSkippedInGenericSignature()) { 191 generic = true; 192 193 // pushing dummy visitor, because we don't want these parameters to appear in generic JVM signature 194 push(new SignatureWriter()); 195 } 196 else { 197 push(signatureVisitor().visitParameterType()); 198 } 199 200 this.currentParameterKind = parameterKind; 201 } 202 203 public void writeParameterTypeEnd() { 204 pop(); 205 206 kotlinParameterTypes.add(new JvmMethodParameterSignature(jvmCurrentType, currentParameterKind)); 207 208 currentParameterKind = null; 209 jvmCurrentType = null; 210 jvmCurrentTypeArrayLevel = 0; 211 } 212 213 public void writeReturnType() { 214 push(signatureVisitor().visitReturnType()); 215 } 216 217 public void writeReturnTypeEnd() { 218 pop(); 219 220 jvmReturnType = jvmCurrentType; 221 jvmCurrentType = null; 222 jvmCurrentTypeArrayLevel = 0; 223 } 224 225 public void writeSuperclass() { 226 push(signatureVisitor().visitSuperclass()); 227 } 228 229 public void writeSuperclassEnd() { 230 pop(); 231 } 232 233 public void writeInterface() { 234 push(signatureVisitor().visitInterface()); 235 } 236 237 public void writeInterfaceEnd() { 238 pop(); 239 } 240 241 242 @Nullable 243 public String makeJavaGenericSignature() { 244 return generic ? signatureWriter.toString() : null; 245 } 246 247 @NotNull 248 public JvmMethodSignature makeJvmMethodSignature(@NotNull String name) { 249 List<Type> types = new ArrayList<Type>(kotlinParameterTypes.size()); 250 for (JvmMethodParameterSignature parameter : kotlinParameterTypes) { 251 types.add(parameter.getAsmType()); 252 } 253 Method asmMethod = new Method(name, jvmReturnType, types.toArray(new Type[types.size()])); 254 return new JvmMethodSignature(asmMethod, makeJavaGenericSignature(), kotlinParameterTypes); 255 } 256 257 258 @Override 259 public String toString() { 260 return signatureWriter.toString(); 261 } 262 } 263