Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed memory leak when returning an array #34

Merged
merged 1 commit into from
May 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 2 additions & 8 deletions example/skel/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,12 +175,6 @@ pub extern "C" fn skeleton_array(execute_data: &mut ExecutionData, _retval: &mut
}

#[no_mangle]
pub extern "C" fn test_array(execute_data: &mut ExecutionData, retval: &mut Zval) {
let mut hm = HashMap::new();
hm.insert("Hello", 123);
hm.insert("World", 456);
hm.insert("Asdf", 789);

let x: ZendHashTable = (&hm).into();
retval.set_array(x);
pub extern "C" fn test_array(_execute_data: &mut ExecutionData, retval: &mut Zval) {
retval.set_array(vec![1, 2, 3, 4]);
}
17 changes: 0 additions & 17 deletions example/skel/test.php
Original file line number Diff line number Diff line change
@@ -1,19 +1,2 @@
<?php

var_dump(skeleton_version(['world' => 'hello', 1, 3],2.1123));
die;

var_dump(SKEL_TEST_CONST, SKEL_TEST_LONG_CONST);
var_dump(test_array());
die;

$x = new TestClass();

skeleton_version(1, 2);

var_dump($x->call(function ($v1, $v2) {
// var_dump($v1, $v2);
// echo "Hello, world! I'm a callable.".PHP_EOL;
// return "Ok rust";
return 0;
}));
17 changes: 13 additions & 4 deletions src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::{error::Error as ErrorTrait, fmt::Display};

use crate::php::enums::DataType;
use crate::php::{enums::DataType, flags::ZvalTypeFlags};

/// The main result type which is passed by the library.
pub type Result<T> = std::result::Result<T, Error>;
Expand All @@ -12,20 +12,26 @@ pub type Result<T> = std::result::Result<T, Error>;
pub enum Error {
/// An incorrect number of arguments was given to a PHP function.
///
/// The type carries two integers - the first representing the minimum
/// The enum carries two integers - the first representing the minimum
/// number of arguments expected, and the second representing the number of
/// arguments that were received.
IncorrectArguments(u32, u32),

/// There was an error converting a Zval into a primitive type.
///
/// The type carries the data type of the Zval.
/// The enum carries the data type of the Zval.
ZvalConversion(DataType),

/// The type of the Zval is unknown.
///
/// The type carries the integer representation of the type of Zval.
/// The enum carries the integer representation of the type of Zval.
UnknownDatatype(u8),

/// Attempted to convert a [`ZvalTypeFlags`] struct to a [`DataType`].
/// The flags did not contain a datatype.
///
/// The enum carries the flags that were attempted to be converted to a [`DataType`].
InvalidTypeToDatatype(ZvalTypeFlags),
}

impl Display for Error {
Expand All @@ -42,6 +48,9 @@ impl Display for Error {
ty
),
Error::UnknownDatatype(dt) => write!(f, "Unknown datatype {}.", dt),
Error::InvalidTypeToDatatype(dt) => {
write!(f, "Type flags did not contain a datatype: {:?}", dt)
}
}
}
}
Expand Down
52 changes: 34 additions & 18 deletions src/php/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use crate::{
IS_REFERENCE, IS_RESOURCE, IS_STRING, IS_TRUE, IS_UNDEF, IS_VOID,
},
errors::{Error, Result},
php::flags::ZvalTypeFlags,
};

/// Valid data types for PHP.
Expand All @@ -32,28 +33,43 @@ pub enum DataType {
Void = IS_VOID,
}

impl TryFrom<ZvalTypeFlags> for DataType {
type Error = Error;

fn try_from(value: ZvalTypeFlags) -> Result<Self> {
macro_rules! contains {
($t: ident) => {
if value.contains(ZvalTypeFlags::$t) {
return Ok(DataType::$t);
}
};
}

contains!(Undef);
contains!(Null);
contains!(False);
contains!(True);
contains!(False);
contains!(Long);
contains!(Double);
contains!(String);
contains!(Array);
contains!(Object);
contains!(Resource);
contains!(Callable);
contains!(ConstantExpression);
contains!(Void);

Err(Error::UnknownDatatype(0))
}
}

