From 05a4893e6b48421ce898989f81dc92b88926fae3 Mon Sep 17 00:00:00 2001 From: ialex32x Date: Thu, 12 Sep 2024 10:34:20 +0800 Subject: [PATCH 01/10] chore: exports check as jsb_notice --- bridge-v8/jsb_module_resolver.cpp | 5 ++--- internal/jsb_macros.h | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/bridge-v8/jsb_module_resolver.cpp b/bridge-v8/jsb_module_resolver.cpp index 5eb728c..a2d0652 100644 --- a/bridge-v8/jsb_module_resolver.cpp +++ b/bridge-v8/jsb_module_resolver.cpp @@ -66,9 +66,8 @@ namespace jsb // update `exports`, because its value may be covered during the execution process of the elevator script. const v8::Local updated_exports = module_obj->Get(context, jsb_name(environment, exports)).ToLocalChecked(); -#if JSB_DEBUG - if (updated_exports != argv[kIndexExports]) { JSB_LOG(Log, "`exports` is overwritten in module"); } -#endif + jsb_notice(updated_exports != argv[kIndexExports], "`exports` is overwritten in module: %s", filename); + p_module.exports.Reset(isolate, updated_exports); return true; } diff --git a/internal/jsb_macros.h b/internal/jsb_macros.h index f3eaaae..df4eb9d 100644 --- a/internal/jsb_macros.h +++ b/internal/jsb_macros.h @@ -47,7 +47,7 @@ #endif #if JSB_DEBUG -# define jsb_notice(Condition, Format, ...) if (!!(Condition)) { JSB_LOG_IMPL(jsb, Warning, Format, ##__VA_ARGS__); } (void) 0 +# define jsb_notice(Condition, Format, ...) if (!!(Condition)) { JSB_LOG_IMPL(jsb, Log, Format, ##__VA_ARGS__); } (void) 0 #else # define jsb_notice(Condition, Format, ...) (void) 0 #endif From e0ce8cfb7b8fb3587f63014feb56169171fda713 Mon Sep 17 00:00:00 2001 From: ialex32x Date: Thu, 12 Sep 2024 18:26:59 +0800 Subject: [PATCH 02/10] fix: explicit rootDir in tsconfig.json --- jsb.config.h | 2 +- scripts/presets/tsconfig.json.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/jsb.config.h b/jsb.config.h index 64abc00..abd177f 100644 --- a/jsb.config.h +++ b/jsb.config.h @@ -74,6 +74,6 @@ #define JSB_TYPESCRIPT_EXT "ts" #define JSB_JAVASCRIPT_EXT "js" -#define JSB_BUNDLE_VERSION 1 +#define JSB_BUNDLE_VERSION 2 #endif diff --git a/scripts/presets/tsconfig.json.txt b/scripts/presets/tsconfig.json.txt index f1dc080..124eb65 100644 --- a/scripts/presets/tsconfig.json.txt +++ b/scripts/presets/tsconfig.json.txt @@ -26,7 +26,7 @@ /* Modules */ "module": "__MODULE__", /* Specify what module code is generated. */ - // "rootDir": "./", /* Specify the root folder within your source files. */ + "rootDir": "./", /* Specify the root folder within your source files. */ // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ From 0e428b3f8ba7ed73dc6b0268a82c673ab66be2fe Mon Sep 17 00:00:00 2001 From: ialex32x Date: Fri, 13 Sep 2024 09:36:15 +0800 Subject: [PATCH 03/10] chore: update prebuilt deps library package url --- README.md | 4 ++-- SCsub | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a87e5f3..0d6f55d 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ git clone https://github.com/ialex32x/GodotJS.git ```sh # download the archive of prebuilt v8 -curl https://github.com/ialex32x/GodotJS-Dependencies/releases/download/v8_r6/v8_r6.zip --output your/download/path/v8.zip +curl https://github.com/ialex32x/GodotJS-Dependencies/releases/download/v8_r9/v8_r9.zip --output your/download/path/v8.zip # extract the zip file into your `GodotJS` directory, # NOTE: no white space after the switch `-o` @@ -66,7 +66,7 @@ The currently used version of `v8` is `12.4.254.20`. **STEP 3:** Compile and launch `Godot Editor`. Then, [install TypeScript/JavaScript presets](./docs/install_ts_presets.md) into a Godot project. > [!NOTE] -> Since the prebuilt `v8` library is built with the `windows-latest` github runner which uses VS2022, encountering `Unresolved external symbol` errors during linkage with `v8_monolith.lib` or `libucrt.lib` may be addressed by updating to the latest version of the `MSVC v143` toolchain, `Windows Universal CRT SDK` and `Visual Studio 2022` itself. +> Since the prebuilt `v8` library is built with the `windows-latest` github runner which uses VS2022, encountering `Unresolved external symbol` errors during linkage with `v8_monolith.lib` or `libucrt.lib` may be addressed by updating to the latest version of the `MSVC v143` toolchain, `Windows Universal CRT SDK` and `Visual Studio 2022` itself. See [GodotJS-Dependencies README](https://github.com/ialex32x/GodotJS-Dependencies) for the version of MSVC C++ Compiler used in different prebuilt library packages. A prebuilt version of `Godot Editor` can be downloaded from [GodotJS-Build](https://github.com/ialex32x/GodotJS-Build/releases). **Because the GodotJS-Build workflow is currently run manually, it may not be built from the latest commit of `GodotJS`.** diff --git a/SCsub b/SCsub index b90144c..ca7920b 100644 --- a/SCsub +++ b/SCsub @@ -12,6 +12,7 @@ def check(condition, text): print("Error: " + text) Exit(2) +prebuilt_deps_url = "https://github.com/ialex32x/GodotJS-Dependencies/releases/download/v8_r9/v8_r9.zip" module_path = os.path.dirname(os.path.abspath("jsb.h")) module_name = os.path.basename(module_path) javascript_engine = "v8" if env["platform"] != "web" else "quickjs" @@ -214,7 +215,7 @@ if javascript_engine == "v8": # it seems v8_monolith must be compiled with `use_rtti=true` explicitly, or the linker will fail on `v8::ArrayBuffer::Allocator` # check existence of v8 (since it's setup manually) - v8_missing_msg = "The v8 engine is not found in GodotJS, please build it initially or download the prebuilt v8 library from https://github.com/ialex32x/GodotJS-Dependencies/releases/download/v8_r6/v8_r6.zip" + v8_missing_msg = f"The v8 engine is not found in GodotJS, please build it initially or download the prebuilt v8 library from {prebuilt_deps_url}" check(os.path.exists("v8/include/v8.h"), v8_missing_msg) if env["platform"] == "macos": From 30ecd52dbe903f14179a2e43149b77ce718d5ea9 Mon Sep 17 00:00:00 2001 From: ialex32x Date: Fri, 13 Sep 2024 15:44:20 +0800 Subject: [PATCH 04/10] refactor: more flexible SCsub --- SCsub | 69 ++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 42 insertions(+), 27 deletions(-) diff --git a/SCsub b/SCsub index ca7920b..cd0073b 100644 --- a/SCsub +++ b/SCsub @@ -12,15 +12,19 @@ def check(condition, text): print("Error: " + text) Exit(2) -prebuilt_deps_url = "https://github.com/ialex32x/GodotJS-Dependencies/releases/download/v8_r9/v8_r9.zip" +jsb_platform = "linux" if env["platform"] == "linuxbsd" else env["platform"] +jsb_arch = env["arch"] +prebuilt_deps_url = "https://github.com/ialex32x/GodotJS-Dependencies/releases/download/v8_r10/v8_r10.zip" module_path = os.path.dirname(os.path.abspath("jsb.h")) module_name = os.path.basename(module_path) -javascript_engine = "v8" if env["platform"] != "web" else "quickjs" -websocket_lib = "lws" if env["platform"] != "web" else "none" +javascript_engine = "v8" if jsb_platform in ["windows", "linux", "macos", "android", "ios"] else "quickjs" +websocket_lib = "lws" if jsb_platform in ["windows", "linux", "macos"] else "none" print("compiling:", module_name) print("javascript engine:", javascript_engine) print("websocket lib:", websocket_lib) +print("platform:", jsb_platform) +print("arch:", jsb_arch) class CompileDefines: def __init__(self, name, value, help = None): @@ -216,23 +220,32 @@ if javascript_engine == "v8": # check existence of v8 (since it's setup manually) v8_missing_msg = f"The v8 engine is not found in GodotJS, please build it initially or download the prebuilt v8 library from {prebuilt_deps_url}" + v8_basename = f"{jsb_platform}.{jsb_arch}.release" check(os.path.exists("v8/include/v8.h"), v8_missing_msg) - if env["platform"] == "macos": - check(os.path.exists("v8/macos.arm64.release/libv8_monolith.a"), v8_missing_msg) - env.Append(LIBPATH=[f'#modules/{module_name}/v8/macos.arm64.release']) + if jsb_platform == "macos": + check(os.path.exists(f"v8/{v8_basename}/libv8_monolith.a"), v8_missing_msg) + env.Append(LIBPATH=[f'#modules/{module_name}/v8/{v8_basename}']) env.Append(LINKFLAGS=["-lv8_monolith"]) - elif env["platform"] == "linuxbsd": - check(os.path.exists("v8/linux.x86_64.release/libv8_monolith.a"), v8_missing_msg) - env.Append(LIBS=[File('v8/linux.x86_64.release/libv8_monolith.a')]) - else: - v8_lib_path = update_path_for_4_3("v8/windows.x86_64.release") + elif jsb_platform == "linux": + check(os.path.exists(f"v8/{v8_basename}/libv8_monolith.a"), v8_missing_msg) + env.Append(LIBS=[File(f"v8/{v8_basename}/libv8_monolith.a")]) + elif jsb_platform == "windows": + v8_lib_path = update_path_for_4_3(f"v8/{v8_basename}") check(os.path.exists(v8_lib_path+"/v8_monolith.lib"), v8_missing_msg) env.Append(LIBS=[File(v8_lib_path+"/v8_monolith.lib")]) env.Append(LINKFLAGS=["winmm.lib", "Dbghelp.lib"]) + elif jsb_platform == "android": + check(os.path.exists(f"v8/{v8_basename}/libv8_monolith.a"), v8_missing_msg) + env.Append(LIBS=[File(f"v8/{v8_basename}/libv8_monolith.a")]) + elif jsb_platform == "ios": + check(os.path.exists(f"v8/{v8_basename}/libv8_monolith.a"), v8_missing_msg) + env.Append(LIBS=[File(f"v8/{v8_basename}/libv8_monolith.a")]) + else: + check(False, f'v8 is not supported on {env["platform"]}') # platform-specific defines - if env["platform"] != "ios": + if jsb_platform != "ios": env_jsb.AppendUnique(CPPDEFINES=["V8_COMPRESS_POINTERS"]) pass @@ -254,23 +267,25 @@ env_jsb.add_source_files(module_obj, "bridge-v8/*.cpp") # lws if websocket_lib == "lws": lws_missing_msg = "The prebuilt lws lib is missing? Please build it at first." - if env["platform"] == "macos": - check(os.path.exists("lws/macos_arm64_release/libwebsockets.a"), lws_missing_msg) - env_jsb.Append(CPPPATH=["lws/macos_arm64_release/include"]) - env.Append(LIBPATH=[f'#modules/{module_name}/lws/macos_arm64_release']) + lws_basename = f"{jsb_platform}_{jsb_arch}_release" + + if jsb_platform == "macos": + check(os.path.exists(f"lws/{lws_basename}/libwebsockets.a"), lws_missing_msg) + env_jsb.Append(CPPPATH=[f"lws/{lws_basename}/include"]) + env.Append(LIBPATH=[f'#modules/{module_name}/lws/{lws_basename}']) env.Append(LINKFLAGS=["-lwebsockets"]) - elif env["platform"] == "linuxbsd": - check(os.path.exists("lws/linux_x86_64_release/libwebsockets.a"), lws_missing_msg) - env_jsb.Append(CPPPATH=["lws/linux_x86_64_release/include"]) - env.Append(LIBS=[File('lws/linux_x86_64_release/libwebsockets.a')]) - elif env["platform"] == "windows": - check(os.path.exists("lws/windows_x86_64_release/websockets.lib"), lws_missing_msg) + elif jsb_platform == "linux": + check(os.path.exists(f"lws/{lws_basename}/libwebsockets.a"), lws_missing_msg) + env_jsb.Append(CPPPATH=[f"lws/{lws_basename}/include"]) + env.Append(LIBS=[File(f"lws/{lws_basename}/libwebsockets.a")]) + elif jsb_platform == "windows": + check(os.path.exists(f"lws/{lws_basename}/websockets.lib"), lws_missing_msg) if env.msvc and env["vsproj"]: - env.Append(CPPPATH=[f"#modules/{module_name}/lws/windows_x86_64_release/include"]) - env_jsb.Append(CPPPATH=["lws/windows_x86_64_release/include"]) - - env.Append(LIBS=[File("lws/windows_x86_64_release/websockets.lib")]) - #TODO lws builds on other platforms + env.Append(CPPPATH=[f"#modules/{module_name}/lws/{lws_basename}/include"]) + env_jsb.Append(CPPPATH=[f"lws/{lws_basename}/include"]) + env.Append(LIBS=[File(f"lws/{lws_basename}/websockets.lib")]) + else: + check(False, f'lws is not supported on {env["platform"]}') generate_code({ # presets for runtime From bdd1e406c94373f00e3ade250562c343730a9dd0 Mon Sep 17 00:00:00 2001 From: ialex32x Date: Fri, 13 Sep 2024 16:47:10 +0800 Subject: [PATCH 05/10] feat: can build for android/ios --- config.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config.py b/config.py index 269b0df..fcc1812 100644 --- a/config.py +++ b/config.py @@ -3,8 +3,8 @@ def can_build(env, platform): # temp # return platform == "windows" - return platform in ["windows", "macos", "linuxbsd"] # currently supported platforms - # return True + # return platform in ["windows", "macos", "linuxbsd", "android", "ios"] # currently supported platforms + return True def configure(env): pass From 246512c402b6c78fa49183ced1b7665a49b0cc0a Mon Sep 17 00:00:00 2001 From: ialex32x Date: Fri, 13 Sep 2024 17:06:48 +0800 Subject: [PATCH 06/10] fix: compiler errors when building android templates --- bridge-v8/jsb_pch.h | 1 + internal/jsb_internal_pch.h | 1 + internal/jsb_timer_manager.h | 1 - jsb_project_preset.h | 4 ++++ 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/bridge-v8/jsb_pch.h b/bridge-v8/jsb_pch.h index 1dc98f8..640c7ff 100644 --- a/bridge-v8/jsb_pch.h +++ b/bridge-v8/jsb_pch.h @@ -3,6 +3,7 @@ #include #include +#include #include "core/core_constants.h" #include "core/string/string_builder.h" diff --git a/internal/jsb_internal_pch.h b/internal/jsb_internal_pch.h index 0238810..8bdcf32 100644 --- a/internal/jsb_internal_pch.h +++ b/internal/jsb_internal_pch.h @@ -2,6 +2,7 @@ #define GODOTJS_INTERNAL_PCH_H #include +#include #include "core/object/object.h" #include "core/variant/variant_utility.h" diff --git a/internal/jsb_timer_manager.h b/internal/jsb_timer_manager.h index f15926b..2295e65 100644 --- a/internal/jsb_timer_manager.h +++ b/internal/jsb_timer_manager.h @@ -2,7 +2,6 @@ #define GODOTJS_TIMER_MANAGER_H #include "jsb_internal_pch.h" -#include namespace jsb::internal { diff --git a/jsb_project_preset.h b/jsb_project_preset.h index 4f90281..0bb2d48 100644 --- a/jsb_project_preset.h +++ b/jsb_project_preset.h @@ -9,7 +9,11 @@ struct GodotJSProjectPreset static const char* get_source(const String& p_filename, size_t& r_len) { if (const char* res = get_source_rt(p_filename, r_len)) return res; +#ifdef TOOLS_ENABLED return get_source_ed(p_filename, r_len); +#else + return nullptr; +#endif } static const char* get_source_rt(const String& p_filename, size_t& r_len); From 06b709ccf25bea79834f4ff8b39d45676520f2d7 Mon Sep 17 00:00:00 2001 From: ialex32x Date: Fri, 13 Sep 2024 20:26:50 +0800 Subject: [PATCH 07/10] chore: check before read to avoid redundant warnings --- internal/jsb_settings.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/internal/jsb_settings.cpp b/internal/jsb_settings.cpp index cf67f3b..e532698 100644 --- a/internal/jsb_settings.cpp +++ b/internal/jsb_settings.cpp @@ -47,8 +47,12 @@ namespace jsb::internal JSB_LOG(Warning, "EditorSettings is not available for %s", jsb_typename(jsb::internal::Settings)); } } - _EDITOR_DEF(kEdDebuggerPort, 9230, true); - _EDITOR_DEF(kEdIgnoredClasses, PackedStringArray(), false); + // check before read to avoid redundant warnings + if (!EditorSettings::get_singleton()) + { + _EDITOR_DEF(kEdDebuggerPort, 9230, true); + _EDITOR_DEF(kEdIgnoredClasses, PackedStringArray(), false); + } #endif _GLOBAL_DEF(kRtDebuggerPort, 9229, JSB_SET_RESTART(true), JSB_SET_IGNORE_DOCS(false), JSB_SET_BASIC(false), JSB_SET_INTERNAL(false)); _GLOBAL_DEF(kRtSourceMapEnabled, true, JSB_SET_RESTART(false), JSB_SET_IGNORE_DOCS(false), JSB_SET_BASIC(true), JSB_SET_INTERNAL(false)); From 4be9cb73c427e2995eae80335a9609508ccd8ff8 Mon Sep 17 00:00:00 2001 From: ialex32x Date: Fri, 13 Sep 2024 20:46:45 +0800 Subject: [PATCH 08/10] fix: check before read to avoid redundant warnings --- internal/jsb_settings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/jsb_settings.cpp b/internal/jsb_settings.cpp index e532698..5e2ab02 100644 --- a/internal/jsb_settings.cpp +++ b/internal/jsb_settings.cpp @@ -48,7 +48,7 @@ namespace jsb::internal } } // check before read to avoid redundant warnings - if (!EditorSettings::get_singleton()) + if (EditorSettings::get_singleton()) { _EDITOR_DEF(kEdDebuggerPort, 9230, true); _EDITOR_DEF(kEdIgnoredClasses, PackedStringArray(), false); From 7804be098163a0178d1c506d83e97fa7ec634d66 Mon Sep 17 00:00:00 2001 From: ialex32x Date: Fri, 13 Sep 2024 20:47:17 +0800 Subject: [PATCH 09/10] fix: DocTools not available in play mode --- weaver/jsb_gdjs_script.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/weaver/jsb_gdjs_script.cpp b/weaver/jsb_gdjs_script.cpp index 97a6692..c565961 100644 --- a/weaver/jsb_gdjs_script.cpp +++ b/weaver/jsb_gdjs_script.cpp @@ -464,11 +464,14 @@ void GodotJSScript::load_module() update_exports(); #ifdef TOOLS_ENABLED // temp and tricky workaround to avoid missing doc when showing on inspector the first time after load - const Vector documentations = get_documentation(); - for (int i = 0; i < documentations.size(); i++) + if (DocTools* doc_tools = EditorHelp::get_doc_data()) { - const DocData::ClassDoc &doc = documentations.get(i); - EditorHelp::get_doc_data()->add_doc(doc); + const Vector documentations = get_documentation(); + for (int i = 0; i < documentations.size(); i++) + { + const DocData::ClassDoc &doc = documentations.get(i); + doc_tools->add_doc(doc); + } } #endif return; From f1174e188358f0d181bfcbe585424fc7e22703c9 Mon Sep 17 00:00:00 2001 From: ialex32x Date: Sat, 14 Sep 2024 12:23:25 +0800 Subject: [PATCH 10/10] fix: crash on android --- README.md | 11 ++++++----- SCsub | 8 ++++++-- TODO | 1 - 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 0d6f55d..f341767 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ git clone https://github.com/ialex32x/GodotJS.git ```sh # download the archive of prebuilt v8 -curl https://github.com/ialex32x/GodotJS-Dependencies/releases/download/v8_r9/v8_r9.zip --output your/download/path/v8.zip +curl https://github.com/ialex32x/GodotJS-Dependencies/releases/download/v8_r11/v8_r11.zip --output your/download/path/v8.zip # extract the zip file into your `GodotJS` directory, # NOTE: no white space after the switch `-o` @@ -56,7 +56,8 @@ The module directroy structure looks like this: ┃ ┣━ include ┃ ┣━ linux.x86_64.release ┃ ┣━ macos.arm64.release - ┃ ┗━ windows.x86_64.release + ┃ ┣━ windows_x86_64_release + ┃ ┗━ ... ┣━ gridmap ┣━ ... ``` @@ -108,9 +109,9 @@ For more information on how to use `GodotJS` in a project, check out [GodotJSExa - [x] Windows: x86_64 - [ ] Windows: arm64, UWP - [x] MacOS: arm64 -- [ ] MacOS: x86_64 +- [x] MacOS: x86_64 (not tested) - [x] Linux: x86_64 - [ ] Linux: arm64 -- [ ] Android -- [ ] iOS +- [x] Android: arm32, arm64, x86_64 (`ndk_platform=android-24`) +- [x] iOS: arm64, x86_64 (not tested) - [ ] WebAssembly (quickjs only) diff --git a/SCsub b/SCsub index cd0073b..6ffd049 100644 --- a/SCsub +++ b/SCsub @@ -14,7 +14,7 @@ def check(condition, text): jsb_platform = "linux" if env["platform"] == "linuxbsd" else env["platform"] jsb_arch = env["arch"] -prebuilt_deps_url = "https://github.com/ialex32x/GodotJS-Dependencies/releases/download/v8_r10/v8_r10.zip" +prebuilt_deps_url = "https://github.com/ialex32x/GodotJS-Dependencies/releases/download/v8_r11/v8_r11.zip" module_path = os.path.dirname(os.path.abspath("jsb.h")) module_name = os.path.basename(module_path) javascript_engine = "v8" if jsb_platform in ["windows", "linux", "macos", "android", "ios"] else "quickjs" @@ -26,6 +26,10 @@ print("websocket lib:", websocket_lib) print("platform:", jsb_platform) print("arch:", jsb_arch) +if jsb_platform == "android": + print("ndk_platform:", env["ndk_platform"]) + print("ANDROID_NDK_ROOT:", env["ANDROID_NDK_ROOT"]) + class CompileDefines: def __init__(self, name, value, help = None): self.name = name @@ -245,7 +249,7 @@ if javascript_engine == "v8": check(False, f'v8 is not supported on {env["platform"]}') # platform-specific defines - if jsb_platform != "ios": + if jsb_platform not in ["ios", "android"]: env_jsb.AppendUnique(CPPDEFINES=["V8_COMPRESS_POINTERS"]) pass diff --git a/TODO b/TODO index e77a4bc..3f2147d 100644 --- a/TODO +++ b/TODO @@ -3,7 +3,6 @@ Core: ✔ directly use typescripts in `godot editor`, hide compiled results (.js files) @done(24-07-08) ✔ global scope defined constants (such as PropertyHint) @done ☐ source-map: incorrect Ln:Col on bundle scripts - ☐ core: possible to call virtual methods even if not defined by scripts ✔ embed jsb sources in C++ @done(24-08-15 23:16) ✔ exception is swallowed if thrown in GodotJS ScriptInstance::callp method @started(24-04-30 17:52) @done(24-07-05 21:42) ✔ SArray: return a simple wrapper with address guarded scope instead of a barebone reference? (`Env::get_object_class` etc.) @done(24-08-15)