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 }