You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

484 lines
10 KiB

12 years ago
/*
* Command line execution tool. Used by test cases and other manual testing.
12 years ago
*
* For maximum portability, compile with -DDUK_CMDLINE_BAREBONES
12 years ago
*/
#if defined(_WIN32) || defined(_WIN64) || defined(WIN32) || \
defined(__WIN32__) || defined(__TOS_WIN__) || defined(__WINDOWS__)
#ifndef DUK_CMDLINE_BAREBONES
/* Force barebones mode on Windows. */
#define DUK_CMDLINE_BAREBONES
#endif
#endif
#ifdef DUK_CMDLINE_BAREBONES
#define NO_READLINE
#define NO_RLIMIT
#define NO_SIGNAL
#endif
#define GREET_CODE(variant) \
"print(" \
"'((o) Duktape" variant "'" \
", " \
"Math.floor(Duktape.version / 10000) + '.' + " \
"Math.floor(Duktape.version / 100) % 100 + '.' + " \
"Duktape.version % 100" \
");"
12 years ago
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef NO_SIGNAL
12 years ago
#include <signal.h>
#endif
#ifndef NO_RLIMIT
12 years ago
#include <sys/resource.h>
#endif
#ifndef NO_READLINE
12 years ago
#include <readline/readline.h>
#include <readline/history.h>
#endif
12 years ago
#include "duktape.h"
12 years ago
#define MEM_LIMIT_NORMAL (128*1024*1024) /* 128 MB */
#define MEM_LIMIT_HIGH (2047*1024*1024) /* ~2 GB */
#define LINEBUF_SIZE 65536
12 years ago
/* FIXME: additional modules should probably only be in some separate tool? */
#if 0
12 years ago
extern void duk_ncurses_register(duk_context *ctx);
extern void duk_socket_register(duk_context *ctx);
extern void duk_fileio_register(duk_context *ctx);
#endif
12 years ago
int interactive_mode = 0;
#ifndef NO_RLIMIT
static void set_resource_limits(rlim_t mem_limit_value) {
12 years ago
int rc;
struct rlimit lim;
rc = getrlimit(RLIMIT_AS, &lim);
if (rc != 0) {
fprintf(stderr, "Warning: cannot read RLIMIT_AS\n");
return;
}
if (lim.rlim_max < mem_limit_value) {
fprintf(stderr, "Warning: rlim_max < mem_limit_value (%d < %d)\n", (int) lim.rlim_max, (int) mem_limit_value);
12 years ago
return;
}
lim.rlim_cur = mem_limit_value;
lim.rlim_max = mem_limit_value;
12 years ago
rc = setrlimit(RLIMIT_AS, &lim);
if (rc != 0) {
fprintf(stderr, "Warning: setrlimit failed\n");
return;
}
#if 0
fprintf(stderr, "Set RLIMIT_AS to %d\n", (int) mem_limit_value);
12 years ago
#endif
}
#endif /* NO_RLIMIT */
12 years ago
#ifndef NO_SIGNAL
12 years ago
static void my_sighandler(int x) {
fprintf(stderr, "Got signal %d\n", x);
}
static void set_sigint_handler(void) {
(void) signal(SIGINT, my_sighandler);
}
#endif /* NO_SIGNAL */
12 years ago
static int get_stack_raw(duk_context *ctx) {
if (!duk_is_object(ctx, -1)) {
return 1;
}
if (!duk_has_prop_string(ctx, -1, "stack")) {
return 1;
}
/* XXX: should check here that object is an Error instance too,
* i.e. 'stack' is special.
*/
duk_get_prop_string(ctx, -1, "stack"); /* caller coerces */
duk_remove(ctx, -2);
return 1;
}
/* Print error to stderr and pop error. */
static void print_error(duk_context *ctx, FILE *f) {
/* Print error objects with a stack trace specially.
* Note that getting the stack trace may throw an error
* so this also needs to be safe call wrapped.
*/
(void) duk_safe_call(ctx, get_stack_raw, 1 /*nargs*/, 1 /*nrets*/, DUK_INVALID_INDEX);
fprintf(f, "%s\n", duk_safe_to_string(ctx, -1));
fflush(f);
duk_pop(ctx);
}
12 years ago
int wrapped_compile_execute(duk_context *ctx) {
int comp_flags;
12 years ago
comp_flags = 0;
duk_compile(ctx, comp_flags);
12 years ago
#if 0
/* FIXME: something similar with public API */
12 years ago
if (interactive_mode) {
duk_hcompiledfunction *f = (duk_hcompiledfunction *) duk_get_hobject(ctx, -1);
if (f && DUK_HOBJECT_IS_COMPILEDFUNCTION((duk_hobject *) f)) {
fprintf(stdout, "[bytecode length %d opcodes, registers %d, constants %d, inner functions %d]\n",
(int) DUK_HCOMPILEDFUNCTION_GET_CODE_COUNT(f),
(int) f->nregs,
(int) DUK_HCOMPILEDFUNCTION_GET_CONSTS_COUNT(f),
(int) DUK_HCOMPILEDFUNCTION_GET_FUNCS_COUNT(f));
12 years ago
fflush(stdout);
} else {
fprintf(stdout, "[invalid compile result]\n");
fflush(stdout);
}
}
#endif
12 years ago
duk_push_global_object(ctx); /* 'this' binding */
duk_call_method(ctx, 0);
12 years ago
if (interactive_mode) {
/*
* In interactive mode, write to stdout so output won't interleave as easily.
*
* NOTE: the ToString() coercion may fail in some cases; for instance,
* if you evaluate:
*
* ( {valueOf: function() {return {}}, toString: function() {return {}}});
*
* The error is:
*
* TypeError: failed to coerce with [[DefaultValue]]
* duk_api.c:1420
*
* These errors are caught and printed out as errors although
* the errors are not generated by user code as such. Changing
* duk_to_string() to duk_safe_to_string() would avoid these
* errors.
*/
fprintf(stdout, "= %s\n", duk_to_string(ctx, -1));
12 years ago
fflush(stdout);
} else {
/* In non-interactive mode, success results are not written at all.
* It is important that the result value is not string coerced,
* as the string coercion may cause an error in some cases.
*/
12 years ago
}
duk_pop(ctx);
12 years ago
return 0;
}
int handle_fh(duk_context *ctx, FILE *f, const char *filename) {
12 years ago
char *buf = NULL;
int len;
int got;
int rc;
int retval = -1;
if (fseek(f, 0, SEEK_END) < 0) {
goto error;
}
len = (int) ftell(f);
if (fseek(f, 0, SEEK_SET) < 0) {
goto error;
}
buf = (char *) malloc(len);
if (!buf) {
goto error;
}
got = fread((void *) buf, (size_t) 1, (size_t) len, f);
duk_push_lstring(ctx, buf, got);
duk_push_string(ctx, filename);
12 years ago
free(buf);
buf = NULL;
interactive_mode = 0; /* global */
rc = duk_safe_call(ctx, wrapped_compile_execute, 2 /*nargs*/, 1 /*nret*/, DUK_INVALID_INDEX);
if (rc != DUK_EXEC_SUCCESS) {
print_error(ctx, stderr);
12 years ago
goto error;
} else {
duk_pop(ctx);
retval = 0;
12 years ago
}
/* fall thru */
error:
if (buf) {
free(buf);
}
return retval;
}
int handle_file(duk_context *ctx, const char *filename) {
12 years ago
FILE *f = NULL;
int retval;
f = fopen(filename, "rb");
if (!f) {
fprintf(stderr, "failed to open source file: %s\n", filename);
fflush(stderr);
goto error;
}
retval = handle_fh(ctx, f, filename);
12 years ago
fclose(f);
return retval;
error:
return -1;
}
int handle_stdin(duk_context *ctx) {
12 years ago
int retval;
retval = handle_fh(ctx, stdin, "stdin");
12 years ago
return retval;
}
#ifdef NO_READLINE
int handle_interactive(duk_context *ctx) {
const char *prompt = "duk> ";
char *buffer = NULL;
int retval = 0;
int rc;
int got_eof = 0;
duk_eval_string(ctx, GREET_CODE(" [no readline]"));
duk_pop(ctx);
buffer = malloc(LINEBUF_SIZE);
if (!buffer) {
fprintf(stderr, "failed to allocated a line buffer\n");
fflush(stderr);
retval = -1;
goto done;
}
while (!got_eof) {
size_t idx = 0;
fwrite(prompt, 1, strlen(prompt), stdout);
fflush(stdout);
for (;;) {
int c = fgetc(stdin);
if (c == EOF) {
got_eof = 1;
break;
} else if (c == '\n') {
break;
} else if (idx >= LINEBUF_SIZE) {
fprintf(stderr, "line too long\n");
fflush(stderr);
retval = -1;
goto done;
} else {
buffer[idx++] = (char) c;
}
}
duk_push_lstring(ctx, buffer, idx);
duk_push_string(ctx, "input");
interactive_mode = 1; /* global */
rc = duk_safe_call(ctx, wrapped_compile_execute, 2 /*nargs*/, 1 /*nret*/, DUK_INVALID_INDEX);
if (rc != DUK_EXEC_SUCCESS) {
/* in interactive mode, write to stdout */
print_error(ctx, stdout);
retval = -1; /* an error 'taints' the execution */
} else {
duk_pop(ctx);
}
}
done:
if (buffer) {
free(buffer);
buffer = NULL;
}
return retval;
}
#else /* NO_READLINE */
int handle_interactive(duk_context *ctx) {
12 years ago
const char *prompt = "duk> ";
char *buffer = NULL;
int retval = 0;
int rc;
duk_eval_string(ctx, GREET_CODE(""));
duk_pop(ctx);
12 years ago
/*
* Note: using readline leads to valgrind-reported leaks inside
* readline itself. Execute code from an input file (and not
* through stdin) for clean valgrind runs.
*/
rl_initialize();
for (;;) {
if (buffer) {
free(buffer);
buffer = NULL;
}
buffer = readline(prompt);
if (!buffer) {
break;
}
if (buffer && buffer[0] != (char) 0) {
add_history(buffer);
}
duk_push_lstring(ctx, buffer, strlen(buffer));
duk_push_string(ctx, "input");
12 years ago
if (buffer) {
free(buffer);
buffer = NULL;
}
interactive_mode = 1; /* global */
rc = duk_safe_call(ctx, wrapped_compile_execute, 2 /*nargs*/, 1 /*nret*/, DUK_INVALID_INDEX);
if (rc != DUK_EXEC_SUCCESS) {
12 years ago
/* in interactive mode, write to stdout */
print_error(ctx, stdout);
12 years ago
retval = -1; /* an error 'taints' the execution */
} else {
duk_pop(ctx);
12 years ago
}
}
if (buffer) {
free(buffer);
buffer = NULL;
}
return retval;
}
#endif /* NO_READLINE */
12 years ago
int main(int argc, char *argv[]) {
duk_context *ctx = NULL;
int retval = 0;
const char *filename = NULL;
int interactive = 0;
int memlimit_high = 1;
12 years ago
int i;
#ifndef NO_SIGNAL
12 years ago
set_sigint_handler();
/* This is useful at the global level; libraries should avoid SIGPIPE though */
/*signal(SIGPIPE, SIG_IGN);*/
#endif
12 years ago
for (i = 1; i < argc; i++) {
char *arg = argv[i];
if (!arg) {
goto usage;
}
if (strcmp(arg, "-r") == 0) {
memlimit_high = 0;
12 years ago
} else if (strlen(arg) > 1 && arg[0] == '-') {
goto usage;
} else {
if (filename) {
goto usage;
}
filename = arg;
}
}
if (!filename) {
interactive = 1;
}
#ifndef NO_RLIMIT
set_resource_limits(memlimit_high ? MEM_LIMIT_HIGH : MEM_LIMIT_NORMAL);
#else
(void) memlimit_high; /* suppress warning */
#endif
ctx = duk_create_heap_default();
12 years ago
#if 0
12 years ago
duk_ncurses_register(ctx);
duk_socket_register(ctx);
duk_fileio_register(ctx);
#endif
12 years ago
if (filename) {
if (strcmp(filename, "-") == 0) {
if (handle_stdin(ctx) != 0) {
12 years ago
retval = 1;
goto cleanup;
}
} else {
if (handle_file(ctx, filename) != 0) {
12 years ago
retval = 1;
goto cleanup;
}
}
}
if (interactive) {
if (handle_interactive(ctx) != 0) {
12 years ago
retval = 1;
goto cleanup;
}
}
cleanup:
if (interactive) {
fprintf(stderr, "Cleaning up...\n");
fflush(stderr);
}
12 years ago
if (ctx) {
duk_destroy_heap(ctx);
12 years ago
}
return retval;
usage:
fprintf(stderr, "Usage: duk [-r] <filename>\n");
12 years ago
fprintf(stderr, "where\n");
fprintf(stderr, " -r use lower memory limit\n");
12 years ago
fprintf(stderr, "\n");
fprintf(stderr, "If <filename> is '-', the entire STDIN executed.\n");
fprintf(stderr, "If <filename> is omitted, interactive mode is started.\n");
fflush(stderr);
exit(1);
}