1
#include "runner/runner.hpp"
2
#include "runner/expecter.hpp"
4
#include <boost/algorithm/string/predicate.hpp>
5
#include <boost/filesystem.hpp>
6
namespace fs = boost::filesystem;
14
// Root Leaf data files (these are generated files, in the build directory)
15
#include "runner/base.leaf.hpp"
18
#include "util/debug.hpp"
20
shared_ptr<ir::module> runner::build_module()
22
shared_ptr<ir::module> m;
24
STATE_CHECK( input_files.size() );
25
auto & first_file = input_files[0];
27
if( boost::ends_with( first_file, ".lfx" ) ) {
28
STATE_CHECK( input_files.size() == 1, "only one lfx file supported as input" );
30
} else if( boost::ends_with( first_file, ".lfb" ) ) {
31
STATE_CHECK( input_files.size() == 1, "only one lfb file supported as input" );
33
} else if( boost::ends_with( first_file, ".leaf" ) ) {
36
STATE_FAIL( "unknown input-file type" );
38
} catch( recoverable_error & re ) {
40
expect->catch_compile_exception(re);
41
return shared_ptr<ir::module>();
47
ir::dump d( std::cout );
48
d.set_show_all_types( dump_ir_types );
54
void runner::x_dump_parse( leaf::node const & n )
57
std::cout << leaf::node_dump::dump_flat(n) << std::endl;
62
leaf::node_dump nd( std::cout );
63
nd.header( "// -- Parse Tree --" );
65
std::cout << std::endl;
68
void runner::x_dump_post_type( leaf::module const & mod ) {
72
std::cout << "// -- Post-Typing Leaf Tree --" << std::endl;
73
leaf::dump d( std::cout );
74
d.set_show_all_types( dump_all_types );
75
d.write_module( mod );
78
void runner::x_dump_pre_type( std::vector<shared_ptr<leaf::statement>> const & sb )
83
std::cout << "// -- Pre-Typing Leaf Tree --" << std::endl;
84
leaf::dump d( std::cout );
85
d.set_show_spec_type( true );
86
d.set_validate( false );
87
for( auto & s : sb ) {
88
d.write_statement( *s );
91
std::cout << std::endl;
94
shared_ptr<leaf::expression> runner::parse_expr( std::string const & expr )
96
auto src = leaf::source::create( expr );
98
shared_ptr<leaf::node> n = leaf::node_parser::parse_single( *src );
101
auto ex = leaf::node_converter::expression_convert( *n );
104
leaf::dump d( std::cout );
105
d.set_show_spec_type( true );
106
d.set_validate( false );
108
std::cout << std::endl;
111
//the expression must be converted to its inferred type to ensure it is representable as
112
//an actual compiled type (such as removing rationals)
113
ex = module_typer->full_type( ex, leaf::typer::tef_convert_inferred );
115
if( dump_post_type ) {
116
leaf::dump d( std::cout );
117
d.set_show_all_types( dump_all_types );
119
std::cout << std::endl;
125
runner::statement_vector_t runner::load_parse_block( std::string const & filename,
126
leaf::scope_style_t scope_style, bool dump ) {
127
DEBUG( "Block-Load", filename );
128
auto s = leaf::source::create( util::load_file( filename ) );
129
s->set_name( filename );
131
return load_parse_block( s, scope_style, dump);
134
runner::statement_vector_t runner::load_parse_block( shared_ptr<leaf::source> src,
135
leaf::scope_style_t scope_style, bool dump ) {
137
DEBUG( "Block-Parse" );
138
auto n = leaf::node_parser::parse_as_block( *src );
139
DEBUG( "Block-Parse-Dump" );
143
DEBUG( "Block-Post-Parse" );
145
auto block = leaf::node_converter::convert_block( *n, scope_style );
147
x_dump_pre_type( block );
149
DEBUG( "Block-Post-Convert" );
154
void runner::do_typing( bool base )
156
module_typer->full_complete_type();
158
if( !base || dump_base ) {
159
x_dump_post_type( *module );
162
if( !base && list_functions ) {
163
std::cout << "Function List" << std::endl;
164
for( auto & fd : module->funcdefns ) {
165
std::string name = fd->bound_decl ? fd->bound_decl->symbol : "anonymous";
166
std::cout << name << " : " << leaf::dump::get( fd->type ) << std::endl;
171
void runner::init_module() {
172
module_typer = make_shared<leaf::typer>( module_name, mloader );
173
module = module_typer->get_module();
175
add_base_block( create_string_source("base.leaf",dataBaseLeaf) );
178
void runner::add_base_block( shared_ptr<leaf::source> src ) {
179
auto list = load_parse_block( src, leaf::scope_style_t::declarative, false );
180
auto block = module_typer->add_root_block( list );
181
block->reference_text = src->get_name();
185
shared_ptr<ir::module> runner::get_expr() {
186
shared_ptr<leaf::expression> ex = parse_expr( util::load_file( input_files.at(0) ) );
187
ir::compiler_leaf c( module->global );
189
shared_ptr<ir::module> m = c.compile_expr( *ex );
190
auto f = m->if_get_function( "eval_expr" );
192
main_trace_function( c, f );
197
shared_ptr<ir::module> runner::get_block()
199
auto list = load_parse_block( input_files.at(0), leaf::scope_style_t::dynamic );
200
auto block = module_typer->add_dynamic_block( list );
203
ir::compiler_leaf c( module->global );
204
return c.compile_block( *block, *module->static_init_block );
207
shared_ptr<ir::module> runner::get_full() {
209
for( auto & file : input_files ) {
210
auto list = load_parse_block( file, leaf::scope_style_t::declarative );
211
auto block = module_typer->add_global_block( list );
212
block->reference_text = file;
216
ir::compiler_leaf c( module->global );
217
return c.compile_module( *module );
220
int runner::execute( shared_ptr<ir::module> m ) {
221
//load explicit libraries (mainly for testing)
222
std::vector<void*> lib_handles;
223
auto load_lib = [&]( std::string const & path ) {
224
auto handle = dlopen( path.c_str(), RTLD_NOW | RTLD_GLOBAL );
225
STATE_CHECK( handle, std::string( "failed loading library: " )
226
+ path + ": " + dlerror() );
227
lib_handles.push_back( handle );
230
for( auto & lib : libraries ) {
233
//load imported libraries
234
for( auto & lib : module->libraries ) {
235
load_lib( mloader->resolve_dynload(lib) );
238
//setup capturing of stdout
239
volatile int old_stdout = 0; //GCC: complains it will get clobbered by longjmp without volatile
240
char out_filename[128] = "/tmp/output_XXXXXX";
241
if( capture_stdout ) {
242
old_stdout = dup(STDOUT_FILENO);
243
int out_file = mkstemp(out_filename);
246
dup2( out_file, STDOUT_FILENO );
249
auto end_redirect = [&]() {
255
dup2(old_stdout, STDOUT_FILENO);
257
auto clean_redirect = [&]() {
258
unlink(out_filename);
261
//volatile to not be clobberd by setjmp/longjmp
262
volatile int return_value = 0;
266
int i = setjmp(abort_point);
269
if( !expect || !expect->catch_runtime_abort( runtime_test::get_error() ) ) {
270
throw recoverable_error( std::string( "abort: " ) + runtime_test::get_error() );
273
runtime_test::prepare( &abort_point );
274
ir_llvm::gen lg( gen_opts );
275
int rv = lg.execute( *m, dump_llvm );
278
runtime_test::check_state();
280
std::string output = runtime_test::get_trace();
282
output = util::load_file( out_filename );
284
//DEBUG( "OUTPUT:", output );
285
expect->check_result( output, rv );
301
void runner::emit_object( shared_ptr<ir::module> m, std::string const & path_base, gen_what_t gw )
303
ir_llvm::gen lg( gen_opts );
304
lg.emit_object( *m, path_base, dump_llvm );
306
if( gw == gw_shared ) {
307
#if defined(IS_DARWIN)
308
std::string cmd( "gcc -dynamiclib -Wl,-undefined,dynamic_lookup,-o," );
309
cmd += mloader->dyn_name(path_base) + ",-init,"
310
+ "_" + *module_name + platform::export_sep + "_init_module"
311
//there just is no "fini" on OSX!
312
//+ " " + mloader->resolve_dyn_core( "runtime" )
313
+ " " + mloader->obj_name(path_base);
315
std::string cmd( "gcc -shared -Wl,--eh-frame-hdr,--build-id,--export-dynamic,-o," );
316
cmd += path_base + ".so,-init,"
317
+ *module_name + platform::export_sep + "_init_module,-fini,"
318
+ *module_name + platform::export_sep + "_final_module"
319
//+ " " + mloader->resolve_dyn_core( "runtime" )
320
+ "," + path_base + ".o";
324
//These work, but don't produce proper shared libraries (ldd says statically linked) (they also don't
325
//contain the shared library dependencies)
326
std::string cmd( "ld --eh-frame-hdr --build-id --export-dynamic -shared -o " );
327
cmd += path_base + ".so -init "
328
+ *module_name + platform::export_sep + "_init_module -fini "
329
+ *module_name + platform::export_sep + "_final_module"
330
//+ " " + mloader->resolve_dyn_core( "runtime" )
331
+ " " + path_base + ".o";
334
for( auto & lib : module->libraries ) {
335
cmd = cmd + " " + mloader->resolve_dynlink( lib );
337
int ret = std::system( cmd.c_str() );
338
STATE_CHECK( ret == 0, cmd );
339
} else if( gw == gw_exe ) {
340
#if defined(IS_DARWIN)
341
std::string cmd( "gcc -Wl -o " );
342
cmd += path_base + " " + mloader->obj_name(path_base)
343
//+ " " + mloader->resolve_static_core( "runtime_support" )
344
+ " " + mloader->resolve_dyn_core( "runtime" )
347
//NOTE: switch to g++ for rare debugging needs with iostream
348
std::string cmd( "gcc -Wl,--eh-frame-hdr,--build-id,--export-dynamic -o " );
349
cmd += path_base + " " + path_base + ".o"
350
+ " " + mloader->resolve_static_core( "runtime_support" )
351
//+ " " + mloader->resolve_dyn_core( "runtime" )
355
for( auto & lib : module->libraries ) {
356
cmd = cmd + " " + mloader->resolve_dynlink( lib );
359
int ret = std::system( cmd.c_str() );
360
STATE_CHECK( ret == 0, cmd );
363
if( gw == gw_shared ) {
364
//detach scope and create leaf object file (this breaks the module!)
365
module->global->detach_base();
366
std::ofstream lo( mloader->leaflib_name(path_base), std::ios::binary );
367
leaf::serial_out sout( lo, module_typer->get_serial_resolver() );
368
sout.set_is_exporting();
369
sout.write_key_ref( leaf::serial_item::t_root, module->global );
370
for( auto & ex : module->var_exports ) {
371
sout.add_declaration( ex );
373
for( auto & ex : module->type_exports ) {
374
sout.add_intr_type( ex );
380
//TODO: more of a util function
381
shared_ptr<leaf::source> runner::create_string_source( std::string const & name, std::string const & content ) {
382
auto s = leaf::source::create( content );