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 }