impl TryFrom<u8> for DataType {
type Error = Error;

fn try_from(value: u8) -> Result<Self> {
match value as u32 {
IS_UNDEF => Ok(DataType::Undef),
IS_NULL => Ok(DataType::Null),
IS_FALSE => Ok(DataType::False),
IS_TRUE => Ok(DataType::True),
IS_LONG => Ok(DataType::Long),
IS_DOUBLE => Ok(DataType::Double),
IS_STRING => Ok(DataType::String),
IS_ARRAY => Ok(DataType::Array),
IS_OBJECT => Ok(DataType::Object),
IS_RESOURCE => Ok(DataType::Resource),
IS_REFERENCE => Ok(DataType::Reference),
IS_CALLABLE => Ok(DataType::Callable),
IS_CONSTANT_AST => Ok(DataType::ConstantExpression),
IS_VOID => Ok(DataType::Void),

_ => Err(Error::UnknownDatatype(value)),
}
let flags = ZvalTypeFlags::from_bits(value.into()).ok_or(Error::UnknownDatatype(value))?;
DataType::try_from(flags)
}
}

Expand Down
60 changes: 47 additions & 13 deletions src/php/flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,55 @@
use bitflags::bitflags;

use crate::bindings::{
CONST_CS, CONST_DEPRECATED, CONST_NO_FILE_CACHE, CONST_PERSISTENT, ZEND_ACC_ABSTRACT,
ZEND_ACC_ANON_CLASS, ZEND_ACC_CALL_VIA_TRAMPOLINE, ZEND_ACC_CHANGED, ZEND_ACC_CLOSURE,
ZEND_ACC_CONSTANTS_UPDATED, ZEND_ACC_CTOR, ZEND_ACC_DEPRECATED, ZEND_ACC_DONE_PASS_TWO,
ZEND_ACC_EARLY_BINDING, ZEND_ACC_FAKE_CLOSURE, ZEND_ACC_FINAL, ZEND_ACC_GENERATOR,
ZEND_ACC_HAS_FINALLY_BLOCK, ZEND_ACC_HAS_RETURN_TYPE, ZEND_ACC_HAS_TYPE_HINTS,
ZEND_ACC_HAS_UNLINKED_USES, ZEND_ACC_HEAP_RT_CACHE, ZEND_ACC_IMMUTABLE,
ZEND_ACC_IMPLICIT_ABSTRACT_CLASS, ZEND_ACC_INTERFACE, ZEND_ACC_LINKED, ZEND_ACC_NEARLY_LINKED,
ZEND_ACC_NEVER_CACHE, ZEND_ACC_NO_DYNAMIC_PROPERTIES, ZEND_ACC_PRELOADED, ZEND_ACC_PRIVATE,
ZEND_ACC_PROMOTED, ZEND_ACC_PROPERTY_TYPES_RESOLVED, ZEND_ACC_PROTECTED, ZEND_ACC_PUBLIC,
ZEND_ACC_RESOLVED_INTERFACES, ZEND_ACC_RESOLVED_PARENT, ZEND_ACC_RETURN_REFERENCE,
ZEND_ACC_REUSE_GET_ITERATOR, ZEND_ACC_STATIC, ZEND_ACC_STRICT_TYPES, ZEND_ACC_TOP_LEVEL,
ZEND_ACC_TRAIT, ZEND_ACC_TRAIT_CLONE, ZEND_ACC_UNRESOLVED_VARIANCE, ZEND_ACC_USES_THIS,
ZEND_ACC_USE_GUARDS, ZEND_ACC_VARIADIC, ZEND_HAS_STATIC_IN_METHODS,
CONST_CS, CONST_DEPRECATED, CONST_NO_FILE_CACHE, CONST_PERSISTENT, IS_ARRAY, IS_CALLABLE,
IS_CONSTANT_AST, IS_DOUBLE, IS_FALSE, IS_LONG, IS_NULL, IS_OBJECT, IS_REFERENCE, IS_RESOURCE,
IS_STRING, IS_TRUE, IS_TYPE_COLLECTABLE, IS_TYPE_REFCOUNTED, IS_UNDEF, IS_VOID,
ZEND_ACC_ABSTRACT, ZEND_ACC_ANON_CLASS, ZEND_ACC_CALL_VIA_TRAMPOLINE, ZEND_ACC_CHANGED,
ZEND_ACC_CLOSURE, ZEND_ACC_CONSTANTS_UPDATED, ZEND_ACC_CTOR, ZEND_ACC_DEPRECATED,
ZEND_ACC_DONE_PASS_TWO, ZEND_ACC_EARLY_BINDING, ZEND_ACC_FAKE_CLOSURE, ZEND_ACC_FINAL,
ZEND_ACC_GENERATOR, ZEND_ACC_HAS_FINALLY_BLOCK, ZEND_ACC_HAS_RETURN_TYPE,
ZEND_ACC_HAS_TYPE_HINTS, ZEND_ACC_HAS_UNLINKED_USES, ZEND_ACC_HEAP_RT_CACHE,
ZEND_ACC_IMMUTABLE, ZEND_ACC_IMPLICIT_ABSTRACT_CLASS, ZEND_ACC_INTERFACE, ZEND_ACC_LINKED,
ZEND_ACC_NEARLY_LINKED, ZEND_ACC_NEVER_CACHE, ZEND_ACC_NO_DYNAMIC_PROPERTIES,
ZEND_ACC_PRELOADED, ZEND_ACC_PRIVATE, ZEND_ACC_PROMOTED, ZEND_ACC_PROPERTY_TYPES_RESOLVED,
ZEND_ACC_PROTECTED, ZEND_ACC_PUBLIC, ZEND_ACC_RESOLVED_INTERFACES, ZEND_ACC_RESOLVED_PARENT,
ZEND_ACC_RETURN_REFERENCE, ZEND_ACC_REUSE_GET_ITERATOR, ZEND_ACC_STATIC, ZEND_ACC_STRICT_TYPES,
ZEND_ACC_TOP_LEVEL, ZEND_ACC_TRAIT, ZEND_ACC_TRAIT_CLONE, ZEND_ACC_UNRESOLVED_VARIANCE,
ZEND_ACC_USES_THIS, ZEND_ACC_USE_GUARDS, ZEND_ACC_VARIADIC, ZEND_HAS_STATIC_IN_METHODS,
Z_TYPE_FLAGS_SHIFT,
};

