summaryrefslogtreecommitdiff
path: root/runtime/hidden_api_access_flags.h
blob: 80a002d96efe0c2bd39e2d0637d1eb2a11a5420b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef ART_RUNTIME_HIDDEN_API_ACCESS_FLAGS_H_
#define ART_RUNTIME_HIDDEN_API_ACCESS_FLAGS_H_

#include "base/bit_utils.h"
#include "modifiers.h"

namespace art {

/* This class is used for encoding and decoding access flags of class members
 * from the boot class path. These access flags might contain additional two bits
 * of information on whether the given class member should be hidden from apps
 * and under what circumstances.
 *
 * The encoding is different inside DexFile, where we are concerned with size,
 * and at runtime where we want to optimize for speed of access. The class
 * provides helper functions to decode/encode both of them.
 *
 * Encoding in DexFile
 * ===================
 *
 * First bit is encoded as inversion of visibility flags (public/private/protected).
 * At most one can be set for any given class member. If two or three are set,
 * this is interpreted as the first bit being set and actual visibility flags
 * being the complement of the encoded flags.
 *
 * Second bit is either encoded as bit 5 for fields and non-native methods, where
 * it carries no other meaning. If a method is native (bit 8 set), bit 9 is used.
 *
 * Bits were selected so that they never increase the length of unsigned LEB-128
 * encoding of the access flags.
 *
 * Encoding at runtime
 * ===================
 *
 * Two bits are set aside in the uint32_t access flags in the intrinsics ordinal
 * space (thus intrinsics need to be special-cased). These are two consecutive
 * bits and they are directly used to store the integer value of the ApiList
 * enum values.
 *
 */
class HiddenApiAccessFlags {
 public:
  enum ApiList {
    kWhitelist = 0,
    kLightGreylist,
    kDarkGreylist,
    kBlacklist,
  };

  static ALWAYS_INLINE ApiList DecodeFromDex(uint32_t dex_access_flags) {
    DexHiddenAccessFlags flags(dex_access_flags);
    uint32_t int_value = (flags.IsFirstBitSet() ? 1 : 0) + (flags.IsSecondBitSet() ? 2 : 0);
    return static_cast<ApiList>(int_value);
  }

  static ALWAYS_INLINE uint32_t RemoveFromDex(uint32_t dex_access_flags) {
    DexHiddenAccessFlags flags(dex_access_flags);
    flags.SetFirstBit(false);
    flags.SetSecondBit(false);
    return flags.GetEncoding();
  }

  static ALWAYS_INLINE uint32_t EncodeForDex(uint32_t dex_access_flags, ApiList value) {
    DexHiddenAccessFlags flags(RemoveFromDex(dex_access_flags));
    uint32_t int_value = static_cast<uint32_t>(value);
    flags.SetFirstBit((int_value & 1) != 0);
    flags.SetSecondBit((int_value & 2) != 0);
    return flags.GetEncoding();
  }

  static ALWAYS_INLINE ApiList DecodeFromRuntime(uint32_t runtime_access_flags) {
    if ((runtime_access_flags & kAccIntrinsic) != 0) {
      return kWhitelist;
    } else {
      uint32_t int_value = (runtime_access_flags & kAccHiddenApiBits) >> kAccFlagsShift;
      return static_cast<ApiList>(int_value);
    }
  }

  static ALWAYS_INLINE uint32_t EncodeForRuntime(uint32_t runtime_access_flags, ApiList value) {
    CHECK_EQ(runtime_access_flags & kAccIntrinsic, 0u);

    uint32_t hidden_api_flags = static_cast<uint32_t>(value) << kAccFlagsShift;
    CHECK_EQ(hidden_api_flags & ~kAccHiddenApiBits, 0u);

    runtime_access_flags &= ~kAccHiddenApiBits;
    return runtime_access_flags | hidden_api_flags;
  }

 private:
  static const int kAccFlagsShift = CTZ(kAccHiddenApiBits);
  static_assert(IsPowerOfTwo((kAccHiddenApiBits >> kAccFlagsShift) + 1),
                "kAccHiddenApiBits are not continuous");

  struct DexHiddenAccessFlags {
    explicit DexHiddenAccessFlags(uint32_t access_flags) : access_flags_(access_flags) {}

    ALWAYS_INLINE uint32_t GetSecondFlag() {
      return ((access_flags_ & kAccNative) != 0) ? kAccDexHiddenBitNative : kAccDexHiddenBit;
    }

    ALWAYS_INLINE bool IsFirstBitSet() {
      static_assert(IsPowerOfTwo(0u), "Following statement checks if *at most* one bit is set");
      return !IsPowerOfTwo(access_flags_ & kAccVisibilityFlags);
    }

    ALWAYS_INLINE void SetFirstBit(bool value) {
      if (IsFirstBitSet() != value) {
        access_flags_ ^= kAccVisibilityFlags;
      }
    }

    ALWAYS_INLINE bool IsSecondBitSet() {
      return (access_flags_ & GetSecondFlag()) != 0;
    }

    ALWAYS_INLINE void SetSecondBit(bool value) {
      if (value) {
        access_flags_ |= GetSecondFlag();
      } else {
        access_flags_ &= ~GetSecondFlag();
      }
    }

    ALWAYS_INLINE uint32_t GetEncoding() const {
      return access_flags_;
    }

    uint32_t access_flags_;
  };
};

}  // namespace art


#endif  // ART_RUNTIME_HIDDEN_API_ACCESS_FLAGS_H_