001/*
002 * Copyright 2010-2013 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
017package org.jetbrains.jet.codegen;
018
019import org.jetbrains.annotations.NotNull;
020import org.jetbrains.asm4.ClassWriter;
021import org.jetbrains.asm4.util.TraceClassVisitor;
022
023import java.io.PrintWriter;
024import java.io.StringWriter;
025
026@SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
027public class ClassBuilderFactories {
028
029    public static ClassBuilderFactory TEST = new ClassBuilderFactory() {
030        @NotNull
031        @Override
032        public ClassBuilderMode getClassBuilderMode() {
033            return ClassBuilderMode.FULL;
034        }
035
036        @Override
037        public ClassBuilder newClassBuilder() {
038            return new TraceBuilder(new BinaryClassWriter());
039        }
040
041        @Override
042        public String asText(ClassBuilder builder) {
043            TraceClassVisitor visitor = (TraceClassVisitor) builder.getVisitor();
044
045            StringWriter writer = new StringWriter();
046            visitor.p.print(new PrintWriter(writer));
047
048            return writer.toString();
049        }
050
051        @Override
052        public byte[] asBytes(ClassBuilder builder) {
053            return ((TraceBuilder) builder).binary.toByteArray();
054        }
055    };
056
057    public static ClassBuilderFactory TEXT = new ClassBuilderFactory() {
058        @NotNull
059        @Override
060        public ClassBuilderMode getClassBuilderMode() {
061            return ClassBuilderMode.FULL;
062        }
063
064        @Override
065        public ClassBuilder newClassBuilder() {
066            return new ClassBuilder.Concrete(new TraceClassVisitor(new PrintWriter(new StringWriter())));
067        }
068
069        @Override
070        public String asText(ClassBuilder builder) {
071            TraceClassVisitor visitor = (TraceClassVisitor) builder.getVisitor();
072
073            StringWriter writer = new StringWriter();
074            visitor.p.print(new PrintWriter(writer));
075
076            return writer.toString();
077        }
078
079        @Override
080        public byte[] asBytes(ClassBuilder builder) {
081            throw new UnsupportedOperationException("TEXT generator asked for bytes");
082        }
083    };
084
085    private ClassBuilderFactories() {
086    }
087
088    public static ClassBuilderFactory binaries(final boolean stubs) {
089        return new ClassBuilderFactory() {
090            @NotNull
091            @Override
092            public ClassBuilderMode getClassBuilderMode() {
093                return stubs ? ClassBuilderMode.STUBS : ClassBuilderMode.FULL;
094            }
095
096            @Override
097            public ClassBuilder newClassBuilder() {
098                return new ClassBuilder.Concrete(new BinaryClassWriter());
099            }
100
101            @Override
102            public String asText(ClassBuilder builder) {
103                throw new UnsupportedOperationException("BINARIES generator asked for text");
104            }
105
106            @Override
107            public byte[] asBytes(ClassBuilder builder) {
108                ClassWriter visitor = (ClassWriter) builder.getVisitor();
109                return visitor.toByteArray();
110            }
111        };
112    }
113
114    private static class BinaryClassWriter extends ClassWriter {
115        public BinaryClassWriter() {
116            super(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
117        }
118
119        @Override
120        protected String getCommonSuperClass(String type1, String type2) {
121            try {
122                return super.getCommonSuperClass(type1, type2);
123            }
124            catch (Throwable t) {
125                // @todo we might need at some point do more sophisticated handling
126                return "java/lang/Object";
127            }
128        }
129    }
130
131    private static class TraceBuilder extends ClassBuilder.Concrete {
132        public final BinaryClassWriter binary;
133
134        public TraceBuilder(BinaryClassWriter binary) {
135            super(new TraceClassVisitor(binary, new PrintWriter(new StringWriter())));
136            this.binary = binary;
137        }
138    }
139}