Browse Source

Update to Duktape 2.0.1

Manuel Freiberger 8 years ago
parent
commit
3adb59c815

+ 4 - 4
Source/AtomicJS/Javascript/JSPlugin.cpp

@@ -150,8 +150,8 @@ namespace Atomic
         gJSVMExports.duk_get_memory_functions = duk_get_memory_functions;
         gJSVMExports.duk_gc = duk_gc;
 
-        gJSVMExports.duk_throw = duk_throw;
-        gJSVMExports.duk_fatal = duk_fatal;
+        gJSVMExports.duk_throw_raw = duk_throw_raw;
+        gJSVMExports.duk_fatal_raw = duk_fatal_raw;
         gJSVMExports.duk_error_raw = duk_error_raw;
         gJSVMExports.duk_error_va_raw = duk_error_va_raw;
 
@@ -227,7 +227,7 @@ namespace Atomic
 
         gJSVMExports.duk_is_undefined = duk_is_undefined;
         gJSVMExports.duk_is_null = duk_is_null;
-        gJSVMExports.duk_is_null_or_undefined = duk_is_null_or_undefined;
+        //gJSVMExports.duk_is_null_or_undefined = duk_is_null_or_undefined;   This is a macro in Duktape 2.0.
         gJSVMExports.duk_is_boolean = duk_is_boolean;
         gJSVMExports.duk_is_number = duk_is_number;
         gJSVMExports.duk_is_nan = duk_is_nan;
@@ -291,7 +291,7 @@ namespace Atomic
         gJSVMExports.duk_to_buffer_raw = duk_to_buffer_raw;
         gJSVMExports.duk_to_pointer = duk_to_pointer;
         gJSVMExports.duk_to_object = duk_to_object;
-        gJSVMExports.duk_to_defaultvalue = duk_to_defaultvalue;
+        gJSVMExports.duk_to_defaultvalue = duk_to_primitive;  // In Duktape 2.0 duk_to_defaultvalue() has to be replaced by duk_to_primitive().
         gJSVMExports.duk_to_primitive = duk_to_primitive;
 
         gJSVMExports.duk_safe_to_lstring = duk_safe_to_lstring;

+ 9 - 5
Source/AtomicJS/Javascript/JSPluginExports.h

@@ -23,6 +23,8 @@
 #pragma once
 
 #include <Duktape/duktape.h>
