Get rid of dirty references. Return a reference & pass-by reference are still working.
The build was successful.
Details
The build was successful.
Details
This commit is contained in:
parent
a7137316f7
commit
3f205c19ce
|
@ -794,7 +794,6 @@ static sxi32 PH7_GenStateArrayNodeValidator(ph7_gen_state *pGen, ph7_expr_node *
|
||||||
PH7_PRIVATE sxi32 PH7_CompileArray(ph7_gen_state *pGen, sxi32 iCompileFlag) {
|
PH7_PRIVATE sxi32 PH7_CompileArray(ph7_gen_state *pGen, sxi32 iCompileFlag) {
|
||||||
sxi32(*xValidator)(ph7_gen_state *, ph7_expr_node *); /* Expression tree validator callback */
|
sxi32(*xValidator)(ph7_gen_state *, ph7_expr_node *); /* Expression tree validator callback */
|
||||||
SyToken *pKey, *pCur;
|
SyToken *pKey, *pCur;
|
||||||
sxi32 iEmitRef = 0;
|
|
||||||
sxi32 nPair = 0;
|
sxi32 nPair = 0;
|
||||||
sxi32 iNest;
|
sxi32 iNest;
|
||||||
sxi32 rc;
|
sxi32 rc;
|
||||||
|
@ -862,31 +861,12 @@ PH7_PRIVATE sxi32 PH7_CompileArray(ph7_gen_state *pGen, sxi32 iCompileFlag) {
|
||||||
/* No available key,load NULL */
|
/* No available key,load NULL */
|
||||||
PH7_VmEmitInstr(pGen->pVm, 0, PH7_OP_LOADC, 0, 0 /* nil index */, 0, 0);
|
PH7_VmEmitInstr(pGen->pVm, 0, PH7_OP_LOADC, 0, 0 /* nil index */, 0, 0);
|
||||||
}
|
}
|
||||||
if(pCur->nType & PH7_TK_AMPER /*'&'*/) {
|
|
||||||
/* Insertion by reference, [i.e: $a = array(&$x);] */
|
|
||||||
xValidator = PH7_GenStateArrayNodeValidator; /* Only variable are allowed */
|
|
||||||
iEmitRef = 1;
|
|
||||||
pCur++; /* Jump the '&' token */
|
|
||||||
if(pCur >= pGen->pIn) {
|
|
||||||
/* Missing value */
|
|
||||||
rc = PH7_GenCompileError(&(*pGen), E_ERROR, pCur->nLine, "array(): Missing referenced variable");
|
|
||||||
if(rc == SXERR_ABORT) {
|
|
||||||
return SXERR_ABORT;
|
|
||||||
}
|
|
||||||
return SXRET_OK;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Compile indice value */
|
/* Compile indice value */
|
||||||
rc = PH7_GenStateCompileArrayEntry(&(*pGen), pCur, pGen->pIn, EXPR_FLAG_RDONLY_LOAD/*Do not create the variable if non-existent*/, xValidator);
|
rc = PH7_GenStateCompileArrayEntry(&(*pGen), pCur, pGen->pIn, EXPR_FLAG_RDONLY_LOAD/*Do not create the variable if non-existent*/, xValidator);
|
||||||
if(rc == SXERR_ABORT) {
|
if(rc == SXERR_ABORT) {
|
||||||
return SXERR_ABORT;
|
return SXERR_ABORT;
|
||||||
}
|
}
|
||||||
if(iEmitRef) {
|
|
||||||
/* Emit the load reference instruction */
|
|
||||||
PH7_VmEmitInstr(pGen->pVm, 0, PH7_OP_LOAD_REF, 0, 0, 0, 0);
|
|
||||||
}
|
|
||||||
xValidator = 0;
|
xValidator = 0;
|
||||||
iEmitRef = 0;
|
|
||||||
nPair++;
|
nPair++;
|
||||||
}
|
}
|
||||||
/* Emit the load map instruction */
|
/* Emit the load map instruction */
|
||||||
|
@ -5073,21 +5053,6 @@ static sxi32 PH7_GenStateEmitExprCode(
|
||||||
(void)PH7_VmPopInstr(pGen->pVm);
|
(void)PH7_VmPopInstr(pGen->pVm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if(iVmOp == PH7_OP_STORE_REF) {
|
|
||||||
pInstr = PH7_VmPopInstr(pGen->pVm);
|
|
||||||
if(pInstr) {
|
|
||||||
if(pInstr->iOp == PH7_OP_LOAD_IDX) {
|
|
||||||
/* Array insertion by reference [i.e: $pArray[] =& $some_var; ]
|
|
||||||
* We have to convert the STORE_REF instruction into STORE_IDX_REF
|
|
||||||
*/
|
|
||||||
iVmOp = PH7_OP_STORE_IDX_REF;
|
|
||||||
iP1 = pInstr->iP1;
|
|
||||||
iP2 = pInstr->iP2;
|
|
||||||
p3 = pInstr->p3;
|
|
||||||
} else {
|
|
||||||
p3 = pInstr->p3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(iVmOp > 0) {
|
if(iVmOp > 0) {
|
||||||
|
|
233
engine/hashmap.c
233
engine/hashmap.c
|
@ -16,10 +16,6 @@
|
||||||
/* Allowed node types */
|
/* Allowed node types */
|
||||||
#define HASHMAP_INT_NODE 1 /* Node with an int [i.e: 64-bit integer] key */
|
#define HASHMAP_INT_NODE 1 /* Node with an int [i.e: 64-bit integer] key */
|
||||||
#define HASHMAP_BLOB_NODE 2 /* Node with a string/BLOB key */
|
#define HASHMAP_BLOB_NODE 2 /* Node with a string/BLOB key */
|
||||||
/* Node control flags */
|
|
||||||
#define HASHMAP_NODE_FOREIGN_OBJ 0x001 /* Node hold a reference to a foreign ph7_value
|
|
||||||
* [i.e: array(&var)/$a[] =& $var ]
|
|
||||||
*/
|
|
||||||
/*
|
/*
|
||||||
* Default hash function for int [i.e; 64-bit integer] keys.
|
* Default hash function for int [i.e; 64-bit integer] keys.
|
||||||
*/
|
*/
|
||||||
|
@ -193,9 +189,7 @@ PH7_PRIVATE void PH7_HashmapUnlinkNode(ph7_hashmap_node *pNode, int bRestore) {
|
||||||
/* Remove the ph7_value associated with this node from the reference table */
|
/* Remove the ph7_value associated with this node from the reference table */
|
||||||
PH7_VmRefObjRemove(pVm, pNode->nValIdx, 0, pNode);
|
PH7_VmRefObjRemove(pVm, pNode->nValIdx, 0, pNode);
|
||||||
/* Restore to the freelist */
|
/* Restore to the freelist */
|
||||||
if((pNode->iFlags & HASHMAP_NODE_FOREIGN_OBJ) == 0) {
|
PH7_VmUnsetMemObj(pVm, pNode->nValIdx, FALSE);
|
||||||
PH7_VmUnsetMemObj(pVm, pNode->nValIdx, FALSE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if(pNode->iType == HASHMAP_BLOB_NODE) {
|
if(pNode->iType == HASHMAP_BLOB_NODE) {
|
||||||
SyBlobRelease(&pNode->xKey.sKey);
|
SyBlobRelease(&pNode->xKey.sKey);
|
||||||
|
@ -271,26 +265,22 @@ static sxi32 HashmapGrowBucket(ph7_hashmap *pMap) {
|
||||||
* Insert a 64-bit integer key and it's associated value (if any) in the given
|
* Insert a 64-bit integer key and it's associated value (if any) in the given
|
||||||
* hashmap.
|
* hashmap.
|
||||||
*/
|
*/
|
||||||
static sxi32 HashmapInsertIntKey(ph7_hashmap *pMap, sxi64 iKey, ph7_value *pValue, sxu32 nRefIdx, int isForeign) {
|
static sxi32 HashmapInsertIntKey(ph7_hashmap *pMap, sxi64 iKey, ph7_value *pValue, sxu32 nRefIdx) {
|
||||||
ph7_hashmap_node *pNode;
|
ph7_hashmap_node *pNode;
|
||||||
sxu32 nIdx;
|
sxu32 nIdx;
|
||||||
sxu32 nHash;
|
sxu32 nHash;
|
||||||
sxi32 rc;
|
sxi32 rc;
|
||||||
if(!isForeign) {
|
ph7_value *pObj;
|
||||||
ph7_value *pObj;
|
/* Reserve a ph7_value for the value */
|
||||||
/* Reserve a ph7_value for the value */
|
pObj = PH7_ReserveMemObj(pMap->pVm);
|
||||||
pObj = PH7_ReserveMemObj(pMap->pVm);
|
if(pObj == 0) {
|
||||||
if(pObj == 0) {
|
return SXERR_MEM;
|
||||||
return SXERR_MEM;
|
|
||||||
}
|
|
||||||
if(pValue) {
|
|
||||||
/* Duplicate the value */
|
|
||||||
PH7_MemObjStore(pValue, pObj);
|
|
||||||
}
|
|
||||||
nIdx = pObj->nIdx;
|
|
||||||
} else {
|
|
||||||
nIdx = nRefIdx;
|
|
||||||
}
|
}
|
||||||
|
if(pValue) {
|
||||||
|
/* Duplicate the value */
|
||||||
|
PH7_MemObjStore(pValue, pObj);
|
||||||
|
}
|
||||||
|
nIdx = pObj->nIdx;
|
||||||
/* Hash the key */
|
/* Hash the key */
|
||||||
nHash = pMap->xIntHash(iKey);
|
nHash = pMap->xIntHash(iKey);
|
||||||
/* Allocate a new int node */
|
/* Allocate a new int node */
|
||||||
|
@ -298,10 +288,6 @@ static sxi32 HashmapInsertIntKey(ph7_hashmap *pMap, sxi64 iKey, ph7_value *pValu
|
||||||
if(pNode == 0) {
|
if(pNode == 0) {
|
||||||
return SXERR_MEM;
|
return SXERR_MEM;
|
||||||
}
|
}
|
||||||
if(isForeign) {
|
|
||||||
/* Mark as a foregin entry */
|
|
||||||
pNode->iFlags |= HASHMAP_NODE_FOREIGN_OBJ;
|
|
||||||
}
|
|
||||||
/* Make sure the bucket is big enough to hold the new entry */
|
/* Make sure the bucket is big enough to hold the new entry */
|
||||||
rc = HashmapGrowBucket(&(*pMap));
|
rc = HashmapGrowBucket(&(*pMap));
|
||||||
if(rc != SXRET_OK) {
|
if(rc != SXRET_OK) {
|
||||||
|
@ -319,26 +305,22 @@ static sxi32 HashmapInsertIntKey(ph7_hashmap *pMap, sxi64 iKey, ph7_value *pValu
|
||||||
* Insert a BLOB key and it's associated value (if any) in the given
|
* Insert a BLOB key and it's associated value (if any) in the given
|
||||||
* hashmap.
|
* hashmap.
|
||||||
*/
|
*/
|
||||||
static sxi32 HashmapInsertBlobKey(ph7_hashmap *pMap, const void *pKey, sxu32 nKeyLen, ph7_value *pValue, sxu32 nRefIdx, int isForeign) {
|
static sxi32 HashmapInsertBlobKey(ph7_hashmap *pMap, const void *pKey, sxu32 nKeyLen, ph7_value *pValue, sxu32 nRefIdx) {
|
||||||
ph7_hashmap_node *pNode;
|
ph7_hashmap_node *pNode;
|
||||||
sxu32 nHash;
|
sxu32 nHash;
|
||||||
sxu32 nIdx;
|
sxu32 nIdx;
|
||||||
sxi32 rc;
|
sxi32 rc;
|
||||||
if(!isForeign) {
|
ph7_value *pObj;
|
||||||
ph7_value *pObj;
|
/* Reserve a ph7_value for the value */
|
||||||
/* Reserve a ph7_value for the value */
|
pObj = PH7_ReserveMemObj(pMap->pVm);
|
||||||
pObj = PH7_ReserveMemObj(pMap->pVm);
|
if(pObj == 0) {
|
||||||
if(pObj == 0) {
|
return SXERR_MEM;
|
||||||
return SXERR_MEM;
|
|
||||||
}
|
|
||||||
if(pValue) {
|
|
||||||
/* Duplicate the value */
|
|
||||||
PH7_MemObjStore(pValue, pObj);
|
|
||||||
}
|
|
||||||
nIdx = pObj->nIdx;
|
|
||||||
} else {
|
|
||||||
nIdx = nRefIdx;
|
|
||||||
}
|
}
|
||||||
|
if(pValue) {
|
||||||
|
/* Duplicate the value */
|
||||||
|
PH7_MemObjStore(pValue, pObj);
|
||||||
|
}
|
||||||
|
nIdx = pObj->nIdx;
|
||||||
/* Hash the key */
|
/* Hash the key */
|
||||||
nHash = pMap->xBlobHash(pKey, nKeyLen);
|
nHash = pMap->xBlobHash(pKey, nKeyLen);
|
||||||
/* Allocate a new blob node */
|
/* Allocate a new blob node */
|
||||||
|
@ -346,10 +328,6 @@ static sxi32 HashmapInsertBlobKey(ph7_hashmap *pMap, const void *pKey, sxu32 nKe
|
||||||
if(pNode == 0) {
|
if(pNode == 0) {
|
||||||
return SXERR_MEM;
|
return SXERR_MEM;
|
||||||
}
|
}
|
||||||
if(isForeign) {
|
|
||||||
/* Mark as a foregin entry */
|
|
||||||
pNode->iFlags |= HASHMAP_NODE_FOREIGN_OBJ;
|
|
||||||
}
|
|
||||||
/* Make sure the bucket is big enough to hold the new entry */
|
/* Make sure the bucket is big enough to hold the new entry */
|
||||||
rc = HashmapGrowBucket(&(*pMap));
|
rc = HashmapGrowBucket(&(*pMap));
|
||||||
if(rc != SXRET_OK) {
|
if(rc != SXRET_OK) {
|
||||||
|
@ -553,7 +531,7 @@ static sxi32 HashmapInsert(
|
||||||
return SXRET_OK;
|
return SXRET_OK;
|
||||||
}
|
}
|
||||||
/* Perform a blob-key insertion */
|
/* Perform a blob-key insertion */
|
||||||
rc = HashmapInsertBlobKey(&(*pMap), SyBlobData(&pKey->sBlob), SyBlobLength(&pKey->sBlob), &(*pVal), 0, FALSE);
|
rc = HashmapInsertBlobKey(&(*pMap), SyBlobData(&pKey->sBlob), SyBlobLength(&pKey->sBlob), &(*pVal), 0);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
IntKey:
|
IntKey:
|
||||||
|
@ -577,7 +555,7 @@ IntKey:
|
||||||
return SXRET_OK;
|
return SXRET_OK;
|
||||||
}
|
}
|
||||||
/* Perform a 64-bit-int-key insertion */
|
/* Perform a 64-bit-int-key insertion */
|
||||||
rc = HashmapInsertIntKey(&(*pMap), pKey->x.iVal, &(*pVal), 0, FALSE);
|
rc = HashmapInsertIntKey(&(*pMap), pKey->x.iVal, &(*pVal), 0);
|
||||||
if(rc == SXRET_OK) {
|
if(rc == SXRET_OK) {
|
||||||
if(pKey->x.iVal >= pMap->iNextIdx) {
|
if(pKey->x.iVal >= pMap->iNextIdx) {
|
||||||
/* Increment the automatic index */
|
/* Increment the automatic index */
|
||||||
|
@ -590,102 +568,7 @@ IntKey:
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* Assign an automatic index */
|
/* Assign an automatic index */
|
||||||
rc = HashmapInsertIntKey(&(*pMap), pMap->iNextIdx, &(*pVal), 0, FALSE);
|
rc = HashmapInsertIntKey(&(*pMap), pMap->iNextIdx, &(*pVal), 0);
|
||||||
if(rc == SXRET_OK) {
|
|
||||||
++pMap->iNextIdx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Insertion result */
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* Insert a given key and it's associated value (foreign index) in the given
|
|
||||||
* hashmap.
|
|
||||||
* This is insertion by reference so be careful to mark the node
|
|
||||||
* with the HASHMAP_NODE_FOREIGN_OBJ flag being set.
|
|
||||||
* The insertion by reference is triggered when the following
|
|
||||||
* expression is encountered.
|
|
||||||
* $var = 10;
|
|
||||||
* $a = array(&var);
|
|
||||||
* OR
|
|
||||||
* $a[] =& $var;
|
|
||||||
* That is,$var is a foreign ph7_value and the $a array have no control
|
|
||||||
* over it's contents.
|
|
||||||
* Note that the node that hold the foreign ph7_value is automatically
|
|
||||||
* removed when the foreign ph7_value is unset.
|
|
||||||
* Example:
|
|
||||||
* $var = 10;
|
|
||||||
* $a[] =& $var;
|
|
||||||
* echo count($a).PHP_EOL; //1
|
|
||||||
* //Unset the foreign ph7_value now
|
|
||||||
* unset($var);
|
|
||||||
* echo count($a); //0
|
|
||||||
* Note that this is a PH7 eXtension.
|
|
||||||
* Refer to the official documentation for more information.
|
|
||||||
* If a node with the given key already exists in the database
|
|
||||||
* then this function overwrite the old value.
|
|
||||||
*/
|
|
||||||
static sxi32 HashmapInsertByRef(
|
|
||||||
ph7_hashmap *pMap, /* Target hashmap */
|
|
||||||
ph7_value *pKey, /* Lookup key */
|
|
||||||
sxu32 nRefIdx /* Foreign ph7_value index */
|
|
||||||
) {
|
|
||||||
ph7_hashmap_node *pNode = 0;
|
|
||||||
sxi32 rc = SXRET_OK;
|
|
||||||
if(pKey && pKey->iFlags & (MEMOBJ_STRING | MEMOBJ_HASHMAP | MEMOBJ_OBJ | MEMOBJ_RES)) {
|
|
||||||
if((pKey->iFlags & MEMOBJ_STRING) == 0) {
|
|
||||||
/* Force a string cast */
|
|
||||||
PH7_MemObjToString(&(*pKey));
|
|
||||||
}
|
|
||||||
if(SyBlobLength(&pKey->sBlob) < 1 || HashmapIsIntKey(&pKey->sBlob)) {
|
|
||||||
if(SyBlobLength(&pKey->sBlob) < 1) {
|
|
||||||
/* Automatic index assign */
|
|
||||||
pKey = 0;
|
|
||||||
}
|
|
||||||
goto IntKey;
|
|
||||||
}
|
|
||||||
if(SXRET_OK == HashmapLookupBlobKey(&(*pMap), SyBlobData(&pKey->sBlob),
|
|
||||||
SyBlobLength(&pKey->sBlob), &pNode)) {
|
|
||||||
/* Overwrite */
|
|
||||||
PH7_VmRefObjRemove(pMap->pVm, pNode->nValIdx, 0, pNode);
|
|
||||||
pNode->nValIdx = nRefIdx;
|
|
||||||
/* Install in the reference table */
|
|
||||||
PH7_VmRefObjInstall(pMap->pVm, nRefIdx, 0, pNode, 0);
|
|
||||||
return SXRET_OK;
|
|
||||||
}
|
|
||||||
/* Perform a blob-key insertion */
|
|
||||||
rc = HashmapInsertBlobKey(&(*pMap), SyBlobData(&pKey->sBlob), SyBlobLength(&pKey->sBlob), 0, nRefIdx, TRUE);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
IntKey:
|
|
||||||
if(pKey) {
|
|
||||||
if((pKey->iFlags & MEMOBJ_INT) == 0) {
|
|
||||||
/* Force an integer cast */
|
|
||||||
PH7_MemObjToInteger(pKey);
|
|
||||||
}
|
|
||||||
if(SXRET_OK == HashmapLookupIntKey(&(*pMap), pKey->x.iVal, &pNode)) {
|
|
||||||
/* Overwrite */
|
|
||||||
PH7_VmRefObjRemove(pMap->pVm, pNode->nValIdx, 0, pNode);
|
|
||||||
pNode->nValIdx = nRefIdx;
|
|
||||||
/* Install in the reference table */
|
|
||||||
PH7_VmRefObjInstall(pMap->pVm, nRefIdx, 0, pNode, 0);
|
|
||||||
return SXRET_OK;
|
|
||||||
}
|
|
||||||
/* Perform a 64-bit-int-key insertion */
|
|
||||||
rc = HashmapInsertIntKey(&(*pMap), pKey->x.iVal, 0, nRefIdx, TRUE);
|
|
||||||
if(rc == SXRET_OK) {
|
|
||||||
if(pKey->x.iVal >= pMap->iNextIdx) {
|
|
||||||
/* Increment the automatic index */
|
|
||||||
pMap->iNextIdx = pKey->x.iVal + 1;
|
|
||||||
/* Make sure the automatic index is not reserved */
|
|
||||||
while(SXRET_OK == HashmapLookupIntKey(&(*pMap), pMap->iNextIdx, 0)) {
|
|
||||||
pMap->iNextIdx++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* Assign an automatic index */
|
|
||||||
rc = HashmapInsertIntKey(&(*pMap), pMap->iNextIdx, 0, nRefIdx, TRUE);
|
|
||||||
if(rc == SXRET_OK) {
|
if(rc == SXRET_OK) {
|
||||||
++pMap->iNextIdx;
|
++pMap->iNextIdx;
|
||||||
}
|
}
|
||||||
|
@ -693,6 +576,7 @@ IntKey:
|
||||||
/* Insertion result */
|
/* Insertion result */
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Extract node value.
|
* Extract node value.
|
||||||
*/
|
*/
|
||||||
|
@ -722,12 +606,12 @@ static sxi32 HashmapInsertNode(ph7_hashmap *pMap, ph7_hashmap_node *pNode, int b
|
||||||
/* Assign an automatic index */
|
/* Assign an automatic index */
|
||||||
rc = HashmapInsert(&(*pMap), 0, pObj);
|
rc = HashmapInsert(&(*pMap), 0, pObj);
|
||||||
} else {
|
} else {
|
||||||
rc = HashmapInsertIntKey(&(*pMap), pNode->xKey.iKey, pObj, 0, FALSE);
|
rc = HashmapInsertIntKey(&(*pMap), pNode->xKey.iKey, pObj, 0);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* Blob key */
|
/* Blob key */
|
||||||
rc = HashmapInsertBlobKey(&(*pMap), SyBlobData(&pNode->xKey.sKey),
|
rc = HashmapInsertBlobKey(&(*pMap), SyBlobData(&pNode->xKey.sKey),
|
||||||
SyBlobLength(&pNode->xKey.sKey), pObj, 0, FALSE);
|
SyBlobLength(&pNode->xKey.sKey), pObj, 0);
|
||||||
}
|
}
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -821,8 +705,8 @@ static int HashmapFindValue(
|
||||||
pVal = HashmapExtractNodeValue(pEntry);
|
pVal = HashmapExtractNodeValue(pEntry);
|
||||||
if(pVal) {
|
if(pVal) {
|
||||||
if((pVal->iFlags | pNeedle->iFlags) & MEMOBJ_NULL) {
|
if((pVal->iFlags | pNeedle->iFlags) & MEMOBJ_NULL) {
|
||||||
sxi32 iF1 = pVal->iFlags & ~MEMOBJ_AUX;
|
sxi32 iF1 = pVal->iFlags;
|
||||||
sxi32 iF2 = pNeedle->iFlags & ~MEMOBJ_AUX;
|
sxi32 iF2 = pNeedle->iFlags;
|
||||||
if(iF1 == iF2) {
|
if(iF1 == iF2) {
|
||||||
/* NULL values are equals */
|
/* NULL values are equals */
|
||||||
if(ppNode) {
|
if(ppNode) {
|
||||||
|
@ -1144,7 +1028,7 @@ PH7_PRIVATE sxi32 PH7_HashmapDup(ph7_hashmap *pSrc, ph7_hashmap *pDest) {
|
||||||
PH7_MemObjRelease(&sKey);
|
PH7_MemObjRelease(&sKey);
|
||||||
} else {
|
} else {
|
||||||
/* Int key insertion */
|
/* Int key insertion */
|
||||||
rc = HashmapInsertIntKey(&(*pDest), pEntry->xKey.iKey, pVal, 0, FALSE);
|
rc = HashmapInsertIntKey(&(*pDest), pEntry->xKey.iKey, pVal, 0);
|
||||||
}
|
}
|
||||||
if(rc != SXRET_OK) {
|
if(rc != SXRET_OK) {
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -1214,7 +1098,7 @@ PH7_PRIVATE sxi32 PH7_HashmapUnion(ph7_hashmap *pLeft, ph7_hashmap *pRight) {
|
||||||
if(pObj) {
|
if(pObj) {
|
||||||
/* Perform the insertion */
|
/* Perform the insertion */
|
||||||
rc = HashmapInsertBlobKey(&(*pLeft), SyBlobData(&pEntry->xKey.sKey), SyBlobLength(&pEntry->xKey.sKey),
|
rc = HashmapInsertBlobKey(&(*pLeft), SyBlobData(&pEntry->xKey.sKey), SyBlobLength(&pEntry->xKey.sKey),
|
||||||
pObj, 0, FALSE);
|
pObj, 0);
|
||||||
if(rc != SXRET_OK) {
|
if(rc != SXRET_OK) {
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -1226,7 +1110,7 @@ PH7_PRIVATE sxi32 PH7_HashmapUnion(ph7_hashmap *pLeft, ph7_hashmap *pRight) {
|
||||||
pObj = HashmapExtractNodeValue(pEntry);
|
pObj = HashmapExtractNodeValue(pEntry);
|
||||||
if(pObj) {
|
if(pObj) {
|
||||||
/* Perform the insertion */
|
/* Perform the insertion */
|
||||||
rc = HashmapInsertIntKey(&(*pLeft), pEntry->xKey.iKey, pObj, 0, FALSE);
|
rc = HashmapInsertIntKey(&(*pLeft), pEntry->xKey.iKey, pObj, 0);
|
||||||
if(rc != SXRET_OK) {
|
if(rc != SXRET_OK) {
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -1345,10 +1229,8 @@ PH7_PRIVATE sxi32 PH7_HashmapRelease(ph7_hashmap *pMap, int FreeDS) {
|
||||||
pNext = pEntry->pPrev; /* Reverse link */
|
pNext = pEntry->pPrev; /* Reverse link */
|
||||||
/* Remove the reference from the foreign table */
|
/* Remove the reference from the foreign table */
|
||||||
PH7_VmRefObjRemove(pVm, pEntry->nValIdx, 0, pEntry);
|
PH7_VmRefObjRemove(pVm, pEntry->nValIdx, 0, pEntry);
|
||||||
if((pEntry->iFlags & HASHMAP_NODE_FOREIGN_OBJ) == 0) {
|
/* Restore the ph7_value to the free list */
|
||||||
/* Restore the ph7_value to the free list */
|
PH7_VmUnsetMemObj(pVm, pEntry->nValIdx, FALSE);
|
||||||
PH7_VmUnsetMemObj(pVm, pEntry->nValIdx, FALSE);
|
|
||||||
}
|
|
||||||
/* Release the node */
|
/* Release the node */
|
||||||
if(pEntry->iType == HASHMAP_BLOB_NODE) {
|
if(pEntry->iType == HASHMAP_BLOB_NODE) {
|
||||||
SyBlobRelease(&pEntry->xKey.sKey);
|
SyBlobRelease(&pEntry->xKey.sKey);
|
||||||
|
@ -1419,40 +1301,7 @@ PH7_PRIVATE sxi32 PH7_HashmapInsert(
|
||||||
) {
|
) {
|
||||||
return HashmapInsert(&(*pMap), &(*pKey), &(*pVal));
|
return HashmapInsert(&(*pMap), &(*pKey), &(*pVal));
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
* Insert a given key and it's associated value (foreign index) in the given
|
|
||||||
* hashmap.
|
|
||||||
* This is insertion by reference so be careful to mark the node
|
|
||||||
* with the HASHMAP_NODE_FOREIGN_OBJ flag being set.
|
|
||||||
* The insertion by reference is triggered when the following
|
|
||||||
* expression is encountered.
|
|
||||||
* $var = 10;
|
|
||||||
* $a = array(&var);
|
|
||||||
* OR
|
|
||||||
* $a[] =& $var;
|
|
||||||
* That is,$var is a foreign ph7_value and the $a array have no control
|
|
||||||
* over it's contents.
|
|
||||||
* Note that the node that hold the foreign ph7_value is automatically
|
|
||||||
* removed when the foreign ph7_value is unset.
|
|
||||||
* Example:
|
|
||||||
* $var = 10;
|
|
||||||
* $a[] =& $var;
|
|
||||||
* echo count($a).PHP_EOL; //1
|
|
||||||
* //Unset the foreign ph7_value now
|
|
||||||
* unset($var);
|
|
||||||
* echo count($a); //0
|
|
||||||
* Note that this is a PH7 eXtension.
|
|
||||||
* Refer to the official documentation for more information.
|
|
||||||
* If a node with the given key already exists in the database
|
|
||||||
* then this function overwrite the old value.
|
|
||||||
*/
|
|
||||||
PH7_PRIVATE sxi32 PH7_HashmapInsertByRef(
|
|
||||||
ph7_hashmap *pMap, /* Target hashmap */
|
|
||||||
ph7_value *pKey, /* Lookup key */
|
|
||||||
sxu32 nRefIdx /* Foreign ph7_value index */
|
|
||||||
) {
|
|
||||||
return HashmapInsertByRef(&(*pMap), &(*pKey), nRefIdx);
|
|
||||||
}
|
|
||||||
/*
|
/*
|
||||||
* Reset the node cursor of a given hashmap.
|
* Reset the node cursor of a given hashmap.
|
||||||
*/
|
*/
|
||||||
|
@ -5500,7 +5349,6 @@ PH7_PRIVATE sxi32 PH7_HashmapDump(SyBlob *pOut, ph7_hashmap *pMap, int ShowType,
|
||||||
ph7_hashmap_node *pEntry;
|
ph7_hashmap_node *pEntry;
|
||||||
ph7_value *pObj;
|
ph7_value *pObj;
|
||||||
sxu32 n = 0;
|
sxu32 n = 0;
|
||||||
int isRef;
|
|
||||||
sxi32 rc;
|
sxi32 rc;
|
||||||
int i;
|
int i;
|
||||||
if(nDepth > 31) {
|
if(nDepth > 31) {
|
||||||
|
@ -5547,13 +5395,8 @@ PH7_PRIVATE sxi32 PH7_HashmapDump(SyBlob *pOut, ph7_hashmap *pMap, int ShowType,
|
||||||
#endif
|
#endif
|
||||||
/* Dump node value */
|
/* Dump node value */
|
||||||
pObj = HashmapExtractNodeValue(pEntry);
|
pObj = HashmapExtractNodeValue(pEntry);
|
||||||
isRef = 0;
|
|
||||||
if(pObj) {
|
if(pObj) {
|
||||||
if(pEntry->iFlags & HASHMAP_NODE_FOREIGN_OBJ) {
|
rc = PH7_MemObjDump(&(*pOut), pObj, ShowType, nTab + 1, nDepth);
|
||||||
/* Referenced object */
|
|
||||||
isRef = 1;
|
|
||||||
}
|
|
||||||
rc = PH7_MemObjDump(&(*pOut), pObj, ShowType, nTab + 1, nDepth, isRef);
|
|
||||||
if(rc == SXERR_LIMIT) {
|
if(rc == SXERR_LIMIT) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -812,7 +812,6 @@ PH7_PRIVATE sxi32 PH7_MemObjStore(ph7_value *pSrc, ph7_value *pDest) {
|
||||||
pObj = (ph7_class_instance *)pDest->x.pOther;
|
pObj = (ph7_class_instance *)pDest->x.pOther;
|
||||||
}
|
}
|
||||||
SyMemcpy((const void *) & (*pSrc), &(*pDest), sizeof(ph7_value) - (sizeof(ph7_vm *) + sizeof(SyBlob) + sizeof(sxu32)));
|
SyMemcpy((const void *) & (*pSrc), &(*pDest), sizeof(ph7_value) - (sizeof(ph7_vm *) + sizeof(SyBlob) + sizeof(sxu32)));
|
||||||
pDest->iFlags &= ~MEMOBJ_AUX;
|
|
||||||
rc = SXRET_OK;
|
rc = SXRET_OK;
|
||||||
if(SyBlobLength(&pSrc->sBlob) > 0) {
|
if(SyBlobLength(&pSrc->sBlob) > 0) {
|
||||||
SyBlobReset(&pDest->sBlob);
|
SyBlobReset(&pDest->sBlob);
|
||||||
|
@ -931,8 +930,8 @@ PH7_PRIVATE sxi32 PH7_MemObjCmp(ph7_value *pObj1, ph7_value *pObj2, int bStrict,
|
||||||
if(bStrict) {
|
if(bStrict) {
|
||||||
sxi32 iF1, iF2;
|
sxi32 iF1, iF2;
|
||||||
/* Strict comparisons with === */
|
/* Strict comparisons with === */
|
||||||
iF1 = pObj1->iFlags & ~MEMOBJ_AUX;
|
iF1 = pObj1->iFlags;
|
||||||
iF2 = pObj2->iFlags & ~MEMOBJ_AUX;
|
iF2 = pObj2->iFlags;
|
||||||
if(iF1 != iF2) {
|
if(iF1 != iF2) {
|
||||||
/* Not of the same type */
|
/* Not of the same type */
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -1206,8 +1205,7 @@ PH7_PRIVATE sxi32 PH7_MemObjDump(
|
||||||
ph7_value *pObj, /* Dump this */
|
ph7_value *pObj, /* Dump this */
|
||||||
int ShowType, /* TRUE to output value type */
|
int ShowType, /* TRUE to output value type */
|
||||||
int nTab, /* # of Whitespace to insert */
|
int nTab, /* # of Whitespace to insert */
|
||||||
int nDepth, /* Nesting level */
|
int nDepth /* Nesting level */
|
||||||
int isRef /* TRUE if referenced object */
|
|
||||||
) {
|
) {
|
||||||
sxi32 rc = SXRET_OK;
|
sxi32 rc = SXRET_OK;
|
||||||
const char *zType;
|
const char *zType;
|
||||||
|
@ -1216,9 +1214,6 @@ PH7_PRIVATE sxi32 PH7_MemObjDump(
|
||||||
SyBlobAppend(&(*pOut), " ", sizeof(char));
|
SyBlobAppend(&(*pOut), " ", sizeof(char));
|
||||||
}
|
}
|
||||||
if(ShowType) {
|
if(ShowType) {
|
||||||
if(isRef) {
|
|
||||||
SyBlobAppend(&(*pOut), "&", sizeof(char));
|
|
||||||
}
|
|
||||||
/* Get value type first */
|
/* Get value type first */
|
||||||
zType = PH7_MemObjTypeDump(pObj);
|
zType = PH7_MemObjTypeDump(pObj);
|
||||||
SyBlobAppend(&(*pOut), zType, SyStrlen(zType));
|
SyBlobAppend(&(*pOut), zType, SyStrlen(zType));
|
||||||
|
|
|
@ -929,7 +929,7 @@ PH7_PRIVATE sxi32 PH7_ClassInstanceDump(SyBlob *pOut, ph7_class_instance *pThis,
|
||||||
#else
|
#else
|
||||||
SyBlobAppend(&(*pOut), "\n", sizeof(char));
|
SyBlobAppend(&(*pOut), "\n", sizeof(char));
|
||||||
#endif
|
#endif
|
||||||
rc = PH7_MemObjDump(&(*pOut), pValue, ShowType, nTab + 1, nDepth, 0);
|
rc = PH7_MemObjDump(&(*pOut), pValue, ShowType, nTab + 1, nDepth);
|
||||||
if(rc == SXERR_LIMIT) {
|
if(rc == SXERR_LIMIT) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -209,8 +209,6 @@ static const ph7_expr_op aOpTable[] = {
|
||||||
{ {"!==", sizeof(char) * 3}, EXPR_OP_TNE, 11, EXPR_OP_NON_ASSOC, PH7_OP_TNE},
|
{ {"!==", sizeof(char) * 3}, EXPR_OP_TNE, 11, EXPR_OP_NON_ASSOC, PH7_OP_TNE},
|
||||||
/* Precedence 12,left-associative */
|
/* Precedence 12,left-associative */
|
||||||
{ {"&", sizeof(char)}, EXPR_OP_BAND, 12, EXPR_OP_ASSOC_LEFT, PH7_OP_BAND},
|
{ {"&", sizeof(char)}, EXPR_OP_BAND, 12, EXPR_OP_ASSOC_LEFT, PH7_OP_BAND},
|
||||||
/* Precedence 12,left-associative */
|
|
||||||
{ {"=&", sizeof(char) * 2}, EXPR_OP_REF, 12, EXPR_OP_ASSOC_LEFT, PH7_OP_STORE_REF},
|
|
||||||
/* Binary operators */
|
/* Binary operators */
|
||||||
/* Precedence 13,left-associative */
|
/* Precedence 13,left-associative */
|
||||||
{ {"^", sizeof(char)}, EXPR_OP_XOR, 13, EXPR_OP_ASSOC_LEFT, PH7_OP_BXOR},
|
{ {"^", sizeof(char)}, EXPR_OP_XOR, 13, EXPR_OP_ASSOC_LEFT, PH7_OP_BXOR},
|
||||||
|
@ -859,13 +857,6 @@ static sxi32 ExprProcessFuncArguments(ph7_gen_state *pGen, ph7_expr_node *pOp, p
|
||||||
iCur++;
|
iCur++;
|
||||||
}
|
}
|
||||||
if(iCur > iNode) {
|
if(iCur > iNode) {
|
||||||
if(apNode[iNode] && (apNode[iNode]->pStart->nType & PH7_TK_AMPER /*'&'*/) && ((iCur - iNode) == 2)
|
|
||||||
&& apNode[iNode + 1]->xCode == PH7_CompileVariable) {
|
|
||||||
PH7_GenCompileError(&(*pGen), E_WARNING, apNode[iNode]->pStart->nLine,
|
|
||||||
"call-time pass-by-reference is deprecated");
|
|
||||||
ExprFreeTree(&(*pGen), apNode[iNode]);
|
|
||||||
apNode[iNode] = 0;
|
|
||||||
}
|
|
||||||
ExprMakeTree(&(*pGen), &apNode[iNode], iCur - iNode);
|
ExprMakeTree(&(*pGen), &apNode[iNode], iCur - iNode);
|
||||||
if(apNode[iNode]) {
|
if(apNode[iNode]) {
|
||||||
/* Put a pointer to the root of the tree in the arguments set */
|
/* Put a pointer to the root of the tree in the arguments set */
|
||||||
|
|
321
engine/vm.c
321
engine/vm.c
|
@ -1324,24 +1324,6 @@ PH7_PRIVATE ph7_value *PH7_ReserveMemObj(ph7_vm *pVm) {
|
||||||
pObj->nIdx = nIdx;
|
pObj->nIdx = nIdx;
|
||||||
return pObj;
|
return pObj;
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
* Insert an entry by reference (not copy) in the given hashmap.
|
|
||||||
*/
|
|
||||||
static sxi32 VmHashmapRefInsert(
|
|
||||||
ph7_hashmap *pMap, /* Target hashmap */
|
|
||||||
const char *zKey, /* Entry key */
|
|
||||||
sxu32 nByte, /* Key length */
|
|
||||||
sxu32 nRefIdx /* Entry index in the object pool */
|
|
||||||
) {
|
|
||||||
ph7_value sKey;
|
|
||||||
sxi32 rc;
|
|
||||||
PH7_MemObjInitFromString(pMap->pVm, &sKey, 0);
|
|
||||||
PH7_MemObjStringAppend(&sKey, zKey, nByte);
|
|
||||||
/* Perform the insertion */
|
|
||||||
rc = PH7_HashmapInsertByRef(&(*pMap), &sKey, nRefIdx);
|
|
||||||
PH7_MemObjRelease(&sKey);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
/*
|
/*
|
||||||
* Extract a variable value from the top active VM frame.
|
* Extract a variable value from the top active VM frame.
|
||||||
* Return a pointer to the variable value on success.
|
* Return a pointer to the variable value on success.
|
||||||
|
@ -2492,26 +2474,14 @@ static sxi32 VmByteCodeExec(
|
||||||
iFlags = pEntry[1].iFlags; /* Save the type of value */
|
iFlags = pEntry[1].iFlags; /* Save the type of value */
|
||||||
/* Perform the insertion */
|
/* Perform the insertion */
|
||||||
while(pEntry < pTos) {
|
while(pEntry < pTos) {
|
||||||
if(pEntry[1].iFlags & MEMOBJ_REFERENCE) {
|
/* Standard insertion */
|
||||||
/* Insertion by reference */
|
PH7_HashmapInsert(pMap,
|
||||||
PH7_HashmapInsertByRef(pMap,
|
(pEntry->iFlags & MEMOBJ_NULL) ? 0 /* Automatic index assign */ : pEntry,
|
||||||
(pEntry->iFlags & MEMOBJ_NULL) ? 0 /* Automatic index assign */ : pEntry,
|
&pEntry[1]
|
||||||
(sxu32)pEntry[1].x.iVal
|
);
|
||||||
);
|
|
||||||
} else {
|
|
||||||
/* Standard insertion */
|
|
||||||
PH7_HashmapInsert(pMap,
|
|
||||||
(pEntry->iFlags & MEMOBJ_NULL) ? 0 /* Automatic index assign */ : pEntry,
|
|
||||||
&pEntry[1]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
/* Set the proper type of array */
|
/* Set the proper type of array */
|
||||||
if((iFlags & MEMOBJ_MIXED) == 0) {
|
if((iFlags & MEMOBJ_MIXED) == 0) {
|
||||||
if(pEntry[1].iFlags & MEMOBJ_REFERENCE) {
|
pFlags = pEntry[1].iFlags;
|
||||||
pFlags = pEntry[1].iFlags ^ MEMOBJ_REFERENCE;
|
|
||||||
} else {
|
|
||||||
pFlags = pEntry[1].iFlags;
|
|
||||||
}
|
|
||||||
if(iFlags != pFlags && iFlags != (pFlags ^ MEMOBJ_HASHMAP)) {
|
if(iFlags != pFlags && iFlags != (pFlags ^ MEMOBJ_HASHMAP)) {
|
||||||
iFlags = MEMOBJ_MIXED;
|
iFlags = MEMOBJ_MIXED;
|
||||||
}
|
}
|
||||||
|
@ -2801,12 +2771,10 @@ static sxi32 VmByteCodeExec(
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* STORE_IDX: P1 * P3
|
* STORE_IDX: P1 * P3
|
||||||
* STORE_IDX_R: P1 * P3
|
|
||||||
*
|
*
|
||||||
* Perfrom a store operation an a hashmap entry.
|
* Perfrom a store operation an a hashmap entry.
|
||||||
*/
|
*/
|
||||||
case PH7_OP_STORE_IDX:
|
case PH7_OP_STORE_IDX: {
|
||||||
case PH7_OP_STORE_IDX_REF: {
|
|
||||||
ph7_hashmap *pMap = 0; /* cc warning */
|
ph7_hashmap *pMap = 0; /* cc warning */
|
||||||
ph7_value *pKey;
|
ph7_value *pKey;
|
||||||
sxu32 nIdx;
|
sxu32 nIdx;
|
||||||
|
@ -2836,7 +2804,7 @@ static sxi32 VmByteCodeExec(
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/* Phase#1: Load the array */
|
/* Phase#1: Load the array */
|
||||||
if((pObj->iFlags & MEMOBJ_STRING) && (pInstr->iOp != PH7_OP_STORE_IDX_REF)) {
|
if(pObj->iFlags & MEMOBJ_STRING) {
|
||||||
VmPopOperand(&pTos, 1);
|
VmPopOperand(&pTos, 1);
|
||||||
if((pTos->iFlags & MEMOBJ_STRING) == 0) {
|
if((pTos->iFlags & MEMOBJ_STRING) == 0) {
|
||||||
/* Force a string cast */
|
/* Force a string cast */
|
||||||
|
@ -2880,12 +2848,7 @@ static sxi32 VmByteCodeExec(
|
||||||
}
|
}
|
||||||
VmPopOperand(&pTos, 1);
|
VmPopOperand(&pTos, 1);
|
||||||
/* Phase#2: Perform the insertion */
|
/* Phase#2: Perform the insertion */
|
||||||
if(pInstr->iOp == PH7_OP_STORE_IDX_REF && pTos->nIdx != SXU32_HIGH) {
|
PH7_HashmapInsert(pMap, pKey, pTos);
|
||||||
/* Insertion by reference */
|
|
||||||
PH7_HashmapInsertByRef(pMap, pKey, pTos->nIdx);
|
|
||||||
} else {
|
|
||||||
PH7_HashmapInsert(pMap, pKey, pTos);
|
|
||||||
}
|
|
||||||
if(pKey) {
|
if(pKey) {
|
||||||
PH7_MemObjRelease(pKey);
|
PH7_MemObjRelease(pKey);
|
||||||
}
|
}
|
||||||
|
@ -3958,96 +3921,6 @@ static sxi32 VmByteCodeExec(
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
* OP_LOAD_REF * * *
|
|
||||||
* Push the index of a referenced object on the stack.
|
|
||||||
*/
|
|
||||||
case PH7_OP_LOAD_REF: {
|
|
||||||
sxu32 nIdx;
|
|
||||||
#ifdef UNTRUST
|
|
||||||
if(pTos < pStack) {
|
|
||||||
goto Abort;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
/* Extract memory object index */
|
|
||||||
nIdx = pTos->nIdx;
|
|
||||||
if(nIdx != SXU32_HIGH /* Not a constant */) {
|
|
||||||
/* Nullify the object */
|
|
||||||
PH7_MemObjRelease(pTos);
|
|
||||||
/* Mark as constant and store the index on the top of the stack */
|
|
||||||
pTos->x.iVal = (sxi64)nIdx;
|
|
||||||
pTos->nIdx = SXU32_HIGH;
|
|
||||||
pTos->iFlags = MEMOBJ_INT | MEMOBJ_REFERENCE;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* OP_STORE_REF * * P3
|
|
||||||
* Perform an assignment operation by reference.
|
|
||||||
*/
|
|
||||||
case PH7_OP_STORE_REF: {
|
|
||||||
SyString sName = { 0, 0 };
|
|
||||||
SyHashEntry *pEntry;
|
|
||||||
sxu32 nIdx;
|
|
||||||
#ifdef UNTRUST
|
|
||||||
if(pTos < pStack) {
|
|
||||||
goto Abort;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if(pInstr->p3 == 0) {
|
|
||||||
char *zName;
|
|
||||||
/* Take the variable name from the Next on the stack */
|
|
||||||
if((pTos->iFlags & MEMOBJ_STRING) == 0) {
|
|
||||||
/* Force a string cast */
|
|
||||||
PH7_MemObjToString(pTos);
|
|
||||||
}
|
|
||||||
if(SyBlobLength(&pTos->sBlob) > 0) {
|
|
||||||
zName = SyMemBackendStrDup(&pVm->sAllocator,
|
|
||||||
(const char *)SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob));
|
|
||||||
if(zName) {
|
|
||||||
SyStringInitFromBuf(&sName, zName, SyBlobLength(&pTos->sBlob));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PH7_MemObjRelease(pTos);
|
|
||||||
pTos--;
|
|
||||||
} else {
|
|
||||||
SyStringInitFromBuf(&sName, pInstr->p3, SyStrlen((const char *)pInstr->p3));
|
|
||||||
}
|
|
||||||
nIdx = pTos->nIdx;
|
|
||||||
if(nIdx == SXU32_HIGH) {
|
|
||||||
if((pTos->iFlags & (MEMOBJ_OBJ | MEMOBJ_HASHMAP | MEMOBJ_RES)) == 0) {
|
|
||||||
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR,
|
|
||||||
"Reference operator require a variable not a constant as it's right operand");
|
|
||||||
} else {
|
|
||||||
ph7_value *pObj;
|
|
||||||
/* Extract the desired variable and if not available dynamically create it */
|
|
||||||
pObj = VmExtractMemObj(&(*pVm), &sName, FALSE, TRUE);
|
|
||||||
if(pObj == 0) {
|
|
||||||
PH7_VmMemoryError(&(*pVm));
|
|
||||||
}
|
|
||||||
/* Perform the store operation */
|
|
||||||
PH7_MemObjStore(pTos, pObj);
|
|
||||||
pTos->nIdx = pObj->nIdx;
|
|
||||||
}
|
|
||||||
} else if(sName.nByte > 0) {
|
|
||||||
VmFrame *pFrame = pVm->pFrame;
|
|
||||||
while(pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION)) {
|
|
||||||
/* Safely ignore the exception frame */
|
|
||||||
pFrame = pFrame->pParent;
|
|
||||||
}
|
|
||||||
/* Query the local frame */
|
|
||||||
pEntry = SyHashGet(&pFrame->hVar, (const void *)sName.zString, sName.nByte);
|
|
||||||
if(pEntry) {
|
|
||||||
rc = SyHashInsert(&pFrame->hVar, (const void *)sName.zString, sName.nByte, SX_INT_TO_PTR(nIdx));
|
|
||||||
if(rc == SXRET_OK) {
|
|
||||||
PH7_VmRefObjInstall(&(*pVm), nIdx, SyHashLastEntry(&pFrame->hVar), 0, 0);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Referenced variable name '%z' does not exists", &sName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
/*
|
/*
|
||||||
* OP_LOAD_EXCEPTION * P2 P3
|
* OP_LOAD_EXCEPTION * P2 P3
|
||||||
* Push an exception in the corresponding container so that
|
* Push an exception in the corresponding container so that
|
||||||
|
@ -5668,9 +5541,6 @@ static const char *VmInstrToString(sxi32 nOp) {
|
||||||
case PH7_OP_STORE_IDX:
|
case PH7_OP_STORE_IDX:
|
||||||
zOp = "STORE_IDX";
|
zOp = "STORE_IDX";
|
||||||
break;
|
break;
|
||||||
case PH7_OP_STORE_IDX_REF:
|
|
||||||
zOp = "STORE_IDX_R";
|
|
||||||
break;
|
|
||||||
case PH7_OP_PULL:
|
case PH7_OP_PULL:
|
||||||
zOp = "PULL";
|
zOp = "PULL";
|
||||||
break;
|
break;
|
||||||
|
@ -5734,12 +5604,6 @@ static const char *VmInstrToString(sxi32 nOp) {
|
||||||
case PH7_OP_CONSUME:
|
case PH7_OP_CONSUME:
|
||||||
zOp = "CONSUME";
|
zOp = "CONSUME";
|
||||||
break;
|
break;
|
||||||
case PH7_OP_LOAD_REF:
|
|
||||||
zOp = "LOAD_REF";
|
|
||||||
break;
|
|
||||||
case PH7_OP_STORE_REF:
|
|
||||||
zOp = "STORE_REF";
|
|
||||||
break;
|
|
||||||
case PH7_OP_MEMBER:
|
case PH7_OP_MEMBER:
|
||||||
zOp = "MEMBER";
|
zOp = "MEMBER";
|
||||||
break;
|
break;
|
||||||
|
@ -5812,163 +5676,6 @@ PH7_PRIVATE void PH7_VmExpandConstantValue(ph7_value *pVal, void *pUserData) {
|
||||||
* Status:
|
* Status:
|
||||||
* Stable.
|
* Stable.
|
||||||
*/
|
*/
|
||||||
/*
|
|
||||||
* int func_num_args(void)
|
|
||||||
* Returns the number of arguments passed to the function.
|
|
||||||
* Parameters
|
|
||||||
* None.
|
|
||||||
* Return
|
|
||||||
* Total number of arguments passed into the current user-defined function
|
|
||||||
* or -1 if called from the globe scope.
|
|
||||||
*/
|
|
||||||
static int vm_builtin_func_num_args(ph7_context *pCtx, int nArg, ph7_value **apArg) {
|
|
||||||
VmFrame *pFrame;
|
|
||||||
ph7_vm *pVm;
|
|
||||||
/* Point to the target VM */
|
|
||||||
pVm = pCtx->pVm;
|
|
||||||
/* Current frame */
|
|
||||||
pFrame = pVm->pFrame;
|
|
||||||
while(pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION)) {
|
|
||||||
/* Safely ignore the exception frame */
|
|
||||||
pFrame = pFrame->pParent;
|
|
||||||
}
|
|
||||||
if(pFrame->pParent == 0) {
|
|
||||||
SXUNUSED(nArg);
|
|
||||||
SXUNUSED(apArg);
|
|
||||||
/* Global frame,return -1 */
|
|
||||||
ph7_result_int(pCtx, -1);
|
|
||||||
return SXRET_OK;
|
|
||||||
}
|
|
||||||
/* Total number of arguments passed to the enclosing function */
|
|
||||||
nArg = (int)SySetUsed(&pFrame->sArg);
|
|
||||||
ph7_result_int(pCtx, nArg);
|
|
||||||
return SXRET_OK;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* value func_get_arg(int $arg_num)
|
|
||||||
* Return an item from the argument list.
|
|
||||||
* Parameters
|
|
||||||
* Argument number(index start from zero).
|
|
||||||
* Return
|
|
||||||
* Returns the specified argument or FALSE on error.
|
|
||||||
*/
|
|
||||||
static int vm_builtin_func_get_arg(ph7_context *pCtx, int nArg, ph7_value **apArg) {
|
|
||||||
ph7_value *pObj = 0;
|
|
||||||
VmSlot *pSlot = 0;
|
|
||||||
VmFrame *pFrame;
|
|
||||||
ph7_vm *pVm;
|
|
||||||
/* Point to the target VM */
|
|
||||||
pVm = pCtx->pVm;
|
|
||||||
/* Current frame */
|
|
||||||
pFrame = pVm->pFrame;
|
|
||||||
while(pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION)) {
|
|
||||||
/* Safely ignore the exception frame */
|
|
||||||
pFrame = pFrame->pParent;
|
|
||||||
}
|
|
||||||
/* Extract the desired index */
|
|
||||||
nArg = ph7_value_to_int(apArg[0]);
|
|
||||||
if(nArg < 0 || nArg >= (int)SySetUsed(&pFrame->sArg)) {
|
|
||||||
/* Invalid index,return FALSE */
|
|
||||||
ph7_result_bool(pCtx, 0);
|
|
||||||
return SXRET_OK;
|
|
||||||
}
|
|
||||||
/* Extract the desired argument */
|
|
||||||
if((pSlot = (VmSlot *)SySetAt(&pFrame->sArg, (sxu32)nArg)) != 0) {
|
|
||||||
if((pObj = (ph7_value *)SySetAt(&pVm->aMemObj, pSlot->nIdx)) != 0) {
|
|
||||||
/* Return the desired argument */
|
|
||||||
ph7_result_value(pCtx, (ph7_value *)pObj);
|
|
||||||
} else {
|
|
||||||
/* No such argument,return false */
|
|
||||||
ph7_result_bool(pCtx, 0);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* CAN'T HAPPEN */
|
|
||||||
ph7_result_bool(pCtx, 0);
|
|
||||||
}
|
|
||||||
return SXRET_OK;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* array func_get_args_byref(void)
|
|
||||||
* Returns an array comprising a function's argument list.
|
|
||||||
* Parameters
|
|
||||||
* None.
|
|
||||||
* Return
|
|
||||||
* Returns an array in which each element is a POINTER to the corresponding
|
|
||||||
* member of the current user-defined function's argument list.
|
|
||||||
* Otherwise FALSE is returned on failure.
|
|
||||||
* NOTE:
|
|
||||||
* Arguments are returned to the array by reference.
|
|
||||||
*/
|
|
||||||
static int vm_builtin_func_get_args_byref(ph7_context *pCtx, int nArg, ph7_value **apArg) {
|
|
||||||
ph7_value *pArray;
|
|
||||||
VmFrame *pFrame;
|
|
||||||
VmSlot *aSlot;
|
|
||||||
sxu32 n;
|
|
||||||
/* Point to the current frame */
|
|
||||||
pFrame = pCtx->pVm->pFrame;
|
|
||||||
while(pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION)) {
|
|
||||||
/* Safely ignore the exception frame */
|
|
||||||
pFrame = pFrame->pParent;
|
|
||||||
}
|
|
||||||
/* Create a new array */
|
|
||||||
pArray = ph7_context_new_array(pCtx);
|
|
||||||
if(pArray == 0) {
|
|
||||||
SXUNUSED(nArg); /* cc warning */
|
|
||||||
SXUNUSED(apArg);
|
|
||||||
ph7_result_bool(pCtx, 0);
|
|
||||||
return SXRET_OK;
|
|
||||||
}
|
|
||||||
/* Start filling the array with the given arguments (Pass by reference) */
|
|
||||||
aSlot = (VmSlot *)SySetBasePtr(&pFrame->sArg);
|
|
||||||
for(n = 0; n < SySetUsed(&pFrame->sArg) ; n++) {
|
|
||||||
PH7_HashmapInsertByRef((ph7_hashmap *)pArray->x.pOther, 0/*Automatic index assign*/, aSlot[n].nIdx);
|
|
||||||
}
|
|
||||||
/* Return the freshly created array */
|
|
||||||
ph7_result_value(pCtx, pArray);
|
|
||||||
return SXRET_OK;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* array func_get_args(void)
|
|
||||||
* Returns an array comprising a copy of function's argument list.
|
|
||||||
* Parameters
|
|
||||||
* None.
|
|
||||||
* Return
|
|
||||||
* Returns an array in which each element is a copy of the corresponding
|
|
||||||
* member of the current user-defined function's argument list.
|
|
||||||
* Otherwise FALSE is returned on failure.
|
|
||||||
*/
|
|
||||||
static int vm_builtin_func_get_args(ph7_context *pCtx, int nArg, ph7_value **apArg) {
|
|
||||||
ph7_value *pObj = 0;
|
|
||||||
ph7_value *pArray;
|
|
||||||
VmFrame *pFrame;
|
|
||||||
VmSlot *aSlot;
|
|
||||||
sxu32 n;
|
|
||||||
/* Point to the current frame */
|
|
||||||
pFrame = pCtx->pVm->pFrame;
|
|
||||||
while(pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION)) {
|
|
||||||
/* Safely ignore the exception frame */
|
|
||||||
pFrame = pFrame->pParent;
|
|
||||||
}
|
|
||||||
/* Create a new array */
|
|
||||||
pArray = ph7_context_new_array(pCtx);
|
|
||||||
if(pArray == 0) {
|
|
||||||
SXUNUSED(nArg); /* cc warning */
|
|
||||||
SXUNUSED(apArg);
|
|
||||||
ph7_result_bool(pCtx, 0);
|
|
||||||
return SXRET_OK;
|
|
||||||
}
|
|
||||||
/* Start filling the array with the given arguments */
|
|
||||||
aSlot = (VmSlot *)SySetBasePtr(&pFrame->sArg);
|
|
||||||
for(n = 0; n < SySetUsed(&pFrame->sArg) ; n++) {
|
|
||||||
pObj = (ph7_value *)SySetAt(&pCtx->pVm->aMemObj, aSlot[n].nIdx);
|
|
||||||
if(pObj) {
|
|
||||||
ph7_array_add_elem(pArray, 0/* Automatic index assign*/, pObj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Return the freshly created array */
|
|
||||||
ph7_result_value(pCtx, pArray);
|
|
||||||
return SXRET_OK;
|
|
||||||
}
|
|
||||||
/*
|
/*
|
||||||
* bool function_exists(string $name)
|
* bool function_exists(string $name)
|
||||||
* Return TRUE if the given function has been defined.
|
* Return TRUE if the given function has been defined.
|
||||||
|
@ -8491,7 +8198,7 @@ static int vm_builtin_var_dump(ph7_context *pCtx, int nArg, ph7_value **apArg) {
|
||||||
/* Reset the working buffer */
|
/* Reset the working buffer */
|
||||||
SyBlobReset(&sDump);
|
SyBlobReset(&sDump);
|
||||||
/* Dump the given expression */
|
/* Dump the given expression */
|
||||||
PH7_MemObjDump(&sDump, pObj, TRUE, 0, 0, 0);
|
PH7_MemObjDump(&sDump, pObj, TRUE, 0, 0);
|
||||||
/* Output */
|
/* Output */
|
||||||
if(SyBlobLength(&sDump) > 0) {
|
if(SyBlobLength(&sDump) > 0) {
|
||||||
ph7_context_output(pCtx, (const char *)SyBlobData(&sDump), (int)SyBlobLength(&sDump));
|
ph7_context_output(pCtx, (const char *)SyBlobData(&sDump), (int)SyBlobLength(&sDump));
|
||||||
|
@ -8527,7 +8234,7 @@ static int vm_builtin_print_r(ph7_context *pCtx, int nArg, ph7_value **apArg) {
|
||||||
ret_string = ph7_value_to_bool(apArg[1]);
|
ret_string = ph7_value_to_bool(apArg[1]);
|
||||||
}
|
}
|
||||||
/* Generate dump */
|
/* Generate dump */
|
||||||
PH7_MemObjDump(&sDump, apArg[0], FALSE, 0, 0, 0);
|
PH7_MemObjDump(&sDump, apArg[0], FALSE, 0, 0);
|
||||||
if(!ret_string) {
|
if(!ret_string) {
|
||||||
/* Output dump */
|
/* Output dump */
|
||||||
ph7_context_output(pCtx, (const char *)SyBlobData(&sDump), (int)SyBlobLength(&sDump));
|
ph7_context_output(pCtx, (const char *)SyBlobData(&sDump), (int)SyBlobLength(&sDump));
|
||||||
|
@ -8559,7 +8266,7 @@ static int vm_builtin_var_export(ph7_context *pCtx, int nArg, ph7_value **apArg)
|
||||||
ret_string = ph7_value_to_bool(apArg[1]);
|
ret_string = ph7_value_to_bool(apArg[1]);
|
||||||
}
|
}
|
||||||
/* Generate dump */
|
/* Generate dump */
|
||||||
PH7_MemObjDump(&sDump, apArg[0], FALSE, 0, 0, 0);
|
PH7_MemObjDump(&sDump, apArg[0], FALSE, 0, 0);
|
||||||
if(!ret_string) {
|
if(!ret_string) {
|
||||||
/* Output dump */
|
/* Output dump */
|
||||||
ph7_context_output(pCtx, (const char *)SyBlobData(&sDump), (int)SyBlobLength(&sDump));
|
ph7_context_output(pCtx, (const char *)SyBlobData(&sDump), (int)SyBlobLength(&sDump));
|
||||||
|
@ -10841,10 +10548,6 @@ static int vm_builtin_utf8_decode(ph7_context *pCtx, int nArg, ph7_value **apArg
|
||||||
}
|
}
|
||||||
/* Table of built-in VM functions. */
|
/* Table of built-in VM functions. */
|
||||||
static const ph7_builtin_func aVmFunc[] = {
|
static const ph7_builtin_func aVmFunc[] = {
|
||||||
{ "func_num_args", vm_builtin_func_num_args },
|
|
||||||
{ "func_get_arg", vm_builtin_func_get_arg },
|
|
||||||
{ "func_get_args", vm_builtin_func_get_args },
|
|
||||||
{ "func_get_args_byref", vm_builtin_func_get_args_byref },
|
|
||||||
{ "function_exists", vm_builtin_func_exists },
|
{ "function_exists", vm_builtin_func_exists },
|
||||||
{ "is_callback", vm_builtin_is_callback },
|
{ "is_callback", vm_builtin_is_callback },
|
||||||
{ "get_defined_functions", vm_builtin_get_defined_func },
|
{ "get_defined_functions", vm_builtin_get_defined_func },
|
||||||
|
|
|
@ -641,7 +641,6 @@ struct ph7_value {
|
||||||
#define MEMOBJ_STRING 0x0080 /* Memory value is a UTF-8 string */
|
#define MEMOBJ_STRING 0x0080 /* Memory value is a UTF-8 string */
|
||||||
#define MEMOBJ_VOID 0x0100 /* Memory value is a void */
|
#define MEMOBJ_VOID 0x0100 /* Memory value is a void */
|
||||||
#define MEMOBJ_MIXED 0x0200 /* Memory value is mixed */
|
#define MEMOBJ_MIXED 0x0200 /* Memory value is mixed */
|
||||||
#define MEMOBJ_REFERENCE 0x0400 /* Memory value hold a reference (64-bit index) of another ph7_value */
|
|
||||||
#define MEMOBJ_HASHMAP 0x0800 /* Memory value is a hashmap aka 'array' in the PHP jargon */
|
#define MEMOBJ_HASHMAP 0x0800 /* Memory value is a hashmap aka 'array' in the PHP jargon */
|
||||||
#define MEMOBJ_NULL 0x1000 /* Memory value is NULL */
|
#define MEMOBJ_NULL 0x1000 /* Memory value is NULL */
|
||||||
/* Mask of all known types */
|
/* Mask of all known types */
|
||||||
|
@ -652,7 +651,6 @@ struct ph7_value {
|
||||||
* Types array, object and resource are not scalar.
|
* Types array, object and resource are not scalar.
|
||||||
*/
|
*/
|
||||||
#define MEMOBJ_SCALAR (MEMOBJ_STRING|MEMOBJ_INT|MEMOBJ_REAL|MEMOBJ_BOOL|MEMOBJ_CHAR|MEMOBJ_VOID|MEMOBJ_NULL)
|
#define MEMOBJ_SCALAR (MEMOBJ_STRING|MEMOBJ_INT|MEMOBJ_REAL|MEMOBJ_BOOL|MEMOBJ_CHAR|MEMOBJ_VOID|MEMOBJ_NULL)
|
||||||
#define MEMOBJ_AUX (MEMOBJ_REFERENCE)
|
|
||||||
/*
|
/*
|
||||||
* The following macro clear the current ph7_value type and replace
|
* The following macro clear the current ph7_value type and replace
|
||||||
* it with the given one.
|
* it with the given one.
|
||||||
|
@ -1453,8 +1451,6 @@ enum ph7_vm_op {
|
||||||
PH7_OP_BOR_STORE, /* Bitor and store '|=' */
|
PH7_OP_BOR_STORE, /* Bitor and store '|=' */
|
||||||
PH7_OP_BXOR_STORE, /* Bitxor and store '^=' */
|
PH7_OP_BXOR_STORE, /* Bitxor and store '^=' */
|
||||||
PH7_OP_CONSUME, /* Consume VM output */
|
PH7_OP_CONSUME, /* Consume VM output */
|
||||||
PH7_OP_LOAD_REF, /* Load reference */
|
|
||||||
PH7_OP_STORE_REF, /* Store a reference to a variable*/
|
|
||||||
PH7_OP_MEMBER, /* Class member run-time access */
|
PH7_OP_MEMBER, /* Class member run-time access */
|
||||||
PH7_OP_CVT_OBJ, /* Object cast */
|
PH7_OP_CVT_OBJ, /* Object cast */
|
||||||
PH7_OP_CVT_CALL, /* Callback cast */
|
PH7_OP_CVT_CALL, /* Callback cast */
|
||||||
|
@ -1626,7 +1622,7 @@ enum json_err_code {
|
||||||
JSON_ERROR_UTF8 /* Malformed UTF-8 characters */
|
JSON_ERROR_UTF8 /* Malformed UTF-8 characters */
|
||||||
};
|
};
|
||||||
/* memobj.c function prototypes */
|
/* memobj.c function prototypes */
|
||||||
PH7_PRIVATE sxi32 PH7_MemObjDump(SyBlob *pOut, ph7_value *pObj, int ShowType, int nTab, int nDepth, int isRef);
|
PH7_PRIVATE sxi32 PH7_MemObjDump(SyBlob *pOut, ph7_value *pObj, int ShowType, int nTab, int nDepth);
|
||||||
PH7_PRIVATE const char *PH7_MemObjTypeDump(ph7_value *pVal);
|
PH7_PRIVATE const char *PH7_MemObjTypeDump(ph7_value *pVal);
|
||||||
PH7_PRIVATE sxi32 PH7_MemObjAdd(ph7_value *pObj1, ph7_value *pObj2, int bAddStore);
|
PH7_PRIVATE sxi32 PH7_MemObjAdd(ph7_value *pObj1, ph7_value *pObj2, int bAddStore);
|
||||||
PH7_PRIVATE sxi32 PH7_MemObjCmp(ph7_value *pObj1, ph7_value *pObj2, int bStrict, int iNest);
|
PH7_PRIVATE sxi32 PH7_MemObjCmp(ph7_value *pObj1, ph7_value *pObj2, int bStrict, int iNest);
|
||||||
|
@ -1739,7 +1735,6 @@ PH7_PRIVATE sxi32 PH7_HashmapRelease(ph7_hashmap *pMap, int FreeDS);
|
||||||
PH7_PRIVATE void PH7_HashmapUnref(ph7_hashmap *pMap);
|
PH7_PRIVATE void PH7_HashmapUnref(ph7_hashmap *pMap);
|
||||||
PH7_PRIVATE sxi32 PH7_HashmapLookup(ph7_hashmap *pMap, ph7_value *pKey, ph7_hashmap_node **ppNode);
|
PH7_PRIVATE sxi32 PH7_HashmapLookup(ph7_hashmap *pMap, ph7_value *pKey, ph7_hashmap_node **ppNode);
|
||||||
PH7_PRIVATE sxi32 PH7_HashmapInsert(ph7_hashmap *pMap, ph7_value *pKey, ph7_value *pVal);
|
PH7_PRIVATE sxi32 PH7_HashmapInsert(ph7_hashmap *pMap, ph7_value *pKey, ph7_value *pVal);
|
||||||
PH7_PRIVATE sxi32 PH7_HashmapInsertByRef(ph7_hashmap *pMap, ph7_value *pKey, sxu32 nRefIdx);
|
|
||||||
PH7_PRIVATE sxi32 PH7_HashmapUnion(ph7_hashmap *pLeft, ph7_hashmap *pRight);
|
PH7_PRIVATE sxi32 PH7_HashmapUnion(ph7_hashmap *pLeft, ph7_hashmap *pRight);
|
||||||
PH7_PRIVATE void PH7_HashmapUnlinkNode(ph7_hashmap_node *pNode, int bRestore);
|
PH7_PRIVATE void PH7_HashmapUnlinkNode(ph7_hashmap_node *pNode, int bRestore);
|
||||||
PH7_PRIVATE sxi32 PH7_HashmapDup(ph7_hashmap *pSrc, ph7_hashmap *pDest);
|
PH7_PRIVATE sxi32 PH7_HashmapDup(ph7_hashmap *pSrc, ph7_hashmap *pDest);
|
||||||
|
|
Loading…
Reference in New Issue