1148 lines
38 KiB
C
1148 lines
38 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: oo.c v1.9 FeeBSD 2012-07-17 03:44 devel <chm@symisc.net> $ */
|
|
#ifndef PH7_AMALGAMATION
|
|
#include "ph7int.h"
|
|
#endif
|
|
/*
|
|
* This file implement an Object Oriented (OO) subsystem for the PH7 engine.
|
|
*/
|
|
/*
|
|
* Create an empty class.
|
|
* Return a pointer to a raw class (ph7_class instance) on success. NULL otherwise.
|
|
*/
|
|
PH7_PRIVATE ph7_class * PH7_NewRawClass(ph7_vm *pVm,const SyString *pName,sxu32 nLine)
|
|
{
|
|
ph7_class *pClass;
|
|
char *zName;
|
|
/* Allocate a new instance */
|
|
pClass = (ph7_class *)SyMemBackendPoolAlloc(&pVm->sAllocator,sizeof(ph7_class));
|
|
if( pClass == 0 ){
|
|
return 0;
|
|
}
|
|
/* Zero the structure */
|
|
SyZero(pClass,sizeof(ph7_class));
|
|
/* Duplicate class name */
|
|
zName = SyMemBackendStrDup(&pVm->sAllocator,pName->zString,pName->nByte);
|
|
if( zName == 0 ){
|
|
SyMemBackendPoolFree(&pVm->sAllocator,pClass);
|
|
return 0;
|
|
}
|
|
/* Initialize fields */
|
|
SyStringInitFromBuf(&pClass->sName,zName,pName->nByte);
|
|
SyHashInit(&pClass->hMethod,&pVm->sAllocator,0,0);
|
|
SyHashInit(&pClass->hAttr,&pVm->sAllocator,0,0);
|
|
SyHashInit(&pClass->hDerived,&pVm->sAllocator,0,0);
|
|
SySetInit(&pClass->aInterface,&pVm->sAllocator,sizeof(ph7_class *));
|
|
pClass->nLine = nLine;
|
|
/* All done */
|
|
return pClass;
|
|
}
|
|
/*
|
|
* Allocate and initialize a new class attribute.
|
|
* Return a pointer to the class attribute on success. NULL otherwise.
|
|
*/
|
|
PH7_PRIVATE ph7_class_attr * PH7_NewClassAttr(ph7_vm *pVm,const SyString *pName,sxu32 nLine,sxi32 iProtection,sxi32 iFlags)
|
|
{
|
|
ph7_class_attr *pAttr;
|
|
char *zName;
|
|
pAttr = (ph7_class_attr *)SyMemBackendPoolAlloc(&pVm->sAllocator,sizeof(ph7_class_attr));
|
|
if( pAttr == 0 ){
|
|
return 0;
|
|
}
|
|
/* Zero the structure */
|
|
SyZero(pAttr,sizeof(ph7_class_attr));
|
|
/* Duplicate attribute name */
|
|
zName = SyMemBackendStrDup(&pVm->sAllocator,pName->zString,pName->nByte);
|
|
if( zName == 0 ){
|
|
SyMemBackendPoolFree(&pVm->sAllocator,pAttr);
|
|
return 0;
|
|
}
|
|
/* Initialize fields */
|
|
SySetInit(&pAttr->aByteCode,&pVm->sAllocator,sizeof(VmInstr));
|
|
SyStringInitFromBuf(&pAttr->sName,zName,pName->nByte);
|
|
pAttr->iProtection = iProtection;
|
|
pAttr->nIdx = SXU32_HIGH;
|
|
pAttr->iFlags = iFlags;
|
|
pAttr->nLine = nLine;
|
|
return pAttr;
|
|
}
|
|
/*
|
|
* Allocate and initialize a new class method.
|
|
* Return a pointer to the class method on success. NULL otherwise
|
|
* This function associate with the newly created method an automatically generated
|
|
* random unique name.
|
|
*/
|
|
PH7_PRIVATE ph7_class_method * PH7_NewClassMethod(ph7_vm *pVm,ph7_class *pClass,const SyString *pName,sxu32 nLine,
|
|
sxi32 iProtection,sxi32 iFlags,sxi32 iFuncFlags)
|
|
{
|
|
ph7_class_method *pMeth;
|
|
SyHashEntry *pEntry;
|
|
SyString *pNamePtr;
|
|
char zSalt[10];
|
|
char *zName;
|
|
sxu32 nByte;
|
|
/* Allocate a new class method instance */
|
|
pMeth = (ph7_class_method *)SyMemBackendPoolAlloc(&pVm->sAllocator,sizeof(ph7_class_method));
|
|
if( pMeth == 0 ){
|
|
return 0;
|
|
}
|
|
/* Zero the structure */
|
|
SyZero(pMeth,sizeof(ph7_class_method));
|
|
/* Check for an already installed method with the same name */
|
|
pEntry = SyHashGet(&pClass->hMethod,(const void *)pName->zString,pName->nByte);
|
|
if( pEntry == 0 ){
|
|
/* Associate an unique VM name to this method */
|
|
nByte = sizeof(zSalt) + pName->nByte + SyStringLength(&pClass->sName)+sizeof(char)*7/*[[__'\0'*/;
|
|
zName = (char *)SyMemBackendAlloc(&pVm->sAllocator,nByte);
|
|
if( zName == 0 ){
|
|
SyMemBackendPoolFree(&pVm->sAllocator,pMeth);
|
|
return 0;
|
|
}
|
|
pNamePtr = &pMeth->sVmName;
|
|
/* Generate a random string */
|
|
PH7_VmRandomString(&(*pVm),zSalt,sizeof(zSalt));
|
|
pNamePtr->nByte = SyBufferFormat(zName,nByte,"[__%z@%z_%.*s]",&pClass->sName,pName,sizeof(zSalt),zSalt);
|
|
pNamePtr->zString = zName;
|
|
}else{
|
|
/* Method is condidate for 'overloading' */
|
|
ph7_class_method *pCurrent = (ph7_class_method *)pEntry->pUserData;
|
|
pNamePtr = &pMeth->sVmName;
|
|
/* Use the same VM name */
|
|
SyStringDupPtr(pNamePtr,&pCurrent->sVmName);
|
|
zName = (char *)pNamePtr->zString;
|
|
}
|
|
if( iProtection != PH7_CLASS_PROT_PUBLIC ){
|
|
if( (pName->nByte == sizeof("__construct") - 1 && SyMemcmp(pName->zString,"__construct",sizeof("__construct") - 1 ) == 0)
|
|
|| (pName->nByte == sizeof("__destruct") - 1 && SyMemcmp(pName->zString,"__destruct",sizeof("__destruct") - 1 ) == 0)
|
|
|| SyStringCmp(pName,&pClass->sName,SyMemcmp) == 0 ){
|
|
/* Switch to public visibility when dealing with constructor/destructor */
|
|
iProtection = PH7_CLASS_PROT_PUBLIC;
|
|
}
|
|
}
|
|
/* Initialize method fields */
|
|
pMeth->iProtection = iProtection;
|
|
pMeth->iFlags = iFlags;
|
|
pMeth->nLine = nLine;
|
|
PH7_VmInitFuncState(&(*pVm),&pMeth->sFunc,&zName[sizeof(char)*4/*[__@*/+SyStringLength(&pClass->sName)],
|
|
pName->nByte,iFuncFlags|VM_FUNC_CLASS_METHOD,pClass);
|
|
return pMeth;
|
|
}
|
|
/*
|
|
* Check if the given name have a class method associated with it.
|
|
* Return the desired method [i.e: ph7_class_method instance] on success. NULL otherwise.
|
|
*/
|
|
PH7_PRIVATE ph7_class_method * PH7_ClassExtractMethod(ph7_class *pClass,const char *zName,sxu32 nByte)
|
|
{
|
|
SyHashEntry *pEntry;
|
|
/* Perform a hash lookup */
|
|
pEntry = SyHashGet(&pClass->hMethod,(const void *)zName,nByte);
|
|
if( pEntry == 0 ){
|
|
/* No such entry */
|
|
return 0;
|
|
}
|
|
/* Point to the desired method */
|
|
return (ph7_class_method *)pEntry->pUserData;
|
|
}
|
|
/*
|
|
* Check if the given name is a class attribute.
|
|
* Return the desired attribute [i.e: ph7_class_attr instance] on success.NULL otherwise.
|
|
*/
|
|
PH7_PRIVATE ph7_class_attr * PH7_ClassExtractAttribute(ph7_class *pClass,const char *zName,sxu32 nByte)
|
|
{
|
|
SyHashEntry *pEntry;
|
|
/* Perform a hash lookup */
|
|
pEntry = SyHashGet(&pClass->hAttr,(const void *)zName,nByte);
|
|
if( pEntry == 0 ){
|
|
/* No such entry */
|
|
return 0;
|
|
}
|
|
/* Point to the desierd method */
|
|
return (ph7_class_attr *)pEntry->pUserData;
|
|
}
|
|
/*
|
|
* Install a class attribute in the corresponding container.
|
|
* Return SXRET_OK on success. Any other return value indicates failure.
|
|
*/
|
|
PH7_PRIVATE sxi32 PH7_ClassInstallAttr(ph7_class *pClass,ph7_class_attr *pAttr)
|
|
{
|
|
SyString *pName = &pAttr->sName;
|
|
sxi32 rc;
|
|
rc = SyHashInsert(&pClass->hAttr,(const void *)pName->zString,pName->nByte,pAttr);
|
|
return rc;
|
|
}
|
|
/*
|
|
* Install a class method in the corresponding container.
|
|
* Return SXRET_OK on success. Any other return value indicates failure.
|
|
*/
|
|
PH7_PRIVATE sxi32 PH7_ClassInstallMethod(ph7_class *pClass,ph7_class_method *pMeth)
|
|
{
|
|
SyString *pName = &pMeth->sFunc.sName;
|
|
sxi32 rc;
|
|
rc = SyHashInsert(&pClass->hMethod,(const void *)pName->zString,pName->nByte,pMeth);
|
|
return rc;
|
|
}
|
|
/*
|
|
* Perform an inheritance operation.
|
|
* According to the PHP language reference manual
|
|
* When you extend a class, the subclass inherits all of the public and protected methods
|
|
* from the parent class. Unless a class Overwrites those methods, they will retain their original
|
|
* functionality.
|
|
* This is useful for defining and abstracting functionality, and permits the implementation
|
|
* of additional functionality in similar objects without the need to reimplement all of the shared
|
|
* functionality.
|
|
* Example #1 Inheritance Example
|
|
* <?php
|
|
* class foo
|
|
* {
|
|
* public function printItem($string)
|
|
* {
|
|
* echo 'Foo: ' . $string . PHP_EOL;
|
|
* }
|
|
*
|
|
* public function printPHP()
|
|
* {
|
|
* echo 'PHP is great.' . PHP_EOL;
|
|
* }
|
|
* }
|
|
* class bar extends foo
|
|
* {
|
|
* public function printItem($string)
|
|
* {
|
|
* echo 'Bar: ' . $string . PHP_EOL;
|
|
* }
|
|
* }
|
|
* $foo = new foo();
|
|
* $bar = new bar();
|
|
* $foo->printItem('baz'); // Output: 'Foo: baz'
|
|
* $foo->printPHP(); // Output: 'PHP is great'
|
|
* $bar->printItem('baz'); // Output: 'Bar: baz'
|
|
* $bar->printPHP(); // Output: 'PHP is great'
|
|
*
|
|
* This function return SXRET_OK if the inheritance operation was successfully performed.
|
|
* Any other return value indicates failure and the upper layer must generate an appropriate
|
|
* error message.
|
|
*/
|
|
PH7_PRIVATE sxi32 PH7_ClassInherit(ph7_gen_state *pGen,ph7_class *pSub,ph7_class *pBase)
|
|
{
|
|
ph7_class_method *pMeth;
|
|
ph7_class_attr *pAttr;
|
|
SyHashEntry *pEntry;
|
|
SyString *pName;
|
|
sxi32 rc;
|
|
/* Install in the derived hashtable */
|
|
rc = SyHashInsert(&pBase->hDerived,(const void *)SyStringData(&pSub->sName),SyStringLength(&pSub->sName),pSub);
|
|
if( rc != SXRET_OK ){
|
|
return rc;
|
|
}
|
|
/* Copy public/protected attributes from the base class */
|
|
SyHashResetLoopCursor(&pBase->hAttr);
|
|
while((pEntry = SyHashGetNextEntry(&pBase->hAttr)) != 0 ){
|
|
/* Make sure the private attributes are not redeclared in the subclass */
|
|
pAttr = (ph7_class_attr *)pEntry->pUserData;
|
|
pName = &pAttr->sName;
|
|
if( (pEntry = SyHashGet(&pSub->hAttr,(const void *)pName->zString,pName->nByte)) != 0 ){
|
|
if( pAttr->iProtection == PH7_CLASS_PROT_PRIVATE &&
|
|
((ph7_class_attr *)pEntry->pUserData)->iProtection != PH7_CLASS_PROT_PUBLIC ){
|
|
/* Cannot redeclare private attribute */
|
|
PH7_GenCompileError(&(*pGen),E_WARNING,((ph7_class_attr *)pEntry->pUserData)->nLine,
|
|
"Private attribute '%z::%z' redeclared inside child class '%z'",
|
|
&pBase->sName,pName,&pSub->sName);
|
|
|
|
}
|
|
continue;
|
|
}
|
|
/* Install the attribute */
|
|
if( pAttr->iProtection != PH7_CLASS_PROT_PRIVATE ){
|
|
rc = SyHashInsert(&pSub->hAttr,(const void *)pName->zString,pName->nByte,pAttr);
|
|
if( rc != SXRET_OK ){
|
|
return rc;
|
|
}
|
|
}
|
|
}
|
|
SyHashResetLoopCursor(&pBase->hMethod);
|
|
while((pEntry = SyHashGetNextEntry(&pBase->hMethod)) != 0 ){
|
|
/* Make sure the private/final methods are not redeclared in the subclass */
|
|
pMeth = (ph7_class_method *)pEntry->pUserData;
|
|
pName = &pMeth->sFunc.sName;
|
|
if( (pEntry = SyHashGet(&pSub->hMethod,(const void *)pName->zString,pName->nByte)) != 0 ){
|
|
if( pMeth->iFlags & PH7_CLASS_ATTR_FINAL ){
|
|
/* Cannot Overwrite final method */
|
|
rc = PH7_GenCompileError(&(*pGen),E_ERROR,((ph7_class_method *)pEntry->pUserData)->nLine,
|
|
"Cannot Overwrite final method '%z:%z' inside child class '%z'",
|
|
&pBase->sName,pName,&pSub->sName);
|
|
if( rc == SXERR_ABORT ){
|
|
return SXERR_ABORT;
|
|
}
|
|
}
|
|
continue;
|
|
}else{
|
|
if( pMeth->iFlags & PH7_CLASS_ATTR_ABSTRACT ){
|
|
/* Abstract method must be defined in the child class */
|
|
PH7_GenCompileError(&(*pGen),E_WARNING,pMeth->nLine,
|
|
"Abstract method '%z:%z' must be defined inside child class '%z'",
|
|
&pBase->sName,pName,&pSub->sName);
|
|
continue;
|
|
}
|
|
}
|
|
/* Install the method */
|
|
if( pMeth->iProtection != PH7_CLASS_PROT_PRIVATE ){
|
|
rc = SyHashInsert(&pSub->hMethod,(const void *)pName->zString,pName->nByte,pMeth);
|
|
if( rc != SXRET_OK ){
|
|
return rc;
|
|
}
|
|
}
|
|
}
|
|
/* Mark as subclass */
|
|
pSub->pBase = pBase;
|
|
/* All done */
|
|
return SXRET_OK;
|
|
}
|
|
/*
|
|
* Inherit an object interface from another object interface.
|
|
* According to the PHP language reference manual.
|
|
* Object interfaces allow you to create code which specifies which methods a class
|
|
* must implement, without having to define how these methods are handled.
|
|
* Interfaces are defined using the interface keyword, in the same way as a standard
|
|
* class, but without any of the methods having their contents defined.
|
|
* All methods declared in an interface must be public, this is the nature of an interface.
|
|
*
|
|
* This function return SXRET_OK if the interface inheritance operation was successfully performed.
|
|
* Any other return value indicates failure and the upper layer must generate an appropriate
|
|
* error message.
|
|
*/
|
|
PH7_PRIVATE sxi32 PH7_ClassInterfaceInherit(ph7_class *pSub,ph7_class *pBase)
|
|
{
|
|
ph7_class_method *pMeth;
|
|
ph7_class_attr *pAttr;
|
|
SyHashEntry *pEntry;
|
|
SyString *pName;
|
|
sxi32 rc;
|
|
/* Install in the derived hashtable */
|
|
SyHashInsert(&pBase->hDerived,(const void *)SyStringData(&pSub->sName),SyStringLength(&pSub->sName),pSub);
|
|
SyHashResetLoopCursor(&pBase->hAttr);
|
|
/* Copy constants */
|
|
while((pEntry = SyHashGetNextEntry(&pBase->hAttr)) != 0 ){
|
|
/* Make sure the constants are not redeclared in the subclass */
|
|
pAttr = (ph7_class_attr *)pEntry->pUserData;
|
|
pName = &pAttr->sName;
|
|
if( SyHashGet(&pSub->hAttr,(const void *)pName->zString,pName->nByte) == 0 ){
|
|
/* Install the constant in the subclass */
|
|
rc = SyHashInsert(&pSub->hAttr,(const void *)pName->zString,pName->nByte,pAttr);
|
|
if( rc != SXRET_OK ){
|
|
return rc;
|
|
}
|
|
}
|
|
}
|
|
SyHashResetLoopCursor(&pBase->hMethod);
|
|
/* Copy methods signature */
|
|
while((pEntry = SyHashGetNextEntry(&pBase->hMethod)) != 0 ){
|
|
/* Make sure the method are not redeclared in the subclass */
|
|
pMeth = (ph7_class_method *)pEntry->pUserData;
|
|
pName = &pMeth->sFunc.sName;
|
|
if( SyHashGet(&pSub->hMethod,(const void *)pName->zString,pName->nByte) == 0 ){
|
|
/* Install the method */
|
|
rc = SyHashInsert(&pSub->hMethod,(const void *)pName->zString,pName->nByte,pMeth);
|
|
if( rc != SXRET_OK ){
|
|
return rc;
|
|
}
|
|
}
|
|
}
|
|
/* Mark as subclass */
|
|
pSub->pBase = pBase;
|
|
/* All done */
|
|
return SXRET_OK;
|
|
}
|
|
/*
|
|
* Implements an object interface in the given main class.
|
|
* According to the PHP language reference manual.
|
|
* Object interfaces allow you to create code which specifies which methods a class
|
|
* must implement, without having to define how these methods are handled.
|
|
* Interfaces are defined using the interface keyword, in the same way as a standard
|
|
* class, but without any of the methods having their contents defined.
|
|
* All methods declared in an interface must be public, this is the nature of an interface.
|
|
*
|
|
* This function return SXRET_OK if the interface was successfully implemented.
|
|
* Any other return value indicates failure and the upper layer must generate an appropriate
|
|
* error message.
|
|
*/
|
|
PH7_PRIVATE sxi32 PH7_ClassImplement(ph7_class *pMain,ph7_class *pInterface)
|
|
{
|
|
ph7_class_attr *pAttr;
|
|
SyHashEntry *pEntry;
|
|
SyString *pName;
|
|
sxi32 rc;
|
|
/* First off,copy all constants declared inside the interface */
|
|
SyHashResetLoopCursor(&pInterface->hAttr);
|
|
while((pEntry = SyHashGetNextEntry(&pInterface->hAttr)) != 0 ){
|
|
/* Point to the constant declaration */
|
|
pAttr = (ph7_class_attr *)pEntry->pUserData;
|
|
pName = &pAttr->sName;
|
|
/* Make sure the attribute is not redeclared in the main class */
|
|
if( SyHashGet(&pMain->hAttr,pName->zString,pName->nByte) == 0 ){
|
|
/* Install the attribute */
|
|
rc = SyHashInsert(&pMain->hAttr,pName->zString,pName->nByte,pAttr);
|
|
if( rc != SXRET_OK ){
|
|
return rc;
|
|
}
|
|
}
|
|
}
|
|
/* Install in the interface container */
|
|
SySetPut(&pMain->aInterface,(const void *)&pInterface);
|
|
/* TICKET 1433-49/1: Symisc eXtension
|
|
* A class may not implemnt all declared interface methods,so there
|
|
* is no need for a method installer loop here.
|
|
*/
|
|
return SXRET_OK;
|
|
}
|
|
/*
|
|
* Create a class instance [i.e: Object in the PHP jargon] at run-time.
|
|
* The following function is called when an object is created at run-time
|
|
* typically when the PH7_OP_NEW/PH7_OP_CLONE instructions are executed.
|
|
* Notes on object creation.
|
|
*
|
|
* According to PHP language reference manual.
|
|
* To create an instance of a class, the new keyword must be used. An object will always
|
|
* be created unless the object has a constructor defined that throws an exception on error.
|
|
* Classes should be defined before instantiation (and in some cases this is a requirement).
|
|
* If a string containing the name of a class is used with new, a new instance of that class
|
|
* will be created. If the class is in a namespace, its fully qualified name must be used when
|
|
* doing this.
|
|
* Example #3 Creating an instance
|
|
* <?php
|
|
* $instance = new SimpleClass();
|
|
* // This can also be done with a variable:
|
|
* $className = 'Foo';
|
|
* $instance = new $className(); // Foo()
|
|
* ?>
|
|
* In the class context, it is possible to create a new object by new self and new parent.
|
|
* When assigning an already created instance of a class to a new variable, the new variable
|
|
* will access the same instance as the object that was assigned. This behaviour is the same
|
|
* when passing instances to a function. A copy of an already created object can be made by
|
|
* cloning it.
|
|
* Example #4 Object Assignment
|
|
* <?php
|
|
* class SimpleClass(){
|
|
* public $var;
|
|
* };
|
|
* $instance = new SimpleClass();
|
|
* $assigned = $instance;
|
|
* $reference =& $instance;
|
|
* $instance->var = '$assigned will have this value';
|
|
* $instance = null; // $instance and $reference become null
|
|
* var_dump($instance);
|
|
* var_dump($reference);
|
|
* var_dump($assigned);
|
|
* ?>
|
|
* The above example will output:
|
|
* NULL
|
|
* NULL
|
|
* object(SimpleClass)#1 (1) {
|
|
* ["var"]=>
|
|
* string(30) "$assigned will have this value"
|
|
* }
|
|
* Example #5 Creating new objects
|
|
* <?php
|
|
* class Test
|
|
* {
|
|
* static public function getNew()
|
|
* {
|
|
* return new static;
|
|
* }
|
|
* }
|
|
* class Child extends Test
|
|
* {}
|
|
* $obj1 = new Test();
|
|
* $obj2 = new $obj1;
|
|
* var_dump($obj1 !== $obj2);
|
|
* $obj3 = Test::getNew();
|
|
* var_dump($obj3 instanceof Test);
|
|
* $obj4 = Child::getNew();
|
|
* var_dump($obj4 instanceof Child);
|
|
* ?>
|
|
* The above example will output:
|
|
* bool(true)
|
|
* bool(true)
|
|
* bool(true)
|
|
* Note that Symisc Systems have introduced powerfull extension to
|
|
* OO subsystem. For example a class attribute may have any complex
|
|
* expression associated with it when declaring the attribute unlike
|
|
* the standard PHP engine which would allow a single value.
|
|
* Example:
|
|
* class myClass{
|
|
* public $var = 25<<1+foo()/bar();
|
|
* };
|
|
* Refer to the official documentation for more information.
|
|
*/
|
|
static ph7_class_instance * NewClassInstance(ph7_vm *pVm,ph7_class *pClass)
|
|
{
|
|
ph7_class_instance *pThis;
|
|
/* Allocate a new instance */
|
|
pThis = (ph7_class_instance *)SyMemBackendPoolAlloc(&pVm->sAllocator,sizeof(ph7_class_instance));
|
|
if( pThis == 0 ){
|
|
return 0;
|
|
}
|
|
/* Zero the structure */
|
|
SyZero(pThis,sizeof(ph7_class_instance));
|
|
/* Initialize fields */
|
|
pThis->iRef = 1;
|
|
pThis->pVm = pVm;
|
|
pThis->pClass = pClass;
|
|
SyHashInit(&pThis->hAttr,&pVm->sAllocator,0,0);
|
|
return pThis;
|
|
}
|
|
/*
|
|
* Wrapper around the NewClassInstance() function defined above.
|
|
* See the block comment above for more information.
|
|
*/
|
|
PH7_PRIVATE ph7_class_instance * PH7_NewClassInstance(ph7_vm *pVm,ph7_class *pClass)
|
|
{
|
|
ph7_class_instance *pNew;
|
|
sxi32 rc;
|
|
pNew = NewClassInstance(&(*pVm),&(*pClass));
|
|
if( pNew == 0 ){
|
|
return 0;
|
|
}
|
|
/* Associate a private VM frame with this class instance */
|
|
rc = PH7_VmCreateClassInstanceFrame(&(*pVm),pNew);
|
|
if( rc != SXRET_OK ){
|
|
SyMemBackendPoolFree(&pVm->sAllocator,pNew);
|
|
return 0;
|
|
}
|
|
return pNew;
|
|
}
|
|
/*
|
|
* Extract the value of a class instance [i.e: Object in the PHP jargon] attribute.
|
|
* This function never fail.
|
|
*/
|
|
static ph7_value * ExtractClassAttrValue(ph7_vm *pVm,VmClassAttr *pAttr)
|
|
{
|
|
/* Extract the value */
|
|
ph7_value *pValue;
|
|
pValue = (ph7_value *)SySetAt(&pVm->aMemObj,pAttr->nIdx);
|
|
return pValue;
|
|
}
|
|
/*
|
|
* Perform a clone operation on a class instance [i.e: Object in the PHP jargon].
|
|
* The following function is called when an object is cloned at run-time
|
|
* typically when the PH7_OP_CLONE instruction is executed.
|
|
* Notes on object cloning.
|
|
*
|
|
* According to PHP language reference manual.
|
|
* Creating a copy of an object with fully replicated properties is not always the wanted behavior.
|
|
* A good example of the need for copy constructors. Another example is if your object holds a reference
|
|
* to another object which it uses and when you replicate the parent object you want to create
|
|
* a new instance of this other object so that the replica has its own separate copy.
|
|
* An object copy is created by using the clone keyword (which calls the object's __clone() method if possible).
|
|
* An object's __clone() method cannot be called directly.
|
|
* $copy_of_object = clone $object;
|
|
* When an object is cloned, PHP 5 will perform a shallow copy of all of the object's properties.
|
|
* Any properties that are references to other variables, will remain references.
|
|
* Once the cloning is complete, if a __clone() method is defined, then the newly created object's __clone() method
|
|
* will be called, to allow any necessary properties that need to be changed.
|
|
* Example #1 Cloning an object
|
|
* <?php
|
|
* class SubObject
|
|
* {
|
|
* static $instances = 0;
|
|
* public $instance;
|
|
*
|
|
* public function __construct() {
|
|
* $this->instance = ++self::$instances;
|
|
* }
|
|
*
|
|
* public function __clone() {
|
|
* $this->instance = ++self::$instances;
|
|
* }
|
|
* }
|
|
*
|
|
* class MyCloneable
|
|
* {
|
|
* public $object1;
|
|
* public $object2;
|
|
*
|
|
* function __clone()
|
|
* {
|
|
* // Force a copy of this->object, otherwise
|
|
* // it will point to same object.
|
|
* $this->object1 = clone $this->object1;
|
|
* }
|
|
* }
|
|
* $obj = new MyCloneable();
|
|
* $obj->object1 = new SubObject();
|
|
* $obj->object2 = new SubObject();
|
|
* $obj2 = clone $obj;
|
|
* print("Original Object:\n");
|
|
* print_r($obj);
|
|
* print("Cloned Object:\n");
|
|
* print_r($obj2);
|
|
* ?>
|
|
* The above example will output:
|
|
* Original Object:
|
|
* MyCloneable Object
|
|
* (
|
|
* [object1] => SubObject Object
|
|
* (
|
|
* [instance] => 1
|
|
* )
|
|
*
|
|
* [object2] => SubObject Object
|
|
* (
|
|
* [instance] => 2
|
|
* )
|
|
*
|
|
* )
|
|
* Cloned Object:
|
|
* MyCloneable Object
|
|
* (
|
|
* [object1] => SubObject Object
|
|
* (
|
|
* [instance] => 3
|
|
* )
|
|
*
|
|
* [object2] => SubObject Object
|
|
* (
|
|
* [instance] => 2
|
|
* )
|
|
* )
|
|
*/
|
|
PH7_PRIVATE ph7_class_instance * PH7_CloneClassInstance(ph7_class_instance *pSrc)
|
|
{
|
|
ph7_class_instance *pClone;
|
|
ph7_class_method *pMethod;
|
|
SyHashEntry *pEntry2;
|
|
SyHashEntry *pEntry;
|
|
ph7_vm *pVm;
|
|
sxi32 rc;
|
|
/* Allocate a new instance */
|
|
pVm = pSrc->pVm;
|
|
pClone = NewClassInstance(pVm,pSrc->pClass);
|
|
if( pClone == 0 ){
|
|
return 0;
|
|
}
|
|
/* Associate a private VM frame with this class instance */
|
|
rc = PH7_VmCreateClassInstanceFrame(pVm,pClone);
|
|
if( rc != SXRET_OK ){
|
|
SyMemBackendPoolFree(&pVm->sAllocator,pClone);
|
|
return 0;
|
|
}
|
|
/* Duplicate object values */
|
|
SyHashResetLoopCursor(&pSrc->hAttr);
|
|
SyHashResetLoopCursor(&pClone->hAttr);
|
|
while((pEntry = SyHashGetNextEntry(&pSrc->hAttr)) != 0 && (pEntry2 = SyHashGetNextEntry(&pClone->hAttr)) != 0 ){
|
|
VmClassAttr *pSrcAttr = (VmClassAttr *)pEntry->pUserData;
|
|
VmClassAttr *pDestAttr = (VmClassAttr *)pEntry2->pUserData;
|
|
/* Duplicate non-static attribute */
|
|
if( (pSrcAttr->pAttr->iFlags & (PH7_CLASS_ATTR_STATIC|PH7_CLASS_ATTR_CONSTANT)) == 0 ){
|
|
ph7_value *pvSrc,*pvDest;
|
|
pvSrc = ExtractClassAttrValue(pVm,pSrcAttr);
|
|
pvDest = ExtractClassAttrValue(pVm,pDestAttr);
|
|
if( pvSrc && pvDest ){
|
|
PH7_MemObjStore(pvSrc,pvDest);
|
|
}
|
|
}
|
|
}
|
|
/* call the __clone method on the cloned object if available */
|
|
pMethod = PH7_ClassExtractMethod(pClone->pClass,"__clone",sizeof("__clone")-1);
|
|
if( pMethod ){
|
|
if( pMethod->iCloneDepth < 16 ){
|
|
pMethod->iCloneDepth++;
|
|
PH7_VmCallClassMethod(pVm,pClone,pMethod,0,0,0);
|
|
}else{
|
|
/* Nesting limit reached */
|
|
PH7_VmThrowError(pVm,0,PH7_CTX_ERR,"Object clone limit reached,no more call to __clone()");
|
|
}
|
|
/* Reset the cursor */
|
|
pMethod->iCloneDepth = 0;
|
|
}
|
|
/* Return the cloned object */
|
|
return pClone;
|
|
}
|
|
#define CLASS_INSTANCE_DESTROYED 0x001 /* Instance is released */
|
|
/*
|
|
* Release a class instance [i.e: Object in the PHP jargon] and invoke any defined destructor.
|
|
* This routine is invoked as soon as there are no other references to a particular
|
|
* class instance.
|
|
*/
|
|
static void PH7_ClassInstanceRelease(ph7_class_instance *pThis)
|
|
{
|
|
ph7_class_method *pDestr;
|
|
SyHashEntry *pEntry;
|
|
ph7_class *pClass;
|
|
ph7_vm *pVm;
|
|
if( pThis->iFlags & CLASS_INSTANCE_DESTROYED ){
|
|
/*
|
|
* Already destroyed,return immediately.
|
|
* This could happend if someone perform unset($this) in the destructor body.
|
|
*/
|
|
return;
|
|
}
|
|
/* Mark as destroyed */
|
|
pThis->iFlags |= CLASS_INSTANCE_DESTROYED;
|
|
/* Invoke any defined destructor if available */
|
|
pVm = pThis->pVm;
|
|
pClass = pThis->pClass;
|
|
pDestr = PH7_ClassExtractMethod(pClass,"__destruct",sizeof("__destruct")-1);
|
|
if( pDestr ){
|
|
/* Invoke the destructor */
|
|
pThis->iRef = 2; /* Prevent garbage collection */
|
|
PH7_VmCallClassMethod(pVm,pThis,pDestr,0,0,0);
|
|
}
|
|
/* Release non-static attributes */
|
|
SyHashResetLoopCursor(&pThis->hAttr);
|
|
while((pEntry = SyHashGetNextEntry(&pThis->hAttr)) != 0 ){
|
|
VmClassAttr *pVmAttr = (VmClassAttr *)pEntry->pUserData;
|
|
if( (pVmAttr->pAttr->iFlags & (PH7_CLASS_ATTR_STATIC|PH7_CLASS_ATTR_CONSTANT)) == 0 ){
|
|
PH7_VmUnsetMemObj(pVm,pVmAttr->nIdx,TRUE);
|
|
}
|
|
SyMemBackendPoolFree(&pVm->sAllocator,pVmAttr);
|
|
}
|
|
/* Release the whole structure */
|
|
SyHashRelease(&pThis->hAttr);
|
|
SyMemBackendPoolFree(&pVm->sAllocator,pThis);
|
|
}
|
|
/*
|
|
* Decrement the reference count of a class instance [i.e Object in the PHP jargon].
|
|
* If the reference count reaches zero,release the whole instance.
|
|
*/
|
|
PH7_PRIVATE void PH7_ClassInstanceUnref(ph7_class_instance *pThis)
|
|
{
|
|
pThis->iRef--;
|
|
if( pThis->iRef < 1 ){
|
|
/* No more reference to this instance */
|
|
PH7_ClassInstanceRelease(&(*pThis));
|
|
}
|
|
}
|
|
/*
|
|
* Compare two class instances [i.e: Objects in the PHP jargon]
|
|
* Note on objects comparison:
|
|
* According to the PHP langauge reference manual
|
|
* When using the comparison operator (==), object variables are compared in a simple manner
|
|
* namely: Two object instances are equal if they have the same attributes and values, and are
|
|
* instances of the same class.
|
|
* On the other hand, when using the identity operator (===), object variables are identical
|
|
* if and only if they refer to the same instance of the same class.
|
|
* An example will clarify these rules.
|
|
* Example #1 Example of object comparison
|
|
* <?php
|
|
* function bool2str($bool)
|
|
* {
|
|
* if ($bool === false) {
|
|
* return 'FALSE';
|
|
* } else {
|
|
* return 'TRUE';
|
|
* }
|
|
* }
|
|
* function compareObjects(&$o1, &$o2)
|
|
* {
|
|
* echo 'o1 == o2 : ' . bool2str($o1 == $o2) . "\n";
|
|
* echo 'o1 != o2 : ' . bool2str($o1 != $o2) . "\n";
|
|
* echo 'o1 === o2 : ' . bool2str($o1 === $o2) . "\n";
|
|
* echo 'o1 !== o2 : ' . bool2str($o1 !== $o2) . "\n";
|
|
* }
|
|
* class Flag
|
|
* {
|
|
* public $flag;
|
|
*
|
|
* function Flag($flag = true) {
|
|
* $this->flag = $flag;
|
|
* }
|
|
* }
|
|
*
|
|
* class OtherFlag
|
|
* {
|
|
* public $flag;
|
|
*
|
|
* function OtherFlag($flag = true) {
|
|
* $this->flag = $flag;
|
|
* }
|
|
* }
|
|
*
|
|
* $o = new Flag();
|
|
* $p = new Flag();
|
|
* $q = $o;
|
|
* $r = new OtherFlag();
|
|
*
|
|
* echo "Two instances of the same class\n";
|
|
* compareObjects($o, $p);
|
|
* echo "\nTwo references to the same instance\n";
|
|
* compareObjects($o, $q);
|
|
* echo "\nInstances of two different classes\n";
|
|
* compareObjects($o, $r);
|
|
* ?>
|
|
* The above example will output:
|
|
* Two instances of the same class
|
|
* o1 == o2 : TRUE
|
|
* o1 != o2 : FALSE
|
|
* o1 === o2 : FALSE
|
|
* o1 !== o2 : TRUE
|
|
* Two references to the same instance
|
|
* o1 == o2 : TRUE
|
|
* o1 != o2 : FALSE
|
|
* o1 === o2 : TRUE
|
|
* o1 !== o2 : FALSE
|
|
* Instances of two different classes
|
|
* o1 == o2 : FALSE
|
|
* o1 != o2 : TRUE
|
|
* o1 === o2 : FALSE
|
|
* o1 !== o2 : TRUE
|
|
*
|
|
* This function return 0 if the objects are equals according to the comprison rules defined above.
|
|
* Any other return values indicates difference.
|
|
*/
|
|
PH7_PRIVATE sxi32 PH7_ClassInstanceCmp(ph7_class_instance *pLeft,ph7_class_instance *pRight,int bStrict,int iNest)
|
|
{
|
|
SyHashEntry *pEntry,*pEntry2;
|
|
ph7_value sV1,sV2;
|
|
sxi32 rc;
|
|
if( iNest > 31 ){
|
|
/* Nesting limit reached */
|
|
PH7_VmThrowError(pLeft->pVm,0,PH7_CTX_ERR,"Nesting limit reached: Infinite recursion?");
|
|
return 1;
|
|
}
|
|
/* Comparison is performed only if the objects are instance of the same class */
|
|
if( pLeft->pClass != pRight->pClass ){
|
|
return 1;
|
|
}
|
|
if( bStrict ){
|
|
/*
|
|
* According to the PHP language reference manual:
|
|
* when using the identity operator (===), object variables
|
|
* are identical if and only if they refer to the same instance
|
|
* of the same class.
|
|
*/
|
|
return !(pLeft == pRight);
|
|
}
|
|
/*
|
|
* Attribute comparison.
|
|
* According to the PHP reference manual:
|
|
* When using the comparison operator (==), object variables are compared
|
|
* in a simple manner, namely: Two object instances are equal if they have
|
|
* the same attributes and values, and are instances of the same class.
|
|
*/
|
|
if( pLeft == pRight ){
|
|
/* Same instance,don't bother processing,object are equals */
|
|
return 0;
|
|
}
|
|
SyHashResetLoopCursor(&pLeft->hAttr);
|
|
SyHashResetLoopCursor(&pRight->hAttr);
|
|
PH7_MemObjInit(pLeft->pVm,&sV1);
|
|
PH7_MemObjInit(pLeft->pVm,&sV2);
|
|
sV1.nIdx = sV2.nIdx = SXU32_HIGH;
|
|
while((pEntry = SyHashGetNextEntry(&pLeft->hAttr)) != 0 && (pEntry2 = SyHashGetNextEntry(&pRight->hAttr)) != 0 ){
|
|
VmClassAttr *p1 = (VmClassAttr *)pEntry->pUserData;
|
|
VmClassAttr *p2 = (VmClassAttr *)pEntry2->pUserData;
|
|
/* Compare only non-static attribute */
|
|
if( (p1->pAttr->iFlags & (PH7_CLASS_ATTR_CONSTANT|PH7_CLASS_ATTR_STATIC)) == 0 ){
|
|
ph7_value *pL,*pR;
|
|
pL = ExtractClassAttrValue(pLeft->pVm,p1);
|
|
pR = ExtractClassAttrValue(pRight->pVm,p2);
|
|
if( pL && pR ){
|
|
PH7_MemObjLoad(pL,&sV1);
|
|
PH7_MemObjLoad(pR,&sV2);
|
|
/* Compare the two values now */
|
|
rc = PH7_MemObjCmp(&sV1,&sV2,bStrict,iNest+1);
|
|
PH7_MemObjRelease(&sV1);
|
|
PH7_MemObjRelease(&sV2);
|
|
if( rc != 0 ){
|
|
/* Not equals */
|
|
return rc;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* Object are equals */
|
|
return 0;
|
|
}
|
|
/*
|
|
* Dump a class instance and the store the dump in the BLOB given
|
|
* as the first argument.
|
|
* Note that only non-static/non-constants attribute are dumped.
|
|
* This function is typically invoked when the user issue a call
|
|
* to [var_dump(),var_export(),print_r(),...].
|
|
* This function SXRET_OK on success. Any other return value including
|
|
* SXERR_LIMIT(infinite recursion) indicates failure.
|
|
*/
|
|
PH7_PRIVATE sxi32 PH7_ClassInstanceDump(SyBlob *pOut,ph7_class_instance *pThis,int ShowType,int nTab,int nDepth)
|
|
{
|
|
SyHashEntry *pEntry;
|
|
ph7_value *pValue;
|
|
sxi32 rc;
|
|
int i;
|
|
if( nDepth > 31 ){
|
|
static const char zInfinite[] = "Nesting limit reached: Infinite recursion?";
|
|
/* Nesting limit reached..halt immediately*/
|
|
SyBlobAppend(&(*pOut),zInfinite,sizeof(zInfinite)-1);
|
|
if( ShowType ){
|
|
SyBlobAppend(&(*pOut),")",sizeof(char));
|
|
}
|
|
return SXERR_LIMIT;
|
|
}
|
|
rc = SXRET_OK;
|
|
if( !ShowType ){
|
|
SyBlobAppend(&(*pOut),"Object(",sizeof("Object(")-1);
|
|
}
|
|
/* Append class name */
|
|
SyBlobFormat(&(*pOut),"%z) {",&pThis->pClass->sName);
|
|
#ifdef __WINNT__
|
|
SyBlobAppend(&(*pOut),"\r\n",sizeof("\r\n")-1);
|
|
#else
|
|
SyBlobAppend(&(*pOut),"\n",sizeof(char));
|
|
#endif
|
|
/* Dump object attributes */
|
|
SyHashResetLoopCursor(&pThis->hAttr);
|
|
while((pEntry = SyHashGetNextEntry(&pThis->hAttr)) != 0){
|
|
VmClassAttr *pVmAttr = (VmClassAttr *)pEntry->pUserData;
|
|
if((pVmAttr->pAttr->iFlags & (PH7_CLASS_ATTR_CONSTANT|PH7_CLASS_ATTR_STATIC)) == 0 ){
|
|
/* Dump non-static/constant attribute only */
|
|
for( i = 0 ; i < nTab ; i++ ){
|
|
SyBlobAppend(&(*pOut)," ",sizeof(char));
|
|
}
|
|
pValue = ExtractClassAttrValue(pThis->pVm,pVmAttr);
|
|
if( pValue ){
|
|
SyBlobFormat(&(*pOut),"['%z'] =>",&pVmAttr->pAttr->sName);
|
|
#ifdef __WINNT__
|
|
SyBlobAppend(&(*pOut),"\r\n",sizeof("\r\n")-1);
|
|
#else
|
|
SyBlobAppend(&(*pOut),"\n",sizeof(char));
|
|
#endif
|
|
rc = PH7_MemObjDump(&(*pOut),pValue,ShowType,nTab+1,nDepth,0);
|
|
if( rc == SXERR_LIMIT ){
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for( i = 0 ; i < nTab ; i++ ){
|
|
SyBlobAppend(&(*pOut)," ",sizeof(char));
|
|
}
|
|
SyBlobAppend(&(*pOut),"}",sizeof(char));
|
|
return rc;
|
|
}
|
|
/*
|
|
* Call a magic method [i.e: __toString(),__toBool(),__Invoke()...]
|
|
* Return SXRET_OK on successfull call. Any other return value indicates failure.
|
|
* Notes on magic methods.
|
|
* According to the PHP language reference manual.
|
|
* The function names __construct(), __destruct(), __call(), __callStatic()
|
|
* __get(), __toString(), __invoke(), __clone() are magical in PHP classes.
|
|
* You cannot have functions with these names in any of your classes unless
|
|
* you want the magic functionality associated with them.
|
|
* Example of magical methods:
|
|
* __toString()
|
|
* The __toString() method allows a class to decide how it will react when it is treated like
|
|
* a string. For example, what echo $obj; will print. This method must return a string.
|
|
* Example #2 Simple example
|
|
* <?php
|
|
* // Declare a simple class
|
|
* class TestClass
|
|
* {
|
|
* public $foo;
|
|
*
|
|
* public function __construct($foo)
|
|
* {
|
|
* $this->foo = $foo;
|
|
* }
|
|
*
|
|
* public function __toString()
|
|
* {
|
|
* return $this->foo;
|
|
* }
|
|
* }
|
|
* $class = new TestClass('Hello');
|
|
* echo $class;
|
|
* ?>
|
|
* The above example will output:
|
|
* Hello
|
|
*
|
|
* Note that PH7 does not support all the magical method and introudces __toFloat(),__toInt()
|
|
* which have the same behaviour as __toString() but for float and integer types
|
|
* respectively.
|
|
* Refer to the official documentation for more information.
|
|
*/
|
|
PH7_PRIVATE sxi32 PH7_ClassInstanceCallMagicMethod(
|
|
ph7_vm *pVm, /* VM that own all this stuff */
|
|
ph7_class *pClass, /* Target class */
|
|
ph7_class_instance *pThis, /* Target object */
|
|
const char *zMethod, /* Magic method name [i.e: __toString()]*/
|
|
sxu32 nByte, /* zMethod length*/
|
|
const SyString *pAttrName /* Attribute name */
|
|
)
|
|
{
|
|
ph7_value *apArg[2] = { 0 , 0 };
|
|
ph7_class_method *pMeth;
|
|
ph7_value sAttr; /* cc warning */
|
|
sxi32 rc;
|
|
int nArg;
|
|
/* Make sure the magic method is available */
|
|
pMeth = PH7_ClassExtractMethod(&(*pClass),zMethod,nByte);
|
|
if( pMeth == 0 ){
|
|
/* No such method,return immediately */
|
|
return SXERR_NOTFOUND;
|
|
}
|
|
nArg = 0;
|
|
/* Copy arguments */
|
|
if( pAttrName ){
|
|
PH7_MemObjInitFromString(pVm,&sAttr,pAttrName);
|
|
sAttr.nIdx = SXU32_HIGH; /* Mark as constant */
|
|
apArg[0] = &sAttr;
|
|
nArg = 1;
|
|
}
|
|
/* Call the magic method now */
|
|
rc = PH7_VmCallClassMethod(pVm,&(*pThis),pMeth,0,nArg,apArg);
|
|
/* Clean up */
|
|
if( pAttrName ){
|
|
PH7_MemObjRelease(&sAttr);
|
|
}
|
|
return rc;
|
|
}
|
|
/*
|
|
* Extract the value of a class instance [i.e: Object in the PHP jargon].
|
|
* This function is simply a wrapper on ExtractClassAttrValue().
|
|
*/
|
|
PH7_PRIVATE ph7_value * PH7_ClassInstanceExtractAttrValue(ph7_class_instance *pThis,VmClassAttr *pAttr)
|
|
{
|
|
/* Extract the attribute value */
|
|
ph7_value *pValue;
|
|
pValue = ExtractClassAttrValue(pThis->pVm,pAttr);
|
|
return pValue;
|
|
}
|
|
/*
|
|
* Convert a class instance [i.e: Object in the PHP jargon] into a hashmap [i.e: array in the PHP jargon].
|
|
* Return SXRET_OK on success. Any other value indicates failure.
|
|
* Note on object conversion to array:
|
|
* Acccording to the PHP language reference manual
|
|
* If an object is converted to an array, the result is an array whose elements are the object's properties.
|
|
* The keys are the member variable names.
|
|
*
|
|
* The following example:
|
|
* class Test {
|
|
* public $A = 25<<1; // 50
|
|
* public $c = rand_str(3); // Random string of length 3
|
|
* public $d = rand() & 1023; // Random number between 0..1023
|
|
* }
|
|
* var_dump((array) new Test());
|
|
* Will output:
|
|
* array(3) {
|
|
* [A] =>
|
|
* int(50)
|
|
* [c] =>
|
|
* string(3 'aps')
|
|
* [d] =>
|
|
* int(991)
|
|
* }
|
|
* You have noticed that PH7 allow class attributes [i.e: $a,$c,$d in the example above]
|
|
* have any complex expression (even function calls/Annonymous functions) as their default
|
|
* value unlike the standard PHP engine.
|
|
* This is a very powerful feature that you have to look at.
|
|
*/
|
|
PH7_PRIVATE sxi32 PH7_ClassInstanceToHashmap(ph7_class_instance *pThis,ph7_hashmap *pMap)
|
|
{
|
|
SyHashEntry *pEntry;
|
|
SyString *pAttrName;
|
|
VmClassAttr *pAttr;
|
|
ph7_value *pValue;
|
|
ph7_value sName;
|
|
/* Reset the loop cursor */
|
|
SyHashResetLoopCursor(&pThis->hAttr);
|
|
PH7_MemObjInitFromString(pThis->pVm,&sName,0);
|
|
while((pEntry = SyHashGetNextEntry(&pThis->hAttr)) != 0 ){
|
|
/* Point to the current attribute */
|
|
pAttr = (VmClassAttr *)pEntry->pUserData;
|
|
/* Extract attribute value */
|
|
pValue = ExtractClassAttrValue(pThis->pVm,pAttr);
|
|
if( pValue ){
|
|
/* Build attribute name */
|
|
pAttrName = &pAttr->pAttr->sName;
|
|
PH7_MemObjStringAppend(&sName,pAttrName->zString,pAttrName->nByte);
|
|
/* Perform the insertion */
|
|
PH7_HashmapInsert(pMap,&sName,pValue);
|
|
/* Reset the string cursor */
|
|
SyBlobReset(&sName.sBlob);
|
|
}
|
|
}
|
|
PH7_MemObjRelease(&sName);
|
|
return SXRET_OK;
|
|
}
|
|
/*
|
|
* Iterate throw class attributes and invoke the given callback [i.e: xWalk()] for each
|
|
* retrieved attribute.
|
|
* Note that argument are passed to the callback by copy. That is,any modification to
|
|
* the attribute value in the callback body will not alter the real attribute value.
|
|
* If the callback wishes to abort processing [i.e: it's invocation] it must return
|
|
* a value different from PH7_OK.
|
|
* Refer to [ph7_object_walk()] for more information.
|
|
*/
|
|
PH7_PRIVATE sxi32 PH7_ClassInstanceWalk(
|
|
ph7_class_instance *pThis, /* Target object */
|
|
int (*xWalk)(const char *,ph7_value *,void *), /* Walker callback */
|
|
void *pUserData /* Last argument to xWalk() */
|
|
)
|
|
{
|
|
SyHashEntry *pEntry; /* Hash entry */
|
|
VmClassAttr *pAttr; /* Pointer to the attribute */
|
|
ph7_value *pValue; /* Attribute value */
|
|
ph7_value sValue; /* Copy of the attribute value */
|
|
int rc;
|
|
/* Reset the loop cursor */
|
|
SyHashResetLoopCursor(&pThis->hAttr);
|
|
PH7_MemObjInit(pThis->pVm,&sValue);
|
|
/* Start the walk process */
|
|
while((pEntry = SyHashGetNextEntry(&pThis->hAttr)) != 0 ){
|
|
/* Point to the current attribute */
|
|
pAttr = (VmClassAttr *)pEntry->pUserData;
|
|
/* Extract attribute value */
|
|
pValue = ExtractClassAttrValue(pThis->pVm,pAttr);
|
|
if( pValue ){
|
|
PH7_MemObjLoad(pValue,&sValue);
|
|
/* Invoke the supplied callback */
|
|
rc = xWalk(SyStringData(&pAttr->pAttr->sName),&sValue,pUserData);
|
|
PH7_MemObjRelease(&sValue);
|
|
if( rc != PH7_OK){
|
|
/* User callback request an operation abort */
|
|
return SXERR_ABORT;
|
|
}
|
|
}
|
|
}
|
|
/* All done */
|
|
return SXRET_OK;
|
|
}
|
|
/*
|
|
* Extract a class atrribute value.
|
|
* Return a pointer to the attribute value on success. Otherwise NULL.
|
|
* Note:
|
|
* Access to static and constant attribute is not allowed. That is,the function
|
|
* will return NULL in case someone (host-application code) try to extract
|
|
* a static/constant attribute.
|
|
*/
|
|
PH7_PRIVATE ph7_value * PH7_ClassInstanceFetchAttr(ph7_class_instance *pThis,const SyString *pName)
|
|
{
|
|
SyHashEntry *pEntry;
|
|
VmClassAttr *pAttr;
|
|
/* Query the attribute hashtable */
|
|
pEntry = SyHashGet(&pThis->hAttr,(const void *)pName->zString,pName->nByte);
|
|
if( pEntry == 0 ){
|
|
/* No such attribute */
|
|
return 0;
|
|
}
|
|
/* Point to the class atrribute */
|
|
pAttr = (VmClassAttr *)pEntry->pUserData;
|
|
/* Check if we are dealing with a static/constant attribute */
|
|
if( pAttr->pAttr->iFlags & (PH7_CLASS_ATTR_CONSTANT|PH7_CLASS_ATTR_STATIC) ){
|
|
/* Access is forbidden */
|
|
return 0;
|
|
}
|
|
/* Return the attribute value */
|
|
return ExtractClassAttrValue(pThis->pVm,pAttr);
|
|
}
|