How do I unload/destroy a lua script? - c++

I'm trying to make a very simple lua wrapper that can be used to load & run multiple Lua scripts. I'm concerned because I don't see any documentation on how to properly destroy/delete loaded scripts without completely destroying the lua_State itself.
Is it possible to delete/unload loaded lua scripts? Is this unnecessary or will continuously calling luaL_dofile lead to a memory leak?
Simplified question.... If I call luaL_dofile on the same lua_State object, will this lead to a memory leak or issues or does lua handle this in the back end as it loads a new script?
Here's a demo...
lua_State* m_lua_state = luaL_newstate();
lua_gc(m_lua_state, LUA_GCSTOP, 0);
luaL_openlibs(m_lua_state);
lua_gc(m_lua_state, LUA_GCRESTART, 0);
for(int i = 0; i < 99999999; i++)
{
// Since I don't unload the previous file, does this cause a memory leak until lua_close is called?
if (luaL_dofile(m_lua_state, file_path.c_str()) != LUA_OK)
{
std::string error_msg = lua_tostring(m_lua_state, -1);
std::cout << "Error: " << error_msg << std::endl;
return false;
}
else
{
lua_getglobal(m_lua_state, function_name.c_str());
if (lua_isfunction(m_lua_state, -1))
{
int stack_size = lua_gettop(m_lua_state);
int number_of_args = 0;
if (lua_pcall(m_lua_state, number_of_args, 0, 0) != LUA_OK)
{
std::cout << "Error Calling Function In Script: " << file_path << "::" << function_name << " - " << lua_tostring(m_lua_state, -1) << std::endl;
}
int total_return_values = lua_gettop(m_lua_state) - stack_size;
}
else
{
std::cout << "Error Invalid Function In Script: " << file_path << "::" << function_name << std::endl;
}
}
}
lua_close(m_lua_state);

If by unload you mean undo the effects of running some Lua code, then it cannot be done unless you save the state of what is precious before running the code. This can be done by sandboxing the code.
If you just want to remove a Lua table, set to nil all references to it and let the table be garbage collected automatically or manually.

When you load a lua file you are really executing it's code. It's not like a dll or something in other languages:
int luaL_dofile (lua_State *L, const char *filename);
Loads and runs the given file. It is defined as the following macro:
If your file is something like this:
function sayHello()
print('Hello, world!')
end
Then loading the file will create a function 'sayHello' on the global object. If you load the file again it will replace the existing function with the new one and there should be no memory leak. Anyone calling 'sayHello' will call the new function on the global object, unless they saved a reference to the original function. Code commonly does this because it is slightly faster to call a local variable instead of a global function. If another file has local sayhi = sayHello at the top for instance then any calls to 'sayhi' will call the original function.
If you are creating classes and expect the data to remain, any objects (lua tables) created before the reload will still reference the metatables for the old classes. The code for your new classes will not automatically apply to classes created before the reload.
Take an example in the LUA docs:
Account = {}
function Account:deposit (v)
self.balance = self.balance + v
end
function Account:new (o)
o = o or {} -- create object if user does not provide one
setmetatable(o, self)
self.__index = self
return o
end
a = Account:new{balance = 0}
a:deposit(100.00)
a is now a table with a 'balance' property of 100.00 and a metatable pointing to the Account table which has a deposit function. If you make a change to the deposit function to print the balance and reload the file, it will replace the global 'Account' table and functions, but the table 'a' will still have the original Account table as it's metatable. So calling 'a.deposit' will still not print the value.

Related

V8 Cannot set ObjectTemplate with name "console"

