Skip to content

Instantly share code, notes, and snippets.

@MDFL64
Created August 23, 2014 21:18
Show Gist options
  • Save MDFL64/9c74c257b786fa58e0e2 to your computer and use it in GitHub Desktop.
Save MDFL64/9c74c257b786fa58e0e2 to your computer and use it in GitHub Desktop.
gm_v8
#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