/* * 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.h" #include HL_PRIM hl_field_lookup *hl_lookup_insert( hl_field_lookup *l, int size, int hash, hl_type *t, int index ) { int min = 0; int max = size; int pos; while( min < max ) { int mid = (min + max) >> 1; int h = l[mid].hashed_name; if( h < hash ) min = mid + 1; else max = mid; } pos = (min + max) >> 1; memmove(l + pos + 1, l + pos, (size - pos) * sizeof(hl_field_lookup)); l[pos].field_index = index; l[pos].hashed_name = hash; l[pos].t = t; return l + pos; } HL_PRIM hl_field_lookup *hl_lookup_find( hl_field_lookup *l, int size, int hash ) { int min = 0; int max = size; while( min < max ) { int mid = (min + max) >> 1; int h = l[mid].hashed_name; if( h < hash ) min = mid + 1; else if( h > hash ) max = mid; else return l + mid; } return NULL; } static int hl_lookup_find_index( hl_field_lookup *l, int size, int hash ) { int min = 0; int max = size; while( min < max ) { int mid = (min + max) >> 1; int h = l[mid].hashed_name; if( h < hash ) min = mid + 1; else if( h > hash ) max = mid; else return mid; } return (min + max) >> 1; } static hl_field_lookup *obj_resolve_field( hl_type_obj *o, int hfield ) { hl_runtime_obj *rt = o->rt; do { hl_field_lookup *f = hl_lookup_find(rt->lookup,rt->nlookup,hfield); if( f ) return f; rt = rt->parent; } while( rt ); return NULL; } static int hl_cache_count = 0; static int hl_cache_size = 0; static hl_mutex *hl_cache_lock = NULL; static hl_field_lookup *hl_cache = NULL; void hl_cache_init() { # ifdef HL_THREADS hl_add_root(&hl_cache_lock); # endif hl_cache_lock = hl_mutex_alloc(false); } HL_PRIM int hl_hash( vbyte *b ) { return hl_hash_gen((uchar*)b,true); } HL_PRIM int hl_hash_utf8( const char *name ) { int h = 0; // ASCII should be enough while( *name ) { h = 223 * h + (unsigned)*name; name++; } h %= 0x1FFFFF7B; return h; } HL_PRIM int hl_hash_gen( const uchar *name, bool cache_name ) { int h = 0; const uchar *oname = name; while( *name ) { h = 223 * h + (unsigned)*name; name++; } h %= 0x1FFFFF7B; if( cache_name ) { hl_field_lookup *l; hl_mutex_acquire(hl_cache_lock); l = hl_lookup_find(hl_cache, hl_cache_count, h); // check for potential conflict (see haxe#5572) while( l && ucmp((uchar*)l->t,oname) != 0 ) { h++; l = hl_lookup_find(hl_cache, hl_cache_count, h); } if( l == NULL ) { if( hl_cache_size == hl_cache_count ) { // resize int newsize = hl_cache_size ? (hl_cache_size * 3) >> 1 : 16; hl_field_lookup *cache = (hl_field_lookup*)malloc(sizeof(hl_field_lookup) * newsize); memcpy(cache,hl_cache,sizeof(hl_field_lookup) * hl_cache_count); free(hl_cache); hl_cache = cache; hl_cache_size = newsize; } hl_lookup_insert(hl_cache,hl_cache_count++,h,(hl_type*)ustrdup(oname),0); } hl_mutex_release(hl_cache_lock); } return h; } HL_PRIM vbyte *hl_field_name( int hash ) { hl_field_lookup *l = hl_lookup_find(hl_cache, hl_cache_count, hash); return l ? (vbyte*)l->t : (vbyte*)USTR("???"); } HL_PRIM void hl_cache_free() { int i; for(i=0;ikind != HOBJ && t->kind != HSTRUCT ) return NULL; rt = hl_get_obj_rt(t); if( fid < 0 || fid >= rt->nfields ) return NULL; while( rt->parent && fid < rt->parent->nfields ) rt = rt->parent; return rt->t->obj->fields + (fid - (rt->parent?rt->parent->nfields:0)); } HL_PRIM int hl_mark_size( int data_size ) { int ptr_count = (data_size + HL_WSIZE - 1) / HL_WSIZE; return ((ptr_count + 31) >> 5) * sizeof(int); } /** Builds class metadata (fields indexes, etc.) Does not require the method table to be finalized. **/ HL_PRIM hl_runtime_obj *hl_get_obj_rt( hl_type *ot ) { hl_type_obj *o = ot->obj; hl_module_context *m = o->m; hl_alloc *alloc = &m->alloc; hl_runtime_obj *p = NULL, *t; int i, size, start, nlookup, compareHash; if( o->rt ) return o->rt; if( o->super ) p = hl_get_obj_rt(o->super); hl_global_lock(true); if( o->rt ) { hl_global_lock(false); return o->rt; } t = (hl_runtime_obj*)hl_malloc(alloc,sizeof(hl_runtime_obj)); t->t = ot; t->nfields = o->nfields + (p ? p->nfields : 0); t->nproto = p ? p->nproto : 0; t->nlookup = o->nfields; t->nbindings = p ? p->nbindings : 0; t->hasPtr = p ? p->hasPtr : false; if( !p ) { t->nlookup += o->nproto; t->nbindings += o->nbindings; } else { for(i=0;inproto;i++) { hl_obj_proto *pr = o->proto + i; if( pr->pindex >= 0 && pr->pindex < p->nproto ) continue; t->nlookup++; } for(i=0;inbindings;i++) { int j; int fid = o->bindings[i<<1]; bool found = false; hl_type_obj *pp = p ? p->t->obj : NULL; while( pp && !found ) { for(j=0;jnbindings;j++) if( pp->bindings[j<<1] == fid ) { found = true; break; } pp = pp->super ? pp->super->obj : NULL; } if( !found ) t->nbindings++; } } t->lookup = (hl_field_lookup*)hl_malloc(alloc,sizeof(hl_field_lookup) * t->nlookup); t->fields_indexes = (int*)hl_malloc(alloc,sizeof(int)*t->nfields); t->bindings = (hl_runtime_binding*)hl_malloc(alloc,sizeof(hl_runtime_binding)*t->nbindings); t->toStringFun = NULL; t->compareFun = NULL; t->castFun = NULL; t->getFieldFun = NULL; t->parent = p; // fields indexes start = 0; if( p ) { start = p->nfields; memcpy(t->fields_indexes, p->fields_indexes, sizeof(int)*p->nfields); } size = p ? p->size : (ot->kind == HSTRUCT ? 0 : HL_WSIZE); // hl_type* nlookup = 0; for(i=0;infields;i++) { hl_type *ft = o->fields[i].t; hl_type *pad = ft; while( pad->kind == HPACKED ) { // align on first field pad = pad->tparam; while( pad->obj->super && hl_get_obj_rt(pad->obj->super)->nfields ) pad = pad->obj->super; pad = pad->obj->fields[0].t; } size += hl_pad_struct(size,pad); t->fields_indexes[i+start] = size; if( *o->fields[i].name ) hl_lookup_insert(t->lookup,nlookup++,o->fields[i].hashed_name,o->fields[i].t,size); else t->nlookup--; if( ft->kind == HPACKED ) { hl_runtime_obj *rts = hl_get_obj_rt(ft->tparam); size += rts->size; if( rts->hasPtr ) t->hasPtr = true; continue; } size += hl_type_size(ft); if( !t->hasPtr && hl_is_ptr(ft) ) t->hasPtr = true; } t->size = size; t->nmethods = p ? p->nmethods : o->nproto; t->methods = NULL; o->rt = t; ot->vobj_proto = NULL; // fields lookup compareHash = hl_hash_gen(USTR("__compare"),false); for(i=0;inproto;i++) { hl_obj_proto *pr = o->proto + i; hl_type *mt; int method_index; if( p ) { if( pr->pindex >= 0 && pr->pindex < p->nproto ) continue; method_index = t->nmethods++; } else method_index = i; if( pr->pindex >= t->nproto ) t->nproto = pr->pindex + 1; mt = m->functions_types[pr->findex]; hl_lookup_insert(t->lookup,nlookup++,pr->hashed_name,mt,-(method_index+1)); // tell if we have a compare fun (req for JIT) if( pr->hashed_name == compareHash && mt->fun->nargs == 2 && mt->fun->args[1]->kind == HDYN && mt->fun->ret->kind == HI32 ) t->compareFun = (void*)(int_val)pr->findex; } // mark bits if( t->hasPtr ) { unsigned int *mark = (unsigned int*)hl_zalloc(alloc,hl_mark_size(t->size)); ot->mark_bits = mark; if( p && p->t->mark_bits ) memcpy(mark, p->t->mark_bits, hl_mark_size(p->size)); for(i=0;infields;i++) { hl_type *ft = o->fields[i].t; if( hl_is_ptr(ft) ) { int pos = t->fields_indexes[i + start] / HL_WSIZE; if( ft->kind == HPACKED ) { hl_runtime_obj *rts = hl_get_obj_rt(ft->tparam); if( rts->t->mark_bits ) memcpy(mark + (pos>>5), rts->t->mark_bits, hl_mark_size(rts->size)); continue; } mark[pos >> 5] |= 1 << (pos & 31); } } } hl_global_lock(false); return t; } /** Fill class prototype with method pointers. Requires method table to be finalized **/ HL_API hl_runtime_obj *hl_get_obj_proto( hl_type *ot ) { hl_type_obj *o = ot->obj; hl_module_context *m = o->m; hl_alloc *alloc = &m->alloc; hl_runtime_obj *p = NULL, *t = hl_get_obj_rt(ot); hl_field_lookup *strField, *cmpField, *castField, *getField; int i; int nmethods, nbindings; if( ot->vobj_proto ) return t; if( o->super ) p = hl_get_obj_proto(o->super); hl_global_lock(true); if( ot->vobj_proto ) { hl_global_lock(false); return t; } if( t->nproto ) { void **fptr = (void**)hl_malloc(alloc, sizeof(void*) * t->nproto); ot->vobj_proto = fptr; if( p ) memcpy(fptr, p->t->vobj_proto, p->nproto * sizeof(void*)); for(i=0;inproto;i++) { hl_obj_proto *p = o->proto + i; if( p->pindex >= 0 ) fptr[p->pindex] = m->functions_ptrs[p->findex]; } } else ot->vobj_proto = (void*)1; t->methods = (void**)hl_malloc(alloc, sizeof(void*) * t->nmethods); if( p ) memcpy(t->methods,p->methods,p->nmethods * sizeof(void*)); nmethods = p ? p->nmethods : 0; for(i=0;inproto;i++) { hl_obj_proto *pr = o->proto + i; int method_index; if( p ) { if( pr->pindex >= 0 && pr->pindex < p->nproto ) method_index = -obj_resolve_field(o->super->obj,pr->hashed_name)->field_index-1; else method_index = nmethods++; } else method_index = i; t->methods[method_index] = m->functions_ptrs[pr->findex]; } // interfaces t->ninterfaces = 0; for(i=0;infields;i++) { if( o->fields[i].hashed_name == 0 ) t->ninterfaces++; } t->interfaces = (int*)hl_malloc(alloc,sizeof(int) * t->ninterfaces); t->ninterfaces = 0; for(i=0;infields;i++) { if( o->fields[i].hashed_name == 0 ) t->interfaces[t->ninterfaces++] = i; } // bindings if( p ) { nbindings = p->nbindings; memcpy(t->bindings,p->bindings,p->nbindings*sizeof(hl_runtime_binding)); } else nbindings = 0; for(i=0;inbindings;i++) { int fid = o->bindings[i<<1]; int mid = o->bindings[(i<<1)|1]; hl_runtime_binding *b = NULL; hl_type *ft; if( p ) { int j; for(j=0;jnbindings;j++) if( p->bindings[j].fid == fid ) { b = t->bindings + j; break; } } if( b == NULL ) b = t->bindings + nbindings++; b->fid = fid; ft = hl_obj_field_fetch(t->t, fid)->t; switch( ft->kind ) { case HFUN: if( ft->fun->nargs == m->functions_types[mid]->fun->nargs ) { // static fun vclosure *c = (vclosure*)hl_malloc(alloc,sizeof(vclosure)); c->fun = m->functions_ptrs[mid]; c->t = m->functions_types[mid]; c->hasValue = false; c->value = NULL; b->closure = NULL; b->ptr = c; break; } // fallthrough case HDYN: // __constructor__ is defined as dynamic in Class b->closure = m->functions_types[mid]; b->ptr = m->functions_ptrs[mid]; break; default: hl_fatal("invalid bind field"); break; } } strField = obj_resolve_field(o,hl_hash_gen(USTR("__string"),false)); cmpField = obj_resolve_field(o,hl_hash_gen(USTR("__compare"),false)); castField = obj_resolve_field(o,hl_hash_gen(USTR("__cast"),false)); getField = obj_resolve_field(o,hl_hash_gen(USTR("__get_field"),false)); t->toStringFun = strField ? t->methods[-(strField->field_index+1)] : NULL; t->compareFun = cmpField && t->compareFun ? t->methods[-(cmpField->field_index+1)] : NULL; t->castFun = castField ? t->methods[-(castField->field_index+1)] : NULL; t->getFieldFun = getField ? t->methods[-(getField->field_index+1)] : NULL; if( p && !t->getFieldFun ) t->getFieldFun = p->getFieldFun; hl_global_lock(false); return t; } HL_API void hl_flush_proto( hl_type *ot ) { int i; hl_type_obj *o = ot->obj; hl_runtime_obj *rt = ot->obj->rt; hl_module_context *m = o->m; if( !rt || !ot->vobj_proto ) return; for(i=0;inbindings;i++) { int fid = o->bindings[i<<1]; int mid = o->bindings[(i<<1)|1]; hl_runtime_binding *b = NULL; int j; for(j=0;jnbindings;j++) if( rt->bindings[j].fid == fid ) { b = rt->bindings + j; break; } void *ptr = m->functions_ptrs[mid]; if( b->closure ) { if( b->ptr != ptr ) b->ptr = ptr; } else { vclosure *c = (vclosure*)b->ptr; if( c->fun != ptr ) c->fun = ptr; } } } HL_API void hl_init_virtual( hl_type *vt, hl_module_context *ctx ) { int i; int vsize = sizeof(vvirtual) + sizeof(void*) * vt->virt->nfields; int size = vsize; hl_field_lookup *l = (hl_field_lookup*)hl_malloc(&ctx->alloc,sizeof(hl_field_lookup)*vt->virt->nfields); int *indexes = (int*)hl_malloc(&ctx->alloc,sizeof(int)*vt->virt->nfields); unsigned int *mark; for(i=0;ivirt->nfields;i++) { hl_obj_field *f = vt->virt->fields + i; hl_lookup_insert(l,i,f->hashed_name,f->t,i); size += hl_pad_struct(size, f->t); indexes[i] = size; size += hl_type_size(f->t); } vt->virt->lookup = l; vt->virt->indexes = indexes; vt->virt->dataSize = size - vsize; mark = (unsigned int*)hl_zalloc(&ctx->alloc, hl_mark_size(size)); vt->mark_bits = mark; mark[0] = 2 | 4; // value | next for(i=0;ivirt->nfields;i++) { hl_obj_field *f = vt->virt->fields + i; if( hl_is_ptr(f->t) ) { int pos = indexes[i] / HL_WSIZE; mark[pos >> 5] |= 1 << (pos & 31); } } } #define hl_dynobj_field(o,f) (hl_is_ptr((f)->t) ? (void*)((o)->values + (f)->field_index) : (void*) ((o)->raw_data + (f)->field_index)) vdynamic *hl_virtual_make_value( vvirtual *v ) { vdynobj *o; int i, nfields; int raw_size = 0, nvalues = 0; if( v->value ) return v->value; nfields = v->t->virt->nfields; o = hl_alloc_dynobj(); // copy the lookup table o->lookup = (hl_field_lookup*)hl_gc_alloc_noptr(sizeof(hl_field_lookup) * nfields); o->nfields = nfields; memcpy(o->lookup,v->t->virt->lookup,nfields * sizeof(hl_field_lookup)); for(i=0;ilookup + i; if( hl_is_ptr(f->t) ) f->field_index = nvalues++; else { raw_size += hl_pad_size(raw_size, f->t); f->field_index = raw_size; raw_size += hl_type_size(f->t); } } // copy the data & rebind virtual addresses o->raw_data = hl_gc_alloc_noptr(raw_size); o->raw_size = raw_size; o->values = hl_gc_alloc_raw(nvalues*sizeof(void*)); o->nvalues = nvalues; for(i=0;ilookup + i; hl_field_lookup *vf = v->t->virt->lookup + i; void **vaddr = hl_vfields(v) + vf->field_index; memcpy(hl_dynobj_field(o,f),*vaddr, hl_type_size(f->t)); *vaddr = hl_dynobj_field(o,f); } // erase virtual data memset(hl_vfields(v) + nfields, 0, v->t->virt->dataSize); o->virtuals = v; v->value = (vdynamic*)o; return v->value; } static bool should_recast( hl_type *t, hl_type *vt ) { if( vt->kind == HF64 && t->kind == HI32 ) return true; if( vt->kind == HNULL && vt->tparam->kind == t->kind ) return true; if( vt->kind == HNULL && vt->tparam->kind == HF64 && t->kind == HI32 ) return true; if( vt->kind == HVIRTUAL && t->kind == HDYNOBJ ) return true; if( vt->kind == HOBJ && t->kind == HOBJ && vt->obj->rt->castFun ) return true; return false; } /** Allocate a virtual fields mapping to a given value. **/ vvirtual *hl_to_virtual( hl_type *vt, vdynamic *obj ) { vvirtual *v = NULL; if( obj == NULL ) return NULL; #ifdef _DEBUG if( vt->virt->nfields && vt->virt->lookup == NULL ) hl_fatal("virtual not initialized"); #endif switch( obj->t->kind ) { case HOBJ: { int i; void **interface_address = NULL; { hl_runtime_obj *rt = obj->t->obj->rt; while( rt ) { for(i=0;ininterfaces;i++) { if( rt->t->obj->fields[rt->interfaces[i]].t == vt ) { int start = rt->parent ? rt->parent->nfields : 0; interface_address = (void**)((char*)obj + rt->fields_indexes[rt->interfaces[i]+start]); break; } } rt = rt->parent; } } if( interface_address ) { v = (vvirtual*)*interface_address; if( v ) return v; } v = (vvirtual*)hl_gc_alloc(vt, sizeof(vvirtual) + sizeof(void*)*vt->virt->nfields); v->t = vt; v->value = obj; v->next = NULL; for(i=0;ivirt->nfields;i++) { hl_field_lookup *f = obj_resolve_field(obj->t->obj,vt->virt->fields[i].hashed_name); if( f && f->field_index < 0 ) { hl_type *ft = vt->virt->fields[i].t; hl_type tmp; hl_type_fun tf; tmp.kind = HMETHOD; tmp.fun = &tf; tf.args = f->t->fun->args + 1; tf.nargs = f->t->fun->nargs - 1; tf.ret = f->t->fun->ret; if( hl_safe_cast(&tmp,ft) ) hl_vfields(v)[i] = obj->t->obj->rt->methods[-f->field_index-1]; else hl_vfields(v)[i] = NULL; } else hl_vfields(v)[i] = f == NULL || !hl_same_type(f->t,vt->virt->fields[i].t) ? NULL : (char*)obj + f->field_index; } if( interface_address ) *interface_address = v; } break; case HDYNOBJ: { int i; int64 need_recast = 0; vdynobj *o = (vdynobj*)obj; v = o->virtuals; while( v ) { if( v->t->virt == vt->virt ) return v; v = v->next; } // allocate a new virtual mapping v = (vvirtual*)hl_gc_alloc(vt, sizeof(vvirtual) + sizeof(void*) * vt->virt->nfields); v->t = vt; v->value = obj; for(i=0;ivirt->nfields;i++) { hl_field_lookup *f = hl_lookup_find(o->lookup,o->nfields,vt->virt->fields[i].hashed_name); hl_type *vft = vt->virt->fields[i].t; void *addr = f == NULL || !hl_same_type(f->t,vft) ? NULL : hl_dynobj_field(o,f); // check if we will perform recast of some fields to match the virtual definition // recast will not work for >64 fields, but this should be pretty rare if( addr == NULL && f && !o->virtuals && should_recast(f->t,vft) ) need_recast |= ((int64)1) << ((int64)i); hl_vfields(v)[i] = addr; } // add it to the list v->next = o->virtuals; o->virtuals = v; // recast if( need_recast ) { bool extra_check = vt->virt->nfields > 63; for(i=0;ivirt->nfields;i++) if( need_recast & (((int64)1) << ((int64)i)) ) { hl_obj_field *f = vt->virt->fields + i; if( extra_check && hl_lookup_find(o->lookup,o->nfields,f->hashed_name) == NULL ) continue; if( hl_is_ptr(f->t) ) hl_dyn_setp(obj,f->hashed_name,f->t,hl_dyn_getp(obj,f->hashed_name,f->t)); else if( f->t->kind == HF64 ) hl_dyn_setd(obj,f->hashed_name,hl_dyn_getd(obj,f->hashed_name)); } } } break; case HVIRTUAL: if( hl_safe_cast(obj->t, vt) ) return (vvirtual*)obj; return hl_to_virtual(vt,hl_virtual_make_value((vvirtual*)obj)); default: hl_error("Can't cast %s to %s", hl_type_str(obj->t), hl_type_str(vt)); break; } return v; } static void hl_dynobj_remap_virtuals( vdynobj *o, hl_field_lookup *f, int_val address_offset ) { vvirtual *v = o->virtuals; bool is_ptr = hl_is_ptr(f->t); while( v ) { hl_field_lookup *vf = hl_lookup_find(v->t->virt->lookup,v->t->virt->nfields,f->hashed_name); int i; if( address_offset ) for(i=0;it->virt->nfields;i++) if( hl_vfields(v)[i] && hl_is_ptr(v->t->virt->fields[i].t) == is_ptr ) ((char**)hl_vfields(v))[i] += address_offset; if( vf ) hl_vfields(v)[vf->field_index] = hl_same_type(vf->t,f->t) ? hl_dynobj_field(o, f) : NULL; v = v->next; } } static void hl_dynobj_delete_field( vdynobj *o, hl_field_lookup *f ) { int i; int index = f->field_index; bool is_ptr = hl_is_ptr(f->t); // erase data if( is_ptr ) { memmove(o->values + index, o->values + index + 1, (o->nvalues - (index + 1)) * sizeof(void*)); o->nvalues--; o->values[o->nvalues] = NULL; for(i=0;infields;i++) { hl_field_lookup *f = o->lookup + i; if( hl_is_ptr(f->t) && f->field_index > index ) f->field_index--; } } else { // no erase needed, compaction will be performed on next add } // remove from virtuals vvirtual *v = o->virtuals; while( v ) { hl_field_lookup *vf = hl_lookup_find(v->t->virt->lookup,v->t->virt->nfields,f->hashed_name); if( vf ) hl_vfields(v)[vf->field_index] = NULL; // remap pointers that were moved if( is_ptr ) { for(i=0;it->virt->nfields;i++) { vf = v->t->virt->lookup + i; if( hl_is_ptr(vf->t) ) { void ***pf = (void***)hl_vfields(v) + vf->field_index; if( *pf && *pf > (void**)(o->values + index) ) *pf = (*pf) - 1; } } } v = v->next; } // remove from lookup int field = (int)(f - o->lookup); memmove(o->lookup + field, o->lookup + field + 1, (o->nfields - (field + 1)) * sizeof(hl_field_lookup)); o->nfields--; } static hl_field_lookup *hl_dynobj_add_field( vdynobj *o, int hfield, hl_type *t ) { int index; int_val address_offset; // expand data if( hl_is_ptr(t) ) { void **nvalues = hl_gc_alloc_raw( (o->nvalues + 1) * sizeof(void*) ); memcpy(nvalues,o->values,o->nvalues * sizeof(void*)); index = o->nvalues; nvalues[index] = NULL; address_offset = (char*)nvalues - (char*)o->values; o->values = nvalues; o->nvalues++; } else { int raw_size = 0; int i; for(i=0;infields;i++) { hl_field_lookup *f = o->lookup + i; if( hl_is_ptr(f->t) ) continue; raw_size += hl_pad_size(raw_size, f->t); raw_size += hl_type_size(f->t); } if( raw_size > o->raw_size ) // our current mapping is better raw_size = o->raw_size; int pad = hl_pad_size(raw_size, t); int size = hl_type_size(t); char *newData = (char*)hl_gc_alloc_noptr(raw_size + pad + size); if( raw_size == o->raw_size ) memcpy(newData,o->raw_data,o->raw_size); else { raw_size = 0; for(i=0;infields;i++) { hl_field_lookup *f = o->lookup + i; int index = f->field_index; if( hl_is_ptr(f->t) ) continue; raw_size += hl_pad_size(raw_size, f->t); memcpy(newData + raw_size, o->raw_data + index, hl_type_size(f->t)); f->field_index = raw_size; if( index != raw_size ) hl_dynobj_remap_virtuals(o, f, 0); raw_size += hl_type_size(f->t); } o->raw_size = raw_size; } address_offset = newData - o->raw_data; o->raw_data = newData; o->raw_size += pad; index = o->raw_size; o->raw_size += size; } // update field table hl_field_lookup *new_lookup = (hl_field_lookup*)hl_gc_alloc_noptr(sizeof(hl_field_lookup) * (o->nfields + 1)); int field_pos = hl_lookup_find_index(o->lookup, o->nfields, hfield); memcpy(new_lookup,o->lookup,field_pos * sizeof(hl_field_lookup)); hl_field_lookup *f = new_lookup + field_pos; f->t = t; f->hashed_name = hfield; f->field_index = index; memcpy(new_lookup + (field_pos + 1),o->lookup + field_pos, (o->nfields - field_pos) * sizeof(hl_field_lookup)); o->nfields++; o->lookup = new_lookup; hl_dynobj_remap_virtuals(o, f, address_offset); return f; } // -------------------- DYNAMIC GET ------------------------------------ static void *hl_obj_lookup( vdynamic *d, int hfield, hl_type **t ) { switch( d->t->kind ) { case HDYNOBJ: { vdynobj *o = (vdynobj*)d; hl_field_lookup *f = hl_lookup_find(o->lookup,o->nfields,hfield); if( f == NULL ) return NULL; *t = f->t; return hl_dynobj_field(o,f); } break; case HOBJ: { hl_field_lookup *f = obj_resolve_field(d->t->obj,hfield); if( f == NULL || f->field_index < 0 ) return NULL; *t = f->t; return (char*)d + f->field_index; } break; case HSTRUCT: { hl_field_lookup *f = obj_resolve_field(d->t->obj,hfield); if( f == NULL || f->field_index < 0 ) return NULL; *t = f->t; return (char*)d->v.ptr + f->field_index; } break; case HVIRTUAL: { vdynamic *v = ((vvirtual*)d)->value; hl_field_lookup *f; if( v ) return hl_obj_lookup(v, hfield, t); f = hl_lookup_find(d->t->virt->lookup,d->t->virt->nfields,hfield); if( f == NULL ) return NULL; *t = f->t; return (char*)d + d->t->virt->indexes[f->field_index]; } default: hl_error("Invalid field access"); break; } return NULL; } // fetch method or dynamic field (getField) static vdynamic *hl_obj_lookup_extra( vdynamic *d, int hfield ) { switch( d->t->kind ) { case HOBJ: case HSTRUCT: { hl_field_lookup *f = obj_resolve_field(d->t->obj,hfield); if( f && f->field_index < 0 ) return (vdynamic*)hl_alloc_closure_ptr(f->t,d->t->obj->rt->methods[-f->field_index-1],d); if( f == NULL ) { hl_runtime_obj *rt = d->t->obj->rt; if( rt->getFieldFun ) return rt->getFieldFun(d,hfield); } return NULL; } break; case HVIRTUAL: { vdynamic *v = ((vvirtual*)d)->value; if( v ) return hl_obj_lookup_extra(v, hfield); } break; default: break; } return NULL; } HL_PRIM int hl_dyn_geti( vdynamic *d, int hfield, hl_type *t ) { hl_type *ft; hl_track_call(HL_TRACK_DYNFIELD, on_dynfield(d,hfield)); void *addr = hl_obj_lookup(d,hfield,&ft); if( !addr ) { d = hl_obj_lookup_extra(d,hfield); return d == NULL ? 0 : hl_dyn_casti(&d,&hlt_dyn,t); } switch( ft->kind ) { case HUI8: return *(unsigned char*)addr; case HUI16: return *(unsigned short*)addr; case HI32: return *(int*)addr; case HI64: return (int)*(int64*)addr; case HF32: return (int)*(float*)addr; case HF64: return (int)*(double*)addr; case HBOOL: return *(bool*)addr; default: return hl_dyn_casti(addr,ft,t); } } HL_PRIM int64 hl_dyn_geti64( vdynamic *d, int hfield ) { hl_type *ft; hl_track_call(HL_TRACK_DYNFIELD, on_dynfield(d,hfield)); void *addr = hl_obj_lookup(d,hfield,&ft); if( !addr ) { d = hl_obj_lookup_extra(d,hfield); return d == NULL ? 0 : hl_dyn_casti64(&d,&hlt_dyn); } switch( ft->kind ) { case HUI8: return *(unsigned char*)addr; case HUI16: return *(unsigned short*)addr; case HI32: return *(int*)addr; case HI64: return *(int64*)addr; case HF32: return (int64)*(float*)addr; case HF64: return (int64)*(double*)addr; case HBOOL: return *(bool*)addr; default: return hl_dyn_casti64(addr,ft); } } HL_PRIM float hl_dyn_getf( vdynamic *d, int hfield ) { hl_type *ft; hl_track_call(HL_TRACK_DYNFIELD, on_dynfield(d,hfield)); void *addr = hl_obj_lookup(d,hfield,&ft); if( !addr ) { d = hl_obj_lookup_extra(d,hfield); return d == NULL ? 0.f : hl_dyn_castf(&d,&hlt_dyn); } return ft->kind == HF32 ? *(float*)addr : hl_dyn_castf(addr,ft); } HL_PRIM double hl_dyn_getd( vdynamic *d, int hfield ) { hl_type *ft; hl_track_call(HL_TRACK_DYNFIELD, on_dynfield(d,hfield)); void *addr = hl_obj_lookup(d,hfield,&ft); if( !addr ) { d = hl_obj_lookup_extra(d,hfield); return d == NULL ? 0. : hl_dyn_castd(&d,&hlt_dyn); } return ft->kind == HF64 ? *(double*)addr : hl_dyn_castd(addr,ft); } HL_PRIM void *hl_dyn_getp( vdynamic *d, int hfield, hl_type *t ) { hl_type *ft; hl_track_call(HL_TRACK_DYNFIELD, on_dynfield(d,hfield)); void *addr = hl_obj_lookup(d,hfield,&ft); if( !addr ) { d = hl_obj_lookup_extra(d,hfield); return d == NULL ? NULL : hl_dyn_castp(&d,&hlt_dyn,t); } return hl_same_type(t,ft) ? *(void**)addr : hl_dyn_castp(addr,ft,t); } // -------------------- DYNAMIC SET ------------------------------------ static void *hl_obj_lookup_set( vdynamic *d, int hfield, hl_type *t, hl_type **ft ) { switch( d->t->kind ) { case HDYNOBJ: { vdynobj *o = (vdynobj*)d; hl_field_lookup *f = hl_lookup_find(o->lookup,o->nfields,hfield); if( f == NULL ) f = hl_dynobj_add_field(o,hfield,t); else if( !hl_same_type(t,f->t) ) { if( hl_is_ptr(t) != hl_is_ptr(f->t) || hl_type_size(t) != hl_type_size(f->t) ) { hl_dynobj_delete_field(o, f); f = hl_dynobj_add_field(o,hfield,t); } else { f->t = t; hl_dynobj_remap_virtuals(o,f,0); } } *ft = f->t; return hl_dynobj_field(o,f); } break; case HOBJ: { hl_field_lookup *f = obj_resolve_field(d->t->obj,hfield); if( f == NULL || f->field_index < 0 ) hl_error("%s does not have field %s",d->t->obj->name,hl_field_name(hfield)); *ft = f->t; return (char*)d + f->field_index; } break; case HSTRUCT: { hl_field_lookup *f = obj_resolve_field(d->t->obj,hfield); if( f == NULL || f->field_index < 0 ) hl_error("%s does not have field %s",d->t->obj->name,hl_field_name(hfield)); *ft = f->t; return (char*)d->v.ptr + f->field_index; } break; case HVIRTUAL: { vvirtual *v = (vvirtual*)d; hl_field_lookup *f; if( v->value ) return hl_obj_lookup_set(v->value, hfield, t, ft); f = hl_lookup_find(v->t->virt->lookup,v->t->virt->nfields,hfield); if( f == NULL || !hl_safe_cast(t,f->t) ) return hl_obj_lookup_set(hl_virtual_make_value(v), hfield, t, ft); *ft = f->t; return (char*)v + v->t->virt->indexes[f->field_index]; } default: hl_error("Invalid field access"); break; } return NULL; } HL_PRIM void hl_dyn_seti( vdynamic *d, int hfield, hl_type *t, int value ) { hl_type *ft = NULL; hl_track_call(HL_TRACK_DYNFIELD, on_dynfield(d,hfield)); void *addr = hl_obj_lookup_set(d,hfield,t,&ft); switch( ft->kind ) { case HUI8: *(unsigned char*)addr = (unsigned char)value; break; case HUI16: *(unsigned short*)addr = (unsigned short)value; break; case HI32: *(int*)addr = value; break; case HI64: *(int64*)addr = value; break; case HBOOL: *(bool*)addr = value != 0; break; case HF32: *(float*)addr = (float)value; break; case HF64: *(double*)addr = value; break; default: { vdynamic tmp; tmp.t = t; tmp.v.i = value; hl_write_dyn(addr,ft,&tmp,true); } break; } } HL_PRIM void hl_dyn_seti64( vdynamic *d, int hfield, int64 value ) { hl_type *ft = NULL; hl_track_call(HL_TRACK_DYNFIELD, on_dynfield(d,hfield)); void *addr = hl_obj_lookup_set(d,hfield,&hlt_i64,&ft); switch( ft->kind ) { case HUI8: *(unsigned char*)addr = (unsigned char)value; break; case HUI16: *(unsigned short*)addr = (unsigned short)value; break; case HI32: *(int*)addr = (int)value; break; case HI64: *(int64*)addr = value; break; case HBOOL: *(bool*)addr = value != 0; break; case HF32: *(float*)addr = (float)value; break; case HF64: *(double*)addr = (double)value; break; default: { vdynamic tmp; tmp.t = &hlt_i64; tmp.v.i64 = value; hl_write_dyn(addr,ft,&tmp,true); } break; } } HL_PRIM void hl_dyn_setf( vdynamic *d, int hfield, float value ) { hl_type *t = NULL; hl_track_call(HL_TRACK_DYNFIELD, on_dynfield(d,hfield)); void *addr = hl_obj_lookup_set(d,hfield,&hlt_f32,&t); if( t->kind == HF32 ) *(float*)addr = value; else { vdynamic tmp; tmp.t = &hlt_f32; tmp.v.f = value; hl_write_dyn(addr,t,&tmp,true); } } HL_PRIM void hl_dyn_setd( vdynamic *d, int hfield, double value ) { hl_type *t = NULL; hl_track_call(HL_TRACK_DYNFIELD, on_dynfield(d,hfield)); void *addr = hl_obj_lookup_set(d,hfield,&hlt_f64,&t); if( t->kind == HF64 ) *(double*)addr = value; else { vdynamic tmp; tmp.t = &hlt_f64; tmp.v.d = value; hl_write_dyn(addr,t,&tmp,true); } } HL_PRIM void hl_dyn_setp( vdynamic *d, int hfield, hl_type *t, void *value ) { hl_type *ft = NULL; hl_track_call(HL_TRACK_DYNFIELD, on_dynfield(d,hfield)); void *addr = hl_obj_lookup_set(d,hfield,t,&ft); if( hl_same_type(t,ft) || (hl_is_ptr(ft) && value == NULL) ) *(void**)addr = value; else if( hl_is_dynamic(t) ) hl_write_dyn(addr,ft,(vdynamic*)value,false); else { vdynamic tmp; tmp.t = t; tmp.v.ptr = value; hl_write_dyn(addr,ft,&tmp, true); } } // -------------------- HAXE API ------------------------------------ HL_PRIM vdynamic *hl_obj_get_field( vdynamic *obj, int hfield ) { if( obj == NULL ) return NULL; switch( obj->t->kind ) { case HOBJ: case HVIRTUAL: case HDYNOBJ: case HSTRUCT: return (vdynamic*)hl_dyn_getp(obj,hfield,&hlt_dyn); default: return NULL; } } HL_PRIM void hl_obj_set_field( vdynamic *obj, int hfield, vdynamic *v ) { if( obj == NULL ) hl_error("Null access"); if( v == NULL ) { hl_dyn_setp(obj,hfield,&hlt_dyn,NULL); return; } hl_track_call(HL_TRACK_DYNFIELD, on_dynfield(obj,hfield)); hl_type *ft = NULL; void *addr = hl_obj_lookup_set(obj,hfield,v->t,&ft); hl_write_dyn(addr,ft,v,false); } HL_PRIM bool hl_obj_has_field( vdynamic *obj, int hfield ) { if( obj == NULL ) return false; switch( obj->t->kind ) { case HOBJ: case HSTRUCT: { hl_field_lookup *l = obj_resolve_field(obj->t->obj, hfield); return l && l->field_index >= 0; } break; case HDYNOBJ: { vdynobj *d = (vdynobj*)obj; hl_field_lookup *f = hl_lookup_find(d->lookup,d->nfields,hfield); return f != NULL; } break; case HVIRTUAL: { vvirtual *v = (vvirtual*)obj; if( v->value ) return hl_obj_has_field(v->value,hfield); return hl_lookup_find(v->t->virt->lookup,v->t->virt->nfields,hfield) != NULL; } default: break; } return false; } HL_PRIM bool hl_obj_delete_field( vdynamic *obj, int hfield ) { if( obj == NULL ) return false; switch( obj->t->kind ) { case HDYNOBJ: { vdynobj *d = (vdynobj*)obj; hl_field_lookup *f = hl_lookup_find(d->lookup,d->nfields,hfield); if( f == NULL ) return false; hl_dynobj_delete_field(d, f); return true; } break; case HVIRTUAL: { vvirtual *v = (vvirtual*)obj; if( v->value ) return hl_obj_delete_field(v->value,hfield); if( hl_lookup_find(v->t->virt->lookup,v->t->virt->nfields,hfield) == NULL ) return false; return hl_obj_delete_field(hl_virtual_make_value(v),hfield); } default: break; } return false; } HL_PRIM varray *hl_obj_fields( vdynamic *obj ) { varray *a = NULL; if( obj == NULL ) return NULL; switch( obj->t->kind ) { case HDYNOBJ: { vdynobj *o = (vdynobj*)obj; int i; a = hl_alloc_array(&hlt_bytes,o->nfields); for(i=0;infields;i++) hl_aptr(a,vbyte*)[i] = (vbyte*)hl_field_name((o->lookup + i)->hashed_name); } break; case HOBJ: case HSTRUCT: { hl_type_obj *tobj = obj->t->obj; hl_runtime_obj *o = tobj->rt; int i, p = 0; a = hl_alloc_array(&hlt_bytes,o->nfields); while( true ) { for(i=0;infields;i++) { hl_obj_field *f = tobj->fields + i; if( !*f->name ) { a->size--; continue; } hl_aptr(a,vbyte*)[p++] = (vbyte*)f->name; } if( tobj->super == NULL ) break; tobj = tobj->super->obj; } } break; case HVIRTUAL: { vvirtual *v = (vvirtual*)obj; int i; if( v->value ) return hl_obj_fields(v->value); a = hl_alloc_array(&hlt_bytes,v->t->virt->nfields); for(i=0;it->virt->nfields;i++) hl_aptr(a,vbyte*)[i] = (vbyte*)v->t->virt->fields[i].name; } break; default: break; } return a; } HL_PRIM vdynamic *hl_obj_copy( vdynamic *obj ) { if( obj == NULL ) return NULL; switch( obj->t->kind ) { case HDYNOBJ: { vdynobj *o = (vdynobj*)obj; vdynobj *c = hl_alloc_dynobj(); int lsize = sizeof(hl_field_lookup) * o->nfields; c->raw_size = o->raw_size; c->nfields = o->nfields; c->nvalues = o->nvalues; c->virtuals = NULL; c->lookup = (hl_field_lookup*)hl_gc_alloc_noptr(lsize); memcpy(c->lookup,o->lookup,lsize); c->raw_data = (char*)hl_gc_alloc_noptr(o->raw_size); c->values = (void**)hl_gc_alloc_raw(o->nvalues * sizeof(void*)); memcpy(c->raw_data,o->raw_data,o->raw_size); memcpy(c->values,o->values,o->nvalues * sizeof(void*)); return (vdynamic*)c; } break; case HVIRTUAL: { vvirtual *v = (vvirtual*)obj; vvirtual *v2; if( v->value ) return hl_obj_copy(v->value); v2 = hl_alloc_virtual(v->t); memcpy(hl_vfields(v2) + v->t->virt->nfields, hl_vfields(v) + v->t->virt->nfields, v->t->virt->dataSize); return (vdynamic*)v2; } default: break; } return NULL; } HL_PRIM vdynamic *hl_get_virtual_value( vdynamic *v ) { return ((vvirtual*)v)->value; } DEFINE_PRIM(_DYN, alloc_obj, _TYPE); DEFINE_PRIM(_DYN, obj_get_field, _DYN _I32); DEFINE_PRIM(_VOID, obj_set_field, _DYN _I32 _DYN); DEFINE_PRIM(_BOOL, obj_has_field, _DYN _I32); DEFINE_PRIM(_BOOL, obj_delete_field, _DYN _I32); DEFINE_PRIM(_ARR, obj_fields, _DYN); DEFINE_PRIM(_DYN, obj_copy, _DYN); DEFINE_PRIM(_DYN, get_virtual_value, _DYN); DEFINE_PRIM(_I32, hash, _BYTES); DEFINE_PRIM(_BYTES, field_name, _I32);