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 = new TestClassBuilderFactory(false);
060    
061        public static ClassBuilderFactory TEST_WITH_SOURCE_RETENTION_ANNOTATIONS = new TestClassBuilderFactory(true);
062        
063        private static class TestClassBuilderFactory implements ClassBuilderFactory {
064            private final boolean generateSourceRetentionAnnotations;
065    
066            public TestClassBuilderFactory(boolean generateSourceRetentionAnnotations) {
067                this.generateSourceRetentionAnnotations = generateSourceRetentionAnnotations;
068            }
069    
070            @NotNull
071            @Override
072            public ClassBuilderMode getClassBuilderMode() {
073                return ClassBuilderMode.full(generateSourceRetentionAnnotations);
074            }
075    
076            @NotNull
077            @Override
078            public ClassBuilder newClassBuilder(@NotNull JvmDeclarationOrigin origin) {
079                return new TraceBuilder(new BinaryClassWriter());
080            }
081    
082            @Override
083            public String asText(ClassBuilder builder) {
084                TraceClassVisitor visitor = (TraceClassVisitor) builder.getVisitor();
085    
086                StringWriter writer = new StringWriter();
087                visitor.p.print(new PrintWriter(writer));
088    
089                return writer.toString();
090            }
091    
092            @Override
093            public byte[] asBytes(ClassBuilder builder) {
094                return ((TraceBuilder) builder).binary.toByteArray();
095            }
096    
097            @Override
098            public void close() {
099    
100            }
101        }
102        
103        @NotNull
104        public static ClassBuilderFactory binaries(final boolean generateSourceRetentionAnnotations) {
105            return new ClassBuilderFactory() {
106                @NotNull
107                @Override
108                public ClassBuilderMode getClassBuilderMode() {
109                    return ClassBuilderMode.full(generateSourceRetentionAnnotations);
110                }
111    
112                @NotNull
113                @Override
114                public ClassBuilder newClassBuilder(@NotNull JvmDeclarationOrigin origin) {
115                    return new AbstractClassBuilder.Concrete(new BinaryClassWriter());
116                }
117    
118                @Override
119                public String asText(ClassBuilder builder) {
120                    throw new UnsupportedOperationException("BINARIES generator asked for text");
121                }
122    
123                @Override
124                public byte[] asBytes(ClassBuilder builder) {
125                    ClassWriter visitor = (ClassWriter) builder.getVisitor();
126                    return visitor.toByteArray();
127                }
128    
129                @Override
130                public void close() {
131    
132                }
133            };
134        }
135    
136        private ClassBuilderFactories() {
137        }
138    
139        private static class BinaryClassWriter extends ClassWriter {
140            public BinaryClassWriter() {
141                super(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
142            }
143    
144            @Override
145            protected String getCommonSuperClass(@NotNull String type1, @NotNull String type2) {
146                // This method is needed to generate StackFrameMap: bytecode metadata for JVM verification. For bytecode version 50.0 (JDK 6)
147                // these maps can be invalid: in this case, JVM would generate them itself (potentially slowing class loading),
148                // for bytecode 51.0+ (JDK 7+) JVM would crash with VerifyError.
149                // It seems that for bytecode emitted by Kotlin compiler, it is safe to return "Object" here, because there will
150                // be "checkcast" generated before making a call, anyway.
151    
152                return "java/lang/Object";
153            }
154        }
155    
156        private static class TraceBuilder extends AbstractClassBuilder.Concrete {
157            public final BinaryClassWriter binary;
158    
159            public TraceBuilder(BinaryClassWriter binary) {
160                super(new TraceClassVisitor(binary, new PrintWriter(new StringWriter())));
161                this.binary = binary;
162            }
163        }
164    }