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 }