# Stackobj.pm # # Copyright (c) 1996 Malcolm Beattie # # You may distribute under the terms of either the GNU General Public # License or the Artistic License, as specified in the README file. # package B::Stackobj; our $VERSION = '1.00'; use Exporter (); @ISA = qw(Exporter); @EXPORT_OK = qw(set_callback T_UNKNOWN T_DOUBLE T_INT VALID_UNSIGNED VALID_INT VALID_DOUBLE VALID_SV REGISTER TEMPORARY); %EXPORT_TAGS = (types => [qw(T_UNKNOWN T_DOUBLE T_INT)], flags => [qw(VALID_INT VALID_DOUBLE VALID_SV VALID_UNSIGNED REGISTER TEMPORARY)]); use Carp qw(confess); use strict; use B qw(class SVf_IOK SVf_NOK SVf_IVisUV); # Types sub T_UNKNOWN () { 0 } sub T_DOUBLE () { 1 } sub T_INT () { 2 } sub T_SPECIAL () { 3 } # Flags sub VALID_INT () { 0x01 } sub VALID_UNSIGNED () { 0x02 } sub VALID_DOUBLE () { 0x04 } sub VALID_SV () { 0x08 } sub REGISTER () { 0x10 } # no implicit write-back when calling subs sub TEMPORARY () { 0x20 } # no implicit write-back needed at all sub SAVE_INT () { 0x40 } #if int part needs to be saved at all sub SAVE_DOUBLE () { 0x80 } #if double part needs to be saved at all # # Callback for runtime code generation # my $runtime_callback = sub { confess "set_callback not yet called" }; sub set_callback (&) { $runtime_callback = shift } sub runtime { &$runtime_callback(@_) } # # Methods # sub write_back { confess "stack object does not implement write_back" } sub invalidate { shift->{flags} &= ~(VALID_INT |VALID_UNSIGNED | VALID_DOUBLE) } sub as_sv { my $obj = shift; if (!($obj->{flags} & VALID_SV)) { $obj->write_back; $obj->{flags} |= VALID_SV; } return $obj->{sv}; } sub as_int { my $obj = shift; if (!($obj->{flags} & VALID_INT)) { $obj->load_int; $obj->{flags} |= VALID_INT|SAVE_INT; } return $obj->{iv}; } sub as_double { my $obj = shift; if (!($obj->{flags} & VALID_DOUBLE)) { $obj->load_double; $obj->{flags} |= VALID_DOUBLE|SAVE_DOUBLE; } return $obj->{nv}; } sub as_numeric { my $obj = shift; return $obj->{type} == T_INT ? $obj->as_int : $obj->as_double; } sub as_bool { my $obj=shift; if ($obj->{flags} & VALID_INT ){ return $obj->{iv}; } if ($obj->{flags} & VALID_DOUBLE ){ return $obj->{nv}; } return sprintf("(SvTRUE(%s))", $obj->as_sv) ; } # # Debugging methods # sub peek { my $obj = shift; my $type = $obj->{type}; my $flags = $obj->{flags}; my @flags; if ($type == T_UNKNOWN) { $type = "T_UNKNOWN"; } elsif ($type == T_INT) { $type = "T_INT"; } elsif ($type == T_DOUBLE) { $type = "T_DOUBLE"; } else { $type = "(illegal type $type)"; } push(@flags, "VALID_INT") if $flags & VALID_INT; push(@flags, "VALID_DOUBLE") if $flags & VALID_DOUBLE; push(@flags, "VALID_SV") if $flags & VALID_SV; push(@flags, "REGISTER") if $flags & REGISTER; push(@flags, "TEMPORARY") if $flags & TEMPORARY; @flags = ("none") unless @flags; return sprintf("%s type=$type flags=%s sv=$obj->{sv}", class($obj), join("|", @flags)); } sub minipeek { my $obj = shift; my $type = $obj->{type}; my $flags = $obj->{flags}; if ($type == T_INT || $flags & VALID_INT) { return $obj->{iv}; } elsif ($type == T_DOUBLE || $flags & VALID_DOUBLE) { return $obj->{nv}; } else { return $obj->{sv}; } } # # Caller needs to ensure that set_int, set_double, # set_numeric and set_sv are only invoked on legal lvalues. # sub set_int { my ($obj, $expr,$unsigned) = @_; runtime("$obj->{iv} = $expr;"); $obj->{flags} &= ~(VALID_SV | VALID_DOUBLE); $obj->{flags} |= VALID_INT|SAVE_INT; $obj->{flags} |= VALID_UNSIGNED if $unsigned; } sub set_double { my ($obj, $expr) = @_; runtime("$obj->{nv} = $expr;"); $obj->{flags} &= ~(VALID_SV | VALID_INT); $obj->{flags} |= VALID_DOUBLE|SAVE_DOUBLE; } sub set_numeric { my ($obj, $expr) = @_; if ($obj->{type} == T_INT) { $obj->set_int($expr); } else { $obj->set_double($expr); } } sub set_sv { my ($obj, $expr) = @_; runtime("SvSetSV($obj->{sv}, $expr);"); $obj->invalidate; $obj->{flags} |= VALID_SV; } # # Stackobj::Padsv # @B::Stackobj::Padsv::ISA = 'B::Stackobj'; sub B::Stackobj::Padsv::new { my ($class, $type, $extra_flags, $ix, $iname, $dname) = @_; $extra_flags |= SAVE_INT if $extra_flags & VALID_INT; $extra_flags |= SAVE_DOUBLE if $extra_flags & VALID_DOUBLE; bless { type => $type, flags => VALID_SV | $extra_flags, sv => "PL_curpad[$ix]", iv => "$iname", nv => "$dname" }, $class; } sub B::Stackobj::Padsv::load_int { my $obj = shift; if ($obj->{flags} & VALID_DOUBLE) { runtime("$obj->{iv} = $obj->{nv};"); } else { runtime("$obj->{iv} = SvIV($obj->{sv});"); } $obj->{flags} |= VALID_INT|SAVE_INT; } sub B::Stackobj::Padsv::load_double { my $obj = shift; $obj->write_back; runtime("$obj->{nv} = SvNV($obj->{sv});"); $obj->{flags} |= VALID_DOUBLE|SAVE_DOUBLE; } sub B::Stackobj::Padsv::save_int { my $obj = shift; return $obj->{flags} & SAVE_INT; } sub B::Stackobj::Padsv::save_double { my $obj = shift; return $obj->{flags} & SAVE_DOUBLE; } sub B::Stackobj::Padsv::write_back { my $obj = shift; my $flags = $obj->{flags}; return if $flags & VALID_SV; if ($flags & VALID_INT) { if ($flags & VALID_UNSIGNED ){ runtime("sv_setuv($obj->{sv}, $obj->{iv});"); }else{ runtime("sv_setiv($obj->{sv}, $obj->{iv});"); } } elsif ($flags & VALID_DOUBLE) { runtime("sv_setnv($obj->{sv}, $obj->{nv});"); } else { confess "write_back failed for lexical @{[$obj->peek]}\n"; } $obj->{flags} |= VALID_SV; } # # Stackobj::Const # @B::Stackobj::Const::ISA = 'B::Stackobj'; sub B::Stackobj::Const::new { my ($class, $sv) = @_; my $obj = bless { flags => 0, sv => $sv # holds the SV object until write_back happens }, $class; if ( ref($sv) eq "B::SPECIAL" ){ $obj->{type}= T_SPECIAL; }else{ my $svflags = $sv->FLAGS; if ($svflags & SVf_IOK) { $obj->{flags} = VALID_INT|VALID_DOUBLE; $obj->{type} = T_INT; if ($svflags & SVf_IVisUV){ $obj->{flags} |= VALID_UNSIGNED; $obj->{nv} = $obj->{iv} = $sv->UVX; }else{ $obj->{nv} = $obj->{iv} = $sv->IV; } } elsif ($svflags & SVf_NOK) { $obj->{flags} = VALID_INT|VALID_DOUBLE; $obj->{type} = T_DOUBLE; $obj->{iv} = $obj->{nv} = $sv->NV; } else { $obj->{type} = T_UNKNOWN; } } return $obj; } sub B::Stackobj::Const::write_back { my $obj = shift; return if $obj->{flags} & VALID_SV; # Save the SV object and replace $obj->{sv} by its C source code name $obj->{sv} = $obj->{sv}->save; $obj->{flags} |= VALID_SV|VALID_INT|VALID_DOUBLE; } sub B::Stackobj::Const::load_int { my $obj = shift; if (ref($obj->{sv}) eq "B::RV"){ $obj->{iv} = int($obj->{sv}->RV->PV); }else{ $obj->{iv} = int($obj->{sv}->PV); } $obj->{flags} |= VALID_INT; } sub B::Stackobj::Const::load_double { my $obj = shift; if (ref($obj->{sv}) eq "B::RV"){ $obj->{nv} = $obj->{sv}->RV->PV + 0.0; }else{ $obj->{nv} = $obj->{sv}->PV + 0.0; } $obj->{flags} |= VALID_DOUBLE; } sub B::Stackobj::Const::invalidate {} # # Stackobj::Bool # @B::Stackobj::Bool::ISA = 'B::Stackobj'; sub B::Stackobj::Bool::new { my ($class, $preg) = @_; my $obj = bless { type => T_INT, flags => VALID_INT|VALID_DOUBLE, iv => $$preg, nv => $$preg, preg => $preg # this holds our ref to the pseudo-reg }, $class; return $obj; } sub B::Stackobj::Bool::write_back { my $obj = shift; return if $obj->{flags} & VALID_SV; $obj->{sv} = "($obj->{iv} ? &PL_sv_yes : &PL_sv_no)"; $obj->{flags} |= VALID_SV; } # XXX Might want to handle as_double/set_double/load_double? sub B::Stackobj::Bool::invalidate {} 1; __END__ =head1 NAME B::Stackobj - Helper module for CC backend =head1 SYNOPSIS use B::Stackobj; =head1 DESCRIPTION See F. =head1 AUTHOR Malcolm Beattie, C =cut