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.annotations.Nullable;
022    import org.jetbrains.kotlin.descriptors.*;
023    
024    public class Flags {
025        private Flags() {}
026    
027        // Common
028    
029        public static final BooleanFlagField HAS_ANNOTATIONS = FlagField.booleanFirst();
030        public static final FlagField<ProtoBuf.Visibility> VISIBILITY = FlagField.after(HAS_ANNOTATIONS, ProtoBuf.Visibility.values());
031        public static final FlagField<ProtoBuf.Modality> MODALITY = FlagField.after(VISIBILITY, ProtoBuf.Modality.values());
032    
033        // Class
034    
035        public static final FlagField<ProtoBuf.Class.Kind> CLASS_KIND = FlagField.after(MODALITY, ProtoBuf.Class.Kind.values());
036        public static final BooleanFlagField IS_INNER = FlagField.booleanAfter(CLASS_KIND);
037        public static final BooleanFlagField IS_DATA = FlagField.booleanAfter(IS_INNER);
038    
039        // Constructors
040    
041        public static final BooleanFlagField IS_SECONDARY = FlagField.booleanAfter(VISIBILITY);
042    
043        // Callables
044    
045        public static final FlagField<ProtoBuf.MemberKind> MEMBER_KIND = FlagField.after(MODALITY, ProtoBuf.MemberKind.values());
046    
047        // Functions
048    
049        public static final BooleanFlagField IS_OPERATOR = FlagField.booleanAfter(MEMBER_KIND);
050        public static final BooleanFlagField IS_INFIX = FlagField.booleanAfter(IS_OPERATOR);
051        public static final BooleanFlagField IS_INLINE = FlagField.booleanAfter(IS_INFIX);
052        public static final BooleanFlagField IS_TAILREC = FlagField.booleanAfter(IS_INLINE);
053        public static final BooleanFlagField IS_EXTERNAL_FUNCTION = FlagField.booleanAfter(IS_TAILREC);
054    
055        // Properties
056    
057        public static final BooleanFlagField IS_VAR = FlagField.booleanAfter(MEMBER_KIND);
058        public static final BooleanFlagField HAS_GETTER = FlagField.booleanAfter(IS_VAR);
059        public static final BooleanFlagField HAS_SETTER = FlagField.booleanAfter(HAS_GETTER);
060        public static final BooleanFlagField IS_CONST = FlagField.booleanAfter(HAS_SETTER);
061        public static final BooleanFlagField IS_LATEINIT = FlagField.booleanAfter(IS_CONST);
062        public static final BooleanFlagField HAS_CONSTANT = FlagField.booleanAfter(IS_LATEINIT);
063    
064        // Parameters
065    
066        public static final BooleanFlagField DECLARES_DEFAULT_VALUE = FlagField.booleanAfter(HAS_ANNOTATIONS);
067        public static final BooleanFlagField IS_CROSSINLINE = FlagField.booleanAfter(DECLARES_DEFAULT_VALUE);
068        public static final BooleanFlagField IS_NOINLINE = FlagField.booleanAfter(IS_CROSSINLINE);
069    
070        // Accessors
071    
072        public static final BooleanFlagField IS_NOT_DEFAULT = FlagField.booleanAfter(MODALITY);
073        public static final BooleanFlagField IS_EXTERNAL_ACCESSOR = FlagField.booleanAfter(IS_NOT_DEFAULT);
074    
075        // ---
076    
077        private static <E> int bitWidth(@NotNull E[] enumEntries) {
078            int length = enumEntries.length - 1;
079            if (length == 0) return 1;
080            for (int i = 31; i >= 0; i--) {
081                if ((length & (1 << i)) != 0) return i + 1;
082            }
083            throw new IllegalStateException("Empty enum: " + enumEntries.getClass());
084        }
085    
086        public static int getClassFlags(
087                boolean hasAnnotations,
088                Visibility visibility,
089                Modality modality,
090                ClassKind kind,
091                boolean inner,
092                boolean isCompanionObject,
093                boolean isData
094        ) {
095            return HAS_ANNOTATIONS.toFlags(hasAnnotations)
096                   | MODALITY.toFlags(modality(modality))
097                   | VISIBILITY.toFlags(visibility(visibility))
098                   | CLASS_KIND.toFlags(classKind(kind, isCompanionObject))
099                   | IS_INNER.toFlags(inner)
100                   | IS_DATA.toFlags(isData)
101                   ;
102        }
103    
104        private static ProtoBuf.Class.Kind classKind(ClassKind kind, boolean isCompanionObject) {
105            if (isCompanionObject) return ProtoBuf.Class.Kind.COMPANION_OBJECT;
106    
107            switch (kind) {
108                case CLASS:
109                    return ProtoBuf.Class.Kind.CLASS;
110                case INTERFACE:
111                    return ProtoBuf.Class.Kind.INTERFACE;
112                case ENUM_CLASS:
113                    return ProtoBuf.Class.Kind.ENUM_CLASS;
114                case ENUM_ENTRY:
115                    return ProtoBuf.Class.Kind.ENUM_ENTRY;
116                case ANNOTATION_CLASS:
117                    return ProtoBuf.Class.Kind.ANNOTATION_CLASS;
118                case OBJECT:
119                    return ProtoBuf.Class.Kind.OBJECT;
120            }
121            throw new IllegalArgumentException("Unknown class kind: " + kind);
122        }
123    
124        public static int getConstructorFlags(
125                boolean hasAnnotations,
126                @NotNull Visibility visibility,
127                boolean isSecondary
128        ) {
129            return HAS_ANNOTATIONS.toFlags(hasAnnotations)
130                   | VISIBILITY.toFlags(visibility(visibility))
131                   | IS_SECONDARY.toFlags(isSecondary)
132                    ;
133        }
134    
135        public static int getFunctionFlags(
136                boolean hasAnnotations,
137                @NotNull Visibility visibility,
138                @NotNull Modality modality,
139                @NotNull CallableMemberDescriptor.Kind memberKind,
140                boolean isOperator,
141                boolean isInfix,
142                boolean isInline,
143                boolean isTailrec,
144                boolean isExternal
145        ) {
146            return HAS_ANNOTATIONS.toFlags(hasAnnotations)
147                   | VISIBILITY.toFlags(visibility(visibility))
148                   | MODALITY.toFlags(modality(modality))
149                   | MEMBER_KIND.toFlags(memberKind(memberKind))
150                   | IS_OPERATOR.toFlags(isOperator)
151                   | IS_INFIX.toFlags(isInfix)
152                   | IS_INLINE.toFlags(isInline)
153                   | IS_TAILREC.toFlags(isTailrec)
154                   | IS_EXTERNAL_FUNCTION.toFlags(isExternal)
155                    ;
156        }
157    
158        public static int getPropertyFlags(
159                boolean hasAnnotations,
160                @NotNull Visibility visibility,
161                @NotNull Modality modality,
162                @NotNull CallableMemberDescriptor.Kind memberKind,
163                boolean isVar,
164                boolean hasGetter,
165                boolean hasSetter,
166                boolean hasConstant,
167                boolean isConst,
168                boolean lateInit
169        ) {
170            return HAS_ANNOTATIONS.toFlags(hasAnnotations)
171                   | VISIBILITY.toFlags(visibility(visibility))
172                   | MODALITY.toFlags(modality(modality))
173                   | MEMBER_KIND.toFlags(memberKind(memberKind))
174                   | IS_VAR.toFlags(isVar)
175                   | HAS_GETTER.toFlags(hasGetter)
176                   | HAS_SETTER.toFlags(hasSetter)
177                   | IS_CONST.toFlags(isConst)
178                   | IS_LATEINIT.toFlags(lateInit)
179                   | HAS_CONSTANT.toFlags(hasConstant)
180                    ;
181        }
182    
183        public static int getAccessorFlags(
184                boolean hasAnnotations,
185                @NotNull Visibility visibility,
186                @NotNull Modality modality,
187                boolean isNotDefault,
188                boolean isExternal
189        ) {
190            return HAS_ANNOTATIONS.toFlags(hasAnnotations)
191                   | MODALITY.toFlags(modality(modality))
192                   | VISIBILITY.toFlags(visibility(visibility))
193                   | IS_NOT_DEFAULT.toFlags(isNotDefault)
194                   | IS_EXTERNAL_ACCESSOR.toFlags(isExternal)
195                   ;
196        }
197    
198        @NotNull
199        private static ProtoBuf.Visibility visibility(@NotNull Visibility visibility) {
200            if (visibility == Visibilities.INTERNAL) {
201                return ProtoBuf.Visibility.INTERNAL;
202            }
203            else if (visibility == Visibilities.PUBLIC) {
204                return ProtoBuf.Visibility.PUBLIC;
205            }
206            else if (visibility == Visibilities.PRIVATE) {
207                return ProtoBuf.Visibility.PRIVATE;
208            }
209            else if (visibility == Visibilities.PRIVATE_TO_THIS) {
210                return ProtoBuf.Visibility.PRIVATE_TO_THIS;
211            }
212            else if (visibility == Visibilities.PROTECTED) {
213                return ProtoBuf.Visibility.PROTECTED;
214            }
215            else if (visibility == Visibilities.LOCAL) {
216                return ProtoBuf.Visibility.LOCAL;
217            }
218            throw new IllegalArgumentException("Unknown visibility: " + visibility);
219        }
220    
221        @NotNull
222        private static ProtoBuf.Modality modality(@NotNull Modality modality) {
223            switch (modality) {
224                case FINAL:
225                    return ProtoBuf.Modality.FINAL;
226                case OPEN:
227                    return ProtoBuf.Modality.OPEN;
228                case ABSTRACT:
229                    return ProtoBuf.Modality.ABSTRACT;
230                case SEALED:
231                    return ProtoBuf.Modality.SEALED;
232            }
233            throw new IllegalArgumentException("Unknown modality: " + modality);
234        }
235    
236        @NotNull
237        private static ProtoBuf.MemberKind memberKind(@NotNull CallableMemberDescriptor.Kind kind) {
238            switch (kind) {
239                case DECLARATION:
240                    return ProtoBuf.MemberKind.DECLARATION;
241                case FAKE_OVERRIDE:
242                    return ProtoBuf.MemberKind.FAKE_OVERRIDE;
243                case DELEGATION:
244                    return ProtoBuf.MemberKind.DELEGATION;
245                case SYNTHESIZED:
246                    return ProtoBuf.MemberKind.SYNTHESIZED;
247            }
248            throw new IllegalArgumentException("Unknown member kind: " + kind);
249        }
250    
251        public static int getValueParameterFlags(
252                boolean hasAnnotations,
253                boolean declaresDefaultValue,
254                boolean isCrossinline,
255                boolean isNoinline
256        ) {
257            return HAS_ANNOTATIONS.toFlags(hasAnnotations)
258                   | DECLARES_DEFAULT_VALUE.toFlags(declaresDefaultValue)
259                   | IS_CROSSINLINE.toFlags(isCrossinline)
260                   | IS_NOINLINE.toFlags(isNoinline)
261                   ;
262        }
263    
264        // Infrastructure
265    
266        public static abstract class FlagField<E> {
267            public static <E extends Internal.EnumLite> FlagField<E> after(FlagField<?> previousField, E[] values) {
268                int offset = previousField.offset + previousField.bitWidth;
269                return new EnumLiteFlagField<E>(offset, values);
270            }
271    
272            public static <E extends Internal.EnumLite> FlagField<E> first(E[] values) {
273                return new EnumLiteFlagField<E>(0, values);
274            }
275    
276            public static BooleanFlagField booleanFirst() {
277                return new BooleanFlagField(0);
278            }
279    
280            public static BooleanFlagField booleanAfter(FlagField<?> previousField) {
281                int offset = previousField.offset + previousField.bitWidth;
282                return new BooleanFlagField(offset);
283            }
284    
285            private final int offset;
286            private final int bitWidth;
287            private final E[] values;
288    
289            private FlagField(int offset, E[] values) {
290                this.offset = offset;
291                this.bitWidth = bitWidth(values);
292                this.values = values;
293            }
294    
295            @Nullable
296            public E get(int flags) {
297                int maskUnshifted = (1 << bitWidth) - 1;
298                int mask = maskUnshifted << offset;
299                int value = (flags & mask) >> offset;
300                for (E e : values) {
301                    if (getIntValue(e) == value) {
302                        return e;
303                    }
304                }
305                return null;
306            }
307    
308            public int toFlags(E value) {
309                return getIntValue(value) << offset;
310            }
311    
312            protected abstract int getIntValue(E value);
313    
314        }
315    
316        public static class BooleanFlagField extends FlagField<Boolean> {
317            private static final Boolean[] BOOLEAN = { false, true };
318    
319            public BooleanFlagField(int offset) {
320                super(offset, BOOLEAN);
321            }
322    
323            @Override
324            protected int getIntValue(Boolean value) {
325                return value ? 1 : 0;
326            }
327    
328            @NotNull
329            @Override
330            public Boolean get(int flags) {
331                //noinspection ConstantConditions
332                return super.get(flags);
333            }
334        }
335    
336        private static class EnumLiteFlagField<E extends Internal.EnumLite> extends FlagField<E> {
337            public EnumLiteFlagField(int offset, E[] values) {
338                super(offset, values);
339            }
340    
341            @Override
342            protected int getIntValue(E value) {
343                return value.getNumber();
344            }
345        }
346    
347    }