/* * Copyright (C)2005-2016 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include HL_PRIM int hl_closure_stack_capture = 0; static void fun_var_args() { hl_error("Variable fun args was not cast to typed function"); } HL_PRIM vclosure *hl_alloc_closure_void( hl_type *t, void *fvalue ) { vclosure *c = (vclosure*)hl_gc_alloc_noptr(sizeof(vclosure)); c->t = t; c->fun = fvalue; c->hasValue = 0; c->value = NULL; return c; } static hl_type *hl_get_closure_type( hl_type *t ) { hl_type_fun *ft = t->fun; if( ft->closure_type.kind != HFUN ) { if( ft->nargs == 0 ) hl_fatal("assert"); ft->closure_type.kind = HFUN; ft->closure_type.p = &ft->closure; ft->closure.nargs = ft->nargs - 1; ft->closure.args = ft->closure.nargs ? ft->args + 1 : NULL; ft->closure.ret = ft->ret; ft->closure.parent = t; } return (hl_type*)&ft->closure_type; } int hl_internal_capture_stack( void **stack, int size ); HL_PRIM vclosure *hl_alloc_closure_ptr( hl_type *fullt, void *fvalue, void *v ) { hl_type *t = hl_get_closure_type(fullt); vclosure *c = (vclosure*)hl_gc_alloc(t, sizeof(vclosure) + sizeof(void*) * hl_closure_stack_capture); c->t = t; c->fun = fvalue; c->hasValue = 1; # ifdef HL_64 int stack = 0; if( hl_closure_stack_capture ) stack = hl_internal_capture_stack((void**)(c + 1), hl_closure_stack_capture); c->stackCount = stack; # endif c->value = v; return c; } HL_PRIM vdynamic *hl_no_closure( vdynamic *c ) { vclosure *cl = (vclosure*)c; if( !cl->hasValue ) return c; if( cl->hasValue == 2 ) return hl_no_closure((vdynamic*)((vclosure_wrapper*)c)->wrappedFun); return (vdynamic*)hl_alloc_closure_void(cl->t->fun->parent,cl->fun); } HL_PRIM vdynamic *hl_make_closure( vdynamic *c, vdynamic *v ) { vclosure *cl = (vclosure*)c; hl_type *t = cl->hasValue ? cl->t->fun->parent : cl->t; if( cl->hasValue == 2 ) return hl_make_closure((vdynamic*)((vclosure_wrapper*)c)->wrappedFun, v); if( t->fun->nargs == 0 || !v || !hl_safe_cast(v->t,t->fun->args[0]) ) return NULL; return (vdynamic*)hl_alloc_closure_ptr( t, cl->fun, v ); } HL_PRIM vdynamic* hl_get_closure_value( vdynamic *c ) { vclosure *cl = (vclosure*)c; if( !cl->hasValue ) return NULL; if( cl->hasValue == 2 ) return hl_get_closure_value((vdynamic*)((vclosure_wrapper*)c)->wrappedFun); if( cl->fun == fun_var_args ) return NULL; return hl_make_dyn(&cl->value, cl->t->fun->parent->fun->args[0]); } HL_PRIM bool hl_fun_compare( vdynamic *a, vdynamic *b ) { vclosure *ca, *cb; if( a == b ) return true; if( !a || !b ) return false; if( a->t->kind != b->t->kind || a->t->kind != HFUN ) return false; ca = (vclosure*)a; cb = (vclosure*)b; if( ca->fun != cb->fun ) return false; if( ca->hasValue && ca->value != cb->value ) return false; return true; } // ------------ DYNAMIC CALLS typedef void *(*fptr_static_call)(void *fun, hl_type *t, void **args, vdynamic *out); typedef void *(*fptr_get_wrapper)(hl_type *t); static fptr_static_call hlc_static_call = NULL; static fptr_get_wrapper hlc_get_wrapper = NULL; static int hlc_call_flags = 0; HL_PRIM void hl_setup_callbacks2( void *c, void *w, int flags ) { hlc_static_call = (fptr_static_call)c; hlc_get_wrapper = (fptr_get_wrapper)w; hlc_call_flags = flags; } HL_PRIM void hl_setup_callbacks( void *c, void *w ) { hl_setup_callbacks2(c,w,0); } #define HL_MAX_ARGS 9 HL_PRIM vdynamic* hl_call_method( vdynamic *c, varray *args ) { vclosure *cl = (vclosure*)c; vdynamic **vargs = hl_aptr(args,vdynamic*); void *pargs[HL_MAX_ARGS]; void *ret; union { double d; int i; float f; int64 i64; } tmp[HL_MAX_ARGS]; hl_type *tret; vdynamic *dret; vdynamic out; int i; if( args->size > HL_MAX_ARGS ) hl_error("Too many arguments"); if( cl->hasValue ) { if( cl->fun == fun_var_args ) { cl = (vclosure*)cl->value; return cl->hasValue ? ((vdynamic* (*)(vdynamic*, varray*))cl->fun)(cl->value, args) : ((vdynamic* (*)(varray*))cl->fun)(args); } hl_error("Can't call closure with value"); } if( args->size < cl->t->fun->nargs ) hl_error("Missing arguments : %d expected but %d passed",cl->t->fun->nargs, args->size); for(i=0;it->fun->nargs;i++) { vdynamic *v = vargs[i]; hl_type *t = cl->t->fun->args[i]; void *p; if( v == NULL ) { if( hl_is_ptr(t) ) p = NULL; else { tmp[i].d = 0; p = &tmp[i].d; } } else switch( t->kind ) { case HBOOL: case HUI8: case HUI16: case HI32: tmp[i].i = hl_dyn_casti(vargs + i, &hlt_dyn,t); p = &tmp[i].i; break; case HI64: tmp[i].i64 = hl_dyn_casti64(vargs + i, &hlt_dyn); p = &tmp[i].i64; break; case HF32: tmp[i].f = hl_dyn_castf(vargs + i, &hlt_dyn); p = &tmp[i].f; break; case HF64: tmp[i].d = hl_dyn_castd(vargs + i, &hlt_dyn); p = &tmp[i].d; break; default: p = hl_dyn_castp(vargs + i,&hlt_dyn,t); break; } pargs[i] = p; } ret = hlc_static_call(hlc_call_flags & 1 ? &cl->fun : cl->fun,cl->t,pargs,&out); tret = cl->t->fun->ret; if( !hl_is_ptr(tret) ) { vdynamic *r; switch( tret->kind ) { case HVOID: return NULL; case HBOOL: return hl_alloc_dynbool(out.v.b); default: r = hl_alloc_dynamic(tret); r->t = tret; r->v.d = out.v.d; // copy return r; } } if( ret == NULL || hl_is_dynamic(tret) ) return (vdynamic*)ret; dret = hl_alloc_dynamic(tret); dret->v.ptr = ret; return dret; } HL_PRIM vdynamic *hl_dyn_call( vclosure *c, vdynamic **args, int nargs ) { struct { varray a; vdynamic *args[HL_MAX_ARGS+1]; } tmp; vclosure ctmp; int i = 0; if( nargs > HL_MAX_ARGS ) hl_error("Too many arguments"); tmp.a.t = &hlt_array; tmp.a.at = &hlt_dyn; tmp.a.size = nargs; if( c->hasValue && c->t->fun->nargs >= 0 ) { ctmp.t = c->t->fun->parent; ctmp.hasValue = 0; ctmp.fun = c->fun; tmp.args[0] = hl_make_dyn(&c->value,ctmp.t->fun->args[0]); tmp.a.size++; for(i=0;icl.t->fun; union { double d; int i; float f; int64 i64; } tmp[HL_MAX_ARGS]; void *vargs[HL_MAX_ARGS+1]; vdynamic out; vclosure *w = c->wrappedFun; int i; int p = 0; void *pret, *aret; if( ret == NULL ) ret = &out; if( w->fun == fun_var_args ) { varray *a; w = (vclosure*)w->value; // the real callback a = hl_alloc_array(&hlt_dyn,tfun->nargs); for(i=0;inargs;i++) { hl_type *t = tfun->args[i]; void *v = hl_is_ptr(t) ? args + i : args[i]; hl_aptr(a,void*)[i] = hl_make_dyn(v,t); } if( w->hasValue ) vargs[p++] = (vdynamic*)w->value; vargs[p++] = (vdynamic*)a; } else { if( w->hasValue ) vargs[p++] = (vdynamic*)w->value; for(i=0;it->fun->nargs;i++) { hl_type *t = tfun->args[i]; hl_type *to = w->t->fun->args[i]; void *v = hl_is_ptr(t) ? args + i : args[i]; switch( to->kind ) { case HUI8: case HUI16: case HI32: case HBOOL: tmp[i].i = hl_dyn_casti(v,t,to); v = &tmp[i].i; break; case HI64: tmp[i].i64 = hl_dyn_casti64(v,t); v = &tmp[i].i64; break; case HF32: tmp[i].f = hl_dyn_castf(v,t); v = &tmp[i].f; break; case HF64: tmp[i].d = hl_dyn_castd(v,t); v = &tmp[i].d; break; default: v = hl_dyn_castp(v,t,to); break; } vargs[p++] = v; } } pret = hlc_static_call(hlc_call_flags & 1 ? &w->fun : w->fun,w->hasValue ? w->t->fun->parent : w->t,vargs,ret); aret = hl_is_ptr(w->t->fun->ret) ? &pret : pret; if( aret == NULL ) aret = &pret; switch( tfun->ret->kind ) { case HVOID: return NULL; case HUI8: case HUI16: case HI32: case HBOOL: ret->v.i = hl_dyn_casti(aret,w->t->fun->ret,tfun->ret); break; case HI64: ret->v.i64 = hl_dyn_casti64(aret,w->t->fun->ret); break; case HF32: ret->v.f = hl_dyn_castf(aret,w->t->fun->ret); break; case HF64: ret->v.d = hl_dyn_castd(aret,w->t->fun->ret); break; default: pret = hl_dyn_castp(aret,w->t->fun->ret,tfun->ret); break; } return pret; } HL_PRIM void *hl_dyn_call_obj( vdynamic *o, hl_type *ft, int hfield, void **args, vdynamic *ret ) { switch( o->t->kind ) { case HDYNOBJ: { vdynobj *d = (vdynobj*)o; hl_field_lookup *l = hl_lookup_find(d->lookup,d->nfields, hfield); if( l != NULL && l->t->kind != HFUN ) hl_error("Field %s is of type %s and cannot be called", hl_field_name(hfield), hl_type_str(l->t)); vclosure *tmp = (vclosure*)d->values[l->field_index]; if( tmp ) { vclosure_wrapper w; w.cl.t = ft; w.cl.fun = hlc_get_wrapper(ft); w.cl.hasValue = 2; # ifdef HL_64 w.cl.stackCount = 0; # endif w.cl.value = &w; w.wrappedFun = tmp; return hl_wrapper_call(&w,args,ret); } hl_error("%s has no method %s",hl_type_str(o->t),hl_field_name(hfield)); } break; case HOBJ: { hl_runtime_obj *rt = o->t->obj->rt; while( true ) { hl_field_lookup *l = hl_lookup_find(rt->lookup,rt->nlookup, hfield); if( l != NULL && l->t->kind != HFUN ) hl_error("Field %s is of type %s and cannot be called", hl_field_name(hfield), hl_type_str(l->t)); if( l != NULL ) { vclosure_wrapper w; vclosure tmp; w.cl.t = ft; w.cl.fun = hlc_get_wrapper(ft); w.cl.hasValue = 2; # ifdef HL_64 w.cl.stackCount = 0; # endif w.cl.value = &w; if( l->field_index < 0 ) { tmp.t = hl_get_closure_type(l->t); tmp.fun = o->t->obj->rt->methods[-l->field_index-1]; tmp.hasValue = 1; # ifdef HL_64 tmp.stackCount = 0; # endif tmp.value = o; w.wrappedFun = &tmp; } else { vclosure *tmp = *(vclosure**)((char*)o + l->field_index); if( !tmp ) break; w.wrappedFun = tmp; } return hl_wrapper_call(&w,args,ret); } rt = rt->parent; if( rt == NULL ) break; } hl_error("%s has no method %s",o->t->obj->name,hl_field_name(hfield)); } break; default: hl_error("Invalid field access"); break; } return NULL; } HL_PRIM vclosure *hl_make_fun_wrapper( vclosure *v, hl_type *to ) { vclosure_wrapper *c; void *wrap = hlc_get_wrapper(to); if( wrap == NULL ) return NULL; if( v->fun != fun_var_args && v->t->fun->nargs != to->fun->nargs ) return NULL; c = (vclosure_wrapper*)hl_gc_alloc(to, sizeof(vclosure_wrapper)); c->cl.t = to; c->cl.fun = wrap; c->cl.hasValue = 2; # ifdef HL_64 c->cl.stackCount = 0; # endif c->cl.value = c; c->wrappedFun = v; return (vclosure*)c; } static hl_type hlt_var_args = { HFUN }; static hl_type_fun hlt_var_fun = { NULL, &hlt_void, -1, &hlt_var_args, { HFUN, NULL }, { NULL, &hlt_void, -1, &hlt_var_args} }; HL_PRIM vdynamic *hl_make_var_args( vclosure *c ) { hlt_var_args.fun = &hlt_var_fun; hlt_var_fun.closure_type.p = &hlt_var_fun; return (vdynamic*)hl_alloc_closure_ptr(&hlt_var_args,fun_var_args,c); } HL_PRIM void hl_prim_not_loaded() { hl_error("Primitive or library is missing"); } HL_PRIM bool hl_is_prim_loaded( vdynamic *f ) { return f && f->t->kind == HFUN && ((vclosure*)f)->fun != hl_prim_not_loaded; } DEFINE_PRIM(_DYN, no_closure, _DYN); DEFINE_PRIM(_DYN, make_closure, _DYN _DYN); DEFINE_PRIM(_DYN, get_closure_value, _DYN); DEFINE_PRIM(_BOOL, fun_compare, _DYN _DYN); DEFINE_PRIM(_DYN, make_var_args, _FUN(_DYN,_ARR)); DEFINE_PRIM(_DYN, call_method, _DYN _ARR); DEFINE_PRIM(_BOOL, is_prim_loaded, _DYN); #if defined(HL_VCC) && !defined(HL_XBO) static LONG CALLBACK global_handler( PEXCEPTION_POINTERS inf ) { switch( inf->ExceptionRecord->ExceptionCode ) { case EXCEPTION_ACCESS_VIOLATION: hl_error("Access violation"); case EXCEPTION_STACK_OVERFLOW: hl_error("Stack overflow"); default: break; } return EXCEPTION_CONTINUE_SEARCH; } #endif HL_PRIM vdynamic *hl_dyn_call_safe( vclosure *c, vdynamic **args, int nargs, bool *isException ) { hl_trap_ctx trap; vdynamic *ret, *exc; *isException = false; hl_trap(trap, exc, on_exception); # if defined(HL_VCC) && !defined(HL_XBO) ULONG size = 32<<10; SetThreadStackGuarantee(&size); static bool first = true; if( first && !hl_detect_debugger() ) { first = false; AddVectoredExceptionHandler(1,global_handler); } # endif ret = hl_dyn_call(c,args,nargs); hl_endtrap(trap); return ret; on_exception: hl_endtrap(trap); *isException = true; return exc; }