diff options
author | 2015-09-27 19:50:40 +0000 | |
---|---|---|
committer | 2015-09-27 19:50:40 +0000 | |
commit | 7bbb80ab52c203e44d2ded2c947b3b03b4b31ec4 (patch) | |
tree | eb54c557467f1401c4bd5729fa2b9e8ae91e8ffd | |
parent | b72123440d8541362ebdb131436f9dbdda5fd329 (diff) |
Revert "lambda: Experimental support for capture-variable and liberate-variable"
Test fails.
This reverts commit b72123440d8541362ebdb131436f9dbdda5fd329.
Change-Id: Ic9ed92f8c826d8465eb36b746dc44af05caf041c
26 files changed, 172 insertions, 919 deletions
diff --git a/compiler/dex/quick/quick_compiler.cc b/compiler/dex/quick/quick_compiler.cc index 1cd742abac..b5ecf9c418 100644 --- a/compiler/dex/quick/quick_compiler.cc +++ b/compiler/dex/quick/quick_compiler.cc @@ -391,9 +391,9 @@ static int kAllOpcodes[] = { Instruction::IGET_SHORT_QUICK, Instruction::INVOKE_LAMBDA, Instruction::UNUSED_F4, - Instruction::CAPTURE_VARIABLE, + Instruction::UNUSED_F5, Instruction::CREATE_LAMBDA, - Instruction::LIBERATE_VARIABLE, + Instruction::UNUSED_F7, Instruction::BOX_LAMBDA, Instruction::UNBOX_LAMBDA, Instruction::UNUSED_FA, diff --git a/runtime/Android.mk b/runtime/Android.mk index 059c4cdd57..995a1d5c0d 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -104,7 +104,6 @@ LIBART_COMMON_SRC_FILES := \ lambda/box_table.cc \ lambda/closure.cc \ lambda/closure_builder.cc \ - lambda/leaking_allocator.cc \ jni_internal.cc \ jobject_comparator.cc \ linear_alloc.cc \ diff --git a/runtime/dex_instruction-inl.h b/runtime/dex_instruction-inl.h index e160a103d9..7344d13805 100644 --- a/runtime/dex_instruction-inl.h +++ b/runtime/dex_instruction-inl.h @@ -454,8 +454,8 @@ inline bool Instruction::HasVarArgs25x() const { return FormatOf(Opcode()) == k25x; } -// Copies all of the parameter registers into the arg array. Check the length with VRegB_25x()+2. -inline void Instruction::GetAllArgs25x(uint32_t (&arg)[kMaxVarArgRegs25x]) const { +// Copies all of the parameter registers into the arg array. Check the length with VRegB_25x()+1. +inline void Instruction::GetAllArgs25x(uint32_t arg[kMaxVarArgRegs]) const { DCHECK_EQ(FormatOf(Opcode()), k25x); /* @@ -500,21 +500,19 @@ inline void Instruction::GetAllArgs25x(uint32_t (&arg)[kMaxVarArgRegs25x]) const */ switch (count) { case 4: - arg[5] = (Fetch16(0) >> 8) & 0x0f; // vG + arg[4] = (Fetch16(0) >> 8) & 0x0f; // vG FALLTHROUGH_INTENDED; case 3: - arg[4] = (reg_list >> 12) & 0x0f; // vF + arg[3] = (reg_list >> 12) & 0x0f; // vF FALLTHROUGH_INTENDED; case 2: - arg[3] = (reg_list >> 8) & 0x0f; // vE + arg[2] = (reg_list >> 8) & 0x0f; // vE FALLTHROUGH_INTENDED; case 1: - arg[2] = (reg_list >> 4) & 0x0f; // vD + arg[1] = (reg_list >> 4) & 0x0f; // vD FALLTHROUGH_INTENDED; default: // case 0 - // The required lambda 'this' is actually a pair, but the pair is implicit. arg[0] = VRegC_25x(); // vC - arg[1] = arg[0] + 1; // vC + 1 break; } } diff --git a/runtime/dex_instruction.cc b/runtime/dex_instruction.cc index 5250b0d79b..fc4df1475a 100644 --- a/runtime/dex_instruction.cc +++ b/runtime/dex_instruction.cc @@ -322,10 +322,10 @@ std::string Instruction::DumpString(const DexFile* file) const { } case k25x: { if (Opcode() == INVOKE_LAMBDA) { - uint32_t arg[kMaxVarArgRegs25x]; + uint32_t arg[kMaxVarArgRegs]; GetAllArgs25x(arg); const size_t num_extra_var_args = VRegB_25x(); - DCHECK_LE(num_extra_var_args + 2, arraysize(arg)); + DCHECK_LE(num_extra_var_args + 1, kMaxVarArgRegs); // invoke-lambda vC, {vD, vE, vF, vG} os << opcode << " v" << arg[0] << ", {"; @@ -333,7 +333,7 @@ std::string Instruction::DumpString(const DexFile* file) const { if (i != 0) { os << ", "; } - os << "v" << arg[i+2]; // Don't print the pair of vC registers. Pair is implicit. + os << "v" << arg[i+1]; } os << "}"; break; diff --git a/runtime/dex_instruction.h b/runtime/dex_instruction.h index 48a12e53af..df2d3799ab 100644 --- a/runtime/dex_instruction.h +++ b/runtime/dex_instruction.h @@ -180,11 +180,9 @@ class Instruction { kVerifyVarArgRangeNonZero = 0x100000, kVerifyRuntimeOnly = 0x200000, kVerifyError = 0x400000, - kVerifyRegCString = 0x800000, }; static constexpr uint32_t kMaxVarArgRegs = 5; - static constexpr uint32_t kMaxVarArgRegs25x = 6; // lambdas are 2 registers. // Returns the size (in 2 byte code units) of this instruction. size_t SizeInCodeUnits() const { @@ -410,7 +408,7 @@ class Instruction { void GetVarArgs(uint32_t args[kMaxVarArgRegs]) const { return GetVarArgs(args, Fetch16(0)); } - void GetAllArgs25x(uint32_t (&args)[kMaxVarArgRegs25x]) const; + void GetAllArgs25x(uint32_t args[kMaxVarArgRegs]) const; // Returns the opcode field of the instruction. The given "inst_data" parameter must be the first // 16 bits of instruction. @@ -538,7 +536,7 @@ class Instruction { int GetVerifyTypeArgumentC() const { return (kInstructionVerifyFlags[Opcode()] & (kVerifyRegC | kVerifyRegCField | - kVerifyRegCNewArray | kVerifyRegCType | kVerifyRegCWide | kVerifyRegCString)); + kVerifyRegCNewArray | kVerifyRegCType | kVerifyRegCWide)); } int GetVerifyExtraFlags() const { diff --git a/runtime/dex_instruction_list.h b/runtime/dex_instruction_list.h index 9d7e0c4409..a176772a84 100644 --- a/runtime/dex_instruction_list.h +++ b/runtime/dex_instruction_list.h @@ -263,10 +263,10 @@ V(0xF2, IGET_SHORT_QUICK, "iget-short-quick", k22c, true, kIndexFieldOffset, kContinue | kThrow | kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \ V(0xF3, INVOKE_LAMBDA, "invoke-lambda", k25x, false, kIndexNone, kContinue | kThrow | kInvoke | kExperimental, kVerifyRegC /*TODO: | kVerifyVarArg*/) \ V(0xF4, UNUSED_F4, "unused-f4", k10x, false, kIndexUnknown, 0, kVerifyError) \ - V(0xF5, CAPTURE_VARIABLE, "capture-variable", k21c, false, kIndexStringRef, kExperimental, kVerifyRegA | kVerifyRegBString) \ + V(0xF5, UNUSED_F5, "unused-f5", k10x, false, kIndexUnknown, 0, kVerifyError) \ /* TODO(iam): get rid of the unused 'false' column */ \ V(0xF6, CREATE_LAMBDA, "create-lambda", k21c, false_UNUSED, kIndexMethodRef, kContinue | kThrow | kExperimental, kVerifyRegA | kVerifyRegBMethod) \ - V(0xF7, LIBERATE_VARIABLE, "liberate-variable", k22c, false, kIndexStringRef, kExperimental, kVerifyRegA | kVerifyRegB | kVerifyRegCString) \ + V(0xF7, UNUSED_F7, "unused-f7", k10x, false, kIndexUnknown, 0, kVerifyError) \ V(0xF8, BOX_LAMBDA, "box-lambda", k22x, true, kIndexNone, kContinue | kExperimental, kVerifyRegA | kVerifyRegB) \ V(0xF9, UNBOX_LAMBDA, "unbox-lambda", k22c, true, kIndexTypeRef, kContinue | kThrow | kExperimental, kVerifyRegA | kVerifyRegB | kVerifyRegCType) \ V(0xFA, UNUSED_FA, "unused-fa", k10x, false, kIndexUnknown, 0, kVerifyError) \ diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index c1af2efcbf..68d56f5198 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -27,9 +27,6 @@ namespace art { namespace interpreter { -// All lambda closures have to be a consecutive pair of virtual registers. -static constexpr size_t kLambdaVirtualRegisterWidth = 2; - void ThrowNullPointerExceptionFromInterpreter() { ThrowNullPointerExceptionFromDexPC(); } @@ -486,16 +483,13 @@ void AbortTransactionV(Thread* self, const char* fmt, va_list args) { } // Separate declaration is required solely for the attributes. -template <bool is_range, - bool do_assignability_check, - size_t kVarArgMax> - SHARED_REQUIRES(Locks::mutator_lock_) +template<bool is_range, bool do_assignability_check> SHARED_REQUIRES(Locks::mutator_lock_) static inline bool DoCallCommon(ArtMethod* called_method, Thread* self, ShadowFrame& shadow_frame, JValue* result, uint16_t number_of_inputs, - uint32_t (&arg)[kVarArgMax], + uint32_t arg[Instruction::kMaxVarArgRegs], uint32_t vregC) ALWAYS_INLINE; SHARED_REQUIRES(Locks::mutator_lock_) @@ -515,15 +509,13 @@ static inline bool NeedsInterpreter(Thread* self, ShadowFrame* new_shadow_frame) Dbg::IsForcedInterpreterNeededForCalling(self, target); } -template <bool is_range, - bool do_assignability_check, - size_t kVarArgMax> +template<bool is_range, bool do_assignability_check> static inline bool DoCallCommon(ArtMethod* called_method, Thread* self, ShadowFrame& shadow_frame, JValue* result, uint16_t number_of_inputs, - uint32_t (&arg)[kVarArgMax], + uint32_t arg[Instruction::kMaxVarArgRegs], uint32_t vregC) { bool string_init = false; // Replace calls to String.<init> with equivalent StringFactory call. @@ -568,10 +560,10 @@ static inline bool DoCallCommon(ArtMethod* called_method, number_of_inputs--; // Rewrite the var-args, dropping the 0th argument ("this") - for (uint32_t i = 1; i < arraysize(arg); ++i) { + for (uint32_t i = 1; i < Instruction::kMaxVarArgRegs; ++i) { arg[i - 1] = arg[i]; } - arg[arraysize(arg) - 1] = 0; + arg[Instruction::kMaxVarArgRegs - 1] = 0; // Rewrite the non-var-arg case vregC++; // Skips the 0th vreg in the range ("this"). @@ -677,7 +669,7 @@ static inline bool DoCallCommon(ArtMethod* called_method, AssignRegister(new_shadow_frame, shadow_frame, dest_reg, src_reg); } } else { - DCHECK_LE(number_of_inputs, arraysize(arg)); + DCHECK_LE(number_of_inputs, Instruction::kMaxVarArgRegs); for (; arg_index < number_of_inputs; ++arg_index) { AssignRegister(new_shadow_frame, shadow_frame, first_dest_reg + arg_index, arg[arg_index]); @@ -740,13 +732,12 @@ bool DoLambdaCall(ArtMethod* called_method, Thread* self, ShadowFrame& shadow_fr const Instruction* inst, uint16_t inst_data, JValue* result) { const uint4_t num_additional_registers = inst->VRegB_25x(); // Argument word count. - const uint16_t number_of_inputs = num_additional_registers + kLambdaVirtualRegisterWidth; - // The lambda closure register is always present and is not encoded in the count. - // Furthermore, the lambda closure register is always wide, so it counts as 2 inputs. + const uint16_t number_of_inputs = num_additional_registers + 1; + // The first input register is always present and is not encoded in the count. // TODO: find a cleaner way to separate non-range and range information without duplicating // code. - uint32_t arg[Instruction::kMaxVarArgRegs25x]; // only used in invoke-XXX. + uint32_t arg[Instruction::kMaxVarArgRegs]; // only used in invoke-XXX. uint32_t vregC = 0; // only used in invoke-XXX-range. if (is_range) { vregC = inst->VRegC_3rc(); @@ -772,7 +763,7 @@ bool DoCall(ArtMethod* called_method, Thread* self, ShadowFrame& shadow_frame, // TODO: find a cleaner way to separate non-range and range information without duplicating // code. - uint32_t arg[Instruction::kMaxVarArgRegs] = {}; // only used in invoke-XXX. + uint32_t arg[Instruction::kMaxVarArgRegs]; // only used in invoke-XXX. uint32_t vregC = 0; if (is_range) { vregC = inst->VRegC_3rc(); diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index f57bddbb4f..7398778d15 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -34,12 +34,7 @@ #include "dex_instruction-inl.h" #include "entrypoints/entrypoint_utils-inl.h" #include "handle_scope-inl.h" -#include "lambda/art_lambda_method.h" #include "lambda/box_table.h" -#include "lambda/closure.h" -#include "lambda/closure_builder-inl.h" -#include "lambda/leaking_allocator.h" -#include "lambda/shorty_field_type.h" #include "mirror/class-inl.h" #include "mirror/method.h" #include "mirror/object-inl.h" @@ -138,44 +133,32 @@ static inline bool IsValidLambdaTargetOrThrow(ArtMethod* called_method) return success; } -// Write out the 'Closure*' into vreg and vreg+1, as if it was a jlong. +// Write out the 'ArtMethod*' into vreg and vreg+1 static inline void WriteLambdaClosureIntoVRegs(ShadowFrame& shadow_frame, - const lambda::Closure* lambda_closure, + const ArtMethod& called_method, uint32_t vreg) { // Split the method into a lo and hi 32 bits so we can encode them into 2 virtual registers. - uint32_t closure_lo = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(lambda_closure)); - uint32_t closure_hi = static_cast<uint32_t>(reinterpret_cast<uint64_t>(lambda_closure) + uint32_t called_method_lo = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(&called_method)); + uint32_t called_method_hi = static_cast<uint32_t>(reinterpret_cast<uint64_t>(&called_method) >> BitSizeOf<uint32_t>()); // Use uint64_t instead of uintptr_t to allow shifting past the max on 32-bit. static_assert(sizeof(uint64_t) >= sizeof(uintptr_t), "Impossible"); - DCHECK_NE(closure_lo | closure_hi, 0u); + DCHECK_NE(called_method_lo | called_method_hi, 0u); - shadow_frame.SetVReg(vreg, closure_lo); - shadow_frame.SetVReg(vreg + 1, closure_hi); + shadow_frame.SetVReg(vreg, called_method_lo); + shadow_frame.SetVReg(vreg + 1, called_method_hi); } // Handles create-lambda instructions. // Returns true on success, otherwise throws an exception and returns false. // (Exceptions are thrown by creating a new exception and then being put in the thread TLS) // -// The closure must be allocated big enough to hold the data, and should not be -// pre-initialized. It is initialized with the actual captured variables as a side-effect, -// although this should be unimportant to the caller since this function also handles storing it to -// the ShadowFrame. -// // As a work-in-progress implementation, this shoves the ArtMethod object corresponding // to the target dex method index into the target register vA and vA + 1. template<bool do_access_check> -static inline bool DoCreateLambda(Thread* self, - const Instruction* inst, - /*inout*/ShadowFrame& shadow_frame, - /*inout*/lambda::ClosureBuilder* closure_builder, - /*inout*/lambda::Closure* uninitialized_closure) { - DCHECK(closure_builder != nullptr); - DCHECK(uninitialized_closure != nullptr); - DCHECK_ALIGNED(uninitialized_closure, alignof(lambda::Closure)); - +static inline bool DoCreateLambda(Thread* self, ShadowFrame& shadow_frame, + const Instruction* inst) { /* * create-lambda is opcode 0x21c * - vA is the target register where the closure will be stored into @@ -188,69 +171,16 @@ static inline bool DoCreateLambda(Thread* self, ArtMethod* const called_method = FindMethodFromCode<kStatic, do_access_check>( method_idx, &receiver, sf_method, self); - uint32_t vreg_dest_closure = inst->VRegA_21c(); + uint32_t vregA = inst->VRegA_21c(); if (UNLIKELY(!IsValidLambdaTargetOrThrow(called_method))) { CHECK(self->IsExceptionPending()); - shadow_frame.SetVReg(vreg_dest_closure, 0u); - shadow_frame.SetVReg(vreg_dest_closure + 1, 0u); + shadow_frame.SetVReg(vregA, 0u); + shadow_frame.SetVReg(vregA + 1, 0u); return false; } - lambda::ArtLambdaMethod* initialized_lambda_method; - // Initialize the ArtLambdaMethod with the right data. - { - lambda::ArtLambdaMethod* uninitialized_lambda_method = - reinterpret_cast<lambda::ArtLambdaMethod*>( - lambda::LeakingAllocator::AllocateMemory(self, sizeof(lambda::ArtLambdaMethod))); - - std::string captured_variables_shorty = closure_builder->GetCapturedVariableShortyTypes(); - std::string captured_variables_long_type_desc; - - // Synthesize a long type descriptor from the short one. - for (char shorty : captured_variables_shorty) { - lambda::ShortyFieldType shorty_field_type(shorty); - if (shorty_field_type.IsObject()) { - // Not the true type, but good enough until we implement verifier support. - captured_variables_long_type_desc += "Ljava/lang/Object;"; - UNIMPLEMENTED(FATAL) << "create-lambda with an object captured variable"; - } else if (shorty_field_type.IsLambda()) { - // Not the true type, but good enough until we implement verifier support. - captured_variables_long_type_desc += "Ljava/lang/Runnable;"; - UNIMPLEMENTED(FATAL) << "create-lambda with a lambda captured variable"; - } else { - // The primitive types have the same length shorty or not, so this is always correct. - DCHECK(shorty_field_type.IsPrimitive()); - captured_variables_long_type_desc += shorty_field_type; - } - } - - // Copy strings to dynamically allocated storage. This leaks, but that's ok. Fix it later. - // TODO: Strings need to come from the DexFile, so they won't need their own allocations. - char* captured_variables_type_desc = lambda::LeakingAllocator::MakeFlexibleInstance<char>( - self, - captured_variables_long_type_desc.size() + 1); - strcpy(captured_variables_type_desc, captured_variables_long_type_desc.c_str()); - char* captured_variables_shorty_copy = lambda::LeakingAllocator::MakeFlexibleInstance<char>( - self, - captured_variables_shorty.size() + 1); - strcpy(captured_variables_shorty_copy, captured_variables_shorty.c_str()); - - new (uninitialized_lambda_method) lambda::ArtLambdaMethod(called_method, - captured_variables_type_desc, - captured_variables_shorty_copy, - true); // innate lambda - initialized_lambda_method = uninitialized_lambda_method; - } - - // Write all the closure captured variables and the closure header into the closure. - lambda::Closure* initialized_closure; - { - initialized_closure = - closure_builder->CreateInPlace(uninitialized_closure, initialized_lambda_method); - } - - WriteLambdaClosureIntoVRegs(/*inout*/shadow_frame, initialized_closure, vreg_dest_closure); + WriteLambdaClosureIntoVRegs(shadow_frame, *called_method, vregA); return true; } @@ -259,11 +189,13 @@ static inline bool DoCreateLambda(Thread* self, // Validates that the art method points to a valid lambda function, otherwise throws // an exception and returns null. // (Exceptions are thrown by creating a new exception and then being put in the thread TLS) -static inline lambda::Closure* ReadLambdaClosureFromVRegsOrThrow(ShadowFrame& shadow_frame, - uint32_t vreg) +static inline ArtMethod* ReadLambdaClosureFromVRegsOrThrow(ShadowFrame& shadow_frame, + uint32_t vreg) SHARED_REQUIRES(Locks::mutator_lock_) { - // Lambda closures take up a consecutive pair of 2 virtual registers. - // On 32-bit the high bits are always 0. + // TODO(iam): Introduce a closure abstraction that will contain the captured variables + // instead of just an ArtMethod. + // This is temporarily using 2 vregs because a native ArtMethod can be up to 64-bit, + // but once proper variable capture is implemented it will only use 1 vreg. uint32_t vc_value_lo = shadow_frame.GetVReg(vreg); uint32_t vc_value_hi = shadow_frame.GetVReg(vreg + 1); @@ -272,285 +204,17 @@ static inline lambda::Closure* ReadLambdaClosureFromVRegsOrThrow(ShadowFrame& sh // Use uint64_t instead of uintptr_t to allow left-shifting past the max on 32-bit. static_assert(sizeof(uint64_t) >= sizeof(uintptr_t), "Impossible"); - lambda::Closure* const lambda_closure = reinterpret_cast<lambda::Closure*>(vc_value_ptr); - DCHECK_ALIGNED(lambda_closure, alignof(lambda::Closure)); + ArtMethod* const called_method = reinterpret_cast<ArtMethod* const>(vc_value_ptr); // Guard against the user passing a null closure, which is odd but (sadly) semantically valid. - if (UNLIKELY(lambda_closure == nullptr)) { + if (UNLIKELY(called_method == nullptr)) { ThrowNullPointerExceptionFromInterpreter(); return nullptr; - } else if (UNLIKELY(!IsValidLambdaTargetOrThrow(lambda_closure->GetTargetMethod()))) { - // Sanity check against data corruption. + } else if (UNLIKELY(!IsValidLambdaTargetOrThrow(called_method))) { return nullptr; } - return lambda_closure; -} - -// Forward declaration for lock annotations. See below for documentation. -template <bool do_access_check> -static inline const char* GetStringDataByDexStringIndexOrThrow(ShadowFrame& shadow_frame, - uint32_t string_idx) - SHARED_REQUIRES(Locks::mutator_lock_); - -// Find the c-string data corresponding to a dex file's string index. -// Otherwise, returns null if not found and throws a VerifyError. -// -// Note that with do_access_check=false, we never return null because the verifier -// must guard against invalid string indices. -// (Exceptions are thrown by creating a new exception and then being put in the thread TLS) -template <bool do_access_check> -static inline const char* GetStringDataByDexStringIndexOrThrow(ShadowFrame& shadow_frame, - uint32_t string_idx) { - ArtMethod* method = shadow_frame.GetMethod(); - const DexFile* dex_file = method->GetDexFile(); - - mirror::Class* declaring_class = method->GetDeclaringClass(); - if (!do_access_check) { - // MethodVerifier refuses methods with string_idx out of bounds. - DCHECK_LT(string_idx, declaring_class->GetDexCache()->NumStrings()); - } else { - // Access checks enabled: perform string index bounds ourselves. - if (string_idx >= dex_file->GetHeader().string_ids_size_) { - ThrowVerifyError(declaring_class, "String index '%" PRIu32 "' out of bounds", - string_idx); - return nullptr; - } - } - - const char* type_string = dex_file->StringDataByIdx(string_idx); - - if (UNLIKELY(type_string == nullptr)) { - CHECK_EQ(false, do_access_check) - << " verifier should've caught invalid string index " << string_idx; - CHECK_EQ(true, do_access_check) - << " string idx size check should've caught invalid string index " << string_idx; - } - - return type_string; -} - -// Handles capture-variable instructions. -// Returns true on success, otherwise throws an exception and returns false. -// (Exceptions are thrown by creating a new exception and then being put in the thread TLS) -template<bool do_access_check> -static inline bool DoCaptureVariable(Thread* self, - const Instruction* inst, - /*inout*/ShadowFrame& shadow_frame, - /*inout*/lambda::ClosureBuilder* closure_builder) { - DCHECK(closure_builder != nullptr); - using lambda::ShortyFieldType; - /* - * capture-variable is opcode 0xf6, fmt 0x21c - * - vA is the source register of the variable that will be captured - * - vB is the string ID of the variable's type that will be captured - */ - const uint32_t source_vreg = inst->VRegA_21c(); - const uint32_t string_idx = inst->VRegB_21c(); - // TODO: this should be a proper [type id] instead of a [string ID] pointing to a type. - - const char* type_string = GetStringDataByDexStringIndexOrThrow<do_access_check>(shadow_frame, - string_idx); - if (UNLIKELY(type_string == nullptr)) { - CHECK(self->IsExceptionPending()); - return false; - } - - char type_first_letter = type_string[0]; - ShortyFieldType shorty_type; - if (do_access_check && - UNLIKELY(!ShortyFieldType::MaybeCreate(type_first_letter, /*out*/&shorty_type))) { // NOLINT: [whitespace/comma] [3] - ThrowVerifyError(shadow_frame.GetMethod()->GetDeclaringClass(), - "capture-variable vB must be a valid type"); - return false; - } else { - // Already verified that the type is valid. - shorty_type = ShortyFieldType(type_first_letter); - } - - const size_t captured_variable_count = closure_builder->GetCaptureCount(); - - // Note: types are specified explicitly so that the closure is packed tightly. - switch (shorty_type) { - case ShortyFieldType::kBoolean: { - uint32_t primitive_narrow_value = shadow_frame.GetVReg(source_vreg); - closure_builder->CaptureVariablePrimitive<bool>(primitive_narrow_value); - break; - } - case ShortyFieldType::kByte: { - uint32_t primitive_narrow_value = shadow_frame.GetVReg(source_vreg); - closure_builder->CaptureVariablePrimitive<int8_t>(primitive_narrow_value); - break; - } - case ShortyFieldType::kChar: { - uint32_t primitive_narrow_value = shadow_frame.GetVReg(source_vreg); - closure_builder->CaptureVariablePrimitive<uint16_t>(primitive_narrow_value); - break; - } - case ShortyFieldType::kShort: { - uint32_t primitive_narrow_value = shadow_frame.GetVReg(source_vreg); - closure_builder->CaptureVariablePrimitive<int16_t>(primitive_narrow_value); - break; - } - case ShortyFieldType::kInt: { - uint32_t primitive_narrow_value = shadow_frame.GetVReg(source_vreg); - closure_builder->CaptureVariablePrimitive<int32_t>(primitive_narrow_value); - break; - } - case ShortyFieldType::kDouble: { - closure_builder->CaptureVariablePrimitive(shadow_frame.GetVRegDouble(source_vreg)); - break; - } - case ShortyFieldType::kFloat: { - closure_builder->CaptureVariablePrimitive(shadow_frame.GetVRegFloat(source_vreg)); - break; - } - case ShortyFieldType::kLambda: { - UNIMPLEMENTED(FATAL) << " capture-variable with type kLambda"; - // TODO: Capturing lambdas recursively will be done at a later time. - UNREACHABLE(); - } - case ShortyFieldType::kLong: { - closure_builder->CaptureVariablePrimitive(shadow_frame.GetVRegLong(source_vreg)); - break; - } - case ShortyFieldType::kObject: { - closure_builder->CaptureVariableObject(shadow_frame.GetVRegReference(source_vreg)); - UNIMPLEMENTED(FATAL) << " capture-variable with type kObject"; - // TODO: finish implementing this. disabled for now since we can't track lambda refs for GC. - UNREACHABLE(); - } - - default: - LOG(FATAL) << "Invalid shorty type value " << shorty_type; - UNREACHABLE(); - } - - DCHECK_EQ(captured_variable_count + 1, closure_builder->GetCaptureCount()); - - return true; -} - -// Handles capture-variable instructions. -// Returns true on success, otherwise throws an exception and returns false. -// (Exceptions are thrown by creating a new exception and then being put in the thread TLS) -template<bool do_access_check> -static inline bool DoLiberateVariable(Thread* self, - const Instruction* inst, - size_t captured_variable_index, - /*inout*/ShadowFrame& shadow_frame) { - using lambda::ShortyFieldType; - /* - * liberate-variable is opcode 0xf7, fmt 0x22c - * - vA is the destination register - * - vB is the register with the lambda closure in it - * - vC is the string ID which needs to be a valid field type descriptor - */ - - const uint32_t dest_vreg = inst->VRegA_22c(); - const uint32_t closure_vreg = inst->VRegB_22c(); - const uint32_t string_idx = inst->VRegC_22c(); - // TODO: this should be a proper [type id] instead of a [string ID] pointing to a type. - - - // Synthesize a long type descriptor from a shorty type descriptor list. - // TODO: Fix the dex encoding to contain the long and short type descriptors. - const char* type_string = GetStringDataByDexStringIndexOrThrow<do_access_check>(shadow_frame, - string_idx); - if (UNLIKELY(do_access_check && type_string == nullptr)) { - CHECK(self->IsExceptionPending()); - shadow_frame.SetVReg(dest_vreg, 0); - return false; - } - - char type_first_letter = type_string[0]; - ShortyFieldType shorty_type; - if (do_access_check && - UNLIKELY(!ShortyFieldType::MaybeCreate(type_first_letter, /*out*/&shorty_type))) { // NOLINT: [whitespace/comma] [3] - ThrowVerifyError(shadow_frame.GetMethod()->GetDeclaringClass(), - "liberate-variable vC must be a valid type"); - shadow_frame.SetVReg(dest_vreg, 0); - return false; - } else { - // Already verified that the type is valid. - shorty_type = ShortyFieldType(type_first_letter); - } - - // Check for closure being null *after* the type check. - // This way we can access the type info in case we fail later, to know how many vregs to clear. - const lambda::Closure* lambda_closure = - ReadLambdaClosureFromVRegsOrThrow(/*inout*/shadow_frame, closure_vreg); - - // Failed lambda target runtime check, an exception was raised. - if (UNLIKELY(lambda_closure == nullptr)) { - CHECK(self->IsExceptionPending()); - - // Clear the destination vreg(s) to be safe. - shadow_frame.SetVReg(dest_vreg, 0); - if (shorty_type.IsPrimitiveWide() || shorty_type.IsLambda()) { - shadow_frame.SetVReg(dest_vreg + 1, 0); - } - return false; - } - - if (do_access_check && - UNLIKELY(captured_variable_index >= lambda_closure->GetNumberOfCapturedVariables())) { - ThrowVerifyError(shadow_frame.GetMethod()->GetDeclaringClass(), - "liberate-variable captured variable index %zu out of bounds", - lambda_closure->GetNumberOfCapturedVariables()); - // Clear the destination vreg(s) to be safe. - shadow_frame.SetVReg(dest_vreg, 0); - if (shorty_type.IsPrimitiveWide() || shorty_type.IsLambda()) { - shadow_frame.SetVReg(dest_vreg + 1, 0); - } - return false; - } - - // Verify that the runtime type of the captured-variable matches the requested dex type. - if (do_access_check) { - ShortyFieldType actual_type = lambda_closure->GetCapturedShortyType(captured_variable_index); - if (actual_type != shorty_type) { - ThrowVerifyError(shadow_frame.GetMethod()->GetDeclaringClass(), - "cannot liberate-variable of runtime type '%c' to dex type '%c'", - static_cast<char>(actual_type), - static_cast<char>(shorty_type)); - - shadow_frame.SetVReg(dest_vreg, 0); - if (shorty_type.IsPrimitiveWide() || shorty_type.IsLambda()) { - shadow_frame.SetVReg(dest_vreg + 1, 0); - } - return false; - } - - if (actual_type.IsLambda() || actual_type.IsObject()) { - UNIMPLEMENTED(FATAL) << "liberate-variable type checks needs to " - << "parse full type descriptor for objects and lambdas"; - } - } - - // Unpack the captured variable from the closure into the correct type, then save it to the vreg. - if (shorty_type.IsPrimitiveNarrow()) { - uint32_t primitive_narrow_value = - lambda_closure->GetCapturedPrimitiveNarrow(captured_variable_index); - shadow_frame.SetVReg(dest_vreg, primitive_narrow_value); - } else if (shorty_type.IsPrimitiveWide()) { - uint64_t primitive_wide_value = - lambda_closure->GetCapturedPrimitiveWide(captured_variable_index); - shadow_frame.SetVRegLong(dest_vreg, static_cast<int64_t>(primitive_wide_value)); - } else if (shorty_type.IsObject()) { - mirror::Object* unpacked_object = - lambda_closure->GetCapturedObject(captured_variable_index); - shadow_frame.SetVRegReference(dest_vreg, unpacked_object); - - UNIMPLEMENTED(FATAL) << "liberate-variable cannot unpack objects yet"; - } else if (shorty_type.IsLambda()) { - UNIMPLEMENTED(FATAL) << "liberate-variable cannot unpack lambdas yet"; - } else { - LOG(FATAL) << "unreachable"; - UNREACHABLE(); - } - - return true; + return called_method; } template<bool do_access_check> @@ -565,24 +229,22 @@ static inline bool DoInvokeLambda(Thread* self, ShadowFrame& shadow_frame, const * * - reading var-args for 0x25 gets us vD,vE,vF,vG (but not vB) */ - uint32_t vreg_closure = inst->VRegC_25x(); - const lambda::Closure* lambda_closure = - ReadLambdaClosureFromVRegsOrThrow(shadow_frame, vreg_closure); + uint32_t vC = inst->VRegC_25x(); + ArtMethod* const called_method = ReadLambdaClosureFromVRegsOrThrow(shadow_frame, vC); // Failed lambda target runtime check, an exception was raised. - if (UNLIKELY(lambda_closure == nullptr)) { + if (UNLIKELY(called_method == nullptr)) { CHECK(self->IsExceptionPending()); result->SetJ(0); return false; } - ArtMethod* const called_method = lambda_closure->GetTargetMethod(); // Invoke a non-range lambda return DoLambdaCall<false, do_access_check>(called_method, self, shadow_frame, inst, inst_data, result); } -// Handles invoke-XXX/range instructions (other than invoke-lambda[-range]). +// Handles invoke-XXX/range instructions. // Returns true on success, otherwise throws an exception and returns false. template<InvokeType type, bool is_range, bool do_access_check> static inline bool DoInvoke(Thread* self, ShadowFrame& shadow_frame, const Instruction* inst, @@ -859,17 +521,17 @@ static inline bool DoBoxLambda(Thread* self, ShadowFrame& shadow_frame, const In uint32_t vreg_target_object = inst->VRegA_22x(inst_data); uint32_t vreg_source_closure = inst->VRegB_22x(); - lambda::Closure* lambda_closure = ReadLambdaClosureFromVRegsOrThrow(shadow_frame, - vreg_source_closure); + ArtMethod* closure_method = ReadLambdaClosureFromVRegsOrThrow(shadow_frame, + vreg_source_closure); // Failed lambda target runtime check, an exception was raised. - if (UNLIKELY(lambda_closure == nullptr)) { + if (UNLIKELY(closure_method == nullptr)) { CHECK(self->IsExceptionPending()); return false; } mirror::Object* closure_as_object = - Runtime::Current()->GetLambdaBoxTable()->BoxLambda(lambda_closure); + Runtime::Current()->GetLambdaBoxTable()->BoxLambda(closure_method); // Failed to box the lambda, an exception was raised. if (UNLIKELY(closure_as_object == nullptr)) { @@ -902,16 +564,16 @@ static inline bool DoUnboxLambda(Thread* self, return false; } - lambda::Closure* unboxed_closure = nullptr; + ArtMethod* unboxed_closure = nullptr; // Raise an exception if unboxing fails. if (!Runtime::Current()->GetLambdaBoxTable()->UnboxLambda(boxed_closure_object, - /*out*/&unboxed_closure)) { + &unboxed_closure)) { CHECK(self->IsExceptionPending()); return false; } DCHECK(unboxed_closure != nullptr); - WriteLambdaClosureIntoVRegs(/*inout*/shadow_frame, unboxed_closure, vreg_target_closure); + WriteLambdaClosureIntoVRegs(shadow_frame, *unboxed_closure, vreg_target_closure); return true; } @@ -988,13 +650,10 @@ EXPLICIT_DO_INVOKE_VIRTUAL_QUICK_TEMPLATE_DECL(true); // invoke-virtual-quick- #undef EXPLICIT_INSTANTIATION_DO_INVOKE_VIRTUAL_QUICK // Explicitly instantiate all DoCreateLambda functions. -#define EXPLICIT_DO_CREATE_LAMBDA_DECL(_do_check) \ -template SHARED_REQUIRES(Locks::mutator_lock_) \ -bool DoCreateLambda<_do_check>(Thread* self, \ - const Instruction* inst, \ - /*inout*/ShadowFrame& shadow_frame, \ - /*inout*/lambda::ClosureBuilder* closure_builder, \ - /*inout*/lambda::Closure* uninitialized_closure); +#define EXPLICIT_DO_CREATE_LAMBDA_DECL(_do_check) \ +template SHARED_REQUIRES(Locks::mutator_lock_) \ +bool DoCreateLambda<_do_check>(Thread* self, ShadowFrame& shadow_frame, \ + const Instruction* inst) EXPLICIT_DO_CREATE_LAMBDA_DECL(false); // create-lambda EXPLICIT_DO_CREATE_LAMBDA_DECL(true); // create-lambda @@ -1030,29 +689,7 @@ EXPLICIT_DO_UNBOX_LAMBDA_DECL(false); // unbox-lambda EXPLICIT_DO_UNBOX_LAMBDA_DECL(true); // unbox-lambda #undef EXPLICIT_DO_BOX_LAMBDA_DECL -// Explicitly instantiate all DoCaptureVariable functions. -#define EXPLICIT_DO_CAPTURE_VARIABLE_DECL(_do_check) \ -template SHARED_REQUIRES(Locks::mutator_lock_) \ -bool DoCaptureVariable<_do_check>(Thread* self, \ - const Instruction* inst, \ - ShadowFrame& shadow_frame, \ - lambda::ClosureBuilder* closure_builder); - -EXPLICIT_DO_CAPTURE_VARIABLE_DECL(false); // capture-variable -EXPLICIT_DO_CAPTURE_VARIABLE_DECL(true); // capture-variable -#undef EXPLICIT_DO_CREATE_LAMBDA_DECL -// Explicitly instantiate all DoLiberateVariable functions. -#define EXPLICIT_DO_LIBERATE_VARIABLE_DECL(_do_check) \ -template SHARED_REQUIRES(Locks::mutator_lock_) \ -bool DoLiberateVariable<_do_check>(Thread* self, \ - const Instruction* inst, \ - size_t captured_variable_index, \ - ShadowFrame& shadow_frame); \ - -EXPLICIT_DO_LIBERATE_VARIABLE_DECL(false); // liberate-variable -EXPLICIT_DO_LIBERATE_VARIABLE_DECL(true); // liberate-variable -#undef EXPLICIT_DO_LIBERATE_LAMBDA_DECL } // namespace interpreter } // namespace art diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc index 9677d79de3..72e2ba0e7b 100644 --- a/runtime/interpreter/interpreter_goto_table_impl.cc +++ b/runtime/interpreter/interpreter_goto_table_impl.cc @@ -17,13 +17,9 @@ #if !defined(__clang__) // Clang 3.4 fails to build the goto interpreter implementation. - -#include "base/stl_util.h" // MakeUnique #include "interpreter_common.h" #include "safe_math.h" -#include <memory> // std::unique_ptr - namespace art { namespace interpreter { @@ -183,9 +179,6 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF } } - std::unique_ptr<lambda::ClosureBuilder> lambda_closure_builder; - size_t lambda_captured_variable_index = 0; - // Jump to first instruction. ADVANCE(0); UNREACHABLE_CODE_CHECK(); @@ -2419,20 +2412,7 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF HANDLE_INSTRUCTION_END(); HANDLE_EXPERIMENTAL_INSTRUCTION_START(CREATE_LAMBDA) { - if (lambda_closure_builder == nullptr) { - // DoCreateLambda always needs a ClosureBuilder, even if it has 0 captured variables. - lambda_closure_builder = MakeUnique<lambda::ClosureBuilder>(); - } - - // TODO: these allocations should not leak, and the lambda method should not be local. - lambda::Closure* lambda_closure = - reinterpret_cast<lambda::Closure*>(alloca(lambda_closure_builder->GetSize())); - bool success = DoCreateLambda<do_access_check>(self, - inst, - /*inout*/shadow_frame, - /*inout*/lambda_closure_builder.get(), - /*inout*/lambda_closure); - lambda_closure_builder.reset(nullptr); // reset state of variables captured + bool success = DoCreateLambda<true>(self, shadow_frame, inst); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_EXPERIMENTAL_INSTRUCTION_END(); @@ -2449,31 +2429,6 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF } HANDLE_EXPERIMENTAL_INSTRUCTION_END(); - HANDLE_EXPERIMENTAL_INSTRUCTION_START(CAPTURE_VARIABLE) { - if (lambda_closure_builder == nullptr) { - lambda_closure_builder = MakeUnique<lambda::ClosureBuilder>(); - } - - bool success = DoCaptureVariable<do_access_check>(self, - inst, - /*inout*/shadow_frame, - /*inout*/lambda_closure_builder.get()); - - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); - } - HANDLE_EXPERIMENTAL_INSTRUCTION_END(); - - HANDLE_EXPERIMENTAL_INSTRUCTION_START(LIBERATE_VARIABLE) { - bool success = DoLiberateVariable<do_access_check>(self, - inst, - lambda_captured_variable_index, - /*inout*/shadow_frame); - // Temporarily only allow sequences of 'liberate-variable, liberate-variable, ...' - lambda_captured_variable_index++; - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); - } - HANDLE_EXPERIMENTAL_INSTRUCTION_END(); - HANDLE_INSTRUCTION_START(UNUSED_3E) UnexpectedOpcode(inst, shadow_frame); HANDLE_INSTRUCTION_END(); @@ -2510,6 +2465,14 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF UnexpectedOpcode(inst, shadow_frame); HANDLE_INSTRUCTION_END(); + HANDLE_INSTRUCTION_START(UNUSED_F5) + UnexpectedOpcode(inst, shadow_frame); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(UNUSED_F7) + UnexpectedOpcode(inst, shadow_frame); + HANDLE_INSTRUCTION_END(); + HANDLE_INSTRUCTION_START(UNUSED_FA) UnexpectedOpcode(inst, shadow_frame); HANDLE_INSTRUCTION_END(); diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc index 083dfb5267..b5cc11e070 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -14,12 +14,9 @@ * limitations under the License. */ -#include "base/stl_util.h" // MakeUnique #include "interpreter_common.h" #include "safe_math.h" -#include <memory> // std::unique_ptr - namespace art { namespace interpreter { @@ -85,11 +82,6 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, const uint16_t* const insns = code_item->insns_; const Instruction* inst = Instruction::At(insns + dex_pc); uint16_t inst_data; - - // TODO: collapse capture-variable+create-lambda into one opcode, then we won't need - // to keep this live for the scope of the entire function call. - std::unique_ptr<lambda::ClosureBuilder> lambda_closure_builder; - size_t lambda_captured_variable_index = 0; while (true) { dex_pc = inst->GetDexPc(insns); shadow_frame.SetDexPC(dex_pc); @@ -2243,63 +2235,19 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } - case Instruction::CAPTURE_VARIABLE: { - if (!IsExperimentalInstructionEnabled(inst)) { - UnexpectedOpcode(inst, shadow_frame); - } - - if (lambda_closure_builder == nullptr) { - lambda_closure_builder = MakeUnique<lambda::ClosureBuilder>(); - } - - PREAMBLE(); - bool success = DoCaptureVariable<do_access_check>(self, - inst, - /*inout*/shadow_frame, - /*inout*/lambda_closure_builder.get()); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } case Instruction::CREATE_LAMBDA: { if (!IsExperimentalInstructionEnabled(inst)) { UnexpectedOpcode(inst, shadow_frame); } PREAMBLE(); - - if (lambda_closure_builder == nullptr) { - // DoCreateLambda always needs a ClosureBuilder, even if it has 0 captured variables. - lambda_closure_builder = MakeUnique<lambda::ClosureBuilder>(); - } - - // TODO: these allocations should not leak, and the lambda method should not be local. - lambda::Closure* lambda_closure = - reinterpret_cast<lambda::Closure*>(alloca(lambda_closure_builder->GetSize())); - bool success = DoCreateLambda<do_access_check>(self, - inst, - /*inout*/shadow_frame, - /*inout*/lambda_closure_builder.get(), - /*inout*/lambda_closure); - lambda_closure_builder.reset(nullptr); // reset state of variables captured - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::LIBERATE_VARIABLE: { - if (!IsExperimentalInstructionEnabled(inst)) { - UnexpectedOpcode(inst, shadow_frame); - } - - PREAMBLE(); - bool success = DoLiberateVariable<do_access_check>(self, - inst, - lambda_captured_variable_index, - /*inout*/shadow_frame); - // Temporarily only allow sequences of 'liberate-variable, liberate-variable, ...' - lambda_captured_variable_index++; + bool success = DoCreateLambda<do_access_check>(self, shadow_frame, inst); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } - case Instruction::UNUSED_F4: { + case Instruction::UNUSED_F4: + case Instruction::UNUSED_F5: + case Instruction::UNUSED_F7: { if (!IsExperimentalInstructionEnabled(inst)) { UnexpectedOpcode(inst, shadow_frame); } diff --git a/runtime/lambda/art_lambda_method.h b/runtime/lambda/art_lambda_method.h index ea13eb7af6..892d8c6f6b 100644 --- a/runtime/lambda/art_lambda_method.h +++ b/runtime/lambda/art_lambda_method.h @@ -35,7 +35,7 @@ class ArtLambdaMethod { // (Ownership of strings is retained by the caller and the lifetime should exceed this class). ArtLambdaMethod(ArtMethod* target_method, const char* captured_variables_type_descriptor, - const char* captured_variables_shorty, + const char* captured_variables_shorty_, bool innate_lambda = true); // Get the target method for this lambda that would be used by the invoke-lambda dex instruction. diff --git a/runtime/lambda/box_table.cc b/runtime/lambda/box_table.cc index 8eef10bbad..26575fd995 100644 --- a/runtime/lambda/box_table.cc +++ b/runtime/lambda/box_table.cc @@ -18,8 +18,6 @@ #include "base/mutex.h" #include "common_throws.h" #include "gc_root-inl.h" -#include "lambda/closure.h" -#include "lambda/leaking_allocator.h" #include "mirror/method.h" #include "mirror/object-inl.h" #include "thread.h" @@ -28,53 +26,11 @@ namespace art { namespace lambda { -// Temporarily represent the lambda Closure as its raw bytes in an array. -// TODO: Generate a proxy class for the closure when boxing the first time. -using BoxedClosurePointerType = mirror::ByteArray*; - -static mirror::Class* GetBoxedClosureClass() SHARED_REQUIRES(Locks::mutator_lock_) { - return mirror::ByteArray::GetArrayClass(); -} - -namespace { - // Convenience functions to allocating/deleting box table copies of the closures. - struct ClosureAllocator { - // Deletes a Closure that was allocated through ::Allocate. - static void Delete(Closure* ptr) { - delete[] reinterpret_cast<char*>(ptr); - } - - // Returns a well-aligned pointer to a newly allocated Closure on the 'new' heap. - static Closure* Allocate(size_t size) { - DCHECK_GE(size, sizeof(Closure)); - - // TODO: Maybe point to the interior of the boxed closure object after we add proxy support? - Closure* closure = reinterpret_cast<Closure*>(new char[size]); - DCHECK_ALIGNED(closure, alignof(Closure)); - return closure; - } - }; -} // namespace BoxTable::BoxTable() : allow_new_weaks_(true), new_weaks_condition_("lambda box table allowed weaks", *Locks::lambda_table_lock_) {} -BoxTable::~BoxTable() { - // Free all the copies of our closures. - for (auto map_iterator = map_.begin(); map_iterator != map_.end(); ++map_iterator) { - std::pair<UnorderedMapKeyType, ValueType>& key_value_pair = *map_iterator; - - Closure* closure = key_value_pair.first; - - // Remove from the map first, so that it doesn't try to access dangling pointer. - map_iterator = map_.Erase(map_iterator); - - // Safe to delete, no dangling pointers. - ClosureAllocator::Delete(closure); - } -} - mirror::Object* BoxTable::BoxLambda(const ClosureType& closure) { Thread* self = Thread::Current(); @@ -102,29 +58,22 @@ mirror::Object* BoxTable::BoxLambda(const ClosureType& closure) { // Release the lambda table lock here, so that thread suspension is allowed. - // Convert the Closure into a managed byte[] which will serve + // Convert the ArtMethod into a java.lang.reflect.Method which will serve // as the temporary 'boxed' version of the lambda. This is good enough // to check all the basic object identities that a boxed lambda must retain. - // It's also good enough to contain all the captured primitive variables. // TODO: Boxing an innate lambda (i.e. made with create-lambda) should make a proxy class // TODO: Boxing a learned lambda (i.e. made with unbox-lambda) should return the original object - BoxedClosurePointerType closure_as_array_object = - mirror::ByteArray::Alloc(self, closure->GetSize()); - + mirror::Method* method_as_object = + mirror::Method::CreateFromArtMethod(self, closure); // There are no thread suspension points after this, so we don't need to put it into a handle. - if (UNLIKELY(closure_as_array_object == nullptr)) { + if (UNLIKELY(method_as_object == nullptr)) { // Most likely an OOM has occurred. CHECK(self->IsExceptionPending()); return nullptr; } - // Write the raw closure data into the byte[]. - closure->CopyTo(closure_as_array_object->GetRawData(sizeof(uint8_t), // component size - 0 /*index*/), // index - closure_as_array_object->GetLength()); - // The method has been successfully boxed into an object, now insert it into the hash map. { MutexLock mu(self, *Locks::lambda_table_lock_); @@ -138,56 +87,38 @@ mirror::Object* BoxTable::BoxLambda(const ClosureType& closure) { return value.Read(); } - // Otherwise we need to insert it into the hash map in this thread. - - // Make a copy for the box table to keep, in case the closure gets collected from the stack. - // TODO: GC may need to sweep for roots in the box table's copy of the closure. - Closure* closure_table_copy = ClosureAllocator::Allocate(closure->GetSize()); - closure->CopyTo(closure_table_copy, closure->GetSize()); - - // The closure_table_copy needs to be deleted by us manually when we erase it from the map. - - // Actually insert into the table. - map_.Insert({closure_table_copy, ValueType(closure_as_array_object)}); + // Otherwise we should insert it into the hash map in this thread. + map_.Insert(std::make_pair(closure, ValueType(method_as_object))); } - return closure_as_array_object; + return method_as_object; } bool BoxTable::UnboxLambda(mirror::Object* object, ClosureType* out_closure) { DCHECK(object != nullptr); *out_closure = nullptr; - Thread* self = Thread::Current(); - // Note that we do not need to access lambda_table_lock_ here // since we don't need to look at the map. mirror::Object* boxed_closure_object = object; - // Raise ClassCastException if object is not instanceof byte[] - if (UNLIKELY(!boxed_closure_object->InstanceOf(GetBoxedClosureClass()))) { - ThrowClassCastException(GetBoxedClosureClass(), boxed_closure_object->GetClass()); + // Raise ClassCastException if object is not instanceof java.lang.reflect.Method + if (UNLIKELY(!boxed_closure_object->InstanceOf(mirror::Method::StaticClass()))) { + ThrowClassCastException(mirror::Method::StaticClass(), boxed_closure_object->GetClass()); return false; } // TODO(iam): We must check that the closure object extends/implements the type - // specified in [type id]. This is not currently implemented since it's always a byte[]. + // specified in [type id]. This is not currently implemented since it's always a Method. // If we got this far, the inputs are valid. - // Shuffle the byte[] back into a raw closure, then allocate it, copy, and return it. - BoxedClosurePointerType boxed_closure_as_array = - down_cast<BoxedClosurePointerType>(boxed_closure_object); - - const int8_t* unaligned_interior_closure = boxed_closure_as_array->GetData(); + // Write out the java.lang.reflect.Method's embedded ArtMethod* into the vreg target. + mirror::AbstractMethod* boxed_closure_as_method = + down_cast<mirror::AbstractMethod*>(boxed_closure_object); - // Allocate a copy that can "escape" and copy the closure data into that. - Closure* unboxed_closure = - LeakingAllocator::MakeFlexibleInstance<Closure>(self, boxed_closure_as_array->GetLength()); - // TODO: don't just memcpy the closure, it's unsafe when we add references to the mix. - memcpy(unboxed_closure, unaligned_interior_closure, boxed_closure_as_array->GetLength()); - - DCHECK_EQ(unboxed_closure->GetSize(), static_cast<size_t>(boxed_closure_as_array->GetLength())); + ArtMethod* unboxed_closure = boxed_closure_as_method->GetArtMethod(); + DCHECK(unboxed_closure != nullptr); *out_closure = unboxed_closure; return true; @@ -196,7 +127,7 @@ bool BoxTable::UnboxLambda(mirror::Object* object, ClosureType* out_closure) { BoxTable::ValueType BoxTable::FindBoxedLambda(const ClosureType& closure) const { auto map_iterator = map_.Find(closure); if (map_iterator != map_.end()) { - const std::pair<UnorderedMapKeyType, ValueType>& key_value_pair = *map_iterator; + const std::pair<ClosureType, ValueType>& key_value_pair = *map_iterator; const ValueType& value = key_value_pair.second; DCHECK(!value.IsNull()); // Never store null boxes. @@ -226,7 +157,7 @@ void BoxTable::SweepWeakBoxedLambdas(IsMarkedVisitor* visitor) { */ std::vector<ClosureType> remove_list; for (auto map_iterator = map_.begin(); map_iterator != map_.end(); ) { - std::pair<UnorderedMapKeyType, ValueType>& key_value_pair = *map_iterator; + std::pair<ClosureType, ValueType>& key_value_pair = *map_iterator; const ValueType& old_value = key_value_pair.second; @@ -235,15 +166,10 @@ void BoxTable::SweepWeakBoxedLambdas(IsMarkedVisitor* visitor) { mirror::Object* new_value = visitor->IsMarked(old_value_raw); if (new_value == nullptr) { - // The object has been swept away. const ClosureType& closure = key_value_pair.first; - + // The object has been swept away. // Delete the entry from the map. - map_iterator = map_.Erase(map_iterator); - - // Clean up the memory by deleting the closure. - ClosureAllocator::Delete(closure); - + map_iterator = map_.Erase(map_.Find(closure)); } else { // The object has been moved. // Update the map. @@ -282,33 +208,16 @@ void BoxTable::BroadcastForNewWeakBoxedLambdas() { new_weaks_condition_.Broadcast(self); } -void BoxTable::EmptyFn::MakeEmpty(std::pair<UnorderedMapKeyType, ValueType>& item) const { - item.first = nullptr; - - Locks::mutator_lock_->AssertSharedHeld(Thread::Current()); - item.second = ValueType(); // Also clear the GC root. -} - -bool BoxTable::EmptyFn::IsEmpty(const std::pair<UnorderedMapKeyType, ValueType>& item) const { - return item.first == nullptr; -} - -bool BoxTable::EqualsFn::operator()(const UnorderedMapKeyType& lhs, - const UnorderedMapKeyType& rhs) const { +bool BoxTable::EqualsFn::operator()(const ClosureType& lhs, const ClosureType& rhs) const { // Nothing needs this right now, but leave this assertion for later when // we need to look at the references inside of the closure. - Locks::mutator_lock_->AssertSharedHeld(Thread::Current()); - - return lhs->ReferenceEquals(rhs); -} - -size_t BoxTable::HashFn::operator()(const UnorderedMapKeyType& key) const { - const lambda::Closure* closure = key; - DCHECK_ALIGNED(closure, alignof(lambda::Closure)); + if (kIsDebugBuild) { + Locks::mutator_lock_->AssertSharedHeld(Thread::Current()); + } - // Need to hold mutator_lock_ before calling into Closure::GetHashCode. - Locks::mutator_lock_->AssertSharedHeld(Thread::Current()); - return closure->GetHashCode(); + // TODO: Need rework to use read barriers once closures have references inside of them that can + // move. Until then, it's safe to just compare the data inside of it directly. + return lhs == rhs; } } // namespace lambda diff --git a/runtime/lambda/box_table.h b/runtime/lambda/box_table.h index adb733271e..9ffda6658f 100644 --- a/runtime/lambda/box_table.h +++ b/runtime/lambda/box_table.h @@ -34,7 +34,6 @@ class Object; // forward declaration } // namespace mirror namespace lambda { -struct Closure; // forward declaration /* * Store a table of boxed lambdas. This is required to maintain object referential equality @@ -45,7 +44,7 @@ struct Closure; // forward declaration */ class BoxTable FINAL { public: - using ClosureType = art::lambda::Closure*; + using ClosureType = art::ArtMethod*; // Boxes a closure into an object. Returns null and throws an exception on failure. mirror::Object* BoxLambda(const ClosureType& closure) @@ -73,9 +72,10 @@ class BoxTable FINAL { REQUIRES(!Locks::lambda_table_lock_); BoxTable(); - ~BoxTable(); + ~BoxTable() = default; private: + // FIXME: This needs to be a GcRoot. // Explanation: // - After all threads are suspended (exclusive mutator lock), // the concurrent-copying GC can move objects from the "from" space to the "to" space. @@ -97,30 +97,30 @@ class BoxTable FINAL { void BlockUntilWeaksAllowed() SHARED_REQUIRES(Locks::lambda_table_lock_); - // Wrap the Closure into a unique_ptr so that the HashMap can delete its memory automatically. - using UnorderedMapKeyType = ClosureType; - // EmptyFn implementation for art::HashMap struct EmptyFn { - void MakeEmpty(std::pair<UnorderedMapKeyType, ValueType>& item) const - NO_THREAD_SAFETY_ANALYSIS; // SHARED_REQUIRES(Locks::mutator_lock_) - - bool IsEmpty(const std::pair<UnorderedMapKeyType, ValueType>& item) const; + void MakeEmpty(std::pair<ClosureType, ValueType>& item) const { + item.first = nullptr; + } + bool IsEmpty(const std::pair<ClosureType, ValueType>& item) const { + return item.first == nullptr; + } }; // HashFn implementation for art::HashMap struct HashFn { - size_t operator()(const UnorderedMapKeyType& key) const - NO_THREAD_SAFETY_ANALYSIS; // SHARED_REQUIRES(Locks::mutator_lock_) + size_t operator()(const ClosureType& key) const { + // TODO(iam): Rewrite hash function when ClosureType is no longer an ArtMethod* + return static_cast<size_t>(reinterpret_cast<uintptr_t>(key)); + } }; // EqualsFn implementation for art::HashMap struct EqualsFn { - bool operator()(const UnorderedMapKeyType& lhs, const UnorderedMapKeyType& rhs) const - NO_THREAD_SAFETY_ANALYSIS; // SHARED_REQUIRES(Locks::mutator_lock_) + bool operator()(const ClosureType& lhs, const ClosureType& rhs) const; }; - using UnorderedMap = art::HashMap<UnorderedMapKeyType, + using UnorderedMap = art::HashMap<ClosureType, ValueType, EmptyFn, HashFn, diff --git a/runtime/lambda/closure.cc b/runtime/lambda/closure.cc index 179e4ee7f2..95a17c660c 100644 --- a/runtime/lambda/closure.cc +++ b/runtime/lambda/closure.cc @@ -124,55 +124,6 @@ void Closure::CopyTo(void* target, size_t target_size) const { memcpy(target, this, GetSize()); } -ArtMethod* Closure::GetTargetMethod() const { - return const_cast<ArtMethod*>(lambda_info_->GetArtMethod()); -} - -uint32_t Closure::GetHashCode() const { - // Start with a non-zero constant, a prime number. - uint32_t result = 17; - - // Include the hash with the ArtMethod. - { - uintptr_t method = reinterpret_cast<uintptr_t>(GetTargetMethod()); - result = 31 * result + Low32Bits(method); - if (sizeof(method) == sizeof(uint64_t)) { - result = 31 * result + High32Bits(method); - } - } - - // Include a hash for each captured variable. - for (size_t i = 0; i < GetCapturedVariablesSize(); ++i) { - // TODO: not safe for GC-able values since the address can move and the hash code would change. - uint8_t captured_variable_raw_value; - CopyUnsafeAtOffset<uint8_t>(i, /*out*/&captured_variable_raw_value); // NOLINT: [whitespace/comma] [3] - - result = 31 * result + captured_variable_raw_value; - } - - // TODO: Fix above loop to work for objects and lambdas. - static_assert(kClosureSupportsGarbageCollection == false, - "Need to update above loop to read the hash code from the " - "objects and lambdas recursively"); - - return result; -} - -bool Closure::ReferenceEquals(const Closure* other) const { - DCHECK(other != nullptr); - - // TODO: Need rework to use read barriers once closures have references inside of them that can - // move. Until then, it's safe to just compare the data inside of it directly. - static_assert(kClosureSupportsReferences == false, - "Unsafe to use memcmp in read barrier collector"); - - if (GetSize() != other->GetSize()) { - return false; - } - - return memcmp(this, other, GetSize()); -} - size_t Closure::GetNumberOfCapturedVariables() const { // TODO: refactor into art_lambda_method.h. Parsing should only be required here as a DCHECK. VariableInfo variable_info = diff --git a/runtime/lambda/closure.h b/runtime/lambda/closure.h index 31ff1944d2..60d117e9e2 100644 --- a/runtime/lambda/closure.h +++ b/runtime/lambda/closure.h @@ -49,19 +49,6 @@ struct PACKED(sizeof(ArtLambdaMethod*)) Closure { // The target_size must be at least as large as GetSize(). void CopyTo(void* target, size_t target_size) const; - // Get the target method, i.e. the method that will be dispatched into with invoke-lambda. - ArtMethod* GetTargetMethod() const; - - // Calculates the hash code. Value is recomputed each time. - uint32_t GetHashCode() const SHARED_REQUIRES(Locks::mutator_lock_); - - // Is this the same closure as other? e.g. same target method, same variables captured. - // - // Determines whether the two Closures are interchangeable instances. - // Does *not* call Object#equals recursively. If two Closures compare ReferenceEquals true that - // means that they are interchangeable values (usually for the purpose of boxing/unboxing). - bool ReferenceEquals(const Closure* other) const SHARED_REQUIRES(Locks::mutator_lock_); - // How many variables were captured? size_t GetNumberOfCapturedVariables() const; diff --git a/runtime/lambda/closure_builder-inl.h b/runtime/lambda/closure_builder-inl.h index 3cec21f3ba..41a803baf2 100644 --- a/runtime/lambda/closure_builder-inl.h +++ b/runtime/lambda/closure_builder-inl.h @@ -35,8 +35,6 @@ void ClosureBuilder::CaptureVariablePrimitive(T value) { values_.push_back(value_storage); size_ += sizeof(T); - - shorty_types_ += kShortyType; } } // namespace lambda diff --git a/runtime/lambda/closure_builder.cc b/runtime/lambda/closure_builder.cc index 739e965238..9c37db8fcc 100644 --- a/runtime/lambda/closure_builder.cc +++ b/runtime/lambda/closure_builder.cc @@ -64,8 +64,6 @@ void ClosureBuilder::CaptureVariableObject(mirror::Object* object) { UNIMPLEMENTED(FATAL) << "can't yet safely capture objects with read barrier"; } } - - shorty_types_ += ShortyFieldType::kObject; } void ClosureBuilder::CaptureVariableLambda(Closure* closure) { @@ -80,8 +78,6 @@ void ClosureBuilder::CaptureVariableLambda(Closure* closure) { // A closure may be sized dynamically, so always query it for the true size. size_ += closure->GetSize(); - - shorty_types_ += ShortyFieldType::kLambda; } size_t ClosureBuilder::GetSize() const { @@ -89,15 +85,9 @@ size_t ClosureBuilder::GetSize() const { } size_t ClosureBuilder::GetCaptureCount() const { - DCHECK_EQ(values_.size(), shorty_types_.size()); return values_.size(); } -const std::string& ClosureBuilder::GetCapturedVariableShortyTypes() const { - DCHECK_EQ(values_.size(), shorty_types_.size()); - return shorty_types_; -} - Closure* ClosureBuilder::CreateInPlace(void* memory, ArtLambdaMethod* target_method) const { DCHECK(memory != nullptr); DCHECK(target_method != nullptr); @@ -148,14 +138,11 @@ size_t ClosureBuilder::WriteValues(ArtLambdaMethod* target_method, size_t variables_size) const { size_t total_size = header_size; const char* shorty_types = target_method->GetCapturedVariablesShortyTypeDescriptor(); - DCHECK_STREQ(shorty_types, shorty_types_.c_str()); size_t variables_offset = 0; size_t remaining_size = variables_size; const size_t shorty_count = target_method->GetNumberOfCapturedVariables(); - DCHECK_EQ(shorty_count, GetCaptureCount()); - for (size_t i = 0; i < shorty_count; ++i) { ShortyFieldType shorty{shorty_types[i]}; // NOLINT [readability/braces] [4] diff --git a/runtime/lambda/closure_builder.h b/runtime/lambda/closure_builder.h index 23eb484529..542e12afaa 100644 --- a/runtime/lambda/closure_builder.h +++ b/runtime/lambda/closure_builder.h @@ -40,12 +40,13 @@ class ArtLambdaMethod; // forward declaration // // The mutator lock must be held for the duration of the lifetime of this object, // since it needs to temporarily store heap references into an internal list. -class ClosureBuilder { +class ClosureBuilder : ValueObject { public: using ShortyTypeEnum = decltype(ShortyFieldType::kByte); + // Mark this primitive value to be captured as the specified type. - template <typename T, ShortyTypeEnum kShortyType = ShortyFieldTypeSelectEnum<T>::value> + template <typename T, ShortyTypeEnum kShortyType> void CaptureVariablePrimitive(T value); // Mark this object reference to be captured. @@ -62,9 +63,6 @@ class ClosureBuilder { // Returns how many variables have been captured so far. size_t GetCaptureCount() const; - // Get the list of captured variables' shorty field types. - const std::string& GetCapturedVariableShortyTypes() const; - // Creates a closure in-place and writes out the data into 'memory'. // Memory must be at least 'GetSize' bytes large. // All previously marked data to be captured is now written out. @@ -95,7 +93,6 @@ class ClosureBuilder { size_t size_ = kInitialSize; bool is_dynamic_size_ = false; std::vector<ShortyFieldTypeTraits::MaxType> values_; - std::string shorty_types_; }; } // namespace lambda diff --git a/runtime/lambda/leaking_allocator.cc b/runtime/lambda/leaking_allocator.cc deleted file mode 100644 index 4910732a6c..0000000000 --- a/runtime/lambda/leaking_allocator.cc +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2015 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 "lambda/leaking_allocator.h" -#include "linear_alloc.h" -#include "runtime.h" - -namespace art { -namespace lambda { - -void* LeakingAllocator::AllocateMemory(Thread* self, size_t byte_size) { - // TODO: use GetAllocatorForClassLoader to allocate lambda ArtMethod data. - return Runtime::Current()->GetLinearAlloc()->Alloc(self, byte_size); -} - -} // namespace lambda -} // namespace art diff --git a/runtime/lambda/leaking_allocator.h b/runtime/lambda/leaking_allocator.h deleted file mode 100644 index c3222d0485..0000000000 --- a/runtime/lambda/leaking_allocator.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef ART_RUNTIME_LAMBDA_LEAKING_ALLOCATOR_H_ -#define ART_RUNTIME_LAMBDA_LEAKING_ALLOCATOR_H_ - -#include <utility> // std::forward - -namespace art { -class Thread; // forward declaration - -namespace lambda { - -// Temporary class to centralize all the leaking allocations. -// Allocations made through this class are never freed, but it is a placeholder -// that means that the calling code needs to be rewritten to properly: -// -// (a) Have a lifetime scoped to some other entity. -// (b) Not be allocated over and over again if it was already allocated once (immutable data). -// -// TODO: do all of the above a/b for each callsite, and delete this class. -class LeakingAllocator { - public: - // Allocate byte_size bytes worth of memory. Never freed. - static void* AllocateMemory(Thread* self, size_t byte_size); - - // Make a new instance of T, flexibly sized, in-place at newly allocated memory. Never freed. - template <typename T, typename... Args> - static T* MakeFlexibleInstance(Thread* self, size_t byte_size, Args&&... args) { - return new (AllocateMemory(self, byte_size)) T(std::forward<Args>(args)...); - } - - // Make a new instance of T in-place at newly allocated memory. Never freed. - template <typename T, typename... Args> - static T* MakeInstance(Thread* self, Args&&... args) { - return new (AllocateMemory(self, sizeof(T))) T(std::forward<Args>(args)...); - } -}; - -} // namespace lambda -} // namespace art - -#endif // ART_RUNTIME_LAMBDA_LEAKING_ALLOCATOR_H_ diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 43c0b41caf..3d4f04c70c 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -1007,9 +1007,6 @@ bool MethodVerifier::VerifyInstruction(const Instruction* inst, uint32_t code_of case Instruction::kVerifyRegCWide: result = result && CheckWideRegisterIndex(inst->VRegC()); break; - case Instruction::kVerifyRegCString: - result = result && CheckStringIndex(inst->VRegC()); - break; } switch (inst->GetVerifyExtraFlags()) { case Instruction::kVerifyArrayData: @@ -3151,13 +3148,6 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { Fail(VERIFY_ERROR_FORCE_INTERPRETER); // TODO(iam): implement invoke-lambda verification break; } - case Instruction::CAPTURE_VARIABLE: { - // Don't bother verifying, instead the interpreter will take the slow path with access checks. - // If the code would've normally hard-failed, then the interpreter will throw the - // appropriate verification errors at runtime. - Fail(VERIFY_ERROR_FORCE_INTERPRETER); // TODO(iam): implement capture-variable verification - break; - } case Instruction::CREATE_LAMBDA: { // Don't bother verifying, instead the interpreter will take the slow path with access checks. // If the code would've normally hard-failed, then the interpreter will throw the @@ -3165,15 +3155,10 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { Fail(VERIFY_ERROR_FORCE_INTERPRETER); // TODO(iam): implement create-lambda verification break; } - case Instruction::LIBERATE_VARIABLE: { - // Don't bother verifying, instead the interpreter will take the slow path with access checks. - // If the code would've normally hard-failed, then the interpreter will throw the - // appropriate verification errors at runtime. - Fail(VERIFY_ERROR_FORCE_INTERPRETER); // TODO(iam): implement liberate-variable verification - break; - } - case Instruction::UNUSED_F4: { + case Instruction::UNUSED_F4: + case Instruction::UNUSED_F5: + case Instruction::UNUSED_F7: { DCHECK(false); // TODO(iam): Implement opcodes for lambdas // Conservatively fail verification on release builds. Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Unexpected opcode " << inst->DumpString(dex_file_); diff --git a/test/955-lambda-smali/expected.txt b/test/955-lambda-smali/expected.txt index 16381e4b46..36370998f4 100644 --- a/test/955-lambda-smali/expected.txt +++ b/test/955-lambda-smali/expected.txt @@ -16,13 +16,3 @@ Caught NPE (MoveResult) testF success (MoveResult) testD success (MoveResult) testL success -(CaptureVariables) (0-args, 1 captured variable 'Z'): value is true -(CaptureVariables) (0-args, 1 captured variable 'B'): value is R -(CaptureVariables) (0-args, 1 captured variable 'C'): value is ∂ -(CaptureVariables) (0-args, 1 captured variable 'S'): value is 1000 -(CaptureVariables) (0-args, 1 captured variable 'I'): value is 12345678 -(CaptureVariables) (0-args, 1 captured variable 'J'): value is 3287471278325742 -(CaptureVariables) (0-args, 1 captured variable 'F'): value is Infinity -(CaptureVariables) (0-args, 1 captured variable 'D'): value is -Infinity -(CaptureVariables) (0-args, 8 captured variable 'ZBCSIJFD'): value is true,R,∂,1000,12345678,3287471278325742,Infinity,-Infinity -(CaptureVariables) Caught NPE diff --git a/test/955-lambda-smali/smali/BoxUnbox.smali b/test/955-lambda-smali/smali/BoxUnbox.smali index 915de2d55d..108b5fafbc 100644 --- a/test/955-lambda-smali/smali/BoxUnbox.smali +++ b/test/955-lambda-smali/smali/BoxUnbox.smali @@ -1,3 +1,4 @@ +# # Copyright (C) 2015 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -35,8 +36,8 @@ .end method #TODO: should use a closure type instead of ArtMethod. -.method public static doHelloWorld(J)V - .registers 4 # 1 wide parameters, 2 locals +.method public static doHelloWorld(Ljava/lang/reflect/ArtMethod;)V + .registers 3 # 1 parameters, 2 locals const-string v0, "(BoxUnbox) Hello boxing world! (0-args, no closure)" @@ -50,9 +51,9 @@ .method private static testBox()V .registers 3 - create-lambda v0, LBoxUnbox;->doHelloWorld(J)V + create-lambda v0, LBoxUnbox;->doHelloWorld(Ljava/lang/reflect/ArtMethod;)V box-lambda v2, v0 # v2 = box(v0) - unbox-lambda v0, v2, J # v0 = unbox(v2) + unbox-lambda v0, v2, Ljava/lang/reflect/ArtMethod; # v0 = unbox(v2) invoke-lambda v0, {} return-void @@ -62,7 +63,7 @@ .method private static testBoxEquality()V .registers 6 # 0 parameters, 6 locals - create-lambda v0, LBoxUnbox;->doHelloWorld(J)V + create-lambda v0, LBoxUnbox;->doHelloWorld(Ljava/lang/reflect/ArtMethod;)V box-lambda v2, v0 # v2 = box(v0) box-lambda v3, v0 # v3 = box(v0) @@ -94,7 +95,7 @@ const v0, 0 # v0 = null const v1, 0 # v1 = null :start - unbox-lambda v2, v0, J + unbox-lambda v2, v0, Ljava/lang/reflect/ArtMethod; # attempting to unbox a null lambda will throw NPE :end return-void @@ -139,7 +140,7 @@ const-string v0, "This is not a boxed lambda" :start # TODO: use \FunctionalType; here instead - unbox-lambda v2, v0, J + unbox-lambda v2, v0, Ljava/lang/reflect/ArtMethod; # can't use a string, expects a lambda object here. throws ClassCastException. :end return-void diff --git a/test/955-lambda-smali/smali/Main.smali b/test/955-lambda-smali/smali/Main.smali index 9892d6124e..5d2aabb386 100644 --- a/test/955-lambda-smali/smali/Main.smali +++ b/test/955-lambda-smali/smali/Main.smali @@ -24,7 +24,6 @@ invoke-static {}, LTrivialHelloWorld;->run()V invoke-static {}, LBoxUnbox;->run()V invoke-static {}, LMoveResult;->run()V - invoke-static {}, LCaptureVariables;->run()V # TODO: add tests when verification fails diff --git a/test/955-lambda-smali/smali/MoveResult.smali b/test/955-lambda-smali/smali/MoveResult.smali index 52f7ba363b..1725da3044 100644 --- a/test/955-lambda-smali/smali/MoveResult.smali +++ b/test/955-lambda-smali/smali/MoveResult.smali @@ -41,7 +41,7 @@ .method public static testZ()V .registers 6 - create-lambda v0, LMoveResult;->lambdaZ(J)Z + create-lambda v0, LMoveResult;->lambdaZ(Ljava/lang/reflect/ArtMethod;)Z invoke-lambda v0, {} move-result v2 const v3, 1 @@ -61,7 +61,7 @@ .end method # Lambda target for testZ. Always returns "true". -.method public static lambdaZ(J)Z +.method public static lambdaZ(Ljava/lang/reflect/ArtMethod;)Z .registers 3 const v0, 1 @@ -73,7 +73,7 @@ .method public static testB()V .registers 6 - create-lambda v0, LMoveResult;->lambdaB(J)B + create-lambda v0, LMoveResult;->lambdaB(Ljava/lang/reflect/ArtMethod;)B invoke-lambda v0, {} move-result v2 const v3, 15 @@ -93,7 +93,7 @@ .end method # Lambda target for testB. Always returns "15". -.method public static lambdaB(J)B +.method public static lambdaB(Ljava/lang/reflect/ArtMethod;)B .registers 3 # 1 parameters, 2 locals const v0, 15 @@ -105,7 +105,7 @@ .method public static testS()V .registers 6 - create-lambda v0, LMoveResult;->lambdaS(J)S + create-lambda v0, LMoveResult;->lambdaS(Ljava/lang/reflect/ArtMethod;)S invoke-lambda v0, {} move-result v2 const/16 v3, 31000 @@ -125,7 +125,7 @@ .end method # Lambda target for testS. Always returns "31000". -.method public static lambdaS(J)S +.method public static lambdaS(Ljava/lang/reflect/ArtMethod;)S .registers 3 const/16 v0, 31000 @@ -137,7 +137,7 @@ .method public static testI()V .registers 6 - create-lambda v0, LMoveResult;->lambdaI(J)I + create-lambda v0, LMoveResult;->lambdaI(Ljava/lang/reflect/ArtMethod;)I invoke-lambda v0, {} move-result v2 const v3, 128000 @@ -157,7 +157,7 @@ .end method # Lambda target for testI. Always returns "128000". -.method public static lambdaI(J)I +.method public static lambdaI(Ljava/lang/reflect/ArtMethod;)I .registers 3 const v0, 128000 @@ -167,9 +167,9 @@ # Test that chars are returned correctly via move-result. .method public static testC()V - .registers 7 + .registers 6 - create-lambda v0, LMoveResult;->lambdaC(J)C + create-lambda v0, LMoveResult;->lambdaC(Ljava/lang/reflect/ArtMethod;)C invoke-lambda v0, {} move-result v2 const v3, 65535 @@ -189,7 +189,7 @@ .end method # Lambda target for testC. Always returns "65535". -.method public static lambdaC(J)C +.method public static lambdaC(Ljava/lang/reflect/ArtMethod;)C .registers 3 const v0, 65535 @@ -199,12 +199,12 @@ # Test that longs are returned correctly via move-result. .method public static testJ()V - .registers 9 + .registers 8 - create-lambda v0, LMoveResult;->lambdaJ(J)J + create-lambda v0, LMoveResult;->lambdaJ(Ljava/lang/reflect/ArtMethod;)J invoke-lambda v0, {} move-result v2 - const-wide v4, 0xdeadf00dc0ffeeL + const-wide v4, 0xdeadf00dc0ffee if-ne v4, v2, :is_not_equal const-string v6, "(MoveResult) testJ success" @@ -220,11 +220,11 @@ .end method -# Lambda target for testC. Always returns "0xdeadf00dc0ffeeL". -.method public static lambdaJ(J)J - .registers 5 +# Lambda target for testC. Always returns "0xdeadf00dc0ffee". +.method public static lambdaJ(Ljava/lang/reflect/ArtMethod;)J + .registers 4 - const-wide v0, 0xdeadf00dc0ffeeL + const-wide v0, 0xdeadf00dc0ffee return-wide v0 .end method @@ -233,7 +233,7 @@ .method public static testF()V .registers 6 - create-lambda v0, LMoveResult;->lambdaF(J)F + create-lambda v0, LMoveResult;->lambdaF(Ljava/lang/reflect/ArtMethod;)F invoke-lambda v0, {} move-result v2 const v3, infinityf @@ -253,8 +253,8 @@ .end method # Lambda target for testF. Always returns "infinityf". -.method public static lambdaF(J)F - .registers 4 +.method public static lambdaF(Ljava/lang/reflect/ArtMethod;)F + .registers 3 const v0, infinityf return v0 @@ -265,10 +265,10 @@ .method public static testD()V .registers 8 - create-lambda v0, LMoveResult;->lambdaD(J)D + create-lambda v0, LMoveResult;->lambdaD(Ljava/lang/reflect/ArtMethod;)D invoke-lambda v0, {} move-result-wide v2 - const-wide v4, -infinity + const-wide v4, infinity if-ne v4, v2, :is_not_equal const-string v6, "(MoveResult) testD success" @@ -285,10 +285,10 @@ .end method # Lambda target for testD. Always returns "infinity". -.method public static lambdaD(J)D - .registers 5 +.method public static lambdaD(Ljava/lang/reflect/ArtMethod;)D + .registers 4 - const-wide v0, -infinity + const-wide v0, infinity # 123.456789 return-wide v0 .end method @@ -298,7 +298,7 @@ .method public static testL()V .registers 8 - create-lambda v0, LMoveResult;->lambdaL(J)Ljava/lang/String; + create-lambda v0, LMoveResult;->lambdaL(Ljava/lang/reflect/ArtMethod;)Ljava/lang/String; invoke-lambda v0, {} move-result-object v2 const-string v4, "Interned string" @@ -319,8 +319,8 @@ .end method # Lambda target for testL. Always returns "Interned string" (string). -.method public static lambdaL(J)Ljava/lang/String; - .registers 5 +.method public static lambdaL(Ljava/lang/reflect/ArtMethod;)Ljava/lang/String; + .registers 4 const-string v0, "Interned string" return-object v0 diff --git a/test/955-lambda-smali/smali/TrivialHelloWorld.smali b/test/955-lambda-smali/smali/TrivialHelloWorld.smali index 3444b13a65..38ee95ac7e 100644 --- a/test/955-lambda-smali/smali/TrivialHelloWorld.smali +++ b/test/955-lambda-smali/smali/TrivialHelloWorld.smali @@ -25,12 +25,12 @@ .method public static run()V .registers 8 # Trivial 0-arg hello world - create-lambda v0, LTrivialHelloWorld;->doHelloWorld(J)V + create-lambda v0, LTrivialHelloWorld;->doHelloWorld(Ljava/lang/reflect/ArtMethod;)V # TODO: create-lambda should not write to both v0 and v1 invoke-lambda v0, {} # Slightly more interesting 4-arg hello world - create-lambda v2, doHelloWorldArgs(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + create-lambda v2, doHelloWorldArgs(Ljava/lang/reflect/ArtMethod;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V # TODO: create-lambda should not write to both v2 and v3 const-string v4, "A" const-string v5, "B" @@ -43,9 +43,9 @@ return-void .end method -#TODO: should use a closure type instead of jlong. -.method public static doHelloWorld(J)V - .registers 5 # 1 wide parameters, 3 locals +#TODO: should use a closure type instead of ArtMethod. +.method public static doHelloWorld(Ljava/lang/reflect/ArtMethod;)V + .registers 3 # 1 parameters, 2 locals const-string v0, "Hello world! (0-args, no closure)" @@ -55,17 +55,17 @@ return-void .end method -#TODO: should use a closure type instead of jlong. -.method public static doHelloWorldArgs(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - .registers 9 # 1 wide parameter, 4 narrow parameters, 3 locals +#TODO: should use a closure type instead of ArtMethod. +.method public static doHelloWorldArgs(Ljava/lang/reflect/ArtMethod;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + .registers 7 # 5 parameters, 2 locals const-string v0, " Hello world! (4-args, no closure)" sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; + invoke-virtual {v1, p1}, Ljava/io/PrintStream;->print(Ljava/lang/String;)V invoke-virtual {v1, p2}, Ljava/io/PrintStream;->print(Ljava/lang/String;)V invoke-virtual {v1, p3}, Ljava/io/PrintStream;->print(Ljava/lang/String;)V invoke-virtual {v1, p4}, Ljava/io/PrintStream;->print(Ljava/lang/String;)V - invoke-virtual {v1, p5}, Ljava/io/PrintStream;->print(Ljava/lang/String;)V invoke-virtual {v1, v0}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V |