From 3dae8a5b62643d78acd1a4c452380d3efa0e02f0 Mon Sep 17 00:00:00 2001 From: Brett Langdon Date: Mon, 31 Dec 2012 15:04:52 -0500 Subject: [PATCH] support for calling js functions by name rather than through 'nodelua' --- src/luastate.cc | 262 ++++++++++++++++++++++++++++++++++++++++++++---- src/luastate.h | 17 +++- 2 files changed, 260 insertions(+), 19 deletions(-) diff --git a/src/luastate.cc b/src/luastate.cc index 98af77a..8a8e63d 100644 --- a/src/luastate.cc +++ b/src/luastate.cc @@ -4,24 +4,38 @@ using namespace v8; uv_async_t async; +std::map > functions; struct async_baton{ + bool has_cb; Persistent callback; - char* name; + char* data; bool error; char msg[1000]; LuaState* state; }; + void do_file(uv_work_t *req){ async_baton* baton = static_cast(req->data); - if(luaL_dofile(baton->state->lua_, baton->name)){ + if(luaL_dofile(baton->state->lua_, baton->data)){ + baton->error = true; + sprintf(baton->msg, "Exception In File %s Has Failed:\n%s\n", baton->data, lua_tostring(baton->state->lua_, -1)); + } +} + + +void do_string(uv_work_t *req){ + async_baton* baton = static_cast(req->data); + + if(luaL_dostring(baton->state->lua_, baton->data)){ baton->error = true; - sprintf(baton->msg, "Exception In File %s Has Failed:\n%s\n", baton->name, lua_tostring(baton->state->lua_, -1)); + sprintf(baton->msg, "Exception Of Lua Code Has Failed:\n%s\n", baton->data, lua_tostring(baton->state->lua_, -1)); } } + void async_after(uv_work_t *req){ HandleScope scope; @@ -44,7 +58,9 @@ void async_after(uv_work_t *req){ TryCatch try_catch; - baton->callback->Call(Context::GetCurrent()->Global(), argc, argv); + if(baton->has_cb){ + baton->callback->Call(Context::GetCurrent()->Global(), argc, argv); + } baton->callback.Dispose(); delete baton; @@ -55,9 +71,11 @@ void async_after(uv_work_t *req){ } } + LuaState::LuaState(){}; LuaState::~LuaState(){}; + void LuaState::Init(Handle target){ Local tpl = FunctionTemplate::New(New); tpl->SetClassName(String::NewSymbol("LuaState")); @@ -68,18 +86,64 @@ void LuaState::Init(Handle target){ tpl->PrototypeTemplate()->Set(String::NewSymbol("doFile"), FunctionTemplate::New(DoFile)->GetFunction()); + tpl->PrototypeTemplate()->Set(String::NewSymbol("doStringSync"), + FunctionTemplate::New(DoStringSync)->GetFunction()); + tpl->PrototypeTemplate()->Set(String::NewSymbol("doString"), + FunctionTemplate::New(DoString)->GetFunction()); + tpl->PrototypeTemplate()->Set(String::NewSymbol("setGlobal"), FunctionTemplate::New(SetGlobal)->GetFunction()); + tpl->PrototypeTemplate()->Set(String::NewSymbol("getGlobal"), + FunctionTemplate::New(GetGlobal)->GetFunction()); + + tpl->PrototypeTemplate()->Set(String::NewSymbol("status"), + FunctionTemplate::New(Status)->GetFunction()); + tpl->PrototypeTemplate()->Set(String::NewSymbol("close"), + FunctionTemplate::New(Close)->GetFunction()); + tpl->PrototypeTemplate()->Set(String::NewSymbol("collectGarbage"), + FunctionTemplate::New(CollectGarbage)->GetFunction()); + + tpl->PrototypeTemplate()->Set(String::NewSymbol("registerFunction"), + FunctionTemplate::New(RegisterFunction)->GetFunction()); Persistent constructor = Persistent::New(tpl->GetFunction()); target->Set(String::NewSymbol("LuaState"), constructor); } + +int LuaState::CallFunction(lua_State* L){ + int n = lua_gettop(L); + + char * func_name = (char *)lua_tostring(L, lua_upvalueindex(1)); + + const unsigned argc = n; + Local* argv = new Local[argc]; + int i; + for(i = 1; i <= n; ++i){ + argv[i - 1] = lua_to_value(L, i); + } + + Handle ret_val = Undefined(); + + std::map >::iterator iter; + for(iter = functions.begin(); iter != functions.end(); iter++){ + if(strcmp(iter->first, func_name) == 0){ + Persistent func = iter->second; + ret_val = func->Call(Context::GetCurrent()->Global(), argc, argv); + break; + } + } + + push_value_to_lua(L, ret_val); + return 1; +} + + Handle LuaState::New(const Arguments& args){ HandleScope scope; if(!args.IsConstructCall()) { - return ThrowException(Exception::TypeError(String::New("Use the new operator to create instances of this object."))); + return ThrowException(Exception::TypeError(String::New("LuaState Requires The 'new' Operator To Create An Instance"))); } LuaState* obj = new LuaState(); @@ -90,16 +154,17 @@ Handle LuaState::New(const Arguments& args){ return args.This(); } + Handle LuaState::DoFileSync(const Arguments& args){ HandleScope scope; if(args.Length() < 1){ - ThrowException(Exception::TypeError(String::New("doFileSync Takes Only 1 Argument"))); + ThrowException(Exception::TypeError(String::New("LuaState.doFileSync Takes Only 1 Argument"))); return scope.Close(Undefined()); } if(!args[0]->IsString()){ - ThrowException(Exception::TypeError(String::New("doFileSync Argument 1 Must Be A String"))); + ThrowException(Exception::TypeError(String::New("LuaState.doFileSync Argument 1 Must Be A String"))); return scope.Close(Undefined()); } @@ -120,49 +185,117 @@ Handle LuaState::DoFileSync(const Arguments& args){ } } + Handle LuaState::DoFile(const Arguments& args){ HandleScope scope; if(args.Length() < 1){ - ThrowException(Exception::TypeError(String::New("doFile Requires At Least 1 Argument"))); + ThrowException(Exception::TypeError(String::New("LuaState.doFile Requires At Least 1 Argument"))); return scope.Close(Undefined()); } if(!args[0]->IsString()){ - ThrowException(Exception::TypeError(String::New("doFile Argument 1 Must Be A String"))); + ThrowException(Exception::TypeError(String::New("LuaState.doFile First Argument Must Be A String"))); return scope.Close(Undefined()); } - if(args.Length() > 2 && !args[1]->IsFunction()){ - ThrowException(Exception::TypeError(String::New("doFile Argument 2 Must Be A Function"))); + if(args.Length() > 1 && !args[1]->IsFunction()){ + ThrowException(Exception::TypeError(String::New("LuaState.doFile Second Argument Must Be A Function"))); return scope.Close(Undefined()); } - char* file_name = get_str(args[0]); - Persistent callback; + LuaState* obj = ObjectWrap::Unwrap(args.This()); + async_baton* baton = new async_baton(); + baton->data = get_str(args[0]); + baton->state = obj; + obj->Ref(); + if(args.Length() > 1){ - callback = Persistent::New(Local::Cast(args[1])); + baton->has_cb = true; + baton->callback = Persistent::New(Local::Cast(args[1])); + } + + uv_work_t *req = new uv_work_t; + req->data = baton; + uv_queue_work(uv_default_loop(), req, do_file, async_after); + + return scope.Close(Undefined()); +} + + +Handle LuaState::DoStringSync(const Arguments& args) { + HandleScope scope; + + if(args.Length() < 1){ + ThrowException(Exception::TypeError(String::New("LuaState.doStringSync Requires 1 Argument"))); + return scope.Close(Undefined()); + } + + char *lua_code = get_str(args[0]); + + LuaState* obj = ObjectWrap::Unwrap(args.This()); + if(luaL_dostring(obj->lua_, lua_code)){ + char buf[1000]; + sprintf(buf, "Execution Of Lua Code Has Failed:\n%s\n", lua_tostring(obj->lua_, -1)); + ThrowException(Exception::TypeError(String::New(buf))); + return scope.Close(Undefined()); + } + + if(lua_gettop(obj->lua_)){ + return scope.Close(lua_to_value(obj->lua_, -1)); + } else{ + return scope.Close(Undefined()); + } +} + + +Handle LuaState::DoString(const Arguments& args){ + HandleScope scope; + + if(args.Length() < 1){ + ThrowException(Exception::TypeError(String::New("LuaState.doString Requires At Least 1 Argument"))); + return scope.Close(Undefined()); + } + + if(!args[0]->IsString()){ + ThrowException(Exception::TypeError(String::New("LuaState.doString: First Argument Must Be A String"))); + return scope.Close(Undefined()); } LuaState* obj = ObjectWrap::Unwrap(args.This()); async_baton* baton = new async_baton(); - baton->name = file_name; - baton->callback = callback; + baton->data = get_str(args[0]); baton->state = obj; obj->Ref(); + if(args.Length() > 1 && !args[1]->IsFunction()){ + ThrowException(Exception::TypeError(String::New("LuaState.doString Second Argument Must Be A Function"))); + return scope.Close(Undefined()); + } + + if(args.Length() > 1){ + baton->has_cb = true; + baton->callback = Persistent::New(Local::Cast(args[1])); + } + uv_work_t *req = new uv_work_t; req->data = baton; - uv_queue_work(uv_default_loop(), req, do_file, async_after); + uv_queue_work(uv_default_loop(), req, do_string, async_after); return scope.Close(Undefined()); } + Handle LuaState::SetGlobal(const Arguments& args) { HandleScope scope; if(args.Length() < 2){ - ThrowException(Exception::TypeError(String::New("Wrong number of arguments"))); + ThrowException(Exception::TypeError(String::New("LuaState.setGlobal Requires 2 Arguments"))); + return scope.Close(Undefined()); + } + + if(!args[0]->IsString()){ + ThrowException(Exception::TypeError(String::New("LuaState.setGlobal Argument 1 Must Be A String"))); return scope.Close(Undefined()); } @@ -175,3 +308,96 @@ Handle LuaState::SetGlobal(const Arguments& args) { return scope.Close(Undefined()); } + + +Handle LuaState::GetGlobal(const Arguments& args) { + HandleScope scope; + + if(args.Length() < 1){ + ThrowException(Exception::TypeError(String::New("LuaState.getGlobal Requires 1 Argument"))); + return scope.Close(Undefined()); + } + + if(!args[0]->IsString()){ + ThrowException(Exception::TypeError(String::New("LuaState.getGlobal Argument 1 Must Be A String"))); + return scope.Close(Undefined()); + } + + char *global_name = get_str(args[0]); + + LuaState* obj = ObjectWrap::Unwrap(args.This()); + lua_getglobal(obj->lua_, global_name); + + Local val = lua_to_value(obj->lua_, -1); + + return scope.Close(val); +} + +Handle LuaState::Close(const Arguments& args){ + HandleScope scope; + LuaState* obj = ObjectWrap::Unwrap(args.This()); + lua_close(obj->lua_); + return scope.Close(Undefined()); +} + + +Handle LuaState::Status(const Arguments& args){ + HandleScope scope; + LuaState* obj = ObjectWrap::Unwrap(args.This()); + int status = lua_status(obj->lua_); + + return scope.Close(Number::New(status)); +} + + +Handle LuaState::CollectGarbage(const Arguments& args){ + HandleScope scope; + + if(args.Length() < 1){ + ThrowException(Exception::TypeError(String::New("LuaState.collectGarbage Requires 1 Argument"))); + return scope.Close(Undefined()); + } + + if(!args[0]->IsNumber()){ + ThrowException(Exception::TypeError(String::New("LuaSatte.collectGarbage Argument 1 Must Be A Number, try nodelua.GC.[TYPE]"))); + return scope.Close(Undefined()); + } + + LuaState* obj = ObjectWrap::Unwrap(args.This()); + int type = (int)args[0]->ToNumber()->Value(); + int gc = lua_gc(obj->lua_, type, 0); + + return scope.Close(Number::New(gc)); +} + + +Handle LuaState::RegisterFunction(const Arguments& args){ + HandleScope scope; + + if(args.Length() < 1){ + ThrowException(Exception::TypeError(String::New("nodelua.registerFunction Must Have 2 Arguments"))); + return scope.Close(Undefined()); + } + + if(!args[0]->IsString()){ + ThrowException(Exception::TypeError(String::New("nodelua.registerFunction Argument 1 Must Be A String"))); + return scope.Close(Undefined()); + } + + if(!args[1]->IsFunction()){ + ThrowException(Exception::TypeError(String::New("nodelua.registerFunction Argument 2 Must Be A Function"))); + return scope.Close(Undefined()); + } + + Persistent func = Persistent::New(Local::Cast(args[1])); + char* func_name = get_str(args[0]); + functions[func_name] = func; + + LuaState* obj = ObjectWrap::Unwrap(args.This()); + + lua_pushstring(obj->lua_, func_name); + lua_pushcclosure(obj->lua_, CallFunction, 1); + lua_setglobal(obj->lua_, func_name); + + return scope.Close(Undefined()); +} diff --git a/src/luastate.h b/src/luastate.h index 61bd22e..95ba6c6 100644 --- a/src/luastate.h +++ b/src/luastate.h @@ -1,6 +1,7 @@ #ifndef LUASTATE_H #define LUASTATE_H +#include #include #include "utils.h" @@ -13,16 +14,30 @@ extern "C"{ class LuaState : public node::ObjectWrap{ public: - static void Init(v8::Handle target); lua_State* lua_; + static void Init(v8::Handle target); + static int CallFunction(lua_State* L); + private: LuaState(); ~LuaState(); static v8::Handle New(const v8::Arguments& args); + static v8::Handle Status(const v8::Arguments& args); + static v8::Handle Close(const v8::Arguments& args); + static v8::Handle CollectGarbage(const v8::Arguments& args); + static v8::Handle DoFileSync(const v8::Arguments& args); static v8::Handle DoFile(const v8::Arguments& args); + + static v8::Handle DoStringSync(const v8::Arguments& args); + static v8::Handle DoString(const v8::Arguments& args); + static v8::Handle SetGlobal(const v8::Arguments& args); + static v8::Handle GetGlobal(const v8::Arguments& args); + + static v8::Handle RegisterFunction(const v8::Arguments& args); + }; #endif