Branch: Tag:

2000-03-14

2000-03-14 21:33:24 by Leif Stensson <leif@lysator.liu.se>

Rewrote stuff, and added several new functions.

Rev: src/modules/Perl/perlmod.c:1.12

1: + /* $Id: perlmod.c,v 1.12 2000/03/14 21:33:24 leif Exp $ */ +  + #include "builtin_functions.h"   #include "global.h"   #include "svalue.h"   #include "array.h"
8:   #include "mapping.h"   #include "perl_machine.h"    +  /* this is just for debugging */ + #define _sv_2mortal(x) (x) +    #ifdef HAVE_PERL      #include <EXTERN.h>
28:    char **env;    char *env_block;    struct array *argv_strings; -  int parsed; +  int constructed, parsed; +  int array_size_limit;    PerlInterpreter *my_perl;   };      #define THIS ((struct perlmod_storage *)(fp->current_storage))   #define PERL THIS->my_perl    -  + /* since both Perl and Pike likes to use "sp" as a stack pointer, +  * let's define some Pike macros as functions... +  */ + static void _push_int(INT32 i) { push_int(i);} + static void _push_float(float f) { push_float(f);} + static void _push_string(struct pike_string *s) { push_string(s);} + static void _push_array(struct array *a) { push_array(a);} + static void _pop_n_elems(int n) { pop_n_elems(n);} + static struct svalue *_pikesp() { return Pike_sp;} + static void _pike_pop() { --sp;} + #undef sp +  + #ifndef BLOCKING +  + #define MT_PERMIT THREADS_ALLOW(); mt_lock(&perl_running); + #define MT_FORBID mt_unlock(&perl_running); THREADS_DISALLOW(); +  + #endif +  + /* utility function: push a zero_type zero */ + static void _push_zerotype() + { push_int(0); +  Pike_sp[-1].subtype = 1; + } +  + static SV * _pikev2sv(struct svalue *s) + { switch (s->type) +  { case T_INT: +  return newSViv(s->u.integer); break; +  case T_FLOAT: +  return newSVnv(s->u.float_number); break; +  case T_STRING: +  if (s->u.string->size_shift) break; +  return newSVpv(s->u.string->str, s->u.string->len); break; +  } +  error("Unsupported value type.\n"); +  return 0; + } +  + static void _sv_to_svalue(SV *sv, struct svalue *sval) + { if (sv && (SvOK(sv))) +  { if (SvIOKp(sv)) +  { sval->type = T_INT; sval->subtype = 0; +  sval->u.integer = SvIV(sv); +  return; +  } +  else if (SvNOKp(sv)) +  { sval->type = T_FLOAT; sval->subtype = 0; +  sval->u.float_number = SvNV(sv); +  return; +  } +  else if (SvPOKp(sv)) +  { sval->type = T_STRING; sval->subtype = 0; +  sval->u.string = make_shared_binary_string(SvPVX(sv), SvCUR(sv)); +  return; +  } +  } +  sval->type = T_INT; sval->u.integer = 0; +  sval->subtype = !sv; /* zero-type zero if NULL pointer */ + } +  + static void _pikepush_sv(SV *sv) + { if (!SvOK(sv)) +  _push_int(0); +  else if (SvIOKp(sv)) +  _push_int(SvIV(sv)); +  else if (SvNOKp(sv)) +  _push_float((float)(SvNV(sv))); +  else if (SvPOKp(sv)) +  _push_string(make_shared_binary_string(SvPVX(sv), SvCUR(sv))); +  else +  _push_int(0); + } +  +    static void init_perl_glue(struct object *o) - { -  PerlInterpreter *p; -  THIS->argv=0; -  THIS->env=0; -  THIS->env_block=0; -  THIS->argv_strings=0; -  THIS->parsed=0; + { PerlInterpreter *p;    -  + #ifdef PIKE_PERLDEBUG +  fprintf(stderr, "[init_perl_glue]\n"); + #endif +  +  THIS->argv = 0; +  THIS->env = 0; +  THIS->env_block = 0; +  THIS->argv_strings = 0; +  THIS->constructed = 0; +  THIS->parsed = 0; +  THIS->array_size_limit = 500; +    #ifndef MULTIPLICITY    if(num_perl_interpreters>0)    {
53: Inside #if undefined(MULTIPLICITY)
   return;    }   #endif -  THREADS_ALLOW(); -  mt_lock(&perl_running); +  MT_PERMIT;    p=perl_alloc(); -  mt_unlock(&perl_running); -  THREADS_DISALLOW(); +  MT_FORBID;    PERL=p;    if(p) num_perl_interpreters++;   }    - static void exit_perl_glue(struct object *o) + static void _free_arg_and_env() + { if(THIS->argv)    { -  if(PERL) -  { -  struct perlmod_storage *storage=THIS; -  -  THREADS_ALLOW(); -  mt_lock(&perl_running); -  if(storage->parsed) -  { -  perl_destruct(storage->my_perl); -  storage->parsed=0; -  } -  perl_free(storage->my_perl); -  mt_unlock(&perl_running); -  THREADS_DISALLOW(); -  num_perl_interpreters--; -  } -  if(THIS->argv) -  { +     free((char *)THIS->argv);    THIS->argv=0;    }
