diff --git a/ports/javascript/JSBackend.patch b/ports/javascript/JSBackend.patch new file mode 100644 index 0000000000..a5fd41a3fb --- /dev/null +++ b/ports/javascript/JSBackend.patch @@ -0,0 +1,10 @@ +--- JSBackend.cpp 2018-01-10 16:35:07.331418145 +1100 ++++ JSBackend_mp_js.cpp 2018-01-10 16:40:04.804633134 +1100 +@@ -4280,6 +4280,7 @@ + + void JSWriter::calculateNativizedVars(const Function *F) { + NativizedVars.clear(); ++ return; + + for (Function::const_iterator I = F->begin(), BE = F->end(); I != BE; ++I) { + auto BI = &*I; diff --git a/ports/javascript/Makefile b/ports/javascript/Makefile new file mode 100644 index 0000000000..3ce698f101 --- /dev/null +++ b/ports/javascript/Makefile @@ -0,0 +1,59 @@ +include ../../py/mkenv.mk + +CROSS = 0 + +QSTR_DEFS = qstrdefsport.h + +include $(TOP)/py/py.mk + +CC = emcc -g4 +LD = emcc -g4 + +INC += -I. +INC += -I$(TOP) +INC += -I$(BUILD) + +CPP = clang -E +CFLAGS = -m32 $(INC) -Wall -Werror -std=c99 $(COPT) +LDFLAGS = -m32 -Wl,-Map=$@.map,--cref -Wl,--gc-sections + +CFLAGS += -O0 -DNDEBUG +CFLAGS += -fdata-sections -ffunction-sections + +SRC_LIB = $(addprefix lib/,\ + utils/interrupt_char.c \ + utils/stdout_helpers.c \ + utils/pyexec.c \ + mp-readline/readline.c \ + ) + +SRC_C = \ + main.c \ + mphalport.c \ + modutime.c \ + +SRC_QSTR += $(SRC_C) + +OBJ = +OBJ = $(PY_O) +OBJ += $(addprefix $(BUILD)/, $(SRC_LIB:.c=.o)) +OBJ += $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) + +JSFLAGS = -O0 -s EXPORTED_FUNCTIONS="['_mp_js_init', '_mp_js_init_repl', '_mp_js_do_str', '_mp_js_process_char', '_mp_hal_get_interrupt_char', '_mp_keyboard_interrupt']" -s EXTRA_EXPORTED_RUNTIME_METHODS="['ccall', 'cwrap']" --memory-init-file 0 --js-library library.js + +all: $(BUILD)/micropython.js + +$(BUILD)/micropython.js: $(OBJ) library.js wrapper.js + $(ECHO) "LINK $(BUILD)/firmware.js" + $(Q)emcc $(LDFLAGS) -o $(BUILD)/firmware.js $(OBJ) $(JSFLAGS) + cat $(BUILD)/firmware.js > $@ + cat wrapper.js >> $@ + +min: $(BUILD)/micropython.js + uglifyjs $< -c -o $(BUILD)/micropython.min.js + +test: $(BUILD)/micropython.js $(TOP)/tests/run-tests + $(eval DIRNAME=ports/$(notdir $(CURDIR))) + cd $(TOP)/tests && MICROPY_MICROPYTHON=../ports/javascript/node_run.sh ./run-tests + +include $(TOP)/py/mkrules.mk diff --git a/ports/javascript/README.md b/ports/javascript/README.md new file mode 100644 index 0000000000..ac268bb3bf --- /dev/null +++ b/ports/javascript/README.md @@ -0,0 +1,128 @@ +MicroPython.js +============== + +MicroPython transmuted into Javascript by Emscripten. + +Dependencies +------------ + +Building micropython.js bears the same requirements as the standard MicroPython +ports with the addition of Emscripten (and uglify-js for the minified file). + +A standard installation of Emscripten should provide functional code, however +if memory errors are encountered it may be worthwhile to modify the tool. +`emscripten-fastcomp/lib/Target/JSBackend.cpp` may require the minor fix +found in JSBackend.patch. This patch attempts to address situations where +C code running through Emscripten is denied access to Javascript variables +leading to false-positives in the MicroPython garbage collector as variables +with pointers exclusively in Javascript will be erased prematurely. +Refer to Emscripten documentation for instructions on building Emscripten +from source. + +Build instructions +------------------ + +In order to build micropython.js, run: + + $ make + +To generate the minified file micropython.min.js, run: + + $ make min + +Running with Node.js +-------------------- + +Access the repl with: + + $ node build/micropython.js + +Stack size may be modified using: + + $ node build/micropython.js -X stack=64K + +Where stack size may be represented in Bytes, KiB or MiB. + +MicroPython scripts may be executed using: + + $ node build/micropython.js hello.py + +Alternatively micropython.js may by accessed by other javascript programs in node +using the require command and the general API outlined below. For example: + +```javascript +var mp_js = require('./build/micropython.js'); + +mp_js_init(64 * 1024); +mp_js_do_str("print('hello world')\n"); +``` + +Running with HTML +----------------- + +The prerequisite for browser operation of micropython.js is an element with +the id `mp_js_stdout` which receives `print` events. The following code +demonstrates basic functionality: + +```html + + + + + + +
+ + + +``` + +MicroPython code execution will suspend the browser so be sure to atomize usage +within this environment. Unfortunately interrupts have not been implemented for the +browser. + +Testing +------- + +Run the test suite using: + + $ make test + +API +--- + +The following functions have been exposed to javascript. + +``` +mp_js_init(stack_size) +``` + +Initialize MicroPython with the given stack size in bytes. This must be +called before attempting to interact with MicroPython. + +``` +mp_js_do_str(code) +``` + +Execute the input code. `code` must be a `string`. + +``` +mp_js_init_repl() +``` + +Initialize MicroPython repl. Must be called before entering characters into +the repl. + +``` +mp_js_process_char(char) +``` + +Input character into MicroPython repl. `char` must be of type `number`. This +will execute MicroPython code when necessary. diff --git a/ports/javascript/library.h b/ports/javascript/library.h new file mode 100644 index 0000000000..47982e0640 --- /dev/null +++ b/ports/javascript/library.h @@ -0,0 +1,31 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017, 2018 Rami Ali + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/obj.h" + +extern void mp_js_write(const char *str, mp_uint_t len); +extern int mp_js_ticks_ms(void); +extern void mp_js_hook(void); diff --git a/ports/javascript/library.js b/ports/javascript/library.js new file mode 100644 index 0000000000..ed58167d5d --- /dev/null +++ b/ports/javascript/library.js @@ -0,0 +1,69 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017, 2018 Rami Ali + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +mergeInto(LibraryManager.library, { + mp_js_write: function(ptr, len) { + for (var i = 0; i < len; ++i) { + c = String.fromCharCode(getValue(ptr + i, 'i8')); + if (typeof window === 'undefined') { + process.stdout.write(c); + } else { + var mp_js_stdout = document.getElementById('mp_js_stdout'); + var print = new Event('print'); + print.data = c; + mp_js_stdout.dispatchEvent(print); + } + } + }, + + mp_js_ticks_ms: function() { + return (new Date()).getTime() - MP_JS_EPOCH; + }, + + mp_js_hook: function() { + if (typeof window === 'undefined') { + var mp_interrupt_char = Module.ccall('mp_hal_get_interrupt_char', 'number', ['number'], ['null']); + var fs = require('fs'); + + var buf = new Buffer(1); + try { + var n = fs.readSync(process.stdin.fd, buf, 0, 1); + if (n > 0) { + if (buf[0] == mp_interrupt_char) { + Module.ccall('mp_keyboard_interrupt', 'null', ['null'], ['null']); + } else { + process.stdout.write(String.fromCharCode(buf[0])); + } + } + } catch (e) { + if (e.code === 'EAGAIN') { + } else { + throw e; + } + } + } + }, +}); diff --git a/ports/javascript/main.c b/ports/javascript/main.c new file mode 100644 index 0000000000..65bae98b44 --- /dev/null +++ b/ports/javascript/main.c @@ -0,0 +1,135 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George and 2017, 2018 Rami Ali + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include +#include + +#include "py/compile.h" +#include "py/runtime.h" +#include "py/repl.h" +#include "py/gc.h" +#include "py/mperrno.h" +#include "lib/utils/pyexec.h" + +#include "library.h" + +#if MICROPY_ENABLE_COMPILER +void do_str(const char *src, mp_parse_input_kind_t input_kind) { + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, src, strlen(src), 0); + qstr source_name = lex->source_name; + mp_parse_tree_t parse_tree = mp_parse(lex, input_kind); + mp_obj_t module_fun = mp_compile(&parse_tree, source_name, MP_EMIT_OPT_NONE, false); + mp_call_function_0(module_fun); + nlr_pop(); + } else { + // uncaught exception + if (mp_obj_is_subclass_fast(mp_obj_get_type((mp_obj_t)nlr.ret_val), &mp_type_SystemExit)) { + // at the moment, the value of SystemExit is unused + } else { + mp_obj_print_exception(&mp_plat_print, (mp_obj_t)nlr.ret_val); + } + } +} +#endif + +static char *stack_top; + +void mp_js_do_str(const char *code) { + do_str(code, MP_PARSE_FILE_INPUT); +} + +int mp_js_process_char(int c) { + return pyexec_event_repl_process_char(c); +} + +void mp_js_init(int heap_size) { + int stack_dummy; + stack_top = (char*)&stack_dummy; + + #if MICROPY_ENABLE_GC + char *heap = (char*)malloc(heap_size * sizeof(char)); + gc_init(heap, heap + heap_size); + #endif + + #if MICROPY_ENABLE_PYSTACK + static mp_obj_t pystack[1024]; + mp_pystack_init(pystack, &pystack[MP_ARRAY_SIZE(pystack)]); + #endif + + mp_init(); + + mp_obj_list_init(mp_sys_path, 0); + mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR_)); + mp_obj_list_init(mp_sys_argv, 0); +} + +void mp_js_init_repl() { + pyexec_event_repl_init(); +} + +void gc_collect(void) { + // WARNING: This gc_collect implementation doesn't try to get root + // pointers from CPU registers, and thus may function incorrectly. + jmp_buf dummy; + if (setjmp(dummy) == 0) { + longjmp(dummy, 1); + } + gc_collect_start(); + gc_collect_root((void*)stack_top, ((mp_uint_t)(void*)(&dummy + 1) - (mp_uint_t)stack_top) / sizeof(mp_uint_t)); + gc_collect_end(); +} + +mp_lexer_t *mp_lexer_new_from_file(const char *filename) { + mp_raise_OSError(MP_ENOENT); +} + +mp_import_stat_t mp_import_stat(const char *path) { + return MP_IMPORT_STAT_NO_EXIST; +} + +mp_obj_t mp_builtin_open(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_open_obj, 1, mp_builtin_open); + +void nlr_jump_fail(void *val) { + while (1); +} + +void NORETURN __fatal_error(const char *msg) { + while (1); +} + +#ifndef NDEBUG +void MP_WEAK __assert_func(const char *file, int line, const char *func, const char *expr) { + printf("Assertion '%s' failed, at file %s:%d\n", expr, file, line); + __fatal_error("Assertion failed"); +} +#endif diff --git a/ports/javascript/modutime.c b/ports/javascript/modutime.c new file mode 100644 index 0000000000..6c6bdcfa27 --- /dev/null +++ b/ports/javascript/modutime.c @@ -0,0 +1,56 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George and 2017, 2018 Rami Ali + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include + +#include "mphalport.h" +#include "py/nlr.h" +#include "py/smallint.h" +#include "py/obj.h" +#include "py/runtime.h" +#include "lib/timeutils/timeutils.h" +#include "extmod/utime_mphal.h" + +STATIC const mp_rom_map_elem_t time_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_utime) }, + + { MP_ROM_QSTR(MP_QSTR_sleep), MP_ROM_PTR(&mp_utime_sleep_obj) }, + { MP_ROM_QSTR(MP_QSTR_sleep_ms), MP_ROM_PTR(&mp_utime_sleep_ms_obj) }, + { MP_ROM_QSTR(MP_QSTR_sleep_us), MP_ROM_PTR(&mp_utime_sleep_us_obj) }, + { MP_ROM_QSTR(MP_QSTR_ticks_ms), MP_ROM_PTR(&mp_utime_ticks_ms_obj) }, + { MP_ROM_QSTR(MP_QSTR_ticks_us), MP_ROM_PTR(&mp_utime_ticks_us_obj) }, + { MP_ROM_QSTR(MP_QSTR_ticks_cpu), MP_ROM_PTR(&mp_utime_ticks_cpu_obj) }, + { MP_ROM_QSTR(MP_QSTR_ticks_add), MP_ROM_PTR(&mp_utime_ticks_add_obj) }, + { MP_ROM_QSTR(MP_QSTR_ticks_diff), MP_ROM_PTR(&mp_utime_ticks_diff_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(time_module_globals, time_module_globals_table); + +const mp_obj_module_t mp_module_utime = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&time_module_globals, +}; diff --git a/ports/javascript/mpconfigport.h b/ports/javascript/mpconfigport.h new file mode 100644 index 0000000000..228113c48e --- /dev/null +++ b/ports/javascript/mpconfigport.h @@ -0,0 +1,212 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George and 2017, 2018 Rami Ali + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include + +// options to control how MicroPython is built + +// You can disable the built-in MicroPython compiler by setting the following +// config option to 0. If you do this then you won't get a REPL prompt, but you +// will still be able to execute pre-compiled scripts, compiled with mpy-cross. +#define MICROPY_ENABLE_COMPILER (1) + +#define MICROPY_QSTR_BYTES_IN_HASH (2) +#define MICROPY_ALLOC_PATH_MAX (256) +#define MICROPY_ALLOC_PARSE_CHUNK_INIT (16) +#define MICROPY_EMIT_X64 (0) //BROKEN +#define MICROPY_EMIT_THUMB (0) //BROKEN +#define MICROPY_EMIT_INLINE_THUMB (0) +#define MICROPY_COMP_MODULE_CONST (0) +#define MICROPY_COMP_CONST (1) +#define MICROPY_COMP_DOUBLE_TUPLE_ASSIGN (1) +#define MICROPY_COMP_TRIPLE_TUPLE_ASSIGN (0) +#define MICROPY_MEM_STATS (0) //BROKEN +#define MICROPY_DEBUG_PRINTERS (0) +#define MICROPY_ENABLE_GC (1) +#define MICROPY_GC_ALLOC_THRESHOLD (1) +#define MICROPY_GC_USES_ALLOCATED_SIZE (1) +#define MICROPY_REPL_EVENT_DRIVEN (1) +#define MICROPY_HELPER_REPL (1) +#define MICROPY_HELPER_LEXER_UNIX (0) +#define MICROPY_ENABLE_SOURCE_LINE (1) +#define MICROPY_ENABLE_DOC_STRING (1) +#define MICROPY_WARNINGS (1) +#define MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG (1) +#define MICROPY_PY_ASYNC_AWAIT (1) +#define MICROPY_PY_BUILTINS_BYTEARRAY (1) +#define MICROPY_PY_BUILTINS_MEMORYVIEW (1) +#define MICROPY_PY_BUILTINS_ENUMERATE (1) +#define MICROPY_PY_BUILTINS_FILTER (1) +#define MICROPY_PY_BUILTINS_FROZENSET (1) +#define MICROPY_PY_BUILTINS_REVERSED (1) +#define MICROPY_PY_BUILTINS_SET (1) +#define MICROPY_PY_BUILTINS_SLICE (1) +#define MICROPY_PY_BUILTINS_PROPERTY (1) +#define MICROPY_PY_BUILTINS_MIN_MAX (1) +#define MICROPY_PY___FILE__ (1) +#define MICROPY_PY_GC (1) +#define MICROPY_PY_ARRAY (1) +#define MICROPY_PY_ATTRTUPLE (1) +#define MICROPY_PY_COLLECTIONS (1) +#define MICROPY_PY_MATH (1) +#define MICROPY_PY_MATH_SPECIAL_FUNCTIONS (1) +#define MICROPY_PY_CMATH (1) +#define MICROPY_PY_IO (1) +#define MICROPY_PY_STRUCT (1) +#define MICROPY_PY_SYS (1) +#define MICROPY_PY_SYS_MAXSIZE (1) +#define MICROPY_CPYTHON_COMPAT (1) +#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) +#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_DOUBLE) + +#define MICROPY_USE_INTERNAL_PRINTF (0) +#define MICROPY_ENABLE_PYSTACK (1) +#define MICROPY_KBD_EXCEPTION (1) +#define MICROPY_PY_UTIME_MP_HAL (1) +#define MICROPY_REPL_AUTO_INDENT (1) +#define MICROPY_PY_FUNCTION_ATTRS (1) +#define MICROPY_PY_BUILTINS_STR_UNICODE (1) +#define MICROPY_PY_BUILTINS_STR_CENTER (1) +#define MICROPY_PY_BUILTINS_STR_PARTITION (1) +#define MICROPY_PY_BUILTINS_STR_SPLITLINES (1) +#define MICROPY_PY_BUILTINS_SLICE_ATTRS (1) +#define MICROPY_PY_ALL_SPECIAL_METHODS (1) +#define MICROPY_PY_BUILTINS_COMPILE (1) +#define MICROPY_PY_BUILTINS_EXECFILE (1) +#define MICROPY_PY_BUILTINS_INPUT (1) +#define MICROPY_PY_BUILTINS_POW3 (1) +#define MICROPY_PY_BUILTINS_HELP (1) +#define MICROPY_PY_BUILTINS_HELP_MODULES (1) +#define MICROPY_PY_MICROPYTHON_MEM_INFO (1) +#define MICROPY_PY_ARRAY_SLICE_ASSIGN (1) +#define MICROPY_PY_COLLECTIONS_ORDEREDDICT (1) +#define MICROPY_PY_SYS_PLATFORM "javascript" +#define MICROPY_PY_UERRNO (1) +#define MICROPY_PY_UCTYPES (1) +#define MICROPY_PY_UZLIB (1) +#define MICROPY_PY_UJSON (1) +#define MICROPY_PY_URE (1) +#define MICROPY_PY_UHEAPQ (1) +#define MICROPY_PY_UHASHLIB (1) +#define MICROPY_PY_UBINASCII (1) +#define MICROPY_PY_URANDOM (1) +#define MICROPY_PY_URANDOM_EXTRA_FUNCS (1) +#define MICROPY_PY_USELECT (1) +#define MICROPY_PY_FRAMEBUF (1) +#define MICROPY_STREAMS_NON_BLOCK (1) +#define MICROPY_MODULE_WEAK_LINKS (1) +#define MICROPY_CAN_OVERRIDE_BUILTINS (1) +#define MICROPY_USE_INTERNAL_ERRNO (1) +#define MICROPY_ENABLE_SCHEDULER (1) +#define MICROPY_SCHEDULER_DEPTH (1) + +#define MP_SSIZE_MAX (0x7fffffff) + +extern const struct _mp_obj_module_t mp_module_utime; + +#define MICROPY_PORT_BUILTIN_MODULES \ + { MP_ROM_QSTR(MP_QSTR_utime), MP_ROM_PTR(&mp_module_utime) }, \ + +#define MICROPY_PORT_BUILTIN_MODULE_WEAK_LINKS \ + { MP_ROM_QSTR(MP_QSTR_binascii), MP_ROM_PTR(&mp_module_ubinascii) }, \ + { MP_ROM_QSTR(MP_QSTR_collections), MP_ROM_PTR(&mp_module_collections) }, \ + { MP_ROM_QSTR(MP_QSTR_re), MP_ROM_PTR(&mp_module_ure) }, \ + { MP_ROM_QSTR(MP_QSTR_zlib), MP_ROM_PTR(&mp_module_uzlib) }, \ + { MP_ROM_QSTR(MP_QSTR_json), MP_ROM_PTR(&mp_module_ujson) }, \ + { MP_ROM_QSTR(MP_QSTR_heapq), MP_ROM_PTR(&mp_module_uheapq) }, \ + { MP_ROM_QSTR(MP_QSTR_hashlib), MP_ROM_PTR(&mp_module_uhashlib) }, \ + { MP_ROM_QSTR(MP_QSTR_io), MP_ROM_PTR(&mp_module_io) }, \ + { MP_ROM_QSTR(MP_QSTR_random), MP_ROM_PTR(&mp_module_urandom) }, \ + { MP_ROM_QSTR(MP_QSTR_time), MP_ROM_PTR(&mp_module_utime) }, \ + { MP_ROM_QSTR(MP_QSTR_select), MP_ROM_PTR(&mp_module_uselect) }, \ + { MP_ROM_QSTR(MP_QSTR_struct), MP_ROM_PTR(&mp_module_ustruct) }, \ + { MP_ROM_QSTR(MP_QSTR_errno), MP_ROM_PTR(&mp_module_uerrno) }, \ + +//#define MICROPY_EVENT_POLL_HOOK {ets_event_poll();} +#if MICROPY_PY_THREAD +#define MICROPY_EVENT_POLL_HOOK \ + do { \ + extern void mp_handle_pending(void); \ + mp_handle_pending(); \ + if (pyb_thread_enabled) { \ + MP_THREAD_GIL_EXIT(); \ + pyb_thread_yield(); \ + MP_THREAD_GIL_ENTER(); \ + } else { \ + } \ + } while (0); + +#define MICROPY_THREAD_YIELD() pyb_thread_yield() +#else +#define MICROPY_EVENT_POLL_HOOK \ + do { \ + extern void mp_handle_pending(void); \ + mp_handle_pending(); \ + } while (0); + +#define MICROPY_THREAD_YIELD() +#endif + +#define MICROPY_VM_HOOK_COUNT (10) +#define MICROPY_VM_HOOK_INIT static uint vm_hook_divisor = MICROPY_VM_HOOK_COUNT; +#define MICROPY_VM_HOOK_POLL if (--vm_hook_divisor == 0) { \ + vm_hook_divisor = MICROPY_VM_HOOK_COUNT; \ + extern void mp_js_hook(void); \ + mp_js_hook(); \ + } +#define MICROPY_VM_HOOK_LOOP MICROPY_VM_HOOK_POLL +#define MICROPY_VM_HOOK_RETURN MICROPY_VM_HOOK_POLL + +// type definitions for the specific machine + +//#define MICROPY_MAKE_POINTER_CALLABLE(p) ((void*)((mp_uint_t)(p) | 1)) + +// This port is intended to be 32-bit, but unfortunately, int32_t for +// different targets may be defined in different ways - either as int +// or as long. This requires different printf formatting specifiers +// to print such value. So, we avoid int32_t and use int directly. +#define UINT_FMT "%u" +#define INT_FMT "%d" +typedef int mp_int_t; // must be pointer size +typedef unsigned mp_uint_t; // must be pointer size +typedef long mp_off_t; + +#define MP_PLAT_PRINT_STRN(str, len) mp_hal_stdout_tx_strn_cooked(str, len) + +// extra built in names to add to the global namespace +#define MICROPY_PORT_BUILTINS \ + { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&mp_builtin_open_obj) }, + +// We need to provide a declaration/definition of alloca() +#include + +#define MICROPY_HW_BOARD_NAME "JS" +#define MICROPY_HW_MCU_NAME "Emscripten" + +#define MP_STATE_PORT MP_STATE_VM + +#define MICROPY_PORT_ROOT_POINTERS \ + const char *readline_hist[8]; diff --git a/ports/javascript/mphalport.c b/ports/javascript/mphalport.c new file mode 100644 index 0000000000..18e3e2adeb --- /dev/null +++ b/ports/javascript/mphalport.c @@ -0,0 +1,62 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George and 2017, 2018 Rami Ali + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "library.h" +#include "mphalport.h" + +void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) { + mp_js_write(str, len); +} + +void mp_hal_delay_ms(mp_uint_t ms) { + uint32_t start = mp_hal_ticks_ms(); + while (mp_hal_ticks_ms() - start < ms) { + } +} + +void mp_hal_delay_us(mp_uint_t us) { + uint32_t start = mp_hal_ticks_us(); + while (mp_hal_ticks_us() - start < us) { + } +} + +mp_uint_t mp_hal_ticks_us(void) { + return mp_js_ticks_ms() * 1000; +} + +mp_uint_t mp_hal_ticks_ms(void) { + return mp_js_ticks_ms(); +} + +mp_uint_t mp_hal_ticks_cpu(void) { + return 0; +} + +extern int mp_interrupt_char; + +int mp_hal_get_interrupt_char(void) { + return mp_interrupt_char; +} diff --git a/ports/javascript/mphalport.h b/ports/javascript/mphalport.h new file mode 100644 index 0000000000..8ba66fcc9f --- /dev/null +++ b/ports/javascript/mphalport.h @@ -0,0 +1,39 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George and 2017, 2018 Rami Ali + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/obj.h" +#include "lib/utils/interrupt_char.h" + +#define mp_hal_stdin_rx_chr() (0) +void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len); + +void mp_hal_delay_ms(mp_uint_t ms); +void mp_hal_delay_us(mp_uint_t us); +mp_uint_t mp_hal_ticks_ms(void); +mp_uint_t mp_hal_ticks_us(void); +mp_uint_t mp_hal_ticks_cpu(void); + +int mp_hal_get_interrupt_char(void); diff --git a/ports/javascript/node_run.sh b/ports/javascript/node_run.sh new file mode 100755 index 0000000000..466ffe39e3 --- /dev/null +++ b/ports/javascript/node_run.sh @@ -0,0 +1,2 @@ +#!/bin/sh +node $(dirname $0)/build/micropython.js "$@" diff --git a/ports/javascript/qstrdefsport.h b/ports/javascript/qstrdefsport.h new file mode 100644 index 0000000000..3ba897069b --- /dev/null +++ b/ports/javascript/qstrdefsport.h @@ -0,0 +1 @@ +// qstrs specific to this port diff --git a/ports/javascript/wrapper.js b/ports/javascript/wrapper.js new file mode 100644 index 0000000000..6109de902f --- /dev/null +++ b/ports/javascript/wrapper.js @@ -0,0 +1,70 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017, 2018 Rami Ali + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +mp_js_init = Module.cwrap('mp_js_init', 'null', ['number']); +mp_js_do_str = Module.cwrap('mp_js_do_str', 'null', ['string']); +mp_js_init_repl = Module.cwrap('mp_js_init_repl', 'null', ['null']); +mp_js_process_char = Module.cwrap('mp_js_process_char', 'number', ['number']); + +var MP_JS_EPOCH = (new Date()).getTime(); + +if (typeof window === 'undefined' && require.main === module) { + var fs = require('fs'); + var stack_size = 64 * 1024; + var contents = ''; + var repl = true; + + for (var i = 0; i < process.argv.length; i++) { + if (process.argv[i] === '-X' && i < process.argv.length - 1) { + if (process.argv[i + 1].includes('stack=')) { + stack_size = parseInt(process.argv[i + 1].split('stack=')[1]); + if (process.argv[i + 1].substr(-1).toLowerCase() === 'k') { + stack_size *= 1024; + } else if (process.argv[i + 1].substr(-1).toLowerCase() === 'm') { + stack_size *= 1024 * 1024; + } + } + } else if (process.argv[i].includes('.py')) { + contents += fs.readFileSync(process.argv[i], 'utf8'); + repl = false;; + } + } + mp_js_init(stack_size); + + if (repl) { + mp_js_init_repl(); + process.stdin.setRawMode(true); + process.stdin.on('data', function (data) { + for (var i = 0; i < data.length; i++) { + if (mp_js_process_char(data[i])) { + process.exit() + } + } + }); + } else { + mp_js_do_str(contents); + } +}