Skip to content

Commit 042c70a

Browse files
Fixed memory leak when returning an array (#34)
1 parent d73788e commit 042c70a

File tree

6 files changed

+107
-70
lines changed

6 files changed

+107
-70
lines changed

example/skel/src/lib.rs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -175,12 +175,6 @@ pub extern "C" fn skeleton_array(execute_data: &mut ExecutionData, _retval: &mut
175175
}
176176

177177
#[no_mangle]
178-
pub extern "C" fn test_array(execute_data: &mut ExecutionData, retval: &mut Zval) {
179-
let mut hm = HashMap::new();
180-
hm.insert("Hello", 123);
181-
hm.insert("World", 456);
182-
hm.insert("Asdf", 789);
183-
184-
let x: ZendHashTable = (&hm).into();
185-
retval.set_array(x);
178+
pub extern "C" fn test_array(_execute_data: &mut ExecutionData, retval: &mut Zval) {
179+
retval.set_array(vec![1, 2, 3, 4]);
186180
}

example/skel/test.php

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,2 @@
11
<?php
2-
3-
var_dump(skeleton_version(['world' => 'hello', 1, 3],2.1123));
4-
die;
5-
6-
var_dump(SKEL_TEST_CONST, SKEL_TEST_LONG_CONST);
72
var_dump(test_array());
8-
die;
9-
10-
$x = new TestClass();
11-
12-
skeleton_version(1, 2);
13-
14-
var_dump($x->call(function ($v1, $v2) {
15-
// var_dump($v1, $v2);
16-
// echo "Hello, world! I'm a callable.".PHP_EOL;
17-
// return "Ok rust";
18-
return 0;
19-
}));

src/errors.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::{error::Error as ErrorTrait, fmt::Display};
22

3-
use crate::php::enums::DataType;
3+
use crate::php::{enums::DataType, flags::ZvalTypeFlags};
44

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

2020
/// There was an error converting a Zval into a primitive type.
2121
///
22-
/// The type carries the data type of the Zval.
22+
/// The enum carries the data type of the Zval.
2323
ZvalConversion(DataType),
2424

2525
/// The type of the Zval is unknown.
2626
///
27-
/// The type carries the integer representation of the type of Zval.
27+
/// The enum carries the integer representation of the type of Zval.
2828
UnknownDatatype(u8),
29+
30+
/// Attempted to convert a [`ZvalTypeFlags`] struct to a [`DataType`].
31+
/// The flags did not contain a datatype.
32+
///
33+
/// The enum carries the flags that were attempted to be converted to a [`DataType`].
34+
InvalidTypeToDatatype(ZvalTypeFlags),
2935
}
3036

3137
impl Display for Error {
@@ -42,6 +48,9 @@ impl Display for Error {
4248
ty
4349
),
4450
Error::UnknownDatatype(dt) => write!(f, "Unknown datatype {}.", dt),
51+
Error::InvalidTypeToDatatype(dt) => {
52+
write!(f, "Type flags did not contain a datatype: {:?}", dt)
53+
}
4554
}
4655
}
4756
}

src/php/enums.rs

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use crate::{
88
IS_REFERENCE, IS_RESOURCE, IS_STRING, IS_TRUE, IS_UNDEF, IS_VOID,
99
},
1010
errors::{Error, Result},
11+
php::flags::ZvalTypeFlags,
1112
};
1213

1314
/// Valid data types for PHP.
@@ -32,28 +33,43 @@ pub enum DataType {
3233
Void = IS_VOID,
3334
}
3435

36+
impl TryFrom<ZvalTypeFlags> for DataType {
37+
type Error = Error;
38+
39+
fn try_from(value: ZvalTypeFlags) -> Result<Self> {
40+
macro_rules! contains {
41+
($t: ident) => {
42+
if value.contains(ZvalTypeFlags::$t) {
43+
return Ok(DataType::$t);
44+
}
45+
};
46+
}
47+
48+
contains!(Undef);
49+
contains!(Null);
50+
contains!(False);
51+
contains!(True);
52+
contains!(False);
53+
contains!(Long);
54+
contains!(Double);
55+
contains!(String);
56+
contains!(Array);
57+
contains!(Object);
58+
contains!(Resource);
59+
contains!(Callable);
60+
contains!(ConstantExpression);
61+
contains!(Void);
62+
63+
Err(Error::UnknownDatatype(0))
64+
}
65+
}
66+
3567
impl TryFrom<u8> for DataType {
3668
type Error = Error;
3769

3870
fn try_from(value: u8) -> Result<Self> {
39-
match value as u32 {
40-
IS_UNDEF => Ok(DataType::Undef),
41-
IS_NULL => Ok(DataType::Null),
42-
IS_FALSE => Ok(DataType::False),
43-
IS_TRUE => Ok(DataType::True),
44-
IS_LONG => Ok(DataType::Long),
45-
IS_DOUBLE => Ok(DataType::Double),
46-
IS_STRING => Ok(DataType::String),
47-
IS_ARRAY => Ok(DataType::Array),
48-
IS_OBJECT => Ok(DataType::Object),
49-
IS_RESOURCE => Ok(DataType::Resource),
50-
IS_REFERENCE => Ok(DataType::Reference),
51-
IS_CALLABLE => Ok(DataType::Callable),
52-
IS_CONSTANT_AST => Ok(DataType::ConstantExpression),
53-
IS_VOID => Ok(DataType::Void),
54-
55-
_ => Err(Error::UnknownDatatype(value)),
56-
}
71+
let flags = ZvalTypeFlags::from_bits(value.into()).ok_or(Error::UnknownDatatype(value))?;
72+
DataType::try_from(flags)
5773
}
5874
}
5975

