+sub_op_config_t *sub_op_dup(pTHX_ const sub_op_config_t *orig) {
+ sub_op_config_t *dupe = PerlMemShared_malloc(sizeof *dupe);
+
+ dupe->name = so_strndup(orig->name, orig->namelen);
+ dupe->namelen = orig->namelen;
+
+ dupe->proto = so_strndup(orig->proto, orig->protolen);
+ dupe->protolen = orig->protolen;
+
+ dupe->call = orig->call;
+ dupe->ref = orig->ref;
+ dupe->ud = orig->ud;
+
+ return dupe;
+}
+
+void sub_op_free(pTHX_ sub_op_config_t *c) {
+ PerlMemShared_free((char *) c->name);
+ PerlMemShared_free(c);
+
+ return;
+}
+
+OP *sub_op_study(const OP *o, OP **last_arg_p, OP **rv2cv_p) {
+ OP *ex_list, *last_arg, *rv2cv, *gvop;
+
+ ex_list = cUNOPo->op_first;
+ /* pushmark when a method call */
+ if (!ex_list || ex_list->op_type != OP_NULL)
+ goto skip;
+
+ rv2cv = cUNOPx(ex_list)->op_first;
+ if (!rv2cv)
+ goto skip;
+
+ while (1) {
+ OP *next = rv2cv->op_sibling;
+ if (!next)
+ break;
+ last_arg = rv2cv;
+ rv2cv = next;
+ }
+
+ if (!(rv2cv->op_flags & OPf_KIDS))
+ goto skip;
+
+ gvop = cUNOPx(rv2cv)->op_first;
+
+ if (gvop && gvop->op_type == OP_GV)
+ goto done;
+
+skip:
+ last_arg = NULL;
+ rv2cv = NULL;
+ gvop = NULL;
+
+done:
+ if (last_arg_p)
+ *last_arg_p = last_arg;
+ if (rv2cv_p)
+ *rv2cv_p = rv2cv;
+
+ return gvop;
+}
+
+/* --- Private helpers ----------------------------------------------------- */
+
+STATIC IV so_hint(pTHX) {
+#define so_hint() so_hint(aTHX)