Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/IJavaScriptContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
class IJavaScriptContext
{
public:
virtual ~IJavaScriptContext() = default; //change added
virtual bool runScript(const char *script, bool isModule=true, std::string name="", const char *args = nullptr, bool isApplication=false) = 0;
virtual bool runFile(const char *file, const char* args, bool isApplication=false) = 0;
virtual std::string getUrl() = 0;
Expand Down
62 changes: 36 additions & 26 deletions src/JSRuntimeClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,9 @@ bool JSRuntimeClient::run()
t.detach();

std::unique_lock<std::mutex> lock(mStateMutex);
mStateCondition.wait_for(lock, std::chrono::seconds(5));
mStateCondition.wait_for(lock, std::chrono::seconds(5), [this]() {
return mState != "none";
});
return mState == "open";
}

Expand Down Expand Up @@ -152,37 +154,45 @@ void JSRuntimeClient::onClose(websocketpp::connection_hdl hdl)
#ifndef UNIT_TEST_BUILD
int main(int argc, char **argv)
{
std::string command;
std::string response;
try {
std::string command;
std::string response;

if (argc > 1)
{
NativeJSLogger::log(INFO, "Send input commands at ws://localhost:%s\n", std::to_string(WS_SERVER_PORT).c_str());
return -1;
}

JSRuntimeClient *client = JSRuntimeClient::getInstance();
client->initialize(WS_SERVER_PORT);
if (!client->run())
{
NativeJSLogger::log(ERROR, "Unable to connect to server\n");
return -1;
}
if (argc > 1)
{
NativeJSLogger::log(INFO, "Send input commands at ws://localhost:%s\n", std::to_string(WS_SERVER_PORT).c_str());
return -1;
}

while (client->getState() == "open" && std::getline(std::cin, command))
{
client->sendCommand(command, response);
if (!response.empty())
JSRuntimeClient *client = JSRuntimeClient::getInstance();
client->initialize(WS_SERVER_PORT);
if (!client->run())
{
NativeJSLogger::log(INFO, "Response: %s\n", response.c_str());
NativeJSLogger::log(ERROR, "Unable to connect to server\n");
return -1;
}
else

while (client->getState() == "open" && std::getline(std::cin, command))
{
NativeJSLogger::log(WARN, "Missing response\n");
break;
client->sendCommand(command, response);
if (!response.empty())
{
NativeJSLogger::log(INFO, "Response: %s\n", response.c_str());
}
else
{
NativeJSLogger::log(WARN, "Missing response\n");
break;
}
}
}

return 0;
return 0;
} catch (const std::exception& e) {
NativeJSLogger::log(ERROR, "Uncaught exception in main: %s\n", e.what());
return -1;
} catch (...) {
NativeJSLogger::log(ERROR, "Unknown exception caught in main\n");
return -1;
}
}
#endif
46 changes: 28 additions & 18 deletions src/JSRuntimeClientContainer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,36 @@
#include "NativeJSLogger.h"
int main()
{
std::string containerId = "com.sky.as.apps_TestApp";
const std::string basePath = "/opt/twocontext"; // constant base path
const std::vector<std::string> apps = {"app1", "app2"};

std::string ipAddress = JSRuntimeContainer::getContainerIpAddress(containerId);
if (ipAddress.empty()) {
NativeJSLogger::log(ERROR, "Failed to retrieve IP address for container");
return 1;
}
try {
std::string containerId = "com.sky.as.apps_TestApp";
const std::string basePath = "/opt/twocontext"; // constant base path
const std::vector<std::string> apps = {"app1", "app2"};

std::string ipAddress = JSRuntimeContainer::getContainerIpAddress(containerId);
if (ipAddress.empty()) {
NativeJSLogger::log(ERROR, "Failed to retrieve IP address for container");
return 1;
}

for (const auto &app : apps) {
std::string url = basePath + std::string("/") + app + std::string("/index.html");
if (access(url.c_str(), F_OK) == 0) {
std::string pathAppConfig = basePath + std::string("/") + app + std::string("/app.config");
std::string options = JSRuntimeContainer::parseAppConfig(pathAppConfig);
std::string message = JSRuntimeContainer::buildLaunchMessage(url, options);
JSRuntimeContainer::connectAndSend(ipAddress, message);
for (const auto &app : apps) {
std::string url = basePath + std::string("/") + app + std::string("/index.html");
if (access(url.c_str(), F_OK) == 0) {
std::string pathAppConfig = basePath + std::string("/") + app + std::string("/app.config");
std::string options = JSRuntimeContainer::parseAppConfig(pathAppConfig);
std::string message = JSRuntimeContainer::buildLaunchMessage(url, options);
JSRuntimeContainer::connectAndSend(ipAddress, message);
}
}
}

return 0;
return 0;
}
catch (const std::exception& e) {
NativeJSLogger::log(ERROR, "Exception in main: %s", e.what());
return 1;
}
catch (...) {
NativeJSLogger::log(ERROR, "Unknown exception in main");
return 1;
}
}

1 change: 1 addition & 0 deletions src/JSRuntimeServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ class JsonWrap
if (!itm || !cJSON_IsNumber(itm))
{
std::cerr << "Error: " << name << "is not a Uint32_t" << std::endl;

Copilot AI Jan 19, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message in JsonWrap::getUint32 prints "Error: " << name << "is not a Uint32_t", which is missing a space before is and uses a non-standard type spelling (Uint32_t). For clarity and consistency with the actual type, consider changing the message to something like "Error: <name> is not a uint32_t".

Suggested change
std::cerr << "Error: " << name << "is not a Uint32_t" << std::endl;
std::cerr << "Error: " << name << " is not a uint32_t" << std::endl;

Copilot uses AI. Check for mistakes.
res=0;
err = true;
}
else
Expand Down
91 changes: 71 additions & 20 deletions src/NativeJSRenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,6 @@ void NativeJSRenderer::createApplicationInternal(ApplicationRequest& appRequest)
context->setCreateApplicationEndTime(endTime, id);

mContextMap[id].context=context;
mUserMutex.unlock();
}

