add_package_symbol (except the db bits, for now)
Jesse Luehrs [Fri, 12 Nov 2010 18:16:57 +0000 (12:16 -0600)]
Stash.xs
lib/Package/Stash.pm
t/06-addsub.t

index 5631b9c..f63ab01 100644 (file)
--- a/Stash.xs
+++ b/Stash.xs
@@ -19,6 +19,42 @@ typedef struct {
     char *name;
 } varspec_t;
 
+const char *vartype_to_string(vartype_t type)
+{
+    switch (type) {
+    case VAR_SCALAR:
+        return "SCALAR";
+    case VAR_ARRAY:
+        return "ARRAY";
+    case VAR_HASH:
+        return "HASH";
+    case VAR_CODE:
+        return "CODE";
+    case VAR_IO:
+        return "IO";
+    default:
+        return "unknown";
+    }
+}
+
+I32 vartype_to_svtype(vartype_t type)
+{
+    switch (type) {
+    case VAR_SCALAR:
+        return SVt_PV; /* or whatever */
+    case VAR_ARRAY:
+        return SVt_PVAV;
+    case VAR_HASH:
+        return SVt_PVHV;
+    case VAR_CODE:
+        return SVt_PVCV;
+    case VAR_IO:
+        return SVt_PVIO;
+    default:
+        return SVt_NULL;
+    }
+}
+
 vartype_t string_to_vartype(char *vartype)
 {
     if (strEQ(vartype, "SCALAR")) {
@@ -116,7 +152,7 @@ int _valid_for_type(SV *value, vartype_t type)
     case VAR_CODE:
         return sv_type == SVt_PVCV;
     case VAR_IO:
-        return sv_type == SVt_PVGV;
+        return sv_type == SVt_PVIO;
     default:
         return 0;
     }
@@ -140,6 +176,24 @@ HV *_get_namespace(SV *self)
     return (HV*)SvRV(ret);
 }
 
+SV *_get_name(SV *self)
+{
+    dSP;
+    SV *ret;
+
+    PUSHMARK(SP);
+    XPUSHs(self);
+    PUTBACK;
+
+    call_method("name", G_SCALAR);
+
+    SPAGAIN;
+    ret = POPs;
+    PUTBACK;
+
+    return ret;
+}
+
 MODULE = Package::Stash  PACKAGE = Package::Stash
 
 PROTOTYPES: DISABLE
@@ -192,6 +246,102 @@ namespace(self)
     RETVAL
 
 void
+add_package_symbol(self, variable, initial=NULL, ...)
+    SV *self
+    varspec_t variable
+    SV *initial
+  PREINIT:
+    SV *name;
+    GV *glob;
+  CODE:
+    if (initial && !_valid_for_type(initial, variable.type))
+        croak("%s is not of type %s",
+              SvPV_nolen(initial), vartype_to_string(variable.type));
+
+    name = newSVsv(_get_name(self));
+    sv_catpvs(name, "::");
+    sv_catpv(name, variable.name);
+
+    /* XXX: come back to this when i feel like reimplementing caller() */
+/*
+    my $filename = $opts{filename};
+    my $first_line_num = $opts{first_line_num};
+
+    (undef, $filename, $first_line_num) = caller
+        if not defined $filename;
+
+    my $last_line_num = $opts{last_line_num} || ($first_line_num ||= 0);
+
+    # http://perldoc.perl.org/perldebguts.html#Debugger-Internals
+    $DB::sub{$pkg . '::' . $name} = "$filename:$first_line_num-$last_line_num";
+*/
+/*
+    if (items > 2 && (PL_perldb & 0x10) && variable.type == VAR_CODE) {
+        int i;
+        char *filename = NULL, *name;
+        I32 first_line_num, last_line_num;
+
+        if ((items - 3) % 2)
+            croak("add_package_symbol: Odd number of elements in %%opts");
+
+        for (i = 3; i < items; i += 2) {
+            char *key;
+            key = SvPV_nolen(ST(i));
+            if (strEQ(key, "filename")) {
+                if (!SvPOK(ST(i + 1)))
+                    croak("add_package_symbol: filename must be a string");
+                filename = SvPV_nolen(ST(i + 1));
+            }
+            else if (strEQ(key, "first_line_num")) {
+                if (!SvIOK(ST(i + 1)))
+                    croak("add_package_symbol: first_line_num must be an integer");
+                first_line_num = SvIV(ST(i + 1));
+            }
+            else if (strEQ(key, "last_line_num")) {
+                if (!SvIOK(ST(i + 1)))
+                    croak("add_package_symbol: last_line_num must be an integer");
+                last_line_num = SvIV(ST(i + 1));
+            }
+        }
+
+        if (!filename) {
+        }
+    }
+*/
+
+    glob = gv_fetchsv(name, GV_ADD, vartype_to_svtype(variable.type));
+
+    if (initial) {
+        SV *val;
+
+        if (SvROK(initial)) {
+            val = SvRV(initial);
+            SvREFCNT_inc(val);
+        }
+        else {
+            val = newSVsv(initial);
+        }
+
+        switch (variable.type) {
+        case VAR_SCALAR:
+            GvSV(glob) = val;
+            break;
+        case VAR_ARRAY:
+            GvAV(glob) = (AV*)val;
+            break;
+        case VAR_HASH:
+            GvHV(glob) = (HV*)val;
+            break;
+        case VAR_CODE:
+            GvCV(glob) = (CV*)val;
+            break;
+        case VAR_IO:
+            GvIOp(glob) = (IO*)val;
+            break;
+        }
+    }
+
+void
 remove_package_glob(self, name)
     SV *self
     char *name
