Viewing File: /usr/local/cpanel/3rdparty/perl/536/cpanel-lib/x86_64-linux/B/C/Optimizer/DowngradePVXV.pm

package B::C::Optimizer::DowngradePVXV;

use B::C::Std;

use B::C::Decimal qw/get_integer_value intmax/;
use B             qw{SVf_NOK SVp_NOK SVs_OBJECT SVf_IOK SVf_ROK SVf_POK SVp_POK SVp_IOK SVf_IsCOW SVf_READONLY SVs_PADSTALE SVs_PADTMP SVf_PROTECT};

use Exporter ();
our @ISA = qw(Exporter);

our @EXPORT_OK = qw/downgrade_pviv downgrade_pvnv/;

# we need to keep them in memory to do not reuse the same memory location
my @EXTRA;

sub SVt_IV   { 1 }
sub SVt_NV   { 2 }
sub SVt_PV   { 3 }
sub SVt_PVIV { 5 }
sub SVt_PVNV { 6 }
sub SVt_MASK { 0xf }    # smallest bitmask that covers all types

my $DEBUG = 0;

my $REGEXP_INTEGER = qr{^(?:[1-9][0-9]*|0)\z};

sub ddebug (@what) {
    return unless $DEBUG;

    local %ENV;    # avoid error with taint from op/taint.t
    my $msg = join ' ', map { defined $_ ? $_ : 'undef' } @what;

    qx{/usr/bin/echo '$msg' >> /tmp/downgrade};
    return 1;
}

sub is_simple_pviv ($sv) {

    my $flags = $sv->FLAGS;

    return if ( $flags & SVf_ROK ) == SVf_ROK;
    return if ( $flags & SVt_MASK ) != SVt_PVIV();

    # downgrade to IV if private_POK is set without having the public POK set
    return 1 if ( $flags & SVp_POK && !( $flags & SVf_POK ) );

    # remove insignificant flags for us as a PVIV
    $flags &= ~SVf_IsCOW if $flags & SVp_POK;
    $flags &= ~SVf_IOK;
    $flags &= ~SVf_POK;
    $flags &= ~SVp_IOK;
    $flags &= ~SVp_POK;
    $flags &= ~SVf_READONLY;
    $flags &= ~SVf_PROTECT;

    # remove the type
    $flags &= ~SVt_MASK();

    ddebug( "PVIV with flags", $flags ) if $flags;

    return $flags == 0;
}

sub is_simple_pvnv ($sv) {    # should factorize this with the other is_simple funcion, once ready

    my $flags = $sv->FLAGS;

    return if ( $flags & SVf_ROK ) == SVf_ROK;
    return if ( $flags & SVt_MASK ) != SVt_PVNV();

    return if $sv->IsBool;

    # remove insignificant flags for us as a PVIV
    $flags &= ~SVf_IsCOW if $flags & SVp_POK;
    $flags &= ~SVf_IOK;
    $flags &= ~SVf_POK;
    $flags &= ~SVf_NOK;
    $flags &= ~SVp_IOK;
    $flags &= ~SVp_POK;
    $flags &= ~SVp_NOK;

    # bonus ?
    $flags &= ~SVf_READONLY;
    $flags &= ~SVs_PADSTALE;
    $flags &= ~SVs_PADTMP;
    $flags &= ~SVf_PROTECT;

    # remove the type
    $flags &= ~SVt_MASK();

    ddebug( "PVNV with flags", $flags ) if $flags;

    return $flags == 0;
}

sub custom_flags ( $sv, $type = 0 ) {

    $type ||= 0;

    # remove the current type
    my $flags = $sv->FLAGS & ~SVt_MASK();

    # use the new type
    $flags |= $type;

    if ( $type == SVt_IV() ) {
        $flags |= ( SVf_IOK | SVp_IOK );

        $flags &= ~SVf_NOK;
        $flags &= ~SVp_NOK;
        $flags &= ~SVf_POK;
        $flags &= ~SVp_POK;

    }
    elsif ( $type == SVt_NV() ) {
        $flags |= ( SVf_NOK | SVp_NOK );

        $flags &= ~SVf_IOK;
        $flags &= ~SVp_IOK;
        $flags &= ~SVf_POK;
        $flags &= ~SVp_POK;

    }
    elsif ( $type == SVt_PV() ) {
        $flags |= ( SVf_POK | SVp_POK | SVf_IsCOW );

        $flags &= ~SVf_IOK;
        $flags &= ~SVp_IOK;
        $flags &= ~SVf_NOK;
        $flags &= ~SVp_NOK;
    }

    return $flags;
}

