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

FFI::CType reflection API #7217

Closed
wants to merge 5 commits into from
Closed

Conversation

dstogov
Copy link
Member

@dstogov dstogov commented Jul 6, 2021

No description provided.

Copy link
Member

@nikic nikic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks reasonable to me, don't have anything to add in terms of implementation.

Just some bikeshedding on method names.

ext/ffi/ffi.stub.php Outdated Show resolved Hide resolved
ext/ffi/ffi.stub.php Outdated Show resolved Hide resolved
ext/ffi/ffi.stub.php Show resolved Hide resolved
ext/ffi/ffi.stub.php Outdated Show resolved Hide resolved
ext/ffi/ffi.stub.php Outdated Show resolved Hide resolved
except for "getPointerType" -> "getPointerElementType"
@dstogov
Copy link
Member Author

dstogov commented Jul 6, 2021

Just some bikeshedding on method names.

I changes names according to your suggestion, except for getPointerType. (getPointerElementType looks wrong to me)

Copy link
Contributor

@lisachenko lisachenko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello, @dstogov! Thank you for this improvement! Looks awesome!

I have only small suggestions regarding better method names, which will be used better.

Also, want to avoid some specific methods for CType that can be implemented better as children classes, eg CArrayType, CStructureType. But this can require some additional changes... @dstogov @nikic what do you think?

ext/ffi/ffi.c Show resolved Hide resolved
}
/* }}} */

ZEND_METHOD(FFI_CType, getArrayElementType) /* {{{ */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about extraction of separate CArrayType for this? It can make API stable and more logical, as there is no point in calling getArrayElementType for simple scalar types - it will always throw an error, which can be example of LSP principle - we will have methods that won't work.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, it's possible to introduce and instantiate CArrayType instead of CType, but I don't like to do this in last minute and without RFC.


type = ZEND_FFI_TYPE(ctype->type);
if (type->kind != ZEND_FFI_TYPE_ARRAY) {
zend_throw_error(zend_ffi_exception_ce, "FFI\\CType is not an array");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This runtime check can be avoid by moving implementation into the separate CArrayType

}
/* }}} */

ZEND_METHOD(FFI_CType, getPointerType) /* {{{ */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, we can have separate CPointerType, which will give us better visibility in PHP code.

ext/ffi/ffi.c Show resolved Hide resolved
}
/* }}} */

ZEND_METHOD(FFI_CType, getFuncArgType) /* {{{ */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getFunctionArgumentType()?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in my opinion, this longer name doesn't make things more clear.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PHP is already already using "Parameter" instead of "Argument" for reflection on types elsewhere.

Also, would omitting Func for something like getParameterType make more sense - nothing other than functions would have parameters.

zend_ffi_ctype *ret;

ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_LONG(n)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

argumentIndex?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes sense, but not in this place but in stub, and probably $arg_index.

}

if (!type->func.args) {
zend_throw_error(zend_ffi_exception_ce, "Wrong argument number");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe state explicitly that "Function $name has not arguments".

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May be this makes sense, however, we don't know the function name at this point.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, then current one should be ok

Copy link
Contributor

@TysonAndre TysonAndre Jul 7, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
zend_throw_error(zend_ffi_exception_ce, "Wrong argument number");
zend_throw_error(zend_ffi_exception_ce, "Missing parameter offset #d of FFI function with %d parameter(s)", ...);

Maybe something more along these lines with the argument count and offset being fetched, to speed up debugging? (my wording can be improved)

ext/ffi/ffi.c Show resolved Hide resolved
public function getFuncABI(): int {}
public function getFuncReturnType(): CType {}
public function getFuncArgCount(): int {}
public function getFuncArgType(int $n): CType {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public function getFuncArgType(int $n): CType {}
public function getFunctionArgumentType(int $argumentIndex): CType {}

@lisachenko
Copy link
Contributor

lisachenko commented Jul 6, 2021

As I can see method names have been partially updated already.

@nikic
Copy link
Member

nikic commented Jul 6, 2021

Also, want to avoid some specific methods for CType that can be implemented better as children classes, eg CArrayType, CStructureType. But this can require some additional changes... @dstogov @nikic what do you think?

I agree that using subclasses would be cleaner in principle, but I'm also okay with the current approach.

@nikic
Copy link
Member

nikic commented Jul 6, 2021

Just some bikeshedding on method names.

I changes names according to your suggestion, except for getPointerType. (getPointerElementType looks wrong to me)

Ah, I probably picked up an LLVM-ism, where this is called "pointer element type" (or sometimes "pointee type"). I think just keeping getPointerType() is fine.

Copy link
Contributor

@TysonAndre TysonAndre left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM despite nits on naming - I think it'd be easier to remember this if names were consistent with Reflection APIs already in PHP such as https://www.php.net/manual/en/class.reflectionfunctionabstract.php

}
/* }}} */

ZEND_METHOD(FFI_CType, getArrayLength) /* {{{ */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would implementing Countable and calling this Countable::count() make sense here (continuing to throw for non-arrays)? for ($i = 0; $i < count($ffiArray); $i++) {...}

Then again, that'd be a much larger change and that interface isn't in FFI\CData for 8.0

}
/* }}} */