I try to set a global object named console, but that specific name causes a crash and to me it seems like it's telling me that the object already exists. I can though set it to anything else. Why can I not set the object to name "console"?
V8 Version is 6.5.254.6
Error
# Fatal error in ../../src/objects.cc, line 6007
# Debug check failed: !it.IsFound().
Code Snippet
isolate->Enter();
v8::HandleScope handle_scope(isolate);
// Create globals
auto globalObj = v8::ObjectTemplate::New(isolate);
// Create console object
auto consoleObj = v8::ObjectTemplate::New(isolate);
// Log
auto logcb = [](V8CallbackArgs args) {
auto& log = Log::Instance();
for (size_t i = 1; i < args.Length(); i++)
log << *v8::String::Utf8Value(args[i]);
log << std::endl;
};
consoleObj->Set(v8::String::NewFromUtf8(isolate, "log"), v8::FunctionTemplate::New(isolate, logcb));
// Set global object
globalObj->Set(v8::String::NewFromUtf8(isolate, "console"), consoleObj); // nonono cannot have it console, con is ok though
// Create script context
context = v8::Persistent<v8::Context, v8::CopyablePersistentTraits<v8::Context>>(isolate, v8::Context::New(isolate, nullptr, globalObj));
{
v8::Context::Scope context_scope(context.Get(isolate));
v8::TryCatch tc(isolate);
auto source = v8::String::NewFromUtf8(src.c_str());
auto script = v8::Script::Compile(source);
if (script->Run().IsEmpty() && CV8ScriptRuntime::isDebug) {
v8loge "Error loading script \"" << filepath << "\n" << std::endl
<< "Exception: " << *v8::String::Utf8Value(tc.Exception()) << std::endl
<< "Stack trace: " << *v8::String::Utf8Value(tc.StackTrace()) << std::endl << Log::White;
}
}
Your observation is correct. When V8 initializes a Context, it installs a global console object into it, and assumes (guarded by a check) that a property with that name does not exist yet. When you provide your own "console", that check fails.
If you want to overwrite V8's built-in console, you'll have to create the Context first (via v8::Context::New), then delete (or overwrite) the console property on its global object.
If all you wanted was to have a console object in your embedder just like the one you know from browsers, then I'm happy to inform you that this work has already been done and you can simply use it :-)

Why Isn't find_one working in MongoDB C++?

I have a MongoDB 3.0.7 database, created with the mongo shell. The following works fine:
% mongo test
> vs = db.myCollection.findOne({"somefield.subfield": "somevalue"})
but when I do this in C++:
mongocxx::instance inst{};
mongocxx::client conn{};
auto db = conn["test"];
bsoncxx::stdx::optional< bsoncxx::document::value> docObj;
try {
docObj =
db["myCollection"]
.find_one(document{} <<
"somefield.subfield" << "someValue" <<
bsoncxx::builder::stream::finalize);
} catch (mongocxx::exception::operation e) {
std::cerr << "Retrieval failed (and exception thrown)";
}
if (docObj == bsoncxx::stdx::nullopt)
std::cerr << "Failed to find object";
I get "Failed to find object". What am I missing here?
Update: 11/23/2015, 10:00
I've installed the latest cxx driver (0.3.0), and made the following changes:
mongocxx::instance inst{};
mongocxx::client *connPtr;
bsoncxx::stdx::string_view connectionString("mongodb://localhost");
connPtr = new mongocxx::client(mongocxx::uri(connectionString));
auto db = connPtr->database("test");;
bsoncxx::stdx::optional< bsoncxx::document::value> docObj;
try {
docObj =
db["myCollection"]
.find_one(document{} <<
"somefield.subfield" << "someValue" <<
bsoncxx::builder::stream::finalize);
} catch (mongocxx::exception::operation e) {
std::cerr << "Retrieval failed (and exception thrown)";
}
if (docObj == bsoncxx::stdx::nullopt)
std::cerr << "Failed to find object";
I'm back to exactly the same thing. Calling db.list_collections(document{}) retrieves no results.
The bsoncxx library has two document types, views and values. A document::value contains the actual document data, and a document::view is just a reference to some underlying value. Values must outlive the views that use them.
There's a bug in the new c++11 driver with how document::values are passed around. This code produces a document::value :
document{} << "someField" << "someValue" << finalize;
The collection.find_one() method takes a document::view, and document::values convert implicitly to document::views. Unfortunately, this means if you dynamically build a document in your call to find_one(), as above, you can shoot yourself in the foot:
collection.find_one(document{} << "someField" << "someValue" << finalize);
finalize makes a temporary document::value, then find_one converts that to a document::view. The temporary value is dropped on the floor, leaving your view value-less, like a dangling pointer.
A workaround is to make your value in a separate call, and keep it around:
document::value doc = document{} << "someField" << "someValue" << finalize;
collection.find_one(doc.view());
I suspect this is what's causing your queries to fail; if not, it's something to make your code resilient to nonetheless!
You can track this ticket for the real fix for this problem.

Adobe Air - native process communication with exe fails