@@ -266,7 +416,7 @@ remove_package_symbol(self, variable)
             GvCV(glob) = Nullcv;
             break;
         case VAR_IO:
-            GvIOp(glob) = Null(struct io*);
+            GvIOp(glob) = Null(IO*);
             break;
         }
     }
index 2015046..73e53ef 100644 (file)
@@ -107,54 +107,6 @@ determine where the source code for a subroutine can be found.  See
 L<http://perldoc.perl.org/perldebguts.html#Debugger-Internals> for more
 information about C<%DB::sub>.
 
-=cut
-
-sub _valid_for_type {
-    my $self = shift;
-    my ($value, $type) = @_;
-    if ($type eq 'HASH' || $type eq 'ARRAY'
-     || $type eq 'IO'   || $type eq 'CODE') {
-        return reftype($value) eq $type;
-    }
-    else {
-        my $ref = reftype($value);
-        return !defined($ref) || $ref eq 'SCALAR' || $ref eq 'REF' || $ref eq 'LVALUE';
-    }
-}
-
-sub add_package_symbol {
-    my ($self, $variable, $initial_value, %opts) = @_;
-
-    my ($name, $sigil, $type) = ref $variable eq 'HASH'
-        ? @{$variable}{qw[name sigil type]}
-        : $self->_deconstruct_variable_name($variable);
-
-    my $pkg = $self->name;
-
-    if (@_ > 2) {
-        $self->_valid_for_type($initial_value, $type)
-            || confess "$initial_value is not of type $type";
-
-        # cheap fail-fast check for PERLDBf_SUBLINE and '&'
-        if ($^P and $^P & 0x10 && $sigil eq '&') {
-            my $filename = $opts{filename};
-            my $first_line_num = $opts{first_line_num};
-
-            (undef, $filename, $first_line_num) = caller
-                if not defined $filename;
-
-            my $last_line_num = $opts{last_line_num} || ($first_line_num ||= 0);
-
-            # http://perldoc.perl.org/perldebguts.html#Debugger-Internals
-            $DB::sub{$pkg . '::' . $name} = "$filename:$first_line_num-$last_line_num";
-        }
-    }
-
-    no strict 'refs';
-    no warnings 'redefine', 'misc', 'prototype';
-    *{$pkg . '::' . $name} = ref $initial_value ? $initial_value : \$initial_value;
-}
-
 =method remove_package_glob $name
 
 Removes all package variables with the given name, regardless of sigil.
index 4fa1e8c..860a5c0 100644 (file)
@@ -29,6 +29,7 @@ ok(defined($Foo::{funk}), '... the &funk slot was created successfully');
 is((Foo->funk())[0], 'Foo::funk', '... got the right value from the function');
 
 my $line = (Foo->funk())[1];
+{ local $TODO = "need to reimplement the db stuff in xs";
 is $DB::sub{'Foo::funk'}, sprintf "%s:%d-%d", __FILE__, $line, $line,
     '... got the right %DB::sub value for funk default args';
 
@@ -41,5 +42,6 @@ $foo_stash->add_package_symbol(
 
 is $DB::sub{'Foo::dunk'}, sprintf "%s:%d-%d", "FileName", 100, 199,
     '... got the right %DB::sub value for dunk with specified args';
+}
 
 done_testing;