rive-app / rive-app/rive-runtime
Regression (runtime-v0.1.107): eager per-file ScriptingVM in File::import breaks deferred view-model binding
Nobody has claimed this yet.
- Dominant language
- C++
- Stars
- 1.2k
- Forks
- 121
- PR merge metrics
- No merged PRs in 30d
Description
Summary
Since runtime-v0.1.107, a script in a state machine that reads data-bound view-model properties throws attempt to index nil (and its effects never apply) when the host binds the view-model instance after loading the file via the CommandQueue. It worked on runtime-v0.1.106 and earlier.
Bisect
Built rive_render (headless CommandQueue host) against successive tags:
| runtime | result |
|---|---|
| v0.1.106 | ✅ script runs, reads bound VM data correctly |
| v0.1.107 | ❌ attempt to index nil on data-bound property access in the script |
| v0.1.127 | ❌ same |
Cause
src/command_server.cpp, CommandServer::processCommands() loadFile case, gained (v0.1.107):
#ifdef WITH_RIVE_SCRIPTING
auto scriptingContext = std::make_unique<CPPRuntimeScriptingContext>(m_factory);
scriptingContext->setRenderContext(m_factory);
auto vm = make_rcp<ScriptingVM>(std::move(scriptingContext));
rcp<rive::File> file = rive::File::import(rivBytes, m_factory, nullptr, m_fileAssetLoader, vm.get());
#else
rcp<rive::File> file = rive::File::import(rivBytes, m_factory, nullptr, m_fileAssetLoader);
#endif
The per-file ScriptingVM is now created eagerly, inside File::import, so the file's script instances initialize during import. But a CommandQueue host typically binds view-model data after loadFile:
loadFile → instantiateArtboard/StateMachine → createDefaultInstance →
setViewModelInstance* → bindViewModelInstance → advance
So the script's first run happens against a view model that isn't bound yet. Reads of data-bound (incl. nested) properties resolve to nil. Even though the script re-runs when the VM is later bound, the data-bound targets it needs are unavailable at the point it errors.
Expected
A script should be able to observe view-model data that the host binds after a scripted import (the pre-v0.1.107 behavior), or there should be a documented way to defer/re-initialize the script VM after bindViewModelInstance.
Workaround
Importing without the eager VM (the pre-0.1.107 File::import(...) path) restores correct behavior — scripts then initialize lazily, after the view model is bound.
Environment
- Headless CommandQueue/CommandServer host (Metal on macOS, Vulkan/SwiftShader on Linux),
WITH_RIVE_SCRIPTINGenabled. - Reproduces identically on macOS and Linux; not platform-specific.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in src/command_server.cpp, at CommandServer::processCommands() and its loadFile case, then trace the documented loadFile → instantiateArtboard/StateMachine → setViewModelInstance* → bindViewModelInstance → advance sequence. Compare the v0.1.107 eager ScriptingVM path with the pre-v0.1.107 File::import path and reproduce with a headless CommandQueue host. Done means a script can read bound, including nested, view-model data when binding occurs after import without attempting to index nil.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 65/100