[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Start of Lua rewrite in C++
- From: Colin <share-lua@...>
- Date: Wed, 24 Oct 2007 18:34:14 +0200
Hi,
attached is the result of a few hours of starting to rewrite Lua
in C++. Actually I only wanted to understand the internals of Lua
better, so I started making some notes; these notes slowly turned
into code, and in the end I made it compile with g++ 4, for fun.
A few hours of work is of course not nearly enough, but it helped
me understand Lua as much as I need for my current task at hand,
so this is not even 1% complete, but if anybody is interested in
continuing ... here it is, do as you please.
It's not particularly heavy-weight C++, it's more of a first step
that only changes some obvious things to clean up the cast and
macro chaos. It also orthogonalises a bit of the naming.
Regards, Colin
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <inttypes.h>
namespace lua
{
typedef double number;
typedef uint32_t instruction;
const size_t minstack = 20;
const size_t extrastack = 5;
const size_t maxcalls = 42;
const size_t extraspace = 0;
const size_t basicstack = 2 * minstack;
const size_t basiccallinfo = 8;
#define LUA_ASSERT( aRG1 ) \
assert( aRG1 )
#define LUA_CHECKED( aRG1, aRG2 ) \
( LUA_ASSERT( aRG1 ), ( aRG2 ) )
typedef void * ( * reallocator )( void * ud, void * ptr, size_t osize, size_t nsize );
namespace types
{
static const uint8_t none = -1;
static const uint8_t nil = 0;
static const uint8_t boolean = 1;
static const uint8_t lightuserdata = 2;
static const uint8_t number = 3;
static const uint8_t string = 4;
static const uint8_t table = 5;
static const uint8_t closure = 6;
static const uint8_t userdata = 7;
static const uint8_t coroutine = 8;
} // types
namespace detail
{
struct memory;
struct gc_state;
namespace types
{
using namespace ::lua::types;
static const uint8_t last_type = 8;
static const uint8_t type_count = last_type + 1;
static const uint8_t prototype = last_type + 1;
static const uint8_t upvalue = last_type + 2;
static const uint8_t deadkey = last_type + 3;
} // types
template< class T > T bitmask1( const unsigned b ) { return T( 1 ) << b; }
template< class T > T bitmask2( const unsigned b, const unsigned c ) { return ( T( 1 ) << b ) | ( T( 1 ) << c ); }
template< class T > T set1bit( T & t, const unsigned b ) { return t |= bitmask1< T >( b ); }
template< class T > T set2bits( T & t, const unsigned b, const unsigned c ) { return t |= ( bitmask2< T >( b, c ) ); }
template< class T > T reset1bit( T & t, const unsigned b ) { return t &= ~ bitmask1< T >( b ); }
template< class T > T reset2bits( T & t, const unsigned b, const unsigned c ) { return t &= ~ ( bitmask2< T >( b, c ) ); }
void * default_realloc( void *, void * ptr, size_t, size_t nsize )
{
if ( nsize )
return ::realloc( ptr, nsize );
::free( ptr );
return 0;
}
void * realloc_wrapper( gc_state * s, void * ptr, const size_t osize, const size_t nsize );
void * malloc( gc_state * s, const size_t nsize )
{
return realloc_wrapper( s, 0, 0, nsize );
}
template< class T >
T * malloc( gc_state * s )
{
return static_cast< T * >( realloc_wrapper( s, 0, 0, sizeof( T ) ) );
}
template< class T >
T * malloc( gc_state * s, const size_t nsize )
{
return static_cast< T * >( realloc_wrapper( s, 0, 0, sizeof( T ) * nsize ) );
}
void free( gc_state * s, void * ptr, size_t osize )
{
realloc_wrapper( s, ptr, osize, 0 );
}
template< class T >
void free( gc_state * s, T * ptr )
{
realloc_wrapper( s, ptr, sizeof( T ), 0 );
}
template< class T >
void free( gc_state * s, T * ptr, const size_t osize )
{
realloc_wrapper( s, ptr, sizeof( T ) * osize, 0 );
}
template< class T >
T * realloc( gc_state * s, T * ptr, const size_t osize, const size_t nsize )
{
return static_cast< T * >( realloc_wrapper( s, ptr, osize * sizeof( T ), nsize * sizeof( T ) ) );
}
typedef int ( * c_function )( gc_state * );
union gc_union;
struct gc_super
{
gc_union * next;
uint8_t type;
uint8_t mark;
};
struct st_value
{
union
{
gc_union * gc;
gc_super * super;
void * pointer;
::lua::number number;
bool boolean;
};
uint8_t type;
};
struct table_node;
struct table_key
{
st_value key;
table_node * next;
};
struct table_node
{
table_key key;
st_value value;
};
struct gc_table : public gc_super
{
uint8_t flags;
uint8_t lsizenode;
gc_table * meta;
st_value * array;
table_node * node;
table_node * last;
gc_super * gc_list;
size_t size;
};
struct gc_string : public gc_super
{
size_t size;
uint8_t reserved;
uint32_t hash;
};
struct gc_userdata : public gc_super
{
size_t size;
gc_table * env;
gc_table * meta;
};
struct locvar
{
gc_string * varname;
size_t startpc;
size_t endpc;
};
struct gc_upvalue : public gc_super
{
st_value * v;
union
{
st_value value;
struct
{
gc_upvalue * prev;
gc_upvalue * next;
};
};
};
struct gc_prototype : public gc_super
{
st_value * k; /* constants used by the function */
instruction * code;
gc_prototype ** p; /* functions defined inside the function */
int * lineinfo; /* map from opcodes to source lines */
locvar * locvars; /* information about local variables */
gc_string ** upvalues; /* upvalue names */
gc_string * source;
int sizeupvalues;
int sizek; /* size of `k' */
int sizecode;
int sizelineinfo;
int sizep; /* size of `p' */
int sizelocvars;
int linedefined;
int lastlinedefined;
gc_super * gclist;
uint8_t nups; /* number of upvalues */
uint8_t numparams;
uint8_t is_vararg;
uint8_t maxstacksize;
};
struct gc_closure_super : gc_super
{
uint8_t C;
uint8_t upvalues;
gc_super * gclist;
gc_table * env;
};
struct gc_c_closure : public gc_closure_super
{
c_function f;
st_value upvalue[ 1 ];
};
struct gc_lua_closure : public gc_closure_super
{
gc_prototype * p;
gc_upvalue * upvalues[ 1 ];
};
union gc_closure
{
gc_closure_super s;
gc_c_closure c;
gc_lua_closure l;
};
struct stringtable
{
stringtable()
: hash( 0 ),
used( 0 ),
size( 0 )
{ }
gc_union ** hash;
size_t used;
size_t size;
};
size_t hash_modulus( const size_t hash, const size_t size )
{
LUA_ASSERT( ! ( size & ( size - 1 ) ) );
return hash & ( size - 1 );
}
size_t hash_function( const char * s, const size_t l )
{
unsigned h = l;
const size_t step = ( l >> 5 ) + 1;
for ( size_t l1 = l; l1 >= step; l1 -= step )
h = h ^ ( ( h << 5 ) + ( h >> 2 ) + ( * reinterpret_cast< const unsigned char * >( s + l1 - 1 ) ) );
return h;
}
void set_nil( st_value * a )
{
a->type = types::nil;
}
struct memory
{
explicit memory( gc_state * main, reallocator realloc, void * userdata )
: main( main ),
realloc( realloc ),
realloc_total( 0 ),
realloc_userdata( userdata )
{
set_nil( & registry );
}
gc_state * main;
st_value registry;
reallocator realloc;
size_t realloc_total;
void * realloc_userdata;
stringtable strings;
};
struct callinfo
{
st_value * st_base;
st_value * st_func;
st_value * st_top;
const instruction * savedpc;
int nresults;
int tailcalls;
};
struct gc_state : public gc_super
{
uint8_t status;
size_t st_size; // st_end - st_begin + 1 + extrastack
st_value * st_begin;
st_value * st_end;
st_value * st_base;
st_value * st_top;
callinfo * ci_begin;
callinfo * ci_end;
callinfo * ci_current;
st_value env;
st_value globals;
gc_union * open_upvalues;
const instruction * savedpc;
::lua::detail::memory * memory;
};
class main_state : public gc_state
{
public:
main_state();
main_state( reallocator realloc, void * userdata );
~main_state();
private:
void initialise();
::lua::detail::memory memory_;
private:
main_state( const main_state & );
void operator= ( const main_state & );
};
void correct_stack_impl( gc_state * s, st_value * old );
st_value * realloc_stack_impl( gc_state * s, size_t nelems );
void realloc_stack( gc_state * s, size_t nelems )
{
correct_stack_impl( s, realloc_stack_impl( s, nelems ) );
}
void grow_stack( gc_state * s, size_t nelems )
{
if ( nelems <= s->st_size )
realloc_stack( s, 2 * s->st_size );
else
realloc_stack( s, s->st_size + nelems );
}
void realloc_callinfo( gc_state * s, size_t nelems )
{
const size_t diff = s->ci_current - s->ci_begin;
s->ci_begin = realloc< callinfo >( s, s->ci_begin, s->ci_end - s->ci_begin, nelems );
s->ci_current = s->ci_begin + diff;
s->ci_end = s->ci_begin + nelems - 1; // Why the -1?
}
callinfo * grow_callinfo( gc_state * s )
{
const size_t size = s->ci_end - s->ci_begin;
if ( size > maxcalls )
throw "callinfo growing in error handler";
realloc_callinfo( s, 2 * size );
if ( s->ci_end > maxcalls + s->ci_begin )
throw "callinfo growing too large";
return ++s->ci_current;
}
union gc_union
{
gc_super super;
gc_closure closure;
gc_prototype prototype;
gc_string string;
gc_table table;
gc_userdata userdata;
gc_upvalue upvalue;
gc_state state;
};
gc_union * get_next( gc_union * u ) { return u->super.next; }
uint8_t type( st_value * a ) { return a->type; }
uint8_t type( gc_super * a ) { return a->type; }
bool is_nil( gc_super * a ) { return a->type == types::nil; }
bool is_boolean( gc_super * a ) { return a->type == types::boolean; }
bool is_lightuserdata( gc_super * a ) { return a->type == types::lightuserdata; }
bool is_number( gc_super * a ) { return a->type == types::number; }
bool is_string( gc_super * a ) { return a->type == types::string; }
bool is_table( gc_super * a ) { return a->type == types::table; }
bool is_closure( gc_super * a ) { return a->type == types::closure; }
bool is_userdata( gc_super * a ) { return a->type == types::userdata; }
bool is_coroutine( gc_super * a ) { return a->type == types::coroutine; }
bool is_nil( st_value * a ) { return a->type == types::nil; }
bool is_boolean( st_value * a ) { return a->type == types::boolean; }
bool is_lightuserdata( st_value * a ) { return a->type == types::lightuserdata; }
bool is_number( st_value * a ) { return a->type == types::number; }
bool is_string( st_value * a ) { return a->type == types::string; }
bool is_table( st_value * a ) { return a->type == types::table; }
bool is_closure( st_value * a ) { return a->type == types::closure; }
bool is_userdata( st_value * a ) { return a->type == types::userdata; }
bool is_coroutine( st_value * a ) { return a->type == types::coroutine; }
bool is_upvalue( gc_super * a ) { return a->type == types::upvalue; }
bool is_c_closure( st_value * a ) { return is_closure( a ) && a->gc->closure.s.C; }
bool is_lua_closure( st_value * a ) { return is_closure( a ) && ! a->gc->closure.s.C; }
bool is_collectable( st_value * a ) { return type( a ) >= types::string; }
size_t get_size( gc_string * a ) { return sizeof( gc_string ) + sizeof( char ) * ( a->size + 1 ); }
size_t get_size( gc_userdata * a ) { return sizeof( gc_userdata ) + a->size; }
gc_super * to_super( st_value * a ) { return LUA_CHECKED( is_collectable( a ), & a->gc->super ); }
bool to_boolean( st_value * a ) { return LUA_CHECKED( is_boolean( a ), a->boolean ); }
void * to_pointer( st_value * a ) { return LUA_CHECKED( is_lightuserdata( a ), a->pointer ); }
gc_string * to_string( st_value * a ) { return LUA_CHECKED( is_string( a ), & a->gc->string ); }
gc_table * to_table( st_value * a ) { return LUA_CHECKED( is_table( a ), & a->gc->table ); }
gc_closure * to_closure( st_value * a ) { return LUA_CHECKED( is_closure( a ), & a->gc->closure ); }
gc_string * to_string( gc_union * a ) { return LUA_CHECKED( is_string( & a->super ), & a->string ); }
gc_upvalue * to_upvalue( gc_union * a ) { return LUA_CHECKED( is_upvalue( & a->super ), & a->upvalue ); }
bool is_false( st_value * a ) { return is_nil( a ) || ( is_boolean( a ) && ! to_boolean( a ) ); }
bool is_true( st_value * a ) { return ! is_false( a ); }
void realloc_strings( gc_state * s, const size_t news )
{
// TODO sweepstring check
stringtable * t = & s->memory->strings;
gc_union ** newh = malloc< gc_union * >( s, news );
::memset( newh, 0, news * sizeof( gc_union * ) );
for ( size_t i = 0; i < t->size; ++t )
{
gc_union * p = t->hash[ i ];
while ( p )
{
gc_union * n = get_next( p );
const size_t h0 = to_string( p )->hash;
const size_t h1 = hash_modulus( h0, news );
p->super.next = newh[ h1 ];
newh[ h1 ] = p;
p = n;
}
}
free< gc_union * >( s, t->hash, t->size );
t->hash = newh;
t->size = news;
}
void set_userdata( gc_state * s, st_value * t, gc_super * x )
{
t->super = x;
t->type = types::userdata;
// TODO: checkliveness
}
void api_increment_stack( gc_state * s )
{
LUA_CHECKED( s->st_top < s->ci_current->st_top, ++s->st_top );
}
gc_union * push_next( gc_super * a )
{
gc_union * nrv = a->next;
a->next = reinterpret_cast< gc_union * >( a );
return nrv;
}
gc_userdata * new_userdata_impl( gc_state * s, const size_t size, gc_table * env )
{
gc_userdata * nrv = static_cast< gc_userdata * >( malloc( s, sizeof( gc_userdata ) + size ) );
nrv->type = types::userdata;
nrv->size = size;
nrv->env = env;
nrv->meta = 0;
nrv->next = push_next( s->memory->main );
return nrv;
}
gc_table * get_current_env( gc_state * s )
{
if ( s->ci_current == s->ci_begin )
return to_table( & s->globals );
else
return to_closure( s->ci_current->st_func )->s.env;
}
void * new_userdata( gc_state * s, size_t size )
{
// TODO checkgc()
// TODO check alignment
gc_userdata * nrv = new_userdata_impl( s, size, get_current_env( s ) );
set_userdata( s, s->st_top, nrv );
api_increment_stack( s );
return nrv + 1;
}
template< class T >
T * new_userdata( gc_state * s )
{
return static_cast< T * >( new_userdata( s, sizeof( T ) ) );
}
main_state::main_state()
: memory_( this, default_realloc, 0 )
{
initialise();
}
main_state::main_state( reallocator realloc, void * userdata )
: memory_( this, realloc, userdata )
{
initialise();
}
main_state::~main_state()
{
// TODO heck of a lot of stuff
}
void main_state::initialise()
{
next = 0;
type = types::coroutine;
mark = 0; // TODO
status = 0;
st_size = 0;
st_begin = st_end = st_base = st_top = 0;
ci_begin = ci_end = ci_current = 0;
set_nil( & env );
set_nil( & globals );
savedpc = 0;
memory = & memory_;
realloc_stack( this, basicstack );
realloc_callinfo( this, basiccallinfo );
set_nil( st_top++ );
}
void * realloc_wrapper( gc_state * s, void * ptr, const size_t osize, const size_t nsize )
{
memory * g = s->memory;
LUA_ASSERT( bool( osize ) == bool( ptr ) );
ptr = g->realloc( g->realloc_userdata, ptr, osize, nsize );
if ( nsize && ! bool( ptr ) )
throw "out of memory";
LUA_ASSERT( bool( nsize ) == bool( ptr ) );
g->realloc_total += nsize - osize;
return ptr;
}
void correct_stack_impl( gc_state * s, st_value * old )
{
const size_t diff = s->st_begin - old;
s->st_top += diff;
s->st_base += diff;
for ( gc_union * u = s->open_upvalues; u; u = get_next( u ) )
{
to_upvalue( u )->v += diff;
}
for ( callinfo * i = s->ci_begin; i <= s->ci_current; ++i )
{
i->st_top += diff;
i->st_base += diff;
i->st_func += diff;
}
}
st_value * realloc_stack_impl( gc_state * s, size_t nelems )
{
st_value * old = s->st_begin;
const size_t realn = nelems + 1 + extrastack;
s->st_begin = realloc< st_value >( s, s->st_begin, s->st_end - s->st_begin, realn );
s->st_end = s->st_begin + nelems;
s->st_size = realn;
return old;
}
} // detail
} // lua
int main( int, char ** )
{
return 0;
}