/*
 * The builtin VM object.
 * Copyright (c) 1998-1999 New Generation Software (NGS) Oy
 *
 * Author: Markku Rossi <mtr@ngs.fi>
 */

/*
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA
 */

/*
 * $Source: /tmp/cvsroot-15-2-2007/clamav-devel/libclamav/js/b_vm.c,v $
 * $Id: b_vm.c,v 1.2 2006/10/28 11:27:44 njh Exp $
 */

/*
 * Class:
 *
 * - static methods:
 *
 *   VM.garbageCollect ()
 *   VM.stackTrace ()
 *
 * - properties:		type		mutable
 *
 *   VM.dispatchMethod		string
 *   VM.fdCount                 integer
 *   VM.gcCount			integer
 *   VM.gcTrigger		integer		yes
 *   VM.heapAllocated		integer
 *   VM.heapFree		integer
 *   VM.heapSize		integer
 *   VM.numConstants		integer
 *   VM.numGlobals		integer
 *   VM.stackSize		integer
 *   VM.stacktraceOnError	boolean		yes
 *   VM.verbose			integer 	yes
 *   VM.verboseStacktrace	boolean		yes
 *   VM.version			string
 *   VM.versionMajor		integer
 *   VM.versionMinor		integer
 *   VM.versionPatch		integer
 *   VM.warnUndef		boolean		yes
 */
#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif

#ifdef	CL_EXPERIMENTAL

#include "jsint.h"

/*
 * Types and definitions.
 */

struct vm_ctx_st
{
  JSSymbol s_garbageCollect;
  JSSymbol s_stackTrace;

  JSSymbol s_dispatchMethod;
  JSSymbol s_fdCount;
  JSSymbol s_gcCount;
  JSSymbol s_gcTrigger;
  JSSymbol s_heapAllocated;
  JSSymbol s_heapFree;
  JSSymbol s_heapSize;
  JSSymbol s_numConstants;
  JSSymbol s_numGlobals;
  JSSymbol s_stackSize;
  JSSymbol s_stacktraceOnError;
  JSSymbol s_verbose;
  JSSymbol s_verboseStacktrace;
  JSSymbol s_version;
  JSSymbol s_versionMajor;
  JSSymbol s_versionMinor;
  JSSymbol s_versionPatch;
  JSSymbol s_warnUndef;
};

typedef struct vm_ctx_st VMCtx;


/*
 * Static functions.
 */

/* Method proc */
static int
method (JSVirtualMachine *vm, JSBuiltinInfo *builtin_info,
	void *instance_context, JSSymbol method, JSNode *result_return,
	JSNode *args)
{
  VMCtx *ctx = builtin_info->obj_context;

  /* The default return value is undefined. */
  result_return->type = JS_UNDEFINED;

  if (method == ctx->s_garbageCollect)
    {
      if (args->u.vinteger != 0)
	goto argument_error;

      /* Ok, let's trigger a garbage collection. */
      vm->gc.bytes_allocated = vm->gc.trigger + 1;
    }
  /* ********************************************************************** */
  else if (method == ctx->s_stackTrace)
    {
      unsigned int limit;

      if (args->u.vinteger == 0)
	limit = -1;
      else if (args->u.vinteger == 1)
	{
	  if (args[1].type != JS_INTEGER)
	    goto argument_type_error;

	  limit = args[1].u.vinteger;
	}
      else
	goto argument_error;

      js_vm_stacktrace (vm, limit);
    }
  /* ********************************************************************** */
  else if (method == vm->syms.s_toString)
    {
      if (args->u.vinteger != 0)
	goto argument_error;

      js_vm_make_static_string (vm, result_return, "VM", 2);
    }
  /* ********************************************************************** */
  else
    return JS_PROPERTY_UNKNOWN;

  return JS_PROPERTY_FOUND;


  /*
   * Error handling.
   */

 argument_error:
  sprintf (vm->error, "VM.%s(): illegal amout of arguments",
	   js_vm_symname (vm, method));
  js_vm_error (vm);

 argument_type_error:
  sprintf (vm->error, "VM.%s(): illegal argument",
	   js_vm_symname (vm, method));
  js_vm_error (vm);

  /* NOTREACHED */
  return 0;
}

/* Property proc. */
static int
property (JSVirtualMachine *vm, JSBuiltinInfo *builtin_info,
	  void *instance_context, JSSymbol property, int set, JSNode *node)
{
  VMCtx *ctx = builtin_info->obj_context;

  if (property == ctx->s_dispatchMethod)
    {
      if (set)
	goto immutable;

      js_vm_make_static_string (vm, node,
				vm->dispatch_method_name,
				strlen (vm->dispatch_method_name));
    }
  /* ***************************************************************** */
  else if (property == ctx->s_fdCount)
    {
      if (set)
	goto immutable;

      node->type = JS_INTEGER;
      node->u.vinteger = vm->fd_count;
    }
  /* ***************************************************************** */
  else if (property == ctx->s_gcCount)
    {
      if (set)
	goto immutable;

      node->type = JS_INTEGER;
      node->u.vinteger = vm->gc.count;
    }
  /* ***************************************************************** */
  else if (property == ctx->s_gcTrigger)
    {
      if (set)
	{
	  if (node->type != JS_INTEGER)
	    goto value_error;
	  vm->gc.trigger = node->u.vinteger;
	}
      else
	{
	  node->type = JS_INTEGER;
	  node->u.vinteger = vm->gc.trigger;
	}
    }
  /* ***************************************************************** */
  else if (property == ctx->s_heapAllocated)
    {
      if (set)
	goto immutable;

      node->type = JS_INTEGER;
      node->u.vinteger = vm->gc.bytes_allocated;
    }
  /* ***************************************************************** */
  else if (property == ctx->s_heapFree)
    {
      if (set)
	goto immutable;

      node->type = JS_INTEGER;
      node->u.vinteger = vm->gc.bytes_free;
    }
  /* ***************************************************************** */
  else if (property == ctx->s_heapSize)
    {
      if (set)
	goto immutable;

      node->type = JS_INTEGER;
      node->u.vinteger = vm->heap_size;
    }
  /* ***************************************************************** */
  else if (property == ctx->s_numConstants)
    {
      if (set)
	goto immutable;

      node->type = JS_INTEGER;
      node->u.vinteger = vm->num_consts;
    }
  /* ***************************************************************** */
  else if (property == ctx->s_numGlobals)
    {
      if (set)
	goto immutable;

      node->type = JS_INTEGER;
      node->u.vinteger = vm->num_globals;
    }
  /* ***************************************************************** */
  else if (property == ctx->s_stackSize)
    {
      if (set)
	goto immutable;

      node->type = JS_INTEGER;
      node->u.vinteger = vm->stack_size;
    }
  /* ***************************************************************** */
  else if (property == ctx->s_stacktraceOnError)
    {
      if (set)
	{
	  if (node->type != JS_BOOLEAN)
	    goto value_error;
	  vm->stacktrace_on_error = node->u.vboolean;
	}
      else
	{
	  node->type = JS_BOOLEAN;
	  node->u.vboolean = vm->stacktrace_on_error;
	}
    }
  /* ***************************************************************** */
  else if (property == ctx->s_verbose)
    {
      if (set)
	{
	  if (node->type != JS_INTEGER)
	    goto value_error;
	  vm->verbose = node->u.vinteger;
	}
      else
	{
	  node->type = JS_INTEGER;
	  node->u.vinteger = vm->verbose;
	}
    }
  /* ***************************************************************** */
  else if (property == ctx->s_verboseStacktrace)
    {
      if (set)
	{
	  if (node->type != JS_BOOLEAN)
	    goto value_error;
	  vm->verbose_stacktrace = node->u.vboolean;
	}
      else
	{
	  node->type = JS_BOOLEAN;
	  node->u.vboolean = vm->verbose_stacktrace;
	}
    }
  /* ***************************************************************** */
  else if (property == ctx->s_version)
    {
      if (set)
	goto immutable;

      js_vm_make_static_string (vm, node, VERSION, strlen (VERSION));
    }
  /* ***************************************************************** */
  else if (property == ctx->s_versionMajor)
    {
      if (set)
	goto immutable;

      node->type = JS_INTEGER;
      node->u.vinteger = atoi (VERSION);
    }
  /* ***************************************************************** */
  else if (property == ctx->s_versionMinor)
    {
      if (set)
	goto immutable;

      node->type = JS_INTEGER;
      node->u.vinteger = atoi (VERSION + 2);
    }
  /* ***************************************************************** */
  else if (property == ctx->s_versionPatch)
    {
      if (set)
	goto immutable;

      node->type = JS_INTEGER;
      node->u.vinteger = atoi (VERSION + 4);
    }
  /* ***************************************************************** */
  else if (property == ctx->s_warnUndef)
    {
      if (set)
	{
	  if (node->type != JS_INTEGER)
	    goto value_error;
	  vm->warn_undef = node->u.vinteger != 0;
	}
      else
	{
	  node->type = JS_INTEGER;
	  node->u.vinteger = vm->warn_undef;
	}
    }
  /* ***************************************************************** */
  else
    {
      if (!set)
	node->type = JS_UNDEFINED;

      return JS_PROPERTY_UNKNOWN;
    }

  return JS_PROPERTY_FOUND;


  /*
   * Error handling.
   */

 value_error:
  sprintf (vm->error, "VM.%s: illegal value",
	   js_vm_symname (vm, property));
  js_vm_error (vm);

 immutable:
  sprintf (vm->error, "VM.%s: immutable property",
	   js_vm_symname (vm, property));
  js_vm_error (vm);

  /* NOTREACHED. */
  return 0;
}


/*
 * Global functions.
 */

void
js_builtin_VM (JSVirtualMachine *vm)
{
  JSNode *n;
  JSBuiltinInfo *info;
  VMCtx *ctx;

  ctx = js_calloc (vm, 1, sizeof (*ctx));

  ctx->s_garbageCollect		= js_vm_intern (vm, "garbageCollect");
  ctx->s_stackTrace		= js_vm_intern (vm, "stackTrace");

  ctx->s_dispatchMethod		= js_vm_intern (vm, "dispatchMethod");
  ctx->s_fdCount		= js_vm_intern (vm, "fdCount");
  ctx->s_gcCount		= js_vm_intern (vm, "gcCount");
  ctx->s_gcTrigger		= js_vm_intern (vm, "gcTrigger");
  ctx->s_heapAllocated		= js_vm_intern (vm, "heapAllocated");
  ctx->s_heapFree		= js_vm_intern (vm, "heapFree");
  ctx->s_heapSize		= js_vm_intern (vm, "heapSize");
  ctx->s_numConstants		= js_vm_intern (vm, "numConstants");
  ctx->s_numGlobals		= js_vm_intern (vm, "numGlobals");
  ctx->s_stackSize		= js_vm_intern (vm, "stackSize");
  ctx->s_stacktraceOnError	= js_vm_intern (vm, "stacktraceOnError");
  ctx->s_verbose		= js_vm_intern (vm, "verbose");
  ctx->s_verboseStacktrace	= js_vm_intern (vm, "verboseStacktrace");
  ctx->s_version		= js_vm_intern (vm, "version");
  ctx->s_versionMajor		= js_vm_intern (vm, "versionMajor");
  ctx->s_versionMinor		= js_vm_intern (vm, "versionMinor");
  ctx->s_versionPatch		= js_vm_intern (vm, "versionPatch");
  ctx->s_warnUndef		= js_vm_intern (vm, "warnUndef");

  /* Object information. */

  info = js_vm_builtin_info_create (vm);

  info->method_proc 		= method;
  info->property_proc 		= property;
  info->obj_context 		= ctx;
  info->obj_context_delete	= js_free;

  /* Define it. */
  n = &vm->globals[js_vm_intern (vm, "VM")];
  js_vm_builtin_create (vm, n, info, NULL);
}
#endif	/*CL_EXPERIMENTAL*/