001 /* 002 * Copyright 2010-2016 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.asJava.builder; 018 019 import com.intellij.psi.PsiElement; 020 import com.intellij.psi.impl.compiled.InnerClassSourceStrategy; 021 import com.intellij.psi.impl.compiled.StubBuildingVisitor; 022 import com.intellij.psi.impl.java.stubs.PsiClassStub; 023 import com.intellij.psi.impl.java.stubs.PsiJavaFileStub; 024 import com.intellij.psi.stubs.StubBase; 025 import com.intellij.psi.stubs.StubElement; 026 import com.intellij.util.containers.Stack; 027 import org.jetbrains.annotations.NotNull; 028 import org.jetbrains.annotations.Nullable; 029 import org.jetbrains.kotlin.fileClasses.OldPackageFacadeClassUtils; 030 import org.jetbrains.kotlin.codegen.AbstractClassBuilder; 031 import org.jetbrains.kotlin.name.FqName; 032 import org.jetbrains.kotlin.psi.KtFile; 033 import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin; 034 import org.jetbrains.org.objectweb.asm.ClassVisitor; 035 import org.jetbrains.org.objectweb.asm.FieldVisitor; 036 import org.jetbrains.org.objectweb.asm.MethodVisitor; 037 038 import java.util.List; 039 040 public class StubClassBuilder extends AbstractClassBuilder { 041 private static final InnerClassSourceStrategy<Object> EMPTY_STRATEGY = new InnerClassSourceStrategy<Object>() { 042 @Override 043 public Object findInnerClass(String s, Object o) { 044 return null; 045 } 046 047 @Override 048 public void accept(Object innerClass, StubBuildingVisitor<Object> visitor) { 049 throw new UnsupportedOperationException("Shall not be called!"); 050 } 051 }; 052 private final StubElement parent; 053 private StubBuildingVisitor v; 054 private final Stack<StubElement> parentStack; 055 private boolean isPackageClass = false; 056 057 public StubClassBuilder(@NotNull Stack<StubElement> parentStack) { 058 this.parentStack = parentStack; 059 this.parent = parentStack.peek(); 060 } 061 062 @NotNull 063 @Override 064 public ClassVisitor getVisitor() { 065 assert v != null : "Called before class is defined"; 066 return v; 067 } 068 069 @Override 070 public void defineClass( 071 PsiElement origin, 072 int version, 073 int access, 074 @NotNull String name, 075 @Nullable String signature, 076 @NotNull String superName, 077 @NotNull String[] interfaces 078 ) { 079 assert v == null : "defineClass() called twice?"; 080 081 v = new StubBuildingVisitor<Object>(null, EMPTY_STRATEGY, parent, access, calculateShortName(name)); 082 083 super.defineClass(origin, version, access, name, signature, superName, interfaces); 084 085 if (origin instanceof KtFile) { 086 FqName packageName = ((KtFile) origin).getPackageFqName(); 087 String packageClassName = OldPackageFacadeClassUtils.getPackageClassName(packageName); 088 089 if (name.equals(packageClassName) || name.endsWith("/" + packageClassName)) { 090 isPackageClass = true; 091 } 092 } 093 094 if (!isPackageClass) { 095 parentStack.push(v.getResult()); 096 } 097 098 ((StubBase) v.getResult()).putUserData(ClsWrapperStubPsiFactory.ORIGIN, LightElementOriginKt.toLightClassOrigin(origin)); 099 } 100 101 @Nullable 102 private String calculateShortName(@NotNull String internalName) { 103 if (parent instanceof PsiJavaFileStub) { 104 String packagePrefix = getPackageInternalNamePrefix((PsiJavaFileStub) parent); 105 assert internalName.startsWith(packagePrefix) : internalName + " : " + packagePrefix; 106 return internalName.substring(packagePrefix.length()); 107 } 108 if (parent instanceof PsiClassStub<?>) { 109 String parentPrefix = getClassInternalNamePrefix((PsiClassStub) parent); 110 if (parentPrefix == null) return null; 111 112 assert internalName.startsWith(parentPrefix) : internalName + " : " + parentPrefix; 113 return internalName.substring(parentPrefix.length()); 114 } 115 return null; 116 } 117 118 @Nullable 119 private String getClassInternalNamePrefix(@NotNull PsiClassStub classStub) { 120 PsiJavaFileStub fileStub = (PsiJavaFileStub) parentStack.get(0); 121 122 String packageName = fileStub.getPackageName(); 123 124 String classStubQualifiedName = classStub.getQualifiedName(); 125 if (classStubQualifiedName == null) return null; 126 127 if (packageName.isEmpty()) { 128 return classStubQualifiedName.replace('.', '$') + "$"; 129 } 130 else { 131 return packageName.replace('.', '/') + "/" + classStubQualifiedName.substring(packageName.length() + 1).replace('.', '$') + "$"; 132 } 133 } 134 135 136 @NotNull 137 private static String getPackageInternalNamePrefix(@NotNull PsiJavaFileStub fileStub) { 138 String packageName = fileStub.getPackageName(); 139 if (packageName.isEmpty()) { 140 return ""; 141 } 142 else { 143 return packageName.replace('.', '/') + "/"; 144 } 145 } 146 147 @NotNull 148 @Override 149 public MethodVisitor newMethod( 150 @NotNull JvmDeclarationOrigin origin, 151 int access, 152 @NotNull String name, 153 @NotNull String desc, 154 @Nullable String signature, 155 @Nullable String[] exceptions 156 ) { 157 MethodVisitor internalVisitor = super.newMethod(origin, access, name, desc, signature, exceptions); 158 159 if (internalVisitor != EMPTY_METHOD_VISITOR) { 160 // If stub for method generated 161 markLastChild(origin); 162 } 163 164 return internalVisitor; 165 } 166 167 @NotNull 168 @Override 169 public FieldVisitor newField( 170 @NotNull JvmDeclarationOrigin origin, 171 int access, 172 @NotNull String name, 173 @NotNull String desc, 174 @Nullable String signature, 175 @Nullable Object value 176 ) { 177 FieldVisitor internalVisitor = super.newField(origin, access, name, desc, signature, value); 178 179 if (internalVisitor != EMPTY_FIELD_VISITOR) { 180 // If stub for field generated 181 markLastChild(origin); 182 } 183 184 return internalVisitor; 185 } 186 187 private void markLastChild(@NotNull JvmDeclarationOrigin origin) { 188 List children = v.getResult().getChildrenStubs(); 189 StubBase last = (StubBase) children.get(children.size() - 1); 190 191 LightElementOrigin oldOrigin = last.getUserData(ClsWrapperStubPsiFactory.ORIGIN); 192 if (oldOrigin != null) { 193 PsiElement originalElement = oldOrigin.getOriginalElement(); 194 throw new IllegalStateException("Rewriting origin element: " + 195 (originalElement != null ? originalElement.getText() : null) + " for stub " + last.toString()); 196 } 197 198 last.putUserData(ClsWrapperStubPsiFactory.ORIGIN, LightElementOriginKt.toLightMemberOrigin(origin)); 199 } 200 201 @Override 202 public void done() { 203 if (!isPackageClass) { 204 StubElement pop = parentStack.pop(); 205 assert pop == v.getResult() : "parentStack: got " + pop + ", expected " + v.getResult(); 206 } 207 super.done(); 208 } 209 }