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 16, 2026

Copy link

Choose a reason for hiding this comment

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

Missing space in error message. The error message concatenates name directly with "is" without a space separator, resulting in output like "valueis not a Uint32_t" instead of "value 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
78 changes: 63 additions & 15 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 16, 2026

Copy link

Choose a reason for hiding this comment

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

Attempting to access mContextMap[id].url when id is not found in the map causes undefined behavior. This line is in the else branch that executes when the id is not found (line 445), so accessing mContextMap[id].url will either access an invalid entry or create a new one. Remove the .c_str() call or avoid accessing mContextMap[id] altogether in this error path.

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;

Copilot AI Jan 16, 2026

Copy link

Choose a reason for hiding this comment

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

The variable id is declared inside the if block on line 507 but is used to initialize ApplicationRequest on line 509. Since uint32_t is uninitialized, this will result in using an indeterminate value. Initialize id before using it or ensure it's assigned a valid value.

Suggested change
uint32_t id;
uint32_t id = 0;

Copilot uses AI. Check for mistakes.
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);
Expand Down Expand Up @@ -620,18 +619,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 +660 to +681

Copilot AI Jan 16, 2026

Copy link

Choose a reason for hiding this comment

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

After checking if curl_easy_setopt fails and logging an error, the code calls curl_easy_cleanup but continues execution instead of returning immediately. This results in attempting to use an already-cleaned-up curl handle in subsequent curl_easy_setopt calls, which leads to undefined behavior. Add a return statement after curl_easy_cleanup to exit the function early on error.

Suggested change
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);
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
24 changes: 21 additions & 3 deletions src/jsc/JavaScriptUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ void dispatchPending()
{
std::unique_lock<std::mutex> lock(gDispatchMutex);
std::list<std::function<void ()>> pending = std::move(gPendingFun);
gDispatchMutex.unlock();
lock.unlock();
for(auto& fun : pending)
fun();
}
Expand Down Expand Up @@ -321,17 +321,32 @@ rtError rtReadBinaryBinding(int numArgs, const rtValue* args, rtValue* result, v
const char *fd = "hello.wasm";
struct stat buf;

stat(fd, &buf);
if (stat(fd, &buf) != 0)
{
rtLogError("Failed to stat file: %s", fd);
fclose(ptr);
return RT_ERROR;
}

int size = buf.st_size;

buffer = (char*)malloc(size);
fread(buffer,size,1,ptr); // read 10 bytes to our buffer
size_t bytesRead = fread(buffer, size, 1, ptr);
fclose(ptr);
Comment on lines +324 to 335

Copilot AI Jan 16, 2026

Copy link

Choose a reason for hiding this comment

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

The fopen call may fail and return nullptr, but there's no check before using ptr. Before calling stat, fread, or fclose with ptr, verify that fopen succeeded and ptr is not null to prevent null pointer dereferences.

Copilot uses AI. Check for mistakes.

if (bytesRead != 1)
{
rtLogError("Failed to read file: expected 1 item, read %zu items", bytesRead);
free(buffer);
return RT_ERROR;
}

if (result)
{
result->setString(buffer);
}

free(buffer);
return RT_OK;
}

Expand Down Expand Up @@ -693,13 +708,15 @@ rtError rtJSRuntimeDownloadMetrics(int numArgs, const rtValue* args, rtValue* re
rtValue keys;
if (map->Get("allKeys", &keys) != RT_OK) {
rtLogWarn("Could not retrieve url for network metrics data.");
delete netMetricsArray; //newly added
return RT_FAIL;
}
rtObjectRef objRef = keys.toObject();
rtArrayObject* keysArray = static_cast<rtArrayObject*>(objRef.getPtr());

if (!keysArray) {
rtLogWarn("No url found in the network metrics data.");
delete netMetricsArray; //newly added
return RT_FAIL;
}

Expand All @@ -715,6 +732,7 @@ rtError rtJSRuntimeDownloadMetrics(int numArgs, const rtValue* args, rtValue* re
NetworkMetrics* metrics = (NetworkMetrics*)storedValue.toVoidPtr();
if (!metrics) {
rtLogError("Failed to cast stored value to NetworkMetrics structure for url: %s.", key.cString());
delete netMetricsArray; //newly added
return RT_FAIL;
}
rtMapObject* metricsMap = new rtMapObject();
Expand Down
Loading
Loading