Created
August 23, 2014 21:18
-
-
Save MDFL64/9c74c257b786fa58e0e2 to your computer and use it in GitHub Desktop.
gm_v8
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <map> | |
#include "GarrysMod/Lua/Interface.h" | |
#include "Color.h" | |
#include "tier0/dbg.h" | |
#include "v8.h" | |
/// Lua Loadstring function -- Credit http://facepunch.com/showthread.php?t=1386454 | |
#include <windows.h> //TODO make this work on other platforms | |
HMODULE module_lua = GetModuleHandle("lua_shared.dll"); | |
typedef int(*f_loadstring)(lua_State* state, const char* code); | |
f_loadstring luaL_loadstring = (f_loadstring)GetProcAddress(module_lua,"luaL_loadstring"); | |
/// | |
using namespace std; | |
using namespace GarrysMod; | |
using namespace v8; | |
//Credit to gm_io, I am a dumb pleb and have no idea what reinterpret_cast does. | |
#define LOADINTERFACE(_module_, _version_, _out_) Sys_LoadInterface(_module_, _version_, NULL, reinterpret_cast<void**>(& _out_ )) | |
#define V8SCOPE Isolate::Scope isolate_scope(v8engine);HandleScope handle_scope(v8engine) | |
#define V8SCOPE_ESC Isolate::Scope isolate_scope(v8engine);EscapableHandleScope handle_scope(v8engine) | |
#define ESC handle_scope.Escape | |
#define COLOR_V8 Color(255,200,180,255) | |
#define COLOR_LOG Color(200,200,200,255) | |
#define COLOR_ERR Color(255,100,100,255) | |
#undef LUA | |
#define LUA glua_state->luabase | |
Isolate* v8engine; | |
Eternal<ObjectTemplate> template_libs; | |
Eternal<ObjectTemplate> template_luaProxy; | |
lua_State* glua_state; | |
map<int,UniquePersistent<Object>> lua_proxies; | |
int proxyTableRef; | |
//Removes a table from the top of the lua stack. Converts it to a js proxy. | |
Local<Object> luaTableOrFunction2proxy() { | |
V8SCOPE_ESC; | |
LUA->ReferencePush(proxyTableRef); | |
LUA->Push(-2); | |
LUA->GetTable(-2); | |
int id; | |
if (LUA->IsType(-1,Lua::Type::NUMBER)) { | |
id = LUA->GetNumber(); | |
} else { | |
LUA->Pop(); | |
LUA->Push(-2); | |
id = LUA->ReferenceCreate(); | |
LUA->Push(-2); | |
LUA->PushNumber(id); | |
LUA->SetTable(-3); | |
} | |
LUA->Pop(2); | |
//Done with lua stack fuckery. Yay! | |
Local<Object> proxyHandle; | |
if (lua_proxies.count(id)) { | |
//ConColorMsg(COLOR_V8,"[V8] Reused proxy!\n"); | |
proxyHandle = Local<Object>::New(v8engine,lua_proxies[id]); | |
} else { | |
//ConColorMsg(COLOR_V8,"[V8] New proxy!\n"); | |
proxyHandle = template_luaProxy.Get(v8engine)->NewInstance(); | |
proxyHandle->SetInternalField(0,Number::New(v8engine,id)); | |
lua_proxies[id].Reset(v8engine,proxyHandle); | |
//lua_proxies[id].SetWeak TO DO GC | |
} | |
return ESC(proxyHandle); | |
} | |
Local<Value> luaVal2js() { | |
V8SCOPE_ESC; | |
switch (LUA->GetType(-1)) { | |
//Basic Types | |
case Lua::Type::NUMBER: | |
return ESC(Number::New(v8engine,LUA->GetNumber())); | |
case Lua::Type::STRING: | |
return ESC(String::NewFromUtf8(v8engine,LUA->GetString())); | |
//Primitive Types | |
case Lua::Type::BOOL: | |
return Boolean::New(v8engine,LUA->GetBool()); | |
case Lua::Type::NIL: | |
LUA->Pop(); | |
return Null(v8engine); | |
//Proxy Types | |
case Lua::Type::TABLE: | |
case Lua::Type::FUNCTION: | |
return ESC(luaTableOrFunction2proxy()); | |
//Non-Convertable Types | |
default: | |
LUA->Pop(); | |
return ESC(String::NewFromUtf8(v8engine,"!LUA TO JS CONVERSION FAILED!")); | |
} | |
} | |
int luaf_v8_run(lua_State* state) { | |
LUA->CheckType(1, Lua::Type::STRING); | |
V8SCOPE; | |
const char* source_raw = LUA->GetString(1); | |
Local<Context> context = Context::New(v8engine,NULL,template_libs.Get(v8engine)); | |
Context::Scope context_scope(context); | |
Local<Object> lualib = context->Global()->Get(String::NewFromUtf8(v8engine,"glua"))->ToObject(); | |
//Global proxy | |
LUA->PushSpecial(Lua::SPECIAL_GLOB); | |
lualib->Set(String::NewFromUtf8(v8engine,"G"),luaTableOrFunction2proxy()); | |
//Input | |
if (LUA->IsType(2,Lua::Type::STRING)) | |
lualib->Set(String::NewFromUtf8(v8engine,"input"),String::NewFromUtf8(v8engine,LUA->GetString(2))); | |
// Create a string containing the JavaScript source code, then compile it. | |
Local<String> source = String::NewFromUtf8(v8engine,source_raw); | |
TryCatch trycatch; | |
Local<Script> script = Script::Compile(source); | |
if (script.IsEmpty()) { | |
String::Utf8Value trace_str(trycatch.StackTrace()); | |
ConColorMsg(COLOR_V8,"[V8]"); | |
ConColorMsg(COLOR_ERR,"[COMPILE ERROR] "); | |
ConColorMsg(COLOR_ERR,*trace_str); | |
ConColorMsg(COLOR_ERR,"\n"); | |
} else { | |
// Run the script to get the result. | |
Local<Value> result = script->Run(); | |
if (result.IsEmpty()) { | |
String::Utf8Value trace_str(trycatch.StackTrace()); | |
ConColorMsg(COLOR_V8,"[V8]"); | |
ConColorMsg(COLOR_ERR,"[ERROR] "); | |
ConColorMsg(COLOR_ERR,*trace_str); | |
ConColorMsg(COLOR_ERR,"\n"); | |
} else { | |
Local<Value> exports = lualib->Get(String::NewFromUtf8(v8engine,"output")); | |
String::Utf8Value ex_str(exports); | |
LUA->PushString(*ex_str); | |
return 1; | |
} | |
} | |
return 0; | |
} | |
void jsf_glua_run(const FunctionCallbackInfo<Value>& call_info) { | |
V8SCOPE; | |
if (call_info[0]->IsString()) { | |
String::Utf8Value code_str(call_info[0]); | |
luaL_loadstring(glua_state,*code_str); | |
} else | |
return; | |
if (LUA->IsType(-1, Lua::Type::FUNCTION)) | |
LUA->Call(0,1); | |
else | |
return; | |
if (LUA->IsType(-1, Lua::Type::STRING)) | |
call_info.GetReturnValue().Set(String::NewFromUtf8(v8engine,LUA->GetString(-1))); | |
} | |
void jsf_console_log(const FunctionCallbackInfo<Value>& call_info) { | |
V8SCOPE; | |
String::Utf8Value print_str(call_info[0]); | |
ConColorMsg(COLOR_V8,"[V8]"); | |
ConColorMsg(COLOR_LOG,"[LOG] "); | |
ConColorMsg(COLOR_LOG,*print_str); | |
ConColorMsg(COLOR_LOG,"\n"); | |
} | |
void jsf_proxy_toString(const FunctionCallbackInfo<Value>& call_info) { | |
V8SCOPE; | |
int id = call_info.This()->GetInternalField(0)->Uint32Value(); | |
LUA->ReferencePush(id); | |
if (LUA->IsType(-1,Lua::Type::TABLE)) { | |
call_info.GetReturnValue().Set(String::NewFromUtf8(v8engine,"[LUA TABLE PROXY]")); | |
} else if (LUA->IsType(-1,Lua::Type::FUNCTION)) { | |
call_info.GetReturnValue().Set(String::NewFromUtf8(v8engine,"[LUA FUNCTION PROXY]")); | |
} | |
LUA->Pop(); | |
} | |
void jsi_proxy_gets(Local<String> key,const PropertyCallbackInfo<Value>& info) { | |
V8SCOPE; | |
int id = info.This()->GetInternalField(0)->Uint32Value(); | |
String::Utf8Value raw_key(key); | |
LUA->ReferencePush(id); | |
if (LUA->IsType(-1,Lua::Type::TABLE)) { | |
LUA->PushString(*raw_key); | |
LUA->GetTable(-2); | |
//Convert to js value | |
Local<Value> val = luaVal2js(); | |
if (!val->IsNull()) | |
info.GetReturnValue().Set(val); | |
} | |
LUA->Pop(); | |
} | |
/* | |
void js_proxy_set_s(Local<String> key,Local<Value> value,const PropertyCallbackInfo<Value>& info) { | |
V8SCOPE; | |
} | |
*/ | |
void jsi_proxy_getn(uint32_t key,const PropertyCallbackInfo<Value>& info) { | |
V8SCOPE; | |
int id = info.This()->GetInternalField(0)->Uint32Value(); | |
LUA->ReferencePush(id); | |
if (LUA->IsType(-1,Lua::Type::TABLE)) { | |
LUA->PushNumber(key); | |
LUA->GetTable(-2); | |
//Convert to js value | |
Local<Value> val = luaVal2js(); | |
if (!val->IsNull()) | |
info.GetReturnValue().Set(val); | |
} | |
LUA->Pop(); | |
} | |
/* | |
void js_proxy_set_n(uint32_t key,Local<Value> value,const PropertyCallbackInfo<Value>& info) { | |
V8SCOPE; | |
}*/ | |
GMOD_MODULE_OPEN() | |
{ | |
v8engine= Isolate::New(); | |
V8SCOPE; | |
Local<ObjectTemplate> libs = ObjectTemplate::New(); | |
template_libs.Set(v8engine,libs); | |
Local<ObjectTemplate> libGLua = ObjectTemplate::New(); | |
libs->Set(v8engine,"glua",libGLua); | |
libGLua->Set(v8engine,"run",FunctionTemplate::New(v8engine,&jsf_glua_run)); | |
Local<ObjectTemplate> libConsole = ObjectTemplate::New(); | |
libs->Set(v8engine,"console",libConsole); | |
libConsole->Set(v8engine,"log",FunctionTemplate::New(v8engine,&jsf_console_log)); | |
//Lua proxy crap | |
Local<ObjectTemplate> luaProxy = ObjectTemplate::New(); | |
template_luaProxy.Set(v8engine,luaProxy); | |
luaProxy->SetInternalFieldCount(1); | |
luaProxy->SetNamedPropertyHandler(&jsi_proxy_gets); | |
luaProxy->SetIndexedPropertyHandler(&jsi_proxy_getn); | |
luaProxy->Set(v8engine,"toString",FunctionTemplate::New(v8engine,&jsf_proxy_toString)); | |
//Lua setup | |
glua_state= state; | |
LUA->CreateTable(); | |
proxyTableRef = LUA->ReferenceCreate(); | |
LUA->PushSpecial(Lua::SPECIAL_GLOB); | |
LUA->PushString("v8"); | |
LUA->CreateTable(); | |
LUA->PushString("run"); | |
LUA->PushCFunction(luaf_v8_run); | |
LUA->SetTable(-3); | |
LUA->SetTable(-3); | |
//LUA->PushSpecial(Lua::SPECIAL_GLOB); | |
//table2proxy(); | |
ConColorMsg(COLOR_V8,"[V8] Module Initialized!\n"); | |
return 0; | |
} | |
//Called when the goddamn module is closed | |
//Not sure if this ever gets called since we're running in the menu context | |
GMOD_MODULE_CLOSE() | |
{ | |
lua_proxies.clear(); //Fun Fact: If you don't do this, V8 will break itself and hang 2-3 minutes trying to figure out what to do with the leftover handles. | |
v8engine->Dispose(); | |
ConColorMsg(COLOR_V8,"[V8] Module Stopped!\n"); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment