/* Copyright (C) 1992, 1993, 1994, 1996, 1997 artofcode LLC.  All rights reserved.
  
  This program is free software; you can redistribute it and/or modify it
  under the terms of the GNU General Public License as published by the
  Free Software Foundation; either version 2 of the License, or (at your
  option) any later version.

  This program is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  General Public License for more details.

  You should have received a copy of the GNU General Public License along
  with this program; if not, write to the Free Software Foundation, Inc.,
  59 Temple Place, Suite 330, Boston, MA, 02111-1307.

*/

/*$Id: ivmspace.h,v 1.2.6.1.2.1 2003/01/17 00:49:04 giles Exp $ */
/* Local/global space management */
/* Requires iref.h */

#ifndef ivmspace_INCLUDED
#  define ivmspace_INCLUDED

#include "gsgc.h"

/*
 * r_space_bits and r_space_shift, which define the bits in a ref
 * that carry VM space information, are defined in iref.h.
 * r_space_bits must be at least 2.
 */
#define a_space (((1 << r_space_bits) - 1) << r_space_shift)
/*
 * The i_vm_xxx values are defined in gsgc.h.
 */
typedef enum {
    avm_foreign = (i_vm_foreign << r_space_shift),
    avm_system = (i_vm_system << r_space_shift),
    avm_global = (i_vm_global << r_space_shift),
    avm_local = (i_vm_local << r_space_shift),
    avm_max = avm_local
} avm_space;

#define r_space(rp) (avm_space)(r_type_attrs(rp) & a_space)
#define r_space_index(rp) ((int)r_space(rp) >> r_space_shift)
#define r_set_space(rp,space) r_store_attrs(rp, a_space, (uint)space)

/*
 * According to the PostScript language specification, attempting to store
 * a reference to a local object into a global object must produce an
 * invalidaccess error.  However, systemdict must be able to refer to
 * a number of local dictionaries such as userdict and errordict.
 * Therefore, we implement a special hack in 'def' that allows such stores
 * if the dictionary being stored into is systemdict (which is normally
 * only writable during initialization) or a dictionary that appears
 * in systemdict (such as level2dict), and the current save level is zero
 * (to guarantee that we can't get dangling pointers).
 * We could allow this for any global dictionary, except that the garbage
 * collector must treat any such dictionaries as roots when collecting
 * local VM without collecting global VM.
 * We make a similar exception for .makeglobaloperator; this requires
 * treating the operator table as a GC root as well.
 *
 * We extend the local-into-global store check because we have four VM
 * spaces (local, global, system, and foreign), and we allow PostScript
 * programs to create objects in any of the first three.  If we define
 * the "generation" of an object as foreign = 0, system = 1, global = 2,
 * and local = 3, then a store is legal iff the generation of the object
 * into which a pointer is being stored is greater than or equal to
 * the generation of the object into which the store is occurring.
 *
 * We must check for local-into-global stores in three categories of places:
 *
 *      - The scanner, when it encounters a //name inside {}.
 *
 *      - All operators that allocate ref-containing objects and also
 *      store into them:
 *              packedarray  gstate  makepattern?
 *              makefont  scalefont  definefont  filter
 *
 *      - All operators that store refs into existing objects
 *      ("operators" marked with * are actually PostScript procedures):
 *              put(array)  putinterval(array)  astore  copy(to array)
 *              def  store*  put(dict)  copy(dict)
 *              dictstack  execstack  .make(global)operator
 *              currentgstate  defineusername
 */

/* Test whether an object is in local space, */
/* which implies that we need not check when storing into it. */
#define r_is_local(rp) (r_space(rp) == avm_local)
/* Test whether an object is foreign, i.e., outside known space. */
#define r_is_foreign(rp) (r_space(rp) == avm_foreign)
/* Check whether a store is allowed. */
#define store_check_space(destspace,rpnew)\
  if ( r_space(rpnew) > (destspace) )\
    return_error(e_invalidaccess)
#define store_check_dest(rpdest,rpnew)\
  store_check_space(r_space(rpdest), rpnew)
/* BACKWARD COMPATIBILITY (not used by any Ghostscript code per se) */
#define check_store_space(rdest,rnewcont)\
  store_check_dest(&(rdest),&(rnewcont))

#endif /* ivmspace_INCLUDED */
