| #ifdef WITH_RIVE_SCRIPTING |
| // Scoped asset reference resolution, shared between the Luau backend and the |
| // wasm module blob (which cannot link rive_lua_libs.cpp). |
| #include "rive/lua/rive_lua_libs.hpp" |
| |
| #include "lua.h" |
| |
| using namespace rive; |
| |
| ScriptingContext::ScopedAssetReference::ScopedAssetReference( |
| lua_State* L, |
| const char* reference) |
| { |
| std::string request(reference); |
| static const std::string kLibraryPrefix = "lib:"; |
| if (request.compare(0, kLibraryPrefix.size(), kLibraryPrefix) == 0) |
| { |
| size_t slash = request.find('/', kLibraryPrefix.size()); |
| if (slash != std::string::npos) |
| { |
| m_label = request.substr(kLibraryPrefix.size(), |
| slash - kLibraryPrefix.size()); |
| m_path = request.substr(slash + 1); |
| return; |
| } |
| } |
| m_bare = std::move(request); |
| if (L == nullptr) |
| { |
| return; |
| } |
| // Mangled chunknames self-describe; the calling chunk's first segment |
| // carries its scope. Skip C frames like lua_require does. |
| lua_Debug ar; |
| int level = 1; |
| do |
| { |
| if (!lua_getinfo(L, level++, "s", &ar)) |
| { |
| return; |
| } |
| } while (ar.what[0] == 'C'); |
| if (ar.source == nullptr) |
| { |
| return; |
| } |
| std::string chunkname(ar.source); |
| size_t slash = chunkname.find('/'); |
| if (slash == std::string::npos) |
| { |
| return; |
| } |
| size_t at = chunkname.find('@'); |
| if (at > 0 && at < slash) |
| { |
| m_scopePrefix = chunkname.substr(0, slash); |
| } |
| } |
| |
| bool ScriptingContext::ScopedAssetReference::matchesLibrary( |
| const std::string& registeredName) const |
| { |
| // <label>[#<digits>]@<digits>/<path> |
| if (registeredName.size() <= m_label.size() || |
| registeredName.compare(0, m_label.size(), m_label) != 0) |
| { |
| return false; |
| } |
| size_t i = m_label.size(); |
| if (registeredName[i] == '#') |
| { |
| size_t start = ++i; |
| while (i < registeredName.size() && registeredName[i] >= '0' && |
| registeredName[i] <= '9') |
| { |
| i++; |
| } |
| if (i == start) |
| { |
| return false; |
| } |
| } |
| if (i >= registeredName.size() || registeredName[i] != '@') |
| { |
| return false; |
| } |
| size_t start = ++i; |
| while (i < registeredName.size() && registeredName[i] >= '0' && |
| registeredName[i] <= '9') |
| { |
| i++; |
| } |
| if (i == start || i >= registeredName.size() || registeredName[i] != '/') |
| { |
| return false; |
| } |
| return registeredName.compare(i + 1, std::string::npos, m_path) == 0; |
| } |
| |
| int ScriptingContext::ScopedAssetReference::match( |
| const std::string& registeredName, |
| const std::string& shortName) const |
| { |
| if (!m_label.empty()) |
| { |
| return matchesLibrary(registeredName) ? 1 : 0; |
| } |
| if (!m_scopePrefix.empty() && |
| registeredName.size() > m_scopePrefix.size() && |
| registeredName[m_scopePrefix.size()] == '/' && |
| registeredName.compare(0, m_scopePrefix.size(), m_scopePrefix) == 0) |
| { |
| // Inside the caller's library both the scope relative path and the |
| // short name address it. |
| if (registeredName.compare(m_scopePrefix.size() + 1, |
| std::string::npos, |
| m_bare) == 0 || |
| shortName == m_bare) |
| { |
| return 2; |
| } |
| return 0; |
| } |
| // Host assets only; other libraries need lib: or their own scope. |
| size_t slash = registeredName.find('/'); |
| size_t firstSegment = |
| slash == std::string::npos ? registeredName.size() : slash; |
| size_t at = registeredName.find('@'); |
| if (at > 0 && at < firstSegment) |
| { |
| return 0; |
| } |
| return registeredName == m_bare || shortName == m_bare ? 1 : 0; |
| } |
| |
| #endif |