001    /*
002     * Copyright 2010-2015 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;
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.stubs.StubBase;
023    import com.intellij.psi.stubs.StubElement;
024    import com.intellij.util.containers.Stack;
025    import org.jetbrains.annotations.NotNull;
026    import org.jetbrains.annotations.Nullable;
027    import org.jetbrains.kotlin.codegen.AbstractClassBuilder;
028    import org.jetbrains.kotlin.name.FqName;
029    import org.jetbrains.kotlin.psi.KtFile;
030    import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin;
031    import org.jetbrains.org.objectweb.asm.ClassVisitor;
032    import org.jetbrains.org.objectweb.asm.FieldVisitor;
033    import org.jetbrains.org.objectweb.asm.MethodVisitor;
034    
035    import java.util.List;
036    
037    public class StubClassBuilder extends AbstractClassBuilder {
038        private static final InnerClassSourceStrategy<Object> EMPTY_STRATEGY = new InnerClassSourceStrategy<Object>() {
039            @Override
040            public Object findInnerClass(String s, Object o) {
041                return null;
042            }
043    
044            @Override
045            public void accept(Object innerClass, StubBuildingVisitor<Object> visitor) {
046                throw new UnsupportedOperationException("Shall not be called!");
047            }
048        };
049        private final StubElement parent;
050        private StubBuildingVisitor v;
051        private final Stack<StubElement> parentStack;
052        private boolean isPackageClass = false;
053    
054        public StubClassBuilder(@NotNull Stack<StubElement> parentStack) {
055            this.parentStack = parentStack;
056            this.parent = parentStack.peek();
057        }
058    
059        @NotNull
060        @Override
061        public ClassVisitor getVisitor() {
062            assert v != null : "Called before class is defined";
063            return v;
064        }
065    
066        @Override
067        public void defineClass(
068                PsiElement origin,
069                int version,
070                int access,
071                @NotNull String name,
072                @Nullable String signature,
073                @NotNull String superName,
074                @NotNull String[] interfaces
075        ) {
076            assert v == null : "defineClass() called twice?";
077            v = new StubBuildingVisitor<Object>(null, EMPTY_STRATEGY, parent, access, null);
078    
079            super.defineClass(origin, version, access, name, signature, superName, interfaces);
080    
081            if (origin instanceof KtFile) {
082                FqName packageName = ((KtFile) origin).getPackageFqName();
083                String packageClassName = OldPackageFacadeClassUtils.getPackageClassName(packageName);
084    
085                if (name.equals(packageClassName) || name.endsWith("/" + packageClassName)) {
086                    isPackageClass = true;
087                }
088            }
089    
090            if (!isPackageClass) {
091                parentStack.push(v.getResult());
092            }
093    
094            ((StubBase) v.getResult()).putUserData(ClsWrapperStubPsiFactory.ORIGIN, LightElementOriginKt.toLightClassOrigin(origin));
095        }
096    
097        @NotNull
098        @Override
099        public MethodVisitor newMethod(
100                @NotNull JvmDeclarationOrigin origin,
101                int access,
102                @NotNull String name,
103                @NotNull String desc,
104                @Nullable String signature,
105                @Nullable String[] exceptions
106        ) {
107            MethodVisitor internalVisitor = super.newMethod(origin, access, name, desc, signature, exceptions);
108    
109            if (internalVisitor != EMPTY_METHOD_VISITOR) {
110                // If stub for method generated
111                markLastChild(origin);
112            }
113    
114            return internalVisitor;
115        }
116    
117        @NotNull
118        @Override
119        public FieldVisitor newField(
120                @NotNull JvmDeclarationOrigin origin,
121                int access,
122                @NotNull String name,
123                @NotNull String desc,
124                @Nullable String signature,
125                @Nullable Object value
126        ) {
127            FieldVisitor internalVisitor = super.newField(origin, access, name, desc, signature, value);
128    
129            if (internalVisitor != EMPTY_FIELD_VISITOR) {
130                // If stub for field generated
131                markLastChild(origin);
132            }
133    
134            return internalVisitor;
135        }
136    
137        private void markLastChild(@NotNull JvmDeclarationOrigin origin) {
138            List children = v.getResult().getChildrenStubs();
139            StubBase last = (StubBase) children.get(children.size() - 1);
140    
141            LightElementOrigin oldOrigin = last.getUserData(ClsWrapperStubPsiFactory.ORIGIN);
142            if (oldOrigin != null) {
143                PsiElement originalElement = oldOrigin.getOriginalElement();
144                throw new IllegalStateException("Rewriting origin element: " +
145                                                (originalElement != null ? originalElement.getText() : null) + " for stub " + last.toString());
146            }
147    
148            last.putUserData(ClsWrapperStubPsiFactory.ORIGIN, LightElementOriginKt.toLightMemberOrigin(origin));
149        }
150    
151        @Override
152        public void done() {
153            if (!isPackageClass) {
154                StubElement pop = parentStack.pop();
155                assert pop == v.getResult() : "parentStack: got " + pop + ", expected " + v.getResult();
156            }
157            super.done();
158        }
159    }