102:    }   }    - static void perlmod_run(INT32 args) + static void exit_perl_glue(struct object *o)   { -  INT32 i; -  PerlInterpreter *p=PERL; -  if(!p) error("No perl interpreter available.\n"); -  pop_n_elems(args); + #ifdef PIKE_PERLDEBUG +  fprintf(stderr, "[exit_perl_glue]\n"); + #endif    -  if(!THIS->argv_strings) -  error("Perl->create() must be called first.\n"); +  if(PERL) +  { +  struct perlmod_storage *storage=THIS;    -  THREADS_ALLOW(); -  mt_lock(&perl_running); -  i=perl_run(p); -  mt_unlock(&perl_running); -  THREADS_DISALLOW(); -  push_int(i); +  MT_PERMIT; +  if(storage->constructed) +  { +  perl_destruct(storage->my_perl); +  storage->constructed=0;    } -  +  perl_free(storage->my_perl); +  MT_FORBID; +  num_perl_interpreters--; +  } +  _free_arg_and_env(); + }      static void perlmod_create(INT32 args) -  + { PerlInterpreter *p=PERL; +  struct perlmod_storage *storage=THIS; +  + #ifdef PIKE_PERLDEBUG +  fprintf(stderr, "[perlmod_create, %d args]\n", args); + #endif +  +  if (args != 0) error("Perl->create takes no arguments."); +  if(!p) error("No perl interpreter available.\n"); +  +  MT_PERMIT; +  if(!storage->constructed) +  { perl_construct(p); +  storage->constructed++; +  } +  MT_FORBID; +  pop_n_elems(args); +  push_int(0); + } +  + static void perlmod_parse(INT32 args)   {    extern void xs_init(void);    int e;
128:    PerlInterpreter *p=PERL;    struct perlmod_storage *storage=THIS;    -  check_all_args("Perl->create",args,BIT_ARRAY, BIT_MAPPING|BIT_VOID, 0); + #ifdef PIKE_PERLDEBUG +  fprintf(stderr, "[perlmod_parse, %d args]\n", args); + #endif +  +  check_all_args("Perl->parse",args,BIT_ARRAY, BIT_MAPPING|BIT_VOID, 0);    if(!p) error("No perl interpreter available.\n");    -  if(THIS->argv_strings) -  error("Perl->create() can only be called once.\n"); -  +     switch(args)    {    default: -  env_mapping=sp[1-args].u.mapping; +  env_mapping = Pike_sp[1-args].u.mapping;    mapping_fix_type_field(env_mapping);       if(m_ind_types(env_mapping) & ~BIT_STRING)
146:    error("Bad argument 2 to Perl->create().\n");       case 1: -  THIS->argv_strings=sp[-args].u.array; +  if (THIS->argv_strings || THIS->env_block) +  { /* if we have already setup args/env, free the old values now */ +  _free_arg_and_env(); +  } +  +  THIS->argv_strings = Pike_sp[-args].u.array;    add_ref(THIS->argv_strings);    array_fix_type_field(THIS->argv_strings);   
154:    error("Perl: Too few elements in argv array.\n");       if(THIS->argv_strings->type_field & ~BIT_STRING) -  error("Bad argument 1 to Perl->create().\n"); +  error("Bad argument 1 to Perl->parse().\n");    }       THIS->argv=(char **)xalloc(sizeof(char *)*THIS->argv_strings->size);
189:    *(env_blockp++)=0;    }    THIS->env[d]=0; -  } else { +  } +  else +  {    /* Perl likes to be able to write in the environment block,    * give it it's own copy to protect ourselves.. /Hubbe    */
225:    THIS->env[d]=0;    }    -  THREADS_ALLOW(); -  mt_lock(&perl_running); -  if(!storage->parsed) -  { -  perl_construct(p); -  storage->parsed++; -  } +  +  THIS->parsed++; +  +  MT_PERMIT;    e=perl_parse(p,    xs_init,    storage->argv_strings->size,    storage->argv,    storage->env); -  mt_unlock(&perl_running); -  THREADS_DISALLOW(); +  MT_FORBID;    pop_n_elems(args);    push_int(e);   }    - static void perlmod_eval(INT32 args) + static void perlmod_run(INT32 args)   { -  error("Perl->eval not yet implemented.\n"); +  INT32 i; +  PerlInterpreter *p=PERL; +  if(!p) error("No perl interpreter available.\n"); +  pop_n_elems(args); +  +  if(!THIS->constructed || !THIS->parsed) +  error("No Perl program loaded (run() called before parse()).\n"); +  +  MT_PERMIT; +  i=perl_run(p); +  MT_FORBID; +  +  push_int(i);   }    -  + static void _perlmod_eval(INT32 args, int perlflags) + { PerlInterpreter *p = PERL; +  struct pike_string *arg1; +  struct perlmod_storage *storage = THIS; +  int i, n; + #define sp _perlsp +  dSP; +  +  if (!p) error("Perl interpreter not available.\n"); +  +  check_all_args("Perl->eval", args, BIT_STRING, 0); +  arg1 = _pikesp()[-args].u.string; +  +  ENTER; +  SAVETMPS; +  PUSHMARK(sp); +  +  PUTBACK; + #undef sp +  MT_PERMIT; +  +  if (!storage->parsed) +  { static char *dummyargv[] = { "perl", "-e", "1", 0 }; +  extern void xs_init(void); +  perl_parse(p, xs_init, 3, dummyargv, NULL); +  storage->parsed++; +  } +  +  n = perl_eval_sv(newSVpv(arg1->str, arg1->len), perlflags | G_EVAL); +  +  MT_FORBID; +  +  _pop_n_elems(args); +  + #define sp _perlsp +  SPAGAIN; +  +  if (SvTRUE(GvSV(errgv))) +  { char errtmp[256]; +  memset(errtmp, 0, sizeof(errtmp)); +  strcpy(errtmp, "Error from Perl: "); +  strncpy(errtmp+strlen(errtmp), SvPV(GvSV(errgv), na), 254-strlen(errtmp)); +  POPs; +  PUTBACK; FREETMPS; LEAVE; +  error(errtmp); +  } +  +  if (perlflags & G_ARRAY) +  { struct array *a = allocate_array(n); +  for(i = 0; i < n; ++i) +  _sv_to_svalue(POPs, &(a->item[(n-1)-i])); +  _push_array(a); +  } +  else if (n > 0) +  { for(; n > 1; --n) POPs; +  _pikepush_sv(POPs); +  } +  else _push_zerotype(); +  +  PUTBACK; FREETMPS; LEAVE; + #undef sp + } +  + static void perlmod_eval(INT32 args) +  { return _perlmod_eval(args, G_SCALAR);} +  + static void perlmod_eval_list(INT32 args) +  { return _perlmod_eval(args, G_ARRAY);} +  + static void _perlmod_call(INT32 args, int perlflags) + { PerlInterpreter *p = PERL; +  int i, n; char *pv; + #define sp _perlsp +  dSP; +  + #ifdef PIKE_PERLDEBUG +  fprintf(stderr, "[perlmod_call: args=%d]\n", args); + #endif +  +  if (!p) error("No perl interpreter available.\n"); +  +  if (args < 1) error("Too few arguments.\n"); +  if (args > 201) error("Too many arguments.\n"); +  +  if (_pikesp()[-args].type != T_STRING || +  _pikesp()[-args].u.string->size_shift) +  error("bad Perl function name (must be an 8-bit string)"); +  +  ENTER; +  SAVETMPS; +  PUSHMARK(sp); +  +  for(n = 1; n < args; ++n) +  { struct svalue *s = &(_pikesp()[n-args]); +  char *msg; +  switch (s->type) +  { case T_INT: +  XPUSHs(sv_2mortal(newSViv(s->u.integer))); +  break; +  case T_FLOAT: +  XPUSHs(sv_2mortal(newSVnv((double)(s->u.float_number)))); +  break; +  case T_STRING: +  if (s->u.string->size_shift) +  { PUTBACK; FREETMPS; LEAVE; +  error("widestrings not supported in Pike-to-Perl call interface"); +  return; +  } +  XPUSHs(sv_2mortal(newSVpv(s->u.string->str, s->u.string->len))); +  break; +  case T_MAPPING: +  msg = "Mapping argument not allowed here.\n"; if (0) +  case T_OBJECT: +  msg = "Object argument not allowed here.\n"; if (0) +  case T_MULTISET: +  msg = "Multiset argument not allowed here.\n"; if (0) +  case T_ARRAY: +  msg = "Array argument not allowed here.\n"; if (0) +  default: +  msg = "Unsupported argument type.\n"; +  PUTBACK; FREETMPS; LEAVE; +  error(msg); +  return; +  } +  } +  PUTBACK; +  +  pv = Pike_sp[-args].u.string->str; + #undef sp +  MT_PERMIT; +  +  n = perl_call_pv(pv, perlflags); +  +  MT_FORBID; + #define sp _perlsp +  +  _pop_n_elems(args); +  +  SPAGAIN; +  +  if (SvTRUE(GvSV(errgv))) +  { char errtmp[256]; +  memset(errtmp, 0, sizeof(errtmp)); +  strcpy(errtmp, "Error from Perl: "); +  strncpy(errtmp+strlen(errtmp), SvPV(GvSV(errgv), na), 254-strlen(errtmp)); +  POPs; +  PUTBACK; FREETMPS; LEAVE; +  error(errtmp); +  } +  +  if (n < 0) +  { PUTBACK; FREETMPS; LEAVE; +  error("Internal error: perl_call_pv returned a negative number.\n"); +  } +  +  if (!(perlflags & G_ARRAY) && n > 1) +  while (n > 1) --n, POPs; +  +  if (n > THIS->array_size_limit) +  { PUTBACK; FREETMPS; LEAVE; +  error("Perl function returned too many values.\n"); +  } +  +  if (perlflags & G_ARRAY) +  { struct array *a = allocate_array(n); +  for(i = 0; i < n; ++i) +  _sv_to_svalue(POPs, &(a->item[(n-1)-i])); +  _push_array(a); +  } +  else if (n == 1) +  _pikepush_sv(POPs); +  else /* shouldn't happen unless we put G_DISCARD in perlflags */ +  _push_zerotype(); +  +  PUTBACK; FREETMPS; LEAVE; + #undef sp + } +  + static void perlmod_call_list(INT32 args) + { _perlmod_call(args, G_ARRAY | G_EVAL); + } +    static void perlmod_call(INT32 args) - { -  error("Perl->call not yet implemented.\n"); + { _perlmod_call(args, G_SCALAR | G_EVAL);   }    -  + static void _perlmod_varop(INT32 args, int op, int type) + { int i, wanted_args; +  +  wanted_args = type == 'S' ? 1 : 2; +  if (op == 'W') ++wanted_args; +  +  if (!(PERL)) error("No Perl interpreter available.\n"); +  +  if (args != wanted_args) error("Wrong number of arguments.\n"); +  if (Pike_sp[-args].type != T_STRING || +  Pike_sp[-args].u.string->size_shift != 0) +  error("Variable name must be an 8-bit string.\n"); +  +  if (type == 'S') /* scalar */ +  { SV *sv = perl_get_sv(Pike_sp[-args].u.string->str, TRUE | GV_ADDMULTI); +  if (op == 'W') +  { sv_setsv(sv, sv_2mortal(_pikev2sv(Pike_sp-1)));} +  pop_n_elems(args); +  if (op == 'R') _pikepush_sv(sv); +  } +  else if (type == 'A') /* array */ +  { AV *av = perl_get_av(Pike_sp[-args].u.string->str, TRUE | GV_ADDMULTI); +  SV **svp; +  if (Pike_sp[1-args].type != T_INT || (i = Pike_sp[1-args].u.integer) < 0) +  error("Array subscript must be a non-negative integer.\n"); +  if (op == 'W') +  av_store(av, i, _sv_2mortal(_pikev2sv(Pike_sp+2-args))); +  pop_n_elems(args); +  if (op == 'R') +  { if ((svp = av_fetch(av, i, 0))) _pikepush_sv(*svp); +  else _push_zerotype(); +  } +  } +  else if (type == 'H') /* hash */ +  { HV *hv = perl_get_hv(Pike_sp[-args].u.string->str, TRUE | GV_ADDMULTI); +  SV *key = sv_2mortal(_pikev2sv(Pike_sp+1-args)); +  HE *he; +  if (op == 'W') +  { if ((he = hv_store_ent +  (hv, key, _sv_2mortal(_pikev2sv(Pike_sp+2-args)), 0))) +  sv_setsv(HeVAL(he), _sv_2mortal(_pikev2sv(Pike_sp+2-args))); +  else +  error("Internal error: hv_store_ent returned NULL.\n"); +  } +  pop_n_elems(args); +  if (op == 'R') +  { if ((he = hv_fetch_ent(hv, key, 0, 0))) +  _pikepush_sv(HeVAL(he)); +  else +  _push_zerotype(); +  } +  } +  else error("Internal error in _perlmod_varop.\n"); +  +  if (op != 'R') push_int(0); + } +  + static void perlmod_get_scalar(INT32 args) +  { _perlmod_varop(args, 'R', 'S');} + static void perlmod_set_scalar(INT32 args) +  { _perlmod_varop(args, 'W', 'S');} + static void perlmod_get_array_item(INT32 args) +  { _perlmod_varop(args, 'R', 'A');} + static void perlmod_set_array_item(INT32 args) +  { _perlmod_varop(args, 'W', 'A');} + static void perlmod_get_hash_item(INT32 args) +  { _perlmod_varop(args, 'R', 'H');} + static void perlmod_set_hash_item(INT32 args) +  { _perlmod_varop(args, 'W', 'H');} +  + static void perlmod_array_size(INT32 args) + { AV *av; +  if (args != 1) error("Wrong number of arguments.\n"); +  if (Pike_sp[-args].type != T_STRING || +  Pike_sp[-args].u.string->size_shift != 0) +  error("Array name must be given as an 8-bit string.\n"); +  +  av = perl_get_av(Pike_sp[-args].u.string->str, TRUE | GV_ADDMULTI); +  if (!av) error("Interal error: perl_get_av() return NULL.\n"); +  pop_n_elems(args); +  /* Return av_len()+1, since av_len() returns the value of the highest +  * index, which is 1 less than the size. */ +  _push_int(av_len(av)+1); + } +  + static void perlmod_get_whole_array(INT32 args) + { AV *av; int i, n; struct array *arr; +  if (args != 1) error("Wrong number of arguments.\n"); +  if (Pike_sp[-args].type != T_STRING || +  Pike_sp[-args].u.string->size_shift != 0) +  error("Array name must be given as an 8-bit string.\n"); +  +  av = perl_get_av(Pike_sp[-args].u.string->str, TRUE | GV_ADDMULTI); +  if (!av) error("Interal error: perl_get_av() returned NULL.\n"); +  n = av_len(av) + 1; +  +  if (n > THIS->array_size_limit) +  error("The array is larger than array_size_limit.\n"); +  +  arr = allocate_array(n); +  for(i = 0; i < n; ++i) +  { SV **svp = av_fetch(av, i, 0); +  _sv_to_svalue(svp ? *svp : NULL, &(arr->item[i])); +  } +  pop_n_elems(args); +  push_array(arr); + } +  + static void perlmod_get_hash_keys(INT32 args) + { HV *hv; HE *he; SV *sv; int i, n; I32 len; struct array *arr; +  if (args != 1) error("Wrong number of arguments.\n"); +  if (Pike_sp[-args].type != T_STRING || +  Pike_sp[-args].u.string->size_shift != 0) +  error("Hash name must be given as an 8-bit string.\n"); +  +  hv = perl_get_hv(Pike_sp[-args].u.string->str, TRUE | GV_ADDMULTI); +  if (!hv) error("Interal error: perl_get_av() return NULL.\n"); +  +  /* count number of elements in hash */ +  for(n = 0, hv_iterinit(hv); (he = hv_iternext(hv)); ++n); +  +  if (n > THIS->array_size_limit) +  error("The array is larger than array_size_limit.\n"); +  +  arr = allocate_array(n); +  for(i = 0, hv_iterinit(hv); (he = hv_iternext(hv)); ++i) +  _sv_to_svalue(hv_iterkey(he, &len), +  &(arr->item[i])); +  +  pop_n_elems(args); +  push_array(arr); + } +  + static void perlmod_array_size_limit(INT32 args) + { int i; +  switch (args) +  { case 0: +  break; +  case 1: +  if (Pike_sp[-args].type != T_INT || Pike_sp[-args].u.integer < 1) +  error("Argument must be a integer in range 1 to 2147483647."); +  THIS->array_size_limit = Pike_sp[-args].u.integer; +  break; +  default: +  error("Wrong number of arguments.\n"); +  } +  pop_n_elems(args); +  _push_int(THIS->array_size_limit); + } +    void pike_module_init(void)   { -  perl_destruct_level=2; + #ifdef PIKE_PERLDEBUG +  fprintf(stderr, "[perl: module init]\n"); + #endif +  +  perl_destruct_level=1; +     start_new_program();    ADD_STORAGE(struct perlmod_storage); -  +  /* function(void:int) */ +  ADD_FUNCTION("create",perlmod_create,tFunc(tVoid,tInt),0);    /* function(array(string),void|mapping(string:string):int) */ -  ADD_FUNCTION("create",perlmod_create,tFunc(tArr(tStr) tOr(tVoid,tMap(tStr,tStr)),tInt),0); +  ADD_FUNCTION("parse",perlmod_parse,tFunc(tArr(tStr) tOr(tVoid,tMap(tStr,tStr)),tInt),0);    /* function(:int) */    ADD_FUNCTION("run",perlmod_run,tFunc(tNone,tInt),0); -  +  +  /* function(string,mixed...:mixed) */ +  ADD_FUNCTION("call",perlmod_call,tFuncV(tStr,tMix,tMix),0); +  +  /* function(string,mixed...:mixed) */ +  ADD_FUNCTION("call_list",perlmod_call_list,tFuncV(tStr,tMix,tMix),0); +  +  /* function(string:mixed) */ +  ADD_FUNCTION("eval",perlmod_eval,tFunc(tStr,tMix),0); +  +  /* function(string:array) */ +  ADD_FUNCTION("eval_list",perlmod_eval_list,tFunc(tStr,tArr(tMix)),0); +  +  /* function(string:mixed) */ +  ADD_FUNCTION("get_scalar",perlmod_get_scalar,tFunc(tStr,tMix),0); +  +  /* function(string,mixed:mixed) */ +  ADD_FUNCTION("set_scalar",perlmod_set_scalar,tFunc(tStr tMix,tMix),0); +  +  /* function(string,int:mixed) */ +  ADD_FUNCTION("get_array_item",perlmod_get_array_item, +  tFunc(tStr tInt,tMix),0); +  +  /* function(string,int,mixed:mixed) */ +  ADD_FUNCTION("set_array_item",perlmod_set_array_item, +  tFunc(tStr tInt tMix,tMix),0); +  +  /* function(string,mixed:mixed) */ +  ADD_FUNCTION("get_hash_item",perlmod_get_hash_item, +  tFunc(tStr tMix,tMix),0); +  +  /* function(string,mixed,mixed:mixed) */ +  ADD_FUNCTION("set_hash_item",perlmod_set_hash_item, +  tFunc(tStr tMix tMix,tMix),0); +     /* function(string:int) */ -  ADD_FUNCTION("eval",perlmod_eval,tFunc(tStr,tInt),0); -  /* function(string,mixed...:int) */ -  ADD_FUNCTION("call",perlmod_call,tFuncV(tStr,tMix,tInt),0); +  ADD_FUNCTION("array_size",perlmod_array_size, +  tFunc(tStr,tInt),0); +  +  /* function(string:int) */ +  ADD_FUNCTION("get_array",perlmod_get_whole_array, +  tFunc(tStr,tArr(tMix)),0); +  +  /* function(string:int) */ +  ADD_FUNCTION("get_hash_keys",perlmod_get_whole_array, +  tFunc(tStr,tArr(tMix)),0); +  + #if 0 +  /* function(string,array:array) */ +  ADD_FUNCTION("set_array", perlmod_set_whole_array, +  tFunc(tStr tArr(tMix),tArr(tMix)),0); + #endif +  +  /* function(void|int:int) */ +  ADD_FUNCTION("array_size_limit",perlmod_array_size_limit, +  tFunc(tOr(tVoid,tInt),tInt),0); +     set_init_callback(init_perl_glue);    set_exit_callback(exit_perl_glue);    end_class("Perl",0);