I'm struggling with Adobe Air native process communication. I'd like to pass an c++ exe shellcommands stored at processArgs when started. The c++ program uses this arguments to choose device channel and symbolrate of an CAN-Bus-Adapter. The c program itself creates an json database for an html page. While the c program is processing i'd like to get some feedback of the program and if it exits adobe air should create a link for the html page with the function onExit. The c program uses standart output of iostream lib ("cout", "cerr") to send messages to adobe air.
Adobe Air script:
var process;
function launchProcess()
{
if(air.NativeProcess.isSupported)
{
setupAndLaunch();
}
else
{
air.Introspector.Console.log("NativeProcess not supported.");
}
}
function setupAndLaunch()
{
var cpp_device = $( "#device option:selected" ).val();
var cpp_channel= $("#channel option:selected").text();
var cpp_symbolRate= $("#symbolRate option:selected").val();
air.Introspector.Console.log("CHANNEL",cpp_channel);
air.Introspector.Console.log("Device",cpp_device);
air.Introspector.Console.log("Symbol Rate",cpp_symbolRate);
var nativeProcessStartupInfo = new air.NativeProcessStartupInfo();
var file = air.File.applicationDirectory.resolvePath("InteractiveDocumentation.exe");
nativeProcessStartupInfo.executable = file;
var processArgs = new air.Vector["<String>"]();
processArgs.push(cpp_device);
processArgs.push(cpp_channel);
processArgs.push(cpp_symbolRate);
nativeProcessStartupInfo.arguments = processArgs;
process = new air.NativeProcess();
process.start(nativeProcessStartupInfo);
process.addEventListener(air.ProgressEvent.STANDARD_OUTPUT_DATA, onOutputData);
process.addEventListener(air.ProgressEvent.STANDARD_ERROR_DATA, onErrorData);
process.addEventListener(air.IOErrorEvent.STANDARD_OUTPUT_IO_ERROR, onIOError);
process.addEventListener(air.IOErrorEvent.STANDARD_ERROR_IO_ERROR, onIOError);
process.addEventListener(air.NativeProcessExitEvent.EXIT, onExit);
}
function onOutputData(event)
{
air.Introspector.Console.log("Got: ", process.standardOutput.readUTFBytes(process.standardOutput.bytesAvailable));
}
function onErrorData(event)
{
air.Introspector.Console.log("ERROR -", process.standardError.readUTFBytes(process.standardError.bytesAvailable));
}
function onExit(event)
{
air.Introspector.Console.log("Process exited with ",event.exitCode);
$("#output").html("Start");
}
function onIOError(event)
{
air.Introspector.Console.log(event.toString());
}
$(function()
{
$("#start").click(function()
{
air.Introspector.Console.log("START");
launchProcess();
});
});
the c program is quite long, and here i post only the main part
int main( int argc, char *argv[])
{
//! get shell commands for init
// echo shell commands
for( int count = 0; count < argc; count++ )
{
cout << " argv[" << count << "] "
<< argv[count];
}
// handle data
intDevice = (int)(argv[1][0] - '0');
intChannel = (int)(argv[2][0] - '0');
intChannel -= 1;
intSymbolRate = atoi(argv[3]);
//! class function calls
useDll CanFunction;
try
{
CanFunction.initProg();
CanFunction.ecuScan();
CanFunction.openSession();
CanFunction.readECU();
CanFunction.stopProg();
return 0;
}
//! exception handling
catch(int faultNumber)
{
....
}
}
First I used only the onExit listener and everything works fine. After I used start conditions adobe air only responses if i call no class functions except the CanFunction.initProg() and the onExit function was called but skipped the jQuery command to create the link. If I add the class function calls, the c program is called and the json database created but Adobe Air doesnt received any responses. The json database is still created. This confuses me.
My c program uses some *.dll files to communicate with the bus so i could imagine that it is a windows problem. But it is still weird.
Has anybody an idea why adobe air communication doesnt work with my c program if i call my class functions or why the jQuery command is skipped?
Or is there a better solution to call a c++ exe from adobe air?

MySQL call procedure with output parameter from C++ mysqlpp