bitflags! {
/// Flags used for setting the type of Zval.
pub struct ZvalTypeFlags: u32 {
const Undef = IS_UNDEF;
const Null = IS_NULL;
const False = IS_FALSE;
const True = IS_TRUE;
const Long = IS_LONG;
const Double = IS_DOUBLE;
const String = IS_STRING;
const Array = IS_ARRAY;
const Object = IS_OBJECT;
const Resource = IS_RESOURCE;
const Reference = IS_REFERENCE;
const Callable = IS_CALLABLE;
const ConstantExpression = IS_CONSTANT_AST;
const Void = IS_VOID;

const InternedStringEx = Self::String.bits;
const StringEx = Self::String.bits | Self::RefCounted.bits;
const ArrayEx = Self::Array.bits | Self::RefCounted.bits | Self::Collectable.bits;
const ObjectEx = Self::Object.bits | Self::RefCounted.bits | Self::Collectable.bits;
const ResourceEx = Self::Resource.bits | Self::RefCounted.bits;
const ReferenceEx = Self::Reference.bits | Self::RefCounted.bits;
const ConstantAstEx = Self::ConstantExpression.bits | Self::RefCounted.bits;

const RefCounted = (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT);
const Collectable = (IS_TYPE_COLLECTABLE << Z_TYPE_FLAGS_SHIFT);
}
}