sub downgrade_pviv ( $sv, $fullname ) {

    return unless is_simple_pviv($sv);

    my $iok  = $sv->FLAGS & SVf_IOK;
    my $pok  = $sv->FLAGS & SVf_POK;
    my $ppok = $sv->FLAGS & SVp_POK;

    return if $iok && $pok && $sv->PV ne $sv->IVX;

    if ( $ppok && !$pok ) {
        my $can_downgrade_to_iv = $sv->can_downgrade_to_iv;
        ddebug( "- PVIV downgrade skipped - can downgrade %d", _sv_to_str($sv), $can_downgrade_to_iv );
        return unless $can_downgrade_to_iv;
    }

    #tidyoff
    if (  !$pok && $iok
        or $iok && $sv->PV =~ $REGEXP_INTEGER ) {    # PVIV used as IV let's downgrade it as an IV
        ddebug("downgrade PVIV to IV - case a");

        push @EXTRA, int get_integer_value( $sv->IVX );
        my $sviv = B::svref_2object( \$EXTRA[-1] );
        return B::IV::save( $sviv, $fullname, { flags => custom_flags( $sv, SVt_IV() ), refcnt => $sv->REFCNT } );

        #return B::IV::save( $sviv, $fullname );
    }
    elsif ( $pok && $sv->PV =~ $REGEXP_INTEGER && length( $sv->PV ) <= 18 ) {    # use Config{...}
        ddebug("downgrade PVIV to IV - case b");

        # downgrade a PV that looks like an IV (and not too long) to a simple IV
        push @EXTRA, int( "" . $sv->PV );
        my $sviv = B::svref_2object( \$EXTRA[-1] );
        return B::IV::save( $sviv, $fullname, { flags => custom_flags( $sv, SVt_IV() ), refcnt => $sv->REFCNT } );
    }
    elsif ($pok) {                                                               # maybe do not downgrade it to PV if the string is only 0-9 ??
        ddebug("downgrade the PVIV as a regular PV");
        push @EXTRA, "" . $sv->PV;
        my $svpv = B::svref_2object( \$EXTRA[-1] );
        return B::PV::save( $svpv, $fullname, { flags => custom_flags( $sv, SVt_PV() ), refcnt => $sv->REFCNT } );
    }
    else {
        ddebug( sprintf( "downgrade PVIV skipped ? %s", _sv_to_str($sv) ) );
    }

    #tidyon

    return;
}