src/php/flags.rs

Lines changed: 47 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,55 @@
33
use bitflags::bitflags;
44

55
use crate::bindings::{
6-
CONST_CS, CONST_DEPRECATED, CONST_NO_FILE_CACHE, CONST_PERSISTENT, ZEND_ACC_ABSTRACT,
7-
ZEND_ACC_ANON_CLASS, ZEND_ACC_CALL_VIA_TRAMPOLINE, ZEND_ACC_CHANGED, ZEND_ACC_CLOSURE,
8-
ZEND_ACC_CONSTANTS_UPDATED, ZEND_ACC_CTOR, ZEND_ACC_DEPRECATED, ZEND_ACC_DONE_PASS_TWO,
9-
ZEND_ACC_EARLY_BINDING, ZEND_ACC_FAKE_CLOSURE, ZEND_ACC_FINAL, ZEND_ACC_GENERATOR,
10-
ZEND_ACC_HAS_FINALLY_BLOCK, ZEND_ACC_HAS_RETURN_TYPE, ZEND_ACC_HAS_TYPE_HINTS,
11-
ZEND_ACC_HAS_UNLINKED_USES, ZEND_ACC_HEAP_RT_CACHE, ZEND_ACC_IMMUTABLE,
12-
ZEND_ACC_IMPLICIT_ABSTRACT_CLASS, ZEND_ACC_INTERFACE, ZEND_ACC_LINKED, ZEND_ACC_NEARLY_LINKED,
13-
ZEND_ACC_NEVER_CACHE, ZEND_ACC_NO_DYNAMIC_PROPERTIES, ZEND_ACC_PRELOADED, ZEND_ACC_PRIVATE,
14-
ZEND_ACC_PROMOTED, ZEND_ACC_PROPERTY_TYPES_RESOLVED, ZEND_ACC_PROTECTED, ZEND_ACC_PUBLIC,
15-
ZEND_ACC_RESOLVED_INTERFACES, ZEND_ACC_RESOLVED_PARENT, ZEND_ACC_RETURN_REFERENCE,
16-
ZEND_ACC_REUSE_GET_ITERATOR, ZEND_ACC_STATIC, ZEND_ACC_STRICT_TYPES, ZEND_ACC_TOP_LEVEL,
17-
ZEND_ACC_TRAIT, ZEND_ACC_TRAIT_CLONE, ZEND_ACC_UNRESOLVED_VARIANCE, ZEND_ACC_USES_THIS,
18-
ZEND_ACC_USE_GUARDS, ZEND_ACC_VARIADIC, ZEND_HAS_STATIC_IN_METHODS,
6+
CONST_CS, CONST_DEPRECATED, CONST_NO_FILE_CACHE, CONST_PERSISTENT, IS_ARRAY, IS_CALLABLE,
7+
IS_CONSTANT_AST, IS_DOUBLE, IS_FALSE, IS_LONG, IS_NULL, IS_OBJECT, IS_REFERENCE, IS_RESOURCE,
8+
IS_STRING, IS_TRUE, IS_TYPE_COLLECTABLE, IS_TYPE_REFCOUNTED, IS_UNDEF, IS_VOID,
9+
ZEND_ACC_ABSTRACT, ZEND_ACC_ANON_CLASS, ZEND_ACC_CALL_VIA_TRAMPOLINE, ZEND_ACC_CHANGED,
10+
ZEND_ACC_CLOSURE, ZEND_ACC_CONSTANTS_UPDATED, ZEND_ACC_CTOR, ZEND_ACC_DEPRECATED,
11+
ZEND_ACC_DONE_PASS_TWO, ZEND_ACC_EARLY_BINDING, ZEND_ACC_FAKE_CLOSURE, ZEND_ACC_FINAL,
12+
ZEND_ACC_GENERATOR, ZEND_ACC_HAS_FINALLY_BLOCK, ZEND_ACC_HAS_RETURN_TYPE,
13+
ZEND_ACC_HAS_TYPE_HINTS, ZEND_ACC_HAS_UNLINKED_USES, ZEND_ACC_HEAP_RT_CACHE,
14+
ZEND_ACC_IMMUTABLE, ZEND_ACC_IMPLICIT_ABSTRACT_CLASS, ZEND_ACC_INTERFACE, ZEND_ACC_LINKED,
15+
ZEND_ACC_NEARLY_LINKED, ZEND_ACC_NEVER_CACHE, ZEND_ACC_NO_DYNAMIC_PROPERTIES,
16+
ZEND_ACC_PRELOADED, ZEND_ACC_PRIVATE, ZEND_ACC_PROMOTED, ZEND_ACC_PROPERTY_TYPES_RESOLVED,
17+
ZEND_ACC_PROTECTED, ZEND_ACC_PUBLIC, ZEND_ACC_RESOLVED_INTERFACES, ZEND_ACC_RESOLVED_PARENT,
18+
ZEND_ACC_RETURN_REFERENCE, ZEND_ACC_REUSE_GET_ITERATOR, ZEND_ACC_STATIC, ZEND_ACC_STRICT_TYPES,
19+
ZEND_ACC_TOP_LEVEL, ZEND_ACC_TRAIT, ZEND_ACC_TRAIT_CLONE, ZEND_ACC_UNRESOLVED_VARIANCE,
20+
ZEND_ACC_USES_THIS, ZEND_ACC_USE_GUARDS, ZEND_ACC_VARIADIC, ZEND_HAS_STATIC_IN_METHODS,
21+
Z_TYPE_FLAGS_SHIFT,
1922
};
2023

