1260 lines
42 KiB
C
1260 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 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) {
|
|
#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;
|
|
}
|
|
/* 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->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;
|
|
}
|
|
/*
|
|
* 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;
|
|
}
|
|
/*
|
|
* 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->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_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;
|
|
}
|