+#include <Duktape/duk_logging.h>
+#include <Duktape/duk_v1_compat.h>
 
 namespace Atomic
 {
@@ -51,8 +53,8 @@ namespace Atomic
         typedef void(*__duk_get_memory_functions)(duk_context *ctx, duk_memory_functions *out_funcs);
         typedef void(*__duk_gc)(duk_context *ctx, duk_uint_t flags);
 
-        typedef void(*__duk_throw)(duk_context *ctx);
-        typedef void(*__duk_fatal)(duk_context *ctx, duk_errcode_t err_code, const char *err_msg);
+        typedef void(*__duk_throw_raw)(duk_context *ctx);
+        typedef void(*__duk_fatal_raw)(duk_context *ctx, const char *err_msg);
         typedef void(*__duk_error_raw)(duk_context *ctx, duk_errcode_t err_code, const char *filename, duk_int_t line, const char *fmt, ...);
         typedef void(*__duk_error_va_raw)(duk_context *ctx, duk_errcode_t err_code, const char *filename, duk_int_t line, const char *fmt, va_list ap);
 
@@ -261,7 +263,7 @@ namespace Atomic
         typedef duk_int_t(*__duk_pcall_method)(duk_context *ctx, duk_idx_t nargs);
         typedef duk_int_t(*__duk_pcall_prop)(duk_context *ctx, duk_idx_t obj_index, duk_idx_t nargs);
         typedef void(*__duk_new)(duk_context *ctx, duk_idx_t nargs);
-        typedef duk_int_t(*__duk_safe_call)(duk_context *ctx, duk_safe_call_function func, duk_idx_t nargs, duk_idx_t nrets);
+        typedef duk_int_t(*__duk_safe_call)(duk_context *ctx, duk_safe_call_function func, void* data, duk_idx_t nargs, duk_idx_t nrets);
 
         typedef duk_int_t(*__duk_eval_raw)(duk_context *ctx, const char *src_buffer, duk_size_t src_length, duk_uint_t flags);
         typedef duk_int_t(*__duk_compile_raw)(duk_context *ctx, const char *src_buffer, duk_size_t src_length, duk_uint_t flags);
@@ -271,12 +273,14 @@ namespace Atomic
 
         typedef void(*__duk_push_context_dump)(duk_context *ctx);
 
+
         typedef void(*__duk_debugger_attach)(duk_context *ctx,
             duk_debug_read_function read_cb,
             duk_debug_write_function write_cb,
             duk_debug_peek_function peek_cb,
             duk_debug_read_flush_function read_flush_cb,
             duk_debug_write_flush_function write_flush_cb,
+            duk_debug_request_function request_cb,
             duk_debug_detached_function detached_cb,
             void *udata);
         typedef void(*__duk_debugger_detach)(duk_context *ctx);
@@ -296,8 +300,8 @@ namespace Atomic
             __duk_get_memory_functions duk_get_memory_functions;
             __duk_gc duk_gc;
 
-            __duk_throw duk_throw;
-            __duk_fatal duk_fatal;
+            __duk_throw_raw duk_throw_raw;
+            __duk_fatal_raw duk_fatal_raw;
             __duk_error_raw duk_error_raw;
             __duk_error_va_raw duk_error_va_raw;
 

+ 4 - 0
Source/AtomicJS/Javascript/JSVM.cpp

@@ -21,6 +21,8 @@
 //
 
 #include <Duktape/duktape.h>
+#include <Duktape/duk_logging.h>
+#include <Duktape/duk_module_duktape.h>
 
 #include <Atomic/Core/Profiler.h>
 #include <Atomic/Core/CoreEvents.h>
@@ -85,6 +87,8 @@ JSVM::~JSVM()
 void JSVM::InitJSContext()
 {
     ctx_ = duk_create_heap_default();
+    duk_logging_init(ctx_, 0);
+    duk_module_duktape_init(ctx_);
 
     jsapi_init_atomic(this);
 

+ 11 - 15
Source/ThirdParty/Duktape/README.TXT

@@ -1,17 +1,13 @@
+To create the duk_config.h file change into the directory ${DUKTAPE}/tools and run the
+following Python command (you can copy/paste it):
 
-duk_config.h generated by replacing default in util/make_dist.py with:
+python2 configure.py \
+    --output-directory=out \
+    --omit-removed-config-options --omit-deprecated-config-options --omit-unused-config-options \
+    --emit-config-sanity-check \
+    --option-file ../config/examples/performance_sensitive.yaml \
+    --option-file ../config/examples/timing_sensitive.yaml
 
-# Build default duk_config.h from snippets using genconfig.
-exec_print_stdout([
-	'python', os.path.join('config', 'genconfig.py'), '--metadata', 'config',
-	'--output', os.path.join(dist, 'duk_config.h.tmp'),
-	'--git-commit', git_commit, '--git-describe', git_describe, '--git-branch', git_branch,
-	'--omit-removed-config-options', '--omit-unused-config-options',
-	'--emit-config-sanity-check',
-	'--support-feature-options',
-	'--option-file', dist + '/../config/examples/performance_sensitive.yaml',
-	'--option-file', dist + '/../config/examples/timing_sensitive.yaml',
-	'duk-config-header'
-])
-
-... and then: python util/make_dist.py
+An 'extern "C"' block has to be added to the headers duk_logging.h, duk_module_duktape.h and
+duk_v1_compat.h (from the directory ${DUKTAPE}/extras) to avoid C++ decoration of the function
+names.

File diff suppressed because it is too large
+ 295 - 939
Source/ThirdParty/Duktape/duk_config.h


+ 380 - 0
Source/ThirdParty/Duktape/duk_logging.c

@@ -0,0 +1,380 @@
+/*
+ *  Logging support
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include "duktape.h"
+#include "duk_logging.h"
+
+/* XXX: uses stderr always for now, configurable? */
+
+#define DUK_LOGGING_FLUSH  /* Duktape 1.x: flush stderr */
+
+/* 3-letter log level strings. */
+static const char duk__log_level_strings[] = {
+	'T', 'R', 'C', 'D', 'B', 'G', 'I', 'N', 'F',
+	'W', 'R', 'N', 'E', 'R', 'R', 'F', 'T', 'L'
+};
+
+/* Log method names. */
+static const char *duk__log_method_names[] = {
+	"trace", "debug", "info", "warn", "error", "fatal"
+};
+
+/* Constructor. */
+static duk_ret_t duk__logger_constructor(duk_context *ctx) {
+	duk_idx_t nargs;
+
+	/* Calling as a non-constructor is not meaningful. */
+	if (!duk_is_constructor_call(ctx)) {
+		return DUK_RET_TYPE_ERROR;
+	}
+
+	nargs = duk_get_top(ctx);
+	duk_set_top(ctx, 1);
+
+	duk_push_this(ctx);
+
+	/* [ name this ] */
+
+	if (nargs == 0) {
+		/* Automatic defaulting of logger name from caller.  This
+		 * would work poorly with tail calls, but constructor calls
+		 * are currently never tail calls, so tail calls are not an
+		 * issue now.
+		 */
+
+		duk_inspect_callstack_entry(ctx, -2);
+		if (duk_is_object(ctx, -1)) {
+			if (duk_get_prop_string(ctx, -1, "function")) {
+				if (duk_get_prop_string(ctx, -1, "fileName")) {
+					if (duk_is_string(ctx, -1)) {
+						duk_replace(ctx, 0);
+					}
+				}
+			}
+		}
+		/* Leave values on stack on purpose, ignored below. */
+
+		/* Stripping the filename might be a good idea
+		 * ("/foo/bar/quux.js" -> logger name "quux"),
+		 * but now used verbatim.
+		 */
+	}
+	/* The stack is unbalanced here on purpose; we only rely on the
+	 * initial two values: [ name this ].
+	 */
+
+	if (duk_is_string(ctx, 0)) {
+		duk_dup(ctx, 0);
+		duk_put_prop_string(ctx, 1, "n");
+	} else {
+		/* don't set 'n' at all, inherited value is used as name */
+	}
+
+	duk_compact(ctx, 1);
+
+	return 0;  /* keep default instance */
+}
+
+/* Default function to format objects.  Tries to use toLogString() but falls
+ * back to toString().  Any errors are propagated out without catching.
+ */
+static duk_ret_t duk__logger_prototype_fmt(duk_context *ctx) {
+	if (duk_get_prop_string(ctx, 0, "toLogString")) {
+		/* [ arg toLogString ] */
+
+		duk_dup(ctx, 0);
+		duk_call_method(ctx, 0);
+
+		/* [ arg result ] */
+		return 1;
+	}
+
+	/* [ arg undefined ] */
+	duk_pop(ctx);
+	duk_to_string(ctx, 0);
+	return 1;
+}
+
+/* Default function to write a formatted log line.  Writes to stderr,
+ * appending a newline to the log line.
+ *
+ * The argument is a buffer; avoid coercing the buffer to a string to
+ * avoid string table traffic.
+ */
+static duk_ret_t duk__logger_prototype_raw(duk_context *ctx) {
+	const char *data;
+	duk_size_t data_len;
+
+	data = (const char *) duk_require_buffer(ctx, 0, &data_len);
+	fwrite((const void *) data, 1, data_len, stderr);
+	fputc((int) '\n', stderr);
+#if defined(DUK_LOGGING_FLUSH)
+	fflush(stderr);
+#endif
+	return 0;
+}
+
+/* Log frontend shared helper, magic value indicates log level.  Provides
+ * frontend functions: trace(), debug(), info(), warn(), error(), fatal().
+ * This needs to have small footprint, reasonable performance, minimal
+ * memory churn, etc.
+ */
+static duk_ret_t duk__logger_prototype_log_shared(duk_context *ctx) {
+	duk_double_t now;
+	duk_time_components comp;
+	duk_small_int_t entry_lev;
+	duk_small_int_t logger_lev;
+	duk_int_t nargs;
+	duk_int_t i;
+	duk_size_t tot_len;
+	const duk_uint8_t *arg_str;
+	duk_size_t arg_len;
+	duk_uint8_t *buf, *p;
+	const duk_uint8_t *q;
+	duk_uint8_t date_buf[32];  /* maximum format length is 24+1 (NUL), round up. */
+	duk_size_t date_len;
+	duk_small_int_t rc;
+
+	/* XXX: sanitize to printable (and maybe ASCII) */
+	/* XXX: better multiline */
+
+	/*
+	 *  Logger arguments are:
+	 *
+	 *    magic: log level (0-5)
+	 *    this: logger
+	 *    stack: plain log args
+	 *
+	 *  We want to minimize memory churn so a two-pass approach
+	 *  is used: first pass formats arguments and computes final
+	 *  string length, second pass copies strings into a buffer
+	 *  allocated directly with the correct size.  If the backend
+	 *  function plays nice, it won't coerce the buffer to a string
+	 *  (and thus intern it).
+	 */
+
+	entry_lev = duk_get_current_magic(ctx);
+	if (entry_lev < DUK_LOG_TRACE || entry_lev > DUK_LOG_FATAL) {
+		/* Should never happen, check just in case. */
+		return 0;
+	}
+	nargs = duk_get_top(ctx);
+
+	/* [ arg1 ... argN this ] */
+
+	/*
+	 *  Log level check
+	 */
+
+	duk_push_this(ctx);
+
+	duk_get_prop_string(ctx, -1, "l");
+	logger_lev = (duk_small_int_t) duk_get_int(ctx, -1);
+	if (entry_lev < logger_lev) {
+		return 0;
+	}
+	/* log level could be popped but that's not necessary */
+
+	now = duk_get_now(ctx);
+	duk_time_to_components(ctx, now, &comp);
+	sprintf((char *) date_buf, "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ",
+	        (int) comp.year, (int) comp.month + 1, (int) comp.day,
+	        (int) comp.hours, (int) comp.minutes, (int) comp.seconds,
+	        (int) comp.milliseconds);
+
+	date_len = strlen((const char *) date_buf);
+
+	duk_get_prop_string(ctx, -2, "n");
+	duk_to_string(ctx, -1);
+
+	/* [ arg1 ... argN this loggerLevel loggerName ] */
+
+	/*
+	 *  Pass 1
+	 */
+
+	/* Line format: <time> <entryLev> <loggerName>: <msg> */
+
+	tot_len = 0;
+	tot_len += 3 +  /* separators: space, space, colon */
+	           3 +  /* level string */
+	           date_len +  /* time */
+	           duk_get_length(ctx, -1);  /* loggerName */
+
+	for (i = 0; i < nargs; i++) {
+		/* When formatting an argument to a string, errors may happen from multiple
+		 * causes.  In general we want to catch obvious errors like a toLogString()
+		 * throwing an error, but we don't currently try to catch every possible
+		 * error.  In particular, internal errors (like out of memory or stack) are
+		 * not caught.  Also, we expect Error toString() to not throw an error.
+		 */
+		if (duk_is_object(ctx, i)) {
+			/* duk_pcall_prop() may itself throw an error, but we're content
+			 * in catching the obvious errors (like toLogString() throwing an
+			 * error).
+			 */
+			duk_push_string(ctx, "fmt");
+			duk_dup(ctx, i);
+			/* [ arg1 ... argN this loggerLevel loggerName 'fmt' arg ] */
+			/* call: this.fmt(arg) */
+			rc = duk_pcall_prop(ctx, -5 /*obj_index*/, 1 /*nargs*/);
+			if (rc) {
+				/* Keep the error as the result (coercing it might fail below,
+				 * but we don't catch that now).
+				 */
+				;
+			}
+			duk_replace(ctx, i);
+		}
+		(void) duk_to_lstring(ctx, i, &arg_len);
+		tot_len++;  /* sep (even before first one) */
+		tot_len += arg_len;
+	}
+
+	/*
+	 *  Pass 2
+	 */
+
+	/* XXX: Here it'd be nice if we didn't need to allocate a new fixed
+	 * buffer for every write.  This would be possible if raw() took a
+	 * buffer and a length.  We could then use a preallocated buffer for
+	 * most log writes and request raw() to write a partial buffer.
+	 */
+
+	buf = (duk_uint8_t *) duk_push_fixed_buffer(ctx, tot_len);
+	p = buf;
+
+	memcpy((void *) p, (const void *) date_buf, (size_t) date_len);
+	p += date_len;
+	*p++ = (duk_uint8_t) ' ';
+
+	q = (const duk_uint8_t *) duk__log_level_strings + (entry_lev * 3);
+	memcpy((void *) p, (const void *) q, (size_t) 3);
+	p += 3;
+
+	*p++ = (duk_uint8_t) ' ';
+
+	arg_str = (const duk_uint8_t *) duk_get_lstring(ctx, -2, &arg_len);
+	memcpy((void *) p, (const void *) arg_str, (size_t) arg_len);
+	p += arg_len;
+
+	*p++ = (duk_uint8_t) ':';
+
+	for (i = 0; i < nargs; i++) {
+		*p++ = (duk_uint8_t) ' ';
+
+		arg_str = (const duk_uint8_t *) duk_get_lstring(ctx, i, &arg_len);
+		memcpy((void *) p, (const void *) arg_str, (size_t) arg_len);
+		p += arg_len;
+	}
+
+	/* [ arg1 ... argN this loggerLevel loggerName buffer ] */
+
+	/* Call this.raw(msg); look up through the instance allows user to override
+	 * the raw() function in the instance or in the prototype for maximum
+	 * flexibility.
+	 */
+	duk_push_string(ctx, "raw");
+	duk_dup(ctx, -2);
+	/* [ arg1 ... argN this loggerLevel loggerName buffer 'raw' buffer ] */
+	duk_call_prop(ctx, -6, 1);  /* this.raw(buffer) */
+
+	return 0;
+}
+
+void duk_log_va(duk_context *ctx, duk_int_t level, const char *fmt, va_list ap) {
+	if (level < 0) {
+		level = 0;
+	} else if (level > (int) (sizeof(duk__log_method_names) / sizeof(const char *)) - 1) {
+		level = (int) (sizeof(duk__log_method_names) / sizeof(const char *)) - 1;
+	}
+
+	duk_push_global_stash(ctx);
+	duk_get_prop_string(ctx, -1, "\xff" "logger:constructor");  /* fixed at init time */
+	duk_get_prop_string(ctx, -1, "clog");
+	duk_get_prop_string(ctx, -1, duk__log_method_names[level]);
+	duk_dup(ctx, -2);
+	duk_push_vsprintf(ctx, fmt, ap);
+
+	/* [ ... stash Logger clog logfunc clog(=this) msg ] */
+
+	duk_call_method(ctx, 1 /*nargs*/);
+
+	/* [ ... stash Logger clog res ] */
+
+	duk_pop_n(ctx, 4);
+}
+
+void duk_log(duk_context *ctx, duk_int_t level, const char *fmt, ...) {
+	va_list ap;
+
+	va_start(ap, fmt);
+	duk_log_va(ctx, level, fmt, ap);
+	va_end(ap);
+}
+
+void duk_logging_init(duk_context *ctx, duk_uint_t flags) {
+	/* XXX: Add .name property for logger functions (useful for stack traces if they throw). */
+
+	(void) flags;
+
+	duk_eval_string(ctx,
+		"(function(cons,prot){"
+		"Object.defineProperty(Duktape,'Logger',{value:cons,writable:true,configurable:true});"
+		"Object.defineProperty(cons,'prototype',{value:prot});"
+		"Object.defineProperty(cons,'clog',{value:new Duktape.Logger('C'),writable:true,configurable:true});"
+		"});");
+
+	duk_push_c_function(ctx, duk__logger_constructor, DUK_VARARGS /*nargs*/);  /* Duktape.Logger */
+	duk_push_object(ctx);  /* Duktape.Logger.prototype */
+
+	/* [ ... func Duktape.Logger Duktape.Logger.prototype ] */
+
+	duk_push_string(ctx, "name");
+	duk_push_string(ctx, "Logger");
+	duk_def_prop(ctx, -4, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_FORCE);
+
+	duk_dup_top(ctx);
+	duk_put_prop_string(ctx, -2, "constructor");
+	duk_push_int(ctx, 2);
+	duk_put_prop_string(ctx, -2, "l");
+	duk_push_string(ctx, "anon");
+	duk_put_prop_string(ctx, -2, "n");
+	duk_push_c_function(ctx, duk__logger_prototype_fmt, 1 /*nargs*/);
+	duk_put_prop_string(ctx, -2, "fmt");
+	duk_push_c_function(ctx, duk__logger_prototype_raw, 1 /*nargs*/);
+	duk_put_prop_string(ctx, -2, "raw");
+	duk_push_c_function(ctx, duk__logger_prototype_log_shared, DUK_VARARGS /*nargs*/);
+	duk_set_magic(ctx, -1, 0);  /* magic=0: trace */
+	duk_put_prop_string(ctx, -2, "trace");
+	duk_push_c_function(ctx, duk__logger_prototype_log_shared, DUK_VARARGS /*nargs*/);
+	duk_set_magic(ctx, -1, 1);  /* magic=1: debug */
+	duk_put_prop_string(ctx, -2, "debug");
+	duk_push_c_function(ctx, duk__logger_prototype_log_shared, DUK_VARARGS /*nargs*/);
+	duk_set_magic(ctx, -1, 2);  /* magic=2: info */
+	duk_put_prop_string(ctx, -2, "info");
+	duk_push_c_function(ctx, duk__logger_prototype_log_shared, DUK_VARARGS /*nargs*/);
+	duk_set_magic(ctx, -1, 3);  /* magic=3: warn */
+	duk_put_prop_string(ctx, -2, "warn");
+	duk_push_c_function(ctx, duk__logger_prototype_log_shared, DUK_VARARGS /*nargs*/);
+	duk_set_magic(ctx, -1, 4);  /* magic=4: error */
+	duk_put_prop_string(ctx, -2, "error");
+	duk_push_c_function(ctx, duk__logger_prototype_log_shared, DUK_VARARGS /*nargs*/);
+	duk_set_magic(ctx, -1, 5);  /* magic=5: fatal */
+	duk_put_prop_string(ctx, -2, "fatal");
+
+	/* [ ... func Duktape.Logger Duktape.Logger.prototype ] */
+
+	/* XXX: when using ROM built-ins, "Duktape" is read-only by default so
+	 * setting Duktape.Logger will now fail.
+	 */
+
+	/* [ ... func Duktape.Logger Duktape.Logger.prototype ] */
+
+	duk_call(ctx, 2);
+	duk_pop(ctx);
+}

+ 28 - 0
Source/ThirdParty/Duktape/duk_logging.h

@@ -0,0 +1,28 @@
+#if !defined(DUK_LOGGING_H_INCLUDED)
+#define DUK_LOGGING_H_INCLUDED
+
+#include "duktape.h"
+
+/* Log levels */
+#define DUK_LOG_TRACE                     0
+#define DUK_LOG_DEBUG                     1
+#define DUK_LOG_INFO                      2
+#define DUK_LOG_WARN                      3
+#define DUK_LOG_ERROR                     4
+#define DUK_LOG_FATAL                     5
+
+/* No flags at the moment. */
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+extern void duk_logging_init(duk_context *ctx, duk_uint_t flags);
+extern void duk_log_va(duk_context *ctx, duk_int_t level, const char *fmt, va_list ap);
+extern void duk_log(duk_context *ctx, duk_int_t level, const char *fmt, ...);
+
+#if defined(__cplusplus)
+} // extern "C"
+#endif
+
+#endif  /* DUK_LOGGING_H_INCLUDED */

+ 463 - 0
Source/ThirdParty/Duktape/duk_module_duktape.c

@@ -0,0 +1,463 @@
+/*
+ *  Duktape 1.x compatible module loading framework
+ */
+
+#include "duktape.h"
+#include "duk_module_duktape.h"
+
+#if 0  /* Enable manually */
+#define DUK__ASSERT(x) do { \
+		if (!(x)) { \
+			fprintf(stderr, "ASSERTION FAILED at %s:%d: " #x "\n", __FILE__, __LINE__); \
+			fflush(stderr);  \
+		} \
+	} while (0)
+#define DUK__ASSERT_TOP(ctx,val) do { \
+		DUK__ASSERT(duk_get_top((ctx)) == (val)); \
+	} while (0)
+#else
+#define DUK__ASSERT(x) do { (void) (x); } while (0)
+#define DUK__ASSERT_TOP(ctx,val) do { (void) ctx; (void) (val); } while (0)
+#endif
+
+static void duk__resolve_module_id(duk_context *ctx, const char *req_id, const char *mod_id) {
+	duk_uint8_t buf[DUK_COMMONJS_MODULE_ID_LIMIT];
+	duk_uint8_t *p;
+	duk_uint8_t *q;
+	duk_uint8_t *q_last;  /* last component */
+	duk_int_t int_rc;
+
+	DUK__ASSERT(req_id != NULL);
+	/* mod_id may be NULL */
+
+	/*
+	 *  A few notes on the algorithm:
+	 *
+	 *    - Terms are not allowed to begin with a period unless the term
+	 *      is either '.' or '..'.  This simplifies implementation (and
+	 *      is within CommonJS modules specification).
+	 *
+	 *    - There are few output bound checks here.  This is on purpose:
+	 *      the resolution input is length checked and the output is never
+	 *      longer than the input.  The resolved output is written directly
+	 *      over the input because it's never longer than the input at any
+	 *      point in the algorithm.
+	 *
+	 *    - Non-ASCII characters are processed as individual bytes and
+	 *      need no special treatment.  However, U+0000 terminates the
+	 *      algorithm; this is not an issue because U+0000 is not a
+	 *      desirable term character anyway.
+	 */
+
+	/*
+	 *  Set up the resolution input which is the requested ID directly
+	 *  (if absolute or no current module path) or with current module
+	 *  ID prepended (if relative and current module path exists).
+	 *
+	 *  Suppose current module is 'foo/bar' and relative path is './quux'.
+	 *  The 'bar' component must be replaced so the initial input here is
+	 *  'foo/bar/.././quux'.
+	 */
+
+	if (mod_id != NULL && req_id[0] == '.') {
+		int_rc = snprintf((char *) buf, sizeof(buf), "%s/../%s", mod_id, req_id);
+	} else {
+		int_rc = snprintf((char *) buf, sizeof(buf), "%s", req_id);
+	}
+	if (int_rc >= (duk_int_t) sizeof(buf) || int_rc < 0) {
+		/* Potentially truncated, NUL not guaranteed in any case.
+		 * The (int_rc < 0) case should not occur in practice.
+		 */
+		goto resolve_error;
+	}
+	DUK__ASSERT(strlen((const char *) buf) < sizeof(buf));  /* at most sizeof(buf) - 1 */
+
+	/*
+	 *  Resolution loop.  At the top of the loop we're expecting a valid
+	 *  term: '.', '..', or a non-empty identifier not starting with a period.
+	 */
+
+	p = buf;
+	q = buf;
+	for (;;) {
+		duk_uint_fast8_t c;
+
+		/* Here 'p' always points to the start of a term.
+		 *
+		 * We can also unconditionally reset q_last here: if this is
+		 * the last (non-empty) term q_last will have the right value
+		 * on loop exit.
+		 */
+
+		DUK__ASSERT(p >= q);  /* output is never longer than input during resolution */
+
+		q_last = q;
+
+		c = *p++;
+		if (c == 0) {
+			goto resolve_error;
+		} else if (c == '.') {
+			c = *p++;
+			if (c == '/') {
+				/* Term was '.' and is eaten entirely (including dup slashes). */
+				goto eat_dup_slashes;
+			}
+			if (c == '.' && *p == '/') {
+				/* Term was '..', backtrack resolved name by one component.
+				 *  q[-1] = previous slash (or beyond start of buffer)
+				 *  q[-2] = last char of previous component (or beyond start of buffer)
+				 */
+				p++;  /* eat (first) input slash */
+				DUK__ASSERT(q >= buf);
+				if (q == buf) {
+					goto resolve_error;
+				}
+				DUK__ASSERT(*(q - 1) == '/');
+				q--;  /* Backtrack to last output slash (dups already eliminated). */
+				for (;;) {
+					/* Backtrack to previous slash or start of buffer. */
+					DUK__ASSERT(q >= buf);
+					if (q == buf) {
+						break;
+					}
+					if (*(q - 1) == '/') {
+						break;
+					}
+					q--;
+				}
+				goto eat_dup_slashes;
+			}
+			goto resolve_error;
+		} else if (c == '/') {
+			/* e.g. require('/foo'), empty terms not allowed */
+			goto resolve_error;
+		} else {
+			for (;;) {
+				/* Copy term name until end or '/'. */
+				*q++ = c;
+				c = *p++;
+				if (c == 0) {
+					/* This was the last term, and q_last was
+					 * updated to match this term at loop top.
+					 */
+					goto loop_done;
+				} else if (c == '/') {
+					*q++ = '/';
+					break;
+				} else {
+					/* write on next loop */
+				}
+			}
+		}
+
+	 eat_dup_slashes:
+		for (;;) {
+			/* eat dup slashes */
+			c = *p;
+			if (c != '/') {
+				break;
+			}
+			p++;
+		}
+	}
+ loop_done:
+	/* Output #1: resolved absolute name. */
+	DUK__ASSERT(q >= buf);
+	duk_push_lstring(ctx, (const char *) buf, (size_t) (q - buf));
+
+	/* Output #2: last component name. */
+	DUK__ASSERT(q >= q_last);
+	DUK__ASSERT(q_last >= buf);
+	duk_push_lstring(ctx, (const char *) q_last, (size_t) (q - q_last));
+	return;
+
+ resolve_error:
+	duk_error(ctx, DUK_ERR_TYPE_ERROR, "cannot resolve module id: %s", (const char *) req_id);
+}
+
+/* Stack indices for better readability. */
+#define DUK__IDX_REQUESTED_ID   0   /* module id requested */
+#define DUK__IDX_REQUIRE        1   /* current require() function */
+#define DUK__IDX_REQUIRE_ID     2   /* the base ID of the current require() function, resolution base */
+#define DUK__IDX_RESOLVED_ID    3   /* resolved, normalized absolute module ID */
+#define DUK__IDX_LASTCOMP       4   /* last component name in resolved path */
+#define DUK__IDX_DUKTAPE        5   /* Duktape object */
+#define DUK__IDX_MODLOADED      6   /* Duktape.modLoaded[] module cache */
+#define DUK__IDX_UNDEFINED      7   /* 'undefined', artifact of lookup */
+#define DUK__IDX_FRESH_REQUIRE  8   /* new require() function for module, updated resolution base */
+#define DUK__IDX_EXPORTS        9   /* default exports table */
+#define DUK__IDX_MODULE         10  /* module object containing module.exports, etc */
+
+static duk_ret_t duk__require(duk_context *ctx) {
+	const char *str_req_id;  /* requested identifier */
+	const char *str_mod_id;  /* require.id of current module */
+	duk_int_t pcall_rc;
+
+	/* NOTE: we try to minimize code size by avoiding unnecessary pops,
+	 * so the stack looks a bit cluttered in this function.  DUK__ASSERT_TOP()
+	 * assertions are used to ensure stack configuration is correct at each
+	 * step.
+	 */
+
+	/*
+	 *  Resolve module identifier into canonical absolute form.
+	 */
+
+	str_req_id = duk_require_string(ctx, DUK__IDX_REQUESTED_ID);
+	duk_push_current_function(ctx);
+	duk_get_prop_string(ctx, -1, "id");
+	str_mod_id = duk_get_string(ctx, DUK__IDX_REQUIRE_ID);  /* ignore non-strings */
+	duk__resolve_module_id(ctx, str_req_id, str_mod_id);
+	str_req_id = NULL;
+	str_mod_id = NULL;
+
+	/* [ requested_id require require.id resolved_id last_comp ] */
+	DUK__ASSERT_TOP(ctx, DUK__IDX_LASTCOMP + 1);
+
+	/*
+	 *  Cached module check.
+	 *
+	 *  If module has been loaded or its loading has already begun without
+	 *  finishing, return the same cached value (module.exports).  The
+	 *  value is registered when module load starts so that circular
+	 *  references can be supported to some extent.
+	 */
+
+	duk_push_global_stash(ctx);
+	duk_get_prop_string(ctx, -1, "\xff" "module:Duktape");
+	duk_remove(ctx, -2);  /* Lookup stashed, original 'Duktape' object. */
+	duk_get_prop_string(ctx, DUK__IDX_DUKTAPE, "modLoaded");  /* Duktape.modLoaded */
+	duk_require_type_mask(ctx, DUK__IDX_MODLOADED, DUK_TYPE_MASK_OBJECT);
+	DUK__ASSERT_TOP(ctx, DUK__IDX_MODLOADED + 1);
+
+	duk_dup(ctx, DUK__IDX_RESOLVED_ID);
+	if (duk_get_prop(ctx, DUK__IDX_MODLOADED)) {
+		/* [ requested_id require require.id resolved_id last_comp Duktape Duktape.modLoaded Duktape.modLoaded[id] ] */
+		duk_get_prop_string(ctx, -1, "exports");  /* return module.exports */
+		return 1;
+	}
+	DUK__ASSERT_TOP(ctx, DUK__IDX_UNDEFINED + 1);
+
+	/* [ requested_id require require.id resolved_id last_comp Duktape Duktape.modLoaded undefined ] */
+
+	/*
+	 *  Module not loaded (and loading not started previously).
+	 *
+	 *  Create a new require() function with 'id' set to resolved ID
+	 *  of module being loaded.  Also create 'exports' and 'module'
+	 *  tables but don't register exports to the loaded table yet.
+	 *  We don't want to do that unless the user module search callbacks
+	 *  succeeds in finding the module.
+	 */
+
+	/* Fresh require: require.id is left configurable (but not writable)
+	 * so that is not easy to accidentally tweak it, but it can still be
+	 * done with Object.defineProperty().
+	 *
+	 * XXX: require.id could also be just made non-configurable, as there
+	 * is no practical reason to touch it (at least from Ecmascript code).
+	 */
+	duk_push_c_function(ctx, duk__require, 1 /*nargs*/);
+	duk_push_string(ctx, "name");
+	duk_push_string(ctx, "require");
+	duk_def_prop(ctx, DUK__IDX_FRESH_REQUIRE, DUK_DEFPROP_HAVE_VALUE);  /* not writable, not enumerable, not configurable */
+	duk_push_string(ctx, "id");
+	duk_dup(ctx, DUK__IDX_RESOLVED_ID);
+	duk_def_prop(ctx, DUK__IDX_FRESH_REQUIRE, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_SET_CONFIGURABLE);  /* a fresh require() with require.id = resolved target module id */
+
+	/* Module table:
+	 * - module.exports: initial exports table (may be replaced by user)
+	 * - module.id is non-writable and non-configurable, as the CommonJS
+	 *   spec suggests this if possible
+	 * - module.filename: not set, defaults to resolved ID if not explicitly
+	 *   set by modSearch() (note capitalization, not .fileName, matches Node.js)
+	 * - module.name: not set, defaults to last component of resolved ID if
+	 *   not explicitly set by modSearch()
+	 */
+	duk_push_object(ctx);  /* exports */
+	duk_push_object(ctx);  /* module */
+	duk_push_string(ctx, "exports");
+	duk_dup(ctx, DUK__IDX_EXPORTS);
+	duk_def_prop(ctx, DUK__IDX_MODULE, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_SET_WRITABLE | DUK_DEFPROP_SET_CONFIGURABLE);  /* module.exports = exports */
+	duk_push_string(ctx, "id");
+	duk_dup(ctx, DUK__IDX_RESOLVED_ID);  /* resolved id: require(id) must return this same module */
+	duk_def_prop(ctx, DUK__IDX_MODULE, DUK_DEFPROP_HAVE_VALUE);  /* module.id = resolved_id; not writable, not enumerable, not configurable */
+	duk_compact(ctx, DUK__IDX_MODULE);  /* module table remains registered to modLoaded, minimize its size */
+	DUK__ASSERT_TOP(ctx, DUK__IDX_MODULE + 1);
+
+	/* [ requested_id require require.id resolved_id last_comp Duktape Duktape.modLoaded undefined fresh_require exports module ] */
+
+	/* Register the module table early to modLoaded[] so that we can
+	 * support circular references even in modSearch().  If an error
+	 * is thrown, we'll delete the reference.
+	 */
+	duk_dup(ctx, DUK__IDX_RESOLVED_ID);
+	duk_dup(ctx, DUK__IDX_MODULE);
+	duk_put_prop(ctx, DUK__IDX_MODLOADED);  /* Duktape.modLoaded[resolved_id] = module */
+
+	/*
+	 *  Call user provided module search function and build the wrapped
+	 *  module source code (if necessary).  The module search function
+	 *  can be used to implement pure Ecmacsript, pure C, and mixed
+	 *  Ecmascript/C modules.
+	 *
+	 *  The module search function can operate on the exports table directly
+	 *  (e.g. DLL code can register values to it).  It can also return a
+	 *  string which is interpreted as module source code (if a non-string
+	 *  is returned the module is assumed to be a pure C one).  If a module
+	 *  cannot be found, an error must be thrown by the user callback.
+	 *
+	 *  Because Duktape.modLoaded[] already contains the module being
+	 *  loaded, circular references for C modules should also work
+	 *  (although expected to be quite rare).
+	 */
+
+	duk_push_string(ctx, "(function(require,exports,module){");
+
+	/* Duktape.modSearch(resolved_id, fresh_require, exports, module). */
+	duk_get_prop_string(ctx, DUK__IDX_DUKTAPE, "modSearch");  /* Duktape.modSearch */
+	duk_dup(ctx, DUK__IDX_RESOLVED_ID);
+	duk_dup(ctx, DUK__IDX_FRESH_REQUIRE);
+	duk_dup(ctx, DUK__IDX_EXPORTS);
+	duk_dup(ctx, DUK__IDX_MODULE);  /* [ ... Duktape.modSearch resolved_id last_comp fresh_require exports module ] */
+	pcall_rc = duk_pcall(ctx, 4 /*nargs*/);  /* -> [ ... source ] */
+	DUK__ASSERT_TOP(ctx, DUK__IDX_MODULE + 3);
+
+	if (pcall_rc != DUK_EXEC_SUCCESS) {
+		/* Delete entry in Duktape.modLoaded[] and rethrow. */
+		goto delete_rethrow;
+	}
+
+	/* If user callback did not return source code, module loading
+	 * is finished (user callback initialized exports table directly).
+	 */
+	if (!duk_is_string(ctx, -1)) {
+		/* User callback did not return source code, so module loading
+		 * is finished: just update modLoaded with final module.exports
+		 * and we're done.
+		 */
+		goto return_exports;
+	}
+
+	/* Finish the wrapped module source.  Force module.filename as the
+	 * function .fileName so it gets set for functions defined within a
+	 * module.  This also ensures loggers created within the module get
+	 * the module ID (or overridden filename) as their default logger name.
+	 * (Note capitalization: .filename matches Node.js while .fileName is
+	 * used elsewhere in Duktape.)
+	 */
+	duk_push_string(ctx, "})");
+	duk_concat(ctx, 3);
+	if (!duk_get_prop_string(ctx, DUK__IDX_MODULE, "filename")) {
+		/* module.filename for .fileName, default to resolved ID if
+		 * not present.
+		 */
+		duk_pop(ctx);
+		duk_dup(ctx, DUK__IDX_RESOLVED_ID);
+	}
+	pcall_rc = duk_pcompile(ctx, DUK_COMPILE_EVAL);
+	if (pcall_rc != DUK_EXEC_SUCCESS) {
+		goto delete_rethrow;
+	}
+	pcall_rc = duk_pcall(ctx, 0);  /* -> eval'd function wrapper (not called yet) */
+	if (pcall_rc != DUK_EXEC_SUCCESS) {
+		goto delete_rethrow;
+	}
+
+	/* Module has now evaluated to a wrapped module function.  Force its
+	 * .name to match module.name (defaults to last component of resolved
+	 * ID) so that it is shown in stack traces too.  Note that we must not
+	 * introduce an actual name binding into the function scope (which is
+	 * usually the case with a named function) because it would affect the
+	 * scope seen by the module and shadow accesses to globals of the same name.
+	 * This is now done by compiling the function as anonymous and then forcing
+	 * its .name without setting a "has name binding" flag.
+	 */
+
+	duk_push_string(ctx, "name");
+	if (!duk_get_prop_string(ctx, DUK__IDX_MODULE, "name")) {
+		/* module.name for .name, default to last component if
+		 * not present.
+		 */
+		duk_pop(ctx);
+		duk_dup(ctx, DUK__IDX_LASTCOMP);
+	}
+	duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_FORCE);
+
+	/*
+	 *  Call the wrapped module function.
+	 *
+	 *  Use a protected call so that we can update Duktape.modLoaded[resolved_id]
+	 *  even if the module throws an error.
+	 */
+
+	/* [ requested_id require require.id resolved_id last_comp Duktape Duktape.modLoaded undefined fresh_require exports module mod_func ] */
+	DUK__ASSERT_TOP(ctx, DUK__IDX_MODULE + 2);
+
+	duk_dup(ctx, DUK__IDX_EXPORTS);  /* exports (this binding) */
+	duk_dup(ctx, DUK__IDX_FRESH_REQUIRE);  /* fresh require (argument) */
+	duk_get_prop_string(ctx, DUK__IDX_MODULE, "exports");  /* relookup exports from module.exports in case it was changed by modSearch */
+	duk_dup(ctx, DUK__IDX_MODULE);  /* module (argument) */
+	DUK__ASSERT_TOP(ctx, DUK__IDX_MODULE + 6);
+
+	/* [ requested_id require require.id resolved_id last_comp Duktape Duktape.modLoaded undefined fresh_require exports module mod_func exports fresh_require exports module ] */
+
+	pcall_rc = duk_pcall_method(ctx, 3 /*nargs*/);
+	if (pcall_rc != DUK_EXEC_SUCCESS) {
+		/* Module loading failed.  Node.js will forget the module
+		 * registration so that another require() will try to load
+		 * the module again.  Mimic that behavior.
+		 */
+		goto delete_rethrow;
+	}
+
+	/* [ requested_id require require.id resolved_id last_comp Duktape Duktape.modLoaded undefined fresh_require exports module result(ignored) ] */
+	DUK__ASSERT_TOP(ctx, DUK__IDX_MODULE + 2);
+
+	/* fall through */
+
+ return_exports:
+	duk_get_prop_string(ctx, DUK__IDX_MODULE, "exports");
+	duk_compact(ctx, -1);  /* compact the exports table */
+	return 1;  /* return module.exports */
+
+ delete_rethrow:
+	duk_dup(ctx, DUK__IDX_RESOLVED_ID);
+	duk_del_prop(ctx, DUK__IDX_MODLOADED);  /* delete Duktape.modLoaded[resolved_id] */
+	duk_throw(ctx);  /* rethrow original error */
+	return 0;  /* not reachable */
+}
+
+void duk_module_duktape_init(duk_context *ctx) {
+	/* Stash 'Duktape' in case it's modified. */
+	duk_push_global_stash(ctx);
+	duk_get_global_string(ctx, "Duktape");
+	duk_put_prop_string(ctx, -2, "\xff" "module:Duktape");
+	duk_pop(ctx);
+
+	/* Register `require` as a global function. */
+	duk_eval_string(ctx,
+		"(function(req){"
+		"var D=Object.defineProperty;"
+		"D(req,'name',{value:'require'});"
+		"D(this,'require',{value:req,writable:true,configurable:true});"
+		"D(Duktape,'modLoaded',{value:Object.create(null),writable:true,configurable:true});"
+		"})");
+	duk_push_c_function(ctx, duk__require, 1 /*nargs*/);
+	duk_call(ctx, 1);
+	duk_pop(ctx);
+}
+
+#undef DUK__ASSERT
+#undef DUK__ASSERT_TOP
+#undef DUK__IDX_REQUESTED_ID
+#undef DUK__IDX_REQUIRE
+#undef DUK__IDX_REQUIRE_ID
+#undef DUK__IDX_RESOLVED_ID
+#undef DUK__IDX_LASTCOMP
+#undef DUK__IDX_DUKTAPE
+#undef DUK__IDX_MODLOADED
+#undef DUK__IDX_UNDEFINED
+#undef DUK__IDX_FRESH_REQUIRE
+#undef DUK__IDX_EXPORTS
+#undef DUK__IDX_MODULE

