=begin
###############################################################
TCC Compiler for RGSS, v0.0.1
TCC original author: Fabrice Bellard
Porting: gjz010
Modified TCC source: [url]https://github.com/gjz010/tcc-nofs[/url]]https://github.com/gjz010/tcc-nofs
UVFS source: [url]https://github.com/gjz010/uvfs-tcc[/url]]https://github.com/gjz010/uvfs-tcc
###############################################################
API Document
###############################################################
TCC::Compiler
-----------------
The main wrapper class for using compiler.
Methods:
logs, set_options, add_include_path, add_library_path, add_library,
compile, define_macro, undef_macro, add_symbol, relocate,
get_symbol, get_cwp_symbol, dispose
CallWndProc
-----------------
Three variants of CallWndProc are provided:
TCC::call_window_proc_pl(addr, a, b=0) # Call with one buffer and one integer.
TCC::call_window_proc(addr, a=0, b=0) # Call with two integers.
TCC::call_window_proc_pp(addr, a, b) # Call with two buffers.
On stdlib
-----------------
The porting provides under a virtual internal filesystem called UVFS.
In most time you don't need to care about how UVFS works.
The stdlib provides tcc-libraries as-is:
tcc@uvfs://include
tcc@uvfs://include/winapi
tcc@uvfs://lib/{gdi32.def, kernel32.def, libtcc1.a, msvcrt.def, user32.def}
tcc@uvfs://lib/libtcc # Dangerous! Not the same as this libtcc, and not recommended.
On relocation
-----------------
Imported data may require dllimport to work. See libtcc examples.
CallWndProc-supported callback results in name-mangling.
Use get_cwp_symbol instead.
TCC::TCC_Raw
-----------------
Raw APIs for TCC. Use this if you are confident.
All methods are prefixed with "r_".
###############################################################
Usage
###############################################################
# The C source code.
SCRIPT= <<EOF
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
LRESULT CALLBACK foo(HWND a, UINT b, WPARAM c, LPARAM d){
char* buf=malloc(1024);
sprintf(buf, "Warm welcome from TCC!, %d+%d=%d\n", c, d, c+d);
MessageBox(0, buf, "Hi!", MB_OK);
free(buf);
return 0;
}
EOF
# Compiler options.
compiler=TCC::Compiler.new.set_options("-O2 -Wall -pedantic -Werror").
add_library("user32"). # Add user32 for MessageBox.
compile(SCRIPT). # Compile the string.
relocate # And then relocate.
# Call the compiled function with CallWindowProc.
TCC::call_window_proc(compiler.get_cwp_symbol("foo"), 1, 2)
# Dispose the compiler.
compiler.dispose
###############################################################
Known Issues
###############################################################
1. "-Werror" not working.
2. Bad options into set_options will cause a silent exit.
###############################################################
=end
module TCC
# Raw calls to TCC Compiler.
module TCC_Raw
TCC_CALLS_TABLE =
[
["L",:r_tcc_new, "V"],
["V",:r_tcc_delete, "L"],
["V",:r_tcc_set_lib_path, "LP"],
["V",:r_tcc_set_error_func, "LLL"],
["V",:r_tcc_set_options, "LP"],
["L",:r_tcc_add_include_path, "LP"],
["L",:r_tcc_add_sysinclude_path, "LP"],
["V",:r_tcc_define_symbol, "LPP"],
["V",:r_tcc_undefine_symbol, "LP"],
["L",:r_tcc_add_file, "LP"],
["L",:r_tcc_compile_string, "LP"],
["L",:r_tcc_set_output_type, "LL"],
["L",:r_tcc_add_library_path, "LP"],
["L",:r_tcc_add_library, "LP"],
["L",:r_tcc_add_symbol, "LPL"],
["L",:r_tcc_output_file, "LP"],
["L",:r_tcc_run, "LLP"],
["L",:r_tcc_relocate, "LL"],
["L",:r_tcc_get_symbol, "LP"],
["V",:initialize_uvfs, "V"]
]
for v in TCC_CALLS_TABLE
begin
api=Win32API.new "libtcc.dll",v[1].to_s,v[2],v[0]
lambda {|a| define_method(v[1]) do |*args| a.call(*args) end}.call(api)
end
end
TCC_OUTPUT_MEMORY = 1
TCC_OUTPUT_EXE = 2
TCC_OUTPUT_DLL = 3
TCC_OUTPUT_OBJ = 4
TCC_OUTPUT_PREPROCESS = 5
TCC_RELOCATE_AUTO = 1
# CWP for calling into function.
CallWindowProc = Win32API.new 'user32', 'CallWindowProcW', 'pLLLL', 'L'
CallWindowProcPL = Win32API.new 'user32', 'CallWindowProcW', 'pLLPL', 'L'
CallWindowProcPP = Win32API.new 'user32', 'CallWindowProcW', 'pLLPP', 'L'
extend self
end
TCC_Raw::initialize_uvfs()
class IllegalStateError < StandardError
end
class CompileError < StandardError
attr_reader :log
def initialize(log)
@log=log
end
def to_s
@log.to_s
end
end
class RelocationError < StandardError
attr_reader :log
def initialize(log)
@log=log
end
def to_s
@log.to_s
end
end
class Compiler
STATE_IDLE=0
STATE_COMPILED=1
STATE_RELOCATED=2
STATE_READY=3
def initialize
@s=TCC_Raw::r_tcc_new()
@state=0
initialize_error_handler
TCC_Raw::r_tcc_set_lib_path(@s, "nul@uvfs://")
add_include_path("tcc@uvfs://include").
add_include_path("tcc@uvfs://include/winapi").
add_library_path("tcc@uvfs://lib")
TCC_Raw::r_tcc_set_output_type(@s, TCC_Raw::TCC_OUTPUT_MEMORY)
@buffer=[]
end
def logs
@buffer
end
def initialize_error_handler
# Do nothing.
end
def fetch_log
# Do nothing
end
def add_include_path(path)
if @state!=STATE_IDLE
raise IllegalStateError.new
end
TCC_Raw::r_tcc_add_include_path(@s, path)
return self
end
def add_library_path(path)
if @state!=STATE_IDLE
raise IllegalStateError.new
end
TCC_Raw::r_tcc_add_library_path(@s, path)
return self
end
def add_library(lib)
if @state!=STATE_IDLE
raise IllegalStateError.new
end
TCC_Raw::r_tcc_add_library(@s, lib)
return self
end
def set_options(options)
if @state!=STATE_IDLE
raise IllegalStateError.new
end
TCC_Raw::r_tcc_set_options(@s, options)
return self
end
def compile(script)
if @state!=STATE_IDLE
raise IllegalStateError.new
end
ret=TCC_Raw::r_tcc_compile_string(@s, script)
fetch_log
if ret==-1
raise (CompileError.new(@buffer))
end
@state=STATE_COMPILED
return self
end
def define_macro(sym, v)
if @state!=STATE_IDLE
raise IllegalStateError.new
end
TCC_Raw::r_tcc_add_symbol(@s, sym, v)
return self
end
def undef_macro(sym)
if @state!=STATE_IDLE
raise IllegalStateError.new
end
TCC_Raw::r_tcc_undefine_symbol(@s, sym)
return self
end
def add_symbol(sym, addr)
if @state!=STATE_COMPILED
raise IllegalStateError.new
end
TCC_Raw::r_tcc_add_symbol(@s, sym, addr)
return self
end
def relocate
if @state!=STATE_COMPILED
raise IllegalStateError.new
end
ret=TCC_Raw::r_tcc_relocate(@s, TCC_Raw::TCC_RELOCATE_AUTO)
fetch_log
if ret<0
raise (RelocationError.new(@buffer))
end
@state=STATE_READY
return self
end
def dispose
TCC_Raw::r_tcc_delete(@s)
end
def get_symbol(sym)
if @state!=STATE_READY
raise IllegalStateError.new
end
s=TCC_Raw::r_tcc_get_symbol(@s, sym)
s
end
def get_cwp_symbol(sym)
get_symbol("_"+sym+"@16")
end
end
def call_window_proc(addr, a=0, b=0)
TCC_Raw::CallWindowProc.call addr,0,0,a,b
end
def call_window_proc_pl(addr, a, b=0)
TCC_Raw::CallWindowProcPL.call addr,0,0,a,b
end
def call_window_proc_pp(addr, a, b)
TCC_Raw::CallWindowProcPP.call addr,0,0,a,b
end
def log_handler
tcc=TCC::Compiler.new.compile( <<EOF
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
const char* buffer=0;
void tcc_error_handler(void* arg, const char* msg){
int n=0;
if(buffer==0){
n=strlen(msg)+2;
buffer=malloc(n);
strcpy(buffer, msg);
buffer[n-2]='\n';
buffer[n-1]=0;
}else{
n=strlen(msg)+2+strlen(buffer);
buffer=realloc(buffer, n);
strcat(buffer, msg);
buffer[n-2]='\n';
buffer[n-1]=0;
}
}
LRESULT CALLBACK move_tcc_log(HWND a, UINT b, WPARAM addrlog, LPARAM d){
if(buffer==0) return 0;
if(addrlog==0){
return strlen(buffer)+1;
}else{
strcpy(addrlog, buffer);
free(buffer);
int l=strlen(buffer)+1;
buffer=0;
return l;
}
}
EOF
).relocate
handler=tcc.get_symbol("tcc_error_handler")
moveback=tcc.get_cwp_symbol("move_tcc_log")
{:handler=>handler, :moveback=>moveback}
end
extend self
LOG_HANDLER = TCC::log_handler
class Compiler
def initialize_error_handler
TCC_Raw::r_tcc_set_error_func(@s, 0, LOG_HANDLER[:handler])
end
def fetch_log
len=TCC::call_window_proc(LOG_HANDLER[:moveback])
if len==0
return false
end
buffer = 0.chr * len
TCC::call_window_proc_pl(LOG_HANDLER[:moveback], buffer)
@buffer+=(buffer.split "\n")
return true
end
end
end