diff --git a/mozilla/modules/xml/js/Makefile b/mozilla/modules/xml/js/Makefile new file mode 100644 index 00000000000..5c38c59df25 --- /dev/null +++ b/mozilla/modules/xml/js/Makefile @@ -0,0 +1,30 @@ +#!gmake +# +# The contents of this file are subject to the Netscape Public License +# Version 1.0 (the "NPL"); you may not use this file except in +# compliance with the NPL. You may obtain a copy of the NPL at +# http://www.mozilla.org/NPL/ +# +# Software distributed under the NPL is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL +# for the specific language governing rights and limitations under the +# NPL. +# +# The Initial Developer of this code under the NPL is Netscape +# Communications Corporation. Portions created by Netscape are +# Copyright (C) 1998 Netscape Communications Corporation. All Rights +# Reserved. + +DEPTH = ../../.. +REQUIRES = xmlparse xmltok expat js +CSRCS = jsxparse.c \ + jsxgraph.c \ + xmljs.c \ + $(NULL) + +EXPORTS = xmljs.h +LIBRARY_NAME = xmljs +MODULE = xmljs + +include $(DEPTH)/config/rules.mk + diff --git a/mozilla/modules/xml/js/README b/mozilla/modules/xml/js/README new file mode 100644 index 00000000000..d1593f98c32 --- /dev/null +++ b/mozilla/modules/xml/js/README @@ -0,0 +1,10 @@ +Some primitive JS/XML glue. Contact shaver@netscape.com. + +XMLParser: a thin wrapper around the parser APIs of James Clark's `expat' + XML parser. + +XMLGraph: JSClass that generates a simple JS object graph from XML. This is + NOT W3C Level 1 DOM compliant by any stretch of even my vivid + imagination. + +These objects may never show up in Mozilla, of course. diff --git a/mozilla/modules/xml/js/jsxgraph.c b/mozilla/modules/xml/js/jsxgraph.c new file mode 100644 index 00000000000..0409bd68781 --- /dev/null +++ b/mozilla/modules/xml/js/jsxgraph.c @@ -0,0 +1,264 @@ +#define XMLJS_INTERNAL +#include "xmljs.h" +#undef XMLJS_INTERNAL + +#if DEBUG_shaver +#include +#endif + +/** + * An XMLGraph JS object produces an object graph in response + * to XML parser events. + */ + +typedef struct { + XMLCallback cb; + JSObject *current; + char *nameBy; +} XMLGraphCallback; + +static char +xmlg_up_str[] = "__up__"; + +static void +StartElementHandler(void *userdata, const char *name, const char **atts) +{ + XMLGraphCallback * cb = (XMLGraphCallback *)userdata; + int i; + JSObject *element, *attobj, *array; + JSContext *cx = cb->cb.cx; + jsval rval; + + if (!cb->current) { + if (!JS_EvaluateScript(cx, JS_GetGlobalObject(cx), xmljs_newObj_str, + xmljs_newObj_size, + "XMLGraph internal", 0, &rval)) { + JS_ReportError(cx, "failed to create new Object"); + return; + } + element = JSVAL_TO_OBJECT(rval); + cb->current = element; + if (!JS_DefineProperty(cx, cb->cb.obj, "graph", rval, + NULL, NULL, JSPROP_ENUMERATE)) { + JS_ReportError(cx, "failed to define graph prop"); + return; + } + } + if (!JS_EvaluateScript(cx, JS_GetGlobalObject(cx), xmljs_newObj_str, + xmljs_newObj_size, + "XMLGraph internal", 0, &rval)) { + JS_ReportError(cx, "failed to create new Object"); + return; + } + element = JSVAL_TO_OBJECT(rval); + + /* populate and set __attributes__ */ + if (atts[0]) { + JSString *attstr; + if (!JS_EvaluateScript(cx, JS_GetGlobalObject(cx), xmljs_newObj_str, + xmljs_newObj_size, + "XMLGraph internal", 0, &rval)) + return; + attobj = JSVAL_TO_OBJECT(rval); + + for (i = 0; atts[i]; i+=2) { + /* + * if we have an attribute that matches nameBy, use + * the value of that attribute to name the node + */ + if (cb->nameBy && !strcmp(cb->nameBy, atts[i])) + name = atts[i+1]; + + attstr = JS_NewStringCopyZ(cx, atts[i+1]); + if (!attstr || + !JS_DefineProperty(cx, attobj, atts[i], + STRING_TO_JSVAL(attstr), + NULL, NULL, JSPROP_ENUMERATE)) + return; + } + if (!JS_DefineProperty(cx, element, "__attributes__", + OBJECT_TO_JSVAL(attobj), NULL, NULL, + 0)) + return; + } + + /* Check for existing property with this name */ + if (!JS_GetProperty(cx, cb->current, name, &rval)) + return; + + if (JSVAL_IS_OBJECT(rval)) { + JSObject *existing = JSVAL_TO_OBJECT(rval); + if (JS_IsArrayObject(cx, existing)) { + /* already an array, so append */ + jsuint length; + if (!JS_GetArrayLength(cx, existing, &length) || + !JS_DefineElement(cx, existing, (jsint)length, + OBJECT_TO_JSVAL(element), NULL, NULL, + JSPROP_ENUMERATE)) + return; + } else { + /* replace with an array [current, new] */ + jsval vector[2]; + vector[0] = OBJECT_TO_JSVAL(existing); + vector[1] = OBJECT_TO_JSVAL(element); + array = JS_NewArrayObject(cx, 2, vector); + if (!array || + !JS_DefineProperty(cx, cb->current, name, + OBJECT_TO_JSVAL(array), NULL, NULL, + JSPROP_ENUMERATE)) + return; + } + } else if (!JS_DefineProperty(cx, cb->current, name, + OBJECT_TO_JSVAL(element), NULL, NULL, + JSPROP_ENUMERATE)) + return; + + /* define backpointer __up__ */ + if (!JS_DefineProperty(cx, element, xmlg_up_str, + OBJECT_TO_JSVAL(cb->current), + NULL, NULL, 0)) + return; + /* + * XXX DefineProperty(element, "__element__", elementstr) + */ + + cb->current = element; +} + +static void +EndElementHandler(void *userdata, const char *name) +{ + /* XXX check to be sure that it's the right one ending! */ + XMLGraphCallback *cb = (XMLGraphCallback *)userdata; + JSContext *cx = cb->cb.cx; + jsval rval; + + if (!JS_GetProperty(cx, cb->current, xmlg_up_str, &rval)) + return; + cb->current = JSVAL_TO_OBJECT(rval); +} + +static void +CharacterDataHandler(void *userdata, const char *s, int len) +{ + XMLGraphCallback *cb = (XMLGraphCallback *)userdata; + JSString *str; + JSContext *cx = cb->cb.cx; + + str = JS_NewStringCopyN(cx, s, len); + if (!str) + return; + JS_DefineProperty(cx, cb->current, "__cdata__", STRING_TO_JSVAL(str), + NULL, NULL, 0); +} + +static void +ProcessingInstructionHandler(void *userdata, const char *target, + const char *data) +{ + /* + * (cb->obj).processingInstruction(element, target, data) + */ +} + +static JSBool +PreParse(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + XMLGraphCallback *cb; + if (!JS_DeleteProperty(cx, obj, "graph")) + return JS_FALSE; + cb = (XMLGraphCallback *)JS_GetPrivate(cx, obj); + if (!cb) + return JS_FALSE; + cb->current = NULL; + return JS_TRUE; +} + +static JSBool +XMLGraph(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + XMLGraphCallback *cb = JS_malloc(cx, sizeof(XMLGraphCallback)); + if (!cb) + return JS_FALSE; + cb->cb.xml = NULL; + cb->cb.obj = obj; + cb->cb.cx = cx; + cb->current = NULL; + cb->nameBy = NULL; + cb->cb.start = StartElementHandler; + cb->cb.end = EndElementHandler; + cb->cb.cdata = CharacterDataHandler; + cb->cb.processing = ProcessingInstructionHandler; + cb->cb.preParse = PreParse; + cb->cb.postParse = NULL; + return JS_SetPrivate(cx, obj, (void *)cb); +} + +static void +xmlgraph_finalize(JSContext *cx, JSObject *obj) +{ + XMLGraphCallback *cb; + cb = (XMLGraphCallback *)JS_GetPrivate(cx, obj); + if (!cb) + return; + if (cb->nameBy) + JS_free(cx, cb->nameBy); + JS_free(cx, cb); +} + +static JSBool +xmlgraph_nameBySetter(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSString *namestr; + XMLGraphCallback *cb = JS_GetPrivate(cx, obj); + if (!cb) + return JS_TRUE; + if (*vp == JSVAL_NULL) { + if (cb->nameBy) + JS_free(cx, cb->nameBy); + cb->nameBy = NULL; + return JS_TRUE; + } + namestr = JS_ValueToString(cx, *vp); + if (!namestr) + return JS_FALSE; + if (cb->nameBy) + JS_free(cx, cb->nameBy); + cb->nameBy = JS_strdup(cx, JS_GetStringBytes(namestr)); + if (!cb->nameBy) + return JS_FALSE; + return JS_TRUE; +} + +JSClass XMLGraph_class = { + "XMLGraph", JSCLASS_HAS_PRIVATE, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, xmlgraph_finalize +}; + +JSFunctionSpec xmlgraph_funcs[] = { + {0} +}; + +JSPropertySpec xmlgraph_props[] = { + {"nameBy", -1, JSPROP_ENUMERATE, NULL, xmlgraph_nameBySetter}, + {0} +}; + +JSBool +XMLGraph_Init(JSContext *cx, JSObject *obj, JSObject *parent_proto) +{ + jsval v = JSVAL_NULL; + JSObject *proto = JS_InitClass(cx, obj, parent_proto, &XMLGraph_class, + XMLGraph, 1, + xmlgraph_props, xmlgraph_funcs, + NULL, NULL); + + if (!proto) + return JS_FALSE; + if (!JS_SetProperty(cx, proto, "nameBy", &v)) { + JS_ReportError(cx, "failed to set XMLGraph.prototype.nameBy = null"); + return JS_FALSE; + } + return JS_TRUE; +} diff --git a/mozilla/modules/xml/js/jsxparse.c b/mozilla/modules/xml/js/jsxparse.c new file mode 100644 index 00000000000..4934676ef73 --- /dev/null +++ b/mozilla/modules/xml/js/jsxparse.c @@ -0,0 +1,218 @@ +#define XMLJS_INTERNAL +#include "xmljs.h" +#undef XMLJS_INTERNAL + +static void +StartElementHandler(void *userdata, const char *name, const char **atts) +{ + XMLCallback *cb = (XMLCallback *)userdata; + int i; + JSString *namestr = NULL, *attstr = NULL; + JSObject *attobj = NULL; + JSContext *cx = cb->cx; + jsval argv[2]; + jsval rval; + JSFunction *fun; + + if (!JS_GetProperty(cx, cb->obj, "startElement", &rval) || + JSVAL_IS_VOID(rval) || JSVAL_IS_NULL(rval)) + return; + + fun = JS_ValueToFunction(cx, rval); + if (!fun) + return; + + /* oh, for local roots */ + JS_AddRoot(cx, &namestr); + JS_AddRoot(cx, &attobj); + JS_AddRoot(cx, &attstr); + + namestr = JS_NewStringCopyZ(cx, name); + + if (atts[0]) { + if (!JS_EvaluateScript(cx, JS_GetGlobalObject(cx), xmljs_newObj_str, + xmljs_newObj_size, + "XMLParser internal", 0, &rval)) { + JS_ReportError(cx, "failed to create attobj"); + goto out; + } + attobj = JSVAL_TO_OBJECT(rval); + + for (i = 0; atts[i]; i+=2) { + attstr = JS_NewStringCopyZ(cx, atts[i+1]); + if (!attstr || + !JS_DefineProperty(cx, attobj, atts[i], + STRING_TO_JSVAL(attstr), + NULL, NULL, JSPROP_ENUMERATE)) { + JS_ReportError(cx, "defining att prop %s failed", atts[i]); + goto out; + } + } + } + + argv[0] = STRING_TO_JSVAL(namestr); + argv[1] = OBJECT_TO_JSVAL(attobj); + JS_CallFunction(cx, cb->obj, fun, 2, argv, &rval); + + out: + JS_RemoveRoot(cx, &namestr); + JS_RemoveRoot(cx, &attstr); + JS_RemoveRoot(cx, &attobj); +} + +static void +EndElementHandler(void *userdata, const char *name) +{ + XMLCallback *cb = (XMLCallback *)userdata; + JSString *namestr = NULL; + JSContext *cx = cb->cx; + jsval argv[1]; + jsval rval; + JSFunction *fun; + + if (!JS_GetProperty(cx, cb->obj, "endElement", &rval) || + JSVAL_IS_VOID(rval) || JSVAL_IS_NULL(rval)) + return; + + fun = JS_ValueToFunction(cx, rval); + if (!fun) + return; + + /* oh, for local roots */ + JS_AddRoot(cx, &namestr); + + namestr = JS_NewStringCopyZ(cx, name); + if (!namestr) + goto out; + argv[0] = STRING_TO_JSVAL(namestr); + JS_CallFunction(cx, cb->obj, fun, 1, argv, &rval); + + out: + JS_RemoveRoot(cx, &namestr); +} + +static void +CharacterDataHandler(void *userdata, const char *s, int len) +{ + XMLCallback *cb = (XMLCallback *)userdata; + JSString *cdatastr = NULL; + JSContext *cx = cb->cx; + jsval argv[1]; + jsval rval; + JSFunction *fun; + + if (!JS_GetProperty(cx, cb->obj, "characterData", &rval) || + JSVAL_IS_VOID(rval) || JSVAL_IS_NULL(rval)) + return; + + fun = JS_ValueToFunction(cx, rval); + if (!fun) + return; + + /* oh, for local roots */ + JS_AddRoot(cx, &cdatastr); + + cdatastr = JS_NewStringCopyZ(cx, s); + if (!cdatastr) + goto out; + argv[0] = STRING_TO_JSVAL(cdatastr); + JS_CallFunction(cx, cb->obj, fun, 1, argv, &rval); + + out: + JS_RemoveRoot(cx, &cdatastr); +} + +static void +ProcessingInstructionHandler(void *userdata, const char *target, + const char *data) +{ + XMLCallback *cb = (XMLCallback *)userdata; + JSString *targetstr = NULL, *datastr = NULL; + JSContext *cx = cb->cx; + jsval argv[2]; + jsval rval; + JSFunction *fun; + + if (!JS_GetProperty(cx, cb->obj, "processingInstruction", &rval) || + JSVAL_IS_VOID(rval) || JSVAL_IS_NULL(rval)) + return; + + fun = JS_ValueToFunction(cx, rval); + if (!fun) + return; + JS_AddRoot(cx, &targetstr); + JS_AddRoot(cx, &datastr); + targetstr = JS_NewStringCopyZ(cx, target); + if (!targetstr) + goto out; + datastr = JS_NewStringCopyZ(cx, data); + if (!datastr) + goto out; + argv[0] = STRING_TO_JSVAL(targetstr); + argv[1] = STRING_TO_JSVAL(datastr); + JS_CallFunction(cx, cb->obj, fun, 2, argv, &rval); + + out: + JS_RemoveRoot(cx, &targetstr); + JS_RemoveRoot(cx, &datastr); +} + +static JSBool +XMLParser(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + XMLCallback *cb = JS_malloc(cx, sizeof(XMLCallback)); + if (!cb) + return JS_FALSE; + cb->start = StartElementHandler; + cb->end = EndElementHandler; + cb->cdata = CharacterDataHandler; + cb->processing = ProcessingInstructionHandler; + cb->xml = NULL; + cb->obj = obj; + cb->preParse = cb->postParse = NULL; + cb->cx = cx; /* XXX obj can persist longer than cx *sigh* */ + return JS_SetPrivate(cx, obj, (void *)cb); +} + +static void +xmlparser_finalize(JSContext *cx, JSObject *obj) +{ + XMLCallback *cb; + + cb = (XMLCallback *)JS_GetPrivate(cx, obj); + if (!cb) + return; + JS_free(cx, cb); +} + +JSClass XMLParser_class = { + "XMLParser", JSCLASS_HAS_PRIVATE, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, xmlparser_finalize +}; + +JSFunctionSpec xmlparser_funcs[] = { + {0} +}; + +JSPropertySpec xmlparser_props[] = { + {0} +}; + +JSBool +XMLParser_Init(JSContext *cx, JSObject *obj, JSObject *parent_proto) +{ + JSObject *proto = JS_InitClass(cx, obj, parent_proto, &XMLParser_class, + XMLParser, 1, + xmlparser_props, xmlparser_funcs, + NULL, NULL); + return proto && + JS_DefineProperty(cx, proto, "startElement", JSVAL_NULL, NULL, NULL, + JSPROP_ENUMERATE) && + JS_DefineProperty(cx, proto, "endElement", JSVAL_NULL, NULL, NULL, + JSPROP_ENUMERATE) && + JS_DefineProperty(cx, proto, "characterData", JSVAL_NULL, NULL, NULL, + JSPROP_ENUMERATE) && + JS_DefineProperty(cx, proto, "processingInstruction", JSVAL_NULL, + NULL, NULL, JSPROP_ENUMERATE); +} diff --git a/mozilla/modules/xml/js/test/Makefile b/mozilla/modules/xml/js/test/Makefile new file mode 100644 index 00000000000..e64eab3fd91 --- /dev/null +++ b/mozilla/modules/xml/js/test/Makefile @@ -0,0 +1,38 @@ +#!gmake +# +# The contents of this file are subject to the Netscape Public License +# Version 1.0 (the "NPL"); you may not use this file except in +# compliance with the NPL. You may obtain a copy of the NPL at +# http://www.mozilla.org/NPL/ +# +# Software distributed under the NPL is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL +# for the specific language governing rights and limitations under the +# NPL. +# +# The Initial Developer of this code under the NPL is Netscape +# Communications Corporation. Portions created by Netscape are +# Copyright (C) 1998 Netscape Communications Corporation. All Rights +# Reserved. + +DEPTH = ../../../.. +REQUIRES = xmlparse xmltok expat js xmljs nspr +CSRCS = js.c \ + $(NULL) + +PROGRAM = xmljs + +include $(DEPTH)/config/rules.mk +CFLAGS += -DJSFILE -I. + +MYLIBS = -lxmljs -lexpat -lxmltok -ljs $(LIBNSPR) -ldl -lm + +js: + rm -f $(PROGRAM) + @make $(PROGRAM) + +$(PROGRAM): $(OBJS) + @$(MAKE_OBJDIR) + $(CC) -o $@ $(LDFLAGS) $(OBJS) -L$(DIST)/bin $(MYLIBS) + + diff --git a/mozilla/modules/xml/js/test/js.c b/mozilla/modules/xml/js/test/js.c new file mode 100644 index 00000000000..1f4fcba2ac9 --- /dev/null +++ b/mozilla/modules/xml/js/test/js.c @@ -0,0 +1,1265 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + +/* + * JS shell. + */ +#include "jsstddef.h" +#include +#include +#include +#include +#include "prtypes.h" +#ifndef NSPR20 +#include "prarena.h" +#else +#include "plarena.h" +#endif +#include "prlog.h" +#include "prprf.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 "jsscan.h" +#include "jsscope.h" +#include "jsscript.h" + +#ifdef LIVECONNECT +#include "jsjava.h" +#endif + +#ifdef JSDEBUGGER +#include "jsdebug.h" +#ifdef JSDEBUGGER_JAVA_UI +#include "jsdjava.h" +#endif /* JSDEBUGGER_JAVA_UI */ +#endif /* JSDEBUGGER */ + +#ifdef XP_UNIX +#include +#include +#include +#include +#endif + +#ifdef XP_MAC +#define isatty(f) 1 +#endif + +#include "xmljs.h" + +#ifndef JSFILE +# error "JSFILE must be defined for this module to work." +#endif + +#ifdef JSDEBUGGER +static JSDContext *_jsdc; +#ifdef JSDEBUGGER_JAVA_UI +static JSDJContext *_jsdjc; +#endif /* JSDEBUGGER_JAVA_UI */ +#endif /* JSDEBUGGER */ + +static void +Process(JSContext *cx, JSObject *obj, char *filename) +{ + JSTokenStream *ts; + JSCodeGenerator cg; + JSBool ok; + JSScript *script; + jsval result; + JSString *str; + + ts = js_NewFileTokenStream(cx, filename, stdin); + if (!ts) + goto out; +#ifdef JSDEBUGGER + if (!filename) + ts->filename = "typein"; +#endif + if (isatty(fileno(ts->file))) { + ts->flags |= TSF_INTERACTIVE; + } else { + /* 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. + */ + char ch = fgetc(ts->file); + if (ch == '#') { + while((ch = fgetc(ts->file)) != EOF) { + if(ch == '\n' || ch == '\r') + break; + } + } + ungetc(ch, ts->file); + } + + do { + js_InitCodeGenerator(cx, &cg, ts->filename, ts->lineno, ts->principals); + do { + if (ts->flags & TSF_INTERACTIVE) + printf("js> "); + ok = js_CompileTokenStream(cx, obj, ts, &cg); + if (ts->flags & TSF_ERROR) { + ts->flags &= ~TSF_ERROR; + CLEAR_PUSHBACK(ts); + ok = JS_TRUE; + } + } while (ok && !(ts->flags & TSF_EOF) && CG_OFFSET(&cg) == 0); + if (ok) { + script = js_NewScriptFromCG(cx, &cg, NULL); + if (script) { + if (JS_ExecuteScript(cx, obj, script, &result) && + (ts->flags & TSF_INTERACTIVE) && + result != JSVAL_VOID) { + str = JS_ValueToString(cx, result); + if (str) + printf("%s\n", JS_GetStringBytes(str)); + } + JS_DestroyScript(cx, script); + } + } + cg.firstLine = ts->lineno; + js_ResetCodeGenerator(cx, &cg); + RESET_TOKENBUF(ts); + } while (!(ts->flags & TSF_EOF)); + +out: + if (ts) + (void) js_CloseTokenStream(cx, ts); + PR_FreeArenaPool(&cx->codePool); + PR_FreeArenaPool(&cx->tempPool); +} + +static JSBool +Version(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + if (argc > 0 && JSVAL_IS_INT(argv[0])) + *rval = INT_TO_JSVAL(JS_SetVersion(cx, JSVAL_TO_INT(argv[0]))); + else + *rval = INT_TO_JSVAL(JS_GetVersion(cx)); + return JS_TRUE; +} + +static JSBool +Load(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + uintN i; + JSString *str; + const char *filename; + JSScript *script; + JSBool ok; + jsval result; + + for (i = 0; i < argc; i++) { + str = JS_ValueToString(cx, argv[i]); + if (!str) + return JS_FALSE; + argv[i] = STRING_TO_JSVAL(str); + filename = JS_GetStringBytes(str); + errno = 0; + script = JS_CompileFile(cx, obj, filename); + if (!script) { + fprintf(stderr, "js: cannot load %s", filename); + if (errno) + fprintf(stderr, ": %s", strerror(errno)); + putc('\n', stderr); + continue; + } + ok = JS_ExecuteScript(cx, obj, script, &result); + JS_DestroyScript(cx, script); + if (!ok) + return JS_FALSE; + } + return JS_TRUE; +} + +static JSBool +Print(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + uintN i, n; + JSString *str; + + for (i = n = 0; i < argc; i++) { + str = JS_ValueToString(cx, argv[i]); + if (!str) + return JS_FALSE; + printf("%s%s", i ? " " : "", JS_GetStringBytes(str)); + n++; + } + if (n) + putchar('\n'); + return JS_TRUE; +} + +static JSBool +Help(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); + +static JSBool +Quit(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ +#ifdef LIVECONNECT + JSJ_SimpleShutdown(); +#endif + exit(0); + return JS_FALSE; +} + +#ifdef GC_MARK_DEBUG +extern JS_FRIEND_DATA(FILE *) js_DumpGCHeap; +#endif + +static JSBool +GC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSRuntime *rt; + uint32 preBytes; + + rt = cx->runtime; + preBytes = rt->gcBytes; +#ifdef GC_MARK_DEBUG + if (argc && JSVAL_IS_STRING(argv[0])) { + char *name = JS_GetStringBytes(JSVAL_TO_STRING(argv[0])); + FILE *file = fopen(name, "w"); + if (!file) { + fprintf(stderr, "gc: can't open %s: %s\n", strerror(errno)); + return JS_FALSE; + } + js_DumpGCHeap = file; + } else { + js_DumpGCHeap = stdout; + } +#endif + js_ForceGC(cx); +#ifdef GC_MARK_DEBUG + if (js_DumpGCHeap != stdout) + fclose(js_DumpGCHeap); + js_DumpGCHeap = NULL; +#endif + printf("before %lu, after %lu, break %08lx\n", + (unsigned long)preBytes, (unsigned long)rt->gcBytes, +#ifdef XP_UNIX + (unsigned long)sbrk(0) +#else + 0 +#endif + ); +#ifdef JS_GCMETER + js_DumpGCStats(rt, stdout); +#endif + return JS_TRUE; +} + +static JSBool +GetTrapArgs(JSContext *cx, uintN argc, jsval *argv, JSScript **scriptp, + int32 *ip) +{ + uintN intarg; + JSFunction *fun; + + *scriptp = cx->fp->down->script; + *ip = 0; + if (argc != 0) { + intarg = 0; + if (JS_TypeOfValue(cx, argv[0]) == JSTYPE_FUNCTION) { + fun = JS_ValueToFunction(cx, argv[0]); + if (!fun) + return JS_FALSE; + *scriptp = fun->script; + intarg++; + } + if (argc > intarg) { + if (!JS_ValueToInt32(cx, argv[intarg], ip)) + return JS_FALSE; + } + } + return JS_TRUE; +} + +static JSTrapStatus +TrapHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval, + void *closure) +{ + JSString *str; + JSStackFrame *caller; + + str = closure; + caller = cx->fp->down; + if (!JS_EvaluateScript(cx, caller->scopeChain, + JS_GetStringBytes(str), JS_GetStringLength(str), + caller->script->filename, caller->script->lineno, + rval)) { + return JSTRAP_ERROR; + } + if (*rval != JSVAL_VOID) + return JSTRAP_RETURN; + return JSTRAP_CONTINUE; +} + +static JSBool +Trap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSString *str; + JSScript *script; + int32 i; + + if (argc == 0) { + JS_ReportError(cx, "usage: trap [fun] [pc] expr"); + return JS_FALSE; + } + argc--; + str = JS_ValueToString(cx, argv[argc]); + if (!str) + return JS_FALSE; + argv[argc] = STRING_TO_JSVAL(str); + if (!GetTrapArgs(cx, argc, argv, &script, &i)) + return JS_FALSE; + return JS_SetTrap(cx, script, script->code + i, TrapHandler, str); +} + +static JSBool +Untrap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSScript *script; + int32 i; + + if (!GetTrapArgs(cx, argc, argv, &script, &i)) + return JS_FALSE; + JS_ClearTrap(cx, script, script->code + i, NULL, NULL); + return JS_TRUE; +} + +static JSBool +LineToPC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSScript *script; + int32 i; + uintN lineno; + jsbytecode *pc; + + if (argc == 0) { + JS_ReportError(cx, "usage: line2pc [fun] line"); + return JS_FALSE; + } + script = cx->fp->down->script; + if (!GetTrapArgs(cx, argc, argv, &script, &i)) + return JS_FALSE; + lineno = (i == 0) ? script->lineno : (uintN)i; + pc = JS_LineNumberToPC(cx, script, lineno); + if (!pc) + return JS_FALSE; + *rval = INT_TO_JSVAL(pc - script->code); + return JS_TRUE; +} + +static JSBool +PCToLine(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSScript *script; + int32 i; + uintN lineno; + + if (!GetTrapArgs(cx, argc, argv, &script, &i)) + return JS_FALSE; + lineno = JS_PCToLineNumber(cx, script, script->code + i); + if (!lineno) + return JS_FALSE; + *rval = INT_TO_JSVAL(lineno); + return JS_TRUE; +} + +#ifdef DEBUG + +static void +SingleNote(JSContext *cx, JSFunction *fun ) +{ + uintN offset, delta; + jssrcnote *notes, *sn; + JSSrcNoteType type; + jsatomid atomIndex; + JSAtom *atom; + + notes = fun->script->notes; + if (notes) { + printf("\nSource notes:\n"); + offset = 0; + for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { + delta = SN_DELTA(sn); + offset += delta; + printf("%3u: %5u [%4u] %-8s", + sn - notes, offset, delta, js_SrcNoteName[SN_TYPE(sn)]); + type = SN_TYPE(sn); + switch (type) { + case SRC_SETLINE: + printf(" lineno %u", (uintN) js_GetSrcNoteOffset(sn, 0)); + break; + case SRC_FOR: + printf(" cond %u update %u tail %u", + (uintN) js_GetSrcNoteOffset(sn, 0), + (uintN) js_GetSrcNoteOffset(sn, 1), + (uintN) js_GetSrcNoteOffset(sn, 2)); + break; + case SRC_COMMA: + case SRC_PCBASE: + printf(" offset %u", (uintN) js_GetSrcNoteOffset(sn, 0)); + break; + case SRC_LABEL: + case SRC_LABELBRACE: + case SRC_BREAK2LABEL: + case SRC_CONT2LABEL: + case SRC_FUNCDEF: + atomIndex = (jsatomid) js_GetSrcNoteOffset(sn, 0); + atom = js_GetAtom(cx, &fun->script->atomMap, atomIndex); + printf(" atom %u (%s)", (uintN)atomIndex, ATOM_BYTES(atom)); + break; + default:; + } + putchar('\n'); + } + } +} + +static JSBool +Notes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + uintN i; + JSFunction *fun; + + for (i = 0; i < argc; i++) { + fun = JS_ValueToFunction(cx, argv[i]); + if (!fun) + return JS_FALSE; + + SingleNote(cx, fun); + } + return JS_TRUE; +} + +static JSBool +ExceptionTable(JSContext *cx, JSFunction *fun) +{ + JSTryNote *iter = fun->script->trynotes; + + if (!iter) + return JS_TRUE; + printf("\nException table:\nstart\tend\tcatch\tfinally\n"); + while (iter->start && iter->end) { + printf(" %d\t%d\t%d\t%d\n", + iter->start, iter->end, iter->catch, iter->finally); + iter++; + } + return JS_TRUE; +} + +static JSBool +Disassemble(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSBool lines; + uintN i; + JSFunction *fun; + + if (argc > 0 && + JSVAL_IS_STRING(argv[0]) && + !strcmp(JS_GetStringBytes(JSVAL_TO_STRING(argv[0])), "-l")) { + lines = JS_TRUE; + argv++, argc--; + } else { + lines = JS_FALSE; + } + for (i = 0; i < argc; i++) { + fun = JS_ValueToFunction(cx, argv[i]); + if (!fun) + return JS_FALSE; + + js_Disassemble(cx, fun->script, lines, stdout); + SingleNote(cx, fun); + ExceptionTable(cx, fun); + } + return JS_TRUE; +} + +static JSBool +DisassWithSrc(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ +#define LINE_BUF_LEN 512 + uintN i, len, line1, line2, bupline; + JSFunction *fun; + FILE *file; + char linebuf[LINE_BUF_LEN]; + jsbytecode *pc, *end; + static char sep[] = ";-------------------------"; + + for (i = 0; i < argc; i++) { + fun = JS_ValueToFunction(cx, argv[i]); + if (!fun) + return JS_FALSE; + + if (!fun->script || !fun->script->filename) { + JS_ReportError(cx, "only works on JS scripts read from files"); + return JS_FALSE; + } + + file = fopen(fun->script->filename, "r"); + if (!file) { + JS_ReportError(cx, "can't open %s: %s", fun->script->filename, strerror(errno)); + return JS_FALSE; + } + + pc = fun->script->code; + end = pc + fun->script->length; + + /* burn the leading lines */ + line2 = JS_PCToLineNumber(cx, fun->script, pc); + for (line1 = 0; line1 < line2 - 1; line1++) + fgets(linebuf, LINE_BUF_LEN, file); + + bupline = 0; + while (pc < end) { + line2 = JS_PCToLineNumber(cx, fun->script, pc); + + if (line2 < line1) { + if (bupline != line2) { + bupline = line2; + printf("%s %3u: BACKUP\n", sep, line2); + } + } else { + if (bupline && line1 == line2) + printf("%s %3u: RESTORE\n", sep, line2); + bupline = 0; + while (line1 < line2) { + if (!fgets(linebuf, LINE_BUF_LEN, file)) { + JS_ReportError(cx, "unexpected EOF in %s", + fun->script->filename); + goto bail; + } + line1++; + printf("%s %3u: %s", sep, line1, linebuf); + } + } + + len = js_Disassemble1(cx, fun->script, pc, pc - fun->script->code, + JS_TRUE, stdout); + if (!len) + return JS_FALSE; + pc += len; + } + + bail: + fclose(file); + } + return JS_TRUE; +} + +static JSBool +Tracing(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSBool bval; + JSString *str; + + if (argc == 0) { + *rval = BOOLEAN_TO_JSVAL(cx->tracefp != 0); + return JS_TRUE; + } + + switch (JS_TypeOfValue(cx, argv[0])) { + case JSTYPE_NUMBER: + bval = JSVAL_IS_INT(argv[0]) + ? JSVAL_TO_INT(argv[0]) + : (jsint) *JSVAL_TO_DOUBLE(argv[0]); + break; + case JSTYPE_BOOLEAN: + bval = JSVAL_TO_BOOLEAN(argv[0]); + break; + default: + str = JS_ValueToString(cx, argv[0]); + if (!str) + return JS_FALSE; + fprintf(stderr, "tracing: illegal argument %s\n", + JS_GetStringBytes(str)); + return JS_TRUE; + } + cx->tracefp = bval ? stdout : NULL; + return JS_TRUE; +} + +int +DumpAtom(PRHashEntry *he, int i, void *arg) +{ + FILE *fp = arg; + JSAtom *atom = (JSAtom *)he; + + fprintf(fp, "%3d %08x %5lu ", + i, (uintN)he->keyHash, (unsigned long)atom->number); + if (ATOM_IS_STRING(atom)) + fprintf(fp, "\"%s\"\n", ATOM_BYTES(atom)); + else if (ATOM_IS_INT(atom)) + fprintf(fp, "%ld\n", (long)ATOM_TO_INT(atom)); + else + fprintf(fp, "%.16g\n", *ATOM_TO_DOUBLE(atom)); + return HT_ENUMERATE_NEXT; +} + +int +DumpSymbol(PRHashEntry *he, int i, void *arg) +{ + FILE *fp = arg; + JSSymbol *sym = (JSSymbol *)he; + + fprintf(fp, "%3d %08x", i, (uintN)he->keyHash); + if (JSVAL_IS_INT(sym_id(sym))) + fprintf(fp, " [%ld]\n", (long)JSVAL_TO_INT(sym_id(sym))); + else + fprintf(fp, " \"%s\"\n", ATOM_BYTES(sym_atom(sym))); + return HT_ENUMERATE_NEXT; +} + +extern JS_FRIEND_DATA(JSScopeOps) js_list_scope_ops; + +void +DumpScope(JSContext *cx, JSObject *obj, PRHashEnumerator dump, FILE *fp) +{ + JSScope *scope; + JSSymbol *sym; + int i; + + fprintf(fp, "\n%s scope contents:\n", OBJ_GET_CLASS(cx, obj)->name); + scope = (JSScope *)obj->map; + if (!MAP_IS_NATIVE(&scope->map)) + return; + if (scope->ops == &js_list_scope_ops) { + for (sym = (JSSymbol *)scope->data, i = 0; sym; + sym = (JSSymbol *)sym->entry.next, i++) { + DumpSymbol(&sym->entry, i, fp); + } + } else { + PR_HashTableDump(scope->data, dump, fp); + } +} + +/* These are callable from gdb. */ +void Dsym(JSSymbol *sym) { if (sym) DumpSymbol(&sym->entry, 0, stderr); } +void Datom(JSAtom *atom) { if (atom) DumpAtom(&atom->entry, 0, stderr); } + +static JSBool +DumpStats(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + uintN i; + JSString *str; + const char *bytes; + JSAtom *atom; + JSObject *obj2; + JSProperty *prop; + jsval value; + + for (i = 0; i < argc; i++) { + str = JS_ValueToString(cx, argv[i]); + if (!str) + return JS_FALSE; + bytes = JS_GetStringBytes(str); + if (strcmp(bytes, "arena") == 0) { +#ifdef ARENAMETER + PR_DumpArenaStats(stdout); +#endif + } else if (strcmp(bytes, "atom") == 0) { + printf("\natom table contents:\n"); + PR_HashTableDump(cx->runtime->atomState.table, DumpAtom, stdout); + } else if (strcmp(bytes, "global") == 0) { + DumpScope(cx, cx->globalObject, DumpSymbol, stdout); + } else { + atom = js_Atomize(cx, bytes, JS_GetStringLength(str), 0); + if (!atom) + return JS_FALSE; + if (!js_FindProperty(cx, (jsid)atom, &obj, &obj2, &prop)) + return JS_FALSE; + if (prop) { + OBJ_DROP_PROPERTY(cx, obj2, prop); + if (!OBJ_GET_PROPERTY(cx, obj, (jsid)atom, &value)) + return JS_FALSE; + } + if (!prop || !JSVAL_IS_OBJECT(value)) { + fprintf(stderr, "js: invalid stats argument %s\n", + bytes); + continue; + } + obj = JSVAL_TO_OBJECT(value); + if (obj) + DumpScope(cx, obj, DumpSymbol, stdout); + } + } + return JS_TRUE; +} + +#endif /* DEBUG */ + +#ifdef TEST_EXPORT +static JSBool +DoExport(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSAtom *atom; + JSObject *obj2; + JSProperty *prop; + JSBool ok; + uintN attrs; + + if (argc != 2) { + JS_ReportError(cx, "usage: doexp obj id"); + return JS_FALSE; + } + if (!JS_ValueToObject(cx, argv[0], &obj)) + return JS_FALSE; + argv[0] = OBJECT_TO_JSVAL(obj); + atom = js_ValueToStringAtom(cx, argv[1]); + if (!atom) + return JS_FALSE; + if (!OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &obj2, &prop)) + return JS_FALSE; + if (!prop) { + ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, NULL, NULL, + JSPROP_EXPORTED, NULL); + } else { + ok = OBJ_GET_ATTRIBUTES(cx, obj, (jsid)atom, prop, &attrs); + if (ok) { + attrs |= JSPROP_EXPORTED; + ok = OBJ_SET_ATTRIBUTES(cx, obj, (jsid)atom, prop, &attrs); + } + OBJ_DROP_PROPERTY(cx, obj2, prop); + } + return ok; +} +#endif + +#ifdef TEST_CVTARGS +static JSBool +ConvertArgs(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSBool b; + jschar c; + int32 i, j; + uint32 u; + jsdouble d, I; + char *s; + JSString *str; + JSObject *obj; + JSFunction *fun; + jsval v; + + if (!JS_ConvertArguments(cx, argc, argv, "b/ciujdIsSofv*", + &b, &c, &i, &u, &j, &d, &I, &s, &str, &obj, &fun, + &v)) { + return JS_FALSE; + } + printf("b %u, c %x (%c), i %ld, u %lu, j %ld\n", b, c, (char)c, i, u, j); + printf("d %g, I %g, s %s, S %s, obj %s, fun %s, v %s\n", + d, I, s, JS_GetStringBytes(str), + JS_GetStringBytes(JS_ValueToString(cx, OBJECT_TO_JSVAL(obj))), + JS_GetStringBytes(JS_DecompileFunction(cx, fun, 4)), + JS_GetStringBytes(JS_ValueToString(cx, v))); + return JS_TRUE; +} +#endif + +static JSFunctionSpec shell_functions[] = { + {"version", Version, 0}, + {"load", Load, 1}, + {"print", Print, 0}, + {"help", Help, 0}, + {"quit", Quit, 0}, + {"gc", GC, 0}, + {"trap", Trap, 3}, + {"untrap", Untrap, 2}, + {"line2pc", LineToPC, 0}, + {"pc2line", PCToLine, 0}, +#ifdef DEBUG + {"dis", Disassemble, 1}, + {"dissrc", DisassWithSrc, 1}, + {"notes", Notes, 1}, + {"tracing", Tracing, 0}, + {"stats", DumpStats, 1}, +#endif +#ifdef TEST_EXPORT + {"doexp", DoExport, 2}, +#endif +#ifdef TEST_CVTARGS + {"cvtargs", ConvertArgs, 0, 0, 12}, +#endif + {0} +}; + +/* NOTE: These must be kept in sync with the above. */ + +static char *shell_help_messages[] = { + "version [number] Get or set JavaScript version number", + "load ['foo.js' ...] Load files named by string arguments", + "print [expr ...] Evaluate and print expressions", + "help [name ...] Display usage and help messages", + "quit Quit mocha", + "gc Run the garbage collector", + "trap [fun] [pc] expr Trap bytecode execution", + "untrap [fun] [pc] Remove a trap", + "line2pc [fun] line Map line number to PC", + "pc2line [fun] [pc] Map PC to line number", +#ifdef DEBUG + "dis [fun] Disassemble functions into bytecodes", + "dissrc [fun] Disassemble functions with source lines", + "notes [fun] Show source notes for functions", + "tracing [toggle] Turn tracing on or off", + "stats [string ...] Dump 'arena', 'atom', 'global' stats", +#endif +#ifdef TEST_EXPORT + "doexp obj id Export identified property from object", +#endif +#ifdef TEST_CVTARGS + "cvtargs b c ... Test JS_ConvertArguments", +#endif + 0 +}; + +static void +ShowHelpHeader(void) +{ + printf("%-9s %-22s %s\n", "Command", "Usage", "Description"); + printf("%-9s %-22s %s\n", "=======", "=====", "==========="); +} + +static void +ShowHelpForCommand(uintN n) +{ + printf("%-9.9s %s\n", shell_functions[n].name, shell_help_messages[n]); +} + +static JSBool +Help(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + uintN i, j; + int did_header, did_something; + JSType type; + JSFunction *fun; + JSString *str; + const char *bytes; + + if (argc == 0) { + ShowHelpHeader(); + for (i = 0; shell_functions[i].name; i++) + ShowHelpForCommand(i); + } else { + did_header = 0; + for (i = 0; i < argc; i++) { + did_something = 0; + type = JS_TypeOfValue(cx, argv[i]); + if (type == JSTYPE_FUNCTION) { + fun = JS_ValueToFunction(cx, argv[i]); + str = fun->atom ? ATOM_TO_STRING(fun->atom) : NULL; + } else if (type == JSTYPE_STRING) { + str = JSVAL_TO_STRING(argv[i]); + } else { + str = NULL; + } + if (str) { + bytes = JS_GetStringBytes(str); + for (j = 0; shell_functions[j].name; j++) { + if (!strcmp(bytes, shell_functions[j].name)) { + if (!did_header) { + did_header = 1; + ShowHelpHeader(); + } + did_something = 1; + ShowHelpForCommand(j); + break; + } + } + } + if (!did_something) { + str = JS_ValueToString(cx, argv[i]); + if (!str) + return JS_FALSE; + fprintf(stderr, "Sorry, no help for %s\n", + JS_GetStringBytes(str)); + } + } + } + return JS_TRUE; +} + +/* + * Define a JS object called "it". Give it class operations that printf why + * they're being called for tutorial purposes. + */ +enum its_tinyid { + ITS_COLOR, ITS_HEIGHT, ITS_WIDTH, ITS_FUNNY, ITS_ARRAY, ITS_RDONLY +}; + +static JSPropertySpec its_props[] = { + {"color", ITS_COLOR, JSPROP_ENUMERATE}, + {"height", ITS_HEIGHT, JSPROP_ENUMERATE}, + {"width", ITS_WIDTH, JSPROP_ENUMERATE}, + {"funny", ITS_FUNNY, JSPROP_ENUMERATE}, + {"array", ITS_ARRAY, JSPROP_ENUMERATE}, + {"rdonly", ITS_RDONLY, JSPROP_READONLY}, + {0} +}; + +static JSBool its_noisy; /* whether to be noisy when finalizing it */ + +static JSBool +its_addProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + if (its_noisy) { + printf("adding its property %s,", + JS_GetStringBytes(JS_ValueToString(cx, id))); + printf(" initial value %s\n", + JS_GetStringBytes(JS_ValueToString(cx, *vp))); + } + return JS_TRUE; +} + +static JSBool +its_delProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + if (its_noisy) { + printf("deleting its property %s,", + JS_GetStringBytes(JS_ValueToString(cx, id))); + printf(" current value %s\n", + JS_GetStringBytes(JS_ValueToString(cx, *vp))); + } + return JS_TRUE; +} + +static JSBool +its_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + if (its_noisy) { + printf("getting its property %s,", + JS_GetStringBytes(JS_ValueToString(cx, id))); + printf(" current value %s\n", + JS_GetStringBytes(JS_ValueToString(cx, *vp))); + } + return JS_TRUE; +} + +static JSBool +its_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + if (its_noisy) { + printf("setting its property %s,", + JS_GetStringBytes(JS_ValueToString(cx, id))); + printf(" new value %s\n", + JS_GetStringBytes(JS_ValueToString(cx, *vp))); + } + if (JSVAL_IS_STRING(id) && + !strcmp(JS_GetStringBytes(JSVAL_TO_STRING(id)), "noisy")) { + return JS_ValueToBoolean(cx, *vp, &its_noisy); + } + return JS_TRUE; +} + +static JSBool +its_enumerate(JSContext *cx, JSObject *obj) +{ + if (its_noisy) + printf("enumerate its properties\n"); + return JS_TRUE; +} + +static JSBool +its_resolve(JSContext *cx, JSObject *obj, jsval id) +{ + if (its_noisy) { + printf("resolving its property %s\n", + JS_GetStringBytes(JS_ValueToString(cx, id))); + } + return JS_TRUE; +} + +static JSBool +its_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp) +{ + if (its_noisy) + printf("converting it to %s type\n", JS_GetTypeName(cx, type)); + return JS_TRUE; +} + +static void +its_finalize(JSContext *cx, JSObject *obj) +{ + if (its_noisy) + printf("finalizing it\n"); +} + +static JSClass its_class = { + "It", 0, + its_addProperty, its_delProperty, its_getProperty, its_setProperty, + its_enumerate, its_resolve, its_convert, its_finalize +}; + +static void +my_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report) +{ + int i, j, k, n; + + fputs("js: ", stderr); + if (!report) { + fprintf(stderr, "%s\n", message); + return; + } + + if (report->filename) + fprintf(stderr, "%s, ", report->filename); + if (report->lineno) + fprintf(stderr, "line %u: ", report->lineno); + fputs(message, stderr); + if (!report->linebuf) { + putc('\n', stderr); + return; + } + + fprintf(stderr, ":\n%s\n", report->linebuf); + n = report->tokenptr - report->linebuf; + for (i = j = 0; i < n; i++) { + if (report->linebuf[i] == '\t') { + for (k = (j + 8) & ~7; j < k; j++) + putc('.', stderr); + continue; + } + putc('.', stderr); + j++; + } + fputs("^\n", stderr); +} + +#if defined DEBUG && defined XP_UNIX +static JSBool +Exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSFunction *fun; + const char *name, **nargv; + uintN i, nargc; + JSString *str; + pid_t pid; + int status; + + fun = JS_ValueToFunction(cx, argv[-2]); + if (!fun) + return JS_FALSE; + if (!fun->atom) + return JS_TRUE; + name = ATOM_BYTES(fun->atom); + nargc = 1 + argc; + nargv = JS_malloc(cx, (nargc + 1) * sizeof(char *)); + if (!nargv) + return JS_FALSE; + nargv[0] = name; + for (i = 1; i < nargc; i++) { + str = JS_ValueToString(cx, argv[i-1]); + if (!str) { + JS_free(cx, nargv); + return JS_FALSE; + } + nargv[i] = JS_GetStringBytes(str); + } + nargv[nargc] = 0; + pid = fork(); + switch (pid) { + case -1: + perror("js"); + break; + case 0: + (void) execvp(name, (char **)nargv); + perror("js"); + exit(127); + default: + while (waitpid(pid, &status, 0) < 0 && errno == EINTR) + ; + break; + } + JS_free(cx, nargv); + return JS_TRUE; +} +#endif + +static JSBool +global_resolve(JSContext *cx, JSObject *obj, jsval id) +{ +#if defined DEBUG && defined XP_UNIX + /* + * Do this expensive hack only for unoptimized Unix builds, which are not + * used for benchmarking. + */ + char *path, *comp, *full; + const char *name; + JSBool ok, found; + JSFunction *fun; + + if (!JSVAL_IS_STRING(id)) + return JS_TRUE; + path = getenv("PATH"); + if (!path) + return JS_TRUE; + path = JS_strdup(cx, path); + if (!path) + return JS_FALSE; + name = JS_GetStringBytes(JSVAL_TO_STRING(id)); + ok = JS_TRUE; + for (comp = strtok(path, ":"); comp; comp = strtok(NULL, ":")) { + if (*comp != '\0') { + full = PR_smprintf("%s/%s", comp, name); + if (!full) { + JS_ReportOutOfMemory(cx); + ok = JS_FALSE; + break; + } + } else { + full = (char *)name; + } + found = (access(full, X_OK) == 0); + if (*comp != '\0') + free(full); + if (found) { + fun = JS_DefineFunction(cx, obj, name, Exec, 0, JSPROP_ENUMERATE); + ok = (fun != NULL); + break; + } + } + JS_free(cx, path); + return ok; +#else + return JS_TRUE; +#endif +} + +static JSClass global_class = { + "global", 0, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, global_resolve, JS_ConvertStub, JS_FinalizeStub +}; + +int +main(int argc, char **argv) +{ + int c, i; + JSVersion version; + JSRuntime *rt; + JSContext *cx; + JSObject *glob, *it; + +#ifdef XP_OS2 + /* these streams are normally line buffered on OS/2 and need a \n, * + * so we need to unbuffer then to get a reasonable prompt */ + setbuf(stdout,0); + setbuf(stderr,0); +#endif + + version = JSVERSION_DEFAULT; +#ifdef XP_UNIX + while ((c = getopt(argc, argv, "v:")) != -1) { + switch (c) { + case 'v': + version = atoi(optarg); + break; + default: + fprintf(stderr, "usage: js [-v version]\n"); + return 2; + } + } + argc -= optind; + argv += optind; +#else + c = -1; + argc--; + argv++; +#endif + + rt = JS_NewRuntime(8L * 1024L * 1024L); + if (!rt) + return 1; + cx = JS_NewContext(rt, 8192); + if (!cx) + return 1; + JS_SetErrorReporter(cx, my_ErrorReporter); + + glob = JS_NewObject(cx, &global_class, NULL, NULL); + if (!glob) + return 1; + if (!JS_InitStandardClasses(cx, glob)) + return 1; + if (!XML_Init(cx, glob)) + return 1; + if (!JS_DefineFunctions(cx, glob, shell_functions)) + return 1; + + /* Set version only after there is a global object. */ + if (version != JSVERSION_DEFAULT) + JS_SetVersion(cx, version); + + it = JS_DefineObject(cx, glob, "it", &its_class, NULL, 0); + if (!it) + return 1; + if (!JS_DefineProperties(cx, it, its_props)) + return 1; + +#ifdef LIVECONNECT + if (!JSJ_SimpleInit(cx, glob, NULL, getenv("CLASSPATH"))) + return 1; +#endif + +#ifdef JSDEBUGGER + /* + * XXX A command line option to enable debugging (or not) would be good + */ + _jsdc = JSD_DebuggerOnForUser(rt, NULL, NULL); + if (!_jsdc) + return 1; + JSD_JSContextInUse(_jsdc, cx); + +#ifdef JSDEBUGGER_JAVA_UI + _jsdjc = JSDJ_CreateContext(); + if (! _jsdjc) + return 1; + JSDJ_SetJSDContext(_jsdjc, _jsdc); + JSDJ_CreateJavaVMAndStartDebugger(_jsdjc); + /* + * XXX This would be the place to wait for the debugger to start. + * Waiting would be nice in general, but especially when a js file + * is passed on the cmd line. + */ +#endif /* JSDEBUGGER_JAVA_UI */ +#endif /* JSDEBUGGER */ + + if (argc > 0) { + for (i = 0; i < argc; i++) + Process(cx, glob, argv[i]); + } else { + Process(cx, glob, NULL); + } + +#ifdef JSDEBUGGER + if (_jsdc) + JSD_DebuggerOff(_jsdc); +#endif /* JSDEBUGGER */ + + JS_DestroyContext(cx); + JS_DestroyRuntime(rt); + JS_ShutDown(); + return 0; +} diff --git a/mozilla/modules/xml/js/test/jsstddef.h b/mozilla/modules/xml/js/test/jsstddef.h new file mode 100644 index 00000000000..698ba9353b9 --- /dev/null +++ b/mozilla/modules/xml/js/test/jsstddef.h @@ -0,0 +1,41 @@ +/*stddef inclusion here to first declare ptrdif as a signed long instead of a signed int*/ + +#ifdef _WINDOWS +# ifndef XP_WIN +# define XP_WIN +# endif +#if defined(_WIN32) || defined(WIN32) +# ifndef XP_WIN32 +# define XP_WIN32 +# endif +#else +# ifndef XP_WIN16 +# define XP_WIN16 +# endif +#endif +#endif + +#ifdef XP_WIN16 +#ifndef _PTRDIFF_T_DEFINED +typedef long ptrdiff_t; + +/* + * The Win16 compiler treats pointer differences as 16-bit signed values. + * This macro allows us to treat them as 17-bit signed values, stored in + * a 32-bit type. + */ +#define PTRDIFF(p1, p2, type) \ + ((((unsigned long)(p1)) - ((unsigned long)(p2))) / sizeof(type)) + +#define _PTRDIFF_T_DEFINED +#endif /*_PTRDIFF_T_DEFINED*/ +#else /*WIN16*/ + +#define PTRDIFF(p1, p2, type) \ + ((p1) - (p2)) + +#endif + +#include + + diff --git a/mozilla/modules/xml/js/test/moz.rdf b/mozilla/modules/xml/js/test/moz.rdf new file mode 100644 index 00000000000..6ebc2000b83 --- /dev/null +++ b/mozilla/modules/xml/js/test/moz.rdf @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mozilla/modules/xml/js/test/navcntr.rdf b/mozilla/modules/xml/js/test/navcntr.rdf new file mode 100644 index 00000000000..564ca4b2468 --- /dev/null +++ b/mozilla/modules/xml/js/test/navcntr.rdf @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mozilla/modules/xml/js/test/test.xml b/mozilla/modules/xml/js/test/test.xml new file mode 100644 index 00000000000..789a5897d51 --- /dev/null +++ b/mozilla/modules/xml/js/test/test.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/mozilla/modules/xml/js/test/xmlgraph.js b/mozilla/modules/xml/js/test/xmlgraph.js new file mode 100644 index 00000000000..d2dbc56074a --- /dev/null +++ b/mozilla/modules/xml/js/test/xmlgraph.js @@ -0,0 +1,23 @@ +prefix = ""; + +xml = new XMLGraph(); +xml.parseFile("moz.rdf"); + +function dumpobj(o, name) { + print(prefix + "dumping " + name); + prefix += " "; + if (typeof(o) != "object") { + print(prefix + typeof(o) + " " + o + " is not an object!\n"); + return; + } + for (i in o) { + if (typeof(o[i]) == "object") + dumpobj(o[i], i); + else + print(prefix + i + "=" + o[i]); + } + prefix = prefix.substring(2); + print(prefix + "done " + name); +} + +dumpobj(xml.graph, "RDF:RDF"); diff --git a/mozilla/modules/xml/js/test/xmlparse.js b/mozilla/modules/xml/js/test/xmlparse.js new file mode 100644 index 00000000000..9d52fa0b882 --- /dev/null +++ b/mozilla/modules/xml/js/test/xmlparse.js @@ -0,0 +1,29 @@ +prefix = ""; + +function startElement(name, attr) { + print(prefix + 'starting "' + name + '"') + prefix += " "; + for (i in attr) { + print(prefix + i + '=' + attr[i]); + } +} + +function endElement(name) { + prefix = prefix.substring(2); + print(prefix + 'ending "' + name + '"'); +} + +function characterData(cdata) { + print(prefix + 'CDATA: "' + cdata + '"'); +} + +function processingInstruction(target, data) { + print(prefix + 'processing insn for "' + target + '": "' + data + '"'); +} + +xml = new XMLParser(); +xml.startElement = startElement; +xml.endElement = endElement; +xml.characterData = null; // characterData; + +xml.parseFile("moz.rdf"); diff --git a/mozilla/modules/xml/js/xmljs.c b/mozilla/modules/xml/js/xmljs.c new file mode 100644 index 00000000000..15d8f899602 --- /dev/null +++ b/mozilla/modules/xml/js/xmljs.c @@ -0,0 +1,147 @@ +#define XMLJS_INTERNAL +#include "xmljs.h" +#undef XMLJS_INTERNAL +#include +#include +#include +#include + +/** + * Parse a given string of XML. + * If we fail, we (try to) set an error property on the given obj, and + * return JSVAL_FALSE. + * If all is well, we return JSVAL_TRUE. + */ + +JSBool +xmljs_parse(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + XMLCallback *cb; + XML_Parser xml; + JSString *str; + JSBool ok; + + cb = (XMLCallback *)JS_GetPrivate(cx, obj); + if (!cb) { + JS_ReportError(cx, "XMLParser object has no parser!"); + return JS_FALSE; + } + if (cb->preParse && + !cb->preParse(cx, obj, argc, argv, rval)) + return JS_FALSE; + if (argc < 1) { + *rval = JSVAL_TRUE; + return JS_TRUE; + } + + xml = XML_ParserCreate(NULL); + if (!xml) + return JS_FALSE; /* XXX report error */ + /* after this point, have to leave via out: */ + + XML_SetElementHandler(xml, cb->start, cb->end); + XML_SetCharacterDataHandler(xml, cb->cdata); + XML_SetProcessingInstructionHandler(xml, cb->processing); + XML_SetUserData(xml, (void *)cb); + cb->xml = xml; + str = JS_ValueToString(cx, argv[0]); + if (!XML_Parse(xml, JS_GetStringBytes(str), JS_GetStringLength(str), 1)) { + str = JS_NewStringCopyZ(cx, XML_ErrorString(XML_GetErrorCode(xml))); + if (!str) { + ok = JS_FALSE; + goto out; + } + if (!JS_DefineProperty(cx, obj, "error", STRING_TO_JSVAL(str), + NULL, NULL, JSPROP_ENUMERATE)) { + ok = JS_FALSE; + goto out; + } + *rval = JSVAL_FALSE; + } else { + *rval = JSVAL_TRUE; + } + out: + XML_ParserFree(xml); + cb->xml = NULL; + return ok; +} + +/** + * Get a file into a string and call the above. + * XXX when File object is present, we don't need this mess. + */ + +static JSBool +xmljs_parseFile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSString *str, *filename; + struct stat sbuf; + char * buf; + int fd, len; + + *rval = JSVAL_TRUE; + if (argc < 1) + return JS_TRUE; + filename = JS_ValueToString(cx, argv[0]); + if (!filename) + return JS_TRUE; + + argv[0] = STRING_TO_JSVAL(filename); + + fd = open(JS_GetStringBytes(filename), O_RDONLY); + if (fd < 0) { + JS_ReportError(cx, + "XMLParser.prototype.parseFile: open of %s failed!\n", + JS_GetStringBytes(filename)); + return JS_FALSE; + } + + if (fstat(fd, &sbuf) < 0) { + JS_ReportError(cx, + "XMLParser.prototype.parseFile: stat of %s failed!\n", + JS_GetStringBytes(filename)); + close(fd); + return JS_FALSE; + } + + len = sbuf.st_size; + buf = (char *)JS_malloc(cx, len); + if (read(fd, (void *)buf, (size_t)len) < len) { + JS_ReportError(cx, + "XMLParser.prototype.parseFile: read of %s failed!\n", + JS_GetStringBytes(filename)); + JS_free(cx, buf); + close(fd); + return JS_FALSE; + } + close(fd); + str = JS_NewString(cx, buf, len); /* hand buf off to engine; don't free! */ + if (!str) { + JS_free(cx, buf); + return JS_FALSE; + } + argv[0] = STRING_TO_JSVAL(str); + return JS_CallFunctionName(cx, obj, "parse", argc, argv, rval); +} + +JSBool +XML_Init(JSContext *cx, JSObject *obj) +{ + jsval rval; + JSObject *proto; + + if (!JS_EvaluateScript(cx, JS_GetGlobalObject(cx), xmljs_newObj_str, + xmljs_newObj_size, + "XMLJS internal", 0, &rval)) + return JS_FALSE; + + proto = JSVAL_TO_OBJECT(rval); + if (!JS_DefineFunction(cx, proto, "parse", xmljs_parse, + 1, 0) || + !JS_DefineFunction(cx, proto, "parseFile", xmljs_parseFile, + 1, 0)) + return JS_FALSE; + + return XMLParser_Init(cx, obj, proto) && XMLGraph_Init(cx, obj, proto); +} diff --git a/mozilla/modules/xml/js/xmljs.h b/mozilla/modules/xml/js/xmljs.h new file mode 100644 index 00000000000..bffb0f6f56a --- /dev/null +++ b/mozilla/modules/xml/js/xmljs.h @@ -0,0 +1,34 @@ +#ifndef XMLJS_H +#define XMLJS_H + +#include +#include + +extern JSBool +XML_Init(JSContext *cx, JSObject *obj); + +#ifdef XMLJS_INTERNAL +typedef struct { + JSContext *cx; + JSObject *obj; + XML_Parser xml; + XML_StartElementHandler start; + XML_EndElementHandler end; + XML_CharacterDataHandler cdata; + XML_ProcessingInstructionHandler processing; + JSNative preParse; + JSNative postParse; +} XMLCallback; + +extern JSBool +XMLParser_Init(JSContext *cx, JSObject *obj, JSObject *parent_proto); + +extern JSBool +XMLGraph_Init(JSContext *cx, JSObject *obj, JSObject *parent_proto); + +#define xmljs_newObj_str "new Object();" +#define xmljs_newObj_size (sizeof(xmljs_newObj_str) - 1) + +#endif /* XMLJS_INTERNAL */ + +#endif /* XMLJS_H */