+ 22 - 0
Source/ThirdParty/Duktape/duk_module_duktape.h

@@ -0,0 +1,22 @@
+#if !defined(DUK_MODULE_DUKTAPE_H_INCLUDED)
+#define DUK_MODULE_DUKTAPE_H_INCLUDED
+
+#include "duktape.h"
+
+/* Maximum length of CommonJS module identifier to resolve.  Length includes
+ * both current module ID, requested (possibly relative) module ID, and a
+ * slash in between.
+ */
+#define  DUK_COMMONJS_MODULE_ID_LIMIT  256
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+extern void duk_module_duktape_init(duk_context *ctx);
+
+#if defined(__cplusplus)
+} // extern "C"
+#endif
+
+#endif  /* DUK_MODULE_DUKTAPE_H_INCLUDED */

+ 131 - 0
Source/ThirdParty/Duktape/duk_v1_compat.c

@@ -0,0 +1,131 @@
+#include <stdio.h>
+#include "duktape.h"
+#include "duk_v1_compat.h"
+
+/*
+ *  duk_dump_context_{stdout,stderr}()
+ */
+
+void duk_dump_context_stdout(duk_context *ctx) {
+	duk_push_context_dump(ctx);
+	fprintf(stdout, "%s\n", duk_safe_to_string(ctx, -1));
+	duk_pop(ctx);
+}
+
+void duk_dump_context_stderr(duk_context *ctx) {
+	duk_push_context_dump(ctx);
+	fprintf(stderr, "%s\n", duk_safe_to_string(ctx, -1));
+	duk_pop(ctx);
+}
+
+/*
+ *  duk_push_string_file() and duk_push_string_file_raw()
+ */
+
+const char *duk_push_string_file_raw(duk_context *ctx, const char *path, duk_uint_t flags) {
+	FILE *f = NULL;
+	char *buf;
+	long sz;  /* ANSI C typing */
+
+	if (!path) {
+		goto fail;
+	}
+	f = fopen(path, "rb");
+	if (!f) {
+		goto fail;
+	}
+	if (fseek(f, 0, SEEK_END) < 0) {
+		goto fail;
+	}
+	sz = ftell(f);
+	if (sz < 0) {
+		goto fail;
+	}
+	if (fseek(f, 0, SEEK_SET) < 0) {
+		goto fail;
+	}
+	buf = (char *) duk_push_fixed_buffer(ctx, (duk_size_t) sz);
+	if ((size_t) fread(buf, 1, (size_t) sz, f) != (size_t) sz) {
+		duk_pop(ctx);
+		goto fail;
+	}
+	(void) fclose(f);  /* ignore fclose() error */
+	return duk_buffer_to_string(ctx, -1);
+
+ fail:
+	if (f) {
+		(void) fclose(f);  /* ignore fclose() error */
+	}
+
+	if (flags & DUK_STRING_PUSH_SAFE) {
+		duk_push_undefined(ctx);
+	} else {
+		duk_error(ctx, DUK_ERR_TYPE_ERROR, "read file error");
+	}
+	return NULL;
+}
+
+/*
+ *  duk_eval_file(), duk_compile_file(), and their variants
+ */
+
+void duk_eval_file(duk_context *ctx, const char *path) {
+	duk_push_string_file_raw(ctx, path, 0);
+	duk_push_string(ctx, path);
+	duk_compile(ctx, DUK_COMPILE_EVAL);
+	duk_push_global_object(ctx);  /* 'this' binding */
+	duk_call_method(ctx, 0);
+}
+
+void duk_eval_file_noresult(duk_context *ctx, const char *path) {
+	duk_eval_file(ctx, path);
+	duk_pop(ctx);
+}
+
+duk_int_t duk_peval_file(duk_context *ctx, const char *path) {
+	duk_int_t rc;
+
+	duk_push_string_file_raw(ctx, path, DUK_STRING_PUSH_SAFE);
+	duk_push_string(ctx, path);
+	rc = duk_pcompile(ctx, DUK_COMPILE_EVAL);
+	if (rc != 0) {
+		return rc;
+	}
+	duk_push_global_object(ctx);  /* 'this' binding */
+	rc = duk_pcall_method(ctx, 0);
+	return rc;
+}
+
+duk_int_t duk_peval_file_noresult(duk_context *ctx, const char *path) {
+	duk_int_t rc;
+
+	rc = duk_peval_file(ctx, path);
+	duk_pop(ctx);
+	return rc;
+}
+
+void duk_compile_file(duk_context *ctx, duk_uint_t flags, const char *path) {
+	duk_push_string_file_raw(ctx, path, 0);
+	duk_push_string(ctx, path);
+	duk_compile(ctx, flags);
+}
+
+duk_int_t duk_pcompile_file(duk_context *ctx, duk_uint_t flags, const char *path) {
+	duk_int_t rc;
+
+	duk_push_string_file_raw(ctx, path, DUK_STRING_PUSH_SAFE);
+	duk_push_string(ctx, path);
+	rc = duk_pcompile(ctx, flags);
+	return rc;
+}
+
+/*
+ *  duk_to_defaultvalue()
+ */
+
+void duk_to_defaultvalue(duk_context *ctx, duk_idx_t idx, duk_int_t hint) {
+	duk_require_type_mask(ctx, idx, DUK_TYPE_MASK_OBJECT |
+	                                DUK_TYPE_MASK_BUFFER |
+	                                DUK_TYPE_MASK_LIGHTFUNC);
+	duk_to_primitive(ctx, idx, hint);
+}