void NativeJSRenderer::runApplicationInternal(ApplicationRequest& appRequest)
Expand Down Expand Up @@ -445,7 +444,7 @@ void NativeJSRenderer::terminateApplicationInternal(ApplicationRequest& AppReque

else
{
NativeJSLogger::log(ERROR, "Unable to find application with id: %d and url: %s\n", id, mContextMap[id].url);
NativeJSLogger::log(ERROR, "Unable to find application with id: %d and url: %s\n", id, mContextMap[id].url.c_str());

Copilot AI Jan 19, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In terminateApplicationInternal, the else branch logs mContextMap[id].url.c_str() even though this branch is only taken when mContextMap.find(id) == mContextMap.end(), so mContextMap[id] will default-insert a new entry for a non-existent id just to log it. This both masks the "not found" condition and mutates the map unexpectedly; the log should use the iterator (mapEntry) or avoid indexing the map when the id is absent.

Suggested change
NativeJSLogger::log(ERROR, "Unable to find application with id: %d and url: %s\n", id, mContextMap[id].url.c_str());
NativeJSLogger::log(ERROR, "Unable to find application with id: %d\n", id);

Copilot uses AI. Check for mistakes.
return ;
}

Expand All @@ -468,7 +467,6 @@ void NativeJSRenderer::run()
{
while(mRunning)
{
uint32_t id;
mUserMutex.lock();
if (mConsoleMode) {
processDevConsoleRequests();
Expand Down Expand Up @@ -506,6 +504,7 @@ void NativeJSRenderer::run()
if(!mTestFileName.empty())
{
ModuleSettings settings;
uint32_t id;
settings.enableJSDOM = mEnableTestFileDOMSupport;
ApplicationRequest appRequest(id, RUN, mTestFileName, settings.enableHttp, settings.enableXHR, settings.enableWebSocket, settings.enableWebSocketEnhanced, settings.enableFetch, settings.enableJSDOM, settings.enableWindow, settings.enablePlayer);
NativeJSRenderer::createApplicationInternal(appRequest);
Comment on lines 504 to 510

Copilot AI Jan 19, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In NativeJSRenderer::run(), the id used to construct ApplicationRequest appRequest(id, RUN, mTestFileName, ...) is never initialized, so mId and all subsequent uses of this id (context map access, logging, etc.) rely on an indeterminate value. This is undefined behavior and can result in collisions or hard-to-reproduce failures; the id should be obtained from a defined source (e.g., a counter or createApplication-like path) before being passed into ApplicationRequest.

Copilot uses AI. Check for mistakes.
Expand All @@ -531,27 +530,30 @@ void NativeJSRenderer::run()

void NativeJSRenderer::processDevConsoleRequests()
{
std::deque<std::string> localQueue;

// Move items from shared queue to local queue while holding the lock
mConsoleState->inputMutex.lock();

if (mConsoleState->codeToExecute.empty()) {
mConsoleState->inputMutex.unlock();
return;
}
localQueue.swap(mConsoleState->codeToExecute);
mConsoleState->inputMutex.unlock();

std::lock_guard<std::mutex> lockg(mConsoleState->isProcessing_cv_m);
bool dataProcessed = false;

for (; !mConsoleState->codeToExecute.empty(); mConsoleState->codeToExecute.pop_front()) {
bool ret = mConsoleState->consoleContext->runScript(mConsoleState->codeToExecute.front().c_str(), false);
// Process items from local queue (no race condition)
for (const auto& code : localQueue) {
bool ret = mConsoleState->consoleContext->runScript(code.c_str(), false);
dataProcessed = true;
}

if (dataProcessed) {
mConsoleState->isProcessing = false;
mConsoleState->isProcessing_cv.notify_one();
}

mConsoleState->inputMutex.unlock();
}

std::atomic_bool NativeJSRenderer::consoleLoop = true;
Expand Down Expand Up @@ -620,18 +622,67 @@ bool NativeJSRenderer::downloadFile(std::string& url, MemoryStruct& chunk)
curl = curl_easy_init();
if (curl)
{
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, HeaderCallback);
curl_easy_setopt(curl, CURLOPT_HEADERDATA, (void *)&chunk);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30);
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, true);
curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-agent/1.0");
curl_easy_setopt(curl, CURLOPT_PROXY, "");
res = curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
if (res != CURLE_OK) {
NativeJSLogger::log(ERROR, "Failed to set CURLOPT_URL: %s\n", curl_easy_strerror(res));
curl_easy_cleanup(curl);
}
res = curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
if (res != CURLE_OK) {
NativeJSLogger::log(ERROR, "Failed to set CURLOPT_FOLLOWLOCATION: %s\n", curl_easy_strerror(res));
curl_easy_cleanup(curl);
}
res = curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, HeaderCallback);
if (res != CURLE_OK) {
NativeJSLogger::log(ERROR, "Failed to set CURLOPT_HEADERFUNCTION: %s\n", curl_easy_strerror(res));
curl_easy_cleanup(curl);
}
res = curl_easy_setopt(curl, CURLOPT_HEADERDATA, (void *)&chunk);
if (res != CURLE_OK) {
NativeJSLogger::log(ERROR, "Failed to set CURLOPT_HEADERDATA: %s\n", curl_easy_strerror(res));
curl_easy_cleanup(curl);
}
res = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
if (res != CURLE_OK) {
NativeJSLogger::log(ERROR, "Failed to set CURLOPT_WRITEFUNCTION: %s\n", curl_easy_strerror(res));
curl_easy_cleanup(curl);
}
res = curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);
if (res != CURLE_OK) {
NativeJSLogger::log(ERROR, "Failed to set CURLOPT_WRITEDATA: %s\n", curl_easy_strerror(res));
curl_easy_cleanup(curl);
}
res = curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30);
if (res != CURLE_OK) {
NativeJSLogger::log(ERROR, "Failed to set CURLOPT_TIMEOUT: %s\n", curl_easy_strerror(res));
curl_easy_cleanup(curl);
}
res = curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
if (res != CURLE_OK) {
NativeJSLogger::log(ERROR, "Failed to set CURLOPT_NOSIGNAL: %s\n", curl_easy_strerror(res));
curl_easy_cleanup(curl);
}
res = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2);
if (res != CURLE_OK) {
NativeJSLogger::log(ERROR, "Failed to set CURLOPT_SSL_VERIFYHOST: %s\n", curl_easy_strerror(res));
curl_easy_cleanup(curl);
}
res = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, true);
if (res != CURLE_OK) {
NativeJSLogger::log(ERROR, "Failed to set CURLOPT_SSL_VERIFYPEER: %s\n", curl_easy_strerror(res));
curl_easy_cleanup(curl);
}
res = curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-agent/1.0");
if (res != CURLE_OK) {
NativeJSLogger::log(ERROR, "Failed to set CURLOPT_USERAGENT: %s\n", curl_easy_strerror(res));
curl_easy_cleanup(curl);

}
res = curl_easy_setopt(curl, CURLOPT_PROXY, "");
if (res != CURLE_OK) {
NativeJSLogger::log(ERROR, "Failed to set CURLOPT_PROXY: %s\n", curl_easy_strerror(res));
curl_easy_cleanup(curl);
Comment on lines +664 to +684

Copilot AI Jan 19, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In NativeJSRenderer::downloadFile, when any curl_easy_setopt call fails you log an error and call curl_easy_cleanup(curl) but then continue to use curl for subsequent curl_easy_setopt calls and later curl_easy_perform, which is undefined behavior because the handle has been cleaned up. After a failed curl_easy_setopt, the function should stop using the handle (e.g., return false immediately or guard the curl_easy_perform path with a success flag) instead of continuing with a freed/invalid CURL*.

Suggested change
}
res = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2);
if (res != CURLE_OK) {
NativeJSLogger::log(ERROR, "Failed to set CURLOPT_SSL_VERIFYHOST: %s\n", curl_easy_strerror(res));
curl_easy_cleanup(curl);
}
res = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, true);
if (res != CURLE_OK) {
NativeJSLogger::log(ERROR, "Failed to set CURLOPT_SSL_VERIFYPEER: %s\n", curl_easy_strerror(res));
curl_easy_cleanup(curl);
}
res = curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-agent/1.0");
if (res != CURLE_OK) {
NativeJSLogger::log(ERROR, "Failed to set CURLOPT_USERAGENT: %s\n", curl_easy_strerror(res));
curl_easy_cleanup(curl);
}
res = curl_easy_setopt(curl, CURLOPT_PROXY, "");
if (res != CURLE_OK) {
NativeJSLogger::log(ERROR, "Failed to set CURLOPT_PROXY: %s\n", curl_easy_strerror(res));
curl_easy_cleanup(curl);
return ret;
}
res = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2);
if (res != CURLE_OK) {
NativeJSLogger::log(ERROR, "Failed to set CURLOPT_SSL_VERIFYHOST: %s\n", curl_easy_strerror(res));
curl_easy_cleanup(curl);
return ret;
}
res = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, true);
if (res != CURLE_OK) {
NativeJSLogger::log(ERROR, "Failed to set CURLOPT_SSL_VERIFYPEER: %s\n", curl_easy_strerror(res));
curl_easy_cleanup(curl);
return ret;
}
res = curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-agent/1.0");
if (res != CURLE_OK) {
NativeJSLogger::log(ERROR, "Failed to set CURLOPT_USERAGENT: %s\n", curl_easy_strerror(res));
curl_easy_cleanup(curl);
return ret;
}
res = curl_easy_setopt(curl, CURLOPT_PROXY, "");
if (res != CURLE_OK) {
NativeJSLogger::log(ERROR, "Failed to set CURLOPT_PROXY: %s\n", curl_easy_strerror(res));
curl_easy_cleanup(curl);
return ret;

Copilot uses AI. Check for mistakes.
}


//curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
Expand Down
10 changes: 9 additions & 1 deletion src/jsc/JavaScriptContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,14 @@ if (mModuleSettings.enablePlayer)
gTopLevelContext = nullptr;
}
mPriv->releaseAllProtected();

//changed added
if (mNetworkMetricsData)
{
delete mNetworkMetricsData;
mNetworkMetricsData = nullptr;
}

JSGlobalContextRelease(mContext);
JSContextGroupRelease(mContextGroup);
rtLogInfo("%s end", __FUNCTION__);
Expand Down Expand Up @@ -394,7 +402,7 @@ if (mModuleSettings.enablePlayer)
gAAMPJSBindings = new AAMPJSBindings();
loadAAMPJSBindingsLib();
}
if (gAAMPJSBindings->fnLoadJS)
if (gAAMPJSBindings && gAAMPJSBindings->fnLoadJS)
{
gAAMPJSBindings->fnLoadJS(mContext);
}
Expand Down
2 changes: 1 addition & 1 deletion src/jsc/JavaScriptEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ bool JavaScriptEngine::initialize()
if (garbageCollectInterval)
{
garbageCollectIntervalValue = atof(garbageCollectInterval);
NativeJSLogger::log(INFO, "garbage collection interval value: %d\n", garbageCollectIntervalValue);
NativeJSLogger::log(INFO, "garbage collection interval value: %f\n", garbageCollectIntervalValue);
}
mGarbageCollectionTag = installTimeout(garbageCollectIntervalValue, true,
[engine] () mutable {
Expand Down
Loading
Loading