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