+ 36 - 0
Source/ThirdParty/Duktape/duk_v1_compat.h

@@ -0,0 +1,36 @@
+#if !defined(DUK_V1_COMPAT_INCLUDED)
+#define DUK_V1_COMPAT_INCLUDED
+
+#include "duktape.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/* Straight flag rename */
+#if !defined(DUK_ENUM_INCLUDE_INTERNAL)
+#define DUK_ENUM_INCLUDE_INTERNAL DUK_ENUM_INCLUDE_HIDDEN
+#endif
+
+/* Flags for duk_push_string_file_raw() */
+#define DUK_STRING_PUSH_SAFE              (1 << 0)    /* no error if file does not exist */
+
+extern void duk_dump_context_stdout(duk_context *ctx);
+extern void duk_dump_context_stderr(duk_context *ctx);
+extern const char *duk_push_string_file_raw(duk_context *ctx, const char *path, duk_uint_t flags);
+extern void duk_eval_file(duk_context *ctx, const char *path);
+extern void duk_eval_file_noresult(duk_context *ctx, const char *path);
+extern duk_int_t duk_peval_file(duk_context *ctx, const char *path);
+extern duk_int_t duk_peval_file_noresult(duk_context *ctx, const char *path);
+extern void duk_compile_file(duk_context *ctx, duk_uint_t flags, const char *path);
+extern duk_int_t duk_pcompile_file(duk_context *ctx, duk_uint_t flags, const char *path);
+extern void duk_to_defaultvalue(duk_context *ctx, duk_idx_t idx, duk_int_t hint);
+
+#define duk_push_string_file(ctx,path) \
+	duk_push_string_file_raw((ctx), (path), 0)
+
+#if defined(__cplusplus)
+} // extern "C"
+#endif
+
+#endif  /* DUK_V1_COMPAT_INCLUDED */

File diff suppressed because it is too large
+ 1266 - 2542
Source/ThirdParty/Duktape/duktape.c


File diff suppressed because it is too large
+ 404 - 296
Source/ThirdParty/Duktape/duktape.h


Some files were not shown because too many files changed in this diff