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 com.google.protobuf.Internal;
020    import org.jetbrains.annotations.NotNull;
021    import org.jetbrains.kotlin.descriptors.*;
022    
023    public class Flags {
024        private Flags() {}
025    
026        // Common
027    
028        public static final FlagField<Boolean> HAS_ANNOTATIONS = FlagField.booleanFirst();
029    
030        public static final FlagField<ProtoBuf.Visibility> VISIBILITY = FlagField.after(HAS_ANNOTATIONS, ProtoBuf.Visibility.values());
031    
032        public static final FlagField<ProtoBuf.Modality> MODALITY = FlagField.after(VISIBILITY, ProtoBuf.Modality.values());
033    
034        // Class
035    
036        public static final FlagField<ProtoBuf.Class.Kind> CLASS_KIND = FlagField.after(MODALITY, ProtoBuf.Class.Kind.values());
037    
038        public static final FlagField<Boolean> INNER = FlagField.booleanAfter(CLASS_KIND);
039    
040        // Callables
041    
042        public static final FlagField<ProtoBuf.Callable.CallableKind> CALLABLE_KIND = FlagField.after(MODALITY,
043                                                                                                      ProtoBuf.Callable.CallableKind.values());
044    
045        public static final FlagField<ProtoBuf.Callable.MemberKind> MEMBER_KIND = FlagField.after(CALLABLE_KIND,
046                                                                                                  ProtoBuf.Callable.MemberKind.values());
047        public static final FlagField<Boolean> HAS_GETTER = FlagField.booleanAfter(MEMBER_KIND);
048        public static final FlagField<Boolean> HAS_SETTER = FlagField.booleanAfter(HAS_GETTER);
049        public static final FlagField<Boolean> HAS_CONSTANT = FlagField.booleanAfter(HAS_SETTER);
050    
051        public static final FlagField<Boolean> LATE_INIT = FlagField.booleanAfter(HAS_CONSTANT);
052    
053        // Parameters
054    
055        public static final FlagField<Boolean> DECLARES_DEFAULT_VALUE = FlagField.booleanAfter(HAS_ANNOTATIONS);
056    
057        // Accessors
058    
059        // It's important that this flag is negated: "is NOT default" instead of "is default"
060        public static final FlagField<Boolean> IS_NOT_DEFAULT = FlagField.booleanAfter(MODALITY);
061    
062        // ---
063    
064        private static <E> int bitWidth(@NotNull E[] enumEntries) {
065            int length = enumEntries.length - 1;
066            if (length == 0) return 1;
067            for (int i = 31; i >= 0; i--) {
068                if ((length & (1 << i)) != 0) return i + 1;
069            }
070            throw new IllegalStateException("Empty enum: " + enumEntries.getClass());
071        }
072    
073        public static int getClassFlags(
074                boolean hasAnnotations,
075                Visibility visibility,
076                Modality modality,
077                ClassKind kind,
078                boolean inner,
079                boolean isCompanionObject
080        ) {
081            return HAS_ANNOTATIONS.toFlags(hasAnnotations)
082                   | MODALITY.toFlags(modality(modality))
083                   | VISIBILITY.toFlags(visibility(visibility))
084                   | CLASS_KIND.toFlags(classKind(kind, isCompanionObject))
085                   | INNER.toFlags(inner)
086                   ;
087        }
088    
089        private static ProtoBuf.Class.Kind classKind(ClassKind kind, boolean isCompanionObject) {
090            if (isCompanionObject) return ProtoBuf.Class.Kind.CLASS_OBJECT;
091    
092            switch (kind) {
093                case CLASS:
094                    return ProtoBuf.Class.Kind.CLASS;
095                case INTERFACE:
096                    return ProtoBuf.Class.Kind.TRAIT;
097                case ENUM_CLASS:
098                    return ProtoBuf.Class.Kind.ENUM_CLASS;
099                case ENUM_ENTRY:
100                    return ProtoBuf.Class.Kind.ENUM_ENTRY;
101                case ANNOTATION_CLASS:
102                    return ProtoBuf.Class.Kind.ANNOTATION_CLASS;
103                case OBJECT:
104                    return ProtoBuf.Class.Kind.OBJECT;
105            }
106            throw new IllegalArgumentException("Unknown class kind: " + kind);
107        }
108    
109        public static int getCallableFlags(
110                boolean hasAnnotations,
111                @NotNull Visibility visibility,
112                @NotNull Modality modality,
113                @NotNull CallableMemberDescriptor.Kind memberKind,
114                @NotNull ProtoBuf.Callable.CallableKind callableKind,
115                boolean hasGetter,
116                boolean hasSetter,
117                boolean hasConstant,
118                boolean lateInit
119        ) {
120            return HAS_ANNOTATIONS.toFlags(hasAnnotations)
121                   | MODALITY.toFlags(modality(modality))
122                   | VISIBILITY.toFlags(visibility(visibility))
123                   | MEMBER_KIND.toFlags(memberKind(memberKind))
124                   | CALLABLE_KIND.toFlags(callableKind)
125                   | HAS_GETTER.toFlags(hasGetter)
126                   | HAS_SETTER.toFlags(hasSetter)
127                   | HAS_CONSTANT.toFlags(hasConstant)
128                   | LATE_INIT.toFlags(lateInit)
129                   ;
130        }
131    
132        public static int getAccessorFlags(
133                boolean hasAnnotations,
134                @NotNull Visibility visibility,
135                @NotNull Modality modality,
136                boolean isNotDefault
137        ) {
138            return HAS_ANNOTATIONS.toFlags(hasAnnotations)
139                   | MODALITY.toFlags(modality(modality))
140                   | VISIBILITY.toFlags(visibility(visibility))
141                   | IS_NOT_DEFAULT.toFlags(isNotDefault)
142                   ;
143        }
144    
145        @NotNull
146        private static ProtoBuf.Visibility visibility(@NotNull Visibility visibility) {
147            if (visibility == Visibilities.INTERNAL) {
148                return ProtoBuf.Visibility.INTERNAL;
149            }
150            else if (visibility == Visibilities.PUBLIC) {
151                return ProtoBuf.Visibility.PUBLIC;
152            }
153            else if (visibility == Visibilities.PRIVATE) {
154                return ProtoBuf.Visibility.PRIVATE;
155            }
156            else if (visibility == Visibilities.PRIVATE_TO_THIS) {
157                return ProtoBuf.Visibility.PRIVATE_TO_THIS;
158            }
159            else if (visibility == Visibilities.PROTECTED) {
160                return ProtoBuf.Visibility.PROTECTED;
161            }
162            else if (visibility == Visibilities.LOCAL) {
163                return ProtoBuf.Visibility.LOCAL;
164            }
165            throw new IllegalArgumentException("Unknown visibility: " + visibility);
166        }
167    
168        @NotNull
169        private static ProtoBuf.Modality modality(@NotNull Modality modality) {
170            switch (modality) {
171                case FINAL:
172                    return ProtoBuf.Modality.FINAL;
173                case OPEN:
174                    return ProtoBuf.Modality.OPEN;
175                case ABSTRACT:
176                    return ProtoBuf.Modality.ABSTRACT;
177                case SEALED:
178                    return ProtoBuf.Modality.SEALED;
179            }
180            throw new IllegalArgumentException("Unknown modality: " + modality);
181        }
182    
183        @NotNull
184        private static ProtoBuf.Callable.MemberKind memberKind(@NotNull CallableMemberDescriptor.Kind kind) {
185            switch (kind) {
186                case DECLARATION:
187                    return ProtoBuf.Callable.MemberKind.DECLARATION;
188                case FAKE_OVERRIDE:
189                    return ProtoBuf.Callable.MemberKind.FAKE_OVERRIDE;
190                case DELEGATION:
191                    return ProtoBuf.Callable.MemberKind.DELEGATION;
192                case SYNTHESIZED:
193                    return ProtoBuf.Callable.MemberKind.SYNTHESIZED;
194            }
195            throw new IllegalArgumentException("Unknown member kind: " + kind);
196        }
197    
198        public static int getValueParameterFlags(boolean hasAnnotations, boolean declaresDefaultValue) {
199            return HAS_ANNOTATIONS.toFlags(hasAnnotations)
200                   | DECLARES_DEFAULT_VALUE.toFlags(declaresDefaultValue)
201                   ;
202        }
203    
204        // Infrastructure
205    
206        public static abstract class FlagField<E> {
207            public static <E extends Internal.EnumLite> FlagField<E> after(FlagField<?> previousField, E[] values) {
208                int offset = previousField.offset + previousField.bitWidth;
209                return new EnumLiteFlagField<E>(offset, values);
210            }
211    
212            public static <E extends Internal.EnumLite> FlagField<E> first(E[] values) {
213                return new EnumLiteFlagField<E>(0, values);
214            }
215    
216            public static FlagField<Boolean> booleanFirst() {
217                return new BooleanFlagField(0);
218            }
219    
220            public static FlagField<Boolean> booleanAfter(FlagField<?> previousField) {
221                int offset = previousField.offset + previousField.bitWidth;
222                return new BooleanFlagField(offset);
223            }
224    
225            private final int offset;
226            private final int bitWidth;
227            private final E[] values;
228    
229            private FlagField(int offset, E[] values) {
230                this.offset = offset;
231                this.bitWidth = bitWidth(values);
232                this.values = values;
233            }
234    
235            public E get(int flags) {
236                int maskUnshifted = (1 << bitWidth) - 1;
237                int mask = maskUnshifted << offset;
238                int value = (flags & mask) >> offset;
239                for (E e : values) {
240                    if (getIntValue(e) == value) {
241                        return e;
242                    }
243                }
244                throw new IllegalStateException("Flag not found: " + value);
245            }
246    
247            public int toFlags(E value) {
248                return getIntValue(value) << offset;
249            }
250    
251            protected abstract int getIntValue(E value);
252    
253        }
254    
255        private static class BooleanFlagField extends FlagField<Boolean> {
256            private static final Boolean[] BOOLEAN = { false, true };
257    
258            public BooleanFlagField(int offset) {
259                super(offset, BOOLEAN);
260            }
261    
262            @Override
263            protected int getIntValue(Boolean value) {
264                return value ? 1 : 0;
265            }
266        }
267    
268        private static class EnumLiteFlagField<E extends Internal.EnumLite> extends FlagField<E> {
269            public EnumLiteFlagField(int offset, E[] values) {
270                super(offset, values);
271            }
272    
273            @Override
274            protected int getIntValue(E value) {
275                return value.getNumber();
276            }
277        }
278    
279    }