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.resolve.jvm.diagnostics.JvmDeclarationOrigin;
029    import org.jetbrains.org.objectweb.asm.ClassVisitor;
030    import org.jetbrains.org.objectweb.asm.FieldVisitor;
031    import org.jetbrains.org.objectweb.asm.MethodVisitor;
032    
033    import java.util.List;
034    
035    public class StubClassBuilder extends AbstractClassBuilder {
036        private static final InnerClassSourceStrategy<Object> EMPTY_STRATEGY = new InnerClassSourceStrategy<Object>() {
037            @Override
038            public Object findInnerClass(String s, Object o) {
039                return null;
040            }
041    
042            @Override
043            public void accept(Object innerClass, StubBuildingVisitor<Object> visitor) {
044                throw new UnsupportedOperationException("Shall not be called!");
045            }
046        };
047        private final StubElement parent;
048        private StubBuildingVisitor v;
049        private final Stack<StubElement> parentStack;
050    
051        public StubClassBuilder(@NotNull Stack<StubElement> parentStack) {
052            this.parentStack = parentStack;
053            this.parent = parentStack.peek();
054        }
055    
056        @NotNull
057        @Override
058        public ClassVisitor getVisitor() {
059            assert v != null : "Called before class is defined";
060            return v;
061        }
062    
063        @Override
064        public void defineClass(
065                PsiElement origin,
066                int version,
067                int access,
068                @NotNull String name,
069                @Nullable String signature,
070                @NotNull String superName,
071                @NotNull String[] interfaces
072        ) {
073            assert v == null : "defineClass() called twice?";
074            v = new StubBuildingVisitor<Object>(null, EMPTY_STRATEGY, parent, access, null);
075    
076            super.defineClass(origin, version, access, name, signature, superName, interfaces);
077    
078            parentStack.push(v.getResult());
079    
080            ((StubBase) v.getResult()).putUserData(ClsWrapperStubPsiFactory.ORIGIN_ELEMENT, origin);
081        }
082    
083        @NotNull
084        @Override
085        public MethodVisitor newMethod(
086                @NotNull JvmDeclarationOrigin origin,
087                int access,
088                @NotNull String name,
089                @NotNull String desc,
090                @Nullable String signature,
091                @Nullable String[] exceptions
092        ) {
093            MethodVisitor internalVisitor = super.newMethod(origin, access, name, desc, signature, exceptions);
094    
095            if (internalVisitor != EMPTY_METHOD_VISITOR) {
096                // If stub for method generated
097                markLastChild(origin.getElement());
098            }
099    
100            return internalVisitor;
101        }
102    
103        @NotNull
104        @Override
105        public FieldVisitor newField(
106                @NotNull JvmDeclarationOrigin origin,
107                int access,
108                @NotNull String name,
109                @NotNull String desc,
110                @Nullable String signature,
111                @Nullable Object value
112        ) {
113            FieldVisitor internalVisitor = super.newField(origin, access, name, desc, signature, value);
114    
115            if (internalVisitor != EMPTY_FIELD_VISITOR) {
116                // If stub for field generated
117                markLastChild(origin.getElement());
118            }
119    
120            return internalVisitor;
121        }
122    
123        private void markLastChild(@Nullable PsiElement origin) {
124            List children = v.getResult().getChildrenStubs();
125            StubBase last = (StubBase) children.get(children.size() - 1);
126    
127            PsiElement oldOrigin = last.getUserData(ClsWrapperStubPsiFactory.ORIGIN_ELEMENT);
128            if (oldOrigin != null) {
129                throw new IllegalStateException("Rewriting origin element: " + oldOrigin.getText() + " for stub " + last.toString());
130            }
131    
132            last.putUserData(ClsWrapperStubPsiFactory.ORIGIN_ELEMENT, origin);
133        }
134    
135        @Override
136        public void done() {
137            StubElement pop = parentStack.pop();
138            assert pop == v.getResult();
139            super.done();
140        }
141    }