belliash
eb79ed500e
All checks were successful
The build was successful.
Single variable cannot have many values of different types. This also saves some memory, because union allocates memory just for one of its members.
1199 lines
40 KiB
C
1199 lines
40 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 variables 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) {
|
|
/*
|
|
** 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->x.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;
|
|
}
|
|
}
|
|
/*
|
|
* 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->x.rVal;
|
|
} else if(iFlags & (MEMOBJ_INT | MEMOBJ_BOOL)) {
|
|
return (ph7_real)pObj->x.iVal;
|
|
} else if(iFlags & MEMOBJ_STRING) {
|
|
SyString sString;
|
|
ph7_real rVal = 0.0;
|
|
SyStringInitFromBuf(&sString, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob));
|
|
if(SyBlobLength(&pObj->sBlob) > 0) {
|
|
/* Convert as much as we can */
|
|
SyStrToReal(sString.zString, sString.nByte, (void *)&rVal, 0);
|
|
}
|
|
return rVal;
|
|
} else if(iFlags & MEMOBJ_NULL) {
|
|
return 0.0;
|
|
} 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.x.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->x.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) {
|
|
return pObj->x.rVal != 0.0 ? 1 : 0;
|
|
} 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;
|
|
}
|
|
/*
|
|
* 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->x.rVal = MemObjRealValue(&(*pObj));
|
|
/* Invalidate any prior representations */
|
|
SyBlobRelease(&pObj->sBlob);
|
|
MemObjSetType(pObj, MEMOBJ_REAL);
|
|
}
|
|
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;
|
|
}
|
|
/* Instantiate 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.
|
|
* According 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->x.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;
|
|
}
|
|
/*
|
|
* 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;
|
|
}
|
|
/*
|
|
* 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->x.rVal = rVal;
|
|
pObj->iFlags = MEMOBJ_REAL;
|
|
return SXRET_OK;
|
|
}
|
|
/*
|
|
* 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;
|
|
}
|
|
/*
|
|
* 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;
|
|
}
|
|
/*
|
|
* 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->x.rVal;
|
|
if((pObj2->iFlags & MEMOBJ_REAL) == 0) {
|
|
PH7_MemObjToReal(pObj2);
|
|
}
|
|
r2 = pObj2->x.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->x.rVal;
|
|
b = pObj2->x.rVal;
|
|
pObj1->x.rVal = a + b;
|
|
MemObjSetType(pObj1, MEMOBJ_REAL);
|
|
} 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_VmMemoryError(pObj1->pVm);
|
|
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_VmMemoryError(pObj1->pVm);
|
|
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;
|
|
}
|