blob: 59fea15c8f329b00c4cb9b48ba24a8fd8ee75c4c [file] [log] [blame]
/* --COPYRIGHT--,EPL
* Copyright (c) 2008-2019 Texas Instruments Incorporated
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Texas Instruments - initial implementation
*
* --/COPYRIGHT--*/
/*
* ======== tcf.c ========
*
*! Revision History
*! ================
*! 05-Feb-2003 jgc: Return NULL from mkArgs if file INITSCRIPT
*! is not found. DDTS SDSsq26184.
*! 30-Nov-2001 jgc: Added linux jvm path to JVMNAMES
*/
#include <xdc/std.h>
#define GT_TRACE 1
#include <xdc/services/host/lib/gt.h>
#include <xdc/services/host/lib/gs.h>
#include <xdc/services/host/lib/gld.h>
#if defined(xdc_target__os_Windows)
#include <windows.h>
#include <io.h>
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#endif
#include "tcf.h"
#include <jni.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#define MAXOPTIONS 64 /* max options for JVM */
#define MAXNAME 512 /* maximum file name length */
#define MAXCMD 1024 /* max cmd displayed for -n option */
#define MAXPATH (3 * MAXNAME + 3) /* enough for 3 names */
#define MAXOPTLEN (MAXPATH + 128) /* enough for 1 path */
#define JSSHELL "config/Shell"
#define JSDEBUG "config/ShellGUIDebugger"
#define INITSCRIPT "tconfini.tcf"
#define XDCPATHOPT "-DXDCPATH="
#define XDCPATHOPTLEN (sizeof(XDCPATHOPT) - 1)
#if defined(xdc_target__os_Windows)
#define DIRSTR "\\"
#define FILSTR ";"
#define JRE "jre" DIRSTR
#define CALLBACK __stdcall
#define EXHOME "'C:\\Program Files\\Java\\jre-1.8.0_66'."
/* 64-bit installations don't separate 'client' and 'server' anymore. */
static String JVMNAMES[] = {
JRE "bin\\server\\jvm.dll",
JRE "bin\\client\\jvm.dll"
};
#else
#include <unistd.h>
#define DIRSTR "/"
#define FILSTR ":"
#define JRE "jre" DIRSTR
#define CALLBACK
#if defined(xdc_target__os_MacOS)
#define EXHOME "'/Library/Java/JavaVirtualMachines/jdk-1.8.0_66.jdk/Contents/Home'."
static String JVMNAMES[] = {
JRE "lib/server/libjvm.dylib"
};
//static String JVMNAMES[] = {
// JRE "lib/jli/libjli.dylib"
//};
#else
#define EXHOME "'/usr/lib/jvm/java-8-openjdk-amd64'."
static String JVMNAMES[] = {
JRE "lib/amd64/server/libjvm.so", /* Oracle's JRE 1.8 */
JRE "lib/server/libjvm.so", /* Oracle's JRE 9 */
JRE "lib/amd64/classic/libjvm.so", /* IBM's JRE, or /j9vm/ or /default/ */
JRE "lib/arm/jamvm/libjvm.so", /* or /cacao/ or /server/ */
JRE "lib/arm/server/libjvm.so" /* or /jamvm/ or /cacao/ */
};
#endif
#endif
#define ENOHOME "can't find a JVM; try setting the environment\
variable 'XDCTOOLS_JAVA_HOME' to the absolute path of any directory\
containing a 64-bit Java Runtime Environment (1.8.0 or greater); e.g., " EXHOME
#define EBADHOME "can't find a JVM; the environment variable\
'XDCTOOLS_JAVA_HOME' is set, but does not appear to be a directory\
containing a 64-bit Java Runtime Environment (1.8.0 or greater); e.g., " EXHOME
typedef struct TCF_Obj {
JavaVM *jvm; /* JVM interface pointer */
jmethodID pmid; /* cached Shell.processLine method id */
jmethodID gmid; /* cached Shell.getOutput method id */
jmethodID emid; /* cached Shell.exit method id */
String *pathTab; /* table of paths to locate jars, dlls, ... */
Ptr jvmLib; /* handle to loaded JVM DLL */
Int sesCount; /* number of active sessions */
Char cmdBuf[MAXCMD + 1]; /* command buffer for nflag support */
Char *cmd; /* current pointer into cmdBuf */
String xdcPathOption; /* NULL or "-DXDCPATH=..." */
Bool gflag; /* debug flag */
Bool nflag; /* no-execute flag */
Bool sflag; /* suppress startup file flag */
String gopts; /* debug options */
} TCF_Obj;
/*
* ======== TCF_SesObj ========
* Each session encapsulates a client's configuration; separate sessions
* allow multiple concurrent configuration clients.
*/
typedef struct TCF_SesObj {
TCF_Handle tcf; /* containing TCF interpreter; i.e., JVM */
JNIEnv *env; /* current thread environment */
jobject shell; /* Shell object handle */
jmethodID pmid; /* cached processLine method id */
jmethodID gmid; /* cached getOutput method id */
jmethodID emid; /* cached Shell.exit method id */
} TCF_SesObj;
/*
* ======== VMFxn ========
* Java virtual machine create function
*/
typedef jint (*VMFxn)(JavaVM **, void **, void *);
static String defaultPathTab[] = { ".", NULL };
static String defaultJarTab[] = {
"js.jar", /* rhino JS engine */
"config.jar", /* TCOM model */
NULL
};
static String defaultJvmOpts[] = { NULL };
/*
* ======== TCF_ATTRS ========
* Default attributes for TCF object.
*/
TCF_Attrs TCF_ATTRS = {
defaultPathTab, /* search path for various files */
defaultJarTab, /* jar list */
defaultJvmOpts, /* JVM option string table */
".", /* root configuration directory */
NULL, /* class path override */
NULL, /* exit hook function */
NULL, /* abort hook function */
FALSE, /* debug flag */
FALSE, /* no-execute flag */
FALSE, /* suppress startup file flag */
"", /* no debug options */
};
static GT_Mask curTrace; /* trace mask */
static Int curInit = 0; /* TCF_init()/exit() counter */
static Bool appendArgs(TCF_Handle tcf,
jobjectArray args, int start, int argc, String argv[]);
static Void appendCmd(TCF_Handle tcf, String format, ...);
static JavaVM *createVM(TCF_Handle tcf, String jvmPath,JavaVMOption options[]);
static String findFile(String file, String pathTab[]);
static Bool getClassPath(Char *buf, String jars[], String path[], String cp);
static JNIEnv *getEnv(TCF_Handle tcf);
static String getFilePath(String parent, String suffix);
static VMFxn /*@null@*/ getVMCreateFxn(TCF_Handle tcf, String jvmPath);
static Bool getXDCPath(String prefix, String *xdcPath);
static Bool isFile(String name);
static Int CALLBACK jvmVfprintf(FILE *stream, const char *format, va_list ap);
static jobjectArray mkArgs(TCF_Handle tcf, int argc, char *argv[]);
static jobject mkShell(TCF_Handle tcf, String shell, Int argc, String argv[]);
static Int process(TCF_Session ses, String line);
static Int runJS(TCF_Handle tcf, Int argc, String argv[]);
static Void setPath(String oldPath);
/*
* ======== TCF_create ========
*/
TCF_Handle TCF_create(String name, TCF_Attrs *attrs, String *error)
{
Char classBuf[MAXPATH + 1];
String classPath = classBuf;
Char rootDirOption[MAXOPTLEN + 1]; /* -Dconfig.rootDir=... */
Char debugOption[MAXOPTLEN + 1]; /* -Dconfig.gopts=... */
Char classOption[MAXOPTLEN + 1]; /* -Djava.class.path=... */
JavaVMOption jvmOptions[MAXOPTIONS + 1]; /* +1 for NULL termination */
Int nOpts;
Int jreNameOffset;
String dummy;
Int i;
String tmp;
TCF_Handle tcf;
TCF_Attrs localAttrs;
Bool xdcPathIsSet = FALSE; /* allow jvmOpts to override XDCPATH */
String home;
GT_3trace(curTrace, GT_ENTER, "TCF_create(%s, 0x%lx, 0x%lx)\n",
name == NULL ? "''" : name, attrs, error);
/* create a local copy of the attrs structure so that we can modify it */
localAttrs = (attrs == NULL ? TCF_ATTRS : *attrs);
attrs = &localAttrs;
/* make all NULL pointers non-NULL */
if (error == NULL) {
error = &dummy;
}
*error = NULL;
if (name == NULL) {
name = "";
}
if (attrs->rootDir == NULL) {
attrs->rootDir = ".";
}
nOpts = 0;
if (attrs->jvmOpts != NULL) {
for (i = 0; attrs->jvmOpts[i] != NULL && i < MAXOPTIONS; i++) {
if (!strncmp(XDCPATHOPT, attrs->jvmOpts[i], XDCPATHOPTLEN)) {
xdcPathIsSet = TRUE;
}
jvmOptions[nOpts++].optionString = attrs->jvmOpts[i];
}
}
if ((nOpts + 11) > MAXOPTIONS) { /* 11 more options to add below */
*error = "too many options for JVM";
return (NULL);
}
if ((tcf = (TCF_Obj *)GS_calloc(sizeof(TCF_Obj))) == NULL) {
*error = "out of memory";
return (NULL);
}
tcf->sesCount = 0;
tcf->pathTab = attrs->pathTab;
tcf->xdcPathOption = NULL;
tcf->nflag = attrs->nflag;
tcf->sflag = attrs->sflag;
tcf->gflag = attrs->gflag;
tcf->gopts = attrs->gopts == NULL ? "" : attrs->gopts;
if ((strlen(name) > 0) && !isFile(name)) {
*error = "specified script is not a file";
return (NULL);
}
tcf->cmd = tcf->cmdBuf;
tcf->jvmLib = NULL;
if (tcf->nflag == TRUE) {
GT_set("TC+7");
}
/* if '-g' is specified *and* jsd.jar exists, use it in lieu of js.jar */
if (attrs->gflag && findFile("jsd.jar", attrs->pathTab) != NULL) {
for (i = 0; attrs->jarTab[i] != NULL; i++) {
if (strcmp(attrs->jarTab[i], "js.jar") == 0) {
attrs->jarTab[i] = "jsd.jar";
}
}
}
/* compute initial class path */
getClassPath(classBuf, attrs->jarTab, attrs->pathTab, attrs->classPath);
/* set class path and library for JVM */
sprintf(classOption, "-Djava.class.path=%s", classPath);
jvmOptions[nOpts++].optionString = classOption;
/* pass rootDir to scripts via environment */
sprintf(rootDirOption, "-Dconfig.rootDir=%s", attrs->rootDir);
jvmOptions[nOpts++].optionString = rootDirOption;
/* pass XDCPATH via environment */
if (!xdcPathIsSet && getXDCPath(XDCPATHOPT, &tcf->xdcPathOption)) {
jvmOptions[nOpts++].optionString = tcf->xdcPathOption;
}
/* pass gopts via environment */
if (strlen(tcf->gopts) < (MAXOPTLEN - 32)) {
sprintf(debugOption, "-Dconfig.gopts=%s", tcf->gopts);
jvmOptions[nOpts++].optionString = debugOption;
}
/* set vfprintf output hook for JVM */
jvmOptions[nOpts].optionString = "vfprintf";
jvmOptions[nOpts++].extraInfo = jvmVfprintf;
/* set exit and abort hooks for JVM */
if (attrs->jvmExit != NULL) {
jvmOptions[nOpts].optionString = "exit";
jvmOptions[nOpts++].extraInfo = attrs->jvmExit;
}
if (attrs->jvmAbort != NULL) {
jvmOptions[nOpts].optionString = "abort";
jvmOptions[nOpts++].extraInfo = attrs->jvmAbort;
}
/* NULL terminate JVM options array */
jvmOptions[nOpts].optionString = NULL;
GT_assert(curTrace, nOpts <= MAXOPTIONS);
/* To locate java command (for java command line equivalent computation),
* first check for it in 'jre' subdirectory because that's where the
* XDCtools product keeps it. Then, check the path in a JRE installation
* without 'jre' subdirectory.
*/
if ((tmp = findFile(JRE "bin" DIRSTR "java.exe", attrs->pathTab)) == NULL
&& (tmp = findFile(JRE "bin" DIRSTR "java", attrs->pathTab)) == NULL
&& (tmp = findFile("bin" DIRSTR "java.exe", attrs->pathTab)) == NULL
&& (tmp = findFile("bin" DIRSTR "java", attrs->pathTab)) == NULL) {
tmp = "java";
}
appendCmd(tcf, "%s ", tmp);
/* For each member of JVMNAMES, we look for its full path first, and then
* for the path without 'jre' component.
*/
jreNameOffset = 4;
/*
* Locate JVM DLL
*
* If it's defined, add "$XDCTOOLS_JAVA_HOME" to the end of the path.
* This allows xs to be used in distributions that don't include a JRE;
* the user must define XDCTOOLS_JAVA_HOME.
*/
home = getenv("XDCTOOLS_JAVA_HOME");
GT_1trace(curTrace, GT_ENTER, "XDCTOOLS_JAVA_HOME = %s\n", home == NULL ? "null" : home);
for (i = 0; i < (Int)(sizeof (JVMNAMES) / sizeof (String)); i++) {
String suffix = JVMNAMES[i];
if ((tmp = findFile(suffix, attrs->pathTab)) != NULL
|| (home != NULL && (tmp = getFilePath(home, suffix)) != NULL)) {
break;
}
}
if (tmp == NULL) {
for (i = 0; i < (Int)(sizeof (JVMNAMES) / sizeof (String)); i++) {
String suffix = &(JVMNAMES[i][jreNameOffset]);
if ((tmp = findFile(suffix, attrs->pathTab)) != NULL
|| (home != NULL
&& (tmp = getFilePath(home, suffix)) != NULL)) {
break;
}
}
if (tmp == NULL) {
*error = (home == NULL) ? ENOHOME : EBADHOME;
TCF_delete(tcf);
return (NULL);
}
}
/* create JVM */
if ((tcf->jvm = createVM(tcf, tmp, jvmOptions)) == NULL) {
*error = "create of JVM failed";
TCF_delete(tcf);
return (NULL);
}
return (tcf);
}
/*
* ======== TCF_delete ========
*/
Void TCF_delete(TCF_Handle tcf)
{
GT_1trace(curTrace, GT_ENTER, "TCF_delete(0x%lx)\n", tcf);
if (tcf != NULL) {
if (tcf->xdcPathOption != NULL) {
GS_free(tcf->xdcPathOption);
}
if (tcf->jvm != NULL) {
/*
* Detach the current thread so that it appears to have exited
* when the application's main method exits.
*/
if ((*tcf->jvm)->DestroyJavaVM(tcf->jvm) != 0) {
(void)(*tcf->jvm)->DetachCurrentThread(tcf->jvm);
GT_1trace(curTrace, GT_1CLASS,
"TCF_delete: Could not destroy or detach 0x%lx.\n", tcf);
}
tcf->jvm = NULL;
}
if (tcf->jvmLib != NULL) {
GLD_delete(tcf->jvmLib);
tcf->jvmLib = NULL;
}
GS_free(tcf);
}
}
/*
* ======== TCF_exit ========
*/
Void TCF_exit(Void)
{
GT_1trace(curTrace, GT_ENTER, "TCF_exit(): (init count = %d)\n", curInit);
if (--curInit == 0) {
GS_exit();
GT_exit();
GLD_exit();
}
}
/*
* ======== TCF_getOutput ========
*/
Int TCF_getOutput(TCF_Session ses, TCF_Stream s, Char *buf, Int buflen)
{
jobject jstr;
jint sid;
Int i;
SizeT len;
GT_4trace(curTrace, GT_ENTER,
"TCF_getOutput(0x%lx, %d, 0x%lx, 0x%lx)\n", ses, s, buf, buflen);
if (buf == NULL || buflen <= 1) {
return (0);
}
/* initialize output counters */
i = 0; /* index of next output character */
len = 0; /* length for message to output */
/* call getOutput to get the Java String containing any message */
sid = (jint)s;
jstr = (*ses->env)->CallObjectMethod(ses->env, ses->shell, ses->gmid, sid, buflen - 1);
if (jstr != NULL) {
const char *msg;
msg = (*ses->env)->GetStringUTFChars(ses->env, jstr, NULL);
if (msg != NULL) {
len = strlen(msg);
for (i = 0; i < buflen && i < len; i++) {
buf[i] = msg[i];
}
if (i == buflen) {
GT_assert(curTrace, 0 == 1); /* should never get here */
i--; /* but just in case ... */
}
(*ses->env)->ReleaseStringUTFChars(ses->env, jstr, msg);
}
/* delete the reference to the Java String */
(*ses->env)->DeleteLocalRef(ses->env, jstr);
}
/* handle any exceptions by displaying to stderr and clear it */
if ((*ses->env)->ExceptionOccurred(ses->env)) {
(*ses->env)->ExceptionDescribe(ses->env);
}
buf[i] = '\0'; /* NULL terminate the output string */
return ((Int)len); /* return the length of the message */
}
/*
* ======== TCF_init ========
*/
Void TCF_init(Void)
{
if (curInit++ == 0) {
GS_init();
GT_init();
GLD_init();
GT_create(&curTrace, "TC");
}
GT_1trace(curTrace, GT_ENTER, "TCF_init(): %d\n", curInit);
}
/*
* ======== TCF_process ========
*/
Int TCF_process(TCF_Session ses, String command)
{
Int status = TCF_EINTERN;
JNIEnv *env = getEnv(ses->tcf);
GT_2trace(curTrace, GT_ENTER, "TCF_process(0x%lx, 0x%lx)\n", ses, command);
if (env != NULL) {
status = process(ses, command);
/* handle any exceptions by displaying to stderr and clear it */
if ((*env)->ExceptionOccurred(env)) {
(*env)->ExceptionDescribe(env);
if (status == TCF_EOK) {
status = TCF_EFAIL;
}
}
}
return (status);
}
/*
* ======== TCF_run ========
*/
Int TCF_run(TCF_Handle tcf, Int argc, String argv[])
{
Int status = TCF_EFAIL;
String emptyArgs[] = {NULL};
JNIEnv *env = getEnv(tcf);
if (argv == NULL) {
argc = 0;
argv = emptyArgs;
}
GT_3trace(curTrace, GT_ENTER, "TCF_run(0x%lx, 0x%lx, 0x%lx)\n", tcf,
argc, argv);
if (env != NULL) {
status = runJS(tcf, argc, argv);
/* handle any exceptions */
if ((*env)->ExceptionOccurred(env)) {
(*env)->ExceptionDescribe(env);
if (status == TCF_EOK) {
status = TCF_EFAIL;
}
}
}
tcf->cmd = tcf->cmdBuf;
tcf->cmd[0] = '\0';
return (status);
}
/*
* ======== TCF_start ========
*/
TCF_Session TCF_start(TCF_Handle tcf, Int argc, String argv[], String *error)
{
TCF_Session ses;
String dummy;
GT_4trace(curTrace, GT_ENTER, "TCF_start(0x%lx, 0x%lx, 0x%lx, 0x%lx)\n",
tcf, argc, argv, error);
/* ensure that error is never NULL */
if (error == NULL) {
error = &dummy;
}
*error = NULL; /* initialize error to indicate no error */
if ((ses = (TCF_SesObj *)GS_calloc(sizeof(TCF_SesObj))) == NULL) {
*error = "out of memory";
return (NULL);
}
ses->tcf = tcf;
tcf->sesCount++;
if ((*tcf->jvm)->AttachCurrentThread(tcf->jvm, (void **)&ses->env, NULL) != 0) {
*error = "can't attach to the current thread";
ses->env = NULL;
TCF_stop(ses);
return (NULL);
}
if ((ses->shell = mkShell(tcf, JSSHELL, argc, argv)) == NULL) {
/* handle any exceptions by displaying to stderr and clear it */
if ((*ses->env)->ExceptionOccurred(ses->env)) {
(*ses->env)->ExceptionDescribe(ses->env);
*error = "exception occured";
}
TCF_stop(ses);
if (*error == NULL) {
*error = "can't create configuration shell";
}
return (NULL);
}
ses->pmid = tcf->pmid;
ses->gmid = tcf->gmid;
ses->emid = tcf->emid;
return (ses);
}
/*
* ======== TCF_stop ========
*/
Void TCF_stop(TCF_Session ses)
{
GT_1trace(curTrace, GT_ENTER, "TCF_stop(0x%lx)\n", ses);
if (ses != NULL) {
ses->tcf->sesCount--;
/* delete reference to shell object */
if (ses->shell != NULL) {
/* exit the shell */
(*ses->env)->CallVoidMethod(ses->env, ses->shell, ses->emid);
/* handle any exceptions by displaying to stderr and clear it */
if ((*ses->env)->ExceptionOccurred(ses->env)) {
(*ses->env)->ExceptionDescribe(ses->env);
GT_assert(curTrace, 0 == 1); /* this should never occur */
}
/* remove our reference to the shell object */
(*ses->env)->DeleteLocalRef(ses->env, ses->shell);
ses->shell = NULL;
}
/*
* Warning: Detaching the current thread also releases
* local references. If a single thread has two sessions
* and stops one, the references to the second are released!
* Thus, further access to the second session will likely
* result in a corruption of the JVM.
*
* We delay detaching until all session objects are stopped
* (via sesCount). A better solution would be to count the
* number of sessions attached to the current thread; this
* would enable earlier cleanup of any local references.
*/
if (ses->env != NULL && ses->tcf->sesCount == 0) {
(*ses->tcf->jvm)->DetachCurrentThread(ses->tcf->jvm);
ses->env = NULL;
}
GS_free(ses);
}
}
/*
* ======== appendArgs ========
* Create Java strings for each of the arguments in argv[] and put
* them into the Java array args starting from item start.
*/
static Bool appendArgs(TCF_Handle tcf,
jobjectArray args, int start, int argc, String argv[])
{
JNIEnv *env;
jstring jstr;
int i;
/* get the environment pointer associated with this thread */
if ((env = getEnv(tcf)) == NULL) {
return (FALSE);
}
/* create Java strings and save reference in args array */
for (i = 0; i < argc; i++) {
/* create a Java String for each argv[] string */
if ((jstr = (*env)->NewStringUTF(env, argv[i])) == 0) {
GT_0trace(curTrace, GT_1CLASS, "Can't create Java String\n");
return (FALSE);
}
appendCmd(tcf, "%s ", argv[i]);
GT_2trace(curTrace, GT_1CLASS, "JVM arg[%d] = %s\n",
i + start, argv[i]);
/* assign new String to array slot */
(*env)->SetObjectArrayElement(env, args, i + start, jstr);
(*env)->DeleteLocalRef(env, jstr);
}
return (TRUE);
}
/*
* ======== appendCmd ========
*/
static Void appendCmd(TCF_Handle tcf, String format, ...)
{
va_list va;
uintptr_t count;
va_start(va, format);
if (tcf->nflag) {
if (format != NULL && format[0] != '\0') {
if ((count = MAXCMD - (tcf->cmd - tcf->cmdBuf)) > 0) {
Char tmpBuf[MAXCMD + 1];
Int tmp = vsprintf(tmpBuf, format, va);
if (count >= tmp) {
strcat(tcf->cmd, tmpBuf);
tcf->cmd += tmp;
}
GT_assert(curTrace, (tmp >= 0 && tmp <= count));
}
}
if (format == NULL) { /* end-of-stream flag */
tcf->cmdBuf[MAXCMD] = '\0'; /* force NULL termination */
}
}
va_end(va);
}
/*
* ======== createVM ========
*/
static JavaVM *createVM(TCF_Handle tcf, String jvmPath, JavaVMOption options[])
{
jint res;
JavaVM *jvm;
JNIEnv *env;
int i;
JavaVMInitArgs vm_args;
VMFxn newVMFxn;
GT_3trace(curTrace, GT_ENTER, "createVM(0x%lx, %s, 0x%lx)\n",
tcf, jvmPath, options);
/* count number of options in options array */
for (i = 0; i < MAXOPTIONS; i++) {
if (options[i].optionString == NULL) {
break;
}
else {
/*printf("Option %d is %s, extra %x\n", i, options[i].optionString,
options[i].extraInfo);*/
}
}
memset(&vm_args, 0, sizeof(vm_args));
vm_args.version = JNI_VERSION_1_2;
vm_args.options = options;
vm_args.nOptions = i;
vm_args.ignoreUnrecognized = JNI_TRUE;
for (i = 0; i < (Int)vm_args.nOptions; i++) {
if (options[i].optionString[0] == '-') { /* ignore exit/abort opts */
appendCmd(tcf, "%s ", options[i].optionString);
}
GT_2trace(curTrace, GT_1CLASS, "JVM option[%d] = %s\n",
i, options[i].optionString);
}
/* Get fxn to create the Java VM */
if ((newVMFxn = getVMCreateFxn(tcf, jvmPath)) == NULL) {
return (NULL);
}
/* Create the Java VM */
if ((res = newVMFxn(&jvm, (void **)&env, &vm_args)) < 0) {
GT_1trace(curTrace, GT_1CLASS, "Can't create Java VM (status = %d)\n",
res);
return (NULL);
}
return (jvm);
}
/*
* ======== findFile ========
*/
static String findFile(String file, String pathTab[])
{
Int i;
String name;
GT_2trace(curTrace, GT_ENTER, "findFile(%s, 0x%lx)\n", file, pathTab);
/* if file is a full path name, test it unadorned */
if (file[0] == DIRSTR[0] || file[1] == ':') {
if ((name = getFilePath(NULL, file)) != NULL) {
return (name);
}
}
/* otherwise, try every prefix in pathTab */
for (i = 0; pathTab[i] != NULL; i++) {
if ((name = getFilePath(pathTab[i], file)) != NULL) {
return (name);
}
}
GT_1trace(curTrace, GT_1CLASS, " file '%s' not found.\n", file);
return (NULL);
}
/*
* ======== getFilePath ========
*/
static String getFilePath(String parent, String suffix)
{
static Char nameBuf[MAXNAME + 1];
if (parent == NULL) {
strcpy(nameBuf, suffix);
}
else if ((strlen(parent) + strlen(suffix) + 2) <= MAXNAME) {
sprintf(nameBuf, "%s%s%s", parent, DIRSTR, suffix);
}
else {
return (NULL);
}
GT_1trace(curTrace, GT_1CLASS, " testing %s ...\n", nameBuf);
if (isFile(nameBuf)) {
GT_1trace(curTrace, GT_1CLASS, " found %s\n", nameBuf);
return (nameBuf);
}
return (NULL);
}
/*
* ======== getClassPath ========
* Set Java's class path as follows:
* location of jarTab[0]; location of jarTab[1], ...; classPath
*
* Note: if the specified jar file can not be found, its path is *not*
* added to the class path and no error is reported.
*/
static Bool getClassPath(Char *classBuf, String jarTab[], String pathTab[],
String classPath)
{
String tmp;
String *namePtr;
GT_3trace(curTrace, GT_ENTER, "getClassPath(0x%lx, 0x%lx, 0x%lx)\n",
classBuf, jarTab, pathTab);
classBuf[0] = '\0';
for (namePtr = jarTab; *namePtr != NULL; namePtr++) {
if ((tmp = findFile(*namePtr, pathTab)) != NULL) {
if ((strlen(classBuf) + strlen(tmp) + 1) >= MAXPATH) {
GT_0trace(curTrace, GT_6CLASS,
"truncating CLASSPATH: not enough space\n");
return (FALSE);
}
strcat(classBuf, tmp);
strcat(classBuf, FILSTR);
}
}
if (classPath != NULL) {
if ((Int)(strlen(classBuf) + strlen(classPath) + 1) >= MAXPATH) {
GT_0trace(curTrace, GT_6CLASS,
"truncating CLASSPATH: not enough space\n");
return (FALSE);
}
strcat(classBuf, classPath);
}
GT_1trace(curTrace, GT_1CLASS, "default class path = %s\n", classBuf);
return (TRUE);
}
/*
* ======== getEnv ========
*/
static JNIEnv *getEnv(TCF_Handle tcf)
{
JNIEnv *env;
if ((*tcf->jvm)->GetEnv(tcf->jvm, (void **)&env, JNI_VERSION_1_2) != JNI_OK) {
GT_0trace(curTrace, GT_1CLASS, "Thread not attached to JVM\n");
return (NULL);
}
return (env);
}
/*
* ======== getVMCreateFxn ========
*/
static VMFxn getVMCreateFxn(TCF_Handle tcf, String jvmPath)
{
VMFxn newVMFxn;
String savedPath = NULL;
#if defined(xdc_target__os_Windows)
String newPath = NULL;
uintptr_t pathLen;
#endif
GT_2trace(curTrace, GT_ENTER, "getVMCreateFxn(0x%lx, %s)\n", tcf, jvmPath);
#if defined(xdc_target__os_Windows)
/* We need to add jre/bin to PATH so that msvcr*.dll can be found there
* because in Windows7 the system directory does not always contain C
* runtime DLLs.
*
* If for any reason, we can't read PATH, we'll just continue silently.
*/
/* Check how much space we need for old PATH or if PATH is defined */
pathLen = GetEnvironmentVariable("PATH", NULL, 0);
if (pathLen != 0 || GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
uintptr_t jrebinLen = 0; /* length of the JRE path prefix to jre/bin */
uintptr_t newPathLen; /* length of path with appended JRE path */
Char *cp;
/* locate last "bin/" in jvmPath (which should have msvc*.dll) */
for (cp = jvmPath; (cp = strstr(cp, "\\bin\\")) != NULL;) {
cp += 4;
jrebinLen = cp - jvmPath;
}
/* if we found a last /bin in jvmPath, add it to the PATH */
if (jrebinLen != 0) {
newPathLen = pathLen + jrebinLen + 1; /* add 1 for ';' */
if ((newPath = (String)GS_alloc((UInt)newPathLen + 1)) != NULL) {
Char *pnt;
memcpy(newPath, jvmPath, jrebinLen);
pnt = newPath + jrebinLen;
if (pathLen == 0) {
*pnt = '\0';
SetEnvironmentVariable("PATH", newPath);
}
else if ((savedPath = (String)GS_alloc((UInt)pathLen + 1))
!= NULL) {
pathLen = GetEnvironmentVariable("PATH", savedPath,
(UInt32)pathLen + 1);
if (pathLen != 0) {
*(pnt++) = ';';
memcpy(pnt, savedPath, pathLen);
pnt[pathLen] = '\0';
SetEnvironmentVariable("PATH", newPath);
}
}
GS_free(newPath);
}
}
}
#endif
/* load the JVM */
tcf->jvmLib = GLD_create(jvmPath);
/* restore the Windows path
*
* If savedPath is NULL, PATH is removed from the environment, which is
* exactly what we want since savedPath is NULL only when PATH wasn't in
* the environment initially.
*/
setPath(savedPath);
if (savedPath != NULL) {
GS_free(savedPath);
}
if (tcf->jvmLib == NULL) {
GT_0trace(curTrace, GT_1CLASS, "Can't load Java VM dll\n");
return (NULL);
}
newVMFxn = (VMFxn)GLD_getFxn(tcf->jvmLib, "JNI_CreateJavaVM");
if (newVMFxn == NULL) {
GT_0trace(curTrace, GT_1CLASS,
"Can't locate JNI_CreateJavaVM() function\n");
return (NULL);
}
return (newVMFxn);
}
/*
* ======== getXDCPath ========
*/
static Bool getXDCPath(String prefix, String *xdcPath)
{
String tmp;
if ((tmp = getenv("XDCPATH")) != NULL) {
String xdcPathBuf;
UInt len = (Int)(strlen(prefix) + strlen(tmp) + 1);
if ((xdcPathBuf = (String)GS_calloc(len)) != NULL) {
strcpy(xdcPathBuf, prefix);
strcat(xdcPathBuf, tmp);
*xdcPath = xdcPathBuf;
return (TRUE);
}
else {
GT_0trace(curTrace, GT_6CLASS,
"ignoring XDCPATH environment variable: out of memory\n");
}
}
*xdcPath = NULL;
return (FALSE);
}
/*
* ======== isFile ========
* Return TRUE iff name exists and is not a directory
*/
static Bool isFile(String name)
{
struct stat buf;
if (stat(name, &buf) == 0 && !S_ISDIR(buf.st_mode)) {
return (TRUE);
}
return (FALSE);
}
/*
* ======== jvmVfprintf ========
*/
static Int CALLBACK jvmVfprintf(FILE *stream, const char *format, va_list ap)
{
return (vfprintf(stderr, format, ap));
}
/*
* ======== mkArgs ========
*/
static jobjectArray mkArgs(TCF_Handle tcf, int argc, char *argv[])
{
jclass stringClass;
jobjectArray args;
String tmp;
int extra = 0;
String extraTab[] = {"-f", INITSCRIPT};
JNIEnv *env;
GT_3trace(curTrace, GT_ENTER, "mkArgs(0x%lx, %d, 0x%lx)\n",
tcf, argc, argv);
/* get the environment pointer associated with this thread */
if ((env = getEnv(tcf)) == NULL) {
return (NULL);
}
/* find Java String class */
if ((stringClass = (*env)->FindClass(env, "java/lang/String")) == 0) {
GT_0trace(curTrace, GT_1CLASS, "Can't find Class java/lang/String\n");
return (NULL);
}
/* if INITSCRIPT exists, add "-f INITSCRIPT" to argument list */
if (!tcf->sflag){
if ((tmp = findFile(INITSCRIPT, tcf->pathTab)) != NULL){
extra = (int)(sizeof (extraTab) / sizeof (String));
extraTab[1] = tmp;
}
else {
GT_1trace(curTrace, GT_1CLASS, "Can't find file %s\n", INITSCRIPT);
return (NULL);
}
}
/* create Java String array */
if ((args = (*env)->NewObjectArray(env, extra + argc, stringClass, NULL))
== 0) {
GT_0trace(curTrace, GT_1CLASS, "Can't create Java array\n");
return (NULL);
}
/* create Java strings and save reference in args array */
if ((extra > 0 && appendArgs(tcf, args, 0, extra, extraTab) != TRUE)
|| appendArgs(tcf, args, extra, argc, argv) != TRUE) {
GT_0trace(curTrace, GT_1CLASS, "Can't create Java string\n");
(*env)->DeleteLocalRef(env, args);
return (NULL);
}
return (args);
}
/*
* ======== mkShell ========
*/
static jobject mkShell(TCF_Handle tcf, String shellName, Int argc, String argv[])
{
JNIEnv *env;
jclass cls;
jmethodID mid;
jobjectArray args;
jobject shell = NULL;
GT_4trace(curTrace, GT_ENTER, "mkShell(0x%lx, %s, %d, 0x%lx)\n",
tcf, shellName, argc, argv);
/* get the environment pointer associated with this thread */
if ((env = getEnv(tcf)) == NULL) {
return (NULL);
}
/* lookup the JavaScript Shell class */
if ((cls = (*env)->FindClass(env, shellName)) == 0) {
GT_1trace(curTrace, GT_1CLASS, "Can't find class %s\n", shellName);
return (NULL);
}
/* lookup the constructor for the Shell and processLine instance method */
mid = (*env)->GetMethodID(env, cls, "<init>", "([Ljava/lang/String;)V");
if (mid == 0) {
GT_1trace(curTrace, GT_1CLASS,
"Can't find constructor %s(String[])\n", shellName);
return (NULL);
}
tcf->pmid = (*env)->GetMethodID(env, cls, "processLine",
"(Ljava/lang/String;)I");
if (tcf->pmid == 0) {
GT_0trace(curTrace, GT_1CLASS,
"Can't find method processLine(String)\n");
return (NULL);
}
tcf->gmid = (*env)->GetMethodID(env, cls, "getOutput",
"(II)Ljava/lang/String;");
if (tcf->gmid == 0) {
GT_0trace(curTrace, GT_1CLASS, "Can't find method getOutput()\n");
return (NULL);
}
tcf->emid = (*env)->GetMethodID(env, cls, "exit", "()V");
if (tcf->emid == 0) {
GT_0trace(curTrace, GT_1CLASS, "Can't find method exit()\n");
return (NULL);
}
/* build up the args[] string array for shell constructor */
if ((args = mkArgs(tcf, argc, argv)) == 0) {
GT_1trace(curTrace, GT_1CLASS,
"Can't create argument array for %s()\n", shellName);
return (NULL);
}
/* create a Shell object */
shell = (*env)->NewObject(env, cls, mid, args);
/* delete args[] String array */
(*env)->DeleteLocalRef(env, args);
/* check for success */
if (shell == NULL) {
GT_1trace(curTrace, GT_1CLASS, "Can't create %s object\n", shellName);
}
return (shell);
}
/*
* ======== process ========
*/
static Int process(TCF_Session ses, String line)
{
jstring jstr;
jint result;
/* create the Java String */
if ((jstr = (*ses->env)->NewStringUTF(ses->env, line)) == 0) {
GT_0trace(curTrace, GT_1CLASS, "Can't create Java String\n");
return (TCF_EINTERN);
}
/* call processLine with the string and get result */
result = (*ses->env)->CallIntMethod(ses->env, ses->shell, ses->pmid, jstr);
/* delete the reference to the Java String */
(*ses->env)->DeleteLocalRef(ses->env, jstr);
return ((Int)result);
}
/*
* ======== runJS ========
* Run the JavaScript shell with the specified arguments (argv[]).
*
* Returns the exit status of the JVM which is the "quit()" status of
* the JavaScript.
*/
static Int runJS(TCF_Handle tcf, Int argc, String argv[])
{
jclass cls;
jmethodID mid;
jobjectArray args;
JNIEnv *env;
Char tmpBuf[MAXNAME + 1];
Int i;
Int status = TCF_EOK;
String shellClsName; /* name of JS shell Class to use */
String shellMetName; /* shell's "main" method */
String shellMetSig; /* shell's main signature */
shellMetName = "main";
shellClsName = JSSHELL;
shellMetSig = "([Ljava/lang/String;)I";
if (tcf->gflag) {
/* To use the debugger from Rhino directly, set the following
* environment variable:
* XS_JSDEBUGGER="org/mozilla/javascript/tools/debugger/Main"
*/
if ((shellClsName = getenv("XS_JSDEBUGGER")) == NULL
|| shellClsName[0] == '\0') {
shellClsName = JSDEBUG;
}
shellMetSig = "([Ljava/lang/String;)V";
}
GT_4trace(curTrace, GT_ENTER, "runJS(0x%lx, 0x%lx, 0x%lx): %s\n", tcf,
argc, argv, shellClsName);
/* get the environment pointer associated with this thread */
if ((env = getEnv(tcf)) == NULL) {
return (TCF_EINTERN);
}
/* lookup the JavaScript Shell class */
if ((cls = (*env)->FindClass(env, shellClsName)) == 0) {
GT_1trace(curTrace, GT_1CLASS, "Can't find class %s\n", shellClsName);
return (TCF_EINTERN);
}
/* lookup the shell's "main" method in this class */
mid = (*env)->GetStaticMethodID(env, cls, shellMetName, shellMetSig);
if (mid == 0) {
GT_3trace(curTrace, GT_1CLASS, "Can't find method %s.%s(): %s\n",
shellClsName, shellMetName, shellMetSig);
return (TCF_EINTERN);
}
/* convert class path (with '/'s) into '.' separated class name */
for (i = 0; i < MAXNAME && shellClsName[i] != '\0'; i++) {
tmpBuf[i] = shellClsName[i] == '/' ? '.' : shellClsName[i];
}
tmpBuf[i] = '\0'; /* force NULL termination and write to command */
appendCmd(tcf, "%s.%s ", tmpBuf, shellMetName);
/* build up the args[] string array for main() */
if ((args = mkArgs(tcf, argc, argv)) == 0) {
GT_0trace(curTrace, GT_1CLASS,
"Can't create argument array for main()\n");
return (TCF_EINTERN);
}
/* invoke main(String args[]) */
if (tcf->nflag == TRUE) {
GT_1trace(curTrace, GT_7CLASS, "%s\n", tcf->cmdBuf);
}
else {
if (tcf->gflag) {
(*env)->CallStaticVoidMethod(env, cls, mid, args);
status = TCF_EOK;
}
else {
status = (*env)->CallStaticIntMethod(env, cls, mid, args);
}
}
/* delete args[] String array */
(*env)->DeleteLocalRef(env, args);
GT_3trace(curTrace, GT_1CLASS, "returned from %s.%s (status = %d)\n",
shellClsName, shellMetName, status);
return (status);
}
/*
* ======== setPath ========
* Set the environment variable PATH on Windows, and NOP on Linux.
*
* If oldPath is NULL, PATH is removed from the environment.
*/
static Void setPath(String oldPath)
{
#if defined(xdc_target__os_Windows)
SetEnvironmentVariable("PATH", oldPath);
#endif
}