This is an automated email from the ASF dual-hosted git repository.

xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx-apps.git

commit 4439e0c0b5f7aae7e581cb642f77762f6b525cba
Author: Huang Qi <huang...@xiaomi.com>
AuthorDate: Mon Mar 15 18:56:18 2021 +0800

    interpreters/quickjs: Implement a minimal interpreter
    
    Signed-off-by: Huang Qi <huang...@xiaomi.com>
    Change-Id: I782d509f6fcc86ade34eda5dbe5845603b819e0e
---
 interpreters/quickjs/Kconfig   |  12 +
 interpreters/quickjs/Makefile  |   6 +-
 interpreters/quickjs/qjsmini.c | 676 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 693 insertions(+), 1 deletion(-)

diff --git a/interpreters/quickjs/Kconfig b/interpreters/quickjs/Kconfig
index 6db7026..8969226 100644
--- a/interpreters/quickjs/Kconfig
+++ b/interpreters/quickjs/Kconfig
@@ -9,6 +9,10 @@ config INTERPRETERS_QUICKJS
 
 if INTERPRETERS_QUICKJS
 
+config INTERPRETERS_QUICKJS_MINI
+       bool "Minimal interpreter"
+       default n
+
 config INTERPRETERS_QUICKJS_BIGNUM
        bool "Bignum support"
        default n
@@ -24,4 +28,12 @@ config INTERPRETERS_QUICKJS_STACKSIZE
        int "QuickJS interpreter stack size"
        default 8192
 
+config INTERPRETERS_QUICKJS_EXT_HOOK
+       bool "External init/destory hook"
+       default n
+       depends on INTERPRETERS_QUICKJS_MINI
+       ---help---
+               Since minimal interpreter only support 'console.log',
+               you can export custom module by implement init/destory hook.
+
 endif
diff --git a/interpreters/quickjs/Makefile b/interpreters/quickjs/Makefile
index 1a1f2ef..1d42636 100644
--- a/interpreters/quickjs/Makefile
+++ b/interpreters/quickjs/Makefile
@@ -27,9 +27,13 @@ QUICKJS_URL_BASE = https://bellard.org/quickjs/
 QUICKJS_URL      = $(QUICKJS_URL_BASE)/$(QUICKJS_TARBALL)
 
 CSRCS = quickjs.c libregexp.c libbf.c libunicode.c cutils.c
-CSRCS += quickjs-libc.c repl.c
 
+ifeq ($(CONFIG_INTERPRETERS_QUICKJS_MINI),y)
+MAINSRC = qjsmini.c
+else
+CSRCS += quickjs-libc.c repl.c
 MAINSRC = qjs.c
+endif
 
 VERSION=\"$(QUICKJS_VERSION)\"
 
