diff --git a/pkg/vm_service/lib/src/vm_service.dart b/pkg/vm_service/lib/src/vm_service.dart index c5581f4f2ef..053a9b6926c 100644 --- a/pkg/vm_service/lib/src/vm_service.dart +++ b/pkg/vm_service/lib/src/vm_service.dart @@ -4726,6 +4726,13 @@ class InstanceRef extends ObjRef { @optional ContextRef? closureContext; + /// The receiver captured by tear-off Closure instance. + /// + /// Provided for instance kinds: + /// - Closure + @optional + InstanceRef? closureReceiver; + /// The port ID for a ReceivePort. /// /// Provided for instance kinds: @@ -4771,6 +4778,7 @@ class InstanceRef extends ObjRef { this.pattern, this.closureFunction, this.closureContext, + this.closureReceiver, this.portId, this.allocationLocation, this.debugName, @@ -4813,6 +4821,9 @@ class InstanceRef extends ObjRef { closureContext = createServiceObject(json['closureContext'], const ['ContextRef']) as ContextRef?; + closureReceiver = + createServiceObject(json['closureReceiver'], const ['InstanceRef']) + as InstanceRef?; portId = json['portId']; allocationLocation = createServiceObject(json['allocationLocation'], const ['InstanceRef']) @@ -4847,6 +4858,7 @@ class InstanceRef extends ObjRef { _setIfNotNull(json, 'pattern', pattern?.toJson()); _setIfNotNull(json, 'closureFunction', closureFunction?.toJson()); _setIfNotNull(json, 'closureContext', closureContext?.toJson()); + _setIfNotNull(json, 'closureReceiver', closureReceiver?.toJson()); _setIfNotNull(json, 'portId', portId); _setIfNotNull(json, 'allocationLocation', allocationLocation?.toJson()); _setIfNotNull(json, 'debugName', debugName); @@ -5109,6 +5121,14 @@ class Instance extends Obj implements InstanceRef { @override ContextRef? closureContext; + /// The receiver captured by tear-off Closure instance. + /// + /// Provided for instance kinds: + /// - Closure + @optional + @override + InstanceRef? closureReceiver; + /// Whether this regular expression is case sensitive. /// /// Provided for instance kinds: @@ -5280,6 +5300,7 @@ class Instance extends Obj implements InstanceRef { this.pattern, this.closureFunction, this.closureContext, + this.closureReceiver, this.isCaseSensitive, this.isMultiLine, this.propertyKey, @@ -5356,6 +5377,9 @@ class Instance extends Obj implements InstanceRef { closureContext = createServiceObject(json['closureContext'], const ['ContextRef']) as ContextRef?; + closureReceiver = + createServiceObject(json['closureReceiver'], const ['InstanceRef']) + as InstanceRef?; isCaseSensitive = json['isCaseSensitive']; isMultiLine = json['isMultiLine']; propertyKey = @@ -5426,6 +5450,7 @@ class Instance extends Obj implements InstanceRef { _setIfNotNull(json, 'pattern', pattern?.toJson()); _setIfNotNull(json, 'closureFunction', closureFunction?.toJson()); _setIfNotNull(json, 'closureContext', closureContext?.toJson()); + _setIfNotNull(json, 'closureReceiver', closureReceiver?.toJson()); _setIfNotNull(json, 'isCaseSensitive', isCaseSensitive); _setIfNotNull(json, 'isMultiLine', isMultiLine); _setIfNotNull(json, 'propertyKey', propertyKey?.toJson()); diff --git a/runtime/lib/function.cc b/runtime/lib/function.cc index f0645a498a6..03a2cefcd49 100644 --- a/runtime/lib/function.cc +++ b/runtime/lib/function.cc @@ -72,35 +72,41 @@ static bool ClosureEqualsHelper(Zone* zone, return false; } } - if (func_a.IsImplicitClosureFunction() && - func_b.IsImplicitClosureFunction()) { + if (func_a.IsImplicitClosureFunction()) { + ASSERT(func_b.IsImplicitClosureFunction()); if (!func_a.is_static()) { + ASSERT(!func_b.is_static()); // Check that the both receiver instances are the same. - const Context& context_a = Context::Handle(zone, receiver.context()); - const Context& context_b = Context::Handle(zone, other_closure.context()); - return context_a.At(0) == context_b.At(0); - } - } else if (func_a.IsGeneric()) { - // Additional constraints for closures of generic functions: - // (1) Different instantiations of the same generic closure - // with the same type arguments should be equal. - // This means that instantiated generic closures are not unique - // and equality of instantiated generic closures should not be - // based on identity. - // (2) Instantiations of non-equal generic closures should be non-equal. - // This means that equality of non-instantiated generic closures - // should not be based on identity too as it won't match equality - // after instantiation. - if ((receiver.context() != other_closure.context()) || - (receiver.instantiator_type_arguments() != - other_closure.instantiator_type_arguments()) || - (receiver.function_type_arguments() != - other_closure.function_type_arguments())) { - return false; + const Instance& receiver_a = + Instance::Handle(zone, receiver.GetImplicitClosureReceiver()); + const Instance& receiver_b = + Instance::Handle(zone, other_closure.GetImplicitClosureReceiver()); + return receiver_a.ptr() == receiver_b.ptr(); } } else { - // Closures of non-generic functions are unique. - return false; + ASSERT(!func_b.IsImplicitClosureFunction()); + if (func_a.IsGeneric()) { + // Additional constraints for closures of generic functions: + // (1) Different instantiations of the same generic closure + // with the same type arguments should be equal. + // This means that instantiated generic closures are not unique + // and equality of instantiated generic closures should not be + // based on identity. + // (2) Instantiations of non-equal generic closures should be non-equal. + // This means that equality of non-instantiated generic closures + // should not be based on identity too as it won't match equality + // after instantiation. + if ((receiver.GetContext() != other_closure.GetContext()) || + (receiver.instantiator_type_arguments() != + other_closure.instantiator_type_arguments()) || + (receiver.function_type_arguments() != + other_closure.function_type_arguments())) { + return false; + } + } else { + // Closures of non-generic functions are unique. + return false; + } } return true; } diff --git a/runtime/lib/isolate.cc b/runtime/lib/isolate.cc index 015e152be60..25a5649e64c 100644 --- a/runtime/lib/isolate.cc +++ b/runtime/lib/isolate.cc @@ -238,7 +238,7 @@ static ObjectPtr ValidateMessageObject(Zone* zone, case kClosureCid: closure ^= raw; // Only context has to be checked. - working_set->Add(closure.context()); + working_set->Add(closure.RawContext()); continue; #define MESSAGE_SNAPSHOT_ILLEGAL(type) \ diff --git a/runtime/observatory/tests/service/get_version_rpc_test.dart b/runtime/observatory/tests/service/get_version_rpc_test.dart index 39ccbf21ef8..49f24ee2185 100644 --- a/runtime/observatory/tests/service/get_version_rpc_test.dart +++ b/runtime/observatory/tests/service/get_version_rpc_test.dart @@ -12,7 +12,7 @@ var tests = [ final result = await vm.invokeRpcNoUpgrade('getVersion', {}); expect(result['type'], 'Version'); expect(result['major'], 4); - expect(result['minor'], 14); + expect(result['minor'], 15); expect(result['_privateMajor'], 0); expect(result['_privateMinor'], 0); }, diff --git a/runtime/vm/compiler/backend/il_serializer.cc b/runtime/vm/compiler/backend/il_serializer.cc index d3cbf798a7a..41e6287ca76 100644 --- a/runtime/vm/compiler/backend/il_serializer.cc +++ b/runtime/vm/compiler/backend/il_serializer.cc @@ -1581,7 +1581,7 @@ void FlowGraphSerializer::WriteObjectImpl(const Object& x, break; case kClosureCid: { const auto& closure = Closure::Cast(x); - if (closure.context() != Object::null()) { + if (closure.RawContext() != Object::null()) { UNIMPLEMENTED(); } ASSERT(closure.IsCanonical()); @@ -1870,9 +1870,9 @@ const Object& FlowGraphDeserializer::ReadObjectImpl(intptr_t cid, const auto& delayed_type_arguments = Read(); const auto& function = Read(); auto& closure = Closure::ZoneHandle( - Z, - Closure::New(instantiator_type_arguments, function_type_arguments, - delayed_type_arguments, function, Context::Handle(Z))); + Z, Closure::New(instantiator_type_arguments, function_type_arguments, + delayed_type_arguments, function, + Object::null_object())); closure ^= closure.Canonicalize(thread()); return closure; } diff --git a/runtime/vm/compiler/backend/slot.h b/runtime/vm/compiler/backend/slot.h index 06832c59900..e6928147e0e 100644 --- a/runtime/vm/compiler/backend/slot.h +++ b/runtime/vm/compiler/backend/slot.h @@ -102,7 +102,7 @@ class ParsedFunction; #define NONNULLABLE_BOXED_NATIVE_SLOTS_LIST(V) \ V(Array, UntaggedArray, length, Smi, FINAL) \ V(Closure, UntaggedClosure, function, Function, FINAL) \ - V(Closure, UntaggedClosure, context, Context, FINAL) \ + V(Closure, UntaggedClosure, context, Dynamic, FINAL) \ V(Closure, UntaggedClosure, hash, Context, VAR) \ V(Finalizer, UntaggedFinalizer, callback, Closure, FINAL) \ V(NativeFinalizer, UntaggedFinalizer, callback, Pointer, FINAL) \ diff --git a/runtime/vm/compiler/compiler_state.cc b/runtime/vm/compiler/compiler_state.cc index 8aa90efb229..4dd3899444b 100644 --- a/runtime/vm/compiler/compiler_state.cc +++ b/runtime/vm/compiler/compiler_state.cc @@ -37,42 +37,6 @@ T* PutIfAbsent(Thread* thread, return array->At(index); } -LocalVariable* CompilerState::GetDummyCapturedVariable(intptr_t context_id, - intptr_t index) { - return PutIfAbsent( - thread(), &dummy_captured_vars_, index, [&]() { - Zone* const Z = thread()->zone(); - const AbstractType& dynamic_type = - AbstractType::ZoneHandle(Z, Type::DynamicType()); - const String& name = String::ZoneHandle( - Z, Symbols::NewFormatted(thread(), ":context_var%" Pd, index)); - LocalVariable* var = - new (Z) LocalVariable(TokenPosition::kNoSource, - TokenPosition::kNoSource, name, dynamic_type); - var->set_is_captured(); - var->set_index(VariableIndex(index)); - return var; - }); -} - -const ZoneGrowableArray& CompilerState::GetDummyContextSlots( - intptr_t context_id, - intptr_t num_context_variables) { - return *PutIfAbsent>( - thread(), &dummy_slots_, num_context_variables, [&]() { - Zone* const Z = thread()->zone(); - - auto slots = - new (Z) ZoneGrowableArray(num_context_variables); - for (intptr_t i = 0; i < num_context_variables; i++) { - LocalVariable* var = GetDummyCapturedVariable(context_id, i); - slots->Add(&Slot::GetContextVariableSlotFor(thread(), *var)); - } - - return slots; - }); -} - CompilerTracing CompilerState::ShouldTrace(const Function& func) { return FlowGraphPrinter::ShouldPrint(func) ? CompilerTracing::kOn : CompilerTracing::kOff; diff --git a/runtime/vm/compiler/compiler_state.h b/runtime/vm/compiler/compiler_state.h index 56c5e7d9bf1..f7e385ed6ab 100644 --- a/runtime/vm/compiler/compiler_state.h +++ b/runtime/vm/compiler/compiler_state.h @@ -72,19 +72,6 @@ class CompilerState : public ThreadStackResource { SlotCache* slot_cache() const { return slot_cache_; } void set_slot_cache(SlotCache* cache) { slot_cache_ = cache; } - // Create a dummy list of local variables representing a context object - // with the given number of captured variables and given ID. - const ZoneGrowableArray& GetDummyContextSlots( - intptr_t context_id, - intptr_t num_context_slots); - - // Create a dummy LocalVariable that represents a captured local variable - // at the given index in the context with given ID. - // - // This function returns the same variable when it is called with the - // same index. - LocalVariable* GetDummyCapturedVariable(intptr_t context_id, intptr_t index); - bool is_aot() const { return is_aot_; } bool is_optimizing() const { return is_optimizing_; } diff --git a/runtime/vm/compiler/frontend/constant_reader.cc b/runtime/vm/compiler/frontend/constant_reader.cc index fb7137ee5a1..fc0863c7849 100644 --- a/runtime/vm/compiler/frontend/constant_reader.cc +++ b/runtime/vm/compiler/frontend/constant_reader.cc @@ -566,7 +566,7 @@ InstancePtr ConstantReader::ReadConstantInternal(intptr_t constant_index) { // closures. Though inner closures cannot be constants. We should // therefore see `null here. ASSERT(closure.function_type_arguments() == TypeArguments::null()); - Context& context = Context::Handle(Z, closure.context()); + Object& context = Object::Handle(Z, closure.RawContext()); instance = Closure::New(type_arguments2, Object::null_type_arguments(), type_arguments, function, context, Heap::kOld); break; diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc index d218f63d31d..c2f0e5a7d36 100644 --- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc +++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc @@ -2564,7 +2564,7 @@ Fragment StreamingFlowGraphBuilder::BuildSuperPropertyGet(TokenPosition* p) { Function& target = Function::ZoneHandle(Z, function.ImplicitClosureFunction()); ASSERT(!target.IsNull()); - // Generate inline code for allocation closure object with context + // Generate inline code for allocation closure object // which captures `this`. return BuildImplicitClosureCreation(target); } diff --git a/runtime/vm/compiler/frontend/kernel_to_il.cc b/runtime/vm/compiler/frontend/kernel_to_il.cc index c616dd9ad80..ebae2bce360 100644 --- a/runtime/vm/compiler/frontend/kernel_to_il.cc +++ b/runtime/vm/compiler/frontend/kernel_to_il.cc @@ -2153,49 +2153,15 @@ Fragment FlowGraphBuilder::BuildTypedDataFactoryConstructor( return instructions; } -static const LocalScope* MakeImplicitClosureScope(Zone* Z, const Class& klass) { - ASSERT(!klass.IsNull()); - // Note that if klass is _Closure, DeclarationType will be _Closure, - // and not the signature type. - Type& klass_type = Type::ZoneHandle(Z, klass.DeclarationType()); - - LocalVariable* receiver_variable = - new (Z) LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, - Symbols::This(), klass_type); - - receiver_variable->set_is_captured(); - // receiver_variable->set_is_final(); - LocalScope* scope = new (Z) LocalScope(nullptr, 0, 0); - scope->set_context_level(0); - scope->AddVariable(receiver_variable); - scope->AddContextVariable(receiver_variable); - return scope; -} - Fragment FlowGraphBuilder::BuildImplicitClosureCreation( const Function& target) { // The function cannot be local and have parent generic functions. ASSERT(!target.HasGenericParent()); + ASSERT(target.IsImplicitInstanceClosureFunction()); Fragment fragment; fragment += Constant(target); - - // Allocate a context that closes over `this`. - // Note: this must be kept in sync with ScopeBuilder::BuildScopes. - const LocalScope* implicit_closure_scope = - MakeImplicitClosureScope(Z, Class::Handle(Z, target.Owner())); - fragment += AllocateContext(implicit_closure_scope->context_slots()); - LocalVariable* context = MakeTemporary(); - - // Store `this`. The context doesn't need a parent pointer because it doesn't - // close over anything else. - fragment += LoadLocal(context); fragment += LoadLocal(parsed_function_->receiver_var()); - fragment += StoreNativeField( - Slot::GetContextVariableSlotFor( - thread_, *implicit_closure_scope->context_variables()[0]), - StoreFieldInstr::Kind::kInitializing); - fragment += AllocateClosure(); LocalVariable* closure = MakeTemporary(); @@ -3757,8 +3723,6 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfNoSuchMethodForwarder( body += IntConstant(function.NumParameters()); } body += LoadLocal(parsed_function_->current_context_var()); - body += LoadNativeField(Slot::GetContextVariableSlotFor( - thread_, *parsed_function_->receiver_var())); body += StoreFpRelativeSlot( kWordSize * compiler::target::frame_layout.param_end_from_fp); } @@ -3896,8 +3860,6 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfNoSuchMethodForwarder( body += Constant(type); } else { body += LoadLocal(parsed_function_->current_context_var()); - body += LoadNativeField(Slot::GetContextVariableSlotFor( - thread_, *parsed_function_->receiver_var())); } } else { body += LoadLocal(parsed_function_->ParameterVariable(0)); @@ -4222,12 +4184,9 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfImplicitClosureFunction( LocalVariable* receiver = MakeTemporary(); closure += LoadLocal(receiver); } else if (!target.is_static()) { - // The context has a fixed shape: a single variable which is the - // closed-over receiver. + // The closure context is the receiver. closure += LoadLocal(parsed_function_->ParameterVariable(0)); closure += LoadNativeField(Slot::Closure_context()); - closure += LoadNativeField(Slot::GetContextVariableSlotFor( - thread_, *parsed_function_->receiver_var())); } closure += PushExplicitParameters(function); diff --git a/runtime/vm/compiler/frontend/scope_builder.cc b/runtime/vm/compiler/frontend/scope_builder.cc index 012a6865977..833a46f766c 100644 --- a/runtime/vm/compiler/frontend/scope_builder.cc +++ b/runtime/vm/compiler/frontend/scope_builder.cc @@ -397,9 +397,8 @@ ScopeBuildingResult* ScopeBuilder::BuildScopes() { break; } case UntaggedFunction::kMethodExtractor: { - // Add a receiver parameter. Though it is captured, we emit code to - // explicitly copy it to a fixed offset in a freshly-allocated context - // instead of using the generic code for regular functions. + // Add a receiver parameter. Though it is captured, we emit code to + // explicitly copy it to a freshly-allocated closure. // Therefore, it isn't necessary to mark it as captured here. Class& klass = Class::Handle(Z, function.Owner()); Type& klass_type = H.GetDeclarationType(klass); diff --git a/runtime/vm/compiler/stub_code_compiler.cc b/runtime/vm/compiler/stub_code_compiler.cc index 37d78ddd03f..c7fb8ee216e 100644 --- a/runtime/vm/compiler/stub_code_compiler.cc +++ b/runtime/vm/compiler/stub_code_compiler.cc @@ -1182,6 +1182,7 @@ VM_TYPE_TESTING_STUB_CODE_LIST(GENERATE_BREAKPOINT_STUB) // Called for inline allocation of closure. // Input (preserved): // AllocateClosureABI::kFunctionReg: closure function. +// AllocateClosureABI::kContextReg: closure context. // Output: // AllocateClosureABI::kResultReg: new allocated Closure object. // Clobbered: @@ -1191,9 +1192,6 @@ void StubCodeCompiler::GenerateAllocateClosureStub() { target::RoundedAllocationSize(target::Closure::InstanceSize()); __ EnsureHasClassIdInDEBUG(kFunctionCid, AllocateClosureABI::kFunctionReg, AllocateClosureABI::kScratchReg); - __ EnsureHasClassIdInDEBUG(kContextCid, AllocateClosureABI::kContextReg, - AllocateClosureABI::kScratchReg, - /*can_be_null=*/true); if (!FLAG_use_slow_path && FLAG_inline_alloc) { Label slow_case; __ Comment("Inline allocation of uninitialized closure"); diff --git a/runtime/vm/compiler/stub_code_compiler_arm.cc b/runtime/vm/compiler/stub_code_compiler_arm.cc index 3f5a86b4cf7..dae93c0fd4e 100644 --- a/runtime/vm/compiler/stub_code_compiler_arm.cc +++ b/runtime/vm/compiler/stub_code_compiler_arm.cc @@ -224,49 +224,14 @@ void StubCodeCompiler::GenerateBuildMethodExtractorStub( __ ldr(R0, Address(FP, kReceiverOffset * target::kWordSize), NE); __ ldr(R3, Address(R0, R4), NE); - // Push type arguments & extracted method. + // Push type arguments. __ Push(R3); - __ Push(R1); - // Allocate context. - { - Label done, slow_path; - if (!FLAG_use_slow_path && FLAG_inline_alloc) { - __ TryAllocateArray(kContextCid, target::Context::InstanceSize(1), - &slow_path, - R0, // instance - R1, // end address - R2, R3); - __ ldr(R1, Address(THR, target::Thread::object_null_offset())); - __ str(R1, FieldAddress(R0, target::Context::parent_offset())); - __ LoadImmediate(R1, 1); - __ str(R1, FieldAddress(R0, target::Context::num_variables_offset())); - __ b(&done); - } - - __ Bind(&slow_path); - - __ LoadImmediate(/*num_vars=*/R1, 1); - __ LoadObject(CODE_REG, context_allocation_stub); - __ ldr(R0, FieldAddress(CODE_REG, target::Code::entry_point_offset())); - __ blx(R0); - - __ Bind(&done); - } - - // Put context in right register for AllocateClosure call. - __ MoveRegister(AllocateClosureABI::kContextReg, R0); - - // Store receiver in context - __ ldr(AllocateClosureABI::kScratchReg, + // Put function and context (receiver) in right registers for + // AllocateClosure stub. + __ MoveRegister(AllocateClosureABI::kFunctionReg, R1); + __ ldr(AllocateClosureABI::kContextReg, Address(FP, target::kWordSize * kReceiverOffset)); - __ StoreIntoObject(AllocateClosureABI::kContextReg, - FieldAddress(AllocateClosureABI::kContextReg, - target::Context::variable_offset(0)), - AllocateClosureABI::kScratchReg); - - // Pop function. - __ Pop(AllocateClosureABI::kFunctionReg); // Allocate closure. After this point, we only use the registers in // AllocateClosureABI. diff --git a/runtime/vm/compiler/stub_code_compiler_arm64.cc b/runtime/vm/compiler/stub_code_compiler_arm64.cc index e4a8d457d83..944fbdd4087 100644 --- a/runtime/vm/compiler/stub_code_compiler_arm64.cc +++ b/runtime/vm/compiler/stub_code_compiler_arm64.cc @@ -676,51 +676,14 @@ void StubCodeCompiler::GenerateBuildMethodExtractorStub( __ LoadCompressed(R3, Address(R0, R4)); __ Bind(&no_type_args); - // Push type arguments & extracted method. + // Push type arguments. __ Push(R3); - __ Push(R1); - // Allocate context. - { - Label done, slow_path; - if (!FLAG_use_slow_path && FLAG_inline_alloc) { - __ TryAllocateArray(kContextCid, target::Context::InstanceSize(1), - &slow_path, - R0, // instance - R1, // end address - R2, R3); - __ StoreCompressedIntoObjectNoBarrier( - R0, FieldAddress(R0, target::Context::parent_offset()), NULL_REG); - __ LoadImmediate(R1, 1); - __ str(R1, FieldAddress(R0, target::Context::num_variables_offset()), - kFourBytes); - __ b(&done); - } - - __ Bind(&slow_path); - - __ LoadImmediate(/*num_vars=*/R1, 1); - __ LoadObject(CODE_REG, context_allocation_stub); - __ ldr(R0, FieldAddress(CODE_REG, target::Code::entry_point_offset())); - __ blr(R0); - - __ Bind(&done); - } - - // Put context in right register for AllocateClosure call. - __ MoveRegister(AllocateClosureABI::kContextReg, R0); - - // Store receiver in context - __ ldr(AllocateClosureABI::kScratchReg, + // Put function and context (receiver) in right registers for + // AllocateClosure stub. + __ MoveRegister(AllocateClosureABI::kFunctionReg, R1); + __ ldr(AllocateClosureABI::kContextReg, Address(FP, target::kWordSize * kReceiverOffset)); - __ StoreCompressedIntoObject( - AllocateClosureABI::kContextReg, - FieldAddress(AllocateClosureABI::kContextReg, - target::Context::variable_offset(0)), - AllocateClosureABI::kScratchReg); - - // Pop function before pushing context. - __ Pop(AllocateClosureABI::kFunctionReg); // Allocate closure. After this point, we only use the registers in // AllocateClosureABI. diff --git a/runtime/vm/compiler/stub_code_compiler_riscv.cc b/runtime/vm/compiler/stub_code_compiler_riscv.cc index 73783a58475..423d732ad43 100644 --- a/runtime/vm/compiler/stub_code_compiler_riscv.cc +++ b/runtime/vm/compiler/stub_code_compiler_riscv.cc @@ -548,49 +548,14 @@ void StubCodeCompiler::GenerateBuildMethodExtractorStub( __ LoadCompressed(T3, Address(TMP, 0)); __ Bind(&no_type_args); - // Push type arguments & extracted method. - __ PushRegistersInOrder({T3, T1}); + // Push type arguments. + __ PushRegister(T3); - // Allocate context. - { - Label done, slow_path; - if (!FLAG_use_slow_path && FLAG_inline_alloc) { - __ TryAllocateArray(kContextCid, target::Context::InstanceSize(1), - &slow_path, - A0, // instance - T1, // end address - T2, T3); - __ StoreCompressedIntoObjectNoBarrier( - A0, FieldAddress(A0, target::Context::parent_offset()), NULL_REG); - __ LoadImmediate(T1, 1); - __ sw(T1, FieldAddress(A0, target::Context::num_variables_offset())); - __ j(&done, compiler::Assembler::kNearJump); - } - - __ Bind(&slow_path); - - __ LoadImmediate(/*num_vars=*/T1, 1); - __ LoadObject(CODE_REG, context_allocation_stub); - __ lx(RA, FieldAddress(CODE_REG, target::Code::entry_point_offset())); - __ jalr(RA); - - __ Bind(&done); - } - - // Put context in right register for AllocateClosure call. - __ MoveRegister(AllocateClosureABI::kContextReg, A0); - - // Store receiver in context - __ lx(AllocateClosureABI::kScratchReg, + // Put function and context (receiver) in right registers for + // AllocateClosure stub. + __ MoveRegister(AllocateClosureABI::kFunctionReg, T1); + __ lx(AllocateClosureABI::kContextReg, Address(FP, target::kWordSize * kReceiverOffset)); - __ StoreCompressedIntoObject( - AllocateClosureABI::kContextReg, - FieldAddress(AllocateClosureABI::kContextReg, - target::Context::variable_offset(0)), - AllocateClosureABI::kScratchReg); - - // Pop function before pushing context. - __ PopRegister(AllocateClosureABI::kFunctionReg); // Allocate closure. After this point, we only use the registers in // AllocateClosureABI. diff --git a/runtime/vm/compiler/stub_code_compiler_x64.cc b/runtime/vm/compiler/stub_code_compiler_x64.cc index 78d995d6aa3..98b3b563c90 100644 --- a/runtime/vm/compiler/stub_code_compiler_x64.cc +++ b/runtime/vm/compiler/stub_code_compiler_x64.cc @@ -636,49 +636,11 @@ void StubCodeCompiler::GenerateBuildMethodExtractorStub( __ Bind(&no_type_args); __ pushq(RCX); - // Push extracted method. - __ pushq(RBX); - - // Allocate context. - { - Label done, slow_path; - if (!FLAG_use_slow_path && FLAG_inline_alloc) { - __ TryAllocateArray(kContextCid, target::Context::InstanceSize(1), - &slow_path, Assembler::kFarJump, - RAX, // instance - RSI, // end address - RDI); - __ movq(RSI, Address(THR, target::Thread::object_null_offset())); - __ StoreCompressedIntoObjectNoBarrier( - RAX, FieldAddress(RAX, target::Context::parent_offset()), RSI); - __ movl(FieldAddress(RAX, target::Context::num_variables_offset()), - Immediate(1)); - __ jmp(&done); - } - - __ Bind(&slow_path); - - __ LoadImmediate(/*num_vars=*/R10, Immediate(1)); - __ LoadObject(CODE_REG, context_allocation_stub); - __ call(FieldAddress(CODE_REG, target::Code::entry_point_offset())); - - __ Bind(&done); - } - - // Put context in right register for AllocateClosure call. - __ MoveRegister(AllocateClosureABI::kContextReg, RAX); - - // Store receiver in context - __ movq(AllocateClosureABI::kScratchReg, + // Put function and context (receiver) in right registers for + // AllocateClosure stub. + __ MoveRegister(AllocateClosureABI::kFunctionReg, RBX); + __ movq(AllocateClosureABI::kContextReg, Address(RBP, target::kWordSize * kReceiverOffsetInWords)); - __ StoreCompressedIntoObject( - AllocateClosureABI::kContextReg, - FieldAddress(AllocateClosureABI::kContextReg, - target::Context::variable_offset(0)), - AllocateClosureABI::kScratchReg); - - // Pop function. - __ popq(AllocateClosureABI::kFunctionReg); // Allocate closure. After this point, we only use the registers in // AllocateClosureABI. diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc index 46b8ec60b85..d3051a91474 100644 --- a/runtime/vm/debugger.cc +++ b/runtime/vm/debugger.cc @@ -717,7 +717,7 @@ const Context& ActivationFrame::GetSavedCurrentContext() { ASSERT(function().name() == Symbols::call().ptr()); ASSERT(function().IsInvokeFieldDispatcher()); // Closure.call frames. - ctx_ = Closure::Cast(obj).context(); + ctx_ = Closure::Cast(obj).GetContext(); } else if (obj.IsContext()) { ctx_ = Context::Cast(obj).ptr(); } else { diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc index 0de267c9e64..0d93f2ab04a 100644 --- a/runtime/vm/object.cc +++ b/runtime/vm/object.cc @@ -10338,6 +10338,14 @@ bool Function::IsImplicitStaticClosureFunction(FunctionPtr func) { StaticBit::decode(kind_tag); } +bool Function::IsImplicitInstanceClosureFunction(FunctionPtr func) { + NoSafepointScope no_safepoint; + uint32_t kind_tag = func->untag()->kind_tag_.load(std::memory_order_relaxed); + return (KindBits::decode(kind_tag) == + UntaggedFunction::kImplicitClosureFunction) && + !StaticBit::decode(kind_tag); +} + FunctionPtr Function::New(Heap::Space space) { ASSERT(Object::function_class() != Class::null()); return Object::Allocate(space); @@ -10816,11 +10824,10 @@ ClosurePtr Function::ImplicitStaticClosure() const { } Zone* zone = thread->zone(); - const auto& null_context = Context::Handle(zone); const auto& closure = Closure::Handle(zone, Closure::New(Object::null_type_arguments(), Object::null_type_arguments(), *this, - null_context, Heap::kOld)); + Object::null_object(), Heap::kOld)); set_implicit_static_closure(closure); return implicit_static_closure(); } @@ -10828,15 +10835,13 @@ ClosurePtr Function::ImplicitStaticClosure() const { ClosurePtr Function::ImplicitInstanceClosure(const Instance& receiver) const { ASSERT(IsImplicitClosureFunction()); Zone* zone = Thread::Current()->zone(); - const Context& context = Context::Handle(zone, Context::New(1)); - context.SetAt(0, receiver); TypeArguments& instantiator_type_arguments = TypeArguments::Handle(zone); if (!HasInstantiatedSignature(kCurrentClass)) { instantiator_type_arguments = receiver.GetTypeArguments(); } ASSERT(!HasGenericParent()); // No generic parent function. return Closure::New(instantiator_type_arguments, - Object::null_type_arguments(), *this, context); + Object::null_type_arguments(), *this, receiver); } FunctionPtr Function::ImplicitClosureTarget(Zone* zone) const { @@ -26235,7 +26240,7 @@ bool Closure::CanonicalizeEquals(const Instance& other) const { other_closure.function_type_arguments()) && (delayed_type_arguments() == other_closure.delayed_type_arguments()) && (function() == other_closure.function()) && - (context() == other_closure.context()); + (RawContext() == other_closure.RawContext()); } void Closure::CanonicalizeFieldsLocked(Thread* thread) const { @@ -26290,9 +26295,8 @@ uword Closure::ComputeHash() const { result = CombineHashes(result, delayed_type_args.Hash()); } if (func.IsImplicitInstanceClosureFunction()) { - const Context& context = Context::Handle(zone, this->context()); const Instance& receiver = - Instance::Handle(zone, Instance::RawCast(context.At(0))); + Instance::Handle(zone, GetImplicitClosureReceiver()); const Integer& receiverHash = Integer::Handle(zone, receiver.IdentityHashCode(thread)); result = CombineHashes(result, receiverHash.AsTruncatedUint32Value()); @@ -26310,7 +26314,7 @@ uword Closure::ComputeHash() const { ClosurePtr Closure::New(const TypeArguments& instantiator_type_arguments, const TypeArguments& function_type_arguments, const Function& function, - const Context& context, + const Object& context, Heap::Space space) { // We store null delayed type arguments, not empty ones, in closures with // non-generic functions a) to make method extraction slightly faster and @@ -26326,12 +26330,16 @@ ClosurePtr Closure::New(const TypeArguments& instantiator_type_arguments, const TypeArguments& function_type_arguments, const TypeArguments& delayed_type_arguments, const Function& function, - const Context& context, + const Object& context, Heap::Space space) { ASSERT(instantiator_type_arguments.IsCanonical()); ASSERT(function_type_arguments.IsCanonical()); ASSERT(delayed_type_arguments.IsCanonical()); ASSERT(FunctionType::Handle(function.signature()).IsCanonical()); + ASSERT( + (function.IsImplicitInstanceClosureFunction() && context.IsInstance()) || + (function.IsNonImplicitClosureFunction() && context.IsContext()) || + context.IsNull()); const auto& result = Closure::Handle(Object::Allocate(space)); result.untag()->set_instantiator_type_arguments( instantiator_type_arguments.ptr()); diff --git a/runtime/vm/object.h b/runtime/vm/object.h index ae3b6cb2ece..a9949aaacb5 100644 --- a/runtime/vm/object.h +++ b/runtime/vm/object.h @@ -3324,8 +3324,9 @@ class Function : public Object { FunctionPtr ForwardingTarget() const; void SetForwardingTarget(const Function& target) const; - UntaggedFunction::Kind kind() const { - return untag()->kind_tag_.Read(); + UntaggedFunction::Kind kind() const { return KindOf(ptr()); } + static UntaggedFunction::Kind KindOf(FunctionPtr func) { + return func->untag()->kind_tag_.Read(); } UntaggedFunction::AsyncModifier modifier() const { @@ -3877,6 +3878,9 @@ class Function : public Object { bool IsImplicitClosureFunction() const { return kind() == UntaggedFunction::kImplicitClosureFunction; } + static bool IsImplicitClosureFunction(FunctionPtr func) { + return KindOf(func) == UntaggedFunction::kImplicitClosureFunction; + } // Returns true if this function represents a non implicit closure function. bool IsNonImplicitClosureFunction() const { @@ -3895,6 +3899,7 @@ class Function : public Object { bool IsImplicitInstanceClosureFunction() const { return IsImplicitClosureFunction() && !is_static(); } + static bool IsImplicitInstanceClosureFunction(FunctionPtr func); // Returns true if this function has a parent function. bool HasParent() const { return parent_function() != Function::null(); } @@ -12477,13 +12482,21 @@ class Closure : public Instance { return closure.untag()->function(); } - ContextPtr context() const { return untag()->context(); } + ObjectPtr RawContext() const { return untag()->context(); } + + ContextPtr GetContext() const { + ASSERT(!Function::IsImplicitClosureFunction(function())); + return Context::RawCast(RawContext()); + } + + InstancePtr GetImplicitClosureReceiver() const { + ASSERT(Function::IsImplicitInstanceClosureFunction(function())); + return Instance::RawCast(RawContext()); + } + static intptr_t context_offset() { return OFFSET_OF(UntaggedClosure, context_); } - static ContextPtr ContextOf(ClosurePtr closure) { - return closure.untag()->context(); - } // Returns whether the closure is generic, that is, it has a generic closure // function and no delayed type arguments. @@ -12508,14 +12521,14 @@ class Closure : public Instance { static ClosurePtr New(const TypeArguments& instantiator_type_arguments, const TypeArguments& function_type_arguments, const Function& function, - const Context& context, + const Object& context, Heap::Space space = Heap::kNew); static ClosurePtr New(const TypeArguments& instantiator_type_arguments, const TypeArguments& function_type_arguments, const TypeArguments& delayed_type_arguments, const Function& function, - const Context& context, + const Object& context, Heap::Space space = Heap::kNew); FunctionTypePtr GetInstantiatedSignature(Zone* zone) const; diff --git a/runtime/vm/object_graph_copy.cc b/runtime/vm/object_graph_copy.cc index a2b2be60390..1a38992aa2c 100644 --- a/runtime/vm/object_graph_copy.cc +++ b/runtime/vm/object_graph_copy.cc @@ -1031,7 +1031,7 @@ class RetainingPath { if (cid == kClosureCid) { closure ^= raw; // Only context has to be checked. - working_list->Add(closure.context()); + working_list->Add(closure.RawContext()); break; } // These we are not expected to drill into as they can't be on diff --git a/runtime/vm/object_service.cc b/runtime/vm/object_service.cc index 98096a11087..55deec55a6e 100644 --- a/runtime/vm/object_service.cc +++ b/runtime/vm/object_service.cc @@ -1881,10 +1881,19 @@ void Closure::PrintJSONImpl(JSONStream* stream, bool ref) const { JSONObject jsobj(stream); PrintSharedInstanceJSON(&jsobj, ref); jsobj.AddProperty("kind", "Closure"); - jsobj.AddProperty("closureFunction", - Function::Handle(Closure::Cast(*this).function())); - jsobj.AddProperty("closureContext", - Context::Handle(Closure::Cast(*this).context())); + const auto& func = Function::Handle(function()); + jsobj.AddProperty("closureFunction", func); + if (!func.IsImplicitClosureFunction()) { + jsobj.AddProperty("closureContext", Context::Handle(GetContext())); + } else { + jsobj.AddProperty("closureContext", Object::null_object()); + } + if (func.IsImplicitInstanceClosureFunction()) { + jsobj.AddProperty("closureReceiver", + Object::Handle(GetImplicitClosureReceiver())); + } else { + jsobj.AddProperty("closureReceiver", Object::null_object()); + } if (ref) { return; } diff --git a/runtime/vm/object_test.cc b/runtime/vm/object_test.cc index 7ce157ddef5..03250fd85ba 100644 --- a/runtime/vm/object_test.cc +++ b/runtime/vm/object_test.cc @@ -2700,7 +2700,7 @@ ISOLATE_UNIT_TEST_CASE(Closure) { EXPECT_EQ(closure_class.id(), kClosureCid); const Function& closure_function = Function::Handle(closure.function()); EXPECT_EQ(closure_function.ptr(), function.ptr()); - const Context& closure_context = Context::Handle(closure.context()); + const Context& closure_context = Context::Handle(closure.GetContext()); EXPECT_EQ(closure_context.ptr(), context.ptr()); } diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h index 1c4aa2f920a..b3e4073c749 100644 --- a/runtime/vm/raw_object.h +++ b/runtime/vm/raw_object.h @@ -2901,7 +2901,9 @@ class UntaggedClosure : public UntaggedInstance { // determine whether a given closure value is generic. COMPRESSED_POINTER_FIELD(TypeArgumentsPtr, delayed_type_arguments) COMPRESSED_POINTER_FIELD(FunctionPtr, function) - COMPRESSED_POINTER_FIELD(ContextPtr, context) + // For tear-offs - captured receiver. + // For ordinary closures - Context object with captured variables. + COMPRESSED_POINTER_FIELD(ObjectPtr, context) COMPRESSED_POINTER_FIELD(SmiPtr, hash) VISIT_TO(hash) diff --git a/runtime/vm/runtime_entry.cc b/runtime/vm/runtime_entry.cc index ec885398278..dfedaf9b044 100644 --- a/runtime/vm/runtime_entry.cc +++ b/runtime/vm/runtime_entry.cc @@ -693,7 +693,7 @@ DEFINE_RUNTIME_ENTRY(SubtypeCheck, 5) { // Return value: newly allocated closure. DEFINE_RUNTIME_ENTRY(AllocateClosure, 2) { const auto& function = Function::CheckedHandle(zone, arguments.ArgAt(0)); - const auto& context = Context::CheckedHandle(zone, arguments.ArgAt(1)); + const auto& context = Object::Handle(zone, arguments.ArgAt(1)); const Closure& closure = Closure::Handle( zone, Closure::New(Object::null_type_arguments(), Object::null_type_arguments(), diff --git a/runtime/vm/service.h b/runtime/vm/service.h index 05725b7720a..ceb333259cc 100644 --- a/runtime/vm/service.h +++ b/runtime/vm/service.h @@ -18,7 +18,7 @@ namespace dart { #define SERVICE_PROTOCOL_MAJOR_VERSION 4 -#define SERVICE_PROTOCOL_MINOR_VERSION 14 +#define SERVICE_PROTOCOL_MINOR_VERSION 15 class Array; class EmbedderServiceHandler; diff --git a/runtime/vm/service/service.md b/runtime/vm/service/service.md index 40b77aee12b..038c92ccae0 100644 --- a/runtime/vm/service/service.md +++ b/runtime/vm/service/service.md @@ -1,4 +1,4 @@ -# Dart VM Service Protocol 4.14 +# Dart VM Service Protocol 4.15 > Please post feedback to the [observatory-discuss group][discuss-list] @@ -2967,6 +2967,12 @@ class @Instance extends @Object { // Closure @Context closureContext [optional]; + // The receiver captured by tear-off Closure instance. + // + // Provided for instance kinds: + // Closure + @Instance closureReceiver [optional]; + // The port ID for a ReceivePort. // // Provided for instance kinds: @@ -3200,6 +3206,12 @@ class Instance extends Object { // Closure @Context closureContext [optional]; + // The receiver captured by tear-off Closure instance. + // + // Provided for instance kinds: + // Closure + @Instance closureReceiver [optional]; + // Whether this regular expression is case sensitive. // // Provided for instance kinds: @@ -4787,5 +4799,6 @@ version | comments 4.12 | Added `@TypeParameters` and changed `TypeParameters` to extend `Object`. 4.13 | Added `librariesAlreadyCompiled` to `getSourceReport`. 4.14 | Added `Finalizer`, `NativeFinalizer`, and `FinalizerEntry`. +4.15 | Added `closureReceiver` property to `@Instance` and `Instance`. [discuss-list]: https://groups.google.com/a/dartlang.org/forum/#!forum/observatory-discuss diff --git a/runtime/vm/stack_trace.cc b/runtime/vm/stack_trace.cc index 409fb67b3d4..e5eebdebd5f 100644 --- a/runtime/vm/stack_trace.cc +++ b/runtime/vm/stack_trace.cc @@ -353,10 +353,10 @@ void AsyncAwareStackUnwinder::UnwindAwaiterFrame() { while (!awaiter_frame_.closure.IsNull()) { function_ = awaiter_frame_.closure.function(); - context_ = awaiter_frame_.closure.context(); const auto awaiter_link = function_.awaiter_link(); if (awaiter_link.depth != ClosureData::kNoAwaiterLinkDepth) { + context_ = awaiter_frame_.closure.GetContext(); intptr_t depth = awaiter_link.depth; while (depth-- > 0) { context_ = context_.parent(); @@ -448,11 +448,8 @@ void AsyncAwareStackUnwinder::UnwindFrameToStreamListener() { } // All implicit closure functions (tear-offs) have the "this" receiver - // captured. - context_ = closure_.context(); - ASSERT(context_.num_variables() == 1); - stream_iterator_ = context_.At(0); - ASSERT(stream_iterator_.IsInstance()); + // captured in the context. + stream_iterator_ = closure_.GetImplicitClosureReceiver(); if (stream_iterator_.GetClassId() != _StreamIterator().id()) { UNREACHABLE(); @@ -519,7 +516,7 @@ bool StackTraceUtils::GetSuspendState(const Closure& closure, const Function& function = Function::Handle(closure.function()); const auto awaiter_link = function.awaiter_link(); if (awaiter_link.depth != ClosureData::kNoAwaiterLinkDepth) { - Context& context = Context::Handle(closure.context()); + Context& context = Context::Handle(closure.GetContext()); intptr_t depth = awaiter_link.depth; while (depth-- > 0) { context = context.parent();