├── .gitignore ├── README.md ├── binding.gyp ├── examples ├── config │ ├── config.lua │ └── index.js ├── constants │ └── index.js ├── files │ ├── index.js │ └── test.lua ├── functions │ └── index.js └── simple │ └── index.js ├── lib └── index.js ├── package.json └── src ├── luastate.cc ├── luastate.h ├── nodelua.cc ├── utils.cc └── utils.h /.gitignore: -------------------------------------------------------------------------------- 1 | build -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | NodeLua 2 | ====== 3 | **This project is no longer maintained.** 4 | 5 | NodeLua is a module to expose Lua bindings to Node.JS. 6 | 7 | This is still a work in progress, collaborators welcome. 8 | 9 | ## Install 10 | 11 | Requires Lua 5.1, will *not* work with 5.2 12 | 13 | Lua and it's C libraries are required for this module to work. 14 | 15 | ```bash 16 | npm install nodelua 17 | ``` 18 | 19 | ```javascript 20 | var nodelua = require('nodelua'); 21 | ``` 22 | 23 | ### Environment Variables 24 | These two environment variables should really only be used when there are installation problems 25 | 26 | * `NODELUA_INCLUDE` - additional directory to search for lua.h in. example: `NODELUA_INCLUDE=/opt/lua` 27 | * `NODELUA_FLAGS` - additional library flags to use. example: `NODELUA_FLAGS=-llua5.1` 28 | 29 | ## Installation Problems 30 | To try and narrow down where the error is coming from try running the following commands: 31 | ```bash 32 | $ find /usr/include /usr/local/include -name lua.h | sed s/lua.h// 33 | /usr/include/lua5.1/ 34 | $ pkg-config --libs-only-l --silence-errors lua || pkg-config --libs-only-l --silence-errors lua5.1 35 | -llua5.1 36 | ``` 37 | 38 | If instead they show nothing or an error then there are a few possible explanations: 39 | 40 | * Lua Libraries are not installed 41 | * This can be remedied with something like `[sudo] apt-get install liblua5.1-dev` 42 | * Lua Libraries are not in an expected location `/usr/include/` or `/usr/local/include` 43 | * This can be solved by setting install time environment variables `NODELUA_INCLUDE` and `NODELUA_FLAGS` 44 | * `NODELUA_INCLUDE="/path/where/lua.h/is/" NODELUA_FLAGS="-llua5.1" npm install nodelua` 45 | 46 | 47 | ## API 48 | ### NodeLua 49 | The `NodeLua` module itself contains the object `LuaState` as well as some constants. 50 | ```javascript 51 | var lua = new nodelua.LuaState('lua') 52 | ``` 53 | 54 | #### -- STATUS 55 | `STATUS` is an object that contains the constants for values returned by `LuaState.status()` or `LuaState.statusSync()`. 56 | 57 | `nodelua.STATUS` conatins the following constants: 58 | * `YIELD: 1` 59 | * `ERRRUN: 2` 60 | * `ERRSYNTAX: 3` 61 | * `ERRMEM: 4` 62 | * `ERRERR: 5` 63 | 64 | #### -- GC 65 | `GC` is an object of constants used for controlling the lua garbage collector. 66 | 67 | `nodelua.GC` conatins the following constants: 68 | * `STOP: 0` 69 | * `RESTART: 1` 70 | * `COLLECT: 2` 71 | * `COUNT: 3` 72 | * `COUNTB: 4` 73 | * `STEP: 5` 74 | * `SETPAUSE: 6` 75 | * `SETSTEPMUL: 7` 76 | 77 | #### -- INFO 78 | `INFO` is an object containing constants with information about the version of lua you are using. 79 | 80 | `nodelua.INFO` contains the following constants: 81 | * `VERSION` 82 | * `VERSION_NUM` 83 | * `COPYRIGHT` 84 | * `AUTHORS` 85 | 86 | ### LuaState 87 | The `LuaState` is an object wrapper around a `lua_State` instance. 88 | 89 | #### -- new LuaState(name) 90 | When creating a new `LuaState` you must provide it with a name, this is to help stop conflicts between registering functions. 91 | You should provide unique names to each `LuaState` instance. 92 | 93 | #### -- getName() 94 | Returns the name provided when creating creating the `LuaState` 95 | 96 | #### -- doFile(file_name, callback) 97 | The `doFile` method is used to load and execute lua code stored in `file_name`. 98 | ```javascript 99 | lua.doFile('test.lua', function(error, ret_value){ 100 | if(!error && ret_value){ 101 | console.dir(ret_value); 102 | } else{ 103 | console.error(error); 104 | } 105 | }); 106 | ``` 107 | 108 | #### -- doFileSync(file_name) 109 | This is the synchronous version of `doFile`, any value returned from the script is returned. 110 | ```javascript 111 | var ret_value = lua.doFileSync('test.lua'); 112 | console.dir(ret_value); 113 | ``` 114 | 115 | #### -- doString(lua_code, callback) 116 | The `doString` method is the same as `doFile` except the code is loaded from `lua_code` rather than from a file. 117 | ```javascript 118 | lua.doString("print('Hello, Lua')", function(error, ret_value){ 119 | if(!error && ret_value){ 120 | console.dir(ret_value); 121 | } else{ 122 | console.error(error); 123 | } 124 | }); 125 | ``` 126 | 127 | #### -- doStringSync(lua_code) 128 | This is the synchronous version of `doString`, any value returned from the script is returned. 129 | ```javascript 130 | var ret_value = lua.doString("return 5"); 131 | console.dir(ret_value); 132 | ``` 133 | 134 | #### -- setGlobal(name, value) 135 | The `setGlobal` method is used to provide lua with the global variable `name` containing the value `value`. 136 | ```javascript 137 | lua.setGlobal('test', 'value'); 138 | ``` 139 | 140 | #### -- getGlobal(name) 141 | The `getGlobal` method is used to retrieve either a value set by `setGlobal` or a global variable in any lua code that has been run. 142 | ```javascript 143 | console.log(lua.getGlobal('test')); 144 | ``` 145 | 146 | #### -- registerFunction(name, func) 147 | `registerFunction` is used to expose a javascript function to lua. 148 | ```javascript 149 | lua.registerFunction('add_them', function(a, b){ 150 | return a + b; 151 | }); 152 | var ret_value = lua.doStringSync('return add_them(2, 4)'); 153 | console.dir(ret_value); 154 | ``` 155 | 156 | #### -- status(callback) 157 | `status` will return the current status code for lua. The result can be `0` for normal or one of the error codes in `nodelua.STATUS`. 158 | ```javascript 159 | lua.status(function(code){ 160 | if(code == nodelua.STATUS.ERRSYNTAX){ 161 | console.error('Lua Syntax Error'); 162 | } 163 | }); 164 | ``` 165 | 166 | #### -- statusSync() 167 | This is the synchronous version of `status` 168 | ```javascript 169 | var code = lua.statusSync(); 170 | console.dir(code); 171 | ``` 172 | 173 | #### -- collectGarbage(GC_CODE, callback) 174 | `collectGarbage` is used to control the lua garbage collector. `GC_CODE` should be one of the codes taken from `nodelua.GC`. 175 | ```javascript 176 | lua.collectGarbage(nodelua.GC.COLLECT, function(code){ 177 | console.dir(code); 178 | }); 179 | ``` 180 | 181 | #### -- collectGarbageSync(GC_CODE) 182 | This is the synchronous version of `collectGarbage`. 183 | ```javascript 184 | var code = lua.collectGarbageSync(nodelua.GC.COLLECT); 185 | console.dir(code); 186 | ``` 187 | 188 | #### -- push(value) 189 | Push `value` onto the Lua stack. 190 | ```javascript 191 | lua.push(5); 192 | ``` 193 | 194 | #### -- pop(num) 195 | Pop `num` items from the stack. Default is 1. 196 | ```javascript 197 | lua.pop(5); 198 | ``` 199 | 200 | #### -- getTop() 201 | Return the number of elements on the Lua stack. 202 | ```javascript 203 | var num = lua.getTop(); 204 | ``` 205 | 206 | #### -- setTop(index) 207 | Set the top of the Lua stack to `index`. 208 | ```javascript 209 | lua.setTop(3); 210 | ``` 211 | 212 | #### -- replace(index) 213 | Replaces the top stack element into the specified `index` 214 | ```javascript 215 | lua.replace(3); 216 | ``` 217 | 218 | #### -- close() 219 | `close` should be used whenever you have finished using a `LuaState`. This will simply call `lua_close` on the `lua_State` for that object. 220 | 221 | ## Example 222 | See `./examples/`. 223 | ```javascript 224 | var nodelua = require('nodelua'); 225 | var lua = new nodelua.LuaState('example'); 226 | 227 | lua.registerFunction('add_them', function(a, b){ 228 | return a + b; 229 | }); 230 | 231 | lua.doFile('some_file.lua', function(error, ret_value){ 232 | console.dir(lua.getGlobal('some_var')); 233 | }); 234 | ``` 235 | 236 | ## License 237 | The MIT License (MIT) 238 | Copyright (c) 2012 Brett Langdon 239 | 240 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 241 | 242 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 243 | 244 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 245 | -------------------------------------------------------------------------------- /binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "nodelua", 5 | "variables": { 6 | "lua_include": " (http://www.brett.is)", 19 | "contributors": [ 20 | "York Gu (http://blog.yorkgu.me)" 21 | ], 22 | "license": "MIT", 23 | "gypfile": true, 24 | "engines": { 25 | "node": ">=0.10.0" 26 | }, 27 | "engineStrict": true 28 | } 29 | -------------------------------------------------------------------------------- /src/luastate.cc: -------------------------------------------------------------------------------- 1 | #define BUILDING_NODELUA 2 | #include "luastate.h" 3 | 4 | using namespace v8; 5 | 6 | uv_async_t async; 7 | std::map > functions; 8 | 9 | struct async_baton{ 10 | bool has_cb; 11 | Persistent callback; 12 | char* data; 13 | bool error; 14 | char msg[1000]; 15 | LuaState* state; 16 | }; 17 | 18 | struct simple_baton{ 19 | bool has_cb; 20 | Persistent callback; 21 | int data; 22 | int result; 23 | LuaState* state; 24 | }; 25 | 26 | 27 | void do_file(uv_work_t *req){ 28 | async_baton* baton = static_cast(req->data); 29 | 30 | if(luaL_dofile(baton->state->lua_, baton->data)){ 31 | baton->error = true; 32 | sprintf(baton->msg, "Exception In File %s Has Failed:\n%s\n", baton->data, lua_tostring(baton->state->lua_, -1)); 33 | } 34 | } 35 | 36 | 37 | void do_gc(uv_work_t *req){ 38 | simple_baton* baton = static_cast(req->data); 39 | 40 | baton->result = lua_gc(baton->state->lua_, baton->data, 0); 41 | } 42 | 43 | 44 | void do_status(uv_work_t *req){ 45 | simple_baton* baton = static_cast(req->data); 46 | 47 | baton->result = lua_status(baton->state->lua_); 48 | } 49 | 50 | 51 | void simple_after(uv_work_t *req, int status){ 52 | HandleScope scope; 53 | 54 | simple_baton* baton = static_cast(req->data); 55 | 56 | const int argc = 1; 57 | Local argv[] = { Number::New(baton->result) }; 58 | 59 | TryCatch try_catch; 60 | 61 | if(baton->has_cb){ 62 | baton->callback->Call(Context::GetCurrent()->Global(), argc, argv); 63 | } 64 | 65 | baton->callback.Dispose(); 66 | delete baton; 67 | delete req; 68 | 69 | if(try_catch.HasCaught()){ 70 | node::FatalException(try_catch); 71 | } 72 | } 73 | 74 | void do_string(uv_work_t *req){ 75 | async_baton* baton = static_cast(req->data); 76 | 77 | if(luaL_dostring(baton->state->lua_, baton->data)){ 78 | baton->error = true; 79 | sprintf(baton->msg, "Exception Of Lua Code Has Failed:\n%s\n", lua_tostring(baton->state->lua_, -1)); 80 | } 81 | } 82 | 83 | 84 | void async_after(uv_work_t *req, int status){ 85 | HandleScope scope; 86 | 87 | async_baton* baton = (async_baton *)req->data; 88 | 89 | Local argv[2]; 90 | const int argc = 2; 91 | 92 | if(baton->error){ 93 | argv[0] = String::NewSymbol(baton->msg); 94 | argv[1] = Local::New(Undefined()); 95 | } else{ 96 | argv[0] = Local::New(Undefined()); 97 | if(lua_gettop(baton->state->lua_)){ 98 | argv[1] = lua_to_value(baton->state->lua_, -1); 99 | } else{ 100 | argv[1] = Local::New(Undefined()); 101 | } 102 | } 103 | 104 | TryCatch try_catch; 105 | 106 | if(baton->has_cb){ 107 | baton->callback->Call(Context::GetCurrent()->Global(), argc, argv); 108 | } 109 | 110 | baton->callback.Dispose(); 111 | delete baton; 112 | delete req; 113 | 114 | if(try_catch.HasCaught()){ 115 | node::FatalException(try_catch); 116 | } 117 | } 118 | 119 | 120 | LuaState::LuaState(){}; 121 | LuaState::~LuaState(){}; 122 | 123 | 124 | void LuaState::Init(Handle target){ 125 | Local tpl = FunctionTemplate::New(New); 126 | tpl->SetClassName(String::NewSymbol("LuaState")); 127 | tpl->InstanceTemplate()->SetInternalFieldCount(2); 128 | 129 | tpl->PrototypeTemplate()->Set(String::NewSymbol("doFileSync"), 130 | FunctionTemplate::New(DoFileSync)->GetFunction()); 131 | tpl->PrototypeTemplate()->Set(String::NewSymbol("doFile"), 132 | FunctionTemplate::New(DoFile)->GetFunction()); 133 | 134 | tpl->PrototypeTemplate()->Set(String::NewSymbol("doStringSync"), 135 | FunctionTemplate::New(DoStringSync)->GetFunction()); 136 | tpl->PrototypeTemplate()->Set(String::NewSymbol("doString"), 137 | FunctionTemplate::New(DoString)->GetFunction()); 138 | 139 | tpl->PrototypeTemplate()->Set(String::NewSymbol("setGlobal"), 140 | FunctionTemplate::New(SetGlobal)->GetFunction()); 141 | tpl->PrototypeTemplate()->Set(String::NewSymbol("getGlobal"), 142 | FunctionTemplate::New(GetGlobal)->GetFunction()); 143 | 144 | tpl->PrototypeTemplate()->Set(String::NewSymbol("status"), 145 | FunctionTemplate::New(Status)->GetFunction()); 146 | tpl->PrototypeTemplate()->Set(String::NewSymbol("statusSync"), 147 | FunctionTemplate::New(StatusSync)->GetFunction()); 148 | 149 | tpl->PrototypeTemplate()->Set(String::NewSymbol("collectGarbage"), 150 | FunctionTemplate::New(CollectGarbage)->GetFunction()); 151 | tpl->PrototypeTemplate()->Set(String::NewSymbol("collectGarbageSync"), 152 | FunctionTemplate::New(CollectGarbageSync)->GetFunction()); 153 | 154 | tpl->PrototypeTemplate()->Set(String::NewSymbol("close"), 155 | FunctionTemplate::New(Close)->GetFunction()); 156 | tpl->PrototypeTemplate()->Set(String::NewSymbol("getName"), 157 | FunctionTemplate::New(GetName)->GetFunction()); 158 | 159 | tpl->PrototypeTemplate()->Set(String::NewSymbol("registerFunction"), 160 | FunctionTemplate::New(RegisterFunction)->GetFunction()); 161 | 162 | tpl->PrototypeTemplate()->Set(String::NewSymbol("push"), 163 | FunctionTemplate::New(Push)->GetFunction()); 164 | tpl->PrototypeTemplate()->Set(String::NewSymbol("pop"), 165 | FunctionTemplate::New(Pop)->GetFunction()); 166 | tpl->PrototypeTemplate()->Set(String::NewSymbol("getTop"), 167 | FunctionTemplate::New(GetTop)->GetFunction()); 168 | tpl->PrototypeTemplate()->Set(String::NewSymbol("setTop"), 169 | FunctionTemplate::New(SetTop)->GetFunction()); 170 | tpl->PrototypeTemplate()->Set(String::NewSymbol("replace"), 171 | FunctionTemplate::New(Replace)->GetFunction()); 172 | 173 | Persistent constructor = Persistent::New(tpl->GetFunction()); 174 | target->Set(String::NewSymbol("LuaState"), constructor); 175 | } 176 | 177 | 178 | int LuaState::CallFunction(lua_State* L){ 179 | int n = lua_gettop(L); 180 | 181 | char * func_name = (char *)lua_tostring(L, lua_upvalueindex(1)); 182 | 183 | const unsigned argc = n; 184 | Local* argv = new Local[argc]; 185 | int i; 186 | for(i = 1; i <= n; ++i){ 187 | argv[i - 1] = lua_to_value(L, i); 188 | } 189 | 190 | Handle ret_val = Undefined(); 191 | 192 | std::map >::iterator iter; 193 | for(iter = functions.begin(); iter != functions.end(); iter++){ 194 | if(strcmp(iter->first, func_name) == 0){ 195 | Persistent func = iter->second; 196 | ret_val = func->Call(Context::GetCurrent()->Global(), argc, argv); 197 | break; 198 | } 199 | } 200 | 201 | push_value_to_lua(L, ret_val); 202 | return 1; 203 | } 204 | 205 | 206 | Handle LuaState::New(const Arguments& args){ 207 | HandleScope scope; 208 | 209 | if(!args.IsConstructCall()) { 210 | return ThrowException(Exception::TypeError(String::New("LuaState Requires The 'new' Operator To Create An Instance"))); 211 | } 212 | 213 | if(!args.Length() > 0){ 214 | return ThrowException(Exception::TypeError(String::New("LuaState Requires 1 Argument"))); 215 | } 216 | 217 | if(!args[0]->IsString()){ 218 | return ThrowException(Exception::TypeError(String::New("LuaState First Argument Must Be A String"))); 219 | } 220 | 221 | LuaState* obj = new LuaState(); 222 | obj->name_ = get_str(args[0]); 223 | obj->lua_ = lua_open(); 224 | luaL_openlibs(obj->lua_); 225 | obj->Wrap(args.This()); 226 | 227 | return args.This(); 228 | } 229 | 230 | 231 | Handle LuaState::GetName(const Arguments& args){ 232 | HandleScope scope; 233 | 234 | LuaState* obj = ObjectWrap::Unwrap(args.This()); 235 | return scope.Close(String::New(obj->name_)); 236 | } 237 | 238 | 239 | Handle LuaState::DoFileSync(const Arguments& args){ 240 | HandleScope scope; 241 | 242 | if(args.Length() < 1){ 243 | ThrowException(Exception::TypeError(String::New("LuaState.doFileSync Takes Only 1 Argument"))); 244 | return scope.Close(Undefined()); 245 | } 246 | 247 | if(!args[0]->IsString()){ 248 | ThrowException(Exception::TypeError(String::New("LuaState.doFileSync Argument 1 Must Be A String"))); 249 | return scope.Close(Undefined()); 250 | } 251 | 252 | char* file_name = get_str(args[0]); 253 | 254 | LuaState* obj = ObjectWrap::Unwrap(args.This()); 255 | if(luaL_dofile(obj->lua_, file_name)){ 256 | char buf[1000]; 257 | sprintf(buf, "Exception Of File %s Has Failed:\n%s\n", file_name, lua_tostring(obj->lua_, -1)); 258 | ThrowException(Exception::Error(String::New(buf))); 259 | return scope.Close(Undefined()); 260 | } 261 | 262 | if(lua_gettop(obj->lua_)){ 263 | return scope.Close(lua_to_value(obj->lua_, -1)); 264 | } else{ 265 | return scope.Close(Undefined()); 266 | } 267 | } 268 | 269 | 270 | Handle LuaState::DoFile(const Arguments& args){ 271 | HandleScope scope; 272 | 273 | if(args.Length() < 1){ 274 | ThrowException(Exception::TypeError(String::New("LuaState.doFile Requires At Least 1 Argument"))); 275 | return scope.Close(Undefined()); 276 | } 277 | 278 | if(!args[0]->IsString()){ 279 | ThrowException(Exception::TypeError(String::New("LuaState.doFile First Argument Must Be A String"))); 280 | return scope.Close(Undefined()); 281 | } 282 | 283 | if(args.Length() > 1 && !args[1]->IsFunction()){ 284 | ThrowException(Exception::TypeError(String::New("LuaState.doFile Second Argument Must Be A Function"))); 285 | return scope.Close(Undefined()); 286 | } 287 | 288 | LuaState* obj = ObjectWrap::Unwrap(args.This()); 289 | async_baton* baton = new async_baton(); 290 | baton->data = get_str(args[0]); 291 | baton->state = obj; 292 | obj->Ref(); 293 | 294 | if(args.Length() > 1){ 295 | baton->has_cb = true; 296 | baton->callback = Persistent::New(Local::Cast(args[1])); 297 | } 298 | 299 | uv_work_t *req = new uv_work_t; 300 | req->data = baton; 301 | uv_queue_work(uv_default_loop(), req, do_file, async_after); 302 | 303 | return scope.Close(Undefined()); 304 | } 305 | 306 | 307 | Handle LuaState::DoStringSync(const Arguments& args) { 308 | HandleScope scope; 309 | 310 | if(args.Length() < 1){ 311 | ThrowException(Exception::TypeError(String::New("LuaState.doStringSync Requires 1 Argument"))); 312 | return scope.Close(Undefined()); 313 | } 314 | 315 | char *lua_code = get_str(args[0]); 316 | 317 | LuaState* obj = ObjectWrap::Unwrap(args.This()); 318 | if(luaL_dostring(obj->lua_, lua_code)){ 319 | char buf[1000]; 320 | sprintf(buf, "Execution Of Lua Code Has Failed:\n%s\n", lua_tostring(obj->lua_, -1)); 321 | ThrowException(Exception::Error(String::New(buf))); 322 | return scope.Close(Undefined()); 323 | } 324 | 325 | if(lua_gettop(obj->lua_)){ 326 | return scope.Close(lua_to_value(obj->lua_, -1)); 327 | } else{ 328 | return scope.Close(Undefined()); 329 | } 330 | } 331 | 332 | 333 | Handle LuaState::DoString(const Arguments& args){ 334 | HandleScope scope; 335 | 336 | if(args.Length() < 1){ 337 | ThrowException(Exception::TypeError(String::New("LuaState.doString Requires At Least 1 Argument"))); 338 | return scope.Close(Undefined()); 339 | } 340 | 341 | if(!args[0]->IsString()){ 342 | ThrowException(Exception::TypeError(String::New("LuaState.doString: First Argument Must Be A String"))); 343 | return scope.Close(Undefined()); 344 | } 345 | 346 | LuaState* obj = ObjectWrap::Unwrap(args.This()); 347 | async_baton* baton = new async_baton(); 348 | baton->data = get_str(args[0]); 349 | baton->state = obj; 350 | obj->Ref(); 351 | 352 | if(args.Length() > 1 && !args[1]->IsFunction()){ 353 | ThrowException(Exception::TypeError(String::New("LuaState.doString Second Argument Must Be A Function"))); 354 | return scope.Close(Undefined()); 355 | } 356 | 357 | if(args.Length() > 1){ 358 | baton->has_cb = true; 359 | baton->callback = Persistent::New(Local::Cast(args[1])); 360 | } 361 | 362 | uv_work_t *req = new uv_work_t; 363 | req->data = baton; 364 | uv_queue_work(uv_default_loop(), req, do_string, async_after); 365 | 366 | return scope.Close(Undefined()); 367 | } 368 | 369 | 370 | Handle LuaState::SetGlobal(const Arguments& args) { 371 | HandleScope scope; 372 | 373 | if(args.Length() < 2){ 374 | ThrowException(Exception::TypeError(String::New("LuaState.setGlobal Requires 2 Arguments"))); 375 | return scope.Close(Undefined()); 376 | } 377 | 378 | if(!args[0]->IsString()){ 379 | ThrowException(Exception::TypeError(String::New("LuaState.setGlobal Argument 1 Must Be A String"))); 380 | return scope.Close(Undefined()); 381 | } 382 | 383 | char *global_name = get_str(args[0]); 384 | 385 | LuaState* obj = ObjectWrap::Unwrap(args.This()); 386 | 387 | push_value_to_lua(obj->lua_, args[1]); 388 | lua_setglobal(obj->lua_, global_name); 389 | 390 | return scope.Close(Undefined()); 391 | } 392 | 393 | 394 | Handle LuaState::GetGlobal(const Arguments& args) { 395 | HandleScope scope; 396 | 397 | if(args.Length() < 1){ 398 | ThrowException(Exception::TypeError(String::New("LuaState.getGlobal Requires 1 Argument"))); 399 | return scope.Close(Undefined()); 400 | } 401 | 402 | if(!args[0]->IsString()){ 403 | ThrowException(Exception::TypeError(String::New("LuaState.getGlobal Argument 1 Must Be A String"))); 404 | return scope.Close(Undefined()); 405 | } 406 | 407 | char *global_name = get_str(args[0]); 408 | 409 | LuaState* obj = ObjectWrap::Unwrap(args.This()); 410 | lua_getglobal(obj->lua_, global_name); 411 | 412 | Local val = lua_to_value(obj->lua_, -1); 413 | 414 | return scope.Close(val); 415 | } 416 | 417 | Handle LuaState::Close(const Arguments& args){ 418 | HandleScope scope; 419 | LuaState* obj = ObjectWrap::Unwrap(args.This()); 420 | lua_close(obj->lua_); 421 | return scope.Close(Undefined()); 422 | } 423 | 424 | 425 | Handle LuaState::Status(const Arguments& args){ 426 | HandleScope scope; 427 | LuaState* obj = ObjectWrap::Unwrap(args.This()); 428 | simple_baton* baton = new simple_baton(); 429 | baton->state = obj; 430 | obj->Ref(); 431 | 432 | if(args.Length() > 0 && !args[0]->IsFunction()){ 433 | ThrowException(Exception::TypeError(String::New("LuaState.status First Argument Must Be A Function"))); 434 | return scope.Close(Undefined()); 435 | } 436 | 437 | if(args.Length() > 0){ 438 | baton->has_cb = true; 439 | baton->callback = Persistent::New(Local::Cast(args[0])); 440 | } 441 | 442 | uv_work_t *req = new uv_work_t; 443 | req->data = baton; 444 | uv_queue_work(uv_default_loop(), req, do_status, simple_after); 445 | 446 | return scope.Close(Undefined()); 447 | } 448 | 449 | 450 | Handle LuaState::StatusSync(const Arguments& args){ 451 | HandleScope scope; 452 | LuaState* obj = ObjectWrap::Unwrap(args.This()); 453 | int status = lua_status(obj->lua_); 454 | 455 | return scope.Close(Number::New(status)); 456 | } 457 | 458 | 459 | Handle LuaState::CollectGarbage(const Arguments& args){ 460 | HandleScope scope; 461 | 462 | if(args.Length() < 1){ 463 | ThrowException(Exception::TypeError(String::New("LuaState.collectGarbage Requires 1 Argument"))); 464 | return scope.Close(Undefined()); 465 | } 466 | 467 | if(!args[0]->IsNumber()){ 468 | ThrowException(Exception::TypeError(String::New("LuaSatte.collectGarbage Argument 1 Must Be A Number, try nodelua.GC.[TYPE]"))); 469 | return scope.Close(Undefined()); 470 | } 471 | 472 | LuaState* obj = ObjectWrap::Unwrap(args.This()); 473 | int type = (int)args[0]->ToNumber()->Value(); 474 | 475 | simple_baton* baton = new simple_baton(); 476 | baton->data = type; 477 | baton->state = obj; 478 | obj->Ref(); 479 | 480 | if(args.Length() > 1 && !args[1]->IsFunction()){ 481 | ThrowException(Exception::TypeError(String::New("LuaState.collectGarbage Second Argument Must Be A Function"))); 482 | return scope.Close(Undefined()); 483 | } 484 | 485 | if(args.Length() > 1){ 486 | baton->has_cb = true; 487 | baton->callback = Persistent::New(Local::Cast(args[1])); 488 | } 489 | 490 | uv_work_t *req = new uv_work_t; 491 | req->data = baton; 492 | uv_queue_work(uv_default_loop(), req, do_gc, simple_after); 493 | 494 | return scope.Close(Undefined()); 495 | } 496 | 497 | 498 | Handle LuaState::CollectGarbageSync(const Arguments& args){ 499 | HandleScope scope; 500 | 501 | if(args.Length() < 1){ 502 | ThrowException(Exception::TypeError(String::New("LuaState.collectGarbageSync Requires 1 Argument"))); 503 | return scope.Close(Undefined()); 504 | } 505 | 506 | if(!args[0]->IsNumber()){ 507 | ThrowException(Exception::TypeError(String::New("LuaSatte.collectGarbageSync Argument 1 Must Be A Number, try nodelua.GC.[TYPE]"))); 508 | return scope.Close(Undefined()); 509 | } 510 | 511 | LuaState* obj = ObjectWrap::Unwrap(args.This()); 512 | int type = (int)args[0]->ToNumber()->Value(); 513 | int gc = lua_gc(obj->lua_, type, 0); 514 | 515 | return scope.Close(Number::New(gc)); 516 | } 517 | 518 | 519 | Handle LuaState::RegisterFunction(const Arguments& args){ 520 | HandleScope scope; 521 | 522 | if(args.Length() < 1){ 523 | ThrowException(Exception::TypeError(String::New("nodelua.registerFunction Must Have 2 Arguments"))); 524 | return scope.Close(Undefined()); 525 | } 526 | 527 | if(!args[0]->IsString()){ 528 | ThrowException(Exception::TypeError(String::New("nodelua.registerFunction Argument 1 Must Be A String"))); 529 | return scope.Close(Undefined()); 530 | } 531 | 532 | if(!args[1]->IsFunction()){ 533 | ThrowException(Exception::TypeError(String::New("nodelua.registerFunction Argument 2 Must Be A Function"))); 534 | return scope.Close(Undefined()); 535 | } 536 | 537 | LuaState* obj = ObjectWrap::Unwrap(args.This()); 538 | 539 | Persistent func = Persistent::New(Local::Cast(args[1])); 540 | char* func_name = get_str(args[0]); 541 | Local func_key = String::Concat(String::New(func_name), String::New(":")); 542 | func_key = String::Concat(func_key, String::New(obj->name_)); 543 | functions[get_str(func_key)] = func; 544 | 545 | lua_pushstring(obj->lua_, get_str(func_key)); 546 | lua_pushcclosure(obj->lua_, CallFunction, 1); 547 | lua_setglobal(obj->lua_, func_name); 548 | 549 | return scope.Close(Undefined()); 550 | } 551 | 552 | 553 | Handle LuaState::Push(const Arguments& args) { 554 | HandleScope scope; 555 | 556 | if(args.Length() < 1){ 557 | ThrowException(Exception::TypeError(String::New("LuaState.push Requires 1 Argument"))); 558 | return scope.Close(Undefined()); 559 | } 560 | 561 | LuaState* obj = ObjectWrap::Unwrap(args.This()); 562 | 563 | push_value_to_lua(obj->lua_, args[0]); 564 | 565 | return scope.Close(Undefined()); 566 | } 567 | 568 | 569 | Handle LuaState::Pop(const Arguments& args) { 570 | HandleScope scope; 571 | 572 | int pop_n = 1; 573 | if(args.Length() > 0 && args[0]->IsNumber()){ 574 | pop_n = (int)args[0]->ToNumber()->Value(); 575 | } 576 | 577 | LuaState* obj = ObjectWrap::Unwrap(args.This()); 578 | lua_pop(obj->lua_, pop_n); 579 | 580 | return scope.Close(Undefined()); 581 | } 582 | 583 | 584 | Handle LuaState::GetTop(const Arguments& args) { 585 | HandleScope scope; 586 | 587 | LuaState* obj = ObjectWrap::Unwrap(args.This()); 588 | int n = lua_gettop(obj->lua_); 589 | 590 | return scope.Close(Number::New(n)); 591 | } 592 | 593 | 594 | Handle LuaState::SetTop(const Arguments& args) { 595 | HandleScope scope; 596 | 597 | int set_n = 0; 598 | if(args.Length() > 0 && args[0]->IsNumber()){ 599 | set_n = (int)args[0]->ToNumber()->Value(); 600 | } 601 | 602 | LuaState* obj = ObjectWrap::Unwrap(args.This()); 603 | lua_settop(obj->lua_, set_n); 604 | 605 | return scope.Close(Undefined()); 606 | } 607 | 608 | 609 | Handle LuaState::Replace(const Arguments& args) { 610 | HandleScope scope; 611 | 612 | if(args.Length() < 1){ 613 | ThrowException(Exception::TypeError(String::New("LuaState.replace Requires 1 Argument"))); 614 | return scope.Close(Undefined()); 615 | } 616 | 617 | if(!args[0]->IsNumber()){ 618 | ThrowException(Exception::TypeError(String::New("LuaState.replace Argument 1 Must Be A Number"))); 619 | return scope.Close(Undefined()); 620 | } 621 | 622 | int index = (int)args[0]->ToNumber()->Value(); 623 | 624 | LuaState* obj = ObjectWrap::Unwrap(args.This()); 625 | lua_replace(obj->lua_, index); 626 | 627 | return scope.Close(Undefined()); 628 | } 629 | -------------------------------------------------------------------------------- /src/luastate.h: -------------------------------------------------------------------------------- 1 | #ifndef LUASTATE_H 2 | #define LUASTATE_H 3 | 4 | #include 5 | #include 6 | 7 | #include "utils.h" 8 | 9 | extern "C"{ 10 | #include 11 | #include 12 | #include 13 | } 14 | 15 | class LuaState : public node::ObjectWrap{ 16 | public: 17 | lua_State* lua_; 18 | char* name_; 19 | 20 | static void Init(v8::Handle target); 21 | static int CallFunction(lua_State* L); 22 | 23 | private: 24 | LuaState(); 25 | ~LuaState(); 26 | 27 | static v8::Handle New(const v8::Arguments& args); 28 | static v8::Handle Close(const v8::Arguments& args); 29 | static v8::Handle GetName(const v8::Arguments& args); 30 | 31 | static v8::Handle CollectGarbage(const v8::Arguments& args); 32 | static v8::Handle CollectGarbageSync(const v8::Arguments& args); 33 | 34 | static v8::Handle Status(const v8::Arguments& args); 35 | static v8::Handle StatusSync(const v8::Arguments& args); 36 | 37 | 38 | static v8::Handle DoFileSync(const v8::Arguments& args); 39 | static v8::Handle DoFile(const v8::Arguments& args); 40 | 41 | static v8::Handle DoStringSync(const v8::Arguments& args); 42 | static v8::Handle DoString(const v8::Arguments& args); 43 | 44 | static v8::Handle SetGlobal(const v8::Arguments& args); 45 | static v8::Handle GetGlobal(const v8::Arguments& args); 46 | 47 | static v8::Handle RegisterFunction(const v8::Arguments& args); 48 | 49 | static v8::Handle Push(const v8::Arguments& args); 50 | static v8::Handle Pop(const v8::Arguments& args); 51 | static v8::Handle GetTop(const v8::Arguments& args); 52 | static v8::Handle SetTop(const v8::Arguments& args); 53 | static v8::Handle Replace(const v8::Arguments& args); 54 | }; 55 | #endif 56 | -------------------------------------------------------------------------------- /src/nodelua.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "luastate.h" 5 | 6 | extern "C"{ 7 | #include 8 | } 9 | 10 | using namespace v8; 11 | 12 | void init_info_constants(Handle target){ 13 | Local constants = Object::New(); 14 | constants->Set(String::NewSymbol("VERSION"), String::New(LUA_VERSION)); 15 | constants->Set(String::NewSymbol("VERSION_NUM"), Number::New(LUA_VERSION_NUM)); 16 | constants->Set(String::NewSymbol("COPYRIGHT"), String::New(LUA_COPYRIGHT)); 17 | constants->Set(String::NewSymbol("AUTHORS"), String::New(LUA_AUTHORS)); 18 | target->Set(String::NewSymbol("INFO"), constants); 19 | } 20 | 21 | 22 | void init_status_constants(Handle target){ 23 | Local constants = Object::New(); 24 | constants->Set(String::NewSymbol("YIELD"), Number::New(LUA_YIELD)); 25 | constants->Set(String::NewSymbol("ERRRUN"), Number::New(LUA_ERRRUN)); 26 | constants->Set(String::NewSymbol("ERRSYNTAX"), Number::New(LUA_ERRSYNTAX)); 27 | constants->Set(String::NewSymbol("ERRMEM"), Number::New(LUA_ERRMEM)); 28 | constants->Set(String::NewSymbol("ERRERR"), Number::New(LUA_ERRERR)); 29 | target->Set(String::NewSymbol("STATUS"), constants); 30 | } 31 | 32 | 33 | void init_gc_constants(Handle target){ 34 | Local constants = Object::New(); 35 | constants->Set(String::NewSymbol("STOP"), Number::New(LUA_GCSTOP)); 36 | constants->Set(String::NewSymbol("RESTART"), Number::New(LUA_GCRESTART)); 37 | constants->Set(String::NewSymbol("COLLECT"), Number::New(LUA_GCCOLLECT)); 38 | constants->Set(String::NewSymbol("COUNT"), Number::New(LUA_GCCOUNT)); 39 | constants->Set(String::NewSymbol("COUNTB"), Number::New(LUA_GCCOUNTB)); 40 | constants->Set(String::NewSymbol("STEP"), Number::New(LUA_GCSTEP)); 41 | constants->Set(String::NewSymbol("SETPAUSE"), Number::New(LUA_GCSETPAUSE)); 42 | constants->Set(String::NewSymbol("SETSTEPMUL"), Number::New(LUA_GCSETSTEPMUL)); 43 | target->Set(String::NewSymbol("GC"), constants); 44 | } 45 | 46 | 47 | void init(Handle target) { 48 | LuaState::Init(target); 49 | init_gc_constants(target); 50 | init_status_constants(target); 51 | init_info_constants(target); 52 | } 53 | NODE_MODULE(nodelua, init) 54 | -------------------------------------------------------------------------------- /src/utils.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "utils.h" 3 | 4 | char * get_str(v8::Local val){ 5 | if(!val->IsString()){ 6 | v8::ThrowException(v8::Exception::TypeError(v8::String::New("Argument Must Be A String"))); 7 | return NULL; 8 | } 9 | 10 | v8::String::Utf8Value val_string(val); 11 | char * val_char_ptr = (char *) malloc(val_string.length() + 1); 12 | strcpy(val_char_ptr, *val_string); 13 | return val_char_ptr; 14 | } 15 | 16 | 17 | v8::Local lua_to_value(lua_State* L, int i){ 18 | switch(lua_type(L, i)){ 19 | case LUA_TBOOLEAN: 20 | return v8::Local::New(v8::Boolean::New((int)lua_toboolean(L, i))); 21 | break; 22 | case LUA_TNUMBER: 23 | return v8::Local::New(v8::Number::New(lua_tonumber(L, i))); 24 | break; 25 | case LUA_TSTRING: 26 | return v8::String::New((char *)lua_tostring(L, i)); 27 | break; 28 | case LUA_TTABLE: 29 | { 30 | v8::Local obj = v8::Object::New(); 31 | lua_pushnil(L); 32 | while(lua_next(L, -2) != 0){ 33 | v8::Local key = lua_to_value(L, -2); 34 | v8::Local value = lua_to_value(L, -1); 35 | obj->Set(key, value); 36 | lua_pop(L, 1); 37 | } 38 | return obj; 39 | break; 40 | } 41 | default: 42 | return v8::Local::New(v8::Undefined()); 43 | break; 44 | } 45 | } 46 | 47 | void push_value_to_lua(lua_State* L, v8::Handle value){ 48 | if(value->IsString()){ 49 | lua_pushstring(L, get_str(v8::Local::New(value))); 50 | }else if(value->IsNumber()){ 51 | int i_value = value->ToNumber()->Value(); 52 | lua_pushinteger(L, i_value); 53 | }else if(value->IsBoolean()){ 54 | int b_value = (int)value->ToBoolean()->Value(); 55 | lua_pushboolean(L, b_value); 56 | }else if(value->IsObject()){ 57 | lua_newtable(L); 58 | v8::Local obj = value->ToObject(); 59 | v8::Local keys = obj->GetPropertyNames(); 60 | for(uint32_t i = 0; i < keys->Length(); ++i){ 61 | v8::Local key = keys->Get(i); 62 | v8::Local val = obj->Get(key); 63 | push_value_to_lua(L, key); 64 | push_value_to_lua(L, val); 65 | lua_settable(L, -3); 66 | } 67 | }else{ 68 | lua_pushnil(L); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef LUAUTILS_H 2 | #define LUAUTILS_H 3 | 4 | #include 5 | #include 6 | 7 | extern "C"{ 8 | #include 9 | #include 10 | #include 11 | } 12 | 13 | char * get_str(v8::Local val); 14 | v8::Local lua_to_value(lua_State* L, int); 15 | void push_value_to_lua(lua_State* L, v8::Handle value); 16 | #endif 17 | --------------------------------------------------------------------------------