1111 lines
		
	
	
		
			30 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1111 lines
		
	
	
		
			30 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|     Copyright (c) 2016-2018 Chung, Hyung-Hwan. All rights reserved.
 | |
| 
 | |
|     Redistribution and use in source and binary forms, with or without
 | |
|     modification, are permitted provided that the following conditions
 | |
|     are met:
 | |
|     1. Redistributions of source code must retain the above copyright
 | |
|        notice, this list of conditions and the following disclaimer.
 | |
|     2. Redistributions in binary form must reproduce the above copyright
 | |
|        notice, this list of conditions and the following disclaimer in the
 | |
|        documentation and/or other materials provided with the distribution.
 | |
| 
 | |
|     THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
 | |
|     IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 | |
|     OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 | |
|     IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 | |
|     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 | |
|     NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | |
|     DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | |
|     THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | |
|     (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 | |
|     THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | |
|  */
 | |
| 
 | |
| #include "hak-prv.h"
 | |
| 
 | |
| #if defined(HAK_PROFILE_VM)
 | |
| #include <sys/time.h>
 | |
| #include <sys/resource.h> /* getrusage */
 | |
| #endif
 | |
| 
 | |
| void* hak_allocbytes (hak_t* hak, hak_oow_t size)
 | |
| {
 | |
| 	hak_gchdr_t* gch;
 | |
| 	hak_oow_t allocsize;
 | |
| 	int gc_called = 0;
 | |
| #if defined(HAK_PROFILE_VM)
 | |
| 	struct rusage ru;
 | |
| 	hak_ntime_t rut;
 | |
| #endif
 | |
| 
 | |
| #if defined(HAK_BUILD_DEBUG)
 | |
| 	if ((hak->option.trait & HAK_TRAIT_DEBUG_GC) && !(hak->option.trait & HAK_TRAIT_NOGC)) hak_gc(hak, 1);
 | |
| #endif
 | |
| 
 | |
| #if defined(HAK_PROFILE_VM)
 | |
| 	getrusage(RUSAGE_SELF, &ru);
 | |
| 	HAK_INIT_NTIME (&rut,  ru.ru_utime.tv_sec, HAK_USEC_TO_NSEC(ru.ru_utime.tv_usec));
 | |
| #endif
 | |
| 
 | |
| 	allocsize = HAK_SIZEOF(*gch) + size;
 | |
| 
 | |
| 	if (hak->gci.bsz >= hak->gci.threshold)
 | |
| 	{
 | |
| 		hak_gc(hak, 0);
 | |
| 		hak->gci.threshold = hak->gci.bsz + 100000; /* TODO: change this fomula */
 | |
| 		gc_called = 1;
 | |
| 	}
 | |
| 
 | |
| 	if (hak->gci.lazy_sweep) hak_gc_ms_sweep_lazy(hak, allocsize);
 | |
| 
 | |
| 	gch = (hak_gchdr_t*)hak_callocheapmem_noseterr(hak, hak->heap, allocsize);
 | |
| 	if (!gch)
 | |
| 	{
 | |
| 		if (HAK_UNLIKELY(hak->option.trait & HAK_TRAIT_NOGC)) goto calloc_heapmem_fail;
 | |
| 		if (gc_called) goto sweep_the_rest;
 | |
| 
 | |
| 		hak_gc(hak, 0);
 | |
| 		if (hak->gci.lazy_sweep) hak_gc_ms_sweep_lazy(hak, allocsize);
 | |
| 
 | |
| 		gch = (hak_gchdr_t*)hak_callocheapmem_noseterr(hak, hak->heap, allocsize);
 | |
| 		if (HAK_UNLIKELY(!gch))
 | |
| 		{
 | |
| 		sweep_the_rest:
 | |
| 			if (hak->gci.lazy_sweep)
 | |
| 			{
 | |
| 				hak_gc_ms_sweep_lazy(hak, HAK_TYPE_MAX(hak_oow_t)); /* sweep the rest */
 | |
| 				gch = (hak_gchdr_t*)hak_callocheapmem(hak, hak->heap, allocsize);
 | |
| 				if (HAK_UNLIKELY(!gch)) return HAK_NULL;
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 			calloc_heapmem_fail:
 | |
| 				hak_seterrnum(hak, HAK_EOOMEM);
 | |
| 				return HAK_NULL;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (hak->gci.lazy_sweep && hak->gci.ls.curr == hak->gci.b)
 | |
| 	{
 | |
| 		/* if the lazy sweeping point is at the beginning of the allocation block,
 | |
| 		 * hak->gc.ls.prev must get updated */
 | |
| 		HAK_ASSERT(hak, hak->gci.ls.prev == HAK_NULL);
 | |
| 		hak->gci.ls.prev = gch;
 | |
| 	}
 | |
| 
 | |
| 	gch->next = hak->gci.b;
 | |
| 	hak->gci.b = gch;
 | |
| 	hak->gci.bsz += size;
 | |
| 
 | |
| 
 | |
| #if defined(HAK_PROFILE_VM)
 | |
| 	getrusage(RUSAGE_SELF, &ru);
 | |
| 	HAK_SUB_NTIME_SNS (&rut, &rut, ru.ru_utime.tv_sec, HAK_USEC_TO_NSEC(ru.ru_utime.tv_usec));
 | |
| 	HAK_SUB_NTIME (&hak->gci.stat.alloc, &hak->gci.stat.alloc, &rut); /* do subtraction because rut is negative */
 | |
| #endif
 | |
| 	return (hak_uint8_t*)(gch + 1);
 | |
| 
 | |
| }
 | |
| 
 | |
| static HAK_INLINE hak_oop_t alloc_oop_array (hak_t* hak, hak_oow_t size, int ngc)
 | |
| {
 | |
| 	hak_oop_oop_t hdr;
 | |
| 	hak_oow_t nbytes, nbytes_aligned;
 | |
| 
 | |
| 	nbytes = size * HAK_SIZEOF(hak_oop_t);
 | |
| 
 | |
| 	/* this isn't really necessary since nbytes must be
 | |
| 	 * aligned already. */
 | |
| 	nbytes_aligned = HAK_ALIGN(nbytes, HAK_SIZEOF(hak_oop_t));
 | |
| 
 | |
| 	if (HAK_UNLIKELY(ngc))
 | |
| 	{
 | |
| 		hdr = (hak_oop_oop_t)hak_callocmem(hak, HAK_SIZEOF(hak_obj_t) + nbytes_aligned);
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		/* making the number of bytes to allocate a multiple of
 | |
| 		 * HAK_SIZEOF(hak_oop_t) will guarantee the starting address
 | |
| 		 * of the allocated space to be an even number.
 | |
| 		 * see HAK_OOP_IS_NUMERIC() and HAK_OOP_IS_POINTER() */
 | |
| 		hdr = (hak_oop_oop_t)hak_allocbytes(hak, HAK_SIZEOF(hak_obj_t) + nbytes_aligned);
 | |
| 	}
 | |
| 	if (!hdr) return HAK_NULL;
 | |
| 
 | |
| 	hdr->_flags = HAK_OBJ_MAKE_FLAGS(HAK_OBJ_TYPE_OOP, HAK_SIZEOF(hak_oop_t), 0, 0, 0, ngc, 0);
 | |
| 	HAK_OBJ_SET_SIZE (hdr, size);
 | |
| 	/*HAK_OBJ_SET_CLASS (hdr, hak->_nil);*/
 | |
| 
 | |
| 	while (size > 0) hdr->slot[--size] = hak->_nil;
 | |
| 
 | |
| 	return (hak_oop_t)hdr;
 | |
| }
 | |
| 
 | |
| 
 | |
| hak_oop_t hak_allocoopobj (hak_t* hak, hak_oow_t size)
 | |
| {
 | |
| 	return alloc_oop_array(hak, size, 0);
 | |
| }
 | |
| 
 | |
| hak_oop_t hak_allocoopobjwithtrailer (hak_t* hak, hak_oow_t size, const hak_oob_t* bptr, hak_oow_t blen)
 | |
| {
 | |
| 	hak_oop_oop_t hdr;
 | |
| 	hak_oow_t nbytes, nbytes_aligned;
 | |
| 	hak_oow_t i;
 | |
| 
 | |
| 	/* +1 for the trailer size of the hak_oow_t type */
 | |
| 	nbytes = (size + 1) * HAK_SIZEOF(hak_oop_t) + blen;
 | |
| 	nbytes_aligned = HAK_ALIGN(nbytes, HAK_SIZEOF(hak_oop_t));
 | |
| 
 | |
| 	hdr = (hak_oop_oop_t)hak_allocbytes(hak, HAK_SIZEOF(hak_obj_t) + nbytes_aligned);
 | |
| 	if (HAK_UNLIKELY(!hdr)) return HAK_NULL;
 | |
| 
 | |
| 	hdr->_flags = HAK_OBJ_MAKE_FLAGS(HAK_OBJ_TYPE_OOP, HAK_SIZEOF(hak_oop_t), 0, 0, 0, 0, 1);
 | |
| 	HAK_OBJ_SET_SIZE (hdr, size);
 | |
| 	/*HAK_OBJ_SET_CLASS (hdr, hak->_nil);*/
 | |
| 
 | |
| 	for (i = 0; i < size; i++) hdr->slot[i] = hak->_nil;
 | |
| 
 | |
| 	/* [NOTE] this is not converted to a SMOOI object */
 | |
| 	hdr->slot[size] = (hak_oop_t)blen;
 | |
| 
 | |
| 	if (bptr) HAK_MEMCPY(&hdr->slot[size + 1], bptr, blen);
 | |
| 	else HAK_MEMSET(&hdr->slot[size + 1], 0, blen);
 | |
| 
 | |
| 	return (hak_oop_t)hdr;
 | |
| }
 | |
| 
 | |
| static HAK_INLINE hak_oop_t alloc_numeric_array (hak_t* hak, const void* ptr, hak_oow_t len, hak_obj_type_t type, hak_oow_t unit, int extra, int ngc)
 | |
| {
 | |
| 	/* allocate a variable object */
 | |
| 
 | |
| 	hak_oop_t hdr;
 | |
| 	hak_oow_t xbytes;
 | |
| 	hak_oow_t nbytes;
 | |
| 	hak_oow_t nbytes_aligned;
 | |
| 	hak_oow_t nbytes_total;
 | |
| 
 | |
| 	xbytes = len * unit;
 | |
| 	/* 'extra' indicates an extra unit to append at the end.
 | |
| 	 * it's useful to store a string with a terminating null */
 | |
| 	nbytes = extra? xbytes + unit: xbytes;
 | |
| 	nbytes_aligned = HAK_ALIGN(nbytes, HAK_SIZEOF(hak_oop_t));
 | |
| /* TODO: check overflow in size calculation*/
 | |
| 
 | |
| 	nbytes_total = HAK_SIZEOF(hak_obj_t) + nbytes_aligned;
 | |
| 	/* making the number of bytes to allocate a multiple of
 | |
| 	 * HAK_SIZEOF(hak_oop_t) will guarantee the starting address
 | |
| 	 * of the allocated space to be an even number.
 | |
| 	 * see HAK_OOP_IS_NUMERIC() and HAK_OOP_IS_POINTER() */
 | |
| 	if (HAK_UNLIKELY(ngc))
 | |
| 		hdr = (hak_oop_t)hak_callocmem(hak, nbytes_total);
 | |
| 	else
 | |
| 		hdr = (hak_oop_t)hak_allocbytes(hak, nbytes_total);
 | |
| 
 | |
| 	if (HAK_LIKELY(hdr))
 | |
| 	{
 | |
| 		hdr->_flags = HAK_OBJ_MAKE_FLAGS(type, unit, extra, 0, 0, ngc, 0);
 | |
| 		hdr->_size = len;
 | |
| 		HAK_OBJ_SET_SIZE (hdr, len);
 | |
| 		/*HAK_OBJ_SET_CLASS (hdr, hak->_nil);*/
 | |
| 
 | |
| 		if (ptr)
 | |
| 		{
 | |
| 			/* copy data */
 | |
| 			HAK_MEMCPY(hdr + 1, ptr, xbytes);
 | |
| 			HAK_MEMSET((hak_uint8_t*)(hdr + 1) + xbytes, 0, nbytes_aligned - xbytes);
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			/* initialize with zeros when the string pointer is not given */
 | |
| 			HAK_MEMSET(hdr + 1, 0, nbytes_aligned);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return hdr;
 | |
| }
 | |
| 
 | |
| hak_oop_t hak_alloccharobj (hak_t* hak, const hak_ooch_t* ptr, hak_oow_t len)
 | |
| {
 | |
| 	return alloc_numeric_array(hak, ptr, len, HAK_OBJ_TYPE_CHAR, HAK_SIZEOF(hak_ooch_t), 1, 0);
 | |
| }
 | |
| 
 | |
| hak_oop_t hak_allocbyteobj (hak_t* hak, const hak_oob_t* ptr, hak_oow_t len)
 | |
| {
 | |
| 	return alloc_numeric_array(hak, ptr, len, HAK_OBJ_TYPE_BYTE, HAK_SIZEOF(hak_oob_t), 0, 0);
 | |
| }
 | |
| 
 | |
| hak_oop_t hak_allochalfwordobj (hak_t* hak, const hak_oohw_t* ptr, hak_oow_t len)
 | |
| {
 | |
| 	return alloc_numeric_array(hak, ptr, len, HAK_OBJ_TYPE_HALFWORD, HAK_SIZEOF(hak_oohw_t), 0, 0);
 | |
| }
 | |
| 
 | |
| hak_oop_t hak_allocwordobj (hak_t* hak, const hak_oow_t* ptr, hak_oow_t len)
 | |
| {
 | |
| 	return alloc_numeric_array(hak, ptr, len, HAK_OBJ_TYPE_WORD, HAK_SIZEOF(hak_oow_t), 0, 0);
 | |
| }
 | |
| 
 | |
| /* ------------------------------------------------------------------------ *
 | |
|  * COMMON OBJECTS
 | |
|  * ------------------------------------------------------------------------ */
 | |
| 
 | |
| hak_oop_t hak_hatchundef (hak_t* hak)
 | |
| {
 | |
| 	/* create the undef object for bootstrapping.
 | |
| 	 * this function doesn't set the class field */
 | |
| 
 | |
| 	hak_oop_t v;
 | |
| 	v = hak_allocoopobj(hak, 0);
 | |
| 	if (HAK_UNLIKELY(!v))
 | |
| 	{
 | |
| 		const hak_ooch_t* orgmsg = hak_backuperrmsg(hak);
 | |
| 		hak_seterrbfmt(hak, HAK_ERRNUM(hak), "unable to make undef - %js", orgmsg);
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		HAK_OBJ_SET_FLAGS_KERNEL(v, 1);
 | |
| 	}
 | |
| 	return v;
 | |
| }
 | |
| 
 | |
| hak_oop_t hak_hatchnil (hak_t* hak)
 | |
| {
 | |
| 	/* create the nil object for bootstrapping.
 | |
| 	 * this function doesn't set the class field */
 | |
| 
 | |
| 	hak_oop_t v;
 | |
| 	v = hak_allocoopobj(hak, 0);
 | |
| 	if (HAK_UNLIKELY(!v))
 | |
| 	{
 | |
| 		const hak_ooch_t* orgmsg = hak_backuperrmsg(hak);
 | |
| 		hak_seterrbfmt(hak, HAK_ERRNUM(hak), "unable to make nil - %js", orgmsg);
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		HAK_OBJ_SET_FLAGS_KERNEL(v, 1);
 | |
| 	}
 | |
| 	return v;
 | |
| }
 | |
| 
 | |
| hak_oop_t hak_makecons (hak_t* hak, hak_oop_t car, hak_oop_t cdr)
 | |
| {
 | |
| /* TODO: use hak_instantiate() */
 | |
| #if 0
 | |
| 	hak_oop_cons_t cons;
 | |
| 
 | |
| 	hak_pushvolat(hak, &car);
 | |
| 	hak_pushvolat(hak, &cdr);
 | |
| 
 | |
| 	cons = (hak_oop_cons_t)hak_allocoopobj(hak, HAK_BRAND_CONS, 2);
 | |
| 	if (HAK_LIKELY(cons))
 | |
| 	{
 | |
| 		cons->car = car;
 | |
| 		cons->cdr = cdr;
 | |
| 		HAK_OBJ_SET_CLASS (cons, (hak_oop_t)hak->c_cons);
 | |
| 	}
 | |
| 
 | |
| 	hak_popvolats(hak, 2);
 | |
| 
 | |
| 	return (hak_oop_t)cons;
 | |
| #else
 | |
| 	hak_oop_cons_t v;
 | |
| 	hak_pushvolat(hak, &car);
 | |
| 	hak_pushvolat(hak, &cdr);
 | |
| 	v = (hak_oop_cons_t)hak_instantiate(hak, hak->c_cons, HAK_NULL, 0);
 | |
| 	hak_popvolats(hak, 2);
 | |
| 	if (HAK_UNLIKELY(!v))
 | |
| 	{
 | |
| 		const hak_ooch_t* orgmsg = hak_backuperrmsg(hak);
 | |
| 		hak_seterrbfmt(hak, HAK_ERRNUM(hak), "unable to instantiate %O - %js", hak->c_cons->name, orgmsg);
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		v->car = car;
 | |
| 		v->cdr = cdr;
 | |
| 	}
 | |
| 	return (hak_oop_t)v;
 | |
| #endif
 | |
| }
 | |
| 
 | |
| hak_oop_t hak_makearray (hak_t* hak, hak_oow_t len)
 | |
| {
 | |
| #if 0
 | |
| 	hak_oop_t v;
 | |
| 	v = hak_allocoopobj(hak, HAK_BRAND_ARRAY, size);
 | |
| 	if (HAK_LIKELY(v)) HAK_OBJ_SET_CLASS (v, (hak_oop_t)hak->c_array);
 | |
| 	return v;
 | |
| #else
 | |
| 	hak_oop_t v;
 | |
| 	v = hak_instantiate(hak, hak->c_array, HAK_NULL, len);
 | |
| 	if (HAK_UNLIKELY(!v))
 | |
| 	{
 | |
| 		const hak_ooch_t* orgmsg = hak_backuperrmsg(hak);
 | |
| 		hak_seterrbfmt(hak, HAK_ERRNUM(hak),
 | |
| 			"unable to instantiate %O - %js", hak->c_array->name, orgmsg);
 | |
| 	}
 | |
| 	return v;
 | |
| #endif
 | |
| }
 | |
| 
 | |
| hak_oop_t hak_makechararray (hak_t* hak, const hak_ooch_t* ptr, hak_oow_t len)
 | |
| {
 | |
| 	hak_oop_t v;
 | |
| 	v = hak_instantiate(hak, hak->c_character_array, ptr, len);
 | |
| 	if (HAK_UNLIKELY(!v))
 | |
| 	{
 | |
| 		const hak_ooch_t* orgmsg = hak_backuperrmsg(hak);
 | |
| 		hak_seterrbfmt(hak, HAK_ERRNUM(hak),
 | |
| 			"unable to instantiate %O - %js", hak->c_character_array->name, orgmsg);
 | |
| 	}
 | |
| 	return v;
 | |
| }
 | |
| 
 | |
| hak_oop_t hak_makebytearray (hak_t* hak, const hak_oob_t* ptr, hak_oow_t len)
 | |
| {
 | |
| #if 0
 | |
| 	hak_oop_t v;
 | |
| 	v = hak_allocbyteobj(hak, HAK_BRAND_BYTE_ARRAY, ptr, size);
 | |
| 	if (HAK_LIKELY(v)) HAK_OBJ_SET_CLASS (v, (hak_oop_t)hak->c_byte_array);
 | |
| 	return v;
 | |
| #else
 | |
| 	hak_oop_t v;
 | |
| 	v = hak_instantiate(hak, hak->c_byte_array, ptr, len);
 | |
| 	if (HAK_UNLIKELY(!v))
 | |
| 	{
 | |
| 		const hak_ooch_t* orgmsg = hak_backuperrmsg(hak);
 | |
| 		hak_seterrbfmt(hak, HAK_ERRNUM(hak),
 | |
| 			"unable to instantiate %O - %js", hak->c_byte_array->name, orgmsg);
 | |
| 	}
 | |
| 	return v;
 | |
| #endif
 | |
| }
 | |
| 
 | |
| hak_oop_t hak_makebytestringwithbytes (hak_t* hak, const hak_oob_t* ptr, hak_oow_t len)
 | |
| {
 | |
| 	hak_oop_byte_t v;
 | |
| 	v = (hak_oop_byte_t)hak_instantiate(hak, hak->c_byte_string, ptr, len);
 | |
| 	if (HAK_UNLIKELY(!v))
 | |
| 	{
 | |
| 		const hak_ooch_t* orgmsg = hak_backuperrmsg(hak);
 | |
| 		hak_seterrbfmt(hak, HAK_ERRNUM(hak),
 | |
| 			"unable to instantiate %O with bytes - %js", hak->c_byte_string->name, orgmsg);
 | |
| 	}
 | |
| 	return (hak_oop_t)v;
 | |
| }
 | |
| 
 | |
| hak_oop_t hak_makebytestring (hak_t* hak, const hak_ooch_t* ptr, hak_oow_t len)
 | |
| {
 | |
| 	/* a byte string is a byte array with an extra null at the back.
 | |
| 	 * the input to this function, however, is the pointer to hak_ooch_t data
 | |
| 	 * because this function is mainly used to convert a token to a byte string.
 | |
| 	 * the token in the compiler is stored as a hak_ooch_t string. */
 | |
| 
 | |
| 	hak_oop_byte_t v;
 | |
| 
 | |
| 	v = (hak_oop_byte_t)hak_instantiate(hak, hak->c_byte_string, HAK_NULL, len);
 | |
| 	if (HAK_UNLIKELY(!v))
 | |
| 	{
 | |
| 		const hak_ooch_t* orgmsg = hak_backuperrmsg(hak);
 | |
| 		hak_seterrbfmt(hak, HAK_ERRNUM(hak),
 | |
| 			"unable to instantiate %O - %js", hak->c_byte_string->name, orgmsg);
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		hak_oow_t i;
 | |
| 		hak_oob_t b;
 | |
| 		for (i = 0; i < len; i++)
 | |
| 		{
 | |
| 			b = ptr[i] & 0xFF;
 | |
| 			HAK_OBJ_SET_BYTE_VAL(v, i, b);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return (hak_oop_t)v;
 | |
| }
 | |
| 
 | |
| hak_oop_t hak_makestring (hak_t* hak, const hak_ooch_t* ptr, hak_oow_t len)
 | |
| {
 | |
| 	hak_oop_t v;
 | |
| 	v = hak_instantiate(hak, hak->c_string, ptr, len);
 | |
| 	if (HAK_UNLIKELY(!v))
 | |
| 	{
 | |
| 		const hak_ooch_t* orgmsg = hak_backuperrmsg(hak);
 | |
| 		hak_seterrbfmt(hak, HAK_ERRNUM(hak),
 | |
| 			"unable to instantiate %O - %js", hak->c_string->name, orgmsg);
 | |
| 	}
 | |
| 	return v;
 | |
| }
 | |
| 
 | |
| hak_oop_t hak_makestringwithuchars (hak_t* hak, const hak_uch_t* ptr, hak_oow_t len)
 | |
| {
 | |
| 	/* you must provide the payload when calling this variant. it can't figure out
 | |
| 	 * the actual number of hak_ooch_t characters */
 | |
| 	if (!ptr)
 | |
| 	{
 | |
| 		hak_seterrbfmt(hak, HAK_EINVAL,
 | |
| 			"unable to instantiate %O - null payload", hak->c_string->name);
 | |
| 		return HAK_NULL;
 | |
| 	}
 | |
| 
 | |
| #if defined(HAK_OOCH_IS_UCH)
 | |
| 	return hak_makestring(hak, ptr, len);
 | |
| #else
 | |
| 	hak_oow_t xlen;
 | |
| 	hak_ooch_t* xptr;
 | |
| 
 | |
| 	xptr = hak_duputooochars(hak, ptr, len, &xlen);
 | |
| 	if (HAK_UNLIKELY(!xptr))
 | |
| 	{
 | |
| 		const hak_ooch_t* orgmsg = hak_backuperrmsg(hak);
 | |
| 		hak_seterrbfmt(hak, HAK_ERRNUM(hak),
 | |
| 			"unable to instantiate %O - %js", hak->c_string->name, orgmsg);
 | |
| 		return HAK_NULL;
 | |
| 	}
 | |
| 
 | |
| 	return hak_makestring(hak, xptr, xlen);
 | |
| #endif
 | |
| }
 | |
| 
 | |
| hak_oop_t hak_makestringwithbchars (hak_t* hak, const hak_bch_t* ptr, hak_oow_t len)
 | |
| {
 | |
| 	/* you must provide the payload when calling this variant. it can't figure out
 | |
| 	 * the actual number of hak_ooch_t characters */
 | |
| 	if (!ptr)
 | |
| 	{
 | |
| 		hak_seterrbfmt(hak, HAK_EINVAL,
 | |
| 			"unable to instantiate %O - null payload", hak->c_string->name);
 | |
| 		return HAK_NULL;
 | |
| 	}
 | |
| 
 | |
| #if defined(HAK_OOCH_IS_UCH)
 | |
| 	hak_oow_t xlen;
 | |
| 	hak_ooch_t* xptr;
 | |
| 
 | |
| 	xptr = hak_dupbtooochars(hak, ptr, len, &xlen);
 | |
| 	if (HAK_UNLIKELY(!xptr))
 | |
| 	{
 | |
| 		const hak_ooch_t* orgmsg = hak_backuperrmsg(hak);
 | |
| 		hak_seterrbfmt(hak, HAK_ERRNUM(hak),
 | |
| 			"unable to instantiate %O - %js", hak->c_string->name, orgmsg);
 | |
| 		return HAK_NULL;
 | |
| 	}
 | |
| 
 | |
| 	return hak_makestring(hak, xptr, xlen);
 | |
| #else
 | |
| 	return hak_makestring(hak, ptr, len);
 | |
| #endif
 | |
| }
 | |
| 
 | |
| hak_oop_t hak_makefpdec (hak_t* hak, hak_oop_t value, hak_ooi_t scale)
 | |
| {
 | |
| 	hak_oop_fpdec_t f;
 | |
| 
 | |
| 	HAK_ASSERT(hak, hak_isint(hak, value));
 | |
| 
 | |
| 	if (scale <= 0) return value; /* if scale is 0 or less, return the value as it it */
 | |
| 
 | |
| 	if (scale > HAK_SMOOI_MAX)
 | |
| 	{
 | |
| 		hak_seterrbfmt(hak, HAK_EINVAL, "fpdec scale too large - %zd", scale);
 | |
| 		return HAK_NULL;
 | |
| 	}
 | |
| 
 | |
| 	hak_pushvolat(hak, &value);
 | |
| 	f = (hak_oop_fpdec_t)hak_instantiate(hak, hak->c_fixed_point_decimal, HAK_NULL, 0);
 | |
| 	hak_popvolat(hak);
 | |
| 
 | |
| 	if (HAK_UNLIKELY(!f))
 | |
| 	{
 | |
| 		const hak_ooch_t* orgmsg = hak_backuperrmsg(hak);
 | |
| 		hak_seterrbfmt(
 | |
| 			hak, HAK_ERRNUM(hak), "unable to instantiate %O - %js",
 | |
| 			hak->c_fixed_point_decimal->name, orgmsg);
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		f->value = value;
 | |
| 		f->scale = HAK_SMOOI_TO_OOP(scale);
 | |
| 	}
 | |
| 
 | |
| 	return (hak_oop_t)f;
 | |
| }
 | |
| 
 | |
| hak_oop_t hak_makeclass (hak_t* hak, hak_oop_t class_name, hak_oop_t superclass, hak_ooi_t spec, hak_ooi_t selfspec, hak_oop_t ivars_str, hak_oop_t cvars_str)
 | |
| {
 | |
| 	hak_oop_class_t c;
 | |
| 
 | |
| 	hak_pushvolat(hak, &class_name);
 | |
| 	hak_pushvolat(hak, &superclass);
 | |
| 	hak_pushvolat(hak, &ivars_str);
 | |
| 	hak_pushvolat(hak, &cvars_str);
 | |
| 	c = (hak_oop_class_t)hak_instantiate(hak, hak->c_class, HAK_NULL, HAK_CLASS_SELFSPEC_CLASSVARS(selfspec));
 | |
| 	hak_popvolats(hak, 4);
 | |
| 	if (HAK_UNLIKELY(!c))
 | |
| 	{
 | |
| 		const hak_ooch_t* orgmsg = hak_backuperrmsg(hak);
 | |
| 		hak_seterrbfmt(hak, HAK_ERRNUM(hak),
 | |
| 			"unable to instantiate class %O - %js", class_name, orgmsg);
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		hak_ooi_t nivars_super;
 | |
| 
 | |
| 		if (!HAK_IS_NIL(hak, superclass))
 | |
| 		{
 | |
| 			hak_ooi_t superspec;
 | |
| 			superspec = HAK_OOP_TO_SMOOI(((hak_oop_class_t)superclass)->spec);
 | |
| 			nivars_super = HAK_OOP_TO_SMOOI(((hak_oop_class_t)superclass)->nivars_super) + HAK_CLASS_SPEC_NAMED_INSTVARS(superspec);
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			nivars_super = 0;
 | |
| 		}
 | |
| 
 | |
| 		c->spec = HAK_SMOOI_TO_OOP(spec);
 | |
| 		c->selfspec = HAK_SMOOI_TO_OOP(selfspec);
 | |
| 		c->name = class_name;
 | |
| 		c->superclass = superclass;
 | |
| 		c->nivars_super = HAK_SMOOI_TO_OOP(nivars_super);
 | |
| 		c->ibrand = HAK_SMOOI_TO_OOP(HAK_BRAND_INSTANCE); /* TODO: really need ibrand??? */
 | |
| 
 | |
| 		/* TODO: remember ivars_str and vars_str? */
 | |
| 		/* duplicate ivars_str and cvars_str and set it to c->ivarnames and c->cvarnames???? */
 | |
| 	}
 | |
| 
 | |
| 	return (hak_oop_t)c;
 | |
| }
 | |
| 
 | |
| struct decoded_spec_t
 | |
| {
 | |
| 	hak_obj_type_t type;
 | |
| 	hak_oow_t alloclen;
 | |
| 	int flexi;
 | |
| };
 | |
| typedef struct decoded_spec_t decoded_spec_t;
 | |
| 
 | |
| static HAK_INLINE int decode_spec (hak_t* hak, hak_oop_class_t _class, hak_oow_t num_flexi_fields, decoded_spec_t* dspec)
 | |
| {
 | |
| 	hak_oow_t spec;
 | |
| 	hak_oow_t num_fixed_fields;
 | |
| 	hak_obj_type_t indexed_type;
 | |
| 
 | |
| 	HAK_ASSERT(hak, HAK_OOP_IS_POINTER(_class));
 | |
| 	HAK_ASSERT(hak, HAK_CLASSOF(hak, _class) == (hak_oop_t)hak->c_class);
 | |
| 
 | |
| 	HAK_ASSERT(hak, HAK_OOP_IS_SMOOI(_class->spec));
 | |
| 	spec = HAK_OOP_TO_SMOOI(_class->spec);
 | |
| 
 | |
| 	num_fixed_fields = HAK_CLASS_SPEC_NAMED_INSTVARS(spec);
 | |
| 	HAK_ASSERT(hak, num_fixed_fields <= HAK_MAX_NAMED_INSTVARS);
 | |
| 
 | |
| 	if (HAK_CLASS_SPEC_IS_INDEXED(spec))
 | |
| 	{
 | |
| 		indexed_type = (hak_obj_type_t)HAK_CLASS_SPEC_INDEXED_TYPE(spec);
 | |
| 
 | |
| 		/* the number of the fixed fields for a non-pointer object are supported.
 | |
| 		 * the fixed fields of a pointer object holds named instance variables
 | |
| 		 * and a non-pointer object is facilitated with the fixed fields of the size
 | |
| 		 * specified in the class description like #byte(5), #word(10).
 | |
| 		 *
 | |
| 		 * when it comes to spec decoding, there is no difference between a pointer
 | |
| 		 * object and a non-pointer object */
 | |
| 
 | |
| 		if (num_flexi_fields > HAK_MAX_INDEXED_INSTVARS(num_fixed_fields))
 | |
| 		{
 | |
| 			hak_seterrbfmt(hak, HAK_EINVAL, "number of flexi-fields(%zu) too big for class %O", num_flexi_fields, _class);
 | |
| 			return -1;
 | |
| 		}
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		/* named instance variables only. treat it as if it is an
 | |
| 		 * indexable class with no variable data */
 | |
| 
 | |
| 		/* for an object composed of non-oop fields,
 | |
| 		 * the field can be accessed using a instance variable name.
 | |
| 		 * the instructions for instance variable access must cater for this.
 | |
| 		 * for example, the Primitive class is HAK_OBJ_TYPE_WORD and not variable */
 | |
| 		/*indexed_type = HAK_OBJ_TYPE_OOP; <- no more fixed to OOP fields only. */
 | |
| 		indexed_type = (hak_obj_type_t)HAK_CLASS_SPEC_INDEXED_TYPE(spec);
 | |
| 
 | |
| 		if (num_flexi_fields > 0)
 | |
| 		{
 | |
| 			hak_seterrbfmt(hak, HAK_EPERM, "flexi-fields(%zu) disallowed for class %O", num_flexi_fields, _class);
 | |
| 			return -1;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	HAK_ASSERT(hak, num_fixed_fields + num_flexi_fields <= HAK_OBJ_SIZE_MAX);
 | |
| 	dspec->flexi = !!HAK_CLASS_SPEC_IS_INDEXED(spec);
 | |
| 	dspec->type = indexed_type;
 | |
| 	dspec->alloclen = num_fixed_fields + num_flexi_fields + HAK_OOP_TO_SMOOI(_class->nivars_super);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| hak_oop_t hak_instantiate (hak_t* hak, hak_oop_class_t _class, const void* vptr, hak_oow_t vlen)
 | |
| {
 | |
| 	hak_oop_t oop;
 | |
| 	decoded_spec_t dspec;
 | |
| 	hak_oow_t tmp_count = 0;
 | |
| 
 | |
| 	HAK_ASSERT(hak, hak->_nil != HAK_NULL);
 | |
| 
 | |
| 	if (decode_spec(hak, _class, vlen, &dspec) <= -1) return HAK_NULL;
 | |
| 
 | |
| 	hak_pushvolat(hak, (hak_oop_t*)&_class); tmp_count++;
 | |
| 
 | |
| 	switch (dspec.type)
 | |
| 	{
 | |
| 		case HAK_OBJ_TYPE_OOP:
 | |
| 			/* both the fixed part(named instance variables) and
 | |
| 			 * the variable part(indexed instance variables) are allowed. */
 | |
| 			oop = hak_allocoopobj(hak, dspec.alloclen);
 | |
| 			if (HAK_LIKELY(oop))
 | |
| 			{
 | |
| 		#if 0
 | |
| 				/* initialize named instance variables with default values */
 | |
| 				if (_class->initv[0] != hak->_nil)
 | |
| 				{
 | |
| 					hak_oow_t i = HAK_OBJ_GET_SIZE(_class->initv[0]);
 | |
| 
 | |
| 					/* [NOTE] i don't deep-copy initial values.
 | |
| 					 *   if you change the contents of compound values like arrays,
 | |
| 					 *   it affects subsequent instantiation of the class.
 | |
| 					 *   it's important that the compiler should mark compound initial
 | |
| 					 *   values read-only. */
 | |
| 					while (i > 0)
 | |
| 					{
 | |
| 						--i;
 | |
| 						HAK_OBJ_SET_OOP_VAL (oop, i, HAK_OBJ_GET_OOP_VAL(_class->initv[0], i));
 | |
| 					}
 | |
| 				}
 | |
| 		#endif
 | |
| 			}
 | |
| 			HAK_ASSERT(hak, vptr == HAK_NULL);
 | |
| 			/*
 | |
| 			This function is not GC-safe. so i don't want to initialize
 | |
| 			the payload of a pointer object. The caller can call this
 | |
| 			function and initialize payloads then.
 | |
| 			if (oop && vptr && vlen > 0)
 | |
| 			{
 | |
| 				hak_oop_oop_t hdr = (hak_oop_oop_t)oop;
 | |
| 				HAK_MEMCPY(&hdr->slot[named_ivar], vptr, vlen * HAK_SIZEOF(hak_oop_t));
 | |
| 			}
 | |
| 
 | |
| 			For the above code to work, it should protect the elements of
 | |
| 			the vptr array with hak_pushvolat(). So it might be better
 | |
| 			to disallow a non-NULL vptr when indexed_type is OOP. See
 | |
| 			the assertion above this comment block.
 | |
| 			*/
 | |
| 			break;
 | |
| 
 | |
| 		case HAK_OBJ_TYPE_CHAR:
 | |
| 			oop = hak_alloccharobj(hak, (const hak_ooch_t*)vptr, dspec.alloclen);
 | |
| 			break;
 | |
| 
 | |
| 		case HAK_OBJ_TYPE_BYTE:
 | |
| 			oop = hak_allocbyteobj(hak, (const hak_oob_t*)vptr, dspec.alloclen);
 | |
| 			break;
 | |
| 
 | |
| 		case HAK_OBJ_TYPE_HALFWORD:
 | |
| 			oop = hak_allochalfwordobj(hak, (const hak_oohw_t*)vptr, dspec.alloclen);
 | |
| 			break;
 | |
| 
 | |
| 		case HAK_OBJ_TYPE_WORD:
 | |
| 			oop = hak_allocwordobj(hak, (const hak_oow_t*)vptr, dspec.alloclen);
 | |
| 			break;
 | |
| 
 | |
| 		/* TODO: more types... HAK_OBJ_TYPE_INT... HAK_OBJ_TYPE_FLOAT, HAK_OBJ_TYPE_UINT16, etc*/
 | |
| 		default:
 | |
| 			hak_seterrnum(hak, HAK_EINTERN);
 | |
| 			oop = HAK_NULL;
 | |
| 			break;
 | |
| 	}
 | |
| 
 | |
| 	if (HAK_LIKELY(oop))
 | |
| 	{
 | |
| 		hak_ooi_t spec;
 | |
| 		HAK_OBJ_SET_CLASS (oop, (hak_oop_t)_class);
 | |
| 		spec = HAK_OOP_TO_SMOOI(_class->spec);
 | |
| 		if (HAK_CLASS_SPEC_IS_IMMUTABLE(spec)) HAK_OBJ_SET_FLAGS_RDONLY (oop, 1);
 | |
| 	#if 0 /* TODO: revive this part */
 | |
| 		if (HAK_CLASS_SPEC_IS_UNCOPYABLE(spec)) HAK_OBJ_SET_FLAGS_UNCOPYABLE (oop, 1);
 | |
| 	#endif
 | |
| 		HAK_OBJ_SET_FLAGS_FLEXI(oop, dspec.flexi);
 | |
| 	}
 | |
| 	hak_popvolats(hak, tmp_count);
 | |
| 	return oop;
 | |
| }
 | |
| 
 | |
| hak_oop_t hak_instantiatewithtrailer (hak_t* hak, hak_oop_class_t _class, hak_oow_t vlen, const hak_oob_t* trptr, hak_oow_t trlen)
 | |
| {
 | |
| 	hak_oop_t oop;
 | |
| 	decoded_spec_t dspec;
 | |
| 	hak_oow_t tmp_count = 0;
 | |
| 
 | |
| 	HAK_ASSERT(hak, hak->_nil != HAK_NULL);
 | |
| 
 | |
| 	if (decode_spec(hak, _class, vlen, &dspec) <= -1) return HAK_NULL;
 | |
| 
 | |
| 	hak_pushvolat(hak, (hak_oop_t*)&_class); tmp_count++;
 | |
| 
 | |
| 	switch (dspec.type)
 | |
| 	{
 | |
| 		case HAK_OBJ_TYPE_OOP:
 | |
| 			oop = hak_allocoopobjwithtrailer(hak, dspec.alloclen, trptr, trlen);
 | |
| 			if (HAK_LIKELY(oop))
 | |
| 			{
 | |
| 				/* initialize named instance variables with default values */
 | |
| 			#if 0 /* TODO: revive this part */
 | |
| 				if (_class->initv[0] != hak->_nil)
 | |
| 				{
 | |
| 					hak_oow_t i = HAK_OBJ_GET_SIZE(_class->initv[0]);
 | |
| 
 | |
| 					/* [NOTE] i don't deep-copy initial values.
 | |
| 					 *   if you change the contents of compound values like arrays,
 | |
| 					 *   it affects subsequent instantiation of the class.
 | |
| 					 *   it's important that the compiler should mark compound initial
 | |
| 					 *   values read-only. */
 | |
| 					while (i > 0)
 | |
| 					{
 | |
| 						--i;
 | |
| 						HAK_STORE_OOP(hak, HAK_OBJ_GET_OOP_PTR(oop, i), HAK_OBJ_GET_OOP_VAL(_class->initv[0], i));
 | |
| 					}
 | |
| 				}
 | |
| 			#endif
 | |
| 			}
 | |
| 
 | |
| 			break;
 | |
| 
 | |
| 		default:
 | |
| 		#if 0
 | |
| 			HAK_DEBUG3 (hak, "Not allowed to instantiate a non-pointer object of the %.*js class with trailer %zu\n",
 | |
| 				HAK_OBJ_GET_SIZE(_class->name),
 | |
| 				HAK_OBJ_GET_CHAR_SLOT(_class->name),
 | |
| 				trlen);
 | |
| 		#endif
 | |
| 
 | |
| 			hak_seterrnum(hak, HAK_EPERM);
 | |
| 			oop = HAK_NULL;
 | |
| 			break;
 | |
| 	}
 | |
| 
 | |
| 	if (HAK_LIKELY(oop))
 | |
| 	{
 | |
| 		hak_ooi_t spec;
 | |
| 		HAK_OBJ_SET_CLASS (oop, (hak_oop_t)_class);
 | |
| 		spec = HAK_OOP_TO_SMOOI(_class->spec);
 | |
| 		if (HAK_CLASS_SPEC_IS_IMMUTABLE(spec)) HAK_OBJ_SET_FLAGS_RDONLY (oop, 1);
 | |
| 	#if 0 /* TODO: revive this part */
 | |
| 		/* the object with trailer is to to uncopyable in hak_allocoopobjwithtrailer() so no need to check/set it again here
 | |
| 		if (HAK_CLASS_SPEC_IS_UNCOPYABLE(spec)) HAK_OBJ_SET_FLAGS_UNCOPYABLE (oop, 1);
 | |
| 		*/
 | |
| 	#endif
 | |
| 		HAK_OBJ_SET_FLAGS_FLEXI(oop, dspec.flexi);
 | |
| 	}
 | |
| 	hak_popvolats(hak, tmp_count);
 | |
| 	return oop;
 | |
| }
 | |
| 
 | |
| /* ------------------------------------------------------------------------ *
 | |
|  * NGC HANDLING
 | |
|  * ------------------------------------------------------------------------ */
 | |
| 
 | |
| void hak_freengcobj (hak_t* hak, hak_oop_t obj)
 | |
| {
 | |
| 	if (HAK_OOP_IS_POINTER(obj) && HAK_OBJ_GET_FLAGS_NGC(obj)) hak_freemem(hak, obj);
 | |
| }
 | |
| 
 | |
| hak_oop_t hak_makengcbytearray (hak_t* hak, const hak_oob_t* ptr, hak_oow_t len)
 | |
| {
 | |
| 	return alloc_numeric_array(hak, ptr, len, HAK_OBJ_TYPE_BYTE, HAK_SIZEOF(hak_oob_t), 0, 1);
 | |
| }
 | |
| 
 | |
| hak_oop_t hak_remakengcbytearray (hak_t* hak, hak_oop_t obj, hak_oow_t newsize)
 | |
| {
 | |
| 	hak_oop_t tmp;
 | |
| 
 | |
| 	HAK_ASSERT(hak, !obj || (HAK_OOP_IS_POINTER(obj) && HAK_OBJ_GET_FLAGS_NGC(obj)));
 | |
| 
 | |
| 	/* no hak_pushvolat() is needed because 'obj' is a non-GC object. */
 | |
| 	/* TODO: improve this by using realloc */
 | |
| 
 | |
| 	tmp = hak_makengcbytearray(hak, HAK_NULL, newsize);
 | |
| 	if (HAK_LIKELY(tmp))
 | |
| 	{
 | |
| 		if (obj)
 | |
| 		{
 | |
| 			hak_oow_t cpsize;
 | |
| 			cpsize =  (newsize > HAK_OBJ_GET_SIZE(obj))? HAK_OBJ_GET_SIZE(obj): newsize;
 | |
| 			HAK_MEMCPY(((hak_oop_byte_t)tmp)->slot, ((hak_oop_byte_t)obj)->slot, cpsize * HAK_SIZEOF(hak_oob_t));
 | |
| 		}
 | |
| 		hak_freengcobj(hak, obj);
 | |
| 	}
 | |
| 	return tmp;
 | |
| }
 | |
| 
 | |
| hak_oop_t hak_makengcarray (hak_t* hak, hak_oow_t len)
 | |
| {
 | |
| 	return alloc_numeric_array(hak, HAK_NULL, len, HAK_OBJ_TYPE_OOP, HAK_SIZEOF(hak_oop_t), 0, 1);
 | |
| }
 | |
| 
 | |
| hak_oop_t hak_remakengcarray (hak_t* hak, hak_oop_t obj, hak_oow_t newsize)
 | |
| {
 | |
| 	hak_oop_t tmp;
 | |
| 
 | |
| 	HAK_ASSERT(hak, !obj || (HAK_OOP_IS_POINTER(obj) && HAK_OBJ_GET_FLAGS_NGC(obj)));
 | |
| 
 | |
| 	/* no hak_pushvolat() is needed because 'obj' is a non-GC object. */
 | |
| 	/* TODO: improve this by using realloc */
 | |
| 
 | |
| 	tmp = hak_makengcarray(hak, newsize);
 | |
| 	if (HAK_LIKELY(tmp))
 | |
| 	{
 | |
| 		if (obj)
 | |
| 		{
 | |
| 			hak_oow_t cpsize;
 | |
| 			cpsize =  (newsize > HAK_OBJ_GET_SIZE(obj))? HAK_OBJ_GET_SIZE(obj): newsize;
 | |
| 			HAK_MEMCPY(((hak_oop_oop_t)tmp)->slot, ((hak_oop_oop_t)obj)->slot, cpsize * HAK_SIZEOF(hak_oop_t));
 | |
| 		}
 | |
| 		hak_freengcobj(hak, obj);
 | |
| 	}
 | |
| 	return tmp;
 | |
| }
 | |
| 
 | |
| /* ------------------------------------------------------------------------ *
 | |
|  * CONS
 | |
|  * ------------------------------------------------------------------------ */
 | |
| hak_oow_t hak_countcons (hak_t* hak, hak_oop_t cons)
 | |
| {
 | |
| 	/* this function ignores the last cdr */
 | |
| 	hak_oow_t count = 1;
 | |
| 
 | |
| 	HAK_ASSERT(hak, HAK_IS_CONS(hak, cons));
 | |
| 	do
 | |
| 	{
 | |
| 		cons = HAK_CONS_CDR(cons);
 | |
| 		if (!HAK_IS_CONS(hak, cons)) break;
 | |
| 		count++;
 | |
| 	}
 | |
| 	while (1);
 | |
| 
 | |
| 	return count;
 | |
| }
 | |
| 
 | |
| hak_oop_t hak_getlastconscdr (hak_t* hak, hak_oop_t cons)
 | |
| {
 | |
| 	HAK_ASSERT(hak, HAK_IS_CONS(hak, cons));
 | |
| 	do
 | |
| 	{
 | |
| 		cons = HAK_CONS_CDR(cons);
 | |
| 		if (!HAK_IS_CONS(hak, cons)) break;
 | |
| 	}
 | |
| 	while (1);
 | |
| 
 | |
| 	return cons;
 | |
| }
 | |
| 
 | |
| hak_oop_t hak_reversecons (hak_t* hak, hak_oop_t cons)
 | |
| {
 | |
| 	hak_oop_t ptr, prev, next;
 | |
| 
 | |
| 	/* Note: The non-nil cdr in the last cons cell gets lost.
 | |
| 	 *  e.g.) Reversing (1 2 3 . 4) results in (3 2 1) */
 | |
| 
 | |
| 	HAK_ASSERT(hak, HAK_IS_CONS(hak, cons));
 | |
| 
 | |
| 	prev = hak->_nil;
 | |
| 	ptr = cons;
 | |
| 
 | |
| 	do
 | |
| 	{
 | |
| 		next = HAK_CONS_CDR(ptr);
 | |
| 		HAK_CONS_CDR(ptr) = prev;
 | |
| 		prev = ptr;
 | |
| 		if (!HAK_IS_CONS(hak, next)) break;
 | |
| 		ptr = next;
 | |
| 	}
 | |
| 	while (1);
 | |
| 
 | |
| 	return ptr;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* ------------------------------------------------------------------------ *
 | |
|  * OBJECT HASHING
 | |
|  * ------------------------------------------------------------------------ */
 | |
| int hak_hashobj (hak_t* hak, hak_oop_t obj, hak_oow_t* xhv)
 | |
| {
 | |
| 	hak_oow_t hv;
 | |
| 
 | |
| 	if (obj == hak->_nil)
 | |
| 	{
 | |
| 		*xhv = 0;
 | |
| 		return 0;
 | |
| 	}
 | |
| 	else if (obj == hak->_true)
 | |
| 	{
 | |
| 		*xhv = 1;
 | |
| 		return 0;
 | |
| 	}
 | |
| 	else if (obj == hak->_false)
 | |
| 	{
 | |
| 		*xhv = 2;
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	switch (HAK_OOP_GET_TAG(obj))
 | |
| 	{
 | |
| 		case HAK_OOP_TAG_SMOOI:
 | |
| 			hv = HAK_OOP_TO_SMOOI(obj);
 | |
| 			break;
 | |
| 
 | |
| /*
 | |
| 		case HAK_OOP_TAG_SMPTR:
 | |
| 			hv = (hak_oow_t)HAK_OOP_TO_SMPTR(obj);
 | |
| 			break;
 | |
| */
 | |
| 
 | |
| 		case HAK_OOP_TAG_CHAR:
 | |
| 			hv = HAK_OOP_TO_CHAR(obj);
 | |
| 			break;
 | |
| 
 | |
| /*
 | |
| 		case HAK_OOP_TAG_ERROR:
 | |
| 			hv = HAK_OOP_TO_ERROR(obj);
 | |
| 			break;
 | |
| */
 | |
| 
 | |
| 		default:
 | |
| 		{
 | |
| 			int type;
 | |
| 
 | |
| 			HAK_ASSERT(hak, HAK_OOP_IS_POINTER(obj));
 | |
| 			type = HAK_OBJ_GET_FLAGS_TYPE(obj);
 | |
| 			switch (type)
 | |
| 			{
 | |
| 				case HAK_OBJ_TYPE_BYTE:
 | |
| 					hv = hak_hash_bytes(((hak_oop_byte_t)obj)->slot, HAK_OBJ_GET_SIZE(obj));
 | |
| 					break;
 | |
| 
 | |
| 				case HAK_OBJ_TYPE_CHAR:
 | |
| 					hv = hak_hash_oochars(((hak_oop_char_t)obj)->slot, HAK_OBJ_GET_SIZE(obj));
 | |
| 					break;
 | |
| 
 | |
| 				case HAK_OBJ_TYPE_HALFWORD:
 | |
| 					hv = hak_hash_halfwords(((hak_oop_halfword_t)obj)->slot, HAK_OBJ_GET_SIZE(obj));
 | |
| 					break;
 | |
| 
 | |
| 				case HAK_OBJ_TYPE_WORD:
 | |
| 					hv = hak_hash_words(((hak_oop_word_t)obj)->slot, HAK_OBJ_GET_SIZE(obj));
 | |
| 					break;
 | |
| 
 | |
| 				default:
 | |
| 					/* HAK_OBJ_TYPE_OOP, ... */
 | |
| 					hak_seterrbfmt(hak, HAK_ENOIMPL, "no builtin hash implemented for %O", obj); /* TODO: better error code? */
 | |
| 					return -1;
 | |
| 			}
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* i assume that hak_hashxxx() functions limits the return value to fall
 | |
| 	 * between 0 and HAK_SMOOI_MAX inclusive */
 | |
| 	HAK_ASSERT(hak, hv >= 0 && hv <= HAK_SMOOI_MAX);
 | |
| 	*xhv = hv;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /* ------------------------------------------------------------------------ *
 | |
|  * OBJECT EQUALITY
 | |
|  * ------------------------------------------------------------------------ */
 | |
| int hak_equalobjs (hak_t* hak, hak_oop_t rcv, hak_oop_t arg)
 | |
| {
 | |
| 	int rtag;
 | |
| 
 | |
| 	if (rcv == arg) return 1; /* identical. so equal */
 | |
| 
 | |
| 	rtag = HAK_OOP_GET_TAG(rcv);
 | |
| 	if (rtag != HAK_OOP_GET_TAG(arg)) return 0;
 | |
| 
 | |
| 	switch (rtag)
 | |
| 	{
 | |
| 		case HAK_OOP_TAG_SMOOI:
 | |
| 			return HAK_OOP_TO_SMOOI(rcv) == HAK_OOP_TO_SMOOI(arg)? 1: 0;
 | |
| 
 | |
| 		case HAK_OOP_TAG_SMPTR:
 | |
| 			return HAK_OOP_TO_SMPTR(rcv) == HAK_OOP_TO_SMPTR(arg)? 1: 0;
 | |
| 
 | |
| 		case HAK_OOP_TAG_CHAR:
 | |
| 			return HAK_OOP_TO_CHAR(rcv) == HAK_OOP_TO_CHAR(arg)? 1: 0;
 | |
| 
 | |
| 		case HAK_OOP_TAG_ERROR:
 | |
| 			return HAK_OOP_TO_ERROR(rcv) == HAK_OOP_TO_ERROR(arg)? 1: 0;
 | |
| 
 | |
| 		default:
 | |
| 		{
 | |
| 			HAK_ASSERT(hak, HAK_OOP_IS_POINTER(rcv));
 | |
| 
 | |
| 			if (HAK_OBJ_GET_CLASS(rcv) != HAK_OBJ_GET_CLASS(arg)) return 0; /* different class, not equal */
 | |
| 			HAK_ASSERT(hak, HAK_OBJ_GET_FLAGS_TYPE(rcv) == HAK_OBJ_GET_FLAGS_TYPE(arg));
 | |
| 
 | |
| 			if (HAK_OBJ_GET_SIZE(rcv) != HAK_OBJ_GET_SIZE(arg)) return 0; /* different size, not equal */
 | |
| 
 | |
| 			switch (HAK_OBJ_GET_FLAGS_TYPE(rcv))
 | |
| 			{
 | |
| 				case HAK_OBJ_TYPE_BYTE:
 | |
| 				case HAK_OBJ_TYPE_CHAR:
 | |
| 				case HAK_OBJ_TYPE_HALFWORD:
 | |
| 				case HAK_OBJ_TYPE_WORD:
 | |
| 					return (HAK_MEMCMP(HAK_OBJ_GET_BYTE_SLOT(rcv), HAK_OBJ_GET_BYTE_SLOT(arg), HAK_BYTESOF(hak,rcv)) == 0)? 1: 0;
 | |
| 
 | |
| 				default:
 | |
| 				{
 | |
| 					hak_oow_t i, size;
 | |
| 
 | |
| 					if (rcv == hak->_nil) return arg == hak->_nil? 1: 0;
 | |
| 					if (rcv == hak->_true) return arg == hak->_true? 1: 0;
 | |
| 					if (rcv == hak->_false) return arg == hak->_false? 1: 0;
 | |
| 
 | |
| 					/* HAK_OBJ_TYPE_OOP, ... */
 | |
| 					HAK_ASSERT(hak, HAK_OBJ_GET_FLAGS_TYPE(rcv) == HAK_OBJ_TYPE_OOP);
 | |
| 
 | |
| 				#if 0
 | |
| 					hak_seterrbfmt(hak, HAK_ENOIMPL, "no builtin comparison implemented for %O and %O", rcv, arg); /* TODO: better error code */
 | |
| 					return -1;
 | |
| 				#else
 | |
| 
 | |
| 					if (HAK_IS_PROCESS(hak,rcv))
 | |
| 					{
 | |
| 						/* the stack in a process object doesn't need to be
 | |
| 						 * scanned in full. the slots above the stack pointer
 | |
| 						 * are garbages. */
 | |
| 						size = HAK_PROCESS_NAMED_INSTVARS +
 | |
| 							  HAK_OOP_TO_SMOOI(((hak_oop_process_t)rcv)->sp) + 1;
 | |
| 						HAK_ASSERT(hak, size <= HAK_OBJ_GET_SIZE(rcv));
 | |
| 					}
 | |
| 					else
 | |
| 					{
 | |
| 						size = HAK_OBJ_GET_SIZE(rcv);
 | |
| 					}
 | |
| 					for (i = 0; i < size; i++)
 | |
| 					{
 | |
| 						int n;
 | |
| 						/* TODO: remove recursion */
 | |
| 						/* NOTE: even if the object implements the equality method,
 | |
| 						 * this primitive method doesn't honor it. */
 | |
| 						n = hak_equalobjs(hak, ((hak_oop_oop_t)rcv)->slot[i], ((hak_oop_oop_t)arg)->slot[i]);
 | |
| 						if (n <= 0) return n;
 | |
| 					}
 | |
| 
 | |
| 					/* the default implementation doesn't take the trailer space into account */
 | |
| 					return 1;
 | |
| 				#endif
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 |