📄 js.c
字号:
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sw=4 et tw=78: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** *//* * JS shell. */#include "jsstddef.h"#include <errno.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <locale.h>#include "jstypes.h"#include "jsarena.h"#include "jsutil.h"#include "jsprf.h"#include "jsapi.h"#include "jsatom.h"#include "jscntxt.h"#include "jsdbgapi.h"#include "jsemit.h"#include "jsfun.h"#include "jsgc.h"#include "jslock.h"#include "jsobj.h"#include "jsparse.h"#include "jsscope.h"#include "jsscript.h"#ifdef PERLCONNECT#include "perlconnect/jsperl.h"#endif#ifdef LIVECONNECT#include "jsjava.h"#endif#ifdef JSDEBUGGER#include "jsdebug.h"#ifdef JSDEBUGGER_JAVA_UI#include "jsdjava.h"#endif /* JSDEBUGGER_JAVA_UI */#ifdef JSDEBUGGER_C_UI#include "jsdb.h"#endif /* JSDEBUGGER_C_UI */#endif /* JSDEBUGGER */#ifdef XP_UNIX#include <unistd.h>#include <sys/types.h>#include <sys/wait.h>#endif#if defined(XP_WIN) || defined(XP_OS2)#include <io.h> /* for isatty() */#endiftypedef enum JSShellExitCode { EXITCODE_RUNTIME_ERROR = 3, EXITCODE_FILE_NOT_FOUND = 4, EXITCODE_OUT_OF_MEMORY = 5} JSShellExitCode;size_t gStackChunkSize = 8192;/* Assume that we can not use more than 5e5 bytes of C stack by default. */static size_t gMaxStackSize = 500000;static jsuword gStackBase;int gExitCode = 0;JSBool gQuitting = JS_FALSE;FILE *gErrFile = NULL;FILE *gOutFile = NULL;#ifdef JSDEBUGGERstatic JSDContext *_jsdc;#ifdef JSDEBUGGER_JAVA_UIstatic JSDJContext *_jsdjc;#endif /* JSDEBUGGER_JAVA_UI */#endif /* JSDEBUGGER */static JSBool reportWarnings = JS_TRUE;static JSBool compileOnly = JS_FALSE;typedef enum JSShellErrNum {#define MSG_DEF(name, number, count, exception, format) \ name = number,#include "jsshell.msg"#undef MSG_DEF JSShellErr_Limit#undef MSGDEF} JSShellErrNum;static const JSErrorFormatString *my_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber);static JSObject *split_setup(JSContext *cx);#ifdef EDITLINEextern char *readline(const char *prompt);extern void add_history(char *line);#endifstatic JSBoolGetLine(JSContext *cx, char *bufp, FILE *file, const char *prompt) {#ifdef EDITLINE /* * Use readline only if file is stdin, because there's no way to specify * another handle. Are other filehandles interactive? */ if (file == stdin) { char *linep = readline(prompt); if (!linep) return JS_FALSE; if (linep[0] != '\0') add_history(linep); strcpy(bufp, linep); JS_free(cx, linep); bufp += strlen(bufp); *bufp++ = '\n'; *bufp = '\0'; } else#endif { char line[256]; fprintf(gOutFile, prompt); fflush(gOutFile); if (!fgets(line, sizeof line, file)) return JS_FALSE; strcpy(bufp, line); } return JS_TRUE;}static voidProcess(JSContext *cx, JSObject *obj, char *filename, JSBool forceTTY){ JSBool ok, hitEOF; JSScript *script; jsval result; JSString *str; char buffer[4096]; char *bufp; int lineno; int startline; FILE *file; jsuword stackLimit; if (forceTTY || !filename || strcmp(filename, "-") == 0) { file = stdin; } else { file = fopen(filename, "r"); if (!file) { JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_CANT_OPEN, filename, strerror(errno)); gExitCode = EXITCODE_FILE_NOT_FOUND; return; } } if (gMaxStackSize == 0) { /* * Disable checking for stack overflow if limit is zero. */ stackLimit = 0; } else {#if JS_STACK_GROWTH_DIRECTION > 0 stackLimit = gStackBase + gMaxStackSize;#else stackLimit = gStackBase - gMaxStackSize;#endif } JS_SetThreadStackLimit(cx, stackLimit); if (!forceTTY && !isatty(fileno(file))) { /* * It's not interactive - just execute it. * * Support the UNIX #! shell hack; gobble the first line if it starts * with '#'. TODO - this isn't quite compatible with sharp variables, * as a legal js program (using sharp variables) might start with '#'. * But that would require multi-character lookahead. */ int ch = fgetc(file); if (ch == '#') { while((ch = fgetc(file)) != EOF) { if (ch == '\n' || ch == '\r') break; } } ungetc(ch, file); script = JS_CompileFileHandle(cx, obj, filename, file); if (script) { if (!compileOnly) (void)JS_ExecuteScript(cx, obj, script, &result); JS_DestroyScript(cx, script); } return; } /* It's an interactive filehandle; drop into read-eval-print loop. */ lineno = 1; hitEOF = JS_FALSE; do { bufp = buffer; *bufp = '\0'; /* * Accumulate lines until we get a 'compilable unit' - one that either * generates an error (before running out of source) or that compiles * cleanly. This should be whenever we get a complete statement that * coincides with the end of a line. */ startline = lineno; do { if (!GetLine(cx, bufp, file, startline == lineno ? "js> " : "")) { hitEOF = JS_TRUE; break; } bufp += strlen(bufp); lineno++; } while (!JS_BufferIsCompilableUnit(cx, obj, buffer, strlen(buffer))); /* Clear any pending exception from previous failed compiles. */ JS_ClearPendingException(cx); script = JS_CompileScript(cx, obj, buffer, strlen(buffer), "typein", startline); if (script) { if (!compileOnly) { ok = JS_ExecuteScript(cx, obj, script, &result); if (ok && result != JSVAL_VOID) { str = JS_ValueToString(cx, result); if (str) fprintf(gOutFile, "%s\n", JS_GetStringBytes(str)); else ok = JS_FALSE; } } JS_DestroyScript(cx, script); } } while (!hitEOF && !gQuitting); fprintf(gOutFile, "\n"); return;}static intusage(void){ fprintf(gErrFile, "%s\n", JS_GetImplementationVersion()); fprintf(gErrFile, "usage: js [-PswWxCi] [-b branchlimit] [-c stackchunksize] [-v version] [-f scriptfile] [-e script] [-S maxstacksize] [scriptfile] [scriptarg...]\n"); return 2;}static uint32 gBranchCount;static uint32 gBranchLimit;static JSBoolmy_BranchCallback(JSContext *cx, JSScript *script){ if (++gBranchCount == gBranchLimit) { if (script) { if (script->filename) fprintf(gErrFile, "%s:", script->filename); fprintf(gErrFile, "%u: script branch callback (%u callbacks)\n", script->lineno, gBranchLimit); } else { fprintf(gErrFile, "native branch callback (%u callbacks)\n", gBranchLimit); } gBranchCount = 0; return JS_FALSE; } if ((gBranchCount & 0x3fff) == 1) JS_MaybeGC(cx); return JS_TRUE;}extern JSClass global_class;static intProcessArgs(JSContext *cx, JSObject *obj, char **argv, int argc){ int i, j, length; JSObject *argsObj; char *filename = NULL; JSBool isInteractive = JS_TRUE; JSBool forceTTY = JS_FALSE; /* * Scan past all optional arguments so we can create the arguments object * before processing any -f options, which must interleave properly with * -v and -w options. This requires two passes, and without getopt, we'll * have to keep the option logic here and in the second for loop in sync. */ for (i = 0; i < argc; i++) { if (argv[i][0] != '-' || argv[i][1] == '\0') { ++i; break; } switch (argv[i][1]) { case 'b': case 'c': case 'f': case 'e': case 'v': case 'S': ++i; break; default:; } } /* * Create arguments early and define it to root it, so it's safe from any * GC calls nested below, and so it is available to -f <file> arguments. */ argsObj = JS_NewArrayObject(cx, 0, NULL); if (!argsObj) return 1; if (!JS_DefineProperty(cx, obj, "arguments", OBJECT_TO_JSVAL(argsObj), NULL, NULL, 0)) { return 1; } length = argc - i; for (j = 0; j < length; j++) { JSString *str = JS_NewStringCopyZ(cx, argv[i++]); if (!str) return 1; if (!JS_DefineElement(cx, argsObj, j, STRING_TO_JSVAL(str), NULL, NULL, JSPROP_ENUMERATE)) { return 1; } } for (i = 0; i < argc; i++) { if (argv[i][0] != '-' || argv[i][1] == '\0') { filename = argv[i++]; isInteractive = JS_FALSE; break; } switch (argv[i][1]) { case 'v': if (++i == argc) return usage(); JS_SetVersion(cx, (JSVersion) atoi(argv[i])); break; case 'w': reportWarnings = JS_TRUE; break; case 'W': reportWarnings = JS_FALSE; break; case 's': JS_ToggleOptions(cx, JSOPTION_STRICT); break; case 'x': JS_ToggleOptions(cx, JSOPTION_XML); break; case 'P': if (JS_GET_CLASS(cx, JS_GetPrototype(cx, obj)) != &global_class) { JSObject *gobj; if (!JS_SealObject(cx, obj, JS_TRUE)) return JS_FALSE; gobj = JS_NewObject(cx, &global_class, NULL, NULL); if (!gobj) return JS_FALSE; if (!JS_SetPrototype(cx, gobj, obj)) return JS_FALSE; JS_SetParent(cx, gobj, NULL); JS_SetGlobalObject(cx, gobj); obj = gobj; } break; case 'b': gBranchLimit = atoi(argv[++i]); JS_SetBranchCallback(cx, my_BranchCallback); JS_ToggleOptions(cx, JSOPTION_NATIVE_BRANCH_CALLBACK); break; case 'c': /* set stack chunk size */ gStackChunkSize = atoi(argv[++i]); break; case 'f': if (++i == argc) return usage(); Process(cx, obj, argv[i], JS_FALSE); /* * XXX: js -f foo.js should interpret foo.js and then * drop into interactive mode, but that breaks the test * harness. Just execute foo.js for now. */ isInteractive = JS_FALSE; break; case 'e': { jsval rval; if (++i == argc) return usage(); /* Pass a filename of -e to imitate PERL */ JS_EvaluateScript(cx, obj, argv[i], strlen(argv[i]), "-e", 1, &rval); isInteractive = JS_FALSE; break; } case 'C': compileOnly = JS_TRUE; isInteractive = JS_FALSE; break; case 'i': isInteractive = forceTTY = JS_TRUE; break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -