summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/dex/dex_to_dex_compiler.h2
-rw-r--r--compiler/optimizing/inliner.h2
-rw-r--r--compiler/optimizing/intrinsics.cc2
-rw-r--r--compiler/optimizing/load_store_elimination.cc378
-rw-r--r--compiler/optimizing/nodes.h2
-rw-r--r--dex2oat/dex2oat.cc2
-rw-r--r--dex2oat/dex2oat_test.cc2
-rw-r--r--dexoptanalyzer/dexoptanalyzer.cc1
-rw-r--r--openjdkjvmti/deopt_manager.cc2
-rw-r--r--openjdkjvmti/ti_breakpoint.cc2
-rw-r--r--openjdkjvmti/ti_field.cc2
-rw-r--r--openjdkjvmti/ti_method.cc2
-rw-r--r--profman/profman.cc50
-rw-r--r--runtime/Android.bp41
-rw-r--r--runtime/art_field.h2
-rw-r--r--runtime/art_method-inl.h9
-rw-r--r--runtime/art_method.h2
-rw-r--r--runtime/base/file_utils.cc5
-rw-r--r--runtime/base/file_utils.h3
-rw-r--r--runtime/common_throws.cc2
-rw-r--r--runtime/dex/dex_file_verifier.cc1
-rw-r--r--runtime/dex/invoke_type.h (renamed from runtime/invoke_type.h)6
-rw-r--r--runtime/dex/modifiers.cc58
-rw-r--r--runtime/dex/modifiers.h (renamed from runtime/modifiers.h)10
-rw-r--r--runtime/entrypoints/entrypoint_utils-inl.h2
-rw-r--r--runtime/hidden_api.h170
-rw-r--r--runtime/hidden_api_access_flags.h2
-rw-r--r--runtime/interpreter/unstarted_runtime.cc20
-rw-r--r--runtime/jit/profile_compilation_info.cc24
-rw-r--r--runtime/jit/profile_compilation_info.h11
-rw-r--r--runtime/jit/profile_compilation_info_test.cc86
-rw-r--r--runtime/jni_internal.cc25
-rw-r--r--runtime/mirror/class-inl.h9
-rw-r--r--runtime/mirror/class.h2
-rw-r--r--runtime/mirror/field.h2
-rw-r--r--runtime/mirror/method_handles_lookup.cc2
-rw-r--r--runtime/native/dalvik_system_ZygoteHooks.cc2
-rw-r--r--runtime/native/java_lang_Class.cc148
-rw-r--r--runtime/native/java_lang_reflect_Field.cc13
-rw-r--r--runtime/oat_file_assistant.cc17
-rw-r--r--runtime/oat_file_assistant.h10
-rw-r--r--runtime/oat_file_assistant_test.cc58
-rw-r--r--runtime/oat_file_manager.cc20
-rw-r--r--runtime/oat_file_manager.h3
-rw-r--r--runtime/reflection.cc14
-rw-r--r--runtime/runtime.cc2
-rw-r--r--runtime/runtime.h28
-rw-r--r--runtime/runtime_intrinsics.cc2
-rw-r--r--runtime/utf.cc3
-rw-r--r--runtime/utils.cc36
-rw-r--r--runtime/utils.h4
-rw-r--r--runtime/vdex_file.h8
-rw-r--r--runtime/verifier/reg_type-inl.h3
-rw-r--r--test/530-checker-lse/src/Main.java175
-rw-r--r--test/608-checker-unresolved-lse/src/Main.java1
-rw-r--r--test/639-checker-code-sinking/expected.txt2
-rw-r--r--test/639-checker-code-sinking/src/Main.java3
-rw-r--r--test/674-hiddenapi/api-blacklist.txt25
-rw-r--r--test/674-hiddenapi/api-dark-greylist.txt25
-rw-r--r--test/674-hiddenapi/api-light-greylist.txt25
-rw-r--r--test/674-hiddenapi/build38
-rw-r--r--test/674-hiddenapi/check23
-rw-r--r--test/674-hiddenapi/expected.txt0
-rw-r--r--test/674-hiddenapi/hiddenapi.cc296
-rw-r--r--test/674-hiddenapi/info.txt15
-rw-r--r--test/674-hiddenapi/src-art/Main.java157
-rw-r--r--test/674-hiddenapi/src-ex/ChildClass.java438
-rw-r--r--test/674-hiddenapi/src-ex/JNI.java29
-rw-r--r--test/674-hiddenapi/src-ex/Linking.java193
-rw-r--r--test/674-hiddenapi/src-ex/Reflection.java205
-rw-r--r--test/674-hiddenapi/src/NullaryConstructorBlacklist.java21
-rw-r--r--test/674-hiddenapi/src/NullaryConstructorDarkGreylist.java21
-rw-r--r--test/674-hiddenapi/src/NullaryConstructorLightGreylist.java21
-rw-r--r--test/674-hiddenapi/src/NullaryConstructorWhitelist.java21
-rw-r--r--test/674-hiddenapi/src/ParentClass.java133
-rw-r--r--test/674-hiddenapi/src/ParentInterface.java41
-rwxr-xr-xtest/674-vdex-uncompress/build19
-rw-r--r--test/674-vdex-uncompress/expected.txt2
-rw-r--r--test/674-vdex-uncompress/info.txt2
-rw-r--r--test/674-vdex-uncompress/run17
-rw-r--r--test/674-vdex-uncompress/src/Main.java37
-rw-r--r--test/Android.bp1
-rw-r--r--test/Android.run-test.mk1
-rwxr-xr-xtest/etc/default-build47
-rw-r--r--test/knownfailures.json5
-rwxr-xr-xtest/run-test6
-rw-r--r--test/ti-agent/common_helper.h2
-rw-r--r--tools/cpp-define-generator/constant_class.def2
-rw-r--r--tools/cpp-define-generator/constant_globals.def2
89 files changed, 2781 insertions, 584 deletions
diff --git a/compiler/dex/dex_to_dex_compiler.h b/compiler/dex/dex_to_dex_compiler.h
index abd048167c..2105a9ded4 100644
--- a/compiler/dex/dex_to_dex_compiler.h
+++ b/compiler/dex/dex_to_dex_compiler.h
@@ -23,8 +23,8 @@
#include "base/bit_vector.h"
#include "dex/dex_file.h"
+#include "dex/invoke_type.h"
#include "handle.h"
-#include "invoke_type.h"
#include "method_reference.h"
#include "quicken_info.h"
diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h
index e81d97b0a8..02465d37ba 100644
--- a/compiler/optimizing/inliner.h
+++ b/compiler/optimizing/inliner.h
@@ -18,7 +18,7 @@
#define ART_COMPILER_OPTIMIZING_INLINER_H_
#include "dex/dex_file_types.h"
-#include "invoke_type.h"
+#include "dex/invoke_type.h"
#include "jit/profile_compilation_info.h"
#include "optimization.h"
diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc
index 6928b70df7..acb830e524 100644
--- a/compiler/optimizing/intrinsics.cc
+++ b/compiler/optimizing/intrinsics.cc
@@ -19,9 +19,9 @@
#include "art_field-inl.h"
#include "art_method-inl.h"
#include "class_linker.h"
+#include "dex/invoke_type.h"
#include "driver/compiler_driver.h"
#include "driver/compiler_options.h"
-#include "invoke_type.h"
#include "mirror/dex_cache-inl.h"
#include "nodes.h"
#include "scoped_thread_state_change-inl.h"
diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc
index aae94b227c..88326d321b 100644
--- a/compiler/optimizing/load_store_elimination.cc
+++ b/compiler/optimizing/load_store_elimination.cc
@@ -25,51 +25,6 @@
#include <iostream>
-/**
- * The general algorithm of load-store elimination (LSE).
- * Load-store analysis in the previous pass collects a list of heap locations
- * and does alias analysis of those heap locations.
- * LSE keeps track of a list of heap values corresponding to the heap
- * locations. It visits basic blocks in reverse post order and for
- * each basic block, visits instructions sequentially, and processes
- * instructions as follows:
- * - If the instruction is a load, and the heap location for that load has a
- * valid heap value, the load can be eliminated. In order to maintain the
- * validity of all heap locations during the optimization phase, the real
- * elimination is delayed till the end of LSE.
- * - If the instruction is a store, it updates the heap value for the heap
- * location of the store with the store instruction. The real heap value
- * can be fetched from the store instruction. Heap values are invalidated
- * for heap locations that may alias with the store instruction's heap
- * location. The store instruction can be eliminated unless the value stored
- * is later needed e.g. by a load from the same/aliased heap location or
- * the heap location persists at method return/deoptimization.
- * The store instruction is also needed if it's not used to track the heap
- * value anymore, e.g. when it fails to merge with the heap values from other
- * predecessors.
- * - A store that stores the same value as the heap value is eliminated.
- * - The list of heap values are merged at basic block entry from the basic
- * block's predecessors. The algorithm is single-pass, so loop side-effects is
- * used as best effort to decide if a heap location is stored inside the loop.
- * - A special type of objects called singletons are instantiated in the method
- * and have a single name, i.e. no aliases. Singletons have exclusive heap
- * locations since they have no aliases. Singletons are helpful in narrowing
- * down the life span of a heap location such that they do not always
- * need to participate in merging heap values. Allocation of a singleton
- * can be eliminated if that singleton is not used and does not persist
- * at method return/deoptimization.
- * - For newly instantiated instances, their heap values are initialized to
- * language defined default values.
- * - Some instructions such as invokes are treated as loading and invalidating
- * all the heap values, depending on the instruction's side effects.
- * - Finalizable objects are considered as persisting at method
- * return/deoptimization.
- * - Currently this LSE algorithm doesn't handle SIMD graph, e.g. with VecLoad
- * and VecStore instructions.
- * - Currently this LSE algorithm doesn't handle graph with try-catch, due to
- * the special block merging structure.
- */
-
namespace art {
// An unknown heap value. Loads with such a value in the heap location cannot be eliminated.
@@ -104,7 +59,8 @@ class LSEVisitor : public HGraphDelegateVisitor {
removed_loads_(allocator_.Adapter(kArenaAllocLSE)),
substitute_instructions_for_loads_(allocator_.Adapter(kArenaAllocLSE)),
possibly_removed_stores_(allocator_.Adapter(kArenaAllocLSE)),
- singleton_new_instances_(allocator_.Adapter(kArenaAllocLSE)) {
+ singleton_new_instances_(allocator_.Adapter(kArenaAllocLSE)),
+ singleton_new_arrays_(allocator_.Adapter(kArenaAllocLSE)) {
}
void VisitBasicBlock(HBasicBlock* block) OVERRIDE {
@@ -132,26 +88,19 @@ class LSEVisitor : public HGraphDelegateVisitor {
return type_conversion;
}
- // Find an instruction's substitute if it's a removed load.
+ // Find an instruction's substitute if it should be removed.
// Return the same instruction if it should not be removed.
HInstruction* FindSubstitute(HInstruction* instruction) {
- if (!IsLoad(instruction)) {
- return instruction;
- }
size_t size = removed_loads_.size();
for (size_t i = 0; i < size; i++) {
if (removed_loads_[i] == instruction) {
- HInstruction* substitute = substitute_instructions_for_loads_[i];
- // The substitute list is a flat hierarchy.
- DCHECK_EQ(FindSubstitute(substitute), substitute);
- return substitute;
+ return substitute_instructions_for_loads_[i];
}
}
return instruction;
}
void AddRemovedLoad(HInstruction* load, HInstruction* heap_value) {
- DCHECK(IsLoad(load));
DCHECK_EQ(FindSubstitute(heap_value), heap_value) <<
"Unexpected heap_value that has a substitute " << heap_value->DebugName();
removed_loads_.push_back(load);
@@ -258,59 +207,28 @@ class LSEVisitor : public HGraphDelegateVisitor {
new_instance->GetBlock()->RemoveInstruction(new_instance);
}
}
- }
-
- private:
- static bool IsLoad(HInstruction* instruction) {
- if (instruction == kUnknownHeapValue || instruction == kDefaultHeapValue) {
- return false;
- }
- // Unresolved load is not treated as a load.
- return instruction->IsInstanceFieldGet() ||
- instruction->IsStaticFieldGet() ||
- instruction->IsArrayGet();
- }
-
- static bool IsStore(HInstruction* instruction) {
- if (instruction == kUnknownHeapValue || instruction == kDefaultHeapValue) {
- return false;
- }
- // Unresolved store is not treated as a store.
- return instruction->IsInstanceFieldSet() ||
- instruction->IsArraySet() ||
- instruction->IsStaticFieldSet();
- }
-
- // Returns the real heap value by finding its substitute or by "peeling"
- // a store instruction.
- HInstruction* GetRealHeapValue(HInstruction* heap_value) {
- if (IsLoad(heap_value)) {
- return FindSubstitute(heap_value);
- }
- if (!IsStore(heap_value)) {
- return heap_value;
- }
+ for (HInstruction* new_array : singleton_new_arrays_) {
+ size_t removed = HConstructorFence::RemoveConstructorFences(new_array);
+ MaybeRecordStat(stats_,
+ MethodCompilationStat::kConstructorFenceRemovedLSE,
+ removed);
- // We keep track of store instructions as the heap values which might be
- // eliminated if the stores are later found not necessary. The real stored
- // value needs to be fetched from the store instruction.
- if (heap_value->IsInstanceFieldSet()) {
- heap_value = heap_value->AsInstanceFieldSet()->GetValue();
- } else if (heap_value->IsStaticFieldSet()) {
- heap_value = heap_value->AsStaticFieldSet()->GetValue();
- } else {
- DCHECK(heap_value->IsArraySet());
- heap_value = heap_value->AsArraySet()->GetValue();
+ if (!new_array->HasNonEnvironmentUses()) {
+ new_array->RemoveEnvironmentUsers();
+ new_array->GetBlock()->RemoveInstruction(new_array);
+ }
}
- // heap_value may already be a removed load.
- return FindSubstitute(heap_value);
}
- // If heap_value is a store, need to keep the store.
- // This is necessary if a heap value is killed or replaced by another value,
- // so that the store is no longer used to track heap value.
+ private:
+ // If heap_values[index] is an instance field store, need to keep the store.
+ // This is necessary if a heap value is killed due to merging, or loop side
+ // effects (which is essentially merging also), since a load later from the
+ // location won't be eliminated.
void KeepIfIsStore(HInstruction* heap_value) {
- if (!IsStore(heap_value)) {
+ if (heap_value == kDefaultHeapValue ||
+ heap_value == kUnknownHeapValue ||
+ !(heap_value->IsInstanceFieldSet() || heap_value->IsArraySet())) {
return;
}
auto idx = std::find(possibly_removed_stores_.begin(),
@@ -321,41 +239,26 @@ class LSEVisitor : public HGraphDelegateVisitor {
}
}
- // If a heap location X may alias with heap location at `loc_index`
- // and heap_values of that heap location X holds a store, keep that store.
- // It's needed for a dependent load that's not eliminated since any store
- // that may put value into the load's heap location needs to be kept.
- void KeepStoresIfAliasedToLocation(ScopedArenaVector<HInstruction*>& heap_values,
- size_t loc_index) {
- for (size_t i = 0; i < heap_values.size(); i++) {
- if ((i == loc_index) || heap_location_collector_.MayAlias(i, loc_index)) {
- KeepIfIsStore(heap_values[i]);
- }
- }
- }
-
void HandleLoopSideEffects(HBasicBlock* block) {
DCHECK(block->IsLoopHeader());
int block_id = block->GetBlockId();
ScopedArenaVector<HInstruction*>& heap_values = heap_values_for_[block_id];
- HBasicBlock* pre_header = block->GetLoopInformation()->GetPreHeader();
- ScopedArenaVector<HInstruction*>& pre_header_heap_values =
- heap_values_for_[pre_header->GetBlockId()];
- // Don't eliminate loads in irreducible loops.
- // Also keep the stores before the loop.
+ // Don't eliminate loads in irreducible loops. This is safe for singletons, because
+ // they are always used by the non-eliminated loop-phi.
if (block->GetLoopInformation()->IsIrreducible()) {
if (kIsDebugBuild) {
for (size_t i = 0; i < heap_values.size(); i++) {
DCHECK_EQ(heap_values[i], kUnknownHeapValue);
}
}
- for (size_t i = 0; i < heap_values.size(); i++) {
- KeepIfIsStore(pre_header_heap_values[i]);
- }
return;
}
+ HBasicBlock* pre_header = block->GetLoopInformation()->GetPreHeader();
+ ScopedArenaVector<HInstruction*>& pre_header_heap_values =
+ heap_values_for_[pre_header->GetBlockId()];
+
// Inherit the values from pre-header.
for (size_t i = 0; i < heap_values.size(); i++) {
heap_values[i] = pre_header_heap_values[i];
@@ -367,17 +270,18 @@ class LSEVisitor : public HGraphDelegateVisitor {
for (size_t i = 0; i < heap_values.size(); i++) {
HeapLocation* location = heap_location_collector_.GetHeapLocation(i);
ReferenceInfo* ref_info = location->GetReferenceInfo();
- if (ref_info->IsSingleton() && !location->IsValueKilledByLoopSideEffects()) {
- // A singleton's field that's not stored into inside a loop is
+ if (ref_info->IsSingletonAndRemovable() &&
+ !location->IsValueKilledByLoopSideEffects()) {
+ // A removable singleton's field that's not stored into inside a loop is
// invariant throughout the loop. Nothing to do.
} else {
- // heap value is killed by loop side effects.
+ // heap value is killed by loop side effects (stored into directly, or
+ // due to aliasing). Or the heap value may be needed after method return
+ // or deoptimization.
KeepIfIsStore(pre_header_heap_values[i]);
heap_values[i] = kUnknownHeapValue;
}
}
- } else {
- // The loop doesn't kill any value.
}
}
@@ -396,73 +300,45 @@ class LSEVisitor : public HGraphDelegateVisitor {
ScopedArenaVector<HInstruction*>& heap_values = heap_values_for_[block->GetBlockId()];
for (size_t i = 0; i < heap_values.size(); i++) {
HInstruction* merged_value = nullptr;
- // If we can merge the store itself from the predecessors, we keep
- // the store as the heap value as long as possible. In case we cannot
- // merge the store, we try to merge the values of the stores.
- HInstruction* merged_store_value = nullptr;
// Whether merged_value is a result that's merged from all predecessors.
bool from_all_predecessors = true;
ReferenceInfo* ref_info = heap_location_collector_.GetHeapLocation(i)->GetReferenceInfo();
- HInstruction* ref = ref_info->GetReference();
HInstruction* singleton_ref = nullptr;
if (ref_info->IsSingleton()) {
- // We do more analysis based on singleton's liveness when merging
- // heap values for such cases.
- singleton_ref = ref;
+ // We do more analysis of liveness when merging heap values for such
+ // cases since stores into such references may potentially be eliminated.
+ singleton_ref = ref_info->GetReference();
}
for (HBasicBlock* predecessor : predecessors) {
HInstruction* pred_value = heap_values_for_[predecessor->GetBlockId()][i];
- if (!IsStore(pred_value)) {
- pred_value = FindSubstitute(pred_value);
- }
- DCHECK(pred_value != nullptr);
- HInstruction* pred_store_value = GetRealHeapValue(pred_value);
if ((singleton_ref != nullptr) &&
!singleton_ref->GetBlock()->Dominates(predecessor)) {
- // singleton_ref is not live in this predecessor. No need to merge
- // since singleton_ref is not live at the beginning of this block.
+ // singleton_ref is not live in this predecessor. Skip this predecessor since
+ // it does not really have the location.
DCHECK_EQ(pred_value, kUnknownHeapValue);
from_all_predecessors = false;
- break;
+ continue;
}
if (merged_value == nullptr) {
// First seen heap value.
- DCHECK(pred_value != nullptr);
merged_value = pred_value;
} else if (pred_value != merged_value) {
// There are conflicting values.
merged_value = kUnknownHeapValue;
- // We may still be able to merge store values.
- }
-
- // Conflicting stores may be storing the same value. We do another merge
- // of real stored values.
- if (merged_store_value == nullptr) {
- // First seen store value.
- DCHECK(pred_store_value != nullptr);
- merged_store_value = pred_store_value;
- } else if (pred_store_value != merged_store_value) {
- // There are conflicting store values.
- merged_store_value = kUnknownHeapValue;
- // There must be conflicting stores also.
- DCHECK_EQ(merged_value, kUnknownHeapValue);
- // No need to merge anymore.
break;
}
}
- if (merged_value == nullptr) {
- DCHECK(!from_all_predecessors);
- DCHECK(singleton_ref != nullptr);
- }
- if (from_all_predecessors) {
- if (ref_info->IsSingletonAndRemovable() &&
- block->IsSingleReturnOrReturnVoidAllowingPhis()) {
- // Values in the singleton are not needed anymore.
- } else if (!IsStore(merged_value)) {
- // We don't track merged value as a store anymore. We have to
- // hold the stores in predecessors live here.
+ if (ref_info->IsSingleton()) {
+ if (ref_info->IsSingletonAndNonRemovable() ||
+ (merged_value == kUnknownHeapValue &&
+ !block->IsSingleReturnOrReturnVoidAllowingPhis())) {
+ // The heap value may be needed after method return or deoptimization,
+ // or there are conflicting heap values from different predecessors and
+ // this block is not a single return,
+ // keep the last store in each predecessor since future loads may not
+ // be eliminated.
for (HBasicBlock* predecessor : predecessors) {
ScopedArenaVector<HInstruction*>& pred_values =
heap_values_for_[predecessor->GetBlockId()];
@@ -470,33 +346,18 @@ class LSEVisitor : public HGraphDelegateVisitor {
}
}
} else {
- DCHECK(singleton_ref != nullptr);
- // singleton_ref is non-existing at the beginning of the block. There is
- // no need to keep the stores.
+ // Currenctly we don't eliminate stores to non-singletons.
}
- if (!from_all_predecessors) {
+ if ((merged_value == nullptr) || !from_all_predecessors) {
DCHECK(singleton_ref != nullptr);
DCHECK((singleton_ref->GetBlock() == block) ||
- !singleton_ref->GetBlock()->Dominates(block))
- << "method: " << GetGraph()->GetMethodName();
+ !singleton_ref->GetBlock()->Dominates(block));
// singleton_ref is not defined before block or defined only in some of its
// predecessors, so block doesn't really have the location at its entry.
heap_values[i] = kUnknownHeapValue;
- } else if (predecessors.size() == 1) {
- // Inherit heap value from the single predecessor.
- DCHECK_EQ(heap_values_for_[predecessors[0]->GetBlockId()][i], merged_value);
- heap_values[i] = merged_value;
} else {
- DCHECK(merged_value == kUnknownHeapValue ||
- merged_value == kDefaultHeapValue ||
- merged_value->GetBlock()->Dominates(block));
- if (merged_value != kUnknownHeapValue) {
- heap_values[i] = merged_value;
- } else {
- // Stores in different predecessors may be storing the same value.
- heap_values[i] = merged_store_value;
- }
+ heap_values[i] = merged_value;
}
}
}
@@ -562,12 +423,23 @@ class LSEVisitor : public HGraphDelegateVisitor {
heap_values[idx] = constant;
return;
}
- heap_value = GetRealHeapValue(heap_value);
+ if (heap_value != kUnknownHeapValue) {
+ if (heap_value->IsInstanceFieldSet() || heap_value->IsArraySet()) {
+ HInstruction* store = heap_value;
+ // This load must be from a singleton since it's from the same
+ // field/element that a "removed" store puts the value. That store
+ // must be to a singleton's field/element.
+ DCHECK(ref_info->IsSingleton());
+ // Get the real heap value of the store.
+ heap_value = heap_value->IsInstanceFieldSet() ? store->InputAt(1) : store->InputAt(2);
+ // heap_value may already have a substitute.
+ heap_value = FindSubstitute(heap_value);
+ }
+ }
if (heap_value == kUnknownHeapValue) {
// Load isn't eliminated. Put the load as the value into the HeapLocation.
// This acts like GVN but with better aliasing analysis.
heap_values[idx] = instruction;
- KeepStoresIfAliasedToLocation(heap_values, idx);
} else {
if (DataType::Kind(heap_value->GetType()) != DataType::Kind(instruction->GetType())) {
// The only situation where the same heap location has different type is when
@@ -580,10 +452,6 @@ class LSEVisitor : public HGraphDelegateVisitor {
DCHECK(heap_value->IsArrayGet()) << heap_value->DebugName();
DCHECK(instruction->IsArrayGet()) << instruction->DebugName();
}
- // Load isn't eliminated. Put the load as the value into the HeapLocation.
- // This acts like GVN but with better aliasing analysis.
- heap_values[idx] = instruction;
- KeepStoresIfAliasedToLocation(heap_values, idx);
return;
}
AddRemovedLoad(instruction, heap_value);
@@ -592,21 +460,12 @@ class LSEVisitor : public HGraphDelegateVisitor {
}
bool Equal(HInstruction* heap_value, HInstruction* value) {
- DCHECK(!IsStore(value)) << value->DebugName();
- if (heap_value == kUnknownHeapValue) {
- // Don't compare kUnknownHeapValue with other values.
- return false;
- }
if (heap_value == value) {
return true;
}
if (heap_value == kDefaultHeapValue && GetDefaultValue(value->GetType()) == value) {
return true;
}
- HInstruction* real_heap_value = GetRealHeapValue(heap_value);
- if (real_heap_value != heap_value) {
- return Equal(real_heap_value, value);
- }
return false;
}
@@ -617,7 +476,6 @@ class LSEVisitor : public HGraphDelegateVisitor {
size_t vector_length,
int16_t declaring_class_def_index,
HInstruction* value) {
- DCHECK(!IsStore(value)) << value->DebugName();
// value may already have a substitute.
value = FindSubstitute(value);
HInstruction* original_ref = heap_location_collector_.HuntForOriginalReference(ref);
@@ -628,47 +486,59 @@ class LSEVisitor : public HGraphDelegateVisitor {
ScopedArenaVector<HInstruction*>& heap_values =
heap_values_for_[instruction->GetBlock()->GetBlockId()];
HInstruction* heap_value = heap_values[idx];
+ bool same_value = false;
bool possibly_redundant = false;
-
if (Equal(heap_value, value)) {
// Store into the heap location with the same value.
- // This store can be eliminated right away.
- instruction->GetBlock()->RemoveInstruction(instruction);
- return;
- } else {
+ same_value = true;
+ } else if (index != nullptr &&
+ heap_location_collector_.GetHeapLocation(idx)->HasAliasedLocations()) {
+ // For array element, don't eliminate stores if the location can be aliased
+ // (due to either ref or index aliasing).
+ } else if (ref_info->IsSingleton()) {
+ // Store into a field/element of a singleton. The value cannot be killed due to
+ // aliasing/invocation. It can be redundant since future loads can
+ // directly get the value set by this instruction. The value can still be killed due to
+ // merging or loop side effects. Stores whose values are killed due to merging/loop side
+ // effects later will be removed from possibly_removed_stores_ when that is detected.
+ // Stores whose values may be needed after method return or deoptimization
+ // are also removed from possibly_removed_stores_ when that is detected.
+ possibly_redundant = true;
HLoopInformation* loop_info = instruction->GetBlock()->GetLoopInformation();
- if (loop_info == nullptr) {
- // Store is not in a loop. We try to precisely track the heap value by
- // the store.
- possibly_redundant = true;
- } else if (!loop_info->IsIrreducible()) {
- // instruction is a store in the loop so the loop must do write.
+ if (loop_info != nullptr) {
+ // instruction is a store in the loop so the loop must does write.
DCHECK(side_effects_.GetLoopEffects(loop_info->GetHeader()).DoesAnyWrite());
- if (ref_info->IsSingleton() && !loop_info->IsDefinedOutOfTheLoop(original_ref)) {
- // original_ref is created inside the loop. Value stored to it isn't needed at
- // the loop header. This is true for outer loops also.
- possibly_redundant = true;
- } else {
+
+ if (loop_info->IsDefinedOutOfTheLoop(original_ref)) {
+ DCHECK(original_ref->GetBlock()->Dominates(loop_info->GetPreHeader()));
// Keep the store since its value may be needed at the loop header.
+ possibly_redundant = false;
+ } else {
+ // The singleton is created inside the loop. Value stored to it isn't needed at
+ // the loop header. This is true for outer loops also.
}
- } else {
- // Keep the store inside irreducible loops.
}
}
- if (possibly_redundant) {
+ if (same_value || possibly_redundant) {
possibly_removed_stores_.push_back(instruction);
}
- // Put the store as the heap value. If the value is loaded or needed after
- // return/deoptimization later, this store isn't really redundant.
- heap_values[idx] = instruction;
-
+ if (!same_value) {
+ if (possibly_redundant) {
+ DCHECK(instruction->IsInstanceFieldSet() || instruction->IsArraySet());
+ // Put the store as the heap value. If the value is loaded from heap
+ // by a load later, this store isn't really redundant.
+ heap_values[idx] = instruction;
+ } else {
+ heap_values[idx] = value;
+ }
+ }
// This store may kill values in other heap locations due to aliasing.
for (size_t i = 0; i < heap_values.size(); i++) {
if (i == idx) {
continue;
}
- if (Equal(heap_values[i], value)) {
+ if (heap_values[i] == value) {
// Same value should be kept even if aliasing happens.
continue;
}
@@ -677,9 +547,7 @@ class LSEVisitor : public HGraphDelegateVisitor {
continue;
}
if (heap_location_collector_.MayAlias(i, idx)) {
- // Kill heap locations that may alias and as a result if the heap value
- // is a store, the store needs to be kept.
- KeepIfIsStore(heap_values[i]);
+ // Kill heap locations that may alias.
heap_values[i] = kUnknownHeapValue;
}
}
@@ -765,35 +633,24 @@ class LSEVisitor : public HGraphDelegateVisitor {
const ScopedArenaVector<HInstruction*>& heap_values =
heap_values_for_[instruction->GetBlock()->GetBlockId()];
for (HInstruction* heap_value : heap_values) {
+ // Filter out fake instructions before checking instruction kind below.
+ if (heap_value == kUnknownHeapValue || heap_value == kDefaultHeapValue) {
+ continue;
+ }
// A store is kept as the heap value for possibly removed stores.
- // That value stored is generally observeable after deoptimization, except
- // for singletons that don't escape after deoptimization.
- if (IsStore(heap_value)) {
- if (heap_value->IsStaticFieldSet()) {
- KeepIfIsStore(heap_value);
- continue;
- }
+ if (heap_value->IsInstanceFieldSet() || heap_value->IsArraySet()) {
+ // Check whether the reference for a store is used by an environment local of
+ // HDeoptimize.
HInstruction* reference = heap_value->InputAt(0);
- if (heap_location_collector_.FindReferenceInfoOf(reference)->IsSingleton()) {
- if (reference->IsNewInstance() && reference->AsNewInstance()->IsFinalizable()) {
- // Finalizable objects alway escape.
+ DCHECK(heap_location_collector_.FindReferenceInfoOf(reference)->IsSingleton());
+ for (const HUseListNode<HEnvironment*>& use : reference->GetEnvUses()) {
+ HEnvironment* user = use.GetUser();
+ if (user->GetHolder() == instruction) {
+ // The singleton for the store is visible at this deoptimization
+ // point. Need to keep the store so that the heap value is
+ // seen by the interpreter.
KeepIfIsStore(heap_value);
- continue;
}
- // Check whether the reference for a store is used by an environment local of
- // HDeoptimize. If not, the singleton is not observed after
- // deoptimizion.
- for (const HUseListNode<HEnvironment*>& use : reference->GetEnvUses()) {
- HEnvironment* user = use.GetUser();
- if (user->GetHolder() == instruction) {
- // The singleton for the store is visible at this deoptimization
- // point. Need to keep the store so that the heap value is
- // seen by the interpreter.
- KeepIfIsStore(heap_value);
- }
- }
- } else {
- KeepIfIsStore(heap_value);
}
}
}
@@ -901,7 +758,7 @@ class LSEVisitor : public HGraphDelegateVisitor {
return;
}
if (ref_info->IsSingletonAndRemovable()) {
- singleton_new_instances_.push_back(new_array);
+ singleton_new_arrays_.push_back(new_array);
}
ScopedArenaVector<HInstruction*>& heap_values =
heap_values_for_[new_array->GetBlock()->GetBlockId()];
@@ -934,6 +791,7 @@ class LSEVisitor : public HGraphDelegateVisitor {
ScopedArenaVector<HInstruction*> possibly_removed_stores_;
ScopedArenaVector<HInstruction*> singleton_new_instances_;
+ ScopedArenaVector<HInstruction*> singleton_new_arrays_;
DISALLOW_COPY_AND_ASSIGN(LSEVisitor);
};
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index b0657d6f1c..a9782a6afd 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -32,11 +32,11 @@
#include "deoptimization_kind.h"
#include "dex/dex_file.h"
#include "dex/dex_file_types.h"
+#include "dex/invoke_type.h"
#include "entrypoints/quick/quick_entrypoints_enum.h"
#include "handle.h"
#include "handle_scope.h"
#include "intrinsics_enum.h"
-#include "invoke_type.h"
#include "locations.h"
#include "method_reference.h"
#include "mirror/class.h"
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index a836d75a72..8555abf9fd 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -2404,7 +2404,7 @@ class Dex2Oat FINAL {
bool AddDexFileSources() {
TimingLogger::ScopedTiming t2("AddDexFileSources", timings_);
- if (input_vdex_file_ != nullptr) {
+ if (input_vdex_file_ != nullptr && input_vdex_file_->HasDexSection()) {
DCHECK_EQ(oat_writers_.size(), 1u);
const std::string& name = zip_location_.empty() ? dex_locations_[0] : zip_location_;
DCHECK(!name.empty());
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index 798e3f6d1c..6fcf6952e8 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -1136,7 +1136,7 @@ TEST_F(Dex2oatClassLoaderContextTest, ContextWithStrippedDexFilesBackedByOdex) {
std::string expected_classpath_key;
{
// Open the oat file to get the expected classpath.
- OatFileAssistant oat_file_assistant(stripped_classpath.c_str(), kRuntimeISA, false);
+ OatFileAssistant oat_file_assistant(stripped_classpath.c_str(), kRuntimeISA, false, false);
std::unique_ptr<OatFile> oat_file(oat_file_assistant.GetBestOatFile());
std::vector<std::unique_ptr<const DexFile>> oat_dex_files =
OatFileAssistant::LoadDexFiles(*oat_file, stripped_classpath.c_str());
diff --git a/dexoptanalyzer/dexoptanalyzer.cc b/dexoptanalyzer/dexoptanalyzer.cc
index bedc4576d5..6d4b3e3e52 100644
--- a/dexoptanalyzer/dexoptanalyzer.cc
+++ b/dexoptanalyzer/dexoptanalyzer.cc
@@ -260,6 +260,7 @@ class DexoptAnalyzer FINAL {
oat_file_assistant = std::make_unique<OatFileAssistant>(dex_file_.c_str(),
isa_,
false /*load_executable*/,
+ false /*only_load_system_executable*/,
vdex_fd_,
oat_fd_,
zip_fd_);
diff --git a/openjdkjvmti/deopt_manager.cc b/openjdkjvmti/deopt_manager.cc
index 53d84836fc..9e11a25e58 100644
--- a/openjdkjvmti/deopt_manager.cc
+++ b/openjdkjvmti/deopt_manager.cc
@@ -38,11 +38,11 @@
#include "base/enums.h"
#include "base/mutex-inl.h"
#include "dex/dex_file_annotations.h"
+#include "dex/modifiers.h"
#include "events-inl.h"
#include "jni_internal.h"
#include "mirror/class-inl.h"
#include "mirror/object_array-inl.h"
-#include "modifiers.h"
#include "nativehelper/scoped_local_ref.h"
#include "runtime_callbacks.h"
#include "scoped_thread_state_change-inl.h"
diff --git a/openjdkjvmti/ti_breakpoint.cc b/openjdkjvmti/ti_breakpoint.cc
index fa7a34401d..d5fffdf439 100644
--- a/openjdkjvmti/ti_breakpoint.cc
+++ b/openjdkjvmti/ti_breakpoint.cc
@@ -39,11 +39,11 @@
#include "base/mutex-inl.h"
#include "deopt_manager.h"
#include "dex/dex_file_annotations.h"
+#include "dex/modifiers.h"
#include "events-inl.h"
#include "jni_internal.h"
#include "mirror/class-inl.h"
#include "mirror/object_array-inl.h"
-#include "modifiers.h"
#include "nativehelper/scoped_local_ref.h"
#include "runtime_callbacks.h"
#include "scoped_thread_state_change-inl.h"
diff --git a/openjdkjvmti/ti_field.cc b/openjdkjvmti/ti_field.cc
index db5c31c43d..c016966d21 100644
--- a/openjdkjvmti/ti_field.cc
+++ b/openjdkjvmti/ti_field.cc
@@ -35,9 +35,9 @@
#include "art_jvmti.h"
#include "base/enums.h"
#include "dex/dex_file_annotations.h"
+#include "dex/modifiers.h"
#include "jni_internal.h"
#include "mirror/object_array-inl.h"
-#include "modifiers.h"
#include "scoped_thread_state_change-inl.h"
#include "thread-current-inl.h"
diff --git a/openjdkjvmti/ti_method.cc b/openjdkjvmti/ti_method.cc
index 57fb699435..3f144c8f0f 100644
--- a/openjdkjvmti/ti_method.cc
+++ b/openjdkjvmti/ti_method.cc
@@ -40,6 +40,7 @@
#include "dex/code_item_accessors-inl.h"
#include "dex/dex_file_annotations.h"
#include "dex/dex_file_types.h"
+#include "dex/modifiers.h"
#include "events-inl.h"
#include "jit/jit.h"
#include "jni_internal.h"
@@ -47,7 +48,6 @@
#include "mirror/class_loader.h"
#include "mirror/object-inl.h"
#include "mirror/object_array-inl.h"
-#include "modifiers.h"
#include "nativehelper/scoped_local_ref.h"
#include "oat_file.h"
#include "runtime_callbacks.h"
diff --git a/profman/profman.cc b/profman/profman.cc
index 9f3e3b6ac5..ffc3c0170f 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -149,6 +149,10 @@ NO_RETURN static void Usage(const char *fmt, ...) {
UsageError(" --boot-image-sampled-method-threshold=<value>: minimum number of profiles a");
UsageError(" non-hot method needs to be in order to be hot in the output profile. The");
UsageError(" default is max int.");
+ UsageError(" --copy-and-update-profile-key: if present, profman will copy the profile from");
+ UsageError(" the file passed with --profile-fd(file) to the profile passed with");
+ UsageError(" --reference-profile-fd(file) and update at the same time the profile-key");
+ UsageError(" of entries corresponding to the apks passed with --apk(-fd).");
UsageError("");
exit(EXIT_FAILURE);
@@ -186,7 +190,8 @@ class ProfMan FINAL {
test_profile_method_percerntage_(kDefaultTestProfileMethodPercentage),
test_profile_class_percentage_(kDefaultTestProfileClassPercentage),
test_profile_seed_(NanoTime()),
- start_ns_(NanoTime()) {}
+ start_ns_(NanoTime()),
+ copy_and_update_profile_key_(false) {}
~ProfMan() {
LogCompletionTime();
@@ -302,11 +307,13 @@ class ProfMan FINAL {
"should only be used together");
}
ProfileAssistant::ProcessingResult result;
+
if (profile_files_.empty()) {
// The file doesn't need to be flushed here (ProcessProfiles will do it)
// so don't check the usage.
File file(reference_profile_file_fd_, false);
- result = ProfileAssistant::ProcessProfiles(profile_files_fd_, reference_profile_file_fd_);
+ result = ProfileAssistant::ProcessProfiles(profile_files_fd_,
+ reference_profile_file_fd_);
CloseAllFds(profile_files_fd_, "profile_files_fd_");
} else {
result = ProfileAssistant::ProcessProfiles(profile_files_, reference_profile_file_);
@@ -314,7 +321,7 @@ class ProfMan FINAL {
return result;
}
- void OpenApkFilesFromLocations(std::vector<std::unique_ptr<const DexFile>>* dex_files) {
+ void OpenApkFilesFromLocations(std::vector<std::unique_ptr<const DexFile>>* dex_files) const {
bool use_apk_fd_list = !apks_fd_.empty();
if (use_apk_fd_list) {
// Get the APKs from the collection of FDs.
@@ -1070,6 +1077,42 @@ class ProfMan FINAL {
return !test_profile_.empty();
}
+ bool ShouldCopyAndUpdateProfileKey() const {
+ return copy_and_update_profile_key_;
+ }
+
+ bool CopyAndUpdateProfileKey() const {
+ // Validate that at least one profile file was passed, as well as a reference profile.
+ if (!(profile_files_.size() == 1 ^ profile_files_fd_.size() == 1)) {
+ Usage("Only one profile file should be specified.");
+ }
+ if (reference_profile_file_.empty() && !FdIsValid(reference_profile_file_fd_)) {
+ Usage("No reference profile file specified.");
+ }
+
+ if (apk_files_.empty() && apks_fd_.empty()) {
+ Usage("No apk files specified");
+ }
+
+ bool use_fds = profile_files_fd_.size() == 1;
+
+ ProfileCompilationInfo profile;
+ // Do not clear if invalid. The input might be an archive.
+ if (profile.Load(profile_files_[0], /*clear_if_invalid*/ false)) {
+ // Open the dex files to look up classes and methods.
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+ OpenApkFilesFromLocations(&dex_files);
+ if (!profile.UpdateProfileKeys(dex_files)) {
+ return false;
+ }
+ return use_fds
+ ? profile.Save(reference_profile_file_fd_)
+ : profile.Save(reference_profile_file_, /*bytes_written*/ nullptr);
+ } else {
+ return false;
+ }
+ }
+
private:
static void ParseFdForCollection(const StringPiece& option,
const char* arg_name,
@@ -1114,6 +1157,7 @@ class ProfMan FINAL {
uint16_t test_profile_class_percentage_;
uint32_t test_profile_seed_;
uint64_t start_ns_;
+ bool copy_and_update_profile_key_;
};
// See ProfileAssistant::ProcessingResult for return codes.
diff --git a/runtime/Android.bp b/runtime/Android.bp
index aba2b0e2a0..f2f7c3e3d0 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -14,12 +14,6 @@
// limitations under the License.
//
-// Keep the __jit_debug_register_code symbol as a unique symbol during ICF for architectures where
-// we use gold as the linker (arm, x86, x86_64). The symbol is used by the debuggers to detect when
-// new jit code is generated. We don't want it to be called when a different function with the same
-// (empty) body is called.
-JIT_DEBUG_REGISTER_CODE_LDFLAGS = ["-Wl,--keep-unique,__jit_debug_register_code"]
-
cc_defaults {
name: "libdexfile_defaults",
defaults: ["art_defaults"],
@@ -33,6 +27,7 @@ cc_defaults {
"dex/dex_file_tracking_registrar.cc",
"dex/dex_file_verifier.cc",
"dex/dex_instruction.cc",
+ "dex/modifiers.cc",
"dex/standard_dex_file.cc",
"utf.cc",
"utils.cc",
@@ -56,16 +51,8 @@ cc_defaults {
],
},
},
- header_libs: [
- "jni_headers",
- ],
generated_sources: ["art_operator_srcs"],
- // asm_support_gen.h (used by asm_support.h) is generated with cpp-define-generator
- generated_headers: ["cpp-define-generator-asm-support"],
- // export our headers so the libart-gtest targets can use it as well.
- export_generated_headers: ["cpp-define-generator-asm-support"],
include_dirs: [
- "external/icu/icu4c/source/common",
"external/zlib",
],
shared_libs: [
@@ -78,11 +65,20 @@ cc_defaults {
// Exporting "." would shadow the system elf.h with our elf.h,
// which in turn breaks any tools that reference this library.
// export_include_dirs: ["."],
+}
- // ART's macros.h depends on libbase's macros.h.
- // Note: runtime_options.h depends on cmdline. But we don't really want to export this
- // generically. dex2oat takes care of it itself.
- export_shared_lib_headers: ["libbase"],
+gensrcs {
+ name: "dexfile_operator_srcs",
+ cmd: "$(location generate-operator-out.py) art/runtime $(in) > $(out)",
+ tool_files: ["generate-operator-out.py"],
+ srcs: [
+ "dex/dex_file.h",
+ "dex/dex_file_layout.h",
+ "dex/dex_instruction.h",
+ "dex/dex_instruction_utils.h",
+ "dex/invoke_type.h",
+ ],
+ output_extension: "operator_out.cc",
}
art_cc_library {
@@ -95,6 +91,12 @@ art_cc_library {
},
}
+// Keep the __jit_debug_register_code symbol as a unique symbol during ICF for architectures where
+// we use gold as the linker (arm, x86, x86_64). The symbol is used by the debuggers to detect when
+// new jit code is generated. We don't want it to be called when a different function with the same
+// (empty) body is called.
+JIT_DEBUG_REGISTER_CODE_LDFLAGS = ["-Wl,--keep-unique,__jit_debug_register_code"]
+
cc_defaults {
name: "libart_defaults",
defaults: ["art_defaults"],
@@ -142,6 +144,7 @@ cc_defaults {
"dex/dex_file_tracking_registrar.cc",
"dex/dex_file_verifier.cc",
"dex/dex_instruction.cc",
+ "dex/modifiers.cc",
"dex/standard_dex_file.cc",
"dex_to_dex_decompiler.cc",
"elf_file.cc",
@@ -535,6 +538,7 @@ gensrcs {
"dex/dex_file_layout.h",
"dex/dex_instruction.h",
"dex/dex_instruction_utils.h",
+ "dex/invoke_type.h",
"gc_root.h",
"gc/allocator_type.h",
"gc/allocator/rosalloc.h",
@@ -547,7 +551,6 @@ gensrcs {
"image.h",
"instrumentation.h",
"indirect_reference_table.h",
- "invoke_type.h",
"jdwp_provider.h",
"jdwp/jdwp.h",
"jdwp/jdwp_constants.h",
diff --git a/runtime/art_field.h b/runtime/art_field.h
index 46b013da7e..0eeeef2f2f 100644
--- a/runtime/art_field.h
+++ b/runtime/art_field.h
@@ -20,8 +20,8 @@
#include <jni.h>
#include "dex/dex_file_types.h"
+#include "dex/modifiers.h"
#include "gc_root.h"
-#include "modifiers.h"
#include "obj_ptr.h"
#include "offsets.h"
#include "primitive.h"
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index c9a77331a7..65bacd8237 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -27,8 +27,9 @@
#include "dex/dex_file-inl.h"
#include "dex/dex_file_annotations.h"
#include "dex/dex_file_types.h"
+#include "dex/invoke_type.h"
#include "gc_root-inl.h"
-#include "invoke_type.h"
+#include "intrinsics_enum.h"
#include "jit/profiling_info.h"
#include "mirror/class-inl.h"
#include "mirror/dex_cache-inl.h"
@@ -412,7 +413,11 @@ inline void ArtMethod::SetIntrinsic(uint32_t intrinsic) {
DCHECK_EQ(is_default_conflict, IsDefaultConflicting());
DCHECK_EQ(is_compilable, IsCompilable());
DCHECK_EQ(must_count_locks, MustCountLocks());
- DCHECK_EQ(hidden_api_list, GetHiddenApiAccessFlags());
+ // We need to special case java.lang.ref.Reference.getRefererent. The Java method
+ // is hidden but we do not yet have a way of making intrinsics hidden.
+ if (intrinsic != static_cast<uint32_t>(Intrinsics::kReferenceGetReferent)) {
+ DCHECK_EQ(hidden_api_list, GetHiddenApiAccessFlags());
+ }
} else {
SetAccessFlags(new_value);
}
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 4501450e05..ce8e8ac612 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -31,8 +31,8 @@
#include "dex/code_item_accessors.h"
#include "dex/dex_file.h"
#include "dex/dex_instruction_iterator.h"
+#include "dex/modifiers.h"
#include "gc_root.h"
-#include "modifiers.h"
#include "obj_ptr.h"
#include "offsets.h"
#include "primitive.h"
diff --git a/runtime/base/file_utils.cc b/runtime/base/file_utils.cc
index 63b4ac56d0..d22fd994ee 100644
--- a/runtime/base/file_utils.cc
+++ b/runtime/base/file_utils.cc
@@ -353,4 +353,9 @@ int MadviseLargestPageAlignedRegion(const uint8_t* begin, const uint8_t* end, in
return 0;
}
+bool LocationIsOnSystem(const char* location) {
+ UniqueCPtr<const char[]> path(realpath(location, nullptr));
+ return path != nullptr && android::base::StartsWith(path.get(), GetAndroidRoot().c_str());
+}
+
} // namespace art
diff --git a/runtime/base/file_utils.h b/runtime/base/file_utils.h
index e4555ad3cb..cac0950d9c 100644
--- a/runtime/base/file_utils.h
+++ b/runtime/base/file_utils.h
@@ -82,6 +82,9 @@ int64_t GetFileSizeBytes(const std::string& filename);
// Madvise the largest page aligned region within begin and end.
int MadviseLargestPageAlignedRegion(const uint8_t* begin, const uint8_t* end, int advice);
+// Return whether the location is on system (i.e. android root).
+bool LocationIsOnSystem(const char* location);
+
} // namespace art
#endif // ART_RUNTIME_BASE_FILE_UTILS_H_
diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc
index 92d86519dc..03774f45cd 100644
--- a/runtime/common_throws.cc
+++ b/runtime/common_throws.cc
@@ -26,7 +26,7 @@
#include "class_linker-inl.h"
#include "dex/dex_file-inl.h"
#include "dex/dex_instruction-inl.h"
-#include "invoke_type.h"
+#include "dex/invoke_type.h"
#include "mirror/class-inl.h"
#include "mirror/method_type.h"
#include "mirror/object-inl.h"
diff --git a/runtime/dex/dex_file_verifier.cc b/runtime/dex/dex_file_verifier.cc
index 5800bb1006..f7fdbb027c 100644
--- a/runtime/dex/dex_file_verifier.cc
+++ b/runtime/dex/dex_file_verifier.cc
@@ -27,6 +27,7 @@
#include "dex_file-inl.h"
#include "experimental_flags.h"
#include "leb128.h"
+#include "modifiers.h"
#include "safe_map.h"
#include "utf-inl.h"
#include "utils.h"
diff --git a/runtime/invoke_type.h b/runtime/dex/invoke_type.h
index 2b877e6f51..726d269a3e 100644
--- a/runtime/invoke_type.h
+++ b/runtime/dex/invoke_type.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ART_RUNTIME_INVOKE_TYPE_H_
-#define ART_RUNTIME_INVOKE_TYPE_H_
+#ifndef ART_RUNTIME_DEX_INVOKE_TYPE_H_
+#define ART_RUNTIME_DEX_INVOKE_TYPE_H_
#include <iosfwd>
@@ -35,4 +35,4 @@ std::ostream& operator<<(std::ostream& os, const InvokeType& rhs);
} // namespace art
-#endif // ART_RUNTIME_INVOKE_TYPE_H_
+#endif // ART_RUNTIME_DEX_INVOKE_TYPE_H_
diff --git a/runtime/dex/modifiers.cc b/runtime/dex/modifiers.cc
new file mode 100644
index 0000000000..30daefb172
--- /dev/null
+++ b/runtime/dex/modifiers.cc
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+#include <string>
+
+#include "modifiers.h"
+
+namespace art {
+
+std::string PrettyJavaAccessFlags(uint32_t access_flags) {
+ std::string result;
+ if ((access_flags & kAccPublic) != 0) {
+ result += "public ";
+ }
+ if ((access_flags & kAccProtected) != 0) {
+ result += "protected ";
+ }
+ if ((access_flags & kAccPrivate) != 0) {
+ result += "private ";
+ }
+ if ((access_flags & kAccFinal) != 0) {
+ result += "final ";
+ }
+ if ((access_flags & kAccStatic) != 0) {
+ result += "static ";
+ }
+ if ((access_flags & kAccAbstract) != 0) {
+ result += "abstract ";
+ }
+ if ((access_flags & kAccInterface) != 0) {
+ result += "interface ";
+ }
+ if ((access_flags & kAccTransient) != 0) {
+ result += "transient ";
+ }
+ if ((access_flags & kAccVolatile) != 0) {
+ result += "volatile ";
+ }
+ if ((access_flags & kAccSynchronized) != 0) {
+ result += "synchronized ";
+ }
+ return result;
+}
+
+} // namespace art
diff --git a/runtime/modifiers.h b/runtime/dex/modifiers.h
index 0e2db932bb..2998f602d4 100644
--- a/runtime/modifiers.h
+++ b/runtime/dex/modifiers.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ART_RUNTIME_MODIFIERS_H_
-#define ART_RUNTIME_MODIFIERS_H_
+#ifndef ART_RUNTIME_DEX_MODIFIERS_H_
+#define ART_RUNTIME_DEX_MODIFIERS_H_
#include <stdint.h>
@@ -138,7 +138,11 @@ static constexpr uint32_t kAccValidInterfaceFlags = kAccPublic | kAccInterface |
static constexpr uint32_t kAccVisibilityFlags = kAccPublic | kAccPrivate | kAccProtected;
+// Returns a human-readable version of the Java part of the access flags, e.g., "private static "
+// (note the trailing whitespace).
+std::string PrettyJavaAccessFlags(uint32_t access_flags);
+
} // namespace art
-#endif // ART_RUNTIME_MODIFIERS_H_
+#endif // ART_RUNTIME_DEX_MODIFIERS_H_
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index 3048f45f30..9ef7d426df 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -25,12 +25,12 @@
#include "class_linker-inl.h"
#include "common_throws.h"
#include "dex/dex_file.h"
+#include "dex/invoke_type.h"
#include "entrypoints/quick/callee_save_frame.h"
#include "handle_scope-inl.h"
#include "imt_conflict_table.h"
#include "imtable-inl.h"
#include "indirect_reference_table.h"
-#include "invoke_type.h"
#include "jni_internal.h"
#include "mirror/array.h"
#include "mirror/class-inl.h"
diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h
new file mode 100644
index 0000000000..de3a51a2ac
--- /dev/null
+++ b/runtime/hidden_api.h
@@ -0,0 +1,170 @@
+/*
+ * 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_H_
+#define ART_RUNTIME_HIDDEN_API_H_
+
+#include "hidden_api_access_flags.h"
+#include "reflection.h"
+#include "runtime.h"
+
+namespace art {
+namespace hiddenapi {
+
+// Returns true if member with `access flags` should only be accessed from
+// boot class path.
+inline bool IsMemberHidden(uint32_t access_flags) {
+ if (!Runtime::Current()->AreHiddenApiChecksEnabled()) {
+ return false;
+ }
+
+ switch (HiddenApiAccessFlags::DecodeFromRuntime(access_flags)) {
+ case HiddenApiAccessFlags::kWhitelist:
+ case HiddenApiAccessFlags::kLightGreylist:
+ case HiddenApiAccessFlags::kDarkGreylist:
+ return false;
+ case HiddenApiAccessFlags::kBlacklist:
+ return true;
+ }
+}
+
+// Returns true if we should warn about non-boot class path accessing member
+// with `access_flags`.
+inline bool ShouldWarnAboutMember(uint32_t access_flags) {
+ if (!Runtime::Current()->AreHiddenApiChecksEnabled()) {
+ return false;
+ }
+
+ switch (HiddenApiAccessFlags::DecodeFromRuntime(access_flags)) {
+ case HiddenApiAccessFlags::kWhitelist:
+ return false;
+ case HiddenApiAccessFlags::kLightGreylist:
+ case HiddenApiAccessFlags::kDarkGreylist:
+ return true;
+ case HiddenApiAccessFlags::kBlacklist:
+ // We should never access a blacklisted member from non-boot class path,
+ // but this function is called before we establish the origin of the access.
+ // Return false here, we do not want to warn when boot class path accesses
+ // a blacklisted member.
+ return false;
+ }
+}
+
+// Returns true if caller `num_frames` up the stack is in boot class path.
+inline bool IsCallerInBootClassPath(Thread* self, size_t num_frames)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::Class> klass = GetCallingClass(self, num_frames);
+ if (klass == nullptr) {
+ // Unattached native thread. Assume that this is *not* boot class path.
+ return false;
+ }
+ return klass->IsBootStrapClassLoaded();
+}
+
+// Returns true if `caller` should not be allowed to access member with `access_flags`.
+inline bool ShouldBlockAccessToMember(uint32_t access_flags, mirror::Class* caller)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return IsMemberHidden(access_flags) &&
+ !caller->IsBootStrapClassLoaded();
+}
+
+// Returns true if `caller` should not be allowed to access `member`.
+template<typename T>
+inline bool ShouldBlockAccessToMember(T* member, ArtMethod* caller)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(member != nullptr);
+ DCHECK(!caller->IsRuntimeMethod());
+ return ShouldBlockAccessToMember(member->GetAccessFlags(), caller->GetDeclaringClass());
+}
+
+// Returns true if the caller `num_frames` up the stack should not be allowed
+// to access `member`.
+template<typename T>
+inline bool ShouldBlockAccessToMember(T* member, Thread* self, size_t num_frames)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(member != nullptr);
+ return IsMemberHidden(member->GetAccessFlags()) &&
+ !IsCallerInBootClassPath(self, num_frames); // This is expensive. Save it for last.
+}
+
+// Issue a warning about field access.
+inline void WarnAboutMemberAccess(ArtField* field) REQUIRES_SHARED(Locks::mutator_lock_) {
+ Runtime::Current()->SetPendingHiddenApiWarning(true);
+ LOG(WARNING) << "Access to hidden field " << field->PrettyField();
+}
+
+// Issue a warning about method access.
+inline void WarnAboutMemberAccess(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) {
+ Runtime::Current()->SetPendingHiddenApiWarning(true);
+ LOG(WARNING) << "Access to hidden method " << method->PrettyMethod();
+}
+
+// Set access flags of `member` to be in hidden API whitelist. This can be disabled
+// with a Runtime::SetDedupHiddenApiWarnings.
+template<typename T>
+inline void MaybeWhitelistMember(T* member) REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (Runtime::Current()->ShouldDedupeHiddenApiWarnings()) {
+ member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime(
+ member->GetAccessFlags(), HiddenApiAccessFlags::kWhitelist));
+ DCHECK(!ShouldWarnAboutMember(member->GetAccessFlags()));
+ }
+}
+
+// Check if `caller` should be allowed to access `member` and warn if not.
+template<typename T>
+inline void MaybeWarnAboutMemberAccess(T* member, ArtMethod* caller)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(member != nullptr);
+ DCHECK(!caller->IsRuntimeMethod());
+ if (!Runtime::Current()->AreHiddenApiChecksEnabled() ||
+ member == nullptr ||
+ !ShouldWarnAboutMember(member->GetAccessFlags()) ||
+ caller->GetDeclaringClass()->IsBootStrapClassLoaded()) {
+ return;
+ }
+
+ WarnAboutMember(member);
+ MaybeWhitelistMember(member);
+}
+
+// Check if the caller `num_frames` up the stack should be allowed to access
+// `member` and warn if not.
+template<typename T>
+inline void MaybeWarnAboutMemberAccess(T* member, Thread* self, size_t num_frames)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (!Runtime::Current()->AreHiddenApiChecksEnabled() ||
+ member == nullptr ||
+ !ShouldWarnAboutMember(member->GetAccessFlags())) {
+ return;
+ }
+
+ // Walk the stack to find the caller. This is *very* expensive. Save it for last.
+ ObjPtr<mirror::Class> klass = GetCallingClass(self, num_frames);
+ if (klass == nullptr) {
+ // Unattached native thread, assume that this is *not* boot class path
+ // and enforce the rules.
+ } else if (klass->IsBootStrapClassLoaded()) {
+ return;
+ }
+
+ WarnAboutMemberAccess(member);
+ MaybeWhitelistMember(member);
+}
+
+} // namespace hiddenapi
+} // namespace art
+
+#endif // ART_RUNTIME_HIDDEN_API_H_
diff --git a/runtime/hidden_api_access_flags.h b/runtime/hidden_api_access_flags.h
index 80a002d96e..c328f965d2 100644
--- a/runtime/hidden_api_access_flags.h
+++ b/runtime/hidden_api_access_flags.h
@@ -18,7 +18,7 @@
#define ART_RUNTIME_HIDDEN_API_ACCESS_FLAGS_H_
#include "base/bit_utils.h"
-#include "modifiers.h"
+#include "dex/modifiers.h"
namespace art {
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index d1436fa9cf..b9b00519d1 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -38,6 +38,7 @@
#include "entrypoints/entrypoint_utils-inl.h"
#include "gc/reference_processor.h"
#include "handle_scope-inl.h"
+#include "hidden_api.h"
#include "interpreter/interpreter_common.h"
#include "jvalue-inl.h"
#include "mirror/array-inl.h"
@@ -265,7 +266,11 @@ void UnstartedRuntime::UnstartedClassNewInstance(
bool ok = false;
auto* cl = Runtime::Current()->GetClassLinker();
if (cl->EnsureInitialized(self, h_klass, true, true)) {
- auto* cons = h_klass->FindConstructor("()V", cl->GetImagePointerSize());
+ ArtMethod* cons = h_klass->FindConstructor("()V", cl->GetImagePointerSize());
+ if (cons != nullptr &&
+ hiddenapi::ShouldBlockAccessToMember(cons, shadow_frame->GetMethod())) {
+ cons = nullptr;
+ }
if (cons != nullptr) {
Handle<mirror::Object> h_obj(hs.NewHandle(klass->AllocObject(self)));
CHECK(h_obj != nullptr); // We don't expect OOM at compile-time.
@@ -308,6 +313,10 @@ void UnstartedRuntime::UnstartedClassGetDeclaredField(
}
}
}
+ if (found != nullptr &&
+ hiddenapi::ShouldBlockAccessToMember(found, shadow_frame->GetMethod())) {
+ found = nullptr;
+ }
if (found == nullptr) {
AbortTransactionOrFail(self, "Failed to find field in Class.getDeclaredField in un-started "
" runtime. name=%s class=%s", name2->ToModifiedUtf8().c_str(),
@@ -370,6 +379,10 @@ void UnstartedRuntime::UnstartedClassGetDeclaredMethod(
self, klass, name, args);
}
}
+ if (method != nullptr &&
+ hiddenapi::ShouldBlockAccessToMember(method->GetArtMethod(), shadow_frame->GetMethod())) {
+ method = nullptr;
+ }
result->SetL(method);
}
@@ -404,6 +417,11 @@ void UnstartedRuntime::UnstartedClassGetDeclaredConstructor(
false>(self, klass, args);
}
}
+ if (constructor != nullptr &&
+ hiddenapi::ShouldBlockAccessToMember(
+ constructor->GetArtMethod(), shadow_frame->GetMethod())) {
+ constructor = nullptr;
+ }
result->SetL(constructor);
}
diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc
index 33fa0d6a26..4bf2895723 100644
--- a/runtime/jit/profile_compilation_info.cc
+++ b/runtime/jit/profile_compilation_info.cc
@@ -2030,4 +2030,28 @@ bool ProfileCompilationInfo::IsProfileFile(int fd) {
return memcmp(buffer, kProfileMagic, byte_count) == 0;
}
+bool ProfileCompilationInfo::UpdateProfileKeys(
+ const std::vector<std::unique_ptr<const DexFile>>& dex_files) {
+ for (const std::unique_ptr<const DexFile>& dex_file : dex_files) {
+ for (DexFileData* dex_data : info_) {
+ if (dex_data->checksum == dex_file->GetLocationChecksum()
+ && dex_data->num_method_ids == dex_file->NumMethodIds()) {
+ std::string new_profile_key = GetProfileDexFileKey(dex_file->GetLocation());
+ if (dex_data->profile_key != new_profile_key) {
+ if (profile_key_map_.find(new_profile_key) != profile_key_map_.end()) {
+ // We can't update the key if the new key belongs to a different dex file.
+ LOG(ERROR) << "Cannot update profile key to " << new_profile_key
+ << " because the new key belongs to another dex file.";
+ return false;
+ }
+ profile_key_map_.erase(dex_data->profile_key);
+ profile_key_map_.Put(new_profile_key, dex_data->profile_index);
+ dex_data->profile_key = new_profile_key;
+ }
+ }
+ }
+ }
+ return true;
+}
+
} // namespace art
diff --git a/runtime/jit/profile_compilation_info.h b/runtime/jit/profile_compilation_info.h
index 29a4c115fa..350ce9ed8d 100644
--- a/runtime/jit/profile_compilation_info.h
+++ b/runtime/jit/profile_compilation_info.h
@@ -416,6 +416,17 @@ class ProfileCompilationInfo {
// Return true if the fd points to a profile file.
bool IsProfileFile(int fd);
+ // Update the profile keys corresponding to the given dex files based on their current paths.
+ // This method allows fix-ups in the profile for dex files that might have been renamed.
+ // The new profile key will be constructed based on the current dex location.
+ //
+ // The matching [profile key <-> dex_file] is done based on the dex checksum and the number of
+ // methods ids. If neither is a match then the profile key is not updated.
+ //
+ // If the new profile key would collide with an existing key (for a different dex)
+ // the method returns false. Otherwise it returns true.
+ bool UpdateProfileKeys(const std::vector<std::unique_ptr<const DexFile>>& dex_files);
+
private:
enum ProfileLoadStatus {
kProfileLoadWouldOverwiteData,
diff --git a/runtime/jit/profile_compilation_info_test.cc b/runtime/jit/profile_compilation_info_test.cc
index 55989d8c52..b4265d1a28 100644
--- a/runtime/jit/profile_compilation_info_test.cc
+++ b/runtime/jit/profile_compilation_info_test.cc
@@ -22,6 +22,7 @@
#include "class_linker-inl.h"
#include "common_runtime_test.h"
#include "dex/dex_file.h"
+#include "dex/dex_file_loader.h"
#include "handle_scope-inl.h"
#include "jit/profile_compilation_info.h"
#include "linear_alloc.h"
@@ -1038,4 +1039,89 @@ TEST_F(ProfileCompilationInfoTest, LoadFromZipFailBadProfile) {
ASSERT_FALSE(loaded_info.Load(GetFd(zip)));
}
+TEST_F(ProfileCompilationInfoTest, UpdateProfileKeyOk) {
+ std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("MultiDex");
+
+ ProfileCompilationInfo info;
+ for (const std::unique_ptr<const DexFile>& dex : dex_files) {
+ // Create the profile with a different location so that we can update it to the
+ // real dex location later.
+ std::string base_location = DexFileLoader::GetBaseLocation(dex->GetLocation());
+ std::string multidex_suffix = DexFileLoader::GetMultiDexSuffix(dex->GetLocation());
+ std::string old_name = base_location + "-old" + multidex_suffix;
+ info.AddMethodIndex(Hotness::kFlagHot,
+ old_name,
+ dex->GetLocationChecksum(),
+ /* method_idx */ 0,
+ dex->NumMethodIds());
+ }
+
+ // Update the profile keys based on the original dex files
+ ASSERT_TRUE(info.UpdateProfileKeys(dex_files));
+
+ // Verify that we find the methods when searched with the original dex files.
+ for (const std::unique_ptr<const DexFile>& dex : dex_files) {
+ std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi =
+ info.GetMethod(dex->GetLocation(), dex->GetLocationChecksum(), /* method_idx */ 0);
+ ASSERT_TRUE(loaded_pmi != nullptr);
+ }
+}
+
+TEST_F(ProfileCompilationInfoTest, UpdateProfileKeyOkButNoUpdate) {
+ std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("MultiDex");
+
+ ProfileCompilationInfo info;
+ info.AddMethodIndex(Hotness::kFlagHot,
+ "my.app",
+ /* checksum */ 123,
+ /* method_idx */ 0,
+ /* num_method_ids */ 10);
+
+ // Update the profile keys based on the original dex files
+ ASSERT_TRUE(info.UpdateProfileKeys(dex_files));
+
+ // Verify that we did not perform any update and that we cannot find anything with the new
+ // location.
+ for (const std::unique_ptr<const DexFile>& dex : dex_files) {
+ std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi =
+ info.GetMethod(dex->GetLocation(), dex->GetLocationChecksum(), /* method_idx */ 0);
+ ASSERT_TRUE(loaded_pmi == nullptr);
+ }
+
+ // Verify that we can find the original entry.
+ std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi =
+ info.GetMethod("my.app", /* checksum */ 123, /* method_idx */ 0);
+ ASSERT_TRUE(loaded_pmi != nullptr);
+}
+
+TEST_F(ProfileCompilationInfoTest, UpdateProfileKeyFail) {
+ std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("MultiDex");
+
+
+ ProfileCompilationInfo info;
+ // Add all dex
+ for (const std::unique_ptr<const DexFile>& dex : dex_files) {
+ // Create the profile with a different location so that we can update it to the
+ // real dex location later.
+ std::string base_location = DexFileLoader::GetBaseLocation(dex->GetLocation());
+ std::string multidex_suffix = DexFileLoader::GetMultiDexSuffix(dex->GetLocation());
+ std::string old_name = base_location + "-old" + multidex_suffix;
+ info.AddMethodIndex(Hotness::kFlagHot,
+ old_name,
+ dex->GetLocationChecksum(),
+ /* method_idx */ 0,
+ dex->NumMethodIds());
+ }
+
+ // Add a method index using the location we want to rename to.
+ // This will cause the rename to fail because an existing entry would already have that name.
+ info.AddMethodIndex(Hotness::kFlagHot,
+ dex_files[0]->GetLocation(),
+ /* checksum */ 123,
+ /* method_idx */ 0,
+ dex_files[0]->NumMethodIds());
+
+ ASSERT_FALSE(info.UpdateProfileKeys(dex_files));
+}
+
} // namespace art
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index b8e6ebe8d8..c40360f612 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -34,6 +34,7 @@
#include "class_linker-inl.h"
#include "dex/dex_file-inl.h"
#include "fault_handler.h"
+#include "hidden_api.h"
#include "gc/accounting/card_table-inl.h"
#include "gc_root.h"
#include "indirect_reference_table-inl.h"
@@ -79,15 +80,17 @@ namespace art {
// things not rendering correctly. E.g. b/16858794
static constexpr bool kWarnJniAbort = false;
-// Helpers to call instrumentation functions for fields. These take jobjects so we don't need to set
-// up handles for the rare case where these actually do something. Once these functions return it is
-// possible there will be a pending exception if the instrumentation happens to throw one.
+// Helpers to check if we need to warn about accessing hidden API fields and to call instrumentation
+// functions for them. These take jobjects so we don't need to set up handles for the rare case
+// where these actually do something. Once these functions return it is possible there will be
+// a pending exception if the instrumentation happens to throw one.
static void NotifySetObjectField(ArtField* field, jobject obj, jobject jval)
REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK_EQ(field->GetTypeAsPrimitiveType(), Primitive::kPrimNot);
+ Thread* self = Thread::Current();
+ hiddenapi::MaybeWarnAboutMemberAccess(field, self, /* num_frames */ 1);
instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
if (UNLIKELY(instrumentation->HasFieldWriteListeners())) {
- Thread* self = Thread::Current();
ArtMethod* cur_method = self->GetCurrentMethod(/*dex_pc*/ nullptr,
/*check_suspended*/ true,
/*abort_on_error*/ false);
@@ -112,9 +115,10 @@ static void NotifySetObjectField(ArtField* field, jobject obj, jobject jval)
static void NotifySetPrimitiveField(ArtField* field, jobject obj, JValue val)
REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK_NE(field->GetTypeAsPrimitiveType(), Primitive::kPrimNot);
+ Thread* self = Thread::Current();
+ hiddenapi::MaybeWarnAboutMemberAccess(field, self, /* num_frames */ 1);
instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
if (UNLIKELY(instrumentation->HasFieldWriteListeners())) {
- Thread* self = Thread::Current();
ArtMethod* cur_method = self->GetCurrentMethod(/*dex_pc*/ nullptr,
/*check_suspended*/ true,
/*abort_on_error*/ false);
@@ -136,9 +140,10 @@ static void NotifySetPrimitiveField(ArtField* field, jobject obj, JValue val)
static void NotifyGetField(ArtField* field, jobject obj)
REQUIRES_SHARED(Locks::mutator_lock_) {
+ Thread* self = Thread::Current();
+ hiddenapi::MaybeWarnAboutMemberAccess(field, self, /* num_frames */ 1);
instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
if (UNLIKELY(instrumentation->HasFieldReadListeners())) {
- Thread* self = Thread::Current();
ArtMethod* cur_method = self->GetCurrentMethod(/*dex_pc*/ nullptr,
/*check_suspended*/ true,
/*abort_on_error*/ false);
@@ -238,6 +243,10 @@ static jmethodID FindMethodID(ScopedObjectAccess& soa, jclass jni_class,
} else {
method = c->FindClassMethod(name, sig, pointer_size);
}
+ if (method != nullptr &&
+ hiddenapi::ShouldBlockAccessToMember(method, soa.Self(), /* num_frames */ 1)) {
+ method = nullptr;
+ }
if (method == nullptr || method->IsStatic() != is_static) {
ThrowNoSuchMethodError(soa, c, name, sig, is_static ? "static" : "non-static");
return nullptr;
@@ -314,6 +323,10 @@ static jfieldID FindFieldID(const ScopedObjectAccess& soa, jclass jni_class, con
} else {
field = c->FindInstanceField(name, field_type->GetDescriptor(&temp));
}
+ if (field != nullptr &&
+ hiddenapi::ShouldBlockAccessToMember(field, soa.Self(), /* num_frames */ 1)) {
+ field = nullptr;
+ }
if (field == nullptr) {
soa.Self()->ThrowNewExceptionF("Ljava/lang/NoSuchFieldError;",
"no \"%s\" field \"%s\" in class \"%s\" or its superclasses",
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 302a5e622e..36388eb3aa 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -26,11 +26,12 @@
#include "class_linker.h"
#include "class_loader.h"
#include "common_throws.h"
-#include "dex_cache.h"
#include "dex/dex_file-inl.h"
+#include "dex/invoke_type.h"
+#include "dex_cache.h"
#include "gc/heap-inl.h"
+#include "hidden_api.h"
#include "iftable.h"
-#include "invoke_type.h"
#include "subtype_check.h"
#include "object-inl.h"
#include "object_array.h"
@@ -1143,6 +1144,10 @@ inline bool Class::CanAccessMember(ObjPtr<Class> access_to, uint32_t member_flag
if (this == access_to) {
return true;
}
+ // Do not allow non-boot class path classes access hidden APIs.
+ if (hiddenapi::ShouldBlockAccessToMember(member_flags, this)) {
+ return false;
+ }
// Public members are trivially accessible
if (member_flags & kAccPublic) {
return true;
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 84b032620f..ced7c7c908 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -25,10 +25,10 @@
#include "class_status.h"
#include "dex/dex_file.h"
#include "dex/dex_file_types.h"
+#include "dex/modifiers.h"
#include "gc/allocator_type.h"
#include "gc_root.h"
#include "imtable.h"
-#include "modifiers.h"
#include "object.h"
#include "object_array.h"
#include "primitive.h"
diff --git a/runtime/mirror/field.h b/runtime/mirror/field.h
index 6845575d18..dd09be331a 100644
--- a/runtime/mirror/field.h
+++ b/runtime/mirror/field.h
@@ -19,8 +19,8 @@
#include "accessible_object.h"
#include "base/enums.h"
+#include "dex/modifiers.h"
#include "gc_root.h"
-#include "modifiers.h"
#include "obj_ptr.h"
#include "object.h"
#include "primitive.h"
diff --git a/runtime/mirror/method_handles_lookup.cc b/runtime/mirror/method_handles_lookup.cc
index a390a2ef53..039bbf2932 100644
--- a/runtime/mirror/method_handles_lookup.cc
+++ b/runtime/mirror/method_handles_lookup.cc
@@ -17,11 +17,11 @@
#include "method_handles_lookup.h"
#include "class-inl.h"
+#include "dex/modifiers.h"
#include "gc_root-inl.h"
#include "handle_scope.h"
#include "jni_internal.h"
#include "mirror/method_handle_impl.h"
-#include "modifiers.h"
#include "object-inl.h"
#include "well_known_classes.h"
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index e22726b79b..2892967a51 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -286,7 +286,7 @@ static void ZygoteHooks_nativePostForkChild(JNIEnv* env,
}
if ((runtime_flags & DISABLE_HIDDEN_API_CHECKS) != 0) {
- Runtime::Current()->DisableHiddenApiChecks();
+ Runtime::Current()->SetHiddenApiChecksEnabled(false);
runtime_flags &= ~DISABLE_HIDDEN_API_CHECKS;
}
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index 7b999c04af..5544275984 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -25,6 +25,7 @@
#include "common_throws.h"
#include "dex/dex_file-inl.h"
#include "dex/dex_file_annotations.h"
+#include "hidden_api.h"
#include "jni_internal.h"
#include "mirror/class-inl.h"
#include "mirror/class_loader.h"
@@ -47,6 +48,75 @@
namespace art {
+ALWAYS_INLINE static bool ShouldEnforceHiddenApi(Thread* self)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (!Runtime::Current()->AreHiddenApiChecksEnabled()) {
+ return false;
+ }
+
+ // Walk the stack and find the first frame not from java.lang.Class.
+ // This is very expensive. Save this till the last.
+ struct FirstNonClassClassCallerVisitor : public StackVisitor {
+ explicit FirstNonClassClassCallerVisitor(Thread* thread)
+ : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames),
+ caller(nullptr) {
+ }
+
+ bool VisitFrame() REQUIRES_SHARED(Locks::mutator_lock_) {
+ ArtMethod *m = GetMethod();
+ if (m == nullptr) {
+ // Attached native thread. Assume this is *not* boot class path.
+ caller = nullptr;
+ return false;
+ } else if (m->IsRuntimeMethod()) {
+ // Internal runtime method, continue walking the stack.
+ return true;
+ } else if (m->GetDeclaringClass()->IsClassClass()) {
+ return true;
+ } else {
+ caller = m;
+ return false;
+ }
+ }
+
+ ArtMethod* caller;
+ };
+
+ FirstNonClassClassCallerVisitor visitor(self);
+ visitor.WalkStack();
+ return visitor.caller == nullptr ||
+ !visitor.caller->GetDeclaringClass()->IsBootStrapClassLoaded();
+}
+
+// Returns true if the first non-ClassClass caller up the stack should not be
+// allowed access to `member`.
+template<typename T>
+ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(member != nullptr);
+ return hiddenapi::IsMemberHidden(member->GetAccessFlags()) &&
+ ShouldEnforceHiddenApi(self);
+}
+
+// Returns true if a class member should be discoverable with reflection given
+// the criteria. Some reflection calls only return public members
+// (public_only == true), some members should be hidden from non-boot class path
+// callers (enforce_hidden_api == true).
+ALWAYS_INLINE static bool IsDiscoverable(bool public_only,
+ bool enforce_hidden_api,
+ uint32_t access_flags) {
+ if (public_only && ((access_flags & kAccPublic) == 0)) {
+ return false;
+ }
+
+ if (enforce_hidden_api && hiddenapi::IsMemberHidden(access_flags)) {
+ return false;
+ }
+
+ return true;
+}
+
+
ALWAYS_INLINE static inline ObjPtr<mirror::Class> DecodeClass(
const ScopedFastNativeObjectAccess& soa, jobject java_class)
REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -164,17 +234,16 @@ static mirror::ObjectArray<mirror::Field>* GetDeclaredFields(
IterationRange<StrideIterator<ArtField>> ifields = klass->GetIFields();
IterationRange<StrideIterator<ArtField>> sfields = klass->GetSFields();
size_t array_size = klass->NumInstanceFields() + klass->NumStaticFields();
- if (public_only) {
- // Lets go subtract all the non public fields.
- for (ArtField& field : ifields) {
- if (!field.IsPublic()) {
- --array_size;
- }
+ bool enforce_hidden_api = ShouldEnforceHiddenApi(self);
+ // Lets go subtract all the non discoverable fields.
+ for (ArtField& field : ifields) {
+ if (!IsDiscoverable(public_only, enforce_hidden_api, field.GetAccessFlags())) {
+ --array_size;
}
- for (ArtField& field : sfields) {
- if (!field.IsPublic()) {
- --array_size;
- }
+ }
+ for (ArtField& field : sfields) {
+ if (!IsDiscoverable(public_only, enforce_hidden_api, field.GetAccessFlags())) {
+ --array_size;
}
}
size_t array_idx = 0;
@@ -184,7 +253,7 @@ static mirror::ObjectArray<mirror::Field>* GetDeclaredFields(
return nullptr;
}
for (ArtField& field : ifields) {
- if (!public_only || field.IsPublic()) {
+ if (IsDiscoverable(public_only, enforce_hidden_api, field.GetAccessFlags())) {
auto* reflect_field = mirror::Field::CreateFromArtField<kRuntimePointerSize>(self,
&field,
force_resolve);
@@ -199,7 +268,7 @@ static mirror::ObjectArray<mirror::Field>* GetDeclaredFields(
}
}
for (ArtField& field : sfields) {
- if (!public_only || field.IsPublic()) {
+ if (IsDiscoverable(public_only, enforce_hidden_api, field.GetAccessFlags())) {
auto* reflect_field = mirror::Field::CreateFromArtField<kRuntimePointerSize>(self,
&field,
force_resolve);
@@ -354,8 +423,13 @@ static jobject Class_getPublicFieldRecursive(JNIEnv* env, jobject javaThis, jstr
ThrowNullPointerException("name == null");
return nullptr;
}
- return soa.AddLocalReference<jobject>(
- GetPublicFieldRecursive(soa.Self(), DecodeClass(soa, javaThis), name_string));
+
+ mirror::Field* field = GetPublicFieldRecursive(
+ soa.Self(), DecodeClass(soa, javaThis), name_string);
+ if (field == nullptr || ShouldBlockAccessToMember(field->GetArtField(), soa.Self())) {
+ return nullptr;
+ }
+ return soa.AddLocalReference<jobject>(field);
}
static jobject Class_getDeclaredField(JNIEnv* env, jobject javaThis, jstring name) {
@@ -369,7 +443,7 @@ static jobject Class_getDeclaredField(JNIEnv* env, jobject javaThis, jstring nam
Handle<mirror::Class> h_klass = hs.NewHandle(DecodeClass(soa, javaThis));
Handle<mirror::Field> result =
hs.NewHandle(GetDeclaredField(soa.Self(), h_klass.Get(), h_string.Get()));
- if (result == nullptr) {
+ if (result == nullptr || ShouldBlockAccessToMember(result->GetArtField(), soa.Self())) {
std::string name_str = h_string->ToModifiedUtf8();
if (name_str == "value" && h_klass->IsStringClass()) {
// We log the error for this specific case, as the user might just swallow the exception.
@@ -399,24 +473,32 @@ static jobject Class_getDeclaredConstructorInternal(
soa.Self(),
DecodeClass(soa, javaThis),
soa.Decode<mirror::ObjectArray<mirror::Class>>(args));
+ if (result == nullptr || ShouldBlockAccessToMember(result->GetArtMethod(), soa.Self())) {
+ return nullptr;
+ }
return soa.AddLocalReference<jobject>(result);
}
-static ALWAYS_INLINE inline bool MethodMatchesConstructor(ArtMethod* m, bool public_only)
+static ALWAYS_INLINE inline bool MethodMatchesConstructor(
+ ArtMethod* m, bool public_only, bool enforce_hidden_api)
REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(m != nullptr);
- return (!public_only || m->IsPublic()) && !m->IsStatic() && m->IsConstructor();
+ return m->IsConstructor() &&
+ !m->IsStatic() &&
+ IsDiscoverable(public_only, enforce_hidden_api, m->GetAccessFlags());
}
static jobjectArray Class_getDeclaredConstructorsInternal(
JNIEnv* env, jobject javaThis, jboolean publicOnly) {
ScopedFastNativeObjectAccess soa(env);
StackHandleScope<2> hs(soa.Self());
+ bool public_only = (publicOnly != JNI_FALSE);
+ bool enforce_hidden_api = ShouldEnforceHiddenApi(soa.Self());
Handle<mirror::Class> h_klass = hs.NewHandle(DecodeClass(soa, javaThis));
size_t constructor_count = 0;
// Two pass approach for speed.
for (auto& m : h_klass->GetDirectMethods(kRuntimePointerSize)) {
- constructor_count += MethodMatchesConstructor(&m, publicOnly != JNI_FALSE) ? 1u : 0u;
+ constructor_count += MethodMatchesConstructor(&m, public_only, enforce_hidden_api) ? 1u : 0u;
}
auto h_constructors = hs.NewHandle(mirror::ObjectArray<mirror::Constructor>::Alloc(
soa.Self(), mirror::Constructor::ArrayClass(), constructor_count));
@@ -426,7 +508,7 @@ static jobjectArray Class_getDeclaredConstructorsInternal(
}
constructor_count = 0;
for (auto& m : h_klass->GetDirectMethods(kRuntimePointerSize)) {
- if (MethodMatchesConstructor(&m, publicOnly != JNI_FALSE)) {
+ if (MethodMatchesConstructor(&m, public_only, enforce_hidden_api)) {
DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize);
DCHECK(!Runtime::Current()->IsActiveTransaction());
auto* constructor = mirror::Constructor::CreateFromArtMethod<kRuntimePointerSize, false>(
@@ -452,6 +534,9 @@ static jobject Class_getDeclaredMethodInternal(JNIEnv* env, jobject javaThis,
DecodeClass(soa, javaThis),
soa.Decode<mirror::String>(name),
soa.Decode<mirror::ObjectArray<mirror::Class>>(args));
+ if (result == nullptr || ShouldBlockAccessToMember(result->GetArtMethod(), soa.Self())) {
+ return nullptr;
+ }
return soa.AddLocalReference<jobject>(result);
}
@@ -459,13 +544,17 @@ static jobjectArray Class_getDeclaredMethodsUnchecked(JNIEnv* env, jobject javaT
jboolean publicOnly) {
ScopedFastNativeObjectAccess soa(env);
StackHandleScope<2> hs(soa.Self());
+
+ bool enforce_hidden_api = ShouldEnforceHiddenApi(soa.Self());
+ bool public_only = (publicOnly != JNI_FALSE);
+
Handle<mirror::Class> klass = hs.NewHandle(DecodeClass(soa, javaThis));
size_t num_methods = 0;
- for (auto& m : klass->GetDeclaredMethods(kRuntimePointerSize)) {
- auto modifiers = m.GetAccessFlags();
+ for (ArtMethod& m : klass->GetDeclaredMethods(kRuntimePointerSize)) {
+ uint32_t modifiers = m.GetAccessFlags();
// Add non-constructor declared methods.
- if ((publicOnly == JNI_FALSE || (modifiers & kAccPublic) != 0) &&
- (modifiers & kAccConstructor) == 0) {
+ if ((modifiers & kAccConstructor) == 0 &&
+ IsDiscoverable(public_only, enforce_hidden_api, modifiers)) {
++num_methods;
}
}
@@ -476,10 +565,10 @@ static jobjectArray Class_getDeclaredMethodsUnchecked(JNIEnv* env, jobject javaT
return nullptr;
}
num_methods = 0;
- for (auto& m : klass->GetDeclaredMethods(kRuntimePointerSize)) {
- auto modifiers = m.GetAccessFlags();
- if ((publicOnly == JNI_FALSE || (modifiers & kAccPublic) != 0) &&
- (modifiers & kAccConstructor) == 0) {
+ for (ArtMethod& m : klass->GetDeclaredMethods(kRuntimePointerSize)) {
+ uint32_t modifiers = m.GetAccessFlags();
+ if ((modifiers & kAccConstructor) == 0 &&
+ IsDiscoverable(public_only, enforce_hidden_api, modifiers)) {
DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize);
DCHECK(!Runtime::Current()->IsActiveTransaction());
auto* method =
@@ -693,11 +782,11 @@ static jobject Class_newInstance(JNIEnv* env, jobject javaThis) {
return nullptr;
}
}
- auto* constructor = klass->GetDeclaredConstructor(
+ ArtMethod* constructor = klass->GetDeclaredConstructor(
soa.Self(),
ScopedNullHandle<mirror::ObjectArray<mirror::Class>>(),
kRuntimePointerSize);
- if (UNLIKELY(constructor == nullptr)) {
+ if (UNLIKELY(constructor == nullptr) || ShouldBlockAccessToMember(constructor, soa.Self())) {
soa.Self()->ThrowNewExceptionF("Ljava/lang/InstantiationException;",
"%s has no zero argument constructor",
klass->PrettyClass().c_str());
@@ -742,6 +831,7 @@ static jobject Class_newInstance(JNIEnv* env, jobject javaThis) {
return nullptr;
}
}
+ hiddenapi::MaybeWarnAboutMemberAccess(constructor, soa.Self(), /* num_frames */ 1);
// Invoke the constructor.
JValue result;
uint32_t args[1] = { static_cast<uint32_t>(reinterpret_cast<uintptr_t>(receiver.Get())) };
diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc
index f990c0421d..db7f4bb18c 100644
--- a/runtime/native/java_lang_reflect_Field.cc
+++ b/runtime/native/java_lang_reflect_Field.cc
@@ -25,6 +25,7 @@
#include "common_throws.h"
#include "dex/dex_file-inl.h"
#include "dex/dex_file_annotations.h"
+#include "hidden_api.h"
#include "jni_internal.h"
#include "mirror/class-inl.h"
#include "mirror/field-inl.h"
@@ -161,6 +162,9 @@ static jobject Field_get(JNIEnv* env, jobject javaField, jobject javaObj) {
DCHECK(soa.Self()->IsExceptionPending());
return nullptr;
}
+
+ hiddenapi::MaybeWarnAboutMemberAccess(f->GetArtField(), soa.Self(), /* num_frames */ 1);
+
// We now don't expect suspension unless an exception is thrown.
// Get the field's value, boxing if necessary.
Primitive::Type field_type = f->GetTypeAsPrimitiveType();
@@ -183,13 +187,14 @@ ALWAYS_INLINE inline static JValue GetPrimitiveField(JNIEnv* env,
DCHECK(soa.Self()->IsExceptionPending());
return JValue();
}
-
// If field is not set to be accessible, verify it can be accessed by the caller.
if (!f->IsAccessible() && !VerifyFieldAccess<false>(soa.Self(), f, o)) {
DCHECK(soa.Self()->IsExceptionPending());
return JValue();
}
+ hiddenapi::MaybeWarnAboutMemberAccess(f->GetArtField(), soa.Self(), /* num_frames */ 1);
+
// We now don't expect suspension unless an exception is thrown.
// Read the value.
Primitive::Type field_type = f->GetTypeAsPrimitiveType();
@@ -351,11 +356,15 @@ static void Field_set(JNIEnv* env, jobject javaField, jobject javaObj, jobject j
DCHECK(soa.Self()->IsExceptionPending());
return;
}
+
// If field is not set to be accessible, verify it can be accessed by the caller.
if (!f->IsAccessible() && !VerifyFieldAccess<true>(soa.Self(), f, o)) {
DCHECK(soa.Self()->IsExceptionPending());
return;
}
+
+ hiddenapi::MaybeWarnAboutMemberAccess(f->GetArtField(), soa.Self(), /* num_frames */ 1);
+
SetFieldValue(o, f, field_prim_type, true, unboxed_value);
}
@@ -391,6 +400,8 @@ static void SetPrimitiveField(JNIEnv* env,
return;
}
+ hiddenapi::MaybeWarnAboutMemberAccess(f->GetArtField(), soa.Self(), /* num_frames */ 1);
+
// Write the value.
SetFieldValue(o, f, field_type, false, wide_value);
}
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 73ca19a363..15a5954396 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -72,9 +72,12 @@ std::ostream& operator << (std::ostream& stream, const OatFileAssistant::OatStat
OatFileAssistant::OatFileAssistant(const char* dex_location,
const InstructionSet isa,
- bool load_executable)
+ bool load_executable,
+ bool only_load_system_executable)
: OatFileAssistant(dex_location,
- isa, load_executable,
+ isa,
+ load_executable,
+ only_load_system_executable,
-1 /* vdex_fd */,
-1 /* oat_fd */,
-1 /* zip_fd */) {}
@@ -83,11 +86,13 @@ OatFileAssistant::OatFileAssistant(const char* dex_location,
OatFileAssistant::OatFileAssistant(const char* dex_location,
const InstructionSet isa,
bool load_executable,
+ bool only_load_system_executable,
int vdex_fd,
int oat_fd,
int zip_fd)
: isa_(isa),
load_executable_(load_executable),
+ only_load_system_executable_(only_load_system_executable),
odex_(this, /*is_oat_location*/ false),
oat_(this, /*is_oat_location*/ true),
zip_fd_(zip_fd) {
@@ -1122,6 +1127,10 @@ const OatFile* OatFileAssistant::OatFileInfo::GetFile() {
if (!load_attempted_) {
load_attempted_ = true;
if (filename_provided_) {
+ bool executable = oat_file_assistant_->load_executable_;
+ if (executable && oat_file_assistant_->only_load_system_executable_) {
+ executable = LocationIsOnSystem(filename_.c_str());
+ }
std::string error_msg;
if (use_fd_) {
if (oat_fd_ >= 0 && vdex_fd_ >= 0) {
@@ -1130,7 +1139,7 @@ const OatFile* OatFileAssistant::OatFileInfo::GetFile() {
filename_.c_str(),
nullptr,
nullptr,
- oat_file_assistant_->load_executable_,
+ executable,
false /* low_4gb */,
oat_file_assistant_->dex_location_.c_str(),
&error_msg));
@@ -1140,7 +1149,7 @@ const OatFile* OatFileAssistant::OatFileInfo::GetFile() {
filename_.c_str(),
nullptr,
nullptr,
- oat_file_assistant_->load_executable_,
+ executable,
false /* low_4gb */,
oat_file_assistant_->dex_location_.c_str(),
&error_msg));
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index 6c01c1e880..a6140304c2 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -119,9 +119,13 @@ class OatFileAssistant {
//
// load_executable should be true if the caller intends to try and load
// executable code for this dex location.
+ //
+ // only_load_system_executable should be true if the caller intends to have
+ // only oat files from /system loaded executable.
OatFileAssistant(const char* dex_location,
const InstructionSet isa,
- bool load_executable);
+ bool load_executable,
+ bool only_load_system_executable = false);
// Similar to this(const char*, const InstructionSet, bool), however, if a valid zip_fd is
// provided, vdex, oat, and zip files will be read from vdex_fd, oat_fd and zip_fd respectively.
@@ -129,6 +133,7 @@ class OatFileAssistant {
OatFileAssistant(const char* dex_location,
const InstructionSet isa,
bool load_executable,
+ bool only_load_system_executable,
int vdex_fd,
int oat_fd,
int zip_fd);
@@ -487,6 +492,9 @@ class OatFileAssistant {
// Whether we will attempt to load oat files executable.
bool load_executable_ = false;
+ // Whether only oat files on /system are loaded executable.
+ const bool only_load_system_executable_ = false;
+
// Cached value of the required dex checksums.
// This should be accessed only by the GetRequiredDexChecksums() method.
std::vector<uint32_t> cached_required_dex_checksums_;
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index a98da0f029..50f5e7a0d5 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -246,6 +246,7 @@ TEST_F(OatFileAssistantTest, GetDexOptNeededWithFd) {
OatFileAssistant oat_file_assistant(dex_location.c_str(),
kRuntimeISA,
false,
+ false,
vdex_fd.get(),
odex_fd.get(),
zip_fd.get());
@@ -285,6 +286,7 @@ TEST_F(OatFileAssistantTest, GetDexOptNeededWithInvalidOdexFd) {
OatFileAssistant oat_file_assistant(dex_location.c_str(),
kRuntimeISA,
false,
+ false,
vdex_fd.get(),
-1 /* oat_fd */,
zip_fd.get());
@@ -319,6 +321,7 @@ TEST_F(OatFileAssistantTest, GetDexOptNeededWithInvalidVdexFd) {
OatFileAssistant oat_file_assistant(dex_location.c_str(),
kRuntimeISA,
false,
+ false,
-1 /* vdex_fd */,
odex_fd.get(),
zip_fd.get());
@@ -342,6 +345,7 @@ TEST_F(OatFileAssistantTest, GetDexOptNeededWithInvalidOdexVdexFd) {
OatFileAssistant oat_file_assistant(dex_location.c_str(),
kRuntimeISA,
false,
+ false,
-1 /* vdex_fd */,
-1 /* oat_fd */,
zip_fd);
@@ -1439,6 +1443,60 @@ TEST_F(OatFileAssistantTest, GetDexOptNeededWithUpToDateContextRelative) {
default_filter, false, false, relative_context.get()));
}
+TEST_F(OatFileAssistantTest, SystemOdex) {
+ std::string dex_location = GetScratchDir() + "/OatUpToDate.jar";
+ std::string odex_location = GetScratchDir() + "/OatUpToDate.odex";
+ std::string system_location = GetAndroidRoot() + "/OatUpToDate.jar";
+
+ std::string error_msg;
+
+ Copy(GetDexSrc1(), dex_location);
+ EXPECT_FALSE(LocationIsOnSystem(dex_location.c_str()));
+
+ {
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ kRuntimeISA,
+ true,
+ false);
+ int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg);
+ EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg;
+ EXPECT_TRUE(oat_file_assistant.GetBestOatFile()->IsExecutable());
+ }
+
+ {
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ kRuntimeISA,
+ true,
+ true);
+ int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg);
+ EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg;
+ EXPECT_FALSE(oat_file_assistant.GetBestOatFile()->IsExecutable());
+ }
+
+ Copy(GetDexSrc1(), system_location);
+ EXPECT_TRUE(LocationIsOnSystem(system_location.c_str()));
+
+ {
+ OatFileAssistant oat_file_assistant(system_location.c_str(),
+ kRuntimeISA,
+ true,
+ false);
+ int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg);
+ EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg;
+ EXPECT_TRUE(oat_file_assistant.GetBestOatFile()->IsExecutable());
+ }
+
+ {
+ OatFileAssistant oat_file_assistant(system_location.c_str(),
+ kRuntimeISA,
+ true,
+ true);
+ int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg);
+ EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg;
+ EXPECT_TRUE(oat_file_assistant.GetBestOatFile()->IsExecutable());
+ }
+}
+
// TODO: More Tests:
// * Test class linker falls back to unquickened dex for DexNoOat
// * Test class linker falls back to unquickened dex for MultiDexNoOat
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index 9503360167..e4194442d3 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -56,15 +56,11 @@ using android::base::StringPrintf;
// If true, we attempt to load the application image if it exists.
static constexpr bool kEnableAppImage = true;
-static bool OatFileIsOnSystem(const std::unique_ptr<const OatFile>& oat_file) {
- UniqueCPtr<const char[]> path(realpath(oat_file->GetLocation().c_str(), nullptr));
- return path != nullptr && android::base::StartsWith(oat_file->GetLocation(),
- GetAndroidRoot().c_str());
-}
-
const OatFile* OatFileManager::RegisterOatFile(std::unique_ptr<const OatFile> oat_file) {
WriterMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_);
- CHECK(!only_use_system_oat_files_ || OatFileIsOnSystem(oat_file))
+ CHECK(!only_use_system_oat_files_ ||
+ LocationIsOnSystem(oat_file->GetLocation().c_str()) ||
+ !oat_file->IsExecutable())
<< "Registering a non /system oat file: " << oat_file->GetLocation();
DCHECK(oat_file != nullptr);
if (kIsDebugBuild) {
@@ -424,7 +420,8 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat(
OatFileAssistant oat_file_assistant(dex_location,
kRuntimeISA,
- !runtime->IsAotCompiler());
+ !runtime->IsAotCompiler(),
+ only_use_system_oat_files_);
// Lock the target oat location to avoid races generating and loading the
// oat file.
@@ -437,8 +434,7 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat(
const OatFile* source_oat_file = nullptr;
- // No point in trying to make up-to-date if we can only use system oat files.
- if (!only_use_system_oat_files_ && !oat_file_assistant.IsUpToDate()) {
+ if (!oat_file_assistant.IsUpToDate()) {
// Update the oat file on disk if we can, based on the --compiler-filter
// option derived from the current runtime options.
// This may fail, but that's okay. Best effort is all that matters here.
@@ -474,9 +470,7 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat(
// Get the oat file on disk.
std::unique_ptr<const OatFile> oat_file(oat_file_assistant.GetBestOatFile().release());
- if (oat_file != nullptr && only_use_system_oat_files_ && !OatFileIsOnSystem(oat_file)) {
- // If the oat file is not on /system, don't use it.
- } else if ((class_loader != nullptr || dex_elements != nullptr) && oat_file != nullptr) {
+ if ((class_loader != nullptr || dex_elements != nullptr) && oat_file != nullptr) {
// Prevent oat files from being loaded if no class_loader or dex_elements are provided.
// This can happen when the deprecated DexFile.<init>(String) is called directly, and it
// could load oat files without checking the classpath, which would be incorrect.
diff --git a/runtime/oat_file_manager.h b/runtime/oat_file_manager.h
index dd6b7ba2ff..038474e31f 100644
--- a/runtime/oat_file_manager.h
+++ b/runtime/oat_file_manager.h
@@ -127,6 +127,9 @@ class OatFileManager {
std::set<std::unique_ptr<const OatFile>> oat_files_ GUARDED_BY(Locks::oat_file_manager_lock_);
bool have_non_pic_oat_file_;
+
+ // Only use the compiled code in an OAT file when the file is on /system. If the OAT file
+ // is not on /system, don't load it "executable".
bool only_use_system_oat_files_;
DISALLOW_COPY_AND_ASSIGN(OatFileManager);
diff --git a/runtime/reflection.cc b/runtime/reflection.cc
index 635a03afe0..6ffafe02f1 100644
--- a/runtime/reflection.cc
+++ b/runtime/reflection.cc
@@ -465,6 +465,9 @@ JValue InvokeWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa, jobject o
}
ArtMethod* method = jni::DecodeArtMethod(mid);
+
+ hiddenapi::MaybeWarnAboutMemberAccess(method, soa.Self(), /* num_frames */ 1);
+
bool is_string_init = method->GetDeclaringClass()->IsStringClass() && method->IsConstructor();
if (is_string_init) {
// Replace calls to String.<init> with equivalent StringFactory call.
@@ -496,6 +499,9 @@ JValue InvokeWithJValues(const ScopedObjectAccessAlreadyRunnable& soa, jobject o
}
ArtMethod* method = jni::DecodeArtMethod(mid);
+
+ hiddenapi::MaybeWarnAboutMemberAccess(method, soa.Self(), /* num_frames */ 1);
+
bool is_string_init = method->GetDeclaringClass()->IsStringClass() && method->IsConstructor();
if (is_string_init) {
// Replace calls to String.<init> with equivalent StringFactory call.
@@ -528,6 +534,9 @@ JValue InvokeVirtualOrInterfaceWithJValues(const ScopedObjectAccessAlreadyRunnab
ObjPtr<mirror::Object> receiver = soa.Decode<mirror::Object>(obj);
ArtMethod* method = FindVirtualMethod(receiver, jni::DecodeArtMethod(mid));
+
+ hiddenapi::MaybeWarnAboutMemberAccess(method, soa.Self(), /* num_frames */ 1);
+
bool is_string_init = method->GetDeclaringClass()->IsStringClass() && method->IsConstructor();
if (is_string_init) {
// Replace calls to String.<init> with equivalent StringFactory call.
@@ -560,6 +569,9 @@ JValue InvokeVirtualOrInterfaceWithVarArgs(const ScopedObjectAccessAlreadyRunnab
ObjPtr<mirror::Object> receiver = soa.Decode<mirror::Object>(obj);
ArtMethod* method = FindVirtualMethod(receiver, jni::DecodeArtMethod(mid));
+
+ hiddenapi::MaybeWarnAboutMemberAccess(method, soa.Self(), /* num_frames */ 1);
+
bool is_string_init = method->GetDeclaringClass()->IsStringClass() && method->IsConstructor();
if (is_string_init) {
// Replace calls to String.<init> with equivalent StringFactory call.
@@ -604,6 +616,8 @@ jobject InvokeMethod(const ScopedObjectAccessAlreadyRunnable& soa, jobject javaM
}
}
+ hiddenapi::MaybeWarnAboutMemberAccess(m, soa.Self(), num_frames);
+
ObjPtr<mirror::Object> receiver;
if (!m->IsStatic()) {
// Replace calls to String.<init> with equivalent StringFactory call.
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 33bebe0887..6d065d6146 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -266,6 +266,8 @@ Runtime::Runtime()
is_low_memory_mode_(false),
safe_mode_(false),
do_hidden_api_checks_(false),
+ pending_hidden_api_warning_(false),
+ dedupe_hidden_api_warnings_(true),
dump_native_stack_on_sig_quit_(true),
pruned_dalvik_cache_(false),
// Initially assume we perceive jank in case the process state is never updated.
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 022a1be124..184e4e5b91 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -520,14 +520,30 @@ class Runtime {
bool IsVerificationEnabled() const;
bool IsVerificationSoftFail() const;
- void DisableHiddenApiChecks() {
- do_hidden_api_checks_ = false;
+ void SetHiddenApiChecksEnabled(bool value) {
+ do_hidden_api_checks_ = value;
}
bool AreHiddenApiChecksEnabled() const {
return do_hidden_api_checks_;
}
+ void SetPendingHiddenApiWarning(bool value) {
+ pending_hidden_api_warning_ = value;
+ }
+
+ bool HasPendingHiddenApiWarning() const {
+ return pending_hidden_api_warning_;
+ }
+
+ void SetDedupeHiddenApiWarnings(bool value) {
+ dedupe_hidden_api_warnings_ = value;
+ }
+
+ bool ShouldDedupeHiddenApiWarnings() {
+ return dedupe_hidden_api_warnings_;
+ }
+
bool IsDexFileFallbackEnabled() const {
return allow_dex_file_fallback_;
}
@@ -968,6 +984,14 @@ class Runtime {
// Whether access checks on hidden API should be performed.
bool do_hidden_api_checks_;
+ // Whether the application has used an API which is not restricted but we
+ // should issue a warning about it.
+ bool pending_hidden_api_warning_;
+
+ // Do not warn about the same hidden API access violation twice.
+ // This is only used for testing.
+ bool dedupe_hidden_api_warnings_;
+
// Whether threads should dump their native stack on SIGQUIT.
bool dump_native_stack_on_sig_quit_;
diff --git a/runtime/runtime_intrinsics.cc b/runtime/runtime_intrinsics.cc
index f710ebeb4c..3295a86e59 100644
--- a/runtime/runtime_intrinsics.cc
+++ b/runtime/runtime_intrinsics.cc
@@ -18,8 +18,8 @@
#include "art_method-inl.h"
#include "class_linker.h"
+#include "dex/invoke_type.h"
#include "intrinsics_enum.h"
-#include "invoke_type.h"
#include "mirror/class.h"
#include "runtime.h"
#include "scoped_thread_state_change-inl.h"
diff --git a/runtime/utf.cc b/runtime/utf.cc
index 93fcb32136..32ae187297 100644
--- a/runtime/utf.cc
+++ b/runtime/utf.cc
@@ -18,8 +18,7 @@
#include <android-base/logging.h>
-#include "mirror/array.h"
-#include "mirror/object-inl.h"
+#include "base/casts.h"
#include "utf-inl.h"
namespace art {
diff --git a/runtime/utils.cc b/runtime/utils.cc
index 79ddcb9bff..b2ec669f32 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -30,7 +30,6 @@
#include "android-base/stringprintf.h"
#include "android-base/strings.h"
-#include "dex/dex_file-inl.h"
#include "os.h"
#include "utf-inl.h"
@@ -126,41 +125,6 @@ std::string PrettyDescriptor(const char* descriptor) {
return result;
}
-std::string PrettyJavaAccessFlags(uint32_t access_flags) {
- std::string result;
- if ((access_flags & kAccPublic) != 0) {
- result += "public ";
- }
- if ((access_flags & kAccProtected) != 0) {
- result += "protected ";
- }
- if ((access_flags & kAccPrivate) != 0) {
- result += "private ";
- }
- if ((access_flags & kAccFinal) != 0) {
- result += "final ";
- }
- if ((access_flags & kAccStatic) != 0) {
- result += "static ";
- }
- if ((access_flags & kAccAbstract) != 0) {
- result += "abstract ";
- }
- if ((access_flags & kAccInterface) != 0) {
- result += "interface ";
- }
- if ((access_flags & kAccTransient) != 0) {
- result += "transient ";
- }
- if ((access_flags & kAccVolatile) != 0) {
- result += "volatile ";
- }
- if ((access_flags & kAccSynchronized) != 0) {
- result += "synchronized ";
- }
- return result;
-}
-
std::string PrettySize(int64_t byte_count) {
// The byte thresholds at which we display amounts. A byte count is displayed
// in unit U when kUnitThresholds[U] <= bytes < kUnitThresholds[U+1].
diff --git a/runtime/utils.h b/runtime/utils.h
index 7402c12280..abdafcc9f7 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -82,10 +82,6 @@ void AppendPrettyDescriptor(const char* descriptor, std::string* result);
std::string PrettyDescriptor(const char* descriptor);
std::string PrettyDescriptor(Primitive::Type type);
-// Returns a human-readable version of the Java part of the access flags, e.g., "private static "
-// (note the trailing whitespace).
-std::string PrettyJavaAccessFlags(uint32_t access_flags);
-
// Returns a human-readable size string such as "1MB".
std::string PrettySize(int64_t size_in_bytes);
diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h
index 4e45128420..202380de8f 100644
--- a/runtime/vdex_file.h
+++ b/runtime/vdex_file.h
@@ -218,6 +218,10 @@ class VdexFile {
ArrayRef<const uint8_t> GetQuickenedInfoOf(const DexFile& dex_file,
uint32_t dex_method_idx) const;
+ bool HasDexSection() const {
+ return GetHeader().GetDexSize() != 0;
+ }
+
private:
uint32_t GetQuickeningInfoTableOffset(const uint8_t* source_dex_begin) const;
@@ -235,10 +239,6 @@ class VdexFile {
uint32_t num_method_ids,
const ArrayRef<const uint8_t>& quickening_info) const;
- bool HasDexSection() const {
- return GetHeader().GetDexSize() != 0;
- }
-
bool ContainsDexFile(const DexFile& dex_file) const;
const uint8_t* DexBegin() const {
diff --git a/runtime/verifier/reg_type-inl.h b/runtime/verifier/reg_type-inl.h
index f719782727..9e12d636d4 100644
--- a/runtime/verifier/reg_type-inl.h
+++ b/runtime/verifier/reg_type-inl.h
@@ -48,9 +48,6 @@ inline bool RegType::CanAccess(const RegType& other) const {
inline bool RegType::CanAccessMember(ObjPtr<mirror::Class> klass, uint32_t access_flags) const {
DCHECK(IsReferenceTypes());
- if ((access_flags & kAccPublic) != 0) {
- return true;
- }
if (IsNull()) {
return true;
}
diff --git a/test/530-checker-lse/src/Main.java b/test/530-checker-lse/src/Main.java
index 98838c5089..f6332b5503 100644
--- a/test/530-checker-lse/src/Main.java
+++ b/test/530-checker-lse/src/Main.java
@@ -398,6 +398,7 @@ public class Main {
/// CHECK-START: int Main.test15() load_store_elimination (after)
/// CHECK: <<Const2:i\d+>> IntConstant 2
/// CHECK: StaticFieldSet
+ /// CHECK: StaticFieldSet
/// CHECK-NOT: StaticFieldGet
/// CHECK: Return [<<Const2>>]
@@ -772,127 +773,6 @@ public class Main {
return obj;
}
- /// CHECK-START: void Main.testStoreStore2(TestClass2) load_store_elimination (before)
- /// CHECK: InstanceFieldSet
- /// CHECK: InstanceFieldSet
- /// CHECK: InstanceFieldSet
- /// CHECK: InstanceFieldSet
-
- /// CHECK-START: void Main.testStoreStore2(TestClass2) load_store_elimination (after)
- /// CHECK: InstanceFieldSet
- /// CHECK: InstanceFieldSet
- /// CHECK-NOT: InstanceFieldSet
-
- private static void testStoreStore2(TestClass2 obj) {
- obj.i = 41;
- obj.j = 42;
- obj.i = 43;
- obj.j = 44;
- }
-
- /// CHECK-START: void Main.testStoreStore3(TestClass2, boolean) load_store_elimination (before)
- /// CHECK: InstanceFieldSet
- /// CHECK: InstanceFieldSet
- /// CHECK: InstanceFieldSet
- /// CHECK: InstanceFieldSet
-
- /// CHECK-START: void Main.testStoreStore3(TestClass2, boolean) load_store_elimination (after)
- /// CHECK: InstanceFieldSet
- /// CHECK: InstanceFieldSet
- /// CHECK: InstanceFieldSet
- /// CHECK-NOT: InstanceFieldSet
-
- private static void testStoreStore3(TestClass2 obj, boolean flag) {
- obj.i = 41;
- obj.j = 42; // redundant since it's overwritten in both branches below.
- if (flag) {
- obj.j = 43;
- } else {
- obj.j = 44;
- }
- }
-
- /// CHECK-START: void Main.testStoreStore4() load_store_elimination (before)
- /// CHECK: StaticFieldSet
- /// CHECK: StaticFieldSet
-
- /// CHECK-START: void Main.testStoreStore4() load_store_elimination (after)
- /// CHECK: StaticFieldSet
- /// CHECK-NOT: StaticFieldSet
-
- private static void testStoreStore4() {
- TestClass.si = 61;
- TestClass.si = 62;
- }
-
- /// CHECK-START: int Main.testStoreStore5(TestClass2, TestClass2) load_store_elimination (before)
- /// CHECK: InstanceFieldSet
- /// CHECK: InstanceFieldGet
- /// CHECK: InstanceFieldSet
-
- /// CHECK-START: int Main.testStoreStore5(TestClass2, TestClass2) load_store_elimination (after)
- /// CHECK: InstanceFieldSet
- /// CHECK: InstanceFieldGet
- /// CHECK: InstanceFieldSet
-
- private static int testStoreStore5(TestClass2 obj1, TestClass2 obj2) {
- obj1.i = 71; // This store is needed since obj2.i may load from it.
- int i = obj2.i;
- obj1.i = 72;
- return i;
- }
-
- /// CHECK-START: int Main.testStoreStore6(TestClass2, TestClass2) load_store_elimination (before)
- /// CHECK: InstanceFieldSet
- /// CHECK: InstanceFieldGet
- /// CHECK: InstanceFieldSet
-
- /// CHECK-START: int Main.testStoreStore6(TestClass2, TestClass2) load_store_elimination (after)
- /// CHECK-NOT: InstanceFieldSet
- /// CHECK: InstanceFieldGet
- /// CHECK: InstanceFieldSet
-
- private static int testStoreStore6(TestClass2 obj1, TestClass2 obj2) {
- obj1.i = 81; // This store is not needed since obj2.j cannot load from it.
- int j = obj2.j;
- obj1.i = 82;
- return j;
- }
-
- /// CHECK-START: int Main.testNoSideEffects(int[]) load_store_elimination (before)
- /// CHECK: ArraySet
- /// CHECK: ArraySet
- /// CHECK: ArraySet
- /// CHECK: ArrayGet
-
- /// CHECK-START: int Main.testNoSideEffects(int[]) load_store_elimination (after)
- /// CHECK: ArraySet
- /// CHECK: ArraySet
- /// CHECK-NOT: ArraySet
- /// CHECK-NOT: ArrayGet
-
- private static int testNoSideEffects(int[] array) {
- array[0] = 101;
- array[1] = 102;
- int bitCount = Integer.bitCount(0x3456);
- array[1] = 103;
- return array[0] + bitCount;
- }
-
- /// CHECK-START: void Main.testThrow(TestClass2, java.lang.Exception) load_store_elimination (before)
- /// CHECK: InstanceFieldSet
- /// CHECK: Throw
-
- /// CHECK-START: void Main.testThrow(TestClass2, java.lang.Exception) load_store_elimination (after)
- /// CHECK: InstanceFieldSet
- /// CHECK: Throw
-
- // Make sure throw keeps the store.
- private static void testThrow(TestClass2 obj, Exception e) throws Exception {
- obj.i = 55;
- throw e;
- }
-
/// CHECK-START: int Main.testStoreStoreWithDeoptimize(int[]) load_store_elimination (before)
/// CHECK: NewInstance
/// CHECK: InstanceFieldSet
@@ -934,6 +814,23 @@ public class Main {
return arr[0] + arr[1] + arr[2] + arr[3];
}
+ /// CHECK-START: int Main.testNoSideEffects(int[]) load_store_elimination (before)
+ /// CHECK: ArraySet
+ /// CHECK: ArraySet
+ /// CHECK: ArrayGet
+
+ /// CHECK-START: int Main.testNoSideEffects(int[]) load_store_elimination (after)
+ /// CHECK: ArraySet
+ /// CHECK: ArraySet
+ /// CHECK-NOT: ArrayGet
+
+ private static int testNoSideEffects(int[] array) {
+ array[0] = 101;
+ int bitCount = Integer.bitCount(0x3456);
+ array[1] = array[0] + 1;
+ return array[0] + bitCount;
+ }
+
/// CHECK-START: double Main.getCircleArea(double, boolean) load_store_elimination (before)
/// CHECK: NewInstance
@@ -1208,46 +1105,16 @@ public class Main {
assertIntEquals(testStoreStore().i, 41);
assertIntEquals(testStoreStore().j, 43);
+ assertIntEquals(testStoreStoreWithDeoptimize(new int[4]), 4);
assertIntEquals(testExitMerge(true), 2);
assertIntEquals(testExitMerge2(true), 2);
assertIntEquals(testExitMerge2(false), 2);
- TestClass2 testclass2 = new TestClass2();
- testStoreStore2(testclass2);
- assertIntEquals(testclass2.i, 43);
- assertIntEquals(testclass2.j, 44);
-
- testStoreStore3(testclass2, true);
- assertIntEquals(testclass2.i, 41);
- assertIntEquals(testclass2.j, 43);
- testStoreStore3(testclass2, false);
- assertIntEquals(testclass2.i, 41);
- assertIntEquals(testclass2.j, 44);
-
- testStoreStore4();
- assertIntEquals(TestClass.si, 62);
-
- int ret = testStoreStore5(testclass2, testclass2);
- assertIntEquals(testclass2.i, 72);
- assertIntEquals(ret, 71);
-
- testclass2.j = 88;
- ret = testStoreStore6(testclass2, testclass2);
- assertIntEquals(testclass2.i, 82);
- assertIntEquals(ret, 88);
-
- ret = testNoSideEffects(iarray);
+ int ret = testNoSideEffects(iarray);
assertIntEquals(iarray[0], 101);
- assertIntEquals(iarray[1], 103);
+ assertIntEquals(iarray[1], 102);
assertIntEquals(ret, 108);
-
- try {
- testThrow(testclass2, new Exception());
- } catch (Exception e) {}
- assertIntEquals(testclass2.i, 55);
-
- assertIntEquals(testStoreStoreWithDeoptimize(new int[4]), 4);
}
static boolean sFlag;
diff --git a/test/608-checker-unresolved-lse/src/Main.java b/test/608-checker-unresolved-lse/src/Main.java
index a39dd51bdf..c6f8854b49 100644
--- a/test/608-checker-unresolved-lse/src/Main.java
+++ b/test/608-checker-unresolved-lse/src/Main.java
@@ -88,6 +88,7 @@ public class Main extends MissingSuperClass {
/// CHECK-START: void Main.staticFieldTest() load_store_elimination (after)
/// CHECK: StaticFieldSet
+ /// CHECK: StaticFieldSet
/// CHECK: UnresolvedStaticFieldGet
public static void staticFieldTest() {
// Ensure Foo is initialized.
diff --git a/test/639-checker-code-sinking/expected.txt b/test/639-checker-code-sinking/expected.txt
index 5d4833aca8..52e756c231 100644
--- a/test/639-checker-code-sinking/expected.txt
+++ b/test/639-checker-code-sinking/expected.txt
@@ -1,3 +1,3 @@
0
class java.lang.Object
-42
+43
diff --git a/test/639-checker-code-sinking/src/Main.java b/test/639-checker-code-sinking/src/Main.java
index a1c30f7b4e..7496925adc 100644
--- a/test/639-checker-code-sinking/src/Main.java
+++ b/test/639-checker-code-sinking/src/Main.java
@@ -337,7 +337,7 @@ public class Main {
public static void testStoreStore(boolean doThrow) {
Main m = new Main();
m.intField = 42;
- m.intField2 = 43;
+ m.intField = 43;
if (doThrow) {
throw new Error(m.$opt$noinline$toString());
}
@@ -349,7 +349,6 @@ public class Main {
volatile int volatileField;
int intField;
- int intField2;
Object objectField;
static boolean doThrow;
static boolean doLoop;
diff --git a/test/674-hiddenapi/api-blacklist.txt b/test/674-hiddenapi/api-blacklist.txt
new file mode 100644
index 0000000000..d43360c62f
--- /dev/null
+++ b/test/674-hiddenapi/api-blacklist.txt
@@ -0,0 +1,25 @@
+LNullaryConstructorBlacklist;-><init>()V
+LParentClass;->fieldPublicBlacklist:I
+LParentClass;->fieldPackageBlacklist:I
+LParentClass;->fieldProtectedBlacklist:I
+LParentClass;->fieldPrivateBlacklist:I
+LParentClass;->fieldPublicStaticBlacklist:I
+LParentClass;->fieldPackageStaticBlacklist:I
+LParentClass;->fieldProtectedStaticBlacklist:I
+LParentClass;->fieldPrivateStaticBlacklist:I
+LParentClass;->methodPublicBlacklist()I
+LParentClass;->methodPackageBlacklist()I
+LParentClass;->methodProtectedBlacklist()I
+LParentClass;->methodPrivateBlacklist()I
+LParentClass;->methodPublicStaticBlacklist()I
+LParentClass;->methodPackageStaticBlacklist()I
+LParentClass;->methodProtectedStaticBlacklist()I
+LParentClass;->methodPrivateStaticBlacklist()I
+LParentClass;-><init>(IC)V
+LParentClass;-><init>(FC)V
+LParentClass;-><init>(JC)V
+LParentClass;-><init>(DC)V
+LParentInterface;->fieldPublicStaticBlacklist:I
+LParentInterface;->methodPublicBlacklist()I
+LParentInterface;->methodPublicStaticBlacklist()I
+LParentInterface;->methodPublicDefaultBlacklist()I \ No newline at end of file
diff --git a/test/674-hiddenapi/api-dark-greylist.txt b/test/674-hiddenapi/api-dark-greylist.txt
new file mode 100644
index 0000000000..d0f35f64bc
--- /dev/null
+++ b/test/674-hiddenapi/api-dark-greylist.txt
@@ -0,0 +1,25 @@
+LNullaryConstructorDarkGreylist;-><init>()V
+LParentClass;->fieldPublicDarkGreylist:I
+LParentClass;->fieldPackageDarkGreylist:I
+LParentClass;->fieldProtectedDarkGreylist:I
+LParentClass;->fieldPrivateDarkGreylist:I
+LParentClass;->fieldPublicStaticDarkGreylist:I
+LParentClass;->fieldPackageStaticDarkGreylist:I
+LParentClass;->fieldProtectedStaticDarkGreylist:I
+LParentClass;->fieldPrivateStaticDarkGreylist:I
+LParentClass;->methodPublicDarkGreylist()I
+LParentClass;->methodPackageDarkGreylist()I
+LParentClass;->methodProtectedDarkGreylist()I
+LParentClass;->methodPrivateDarkGreylist()I
+LParentClass;->methodPublicStaticDarkGreylist()I
+LParentClass;->methodPackageStaticDarkGreylist()I
+LParentClass;->methodProtectedStaticDarkGreylist()I
+LParentClass;->methodPrivateStaticDarkGreylist()I
+LParentClass;-><init>(IB)V
+LParentClass;-><init>(FB)V
+LParentClass;-><init>(JB)V
+LParentClass;-><init>(DB)V
+LParentInterface;->fieldPublicStaticDarkGreylist:I
+LParentInterface;->methodPublicDarkGreylist()I
+LParentInterface;->methodPublicStaticDarkGreylist()I
+LParentInterface;->methodPublicDefaultDarkGreylist()I \ No newline at end of file
diff --git a/test/674-hiddenapi/api-light-greylist.txt b/test/674-hiddenapi/api-light-greylist.txt
new file mode 100644
index 0000000000..2809025cfd
--- /dev/null
+++ b/test/674-hiddenapi/api-light-greylist.txt
@@ -0,0 +1,25 @@
+LNullaryConstructorLightGreylist;-><init>()V
+LParentClass;->fieldPublicLightGreylist:I
+LParentClass;->fieldPackageLightGreylist:I
+LParentClass;->fieldProtectedLightGreylist:I
+LParentClass;->fieldPrivateLightGreylist:I
+LParentClass;->fieldPublicStaticLightGreylist:I
+LParentClass;->fieldPackageStaticLightGreylist:I
+LParentClass;->fieldProtectedStaticLightGreylist:I
+LParentClass;->fieldPrivateStaticLightGreylist:I
+LParentClass;->methodPublicLightGreylist()I
+LParentClass;->methodPackageLightGreylist()I
+LParentClass;->methodProtectedLightGreylist()I
+LParentClass;->methodPrivateLightGreylist()I
+LParentClass;->methodPublicStaticLightGreylist()I
+LParentClass;->methodPackageStaticLightGreylist()I
+LParentClass;->methodProtectedStaticLightGreylist()I
+LParentClass;->methodPrivateStaticLightGreylist()I
+LParentClass;-><init>(IZ)V
+LParentClass;-><init>(FZ)V
+LParentClass;-><init>(JZ)V
+LParentClass;-><init>(DZ)V
+LParentInterface;->fieldPublicStaticLightGreylist:I
+LParentInterface;->methodPublicLightGreylist()I
+LParentInterface;->methodPublicStaticLightGreylist()I
+LParentInterface;->methodPublicDefaultLightGreylist()I \ No newline at end of file
diff --git a/test/674-hiddenapi/build b/test/674-hiddenapi/build
new file mode 100644
index 0000000000..330a6def29
--- /dev/null
+++ b/test/674-hiddenapi/build
@@ -0,0 +1,38 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+set -e
+
+# Build the jars twice. First with applying hiddenapi, creating a boot jar, then
+# a second time without to create a normal jar. We need to do this because we
+# want to load the jar once as an app module and once as a member of the boot
+# class path. The DexFileVerifier would fail on the former as it does not allow
+# hidden API access flags in dex files. DexFileVerifier is not invoked on boot
+# class path dex files, so the boot jar loads fine in the latter case.
+
+export USE_HIDDENAPI=true
+./default-build "$@"
+
+# Move the jar file into the resource folder to be bundled with the test.
+mkdir res
+mv ${TEST_NAME}.jar res/boot.jar
+
+# Clear all intermediate files otherwise default-build would either skip
+# compilation or fail rebuilding.
+rm -rf classes*
+
+export USE_HIDDENAPI=false
+./default-build "$@"
diff --git a/test/674-hiddenapi/check b/test/674-hiddenapi/check
new file mode 100644
index 0000000000..c319a0ae97
--- /dev/null
+++ b/test/674-hiddenapi/check
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# 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.
+
+# Remove pid and date from the log messages.
+grep -vE '^dalvikvm(32|64) E [^]]+]' "$2" \
+ | grep -v JNI_OnLoad \
+ | grep -v JNI_OnUnload \
+ > "$2.tmp"
+
+./default-check "$1" "$2.tmp"
diff --git a/test/674-hiddenapi/expected.txt b/test/674-hiddenapi/expected.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/674-hiddenapi/expected.txt
diff --git a/test/674-hiddenapi/hiddenapi.cc b/test/674-hiddenapi/hiddenapi.cc
new file mode 100644
index 0000000000..baff6f758d
--- /dev/null
+++ b/test/674-hiddenapi/hiddenapi.cc
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include "class_linker.h"
+#include "dex/art_dex_file_loader.h"
+#include "jni.h"
+#include "runtime.h"
+#include "scoped_thread_state_change-inl.h"
+#include "thread.h"
+#include "ti-agent/scoped_utf_chars.h"
+
+namespace art {
+namespace Test674HiddenApi {
+
+extern "C" JNIEXPORT void JNICALL Java_Main_init(JNIEnv*, jclass) {
+ Runtime::Current()->SetHiddenApiChecksEnabled(true);
+ Runtime::Current()->SetDedupeHiddenApiWarnings(false);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_appendToBootClassLoader(
+ JNIEnv* env, jclass, jstring jpath) {
+ ScopedUtfChars utf(env, jpath);
+ const char* path = utf.c_str();
+ if (path == nullptr) {
+ return;
+ }
+
+ ArtDexFileLoader dex_loader;
+ std::string error_msg;
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+ if (!dex_loader.Open(path,
+ path,
+ /* verify */ false,
+ /* verify_checksum */ true,
+ &error_msg,
+ &dex_files)) {
+ LOG(FATAL) << "Could not open " << path << " for boot classpath extension: " << error_msg;
+ UNREACHABLE();
+ }
+
+ ScopedObjectAccess soa(Thread::Current());
+ for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
+ Runtime::Current()->GetClassLinker()->AppendToBootClassPath(
+ Thread::Current(), *dex_file.release());
+ }
+}
+
+static jobject NewInstance(JNIEnv* env, jclass klass) {
+ jmethodID constructor = env->GetMethodID(klass, "<init>", "()V");
+ if (constructor == NULL) {
+ return NULL;
+ }
+ return env->NewObject(klass, constructor);
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_JNI_canDiscoverField(
+ JNIEnv* env, jclass, jclass klass, jstring name, jboolean is_static) {
+ ScopedUtfChars utf_name(env, name);
+ jfieldID field = is_static ? env->GetStaticFieldID(klass, utf_name.c_str(), "I")
+ : env->GetFieldID(klass, utf_name.c_str(), "I");
+ if (field == NULL) {
+ env->ExceptionClear();
+ return JNI_FALSE;
+ }
+
+ return JNI_TRUE;
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_JNI_canGetField(
+ JNIEnv* env, jclass, jclass klass, jstring name, jboolean is_static) {
+ ScopedUtfChars utf_name(env, name);
+ jfieldID field = is_static ? env->GetStaticFieldID(klass, utf_name.c_str(), "I")
+ : env->GetFieldID(klass, utf_name.c_str(), "I");
+ if (field == NULL) {
+ env->ExceptionClear();
+ return JNI_FALSE;
+ }
+ if (is_static) {
+ env->GetStaticIntField(klass, field);
+ } else {
+ jobject obj = NewInstance(env, klass);
+ if (obj == NULL) {
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ return JNI_FALSE;
+ }
+ env->GetIntField(obj, field);
+ }
+
+ if (env->ExceptionOccurred()) {
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ return JNI_FALSE;
+ }
+
+ return JNI_TRUE;
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_JNI_canSetField(
+ JNIEnv* env, jclass, jclass klass, jstring name, jboolean is_static) {
+ ScopedUtfChars utf_name(env, name);
+ jfieldID field = is_static ? env->GetStaticFieldID(klass, utf_name.c_str(), "I")
+ : env->GetFieldID(klass, utf_name.c_str(), "I");
+ if (field == NULL) {
+ env->ExceptionClear();
+ return JNI_FALSE;
+ }
+ if (is_static) {
+ env->SetStaticIntField(klass, field, 42);
+ } else {
+ jobject obj = NewInstance(env, klass);
+ if (obj == NULL) {
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ return JNI_FALSE;
+ }
+ env->SetIntField(obj, field, 42);
+ }
+
+ if (env->ExceptionOccurred()) {
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ return JNI_FALSE;
+ }
+
+ return JNI_TRUE;
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_JNI_canDiscoverMethod(
+ JNIEnv* env, jclass, jclass klass, jstring name, jboolean is_static) {
+ ScopedUtfChars utf_name(env, name);
+ jmethodID method = is_static ? env->GetStaticMethodID(klass, utf_name.c_str(), "()I")
+ : env->GetMethodID(klass, utf_name.c_str(), "()I");
+ if (method == NULL) {
+ env->ExceptionClear();
+ return JNI_FALSE;
+ }
+
+ return JNI_TRUE;
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_JNI_canInvokeMethodA(
+ JNIEnv* env, jclass, jclass klass, jstring name, jboolean is_static) {
+ ScopedUtfChars utf_name(env, name);
+ jmethodID method = is_static ? env->GetStaticMethodID(klass, utf_name.c_str(), "()I")
+ : env->GetMethodID(klass, utf_name.c_str(), "()I");
+ if (method == NULL) {
+ env->ExceptionClear();
+ return JNI_FALSE;
+ }
+
+ if (is_static) {
+ env->CallStaticIntMethodA(klass, method, nullptr);
+ } else {
+ jobject obj = NewInstance(env, klass);
+ if (obj == NULL) {
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ return JNI_FALSE;
+ }
+ env->CallIntMethodA(obj, method, nullptr);
+ }
+
+ if (env->ExceptionOccurred()) {
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ return JNI_FALSE;
+ }
+
+ return JNI_TRUE;
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_JNI_canInvokeMethodV(
+ JNIEnv* env, jclass, jclass klass, jstring name, jboolean is_static) {
+ ScopedUtfChars utf_name(env, name);
+ jmethodID method = is_static ? env->GetStaticMethodID(klass, utf_name.c_str(), "()I")
+ : env->GetMethodID(klass, utf_name.c_str(), "()I");
+ if (method == NULL) {
+ env->ExceptionClear();
+ return JNI_FALSE;
+ }
+
+ if (is_static) {
+ env->CallStaticIntMethod(klass, method);
+ } else {
+ jobject obj = NewInstance(env, klass);
+ if (obj == NULL) {
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ return JNI_FALSE;
+ }
+ env->CallIntMethod(obj, method);
+ }
+
+ if (env->ExceptionOccurred()) {
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ return JNI_FALSE;
+ }
+
+ return JNI_TRUE;
+}
+
+static constexpr size_t kConstructorSignatureLength = 5; // e.g. (IZ)V
+static constexpr size_t kNumConstructorArgs = kConstructorSignatureLength - 3;
+
+extern "C" JNIEXPORT jboolean JNICALL Java_JNI_canDiscoverConstructor(
+ JNIEnv* env, jclass, jclass klass, jstring args) {
+ ScopedUtfChars utf_args(env, args);
+ jmethodID constructor = env->GetMethodID(klass, "<init>", utf_args.c_str());
+ if (constructor == NULL) {
+ env->ExceptionClear();
+ return JNI_FALSE;
+ }
+
+ return JNI_TRUE;
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_JNI_canInvokeConstructorA(
+ JNIEnv* env, jclass, jclass klass, jstring args) {
+ ScopedUtfChars utf_args(env, args);
+ jmethodID constructor = env->GetMethodID(klass, "<init>", utf_args.c_str());
+ if (constructor == NULL) {
+ env->ExceptionClear();
+ return JNI_FALSE;
+ }
+
+ // CheckJNI won't allow out-of-range values, so just zero everything.
+ CHECK_EQ(strlen(utf_args.c_str()), kConstructorSignatureLength);
+ size_t initargs_size = sizeof(jvalue) * kNumConstructorArgs;
+ jvalue *initargs = reinterpret_cast<jvalue*>(alloca(initargs_size));
+ memset(initargs, 0, initargs_size);
+
+ env->NewObjectA(klass, constructor, initargs);
+ if (env->ExceptionOccurred()) {
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ return JNI_FALSE;
+ }
+
+ return JNI_TRUE;
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_JNI_canInvokeConstructorV(
+ JNIEnv* env, jclass, jclass klass, jstring args) {
+ ScopedUtfChars utf_args(env, args);
+ jmethodID constructor = env->GetMethodID(klass, "<init>", utf_args.c_str());
+ if (constructor == NULL) {
+ env->ExceptionClear();
+ return JNI_FALSE;
+ }
+
+ // CheckJNI won't allow out-of-range values, so just zero everything.
+ CHECK_EQ(strlen(utf_args.c_str()), kConstructorSignatureLength);
+ size_t initargs_size = sizeof(jvalue) * kNumConstructorArgs;
+ jvalue *initargs = reinterpret_cast<jvalue*>(alloca(initargs_size));
+ memset(initargs, 0, initargs_size);
+
+ static_assert(kNumConstructorArgs == 2, "Change the varargs below if you change the constant");
+ env->NewObject(klass, constructor, initargs[0], initargs[1]);
+ if (env->ExceptionOccurred()) {
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ return JNI_FALSE;
+ }
+
+ return JNI_TRUE;
+}
+
+extern "C" JNIEXPORT jint JNICALL Java_Reflection_getHiddenApiAccessFlags(JNIEnv*, jclass) {
+ return static_cast<jint>(kAccHiddenApiBits);
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_ChildClass_hasPendingWarning(JNIEnv*, jclass) {
+ return Runtime::Current()->HasPendingHiddenApiWarning();
+}
+
+extern "C" JNIEXPORT void JNICALL Java_ChildClass_clearWarning(JNIEnv*, jclass) {
+ Runtime::Current()->SetPendingHiddenApiWarning(false);
+}
+
+} // namespace Test674HiddenApi
+} // namespace art
diff --git a/test/674-hiddenapi/info.txt b/test/674-hiddenapi/info.txt
new file mode 100644
index 0000000000..25ac6ae78f
--- /dev/null
+++ b/test/674-hiddenapi/info.txt
@@ -0,0 +1,15 @@
+Test whether hidden API access flags are being enforced. The test is composed of
+two JARs. The first (parent) defines methods and fields and the second (child)
+tries to access them with reflection/JNI or link against them. Note that the
+first is compiled twice - once with and once without hidden access flags.
+
+The test then proceeds to exercise the following combinations of class loading:
+(a) Both parent and child dex loaded with PathClassLoader, parent's class loader
+ is the child's class loader's parent. Access flags should not be enforced as
+ the parent does not belong to boot class path.
+(b) Parent is appended to boot class path, child is loaded with PathClassLoader.
+ In this situation child should not be able to access hidden methods/fields
+ of the parent.
+(c) Both parent and child are appended to boot class path. Restrictions should
+ not apply as hidden APIs are accessible within the boundaries of the boot
+ class path.
diff --git a/test/674-hiddenapi/src-art/Main.java b/test/674-hiddenapi/src-art/Main.java
new file mode 100644
index 0000000000..a808e946a9
--- /dev/null
+++ b/test/674-hiddenapi/src-art/Main.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+import dalvik.system.InMemoryDexClassLoader;
+import dalvik.system.PathClassLoader;
+import java.io.File;
+import java.io.InputStream;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+import java.nio.file.Files;
+import java.util.Arrays;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[0]);
+ prepareNativeLibFileName(args[0]);
+
+ // Enable hidden API checks in case they are disabled by default.
+ init();
+
+ // Run test with both parent and child dex files loaded with class loaders.
+ // The expectation is that hidden members in parent should be visible to
+ // the child.
+ doTest(false, false);
+ doUnloading();
+
+ // Now append parent dex file to boot class path and run again. This time
+ // the child dex file should not be able to access private APIs of the parent.
+ appendToBootClassLoader(DEX_PARENT_BOOT);
+ doTest(true, false);
+ doUnloading();
+
+ // And finally append to child to boot class path as well. With both in the
+ // boot class path, access should be granted.
+ appendToBootClassLoader(DEX_CHILD);
+ doTest(true, true);
+ doUnloading();
+ }
+
+ private static void doTest(boolean parentInBoot, boolean childInBoot) throws Exception {
+ // Load parent dex if it is not in boot class path.
+ ClassLoader parentLoader = null;
+ if (parentInBoot) {
+ parentLoader = BOOT_CLASS_LOADER;
+ } else {
+ parentLoader = new PathClassLoader(DEX_PARENT, ClassLoader.getSystemClassLoader());
+ }
+
+ // Load child dex if it is not in boot class path.
+ ClassLoader childLoader = null;
+ if (childInBoot) {
+ if (parentLoader != BOOT_CLASS_LOADER) {
+ throw new IllegalStateException(
+ "DeclaringClass must be in parent class loader of CallingClass");
+ }
+ childLoader = BOOT_CLASS_LOADER;
+ } else {
+ childLoader = new InMemoryDexClassLoader(readDexFile(DEX_CHILD), parentLoader);
+ }
+
+ // Create a unique copy of the native library. Each shared library can only
+ // be loaded once, but for some reason even classes from a class loader
+ // cannot register their native methods against symbols in a shared library
+ // loaded by their parent class loader.
+ String nativeLibCopy = createNativeLibCopy(parentInBoot, childInBoot);
+
+ // Invoke ChildClass.runTest
+ Class.forName("ChildClass", true, childLoader)
+ .getDeclaredMethod("runTest", String.class, Boolean.TYPE, Boolean.TYPE)
+ .invoke(null, nativeLibCopy, parentInBoot, childInBoot);
+ }
+
+ // Routine which tries to figure out the absolute path of our native library.
+ private static void prepareNativeLibFileName(String arg) throws Exception {
+ String libName = System.mapLibraryName(arg);
+ Method libPathsMethod = Runtime.class.getDeclaredMethod("getLibPaths");
+ libPathsMethod.setAccessible(true);
+ String[] libPaths = (String[]) libPathsMethod.invoke(Runtime.getRuntime());
+ nativeLibFileName = null;
+ for (String p : libPaths) {
+ String candidate = p + libName;
+ if (new File(candidate).exists()) {
+ nativeLibFileName = candidate;
+ break;
+ }
+ }
+ if (nativeLibFileName == null) {
+ throw new IllegalStateException("Didn't find " + libName + " in " +
+ Arrays.toString(libPaths));
+ }
+ }
+
+ // Helper to read dex file into memory.
+ private static ByteBuffer readDexFile(String jarFileName) throws Exception {
+ ZipFile zip = new ZipFile(new File(jarFileName));
+ ZipEntry entry = zip.getEntry("classes.dex");
+ InputStream is = zip.getInputStream(entry);
+ int offset = 0;
+ int size = (int) entry.getSize();
+ ByteBuffer buffer = ByteBuffer.allocate(size);
+ while (is.available() > 0) {
+ is.read(buffer.array(), offset, size - offset);
+ }
+ is.close();
+ zip.close();
+ return buffer;
+ }
+
+ // Copy native library to a new file with a unique name so it does not conflict
+ // with other loaded instance of the same binary file.
+ private static String createNativeLibCopy(boolean parentInBoot, boolean childInBoot)
+ throws Exception {
+ String tempFileName = System.mapLibraryName(
+ "hiddenapitest_" + (parentInBoot ? "1" : "0") + (childInBoot ? "1" : "0"));
+ File tempFile = new File(System.getenv("DEX_LOCATION"), tempFileName);
+ Files.copy(new File(nativeLibFileName).toPath(), tempFile.toPath());
+ return tempFile.getAbsolutePath();
+ }
+
+ private static void doUnloading() {
+ // Do multiple GCs to prevent rare flakiness if some other thread is keeping the
+ // classloader live.
+ for (int i = 0; i < 5; ++i) {
+ Runtime.getRuntime().gc();
+ }
+ }
+
+ private static String nativeLibFileName;
+
+ private static final String DEX_PARENT =
+ new File(System.getenv("DEX_LOCATION"), "674-hiddenapi.jar").getAbsolutePath();
+ private static final String DEX_PARENT_BOOT =
+ new File(new File(System.getenv("DEX_LOCATION"), "res"), "boot.jar").getAbsolutePath();
+ private static final String DEX_CHILD =
+ new File(System.getenv("DEX_LOCATION"), "674-hiddenapi-ex.jar").getAbsolutePath();
+
+ private static ClassLoader BOOT_CLASS_LOADER = Object.class.getClassLoader();
+
+ private static native void appendToBootClassLoader(String dexPath);
+ private static native void init();
+}
diff --git a/test/674-hiddenapi/src-ex/ChildClass.java b/test/674-hiddenapi/src-ex/ChildClass.java
new file mode 100644
index 0000000000..babd88359b
--- /dev/null
+++ b/test/674-hiddenapi/src-ex/ChildClass.java
@@ -0,0 +1,438 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.type.PrimitiveType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeVisitor;
+
+public class ChildClass {
+ enum PrimitiveType {
+ TInteger('I', Integer.TYPE, Integer.valueOf(0)),
+ TLong('J', Long.TYPE, Long.valueOf(0)),
+ TFloat('F', Float.TYPE, Float.valueOf(0)),
+ TDouble('D', Double.TYPE, Double.valueOf(0)),
+ TBoolean('Z', Boolean.TYPE, Boolean.valueOf(false)),
+ TByte('B', Byte.TYPE, Byte.valueOf((byte) 0)),
+ TShort('S', Short.TYPE, Short.valueOf((short) 0)),
+ TCharacter('C', Character.TYPE, Character.valueOf('0'));
+
+ PrimitiveType(char shorty, Class klass, Object value) {
+ mShorty = shorty;
+ mClass = klass;
+ mDefaultValue = value;
+ }
+
+ public char mShorty;
+ public Class mClass;
+ public Object mDefaultValue;
+ }
+
+ enum Hiddenness {
+ Whitelist(PrimitiveType.TShort),
+ LightGreylist(PrimitiveType.TBoolean),
+ DarkGreylist(PrimitiveType.TByte),
+ Blacklist(PrimitiveType.TCharacter);
+
+ Hiddenness(PrimitiveType type) { mAssociatedType = type; }
+ public PrimitiveType mAssociatedType;
+ }
+
+ enum Visibility {
+ Public(PrimitiveType.TInteger),
+ Package(PrimitiveType.TFloat),
+ Protected(PrimitiveType.TLong),
+ Private(PrimitiveType.TDouble);
+
+ Visibility(PrimitiveType type) { mAssociatedType = type; }
+ public PrimitiveType mAssociatedType;
+ }
+
+ enum Behaviour {
+ Granted,
+ Warning,
+ Denied,
+ }
+
+ private static final boolean booleanValues[] = new boolean[] { false, true };
+
+ public static void runTest(String libFileName, boolean expectedParentInBoot,
+ boolean expectedChildInBoot) throws Exception {
+ System.load(libFileName);
+
+ // Check expectations about loading into boot class path.
+ isParentInBoot = (ParentClass.class.getClassLoader().getParent() == null);
+ if (isParentInBoot != expectedParentInBoot) {
+ throw new RuntimeException("Expected ParentClass " +
+ (expectedParentInBoot ? "" : "not ") + "in boot class path");
+ }
+ isChildInBoot = (ChildClass.class.getClassLoader().getParent() == null);
+ if (isChildInBoot != expectedChildInBoot) {
+ throw new RuntimeException("Expected ChildClass " + (expectedChildInBoot ? "" : "not ") +
+ "in boot class path");
+ }
+
+ boolean isSameBoot = (isParentInBoot == isChildInBoot);
+
+ // Run meaningful combinations of access flags.
+ for (Hiddenness hiddenness : Hiddenness.values()) {
+ final Behaviour expected;
+ if (isSameBoot || hiddenness == Hiddenness.Whitelist) {
+ expected = Behaviour.Granted;
+ } else if (hiddenness == Hiddenness.Blacklist) {
+ expected = Behaviour.Denied;
+ } else {
+ expected = Behaviour.Warning;
+ }
+
+ for (boolean isStatic : booleanValues) {
+ String suffix = (isStatic ? "Static" : "") + hiddenness.name();
+
+ for (Visibility visibility : Visibility.values()) {
+ // Test reflection and JNI on methods and fields
+ for (Class klass : new Class<?>[] { ParentClass.class, ParentInterface.class }) {
+ String baseName = visibility.name() + suffix;
+ checkField(klass, "field" + baseName, isStatic, visibility, expected);
+ checkMethod(klass, "method" + baseName, isStatic, visibility, expected);
+ }
+
+ // Check whether one can use a class constructor.
+ checkConstructor(ParentClass.class, visibility, hiddenness, expected);
+
+ // Check whether you can use an interface default method.
+ String name = "method" + visibility.name() + "Default" + hiddenness.name();
+ checkMethod(ParentInterface.class, name, /*isStatic*/ false, visibility, expected);
+ }
+
+ // Test whether static linking succeeds.
+ checkLinking("LinkFieldGet" + suffix, /*takesParameter*/ false, expected);
+ checkLinking("LinkFieldSet" + suffix, /*takesParameter*/ true, expected);
+ checkLinking("LinkMethod" + suffix, /*takesParameter*/ false, expected);
+ }
+
+ // Check whether Class.newInstance succeeds.
+ checkNullaryConstructor(Class.forName("NullaryConstructor" + hiddenness.name()), expected);
+ }
+ }
+
+ private static void checkField(Class<?> klass, String name, boolean isStatic,
+ Visibility visibility, Behaviour behaviour) throws Exception {
+
+ boolean isPublic = (visibility == Visibility.Public);
+ boolean canDiscover = (behaviour != Behaviour.Denied);
+ boolean setsWarning = (behaviour == Behaviour.Warning);
+
+ if (klass.isInterface() && (!isStatic || !isPublic)) {
+ // Interfaces only have public static fields.
+ return;
+ }
+
+ // Test discovery with reflection.
+
+ if (Reflection.canDiscoverWithGetDeclaredField(klass, name) != canDiscover) {
+ throwDiscoveryException(klass, name, true, "getDeclaredField()", canDiscover);
+ }
+
+ if (Reflection.canDiscoverWithGetDeclaredFields(klass, name) != canDiscover) {
+ throwDiscoveryException(klass, name, true, "getDeclaredFields()", canDiscover);
+ }
+
+ if (Reflection.canDiscoverWithGetField(klass, name) != (canDiscover && isPublic)) {
+ throwDiscoveryException(klass, name, true, "getField()", (canDiscover && isPublic));
+ }
+
+ if (Reflection.canDiscoverWithGetFields(klass, name) != (canDiscover && isPublic)) {
+ throwDiscoveryException(klass, name, true, "getFields()", (canDiscover && isPublic));
+ }
+
+ // Test discovery with JNI.
+
+ if (JNI.canDiscoverField(klass, name, isStatic) != canDiscover) {
+ throwDiscoveryException(klass, name, true, "JNI", canDiscover);
+ }
+
+ // Finish here if we could not discover the field.
+
+ if (!canDiscover) {
+ return;
+ }
+
+ // Test that modifiers are unaffected.
+
+ if (Reflection.canObserveFieldHiddenAccessFlags(klass, name)) {
+ throwModifiersException(klass, name, true);
+ }
+
+ // Test getters and setters when meaningful.
+
+ clearWarning();
+ if (!Reflection.canGetField(klass, name)) {
+ throwAccessException(klass, name, true, "Field.getInt()");
+ }
+ if (hasPendingWarning() != setsWarning) {
+ throwWarningException(klass, name, true, "Field.getInt()", setsWarning);
+ }
+
+ clearWarning();
+ if (!Reflection.canSetField(klass, name)) {
+ throwAccessException(klass, name, true, "Field.setInt()");
+ }
+ if (hasPendingWarning() != setsWarning) {
+ throwWarningException(klass, name, true, "Field.setInt()", setsWarning);
+ }
+
+ clearWarning();
+ if (!JNI.canGetField(klass, name, isStatic)) {
+ throwAccessException(klass, name, true, "getIntField");
+ }
+ if (hasPendingWarning() != setsWarning) {
+ throwWarningException(klass, name, true, "getIntField", setsWarning);
+ }
+
+ clearWarning();
+ if (!JNI.canSetField(klass, name, isStatic)) {
+ throwAccessException(klass, name, true, "setIntField");
+ }
+ if (hasPendingWarning() != setsWarning) {
+ throwWarningException(klass, name, true, "setIntField", setsWarning);
+ }
+ }
+
+ private static void checkMethod(Class<?> klass, String name, boolean isStatic,
+ Visibility visibility, Behaviour behaviour) throws Exception {
+
+ boolean isPublic = (visibility == Visibility.Public);
+ if (klass.isInterface() && !isPublic) {
+ // All interface members are public.
+ return;
+ }
+
+ boolean canDiscover = (behaviour != Behaviour.Denied);
+ boolean setsWarning = (behaviour == Behaviour.Warning);
+
+ // Test discovery with reflection.
+
+ if (Reflection.canDiscoverWithGetDeclaredMethod(klass, name) != canDiscover) {
+ throwDiscoveryException(klass, name, false, "getDeclaredMethod()", canDiscover);
+ }
+
+ if (Reflection.canDiscoverWithGetDeclaredMethods(klass, name) != canDiscover) {
+ throwDiscoveryException(klass, name, false, "getDeclaredMethods()", canDiscover);
+ }
+
+ if (Reflection.canDiscoverWithGetMethod(klass, name) != (canDiscover && isPublic)) {
+ throwDiscoveryException(klass, name, false, "getMethod()", (canDiscover && isPublic));
+ }
+
+ if (Reflection.canDiscoverWithGetMethods(klass, name) != (canDiscover && isPublic)) {
+ throwDiscoveryException(klass, name, false, "getMethods()", (canDiscover && isPublic));
+ }
+
+ // Test discovery with JNI.
+
+ if (JNI.canDiscoverMethod(klass, name, isStatic) != canDiscover) {
+ throwDiscoveryException(klass, name, false, "JNI", canDiscover);
+ }
+
+ // Finish here if we could not discover the field.
+
+ if (!canDiscover) {
+ return;
+ }
+
+ // Test that modifiers are unaffected.
+
+ if (Reflection.canObserveMethodHiddenAccessFlags(klass, name)) {
+ throwModifiersException(klass, name, false);
+ }
+
+ // Test whether we can invoke the method. This skips non-static interface methods.
+
+ if (!klass.isInterface() || isStatic) {
+ clearWarning();
+ if (!Reflection.canInvokeMethod(klass, name)) {
+ throwAccessException(klass, name, false, "invoke()");
+ }
+ if (hasPendingWarning() != setsWarning) {
+ throwWarningException(klass, name, false, "invoke()", setsWarning);
+ }
+
+ clearWarning();
+ if (!JNI.canInvokeMethodA(klass, name, isStatic)) {
+ throwAccessException(klass, name, false, "CallMethodA");
+ }
+ if (hasPendingWarning() != setsWarning) {
+ throwWarningException(klass, name, false, "CallMethodA()", setsWarning);
+ }
+
+ clearWarning();
+ if (!JNI.canInvokeMethodV(klass, name, isStatic)) {
+ throwAccessException(klass, name, false, "CallMethodV");
+ }
+ if (hasPendingWarning() != setsWarning) {
+ throwWarningException(klass, name, false, "CallMethodV()", setsWarning);
+ }
+ }
+ }
+
+ private static void checkConstructor(Class<?> klass, Visibility visibility, Hiddenness hiddenness,
+ Behaviour behaviour) throws Exception {
+
+ boolean isPublic = (visibility == Visibility.Public);
+ String signature = "(" + visibility.mAssociatedType.mShorty +
+ hiddenness.mAssociatedType.mShorty + ")V";
+ String fullName = "<init>" + signature;
+ Class<?> args[] = new Class[] { visibility.mAssociatedType.mClass,
+ hiddenness.mAssociatedType.mClass };
+ Object initargs[] = new Object[] { visibility.mAssociatedType.mDefaultValue,
+ hiddenness.mAssociatedType.mDefaultValue };
+
+ boolean canDiscover = (behaviour != Behaviour.Denied);
+ boolean setsWarning = (behaviour == Behaviour.Warning);
+
+ // Test discovery with reflection.
+
+ if (Reflection.canDiscoverWithGetDeclaredConstructor(klass, args) != canDiscover) {
+ throwDiscoveryException(klass, fullName, false, "getDeclaredConstructor()", canDiscover);
+ }
+
+ if (Reflection.canDiscoverWithGetDeclaredConstructors(klass, args) != canDiscover) {
+ throwDiscoveryException(klass, fullName, false, "getDeclaredConstructors()", canDiscover);
+ }
+
+ if (Reflection.canDiscoverWithGetConstructor(klass, args) != (canDiscover && isPublic)) {
+ throwDiscoveryException(
+ klass, fullName, false, "getConstructor()", (canDiscover && isPublic));
+ }
+
+ if (Reflection.canDiscoverWithGetConstructors(klass, args) != (canDiscover && isPublic)) {
+ throwDiscoveryException(
+ klass, fullName, false, "getConstructors()", (canDiscover && isPublic));
+ }
+
+ // Test discovery with JNI.
+
+ if (JNI.canDiscoverConstructor(klass, signature) != canDiscover) {
+ throwDiscoveryException(klass, fullName, false, "JNI", canDiscover);
+ }
+
+ // Finish here if we could not discover the field.
+
+ if (!canDiscover) {
+ return;
+ }
+
+ // Test whether we can invoke the constructor.
+
+ clearWarning();
+ if (!Reflection.canInvokeConstructor(klass, args, initargs)) {
+ throwAccessException(klass, fullName, false, "invoke()");
+ }
+ if (hasPendingWarning() != setsWarning) {
+ throwWarningException(klass, fullName, false, "invoke()", setsWarning);
+ }
+
+ clearWarning();
+ if (!JNI.canInvokeConstructorA(klass, signature)) {
+ throwAccessException(klass, fullName, false, "NewObjectA");
+ }
+ if (hasPendingWarning() != setsWarning) {
+ throwWarningException(klass, fullName, false, "NewObjectA", setsWarning);
+ }
+
+ clearWarning();
+ if (!JNI.canInvokeConstructorV(klass, signature)) {
+ throwAccessException(klass, fullName, false, "NewObjectV");
+ }
+ if (hasPendingWarning() != setsWarning) {
+ throwWarningException(klass, fullName, false, "NewObjectV", setsWarning);
+ }
+ }
+
+ private static void checkNullaryConstructor(Class<?> klass, Behaviour behaviour)
+ throws Exception {
+ boolean canAccess = (behaviour != Behaviour.Denied);
+ boolean setsWarning = (behaviour == Behaviour.Warning);
+
+ clearWarning();
+ if (Reflection.canUseNewInstance(klass) != canAccess) {
+ throw new RuntimeException("Expected to " + (canAccess ? "" : "not ") +
+ "be able to construct " + klass.getName() + ". " +
+ "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot);
+ }
+ if (canAccess && hasPendingWarning() != setsWarning) {
+ throwWarningException(klass, "nullary constructor", false, "newInstance", setsWarning);
+ }
+ }
+
+ private static void checkLinking(String className, boolean takesParameter, Behaviour behaviour)
+ throws Exception {
+ boolean canAccess = (behaviour != Behaviour.Denied);
+ boolean setsWarning = false; // we do not set the flag in verifier or at runtime
+
+ clearWarning();
+ if (Linking.canAccess(className, takesParameter) != canAccess) {
+ throw new RuntimeException("Expected to " + (canAccess ? "" : "not ") +
+ "be able to verify " + className + "." +
+ "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot);
+ }
+ if (canAccess && hasPendingWarning() != setsWarning) {
+ throwWarningException(
+ Class.forName(className), "access", false, "static linking", setsWarning);
+ }
+ }
+
+ private static void throwDiscoveryException(Class<?> klass, String name, boolean isField,
+ String fn, boolean canAccess) {
+ throw new RuntimeException("Expected " + (isField ? "field " : "method ") + klass.getName() +
+ "." + name + " to " + (canAccess ? "" : "not ") + "be discoverable with " + fn + ". " +
+ "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot);
+ }
+
+ private static void throwAccessException(Class<?> klass, String name, boolean isField,
+ String fn) {
+ throw new RuntimeException("Expected to be able to access " + (isField ? "field " : "method ") +
+ klass.getName() + "." + name + " using " + fn + ". " +
+ "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot);
+ }
+
+ private static void throwWarningException(Class<?> klass, String name, boolean isField,
+ String fn, boolean setsWarning) {
+ throw new RuntimeException("Expected access to " + (isField ? "field " : "method ") +
+ klass.getName() + "." + name + " using " + fn + " to " + (setsWarning ? "" : "not ") +
+ "set the warning flag. " +
+ "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot);
+ }
+
+ private static void throwModifiersException(Class<?> klass, String name, boolean isField) {
+ throw new RuntimeException("Expected " + (isField ? "field " : "method ") + klass.getName() +
+ "." + name + " to not expose hidden modifiers");
+ }
+
+ private static boolean isParentInBoot;
+ private static boolean isChildInBoot;
+
+ private static native boolean hasPendingWarning();
+ private static native void clearWarning();
+}
diff --git a/test/674-hiddenapi/src-ex/JNI.java b/test/674-hiddenapi/src-ex/JNI.java
new file mode 100644
index 0000000000..5dfb2963fa
--- /dev/null
+++ b/test/674-hiddenapi/src-ex/JNI.java
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+public class JNI {
+ public static native boolean canDiscoverField(Class<?> klass, String name, boolean isStatic);
+ public static native boolean canGetField(Class<?> klass, String name, boolean isStatic);
+ public static native boolean canSetField(Class<?> klass, String name, boolean isStatic);
+
+ public static native boolean canDiscoverMethod(Class<?> klass, String name, boolean isStatic);
+ public static native boolean canInvokeMethodA(Class<?> klass, String name, boolean isStatic);
+ public static native boolean canInvokeMethodV(Class<?> klass, String name, boolean isStatic);
+
+ public static native boolean canDiscoverConstructor(Class<?> klass, String signature);
+ public static native boolean canInvokeConstructorA(Class<?> klass, String signature);
+ public static native boolean canInvokeConstructorV(Class<?> klass, String signature);
+}
diff --git a/test/674-hiddenapi/src-ex/Linking.java b/test/674-hiddenapi/src-ex/Linking.java
new file mode 100644
index 0000000000..c6735d85fe
--- /dev/null
+++ b/test/674-hiddenapi/src-ex/Linking.java
@@ -0,0 +1,193 @@
+/*
+ * 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.
+ */
+
+import java.lang.reflect.InvocationTargetException;
+
+public class Linking {
+ public static boolean canAccess(String className, boolean takesParameter) throws Exception {
+ try {
+ Class<?> c = Class.forName(className);
+ if (takesParameter) {
+ c.getDeclaredMethod("access", Integer.TYPE).invoke(null, 42);
+ } else {
+ c.getDeclaredMethod("access").invoke(null);
+ }
+ return true;
+ } catch (InvocationTargetException ex) {
+ if (ex.getCause() instanceof IllegalAccessError) {
+ return false;
+ } else {
+ throw ex;
+ }
+ }
+ }
+}
+
+// INSTANCE FIELD GET
+
+class LinkFieldGetWhitelist {
+ public static int access() {
+ return new ParentClass().fieldPublicWhitelist;
+ }
+}
+
+class LinkFieldGetLightGreylist {
+ public static int access() {
+ return new ParentClass().fieldPublicLightGreylist;
+ }
+}
+
+class LinkFieldGetDarkGreylist {
+ public static int access() {
+ return new ParentClass().fieldPublicDarkGreylist;
+ }
+}
+
+class LinkFieldGetBlacklist {
+ public static int access() {
+ return new ParentClass().fieldPublicBlacklist;
+ }
+}
+
+// INSTANCE FIELD SET
+
+class LinkFieldSetWhitelist {
+ public static void access(int x) {
+ new ParentClass().fieldPublicWhitelist = x;
+ }
+}
+
+class LinkFieldSetLightGreylist {
+ public static void access(int x) {
+ new ParentClass().fieldPublicLightGreylist = x;
+ }
+}
+
+class LinkFieldSetDarkGreylist {
+ public static void access(int x) {
+ new ParentClass().fieldPublicDarkGreylist = x;
+ }
+}
+
+class LinkFieldSetBlacklist {
+ public static void access(int x) {
+ new ParentClass().fieldPublicBlacklist = x;
+ }
+}
+
+// STATIC FIELD GET
+
+class LinkFieldGetStaticWhitelist {
+ public static int access() {
+ return ParentClass.fieldPublicStaticWhitelist;
+ }
+}
+
+class LinkFieldGetStaticLightGreylist {
+ public static int access() {
+ return ParentClass.fieldPublicStaticLightGreylist;
+ }
+}
+
+class LinkFieldGetStaticDarkGreylist {
+ public static int access() {
+ return ParentClass.fieldPublicStaticDarkGreylist;
+ }
+}
+
+class LinkFieldGetStaticBlacklist {
+ public static int access() {
+ return ParentClass.fieldPublicStaticBlacklist;
+ }
+}
+
+// STATIC FIELD SET
+
+class LinkFieldSetStaticWhitelist {
+ public static void access(int x) {
+ ParentClass.fieldPublicStaticWhitelist = x;
+ }
+}
+
+class LinkFieldSetStaticLightGreylist {
+ public static void access(int x) {
+ ParentClass.fieldPublicStaticLightGreylist = x;
+ }
+}
+
+class LinkFieldSetStaticDarkGreylist {
+ public static void access(int x) {
+ ParentClass.fieldPublicStaticDarkGreylist = x;
+ }
+}
+
+class LinkFieldSetStaticBlacklist {
+ public static void access(int x) {
+ ParentClass.fieldPublicStaticBlacklist = x;
+ }
+}
+
+// INVOKE INSTANCE METHOD
+
+class LinkMethodWhitelist {
+ public static int access() {
+ return new ParentClass().methodPublicWhitelist();
+ }
+}
+
+class LinkMethodLightGreylist {
+ public static int access() {
+ return new ParentClass().methodPublicLightGreylist();
+ }
+}
+
+class LinkMethodDarkGreylist {
+ public static int access() {
+ return new ParentClass().methodPublicDarkGreylist();
+ }
+}
+
+class LinkMethodBlacklist {
+ public static int access() {
+ return new ParentClass().methodPublicBlacklist();
+ }
+}
+
+// INVOKE STATIC METHOD
+
+class LinkMethodStaticWhitelist {
+ public static int access() {
+ return ParentClass.methodPublicStaticWhitelist();
+ }
+}
+
+class LinkMethodStaticLightGreylist {
+ public static int access() {
+ return ParentClass.methodPublicStaticLightGreylist();
+ }
+}
+
+class LinkMethodStaticDarkGreylist {
+ public static int access() {
+ return ParentClass.methodPublicStaticDarkGreylist();
+ }
+}
+
+class LinkMethodStaticBlacklist {
+ public static int access() {
+ return ParentClass.methodPublicStaticBlacklist();
+ }
+}
diff --git a/test/674-hiddenapi/src-ex/Reflection.java b/test/674-hiddenapi/src-ex/Reflection.java
new file mode 100644
index 0000000000..3667e91611
--- /dev/null
+++ b/test/674-hiddenapi/src-ex/Reflection.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+
+public class Reflection {
+ public static boolean canDiscoverWithGetDeclaredField(Class<?> klass, String name) {
+ try {
+ klass.getDeclaredField(name);
+ return true;
+ } catch (NoSuchFieldException ex) {
+ return false;
+ }
+ }
+
+ public static boolean canDiscoverWithGetDeclaredFields(Class<?> klass, String name) {
+ for (Field f : klass.getDeclaredFields()) {
+ if (f.getName().equals(name)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static boolean canDiscoverWithGetField(Class<?> klass, String name) {
+ try {
+ klass.getField(name);
+ return true;
+ } catch (NoSuchFieldException ex) {
+ return false;
+ }
+ }
+
+ public static boolean canDiscoverWithGetFields(Class<?> klass, String name) {
+ for (Field f : klass.getFields()) {
+ if (f.getName().equals(name)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static boolean canGetField(Class<?> klass, String name) {
+ try {
+ Field f = klass.getDeclaredField(name);
+ f.setAccessible(true);
+ f.getInt(Modifier.isStatic(f.getModifiers()) ? null : klass.newInstance());
+ return true;
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ return false;
+ }
+ }
+
+ public static boolean canSetField(Class<?> klass, String name) {
+ try {
+ Field f = klass.getDeclaredField(name);
+ f.setAccessible(true);
+ f.setInt(Modifier.isStatic(f.getModifiers()) ? null : klass.newInstance(), 42);
+ return true;
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ return false;
+ }
+ }
+
+ public static boolean canDiscoverWithGetDeclaredMethod(Class<?> klass, String name) {
+ try {
+ klass.getDeclaredMethod(name);
+ return true;
+ } catch (NoSuchMethodException ex) {
+ return false;
+ }
+ }
+
+ public static boolean canDiscoverWithGetDeclaredMethods(Class<?> klass, String name) {
+ for (Method m : klass.getDeclaredMethods()) {
+ if (m.getName().equals(name)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static boolean canDiscoverWithGetMethod(Class<?> klass, String name) {
+ try {
+ klass.getMethod(name);
+ return true;
+ } catch (NoSuchMethodException ex) {
+ return false;
+ }
+ }
+
+ public static boolean canDiscoverWithGetMethods(Class<?> klass, String name) {
+ for (Method m : klass.getMethods()) {
+ if (m.getName().equals(name)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static boolean canInvokeMethod(Class<?> klass, String name) {
+ try {
+ Method m = klass.getDeclaredMethod(name);
+ m.setAccessible(true);
+ m.invoke(klass.isInterface() ? null : klass.newInstance());
+ return true;
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ return false;
+ }
+ }
+
+ public static boolean canDiscoverWithGetDeclaredConstructor(Class<?> klass, Class<?> args[]) {
+ try {
+ klass.getDeclaredConstructor(args);
+ return true;
+ } catch (NoSuchMethodException ex) {
+ return false;
+ }
+ }
+
+ public static boolean canDiscoverWithGetDeclaredConstructors(Class<?> klass, Class<?> args[]) {
+ for (Constructor c : klass.getDeclaredConstructors()) {
+ if (Arrays.equals(c.getParameterTypes(), args)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static boolean canDiscoverWithGetConstructor(Class<?> klass, Class<?> args[]) {
+ try {
+ klass.getConstructor(args);
+ return true;
+ } catch (NoSuchMethodException ex) {
+ return false;
+ }
+ }
+
+ public static boolean canDiscoverWithGetConstructors(Class<?> klass, Class<?> args[]) {
+ for (Constructor c : klass.getConstructors()) {
+ if (Arrays.equals(c.getParameterTypes(), args)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static boolean canInvokeConstructor(Class<?> klass, Class<?> args[], Object[] initargs) {
+ try {
+ Constructor c = klass.getDeclaredConstructor(args);
+ c.setAccessible(true);
+ c.newInstance(initargs);
+ return true;
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ return false;
+ }
+ }
+
+ public static boolean canUseNewInstance(Class<?> klass) throws IllegalAccessException {
+ try {
+ klass.newInstance();
+ return true;
+ } catch (InstantiationException ex) {
+ return false;
+ }
+ }
+
+ private static native int getHiddenApiAccessFlags();
+
+ public static boolean canObserveFieldHiddenAccessFlags(Class<?> klass, String name)
+ throws Exception {
+ return (klass.getDeclaredField(name).getModifiers() & getHiddenApiAccessFlags()) != 0;
+ }
+
+ public static boolean canObserveMethodHiddenAccessFlags(Class<?> klass, String name)
+ throws Exception {
+ return (klass.getDeclaredMethod(name).getModifiers() & getHiddenApiAccessFlags()) != 0;
+ }
+
+ public static boolean canObserveConstructorHiddenAccessFlags(Class<?> klass, Class<?> args[])
+ throws Exception {
+ return (klass.getConstructor(args).getModifiers() & getHiddenApiAccessFlags()) != 0;
+ }
+}
diff --git a/test/674-hiddenapi/src/NullaryConstructorBlacklist.java b/test/674-hiddenapi/src/NullaryConstructorBlacklist.java
new file mode 100644
index 0000000000..5bf6278a77
--- /dev/null
+++ b/test/674-hiddenapi/src/NullaryConstructorBlacklist.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+public class NullaryConstructorBlacklist {
+ public NullaryConstructorBlacklist() { x = 22; }
+ public NullaryConstructorBlacklist(int y) { x = y; }
+ protected int x;
+}
diff --git a/test/674-hiddenapi/src/NullaryConstructorDarkGreylist.java b/test/674-hiddenapi/src/NullaryConstructorDarkGreylist.java
new file mode 100644
index 0000000000..c25a767d1d
--- /dev/null
+++ b/test/674-hiddenapi/src/NullaryConstructorDarkGreylist.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+public class NullaryConstructorDarkGreylist {
+ public NullaryConstructorDarkGreylist() { x = 22; }
+ public NullaryConstructorDarkGreylist(int y) { x = y; }
+ protected int x;
+}
diff --git a/test/674-hiddenapi/src/NullaryConstructorLightGreylist.java b/test/674-hiddenapi/src/NullaryConstructorLightGreylist.java
new file mode 100644
index 0000000000..d5dac8b7c0
--- /dev/null
+++ b/test/674-hiddenapi/src/NullaryConstructorLightGreylist.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+public class NullaryConstructorLightGreylist {
+ public NullaryConstructorLightGreylist() { x = 22; }
+ public NullaryConstructorLightGreylist(int y) { x = y; }
+ protected int x;
+}
diff --git a/test/674-hiddenapi/src/NullaryConstructorWhitelist.java b/test/674-hiddenapi/src/NullaryConstructorWhitelist.java
new file mode 100644
index 0000000000..d1019077cf
--- /dev/null
+++ b/test/674-hiddenapi/src/NullaryConstructorWhitelist.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+public class NullaryConstructorWhitelist {
+ public NullaryConstructorWhitelist() { x = 22; }
+ public NullaryConstructorWhitelist(int y) { x = y; }
+ protected int x;
+}
diff --git a/test/674-hiddenapi/src/ParentClass.java b/test/674-hiddenapi/src/ParentClass.java
new file mode 100644
index 0000000000..edad02dc2c
--- /dev/null
+++ b/test/674-hiddenapi/src/ParentClass.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+public class ParentClass {
+ public ParentClass() {}
+
+ // INSTANCE FIELD
+
+ public int fieldPublicWhitelist = 211;
+ int fieldPackageWhitelist = 212;
+ protected int fieldProtectedWhitelist = 213;
+ private int fieldPrivateWhitelist = 214;
+
+ public int fieldPublicLightGreylist = 221;
+ int fieldPackageLightGreylist = 222;
+ protected int fieldProtectedLightGreylist = 223;
+ private int fieldPrivateLightGreylist = 224;
+
+ public int fieldPublicDarkGreylist = 231;
+ int fieldPackageDarkGreylist = 232;
+ protected int fieldProtectedDarkGreylist = 233;
+ private int fieldPrivateDarkGreylist = 234;
+
+ public int fieldPublicBlacklist = 241;
+ int fieldPackageBlacklist = 242;
+ protected int fieldProtectedBlacklist = 243;
+ private int fieldPrivateBlacklist = 244;
+
+ // STATIC FIELD
+
+ public static int fieldPublicStaticWhitelist = 111;
+ static int fieldPackageStaticWhitelist = 112;
+ protected static int fieldProtectedStaticWhitelist = 113;
+ private static int fieldPrivateStaticWhitelist = 114;
+
+ public static int fieldPublicStaticLightGreylist = 121;
+ static int fieldPackageStaticLightGreylist = 122;
+ protected static int fieldProtectedStaticLightGreylist = 123;
+ private static int fieldPrivateStaticLightGreylist = 124;
+
+ public static int fieldPublicStaticDarkGreylist = 131;
+ static int fieldPackageStaticDarkGreylist = 132;
+ protected static int fieldProtectedStaticDarkGreylist = 133;
+ private static int fieldPrivateStaticDarkGreylist = 134;
+
+ public static int fieldPublicStaticBlacklist = 141;
+ static int fieldPackageStaticBlacklist = 142;
+ protected static int fieldProtectedStaticBlacklist = 143;
+ private static int fieldPrivateStaticBlacklist = 144;
+
+ // INSTANCE METHOD
+
+ public int methodPublicWhitelist() { return 411; }
+ int methodPackageWhitelist() { return 412; }
+ protected int methodProtectedWhitelist() { return 413; }
+ private int methodPrivateWhitelist() { return 414; }
+
+ public int methodPublicLightGreylist() { return 421; }
+ int methodPackageLightGreylist() { return 422; }
+ protected int methodProtectedLightGreylist() { return 423; }
+ private int methodPrivateLightGreylist() { return 424; }
+
+ public int methodPublicDarkGreylist() { return 431; }
+ int methodPackageDarkGreylist() { return 432; }
+ protected int methodProtectedDarkGreylist() { return 433; }
+ private int methodPrivateDarkGreylist() { return 434; }
+
+ public int methodPublicBlacklist() { return 441; }
+ int methodPackageBlacklist() { return 442; }
+ protected int methodProtectedBlacklist() { return 443; }
+ private int methodPrivateBlacklist() { return 444; }
+
+ // STATIC METHOD
+
+ public static int methodPublicStaticWhitelist() { return 311; }
+ static int methodPackageStaticWhitelist() { return 312; }
+ protected static int methodProtectedStaticWhitelist() { return 313; }
+ private static int methodPrivateStaticWhitelist() { return 314; }
+
+ public static int methodPublicStaticLightGreylist() { return 321; }
+ static int methodPackageStaticLightGreylist() { return 322; }
+ protected static int methodProtectedStaticLightGreylist() { return 323; }
+ private static int methodPrivateStaticLightGreylist() { return 324; }
+
+ public static int methodPublicStaticDarkGreylist() { return 331; }
+ static int methodPackageStaticDarkGreylist() { return 332; }
+ protected static int methodProtectedStaticDarkGreylist() { return 333; }
+ private static int methodPrivateStaticDarkGreylist() { return 334; }
+
+ public static int methodPublicStaticBlacklist() { return 341; }
+ static int methodPackageStaticBlacklist() { return 342; }
+ protected static int methodProtectedStaticBlacklist() { return 343; }
+ private static int methodPrivateStaticBlacklist() { return 344; }
+
+ // CONSTRUCTOR
+
+ // Whitelist
+ public ParentClass(int x, short y) {}
+ ParentClass(float x, short y) {}
+ protected ParentClass(long x, short y) {}
+ private ParentClass(double x, short y) {}
+
+ // Light greylist
+ public ParentClass(int x, boolean y) {}
+ ParentClass(float x, boolean y) {}
+ protected ParentClass(long x, boolean y) {}
+ private ParentClass(double x, boolean y) {}
+
+ // Dark greylist
+ public ParentClass(int x, byte y) {}
+ ParentClass(float x, byte y) {}
+ protected ParentClass(long x, byte y) {}
+ private ParentClass(double x, byte y) {}
+
+ // Blacklist
+ public ParentClass(int x, char y) {}
+ ParentClass(float x, char y) {}
+ protected ParentClass(long x, char y) {}
+ private ParentClass(double x, char y) {}
+}
diff --git a/test/674-hiddenapi/src/ParentInterface.java b/test/674-hiddenapi/src/ParentInterface.java
new file mode 100644
index 0000000000..e36fe0e6b2
--- /dev/null
+++ b/test/674-hiddenapi/src/ParentInterface.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+public interface ParentInterface {
+ // STATIC FIELD
+ static int fieldPublicStaticWhitelist = 11;
+ static int fieldPublicStaticLightGreylist = 12;
+ static int fieldPublicStaticDarkGreylist = 13;
+ static int fieldPublicStaticBlacklist = 14;
+
+ // INSTANCE METHOD
+ int methodPublicWhitelist();
+ int methodPublicBlacklist();
+ int methodPublicLightGreylist();
+ int methodPublicDarkGreylist();
+
+ // STATIC METHOD
+ static int methodPublicStaticWhitelist() { return 21; }
+ static int methodPublicStaticLightGreylist() { return 22; }
+ static int methodPublicStaticDarkGreylist() { return 23; }
+ static int methodPublicStaticBlacklist() { return 24; }
+
+ // DEFAULT METHOD
+ default int methodPublicDefaultWhitelist() { return 31; }
+ default int methodPublicDefaultLightGreylist() { return 32; }
+ default int methodPublicDefaultDarkGreylist() { return 33; }
+ default int methodPublicDefaultBlacklist() { return 34; }
+}
diff --git a/test/674-vdex-uncompress/build b/test/674-vdex-uncompress/build
new file mode 100755
index 0000000000..7b1804d3e0
--- /dev/null
+++ b/test/674-vdex-uncompress/build
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+# Uncompress and align the dex files so that dex2oat will not copy the dex
+# code to the .vdex file.
+./default-build "$@" --zip-compression-method store --zip-align 4
diff --git a/test/674-vdex-uncompress/expected.txt b/test/674-vdex-uncompress/expected.txt
new file mode 100644
index 0000000000..d0f61f692c
--- /dev/null
+++ b/test/674-vdex-uncompress/expected.txt
@@ -0,0 +1,2 @@
+In foo
+In foo
diff --git a/test/674-vdex-uncompress/info.txt b/test/674-vdex-uncompress/info.txt
new file mode 100644
index 0000000000..6aa6f7b0d5
--- /dev/null
+++ b/test/674-vdex-uncompress/info.txt
@@ -0,0 +1,2 @@
+Test that dex2oat can compile an APK that has uncompressed dex files,
+and where --input-vdex is passed.
diff --git a/test/674-vdex-uncompress/run b/test/674-vdex-uncompress/run
new file mode 100644
index 0000000000..edf699f842
--- /dev/null
+++ b/test/674-vdex-uncompress/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# 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.
+
+exec ${RUN} -Xcompiler-option --compiler-filter=verify --vdex "${@}"
diff --git a/test/674-vdex-uncompress/src/Main.java b/test/674-vdex-uncompress/src/Main.java
new file mode 100644
index 0000000000..0a25b564fe
--- /dev/null
+++ b/test/674-vdex-uncompress/src/Main.java
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+public class Main {
+ Main() {
+ // Will be quickened with RETURN_VOID_NO_BARRIER.
+ }
+
+ public static void main(String[] args) {
+ Main m = new Main();
+ Object o = m;
+ // The call and field accesses will be quickened.
+ m.foo(m.a);
+
+ // The checkcast will be quickened.
+ m.foo(((Main)o).a);
+ }
+
+ int a;
+ void foo(int a) {
+ System.out.println("In foo");
+ }
+}
+
diff --git a/test/Android.bp b/test/Android.bp
index 49a34a1246..470a68f386 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -389,6 +389,7 @@ cc_defaults {
"661-oat-writer-layout/oat_writer_layout.cc",
"664-aget-verifier/aget-verifier.cc",
"667-jit-jni-stub/jit_jni_stub_test.cc",
+ "674-hiddenapi/hiddenapi.cc",
"708-jit-cache-churn/jit.cc",
],
shared_libs: [
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 2cada76d90..2df0cc6fae 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -20,6 +20,7 @@ include art/build/Android.common_test.mk
# Dependencies for actually running a run-test.
TEST_ART_RUN_TEST_DEPENDENCIES := \
$(HOST_OUT_EXECUTABLES)/dx \
+ $(HOST_OUT_EXECUTABLES)/hiddenapi \
$(HOST_OUT_EXECUTABLES)/jasmin \
$(HOST_OUT_EXECUTABLES)/smali \
$(HOST_OUT_EXECUTABLES)/dexmerger \
diff --git a/test/etc/default-build b/test/etc/default-build
index 5c8257f210..4dc2393c54 100755
--- a/test/etc/default-build
+++ b/test/etc/default-build
@@ -81,6 +81,17 @@ else
HAS_SRC_DEX2OAT_UNRESOLVED=false
fi
+if [ -f api-light-greylist.txt -o -f api-dark-greylist.txt -o -f api-blacklist.txt ]; then
+ HAS_HIDDENAPI_SPEC=true
+else
+ HAS_HIDDENAPI_SPEC=false
+fi
+
+# USE_HIDDENAPI=false run-test... will disable hiddenapi.
+if [ -z "${USE_HIDDENAPI}" ]; then
+ USE_HIDDENAPI=true
+fi
+
# DESUGAR=false run-test... will disable desugar.
if [[ "$DESUGAR" == false ]]; then
USE_DESUGAR=false
@@ -321,6 +332,24 @@ function make_dexmerge() {
${DXMERGER} "$dst_file" "${dex_files_to_merge[@]}"
}
+function make_hiddenapi() {
+ local args=()
+ while [[ $# -gt 0 ]]; do
+ args+=("--dex=$1")
+ shift
+ done
+ if [ -f api-light-greylist.txt ]; then
+ args+=("--light-greylist=api-light-greylist.txt")
+ fi
+ if [ -f api-dark-greylist.txt ]; then
+ args+=("--dark-greylist=api-dark-greylist.txt")
+ fi
+ if [ -f api-blacklist.txt ]; then
+ args+=("--blacklist=api-blacklist.txt")
+ fi
+ ${HIDDENAPI} "${args[@]}"
+}
+
# Print the directory name only if it exists.
function maybe_dir() {
local dirname="$1"
@@ -334,6 +363,13 @@ if [ -e classes.dex ]; then
exit 0
fi
+# Helper function for a common test. Evaluate with $(has_mutlidex).
+function has_multidex() {
+ echo [ ${HAS_SRC_MULTIDEX} = "true" \
+ -o ${HAS_JASMIN_MULTIDEX} = "true" \
+ -o ${HAS_SMALI_MULTIDEX} = "true" ]
+}
+
if [ ${HAS_SRC_DEX2OAT_UNRESOLVED} = "true" ]; then
mkdir classes
mkdir classes-ex
@@ -501,9 +537,18 @@ if [ ${HAS_SRC_EX} = "true" ]; then
fi
fi
+# Apply hiddenapi on the dex files if the test has API list file(s).
+if [ ${NEED_DEX} = "true" -a ${USE_HIDDENAPI} = "true" -a ${HAS_HIDDENAPI_SPEC} = "true" ]; then
+ if $(has_multidex); then
+ make_hiddenapi classes.dex classes2.dex
+ else
+ make_hiddenapi classes.dex
+ fi
+fi
+
# Create a single dex jar with two dex files for multidex.
if [ ${NEED_DEX} = "true" ]; then
- if [ ${HAS_SRC_MULTIDEX} = "true" ] || [ ${HAS_JASMIN_MULTIDEX} = "true" ] || [ ${HAS_SMALI_MULTIDEX} = "true" ]; then
+ if $(has_multidex); then
zip $TEST_NAME.jar classes.dex classes2.dex
else
zip $TEST_NAME.jar classes.dex
diff --git a/test/knownfailures.json b/test/knownfailures.json
index 41d976a174..27bec3e965 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -256,11 +256,6 @@
"testing deoptimizing at quick-to-interpreter bridge."]
},
{
- "tests": "137-cfi",
- "description": ["CFI unwinding expects managed frames"],
- "variant": "interpreter"
- },
- {
"tests": "906-iterate-heap",
"description": ["Test 906 iterates the heap filtering with different",
"options. No instances should be created between those",
diff --git a/test/run-test b/test/run-test
index 75fe15c919..a453f22e29 100755
--- a/test/run-test
+++ b/test/run-test
@@ -113,6 +113,12 @@ if [ -z "$ZIPALIGN" ]; then
fi
export ZIPALIGN
+# If hiddenapi was not set by the environment variable, assume it is in
+# ANDROID_HOST_OUT.
+if [ -z "$HIDDENAPI" ]; then
+ export HIDDENAPI="${ANDROID_HOST_OUT}/bin/hiddenapi"
+fi
+
info="info.txt"
build="build"
run="run"
diff --git a/test/ti-agent/common_helper.h b/test/ti-agent/common_helper.h
index fafa1afcda..e46abb6f5a 100644
--- a/test/ti-agent/common_helper.h
+++ b/test/ti-agent/common_helper.h
@@ -22,7 +22,7 @@
namespace art {
-// Taken from art/runtime/modifiers.h
+// Taken from art/runtime/dex/modifiers.h
static constexpr uint32_t kAccStatic = 0x0008; // field, method, ic
jobject GetJavaField(jvmtiEnv* jvmti, JNIEnv* env, jclass field_klass, jfieldID f);
diff --git a/tools/cpp-define-generator/constant_class.def b/tools/cpp-define-generator/constant_class.def
index 4f1d875e5a..1310103ab7 100644
--- a/tools/cpp-define-generator/constant_class.def
+++ b/tools/cpp-define-generator/constant_class.def
@@ -15,8 +15,8 @@
*/
#if defined(DEFINE_INCLUDE_DEPENDENCIES)
-#include "modifiers.h" // kAccClassIsFinalizable
#include "base/bit_utils.h" // MostSignificantBit
+#include "dex/modifiers.h" // kAccClassIsFinalizable
#endif
#define DEFINE_FLAG_OFFSET(type_name, field_name, expr) \
diff --git a/tools/cpp-define-generator/constant_globals.def b/tools/cpp-define-generator/constant_globals.def
index 5018f52937..539633e0b3 100644
--- a/tools/cpp-define-generator/constant_globals.def
+++ b/tools/cpp-define-generator/constant_globals.def
@@ -18,8 +18,8 @@
#if defined(DEFINE_INCLUDE_DEPENDENCIES)
#include <atomic> // std::memory_order_relaxed
+#include "dex/modifiers.h"
#include "globals.h" // art::kObjectAlignment
-#include "modifiers.h"
#endif
DEFINE_EXPR(STD_MEMORY_ORDER_RELAXED, int32_t, std::memory_order_relaxed)