├── .gitignore ├── .gitmodules ├── LICENSE ├── Makefile ├── README.md ├── deps └── miniz.c ├── example-apps ├── .gitignore ├── Makefile ├── echo-server-src │ ├── main.js │ ├── pretty-print.js │ └── theme-256.js └── repl-src │ ├── main.js │ ├── pretty-print.js │ └── theme-256.js ├── src ├── duv │ ├── .gitignore │ ├── async.c │ ├── async.h │ ├── callbacks.c │ ├── callbacks.h │ ├── check.c │ ├── check.h │ ├── dns.c │ ├── dns.h │ ├── dschema.c │ ├── dschema.h │ ├── duv.c │ ├── duv.h │ ├── fs.c │ ├── fs.h │ ├── fs_event.c │ ├── fs_event.h │ ├── fs_poll.c │ ├── fs_poll.h │ ├── handle.c │ ├── handle.h │ ├── idle.c │ ├── idle.h │ ├── loop.c │ ├── loop.h │ ├── misc.c │ ├── misc.h │ ├── pipe.c │ ├── pipe.h │ ├── poll.c │ ├── poll.h │ ├── prepare.c │ ├── prepare.h │ ├── process.c │ ├── process.h │ ├── req.c │ ├── req.h │ ├── signal.c │ ├── signal.h │ ├── stream.c │ ├── stream.h │ ├── tcp.c │ ├── tcp.h │ ├── timer.c │ ├── timer.h │ ├── tty.c │ ├── tty.h │ ├── udp.c │ ├── udp.h │ ├── utils.c │ └── utils.h ├── env.c ├── env.h ├── main.c ├── path.c ├── path.h └── test-path.c ├── target └── .keepme └── test-app ├── .jshintrc ├── bootstrap.js ├── deps ├── bodec.js ├── http-codec.js ├── msgpack-codec.js ├── net.js ├── theme-16.js ├── theme-256.js ├── utils.js ├── websocket-codec.js └── wrap-socket.js ├── http-server.js ├── main.js ├── test-nucleus.js └── test-uv.js /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "deps/libuv"] 2 | path = deps/libuv 3 | url = https://github.com/libuv/libuv.git 4 | [submodule "deps/duktape-releases"] 5 | path = deps/duktape-releases 6 | url = https://github.com/svaarala/duktape-releases.git 7 | [submodule "deps/mbedtls"] 8 | path = deps/mbedtls 9 | url = https://github.com/ARMmbed/mbedtls.git 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Nucleus is released under the Apache 2 license. It uses libraries with their own 2 | licenses. These are in submodules and/or the deps folder to keep them 3 | apart. 4 | 5 | ---------------- 6 | 7 | 8 | Apache License 9 | Version 2.0, January 2004 10 | http://www.apache.org/licenses/ 11 | 12 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 13 | 14 | 1. Definitions. 15 | 16 | "License" shall mean the terms and conditions for use, reproduction, 17 | and distribution as defined by Sections 1 through 9 of this document. 18 | 19 | "Licensor" shall mean the copyright owner or entity authorized by 20 | the copyright owner that is granting the License. 21 | 22 | "Legal Entity" shall mean the union of the acting entity and all 23 | other entities that control, are controlled by, or are under common 24 | control with that entity. For the purposes of this definition, 25 | "control" means (i) the power, direct or indirect, to cause the 26 | direction or management of such entity, whether by contract or 27 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 28 | outstanding shares, or (iii) beneficial ownership of such entity. 29 | 30 | "You" (or "Your") shall mean an individual or Legal Entity 31 | exercising permissions granted by this License. 32 | 33 | "Source" form shall mean the preferred form for making modifications, 34 | including but not limited to software source code, documentation 35 | source, and configuration files. 36 | 37 | "Object" form shall mean any form resulting from mechanical 38 | transformation or translation of a Source form, including but 39 | not limited to compiled object code, generated documentation, 40 | and conversions to other media types. 41 | 42 | "Work" shall mean the work of authorship, whether in Source or 43 | Object form, made available under the License, as indicated by a 44 | copyright notice that is included in or attached to the work 45 | (an example is provided in the Appendix below). 46 | 47 | "Derivative Works" shall mean any work, whether in Source or Object 48 | form, that is based on (or derived from) the Work and for which the 49 | editorial revisions, annotations, elaborations, or other modifications 50 | represent, as a whole, an original work of authorship. For the purposes 51 | of this License, Derivative Works shall not include works that remain 52 | separable from, or merely link (or bind by name) to the interfaces of, 53 | the Work and Derivative Works thereof. 54 | 55 | "Contribution" shall mean any work of authorship, including 56 | the original version of the Work and any modifications or additions 57 | to that Work or Derivative Works thereof, that is intentionally 58 | submitted to Licensor for inclusion in the Work by the copyright owner 59 | or by an individual or Legal Entity authorized to submit on behalf of 60 | the copyright owner. For the purposes of this definition, "submitted" 61 | means any form of electronic, verbal, or written communication sent 62 | to the Licensor or its representatives, including but not limited to 63 | communication on electronic mailing lists, source code control systems, 64 | and issue tracking systems that are managed by, or on behalf of, the 65 | Licensor for the purpose of discussing and improving the Work, but 66 | excluding communication that is conspicuously marked or otherwise 67 | designated in writing by the copyright owner as "Not a Contribution." 68 | 69 | "Contributor" shall mean Licensor and any individual or Legal Entity 70 | on behalf of whom a Contribution has been received by Licensor and 71 | subsequently incorporated within the Work. 72 | 73 | 2. Grant of Copyright License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | copyright license to reproduce, prepare Derivative Works of, 77 | publicly display, publicly perform, sublicense, and distribute the 78 | Work and such Derivative Works in Source or Object form. 79 | 80 | 3. Grant of Patent License. Subject to the terms and conditions of 81 | this License, each Contributor hereby grants to You a perpetual, 82 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 83 | (except as stated in this section) patent license to make, have made, 84 | use, offer to sell, sell, import, and otherwise transfer the Work, 85 | where such license applies only to those patent claims licensable 86 | by such Contributor that are necessarily infringed by their 87 | Contribution(s) alone or by combination of their Contribution(s) 88 | with the Work to which such Contribution(s) was submitted. If You 89 | institute patent litigation against any entity (including a 90 | cross-claim or counterclaim in a lawsuit) alleging that the Work 91 | or a Contribution incorporated within the Work constitutes direct 92 | or contributory patent infringement, then any patent licenses 93 | granted to You under this License for that Work shall terminate 94 | as of the date such litigation is filed. 95 | 96 | 4. Redistribution. You may reproduce and distribute copies of the 97 | Work or Derivative Works thereof in any medium, with or without 98 | modifications, and in Source or Object form, provided that You 99 | meet the following conditions: 100 | 101 | (a) You must give any other recipients of the Work or 102 | Derivative Works a copy of this License; and 103 | 104 | (b) You must cause any modified files to carry prominent notices 105 | stating that You changed the files; and 106 | 107 | (c) You must retain, in the Source form of any Derivative Works 108 | that You distribute, all copyright, patent, trademark, and 109 | attribution notices from the Source form of the Work, 110 | excluding those notices that do not pertain to any part of 111 | the Derivative Works; and 112 | 113 | (d) If the Work includes a "NOTICE" text file as part of its 114 | distribution, then any Derivative Works that You distribute must 115 | include a readable copy of the attribution notices contained 116 | within such NOTICE file, excluding those notices that do not 117 | pertain to any part of the Derivative Works, in at least one 118 | of the following places: within a NOTICE text file distributed 119 | as part of the Derivative Works; within the Source form or 120 | documentation, if provided along with the Derivative Works; or, 121 | within a display generated by the Derivative Works, if and 122 | wherever such third-party notices normally appear. The contents 123 | of the NOTICE file are for informational purposes only and 124 | do not modify the License. You may add Your own attribution 125 | notices within Derivative Works that You distribute, alongside 126 | or as an addendum to the NOTICE text from the Work, provided 127 | that such additional attribution notices cannot be construed 128 | as modifying the License. 129 | 130 | You may add Your own copyright statement to Your modifications and 131 | may provide additional or different license terms and conditions 132 | for use, reproduction, or distribution of Your modifications, or 133 | for any such Derivative Works as a whole, provided Your use, 134 | reproduction, and distribution of the Work otherwise complies with 135 | the conditions stated in this License. 136 | 137 | 5. Submission of Contributions. Unless You explicitly state otherwise, 138 | any Contribution intentionally submitted for inclusion in the Work 139 | by You to the Licensor shall be under the terms and conditions of 140 | this License, without any additional terms or conditions. 141 | Notwithstanding the above, nothing herein shall supersede or modify 142 | the terms of any separate license agreement you may have executed 143 | with Licensor regarding such Contributions. 144 | 145 | 6. Trademarks. This License does not grant permission to use the trade 146 | names, trademarks, service marks, or product names of the Licensor, 147 | except as required for reasonable and customary use in describing the 148 | origin of the Work and reproducing the content of the NOTICE file. 149 | 150 | 7. Disclaimer of Warranty. Unless required by applicable law or 151 | agreed to in writing, Licensor provides the Work (and each 152 | Contributor provides its Contributions) on an "AS IS" BASIS, 153 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 154 | implied, including, without limitation, any warranties or conditions 155 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 156 | PARTICULAR PURPOSE. You are solely responsible for determining the 157 | appropriateness of using or redistributing the Work and assume any 158 | risks associated with Your exercise of permissions under this License. 159 | 160 | 8. Limitation of Liability. In no event and under no legal theory, 161 | whether in tort (including negligence), contract, or otherwise, 162 | unless required by applicable law (such as deliberate and grossly 163 | negligent acts) or agreed to in writing, shall any Contributor be 164 | liable to You for damages, including any direct, indirect, special, 165 | incidental, or consequential damages of any character arising as a 166 | result of this License or out of the use or inability to use the 167 | Work (including but not limited to damages for loss of goodwill, 168 | work stoppage, computer failure or malfunction, or any and all 169 | other commercial damages or losses), even if such Contributor 170 | has been advised of the possibility of such damages. 171 | 172 | 9. Accepting Warranty or Additional Liability. While redistributing 173 | the Work or Derivative Works thereof, You may choose to offer, 174 | and charge a fee for, acceptance of support, warranty, indemnity, 175 | or other liability obligations and/or rights consistent with this 176 | License. However, in accepting such obligations, You may act only 177 | on Your own behalf and on Your sole responsibility, not on behalf 178 | of any other Contributor, and only if You agree to indemnify, 179 | defend, and hold each Contributor harmless for any liability 180 | incurred by, or claims asserted against, such Contributor by reason 181 | of your accepting any such warranty or additional liability. 182 | 183 | END OF TERMS AND CONDITIONS 184 | 185 | APPENDIX: How to apply the Apache License to your work. 186 | 187 | To apply the Apache License to your work, attach the following 188 | boilerplate notice, with the fields enclosed by brackets "[]" 189 | replaced with your own identifying information. (Don't include 190 | the brackets!) The text should be enclosed in the appropriate 191 | comment syntax for the file format. We also recommend that a 192 | file or class name and description of purpose be included on the 193 | same "printed page" as the copyright notice for easier 194 | identification within third-party archives. 195 | 196 | Copyright [yyyy] [name of copyright owner] 197 | 198 | Licensed under the Apache License, Version 2.0 (the "License"); 199 | you may not use this file except in compliance with the License. 200 | You may obtain a copy of the License at 201 | 202 | http://www.apache.org/licenses/LICENSE-2.0 203 | 204 | Unless required by applicable law or agreed to in writing, software 205 | distributed under the License is distributed on an "AS IS" BASIS, 206 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 207 | See the License for the specific language governing permissions and 208 | limitations under the License. 209 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Set LIBUV to deps/libuv (for the submodule version), run make init-libuv to checkout the submodule 3 | # Set LIBUV to system to use the version deployed in the system 4 | # Set LIBUV to pkgconfig to use pkg-config for the setup 5 | # 6 | # UVSRC=system 7 | #UVSRC=pkg-config 8 | UVSRC=git 9 | 10 | CC=cc 11 | 12 | # 13 | # Define buildtype : shared or static 14 | # 15 | BUILDTYPE=static 16 | # BUILDTYPE=shared 17 | 18 | # Make sure to `make distclean` before building when changing CC. 19 | # Default build is debug mode. 20 | # CFLAGS=-g 21 | # Uncomment the following to make a small binary 22 | CFLAGS=-Os 23 | # Uncomment the following to make a static musl binary on linux, set BUILTYPE to static 24 | CC=musl-gcc -static 25 | export CC 26 | 27 | LDFLAGS+=-L/usr/local/lib -Ltarget -luv 28 | 29 | BINS=\ 30 | target/main.o\ 31 | 32 | LIBS=\ 33 | target/env.o\ 34 | target/path.o\ 35 | target/miniz.o\ 36 | target/libduv.a\ 37 | target/mbed.a\ 38 | target/duktape.o 39 | 40 | MBED_LIBS=\ 41 | target/mbed_md5.o \ 42 | target/mbed_sha1.o\ 43 | target/mbed_sha256.o\ 44 | target/mbed_sha512.o 45 | 46 | DUV_LIBS=\ 47 | target/duv_loop.o\ 48 | target/duv_handle.o\ 49 | target/duv_req.o\ 50 | target/duv_timer.o\ 51 | target/duv_prepare.o\ 52 | target/duv_check.o\ 53 | target/duv_idle.o\ 54 | target/duv_async.o\ 55 | target/duv_poll.o\ 56 | target/duv_signal.o\ 57 | target/duv_process.o\ 58 | target/duv_stream.o\ 59 | target/duv_tcp.o\ 60 | target/duv_pipe.o\ 61 | target/duv_tty.o\ 62 | target/duv_udp.o\ 63 | target/duv_fs_event.o\ 64 | target/duv_fs_poll.o\ 65 | target/duv_fs.o\ 66 | target/duv_dns.o\ 67 | target/duv_misc.o\ 68 | target/duv_duv.o\ 69 | target/duv_utils.o\ 70 | target/duv_callbacks.o\ 71 | target/duv_dschema.o 72 | 73 | DUV_HEADER=\ 74 | deps/duktape-releases/src/duktape.h\ 75 | deps/duktape-releases/src/duk_config.h\ 76 | src/duv/duv.h 77 | 78 | 79 | LIBUV=deps/libuv 80 | MBEDTLS=deps/mbedtls 81 | 82 | ifeq ($(BUILDTYPE), shared) 83 | CFLAGS+=-fPIC 84 | LDFLAGS+=-lseaduk 85 | UVTARGET=target/libuv.so 86 | else 87 | UVTARGET=target/libuv.a 88 | endif 89 | ifeq ($(UVSRC), pkg-config) 90 | CFLAGS+=$(shell pkg-config --cflags libuv) 91 | LDFLAGS+=$(shell pkg-config --libs libuv) 92 | endif 93 | ifeq ($(UVSRC), git) 94 | CFLAGS+=-Ideps/libuv/include 95 | endif 96 | ifeq ($(UVSRC), system) 97 | CFLAGS+=-I/usr/local/include 98 | endif 99 | CFLAGS+=-I${MBEDTLS}/include 100 | 101 | 102 | all: all-${BUILDTYPE} 103 | install: install-${BUILDTYPE} 104 | 105 | tiny: target/nucleus 106 | cp target/nucleus target/nucleus-tiny 107 | strip target/nucleus-tiny 108 | upx target/nucleus-tiny 109 | 110 | all-static: ${UVTARGET} target/libduv.a target/nucleus 111 | 112 | all-shared: ${UVTARGET} lib-shared target/nucleus 113 | 114 | lib-static: target/libduv.a 115 | 116 | lib-shared: target/libseaduk.so target/libduv.so 117 | 118 | target/libseaduk.so: ${LIBS} 119 | ${CC} $^ ${CFLAGS} -shared -pthread -o $@ 120 | 121 | target/libduv.so: ${DUV_LIBS} 122 | ${CC} $^ ${LDFLAGS} ${CFLAGS} -shared -L/usr/local/lib -luv -pthread -o $@ 123 | 124 | target/nucleus: ${BINS} ${LIBS} 125 | ${CC} $^ ${LDFLAGS} ${CFLAGS} -lm -L/usr/local/lib -luv -pthread -o $@ 126 | 127 | install-static: install-bin install-lib-static install-header 128 | install-shared : install-bin install-lib-shared install-header 129 | 130 | install-bin: target/nucleus 131 | install $< /usr/local/bin/ 132 | 133 | install-lib-static: target/libduv.a 134 | install $^ /usr/local/lib/ 135 | 136 | install-lib-shared: target/libseaduk.so target/libduv.so 137 | install $^ /usr/local/lib/ 138 | 139 | install-header: ${DUV_HEADER} 140 | mkdir -p /usr/local/include/duv 141 | install $^ /usr/local/include/duv/ 142 | 143 | test: test-dir test-zip test-app test-app-tiny test-path 144 | 145 | test-path: 146 | $(CC) src/test-path.c 147 | ./a.out 148 | rm -f a.out 149 | 150 | test-dir: target/nucleus 151 | $< test-app -- 1 2 3 152 | 153 | test-zip: target/nucleus target/test-app.zip 154 | $^ -- 4 5 6 155 | 156 | test-app: target/app 157 | $< 7 8 9 158 | 159 | test-app-tiny: target/app-tiny 160 | $< 10 11 12 161 | 162 | target/app: target/nucleus test-app/* test-app/deps/* 163 | $< test-app -o $@ 164 | 165 | target/app-tiny: target/nucleus test-app/* test-app/deps/* 166 | $< test-app -l -o $@ 167 | 168 | target/test-app.zip: target/nucleus test-app/* test-app/deps/* 169 | $< test-app -z -o $@ 170 | 171 | target/env.o: src/env.c src/env.h 172 | ${CC} -std=c99 ${CFLAGS} -Wall -Wextra -pedantic -Werror -c $< -o $@ 173 | 174 | target/path.o: src/path.c src/path.h 175 | ${CC} -std=c99 ${CFLAGS} -Wall -Wextra -pedantic -Werror -c $< -o $@ 176 | 177 | target/main.o: src/main.c src/*.h 178 | ${CC} -std=c99 ${CFLAGS} -Wall -Wextra -pedantic -Werror -c $< -o $@ 179 | 180 | target/duktape.o: deps/duktape-releases/src/duktape.c deps/duktape-releases/src/duktape.h 181 | ${CC} -std=c99 ${CFLAGS} -Wall -Wextra -pedantic -c $< -o $@ 182 | 183 | target/miniz.o: deps/miniz.c 184 | ${CC} -std=gnu99 ${CFLAGS} -c $< -o $@ 185 | 186 | target/libuv.a: ${LIBUV}/.libs/libuv.a 187 | cp $< $@ 188 | 189 | target/libuv.so: ${LIBUV}/.libs/libuv.so 190 | @if [ `uname -s` == Linux ]; then cp ${LIBUV}/.libs/libuv.so target; fi 191 | @if [ `uname -s` == Darwin ]; then cp ${LIBUV}/.libs/libuv.dylib target; fi 192 | 193 | target/libduv.a: ${DUV_LIBS} 194 | ${AR} cr $@ ${DUV_LIBS} 195 | 196 | target/duv_%.o: src/duv/%.c src/duv/%.h 197 | ${CC} -std=c99 ${CFLAGS} -D_POSIX_C_SOURCE=200112 -Wall -Wextra -pedantic -Werror -c $< -I./deps/libuv/include -o $@ 198 | 199 | target/mbed.a: ${MBED_LIBS} 200 | ${AR} cr $@ ${MBED_LIBS} 201 | 202 | target/mbed_%.o: deps/mbedtls/library/%.c deps/mbedtls/include/mbedtls/%.h 203 | ${CC} -std=c99 ${CFLAGS} -D_POSIX_C_SOURCE=200112 -Wall -Wextra -pedantic -Werror -c $< -I./deps/mbedtls/include -o $@ 204 | 205 | init-duktape: 206 | git submodule init deps/duktape-releases 207 | git submodule update deps/duktape-releases 208 | 209 | init-libuv: 210 | git submodule init deps/libuv 211 | git submodule update deps/libuv 212 | 213 | ${LIBUV}/.libs/libuv.a: ${LIBUV}/Makefile 214 | ${MAKE} -C ${LIBUV} 215 | 216 | ${LIBUV}/.libs/libuv.so: ${LIBUV}/Makefile 217 | ${MAKE} -C ${LIBUV} 218 | 219 | ${LIBUV}/Makefile: ${LIBUV}/configure 220 | cd ${LIBUV}; ./configure; cd - 221 | 222 | ${LIBUV}/configure: ${LIBUV}/autogen.sh 223 | cd ${LIBUV}; ./autogen.sh; cd - 224 | 225 | clean: 226 | rm -rf target/* 227 | 228 | distclean: clean 229 | cd ${LIBUV}; git clean -xdf; cd - 230 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This folder is a concrete implementation of the Nucleus interface using DukTape as the JavaScript engine. 2 | 3 | It's tested regularly on Linux and OSX. 4 | 5 | ## Status 6 | 7 | Currently this port is still im progress, but there is enough to already start 8 | writing userland modules that use the `nucleus` global and parts of libuv. 9 | 10 | ## Building 11 | 12 | I'm currently using a `Makefile` to script building. The libuv submodule 13 | requires `automake` and `libtool` installed (via apt-get, homebrew, etc...). 14 | 15 | Also make sure to include all submodules when cloning nucleus. 16 | 17 | ```sh 18 | # Clone recursivly to get all submodules 19 | git clone --recursive https://github.com/creationix/nucleus.git 20 | # Go to this folder 21 | cd nucleus/implementations/duktape 22 | # Build using 4 parallel cores 23 | make -j4 24 | # Optionally run all the tests. 25 | make test 26 | ``` 27 | 28 | When you're done building the `nucleus` binary can be used to run and build your 29 | own tiny JS scripts. 30 | 31 | ## Testing 32 | 33 | Run `make -j4 test`. It will also attempt (re)build if you have not already. 34 | 35 | Additionally, there are some manually verified tests. To run these, use 36 | `make test-manual`. 37 | 38 | ## Optimizing for Size 39 | 40 | By default the Makefile builds in debug mode with `-g`. If you want a tiny 41 | binary modify the Makefile to have `CC= cc -Os`. If you've done a build before, 42 | make sure to run `make distclean` before trying a new build. 43 | 44 | If you want a small static binary on linux using musl, install musl-tools on 45 | your ubuntu system and set `CC= musl-gcc -Os -static` and rebuild. 46 | 47 | For maximum smallness (especially with the static build), `strip` your nucleus 48 | binary before appending the app zip to it. 49 | -------------------------------------------------------------------------------- /example-apps/.gitignore: -------------------------------------------------------------------------------- 1 | *.zip 2 | echo-server 3 | -------------------------------------------------------------------------------- /example-apps/Makefile: -------------------------------------------------------------------------------- 1 | APPS=\ 2 | echo-server 3 | 4 | all: ${APPS} 5 | 6 | %: ../nucleus %.zip 7 | cat $^ > $@ 8 | chmod +x $@ 9 | 10 | %.zip: %-src %-src/* 11 | cd $<; zip -r ../$@ *.js; cd - 12 | 13 | ../nucleus: 14 | ${MAKE} -C .. 15 | 16 | clean: 17 | rm -f *.zip ${APPS} 18 | -------------------------------------------------------------------------------- /example-apps/echo-server-src/main.js: -------------------------------------------------------------------------------- 1 | // Load a library from a file using the native dofile primitive directly. 2 | var p = nucleus.dofile("pretty-print.js").prettyPrint; 3 | // Load the uv module in the local scope. 4 | var uv = nucleus.uv; 5 | 6 | print("Starting local TCP echo server..."); 7 | 8 | var server = new uv.Tcp(); 9 | server.bind("127.0.0.1", 1337); 10 | server.listen(128, onConnection); 11 | print("Server is now bound and listening for new connections..."); 12 | p(server.getsockname()); 13 | 14 | print("Starting libuv event loop"); 15 | uv.run(); 16 | print("Event loop exited"); 17 | 18 | // function statements for callbacks can go anwehere in JS. 19 | 20 | function onConnection(err) { 21 | if (err) throw err; 22 | var client = new uv.Tcp(); 23 | server.accept(client); 24 | client.readStart(onRead); 25 | 26 | print("New TCP client accepted"); 27 | p(client.getpeername()); 28 | 29 | function onRead(err, chunk) { 30 | if (err) throw err; 31 | if (chunk) { 32 | print("Echoing chunk back to client..."); 33 | p(chunk); 34 | client.write(chunk); 35 | } 36 | else { 37 | print("received EOF from client, shutting down..."); 38 | client.shutdown(function () { 39 | client.close(); 40 | server.close(); 41 | }); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /example-apps/echo-server-src/pretty-print.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var width = 80; 4 | 5 | var quote, quote2, obracket, cbracket, obrace, cbrace, comma, colon; 6 | 7 | var theme = nucleus.dofile('theme-256.js'); 8 | 9 | quote = colorize("quotes", '"', "string"); 10 | quote2 = colorize("quotes", '"'); 11 | obracket = colorize("braces", '['); 12 | cbracket = colorize("braces", ']'); 13 | obrace = colorize("braces", '{'); 14 | cbrace = colorize("braces", '}'); 15 | comma = colorize("sep", ','); 16 | colon = colorize("sep", ':'); 17 | 18 | function color(color_name) { 19 | return "\x1b[" + (color_name ? theme[color_name] : "0") + "m"; 20 | } 21 | 22 | function colorize(color_name, string, reset_name) { 23 | return color(color_name) + string + color(reset_name); 24 | } 25 | 26 | function dump(value) { 27 | 28 | var seen = []; 29 | return dumper(value, 0); 30 | function dumper(value, depth) { 31 | var type = typeof value; 32 | 33 | if (type === "undefined") { 34 | return colorize("undefined", "undefined"); 35 | } 36 | if (value === null) { 37 | return colorize("null", "null"); 38 | } 39 | if (type === "boolean") { 40 | return colorize("boolean", "" + value); 41 | } 42 | if (type === "number") { 43 | return colorize("number", "" + value); 44 | } 45 | if (type === "string") { 46 | var str = JSON.stringify(value); 47 | return (quote + str.substring(1, str.length - 1) + quote2). 48 | replace(/(\\u[0-9a-f]{4}|\\["\\/bfnrt])/g, function (match) { 49 | return colorize("escape", match, "string"); 50 | }); 51 | } 52 | var info = Duktape.info(value); 53 | if (type === "function") { 54 | var fname = value.name || info[1]; 55 | // Native CFunctions don't have a .prototype property. 56 | if (value.prototype) { 57 | return colorize("function", "[Function " + fname + "]"); 58 | } 59 | return colorize("cfunction", "[Native " + fname + "]"); 60 | } 61 | var fullName = Object.prototype.toString.call(value); 62 | var name = fullName.substring(8, fullName.length - 1); 63 | if (name === "RegExp") { 64 | return colorize("regexp", "[RegExp " + value + "]"); 65 | } 66 | if (name === "Thread") { 67 | return colorize("thread", "[Thread " + info[1] + "]"); 68 | } 69 | if (name === "Buffer") { 70 | var preview = Array.prototype.slice.call(value, 0, 10).map(function (byte) { 71 | return byte < 16 ? "0" + byte.toString(16) : byte.toString(16); 72 | }).join(" "); 73 | if (value.length > 10) { preview += "..."; } 74 | // Fixed buffers have undefined for info[4] 75 | if (info[4] === undefined) { 76 | return colorize("buffer", "[Buffer " + preview + "]"); 77 | } 78 | return colorize("dbuffer", "[Dynamic Buffer " + preview + "]"); 79 | } 80 | if (name === "Pointer") { 81 | return colorize("pointer", "[Pointer " + info[1] + "]"); 82 | } 83 | if (name === "Error") { 84 | return colorize("error", "[" + value.constructor.name + " " + value.message + "]"); 85 | } 86 | if (name === "Date") { 87 | return colorize("date", "[Date " + value + "]"); 88 | } 89 | if (name === "String") { 90 | return colorize("string", "[String " + JSON.stringify(value) + "]"); 91 | } 92 | if (name === "Number") { 93 | return colorize("number", "[Number " + value + "]"); 94 | } 95 | if (name !== "Object" && name !== "Array" && name !== "global") { 96 | return colorize("object", "[" + name + " " + info[1] + "]"); 97 | } 98 | if (typeof value.inspect === "function") { 99 | var out = value.inspect(); 100 | if (out) return colorize("object", value.inspect()); 101 | } 102 | 103 | var index = seen.indexOf(value); 104 | if (depth > 2 || index >= 0) { 105 | return colorize("object", "[" + name + " " + info[1] + "]"); 106 | } 107 | seen.push(value); 108 | 109 | var parts, opener, closer; 110 | if (name === "Array") { 111 | opener = obracket; 112 | closer = cbracket; 113 | parts = value.map(function (item) { 114 | return dumper(item, depth + 1); 115 | }); 116 | } 117 | else { 118 | opener = obrace; 119 | closer = cbrace; 120 | parts = Object.keys(value).map(function (key) { 121 | return colorize("property", key) + colon + " " + dumper(value[key], depth + 1); 122 | }); 123 | } 124 | 125 | var line = opener + " " + parts.join(comma + " ") + " " + closer; 126 | var max = width - depth * 2; 127 | if (strip(line).length > max) { 128 | var lines = []; 129 | line = []; 130 | max -= 2; 131 | var left = max; 132 | parts.forEach(function (part) { 133 | var len = strip(part).length + 2; 134 | if (left < len) { 135 | if (line.length) { 136 | lines.push(line); 137 | } 138 | left = max; 139 | line = []; 140 | } 141 | line.push(part); 142 | left -= len; 143 | }); 144 | if (line.length) { 145 | lines.push(line); 146 | } 147 | lines = lines.map(function (line) { 148 | return line.join(comma + " "); 149 | }); 150 | 151 | line = opener + "\n " + lines.join(comma + "\n").split("\n").join("\n ") + "\n" + closer; 152 | } 153 | 154 | return line; 155 | } 156 | } 157 | 158 | function strip(string) { 159 | return string.replace(/\x1b\[[^m]*m/g, ''); 160 | } 161 | 162 | function prettyPrint() { 163 | print(Array.prototype.map.call(arguments, dump).join(" ")); 164 | } 165 | 166 | return { 167 | prettyPrint: prettyPrint, 168 | dump: dump, 169 | color: color, 170 | colorize: colorize, 171 | }; 172 | -------------------------------------------------------------------------------- /example-apps/echo-server-src/theme-256.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // nice color theme using 256-mode colors 4 | var theme = {}; 5 | 6 | theme.property = "38;5;253"; 7 | theme.braces = "38;5;247"; 8 | theme.sep = "38;5;240"; 9 | 10 | theme.undefined = "38;5;244"; 11 | theme.boolean = "38;5;220"; // yellow-orange 12 | theme.number = "38;5;202"; // orange 13 | theme.string = "38;5;34"; // darker green 14 | theme.quotes = "38;5;40"; // green 15 | theme.escape = "38;5;46"; // bright green 16 | theme.function = "38;5;129"; // purple 17 | theme.cfunction = "38;5;161"; // purple-red 18 | theme.thread = "38;5;199"; // pink 19 | 20 | theme.regexp = "38;5;214"; // yellow-orange 21 | theme.date = "38;5;153"; // blue-purple 22 | 23 | theme.null = "38;5;27"; // dark blue 24 | theme.object = "38;5;27"; // blue 25 | theme.buffer = "38;5;39"; // blue2 26 | theme.dbuffer = "38;5;69"; // teal 27 | theme.pointer = "38;5;124"; // red 28 | 29 | theme.error = "38;5;196"; // bright red 30 | theme.success = "38;5;120;48;5;22"; // bright green 31 | theme.failure = "38;5;215;48;5;52"; // bright green 32 | theme.highlight = "38;5;45;48;5;236"; // bright teal with grey background 33 | 34 | return theme; 35 | -------------------------------------------------------------------------------- /example-apps/repl-src/main.js: -------------------------------------------------------------------------------- 1 | var p = nucleus.dofile('pretty-print.js').prettyPrint; 2 | var uv = nucleus.uv; 3 | 4 | var stdin = new uv.Tty(0, true); 5 | var stdout = new uv.Tty(1, false); 6 | var stderr = new uv.Tty(2, false); 7 | 8 | function toAscii(data) { 9 | var str = ""; 10 | for (var i = 0; i < data.length; i++) { 11 | str += String.fromCharCode(data[i]); 12 | } 13 | return str; 14 | } 15 | 16 | stdin.setMode(1); 17 | stdout.write("> "); 18 | stdin.readStart(function (err, chunk) { 19 | if (err) { 20 | stderr.write("" + err); 21 | return stdin.readStop(); 22 | } 23 | if (chunk) { 24 | p(chunk, toAscii(chunk)); 25 | if (chunk.length == 1 && chunk[0] == 0x04) { 26 | stdin.readStop(); 27 | } 28 | stdout.write("> "); 29 | } 30 | else { 31 | stdin.readStop(); 32 | } 33 | }); 34 | 35 | 36 | uv.run(); 37 | uv.ttyResetMode(); 38 | -------------------------------------------------------------------------------- /example-apps/repl-src/pretty-print.js: -------------------------------------------------------------------------------- 1 | ../echo-server-src/pretty-print.js -------------------------------------------------------------------------------- /example-apps/repl-src/theme-256.js: -------------------------------------------------------------------------------- 1 | ../echo-server-src/theme-256.js -------------------------------------------------------------------------------- /src/duv/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.a 3 | -------------------------------------------------------------------------------- /src/duv/async.c: -------------------------------------------------------------------------------- 1 | #include "async.h" 2 | 3 | duk_ret_t duv_new_async(duk_context *ctx) { 4 | dschema_check(ctx, (const duv_schema_entry[]) { 5 | {"callback", duk_is_function}, 6 | {0,0} 7 | }); 8 | uv_async_t *async = duk_push_fixed_buffer(ctx, sizeof(uv_async_t)); 9 | duv_check(ctx, uv_async_init(duv_loop(ctx), async, duv_on_async)); 10 | duv_setup_handle(ctx, (uv_handle_t*)async, DUV_ASYNC); 11 | duk_insert(ctx, 0); 12 | duk_put_prop_string(ctx, 0, "\xffon-async"); 13 | return 1; 14 | } 15 | 16 | duk_ret_t duv_async_send(duk_context *ctx) { 17 | uv_async_t *async = duv_require_this_handle(ctx, DUV_ASYNC_MASK); 18 | duv_check(ctx, uv_async_send(async)); 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /src/duv/async.h: -------------------------------------------------------------------------------- 1 | #ifndef ASYNC_H 2 | #define ASYNC_H 3 | 4 | #include "duv.h" 5 | 6 | duk_ret_t duv_new_async(duk_context *ctx); 7 | duk_ret_t duv_async_send(duk_context *ctx); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /src/duv/callbacks.c: -------------------------------------------------------------------------------- 1 | #include "callbacks.h" 2 | #include "utils.h" 3 | 4 | void duv_on_close(uv_handle_t *handle) { 5 | duv_emit(handle, "\xffon-close", 0, 1); 6 | duk_context *ctx = handle->data; 7 | duv_remove_handle(ctx, handle); 8 | } 9 | 10 | void duv_on_alloc(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { 11 | (void)(handle); 12 | buf->base = malloc(suggested_size); 13 | buf->len = suggested_size; 14 | } 15 | 16 | void duv_on_timeout(uv_timer_t *timer) { 17 | duv_emit((uv_handle_t*)timer, "\xffon-timeout", 0, 0); 18 | } 19 | 20 | void duv_on_prepare(uv_prepare_t *prepare) { 21 | duv_emit((uv_handle_t*)prepare, "\xffon-prepare", 0, 0); 22 | } 23 | 24 | void duv_on_check(uv_check_t *check) { 25 | duv_emit((uv_handle_t*)check, "\xffon-check", 0, 0); 26 | } 27 | 28 | void duv_on_idle(uv_idle_t *idle) { 29 | duv_emit((uv_handle_t*)idle, "\xffon-idle", 0, 0); 30 | } 31 | 32 | void duv_on_async(uv_async_t *async) { 33 | duv_emit((uv_handle_t*)async, "\xffon-async", 0, 0); 34 | } 35 | 36 | void duv_on_read(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { 37 | duk_context *ctx = stream->data; 38 | if (nread >= 0) { 39 | char* out; 40 | duk_push_null(ctx); 41 | out = duk_push_fixed_buffer(ctx, nread); 42 | memcpy(out, buf->base, nread); 43 | } 44 | free(buf->base); 45 | if (nread == 0) return; 46 | if (nread == UV_EOF) { 47 | duk_push_null(ctx); // no error 48 | duk_push_undefined(ctx); // undefined value to signify EOF 49 | } 50 | else if (nread < 0) { 51 | duv_push_status(ctx, nread); 52 | duk_push_undefined(ctx); 53 | } 54 | duv_emit((uv_handle_t*)stream, "\xffon-read", 2, 0); 55 | } 56 | 57 | void duv_on_write(uv_write_t *write, int status) { 58 | duk_context *ctx = write->data; 59 | duv_push_status(ctx, status); 60 | duv_resolve((uv_req_t*)write, 1); 61 | } 62 | 63 | void duv_on_connect(uv_connect_t *connect, int status) { 64 | duk_context *ctx = connect->data; 65 | duv_push_status(ctx, status); 66 | duv_resolve((uv_req_t*)connect, 1); 67 | } 68 | 69 | void duv_on_shutdown(uv_shutdown_t *shutdown, int status) { 70 | duk_context *ctx = shutdown->data; 71 | duv_push_status(ctx, status); 72 | duv_resolve((uv_req_t*)shutdown, 1); 73 | } 74 | 75 | void duv_on_connection(uv_stream_t *server, int status) { 76 | duk_context *ctx = server->data; 77 | if (status) { 78 | duk_push_error_object(ctx, DUK_ERR_ERROR, "%s: %s", uv_err_name(status), uv_strerror(status)); 79 | } 80 | else { 81 | duk_push_null(ctx); 82 | } 83 | duv_emit((uv_handle_t*)server, "\xffon-connection", 1, 0); 84 | } 85 | -------------------------------------------------------------------------------- /src/duv/callbacks.h: -------------------------------------------------------------------------------- 1 | #ifndef CALLBACKS_H 2 | #define CALLBACKS_H 3 | 4 | #include "duv.h" 5 | 6 | // Handle method callbacks 7 | void duv_on_close(uv_handle_t *handle); 8 | void duv_on_alloc(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf); 9 | 10 | // Timer method callbacks 11 | void duv_on_timeout(uv_timer_t *timer); 12 | 13 | // Prepare method callbacks 14 | void duv_on_prepare(uv_prepare_t *prepare); 15 | 16 | // Check method callbacks 17 | void duv_on_check(uv_check_t *check); 18 | 19 | // Idle method callbacks 20 | void duv_on_idle(uv_idle_t *idle); 21 | 22 | // Async method callbacks 23 | void duv_on_async(uv_async_t *async); 24 | 25 | // Stream method callbacks 26 | void duv_on_read(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf); 27 | void duv_on_write(uv_write_t *shutdown, int status); 28 | void duv_on_connect(uv_connect_t *shutdown, int status); 29 | void duv_on_shutdown(uv_shutdown_t *shutdown, int status); 30 | void duv_on_connection(uv_stream_t *server, int status); 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /src/duv/check.c: -------------------------------------------------------------------------------- 1 | #include "check.h" 2 | 3 | duk_ret_t duv_new_check(duk_context *ctx) { 4 | uv_check_t *check = duk_push_fixed_buffer(ctx, sizeof(uv_check_t)); 5 | duv_check(ctx, uv_check_init(duv_loop(ctx), check)); 6 | duv_setup_handle(ctx, (uv_handle_t*)check, DUV_CHECK); 7 | return 1; 8 | } 9 | 10 | duk_ret_t duv_check_start(duk_context *ctx) { 11 | dschema_check(ctx, (const duv_schema_entry[]) { 12 | {"callback", duk_is_function}, 13 | {0,0} 14 | }); 15 | uv_check_t *check = duv_require_this_handle(ctx, DUV_CHECK_MASK); 16 | duk_put_prop_string(ctx, 0, "\xffon-check"); 17 | duv_check(ctx, uv_check_start(check, duv_on_check)); 18 | return 0; 19 | } 20 | 21 | duk_ret_t duv_check_stop(duk_context *ctx) { 22 | uv_check_t *check = duv_require_this_handle(ctx, DUV_CHECK_MASK); 23 | duk_del_prop_string(ctx, -1, "\xffon-check"); 24 | duv_check(ctx, uv_check_stop(check)); 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /src/duv/check.h: -------------------------------------------------------------------------------- 1 | #ifndef CHECK_H 2 | #define CHECK_H 3 | 4 | #include "duv.h" 5 | 6 | duk_ret_t duv_new_check(duk_context *ctx); 7 | duk_ret_t duv_check_start(duk_context *ctx); 8 | duk_ret_t duv_check_stop(duk_context *ctx); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /src/duv/dns.c: -------------------------------------------------------------------------------- 1 | #include "dns.h" 2 | 3 | static int duv_af_string_to_num(const char* string) { 4 | if (!string) return AF_UNSPEC; 5 | #ifdef AF_UNIX 6 | if (strcmp(string, "unix") == 0) return AF_UNIX; 7 | #endif 8 | #ifdef AF_INET 9 | if (strcmp(string, "inet") == 0) return AF_INET; 10 | #endif 11 | #ifdef AF_INET6 12 | if (strcmp(string, "inet6") == 0) return AF_INET6; 13 | #endif 14 | #ifdef AF_IPX 15 | if (strcmp(string, "ipx") == 0) return AF_IPX; 16 | #endif 17 | #ifdef AF_NETLINK 18 | if (strcmp(string, "netlink") == 0) return AF_NETLINK; 19 | #endif 20 | #ifdef AF_X25 21 | if (strcmp(string, "x25") == 0) return AF_X25; 22 | #endif 23 | #ifdef AF_AX25 24 | if (strcmp(string, "ax25") == 0) return AF_AX25; 25 | #endif 26 | #ifdef AF_ATMPVC 27 | if (strcmp(string, "atmpvc") == 0) return AF_ATMPVC; 28 | #endif 29 | #ifdef AF_APPLETALK 30 | if (strcmp(string, "appletalk") == 0) return AF_APPLETALK; 31 | #endif 32 | #ifdef AF_PACKET 33 | if (strcmp(string, "packet") == 0) return AF_PACKET; 34 | #endif 35 | return 0; 36 | } 37 | 38 | static const char* duv_af_num_to_string(const int num) { 39 | switch (num) { 40 | #ifdef AF_UNIX 41 | case AF_UNIX: return "unix"; 42 | #endif 43 | #ifdef AF_INET 44 | case AF_INET: return "inet"; 45 | #endif 46 | #ifdef AF_INET6 47 | case AF_INET6: return "inet6"; 48 | #endif 49 | #ifdef AF_IPX 50 | case AF_IPX: return "ipx"; 51 | #endif 52 | #ifdef AF_NETLINK 53 | case AF_NETLINK: return "netlink"; 54 | #endif 55 | #ifdef AF_X25 56 | case AF_X25: return "x25"; 57 | #endif 58 | #ifdef AF_AX25 59 | case AF_AX25: return "ax25"; 60 | #endif 61 | #ifdef AF_ATMPVC 62 | case AF_ATMPVC: return "atmpvc"; 63 | #endif 64 | #ifdef AF_APPLETALK 65 | case AF_APPLETALK: return "appletalk"; 66 | #endif 67 | #ifdef AF_PACKET 68 | case AF_PACKET: return "packet"; 69 | #endif 70 | } 71 | return NULL; 72 | } 73 | 74 | static int duv_sock_string_to_num(const char* string) { 75 | if (!string) return 0; 76 | #ifdef SOCK_STREAM 77 | if (strcmp(string, "stream") == 0) return SOCK_STREAM; 78 | #endif 79 | #ifdef SOCK_DGRAM 80 | if (strcmp(string, "dgram") == 0) return SOCK_DGRAM; 81 | #endif 82 | #ifdef SOCK_SEQPACKET 83 | if (strcmp(string, "seqpacket") == 0) return SOCK_SEQPACKET; 84 | #endif 85 | #ifdef SOCK_RAW 86 | if (strcmp(string, "raw") == 0) return SOCK_RAW; 87 | #endif 88 | #ifdef SOCK_RDM 89 | if (strcmp(string, "rdm") == 0) return SOCK_RDM; 90 | #endif 91 | return 0; 92 | } 93 | 94 | static const char* duv_sock_num_to_string(const int num) { 95 | switch (num) { 96 | #ifdef SOCK_STREAM 97 | case SOCK_STREAM: return "stream"; 98 | #endif 99 | #ifdef SOCK_DGRAM 100 | case SOCK_DGRAM: return "dgram"; 101 | #endif 102 | #ifdef SOCK_SEQPACKET 103 | case SOCK_SEQPACKET: return "seqpacket"; 104 | #endif 105 | #ifdef SOCK_RAW 106 | case SOCK_RAW: return "raw"; 107 | #endif 108 | #ifdef SOCK_RDM 109 | case SOCK_RDM: return "rdm"; 110 | #endif 111 | } 112 | return NULL; 113 | } 114 | 115 | 116 | void duv_pushaddrinfo(duk_context *ctx, struct addrinfo* res) { 117 | char ip[INET6_ADDRSTRLEN]; 118 | int port, i = 0; 119 | const char *addr; 120 | struct addrinfo* curr = res; 121 | duk_push_array(ctx); 122 | for (curr = res; curr; curr = curr->ai_next) { 123 | if (curr->ai_family == AF_INET || curr->ai_family == AF_INET6) { 124 | duk_push_object(ctx); 125 | if (curr->ai_family == AF_INET) { 126 | addr = (char*) &((struct sockaddr_in*) curr->ai_addr)->sin_addr; 127 | port = ((struct sockaddr_in*) curr->ai_addr)->sin_port; 128 | } else { 129 | addr = (char*) &((struct sockaddr_in6*) curr->ai_addr)->sin6_addr; 130 | port = ((struct sockaddr_in6*) curr->ai_addr)->sin6_port; 131 | } 132 | duk_push_string(ctx, duv_af_num_to_string(curr->ai_family)); 133 | duk_put_prop_string(ctx, -2, "family"); 134 | uv_inet_ntop(curr->ai_family, addr, ip, INET6_ADDRSTRLEN); 135 | duk_push_string(ctx, ip); 136 | duk_put_prop_string(ctx, -2, "addr"); 137 | if (ntohs(port)) { 138 | duk_push_int(ctx, ntohs(port)); 139 | duk_put_prop_string(ctx, -2, "port"); 140 | } 141 | duk_push_string(ctx, duv_sock_num_to_string(curr->ai_socktype)); 142 | duk_put_prop_string(ctx, -2, "socktype"); 143 | duk_push_string(ctx, duv_af_num_to_string(curr->ai_protocol)); 144 | duk_put_prop_string(ctx, -2, "protocol"); 145 | if (curr->ai_canonname) { 146 | duk_push_string(ctx, curr->ai_canonname); 147 | duk_put_prop_string(ctx, -2, "canonname"); 148 | } 149 | duk_put_prop_index(ctx, -2, i++); 150 | } 151 | } 152 | } 153 | 154 | static void duv_on_addrinfo(uv_getaddrinfo_t* req, int status, struct addrinfo* res) { 155 | duk_context *ctx = req->data; 156 | duv_push_status(ctx, status); 157 | if (status < 0) { 158 | duv_resolve((uv_req_t*)req, 1); 159 | } 160 | else { 161 | duv_pushaddrinfo(ctx, res); 162 | duv_resolve((uv_req_t*)req, 2); 163 | } 164 | } 165 | 166 | static void duv_on_nameinfo(uv_getnameinfo_t* req, int status, const char* hostname, const char* service) { 167 | duk_context *ctx = req->data; 168 | duv_push_status(ctx, status); 169 | if (status < 0) { 170 | duv_resolve((uv_req_t*)req, 1); 171 | } 172 | else { 173 | duk_push_string(ctx, hostname); 174 | duk_push_string(ctx, service); 175 | duk_dump_context_stdout(ctx); 176 | duv_resolve((uv_req_t*)req, 3); 177 | } 178 | } 179 | 180 | duk_ret_t duv_getaddrinfo(duk_context *ctx) { 181 | dschema_check(ctx, (const duv_schema_entry[]) { 182 | {"options", duk_is_object}, 183 | {"callback", dschema_is_continuation}, 184 | {0,0} 185 | }); 186 | 187 | uv_getaddrinfo_t* req; 188 | const char* node; 189 | const char* service; 190 | struct addrinfo hints_s; 191 | struct addrinfo* hints = &hints_s; 192 | 193 | duk_get_prop_string(ctx, 0, "node"); 194 | if (!duk_is_null_or_undefined(ctx, -1)) { 195 | node = duk_get_string(ctx, -1); 196 | } 197 | else { 198 | node = NULL; 199 | } 200 | duk_pop(ctx); 201 | 202 | duk_get_prop_string(ctx, 0, "service"); 203 | if (!duk_is_null_or_undefined(ctx, -1)) { 204 | service = duk_get_string(ctx, -1); 205 | } 206 | else { 207 | service = NULL; 208 | } 209 | duk_pop(ctx); 210 | 211 | // Initialize the hints 212 | memset(hints, 0, sizeof(*hints)); 213 | 214 | // Process the `family` hint. 215 | duk_get_prop_string(ctx, 0, "family"); 216 | if (duk_is_number(ctx, -1)) { 217 | hints->ai_family = duk_get_int(ctx, -1); 218 | } 219 | else if (duk_is_string(ctx, -1)) { 220 | hints->ai_family = duv_af_string_to_num(duk_get_string(ctx, -1)); 221 | } 222 | else if (duk_is_null_or_undefined(ctx, -1)) { 223 | hints->ai_family = AF_UNSPEC; 224 | } 225 | else { 226 | duk_error(ctx, DUK_ERR_TYPE_ERROR, "family hint must be string if set"); 227 | } 228 | duk_pop(ctx); 229 | 230 | // Process the `socktype` hint. 231 | duk_get_prop_string(ctx, 0, "socktype"); 232 | if (duk_is_number(ctx, -1)) { 233 | hints->ai_socktype = duk_get_int(ctx, -1); 234 | } 235 | else if (duk_is_string(ctx, -1)) { 236 | hints->ai_socktype = duv_sock_string_to_num(duk_get_string(ctx, -1)); 237 | } 238 | else if (!duk_is_null_or_undefined(ctx, -1)) { 239 | duk_error(ctx, DUK_ERR_TYPE_ERROR, "socktype hint must be string if set"); 240 | } 241 | duk_pop(ctx); 242 | 243 | // Process the `protocol` hint 244 | duk_get_prop_string(ctx, 0, "protocol"); 245 | if (duk_is_number(ctx, -1)) { 246 | hints->ai_protocol = duk_get_int(ctx, -1); 247 | } 248 | else if (duk_is_string(ctx, -1)) { 249 | int protocol = duv_af_string_to_num(duk_get_string(ctx, -1)); 250 | if (protocol) { 251 | hints->ai_protocol = protocol; 252 | } 253 | else { 254 | duk_error(ctx, DUK_ERR_TYPE_ERROR, "Invalid protocol hint"); 255 | } 256 | } 257 | else if (!duk_is_null_or_undefined(ctx, -1)) { 258 | duk_error(ctx, DUK_ERR_TYPE_ERROR, "protocol hint must be string if set"); 259 | } 260 | duk_pop(ctx); 261 | 262 | duk_get_prop_string(ctx, 0, "addrconfig"); 263 | if (duk_get_boolean(ctx, -1)) hints->ai_flags |= AI_ADDRCONFIG; 264 | duk_pop(ctx); 265 | 266 | #ifdef AI_V4MAPPED 267 | duk_get_prop_string(ctx, 0, "v4mapped"); 268 | if (duk_get_boolean(ctx, -1)) hints->ai_flags |= AI_V4MAPPED; 269 | duk_pop(ctx); 270 | #endif 271 | 272 | #ifdef AI_ALL 273 | duk_get_prop_string(ctx, 0, "all"); 274 | if (duk_get_boolean(ctx, -1)) hints->ai_flags |= AI_ALL; 275 | duk_pop(ctx); 276 | #endif 277 | 278 | duk_get_prop_string(ctx, 0, "numerichost"); 279 | if (duk_get_boolean(ctx, -1)) hints->ai_flags |= AI_NUMERICHOST; 280 | duk_pop(ctx); 281 | 282 | duk_get_prop_string(ctx, 0, "passive"); 283 | if (duk_get_boolean(ctx, -1)) hints->ai_flags |= AI_PASSIVE; 284 | duk_pop(ctx); 285 | 286 | duk_get_prop_string(ctx, 0, "numericserv"); 287 | if (duk_get_boolean(ctx, -1)) { 288 | hints->ai_flags |= AI_NUMERICSERV; 289 | /* On OS X upto at least OSX 10.9, getaddrinfo crashes 290 | * if AI_NUMERICSERV is set and the servname is NULL or "0". 291 | * This workaround avoids a segfault in libsystem. 292 | */ 293 | if (NULL == service) service = "00"; 294 | } 295 | duk_pop(ctx); 296 | 297 | duk_get_prop_string(ctx, 0, "canonname"); 298 | if (duk_get_boolean(ctx, -1)) hints->ai_flags |= AI_CANONNAME; 299 | duk_pop(ctx); 300 | 301 | req = duk_push_fixed_buffer(ctx, sizeof(*req)); 302 | duv_setup_request(ctx, (uv_req_t*)req, 1); 303 | 304 | duv_check(ctx, 305 | uv_getaddrinfo(duv_loop(ctx), req, duv_on_addrinfo, node, service, hints) 306 | ); 307 | 308 | return 1; 309 | } 310 | 311 | duk_ret_t duv_getnameinfo(duk_context *ctx) { 312 | dschema_check(ctx, (const duv_schema_entry[]) { 313 | {"options", duk_is_object}, 314 | {"callback", dschema_is_continuation}, 315 | {0,0} 316 | }); 317 | 318 | struct sockaddr_storage addr; 319 | const char* ip = NULL; 320 | int flags = 0; 321 | int port = 0; 322 | 323 | memset(&addr, 0, sizeof(addr)); 324 | 325 | duk_get_prop_string(ctx, 0, "ip"); 326 | if (duk_is_string(ctx, -1)) { 327 | ip = duk_get_string(ctx, -1); 328 | } 329 | else if (!duk_is_null_or_undefined(ctx, -1)) { 330 | duk_error(ctx, DUK_ERR_TYPE_ERROR, "ip property must be string if set"); 331 | } 332 | duk_pop(ctx); 333 | 334 | duk_get_prop_string(ctx, 0, "port"); 335 | if (duk_is_number(ctx, -1)) { 336 | port = duk_get_int(ctx, -1); 337 | } 338 | else if (!duk_is_null_or_undefined(ctx, -1)) { 339 | duk_error(ctx, DUK_ERR_TYPE_ERROR, "port property must be integer if set"); 340 | } 341 | duk_pop(ctx); 342 | 343 | if (ip || port) { 344 | if (!ip) ip = "0.0.0.0"; 345 | if (!uv_ip4_addr(ip, port, (struct sockaddr_in*)&addr)) { 346 | addr.ss_family = AF_INET; 347 | } 348 | else if (!uv_ip6_addr(ip, port, (struct sockaddr_in6*)&addr)) { 349 | addr.ss_family = AF_INET6; 350 | } 351 | else { 352 | duk_error(ctx, DUK_ERR_TYPE_ERROR, "Invalid ip address or port"); 353 | } 354 | } 355 | 356 | duk_get_prop_string(ctx, 0, "family"); 357 | if (duk_is_number(ctx, -1)) { 358 | addr.ss_family = duk_get_int(ctx, -1); 359 | } 360 | else if (duk_is_string(ctx, -1)) { 361 | addr.ss_family = duv_af_string_to_num(duk_get_string(ctx, -1)); 362 | } 363 | else if (!duk_is_null_or_undefined(ctx, -1)) { 364 | duk_error(ctx, DUK_ERR_TYPE_ERROR, "family must be string if set"); 365 | } 366 | duk_pop(ctx); 367 | 368 | uv_getnameinfo_t* req = duk_push_fixed_buffer(ctx, sizeof(*req)); 369 | duv_setup_request(ctx, (uv_req_t*)req, 1); 370 | 371 | duv_check(ctx, 372 | uv_getnameinfo(duv_loop(ctx), req, duv_on_nameinfo, (const struct sockaddr *)&addr, flags) 373 | ); 374 | return 1; 375 | } 376 | -------------------------------------------------------------------------------- /src/duv/dns.h: -------------------------------------------------------------------------------- 1 | #ifndef DNS_H 2 | #define DNS_H 3 | 4 | #include "duv.h" 5 | 6 | void duv_pushaddrinfo(duk_context *ctx, struct addrinfo* res); 7 | duk_ret_t duv_getaddrinfo(duk_context *ctx); 8 | duk_ret_t duv_getnameinfo(duk_context *ctx); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /src/duv/dschema.c: -------------------------------------------------------------------------------- 1 | #include "dschema.h" 2 | 3 | duk_bool_t dschema_is_data(duk_context* ctx, duk_idx_t index) { 4 | return duk_is_string(ctx, index) || duk_is_buffer(ctx, index); 5 | } 6 | 7 | duk_bool_t dschema_is_fd(duk_context* ctx, duk_idx_t index) { 8 | return duk_is_number(ctx, index); 9 | } 10 | 11 | duk_bool_t dschema_is_continuation(duk_context* ctx, duk_idx_t index) { 12 | return !duk_is_valid_index(ctx, index) || 13 | duk_is_function(ctx, index) || 14 | duk_is_undefined(ctx, index); 15 | } 16 | 17 | void dschema_check(duk_context *ctx, const duv_schema_entry schema[]) { 18 | int i; 19 | int top = duk_get_top(ctx); 20 | for (i = 0; schema[i].name; ++i) { 21 | if (schema[i].checker(ctx, i)) continue; 22 | fprintf(stderr, "Checking arg %d-%s\n", i, schema[i].name); 23 | duk_error(ctx, DUK_ERR_TYPE_ERROR, "Invalid argument type for %s", schema[i].name); 24 | } 25 | if (top > i) { 26 | duk_error(ctx, DUK_ERR_TYPE_ERROR, "Too many arguments. Expected at %d, but got %d", i, top); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/duv/dschema.h: -------------------------------------------------------------------------------- 1 | #ifndef DSCHEMA_H 2 | #define DSCHEMA_H 3 | 4 | #include "duv.h" 5 | 6 | typedef struct { 7 | const char* name; 8 | duk_bool_t (*checker)(duk_context *ctx, duk_idx_t index); 9 | } duv_schema_entry; 10 | 11 | duk_bool_t dschema_is_fd(duk_context* ctx, duk_idx_t index); 12 | duk_bool_t dschema_is_data(duk_context* ctx, duk_idx_t index); 13 | duk_bool_t dschema_is_continuation(duk_context* ctx, duk_idx_t index); 14 | void dschema_check(duk_context *ctx, const duv_schema_entry schema[]); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /src/duv/duv.c: -------------------------------------------------------------------------------- 1 | #include "duv.h" 2 | #include "utils.h" 3 | #include "loop.h" 4 | #include "handle.h" 5 | #include "req.h" 6 | #include "timer.h" 7 | #include "prepare.h" 8 | #include "check.h" 9 | #include "idle.h" 10 | #include "async.h" 11 | #include "stream.h" 12 | #include "tcp.h" 13 | #include "pipe.h" 14 | #include "tty.h" 15 | #include "misc.h" 16 | #include "fs.h" 17 | #include "dns.h" 18 | 19 | static const duk_function_list_entry duv_req_methods[] = { 20 | {"inspect", duv_req_tostring, 0}, 21 | {"toString", duv_req_tostring, 0}, 22 | {"cancel", duv_cancel, 0}, 23 | {0,0,0} 24 | }; 25 | 26 | static const duk_function_list_entry duv_handle_methods[] = { 27 | {"inspect", duv_tostring, 0}, 28 | {"toString", duv_tostring, 0}, 29 | {"close", duv_close, 1}, 30 | {"isActive", duv_is_active, 0}, 31 | {"isClosing", duv_is_closing, 0}, 32 | {"ref", duv_ref, 0}, 33 | {"unref", duv_unref, 0}, 34 | {"hasRef", duv_has_ref, 0}, 35 | {0,0,0} 36 | }; 37 | 38 | static const duk_function_list_entry duv_timer_methods[] = { 39 | {"start", duv_timer_start, 3}, 40 | {"stop", duv_timer_stop, 0}, 41 | {"again", duv_timer_again, 0}, 42 | {"setRepeat", duv_timer_set_repeat, 1}, 43 | {"getRepeat", duv_timer_get_repeat, 0}, 44 | {0,0,0} 45 | }; 46 | 47 | static const duk_function_list_entry duv_prepare_methods[] = { 48 | {"start", duv_prepare_start, 1}, 49 | {"stop", duv_prepare_stop, 0}, 50 | {0,0,0} 51 | }; 52 | 53 | static const duk_function_list_entry duv_check_methods[] = { 54 | {"start", duv_check_start, 1}, 55 | {"stop", duv_check_stop, 0}, 56 | {0,0,0} 57 | }; 58 | 59 | static const duk_function_list_entry duv_idle_methods[] = { 60 | {"start", duv_idle_start, 1}, 61 | {"stop", duv_idle_stop, 0}, 62 | {0,0,0} 63 | }; 64 | 65 | static const duk_function_list_entry duv_async_methods[] = { 66 | {"send", duv_async_send, 0}, 67 | {0,0,0} 68 | }; 69 | 70 | static const duk_function_list_entry duv_stream_methods[] = { 71 | {"shutdown", duv_shutdown, 1}, 72 | {"listen", duv_listen, 2}, 73 | {"accept", duv_accept, 1}, 74 | {"readStart", duv_read_start, 1}, 75 | {"readStop", duv_read_stop, 0}, 76 | {"write", duv_write, 2}, 77 | {"isReadable", duv_is_readable, 0}, 78 | {"isWritable", duv_is_writable, 0}, 79 | {"setBlocking", duv_stream_set_blocking, 1}, 80 | {0,0,0} 81 | }; 82 | 83 | static const duk_function_list_entry duv_tcp_methods[] = { 84 | {"open", duv_tcp_open, 1}, 85 | {"nodelay", duv_tcp_nodelay, 1}, 86 | {"keepalive", duv_tcp_keepalive, 2}, 87 | {"simultaneousAccepts", duv_tcp_simultaneous_accepts, 1}, 88 | {"bind", duv_tcp_bind, 2}, 89 | {"getpeername", duv_tcp_getpeername, 0}, 90 | {"getsockname", duv_tcp_getsockname, 0}, 91 | {"connect", duv_tcp_connect, 3}, 92 | {0,0,0} 93 | }; 94 | 95 | static const duk_function_list_entry duv_pipe_methods[] = { 96 | {"open", duv_pipe_open, 1}, 97 | {"bind", duv_pipe_bind, 1}, 98 | {"connect", duv_pipe_connect, 2}, 99 | {"getsockname", duv_pipe_getsockname, 0}, 100 | {"getpeername", duv_pipe_getpeername, 0}, 101 | {"pendingInstances", duv_pipe_pending_instances, 1}, 102 | {"pendingCount", duv_pipe_pending_count, 0}, 103 | {"pendingType", duv_pipe_pending_type, 0}, 104 | {0,0,0} 105 | }; 106 | 107 | static const duk_function_list_entry duv_tty_methods[] = { 108 | {"setMode", duv_tty_set_mode, 1}, 109 | {"getWinsize", duv_tty_get_winsize, 0}, 110 | {0,0,0} 111 | }; 112 | 113 | static const duk_function_list_entry duv_funcs[] = { 114 | // loop.c 115 | {"run", duv_run, 0}, 116 | {"walk", duv_walk, 1}, 117 | 118 | // libuv handle constructors 119 | {"Timer", duv_new_timer, 0}, 120 | {"Prepare", duv_new_prepare, 0}, 121 | {"Check", duv_new_check, 0}, 122 | {"Idle", duv_new_idle, 0}, 123 | {"Async", duv_new_async, 1}, 124 | {"Tcp", duv_new_tcp, 0}, 125 | {"Pipe", duv_new_pipe, 1}, 126 | {"Tty", duv_new_tty, 2}, 127 | 128 | {"ttyResetMode", duv_tty_reset_mode, 0}, 129 | 130 | // fs.c 131 | {"fs_close", duv_fs_close, 2}, 132 | {"fs_open", duv_fs_open, 4}, 133 | {"fs_read", duv_fs_read, 4}, 134 | {"fs_unlink", duv_fs_unlink, 2}, 135 | {"fs_write", duv_fs_write, 4}, 136 | {"fs_mkdir", duv_fs_mkdir, 3}, 137 | {"fs_mkdtemp", duv_fs_mkdtemp, 2}, 138 | {"fs_rmdir", duv_fs_rmdir, 2}, 139 | {"fs_scandir", duv_fs_scandir, 2}, 140 | {"fs_stat", duv_fs_stat, 2}, 141 | {"fs_fstat", duv_fs_fstat, 2}, 142 | {"fs_lstat", duv_fs_lstat, 2}, 143 | {"fs_rename", duv_fs_rename, 3}, 144 | {"fs_fsync", duv_fs_fsync, 2}, 145 | {"fs_fdatasync", duv_fs_fdatasync, 2}, 146 | {"fs_ftruncate", duv_fs_ftruncate, 3}, 147 | {"fs_sendfile", duv_fs_sendfile, 5}, 148 | {"fs_access", duv_fs_access, 3}, 149 | {"fs_chmod", duv_fs_chmod, 3}, 150 | {"fs_fchmod", duv_fs_fchmod, 3}, 151 | {"fs_utime", duv_fs_utime, 4}, 152 | {"fs_futime", duv_fs_futime, 4}, 153 | {"fs_link", duv_fs_link, 3}, 154 | {"fs_symlink", duv_fs_symlink, 4}, 155 | {"fs_readlink", duv_fs_readlink, 2}, 156 | {"fs_chown", duv_fs_chown, 4}, 157 | {"fs_fchown", duv_fs_fchown, 4}, 158 | 159 | // misc.c 160 | {"guess_handle", duv_guess_handle, 1}, 161 | {"version", duv_version, 0}, 162 | {"version_string", duv_version_string, 0}, 163 | {"get_process_title", duv_get_process_title, 0}, 164 | {"set_process_title", duv_set_process_title, 1}, 165 | {"resident_set_memory", duv_resident_set_memory, 0}, 166 | {"uptime", duv_uptime, 0}, 167 | {"getrusage", duv_getrusage, 0}, 168 | {"cpu_info", duv_cpu_info, 0}, 169 | {"interface_addresses", duv_interface_addresses, 0}, 170 | {"loadavg", duv_loadavg, 0}, 171 | {"exepath", duv_exepath, 0}, 172 | {"cwd", duv_cwd, 0}, 173 | {"os_homedir", duv_os_homedir, 0}, 174 | {"chdir", duv_chdir, 1}, 175 | {"get_total_memory", duv_get_total_memory, 0}, 176 | {"hrtime", duv_hrtime, 0}, 177 | {"update_time", duv_update_time, 0}, 178 | {"now", duv_now, 0}, 179 | {"argv", duv_argv, 0}, 180 | 181 | // dns.c 182 | {"getaddrinfo", duv_getaddrinfo, 2}, 183 | {"getnameinfo", duv_getnameinfo, 2}, 184 | 185 | // // miniz.c 186 | // {"inflate", duv_tinfl, 2}, 187 | // {"deflate", duv_tdefl, 2}, 188 | 189 | {NULL, NULL, 0}, 190 | }; 191 | 192 | duk_ret_t duv_push_module(duk_context *ctx) { 193 | 194 | // stack: nucleus 195 | 196 | // uv 197 | duk_push_object(ctx); 198 | duk_put_function_list(ctx, -1, duv_funcs); 199 | 200 | // stack: nucleus uv 201 | 202 | // uv.Req.prototype 203 | duk_push_heap_stash(ctx); 204 | duk_push_object(ctx); 205 | duk_put_function_list(ctx, -1, duv_req_methods); 206 | duk_put_prop_string(ctx, -2, "req-prototype"); 207 | duk_pop(ctx); 208 | 209 | // uv.Handle.prototype 210 | duk_push_object(ctx); 211 | duk_put_function_list(ctx, -1, duv_handle_methods); 212 | 213 | // stack: nucleus uv Handle.prototype 214 | 215 | // uv.Timer.prototype 216 | duk_get_prop_string(ctx, -2, "Timer"); 217 | duk_push_object(ctx); 218 | duk_put_function_list(ctx, -1, duv_timer_methods); 219 | duk_dup(ctx, -3); 220 | duk_set_prototype(ctx, -2); 221 | duk_put_prop_string(ctx, -2, "prototype"); 222 | duk_pop(ctx); 223 | 224 | // stack: nucleus uv Handle.prototype 225 | 226 | // uv.Prepare.prototype 227 | duk_get_prop_string(ctx, -2, "Prepare"); 228 | duk_push_object(ctx); 229 | duk_put_function_list(ctx, -1, duv_prepare_methods); 230 | duk_dup(ctx, -3); 231 | duk_set_prototype(ctx, -2); 232 | duk_put_prop_string(ctx, -2, "prototype"); 233 | duk_pop(ctx); 234 | 235 | // stack: nucleus uv Handle.prototype 236 | 237 | // uv.Check.prototype 238 | duk_get_prop_string(ctx, -2, "Check"); 239 | duk_push_object(ctx); 240 | duk_put_function_list(ctx, -1, duv_check_methods); 241 | duk_dup(ctx, -3); 242 | duk_set_prototype(ctx, -2); 243 | duk_put_prop_string(ctx, -2, "prototype"); 244 | duk_pop(ctx); 245 | 246 | // stack: nucleus uv Handle.prototype 247 | 248 | // uv.Idle.prototype 249 | duk_get_prop_string(ctx, -2, "Idle"); 250 | duk_push_object(ctx); 251 | duk_put_function_list(ctx, -1, duv_idle_methods); 252 | duk_dup(ctx, -3); 253 | duk_set_prototype(ctx, -2); 254 | duk_put_prop_string(ctx, -2, "prototype"); 255 | duk_pop(ctx); 256 | 257 | // stack: nucleus uv Handle.prototype 258 | 259 | // uv.Async.prototype 260 | duk_get_prop_string(ctx, -2, "Async"); 261 | duk_push_object(ctx); 262 | duk_put_function_list(ctx, -1, duv_async_methods); 263 | duk_dup(ctx, -3); 264 | duk_set_prototype(ctx, -2); 265 | duk_put_prop_string(ctx, -2, "prototype"); 266 | duk_pop(ctx); 267 | 268 | // stack: nucleus uv Handle.prototype 269 | 270 | // uv.Stream.prototype 271 | duk_push_object(ctx); 272 | duk_put_function_list(ctx, -1, duv_stream_methods); 273 | duk_dup(ctx, -2); 274 | duk_set_prototype(ctx, -2); 275 | 276 | // stack: nucleus uv Handle.prototype Stream.prototype 277 | 278 | // uv.Tcp.prototype 279 | duk_get_prop_string(ctx, -3, "Tcp"); 280 | duk_push_object(ctx); 281 | duk_put_function_list(ctx, -1, duv_tcp_methods); 282 | duk_dup(ctx, -3); 283 | duk_set_prototype(ctx, -2); 284 | duk_put_prop_string(ctx, -2, "prototype"); 285 | duk_pop(ctx); 286 | 287 | // stack: nucleus uv Handle.prototype Stream.prototype 288 | 289 | // uv.Pipe.prototype 290 | duk_get_prop_string(ctx, -3, "Pipe"); 291 | duk_push_object(ctx); 292 | duk_put_function_list(ctx, -1, duv_pipe_methods); 293 | duk_dup(ctx, -3); 294 | duk_set_prototype(ctx, -2); 295 | duk_put_prop_string(ctx, -2, "prototype"); 296 | duk_pop(ctx); 297 | 298 | // stack: nucleus uv Handle.prototype Stream.prototype 299 | 300 | // uv.Tty.prototype 301 | duk_get_prop_string(ctx, -3, "Tty"); 302 | duk_push_object(ctx); 303 | duk_put_function_list(ctx, -1, duv_tty_methods); 304 | duk_dup(ctx, -3); 305 | duk_set_prototype(ctx, -2); 306 | duk_put_prop_string(ctx, -2, "prototype"); 307 | duk_pop(ctx); 308 | 309 | // stack: nucleus uv Handle.prototype Stream.prototype 310 | 311 | duk_pop_2(ctx); 312 | 313 | // stack: nucleus uv 314 | 315 | return 1; 316 | } 317 | -------------------------------------------------------------------------------- /src/duv/duv.h: -------------------------------------------------------------------------------- 1 | #ifndef DUV_H 2 | #define DUV_H 3 | 4 | #include 5 | #include "../../deps/duktape-releases/src/duktape.h" 6 | 7 | #ifndef PATH_MAX 8 | #define PATH_MAX 1024 9 | #endif 10 | 11 | typedef enum { 12 | DUV_TIMER, 13 | DUV_PREPARE, 14 | DUV_CHECK, 15 | DUV_IDLE, 16 | DUV_ASYNC, 17 | DUV_POLL, 18 | DUV_SIGNAL, 19 | DUV_PROCESS, 20 | DUV_TCP, 21 | DUV_PIPE, 22 | DUV_TTY, 23 | DUV_UDP, 24 | DUV_FS_EVENT, 25 | DUV_FS_POLL, 26 | } duv_type_t; 27 | 28 | typedef enum { 29 | DUV_HANDLE_MASK = 30 | 1 << DUV_TIMER | 31 | 1 << DUV_PREPARE | 32 | 1 << DUV_CHECK | 33 | 1 << DUV_IDLE | 34 | 1 << DUV_ASYNC | 35 | 1 << DUV_POLL | 36 | 1 << DUV_SIGNAL | 37 | 1 << DUV_PROCESS | 38 | 1 << DUV_TCP | 39 | 1 << DUV_PIPE | 40 | 1 << DUV_TTY | 41 | 1 << DUV_UDP | 42 | 1 << DUV_FS_EVENT | 43 | 1 << DUV_FS_POLL, 44 | DUV_TIMER_MASK = 1 << DUV_TIMER, 45 | DUV_PREPARE_MASK = 1 << DUV_PREPARE, 46 | DUV_CHECK_MASK = 1 << DUV_CHECK, 47 | DUV_IDLE_MASK = 1 << DUV_IDLE, 48 | DUV_ASYNC_MASK = 1 << DUV_ASYNC, 49 | DUV_POLL_MASK = 1 << DUV_POLL, 50 | DUV_SIGNAL_MASK = 1 << DUV_SIGNAL, 51 | DUV_PROCESS_MASK = 1 << DUV_PROCESS, 52 | DUV_STREAM_MASK = 1 << DUV_TCP | 1 << DUV_PIPE | 1 << DUV_TTY, 53 | DUV_TCP_MASK = 1 << DUV_TCP, 54 | DUV_PIPE_MASK = 1 << DUV_PIPE, 55 | DUV_TTY_MASK = 1 << DUV_TTY, 56 | DUV_UDP_MASK = 1 << DUV_UDP, 57 | DUV_FS_EVENT_MASK = 1 << DUV_FS_EVENT, 58 | DUV_FS_POLL_MASK = 1 << DUV_FS_POLL, 59 | } duv_type_mask_t; 60 | 61 | // Create a new duv instance with own loop, and push onto duktape context. 62 | duk_ret_t duv_push_module(duk_context *ctx); 63 | 64 | #include "utils.h" 65 | #include "callbacks.h" 66 | 67 | #endif 68 | -------------------------------------------------------------------------------- /src/duv/fs.c: -------------------------------------------------------------------------------- 1 | #include "fs.h" 2 | #include 3 | #include 4 | 5 | static void duv_push_timespec_table(duk_context *ctx, const uv_timespec_t* t) { 6 | duk_push_object(ctx); 7 | duk_push_uint(ctx, t->tv_sec); 8 | duk_put_prop_string(ctx, -2, "sec"); 9 | duk_push_uint(ctx, t->tv_nsec); 10 | duk_put_prop_string(ctx, -2, "nsec"); 11 | } 12 | 13 | static void duv_push_stats_table(duk_context *ctx, const uv_stat_t* s) { 14 | const char* type = NULL; 15 | duk_push_object(ctx); 16 | duk_push_uint(ctx, s->st_mode); 17 | duk_put_prop_string(ctx, -2, "mode"); 18 | duk_push_uint(ctx, s->st_uid); 19 | duk_put_prop_string(ctx, -2, "uid"); 20 | duk_push_uint(ctx, s->st_gid); 21 | duk_put_prop_string(ctx, -2, "gid"); 22 | duk_push_uint(ctx, s->st_size); 23 | duk_put_prop_string(ctx, -2, "size"); 24 | duv_push_timespec_table(ctx, &s->st_atim); 25 | duk_put_prop_string(ctx, -2, "atime"); 26 | duv_push_timespec_table(ctx, &s->st_mtim); 27 | duk_put_prop_string(ctx, -2, "mtime"); 28 | duv_push_timespec_table(ctx, &s->st_ctim); 29 | duk_put_prop_string(ctx, -2, "ctime"); 30 | if (S_ISREG(s->st_mode)) { 31 | type = "file"; 32 | } 33 | else if (S_ISDIR(s->st_mode)) { 34 | type = "directory"; 35 | } 36 | else if (S_ISLNK(s->st_mode)) { 37 | type = "link"; 38 | } 39 | else if (S_ISFIFO(s->st_mode)) { 40 | type = "fifo"; 41 | } 42 | #ifdef S_ISSOCK 43 | else if (S_ISSOCK(s->st_mode)) { 44 | type = "socket"; 45 | } 46 | #endif 47 | else if (S_ISCHR(s->st_mode)) { 48 | type = "char"; 49 | } 50 | else if (S_ISBLK(s->st_mode)) { 51 | type = "block"; 52 | } 53 | if (type) { 54 | duk_push_string(ctx, type); 55 | duk_put_prop_string(ctx, -2, "type"); 56 | } 57 | } 58 | 59 | static int duv_string_to_flags(duk_context *ctx, const char* string) { 60 | bool read = false; 61 | bool write = false; 62 | int flags = 0; 63 | while (string && string[0]) { 64 | switch (string[0]) { 65 | case 'r': read = true; break; 66 | case 'w': write = true; flags |= O_TRUNC | O_CREAT; break; 67 | case 'a': write = true; flags |= O_APPEND | O_CREAT; break; 68 | case '+': read = true; write = true; break; 69 | case 'x': flags |= O_EXCL; break; 70 | #ifndef _WIN32 71 | case 's': flags |= O_SYNC; break; 72 | #endif 73 | 74 | default: 75 | duk_error(ctx, DUK_ERR_TYPE_ERROR, "Unknown file open flag '%s'", string); 76 | return 0; 77 | } 78 | string++; 79 | } 80 | flags |= read ? (write ? O_RDWR : O_RDONLY) : 81 | (write ? O_WRONLY : 0); 82 | return flags; 83 | } 84 | 85 | static void duv_push_error_result(duk_context *ctx, uv_fs_t* req) { 86 | if (req->path) { 87 | duk_push_error_object(ctx, DUK_ERR_ERROR, "%s: %s: %s", uv_err_name(req->result), uv_strerror(req->result), req->path); 88 | } 89 | else { 90 | duk_push_error_object(ctx, DUK_ERR_ERROR, "%s: %s", uv_err_name(req->result), uv_strerror(req->result)); 91 | } 92 | } 93 | 94 | /* Processes a result and pushes the data onto the stack 95 | returns the number of items pushed */ 96 | static void duv_push_fs_result(duk_context *ctx, uv_fs_t* req) { 97 | 98 | char* chunk; 99 | 100 | switch (req->fs_type) { 101 | case UV_FS_CLOSE: 102 | case UV_FS_RENAME: 103 | case UV_FS_UNLINK: 104 | case UV_FS_RMDIR: 105 | case UV_FS_MKDIR: 106 | case UV_FS_FTRUNCATE: 107 | case UV_FS_FSYNC: 108 | case UV_FS_FDATASYNC: 109 | case UV_FS_LINK: 110 | case UV_FS_SYMLINK: 111 | case UV_FS_CHMOD: 112 | case UV_FS_FCHMOD: 113 | case UV_FS_CHOWN: 114 | case UV_FS_FCHOWN: 115 | case UV_FS_UTIME: 116 | case UV_FS_FUTIME: 117 | duk_push_boolean(ctx, 1); 118 | break; 119 | 120 | case UV_FS_OPEN: 121 | case UV_FS_SENDFILE: 122 | case UV_FS_WRITE: 123 | duk_push_uint(ctx, req->result); 124 | break; 125 | 126 | case UV_FS_STAT: 127 | case UV_FS_LSTAT: 128 | case UV_FS_FSTAT: 129 | duv_push_stats_table(ctx, &req->statbuf); 130 | break; 131 | 132 | case UV_FS_READLINK: 133 | duk_push_string(ctx, (char*)req->ptr); 134 | break; 135 | 136 | case UV_FS_MKDTEMP: 137 | duk_push_string(ctx, req->path); 138 | break; 139 | 140 | case UV_FS_READ: 141 | chunk = duk_push_fixed_buffer(ctx, req->result); 142 | duv_push_handle(ctx, req); 143 | duk_get_prop_string(ctx, -1, "\xff""uv-extra"); 144 | uint8_t* base = duk_get_pointer(ctx, -1); 145 | duk_pop_2(ctx); 146 | memcpy(chunk, base, req->result); 147 | break; 148 | 149 | case UV_FS_SCANDIR: 150 | duk_push_object(ctx); 151 | int ret; 152 | uv_dirent_t ent; 153 | while ((ret = uv_fs_scandir_next(req, &ent)) != UV_EOF) { 154 | if (ret < 0) duv_error(ctx, ret); 155 | duk_push_string(ctx, ent.name); 156 | const char* type; 157 | switch (ent.type) { 158 | case UV_DIRENT_FILE: type = "file"; break; 159 | case UV_DIRENT_DIR: type = "directory"; break; 160 | case UV_DIRENT_LINK: type = "link"; break; 161 | case UV_DIRENT_FIFO: type = "fifo"; break; 162 | case UV_DIRENT_SOCKET: type = "socket"; break; 163 | case UV_DIRENT_CHAR: type = "char"; break; 164 | case UV_DIRENT_BLOCK: type = "block"; break; 165 | default: type = "unknown"; break; 166 | } 167 | duk_push_string(ctx, type); 168 | duk_put_prop(ctx, -3); 169 | } 170 | break; 171 | 172 | default: 173 | duk_push_error_object(ctx, DUK_ERR_TYPE_ERROR, "UNKNOWN FS TYPE %d\n", req->fs_type); 174 | break; 175 | } 176 | 177 | } 178 | 179 | static void duv_fs_cb(uv_fs_t* req) { 180 | duk_context *ctx = req->data; 181 | int nargs; 182 | 183 | if (req->result < 0) { 184 | duv_push_error_result(ctx, req); 185 | nargs = 1; 186 | } 187 | else { 188 | duk_push_null(ctx); 189 | duv_push_fs_result(ctx, req); 190 | nargs = 2; 191 | } 192 | 193 | duv_resolve((uv_req_t*)req, nargs); 194 | } 195 | 196 | #define FS_CALL(func, cb, save, extra, req, ...) { \ 197 | duv_check(ctx, uv_fs_##func(duv_loop(ctx), req, __VA_ARGS__, duv_fs_cb)); \ 198 | duv_setup_request(ctx, (uv_req_t*)req, cb); \ 199 | if (extra) { \ 200 | duk_push_pointer(ctx, extra); \ 201 | duk_put_prop_string(ctx, -2, "\xff""uv-extra"); \ 202 | } \ 203 | if (save) { \ 204 | duk_dup(ctx, save); \ 205 | duk_put_prop_string(ctx, -2, "\xff""uv-save"); \ 206 | } \ 207 | return 1; \ 208 | } 209 | 210 | duk_ret_t duv_fs_close(duk_context *ctx) { 211 | dschema_check(ctx, (const duv_schema_entry[]) { 212 | {"fd", dschema_is_fd}, 213 | {"callback", dschema_is_continuation}, 214 | {0,0} 215 | }); 216 | uv_file file = duk_get_int(ctx, 0); 217 | uv_fs_t* req = duk_push_fixed_buffer(ctx, sizeof(*req)); 218 | FS_CALL(close, 1, 0, 0, req, file); 219 | } 220 | 221 | duk_ret_t duv_fs_open(duk_context *ctx) { 222 | dschema_check(ctx, (const duv_schema_entry[]) { 223 | {"path", duk_is_string}, 224 | {"flags", duk_is_string}, 225 | {"mode", duk_is_number}, 226 | {"callback", dschema_is_continuation}, 227 | {0,0} 228 | }); 229 | const char* path = duk_get_string(ctx, 0); 230 | int flags = duv_string_to_flags(ctx, duk_get_string(ctx, 1)); 231 | int mode = duk_get_uint(ctx, 2); 232 | uv_fs_t* req = duk_push_fixed_buffer(ctx, sizeof(*req)); 233 | FS_CALL(open, 3, 0, 0, req, path, flags, mode); 234 | } 235 | 236 | duk_ret_t duv_fs_read(duk_context *ctx) { 237 | dschema_check(ctx, (const duv_schema_entry[]) { 238 | {"fd", dschema_is_fd}, 239 | {"len", duk_is_number}, 240 | {"offset", duk_is_number}, 241 | {"callback", dschema_is_continuation}, 242 | {0,0} 243 | }); 244 | uv_file file = duk_get_uint(ctx, 0); 245 | int64_t len = duk_get_uint(ctx, 1); 246 | int64_t offset = duk_get_uint(ctx, 2); 247 | uv_buf_t buf; 248 | uv_fs_t* req; 249 | buf = uv_buf_init(duk_alloc(ctx, len), len); 250 | req = duk_push_fixed_buffer(ctx, sizeof(*req)); 251 | FS_CALL(read, 3, 0, buf.base, req, file, &buf, 1, offset); 252 | } 253 | 254 | duk_ret_t duv_fs_unlink(duk_context *ctx) { 255 | dschema_check(ctx, (const duv_schema_entry[]) { 256 | {"path", duk_is_string}, 257 | {"callback", dschema_is_continuation}, 258 | {0,0} 259 | }); 260 | const char* path = duk_get_string(ctx, 0); 261 | uv_fs_t* req = duk_push_fixed_buffer(ctx, sizeof(*req)); 262 | FS_CALL(unlink, 1, 0, 0, req, path); 263 | } 264 | 265 | duk_ret_t duv_fs_write(duk_context *ctx) { 266 | dschema_check(ctx, (const duv_schema_entry[]){ 267 | {"fd", dschema_is_fd}, 268 | {"data", dschema_is_data}, 269 | {"offset", duk_is_number}, 270 | {"callback", dschema_is_continuation}, 271 | {0,0} 272 | }); 273 | uv_file file = duk_get_uint(ctx, 0); 274 | uv_buf_t buf; 275 | int64_t offset = duk_get_number(ctx, 2);; 276 | uv_fs_t* req = duk_push_fixed_buffer(ctx, sizeof(*req)); 277 | duv_get_data(ctx, 1, &buf); 278 | FS_CALL(write, 3, 1, 0, req, file, &buf, 1, offset); 279 | } 280 | 281 | duk_ret_t duv_fs_mkdir(duk_context *ctx) { 282 | dschema_check(ctx, (const duv_schema_entry[]) { 283 | {"path", duk_is_string}, 284 | {"mode", duk_is_number}, 285 | {"callback", dschema_is_continuation}, 286 | {0,0} 287 | }); 288 | const char* path = duk_get_string(ctx, 0); 289 | int mode = duk_get_uint(ctx, 1); 290 | uv_fs_t* req = duk_push_fixed_buffer(ctx, sizeof(*req)); 291 | FS_CALL(mkdir, 2, 0, 0, req, path, mode); 292 | } 293 | 294 | duk_ret_t duv_fs_mkdtemp(duk_context *ctx) { 295 | dschema_check(ctx, (const duv_schema_entry[]) { 296 | {"template", duk_is_string}, 297 | {"callback", dschema_is_continuation}, 298 | {0,0} 299 | }); 300 | const char* tpl = duk_get_string(ctx, 0); 301 | uv_fs_t* req = duk_push_fixed_buffer(ctx, sizeof(*req)); 302 | FS_CALL(mkdtemp, 1, 0, 0, req, tpl); 303 | } 304 | 305 | duk_ret_t duv_fs_rmdir(duk_context *ctx) { 306 | dschema_check(ctx, (const duv_schema_entry[]) { 307 | {"path", duk_is_string}, 308 | {"callback", dschema_is_continuation}, 309 | {0,0} 310 | }); 311 | const char* path = duk_get_string(ctx, 0); 312 | uv_fs_t* req = duk_push_fixed_buffer(ctx, sizeof(*req)); 313 | FS_CALL(rmdir, 1, 0, 0, req, path); 314 | } 315 | 316 | duk_ret_t duv_fs_scandir(duk_context *ctx) { 317 | dschema_check(ctx, (const duv_schema_entry[]) { 318 | {"path", duk_is_string}, 319 | {"callback", dschema_is_continuation}, 320 | {0,0} 321 | }); 322 | const char* path = duk_get_string(ctx, 0); 323 | int flags = 0; // TODO: find out what these flags are. 324 | uv_fs_t* req = duk_push_fixed_buffer(ctx, sizeof(*req)); 325 | FS_CALL(scandir, 1, 0, 0, req, path, flags); 326 | } 327 | 328 | duk_ret_t duv_fs_stat(duk_context *ctx) { 329 | dschema_check(ctx, (const duv_schema_entry[]) { 330 | {"path", duk_is_string}, 331 | {"callback", dschema_is_continuation}, 332 | {0,0} 333 | }); 334 | const char* path = duk_get_string(ctx, 0); 335 | uv_fs_t* req = duk_push_fixed_buffer(ctx, sizeof(*req)); 336 | FS_CALL(stat, 1, 0, 0, req, path); 337 | } 338 | 339 | duk_ret_t duv_fs_fstat(duk_context *ctx) { 340 | dschema_check(ctx, (const duv_schema_entry[]) { 341 | {"fd", dschema_is_fd}, 342 | {"callback", dschema_is_continuation}, 343 | {0,0} 344 | }); 345 | uv_file file = duk_get_uint(ctx, 0); 346 | uv_fs_t* req = duk_push_fixed_buffer(ctx, sizeof(*req)); 347 | FS_CALL(fstat, 1, 0, 0, req, file); 348 | } 349 | 350 | duk_ret_t duv_fs_lstat(duk_context *ctx) { 351 | dschema_check(ctx, (const duv_schema_entry[]) { 352 | {"path", duk_is_string}, 353 | {"callback", dschema_is_continuation}, 354 | {0,0} 355 | }); 356 | const char* path = duk_get_string(ctx, 0); 357 | uv_fs_t* req = duk_push_fixed_buffer(ctx, sizeof(*req)); 358 | FS_CALL(lstat, 1, 0, 0, req, path); 359 | } 360 | 361 | duk_ret_t duv_fs_rename(duk_context *ctx) { 362 | dschema_check(ctx, (const duv_schema_entry[]) { 363 | {"path", duk_is_string}, 364 | {"newPath", duk_is_string}, 365 | {"callback", dschema_is_continuation}, 366 | {0,0} 367 | }); 368 | const char* path = duk_get_string(ctx, 0); 369 | const char* new_path = duk_get_string(ctx, 1); 370 | uv_fs_t* req = duk_push_fixed_buffer(ctx, sizeof(*req)); 371 | FS_CALL(rename, 2, 0, 0, req, path, new_path); 372 | } 373 | 374 | duk_ret_t duv_fs_fsync(duk_context *ctx) { 375 | dschema_check(ctx, (const duv_schema_entry[]) { 376 | {"fd", dschema_is_fd}, 377 | {"callback", dschema_is_continuation}, 378 | {0,0} 379 | }); 380 | uv_file file = duk_get_uint(ctx, 0); 381 | uv_fs_t* req = duk_push_fixed_buffer(ctx, sizeof(*req)); 382 | FS_CALL(fsync, 1, 0, 0, req, file); 383 | } 384 | 385 | duk_ret_t duv_fs_fdatasync(duk_context *ctx) { 386 | dschema_check(ctx, (const duv_schema_entry[]) { 387 | {"fd", dschema_is_fd}, 388 | {"callback", dschema_is_continuation}, 389 | {0,0} 390 | }); 391 | uv_file file = duk_get_uint(ctx, 0); 392 | uv_fs_t* req = duk_push_fixed_buffer(ctx, sizeof(*req)); 393 | FS_CALL(fdatasync, 1, 0, 0, req, file); 394 | } 395 | 396 | duk_ret_t duv_fs_ftruncate(duk_context *ctx) { 397 | dschema_check(ctx, (const duv_schema_entry[]) { 398 | {"fd", dschema_is_fd}, 399 | {"offset", duk_is_number}, 400 | {"callback", dschema_is_continuation}, 401 | {0,0} 402 | }); 403 | uv_file file = duk_get_uint(ctx, 0); 404 | int64_t offset = duk_get_uint(ctx, 1); 405 | uv_fs_t* req = duk_push_fixed_buffer(ctx, sizeof(*req)); 406 | FS_CALL(ftruncate, 2, 0, 0, req, file, offset); 407 | } 408 | 409 | duk_ret_t duv_fs_sendfile(duk_context *ctx) { 410 | dschema_check(ctx, (const duv_schema_entry[]) { 411 | {"outFd", dschema_is_fd}, 412 | {"inFd", dschema_is_fd}, 413 | {"inOffset", duk_is_number}, 414 | {"length", duk_is_number}, 415 | {"callback", dschema_is_continuation}, 416 | {0,0} 417 | }); 418 | uv_file out_fd = duk_get_uint(ctx, 0); 419 | uv_file in_fd = duk_get_uint(ctx, 1); 420 | int64_t in_offset = duk_get_uint(ctx, 2); 421 | size_t length = duk_get_uint(ctx, 3); 422 | uv_fs_t* req = duk_push_fixed_buffer(ctx, sizeof(*req)); 423 | FS_CALL(sendfile, 4, 0, 0, req, out_fd, in_fd, in_offset, length); 424 | } 425 | 426 | duk_ret_t duv_fs_access(duk_context *ctx) { 427 | dschema_check(ctx, (const duv_schema_entry[]) { 428 | {"path", duk_is_string}, 429 | {"flags", duk_is_string}, 430 | {"callback", dschema_is_continuation}, 431 | {0,0} 432 | }); 433 | const char* path = duk_get_string(ctx, 0); 434 | const char* string = duk_get_string(ctx, 1); 435 | int flags = 0; 436 | size_t i, l; 437 | for (i = 0, l = strlen(string); i < l; ++i) { 438 | switch (string[i]) { 439 | case 'r': case 'R': flags |= R_OK; break; 440 | case 'w': case 'W': flags |= W_OK; break; 441 | case 'x': case 'X': flags |= X_OK; break; 442 | default: 443 | duk_error(ctx, DUK_ERR_TYPE_ERROR, "Unknown file access mode '%s'", string); 444 | return 0; 445 | } 446 | } 447 | uv_fs_t* req = duk_push_fixed_buffer(ctx, sizeof(*req)); 448 | FS_CALL(access, 2, 0, 0, req, path, flags); 449 | } 450 | 451 | duk_ret_t duv_fs_chmod(duk_context *ctx) { 452 | dschema_check(ctx, (const duv_schema_entry[]) { 453 | {"path", duk_is_string}, 454 | {"mode", duk_is_number}, 455 | {"callback", dschema_is_continuation}, 456 | {0,0} 457 | }); 458 | const char* path = duk_get_string(ctx, 0); 459 | int mode = duk_get_uint(ctx, 1); 460 | uv_fs_t* req = duk_push_fixed_buffer(ctx, sizeof(*req)); 461 | FS_CALL(chmod, 2, 0, 0, req, path, mode); 462 | } 463 | 464 | duk_ret_t duv_fs_fchmod(duk_context *ctx) { 465 | dschema_check(ctx, (const duv_schema_entry[]) { 466 | {"fd", dschema_is_fd}, 467 | {"mode", duk_is_number}, 468 | {"callback", dschema_is_continuation}, 469 | {0,0} 470 | }); 471 | uv_file file = duk_get_uint(ctx, 0); 472 | int mode = duk_get_uint(ctx, 1); 473 | uv_fs_t* req = duk_push_fixed_buffer(ctx, sizeof(*req)); 474 | FS_CALL(fchmod, 2, 0, 0, req, file, mode); 475 | } 476 | 477 | duk_ret_t duv_fs_utime(duk_context *ctx) { 478 | dschema_check(ctx, (const duv_schema_entry[]) { 479 | {"path", duk_is_string}, 480 | {"atime", duk_is_number}, 481 | {"mtime", duk_is_number}, 482 | {"callback", dschema_is_continuation}, 483 | {0,0} 484 | }); 485 | const char* path = duk_get_string(ctx, 0); 486 | double atime = duk_get_number(ctx, 1); 487 | double mtime = duk_get_number(ctx, 2); 488 | uv_fs_t* req = duk_push_fixed_buffer(ctx, sizeof(*req)); 489 | FS_CALL(utime, 3, 0, 0, req, path, atime, mtime); 490 | } 491 | 492 | duk_ret_t duv_fs_futime(duk_context *ctx) { 493 | dschema_check(ctx, (const duv_schema_entry[]) { 494 | {"fd", dschema_is_fd}, 495 | {"atime", duk_is_number}, 496 | {"mtime", duk_is_number}, 497 | {"callback", dschema_is_continuation}, 498 | {0,0} 499 | }); 500 | uv_file file = duk_get_uint(ctx, 0); 501 | double atime = duk_get_number(ctx, 1); 502 | double mtime = duk_get_number(ctx, 2); 503 | uv_fs_t* req = duk_push_fixed_buffer(ctx, sizeof(*req)); 504 | FS_CALL(futime, 3, 0, 0, req, file, atime, mtime); 505 | } 506 | 507 | duk_ret_t duv_fs_link(duk_context *ctx) { 508 | dschema_check(ctx, (const duv_schema_entry[]) { 509 | {"path", duk_is_string}, 510 | {"newPath", duk_is_string}, 511 | {"callback", dschema_is_continuation}, 512 | {0,0} 513 | }); 514 | const char* path = duk_get_string(ctx, 0); 515 | const char* new_path = duk_get_string(ctx, 1); 516 | uv_fs_t* req = duk_push_fixed_buffer(ctx, sizeof(*req)); 517 | FS_CALL(link, 2, 0, 0, req, path, new_path); 518 | } 519 | 520 | duk_ret_t duv_fs_symlink(duk_context *ctx) { 521 | const char* path = duk_require_string(ctx, 0); 522 | const char* new_path = duk_require_string(ctx, 1); 523 | int flags = 0; 524 | uv_fs_t* req; 525 | if (duk_is_object(ctx, 2)) { 526 | duk_get_prop_string(ctx, 2, "dir"); 527 | if (duk_get_boolean(ctx, -1)) flags |= UV_FS_SYMLINK_DIR; 528 | duk_pop(ctx); 529 | duk_get_prop_string(ctx, 2, "junction"); 530 | if (duk_get_boolean(ctx, -1)) flags |= UV_FS_SYMLINK_JUNCTION; 531 | duk_pop(ctx); 532 | } 533 | req = duk_push_fixed_buffer(ctx, sizeof(*req)); 534 | FS_CALL(symlink, 3, 0, 0, req, path, new_path, flags); 535 | } 536 | 537 | duk_ret_t duv_fs_readlink(duk_context *ctx) { 538 | dschema_check(ctx, (const duv_schema_entry[]) { 539 | {"path", duk_is_string}, 540 | {"callback", dschema_is_continuation}, 541 | {0,0} 542 | }); 543 | const char* path = duk_get_string(ctx, 0); 544 | uv_fs_t* req = duk_push_fixed_buffer(ctx, sizeof(*req)); 545 | FS_CALL(readlink, 1, 0, 0, req, path); 546 | } 547 | 548 | duk_ret_t duv_fs_chown(duk_context *ctx) { 549 | dschema_check(ctx, (const duv_schema_entry[]) { 550 | {"path", duk_is_string}, 551 | {"uid", duk_is_number}, 552 | {"gid", duk_is_number}, 553 | {"callback", dschema_is_continuation}, 554 | {0,0} 555 | }); 556 | const char* path = duk_get_string(ctx, 0); 557 | uv_uid_t uid = duk_get_uint(ctx, 1); 558 | uv_uid_t gid = duk_get_uint(ctx, 2); 559 | uv_fs_t* req = duk_push_fixed_buffer(ctx, sizeof(*req)); 560 | FS_CALL(chown, 3, 0, 0, req, path, uid, gid); 561 | } 562 | 563 | duk_ret_t duv_fs_fchown(duk_context *ctx) { 564 | dschema_check(ctx, (const duv_schema_entry[]) { 565 | {"fd", dschema_is_fd}, 566 | {"uid", duk_is_number}, 567 | {"gid", duk_is_number}, 568 | {"callback", dschema_is_continuation}, 569 | {0,0} 570 | }); 571 | uv_file file = duk_get_uint(ctx, 0); 572 | uv_uid_t uid = duk_get_uint(ctx, 1); 573 | uv_uid_t gid = duk_get_uint(ctx, 2); 574 | uv_fs_t* req = duk_push_fixed_buffer(ctx, sizeof(*req)); 575 | FS_CALL(fchown, 3, 0, 0, req, file, uid, gid); 576 | } 577 | -------------------------------------------------------------------------------- /src/duv/fs.h: -------------------------------------------------------------------------------- 1 | #ifndef FS_H 2 | #define FS_H 3 | 4 | #include "duv.h" 5 | 6 | duk_ret_t duv_fs_close(duk_context *ctx); 7 | duk_ret_t duv_fs_open(duk_context *ctx); 8 | duk_ret_t duv_fs_read(duk_context *ctx); 9 | duk_ret_t duv_fs_unlink(duk_context *ctx); 10 | duk_ret_t duv_fs_write(duk_context *ctx); 11 | duk_ret_t duv_fs_mkdir(duk_context *ctx); 12 | duk_ret_t duv_fs_mkdtemp(duk_context *ctx); 13 | duk_ret_t duv_fs_rmdir(duk_context *ctx); 14 | duk_ret_t duv_fs_scandir(duk_context *ctx); 15 | duk_ret_t duv_fs_stat(duk_context *ctx); 16 | duk_ret_t duv_fs_fstat(duk_context *ctx); 17 | duk_ret_t duv_fs_lstat(duk_context *ctx); 18 | duk_ret_t duv_fs_rename(duk_context *ctx); 19 | duk_ret_t duv_fs_fsync(duk_context *ctx); 20 | duk_ret_t duv_fs_fdatasync(duk_context *ctx); 21 | duk_ret_t duv_fs_ftruncate(duk_context *ctx); 22 | duk_ret_t duv_fs_sendfile(duk_context *ctx); 23 | duk_ret_t duv_fs_access(duk_context *ctx); 24 | duk_ret_t duv_fs_chmod(duk_context *ctx); 25 | duk_ret_t duv_fs_fchmod(duk_context *ctx); 26 | duk_ret_t duv_fs_utime(duk_context *ctx); 27 | duk_ret_t duv_fs_futime(duk_context *ctx); 28 | duk_ret_t duv_fs_link(duk_context *ctx); 29 | duk_ret_t duv_fs_symlink(duk_context *ctx); 30 | duk_ret_t duv_fs_readlink(duk_context *ctx); 31 | duk_ret_t duv_fs_chown(duk_context *ctx); 32 | duk_ret_t duv_fs_fchown(duk_context *ctx); 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /src/duv/fs_event.c: -------------------------------------------------------------------------------- 1 | #include "fs_event.h" 2 | -------------------------------------------------------------------------------- /src/duv/fs_event.h: -------------------------------------------------------------------------------- 1 | #ifndef FS_EVENT_H 2 | #define FS_EVENT_H 3 | 4 | #include "duv.h" 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /src/duv/fs_poll.c: -------------------------------------------------------------------------------- 1 | #include "fs_poll.h" 2 | -------------------------------------------------------------------------------- /src/duv/fs_poll.h: -------------------------------------------------------------------------------- 1 | #ifndef FS_POLL_H 2 | #define FS_POLL_H 3 | 4 | #include "duv.h" 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /src/duv/handle.c: -------------------------------------------------------------------------------- 1 | #include "handle.h" 2 | 3 | duk_ret_t duv_tostring(duk_context *ctx) { 4 | duk_push_this(ctx); 5 | duk_get_prop_string(ctx, -1, "\xffuv-type"); 6 | if (duk_is_undefined(ctx, -1)) return 0; 7 | duk_get_prop_string(ctx, -2, "\xffuv-data"); 8 | const char* type = duv_type_to_string(duk_get_int(ctx, -2)); 9 | void* data = duk_get_buffer(ctx, -1, 0); 10 | duk_pop_3(ctx); 11 | duk_push_sprintf(ctx, "[%s %p]", type, data); 12 | return 1; 13 | } 14 | 15 | duk_ret_t duv_close(duk_context *ctx) { 16 | dschema_check(ctx, (const duv_schema_entry[]) { 17 | {"callback", dschema_is_continuation}, 18 | {0,0} 19 | }); 20 | uv_handle_t *handle = duv_require_this_handle(ctx, DUV_HANDLE_MASK); 21 | duk_put_prop_string(ctx, 0, "\xffon-close"); 22 | uv_close(handle, duv_on_close); 23 | return 0; 24 | } 25 | 26 | 27 | duk_ret_t duv_is_active(duk_context *ctx) { 28 | uv_handle_t *handle = duv_require_this_handle(ctx, DUV_HANDLE_MASK); 29 | duk_push_boolean(ctx, uv_is_active(handle)); 30 | return 1; 31 | } 32 | 33 | duk_ret_t duv_is_closing(duk_context *ctx) { 34 | uv_handle_t *handle = duv_require_this_handle(ctx, DUV_HANDLE_MASK); 35 | duk_push_boolean(ctx, uv_is_closing(handle)); 36 | return 1; 37 | } 38 | 39 | duk_ret_t duv_ref(duk_context *ctx) { 40 | uv_handle_t *handle = duv_require_this_handle(ctx, DUV_HANDLE_MASK); 41 | uv_ref(handle); 42 | return 0; 43 | } 44 | 45 | duk_ret_t duv_unref(duk_context *ctx) { 46 | uv_handle_t *handle = duv_require_this_handle(ctx, DUV_HANDLE_MASK); 47 | uv_unref(handle); 48 | return 0; 49 | } 50 | 51 | duk_ret_t duv_has_ref(duk_context *ctx) { 52 | uv_handle_t *handle = duv_require_this_handle(ctx, DUV_HANDLE_MASK); 53 | duk_push_boolean(ctx, uv_has_ref(handle)); 54 | return 1; 55 | } 56 | -------------------------------------------------------------------------------- /src/duv/handle.h: -------------------------------------------------------------------------------- 1 | #ifndef HANDLE_H 2 | #define HANDLE_H 3 | 4 | #include "duv.h" 5 | 6 | duk_ret_t duv_tostring(duk_context *ctx); 7 | duk_ret_t duv_close(duk_context *ctx); 8 | duk_ret_t duv_is_active(duk_context *ctx); 9 | duk_ret_t duv_is_closing(duk_context *ctx); 10 | duk_ret_t duv_ref(duk_context *ctx); 11 | duk_ret_t duv_unref(duk_context *ctx); 12 | duk_ret_t duv_has_ref(duk_context *ctx); 13 | 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /src/duv/idle.c: -------------------------------------------------------------------------------- 1 | #include "idle.h" 2 | 3 | duk_ret_t duv_new_idle(duk_context *ctx) { 4 | uv_idle_t *idle = duk_push_fixed_buffer(ctx, sizeof(uv_idle_t)); 5 | duv_check(ctx, uv_idle_init(duv_loop(ctx), idle)); 6 | duv_setup_handle(ctx, (uv_handle_t*)idle, DUV_IDLE); 7 | return 1; 8 | } 9 | 10 | duk_ret_t duv_idle_start(duk_context *ctx) { 11 | dschema_check(ctx, (const duv_schema_entry[]) { 12 | {"callback", duk_is_function}, 13 | {0,0} 14 | }); 15 | uv_idle_t *idle = duv_require_this_handle(ctx, DUV_IDLE_MASK); 16 | duk_put_prop_string(ctx, 0, "\xffon-idle"); 17 | duv_check(ctx, uv_idle_start(idle, duv_on_idle)); 18 | return 0; 19 | } 20 | 21 | duk_ret_t duv_idle_stop(duk_context *ctx) { 22 | uv_idle_t *idle = duv_require_this_handle(ctx, DUV_IDLE_MASK); 23 | duk_del_prop_string(ctx, -1, "\xffon-idle"); 24 | duv_check(ctx, uv_idle_stop(idle)); 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /src/duv/idle.h: -------------------------------------------------------------------------------- 1 | #ifndef IDLE_H 2 | #define IDLE_H 3 | 4 | #include "duv.h" 5 | 6 | duk_ret_t duv_new_idle(duk_context *ctx); 7 | duk_ret_t duv_idle_start(duk_context *ctx); 8 | duk_ret_t duv_idle_stop(duk_context *ctx); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /src/duv/loop.c: -------------------------------------------------------------------------------- 1 | #include "loop.h" 2 | 3 | duk_ret_t duv_run(duk_context *ctx) { 4 | int ret = uv_run(duv_loop(ctx), UV_RUN_DEFAULT); 5 | if (ret < 0) { 6 | duv_error(ctx, ret); 7 | } 8 | return 0; 9 | } 10 | 11 | static void duv_walk_cb(uv_handle_t *handle, void* c) { 12 | duk_context *ctx = c; 13 | duk_dup(ctx, 0); 14 | duv_push_handle(ctx, handle); 15 | duk_call(ctx, 1); 16 | duk_pop(ctx); 17 | } 18 | 19 | duk_ret_t duv_walk(duk_context *ctx) { 20 | dschema_check(ctx, (const duv_schema_entry[]) { 21 | {"callback", duk_is_function}, 22 | {0,0} 23 | }); 24 | uv_walk(duv_loop(ctx), duv_walk_cb, ctx); 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /src/duv/loop.h: -------------------------------------------------------------------------------- 1 | #ifndef LOOP_H 2 | #define LOOP_H 3 | 4 | #include "duv.h" 5 | 6 | duk_ret_t duv_run(duk_context *ctx); 7 | duk_ret_t duv_walk(duk_context *ctx); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /src/duv/misc.c: -------------------------------------------------------------------------------- 1 | #include "misc.h" 2 | 3 | duk_ret_t duv_guess_handle(duk_context *ctx) { 4 | uv_file file; 5 | 6 | dschema_check(ctx, (const duv_schema_entry[]) { 7 | {"fd", dschema_is_fd}, 8 | {0,0} 9 | }); 10 | 11 | file = duk_get_uint(ctx, 0); 12 | switch (uv_guess_handle(file)) { 13 | #define XX(uc, lc) case UV_##uc: duk_push_string(ctx, #uc); break; 14 | UV_HANDLE_TYPE_MAP(XX) 15 | #undef XX 16 | case UV_FILE: duk_push_string(ctx, "FILE"); break; 17 | default: return 0; 18 | } 19 | return 1; 20 | } 21 | 22 | duk_ret_t duv_version(duk_context *ctx) { 23 | duk_push_uint(ctx, uv_version()); 24 | return 1; 25 | } 26 | 27 | duk_ret_t duv_version_string(duk_context *ctx) { 28 | duk_push_string(ctx, uv_version_string()); 29 | return 1; 30 | } 31 | 32 | duk_ret_t duv_get_process_title(duk_context *ctx) { 33 | char title[MAX_TITLE_LENGTH]; 34 | duv_check(ctx, uv_get_process_title(title, MAX_TITLE_LENGTH)); 35 | duk_push_string(ctx, title); 36 | return 1; 37 | } 38 | 39 | duk_ret_t duv_set_process_title(duk_context *ctx) { 40 | const char* title; 41 | 42 | dschema_check(ctx, (const duv_schema_entry[]) { 43 | {"title", duk_is_string}, 44 | {0,0} 45 | }); 46 | 47 | title = duk_get_string(ctx, 0); 48 | duv_check(ctx, uv_set_process_title(title)); 49 | return 0; 50 | } 51 | 52 | duk_ret_t duv_resident_set_memory(duk_context *ctx) { 53 | size_t rss; 54 | duv_check(ctx, uv_resident_set_memory(&rss)); 55 | duk_push_number(ctx, rss); 56 | return 1; 57 | } 58 | 59 | duk_ret_t duv_uptime(duk_context *ctx) { 60 | double uptime; 61 | duv_check(ctx, uv_uptime(&uptime)); 62 | duk_push_number(ctx, uptime); 63 | return 1; 64 | } 65 | 66 | static void duv_push_timeval_table(duk_context *ctx, const uv_timeval_t* t) { 67 | duk_push_object(ctx); 68 | duk_push_uint(ctx, t->tv_sec); 69 | duk_put_prop_string(ctx, -2, "sec"); 70 | duk_push_uint(ctx, t->tv_usec); 71 | duk_put_prop_string(ctx, -2, "usec"); 72 | } 73 | 74 | duk_ret_t duv_getrusage(duk_context *ctx) { 75 | uv_rusage_t rusage; 76 | duv_check(ctx, uv_getrusage(&rusage)); 77 | duk_push_object(ctx); 78 | // user CPU time used 79 | duv_push_timeval_table(ctx, &rusage.ru_utime); 80 | duk_put_prop_string(ctx, -2, "utime"); 81 | // system CPU time used 82 | duv_push_timeval_table(ctx, &rusage.ru_stime); 83 | duk_put_prop_string(ctx, -2, "stime"); 84 | // maximum resident set size 85 | duk_push_uint(ctx, rusage.ru_maxrss); 86 | duk_put_prop_string(ctx, -2, "maxrss"); 87 | // integral shared memory size 88 | duk_push_uint(ctx, rusage.ru_ixrss); 89 | duk_put_prop_string(ctx, -2, "ixrss"); 90 | // integral unshared data size 91 | duk_push_uint(ctx, rusage.ru_idrss); 92 | duk_put_prop_string(ctx, -2, "idrss"); 93 | // integral unshared stack size 94 | duk_push_uint(ctx, rusage.ru_isrss); 95 | duk_put_prop_string(ctx, -2, "isrss"); 96 | // page reclaims (soft page faults) 97 | duk_push_uint(ctx, rusage.ru_minflt); 98 | duk_put_prop_string(ctx, -2, "minflt"); 99 | // page faults (hard page faults) 100 | duk_push_uint(ctx, rusage.ru_majflt); 101 | duk_put_prop_string(ctx, -2, "majflt"); 102 | // swaps 103 | duk_push_uint(ctx, rusage.ru_nswap); 104 | duk_put_prop_string(ctx, -2, "nswap"); 105 | // block input operations 106 | duk_push_uint(ctx, rusage.ru_inblock); 107 | duk_put_prop_string(ctx, -2, "inblock"); 108 | // block output operations 109 | duk_push_uint(ctx, rusage.ru_oublock); 110 | duk_put_prop_string(ctx, -2, "oublock"); 111 | // IPC messages sent 112 | duk_push_uint(ctx, rusage.ru_msgsnd); 113 | duk_put_prop_string(ctx, -2, "msgsnd"); 114 | // IPC messages received 115 | duk_push_uint(ctx, rusage.ru_msgrcv); 116 | duk_put_prop_string(ctx, -2, "msgrcv"); 117 | // signals received 118 | duk_push_uint(ctx, rusage.ru_nsignals); 119 | duk_put_prop_string(ctx, -2, "nsignals"); 120 | // voluntary context switches 121 | duk_push_uint(ctx, rusage.ru_nvcsw); 122 | duk_put_prop_string(ctx, -2, "nvcsw"); 123 | // involuntary context switches 124 | duk_push_uint(ctx, rusage.ru_nivcsw); 125 | duk_put_prop_string(ctx, -2, "nivcsw"); 126 | return 1; 127 | } 128 | 129 | duk_ret_t duv_cpu_info(duk_context *ctx) { 130 | uv_cpu_info_t* cpu_infos; 131 | int count, i; 132 | duv_check(ctx, uv_cpu_info(&cpu_infos, &count)); 133 | duk_push_array(ctx); 134 | 135 | for (i = 0; i < count; i++) { 136 | duk_push_object(ctx); 137 | duk_push_string(ctx, cpu_infos[i].model); 138 | duk_put_prop_string(ctx, -2, "model"); 139 | duk_push_uint(ctx, cpu_infos[i].speed); 140 | duk_put_prop_string(ctx, -2, "speed"); 141 | duk_push_object(ctx); 142 | duk_push_uint(ctx, cpu_infos[i].cpu_times.user); 143 | duk_put_prop_string(ctx, -2, "user"); 144 | duk_push_uint(ctx, cpu_infos[i].cpu_times.nice); 145 | duk_put_prop_string(ctx, -2, "nice"); 146 | duk_push_uint(ctx, cpu_infos[i].cpu_times.sys); 147 | duk_put_prop_string(ctx, -2, "sys"); 148 | duk_push_uint(ctx, cpu_infos[i].cpu_times.idle); 149 | duk_put_prop_string(ctx, -2, "idle"); 150 | duk_push_uint(ctx, cpu_infos[i].cpu_times.irq); 151 | duk_put_prop_string(ctx, -2, "irq"); 152 | duk_put_prop_string(ctx, -2, "times"); 153 | duk_put_prop_index(ctx, -2, i); 154 | } 155 | 156 | uv_free_cpu_info(cpu_infos, count); 157 | return 1; 158 | } 159 | 160 | duk_ret_t duv_interface_addresses(duk_context *ctx) { 161 | uv_interface_address_t* interfaces; 162 | int count, i; 163 | char ip[INET6_ADDRSTRLEN]; 164 | 165 | uv_interface_addresses(&interfaces, &count); 166 | 167 | duk_push_object(ctx); 168 | 169 | for (i = 0; i < count; i++) { 170 | 171 | duk_get_prop_string(ctx, -1, interfaces[i].name); 172 | if (!duk_is_object(ctx, -1)) { 173 | duk_pop(ctx); 174 | duk_push_array(ctx); 175 | duk_dup(ctx, -1); 176 | duk_put_prop_string(ctx, -3, interfaces[i].name); 177 | } 178 | 179 | duk_push_object(ctx); 180 | 181 | duk_push_boolean(ctx, interfaces[i].is_internal); 182 | duk_put_prop_string(ctx, -2, "internal"); 183 | 184 | if (interfaces[i].address.address4.sin_family == AF_INET) { 185 | uv_ip4_name(&interfaces[i].address.address4, ip, sizeof(ip)); 186 | } else if (interfaces[i].address.address4.sin_family == AF_INET6) { 187 | uv_ip6_name(&interfaces[i].address.address6, ip, sizeof(ip)); 188 | } else { 189 | strncpy(ip, "", INET6_ADDRSTRLEN); 190 | } 191 | duk_push_string(ctx, ip); 192 | duk_put_prop_string(ctx, -2, "ip"); 193 | 194 | duk_push_string(ctx, duv_protocol_to_string(interfaces[i].address.address4.sin_family)); 195 | duk_put_prop_string(ctx, -2, "family"); 196 | 197 | duk_put_prop_index(ctx, -2, duk_get_length(ctx, -2)); 198 | 199 | duk_pop(ctx); 200 | } 201 | 202 | uv_free_interface_addresses(interfaces, count); 203 | return 1; 204 | } 205 | 206 | duk_ret_t duv_loadavg(duk_context *ctx) { 207 | double avg[3]; 208 | int i; 209 | uv_loadavg(avg); 210 | duk_push_array(ctx); 211 | for (i = 0; i < 3; i++) { 212 | duk_push_number(ctx, avg[i]); 213 | duk_put_prop_index(ctx, -2, i); 214 | } 215 | return 1; 216 | } 217 | 218 | duk_ret_t duv_exepath(duk_context *ctx) { 219 | size_t size = 2*PATH_MAX; 220 | char exe_path[2*PATH_MAX]; 221 | duv_check(ctx, uv_exepath(exe_path, &size)); 222 | duk_push_lstring(ctx, exe_path, size); 223 | return 1; 224 | } 225 | 226 | duk_ret_t duv_cwd(duk_context *ctx) { 227 | size_t size = 2*PATH_MAX; 228 | char path[2*PATH_MAX]; 229 | duv_check(ctx, uv_cwd(path, &size)); 230 | duk_push_lstring(ctx, path, size); 231 | return 1; 232 | } 233 | 234 | duk_ret_t duv_os_homedir(duk_context *ctx) { 235 | size_t size = 2*PATH_MAX; 236 | char path[2*PATH_MAX]; 237 | duv_check(ctx, uv_os_homedir(path, &size)); 238 | duk_push_lstring(ctx, path, size); 239 | return 1; 240 | } 241 | 242 | duk_ret_t duv_chdir(duk_context *ctx) { 243 | const char* path; 244 | 245 | dschema_check(ctx, (const duv_schema_entry[]) { 246 | {"path", duk_is_string}, 247 | {0,0} 248 | }); 249 | 250 | path = duk_get_string(ctx, 0); 251 | duv_check(ctx, uv_chdir(path)); 252 | return 0; 253 | } 254 | 255 | duk_ret_t duv_get_total_memory(duk_context *ctx) { 256 | duk_push_number(ctx, uv_get_total_memory()); 257 | return 1; 258 | } 259 | 260 | duk_ret_t duv_hrtime(duk_context *ctx) { 261 | duk_push_number(ctx, uv_hrtime()); 262 | return 1; 263 | } 264 | 265 | duk_ret_t duv_update_time(duk_context *ctx) { 266 | uv_update_time(duv_loop(ctx)); 267 | return 0; 268 | } 269 | 270 | duk_ret_t duv_now(duk_context *ctx) { 271 | uint64_t now = uv_now(duv_loop(ctx)); 272 | duk_push_uint(ctx, now); 273 | return 1; 274 | } 275 | 276 | duk_ret_t duv_argv(duk_context *ctx) { 277 | duk_push_global_stash(ctx); 278 | duk_get_prop_string(ctx, -1, "argv"); 279 | return 1; 280 | } 281 | -------------------------------------------------------------------------------- /src/duv/misc.h: -------------------------------------------------------------------------------- 1 | #ifndef MISC_H 2 | #define MISC_H 3 | 4 | #include "duv.h" 5 | #include "misc.h" 6 | #include "dschema.h" 7 | 8 | #ifndef MAX_TITLE_LENGTH 9 | #define MAX_TITLE_LENGTH (8192) 10 | #endif 11 | 12 | duk_ret_t duv_guess_handle(duk_context *ctx); 13 | duk_ret_t duv_version(duk_context *ctx); 14 | duk_ret_t duv_version_string(duk_context *ctx); 15 | duk_ret_t duv_get_process_title(duk_context *ctx); 16 | duk_ret_t duv_set_process_title(duk_context *ctx); 17 | duk_ret_t duv_resident_set_memory(duk_context *ctx); 18 | duk_ret_t duv_uptime(duk_context *ctx); 19 | duk_ret_t duv_getrusage(duk_context *ctx); 20 | duk_ret_t duv_cpu_info(duk_context *ctx); 21 | duk_ret_t duv_interface_addresses(duk_context *ctx); 22 | duk_ret_t duv_loadavg(duk_context *ctx); 23 | duk_ret_t duv_exepath(duk_context *ctx); 24 | duk_ret_t duv_cwd(duk_context *ctx); 25 | duk_ret_t duv_os_homedir(duk_context *ctx); 26 | duk_ret_t duv_chdir(duk_context *ctx); 27 | duk_ret_t duv_get_total_memory(duk_context *ctx); 28 | duk_ret_t duv_hrtime(duk_context *ctx); 29 | duk_ret_t duv_update_time(duk_context *ctx); 30 | duk_ret_t duv_now(duk_context *ctx); 31 | duk_ret_t duv_argv(duk_context *ctx); 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /src/duv/pipe.c: -------------------------------------------------------------------------------- 1 | #include "pipe.h" 2 | 3 | duk_ret_t duv_new_pipe(duk_context *ctx) { 4 | dschema_check(ctx, (const duv_schema_entry[]) { 5 | {"ipc", duk_is_boolean}, 6 | {0,0} 7 | }); 8 | uv_pipe_t *pipe = duk_push_fixed_buffer(ctx, sizeof(uv_pipe_t)); 9 | duv_check(ctx, uv_pipe_init(duv_loop(ctx), pipe, duk_get_int(ctx, 0))); 10 | duv_setup_handle(ctx, (uv_handle_t*)pipe, DUV_PIPE); 11 | return 1; 12 | } 13 | 14 | duk_ret_t duv_pipe_open(duk_context *ctx) { 15 | dschema_check(ctx, (const duv_schema_entry[]) { 16 | {"fd", duk_is_number}, 17 | {0,0} 18 | }); 19 | uv_pipe_t *pipe = duv_require_this_handle(ctx, DUV_PIPE_MASK); 20 | duv_check(ctx, uv_pipe_open(pipe, 21 | duk_get_int(ctx, 1) 22 | )); 23 | return 0; 24 | } 25 | 26 | duk_ret_t duv_pipe_bind(duk_context *ctx) { 27 | dschema_check(ctx, (const duv_schema_entry[]) { 28 | {"name", duk_is_string}, 29 | {0,0} 30 | }); 31 | uv_pipe_t *pipe = duv_require_this_handle(ctx, DUV_PIPE_MASK); 32 | duv_check(ctx, uv_pipe_bind(pipe, 33 | duk_get_string(ctx, 1) 34 | )); 35 | return 0; 36 | } 37 | 38 | duk_ret_t duv_pipe_connect(duk_context *ctx) { 39 | dschema_check(ctx, (const duv_schema_entry[]) { 40 | {"name", duk_is_string}, 41 | {"callback", dschema_is_continuation}, 42 | {0,0} 43 | }); 44 | uv_pipe_t *pipe = duv_require_this_handle(ctx, DUV_TCP_MASK); 45 | uv_connect_t *req = duk_push_fixed_buffer(ctx, sizeof(*req)); 46 | uv_pipe_connect(req, pipe, duk_get_string(ctx, 1), duv_on_connect); 47 | return duv_setup_request(ctx, (uv_req_t*)req, 2); 48 | } 49 | 50 | duk_ret_t duv_pipe_getsockname(duk_context *ctx) { 51 | uv_pipe_t *pipe = duv_require_this_handle(ctx, DUV_TCP_MASK); 52 | size_t len = 2*PATH_MAX; 53 | char buf[2*PATH_MAX]; 54 | duv_check(ctx, uv_pipe_getsockname(pipe, buf, &len)); 55 | duk_push_lstring(ctx, buf, len); 56 | return 1; 57 | } 58 | 59 | duk_ret_t duv_pipe_getpeername(duk_context *ctx) { 60 | uv_pipe_t *pipe = duv_require_this_handle(ctx, DUV_TCP_MASK); 61 | size_t len = 2*PATH_MAX; 62 | char buf[2*PATH_MAX]; 63 | duv_check(ctx, uv_pipe_getpeername(pipe, buf, &len)); 64 | duk_push_lstring(ctx, buf, len); 65 | return 1; 66 | } 67 | 68 | duk_ret_t duv_pipe_pending_instances(duk_context *ctx) { 69 | dschema_check(ctx, (const duv_schema_entry[]) { 70 | {"count", duk_is_number}, 71 | {0,0} 72 | }); 73 | uv_pipe_t *pipe = duv_require_this_handle(ctx, DUV_TCP_MASK); 74 | uv_pipe_pending_instances(pipe, 75 | duk_get_int(ctx, 1) 76 | ); 77 | return 0; 78 | } 79 | 80 | duk_ret_t duv_pipe_pending_count(duk_context *ctx) { 81 | uv_pipe_t *pipe = duv_require_this_handle(ctx, DUV_TCP_MASK); 82 | duk_push_int(ctx, uv_pipe_pending_count(pipe)); 83 | return 1; 84 | } 85 | 86 | duk_ret_t duv_pipe_pending_type(duk_context *ctx) { 87 | uv_pipe_t *pipe = duv_require_this_handle(ctx, DUV_TCP_MASK); 88 | uv_handle_type type = uv_pipe_pending_type(pipe); 89 | const char* type_name; 90 | switch (type) { 91 | #define XX(uc, lc) \ 92 | case UV_##uc: type_name = #lc; break; 93 | UV_HANDLE_TYPE_MAP(XX) 94 | #undef XX 95 | default: return 0; 96 | } 97 | duk_push_string(ctx, type_name); 98 | return 1; 99 | } 100 | -------------------------------------------------------------------------------- /src/duv/pipe.h: -------------------------------------------------------------------------------- 1 | #ifndef PIPE_H 2 | #define PIPE_H 3 | 4 | #include "duv.h" 5 | 6 | duk_ret_t duv_new_pipe(duk_context *ctx); 7 | duk_ret_t duv_pipe_open(duk_context *ctx); 8 | duk_ret_t duv_pipe_bind(duk_context *ctx); 9 | duk_ret_t duv_pipe_connect(duk_context *ctx); 10 | duk_ret_t duv_pipe_getsockname(duk_context *ctx); 11 | duk_ret_t duv_pipe_getpeername(duk_context *ctx); 12 | duk_ret_t duv_pipe_pending_instances(duk_context *ctx); 13 | duk_ret_t duv_pipe_pending_count(duk_context *ctx); 14 | duk_ret_t duv_pipe_pending_type(duk_context *ctx); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /src/duv/poll.c: -------------------------------------------------------------------------------- 1 | #include "poll.h" 2 | -------------------------------------------------------------------------------- /src/duv/poll.h: -------------------------------------------------------------------------------- 1 | #ifndef POLL_H 2 | #define POLL_H 3 | 4 | #include "duv.h" 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /src/duv/prepare.c: -------------------------------------------------------------------------------- 1 | #include "prepare.h" 2 | 3 | duk_ret_t duv_new_prepare(duk_context *ctx) { 4 | uv_prepare_t *prepare = duk_push_fixed_buffer(ctx, sizeof(uv_prepare_t)); 5 | duv_check(ctx, uv_prepare_init(duv_loop(ctx), prepare)); 6 | duv_setup_handle(ctx, (uv_handle_t*)prepare, DUV_PREPARE); 7 | return 1; 8 | } 9 | 10 | duk_ret_t duv_prepare_start(duk_context *ctx) { 11 | dschema_check(ctx, (const duv_schema_entry[]) { 12 | {"callback", duk_is_function}, 13 | {0,0} 14 | }); 15 | uv_prepare_t *prepare = duv_require_this_handle(ctx, DUV_PREPARE_MASK); 16 | duk_put_prop_string(ctx, 0, "\xffon-prepare"); 17 | duv_check(ctx, uv_prepare_start(prepare, duv_on_prepare)); 18 | return 0; 19 | } 20 | 21 | duk_ret_t duv_prepare_stop(duk_context *ctx) { 22 | uv_prepare_t *prepare = duv_require_this_handle(ctx, DUV_PREPARE_MASK); 23 | duk_del_prop_string(ctx, -1, "\xffon-prepare"); 24 | duv_check(ctx, uv_prepare_stop(prepare)); 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /src/duv/prepare.h: -------------------------------------------------------------------------------- 1 | #ifndef PREPARE_H 2 | #define PREPARE_H 3 | 4 | #include "duv.h" 5 | 6 | duk_ret_t duv_new_prepare(duk_context *ctx); 7 | duk_ret_t duv_prepare_start(duk_context *ctx); 8 | duk_ret_t duv_prepare_stop(duk_context *ctx); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /src/duv/process.c: -------------------------------------------------------------------------------- 1 | #include "process.h" 2 | -------------------------------------------------------------------------------- /src/duv/process.h: -------------------------------------------------------------------------------- 1 | #ifndef PROCESS_H 2 | #define PROCESS_H 3 | 4 | #include "duv.h" 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /src/duv/req.c: -------------------------------------------------------------------------------- 1 | #include "req.h" 2 | 3 | duk_ret_t duv_req_tostring(duk_context *ctx) { 4 | duk_push_this(ctx); 5 | duk_get_prop_string(ctx, -1, "\xff""req-type"); 6 | if (duk_is_undefined(ctx, -1)) return 0; 7 | const char* type = duv_req_type_to_string(duk_get_int(ctx, -1)); 8 | duk_get_prop_string(ctx, -2, "\xffuv-data"); 9 | void* data = duk_get_buffer(ctx, -1, 0); 10 | duk_pop_3(ctx); 11 | duk_push_sprintf(ctx, "[%s %p]", type, data); 12 | return 1; 13 | } 14 | 15 | duk_ret_t duv_cancel(duk_context *ctx) { 16 | duk_push_this(ctx); 17 | duk_get_prop_string(ctx, -1, "\xff""req-type"); 18 | if (duk_is_undefined(ctx, -1)) return 0; 19 | duk_get_prop_string(ctx, -2, "\xffuv-data"); 20 | uv_req_t* req = duk_get_buffer(ctx, -1, 0); 21 | duv_check(ctx, uv_cancel(req)); 22 | duk_pop_2(ctx); 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /src/duv/req.h: -------------------------------------------------------------------------------- 1 | #ifndef REQ_H 2 | #define REQ_H 3 | 4 | #include "duv.h" 5 | 6 | duk_ret_t duv_req_tostring(duk_context *ctx); 7 | duk_ret_t duv_cancel(duk_context *ctx); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /src/duv/signal.c: -------------------------------------------------------------------------------- 1 | #include "signal.h" 2 | -------------------------------------------------------------------------------- /src/duv/signal.h: -------------------------------------------------------------------------------- 1 | #ifndef SIGNAL_H 2 | #define SIGNAL_H 3 | 4 | #include "duv.h" 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /src/duv/stream.c: -------------------------------------------------------------------------------- 1 | #include "stream.h" 2 | 3 | static duk_bool_t duv_is_stream(duk_context *ctx, int index) { 4 | return duv_is_handle_of(ctx, index, DUV_STREAM_MASK); 5 | } 6 | 7 | duk_ret_t duv_shutdown(duk_context *ctx) { 8 | dschema_check(ctx, (const duv_schema_entry[]) { 9 | {"callback", dschema_is_continuation}, 10 | {0,0} 11 | }); 12 | uv_stream_t *stream = duv_require_this_handle(ctx, DUV_STREAM_MASK); 13 | uv_shutdown_t *req = duk_push_fixed_buffer(ctx, sizeof(*req)); 14 | duv_check(ctx, uv_shutdown(req, stream, duv_on_shutdown)); 15 | return duv_setup_request(ctx, (uv_req_t*)req, 1); 16 | } 17 | 18 | duk_ret_t duv_listen(duk_context *ctx) { 19 | dschema_check(ctx, (const duv_schema_entry[]) { 20 | {"backlog", duk_is_number}, 21 | {"callback", dschema_is_continuation}, 22 | {0,0} 23 | }); 24 | uv_stream_t *stream = duv_require_this_handle(ctx, DUV_STREAM_MASK); 25 | duk_put_prop_string(ctx, 0, "\xffon-connection"); 26 | duv_check(ctx, uv_listen(stream, 27 | duk_get_int(ctx, 1), 28 | duv_on_connection 29 | )); 30 | return 0; 31 | } 32 | 33 | duk_ret_t duv_accept(duk_context *ctx) { 34 | dschema_check(ctx, (const duv_schema_entry[]) { 35 | {"client", duv_is_stream}, 36 | {0,0} 37 | }); 38 | uv_stream_t *server = duv_require_this_handle(ctx, DUV_STREAM_MASK); 39 | uv_stream_t *client = duv_get_handle(ctx, 1); 40 | duv_check(ctx, uv_accept(server, client)); 41 | return 0; 42 | } 43 | 44 | duk_ret_t duv_read_start(duk_context *ctx) { 45 | dschema_check(ctx, (const duv_schema_entry[]) { 46 | {"callback", dschema_is_continuation}, 47 | {0,0} 48 | }); 49 | uv_stream_t *stream = duv_require_this_handle(ctx, DUV_STREAM_MASK); 50 | duk_put_prop_string(ctx, 0, "\xffon-read"); 51 | duv_check(ctx, uv_read_start(stream, duv_on_alloc, duv_on_read)); 52 | return 0; 53 | } 54 | 55 | duk_ret_t duv_read_stop(duk_context *ctx) { 56 | uv_stream_t *stream = duv_require_this_handle(ctx, DUV_STREAM_MASK); 57 | duk_del_prop_string(ctx, 0, "\xffon-read"); 58 | duv_check(ctx, uv_read_stop(stream)); 59 | return 0; 60 | } 61 | 62 | duk_ret_t duv_write(duk_context *ctx) { 63 | dschema_check(ctx, (const duv_schema_entry[]) { 64 | {"data", dschema_is_data}, 65 | {"callback", dschema_is_continuation}, 66 | {0,0} 67 | }); 68 | uv_stream_t *stream = duv_require_this_handle(ctx, DUV_STREAM_MASK); 69 | uv_write_t *req = duk_push_fixed_buffer(ctx, sizeof(*req)); 70 | uv_buf_t buf; 71 | duv_get_data(ctx, 1, &buf); 72 | duv_check(ctx, uv_write(req, stream, &buf, 1, duv_on_write)); 73 | return duv_setup_request(ctx, (uv_req_t*)req, 2); 74 | // TODO: handle case where multiple concurrent writes are in flight for a 75 | // single stream. 76 | } 77 | 78 | duk_ret_t duv_is_readable(duk_context *ctx) { 79 | uv_stream_t *stream = duv_require_this_handle(ctx, DUV_STREAM_MASK); 80 | duk_push_boolean(ctx, uv_is_readable(stream)); 81 | return 1; 82 | } 83 | 84 | duk_ret_t duv_is_writable(duk_context *ctx) { 85 | uv_stream_t *stream = duv_require_this_handle(ctx, DUV_STREAM_MASK); 86 | duk_push_boolean(ctx, uv_is_writable(stream)); 87 | return 1; 88 | } 89 | 90 | duk_ret_t duv_stream_set_blocking(duk_context *ctx) { 91 | dschema_check(ctx, (const duv_schema_entry[]) { 92 | {"blocking", duk_is_boolean}, 93 | {0,0} 94 | }); 95 | uv_stream_t *stream = duv_require_this_handle(ctx, DUV_STREAM_MASK); 96 | duv_check(ctx, uv_stream_set_blocking(stream, 97 | duk_get_int(ctx, 1) 98 | )); 99 | return 0; 100 | } 101 | -------------------------------------------------------------------------------- /src/duv/stream.h: -------------------------------------------------------------------------------- 1 | #ifndef STREAM_H 2 | #define STREAM_H 3 | 4 | #include "duv.h" 5 | 6 | duk_ret_t duv_shutdown(duk_context *ctx); 7 | duk_ret_t duv_listen(duk_context *ctx); 8 | duk_ret_t duv_accept(duk_context *ctx); 9 | duk_ret_t duv_read_start(duk_context *ctx); 10 | duk_ret_t duv_read_stop(duk_context *ctx); 11 | duk_ret_t duv_write(duk_context *ctx); 12 | duk_ret_t duv_is_readable(duk_context *ctx); 13 | duk_ret_t duv_is_writable(duk_context *ctx); 14 | duk_ret_t duv_stream_set_blocking(duk_context *ctx); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /src/duv/tcp.c: -------------------------------------------------------------------------------- 1 | #include "tcp.h" 2 | 3 | duk_ret_t duv_new_tcp(duk_context *ctx) { 4 | uv_tcp_t *tcp = duk_push_fixed_buffer(ctx, sizeof(uv_tcp_t)); 5 | duv_check(ctx, uv_tcp_init(duv_loop(ctx), tcp)); 6 | duv_setup_handle(ctx, (uv_handle_t*)tcp, DUV_TCP); 7 | return 1; 8 | } 9 | 10 | duk_ret_t duv_tcp_open(duk_context *ctx) { 11 | dschema_check(ctx, (const duv_schema_entry[]) { 12 | {"fd", duk_is_number}, 13 | {0,0} 14 | }); 15 | uv_tcp_t *tcp = duv_require_this_handle(ctx, DUV_TCP_MASK); 16 | duv_check(ctx, uv_tcp_open(tcp, 17 | duk_get_int(ctx, 1) 18 | )); 19 | return 0; 20 | } 21 | 22 | duk_ret_t duv_tcp_nodelay(duk_context *ctx) { 23 | dschema_check(ctx, (const duv_schema_entry[]) { 24 | {"enable", duk_is_boolean}, 25 | {0,0} 26 | }); 27 | uv_tcp_t *tcp = duv_require_this_handle(ctx, DUV_TCP_MASK); 28 | duv_check(ctx, uv_tcp_nodelay(tcp, 29 | duk_get_int(ctx, 1) 30 | )); 31 | return 0; 32 | } 33 | 34 | duk_ret_t duv_tcp_keepalive(duk_context *ctx) { 35 | dschema_check(ctx, (const duv_schema_entry[]) { 36 | {"enable", duk_is_boolean}, 37 | {"delay", duk_is_number}, 38 | {0,0} 39 | }); 40 | uv_tcp_t *tcp = duv_require_this_handle(ctx, DUV_TCP_MASK); 41 | duv_check(ctx, uv_tcp_keepalive(tcp, 42 | duk_get_int(ctx, 1), 43 | duk_get_int(ctx, 2) 44 | )); 45 | return 0; 46 | } 47 | 48 | duk_ret_t duv_tcp_simultaneous_accepts(duk_context *ctx) { 49 | dschema_check(ctx, (const duv_schema_entry[]) { 50 | {"enable", duk_is_boolean}, 51 | {0,0} 52 | }); 53 | uv_tcp_t *tcp = duv_require_this_handle(ctx, DUV_TCP_MASK); 54 | duv_check(ctx, uv_tcp_simultaneous_accepts(tcp, 55 | duk_get_int(ctx, 1) 56 | )); 57 | return 0; 58 | } 59 | 60 | duk_ret_t duv_tcp_bind(duk_context *ctx) { 61 | dschema_check(ctx, (const duv_schema_entry[]) { 62 | {"host", duk_is_string}, 63 | {"port", duk_is_number}, 64 | {0,0} 65 | }); 66 | uv_tcp_t *tcp = duv_require_this_handle(ctx, DUV_TCP_MASK); 67 | const char *host = duk_get_string(ctx, 1); 68 | int port = duk_get_number(ctx, 2), 69 | flags = 0; 70 | struct sockaddr_storage addr; 71 | if (uv_ip4_addr(host, port, (struct sockaddr_in*)&addr) && 72 | uv_ip6_addr(host, port, (struct sockaddr_in6*)&addr)) { 73 | duk_error(ctx, DUK_ERR_TYPE_ERROR, "Invalid IP address or port"); 74 | } 75 | duv_check(ctx, uv_tcp_bind(tcp, 76 | (struct sockaddr*)&addr, 77 | flags 78 | )); 79 | return 0; 80 | } 81 | 82 | static void duv_push_sockaddr(duk_context *ctx, struct sockaddr_storage* address, int addrlen) { 83 | char ip[INET6_ADDRSTRLEN]; 84 | int port = 0; 85 | if (address->ss_family == AF_INET) { 86 | struct sockaddr_in* addrin = (struct sockaddr_in*)address; 87 | uv_inet_ntop(AF_INET, &(addrin->sin_addr), ip, addrlen); 88 | port = ntohs(addrin->sin_port); 89 | } else if (address->ss_family == AF_INET6) { 90 | struct sockaddr_in6* addrin6 = (struct sockaddr_in6*)address; 91 | uv_inet_ntop(AF_INET6, &(addrin6->sin6_addr), ip, addrlen); 92 | port = ntohs(addrin6->sin6_port); 93 | } 94 | 95 | duk_push_object(ctx); 96 | duk_push_string(ctx, duv_protocol_to_string(address->ss_family)); 97 | duk_put_prop_string(ctx, -2, "family"); 98 | duk_push_number(ctx, port); 99 | duk_put_prop_string(ctx, -2, "port"); 100 | duk_push_string(ctx, ip); 101 | duk_put_prop_string(ctx, -2, "ip"); 102 | } 103 | 104 | duk_ret_t duv_tcp_getpeername(duk_context *ctx) { 105 | uv_tcp_t *tcp = duv_require_this_handle(ctx, DUV_TCP_MASK); 106 | struct sockaddr_storage address; 107 | int addrlen = sizeof(address); 108 | duv_check(ctx, uv_tcp_getpeername(tcp, (struct sockaddr*)&address, &addrlen)); 109 | duv_push_sockaddr(ctx, &address, addrlen); 110 | return 1; 111 | } 112 | 113 | duk_ret_t duv_tcp_getsockname(duk_context *ctx) { 114 | uv_tcp_t *tcp = duv_require_this_handle(ctx, DUV_TCP_MASK); 115 | struct sockaddr_storage address; 116 | int addrlen = sizeof(address); 117 | duv_check(ctx, uv_tcp_getsockname(tcp, (struct sockaddr*)&address, &addrlen)); 118 | duv_push_sockaddr(ctx, &address, addrlen); 119 | return 1; 120 | } 121 | 122 | duk_ret_t duv_tcp_connect(duk_context *ctx) { 123 | dschema_check(ctx, (const duv_schema_entry[]) { 124 | {"host", duk_is_string}, 125 | {"port", duk_is_number}, 126 | {"callback", dschema_is_continuation}, 127 | {0,0} 128 | }); 129 | uv_tcp_t *tcp = duv_require_this_handle(ctx, DUV_TCP_MASK); 130 | uv_connect_t *req = duk_push_fixed_buffer(ctx, sizeof(*req)); 131 | const char *host = duk_get_string(ctx, 1); 132 | int port = duk_get_number(ctx, 2); 133 | struct sockaddr_storage addr; 134 | if (uv_ip4_addr(host, port, (struct sockaddr_in*)&addr) && 135 | uv_ip6_addr(host, port, (struct sockaddr_in6*)&addr)) { 136 | duk_error(ctx, DUK_ERR_TYPE_ERROR, "Invalid IP address or port"); 137 | } 138 | duv_check(ctx, uv_tcp_connect(req, tcp, (struct sockaddr*)&addr, duv_on_connect)); 139 | return duv_setup_request(ctx, (uv_req_t*)req, 3); 140 | } 141 | -------------------------------------------------------------------------------- /src/duv/tcp.h: -------------------------------------------------------------------------------- 1 | #ifndef TCP_H 2 | #define TCP_H 3 | 4 | #include "duv.h" 5 | #include "utils.h" 6 | 7 | duk_ret_t duv_new_tcp(duk_context *ctx); 8 | duk_ret_t duv_tcp_open(duk_context *ctx); 9 | duk_ret_t duv_tcp_nodelay(duk_context *ctx); 10 | duk_ret_t duv_tcp_keepalive(duk_context *ctx); 11 | duk_ret_t duv_tcp_simultaneous_accepts(duk_context *ctx); 12 | duk_ret_t duv_tcp_bind(duk_context *ctx); 13 | duk_ret_t duv_tcp_getpeername(duk_context *ctx); 14 | duk_ret_t duv_tcp_getsockname(duk_context *ctx); 15 | duk_ret_t duv_tcp_connect(duk_context *ctx); 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /src/duv/timer.c: -------------------------------------------------------------------------------- 1 | #include "timer.h" 2 | 3 | duk_ret_t duv_new_timer(duk_context *ctx) { 4 | uv_timer_t *timer = duk_push_fixed_buffer(ctx, sizeof(uv_timer_t)); 5 | duv_check(ctx, uv_timer_init(duv_loop(ctx), timer)); 6 | duv_setup_handle(ctx, (uv_handle_t*)timer, DUV_TIMER); 7 | return 1; 8 | } 9 | 10 | duk_ret_t duv_timer_start(duk_context *ctx) { 11 | dschema_check(ctx, (const duv_schema_entry[]) { 12 | {"timeout", duk_is_number}, 13 | {"repeat", duk_is_number}, 14 | {"callback", duk_is_function}, 15 | {0,0} 16 | }); 17 | uv_timer_t *timer = duv_require_this_handle(ctx, DUV_TIMER_MASK); 18 | int timeout = duk_get_int(ctx, 1); 19 | int repeat = duk_get_int(ctx, 2); 20 | duk_put_prop_string(ctx, 0, "\xffon-timeout"); 21 | duv_check(ctx, uv_timer_start(timer, duv_on_timeout, timeout, repeat)); 22 | return 0; 23 | } 24 | 25 | duk_ret_t duv_timer_stop(duk_context *ctx) { 26 | uv_timer_t *timer = duv_require_this_handle(ctx, DUV_TIMER_MASK); 27 | duk_del_prop_string(ctx, -1, "\xffon-timeout"); 28 | duv_check(ctx, uv_timer_stop(timer)); 29 | return 0; 30 | } 31 | 32 | duk_ret_t duv_timer_again(duk_context *ctx) { 33 | uv_timer_t *timer = duv_require_this_handle(ctx, DUV_TIMER_MASK); 34 | duv_check(ctx, uv_timer_again(timer)); 35 | return 0; 36 | } 37 | 38 | duk_ret_t duv_timer_set_repeat(duk_context *ctx) { 39 | dschema_check(ctx, (const duv_schema_entry[]) { 40 | {"repeat", duk_is_number}, 41 | {0,0} 42 | }); 43 | uv_timer_t *timer = duv_require_this_handle(ctx, DUV_TIMER_MASK); 44 | uint64_t repeat = duk_get_int(ctx, 1); 45 | uv_timer_set_repeat(timer, repeat); 46 | return 0; 47 | } 48 | 49 | duk_ret_t duv_timer_get_repeat(duk_context *ctx) { 50 | uv_timer_t *timer = duv_require_this_handle(ctx, DUV_TIMER_MASK); 51 | duk_push_int(ctx, uv_timer_get_repeat(timer)); 52 | return 1; 53 | } 54 | -------------------------------------------------------------------------------- /src/duv/timer.h: -------------------------------------------------------------------------------- 1 | #ifndef TIMER_H 2 | #define TIMER_H 3 | 4 | #include "duv.h" 5 | 6 | duk_ret_t duv_new_timer(duk_context *ctx); 7 | duk_ret_t duv_timer_start(duk_context *ctx); 8 | duk_ret_t duv_timer_stop(duk_context *ctx); 9 | duk_ret_t duv_timer_again(duk_context *ctx); 10 | duk_ret_t duv_timer_set_repeat(duk_context *ctx); 11 | duk_ret_t duv_timer_get_repeat(duk_context *ctx); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /src/duv/tty.c: -------------------------------------------------------------------------------- 1 | #include "tty.h" 2 | 3 | duk_ret_t duv_new_tty(duk_context *ctx) { 4 | dschema_check(ctx, (const duv_schema_entry[]) { 5 | {"fd", duk_is_number}, 6 | {"readable", duk_is_boolean}, 7 | {0,0} 8 | }); 9 | uv_tty_t *tty = duk_push_fixed_buffer(ctx, sizeof(uv_tty_t)); 10 | duv_check(ctx, uv_tty_init(duv_loop(ctx), tty, 11 | duk_get_int(ctx, 0), 12 | duk_get_int(ctx, 1) 13 | )); 14 | duv_setup_handle(ctx, (uv_handle_t*)tty, DUV_TTY); 15 | return 1; 16 | } 17 | 18 | duk_ret_t duv_tty_set_mode(duk_context *ctx) { 19 | dschema_check(ctx, (const duv_schema_entry[]) { 20 | {"mode", duk_is_number}, 21 | {0,0} 22 | }); 23 | uv_tty_t *tty = duv_require_this_handle(ctx, DUV_TTY_MASK); 24 | duv_check(ctx, uv_tty_set_mode(tty, 25 | duk_get_int(ctx, 1) 26 | )); 27 | return 0; 28 | } 29 | 30 | duk_ret_t duv_tty_get_winsize(duk_context *ctx) { 31 | uv_tty_t *tty = duv_require_this_handle(ctx, DUV_TTY_MASK); 32 | int width, height; 33 | duv_check(ctx, uv_tty_get_winsize(tty, &width, &height)); 34 | duk_push_array(ctx); 35 | duk_push_int(ctx, width); 36 | duk_put_prop_index(ctx, -2, 0); 37 | duk_push_int(ctx, height); 38 | duk_put_prop_index(ctx, -2, 1); 39 | return 1; 40 | } 41 | 42 | duk_ret_t duv_tty_reset_mode(duk_context *ctx) { 43 | duv_check(ctx, uv_tty_reset_mode()); 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /src/duv/tty.h: -------------------------------------------------------------------------------- 1 | #ifndef TTY_H 2 | #define TTY_H 3 | 4 | #include "duv.h" 5 | 6 | duk_ret_t duv_new_tty(duk_context *ctx); 7 | duk_ret_t duv_tty_set_mode(duk_context *ctx); 8 | duk_ret_t duv_tty_get_winsize(duk_context *ctx); 9 | duk_ret_t duv_tty_reset_mode(duk_context *ctx); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /src/duv/udp.c: -------------------------------------------------------------------------------- 1 | #include "udp.h" 2 | -------------------------------------------------------------------------------- /src/duv/udp.h: -------------------------------------------------------------------------------- 1 | #ifndef UDP_H 2 | #define UDP_H 3 | 4 | #include "duv.h" 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /src/duv/utils.c: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | #include 3 | 4 | const char* duv_protocol_to_string(int family) { 5 | #ifdef AF_UNIX 6 | if (family == AF_UNIX) return "UNIX"; 7 | #endif 8 | #ifdef AF_INET 9 | if (family == AF_INET) return "INET"; 10 | #endif 11 | #ifdef AF_INET6 12 | if (family == AF_INET6) return "INET6"; 13 | #endif 14 | #ifdef AF_IPX 15 | if (family == AF_IPX) return "IPX"; 16 | #endif 17 | #ifdef AF_NETLINK 18 | if (family == AF_NETLINK) return "NETLINK"; 19 | #endif 20 | #ifdef AF_X25 21 | if (family == AF_X25) return "X25"; 22 | #endif 23 | #ifdef AF_AX25 24 | if (family == AF_AX25) return "AX25"; 25 | #endif 26 | #ifdef AF_ATMPVC 27 | if (family == AF_ATMPVC) return "ATMPVC"; 28 | #endif 29 | #ifdef AF_APPLETALK 30 | if (family == AF_APPLETALK) return "APPLETALK"; 31 | #endif 32 | #ifdef AF_PACKET 33 | if (family == AF_PACKET) return "PACKET"; 34 | #endif 35 | return NULL; 36 | } 37 | 38 | const char* duv_req_type_to_string(uv_req_type type) { 39 | switch (type) { 40 | case UV_CONNECT: return "uv_connect_t"; 41 | case UV_WRITE: return "uv_write_t"; 42 | case UV_SHUTDOWN: return "uv_shutdown_t"; 43 | case UV_UDP_SEND: return "uv_udp_send_t"; 44 | case UV_FS: return "uv_fs_t"; 45 | case UV_WORK: return "uv_work_t"; 46 | case UV_GETADDRINFO: return "uv_getaddrinfo_t"; 47 | case UV_GETNAMEINFO: return "uv_getnameinfo_t"; 48 | default: return "uv_req_t"; 49 | } 50 | } 51 | 52 | const char* duv_type_to_string(duv_type_t type) { 53 | switch (type) { 54 | case DUV_TIMER: return "uv_timer_t"; 55 | case DUV_PREPARE: return "uv_prepare_t"; 56 | case DUV_CHECK: return "uv_check_t"; 57 | case DUV_IDLE: return "uv_idle_t"; 58 | case DUV_ASYNC: return "uv_async_t"; 59 | case DUV_POLL: return "uv_poll_t"; 60 | case DUV_SIGNAL: return "uv_signal_t"; 61 | case DUV_PROCESS: return "uv_process_t"; 62 | case DUV_TCP: return "uv_tcp_t"; 63 | case DUV_PIPE: return "uv_pipe_t"; 64 | case DUV_TTY: return "uv_tty_t"; 65 | case DUV_UDP: return "uv_udp_t"; 66 | case DUV_FS_EVENT: return "uv_fs_event_t"; 67 | case DUV_FS_POLL: return "uv_fs_poll_t"; 68 | } 69 | return "uv_handle_t"; 70 | } 71 | const char* duv_mask_to_string(duv_type_mask_t mask) { 72 | switch (mask) { 73 | case DUV_HANDLE_MASK: return "uv_handle_t"; 74 | case DUV_TIMER_MASK: return "uv_timer_t"; 75 | case DUV_PREPARE_MASK: return "uv_prepare_t"; 76 | case DUV_CHECK_MASK: return "uv_check_t"; 77 | case DUV_IDLE_MASK: return "uv_idle_t"; 78 | case DUV_ASYNC_MASK: return "uv_async_t"; 79 | case DUV_POLL_MASK: return "uv_poll_t"; 80 | case DUV_SIGNAL_MASK: return "uv_signal_t"; 81 | case DUV_PROCESS_MASK: return "uv_process_t"; 82 | case DUV_STREAM_MASK: return "uv_stream_t"; 83 | case DUV_TCP_MASK: return "uv_tcp_t"; 84 | case DUV_PIPE_MASK: return "uv_pipe_t"; 85 | case DUV_TTY_MASK: return "uv_tty_t"; 86 | case DUV_UDP_MASK: return "uv_udp_t"; 87 | case DUV_FS_EVENT_MASK: return "uv_fs_event_t"; 88 | case DUV_FS_POLL_MASK: return "uv_fs_poll_t"; 89 | } 90 | return "unknown"; 91 | } 92 | 93 | uv_loop_t* duv_loop(duk_context *ctx) { 94 | duk_memory_functions funcs; 95 | duk_get_memory_functions(ctx, &funcs); 96 | return funcs.udata; 97 | } 98 | 99 | void duv_push_status(duk_context *ctx, int status) { 100 | if (status < 0) { 101 | duk_push_error_object(ctx, DUK_ERR_ERROR, "%s: %s", uv_err_name(status), uv_strerror(status)); 102 | } 103 | else { 104 | duk_push_null(ctx); 105 | } 106 | } 107 | 108 | void duv_error(duk_context *ctx, int status) { 109 | duk_error(ctx, DUK_ERR_ERROR, "%s: %s", uv_err_name(status), uv_strerror(status)); 110 | } 111 | 112 | void duv_check(duk_context *ctx, int status) { 113 | if (status < 0) duv_error(ctx, status); 114 | } 115 | 116 | #define KEYLEN sizeof(void*)*2+1 117 | static char key[KEYLEN]; 118 | 119 | // Assumes buffer is at top of stack. 120 | // replaces with this on top of stack. 121 | void duv_setup_handle(duk_context *ctx, uv_handle_t *handle, duv_type_t type) { 122 | // Insert this before buffer 123 | duk_push_this(ctx); 124 | 125 | // Set buffer as uv-data internal property. 126 | duk_insert(ctx, -2); 127 | duk_put_prop_string(ctx, -2, "\xff""uv-data"); 128 | 129 | // Set uv-type from provided parameter. 130 | duk_push_int(ctx, type); 131 | duk_put_prop_string(ctx, -2, "\xff""uv-type"); 132 | 133 | // Store this object in the heap stack keyed by the handle's pointer address. 134 | // This will prevent it from being garbage collected and allow us to find 135 | // it with nothing more than the handle's address. 136 | duv_store_handle(ctx, handle); 137 | 138 | // Store the context in the handle so it can use duktape APIs. 139 | handle->data = ctx; 140 | } 141 | 142 | duk_ret_t duv_setup_request(duk_context *ctx, uv_req_t* req, int callback) { 143 | // Create a new container object for the request with request methods 144 | duk_push_object(ctx); 145 | duk_push_heap_stash(ctx); 146 | duk_get_prop_string(ctx, -1, "req-prototype"); 147 | duk_remove(ctx, -2); 148 | duk_set_prototype(ctx, -2); 149 | 150 | // Set buffer as uv-data internal property. 151 | duk_insert(ctx, -2); 152 | duk_put_prop_string(ctx, -2, "\xff""uv-data"); 153 | 154 | // Store the request type. 155 | duk_push_int(ctx, req->type); 156 | duk_put_prop_string(ctx, -2, "\xff""req-type"); 157 | 158 | // Store a reference to the lua callback 159 | duk_dup(ctx, callback); 160 | duk_put_prop_string(ctx, -2, "\xff""uv-callback"); 161 | 162 | // Store this object in the heap stack keyed by the request's pointer address. 163 | // This will prevent it from being garbage collected and allow us to find 164 | // it with nothing more than the request's address. 165 | duv_store_handle(ctx, req); 166 | 167 | // Store the context in the handle so it can use duktape APIs. 168 | req->data = ctx; 169 | 170 | // TODO: is this still on the stack? 171 | return 1; 172 | } 173 | 174 | void duv_store_handle(duk_context *ctx, void *handle) { 175 | duk_push_heap_stash(ctx); 176 | duk_dup(ctx, -2); 177 | snprintf(key, KEYLEN, "%"PRIXPTR, (uintptr_t)handle); 178 | duk_put_prop_string(ctx, -2, key); 179 | duk_pop(ctx); 180 | } 181 | 182 | void duv_remove_handle(duk_context *ctx, void *handle) { 183 | duk_push_heap_stash(ctx); 184 | snprintf(key, KEYLEN, "%"PRIXPTR, (uintptr_t)handle); 185 | duk_del_prop_string(ctx, -1, key); 186 | duk_pop(ctx); 187 | } 188 | 189 | void duv_push_handle(duk_context *ctx, void *handle) { 190 | duk_push_heap_stash(ctx); 191 | snprintf(key, KEYLEN, "%"PRIXPTR, (uintptr_t)handle); 192 | duk_get_prop_string(ctx, -1, key); 193 | duk_remove(ctx, -2); 194 | } 195 | 196 | void* duv_get_handle(duk_context *ctx, int index) { 197 | duk_get_prop_string(ctx, index, "\xff""uv-data"); 198 | void* handle = duk_get_buffer(ctx, -1, 0); 199 | duk_pop(ctx); 200 | return handle; 201 | } 202 | 203 | duk_bool_t duv_is_handle_of(duk_context *ctx, int index, duv_type_mask_t mask) { 204 | if (!duk_is_object(ctx, index)) return 0; 205 | duk_get_prop_string(ctx, index, "\xff""uv-type"); 206 | int type = duk_get_int(ctx, -1); 207 | duk_bool_t is = (1 << type) & mask; 208 | duk_pop(ctx); 209 | return is; 210 | } 211 | 212 | void* duv_require_this_handle(duk_context *ctx, duv_type_mask_t mask) { 213 | duk_push_this(ctx); 214 | if (!duv_is_handle_of(ctx, -1, mask)) { 215 | duk_pop(ctx); 216 | duk_error(ctx, DUK_ERR_TYPE_ERROR, "this must be %s", duv_mask_to_string(mask)); 217 | } 218 | void *handle = duv_get_handle(ctx, -1); 219 | duk_insert(ctx, 0); 220 | return handle; 221 | } 222 | 223 | void duv_emit(uv_handle_t* handle, const char* key, int nargs, int cleanup) { 224 | duk_context *ctx = handle->data; 225 | duv_push_handle(ctx, handle); 226 | // stack: args... this 227 | duk_get_prop_string(ctx, -1, key); 228 | // stack: args... this fn 229 | if (cleanup) duk_del_prop_string(ctx, -2, key); 230 | // stack: args... this fn 231 | if (!duk_is_function(ctx, -1)) { 232 | duk_pop_n(ctx, 2 + nargs); 233 | return; 234 | } 235 | duk_insert(ctx, -(nargs + 2)); 236 | // stack: fn args... this 237 | duk_insert(ctx, -(nargs + 1)); 238 | // stack: fn this args... 239 | duk_call_method(ctx, nargs); 240 | // stack: result 241 | duk_pop(ctx); 242 | } 243 | 244 | // Assumes nargs are the top of the stack. Rest comes from request 245 | // Return value is not left on the stack. 246 | void duv_resolve(uv_req_t* req, int nargs) { 247 | duk_context *ctx = req->data; 248 | duv_push_handle(ctx, req); 249 | // stack: args... obj 250 | duk_get_prop_string(ctx, -1, "\xff""uv-callback"); 251 | // stack: args... obj callback 252 | duk_del_prop_string(ctx, -2, "\xff""uv-callback"); 253 | // stack: args... obj callback 254 | 255 | if (!duk_is_function(ctx, -1)) { 256 | // stack: args... obj callback 257 | duk_pop_n(ctx, 2 + nargs); 258 | return; 259 | } 260 | duk_remove(ctx, -2); 261 | // stack: args... callback 262 | duk_insert(ctx, -(nargs + 1)); 263 | // stack: callback args... 264 | duk_call(ctx, nargs); 265 | // stack: result 266 | duk_pop(ctx); 267 | 268 | // Remove the request from the GC roots 269 | duv_remove_handle(ctx, req); 270 | } 271 | 272 | void duv_get_data(duk_context *ctx, int index, uv_buf_t *buf) { 273 | if (duk_is_string(ctx, index)) { 274 | buf->base = (char*) duk_get_lstring(ctx, index, &buf->len); 275 | } 276 | else { 277 | buf->base = duk_get_buffer(ctx, index, &buf->len); 278 | } 279 | } 280 | -------------------------------------------------------------------------------- /src/duv/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILS_H 2 | #define UTILS_H 3 | 4 | #include "duv.h" 5 | #include "dschema.h" 6 | 7 | const char* duv_protocol_to_string(int family); 8 | 9 | const char* duv_req_type_to_string(uv_req_type type); 10 | const char* duv_type_to_string(duv_type_t type); 11 | const char* duv_mask_to_string(duv_type_mask_t mask); 12 | 13 | // Retrieve the stored libuv loop from the duktape context. 14 | uv_loop_t* duv_loop(duk_context *ctx); 15 | // Push status as either error or null 16 | void duv_push_status(duk_context *ctx, int status); 17 | // Throw a libuv error in duktape. 18 | void duv_error(duk_context *ctx, int status); 19 | // Check return value from libuv function for errors. 20 | void duv_check(duk_context *ctx, int status); 21 | 22 | // Assumes buffer is at top of stack. 23 | // replaces with this on top of stack. 24 | void duv_setup_handle(duk_context *ctx, uv_handle_t *handle, duv_type_t type); 25 | 26 | // Assumes buffer is at top of stack. 27 | duk_ret_t duv_setup_request(duk_context *ctx, uv_req_t* req, int callback); 28 | 29 | // Store copy of value on top of stack using handle for key 30 | void duv_store_handle(duk_context *ctx, void *handle); 31 | // Remote a value from the global index by key 32 | void duv_remove_handle(duk_context *ctx, void *handle); 33 | // Push the object belonging to a handle on the stack. 34 | void duv_push_handle(duk_context *ctx, void *handle); 35 | // Get the handle from object at index. 36 | void* duv_get_handle(duk_context *ctx, int index); 37 | 38 | // Schema helper for checking arguments to be a certain handle type 39 | duk_bool_t duv_is_handle_of(duk_context *ctx, int index, duv_type_mask_t mask); 40 | // Get the this handle. 41 | void* duv_require_this_handle(duk_context *ctx, duv_type_mask_t mask); 42 | // Assumes nargs are the top of the stack. Rest comes from handle and key. 43 | // Return value is not left on the stack. 44 | void duv_emit(uv_handle_t* handle, const char* key, int nargs, int cleanup); 45 | // Assumes nargs are the top of the stack. Rest comes from request 46 | // Return value is not left on the stack. 47 | void duv_resolve(uv_req_t* req, int nargs); 48 | 49 | void duv_get_data(duk_context *ctx, int index, uv_buf_t *buf); 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /src/env.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include "env.h" 5 | #include 6 | #ifdef __APPLE__ 7 | #include 8 | #define environ (*_NSGetEnviron()) 9 | #elif !defined(_MSC_VER) 10 | extern char **environ; 11 | #endif 12 | 13 | duk_ret_t env_keys(duk_context *ctx) { 14 | #ifndef _WIN32 15 | 16 | duk_push_array(ctx); 17 | for (unsigned int i = 0; environ[i]; ++i) { 18 | const char* var = environ[i]; 19 | const char* s = strchr(var, '='); 20 | const size_t length = s ? (size_t)(s - var) : strlen(var); 21 | duk_push_lstring(ctx, var, length); 22 | duk_put_prop_index(ctx, -2, i); 23 | } 24 | 25 | #else // _WIN32 26 | 27 | int i = 0; 28 | int show_hidden = 0; 29 | WCHAR* p; 30 | WCHAR* environment = GetEnvironmentStringsW(); 31 | if (!environment) return 0; 32 | p = environment; 33 | if (duk_is_boolean(ctx, 0)) { 34 | show_hidden = duk_get_boolean(ctx, 0); 35 | } 36 | 37 | duk_push_array(ctx); 38 | while (*p) { 39 | char* utf8; 40 | size_t utf8_len; 41 | WCHAR* s; 42 | 43 | if (*p == L'=') { 44 | // If the key starts with '=' it is a hidden environment variable. 45 | if (show_hidden) { 46 | s = wcschr(p + 1, L'='); 47 | } 48 | else { 49 | // Skip it 50 | p += wcslen(p) + 1; 51 | continue; 52 | } 53 | } 54 | else { 55 | s = wcschr(p, L'='); 56 | } 57 | 58 | if (!s) { 59 | s = p + wcslen(p); 60 | } 61 | // Convert from WCHAR to UTF-8 encoded char 62 | utf8_len = WideCharToMultiByte(CP_UTF8, 0, p, s - p, NULL, 0, NULL, NULL); 63 | utf8 = malloc(utf8_len); 64 | WideCharToMultiByte(CP_UTF8, 0, p, s - p, utf8, utf8_len, NULL, NULL); 65 | 66 | duk_push_lstring(ctx, utf8, utf8_len); 67 | duk_put_prop_index(ctx, -2, i++); 68 | 69 | free(utf8); 70 | 71 | p = s + wcslen(s) + 1; 72 | } 73 | FreeEnvironmentStringsW(environment); 74 | 75 | #endif 76 | 77 | return 1; 78 | } 79 | 80 | duk_ret_t env_get(duk_context *ctx) { 81 | const char* name = duk_require_string(ctx, 0); 82 | #ifdef _WIN32 83 | char* value; 84 | WCHAR* wname; 85 | WCHAR* wvalue; 86 | size_t wsize, size, wname_size; 87 | 88 | // Convert UTF8 char* name to WCHAR* wname with null terminator 89 | wname_size = MultiByteToWideChar(CP_UTF8, 0, name, -1, NULL, 0); 90 | wname = malloc(wname_size * sizeof(WCHAR)); 91 | if (!wname) return 0 92 | MultiByteToWideChar(CP_UTF8, 0, name, -1, wname, wname_size); 93 | 94 | // Check for the key 95 | wsize = GetEnvironmentVariableW(wname, NULL, 0); 96 | if (!wsize) { 97 | free(wname); 98 | return 0; 99 | } 100 | 101 | // Read the value 102 | wvalue = malloc(wsize * sizeof(WCHAR)); 103 | if (!wvalue) { 104 | free(wname); 105 | return 0; 106 | } 107 | GetEnvironmentVariableW(wname, wvalue, wsize); 108 | 109 | // Convert WCHAR* wvalue to UTF8 char* value 110 | size = WideCharToMultiByte(CP_UTF8, 0, wvalue, -1, NULL, 0, NULL, NULL); 111 | value = malloc(size); 112 | if (!value) { 113 | free(wname); 114 | free(wvalue); 115 | return 0; 116 | } 117 | WideCharToMultiByte(CP_UTF8, 0, wvalue, -1, value, size, NULL, NULL); 118 | 119 | duk_push_lstring(ctx, value, size - 1); 120 | 121 | free(wname); 122 | free(wvalue); 123 | free(value); 124 | 125 | #else 126 | duk_push_string(ctx, getenv(name)); 127 | #endif 128 | return 1; 129 | } 130 | 131 | duk_ret_t env_set(duk_context *ctx) { 132 | const char* name = duk_require_string(ctx, 0); 133 | const char* value = duk_require_string(ctx, 1); 134 | 135 | #ifdef _WIN32 136 | WCHAR* wname; 137 | WCHAR* wvalue; 138 | size_t wname_size, wvalue_size; 139 | int r; 140 | // Convert UTF8 char* name to WCHAR* wname with null terminator 141 | wname_size = MultiByteToWideChar(CP_UTF8, 0, name, -1, NULL, 0); 142 | wname = malloc(wname_size * sizeof(WCHAR)); 143 | if (!wname) return luaL_error(L, "Problem allocating memory for environment variable."); 144 | MultiByteToWideChar(CP_UTF8, 0, name, -1, wname, wname_size); 145 | 146 | // Convert UTF8 char* value to WCHAR* wvalue with null terminator 147 | wvalue_size = MultiByteToWideChar(CP_UTF8, 0, value, -1, NULL, 0); 148 | wvalue = malloc(wvalue_size * sizeof(WCHAR)); 149 | if (!wvalue) { 150 | free(wname); 151 | return 0; 152 | } 153 | MultiByteToWideChar(CP_UTF8, 0, value, -1, wvalue, wvalue_size); 154 | 155 | r = SetEnvironmentVariableW(wname, wvalue); 156 | 157 | free(wname); 158 | free(wvalue); 159 | if (r == 0) return 0; 160 | #else 161 | int r = setenv(name, value, 1); 162 | if (r) { 163 | if (r == EINVAL) { 164 | duk_error(ctx, DUK_ERR_ERROR, "EINVAL: Invalid name."); 165 | } 166 | return 0; 167 | } 168 | #endif 169 | return 0; 170 | } 171 | 172 | duk_ret_t env_unset(duk_context *ctx) { 173 | const char* name = duk_require_string(ctx, 0); 174 | 175 | #ifdef __linux__ 176 | if (unsetenv(name)) { 177 | if (errno == EINVAL) { 178 | duk_error(ctx, DUK_ERR_ERROR, "EINVAL: name contained an '=' character"); 179 | } 180 | return 0; 181 | } 182 | #elif defined(_WIN32) 183 | WCHAR* wname; 184 | size_t wname_size; 185 | // Convert UTF8 char* name to WCHAR* wname with null terminator 186 | wname_size = MultiByteToWideChar(CP_UTF8, 0, name, -1, NULL, 0); 187 | wname = malloc(wname_size * sizeof(WCHAR)); 188 | if (!wname) return 0; 189 | MultiByteToWideChar(CP_UTF8, 0, name, -1, wname, wname_size); 190 | SetEnvironmentVariableW(wname, NULL); 191 | #else 192 | unsetenv(name); 193 | #endif 194 | return 0; 195 | } 196 | -------------------------------------------------------------------------------- /src/env.h: -------------------------------------------------------------------------------- 1 | #ifndef ENV_H 2 | #define ENV_H 3 | 4 | #include "../deps/duktape-releases/src/duktape.h" 5 | duk_ret_t env_keys(duk_context *ctx); 6 | duk_ret_t env_get(duk_context *ctx); 7 | duk_ret_t env_set(duk_context *ctx); 8 | duk_ret_t env_unset(duk_context *ctx); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "../deps/duktape-releases/src/duktape.h" 14 | #define MINIZ_HEADER_FILE_ONLY 15 | #include "../deps/miniz.c" 16 | #include "duv/duv.h" 17 | #include "env.h" 18 | #include "path.h" 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | enum build_mode { 25 | BUILD_LINKED, 26 | BUILD_ZIP, 27 | BUILD_EMBEDDED 28 | }; 29 | 30 | static duk_ret_t nucleus_md5(duk_context *ctx) { 31 | dschema_check(ctx, (const duv_schema_entry[]) { 32 | {"data", dschema_is_data}, 33 | {0,0} 34 | }); 35 | uv_buf_t buf; 36 | duv_get_data(ctx, 0, &buf); 37 | uint8_t* hash = duk_push_fixed_buffer(ctx, 16); 38 | mbedtls_md5((uint8_t*)buf.base, buf.len, hash); 39 | return 1; 40 | } 41 | 42 | static duk_ret_t nucleus_sha1(duk_context *ctx) { 43 | dschema_check(ctx, (const duv_schema_entry[]) { 44 | {"data", dschema_is_data}, 45 | {0,0} 46 | }); 47 | uv_buf_t buf; 48 | duv_get_data(ctx, 0, &buf); 49 | uint8_t* hash = duk_push_fixed_buffer(ctx, 20); 50 | mbedtls_sha1((uint8_t*)buf.base, buf.len, hash); 51 | return 1; 52 | } 53 | 54 | static duk_ret_t nucleus_sha256(duk_context *ctx) { 55 | dschema_check(ctx, (const duv_schema_entry[]) { 56 | {"data", dschema_is_data}, 57 | {0,0} 58 | }); 59 | uv_buf_t buf; 60 | duv_get_data(ctx, 0, &buf); 61 | uint8_t* hash = duk_push_fixed_buffer(ctx, 32); 62 | mbedtls_sha256((uint8_t*)buf.base, buf.len, hash, false); 63 | return 1; 64 | } 65 | 66 | static duk_ret_t nucleus_sha512(duk_context *ctx) { 67 | dschema_check(ctx, (const duv_schema_entry[]) { 68 | {"data", dschema_is_data}, 69 | {0,0} 70 | }); 71 | uv_buf_t buf; 72 | duv_get_data(ctx, 0, &buf); 73 | uint8_t* hash = duk_push_fixed_buffer(ctx, 64); 74 | mbedtls_sha512((uint8_t*)buf.base, buf.len, hash, false); 75 | return 1; 76 | } 77 | 78 | static duk_ret_t nucleus_mask(duk_context *ctx) { 79 | dschema_check(ctx, (const duv_schema_entry[]) { 80 | {"data", dschema_is_data}, 81 | {"mask", dschema_is_data}, 82 | {0,0} 83 | }); 84 | uv_buf_t data, mask; 85 | duv_get_data(ctx, 0, &data); 86 | duv_get_data(ctx, 1, &mask); 87 | uint8_t* out = duk_push_fixed_buffer(ctx, data.len); 88 | for (size_t i = 0; i < data.len; i++) { 89 | out[i] = data.base[i] ^ mask.base[i % mask.len]; 90 | } 91 | return 1; 92 | } 93 | 94 | static duk_ret_t nucleus_hex_encode(duk_context *ctx) { 95 | dschema_check(ctx, (const duv_schema_entry[]) { 96 | {"data", dschema_is_data}, 97 | {0,0} 98 | }); 99 | duk_hex_encode(ctx, 0); 100 | return 1; 101 | } 102 | 103 | static duk_ret_t nucleus_hex_decode(duk_context *ctx) { 104 | dschema_check(ctx, (const duv_schema_entry[]) { 105 | {"hex", duk_is_string}, 106 | {0,0} 107 | }); 108 | duk_hex_decode(ctx, 0); 109 | return 1; 110 | } 111 | 112 | static duk_ret_t nucleus_base64_encode(duk_context *ctx) { 113 | dschema_check(ctx, (const duv_schema_entry[]) { 114 | {"data", dschema_is_data}, 115 | {0,0} 116 | }); 117 | duk_base64_encode(ctx, 0); 118 | return 1; 119 | } 120 | 121 | static duk_ret_t nucleus_base64_decode(duk_context *ctx) { 122 | dschema_check(ctx, (const duv_schema_entry[]) { 123 | {"base64", duk_is_string}, 124 | {0,0} 125 | }); 126 | duk_base64_decode(ctx, 0); 127 | return 1; 128 | } 129 | 130 | 131 | static duk_ret_t nucleus_exit(duk_context *ctx) { 132 | exit(duk_require_int(ctx, 0)); 133 | return 0; 134 | } 135 | 136 | static char* base; 137 | static mz_zip_archive zip; 138 | 139 | static struct { 140 | // (path -- data or null) 141 | duk_c_function read; 142 | // (path cb -- exists) and call cb(name, type) for each entry 143 | duk_c_function scan; 144 | } resource; 145 | 146 | static duk_ret_t duv_path_join(duk_context *ctx) { 147 | char store[PATH_MAX]; 148 | mpath_t buffer = (mpath_t){ 149 | .data = store, 150 | .len = 0, 151 | .max = PATH_MAX 152 | }; 153 | for (int i = 0; i < duk_get_top(ctx); i++) { 154 | path_add(&buffer, path_cstr(duk_get_string(ctx, i))); 155 | } 156 | duk_push_lstring(ctx, buffer.data, buffer.len); 157 | return 1; 158 | } 159 | 160 | // Changes the first arg in place 161 | static void canonicalize(duk_context *ctx) { 162 | duk_require_string(ctx, 0); 163 | duk_push_c_function(ctx, duv_path_join, DUK_VARARGS); 164 | duk_dup(ctx, 0); 165 | duk_call(ctx, 1); 166 | duk_replace(ctx, 0); 167 | } 168 | 169 | // Changes the first arg in place 170 | static void resolve(duk_context *ctx) { 171 | duk_require_string(ctx, 0); 172 | duk_push_c_function(ctx, duv_path_join, DUK_VARARGS); 173 | duk_push_string(ctx, base); 174 | duk_dup(ctx, 0); 175 | duk_call(ctx, 2); 176 | duk_replace(ctx, 0); 177 | } 178 | 179 | static duk_ret_t read_from_zip(duk_context *ctx) { 180 | canonicalize(ctx); 181 | const char* filename = duk_get_string(ctx, 0); 182 | size_t size = 0; 183 | char* data = mz_zip_reader_extract_file_to_heap(&zip, filename, &size, 0); 184 | if (data) { 185 | duk_push_lstring(ctx, data, size); 186 | free(data); 187 | } 188 | else { 189 | duk_push_null(ctx); 190 | } 191 | return 1; 192 | } 193 | 194 | static duk_ret_t scan_from_zip(duk_context *ctx) { 195 | canonicalize(ctx); 196 | duk_require_function(ctx, 1); 197 | const char* input = duk_get_string(ctx, 0); 198 | int index = -1; 199 | if (*input) { 200 | duk_dup(ctx, 0); 201 | duk_push_string(ctx, "/"); 202 | duk_concat(ctx, 2); 203 | input = duk_get_string(ctx, -1); 204 | duk_pop(ctx); 205 | index = mz_zip_reader_locate_file(&zip, input, 0, 0); 206 | if (index < 0) { 207 | duk_push_false(ctx); 208 | return 1; 209 | } 210 | if (!mz_zip_reader_is_file_a_directory(&zip, index)) { 211 | duk_error(ctx, DUK_ERR_ERROR, "%s is not a directory", input); 212 | return 0; 213 | } 214 | } 215 | int pathlen = strlen(input); 216 | char *path = malloc(pathlen + 1); 217 | memcpy(path, input, pathlen + 1); 218 | 219 | int num = mz_zip_reader_get_num_files(&zip); 220 | char entry[PATH_MAX]; 221 | while (++index < num) { 222 | mz_uint size = mz_zip_reader_get_filename(&zip, index, entry, PATH_MAX); 223 | if (strncmp(path, entry, pathlen)) { 224 | break; 225 | } 226 | int offset = pathlen; 227 | if (entry[offset] == '/') { 228 | offset++; 229 | } 230 | size -= offset + 1; 231 | char* match = strchr(entry + offset, '/'); 232 | if (match && match[1]) continue; 233 | duk_dup(ctx, 1); 234 | if (entry[offset + size - 1] == '/') { 235 | duk_push_lstring(ctx, entry + offset, size - 1); 236 | duk_push_string(ctx, "directory"); 237 | } 238 | else { 239 | duk_push_lstring(ctx, entry + offset, size); 240 | duk_push_string(ctx, "file"); 241 | } 242 | duk_call(ctx, 2); 243 | duk_pop(ctx); 244 | } 245 | free(path); 246 | duk_push_true(ctx); 247 | return 1; 248 | } 249 | 250 | static duk_ret_t read_from_disk(duk_context *ctx) { 251 | resolve(ctx); 252 | const char* path = duk_require_string(ctx, 0); 253 | int fd = open(path, O_RDONLY); 254 | if (fd < 0) { 255 | if (errno == ENOENT) { 256 | duk_push_null(ctx); 257 | return 1; 258 | } 259 | duk_error(ctx, DUK_ERR_ERROR, "Failed to open %s: %s", path, strerror(errno)); 260 | return 0; 261 | } 262 | int count = 0; 263 | while (true) { 264 | char buf[1024]; 265 | ssize_t num = read(fd, buf, 1024); 266 | if (num == 0) break; 267 | if (num < 0) { 268 | close(fd); 269 | duk_error(ctx, DUK_ERR_ERROR, "Failed to read %s: %s", path, strerror(errno)); 270 | return 0; 271 | } 272 | duk_push_lstring(ctx, buf, num); 273 | count++; 274 | } 275 | close(fd); 276 | duk_concat(ctx, count); 277 | return 1; 278 | } 279 | static duk_ret_t scan_from_disk(duk_context *ctx) { 280 | resolve(ctx); 281 | duk_require_function(ctx, 1); 282 | const char* path = duk_get_string(ctx, 0); 283 | struct dirent *dp; 284 | DIR *dir = opendir(path); 285 | if (!dir) { 286 | if (errno == ENOENT) { 287 | duk_push_null(ctx); 288 | return 1; 289 | } 290 | duk_error(ctx, DUK_ERR_ERROR, "Failed to opendir %s: %s", path, strerror(errno)); 291 | return 0; 292 | } 293 | while ((dp = readdir(dir)) != NULL) { 294 | if (strcmp(dp->d_name, ".") && strcmp(dp->d_name, "..")) { 295 | duk_dup(ctx, 1); 296 | duk_push_string(ctx, dp->d_name); 297 | switch (dp->d_type) { 298 | case DT_BLK: duk_push_string(ctx, "block"); break; 299 | case DT_CHR: duk_push_string(ctx, "character"); break; 300 | case DT_DIR: duk_push_string(ctx, "directory"); break; 301 | case DT_FIFO: duk_push_string(ctx, "fifo"); break; 302 | case DT_LNK: duk_push_string(ctx, "link"); break; 303 | case DT_REG: duk_push_string(ctx, "file"); break; 304 | case DT_SOCK: duk_push_string(ctx, "socket"); break; 305 | default: duk_push_string(ctx, "unknown"); break; 306 | } 307 | duk_call(ctx, 2); 308 | duk_pop(ctx); 309 | } 310 | } 311 | closedir(dir); 312 | duk_push_true(ctx); 313 | return 1; 314 | } 315 | 316 | static void compile(duk_context *ctx, const char* code, const char* name) { 317 | duk_push_string(ctx, code); 318 | duk_push_string(ctx, name); 319 | duk_compile(ctx, 0); 320 | } 321 | 322 | static duk_ret_t nucleus_compile(duk_context *ctx) { 323 | compile(ctx, duk_require_string(ctx, 0), duk_require_string(ctx, 1)); 324 | return 1; 325 | } 326 | 327 | static duk_ret_t nucleus_readfile(duk_context *ctx) { 328 | return resource.read(ctx); 329 | } 330 | 331 | static duk_ret_t nucleus_scandir(duk_context *ctx) { 332 | return resource.scan(ctx); 333 | } 334 | 335 | static duk_ret_t nucleus_dofile(duk_context *ctx) { 336 | const char* filename = duk_require_string(ctx, 0); 337 | resource.read(ctx); 338 | if (!duk_is_string(ctx, -1)) { 339 | duk_error(ctx, DUK_ERR_ERROR, "No such file %s", filename); 340 | return 1; 341 | } 342 | compile(ctx, duk_get_string(ctx, -1), filename); 343 | duk_call(ctx, 0); 344 | return 1; 345 | } 346 | 347 | static const duk_function_list_entry nucleus_functions[] = { 348 | {"envkeys", env_keys, 1}, 349 | {"getenv", env_get, 1}, 350 | {"setenv", env_set, 2}, 351 | {"unsetenv", env_unset, 1}, 352 | {"exit", nucleus_exit, 1}, 353 | {"compile", nucleus_compile, 2}, 354 | {"readfile", nucleus_readfile, 1}, 355 | {"scandir", nucleus_scandir, 2}, 356 | {"dofile", nucleus_dofile, 1}, 357 | {"pathjoin", duv_path_join, DUK_VARARGS}, 358 | {"md5", nucleus_md5, 1}, 359 | {"sha1", nucleus_sha1, 1}, 360 | {"sha256", nucleus_sha256, 1}, 361 | {"sha512", nucleus_sha512, 1}, 362 | {"mask", nucleus_mask, 2}, 363 | {"hexEncode", nucleus_hex_encode, 1}, 364 | {"hexDecode", nucleus_hex_decode, 1}, 365 | {"base64Encode", nucleus_base64_encode, 1}, 366 | {"base64Decode", nucleus_base64_decode, 1}, 367 | {0,0,0} 368 | }; 369 | 370 | static void duk_put_nucleus(duk_context *ctx, int argc, char *argv[], int argstart) { 371 | // nucleus 372 | duk_push_object(ctx); 373 | 374 | // nucleus.base 375 | duk_push_string(ctx, base); 376 | duk_put_prop_string(ctx, -2, "base"); 377 | 378 | // nucleus.cmd 379 | duk_push_string(ctx, argv[0]); 380 | duk_put_prop_string(ctx, -2, "cmd"); 381 | 382 | // nucleus.args 383 | duk_push_array(ctx); 384 | for (int i = argstart; i < argc; i++) { 385 | duk_push_string(ctx, argv[i]); 386 | duk_put_prop_index(ctx, -2, i - argstart); 387 | } 388 | duk_put_prop_string(ctx, -2, "args"); 389 | 390 | // nucleus.rawArgs 391 | duk_push_array(ctx); 392 | for (int i = 0; i < argc; i++) { 393 | duk_push_string(ctx, argv[i]); 394 | duk_put_prop_index(ctx, -2, i); 395 | } 396 | duk_put_prop_string(ctx, -2, "rawArgs"); 397 | 398 | // nucleus.engine 399 | duk_push_string(ctx, "duktape"); 400 | duk_put_prop_string(ctx, -2, "engine"); 401 | 402 | // nucleus.versions 403 | duk_push_object(ctx); 404 | #ifdef DUK_VERSION 405 | duk_push_string(ctx, "v"); 406 | duk_push_int(ctx, DUK_VERSION / 10000); 407 | duk_push_string(ctx, "."); 408 | duk_push_int(ctx, (DUK_VERSION / 100) % 100); 409 | duk_push_string(ctx, "."); 410 | duk_push_int(ctx, DUK_VERSION % 100); 411 | duk_concat(ctx, 6); 412 | duk_put_prop_string(ctx, -2, "duktape"); 413 | #endif 414 | #ifdef MZ_VERSION 415 | duk_push_string(ctx, "v"); 416 | duk_push_string(ctx, MZ_VERSION); 417 | duk_put_prop_string(ctx, -2, "miniz"); 418 | #endif 419 | duk_put_prop_string(ctx, -2, "versions"); 420 | 421 | duk_put_function_list(ctx, -1, nucleus_functions); 422 | 423 | // nucleus.uv 424 | duv_push_module(ctx); 425 | duk_put_prop_string(ctx, -2, "uv"); 426 | 427 | duk_put_global_string(ctx, "nucleus"); 428 | } 429 | 430 | void print_version() { 431 | fprintf(stderr, "Nucleus v0.0.0\n"); 432 | } 433 | 434 | void print_usage(const char* progname) { 435 | print_version(); 436 | fprintf(stderr, "Usage:\n" 437 | "\n" 438 | " %s source -- args... Run app from source tree\n" 439 | " %s source/custom.js -- args... Run custom main script\n" 440 | " %s source [-l] [-z] [-o target] Build app\n" 441 | "\n" 442 | "Options:\n" 443 | "\n" 444 | " -v | --version Print version and exit\n" 445 | " -h | --help Print help and exit\n" 446 | " -l | --linked Link runtime instead of embedding\n" 447 | " -z | --ziponly Only create zip, no embedding\n" 448 | " -o | --output target Generate output binary at this path\n" 449 | "", 450 | progname, progname, progname); 451 | } 452 | 453 | void add_zip_dir(const char* path, const char* prefix) { 454 | struct dirent *dp; 455 | DIR *dir = opendir(path); 456 | if (!dir) { 457 | printf("path=%s\n", path); 458 | perror("Problem reading directory"); 459 | exit(1); 460 | } 461 | char fullPath[PATH_MAX * 2]; 462 | char fullName[PATH_MAX * 2]; 463 | while ((dp = readdir(dir)) != NULL) { 464 | if (strcmp(dp->d_name, ".") && strcmp(dp->d_name, "..")) { 465 | snprintf(fullPath, PATH_MAX * 2, "%s/%s", prefix, dp->d_name); 466 | switch (dp->d_type) { 467 | case DT_DIR: 468 | snprintf(fullPath, PATH_MAX * 2, "%s/%s", path, dp->d_name); 469 | snprintf(fullName, PATH_MAX * 2, "%s%s/", prefix, dp->d_name); 470 | printf("Adding %s\n", fullName); 471 | mz_zip_writer_add_mem(&zip, fullName, 0, 0, 0); 472 | add_zip_dir(fullPath, fullName); 473 | break; 474 | case DT_REG: case DT_LNK: 475 | snprintf(fullPath, PATH_MAX * 2, "%s/%s", path, dp->d_name); 476 | snprintf(fullName, PATH_MAX * 2, "%s%s", prefix, dp->d_name); 477 | printf("Adding %s\n", fullName); 478 | mz_zip_writer_add_file(&zip, fullPath, fullName, fullPath, 0, 4095); 479 | break; 480 | default: 481 | printf("Skipping %s/%s\n", prefix, dp->d_name); 482 | break; 483 | } 484 | } 485 | } 486 | } 487 | 488 | void build_zip(const char* source, const char* target, enum build_mode mode) { 489 | print_version(); 490 | char nucleus[PATH_MAX*2]; 491 | size_t nucleus_size = PATH_MAX*2; 492 | uv_exepath(nucleus, &nucleus_size); 493 | printf("Creating %s\n", target); 494 | int outfd = open(target, O_WRONLY | O_CREAT, 0777); 495 | if (outfd < 0) { 496 | perror("Cannot create target binary"); 497 | exit(1); 498 | } 499 | switch (mode) { 500 | case BUILD_ZIP: 501 | printf("Creating plain zip\n"); 502 | break; 503 | case BUILD_LINKED: 504 | printf("Inserting link to %.*s\n", (int)nucleus_size, nucleus); 505 | write(outfd, "#!", 2); 506 | write(outfd, nucleus, nucleus_size); 507 | write(outfd, " --\n", 1); 508 | break; 509 | case BUILD_EMBEDDED: 510 | fprintf(stderr, "TODO: embed nucleus\n"); 511 | exit(1); 512 | break; 513 | } 514 | close(outfd); 515 | mz_zip_writer_init_file(&zip, target, 1024); 516 | add_zip_dir(source, ""); 517 | exit(1); 518 | } 519 | 520 | int main(int argc, char *argv[]) { 521 | uv_setup_args(argc, argv); 522 | bool isZip = false; 523 | int argstart = 1; 524 | // If we detect a zip file appended to self, use it. 525 | if (mz_zip_reader_init_file(&zip, argv[0], 0)) { 526 | base = argv[0]; 527 | isZip = true; 528 | } else { 529 | int i; 530 | int linked = 0, ziponly = 0; 531 | int outIndex = 0; 532 | for (i = 1; i < argc; i++) { 533 | if (strcmp(argv[i], "--") == 0) { 534 | if (ziponly || linked || outIndex) { 535 | print_usage(argv[0]); 536 | fprintf(stderr, "\nCannot pass app args while building app binary.\n"); 537 | exit(1); 538 | } 539 | i++; 540 | if (!base && i < argc) { 541 | base = argv[i++]; 542 | } 543 | break; 544 | } 545 | if (argv[i][0] == '-') { 546 | if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) { 547 | print_version(); 548 | exit(1); 549 | } 550 | if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) { 551 | print_usage(argv[0]); 552 | exit(1); 553 | } 554 | if (!strcmp(argv[i], "-l") || !strcmp(argv[i], "--linked")) { 555 | linked = 1; 556 | continue; 557 | } 558 | if (!strcmp(argv[i], "-z") || !strcmp(argv[i], "--ziponly")) { 559 | ziponly = 1; 560 | continue; 561 | } 562 | if (!strcmp(argv[i], "-o") || !strcmp(argv[i], "--output")) { 563 | outIndex = ++i; 564 | continue; 565 | } 566 | print_usage(argv[0]); 567 | fprintf(stderr, "\nUnknown flag: %s\n", argv[i]); 568 | exit(1); 569 | } 570 | if (base) { 571 | print_usage(argv[0]); 572 | fprintf(stderr, "\nUnexpected argument: %s\n", argv[i]); 573 | exit(1); 574 | } 575 | base = argv[i]; 576 | } 577 | if (!base) { 578 | print_usage(argv[0]); 579 | fprintf(stderr, "\nMissing path to app and no embedded zip detected\n"); 580 | exit(1); 581 | } 582 | if (linked && ziponly) { 583 | print_usage(argv[0]); 584 | fprintf(stderr, "\nCannoy specify both linked and zip-only\n"); 585 | exit(1); 586 | } 587 | if ((linked || ziponly) && !outIndex) { 588 | print_usage(argv[0]); 589 | fprintf(stderr, "\nLinked or zip-only option was specified, but not out path was given\n"); 590 | exit(1); 591 | } 592 | if (outIndex) { 593 | if (!argv[outIndex]) { 594 | print_usage(argv[0]); 595 | fprintf(stderr, "\nMissing target path for --output option\n"); 596 | exit(1); 597 | } 598 | build_zip(base, argv[outIndex], 599 | linked ? BUILD_LINKED : 600 | ziponly ? BUILD_ZIP : 601 | BUILD_EMBEDDED); 602 | exit(0); 603 | } 604 | 605 | argstart = i; 606 | 607 | if (mz_zip_reader_init_file(&zip, base, 0)) { 608 | isZip = true; 609 | } 610 | } 611 | 612 | const char* originalBase = base; 613 | base = realpath(base, 0); 614 | if (!base) { 615 | print_usage(argv[0]); 616 | fprintf(stderr, "\nNo such file or directory: %s\n", originalBase); 617 | exit(1); 618 | } 619 | path_t path = path_cstr(base); 620 | path_t ext = path_extension(path); 621 | path_t entry = path_cstr("main.js"); 622 | if (path_eq(ext, path_cstr("js"))) { 623 | entry = path_basename(path); 624 | path = path_dirname(path); 625 | base[path.len] = 0; 626 | } 627 | 628 | // printf("base='%s', entry='%.*s'\n", base, entry.len, entry.data); 629 | 630 | if (isZip) { 631 | resource.read = read_from_zip; 632 | resource.scan = scan_from_zip; 633 | } 634 | else { 635 | resource.read = read_from_disk; 636 | resource.scan = scan_from_disk; 637 | } 638 | 639 | // Setup context with global.nucleus 640 | uv_loop_t loop; 641 | uv_loop_init(&loop); 642 | duk_context *ctx = duk_create_heap(0, 0, 0, &loop, 0); 643 | 644 | duk_put_nucleus(ctx, argc, argv, argstart); 645 | 646 | // Run main.js function 647 | duk_push_string(ctx, "nucleus.dofile('"); 648 | duk_push_lstring(ctx, entry.data, entry.len); 649 | duk_push_string(ctx, "')"); 650 | duk_concat(ctx, 3); 651 | if (duk_peval(ctx)) { 652 | duk_dump_context_stderr(ctx); 653 | duk_get_prop_string(ctx, -1, "stack"); 654 | fprintf(stderr, "Uncaught %s\n", duk_safe_to_string(ctx, -1)); 655 | exit(1); 656 | } 657 | 658 | duk_destroy_heap(ctx); 659 | 660 | return 0; 661 | } 662 | -------------------------------------------------------------------------------- /src/path.c: -------------------------------------------------------------------------------- 1 | #include "path.h" 2 | #include 3 | #include 4 | 5 | path_t path_cstr(const char* str) { 6 | path_t result; 7 | result.data = str; 8 | result.len = strlen(str); 9 | return result; 10 | } 11 | 12 | bool path_eq(path_t a, path_t b) { 13 | if (a.len != b.len) return false; 14 | for (unsigned int i = 0; i < a.len; i++) { 15 | if (a.data[i] != b.data[i]) return false; 16 | } 17 | return true; 18 | } 19 | 20 | 21 | bool path_add(mpath_t *base, path_t path) { 22 | // Do nothing if both are empty. 23 | if (base->len == 0 && path.len == 0) return true; 24 | 25 | // If the base is empty and the path is absolute, preserve leading slash 26 | if (base->len == 0 && base->max > 0 && path.len > 0 && path.data[0] == '/') { 27 | base->data[0] = '/'; 28 | base->len = 1; 29 | } 30 | 31 | // Remove any trailing slash in base if there is path 32 | if (path.len > 0 && base->len > 1 && base->data[base->len - 1] == '/') { 33 | base->len--; 34 | } 35 | 36 | // If the base is exactly '.', truncate it. 37 | if (base->len == 1 && base->data[0] == '.') { 38 | base->len = 0; 39 | } 40 | 41 | unsigned int i = 0; 42 | for (;;) { 43 | // Skip any leading slashes in the path 44 | while (i < path.len && path.data[i] == '/') i++; 45 | 46 | // Find the next slash (or end of string); 47 | unsigned j = i; 48 | while (j < path.len && path.data[j] != '/') j++; 49 | 50 | // Match end of segment 51 | if (j == i) break; 52 | 53 | path_t segment = { 54 | .data = path.data + i, 55 | .len = j - i 56 | }; 57 | 58 | // Skip '.' segments 59 | if (segment.len == 1 && segment.data[0] == '.') { 60 | i = j; 61 | continue; 62 | } 63 | 64 | // Match '..' segment 65 | if (j - i == 2 && path.data[i] == '.' && path.data[i + 1] == '.') { 66 | // If this is an empty path, preserve the '..' 67 | if (base->len == 0) { 68 | if (base->max < 2) return false; 69 | base->data[0] = '.'; 70 | base->data[1] = '.'; 71 | base->len = 2; 72 | i = j; 73 | continue; 74 | } 75 | 76 | // If the path is just a slash, leave it alone. 77 | if (base->len == 1 && base->data[0] == '/') { 78 | i = j; 79 | continue; 80 | } 81 | 82 | // Remove trailing slash, if there is one. 83 | if (base->len > 1 && base->data[base->len - 1] == '/') { base->len--; } 84 | 85 | // If the path ends in '..' already, add another. 86 | if (base->len >= 2 && base->data[base->len - 1] == '.' && base->data[base->len - 2] == '.' && ( 87 | base->len == 2 || (base->len > 2 && base->data[base->len - 3] == '/'))) { 88 | if (base->len + 3 >= base->max) return false; 89 | base->data[base->len++] = '/'; 90 | base->data[base->len++] = '.'; 91 | base->data[base->len++] = '.'; 92 | i = j; 93 | continue; 94 | } 95 | 96 | // Pop one segment, including slash. 97 | while (base->len && base->data[base->len - 1] != '/') base->len--; 98 | // Preserve leading slashes. 99 | if (base->len == 0 && base->data[0] == '/') base->len = 1; 100 | i = j; 101 | continue; 102 | } 103 | 104 | // If the base is empty and the segment is absolute, insert leading slash 105 | // Or if the base does not end in slash, add one as separator 106 | if (base->len ? 107 | base->data[base->len - 1] != '/' : 108 | (path.len && path.data[0] == '/')) { 109 | if (base->len + 1 >= base->max) return false; 110 | base->data[base->len++] = '/'; 111 | } 112 | 113 | // Ensure space in the buffer 114 | if (base->len + segment.len >= base->max) return false; 115 | 116 | memcpy(base->data + base->len, segment.data, segment.len); 117 | base->len += segment.len; 118 | 119 | i = j; 120 | } 121 | 122 | // Use '.' if the base ends up empty. 123 | if (base->len == 0 && base->max > 0) { 124 | base->data[0] = '.'; 125 | base->len = 1; 126 | } 127 | 128 | // Make sure ending matches path 129 | if (path.len) { 130 | if (path.data[path.len - 1] == '/') { 131 | if (base->data[base->len - 1] != '/') base->data[base->len++] = '/'; 132 | } 133 | else { 134 | if (base->len > 1 && base->data[base->len - 1] == '/') base->len--; 135 | } 136 | } 137 | return true; 138 | } 139 | 140 | path_t path_dirname(path_t path) { 141 | int absolute = path.len && path.data[0] == '/'; 142 | int trailing = path.len > 1 && path.data[path.len - 1] == '/'; 143 | path_t result; 144 | result.data = path.data; 145 | result.len = 0; 146 | for (int j = path.len - (trailing ? 2 : 1); j >= 0; j--) { 147 | if (result.data[j] == '/') { 148 | result.len = j; 149 | break; 150 | } 151 | } 152 | if (result.len && trailing) { 153 | result.len++; 154 | } 155 | if (absolute && result.len == 0) { 156 | result.len = 1; 157 | } 158 | return result; 159 | } 160 | 161 | path_t path_extension(path_t path) { 162 | int end = path.len; 163 | if (path.data[end - 1] == '/') end--; 164 | int start = end; 165 | for (int i = end - 1; i >= 0; i--) { 166 | if (path.data[i] == '.') { 167 | start = i + 1; 168 | break; 169 | } 170 | if (path.data[i] == '/') break; 171 | } 172 | path_t result; 173 | result.data = path.data + start; 174 | result.len = end - start; 175 | return result; 176 | } 177 | 178 | path_t path_basename(path_t path) { 179 | int end = path.len; 180 | if (path.data[end - 1] == '/') end--; 181 | int start = 0; 182 | for (int i = end - 1; i >= 0; i--) { 183 | if (path.data[i] == '/') { 184 | start = i + 1; 185 | break; 186 | } 187 | } 188 | path_t result; 189 | result.data = path.data + start; 190 | result.len = end - start; 191 | return result; 192 | } 193 | -------------------------------------------------------------------------------- /src/path.h: -------------------------------------------------------------------------------- 1 | #ifndef PATH_H 2 | #define PATH_H 3 | 4 | #include 5 | 6 | typedef struct { 7 | const char *data; 8 | unsigned int len; 9 | } path_t; 10 | 11 | typedef struct { 12 | char *data; 13 | unsigned int len; 14 | unsigned int max; 15 | } mpath_t; 16 | 17 | union { 18 | path_t path; 19 | mpath_t mpath; 20 | } either_t; 21 | 22 | 23 | path_t path_cstr(const char* str); 24 | bool path_eq(path_t a, path_t b); 25 | bool path_add(mpath_t *base, path_t path); 26 | path_t path_dirname(path_t path); 27 | path_t path_extension(path_t path); 28 | path_t path_basename(path_t path); 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /src/test-path.c: -------------------------------------------------------------------------------- 1 | #include "path.c" 2 | #include 3 | #include 4 | 5 | #define PATH_MAX 4096 6 | 7 | typedef struct { 8 | const char** args; 9 | const char* result; 10 | } test_vector_t; 11 | 12 | static test_vector_t tests[] = (test_vector_t[]){ 13 | {(const char*[]){".", "x/b", "..", "/b/c.js", NULL}, "x/b/c.js"}, 14 | {(const char*[]){"/.", "x/b", "..", "/b/c.js", NULL}, "/x/b/c.js"}, 15 | {(const char*[]){"/foo", "../../../bar", NULL}, "/bar"}, 16 | {(const char*[]){"foo", "../../../bar", NULL}, "../../bar"}, 17 | {(const char*[]){"foo/", "../../../bar", NULL}, "../../bar"}, 18 | {(const char*[]){"foo/x", "../../../bar", NULL}, "../bar"}, 19 | {(const char*[]){"foo/x", "./bar", NULL}, "foo/x/bar"}, 20 | {(const char*[]){"foo/x/", "./bar", NULL}, "foo/x/bar"}, 21 | {(const char*[]){"foo/x/", ".", "bar", NULL}, "foo/x/bar"}, 22 | {(const char*[]){"./", NULL}, "./"}, 23 | {(const char*[]){".", "./", NULL}, "./"}, 24 | {(const char*[]){".", ".", ".", NULL}, "."}, 25 | {(const char*[]){".", "./", ".", NULL}, "."}, 26 | {(const char*[]){".", "/./", ".", NULL}, "."}, 27 | {(const char*[]){".", "/////./", ".", NULL}, "."}, 28 | {(const char*[]){".", NULL}, "."}, 29 | {(const char*[]){"", ".", NULL}, "."}, 30 | {(const char*[]){"", "foo", NULL}, "foo"}, 31 | {(const char*[]){"foo", "/bar", NULL}, "foo/bar"}, 32 | {(const char*[]){"", "/foo", NULL}, "/foo"}, 33 | {(const char*[]){"", "", "/foo", NULL}, "/foo"}, 34 | {(const char*[]){"", "", "foo", NULL}, "foo"}, 35 | {(const char*[]){"foo", "", NULL}, "foo"}, 36 | {(const char*[]){"foo/", "", NULL}, "foo/"}, 37 | {(const char*[]){"foo", "", "/bar", NULL}, "foo/bar"}, 38 | {(const char*[]){"./", "..", "/foo", NULL}, "../foo"}, 39 | {(const char*[]){"./", "..", "..", "/foo", NULL}, "../../foo"}, 40 | {(const char*[]){".", "..", "..", "/foo", NULL}, "../../foo"}, 41 | {(const char*[]){"", "..", "..", "/foo", NULL}, "../../foo"}, 42 | {(const char*[]){"/", NULL}, "/"}, 43 | {(const char*[]){"/", ".", NULL}, "/"}, 44 | {(const char*[]){"/", "..", NULL}, "/"}, 45 | {(const char*[]){"/", "..", "..", NULL}, "/"}, 46 | {(const char*[]){"", NULL}, "."}, 47 | {(const char*[]){"", "", NULL}, "."}, 48 | {(const char*[]){" /foo", NULL}, " /foo"}, 49 | {(const char*[]){" ", "foo", NULL}, " /foo"}, 50 | {(const char*[]){" ", ".", NULL}, " "}, 51 | {(const char*[]){" ", "/", NULL}, " /"}, 52 | {(const char*[]){" ", "", NULL}, " "}, 53 | {(const char*[]){"/", "foo", NULL}, "/foo"}, 54 | {(const char*[]){"/", "/foo", NULL}, "/foo"}, 55 | {(const char*[]){"/", "//foo", NULL}, "/foo"}, 56 | {(const char*[]){"/", "", "/foo", NULL}, "/foo"}, 57 | {(const char*[]){"", "/", "foo", NULL}, "/foo"}, 58 | {(const char*[]){"", "/", "/foo", NULL}, "/foo"}, 59 | {NULL,NULL}, 60 | }; 61 | 62 | static const char* dirname_tests[] = (const char*[]){ 63 | "a/b", "a", 64 | "/a/b", "/a", 65 | "a/b/", "a/", 66 | "/a/b/", "/a/", 67 | "a", "", 68 | "a/", "", 69 | "/a", "/", 70 | "/a/", "/", 71 | "", "", 72 | "/", "/", 73 | 0 74 | }; 75 | 76 | static const char* ext_tests[] = (const char*[]){ 77 | "a.b", "b", 78 | "a.b/", "b", 79 | "a.b/c", "", 80 | "a.b/c.d", "d", 81 | "/", "", 82 | "a/", "", 83 | "a.b.c", "c", 84 | "", "", 85 | 0 86 | }; 87 | 88 | static const char* base_tests[] = (const char*[]){ 89 | "a.b", "a.b", 90 | "a.b/", "a.b", 91 | "a.b/c", "c", 92 | "a.b/c.d", "c.d", 93 | "/", "", 94 | "a/", "a", 95 | "a.b.c", "a.b.c", 96 | "", "", 97 | 0 98 | }; 99 | 100 | int main() { 101 | char store[PATH_MAX]; 102 | test_vector_t *test = &(tests[0]); 103 | while (test->args) { 104 | const char** args = test->args; 105 | const char* expected = test->result; 106 | 107 | printf("join"); 108 | while (*args) { 109 | printf(" '\033[32m%s\033[0m'", *args); 110 | args++; 111 | } 112 | printf(" = '\033[32m%s\033[0m'\n", expected); 113 | args = test->args; 114 | 115 | mpath_t buffer = (mpath_t){ 116 | .data = store, 117 | .len = 0, 118 | .max = PATH_MAX 119 | }; 120 | while (*args) { 121 | path_t path = path_cstr(*args); 122 | path_add(&buffer, path); 123 | args++; 124 | } 125 | if (buffer.len == 0) { 126 | buffer.len = 1; 127 | buffer.data[0] = '.'; 128 | } 129 | 130 | int matched = strlen(expected) == buffer.len && strncmp(expected, buffer.data, buffer.len) == 0; 131 | if (matched) { 132 | printf("\033[34mSuccess\033[0m\n"); 133 | } 134 | else { 135 | printf("\033[1;31mFailed\033[0m: Expected '\033[32m%s\033[0m', but got: '\033[32m%.*s\033[0m'\n", expected, buffer.len, buffer.data); 136 | assert(matched); 137 | } 138 | test++; 139 | } 140 | 141 | const char **dirtest = dirname_tests; 142 | while (*dirtest) { 143 | const char* dir = *dirtest++; 144 | const char* expected = *dirtest++; 145 | printf("dirname '\033[32m%s\033[0m' = '\033[32m%s\033[0m'\n", dir, expected); 146 | path_t result = path_dirname(path_cstr(dir)); 147 | int matched = strlen(expected) == result.len && strncmp(expected, result.data, result.len) == 0; 148 | if (matched) { 149 | printf("\033[34mSuccess\033[0m\n"); 150 | } 151 | else { 152 | printf("\033[1;31mFailed\033[0m: Expected '\033[32m%s\033[0m', but got: '\033[32m%.*s\033[0m'\n", expected, result.len, result.data); 153 | assert(matched); 154 | } 155 | } 156 | 157 | const char **exttest = ext_tests; 158 | while (*exttest) { 159 | const char* path = *exttest++; 160 | const char* expected = *exttest++; 161 | printf("extension '\033[32m%s\033[0m' = '\033[32m%s\033[0m'\n", path, expected); 162 | path_t result = path_extension(path_cstr(path)); 163 | int matched = strlen(expected) == result.len && strncmp(expected, result.data, result.len) == 0; 164 | if (matched) { 165 | printf("\033[34mSuccess\033[0m\n"); 166 | } 167 | else { 168 | printf("\033[1;31mFailed\033[0m: Expected '\033[32m%s\033[0m', but got: '\033[32m%.*s\033[0m'\n", expected, result.len, result.data); 169 | assert(matched); 170 | } 171 | } 172 | 173 | const char **basetest = base_tests; 174 | while (*basetest) { 175 | const char* path = *basetest++; 176 | const char* expected = *basetest++; 177 | printf("basename '\033[32m%s\033[0m' = '\033[32m%s\033[0m'\n", path, expected); 178 | path_t result = path_basename(path_cstr(path)); 179 | int matched = strlen(expected) == result.len && strncmp(expected, result.data, result.len) == 0; 180 | if (matched) { 181 | printf("\033[34mSuccess\033[0m\n"); 182 | } 183 | else { 184 | printf("\033[1;31mFailed\033[0m: Expected '\033[32m%s\033[0m', but got: '\033[32m%.*s\033[0m'\n", expected, result.len, result.data); 185 | assert(matched); 186 | } 187 | } 188 | 189 | } 190 | -------------------------------------------------------------------------------- /target/.keepme: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/creationix/seaduk/917d90f3db0a5c82c407ad6f48bdb4c5cdf8efcf/target/.keepme -------------------------------------------------------------------------------- /test-app/.jshintrc: -------------------------------------------------------------------------------- 1 | 2 | { 3 | // JSHint Default Configuration File (as on JSHint website) 4 | // See http://jshint.com/docs/ for more details 5 | 6 | "maxerr" : 50, // {int} Maximum error before stopping 7 | 8 | // Enforcing 9 | "bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.) 10 | "camelcase" : false, // true: Identifiers must be in camelCase 11 | "curly" : false, // true: Require {} for every new block or scope 12 | "eqeqeq" : true, // true: Require triple equals (===) for comparison 13 | "forin" : false, // true: Require filtering for..in loops with obj.hasOwnProperty() 14 | "freeze" : true, // true: prohibits overwriting prototypes of native objects such as Array, Date etc. 15 | "immed" : false, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());` 16 | "latedef" : false, // true: Require variables/functions to be defined before being used 17 | "newcap" : false, // true: Require capitalization of all constructor functions e.g. `new F()` 18 | "noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee` 19 | "noempty" : true, // true: Prohibit use of empty blocks 20 | "nonbsp" : true, // true: Prohibit "non-breaking whitespace" characters. 21 | "nonew" : false, // true: Prohibit use of constructors for side-effects (without assignment) 22 | "plusplus" : false, // true: Prohibit use of `++` and `--` 23 | "quotmark" : false, // Quotation mark consistency: 24 | // false : do nothing (default) 25 | // true : ensure whatever is used is consistent 26 | // "single" : require single quotes 27 | // "double" : require double quotes 28 | "undef" : true, // true: Require all non-global variables to be declared (prevents global leaks) 29 | "unused" : true, // Unused variables: 30 | // true : all variables, last function parameter 31 | // "vars" : all variables only 32 | // "strict" : all variables, all function parameters 33 | "strict" : true, // true: Requires all functions run in ES5 Strict Mode 34 | "maxparams" : false, // {int} Max number of formal params allowed per function 35 | "maxdepth" : false, // {int} Max depth of nested blocks (within functions) 36 | "maxstatements" : false, // {int} Max number statements per function 37 | "maxcomplexity" : false, // {int} Max cyclomatic complexity per function 38 | "maxlen" : false, // {int} Max number of characters per line 39 | "varstmt" : false, // true: Disallow any var statements. Only `let` and `const` are allowed. 40 | 41 | // Relaxing 42 | "asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons) 43 | "boss" : false, // true: Tolerate assignments where comparisons would be expected 44 | "debug" : false, // true: Allow debugger statements e.g. browser breakpoints. 45 | "eqnull" : false, // true: Tolerate use of `== null` 46 | "esversion" : 5, // {int} Specify the ECMAScript version to which the code must adhere. 47 | "moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features) 48 | // (ex: `for each`, multiple try/catch, function expression…) 49 | "evil" : false, // true: Tolerate use of `eval` and `new Function()` 50 | "expr" : false, // true: Tolerate `ExpressionStatement` as Programs 51 | "funcscope" : false, // true: Tolerate defining variables inside control statements 52 | "globalstrict" : false, // true: Allow global "use strict" (also enables 'strict') 53 | "iterator" : false, // true: Tolerate using the `__iterator__` property 54 | "lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block 55 | "laxbreak" : false, // true: Tolerate possibly unsafe line breakings 56 | "laxcomma" : false, // true: Tolerate comma-first style coding 57 | "loopfunc" : false, // true: Tolerate functions being defined in loops 58 | "multistr" : false, // true: Tolerate multi-line strings 59 | "noyield" : false, // true: Tolerate generator functions with no yield statement in them. 60 | "notypeof" : false, // true: Tolerate invalid typeof operator values 61 | "proto" : false, // true: Tolerate using the `__proto__` property 62 | "scripturl" : false, // true: Tolerate script-targeted URLs 63 | "shadow" : false, // true: Allows re-define variables later in code e.g. `var x=1; x=2;` 64 | "sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation 65 | "supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;` 66 | "validthis" : false, // true: Tolerate using this in a non-constructor function 67 | 68 | // Environments 69 | "browser" : true, // Web Browser (window, document, etc) 70 | "browserify" : false, // Browserify (node.js code in the browser) 71 | "couch" : false, // CouchDB 72 | "devel" : true, // Development/debugging (alert, confirm, etc) 73 | "dojo" : false, // Dojo Toolkit 74 | "jasmine" : false, // Jasmine 75 | "jquery" : false, // jQuery 76 | "mocha" : true, // Mocha 77 | "mootools" : false, // MooTools 78 | "node" : false, // Node.js 79 | "nonstandard" : false, // Widely adopted globals (escape, unescape, etc) 80 | "phantom" : false, // PhantomJS 81 | "prototypejs" : false, // Prototype and Scriptaculous 82 | "qunit" : false, // QUnit 83 | "rhino" : false, // Rhino 84 | "shelljs" : false, // ShellJS 85 | "typed" : false, // Globals for typed array constructions 86 | "worker" : false, // Web Workers 87 | "wsh" : false, // Windows Scripting Host 88 | "yui" : false, // Yahoo User Interface 89 | 90 | // Custom Globals 91 | "globals" : {} // additional predefined global variables 92 | } 93 | -------------------------------------------------------------------------------- /test-app/bootstrap.js: -------------------------------------------------------------------------------- 1 | /*global nucleus,Duktape,p:true*/ 2 | 3 | // Register uv as a require-able module. 4 | Duktape.modLoaded.uv = {exports:nucleus.uv}; 5 | // Bootstrap require by reusing Duktape's default behavior 6 | // It's mostly node.js like. 7 | Duktape.modSearch = function (id) { 8 | "use strict"; 9 | var filename = id + ".js"; 10 | var js = nucleus.readfile(filename); 11 | if (typeof js !== "string") { 12 | throw new Error("No such file in bundle: " + filename); 13 | } 14 | return js; 15 | }; 16 | 17 | p = nucleus.dofile("deps/utils.js").prettyPrint; 18 | -------------------------------------------------------------------------------- /test-app/deps/bodec.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true*/ 2 | "use strict"; 3 | 4 | exports.create = create; 5 | exports.concat = concat; 6 | exports.slice = slice; 7 | exports.indexOf = indexOf; 8 | exports.toString = toString; 9 | 10 | function create(buffer) { 11 | return Buffer(buffer); 12 | } 13 | 14 | function concat(base, buffer) { 15 | return base ? Buffer.concat(base, buffer) : Buffer(buffer); 16 | } 17 | 18 | function slice(buffer, start, end) { 19 | return buffer.slice(start, end); 20 | } 21 | 22 | function indexOf(buffer, raw, start) { 23 | var bl = buffer.length, 24 | rl = raw.length; 25 | outer: for (var i = start || 0; i < bl; i++) { 26 | for (var j = 0; j < rl; j++) { 27 | if (i + j >= bl || buffer[i + j] !== raw.charCodeAt(j)) { 28 | continue outer; 29 | } 30 | } 31 | return i; 32 | } 33 | return -1; 34 | } 35 | 36 | function toString(buffer, start, end) { 37 | return buffer.slice(start, end).toString(); 38 | } 39 | -------------------------------------------------------------------------------- /test-app/deps/http-codec.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true*/ 2 | "use strict"; 3 | 4 | var bodec = require('./bodec'); 5 | var slice = bodec.slice; 6 | var indexOf = bodec.indexOf; 7 | 8 | exports.encoder = encoder; 9 | exports.decoder = decoder; 10 | 11 | // lua-style assert helper 12 | function assert(val, message) { if (!val) throw new Error(message); } 13 | 14 | var STATUS_CODES = { 15 | '100': 'Continue', 16 | '101': 'Switching Protocols', 17 | '102': 'Processing', // RFC 2518, obsoleted by RFC 4918 18 | '200': 'OK', 19 | '201': 'Created', 20 | '202': 'Accepted', 21 | '203': 'Non-Authoritative Information', 22 | '204': 'No Content', 23 | '205': 'Reset Content', 24 | '206': 'Partial Content', 25 | '207': 'Multi-Status', // RFC 4918 26 | '300': 'Multiple Choices', 27 | '301': 'Moved Permanently', 28 | '302': 'Moved Temporarily', 29 | '303': 'See Other', 30 | '304': 'Not Modified', 31 | '305': 'Use Proxy', 32 | '307': 'Temporary Redirect', 33 | '400': 'Bad Request', 34 | '401': 'Unauthorized', 35 | '402': 'Payment Required', 36 | '403': 'Forbidden', 37 | '404': 'Not Found', 38 | '405': 'Method Not Allowed', 39 | '406': 'Not Acceptable', 40 | '407': 'Proxy Authentication Required', 41 | '408': 'Request Time-out', 42 | '409': 'Conflict', 43 | '410': 'Gone', 44 | '411': 'Length Required', 45 | '412': 'Precondition Failed', 46 | '413': 'Request Entity Too Large', 47 | '414': 'Request-URI Too Large', 48 | '415': 'Unsupported Media Type', 49 | '416': 'Requested Range Not Satisfiable', 50 | '417': 'Expectation Failed', 51 | '418': "I'm a teapot", // RFC 2324 52 | '422': 'Unprocessable Entity', // RFC 4918 53 | '423': 'Locked', // RFC 4918 54 | '424': 'Failed Dependency', // RFC 4918 55 | '425': 'Unordered Collection', // RFC 4918 56 | '426': 'Upgrade Required', // RFC 2817 57 | '500': 'Internal Server Error', 58 | '501': 'Not Implemented', 59 | '502': 'Bad Gateway', 60 | '503': 'Service Unavailable', 61 | '504': 'Gateway Time-out', 62 | '505': 'HTTP Version not supported', 63 | '506': 'Variant Also Negotiates', // RFC 2295 64 | '507': 'Insufficient Storage', // RFC 4918 65 | '509': 'Bandwidth Limit Exceeded', 66 | '510': 'Not Extended' // RFC 2774 67 | }; 68 | 69 | function encoder() { 70 | var mode; 71 | 72 | function encodeHead(item) { 73 | if (!item || item === '') { 74 | return item; 75 | } 76 | else if (typeof item !== 'object') { 77 | throw new Error( 78 | "expected an object but got a " + (typeof item) + " when encoding data" 79 | ); 80 | } 81 | var head, chunkedEncoding; 82 | var version = item.version || 1.1; 83 | if (item.method) { 84 | var path = item.path; 85 | assert(path && path.length > 0, "expected non-empty path"); 86 | head = [ item.method + ' ' + item.path + ' HTTP/' + version + '\r\n' ]; 87 | } 88 | else { 89 | var reason = item.reason || STATUS_CODES[item.code]; 90 | head = [ 'HTTP/' + version + ' ' + item.code + ' ' + reason + '\r\n' ]; 91 | } 92 | var headers = item.headers; 93 | if (Array.isArray(headers)) { 94 | for (var i = 0, l = headers.length; i < l; i += 2) { 95 | processHeader(headers[i], headers[i + 1]); 96 | } 97 | } 98 | else { 99 | for (var key in headers) { 100 | processHeader(key, headers[key]); 101 | } 102 | } 103 | function processHeader(key, value) { 104 | var lowerKey = key.toLowerCase(); 105 | if (lowerKey === "transfer-encoding") { 106 | chunkedEncoding = value.toLowerCase() === "chunked"; 107 | } 108 | value = (''+value).replace(/[\r\n]+/, ' '); 109 | head[head.length] = key + ': ' + value + '\r\n'; 110 | } 111 | 112 | head[head.length] = '\r\n'; 113 | 114 | mode = chunkedEncoding && encodeChunked || encodeRaw; 115 | return head.join(''); 116 | } 117 | 118 | function encodeRaw(item) { 119 | if (typeof item !== "string") { 120 | mode = encodeHead; 121 | return encodeHead(item); 122 | } 123 | return item; 124 | } 125 | 126 | function encodeChunked(item) { 127 | if (typeof item !== "string") { 128 | mode = encodeHead; 129 | var extra = encodeHead(item); 130 | if (extra) { 131 | return "0\r\n\r\n" + extra; 132 | } 133 | else { 134 | return "0\r\n\r\n"; 135 | } 136 | } 137 | if (item.length === 0) { 138 | mode = encodeHead; 139 | } 140 | return item.length.toString(16) + "\r\n" + item + "\r\n"; 141 | } 142 | 143 | mode = encodeHead; 144 | function encode(item) { 145 | return mode(item); 146 | } 147 | return encode; 148 | } 149 | 150 | function decoder() { 151 | 152 | // This decoder is somewhat stateful with 5 different parsing states. 153 | var mode; // state variable that points to various decoders 154 | var bytesLeft; // For counted decoder 155 | 156 | // This state is for decoding the status line and headers. 157 | function decodeHead(chunk) { 158 | if (!chunk) return; 159 | 160 | var index = indexOf(chunk, "\r\n\r\n"); 161 | // First make sure we have all the head before continuing 162 | if (index < 0) { 163 | if (chunk.length < 8 * 1024) return; 164 | // But protect against evil clients by refusing heads over 8K long. 165 | throw new Error("entity too large"); 166 | } 167 | var tail = slice(chunk, index + 4); 168 | 169 | // Parse the status/request line 170 | var head = {}; 171 | 172 | index = indexOf(chunk, "\n", 0) + 1; 173 | var line = bodec.toString(chunk, 0, index); 174 | var match = line.match(/^HTTP\/(\d\.\d) (\d+) ([^\r\n]+)/); 175 | if (match) { 176 | head.code = parseInt(match[2]); 177 | head.reason = match[3]; 178 | } 179 | else { 180 | match = line.match(/^([A-Z]+) ([^ ]+) HTTP\/(\d\.\d)/); 181 | if (match) { 182 | head.method = match[1]; 183 | head.path = match[2]; 184 | } 185 | else { 186 | throw new Error("expected HTTP data"); 187 | } 188 | } 189 | head.version = parseFloat(match[3]); 190 | head.keepAlive = head.version > 1.0; 191 | 192 | // We need to inspect some headers to know how to parse the body. 193 | var contentLength; 194 | var chunkedEncoding; 195 | 196 | var headers = head.headers = []; 197 | // Parse the header lines 198 | var start = index; 199 | while ((index = indexOf(chunk, "\n", index) + 1)) { 200 | line = bodec.toString(chunk, start, index); 201 | if (line === '\r\n') break; 202 | start = index; 203 | match = line.match(/^([^:\r\n]+): *([^\r\n]+)/); 204 | if (!match) { 205 | throw new Error("Malformed HTTP header: " + line); 206 | } 207 | var key = match[1], 208 | value = match[2]; 209 | var lowerKey = key.toLowerCase(); 210 | 211 | // Inspect a few headers and remember the values 212 | if (lowerKey === "content-length") { 213 | contentLength = parseInt(value); 214 | } 215 | else if (lowerKey === "transfer-encoding") { 216 | chunkedEncoding = value.toLowerCase() === "chunked"; 217 | } 218 | else if (lowerKey === "connection") { 219 | head.keepAlive = value.toLowerCase() === "keep-alive"; 220 | } 221 | headers.push(key, value); 222 | } 223 | 224 | if (head.keepAlive ? 225 | !(chunkedEncoding || 226 | (contentLength !== undefined && contentLength > 0) 227 | ) : 228 | (head.method === "GET" || head.method === "HEAD")) { 229 | mode = decodeEmpty; 230 | } 231 | else if (chunkedEncoding) { 232 | mode = decodeChunked; 233 | } 234 | else if (contentLength !== undefined) { 235 | bytesLeft = contentLength; 236 | mode = decodeCounted; 237 | } 238 | else if (!head.keepAlive) { 239 | mode = decodeRaw; 240 | } 241 | return [head, tail]; 242 | 243 | } 244 | 245 | // This is used for inserting a single empty string into the output string for known empty bodies 246 | function decodeEmpty(chunk) { 247 | mode = decodeHead; 248 | return ["", chunk || ""]; 249 | } 250 | 251 | function decodeRaw(chunk) { 252 | if (!chunk) return [""]; 253 | if (chunk.length === 0) return; 254 | return [chunk, ""]; 255 | } 256 | 257 | function decodeChunked(chunk) { 258 | var match = chunk.match(/^([0-9a-f]+)([^][^])/i); 259 | if (!match) return; 260 | assert(match[2] === '\r\n', "Invalid chunk encoding header"); 261 | var length = parseInt(match[1], 16); 262 | if (chunk.length < length + 4 + match[1].length) return; 263 | if (length === 0) { 264 | mode = decodeHead; 265 | } 266 | chunk = slice(chunk, match[0].length); 267 | assert(indexOf(chunk, "\r\n") === 0, "Invalid chunk tail"); 268 | return [slice(chunk, 0, length), slice(chunk, length + 2)]; 269 | } 270 | 271 | function decodeCounted(chunk) { 272 | if (bytesLeft === 0) { 273 | mode = decodeEmpty; 274 | return mode(chunk); 275 | } 276 | var length = chunk.length; 277 | // Make sure we have at least one byte to process 278 | if (length === 0) return; 279 | 280 | if (length >= bytesLeft) { 281 | mode = decodeEmpty; 282 | } 283 | 284 | // If the entire chunk fits, pass it all through 285 | if (length <= bytesLeft) { 286 | bytesLeft -= length; 287 | return [chunk, ""]; 288 | } 289 | 290 | return [slice(chunk, 0, bytesLeft), slice(chunk, bytesLeft + 1)]; 291 | } 292 | 293 | // Switch between states by changing which decoder mode points to 294 | mode = decodeHead; 295 | function decode(chunk) { 296 | return mode(chunk); 297 | } 298 | decode.concat = concat; 299 | return decode; 300 | } 301 | 302 | // Concat using node.js style Buffer APIs (works in duktape too) 303 | function concat(buffer, chunk) { 304 | return buffer ? Buffer.concat(buffer, chunk): Buffer(chunk); 305 | } 306 | -------------------------------------------------------------------------------- /test-app/deps/msgpack-codec.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/creationix/seaduk/917d90f3db0a5c82c407ad6f48bdb4c5cdf8efcf/test-app/deps/msgpack-codec.js -------------------------------------------------------------------------------- /test-app/deps/net.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true*/ 2 | "use strict"; 3 | 4 | var uv = require('uv'); 5 | var wrapSocket = require('./wrap-socket'); 6 | 7 | exports.createServer = createServer; 8 | 9 | // options.host -- tcp addr to bind to 10 | // options.port -- tcp port to bind to 11 | // options.backlog -- tcp backlog 12 | // options.encode -- encode function 13 | // options.decode -- decode function 14 | // onClient(client) 15 | // client.read(callback) - read data from stream 16 | // client.read.update(newDecode) - update decoder 17 | // client.write(data, callback) - write data to stream 18 | // client.write.update(newEncode) - update encoder 19 | // client.socket - uv_tcp_t instance 20 | function createServer(options, onClient) { 21 | var server = new uv.Tcp(); 22 | server.bind(options.host || "127.0.0.1", 23 | options.port || 0); 24 | server.listen(options.backlog || 128, onConnection); 25 | 26 | function onConnection(err) { 27 | if (err) throw err; 28 | var client = new uv.Tcp(); 29 | server.accept(client); 30 | var stream = wrapSocket(client, options.decode, options.encode); 31 | onClient(stream); 32 | } 33 | return server; 34 | } 35 | -------------------------------------------------------------------------------- /test-app/deps/theme-16.js: -------------------------------------------------------------------------------- 1 | 2 | // nice color theme using 256-mode colors 3 | ({ 4 | property: "0;37", 5 | braces: "1;30", 6 | sep: "1;30", 7 | 8 | "undefined": "1;30", 9 | boolean: "0;33", 10 | number: "1;33", 11 | string: "0;32", 12 | quotes: "1;32", 13 | escape: "1;32", 14 | function: "0;35", 15 | cfunction: "0;35", 16 | thread: "1;35", 17 | 18 | regexp: "0;31", 19 | date: "1;34", 20 | 21 | null: "1;34", 22 | object: "1;34", 23 | buffer: "1;36", 24 | dbuffer: "0;36", 25 | pointer: "0;31", 26 | 27 | error: "1;31", 28 | success: "1;33;42", 29 | failure: "1;33;41", 30 | highlight: "1;36;44", 31 | }) 32 | -------------------------------------------------------------------------------- /test-app/deps/theme-256.js: -------------------------------------------------------------------------------- 1 | // nice color theme using 256-mode colors 2 | ({ 3 | property: "38;5;253", 4 | braces: "38;5;247", 5 | sep: "38;5;240", 6 | 7 | undefined: "38;5;244", 8 | boolean: "38;5;220", // yellow-orange 9 | number: "38;5;202", // orange 10 | string: "38;5;34", // darker green 11 | quotes: "38;5;40", // green 12 | escape: "38;5;46", // bright green 13 | function: "38;5;129", // purple 14 | cfunction: "38;5;161", // purple-red 15 | thread: "38;5;199", // pink 16 | 17 | regexp: "38;5;214", // yellow-orange 18 | date: "38;5;153", // blue-purple 19 | 20 | null: "38;5;27", // dark blue 21 | object: "38;5;27", // blue 22 | buffer: "38;5;39", // blue2 23 | dbuffer: "38;5;69", // teal 24 | pointer: "38;5;124", // red 25 | 26 | error: "38;5;196", // bright red 27 | success: "38;5;120;48;5;22", // bright green 28 | failure: "38;5;215;48;5;52", // bright green 29 | highlight: "38;5;45;48;5;236", // bright teal with grey background 30 | }) 31 | -------------------------------------------------------------------------------- /test-app/deps/utils.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | "use strict"; 3 | 4 | var width = 80; 5 | 6 | var quote, quote2, obracket, cbracket, obrace, cbrace, comma, colon; 7 | 8 | var theme = nucleus.dofile('deps/theme-256.js'); 9 | 10 | quote = colorize("quotes", '"', "string"); 11 | quote2 = colorize("quotes", '"'); 12 | obracket = colorize("braces", '['); 13 | cbracket = colorize("braces", ']'); 14 | obrace = colorize("braces", '{'); 15 | cbrace = colorize("braces", '}'); 16 | comma = colorize("sep", ','); 17 | colon = colorize("sep", ':'); 18 | 19 | function color(color_name) { 20 | return "\x1b[" + (color_name ? theme[color_name] : "0") + "m"; 21 | } 22 | 23 | function colorize(color_name, string, reset_name) { 24 | return color(color_name) + string + color(reset_name); 25 | } 26 | 27 | function dump(value) { 28 | 29 | var seen = []; 30 | return dumper(value, 0); 31 | function dumper(value, depth) { 32 | var type = typeof value; 33 | 34 | if (type === "undefined") { 35 | return colorize("undefined", "undefined"); 36 | } 37 | if (value === null) { 38 | return colorize("null", "null"); 39 | } 40 | if (type === "boolean") { 41 | return colorize("boolean", "" + value); 42 | } 43 | if (type === "number") { 44 | return colorize("number", "" + value); 45 | } 46 | if (type === "string") { 47 | var str = JSON.stringify(value); 48 | return (quote + str.substring(1, str.length - 1) + quote2). 49 | replace(/(\\u[0-9a-f]{4}|\\["\\/bfnrt])/g, function (match) { 50 | return colorize("escape", match, "string"); 51 | }); 52 | } 53 | var info = Duktape.info(value); 54 | if (type === "function") { 55 | var fname = value.name || info[1]; 56 | // Native CFunctions don't have a .prototype property. 57 | if (value.prototype) { 58 | return colorize("function", "[Function " + fname + "]"); 59 | } 60 | return colorize("cfunction", "[Native " + fname + "]"); 61 | } 62 | var fullName = Object.prototype.toString.call(value); 63 | var name = fullName.substring(8, fullName.length - 1); 64 | if (name === "RegExp") { 65 | return colorize("regexp", "[RegExp " + value + "]"); 66 | } 67 | if (name === "Thread") { 68 | return colorize("thread", "[Thread " + info[1] + "]"); 69 | } 70 | if (name === "Buffer") { 71 | var preview = Array.prototype.slice.call(value, 0, 10).map(function (byte) { 72 | return byte < 16 ? "0" + byte.toString(16) : byte.toString(16); 73 | }).join(" "); 74 | if (value.length > 10) { preview += "..."; } 75 | // Fixed buffers have undefined for info[4] 76 | if (info[4] === undefined) { 77 | return colorize("buffer", "[Buffer " + preview + "]"); 78 | } 79 | return colorize("dbuffer", "[Dynamic Buffer " + preview + "]"); 80 | } 81 | if (name === "Pointer") { 82 | return colorize("pointer", "[Pointer " + info[1] + "]"); 83 | } 84 | if (name === "Error") { 85 | return colorize("error", "[" + value.constructor.name + " " + value.message + "]"); 86 | } 87 | if (name === "Date") { 88 | return colorize("date", "[Date " + value + "]"); 89 | } 90 | if (name === "String") { 91 | return colorize("string", "[String " + JSON.stringify(value) + "]"); 92 | } 93 | if (name === "Number") { 94 | return colorize("number", "[Number " + value + "]"); 95 | } 96 | if (name !== "Object" && name !== "Array" && name !== "global") { 97 | return colorize("object", "[" + name + " " + info[1] + "]"); 98 | } 99 | if (typeof value.inspect === "function") { 100 | var out = value.inspect(); 101 | if (out) return colorize("object", value.inspect()); 102 | } 103 | 104 | var index = seen.indexOf(value); 105 | if (depth > 2 || index >= 0) { 106 | return colorize("object", "[" + name + " " + info[1] + "]"); 107 | } 108 | seen.push(value); 109 | 110 | var parts, opener, closer; 111 | if (name === "Array") { 112 | opener = obracket; 113 | closer = cbracket; 114 | parts = value.map(function (item) { 115 | return dumper(item, depth + 1); 116 | }); 117 | } 118 | else { 119 | opener = obrace; 120 | closer = cbrace; 121 | parts = Object.keys(value).map(function (key) { 122 | return colorize("property", key) + colon + " " + dumper(value[key], depth + 1); 123 | }); 124 | } 125 | 126 | var line = opener + " " + parts.join(comma + " ") + " " + closer; 127 | var max = width - depth * 2; 128 | if (strip(line).length > max) { 129 | var lines = []; 130 | line = []; 131 | max -= 2; 132 | var left = max; 133 | parts.forEach(function (part) { 134 | var len = strip(part).length + 2; 135 | if (left < len) { 136 | if (line.length) { 137 | lines.push(line); 138 | } 139 | left = max; 140 | line = []; 141 | } 142 | line.push(part); 143 | left -= len; 144 | }); 145 | if (line.length) { 146 | lines.push(line); 147 | } 148 | lines = lines.map(function (line) { 149 | return line.join(comma + " "); 150 | }); 151 | 152 | line = opener + "\n " + lines.join(comma + "\n").split("\n").join("\n ") + "\n" + closer; 153 | } 154 | 155 | return line; 156 | } 157 | } 158 | 159 | function strip(string) { 160 | return string.replace(/\x1b\[[^m]*m/g, ''); 161 | } 162 | 163 | function prettyPrint() { 164 | print(Array.prototype.map.call(arguments, dump).join(" ")); 165 | } 166 | 167 | return { 168 | prettyPrint: prettyPrint, 169 | dump: dump, 170 | color: color, 171 | colorize: colorize, 172 | }; 173 | 174 | })(); 175 | -------------------------------------------------------------------------------- /test-app/deps/websocket-codec.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/creationix/seaduk/917d90f3db0a5c82c407ad6f48bdb4c5cdf8efcf/test-app/deps/websocket-codec.js -------------------------------------------------------------------------------- /test-app/deps/wrap-socket.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true*/ 2 | "use strict"; 3 | 4 | module.exports = wrapSocket; 5 | 6 | // socket implements uv_stream_t interface 7 | // decode takes raw data and outputs [value, extra] or nothing 8 | // encode takes value and ouputs raw data 9 | // returns {read,write,socket} 10 | // read(callback) -> value or undefined 11 | // read.update(newDecode) update decoder 12 | // write(value, callback) -> value can be undefined to send EOS 13 | // write.update(newEncode) update encoder 14 | function wrapSocket(socket, decode, encode) { 15 | var readBuffer; // A buffer of past read data that's not consumed by the 16 | // parser yet. 17 | // fifo queue where writers can get ahead of readers and vice-verse 18 | // if reader is higher, then we have multiple reads waiting for data. 19 | // if writer is higher, we have pending data waiting for readers. 20 | var reader = 0, writer = 0; 21 | var queue = {}; 22 | 23 | var inEnd = false, outEnd = false, closed = false; 24 | 25 | // Atomic pausing and unpausing of the read stream for 26 | // proper back-pressure to the socket. 27 | var paused = true; 28 | function pause() { 29 | if (closed || paused) return; 30 | paused = true; 31 | socket.readStop(); 32 | } 33 | function unpause() { 34 | if (closed || !paused) return; 35 | paused = false; 36 | process(); 37 | // Once we're sure process didn't re-pause the stream, startup the source. 38 | if (!paused) { 39 | socket.readStart(onRead); 40 | } 41 | } 42 | 43 | function onRead(err, chunk) { 44 | // p("onRaw", err, chunk); 45 | // If there is data and a decoder, let's process it. 46 | // TODO: This is probably bad design, we should consider carefully. 47 | // Maybe modify the decoder interface to know about EOS events too. 48 | if (!err && chunk && decode) { 49 | // Feed the data to the decoder 50 | readBuffer = decode.concat(readBuffer, chunk); 51 | return process(); 52 | } 53 | if (!chunk) { 54 | inEnd = true; 55 | checkClose(); 56 | } 57 | 58 | // Otherwise forward everything directly to onData 59 | return onData(err, chunk); 60 | } 61 | 62 | function process() { 63 | while (readBuffer !== undefined) { 64 | // Run the data through the decoder, catching any parse errors. 65 | var out; 66 | try { out = decode(readBuffer); } 67 | catch (err) { return onData(err); } 68 | 69 | // If the decoder wants more data, we're done here. Wait for it. 70 | if (!out) break; 71 | 72 | // Remember the leftover data for next round 73 | readBuffer = out[1]; 74 | 75 | // And emit the event. 76 | // If it had to buffer the data, let's stop processing. 77 | if (onData(null, out[0])) break; 78 | } 79 | } 80 | 81 | function onData(err, data) { 82 | // p("onData", err, data); 83 | // If there is a waiting reader, give it the data. 84 | if (reader > writer) { 85 | var callback = queue[writer++]; 86 | callback(err, data); 87 | return false; 88 | } 89 | // If there was an error and no waiting reader, throw it for now. 90 | // This ensures no swallowed/ignored errors. 91 | if (err) throw err; 92 | 93 | // Enqueue the data for later readers. 94 | queue[writer++] = data; 95 | // Since we had to buffer the data, pause the input. 96 | pause(); 97 | // Return true to tell the processor to pause as well. 98 | return true; 99 | } 100 | 101 | function read(callback) { 102 | // If there is pending data ready, use it immedietly. 103 | if (writer > reader) { 104 | var data = queue[reader++]; 105 | return callback(null, data); 106 | } 107 | // Otherwise queue up the reader and unpause the stream. 108 | queue[reader++] = callback; 109 | return unpause(); 110 | } 111 | 112 | var done = false; 113 | function write(data, callback) { 114 | if (closed) { 115 | throw new Error("Can't write to already closed socket"); 116 | } 117 | if (done) { 118 | throw new Error("Can't write to already shutdown socket"); 119 | } 120 | if (data && encode) data = encode(data); 121 | 122 | // Normalize data to buffer or null 123 | if (data || typeof data === 'string') { 124 | return socket.write(data, callback); 125 | } 126 | socket.shutdown(function (err) { 127 | if (callback) callback(err); 128 | outEnd = true; 129 | checkClose(); 130 | }); 131 | } 132 | 133 | function checkClose() { 134 | if (inEnd && outEnd) { 135 | closed = true; 136 | socket.close(); 137 | } 138 | } 139 | 140 | 141 | read.update = function (newDecode) { 142 | decode = newDecode; 143 | }; 144 | write.update = function (newEncode) { 145 | encode = newEncode; 146 | }; 147 | 148 | return { 149 | read: read, 150 | write: write, 151 | socket: socket 152 | }; 153 | } 154 | -------------------------------------------------------------------------------- /test-app/http-server.js: -------------------------------------------------------------------------------- 1 | /*global nucleus,require,p*/ 2 | nucleus.dofile("bootstrap.js"); 3 | 4 | var httpCodec = require('deps/http-codec'); 5 | var createServer = require("deps/net").createServer; 6 | var uv = nucleus.uv; 7 | 8 | var server = createServer({ 9 | port: 8080, // comment out this line to listen on ephemeral high port 10 | encode: httpCodec.encoder(), 11 | decode: httpCodec.decoder(), 12 | }, function (client) { 13 | "use strict"; 14 | p("client", client, client.socket.getpeername()); 15 | client.read(onRead); 16 | function onRead(err, data) { 17 | if (err) throw err; 18 | p("onRead", data); 19 | if (data) return client.read(onRead); 20 | if (data === "") { 21 | client.write({ 22 | code: 200, 23 | headers: [ 24 | "Server", "seaduk", 25 | "Date", new Date().toUTCString(), 26 | "Connection", "close", 27 | "Content-Length", "12", 28 | "Content-Type", "text/plain", 29 | ] 30 | }); 31 | client.write("Hello World\n"); 32 | client.write(null, function (err) { 33 | if (err) throw err; 34 | print("Closing server"); 35 | server.close(); 36 | }); 37 | } 38 | } 39 | }); 40 | 41 | p("New server created", server, server.getsockname()); 42 | 43 | // Start the libuv event loop 44 | uv.run(); 45 | 46 | print("Event loop exiting..."); 47 | -------------------------------------------------------------------------------- /test-app/main.js: -------------------------------------------------------------------------------- 1 | nucleus.dofile("test-nucleus.js"); 2 | nucleus.dofile("test-uv.js"); 3 | -------------------------------------------------------------------------------- /test-app/test-nucleus.js: -------------------------------------------------------------------------------- 1 | print("\nTesting dofile by loading pretty-printer library from another file"); 2 | var p = nucleus.dofile("deps/utils.js").prettyPrint; 3 | p(p); 4 | 5 | print("\nTesting pretty printer by dumping nucleus"); 6 | p(nucleus); 7 | 8 | print("\nEnvironment variable keys"); 9 | var env = {}; 10 | nucleus.envkeys().map(function (key) { 11 | env[key] = nucleus.getenv(key); 12 | // nucleus.setenv(key.toLowerCase(), key); 13 | // nucleus.unsetenv(key); 14 | }); 15 | p(env); 16 | // p(nucleus.envkeys()); 17 | 18 | print("\nTesting scandir at root"); 19 | p(nucleus.scandir('.', p)); 20 | 21 | print("\nTesting scandir at deps"); 22 | p(nucleus.scandir('deps', p)); 23 | 24 | print("\nTest various buffer types"); 25 | p(Duktape.Buffer("test")); 26 | p(new Duktape.Buffer("test")); 27 | p(new ArrayBuffer("test")); 28 | -------------------------------------------------------------------------------- /test-app/test-uv.js: -------------------------------------------------------------------------------- 1 | var p = nucleus.dofile("deps/utils.js").prettyPrint; 2 | var uv = nucleus.uv; 3 | 4 | function assert(condition, message) { 5 | if (!condition) { 6 | throw new Error(message || "Assertion failed") 7 | } 8 | } 9 | 10 | print("\buv.getaddrinfo"); 11 | uv.getaddrinfo({ 12 | node: "luvit.io", 13 | socktype: "stream", // Only show TCP results 14 | // family: "inet", // Only show IPv4 results 15 | }, function (err, results) { 16 | assert(!err, err); 17 | p("Dns results for luvit.io", results); 18 | }); 19 | uv.run(); 20 | uv.getnameinfo({ 21 | ip: "::1", 22 | family: "inet6", 23 | port: 80, 24 | }, function (err, hostname, service) { 25 | assert(!err, err); 26 | p("localhost", hostname, service); 27 | assert(hostname); 28 | assert(service); 29 | }); 30 | uv.run(); 31 | 32 | 33 | print("\nTimer.prototype"); 34 | p(uv.Timer.prototype); 35 | print("Handle.prototype (via Timer.prototype)"); 36 | p(Object.getPrototypeOf(uv.Timer.prototype)); 37 | 38 | print("\nPrepare.prototype"); 39 | p(uv.Prepare.prototype); 40 | print("Handle.prototype (via Prepare.prototype)"); 41 | p(Object.getPrototypeOf(uv.Prepare.prototype)); 42 | 43 | print("\nCheck.prototype"); 44 | p(uv.Check.prototype); 45 | print("Handle.prototype (via Check.prototype)"); 46 | p(Object.getPrototypeOf(uv.Check.prototype)); 47 | 48 | print("\nIdle.prototype"); 49 | p(uv.Idle.prototype); 50 | print("Handle.prototype (via Idle.prototype)"); 51 | p(Object.getPrototypeOf(uv.Idle.prototype)); 52 | 53 | print("\nAsync.prototype"); 54 | p(uv.Async.prototype); 55 | print("Handle.prototype (via Async.prototype)"); 56 | p(Object.getPrototypeOf(uv.Async.prototype)); 57 | 58 | print("\nTcp.prototype"); 59 | p(uv.Tcp.prototype); 60 | print("Stream.prototype (via Tcp.prototype)"); 61 | var streamProto = Object.getPrototypeOf(uv.Tcp.prototype); 62 | p(streamProto); 63 | print("Handle.prototype (via Stream.prototype)"); 64 | p(Object.getPrototypeOf(streamProto)); 65 | 66 | print("\nPipe.prototype"); 67 | p(uv.Pipe.prototype); 68 | print("Stream.prototype (via Pipe.prototype)"); 69 | p(Object.getPrototypeOf(uv.Pipe.prototype)); 70 | 71 | print("\nTty.prototype"); 72 | p(uv.Tty.prototype); 73 | print("Stream.prototype (via Tty.prototype)"); 74 | p(Object.getPrototypeOf(uv.Tty.prototype)); 75 | 76 | var prepare = new uv.Prepare(); 77 | prepare.start(function () { 78 | print("prepare..."); 79 | }); 80 | prepare.unref(); 81 | 82 | var check = new uv.Check(); 83 | check.start(function () { 84 | print("check..."); 85 | }); 86 | 87 | var idle = new uv.Idle(); 88 | idle.start(function () { 89 | print("idle..."); 90 | idle.stop(); 91 | }); 92 | 93 | var async = new uv.Async(function () { 94 | print("async..."); 95 | }); 96 | p("async", async); 97 | async.send(); 98 | 99 | print("\nTesting uv.walk"); 100 | var timer = new uv.Timer(); 101 | var tcp = new uv.Tcp(); 102 | uv.walk(p); 103 | timer.close(); 104 | tcp.close(); 105 | check.unref(); 106 | idle.unref(); 107 | async.unref(); 108 | 109 | print("\nTesting simple timeout"); 110 | var timer = new uv.Timer(); 111 | timer.start(100, 0, function () { 112 | p("timeout", timer); 113 | timer.close(function () { 114 | p("closed", timer); 115 | }); 116 | }); 117 | uv.run(); 118 | 119 | print("\nTesting simple interval"); 120 | timer = new uv.Timer(); 121 | var count = 3; 122 | timer.start(50, 50, function () { 123 | p("interval", timer); 124 | if (--count) return; 125 | timer.close(function () { 126 | p("close", timer); 127 | }); 128 | }); 129 | uv.run(); 130 | 131 | 132 | print("\nTest two concurrent timers"); 133 | var timer1 = new uv.Timer(); 134 | var timer2 = new uv.Timer(); 135 | timer1.start(100, 0, function () { 136 | p("timeout", timer1); 137 | timer1.close(); 138 | timer2.close(); 139 | }); 140 | timer2.start(10, 20, function () { 141 | p("interval", timer2); 142 | }); 143 | uv.run(); 144 | 145 | print("\nTest shrinking interval"); 146 | timer = new uv.Timer(); 147 | timer.start(10, 0, function () { 148 | var repeat = timer.getRepeat(); 149 | p("interval", timer, repeat); 150 | if (repeat === 0) { 151 | timer.setRepeat(8); 152 | timer.again(); 153 | } 154 | else if (repeat == 2) { 155 | timer.stop(); 156 | timer.close(); 157 | } 158 | else { 159 | timer.setRepeat(repeat / 2); 160 | } 161 | }); 162 | uv.run(); 163 | 164 | assert(typeof uv.version === 'function','misc not compiled in seaduk'); 165 | 166 | var version = uv.version(); 167 | var version_string = uv.version_string(); 168 | p({ 169 | version: version, 170 | version_string: version_string, 171 | }); 172 | assert(typeof version === "number"); 173 | assert(typeof version_string === "string"); 174 | 175 | var rss = uv.resident_set_memory(); 176 | var total = uv.get_total_memory(); 177 | p({ 178 | rss: rss, 179 | total: total 180 | }); 181 | assert(rss < total); 182 | 183 | var uptime = uv.uptime(); 184 | p({uptime: uptime}); 185 | 186 | var rusage = uv.getrusage(); 187 | p(rusage); 188 | 189 | var info = uv.cpu_info(); 190 | p(info); 191 | 192 | var addresses = uv.interface_addresses(); 193 | p(addresses); 194 | 195 | var avg = uv.loadavg(); 196 | p({loadavg:avg}); 197 | assert(avg.length === 3); 198 | 199 | var path = uv.exepath(); 200 | p({exepath: path}); 201 | 202 | var old = uv.cwd(); 203 | uv.chdir("/"); 204 | var cwd = uv.cwd(); 205 | p({ 206 | original: old, 207 | changed: cwd 208 | }); 209 | assert(cwd !== old); 210 | uv.chdir(old); 211 | 212 | var old = uv.get_process_title(); 213 | uv.set_process_title("Magic"); 214 | var changed = uv.get_process_title(); 215 | p({ 216 | original: old, 217 | changed: changed, 218 | }); 219 | assert(old !== changed); 220 | uv.set_process_title(old); 221 | 222 | var time = uv.hrtime(); 223 | p({"hrtime": time}); 224 | 225 | print("\nTesting TCP Server"); 226 | var server = new uv.Tcp(); 227 | server.bind("127.0.0.1", 8080); 228 | server.listen(128, function (err) { 229 | if (err) throw err; 230 | var client = new uv.Tcp(); 231 | server.accept(client); 232 | p("client", client, client.getpeername()); 233 | client.readStart(function (err, chunk) { 234 | if (err) throw err; 235 | p("read", chunk); 236 | if (chunk) { 237 | client.write(chunk); 238 | } 239 | else { 240 | client.shutdown(function () { 241 | client.close(); 242 | server.close(); 243 | }); 244 | } 245 | }); 246 | }); 247 | p("server", server, server.getsockname()); 248 | 249 | var client = new uv.Tcp(); 250 | client.connect("127.0.0.1", 8080, function (err) { 251 | if (err) throw err; 252 | p("client connected", client, client.getpeername(), client.getsockname()); 253 | client.readStart(function (err, chunk) { 254 | if (err) throw err; 255 | p("client onread", chunk); 256 | if (!chunk) { 257 | client.close(); 258 | } 259 | }); 260 | client.write("Hello", function (err) { 261 | if (err) throw err; 262 | client.write("World"); 263 | client.shutdown(); 264 | }); 265 | }); 266 | uv.run(); 267 | 268 | 269 | // Use uv.Async to implement nextTick 270 | var ticks = []; 271 | var ticker = new uv.Async(function () { 272 | 273 | if (!ticks.length) return; 274 | var list = ticks; 275 | ticks = []; 276 | ticker.unref(); 277 | list.forEach(function (cb) { 278 | cb(); 279 | }); 280 | }); 281 | 282 | function nextTick(callback) { 283 | ticks.push(callback); 284 | ticker.ref(); 285 | ticker.send(); 286 | } 287 | 288 | print("before"); 289 | nextTick(function () { 290 | print("tick"); 291 | }); 292 | print("after"); 293 | uv.run(); 294 | 295 | 296 | print("Testing stdio with default mode"); 297 | var stdin = new uv.Tty(0, true); 298 | var stdout = new uv.Tty(1, false); 299 | p(stdin, stdout, stdin.getWinsize()); 300 | print("Type test, hit enter to send"); 301 | print("Press Control-D to stop test"); 302 | stdin.readStart(function (err, chunk) { 303 | if (err) throw err; 304 | p("stdin", chunk); 305 | if (chunk) { 306 | stdout.write(chunk); 307 | } 308 | else { 309 | stdin.readStop(); 310 | } 311 | }); 312 | uv.run(); 313 | stdin.close(); 314 | stdout.close(); 315 | 316 | ticker.close(); 317 | prepare.close(); 318 | check.close(); 319 | idle.close(); 320 | 321 | uv.run(); 322 | --------------------------------------------------------------------------------