diff --git a/perlmod/src/ffi.rs b/perlmod/src/ffi.rs index b37c661..1eeb29c 100644 --- a/perlmod/src/ffi.rs +++ b/perlmod/src/ffi.rs @@ -68,6 +68,7 @@ extern "C" { pub fn RSPL_is_hash(sv: *mut SV) -> bool; pub fn RSPL_type_flags(sv: *mut SV) -> u32; pub fn RSPL_svtype(sv: *mut SV) -> u32; + pub fn RSPL_SvOK(sv: *mut SV) -> bool; pub fn RSPL_SvTRUE(sv: *mut SV) -> bool; pub fn RSPL_newAV() -> *mut AV; @@ -110,7 +111,11 @@ extern "C" { pub fn RSPL_PVLV() -> u32; pub fn RSPL_LvTARG(sv: *mut SV) -> *mut SV; + //pub fn RSPL_LvTYPE(sv: *mut SV) -> u8; pub fn RSPL_vivify_defelem(sv: *mut SV); + + pub fn RSPL_SvFLAGS(sv: *mut SV) -> u32; + pub fn RSPL_SvGETMAGIC(sv: *mut SV) -> bool; } /// Argument marker for the stack. diff --git a/perlmod/src/glue.c b/perlmod/src/glue.c index 63548ca..01267d7 100644 --- a/perlmod/src/glue.c +++ b/perlmod/src/glue.c @@ -139,6 +139,10 @@ extern bool RSPL_is_scalar(SV *sv) { return SvTYPE(sv) < SVt_PVAV; } +extern bool RSPL_SvOK(SV *sv) { + return SvOK(sv); +} + extern bool RSPL_SvTRUE(SV *sv) { return SvTRUE(sv); } @@ -310,8 +314,25 @@ extern SV* RSPL_LvTARG(SV *sv) { return LvTARG(sv); } -extern void RSPL_vivify_defelem(SV *sv) { - Perl_vivify_defelem(aTHX_ sv); +// We prefer this unsigned. +//extern unsigned char RSPL_LvTYPE(SV *sv) { +// return (unsigned char)LvTYPE(sv); +//} + +//extern void RSPL_vivify_defelem(SV *sv) { +// Perl_vivify_defelem(aTHX_ sv); +//} + +//extern uint32_t RSPL_SvFLAGS(SV *sv) { +// return SvFLAGS(sv); +//} + +//extern bool RSPL_SvMAGICAL(SV *sv) { +// return SvMAGICAL(sv); +//} + +extern void RSPL_SvGETMAGIC(SV *sv) { + return SvGETMAGIC(sv); } /* diff --git a/perlmod/src/scalar.rs b/perlmod/src/scalar.rs index fb84f04..870940f 100644 --- a/perlmod/src/scalar.rs +++ b/perlmod/src/scalar.rs @@ -186,48 +186,49 @@ impl ScalarRef { self as *const ScalarRef as *const SV as *mut SV } - /// Get some information about the value's type. - pub fn ty(&self) -> Type { + fn get_type(sv: *mut SV) -> Type { unsafe { - if ffi::RSPL_is_reference(self.sv()) { - Type::Reference - } else { - let flags = ffi::RSPL_type_flags(self.sv()); - if ffi::RSPL_is_array(self.sv()) { - Type::Array - } else if ffi::RSPL_is_hash(self.sv()) { - Type::Hash - } else if flags != 0 { - // non-scalars will not have any flags: - Type::Scalar(Flags::from_bits_truncate(flags as u8)) - } else { - // but `undef` also has no flags, so: - let ty = ffi::RSPL_svtype(self.sv()); - if ty == 0 { - Type::Scalar(Flags::empty()) - } else if ty == ffi::RSPL_PVLV() { - self.get_target() - .map(|s| s.ty()) - .unwrap_or(Type::Other(99)) - } else { - Type::Other(ty as u8) - } - } + // These are simple: + if ffi::RSPL_is_reference(sv) { + return Type::Reference; + } else if ffi::RSPL_is_array(sv) { + return Type::Array; + } else if ffi::RSPL_is_hash(sv) { + return Type::Hash; } - } + + // Scalars have flags: + let flags = ffi::RSPL_type_flags(sv); + if flags != 0 { + return Type::Scalar(Flags::from_bits_truncate(flags as u8)); + } + + // Except for undef, but undef is difficult to catch: + let ty = ffi::RSPL_svtype(sv); + if ty == 0 { + // Looks like undef + return Type::Scalar(Flags::empty()); + } else if ty == ffi::RSPL_PVLV() { + // We don't support all kinds of magic, but some lvalues are simple: + // Try to GET the value and then check for definedness. + ffi::RSPL_SvGETMAGIC(sv); + if !ffi::RSPL_SvOK(sv) { + // This happens when the value points to a non-existing hash element we could + // auto-vivify, but we won't: + return Type::Scalar(Flags::empty()); + } + + // Otherwise we just try to "recurse", which will work for substrings. + return Self::get_type(ffi::RSPL_LvTARG(sv)); + } else { + return Type::Other(ty as u8); + } + }; } - /// Dereference this PVLV. - pub fn get_target(&self) -> Option { - let ptr = unsafe { - ffi::RSPL_vivify_defelem(self.sv()); - ffi::RSPL_LvTARG(self.sv()) - }; - if ptr.is_null() { - None - } else { - Some(unsafe { Scalar::from_raw_ref(ptr) }) - } + /// Get some information about the value's type. + pub fn ty(&self) -> Type { + Self::get_type(self.sv()) } /// Dereference this reference. @@ -400,7 +401,7 @@ impl serde::Serialize for Scalar { } Type::Other(other) => Err(S::Error::custom(format!( "cannot serialize weird magic perl values ({})", - other + other, ))), // These are impossible as they are all handled by different Value enum types: diff --git a/test.pl b/test.pl index ea67f69..782e467 100644 --- a/test.pl +++ b/test.pl @@ -17,7 +17,10 @@ $v->another(54); my $param = { a => 1 }; my $s = "Hello You"; +print "These should be called with a valid substr:\n"; RSPM::Foo142::test(substr($s, 3, 3)); RSPM::Foo142::teststr(substr($s, 3, 3)); +print "Parameter exists: " . (exists($param->{x}) ? "YES" : "NO") . "\n"; RSPM::Foo142::test($param->{x}); +print "Was auto-vivified: " . (exists($param->{x}) ? "YES" : "NO") . "\n"; RSPM::Foo142::teststr($param->{x}); diff --git a/test.pl.expected b/test.pl.expected new file mode 100644 index 0000000..c118214 --- /dev/null +++ b/test.pl.expected @@ -0,0 +1,11 @@ +Called something on Bless { "Hello" }! +Called 'another(54)' on Bless { "Hello" }! +test called with Some("lo ") +teststr called with Some("lo ") +test called with None +teststr called with None +Got (17, 32, ) +Got: 2 values: 17 32 +These should be called with a valid substr: +Parameter exists: NO +Was auto-vivified: NO