24+
bitflags! {
25+
/// Flags used for setting the type of Zval.
26+
pub struct ZvalTypeFlags: u32 {
27+
const Undef = IS_UNDEF;
28+
const Null = IS_NULL;
29+
const False = IS_FALSE;
30+
const True = IS_TRUE;
31+
const Long = IS_LONG;
32+
const Double = IS_DOUBLE;
33+
const String = IS_STRING;
34+
const Array = IS_ARRAY;
35+
const Object = IS_OBJECT;
36+
const Resource = IS_RESOURCE;
37+
const Reference = IS_REFERENCE;
38+
const Callable = IS_CALLABLE;
39+
const ConstantExpression = IS_CONSTANT_AST;
40+
const Void = IS_VOID;
41+
42+
const InternedStringEx = Self::String.bits;
43+
const StringEx = Self::String.bits | Self::RefCounted.bits;
44+
const ArrayEx = Self::Array.bits | Self::RefCounted.bits | Self::Collectable.bits;
45+
const ObjectEx = Self::Object.bits | Self::RefCounted.bits | Self::Collectable.bits;
46+
const ResourceEx = Self::Resource.bits | Self::RefCounted.bits;
47+
const ReferenceEx = Self::Reference.bits | Self::RefCounted.bits;
48+
const ConstantAstEx = Self::ConstantExpression.bits | Self::RefCounted.bits;
49+
50+
const RefCounted = (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT);
51+
const Collectable = (IS_TYPE_COLLECTABLE << Z_TYPE_FLAGS_SHIFT);
52+
}
53+
}
54+
2155
bitflags! {
2256
/// Flags for building classes.
2357
pub struct ClassFlags: u32 {

src/php/types/zval.rs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,14 @@ use crate::{
88
bindings::{
99
_call_user_function_impl, _zval_struct__bindgen_ty_1, _zval_struct__bindgen_ty_2,
1010
ext_php_rs_zend_string_release, zend_is_callable, zend_object, zend_resource, zend_value,
11-
zval, IS_INTERNED_STRING_EX, IS_STRING_EX,
11+
zval,
1212
},
1313
errors::{Error, Result},
1414
};
1515

1616
use crate::php::{
1717
enums::DataType,
18+
flags::ZvalTypeFlags,
1819
types::{long::ZendLong, string::ZendString},
1920
};
2021

@@ -258,7 +259,7 @@ impl<'a> Zval {
258259
{
259260
let zend_str = ZendString::new(val, false);
260261
self.value.str_ = zend_str;
261-
self.u1.type_info = IS_STRING_EX;
262+
self.u1.type_info = ZvalTypeFlags::StringEx.bits();
262263
}
263264

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

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

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

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

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

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

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

@@ -360,7 +361,7 @@ impl<'a> Zval {
360361
where
361362
V: Into<ZendHashTable>,
362363
{
363-
self.u1.type_info = DataType::Array as u32;
364+
self.u1.type_info = ZvalTypeFlags::ArrayEx.bits();
364365
self.value.arr = val.into().into_ptr();
365366
}
366367
}

0 commit comments

Comments
 (0)