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.builtins.CompanionObjectMapping;
021    import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
022    import org.jetbrains.kotlin.descriptors.ClassDescriptor;
023    import org.jetbrains.kotlin.load.java.JvmAbi;
024    import org.jetbrains.kotlin.resolve.DescriptorUtils;
025    import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform;
026    import org.jetbrains.org.objectweb.asm.Type;
027    
028    import static org.jetbrains.kotlin.resolve.DescriptorUtils.isNonCompanionObject;
029    
030    public class FieldInfo {
031    
032        private static final CompanionObjectMapping COMPANION_OBJECT_MAPPING = new CompanionObjectMapping(JvmPlatform.INSTANCE.getBuiltIns());
033    
034        @NotNull
035        public static FieldInfo createForSingleton(@NotNull ClassDescriptor classDescriptor, @NotNull KotlinTypeMapper typeMapper) {
036            if (!classDescriptor.getKind().isSingleton() || DescriptorUtils.isEnumEntry(classDescriptor)) {
037                throw new UnsupportedOperationException("Can't create singleton field for class: " + classDescriptor);
038            }
039    
040            if (isNonCompanionObject(classDescriptor) || COMPANION_OBJECT_MAPPING.hasMappingToObject(classDescriptor)) {
041                return createSingletonViaInstance(classDescriptor, typeMapper);
042            }
043            else {
044                ClassDescriptor ownerDescriptor = DescriptorUtils.getParentOfType(classDescriptor, ClassDescriptor.class);
045                assert ownerDescriptor != null : "Owner not found for class: " + classDescriptor;
046                Type ownerType = typeMapper.mapType(ownerDescriptor);
047                return new FieldInfo(ownerType, typeMapper.mapType(classDescriptor), classDescriptor.getName().asString(), true);
048            }
049        }
050    
051        @NotNull
052        public static FieldInfo createSingletonViaInstance(
053                @NotNull ClassDescriptor classDescriptor,
054                @NotNull KotlinTypeMapper typeMapper
055        ) {
056            Type type = typeMapper.mapType(classDescriptor);
057            return new FieldInfo(type, type, JvmAbi.INSTANCE_FIELD, true);
058        }
059    
060        @NotNull
061        public static FieldInfo createForHiddenField(@NotNull Type owner, @NotNull Type fieldType, @NotNull String fieldName) {
062            return new FieldInfo(owner, fieldType, fieldName, false);
063        }
064    
065        private final Type fieldType;
066        private final Type ownerType;
067        private final String fieldName;
068        private final boolean isStatic;
069    
070        private FieldInfo(@NotNull Type ownerType, @NotNull Type fieldType, @NotNull String fieldName, boolean isStatic) {
071            this.ownerType = ownerType;
072            this.fieldType = fieldType;
073            this.fieldName = fieldName;
074            this.isStatic = isStatic;
075        }
076    
077        @NotNull
078        public Type getFieldType() {
079            return fieldType;
080        }
081    
082        @NotNull
083        public Type getOwnerType() {
084            return ownerType;
085        }
086    
087        @NotNull
088        public String getOwnerInternalName() {
089            return ownerType.getInternalName();
090        }
091    
092        @NotNull
093        public String getFieldName() {
094            return fieldName;
095        }
096    
097        public boolean isStatic() {
098            return isStatic;
099        }
100    
101        @Override
102        public String toString() {
103            return String.format("%s %s.%s : %s", isStatic ? "GETSTATIC" : "GETFIELD", ownerType.getInternalName(), fieldName, fieldType);
104        }
105    }