This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Revert "postpone perl_parse() exit(0) bugfix"
[perl5.git] / builtin.c
index 81e3511..a6373d2 100644 (file)
--- a/builtin.c
+++ b/builtin.c
@@ -32,6 +32,38 @@ static void S_warn_experimental_builtin(pTHX_ const char *name, bool prefix)
                      prefix ? "builtin::" : "", name);
 }
 
+/* These three utilities might want to live elsewhere to be reused from other
+ * code sometime
+ */
+#define prepare_export_lexical()  S_prepare_export_lexical(aTHX)
+static void S_prepare_export_lexical(pTHX)
+{
+    assert(PL_compcv);
+
+    /* We need to have PL_comppad / PL_curpad set correctly for lexical importing */
+    ENTER;
+    SAVESPTR(PL_comppad_name); PL_comppad_name = PadlistNAMES(CvPADLIST(PL_compcv));
+    SAVESPTR(PL_comppad);      PL_comppad      = PadlistARRAY(CvPADLIST(PL_compcv))[1];
+    SAVESPTR(PL_curpad);       PL_curpad       = PadARRAY(PL_comppad);
+}
+
+#define export_lexical(name, sv)  S_export_lexical(aTHX_ name, sv)
+static void S_export_lexical(pTHX_ SV *name, SV *sv)
+{
+    PADOFFSET off = pad_add_name_sv(name, padadd_STATE, 0, 0);
+    SvREFCNT_dec(PL_curpad[off]);
+    PL_curpad[off] = SvREFCNT_inc(sv);
+}
+
+#define finish_export_lexical()  S_finish_export_lexical(aTHX)
+static void S_finish_export_lexical(pTHX)
+{
+    intro_my();
+
+    LEAVE;
+}
+
+
 XS(XS_builtin_true);
 XS(XS_builtin_true)
 {
@@ -97,12 +129,12 @@ XS(XS_builtin_func1_scalar)
         croak_xs_usage(cv, "arg");
 
     switch(ix) {
-        case OP_ISBOOL:
-            Perl_pp_isbool(aTHX);
+        case OP_IS_BOOL:
+            Perl_pp_is_bool(aTHX);
             break;
 
-        case OP_ISWEAK:
-            Perl_pp_isweak(aTHX);
+        case OP_IS_WEAK:
+            Perl_pp_is_weak(aTHX);
             break;
 
         case OP_BLESSED:
@@ -125,6 +157,10 @@ XS(XS_builtin_func1_scalar)
             Perl_pp_floor(aTHX);
             break;
 
+        case OP_IS_TAINTED:
+            Perl_pp_is_tainted(aTHX);
+            break;
+
         default:
             Perl_die(aTHX_ "panic: unhandled opcode %" IVdf
                            " for xs_builtin_func1_scalar()", (IV) ix);
@@ -133,6 +169,173 @@ XS(XS_builtin_func1_scalar)
     XSRETURN(1);
 }
 
+XS(XS_builtin_trim);
+XS(XS_builtin_trim)
+{
+    dXSARGS;
+
+    warn_experimental_builtin("trim", true);
+
+    if (items != 1) {
+        croak_xs_usage(cv, "arg");
+    }
+
+    dTARGET;
+    SV *source = TOPs;
+    STRLEN len;
+    const U8 *start;
+    SV *dest;
+
+    SvGETMAGIC(source);
+
+    if (SvOK(source))
+        start = (const U8*)SvPV_nomg_const(source, len);
+    else {
+        if (ckWARN(WARN_UNINITIALIZED))
+            report_uninit(source);
+        start = (const U8*)"";
+        len = 0;
+    }
+
+    if (DO_UTF8(source)) {
+        const U8 *end = start + len;
+
+        /* Find the first non-space */
+        while(len) {
+            STRLEN thislen;
+            if (!isSPACE_utf8_safe(start, end))
+                break;
+            start += (thislen = UTF8SKIP(start));
+            len -= thislen;
+        }
+
+        /* Find the final non-space */
+        STRLEN thislen;
+        const U8 *cur_end = end;
+        while ((thislen = is_SPACE_utf8_safe_backwards(cur_end, start))) {
+            cur_end -= thislen;
+        }
+        len -= (end - cur_end);
+    }
+    else if (len) {
+        while(len) {
+            if (!isSPACE_L1(*start))
+                break;
+            start++;
+            len--;
+        }
+
+        while(len) {
+            if (!isSPACE_L1(start[len-1]))
+                break;
+            len--;
+        }
+    }
+
+    dest = TARG;
+
+    if (SvPOK(dest) && (dest == source)) {
+        sv_chop(dest, (const char *)start);
+        SvCUR_set(dest, len);
+    }
+    else {
+        SvUPGRADE(dest, SVt_PV);
+        SvGROW(dest, len + 1);
+
+        Copy(start, SvPVX(dest), len, U8);
+        SvPVX(dest)[len] = '\0';
+        SvPOK_on(dest);
+        SvCUR_set(dest, len);
+
+        if (DO_UTF8(source))
+            SvUTF8_on(dest);
+        else
+            SvUTF8_off(dest);
+
+        if (SvTAINTED(source))
+            SvTAINT(dest);
+    }
+
+    SvSETMAGIC(dest);
+
+    SETs(dest);
+
+    XSRETURN(1);
+}
+
+XS(XS_builtin_export_lexically);
+XS(XS_builtin_export_lexically)
+{
+    dXSARGS;
+
+    warn_experimental_builtin("export_lexically", true);
+
+    if(!PL_compcv)
+        Perl_croak(aTHX_
+                "export_lexically can only be called at compile time");
+
+    if(items % 2)
+        Perl_croak(aTHX_ "Odd number of elements in export_lexically");
+
+    for(int i = 0; i < items; i += 2) {
+        SV *name = ST(i);
+        SV *ref  = ST(i+1);
+
+        if(!SvROK(ref))
+            /* diag_listed_as: Expected %s reference in export_lexically */
+            Perl_croak(aTHX_ "Expected a reference in export_lexically");
+
+        char sigil = SvPVX(name)[0];
+        SV *rv = SvRV(ref);
+
+        const char *bad = NULL;
+        switch(sigil) {
+            default:
+                /* overwrites the pointer on the stack; but this is fine, the
+                 * caller's value isn't modified */
+                ST(i) = name = sv_2mortal(Perl_newSVpvf(aTHX_ "&%" SVf, SVfARG(name)));
+
+                /* FALLTHROUGH */
+            case '&':
+                if(SvTYPE(rv) != SVt_PVCV)
+                    bad = "a CODE";
+                break;
+
+            case '$':
+                /* Permit any of SVt_NULL to SVt_PVMG. Technically this also
+                 * includes SVt_INVLIST but it isn't thought possible for pureperl
+                 * code to ever manage to see one of those. */
+                if(SvTYPE(rv) > SVt_PVMG)
+                    bad = "a SCALAR";
+                break;
+
+            case '@':
+                if(SvTYPE(rv) != SVt_PVAV)
+                    bad = "an ARRAY";
+                break;
+
+            case '%':
+                if(SvTYPE(rv) != SVt_PVHV)
+                    bad = "a HASH";
+                break;
+        }
+
+        if(bad)
+            Perl_croak(aTHX_ "Expected %s reference in export_lexically", bad);
+    }
+
+    prepare_export_lexical();
+
+    for(int i = 0; i < items; i += 2) {
+        SV *name = ST(i);
+        SV *ref  = ST(i+1);
+
+        export_lexical(name, SvRV(ref));
+    }
+
+    finish_export_lexical();
+}
+
 XS(XS_builtin_func1_void);
 XS(XS_builtin_func1_void)
 {
@@ -161,6 +364,36 @@ XS(XS_builtin_func1_void)
     XSRETURN(0);
 }
 
+XS(XS_builtin_created_as_string)
+{
+    dXSARGS;
+
+    if(items != 1)
+        croak_xs_usage(cv, "arg");
+
+    SV *arg = ST(0);
+    SvGETMAGIC(arg);
+
+    /* SV was created as string if it has POK and isn't bool */
+    ST(0) = boolSV(SvPOK(arg) && !SvIsBOOL(arg));
+    XSRETURN(1);
+}
+
+XS(XS_builtin_created_as_number)
+{
+    dXSARGS;
+
+    if(items != 1)
+        croak_xs_usage(cv, "arg");
+
+    SV *arg = ST(0);
+    SvGETMAGIC(arg);
+
+    /* SV was created as number if it has NOK or IOK but not POK and is not bool */
+    ST(0) = boolSV(SvNIOK(arg) && !SvPOK(arg) && !SvIsBOOL(arg));
+    XSRETURN(1);
+}
+
 static OP *ck_builtin_func1(pTHX_ OP *entersubop, GV *namegv, SV *ckobj)
 {
     const struct BuiltinFuncDescriptor *builtin = NUM2PTR(const struct BuiltinFuncDescriptor *, SvUV(ckobj));
@@ -174,6 +407,10 @@ static OP *ck_builtin_func1(pTHX_ OP *entersubop, GV *namegv, SV *ckobj)
 
     entersubop = ck_entersub_args_proto(entersubop, namegv, prototype);
 
+    OPCODE opcode = builtin->ckval;
+    if(!opcode)
+        return entersubop;
+
     OP *parent = entersubop, *pushop, *argop;
 
     pushop = cUNOPx(entersubop)->op_first;
@@ -192,11 +429,58 @@ static OP *ck_builtin_func1(pTHX_ OP *entersubop, GV *namegv, SV *ckobj)
 
     op_free(entersubop);
 
-    OPCODE opcode = builtin->ckval;
-
     return newUNOP(opcode, wantflags, argop);
 }
 
+XS(XS_builtin_indexed)
+{
+    dXSARGS;
+
+    switch(GIMME_V) {
+        case G_VOID:
+            Perl_ck_warner(aTHX_ packWARN(WARN_VOID),
+                "Useless use of %s in void context", "builtin::indexed");
+            XSRETURN(0);
+
+        case G_SCALAR:
+            Perl_ck_warner(aTHX_ packWARN(WARN_SCALAR),
+                "Useless use of %s in scalar context", "builtin::indexed");
+            ST(0) = sv_2mortal(newSViv(items * 2));
+            XSRETURN(1);
+
+        case G_LIST:
+            break;
+    }
+
+    SSize_t retcount = items * 2;
+    EXTEND(SP, retcount);
+
+    /* Copy from [items-1] down to [0] so we don't have to make
+     * temporary copies */
+    for(SSize_t index = items - 1; index >= 0; index--) {
+        /* Copy, not alias */
+        ST(index * 2 + 1) = sv_mortalcopy(ST(index));
+        ST(index * 2)     = sv_2mortal(newSViv(index));
+    }
+
+    XSRETURN(retcount);
+}
+
+static OP *ck_builtin_funcN(pTHX_ OP *entersubop, GV *namegv, SV *ckobj)
+{
+    const struct BuiltinFuncDescriptor *builtin = NUM2PTR(const struct BuiltinFuncDescriptor *, SvUV(ckobj));
+
+    warn_experimental_builtin(builtin->name, false);
+
+    SV *prototype = newSVpvs("@");
+    SAVEFREESV(prototype);
+
+    assert(entersubop->op_type == OP_ENTERSUB);
+
+    entersubop = ck_entersub_args_proto(entersubop, namegv, prototype);
+    return entersubop;
+}
+
 static const char builtin_not_recognised[] = "'%" SVf "' is not recognised as a builtin function";
 
 static const struct BuiltinFuncDescriptor builtins[] = {
@@ -205,15 +489,24 @@ static const struct BuiltinFuncDescriptor builtins[] = {
     { "builtin::false",  &XS_builtin_false,  &ck_builtin_const, BUILTIN_CONST_FALSE },
 
     /* unary functions */
-    { "builtin::isbool",   &XS_builtin_func1_scalar, &ck_builtin_func1, OP_ISBOOL   },
-    { "builtin::weaken",   &XS_builtin_func1_void,   &ck_builtin_func1, OP_WEAKEN   },
-    { "builtin::unweaken", &XS_builtin_func1_void,   &ck_builtin_func1, OP_UNWEAKEN },
-    { "builtin::isweak",   &XS_builtin_func1_scalar, &ck_builtin_func1, OP_ISWEAK   },
-    { "builtin::blessed",  &XS_builtin_func1_scalar, &ck_builtin_func1, OP_BLESSED  },
-    { "builtin::refaddr",  &XS_builtin_func1_scalar, &ck_builtin_func1, OP_REFADDR  },
-    { "builtin::reftype",  &XS_builtin_func1_scalar, &ck_builtin_func1, OP_REFTYPE  },
-    { "builtin::ceil",     &XS_builtin_func1_scalar, &ck_builtin_func1, OP_CEIL     },
-    { "builtin::floor",    &XS_builtin_func1_scalar, &ck_builtin_func1, OP_FLOOR    },
+    { "builtin::is_bool",    &XS_builtin_func1_scalar, &ck_builtin_func1, OP_IS_BOOL    },
+    { "builtin::weaken",     &XS_builtin_func1_void,   &ck_builtin_func1, OP_WEAKEN     },
+    { "builtin::unweaken",   &XS_builtin_func1_void,   &ck_builtin_func1, OP_UNWEAKEN   },
+    { "builtin::is_weak",    &XS_builtin_func1_scalar, &ck_builtin_func1, OP_IS_WEAK    },
+    { "builtin::blessed",    &XS_builtin_func1_scalar, &ck_builtin_func1, OP_BLESSED    },
+    { "builtin::refaddr",    &XS_builtin_func1_scalar, &ck_builtin_func1, OP_REFADDR    },
+    { "builtin::reftype",    &XS_builtin_func1_scalar, &ck_builtin_func1, OP_REFTYPE    },
+    { "builtin::ceil",       &XS_builtin_func1_scalar, &ck_builtin_func1, OP_CEIL       },
+    { "builtin::floor",      &XS_builtin_func1_scalar, &ck_builtin_func1, OP_FLOOR      },
+    { "builtin::is_tainted", &XS_builtin_func1_scalar, &ck_builtin_func1, OP_IS_TAINTED },
+    { "builtin::trim",       &XS_builtin_trim,         &ck_builtin_func1, 0 },
+
+    { "builtin::created_as_string", &XS_builtin_created_as_string, &ck_builtin_func1, 0 },
+    { "builtin::created_as_number", &XS_builtin_created_as_number, &ck_builtin_func1, 0 },
+
+    /* list functions */
+    { "builtin::indexed", &XS_builtin_indexed, &ck_builtin_funcN, 0 },
+    { "builtin::export_lexically", &XS_builtin_export_lexically, NULL, 0 },
     { 0 }
 };
 
@@ -224,13 +517,9 @@ XS(XS_builtin_import)
 
     if(!PL_compcv)
         Perl_croak(aTHX_
-                "builtin::import can only be called at compiletime");
+                "builtin::import can only be called at compile time");
 
-    /* We need to have PL_comppad / PL_curpad set correctly for lexical importing */
-    ENTER;
-    SAVESPTR(PL_comppad_name); PL_comppad_name = PadlistNAMES(CvPADLIST(PL_compcv));
-    SAVESPTR(PL_comppad);      PL_comppad      = PadlistARRAY(CvPADLIST(PL_compcv))[1];
-    SAVESPTR(PL_curpad);       PL_curpad       = PadARRAY(PL_comppad);
+    prepare_export_lexical();
 
     for(int i = 1; i < items; i++) {
         SV *sym = ST(i);
@@ -238,20 +527,16 @@ XS(XS_builtin_import)
             Perl_croak(aTHX_ builtin_not_recognised, sym);
 
         SV *ampname = sv_2mortal(Perl_newSVpvf(aTHX_ "&%" SVf, SVfARG(sym)));
-        SV *fqname  = sv_2mortal(Perl_newSVpvf(aTHX_ "builtin::%" SVf, SVfARG(sym)));
+        SV *fqname = sv_2mortal(Perl_newSVpvf(aTHX_ "builtin::%" SVf, SVfARG(sym)));
 
         CV *cv = get_cv(SvPV_nolen(fqname), SvUTF8(fqname) ? SVf_UTF8 : 0);
         if(!cv)
             Perl_croak(aTHX_ builtin_not_recognised, sym);
 
-        PADOFFSET off = pad_add_name_sv(ampname, padadd_STATE, 0, 0);
-        SvREFCNT_dec(PL_curpad[off]);
-        PL_curpad[off] = SvREFCNT_inc(cv);
+        export_lexical(ampname, (SV *)cv);
     }
 
-    intro_my();
-
-    LEAVE;
+    finish_export_lexical();
 }
 
 void