sub downgrade_pvnv ( $sv, $fullname ) {

    return unless is_simple_pvnv($sv);

    my $iok = $sv->FLAGS & SVf_IOK;
    my $nok = $sv->FLAGS & SVf_NOK;
    my $pok = $sv->FLAGS & SVf_POK;

    my $ppok = $sv->FLAGS & SVp_POK;

    return if $iok && $pok && $sv->PV ne $sv->IVX;
    return if $nok && $pok && $sv->PV ne $sv->NV;

    # do not mess with large numbers
    if ( $ppok && $nok && ( $sv->NV > intmax() or $sv->NV < -intmax() ) ) {

        #ddebug("- XXX PVNV downgrade skipped ", _sv_to_str($sv), intmax() );
        return;
    }

    # if the PV is private abort.. in some cases
    if ( $ppok && !$pok or $ppok && ( $sv->FLAGS & SVf_IsCOW ) ) {

        #ddebug("- PVNV downgrade skipped ", _sv_to_str($sv));
        return;
    }

    return unless $iok or $nok or $pok;    # SVs_PADSTALE ?

    #tidyoff
    if (
        $nok && $sv->NV =~ $REGEXP_INTEGER && length( $sv->NV ) <= 18    # !$pok && !$iok &&
      ) {                                                                # PVNV used as IV let's downgrade it as an IV
                                                                         #return;
        ddebug( "downgrade PVNV to IV from NV - case a", _sv_to_str($sv) );

        #eval q{use Devel::Peek}; Dump($sv);
        return if $sv->NV == 0;
        push @EXTRA, int $sv->NV;
        my $sviv = B::svref_2object( \$EXTRA[-1] );
        do { ddebug("WARN: invalid B::IV when downgrading PVNV"); return } unless ref $sviv eq 'B::IV';
        return B::IV::save( $sviv, $fullname, { flags => custom_flags( $sv, SVt_IV() ), refcnt => $sv->REFCNT } );
    }
    elsif ( $pok && $sv->PV =~ $REGEXP_INTEGER && length( $sv->PV ) <= 18 ) {
        ddebug("downgrade PVNV to IV - case b");
        push @EXTRA, int( "" . $sv->PV );
        my $sviv = B::svref_2object( \$EXTRA[-1] );
        do { ddebug("WARN: invalid B::IV when downgrading PVNV"); return } unless ref $sviv eq 'B::IV';
        return B::IV::save( $sviv, $fullname, { flags => custom_flags( $sv, SVt_IV() ), refcnt => $sv->REFCNT } );
    }
    elsif ($iok) {    # && $sv->IVX =~ $REGEXP_INTEGER
        ddebug("downgrade PVNV to IV - case d");
        push @EXTRA, int( "" . $sv->IV );
        my $sviv = B::svref_2object( \$EXTRA[-1] );
        return B::IV::save( $sviv, $fullname, { flags => custom_flags( $sv, SVt_IV() ), refcnt => $sv->REFCNT } );
    }
    elsif ($nok) {

        # need to be sure that the PV is set: checking its length
        if ( length( $sv->PV ) && ( $sv->NV // '' ) ne ( $sv->PV // '' ) ) {

            # for example, we do not want to convert to NV $] = PV 5.025010 , NV 5.02501 or we would lost the 0 padding
            #   ( also true for any other similar variable 'our $X; BEGIN { $X = $] };' )
            ddebug( "Cannot downgrade PVNV to NV - case e: NV and PV differ", 'NV:', $sv->NV, 'PV: ', $sv->PV );
            return;
        }

        ddebug( "downgrade PVNV to NV - case e", _sv_to_str($sv) );

        push @EXTRA, $sv->NV;
        my $svnv = B::svref_2object( \$EXTRA[-1] );

        #debug( "Value ?? %s", )
        do { ddebug("WARN: invalid B::NV when downgrading PVNV"); return } unless ref $svnv eq 'B::NV';
        return B::NV::save( $svnv, $fullname, { flags => custom_flags( $sv, SVt_NV() ), refcnt => $sv->REFCNT } );
    }
    else {
        ddebug( sprintf( "downgrade PVNV skipped ? %s", _sv_to_str($sv) ) );
    }

    # elsif ($pok) {                                                            # maybe do not downgrade it to PV if the string is only 0-9 ??
    #                                                                           # downgrade the PVIV as a regular PV
    #     ddebug("downgrade PVNV to IV - case c");
    #     push @EXTRA, "" . $sv->PV;
    #     my $svpv = B::svref_2object( \$EXTRA[-1] );
    #     return B::PV::save( $svpv, $fullname );
    # }

    #tidyon

    return;
}

# debug helper
sub _sv_to_str ($sv) {

    my ( $flags, $values ) = ( '', '' );

    my $iok  = $sv->FLAGS & SVf_IOK;
    my $nok  = $sv->FLAGS & SVf_NOK;
    my $pok  = $sv->FLAGS & SVf_POK;
    my $ppok = $sv->FLAGS & SVp_POK;

    if ($iok) {
        $flags  .= 'IOK ';
        $values .= 'IV: ' . $sv->IVX . ' ';
    }
    if ($nok) {
        $flags  .= 'NOK ';
        $values .= 'NV: ' . $sv->NV . ' ';
    }
    if ($pok) {
        $flags  .= 'POK ';
        $values .= 'PV: ' . $sv->PV . ' ';
    }
    $flags .= 'pPOK ' if $ppok;

    return sprintf( "SV is %s ; %s ; Flags 0x%x ; SvCUR %d", $flags, $values, $sv->FLAGS, $sv->CUR );
}

1;
Back to Directory File Manager