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;
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    
054        @NotNull
055        public static ClassBuilderFactory TEST = new ClassBuilderFactory() {
056            @NotNull
057            @Override
058            public ClassBuilderMode getClassBuilderMode() {
059                return ClassBuilderMode.FULL;
060            }
061    
062            @NotNull
063            @Override
064            public ClassBuilder newClassBuilder(@NotNull JvmDeclarationOrigin origin) {
065                return new TraceBuilder(new BinaryClassWriter());
066            }
067    
068            @Override
069            public String asText(ClassBuilder builder) {
070                TraceClassVisitor visitor = (TraceClassVisitor) builder.getVisitor();
071    
072                StringWriter writer = new StringWriter();
073                visitor.p.print(new PrintWriter(writer));
074    
075                return writer.toString();
076            }
077    
078            @Override
079            public byte[] asBytes(ClassBuilder builder) {
080                return ((TraceBuilder) builder).binary.toByteArray();
081            }
082        };
083    
084        @NotNull
085        public static ClassBuilderFactory BINARIES = new ClassBuilderFactory() {
086            @NotNull
087            @Override
088            public ClassBuilderMode getClassBuilderMode() {
089                return ClassBuilderMode.FULL;
090            }
091    
092            @NotNull
093            @Override
094            public ClassBuilder newClassBuilder(@NotNull JvmDeclarationOrigin origin) {
095                return new AbstractClassBuilder.Concrete(new BinaryClassWriter());
096            }
097    
098            @Override
099            public String asText(ClassBuilder builder) {
100                throw new UnsupportedOperationException("BINARIES generator asked for text");
101            }
102    
103            @Override
104            public byte[] asBytes(ClassBuilder builder) {
105                ClassWriter visitor = (ClassWriter) builder.getVisitor();
106                return visitor.toByteArray();
107            }
108        };
109    
110        private ClassBuilderFactories() {
111        }
112    
113        private static class BinaryClassWriter extends ClassWriter {
114            public BinaryClassWriter() {
115                super(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
116            }
117    
118            @Override
119            protected String getCommonSuperClass(@NotNull String type1, @NotNull String type2) {
120                try {
121                    return super.getCommonSuperClass(type1, type2);
122                }
123                catch (Throwable t) {
124                    // @todo we might need at some point do more sophisticated handling
125                    return "java/lang/Object";
126                }
127            }
128        }
129    
130        private static class TraceBuilder extends AbstractClassBuilder.Concrete {
131            public final BinaryClassWriter binary;
132    
133            public TraceBuilder(BinaryClassWriter binary) {
134                super(new TraceClassVisitor(binary, new PrintWriter(new StringWriter())));
135                this.binary = binary;
136            }
137        }
138    }