├── .gitignore ├── AUTHORS ├── .gitattributes ├── jsrun.h ├── one.c ├── regex.h ├── jsboolean.c ├── utf.h ├── opnames.h ├── jslex.h ├── jsbuiltin.h ├── astnames.h ├── README ├── Makefile ├── jsparse.h ├── jserror.c ├── jsintern.c ├── jsnumber.c ├── main.c ├── utf.c ├── jscompile.h ├── jsmath.c ├── jsvalue.h ├── jsstate.c ├── jsregexp.c ├── jsfunction.c ├── jsi.h ├── jsgc.c ├── json.c ├── mujs.h ├── jsbuiltin.c ├── jsproperty.c ├── jsobject.c ├── jsvalue.c ├── jsarray.c ├── jsstring.c ├── jsdtoa.c ├── jslex.c └── jsdump.c /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | tags 3 | tests 4 | specs 5 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Tor Andersson 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Define macro for whitespace settings: 2 | [attr]tabs whitespace=trailing-space,space-before-tab,indent-with-non-tab 3 | 4 | * text=auto 5 | Makefile tabs 6 | *.[ch] tabs 7 | *.js tabs 8 | -------------------------------------------------------------------------------- /jsrun.h: -------------------------------------------------------------------------------- 1 | #ifndef js_run_h 2 | #define js_run_h 3 | 4 | js_Environment *jsR_newenvironment(js_State *J, js_Object *variables, js_Environment *outer); 5 | 6 | struct js_Environment 7 | { 8 | js_Environment *outer; 9 | js_Object *variables; 10 | 11 | js_Environment *gcnext; 12 | int gcmark; 13 | }; 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /one.c: -------------------------------------------------------------------------------- 1 | #include "jsarray.c" 2 | #include "jsboolean.c" 3 | #include "jsbuiltin.c" 4 | #include "jscompile.c" 5 | #include "jsdate.c" 6 | #include "jsdtoa.c" 7 | #include "jsdump.c" 8 | #include "jserror.c" 9 | #include "jsfunction.c" 10 | #include "jsgc.c" 11 | #include "jsintern.c" 12 | #include "jslex.c" 13 | #include "jsmath.c" 14 | #include "jsnumber.c" 15 | #include "jsobject.c" 16 | #include "json.c" 17 | #include "jsparse.c" 18 | #include "jsproperty.c" 19 | #include "jsregexp.c" 20 | #include "jsrun.c" 21 | #include "jsstate.c" 22 | #include "jsstring.c" 23 | #include "jsvalue.c" 24 | #include "regex.c" 25 | #include "utf.c" 26 | #include "utftype.c" 27 | -------------------------------------------------------------------------------- /regex.h: -------------------------------------------------------------------------------- 1 | #ifndef regex_h 2 | #define regex_h 3 | 4 | #define regcomp js_regcomp 5 | #define regexec js_regexec 6 | #define regfree js_regfree 7 | 8 | typedef struct Reprog Reprog; 9 | typedef struct Resub Resub; 10 | 11 | Reprog *regcomp(const char *pattern, int cflags, const char **errorp); 12 | int regexec(Reprog *prog, const char *string, Resub *sub, int eflags); 13 | void regfree(Reprog *prog); 14 | 15 | enum { 16 | /* regcomp flags */ 17 | REG_ICASE = 1, 18 | REG_NEWLINE = 2, 19 | 20 | /* regexec flags */ 21 | REG_NOTBOL = 4, 22 | 23 | /* limits */ 24 | REG_MAXSUB = 16 25 | }; 26 | 27 | struct Resub { 28 | unsigned int nsub; 29 | struct { 30 | const char *sp; 31 | const char *ep; 32 | } sub[REG_MAXSUB]; 33 | }; 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /jsboolean.c: -------------------------------------------------------------------------------- 1 | #include "jsi.h" 2 | #include "jsvalue.h" 3 | #include "jsbuiltin.h" 4 | 5 | static void jsB_new_Boolean(js_State *J) 6 | { 7 | js_newboolean(J, js_toboolean(J, 1)); 8 | } 9 | 10 | static void jsB_Boolean(js_State *J) 11 | { 12 | js_pushboolean(J, js_toboolean(J, 1)); 13 | } 14 | 15 | static void Bp_toString(js_State *J) 16 | { 17 | js_Object *self = js_toobject(J, 0); 18 | if (self->type != JS_CBOOLEAN) js_typeerror(J, "not a boolean"); 19 | js_pushliteral(J, self->u.boolean ? "true" : "false"); 20 | } 21 | 22 | static void Bp_valueOf(js_State *J) 23 | { 24 | js_Object *self = js_toobject(J, 0); 25 | if (self->type != JS_CBOOLEAN) js_typeerror(J, "not a boolean"); 26 | js_pushboolean(J, self->u.boolean); 27 | } 28 | 29 | void jsB_initboolean(js_State *J) 30 | { 31 | J->Boolean_prototype->u.boolean = 0; 32 | 33 | js_pushobject(J, J->Boolean_prototype); 34 | { 35 | jsB_propf(J, "toString", Bp_toString, 0); 36 | jsB_propf(J, "valueOf", Bp_valueOf, 0); 37 | } 38 | js_newcconstructor(J, jsB_Boolean, jsB_new_Boolean, 1); 39 | js_defglobal(J, "Boolean", JS_DONTENUM); 40 | } 41 | -------------------------------------------------------------------------------- /utf.h: -------------------------------------------------------------------------------- 1 | #ifndef js_utf_h 2 | #define js_utf_h 3 | 4 | typedef unsigned short Rune; /* 16 bits */ 5 | 6 | #define chartorune jsU_chartorune 7 | #define runetochar jsU_runetochar 8 | #define runelen jsU_runelen 9 | #define utflen jsU_utflen 10 | 11 | #define isalpharune jsU_isalpharune 12 | #define islowerrune jsU_islowerrune 13 | #define isspacerune jsU_isspacerune 14 | #define istitlerune jsU_istitlerune 15 | #define isupperrune jsU_isupperrune 16 | #define tolowerrune jsU_tolowerrune 17 | #define totitlerune jsU_totitlerune 18 | #define toupperrune jsU_toupperrune 19 | 20 | enum 21 | { 22 | UTFmax = 3, /* maximum bytes per rune */ 23 | Runesync = 0x80, /* cannot represent part of a UTF sequence (<) */ 24 | Runeself = 0x80, /* rune and UTF sequences are the same (<) */ 25 | Runeerror = 0xFFFD, /* decoding error in UTF */ 26 | }; 27 | 28 | unsigned int chartorune(Rune *rune, const char *str); 29 | unsigned int runetochar(char *str, const Rune *rune); 30 | unsigned int runelen(int c); 31 | unsigned int utflen(const char *s); 32 | 33 | int isalpharune(Rune c); 34 | int islowerrune(Rune c); 35 | int isspacerune(Rune c); 36 | int istitlerune(Rune c); 37 | int isupperrune(Rune c); 38 | Rune tolowerrune(Rune c); 39 | Rune totitlerune(Rune c); 40 | Rune toupperrune(Rune c); 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /opnames.h: -------------------------------------------------------------------------------- 1 | "pop", 2 | "dup", 3 | "dup2", 4 | "rot2", 5 | "rot3", 6 | "rot4", 7 | "number_0", 8 | "number_1", 9 | "number_pos", 10 | "number_neg", 11 | "number", 12 | "string", 13 | "closure", 14 | "newarray", 15 | "newobject", 16 | "newregexp", 17 | "undef", 18 | "null", 19 | "true", 20 | "false", 21 | "this", 22 | "global", 23 | "current", 24 | "initlocal", 25 | "getlocal", 26 | "setlocal", 27 | "dellocal", 28 | "initvar", 29 | "defvar", 30 | "hasvar", 31 | "getvar", 32 | "setvar", 33 | "delvar", 34 | "in", 35 | "initprop", 36 | "initprop_n", 37 | "initprop_s", 38 | "initgetter", 39 | "initsetter", 40 | "getprop", 41 | "getprop_s", 42 | "setprop", 43 | "setprop_s", 44 | "delprop", 45 | "delprop_s", 46 | "iterator", 47 | "nextiter", 48 | "call", 49 | "new", 50 | "typeof", 51 | "pos", 52 | "neg", 53 | "bitnot", 54 | "lognot", 55 | "inc", 56 | "dec", 57 | "postinc", 58 | "postdec", 59 | "mul", 60 | "div", 61 | "mod", 62 | "add", 63 | "sub", 64 | "shl", 65 | "shr", 66 | "ushr", 67 | "lt", 68 | "gt", 69 | "le", 70 | "ge", 71 | "eq", 72 | "ne", 73 | "stricteq", 74 | "strictne", 75 | "jcase", 76 | "bitand", 77 | "bitxor", 78 | "bitor", 79 | "instanceof", 80 | "throw", 81 | "try", 82 | "endtry", 83 | "catch", 84 | "endcatch", 85 | "with", 86 | "endwith", 87 | "debugger", 88 | "jump", 89 | "jtrue", 90 | "jfalse", 91 | "return", 92 | -------------------------------------------------------------------------------- /jslex.h: -------------------------------------------------------------------------------- 1 | #ifndef js_lex_h 2 | #define js_lex_h 3 | 4 | enum 5 | { 6 | TK_IDENTIFIER = 256, 7 | TK_NUMBER, 8 | TK_STRING, 9 | TK_REGEXP, 10 | 11 | /* multi-character punctuators */ 12 | TK_LE, 13 | TK_GE, 14 | TK_EQ, 15 | TK_NE, 16 | TK_STRICTEQ, 17 | TK_STRICTNE, 18 | TK_SHL, 19 | TK_SHR, 20 | TK_USHR, 21 | TK_AND, 22 | TK_OR, 23 | TK_ADD_ASS, 24 | TK_SUB_ASS, 25 | TK_MUL_ASS, 26 | TK_DIV_ASS, 27 | TK_MOD_ASS, 28 | TK_SHL_ASS, 29 | TK_SHR_ASS, 30 | TK_USHR_ASS, 31 | TK_AND_ASS, 32 | TK_OR_ASS, 33 | TK_XOR_ASS, 34 | TK_INC, 35 | TK_DEC, 36 | 37 | /* keywords */ 38 | TK_BREAK, 39 | TK_CASE, 40 | TK_CATCH, 41 | TK_CONTINUE, 42 | TK_DEBUGGER, 43 | TK_DEFAULT, 44 | TK_DELETE, 45 | TK_DO, 46 | TK_ELSE, 47 | TK_FALSE, 48 | TK_FINALLY, 49 | TK_FOR, 50 | TK_FUNCTION, 51 | TK_IF, 52 | TK_IN, 53 | TK_INSTANCEOF, 54 | TK_NEW, 55 | TK_NULL, 56 | TK_RETURN, 57 | TK_SWITCH, 58 | TK_THIS, 59 | TK_THROW, 60 | TK_TRUE, 61 | TK_TRY, 62 | TK_TYPEOF, 63 | TK_VAR, 64 | TK_VOID, 65 | TK_WHILE, 66 | TK_WITH, 67 | }; 68 | 69 | int jsY_iswhite(int c); 70 | int jsY_isnewline(int c); 71 | int jsY_ishex(int c); 72 | int jsY_tohex(int c); 73 | 74 | const char *jsY_tokenstring(int token); 75 | int jsY_findword(const char *s, const char **list, int num); 76 | 77 | void jsY_initlex(js_State *J, const char *filename, const char *source); 78 | int jsY_lex(js_State *J); 79 | int jsY_lexjson(js_State *J); 80 | 81 | #endif 82 | -------------------------------------------------------------------------------- /jsbuiltin.h: -------------------------------------------------------------------------------- 1 | #ifndef js_builtin_h 2 | #define js_builtin_h 3 | 4 | void jsB_init(js_State *J); 5 | void jsB_initobject(js_State *J); 6 | void jsB_initarray(js_State *J); 7 | void jsB_initfunction(js_State *J); 8 | void jsB_initboolean(js_State *J); 9 | void jsB_initnumber(js_State *J); 10 | void jsB_initstring(js_State *J); 11 | void jsB_initregexp(js_State *J); 12 | void jsB_initerror(js_State *J); 13 | void jsB_initmath(js_State *J); 14 | void jsB_initjson(js_State *J); 15 | void jsB_initdate(js_State *J); 16 | 17 | void jsB_propf(js_State *J, const char *name, js_CFunction cfun, int n); 18 | void jsB_propn(js_State *J, const char *name, double number); 19 | void jsB_props(js_State *J, const char *name, const char *string); 20 | 21 | typedef struct js_Buffer { unsigned int n, m; char s[64]; } js_Buffer; 22 | 23 | static void js_putc(js_State *J, js_Buffer **sbp, int c) 24 | { 25 | js_Buffer *sb = *sbp; 26 | if (!sb) { 27 | sb = js_malloc(J, sizeof *sb); 28 | sb->n = 0; 29 | sb->m = sizeof sb->s; 30 | *sbp = sb; 31 | } else if (sb->n == sb->m) { 32 | sb = js_realloc(J, sb, (sb->m *= 2) + offsetof(js_Buffer, s)); 33 | *sbp = sb; 34 | } 35 | sb->s[sb->n++] = c; 36 | } 37 | 38 | static inline void js_puts(js_State *J, js_Buffer **sb, const char *s) 39 | { 40 | while (*s) 41 | js_putc(J, sb, *s++); 42 | } 43 | 44 | static inline void js_putm(js_State *J, js_Buffer **sb, const char *s, const char *e) 45 | { 46 | while (s < e) 47 | js_putc(J, sb, *s++); 48 | } 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /astnames.h: -------------------------------------------------------------------------------- 1 | "list", 2 | "fundec", 3 | "identifier", 4 | "exp_identifier", 5 | "exp_number", 6 | "exp_string", 7 | "exp_regexp", 8 | "exp_undef", 9 | "exp_null", 10 | "exp_true", 11 | "exp_false", 12 | "exp_this", 13 | "exp_array", 14 | "exp_object", 15 | "exp_prop_val", 16 | "exp_prop_get", 17 | "exp_prop_set", 18 | "exp_fun", 19 | "exp_index", 20 | "exp_member", 21 | "exp_call", 22 | "exp_new", 23 | "exp_postinc", 24 | "exp_postdec", 25 | "exp_delete", 26 | "exp_void", 27 | "exp_typeof", 28 | "exp_preinc", 29 | "exp_predec", 30 | "exp_pos", 31 | "exp_neg", 32 | "exp_bitnot", 33 | "exp_lognot", 34 | "exp_mod", 35 | "exp_div", 36 | "exp_mul", 37 | "exp_sub", 38 | "exp_add", 39 | "exp_ushr", 40 | "exp_shr", 41 | "exp_shl", 42 | "exp_in", 43 | "exp_instanceof", 44 | "exp_ge", 45 | "exp_le", 46 | "exp_gt", 47 | "exp_lt", 48 | "exp_strictne", 49 | "exp_stricteq", 50 | "exp_ne", 51 | "exp_eq", 52 | "exp_bitand", 53 | "exp_bitxor", 54 | "exp_bitor", 55 | "exp_logand", 56 | "exp_logor", 57 | "exp_cond", 58 | "exp_ass", 59 | "exp_ass_mul", 60 | "exp_ass_div", 61 | "exp_ass_mod", 62 | "exp_ass_add", 63 | "exp_ass_sub", 64 | "exp_ass_shl", 65 | "exp_ass_shr", 66 | "exp_ass_ushr", 67 | "exp_ass_bitand", 68 | "exp_ass_bitxor", 69 | "exp_ass_bitor", 70 | "exp_comma", 71 | "exp_var", 72 | "stm_block", 73 | "stm_empty", 74 | "stm_var", 75 | "stm_if", 76 | "stm_do", 77 | "stm_while", 78 | "stm_for", 79 | "stm_for_var", 80 | "stm_for_in", 81 | "stm_for_in_var", 82 | "stm_continue", 83 | "stm_break", 84 | "stm_return", 85 | "stm_with", 86 | "stm_switch", 87 | "stm_throw", 88 | "stm_try", 89 | "stm_debugger", 90 | "stm_label", 91 | "stm_case", 92 | "stm_default", 93 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | MuJS: an embeddable Javascript interpreter in C. 2 | 3 | ABOUT 4 | 5 | MuJS is a lightweight Javascript interpreter designed for embedding in 6 | other software to extend them with scripting capabilities. 7 | 8 | LICENSE 9 | 10 | MuJS is Copyright 2014 Artifex Software, Inc. 11 | 12 | This program is free software: you can redistribute it and/or modify it under 13 | the terms of the GNU Affero General Public License as published by the Free 14 | Software Foundation, either version 3 of the License, or (at your option) any 15 | later version. 16 | 17 | This program is distributed in the hope that it will be useful, but WITHOUT ANY 18 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 19 | PARTICULAR PURPOSE. See the GNU General Public License for more details. 20 | 21 | You should have received a copy of the GNU Affero General Public License along 22 | with this program. If not, see . 23 | 24 | For commercial licensing please contact sales@artifex.com. 25 | 26 | COMPILING 27 | 28 | If you are building from source you can either use the provided Unix Makefile: 29 | 30 | make release 31 | 32 | Or compile the source with your preferred compiler: 33 | 34 | cc -O3 -c one.c -o libmujs.o 35 | 36 | INSTALLING 37 | 38 | To install the MuJS command line interpreter, static library and header file: 39 | 40 | make prefix=/usr/local install 41 | 42 | DOWNLOAD 43 | 44 | The latest development source is available directly from the git repository: 45 | 46 | git clone http://git.ghostscript.com/mujs.git 47 | 48 | REPORTING BUGS AND PROBLEMS 49 | 50 | Report bugs on the ghostscript bugzilla, with MuJS as the selected component. 51 | 52 | http://bugs.ghostscript.com/ 53 | 54 | The MuJS developers hang out on IRC in the #ghostscript channel on 55 | irc.freenode.net. 56 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SRCS := $(wildcard js*.c utf*.c regex.c) 2 | HDRS := $(wildcard js*.h mujs.h utf.h regex.h) 3 | OBJS := $(SRCS:%.c=build/%.o) 4 | 5 | prefix ?= /usr/local 6 | bindir ?= $(prefix)/bin 7 | incdir ?= $(prefix)/include 8 | libdir ?= $(prefix)/lib 9 | 10 | CC := clang 11 | CFLAGS := -std=c99 -pedantic -Wall -Wextra -Wno-unused-parameter -Wunreachable-code 12 | 13 | ifeq "$(build)" "debug" 14 | CFLAGS += -g 15 | else 16 | CFLAGS += -O2 17 | endif 18 | 19 | default: build build/mujs build/mujsone 20 | 21 | debug: 22 | $(MAKE) build=debug 23 | 24 | release: 25 | $(MAKE) build=release 26 | 27 | astnames.h: jsparse.h 28 | grep '\(AST\|EXP\|STM\)_' jsparse.h | sed 's/^[ \t]*\(AST_\)\?/"/;s/,.*/",/' | tr A-Z a-z > $@ 29 | 30 | opnames.h: jscompile.h 31 | grep 'OP_' jscompile.h | sed 's/^[ \t]*OP_/"/;s/,.*/",/' | tr A-Z a-z > $@ 32 | 33 | one.c: $(SRCS) 34 | ls $(SRCS) | awk '{print "#include \""$$1"\""}' > $@ 35 | 36 | jsdump.c: astnames.h opnames.h 37 | 38 | build: 39 | mkdir -p build 40 | 41 | build/%.o: %.c $(HDRS) 42 | $(CC) $(CFLAGS) -o $@ -c $< 43 | 44 | build/libmujs.a: $(OBJS) 45 | ar cru $@ $^ 46 | 47 | build/mujs: build/main.o build/libmujs.a 48 | $(CC) $(LDFLAGS) -o $@ $^ -lm 49 | 50 | build/mujsone: build/main.o build/one.o 51 | $(CC) $(LDFLAGS) -o $@ $^ -lm 52 | 53 | install: release 54 | install -d $(DESTDIR)$(incdir) 55 | install -d $(DESTDIR)$(libdir) 56 | install -d $(DESTDIR)$(bindir) 57 | install -t $(DESTDIR)$(incdir) mujs.h 58 | install -t $(DESTDIR)$(libdir) build/libmujs.a 59 | install -t $(DESTDIR)$(bindir) build/mujs 60 | 61 | VERSION = $(shell git describe --tags --always) 62 | 63 | tarball: 64 | git archive --format=zip --prefix=mujs-$(VERSION)/ HEAD > mujs-$(VERSION).zip 65 | git archive --format=tar --prefix=mujs-$(VERSION)/ HEAD | gzip > mujs-$(VERSION).tar.gz 66 | git archive --format=tar --prefix=mujs-$(VERSION)/ HEAD | xz > mujs-$(VERSION).tar.xz 67 | 68 | tags: $(SRCS) main.c $(HDRS) 69 | ctags $^ 70 | 71 | test: build/mujs 72 | python tests/sputniktests/tools/sputnik.py --tests=tests/sputniktests --command ./build/mujs --summary 73 | 74 | clean: 75 | rm -f astnames.h opnames.h one.c build/* 76 | 77 | .PHONY: default test clean install debug release 78 | -------------------------------------------------------------------------------- /jsparse.h: -------------------------------------------------------------------------------- 1 | #ifndef js_parse_h 2 | #define js_parse_h 3 | 4 | enum js_AstType 5 | { 6 | AST_LIST, 7 | AST_FUNDEC, 8 | AST_IDENTIFIER, 9 | 10 | EXP_IDENTIFIER, 11 | EXP_NUMBER, 12 | EXP_STRING, 13 | EXP_REGEXP, 14 | 15 | /* literals */ 16 | EXP_UNDEF, /* for array elisions */ 17 | EXP_NULL, 18 | EXP_TRUE, 19 | EXP_FALSE, 20 | EXP_THIS, 21 | 22 | EXP_ARRAY, 23 | EXP_OBJECT, 24 | EXP_PROP_VAL, 25 | EXP_PROP_GET, 26 | EXP_PROP_SET, 27 | 28 | EXP_FUN, 29 | 30 | /* expressions */ 31 | EXP_INDEX, 32 | EXP_MEMBER, 33 | EXP_CALL, 34 | EXP_NEW, 35 | 36 | EXP_POSTINC, 37 | EXP_POSTDEC, 38 | 39 | EXP_DELETE, 40 | EXP_VOID, 41 | EXP_TYPEOF, 42 | EXP_PREINC, 43 | EXP_PREDEC, 44 | EXP_POS, 45 | EXP_NEG, 46 | EXP_BITNOT, 47 | EXP_LOGNOT, 48 | 49 | EXP_MOD, 50 | EXP_DIV, 51 | EXP_MUL, 52 | EXP_SUB, 53 | EXP_ADD, 54 | EXP_USHR, 55 | EXP_SHR, 56 | EXP_SHL, 57 | EXP_IN, 58 | EXP_INSTANCEOF, 59 | EXP_GE, 60 | EXP_LE, 61 | EXP_GT, 62 | EXP_LT, 63 | EXP_STRICTNE, 64 | EXP_STRICTEQ, 65 | EXP_NE, 66 | EXP_EQ, 67 | EXP_BITAND, 68 | EXP_BITXOR, 69 | EXP_BITOR, 70 | EXP_LOGAND, 71 | EXP_LOGOR, 72 | 73 | EXP_COND, 74 | 75 | EXP_ASS, 76 | EXP_ASS_MUL, 77 | EXP_ASS_DIV, 78 | EXP_ASS_MOD, 79 | EXP_ASS_ADD, 80 | EXP_ASS_SUB, 81 | EXP_ASS_SHL, 82 | EXP_ASS_SHR, 83 | EXP_ASS_USHR, 84 | EXP_ASS_BITAND, 85 | EXP_ASS_BITXOR, 86 | EXP_ASS_BITOR, 87 | 88 | EXP_COMMA, 89 | 90 | EXP_VAR, /* var initializer */ 91 | 92 | /* statements */ 93 | STM_BLOCK, 94 | STM_EMPTY, 95 | STM_VAR, 96 | STM_IF, 97 | STM_DO, 98 | STM_WHILE, 99 | STM_FOR, 100 | STM_FOR_VAR, 101 | STM_FOR_IN, 102 | STM_FOR_IN_VAR, 103 | STM_CONTINUE, 104 | STM_BREAK, 105 | STM_RETURN, 106 | STM_WITH, 107 | STM_SWITCH, 108 | STM_THROW, 109 | STM_TRY, 110 | STM_DEBUGGER, 111 | 112 | STM_LABEL, 113 | STM_CASE, 114 | STM_DEFAULT, 115 | }; 116 | 117 | typedef struct js_JumpList js_JumpList; 118 | 119 | struct js_JumpList 120 | { 121 | enum js_AstType type; 122 | int inst; 123 | js_JumpList *next; 124 | }; 125 | 126 | struct js_Ast 127 | { 128 | int type; 129 | int line; 130 | js_Ast *parent, *a, *b, *c, *d; 131 | double number; 132 | const char *string; 133 | js_JumpList *jumps; /* list of break/continue jumps to patch */ 134 | int casejump; /* for switch case clauses */ 135 | js_Ast *gcnext; /* next in alloc list */ 136 | }; 137 | 138 | js_Ast *jsP_parsefunction(js_State *J, const char *filename, const char *params, const char *body); 139 | js_Ast *jsP_parse(js_State *J, const char *filename, const char *source); 140 | void jsP_freeparse(js_State *J); 141 | 142 | const char *jsP_aststring(enum js_AstType type); 143 | void jsP_dumpsyntax(js_State *J, js_Ast *prog); 144 | void jsP_dumplist(js_State *J, js_Ast *prog); 145 | 146 | #endif 147 | -------------------------------------------------------------------------------- /jserror.c: -------------------------------------------------------------------------------- 1 | #include "jsi.h" 2 | #include "jsvalue.h" 3 | #include "jsbuiltin.h" 4 | 5 | #define QQ(X) #X 6 | #define Q(X) QQ(X) 7 | 8 | static void Ep_toString(js_State *J) 9 | { 10 | const char *name = "Error"; 11 | const char *message = ""; 12 | 13 | if (!js_isobject(J, -1)) 14 | js_typeerror(J, "not an object"); 15 | 16 | js_getproperty(J, 0, "name"); 17 | if (js_isdefined(J, -1)) 18 | name = js_tostring(J, -1); 19 | js_pop(J, 1); 20 | 21 | js_getproperty(J, 0, "message"); 22 | if (js_isdefined(J, -1)) 23 | message = js_tostring(J, -1); 24 | js_pop(J, 1); 25 | 26 | if (!strcmp(name, "")) 27 | js_pushliteral(J, message); 28 | else if (!strcmp(message, "")) 29 | js_pushliteral(J, name); 30 | else { 31 | js_pushliteral(J, name); 32 | js_pushliteral(J, ": "); 33 | js_concat(J); 34 | js_pushliteral(J, message); 35 | js_concat(J); 36 | } 37 | } 38 | 39 | static int jsB_ErrorX(js_State *J, js_Object *prototype) 40 | { 41 | js_pushobject(J, jsV_newobject(J, JS_CERROR, prototype)); 42 | if (js_isdefined(J, 1)) { 43 | js_pushstring(J, js_tostring(J, 1)); 44 | js_setproperty(J, -2, "message"); 45 | } 46 | return 1; 47 | } 48 | 49 | static void js_newerrorx(js_State *J, const char *message, js_Object *prototype) 50 | { 51 | js_pushobject(J, jsV_newobject(J, JS_CERROR, prototype)); 52 | js_pushstring(J, message); 53 | js_setproperty(J, -2, "message"); 54 | } 55 | 56 | #define DERROR(name, Name) \ 57 | static void jsB_##Name(js_State *J) { \ 58 | jsB_ErrorX(J, J->Name##_prototype); \ 59 | } \ 60 | void js_new##name(js_State *J, const char *s) { \ 61 | js_newerrorx(J, s, J->Name##_prototype); \ 62 | } \ 63 | void js_##name(js_State *J, const char *fmt, ...) { \ 64 | va_list ap; \ 65 | char buf[256]; \ 66 | va_start(ap, fmt); \ 67 | vsnprintf(buf, sizeof buf, fmt, ap); \ 68 | va_end(ap); \ 69 | js_newerrorx(J, buf, J->Name##_prototype); \ 70 | js_throw(J); \ 71 | } 72 | 73 | DERROR(error, Error) 74 | DERROR(evalerror, EvalError) 75 | DERROR(rangeerror, RangeError) 76 | DERROR(referenceerror, ReferenceError) 77 | DERROR(syntaxerror, SyntaxError) 78 | DERROR(typeerror, TypeError) 79 | DERROR(urierror, URIError) 80 | 81 | #undef DERROR 82 | 83 | void jsB_initerror(js_State *J) 84 | { 85 | js_pushobject(J, J->Error_prototype); 86 | { 87 | jsB_props(J, "name", "Error"); 88 | jsB_props(J, "message", "an error has occurred"); 89 | jsB_propf(J, "toString", Ep_toString, 0); 90 | } 91 | js_newcconstructor(J, jsB_Error, jsB_Error, 1); 92 | js_defglobal(J, "Error", JS_DONTENUM); 93 | 94 | #define IERROR(NAME) \ 95 | js_pushobject(J, J->NAME##_prototype); \ 96 | jsB_props(J, "name", Q(NAME)); \ 97 | js_newcconstructor(J, jsB_##NAME, jsB_##NAME, 1); \ 98 | js_defglobal(J, Q(NAME), JS_DONTENUM); 99 | 100 | IERROR(EvalError); 101 | IERROR(RangeError); 102 | IERROR(ReferenceError); 103 | IERROR(SyntaxError); 104 | IERROR(TypeError); 105 | IERROR(URIError); 106 | 107 | #undef IERROR 108 | } 109 | -------------------------------------------------------------------------------- /jsintern.c: -------------------------------------------------------------------------------- 1 | #include "jsi.h" 2 | 3 | /* Use an AA-tree to quickly look up interned strings. */ 4 | 5 | struct js_StringNode 6 | { 7 | js_StringNode *left, *right; 8 | int level; 9 | char string[1]; 10 | }; 11 | 12 | static js_StringNode jsS_sentinel = { &jsS_sentinel, &jsS_sentinel, 0, ""}; 13 | 14 | static js_StringNode *jsS_newstringnode(js_State *J, const char *string, const char **result) 15 | { 16 | int n = strlen(string); 17 | js_StringNode *node = js_malloc(J, offsetof(js_StringNode, string) + n + 1); 18 | node->left = node->right = &jsS_sentinel; 19 | node->level = 1; 20 | strcpy(node->string, string); 21 | return *result = node->string, node; 22 | } 23 | 24 | static js_StringNode *jsS_skew(js_StringNode *node) 25 | { 26 | if (node->left->level == node->level) { 27 | js_StringNode *temp = node; 28 | node = node->left; 29 | temp->left = node->right; 30 | node->right = temp; 31 | } 32 | return node; 33 | } 34 | 35 | static js_StringNode *jsS_split(js_StringNode *node) 36 | { 37 | if (node->right->right->level == node->level) { 38 | js_StringNode *temp = node; 39 | node = node->right; 40 | temp->right = node->left; 41 | node->left = temp; 42 | ++node->level; 43 | } 44 | return node; 45 | } 46 | 47 | static js_StringNode *jsS_insert(js_State *J, js_StringNode *node, const char *string, const char **result) 48 | { 49 | if (node != &jsS_sentinel) { 50 | int c = strcmp(string, node->string); 51 | if (c < 0) 52 | node->left = jsS_insert(J, node->left, string, result); 53 | else if (c > 0) 54 | node->right = jsS_insert(J, node->right, string, result); 55 | else 56 | return *result = node->string, node; 57 | node = jsS_skew(node); 58 | node = jsS_split(node); 59 | return node; 60 | } 61 | return jsS_newstringnode(J, string, result); 62 | } 63 | 64 | static void dumpstringnode(js_StringNode *node, int level) 65 | { 66 | int i; 67 | if (node->left != &jsS_sentinel) 68 | dumpstringnode(node->left, level + 1); 69 | printf("%d: ", node->level); 70 | for (i = 0; i < level; ++i) 71 | putchar('\t'); 72 | printf("'%s'\n", node->string); 73 | if (node->right != &jsS_sentinel) 74 | dumpstringnode(node->right, level + 1); 75 | } 76 | 77 | void jsS_dumpstrings(js_State *J) 78 | { 79 | js_StringNode *root = J->strings; 80 | printf("interned strings {\n"); 81 | if (root && root != &jsS_sentinel) 82 | dumpstringnode(root, 1); 83 | printf("}\n"); 84 | } 85 | 86 | static void jsS_freestringnode(js_State *J, js_StringNode *node) 87 | { 88 | if (node->left != &jsS_sentinel) jsS_freestringnode(J, node->left); 89 | if (node->right != &jsS_sentinel) jsS_freestringnode(J, node->right); 90 | js_free(J, node); 91 | } 92 | 93 | void jsS_freestrings(js_State *J) 94 | { 95 | if (J->strings && J->strings != &jsS_sentinel) 96 | jsS_freestringnode(J, J->strings); 97 | } 98 | 99 | const char *js_intern(js_State *J, const char *s) 100 | { 101 | const char *result; 102 | if (!J->strings) 103 | J->strings = &jsS_sentinel; 104 | J->strings = jsS_insert(J, J->strings, s, &result); 105 | return result; 106 | } 107 | -------------------------------------------------------------------------------- /jsnumber.c: -------------------------------------------------------------------------------- 1 | #include "jsi.h" 2 | #include "jsvalue.h" 3 | #include "jsbuiltin.h" 4 | 5 | static void jsB_new_Number(js_State *J) 6 | { 7 | js_newnumber(J, js_gettop(J) > 1 ? js_tonumber(J, 1) : 0); 8 | } 9 | 10 | static void jsB_Number(js_State *J) 11 | { 12 | js_pushnumber(J, js_gettop(J) > 1 ? js_tonumber(J, 1) : 0); 13 | } 14 | 15 | static void Np_valueOf(js_State *J) 16 | { 17 | js_Object *self = js_toobject(J, 0); 18 | if (self->type != JS_CNUMBER) js_typeerror(J, "not a number"); 19 | js_pushnumber(J, self->u.number); 20 | } 21 | 22 | static void Np_toString(js_State *J) 23 | { 24 | js_Object *self = js_toobject(J, 0); 25 | int radix = js_isundefined(J, 1) ? 10 : js_tointeger(J, 1); 26 | if (self->type != JS_CNUMBER) js_typeerror(J, "not a number"); 27 | if (radix < 2 || radix > 36) 28 | js_rangeerror(J, "invalid radix"); 29 | if (radix != 10) 30 | js_rangeerror(J, "invalid radix"); 31 | js_pushliteral(J, jsV_numbertostring(J, self->u.number)); 32 | } 33 | 34 | /* Customized ToString() on a number */ 35 | void numtostr(js_State *J, const char *fmt, int w, double n) 36 | { 37 | char buf[32], *e; 38 | if (isnan(n)) js_pushliteral(J, "NaN"); 39 | else if (isinf(n)) js_pushliteral(J, n < 0 ? "-Infinity" : "Infinity"); 40 | else if (n == 0) js_pushliteral(J, "0"); 41 | else { 42 | if (w < 1) w = 1; 43 | if (w > 17) w = 17; 44 | sprintf(buf, fmt, w, n); 45 | e = strchr(buf, 'e'); 46 | if (e) { 47 | int exp = atoi(e+1); 48 | sprintf(e, "e%+d", exp); 49 | } 50 | js_pushstring(J, buf); 51 | } 52 | } 53 | 54 | static void Np_toFixed(js_State *J) 55 | { 56 | js_Object *self = js_toobject(J, 0); 57 | int width = js_tointeger(J, 1); 58 | if (self->type != JS_CNUMBER) js_typeerror(J, "not a number"); 59 | numtostr(J, "%.*f", width, self->u.number); 60 | } 61 | 62 | static void Np_toExponential(js_State *J) 63 | { 64 | js_Object *self = js_toobject(J, 0); 65 | int width = js_tointeger(J, 1); 66 | if (self->type != JS_CNUMBER) js_typeerror(J, "not a number"); 67 | numtostr(J, "%.*e", width, self->u.number); 68 | } 69 | 70 | static void Np_toPrecision(js_State *J) 71 | { 72 | js_Object *self = js_toobject(J, 0); 73 | int width = js_tointeger(J, 1); 74 | if (self->type != JS_CNUMBER) js_typeerror(J, "not a number"); 75 | numtostr(J, "%.*g", width, self->u.number); 76 | } 77 | 78 | void jsB_initnumber(js_State *J) 79 | { 80 | J->Number_prototype->u.number = 0; 81 | 82 | js_pushobject(J, J->Number_prototype); 83 | { 84 | jsB_propf(J, "valueOf", Np_valueOf, 0); 85 | jsB_propf(J, "toString", Np_toString, 1); 86 | jsB_propf(J, "toLocaleString", Np_toString, 0); 87 | jsB_propf(J, "toFixed", Np_toFixed, 1); 88 | jsB_propf(J, "toExponential", Np_toExponential, 1); 89 | jsB_propf(J, "toPrecision", Np_toPrecision, 1); 90 | } 91 | js_newcconstructor(J, jsB_Number, jsB_new_Number, 1); 92 | { 93 | jsB_propn(J, "MAX_VALUE", 1.7976931348623157e+308); 94 | jsB_propn(J, "MIN_VALUE", 5e-324); 95 | jsB_propn(J, "NaN", NAN); 96 | jsB_propn(J, "NEGATIVE_INFINITY", -INFINITY); 97 | jsB_propn(J, "POSITIVE_INFINITY", INFINITY); 98 | } 99 | js_defglobal(J, "Number", JS_DONTENUM); 100 | } 101 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "mujs.h" 6 | 7 | #define PS1 "> " 8 | 9 | static void jsB_gc(js_State *J) 10 | { 11 | int report = js_toboolean(J, 1); 12 | js_gc(J, report); 13 | js_pushundefined(J); 14 | } 15 | 16 | static void jsB_load(js_State *J) 17 | { 18 | const char *filename = js_tostring(J, 1); 19 | int rv = js_dofile(J, filename); 20 | js_pushboolean(J, !rv); 21 | } 22 | 23 | static void jsB_print(js_State *J) 24 | { 25 | unsigned int i, top = js_gettop(J); 26 | for (i = 1; i < top; ++i) { 27 | const char *s = js_tostring(J, i); 28 | if (i > 1) putchar(' '); 29 | fputs(s, stdout); 30 | } 31 | putchar('\n'); 32 | js_pushundefined(J); 33 | } 34 | 35 | static void jsB_write(js_State *J) 36 | { 37 | unsigned int i, top = js_gettop(J); 38 | for (i = 1; i < top; ++i) { 39 | const char *s = js_tostring(J, i); 40 | if (i > 1) putchar(' '); 41 | fputs(s, stdout); 42 | } 43 | js_pushundefined(J); 44 | } 45 | 46 | static void jsB_read(js_State *J) 47 | { 48 | const char *filename = js_tostring(J, 1); 49 | FILE *f; 50 | char *s; 51 | int n, t; 52 | 53 | f = fopen(filename, "rb"); 54 | if (!f) { 55 | js_error(J, "cannot open file: '%s'", filename); 56 | } 57 | 58 | if (fseek(f, 0, SEEK_END) < 0) { 59 | fclose(f); 60 | js_error(J, "cannot seek in file: '%s'", filename); 61 | } 62 | n = ftell(f); 63 | fseek(f, 0, SEEK_SET); 64 | 65 | s = malloc(n + 1); 66 | if (!s) { 67 | fclose(f); 68 | js_error(J, "cannot allocate storage for file contents: '%s'", filename); 69 | } 70 | 71 | t = fread(s, 1, n, f); 72 | if (t != n) { 73 | free(s); 74 | fclose(f); 75 | js_error(J, "cannot read data from file: '%s'", filename); 76 | } 77 | s[n] = 0; 78 | 79 | js_pushstring(J, s); 80 | free(s); 81 | fclose(f); 82 | } 83 | 84 | static void jsB_readline(js_State *J) 85 | { 86 | char line[256]; 87 | int n; 88 | if (!fgets(line, sizeof line, stdin)) 89 | js_error(J, "cannot read line from stdin"); 90 | n = strlen(line); 91 | if (n > 0 && line[n-1] == '\n') 92 | line[n-1] = 0; 93 | js_pushstring(J, line); 94 | } 95 | 96 | static void jsB_quit(js_State *J) 97 | { 98 | exit(js_tonumber(J, 1)); 99 | } 100 | 101 | static const char *require_js = 102 | "function require(name) {\n" 103 | "var cache = require.cache;\n" 104 | "if (name in cache) return cache[name];\n" 105 | "var exports = {};\n" 106 | "cache[name] = exports;\n" 107 | "Function('exports', read(name+'.js'))(exports);\n" 108 | "return exports;\n" 109 | "}\n" 110 | "require.cache = Object.create(null);\n" 111 | ; 112 | 113 | int 114 | main(int argc, char **argv) 115 | { 116 | char line[256]; 117 | js_State *J; 118 | int i; 119 | 120 | J = js_newstate(NULL, NULL); 121 | 122 | js_newcfunction(J, jsB_gc, 0); 123 | js_setglobal(J, "gc"); 124 | 125 | js_newcfunction(J, jsB_load, 1); 126 | js_setglobal(J, "load"); 127 | 128 | js_newcfunction(J, jsB_print, 1); 129 | js_setglobal(J, "print"); 130 | 131 | js_newcfunction(J, jsB_write, 0); 132 | js_setglobal(J, "write"); 133 | 134 | js_newcfunction(J, jsB_read, 1); 135 | js_setglobal(J, "read"); 136 | 137 | js_newcfunction(J, jsB_readline, 0); 138 | js_setglobal(J, "readline"); 139 | 140 | js_newcfunction(J, jsB_quit, 1); 141 | js_setglobal(J, "quit"); 142 | 143 | js_dostring(J, require_js, 0); 144 | 145 | if (argc > 1) { 146 | for (i = 1; i < argc; ++i) { 147 | if (js_dofile(J, argv[i])) 148 | return 1; 149 | js_gc(J, 0); 150 | } 151 | } else { 152 | fputs(PS1, stdout); 153 | while (fgets(line, sizeof line, stdin)) { 154 | js_dostring(J, line, 1); 155 | fputs(PS1, stdout); 156 | } 157 | putchar('\n'); 158 | js_gc(J, 1); 159 | } 160 | 161 | js_freestate(J); 162 | 163 | return 0; 164 | } 165 | -------------------------------------------------------------------------------- /utf.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The authors of this software are Rob Pike and Ken Thompson. 3 | * Copyright (c) 2002 by Lucent Technologies. 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose without fee is hereby granted, provided that this entire notice 6 | * is included in all copies of any software which is or includes a copy 7 | * or modification of this software and in all copies of the supporting 8 | * documentation for such software. 9 | * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED 10 | * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE 11 | * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY 12 | * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. 13 | */ 14 | #include 15 | #include 16 | 17 | #include "utf.h" 18 | 19 | typedef unsigned char uchar; 20 | 21 | enum 22 | { 23 | Bit1 = 7, 24 | Bitx = 6, 25 | Bit2 = 5, 26 | Bit3 = 4, 27 | Bit4 = 3, 28 | 29 | T1 = ((1<<(Bit1+1))-1) ^ 0xFF, /* 0000 0000 */ 30 | Tx = ((1<<(Bitx+1))-1) ^ 0xFF, /* 1000 0000 */ 31 | T2 = ((1<<(Bit2+1))-1) ^ 0xFF, /* 1100 0000 */ 32 | T3 = ((1<<(Bit3+1))-1) ^ 0xFF, /* 1110 0000 */ 33 | T4 = ((1<<(Bit4+1))-1) ^ 0xFF, /* 1111 0000 */ 34 | 35 | Rune1 = (1<<(Bit1+0*Bitx))-1, /* 0000 0000 0111 1111 */ 36 | Rune2 = (1<<(Bit2+1*Bitx))-1, /* 0000 0111 1111 1111 */ 37 | Rune3 = (1<<(Bit3+2*Bitx))-1, /* 1111 1111 1111 1111 */ 38 | 39 | Maskx = (1< T1 54 | */ 55 | c = *(uchar*)str; 56 | if(c < Tx) { 57 | *rune = c; 58 | return 1; 59 | } 60 | 61 | /* 62 | * two character sequence 63 | * 0080-07FF => T2 Tx 64 | */ 65 | c1 = *(uchar*)(str+1) ^ Tx; 66 | if(c1 & Testx) 67 | goto bad; 68 | if(c < T3) { 69 | if(c < T2) 70 | goto bad; 71 | l = ((c << Bitx) | c1) & Rune2; 72 | if(l <= Rune1) 73 | goto bad; 74 | *rune = l; 75 | return 2; 76 | } 77 | 78 | /* 79 | * three character sequence 80 | * 0800-FFFF => T3 Tx Tx 81 | */ 82 | c2 = *(uchar*)(str+2) ^ Tx; 83 | if(c2 & Testx) 84 | goto bad; 85 | if(c < T4) { 86 | l = ((((c << Bitx) | c1) << Bitx) | c2) & Rune3; 87 | if(l <= Rune2) 88 | goto bad; 89 | *rune = l; 90 | return 3; 91 | } 92 | 93 | /* 94 | * bad decoding 95 | */ 96 | bad: 97 | *rune = Bad; 98 | return 1; 99 | } 100 | 101 | unsigned int 102 | runetochar(char *str, const Rune *rune) 103 | { 104 | unsigned int c; 105 | 106 | /* 107 | * one character sequence 108 | * 00000-0007F => 00-7F 109 | */ 110 | c = *rune; 111 | if(c <= Rune1) { 112 | str[0] = c; 113 | return 1; 114 | } 115 | 116 | /* 117 | * two character sequence 118 | * 0080-07FF => T2 Tx 119 | */ 120 | if(c <= Rune2) { 121 | str[0] = T2 | (c >> 1*Bitx); 122 | str[1] = Tx | (c & Maskx); 123 | return 2; 124 | } 125 | 126 | /* 127 | * three character sequence 128 | * 0800-FFFF => T3 Tx Tx 129 | */ 130 | str[0] = T3 | (c >> 2*Bitx); 131 | str[1] = Tx | ((c >> 1*Bitx) & Maskx); 132 | str[2] = Tx | (c & Maskx); 133 | return 3; 134 | } 135 | 136 | unsigned int 137 | runelen(int c) 138 | { 139 | Rune rune; 140 | char str[10]; 141 | 142 | rune = c; 143 | return runetochar(str, &rune); 144 | } 145 | 146 | unsigned int 147 | utflen(const char *s) 148 | { 149 | unsigned int c; 150 | unsigned int n; 151 | Rune rune; 152 | 153 | n = 0; 154 | for(;;) { 155 | c = *(uchar*)s; 156 | if(c < Runeself) { 157 | if(c == 0) 158 | return n; 159 | s++; 160 | } else 161 | s += chartorune(&rune, s); 162 | n++; 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /jscompile.h: -------------------------------------------------------------------------------- 1 | #ifndef js_compile_h 2 | #define js_compile_h 3 | 4 | enum js_OpCode 5 | { 6 | OP_POP, /* A -- */ 7 | OP_DUP, /* A -- A A */ 8 | OP_DUP2, /* A B -- A B A B */ 9 | OP_ROT2, /* A B -- B A */ 10 | OP_ROT3, /* A B C -- C A B */ 11 | OP_ROT4, /* A B C D -- D A B C */ 12 | 13 | OP_NUMBER_0, /* -- 0 */ 14 | OP_NUMBER_1, /* -- 1 */ 15 | OP_NUMBER_POS, /* -K- K */ 16 | OP_NUMBER_NEG, /* -K- -K */ 17 | 18 | OP_NUMBER, /* -N- */ 19 | OP_STRING, /* -S- */ 20 | OP_CLOSURE, /* -F- */ 21 | 22 | OP_NEWARRAY, 23 | OP_NEWOBJECT, 24 | OP_NEWREGEXP, 25 | 26 | OP_UNDEF, 27 | OP_NULL, 28 | OP_TRUE, 29 | OP_FALSE, 30 | 31 | OP_THIS, 32 | OP_GLOBAL, 33 | OP_CURRENT, /* currently executing function object */ 34 | 35 | OP_INITLOCAL, /* -K- */ 36 | OP_GETLOCAL, /* -K- */ 37 | OP_SETLOCAL, /* -K- */ 38 | OP_DELLOCAL, /* -K- false */ 39 | 40 | OP_INITVAR, /* -S- */ 41 | OP_DEFVAR, /* -S- */ 42 | OP_HASVAR, /* -S- ( | undefined ) */ 43 | OP_GETVAR, /* -S- */ 44 | OP_SETVAR, /* -S- */ 45 | OP_DELVAR, /* -S- */ 46 | 47 | OP_IN, /* -- */ 48 | 49 | OP_INITPROP, /* -- */ 50 | OP_INITPROP_N, /* -- */ 51 | OP_INITPROP_S, /* -- */ 52 | 53 | OP_INITGETTER, /* -- */ 54 | OP_INITSETTER, /* -- */ 55 | 56 | OP_GETPROP, /* -- */ 57 | OP_GETPROP_S, /* -S- */ 58 | OP_SETPROP, /* -- */ 59 | OP_SETPROP_S, /* -S- */ 60 | OP_DELPROP, /* -- */ 61 | OP_DELPROP_S, /* -S- */ 62 | 63 | OP_ITERATOR, /* -- */ 64 | OP_NEXTITER, /* -- ( true | false ) */ 65 | 66 | OP_CALL, /* -(numargs)- */ 67 | OP_NEW, /* -(numargs)- */ 68 | 69 | OP_TYPEOF, 70 | OP_POS, 71 | OP_NEG, 72 | OP_BITNOT, 73 | OP_LOGNOT, 74 | OP_INC, /* -- ToNumber(x)+1 */ 75 | OP_DEC, /* -- ToNumber(x)-1 */ 76 | OP_POSTINC, /* -- ToNumber(x)+1 ToNumber(x) */ 77 | OP_POSTDEC, /* -- ToNumber(x)-1 ToNumber(x) */ 78 | 79 | OP_MUL, 80 | OP_DIV, 81 | OP_MOD, 82 | OP_ADD, 83 | OP_SUB, 84 | OP_SHL, 85 | OP_SHR, 86 | OP_USHR, 87 | OP_LT, 88 | OP_GT, 89 | OP_LE, 90 | OP_GE, 91 | OP_EQ, 92 | OP_NE, 93 | OP_STRICTEQ, 94 | OP_STRICTNE, 95 | OP_JCASE, 96 | OP_BITAND, 97 | OP_BITXOR, 98 | OP_BITOR, 99 | 100 | OP_INSTANCEOF, 101 | 102 | OP_THROW, 103 | 104 | OP_TRY, /* -ADDR- /jump/ or -ADDR- */ 105 | OP_ENDTRY, 106 | 107 | OP_CATCH, /* push scope chain with exception variable */ 108 | OP_ENDCATCH, 109 | 110 | OP_WITH, 111 | OP_ENDWITH, 112 | 113 | OP_DEBUGGER, 114 | OP_JUMP, 115 | OP_JTRUE, 116 | OP_JFALSE, 117 | OP_RETURN, 118 | }; 119 | 120 | struct js_Function 121 | { 122 | const char *name; 123 | int script; 124 | int lightweight; 125 | unsigned int arguments; 126 | unsigned int numparams; 127 | 128 | js_Instruction *code; 129 | unsigned int codecap, codelen; 130 | 131 | js_Function **funtab; 132 | unsigned int funcap, funlen; 133 | 134 | double *numtab; 135 | unsigned int numcap, numlen; 136 | 137 | const char **strtab; 138 | unsigned int strcap, strlen; 139 | 140 | const char **vartab; 141 | unsigned int varcap, varlen; 142 | 143 | const char *filename; 144 | int line; 145 | 146 | js_Function *gcnext; 147 | int gcmark; 148 | }; 149 | 150 | js_Function *jsC_compilefunction(js_State *J, js_Ast *prog); 151 | js_Function *jsC_compile(js_State *J, js_Ast *prog); 152 | const char *jsC_opcodestring(enum js_OpCode opcode); 153 | void jsC_dumpfunction(js_State *J, js_Function *fun); 154 | 155 | #endif 156 | -------------------------------------------------------------------------------- /jsmath.c: -------------------------------------------------------------------------------- 1 | #include "jsi.h" 2 | #include "jsvalue.h" 3 | #include "jsbuiltin.h" 4 | 5 | static void Math_abs(js_State *J) 6 | { 7 | js_pushnumber(J, fabs(js_tonumber(J, 1))); 8 | } 9 | 10 | static void Math_acos(js_State *J) 11 | { 12 | js_pushnumber(J, acos(js_tonumber(J, 1))); 13 | } 14 | 15 | static void Math_asin(js_State *J) 16 | { 17 | js_pushnumber(J, asin(js_tonumber(J, 1))); 18 | } 19 | 20 | static void Math_atan(js_State *J) 21 | { 22 | js_pushnumber(J, atan(js_tonumber(J, 1))); 23 | } 24 | 25 | static void Math_atan2(js_State *J) 26 | { 27 | double y = js_tonumber(J, 1); 28 | double x = js_tonumber(J, 2); 29 | js_pushnumber(J, atan2(y, x)); 30 | } 31 | 32 | static void Math_ceil(js_State *J) 33 | { 34 | js_pushnumber(J, ceil(js_tonumber(J, 1))); 35 | } 36 | 37 | static void Math_cos(js_State *J) 38 | { 39 | js_pushnumber(J, cos(js_tonumber(J, 1))); 40 | } 41 | 42 | static void Math_exp(js_State *J) 43 | { 44 | js_pushnumber(J, exp(js_tonumber(J, 1))); 45 | } 46 | 47 | static void Math_floor(js_State *J) 48 | { 49 | js_pushnumber(J, floor(js_tonumber(J, 1))); 50 | } 51 | 52 | static void Math_log(js_State *J) 53 | { 54 | js_pushnumber(J, log(js_tonumber(J, 1))); 55 | } 56 | 57 | static void Math_pow(js_State *J) 58 | { 59 | double x = js_tonumber(J, 1); 60 | double y = js_tonumber(J, 2); 61 | if (!isfinite(y) && fabs(x) == 1) 62 | js_pushnumber(J, NAN); 63 | else 64 | js_pushnumber(J, pow(x,y)); 65 | } 66 | 67 | static void Math_random(js_State *J) 68 | { 69 | js_pushnumber(J, (double)rand() / (RAND_MAX - 1)); 70 | } 71 | 72 | static void Math_round(js_State *J) 73 | { 74 | double x = js_tonumber(J, 1); 75 | double r = round(x); 76 | if (r - x == -0.5) 77 | js_pushnumber(J, x == -0.5 ? -0.0 : r + 1.0); 78 | else 79 | js_pushnumber(J, r); 80 | } 81 | 82 | static void Math_sin(js_State *J) 83 | { 84 | js_pushnumber(J, sin(js_tonumber(J, 1))); 85 | } 86 | 87 | static void Math_sqrt(js_State *J) 88 | { 89 | js_pushnumber(J, sqrt(js_tonumber(J, 1))); 90 | } 91 | 92 | static void Math_tan(js_State *J) 93 | { 94 | js_pushnumber(J, tan(js_tonumber(J, 1))); 95 | } 96 | 97 | static void Math_max(js_State *J) 98 | { 99 | unsigned int i, n = js_gettop(J); 100 | double x = -INFINITY; 101 | for (i = 1; i < n; ++i) { 102 | double y = js_tonumber(J, i); 103 | if (isnan(y)) { 104 | x = y; 105 | break; 106 | } 107 | if (signbit(x) == signbit(y)) 108 | x = x > y ? x : y; 109 | else if (signbit(x)) 110 | x = y; 111 | } 112 | js_pushnumber(J, x); 113 | } 114 | 115 | static void Math_min(js_State *J) 116 | { 117 | unsigned int i, n = js_gettop(J); 118 | double x = INFINITY; 119 | for (i = 1; i < n; ++i) { 120 | double y = js_tonumber(J, i); 121 | if (isnan(y)) { 122 | x = y; 123 | break; 124 | } 125 | if (signbit(x) == signbit(y)) 126 | x = x < y ? x : y; 127 | else if (signbit(y)) 128 | x = y; 129 | } 130 | js_pushnumber(J, x); 131 | } 132 | 133 | void jsB_initmath(js_State *J) 134 | { 135 | js_pushobject(J, jsV_newobject(J, JS_CMATH, J->Object_prototype)); 136 | { 137 | jsB_propn(J, "E", 2.7182818284590452354); 138 | jsB_propn(J, "LN10", 2.302585092994046); 139 | jsB_propn(J, "LN2", 0.6931471805599453); 140 | jsB_propn(J, "LOG2E", 1.4426950408889634); 141 | jsB_propn(J, "LOG10E", 0.4342944819032518); 142 | jsB_propn(J, "PI", 3.1415926535897932); 143 | jsB_propn(J, "SQRT1_2", 0.7071067811865476); 144 | jsB_propn(J, "SQRT2", 1.4142135623730951); 145 | 146 | jsB_propf(J, "abs", Math_abs, 1); 147 | jsB_propf(J, "acos", Math_acos, 1); 148 | jsB_propf(J, "asin", Math_asin, 1); 149 | jsB_propf(J, "atan", Math_atan, 1); 150 | jsB_propf(J, "atan2", Math_atan2, 2); 151 | jsB_propf(J, "ceil", Math_ceil, 1); 152 | jsB_propf(J, "cos", Math_cos, 1); 153 | jsB_propf(J, "exp", Math_exp, 1); 154 | jsB_propf(J, "floor", Math_floor, 1); 155 | jsB_propf(J, "log", Math_log, 1); 156 | jsB_propf(J, "max", Math_max, 0); 157 | jsB_propf(J, "min", Math_min, 0); 158 | jsB_propf(J, "pow", Math_pow, 2); 159 | jsB_propf(J, "random", Math_random, 0); 160 | jsB_propf(J, "round", Math_round, 1); 161 | jsB_propf(J, "sin", Math_sin, 1); 162 | jsB_propf(J, "sqrt", Math_sqrt, 1); 163 | jsB_propf(J, "tan", Math_tan, 1); 164 | } 165 | js_defglobal(J, "Math", JS_DONTENUM); 166 | } 167 | -------------------------------------------------------------------------------- /jsvalue.h: -------------------------------------------------------------------------------- 1 | #ifndef js_object_h 2 | #define js_object_h 3 | 4 | typedef struct js_Property js_Property; 5 | typedef struct js_Iterator js_Iterator; 6 | 7 | /* Hint to ToPrimitive() */ 8 | enum { 9 | JS_HNONE, 10 | JS_HNUMBER, 11 | JS_HSTRING 12 | }; 13 | 14 | enum js_Type { 15 | JS_TUNDEFINED, 16 | JS_TNULL, 17 | JS_TBOOLEAN, 18 | JS_TNUMBER, 19 | JS_TSTRING, 20 | JS_TOBJECT, 21 | }; 22 | 23 | enum js_Class { 24 | JS_COBJECT, 25 | JS_CARRAY, 26 | JS_CFUNCTION, 27 | JS_CSCRIPT, /* function created from global/eval code */ 28 | JS_CCFUNCTION, /* built-in function */ 29 | JS_CERROR, 30 | JS_CBOOLEAN, 31 | JS_CNUMBER, 32 | JS_CSTRING, 33 | JS_CREGEXP, 34 | JS_CDATE, 35 | JS_CMATH, 36 | JS_CJSON, 37 | JS_CITERATOR, 38 | JS_CUSERDATA, 39 | }; 40 | 41 | struct js_Value 42 | { 43 | enum js_Type type; 44 | union { 45 | int boolean; 46 | double number; 47 | const char *string; 48 | js_Object *object; 49 | } u; 50 | }; 51 | 52 | struct js_Regexp 53 | { 54 | void *prog; 55 | const char *source; 56 | unsigned short flags; 57 | unsigned short last; 58 | }; 59 | 60 | struct js_Object 61 | { 62 | enum js_Class type; 63 | int extensible; 64 | js_Property *properties; 65 | js_Property *head, *tail; /* for enumeration */ 66 | js_Object *prototype; 67 | union { 68 | int boolean; 69 | double number; 70 | struct { 71 | const char *string; 72 | unsigned int length; 73 | } s; 74 | struct { 75 | unsigned int length; 76 | } a; 77 | struct { 78 | js_Function *function; 79 | js_Environment *scope; 80 | } f; 81 | struct { 82 | js_CFunction function; 83 | js_CFunction constructor; 84 | unsigned int length; 85 | } c; 86 | js_Regexp r; 87 | struct { 88 | js_Object *target; 89 | js_Iterator *head; 90 | } iter; 91 | struct { 92 | const char *tag; 93 | void *data; 94 | } user; 95 | } u; 96 | js_Object *gcnext; 97 | int gcmark; 98 | }; 99 | 100 | struct js_Property 101 | { 102 | const char *name; 103 | js_Property *left, *right; 104 | js_Property **prevp, *next; /* for enumeration */ 105 | int level; 106 | int atts; 107 | js_Value value; 108 | js_Object *getter; 109 | js_Object *setter; 110 | }; 111 | 112 | struct js_Iterator 113 | { 114 | const char *name; 115 | js_Iterator *next; 116 | }; 117 | 118 | /* jsrun.c */ 119 | js_Value js_tovalue(js_State *J, int idx); 120 | js_Value js_toprimitive(js_State *J, int idx, int hint); 121 | js_Object *js_toobject(js_State *J, int idx); 122 | void js_pushvalue(js_State *J, js_Value v); 123 | void js_pushobject(js_State *J, js_Object *v); 124 | 125 | /* jsvalue.c */ 126 | int jsV_toboolean(js_State *J, const js_Value *v); 127 | double jsV_tonumber(js_State *J, const js_Value *v); 128 | double jsV_tointeger(js_State *J, const js_Value *v); 129 | const char *jsV_tostring(js_State *J, const js_Value *v); 130 | js_Object *jsV_toobject(js_State *J, const js_Value *v); 131 | js_Value jsV_toprimitive(js_State *J, const js_Value *v, int preferred); 132 | 133 | double js_stringtofloat(const char *s, char **ep); 134 | double jsV_numbertointeger(double n); 135 | int jsV_numbertoint32(double n); 136 | unsigned int jsV_numbertouint32(double n); 137 | short jsV_numbertoint16(double n); 138 | unsigned short jsV_numbertouint16(double n); 139 | const char *jsV_numbertostring(js_State *J, double number); 140 | double jsV_stringtonumber(js_State *J, const char *string); 141 | 142 | /* jsproperty.c */ 143 | js_Object *jsV_newobject(js_State *J, enum js_Class type, js_Object *prototype); 144 | js_Property *jsV_getownproperty(js_State *J, js_Object *obj, const char *name); 145 | js_Property *jsV_getpropertyx(js_State *J, js_Object *obj, const char *name, int *own); 146 | js_Property *jsV_getproperty(js_State *J, js_Object *obj, const char *name); 147 | js_Property *jsV_setproperty(js_State *J, js_Object *obj, const char *name); 148 | js_Property *jsV_nextproperty(js_State *J, js_Object *obj, const char *name); 149 | void jsV_delproperty(js_State *J, js_Object *obj, const char *name); 150 | 151 | js_Object *jsV_newiterator(js_State *J, js_Object *obj, int own); 152 | const char *jsV_nextiterator(js_State *J, js_Object *iter); 153 | 154 | void jsV_resizearray(js_State *J, js_Object *obj, unsigned int newlen); 155 | 156 | /* jsdump.c */ 157 | void js_dumpobject(js_State *J, js_Object *obj); 158 | void js_dumpvalue(js_State *J, js_Value v); 159 | 160 | #endif 161 | -------------------------------------------------------------------------------- /jsstate.c: -------------------------------------------------------------------------------- 1 | #include "jsi.h" 2 | #include "jsparse.h" 3 | #include "jscompile.h" 4 | #include "jsvalue.h" 5 | #include "jsrun.h" 6 | #include "jsbuiltin.h" 7 | 8 | static void *js_defaultalloc(void *actx, void *ptr, unsigned int size) 9 | { 10 | if (size == 0) { 11 | free(ptr); 12 | return NULL; 13 | } 14 | if (!ptr) 15 | return malloc(size); 16 | return realloc(ptr, size); 17 | } 18 | 19 | static void js_defaultpanic(js_State *J) 20 | { 21 | fprintf(stderr, "mujs: uncaught exception: %s\n", js_tostring(J, -1)); 22 | /* return to javascript to abort */ 23 | } 24 | 25 | int js_ploadstring(js_State *J, const char *filename, const char *source) 26 | { 27 | if (js_try(J)) 28 | return 1; 29 | js_loadstring(J, filename, source); 30 | js_endtry(J); 31 | return 0; 32 | } 33 | 34 | int js_ploadfile(js_State *J, const char *filename) 35 | { 36 | if (js_try(J)) 37 | return 1; 38 | js_loadfile(J, filename); 39 | js_endtry(J); 40 | return 0; 41 | } 42 | 43 | static void js_loadstringx(js_State *J, const char *filename, const char *source, int iseval) 44 | { 45 | js_Ast *P; 46 | js_Function *F; 47 | 48 | if (js_try(J)) { 49 | jsP_freeparse(J); 50 | js_throw(J); 51 | } 52 | 53 | P = jsP_parse(J, filename, source); 54 | F = jsC_compile(J, P); 55 | jsP_freeparse(J); 56 | js_newscript(J, F, iseval ? NULL : J->GE); 57 | 58 | js_endtry(J); 59 | } 60 | 61 | void js_loadeval(js_State *J, const char *filename, const char *source) 62 | { 63 | js_loadstringx(J, filename, source, 1); 64 | } 65 | 66 | void js_loadstring(js_State *J, const char *filename, const char *source) 67 | { 68 | js_loadstringx(J, filename, source, 0); 69 | } 70 | 71 | void js_loadfile(js_State *J, const char *filename) 72 | { 73 | FILE *f; 74 | char *s; 75 | int n, t; 76 | 77 | f = fopen(filename, "rb"); 78 | if (!f) { 79 | js_error(J, "cannot open file: '%s'", filename); 80 | } 81 | 82 | if (fseek(f, 0, SEEK_END) < 0) { 83 | fclose(f); 84 | js_error(J, "cannot seek in file: '%s'", filename); 85 | } 86 | n = ftell(f); 87 | fseek(f, 0, SEEK_SET); 88 | 89 | s = js_malloc(J, n + 1); /* add space for string terminator */ 90 | if (!s) { 91 | fclose(f); 92 | js_error(J, "cannot allocate storage for file contents: '%s'", filename); 93 | } 94 | 95 | t = fread(s, 1, n, f); 96 | if (t != n) { 97 | js_free(J, s); 98 | fclose(f); 99 | js_error(J, "cannot read data from file: '%s'", filename); 100 | } 101 | 102 | s[n] = 0; /* zero-terminate string containing file data */ 103 | 104 | if (js_try(J)) { 105 | js_free(J, s); 106 | fclose(f); 107 | js_throw(J); 108 | } 109 | 110 | js_loadstring(J, filename, s); 111 | 112 | js_free(J, s); 113 | fclose(f); 114 | js_endtry(J); 115 | } 116 | 117 | int js_dostring(js_State *J, const char *source, int report) 118 | { 119 | if (js_try(J)) { 120 | fprintf(stderr, "mujs: %s\n", js_tostring(J, -1)); 121 | js_pop(J, 1); 122 | return 1; 123 | } 124 | js_loadstring(J, "(string)", source); 125 | js_pushglobal(J); 126 | js_call(J, 0); 127 | if (report) 128 | if (js_isdefined(J, -1)) 129 | printf("%s\n", js_tostring(J, -1)); 130 | js_pop(J, 1); 131 | js_endtry(J); 132 | return 0; 133 | } 134 | 135 | int js_dofile(js_State *J, const char *filename) 136 | { 137 | if (js_try(J)) { 138 | fprintf(stderr, "mujs: %s\n", js_tostring(J, -1)); 139 | js_pop(J, 1); 140 | return 1; 141 | } 142 | js_loadfile(J, filename); 143 | js_pushglobal(J); 144 | js_call(J, 0); 145 | js_pop(J, 1); 146 | js_endtry(J); 147 | return 0; 148 | } 149 | 150 | js_Panic js_atpanic(js_State *J, js_Panic panic) 151 | { 152 | js_Panic old = J->panic; 153 | J->panic = panic; 154 | return old; 155 | } 156 | 157 | js_State *js_newstate(js_Alloc alloc, void *actx) 158 | { 159 | js_State *J; 160 | 161 | if (!alloc) 162 | alloc = js_defaultalloc; 163 | 164 | J = alloc(actx, NULL, sizeof *J); 165 | if (!J) 166 | return NULL; 167 | memset(J, 0, sizeof(*J)); 168 | J->actx = actx; 169 | J->alloc = alloc; 170 | 171 | J->panic = js_defaultpanic; 172 | 173 | J->stack = alloc(actx, NULL, JS_STACKSIZE * sizeof *J->stack); 174 | if (!J->stack) { 175 | alloc(actx, NULL, 0); 176 | return NULL; 177 | } 178 | 179 | J->gcmark = 1; 180 | J->nextref = 0; 181 | 182 | J->R = jsV_newobject(J, JS_COBJECT, NULL); 183 | J->G = jsV_newobject(J, JS_COBJECT, NULL); 184 | J->E = jsR_newenvironment(J, J->G, NULL); 185 | J->GE = J->E; 186 | 187 | jsB_init(J); 188 | 189 | return J; 190 | } 191 | -------------------------------------------------------------------------------- /jsregexp.c: -------------------------------------------------------------------------------- 1 | #include "jsi.h" 2 | #include "jsvalue.h" 3 | #include "jsbuiltin.h" 4 | #include "regex.h" 5 | 6 | void js_newregexp(js_State *J, const char *pattern, int flags) 7 | { 8 | const char *error; 9 | js_Object *obj; 10 | Reprog *prog; 11 | int opts; 12 | 13 | obj = jsV_newobject(J, JS_CREGEXP, J->RegExp_prototype); 14 | 15 | opts = 0; 16 | if (flags & JS_REGEXP_I) opts |= REG_ICASE; 17 | if (flags & JS_REGEXP_M) opts |= REG_NEWLINE; 18 | 19 | prog = js_regcomp(pattern, opts, &error); 20 | if (!prog) 21 | js_syntaxerror(J, "regular expression: %s", error); 22 | 23 | obj->u.r.prog = prog; 24 | obj->u.r.source = pattern; 25 | obj->u.r.flags = flags; 26 | obj->u.r.last = 0; 27 | js_pushobject(J, obj); 28 | } 29 | 30 | void js_RegExp_prototype_exec(js_State *J, js_Regexp *re, const char *text) 31 | { 32 | unsigned int i; 33 | int opts; 34 | Resub m; 35 | 36 | opts = 0; 37 | if (re->flags & JS_REGEXP_G) { 38 | if (re->last > strlen(text)) { 39 | re->last = 0; 40 | js_pushnull(J); 41 | return; 42 | } 43 | if (re->last > 0) { 44 | text += re->last; 45 | opts |= REG_NOTBOL; 46 | } 47 | } 48 | 49 | if (!js_regexec(re->prog, text, &m, opts)) { 50 | js_newarray(J); 51 | js_pushstring(J, text); 52 | js_setproperty(J, -2, "input"); 53 | js_pushnumber(J, js_utfptrtoidx(text, m.sub[0].sp)); 54 | js_setproperty(J, -2, "index"); 55 | for (i = 0; i < m.nsub; ++i) { 56 | js_pushlstring(J, m.sub[i].sp, m.sub[i].ep - m.sub[i].sp); 57 | js_setindex(J, -2, i); 58 | } 59 | if (re->flags & JS_REGEXP_G) 60 | re->last = re->last + (m.sub[0].ep - text); 61 | return; 62 | } 63 | 64 | if (re->flags & JS_REGEXP_G) 65 | re->last = 0; 66 | 67 | js_pushnull(J); 68 | } 69 | 70 | static void Rp_test(js_State *J) 71 | { 72 | js_Regexp *re; 73 | const char *text; 74 | int opts; 75 | Resub m; 76 | 77 | re = js_toregexp(J, 0); 78 | text = js_tostring(J, 1); 79 | 80 | opts = 0; 81 | if (re->flags & JS_REGEXP_G) { 82 | if (re->last > strlen(text)) { 83 | re->last = 0; 84 | js_pushboolean(J, 0); 85 | return; 86 | } 87 | if (re->last > 0) { 88 | text += re->last; 89 | opts |= REG_NOTBOL; 90 | } 91 | } 92 | 93 | if (!js_regexec(re->prog, text, &m, opts)) { 94 | if (re->flags & JS_REGEXP_G) 95 | re->last = re->last + (m.sub[0].ep - text); 96 | js_pushboolean(J, 1); 97 | return; 98 | } 99 | 100 | if (re->flags & JS_REGEXP_G) 101 | re->last = 0; 102 | 103 | js_pushboolean(J, 0); 104 | } 105 | 106 | static void jsB_new_RegExp(js_State *J) 107 | { 108 | js_Regexp *old; 109 | const char *pattern; 110 | int flags; 111 | 112 | if (js_isregexp(J, 1)) { 113 | if (js_isdefined(J, 2)) 114 | js_typeerror(J, "cannot supply flags when creating one RegExp from another"); 115 | old = js_toregexp(J, 1); 116 | pattern = old->source; 117 | flags = old->flags; 118 | } else if (js_isundefined(J, 1)) { 119 | pattern = ""; 120 | flags = 0; 121 | } else { 122 | pattern = js_tostring(J, 1); 123 | flags = 0; 124 | } 125 | 126 | if (js_isdefined(J, 2)) { 127 | const char *s = js_tostring(J, 2); 128 | int g = 0, i = 0, m = 0; 129 | while (*s) { 130 | if (*s == 'g') ++g; 131 | else if (*s == 'i') ++i; 132 | else if (*s == 'm') ++m; 133 | else js_syntaxerror(J, "invalid regular expression flag: '%c'", *s); 134 | ++s; 135 | } 136 | if (g > 1) js_syntaxerror(J, "invalid regular expression flag: 'g'"); 137 | if (i > 1) js_syntaxerror(J, "invalid regular expression flag: 'i'"); 138 | if (m > 1) js_syntaxerror(J, "invalid regular expression flag: 'm'"); 139 | if (g) flags |= JS_REGEXP_G; 140 | if (i) flags |= JS_REGEXP_I; 141 | if (m) flags |= JS_REGEXP_M; 142 | } 143 | 144 | js_newregexp(J, pattern, flags); 145 | } 146 | 147 | static void jsB_RegExp(js_State *J) 148 | { 149 | if (js_gettop(J) == 2 && js_isregexp(J, 1)) 150 | return; 151 | jsB_new_RegExp(J); 152 | } 153 | 154 | static void Rp_toString(js_State *J) 155 | { 156 | js_Regexp *re; 157 | char *out; 158 | 159 | re = js_toregexp(J, 0); 160 | 161 | out = js_malloc(J, strlen(re->source) + 6); /* extra space for //gim */ 162 | strcpy(out, "/"); 163 | strcat(out, re->source); 164 | strcat(out, "/"); 165 | if (re->flags & JS_REGEXP_G) strcat(out, "g"); 166 | if (re->flags & JS_REGEXP_I) strcat(out, "i"); 167 | if (re->flags & JS_REGEXP_M) strcat(out, "m"); 168 | 169 | if (js_try(J)) { 170 | js_free(J, out); 171 | js_throw(J); 172 | } 173 | js_pop(J, 0); 174 | js_pushstring(J, out); 175 | js_endtry(J); 176 | js_free(J, out); 177 | } 178 | 179 | static void Rp_exec(js_State *J) 180 | { 181 | js_RegExp_prototype_exec(J, js_toregexp(J, 0), js_tostring(J, 1)); 182 | } 183 | 184 | void jsB_initregexp(js_State *J) 185 | { 186 | js_pushobject(J, J->RegExp_prototype); 187 | { 188 | jsB_propf(J, "toString", Rp_toString, 0); 189 | jsB_propf(J, "test", Rp_test, 0); 190 | jsB_propf(J, "exec", Rp_exec, 0); 191 | } 192 | js_newcconstructor(J, jsB_RegExp, jsB_new_RegExp, 1); 193 | js_defglobal(J, "RegExp", JS_DONTENUM); 194 | } 195 | -------------------------------------------------------------------------------- /jsfunction.c: -------------------------------------------------------------------------------- 1 | #include "jsi.h" 2 | #include "jsparse.h" 3 | #include "jscompile.h" 4 | #include "jsvalue.h" 5 | #include "jsbuiltin.h" 6 | 7 | static void jsB_Function(js_State *J) 8 | { 9 | unsigned int i, top = js_gettop(J); 10 | js_Buffer *sb = NULL; 11 | const char *body; 12 | js_Ast *parse; 13 | js_Function *fun; 14 | 15 | /* p1, p2, ..., pn */ 16 | if (top > 2) { 17 | for (i = 1; i < top - 1; ++i) { 18 | if (i > 1) 19 | js_putc(J, &sb, ','); 20 | js_puts(J, &sb, js_tostring(J, i)); 21 | } 22 | js_putc(J, &sb, ')'); 23 | } 24 | 25 | /* body */ 26 | body = js_isdefined(J, top - 1) ? js_tostring(J, top - 1) : ""; 27 | 28 | if (js_try(J)) { 29 | js_free(J, sb); 30 | jsP_freeparse(J); 31 | js_throw(J); 32 | } 33 | 34 | parse = jsP_parsefunction(J, "Function", sb ? sb->s : NULL, body); 35 | fun = jsC_compilefunction(J, parse); 36 | 37 | js_endtry(J); 38 | js_free(J, sb); 39 | jsP_freeparse(J); 40 | 41 | js_newfunction(J, fun, J->GE); 42 | } 43 | 44 | static void jsB_Function_prototype(js_State *J) 45 | { 46 | js_pushundefined(J); 47 | } 48 | 49 | static void Fp_toString(js_State *J) 50 | { 51 | js_Object *self = js_toobject(J, 0); 52 | char *s; 53 | unsigned int i, n; 54 | 55 | if (!js_iscallable(J, 0)) 56 | js_typeerror(J, "not a function"); 57 | 58 | if (self->type == JS_CFUNCTION || self->type == JS_CSCRIPT) { 59 | js_Function *F = self->u.f.function; 60 | n = strlen("function () { ... }"); 61 | n += strlen(F->name); 62 | for (i = 0; i < F->numparams; ++i) 63 | n += strlen(F->vartab[i]) + 1; 64 | s = js_malloc(J, n); 65 | strcpy(s, "function "); 66 | strcat(s, F->name); 67 | strcat(s, "("); 68 | for (i = 0; i < F->numparams; ++i) { 69 | if (i > 0) strcat(s, ","); 70 | strcat(s, F->vartab[i]); 71 | } 72 | strcat(s, ") { ... }"); 73 | if (js_try(J)) { 74 | js_free(J, s); 75 | js_throw(J); 76 | } 77 | js_pushstring(J, s); 78 | js_free(J, s); 79 | } else { 80 | js_pushliteral(J, "function () { ... }"); 81 | } 82 | } 83 | 84 | static void Fp_apply(js_State *J) 85 | { 86 | int i, n; 87 | 88 | if (!js_iscallable(J, 0)) 89 | js_typeerror(J, "not a function"); 90 | 91 | js_copy(J, 0); 92 | js_copy(J, 1); 93 | 94 | n = js_getlength(J, 2); 95 | for (i = 0; i < n; ++i) 96 | js_getindex(J, 2, i); 97 | 98 | js_call(J, n); 99 | } 100 | 101 | static void Fp_call(js_State *J) 102 | { 103 | unsigned int i, top = js_gettop(J); 104 | 105 | if (!js_iscallable(J, 0)) 106 | js_typeerror(J, "not a function"); 107 | 108 | for (i = 0; i < top; ++i) 109 | js_copy(J, i); 110 | 111 | js_call(J, top - 2); 112 | } 113 | 114 | static void callbound(js_State *J) 115 | { 116 | unsigned int top = js_gettop(J); 117 | unsigned int i, fun, args, n; 118 | 119 | fun = js_gettop(J); 120 | js_currentfunction(J); 121 | js_getproperty(J, fun, "__TargetFunction__"); 122 | js_getproperty(J, fun, "__BoundThis__"); 123 | 124 | args = js_gettop(J); 125 | js_getproperty(J, fun, "__BoundArguments__"); 126 | n = js_getlength(J, args); 127 | for (i = 0; i < n; ++i) 128 | js_getindex(J, args, i); 129 | js_remove(J, args); 130 | 131 | for (i = 1; i < top; ++i) 132 | js_copy(J, i); 133 | 134 | js_call(J, n + top - 1); 135 | } 136 | 137 | static void constructbound(js_State *J) 138 | { 139 | unsigned int top = js_gettop(J); 140 | unsigned int i, fun, args, n; 141 | 142 | fun = js_gettop(J); 143 | js_currentfunction(J); 144 | js_getproperty(J, fun, "__TargetFunction__"); 145 | 146 | args = js_gettop(J); 147 | js_getproperty(J, fun, "__BoundArguments__"); 148 | n = js_getlength(J, args); 149 | for (i = 0; i < n; ++i) 150 | js_getindex(J, args, i); 151 | js_remove(J, args); 152 | 153 | for (i = 1; i < top; ++i) 154 | js_copy(J, i); 155 | 156 | js_construct(J, n + top - 1); 157 | } 158 | 159 | static void Fp_bind(js_State *J) 160 | { 161 | unsigned int i, top = js_gettop(J); 162 | unsigned int n; 163 | 164 | if (!js_iscallable(J, 0)) 165 | js_typeerror(J, "not a function"); 166 | 167 | n = js_getlength(J, 0); 168 | if (n > top - 2) 169 | n -= top - 2; 170 | else 171 | n = 0; 172 | 173 | js_newcconstructor(J, callbound, constructbound, n); 174 | 175 | /* Reuse target function's prototype for HasInstance check. */ 176 | js_getproperty(J, 0, "prototype"); 177 | js_defproperty(J, -2, "prototype", JS_READONLY | JS_DONTENUM | JS_DONTCONF); 178 | 179 | /* target function */ 180 | js_copy(J, 0); 181 | js_defproperty(J, -2, "__TargetFunction__", JS_READONLY | JS_DONTENUM | JS_DONTCONF); 182 | 183 | /* bound this */ 184 | js_copy(J, 1); 185 | js_defproperty(J, -2, "__BoundThis__", JS_READONLY | JS_DONTENUM | JS_DONTCONF); 186 | 187 | /* bound arguments */ 188 | js_newarray(J); 189 | for (i = 2; i < top; ++i) { 190 | js_copy(J, i); 191 | js_setindex(J, -2, i - 2); 192 | } 193 | js_defproperty(J, -2, "__BoundArguments__", JS_READONLY | JS_DONTENUM | JS_DONTCONF); 194 | } 195 | 196 | void jsB_initfunction(js_State *J) 197 | { 198 | J->Function_prototype->u.c.function = jsB_Function_prototype; 199 | J->Function_prototype->u.c.constructor = NULL; 200 | 201 | js_pushobject(J, J->Function_prototype); 202 | { 203 | jsB_propf(J, "toString", Fp_toString, 2); 204 | jsB_propf(J, "apply", Fp_apply, 2); 205 | jsB_propf(J, "call", Fp_call, 1); 206 | jsB_propf(J, "bind", Fp_bind, 1); 207 | } 208 | js_newcconstructor(J, jsB_Function, jsB_Function, 1); 209 | js_defglobal(J, "Function", JS_DONTENUM); 210 | } 211 | -------------------------------------------------------------------------------- /jsi.h: -------------------------------------------------------------------------------- 1 | #ifndef jsi_h 2 | #define jsi_h 3 | 4 | #include "mujs.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | /* Microsoft Visual C */ 16 | #ifdef _MSC_VER 17 | #pragma warning(disable:4996) /* _CRT_SECURE_NO_WARNINGS */ 18 | #pragma warning(disable:4244) /* implicit conversion from double to int */ 19 | #pragma warning(disable:4267) /* implicit conversion of int to smaller int */ 20 | #define inline __inline 21 | #define snprintf _snprintf 22 | #define vsnprintf _vsnprintf 23 | #if _MSC_VER < 1800 24 | #define round(x) floor((x) < 0 ? (x) - 0.5 : (x) + 0.5) 25 | #define isnan(x) _isnan(x) 26 | #define isinf(x) (!_finite(x)) 27 | #define isfinite(x) _finite(x) 28 | static __inline int signbit(double x) {union{double d;__int64 i;}u;u.d=x;return u.i>>63;} 29 | #define INFINITY (DBL_MAX+DBL_MAX) 30 | #define NAN (INFINITY-INFINITY) 31 | #endif /* old MSVC */ 32 | #endif 33 | 34 | #define nelem(a) (sizeof (a) / sizeof (a)[0]) 35 | 36 | void *js_malloc(js_State *J, unsigned int size); 37 | void *js_realloc(js_State *J, void *ptr, unsigned int size); 38 | void js_free(js_State *J, void *ptr); 39 | 40 | typedef struct js_Regexp js_Regexp; 41 | typedef struct js_Value js_Value; 42 | typedef struct js_Object js_Object; 43 | typedef struct js_Ast js_Ast; 44 | typedef struct js_Function js_Function; 45 | typedef struct js_Environment js_Environment; 46 | typedef struct js_StringNode js_StringNode; 47 | typedef struct js_Jumpbuf js_Jumpbuf; 48 | 49 | /* Limits */ 50 | 51 | #define JS_STACKSIZE 256 /* value stack size */ 52 | #define JS_ENVLIMIT 64 /* environment stack size */ 53 | #define JS_TRYLIMIT 64 /* exception stack size */ 54 | #define JS_GCLIMIT 10000 /* run gc cycle every N allocations */ 55 | 56 | /* instruction size -- change to unsigned int if you get integer overflow syntax errors */ 57 | typedef unsigned short js_Instruction; 58 | 59 | /* String interning */ 60 | 61 | const char *js_intern(js_State *J, const char *s); 62 | void jsS_dumpstrings(js_State *J); 63 | void jsS_freestrings(js_State *J); 64 | 65 | /* Portable strtod and printf float formatting */ 66 | 67 | void js_fmtexp(char *p, int e); 68 | void js_dtoa(double f, char *digits, int *exp, int *neg, int *ndigits); 69 | double js_strtod(const char *as, char **aas); 70 | 71 | /* Private stack functions */ 72 | 73 | void js_newfunction(js_State *J, js_Function *function, js_Environment *scope); 74 | void js_newscript(js_State *J, js_Function *function, js_Environment *scope); 75 | void js_loadeval(js_State *J, const char *filename, const char *source); 76 | 77 | js_Regexp *js_toregexp(js_State *J, int idx); 78 | int js_isarrayindex(js_State *J, const char *str, unsigned int *idx); 79 | int js_runeat(js_State *J, const char *s, int i); 80 | int js_utfptrtoidx(const char *s, const char *p); 81 | const char *js_utfidxtoptr(const char *s, int i); 82 | 83 | void js_dup(js_State *J); 84 | void js_dup2(js_State *J); 85 | void js_rot2(js_State *J); 86 | void js_rot3(js_State *J); 87 | void js_rot2pop1(js_State *J); 88 | void js_rot3pop2(js_State *J); 89 | void js_dup1rot3(js_State *J); 90 | void js_dup1rot4(js_State *J); 91 | 92 | void js_RegExp_prototype_exec(js_State *J, js_Regexp *re, const char *text); 93 | 94 | /* Exception handling */ 95 | 96 | struct js_Jumpbuf 97 | { 98 | jmp_buf buf; 99 | js_Environment *E; 100 | int envtop; 101 | int top, bot; 102 | js_Instruction *pc; 103 | }; 104 | 105 | void js_savetry(js_State *J, js_Instruction *pc); 106 | 107 | #define js_trypc(J, PC) \ 108 | (js_savetry(J, PC), setjmp(J->trybuf[J->trylen++].buf)) 109 | 110 | #define js_try(J) \ 111 | (js_savetry(J, NULL), setjmp(J->trybuf[J->trylen++].buf)) 112 | 113 | #define js_endtry(J) \ 114 | (--J->trylen) 115 | 116 | /* State struct */ 117 | 118 | struct js_State 119 | { 120 | void *actx; 121 | js_Alloc alloc; 122 | js_Panic panic; 123 | 124 | js_StringNode *strings; 125 | 126 | /* parser input source */ 127 | const char *filename; 128 | const char *source; 129 | int line; 130 | 131 | /* lexer state */ 132 | struct { char *text; unsigned int len, cap; } lexbuf; 133 | int lexline; 134 | int lexchar; 135 | int lasttoken; 136 | int newline; 137 | 138 | /* parser state */ 139 | int lookahead; 140 | const char *text; 141 | double number; 142 | js_Ast *gcast; /* list of allocated nodes to free after parsing */ 143 | 144 | /* compiler state */ 145 | int strict; 146 | 147 | /* runtime environment */ 148 | js_Object *Object_prototype; 149 | js_Object *Array_prototype; 150 | js_Object *Function_prototype; 151 | js_Object *Boolean_prototype; 152 | js_Object *Number_prototype; 153 | js_Object *String_prototype; 154 | js_Object *RegExp_prototype; 155 | js_Object *Date_prototype; 156 | 157 | js_Object *Error_prototype; 158 | js_Object *EvalError_prototype; 159 | js_Object *RangeError_prototype; 160 | js_Object *ReferenceError_prototype; 161 | js_Object *SyntaxError_prototype; 162 | js_Object *TypeError_prototype; 163 | js_Object *URIError_prototype; 164 | 165 | int nextref; /* for js_ref use */ 166 | js_Object *R; /* registry of hidden values */ 167 | js_Object *G; /* the global object */ 168 | js_Environment *E; /* current environment scope */ 169 | js_Environment *GE; /* global environment scope (at the root) */ 170 | 171 | /* execution stack */ 172 | int top, bot; 173 | js_Value *stack; 174 | 175 | /* garbage collector list */ 176 | int gcmark; 177 | int gccounter; 178 | js_Environment *gcenv; 179 | js_Function *gcfun; 180 | js_Object *gcobj; 181 | 182 | /* environments on the call stack but currently not in scope */ 183 | int envtop; 184 | js_Environment *envstack[JS_ENVLIMIT]; 185 | 186 | /* exception stack */ 187 | int trylen; 188 | js_Jumpbuf trybuf[JS_TRYLIMIT]; 189 | }; 190 | 191 | #endif 192 | -------------------------------------------------------------------------------- /jsgc.c: -------------------------------------------------------------------------------- 1 | #include "jsi.h" 2 | #include "jscompile.h" 3 | #include "jsvalue.h" 4 | #include "jsrun.h" 5 | 6 | #include "regex.h" 7 | 8 | static void jsG_markobject(js_State *J, int mark, js_Object *obj); 9 | 10 | static void jsG_freeenvironment(js_State *J, js_Environment *env) 11 | { 12 | js_free(J, env); 13 | } 14 | 15 | static void jsG_freefunction(js_State *J, js_Function *fun) 16 | { 17 | js_free(J, fun->funtab); 18 | js_free(J, fun->numtab); 19 | js_free(J, fun->strtab); 20 | js_free(J, fun->vartab); 21 | js_free(J, fun->code); 22 | js_free(J, fun); 23 | } 24 | 25 | static void jsG_freeproperty(js_State *J, js_Property *node) 26 | { 27 | while (node) { 28 | js_Property *next = node->next; 29 | js_free(J, node); 30 | node = next; 31 | } 32 | } 33 | 34 | static void jsG_freeiterator(js_State *J, js_Iterator *node) 35 | { 36 | while (node) { 37 | js_Iterator *next = node->next; 38 | js_free(J, node); 39 | node = next; 40 | } 41 | } 42 | 43 | static void jsG_freeobject(js_State *J, js_Object *obj) 44 | { 45 | if (obj->head) 46 | jsG_freeproperty(J, obj->head); 47 | if (obj->type == JS_CREGEXP) 48 | js_regfree(obj->u.r.prog); 49 | if (obj->type == JS_CITERATOR) 50 | jsG_freeiterator(J, obj->u.iter.head); 51 | js_free(J, obj); 52 | } 53 | 54 | static void jsG_markfunction(js_State *J, int mark, js_Function *fun) 55 | { 56 | unsigned int i; 57 | fun->gcmark = mark; 58 | for (i = 0; i < fun->funlen; ++i) 59 | if (fun->funtab[i]->gcmark != mark) 60 | jsG_markfunction(J, mark, fun->funtab[i]); 61 | } 62 | 63 | static void jsG_markenvironment(js_State *J, int mark, js_Environment *env) 64 | { 65 | do { 66 | env->gcmark = mark; 67 | if (env->variables->gcmark != mark) 68 | jsG_markobject(J, mark, env->variables); 69 | env = env->outer; 70 | } while (env && env->gcmark != mark); 71 | } 72 | 73 | static void jsG_markproperty(js_State *J, int mark, js_Property *node) 74 | { 75 | while (node) { 76 | if (node->value.type == JS_TOBJECT && node->value.u.object->gcmark != mark) 77 | jsG_markobject(J, mark, node->value.u.object); 78 | if (node->getter && node->getter->gcmark != mark) 79 | jsG_markobject(J, mark, node->getter); 80 | if (node->setter && node->setter->gcmark != mark) 81 | jsG_markobject(J, mark, node->setter); 82 | node = node->next; 83 | } 84 | } 85 | 86 | static void jsG_markobject(js_State *J, int mark, js_Object *obj) 87 | { 88 | obj->gcmark = mark; 89 | if (obj->head) 90 | jsG_markproperty(J, mark, obj->head); 91 | if (obj->prototype && obj->prototype->gcmark != mark) 92 | jsG_markobject(J, mark, obj->prototype); 93 | if (obj->type == JS_CITERATOR) { 94 | jsG_markobject(J, mark, obj->u.iter.target); 95 | } 96 | if (obj->type == JS_CFUNCTION || obj->type == JS_CSCRIPT) { 97 | if (obj->u.f.scope && obj->u.f.scope->gcmark != mark) 98 | jsG_markenvironment(J, mark, obj->u.f.scope); 99 | if (obj->u.f.function && obj->u.f.function->gcmark != mark) 100 | jsG_markfunction(J, mark, obj->u.f.function); 101 | } 102 | } 103 | 104 | static void jsG_markstack(js_State *J, int mark) 105 | { 106 | js_Value *v = J->stack; 107 | int n = J->top; 108 | while (n--) { 109 | if (v->type == JS_TOBJECT && v->u.object->gcmark != mark) 110 | jsG_markobject(J, mark, v->u.object); 111 | ++v; 112 | } 113 | } 114 | 115 | void js_gc(js_State *J, int report) 116 | { 117 | js_Function *fun, *nextfun, **prevnextfun; 118 | js_Object *obj, *nextobj, **prevnextobj; 119 | js_Environment *env, *nextenv, **prevnextenv; 120 | int nenv = 0, nfun = 0, nobj = 0; 121 | int genv = 0, gfun = 0, gobj = 0; 122 | int mark; 123 | int i; 124 | 125 | mark = J->gcmark = J->gcmark == 1 ? 2 : 1; 126 | 127 | jsG_markobject(J, mark, J->Object_prototype); 128 | jsG_markobject(J, mark, J->Array_prototype); 129 | jsG_markobject(J, mark, J->Function_prototype); 130 | jsG_markobject(J, mark, J->Boolean_prototype); 131 | jsG_markobject(J, mark, J->Number_prototype); 132 | jsG_markobject(J, mark, J->String_prototype); 133 | jsG_markobject(J, mark, J->RegExp_prototype); 134 | jsG_markobject(J, mark, J->Date_prototype); 135 | 136 | jsG_markobject(J, mark, J->Error_prototype); 137 | jsG_markobject(J, mark, J->EvalError_prototype); 138 | jsG_markobject(J, mark, J->RangeError_prototype); 139 | jsG_markobject(J, mark, J->ReferenceError_prototype); 140 | jsG_markobject(J, mark, J->SyntaxError_prototype); 141 | jsG_markobject(J, mark, J->TypeError_prototype); 142 | jsG_markobject(J, mark, J->URIError_prototype); 143 | 144 | jsG_markobject(J, mark, J->R); 145 | jsG_markobject(J, mark, J->G); 146 | 147 | jsG_markstack(J, mark); 148 | 149 | jsG_markenvironment(J, mark, J->E); 150 | jsG_markenvironment(J, mark, J->GE); 151 | for (i = 0; i < J->envtop; ++i) 152 | jsG_markenvironment(J, mark, J->envstack[i]); 153 | 154 | prevnextenv = &J->gcenv; 155 | for (env = J->gcenv; env; env = nextenv) { 156 | nextenv = env->gcnext; 157 | if (env->gcmark != mark) { 158 | *prevnextenv = nextenv; 159 | jsG_freeenvironment(J, env); 160 | ++genv; 161 | } else { 162 | prevnextenv = &env->gcnext; 163 | } 164 | ++nenv; 165 | } 166 | 167 | prevnextfun = &J->gcfun; 168 | for (fun = J->gcfun; fun; fun = nextfun) { 169 | nextfun = fun->gcnext; 170 | if (fun->gcmark != mark) { 171 | *prevnextfun = nextfun; 172 | jsG_freefunction(J, fun); 173 | ++gfun; 174 | } else { 175 | prevnextfun = &fun->gcnext; 176 | } 177 | ++nfun; 178 | } 179 | 180 | prevnextobj = &J->gcobj; 181 | for (obj = J->gcobj; obj; obj = nextobj) { 182 | nextobj = obj->gcnext; 183 | if (obj->gcmark != mark) { 184 | *prevnextobj = nextobj; 185 | jsG_freeobject(J, obj); 186 | ++gobj; 187 | } else { 188 | prevnextobj = &obj->gcnext; 189 | } 190 | ++nobj; 191 | } 192 | 193 | if (report) 194 | printf("garbage collected: %d/%d envs, %d/%d funs, %d/%d objs\n", 195 | genv, nenv, gfun, nfun, gobj, nobj); 196 | } 197 | 198 | void js_freestate(js_State *J) 199 | { 200 | js_Function *fun, *nextfun; 201 | js_Object *obj, *nextobj; 202 | js_Environment *env, *nextenv; 203 | 204 | for (env = J->gcenv; env; env = nextenv) 205 | nextenv = env->gcnext, jsG_freeenvironment(J, env); 206 | for (fun = J->gcfun; fun; fun = nextfun) 207 | nextfun = fun->gcnext, jsG_freefunction(J, fun); 208 | for (obj = J->gcobj; obj; obj = nextobj) 209 | nextobj = obj->gcnext, jsG_freeobject(J, obj); 210 | 211 | jsS_freestrings(J); 212 | 213 | js_free(J, J->lexbuf.text); 214 | J->alloc(J->actx, J->stack, 0); 215 | J->alloc(J->actx, J, 0); 216 | } 217 | -------------------------------------------------------------------------------- /json.c: -------------------------------------------------------------------------------- 1 | #include "jsi.h" 2 | #include "jslex.h" 3 | #include "jsvalue.h" 4 | #include "jsbuiltin.h" 5 | 6 | #include "utf.h" 7 | 8 | static void jsonnext(js_State *J) 9 | { 10 | J->lookahead = jsY_lexjson(J); 11 | } 12 | 13 | static int jsonaccept(js_State *J, int t) 14 | { 15 | if (J->lookahead == t) { 16 | jsonnext(J); 17 | return 1; 18 | } 19 | return 0; 20 | } 21 | 22 | static void jsonexpect(js_State *J, int t) 23 | { 24 | if (!jsonaccept(J, t)) 25 | js_syntaxerror(J, "JSON: unexpected token: %s (expected %s)", 26 | jsY_tokenstring(J->lookahead), jsY_tokenstring(t)); 27 | } 28 | 29 | static void jsonvalue(js_State *J) 30 | { 31 | int i; 32 | const char *name; 33 | 34 | switch (J->lookahead) { 35 | case TK_STRING: 36 | js_pushliteral(J, J->text); 37 | jsonnext(J); 38 | break; 39 | 40 | case TK_NUMBER: 41 | js_pushnumber(J, J->number); 42 | jsonnext(J); 43 | break; 44 | 45 | case '{': 46 | js_newobject(J); 47 | jsonnext(J); 48 | if (J->lookahead == '}') 49 | return; 50 | do { 51 | if (J->lookahead != TK_STRING) 52 | js_syntaxerror(J, "JSON: unexpected token: %s (expected string)", jsY_tokenstring(J->lookahead)); 53 | name = J->text; 54 | jsonnext(J); 55 | jsonexpect(J, ':'); 56 | jsonvalue(J); 57 | js_setproperty(J, -2, name); 58 | } while (jsonaccept(J, ',')); 59 | jsonexpect(J, '}'); 60 | break; 61 | 62 | case '[': 63 | js_newarray(J); 64 | jsonnext(J); 65 | i = 0; 66 | if (J->lookahead == ']') 67 | return; 68 | do { 69 | jsonvalue(J); 70 | js_setindex(J, -2, i++); 71 | } while (jsonaccept(J, ',')); 72 | jsonexpect(J, ']'); 73 | break; 74 | 75 | case TK_TRUE: 76 | js_pushboolean(J, 1); 77 | jsonnext(J); 78 | break; 79 | 80 | case TK_FALSE: 81 | js_pushboolean(J, 0); 82 | jsonnext(J); 83 | break; 84 | 85 | case TK_NULL: 86 | js_pushnull(J); 87 | jsonnext(J); 88 | break; 89 | 90 | default: 91 | js_syntaxerror(J, "JSON: unexpected token: %s", jsY_tokenstring(J->lookahead)); 92 | } 93 | } 94 | 95 | static void JSON_parse(js_State *J) 96 | { 97 | const char *source = js_tostring(J, 1); 98 | jsY_initlex(J, "JSON", source); 99 | jsonnext(J); 100 | jsonvalue(J); 101 | // TODO: reviver Walk() 102 | } 103 | 104 | static void fmtnum(js_State *J, js_Buffer **sb, double n) 105 | { 106 | if (isnan(n)) js_puts(J, sb, "NaN"); 107 | else if (isinf(n)) js_puts(J, sb, n < 0 ? "-Infinity" : "Infinity"); 108 | else if (n == 0) js_puts(J, sb, "0"); 109 | else { 110 | char buf[40]; 111 | sprintf(buf, "%.17g", n); 112 | js_puts(J, sb, buf); 113 | } 114 | } 115 | 116 | static void fmtstr(js_State *J, js_Buffer **sb, const char *s) 117 | { 118 | static const char *HEX = "0123456789ABCDEF"; 119 | Rune c; 120 | js_putc(J, sb, '"'); 121 | while (*s) { 122 | s += chartorune(&c, s); 123 | switch (c) { 124 | case '"': js_puts(J, sb, "\\\""); break; 125 | case '\\': js_puts(J, sb, "\\\\"); break; 126 | case '\b': js_puts(J, sb, "\\b"); break; 127 | case '\f': js_puts(J, sb, "\\f"); break; 128 | case '\n': js_puts(J, sb, "\\n"); break; 129 | case '\r': js_puts(J, sb, "\\r"); break; 130 | case '\t': js_puts(J, sb, "\\t"); break; 131 | default: 132 | if (c < ' ') { 133 | js_puts(J, sb, "\\u"); 134 | js_putc(J, sb, HEX[(c>>12)&15]); 135 | js_putc(J, sb, HEX[(c>>8)&15]); 136 | js_putc(J, sb, HEX[(c>>4)&15]); 137 | js_putc(J, sb, HEX[c&15]); 138 | } else { 139 | js_putc(J, sb, c); break; 140 | } 141 | } 142 | } 143 | js_putc(J, sb, '"'); 144 | } 145 | 146 | static int fmtvalue(js_State *J, js_Buffer **sb, const char *key); 147 | 148 | static void fmtobject(js_State *J, js_Buffer **sb, js_Object *obj) 149 | { 150 | js_Property *ref; 151 | int save; 152 | int n = 0; 153 | 154 | js_putc(J, sb, '{'); 155 | for (ref = obj->head; ref; ref = ref->next) { 156 | if (ref->atts & JS_DONTENUM) 157 | continue; 158 | save = (*sb)->n; 159 | if (n) js_putc(J, sb, ','); 160 | fmtstr(J, sb, ref->name); 161 | js_putc(J, sb, ':'); 162 | js_pushvalue(J, ref->value); 163 | if (!fmtvalue(J, sb, ref->name)) 164 | (*sb)->n = save; 165 | else 166 | ++n; 167 | js_pop(J, 1); 168 | } 169 | js_putc(J, sb, '}'); 170 | } 171 | 172 | static void fmtarray(js_State *J, js_Buffer **sb) 173 | { 174 | unsigned int n, k; 175 | char buf[40]; 176 | 177 | n = js_getlength(J, -1); 178 | 179 | js_putc(J, sb, '['); 180 | for (k = 0; k < n; ++k) { 181 | if (k) js_putc(J, sb, ','); 182 | sprintf(buf, "%u", k); 183 | js_getproperty(J, -1, buf); 184 | if (!fmtvalue(J, sb, buf)) 185 | js_puts(J, sb, "null"); 186 | js_pop(J, 1); 187 | } 188 | js_putc(J, sb, ']'); 189 | } 190 | 191 | static int fmtvalue(js_State *J, js_Buffer **sb, const char *key) 192 | { 193 | if (js_try(J)) { 194 | js_free(J, *sb); 195 | js_throw(J); 196 | } 197 | if (js_isobject(J, -1)) { 198 | if (js_hasproperty(J, -1, "toJSON")) { 199 | if (js_iscallable(J, -1)) { 200 | js_copy(J, -2); 201 | js_pushliteral(J, key); 202 | js_call(J, 1); 203 | js_rot2pop1(J); 204 | } else { 205 | js_pop(J, 1); 206 | } 207 | } 208 | } 209 | js_endtry(J); 210 | 211 | // TODO: replacer() 212 | 213 | if (js_isobject(J, -1) && !js_iscallable(J, -1)) { 214 | js_Object *obj = js_toobject(J, -1); 215 | switch (obj->type) { 216 | case JS_CNUMBER: fmtnum(J, sb, obj->u.number); break; 217 | case JS_CSTRING: fmtstr(J, sb, obj->u.s.string); break; 218 | case JS_CBOOLEAN: js_puts(J, sb, obj->u.boolean ? "true" : "false"); break; 219 | case JS_CARRAY: fmtarray(J, sb); break; 220 | default: fmtobject(J, sb, obj); break; 221 | } 222 | } 223 | else if (js_isboolean(J, -1)) 224 | js_puts(J, sb, js_toboolean(J, -1) ? "true" : "false"); 225 | else if (js_isnumber(J, -1)) 226 | fmtnum(J, sb, js_tonumber(J, -1)); 227 | else if (js_isstring(J, -1)) 228 | fmtstr(J, sb, js_tostring(J, -1)); 229 | else if (js_isnull(J, -1)) 230 | js_puts(J, sb, "null"); 231 | else 232 | return 0; 233 | 234 | return 1; 235 | } 236 | 237 | static void JSON_stringify(js_State *J) 238 | { 239 | js_Buffer *sb = NULL; 240 | if (js_isdefined(J, 1)) { 241 | js_copy(J, 1); 242 | if (fmtvalue(J, &sb, "")) { 243 | js_putc(J, &sb, 0); 244 | if (js_try(J)) { 245 | js_free(J, sb); 246 | js_throw(J); 247 | } 248 | js_pushstring(J, sb ? sb->s : ""); 249 | js_endtry(J); 250 | js_free(J, sb); 251 | } 252 | } else { 253 | js_pushundefined(J); 254 | } 255 | } 256 | 257 | void jsB_initjson(js_State *J) 258 | { 259 | js_pushobject(J, jsV_newobject(J, JS_CJSON, J->Object_prototype)); 260 | { 261 | jsB_propf(J, "parse", JSON_parse, 2); 262 | jsB_propf(J, "stringify", JSON_stringify, 3); 263 | } 264 | js_defglobal(J, "JSON", JS_DONTENUM); 265 | } 266 | -------------------------------------------------------------------------------- /mujs.h: -------------------------------------------------------------------------------- 1 | #ifndef mujs_h 2 | #define mujs_h 3 | 4 | /* noreturn is a GCC extension */ 5 | #ifdef __GNUC__ 6 | #define JS_NORETURN __attribute__((noreturn)) 7 | #else 8 | #ifdef _MSC_VER 9 | #define JS_NORETURN __declspec(noreturn) 10 | #else 11 | #define JS_NORETURN 12 | #endif 13 | #endif 14 | 15 | /* GCC can do type checking of printf strings */ 16 | #ifdef __printflike 17 | #define JS_PRINTFLIKE __printflike 18 | #else 19 | #if __GNUC__ > 2 || __GNUC__ == 2 && __GNUC_MINOR__ >= 7 20 | #define JS_PRINTFLIKE(fmtarg, firstvararg) \ 21 | __attribute__((__format__ (__printf__, fmtarg, firstvararg))) 22 | #else 23 | #define JS_PRINTFLIKE(fmtarg, firstvararg) 24 | #endif 25 | #endif 26 | 27 | typedef struct js_State js_State; 28 | 29 | typedef void *(*js_Alloc)(void *memctx, void *ptr, unsigned int size); 30 | typedef void (*js_Panic)(js_State *J); 31 | typedef void (*js_CFunction)(js_State *J); 32 | 33 | /* Basic functions */ 34 | js_State *js_newstate(js_Alloc alloc, void *actx); 35 | js_Panic js_atpanic(js_State *J, js_Panic panic); 36 | void js_freestate(js_State *J); 37 | void js_gc(js_State *J, int report); 38 | 39 | int js_dostring(js_State *J, const char *source, int report); 40 | int js_dofile(js_State *J, const char *filename); 41 | int js_ploadstring(js_State *J, const char *filename, const char *source); 42 | int js_ploadfile(js_State *J, const char *filename); 43 | int js_pcall(js_State *J, int n); 44 | int js_pconstruct(js_State *J, int n); 45 | 46 | /* RegExp flags */ 47 | enum { 48 | JS_REGEXP_G = 1, 49 | JS_REGEXP_I = 2, 50 | JS_REGEXP_M = 4, 51 | }; 52 | 53 | /* Property attribute flags */ 54 | enum { 55 | JS_READONLY = 1, 56 | JS_DONTENUM = 2, 57 | JS_DONTCONF = 4, 58 | }; 59 | 60 | void js_newerror(js_State *J, const char *message); 61 | void js_newevalerror(js_State *J, const char *message); 62 | void js_newrangeerror(js_State *J, const char *message); 63 | void js_newreferenceerror(js_State *J, const char *message); 64 | void js_newsyntaxerror(js_State *J, const char *message); 65 | void js_newtypeerror(js_State *J, const char *message); 66 | void js_newurierror(js_State *J, const char *message); 67 | 68 | JS_NORETURN void js_error(js_State *J, const char *fmt, ...) JS_PRINTFLIKE(2,3); 69 | JS_NORETURN void js_evalerror(js_State *J, const char *fmt, ...) JS_PRINTFLIKE(2,3); 70 | JS_NORETURN void js_rangeerror(js_State *J, const char *fmt, ...) JS_PRINTFLIKE(2,3); 71 | JS_NORETURN void js_referenceerror(js_State *J, const char *fmt, ...) JS_PRINTFLIKE(2,3); 72 | JS_NORETURN void js_syntaxerror(js_State *J, const char *fmt, ...) JS_PRINTFLIKE(2,3); 73 | JS_NORETURN void js_typeerror(js_State *J, const char *fmt, ...) JS_PRINTFLIKE(2,3); 74 | JS_NORETURN void js_urierror(js_State *J, const char *fmt, ...) JS_PRINTFLIKE(2,3); 75 | JS_NORETURN void js_throw(js_State *J); 76 | 77 | void js_loadstring(js_State *J, const char *filename, const char *source); 78 | void js_loadfile(js_State *J, const char *filename); 79 | 80 | void js_call(js_State *J, int n); 81 | void js_construct(js_State *J, int n); 82 | 83 | const char *js_ref(js_State *J); 84 | void js_unref(js_State *J, const char *ref); 85 | 86 | void js_getregistry(js_State *J, const char *name); 87 | void js_setregistry(js_State *J, const char *name); 88 | void js_delregistry(js_State *J, const char *name); 89 | 90 | void js_getglobal(js_State *J, const char *name); 91 | void js_setglobal(js_State *J, const char *name); 92 | void js_defglobal(js_State *J, const char *name, int atts); 93 | 94 | int js_hasproperty(js_State *J, int idx, const char *name); 95 | void js_getproperty(js_State *J, int idx, const char *name); 96 | void js_setproperty(js_State *J, int idx, const char *name); 97 | void js_defproperty(js_State *J, int idx, const char *name, int atts); 98 | void js_delproperty(js_State *J, int idx, const char *name); 99 | void js_defaccessor(js_State *J, int idx, const char *name, int atts); 100 | 101 | unsigned int js_getlength(js_State *J, int idx); 102 | void js_setlength(js_State *J, int idx, unsigned int len); 103 | int js_hasindex(js_State *J, int idx, unsigned int i); 104 | void js_getindex(js_State *J, int idx, unsigned int i); 105 | void js_setindex(js_State *J, int idx, unsigned int i); 106 | void js_delindex(js_State *J, int idx, unsigned int i); 107 | 108 | void js_currentfunction(js_State *J); 109 | void js_pushglobal(js_State *J); 110 | void js_pushundefined(js_State *J); 111 | void js_pushnull(js_State *J); 112 | void js_pushboolean(js_State *J, int v); 113 | void js_pushnumber(js_State *J, double v); 114 | void js_pushstring(js_State *J, const char *v); 115 | void js_pushlstring(js_State *J, const char *v, unsigned int n); 116 | void js_pushliteral(js_State *J, const char *v); 117 | 118 | void js_newobject(js_State *J); 119 | void js_newarray(js_State *J); 120 | void js_newboolean(js_State *J, int v); 121 | void js_newnumber(js_State *J, double v); 122 | void js_newstring(js_State *J, const char *v); 123 | void js_newerror(js_State *J, const char *message); 124 | void js_newcfunction(js_State *J, js_CFunction fun, unsigned int length); 125 | void js_newcconstructor(js_State *J, js_CFunction fun, js_CFunction con, unsigned int length); 126 | void js_newuserdata(js_State *J, const char *tag, void *data); 127 | void js_newregexp(js_State *J, const char *pattern, int flags); 128 | 129 | void js_pushiterator(js_State *J, int idx, int own); 130 | const char *js_nextiterator(js_State *J, int idx); 131 | 132 | int js_isdefined(js_State *J, int idx); 133 | int js_isundefined(js_State *J, int idx); 134 | int js_isnull(js_State *J, int idx); 135 | int js_isboolean(js_State *J, int idx); 136 | int js_isnumber(js_State *J, int idx); 137 | int js_isstring(js_State *J, int idx); 138 | int js_isprimitive(js_State *J, int idx); 139 | int js_isobject(js_State *J, int idx); 140 | int js_isarray(js_State *J, int idx); 141 | int js_isregexp(js_State *J, int idx); 142 | int js_iscallable(js_State *J, int idx); 143 | int js_isuserdata(js_State *J, int idx, const char *tag); 144 | 145 | int js_toboolean(js_State *J, int idx); 146 | double js_tonumber(js_State *J, int idx); 147 | const char *js_tostring(js_State *J, int idx); 148 | void *js_touserdata(js_State *J, int idx, const char *tag); 149 | 150 | double js_tointeger(js_State *J, int idx); 151 | int js_toint32(js_State *J, int idx); 152 | unsigned int js_touint32(js_State *J, int idx); 153 | short js_toint16(js_State *J, int idx); 154 | unsigned short js_touint16(js_State *J, int idx); 155 | 156 | int js_gettop(js_State *J); 157 | void js_settop(js_State *J, int idx); 158 | void js_pop(js_State *J, int n); 159 | void js_rot(js_State *J, int n); 160 | void js_copy(js_State *J, int idx); 161 | void js_remove(js_State *J, int idx); 162 | void js_insert(js_State *J, int idx); 163 | void js_replace(js_State* J, int idx); 164 | 165 | void js_concat(js_State *J); 166 | int js_compare(js_State *J, int *okay); 167 | int js_equal(js_State *J); 168 | int js_strictequal(js_State *J); 169 | int js_instanceof(js_State *J); 170 | 171 | #endif 172 | -------------------------------------------------------------------------------- /jsbuiltin.c: -------------------------------------------------------------------------------- 1 | #include "jsi.h" 2 | #include "jslex.h" 3 | #include "jscompile.h" 4 | #include "jsvalue.h" 5 | #include "jsbuiltin.h" 6 | 7 | static void jsB_globalf(js_State *J, const char *name, js_CFunction cfun, int n) 8 | { 9 | js_newcfunction(J, cfun, n); 10 | js_defglobal(J, name, JS_DONTENUM); 11 | } 12 | 13 | void jsB_propf(js_State *J, const char *name, js_CFunction cfun, int n) 14 | { 15 | js_newcfunction(J, cfun, n); 16 | js_defproperty(J, -2, name, JS_DONTENUM); 17 | } 18 | 19 | void jsB_propn(js_State *J, const char *name, double number) 20 | { 21 | js_pushnumber(J, number); 22 | js_defproperty(J, -2, name, JS_READONLY | JS_DONTENUM | JS_DONTCONF); 23 | } 24 | 25 | void jsB_props(js_State *J, const char *name, const char *string) 26 | { 27 | js_pushliteral(J, string); 28 | js_defproperty(J, -2, name, JS_DONTENUM); 29 | } 30 | 31 | static void jsB_eval(js_State *J) 32 | { 33 | if (!js_isstring(J, -1)) { 34 | js_copy(J, 1); 35 | return; 36 | } 37 | js_loadeval(J, "(eval)", js_tostring(J, -1)); 38 | js_pushglobal(J); 39 | js_call(J, 0); 40 | } 41 | 42 | static void jsB_parseInt(js_State *J) 43 | { 44 | const char *s = js_tostring(J, 1); 45 | double radix = js_isdefined(J, 2) ? js_tonumber(J, 2) : 10; 46 | char *e; 47 | double n; 48 | 49 | while (jsY_iswhite(*s) || jsY_isnewline(*s)) ++s; 50 | if (radix == 0) 51 | radix = 10; 52 | if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) { 53 | s += 2; 54 | radix = 16; 55 | } 56 | n = strtol(s, &e, radix); 57 | if (s == e) 58 | js_pushnumber(J, NAN); 59 | else 60 | js_pushnumber(J, n); 61 | } 62 | 63 | static void jsB_parseFloat(js_State *J) 64 | { 65 | const char *s = js_tostring(J, 1); 66 | char *e; 67 | double n; 68 | 69 | while (jsY_iswhite(*s) || jsY_isnewline(*s)) ++s; 70 | if (!strncmp(s, "Infinity", 8)) 71 | js_pushnumber(J, INFINITY); 72 | else if (!strncmp(s, "+Infinity", 9)) 73 | js_pushnumber(J, INFINITY); 74 | else if (!strncmp(s, "-Infinity", 9)) 75 | js_pushnumber(J, -INFINITY); 76 | else { 77 | n = js_stringtofloat(s, &e); 78 | if (e == s) 79 | js_pushnumber(J, NAN); 80 | else 81 | js_pushnumber(J, n); 82 | } 83 | } 84 | 85 | static void jsB_isNaN(js_State *J) 86 | { 87 | double n = js_tonumber(J, 1); 88 | js_pushboolean(J, isnan(n)); 89 | } 90 | 91 | static void jsB_isFinite(js_State *J) 92 | { 93 | double n = js_tonumber(J, 1); 94 | js_pushboolean(J, isfinite(n)); 95 | } 96 | 97 | static void Encode(js_State *J, const char *str, const char *unescaped) 98 | { 99 | js_Buffer *sb = NULL; 100 | 101 | static const char *HEX = "0123456789ABCDEF"; 102 | 103 | while (*str) { 104 | int c = (unsigned char) *str++; 105 | if (strchr(unescaped, c)) 106 | js_putc(J, &sb, c); 107 | else { 108 | js_putc(J, &sb, '%'); 109 | js_putc(J, &sb, HEX[(c >> 4) & 0xf]); 110 | js_putc(J, &sb, HEX[c & 0xf]); 111 | } 112 | } 113 | js_putc(J, &sb, 0); 114 | 115 | if (js_try(J)) { 116 | js_free(J, sb); 117 | js_throw(J); 118 | } 119 | js_pushstring(J, sb ? sb->s : ""); 120 | js_endtry(J); 121 | js_free(J, sb); 122 | } 123 | 124 | static void Decode(js_State *J, const char *str, const char *reserved) 125 | { 126 | js_Buffer *sb = NULL; 127 | int a, b; 128 | 129 | while (*str) { 130 | int c = (unsigned char) *str++; 131 | if (c != '%') 132 | js_putc(J, &sb, c); 133 | else { 134 | if (!str[0] || !str[1]) 135 | js_urierror(J, "truncated escape sequence"); 136 | a = *str++; 137 | b = *str++; 138 | if (!jsY_ishex(a) || !jsY_ishex(b)) 139 | js_urierror(J, "invalid escape sequence"); 140 | c = jsY_tohex(a) << 4 | jsY_tohex(b); 141 | if (!strchr(reserved, c)) 142 | js_putc(J, &sb, c); 143 | else { 144 | js_putc(J, &sb, '%'); 145 | js_putc(J, &sb, a); 146 | js_putc(J, &sb, b); 147 | } 148 | } 149 | } 150 | js_putc(J, &sb, 0); 151 | 152 | if (js_try(J)) { 153 | js_free(J, sb); 154 | js_throw(J); 155 | } 156 | js_pushstring(J, sb ? sb->s : ""); 157 | js_endtry(J); 158 | js_free(J, sb); 159 | } 160 | 161 | #define URIRESERVED ";/?:@&=+$," 162 | #define URIALPHA "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" 163 | #define URIDIGIT "0123456789" 164 | #define URIMARK "-_.!~*`()" 165 | #define URIUNESCAPED URIALPHA URIDIGIT URIMARK 166 | 167 | static void jsB_decodeURI(js_State *J) 168 | { 169 | Decode(J, js_tostring(J, 1), URIRESERVED "#"); 170 | } 171 | 172 | static void jsB_decodeURIComponent(js_State *J) 173 | { 174 | Decode(J, js_tostring(J, 1), ""); 175 | } 176 | 177 | static void jsB_encodeURI(js_State *J) 178 | { 179 | Encode(J, js_tostring(J, 1), URIUNESCAPED URIRESERVED "#"); 180 | } 181 | 182 | static void jsB_encodeURIComponent(js_State *J) 183 | { 184 | Encode(J, js_tostring(J, 1), URIUNESCAPED); 185 | } 186 | 187 | void jsB_init(js_State *J) 188 | { 189 | /* Create the prototype objects here, before the constructors */ 190 | J->Object_prototype = jsV_newobject(J, JS_COBJECT, NULL); 191 | J->Array_prototype = jsV_newobject(J, JS_CARRAY, J->Object_prototype); 192 | J->Function_prototype = jsV_newobject(J, JS_CCFUNCTION, J->Object_prototype); 193 | J->Boolean_prototype = jsV_newobject(J, JS_CBOOLEAN, J->Object_prototype); 194 | J->Number_prototype = jsV_newobject(J, JS_CNUMBER, J->Object_prototype); 195 | J->String_prototype = jsV_newobject(J, JS_CSTRING, J->Object_prototype); 196 | J->RegExp_prototype = jsV_newobject(J, JS_COBJECT, J->Object_prototype); 197 | J->Date_prototype = jsV_newobject(J, JS_CDATE, J->Object_prototype); 198 | 199 | /* All the native error types */ 200 | J->Error_prototype = jsV_newobject(J, JS_CERROR, J->Object_prototype); 201 | J->EvalError_prototype = jsV_newobject(J, JS_CERROR, J->Error_prototype); 202 | J->RangeError_prototype = jsV_newobject(J, JS_CERROR, J->Error_prototype); 203 | J->ReferenceError_prototype = jsV_newobject(J, JS_CERROR, J->Error_prototype); 204 | J->SyntaxError_prototype = jsV_newobject(J, JS_CERROR, J->Error_prototype); 205 | J->TypeError_prototype = jsV_newobject(J, JS_CERROR, J->Error_prototype); 206 | J->URIError_prototype = jsV_newobject(J, JS_CERROR, J->Error_prototype); 207 | 208 | /* Create the constructors and fill out the prototype objects */ 209 | jsB_initobject(J); 210 | jsB_initarray(J); 211 | jsB_initfunction(J); 212 | jsB_initboolean(J); 213 | jsB_initnumber(J); 214 | jsB_initstring(J); 215 | jsB_initregexp(J); 216 | jsB_initdate(J); 217 | jsB_initerror(J); 218 | jsB_initmath(J); 219 | jsB_initjson(J); 220 | 221 | /* Initialize the global object */ 222 | js_pushnumber(J, NAN); 223 | js_defglobal(J, "NaN", JS_READONLY | JS_DONTENUM | JS_DONTCONF); 224 | 225 | js_pushnumber(J, INFINITY); 226 | js_defglobal(J, "Infinity", JS_READONLY | JS_DONTENUM | JS_DONTCONF); 227 | 228 | js_pushundefined(J); 229 | js_defglobal(J, "undefined", JS_READONLY | JS_DONTENUM | JS_DONTCONF); 230 | 231 | jsB_globalf(J, "eval", jsB_eval, 1); 232 | jsB_globalf(J, "parseInt", jsB_parseInt, 1); 233 | jsB_globalf(J, "parseFloat", jsB_parseFloat, 1); 234 | jsB_globalf(J, "isNaN", jsB_isNaN, 1); 235 | jsB_globalf(J, "isFinite", jsB_isFinite, 1); 236 | 237 | jsB_globalf(J, "decodeURI", jsB_decodeURI, 1); 238 | jsB_globalf(J, "decodeURIComponent", jsB_decodeURIComponent, 1); 239 | jsB_globalf(J, "encodeURI", jsB_encodeURI, 1); 240 | jsB_globalf(J, "encodeURIComponent", jsB_encodeURIComponent, 1); 241 | } 242 | -------------------------------------------------------------------------------- /jsproperty.c: -------------------------------------------------------------------------------- 1 | #include "jsi.h" 2 | #include "jsvalue.h" 3 | 4 | /* 5 | Use an AA-tree to quickly look up properties in objects: 6 | 7 | The level of every leaf node is one. 8 | The level of every left child is one less than its parent. 9 | The level of every right child is equal or one less than its parent. 10 | The level of every right grandchild is less than its grandparent. 11 | Every node of level greater than one has two children. 12 | 13 | A link where the child's level is equal to that of its parent is called a horizontal link. 14 | Individual right horizontal links are allowed, but consecutive ones are forbidden. 15 | Left horizontal links are forbidden. 16 | 17 | skew() fixes left horizontal links. 18 | split() fixes consecutive right horizontal links. 19 | */ 20 | 21 | static js_Property sentinel = { 22 | "", 23 | &sentinel, &sentinel, 24 | NULL, NULL, 25 | 0, 0, 26 | { 0, { 0 } }, 27 | NULL, NULL 28 | }; 29 | 30 | static js_Property *newproperty(js_State *J, const char *name) 31 | { 32 | js_Property *node = js_malloc(J, sizeof *node); 33 | node->name = js_intern(J, name); 34 | node->left = node->right = &sentinel; 35 | node->prevp = NULL; 36 | node->next = NULL; 37 | node->level = 1; 38 | node->atts = 0; 39 | node->value.type = JS_TUNDEFINED; 40 | node->value.u.number = 0; 41 | node->getter = NULL; 42 | node->setter = NULL; 43 | return node; 44 | } 45 | 46 | static js_Property *lookup(js_Property *node, const char *name) 47 | { 48 | while (node != &sentinel) { 49 | int c = strcmp(name, node->name); 50 | if (c == 0) 51 | return node; 52 | else if (c < 0) 53 | node = node->left; 54 | else 55 | node = node->right; 56 | } 57 | return NULL; 58 | } 59 | 60 | static js_Property *skew(js_Property *node) 61 | { 62 | if (node->left->level == node->level) { 63 | js_Property *temp = node; 64 | node = node->left; 65 | temp->left = node->right; 66 | node->right = temp; 67 | } 68 | return node; 69 | } 70 | 71 | static js_Property *split(js_Property *node) 72 | { 73 | if (node->right->right->level == node->level) { 74 | js_Property *temp = node; 75 | node = node->right; 76 | temp->right = node->left; 77 | node->left = temp; 78 | ++node->level; 79 | } 80 | return node; 81 | } 82 | 83 | static js_Property *insert(js_State *J, js_Property *node, const char *name, js_Property **result) 84 | { 85 | if (node != &sentinel) { 86 | int c = strcmp(name, node->name); 87 | if (c < 0) 88 | node->left = insert(J, node->left, name, result); 89 | else if (c > 0) 90 | node->right = insert(J, node->right, name, result); 91 | else 92 | return *result = node; 93 | node = skew(node); 94 | node = split(node); 95 | return node; 96 | } 97 | return *result = newproperty(J, name); 98 | } 99 | 100 | static void freeproperty(js_State *J, js_Property *node) 101 | { 102 | if (node->next) 103 | node->next->prevp = node->prevp; 104 | *node->prevp = node->next; 105 | js_free(J, node); 106 | } 107 | 108 | static js_Property *delete(js_State *J, js_Property *node, const char *name) 109 | { 110 | js_Property *temp, *succ; 111 | 112 | if (node != &sentinel) { 113 | int c = strcmp(name, node->name); 114 | if (c < 0) { 115 | node->left = delete(J, node->left, name); 116 | } else if (c > 0) { 117 | node->right = delete(J, node->right, name); 118 | } else { 119 | if (node->left == &sentinel) { 120 | temp = node; 121 | node = node->right; 122 | freeproperty(J, temp); 123 | } else if (node->right == &sentinel) { 124 | temp = node; 125 | node = node->left; 126 | freeproperty(J, temp); 127 | } else { 128 | succ = node->right; 129 | while (succ->left != &sentinel) 130 | succ = succ->left; 131 | node->name = succ->name; 132 | node->atts = succ->atts; 133 | node->value = succ->value; 134 | node->right = delete(J, node->right, succ->name); 135 | } 136 | } 137 | 138 | if (node->left->level < node->level - 1 || 139 | node->right->level < node->level - 1) 140 | { 141 | if (node->right->level > --node->level) 142 | node->right->level = node->level; 143 | node = skew(node); 144 | node->right = skew(node->right); 145 | node->right->right = skew(node->right->right); 146 | node = split(node); 147 | node->right = split(node->right); 148 | } 149 | } 150 | return node; 151 | } 152 | 153 | 154 | js_Object *jsV_newobject(js_State *J, enum js_Class type, js_Object *prototype) 155 | { 156 | js_Object *obj = js_malloc(J, sizeof *obj); 157 | memset(obj, 0, sizeof *obj); 158 | obj->gcmark = 0; 159 | obj->gcnext = J->gcobj; 160 | J->gcobj = obj; 161 | ++J->gccounter; 162 | 163 | obj->type = type; 164 | obj->properties = &sentinel; 165 | obj->prototype = prototype; 166 | obj->extensible = 1; 167 | return obj; 168 | } 169 | 170 | js_Property *jsV_getownproperty(js_State *J, js_Object *obj, const char *name) 171 | { 172 | return lookup(obj->properties, name); 173 | } 174 | 175 | js_Property *jsV_getpropertyx(js_State *J, js_Object *obj, const char *name, int *own) 176 | { 177 | *own = 1; 178 | do { 179 | js_Property *ref = lookup(obj->properties, name); 180 | if (ref) 181 | return ref; 182 | obj = obj->prototype; 183 | *own = 0; 184 | } while (obj); 185 | return NULL; 186 | } 187 | 188 | js_Property *jsV_getproperty(js_State *J, js_Object *obj, const char *name) 189 | { 190 | do { 191 | js_Property *ref = lookup(obj->properties, name); 192 | if (ref) 193 | return ref; 194 | obj = obj->prototype; 195 | } while (obj); 196 | return NULL; 197 | } 198 | 199 | js_Property *jsV_setproperty(js_State *J, js_Object *obj, const char *name) 200 | { 201 | js_Property *result; 202 | 203 | if (!obj->extensible) 204 | return lookup(obj->properties, name); 205 | 206 | obj->properties = insert(J, obj->properties, name, &result); 207 | if (!result->prevp) { 208 | if (!obj->head) { 209 | result->prevp = &obj->head; 210 | obj->tail = obj->head = result; 211 | } else { 212 | result->prevp = &obj->tail->next; 213 | obj->tail->next = result; 214 | obj->tail = result; 215 | } 216 | } 217 | return result; 218 | } 219 | 220 | void jsV_delproperty(js_State *J, js_Object *obj, const char *name) 221 | { 222 | obj->properties = delete(J, obj->properties, name); 223 | } 224 | 225 | /* Flatten hierarchy of enumerable properties into an iterator object */ 226 | 227 | static int itshadow(js_State *J, js_Object *top, js_Object *bot, const char *name) 228 | { 229 | unsigned int k; 230 | while (top != bot) { 231 | js_Property *prop = lookup(top->properties, name); 232 | if (prop && !(prop->atts & JS_DONTENUM)) 233 | return 1; 234 | if (top->type == JS_CSTRING) 235 | if (js_isarrayindex(J, name, &k) && k < top->u.s.length) 236 | return 1; 237 | top = top->prototype; 238 | } 239 | return 0; 240 | } 241 | 242 | static void itwalk(js_State *J, js_Object *io, js_Object *top, int own) 243 | { 244 | js_Object *obj = top; 245 | js_Iterator *tail = NULL; 246 | char buf[32]; 247 | unsigned int k; 248 | 249 | #define ITADD(x) \ 250 | js_Iterator *node = js_malloc(J, sizeof *node); \ 251 | node->name = x; \ 252 | node->next = NULL; \ 253 | if (!tail) { \ 254 | io->u.iter.head = tail = node; \ 255 | } else { \ 256 | tail->next = node; \ 257 | tail = node; \ 258 | } 259 | 260 | while (obj) { 261 | js_Property *prop = obj->head; 262 | while (prop) { 263 | if (!(prop->atts & JS_DONTENUM) && !itshadow(J, top, obj, prop->name)) { 264 | ITADD(prop->name); 265 | } 266 | prop = prop->next; 267 | } 268 | 269 | if (obj->type == JS_CSTRING) { 270 | for (k = 0; k < obj->u.s.length; ++k) { 271 | sprintf(buf, "%u", k); 272 | if (!itshadow(J, top, obj, buf)) { 273 | ITADD(js_intern(J, buf)); 274 | } 275 | } 276 | } 277 | 278 | if (own) 279 | break; 280 | obj = obj->prototype; 281 | } 282 | } 283 | 284 | js_Object *jsV_newiterator(js_State *J, js_Object *obj, int own) 285 | { 286 | js_Object *io = jsV_newobject(J, JS_CITERATOR, NULL); 287 | io->u.iter.target = obj; 288 | io->u.iter.head = NULL; 289 | itwalk(J, io, obj, own); 290 | return io; 291 | } 292 | 293 | const char *jsV_nextiterator(js_State *J, js_Object *io) 294 | { 295 | unsigned int k; 296 | if (io->type != JS_CITERATOR) 297 | js_typeerror(J, "not an iterator"); 298 | while (io->u.iter.head) { 299 | js_Iterator *next = io->u.iter.head->next; 300 | const char *name = io->u.iter.head->name; 301 | js_free(J, io->u.iter.head); 302 | io->u.iter.head = next; 303 | if (jsV_getproperty(J, io->u.iter.target, name)) 304 | return name; 305 | if (io->u.iter.target->type == JS_CSTRING) 306 | if (js_isarrayindex(J, name, &k) && k < io->u.iter.target->u.s.length) 307 | return name; 308 | } 309 | return NULL; 310 | } 311 | 312 | /* Walk all the properties and delete them one by one for arrays */ 313 | 314 | void jsV_resizearray(js_State *J, js_Object *obj, unsigned int newlen) 315 | { 316 | const char *s; 317 | unsigned int k; 318 | if (newlen < obj->u.a.length) { 319 | js_Object *it = jsV_newiterator(J, obj, 1); 320 | while ((s = jsV_nextiterator(J, it))) { 321 | k = jsV_numbertouint32(jsV_stringtonumber(J, s)); 322 | if (k >= newlen && !strcmp(s, jsV_numbertostring(J, k))) 323 | jsV_delproperty(J, obj, s); 324 | } 325 | } 326 | obj->u.a.length = newlen; 327 | } 328 | -------------------------------------------------------------------------------- /jsobject.c: -------------------------------------------------------------------------------- 1 | #include "jsi.h" 2 | #include "jsvalue.h" 3 | #include "jsbuiltin.h" 4 | 5 | static void jsB_new_Object(js_State *J) 6 | { 7 | if (js_gettop(J) == 1 || js_isundefined(J, 1) || js_isnull(J, 1)) 8 | js_newobject(J); 9 | else 10 | js_pushobject(J, js_toobject(J, 1)); 11 | } 12 | 13 | static void jsB_Object(js_State *J) 14 | { 15 | if (js_gettop(J) == 1 || js_isundefined(J, 1) || js_isnull(J, 1)) 16 | js_newobject(J); 17 | else 18 | js_pushobject(J, js_toobject(J, 1)); 19 | } 20 | 21 | static void Op_toString(js_State *J) 22 | { 23 | js_Object *self = js_toobject(J, 0); 24 | switch (self->type) { 25 | case JS_COBJECT: js_pushliteral(J, "[object Object]"); break; 26 | case JS_CARRAY: js_pushliteral(J, "[object Array]"); break; 27 | case JS_CFUNCTION: js_pushliteral(J, "[object Function]"); break; 28 | case JS_CSCRIPT: js_pushliteral(J, "[object Function]"); break; 29 | case JS_CCFUNCTION: js_pushliteral(J, "[object Function]"); break; 30 | case JS_CERROR: js_pushliteral(J, "[object Error]"); break; 31 | case JS_CBOOLEAN: js_pushliteral(J, "[object Boolean]"); break; 32 | case JS_CNUMBER: js_pushliteral(J, "[object Number]"); break; 33 | case JS_CSTRING: js_pushliteral(J, "[object String]"); break; 34 | case JS_CREGEXP: js_pushliteral(J, "[object RegExp]"); break; 35 | case JS_CDATE: js_pushliteral(J, "[object Date]"); break; 36 | case JS_CMATH: js_pushliteral(J, "[object Math]"); break; 37 | case JS_CJSON: js_pushliteral(J, "[object JSON]"); break; 38 | case JS_CITERATOR: js_pushliteral(J, "[Iterator]"); break; 39 | case JS_CUSERDATA: 40 | js_pushliteral(J, "[object "); 41 | js_pushliteral(J, self->u.user.tag); 42 | js_concat(J); 43 | js_pushliteral(J, "]"); 44 | js_concat(J); 45 | break; 46 | } 47 | } 48 | 49 | static void Op_valueOf(js_State *J) 50 | { 51 | js_copy(J, 0); 52 | } 53 | 54 | static void Op_hasOwnProperty(js_State *J) 55 | { 56 | js_Object *self = js_toobject(J, 0); 57 | const char *name = js_tostring(J, 1); 58 | js_Property *ref = jsV_getownproperty(J, self, name); 59 | js_pushboolean(J, ref != NULL); 60 | } 61 | 62 | static void Op_isPrototypeOf(js_State *J) 63 | { 64 | js_Object *self = js_toobject(J, 0); 65 | if (js_isobject(J, 1)) { 66 | js_Object *V = js_toobject(J, 1); 67 | do { 68 | V = V->prototype; 69 | if (V == self) { 70 | js_pushboolean(J, 1); 71 | return; 72 | } 73 | } while (V); 74 | } 75 | js_pushboolean(J, 0); 76 | } 77 | 78 | static void Op_propertyIsEnumerable(js_State *J) 79 | { 80 | js_Object *self = js_toobject(J, 0); 81 | const char *name = js_tostring(J, 1); 82 | js_Property *ref = jsV_getownproperty(J, self, name); 83 | js_pushboolean(J, ref && !(ref->atts & JS_DONTENUM)); 84 | } 85 | 86 | static void O_getPrototypeOf(js_State *J) 87 | { 88 | js_Object *obj; 89 | if (!js_isobject(J, 1)) 90 | js_typeerror(J, "not an object"); 91 | obj = js_toobject(J, 1); 92 | if (obj->prototype) 93 | js_pushobject(J, obj->prototype); 94 | else 95 | js_pushnull(J); 96 | } 97 | 98 | static void O_getOwnPropertyDescriptor(js_State *J) 99 | { 100 | js_Object *obj; 101 | js_Property *ref; 102 | if (!js_isobject(J, 1)) 103 | js_typeerror(J, "not an object"); 104 | obj = js_toobject(J, 1); 105 | ref = jsV_getproperty(J, obj, js_tostring(J, 2)); 106 | if (!ref) 107 | js_pushundefined(J); 108 | else { 109 | js_newobject(J); 110 | if (!ref->getter && !ref->setter) { 111 | js_pushvalue(J, ref->value); 112 | js_setproperty(J, -2, "value"); 113 | js_pushboolean(J, !(ref->atts & JS_READONLY)); 114 | js_setproperty(J, -2, "writable"); 115 | } else { 116 | if (ref->getter) 117 | js_pushobject(J, ref->getter); 118 | else 119 | js_pushundefined(J); 120 | js_setproperty(J, -2, "get"); 121 | if (ref->setter) 122 | js_pushobject(J, ref->setter); 123 | else 124 | js_pushundefined(J); 125 | js_setproperty(J, -2, "set"); 126 | } 127 | js_pushboolean(J, !(ref->atts & JS_DONTENUM)); 128 | js_setproperty(J, -2, "enumerable"); 129 | js_pushboolean(J, !(ref->atts & JS_DONTCONF)); 130 | js_setproperty(J, -2, "configurable"); 131 | } 132 | } 133 | 134 | static void O_getOwnPropertyNames(js_State *J) 135 | { 136 | js_Object *obj; 137 | js_Property *ref; 138 | unsigned int k; 139 | int i; 140 | 141 | if (!js_isobject(J, 1)) 142 | js_typeerror(J, "not an object"); 143 | obj = js_toobject(J, 1); 144 | 145 | js_newarray(J); 146 | 147 | i = 0; 148 | for (ref = obj->head; ref; ref = ref->next) { 149 | js_pushliteral(J, ref->name); 150 | js_setindex(J, -2, i++); 151 | } 152 | 153 | if (obj->type == JS_CARRAY) { 154 | js_pushliteral(J, "length"); 155 | js_setindex(J, -2, i++); 156 | } 157 | 158 | if (obj->type == JS_CSTRING) { 159 | js_pushliteral(J, "length"); 160 | js_setindex(J, -2, i++); 161 | for (k = 0; k < obj->u.s.length; ++k) { 162 | js_pushnumber(J, k); 163 | js_setindex(J, -2, i++); 164 | } 165 | } 166 | 167 | if (obj->type == JS_CREGEXP) { 168 | js_pushliteral(J, "source"); 169 | js_setindex(J, -2, i++); 170 | js_pushliteral(J, "global"); 171 | js_setindex(J, -2, i++); 172 | js_pushliteral(J, "ignoreCase"); 173 | js_setindex(J, -2, i++); 174 | js_pushliteral(J, "multiline"); 175 | js_setindex(J, -2, i++); 176 | js_pushliteral(J, "lastIndex"); 177 | js_setindex(J, -2, i++); 178 | } 179 | } 180 | 181 | static void ToPropertyDescriptor(js_State *J, js_Object *obj, const char *name, js_Object *desc) 182 | { 183 | int haswritable = 0; 184 | int hasvalue = 0; 185 | int enumerable = 0; 186 | int configurable = 0; 187 | int writable = 0; 188 | int atts = 0; 189 | 190 | js_pushobject(J, obj); 191 | js_pushobject(J, desc); 192 | 193 | if (js_hasproperty(J, -1, "writable")) { 194 | haswritable = 1; 195 | writable = js_toboolean(J, -1); 196 | js_pop(J, 1); 197 | } 198 | if (js_hasproperty(J, -1, "enumerable")) { 199 | enumerable = js_toboolean(J, -1); 200 | js_pop(J, 1); 201 | } 202 | if (js_hasproperty(J, -1, "configurable")) { 203 | configurable = js_toboolean(J, -1); 204 | js_pop(J, 1); 205 | } 206 | if (js_hasproperty(J, -1, "value")) { 207 | hasvalue = 1; 208 | js_setproperty(J, -3, name); 209 | } 210 | 211 | if (!writable) atts |= JS_READONLY; 212 | if (!enumerable) atts |= JS_DONTENUM; 213 | if (!configurable) atts |= JS_DONTCONF; 214 | 215 | if (js_hasproperty(J, -1, "get")) { 216 | if (haswritable || hasvalue) 217 | js_typeerror(J, "value/writable and get/set attributes are exclusive"); 218 | } else { 219 | js_pushundefined(J); 220 | } 221 | 222 | if (js_hasproperty(J, -2, "set")) { 223 | if (haswritable || hasvalue) 224 | js_typeerror(J, "value/writable and get/set attributes are exclusive"); 225 | } else { 226 | js_pushundefined(J); 227 | } 228 | 229 | js_defaccessor(J, -4, name, atts); 230 | 231 | js_pop(J, 2); 232 | } 233 | 234 | static void O_defineProperty(js_State *J) 235 | { 236 | if (!js_isobject(J, 1)) js_typeerror(J, "not an object"); 237 | if (!js_isobject(J, 3)) js_typeerror(J, "not an object"); 238 | ToPropertyDescriptor(J, js_toobject(J, 1), js_tostring(J, 2), js_toobject(J, 3)); 239 | js_copy(J, 1); 240 | } 241 | 242 | static void O_defineProperties(js_State *J) 243 | { 244 | js_Object *props; 245 | js_Property *ref; 246 | 247 | if (!js_isobject(J, 1)) js_typeerror(J, "not an object"); 248 | if (!js_isobject(J, 2)) js_typeerror(J, "not an object"); 249 | 250 | props = js_toobject(J, 2); 251 | for (ref = props->head; ref; ref = ref->next) { 252 | if (!(ref->atts & JS_DONTENUM)) { 253 | js_pushvalue(J, ref->value); 254 | ToPropertyDescriptor(J, js_toobject(J, 1), ref->name, js_toobject(J, -1)); 255 | js_pop(J, 1); 256 | } 257 | } 258 | 259 | js_copy(J, 1); 260 | } 261 | 262 | static void O_create(js_State *J) 263 | { 264 | js_Object *obj; 265 | js_Object *proto; 266 | js_Object *props; 267 | js_Property *ref; 268 | 269 | if (js_isobject(J, 1)) 270 | proto = js_toobject(J, 1); 271 | else if (js_isnull(J, 1)) 272 | proto = NULL; 273 | else 274 | js_typeerror(J, "not an object or null"); 275 | 276 | obj = jsV_newobject(J, JS_COBJECT, proto); 277 | js_pushobject(J, obj); 278 | 279 | if (js_isdefined(J, 2)) { 280 | if (!js_isobject(J, 2)) js_typeerror(J, "not an object"); 281 | props = js_toobject(J, 2); 282 | for (ref = props->head; ref; ref = ref->next) { 283 | if (!(ref->atts & JS_DONTENUM)) { 284 | if (ref->value.type != JS_TOBJECT) js_typeerror(J, "not an object"); 285 | ToPropertyDescriptor(J, obj, ref->name, ref->value.u.object); 286 | } 287 | } 288 | } 289 | } 290 | 291 | static void O_keys(js_State *J) 292 | { 293 | js_Object *obj; 294 | js_Property *ref; 295 | unsigned int k; 296 | int i; 297 | 298 | if (!js_isobject(J, 1)) 299 | js_typeerror(J, "not an object"); 300 | obj = js_toobject(J, 1); 301 | 302 | js_newarray(J); 303 | 304 | i = 0; 305 | for (ref = obj->head; ref; ref = ref->next) { 306 | if (!(ref->atts & JS_DONTENUM)) { 307 | js_pushliteral(J, ref->name); 308 | js_setindex(J, -2, i++); 309 | } 310 | } 311 | 312 | if (obj->type == JS_CSTRING) { 313 | for (k = 0; k < obj->u.s.length; ++k) { 314 | js_pushnumber(J, k); 315 | js_setindex(J, -2, i++); 316 | } 317 | } 318 | } 319 | 320 | static void O_preventExtensions(js_State *J) 321 | { 322 | if (!js_isobject(J, 1)) 323 | js_typeerror(J, "not an object"); 324 | js_toobject(J, 1)->extensible = 0; 325 | js_copy(J, 1); 326 | } 327 | 328 | static void O_isExtensible(js_State *J) 329 | { 330 | if (!js_isobject(J, 1)) 331 | js_typeerror(J, "not an object"); 332 | js_pushboolean(J, js_toobject(J, 1)->extensible); 333 | } 334 | 335 | static void O_seal(js_State *J) 336 | { 337 | js_Object *obj; 338 | js_Property *ref; 339 | 340 | if (!js_isobject(J, 1)) 341 | js_typeerror(J, "not an object"); 342 | 343 | obj = js_toobject(J, 1); 344 | obj->extensible = 0; 345 | 346 | for (ref = obj->head; ref; ref = ref->next) 347 | ref->atts |= JS_DONTCONF; 348 | 349 | js_copy(J, 1); 350 | } 351 | 352 | static void O_isSealed(js_State *J) 353 | { 354 | js_Object *obj; 355 | js_Property *ref; 356 | 357 | if (!js_isobject(J, 1)) 358 | js_typeerror(J, "not an object"); 359 | 360 | obj = js_toobject(J, 1); 361 | if (obj->extensible) { 362 | js_pushboolean(J, 0); 363 | return; 364 | } 365 | 366 | for (ref = obj->head; ref; ref = ref->next) { 367 | if (!(ref->atts & JS_DONTCONF)) { 368 | js_pushboolean(J, 0); 369 | return; 370 | } 371 | } 372 | 373 | js_pushboolean(J, 1); 374 | } 375 | 376 | static void O_freeze(js_State *J) 377 | { 378 | js_Object *obj; 379 | js_Property *ref; 380 | 381 | if (!js_isobject(J, 1)) 382 | js_typeerror(J, "not an object"); 383 | 384 | obj = js_toobject(J, 1); 385 | obj->extensible = 0; 386 | 387 | for (ref = obj->head; ref; ref = ref->next) 388 | ref->atts |= JS_READONLY | JS_DONTCONF; 389 | 390 | js_copy(J, 1); 391 | } 392 | 393 | static void O_isFrozen(js_State *J) 394 | { 395 | js_Object *obj; 396 | js_Property *ref; 397 | 398 | if (!js_isobject(J, 1)) 399 | js_typeerror(J, "not an object"); 400 | 401 | obj = js_toobject(J, 1); 402 | if (obj->extensible) { 403 | js_pushboolean(J, 0); 404 | return; 405 | } 406 | 407 | for (ref = obj->head; ref; ref = ref->next) { 408 | if (!(ref->atts & (JS_READONLY | JS_DONTCONF))) { 409 | js_pushboolean(J, 0); 410 | return; 411 | } 412 | } 413 | 414 | js_pushboolean(J, 1); 415 | } 416 | 417 | void jsB_initobject(js_State *J) 418 | { 419 | js_pushobject(J, J->Object_prototype); 420 | { 421 | jsB_propf(J, "toString", Op_toString, 0); 422 | jsB_propf(J, "toLocaleString", Op_toString, 0); 423 | jsB_propf(J, "valueOf", Op_valueOf, 0); 424 | jsB_propf(J, "hasOwnProperty", Op_hasOwnProperty, 1); 425 | jsB_propf(J, "isPrototypeOf", Op_isPrototypeOf, 1); 426 | jsB_propf(J, "propertyIsEnumerable", Op_propertyIsEnumerable, 1); 427 | } 428 | js_newcconstructor(J, jsB_Object, jsB_new_Object, 1); 429 | { 430 | /* ES5 */ 431 | jsB_propf(J, "getPrototypeOf", O_getPrototypeOf, 1); 432 | jsB_propf(J, "getOwnPropertyDescriptor", O_getOwnPropertyDescriptor, 2); 433 | jsB_propf(J, "getOwnPropertyNames", O_getOwnPropertyNames, 1); 434 | jsB_propf(J, "create", O_create, 2); 435 | jsB_propf(J, "defineProperty", O_defineProperty, 3); 436 | jsB_propf(J, "defineProperties", O_defineProperties, 2); 437 | jsB_propf(J, "seal", O_seal, 1); 438 | jsB_propf(J, "freeze", O_freeze, 1); 439 | jsB_propf(J, "preventExtensions", O_preventExtensions, 1); 440 | jsB_propf(J, "isSealed", O_isSealed, 1); 441 | jsB_propf(J, "isFrozen", O_isFrozen, 1); 442 | jsB_propf(J, "isExtensible", O_isExtensible, 1); 443 | jsB_propf(J, "keys", O_keys, 1); 444 | } 445 | js_defglobal(J, "Object", JS_DONTENUM); 446 | } 447 | -------------------------------------------------------------------------------- /jsvalue.c: -------------------------------------------------------------------------------- 1 | #include "jsi.h" 2 | #include "jslex.h" 3 | #include "jscompile.h" 4 | #include "jsvalue.h" 5 | #include "utf.h" 6 | 7 | double jsV_numbertointeger(double n) 8 | { 9 | double sign = n < 0 ? -1 : 1; 10 | if (isnan(n)) return 0; 11 | if (n == 0 || isinf(n)) return n; 12 | return sign * floor(abs(n)); 13 | } 14 | 15 | int jsV_numbertoint32(double n) 16 | { 17 | double two32 = 4294967296.0; 18 | double two31 = 2147483648.0; 19 | 20 | if (!isfinite(n) || n == 0) 21 | return 0; 22 | 23 | n = fmod(n, two32); 24 | n = n >= 0 ? floor(n) : ceil(n) + two32; 25 | if (n >= two31) 26 | return n - two32; 27 | else 28 | return n; 29 | } 30 | 31 | unsigned int jsV_numbertouint32(double n) 32 | { 33 | return jsV_numbertoint32(n); 34 | } 35 | 36 | short jsV_numbertoint16(double n) 37 | { 38 | return jsV_numbertoint32(n); 39 | } 40 | 41 | unsigned short jsV_numbertouint16(double n) 42 | { 43 | return jsV_numbertoint32(n); 44 | } 45 | 46 | /* obj.toString() */ 47 | static int jsV_toString(js_State *J, js_Object *obj) 48 | { 49 | js_pushobject(J, obj); 50 | js_getproperty(J, -1, "toString"); 51 | if (js_iscallable(J, -1)) { 52 | js_rot2(J); 53 | js_call(J, 0); 54 | if (js_isprimitive(J, -1)) 55 | return 1; 56 | js_pop(J, 1); 57 | return 0; 58 | } 59 | js_pop(J, 2); 60 | return 0; 61 | } 62 | 63 | /* obj.valueOf() */ 64 | static int jsV_valueOf(js_State *J, js_Object *obj) 65 | { 66 | js_pushobject(J, obj); 67 | js_getproperty(J, -1, "valueOf"); 68 | if (js_iscallable(J, -1)) { 69 | js_rot2(J); 70 | js_call(J, 0); 71 | if (js_isprimitive(J, -1)) 72 | return 1; 73 | js_pop(J, 1); 74 | return 0; 75 | } 76 | js_pop(J, 2); 77 | return 0; 78 | } 79 | 80 | /* ToPrimitive() on a value */ 81 | js_Value jsV_toprimitive(js_State *J, const js_Value *v, int preferred) 82 | { 83 | js_Value vv; 84 | js_Object *obj; 85 | 86 | if (v->type != JS_TOBJECT) 87 | return *v; 88 | 89 | obj = v->u.object; 90 | 91 | if (preferred == JS_HNONE) 92 | preferred = obj->type == JS_CDATE ? JS_HSTRING : JS_HNUMBER; 93 | 94 | if (preferred == JS_HSTRING) { 95 | if (jsV_toString(J, obj) || jsV_valueOf(J, obj)) { 96 | vv = js_tovalue(J, -1); 97 | js_pop(J, 1); 98 | return vv; 99 | } 100 | } else { 101 | if (jsV_valueOf(J, obj) || jsV_toString(J, obj)) { 102 | vv = js_tovalue(J, -1); 103 | js_pop(J, 1); 104 | return vv; 105 | } 106 | } 107 | 108 | js_typeerror(J, "cannot convert object to primitive"); 109 | } 110 | 111 | /* ToBoolean() on a value */ 112 | int jsV_toboolean(js_State *J, const js_Value *v) 113 | { 114 | switch (v->type) { 115 | default: 116 | case JS_TUNDEFINED: return 0; 117 | case JS_TNULL: return 0; 118 | case JS_TBOOLEAN: return v->u.boolean; 119 | case JS_TNUMBER: return v->u.number != 0 && !isnan(v->u.number); 120 | case JS_TSTRING: return v->u.string[0] != 0; 121 | case JS_TOBJECT: return 1; 122 | } 123 | } 124 | 125 | double js_stringtofloat(const char *s, char **ep) 126 | { 127 | char *end; 128 | double n; 129 | const char *e = s; 130 | if (*e == '+' || *e == '-') ++e; 131 | while (*e >= '0' && *e <= '9') ++e; 132 | if (*e == '.') ++e; 133 | while (*e >= '0' && *e <= '9') ++e; 134 | if (*e == 'e' || *e == 'E') { 135 | ++e; 136 | if (*e == '+' || *e == '-') ++e; 137 | while (*e >= '0' && *e <= '9') ++e; 138 | } 139 | n = js_strtod(s, &end); 140 | if (end == e) { 141 | *ep = (char*)e; 142 | return n; 143 | } 144 | *ep = (char*)s; 145 | return 0; 146 | } 147 | 148 | /* ToNumber() on a string */ 149 | double jsV_stringtonumber(js_State *J, const char *s) 150 | { 151 | char *e; 152 | double n; 153 | while (jsY_iswhite(*s) || jsY_isnewline(*s)) ++s; 154 | if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X') && s[2] != 0) 155 | n = strtol(s + 2, &e, 16); 156 | else if (!strncmp(s, "Infinity", 8)) 157 | n = INFINITY, e = (char*)s + 8; 158 | else if (!strncmp(s, "+Infinity", 9)) 159 | n = INFINITY, e = (char*)s + 9; 160 | else if (!strncmp(s, "-Infinity", 9)) 161 | n = -INFINITY, e = (char*)s + 9; 162 | else 163 | n = js_stringtofloat(s, &e); 164 | while (jsY_iswhite(*e) || jsY_isnewline(*e)) ++e; 165 | if (*e) return NAN; 166 | return n; 167 | } 168 | 169 | /* ToNumber() on a value */ 170 | double jsV_tonumber(js_State *J, const js_Value *v) 171 | { 172 | switch (v->type) { 173 | default: 174 | case JS_TUNDEFINED: return NAN; 175 | case JS_TNULL: return 0; 176 | case JS_TBOOLEAN: return v->u.boolean; 177 | case JS_TNUMBER: return v->u.number; 178 | case JS_TSTRING: return jsV_stringtonumber(J, v->u.string); 179 | case JS_TOBJECT: 180 | { 181 | js_Value vv = jsV_toprimitive(J, v, JS_HNUMBER); 182 | return jsV_tonumber(J, &vv); 183 | } 184 | } 185 | } 186 | 187 | double jsV_tointeger(js_State *J, const js_Value *v) 188 | { 189 | return jsV_numbertointeger(jsV_tonumber(J, v)); 190 | } 191 | 192 | /* ToString() on a number */ 193 | const char *jsV_numbertostring(js_State *J, double f) 194 | { 195 | char buf[32], digits[32], *p = buf, *s = digits; 196 | int exp, neg, ndigits, point; 197 | 198 | if (isnan(f)) return "NaN"; 199 | if (isinf(f)) return f < 0 ? "-Infinity" : "Infinity"; 200 | if (f == 0) return "0"; 201 | 202 | js_dtoa(f, digits, &exp, &neg, &ndigits); 203 | point = ndigits + exp; 204 | 205 | if (neg) 206 | *p++ = '-'; 207 | 208 | if (point < -5 || point > 21) { 209 | *p++ = *s++; 210 | if (ndigits > 1) { 211 | int n = ndigits - 1; 212 | *p++ = '.'; 213 | while (n--) 214 | *p++ = *s++; 215 | } 216 | js_fmtexp(p, point - 1); 217 | } 218 | 219 | else if (point <= 0) { 220 | *p++ = '0'; 221 | *p++ = '.'; 222 | while (point++ < 0) 223 | *p++ = '0'; 224 | while (ndigits-- > 0) 225 | *p++ = *s++; 226 | *p = 0; 227 | } 228 | 229 | else { 230 | while (ndigits-- > 0) { 231 | *p++ = *s++; 232 | if (--point == 0 && ndigits > 0) 233 | *p++ = '.'; 234 | } 235 | while (point-- > 0) 236 | *p++ = '0'; 237 | *p = 0; 238 | } 239 | 240 | return js_intern(J, buf); 241 | } 242 | 243 | /* ToString() on a value */ 244 | const char *jsV_tostring(js_State *J, const js_Value *v) 245 | { 246 | switch (v->type) { 247 | default: 248 | case JS_TUNDEFINED: return "undefined"; 249 | case JS_TNULL: return "null"; 250 | case JS_TBOOLEAN: return v->u.boolean ? "true" : "false"; 251 | case JS_TNUMBER: return jsV_numbertostring(J, v->u.number); 252 | case JS_TSTRING: return v->u.string; 253 | case JS_TOBJECT: 254 | { 255 | js_Value vv = jsV_toprimitive(J, v, JS_HSTRING); 256 | return jsV_tostring(J, &vv); 257 | } 258 | } 259 | } 260 | 261 | /* Objects */ 262 | 263 | static js_Object *jsV_newboolean(js_State *J, int v) 264 | { 265 | js_Object *obj = jsV_newobject(J, JS_CBOOLEAN, J->Boolean_prototype); 266 | obj->u.boolean = v; 267 | return obj; 268 | } 269 | 270 | static js_Object *jsV_newnumber(js_State *J, double v) 271 | { 272 | js_Object *obj = jsV_newobject(J, JS_CNUMBER, J->Number_prototype); 273 | obj->u.number = v; 274 | return obj; 275 | } 276 | 277 | static js_Object *jsV_newstring(js_State *J, const char *v) 278 | { 279 | js_Object *obj = jsV_newobject(J, JS_CSTRING, J->String_prototype); 280 | obj->u.s.string = v; 281 | obj->u.s.length = utflen(v); 282 | return obj; 283 | } 284 | 285 | /* ToObject() on a value */ 286 | js_Object *jsV_toobject(js_State *J, const js_Value *v) 287 | { 288 | switch (v->type) { 289 | default: 290 | case JS_TUNDEFINED: js_typeerror(J, "cannot convert undefined to object"); 291 | case JS_TNULL: js_typeerror(J, "cannot convert null to object"); 292 | case JS_TBOOLEAN: return jsV_newboolean(J, v->u.boolean); 293 | case JS_TNUMBER: return jsV_newnumber(J, v->u.number); 294 | case JS_TSTRING: return jsV_newstring(J, v->u.string); 295 | case JS_TOBJECT: return v->u.object; 296 | } 297 | } 298 | 299 | void js_newobject(js_State *J) 300 | { 301 | js_pushobject(J, jsV_newobject(J, JS_COBJECT, J->Object_prototype)); 302 | } 303 | 304 | void js_newarray(js_State *J) 305 | { 306 | js_pushobject(J, jsV_newobject(J, JS_CARRAY, J->Array_prototype)); 307 | } 308 | 309 | void js_newboolean(js_State *J, int v) 310 | { 311 | js_pushobject(J, jsV_newboolean(J, v)); 312 | } 313 | 314 | void js_newnumber(js_State *J, double v) 315 | { 316 | js_pushobject(J, jsV_newnumber(J, v)); 317 | } 318 | 319 | void js_newstring(js_State *J, const char *v) 320 | { 321 | js_pushobject(J, jsV_newstring(J, v)); 322 | } 323 | 324 | void js_newfunction(js_State *J, js_Function *fun, js_Environment *scope) 325 | { 326 | js_Object *obj = jsV_newobject(J, JS_CFUNCTION, J->Function_prototype); 327 | obj->u.f.function = fun; 328 | obj->u.f.scope = scope; 329 | js_pushobject(J, obj); 330 | { 331 | js_pushnumber(J, fun->numparams); 332 | js_defproperty(J, -2, "length", JS_READONLY | JS_DONTENUM | JS_DONTCONF); 333 | js_newobject(J); 334 | { 335 | js_copy(J, -2); 336 | js_defproperty(J, -2, "constructor", JS_DONTENUM); 337 | } 338 | js_defproperty(J, -2, "prototype", JS_DONTCONF); 339 | } 340 | } 341 | 342 | void js_newscript(js_State *J, js_Function *fun, js_Environment *scope) 343 | { 344 | js_Object *obj = jsV_newobject(J, JS_CSCRIPT, NULL); 345 | obj->u.f.function = fun; 346 | obj->u.f.scope = scope; 347 | js_pushobject(J, obj); 348 | } 349 | 350 | void js_newcfunction(js_State *J, js_CFunction cfun, unsigned int length) 351 | { 352 | js_Object *obj = jsV_newobject(J, JS_CCFUNCTION, J->Function_prototype); 353 | obj->u.c.function = cfun; 354 | obj->u.c.constructor = NULL; 355 | obj->u.c.length = length; 356 | js_pushobject(J, obj); 357 | { 358 | js_pushnumber(J, length); 359 | js_defproperty(J, -2, "length", JS_READONLY | JS_DONTENUM | JS_DONTCONF); 360 | js_newobject(J); 361 | { 362 | js_copy(J, -2); 363 | js_defproperty(J, -2, "constructor", JS_DONTENUM); 364 | } 365 | js_defproperty(J, -2, "prototype", JS_DONTCONF); 366 | } 367 | } 368 | 369 | /* prototype -- constructor */ 370 | void js_newcconstructor(js_State *J, js_CFunction cfun, js_CFunction ccon, unsigned int length) 371 | { 372 | js_Object *obj = jsV_newobject(J, JS_CCFUNCTION, J->Function_prototype); 373 | obj->u.c.function = cfun; 374 | obj->u.c.constructor = ccon; 375 | js_pushobject(J, obj); /* proto obj */ 376 | { 377 | js_pushnumber(J, length); 378 | js_defproperty(J, -2, "length", JS_READONLY | JS_DONTENUM | JS_DONTCONF); 379 | js_rot2(J); /* obj proto */ 380 | js_copy(J, -2); /* obj proto obj */ 381 | js_defproperty(J, -2, "constructor", JS_DONTENUM); 382 | js_defproperty(J, -2, "prototype", JS_READONLY | JS_DONTENUM | JS_DONTCONF); 383 | } 384 | } 385 | 386 | void js_newuserdata(js_State *J, const char *tag, void *data) 387 | { 388 | js_Object *prototype = NULL; 389 | js_Object *obj; 390 | 391 | if (js_isobject(J, -1)) 392 | prototype = js_toobject(J, -1); 393 | js_pop(J, 1); 394 | 395 | obj = jsV_newobject(J, JS_CUSERDATA, prototype); 396 | obj->u.user.tag = tag; 397 | obj->u.user.data = data; 398 | js_pushobject(J, obj); 399 | } 400 | 401 | /* Non-trivial operations on values. These are implemented using the stack. */ 402 | 403 | int js_instanceof(js_State *J) 404 | { 405 | js_Object *O, *V; 406 | 407 | if (!js_iscallable(J, -1)) 408 | js_typeerror(J, "instanceof: invalid operand"); 409 | 410 | if (!js_isobject(J, -2)) 411 | return 0; 412 | 413 | js_getproperty(J, -1, "prototype"); 414 | if (!js_isobject(J, -1)) 415 | js_typeerror(J, "instanceof: 'prototype' property is not an object"); 416 | O = js_toobject(J, -1); 417 | js_pop(J, 1); 418 | 419 | V = js_toobject(J, -2); 420 | while (V) { 421 | V = V->prototype; 422 | if (O == V) 423 | return 1; 424 | } 425 | 426 | return 0; 427 | } 428 | 429 | void js_concat(js_State *J) 430 | { 431 | js_Value va = js_toprimitive(J, -2, JS_HNONE); 432 | js_Value vb = js_toprimitive(J, -1, JS_HNONE); 433 | if (va.type == JS_TSTRING || vb.type == JS_TSTRING) { 434 | const char *sa = jsV_tostring(J, &va); 435 | const char *sb = jsV_tostring(J, &vb); 436 | char *sab = js_malloc(J, strlen(sa) + strlen(sb) + 1); 437 | strcpy(sab, sa); 438 | strcat(sab, sb); 439 | if (js_try(J)) { 440 | js_free(J, sab); 441 | js_throw(J); 442 | } 443 | js_pop(J, 2); 444 | js_pushstring(J, sab); 445 | js_endtry(J); 446 | js_free(J, sab); 447 | } else { 448 | double x = jsV_tonumber(J, &va); 449 | double y = jsV_tonumber(J, &vb); 450 | js_pop(J, 2); 451 | js_pushnumber(J, x + y); 452 | } 453 | } 454 | 455 | int js_compare(js_State *J, int *okay) 456 | { 457 | js_Value va = js_toprimitive(J, -2, JS_HNUMBER); 458 | js_Value vb = js_toprimitive(J, -1, JS_HNUMBER); 459 | 460 | *okay = 1; 461 | if (va.type == JS_TSTRING && vb.type == JS_TSTRING) { 462 | return strcmp(va.u.string, vb.u.string); 463 | } else { 464 | double x = jsV_tonumber(J, &va); 465 | double y = jsV_tonumber(J, &vb); 466 | if (isnan(x) || isnan(y)) 467 | *okay = 0; 468 | return x < y ? -1 : x > y ? 1 : 0; 469 | } 470 | } 471 | 472 | int js_equal(js_State *J) 473 | { 474 | js_Value x = js_tovalue(J, -2); 475 | js_Value y = js_tovalue(J, -1); 476 | 477 | retry: 478 | if (x.type == y.type) { 479 | if (x.type == JS_TUNDEFINED) return 1; 480 | if (x.type == JS_TNULL) return 1; 481 | if (x.type == JS_TNUMBER) return x.u.number == y.u.number; 482 | if (x.type == JS_TBOOLEAN) return x.u.boolean == y.u.boolean; 483 | if (x.type == JS_TSTRING) return !strcmp(x.u.string, y.u.string); 484 | if (x.type == JS_TOBJECT) return x.u.object == y.u.object; 485 | return 0; 486 | } 487 | 488 | if (x.type == JS_TNULL && y.type == JS_TUNDEFINED) return 1; 489 | if (x.type == JS_TUNDEFINED && y.type == JS_TNULL) return 1; 490 | 491 | if (x.type == JS_TNUMBER && y.type == JS_TSTRING) 492 | return x.u.number == jsV_tonumber(J, &y); 493 | if (x.type == JS_TSTRING && y.type == JS_TNUMBER) 494 | return jsV_tonumber(J, &x) == y.u.number; 495 | 496 | if (x.type == JS_TBOOLEAN) { 497 | x.type = JS_TNUMBER; 498 | x.u.number = x.u.boolean; 499 | goto retry; 500 | } 501 | if (y.type == JS_TBOOLEAN) { 502 | y.type = JS_TNUMBER; 503 | y.u.number = y.u.boolean; 504 | goto retry; 505 | } 506 | if ((x.type == JS_TSTRING || x.type == JS_TNUMBER) && y.type == JS_TOBJECT) { 507 | y = jsV_toprimitive(J, &y, JS_HNONE); 508 | goto retry; 509 | } 510 | if (x.type == JS_TOBJECT && (y.type == JS_TSTRING || y.type == JS_TNUMBER)) { 511 | x = jsV_toprimitive(J, &x, JS_HNONE); 512 | goto retry; 513 | } 514 | 515 | return 0; 516 | } 517 | 518 | int js_strictequal(js_State *J) 519 | { 520 | js_Value va = js_tovalue(J, -2); 521 | js_Value vb = js_tovalue(J, -1); 522 | 523 | if (va.type != vb.type) return 0; 524 | if (va.type == JS_TUNDEFINED) return 1; 525 | if (va.type == JS_TNULL) return 1; 526 | if (va.type == JS_TNUMBER) return va.u.number == vb.u.number; 527 | if (va.type == JS_TBOOLEAN) return va.u.boolean == vb.u.boolean; 528 | if (va.type == JS_TSTRING) return !strcmp(va.u.string, vb.u.string); 529 | if (va.type == JS_TOBJECT) return va.u.object == vb.u.object; 530 | return 0; 531 | } 532 | -------------------------------------------------------------------------------- /jsarray.c: -------------------------------------------------------------------------------- 1 | #include "jsi.h" 2 | #include "jsvalue.h" 3 | #include "jsbuiltin.h" 4 | 5 | unsigned int js_getlength(js_State *J, int idx) 6 | { 7 | unsigned int len; 8 | js_getproperty(J, idx, "length"); 9 | len = js_touint32(J, -1); 10 | js_pop(J, 1); 11 | return len; 12 | } 13 | 14 | void js_setlength(js_State *J, int idx, unsigned int len) 15 | { 16 | js_pushnumber(J, len); 17 | js_setproperty(J, idx, "length"); 18 | } 19 | 20 | int js_hasindex(js_State *J, int idx, unsigned int i) 21 | { 22 | char buf[32]; 23 | sprintf(buf, "%u", i); 24 | return js_hasproperty(J, idx, buf); 25 | } 26 | 27 | void js_getindex(js_State *J, int idx, unsigned int i) 28 | { 29 | char buf[32]; 30 | sprintf(buf, "%u", i); 31 | js_getproperty(J, idx, buf); 32 | } 33 | 34 | void js_setindex(js_State *J, int idx, unsigned int i) 35 | { 36 | char buf[32]; 37 | sprintf(buf, "%u", i); 38 | js_setproperty(J, idx, buf); 39 | } 40 | 41 | void js_delindex(js_State *J, int idx, unsigned int i) 42 | { 43 | char buf[32]; 44 | sprintf(buf, "%u", i); 45 | js_delproperty(J, idx, buf); 46 | } 47 | 48 | static void jsB_new_Array(js_State *J) 49 | { 50 | unsigned int i, top = js_gettop(J); 51 | 52 | js_newarray(J); 53 | 54 | if (top == 2) { 55 | if (js_isnumber(J, 1)) { 56 | js_copy(J, 1); 57 | js_setproperty(J, -2, "length"); 58 | } else { 59 | js_copy(J, 1); 60 | js_setindex(J, -2, 0); 61 | } 62 | } else { 63 | for (i = 1; i < top; ++i) { 64 | js_copy(J, i); 65 | js_setindex(J, -2, i - 1); 66 | } 67 | } 68 | } 69 | 70 | static void Ap_concat(js_State *J) 71 | { 72 | unsigned int i, top = js_gettop(J); 73 | unsigned int n, k, len; 74 | 75 | js_newarray(J); 76 | n = 0; 77 | 78 | for (i = 0; i < top; ++i) { 79 | js_copy(J, i); 80 | if (js_isarray(J, -1)) { 81 | len = js_getlength(J, -1); 82 | for (k = 0; k < len; ++k) 83 | if (js_hasindex(J, -1, k)) 84 | js_setindex(J, -3, n++); 85 | js_pop(J, 1); 86 | } else { 87 | js_setindex(J, -2, n++); 88 | } 89 | } 90 | } 91 | 92 | static void Ap_join(js_State *J) 93 | { 94 | char * volatile out = NULL; 95 | const char *sep; 96 | const char *r; 97 | unsigned int seplen; 98 | unsigned int k, n, len; 99 | 100 | len = js_getlength(J, 0); 101 | 102 | if (js_isdefined(J, 1)) { 103 | sep = js_tostring(J, 1); 104 | seplen = strlen(sep); 105 | } else { 106 | sep = ","; 107 | seplen = 1; 108 | } 109 | 110 | if (len == 0) { 111 | js_pushliteral(J, ""); 112 | return; 113 | } 114 | 115 | if (js_try(J)) { 116 | js_free(J, out); 117 | js_throw(J); 118 | } 119 | 120 | n = 1; 121 | for (k = 0; k < len; ++k) { 122 | js_getindex(J, 0, k); 123 | if (js_isundefined(J, -1) || js_isnull(J, -1)) 124 | r = ""; 125 | else 126 | r = js_tostring(J, -1); 127 | n += strlen(r); 128 | 129 | if (k == 0) { 130 | out = js_malloc(J, n); 131 | strcpy(out, r); 132 | } else { 133 | n += seplen; 134 | out = realloc(out, n); 135 | strcat(out, sep); 136 | strcat(out, r); 137 | } 138 | 139 | js_pop(J, 1); 140 | } 141 | 142 | js_pushstring(J, out); 143 | js_endtry(J); 144 | js_free(J, out); 145 | } 146 | 147 | static void Ap_pop(js_State *J) 148 | { 149 | unsigned int n; 150 | 151 | n = js_getlength(J, 0); 152 | 153 | if (n > 0) { 154 | js_getindex(J, 0, n - 1); 155 | js_delindex(J, 0, n - 1); 156 | js_setlength(J, 0, n - 1); 157 | } else { 158 | js_setlength(J, 0, 0); 159 | js_pushundefined(J); 160 | } 161 | } 162 | 163 | static void Ap_push(js_State *J) 164 | { 165 | unsigned int i, top = js_gettop(J); 166 | unsigned int n; 167 | 168 | n = js_getlength(J, 0); 169 | 170 | for (i = 1; i < top; ++i, ++n) { 171 | js_copy(J, i); 172 | js_setindex(J, 0, n); 173 | } 174 | 175 | js_setlength(J, 0, n); 176 | 177 | js_pushnumber(J, n); 178 | } 179 | 180 | static void Ap_reverse(js_State *J) 181 | { 182 | unsigned int len, middle, lower; 183 | 184 | len = js_getlength(J, 0); 185 | middle = len / 2; 186 | lower = 0; 187 | 188 | while (lower != middle) { 189 | unsigned int upper = len - lower - 1; 190 | int haslower = js_hasindex(J, 0, lower); 191 | int hasupper = js_hasindex(J, 0, upper); 192 | if (haslower && hasupper) { 193 | js_setindex(J, 0, lower); 194 | js_setindex(J, 0, upper); 195 | } else if (hasupper) { 196 | js_setindex(J, 0, lower); 197 | js_delindex(J, 0, upper); 198 | } else if (haslower) { 199 | js_setindex(J, 0, upper); 200 | js_delindex(J, 0, lower); 201 | } 202 | ++lower; 203 | } 204 | 205 | js_copy(J, 0); 206 | } 207 | 208 | static void Ap_shift(js_State *J) 209 | { 210 | unsigned int k, len; 211 | 212 | len = js_getlength(J, 0); 213 | 214 | if (len == 0) { 215 | js_setlength(J, 0, 0); 216 | js_pushundefined(J); 217 | return; 218 | } 219 | 220 | js_getindex(J, 0, 0); 221 | 222 | for (k = 1; k < len; ++k) { 223 | if (js_hasindex(J, 0, k)) 224 | js_setindex(J, 0, k - 1); 225 | else 226 | js_delindex(J, 0, k - 1); 227 | } 228 | 229 | js_delindex(J, 0, len - 1); 230 | js_setlength(J, 0, len - 1); 231 | } 232 | 233 | static void Ap_slice(js_State *J) 234 | { 235 | unsigned int len, s, e, n; 236 | double sv, ev; 237 | 238 | js_newarray(J); 239 | 240 | len = js_getlength(J, 0); 241 | sv = js_tointeger(J, 1); 242 | ev = js_isdefined(J, 2) ? js_tointeger(J, 2) : len; 243 | 244 | if (sv < 0) sv = sv + len; 245 | if (ev < 0) ev = ev + len; 246 | 247 | s = sv < 0 ? 0 : sv > len ? len : sv; 248 | e = ev < 0 ? 0 : ev > len ? len : ev; 249 | 250 | for (n = 0; s < e; ++s, ++n) 251 | if (js_hasindex(J, 0, s)) 252 | js_setindex(J, -2, n); 253 | } 254 | 255 | static int compare(js_State *J, unsigned int x, unsigned int y, int *hasx, int *hasy, int hasfn) 256 | { 257 | const char *sx, *sy; 258 | int c; 259 | 260 | *hasx = js_hasindex(J, 0, x); 261 | *hasy = js_hasindex(J, 0, y); 262 | 263 | if (*hasx && *hasy) { 264 | int unx = js_isundefined(J, -2); 265 | int uny = js_isundefined(J, -1); 266 | if (unx && uny) return 0; 267 | if (unx) return 1; 268 | if (uny) return -1; 269 | 270 | if (hasfn) { 271 | js_copy(J, 1); /* copy function */ 272 | js_pushglobal(J); /* set this object */ 273 | js_copy(J, -4); /* copy x */ 274 | js_copy(J, -4); /* copy y */ 275 | js_call(J, 2); 276 | c = js_tonumber(J, -1); 277 | js_pop(J, 1); 278 | return c; 279 | } 280 | 281 | sx = js_tostring(J, -2); 282 | sy = js_tostring(J, -1); 283 | return strcmp(sx, sy); 284 | } 285 | 286 | if (*hasx) return -1; 287 | if (*hasy) return 1; 288 | return 0; 289 | } 290 | 291 | static void Ap_sort(js_State *J) 292 | { 293 | unsigned int len, i, k; 294 | int hasx, hasy, hasfn; 295 | 296 | len = js_getlength(J, 0); 297 | 298 | hasfn = js_iscallable(J, 1); 299 | 300 | for (i = 1; i < len; ++i) { 301 | k = i; 302 | while (k > 0 && compare(J, k - 1, k, &hasx, &hasy, hasfn) > 0) { 303 | if (hasx && hasy) { 304 | js_setindex(J, 0, k - 1); 305 | js_setindex(J, 0, k); 306 | } else if (hasx) { 307 | js_delindex(J, 0, k - 1); 308 | js_setindex(J, 0, k); 309 | } else if (hasy) { 310 | js_setindex(J, 0, k - 1); 311 | js_delindex(J, 0, k); 312 | } 313 | --k; 314 | } 315 | } 316 | 317 | js_copy(J, 0); 318 | } 319 | 320 | static void Ap_splice(js_State *J) 321 | { 322 | unsigned int top = js_gettop(J); 323 | unsigned int len, start, del, add, k; 324 | double f; 325 | 326 | js_newarray(J); 327 | 328 | len = js_getlength(J, 0); 329 | 330 | f = js_tointeger(J, 1); 331 | if (f < 0) f = f + len; 332 | start = f < 0 ? 0 : f > len ? len : f; 333 | 334 | f = js_tointeger(J, 2); 335 | del = f < 0 ? 0 : f > len - start ? len - start : f; 336 | 337 | /* copy deleted items to return array */ 338 | for (k = 0; k < del; ++k) 339 | if (js_hasindex(J, 0, start + k)) 340 | js_setindex(J, -2, k); 341 | 342 | /* shift the tail to resize the hole left by deleted items */ 343 | add = top - 3; 344 | if (add < del) { 345 | for (k = start; k < len - del; ++k) { 346 | if (js_hasindex(J, 0, k + del)) 347 | js_setindex(J, 0, k + add); 348 | else 349 | js_delindex(J, 0, k + del); 350 | } 351 | for (k = len; k > len - del + add; --k) 352 | js_delindex(J, 0, k - 1); 353 | } else if (add > del) { 354 | for (k = len - del; k > start; --k) { 355 | if (js_hasindex(J, 0, k + del - 1)) 356 | js_setindex(J, 0, k + add - 1); 357 | else 358 | js_delindex(J, 0, k + add - 1); 359 | } 360 | } 361 | 362 | /* copy new items into the hole */ 363 | for (k = 0; k < add; ++k) { 364 | js_copy(J, 3 + k); 365 | js_setindex(J, 0, start + k); 366 | } 367 | 368 | js_setlength(J, 0, len - del + add); 369 | } 370 | 371 | static void Ap_unshift(js_State *J) 372 | { 373 | unsigned int i, top = js_gettop(J); 374 | unsigned int k, len; 375 | 376 | len = js_getlength(J, 0); 377 | 378 | for (k = len; k > 0; --k) { 379 | int from = k - 1; 380 | int to = k + top - 2; 381 | if (js_hasindex(J, 0, from)) 382 | js_setindex(J, 0, to); 383 | else 384 | js_delindex(J, 0, to); 385 | } 386 | 387 | for (i = 1; i < top; ++i) { 388 | js_copy(J, i); 389 | js_setindex(J, 0, i - 1); 390 | } 391 | 392 | js_setlength(J, 0, len + top - 1); 393 | 394 | js_pushnumber(J, len + top - 1); 395 | } 396 | 397 | static void Ap_toString(js_State *J) 398 | { 399 | unsigned int top = js_gettop(J); 400 | js_pop(J, top - 1); 401 | Ap_join(J); 402 | } 403 | 404 | static void Ap_indexOf(js_State *J) 405 | { 406 | int k, len, from; 407 | 408 | len = js_getlength(J, 0); 409 | from = js_isdefined(J, 2) ? js_tointeger(J, 2) : 0; 410 | if (from < 0) from = len + from; 411 | if (from < 0) from = 0; 412 | 413 | js_copy(J, 1); 414 | for (k = from; k < len; ++k) { 415 | if (js_hasindex(J, 0, k)) { 416 | if (js_strictequal(J)) { 417 | js_pushnumber(J, k); 418 | return; 419 | } 420 | js_pop(J, 1); 421 | } 422 | } 423 | 424 | js_pushnumber(J, -1); 425 | } 426 | 427 | static void Ap_lastIndexOf(js_State *J) 428 | { 429 | int k, len, from; 430 | 431 | len = js_getlength(J, 0); 432 | from = js_isdefined(J, 2) ? js_tointeger(J, 2) : len - 1; 433 | if (from > len - 1) from = len - 1; 434 | if (from < 0) from = len + from; 435 | 436 | js_copy(J, 1); 437 | for (k = from; k >= 0; --k) { 438 | if (js_hasindex(J, 0, k)) { 439 | if (js_strictequal(J)) { 440 | js_pushnumber(J, k); 441 | return; 442 | } 443 | js_pop(J, 1); 444 | } 445 | } 446 | 447 | js_pushnumber(J, -1); 448 | } 449 | 450 | static void Ap_every(js_State *J) 451 | { 452 | int hasthis = js_gettop(J) >= 3; 453 | int k, len; 454 | 455 | if (!js_iscallable(J, 1)) 456 | js_typeerror(J, "callback is not a function"); 457 | 458 | len = js_getlength(J, 0); 459 | for (k = 0; k < len; ++k) { 460 | if (js_hasindex(J, 0, k)) { 461 | js_copy(J, 1); 462 | if (hasthis) 463 | js_copy(J, 2); 464 | else 465 | js_pushundefined(J); 466 | js_copy(J, -3); 467 | js_pushnumber(J, k); 468 | js_copy(J, 0); 469 | js_call(J, 3); 470 | if (!js_toboolean(J, -1)) 471 | return; 472 | js_pop(J, 2); 473 | } 474 | } 475 | 476 | js_pushboolean(J, 1); 477 | } 478 | 479 | static void Ap_some(js_State *J) 480 | { 481 | int hasthis = js_gettop(J) >= 3; 482 | int k, len; 483 | 484 | if (!js_iscallable(J, 1)) 485 | js_typeerror(J, "callback is not a function"); 486 | 487 | len = js_getlength(J, 0); 488 | for (k = 0; k < len; ++k) { 489 | if (js_hasindex(J, 0, k)) { 490 | js_copy(J, 1); 491 | if (hasthis) 492 | js_copy(J, 2); 493 | else 494 | js_pushundefined(J); 495 | js_copy(J, -3); 496 | js_pushnumber(J, k); 497 | js_copy(J, 0); 498 | js_call(J, 3); 499 | if (js_toboolean(J, -1)) 500 | return; 501 | js_pop(J, 2); 502 | } 503 | } 504 | 505 | js_pushboolean(J, 0); 506 | } 507 | 508 | static void Ap_forEach(js_State *J) 509 | { 510 | int hasthis = js_gettop(J) >= 3; 511 | int k, len; 512 | 513 | if (!js_iscallable(J, 1)) 514 | js_typeerror(J, "callback is not a function"); 515 | 516 | len = js_getlength(J, 0); 517 | for (k = 0; k < len; ++k) { 518 | if (js_hasindex(J, 0, k)) { 519 | js_copy(J, 1); 520 | if (hasthis) 521 | js_copy(J, 2); 522 | else 523 | js_pushundefined(J); 524 | js_copy(J, -3); 525 | js_pushnumber(J, k); 526 | js_copy(J, 0); 527 | js_call(J, 3); 528 | js_pop(J, 2); 529 | } 530 | } 531 | 532 | js_pushundefined(J); 533 | } 534 | 535 | static void Ap_map(js_State *J) 536 | { 537 | int hasthis = js_gettop(J) >= 3; 538 | int k, len; 539 | 540 | if (!js_iscallable(J, 1)) 541 | js_typeerror(J, "callback is not a function"); 542 | 543 | js_newarray(J); 544 | 545 | len = js_getlength(J, 0); 546 | for (k = 0; k < len; ++k) { 547 | if (js_hasindex(J, 0, k)) { 548 | js_copy(J, 1); 549 | if (hasthis) 550 | js_copy(J, 2); 551 | else 552 | js_pushundefined(J); 553 | js_copy(J, -3); 554 | js_pushnumber(J, k); 555 | js_copy(J, 0); 556 | js_call(J, 3); 557 | js_setindex(J, -3, k); 558 | js_pop(J, 1); 559 | } 560 | } 561 | } 562 | 563 | static void Ap_filter(js_State *J) 564 | { 565 | int hasthis = js_gettop(J) >= 3; 566 | int k, to, len; 567 | 568 | if (!js_iscallable(J, 1)) 569 | js_typeerror(J, "callback is not a function"); 570 | 571 | js_newarray(J); 572 | to = 0; 573 | 574 | len = js_getlength(J, 0); 575 | for (k = 0; k < len; ++k) { 576 | if (js_hasindex(J, 0, k)) { 577 | js_copy(J, 1); 578 | if (hasthis) 579 | js_copy(J, 2); 580 | else 581 | js_pushundefined(J); 582 | js_copy(J, -3); 583 | js_pushnumber(J, k); 584 | js_copy(J, 0); 585 | js_call(J, 3); 586 | if (js_toboolean(J, -1)) { 587 | js_pop(J, 1); 588 | js_setindex(J, -2, to++); 589 | } else { 590 | js_pop(J, 2); 591 | } 592 | } 593 | } 594 | } 595 | 596 | static void Ap_reduce(js_State *J) 597 | { 598 | int hasinitial = js_gettop(J) >= 3; 599 | int k, len; 600 | 601 | if (!js_iscallable(J, 1)) 602 | js_typeerror(J, "callback is not a function"); 603 | 604 | len = js_getlength(J, 0); 605 | k = 0; 606 | 607 | if (len == 0 && !hasinitial) 608 | js_typeerror(J, "no initial value"); 609 | 610 | /* initial value of accumulator */ 611 | if (hasinitial) 612 | js_copy(J, 2); 613 | else { 614 | while (k < len) 615 | if (js_hasindex(J, 0, k++)) 616 | break; 617 | if (k == len) 618 | js_typeerror(J, "no initial value"); 619 | } 620 | 621 | while (k < len) { 622 | if (js_hasindex(J, 0, k)) { 623 | js_copy(J, 1); 624 | js_pushundefined(J); 625 | js_rot(J, 4); /* accumulator on top */ 626 | js_rot(J, 4); /* property on top */ 627 | js_pushnumber(J, k); 628 | js_copy(J, 0); 629 | js_call(J, 4); /* calculate new accumulator */ 630 | } 631 | ++k; 632 | } 633 | 634 | /* return accumulator */ 635 | } 636 | 637 | static void Ap_reduceRight(js_State *J) 638 | { 639 | int hasinitial = js_gettop(J) >= 3; 640 | int k, len; 641 | 642 | if (!js_iscallable(J, 1)) 643 | js_typeerror(J, "callback is not a function"); 644 | 645 | len = js_getlength(J, 0); 646 | k = len - 1; 647 | 648 | if (len == 0 && !hasinitial) 649 | js_typeerror(J, "no initial value"); 650 | 651 | /* initial value of accumulator */ 652 | if (hasinitial) 653 | js_copy(J, 2); 654 | else { 655 | while (k >= 0) 656 | if (js_hasindex(J, 0, k--)) 657 | break; 658 | if (k < 0) 659 | js_typeerror(J, "no initial value"); 660 | } 661 | 662 | while (k >= 0) { 663 | if (js_hasindex(J, 0, k)) { 664 | js_copy(J, 1); 665 | js_pushundefined(J); 666 | js_rot(J, 4); /* accumulator on top */ 667 | js_rot(J, 4); /* property on top */ 668 | js_pushnumber(J, k); 669 | js_copy(J, 0); 670 | js_call(J, 4); /* calculate new accumulator */ 671 | } 672 | --k; 673 | } 674 | 675 | /* return accumulator */ 676 | } 677 | 678 | static void A_isArray(js_State *J) 679 | { 680 | if (js_isobject(J, 1)) { 681 | js_Object *T = js_toobject(J, 1); 682 | js_pushboolean(J, T->type == JS_CARRAY); 683 | } else { 684 | js_pushboolean(J, 0); 685 | } 686 | } 687 | 688 | void jsB_initarray(js_State *J) 689 | { 690 | js_pushobject(J, J->Array_prototype); 691 | { 692 | jsB_propf(J, "toString", Ap_toString, 0); 693 | jsB_propf(J, "concat", Ap_concat, 1); 694 | jsB_propf(J, "join", Ap_join, 1); 695 | jsB_propf(J, "pop", Ap_pop, 0); 696 | jsB_propf(J, "push", Ap_push, 1); 697 | jsB_propf(J, "reverse", Ap_reverse, 0); 698 | jsB_propf(J, "shift", Ap_shift, 0); 699 | jsB_propf(J, "slice", Ap_slice, 2); 700 | jsB_propf(J, "sort", Ap_sort, 1); 701 | jsB_propf(J, "splice", Ap_splice, 2); 702 | jsB_propf(J, "unshift", Ap_unshift, 1); 703 | 704 | /* ES5 */ 705 | jsB_propf(J, "indexOf", Ap_indexOf, 1); 706 | jsB_propf(J, "lastIndexOf", Ap_lastIndexOf, 1); 707 | jsB_propf(J, "every", Ap_every, 1); 708 | jsB_propf(J, "some", Ap_some, 1); 709 | jsB_propf(J, "forEach", Ap_forEach, 1); 710 | jsB_propf(J, "map", Ap_map, 1); 711 | jsB_propf(J, "filter", Ap_filter, 1); 712 | jsB_propf(J, "reduce", Ap_reduce, 1); 713 | jsB_propf(J, "reduceRight", Ap_reduceRight, 1); 714 | } 715 | js_newcconstructor(J, jsB_new_Array, jsB_new_Array, 1); 716 | { 717 | /* ES5 */ 718 | jsB_propf(J, "isArray", A_isArray, 1); 719 | } 720 | js_defglobal(J, "Array", JS_DONTENUM); 721 | } 722 | -------------------------------------------------------------------------------- /jsstring.c: -------------------------------------------------------------------------------- 1 | #include "jsi.h" 2 | #include "jsvalue.h" 3 | #include "jsbuiltin.h" 4 | #include "utf.h" 5 | #include "regex.h" 6 | 7 | int js_runeat(js_State *J, const char *s, int i) 8 | { 9 | Rune rune = 0; 10 | while (i-- >= 0) { 11 | rune = *(unsigned char*)s; 12 | if (rune < Runeself) { 13 | if (rune == 0) 14 | return 0; 15 | ++s; 16 | } else 17 | s += chartorune(&rune, s); 18 | } 19 | return rune; 20 | } 21 | 22 | const char *js_utfidxtoptr(const char *s, int i) 23 | { 24 | Rune rune; 25 | while (i-- > 0) { 26 | rune = *(unsigned char*)s; 27 | if (rune < Runeself) { 28 | if (rune == 0) 29 | return NULL; 30 | ++s; 31 | } else 32 | s += chartorune(&rune, s); 33 | } 34 | return s; 35 | } 36 | 37 | int js_utfptrtoidx(const char *s, const char *p) 38 | { 39 | Rune rune; 40 | int i = 0; 41 | while (s < p) { 42 | if (*(unsigned char *)s < Runeself) 43 | ++s; 44 | else 45 | s += chartorune(&rune, s); 46 | ++i; 47 | } 48 | return i; 49 | } 50 | 51 | static void jsB_new_String(js_State *J) 52 | { 53 | js_newstring(J, js_gettop(J) > 1 ? js_tostring(J, 1) : ""); 54 | } 55 | 56 | static void jsB_String(js_State *J) 57 | { 58 | js_pushliteral(J, js_gettop(J) > 1 ? js_tostring(J, 1) : ""); 59 | } 60 | 61 | static void Sp_toString(js_State *J) 62 | { 63 | js_Object *self = js_toobject(J, 0); 64 | if (self->type != JS_CSTRING) js_typeerror(J, "not a string"); 65 | js_pushliteral(J, self->u.s.string); 66 | } 67 | 68 | static void Sp_valueOf(js_State *J) 69 | { 70 | js_Object *self = js_toobject(J, 0); 71 | if (self->type != JS_CSTRING) js_typeerror(J, "not a string"); 72 | js_pushliteral(J, self->u.s.string); 73 | } 74 | 75 | static void Sp_charAt(js_State *J) 76 | { 77 | char buf[UTFmax + 1]; 78 | const char *s = js_tostring(J, 0); 79 | int pos = js_tointeger(J, 1); 80 | Rune rune = js_runeat(J, s, pos); 81 | if (rune > 0) { 82 | buf[runetochar(buf, &rune)] = 0; 83 | js_pushstring(J, buf); 84 | } else { 85 | js_pushliteral(J, ""); 86 | } 87 | } 88 | 89 | static void Sp_charCodeAt(js_State *J) 90 | { 91 | const char *s = js_tostring(J, 0); 92 | int pos = js_tointeger(J, 1); 93 | Rune rune = js_runeat(J, s, pos); 94 | if (rune > 0) 95 | js_pushnumber(J, rune); 96 | else 97 | js_pushnumber(J, NAN); 98 | } 99 | 100 | static void Sp_concat(js_State *J) 101 | { 102 | unsigned int i, top = js_gettop(J); 103 | unsigned int n; 104 | char * volatile out; 105 | const char *s; 106 | 107 | if (top == 1) 108 | return; 109 | 110 | s = js_tostring(J, 0); 111 | n = strlen(s); 112 | out = js_malloc(J, n + 1); 113 | strcpy(out, s); 114 | 115 | if (js_try(J)) { 116 | js_free(J, out); 117 | js_throw(J); 118 | } 119 | 120 | for (i = 1; i < top; ++i) { 121 | s = js_tostring(J, i); 122 | n += strlen(s); 123 | out = realloc(out, n + 1); 124 | strcat(out, s); 125 | } 126 | 127 | js_pushstring(J, out); 128 | js_endtry(J); 129 | js_free(J, out); 130 | } 131 | 132 | static void Sp_indexOf(js_State *J) 133 | { 134 | const char *haystack = js_tostring(J, 0); 135 | const char *needle = js_tostring(J, 1); 136 | int pos = js_tointeger(J, 2); 137 | int len = strlen(needle); 138 | int k = 0; 139 | Rune rune; 140 | while (*haystack) { 141 | if (k >= pos && !strncmp(haystack, needle, len)) { 142 | js_pushnumber(J, k); 143 | return; 144 | } 145 | haystack += chartorune(&rune, haystack); 146 | ++k; 147 | } 148 | js_pushnumber(J, -1); 149 | } 150 | 151 | static void Sp_lastIndexOf(js_State *J) 152 | { 153 | const char *haystack = js_tostring(J, 0); 154 | const char *needle = js_tostring(J, 1); 155 | int pos = js_isdefined(J, 2) ? js_tointeger(J, 2) : strlen(haystack); 156 | int len = strlen(needle); 157 | int k = 0, last = -1; 158 | Rune rune; 159 | while (*haystack && k <= pos) { 160 | if (!strncmp(haystack, needle, len)) 161 | last = k; 162 | haystack += chartorune(&rune, haystack); 163 | ++k; 164 | } 165 | js_pushnumber(J, last); 166 | } 167 | 168 | static void Sp_localeCompare(js_State *J) 169 | { 170 | const char *a = js_tostring(J, 0); 171 | const char *b = js_tostring(J, 1); 172 | js_pushnumber(J, strcmp(a, b)); 173 | } 174 | 175 | static void Sp_slice(js_State *J) 176 | { 177 | const char *str = js_tostring(J, 0); 178 | const char *ss, *ee; 179 | int len = utflen(str); 180 | int s = js_tointeger(J, 1); 181 | int e = js_isdefined(J, 2) ? js_tointeger(J, 2) : len; 182 | 183 | s = s < 0 ? s + len : s; 184 | e = e < 0 ? e + len : e; 185 | 186 | s = s < 0 ? 0 : s > len ? len : s; 187 | e = e < 0 ? 0 : e > len ? len : e; 188 | 189 | if (s < e) { 190 | ss = js_utfidxtoptr(str, s); 191 | ee = js_utfidxtoptr(ss, e - s); 192 | } else { 193 | ss = js_utfidxtoptr(str, e); 194 | ee = js_utfidxtoptr(ss, s - e); 195 | } 196 | 197 | js_pushlstring(J, ss, ee - ss); 198 | } 199 | 200 | static void Sp_substring(js_State *J) 201 | { 202 | const char *str = js_tostring(J, 0); 203 | const char *ss, *ee; 204 | int len = utflen(str); 205 | int s = js_tointeger(J, 1); 206 | int e = js_isdefined(J, 2) ? js_tointeger(J, 2) : len; 207 | 208 | s = s < 0 ? 0 : s > len ? len : s; 209 | e = e < 0 ? 0 : e > len ? len : e; 210 | 211 | if (s < e) { 212 | ss = js_utfidxtoptr(str, s); 213 | ee = js_utfidxtoptr(ss, e - s); 214 | } else { 215 | ss = js_utfidxtoptr(str, e); 216 | ee = js_utfidxtoptr(ss, s - e); 217 | } 218 | 219 | js_pushlstring(J, ss, ee - ss); 220 | } 221 | 222 | static void Sp_toLowerCase(js_State *J) 223 | { 224 | const char *src = js_tostring(J, 0); 225 | char *dst = js_malloc(J, UTFmax * strlen(src) + 1); 226 | const char *s = src; 227 | char *d = dst; 228 | Rune rune; 229 | while (*s) { 230 | s += chartorune(&rune, s); 231 | rune = tolowerrune(rune); 232 | d += runetochar(d, &rune); 233 | } 234 | *d = 0; 235 | if (js_try(J)) { 236 | js_free(J, dst); 237 | js_throw(J); 238 | } 239 | js_pushstring(J, dst); 240 | js_endtry(J); 241 | js_free(J, dst); 242 | } 243 | 244 | static void Sp_toUpperCase(js_State *J) 245 | { 246 | const char *src = js_tostring(J, 0); 247 | char *dst = js_malloc(J, UTFmax * strlen(src) + 1); 248 | const char *s = src; 249 | char *d = dst; 250 | Rune rune; 251 | while (*s) { 252 | s += chartorune(&rune, s); 253 | rune = toupperrune(rune); 254 | d += runetochar(d, &rune); 255 | } 256 | *d = 0; 257 | if (js_try(J)) { 258 | js_free(J, dst); 259 | js_throw(J); 260 | } 261 | js_pushstring(J, dst); 262 | js_endtry(J); 263 | js_free(J, dst); 264 | } 265 | 266 | static int istrim(int c) 267 | { 268 | return c == 0x9 || c == 0xB || c == 0xC || c == 0x20 || c == 0xA0 || c == 0xFEFF || 269 | c == 0xA || c == 0xD || c == 0x2028 || c == 0x2029; 270 | } 271 | 272 | static void Sp_trim(js_State *J) 273 | { 274 | const char *s, *e; 275 | s = js_tostring(J, 0); 276 | while (istrim(*s)) 277 | ++s; 278 | e = s + strlen(s); 279 | while (e > s && istrim(e[-1])) 280 | --e; 281 | js_pushlstring(J, s, e - s); 282 | } 283 | 284 | static void S_fromCharCode(js_State *J) 285 | { 286 | unsigned int i, top = js_gettop(J); 287 | Rune c; 288 | char *s, *p; 289 | 290 | s = p = js_malloc(J, (top-1) * UTFmax + 1); 291 | 292 | if (js_try(J)) { 293 | js_free(J, s); 294 | js_throw(J); 295 | } 296 | 297 | for (i = 1; i < top; ++i) { 298 | c = js_touint16(J, i); 299 | p += runetochar(p, &c); 300 | } 301 | *p = 0; 302 | js_pushstring(J, s); 303 | 304 | js_endtry(J); 305 | js_free(J, s); 306 | } 307 | 308 | static void Sp_match(js_State *J) 309 | { 310 | js_Regexp *re; 311 | const char *text; 312 | unsigned int len; 313 | const char *a, *b, *c, *e; 314 | Resub m; 315 | 316 | text = js_tostring(J, 0); 317 | 318 | if (js_isregexp(J, 1)) 319 | js_copy(J, 1); 320 | else if (js_isundefined(J, 1)) 321 | js_newregexp(J, "", 0); 322 | else 323 | js_newregexp(J, js_tostring(J, 1), 0); 324 | 325 | re = js_toregexp(J, -1); 326 | if (!(re->flags & JS_REGEXP_G)) { 327 | js_RegExp_prototype_exec(J, re, text); 328 | return; 329 | } 330 | 331 | re->last = 0; 332 | 333 | js_newarray(J); 334 | 335 | len = 0; 336 | a = text; 337 | e = text + strlen(text); 338 | while (a <= e) { 339 | if (js_regexec(re->prog, a, &m, a > text ? REG_NOTBOL : 0)) 340 | break; 341 | 342 | b = m.sub[0].sp; 343 | c = m.sub[0].ep; 344 | 345 | js_pushlstring(J, b, c - b); 346 | js_setindex(J, -2, len++); 347 | 348 | a = c; 349 | if (c - b == 0) 350 | ++a; 351 | } 352 | } 353 | 354 | static void Sp_search(js_State *J) 355 | { 356 | js_Regexp *re; 357 | const char *text; 358 | Resub m; 359 | 360 | text = js_tostring(J, 0); 361 | 362 | if (js_isregexp(J, 1)) 363 | js_copy(J, 1); 364 | else if (js_isundefined(J, 1)) 365 | js_newregexp(J, "", 0); 366 | else 367 | js_newregexp(J, js_tostring(J, 1), 0); 368 | 369 | re = js_toregexp(J, -1); 370 | 371 | if (!js_regexec(re->prog, text, &m, 0)) 372 | js_pushnumber(J, js_utfptrtoidx(text, m.sub[0].sp)); 373 | else 374 | js_pushnumber(J, -1); 375 | } 376 | 377 | static void Sp_replace_regexp(js_State *J) 378 | { 379 | js_Regexp *re; 380 | const char *source, *s, *r; 381 | js_Buffer *sb = NULL; 382 | unsigned int n, x; 383 | Resub m; 384 | 385 | source = js_tostring(J, 0); 386 | re = js_toregexp(J, 1); 387 | 388 | if (js_regexec(re->prog, source, &m, 0)) { 389 | js_copy(J, 0); 390 | return; 391 | } 392 | 393 | re->last = 0; 394 | 395 | loop: 396 | s = m.sub[0].sp; 397 | n = m.sub[0].ep - m.sub[0].sp; 398 | 399 | if (js_iscallable(J, 2)) { 400 | js_copy(J, 2); 401 | js_pushglobal(J); 402 | for (x = 0; m.sub[x].sp; ++x) /* arg 0..x: substring and subexps that matched */ 403 | js_pushlstring(J, m.sub[x].sp, m.sub[x].ep - m.sub[x].sp); 404 | js_pushnumber(J, s - source); /* arg x+2: offset within search string */ 405 | js_copy(J, 0); /* arg x+3: search string */ 406 | js_call(J, 2 + x); 407 | r = js_tostring(J, -1); 408 | js_putm(J, &sb, source, s); 409 | js_puts(J, &sb, r); 410 | js_pop(J, 1); 411 | } else { 412 | r = js_tostring(J, 2); 413 | js_putm(J, &sb, source, s); 414 | while (*r) { 415 | if (*r == '$') { 416 | switch (*(++r)) { 417 | case '$': js_putc(J, &sb, '$'); break; 418 | case '`': js_putm(J, &sb, source, s); break; 419 | case '\'': js_puts(J, &sb, s + n); break; 420 | case '&': 421 | js_putm(J, &sb, s, s + n); 422 | break; 423 | case '0': case '1': case '2': case '3': case '4': 424 | case '5': case '6': case '7': case '8': case '9': 425 | x = *r - '0'; 426 | if (r[1] >= '0' && r[1] <= '9') 427 | x = x * 10 + *(++r) - '0'; 428 | if (x > 0 && x < m.nsub) { 429 | js_putm(J, &sb, m.sub[x].sp, m.sub[x].ep); 430 | } else { 431 | js_putc(J, &sb, '$'); 432 | if (x > 10) { 433 | js_putc(J, &sb, '0' + x / 10); 434 | js_putc(J, &sb, '0' + x % 10); 435 | } else { 436 | js_putc(J, &sb, '0' + x); 437 | } 438 | } 439 | break; 440 | default: 441 | js_putc(J, &sb, '$'); 442 | js_putc(J, &sb, *r); 443 | break; 444 | } 445 | ++r; 446 | } else { 447 | js_putc(J, &sb, *r++); 448 | } 449 | } 450 | } 451 | 452 | if (re->flags & JS_REGEXP_G) { 453 | source = m.sub[0].ep; 454 | if (n == 0) { 455 | if (*source) 456 | js_putc(J, &sb, *source++); 457 | else 458 | goto end; 459 | } 460 | if (!js_regexec(re->prog, source, &m, REG_NOTBOL)) 461 | goto loop; 462 | } 463 | 464 | end: 465 | js_puts(J, &sb, s + n); 466 | js_putc(J, &sb, 0); 467 | 468 | if (js_try(J)) { 469 | js_free(J, sb); 470 | js_throw(J); 471 | } 472 | js_pushstring(J, sb ? sb->s : ""); 473 | js_endtry(J); 474 | js_free(J, sb); 475 | } 476 | 477 | static void Sp_replace_string(js_State *J) 478 | { 479 | const char *source, *needle, *s, *r; 480 | js_Buffer *sb = NULL; 481 | int n; 482 | 483 | source = js_tostring(J, 0); 484 | needle = js_tostring(J, 1); 485 | 486 | s = strstr(source, needle); 487 | if (!s) { 488 | js_copy(J, 0); 489 | return; 490 | } 491 | n = strlen(needle); 492 | 493 | if (js_iscallable(J, 2)) { 494 | js_copy(J, 2); 495 | js_pushglobal(J); 496 | js_pushlstring(J, s, n); /* arg 1: substring that matched */ 497 | js_pushnumber(J, s - source); /* arg 2: offset within search string */ 498 | js_copy(J, 0); /* arg 3: search string */ 499 | js_call(J, 3); 500 | r = js_tostring(J, -1); 501 | js_putm(J, &sb, source, s); 502 | js_puts(J, &sb, r); 503 | js_puts(J, &sb, s + n); 504 | js_putc(J, &sb, 0); 505 | js_pop(J, 1); 506 | } else { 507 | r = js_tostring(J, 2); 508 | js_putm(J, &sb, source, s); 509 | while (*r) { 510 | if (*r == '$') { 511 | switch (*(++r)) { 512 | case '$': js_putc(J, &sb, '$'); break; 513 | case '&': js_putm(J, &sb, s, s + n); break; 514 | case '`': js_putm(J, &sb, source, s); break; 515 | case '\'': js_puts(J, &sb, s + n); break; 516 | default: js_putc(J, &sb, '$'); js_putc(J, &sb, *r); break; 517 | } 518 | ++r; 519 | } else { 520 | js_putc(J, &sb, *r++); 521 | } 522 | } 523 | js_puts(J, &sb, s + n); 524 | js_putc(J, &sb, 0); 525 | } 526 | 527 | if (js_try(J)) { 528 | js_free(J, sb); 529 | js_throw(J); 530 | } 531 | js_pushstring(J, sb ? sb->s : ""); 532 | js_endtry(J); 533 | js_free(J, sb); 534 | } 535 | 536 | static void Sp_replace(js_State *J) 537 | { 538 | if (js_isregexp(J, 1)) 539 | Sp_replace_regexp(J); 540 | else 541 | Sp_replace_string(J); 542 | } 543 | 544 | static void Sp_split_regexp(js_State *J) 545 | { 546 | js_Regexp *re; 547 | const char *text; 548 | unsigned int limit, len, k; 549 | const char *p, *a, *b, *c, *e; 550 | Resub m; 551 | 552 | text = js_tostring(J, 0); 553 | re = js_toregexp(J, 1); 554 | limit = js_isdefined(J, 2) ? js_touint32(J, 2) : 1 << 30; 555 | 556 | js_newarray(J); 557 | len = 0; 558 | 559 | e = text + strlen(text); 560 | 561 | /* splitting the empty string */ 562 | if (e == 0) { 563 | if (js_regexec(re->prog, text, &m, 0)) { 564 | if (len == limit) return; 565 | js_pushliteral(J, ""); 566 | js_setindex(J, -2, 0); 567 | } 568 | return; 569 | } 570 | 571 | p = a = text; 572 | while (a < e) { 573 | if (js_regexec(re->prog, a, &m, a > text ? REG_NOTBOL : 0)) 574 | break; /* no match */ 575 | 576 | b = m.sub[0].sp; 577 | c = m.sub[0].ep; 578 | 579 | /* empty string at end of last match */ 580 | if (b == p) { 581 | ++a; 582 | continue; 583 | } 584 | 585 | if (len == limit) return; 586 | js_pushlstring(J, p, b - p); 587 | js_setindex(J, -2, len++); 588 | 589 | for (k = 1; k < m.nsub; ++k) { 590 | if (len == limit) return; 591 | js_pushlstring(J, m.sub[k].sp, m.sub[k].ep - m.sub[k].sp); 592 | js_setindex(J, -2, len++); 593 | } 594 | 595 | a = p = c; 596 | } 597 | 598 | if (len == limit) return; 599 | js_pushstring(J, p); 600 | js_setindex(J, -2, len); 601 | } 602 | 603 | static void Sp_split_string(js_State *J) 604 | { 605 | const char *str = js_tostring(J, 0); 606 | const char *sep = js_tostring(J, 1); 607 | unsigned int limit = js_isdefined(J, 2) ? js_touint32(J, 2) : 1 << 30; 608 | unsigned int i, n; 609 | 610 | js_newarray(J); 611 | 612 | n = strlen(sep); 613 | 614 | /* empty string */ 615 | if (n == 0) { 616 | Rune rune; 617 | for (i = 0; *str && i < limit; ++i) { 618 | n = chartorune(&rune, str); 619 | js_pushlstring(J, str, n); 620 | js_setindex(J, -2, i); 621 | str += n; 622 | } 623 | return; 624 | } 625 | 626 | for (i = 0; str && i < limit; ++i) { 627 | const char *s = strstr(str, sep); 628 | if (s) { 629 | js_pushlstring(J, str, s-str); 630 | js_setindex(J, -2, i); 631 | str = s + n; 632 | } else { 633 | js_pushstring(J, str); 634 | js_setindex(J, -2, i); 635 | str = NULL; 636 | } 637 | } 638 | } 639 | 640 | static void Sp_split(js_State *J) 641 | { 642 | if (js_isundefined(J, 1)) { 643 | js_newarray(J); 644 | js_copy(J, 0); 645 | js_setindex(J, -2, 0); 646 | } else if (js_isregexp(J, 1)) { 647 | Sp_split_regexp(J); 648 | } else { 649 | Sp_split_string(J); 650 | } 651 | } 652 | 653 | void jsB_initstring(js_State *J) 654 | { 655 | J->String_prototype->u.s.string = ""; 656 | J->String_prototype->u.s.length = 0; 657 | 658 | js_pushobject(J, J->String_prototype); 659 | { 660 | jsB_propf(J, "toString", Sp_toString, 0); 661 | jsB_propf(J, "valueOf", Sp_valueOf, 0); 662 | jsB_propf(J, "charAt", Sp_charAt, 1); 663 | jsB_propf(J, "charCodeAt", Sp_charCodeAt, 1); 664 | jsB_propf(J, "concat", Sp_concat, 1); 665 | jsB_propf(J, "indexOf", Sp_indexOf, 1); 666 | jsB_propf(J, "lastIndexOf", Sp_lastIndexOf, 1); 667 | jsB_propf(J, "localeCompare", Sp_localeCompare, 1); 668 | jsB_propf(J, "match", Sp_match, 1); 669 | jsB_propf(J, "replace", Sp_replace, 2); 670 | jsB_propf(J, "search", Sp_search, 1); 671 | jsB_propf(J, "slice", Sp_slice, 2); 672 | jsB_propf(J, "split", Sp_split, 2); 673 | jsB_propf(J, "substring", Sp_substring, 2); 674 | jsB_propf(J, "toLowerCase", Sp_toLowerCase, 0); 675 | jsB_propf(J, "toLocaleLowerCase", Sp_toLowerCase, 0); 676 | jsB_propf(J, "toUpperCase", Sp_toUpperCase, 0); 677 | jsB_propf(J, "toLocaleUpperCase", Sp_toUpperCase, 0); 678 | 679 | /* ES5 */ 680 | jsB_propf(J, "trim", Sp_trim, 0); 681 | } 682 | js_newcconstructor(J, jsB_String, jsB_new_String, 1); 683 | { 684 | jsB_propf(J, "fromCharCode", S_fromCharCode, 1); 685 | } 686 | js_defglobal(J, "String", JS_DONTENUM); 687 | } 688 | -------------------------------------------------------------------------------- /jsdtoa.c: -------------------------------------------------------------------------------- 1 | /* The authors of this software are Rob Pike and Ken Thompson. 2 | * Copyright (c) 2002 by Lucent Technologies. 3 | * Permission to use, copy, modify, and distribute this software for any 4 | * purpose without fee is hereby granted, provided that this entire notice 5 | * is included in all copies of any software which is or includes a copy 6 | * or modification of this software and in all copies of the supporting 7 | * documentation for such software. 8 | * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED 9 | * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY 10 | * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY 11 | * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. 12 | */ 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "jsi.h" 22 | 23 | typedef unsigned long ulong; 24 | 25 | enum { NSIGNIF = 17 }; 26 | 27 | /* 28 | * first few powers of 10, enough for about 1/2 of the 29 | * total space for doubles. 30 | */ 31 | static double pows10[] = 32 | { 33 | 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 34 | 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 35 | 1e20, 1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29, 36 | 1e30, 1e31, 1e32, 1e33, 1e34, 1e35, 1e36, 1e37, 1e38, 1e39, 37 | 1e40, 1e41, 1e42, 1e43, 1e44, 1e45, 1e46, 1e47, 1e48, 1e49, 38 | 1e50, 1e51, 1e52, 1e53, 1e54, 1e55, 1e56, 1e57, 1e58, 1e59, 39 | 1e60, 1e61, 1e62, 1e63, 1e64, 1e65, 1e66, 1e67, 1e68, 1e69, 40 | 1e70, 1e71, 1e72, 1e73, 1e74, 1e75, 1e76, 1e77, 1e78, 1e79, 41 | 1e80, 1e81, 1e82, 1e83, 1e84, 1e85, 1e86, 1e87, 1e88, 1e89, 42 | 1e90, 1e91, 1e92, 1e93, 1e94, 1e95, 1e96, 1e97, 1e98, 1e99, 43 | 1e100, 1e101, 1e102, 1e103, 1e104, 1e105, 1e106, 1e107, 1e108, 1e109, 44 | 1e110, 1e111, 1e112, 1e113, 1e114, 1e115, 1e116, 1e117, 1e118, 1e119, 45 | 1e120, 1e121, 1e122, 1e123, 1e124, 1e125, 1e126, 1e127, 1e128, 1e129, 46 | 1e130, 1e131, 1e132, 1e133, 1e134, 1e135, 1e136, 1e137, 1e138, 1e139, 47 | 1e140, 1e141, 1e142, 1e143, 1e144, 1e145, 1e146, 1e147, 1e148, 1e149, 48 | 1e150, 1e151, 1e152, 1e153, 1e154, 1e155, 1e156, 1e157, 1e158, 1e159, 49 | }; 50 | #define npows10 ((int)(sizeof(pows10)/sizeof(pows10[0]))) 51 | #define pow10(x) fmtpow10(x) 52 | 53 | static double 54 | pow10(int n) 55 | { 56 | double d; 57 | int neg; 58 | 59 | neg = 0; 60 | if(n < 0){ 61 | neg = 1; 62 | n = -n; 63 | } 64 | 65 | if(n < npows10) 66 | d = pows10[n]; 67 | else{ 68 | d = pows10[npows10-1]; 69 | for(;;){ 70 | n -= npows10 - 1; 71 | if(n < npows10){ 72 | d *= pows10[n]; 73 | break; 74 | } 75 | d *= pows10[npows10 - 1]; 76 | } 77 | } 78 | if(neg) 79 | return 1./d; 80 | return d; 81 | } 82 | 83 | /* 84 | * add 1 to the decimal integer string a of length n. 85 | * if 99999 overflows into 10000, return 1 to tell caller 86 | * to move the virtual decimal point. 87 | */ 88 | static int 89 | xadd1(char *a, int n) 90 | { 91 | char *b; 92 | int c; 93 | 94 | if(n < 0 || n > NSIGNIF) 95 | return 0; 96 | for(b = a+n-1; b >= a; b--) { 97 | c = *b + 1; 98 | if(c <= '9') { 99 | *b = c; 100 | return 0; 101 | } 102 | *b = '0'; 103 | } 104 | /* 105 | * need to overflow adding digit. 106 | * shift number down and insert 1 at beginning. 107 | * decimal is known to be 0s or we wouldn't 108 | * have gotten this far. (e.g., 99999+1 => 00000) 109 | */ 110 | a[0] = '1'; 111 | return 1; 112 | } 113 | 114 | /* 115 | * subtract 1 from the decimal integer string a. 116 | * if 10000 underflows into 09999, make it 99999 117 | * and return 1 to tell caller to move the virtual 118 | * decimal point. this way, xsub1 is inverse of xadd1. 119 | */ 120 | static int 121 | xsub1(char *a, int n) 122 | { 123 | char *b; 124 | int c; 125 | 126 | if(n < 0 || n > NSIGNIF) 127 | return 0; 128 | for(b = a+n-1; b >= a; b--) { 129 | c = *b - 1; 130 | if(c >= '0') { 131 | if(c == '0' && b == a) { 132 | /* 133 | * just zeroed the top digit; shift everyone up. 134 | * decimal is known to be 9s or we wouldn't 135 | * have gotten this far. (e.g., 10000-1 => 09999) 136 | */ 137 | *b = '9'; 138 | return 1; 139 | } 140 | *b = c; 141 | return 0; 142 | } 143 | *b = '9'; 144 | } 145 | /* 146 | * can't get here. the number a is always normalized 147 | * so that it has a nonzero first digit. 148 | */ 149 | return 0; 150 | } 151 | 152 | /* 153 | * format exponent like sprintf(p, "e%+d", e) 154 | */ 155 | void 156 | js_fmtexp(char *p, int e) 157 | { 158 | char se[9]; 159 | int i; 160 | 161 | *p++ = 'e'; 162 | if(e < 0) { 163 | *p++ = '-'; 164 | e = -e; 165 | } else 166 | *p++ = '+'; 167 | i = 0; 168 | while(e) { 169 | se[i++] = e % 10 + '0'; 170 | e /= 10; 171 | } 172 | while(i < 1) 173 | se[i++] = '0'; 174 | while(i > 0) 175 | *p++ = se[--i]; 176 | *p++ = '\0'; 177 | } 178 | 179 | /* 180 | * compute decimal integer m, exp such that: 181 | * f = m*10^exp 182 | * m is as short as possible with losing exactness 183 | * assumes special cases (NaN, +Inf, -Inf) have been handled. 184 | */ 185 | void 186 | js_dtoa(double f, char *s, int *exp, int *neg, int *ns) 187 | { 188 | int c, d, e2, e, ee, i, ndigit, oerrno; 189 | char tmp[NSIGNIF+10]; 190 | double g; 191 | 192 | oerrno = errno; /* in case strtod smashes errno */ 193 | 194 | /* 195 | * make f non-negative. 196 | */ 197 | *neg = 0; 198 | if(f < 0) { 199 | f = -f; 200 | *neg = 1; 201 | } 202 | 203 | /* 204 | * must handle zero specially. 205 | */ 206 | if(f == 0){ 207 | *exp = 0; 208 | s[0] = '0'; 209 | s[1] = '\0'; 210 | *ns = 1; 211 | return; 212 | } 213 | 214 | /* 215 | * find g,e such that f = g*10^e. 216 | * guess 10-exponent using 2-exponent, then fine tune. 217 | */ 218 | frexp(f, &e2); 219 | e = (int)(e2 * .301029995664); 220 | g = f * pow10(-e); 221 | while(g < 1) { 222 | e--; 223 | g = f * pow10(-e); 224 | } 225 | while(g >= 10) { 226 | e++; 227 | g = f * pow10(-e); 228 | } 229 | 230 | /* 231 | * convert NSIGNIF digits as a first approximation. 232 | */ 233 | for(i=0; i g) { 252 | if(xadd1(s, NSIGNIF)) { 253 | /* gained a digit */ 254 | e--; 255 | js_fmtexp(s+NSIGNIF, e); 256 | } 257 | continue; 258 | } 259 | if(f < g) { 260 | if(xsub1(s, NSIGNIF)) { 261 | /* lost a digit */ 262 | e++; 263 | js_fmtexp(s+NSIGNIF, e); 264 | } 265 | continue; 266 | } 267 | break; 268 | } 269 | 270 | /* 271 | * play with the decimal to try to simplify. 272 | */ 273 | 274 | /* 275 | * bump last few digits up to 9 if we can 276 | */ 277 | for(i=NSIGNIF-1; i>=NSIGNIF-3; i--) { 278 | c = s[i]; 279 | if(c != '9') { 280 | s[i] = '9'; 281 | g = js_strtod(s, NULL); 282 | if(g != f) { 283 | s[i] = c; 284 | break; 285 | } 286 | } 287 | } 288 | 289 | /* 290 | * add 1 in hopes of turning 9s to 0s 291 | */ 292 | if(s[NSIGNIF-1] == '9') { 293 | strcpy(tmp, s); 294 | ee = e; 295 | if(xadd1(tmp, NSIGNIF)) { 296 | ee--; 297 | js_fmtexp(tmp+NSIGNIF, ee); 298 | } 299 | g = js_strtod(tmp, NULL); 300 | if(g == f) { 301 | strcpy(s, tmp); 302 | e = ee; 303 | } 304 | } 305 | 306 | /* 307 | * bump last few digits down to 0 as we can. 308 | */ 309 | for(i=NSIGNIF-1; i>=NSIGNIF-3; i--) { 310 | c = s[i]; 311 | if(c != '0') { 312 | s[i] = '0'; 313 | g = js_strtod(s, NULL); 314 | if(g != f) { 315 | s[i] = c; 316 | break; 317 | } 318 | } 319 | } 320 | 321 | /* 322 | * remove trailing zeros. 323 | */ 324 | ndigit = NSIGNIF; 325 | while(ndigit > 1 && s[ndigit-1] == '0'){ 326 | e++; 327 | --ndigit; 328 | } 329 | s[ndigit] = 0; 330 | *exp = e; 331 | *ns = ndigit; 332 | errno = oerrno; 333 | } 334 | 335 | static ulong 336 | umuldiv(ulong a, ulong b, ulong c) 337 | { 338 | double d; 339 | 340 | d = ((double)a * (double)b) / (double)c; 341 | if(d >= 4294967295.) 342 | d = 4294967295.; 343 | return (ulong)d; 344 | } 345 | 346 | /* 347 | * This routine will convert to arbitrary precision 348 | * floating point entirely in multi-precision fixed. 349 | * The answer is the closest floating point number to 350 | * the given decimal number. Exactly half way are 351 | * rounded ala ieee rules. 352 | * Method is to scale input decimal between .500 and .999... 353 | * with external power of 2, then binary search for the 354 | * closest mantissa to this decimal number. 355 | * Nmant is is the required precision. (53 for ieee dp) 356 | * Nbits is the max number of bits/word. (must be <= 28) 357 | * Prec is calculated - the number of words of fixed mantissa. 358 | */ 359 | enum 360 | { 361 | Nbits = 28, /* bits safely represented in a ulong */ 362 | Nmant = 53, /* bits of precision required */ 363 | Prec = (Nmant+Nbits+1)/Nbits, /* words of Nbits each to represent mantissa */ 364 | Sigbit = 1<<(Prec*Nbits-Nmant), /* first significant bit of Prec-th word */ 365 | Ndig = 1500, 366 | One = (ulong)(1<>1), 368 | Maxe = 310, 369 | 370 | Fsign = 1<<0, /* found - */ 371 | Fesign = 1<<1, /* found e- */ 372 | Fdpoint = 1<<2, /* found . */ 373 | 374 | S0 = 0, /* _ _S0 +S1 #S2 .S3 */ 375 | S1, /* _+ #S2 .S3 */ 376 | S2, /* _+# #S2 .S4 eS5 */ 377 | S3, /* _+. #S4 */ 378 | S4, /* _+#.# #S4 eS5 */ 379 | S5, /* _+#.#e +S6 #S7 */ 380 | S6, /* _+#.#e+ #S7 */ 381 | S7 /* _+#.#e+# #S7 */ 382 | }; 383 | 384 | static int xcmp(char*, char*); 385 | static int fpcmp(char*, ulong*); 386 | static void frnorm(ulong*); 387 | static void divascii(char*, int*, int*, int*); 388 | static void mulascii(char*, int*, int*, int*); 389 | 390 | typedef struct Tab Tab; 391 | struct Tab 392 | { 393 | int bp; 394 | int siz; 395 | char* cmp; 396 | }; 397 | 398 | double 399 | js_strtod(const char *as, char **aas) 400 | { 401 | int na, ex, dp, bp, c, i, flag, state; 402 | ulong low[Prec], hig[Prec], mid[Prec]; 403 | double d; 404 | char *s, a[Ndig]; 405 | 406 | flag = 0; /* Fsign, Fesign, Fdpoint */ 407 | na = 0; /* number of digits of a[] */ 408 | dp = 0; /* na of decimal point */ 409 | ex = 0; /* exonent */ 410 | 411 | state = S0; 412 | for(s=(char*)as;; s++) { 413 | c = *s; 414 | if(c >= '0' && c <= '9') { 415 | switch(state) { 416 | case S0: 417 | case S1: 418 | case S2: 419 | state = S2; 420 | break; 421 | case S3: 422 | case S4: 423 | state = S4; 424 | break; 425 | 426 | case S5: 427 | case S6: 428 | case S7: 429 | state = S7; 430 | ex = ex*10 + (c-'0'); 431 | continue; 432 | } 433 | if(na == 0 && c == '0') { 434 | dp--; 435 | continue; 436 | } 437 | if(na < Ndig-50) 438 | a[na++] = c; 439 | continue; 440 | } 441 | switch(c) { 442 | case '\t': 443 | case '\n': 444 | case '\v': 445 | case '\f': 446 | case '\r': 447 | case ' ': 448 | if(state == S0) 449 | continue; 450 | break; 451 | case '-': 452 | if(state == S0) 453 | flag |= Fsign; 454 | else 455 | flag |= Fesign; 456 | case '+': 457 | if(state == S0) 458 | state = S1; 459 | else 460 | if(state == S5) 461 | state = S6; 462 | else 463 | break; /* syntax */ 464 | continue; 465 | case '.': 466 | flag |= Fdpoint; 467 | dp = na; 468 | if(state == S0 || state == S1) { 469 | state = S3; 470 | continue; 471 | } 472 | if(state == S2) { 473 | state = S4; 474 | continue; 475 | } 476 | break; 477 | case 'e': 478 | case 'E': 479 | if(state == S2 || state == S4) { 480 | state = S5; 481 | continue; 482 | } 483 | break; 484 | } 485 | break; 486 | } 487 | 488 | /* 489 | * clean up return char-pointer 490 | */ 491 | switch(state) { 492 | case S0: 493 | if(xcmp(s, "nan") == 0) { 494 | if(aas != NULL) 495 | *aas = s+3; 496 | goto retnan; 497 | } 498 | case S1: 499 | if(xcmp(s, "infinity") == 0) { 500 | if(aas != NULL) 501 | *aas = s+8; 502 | goto retinf; 503 | } 504 | if(xcmp(s, "inf") == 0) { 505 | if(aas != NULL) 506 | *aas = s+3; 507 | goto retinf; 508 | } 509 | case S3: 510 | if(aas != NULL) 511 | *aas = (char*)as; 512 | goto ret0; /* no digits found */ 513 | case S6: 514 | s--; /* back over +- */ 515 | case S5: 516 | s--; /* back over e */ 517 | break; 518 | } 519 | if(aas != NULL) 520 | *aas = s; 521 | 522 | if(flag & Fdpoint) 523 | while(na > 0 && a[na-1] == '0') 524 | na--; 525 | if(na == 0) 526 | goto ret0; /* zero */ 527 | a[na] = 0; 528 | if(!(flag & Fdpoint)) 529 | dp = na; 530 | if(flag & Fesign) 531 | ex = -ex; 532 | dp += ex; 533 | if(dp < -Maxe){ 534 | errno = ERANGE; 535 | goto ret0; /* underflow by exp */ 536 | } else 537 | if(dp > +Maxe) 538 | goto retinf; /* overflow by exp */ 539 | 540 | /* 541 | * normalize the decimal ascii number 542 | * to range .[5-9][0-9]* e0 543 | */ 544 | bp = 0; /* binary exponent */ 545 | while(dp > 0) 546 | divascii(a, &na, &dp, &bp); 547 | while(dp < 0 || a[0] < '5') 548 | mulascii(a, &na, &dp, &bp); 549 | 550 | /* close approx by naive conversion */ 551 | mid[0] = 0; 552 | mid[1] = 1; 553 | for(i=0; (c=a[i]) != '\0'; i++) { 554 | mid[0] = mid[0]*10 + (c-'0'); 555 | mid[1] = mid[1]*10; 556 | if(i >= 8) 557 | break; 558 | } 559 | low[0] = umuldiv(mid[0], One, mid[1]); 560 | hig[0] = umuldiv(mid[0]+1, One, mid[1]); 561 | for(i=1; i>= 1; 576 | } 577 | frnorm(mid); 578 | 579 | /* compare */ 580 | c = fpcmp(a, mid); 581 | if(c > 0) { 582 | c = 1; 583 | for(i=0; i= Sigbit/2) { 609 | mid[Prec-1] += Sigbit; 610 | frnorm(mid); 611 | } 612 | goto out; 613 | 614 | ret0: 615 | if(flag & Fsign) 616 | return -0.0; 617 | return 0; 618 | 619 | retnan: 620 | return NAN; 621 | 622 | retinf: 623 | /* 624 | * Unix strtod requires these. Plan 9 would return Inf(0) or Inf(-1). */ 625 | errno = ERANGE; 626 | if(flag & Fsign) 627 | return -HUGE_VAL; 628 | return HUGE_VAL; 629 | 630 | out: 631 | d = 0; 632 | for(i=0; i0; i--) { 650 | f[i] += c; 651 | c = f[i] >> Nbits; 652 | f[i] &= One-1; 653 | } 654 | f[0] += c; 655 | } 656 | 657 | static int 658 | fpcmp(char *a, ulong* f) 659 | { 660 | ulong tf[Prec]; 661 | int i, d, c; 662 | 663 | for(i=0; i> Nbits) + '0'; 672 | tf[0] &= One-1; 673 | 674 | /* compare next digit */ 675 | c = *a; 676 | if(c == 0) { 677 | if('0' < d) 678 | return -1; 679 | if(tf[0] != 0) 680 | goto cont; 681 | for(i=1; i d) 687 | return +1; 688 | if(c < d) 689 | return -1; 690 | a++; 691 | cont:; 692 | } 693 | } 694 | 695 | static void 696 | divby(char *a, int *na, int b) 697 | { 698 | int n, c; 699 | char *p; 700 | 701 | p = a; 702 | n = 0; 703 | while(n>>b == 0) { 704 | c = *a++; 705 | if(c == 0) { 706 | while(n) { 707 | c = n*10; 708 | if(c>>b) 709 | break; 710 | n = c; 711 | } 712 | goto xx; 713 | } 714 | n = n*10 + c-'0'; 715 | (*na)--; 716 | } 717 | for(;;) { 718 | c = n>>b; 719 | n -= c<>b; 731 | n -= c<= (int)(nelem(tab1))) 760 | d = (int)(nelem(tab1))-1; 761 | t = tab1 + d; 762 | b = t->bp; 763 | if(memcmp(a, t->cmp, t->siz) > 0) 764 | d--; 765 | *dp -= d; 766 | *bp += b; 767 | divby(a, na, b); 768 | } 769 | 770 | static void 771 | mulby(char *a, char *p, char *q, int b) 772 | { 773 | int n, c; 774 | 775 | n = 0; 776 | *p = 0; 777 | for(;;) { 778 | q--; 779 | if(q < a) 780 | break; 781 | c = *q - '0'; 782 | c = (c<= (int)(nelem(tab2))) 820 | d = (int)(nelem(tab2))-1; 821 | t = tab2 + d; 822 | b = t->bp; 823 | if(memcmp(a, t->cmp, t->siz) < 0) 824 | d--; 825 | p = a + *na; 826 | *bp -= b; 827 | *dp += d; 828 | *na += d; 829 | mulby(a, p+d, p, b); 830 | } 831 | 832 | static int 833 | xcmp(char *a, char *b) 834 | { 835 | int c1, c2; 836 | 837 | while((c1 = *b++) != '\0') { 838 | c2 = *a++; 839 | if(c2 >= 'A' && c2 <= 'Z') 840 | c2 = c2 - 'A' + 'a'; 841 | if(c1 != c2) 842 | return 1; 843 | } 844 | return 0; 845 | } 846 | -------------------------------------------------------------------------------- /jslex.c: -------------------------------------------------------------------------------- 1 | #include "jsi.h" 2 | #include "jslex.h" 3 | #include "utf.h" 4 | 5 | JS_NORETURN static void jsY_error(js_State *J, const char *fmt, ...) JS_PRINTFLIKE(2,3); 6 | 7 | static void jsY_error(js_State *J, const char *fmt, ...) 8 | { 9 | va_list ap; 10 | char buf[512]; 11 | char msgbuf[256]; 12 | 13 | va_start(ap, fmt); 14 | vsnprintf(msgbuf, 256, fmt, ap); 15 | va_end(ap); 16 | 17 | snprintf(buf, 256, "%s:%d: ", J->filename, J->lexline); 18 | strcat(buf, msgbuf); 19 | 20 | js_newsyntaxerror(J, buf); 21 | js_throw(J); 22 | } 23 | 24 | static const char *tokenstring[] = { 25 | "(end-of-file)", 26 | "'\\x01'", "'\\x02'", "'\\x03'", "'\\x04'", "'\\x05'", "'\\x06'", "'\\x07'", 27 | "'\\x08'", "'\\x09'", "'\\x0A'", "'\\x0B'", "'\\x0C'", "'\\x0D'", "'\\x0E'", "'\\x0F'", 28 | "'\\x10'", "'\\x11'", "'\\x12'", "'\\x13'", "'\\x14'", "'\\x15'", "'\\x16'", "'\\x17'", 29 | "'\\x18'", "'\\x19'", "'\\x1A'", "'\\x1B'", "'\\x1C'", "'\\x1D'", "'\\x1E'", "'\\x1F'", 30 | "' '", "'!'", "'\"'", "'#'", "'$'", "'%'", "'&'", "'\\''", 31 | "'('", "')'", "'*'", "'+'", "','", "'-'", "'.'", "'/'", 32 | "'0'", "'1'", "'2'", "'3'", "'4'", "'5'", "'6'", "'7'", 33 | "'8'", "'9'", "':'", "';'", "'<'", "'='", "'>'", "'?'", 34 | "'@'", "'A'", "'B'", "'C'", "'D'", "'E'", "'F'", "'G'", 35 | "'H'", "'I'", "'J'", "'K'", "'L'", "'M'", "'N'", "'O'", 36 | "'P'", "'Q'", "'R'", "'S'", "'T'", "'U'", "'V'", "'W'", 37 | "'X'", "'Y'", "'Z'", "'['", "'\'", "']'", "'^'", "'_'", 38 | "'`'", "'a'", "'b'", "'c'", "'d'", "'e'", "'f'", "'g'", 39 | "'h'", "'i'", "'j'", "'k'", "'l'", "'m'", "'n'", "'o'", 40 | "'p'", "'q'", "'r'", "'s'", "'t'", "'u'", "'v'", "'w'", 41 | "'x'", "'y'", "'z'", "'{'", "'|'", "'}'", "'~'", "'\\x7F'", 42 | 43 | 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 44 | 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 45 | 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 46 | 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 47 | 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 48 | 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 49 | 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 50 | 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 51 | 52 | "(identifier)", "(number)", "(string)", "(regexp)", 53 | 54 | "'<='", "'>='", "'=='", "'!='", "'==='", "'!=='", 55 | "'<<'", "'>>'", "'>>>'", "'&&'", "'||'", 56 | "'+='", "'-='", "'*='", "'/='", "'%='", 57 | "'<<='", "'>>='", "'>>>='", "'&='", "'|='", "'^='", 58 | "'++'", "'--'", 59 | 60 | "'break'", "'case'", "'catch'", "'continue'", "'debugger'", 61 | "'default'", "'delete'", "'do'", "'else'", "'false'", "'finally'", "'for'", 62 | "'function'", "'if'", "'in'", "'instanceof'", "'new'", "'null'", "'return'", 63 | "'switch'", "'this'", "'throw'", "'true'", "'try'", "'typeof'", "'var'", 64 | "'void'", "'while'", "'with'", 65 | }; 66 | 67 | const char *jsY_tokenstring(int token) 68 | { 69 | if (token >= 0 && token < (int)nelem(tokenstring)) 70 | if (tokenstring[token]) 71 | return tokenstring[token]; 72 | return ""; 73 | } 74 | 75 | static const char *keywords[] = { 76 | "break", "case", "catch", "continue", "debugger", "default", "delete", 77 | "do", "else", "false", "finally", "for", "function", "if", "in", 78 | "instanceof", "new", "null", "return", "switch", "this", "throw", 79 | "true", "try", "typeof", "var", "void", "while", "with", 80 | }; 81 | 82 | int jsY_findword(const char *s, const char **list, int num) 83 | { 84 | int l = 0; 85 | int r = num - 1; 86 | while (l <= r) { 87 | int m = (l + r) >> 1; 88 | int c = strcmp(s, list[m]); 89 | if (c < 0) 90 | r = m - 1; 91 | else if (c > 0) 92 | l = m + 1; 93 | else 94 | return m; 95 | } 96 | return -1; 97 | } 98 | 99 | static int jsY_findkeyword(js_State *J, const char *s) 100 | { 101 | int i = jsY_findword(s, keywords, nelem(keywords)); 102 | if (i >= 0) { 103 | J->text = keywords[i]; 104 | return TK_BREAK + i; /* first keyword + i */ 105 | } 106 | J->text = js_intern(J, s); 107 | return TK_IDENTIFIER; 108 | } 109 | 110 | int jsY_iswhite(int c) 111 | { 112 | return c == 0x9 || c == 0xB || c == 0xC || c == 0x20 || c == 0xA0 || c == 0xFEFF; 113 | } 114 | 115 | int jsY_isnewline(int c) 116 | { 117 | return c == 0xA || c == 0xD || c == 0x2028 || c == 0x2029; 118 | } 119 | 120 | #define isalpha(c) ((((unsigned)(c)|32)-'a') < 26) 121 | #define isdigit(c) (((unsigned)(c)-'0') < 10) 122 | #define ishex(c) ((((unsigned)(c)|32)-'a') < 6) 123 | 124 | static int jsY_isidentifierstart(int c) 125 | { 126 | return isalpha(c) || c == '$' || c == '_' || isalpharune(c); 127 | } 128 | 129 | static int jsY_isidentifierpart(int c) 130 | { 131 | return isdigit(c) || isalpha(c) || c == '$' || c == '_' || isalpharune(c); 132 | } 133 | 134 | static int jsY_isdec(int c) 135 | { 136 | return isdigit(c); 137 | } 138 | 139 | int jsY_ishex(int c) 140 | { 141 | return isdigit(c) || ishex(c); 142 | } 143 | 144 | int jsY_tohex(int c) 145 | { 146 | if (c >= '0' && c <= '9') return c - '0'; 147 | if (c >= 'a' && c <= 'f') return c - 'a' + 0xA; 148 | if (c >= 'A' && c <= 'F') return c - 'A' + 0xA; 149 | return 0; 150 | } 151 | 152 | #define PEEK (J->lexchar) 153 | #define NEXT() jsY_next(J) 154 | #define ACCEPT(x) (PEEK == x ? (NEXT(), 1) : 0) 155 | #define EXPECT(x) if (!ACCEPT(x)) jsY_error(J, "expected '%c'", x) 156 | 157 | static void jsY_next(js_State *J) 158 | { 159 | Rune c; 160 | J->source += chartorune(&c, J->source); 161 | /* consume CR LF as one unit */ 162 | if (c == '\r' && *J->source == '\n') 163 | ++J->source; 164 | if (jsY_isnewline(c)) { 165 | J->line++; 166 | c = '\n'; 167 | } 168 | J->lexchar = c; 169 | } 170 | 171 | static void jsY_unescape(js_State *J) 172 | { 173 | if (ACCEPT('\\')) { 174 | if (ACCEPT('u')) { 175 | int x = 0; 176 | if (!jsY_ishex(PEEK)) goto error; x |= jsY_tohex(PEEK) << 12; NEXT(); 177 | if (!jsY_ishex(PEEK)) goto error; x |= jsY_tohex(PEEK) << 8; NEXT(); 178 | if (!jsY_ishex(PEEK)) goto error; x |= jsY_tohex(PEEK) << 4; NEXT(); 179 | if (!jsY_ishex(PEEK)) goto error; x |= jsY_tohex(PEEK); 180 | J->lexchar = x; 181 | return; 182 | } 183 | error: 184 | jsY_error(J, "unexpected escape sequence"); 185 | } 186 | } 187 | 188 | static void textinit(js_State *J) 189 | { 190 | if (!J->lexbuf.text) { 191 | J->lexbuf.cap = 4096; 192 | J->lexbuf.text = js_malloc(J, J->lexbuf.cap); 193 | } 194 | J->lexbuf.len = 0; 195 | } 196 | 197 | static void textpush(js_State *J, Rune c) 198 | { 199 | int n = runelen(c); 200 | if (J->lexbuf.len + n > J->lexbuf.cap) { 201 | J->lexbuf.cap = J->lexbuf.cap * 2; 202 | J->lexbuf.text = js_realloc(J, J->lexbuf.text, J->lexbuf.cap); 203 | } 204 | J->lexbuf.len += runetochar(J->lexbuf.text + J->lexbuf.len, &c); 205 | } 206 | 207 | static char *textend(js_State *J) 208 | { 209 | textpush(J, 0); 210 | return J->lexbuf.text; 211 | } 212 | 213 | static void lexlinecomment(js_State *J) 214 | { 215 | while (PEEK && PEEK != '\n') 216 | NEXT(); 217 | } 218 | 219 | static int lexcomment(js_State *J) 220 | { 221 | /* already consumed initial '/' '*' sequence */ 222 | while (PEEK != 0) { 223 | if (ACCEPT('*')) { 224 | while (PEEK == '*') 225 | NEXT(); 226 | if (ACCEPT('/')) 227 | return 0; 228 | } 229 | NEXT(); 230 | } 231 | return -1; 232 | } 233 | 234 | static double lexhex(js_State *J) 235 | { 236 | double n = 0; 237 | if (!jsY_ishex(PEEK)) 238 | jsY_error(J, "malformed hexadecimal number"); 239 | while (jsY_ishex(PEEK)) { 240 | n = n * 16 + jsY_tohex(PEEK); 241 | NEXT(); 242 | } 243 | return n; 244 | } 245 | 246 | #if 0 247 | 248 | static double lexinteger(js_State *J) 249 | { 250 | double n = 0; 251 | if (!jsY_isdec(PEEK)) 252 | jsY_error(J, "malformed number"); 253 | while (jsY_isdec(PEEK)) { 254 | n = n * 10 + (PEEK - '0'); 255 | NEXT(); 256 | } 257 | return n; 258 | } 259 | 260 | static double lexfraction(js_State *J) 261 | { 262 | double n = 0; 263 | double d = 1; 264 | while (jsY_isdec(PEEK)) { 265 | n = n * 10 + (PEEK - '0'); 266 | d = d * 10; 267 | NEXT(); 268 | } 269 | return n / d; 270 | } 271 | 272 | static double lexexponent(js_State *J) 273 | { 274 | double sign; 275 | if (ACCEPT('e') || ACCEPT('E')) { 276 | if (ACCEPT('-')) sign = -1; 277 | else if (ACCEPT('+')) sign = 1; 278 | else sign = 1; 279 | return sign * lexinteger(J); 280 | } 281 | return 0; 282 | } 283 | 284 | static int lexnumber(js_State *J) 285 | { 286 | double n; 287 | double e; 288 | 289 | if (ACCEPT('0')) { 290 | if (ACCEPT('x') || ACCEPT('X')) { 291 | J->number = lexhex(J); 292 | return TK_NUMBER; 293 | } 294 | if (jsY_isdec(PEEK)) 295 | jsY_error(J, "number with leading zero"); 296 | n = 0; 297 | if (ACCEPT('.')) 298 | n += lexfraction(J); 299 | } else if (ACCEPT('.')) { 300 | if (!jsY_isdec(PEEK)) 301 | return '.'; 302 | n = lexfraction(J); 303 | } else { 304 | n = lexinteger(J); 305 | if (ACCEPT('.')) 306 | n += lexfraction(J); 307 | } 308 | 309 | e = lexexponent(J); 310 | if (e < 0) 311 | n /= pow(10, -e); 312 | else if (e > 0) 313 | n *= pow(10, e); 314 | 315 | if (jsY_isidentifierstart(PEEK)) 316 | jsY_error(J, "number with letter suffix"); 317 | 318 | J->number = n; 319 | return TK_NUMBER; 320 | } 321 | 322 | #else 323 | 324 | static int lexnumber(js_State *J) 325 | { 326 | const char *s = J->source - 1; 327 | 328 | if (ACCEPT('0')) { 329 | if (ACCEPT('x') || ACCEPT('X')) { 330 | J->number = lexhex(J); 331 | return TK_NUMBER; 332 | } 333 | if (jsY_isdec(PEEK)) 334 | jsY_error(J, "number with leading zero"); 335 | if (ACCEPT('.')) { 336 | while (jsY_isdec(PEEK)) 337 | NEXT(); 338 | } 339 | } else if (ACCEPT('.')) { 340 | if (!jsY_isdec(PEEK)) 341 | return '.'; 342 | while (jsY_isdec(PEEK)) 343 | NEXT(); 344 | } else { 345 | while (jsY_isdec(PEEK)) 346 | NEXT(); 347 | if (ACCEPT('.')) { 348 | while (jsY_isdec(PEEK)) 349 | NEXT(); 350 | } 351 | } 352 | 353 | if (ACCEPT('e') || ACCEPT('E')) { 354 | if (PEEK == '-' || PEEK == '+') 355 | NEXT(); 356 | while (jsY_isdec(PEEK)) 357 | NEXT(); 358 | } 359 | 360 | if (jsY_isidentifierstart(PEEK)) 361 | jsY_error(J, "number with letter suffix"); 362 | 363 | J->number = js_strtod(s, NULL); 364 | return TK_NUMBER; 365 | 366 | } 367 | 368 | #endif 369 | 370 | static int lexescape(js_State *J) 371 | { 372 | int x = 0; 373 | 374 | /* already consumed '\' */ 375 | 376 | if (ACCEPT('\n')) 377 | return 0; 378 | 379 | switch (PEEK) { 380 | case 'u': 381 | NEXT(); 382 | if (!jsY_ishex(PEEK)) return 1; else { x |= jsY_tohex(PEEK) << 12; NEXT(); } 383 | if (!jsY_ishex(PEEK)) return 1; else { x |= jsY_tohex(PEEK) << 8; NEXT(); } 384 | if (!jsY_ishex(PEEK)) return 1; else { x |= jsY_tohex(PEEK) << 4; NEXT(); } 385 | if (!jsY_ishex(PEEK)) return 1; else { x |= jsY_tohex(PEEK); NEXT(); } 386 | textpush(J, x); 387 | break; 388 | case 'x': 389 | NEXT(); 390 | if (!jsY_ishex(PEEK)) return 1; else { x |= jsY_tohex(PEEK) << 4; NEXT(); } 391 | if (!jsY_ishex(PEEK)) return 1; else { x |= jsY_tohex(PEEK); NEXT(); } 392 | textpush(J, x); 393 | break; 394 | case '0': textpush(J, 0); NEXT(); break; 395 | case '\\': textpush(J, '\\'); NEXT(); break; 396 | case '\'': textpush(J, '\''); NEXT(); break; 397 | case '"': textpush(J, '"'); NEXT(); break; 398 | case 'b': textpush(J, '\b'); NEXT(); break; 399 | case 'f': textpush(J, '\f'); NEXT(); break; 400 | case 'n': textpush(J, '\n'); NEXT(); break; 401 | case 'r': textpush(J, '\r'); NEXT(); break; 402 | case 't': textpush(J, '\t'); NEXT(); break; 403 | case 'v': textpush(J, '\v'); NEXT(); break; 404 | default: textpush(J, PEEK); NEXT(); break; 405 | } 406 | return 0; 407 | } 408 | 409 | static int lexstring(js_State *J) 410 | { 411 | const char *s; 412 | 413 | int q = PEEK; 414 | NEXT(); 415 | 416 | textinit(J); 417 | 418 | while (PEEK != q) { 419 | if (PEEK == 0 || PEEK == '\n') 420 | jsY_error(J, "string not terminated"); 421 | if (ACCEPT('\\')) { 422 | if (lexescape(J)) 423 | jsY_error(J, "malformed escape sequence"); 424 | } else { 425 | textpush(J, PEEK); 426 | NEXT(); 427 | } 428 | } 429 | EXPECT(q); 430 | 431 | s = textend(J); 432 | 433 | J->text = js_intern(J, s); 434 | return TK_STRING; 435 | } 436 | 437 | /* the ugliest language wart ever... */ 438 | static int isregexpcontext(int last) 439 | { 440 | switch (last) { 441 | case ']': 442 | case ')': 443 | case '}': 444 | case TK_IDENTIFIER: 445 | case TK_NUMBER: 446 | case TK_STRING: 447 | case TK_FALSE: 448 | case TK_NULL: 449 | case TK_THIS: 450 | case TK_TRUE: 451 | return 0; 452 | default: 453 | return 1; 454 | } 455 | } 456 | 457 | static int lexregexp(js_State *J) 458 | { 459 | const char *s; 460 | int g, m, i; 461 | int inclass = 0; 462 | 463 | /* already consumed initial '/' */ 464 | 465 | textinit(J); 466 | 467 | /* regexp body */ 468 | while (PEEK != '/' || inclass) { 469 | if (PEEK == 0 || PEEK == '\n') { 470 | jsY_error(J, "regular expression not terminated"); 471 | } else if (ACCEPT('\\')) { 472 | if (ACCEPT('/')) { 473 | textpush(J, '/'); 474 | } else { 475 | textpush(J, '\\'); 476 | if (PEEK == 0 || PEEK == '\n') 477 | jsY_error(J, "regular expression not terminated"); 478 | textpush(J, PEEK); 479 | NEXT(); 480 | } 481 | } else { 482 | if (PEEK == '[' && !inclass) 483 | inclass = 1; 484 | if (PEEK == ']' && inclass) 485 | inclass = 0; 486 | textpush(J, PEEK); 487 | NEXT(); 488 | } 489 | } 490 | EXPECT('/'); 491 | 492 | s = textend(J); 493 | 494 | /* regexp flags */ 495 | g = i = m = 0; 496 | 497 | while (jsY_isidentifierpart(PEEK)) { 498 | if (ACCEPT('g')) ++g; 499 | else if (ACCEPT('i')) ++i; 500 | else if (ACCEPT('m')) ++m; 501 | else jsY_error(J, "illegal flag in regular expression: %c", PEEK); 502 | } 503 | 504 | if (g > 1 || i > 1 || m > 1) 505 | jsY_error(J, "duplicated flag in regular expression"); 506 | 507 | J->text = js_intern(J, s); 508 | J->number = 0; 509 | if (g) J->number += JS_REGEXP_G; 510 | if (i) J->number += JS_REGEXP_I; 511 | if (m) J->number += JS_REGEXP_M; 512 | return TK_REGEXP; 513 | } 514 | 515 | /* simple "return [no Line Terminator here] ..." contexts */ 516 | static int isnlthcontext(int last) 517 | { 518 | switch (last) { 519 | case TK_BREAK: 520 | case TK_CONTINUE: 521 | case TK_RETURN: 522 | case TK_THROW: 523 | return 1; 524 | default: 525 | return 0; 526 | } 527 | } 528 | 529 | static int jsY_lexx(js_State *J) 530 | { 531 | J->newline = 0; 532 | 533 | while (1) { 534 | J->lexline = J->line; /* save location of beginning of token */ 535 | 536 | while (jsY_iswhite(PEEK)) 537 | NEXT(); 538 | 539 | if (ACCEPT('\n')) { 540 | J->newline = 1; 541 | if (isnlthcontext(J->lasttoken)) 542 | return ';'; 543 | continue; 544 | } 545 | 546 | if (ACCEPT('/')) { 547 | if (ACCEPT('/')) { 548 | lexlinecomment(J); 549 | continue; 550 | } else if (ACCEPT('*')) { 551 | if (lexcomment(J)) 552 | jsY_error(J, "multi-line comment not terminated"); 553 | continue; 554 | } else if (isregexpcontext(J->lasttoken)) { 555 | return lexregexp(J); 556 | } else if (ACCEPT('=')) { 557 | return TK_DIV_ASS; 558 | } else { 559 | return '/'; 560 | } 561 | } 562 | 563 | if (PEEK >= '0' && PEEK <= '9') { 564 | return lexnumber(J); 565 | } 566 | 567 | switch (PEEK) { 568 | case '(': NEXT(); return '('; 569 | case ')': NEXT(); return ')'; 570 | case ',': NEXT(); return ','; 571 | case ':': NEXT(); return ':'; 572 | case ';': NEXT(); return ';'; 573 | case '?': NEXT(); return '?'; 574 | case '[': NEXT(); return '['; 575 | case ']': NEXT(); return ']'; 576 | case '{': NEXT(); return '{'; 577 | case '}': NEXT(); return '}'; 578 | case '~': NEXT(); return '~'; 579 | 580 | case '\'': 581 | case '"': 582 | return lexstring(J); 583 | 584 | case '.': 585 | return lexnumber(J); 586 | 587 | case '<': 588 | NEXT(); 589 | if (ACCEPT('<')) { 590 | if (ACCEPT('=')) 591 | return TK_SHL_ASS; 592 | return TK_SHL; 593 | } 594 | if (ACCEPT('=')) 595 | return TK_LE; 596 | return '<'; 597 | 598 | case '>': 599 | NEXT(); 600 | if (ACCEPT('>')) { 601 | if (ACCEPT('>')) { 602 | if (ACCEPT('=')) 603 | return TK_USHR_ASS; 604 | return TK_USHR; 605 | } 606 | if (ACCEPT('=')) 607 | return TK_SHR_ASS; 608 | return TK_SHR; 609 | } 610 | if (ACCEPT('=')) 611 | return TK_GE; 612 | return '>'; 613 | 614 | case '=': 615 | NEXT(); 616 | if (ACCEPT('=')) { 617 | if (ACCEPT('=')) 618 | return TK_STRICTEQ; 619 | return TK_EQ; 620 | } 621 | return '='; 622 | 623 | case '!': 624 | NEXT(); 625 | if (ACCEPT('=')) { 626 | if (ACCEPT('=')) 627 | return TK_STRICTNE; 628 | return TK_NE; 629 | } 630 | return '!'; 631 | 632 | case '+': 633 | NEXT(); 634 | if (ACCEPT('+')) 635 | return TK_INC; 636 | if (ACCEPT('=')) 637 | return TK_ADD_ASS; 638 | return '+'; 639 | 640 | case '-': 641 | NEXT(); 642 | if (ACCEPT('-')) 643 | return TK_DEC; 644 | if (ACCEPT('=')) 645 | return TK_SUB_ASS; 646 | return '-'; 647 | 648 | case '*': 649 | NEXT(); 650 | if (ACCEPT('=')) 651 | return TK_MUL_ASS; 652 | return '*'; 653 | 654 | case '%': 655 | NEXT(); 656 | if (ACCEPT('=')) 657 | return TK_MOD_ASS; 658 | return '%'; 659 | 660 | case '&': 661 | NEXT(); 662 | if (ACCEPT('&')) 663 | return TK_AND; 664 | if (ACCEPT('=')) 665 | return TK_AND_ASS; 666 | return '&'; 667 | 668 | case '|': 669 | NEXT(); 670 | if (ACCEPT('|')) 671 | return TK_OR; 672 | if (ACCEPT('=')) 673 | return TK_OR_ASS; 674 | return '|'; 675 | 676 | case '^': 677 | NEXT(); 678 | if (ACCEPT('=')) 679 | return TK_XOR_ASS; 680 | return '^'; 681 | 682 | case 0: 683 | return 0; /* EOF */ 684 | } 685 | 686 | /* Handle \uXXXX escapes in identifiers */ 687 | jsY_unescape(J); 688 | if (jsY_isidentifierstart(PEEK)) { 689 | textinit(J); 690 | textpush(J, PEEK); 691 | 692 | NEXT(); 693 | jsY_unescape(J); 694 | while (jsY_isidentifierpart(PEEK)) { 695 | textpush(J, PEEK); 696 | NEXT(); 697 | jsY_unescape(J); 698 | } 699 | 700 | textend(J); 701 | 702 | return jsY_findkeyword(J, J->lexbuf.text); 703 | } 704 | 705 | if (PEEK >= 0x20 && PEEK <= 0x7E) 706 | jsY_error(J, "unexpected character: '%c'", PEEK); 707 | jsY_error(J, "unexpected character: \\u%04X", PEEK); 708 | } 709 | } 710 | 711 | void jsY_initlex(js_State *J, const char *filename, const char *source) 712 | { 713 | J->filename = filename; 714 | J->source = source; 715 | J->line = 1; 716 | J->lasttoken = 0; 717 | NEXT(); /* load first lookahead character */ 718 | } 719 | 720 | int jsY_lex(js_State *J) 721 | { 722 | return J->lasttoken = jsY_lexx(J); 723 | } 724 | 725 | int jsY_lexjson(js_State *J) 726 | { 727 | while (1) { 728 | J->lexline = J->line; /* save location of beginning of token */ 729 | 730 | while (jsY_iswhite(PEEK) || PEEK == '\n') 731 | NEXT(); 732 | 733 | if (PEEK >= '0' && PEEK <= '9') { 734 | return lexnumber(J); 735 | } 736 | 737 | switch (PEEK) { 738 | case ',': NEXT(); return ','; 739 | case ':': NEXT(); return ':'; 740 | case '[': NEXT(); return '['; 741 | case ']': NEXT(); return ']'; 742 | case '{': NEXT(); return '{'; 743 | case '}': NEXT(); return '}'; 744 | 745 | case '\'': 746 | case '"': 747 | return lexstring(J); 748 | 749 | case '.': 750 | return lexnumber(J); 751 | 752 | case 0: 753 | return 0; /* EOF */ 754 | } 755 | 756 | if (PEEK >= 0x20 && PEEK <= 0x7E) 757 | jsY_error(J, "unexpected character: '%c'", PEEK); 758 | jsY_error(J, "unexpected character: \\u%04X", PEEK); 759 | } 760 | } 761 | -------------------------------------------------------------------------------- /jsdump.c: -------------------------------------------------------------------------------- 1 | #include "jsi.h" 2 | #include "jsparse.h" 3 | #include "jscompile.h" 4 | #include "jsvalue.h" 5 | 6 | #include "utf.h" 7 | 8 | #include 9 | 10 | static const char *astname[] = { 11 | #include "astnames.h" 12 | }; 13 | 14 | static const char *opname[] = { 15 | #include "opnames.h" 16 | }; 17 | 18 | const char *jsP_aststring(enum js_AstType type) 19 | { 20 | if (type > nelem(astname)) 21 | return ""; 22 | return astname[type]; 23 | } 24 | 25 | const char *jsC_opcodestring(enum js_OpCode opcode) 26 | { 27 | if (opcode > nelem(opname)) 28 | return ""; 29 | return opname[opcode]; 30 | } 31 | 32 | static int prec(enum js_AstType type) 33 | { 34 | switch (type) { 35 | case EXP_IDENTIFIER: 36 | case EXP_NUMBER: 37 | case EXP_STRING: 38 | case EXP_REGEXP: 39 | case EXP_UNDEF: 40 | case EXP_NULL: 41 | case EXP_TRUE: 42 | case EXP_FALSE: 43 | case EXP_THIS: 44 | case EXP_ARRAY: 45 | case EXP_OBJECT: 46 | return 170; 47 | 48 | case EXP_FUN: 49 | case EXP_INDEX: 50 | case EXP_MEMBER: 51 | case EXP_CALL: 52 | case EXP_NEW: 53 | return 160; 54 | 55 | case EXP_POSTINC: 56 | case EXP_POSTDEC: 57 | return 150; 58 | 59 | case EXP_DELETE: 60 | case EXP_VOID: 61 | case EXP_TYPEOF: 62 | case EXP_PREINC: 63 | case EXP_PREDEC: 64 | case EXP_POS: 65 | case EXP_NEG: 66 | case EXP_BITNOT: 67 | case EXP_LOGNOT: 68 | return 140; 69 | 70 | case EXP_MOD: 71 | case EXP_DIV: 72 | case EXP_MUL: 73 | return 130; 74 | 75 | case EXP_SUB: 76 | case EXP_ADD: 77 | return 120; 78 | 79 | case EXP_USHR: 80 | case EXP_SHR: 81 | case EXP_SHL: 82 | return 110; 83 | 84 | case EXP_IN: 85 | case EXP_INSTANCEOF: 86 | case EXP_GE: 87 | case EXP_LE: 88 | case EXP_GT: 89 | case EXP_LT: 90 | return 100; 91 | 92 | case EXP_STRICTNE: 93 | case EXP_STRICTEQ: 94 | case EXP_NE: 95 | case EXP_EQ: 96 | return 90; 97 | 98 | case EXP_BITAND: return 80; 99 | case EXP_BITXOR: return 70; 100 | case EXP_BITOR: return 60; 101 | case EXP_LOGAND: return 50; 102 | case EXP_LOGOR: return 40; 103 | 104 | case EXP_COND: 105 | return 30; 106 | 107 | case EXP_ASS: 108 | case EXP_ASS_MUL: 109 | case EXP_ASS_DIV: 110 | case EXP_ASS_MOD: 111 | case EXP_ASS_ADD: 112 | case EXP_ASS_SUB: 113 | case EXP_ASS_SHL: 114 | case EXP_ASS_SHR: 115 | case EXP_ASS_USHR: 116 | case EXP_ASS_BITAND: 117 | case EXP_ASS_BITXOR: 118 | case EXP_ASS_BITOR: 119 | return 20; 120 | 121 | #define COMMA 15 122 | 123 | case EXP_COMMA: 124 | return 10; 125 | 126 | default: 127 | return 0; 128 | } 129 | } 130 | 131 | static void pc(int c) 132 | { 133 | putchar(c); 134 | } 135 | 136 | static void ps(const char *s) 137 | { 138 | fputs(s, stdout); 139 | } 140 | 141 | static void in(int d) 142 | { 143 | while (d-- > 0) 144 | putchar('\t'); 145 | } 146 | 147 | static void nl(void) 148 | { 149 | putchar('\n'); 150 | } 151 | 152 | /* Pretty-printed Javascript syntax */ 153 | 154 | static void pstmlist(int d, js_Ast *list); 155 | static void pexpi(int d, int i, js_Ast *exp); 156 | static void pstm(int d, js_Ast *stm); 157 | static void slist(int d, js_Ast *list); 158 | static void sblock(int d, js_Ast *list); 159 | 160 | static void pargs(int d, js_Ast *list) 161 | { 162 | while (list) { 163 | assert(list->type == AST_LIST); 164 | pexpi(d, COMMA, list->a); 165 | list = list->b; 166 | if (list) 167 | ps(", "); 168 | } 169 | } 170 | 171 | static void parray(int d, js_Ast *list) 172 | { 173 | ps("["); 174 | while (list) { 175 | assert(list->type == AST_LIST); 176 | pexpi(d, COMMA, list->a); 177 | list = list->b; 178 | if (list) 179 | ps(", "); 180 | } 181 | ps("]"); 182 | } 183 | 184 | static void pobject(int d, js_Ast *list) 185 | { 186 | ps("{"); 187 | while (list) { 188 | js_Ast *kv = list->a; 189 | assert(list->type == AST_LIST); 190 | switch (kv->type) { 191 | case EXP_PROP_VAL: 192 | pexpi(d, COMMA, kv->a); 193 | ps(": "); 194 | pexpi(d, COMMA, kv->b); 195 | break; 196 | case EXP_PROP_GET: 197 | ps("get "); 198 | pexpi(d, COMMA, kv->a); 199 | ps("() {\n"); 200 | pstmlist(d, kv->c); 201 | in(d); ps("}"); 202 | break; 203 | case EXP_PROP_SET: 204 | ps("set "); 205 | pexpi(d, COMMA, kv->a); 206 | ps("("); 207 | pargs(d, kv->b); 208 | ps(") {\n"); 209 | pstmlist(d, kv->c); 210 | in(d); ps("}"); 211 | break; 212 | } 213 | list = list->b; 214 | if (list) 215 | ps(", "); 216 | } 217 | ps("}"); 218 | } 219 | 220 | static void pstr(const char *s) 221 | { 222 | static const char *HEX = "0123456789ABCDEF"; 223 | Rune c; 224 | pc('"'); 225 | while (*s) { 226 | s += chartorune(&c, s); 227 | switch (c) { 228 | case '"': ps("\\\""); break; 229 | case '\\': ps("\\\\"); break; 230 | case '\b': ps("\\b"); break; 231 | case '\f': ps("\\f"); break; 232 | case '\n': ps("\\n"); break; 233 | case '\r': ps("\\r"); break; 234 | case '\t': ps("\\t"); break; 235 | default: 236 | if (c < ' ' || c > 127) { 237 | ps("\\u"); 238 | pc(HEX[(c>>12)&15]); 239 | pc(HEX[(c>>8)&15]); 240 | pc(HEX[(c>>4)&15]); 241 | pc(HEX[c&15]); 242 | } else { 243 | pc(c); break; 244 | } 245 | } 246 | } 247 | pc('"'); 248 | } 249 | 250 | static void pregexp(const char *prog, int flags) 251 | { 252 | pc('/'); 253 | ps(prog); 254 | pc('/'); 255 | if (flags & JS_REGEXP_G) pc('g'); 256 | if (flags & JS_REGEXP_I) pc('i'); 257 | if (flags & JS_REGEXP_M) pc('m'); 258 | } 259 | 260 | static void pbin(int d, int p, js_Ast *exp, const char *op) 261 | { 262 | pexpi(d, p, exp->a); 263 | ps(op); 264 | pexpi(d, p, exp->b); 265 | } 266 | 267 | static void puna(int d, int p, js_Ast *exp, const char *pre, const char *suf) 268 | { 269 | ps(pre); 270 | pexpi(d, p, exp->a); 271 | ps(suf); 272 | } 273 | 274 | static void pexpi(int d, int p, js_Ast *exp) 275 | { 276 | int tp = prec(exp->type); 277 | int paren = 0; 278 | if (tp < p) { 279 | pc('('); 280 | paren = 1; 281 | } 282 | p = tp; 283 | 284 | switch (exp->type) { 285 | case AST_IDENTIFIER: ps(exp->string); break; 286 | case EXP_IDENTIFIER: ps(exp->string); break; 287 | case EXP_NUMBER: printf("%.9g", exp->number); break; 288 | case EXP_STRING: pstr(exp->string); break; 289 | case EXP_REGEXP: pregexp(exp->string, exp->number); break; 290 | 291 | case EXP_UNDEF: break; 292 | case EXP_NULL: ps("null"); break; 293 | case EXP_TRUE: ps("true"); break; 294 | case EXP_FALSE: ps("false"); break; 295 | case EXP_THIS: ps("this"); break; 296 | 297 | case EXP_OBJECT: pobject(d, exp->a); break; 298 | case EXP_ARRAY: parray(d, exp->a); break; 299 | 300 | case EXP_DELETE: puna(d, p, exp, "delete ", ""); break; 301 | case EXP_VOID: puna(d, p, exp, "void ", ""); break; 302 | case EXP_TYPEOF: puna(d, p, exp, "typeof ", ""); break; 303 | case EXP_PREINC: puna(d, p, exp, "++", ""); break; 304 | case EXP_PREDEC: puna(d, p, exp, "--", ""); break; 305 | case EXP_POSTINC: puna(d, p, exp, "", "++"); break; 306 | case EXP_POSTDEC: puna(d, p, exp, "", "--"); break; 307 | case EXP_POS: puna(d, p, exp, "+", ""); break; 308 | case EXP_NEG: puna(d, p, exp, "-", ""); break; 309 | case EXP_BITNOT: puna(d, p, exp, "~", ""); break; 310 | case EXP_LOGNOT: puna(d, p, exp, "!", ""); break; 311 | 312 | case EXP_LOGOR: pbin(d, p, exp, " || "); break; 313 | case EXP_LOGAND: pbin(d, p, exp, " && "); break; 314 | case EXP_BITOR: pbin(d, p, exp, " | "); break; 315 | case EXP_BITXOR: pbin(d, p, exp, " ^ "); break; 316 | case EXP_BITAND: pbin(d, p, exp, " & "); break; 317 | case EXP_EQ: pbin(d, p, exp, " == "); break; 318 | case EXP_NE: pbin(d, p, exp, " != "); break; 319 | case EXP_STRICTEQ: pbin(d, p, exp, " === "); break; 320 | case EXP_STRICTNE: pbin(d, p, exp, " !== "); break; 321 | case EXP_LT: pbin(d, p, exp, " < "); break; 322 | case EXP_GT: pbin(d, p, exp, " > "); break; 323 | case EXP_LE: pbin(d, p, exp, " <= "); break; 324 | case EXP_GE: pbin(d, p, exp, " >= "); break; 325 | case EXP_INSTANCEOF: pbin(d, p, exp, " instanceof "); break; 326 | case EXP_IN: pbin(d, p, exp, " in "); break; 327 | case EXP_SHL: pbin(d, p, exp, " << "); break; 328 | case EXP_SHR: pbin(d, p, exp, " >> "); break; 329 | case EXP_USHR: pbin(d, p, exp, " >>> "); break; 330 | case EXP_ADD: pbin(d, p, exp, " + "); break; 331 | case EXP_SUB: pbin(d, p, exp, " - "); break; 332 | case EXP_MUL: pbin(d, p, exp, " * "); break; 333 | case EXP_DIV: pbin(d, p, exp, " / "); break; 334 | case EXP_MOD: pbin(d, p, exp, " % "); break; 335 | case EXP_ASS: pbin(d, p, exp, " = "); break; 336 | case EXP_ASS_MUL: pbin(d, p, exp, " *= "); break; 337 | case EXP_ASS_DIV: pbin(d, p, exp, " /= "); break; 338 | case EXP_ASS_MOD: pbin(d, p, exp, " %= "); break; 339 | case EXP_ASS_ADD: pbin(d, p, exp, " += "); break; 340 | case EXP_ASS_SUB: pbin(d, p, exp, " -= "); break; 341 | case EXP_ASS_SHL: pbin(d, p, exp, " <<= "); break; 342 | case EXP_ASS_SHR: pbin(d, p, exp, " >>= "); break; 343 | case EXP_ASS_USHR: pbin(d, p, exp, " >>>= "); break; 344 | case EXP_ASS_BITAND: pbin(d, p, exp, " &= "); break; 345 | case EXP_ASS_BITXOR: pbin(d, p, exp, " ^= "); break; 346 | case EXP_ASS_BITOR: pbin(d, p, exp, " |= "); break; 347 | 348 | case EXP_COMMA: pbin(d, p, exp, ", "); break; 349 | 350 | case EXP_COND: 351 | pexpi(d, p, exp->a); 352 | ps(" ? "); 353 | pexpi(d, p, exp->b); 354 | ps(" : "); 355 | pexpi(d, p, exp->c); 356 | break; 357 | 358 | case EXP_INDEX: 359 | pexpi(d, p, exp->a); 360 | pc('['); 361 | pexpi(d, 0, exp->b); 362 | pc(']'); 363 | break; 364 | 365 | case EXP_MEMBER: 366 | pexpi(d, p, exp->a); 367 | pc('.'); 368 | pexpi(d, p, exp->b); 369 | break; 370 | 371 | case EXP_CALL: 372 | pexpi(d, p, exp->a); 373 | pc('('); 374 | pargs(d, exp->b); 375 | pc(')'); 376 | break; 377 | 378 | case EXP_NEW: 379 | ps("new "); 380 | pexpi(d, p, exp->a); 381 | pc('('); 382 | pargs(d, exp->b); 383 | pc(')'); 384 | break; 385 | 386 | case EXP_FUN: 387 | if (p == 0) pc('('); 388 | ps("function "); 389 | if (exp->a) pexpi(d, 0, exp->a); 390 | pc('('); 391 | pargs(d, exp->b); 392 | ps(") {\n"); 393 | pstmlist(d, exp->c); 394 | in(d); pc('}'); 395 | if (p == 0) pc(')'); 396 | break; 397 | 398 | default: 399 | ps(""); 400 | break; 401 | } 402 | 403 | if (paren) pc(')'); 404 | } 405 | 406 | static void pexp(int d, js_Ast *exp) 407 | { 408 | pexpi(d, 0, exp); 409 | } 410 | 411 | static void pvar(int d, js_Ast *var) 412 | { 413 | assert(var->type == EXP_VAR); 414 | pexp(d, var->a); 415 | if (var->b) { 416 | ps(" = "); 417 | pexp(d, var->b); 418 | } 419 | } 420 | 421 | static void pvarlist(int d, js_Ast *list) 422 | { 423 | while (list) { 424 | assert(list->type == AST_LIST); 425 | pvar(d, list->a); 426 | list = list->b; 427 | if (list) 428 | ps(", "); 429 | } 430 | } 431 | 432 | static void pblock(int d, js_Ast *block) 433 | { 434 | assert(block->type == STM_BLOCK); 435 | ps(" {\n"); 436 | pstmlist(d, block->a); 437 | in(d); pc('}'); 438 | } 439 | 440 | static void pstmh(int d, js_Ast *stm) 441 | { 442 | if (stm->type == STM_BLOCK) 443 | pblock(d, stm); 444 | else { 445 | nl(); 446 | pstm(d+1, stm); 447 | } 448 | } 449 | 450 | static void pcaselist(int d, js_Ast *list) 451 | { 452 | while (list) { 453 | js_Ast *stm = list->a; 454 | if (stm->type == STM_CASE) { 455 | in(d); ps("case "); pexp(d, stm->a); ps(":\n"); 456 | pstmlist(d, stm->b); 457 | } 458 | if (stm->type == STM_DEFAULT) { 459 | in(d); ps("default:\n"); 460 | pstmlist(d, stm->a); 461 | } 462 | list = list->b; 463 | } 464 | } 465 | 466 | static void pstm(int d, js_Ast *stm) 467 | { 468 | if (stm->type == STM_BLOCK) { 469 | pblock(d, stm); 470 | return; 471 | } 472 | 473 | in(d); 474 | 475 | switch (stm->type) { 476 | case AST_FUNDEC: 477 | ps("function "); 478 | pexp(d, stm->a); 479 | pc('('); 480 | pargs(d, stm->b); 481 | ps(") {\n"); 482 | pstmlist(d, stm->c); 483 | in(d); ps("}"); 484 | break; 485 | 486 | case STM_EMPTY: 487 | pc(';'); 488 | break; 489 | 490 | case STM_VAR: 491 | ps("var "); 492 | pvarlist(d, stm->a); 493 | ps(";"); 494 | break; 495 | 496 | case STM_IF: 497 | ps("if ("); pexp(d, stm->a); ps(")"); 498 | pstmh(d, stm->b); 499 | if (stm->c) { 500 | nl(); in(d); ps("else"); 501 | pstmh(d, stm->c); 502 | } 503 | break; 504 | 505 | case STM_DO: 506 | ps("do"); 507 | pstmh(d, stm->a); 508 | nl(); 509 | in(d); ps("while ("); pexp(d, stm->b); ps(");"); 510 | break; 511 | 512 | case STM_WHILE: 513 | ps("while ("); pexp(d, stm->a); ps(")"); 514 | pstmh(d, stm->b); 515 | break; 516 | 517 | case STM_FOR: 518 | ps("for ("); 519 | pexp(d, stm->a); ps("; "); 520 | pexp(d, stm->b); ps("; "); 521 | pexp(d, stm->c); ps(")"); 522 | pstmh(d, stm->d); 523 | break; 524 | case STM_FOR_VAR: 525 | ps("for (var "); 526 | pvarlist(d, stm->a); ps("; "); 527 | pexp(d, stm->b); ps("; "); 528 | pexp(d, stm->c); ps(")"); 529 | pstmh(d, stm->d); 530 | break; 531 | case STM_FOR_IN: 532 | ps("for ("); 533 | pexp(d, stm->a); ps(" in "); 534 | pexp(d, stm->b); ps(")"); 535 | pstmh(d, stm->c); 536 | break; 537 | case STM_FOR_IN_VAR: 538 | ps("for (var "); 539 | pvarlist(d, stm->a); ps(" in "); 540 | pexp(d, stm->b); ps(")"); 541 | pstmh(d, stm->c); 542 | break; 543 | 544 | case STM_CONTINUE: 545 | if (stm->a) { 546 | ps("continue "); pexp(d, stm->a); ps(";"); 547 | } else { 548 | ps("continue;"); 549 | } 550 | break; 551 | 552 | case STM_BREAK: 553 | if (stm->a) { 554 | ps("break "); pexp(d, stm->a); ps(";"); 555 | } else { 556 | ps("break;"); 557 | } 558 | break; 559 | 560 | case STM_RETURN: 561 | if (stm->a) { 562 | ps("return "); pexp(d, stm->a); ps(";"); 563 | } else { 564 | ps("return;"); 565 | } 566 | break; 567 | 568 | case STM_WITH: 569 | ps("with ("); pexp(d, stm->a); ps(")"); 570 | pstmh(d, stm->b); 571 | break; 572 | 573 | case STM_SWITCH: 574 | ps("switch ("); 575 | pexp(d, stm->a); 576 | ps(") {\n"); 577 | pcaselist(d, stm->b); 578 | in(d); ps("}"); 579 | break; 580 | 581 | case STM_THROW: 582 | ps("throw "); pexp(d, stm->a); ps(";"); 583 | break; 584 | 585 | case STM_TRY: 586 | ps("try"); 587 | pstmh(d, stm->a); 588 | if (stm->b && stm->c) { 589 | nl(); in(d); ps("catch ("); pexp(d, stm->b); ps(")"); 590 | pstmh(d, stm->c); 591 | } 592 | if (stm->d) { 593 | nl(); in(d); ps("finally"); 594 | pstmh(d, stm->d); 595 | } 596 | break; 597 | 598 | case STM_LABEL: 599 | pexp(d, stm->a); ps(": "); pstm(d, stm->b); 600 | break; 601 | 602 | case STM_DEBUGGER: 603 | ps("debugger;"); 604 | break; 605 | 606 | default: 607 | pexp(d, stm); pc(';'); 608 | } 609 | } 610 | 611 | static void pstmlist(int d, js_Ast *list) 612 | { 613 | while (list) { 614 | assert(list->type == AST_LIST); 615 | pstm(d+1, list->a); 616 | nl(); 617 | list = list->b; 618 | } 619 | } 620 | 621 | void jsP_dumpsyntax(js_State *J, js_Ast *prog) 622 | { 623 | if (prog->type == AST_LIST) 624 | pstmlist(-1, prog); 625 | else { 626 | pstm(0, prog); 627 | nl(); 628 | } 629 | } 630 | 631 | /* S-expression list representation */ 632 | 633 | static void snode(int d, js_Ast *node) 634 | { 635 | void (*afun)(int,js_Ast*) = snode; 636 | void (*bfun)(int,js_Ast*) = snode; 637 | void (*cfun)(int,js_Ast*) = snode; 638 | void (*dfun)(int,js_Ast*) = snode; 639 | 640 | if (!node) { 641 | return; 642 | } 643 | 644 | if (node->type == AST_LIST) { 645 | slist(d, node); 646 | return; 647 | } 648 | 649 | pc('('); 650 | ps(astname[node->type]); 651 | switch (node->type) { 652 | case AST_IDENTIFIER: pc(' '); ps(node->string); break; 653 | case EXP_IDENTIFIER: pc(' '); ps(node->string); break; 654 | case EXP_STRING: pc(' '); pstr(node->string); break; 655 | case EXP_REGEXP: pc(' '); pregexp(node->string, node->number); break; 656 | case EXP_NUMBER: printf(" %.9g", node->number); break; 657 | case STM_BLOCK: afun = sblock; break; 658 | case AST_FUNDEC: case EXP_FUN: cfun = sblock; break; 659 | case EXP_PROP_GET: cfun = sblock; break; 660 | case EXP_PROP_SET: cfun = sblock; break; 661 | case STM_SWITCH: bfun = sblock; break; 662 | case STM_CASE: bfun = sblock; break; 663 | case STM_DEFAULT: afun = sblock; break; 664 | } 665 | if (node->a) { pc(' '); afun(d, node->a); } 666 | if (node->b) { pc(' '); bfun(d, node->b); } 667 | if (node->c) { pc(' '); cfun(d, node->c); } 668 | if (node->d) { pc(' '); dfun(d, node->d); } 669 | pc(')'); 670 | } 671 | 672 | static void slist(int d, js_Ast *list) 673 | { 674 | pc('['); 675 | while (list) { 676 | assert(list->type == AST_LIST); 677 | snode(d, list->a); 678 | list = list->b; 679 | if (list) 680 | pc(' '); 681 | } 682 | pc(']'); 683 | } 684 | 685 | static void sblock(int d, js_Ast *list) 686 | { 687 | ps("[\n"); 688 | in(d+1); 689 | while (list) { 690 | assert(list->type == AST_LIST); 691 | snode(d+1, list->a); 692 | list = list->b; 693 | if (list) { 694 | nl(); 695 | in(d+1); 696 | } 697 | } 698 | nl(); in(d); pc(']'); 699 | } 700 | 701 | void jsP_dumplist(js_State *J, js_Ast *prog) 702 | { 703 | if (prog->type == AST_LIST) 704 | sblock(0, prog); 705 | else 706 | snode(0, prog); 707 | nl(); 708 | } 709 | 710 | /* Compiled code */ 711 | 712 | void jsC_dumpfunction(js_State *J, js_Function *F) 713 | { 714 | js_Instruction *p = F->code; 715 | js_Instruction *end = F->code + F->codelen; 716 | unsigned int i; 717 | 718 | printf("%s(%d)\n", F->name, F->numparams); 719 | if (F->lightweight) printf("\tlightweight\n"); 720 | if (F->arguments) printf("\targuments\n"); 721 | printf("\tsource %s:%d\n", F->filename, F->line); 722 | for (i = 0; i < F->funlen; ++i) 723 | printf("\tfunction %d %s\n", i, F->funtab[i]->name); 724 | for (i = 0; i < F->varlen; ++i) 725 | printf("\tlocal %d %s\n", i + 1, F->vartab[i]); 726 | 727 | printf("{\n"); 728 | while (p < end) { 729 | int c = *p++; 730 | 731 | printf("% 5d: ", (int)(p - F->code) - 1); 732 | ps(opname[c]); 733 | 734 | switch (c) { 735 | case OP_NUMBER: 736 | printf(" %.9g", F->numtab[*p++]); 737 | break; 738 | case OP_STRING: 739 | pc(' '); 740 | pstr(F->strtab[*p++]); 741 | break; 742 | case OP_NEWREGEXP: 743 | pc(' '); 744 | pregexp(F->strtab[p[0]], p[1]); 745 | p += 2; 746 | break; 747 | 748 | case OP_INITVAR: 749 | case OP_DEFVAR: 750 | case OP_GETVAR: 751 | case OP_SETVAR: 752 | case OP_DELVAR: 753 | case OP_GETPROP_S: 754 | case OP_SETPROP_S: 755 | case OP_DELPROP_S: 756 | case OP_INITPROP_S: 757 | case OP_CATCH: 758 | pc(' '); 759 | ps(F->strtab[*p++]); 760 | break; 761 | 762 | case OP_CLOSURE: 763 | case OP_INITLOCAL: 764 | case OP_GETLOCAL: 765 | case OP_SETLOCAL: 766 | case OP_DELLOCAL: 767 | case OP_NUMBER_POS: 768 | case OP_NUMBER_NEG: 769 | case OP_INITPROP_N: 770 | case OP_CALL: 771 | case OP_NEW: 772 | case OP_JUMP: 773 | case OP_JTRUE: 774 | case OP_JFALSE: 775 | case OP_JCASE: 776 | case OP_TRY: 777 | printf(" %d", *p++); 778 | break; 779 | } 780 | 781 | nl(); 782 | } 783 | printf("}\n"); 784 | 785 | for (i = 0; i < F->funlen; ++i) { 786 | if (F->funtab[i] != F) { 787 | printf("function %d ", i); 788 | jsC_dumpfunction(J, F->funtab[i]); 789 | } 790 | } 791 | } 792 | 793 | /* Runtime values */ 794 | 795 | void js_dumpvalue(js_State *J, js_Value v) 796 | { 797 | switch (v.type) { 798 | case JS_TUNDEFINED: printf("undefined"); break; 799 | case JS_TNULL: printf("null"); break; 800 | case JS_TBOOLEAN: printf(v.u.boolean ? "true" : "false"); break; 801 | case JS_TNUMBER: printf("%.9g", v.u.number); break; 802 | case JS_TSTRING: printf("'%s'", v.u.string); break; 803 | case JS_TOBJECT: 804 | if (v.u.object == J->G) { 805 | printf("[Global]"); 806 | break; 807 | } 808 | switch (v.u.object->type) { 809 | case JS_COBJECT: printf("[Object %p]", v.u.object); break; 810 | case JS_CARRAY: printf("[Array %p]", v.u.object); break; 811 | case JS_CFUNCTION: 812 | printf("[Function %p, %s, %s:%d]", 813 | v.u.object, 814 | v.u.object->u.f.function->name, 815 | v.u.object->u.f.function->filename, 816 | v.u.object->u.f.function->line); 817 | break; 818 | case JS_CSCRIPT: printf("[Script %s]", v.u.object->u.f.function->filename); break; 819 | case JS_CCFUNCTION: printf("[CFunction %p]", v.u.object->u.c.function); break; 820 | case JS_CBOOLEAN: printf("[Boolean %d]", v.u.object->u.boolean); break; 821 | case JS_CNUMBER: printf("[Number %g]", v.u.object->u.number); break; 822 | case JS_CSTRING: printf("[String'%s']", v.u.object->u.s.string); break; 823 | case JS_CERROR: printf("[Error %s]", v.u.object->u.s.string); break; 824 | case JS_CITERATOR: printf("[Iterator %p]", v.u.object); break; 825 | case JS_CUSERDATA: 826 | printf("[Userdata %s %p]", v.u.object->u.user.tag, v.u.object->u.user.data); 827 | break; 828 | default: printf("[Object %p]", v.u.object); break; 829 | } 830 | break; 831 | } 832 | } 833 | 834 | static void js_dumpproperty(js_State *J, js_Property *node) 835 | { 836 | if (node->left->level) 837 | js_dumpproperty(J, node->left); 838 | printf("\t%s: ", node->name); 839 | js_dumpvalue(J, node->value); 840 | printf(",\n"); 841 | if (node->right->level) 842 | js_dumpproperty(J, node->right); 843 | } 844 | 845 | void js_dumpobject(js_State *J, js_Object *obj) 846 | { 847 | printf("{\n"); 848 | if (obj->properties->level) 849 | js_dumpproperty(J, obj->properties); 850 | printf("}\n"); 851 | } 852 | --------------------------------------------------------------------------------