bitflags! {
/// Flags for building classes.
pub struct ClassFlags: u32 {
Expand Down
21 changes: 11 additions & 10 deletions src/php/types/zval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@ use crate::{
bindings::{
_call_user_function_impl, _zval_struct__bindgen_ty_1, _zval_struct__bindgen_ty_2,
ext_php_rs_zend_string_release, zend_is_callable, zend_object, zend_resource, zend_value,
zval, IS_INTERNED_STRING_EX, IS_STRING_EX,
zval,
},
errors::{Error, Result},
};

use crate::php::{
enums::DataType,
flags::ZvalTypeFlags,
types::{long::ZendLong, string::ZendString},
};

Expand Down Expand Up @@ -258,7 +259,7 @@ impl<'a> Zval {
{
let zend_str = ZendString::new(val, false);
self.value.str_ = zend_str;
self.u1.type_info = IS_STRING_EX;
self.u1.type_info = ZvalTypeFlags::StringEx.bits();
}

/// Sets the value of the zval as a persistent string.
Expand All @@ -274,7 +275,7 @@ impl<'a> Zval {
{
let zend_str = ZendString::new(val, true);
self.value.str_ = zend_str;
self.u1.type_info = IS_STRING_EX;
self.u1.type_info = ZvalTypeFlags::StringEx.bits();
}

/// Sets the value of the zval as a interned string.
Expand All @@ -288,7 +289,7 @@ impl<'a> Zval {
{
let zend_str = ZendString::new_interned(val);
self.value.str_ = zend_str;
self.u1.type_info = IS_INTERNED_STRING_EX;
self.u1.type_info = ZvalTypeFlags::InternedStringEx.bits();
}

/// Sets the value of the zval as a long.
Expand All @@ -298,7 +299,7 @@ impl<'a> Zval {
/// * `val` - The value to set the zval as.
pub fn set_long<T: Into<ZendLong>>(&mut self, val: T) {
self.value.lval = val.into();
self.u1.type_info = DataType::Long as u32;
self.u1.type_info = ZvalTypeFlags::Long.bits();
}

/// Sets the value of the zval as a double.
Expand All @@ -308,7 +309,7 @@ impl<'a> Zval {
/// * `val` - The value to set the zval as.
pub fn set_double<T: Into<libc::c_double>>(&mut self, val: T) {
self.value.dval = val.into();
self.u1.type_info = DataType::Double as u32;
self.u1.type_info = ZvalTypeFlags::Double.bits();
}

/// Sets the value of the zval as a boolean.
Expand All @@ -327,7 +328,7 @@ impl<'a> Zval {
/// Sets the value of the zval as null.
/// This is the default of a zval.
pub fn set_null(&mut self) {
self.u1.type_info = DataType::Null as u32;
self.u1.type_info = ZvalTypeFlags::Null.bits();
}

/// Sets the value of the zval as a resource.
Expand All @@ -336,7 +337,7 @@ impl<'a> Zval {
///
/// * `val` - The value to set the zval as.
pub fn set_resource(&mut self, val: *mut zend_resource) {
self.u1.type_info = DataType::Resource as u32;
self.u1.type_info = ZvalTypeFlags::ResourceEx.bits();
self.value.res = val;
}

Expand All @@ -347,7 +348,7 @@ impl<'a> Zval {
/// * `val` - The value to set the zval as.
/// * `copy` - Whether to copy the object or pass as a reference.
pub fn set_object(&mut self, val: *mut zend_object, _copy: bool) {
self.u1.type_info = DataType::Object as u32;
self.u1.type_info = ZvalTypeFlags::ObjectEx.bits();
self.value.obj = val;
}

Expand All @@ -360,7 +361,7 @@ impl<'a> Zval {
where
V: Into<ZendHashTable>,
{
self.u1.type_info = DataType::Array as u32;
self.u1.type_info = ZvalTypeFlags::ArrayEx.bits();
self.value.arr = val.into().into_ptr();
}
}
Expand Down