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.serialization; 018 019 import kotlin.Pair; 020 import org.jetbrains.annotations.NotNull; 021 import org.jetbrains.kotlin.descriptors.ClassDescriptor; 022 import org.jetbrains.kotlin.descriptors.DeclarationDescriptor; 023 import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor; 024 import org.jetbrains.kotlin.name.FqName; 025 import org.jetbrains.kotlin.name.Name; 026 import org.jetbrains.kotlin.types.ErrorUtils; 027 import org.jetbrains.kotlin.utils.ExceptionUtilsKt; 028 import org.jetbrains.kotlin.utils.Interner; 029 030 import java.io.IOException; 031 import java.io.OutputStream; 032 033 import static org.jetbrains.kotlin.serialization.ProtoBuf.QualifiedNameTable.QualifiedName; 034 035 public class StringTableImpl implements StringTable { 036 private static final class FqNameProto { 037 public final QualifiedName.Builder fqName; 038 039 public FqNameProto(@NotNull QualifiedName.Builder fqName) { 040 this.fqName = fqName; 041 } 042 043 @Override 044 public int hashCode() { 045 int result = 13; 046 result = 31 * result + fqName.getParentQualifiedName(); 047 result = 31 * result + fqName.getShortName(); 048 result = 31 * result + fqName.getKind().hashCode(); 049 return result; 050 } 051 052 @Override 053 public boolean equals(Object obj) { 054 if (obj == null || getClass() != obj.getClass()) return false; 055 056 QualifiedName.Builder other = ((FqNameProto) obj).fqName; 057 return fqName.getParentQualifiedName() == other.getParentQualifiedName() 058 && fqName.getShortName() == other.getShortName() 059 && fqName.getKind() == other.getKind(); 060 } 061 } 062 063 private final Interner<String> strings = new Interner<String>(); 064 private final Interner<FqNameProto> qualifiedNames = new Interner<FqNameProto>(); 065 066 public int getSimpleNameIndex(@NotNull Name name) { 067 return getStringIndex(name.asString()); 068 } 069 070 @Override 071 public int getStringIndex(@NotNull String string) { 072 return strings.intern(string); 073 } 074 075 @Override 076 public int getFqNameIndex(@NotNull ClassDescriptor descriptor) { 077 if (ErrorUtils.isError(descriptor)) { 078 throw new IllegalStateException("Cannot get FQ name of error class: " + descriptor); 079 } 080 081 QualifiedName.Builder builder = QualifiedName.newBuilder(); 082 builder.setKind(QualifiedName.Kind.CLASS); 083 084 DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration(); 085 if (containingDeclaration instanceof PackageFragmentDescriptor) { 086 FqName packageFqName = ((PackageFragmentDescriptor) containingDeclaration).getFqName(); 087 if (!packageFqName.isRoot()) { 088 builder.setParentQualifiedName(getPackageFqNameIndex(packageFqName)); 089 } 090 } 091 else if (containingDeclaration instanceof ClassDescriptor) { 092 ClassDescriptor outerClass = (ClassDescriptor) containingDeclaration; 093 builder.setParentQualifiedName(getFqNameIndex(outerClass)); 094 } 095 else { 096 throw new IllegalStateException("Cannot get FQ name of local class: " + descriptor); 097 } 098 099 builder.setShortName(getStringIndex(descriptor.getName().asString())); 100 101 return qualifiedNames.intern(new FqNameProto(builder)); 102 } 103 104 public int getPackageFqNameIndex(@NotNull FqName fqName) { 105 int result = -1; 106 for (Name segment : fqName.pathSegments()) { 107 QualifiedName.Builder builder = QualifiedName.newBuilder(); 108 builder.setShortName(getSimpleNameIndex(segment)); 109 if (result != -1) { 110 builder.setParentQualifiedName(result); 111 } 112 result = qualifiedNames.intern(new FqNameProto(builder)); 113 } 114 return result; 115 } 116 117 @NotNull 118 public Pair<ProtoBuf.StringTable, ProtoBuf.QualifiedNameTable> buildProto() { 119 ProtoBuf.StringTable.Builder strings = ProtoBuf.StringTable.newBuilder(); 120 for (String simpleName : this.strings.getAllInternedObjects()) { 121 strings.addString(simpleName); 122 } 123 124 ProtoBuf.QualifiedNameTable.Builder qualifiedNames = ProtoBuf.QualifiedNameTable.newBuilder(); 125 for (FqNameProto fqName : this.qualifiedNames.getAllInternedObjects()) { 126 qualifiedNames.addQualifiedName(fqName.fqName); 127 } 128 129 return new Pair<ProtoBuf.StringTable, ProtoBuf.QualifiedNameTable>(strings.build(), qualifiedNames.build()); 130 } 131 132 @Override 133 public void serializeTo(@NotNull OutputStream output) { 134 try { 135 Pair<ProtoBuf.StringTable, ProtoBuf.QualifiedNameTable> protos = buildProto(); 136 protos.getFirst().writeDelimitedTo(output); 137 protos.getSecond().writeDelimitedTo(output); 138 } 139 catch (IOException e) { 140 throw ExceptionUtilsKt.rethrow(e); 141 } 142 } 143 }