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