142 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			C++
		
	
	
		
			Executable File
		
	
			
		
		
	
	
			142 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			C++
		
	
	
		
			Executable File
		
	
| #include "master.hpp"
 | |
| 
 | |
| namespace factor
 | |
| {
 | |
| 
 | |
| callback_heap::callback_heap(cell size, factor_vm *parent_) :
 | |
| 	seg(new segment(size,true)),
 | |
| 	here(seg->start),
 | |
| 	parent(parent_) {}
 | |
| 
 | |
| callback_heap::~callback_heap()
 | |
| {
 | |
| 	delete seg;
 | |
| 	seg = NULL;
 | |
| }
 | |
| 
 | |
| void factor_vm::init_callbacks(cell size)
 | |
| {
 | |
| 	callbacks = new callback_heap(size,this);
 | |
| }
 | |
| 
 | |
| bool callback_heap::setup_seh_p()
 | |
| {
 | |
| #if defined(WINDOWS) && defined(FACTOR_X86)
 | |
| 	return true;
 | |
| #else
 | |
| 	return false;
 | |
| #endif
 | |
| }
 | |
| 
 | |
| bool callback_heap::return_takes_param_p()
 | |
| {
 | |
| #if defined(FACTOR_X86) || defined(FACTOR_AMD64)
 | |
| 	return true;
 | |
| #else
 | |
| 	return false;
 | |
| #endif
 | |
| }
 | |
| 
 | |
| instruction_operand callback_heap::callback_operand(code_block *stub, cell index)
 | |
| {
 | |
| 	tagged<array> code_template(parent->special_objects[CALLBACK_STUB]);
 | |
| 	tagged<byte_array> relocation_template(array_nth(code_template.untagged(),0));
 | |
| 
 | |
| 	relocation_entry entry(relocation_template->data<relocation_entry>()[index]);
 | |
| 	return instruction_operand(entry,stub,0);
 | |
| }
 | |
| 
 | |
| void callback_heap::store_callback_operand(code_block *stub, cell index)
 | |
| {
 | |
| 	parent->store_external_address(callback_operand(stub,index));
 | |
| }
 | |
| 
 | |
| void callback_heap::store_callback_operand(code_block *stub, cell index, cell value)
 | |
| {
 | |
| 	callback_operand(stub,index).store_value(value);
 | |
| }
 | |
| 
 | |
| void callback_heap::update(code_block *stub)
 | |
| {
 | |
| 	store_callback_operand(stub,setup_seh_p() ? 2 : 1,(cell)callback_entry_point(stub));
 | |
| 	stub->flush_icache();
 | |
| }
 | |
| 
 | |
| code_block *callback_heap::add(cell owner, cell return_rewind)
 | |
| {
 | |
| 	tagged<array> code_template(parent->special_objects[CALLBACK_STUB]);
 | |
| 	tagged<byte_array> insns(array_nth(code_template.untagged(),1));
 | |
| 	cell size = array_capacity(insns.untagged());
 | |
| 
 | |
| 	cell bump = align(size + sizeof(code_block),data_alignment);
 | |
| 	if(here + bump > seg->end) fatal_error("Out of callback space",0);
 | |
| 
 | |
| 	free_heap_block *free_block = (free_heap_block *)here;
 | |
| 	free_block->make_free(bump);
 | |
| 	here += bump;
 | |
| 
 | |
| 	code_block *stub = (code_block *)free_block;
 | |
| 	stub->owner = owner;
 | |
| 	stub->parameters = false_object;
 | |
| 	stub->relocation = false_object;
 | |
| 
 | |
| 	memcpy(stub->entry_point(),insns->data<void>(),size);
 | |
| 
 | |
| 	/* Store VM pointer */
 | |
| 	store_callback_operand(stub,0,(cell)parent);
 | |
| 
 | |
| 	cell index;
 | |
| 
 | |
| 	if(setup_seh_p())
 | |
| 	{
 | |
| 		store_callback_operand(stub,1);
 | |
| 		index = 1;
 | |
| 	}
 | |
| 	else
 | |
| 		index = 0;
 | |
| 
 | |
| 	/* Store VM pointer */
 | |
| 	store_callback_operand(stub,index + 2,(cell)parent);
 | |
| 
 | |
| 	/* On x86, the RET instruction takes an argument which depends on
 | |
| 	the callback's calling convention */
 | |
| 	if(return_takes_param_p())
 | |
| 		store_callback_operand(stub,index + 3,return_rewind);
 | |
| 
 | |
| 	update(stub);
 | |
| 
 | |
| 	return stub;
 | |
| }
 | |
| 
 | |
| struct callback_updater {
 | |
| 	callback_heap *callbacks;
 | |
| 
 | |
| 	explicit callback_updater(callback_heap *callbacks_) : callbacks(callbacks_) {}
 | |
| 
 | |
| 	void operator()(code_block *stub)
 | |
| 	{
 | |
| 		callbacks->update(stub);
 | |
| 	}
 | |
| };
 | |
| 
 | |
| void callback_heap::update()
 | |
| {
 | |
| 	callback_updater updater(this);
 | |
| 	each_callback(updater);
 | |
| }
 | |
| 
 | |
| /* Allocates memory */
 | |
| void factor_vm::primitive_callback()
 | |
| {
 | |
| 	cell return_rewind = to_cell(ctx->pop());
 | |
| 	tagged<word> w(ctx->pop());
 | |
| 
 | |
| 	w.untag_check(this);
 | |
| 
 | |
| 	void* func = callbacks->add(w.value(),return_rewind)->entry_point();
 | |
| 	CODE_TO_FUNCTION_POINTER_CALLBACK(this, func);
 | |
| 	ctx->push(allot_alien(func));
 | |
| }
 | |
| 
 | |
| }
 |