Aer/memobj.c

1300 lines
42 KiB
C

/*
* Symisc PH7: An embeddable bytecode compiler and a virtual machine for the PHP(5) programming language.
* Copyright (C) 2011-2012, Symisc Systems http://ph7.symisc.net/
* Version 2.1.4
* For information on licensing,redistribution of this file,and for a DISCLAIMER OF ALL WARRANTIES
* please contact Symisc Systems via:
* legal@symisc.net
* licensing@symisc.net
* contact@symisc.net
* or visit:
* http://ph7.symisc.net/
*/
/* $SymiscID: memobj.c v2.7 FreeBSD 2012-08-09 03:40 stable <chm@symisc.net> $ */
#include "ph7int.h"
/* This file handle low-level stuff related to indexed memory objects [i.e: ph7_value] */
/*
* Notes on memory objects [i.e: ph7_value].
* Internally, the PH7 virtual machine manipulates nearly all PHP values
* [i.e: string,int,float,resource,object,bool,null..] as ph7_values structures.
* Each ph7_values struct may cache multiple representations (string,
* integer etc.) of the same value.
*/
/*
* Convert a 64-bit IEEE double into a 64-bit signed integer.
* If the double is too large, return 0x8000000000000000.
*
* Most systems appear to do this simply by assigning ariables and without
* the extra range tests.
* But there are reports that windows throws an expection if the floating
* point value is out of range.
*/
static sxi64 MemObjRealToInt(ph7_value *pObj)
{
#ifdef PH7_OMIT_FLOATING_POINT
/* Real and 64bit integer are the same when floating point arithmetic
* is omitted from the build.
*/
return pObj->rVal;
#else
/*
** Many compilers we encounter do not define constants for the
** minimum and maximum 64-bit integers, or they define them
** inconsistently. And many do not understand the "LL" notation.
** So we define our own static constants here using nothing
** larger than a 32-bit integer constant.
*/
static const sxi64 maxInt = LARGEST_INT64;
static const sxi64 minInt = SMALLEST_INT64;
ph7_real r = pObj->rVal;
if( r<(ph7_real)minInt ){
return minInt;
}else if( r>(ph7_real)maxInt ){
/* minInt is correct here - not maxInt. It turns out that assigning
** a very large positive number to an integer results in a very large
** negative integer. This makes no sense, but it is what x86 hardware
** does so for compatibility we will do the same in software. */
return minInt;
}else{
return (sxi64)r;
}
#endif
}
/*
* Convert a raw token value typically a stream of digit [i.e: hex,octal,binary or decimal]
* to a 64-bit integer.
*/
PH7_PRIVATE sxi64 PH7_TokenValueToInt64(SyString *pVal)
{
sxi64 iVal = 0;
if( pVal->nByte <= 0 ){
return 0;
}
if( pVal->zString[0] == '0' ){
sxi32 c;
if( pVal->nByte == sizeof(char) ){
return 0;
}
c = pVal->zString[1];
if( c == 'x' || c == 'X' ){
/* Hex digit stream */
SyHexStrToInt64(pVal->zString,pVal->nByte,(void *)&iVal,0);
}else if( c == 'b' || c == 'B' ){
/* Binary digit stream */
SyBinaryStrToInt64(pVal->zString,pVal->nByte,(void *)&iVal,0);
}else{
/* Octal digit stream */
SyOctalStrToInt64(pVal->zString,pVal->nByte,(void *)&iVal,0);
}
}else{
/* Decimal digit stream */
SyStrToInt64(pVal->zString,pVal->nByte,(void *)&iVal,0);
}
return iVal;
}
/*
* Return some kind of 64-bit integer value which is the best we can
* do at representing the value that pObj describes as a string
* representation.
*/
static sxi64 MemObjStringToInt(ph7_value *pObj)
{
SyString sVal;
SyStringInitFromBuf(&sVal,SyBlobData(&pObj->sBlob),SyBlobLength(&pObj->sBlob));
return PH7_TokenValueToInt64(&sVal);
}
/*
* Call a magic class method [i.e: __toString(),__toInt(),...]
* Return SXRET_OK if the magic method is available and have been
* successfully called. Any other return value indicates failure.
*/
static sxi32 MemObjCallClassCastMethod(
ph7_vm *pVm, /* VM that trigger the invocation */
ph7_class_instance *pThis, /* Target class instance [i.e: Object] */
const char *zMethod, /* Magic method name [i.e: __toString] */
sxu32 nLen, /* Method name length */
ph7_value *pResult /* OUT: Store the return value of the magic method here */
)
{
ph7_class_method *pMethod;
/* Check if the method is available */
pMethod = PH7_ClassExtractMethod(pThis->pClass,zMethod,nLen);
if( pMethod == 0 ){
/* No such method */
return SXERR_NOTFOUND;
}
/* Invoke the desired method */
PH7_VmCallClassMethod(&(*pVm),&(*pThis),pMethod,&(*pResult),0,0);
/* Method successfully called,pResult should hold the return value */
return SXRET_OK;
}
/*
* Return some kind of integer value which is the best we can
* do at representing the value that pObj describes as an integer.
* If pObj is an integer, then the value is exact. If pObj is
* a floating-point then the value returned is the integer part.
* If pObj is a string, then we make an attempt to convert it into
* a integer and return that.
* If pObj represents a NULL value, return 0.
*/
static sxi64 MemObjIntValue(ph7_value *pObj)
{
sxi32 iFlags;
iFlags = pObj->iFlags;
if (iFlags & MEMOBJ_REAL ){
return MemObjRealToInt(&(*pObj));
}else if( iFlags & (MEMOBJ_INT|MEMOBJ_BOOL) ){
return pObj->x.iVal;
}else if (iFlags & MEMOBJ_STRING) {
return MemObjStringToInt(&(*pObj));
}else if( iFlags & MEMOBJ_NULL ){
return 0;
}else if( iFlags & MEMOBJ_HASHMAP ){
ph7_hashmap *pMap = (ph7_hashmap *)pObj->x.pOther;
sxu32 n = pMap->nEntry;
PH7_HashmapUnref(pMap);
/* Return total number of entries in the hashmap */
return n;
}else if( iFlags & MEMOBJ_OBJ ){
ph7_value sResult;
sxi64 iVal = 1;
sxi32 rc;
/* Invoke the [__toInt()] magic method if available [note that this is a symisc extension] */
PH7_MemObjInit(pObj->pVm,&sResult);
rc = MemObjCallClassCastMethod(pObj->pVm,(ph7_class_instance *)pObj->x.pOther,
"__toInt",sizeof("__toInt")-1,&sResult);
if( rc == SXRET_OK && (sResult.iFlags & MEMOBJ_INT) ){
/* Extract method return value */
iVal = sResult.x.iVal;
}
PH7_ClassInstanceUnref((ph7_class_instance *)pObj->x.pOther);
PH7_MemObjRelease(&sResult);
return iVal;
}else if(iFlags & MEMOBJ_RES ){
return pObj->x.pOther != 0;
}
/* CANT HAPPEN */
return 0;
}
/*
* Return some kind of real value which is the best we can
* do at representing the value that pObj describes as a real.
* If pObj is a real, then the value is exact.If pObj is an
* integer then the integer is promoted to real and that value
* is returned.
* If pObj is a string, then we make an attempt to convert it
* into a real and return that.
* If pObj represents a NULL value, return 0.0
*/
static ph7_real MemObjRealValue(ph7_value *pObj)
{
sxi32 iFlags;
iFlags = pObj->iFlags;
if( iFlags & MEMOBJ_REAL ){
return pObj->rVal;
}else if (iFlags & (MEMOBJ_INT|MEMOBJ_BOOL) ){
return (ph7_real)pObj->x.iVal;
}else if (iFlags & MEMOBJ_STRING){
SyString sString;
#ifdef PH7_OMIT_FLOATING_POINT
ph7_real rVal = 0;
#else
ph7_real rVal = 0.0;
#endif
SyStringInitFromBuf(&sString,SyBlobData(&pObj->sBlob),SyBlobLength(&pObj->sBlob));
if( SyBlobLength(&pObj->sBlob) > 0 ){
/* Convert as much as we can */
#ifdef PH7_OMIT_FLOATING_POINT
rVal = MemObjStringToInt(&(*pObj));
#else
SyStrToReal(sString.zString,sString.nByte,(void *)&rVal,0);
#endif
}
return rVal;
}else if( iFlags & MEMOBJ_NULL ){
#ifdef PH7_OMIT_FLOATING_POINT
return 0;
#else
return 0.0;
#endif
}else if( iFlags & MEMOBJ_HASHMAP ){
/* Return the total number of entries in the hashmap */
ph7_hashmap *pMap = (ph7_hashmap *)pObj->x.pOther;
ph7_real n = (ph7_real)pMap->nEntry;
PH7_HashmapUnref(pMap);
return n;
}else if( iFlags & MEMOBJ_OBJ ){
ph7_value sResult;
ph7_real rVal = 1;
sxi32 rc;
/* Invoke the [__toFloat()] magic method if available [note that this is a symisc extension] */
PH7_MemObjInit(pObj->pVm,&sResult);
rc = MemObjCallClassCastMethod(pObj->pVm,(ph7_class_instance *)pObj->x.pOther,
"__toFloat",sizeof("__toFloat")-1,&sResult);
if( rc == SXRET_OK && (sResult.iFlags & MEMOBJ_REAL) ){
/* Extract method return value */
rVal = sResult.rVal;
}
PH7_ClassInstanceUnref((ph7_class_instance *)pObj->x.pOther);
PH7_MemObjRelease(&sResult);
return rVal;
}else if(iFlags & MEMOBJ_RES ){
return (ph7_real)(pObj->x.pOther != 0);
}
/* NOT REACHED */
return 0;
}
/*
* Return the string representation of a given ph7_value.
* This function never fail and always return SXRET_OK.
*/
static sxi32 MemObjStringValue(SyBlob *pOut,ph7_value *pObj,sxu8 bStrictBool)
{
if( pObj->iFlags & MEMOBJ_REAL ){
SyBlobFormat(&(*pOut),"%.15g",pObj->rVal);
}else if( pObj->iFlags & MEMOBJ_INT ){
SyBlobFormat(&(*pOut),"%qd",pObj->x.iVal);
/* %qd (BSD quad) is equivalent to %lld in the libc printf */
}else if( pObj->iFlags & MEMOBJ_BOOL ){
if( pObj->x.iVal ){
SyBlobAppend(&(*pOut),"TRUE",sizeof("TRUE")-1);
}else{
if( !bStrictBool ){
SyBlobAppend(&(*pOut),"FALSE",sizeof("FALSE")-1);
}
}
}else if( pObj->iFlags & MEMOBJ_HASHMAP ){
SyBlobAppend(&(*pOut),"Array",sizeof("Array")-1);
PH7_HashmapUnref((ph7_hashmap *)pObj->x.pOther);
}else if( pObj->iFlags & MEMOBJ_OBJ ){
ph7_value sResult;
sxi32 rc;
/* Invoke the __toString() method if available */
PH7_MemObjInit(pObj->pVm,&sResult);
rc = MemObjCallClassCastMethod(pObj->pVm,(ph7_class_instance *)pObj->x.pOther,
"__toString",sizeof("__toString")-1,&sResult);
if( rc == SXRET_OK && (sResult.iFlags & MEMOBJ_STRING) && SyBlobLength(&sResult.sBlob) > 0){
/* Expand method return value */
SyBlobDup(&sResult.sBlob,pOut);
}else{
/* Expand "Object" as requested by the PHP language reference manual */
SyBlobAppend(&(*pOut),"Object",sizeof("Object")-1);
}
PH7_ClassInstanceUnref((ph7_class_instance *)pObj->x.pOther);
PH7_MemObjRelease(&sResult);
}else if(pObj->iFlags & MEMOBJ_RES ){
SyBlobFormat(&(*pOut),"ResourceID_%#x",pObj->x.pOther);
}
return SXRET_OK;
}
/*
* Return some kind of boolean value which is the best we can do
* at representing the value that pObj describes as a boolean.
* When converting to boolean, the following values are considered FALSE:
* NULL
* the boolean FALSE itself.
* the integer 0 (zero).
* the real 0.0 (zero).
* the empty string,a stream of zero [i.e: "0","00","000",...] and the string
* "false".
* an array with zero elements.
*/
static sxi32 MemObjBooleanValue(ph7_value *pObj)
{
sxi32 iFlags;
iFlags = pObj->iFlags;
if (iFlags & MEMOBJ_REAL ){
#ifdef PH7_OMIT_FLOATING_POINT
return pObj->rVal ? 1 : 0;
#else
return pObj->rVal != 0.0 ? 1 : 0;
#endif
}else if( iFlags & MEMOBJ_INT ){
return pObj->x.iVal ? 1 : 0;
}else if (iFlags & MEMOBJ_STRING) {
SyString sString;
SyStringInitFromBuf(&sString,SyBlobData(&pObj->sBlob),SyBlobLength(&pObj->sBlob));
if( sString.nByte == 0 ){
/* Empty string */
return 0;
}else if( (sString.nByte == sizeof("true") - 1 && SyStrnicmp(sString.zString,"true",sizeof("true")-1) == 0) ||
(sString.nByte == sizeof("on") - 1 && SyStrnicmp(sString.zString,"on",sizeof("on")-1) == 0) ||
(sString.nByte == sizeof("yes") - 1 && SyStrnicmp(sString.zString,"yes",sizeof("yes")-1) == 0) ){
return 1;
}else if( sString.nByte == sizeof("false") - 1 && SyStrnicmp(sString.zString,"false",sizeof("false")-1) == 0 ){
return 0;
}else{
const char *zIn,*zEnd;
zIn = sString.zString;
zEnd = &zIn[sString.nByte];
while( zIn < zEnd && zIn[0] == '0' ){
zIn++;
}
return zIn >= zEnd ? 0 : 1;
}
}else if( iFlags & MEMOBJ_NULL ){
return 0;
}else if( iFlags & MEMOBJ_HASHMAP ){
ph7_hashmap *pMap = (ph7_hashmap *)pObj->x.pOther;
sxu32 n = pMap->nEntry;
PH7_HashmapUnref(pMap);
return n > 0 ? TRUE : FALSE;
}else if( iFlags & MEMOBJ_OBJ ){
ph7_value sResult;
sxi32 iVal = 1;
sxi32 rc;
/* Invoke the __toBool() method if available [note that this is a symisc extension] */
PH7_MemObjInit(pObj->pVm,&sResult);
rc = MemObjCallClassCastMethod(pObj->pVm,(ph7_class_instance *)pObj->x.pOther,
"__toBool",sizeof("__toBool")-1,&sResult);
if( rc == SXRET_OK && (sResult.iFlags & (MEMOBJ_INT|MEMOBJ_BOOL)) ){
/* Extract method return value */
iVal = (sxi32)(sResult.x.iVal != 0); /* Stupid cc warning -W -Wall -O6 */
}
PH7_ClassInstanceUnref((ph7_class_instance *)pObj->x.pOther);
PH7_MemObjRelease(&sResult);
return iVal;
}else if(iFlags & MEMOBJ_RES ){
return pObj->x.pOther != 0;
}
/* NOT REACHED */
return 0;
}
/*
* If the ph7_value is of type real,try to make it an integer also.
*/
static sxi32 MemObjTryIntger(ph7_value *pObj)
{
pObj->x.iVal = MemObjRealToInt(&(*pObj));
/* Only mark the value as an integer if
**
** (1) the round-trip conversion real->int->real is a no-op, and
** (2) The integer is neither the largest nor the smallest
** possible integer
**
** The second and third terms in the following conditional enforces
** the second condition under the assumption that addition overflow causes
** values to wrap around. On x86 hardware, the third term is always
** true and could be omitted. But we leave it in because other
** architectures might behave differently.
*/
if( pObj->rVal ==(ph7_real)pObj->x.iVal && pObj->x.iVal>SMALLEST_INT64
&& pObj->x.iVal<LARGEST_INT64 ){
pObj->iFlags |= MEMOBJ_INT;
}
return SXRET_OK;
}
/*
* Convert a ph7_value to type integer.Invalidate any prior representations.
*/
PH7_PRIVATE sxi32 PH7_MemObjToInteger(ph7_value *pObj)
{
if( (pObj->iFlags & MEMOBJ_INT) == 0 ){
/* Preform the conversion */
pObj->x.iVal = MemObjIntValue(&(*pObj));
/* Invalidate any prior representations */
SyBlobRelease(&pObj->sBlob);
MemObjSetType(pObj,MEMOBJ_INT);
}
return SXRET_OK;
}
/*
* Convert a ph7_value to type real (Try to get an integer representation also).
* Invalidate any prior representations
*/
PH7_PRIVATE sxi32 PH7_MemObjToReal(ph7_value *pObj)
{
if((pObj->iFlags & MEMOBJ_REAL) == 0 ){
/* Preform the conversion */
pObj->rVal = MemObjRealValue(&(*pObj));
/* Invalidate any prior representations */
SyBlobRelease(&pObj->sBlob);
MemObjSetType(pObj,MEMOBJ_REAL);
/* Try to get an integer representation */
MemObjTryIntger(&(*pObj));
}
return SXRET_OK;
}
/*
* Convert a ph7_value to type boolean.Invalidate any prior representations.
*/
PH7_PRIVATE sxi32 PH7_MemObjToBool(ph7_value *pObj)
{
if( (pObj->iFlags & MEMOBJ_BOOL) == 0 ){
/* Preform the conversion */
pObj->x.iVal = MemObjBooleanValue(&(*pObj));
/* Invalidate any prior representations */
SyBlobRelease(&pObj->sBlob);
MemObjSetType(pObj,MEMOBJ_BOOL);
}
return SXRET_OK;
}
/*
* Convert a ph7_value to type string.Prior representations are NOT invalidated.
*/
PH7_PRIVATE sxi32 PH7_MemObjToString(ph7_value *pObj)
{
sxi32 rc = SXRET_OK;
if( (pObj->iFlags & MEMOBJ_STRING) == 0 ){
/* Perform the conversion */
SyBlobReset(&pObj->sBlob); /* Reset the internal buffer */
rc = MemObjStringValue(&pObj->sBlob,&(*pObj),TRUE);
MemObjSetType(pObj,MEMOBJ_STRING);
}
return rc;
}
/*
* Nullify a ph7_value.In other words invalidate any prior
* representation.
*/
PH7_PRIVATE sxi32 PH7_MemObjToNull(ph7_value *pObj)
{
return PH7_MemObjRelease(pObj);
}
/*
* Convert a ph7_value to type array.Invalidate any prior representations.
* According to the PHP language reference manual.
* For any of the types: integer, float, string, boolean converting a value
* to an array results in an array with a single element with index zero
* and the value of the scalar which was converted.
*/
PH7_PRIVATE sxi32 PH7_MemObjToHashmap(ph7_value *pObj)
{
if( (pObj->iFlags & MEMOBJ_HASHMAP) == 0 ){
ph7_hashmap *pMap;
/* Allocate a new hashmap instance */
pMap = PH7_NewHashmap(pObj->pVm,0,0);
if( pMap == 0 ){
return SXERR_MEM;
}
if( (pObj->iFlags & (MEMOBJ_NULL|MEMOBJ_RES)) == 0 ){
/*
* According to the PHP language reference manual.
* For any of the types: integer, float, string, boolean converting a value
* to an array results in an array with a single element with index zero
* and the value of the scalar which was converted.
*/
if( pObj->iFlags & MEMOBJ_OBJ ){
/* Object cast */
PH7_ClassInstanceToHashmap((ph7_class_instance *)pObj->x.pOther,pMap);
}else{
/* Insert a single element */
PH7_HashmapInsert(pMap,0/* Automatic index assign */,&(*pObj));
}
SyBlobRelease(&pObj->sBlob);
}
/* Invalidate any prior representation */
MemObjSetType(pObj,MEMOBJ_HASHMAP);
pObj->x.pOther = pMap;
}
return SXRET_OK;
}
/*
* Convert a ph7_value to type object.Invalidate any prior representations.
* The new object is instantiated from the builtin stdClass().
* The stdClass() class have a single attribute which is '$value'. This attribute
* hold a copy of the converted ph7_value.
* The internal of the stdClass is as follows:
* class stdClass{
* public $value;
* public function __toInt(){ return (int)$this->value; }
* public function __toBool(){ return (bool)$this->value; }
* public function __toFloat(){ return (float)$this->value; }
* public function __toString(){ return (string)$this->value; }
* function __construct($v){ $this->value = $v; }"
* }
* Refer to the official documentation for more information.
*/
PH7_PRIVATE sxi32 PH7_MemObjToObject(ph7_value *pObj)
{
if( (pObj->iFlags & MEMOBJ_OBJ) == 0 ){
ph7_class_instance *pStd;
ph7_class_method *pCons;
ph7_class *pClass;
ph7_vm *pVm;
/* Point to the underlying VM */
pVm = pObj->pVm;
/* Point to the stdClass() */
pClass = PH7_VmExtractClass(pVm,"stdClass",sizeof("stdClass")-1,0,0);
if( pClass == 0 ){
/* Can't happen,load null instead */
PH7_MemObjRelease(pObj);
return SXRET_OK;
}
/* Instanciate a new stdClass() object */
pStd = PH7_NewClassInstance(pVm,pClass);
if( pStd == 0 ){
/* Out of memory */
PH7_MemObjRelease(pObj);
return SXRET_OK;
}
/* Check if a constructor is available */
pCons = PH7_ClassExtractMethod(pClass,"__construct",sizeof("__construct")-1);
if( pCons ){
ph7_value *apArg[2];
/* Invoke the constructor with one argument */
apArg[0] = pObj;
PH7_VmCallClassMethod(pVm,pStd,pCons,0,1,apArg);
if( pStd->iRef < 1 ){
pStd->iRef = 1;
}
}
/* Invalidate any prior representation */
PH7_MemObjRelease(pObj);
/* Save the new instance */
pObj->x.pOther = pStd;
MemObjSetType(pObj,MEMOBJ_OBJ);
}
return SXRET_OK;
}
/*
* Return a pointer to the appropriate convertion method associated
* with the given type.
* Note on type juggling.
* Accoding to the PHP language reference manual
* PHP does not require (or support) explicit type definition in variable
* declaration; a variable's type is determined by the context in which
* the variable is used. That is to say, if a string value is assigned
* to variable $var, $var becomes a string. If an integer value is then
* assigned to $var, it becomes an integer.
*/
PH7_PRIVATE ProcMemObjCast PH7_MemObjCastMethod(sxi32 iFlags)
{
if( iFlags & MEMOBJ_STRING ){
return PH7_MemObjToString;
}else if( iFlags & MEMOBJ_INT ){
return PH7_MemObjToInteger;
}else if( iFlags & MEMOBJ_REAL ){
return PH7_MemObjToReal;
}else if( iFlags & MEMOBJ_BOOL ){
return PH7_MemObjToBool;
}else if( iFlags & MEMOBJ_HASHMAP ){
return PH7_MemObjToHashmap;
}else if( iFlags & MEMOBJ_OBJ ){
return PH7_MemObjToObject;
}
/* NULL cast */
return PH7_MemObjToNull;
}
/*
* Check whether the ph7_value is numeric [i.e: int/float/bool] or looks
* like a numeric number [i.e: if the ph7_value is of type string.].
* Return TRUE if numeric.FALSE otherwise.
*/
PH7_PRIVATE sxi32 PH7_MemObjIsNumeric(ph7_value *pObj)
{
if( pObj->iFlags & ( MEMOBJ_BOOL|MEMOBJ_INT|MEMOBJ_REAL) ){
return TRUE;
}else if( pObj->iFlags & (MEMOBJ_NULL|MEMOBJ_HASHMAP|MEMOBJ_OBJ|MEMOBJ_RES) ){
return FALSE;
}else if( pObj->iFlags & MEMOBJ_STRING ){
SyString sStr;
sxi32 rc;
SyStringInitFromBuf(&sStr,SyBlobData(&pObj->sBlob),SyBlobLength(&pObj->sBlob));
if( sStr.nByte <= 0 ){
/* Empty string */
return FALSE;
}
/* Check if the string representation looks like a numeric number */
rc = SyStrIsNumeric(sStr.zString,sStr.nByte,0,0);
return rc == SXRET_OK ? TRUE : FALSE;
}
/* NOT REACHED */
return FALSE;
}
/*
* Check whether the ph7_value is empty.Return TRUE if empty.
* FALSE otherwise.
* An ph7_value is considered empty if the following are true:
* NULL value.
* Boolean FALSE.
* Integer/Float with a 0 (zero) value.
* An empty string or a stream of 0 (zero) [i.e: "0","00","000",...].
* An empty array.
* NOTE
* OBJECT VALUE MUST NOT BE MODIFIED.
*/
PH7_PRIVATE sxi32 PH7_MemObjIsEmpty(ph7_value *pObj)
{
if( pObj->iFlags & MEMOBJ_NULL ){
return TRUE;
}else if( pObj->iFlags & MEMOBJ_INT ){
return pObj->x.iVal == 0 ? TRUE : FALSE;
}else if( pObj->iFlags & MEMOBJ_REAL ){
return pObj->rVal == (ph7_real)0 ? TRUE : FALSE;
}else if( pObj->iFlags & MEMOBJ_BOOL ){
return !pObj->x.iVal;
}else if( pObj->iFlags & MEMOBJ_STRING ){
if( SyBlobLength(&pObj->sBlob) <= 0 ){
return TRUE;
}else{
const char *zIn,*zEnd;
zIn = (const char *)SyBlobData(&pObj->sBlob);
zEnd = &zIn[SyBlobLength(&pObj->sBlob)];
while( zIn < zEnd ){
if( zIn[0] != '0' ){
break;
}
zIn++;
}
return zIn >= zEnd ? TRUE : FALSE;
}
}else if( pObj->iFlags & MEMOBJ_HASHMAP ){
ph7_hashmap *pMap = (ph7_hashmap *)pObj->x.pOther;
return pMap->nEntry == 0 ? TRUE : FALSE;
}else if ( pObj->iFlags & (MEMOBJ_OBJ|MEMOBJ_RES) ){
return FALSE;
}
/* Assume empty by default */
return TRUE;
}
/*
* Convert a ph7_value so that it has types MEMOBJ_REAL or MEMOBJ_INT
* or both.
* Invalidate any prior representations. Every effort is made to force
* the conversion, even if the input is a string that does not look
* completely like a number.Convert as much of the string as we can
* and ignore the rest.
*/
PH7_PRIVATE sxi32 PH7_MemObjToNumeric(ph7_value *pObj)
{
if( pObj->iFlags & (MEMOBJ_INT|MEMOBJ_REAL|MEMOBJ_BOOL|MEMOBJ_NULL) ){
if( pObj->iFlags & (MEMOBJ_BOOL|MEMOBJ_NULL) ){
if( pObj->iFlags & MEMOBJ_NULL ){
pObj->x.iVal = 0;
}
MemObjSetType(pObj,MEMOBJ_INT);
}
/* Already numeric */
return SXRET_OK;
}
if( pObj->iFlags & MEMOBJ_STRING ){
sxi32 rc = SXERR_INVALID;
sxu8 bReal = FALSE;
SyString sString;
SyStringInitFromBuf(&sString,SyBlobData(&pObj->sBlob),SyBlobLength(&pObj->sBlob));
/* Check if the given string looks like a numeric number */
if( sString.nByte > 0 ){
rc = SyStrIsNumeric(sString.zString,sString.nByte,&bReal,0);
}
if( bReal ){
PH7_MemObjToReal(&(*pObj));
}else{
if( rc != SXRET_OK ){
/* The input does not look at all like a number,set the value to 0 */
pObj->x.iVal = 0;
}else{
/* Convert as much as we can */
pObj->x.iVal = MemObjStringToInt(&(*pObj));
}
MemObjSetType(pObj,MEMOBJ_INT);
SyBlobRelease(&pObj->sBlob);
}
}else if(pObj->iFlags & (MEMOBJ_OBJ|MEMOBJ_HASHMAP|MEMOBJ_RES)){
PH7_MemObjToInteger(pObj);
}else{
/* Perform a blind cast */
PH7_MemObjToReal(&(*pObj));
}
return SXRET_OK;
}
/*
* Try a get an integer representation of the given ph7_value.
* If the ph7_value is not of type real,this function is a no-op.
*/
PH7_PRIVATE sxi32 PH7_MemObjTryInteger(ph7_value *pObj)
{
if( pObj->iFlags & MEMOBJ_REAL ){
/* Work only with reals */
MemObjTryIntger(&(*pObj));
}
return SXRET_OK;
}
/*
* Initialize a ph7_value to the null type.
*/
PH7_PRIVATE sxi32 PH7_MemObjInit(ph7_vm *pVm,ph7_value *pObj)
{
/* Zero the structure */
SyZero(pObj,sizeof(ph7_value));
/* Initialize fields */
pObj->pVm = pVm;
SyBlobInit(&pObj->sBlob,&pVm->sAllocator);
/* Set the NULL type */
pObj->iFlags = MEMOBJ_NULL;
return SXRET_OK;
}
/*
* Initialize a ph7_value to the integer type.
*/
PH7_PRIVATE sxi32 PH7_MemObjInitFromInt(ph7_vm *pVm,ph7_value *pObj,sxi64 iVal)
{
/* Zero the structure */
SyZero(pObj,sizeof(ph7_value));
/* Initialize fields */
pObj->pVm = pVm;
SyBlobInit(&pObj->sBlob,&pVm->sAllocator);
/* Set the desired type */
pObj->x.iVal = iVal;
pObj->iFlags = MEMOBJ_INT;
return SXRET_OK;
}
/*
* Initialize a ph7_value to the boolean type.
*/
PH7_PRIVATE sxi32 PH7_MemObjInitFromBool(ph7_vm *pVm,ph7_value *pObj,sxi32 iVal)
{
/* Zero the structure */
SyZero(pObj,sizeof(ph7_value));
/* Initialize fields */
pObj->pVm = pVm;
SyBlobInit(&pObj->sBlob,&pVm->sAllocator);
/* Set the desired type */
pObj->x.iVal = iVal ? 1 : 0;
pObj->iFlags = MEMOBJ_BOOL;
return SXRET_OK;
}
#if 0
/*
* Initialize a ph7_value to the real type.
*/
PH7_PRIVATE sxi32 PH7_MemObjInitFromReal(ph7_vm *pVm,ph7_value *pObj,ph7_real rVal)
{
/* Zero the structure */
SyZero(pObj,sizeof(ph7_value));
/* Initialize fields */
pObj->pVm = pVm;
SyBlobInit(&pObj->sBlob,&pVm->sAllocator);
/* Set the desired type */
pObj->rVal = rVal;
pObj->iFlags = MEMOBJ_REAL;
return SXRET_OK;
}
#endif
/*
* Initialize a ph7_value to the array type.
*/
PH7_PRIVATE sxi32 PH7_MemObjInitFromArray(ph7_vm *pVm,ph7_value *pObj,ph7_hashmap *pArray)
{
/* Zero the structure */
SyZero(pObj,sizeof(ph7_value));
/* Initialize fields */
pObj->pVm = pVm;
SyBlobInit(&pObj->sBlob,&pVm->sAllocator);
/* Set the desired type */
pObj->iFlags = MEMOBJ_HASHMAP;
pObj->x.pOther = pArray;
return SXRET_OK;
}
/*
* Initialize a ph7_value to the string type.
*/
PH7_PRIVATE sxi32 PH7_MemObjInitFromString(ph7_vm *pVm,ph7_value *pObj,const SyString *pVal)
{
/* Zero the structure */
SyZero(pObj,sizeof(ph7_value));
/* Initialize fields */
pObj->pVm = pVm;
SyBlobInit(&pObj->sBlob,&pVm->sAllocator);
if( pVal ){
/* Append contents */
SyBlobAppend(&pObj->sBlob,(const void *)pVal->zString,pVal->nByte);
}
/* Set the desired type */
pObj->iFlags = MEMOBJ_STRING;
return SXRET_OK;
}
/*
* Append some contents to the internal buffer of a given ph7_value.
* If the given ph7_value is not of type string,this function
* invalidate any prior representation and set the string type.
* Then a simple append operation is performed.
*/
PH7_PRIVATE sxi32 PH7_MemObjStringAppend(ph7_value *pObj,const char *zData,sxu32 nLen)
{
sxi32 rc;
if( (pObj->iFlags & MEMOBJ_STRING) == 0 ){
/* Invalidate any prior representation */
PH7_MemObjRelease(pObj);
MemObjSetType(pObj,MEMOBJ_STRING);
}
/* Append contents */
rc = SyBlobAppend(&pObj->sBlob,zData,nLen);
return rc;
}
#if 0
/*
* Format and append some contents to the internal buffer of a given ph7_value.
* If the given ph7_value is not of type string,this function invalidate
* any prior representation and set the string type.
* Then a simple format and append operation is performed.
*/
PH7_PRIVATE sxi32 PH7_MemObjStringFormat(ph7_value *pObj,const char *zFormat,va_list ap)
{
sxi32 rc;
if( (pObj->iFlags & MEMOBJ_STRING) == 0 ){
/* Invalidate any prior representation */
PH7_MemObjRelease(pObj);
MemObjSetType(pObj,MEMOBJ_STRING);
}
/* Format and append contents */
rc = SyBlobFormatAp(&pObj->sBlob,zFormat,ap);
return rc;
}
#endif
/*
* Duplicate the contents of a ph7_value.
*/
PH7_PRIVATE sxi32 PH7_MemObjStore(ph7_value *pSrc,ph7_value *pDest)
{
ph7_class_instance *pObj = 0;
ph7_hashmap *pMap = 0;
sxi32 rc;
if( pSrc->iFlags & MEMOBJ_HASHMAP ){
/* Increment reference count */
((ph7_hashmap *)pSrc->x.pOther)->iRef++;
}else if( pSrc->iFlags & MEMOBJ_OBJ ){
/* Increment reference count */
((ph7_class_instance *)pSrc->x.pOther)->iRef++;
}
if( pDest->iFlags & MEMOBJ_HASHMAP ){
pMap = (ph7_hashmap *)pDest->x.pOther;
}else if( pDest->iFlags & MEMOBJ_OBJ ){
pObj = (ph7_class_instance *)pDest->x.pOther;
}
SyMemcpy((const void *)&(*pSrc),&(*pDest),sizeof(ph7_value)-(sizeof(ph7_vm *)+sizeof(SyBlob)+sizeof(sxu32)));
pDest->iFlags &= ~MEMOBJ_AUX;
rc = SXRET_OK;
if( SyBlobLength(&pSrc->sBlob) > 0 ){
SyBlobReset(&pDest->sBlob);
rc = SyBlobDup(&pSrc->sBlob,&pDest->sBlob);
}else{
if( SyBlobLength(&pDest->sBlob) > 0 ){
SyBlobRelease(&pDest->sBlob);
}
}
if( pMap ){
PH7_HashmapUnref(pMap);
}else if( pObj ){
PH7_ClassInstanceUnref(pObj);
}
return rc;
}
/*
* Duplicate the contents of a ph7_value but do not copy internal
* buffer contents,simply point to it.
*/
PH7_PRIVATE sxi32 PH7_MemObjLoad(ph7_value *pSrc,ph7_value *pDest)
{
SyMemcpy((const void *)&(*pSrc),&(*pDest),
sizeof(ph7_value)-(sizeof(ph7_vm *)+sizeof(SyBlob)+sizeof(sxu32)));
if( pSrc->iFlags & MEMOBJ_HASHMAP ){
/* Increment reference count */
((ph7_hashmap *)pSrc->x.pOther)->iRef++;
}else if( pSrc->iFlags & MEMOBJ_OBJ ){
/* Increment reference count */
((ph7_class_instance *)pSrc->x.pOther)->iRef++;
}
if( SyBlobLength(&pDest->sBlob) > 0 ){
SyBlobRelease(&pDest->sBlob);
}
if( SyBlobLength(&pSrc->sBlob) > 0 ){
SyBlobReadOnly(&pDest->sBlob,SyBlobData(&pSrc->sBlob),SyBlobLength(&pSrc->sBlob));
}
return SXRET_OK;
}
/*
* Invalidate any prior representation of a given ph7_value.
*/
PH7_PRIVATE sxi32 PH7_MemObjRelease(ph7_value *pObj)
{
if( (pObj->iFlags & MEMOBJ_NULL) == 0 ){
if( pObj->iFlags & MEMOBJ_HASHMAP ){
PH7_HashmapUnref((ph7_hashmap *)pObj->x.pOther);
}else if( pObj->iFlags & MEMOBJ_OBJ ){
PH7_ClassInstanceUnref((ph7_class_instance *)pObj->x.pOther);
}
/* Release the internal buffer */
SyBlobRelease(&pObj->sBlob);
/* Invalidate any prior representation */
pObj->iFlags = MEMOBJ_NULL;
}
return SXRET_OK;
}
/*
* Compare two ph7_values.
* Return 0 if the values are equals, > 0 if pObj1 is greater than pObj2
* or < 0 if pObj2 is greater than pObj1.
* Type comparison table taken from the PHP language reference manual.
* Comparisons of $x with PHP functions Expression
* gettype() empty() is_null() isset() boolean : if($x)
* $x = ""; string TRUE FALSE TRUE FALSE
* $x = null NULL TRUE TRUE FALSE FALSE
* var $x; NULL TRUE TRUE FALSE FALSE
* $x is undefined NULL TRUE TRUE FALSE FALSE
* $x = array(); array TRUE FALSE TRUE FALSE
* $x = false; boolean TRUE FALSE TRUE FALSE
* $x = true; boolean FALSE FALSE TRUE TRUE
* $x = 1; integer FALSE FALSE TRUE TRUE
* $x = 42; integer FALSE FALSE TRUE TRUE
* $x = 0; integer TRUE FALSE TRUE FALSE
* $x = -1; integer FALSE FALSE TRUE TRUE
* $x = "1"; string FALSE FALSE TRUE TRUE
* $x = "0"; string TRUE FALSE TRUE FALSE
* $x = "-1"; string FALSE FALSE TRUE TRUE
* $x = "php"; string FALSE FALSE TRUE TRUE
* $x = "true"; string FALSE FALSE TRUE TRUE
* $x = "false"; string FALSE FALSE TRUE TRUE
* Loose comparisons with ==
* TRUE FALSE 1 0 -1 "1" "0" "-1" NULL array() "php" ""
* TRUE TRUE FALSE TRUE FALSE TRUE TRUE FALSE TRUE FALSE FALSE TRUE FALSE
* FALSE FALSE TRUE FALSE TRUE FALSE FALSE TRUE FALSE TRUE TRUE FALSE TRUE
* 1 TRUE FALSE TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
* 0 FALSE TRUE FALSE TRUE FALSE FALSE TRUE FALSE TRUE FALSE TRUE TRUE
* -1 TRUE FALSE FALSE FALSE TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE
* "1" TRUE FALSE TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
* "0" FALSE TRUE FALSE TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE
* "-1" TRUE FALSE FALSE FALSE TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE
* NULL FALSE TRUE FALSE TRUE FALSE FALSE FALSE FALSE TRUE TRUE FALSE TRUE
* array() FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE TRUE TRUE FALSE FALSE
* "php" TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE
* "" FALSE TRUE FALSE TRUE FALSE FALSE FALSE FALSE TRUE FALSE FALSE TRUE
* Strict comparisons with ===
* TRUE FALSE 1 0 -1 "1" "0" "-1" NULL array() "php" ""
* TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
* FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
* 1 FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
* 0 FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
* -1 FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
* "1" FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
* "0" FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE
* "-1" FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE
* NULL FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE
* array() FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE
* "php" FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE
* "" FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE
*/
PH7_PRIVATE sxi32 PH7_MemObjCmp(ph7_value *pObj1,ph7_value *pObj2,int bStrict,int iNest)
{
sxi32 iComb;
sxi32 rc;
if( bStrict ){
sxi32 iF1,iF2;
/* Strict comparisons with === */
iF1 = pObj1->iFlags&~MEMOBJ_AUX;
iF2 = pObj2->iFlags&~MEMOBJ_AUX;
if( iF1 != iF2 ){
/* Not of the same type */
return 1;
}
}
/* Combine flag together */
iComb = pObj1->iFlags|pObj2->iFlags;
if( iComb & (MEMOBJ_NULL|MEMOBJ_RES|MEMOBJ_BOOL) ){
/* Convert to boolean: Keep in mind FALSE < TRUE */
if( (pObj1->iFlags & MEMOBJ_BOOL) == 0 ){
PH7_MemObjToBool(pObj1);
}
if( (pObj2->iFlags & MEMOBJ_BOOL) == 0 ){
PH7_MemObjToBool(pObj2);
}
return (sxi32)((pObj1->x.iVal != 0) - (pObj2->x.iVal != 0));
}else if ( iComb & MEMOBJ_HASHMAP ){
/* Hashmap aka 'array' comparison */
if( (pObj1->iFlags & MEMOBJ_HASHMAP) == 0 ){
/* Array is always greater */
return -1;
}
if( (pObj2->iFlags & MEMOBJ_HASHMAP) == 0 ){
/* Array is always greater */
return 1;
}
/* Perform the comparison */
rc = PH7_HashmapCmp((ph7_hashmap *)pObj1->x.pOther,(ph7_hashmap *)pObj2->x.pOther,bStrict);
return rc;
}else if(iComb & MEMOBJ_OBJ ){
/* Object comparison */
if( (pObj1->iFlags & MEMOBJ_OBJ) == 0 ){
/* Object is always greater */
return -1;
}
if( (pObj2->iFlags & MEMOBJ_OBJ) == 0 ){
/* Object is always greater */
return 1;
}
/* Perform the comparison */
rc = PH7_ClassInstanceCmp((ph7_class_instance *)pObj1->x.pOther,(ph7_class_instance *)pObj2->x.pOther,bStrict,iNest);
return rc;
}else if ( iComb & MEMOBJ_STRING ){
SyString s1,s2;
if( !bStrict ){
/*
* According to the PHP language reference manual:
*
* If you compare a number with a string or the comparison involves numerical
* strings, then each string is converted to a number and the comparison
* performed numerically.
*/
if( PH7_MemObjIsNumeric(pObj1) ){
/* Perform a numeric comparison */
goto Numeric;
}
if( PH7_MemObjIsNumeric(pObj2) ){
/* Perform a numeric comparison */
goto Numeric;
}
}
/* Perform a strict string comparison.*/
if( (pObj1->iFlags&MEMOBJ_STRING) == 0 ){
PH7_MemObjToString(pObj1);
}
if( (pObj2->iFlags&MEMOBJ_STRING) == 0 ){
PH7_MemObjToString(pObj2);
}
SyStringInitFromBuf(&s1,SyBlobData(&pObj1->sBlob),SyBlobLength(&pObj1->sBlob));
SyStringInitFromBuf(&s2,SyBlobData(&pObj2->sBlob),SyBlobLength(&pObj2->sBlob));
/*
* Strings are compared using memcmp(). If one value is an exact prefix of the
* other, then the shorter value is less than the longer value.
*/
rc = SyMemcmp((const void *)s1.zString,(const void *)s2.zString,SXMIN(s1.nByte,s2.nByte));
if( rc == 0 ){
if( s1.nByte != s2.nByte ){
rc = s1.nByte < s2.nByte ? -1 : 1;
}
}
return rc;
}else if( iComb & (MEMOBJ_INT|MEMOBJ_REAL) ){
Numeric:
/* Perform a numeric comparison if one of the operand is numeric(integer or real) */
if( (pObj1->iFlags & (MEMOBJ_INT|MEMOBJ_REAL)) == 0 ){
PH7_MemObjToNumeric(pObj1);
}
if( (pObj2->iFlags & (MEMOBJ_INT|MEMOBJ_REAL)) == 0 ){
PH7_MemObjToNumeric(pObj2);
}
if( (pObj1->iFlags & pObj2->iFlags & MEMOBJ_INT) == 0) {
/*
* Symisc eXtension to the PHP language:
* Floating point comparison is introduced and works as expected.
*/
ph7_real r1,r2;
/* Compare as reals */
if( (pObj1->iFlags & MEMOBJ_REAL) == 0 ){
PH7_MemObjToReal(pObj1);
}
r1 = pObj1->rVal;
if( (pObj2->iFlags & MEMOBJ_REAL) == 0 ){
PH7_MemObjToReal(pObj2);
}
r2 = pObj2->rVal;
if( r1 > r2 ){
return 1;
}else if( r1 < r2 ){
return -1;
}
return 0;
}else{
/* Integer comparison */
if( pObj1->x.iVal > pObj2->x.iVal ){
return 1;
}else if( pObj1->x.iVal < pObj2->x.iVal ){
return -1;
}
return 0;
}
}
/* NOT REACHED */
return 0;
}
/*
* Perform an addition operation of two ph7_values.
* The reason this function is implemented here rather than 'vm.c'
* is that the '+' operator is overloaded.
* That is,the '+' operator is used for arithmetic operation and also
* used for operation on arrays [i.e: union]. When used with an array
* The + operator returns the right-hand array appended to the left-hand array.
* For keys that exist in both arrays, the elements from the left-hand array
* will be used, and the matching elements from the right-hand array will
* be ignored.
* This function take care of handling all the scenarios.
*/
PH7_PRIVATE sxi32 PH7_MemObjAdd(ph7_value *pObj1,ph7_value *pObj2,int bAddStore)
{
if( ((pObj1->iFlags|pObj2->iFlags) & MEMOBJ_HASHMAP) == 0 ){
/* Arithemtic operation */
PH7_MemObjToNumeric(pObj1);
PH7_MemObjToNumeric(pObj2);
if( (pObj1->iFlags|pObj2->iFlags) & MEMOBJ_REAL ){
/* Floating point arithmetic */
ph7_real a,b;
if( (pObj1->iFlags & MEMOBJ_REAL) == 0 ){
PH7_MemObjToReal(pObj1);
}
if( (pObj2->iFlags & MEMOBJ_REAL) == 0 ){
PH7_MemObjToReal(pObj2);
}
a = pObj1->rVal;
b = pObj2->rVal;
pObj1->rVal = a+b;
MemObjSetType(pObj1,MEMOBJ_REAL);
/* Try to get an integer representation also */
MemObjTryIntger(&(*pObj1));
}else{
/* Integer arithmetic */
sxi64 a,b;
a = pObj1->x.iVal;
b = pObj2->x.iVal;
pObj1->x.iVal = a+b;
MemObjSetType(pObj1,MEMOBJ_INT);
}
}else{
if( (pObj1->iFlags|pObj2->iFlags) & MEMOBJ_HASHMAP ){
ph7_hashmap *pMap;
sxi32 rc;
if( bAddStore ){
/* Do not duplicate the hashmap,use the left one since its an add&store operation.
*/
if( (pObj1->iFlags & MEMOBJ_HASHMAP) == 0 ){
/* Force a hashmap cast */
rc = PH7_MemObjToHashmap(pObj1);
if( rc != SXRET_OK ){
PH7_VmThrowError(pObj1->pVm,0,PH7_CTX_ERR,"PH7 is running out of memory while creating array");
return rc;
}
}
/* Point to the structure that describe the hashmap */
pMap = (ph7_hashmap *)pObj1->x.pOther;
}else{
/* Create a new hashmap */
pMap = PH7_NewHashmap(pObj1->pVm,0,0);
if( pMap == 0){
PH7_VmThrowError(pObj1->pVm,0,PH7_CTX_ERR,"PH7 is running out of memory while creating array");
return SXERR_MEM;
}
}
if( !bAddStore ){
if(pObj1->iFlags & MEMOBJ_HASHMAP ){
/* Perform a hashmap duplication */
PH7_HashmapDup((ph7_hashmap *)pObj1->x.pOther,pMap);
}else{
if((pObj1->iFlags & MEMOBJ_NULL) == 0 ){
/* Simple insertion */
PH7_HashmapInsert(pMap,0,pObj1);
}
}
}
/* Perform the union */
if(pObj2->iFlags & MEMOBJ_HASHMAP ){
PH7_HashmapUnion(pMap,(ph7_hashmap *)pObj2->x.pOther);
}else{
if((pObj2->iFlags & MEMOBJ_NULL) == 0 ){
/* Simple insertion */
PH7_HashmapInsert(pMap,0,pObj2);
}
}
/* Reflect the change */
if( pObj1->iFlags & MEMOBJ_STRING ){
SyBlobRelease(&pObj1->sBlob);
}
pObj1->x.pOther = pMap;
MemObjSetType(pObj1,MEMOBJ_HASHMAP);
}
}
return SXRET_OK;
}
/*
* Return a printable representation of the type of a given
* ph7_value.
*/
PH7_PRIVATE const char * PH7_MemObjTypeDump(ph7_value *pVal)
{
const char *zType = "";
if( pVal->iFlags & MEMOBJ_NULL ){
zType = "null";
}else if( pVal->iFlags & MEMOBJ_INT ){
zType = "int";
}else if( pVal->iFlags & MEMOBJ_REAL ){
zType = "float";
}else if( pVal->iFlags & MEMOBJ_STRING ){
zType = "string";
}else if( pVal->iFlags & MEMOBJ_BOOL ){
zType = "bool";
}else if( pVal->iFlags & MEMOBJ_HASHMAP ){
zType = "array";
}else if( pVal->iFlags & MEMOBJ_OBJ ){
zType = "object";
}else if( pVal->iFlags & MEMOBJ_RES ){
zType = "resource";
}
return zType;
}
/*
* Dump a ph7_value [i.e: get a printable representation of it's type and contents.].
* Store the dump in the given blob.
*/
PH7_PRIVATE sxi32 PH7_MemObjDump(
SyBlob *pOut, /* Store the dump here */
ph7_value *pObj, /* Dump this */
int ShowType, /* TRUE to output value type */
int nTab, /* # of Whitespace to insert */
int nDepth, /* Nesting level */
int isRef /* TRUE if referenced object */
)
{
sxi32 rc = SXRET_OK;
const char *zType;
int i;
for( i = 0 ; i < nTab ; i++ ){
SyBlobAppend(&(*pOut)," ",sizeof(char));
}
if( ShowType ){
if( isRef ){
SyBlobAppend(&(*pOut),"&",sizeof(char));
}
/* Get value type first */
zType = PH7_MemObjTypeDump(pObj);
SyBlobAppend(&(*pOut),zType,SyStrlen(zType));
}
if((pObj->iFlags & MEMOBJ_NULL) == 0 ){
if ( ShowType ){
SyBlobAppend(&(*pOut),"(",sizeof(char));
}
if( pObj->iFlags & MEMOBJ_HASHMAP ){
/* Dump hashmap entries */
rc = PH7_HashmapDump(&(*pOut),(ph7_hashmap *)pObj->x.pOther,ShowType,nTab+1,nDepth+1);
}else if(pObj->iFlags & MEMOBJ_OBJ ){
/* Dump class instance attributes */
rc = PH7_ClassInstanceDump(&(*pOut),(ph7_class_instance *)pObj->x.pOther,ShowType,nTab+1,nDepth+1);
}else{
SyBlob *pContents = &pObj->sBlob;
/* Get a printable representation of the contents */
if((pObj->iFlags & MEMOBJ_STRING) == 0 ){
MemObjStringValue(&(*pOut),&(*pObj),FALSE);
}else{
/* Append length first */
if( ShowType ){
SyBlobFormat(&(*pOut),"%u '",SyBlobLength(&pObj->sBlob));
}
if( SyBlobLength(pContents) > 0 ){
SyBlobAppend(&(*pOut),SyBlobData(pContents),SyBlobLength(pContents));
}
if( ShowType ){
SyBlobAppend(&(*pOut),"'",sizeof(char));
}
}
}
if( ShowType ){
if( (pObj->iFlags & (MEMOBJ_HASHMAP|MEMOBJ_OBJ)) == 0 ){
SyBlobAppend(&(*pOut),")",sizeof(char));
}
}
}
#ifdef __WINNT__
SyBlobAppend(&(*pOut),"\r\n",sizeof("\r\n")-1);
#else
SyBlobAppend(&(*pOut),"\n",sizeof(char));
#endif
return rc;
}