I need help with the C++ mysqlpp driver calling a stored procedure and retrieving its output parameter. The queries seem to pass successfully but trying to get the stored value causes segmentation fault. My current pseudo code is:
mysqlpp::Connection* connection; // the connection type I am bound to use, no createStatement, prepareStatement methods
Query query = connection->query();
Query transactionQuery = connection->query();
query << "CALL sp_get_transactions_count(" << inputId << ", #transactionsCount);";
transactionQuery << "SELECT #transactionsCount as combinations;";
ClearQuerySentry cleanUpQuery(transactionQuery);
query.exec();
mysqlpp::StoreQueryResult transactionsResult = transactionQuery.store();
if (!transactionsResult || transactionsResult.num_rows() == 0)
{
logWarning(....);
}
else
{
const mysqlpp::Row& transactionRecord = result[0];
environment.pairTransactionsCount = verboseLexicalCast<int>(transactionRecord, "combinations"); // segfault on trying to cast static_cast<const char*>(row[fieldName.c_str()]))
}
I am not very experienced with mysqlpp and MySQL as a whole so it is possible my perception of the solution to be wrong. Thanks in advance.

How to filter out user defined globals in Lua from C++?

Consider this small Lua test script.
g1 = "Global 1"
g2 = "Global 2"
function test ()
local l1
print(g1,g2,l1)
end
test()
Assume you pause the execution at print(g1,g2,l1) and from C++ get all the global variables with this C code:
lua_pushglobaltable(L);
lua_pushnil(L);
while (lua_next(L,-2) != 0) {
const char* name = lua_tostring(L,-2);
// How do I tell a user defined
// global variable (now in name)
// from all other environment variables?
lua_pop(L,1);
}
lua_pop(L,1); // global table
When I get the name of a global entry, how can I tell if this is a global variable defined by the user in the script, like g1 and g2?
Since the user can freely write the script, I can't search for a specific global, I need to tell them apart somehow.
I see two ways. In the first, you record the names of all global variables before loading user scripts:
local S={}
_G["system variables"]=S
for k in pairs(_G) do S[k]=true end
Then in your C code, you traverse globals variables and filter only those whose name is in the table "system variables". Use lua_getglobal(L,"system variables") to get this table.
In the second way, you track the definition of global variables after the system ones have been loaded. You set this up by running this script before loading user scripts:
local U={}
_G["user variables"]=U
local function trace(t,k,v)
U[k]=true
rawset(t,k,v)
end
setmetatable(_G,{ __newindex = trace })
Then in your C code, you traverse globals variables and filter only those whose name is not in the table "user variables". Use lua_getglobal(L,"user variables") to get this table.
In both cases, do not convert keys in _G to strings: indexed the special tables directly with the original keys.
Note that you can call lua_getglobal(L,"system variables") or lua_getglobal(L,"user variables") just once, before the traversal, and index it repeatedly inside the loop.
My solution was to build a hash table of the global environment before I loaded the main script. When I need to get the user defined globals I only display globals not present in the hash table. In this way the script can run at full speed without keeping track of globals in in runtime.
Example of my solution (this is the short version of my implementation):
// The hash table storing global names
std::set<unsigned int> Blacklist;
// Create hash table "Blacklist"
void BlacklistSnapshot(lua_State *L) {
lua_pushglobaltable(L);
lua_pushnil(L);
while (lua_next(L,-2) != 0) { // pop NIL, push name,value
Blacklist.insert(HashName(lua_tostring(L,-2))); // insert to hash table
lua_pop(L,1); // remove value
}
lua_pop(L,1); // Remove global table
}
// Display user defined globals only
void PrintGlobals(lua_State *L) {
lua_pushglobaltable(L);
lua_pushnil(L);
while (lua_next(L,-2) != 0) { // pop NIL, push name,value
// Check if the global is present in our blacklist
if (Blacklist.find(HashName(lua_tostring(L,-2))) == Blacklist.end()) {
// Not present, print it...
PrintFormattedVariable(lua_type(L,-1),lua_tostring(L,-2));
}
lua_pop(L,1); // remove value
}
lua_pop(L,1); // remove global table
}
void RunScript(void) {
// Create new Lua state
L = luaL_newstate();
// Load all Lua libraries
luaL_openlibs(L);
// Create co-routine
CO = lua_newthread(L);
BlacklistSnapshot(CO);
// Load and compile script
AnsiString script(Frame->Script_Edit->Text);
if (luaL_loadbuffer(CO,script.c_str(),script.Length(),"Test") == LUA_OK) {
lua_resume(CO,NULL,0);
} else {
cs_error(CO, "Compiler error: "); // Print compiler error
}
}
The function HashName takes a string and returns the hash key for it as an unsigned int, use whatever Hash algorithm you like here...
When you need to display the globals, call PrintGlobals() (I do it from a hook routine)