diff --git a/interpreters/quickjs/qjsmini.c b/interpreters/quickjs/qjsmini.c
new file mode 100644
index 0000000..e34cd01
--- /dev/null
+++ b/interpreters/quickjs/qjsmini.c
@@ -0,0 +1,676 @@
+/****************************************************************************
+ * apps/interpreters/quickjs/qjsmini.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <inttypes.h>
+#include <string.h>
+#include <assert.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <time.h>
+#include <malloc.h>
+
+#include <quickjs.h>
+#include <cutils.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define MALLOC_OVERHEAD 8
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+struct trace_malloc_data
+{
+  uint8_t *base;
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: help
+ ****************************************************************************/
+
+static void qjs_help(void)
+{
+  printf("QuickJS version " CONFIG_VERSION "\n"
+         "usage: "
+         "qjs"
+         " [options] [file [args]]\n"
+         "-h  --help         list options\n"
+         "-e  --eval EXPR    evaluate EXPR\n"
+         "-T  --trace        trace memory allocation\n"
+         "-d  --dump         dump the memory usage stats\n"
+         "    --memory-limit n       limit the memory usage to 'n' bytes\n"
+         "    --stack-size n         limit the stack size to 'n' bytes\n"
+         "    --unhandled-rejection  dump unhandled promise rejections\n"
+         "-q  --quit         just instantiate the interpreter and quit\n");
+  exit(1);
+}
+
+/****************************************************************************
+ * Name: js_trace_malloc_ptr_offset
+ ****************************************************************************/
+
+static inline unsigned long long
+js_trace_malloc_ptr_offset(uint8_t *ptr,
+                           struct trace_malloc_data *dp)
+{
+  return ptr - dp->base;
+}
+
+/****************************************************************************
+ * Name: js_trace_malloc_usable_size
+ ****************************************************************************/
+
+static inline size_t js_trace_malloc_usable_size(void *ptr)
+{
+  return malloc_usable_size(ptr);
+}
+
+/****************************************************************************
+ * Name: js_trace_malloc_printf
+ ****************************************************************************/
+
+static void __attribute__((format(printf, 2, 3)))
+js_trace_malloc_printf(JSMallocState *s, const char *fmt, ...)
+{
+  va_list ap;
+  int c;
+
+  va_start(ap, fmt);
+  while ((c = *fmt++) != '\0')
+    {
+      if (c == '%')
+        {
+          /* only handle %p and %zd */
+
+          if (*fmt == 'p')
+            {
+              uint8_t *ptr = va_arg(ap, void *);
+              if (ptr == NULL)
+                {
+                  printf("NULL");
+                }
+              else
+                {
+                  printf("H%+06lld.%zd",
+                        js_trace_malloc_ptr_offset(ptr, s->opaque),
+                        js_trace_malloc_usable_size(ptr));
+                }
+
+              fmt++;
+              continue;
+            }
+
+          if (fmt[0] == 'z' && fmt[1] == 'd')
+            {
+              size_t sz = va_arg(ap, size_t);
+              printf("%zd", sz);
+              fmt += 2;
+              continue;
+            }
+        }
+
+      putc(c, stdout);
+    }
+
+  va_end(ap);
+}
+
+/****************************************************************************
+ * Name: js_trace_malloc_init
+ ****************************************************************************/
+
+static void js_trace_malloc_init(struct trace_malloc_data *s)
+{
+  free(s->base = malloc(8));
+}
+
+/****************************************************************************
+ * Name: js_trace_malloc
+ ****************************************************************************/
+
+static void *js_trace_malloc(JSMallocState *s, size_t size)
+{
+  void *ptr;
+
+  /* Do not allocate zero bytes: behavior is platform dependent */
+
+  assert(size != 0);
+
+  if (unlikely(s->malloc_size + size > s->malloc_limit))
+      return NULL;
+  ptr = malloc(size);
+  js_trace_malloc_printf(s, "A %zd -> %p\n", size, ptr);
+  if (ptr)
+    {
+      s->malloc_count++;
+      s->malloc_size += js_trace_malloc_usable_size(ptr) + MALLOC_OVERHEAD;
+    }
+
+  return ptr;
+}
+
+/****************************************************************************
+ * Name: js_trace_free
+ ****************************************************************************/
+
+static void js_trace_free(JSMallocState *s, void *ptr)
+{
+  if (!ptr)
+    return;
+
+  js_trace_malloc_printf(s, "F %p\n", ptr);
+  s->malloc_count--;
+  s->malloc_size -= js_trace_malloc_usable_size(ptr) + MALLOC_OVERHEAD;
+  free(ptr);
+}
+
+/****************************************************************************
+ * Name: js_trace_realloc
+ ****************************************************************************/
+
+static void *js_trace_realloc(JSMallocState *s, void *ptr, size_t size)
+{
+  size_t old_size;
+
+  if (!ptr)
+    {
+      if (size == 0)
+          return NULL;
+      return js_trace_malloc(s, size);
+    }
+
+  old_size = js_trace_malloc_usable_size(ptr);
+  if (size == 0)
+    {
+      js_trace_malloc_printf(s, "R %zd %p\n", size, ptr);
+      s->malloc_count--;
+      s->malloc_size -= old_size + MALLOC_OVERHEAD;
+      free(ptr);
+      return NULL;
+    }
+
+  if (s->malloc_size + size - old_size > s->malloc_limit)
+      return NULL;
+
+  js_trace_malloc_printf(s, "R %zd %p", size, ptr);
+
+  ptr = realloc(ptr, size);
+  js_trace_malloc_printf(s, " -> %p\n", ptr);
+  if (ptr)
+    {
+      s->malloc_size += js_trace_malloc_usable_size(ptr) - old_size;
+    }
+
+  return ptr;
+}
+
+/****************************************************************************
+ * Name: js_dump_obj
+ ****************************************************************************/
+
+static void js_dump_obj(JSContext *ctx, FILE *f, JSValueConst val)
+{
+  const char *str;
+
+  str = JS_ToCString(ctx, val);
+  if (str)
+    {
+      fprintf(f, "%s\n", str);
+      JS_FreeCString(ctx, str);
+    }
+  else
+    {
+      fprintf(f, "[exception]\n");
+    }
+}
+
+/****************************************************************************
+ * Name: js_std_dump_error1
+ ****************************************************************************/
+
+static void js_std_dump_error1(JSContext *ctx, JSValueConst exception_val)
+{
+  JSValue val;
+  BOOL is_error;
+
+  is_error = JS_IsError(ctx, exception_val);
+  js_dump_obj(ctx, stderr, exception_val);
+  if (is_error)
+    {
+      val = JS_GetPropertyStr(ctx, exception_val, "stack");
+      if (!JS_IsUndefined(val))
+        {
+          js_dump_obj(ctx, stderr, val);
+        }
+
+      JS_FreeValue(ctx, val);
+    }
+}
+
+/****************************************************************************
+ * Name: js_std_dump_error
+ ****************************************************************************/
+
+static void js_std_dump_error(JSContext *ctx)
+{
+  JSValue exception_val;
+
+  exception_val = JS_GetException(ctx);
+  js_std_dump_error1(ctx, exception_val);
+  JS_FreeValue(ctx, exception_val);
+}
+
+/****************************************************************************
+ * Name: js_eval_buf
+ ****************************************************************************/
+
+static int js_eval_buf(JSContext *ctx, const void *buf, int buf_len,
+                       const char *filename, int eval_flags)
+{
+  JSValue val;
+  int ret;
+
+  if ((eval_flags & JS_EVAL_TYPE_MASK) == JS_EVAL_TYPE_MODULE)
+    {
+      val = JS_Eval(ctx, buf, buf_len, filename,
+                    eval_flags | JS_EVAL_FLAG_COMPILE_ONLY);
+      if (!JS_IsException(val))
+        {
+          val = JS_EvalFunction(ctx, val);
+        }
+    }
+  else
+    {
+      val = JS_Eval(ctx, buf, buf_len, filename, eval_flags);
+    }
+
+  if (JS_IsException(val))
+    {
+      js_std_dump_error(ctx);
+      ret = -1;
+    }
+  else
+    {
+      ret = 0;
+    }
+
+  JS_FreeValue(ctx, val);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: js_load_file
+ ****************************************************************************/
+
+uint8_t *js_load_file(JSContext *ctx, size_t *pbuf_len, const char *filename)
+{
+  FILE *f;
+  uint8_t *buf;
+  size_t buf_len;
+  long lret;
+
+  f = fopen(filename, "rb");
+  if (!f)
+      return NULL;
+  if (fseek(f, 0, SEEK_END) < 0)
+      goto fail;
+  lret = ftell(f);
+  if (lret < 0)
+      goto fail;
+
+  if (lret == LONG_MAX)
+    {
+      errno = EISDIR;
+      goto fail;
+    }
+
+  buf_len = lret;
+  if (fseek(f, 0, SEEK_SET) < 0)
+      goto fail;
+  if (ctx)
+      buf = js_malloc(ctx, buf_len + 1);
+  else
+      buf = malloc(buf_len + 1);
+  if (!buf)
+      goto fail;
+  if (fread(buf, 1, buf_len, f) != buf_len)
+    {
+      errno = EIO;
+      if (ctx)
+        js_free(ctx, buf);
+      else
+        free(buf);
+    fail:
+      fclose(f);
+      return NULL;
+    }
+
+  buf[buf_len] = '\0';
+  fclose(f);
+  *pbuf_len = buf_len;
+  return buf;
+}
+
+/****************************************************************************
+ * Name: js_eval_file
+ ****************************************************************************/
+
+static int js_eval_file(JSContext *ctx, const char *filename, int module)
+{
+  uint8_t *buf;
+  int ret, eval_flags;
+  size_t buf_len;
+
+  buf = js_load_file(ctx, &buf_len, filename);
+  if (!buf)
+    {
+      perror(filename);
+      exit(1);
+    }
+
+  if (module < 0)
+    {
+      module = (has_suffix(filename, ".mjs") ||
+                JS_DetectModule((const char *)buf, buf_len));
+    }
+
+  if (module)
+    eval_flags = JS_EVAL_TYPE_MODULE;
+  else
+    eval_flags = JS_EVAL_TYPE_GLOBAL;
+  ret = js_eval_buf(ctx, buf, buf_len, filename, eval_flags);
+  js_free(ctx, buf);
+  return ret;
+}
+
+static JSValue js_print(JSContext *ctx, JSValueConst this_val,
+                        int argc, JSValueConst *argv)
+{
+  int i;
+  const char *str;
+  size_t len;
+
+  for (i = 0; i < argc; i++)
+    {
+      if (i != 0)
+        putchar(' ');
+      str = JS_ToCStringLen(ctx, &len, argv[i]);
+      if (!str)
+          return JS_EXCEPTION;
+      fwrite(str, 1, len, stdout);
+      JS_FreeCString(ctx, str);
+    }
+
+  putchar('\n');
+  return JS_UNDEFINED;
+}
+
+void js_std_add_helpers(JSContext *ctx)
+{
+  JSValue global_obj, console;
+  global_obj = JS_GetGlobalObject(ctx);
+
+  console = JS_NewObject(ctx);
+  JS_SetPropertyStr(ctx, console, "log",
+                    JS_NewCFunction(ctx, js_print, "log", 1));
+  JS_SetPropertyStr(ctx, global_obj, "console", console);
+
+  JS_FreeValue(ctx, global_obj);
+}
+
+/****************************************************************************
+ * Public Functions Prototypes
+ ****************************************************************************/
+
+int js_ext_init(JSContext *ctx);
+int js_ext_destroy(JSContext *ctx);
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * qjs_main
+ ****************************************************************************/
+
+int main(int argc, char *argv[])
+{
+  JSRuntime *rt = NULL;
+  JSContext *ctx = NULL;
+  struct trace_malloc_data trace_data =
+  {
+    NULL
+  };
+
+  static const JSMallocFunctions trace_mf =
+  {
+      js_trace_malloc,
+      js_trace_free,
+      js_trace_realloc,
+      malloc_usable_size,
+  };
+
+  int optind;
+  char *expr = NULL;
+  int dump_memory = 0;
+  int trace_memory = 0;
+  int empty_run = 0;
+  size_t memory_limit = 0;
+  size_t stack_size = 0;
+
+  optind = 1;
+  while (optind < argc && *argv[optind] == '-')
+    {
+      char *arg = argv[optind] + 1;
+      const char *longopt = "";
+
+      if (!*arg)
+        break;
+      optind++;
+      if (*arg == '-')
+        {
+          longopt = arg + 1;
+          arg += strlen(arg);
+          if (!*longopt)
+            break;
+        }
+
+      for (; *arg || *longopt; longopt = "")
+        {
+          char opt = *arg;
+          if (opt)
+            {
+              arg++;
+            }
+
+          if (opt == 'h' || opt == '?' || !strcmp(longopt, "help"))
+            {
+              qjs_help();
+              continue;
+            }
+
+          if (opt == 'e' || !strcmp(longopt, "eval"))
+            {
+              if (*arg)
+                {
+                  expr = arg;
+                  break;
+                }
+
+              if (optind < argc)
+                {
+                  expr = argv[optind++];
+                  break;
+                }
+
+              fprintf(stderr, "qjs: missing expression for -e\n");
+              exit(2);
+            }
+
+          if (opt == 'd' || !strcmp(longopt, "dump"))
+            {
+              dump_memory++;
+              continue;
+            }
+
+          if (opt == 'T' || !strcmp(longopt, "trace"))
+            {
+              trace_memory++;
+              continue;
+            }
+
+          if (opt == 'q' || !strcmp(longopt, "quit"))
+            {
+              empty_run++;
+              continue;
+            }
+
+          if (!strcmp(longopt, "memory-limit"))
+            {
+              if (optind >= argc)
+                {
+                  fprintf(stderr, "expecting memory limit");
+                  exit(1);
+                }
+
+              memory_limit = (size_t)strtod(argv[optind++], NULL);
+              continue;
+            }
+
+          if (!strcmp(longopt, "stack-size"))
+            {
+              if (optind >= argc)
+                {
+                  fprintf(stderr, "expecting stack size");
+                  exit(1);
+                }
+
+              stack_size = (size_t)strtod(argv[optind++], NULL);
+              continue;
+            }
+
+          if (opt)
+            {
+              fprintf(stderr, "qjs: unknown option '-%c'\n", opt);
+            }
+          else
+            {
+              fprintf(stderr, "qjs: unknown option '--%s'\n", longopt);
+            }
+
+          qjs_help();
+        }
+    }
+
+  if (trace_memory)
+    {
+      js_trace_malloc_init(&trace_data);
+      rt = JS_NewRuntime2(&trace_mf, &trace_data);
+    }
+  else
+    {
+      rt = JS_NewRuntime();
+    }
+
+  if (!rt)
+    {
+      fprintf(stderr, "qjs: cannot allocate JS runtime\n");
+      goto fail;
+    }
+
+  if (memory_limit != 0)
+    JS_SetMemoryLimit(rt, memory_limit);
+  if (stack_size != 0)
+    JS_SetMaxStackSize(rt, stack_size);
+
+  ctx = JS_NewContext(rt);
+
+  if (!ctx)
+    {
+      fprintf(stderr, "qjs: cannot allocate JS context\n");
+      goto fail;
+    }
+
+  js_std_add_helpers(ctx);
+
+#ifdef CONFIG_INTERPRETERS_QUICKJS_EXT_HOOK
+  if (OK != js_ext_init(ctx))
+    {
+      fprintf(stderr, "qjs: external context init failed\n");
+      goto fail_ctx;
+    }
+#endif
+
+  if (!empty_run)
+    {
+      if (expr)
+        {
+          if (js_eval_buf(ctx, expr, strlen(expr), "<cmdline>", 0))
+            {
+              goto fail;
+            }
+        }
+      else if (optind < argc)
+        {
+          const char *filename;
+          filename = argv[optind];
+          if (js_eval_file(ctx, filename, 1))
+            {
+              goto fail;
+            }
+        }
+    }
+
+  if (dump_memory)
+    {
+      JSMemoryUsage stats;
+      JS_ComputeMemoryUsage(rt, &stats);
+      JS_DumpMemoryUsage(stdout, &stats, rt);
+    }
+
+fail:
+  if (ctx)
+    {
+#ifdef CONFIG_INTERPRETERS_QUICKJS_EXT_HOOK
+      js_ext_destroy(ctx);
+#endif
+      JS_FreeContext(ctx);
+    }
+
+  if (rt)
+    JS_FreeRuntime(rt);
+
+  return 0;
+}

Reply via email to