blob: d33d9caef30a1b57c454b83928c38948055187e6 [file] [edit]
#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