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