ZEND_METHOD(FFI_CType, getFuncReturnType) /* {{{ */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to use the same names that reflection already uses for these methods? https://www.php.net/manual/en/reflectionfunctionabstract.getreturntype.php E.g. getReturnType

}
/* }}} */

ZEND_METHOD(FFI_CType, getFuncArgCount) /* {{{ */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}
/* }}} */

ZEND_METHOD(FFI_CType, getFuncArgType) /* {{{ */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PHP is already already using "Parameter" instead of "Argument" for reflection on types elsewhere.

Also, would omitting Func for something like getParameterType make more sense - nothing other than functions would have parameters.

Comment on lines +4535 to +4537
if (zend_parse_parameters_none() == FAILURE) {
RETURN_THROWS();
}
Copy link
Contributor

@TysonAndre TysonAndre Jul 7, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (zend_parse_parameters_none() == FAILURE) {
RETURN_THROWS();
}
ZEND_PARSE_PARAMETERS_NONE();

nit: Could be shortened here and elsewhere. Is there a reason for different parts of the codebase using different ways of writing this?

}

if (!type->func.args) {
zend_throw_error(zend_ffi_exception_ce, "Wrong argument number");
Copy link
Contributor

@TysonAndre TysonAndre Jul 7, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
zend_throw_error(zend_ffi_exception_ce, "Wrong argument number");
zend_throw_error(zend_ffi_exception_ce, "Missing parameter offset #d of FFI function with %d parameter(s)", ...);

Maybe something more along these lines with the argument count and offset being fetched, to speed up debugging? (my wording can be improved)

@TysonAndre
Copy link
Contributor

TysonAndre commented Jul 7, 2021

Elsewhere in php,

Parameter is used to refer to the function declarations (AST_PARAM, ReflectionFunctionAbstract->getParameters(), etc.)
Argument is used to refer to expressions passed to the functions by the caller (ArgumentCountError, etc.)

Other languages use similar definitions, e.g. https://developer.mozilla.org/en-US/docs/Glossary/Parameter

@dstogov
Copy link
Member Author

dstogov commented Jul 13, 2021

Merged via a2845e3

@SerafimArts
Copy link
Contributor

SerafimArts commented Jul 22, 2021

I understand that it is already merged, but won't $type->getKind() return different values every time in case of different settings of the HAVE_LONG_DOUBLE definition: https://github.com/php/php-src/blob/master/ext/ffi/ffi.c#L69?

$type = \FFI::type('uint8_t');

// #define HAVE_LONG_DOUBLE 1
var_dump($type->getKind()); // 4

// #define HAVE_LONG_DOUBLE 0
var_dump($type->getKind()); // 3

That is, we cannot be guided by these values with complete confidence. Maybe it makes sense to add a set of constants like?

namespace FFI;

class CType 
{
    public const TYPE_KIND_VOID = // ZEND_FFI_TYPE_VOID;
    public const TYPE_KIND_FLOAT = // ZEND_FFI_TYPE_FLOAT;
    // etc...
}

The same goes for other enams defined in ffi.c

@nikic
Copy link
Member

nikic commented Jul 22, 2021

@SerafimArts These constants are already present:

REGISTER_FFI_TYPE_CONSTANT(TYPE_VOID);

@SerafimArts
Copy link
Contributor

SerafimArts commented Jul 22, 2021

@nikic Oh, then I'm sorry, I didn't notice

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants