Coder Social home page Coder Social logo

constructing exceptions in rust about ext-php-rs HOT 11 OPEN

azjezz avatar azjezz commented on September 26, 2024
constructing exceptions in rust

from ext-php-rs.

Comments (11)

ju1ius avatar ju1ius commented on September 26, 2024 1

There's a discord channel that is more appropriate for these kind of lengthy discussions: https://discord.com/channels/115233111977099271/829877152048349266

from ext-php-rs.

ju1ius avatar ju1ius commented on September 26, 2024

Yeah, inheritance doesn't work ATM...

from ext-php-rs.

azjezz avatar azjezz commented on September 26, 2024

i don't think this is related to inheritance, the construct function above will call parent constructor if it's called from the ce constructor ( assuming the ce refers to a class that is in PHP, no ext-php-rs as it doesn't support calling parent constructors ).

however, exceptions are different from normal classes, they need more context ( file, line, trace ), as i said, throwing and catching works, zend will make sure to construct the exception correctly, but it is slow, we need to look at how zend constructs an exception, and basically do the same, so that PhpException wont' be just holding a reference to ce and message in the future, but rather a zend object, if you call new(msg, 0, ce) it will construct that class immediately itself without throwing it, and only throws it when .throw() is called.

this will allow us to be able to construct ext-php-rs exceptions without throwing them.

this is something that a lot of PHP libraries do, to give you an example:

https://github.com/azjezz/psl/blob/b239bb7f8b8feb52908ad6b82ab153b6ff8b6111/src/Psl/Channel/Exception/ClosedChannelException.php#L7-L34

while throwing immediately from forSending() works, and can be implemented in ext-php-rs, it is not the desired behavior, because in PSL those exceptions don't get thrown when constructed, they get sent over the event loop via a suspension (
https://github.com/azjezz/psl/blob/b239bb7f8b8feb52908ad6b82ab153b6ff8b6111/src/Psl/Channel/Internal/BoundedChannelState.php#L84-L99 ), which is later suspended and throws ( https://github.com/azjezz/psl/blob/2.2.x/src/Psl/Channel/Internal/BoundedSender.php#L42-L50 )

from ext-php-rs.

azjezz avatar azjezz commented on September 26, 2024

correction: the construct function above will probably fail if Foo class doesn't define a construct but it's parent Bar has one, we can do something like this to retrieve the ctor ( rusty-pseudo code ):

fn get_constructor() -> option {
  constructor = none;
  if ce.constructor == null {
    traits = ce.traits;
    for trait in traits {
      if trait.has_constructor() {
        constructor = trait.get_constructor();
        break;
      }
    }
  
    if constructor.is_none() {
       if ce.parent != null {
         // we do the same for the parent
         return get_constructor(ce.parent);
       }
    }
  } else {
    constructor = some(ce.constructor);
  }

  constructor 
}

see: https://3v4l.org/ga9Oq

from ext-php-rs.

ju1ius avatar ju1ius commented on September 26, 2024

none of the exception properties weren't initialized, the exception will always not have a backtrace

I think the reason why there's no backtrace is that you might be trying to construct an exception outside of a VM stack frame (when EG(current_execute_data) == null).
If that's the case, then this is expected since no VM stack frame => no trace.
The function that creates your exception instance needs to be called from PHP (at least that's my understanding).

from ext-php-rs.

azjezz avatar azjezz commented on September 26, 2024

no, that's not the issue, if there's no stack frame, we would get an error.

see: https://heap.space/xref/php-src/Zend/zend_exceptions.c?r=45e224cf#205

we are missing something else that i still can't figure out :)

from ext-php-rs.

ju1ius avatar ju1ius commented on September 26, 2024

You would have an error if the exception is thrown without a stack frame, not when constructing the exception object, see https://heap.space/xref/php-src/Zend/zend_exceptions.c?r=45e224cf#244.

from ext-php-rs.

azjezz avatar azjezz commented on September 26, 2024

throwing it doesn't result in an error, if you try the code above an call do throw from_bar() in PHP, it would throw the exception, but with no trace.

from ext-php-rs.

ju1ius avatar ju1ius commented on September 26, 2024

throwing it doesn't result in an error, if you try the code above an call do throw from_bar() in PHP, it would throw the exception, but with no trace.

Mmm okay... have you tried to assert that ExecutorGlobals::get().current_execute_data is not a null pointer at the time you create the exception instance?

from ext-php-rs.

azjezz avatar azjezz commented on September 26, 2024

no, but i don't think that would change anything, as in my test script, the file looks exactly like this:

<?php

function foo() {
    return Psl\Channel\Exception\ClosedChannelException::forSending();
}

function bar() {
    throw foo();
}

function baz() {
    bar();
}

baz();

meaning it is not not possible for another exception to be present.

from ext-php-rs.

azjezz avatar azjezz commented on September 26, 2024

for anyone reading this thread, until this issue is resolved, here's how you can create your own exceptions:

use ext_php_rs::convert::IntoZvalDyn;
use ext_php_rs::error::Result;
use ext_php_rs::ffi;
use ext_php_rs::types::Zval;
use ext_php_rs::zend::ClassEntry;

pub unsafe fn make_exception(
    class: Option<&'static ClassEntry>,
    arguments: Vec<&dyn IntoZvalDyn>,
) -> Result<Zval> {
    let class = class.unwrap();
    let len = arguments.len();
    let arguments = arguments
        .into_iter()
        .map(|val| val.as_zval(false))
        .collect::<Result<Vec<_>>>()?
        .into_boxed_slice();

    let class_ptr = class as *const _ as *mut _;
    let constructor_ptr = class.constructor;
    let object = class.__bindgen_anon_2.create_object.unwrap()(class_ptr);

    ffi::zend_call_known_function(
        constructor_ptr,
        object,
        class_ptr,
        std::ptr::null_mut(),
        len as _,
        arguments.as_ptr() as _,
        std::ptr::null_mut(),
    );

    let object = object
        .as_mut()
        .expect("error: failed to allocate memory for object");

    let mut result = Zval::new();
    result.set_object(object);

    Ok(result)
}

Note: this is not 100% guaranteed to work, if you found an issues with it, let us know :)

let exception = unsafe { make_exception(MY_EXCEPTION_CE, vec![&message]).unwrap() };

from ext-php-rs.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.