8201274: Launch Single-File Source-Code Programs

Reviewed-by: mcimadamore, jlahoda, ksrini, mchung, ihse, alanb
This commit is contained in:
Jonathan Gibbons 2018-06-07 16:06:49 -07:00
parent 628aec8c75
commit fe24730ed9
17 changed files with 1737 additions and 59 deletions

View file

@ -183,7 +183,7 @@ main(int argc, char **argv)
}
// Iterate the rest of command line
for (i = 1; i < argc; i++) {
JLI_List argsInFile = JLI_PreprocessArg(argv[i]);
JLI_List argsInFile = JLI_PreprocessArg(argv[i], JNI_TRUE);
if (NULL == argsInFile) {
JLI_List_add(args, JLI_StringDup(argv[i]));
} else {

View file

@ -79,6 +79,11 @@ static size_t argsCount = 1;
static jboolean stopExpansion = JNI_FALSE;
static jboolean relaunch = JNI_FALSE;
/*
* Prototypes for internal functions.
*/
static jboolean expand(JLI_List args, const char *str, const char *var_name);
JNIEXPORT void JNICALL
JLI_InitArgProcessing(jboolean hasJavaArgs, jboolean disableArgFile) {
// No expansion for relaunch
@ -376,9 +381,22 @@ static JLI_List expandArgFile(const char *arg) {
return rv;
}
/*
* expand a string into a list of words separated by whitespace.
*/
static JLI_List expandArg(const char *arg) {
JLI_List rv;
/* arbitrarily pick 8, seems to be a reasonable number of arguments */
rv = JLI_List_new(8);
expand(rv, arg, NULL);
return rv;
}
JNIEXPORT JLI_List JNICALL
JLI_PreprocessArg(const char *arg)
{
JLI_PreprocessArg(const char *arg, jboolean expandSourceOpt) {
JLI_List rv;
if (firstAppArgIndex > 0) {
@ -392,6 +410,12 @@ JLI_PreprocessArg(const char *arg)
return NULL;
}
if (expandSourceOpt
&& JLI_StrCCmp(arg, "--source") == 0
&& JLI_StrChr(arg, ' ') != NULL) {
return expandArg(arg);
}
if (arg[0] != '@') {
checkArg(arg);
return NULL;
@ -435,9 +459,6 @@ int isTerminalOpt(char *arg) {
JNIEXPORT jboolean JNICALL
JLI_AddArgsFromEnvVar(JLI_List args, const char *var_name) {
char *env = getenv(var_name);
char *p, *arg;
char quote;
JLI_List argsInFile;
if (firstAppArgIndex == 0) {
// Not 'java', return
@ -453,44 +474,64 @@ JLI_AddArgsFromEnvVar(JLI_List args, const char *var_name) {
}
JLI_ReportMessage(ARG_INFO_ENVVAR, var_name, env);
return expand(args, env, var_name);
}
/*
* Expand a string into a list of args.
* If the string is the result of looking up an environment variable,
* var_name should be set to the name of that environment variable,
* for use if needed in error messages.
*/
static jboolean expand(JLI_List args, const char *str, const char *var_name) {
jboolean inEnvVar = (var_name != NULL);
char *p, *arg;
char quote;
JLI_List argsInFile;
// This is retained until the process terminates as it is saved as the args
p = JLI_MemAlloc(JLI_StrLen(env) + 1);
while (*env != '\0') {
while (*env != '\0' && isspace(*env)) {
env++;
p = JLI_MemAlloc(JLI_StrLen(str) + 1);
while (*str != '\0') {
while (*str != '\0' && isspace(*str)) {
str++;
}
// Trailing space
if (*env == '\0') {
if (*str == '\0') {
break;
}
arg = p;
while (*env != '\0' && !isspace(*env)) {
if (*env == '"' || *env == '\'') {
quote = *env++;
while (*env != quote && *env != '\0') {
*p++ = *env++;
while (*str != '\0' && !isspace(*str)) {
if (inEnvVar && (*str == '"' || *str == '\'')) {
quote = *str++;
while (*str != quote && *str != '\0') {
*p++ = *str++;
}
if (*env == '\0') {
if (*str == '\0') {
JLI_ReportMessage(ARG_ERROR8, var_name);
exit(1);
}
env++;
str++;
} else {
*p++ = *env++;
*p++ = *str++;
}
}
*p++ = '\0';
argsInFile = JLI_PreprocessArg(arg);
argsInFile = JLI_PreprocessArg(arg, JNI_FALSE);
if (NULL == argsInFile) {
if (isTerminalOpt(arg)) {
JLI_ReportMessage(ARG_ERROR9, arg, var_name);
if (inEnvVar) {
JLI_ReportMessage(ARG_ERROR9, arg, var_name);
} else {
JLI_ReportMessage(ARG_ERROR15, arg);
}
exit(1);
}
JLI_List_add(args, arg);
@ -501,7 +542,11 @@ JLI_AddArgsFromEnvVar(JLI_List args, const char *var_name) {
for (idx = 0; idx < cnt; idx++) {
arg = argsInFile->elements[idx];
if (isTerminalOpt(arg)) {
JLI_ReportMessage(ARG_ERROR10, arg, argFile, var_name);
if (inEnvVar) {
JLI_ReportMessage(ARG_ERROR10, arg, argFile, var_name);
} else {
JLI_ReportMessage(ARG_ERROR16, arg, argFile);
}
exit(1);
}
JLI_List_add(args, arg);
@ -517,11 +562,15 @@ JLI_AddArgsFromEnvVar(JLI_List args, const char *var_name) {
* caught now.
*/
if (firstAppArgIndex != NOT_FOUND) {
JLI_ReportMessage(ARG_ERROR11, var_name);
if (inEnvVar) {
JLI_ReportMessage(ARG_ERROR11, var_name);
} else {
JLI_ReportMessage(ARG_ERROR17);
}
exit(1);
}
assert (*env == '\0' || isspace(*env));
assert (*str == '\0' || isspace(*str));
}
return JNI_TRUE;
@ -642,7 +691,7 @@ int main(int argc, char** argv) {
if (argc > 1) {
for (i = 0; i < argc; i++) {
JLI_List tokens = JLI_PreprocessArg(argv[i]);
JLI_List tokens = JLI_PreprocessArg(argv[i], JNI_FALSE);
if (NULL != tokens) {
for (j = 0; j < tokens->size; j++) {
printf("Token[%lu]: <%s>\n", (unsigned long) j, tokens->elements[j]);

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -51,6 +51,11 @@
#define ARG_ERROR10 "Error: Option %s in %s is not allowed in environment variable %s"
#define ARG_ERROR11 "Error: Cannot specify main class in environment variable %s"
#define ARG_ERROR12 "Error: %s requires module name"
#define ARG_ERROR13 "Error: %s requires source version"
#define ARG_ERROR14 "Error: Option %s is not allowed with --source"
#define ARG_ERROR15 "Error: Option %s is not allowed in this context"
#define ARG_ERROR16 "Error: Option %s in %s is not allowed in this context"
#define ARG_ERROR17 "Error: Cannot specify main class in this context"
#define JVM_ERROR1 "Error: Could not create the Java Virtual Machine.\n" GEN_ERROR
#define JVM_ERROR2 "Error: Could not detach main thread.\n" JNI_ERROR

View file

@ -172,6 +172,9 @@ static int KnownVMIndex(const char* name);
static void FreeKnownVMs();
static jboolean IsWildCardEnabled();
#define SOURCE_LAUNCHER_MAIN_ENTRY "jdk.compiler/com.sun.tools.javac.launcher.Main"
/*
* This reports error. VM will not be created and no usage is printed.
*/
@ -214,7 +217,7 @@ static jlong initialHeapSize = 0; /* inital heap size */
* Entry point.
*/
JNIEXPORT int JNICALL
JLI_Launch(int argc, char ** argv, /* main argc, argc */
JLI_Launch(int argc, char ** argv, /* main argc, argv */
int jargc, const char** jargv, /* java args */
int appclassc, const char** appclassv, /* app classpath */
const char* fullversion, /* full version defined */
@ -317,8 +320,7 @@ JLI_Launch(int argc, char ** argv, /* main argc, argc */
/* Parse command line options; if the return value of
* ParseArguments is false, the program should exit.
*/
if (!ParseArguments(&argc, &argv, &mode, &what, &ret, jrepath))
{
if (!ParseArguments(&argc, &argv, &mode, &what, &ret, jrepath)) {
return(ret);
}
@ -585,7 +587,8 @@ IsLauncherOption(const char* name) {
return IsClassPathOption(name) ||
IsLauncherMainOption(name) ||
JLI_StrCmp(name, "--describe-module") == 0 ||
JLI_StrCmp(name, "-d") == 0;
JLI_StrCmp(name, "-d") == 0 ||
JLI_StrCmp(name, "--source") == 0;
}
/*
@ -626,6 +629,29 @@ IsWhiteSpaceOption(const char* name) {
IsLauncherOption(name);
}
/*
* Check if it is OK to set the mode.
* If the mode was previously set, and should not be changed,
* a fatal error is reported.
*/
static int
checkMode(int mode, int newMode, const char *arg) {
if (mode == LM_SOURCE) {
JLI_ReportErrorMessage(ARG_ERROR14, arg);
exit(1);
}
return newMode;
}
/*
* Test if an arg identifies a source file.
*/
jboolean
IsSourceFile(const char *arg) {
struct stat st;
return (JLI_HasSuffix(arg, ".java") && stat(arg, &st) == 0);
}
/*
* Checks the command line options to find which JVM type was
* specified. If no command line option was given for the JVM type,
@ -1230,7 +1256,8 @@ GetOpt(int *pargc, char ***pargv, char **poption, char **pvalue) {
value = equals+1;
if (JLI_StrCCmp(arg, "--describe-module=") == 0 ||
JLI_StrCCmp(arg, "--module=") == 0 ||
JLI_StrCCmp(arg, "--class-path=") == 0) {
JLI_StrCCmp(arg, "--class-path=") == 0||
JLI_StrCCmp(arg, "--source=") == 0) {
kind = LAUNCHER_OPTION_WITH_ARGUMENT;
} else {
kind = VM_LONG_OPTION;
@ -1274,17 +1301,28 @@ ParseArguments(int *pargc, char ***pargv,
*/
if (JLI_StrCmp(arg, "-jar") == 0) {
ARG_CHECK(argc, ARG_ERROR2, arg);
mode = LM_JAR;
mode = checkMode(mode, LM_JAR, arg);
} else if (JLI_StrCmp(arg, "--module") == 0 ||
JLI_StrCCmp(arg, "--module=") == 0 ||
JLI_StrCmp(arg, "-m") == 0) {
REPORT_ERROR (has_arg, ARG_ERROR5, arg);
SetMainModule(value);
mode = LM_MODULE;
mode = checkMode(mode, LM_MODULE, arg);
if (has_arg) {
*pwhat = value;
break;
}
} else if (JLI_StrCmp(arg, "--source") == 0 ||
JLI_StrCCmp(arg, "--source=") == 0) {
REPORT_ERROR (has_arg, ARG_ERROR13, arg);
mode = LM_SOURCE;
if (has_arg) {
const char *prop = "-Djdk.internal.javac.source=";
size_t size = JLI_StrLen(prop) + JLI_StrLen(value) + 1;
char *propValue = (char *)JLI_MemAlloc(size);
JLI_Snprintf(propValue, size, "%s%s", prop, value);
AddOption(propValue, NULL);
}
} else if (JLI_StrCmp(arg, "--class-path") == 0 ||
JLI_StrCCmp(arg, "--class-path=") == 0 ||
JLI_StrCmp(arg, "-classpath") == 0 ||
@ -1435,12 +1473,25 @@ ParseArguments(int *pargc, char ***pargv,
if (!_have_classpath) {
SetClassPath(".");
}
mode = LM_CLASS;
mode = IsSourceFile(arg) ? LM_SOURCE : LM_CLASS;
} else if (mode == LM_CLASS && IsSourceFile(arg)) {
/* override LM_CLASS mode if given a source file */
mode = LM_SOURCE;
}
if (argc >= 0) {
*pargc = argc;
*pargv = argv;
if (mode == LM_SOURCE) {
AddOption("--add-modules=ALL-DEFAULT", NULL);
*pwhat = SOURCE_LAUNCHER_MAIN_ENTRY;
// adjust (argc, argv) so that the name of the source file
// is included in the args passed to the source launcher
// main entry class
*pargc = argc + 1;
*pargv = argv - 1;
} else {
if (argc >= 0) {
*pargc = argc;
*pargv = argv;
}
}
*pmode = mode;

View file

@ -230,11 +230,12 @@ enum LaunchMode { // cf. sun.launcher.LauncherHelper
LM_UNKNOWN = 0,
LM_CLASS,
LM_JAR,
LM_MODULE
LM_MODULE,
LM_SOURCE
};
static const char *launchModeNames[]
= { "Unknown", "Main class", "JAR file", "Module" };
= { "Unknown", "Main class", "JAR file", "Module", "Source" };
typedef struct {
int argc;

View file

@ -84,6 +84,16 @@ JLI_MemFree(void *ptr)
free(ptr);
}
jboolean
JLI_HasSuffix(const char *s1, const char *s2)
{
char *p = JLI_StrRChr(s1, '.');
if (p == NULL || *p == '\0') {
return JNI_FALSE;
}
return (JLI_StrCaseCmp(p, s2) == 0);
}
/*
* debug helpers we use
*/

View file

@ -51,7 +51,8 @@ JLI_StringDup(const char *s1);
JNIEXPORT void JNICALL
JLI_MemFree(void *ptr);
int JLI_StrCCmp(const char *s1, const char* s2);
int JLI_StrCCmp(const char *s1, const char *s2);
jboolean JLI_HasSuffix(const char *s1, const char *s2);
typedef struct {
char *arg;
@ -158,7 +159,7 @@ JNIEXPORT void JNICALL
JLI_InitArgProcessing(jboolean hasJavaArgs, jboolean disableArgFile);
JNIEXPORT JLI_List JNICALL
JLI_PreprocessArg(const char *arg);
JLI_PreprocessArg(const char *arg, jboolean expandSourceOpt);
JNIEXPORT jboolean JNICALL
JLI_AddArgsFromEnvVar(JLI_List args, const char *var_name);