diff options
| -rw-r--r-- | compiler/Android.mk | 1 | ||||
| -rw-r--r-- | compiler/compilers.h | 4 | ||||
| -rw-r--r-- | compiler/dex/quick/gen_invoke.cc | 16 | ||||
| -rw-r--r-- | compiler/dex/quick/mir_to_lir.cc | 59 | ||||
| -rw-r--r-- | compiler/dex/quick/mir_to_lir.h | 2 | ||||
| -rw-r--r-- | compiler/optimizing/builder.cc | 4 | ||||
| -rw-r--r-- | compiler/optimizing/code_generator.cc | 2 | ||||
| -rw-r--r-- | compiler/optimizing/graph_visualizer.cc | 199 | ||||
| -rw-r--r-- | compiler/optimizing/graph_visualizer.h | 63 | ||||
| -rw-r--r-- | compiler/optimizing/nodes.cc | 8 | ||||
| -rw-r--r-- | compiler/optimizing/nodes.h | 17 | ||||
| -rw-r--r-- | compiler/optimizing/optimizing_compiler.cc | 23 | ||||
| -rw-r--r-- | compiler/optimizing/ssa_builder.cc | 6 | ||||
| -rw-r--r-- | compiler/optimizing/ssa_liveness_analysis.cc | 8 | ||||
| -rw-r--r-- | compiler/optimizing/ssa_test.cc | 4 | ||||
| -rw-r--r-- | dex2oat/dex2oat.cc | 4 | ||||
| -rw-r--r-- | runtime/check_jni.cc | 2 | ||||
| -rw-r--r-- | runtime/class_linker.cc | 5 | ||||
| -rw-r--r-- | runtime/jni_internal.cc | 606 | ||||
| -rw-r--r-- | runtime/jni_internal_test.cc | 913 | ||||
| -rw-r--r-- | runtime/mirror/array.h | 13 | ||||
| -rw-r--r-- | runtime/native/dalvik_system_VMRuntime.cc | 8 | ||||
| -rw-r--r-- | runtime/parsed_options.cc | 4 |
23 files changed, 1344 insertions, 627 deletions
diff --git a/compiler/Android.mk b/compiler/Android.mk index 1b70d59def..8592aaa4e0 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -82,6 +82,7 @@ LIBART_COMPILER_SRC_FILES := \ optimizing/code_generator.cc \ optimizing/code_generator_arm.cc \ optimizing/code_generator_x86.cc \ + optimizing/graph_visualizer.cc \ optimizing/nodes.cc \ optimizing/optimizing_compiler.cc \ optimizing/ssa_builder.cc \ diff --git a/compiler/compilers.h b/compiler/compilers.h index 3ca78c94c6..e523d647ce 100644 --- a/compiler/compilers.h +++ b/compiler/compilers.h @@ -73,7 +73,7 @@ class QuickCompiler : public Compiler { class OptimizingCompiler FINAL : public QuickCompiler { public: - explicit OptimizingCompiler(CompilerDriver* driver) : QuickCompiler(driver) { } + explicit OptimizingCompiler(CompilerDriver* driver); CompiledMethod* Compile(const DexFile::CodeItem* code_item, uint32_t access_flags, @@ -92,6 +92,8 @@ class OptimizingCompiler FINAL : public QuickCompiler { const DexFile& dex_file) const; private: + UniquePtr<std::ostream> visualizer_output_; + DISALLOW_COPY_AND_ASSIGN(OptimizingCompiler); }; diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc index 7aaffcbed2..24ed4a3346 100644 --- a/compiler/dex/quick/gen_invoke.cc +++ b/compiler/dex/quick/gen_invoke.cc @@ -801,8 +801,10 @@ int Mir2Lir::LoadArgRegs(CallInfo* info, int call_state, const MethodReference& target_method, uint32_t vtable_idx, uintptr_t direct_code, uintptr_t direct_method, InvokeType type, bool skip_this) { - int last_arg_reg = TargetReg(kArg3).GetReg(); - int next_reg = TargetReg(kArg1).GetReg(); + int last_arg_reg = 3 - 1; + int arg_regs[3] = {TargetReg(kArg1).GetReg(), TargetReg(kArg2).GetReg(), TargetReg(kArg3).GetReg()}; + + int next_reg = 0; int next_arg = 0; if (skip_this) { next_reg++; @@ -811,8 +813,8 @@ int Mir2Lir::LoadArgRegs(CallInfo* info, int call_state, for (; (next_reg <= last_arg_reg) && (next_arg < info->num_arg_words); next_reg++) { RegLocation rl_arg = info->args[next_arg++]; rl_arg = UpdateRawLoc(rl_arg); - if (rl_arg.wide && (next_reg <= TargetReg(kArg2).GetReg())) { - RegStorage r_tmp(RegStorage::k64BitPair, next_reg, next_reg + 1); + if (rl_arg.wide && (next_reg <= last_arg_reg - 1)) { + RegStorage r_tmp(RegStorage::k64BitPair, arg_regs[next_reg], arg_regs[next_reg + 1]); LoadValueDirectWideFixed(rl_arg, r_tmp); next_reg++; next_arg++; @@ -821,7 +823,7 @@ int Mir2Lir::LoadArgRegs(CallInfo* info, int call_state, rl_arg = NarrowRegLoc(rl_arg); rl_arg.is_const = false; } - LoadValueDirectFixed(rl_arg, RegStorage::Solo32(next_reg)); + LoadValueDirectFixed(rl_arg, RegStorage::Solo32(arg_regs[next_reg])); } call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, direct_code, direct_method, type); @@ -1571,7 +1573,7 @@ bool Mir2Lir::GenInlinedUnsafeGet(CallInfo* info, RegLocation rl_offset = LoadValue(rl_src_offset, kCoreReg); RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); if (is_long) { - if (cu_->instruction_set == kX86) { + if (cu_->instruction_set == kX86 || cu_->instruction_set == kX86_64) { LoadBaseIndexedDisp(rl_object.reg, rl_offset.reg, 0, 0, rl_result.reg, k64); } else { RegStorage rl_temp_offset = AllocTemp(); @@ -1618,7 +1620,7 @@ bool Mir2Lir::GenInlinedUnsafePut(CallInfo* info, bool is_long, RegLocation rl_value; if (is_long) { rl_value = LoadValueWide(rl_src_value, kCoreReg); - if (cu_->instruction_set == kX86) { + if (cu_->instruction_set == kX86 || cu_->instruction_set == kX86_64) { StoreBaseIndexedDisp(rl_object.reg, rl_offset.reg, 0, 0, rl_value.reg, k64); } else { RegStorage rl_temp_offset = AllocTemp(); diff --git a/compiler/dex/quick/mir_to_lir.cc b/compiler/dex/quick/mir_to_lir.cc index 77119a4667..2c4ca8885a 100644 --- a/compiler/dex/quick/mir_to_lir.cc +++ b/compiler/dex/quick/mir_to_lir.cc @@ -37,7 +37,7 @@ void Mir2Lir::LockArg(int in_position, bool wide) { } // TODO: needs revisit for 64-bit. -RegStorage Mir2Lir::LoadArg(int in_position, bool wide) { +RegStorage Mir2Lir::LoadArg(int in_position, RegisterClass reg_class, bool wide) { RegStorage reg_arg_low = GetArgMappingToPhysicalReg(in_position); RegStorage reg_arg_high = wide ? GetArgMappingToPhysicalReg(in_position + 1) : RegStorage::InvalidReg(); @@ -56,28 +56,45 @@ RegStorage Mir2Lir::LoadArg(int in_position, bool wide) { if (wide && !reg_arg_high.Valid()) { // If the low part is not in a reg, we allocate a pair. Otherwise, we just load to high reg. if (!reg_arg_low.Valid()) { - RegStorage new_regs = AllocTypedTempWide(false, kAnyReg); - reg_arg_low = new_regs.GetLow(); - reg_arg_high = new_regs.GetHigh(); + RegStorage new_regs = AllocTypedTempWide(false, reg_class); LoadBaseDisp(TargetReg(kSp), offset, new_regs, k64); + return new_regs; // The reg_class is OK, we can return. } else { + // Assume that no ABI allows splitting a wide fp reg between a narrow fp reg and memory, + // i.e. the low part is in a core reg. Load the second part in a core reg as well for now. + DCHECK(!reg_arg_low.IsFloat()); reg_arg_high = AllocTemp(); int offset_high = offset + sizeof(uint32_t); Load32Disp(TargetReg(kSp), offset_high, reg_arg_high); + // Continue below to check the reg_class. } } // If the low part is not in a register yet, we need to load it. if (!reg_arg_low.Valid()) { - reg_arg_low = AllocTemp(); + // Assume that if the low part of a wide arg is passed in memory, so is the high part, + // thus we don't get here for wide args as it's handled above. Big-endian ABIs could + // conceivably break this assumption but Android supports only little-endian architectures. + DCHECK(!wide); + reg_arg_low = AllocTypedTemp(false, reg_class); Load32Disp(TargetReg(kSp), offset, reg_arg_low); + return reg_arg_low; // The reg_class is OK, we can return. } - if (wide) { - return RegStorage::MakeRegPair(reg_arg_low, reg_arg_high); - } else { - return reg_arg_low; + RegStorage reg_arg = wide ? RegStorage::MakeRegPair(reg_arg_low, reg_arg_high) : reg_arg_low; + // Check if we need to copy the arg to a different reg_class. + if (!RegClassMatches(reg_class, reg_arg)) { + if (wide) { + RegStorage new_regs = AllocTypedTempWide(false, reg_class); + OpRegCopyWide(new_regs, reg_arg); + reg_arg = new_regs; + } else { + RegStorage new_reg = AllocTypedTemp(false, reg_class); + OpRegCopy(new_reg, reg_arg); + reg_arg = new_reg; + } } + return reg_arg; } void Mir2Lir::LoadArgDirect(int in_position, RegLocation rl_dest) { @@ -138,16 +155,29 @@ bool Mir2Lir::GenSpecialIGet(MIR* mir, const InlineMethod& special) { // Point of no return - no aborts after this GenPrintLabel(mir); LockArg(data.object_arg); + RegStorage reg_obj = LoadArg(data.object_arg, kCoreReg); RegLocation rl_dest = wide ? GetReturnWide(double_or_float) : GetReturn(double_or_float); - RegStorage reg_obj = LoadArg(data.object_arg); + RegisterClass reg_class = RegClassForFieldLoadStore(size, data.is_volatile); + RegStorage r_result = rl_dest.reg; + if (!RegClassMatches(reg_class, r_result)) { + r_result = wide ? AllocTypedTempWide(rl_dest.fp, reg_class) + : AllocTypedTemp(rl_dest.fp, reg_class); + } if (data.is_volatile) { - LoadBaseDispVolatile(reg_obj, data.field_offset, rl_dest.reg, size); + LoadBaseDispVolatile(reg_obj, data.field_offset, r_result, size); // Without context sensitive analysis, we must issue the most conservative barriers. // In this case, either a load or store may follow so we issue both barriers. GenMemBarrier(kLoadLoad); GenMemBarrier(kLoadStore); } else { - LoadBaseDisp(reg_obj, data.field_offset, rl_dest.reg, size); + LoadBaseDisp(reg_obj, data.field_offset, r_result, size); + } + if (r_result != rl_dest.reg) { + if (wide) { + OpRegCopyWide(rl_dest.reg, r_result); + } else { + OpRegCopy(rl_dest.reg, r_result); + } } return true; } @@ -175,8 +205,9 @@ bool Mir2Lir::GenSpecialIPut(MIR* mir, const InlineMethod& special) { GenPrintLabel(mir); LockArg(data.object_arg); LockArg(data.src_arg, wide); - RegStorage reg_obj = LoadArg(data.object_arg); - RegStorage reg_src = LoadArg(data.src_arg, wide); + RegStorage reg_obj = LoadArg(data.object_arg, kCoreReg); + RegisterClass reg_class = RegClassForFieldLoadStore(size, data.is_volatile); + RegStorage reg_src = LoadArg(data.src_arg, reg_class, wide); if (data.is_volatile) { // There might have been a store before this volatile one so insert StoreStore barrier. GenMemBarrier(kStoreStore); diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h index 77e5649716..6a0f3b2a9e 100644 --- a/compiler/dex/quick/mir_to_lir.h +++ b/compiler/dex/quick/mir_to_lir.h @@ -1397,7 +1397,7 @@ class Mir2Lir : public Backend { * @param wide Whether the argument is 64-bit or not. * @return Returns the register (or register pair) for the loaded argument. */ - RegStorage LoadArg(int in_position, bool wide = false); + RegStorage LoadArg(int in_position, RegisterClass reg_class, bool wide = false); /** * @brief Used to load a VR argument directly to a specified register location. diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index b0aa63bb3e..2c2564d2ec 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -311,6 +311,10 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction, } } + if (return_type == Primitive::kPrimDouble || return_type == Primitive::kPrimFloat) { + return false; + } + DCHECK_EQ(argument_index, number_of_arguments); current_block_->AddInstruction(invoke); return true; diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index bbebd3af24..beafbcc386 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -47,7 +47,7 @@ void CodeGenerator::CompileBlock(HBasicBlock* block) { Bind(GetLabelOf(block)); HGraphVisitor* location_builder = GetLocationBuilder(); HGraphVisitor* instruction_visitor = GetInstructionVisitor(); - for (HInstructionIterator it(*block->GetInstructions()); !it.Done(); it.Advance()) { + for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { HInstruction* current = it.Current(); current->Accept(location_builder); InitLocations(current); diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc new file mode 100644 index 0000000000..a7604beac4 --- /dev/null +++ b/compiler/optimizing/graph_visualizer.cc @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2014 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 "graph_visualizer.h" + +#include "driver/dex_compilation_unit.h" +#include "nodes.h" + +namespace art { + +/** + * HGraph visitor to generate a file suitable for the c1visualizer tool and IRHydra. + */ +class HGraphVisualizerPrinter : public HGraphVisitor { + public: + HGraphVisualizerPrinter(HGraph* graph, std::ostream& output) + : HGraphVisitor(graph), output_(output), indent_(0) {} + + void StartTag(const char* name) { + AddIndent(); + output_ << "begin_" << name << std::endl; + indent_++; + } + + void EndTag(const char* name) { + indent_--; + AddIndent(); + output_ << "end_" << name << std::endl; + } + + void PrintProperty(const char* name, const char* property) { + AddIndent(); + output_ << name << " \"" << property << "\"" << std::endl; + } + + void PrintProperty(const char* name, const char* property, int id) { + AddIndent(); + output_ << name << " \"" << property << id << "\"" << std::endl; + } + + void PrintEmptyProperty(const char* name) { + AddIndent(); + output_ << name << std::endl; + } + + void PrintTime(const char* name) { + AddIndent(); + output_ << name << " " << time(NULL) << std::endl; + } + + void PrintInt(const char* name, int value) { + AddIndent(); + output_ << name << " " << value << std::endl; + } + + void AddIndent() { + for (size_t i = 0; i < indent_; ++i) { + output_ << " "; + } + } + + void PrintPredecessors(HBasicBlock* block) { + AddIndent(); + output_ << "predecessors"; + for (size_t i = 0, e = block->GetPredecessors().Size(); i < e; ++i) { + HBasicBlock* predecessor = block->GetPredecessors().Get(i); + output_ << " \"B" << predecessor->GetBlockId() << "\" "; + } + output_<< std::endl; + } + + void PrintSuccessors(HBasicBlock* block) { + AddIndent(); + output_ << "successors"; + for (size_t i = 0, e = block->GetSuccessors().Size(); i < e; ++i) { + HBasicBlock* successor = block->GetSuccessors().Get(i); + output_ << " \"B" << successor->GetBlockId() << "\" "; + } + output_<< std::endl; + } + + + void VisitInstruction(HInstruction* instruction) { + output_ << instruction->DebugName(); + if (instruction->InputCount() > 0) { + output_ << " [ "; + for (HInputIterator inputs(instruction); !inputs.Done(); inputs.Advance()) { + output_ << "v" << inputs.Current()->GetId() << " "; + } + output_ << "]"; + } + } + + void PrintInstructions(const HInstructionList& list) { + const char* kEndInstructionMarker = "<|@"; + for (HInstructionIterator it(list); !it.Done(); it.Advance()) { + HInstruction* instruction = it.Current(); + AddIndent(); + int bci = 0; + output_ << bci << " " << instruction->NumberOfUses() << " v" << instruction->GetId() << " "; + instruction->Accept(this); + output_ << kEndInstructionMarker << std::endl; + } + } + + void Run(const char* pass_name) { + StartTag("cfg"); + PrintProperty("name", pass_name); + VisitInsertionOrder(); + EndTag("cfg"); + } + + void VisitBasicBlock(HBasicBlock* block) { + StartTag("block"); + PrintProperty("name", "B", block->GetBlockId()); + PrintInt("from_bci", -1); + PrintInt("to_bci", -1); + PrintPredecessors(block); + PrintSuccessors(block); + PrintEmptyProperty("xhandlers"); + PrintEmptyProperty("flags"); + if (block->GetDominator() != nullptr) { + PrintProperty("dominator", "B", block->GetDominator()->GetBlockId()); + } + + StartTag("states"); + StartTag("locals"); + PrintInt("size", 0); + PrintProperty("method", "None"); + for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) { + AddIndent(); + HInstruction* instruction = it.Current(); + output_ << instruction->GetId() << " v" << instruction->GetId() << "[ "; + for (HInputIterator inputs(instruction); !inputs.Done(); inputs.Advance()) { + output_ << inputs.Current()->GetId() << " "; + } + output_ << "]" << std::endl; + } + EndTag("locals"); + EndTag("states"); + + StartTag("HIR"); + PrintInstructions(block->GetPhis()); + PrintInstructions(block->GetInstructions()); + EndTag("HIR"); + EndTag("block"); + } + + private: + std::ostream& output_; + size_t indent_; + + DISALLOW_COPY_AND_ASSIGN(HGraphVisualizerPrinter); +}; + +HGraphVisualizer::HGraphVisualizer(std::ostream* output, + HGraph* graph, + const char* string_filter, + const DexCompilationUnit& cu) + : output_(output), graph_(graph), is_enabled_(false) { + if (output == nullptr) { + return; + } + std::string pretty_name = PrettyMethod(cu.GetDexMethodIndex(), *cu.GetDexFile()); + if (pretty_name.find(string_filter) == std::string::npos) { + return; + } + + is_enabled_ = true; + HGraphVisualizerPrinter printer(graph, *output_); + printer.StartTag("compilation"); + printer.PrintProperty("name", pretty_name.c_str()); + printer.PrintProperty("method", pretty_name.c_str()); + printer.PrintTime("date"); + printer.EndTag("compilation"); +} + +void HGraphVisualizer::DumpGraph(const char* pass_name) { + if (!is_enabled_) { + return; + } + HGraphVisualizerPrinter printer(graph_, *output_); + printer.Run(pass_name); +} + +} // namespace art diff --git a/compiler/optimizing/graph_visualizer.h b/compiler/optimizing/graph_visualizer.h new file mode 100644 index 0000000000..433d55d421 --- /dev/null +++ b/compiler/optimizing/graph_visualizer.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2014 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_COMPILER_OPTIMIZING_GRAPH_VISUALIZER_H_ +#define ART_COMPILER_OPTIMIZING_GRAPH_VISUALIZER_H_ + +#include "utils/allocation.h" + +namespace art { + +class DexCompilationUnit; +class HGraph; + +/** + * If enabled, emits compilation information suitable for the c1visualizer tool + * and IRHydra. + * Currently only works if the compiler is single threaded. + */ +class HGraphVisualizer : public ValueObject { + public: + /** + * If output is not null, and the method name of the dex compilation + * unit contains `string_filter`, the compilation information will be + * emitted. + */ + HGraphVisualizer(std::ostream* output, + HGraph* graph, + const char* string_filter, + const DexCompilationUnit& cu); + + /** + * If this visualizer is enabled, emit the compilation information + * in `output_`. + */ + void DumpGraph(const char* pass_name); + + private: + std::ostream* const output_; + HGraph* const graph_; + + // Is true when `output_` is not null, and the compiled method's name + // contains the string_filter given in the constructor. + bool is_enabled_; + + DISALLOW_COPY_AND_ASSIGN(HGraphVisualizer); +}; + +} // namespace art + +#endif // ART_COMPILER_OPTIMIZING_GRAPH_VISUALIZER_H_ diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index cf2d1eec23..afaedd7f18 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -37,10 +37,10 @@ void HGraph::RemoveDeadBlocks(const ArenaBitVector& visited) const { for (size_t j = 0; j < block->GetSuccessors().Size(); ++j) { block->GetSuccessors().Get(j)->RemovePredecessor(block, false); } - for (HInstructionIterator it(*block->GetPhis()); !it.Done(); it.Advance()) { + for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) { block->RemovePhi(it.Current()->AsPhi()); } - for (HInstructionIterator it(*block->GetInstructions()); !it.Done(); it.Advance()) { + for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { block->RemoveInstruction(it.Current()); } } @@ -420,10 +420,10 @@ void HGraphVisitor::VisitInsertionOrder() { } void HGraphVisitor::VisitBasicBlock(HBasicBlock* block) { - for (HInstructionIterator it(*block->GetPhis()); !it.Done(); it.Advance()) { + for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) { it.Current()->Accept(this); } - for (HInstructionIterator it(*block->GetInstructions()); !it.Done(); it.Advance()) { + for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { it.Current()->Accept(this); } } diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 081c2bd08a..27b87ca0da 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -82,7 +82,7 @@ class HGraph : public ArenaObject { void SimplifyCFG(); // Find all natural loops in this graph. Aborts computation and returns false - // if one loop is not natural, that is the header does not dominated the back + // if one loop is not natural, that is the header does not dominate the back // edge. bool FindNaturalLoops() const; @@ -268,8 +268,8 @@ class HBasicBlock : public ArenaObject { HInstruction* GetFirstInstruction() const { return instructions_.first_instruction_; } HInstruction* GetLastInstruction() const { return instructions_.last_instruction_; } - HInstructionList const* GetInstructions() const { return &instructions_; } - HInstructionList const* GetPhis() const { return &phis_; } + const HInstructionList& GetInstructions() const { return instructions_; } + const HInstructionList& GetPhis() const { return phis_; } void AddSuccessor(HBasicBlock* block) { successors_.Add(block); @@ -444,6 +444,17 @@ class HInstruction : public ArenaObject { bool HasUses() const { return uses_ != nullptr || env_uses_ != nullptr; } + size_t NumberOfUses() const { + // TODO: Optimize this method if it is used outside of the HGraphTracer. + size_t result = 0; + HUseListNode<HInstruction>* current = uses_; + while (current != nullptr) { + current = current->GetTail(); + ++result; + } + return result; + } + int GetId() const { return id_; } void SetId(int id) { id_ = id; } diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index a5031e0a7c..f435cb0058 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -14,6 +14,7 @@ * limitations under the License. */ +#include <fstream> #include <stdint.h> #include "builder.h" @@ -21,6 +22,7 @@ #include "compilers.h" #include "driver/compiler_driver.h" #include "driver/dex_compilation_unit.h" +#include "graph_visualizer.h" #include "nodes.h" #include "ssa_liveness_analysis.h" #include "utils/arena_allocator.h" @@ -50,6 +52,22 @@ class CodeVectorAllocator FINAL : public CodeAllocator { DISALLOW_COPY_AND_ASSIGN(CodeVectorAllocator); }; +/** + * If set to true, generates a file suitable for the c1visualizer tool and IRHydra. + */ +static bool kIsVisualizerEnabled = false; + +/** + * Filter to apply to the visualizer. Methods whose name contain that filter will + * be in the file. + */ +static const char* kStringFilter = ""; + +OptimizingCompiler::OptimizingCompiler(CompilerDriver* driver) : QuickCompiler(driver) { + if (kIsVisualizerEnabled) { + visualizer_output_.reset(new std::ofstream("art.cfg")); + } +} CompiledMethod* OptimizingCompiler::TryCompile(const DexFile::CodeItem* code_item, uint32_t access_flags, @@ -70,6 +88,7 @@ CompiledMethod* OptimizingCompiler::TryCompile(const DexFile::CodeItem* code_ite ArenaPool pool; ArenaAllocator arena(&pool); HGraphBuilder builder(&arena, &dex_compilation_unit, &dex_file); + HGraph* graph = builder.BuildGraph(*code_item); if (graph == nullptr) { if (shouldCompile) { @@ -77,6 +96,8 @@ CompiledMethod* OptimizingCompiler::TryCompile(const DexFile::CodeItem* code_ite } return nullptr; } + HGraphVisualizer visualizer(visualizer_output_.get(), graph, kStringFilter, dex_compilation_unit); + visualizer.DumpGraph("builder"); InstructionSet instruction_set = GetCompilerDriver()->GetInstructionSet(); // The optimizing compiler currently does not have a Thumb2 assembler. @@ -104,6 +125,8 @@ CompiledMethod* OptimizingCompiler::TryCompile(const DexFile::CodeItem* code_ite // Run these phases to get some test coverage. graph->BuildDominatorTree(); graph->TransformToSSA(); + visualizer.DumpGraph("ssa"); + graph->FindNaturalLoops(); SsaLivenessAnalysis(*graph).Analyze(); diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc index 1fc041c8c9..50e3254d7c 100644 --- a/compiler/optimizing/ssa_builder.cc +++ b/compiler/optimizing/ssa_builder.cc @@ -30,7 +30,7 @@ void SsaBuilder::BuildSsa() { // 2) Set inputs of loop phis. for (size_t i = 0; i < loop_headers_.Size(); i++) { HBasicBlock* block = loop_headers_.Get(i); - for (HInstructionIterator it(*block->GetPhis()); !it.Done(); it.Advance()) { + for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) { HPhi* phi = it.Current()->AsPhi(); for (size_t pred = 0; pred < block->GetPredecessors().Size(); pred++) { phi->AddInput(ValueOfLocal(block->GetPredecessors().Get(pred), phi->GetRegNumber())); @@ -40,7 +40,7 @@ void SsaBuilder::BuildSsa() { // 3) Clear locals. // TODO: Move this to a dead code eliminator phase. - for (HInstructionIterator it(*GetGraph()->GetEntryBlock()->GetInstructions()); + for (HInstructionIterator it(GetGraph()->GetEntryBlock()->GetInstructions()); !it.Done(); it.Advance()) { HInstruction* current = it.Current(); @@ -106,7 +106,7 @@ void SsaBuilder::VisitBasicBlock(HBasicBlock* block) { // - HStoreLocal: update current value of the local and remove the instruction. // - Instructions that require an environment: populate their environment // with the current values of the locals. - for (HInstructionIterator it(*block->GetInstructions()); !it.Done(); it.Advance()) { + for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { it.Current()->Accept(this); } } diff --git a/compiler/optimizing/ssa_liveness_analysis.cc b/compiler/optimizing/ssa_liveness_analysis.cc index 0ab77ca2ef..7c2ec3966e 100644 --- a/compiler/optimizing/ssa_liveness_analysis.cc +++ b/compiler/optimizing/ssa_liveness_analysis.cc @@ -29,14 +29,14 @@ void SsaLivenessAnalysis::NumberInstructions() { for (HReversePostOrderIterator it(graph_); !it.Done(); it.Advance()) { HBasicBlock* block = it.Current(); - for (HInstructionIterator it(*block->GetPhis()); !it.Done(); it.Advance()) { + for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) { HInstruction* current = it.Current(); if (current->HasUses()) { current->SetSsaIndex(ssa_index++); } } - for (HInstructionIterator it(*block->GetInstructions()); !it.Done(); it.Advance()) { + for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { HInstruction* current = it.Current(); if (current->HasUses()) { current->SetSsaIndex(ssa_index++); @@ -73,7 +73,7 @@ void SsaLivenessAnalysis::ComputeInitialSets() { BitVector* kill = GetKillSet(*block); BitVector* live_in = GetLiveInSet(*block); - for (HBackwardInstructionIterator it(*block->GetInstructions()); !it.Done(); it.Advance()) { + for (HBackwardInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { HInstruction* current = it.Current(); if (current->HasSsaIndex()) { kill->SetBit(current->GetSsaIndex()); @@ -99,7 +99,7 @@ void SsaLivenessAnalysis::ComputeInitialSets() { } } - for (HInstructionIterator it(*block->GetPhis()); !it.Done(); it.Advance()) { + for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) { HInstruction* current = it.Current(); if (current->HasSsaIndex()) { kill->SetBit(current->GetSsaIndex()); diff --git a/compiler/optimizing/ssa_test.cc b/compiler/optimizing/ssa_test.cc index 9be2197ad7..415d14659e 100644 --- a/compiler/optimizing/ssa_test.cc +++ b/compiler/optimizing/ssa_test.cc @@ -66,10 +66,10 @@ static void ReNumberInstructions(HGraph* graph) { int id = 0; for (size_t i = 0, e = graph->GetBlocks().Size(); i < e; ++i) { HBasicBlock* block = graph->GetBlocks().Get(i); - for (HInstructionIterator it(*block->GetPhis()); !it.Done(); it.Advance()) { + for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) { it.Current()->SetId(id++); } - for (HInstructionIterator it(*block->GetInstructions()); !it.Done(); it.Advance()) { + for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { it.Current()->SetId(id++); } } diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 7d7024a517..ad796f8e10 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -958,9 +958,7 @@ static int dex2oat(int argc, char** argv) { bool image = (!image_filename.empty()); if (!image && boot_image_filename.empty()) { boot_image_filename += GetAndroidRoot(); - boot_image_filename += "/framework/boot-"; - boot_image_filename += GetInstructionSetString(instruction_set); - boot_image_filename += ".art"; + boot_image_filename += "/framework/boot.art"; } std::string boot_image_option; if (!boot_image_filename.empty()) { diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc index 0d5a805c8d..cfd0c007f1 100644 --- a/runtime/check_jni.cc +++ b/runtime/check_jni.cc @@ -179,7 +179,7 @@ class ScopedCheck { // times, so using "java.lang.Thread" instead of "java/lang/Thread" might work in some // circumstances, but this is incorrect. void CheckClassName(const char* class_name) { - if (!IsValidJniClassName(class_name)) { + if ((class_name == nullptr) || !IsValidJniClassName(class_name)) { JniAbortF(function_name_, "illegal class name '%s'\n" " (should be of the form 'package/Class', [Lpackage/Class;' or '[[B')", diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 3d268e4538..9034560689 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -2215,7 +2215,10 @@ mirror::Class* ClassLinker::CreateArrayClass(Thread* self, const char* descripto DCHECK(self->IsExceptionPending()); return nullptr; } - + if (UNLIKELY(component_type->IsPrimitiveVoid())) { + ThrowNoClassDefFoundError("Attempt to create array of void primitive type"); + return nullptr; + } // See if the component type is already loaded. Array classes are // always associated with the class loader of their underlying // element type -- an array of Strings goes with the loader for diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index 46349712ac..21dab8d001 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -518,16 +518,28 @@ class Libraries { SafeMap<std::string, SharedLibrary*> libraries_; }; -#define CHECK_NON_NULL_ARGUMENT(value) CHECK_NON_NULL_ARGUMENT_FN_NAME(__FUNCTION__, value) +#define CHECK_NON_NULL_ARGUMENT(value) \ + CHECK_NON_NULL_ARGUMENT_FN_NAME(__FUNCTION__, value, nullptr) -#define CHECK_NON_NULL_ARGUMENT_FN_NAME(name, value) \ +#define CHECK_NON_NULL_ARGUMENT_RETURN_VOID(value) \ + CHECK_NON_NULL_ARGUMENT_FN_NAME(__FUNCTION__, value, ) + +#define CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(value) \ + CHECK_NON_NULL_ARGUMENT_FN_NAME(__FUNCTION__, value, 0) + +#define CHECK_NON_NULL_ARGUMENT_RETURN(value, return_val) \ + CHECK_NON_NULL_ARGUMENT_FN_NAME(__FUNCTION__, value, return_val) + +#define CHECK_NON_NULL_ARGUMENT_FN_NAME(name, value, return_val) \ if (UNLIKELY(value == nullptr)) { \ JniAbortF(name, #value " == null"); \ + return return_val; \ } #define CHECK_NON_NULL_MEMCPY_ARGUMENT(length, value) \ if (UNLIKELY(length != 0 && value == nullptr)) { \ JniAbortF(__FUNCTION__, #value " == null"); \ + return; \ } class JNI { @@ -614,8 +626,8 @@ class JNI { } static jboolean IsAssignableFrom(JNIEnv* env, jclass java_class1, jclass java_class2) { - CHECK_NON_NULL_ARGUMENT(java_class1); - CHECK_NON_NULL_ARGUMENT(java_class2); + CHECK_NON_NULL_ARGUMENT_RETURN(java_class1, JNI_FALSE); + CHECK_NON_NULL_ARGUMENT_RETURN(java_class2, JNI_FALSE); ScopedObjectAccess soa(env); mirror::Class* c1 = soa.Decode<mirror::Class*>(java_class1); mirror::Class* c2 = soa.Decode<mirror::Class*>(java_class2); @@ -623,7 +635,7 @@ class JNI { } static jboolean IsInstanceOf(JNIEnv* env, jobject jobj, jclass java_class) { - CHECK_NON_NULL_ARGUMENT(java_class); + CHECK_NON_NULL_ARGUMENT_RETURN(java_class, JNI_FALSE); if (jobj == nullptr) { // Note: JNI is different from regular Java instanceof in this respect return JNI_TRUE; @@ -647,7 +659,7 @@ class JNI { } static jint ThrowNew(JNIEnv* env, jclass c, const char* msg) { - CHECK_NON_NULL_ARGUMENT(c); + CHECK_NON_NULL_ARGUMENT_RETURN(c, JNI_ERR); return ThrowNewException(env, c, msg, nullptr); } @@ -914,8 +926,8 @@ class JNI { static jboolean CallBooleanMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) { va_list ap; va_start(ap, mid); - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); JValue result(InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, ap)); va_end(ap); @@ -923,15 +935,15 @@ class JNI { } static jboolean CallBooleanMethodV(JNIEnv* env, jobject obj, jmethodID mid, va_list args) { - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, args).GetZ(); } static jboolean CallBooleanMethodA(JNIEnv* env, jobject obj, jmethodID mid, jvalue* args) { - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeVirtualOrInterfaceWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args).GetZ(); @@ -940,8 +952,8 @@ class JNI { static jbyte CallByteMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) { va_list ap; va_start(ap, mid); - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); JValue result(InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, ap)); va_end(ap); @@ -949,15 +961,15 @@ class JNI { } static jbyte CallByteMethodV(JNIEnv* env, jobject obj, jmethodID mid, va_list args) { - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, args).GetB(); } static jbyte CallByteMethodA(JNIEnv* env, jobject obj, jmethodID mid, jvalue* args) { - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeVirtualOrInterfaceWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args).GetB(); @@ -966,8 +978,8 @@ class JNI { static jchar CallCharMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) { va_list ap; va_start(ap, mid); - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); JValue result(InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, ap)); va_end(ap); @@ -975,15 +987,15 @@ class JNI { } static jchar CallCharMethodV(JNIEnv* env, jobject obj, jmethodID mid, va_list args) { - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, args).GetC(); } static jchar CallCharMethodA(JNIEnv* env, jobject obj, jmethodID mid, jvalue* args) { - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeVirtualOrInterfaceWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args).GetC(); @@ -992,8 +1004,8 @@ class JNI { static jdouble CallDoubleMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) { va_list ap; va_start(ap, mid); - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); JValue result(InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, ap)); va_end(ap); @@ -1001,15 +1013,15 @@ class JNI { } static jdouble CallDoubleMethodV(JNIEnv* env, jobject obj, jmethodID mid, va_list args) { - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, args).GetD(); } static jdouble CallDoubleMethodA(JNIEnv* env, jobject obj, jmethodID mid, jvalue* args) { - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeVirtualOrInterfaceWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args).GetD(); @@ -1018,8 +1030,8 @@ class JNI { static jfloat CallFloatMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) { va_list ap; va_start(ap, mid); - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); JValue result(InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, ap)); va_end(ap); @@ -1027,15 +1039,15 @@ class JNI { } static jfloat CallFloatMethodV(JNIEnv* env, jobject obj, jmethodID mid, va_list args) { - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, args).GetF(); } static jfloat CallFloatMethodA(JNIEnv* env, jobject obj, jmethodID mid, jvalue* args) { - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeVirtualOrInterfaceWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args).GetF(); @@ -1044,8 +1056,8 @@ class JNI { static jint CallIntMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) { va_list ap; va_start(ap, mid); - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); JValue result(InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, ap)); va_end(ap); @@ -1053,15 +1065,15 @@ class JNI { } static jint CallIntMethodV(JNIEnv* env, jobject obj, jmethodID mid, va_list args) { - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, args).GetI(); } static jint CallIntMethodA(JNIEnv* env, jobject obj, jmethodID mid, jvalue* args) { - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeVirtualOrInterfaceWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args).GetI(); @@ -1070,8 +1082,8 @@ class JNI { static jlong CallLongMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) { va_list ap; va_start(ap, mid); - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); JValue result(InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, ap)); va_end(ap); @@ -1079,15 +1091,15 @@ class JNI { } static jlong CallLongMethodV(JNIEnv* env, jobject obj, jmethodID mid, va_list args) { - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, args).GetJ(); } static jlong CallLongMethodA(JNIEnv* env, jobject obj, jmethodID mid, jvalue* args) { - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeVirtualOrInterfaceWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args).GetJ(); @@ -1096,8 +1108,8 @@ class JNI { static jshort CallShortMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) { va_list ap; va_start(ap, mid); - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); JValue result(InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, ap)); va_end(ap); @@ -1105,15 +1117,15 @@ class JNI { } static jshort CallShortMethodV(JNIEnv* env, jobject obj, jmethodID mid, va_list args) { - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, args).GetS(); } static jshort CallShortMethodA(JNIEnv* env, jobject obj, jmethodID mid, jvalue* args) { - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeVirtualOrInterfaceWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args).GetS(); @@ -1122,23 +1134,23 @@ class JNI { static void CallVoidMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) { va_list ap; va_start(ap, mid); - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(mid); ScopedObjectAccess soa(env); InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, ap); va_end(ap); } static void CallVoidMethodV(JNIEnv* env, jobject obj, jmethodID mid, va_list args) { - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(mid); ScopedObjectAccess soa(env); InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, args); } static void CallVoidMethodA(JNIEnv* env, jobject obj, jmethodID mid, jvalue* args) { - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(mid); ScopedObjectAccess soa(env); InvokeVirtualOrInterfaceWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args); } @@ -1177,8 +1189,8 @@ class JNI { ...) { va_list ap; va_start(ap, mid); - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); JValue result(InvokeWithVarArgs(soa, obj, mid, ap)); va_end(ap); @@ -1187,16 +1199,16 @@ class JNI { static jboolean CallNonvirtualBooleanMethodV(JNIEnv* env, jobject obj, jclass, jmethodID mid, va_list args) { - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeWithVarArgs(soa, obj, mid, args).GetZ(); } static jboolean CallNonvirtualBooleanMethodA(JNIEnv* env, jobject obj, jclass, jmethodID mid, jvalue* args) { - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args).GetZ(); } @@ -1204,8 +1216,8 @@ class JNI { static jbyte CallNonvirtualByteMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) { va_list ap; va_start(ap, mid); - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); JValue result(InvokeWithVarArgs(soa, obj, mid, ap)); va_end(ap); @@ -1214,16 +1226,16 @@ class JNI { static jbyte CallNonvirtualByteMethodV(JNIEnv* env, jobject obj, jclass, jmethodID mid, va_list args) { - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeWithVarArgs(soa, obj, mid, args).GetB(); } static jbyte CallNonvirtualByteMethodA(JNIEnv* env, jobject obj, jclass, jmethodID mid, jvalue* args) { - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args).GetB(); } @@ -1231,8 +1243,8 @@ class JNI { static jchar CallNonvirtualCharMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) { va_list ap; va_start(ap, mid); - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); JValue result(InvokeWithVarArgs(soa, obj, mid, ap)); va_end(ap); @@ -1241,16 +1253,16 @@ class JNI { static jchar CallNonvirtualCharMethodV(JNIEnv* env, jobject obj, jclass, jmethodID mid, va_list args) { - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeWithVarArgs(soa, obj, mid, args).GetC(); } static jchar CallNonvirtualCharMethodA(JNIEnv* env, jobject obj, jclass, jmethodID mid, jvalue* args) { - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args).GetC(); } @@ -1258,8 +1270,8 @@ class JNI { static jshort CallNonvirtualShortMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) { va_list ap; va_start(ap, mid); - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); JValue result(InvokeWithVarArgs(soa, obj, mid, ap)); va_end(ap); @@ -1268,16 +1280,16 @@ class JNI { static jshort CallNonvirtualShortMethodV(JNIEnv* env, jobject obj, jclass, jmethodID mid, va_list args) { - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeWithVarArgs(soa, obj, mid, args).GetS(); } static jshort CallNonvirtualShortMethodA(JNIEnv* env, jobject obj, jclass, jmethodID mid, jvalue* args) { - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args).GetS(); } @@ -1285,8 +1297,8 @@ class JNI { static jint CallNonvirtualIntMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) { va_list ap; va_start(ap, mid); - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); JValue result(InvokeWithVarArgs(soa, obj, mid, ap)); va_end(ap); @@ -1295,16 +1307,16 @@ class JNI { static jint CallNonvirtualIntMethodV(JNIEnv* env, jobject obj, jclass, jmethodID mid, va_list args) { - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeWithVarArgs(soa, obj, mid, args).GetI(); } static jint CallNonvirtualIntMethodA(JNIEnv* env, jobject obj, jclass, jmethodID mid, jvalue* args) { - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args).GetI(); } @@ -1312,8 +1324,8 @@ class JNI { static jlong CallNonvirtualLongMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) { va_list ap; va_start(ap, mid); - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); JValue result(InvokeWithVarArgs(soa, obj, mid, ap)); va_end(ap); @@ -1322,16 +1334,16 @@ class JNI { static jlong CallNonvirtualLongMethodV(JNIEnv* env, jobject obj, jclass, jmethodID mid, va_list args) { - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeWithVarArgs(soa, obj, mid, args).GetJ(); } static jlong CallNonvirtualLongMethodA(JNIEnv* env, jobject obj, jclass, jmethodID mid, jvalue* args) { - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args).GetJ(); } @@ -1339,8 +1351,8 @@ class JNI { static jfloat CallNonvirtualFloatMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) { va_list ap; va_start(ap, mid); - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); JValue result(InvokeWithVarArgs(soa, obj, mid, ap)); va_end(ap); @@ -1349,16 +1361,16 @@ class JNI { static jfloat CallNonvirtualFloatMethodV(JNIEnv* env, jobject obj, jclass, jmethodID mid, va_list args) { - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeWithVarArgs(soa, obj, mid, args).GetF(); } static jfloat CallNonvirtualFloatMethodA(JNIEnv* env, jobject obj, jclass, jmethodID mid, jvalue* args) { - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args).GetF(); } @@ -1366,8 +1378,8 @@ class JNI { static jdouble CallNonvirtualDoubleMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) { va_list ap; va_start(ap, mid); - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); JValue result(InvokeWithVarArgs(soa, obj, mid, ap)); va_end(ap); @@ -1376,16 +1388,16 @@ class JNI { static jdouble CallNonvirtualDoubleMethodV(JNIEnv* env, jobject obj, jclass, jmethodID mid, va_list args) { - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeWithVarArgs(soa, obj, mid, args).GetD(); } static jdouble CallNonvirtualDoubleMethodA(JNIEnv* env, jobject obj, jclass, jmethodID mid, jvalue* args) { - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args).GetD(); } @@ -1393,8 +1405,8 @@ class JNI { static void CallNonvirtualVoidMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) { va_list ap; va_start(ap, mid); - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(mid); ScopedObjectAccess soa(env); InvokeWithVarArgs(soa, obj, mid, ap); va_end(ap); @@ -1402,16 +1414,16 @@ class JNI { static void CallNonvirtualVoidMethodV(JNIEnv* env, jobject obj, jclass, jmethodID mid, va_list args) { - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(mid); ScopedObjectAccess soa(env); InvokeWithVarArgs(soa, obj, mid, args); } static void CallNonvirtualVoidMethodA(JNIEnv* env, jobject obj, jclass, jmethodID mid, jvalue* args) { - CHECK_NON_NULL_ARGUMENT(obj); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(mid); ScopedObjectAccess soa(env); InvokeWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args); } @@ -1450,8 +1462,8 @@ class JNI { } static void SetObjectField(JNIEnv* env, jobject java_object, jfieldID fid, jobject java_value) { - CHECK_NON_NULL_ARGUMENT(java_object); - CHECK_NON_NULL_ARGUMENT(fid); + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(java_object); + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(fid); ScopedObjectAccess soa(env); mirror::Object* o = soa.Decode<mirror::Object*>(java_object); mirror::Object* v = soa.Decode<mirror::Object*>(java_value); @@ -1460,7 +1472,7 @@ class JNI { } static void SetStaticObjectField(JNIEnv* env, jclass, jfieldID fid, jobject java_value) { - CHECK_NON_NULL_ARGUMENT(fid); + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(fid); ScopedObjectAccess soa(env); mirror::Object* v = soa.Decode<mirror::Object*>(java_value); mirror::ArtField* f = soa.DecodeField(fid); @@ -1468,29 +1480,29 @@ class JNI { } #define GET_PRIMITIVE_FIELD(fn, instance) \ - CHECK_NON_NULL_ARGUMENT(instance); \ - CHECK_NON_NULL_ARGUMENT(fid); \ + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(instance); \ + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(fid); \ ScopedObjectAccess soa(env); \ mirror::Object* o = soa.Decode<mirror::Object*>(instance); \ mirror::ArtField* f = soa.DecodeField(fid); \ return f->Get ##fn (o) #define GET_STATIC_PRIMITIVE_FIELD(fn) \ - CHECK_NON_NULL_ARGUMENT(fid); \ + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(fid); \ ScopedObjectAccess soa(env); \ mirror::ArtField* f = soa.DecodeField(fid); \ return f->Get ##fn (f->GetDeclaringClass()) #define SET_PRIMITIVE_FIELD(fn, instance, value) \ - CHECK_NON_NULL_ARGUMENT(instance); \ - CHECK_NON_NULL_ARGUMENT(fid); \ + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(instance); \ + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(fid); \ ScopedObjectAccess soa(env); \ mirror::Object* o = soa.Decode<mirror::Object*>(instance); \ mirror::ArtField* f = soa.DecodeField(fid); \ f->Set ##fn <false>(o, value) #define SET_STATIC_PRIMITIVE_FIELD(fn, value) \ - CHECK_NON_NULL_ARGUMENT(fid); \ + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(fid); \ ScopedObjectAccess soa(env); \ mirror::ArtField* f = soa.DecodeField(fid); \ f->Set ##fn <false>(f->GetDeclaringClass(), value) @@ -1651,7 +1663,7 @@ class JNI { static jboolean CallStaticBooleanMethod(JNIEnv* env, jclass, jmethodID mid, ...) { va_list ap; va_start(ap, mid); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); JValue result(InvokeWithVarArgs(soa, nullptr, mid, ap)); va_end(ap); @@ -1659,13 +1671,13 @@ class JNI { } static jboolean CallStaticBooleanMethodV(JNIEnv* env, jclass, jmethodID mid, va_list args) { - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeWithVarArgs(soa, nullptr, mid, args).GetZ(); } static jboolean CallStaticBooleanMethodA(JNIEnv* env, jclass, jmethodID mid, jvalue* args) { - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeWithJValues(soa, nullptr, mid, args).GetZ(); } @@ -1673,7 +1685,7 @@ class JNI { static jbyte CallStaticByteMethod(JNIEnv* env, jclass, jmethodID mid, ...) { va_list ap; va_start(ap, mid); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); JValue result(InvokeWithVarArgs(soa, nullptr, mid, ap)); va_end(ap); @@ -1681,13 +1693,13 @@ class JNI { } static jbyte CallStaticByteMethodV(JNIEnv* env, jclass, jmethodID mid, va_list args) { - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeWithVarArgs(soa, nullptr, mid, args).GetB(); } static jbyte CallStaticByteMethodA(JNIEnv* env, jclass, jmethodID mid, jvalue* args) { - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeWithJValues(soa, nullptr, mid, args).GetB(); } @@ -1695,7 +1707,7 @@ class JNI { static jchar CallStaticCharMethod(JNIEnv* env, jclass, jmethodID mid, ...) { va_list ap; va_start(ap, mid); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); JValue result(InvokeWithVarArgs(soa, nullptr, mid, ap)); va_end(ap); @@ -1703,13 +1715,13 @@ class JNI { } static jchar CallStaticCharMethodV(JNIEnv* env, jclass, jmethodID mid, va_list args) { - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeWithVarArgs(soa, nullptr, mid, args).GetC(); } static jchar CallStaticCharMethodA(JNIEnv* env, jclass, jmethodID mid, jvalue* args) { - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeWithJValues(soa, nullptr, mid, args).GetC(); } @@ -1717,7 +1729,7 @@ class JNI { static jshort CallStaticShortMethod(JNIEnv* env, jclass, jmethodID mid, ...) { va_list ap; va_start(ap, mid); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); JValue result(InvokeWithVarArgs(soa, nullptr, mid, ap)); va_end(ap); @@ -1725,13 +1737,13 @@ class JNI { } static jshort CallStaticShortMethodV(JNIEnv* env, jclass, jmethodID mid, va_list args) { - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeWithVarArgs(soa, nullptr, mid, args).GetS(); } static jshort CallStaticShortMethodA(JNIEnv* env, jclass, jmethodID mid, jvalue* args) { - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeWithJValues(soa, nullptr, mid, args).GetS(); } @@ -1739,7 +1751,7 @@ class JNI { static jint CallStaticIntMethod(JNIEnv* env, jclass, jmethodID mid, ...) { va_list ap; va_start(ap, mid); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); JValue result(InvokeWithVarArgs(soa, nullptr, mid, ap)); va_end(ap); @@ -1747,13 +1759,13 @@ class JNI { } static jint CallStaticIntMethodV(JNIEnv* env, jclass, jmethodID mid, va_list args) { - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeWithVarArgs(soa, nullptr, mid, args).GetI(); } static jint CallStaticIntMethodA(JNIEnv* env, jclass, jmethodID mid, jvalue* args) { - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeWithJValues(soa, nullptr, mid, args).GetI(); } @@ -1761,7 +1773,7 @@ class JNI { static jlong CallStaticLongMethod(JNIEnv* env, jclass, jmethodID mid, ...) { va_list ap; va_start(ap, mid); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); JValue result(InvokeWithVarArgs(soa, nullptr, mid, ap)); va_end(ap); @@ -1769,13 +1781,13 @@ class JNI { } static jlong CallStaticLongMethodV(JNIEnv* env, jclass, jmethodID mid, va_list args) { - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeWithVarArgs(soa, nullptr, mid, args).GetJ(); } static jlong CallStaticLongMethodA(JNIEnv* env, jclass, jmethodID mid, jvalue* args) { - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeWithJValues(soa, nullptr, mid, args).GetJ(); } @@ -1783,7 +1795,7 @@ class JNI { static jfloat CallStaticFloatMethod(JNIEnv* env, jclass, jmethodID mid, ...) { va_list ap; va_start(ap, mid); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); JValue result(InvokeWithVarArgs(soa, nullptr, mid, ap)); va_end(ap); @@ -1791,13 +1803,13 @@ class JNI { } static jfloat CallStaticFloatMethodV(JNIEnv* env, jclass, jmethodID mid, va_list args) { - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeWithVarArgs(soa, nullptr, mid, args).GetF(); } static jfloat CallStaticFloatMethodA(JNIEnv* env, jclass, jmethodID mid, jvalue* args) { - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeWithJValues(soa, nullptr, mid, args).GetF(); } @@ -1805,7 +1817,7 @@ class JNI { static jdouble CallStaticDoubleMethod(JNIEnv* env, jclass, jmethodID mid, ...) { va_list ap; va_start(ap, mid); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); JValue result(InvokeWithVarArgs(soa, nullptr, mid, ap)); va_end(ap); @@ -1813,13 +1825,13 @@ class JNI { } static jdouble CallStaticDoubleMethodV(JNIEnv* env, jclass, jmethodID mid, va_list args) { - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeWithVarArgs(soa, nullptr, mid, args).GetD(); } static jdouble CallStaticDoubleMethodA(JNIEnv* env, jclass, jmethodID mid, jvalue* args) { - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeWithJValues(soa, nullptr, mid, args).GetD(); } @@ -1827,20 +1839,20 @@ class JNI { static void CallStaticVoidMethod(JNIEnv* env, jclass, jmethodID mid, ...) { va_list ap; va_start(ap, mid); - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(mid); ScopedObjectAccess soa(env); InvokeWithVarArgs(soa, nullptr, mid, ap); va_end(ap); } static void CallStaticVoidMethodV(JNIEnv* env, jclass, jmethodID mid, va_list args) { - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(mid); ScopedObjectAccess soa(env); InvokeWithVarArgs(soa, nullptr, mid, args); } static void CallStaticVoidMethodA(JNIEnv* env, jclass, jmethodID mid, jvalue* args) { - CHECK_NON_NULL_ARGUMENT(mid); + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(mid); ScopedObjectAccess soa(env); InvokeWithJValues(soa, nullptr, mid, args); } @@ -1869,20 +1881,20 @@ class JNI { } static jsize GetStringLength(JNIEnv* env, jstring java_string) { - CHECK_NON_NULL_ARGUMENT(java_string); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(java_string); ScopedObjectAccess soa(env); return soa.Decode<mirror::String*>(java_string)->GetLength(); } static jsize GetStringUTFLength(JNIEnv* env, jstring java_string) { - CHECK_NON_NULL_ARGUMENT(java_string); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(java_string); ScopedObjectAccess soa(env); return soa.Decode<mirror::String*>(java_string)->GetUtfLength(); } static void GetStringRegion(JNIEnv* env, jstring java_string, jsize start, jsize length, jchar* buf) { - CHECK_NON_NULL_ARGUMENT(java_string); + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(java_string); ScopedObjectAccess soa(env); mirror::String* s = soa.Decode<mirror::String*>(java_string); if (start < 0 || length < 0 || start + length > s->GetLength()) { @@ -1896,7 +1908,7 @@ class JNI { static void GetStringUTFRegion(JNIEnv* env, jstring java_string, jsize start, jsize length, char* buf) { - CHECK_NON_NULL_ARGUMENT(java_string); + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(java_string); ScopedObjectAccess soa(env); mirror::String* s = soa.Decode<mirror::String*>(java_string); if (start < 0 || length < 0 || start + length > s->GetLength()) { @@ -1928,7 +1940,7 @@ class JNI { } static void ReleaseStringChars(JNIEnv* env, jstring java_string, const jchar* chars) { - CHECK_NON_NULL_ARGUMENT(java_string); + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(java_string); delete[] chars; ScopedObjectAccess soa(env); UnpinPrimitiveArray(soa, soa.Decode<mirror::String*>(java_string)->GetCharArray()); @@ -1965,7 +1977,7 @@ class JNI { } static jsize GetArrayLength(JNIEnv* env, jarray java_array) { - CHECK_NON_NULL_ARGUMENT(java_array); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(java_array); ScopedObjectAccess soa(env); mirror::Object* obj = soa.Decode<mirror::Object*>(java_array); if (UNLIKELY(!obj->IsArrayInstance())) { @@ -1985,7 +1997,7 @@ class JNI { static void SetObjectArrayElement(JNIEnv* env, jobjectArray java_array, jsize index, jobject java_value) { - CHECK_NON_NULL_ARGUMENT(java_array); + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(java_array); ScopedObjectAccess soa(env); mirror::ObjectArray<mirror::Object>* array = soa.Decode<mirror::ObjectArray<mirror::Object>*>(java_array); @@ -1994,38 +2006,31 @@ class JNI { } static jbooleanArray NewBooleanArray(JNIEnv* env, jsize length) { - ScopedObjectAccess soa(env); - return NewPrimitiveArray<jbooleanArray, mirror::BooleanArray>(soa, length); + return NewPrimitiveArray<jbooleanArray, mirror::BooleanArray>(env, length); } static jbyteArray NewByteArray(JNIEnv* env, jsize length) { - ScopedObjectAccess soa(env); - return NewPrimitiveArray<jbyteArray, mirror::ByteArray>(soa, length); + return NewPrimitiveArray<jbyteArray, mirror::ByteArray>(env, length); } static jcharArray NewCharArray(JNIEnv* env, jsize length) { - ScopedObjectAccess soa(env); - return NewPrimitiveArray<jcharArray, mirror::CharArray>(soa, length); + return NewPrimitiveArray<jcharArray, mirror::CharArray>(env, length); } static jdoubleArray NewDoubleArray(JNIEnv* env, jsize length) { - ScopedObjectAccess soa(env); - return NewPrimitiveArray<jdoubleArray, mirror::DoubleArray>(soa, length); + return NewPrimitiveArray<jdoubleArray, mirror::DoubleArray>(env, length); } static jfloatArray NewFloatArray(JNIEnv* env, jsize length) { - ScopedObjectAccess soa(env); - return NewPrimitiveArray<jfloatArray, mirror::FloatArray>(soa, length); + return NewPrimitiveArray<jfloatArray, mirror::FloatArray>(env, length); } static jintArray NewIntArray(JNIEnv* env, jsize length) { - ScopedObjectAccess soa(env); - return NewPrimitiveArray<jintArray, mirror::IntArray>(soa, length); + return NewPrimitiveArray<jintArray, mirror::IntArray>(env, length); } static jlongArray NewLongArray(JNIEnv* env, jsize length) { - ScopedObjectAccess soa(env); - return NewPrimitiveArray<jlongArray, mirror::LongArray>(soa, length); + return NewPrimitiveArray<jlongArray, mirror::LongArray>(env, length); } static jobjectArray NewObjectArray(JNIEnv* env, jsize length, jclass element_jclass, @@ -2034,6 +2039,7 @@ class JNI { JniAbortF("NewObjectArray", "negative array length: %d", length); return nullptr; } + CHECK_NON_NULL_ARGUMENT(element_jclass); // Compute the array class corresponding to the given element class. ScopedObjectAccess soa(env); @@ -2075,14 +2081,18 @@ class JNI { } static jshortArray NewShortArray(JNIEnv* env, jsize length) { - ScopedObjectAccess soa(env); - return NewPrimitiveArray<jshortArray, mirror::ShortArray>(soa, length); + return NewPrimitiveArray<jshortArray, mirror::ShortArray>(env, length); } static void* GetPrimitiveArrayCritical(JNIEnv* env, jarray java_array, jboolean* is_copy) { CHECK_NON_NULL_ARGUMENT(java_array); ScopedObjectAccess soa(env); mirror::Array* array = soa.Decode<mirror::Array*>(java_array); + if (UNLIKELY(!array->GetClass()->IsPrimitiveArray())) { + JniAbortF("GetPrimitiveArrayCritical", "expected primitive array, given %s", + PrettyDescriptor(array->GetClass()).c_str()); + return nullptr; + } gc::Heap* heap = Runtime::Current()->GetHeap(); if (heap->IsMovableObject(array)) { heap->IncrementDisableMovingGC(soa.Self()); @@ -2096,196 +2106,174 @@ class JNI { return array->GetRawData(array->GetClass()->GetComponentSize(), 0); } - static void ReleasePrimitiveArrayCritical(JNIEnv* env, jarray array, void* elements, jint mode) { - CHECK_NON_NULL_ARGUMENT(array); - ReleasePrimitiveArray(env, array, elements, mode); + static void ReleasePrimitiveArrayCritical(JNIEnv* env, jarray java_array, void* elements, + jint mode) { + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(java_array); + ScopedObjectAccess soa(env); + mirror::Array* array = soa.Decode<mirror::Array*>(java_array); + if (UNLIKELY(!array->GetClass()->IsPrimitiveArray())) { + JniAbortF("ReleasePrimitiveArrayCritical", "expected primitive array, given %s", + PrettyDescriptor(array->GetClass()).c_str()); + return; + } + const size_t component_size = array->GetClass()->GetComponentSize(); + ReleasePrimitiveArray(soa, array, component_size, elements, mode); } static jboolean* GetBooleanArrayElements(JNIEnv* env, jbooleanArray array, jboolean* is_copy) { - CHECK_NON_NULL_ARGUMENT(array); - ScopedObjectAccess soa(env); - return GetPrimitiveArray<jbooleanArray, jboolean*, mirror::BooleanArray>(soa, array, is_copy); + return GetPrimitiveArray<jbooleanArray, jboolean, mirror::BooleanArray>(env, array, is_copy); } static jbyte* GetByteArrayElements(JNIEnv* env, jbyteArray array, jboolean* is_copy) { - CHECK_NON_NULL_ARGUMENT(array); - ScopedObjectAccess soa(env); - return GetPrimitiveArray<jbyteArray, jbyte*, mirror::ByteArray>(soa, array, is_copy); + return GetPrimitiveArray<jbyteArray, jbyte, mirror::ByteArray>(env, array, is_copy); } static jchar* GetCharArrayElements(JNIEnv* env, jcharArray array, jboolean* is_copy) { - CHECK_NON_NULL_ARGUMENT(array); - ScopedObjectAccess soa(env); - return GetPrimitiveArray<jcharArray, jchar*, mirror::CharArray>(soa, array, is_copy); + return GetPrimitiveArray<jcharArray, jchar, mirror::CharArray>(env, array, is_copy); } static jdouble* GetDoubleArrayElements(JNIEnv* env, jdoubleArray array, jboolean* is_copy) { - CHECK_NON_NULL_ARGUMENT(array); - ScopedObjectAccess soa(env); - return GetPrimitiveArray<jdoubleArray, jdouble*, mirror::DoubleArray>(soa, array, is_copy); + return GetPrimitiveArray<jdoubleArray, jdouble, mirror::DoubleArray>(env, array, is_copy); } static jfloat* GetFloatArrayElements(JNIEnv* env, jfloatArray array, jboolean* is_copy) { - CHECK_NON_NULL_ARGUMENT(array); - ScopedObjectAccess soa(env); - return GetPrimitiveArray<jfloatArray, jfloat*, mirror::FloatArray>(soa, array, is_copy); + return GetPrimitiveArray<jfloatArray, jfloat, mirror::FloatArray>(env, array, is_copy); } static jint* GetIntArrayElements(JNIEnv* env, jintArray array, jboolean* is_copy) { - CHECK_NON_NULL_ARGUMENT(array); - ScopedObjectAccess soa(env); - return GetPrimitiveArray<jintArray, jint*, mirror::IntArray>(soa, array, is_copy); + return GetPrimitiveArray<jintArray, jint, mirror::IntArray>(env, array, is_copy); } static jlong* GetLongArrayElements(JNIEnv* env, jlongArray array, jboolean* is_copy) { - CHECK_NON_NULL_ARGUMENT(array); - ScopedObjectAccess soa(env); - return GetPrimitiveArray<jlongArray, jlong*, mirror::LongArray>(soa, array, is_copy); + return GetPrimitiveArray<jlongArray, jlong, mirror::LongArray>(env, array, is_copy); } static jshort* GetShortArrayElements(JNIEnv* env, jshortArray array, jboolean* is_copy) { - CHECK_NON_NULL_ARGUMENT(array); - ScopedObjectAccess soa(env); - return GetPrimitiveArray<jshortArray, jshort*, mirror::ShortArray>(soa, array, is_copy); + return GetPrimitiveArray<jshortArray, jshort, mirror::ShortArray>(env, array, is_copy); } static void ReleaseBooleanArrayElements(JNIEnv* env, jbooleanArray array, jboolean* elements, jint mode) { - ReleasePrimitiveArray(env, array, elements, mode); + ReleasePrimitiveArray<jbooleanArray, jboolean, mirror::BooleanArray>(env, array, elements, + mode); } static void ReleaseByteArrayElements(JNIEnv* env, jbyteArray array, jbyte* elements, jint mode) { - ReleasePrimitiveArray(env, array, elements, mode); + ReleasePrimitiveArray<jbyteArray, jbyte, mirror::ByteArray>(env, array, elements, mode); } static void ReleaseCharArrayElements(JNIEnv* env, jcharArray array, jchar* elements, jint mode) { - ReleasePrimitiveArray(env, array, elements, mode); + ReleasePrimitiveArray<jcharArray, jchar, mirror::CharArray>(env, array, elements, mode); } static void ReleaseDoubleArrayElements(JNIEnv* env, jdoubleArray array, jdouble* elements, jint mode) { - ReleasePrimitiveArray(env, array, elements, mode); + ReleasePrimitiveArray<jdoubleArray, jdouble, mirror::DoubleArray>(env, array, elements, mode); } static void ReleaseFloatArrayElements(JNIEnv* env, jfloatArray array, jfloat* elements, jint mode) { - ReleasePrimitiveArray(env, array, elements, mode); + ReleasePrimitiveArray<jfloatArray, jfloat, mirror::FloatArray>(env, array, elements, mode); } static void ReleaseIntArrayElements(JNIEnv* env, jintArray array, jint* elements, jint mode) { - ReleasePrimitiveArray(env, array, elements, mode); + ReleasePrimitiveArray<jintArray, jint, mirror::IntArray>(env, array, elements, mode); } static void ReleaseLongArrayElements(JNIEnv* env, jlongArray array, jlong* elements, jint mode) { - ReleasePrimitiveArray(env, array, elements, mode); + ReleasePrimitiveArray<jlongArray, jlong, mirror::LongArray>(env, array, elements, mode); } static void ReleaseShortArrayElements(JNIEnv* env, jshortArray array, jshort* elements, jint mode) { - ReleasePrimitiveArray(env, array, elements, mode); + ReleasePrimitiveArray<jshortArray, jshort, mirror::ShortArray>(env, array, elements, mode); } static void GetBooleanArrayRegion(JNIEnv* env, jbooleanArray array, jsize start, jsize length, jboolean* buf) { - ScopedObjectAccess soa(env); - GetPrimitiveArrayRegion<jbooleanArray, jboolean, mirror::BooleanArray>(soa, array, start, + GetPrimitiveArrayRegion<jbooleanArray, jboolean, mirror::BooleanArray>(env, array, start, length, buf); } static void GetByteArrayRegion(JNIEnv* env, jbyteArray array, jsize start, jsize length, jbyte* buf) { - ScopedObjectAccess soa(env); - GetPrimitiveArrayRegion<jbyteArray, jbyte, mirror::ByteArray>(soa, array, start, length, buf); + GetPrimitiveArrayRegion<jbyteArray, jbyte, mirror::ByteArray>(env, array, start, length, buf); } static void GetCharArrayRegion(JNIEnv* env, jcharArray array, jsize start, jsize length, jchar* buf) { - ScopedObjectAccess soa(env); - GetPrimitiveArrayRegion<jcharArray, jchar, mirror::CharArray>(soa, array, start, length, buf); + GetPrimitiveArrayRegion<jcharArray, jchar, mirror::CharArray>(env, array, start, length, buf); } static void GetDoubleArrayRegion(JNIEnv* env, jdoubleArray array, jsize start, jsize length, jdouble* buf) { - ScopedObjectAccess soa(env); - GetPrimitiveArrayRegion<jdoubleArray, jdouble, mirror::DoubleArray>(soa, array, start, length, + GetPrimitiveArrayRegion<jdoubleArray, jdouble, mirror::DoubleArray>(env, array, start, length, buf); } static void GetFloatArrayRegion(JNIEnv* env, jfloatArray array, jsize start, jsize length, jfloat* buf) { - ScopedObjectAccess soa(env); - GetPrimitiveArrayRegion<jfloatArray, jfloat, mirror::FloatArray>(soa, array, start, length, + GetPrimitiveArrayRegion<jfloatArray, jfloat, mirror::FloatArray>(env, array, start, length, buf); } static void GetIntArrayRegion(JNIEnv* env, jintArray array, jsize start, jsize length, jint* buf) { - ScopedObjectAccess soa(env); - GetPrimitiveArrayRegion<jintArray, jint, mirror::IntArray>(soa, array, start, length, buf); + GetPrimitiveArrayRegion<jintArray, jint, mirror::IntArray>(env, array, start, length, buf); } static void GetLongArrayRegion(JNIEnv* env, jlongArray array, jsize start, jsize length, jlong* buf) { - ScopedObjectAccess soa(env); - GetPrimitiveArrayRegion<jlongArray, jlong, mirror::LongArray>(soa, array, start, length, buf); + GetPrimitiveArrayRegion<jlongArray, jlong, mirror::LongArray>(env, array, start, length, buf); } static void GetShortArrayRegion(JNIEnv* env, jshortArray array, jsize start, jsize length, jshort* buf) { - ScopedObjectAccess soa(env); - GetPrimitiveArrayRegion<jshortArray, jshort, mirror::ShortArray>(soa, array, start, length, + GetPrimitiveArrayRegion<jshortArray, jshort, mirror::ShortArray>(env, array, start, length, buf); } static void SetBooleanArrayRegion(JNIEnv* env, jbooleanArray array, jsize start, jsize length, const jboolean* buf) { - ScopedObjectAccess soa(env); - SetPrimitiveArrayRegion<jbooleanArray, jboolean, mirror::BooleanArray>(soa, array, start, + SetPrimitiveArrayRegion<jbooleanArray, jboolean, mirror::BooleanArray>(env, array, start, length, buf); } static void SetByteArrayRegion(JNIEnv* env, jbyteArray array, jsize start, jsize length, const jbyte* buf) { - ScopedObjectAccess soa(env); - SetPrimitiveArrayRegion<jbyteArray, jbyte, mirror::ByteArray>(soa, array, start, length, buf); + SetPrimitiveArrayRegion<jbyteArray, jbyte, mirror::ByteArray>(env, array, start, length, buf); } static void SetCharArrayRegion(JNIEnv* env, jcharArray array, jsize start, jsize length, const jchar* buf) { - ScopedObjectAccess soa(env); - SetPrimitiveArrayRegion<jcharArray, jchar, mirror::CharArray>(soa, array, start, length, buf); + SetPrimitiveArrayRegion<jcharArray, jchar, mirror::CharArray>(env, array, start, length, buf); } static void SetDoubleArrayRegion(JNIEnv* env, jdoubleArray array, jsize start, jsize length, const jdouble* buf) { - ScopedObjectAccess soa(env); - SetPrimitiveArrayRegion<jdoubleArray, jdouble, mirror::DoubleArray>(soa, array, start, length, + SetPrimitiveArrayRegion<jdoubleArray, jdouble, mirror::DoubleArray>(env, array, start, length, buf); } static void SetFloatArrayRegion(JNIEnv* env, jfloatArray array, jsize start, jsize length, const jfloat* buf) { - ScopedObjectAccess soa(env); - SetPrimitiveArrayRegion<jfloatArray, jfloat, mirror::FloatArray>(soa, array, start, length, + SetPrimitiveArrayRegion<jfloatArray, jfloat, mirror::FloatArray>(env, array, start, length, buf); } static void SetIntArrayRegion(JNIEnv* env, jintArray array, jsize start, jsize length, const jint* buf) { - ScopedObjectAccess soa(env); - SetPrimitiveArrayRegion<jintArray, jint, mirror::IntArray>(soa, array, start, length, buf); + SetPrimitiveArrayRegion<jintArray, jint, mirror::IntArray>(env, array, start, length, buf); } static void SetLongArrayRegion(JNIEnv* env, jlongArray array, jsize start, jsize length, const jlong* buf) { - ScopedObjectAccess soa(env); - SetPrimitiveArrayRegion<jlongArray, jlong, mirror::LongArray>(soa, array, start, length, buf); + SetPrimitiveArrayRegion<jlongArray, jlong, mirror::LongArray>(env, array, start, length, buf); } static void SetShortArrayRegion(JNIEnv* env, jshortArray array, jsize start, jsize length, const jshort* buf) { - ScopedObjectAccess soa(env); - SetPrimitiveArrayRegion<jshortArray, jshort, mirror::ShortArray>(soa, array, start, length, + SetPrimitiveArrayRegion<jshortArray, jshort, mirror::ShortArray>(env, array, start, length, buf); } @@ -2300,7 +2288,7 @@ class JNI { JniAbortF("RegisterNatives", "negative method count: %d", method_count); return JNI_ERR; // Not reached. } - CHECK_NON_NULL_ARGUMENT_FN_NAME("RegisterNatives", java_class); + CHECK_NON_NULL_ARGUMENT_FN_NAME("RegisterNatives", java_class, JNI_ERR); ScopedObjectAccess soa(env); mirror::Class* c = soa.Decode<mirror::Class*>(java_class); if (UNLIKELY(method_count == 0)) { @@ -2308,7 +2296,7 @@ class JNI { << PrettyDescriptor(c); return JNI_OK; } - CHECK_NON_NULL_ARGUMENT_FN_NAME("RegisterNatives", methods); + CHECK_NON_NULL_ARGUMENT_FN_NAME("RegisterNatives", methods, JNI_ERR); for (jint i = 0; i < method_count; ++i) { const char* name = methods[i].name; const char* sig = methods[i].signature; @@ -2345,30 +2333,37 @@ class JNI { } static jint UnregisterNatives(JNIEnv* env, jclass java_class) { - CHECK_NON_NULL_ARGUMENT(java_class); + CHECK_NON_NULL_ARGUMENT_RETURN(java_class, JNI_ERR); ScopedObjectAccess soa(env); mirror::Class* c = soa.Decode<mirror::Class*>(java_class); VLOG(jni) << "[Unregistering JNI native methods for " << PrettyClass(c) << "]"; + size_t unregistered_count = 0; for (size_t i = 0; i < c->NumDirectMethods(); ++i) { mirror::ArtMethod* m = c->GetDirectMethod(i); if (m->IsNative()) { m->UnregisterNative(soa.Self()); + unregistered_count++; } } for (size_t i = 0; i < c->NumVirtualMethods(); ++i) { mirror::ArtMethod* m = c->GetVirtualMethod(i); if (m->IsNative()) { m->UnregisterNative(soa.Self()); + unregistered_count++; } } + if (unregistered_count == 0) { + LOG(WARNING) << "JNI UnregisterNatives: attempt to unregister native methods of class '" + << PrettyDescriptor(c) << "' that contains no native methods"; + } return JNI_OK; } static jint MonitorEnter(JNIEnv* env, jobject java_object) NO_THREAD_SAFETY_ANALYSIS { - CHECK_NON_NULL_ARGUMENT(java_object); + CHECK_NON_NULL_ARGUMENT_RETURN(java_object, JNI_ERR); ScopedObjectAccess soa(env); mirror::Object* o = soa.Decode<mirror::Object*>(java_object); o = o->MonitorEnter(soa.Self()); @@ -2380,7 +2375,7 @@ class JNI { } static jint MonitorExit(JNIEnv* env, jobject java_object) NO_THREAD_SAFETY_ANALYSIS { - CHECK_NON_NULL_ARGUMENT(java_object); + CHECK_NON_NULL_ARGUMENT_RETURN(java_object, JNI_ERR); ScopedObjectAccess soa(env); mirror::Object* o = soa.Decode<mirror::Object*>(java_object); o->MonitorExit(soa.Self()); @@ -2392,7 +2387,7 @@ class JNI { } static jint GetJavaVM(JNIEnv* env, JavaVM** vm) { - CHECK_NON_NULL_ARGUMENT(vm); + CHECK_NON_NULL_ARGUMENT_RETURN(vm, JNI_ERR); Runtime* runtime = Runtime::Current(); if (runtime != nullptr) { *vm = runtime->GetJavaVM(); @@ -2432,7 +2427,7 @@ class JNI { } static jobjectRefType GetObjectRefType(JNIEnv* env, jobject java_object) { - CHECK_NON_NULL_ARGUMENT(java_object); + CHECK_NON_NULL_ARGUMENT_RETURN(java_object, JNIInvalidRefType); // Do we definitely know what kind of reference this is? IndirectRef ref = reinterpret_cast<IndirectRef>(java_object); @@ -2479,51 +2474,82 @@ class JNI { } template<typename JniT, typename ArtT> - static JniT NewPrimitiveArray(const ScopedObjectAccess& soa, jsize length) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + static JniT NewPrimitiveArray(JNIEnv* env, jsize length) { if (UNLIKELY(length < 0)) { JniAbortF("NewPrimitiveArray", "negative array length: %d", length); return nullptr; } + ScopedObjectAccess soa(env); ArtT* result = ArtT::Alloc(soa.Self(), length); return soa.AddLocalReference<JniT>(result); } - template <typename ArrayT, typename CArrayT, typename ArtArrayT> - static CArrayT GetPrimitiveArray(ScopedObjectAccess& soa, ArrayT java_array, - jboolean* is_copy) + template <typename JArrayT, typename ElementT, typename ArtArrayT> + static ArtArrayT* DecodeAndCheckArrayType(ScopedObjectAccess& soa, JArrayT java_array, + const char* fn_name, const char* operation) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { ArtArrayT* array = soa.Decode<ArtArrayT*>(java_array); + if (UNLIKELY(ArtArrayT::GetArrayClass() != array->GetClass())) { + JniAbortF(fn_name, "attempt to %s %s primitive array elements with an object of type %s", + operation, PrettyDescriptor(ArtArrayT::GetArrayClass()->GetComponentType()).c_str(), + PrettyDescriptor(array->GetClass()).c_str()); + return nullptr; + } + DCHECK_EQ(sizeof(ElementT), array->GetClass()->GetComponentSize()); + return array; + } + + template <typename ArrayT, typename ElementT, typename ArtArrayT> + static ElementT* GetPrimitiveArray(JNIEnv* env, ArrayT java_array, jboolean* is_copy) { + CHECK_NON_NULL_ARGUMENT(java_array); + ScopedObjectAccess soa(env); + ArtArrayT* array = DecodeAndCheckArrayType<ArrayT, ElementT, ArtArrayT>(soa, java_array, + "GetArrayElements", + "get"); + if (UNLIKELY(array == nullptr)) { + return nullptr; + } PinPrimitiveArray(soa, array); // Only make a copy if necessary. if (Runtime::Current()->GetHeap()->IsMovableObject(array)) { if (is_copy != nullptr) { *is_copy = JNI_TRUE; } - static const size_t component_size = array->GetClass()->GetComponentSize(); + const size_t component_size = sizeof(ElementT); size_t size = array->GetLength() * component_size; void* data = new uint64_t[RoundUp(size, 8) / 8]; memcpy(data, array->GetData(), size); - return reinterpret_cast<CArrayT>(data); + return reinterpret_cast<ElementT*>(data); } else { if (is_copy != nullptr) { *is_copy = JNI_FALSE; } - return reinterpret_cast<CArrayT>(array->GetData()); + return reinterpret_cast<ElementT*>(array->GetData()); } } - template <typename ArrayT, typename ElementT> + template <typename ArrayT, typename ElementT, typename ArtArrayT> static void ReleasePrimitiveArray(JNIEnv* env, ArrayT java_array, ElementT* elements, jint mode) { + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(java_array); ScopedObjectAccess soa(env); - mirror::Array* array = soa.Decode<mirror::Array*>(java_array); - size_t component_size = array->GetClass()->GetComponentSize(); + ArtArrayT* array = DecodeAndCheckArrayType<ArrayT, ElementT, ArtArrayT>(soa, java_array, + "ReleaseArrayElements", + "release"); + if (array == nullptr) { + return; + } + ReleasePrimitiveArray(soa, array, sizeof(ElementT), elements, mode); + } + + static void ReleasePrimitiveArray(ScopedObjectAccess& soa, mirror::Array* array, + size_t component_size, void* elements, jint mode) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { void* array_data = array->GetRawData(component_size, 0); gc::Heap* heap = Runtime::Current()->GetHeap(); - bool is_copy = array_data != reinterpret_cast<void*>(elements); + bool is_copy = array_data != elements; size_t bytes = array->GetLength() * component_size; - VLOG(heap) << "Release primitive array " << env << " array_data " << array_data - << " elements " << reinterpret_cast<void*>(elements); + VLOG(heap) << "Release primitive array " << soa.Env() << " array_data " << array_data + << " elements " << elements; if (is_copy) { // Sanity check: If elements is not the same as the java array's data, it better not be a // heap address. TODO: This might be slow to check, may be worth keeping track of which @@ -2549,33 +2575,43 @@ class JNI { } } - template <typename JavaArrayT, typename JavaT, typename ArrayT> - static void GetPrimitiveArrayRegion(ScopedObjectAccess& soa, JavaArrayT java_array, - jsize start, jsize length, JavaT* buf) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - CHECK_NON_NULL_ARGUMENT(java_array); - ArrayT* array = soa.Decode<ArrayT*>(java_array); - if (start < 0 || length < 0 || start + length > array->GetLength()) { - ThrowAIOOBE(soa, array, start, length, "src"); - } else { - CHECK_NON_NULL_MEMCPY_ARGUMENT(length, buf); - JavaT* data = array->GetData(); - memcpy(buf, data + start, length * sizeof(JavaT)); + template <typename JArrayT, typename ElementT, typename ArtArrayT> + static void GetPrimitiveArrayRegion(JNIEnv* env, JArrayT java_array, + jsize start, jsize length, ElementT* buf) { + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(java_array); + ScopedObjectAccess soa(env); + ArtArrayT* array = + DecodeAndCheckArrayType<JArrayT, ElementT, ArtArrayT>(soa, java_array, + "GetPrimitiveArrayRegion", + "get region of"); + if (array != nullptr) { + if (start < 0 || length < 0 || start + length > array->GetLength()) { + ThrowAIOOBE(soa, array, start, length, "src"); + } else { + CHECK_NON_NULL_MEMCPY_ARGUMENT(length, buf); + ElementT* data = array->GetData(); + memcpy(buf, data + start, length * sizeof(ElementT)); + } } } - template <typename JavaArrayT, typename JavaT, typename ArrayT> - static void SetPrimitiveArrayRegion(ScopedObjectAccess& soa, JavaArrayT java_array, - jsize start, jsize length, const JavaT* buf) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - CHECK_NON_NULL_ARGUMENT(java_array); - ArrayT* array = soa.Decode<ArrayT*>(java_array); - if (start < 0 || length < 0 || start + length > array->GetLength()) { - ThrowAIOOBE(soa, array, start, length, "dst"); - } else { - CHECK_NON_NULL_MEMCPY_ARGUMENT(length, buf); - JavaT* data = array->GetData(); - memcpy(data + start, buf, length * sizeof(JavaT)); + template <typename JArrayT, typename ElementT, typename ArtArrayT> + static void SetPrimitiveArrayRegion(JNIEnv* env, JArrayT java_array, + jsize start, jsize length, const ElementT* buf) { + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(java_array); + ScopedObjectAccess soa(env); + ArtArrayT* array = + DecodeAndCheckArrayType<JArrayT, ElementT, ArtArrayT>(soa, java_array, + "SetPrimitiveArrayRegion", + "set region of"); + if (array != nullptr) { + if (start < 0 || length < 0 || start + length > array->GetLength()) { + ThrowAIOOBE(soa, array, start, length, "dst"); + } else { + CHECK_NON_NULL_MEMCPY_ARGUMENT(length, buf); + ElementT* data = array->GetData(); + memcpy(data + start, buf, length * sizeof(ElementT)); + } } } }; diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc index 778b9e5a67..83e9b1028a 100644 --- a/runtime/jni_internal_test.cc +++ b/runtime/jni_internal_test.cc @@ -33,35 +33,54 @@ class JniInternalTest : public CommonCompilerTest { // Turn on -verbose:jni for the JNI tests. // gLogVerbosity.jni = true; - vm_->AttachCurrentThread(&env_, NULL); + vm_->AttachCurrentThread(&env_, nullptr); ScopedLocalRef<jclass> aioobe(env_, env_->FindClass("java/lang/ArrayIndexOutOfBoundsException")); - CHECK(aioobe.get() != NULL); + CHECK(aioobe.get() != nullptr); aioobe_ = reinterpret_cast<jclass>(env_->NewGlobalRef(aioobe.get())); ScopedLocalRef<jclass> ase(env_, env_->FindClass("java/lang/ArrayStoreException")); - CHECK(ase.get() != NULL); + CHECK(ase.get() != nullptr); ase_ = reinterpret_cast<jclass>(env_->NewGlobalRef(ase.get())); ScopedLocalRef<jclass> sioobe(env_, env_->FindClass("java/lang/StringIndexOutOfBoundsException")); - CHECK(sioobe.get() != NULL); + CHECK(sioobe.get() != nullptr); sioobe_ = reinterpret_cast<jclass>(env_->NewGlobalRef(sioobe.get())); } + void ExpectException(jclass exception_class) { + EXPECT_TRUE(env_->ExceptionCheck()); + jthrowable exception = env_->ExceptionOccurred(); + EXPECT_NE(nullptr, exception); + env_->ExceptionClear(); + EXPECT_TRUE(env_->IsInstanceOf(exception, exception_class)); + } + + void ExpectClassFound(const char* name) { + EXPECT_NE(env_->FindClass(name), nullptr) << name; + EXPECT_FALSE(env_->ExceptionCheck()) << name; + } + + void ExpectClassNotFound(const char* name) { + EXPECT_EQ(env_->FindClass(name), nullptr) << name; + EXPECT_TRUE(env_->ExceptionCheck()) << name; + env_->ExceptionClear(); + } + void CleanUpJniEnv() { - if (aioobe_ != NULL) { + if (aioobe_ != nullptr) { env_->DeleteGlobalRef(aioobe_); - aioobe_ = NULL; + aioobe_ = nullptr; } - if (ase_ != NULL) { + if (ase_ != nullptr) { env_->DeleteGlobalRef(ase_); - ase_ = NULL; + ase_ = nullptr; } - if (sioobe_ != NULL) { + if (sioobe_ != nullptr) { env_->DeleteGlobalRef(sioobe_); - sioobe_ = NULL; + sioobe_ = nullptr; } } @@ -86,9 +105,9 @@ class JniInternalTest : public CommonCompilerTest { TEST_F(JniInternalTest, AllocObject) { jclass c = env_->FindClass("java/lang/String"); - ASSERT_TRUE(c != NULL); + ASSERT_NE(c, nullptr); jobject o = env_->AllocObject(c); - ASSERT_TRUE(o != NULL); + ASSERT_NE(o, nullptr); // We have an instance of the class we asked for... ASSERT_TRUE(env_->IsInstanceOf(o, c)); @@ -96,139 +115,152 @@ TEST_F(JniInternalTest, AllocObject) { // we didn't call a constructor. ASSERT_EQ(0, env_->GetIntField(o, env_->GetFieldID(c, "count", "I"))); ASSERT_EQ(0, env_->GetIntField(o, env_->GetFieldID(c, "offset", "I"))); - ASSERT_TRUE(env_->GetObjectField(o, env_->GetFieldID(c, "value", "[C")) == NULL); + ASSERT_TRUE(env_->GetObjectField(o, env_->GetFieldID(c, "value", "[C")) == nullptr); } TEST_F(JniInternalTest, GetVersion) { ASSERT_EQ(JNI_VERSION_1_6, env_->GetVersion()); } -#define EXPECT_CLASS_FOUND(NAME) \ - EXPECT_TRUE(env_->FindClass(NAME) != NULL); \ - EXPECT_FALSE(env_->ExceptionCheck()) - -#define EXPECT_CLASS_NOT_FOUND(NAME) \ - EXPECT_TRUE(env_->FindClass(NAME) == NULL); \ - EXPECT_TRUE(env_->ExceptionCheck()); \ - env_->ExceptionClear() - TEST_F(JniInternalTest, FindClass) { // Reference types... - EXPECT_CLASS_FOUND("java/lang/String"); + ExpectClassFound("java/lang/String"); // ...for arrays too, where you must include "L;". - EXPECT_CLASS_FOUND("[Ljava/lang/String;"); + ExpectClassFound("[Ljava/lang/String;"); // Primitive arrays are okay too, if the primitive type is valid. - EXPECT_CLASS_FOUND("[C"); + ExpectClassFound("[C"); { - // We support . as well as / for compatibility, if -Xcheck:jni is off. CheckJniAbortCatcher check_jni_abort_catcher; - EXPECT_CLASS_FOUND("java.lang.String"); + env_->FindClass(nullptr); + check_jni_abort_catcher.Check("name == null"); + + // We support . as well as / for compatibility, if -Xcheck:jni is off. + ExpectClassFound("java.lang.String"); check_jni_abort_catcher.Check("illegal class name 'java.lang.String'"); - EXPECT_CLASS_NOT_FOUND("Ljava.lang.String;"); + ExpectClassNotFound("Ljava.lang.String;"); check_jni_abort_catcher.Check("illegal class name 'Ljava.lang.String;'"); - EXPECT_CLASS_FOUND("[Ljava.lang.String;"); + ExpectClassFound("[Ljava.lang.String;"); check_jni_abort_catcher.Check("illegal class name '[Ljava.lang.String;'"); - EXPECT_CLASS_NOT_FOUND("[java.lang.String"); + ExpectClassNotFound("[java.lang.String"); check_jni_abort_catcher.Check("illegal class name '[java.lang.String'"); // You can't include the "L;" in a JNI class descriptor. - EXPECT_CLASS_NOT_FOUND("Ljava/lang/String;"); + ExpectClassNotFound("Ljava/lang/String;"); check_jni_abort_catcher.Check("illegal class name 'Ljava/lang/String;'"); // But you must include it for an array of any reference type. - EXPECT_CLASS_NOT_FOUND("[java/lang/String"); + ExpectClassNotFound("[java/lang/String"); check_jni_abort_catcher.Check("illegal class name '[java/lang/String'"); - EXPECT_CLASS_NOT_FOUND("[K"); + ExpectClassNotFound("[K"); check_jni_abort_catcher.Check("illegal class name '[K'"); + + // Void arrays aren't allowed. + ExpectClassNotFound("[V"); + check_jni_abort_catcher.Check("illegal class name '[V'"); } // But primitive types aren't allowed... - EXPECT_CLASS_NOT_FOUND("C"); - EXPECT_CLASS_NOT_FOUND("K"); + ExpectClassNotFound("C"); + ExpectClassNotFound("V"); + ExpectClassNotFound("K"); } -#define EXPECT_EXCEPTION(exception_class) \ - do { \ - EXPECT_TRUE(env_->ExceptionCheck()); \ - jthrowable exception = env_->ExceptionOccurred(); \ - EXPECT_NE(static_cast<jthrowable>(NULL), exception); \ - env_->ExceptionClear(); \ - EXPECT_TRUE(env_->IsInstanceOf(exception, exception_class)); \ - } while (false) - TEST_F(JniInternalTest, GetFieldID) { jclass jlnsfe = env_->FindClass("java/lang/NoSuchFieldError"); - ASSERT_TRUE(jlnsfe != NULL); + ASSERT_NE(jlnsfe, nullptr); jclass c = env_->FindClass("java/lang/String"); - ASSERT_TRUE(c != NULL); + ASSERT_NE(c, nullptr); // Wrong type. jfieldID fid = env_->GetFieldID(c, "count", "J"); - EXPECT_EQ(static_cast<jfieldID>(NULL), fid); - EXPECT_EXCEPTION(jlnsfe); + EXPECT_EQ(nullptr, fid); + ExpectException(jlnsfe); // Wrong type where type doesn't exist. fid = env_->GetFieldID(c, "count", "Lrod/jane/freddy;"); - EXPECT_EQ(static_cast<jfieldID>(NULL), fid); - EXPECT_EXCEPTION(jlnsfe); + EXPECT_EQ(nullptr, fid); + ExpectException(jlnsfe); // Wrong name. fid = env_->GetFieldID(c, "Count", "I"); - EXPECT_EQ(static_cast<jfieldID>(NULL), fid); - EXPECT_EXCEPTION(jlnsfe); + EXPECT_EQ(nullptr, fid); + ExpectException(jlnsfe); // Good declared field lookup. fid = env_->GetFieldID(c, "count", "I"); - EXPECT_NE(static_cast<jfieldID>(NULL), fid); - EXPECT_TRUE(fid != NULL); + EXPECT_NE(nullptr, fid); EXPECT_FALSE(env_->ExceptionCheck()); // Good superclass field lookup. c = env_->FindClass("java/lang/StringBuilder"); fid = env_->GetFieldID(c, "count", "I"); - EXPECT_NE(static_cast<jfieldID>(NULL), fid); - EXPECT_TRUE(fid != NULL); + EXPECT_NE(nullptr, fid); + EXPECT_NE(fid, nullptr); EXPECT_FALSE(env_->ExceptionCheck()); // Not instance. fid = env_->GetFieldID(c, "CASE_INSENSITIVE_ORDER", "Ljava/util/Comparator;"); - EXPECT_EQ(static_cast<jfieldID>(NULL), fid); - EXPECT_EXCEPTION(jlnsfe); + EXPECT_EQ(nullptr, fid); + ExpectException(jlnsfe); + + // Bad arguments. + CheckJniAbortCatcher check_jni_abort_catcher; + fid = env_->GetFieldID(nullptr, "count", "I"); + EXPECT_EQ(nullptr, fid); + check_jni_abort_catcher.Check("java_class == null"); + fid = env_->GetFieldID(c, nullptr, "I"); + EXPECT_EQ(nullptr, fid); + check_jni_abort_catcher.Check("name == null"); + fid = env_->GetFieldID(c, "count", nullptr); + EXPECT_EQ(nullptr, fid); + check_jni_abort_catcher.Check("sig == null"); } TEST_F(JniInternalTest, GetStaticFieldID) { jclass jlnsfe = env_->FindClass("java/lang/NoSuchFieldError"); - ASSERT_TRUE(jlnsfe != NULL); + ASSERT_NE(jlnsfe, nullptr); jclass c = env_->FindClass("java/lang/String"); - ASSERT_TRUE(c != NULL); + ASSERT_NE(c, nullptr); // Wrong type. jfieldID fid = env_->GetStaticFieldID(c, "CASE_INSENSITIVE_ORDER", "J"); - EXPECT_EQ(static_cast<jfieldID>(NULL), fid); - EXPECT_EXCEPTION(jlnsfe); + EXPECT_EQ(nullptr, fid); + ExpectException(jlnsfe); // Wrong type where type doesn't exist. fid = env_->GetStaticFieldID(c, "CASE_INSENSITIVE_ORDER", "Lrod/jane/freddy;"); - EXPECT_EQ(static_cast<jfieldID>(NULL), fid); - EXPECT_EXCEPTION(jlnsfe); + EXPECT_EQ(nullptr, fid); + ExpectException(jlnsfe); // Wrong name. fid = env_->GetStaticFieldID(c, "cASE_INSENSITIVE_ORDER", "Ljava/util/Comparator;"); - EXPECT_EQ(static_cast<jfieldID>(NULL), fid); - EXPECT_EXCEPTION(jlnsfe); + EXPECT_EQ(nullptr, fid); + ExpectException(jlnsfe); // Good declared field lookup. fid = env_->GetStaticFieldID(c, "CASE_INSENSITIVE_ORDER", "Ljava/util/Comparator;"); - EXPECT_NE(static_cast<jfieldID>(NULL), fid); - EXPECT_TRUE(fid != NULL); + EXPECT_NE(nullptr, fid); + EXPECT_NE(fid, nullptr); EXPECT_FALSE(env_->ExceptionCheck()); // Not static. fid = env_->GetStaticFieldID(c, "count", "I"); - EXPECT_EQ(static_cast<jfieldID>(NULL), fid); - EXPECT_EXCEPTION(jlnsfe); + EXPECT_EQ(nullptr, fid); + ExpectException(jlnsfe); + + // Bad arguments. + CheckJniAbortCatcher check_jni_abort_catcher; + fid = env_->GetStaticFieldID(nullptr, "CASE_INSENSITIVE_ORDER", "Ljava/util/Comparator;"); + EXPECT_EQ(nullptr, fid); + check_jni_abort_catcher.Check("java_class == null"); + fid = env_->GetStaticFieldID(c, nullptr, "Ljava/util/Comparator;"); + EXPECT_EQ(nullptr, fid); + check_jni_abort_catcher.Check("name == null"); + fid = env_->GetStaticFieldID(c, "CASE_INSENSITIVE_ORDER", nullptr); + EXPECT_EQ(nullptr, fid); + check_jni_abort_catcher.Check("sig == null"); } TEST_F(JniInternalTest, GetMethodID) { @@ -242,24 +274,36 @@ TEST_F(JniInternalTest, GetMethodID) { // Check that java.lang.Object.foo() doesn't exist and NoSuchMethodError is // a pending exception jmethodID method = env_->GetMethodID(jlobject, "foo", "()V"); - EXPECT_EQ(static_cast<jmethodID>(NULL), method); - EXPECT_EXCEPTION(jlnsme); + EXPECT_EQ(nullptr, method); + ExpectException(jlnsme); // Check that java.lang.Object.equals() does exist method = env_->GetMethodID(jlobject, "equals", "(Ljava/lang/Object;)Z"); - EXPECT_NE(static_cast<jmethodID>(NULL), method); + EXPECT_NE(nullptr, method); EXPECT_FALSE(env_->ExceptionCheck()); // Check that GetMethodID for java.lang.String.valueOf(int) fails as the // method is static method = env_->GetMethodID(jlstring, "valueOf", "(I)Ljava/lang/String;"); - EXPECT_EQ(static_cast<jmethodID>(NULL), method); - EXPECT_EXCEPTION(jlnsme); + EXPECT_EQ(nullptr, method); + ExpectException(jlnsme); // Check that GetMethodID for java.lang.NoSuchMethodError.<init>(String) finds the constructor method = env_->GetMethodID(jlnsme, "<init>", "(Ljava/lang/String;)V"); - EXPECT_NE(static_cast<jmethodID>(NULL), method); + EXPECT_NE(nullptr, method); EXPECT_FALSE(env_->ExceptionCheck()); + + // Bad arguments. + CheckJniAbortCatcher check_jni_abort_catcher; + method = env_->GetMethodID(nullptr, "<init>", "(Ljava/lang/String;)V"); + EXPECT_EQ(nullptr, method); + check_jni_abort_catcher.Check("java_class == null"); + method = env_->GetMethodID(jlnsme, nullptr, "(Ljava/lang/String;)V"); + EXPECT_EQ(nullptr, method); + check_jni_abort_catcher.Check("name == null"); + method = env_->GetMethodID(jlnsme, "<init>", nullptr); + EXPECT_EQ(nullptr, method); + check_jni_abort_catcher.Check("sig == null"); } TEST_F(JniInternalTest, GetStaticMethodID) { @@ -272,93 +316,155 @@ TEST_F(JniInternalTest, GetStaticMethodID) { // Check that java.lang.Object.foo() doesn't exist and NoSuchMethodError is // a pending exception jmethodID method = env_->GetStaticMethodID(jlobject, "foo", "()V"); - EXPECT_EQ(static_cast<jmethodID>(NULL), method); - EXPECT_EXCEPTION(jlnsme); + EXPECT_EQ(nullptr, method); + ExpectException(jlnsme); // Check that GetStaticMethodID for java.lang.Object.equals(Object) fails as // the method is not static method = env_->GetStaticMethodID(jlobject, "equals", "(Ljava/lang/Object;)Z"); - EXPECT_EQ(static_cast<jmethodID>(NULL), method); - EXPECT_EXCEPTION(jlnsme); + EXPECT_EQ(nullptr, method); + ExpectException(jlnsme); // Check that java.lang.String.valueOf(int) does exist jclass jlstring = env_->FindClass("java/lang/String"); - method = env_->GetStaticMethodID(jlstring, "valueOf", - "(I)Ljava/lang/String;"); - EXPECT_NE(static_cast<jmethodID>(NULL), method); + method = env_->GetStaticMethodID(jlstring, "valueOf", "(I)Ljava/lang/String;"); + EXPECT_NE(nullptr, method); EXPECT_FALSE(env_->ExceptionCheck()); + + // Bad arguments. + CheckJniAbortCatcher check_jni_abort_catcher; + method = env_->GetStaticMethodID(nullptr, "valueOf", "(I)Ljava/lang/String;"); + EXPECT_EQ(nullptr, method); + check_jni_abort_catcher.Check("java_class == null"); + method = env_->GetStaticMethodID(jlstring, nullptr, "(I)Ljava/lang/String;"); + EXPECT_EQ(nullptr, method); + check_jni_abort_catcher.Check("name == null"); + method = env_->GetStaticMethodID(jlstring, "valueOf", nullptr); + EXPECT_EQ(nullptr, method); + check_jni_abort_catcher.Check("sig == null"); } TEST_F(JniInternalTest, FromReflectedField_ToReflectedField) { jclass jlrField = env_->FindClass("java/lang/reflect/Field"); jclass c = env_->FindClass("java/lang/String"); - ASSERT_TRUE(c != NULL); + ASSERT_NE(c, nullptr); jfieldID fid = env_->GetFieldID(c, "count", "I"); - ASSERT_TRUE(fid != NULL); + ASSERT_NE(fid, nullptr); // Turn the fid into a java.lang.reflect.Field... jobject field = env_->ToReflectedField(c, fid, JNI_FALSE); - ASSERT_TRUE(c != NULL); + ASSERT_NE(c, nullptr); ASSERT_TRUE(env_->IsInstanceOf(field, jlrField)); // ...and back again. jfieldID fid2 = env_->FromReflectedField(field); - ASSERT_TRUE(fid2 != NULL); + ASSERT_NE(fid2, nullptr); // Make sure we can actually use it. jstring s = env_->NewStringUTF("poop"); ASSERT_EQ(4, env_->GetIntField(s, fid2)); + + // Bad arguments. + CheckJniAbortCatcher check_jni_abort_catcher; + field = env_->ToReflectedField(c, nullptr, JNI_FALSE); + EXPECT_EQ(field, nullptr); + check_jni_abort_catcher.Check("fid == null"); + fid2 = env_->FromReflectedField(nullptr); + ASSERT_EQ(fid2, nullptr); + check_jni_abort_catcher.Check("jlr_field == null"); } TEST_F(JniInternalTest, FromReflectedMethod_ToReflectedMethod) { jclass jlrMethod = env_->FindClass("java/lang/reflect/Method"); jclass c = env_->FindClass("java/lang/String"); - ASSERT_TRUE(c != NULL); + ASSERT_NE(c, nullptr); jmethodID mid = env_->GetMethodID(c, "length", "()I"); - ASSERT_TRUE(mid != NULL); + ASSERT_NE(mid, nullptr); // Turn the mid into a java.lang.reflect.Method... jobject method = env_->ToReflectedMethod(c, mid, JNI_FALSE); - ASSERT_TRUE(c != NULL); + ASSERT_NE(c, nullptr); ASSERT_TRUE(env_->IsInstanceOf(method, jlrMethod)); // ...and back again. jmethodID mid2 = env_->FromReflectedMethod(method); - ASSERT_TRUE(mid2 != NULL); + ASSERT_NE(mid2, nullptr); // Make sure we can actually use it. jstring s = env_->NewStringUTF("poop"); ASSERT_EQ(4, env_->CallIntMethod(s, mid2)); + + // Bad arguments. + CheckJniAbortCatcher check_jni_abort_catcher; + method = env_->ToReflectedMethod(c, nullptr, JNI_FALSE); + EXPECT_EQ(method, nullptr); + check_jni_abort_catcher.Check("mid == null"); + mid2 = env_->FromReflectedMethod(method); + ASSERT_EQ(mid2, nullptr); + check_jni_abort_catcher.Check("jlr_method == null"); } -void BogusMethod() { - // You can't pass NULL function pointers to RegisterNatives. +static void BogusMethod() { + // You can't pass nullptr function pointers to RegisterNatives. } -TEST_F(JniInternalTest, RegisterNatives) { +TEST_F(JniInternalTest, RegisterAndUnregisterNatives) { jclass jlobject = env_->FindClass("java/lang/Object"); jclass jlnsme = env_->FindClass("java/lang/NoSuchMethodError"); - // Sanity check that no exceptions are pending + // Sanity check that no exceptions are pending. ASSERT_FALSE(env_->ExceptionCheck()); - // Check that registering to a non-existent java.lang.Object.foo() causes a - // NoSuchMethodError + // Check that registering to a non-existent java.lang.Object.foo() causes a NoSuchMethodError. { - JNINativeMethod methods[] = { { "foo", "()V", NULL } }; - env_->RegisterNatives(jlobject, methods, 1); + JNINativeMethod methods[] = { { "foo", "()V", nullptr } }; + EXPECT_EQ(env_->RegisterNatives(jlobject, methods, 1), JNI_ERR); } - EXPECT_EXCEPTION(jlnsme); + ExpectException(jlnsme); - // Check that registering non-native methods causes a NoSuchMethodError + // Check that registering non-native methods causes a NoSuchMethodError. { - JNINativeMethod methods[] = { { "equals", "(Ljava/lang/Object;)Z", NULL } }; - env_->RegisterNatives(jlobject, methods, 1); + JNINativeMethod methods[] = { { "equals", "(Ljava/lang/Object;)Z", nullptr } }; + EXPECT_EQ(env_->RegisterNatives(jlobject, methods, 1), JNI_ERR); } - EXPECT_EXCEPTION(jlnsme); + ExpectException(jlnsme); - // Check that registering native methods is successful + // Check that registering native methods is successful. { JNINativeMethod methods[] = { { "notify", "()V", reinterpret_cast<void*>(BogusMethod) } }; - env_->RegisterNatives(jlobject, methods, 1); + EXPECT_EQ(env_->RegisterNatives(jlobject, methods, 1), JNI_OK); + } + EXPECT_FALSE(env_->ExceptionCheck()); + EXPECT_EQ(env_->UnregisterNatives(jlobject), JNI_OK); + + // Check that registering no methods isn't a failure. + { + JNINativeMethod methods[] = { }; + EXPECT_EQ(env_->RegisterNatives(jlobject, methods, 0), JNI_OK); + } + EXPECT_FALSE(env_->ExceptionCheck()); + EXPECT_EQ(env_->UnregisterNatives(jlobject), JNI_OK); + + // Check that registering a -ve number of methods is a failure. + CheckJniAbortCatcher check_jni_abort_catcher; + for (int i = -10; i < 0; ++i) { + JNINativeMethod methods[] = { }; + EXPECT_EQ(env_->RegisterNatives(jlobject, methods, i), JNI_ERR); + check_jni_abort_catcher.Check("negative method count: "); } EXPECT_FALSE(env_->ExceptionCheck()); - env_->UnregisterNatives(jlobject); + // Passing a class of null is a failure. + { + JNINativeMethod methods[] = { }; + EXPECT_EQ(env_->RegisterNatives(nullptr, methods, 0), JNI_ERR); + check_jni_abort_catcher.Check("java_class == null"); + } + + // Passing methods as null is a failure. + EXPECT_EQ(env_->RegisterNatives(jlobject, nullptr, 1), JNI_ERR); + check_jni_abort_catcher.Check("methods == null"); + + // Unregisters null is a failure. + EXPECT_EQ(env_->UnregisterNatives(nullptr), JNI_ERR); + check_jni_abort_catcher.Check("java_class == null"); + + // Unregistering a class with no natives is a warning. + EXPECT_EQ(env_->UnregisterNatives(jlnsme), JNI_OK); } #define EXPECT_PRIMITIVE_ARRAY(new_fn, \ @@ -368,52 +474,69 @@ TEST_F(JniInternalTest, RegisterNatives) { release_elements_fn, \ scalar_type, \ expected_class_descriptor) \ + jsize size = 4; \ + \ { \ CheckJniAbortCatcher jni_abort_catcher; \ /* Allocate an negative sized array and check it has the right failure type. */ \ - env_->new_fn(-1); \ + EXPECT_EQ(env_->new_fn(-1), nullptr); \ jni_abort_catcher.Check("negative array length: -1"); \ - env_->new_fn(std::numeric_limits<jint>::min()); \ + EXPECT_EQ(env_->new_fn(std::numeric_limits<jint>::min()), nullptr); \ jni_abort_catcher.Check("negative array length: -2147483648"); \ + /* Pass the array as null. */ \ + EXPECT_EQ(0, env_->GetArrayLength(nullptr)); \ + jni_abort_catcher.Check("java_array == null"); \ + env_->get_region_fn(nullptr, 0, 0, nullptr); \ + jni_abort_catcher.Check("java_array == null"); \ + env_->set_region_fn(nullptr, 0, 0, nullptr); \ + jni_abort_catcher.Check("java_array == null"); \ + env_->get_elements_fn(nullptr, nullptr); \ + jni_abort_catcher.Check("java_array == null"); \ + env_->release_elements_fn(nullptr, nullptr, 0); \ + jni_abort_catcher.Check("java_array == null"); \ + /* Pass the elements for region as null. */ \ + scalar_type ## Array a = env_->new_fn(size); \ + env_->get_region_fn(a, 0, size, nullptr); \ + jni_abort_catcher.Check("buf == null"); \ + env_->set_region_fn(a, 0, size, nullptr); \ + jni_abort_catcher.Check("buf == null"); \ } \ - jsize size = 4; \ - \ /* Allocate an array and check it has the right type and length. */ \ scalar_type ## Array a = env_->new_fn(size); \ - EXPECT_TRUE(a != NULL); \ + EXPECT_NE(a, nullptr); \ EXPECT_TRUE(env_->IsInstanceOf(a, env_->FindClass(expected_class_descriptor))); \ EXPECT_EQ(size, env_->GetArrayLength(a)); \ \ /* GetPrimitiveArrayRegion/SetPrimitiveArrayRegion */ \ /* AIOOBE for negative start offset. */ \ - env_->get_region_fn(a, -1, 1, NULL); \ - EXPECT_EXCEPTION(aioobe_); \ - env_->set_region_fn(a, -1, 1, NULL); \ - EXPECT_EXCEPTION(aioobe_); \ + env_->get_region_fn(a, -1, 1, nullptr); \ + ExpectException(aioobe_); \ + env_->set_region_fn(a, -1, 1, nullptr); \ + ExpectException(aioobe_); \ \ /* AIOOBE for negative length. */ \ - env_->get_region_fn(a, 0, -1, NULL); \ - EXPECT_EXCEPTION(aioobe_); \ - env_->set_region_fn(a, 0, -1, NULL); \ - EXPECT_EXCEPTION(aioobe_); \ + env_->get_region_fn(a, 0, -1, nullptr); \ + ExpectException(aioobe_); \ + env_->set_region_fn(a, 0, -1, nullptr); \ + ExpectException(aioobe_); \ \ /* AIOOBE for buffer overrun. */ \ - env_->get_region_fn(a, size - 1, size, NULL); \ - EXPECT_EXCEPTION(aioobe_); \ - env_->set_region_fn(a, size - 1, size, NULL); \ - EXPECT_EXCEPTION(aioobe_); \ + env_->get_region_fn(a, size - 1, size, nullptr); \ + ExpectException(aioobe_); \ + env_->set_region_fn(a, size - 1, size, nullptr); \ + ExpectException(aioobe_); \ \ - /* It's okay for the buffer to be NULL as long as the length is 0. */ \ - env_->get_region_fn(a, 2, 0, NULL); \ + /* It's okay for the buffer to be nullptr as long as the length is 0. */ \ + env_->get_region_fn(a, 2, 0, nullptr); \ /* Even if the offset is invalid... */ \ - env_->get_region_fn(a, 123, 0, NULL); \ - EXPECT_EXCEPTION(aioobe_); \ + env_->get_region_fn(a, 123, 0, nullptr); \ + ExpectException(aioobe_); \ \ - /* It's okay for the buffer to be NULL as long as the length is 0. */ \ - env_->set_region_fn(a, 2, 0, NULL); \ + /* It's okay for the buffer to be nullptr as long as the length is 0. */ \ + env_->set_region_fn(a, 2, 0, nullptr); \ /* Even if the offset is invalid... */ \ - env_->set_region_fn(a, 123, 0, NULL); \ - EXPECT_EXCEPTION(aioobe_); \ + env_->set_region_fn(a, 123, 0, nullptr); \ + ExpectException(aioobe_); \ \ /* Prepare a couple of buffers. */ \ UniquePtr<scalar_type[]> src_buf(new scalar_type[size]); \ @@ -437,12 +560,12 @@ TEST_F(JniInternalTest, RegisterNatives) { EXPECT_EQ(memcmp(&src_buf[0], &dst_buf[0], size * sizeof(scalar_type)), 0) \ << "full copy not equal"; \ /* GetPrimitiveArrayCritical */ \ - void* v = env_->GetPrimitiveArrayCritical(a, NULL); \ + void* v = env_->GetPrimitiveArrayCritical(a, nullptr); \ EXPECT_EQ(memcmp(&src_buf[0], v, size * sizeof(scalar_type)), 0) \ << "GetPrimitiveArrayCritical not equal"; \ env_->ReleasePrimitiveArrayCritical(a, v, 0); \ /* GetXArrayElements */ \ - scalar_type* xs = env_->get_elements_fn(a, NULL); \ + scalar_type* xs = env_->get_elements_fn(a, nullptr); \ EXPECT_EQ(memcmp(&src_buf[0], xs, size * sizeof(scalar_type)), 0) \ << # get_elements_fn " not equal"; \ env_->release_elements_fn(a, xs, 0); \ @@ -480,31 +603,206 @@ TEST_F(JniInternalTest, ShortArrays) { GetShortArrayElements, ReleaseShortArrayElements, jshort, "[S"); } +TEST_F(JniInternalTest, GetPrimitiveArrayElementsOfWrongType) { + CheckJniAbortCatcher jni_abort_catcher; + jbooleanArray array = env_->NewBooleanArray(10); + jboolean is_copy; + EXPECT_EQ(env_->GetByteArrayElements(reinterpret_cast<jbyteArray>(array), &is_copy), nullptr); + jni_abort_catcher.Check( + "attempt to get byte primitive array elements with an object of type boolean[]"); + EXPECT_EQ(env_->GetShortArrayElements(reinterpret_cast<jshortArray>(array), &is_copy), nullptr); + jni_abort_catcher.Check( + "attempt to get short primitive array elements with an object of type boolean[]"); + EXPECT_EQ(env_->GetCharArrayElements(reinterpret_cast<jcharArray>(array), &is_copy), nullptr); + jni_abort_catcher.Check( + "attempt to get char primitive array elements with an object of type boolean[]"); + EXPECT_EQ(env_->GetIntArrayElements(reinterpret_cast<jintArray>(array), &is_copy), nullptr); + jni_abort_catcher.Check( + "attempt to get int primitive array elements with an object of type boolean[]"); + EXPECT_EQ(env_->GetLongArrayElements(reinterpret_cast<jlongArray>(array), &is_copy), nullptr); + jni_abort_catcher.Check( + "attempt to get long primitive array elements with an object of type boolean[]"); + EXPECT_EQ(env_->GetFloatArrayElements(reinterpret_cast<jfloatArray>(array), &is_copy), nullptr); + jni_abort_catcher.Check( + "attempt to get float primitive array elements with an object of type boolean[]"); + EXPECT_EQ(env_->GetDoubleArrayElements(reinterpret_cast<jdoubleArray>(array), &is_copy), nullptr); + jni_abort_catcher.Check( + "attempt to get double primitive array elements with an object of type boolean[]"); + jbyteArray array2 = env_->NewByteArray(10); + EXPECT_EQ(env_->GetBooleanArrayElements(reinterpret_cast<jbooleanArray>(array2), &is_copy), nullptr); + jni_abort_catcher.Check( + "attempt to get boolean primitive array elements with an object of type byte[]"); + jobject object = env_->NewStringUTF("Test String"); + EXPECT_EQ(env_->GetBooleanArrayElements(reinterpret_cast<jbooleanArray>(object), &is_copy), nullptr); + jni_abort_catcher.Check( + "attempt to get boolean primitive array elements with an object of type java.lang.String"); +} + +TEST_F(JniInternalTest, ReleasePrimitiveArrayElementsOfWrongType) { + CheckJniAbortCatcher jni_abort_catcher; + jbooleanArray array = env_->NewBooleanArray(10); + ASSERT_TRUE(array != nullptr); + jboolean is_copy; + jboolean* elements = env_->GetBooleanArrayElements(array, &is_copy); + ASSERT_TRUE(elements != nullptr); + env_->ReleaseByteArrayElements(reinterpret_cast<jbyteArray>(array), + reinterpret_cast<jbyte*>(elements), 0); + jni_abort_catcher.Check( + "attempt to release byte primitive array elements with an object of type boolean[]"); + env_->ReleaseShortArrayElements(reinterpret_cast<jshortArray>(array), + reinterpret_cast<jshort*>(elements), 0); + jni_abort_catcher.Check( + "attempt to release short primitive array elements with an object of type boolean[]"); + env_->ReleaseCharArrayElements(reinterpret_cast<jcharArray>(array), + reinterpret_cast<jchar*>(elements), 0); + jni_abort_catcher.Check( + "attempt to release char primitive array elements with an object of type boolean[]"); + env_->ReleaseIntArrayElements(reinterpret_cast<jintArray>(array), + reinterpret_cast<jint*>(elements), 0); + jni_abort_catcher.Check( + "attempt to release int primitive array elements with an object of type boolean[]"); + env_->ReleaseLongArrayElements(reinterpret_cast<jlongArray>(array), + reinterpret_cast<jlong*>(elements), 0); + jni_abort_catcher.Check( + "attempt to release long primitive array elements with an object of type boolean[]"); + env_->ReleaseFloatArrayElements(reinterpret_cast<jfloatArray>(array), + reinterpret_cast<jfloat*>(elements), 0); + jni_abort_catcher.Check( + "attempt to release float primitive array elements with an object of type boolean[]"); + env_->ReleaseDoubleArrayElements(reinterpret_cast<jdoubleArray>(array), + reinterpret_cast<jdouble*>(elements), 0); + jni_abort_catcher.Check( + "attempt to release double primitive array elements with an object of type boolean[]"); + jbyteArray array2 = env_->NewByteArray(10); + env_->ReleaseBooleanArrayElements(reinterpret_cast<jbooleanArray>(array2), elements, 0); + jni_abort_catcher.Check( + "attempt to release boolean primitive array elements with an object of type byte[]"); + jobject object = env_->NewStringUTF("Test String"); + env_->ReleaseBooleanArrayElements(reinterpret_cast<jbooleanArray>(object), elements, 0); + jni_abort_catcher.Check( + "attempt to release boolean primitive array elements with an object of type java.lang.String"); +} +TEST_F(JniInternalTest, GetReleasePrimitiveArrayCriticalOfWrongType) { + CheckJniAbortCatcher jni_abort_catcher; + jobject object = env_->NewStringUTF("Test String"); + jboolean is_copy; + void* elements = env_->GetPrimitiveArrayCritical(reinterpret_cast<jarray>(object), &is_copy); + jni_abort_catcher.Check("expected primitive array, given java.lang.String"); + env_->ReleasePrimitiveArrayCritical(reinterpret_cast<jarray>(object), elements, 0); + jni_abort_catcher.Check("expected primitive array, given java.lang.String"); +} + +TEST_F(JniInternalTest, GetPrimitiveArrayRegionElementsOfWrongType) { + CheckJniAbortCatcher jni_abort_catcher; + constexpr size_t kLength = 10; + jbooleanArray array = env_->NewBooleanArray(kLength); + ASSERT_TRUE(array != nullptr); + jboolean elements[kLength]; + env_->GetByteArrayRegion(reinterpret_cast<jbyteArray>(array), 0, kLength, + reinterpret_cast<jbyte*>(elements)); + jni_abort_catcher.Check( + "attempt to get region of byte primitive array elements with an object of type boolean[]"); + env_->GetShortArrayRegion(reinterpret_cast<jshortArray>(array), 0, kLength, + reinterpret_cast<jshort*>(elements)); + jni_abort_catcher.Check( + "attempt to get region of short primitive array elements with an object of type boolean[]"); + env_->GetCharArrayRegion(reinterpret_cast<jcharArray>(array), 0, kLength, + reinterpret_cast<jchar*>(elements)); + jni_abort_catcher.Check( + "attempt to get region of char primitive array elements with an object of type boolean[]"); + env_->GetIntArrayRegion(reinterpret_cast<jintArray>(array), 0, kLength, + reinterpret_cast<jint*>(elements)); + jni_abort_catcher.Check( + "attempt to get region of int primitive array elements with an object of type boolean[]"); + env_->GetLongArrayRegion(reinterpret_cast<jlongArray>(array), 0, kLength, + reinterpret_cast<jlong*>(elements)); + jni_abort_catcher.Check( + "attempt to get region of long primitive array elements with an object of type boolean[]"); + env_->GetFloatArrayRegion(reinterpret_cast<jfloatArray>(array), 0, kLength, + reinterpret_cast<jfloat*>(elements)); + jni_abort_catcher.Check( + "attempt to get region of float primitive array elements with an object of type boolean[]"); + env_->GetDoubleArrayRegion(reinterpret_cast<jdoubleArray>(array), 0, kLength, + reinterpret_cast<jdouble*>(elements)); + jni_abort_catcher.Check( + "attempt to get region of double primitive array elements with an object of type boolean[]"); + jbyteArray array2 = env_->NewByteArray(10); + env_->GetBooleanArrayRegion(reinterpret_cast<jbooleanArray>(array2), 0, kLength, + reinterpret_cast<jboolean*>(elements)); + jni_abort_catcher.Check( + "attempt to get region of boolean primitive array elements with an object of type byte[]"); + jobject object = env_->NewStringUTF("Test String"); + env_->GetBooleanArrayRegion(reinterpret_cast<jbooleanArray>(object), 0, kLength, + reinterpret_cast<jboolean*>(elements)); + jni_abort_catcher.Check( + "attempt to get region of boolean primitive array elements with an object of type java.lang.String"); +} + +TEST_F(JniInternalTest, SetPrimitiveArrayRegionElementsOfWrongType) { + CheckJniAbortCatcher jni_abort_catcher; + constexpr size_t kLength = 10; + jbooleanArray array = env_->NewBooleanArray(kLength); + ASSERT_TRUE(array != nullptr); + jboolean elements[kLength]; + env_->SetByteArrayRegion(reinterpret_cast<jbyteArray>(array), 0, kLength, + reinterpret_cast<jbyte*>(elements)); + jni_abort_catcher.Check( + "attempt to set region of byte primitive array elements with an object of type boolean[]"); + env_->SetShortArrayRegion(reinterpret_cast<jshortArray>(array), 0, kLength, + reinterpret_cast<jshort*>(elements)); + jni_abort_catcher.Check( + "attempt to set region of short primitive array elements with an object of type boolean[]"); + env_->SetCharArrayRegion(reinterpret_cast<jcharArray>(array), 0, kLength, + reinterpret_cast<jchar*>(elements)); + jni_abort_catcher.Check( + "attempt to set region of char primitive array elements with an object of type boolean[]"); + env_->SetIntArrayRegion(reinterpret_cast<jintArray>(array), 0, kLength, + reinterpret_cast<jint*>(elements)); + jni_abort_catcher.Check( + "attempt to set region of int primitive array elements with an object of type boolean[]"); + env_->SetLongArrayRegion(reinterpret_cast<jlongArray>(array), 0, kLength, + reinterpret_cast<jlong*>(elements)); + jni_abort_catcher.Check( + "attempt to set region of long primitive array elements with an object of type boolean[]"); + env_->SetFloatArrayRegion(reinterpret_cast<jfloatArray>(array), 0, kLength, + reinterpret_cast<jfloat*>(elements)); + jni_abort_catcher.Check( + "attempt to set region of float primitive array elements with an object of type boolean[]"); + env_->SetDoubleArrayRegion(reinterpret_cast<jdoubleArray>(array), 0, kLength, + reinterpret_cast<jdouble*>(elements)); + jni_abort_catcher.Check( + "attempt to set region of double primitive array elements with an object of type boolean[]"); + jbyteArray array2 = env_->NewByteArray(10); + env_->SetBooleanArrayRegion(reinterpret_cast<jbooleanArray>(array2), 0, kLength, + reinterpret_cast<jboolean*>(elements)); + jni_abort_catcher.Check( + "attempt to set region of boolean primitive array elements with an object of type byte[]"); + jobject object = env_->NewStringUTF("Test String"); + env_->SetBooleanArrayRegion(reinterpret_cast<jbooleanArray>(object), 0, kLength, + reinterpret_cast<jboolean*>(elements)); + jni_abort_catcher.Check( + "attempt to set region of boolean primitive array elements with an object of type java.lang.String"); +} + TEST_F(JniInternalTest, NewObjectArray) { jclass element_class = env_->FindClass("java/lang/String"); - ASSERT_TRUE(element_class != nullptr); + ASSERT_NE(element_class, nullptr); jclass array_class = env_->FindClass("[Ljava/lang/String;"); - ASSERT_TRUE(array_class != nullptr); + ASSERT_NE(array_class, nullptr); jobjectArray a = env_->NewObjectArray(0, element_class, nullptr); - EXPECT_TRUE(a != nullptr); + EXPECT_NE(a, nullptr); EXPECT_TRUE(env_->IsInstanceOf(a, array_class)); EXPECT_EQ(0, env_->GetArrayLength(a)); a = env_->NewObjectArray(1, element_class, nullptr); - EXPECT_TRUE(a != nullptr); + EXPECT_NE(a, nullptr); EXPECT_TRUE(env_->IsInstanceOf(a, array_class)); EXPECT_EQ(1, env_->GetArrayLength(a)); EXPECT_TRUE(env_->IsSameObject(env_->GetObjectArrayElement(a, 0), nullptr)); -} -TEST_F(JniInternalTest, NewObjectArrayWithNegativeLength) { - jclass element_class = env_->FindClass("java/lang/String"); - ASSERT_TRUE(element_class != nullptr); - jclass array_class = env_->FindClass("[Ljava/lang/String;"); - ASSERT_TRUE(array_class != nullptr); + // Negative array length checks. CheckJniAbortCatcher jni_abort_catcher; - env_->NewObjectArray(-1, element_class, nullptr); jni_abort_catcher.Check("negative array length: -1"); @@ -521,6 +819,8 @@ TEST_F(JniInternalTest, NewObjectArrayWithPrimitiveClasses) { CheckJniAbortCatcher jni_abort_catcher; for (size_t i = 0; i < strlen(primitive_descriptors); ++i) { + env_->NewObjectArray(0, nullptr, nullptr); + jni_abort_catcher.Check("element_jclass == null"); jclass primitive_class = GetPrimitiveClass(primitive_descriptors[i]); env_->NewObjectArray(1, primitive_class, nullptr); std::string error_msg(StringPrintf("not an object type: %s", primitive_names[i])); @@ -530,13 +830,13 @@ TEST_F(JniInternalTest, NewObjectArrayWithPrimitiveClasses) { TEST_F(JniInternalTest, NewObjectArrayWithInitialValue) { jclass element_class = env_->FindClass("java/lang/String"); - ASSERT_TRUE(element_class != nullptr); + ASSERT_NE(element_class, nullptr); jclass array_class = env_->FindClass("[Ljava/lang/String;"); - ASSERT_TRUE(array_class != nullptr); + ASSERT_NE(array_class, nullptr); jstring s = env_->NewStringUTF("poop"); jobjectArray a = env_->NewObjectArray(2, element_class, s); - EXPECT_TRUE(a != nullptr); + EXPECT_NE(a, nullptr); EXPECT_TRUE(env_->IsInstanceOf(a, array_class)); EXPECT_EQ(2, env_->GetArrayLength(a)); EXPECT_TRUE(env_->IsSameObject(env_->GetObjectArrayElement(a, 0), s)); @@ -555,9 +855,9 @@ TEST_F(JniInternalTest, GetArrayLength) { TEST_F(JniInternalTest, GetObjectClass) { jclass string_class = env_->FindClass("java/lang/String"); - ASSERT_TRUE(string_class != NULL); + ASSERT_NE(string_class, nullptr); jclass class_class = env_->FindClass("java/lang/Class"); - ASSERT_TRUE(class_class != NULL); + ASSERT_NE(class_class, nullptr); jstring s = env_->NewStringUTF("poop"); jclass c = env_->GetObjectClass(s); @@ -565,33 +865,50 @@ TEST_F(JniInternalTest, GetObjectClass) { jclass c2 = env_->GetObjectClass(c); ASSERT_TRUE(env_->IsSameObject(class_class, env_->GetObjectClass(c2))); + + // Null as object should fail. + CheckJniAbortCatcher jni_abort_catcher; + EXPECT_EQ(env_->GetObjectClass(nullptr), nullptr); + jni_abort_catcher.Check("java_object == null"); } TEST_F(JniInternalTest, GetSuperclass) { jclass object_class = env_->FindClass("java/lang/Object"); - ASSERT_TRUE(object_class != NULL); + ASSERT_NE(object_class, nullptr); jclass string_class = env_->FindClass("java/lang/String"); - ASSERT_TRUE(string_class != NULL); + ASSERT_NE(string_class, nullptr); jclass runnable_interface = env_->FindClass("java/lang/Runnable"); - ASSERT_TRUE(runnable_interface != NULL); + ASSERT_NE(runnable_interface, nullptr); ASSERT_TRUE(env_->IsSameObject(object_class, env_->GetSuperclass(string_class))); - ASSERT_TRUE(env_->GetSuperclass(object_class) == NULL); + ASSERT_EQ(env_->GetSuperclass(object_class), nullptr); ASSERT_TRUE(env_->IsSameObject(object_class, env_->GetSuperclass(runnable_interface))); + + // Null as class should fail. + CheckJniAbortCatcher jni_abort_catcher; + EXPECT_EQ(env_->GetSuperclass(nullptr), nullptr); + jni_abort_catcher.Check("java_class == null"); } TEST_F(JniInternalTest, IsAssignableFrom) { jclass object_class = env_->FindClass("java/lang/Object"); - ASSERT_TRUE(object_class != NULL); + ASSERT_NE(object_class, nullptr); jclass string_class = env_->FindClass("java/lang/String"); - ASSERT_TRUE(string_class != NULL); + ASSERT_NE(string_class, nullptr); ASSERT_TRUE(env_->IsAssignableFrom(object_class, string_class)); ASSERT_FALSE(env_->IsAssignableFrom(string_class, object_class)); + + // Null as either class should fail. + CheckJniAbortCatcher jni_abort_catcher; + EXPECT_EQ(env_->IsAssignableFrom(nullptr, string_class), JNI_FALSE); + jni_abort_catcher.Check("java_class1 == null"); + EXPECT_EQ(env_->IsAssignableFrom(object_class, nullptr), JNI_FALSE); + jni_abort_catcher.Check("java_class2 == null"); } TEST_F(JniInternalTest, GetObjectRefType) { jclass local = env_->FindClass("java/lang/Object"); - ASSERT_TRUE(local != NULL); + ASSERT_TRUE(local != nullptr); EXPECT_EQ(JNILocalRefType, env_->GetObjectRefType(local)); jobject global = env_->NewGlobalRef(local); @@ -604,33 +921,38 @@ TEST_F(JniInternalTest, GetObjectRefType) { EXPECT_EQ(JNIInvalidRefType, env_->GetObjectRefType(invalid)); // TODO: invoke a native method and test that its arguments are considered local references. + + // Null as object should fail. + CheckJniAbortCatcher jni_abort_catcher; + EXPECT_EQ(JNIInvalidRefType, env_->GetObjectRefType(nullptr)); + jni_abort_catcher.Check("java_object == null"); } TEST_F(JniInternalTest, StaleWeakGlobal) { jclass java_lang_Class = env_->FindClass("java/lang/Class"); - ASSERT_TRUE(java_lang_Class != NULL); - jobjectArray local_ref = env_->NewObjectArray(1, java_lang_Class, NULL); - ASSERT_TRUE(local_ref != NULL); + ASSERT_NE(java_lang_Class, nullptr); + jobjectArray local_ref = env_->NewObjectArray(1, java_lang_Class, nullptr); + ASSERT_NE(local_ref, nullptr); jweak weak_global = env_->NewWeakGlobalRef(local_ref); - ASSERT_TRUE(weak_global != NULL); + ASSERT_NE(weak_global, nullptr); env_->DeleteLocalRef(local_ref); Runtime::Current()->GetHeap()->CollectGarbage(false); // GC should clear the weak global. jobject new_global_ref = env_->NewGlobalRef(weak_global); - EXPECT_TRUE(new_global_ref == NULL); + EXPECT_EQ(new_global_ref, nullptr); jobject new_local_ref = env_->NewLocalRef(weak_global); - EXPECT_TRUE(new_local_ref == NULL); + EXPECT_EQ(new_local_ref, nullptr); } TEST_F(JniInternalTest, NewStringUTF) { - EXPECT_TRUE(env_->NewStringUTF(NULL) == NULL); + EXPECT_EQ(env_->NewStringUTF(nullptr), nullptr); jstring s; s = env_->NewStringUTF(""); - EXPECT_TRUE(s != NULL); + EXPECT_NE(s, nullptr); EXPECT_EQ(0, env_->GetStringLength(s)); EXPECT_EQ(0, env_->GetStringUTFLength(s)); s = env_->NewStringUTF("hello"); - EXPECT_TRUE(s != NULL); + EXPECT_NE(s, nullptr); EXPECT_EQ(5, env_->GetStringLength(s)); EXPECT_EQ(5, env_->GetStringUTFLength(s)); @@ -641,11 +963,11 @@ TEST_F(JniInternalTest, NewString) { jchar chars[] = { 'h', 'i' }; jstring s; s = env_->NewString(chars, 0); - EXPECT_TRUE(s != NULL); + EXPECT_NE(s, nullptr); EXPECT_EQ(0, env_->GetStringLength(s)); EXPECT_EQ(0, env_->GetStringUTFLength(s)); s = env_->NewString(chars, 2); - EXPECT_TRUE(s != NULL); + EXPECT_NE(s, nullptr); EXPECT_EQ(2, env_->GetStringLength(s)); EXPECT_EQ(2, env_->GetStringUTFLength(s)); @@ -654,7 +976,7 @@ TEST_F(JniInternalTest, NewString) { TEST_F(JniInternalTest, NewStringNullCharsZeroLength) { jstring s = env_->NewString(nullptr, 0); - EXPECT_TRUE(s != nullptr); + EXPECT_NE(s, nullptr); EXPECT_EQ(0, env_->GetStringLength(s)); } @@ -678,16 +1000,16 @@ TEST_F(JniInternalTest, GetStringLength_GetStringUTFLength) { TEST_F(JniInternalTest, GetStringRegion_GetStringUTFRegion) { jstring s = env_->NewStringUTF("hello"); - ASSERT_TRUE(s != NULL); + ASSERT_TRUE(s != nullptr); - env_->GetStringRegion(s, -1, 0, NULL); - EXPECT_EXCEPTION(sioobe_); - env_->GetStringRegion(s, 0, -1, NULL); - EXPECT_EXCEPTION(sioobe_); - env_->GetStringRegion(s, 0, 10, NULL); - EXPECT_EXCEPTION(sioobe_); - env_->GetStringRegion(s, 10, 1, NULL); - EXPECT_EXCEPTION(sioobe_); + env_->GetStringRegion(s, -1, 0, nullptr); + ExpectException(sioobe_); + env_->GetStringRegion(s, 0, -1, nullptr); + ExpectException(sioobe_); + env_->GetStringRegion(s, 0, 10, nullptr); + ExpectException(sioobe_); + env_->GetStringRegion(s, 10, 1, nullptr); + ExpectException(sioobe_); jchar chars[4] = { 'x', 'x', 'x', 'x' }; env_->GetStringRegion(s, 1, 2, &chars[1]); @@ -696,20 +1018,20 @@ TEST_F(JniInternalTest, GetStringRegion_GetStringUTFRegion) { EXPECT_EQ('l', chars[2]); EXPECT_EQ('x', chars[3]); - // It's okay for the buffer to be NULL as long as the length is 0. - env_->GetStringRegion(s, 2, 0, NULL); + // It's okay for the buffer to be nullptr as long as the length is 0. + env_->GetStringRegion(s, 2, 0, nullptr); // Even if the offset is invalid... - env_->GetStringRegion(s, 123, 0, NULL); - EXPECT_EXCEPTION(sioobe_); - - env_->GetStringUTFRegion(s, -1, 0, NULL); - EXPECT_EXCEPTION(sioobe_); - env_->GetStringUTFRegion(s, 0, -1, NULL); - EXPECT_EXCEPTION(sioobe_); - env_->GetStringUTFRegion(s, 0, 10, NULL); - EXPECT_EXCEPTION(sioobe_); - env_->GetStringUTFRegion(s, 10, 1, NULL); - EXPECT_EXCEPTION(sioobe_); + env_->GetStringRegion(s, 123, 0, nullptr); + ExpectException(sioobe_); + + env_->GetStringUTFRegion(s, -1, 0, nullptr); + ExpectException(sioobe_); + env_->GetStringUTFRegion(s, 0, -1, nullptr); + ExpectException(sioobe_); + env_->GetStringUTFRegion(s, 0, 10, nullptr); + ExpectException(sioobe_); + env_->GetStringUTFRegion(s, 10, 1, nullptr); + ExpectException(sioobe_); char bytes[4] = { 'x', 'x', 'x', 'x' }; env_->GetStringUTFRegion(s, 1, 2, &bytes[1]); @@ -718,25 +1040,25 @@ TEST_F(JniInternalTest, GetStringRegion_GetStringUTFRegion) { EXPECT_EQ('l', bytes[2]); EXPECT_EQ('x', bytes[3]); - // It's okay for the buffer to be NULL as long as the length is 0. - env_->GetStringUTFRegion(s, 2, 0, NULL); + // It's okay for the buffer to be nullptr as long as the length is 0. + env_->GetStringUTFRegion(s, 2, 0, nullptr); // Even if the offset is invalid... - env_->GetStringUTFRegion(s, 123, 0, NULL); - EXPECT_EXCEPTION(sioobe_); + env_->GetStringUTFRegion(s, 123, 0, nullptr); + ExpectException(sioobe_); } TEST_F(JniInternalTest, GetStringUTFChars_ReleaseStringUTFChars) { - // Passing in a NULL jstring is ignored normally, but caught by -Xcheck:jni. + // Passing in a nullptr jstring is ignored normally, but caught by -Xcheck:jni. { CheckJniAbortCatcher check_jni_abort_catcher; - EXPECT_TRUE(env_->GetStringUTFChars(NULL, NULL) == NULL); + EXPECT_EQ(env_->GetStringUTFChars(nullptr, nullptr), nullptr); check_jni_abort_catcher.Check("GetStringUTFChars received null jstring"); } jstring s = env_->NewStringUTF("hello"); - ASSERT_TRUE(s != NULL); + ASSERT_TRUE(s != nullptr); - const char* utf = env_->GetStringUTFChars(s, NULL); + const char* utf = env_->GetStringUTFChars(s, nullptr); EXPECT_STREQ("hello", utf); env_->ReleaseStringUTFChars(s, utf); @@ -749,10 +1071,10 @@ TEST_F(JniInternalTest, GetStringUTFChars_ReleaseStringUTFChars) { TEST_F(JniInternalTest, GetStringChars_ReleaseStringChars) { jstring s = env_->NewStringUTF("hello"); - ASSERT_TRUE(s != NULL); + ASSERT_TRUE(s != nullptr); jchar expected[] = { 'h', 'e', 'l', 'l', 'o' }; - const jchar* chars = env_->GetStringChars(s, NULL); + const jchar* chars = env_->GetStringChars(s, nullptr); EXPECT_EQ(expected[0], chars[0]); EXPECT_EQ(expected[1], chars[1]); EXPECT_EQ(expected[2], chars[2]); @@ -773,10 +1095,10 @@ TEST_F(JniInternalTest, GetStringChars_ReleaseStringChars) { TEST_F(JniInternalTest, GetStringCritical_ReleaseStringCritical) { jstring s = env_->NewStringUTF("hello"); - ASSERT_TRUE(s != NULL); + ASSERT_TRUE(s != nullptr); jchar expected[] = { 'h', 'e', 'l', 'l', 'o' }; - const jchar* chars = env_->GetStringCritical(s, NULL); + const jchar* chars = env_->GetStringCritical(s, nullptr); EXPECT_EQ(expected[0], chars[0]); EXPECT_EQ(expected[1], chars[1]); EXPECT_EQ(expected[2], chars[2]); @@ -798,45 +1120,72 @@ TEST_F(JniInternalTest, GetStringCritical_ReleaseStringCritical) { TEST_F(JniInternalTest, GetObjectArrayElement_SetObjectArrayElement) { jclass java_lang_Class = env_->FindClass("java/lang/Class"); - ASSERT_TRUE(java_lang_Class != NULL); + ASSERT_TRUE(java_lang_Class != nullptr); - jobjectArray array = env_->NewObjectArray(1, java_lang_Class, NULL); - EXPECT_TRUE(array != NULL); - EXPECT_TRUE(env_->GetObjectArrayElement(array, 0) == NULL); + jobjectArray array = env_->NewObjectArray(1, java_lang_Class, nullptr); + EXPECT_NE(array, nullptr); + EXPECT_EQ(env_->GetObjectArrayElement(array, 0), nullptr); env_->SetObjectArrayElement(array, 0, java_lang_Class); EXPECT_TRUE(env_->IsSameObject(env_->GetObjectArrayElement(array, 0), java_lang_Class)); // ArrayIndexOutOfBounds for negative index. env_->SetObjectArrayElement(array, -1, java_lang_Class); - EXPECT_EXCEPTION(aioobe_); + ExpectException(aioobe_); // ArrayIndexOutOfBounds for too-large index. env_->SetObjectArrayElement(array, 1, java_lang_Class); - EXPECT_EXCEPTION(aioobe_); + ExpectException(aioobe_); // ArrayStoreException thrown for bad types. env_->SetObjectArrayElement(array, 0, env_->NewStringUTF("not a jclass!")); - EXPECT_EXCEPTION(ase_); + ExpectException(ase_); + + // Null as array should fail. + CheckJniAbortCatcher jni_abort_catcher; + EXPECT_EQ(nullptr, env_->GetObjectArrayElement(nullptr, 0)); + jni_abort_catcher.Check("java_array == null"); + env_->SetObjectArrayElement(nullptr, 0, nullptr); + jni_abort_catcher.Check("java_array == null"); } #define EXPECT_STATIC_PRIMITIVE_FIELD(type, field_name, sig, value1, value2) \ do { \ jfieldID fid = env_->GetStaticFieldID(c, field_name, sig); \ - EXPECT_TRUE(fid != NULL); \ + EXPECT_NE(fid, nullptr); \ env_->SetStatic ## type ## Field(c, fid, value1); \ - EXPECT_TRUE(value1 == env_->GetStatic ## type ## Field(c, fid)); \ + EXPECT_EQ(value1, env_->GetStatic ## type ## Field(c, fid)); \ env_->SetStatic ## type ## Field(c, fid, value2); \ - EXPECT_TRUE(value2 == env_->GetStatic ## type ## Field(c, fid)); \ + EXPECT_EQ(value2, env_->GetStatic ## type ## Field(c, fid)); \ + \ + CheckJniAbortCatcher jni_abort_catcher; \ + env_->GetStatic ## type ## Field(nullptr, fid); \ + jni_abort_catcher.Check("received null jclass"); \ + env_->SetStatic ## type ## Field(nullptr, fid, value1); \ + jni_abort_catcher.Check("received null jclass"); \ + env_->GetStatic ## type ## Field(c, nullptr); \ + jni_abort_catcher.Check("fid == null"); \ + env_->SetStatic ## type ## Field(c, nullptr, value1); \ + jni_abort_catcher.Check("fid == null"); \ } while (false) #define EXPECT_PRIMITIVE_FIELD(instance, type, field_name, sig, value1, value2) \ do { \ jfieldID fid = env_->GetFieldID(c, field_name, sig); \ - EXPECT_TRUE(fid != NULL); \ + EXPECT_NE(fid, nullptr); \ env_->Set ## type ## Field(instance, fid, value1); \ - EXPECT_TRUE(value1 == env_->Get ## type ## Field(instance, fid)); \ + EXPECT_EQ(value1, env_->Get ## type ## Field(instance, fid)); \ env_->Set ## type ## Field(instance, fid, value2); \ - EXPECT_TRUE(value2 == env_->Get ## type ## Field(instance, fid)); \ + EXPECT_EQ(value2, env_->Get ## type ## Field(instance, fid)); \ + \ + CheckJniAbortCatcher jni_abort_catcher; \ + env_->Get ## type ## Field(nullptr, fid); \ + jni_abort_catcher.Check("obj == null"); \ + env_->Set ## type ## Field(nullptr, fid, value1); \ + jni_abort_catcher.Check("obj == null"); \ + env_->Get ## type ## Field(instance, nullptr); \ + jni_abort_catcher.Check("fid == null"); \ + env_->Set ## type ## Field(instance, nullptr, value1); \ + jni_abort_catcher.Check("fid == null"); \ } while (false) @@ -845,14 +1194,14 @@ TEST_F(JniInternalTest, GetPrimitiveField_SetPrimitiveField) { Thread::Current()->TransitionFromSuspendedToRunnable(); LoadDex("AllFields"); bool started = runtime_->Start(); - CHECK(started); + ASSERT_TRUE(started); jclass c = env_->FindClass("AllFields"); - ASSERT_TRUE(c != NULL); + ASSERT_NE(c, nullptr); jobject o = env_->AllocObject(c); - ASSERT_TRUE(o != NULL); + ASSERT_NE(o, nullptr); - EXPECT_STATIC_PRIMITIVE_FIELD(Boolean, "sZ", "Z", true, false); + EXPECT_STATIC_PRIMITIVE_FIELD(Boolean, "sZ", "Z", JNI_TRUE, JNI_FALSE); EXPECT_STATIC_PRIMITIVE_FIELD(Byte, "sB", "B", 1, 2); EXPECT_STATIC_PRIMITIVE_FIELD(Char, "sC", "C", 'a', 'b'); EXPECT_STATIC_PRIMITIVE_FIELD(Double, "sD", "D", 1.0, 2.0); @@ -861,7 +1210,7 @@ TEST_F(JniInternalTest, GetPrimitiveField_SetPrimitiveField) { EXPECT_STATIC_PRIMITIVE_FIELD(Long, "sJ", "J", 1, 2); EXPECT_STATIC_PRIMITIVE_FIELD(Short, "sS", "S", 1, 2); - EXPECT_PRIMITIVE_FIELD(o, Boolean, "iZ", "Z", true, false); + EXPECT_PRIMITIVE_FIELD(o, Boolean, "iZ", "Z", JNI_TRUE, JNI_FALSE); EXPECT_PRIMITIVE_FIELD(o, Byte, "iB", "B", 1, 2); EXPECT_PRIMITIVE_FIELD(o, Char, "iC", "C", 'a', 'b'); EXPECT_PRIMITIVE_FIELD(o, Double, "iD", "D", 1.0, 2.0); @@ -878,19 +1227,19 @@ TEST_F(JniInternalTest, GetObjectField_SetObjectField) { runtime_->Start(); jclass c = env_->FindClass("AllFields"); - ASSERT_TRUE(c != NULL); + ASSERT_NE(c, nullptr); jobject o = env_->AllocObject(c); - ASSERT_TRUE(o != NULL); + ASSERT_NE(o, nullptr); jstring s1 = env_->NewStringUTF("hello"); - ASSERT_TRUE(s1 != NULL); + ASSERT_NE(s1, nullptr); jstring s2 = env_->NewStringUTF("world"); - ASSERT_TRUE(s2 != NULL); + ASSERT_NE(s2, nullptr); jfieldID s_fid = env_->GetStaticFieldID(c, "sObject", "Ljava/lang/Object;"); - ASSERT_TRUE(s_fid != NULL); + ASSERT_NE(s_fid, nullptr); jfieldID i_fid = env_->GetFieldID(c, "iObject", "Ljava/lang/Object;"); - ASSERT_TRUE(i_fid != NULL); + ASSERT_NE(i_fid, nullptr); env_->SetStaticObjectField(c, s_fid, s1); ASSERT_TRUE(env_->IsSameObject(s1, env_->GetStaticObjectField(c, s_fid))); @@ -903,27 +1252,27 @@ TEST_F(JniInternalTest, GetObjectField_SetObjectField) { ASSERT_TRUE(env_->IsSameObject(s2, env_->GetObjectField(o, i_fid))); } -TEST_F(JniInternalTest, NewLocalRef_NULL) { - EXPECT_TRUE(env_->NewLocalRef(NULL) == NULL); +TEST_F(JniInternalTest, NewLocalRef_nullptr) { + EXPECT_EQ(env_->NewLocalRef(nullptr), nullptr); } TEST_F(JniInternalTest, NewLocalRef) { jstring s = env_->NewStringUTF(""); - ASSERT_TRUE(s != NULL); + ASSERT_NE(s, nullptr); jobject o = env_->NewLocalRef(s); - EXPECT_TRUE(o != NULL); - EXPECT_TRUE(o != s); + EXPECT_NE(o, nullptr); + EXPECT_NE(o, s); EXPECT_EQ(JNILocalRefType, env_->GetObjectRefType(o)); } -TEST_F(JniInternalTest, DeleteLocalRef_NULL) { - env_->DeleteLocalRef(NULL); +TEST_F(JniInternalTest, DeleteLocalRef_nullptr) { + env_->DeleteLocalRef(nullptr); } TEST_F(JniInternalTest, DeleteLocalRef) { jstring s = env_->NewStringUTF(""); - ASSERT_TRUE(s != NULL); + ASSERT_NE(s, nullptr); env_->DeleteLocalRef(s); // Currently, deleting an already-deleted reference is just a CheckJNI warning. @@ -937,9 +1286,9 @@ TEST_F(JniInternalTest, DeleteLocalRef) { } s = env_->NewStringUTF(""); - ASSERT_TRUE(s != NULL); + ASSERT_NE(s, nullptr); jobject o = env_->NewLocalRef(s); - ASSERT_TRUE(o != NULL); + ASSERT_NE(o, nullptr); env_->DeleteLocalRef(s); env_->DeleteLocalRef(o); @@ -951,7 +1300,7 @@ TEST_F(JniInternalTest, PushLocalFrame_10395422) { // Android historically treated it, and it's how the RI treats it. It's also the more useful // interpretation! ASSERT_EQ(JNI_OK, env_->PushLocalFrame(0)); - env_->PopLocalFrame(NULL); + env_->PopLocalFrame(nullptr); // Negative capacities are not allowed. ASSERT_EQ(JNI_ERR, env_->PushLocalFrame(-1)); @@ -962,7 +1311,7 @@ TEST_F(JniInternalTest, PushLocalFrame_10395422) { TEST_F(JniInternalTest, PushLocalFrame_PopLocalFrame) { jobject original = env_->NewStringUTF(""); - ASSERT_TRUE(original != NULL); + ASSERT_NE(original, nullptr); jobject outer; jobject inner1, inner2; @@ -988,7 +1337,7 @@ TEST_F(JniInternalTest, PushLocalFrame_PopLocalFrame) { // gets a new local reference... EXPECT_EQ(JNIInvalidRefType, env_->GetObjectRefType(inner2)); - env_->PopLocalFrame(NULL); + env_->PopLocalFrame(nullptr); } EXPECT_EQ(JNILocalRefType, env_->GetObjectRefType(original)); EXPECT_EQ(JNIInvalidRefType, env_->GetObjectRefType(outer)); @@ -996,30 +1345,30 @@ TEST_F(JniInternalTest, PushLocalFrame_PopLocalFrame) { EXPECT_EQ(JNIInvalidRefType, env_->GetObjectRefType(inner2)); } -TEST_F(JniInternalTest, NewGlobalRef_NULL) { - EXPECT_TRUE(env_->NewGlobalRef(NULL) == NULL); +TEST_F(JniInternalTest, NewGlobalRef_nullptr) { + EXPECT_EQ(env_->NewGlobalRef(nullptr), nullptr); } TEST_F(JniInternalTest, NewGlobalRef) { jstring s = env_->NewStringUTF(""); - ASSERT_TRUE(s != NULL); + ASSERT_NE(s, nullptr); jobject o = env_->NewGlobalRef(s); - EXPECT_TRUE(o != NULL); - EXPECT_TRUE(o != s); + EXPECT_NE(o, nullptr); + EXPECT_NE(o, s); - // TODO: check that o is a global reference. + EXPECT_EQ(env_->GetObjectRefType(o), JNIGlobalRefType); } -TEST_F(JniInternalTest, DeleteGlobalRef_NULL) { - env_->DeleteGlobalRef(NULL); +TEST_F(JniInternalTest, DeleteGlobalRef_nullptr) { + env_->DeleteGlobalRef(nullptr); } TEST_F(JniInternalTest, DeleteGlobalRef) { jstring s = env_->NewStringUTF(""); - ASSERT_TRUE(s != NULL); + ASSERT_NE(s, nullptr); jobject o = env_->NewGlobalRef(s); - ASSERT_TRUE(o != NULL); + ASSERT_NE(o, nullptr); env_->DeleteGlobalRef(o); // Currently, deleting an already-deleted reference is just a CheckJNI warning. @@ -1033,38 +1382,38 @@ TEST_F(JniInternalTest, DeleteGlobalRef) { } jobject o1 = env_->NewGlobalRef(s); - ASSERT_TRUE(o1 != NULL); + ASSERT_NE(o1, nullptr); jobject o2 = env_->NewGlobalRef(s); - ASSERT_TRUE(o2 != NULL); + ASSERT_NE(o2, nullptr); env_->DeleteGlobalRef(o1); env_->DeleteGlobalRef(o2); } -TEST_F(JniInternalTest, NewWeakGlobalRef_NULL) { - EXPECT_TRUE(env_->NewWeakGlobalRef(NULL) == NULL); +TEST_F(JniInternalTest, NewWeakGlobalRef_nullptr) { + EXPECT_EQ(env_->NewWeakGlobalRef(nullptr), nullptr); } TEST_F(JniInternalTest, NewWeakGlobalRef) { jstring s = env_->NewStringUTF(""); - ASSERT_TRUE(s != NULL); + ASSERT_NE(s, nullptr); jobject o = env_->NewWeakGlobalRef(s); - EXPECT_TRUE(o != NULL); - EXPECT_TRUE(o != s); + EXPECT_NE(o, nullptr); + EXPECT_NE(o, s); - // TODO: check that o is a weak global reference. + EXPECT_EQ(env_->GetObjectRefType(o), JNIWeakGlobalRefType); } -TEST_F(JniInternalTest, DeleteWeakGlobalRef_NULL) { - env_->DeleteWeakGlobalRef(NULL); +TEST_F(JniInternalTest, DeleteWeakGlobalRef_nullptr) { + env_->DeleteWeakGlobalRef(nullptr); } TEST_F(JniInternalTest, DeleteWeakGlobalRef) { jstring s = env_->NewStringUTF(""); - ASSERT_TRUE(s != NULL); + ASSERT_NE(s, nullptr); jobject o = env_->NewWeakGlobalRef(s); - ASSERT_TRUE(o != NULL); + ASSERT_NE(o, nullptr); env_->DeleteWeakGlobalRef(o); // Currently, deleting an already-deleted reference is just a CheckJNI warning. @@ -1078,21 +1427,21 @@ TEST_F(JniInternalTest, DeleteWeakGlobalRef) { } jobject o1 = env_->NewWeakGlobalRef(s); - ASSERT_TRUE(o1 != NULL); + ASSERT_NE(o1, nullptr); jobject o2 = env_->NewWeakGlobalRef(s); - ASSERT_TRUE(o2 != NULL); + ASSERT_NE(o2, nullptr); env_->DeleteWeakGlobalRef(o1); env_->DeleteWeakGlobalRef(o2); } TEST_F(JniInternalTest, Throw) { - EXPECT_EQ(JNI_ERR, env_->Throw(NULL)); + EXPECT_EQ(JNI_ERR, env_->Throw(nullptr)); jclass exception_class = env_->FindClass("java/lang/RuntimeException"); - ASSERT_TRUE(exception_class != NULL); + ASSERT_TRUE(exception_class != nullptr); jthrowable exception = reinterpret_cast<jthrowable>(env_->AllocObject(exception_class)); - ASSERT_TRUE(exception != NULL); + ASSERT_TRUE(exception != nullptr); EXPECT_EQ(JNI_OK, env_->Throw(exception)); EXPECT_TRUE(env_->ExceptionCheck()); @@ -1102,10 +1451,10 @@ TEST_F(JniInternalTest, Throw) { } TEST_F(JniInternalTest, ThrowNew) { - EXPECT_EQ(JNI_ERR, env_->Throw(NULL)); + EXPECT_EQ(JNI_ERR, env_->Throw(nullptr)); jclass exception_class = env_->FindClass("java/lang/RuntimeException"); - ASSERT_TRUE(exception_class != NULL); + ASSERT_TRUE(exception_class != nullptr); jthrowable thrown_exception; @@ -1115,7 +1464,7 @@ TEST_F(JniInternalTest, ThrowNew) { env_->ExceptionClear(); EXPECT_TRUE(env_->IsInstanceOf(thrown_exception, exception_class)); - EXPECT_EQ(JNI_OK, env_->ThrowNew(exception_class, NULL)); + EXPECT_EQ(JNI_OK, env_->ThrowNew(exception_class, nullptr)); EXPECT_TRUE(env_->ExceptionCheck()); thrown_exception = env_->ExceptionOccurred(); env_->ExceptionClear(); @@ -1141,26 +1490,26 @@ TEST_F(JniInternalTest, NewDirectBuffer_GetDirectBufferAddress_GetDirectBufferCa ASSERT_TRUE(started); jclass buffer_class = env_->FindClass("java/nio/Buffer"); - ASSERT_TRUE(buffer_class != NULL); + ASSERT_NE(buffer_class, nullptr); char bytes[1024]; jobject buffer = env_->NewDirectByteBuffer(bytes, sizeof(bytes)); - ASSERT_TRUE(buffer != NULL); + ASSERT_NE(buffer, nullptr); ASSERT_TRUE(env_->IsInstanceOf(buffer, buffer_class)); - ASSERT_TRUE(env_->GetDirectBufferAddress(buffer) == bytes); - ASSERT_TRUE(env_->GetDirectBufferCapacity(buffer) == sizeof(bytes)); + ASSERT_EQ(env_->GetDirectBufferAddress(buffer), bytes); + ASSERT_EQ(env_->GetDirectBufferCapacity(buffer), static_cast<jlong>(sizeof(bytes))); } TEST_F(JniInternalTest, MonitorEnterExit) { - // Create an object to torture + // Create an object to torture. jclass object_class = env_->FindClass("java/lang/Object"); - ASSERT_TRUE(object_class != NULL); + ASSERT_NE(object_class, nullptr); jobject object = env_->AllocObject(object_class); - ASSERT_TRUE(object != NULL); + ASSERT_NE(object, nullptr); // Expected class of exceptions jclass imse_class = env_->FindClass("java/lang/IllegalMonitorStateException"); - ASSERT_TRUE(imse_class != NULL); + ASSERT_NE(imse_class, nullptr); jthrowable thrown_exception; @@ -1197,13 +1546,13 @@ TEST_F(JniInternalTest, MonitorEnterExit) { env_->ExceptionClear(); EXPECT_TRUE(env_->IsInstanceOf(thrown_exception, imse_class)); - // It's an error to call MonitorEnter or MonitorExit on NULL. + // It's an error to call MonitorEnter or MonitorExit on nullptr. { CheckJniAbortCatcher check_jni_abort_catcher; - env_->MonitorEnter(NULL); + env_->MonitorEnter(nullptr); check_jni_abort_catcher.Check("in call to MonitorEnter"); - env_->MonitorExit(NULL); + env_->MonitorExit(nullptr); check_jni_abort_catcher.Check("in call to MonitorExit"); } } @@ -1215,7 +1564,7 @@ TEST_F(JniInternalTest, DetachCurrentThread) { jint err = vm_->DetachCurrentThread(); EXPECT_EQ(JNI_ERR, err); - vm_->AttachCurrentThread(&env_, NULL); // need attached thread for CommonRuntimeTest::TearDown + vm_->AttachCurrentThread(&env_, nullptr); // need attached thread for CommonRuntimeTest::TearDown } } // namespace art diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h index 238506e86d..1b8106e05f 100644 --- a/runtime/mirror/array.h +++ b/runtime/mirror/array.h @@ -155,14 +155,19 @@ class MANAGED PrimitiveArray : public Array { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static void SetArrayClass(Class* array_class) { - CHECK(array_class_ == NULL); - CHECK(array_class != NULL); + CHECK(array_class_ == nullptr); + CHECK(array_class != nullptr); array_class_ = array_class; } + static Class* GetArrayClass() { + DCHECK(array_class_ != nullptr); + return array_class_; + } + static void ResetArrayClass() { - CHECK(array_class_ != NULL); - array_class_ = NULL; + CHECK(array_class_ != nullptr); + array_class_ = nullptr; } static void VisitRoots(RootCallback* callback, void* arg) diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index 50a8e47ee6..8d183dad8b 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -66,10 +66,6 @@ static jobject VMRuntime_newNonMovableArray(JNIEnv* env, jobject, jclass javaEle ThrowNullPointerException(NULL, "element class == null"); return nullptr; } - if (UNLIKELY(element_class->IsPrimitiveVoid())) { - ThrowIllegalArgumentException(NULL, "Can't allocate an array of void"); - return nullptr; - } Runtime* runtime = Runtime::Current(); mirror::Class* array_class = runtime->GetClassLinker()->FindArrayClass(soa.Self(), element_class); if (UNLIKELY(array_class == nullptr)) { @@ -93,10 +89,6 @@ static jobject VMRuntime_newUnpaddedArray(JNIEnv* env, jobject, jclass javaEleme ThrowNullPointerException(NULL, "element class == null"); return nullptr; } - if (UNLIKELY(element_class->IsPrimitiveVoid())) { - ThrowIllegalArgumentException(NULL, "Can't allocate an array of void"); - return nullptr; - } Runtime* runtime = Runtime::Current(); mirror::Class* array_class = runtime->GetClassLinker()->FindArrayClass(soa.Self(), element_class); if (UNLIKELY(array_class == nullptr)) { diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index 623b0c6b22..55b6a270e0 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -689,9 +689,7 @@ bool ParsedOptions::Parse(const Runtime::Options& options, bool ignore_unrecogni if (compiler_callbacks_ == nullptr && image_.empty()) { image_ += GetAndroidRoot(); - image_ += "/framework/boot-"; - image_ += GetInstructionSetString(image_isa_); - image_ += ".art"; + image_ += "/framework/boot.art"; } if (heap_growth_limit_ == 0) { heap_growth_limit_ = heap_maximum_size_; |