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.codegen;
018    
019    import org.jetbrains.annotations.NotNull;
020    import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin;
021    import org.jetbrains.org.objectweb.asm.ClassWriter;
022    import org.jetbrains.org.objectweb.asm.util.TraceClassVisitor;
023    
024    import java.io.PrintWriter;
025    import java.io.StringWriter;
026    
027    @SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
028    public class ClassBuilderFactories {
029        @NotNull
030        public static ClassBuilderFactory THROW_EXCEPTION = new ClassBuilderFactory() {
031            @NotNull
032            @Override
033            public ClassBuilderMode getClassBuilderMode() {
034                return ClassBuilderMode.full(false);
035            }
036    
037            @NotNull
038            @Override
039            public ClassBuilder newClassBuilder(@NotNull JvmDeclarationOrigin origin) {
040                throw new IllegalStateException();
041            }
042    
043            @Override
044            public String asText(ClassBuilder builder) {
045                throw new IllegalStateException();
046            }
047    
048            @Override
049            public byte[] asBytes(ClassBuilder builder) {
050                throw new IllegalStateException();
051            }
052    
053            @Override
054            public void close() {
055                throw new IllegalStateException();
056            }
057        };
058    
059        public static ClassBuilderFactory TEST_KAPT3 = new TestClassBuilderFactory(false) {
060            @NotNull
061            @Override
062            public ClassBuilderMode getClassBuilderMode() {
063                return ClassBuilderMode.KAPT3;
064            }
065        };
066        
067        public static ClassBuilderFactory TEST = new TestClassBuilderFactory(false);
068    
069        public static ClassBuilderFactory TEST_WITH_SOURCE_RETENTION_ANNOTATIONS = new TestClassBuilderFactory(true);
070        
071        private static class TestClassBuilderFactory implements ClassBuilderFactory {
072            private final boolean generateSourceRetentionAnnotations;
073    
074            public TestClassBuilderFactory(boolean generateSourceRetentionAnnotations) {
075                this.generateSourceRetentionAnnotations = generateSourceRetentionAnnotations;
076            }
077    
078            @NotNull
079            @Override
080            public ClassBuilderMode getClassBuilderMode() {
081                return ClassBuilderMode.full(generateSourceRetentionAnnotations);
082            }
083    
084            @NotNull
085            @Override
086            public ClassBuilder newClassBuilder(@NotNull JvmDeclarationOrigin origin) {
087                return new TraceBuilder(new BinaryClassWriter());
088            }
089    
090            @Override
091            public String asText(ClassBuilder builder) {
092                TraceClassVisitor visitor = (TraceClassVisitor) builder.getVisitor();
093    
094                StringWriter writer = new StringWriter();
095                visitor.p.print(new PrintWriter(writer));
096    
097                return writer.toString();
098            }
099    
100            @Override
101            public byte[] asBytes(ClassBuilder builder) {
102                return ((TraceBuilder) builder).binary.toByteArray();
103            }
104    
105            @Override
106            public void close() {
107    
108            }
109        }
110        
111        @NotNull
112        public static ClassBuilderFactory binaries(final boolean generateSourceRetentionAnnotations) {
113            return new ClassBuilderFactory() {
114                @NotNull
115                @Override
116                public ClassBuilderMode getClassBuilderMode() {
117                    return ClassBuilderMode.full(generateSourceRetentionAnnotations);
118                }
119    
120                @NotNull
121                @Override
122                public ClassBuilder newClassBuilder(@NotNull JvmDeclarationOrigin origin) {
123                    return new AbstractClassBuilder.Concrete(new BinaryClassWriter());
124                }
125    
126                @Override
127                public String asText(ClassBuilder builder) {
128                    throw new UnsupportedOperationException("BINARIES generator asked for text");
129                }
130    
131                @Override
132                public byte[] asBytes(ClassBuilder builder) {
133                    ClassWriter visitor = (ClassWriter) builder.getVisitor();
134                    return visitor.toByteArray();
135                }
136    
137                @Override
138                public void close() {
139    
140                }
141            };
142        }
143    
144        private ClassBuilderFactories() {
145        }
146    
147        private static class BinaryClassWriter extends ClassWriter {
148            public BinaryClassWriter() {
149                super(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
150            }
151    
152            @Override
153            protected String getCommonSuperClass(@NotNull String type1, @NotNull String type2) {
154                // This method is needed to generate StackFrameMap: bytecode metadata for JVM verification. For bytecode version 50.0 (JDK 6)
155                // these maps can be invalid: in this case, JVM would generate them itself (potentially slowing class loading),
156                // for bytecode 51.0+ (JDK 7+) JVM would crash with VerifyError.
157                // It seems that for bytecode emitted by Kotlin compiler, it is safe to return "Object" here, because there will
158                // be "checkcast" generated before making a call, anyway.
159    
160                return "java/lang/Object";
161            }
162        }
163    
164        private static class TraceBuilder extends AbstractClassBuilder.Concrete {
165            public final BinaryClassWriter binary;
166    
167            public TraceBuilder(BinaryClassWriter binary) {
168                super(new TraceClassVisitor(binary, new PrintWriter(new StringWriter())));
169                this.binary = binary;
170            }
171        }
172    }