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 }