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 }