Skip to content

Investigate feasibility of disabling module loading during async resource loading #122

@Benjamin-Dobell

Description

@Benjamin-Dobell

Currently we use shadow script instances to disable instantiation of our JS script classes.

However, even though our JS script classes aren't instantiated, the JS modules which contain them are still loaded into memory. Aside from this being potentially wasteful, JS modules are by no means guaranteed to be side effect free.

For example, a module may (pre)load a (scene) resource and instantiate it as a side effect. In which case you end up with a stack like:

jsb::TypeConvert::gd_obj_to_js(v8::Isolate *, const v8::Local<> &, Object *, v8::Local<> &) jsb_type_convert.cpp:439
jsb::TypeConvert::gd_var_to_js(v8::Isolate *, const v8::Local<> &, const Variant &, Variant::Type, v8::Local<> &) jsb_type_convert.cpp:328
jsb::ObjectReflectBindingUtil::_godot_object_method(const v8::FunctionCallbackInfo<> &) jsb_object_bindings.cpp:409
Builtins_CallApiCallbackGeneric 0x0000000101d43124
Builtins_InterpreterEntryTrampoline 0x0000000101d414ec
Builtins_JSEntryTrampoline 0x0000000101d3f068
Builtins_JSEntry 0x0000000101d3ecb4
v8::internal::Invoke(v8::internal::Isolate *, const v8::internal::InvokeParams &) 0x0000000100d75080
v8::internal::Execution::Call(v8::internal::Isolate *, v8::internal::DirectHandle<>, v8::internal::DirectHandle<>, v8::base::Vector<>) 0x0000000100d747d4
v8::Function::Call(v8::Isolate *, v8::Local<>, v8::Local<>, int, v8::Local<> *) 0x0000000100be3cd4
jsb::IModuleResolver::load_from_evaluator(jsb::Environment *, jsb::JavaScriptModule &, const String &, const v8::Local<> &) jsb_module_resolver.cpp:72
jsb::DefaultModuleResolver::load(jsb::Environment *, const String &, const jsb::internal::ISourceReader &, jsb::JavaScriptModule &) jsb_module_resolver.cpp:578
jsb::DefaultModuleResolver::load(jsb::Environment *, const String &, jsb::JavaScriptModule &) jsb_module_resolver.cpp:518
jsb::Environment::_load_module(const String &, const String &) jsb_environment.cpp:1146
jsb::Builtins::_require(const v8::FunctionCallbackInfo<> &) jsb_builtins.cpp:87
Builtins_CallApiCallbackGeneric 0x0000000101d43124
Builtins_InterpreterEntryTrampoline 0x0000000101d414ec
Builtins_JSEntryTrampoline 0x0000000101d3f068
Builtins_JSEntry 0x0000000101d3ecb4
v8::internal::Invoke(v8::internal::Isolate *, const v8::internal::InvokeParams &) 0x0000000100d75080
v8::internal::Execution::Call(v8::internal::Isolate *, v8::internal::DirectHandle<>, v8::internal::DirectHandle<>, v8::base::Vector<>) 0x0000000100d747d4
v8::Function::Call(v8::Isolate *, v8::Local<>, v8::Local<>, int, v8::Local<> *) 0x0000000100be3cd4
jsb::IModuleResolver::load_from_evaluator(jsb::Environment *, jsb::JavaScriptModule &, const String &, const v8::Local<> &) jsb_module_resolver.cpp:72
jsb::DefaultModuleResolver::load(jsb::Environment *, const String &, const jsb::internal::ISourceReader &, jsb::JavaScriptModule &) jsb_module_resolver.cpp:578
jsb::DefaultModuleResolver::load(jsb::Environment *, const String &, jsb::JavaScriptModule &) jsb_module_resolver.cpp:518
jsb::Environment::_load_module(const String &, const String &) jsb_environment.cpp:1146
jsb::Builtins::_require(const v8::FunctionCallbackInfo<> &) jsb_builtins.cpp:87
Builtins_CallApiCallbackGeneric 0x0000000101d43124
Builtins_InterpreterEntryTrampoline 0x0000000101d414ec
Builtins_JSEntryTrampoline 0x0000000101d3f068
Builtins_JSEntry 0x0000000101d3ecb4
v8::internal::Invoke(v8::internal::Isolate *, const v8::internal::InvokeParams &) 0x0000000100d75080
v8::internal::Execution::Call(v8::internal::Isolate *, v8::internal::DirectHandle<>, v8::internal::DirectHandle<>, v8::base::Vector<>) 0x0000000100d747d4
v8::Function::Call(v8::Isolate *, v8::Local<>, v8::Local<>, int, v8::Local<> *) 0x0000000100be3cd4
jsb::IModuleResolver::load_from_evaluator(jsb::Environment *, jsb::JavaScriptModule &, const String &, const v8::Local<> &) jsb_module_resolver.cpp:72
jsb::DefaultModuleResolver::load(jsb::Environment *, const String &, const jsb::internal::ISourceReader &, jsb::JavaScriptModule &) jsb_module_resolver.cpp:578
jsb::DefaultModuleResolver::load(jsb::Environment *, const String &, jsb::JavaScriptModule &) jsb_module_resolver.cpp:518
jsb::Environment::_load_module(const String &, const String &) jsb_environment.cpp:1146
jsb::Builtins::_require(const v8::FunctionCallbackInfo<> &) jsb_builtins.cpp:87
Builtins_CallApiCallbackGeneric 0x0000000101d43124
Builtins_InterpreterEntryTrampoline 0x0000000101d414ec
Builtins_JSEntryTrampoline 0x0000000101d3f068
Builtins_JSEntry 0x0000000101d3ecb4
v8::internal::Invoke(v8::internal::Isolate *, const v8::internal::InvokeParams &) 0x0000000100d75080
v8::internal::Execution::Call(v8::internal::Isolate *, v8::internal::DirectHandle<>, v8::internal::DirectHandle<>, v8::base::Vector<>) 0x0000000100d747d4
v8::Function::Call(v8::Isolate *, v8::Local<>, v8::Local<>, int, v8::Local<> *) 0x0000000100be3cd4
jsb::IModuleResolver::load_from_evaluator(jsb::Environment *, jsb::JavaScriptModule &, const String &, const v8::Local<> &) jsb_module_resolver.cpp:72
jsb::DefaultModuleResolver::load(jsb::Environment *, const String &, const jsb::internal::ISourceReader &, jsb::JavaScriptModule &) jsb_module_resolver.cpp:578
jsb::DefaultModuleResolver::load(jsb::Environment *, const String &, jsb::JavaScriptModule &) jsb_module_resolver.cpp:518
jsb::Environment::_load_module(const String &, const String &) jsb_environment.cpp:1146
jsb::Builtins::_require(const v8::FunctionCallbackInfo<> &) jsb_builtins.cpp:87
Builtins_CallApiCallbackGeneric 0x0000000101d43124
Builtins_InterpreterEntryTrampoline 0x0000000101d414ec
Builtins_JSEntryTrampoline 0x0000000101d3f068
Builtins_JSEntry 0x0000000101d3ecb4
v8::internal::Invoke(v8::internal::Isolate *, const v8::internal::InvokeParams &) 0x0000000100d75080
v8::internal::Execution::Call(v8::internal::Isolate *, v8::internal::DirectHandle<>, v8::internal::DirectHandle<>, v8::base::Vector<>) 0x0000000100d747d4
v8::Function::Call(v8::Isolate *, v8::Local<>, v8::Local<>, int, v8::Local<> *) 0x0000000100be3cd4
jsb::IModuleResolver::load_from_evaluator(jsb::Environment *, jsb::JavaScriptModule &, const String &, const v8::Local<> &) jsb_module_resolver.cpp:72
jsb::DefaultModuleResolver::load(jsb::Environment *, const String &, const jsb::internal::ISourceReader &, jsb::JavaScriptModule &) jsb_module_resolver.cpp:578
jsb::DefaultModuleResolver::load(jsb::Environment *, const String &, jsb::JavaScriptModule &) jsb_module_resolver.cpp:518
jsb::Environment::_load_module(const String &, const String &) jsb_environment.cpp:1146
jsb::Environment::load(const String &, jsb::JavaScriptModule **) jsb_environment.cpp:1376
GodotJSScript::load_module_immediately() jsb_script.cpp:513
[Inlined] GodotJSScript::ensure_module_loaded() const jsb_script.h:47
GodotJSScript::is_valid() const jsb_script.h:111
GodotJSScript::is_abstract() const jsb_script.h:113
Object::set_script(const Variant &) object.cpp:1006
Object::set(const StringName &, const Variant &, bool *) object.cpp:313
ResourceLoaderText::load() resource_format_text.cpp:644
ResourceFormatLoaderText::load(const String &, const String &, Error *, bool, float *, ResourceFormatLoader::CacheMode) resource_format_text.cpp:1412
ResourceLoader::_load(const String &, const String &, const String &, ResourceFormatLoader::CacheMode, Error *, bool, float *) resource_loader.cpp:314
ResourceLoader::_run_load_task(void *) resource_loader.cpp:396
ResourceLoader::_load_start(const String &, const String &, ResourceLoader::LoadThreadMode, ResourceFormatLoader::CacheMode, bool) resource_loader.cpp:660
ResourceLoaderText::load() resource_format_text.cpp:463
ResourceFormatLoaderText::load(const String &, const String &, Error *, bool, float *, ResourceFormatLoader::CacheMode) resource_format_text.cpp:1412
ResourceLoader::_load(const String &, const String &, const String &, ResourceFormatLoader::CacheMode, Error *, bool, float *) resource_loader.cpp:314
ResourceLoader::_run_load_task(void *) resource_loader.cpp:396
WorkerThreadPool::_process_task(WorkerThreadPool::Task *) worker_thread_pool.cpp:140
WorkerThreadPool::_thread_function(void *) worker_thread_pool.cpp:218
Thread::thread_callback(void *) thread_apple.cpp:55
_pthread_start 0x000000019bf402e4

Essentially we're now on a background thread and instantiating script instances, which is what we want to avoid. Especially since we're in a shadow environment, any scripts instantiated fail to instantiate as the user may expect. They end up with an incomplete JS object prototype chain — just the native object chain, no custom script classes function. This is due to the current implementation of gd_obj_to_js. Attempting to modify gd_obj_to_js to instantiate the full scripted (non-shadow) script instances is not possible because the thread only has a shadow environment at its disposal.

The work around for now is to not write modules that have side effects resulting in script instantiation. However, from a user's perspective this is non-obvious.

If this is feasible (maybe it's not) then at the very least we'd need to modify is_valid, is_abstract etc. to no longer load the underlying module if the environment associated with Thread.get_caller_id() is a shadow environment.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions