2005/5/9

     
 

JavaInterpreter.cpp

artefaktur
// -*- mode:C++; tab-width:2; c-basic-offset:2; indent-tabs-mode:nil -*- 
//
// Copyright (C) 2000-2005 by Roger Rene Kommer / artefaktur, Kassel, Germany.
// 
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public License (LGPL).
// 
// 
// 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 
// License ACDK-FreeLicense document enclosed in the distribution
// for more for more details.
// This file is part of the Artefaktur Component Development Kit:
//                         ACDK
// 
// Please refer to
// - http://www.acdk.de
// - http://www.artefaktur.com
// - http://acdk.sourceforge.net
// for more information.
// 
// $Header: /cvsroot/acdk/acdk/acdk_java/src/acdk/java/JavaInterpreter.cpp,v 1.22 2005/04/20 09:13:54 kommer Exp $

#include <acdk.h>
#include <acdk/lang/System.h>
#include <acdk/io/File.h>
#include "JavaInterpreter.h"
#include "JavaObject.h"

namespace acdk {
namespace java {

using namespace acdk::lang;
using namespace acdk::io;

#define USER_CLASSPATH "."
#define PATH_SEPARATOR ';' 

//static 
JavaInterpreter* JavaInterpreter::curJInt = 0;

RJavaInterpreter 
getJavaInterpreter()
{
  if (JavaInterpreter::curJInt != 0)
    return JavaInterpreter::curJInt;

  static RJavaInterpreter tjavaint = new JavaInterpreter(); // was created outside
  return JavaInterpreter::curJInt;
}

JavaInterpreter::JavaInterpreter()
: acdk::lang::Object(),
  _env(0),
  _jvm(0)
{
#ifdef JNI_VERSION_1_2
  JavaVMInitArgs vm_args;
  JavaVMOption   options[3];

  RString acdkhome = System::getProperty("ACDKHOME");
  if (acdkhome != Nil)
  {
    RString str = "-Djava.class.path=" + acdkhome + ::acdk::io::File::separator() + "bin";
    options[0].optionString = strdup(str->c_str());
  } else {
    options[0].optionString = "";
  }
  
  //options[1].optionString = "-verbose:jni";
  vm_args.nOptions = 1;
  
  vm_args.version = JNI_VERSION_1_2; //JNI_VERSION_1_2; //0x00010002;
  vm_args.options = options;
  vm_args.ignoreUnrecognized = true;

  

/* Create the Java VM */
  JNIEnv* tjenv;
  int res = JNI_CreateJavaVM(&_jvm, (void**)&tjenv, &vm_args);
  _env = tjenv;
#else
/FONT>
    JDK1_1InitArgs vm_args;
    char classpath[1024];
    vm_args.version = 0x00010001;
    JNI_GetDefaultJavaVMInitArgs(&vm_args);
    /* Append USER_CLASSPATH to the default system class path */
    sprintf(classpath, "%s%c%s",
            vm_args.classpath, PATH_SEPARATOR, USER_CLASSPATH);
    vm_args.classpath = classpath;
/* Create the Java VM */
    int res = JNI_CreateJavaVM(&_jvm, (void**)&_env, &vm_args);
#endif
/FONT>
   if (res < 0) 
    THROW1(Exception, "Can't create Java VM");
   JavaInterpreter::curJInt = this;
}

//virtual 
JavaInterpreter::~JavaInterpreter()
{
  if (_jvm)
  {
    // this call blocks forever! 
    // _jvm->DestroyJavaVM();
  }
  _jvm = 0;
  JavaInterpreter::curJInt = 0;
}


RJavaObject 
JavaInterpreter::getInstance(IN(RString) classname, ScriptVarArray& args)
{
  return JavaObject::getInstance(this, classname, args);
}

void 
JavaInterpreter::attachCurrentThread()
{
#if defined(ACDK_OS_WIN32)
  _jvm->AttachCurrentThread((void**)&_env.get(), 0);
#endif
/FONT>
}

/*
jclass 
JavaInterpreter::getClassOfString()
{
  static jclass __classofstring = 0;
  if (__classofstring == 0) {
    jstring str = jenv()->NewStringUTF("");
    __classofstring = jenv()->GetObjectClass(str);

  }
  return __classofstring;
}
*/

#if 0
JNIEXPORT jint JNICALL
JNI_CreateJavaVM(JavaVM **vm, void **penv, void *args)
{
  ExecEnv *ee;
  
  /* 
  * At the moment it's only possible to have one Java VM, 
  * since some of the runtime state is in global variables. 
  */
  if (VM_created)
    return JNI_EEXIST;
  
  if (InitializeJavaVM(args) == SYS_OK) {
    ExecEnv *ee = EE();
    *vm = (JavaVM *)(&main_vm);
    *(JNIEnv**)penv = EE2JNIEnv(ee);
    
#ifdef HPROF
    if (JVMPI_EVENT_IS_ENABLED(JVMPI_EVENT_THREAD_START))
      jvmpi_thread_start(ee->thread);
#endif /* HPROF */
    
#ifdef BREAKPTS
    if (debugging) 
      notify_debugger_of_thread_start(ee, ee->thread);
#endif /* BREAKPTS */
    
    ee->stack_base = 0;
    
    VM_created = 1;
    return JNI_OK;
  } else {
    *vm = 0;
    *(JNIEnv**)penv = 0;
    return JNI_ERR;
  }
}


int InitializeJavaVM(void *args)
{
  ExecEnv *ee;
  sys_thread_t *sys_tid;
  HThread *self;
  jint version = *(jint*)args;
  jint result;
  props_md_t *sprops;
  
  /* Check if the host application provides an HPI implementation */
  if (version == JNI_VERSION_1_2) {
    JavaVMInitArgs *vm_args = args;
    int i;
    for (i = 0; i < vm_args->nOptions; i++) {
      JavaVMOption *option = vm_args->options + i;
      if (strcmp(option->optionString, "_hpi") == 0) {
        GetHPI = (GetInterfaceFunc)option->extraInfo;
      }
    }
  }
  
  if (GetHPI == NULL) {
    GetHPI = (GetInterfaceFunc)InitializeHPI(&callbacks);
  }
  
  if (GetHPI == NULL ||
    GetHPI((void **)&hpi_memory_interface, "Memory", 1) ||
    GetHPI((void **)&hpi_library_interface, "Library", 1) ||
    GetHPI((void **)&hpi_system_interface, "System", 1) ||
    GetHPI((void **)&hpi_thread_interface, "Thread", 1) ||
    GetHPI((void **)&hpi_file_interface, "File", 1)) {
    return JNI_ERR;
  }
  
  sprops = GetPropertiesMD();
  if (sprops == NULL) {
    jio_fprintf(stderr, "Can't obtain system-specific information\n");
    return JNI_ERR;
  }
  java_home_dir = sprops->java_home;
  java_dll_dir = sprops->dll_dir;
  init_sysclasspath = sprops->sysclasspath;
  
#if defined(DEBUG) && defined(CHECK_JNI)
  jni_NativeInterfacePtr = &checked_jni_NativeInterface;
#else
/FONT>
  jni_NativeInterfacePtr = &unchecked_jni_NativeInterface;
#endif /* DEBUG && CHECK_JNI */
  
  if (version == JNI_VERSION_1_1) {
    result = Initialize11((JDK1_1InitArgs *)args);
  } else if (version == JNI_VERSION_1_2) {
    result = Initialize12((JavaVMInitArgs *)args, TRUE);
  } else {
    jio_fprintf(stderr, "Unrecognized JNI version: 0x%lx\n", version);
    result = JNI_EVERSION;
  }
#ifdef BREAKPTS
  if (debugging) {
    jni_NativeInterfacePtr = jvmdi_jni_GetNativeInterface();
  }
#endif /* BREAKPTS */
  
  if (result < 0)
    return result;
  
#ifndef NO_HIDDEN_ENV
  {
    char *cmd_env = getenv("_JAVA_OPTIONS");
    if (cmd_env && *cmd_env) {
#define N_MAX_OPTIONS 32
      JavaVMInitArgs vm_args;
      JavaVMOption options[N_MAX_OPTIONS];
      int i;
      cmd_env = sysStrdup(cmd_env);
      if (cmd_env == NULL) {
        return JNI_ERR;
      }
      jio_fprintf(stderr, "Picked up _JAVA_OPTIONS: %s\n", cmd_env);
      for (i = 0; i < N_MAX_OPTIONS;) {
        options[i++].optionString = cmd_env;
        if (*cmd_env != '-') {
          jio_fprintf(stderr, "Bad _JAVA_OPTIONS: %s\n", cmd_env);
          return JNI_ERR;
        }
        while (*cmd_env && *cmd_env != ' ') {
          cmd_env++;
        }
        if (*cmd_env == 0) {
          break;
        }
        *cmd_env++ = 0;
        while (*cmd_env == ' ') {
          cmd_env++;
        }
      }
      vm_args.version = JNI_VERSION_1_2;
      vm_args.options = options;
      vm_args.nOptions = i;
      vm_args.ignoreUnrecognized = FALSE;
      if (Initialize12(&vm_args, FALSE) < 0) {
        return JNI_ERR;
      }
    }
  }
  
  {
    char *sizestr = getenv("_MIN_JAVASTACK_CHUNK_SIZE");
    if (sizestr) {
      min_javastack_chunk_size = atoi(sizestr);
      jio_fprintf(stderr, "Java stack chunks set to %d bytes.\n",
        min_javastack_chunk_size);
    }
  }
#endif /* NO_HIDDEN_ENV */
  
  /* Set the rest of callbacks slots to appropriate values */
#ifdef LOGGING
  sysSetLoggingLevel(logging_level);
#endif
/FONT>
  
  /* Initialize the monitor registry */
  monitorRegistryInit();
  
  if (sysThreadBootstrap(&sys_tid, &_queue_lock,
    offsetof(ExecEnv, sys_thr))) {
    jio_fprintf(stderr, "Can't bootstrap threads\n");
    return JNI_ERR;
  }
  
  ee = SysThread2EE(sys_tid);
  
  /* The setting of the stack base pointer is only temporarly.
  * JNI_CreateJavaVM will later set stack_base back to zero.
  * (Invocation API functions are resilient to stack location.)
  */
  if (!InitializeExecEnv(ee, 0, &args)) {
    return JNI_ENOMEM;
  }
  
  AdjustUserThreadCount(1);
  
  /* Create the monitor cache */
  monitorCacheInit();
  
#ifdef DEBUG
  pExecuteJava = ExecuteJava_C;
#else
/FONT>
  if (debugging) {
    pExecuteJava = ExecuteJava_C;
  } else {
    pExecuteJava = ExecuteJava;
  }
#endif /* DEBUG */
  
  if (dll_name) {
    if (!loadJVMHelperLib(dll_name, dll_options)) {
      return JNI_ERR;
    }
    
    sysFree(dll_name);
    sysFree(dll_options);
  }
  
  if (initHeapSize < MIN_HEAP_SIZE) {
    jio_fprintf(stderr, "The specified initial heap size is too small. "
		    "(%d bytes required.)\n", MIN_HEAP_SIZE);
    return JNI_EINVAL;
  }
  if (initHeapSize > maxHeapSize) {
    jio_fprintf(stderr,
      "Incompatible initial and maximum heap sizes specified:\n\n");
    jio_fprintf(stderr,
      "    initial size: %d bytes, maximum size: %d bytes\n\n",
      initHeapSize, maxHeapSize);
    jio_fprintf(stderr,
      "The initial heap size must be less than or equal to the maximum heap size.\n");
    jio_fprintf(stderr,
      "The default initial and maximum heap sizes are %d and %d bytes.\n",
      MINHEAPSIZE, MAXHEAPSIZE);
    return JNI_EINVAL;
  }
  
  if (InitializeAlloc(maxHeapSize, initHeapSize) != TRUE) {
    jio_fprintf(stderr, "Unable to initialize Java heap.\n");
    return JNI_ENOMEM;
  }
  
  UseLosslessQuickOpcodes = TRUE; 
  if (!InitializeInterpreter()) {
    return JNI_ENOMEM;
  }
  
  /* Check cache parameters */
  if (allocLocalSize >= allocCacheSize) {
    jio_fprintf(stderr,
      "Allocation cache size (%d) must be greater than "
      "local allocation size (%d)\n",
      allocCacheSize, allocLocalSize);
    jio_fprintf(stderr,
      "The default cache and local allocation sizes are "
      " %d bytes and %d bytes\n",
      ALLOC_CACHE_SIZE, ALLOC_LOCAL_SIZE);
    jio_fprintf(stderr,
      "Note: You can turn off local allocation via '-Xml0'\n.");
    return JNI_EINVAL;
  }
  
  if (!LoadJavaLibrary() || !LoadZipLibrary()) {
    return JNI_ERR;
  }
  
  /* Before this point we must not load any classes. Class loading
  * requires the Java and Zip libraries to be loaded.
  */
  
  /* Preallocate the internal exception objects */
  exceptionInit();
  
  self = InitializeClassThread(ee);
  if (self == 0) {
    if (exceptionOccurred(ee)) {
      goto error;
    }
    jio_fprintf(stderr, "Unable to initialize threads\n");
    return JNI_ERR;
  }
  
  InitializeSignals();
  
  if (TimeSlice) {
    sysAdjustTimeSlice(TimeSlice);
  }
  
  InitializeRefs();
  
  {
    ClassClass *cb = FindClass(ee, "java/lang/System", TRUE);
    if (exceptionOccurred(ee)) {
      goto error;
    }
    execute_java_static_method(ee, cb, "initializeSystemClass", "()V");
    if (exceptionOccurred(ee)) {
      goto error;
    }
  }
  
  if (!InitializeSystemClassLoader()) {
    goto error;
  }
  
  /* A temporary workaround to delay the loading of JIT to reduce
  * startup time. The JIT is loaded when the JNI FindClass function
  * is called.
  */
#ifdef DELAY_JIT_LOADING
#else
/FONT>
  FindClass(ee, JAVAPKG "Compiler", TRUE);
  if (!compilerInitialized) {
    /* Either the compiler couldn't be loaded, or it wasn't initialized */
    UseLosslessQuickOpcodes = FALSE;
  }
#endif
/FONT>
  
#ifdef BREAKPTS
  if (debugging) {
    /* So that field watchpoints can work */
    UseLosslessQuickOpcodes = TRUE;
  }
#endif /* BREAKPTS */
  
  if (debugging && debugPort >= 0) {
  /* JNI FindClass is base class loader aware. Since Agent class
	 * is in tools.jar, the Agent has to be found with JNI FindClass
    * as opposed to the VM's FindClass */
    JNIEnv *env = EE2JNIEnv(ee);
    ClassClass *agentcb = (ClassClass *)DeRef(env,
      (*env)->FindClass(env, "sun/tools/agent/Agent"));
    if (exceptionOccurred(ee)) {
      goto error;
    }
    execute_java_static_method(0, agentcb, "boot", "(I)V", debugPort);
    if (exceptionOccurred(ee)) {
      goto error;
    }
  }
  
#ifndef FAST_MONITOR
  /* Expand the monitor cache if the default was overridden */
  if (specified_monitor_cache_size - monCount > 0) {
    monitorCacheExpand(specified_monitor_cache_size - monCount);
  }
#endif/*FAST_MONITOR*/
/FONT>
  
#ifdef HPROF
  if (jvmpi_event_flags) {
    if (jvmpi_jvm_init_done() < 0) {
      jio_fprintf(stderr, "profiler error\n");
      return JNI_ERR;
    }
  }
#endif /* HPROF */
  
#ifdef BREAKPTS
  if (debugging) {
    notify_debugger_of_vm_init(ee);
  }
#endif /* BREAKPTS */
  
  return JNI_OK;
  
error:
  exceptionDescribe(ee);
  return JNI_ERR;
}

#endif //0
/*
void 
JavaInterpreter::parse(RStringArray args)
{
  jclass cls = _env->FindClass("Prog");
  jmethodID mid = _env->GetStaticMethodID(cls, "main", "([Ljava/lang/String;)V");
}
 
int 
JavaInterpreter::run()
{
  return 0;
}

int 
JavaInterpreter::interactive(RReader in, RWriter out, RWriter err)
{
  return 0;
}

void 
JavaInterpreter::eval(RString code)
{
}
 */

void
JavaInterpreter::callMain(IN(RString) clazz, IN(RStringArray) args)
{
  jclass cls = jenv()->FindClass(clazz->c_str());
  if (cls == 0) {
    System::err->println("Cannot find java class: " + clazz);
    return ;
  }
  jmethodID mid = jenv()->GetStaticMethodID(cls, "main", "([Ljava/lang/String;)V");
  if (mid == 0) {
    System::err->println("Cannot find method main in class " + clazz);
    return ;
  }
  jclass stringClass = jenv()->FindClass("java/lang/String");
  jobjectArray jargs = jenv()->NewObjectArray(args->length(), stringClass, 0);
  for (int i = 0; i < args->length(); i++) 
  {
    jstring jstr = jenv()->NewStringUTF(args[i]->c_str());
    jenv()->SetObjectArrayElement(jargs, i, jstr);
  }
  jenv()->CallStaticVoidMethod(cls, mid, jargs);
}
/*

acdk::lang::dmi::ScriptVar 
JavaInterpreter::call(RString clazz, RString func, acdk::lang::dmi::ScriptVarArray& args)
{
  acdk::lang::dmi::ScriptVar s;
  jclass cls = _env->FindClass(clazz->c_str());
  if (cls == 0) {
    System::err->println("Cannot find java class: " + clazz);
    return s;
  }
  jmethodID mid = _env->GetStaticMethodID(cls, "main", "([Ljava/lang/String;)V");
  if (mid == 0) {
    System::err->println("Cannot find method main in class " + clazz);
    return s;
  }
  
  jstring jstr = _env->NewStringUTF(" from ACDK!");

  jclass stringClass = _env->FindClass("java/lang/String");
  jobjectArray jargs = _env->NewObjectArray(1, stringClass, jstr);
  _env->CallStaticVoidMethod(cls, mid, jargs);
  return s;
}
*/
  

  
} //namespace java
} // namespace acdk