@@ -542,6 +542,7 @@ namespace jsb
542542 v8::Isolate::Scope isolate_scope (isolate_);
543543 v8::HandleScope handle_scope (isolate_);
544544 const v8::Local<v8::Context> context = context_.Get (isolate_);
545+ v8::Context::Scope context_scope (context);
545546
546547 for (const Message& message : messages)
547548 {
@@ -639,7 +640,7 @@ namespace jsb
639640 // TODO 0. HOW TO HANDLE COMPLICATED SITUATIONS? SUCH AS NESTED OBJECTS?
640641 jsb_nop ();
641642
642- transfer_in (*p_data);
643+ transfer_in (p_context, *p_data);
643644
644645 // call 'ontransfer'
645646 {
@@ -681,6 +682,13 @@ namespace jsb
681682 v8::Local<v8::Value> value;
682683 if (p_message)
683684 {
685+ const std::vector<TransferData>& transfers = p_message->get_transfers ();
686+
687+ for (const auto & transfer : transfers)
688+ {
689+ p_env->transfer_in (p_context, transfer);
690+ }
691+
684692#if JSB_WITH_V8
685693 Serialization::VariantDeserializerDelegate delegate (p_env, p_message->get_transfers ());
686694 v8::ValueDeserializer deserializer (isolate, p_message->get_buffer ().ptr (), p_message->get_buffer ().size (), &delegate);
@@ -952,6 +960,8 @@ namespace jsb
952960 // hold it in a local variable to avoid gc too early
953961 v8::Global<v8::Object> obj_ref = std::move (object_handle->ref_ );
954962
963+ // TODO: Look into if we ought to be calling obj->free_instance_binding(this)
964+
955965 // TODO do not clear the internal field if calling from JS GC
956966 // if (p_finalize != FinalizationType::None)
957967 // {
@@ -1963,10 +1973,11 @@ namespace jsb
19631973 return _call (isolate, context, js_func.object_ .Get (isolate), v8::Undefined (isolate), p_args, p_argcount, r_error);
19641974 }
19651975
1966- void Environment::transfer_out (NativeObjectID p_worker_handle_id, int transfer_index, const Variant& p_variant, TransferData& r_transfer_data)
1976+ void Environment::prepare_transfer_out (NativeObjectID p_worker_handle_id, int transfer_index, const Variant& p_variant, TransferData& r_transfer_data)
19671977 {
19681978 r_transfer_data.source_worker_id = p_worker_handle_id;
19691979 r_transfer_data.transfer_index = transfer_index;
1980+ r_transfer_data.variant = p_variant;
19701981
19711982 if (p_variant.get_type () == Variant::OBJECT)
19721983 {
@@ -1981,22 +1992,28 @@ namespace jsb
19811992
19821993 script_instance->get_property_state (r_transfer_data.state );
19831994 r_transfer_data.script_path = script->get_path ();
1984-
1985- obj->set_script_instance (nullptr );
19861995 }
1996+ }
1997+ }
1998+
1999+ void Environment::finalize_transfer_out (const TransferData& p_transfer_data)
2000+ {
2001+ const Variant& variant = p_transfer_data.variant ;
19872002
2003+ if (variant.get_type () == Variant::OBJECT)
2004+ {
2005+ Object* obj = variant;
2006+ obj->set_script_instance (nullptr );
19882007 free_object (obj, FinalizationType::None);
19892008 }
19902009
19912010 // For now, we don't do anything special with variants since Godot's variant types are thread safe and use
19922011 // copy-on-write semantics. Technically, it may be advantageous for us to (in future?) clear the variant in the
19932012 // source environment since Godot has optimizations in place that will skip reallocation of the underlying data
19942013 // upon mutation if there's a single reference to that data.
1995-
1996- r_transfer_data.variant = p_variant;
19972014 }
19982015
1999- void Environment::transfer_in (const TransferData& p_data)
2016+ void Environment::transfer_in (const v8::Local<v8::Context>& p_context, const TransferData& p_data)
20002017 {
20012018 if (p_data.variant .get_type () == Variant::Type::OBJECT)
20022019 {
@@ -2009,21 +2026,38 @@ namespace jsb
20092026 return ;
20102027 }
20112028
2012- // restore the object state if it's a GodotJSScript
2029+ jsb_checkf (!object_db_.has_object (instance), " transferred in an object already registered in the environment" );
2030+
2031+ if (!object_db_.has_object (instance))
2032+ {
2033+ v8::Local<v8::Object> obj;
2034+ jsb_check (TypeConvert::gd_obj_to_js (isolate_, p_context, instance, obj));
2035+
2036+ if (instance->is_ref_counted ())
2037+ {
2038+ RefCounted *reference = static_cast <RefCounted *>(instance);
2039+
2040+ if (reference->unreference ())
2041+ {
2042+ // Uh, we really shouldn't end up here. This can only occur if another thread is doing something it
2043+ // really shouldn't be doing. I guess we don't want to be responsible for a leak, but this is bad.
2044+ memdelete (reference);
2045+ JSB_LOG (Error, " transferred object unexpectedly freed: %d" , (uint64_t ) object_id);
2046+ return ;
2047+ }
2048+ }
2049+ }
2050+
20132051 if (!p_data.script_path .is_empty ())
20142052 {
2015- // 1. create a script and script instance
2016- // 2. attach the script & script instance to the object
2017- const Ref<GodotJSScript> script = ResourceLoader::load (p_data.script_path );
2018- jsb_check (script.is_valid ());
2019- jsb_unused (script->can_instantiate ());
2020- ScriptInstance* script_instance = script->instance_construct (instance, false );
2021- jsb_check (script_instance);
2053+ ScriptInstance *script_instance = instance->get_script_instance ();
20222054
2023- // 3. restore the object state
2024- for (const Pair<StringName, Variant>& pair : p_data.state )
2055+ if (script_instance)
20252056 {
2026- script_instance->set (pair.first , pair.second );
2057+ for (const Pair<StringName, Variant>& pair : p_data.state )
2058+ {
2059+ script_instance->set (pair.first , pair.second );
2060+ }
20272061 }
20282062 }
20292063 }
@@ -2034,7 +2068,8 @@ namespace jsb
20342068 if (p_variant.get_type () == Variant::OBJECT)
20352069 {
20362070 TransferData* transfer_data = memnew (TransferData);
2037- p_from->transfer_out (p_worker_handle_id, 0 , p_variant, *transfer_data);
2071+ p_from->prepare_transfer_out (p_worker_handle_id, 0 , p_variant, *transfer_data);
2072+ p_from->finalize_transfer_out (*transfer_data);
20382073 p_to->add_async_call (AsyncCall::TYPE_TRANSFER_, transfer_data);
20392074 }
20402075 else
0 commit comments