Skip to content

Commit efb96ee

Browse files
Add support for --vm.@arg-file in thin launchers
1 parent a4d886d commit efb96ee

File tree

1 file changed

+173
-0
lines changed

1 file changed

+173
-0
lines changed

sdk/src/org.graalvm.launcher.native/src/launcher.cc

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,14 @@
4747
#include <cstdlib>
4848

4949
#include <string>
50+
#include <optional>
5051
#include <iostream>
5152
#include <sstream>
53+
#include <fstream>
5254
#include <vector>
5355

56+
#include <cassert>
57+
5458
#define QUOTE(name) #name
5559
#define STR(macro) QUOTE(macro)
5660

@@ -94,6 +98,7 @@
9498
#define VM_MODULE_PATH_ARG_PREFIX "--vm.-module-path="
9599
#define VM_LIBRARY_PATH_ARG_PREFIX "--vm.Djava.library.path="
96100
#define VM_STACK_SIZE_ARG_PREFIX "--vm.Xss"
101+
#define VM_ARG_FILE_ARG_PREFIX "--vm.@"
97102

98103
#define VM_ARG_OFFSET (sizeof(VM_ARG_PREFIX)-1)
99104
#define VM_CP_ARG_OFFSET (sizeof(VM_CP_ARG_PREFIX)-1)
@@ -102,6 +107,7 @@
102107
#define VM_MODULE_PATH_ARG_OFFSET (sizeof(VM_MODULE_PATH_ARG_PREFIX)-1)
103108
#define VM_LIBRARY_PATH_ARG_OFFSET (sizeof(VM_LIBRARY_PATH_ARG_PREFIX)-1)
104109
#define VM_STACK_SIZE_ARG_OFFSET (sizeof(VM_STACK_SIZE_ARG_PREFIX)-1)
110+
#define VM_ARG_FILE_ARG_OFFSET (sizeof(VM_ARG_FILE_ARG_PREFIX)-1)
105111

106112
#define STARTS_WITH(ARG, PREFIX) (ARG.rfind(PREFIX, 0) != std::string::npos)
107113
#define IS_VM_ARG(ARG) STARTS_WITH(ARG, VM_ARG_PREFIX)
@@ -111,6 +117,7 @@
111117
#define IS_VM_MODULE_PATH_ARG(ARG) STARTS_WITH(ARG, VM_MODULE_PATH_ARG_PREFIX)
112118
#define IS_VM_LIBRARY_PATH_ARG(ARG) STARTS_WITH(ARG, VM_LIBRARY_PATH_ARG_PREFIX)
113119
#define IS_VM_STACK_SIZE_ARG(ARG) STARTS_WITH(ARG, VM_STACK_SIZE_ARG_PREFIX)
120+
#define IS_VM_ARG_FILE_ARG(ARG) STARTS_WITH(ARG, VM_ARG_FILE_ARG_PREFIX)
114121

115122
#define NMT_ARG_NAME "XX:NativeMemoryTracking"
116123
#define NMT_ENV_NAME "NMT_LEVEL_"
@@ -345,6 +352,12 @@ static std::string vm_path(std::string exeDir, bool jvmMode) {
345352
}
346353

347354
static size_t parse_size(std::string_view str);
355+
static void expand_vm_arg_file(const char *arg_file,
356+
std::vector<std::string> *vmArgs,
357+
std::ostringstream *cp,
358+
std::ostringstream *modulePath,
359+
std::ostringstream *libraryPath,
360+
size_t* stack_size);
348361

349362
static void parse_vm_option(
350363
std::vector<std::string> *vmArgs,
@@ -363,6 +376,9 @@ static void parse_vm_option(
363376
*modulePath << CP_SEP_STR << option.substr(VM_MODULE_PATH_ARG_OFFSET);
364377
} else if (IS_VM_LIBRARY_PATH_ARG(option)) {
365378
*libraryPath << CP_SEP_STR << option.substr(VM_LIBRARY_PATH_ARG_OFFSET);
379+
} else if (IS_VM_ARG_FILE_ARG(option)) {
380+
std::string arg_file(option.substr(VM_ARG_FILE_ARG_OFFSET));
381+
expand_vm_arg_file(arg_file.c_str(), vmArgs, cp, modulePath, libraryPath, stack_size);
366382
} else if (IS_VM_ARG(option)) {
367383
if (IS_VM_STACK_SIZE_ARG(option)) {
368384
*stack_size = parse_size(option.substr(VM_STACK_SIZE_ARG_OFFSET));
@@ -375,6 +391,163 @@ static void parse_vm_option(
375391
}
376392
}
377393

394+
enum ArgFileState {
395+
FIND_NEXT,
396+
IN_COMMENT,
397+
IN_QUOTE,
398+
IN_ESCAPE,
399+
SKIP_LEAD_WS,
400+
IN_TOKEN
401+
};
402+
// Parse @arg-files as handled by libjli. See libjli/args.c.
403+
static std::optional<std::string> arg_file_next_token(std::ifstream &input) {
404+
ArgFileState state = FIND_NEXT;
405+
int currentQuoteChar = -1;
406+
std::istream::int_type ch;
407+
408+
std::ostringstream token;
409+
std::ostringstream::pos_type start = token.tellp();
410+
411+
while ((ch = input.get()) != std::istream::traits_type::eof()) {
412+
// Skip white space characters
413+
if (state == FIND_NEXT || state == SKIP_LEAD_WS) {
414+
while (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t' || ch == '\f') {
415+
if ((ch = input.get()) == std::istream::traits_type::eof()) {
416+
goto done;
417+
}
418+
}
419+
state = (state == FIND_NEXT) ? IN_TOKEN : IN_QUOTE;
420+
// Deal with escape sequences
421+
} else if (state == IN_ESCAPE) {
422+
// concatenation directive
423+
if (ch == '\n' || ch == '\r') {
424+
state = SKIP_LEAD_WS;
425+
} else {
426+
// escaped character
427+
switch (ch) {
428+
case 'n':
429+
token << '\n';
430+
break;
431+
case 'r':
432+
token << '\r';
433+
break;
434+
case 't':
435+
token << '\t';
436+
break;
437+
case 'f':
438+
token << '\f';
439+
break;
440+
default:
441+
token << (char) ch;
442+
break;
443+
}
444+
state = IN_QUOTE;
445+
}
446+
continue;
447+
// ignore comment to EOL
448+
} else if (state == IN_COMMENT) {
449+
while (ch != '\n' && ch != '\r') {
450+
if ((ch = input.get()) == std::istream::traits_type::eof()) {
451+
goto done;
452+
}
453+
}
454+
state = FIND_NEXT;
455+
continue;
456+
}
457+
458+
assert(state != IN_ESCAPE);
459+
assert(state != FIND_NEXT);
460+
assert(state != SKIP_LEAD_WS);
461+
assert(state != IN_COMMENT);
462+
463+
switch (ch) {
464+
case ' ':
465+
case '\t':
466+
case '\f':
467+
if (state == IN_QUOTE) {
468+
token << (char) ch;
469+
continue;
470+
}
471+
// fallthrough
472+
case '\n':
473+
case '\r':
474+
return token.str();
475+
case '#':
476+
if (state == IN_QUOTE) {
477+
token << (char) ch;
478+
continue;
479+
}
480+
state = IN_COMMENT;
481+
break;
482+
case '\\':
483+
if (state != IN_QUOTE) {
484+
token << (char) ch;
485+
continue;
486+
}
487+
state = IN_ESCAPE;
488+
break;
489+
case '\'':
490+
case '"':
491+
if (state == IN_QUOTE && currentQuoteChar != ch) {
492+
// not matching quote
493+
token << (char) ch;
494+
continue;
495+
}
496+
if (state == IN_TOKEN) {
497+
currentQuoteChar = ch;
498+
state = IN_QUOTE;
499+
} else {
500+
state = IN_TOKEN;
501+
}
502+
break;
503+
default:
504+
token << (char) ch;
505+
break;
506+
}
507+
}
508+
done:
509+
if (token.tellp() == start) {
510+
return {};
511+
}
512+
return token.str();
513+
}
514+
515+
static void expand_vm_arg_file(const char *arg_file,
516+
std::vector<std::string> *vmArgs,
517+
std::ostringstream *cp,
518+
std::ostringstream *modulePath,
519+
std::ostringstream *libraryPath,
520+
size_t* stack_size) {
521+
if (debug) {
522+
std::cout << "Expanding VM arg file " << arg_file << std::endl;
523+
}
524+
std::ifstream input(arg_file);
525+
if (input.fail()) {
526+
std::cerr << "Error: could not open `" << arg_file << "': " << strerror(errno) << std::endl;
527+
exit(EXIT_FAILURE);
528+
}
529+
530+
while (true) {
531+
std::optional<std::string> token = arg_file_next_token(input);
532+
if (token.has_value()) {
533+
if (STARTS_WITH(token.value(), "--class-path=")) {
534+
*cp << CP_SEP_STR << token.value().substr(sizeof("--class-path=") - 1);
535+
} else if (STARTS_WITH(token.value(), "--module-path=")) {
536+
*modulePath << CP_SEP_STR << token.value().substr(sizeof("--module-path=") - 1);
537+
} else if (STARTS_WITH(token.value(), "-Djava.library.path=")) {
538+
*libraryPath << CP_SEP_STR << token.value().substr(sizeof("-Djava.library.path=") - 1);
539+
} else {
540+
if (STARTS_WITH(token.value(), "-Xss")) {
541+
*stack_size = parse_size(token.value().substr(sizeof("-Xss") - 1));
542+
}
543+
vmArgs->push_back(token.value());
544+
}
545+
} else {
546+
break;
547+
}
548+
}
549+
}
550+
378551
struct MainThreadArgs {
379552
int argc;
380553
char **argv;

0 commit comments

Comments
 (0)