From f9ddaeb119b085be23ba1e9e4919d257844e3e88 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Tue, 9 Feb 2010 16:48:56 -0800 Subject: [PATCH 1/3] Changed --enable-jit to --enable-tracejit (bug 545245, r=sayrer). --- js/src/Makefile.in | 6 +++--- js/src/config/autoconf.mk.in | 2 +- js/src/configure.in | 26 +++++++++++++------------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/js/src/Makefile.in b/js/src/Makefile.in index 03f6123a8447..9a6585ac14c1 100644 --- a/js/src/Makefile.in +++ b/js/src/Makefile.in @@ -224,7 +224,7 @@ INSTALLED_HEADERS = \ jsxml.h \ $(NULL) -ifdef ENABLE_JIT +ifdef ENABLE_TRACEJIT VPATH += $(srcdir)/nanojit INSTALLED_HEADERS += \ @@ -265,7 +265,7 @@ ASFLAGS += -arch 6 ASFILES += jswince.asm endif -endif # ENABLE_JIT +endif # ENABLE_TRACEJIT ifdef HAVE_DTRACE INSTALLED_HEADERS += \ @@ -361,7 +361,7 @@ check-valgrind:: $(check-sync-dirs) $(srcdir)/build $(MOZ_SYNC_BUILD_FILES)/build endif -ifdef ENABLE_JIT +ifdef ENABLE_TRACEJIT check:: $(wildcard $(RUN_TEST_PROGRAM)) $(PYTHON) -u $(srcdir)/trace-test/trace-test.py \ --no-slow --no-progress --tinderbox $(DIST)/bin/js$(BIN_SUFFIX) diff --git a/js/src/config/autoconf.mk.in b/js/src/config/autoconf.mk.in index fdcf58d2a3c0..9773c837e3e8 100644 --- a/js/src/config/autoconf.mk.in +++ b/js/src/config/autoconf.mk.in @@ -341,6 +341,6 @@ HAVE_DTRACE= @HAVE_DTRACE@ VISIBILITY_FLAGS = @VISIBILITY_FLAGS@ WRAP_SYSTEM_INCLUDES = @WRAP_SYSTEM_INCLUDES@ -ENABLE_JIT = @ENABLE_JIT@ +ENABLE_TRACEJIT = @ENABLE_TRACEJIT@ NANOJIT_ARCH = @NANOJIT_ARCH@ HAVE_ARM_SIMD= @HAVE_ARM_SIMD@ diff --git a/js/src/configure.in b/js/src/configure.in index 300fbfc31c7f..5fcb33944e7a 100644 --- a/js/src/configure.in +++ b/js/src/configure.in @@ -2497,28 +2497,28 @@ dnl Configure JIT support case "$target" in i?86-*) - ENABLE_JIT=1 + ENABLE_TRACEJIT=1 NANOJIT_ARCH=i386 ;; x86_64*-*) - ENABLE_JIT=1 + ENABLE_TRACEJIT=1 NANOJIT_ARCH=X64 ;; arm*-*) - ENABLE_JIT=1 + ENABLE_TRACEJIT=1 NANOJIT_ARCH=ARM ;; sparc*-*) - ENABLE_JIT=1 + ENABLE_TRACEJIT=1 NANOJIT_ARCH=Sparc ;; esac -MOZ_ARG_DISABLE_BOOL(jit, -[ --disable-jit Disable JIT support], - ENABLE_JIT=) +MOZ_ARG_DISABLE_BOOL(tracejit, +[ --disable-tracejit Disable tracing JIT support], + ENABLE_TRACEJIT=) -if test "$ENABLE_JIT"; then +if test "$ENABLE_TRACEJIT"; then AC_DEFINE(FEATURE_NANOJIT) AC_DEFINE(JS_TRACER) @@ -2560,12 +2560,12 @@ freebsd*|kfreebsd*) AC_DEFINE(AVMPLUS_OS2) ;; *) - AC_MSG_ERROR([Unrecognized nanojit platform. Use --disable-jit to build without JIT support.]) + AC_MSG_ERROR([Unrecognized nanojit platform. Use --disable-tracejit to build without tracing JIT support.]) esac -fi # ENABLE_JIT +fi # ENABLE_TRACEJIT -AC_SUBST(ENABLE_JIT) +AC_SUBST(ENABLE_TRACEJIT) AC_SUBST(NANOJIT_ARCH) if test -z "$SKIP_COMPILER_CHECKS"; then @@ -4233,8 +4233,8 @@ MOZ_ARG_ENABLE_BOOL(tracevis, MOZ_TRACEVIS= ) if test -n "$MOZ_TRACEVIS"; then AC_DEFINE(MOZ_TRACEVIS) - if test -z "$ENABLE_JIT"; then - AC_MSG_ERROR([--enable-tracevis is incompatible with --disable-jit]) + if test -z "$ENABLE_TRACEJIT"; then + AC_MSG_ERROR([--enable-tracevis is incompatible with --disable-tracejit]) fi fi From 8e1d6facb8fa2eefc78d671c97bedeef8297f152 Mon Sep 17 00:00:00 2001 From: Brendan Eich Date: Wed, 10 Feb 2010 15:17:52 -0800 Subject: [PATCH 2/3] Bug 542002 - Optimize to flat closures even if some upvars can't be copied (r=jorendorff). --- js/src/imacros.c.out | 1 + js/src/jsemit.cpp | 19 +- js/src/jsemit.h | 6 + js/src/jsfun.h | 7 +- js/src/jsopcode.cpp | 16 +- js/src/jsopcode.tbl | 3 +- js/src/jsops.cpp | 6 + js/src/jsparse.cpp | 376 +++++++++--------- js/src/jsparse.h | 9 +- js/src/jstracer.cpp | 84 +++- js/src/jsxdrapi.h | 2 +- .../tests/basic/testGuardCalleeSneakAttack.js | 38 ++ .../basic/testGuardCalleeSneakAttack2.js | 39 ++ .../tests/basic/testPartialFlatClosure.js | 16 + 14 files changed, 389 insertions(+), 233 deletions(-) create mode 100644 js/src/trace-test/tests/basic/testGuardCalleeSneakAttack.js create mode 100644 js/src/trace-test/tests/basic/testGuardCalleeSneakAttack2.js create mode 100644 js/src/trace-test/tests/basic/testPartialFlatClosure.js diff --git a/js/src/imacros.c.out b/js/src/imacros.c.out index 027fb8351fa1..b34724fe6cee 100644 --- a/js/src/imacros.c.out +++ b/js/src/imacros.c.out @@ -917,6 +917,7 @@ uint8 js_opcode2extra[JSOP_LIMIT] = { 0, /* JSOP_SETMETHOD */ 0, /* JSOP_INITMETHOD */ 0, /* JSOP_UNBRAND */ + 0, /* JSOP_UNBRANDTHIS */ 0, /* JSOP_SHARPINIT */ }; #define JSOP_IS_IMACOP(x) (0 \ diff --git a/js/src/jsemit.cpp b/js/src/jsemit.cpp index 8a00c4ce4c68..1a07d5d2a641 100644 --- a/js/src/jsemit.cpp +++ b/js/src/jsemit.cpp @@ -2025,6 +2025,8 @@ BindNameToSlot(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) dn = pn->pn_lexdef; JS_ASSERT(dn->pn_defn); pn->pn_dflags |= (dn->pn_dflags & PND_CONST); + if (pn->isDeoptimized()) + return JS_TRUE; } else { if (!pn->pn_defn) return JS_TRUE; @@ -2616,6 +2618,7 @@ EmitNameOp(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn, op = JSOP_CALLNAME; break; case JSOP_GETGVAR: + JS_ASSERT(!cg->funbox); op = JSOP_CALLGVAR; break; case JSOP_GETARG: @@ -3582,13 +3585,7 @@ js_EmitFunctionScript(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body) } if (cg->flags & TCF_FUN_UNBRAND_THIS) { - if (js_Emit1(cx, cg, JSOP_THIS) < 0) - return false; - if (js_Emit1(cx, cg, JSOP_UNBRAND) < 0) - return false; - if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) - return false; - if (js_Emit1(cx, cg, JSOP_POP) < 0) + if (js_Emit1(cx, cg, JSOP_UNBRANDTHIS) < 0) return false; } @@ -5741,7 +5738,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) */ pn2 = pn->pn_left; atomIndex = (jsatomid) -1; /* quell GCC overwarning */ - switch (pn2->pn_type) { + switch (PN_TYPE(pn2)) { case TOK_NAME: if (!BindNameToSlot(cx, cg, pn2)) return JS_FALSE; @@ -5830,10 +5827,9 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) return JS_FALSE; EMIT_INDEX_OP(JSOP_GETXPROP, atomIndex); } else { + JS_ASSERT(PN_OP(pn2) != JSOP_GETUPVAR); EMIT_UINT16_IMM_OP((PN_OP(pn2) == JSOP_SETGVAR) ? JSOP_GETGVAR - : (PN_OP(pn2) == JSOP_GETUPVAR) - ? JSOP_GETUPVAR : (PN_OP(pn2) == JSOP_SETARG) ? JSOP_GETARG : JSOP_GETLOCAL, @@ -6333,7 +6329,6 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) case TOK_LP: { bool callop = (PN_TYPE(pn) == TOK_LP); - uintN oldflags; /* * Emit callable invocation or operator new (constructor call) code. @@ -6398,7 +6393,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) * JSOP_NEW bytecode with a two-byte immediate telling how many args * were pushed on the operand stack. */ - oldflags = cg->flags; + uintN oldflags = cg->flags; cg->flags &= ~TCF_IN_FOR_INIT; for (pn3 = pn2->pn_next; pn3; pn3 = pn3->pn_next) { if (!js_EmitTree(cx, cg, pn3)) diff --git a/js/src/jsemit.h b/js/src/jsemit.h index 34275049f5dc..94bcfc1af888 100644 --- a/js/src/jsemit.h +++ b/js/src/jsemit.h @@ -305,6 +305,12 @@ struct JSTreeContext { /* tree context for semantic checks */ */ #define TCF_FUN_UNBRAND_THIS 0x100000 +/* + * "Module pattern", i.e., a lambda that is immediately applied and the whole + * of an expression statement. + */ +#define TCF_FUN_MODULE_PATTERN 0x200000 + /* * Flags to check for return; vs. return expr; in a function. */ diff --git a/js/src/jsfun.h b/js/src/jsfun.h index 184bb973d212..a1fb7cc357e1 100644 --- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -128,7 +128,8 @@ typedef union JSLocalNames { JS_ASSERT((fun)->flags & JSFUN_TRCINFO), \ fun->u.n.trcinfo) -struct JSFunction : public JSObject { +struct JSFunction : public JSObject +{ uint16 nargs; /* maximum number of specified arguments, reflected as f.length/f.arity */ uint16 flags; /* flags, see JSFUN_* below and in jsapi.h */ @@ -192,6 +193,10 @@ struct JSFunction : public JSObject { JSAtom *findDuplicateFormal() const; uint32 countInterpretedReservedSlots() const; + + bool mightEscape() const { + return FUN_INTERPRETED(this) && (FUN_FLAT_CLOSURE(this) || u.i.nupvars == 0); + } }; /* diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index a3b9166f59fe..02fbbd99d0af 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -2528,13 +2528,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) break; case SRC_HIDDEN: - /* - * Hide this pop. Don't adjust our stack depth model if - * it's from a goto in a with or for/in. - */ + /* Hide this pop, it's from a goto in a with or for/in. */ todo = -2; - if (lastop == JSOP_UNBRAND) - (void) POP_STR(); break; case SRC_DECL: @@ -5586,14 +5581,9 @@ ReconstructPCStack(JSContext *cx, JSScript *script, jsbytecode *target, } } - /* - * Ignore early-exit code, which is SRC_HIDDEN, but do not ignore the - * hidden POP that sometimes appears after an UNBRAND. See bug 543565. - */ - if (sn && SN_TYPE(sn) == SRC_HIDDEN && - (op != JSOP_POP || js_GetOpcode(cx, script, pc - 1) != JSOP_UNBRAND)) { + /* Ignore early-exit code, which is annotated SRC_HIDDEN. */ + if (sn && SN_TYPE(sn) == SRC_HIDDEN) continue; - } if (SimulateOp(cx, script, op, cs, pc, pcstack, pcdepth) < 0) return -1; diff --git a/js/src/jsopcode.tbl b/js/src/jsopcode.tbl index b0f6f49c7636..02cc062c34f0 100644 --- a/js/src/jsopcode.tbl +++ b/js/src/jsopcode.tbl @@ -602,5 +602,6 @@ OPDEF(JSOP_CONCATN, 234,"concatn", NULL, 3, -1, 1, 13, JOF_UINT16 OPDEF(JSOP_SETMETHOD, 235,"setmethod", NULL, 3, 2, 1, 3, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING) OPDEF(JSOP_INITMETHOD, 236,"initmethod", NULL, 3, 2, 1, 3, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING) OPDEF(JSOP_UNBRAND, 237,"unbrand", NULL, 1, 1, 1, 0, JOF_BYTE) +OPDEF(JSOP_UNBRANDTHIS, 238,"unbrandthis", NULL, 1, 0, 0, 0, JOF_BYTE) -OPDEF(JSOP_SHARPINIT, 238,"sharpinit", NULL, 3, 0, 0, 0, JOF_UINT16|JOF_SHARPSLOT) +OPDEF(JSOP_SHARPINIT, 239,"sharpinit", NULL, 3, 0, 0, 0, JOF_UINT16|JOF_SHARPSLOT) diff --git a/js/src/jsops.cpp b/js/src/jsops.cpp index 289463bbf305..649714a230fb 100644 --- a/js/src/jsops.cpp +++ b/js/src/jsops.cpp @@ -1468,6 +1468,12 @@ BEGIN_CASE(JSOP_THIS) PUSH_OPND(OBJECT_TO_JSVAL(obj)); END_CASE(JSOP_THIS) +BEGIN_CASE(JSOP_UNBRANDTHIS) + COMPUTE_THIS(cx, fp, obj); + if (!obj->unbrand(cx)) + goto error; +END_CASE(JSOP_UNBRANDTHIS) + BEGIN_CASE(JSOP_GETTHISPROP) i = 0; COMPUTE_THIS(cx, fp, obj); diff --git a/js/src/jsparse.cpp b/js/src/jsparse.cpp index 931df7740e5a..2b92765726e0 100644 --- a/js/src/jsparse.cpp +++ b/js/src/jsparse.cpp @@ -326,7 +326,7 @@ JSFunctionBox::shouldUnbrand(uintN methods, uintN slowMethods) const { if (slowMethods != 0) { for (const JSFunctionBox *funbox = this; funbox; funbox = funbox->parent) { - if (!(funbox->node->pn_dflags & PND_MODULEPAT)) + if (!(funbox->tcflags & TCF_FUN_MODULE_PATTERN)) return true; if (funbox->inLoop) return true; @@ -2051,6 +2051,188 @@ OneBlockId(JSParseNode *fn, uint32 id) return true; } +static inline bool +CanFlattenUpvar(JSDefinition *dn, JSFunctionBox *funbox, uint32 tcflags) +{ + /* + * Consider the current function (the lambda, innermost below) using a var + * x defined two static levels up: + * + * function f() { + * // z = g(); + * var x = 42; + * function g() { + * return function () { return x; }; + * } + * return g(); + * } + * + * So long as (1) the initialization in 'var x = 42' dominates all uses of + * g and (2) x is not reassigned, it is safe to optimize the lambda to a + * flat closure. Uncommenting the early call to g makes this optimization + * unsafe (z could name a global setter that calls its argument). + */ + JSFunctionBox *afunbox = funbox; + uintN dnLevel = dn->frameLevel(); + + JS_ASSERT(dnLevel <= funbox->level); + while (afunbox->level != dnLevel) { + afunbox = afunbox->parent; + + /* + * NB: afunbox can't be null because we are sure to find a function box + * whose level == dnLevel before we would try to walk above the root of + * the funbox tree. See bug 493260 comments 16-18. + * + * Assert but check anyway, to protect future changes that bind eval + * upvars in the parser. + */ + JS_ASSERT(afunbox); + + /* + * If this function is reaching up across an enclosing funarg, then we + * cannot copy dn's value into a flat closure slot (the display stops + * working once the funarg escapes). + */ + if (!afunbox || afunbox->node->isFunArg()) + return false; + } + + /* + * If afunbox's function (which is at the same level as dn) is in a loop, + * pessimistically assume the variable initializer may be in the same loop. + * A flat closure would then be unsafe, as the captured variable could be + * assigned after the closure is created. See bug 493232. + */ + if (afunbox->inLoop) + return false; + + /* + * |with| and eval used as an operator defeat lexical scoping: they can be + * used to assign to any in-scope variable. Therefore they must disable + * flat closures that use such upvars. The parser detects these as special + * forms and marks the function heavyweight. + */ + if ((afunbox->parent ? afunbox->parent->tcflags : tcflags) & TCF_FUN_HEAVYWEIGHT) + return false; + + /* + * If afunbox's function is not a lambda, it will be hoisted, so it could + * capture the undefined value that by default initializes var, let, and + * const bindings. And if dn is a function that comes at (meaning a + * function refers to its own name) or strictly after afunbox, we also + * defeat the flat closure optimization for this dn. + */ + JSFunction *afun = (JSFunction *) afunbox->object; + if (!(afun->flags & JSFUN_LAMBDA)) { + if (dn->isBindingForm() || dn->pn_pos >= afunbox->node->pn_pos) + return false; + } + + if (!dn->isInitialized()) + return false; + + JSDefinition::Kind dnKind = dn->kind(); + if (dnKind != JSDefinition::CONST) { + if (dn->isAssigned()) + return false; + + /* + * Any formal could be mutated behind our back via the arguments + * object, so deoptimize if the outer function uses arguments. + * + * In a Function constructor call where the final argument -- the body + * source for the function to create -- contains a nested function + * definition or expression, afunbox->parent will be null. The body + * source might use |arguments| outside of any nested functions it may + * contain, so we have to check the tcflags parameter that was passed + * in from JSCompiler::compileFunctionBody. + */ + if (dnKind == JSDefinition::ARG && + ((afunbox->parent ? afunbox->parent->tcflags : tcflags) & TCF_FUN_USES_ARGUMENTS)) { + return false; + } + } + + /* + * Check quick-and-dirty dominance relation. Function definitions dominate + * their uses thanks to hoisting. Other binding forms hoist as undefined, + * of course, so check forward-reference and blockid relations. + */ + if (dnKind != JSDefinition::FUNCTION) { + /* + * Watch out for code such as + * + * (function () { + * ... + * var jQuery = ... = function (...) { + * return new jQuery.foo.bar(baz); + * } + * ... + * })(); + * + * where the jQuery variable is not reassigned, but of course is not + * initialized at the time that the would-be-flat closure containing + * the jQuery upvar is formed. + */ + if (dn->pn_pos.end >= afunbox->node->pn_pos.end || + (dn->isTopLevel() + ? !MinBlockId(afunbox->node, dn->pn_blockid) + : !dn->isBlockChild() || + !afunbox->node->isBlockChild() || + !OneBlockId(afunbox->node, dn->pn_blockid))) { + return false; + } + } + return true; +} + +static void +FlagHeavyweights(JSDefinition *dn, JSFunctionBox *funbox, uint32& tcflags) +{ + JSFunctionBox *afunbox = funbox->parent; + uintN dnLevel = dn->frameLevel(); + + while (afunbox) { + /* + * Notice that afunbox->level is the static level of the definition or + * expression of the function parsed into afunbox, not the static level + * of its body. Therefore we must add 1 to match dn's level to find the + * afunbox whose body contains the dn definition. + */ + if (afunbox->level + 1U == dnLevel || (dnLevel == 0 && dn->isLet())) { + afunbox->tcflags |= TCF_FUN_HEAVYWEIGHT; + break; + } + afunbox = afunbox->parent; + } + if (!afunbox && (tcflags & TCF_IN_FUNCTION)) + tcflags |= TCF_FUN_HEAVYWEIGHT; +} + +static void +DeoptimizeUsesWithin(JSDefinition *dn, JSFunctionBox *funbox, uint32& tcflags) +{ + JSParseNode **pnup = &dn->dn_uses; + uintN ndeoptimized = 0; + + while (JSParseNode *pnu = *pnup) { + JS_ASSERT(pnu->pn_used); + JS_ASSERT(!pnu->pn_defn); + const JSTokenPos &pos = funbox->node->pn_body->pn_pos; + if (pnu->pn_pos.begin >= pos.begin && pnu->pn_pos.end < pos.end) { + pnu->pn_dflags |= PND_DEOPTIMIZED; + *pnup = pnu->pn_link; + ++ndeoptimized; + continue; + } + pnup = &pnu->pn_link; + } + + if (ndeoptimized != 0) + FlagHeavyweights(dn, funbox, tcflags); +} + void JSCompiler::setFunctionKinds(JSFunctionBox *funbox, uint32& tcflags) { @@ -2181,8 +2363,9 @@ JSCompiler::setFunctionKinds(JSFunctionBox *funbox, uint32& tcflags) } else if (!mutation && !(funbox->tcflags & TCF_FUN_IS_GENERATOR)) { /* * Algol-like functions can read upvars using the dynamic - * link (cx->fp/fp->down). They do not need to entrain and - * search their environment. + * link (cx->fp/fp->down), optimized using the cx->display + * lookup table indexed by static level. They do not need + * to entrain and search their environment objects. */ FUN_METER(display); FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE); @@ -2191,7 +2374,7 @@ JSCompiler::setFunctionKinds(JSFunctionBox *funbox, uint32& tcflags) FUN_METER(setupvar); } } else { - uintN nupvars = 0; + uintN nupvars = 0, nflattened = 0; /* * For each lexical dependency from this closure to an outer @@ -2203,165 +2386,18 @@ JSCompiler::setFunctionKinds(JSFunctionBox *funbox, uint32& tcflags) if (!lexdep->isFreeVar()) { ++nupvars; - - /* - * Consider the current function (the lambda, innermost - * below) using a var x defined two static levels up: - * - * function f() { - * // z = g(); - * var x = 42; - * function g() { - * return function () { return x; }; - * } - * return g(); - * } - * - * So long as (1) the initialization in 'var x = 42' - * dominates all uses of g and (2) x is not reassigned, - * it is safe to optimize the lambda to a flat closure. - * Uncommenting the early call to g makes it unsafe to - * so optimize (z could name a global setter that calls - * its argument). - */ - JSFunctionBox *afunbox = funbox; - uintN lexdepLevel = lexdep->frameLevel(); - - JS_ASSERT(lexdepLevel <= funbox->level); - while (afunbox->level != lexdepLevel) { - afunbox = afunbox->parent; - - /* - * afunbox can't be null because we are sure - * to find a function box whose level == lexdepLevel - * before walking off the top of the funbox tree. - * See bug 493260 comments 16-18. - * - * Assert but check anyway, to check future changes - * that bind eval upvars in the parser. - */ - JS_ASSERT(afunbox); - - /* - * If this function is reaching up across an - * enclosing funarg, we cannot make a flat - * closure. The display stops working once the - * funarg escapes. - */ - if (!afunbox || afunbox->node->isFunArg()) - goto break2; - } - - /* - * If afunbox's function (which is at the same level as - * lexdep) is in a loop, pessimistically assume the - * variable initializer may be in the same loop. A flat - * closure would then be unsafe, as the captured - * variable could be assigned after the closure is - * created. See bug 493232. - */ - if (afunbox->inLoop) - break; - - /* - * with and eval defeat lexical scoping; eval anywhere - * in a variable's scope can assign to it. Both defeat - * the flat closure optimization. The parser detects - * these cases and flags the function heavyweight. - */ - if ((afunbox->parent ? afunbox->parent->tcflags : tcflags) - & TCF_FUN_HEAVYWEIGHT) { - break; - } - - /* - * If afunbox's function is not a lambda, it will be - * hoisted, so it could capture the undefined value - * that by default initializes var/let/const - * bindings. And if lexdep is a function that comes at - * (meaning a function refers to its own name) or - * strictly after afunbox, we also break to defeat the - * flat closure optimization. - */ - JSFunction *afun = (JSFunction *) afunbox->object; - if (!(afun->flags & JSFUN_LAMBDA)) { - if (lexdep->isBindingForm()) - break; - if (lexdep->pn_pos >= afunbox->node->pn_pos) - break; - } - - if (!lexdep->isInitialized()) - break; - - JSDefinition::Kind lexdepKind = lexdep->kind(); - if (lexdepKind != JSDefinition::CONST) { - if (lexdep->isAssigned()) - break; - - /* - * Any formal could be mutated behind our back via - * the arguments object, so deoptimize if the outer - * function uses arguments. - * - * In a Function constructor call where the final - * argument -- the body source for the function to - * create -- contains a nested function definition - * or expression, afunbox->parent will be null. The - * body source might use |arguments| outside of any - * nested functions it may contain, so we have to - * check the tcflags parameter that was passed in - * from JSCompiler::compileFunctionBody. - */ - if (lexdepKind == JSDefinition::ARG && - ((afunbox->parent ? afunbox->parent->tcflags : tcflags) & - TCF_FUN_USES_ARGUMENTS)) { - break; - } - } - - /* - * Check quick-and-dirty dominance relation. Function - * definitions dominate their uses thanks to hoisting. - * Other binding forms hoist as undefined, of course, - * so check forward-reference and blockid relations. - */ - if (lexdepKind != JSDefinition::FUNCTION) { - /* - * Watch out for code such as - * - * (function () { - * ... - * var jQuery = ... = function (...) { - * return new jQuery.foo.bar(baz); - * } - * ... - * })(); - * - * where the jQuery var is not reassigned, but of - * course is not initialized at the time that the - * would-be-flat closure containing the jQuery - * upvar is formed. - */ - if (lexdep->pn_pos.end >= afunbox->node->pn_pos.end) - break; - - if (lexdep->isTopLevel() - ? !MinBlockId(afunbox->node, lexdep->pn_blockid) - : !lexdep->isBlockChild() || - !afunbox->node->isBlockChild() || - !OneBlockId(afunbox->node, lexdep->pn_blockid)) { - break; - } + if (CanFlattenUpvar(lexdep, funbox, tcflags)) { + ++nflattened; + continue; } + DeoptimizeUsesWithin(lexdep, funbox, tcflags); } } - break2: if (nupvars == 0) { FUN_METER(onlyfreevar); FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE); - } else if (!ale) { + } else if (nflattened != 0) { /* * We made it all the way through the upvar loop, so it's * safe to optimize to a flat closure. @@ -2406,30 +2442,8 @@ JSCompiler::setFunctionKinds(JSFunctionBox *funbox, uint32& tcflags) while ((ale = iter()) != NULL) { JSDefinition *lexdep = ALE_DEFN(ale)->resolve(); - - if (!lexdep->isFreeVar()) { - JSFunctionBox *afunbox = funbox->parent; - uintN lexdepLevel = lexdep->frameLevel(); - - while (afunbox) { - /* - * NB: afunbox->level is the static level of - * the definition or expression of the function - * parsed into afunbox, not the static level of - * its body. Therefore we must add 1 to match - * lexdep's level to find the afunbox whose - * body contains the lexdep definition. - */ - if (afunbox->level + 1U == lexdepLevel || - (lexdepLevel == 0 && lexdep->isLet())) { - afunbox->tcflags |= TCF_FUN_HEAVYWEIGHT; - break; - } - afunbox = afunbox->parent; - } - if (!afunbox && (tcflags & TCF_IN_FUNCTION)) - tcflags |= TCF_FUN_HEAVYWEIGHT; - } + if (!lexdep->isFreeVar()) + FlagHeavyweights(lexdep, funbox, tcflags); } } @@ -5746,7 +5760,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) */ if (PN_TYPE(pn2->pn_head) == TOK_FUNCTION && !pn2->pn_head->pn_funbox->node->isFunArg()) { - pn2->pn_head->pn_funbox->node->pn_dflags |= PND_MODULEPAT; + pn2->pn_head->pn_funbox->tcflags |= TCF_FUN_MODULE_PATTERN; } break; case TOK_ASSIGN: diff --git a/js/src/jsparse.h b/js/src/jsparse.h index b53864472ecc..b23669349606 100644 --- a/js/src/jsparse.h +++ b/js/src/jsparse.h @@ -399,7 +399,7 @@ struct JSParseNode { } JSDefinition *lexdef() const { - JS_ASSERT(pn_used); + JS_ASSERT(pn_used || isDeoptimized()); JS_ASSERT(pn_arity == PN_NAME); return pn_lexdef; } @@ -419,9 +419,9 @@ struct JSParseNode { #define PND_PLACEHOLDER 0x80 /* placeholder definition for lexdep */ #define PND_FUNARG 0x100 /* downward or upward funarg usage */ #define PND_BOUND 0x200 /* bound to a stack or global slot */ -#define PND_MODULEPAT 0x400 /* "module pattern", i.e., a lambda - that is immediately applied and the - whole of an expression statement */ +#define PND_DEOPTIMIZED 0x400 /* former pn_used name node, pn_lexdef + still valid, but this use no longer + optimizable via an upvar opcode */ /* Flags to propagate from uses to definition. */ #define PND_USE2DEF_FLAGS (PND_ASSIGNED | PND_FUNARG) @@ -467,6 +467,7 @@ struct JSParseNode { bool isTopLevel() const { return test(PND_TOPLEVEL); } bool isBlockChild() const { return test(PND_BLOCKCHILD); } bool isPlaceholder() const { return test(PND_PLACEHOLDER); } + bool isDeoptimized() const { return test(PND_DEOPTIMIZED); } /* Defined below, see after struct JSDefinition. */ bool isAssigned() const; diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index deb8156e3fd3..4f802f7556e0 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -12523,8 +12523,8 @@ TraceRecorder::record_JSOP_GETDSLOT() LIns* callee_ins = get(&cx->fp->argv[-2]); unsigned index = GET_UINT16(cx->fp->regs->pc); - LIns* dslots_ins = NULL; - LIns* v_ins = stobj_get_dslot(callee_ins, index, dslots_ins); + LIns* dslots_ins = lir->insLoad(LIR_ldp, callee_ins, offsetof(JSObject, dslots)); + LIns* v_ins = lir->insLoad(LIR_ldcp, dslots_ins, index * sizeof(jsval)); stack(0, unbox_jsval(callee->dslots[index], v_ins, snapshot(BRANCH_EXIT))); return ARECORD_CONTINUE; @@ -12541,33 +12541,63 @@ TraceRecorder::record_JSOP_CALLDSLOT() JS_REQUIRES_STACK RecordingStatus TraceRecorder::guardCallee(jsval& callee) { - JS_ASSERT(VALUE_IS_FUNCTION(cx, callee)); - - VMSideExit* branchExit = snapshot(BRANCH_EXIT); JSObject* callee_obj = JSVAL_TO_OBJECT(callee); - LIns* callee_ins = get(&callee); + JS_ASSERT(callee_obj->isFunction()); + JSFunction* callee_fun = (JSFunction*) callee_obj->getPrivate(); + /* + * First, guard on the callee's function (JSFunction*) identity. This is + * necessary since tracing always inlines function calls. But note that + * TR::functionCall avoids calling TR::guardCallee for constant methods + * (those hit in the property cache from JSOP_CALLPROP). + */ + VMSideExit* branchExit = snapshot(BRANCH_EXIT); + LIns* callee_ins = get(&callee); tree->gcthings.addUnique(callee); + guard(true, lir->ins2(LIR_peq, stobj_get_private(callee_ins), - INS_CONSTPTR(callee_obj->getPrivate())), + INS_CONSTPTR(callee_fun)), branchExit); /* - * As long as we have this parent guard, we're guaranteed that if we record - * with a Call object which has a null getPrivate(), then on trace that - * Call object will continue to have a null private, because we're - * effectively guarding on Call object identity and Call objects can't pick - * up a stack frame once they have none. callProp and setCallProp depend - * on this and document where; if this guard is removed make sure to fix - * those methods. Search for the "parent guard" comments in them. + * Second, consider guarding on the parent scope of the callee. + * + * As long as we guard on parent scope, we are guaranteed when recording + * variable accesses for a Call object having no private data that we can + * emit code that avoids checking for an active JSStackFrame for the Call + * object (which would hold fresh variable values -- the Call object's + * dslots would be stale until the stack frame is popped). This is because + * Call objects can't pick up a new stack frame in their private slot once + * they have none. TR::callProp and TR::setCallProp depend on this fact and + * document where; if this guard is removed make sure to fix those methods. + * Search for the "parent guard" comments in them. + * + * In general, a loop in an escaping function scoped by Call objects could + * be traced before the function has returned, and the trace then triggered + * after, or vice versa. The function must escape, i.e., be a "funarg", or + * else there's no need to guard callee parent at all. So once we know (by + * static analysis) that a function may escape, we cannot avoid guarding on + * either the private data of the Call object or the Call object itself, if + * we wish to optimize for the particular deactivated stack frame (null + * private data) case as noted above. */ - guard(true, - lir->ins2(LIR_peq, - stobj_get_parent(callee_ins), - INS_CONSTOBJ(OBJ_GET_PARENT(cx, callee_obj))), - branchExit); + if (FUN_INTERPRETED(callee_fun) && + (!FUN_NULL_CLOSURE(callee_fun) || callee_fun->u.i.nupvars != 0)) { + JSObject* parent = callee_obj->getParent(); + + if (parent != globalObj) { + if (parent->getClass() != &js_CallClass) + RETURN_STOP("closure scoped by neither the global object nor a Call object"); + + guard(true, + lir->ins2(LIR_peq, + stobj_get_parent(callee_ins), + INS_CONSTOBJ(parent)), + branchExit); + } + } return RECORD_CONTINUE; } @@ -14172,7 +14202,7 @@ TraceRecorder::record_JSOP_LAMBDA_FC() return ARECORD_STOP; LIns* args[] = { - INS_CONSTOBJ(globalObj), + scopeChain(), INS_CONSTFUN(fun), cx_ins }; @@ -15389,6 +15419,20 @@ TraceRecorder::record_JSOP_UNBRAND() return ARECORD_CONTINUE; } +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_UNBRANDTHIS() +{ + LIns* this_ins; + RecordingStatus status = getThis(this_ins); + if (status != RECORD_CONTINUE) + return InjectStatus(status); + + LIns* args_ins[] = { this_ins, cx_ins }; + LIns* call_ins = lir->insCall(&js_Unbrand_ci, args_ins); + guard(true, call_ins, OOM_EXIT); + return ARECORD_CONTINUE; +} + JS_REQUIRES_STACK AbortableRecordingStatus TraceRecorder::record_JSOP_SHARPINIT() { diff --git a/js/src/jsxdrapi.h b/js/src/jsxdrapi.h index a0560f05ceca..a407ec323877 100644 --- a/js/src/jsxdrapi.h +++ b/js/src/jsxdrapi.h @@ -205,7 +205,7 @@ JS_XDRFindClassById(JSXDRState *xdr, uint32 id); * before deserialization of bytecode. If the saved version does not match * the current version, abort deserialization and invalidate the file. */ -#define JSXDR_BYTECODE_VERSION (0xb973c0de - 59) +#define JSXDR_BYTECODE_VERSION (0xb973c0de - 60) /* * Library-private functions. diff --git a/js/src/trace-test/tests/basic/testGuardCalleeSneakAttack.js b/js/src/trace-test/tests/basic/testGuardCalleeSneakAttack.js new file mode 100644 index 000000000000..37af1a274ef2 --- /dev/null +++ b/js/src/trace-test/tests/basic/testGuardCalleeSneakAttack.js @@ -0,0 +1,38 @@ +function loop(f, expected) { + // This is the loop that breaks us. + // At record time, f's parent is a Call object with no fp. + // At second execute time, it is a Call object with fp, + // and all the Call object's dslots are still JSVAL_VOID. + for (var i = 0; i < 9; i++) + assertEq(f(), expected); +} + +function C(bad) { + var x = bad; + function f() { + return x; // We trick TR::callProp() into emitting code that gets + // JSVAL_VOID (from the Call object's dslots) + // rather than the actual value (true or false). + } + this.m = f; + return f; +} + +var obj = { + set m(f) { + if (f()) // Call once to resolve x on the Call object, + // for shape consistency. Otherwise loop gets + // recorded twice. + loop(f, true); + } +}; + +loop(C.call(obj, false), false); +C.call(obj, true); + +checkStats({ + recorderStarted: 1, + recorderAborted: 0, + traceCompleted: 2, + traceTriggered: 4 +}); diff --git a/js/src/trace-test/tests/basic/testGuardCalleeSneakAttack2.js b/js/src/trace-test/tests/basic/testGuardCalleeSneakAttack2.js new file mode 100644 index 000000000000..6281fc6677e6 --- /dev/null +++ b/js/src/trace-test/tests/basic/testGuardCalleeSneakAttack2.js @@ -0,0 +1,39 @@ +function loop(f, expected) { + // This is the loop that breaks us. + // At record time, f's parent is a Call object with no fp. + // At second execute time, it is a Call object with fp, + // and all the Call object's dslots are still JSVAL_VOID. + for (var i = 0; i < 9; i++) + assertEq(f(), expected); +} + +function C(bad) { + var x = bad; + function f() { + return x; // We trick TR::callProp() into emitting code that gets + // JSVAL_VOID (from the Call object's dslots) + // rather than the actual value (true or false). + } + if (bad) + void (f + "a!"); + return f; +} + +var obj = { +}; + +// Warm up and trace with C's Call object entrained but its stack frame gone. +loop(C.call(obj, false), false); + +// Sneaky access to f via a prototype method called implicitly by operator +. +Function.prototype.toString = function () { loop(this, true); return "hah"; }; + +// Fail hard if we don't handle the implicit call out of C to F.p.toString. +C.call(obj, true); + +checkStats({ + recorderStarted: 1, + recorderAborted: 0, + traceCompleted: 2, + traceTriggered: 4 +}); diff --git a/js/src/trace-test/tests/basic/testPartialFlatClosure.js b/js/src/trace-test/tests/basic/testPartialFlatClosure.js new file mode 100644 index 000000000000..da022fb6d68a --- /dev/null +++ b/js/src/trace-test/tests/basic/testPartialFlatClosure.js @@ -0,0 +1,16 @@ +assertEq((('-r', function (s) { + function C(i) { + this.m = function () { return i * t; } + } + var t = s; + var a = []; + for (var i = 0; i < 5; i++) + a[a.length] = new C(i); + return a; +})(42))[4].m(), 168); + +checkStats({ + recorderStarted: 1, + recorderAborted: 0, + traceCompleted: 1, +}); From a696115795fb563d5375543a508beb840c6230b5 Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Tue, 9 Feb 2010 17:27:02 -0800 Subject: [PATCH 3/3] Bug 545275 - Remove JS_HAS_FILE_OBJECT support as it's rather broken (and unlikely to be fixed soon) and the presented API isn't very good. r=mrbkap --- js/src/jsapi.cpp | 10 - js/src/jsfile.cpp | 2767 --------------------------------------------- js/src/jsfile.h | 56 - js/src/jsfile.msg | 90 -- 4 files changed, 2923 deletions(-) delete mode 100644 js/src/jsfile.cpp delete mode 100644 js/src/jsfile.h delete mode 100644 js/src/jsfile.msg diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index fb8841ab163b..71de44cee7c6 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -90,10 +90,6 @@ #include "jsatominlines.h" #include "jsscopeinlines.h" -#if JS_HAS_FILE_OBJECT -#include "jsfile.h" -#endif - #if JS_HAS_XML_SUPPORT #include "jsxml.h" #endif @@ -1236,9 +1232,6 @@ JS_InitStandardClasses(JSContext *cx, JSObject *obj) #if JS_HAS_XML_SUPPORT js_InitXMLClasses(cx, obj) && #endif -#if JS_HAS_FILE_OBJECT - js_InitFileClass(cx, obj) && -#endif #if JS_HAS_GENERATORS js_InitIteratorClasses(cx, obj) && #endif @@ -1302,9 +1295,6 @@ static JSStdName standard_class_atoms[] = { {js_InitNamespaceClass, EAGER_ATOM_AND_XCLASP(Namespace)}, {js_InitQNameClass, EAGER_ATOM_AND_XCLASP(QName)}, #endif -#if JS_HAS_FILE_OBJECT - {js_InitFileClass, EAGER_ATOM_AND_CLASP(File)}, -#endif #if JS_HAS_GENERATORS {js_InitIteratorClasses, EAGER_ATOM_AND_CLASP(StopIteration)}, #endif diff --git a/js/src/jsfile.cpp b/js/src/jsfile.cpp deleted file mode 100644 index 77f3d1c4f33d..000000000000 --- a/js/src/jsfile.cpp +++ /dev/null @@ -1,2767 +0,0 @@ -/* -*- 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 File object - */ -#if JS_HAS_FILE_OBJECT - -#include "jsfile.h" -#include "jsstdint.h" - -/* ----------------- Platform-specific includes and defines ----------------- */ -#if defined(XP_WIN) || defined(XP_OS2) -# include -# include -# include -# include -# define FILESEPARATOR '\\' -# define FILESEPARATOR2 '/' -# define CURRENT_DIR "c:\\" -# define POPEN _popen -# define PCLOSE _pclose -#elif defined(SYMBIAN) -# include -# include -# include -# include -# include -# define FILESEPARATOR '\\' -# define FILESEPARATOR2 '/' -# define CURRENT_DIR "c:\\" -# define POPEN popen -# define PCLOSE pclose -#elif defined(XP_UNIX) || defined(XP_BEOS) -# include -# include -# include -# include -# include -# define FILESEPARATOR '/' -# define FILESEPARATOR2 '\0' -# define CURRENT_DIR "/" -# define POPEN popen -# define PCLOSE pclose -#endif - -/* --------------- Platform-independent includes and defines ---------------- */ -#include "jsapi.h" -#include "jsatom.h" -#include "jscntxt.h" -#include "jsdate.h" -#include "jsdbgapi.h" -#include "jsemit.h" -#include "jsfun.h" -#include "jslock.h" -#include "jsobj.h" -#include "jsparse.h" -#include "jsscan.h" -#include "jsscope.h" -#include "jsscript.h" -#include "jsstr.h" -#include "jsutil.h" /* Added by JSIFY */ -#include - -/* NSPR dependencies */ -#include "prio.h" -#include "prerror.h" - -#define SPECIAL_FILE_STRING "Special File" -#define CURRENTDIR_PROPERTY "currentDir" -#define SEPARATOR_PROPERTY "separator" -#define FILE_CONSTRUCTOR "File" -#define PIPE_SYMBOL '|' - -#define ASCII 0 -#define UTF8 1 -#define UCS2 2 - -#define asciistring "text" -#define utfstring "binary" -#define unicodestring "unicode" - -#ifdef PATH_MAX -#define MAX_PATH_LENGTH PATH_MAX -#else -#define MAX_PATH_LENGTH 1024 -#endif -#define MODE_SIZE 256 -#define NUMBER_SIZE 32 -#define MAX_LINE_LENGTH 256 -#define URL_PREFIX "file://" - -#define STDINPUT_NAME "Standard input stream" -#define STDOUTPUT_NAME "Standard output stream" -#define STDERROR_NAME "Standard error stream" - -#define RESOLVE_PATH js_canonicalPath /* js_absolutePath */ - -/* Error handling */ -typedef enum JSFileErrNum { -#define MSG_DEF(name, number, count, exception, format) \ - name = number, -#include "jsfile.msg" -#undef MSG_DEF - JSFileErr_Limit -#undef MSGDEF -} JSFileErrNum; - -#define JSFILE_HAS_DFLT_MSG_STRINGS 1 - -JSErrorFormatString JSFile_ErrorFormatString[JSFileErr_Limit] = { -#if JSFILE_HAS_DFLT_MSG_STRINGS -#define MSG_DEF(name, number, count, exception, format) \ - { format, count }, -#else -#define MSG_DEF(name, number, count, exception, format) \ - { NULL, count }, -#endif -#include "jsfile.msg" -#undef MSG_DEF -}; - -const JSErrorFormatString * -JSFile_GetErrorMessage(void *userRef, const char *locale, - const uintN errorNumber) -{ - if ((errorNumber > 0) && (errorNumber < JSFileErr_Limit)) - return &JSFile_ErrorFormatString[errorNumber]; - else - return NULL; -} - -#define JSFILE_CHECK_NATIVE(op) \ - if (file->isNative) { \ - JS_ReportWarning(cx, "Cannot call or access \"%s\" on native file %s",\ - op, file->path); \ - goto out; \ - } - -#define JSFILE_CHECK_WRITE \ - if (!file->isOpen) { \ - JS_ReportWarning(cx, \ - "File %s is closed, will open it for writing, proceeding", \ - file->path); \ - js_FileOpen(cx, obj, file, "write,append,create"); \ - } \ - if (!js_canWrite(cx, file)) { \ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, \ - JSFILEMSG_CANNOT_WRITE, file->path); \ - goto out; \ - } - -#define JSFILE_CHECK_READ \ - if (!file->isOpen) { \ - JS_ReportWarning(cx, \ - "File %s is closed, will open it for reading, proceeding", \ - file->path); \ - js_FileOpen(cx, obj, file, "read"); \ - } \ - if (!js_canRead(cx, file)) { \ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, \ - JSFILEMSG_CANNOT_READ, file->path); \ - goto out; \ - } - -#define JSFILE_CHECK_OPEN(op) \ - if (!file->isOpen) { \ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, \ - JSFILEMSG_FILE_MUST_BE_OPEN, op); \ - goto out; \ - } - -#define JSFILE_CHECK_CLOSED(op) \ - if (file->isOpen) { \ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, \ - JSFILEMSG_FILE_MUST_BE_CLOSED, op); \ - goto out; \ - } - -#define JSFILE_CHECK_ONE_ARG(op) \ - if (argc != 1) { \ - char str[NUMBER_SIZE]; \ - sprintf(str, "%d", argc); \ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, \ - JSFILEMSG_EXPECTS_ONE_ARG_ERROR, op, str); \ - goto out; \ - } - - -/* - Security mechanism, should define a callback for this. - The parameters are as follows: - SECURITY_CHECK(JSContext *cx, JSPrincipals *ps, char *op_name, JSFile *file) - XXX Should this be a real function returning a JSBool result (and getting - some typesafety help from the compiler?). -*/ -#define SECURITY_CHECK(cx, ps, op, file) \ - /* Define a callback here... */ - - -/* Structure representing the file internally */ -typedef struct JSFile { - char *path; /* the path to the file. */ - JSBool isOpen; - int32 mode; /* mode used to open the file: read, write, append, create, etc.. */ - int32 type; /* Asciiz, utf, unicode */ - char byteBuffer[3]; /* bytes read in advance by js_FileRead ( UTF8 encoding ) */ - jsint nbBytesInBuf; /* number of bytes stored in the buffer above */ - jschar charBuffer; /* character read in advance by readln ( mac files only ) */ - JSBool charBufferUsed; /* flag indicating if the buffer above is being used */ - JSBool hasRandomAccess;/* can the file be randomly accessed? false for stdin, and - UTF-encoded files. */ - JSBool hasAutoflush; /* should we force a flush for each line break? */ - JSBool isNative; /* if the file is using OS-specific file FILE type */ - /* We can actually put the following two in a union since they should never be used at the same time */ - PRFileDesc *handle; /* the handle for the file, if open. */ - FILE *nativehandle; /* native handle, for stuff NSPR doesn't do. */ - JSBool isPipe; /* if the file is really an OS pipe */ -} JSFile; - -/* a few forward declarations... */ -JS_PUBLIC_API(JSObject*) js_NewFileObject(JSContext *cx, char *filename); -static JSBool file_open(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); -static JSBool file_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); - -/* New filename manipulation procesures */ -/* assumes we don't have leading/trailing spaces */ -static JSBool -js_filenameHasAPipe(const char *filename) -{ - if (!filename) - return JS_FALSE; - - return filename[0] == PIPE_SYMBOL || - filename[strlen(filename) - 1] == PIPE_SYMBOL; -} - -static JSBool -js_isAbsolute(const char *name) -{ -#if defined(XP_WIN) || defined(XP_OS2) || defined(SYMBIAN) - return *name && name[1] == ':'; -#else - return (name[0] -# if defined(XP_UNIX) || defined(XP_BEOS) - == -# else - != -# endif - FILESEPARATOR); -#endif -} - -/* - * Concatenates base and name to produce a valid filename. - * Returned string must be freed. -*/ -static char* -js_combinePath(JSContext *cx, const char *base, const char *name) -{ - int len = strlen(base); - char* result = cx->malloc(len + strlen(name) + 2); - - if (!result) - return NULL; - - strcpy(result, base); - - if (base[len - 1] != FILESEPARATOR && base[len - 1] != FILESEPARATOR2) { - result[len] = FILESEPARATOR; - result[len + 1] = '\0'; - } - strcat(result, name); - return result; -} - -/* Extract the last component from a path name. Returned string must be freed */ -static char * -js_fileBaseName(JSContext *cx, const char *pathname) -{ - jsint index, aux; - char *result; - - index = strlen(pathname)-1; - - /* Chop off trailing separators. */ - while (index > 0 && (pathname[index]==FILESEPARATOR || - pathname[index]==FILESEPARATOR2)) { - --index; - } - - aux = index; - - /* Now find the next separator. */ - while (index >= 0 && pathname[index] != FILESEPARATOR && - pathname[index] != FILESEPARATOR2) { - --index; - } - - /* Allocate and copy. */ - result = cx->malloc(aux - index + 1); - if (!result) - return NULL; - strncpy(result, pathname + index + 1, aux - index); - result[aux - index] = '\0'; - return result; -} - -/* - * Returns everything but the last component from a path name. - * Returned string must be freed. - */ -static char * -js_fileDirectoryName(JSContext *cx, const char *pathname) -{ - char *result; - const char *cp, *end; - size_t pathsize; - - end = pathname + strlen(pathname); - cp = end - 1; - - /* If this is already a directory, chop off the trailing /s. */ - while (cp >= pathname) { - if (*cp != FILESEPARATOR && *cp != FILESEPARATOR2) - break; - --cp; - } - - if (cp < pathname && end != pathname) { - /* There were just /s, return the root. */ - result = cx->malloc(1 + 1); /* The separator + trailing NUL. */ - result[0] = FILESEPARATOR; - result[1] = '\0'; - return result; - } - - /* Now chop off the last portion. */ - while (cp >= pathname) { - if (*cp == FILESEPARATOR || *cp == FILESEPARATOR2) - break; - --cp; - } - - /* Check if this is a leaf. */ - if (cp < pathname) { - /* It is, return "pathname/". */ - if (end[-1] == FILESEPARATOR || end[-1] == FILESEPARATOR2) { - /* Already has its terminating /. */ - return JS_strdup(cx, pathname); - } - - pathsize = end - pathname + 1; - result = cx->malloc(pathsize + 1); - if (!result) - return NULL; - - strcpy(result, pathname); - result[pathsize - 1] = FILESEPARATOR; - result[pathsize] = '\0'; - - return result; - } - - /* Return everything up to and including the seperator. */ - pathsize = cp - pathname + 1; - result = cx->malloc(pathsize + 1); - if (!result) - return NULL; - - strncpy(result, pathname, pathsize); - result[pathsize] = '\0'; - - return result; -} - -static char * -js_absolutePath(JSContext *cx, const char * path) -{ - JSObject *obj; - JSString *str; - jsval prop; - - if (js_isAbsolute(path)) { - return JS_strdup(cx, path); - } else { - obj = JS_GetGlobalObject(cx); - if (!JS_GetProperty(cx, obj, FILE_CONSTRUCTOR, &prop)) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_FILE_CONSTRUCTOR_UNDEFINED_ERROR); - return JS_strdup(cx, path); - } - - obj = JSVAL_TO_OBJECT(prop); - if (!JS_GetProperty(cx, obj, CURRENTDIR_PROPERTY, &prop)) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_FILE_CURRENTDIR_UNDEFINED_ERROR); - return JS_strdup(cx, path); - } - - str = JS_ValueToString(cx, prop); - if (!str) - return JS_strdup(cx, path); - - /* should we have an array of curr dirs indexed by drive for windows? */ - return js_combinePath(cx, JS_GetStringBytes(str), path); - } -} - -/* Side effect: will remove spaces in the beginning/end of the filename */ -static char * -js_canonicalPath(JSContext *cx, char *oldpath) -{ - char *tmp; - char *path = oldpath; - char *base, *dir, *current, *result; - jsint c; - jsint back = 0; - unsigned int i = 0, j = strlen(path)-1; - - /* This is probably optional */ - /* Remove possible spaces in the beginning and end */ - while (i < j && path[i] == ' ') - i++; - while (j >= 0 && path[j] == ' ') - j--; - - tmp = cx->malloc(j-i+2); - if (!tmp) - return NULL; - - strncpy(tmp, path + i, j - i + 1); - tmp[j - i + 1] = '\0'; - - path = tmp; - - /* Pipe support. */ - if (js_filenameHasAPipe(path)) - return path; - - /* file:// support. */ - if (!strncmp(path, URL_PREFIX, strlen(URL_PREFIX))) { - tmp = js_canonicalPath(cx, path + strlen(URL_PREFIX)); - cx->free(path); - return tmp; - } - - if (!js_isAbsolute(path)) { - tmp = js_absolutePath(cx, path); - if (!tmp) - return NULL; - cx->free(path); - path = tmp; - } - - result = JS_strdup(cx, ""); - - current = path; - - base = js_fileBaseName(cx, current); - dir = js_fileDirectoryName(cx, current); - - while (strcmp(dir, current)) { - if (!strcmp(base, "..")) { - back++; - } else { - if (back > 0) { - back--; - } else { - tmp = result; - result = cx->malloc(strlen(base) + 1 + strlen(tmp) + 1); - if (!result) - goto out; - - strcpy(result, base); - c = strlen(result); - if (*tmp) { - result[c] = FILESEPARATOR; - result[c + 1] = '\0'; - strcat(result, tmp); - } - cx->free(tmp); - } - } - cx->free(current); - cx->free(base); - current = dir; - base = js_fileBaseName(cx, current); - dir = js_fileDirectoryName(cx, current); - } - - tmp = result; - result = cx->malloc(strlen(dir) + 1 + strlen(tmp) + 1); - if (!result) - goto out; - - strcpy(result, dir); - c = strlen(result); - if (tmp[0]!='\0') { - if ((result[c-1]!=FILESEPARATOR)&&(result[c-1]!=FILESEPARATOR2)) { - result[c] = FILESEPARATOR; - result[c+1] = '\0'; - } - strcat(result, tmp); - } - -out: - if (tmp) - cx->free(tmp); - if (dir) - cx->free(dir); - if (base) - cx->free(base); - if (current) - cx->free(current); - - return result; -} - -/* -------------------------- Text conversion ------------------------------- */ -/* The following is ripped from libi18n/unicvt.c and include files.. */ - -/* - * UTF8 defines and macros - */ -#define ONE_OCTET_BASE 0x00 /* 0xxxxxxx */ -#define ONE_OCTET_MASK 0x7F /* x1111111 */ -#define CONTINUING_OCTET_BASE 0x80 /* 10xxxxxx */ -#define CONTINUING_OCTET_MASK 0x3F /* 00111111 */ -#define TWO_OCTET_BASE 0xC0 /* 110xxxxx */ -#define TWO_OCTET_MASK 0x1F /* 00011111 */ -#define THREE_OCTET_BASE 0xE0 /* 1110xxxx */ -#define THREE_OCTET_MASK 0x0F /* 00001111 */ -#define FOUR_OCTET_BASE 0xF0 /* 11110xxx */ -#define FOUR_OCTET_MASK 0x07 /* 00000111 */ -#define FIVE_OCTET_BASE 0xF8 /* 111110xx */ -#define FIVE_OCTET_MASK 0x03 /* 00000011 */ -#define SIX_OCTET_BASE 0xFC /* 1111110x */ -#define SIX_OCTET_MASK 0x01 /* 00000001 */ - -#define IS_UTF8_1ST_OF_1(x) (( (x)&~ONE_OCTET_MASK ) == ONE_OCTET_BASE) -#define IS_UTF8_1ST_OF_2(x) (( (x)&~TWO_OCTET_MASK ) == TWO_OCTET_BASE) -#define IS_UTF8_1ST_OF_3(x) (( (x)&~THREE_OCTET_MASK) == THREE_OCTET_BASE) -#define IS_UTF8_1ST_OF_4(x) (( (x)&~FOUR_OCTET_MASK ) == FOUR_OCTET_BASE) -#define IS_UTF8_1ST_OF_5(x) (( (x)&~FIVE_OCTET_MASK ) == FIVE_OCTET_BASE) -#define IS_UTF8_1ST_OF_6(x) (( (x)&~SIX_OCTET_MASK ) == SIX_OCTET_BASE) -#define IS_UTF8_2ND_THRU_6TH(x) \ - (( (x)&~CONTINUING_OCTET_MASK ) == CONTINUING_OCTET_BASE) -#define IS_UTF8_1ST_OF_UCS2(x) \ - IS_UTF8_1ST_OF_1(x) \ - || IS_UTF8_1ST_OF_2(x) \ - || IS_UTF8_1ST_OF_3(x) - - -#define MAX_UCS2 0xFFFF -#define DEFAULT_CHAR 0x003F /* Default char is "?" */ -#define BYTE_MASK 0xBF -#define BYTE_MARK 0x80 - - -/* Function: one_ucs2_to_utf8_char - * - * Function takes one UCS-2 char and writes it to a UTF-8 buffer. - * We need a UTF-8 buffer because we don't know before this - * function how many bytes of utf-8 data will be written. It also - * takes a pointer to the end of the UTF-8 buffer so that we don't - * overwrite data. This function returns the number of UTF-8 bytes - * of data written, or -1 if the buffer would have been overrun. - */ - -#define LINE_SEPARATOR 0x2028 -#define PARAGRAPH_SEPARATOR 0x2029 -static int16 one_ucs2_to_utf8_char(unsigned char *tobufp, - unsigned char *tobufendp, - uint16 onechar) -{ - int16 numUTF8bytes = 0; - - if (onechar == LINE_SEPARATOR || onechar == PARAGRAPH_SEPARATOR) { - strcpy((char*)tobufp, "\n"); - return strlen((char*)tobufp); - } - - if (onechar < 0x80) { - numUTF8bytes = 1; - } else if (onechar < 0x800) { - numUTF8bytes = 2; - } else { - /* 0x800 >= onechar <= MAX_UCS2 */ - numUTF8bytes = 3; - } - - tobufp += numUTF8bytes; - - /* return error if we don't have space for the whole character */ - if (tobufp > tobufendp) { - return(-1); - } - - switch(numUTF8bytes) { - case 3: *--tobufp = (onechar | BYTE_MARK) & BYTE_MASK; onechar >>=6; - *--tobufp = (onechar | BYTE_MARK) & BYTE_MASK; onechar >>=6; - *--tobufp = onechar | THREE_OCTET_BASE; - break; - - case 2: *--tobufp = (onechar | BYTE_MARK) & BYTE_MASK; onechar >>=6; - *--tobufp = onechar | TWO_OCTET_BASE; - break; - - case 1: *--tobufp = (unsigned char)onechar; - break; - } - - return numUTF8bytes; -} - -/* - * utf8_to_ucs2_char - * - * Convert a utf8 multibyte character to ucs2 - * - * inputs: pointer to utf8 character(s) - * length of utf8 buffer ("read" length limit) - * pointer to return ucs2 character - * - * outputs: number of bytes in the utf8 character - * -1 if not a valid utf8 character sequence - * -2 if the buffer is too short - */ -static int16 -utf8_to_ucs2_char(const unsigned char *utf8p, int16 buflen, uint16 *ucs2p) -{ - uint16 lead, cont1, cont2; - - /* - * Check for minimum buffer length - */ - if ((buflen < 1) || (utf8p == NULL)) { - return -2; - } - lead = (uint16) (*utf8p); - - /* - * Check for a one octet sequence - */ - if (IS_UTF8_1ST_OF_1(lead)) { - *ucs2p = lead & ONE_OCTET_MASK; - return 1; - } - - /* - * Check for a two octet sequence - */ - if (IS_UTF8_1ST_OF_2(*utf8p)) { - if (buflen < 2) - return -2; - cont1 = (uint16) *(utf8p+1); - if (!IS_UTF8_2ND_THRU_6TH(cont1)) - return -1; - *ucs2p = (lead & TWO_OCTET_MASK) << 6; - *ucs2p |= cont1 & CONTINUING_OCTET_MASK; - return 2; - } - - /* - * Check for a three octet sequence - */ - else if (IS_UTF8_1ST_OF_3(lead)) { - if (buflen < 3) - return -2; - cont1 = (uint16) *(utf8p+1); - cont2 = (uint16) *(utf8p+2); - if ( (!IS_UTF8_2ND_THRU_6TH(cont1)) - || (!IS_UTF8_2ND_THRU_6TH(cont2))) - return -1; - *ucs2p = (lead & THREE_OCTET_MASK) << 12; - *ucs2p |= (cont1 & CONTINUING_OCTET_MASK) << 6; - *ucs2p |= cont2 & CONTINUING_OCTET_MASK; - return 3; - } - else { /* not a valid utf8/ucs2 character */ - return -1; - } -} - -/* ----------------------------- Helper functions --------------------------- */ -/* Ripped off from lm_win.c .. */ -/* where is strcasecmp?.. for now, it's case sensitive.. - * - * strcasecmp is in strings.h, but on windows it's called _stricmp... - * will need to #ifdef this -*/ - -static int32 -js_FileHasOption(JSContext *cx, const char *oldoptions, const char *name) -{ - char *comma, *equal, *current; - char *options = JS_strdup(cx, oldoptions); - int32 found = 0; - - current = options; - for (;;) { - comma = strchr(current, ','); - if (comma) *comma = '\0'; - equal = strchr(current, '='); - if (equal) *equal = '\0'; - if (strcmp(current, name) == 0) { - if (!equal || strcmp(equal + 1, "yes") == 0) - found = 1; - else - found = atoi(equal + 1); - } - if (equal) *equal = '='; - if (comma) *comma = ','; - if (found || !comma) - break; - current = comma + 1; - } - cx->free(options); - return found; -} - -/* empty the buffer */ -static void -js_ResetBuffers(JSFile * file) -{ - file->charBufferUsed = JS_FALSE; - file->nbBytesInBuf = 0; -} - -/* Reset file attributes */ -static void -js_ResetAttributes(JSFile * file) -{ - file->mode = file->type = 0; - file->isOpen = JS_FALSE; - file->handle = NULL; - file->nativehandle = NULL; - file->hasRandomAccess = JS_TRUE; /* Innocent until proven guilty. */ - file->hasAutoflush = JS_FALSE; - file->isNative = JS_FALSE; - file->isPipe = JS_FALSE; - - js_ResetBuffers(file); -} - -static JSBool -js_FileOpen(JSContext *cx, JSObject *obj, JSFile *file, char *mode){ - JSString *type, *mask; - jsval v[2]; - jsval rval; - - type = JS_InternString(cx, asciistring); - mask = JS_NewStringCopyZ(cx, mode); - v[0] = STRING_TO_JSVAL(mask); - v[1] = STRING_TO_JSVAL(type); - - if (!file_open(cx, obj, 2, v, &rval)) - return JS_FALSE; - return JS_TRUE; -} - -/* Buffered version of PR_Read. Used by js_FileRead */ -static int32 -js_BufferedRead(JSFile *f, unsigned char *buf, int32 len) -{ - int32 count = 0; - - while (f->nbBytesInBuf>0&&len>0) { - buf[0] = f->byteBuffer[0]; - f->byteBuffer[0] = f->byteBuffer[1]; - f->byteBuffer[1] = f->byteBuffer[2]; - f->nbBytesInBuf--; - len--; - buf+=1; - count++; - } - - if (len > 0) { - count += (!f->isNative) - ? PR_Read(f->handle, buf, len) - : fread(buf, 1, len, f->nativehandle); - } - return count; -} - -static int32 -js_FileRead(JSContext *cx, JSFile *file, jschar *buf, int32 len, int32 mode) -{ - unsigned char *aux; - int32 count = 0, i; - jsint remainder; - unsigned char utfbuf[3]; - - if (file->charBufferUsed) { - buf[0] = file->charBuffer; - buf++; - len--; - file->charBufferUsed = JS_FALSE; - } - - switch (mode) { - case ASCII: - aux = (unsigned char*)cx->malloc(len); - if (!aux) - return 0; - - count = js_BufferedRead(file, aux, len); - if (count == -1) { - cx->free(aux); - return 0; - } - - for (i = 0; i < len; i++) - buf[i] = (jschar)aux[i]; - - cx->free(aux); - break; - - case UTF8: - remainder = 0; - for (count = 0;count0) { - file->byteBuffer[file->nbBytesInBuf] = utfbuf[0]; - file->nbBytesInBuf++; - utfbuf[0] = utfbuf[1]; - utfbuf[1] = utfbuf[2]; - remainder--; - } - break; - - case UCS2: - count = js_BufferedRead(file, (unsigned char *)buf, len * 2) >> 1; - if (count == -1) - return 0; - - break; - - default: - /* Not reached. */ - JS_ASSERT(0); - } - - if(count == -1) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OP_FAILED, "read", file->path); - } - - return count; -} - -static int32 -js_FileSeek(JSContext *cx, JSFile *file, int32 len, int32 mode) -{ - int32 count = 0, i; - jsint remainder; - unsigned char utfbuf[3]; - jschar tmp; - - switch (mode) { - case ASCII: - count = PR_Seek(file->handle, len, PR_SEEK_CUR); - break; - - case UTF8: - remainder = 0; - for (count = 0;count0) { - file->byteBuffer[file->nbBytesInBuf] = utfbuf[0]; - file->nbBytesInBuf++; - utfbuf[0] = utfbuf[1]; - utfbuf[1] = utfbuf[2]; - remainder--; - } - break; - - case UCS2: - count = PR_Seek(file->handle, len*2, PR_SEEK_CUR)/2; - break; - - default: - /* Not reached. */ - JS_ASSERT(0); - } - - if(count == -1) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OP_FAILED, "seek", file->path); - } - - return count; -} - -static int32 -js_FileWrite(JSContext *cx, JSFile *file, jschar *buf, int32 len, int32 mode) -{ - unsigned char *aux; - int32 count = 0, i, j; - unsigned char *utfbuf; - - switch (mode) { - case ASCII: - aux = (unsigned char*)cx->malloc(len); - if (!aux) - return 0; - - for (i = 0; iisNative) - ? PR_Write(file->handle, aux, len) - : fwrite(aux, 1, len, file->nativehandle); - - if (count==-1) { - cx->free(aux); - return 0; - } - - cx->free(aux); - break; - - case UTF8: - utfbuf = (unsigned char*)cx->malloc(len*3); - if (!utfbuf) return 0; - i = 0; - for (count = 0;countfree(utfbuf); - return 0; - } - i+=j; - } - j = (!file->isNative) - ? PR_Write(file->handle, utfbuf, i) - : fwrite(utfbuf, 1, i, file->nativehandle); - - if (jfree(utfbuf); - return 0; - } - cx->free(utfbuf); - break; - - case UCS2: - count = (!file->isNative) - ? PR_Write(file->handle, buf, len*2) >> 1 - : fwrite(buf, 1, len*2, file->nativehandle) >> 1; - - if (count == -1) - return 0; - break; - - default: - /* Not reached. */ - JS_ASSERT(0); - } - - if(count == -1) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OP_FAILED, "write", file->path); - } - - return count; -} - -/* ----------------------------- Property checkers -------------------------- */ -static JSBool -js_exists(JSContext *cx, JSFile *file) -{ - if (file->isNative) { - /* It doesn't make sense for a pipe of stdstream. */ - return JS_FALSE; - } - - return PR_Access(file->path, PR_ACCESS_EXISTS) == PR_SUCCESS; -} - -static JSBool -js_canRead(JSContext *cx, JSFile *file) -{ - if (!file->isNative) { - if (file->isOpen && !(file->mode & PR_RDONLY)) - return JS_FALSE; - return PR_Access(file->path, PR_ACCESS_READ_OK) == PR_SUCCESS; - } - - if (file->isPipe) { - /* Is this pipe open for reading? */ - return file->path[0] == PIPE_SYMBOL; - } - - return !strcmp(file->path, STDINPUT_NAME); -} - -static JSBool -js_canWrite(JSContext *cx, JSFile *file) -{ - if (!file->isNative) { - if (file->isOpen && !(file->mode & PR_WRONLY)) - return JS_FALSE; - return PR_Access(file->path, PR_ACCESS_WRITE_OK) == PR_SUCCESS; - } - - if(file->isPipe) { - /* Is this pipe open for writing? */ - return file->path[strlen(file->path)-1] == PIPE_SYMBOL; - } - - return !strcmp(file->path, STDOUTPUT_NAME) || - !strcmp(file->path, STDERROR_NAME); -} - -static JSBool -js_isFile(JSContext *cx, JSFile *file) -{ - if (!file->isNative) { - PRFileInfo info; - - if (file->isOpen - ? PR_GetOpenFileInfo(file->handle, &info) - : PR_GetFileInfo(file->path, &info) != PR_SUCCESS) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path); - return JS_FALSE; - } - - return info.type == PR_FILE_FILE; - } - - /* This doesn't make sense for a pipe of stdstream. */ - return JS_FALSE; -} - -static JSBool -js_isDirectory(JSContext *cx, JSFile *file) -{ - if(!file->isNative){ - PRFileInfo info; - - /* Hack needed to get get_property to work. */ - if (!js_exists(cx, file)) - return JS_FALSE; - - if (file->isOpen - ? PR_GetOpenFileInfo(file->handle, &info) - : PR_GetFileInfo(file->path, &info) != PR_SUCCESS) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path); - return JS_FALSE; - } - - return info.type == PR_FILE_DIRECTORY; - } - - /* This doesn't make sense for a pipe of stdstream. */ - return JS_FALSE; -} - -static jsval -js_size(JSContext *cx, JSFile *file) -{ - PRFileInfo info; - - JSFILE_CHECK_NATIVE("size"); - - if (file->isOpen - ? PR_GetOpenFileInfo(file->handle, &info) - : PR_GetFileInfo(file->path, &info) != PR_SUCCESS) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path); - return JSVAL_VOID; - } - - return INT_TO_JSVAL(info.size); - -out: - return JSVAL_VOID; -} - -/* - * Return the parent object - */ -static JSBool -js_parent(JSContext *cx, JSFile *file, jsval *resultp) -{ - char *str; - - /* Since we only care about pipes and native files, return NULL. */ - if (file->isNative) { - *resultp = JSVAL_VOID; - return JS_TRUE; - } - - str = js_fileDirectoryName(cx, file->path); - if (!str) - return JS_FALSE; - - /* If the directory is equal to the original path, we're at the root. */ - if (!strcmp(file->path, str)) { - *resultp = JSVAL_NULL; - } else { - JSObject *obj = js_NewFileObject(cx, str); - if (!obj) { - cx->free(str); - return JS_FALSE; - } - *resultp = OBJECT_TO_JSVAL(obj); - } - - cx->free(str); - return JS_TRUE; -} - -static JSBool -js_name(JSContext *cx, JSFile *file, jsval *vp) -{ - char *name; - JSString *str; - - if (file->isPipe) { - *vp = JSVAL_VOID; - return JS_TRUE; - } - - name = js_fileBaseName(cx, file->path); - if (!name) - return JS_FALSE; - - str = JS_NewString(cx, name, strlen(name)); - if (!str) { - cx->free(name); - return JS_FALSE; - } - - *vp = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -/* ------------------------------ File object methods ---------------------------- */ -static JSBool -file_open(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - JSString *strmode, *strtype; - char *ctype, *mode; - int32 mask, type; - int len; - - mode = NULL; - - SECURITY_CHECK(cx, NULL, "open", file); - - /* A native file that is already open */ - if(file->isOpen && file->isNative) { - JS_ReportWarning(cx, "Native file %s is already open, proceeding", - file->path); - goto good; - } - - /* Close before proceeding */ - if (file->isOpen) { - JS_ReportWarning(cx, "File %s is already open, we will close it and " - "reopen, proceeding", file->path); - if(!file_close(cx, obj, 0, NULL, rval)) - goto out; - } - - if (js_isDirectory(cx, file)) { - JS_ReportWarning(cx, "%s seems to be a directory, there is no point in " - "trying to open it, proceeding", file->path); - goto good; - } - - /* Path must be defined at this point */ - len = strlen(file->path); - - /* Mode */ - if (argc >= 1) { - strmode = JS_ValueToString(cx, argv[0]); - if (!strmode) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_FIRST_ARGUMENT_OPEN_NOT_STRING_ERROR, - argv[0]); - goto out; - } - mode = JS_strdup(cx, JS_GetStringBytes(strmode)); - } else { - if(file->path[0]==PIPE_SYMBOL) { - /* pipe default mode */ - mode = JS_strdup(cx, "read"); - } else if(file->path[len-1]==PIPE_SYMBOL) { - /* pipe default mode */ - mode = JS_strdup(cx, "write"); - } else { - /* non-destructive, permissive defaults. */ - mode = JS_strdup(cx, "readWrite,append,create"); - } - } - - /* Process the mode */ - mask = 0; - /* TODO: this is pretty ugly, we walk thru the string too many times */ - mask |= js_FileHasOption(cx, mode, "read") ? PR_RDONLY : 0; - mask |= js_FileHasOption(cx, mode, "write") ? PR_WRONLY : 0; - mask |= js_FileHasOption(cx, mode, "readWrite")? PR_RDWR : 0; - mask |= js_FileHasOption(cx, mode, "append") ? PR_APPEND : 0; - mask |= js_FileHasOption(cx, mode, "create") ? PR_CREATE_FILE : 0; - mask |= js_FileHasOption(cx, mode, "replace") ? PR_TRUNCATE : 0; - - if (mask & PR_RDWR) - mask |= (PR_RDONLY | PR_WRONLY); - if ((mask & PR_RDONLY) && (mask & PR_WRONLY)) - mask |= PR_RDWR; - - file->hasAutoflush |= js_FileHasOption(cx, mode, "autoflush"); - - /* Type */ - if (argc > 1) { - strtype = JS_ValueToString(cx, argv[1]); - if (!strtype) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_SECOND_ARGUMENT_OPEN_NOT_STRING_ERROR, - argv[1]); - goto out; - } - ctype = JS_GetStringBytes(strtype); - - if(!strcmp(ctype, utfstring)) { - type = UTF8; - } else if (!strcmp(ctype, unicodestring)) { - type = UCS2; - } else { - if (strcmp(ctype, asciistring)) { - JS_ReportWarning(cx, "File type %s is not supported, using " - "'text' instead, proceeding", ctype); - } - type = ASCII; - } - } else { - type = ASCII; - } - - /* Save the relevant fields */ - file->type = type; - file->mode = mask; - file->nativehandle = NULL; - file->hasRandomAccess = (type != UTF8); - - /* - * Deal with pipes here. We can't use NSPR for pipes, so we have to use - * POPEN. - */ - if (file->path[0]==PIPE_SYMBOL || file->path[len-1]==PIPE_SYMBOL) { - if (file->path[0] == PIPE_SYMBOL && file->path[len-1] == PIPE_SYMBOL) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_BIDIRECTIONAL_PIPE_NOT_SUPPORTED); - goto out; - } else { - int i = 0; - char pipemode[3]; - SECURITY_CHECK(cx, NULL, "pipe_open", file); - - if(file->path[0] == PIPE_SYMBOL){ - if(mask & (PR_WRONLY | PR_APPEND | PR_CREATE_FILE | PR_TRUNCATE)){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OPEN_MODE_NOT_SUPPORTED_WITH_PIPES, - mode, file->path); - goto out; - } - /* open(SPOOLER, "| cat -v | lpr -h 2>/dev/null") -- pipe for writing */ - pipemode[i++] = 'r'; -#ifndef XP_UNIX - pipemode[i++] = file->type==UTF8 ? 'b' : 't'; -#endif - pipemode[i++] = '\0'; - file->nativehandle = POPEN(&file->path[1], pipemode); - } else if(file->path[len-1] == PIPE_SYMBOL) { - char *command = cx->malloc(len); - - strncpy(command, file->path, len-1); - command[len-1] = '\0'; - /* open(STATUS, "netstat -an 2>&1 |") */ - pipemode[i++] = 'w'; -#ifndef XP_UNIX - pipemode[i++] = file->type==UTF8 ? 'b' : 't'; -#endif - pipemode[i++] = '\0'; - file->nativehandle = POPEN(command, pipemode); - cx->free(command); - } - /* set the flags */ - file->isNative = JS_TRUE; - file->isPipe = JS_TRUE; - file->hasRandomAccess = JS_FALSE; - } - } else { - /* TODO: what about the permissions?? Java ignores the problem... */ - file->handle = PR_Open(file->path, mask, 0644); - } - - js_ResetBuffers(file); - cx->free(mode); - mode = NULL; - - /* Set the open flag and return result */ - if (file->handle == NULL && file->nativehandle == NULL) { - file->isOpen = JS_FALSE; - - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OP_FAILED, "open", file->path); - goto out; - } - -good: - file->isOpen = JS_TRUE; - *rval = JSVAL_TRUE; - return JS_TRUE; - -out: - if(mode) - cx->free(mode); - return JS_FALSE; -} - -static JSBool -file_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - - SECURITY_CHECK(cx, NULL, "close", file); - - if(!file->isOpen){ - JS_ReportWarning(cx, "File %s is not open, can't close it, proceeding", - file->path); - goto out; - } - - if(!file->isPipe){ - if(file->isNative){ - JS_ReportWarning(cx, "Unable to close a native file, proceeding", file->path); - goto out; - }else{ - if(file->handle && PR_Close(file->handle)){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OP_FAILED, "close", file->path); - - goto out; - } - } - }else{ - if(PCLOSE(file->nativehandle)==-1){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OP_FAILED, "pclose", file->path); - goto out; - } - } - - js_ResetAttributes(file); - *rval = JSVAL_TRUE; - return JS_TRUE; - -out: - return JS_FALSE; -} - - -static JSBool -file_remove(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - - SECURITY_CHECK(cx, NULL, "remove", file); - JSFILE_CHECK_NATIVE("remove"); - JSFILE_CHECK_CLOSED("remove"); - - if ((js_isDirectory(cx, file) ? - PR_RmDir(file->path) : PR_Delete(file->path))==PR_SUCCESS) { - js_ResetAttributes(file); - *rval = JSVAL_TRUE; - return JS_TRUE; - } else { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OP_FAILED, "remove", file->path); - goto out; - } -out: - *rval = JSVAL_FALSE; - return JS_FALSE; -} - -/* Raw PR-based function. No text processing. Just raw data copying. */ -static JSBool -file_copyTo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - char *dest = NULL; - PRFileDesc *handle = NULL; - char *buffer; - jsval count, size; - JSBool fileInitiallyOpen=JS_FALSE; - - SECURITY_CHECK(cx, NULL, "copyTo", file); /* may need a second argument!*/ - JSFILE_CHECK_ONE_ARG("copyTo"); - JSFILE_CHECK_NATIVE("copyTo"); - /* remeber the state */ - fileInitiallyOpen = file->isOpen; - JSFILE_CHECK_READ; - - JSString *str = JS_ValueToString(cx, argv[0]); - if (!str) - goto out; - - dest = JS_GetStringBytes(str); - - /* make sure we are not reading a file open for writing */ - if (file->isOpen && !js_canRead(cx, file)) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_CANNOT_COPY_FILE_OPEN_FOR_WRITING_ERROR, file->path); - goto out; - } - - if (file->handle==NULL){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OP_FAILED, "open", file->path); - goto out; - } - - handle = PR_Open(dest, PR_WRONLY|PR_CREATE_FILE|PR_TRUNCATE, 0644); - - if(!handle){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OP_FAILED, "open", dest); - goto out; - } - - if ((size=js_size(cx, file))==JSVAL_VOID) { - goto out; - } - - buffer = cx->malloc(size); - - count = INT_TO_JSVAL(PR_Read(file->handle, buffer, size)); - - /* reading panic */ - if (count!=size) { - cx->free(buffer); - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_COPY_READ_ERROR, file->path); - goto out; - } - - count = INT_TO_JSVAL(PR_Write(handle, buffer, JSVAL_TO_INT(size))); - - /* writing panic */ - if (count!=size) { - cx->free(buffer); - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_COPY_WRITE_ERROR, file->path); - goto out; - } - - cx->free(buffer); - - if(!fileInitiallyOpen){ - if(!file_close(cx, obj, 0, NULL, rval)) goto out; - } - - if(PR_Close(handle)!=PR_SUCCESS){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OP_FAILED, "close", dest); - goto out; - } - - *rval = JSVAL_TRUE; - return JS_TRUE; -out: - if(file->isOpen && !fileInitiallyOpen){ - if(PR_Close(file->handle)!=PR_SUCCESS){ - JS_ReportWarning(cx, "Can't close %s, proceeding", file->path); - } - } - - if(handle && PR_Close(handle)!=PR_SUCCESS){ - JS_ReportWarning(cx, "Can't close %s, proceeding", dest); - } - - *rval = JSVAL_FALSE; - return JS_FALSE; -} - -static JSBool -file_renameTo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - char *dest; - - SECURITY_CHECK(cx, NULL, "renameTo", file); /* may need a second argument!*/ - JSFILE_CHECK_ONE_ARG("renameTo"); - JSFILE_CHECK_NATIVE("renameTo"); - JSFILE_CHECK_CLOSED("renameTo"); - - JSString *str = JS_ValueToString(cx, argv[0]); - if (!str) - goto out; - - dest = RESOLVE_PATH(cx, JS_GetStringBytes(str)); - - if (PR_Rename(file->path, dest)==PR_SUCCESS){ - /* copy the new filename */ - cx->free(file->path); - file->path = dest; - *rval = JSVAL_TRUE; - return JS_TRUE; - }else{ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_RENAME_FAILED, file->path, dest); - goto out; - } -out: - *rval = JSVAL_FALSE; - return JS_FALSE; -} - -static JSBool -file_flush(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - - SECURITY_CHECK(cx, NULL, "flush", file); - JSFILE_CHECK_NATIVE("flush"); - JSFILE_CHECK_OPEN("flush"); - - if (PR_Sync(file->handle)==PR_SUCCESS){ - *rval = JSVAL_TRUE; - return JS_TRUE; - }else{ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OP_FAILED, "flush", file->path); - goto out; - } -out: - *rval = JSVAL_FALSE; - return JS_FALSE; -} - -static JSBool -file_write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - JSString *str; - int32 count; - uintN i; - - SECURITY_CHECK(cx, NULL, "write", file); - JSFILE_CHECK_WRITE; - - for (i = 0; itype); - if (count==-1){ - *rval = JSVAL_FALSE; - return JS_FALSE; - } - } - - *rval = JSVAL_TRUE; - return JS_TRUE; -out: - *rval = JSVAL_FALSE; - return JS_FALSE; -} - -static JSBool -file_writeln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - JSString *str; - - SECURITY_CHECK(cx, NULL, "writeln", file); - JSFILE_CHECK_WRITE; - - /* don't report an error here */ - if(!file_write(cx, obj, argc, argv, rval)) return JS_FALSE; - /* don't do security here -- we passed the check in file_write */ - str = JS_NewStringCopyZ(cx, "\n"); - - if (js_FileWrite(cx, file, JS_GetStringChars(str), JS_GetStringLength(str), - file->type)==-1){ - *rval = JSVAL_FALSE; - return JS_FALSE; - } - - /* eol causes flush if hasAutoflush is turned on */ - if (file->hasAutoflush) - file_flush(cx, obj, 0, NULL, rval); - - *rval = JSVAL_TRUE; - return JS_TRUE; -out: - *rval = JSVAL_FALSE; - return JS_FALSE; -} - -static JSBool -file_writeAll(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - jsuint i; - jsuint limit; - JSObject *array; - JSObject *elem; - jsval elemval; - - SECURITY_CHECK(cx, NULL, "writeAll", file); - JSFILE_CHECK_ONE_ARG("writeAll"); - JSFILE_CHECK_WRITE; - - if (!JS_IsArrayObject(cx, JSVAL_TO_OBJECT(argv[0]))) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_FIRST_ARGUMENT_WRITEALL_NOT_ARRAY_ERROR); - goto out; - } - - array = JSVAL_TO_OBJECT(argv[0]); - - JS_GetArrayLength(cx, array, &limit); - - for (i = 0; i262144)?262144:want; * arbitrary size limitation */ - - buf = cx->malloc(want*sizeof buf[0]); - if (!buf) goto out; - - count = js_FileRead(cx, file, buf, want, file->type); - if (count>0) { - str = JS_NewUCStringCopyN(cx, buf, count); - *rval = STRING_TO_JSVAL(str); - cx->free(buf); - return JS_TRUE; - } else { - cx->free(buf); - goto out; - } -out: - *rval = JSVAL_FALSE; - return JS_FALSE; -} - -static JSBool -file_readln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - JSString *str; - jschar *buf = NULL, *tmp; - int32 offset, read; - intN room; - jschar data, data2; - - SECURITY_CHECK(cx, NULL, "readln", file); - JSFILE_CHECK_READ; - - buf = cx->malloc(MAX_LINE_LENGTH * sizeof data); - if (!buf) - return JS_FALSE; - - room = MAX_LINE_LENGTH - 1; - offset = 0; - - for (;;) { - read = js_FileRead(cx, file, &data, 1, file->type); - if (read < 0) - goto out; - if (read == 0) - goto eof; - - switch (data) { - case '\r': - read = js_FileRead(cx, file, &data2, 1, file->type); - if (read < 0) - goto out; - - if (read == 1 && data2 != '\n') { - /* We read one char too far. Buffer it. */ - file->charBuffer = data2; - file->charBufferUsed = JS_TRUE; - } - - /* Fall through. */ - case '\n': - goto done; - - default: - if (--room < 0) { - tmp = cx->realloc(buf, (offset + MAX_LINE_LENGTH) * sizeof data); - if (!tmp) - goto out; - - room = MAX_LINE_LENGTH - 1; - buf = tmp; - } - - buf[offset++] = data; - break; - } - } - -eof: - if (offset == 0) { - *rval = JSVAL_NULL; - return JS_TRUE; - } - -done: - buf[offset] = 0; - tmp = cx->realloc(buf, (offset + 1) * sizeof data); - if (!tmp) - goto out; - - str = JS_NewUCString(cx, tmp, offset); - if (!str) - goto out; - - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; - -out: - if (buf) - cx->free(buf); - - return JS_FALSE; -} - -static JSBool -file_readAll(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - JSObject *array; - jsint len; - jsval line; - JSBool lineok = JS_FALSE; - - SECURITY_CHECK(cx, NULL, "readAll", file); - JSFILE_CHECK_READ; - - array = JS_NewArrayObject(cx, 0, NULL); - if (!array) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(array); - - len = 0; - - lineok = file_readln(cx, obj, 0, NULL, &line); - while (lineok && !JSVAL_IS_NULL(line)) { - JS_SetElement(cx, array, len++, &line); - lineok = file_readln(cx, obj, 0, NULL, &line); - } - -out: - return lineok; -} - -static JSBool -file_seek(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - int32 toskip; - int32 pos; - - SECURITY_CHECK(cx, NULL, "seek", file); - JSFILE_CHECK_ONE_ARG("seek"); - JSFILE_CHECK_NATIVE("seek"); - JSFILE_CHECK_READ; - - if (!JS_ValueToInt32(cx, argv[0], &toskip)){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_NUMBER, "seek", argv[0]); - goto out; - } - - if(!file->hasRandomAccess){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_NO_RANDOM_ACCESS, file->path); - goto out; - } - - if(js_isDirectory(cx, file)){ - JS_ReportWarning(cx,"Seek on directories is not supported, proceeding"); - goto out; - } - - pos = js_FileSeek(cx, file, toskip, file->type); - - if (pos!=-1) { - *rval = INT_TO_JSVAL(pos); - return JS_TRUE; - } -out: - *rval = JSVAL_VOID; - return JS_FALSE; -} - -static JSBool -file_list(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - PRDir *dir; - PRDirEntry *entry; - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - JSObject *array; - JSObject *eachFile; - jsint len; - jsval v; - JSRegExp *re = NULL; - JSFunction *func = NULL; - JSString *str; - jsval args[1]; - char *filePath; - - SECURITY_CHECK(cx, NULL, "list", file); - JSFILE_CHECK_NATIVE("list"); - - if (argc==1) { - if (VALUE_IS_REGEXP(cx, argv[0])) { - re = JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[0])); - }else - if (VALUE_IS_FUNCTION(cx, argv[0])) { - func = JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[0])); - }else{ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_FUNCTION_OR_REGEX, argv[0]); - goto out; - } - } - - if (!js_isDirectory(cx, file)) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_CANNOT_DO_LIST_ON_A_FILE, file->path); - goto out; - } - - dir = PR_OpenDir(file->path); - if(!dir){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OP_FAILED, "open", file->path); - goto out; - } - - /* create JSArray here... */ - array = JS_NewArrayObject(cx, 0, NULL); - len = 0; - - while ((entry = PR_ReadDir(dir, PR_SKIP_BOTH))!=NULL) { - /* first, check if we have a regexp */ - if (re!=NULL) { - size_t index = 0; - - str = JS_NewStringCopyZ(cx, entry->name); - if(!js_ExecuteRegExp(cx, re, str, &index, JS_TRUE, &v)){ - /* don't report anything here */ - goto out; - } - /* not matched! */ - if (JSVAL_IS_NULL(v)) { - continue; - } - }else - if (func!=NULL) { - str = JS_NewStringCopyZ(cx, entry->name); - args[0] = STRING_TO_JSVAL(str); - if(!JS_CallFunction(cx, obj, func, 1, args, &v)){ - goto out; - } - - if (v==JSVAL_FALSE) { - continue; - } - } - - filePath = js_combinePath(cx, file->path, (char*)entry->name); - - eachFile = js_NewFileObject(cx, filePath); - cx->free(filePath); - if (!eachFile){ - JS_ReportWarning(cx, "File %s cannot be retrieved", filePath); - continue; - } - v = OBJECT_TO_JSVAL(eachFile); - JS_SetElement(cx, array, len, &v); - JS_SetProperty(cx, array, entry->name, &v); - len++; - } - - if(PR_CloseDir(dir)!=PR_SUCCESS){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OP_FAILED, "close", file->path); - goto out; - } - *rval = OBJECT_TO_JSVAL(array); - return JS_TRUE; -out: - *rval = JSVAL_NULL; - return JS_FALSE; -} - -static JSBool -file_mkdir(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - - SECURITY_CHECK(cx, NULL, "mkdir", file); - JSFILE_CHECK_ONE_ARG("mkdir"); - JSFILE_CHECK_NATIVE("mkdir"); - - /* if the current file is not a directory, find out the directory name */ - if (!js_isDirectory(cx, file)) { - char *dir = js_fileDirectoryName(cx, file->path); - JSObject *dirObj = js_NewFileObject(cx, dir); - - cx->free(dir); - - /* call file_mkdir with the right set of parameters if needed */ - if (file_mkdir(cx, dirObj, argc, argv, rval)) - return JS_TRUE; - else - goto out; - }else{ - JSString *str = JS_ValueToString(cx, argv[0]); - if (!str) - goto out; - - char *dirName = JS_GetStringBytes(str); - char *fullName; - - fullName = js_combinePath(cx, file->path, dirName); - if (PR_MkDir(fullName, 0755)==PR_SUCCESS){ - *rval = JSVAL_TRUE; - cx->free(fullName); - return JS_TRUE; - }else{ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OP_FAILED, "mkdir", fullName); - cx->free(fullName); - goto out; - } - } -out: - *rval = JSVAL_FALSE; - return JS_FALSE; -} - -static JSBool -file_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval*rval) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - JSString *str; - - str = JS_NewStringCopyZ(cx, file->path); - if (!str) - return JS_FALSE; - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -static JSBool -file_toURL(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - char url[MAX_PATH_LENGTH]; - jschar *urlChars; - size_t len; - JSString *str; - - JSFILE_CHECK_NATIVE("toURL"); - - sprintf(url, "file://%s", file->path); - - len = strlen(url); - urlChars = js_InflateString(cx, url, &len); - if (!urlChars) - return JS_FALSE; - str = js_NewString(cx, urlChars, len); - if (!str) { - cx->free(urlChars); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(str); - - /* TODO: js_escape in jsstr.h may go away at some point */ - return js_str_escape(cx, obj, 0, rval, rval); - -out: - *rval = JSVAL_VOID; - return JS_FALSE; -} - - -static void -file_finalize(JSContext *cx, JSObject *obj) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - - if(file) { - /* Close the file before exiting. */ - if(file->isOpen && !file->isNative) { - jsval vp; - file_close(cx, obj, 0, NULL, &vp); - } - - if (file->path) - cx->free(file->path); - - cx->free(file); - } -} - -/* - Allocates memory for the file object, sets fields to defaults. -*/ -static JSFile* -file_init(JSContext *cx, JSObject *obj, char *bytes) -{ - JSFile *file; - - file = cx->malloc(sizeof *file); - if (!file) - return NULL; - memset(file, 0 , sizeof *file); - - js_ResetAttributes(file); - - file->path = RESOLVE_PATH(cx, bytes); - - if (!JS_SetPrivate(cx, obj, file)) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_CANNOT_SET_PRIVATE_FILE, file->path); - cx->free(file); - return NULL; - } - - return file; -} - -/* Returns a JSObject. This function is globally visible */ -JS_PUBLIC_API(JSObject*) -js_NewFileObject(JSContext *cx, char *filename) -{ - JSObject *obj; - JSFile *file; - - obj = JS_NewObject(cx, &js_FileClass, NULL, NULL); - if (!obj){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OBJECT_CREATION_FAILED, "js_NewFileObject"); - return NULL; - } - file = file_init(cx, obj, filename); - if(!file) return NULL; - return obj; -} - -/* Internal function, used for cases which NSPR file support doesn't cover */ -JSObject* -js_NewFileObjectFromFILE(JSContext *cx, FILE *nativehandle, char *filename, - int32 mode, JSBool open, JSBool randomAccess) -{ - JSObject *obj; - JSFile *file; - - obj = JS_NewObject(cx, &js_FileClass, NULL, NULL); - if (!obj){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OBJECT_CREATION_FAILED, "js_NewFileObjectFromFILE"); - return NULL; - } - file = file_init(cx, obj, filename); - if(!file) return NULL; - - file->nativehandle = nativehandle; - - /* free result of RESOLVE_PATH from file_init. */ - JS_ASSERT(file->path != NULL); - cx->free(file->path); - - file->path = strdup(filename); - file->isOpen = open; - file->mode = mode; - file->hasRandomAccess = randomAccess; - file->isNative = JS_TRUE; - return obj; -} - -/* - Real file constructor that is called from JavaScript. - Basically, does error processing and calls file_init. -*/ -static JSBool -file_constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSString *str; - JSFile *file; - - if (!JS_IsConstructing(cx)) { - /* Replace obj with a new File object. */ - obj = JS_NewObject(cx, &js_FileClass, NULL, NULL); - if (!obj) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(obj); - } - - str = (argc == 0) - ? JS_InternString(cx, "") - : JS_ValueToString(cx, argv[0]); - - if (!str) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_FIRST_ARGUMENT_CONSTRUCTOR_NOT_STRING_ERROR, - argv[0]); - return JS_FALSE; - } - - file = file_init(cx, obj, JS_GetStringBytes(str)); - if (!file) - return JS_FALSE; - - SECURITY_CHECK(cx, NULL, "constructor", file); - - return JS_TRUE; -} - -/* -------------------- File methods and properties ------------------------- */ -static JSFunctionSpec file_functions[] = { - { "open", file_open, 0}, - { "close", file_close, 0}, - { "remove", file_remove, 0}, - { "copyTo", file_copyTo, 0}, - { "renameTo", file_renameTo, 0}, - { "flush", file_flush, 0}, - { "seek", file_seek, 0}, - { "read", file_read, 0}, - { "readln", file_readln, 0}, - { "readAll", file_readAll, 0}, - { "write", file_write, 0}, - { "writeln", file_writeln, 0}, - { "writeAll", file_writeAll, 0}, - { "list", file_list, 0}, - { "mkdir", file_mkdir, 0}, - { "toString", file_toString, 0}, - { "toURL", file_toURL, 0}, - {0} -}; - -enum file_tinyid { - FILE_LENGTH = -2, - FILE_PARENT = -3, - FILE_PATH = -4, - FILE_NAME = -5, - FILE_ISDIR = -6, - FILE_ISFILE = -7, - FILE_EXISTS = -8, - FILE_CANREAD = -9, - FILE_CANWRITE = -10, - FILE_OPEN = -11, - FILE_TYPE = -12, - FILE_MODE = -13, - FILE_CREATED = -14, - FILE_MODIFIED = -15, - FILE_SIZE = -16, - FILE_RANDOMACCESS = -17, - FILE_POSITION = -18, - FILE_APPEND = -19, - FILE_REPLACE = -20, - FILE_AUTOFLUSH = -21, - FILE_ISNATIVE = -22, -}; - -static JSPropertySpec file_props[] = { - {"length", FILE_LENGTH, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"parent", FILE_PARENT, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"path", FILE_PATH, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"name", FILE_NAME, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"isDirectory", FILE_ISDIR, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"isFile", FILE_ISFILE, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"exists", FILE_EXISTS, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"canRead", FILE_CANREAD, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"canWrite", FILE_CANWRITE, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"canAppend", FILE_APPEND, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"canReplace", FILE_REPLACE, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"isOpen", FILE_OPEN, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"type", FILE_TYPE, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"mode", FILE_MODE, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"creationTime", FILE_CREATED, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"lastModified", FILE_MODIFIED, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"size", FILE_SIZE, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"hasRandomAccess", FILE_RANDOMACCESS, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"hasAutoFlush", FILE_AUTOFLUSH, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"position", FILE_POSITION, JSPROP_ENUMERATE }, - {"isNative", FILE_ISNATIVE, JSPROP_ENUMERATE | JSPROP_READONLY }, - {0} -}; - -/* ------------------------- Property getter/setter ------------------------- */ -static JSBool -file_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - char *bytes; - JSString *str; - jsint tiny; - PRFileInfo info; - JSBool flag; - PRExplodedTime expandedTime; - - tiny = JSVAL_TO_INT(id); - if (!file) - return JS_TRUE; - - switch (tiny) { - case FILE_PARENT: - SECURITY_CHECK(cx, NULL, "parent", file); - if (!js_parent(cx, file, vp)) - return JS_FALSE; - break; - case FILE_PATH: - str = JS_NewStringCopyZ(cx, file->path); - if (!str) - return JS_FALSE; - *vp = STRING_TO_JSVAL(str); - break; - case FILE_NAME: - if (!js_name(cx, file, vp)) - return JS_FALSE; - break; - case FILE_ISDIR: - SECURITY_CHECK(cx, NULL, "isDirectory", file); - *vp = BOOLEAN_TO_JSVAL(js_isDirectory(cx, file)); - break; - case FILE_ISFILE: - SECURITY_CHECK(cx, NULL, "isFile", file); - *vp = BOOLEAN_TO_JSVAL(js_isFile(cx, file)); - break; - case FILE_EXISTS: - SECURITY_CHECK(cx, NULL, "exists", file); - *vp = BOOLEAN_TO_JSVAL(js_exists(cx, file)); - break; - case FILE_ISNATIVE: - SECURITY_CHECK(cx, NULL, "isNative", file); - *vp = BOOLEAN_TO_JSVAL(file->isNative); - break; - case FILE_CANREAD: - SECURITY_CHECK(cx, NULL, "canRead", file); - *vp = BOOLEAN_TO_JSVAL(js_canRead(cx, file)); - break; - case FILE_CANWRITE: - SECURITY_CHECK(cx, NULL, "canWrite", file); - *vp = BOOLEAN_TO_JSVAL(js_canWrite(cx, file)); - break; - case FILE_OPEN: - SECURITY_CHECK(cx, NULL, "isOpen", file); - *vp = BOOLEAN_TO_JSVAL(file->isOpen); - break; - case FILE_APPEND : - SECURITY_CHECK(cx, NULL, "canAppend", file); - JSFILE_CHECK_OPEN("canAppend"); - *vp = BOOLEAN_TO_JSVAL(!file->isNative && - (file->mode&PR_APPEND)==PR_APPEND); - break; - case FILE_REPLACE : - SECURITY_CHECK(cx, NULL, "canReplace", file); - JSFILE_CHECK_OPEN("canReplace"); - *vp = BOOLEAN_TO_JSVAL(!file->isNative && - (file->mode&PR_TRUNCATE)==PR_TRUNCATE); - break; - case FILE_AUTOFLUSH : - SECURITY_CHECK(cx, NULL, "hasAutoFlush", file); - JSFILE_CHECK_OPEN("hasAutoFlush"); - *vp = BOOLEAN_TO_JSVAL(!file->isNative && file->hasAutoflush); - break; - case FILE_TYPE: - SECURITY_CHECK(cx, NULL, "type", file); - JSFILE_CHECK_OPEN("type"); - if(js_isDirectory(cx, file)){ - *vp = JSVAL_VOID; - break; - } - - switch (file->type) { - case ASCII: - *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, asciistring)); - break; - case UTF8: - *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, utfstring)); - break; - case UCS2: - *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, unicodestring)); - break; - default: - JS_ReportWarning(cx, "Unsupported file type %d, proceeding", - file->type); - } - break; - case FILE_MODE: - SECURITY_CHECK(cx, NULL, "mode", file); - JSFILE_CHECK_OPEN("mode"); - bytes = cx->malloc(MODE_SIZE); - bytes[0] = '\0'; - flag = JS_FALSE; - - if ((file->mode&PR_RDONLY)==PR_RDONLY) { - if (flag) strcat(bytes, ","); - strcat(bytes, "read"); - flag = JS_TRUE; - } - if ((file->mode&PR_WRONLY)==PR_WRONLY) { - if (flag) strcat(bytes, ","); - strcat(bytes, "write"); - flag = JS_TRUE; - } - if ((file->mode&PR_RDWR)==PR_RDWR) { - if (flag) strcat(bytes, ","); - strcat(bytes, "readWrite"); - flag = JS_TRUE; - } - if ((file->mode&PR_APPEND)==PR_APPEND) { - if (flag) strcat(bytes, ","); - strcat(bytes, "append"); - flag = JS_TRUE; - } - if ((file->mode&PR_CREATE_FILE)==PR_CREATE_FILE) { - if (flag) strcat(bytes, ","); - strcat(bytes, "create"); - flag = JS_TRUE; - } - if ((file->mode&PR_TRUNCATE)==PR_TRUNCATE) { - if (flag) strcat(bytes, ","); - strcat(bytes, "replace"); - flag = JS_TRUE; - } - if (file->hasAutoflush) { - if (flag) strcat(bytes, ","); - strcat(bytes, "hasAutoFlush"); - flag = JS_TRUE; - } - *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, bytes)); - cx->free(bytes); - break; - case FILE_CREATED: - SECURITY_CHECK(cx, NULL, "creationTime", file); - JSFILE_CHECK_NATIVE("creationTime"); - if(((file->isOpen)? - PR_GetOpenFileInfo(file->handle, &info): - PR_GetFileInfo(file->path, &info))!=PR_SUCCESS){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path); - goto out; - } - - PR_ExplodeTime(info.creationTime, PR_LocalTimeParameters,&expandedTime); - *vp = OBJECT_TO_JSVAL(js_NewDateObject(cx, expandedTime.tm_year, - expandedTime.tm_month, - expandedTime.tm_mday, - expandedTime.tm_hour, - expandedTime.tm_min, - expandedTime.tm_sec)); - break; - case FILE_MODIFIED: - SECURITY_CHECK(cx, NULL, "lastModified", file); - JSFILE_CHECK_NATIVE("lastModified"); - if(((file->isOpen)? - PR_GetOpenFileInfo(file->handle, &info): - PR_GetFileInfo(file->path, &info))!=PR_SUCCESS){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path); - goto out; - } - - PR_ExplodeTime(info.modifyTime, PR_LocalTimeParameters, &expandedTime); - *vp = OBJECT_TO_JSVAL(js_NewDateObject(cx, expandedTime.tm_year, - expandedTime.tm_month, - expandedTime.tm_mday, - expandedTime.tm_hour, - expandedTime.tm_min, - expandedTime.tm_sec)); - break; - case FILE_SIZE: - SECURITY_CHECK(cx, NULL, "size", file); - *vp = js_size(cx, file); - break; - case FILE_LENGTH: - SECURITY_CHECK(cx, NULL, "length", file); - JSFILE_CHECK_NATIVE("length"); - - if (js_isDirectory(cx, file)) { /* XXX debug me */ - PRDir *dir; - PRDirEntry *entry; - jsint count = 0; - - if(!(dir = PR_OpenDir(file->path))){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_CANNOT_OPEN_DIR, file->path); - goto out; - } - - while ((entry = PR_ReadDir(dir, PR_SKIP_BOTH))) { - count++; - } - - if(!PR_CloseDir(dir)){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OP_FAILED, "close", file->path); - - goto out; - } - - *vp = INT_TO_JSVAL(count); - break; - }else{ - /* return file size */ - *vp = js_size(cx, file); - } - break; - case FILE_RANDOMACCESS: - SECURITY_CHECK(cx, NULL, "hasRandomAccess", file); - JSFILE_CHECK_OPEN("hasRandomAccess"); - *vp = BOOLEAN_TO_JSVAL(file->hasRandomAccess); - break; - case FILE_POSITION: - SECURITY_CHECK(cx, NULL, "position", file); - JSFILE_CHECK_NATIVE("position"); - JSFILE_CHECK_OPEN("position"); - - if(!file->hasRandomAccess){ - JS_ReportWarning(cx, "File %s doesn't support random access, can't report the position, proceeding"); - *vp = JSVAL_VOID; - break; - } - - if (file->isOpen && js_isFile(cx, file)) { - int pos = PR_Seek(file->handle, 0, PR_SEEK_CUR); - if(pos!=-1){ - *vp = INT_TO_JSVAL(pos); - }else{ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_CANNOT_REPORT_POSITION, file->path); - goto out; - } - }else { - JS_ReportWarning(cx, "File %s is closed or not a plain file," - " can't report position, proceeding"); - goto out; - } - break; - default: - SECURITY_CHECK(cx, NULL, "file_access", file); - - /* this is some other property -- try to use the dir["file"] syntax */ - if (js_isDirectory(cx, file)) { - PRDir *dir = NULL; - PRDirEntry *entry = NULL; - char *prop_name; - - str = JS_ValueToString(cx, id); - if (!str) - return JS_FALSE; - - prop_name = JS_GetStringBytes(str); - - /* no native files past this point */ - dir = PR_OpenDir(file->path); - if(!dir) { - /* This is probably not a directory */ - JS_ReportWarning(cx, "Can't open directory %s", file->path); - return JS_FALSE; - } - - while ((entry = PR_ReadDir(dir, PR_SKIP_NONE)) != NULL) { - if (!strcmp(entry->name, prop_name)){ - bytes = js_combinePath(cx, file->path, prop_name); - *vp = OBJECT_TO_JSVAL(js_NewFileObject(cx, bytes)); - PR_CloseDir(dir); - cx->free(bytes); - return !JSVAL_IS_NULL(*vp); - } - } - PR_CloseDir(dir); - } - } - return JS_TRUE; - -out: - return JS_FALSE; -} - -static JSBool -file_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - jsint slot; - - if (JSVAL_IS_STRING(id)){ - return JS_TRUE; - } - - slot = JSVAL_TO_INT(id); - - switch (slot) { - /* File.position = 10 */ - case FILE_POSITION: - SECURITY_CHECK(cx, NULL, "set_position", file); - JSFILE_CHECK_NATIVE("set_position"); - - if(!file->hasRandomAccess){ - JS_ReportWarning(cx, "File %s doesn't support random access, can't " - "report the position, proceeding"); - goto out; - } - - if (file->isOpen && js_isFile(cx, file)) { - int32 pos; - int32 offset; - - if (!JS_ValueToInt32(cx, *vp, &offset)){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_NUMBER, "position", *vp); - goto out; - } - - pos = PR_Seek(file->handle, offset, PR_SEEK_SET); - - if(pos!=-1){ - *vp = INT_TO_JSVAL(pos); - }else{ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_CANNOT_SET_POSITION, file->path); - goto out; - } - } else { - JS_ReportWarning(cx, "File %s is closed or not a file, can't set " - "position, proceeding", file->path); - goto out; - } - } - - return JS_TRUE; -out: - return JS_FALSE; -} - -/* - File.currentDir = new File("D:\") or File.currentDir = "D:\" -*/ -static JSBool -file_currentDirSetter(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSFile *file; - - file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - - /* Look at the rhs and extract a file object from it */ - if (JSVAL_IS_OBJECT(*vp)) { - if (JS_InstanceOf(cx, obj, &js_FileClass, NULL)) { - /* Braindamaged rhs -- just return the old value */ - if (file && (!js_exists(cx, file) || !js_isDirectory(cx, file))) { - JS_GetProperty(cx, obj, CURRENTDIR_PROPERTY, vp); - return JS_FALSE; - } else { - chdir(file->path); - return JS_TRUE; - } - } else { - return JS_FALSE; - } - } else { - JSObject *rhsObject; - char *path; - - JSString *str = JS_ValueToString(cx, *vp); - if (!str) - return JS_FALSE; - - path = JS_GetStringBytes(str); - rhsObject = js_NewFileObject(cx, path); - if (!rhsObject) - return JS_FALSE; - - if (!file || !js_exists(cx, file) || !js_isDirectory(cx, file)){ - JS_GetProperty(cx, obj, CURRENTDIR_PROPERTY, vp); - } else { - *vp = OBJECT_TO_JSVAL(rhsObject); - chdir(path); - } - } - - return JS_TRUE; -} - -/* Declare class */ -JSClass js_FileClass = { - "File", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_File), - JS_PropertyStub, JS_PropertyStub, file_getProperty, file_setProperty, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, file_finalize -}; - -/* -------------------- Functions exposed to the outside -------------------- */ -JS_PUBLIC_API(JSObject*) -js_InitFileClass(JSContext *cx, JSObject* obj) -{ - JSObject *file, *ctor, *afile; - jsval vp; - char *currentdir; - char separator[2]; - - file = JS_InitClass(cx, obj, NULL, &js_FileClass, file_constructor, 1, - file_props, file_functions, NULL, NULL); - if (!file) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_INIT_FAILED); - return NULL; - } - - ctor = JS_GetConstructor(cx, file); - if (!ctor) return NULL; - - /* Define CURRENTDIR property. We are doing this to get a - slash at the end of the current dir */ - afile = js_NewFileObject(cx, CURRENT_DIR); - currentdir = cx->malloc(MAX_PATH_LENGTH); - currentdir = getcwd(currentdir, MAX_PATH_LENGTH); - afile = js_NewFileObject(cx, currentdir); - cx->free(currentdir); - vp = OBJECT_TO_JSVAL(afile); - JS_DefinePropertyWithTinyId(cx, ctor, CURRENTDIR_PROPERTY, 0, vp, - JS_PropertyStub, file_currentDirSetter, - JSPROP_ENUMERATE | JSPROP_READONLY ); - - /* Define input */ - vp = OBJECT_TO_JSVAL(js_NewFileObjectFromFILE(cx, stdin, - STDINPUT_NAME, PR_RDONLY, JS_TRUE, JS_FALSE)); - JS_SetProperty(cx, ctor, "input", &vp); - - /* Define output */ - vp = OBJECT_TO_JSVAL(js_NewFileObjectFromFILE(cx, stdout, - STDOUTPUT_NAME, PR_WRONLY, JS_TRUE, JS_FALSE)); - JS_SetProperty(cx, ctor, "output", &vp); - - /* Define error */ - vp = OBJECT_TO_JSVAL(js_NewFileObjectFromFILE(cx, stderr, - STDERROR_NAME, PR_WRONLY, JS_TRUE, JS_FALSE)); - JS_SetProperty(cx, ctor, "error", &vp); - - separator[0] = FILESEPARATOR; - separator[1] = '\0'; - vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, separator)); - JS_DefinePropertyWithTinyId(cx, ctor, SEPARATOR_PROPERTY, 0, vp, - JS_PropertyStub, JS_PropertyStub, - JSPROP_ENUMERATE | JSPROP_READONLY ); - return file; -} -#endif /* JS_HAS_FILE_OBJECT */ diff --git a/js/src/jsfile.h b/js/src/jsfile.h deleted file mode 100644 index 78707e8b72d3..000000000000 --- a/js/src/jsfile.h +++ /dev/null @@ -1,56 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** 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 ***** */ - -#ifndef _jsfile_h__ -#define _jsfile_h__ - -#if JS_HAS_FILE_OBJECT - -#include "jsobj.h" - -extern JS_PUBLIC_API(JSObject*) -js_InitFileClass(JSContext *cx, JSObject* obj); - -extern JS_PUBLIC_API(JSObject*) -js_NewFileObject(JSContext *cx, char *bytes); - -extern JSClass js_FileClass; - -#endif /* JS_HAS_FILE_OBJECT */ -#endif /* _jsfile_h__ */ diff --git a/js/src/jsfile.msg b/js/src/jsfile.msg deleted file mode 100644 index 137b35d87405..000000000000 --- a/js/src/jsfile.msg +++ /dev/null @@ -1,90 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** 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 ***** */ - -/* - Error messages for jsfile.c. See js.msg for format specification. -*/ - -MSG_DEF(JSFILEMSG_NOT_AN_ERROR, 0, 0, JSEXN_NONE, "") -MSG_DEF(JSFILEMSG_FILE_CONSTRUCTOR_UNDEFINED_ERROR, 1, 0, JSEXN_NONE, "File constructor is undefined") -MSG_DEF(JSFILEMSG_FILE_CURRENTDIR_UNDEFINED_ERROR, 2, 0, JSEXN_NONE, "File.currentDir is undefined") -MSG_DEF(JSFILEMSG_FIRST_ARGUMENT_OPEN_NOT_STRING_ERROR, 3, 1, JSEXN_NONE, "The first argument {0} to file.open must be a string") -MSG_DEF(JSFILEMSG_SECOND_ARGUMENT_OPEN_NOT_STRING_ERROR, 4, 0, JSEXN_NONE, "The second argument to file.open must be a string") -MSG_DEF(JSFILEMSG_CANNOT_COPY_FILE_OPEN_FOR_WRITING_ERROR, 5, 1, JSEXN_NONE, "Cannot copy file {0} open for writing") -MSG_DEF(JSFILEMSG_CANNOT_ACCESS_FILE_INFO_ERROR, 6, 1, JSEXN_NONE, "Cannot access file information for {0}") -MSG_DEF(JSFILEMSG_COPY_READ_ERROR, 7, 1, JSEXN_NONE, "An error occured while attempting to read a file {0} to copy") -MSG_DEF(JSFILEMSG_COPY_WRITE_ERROR, 8, 1, JSEXN_NONE, "An error occured while attempting to copy into file {0}") -MSG_DEF(JSFILEMSG_EXPECTS_ONE_ARG_ERROR, 9, 0, JSEXN_NONE, "Operation {0} expects one argument, not {1}") -MSG_DEF(JSFILEMSG_CANNOT_FLUSH_CLOSE_FILE_ERROR, 10, 1, JSEXN_NONE, "Cannot flush closed file {0}") -MSG_DEF(JSFILEMSG_CANNOT_OPEN_WRITING_ERROR, 11, 1, JSEXN_NONE, "Cannot open file {0} for writing") -MSG_DEF(JSFILEMSG_WRITEALL_EXPECTS_ONE_ARG_ERROR, 12, 0, JSEXN_NONE, "writeAll expects one argument") -MSG_DEF(JSFILEMSG_FIRST_ARGUMENT_WRITEALL_NOT_ARRAY_ERROR, 13, 0, JSEXN_NONE, "writeAll expects an array as an argument") -MSG_DEF(JSFILEMSG_UNUSED0, 14, 0, JSEXN_NONE, "Unused error message slot") -MSG_DEF(JSFILEMSG_CANNOT_OPEN_FILE_ERROR, 15, 1, JSEXN_NONE, "Cannot open file {0}") -MSG_DEF(JSFILEMSG_FIRST_ARGUMENT_CONSTRUCTOR_NOT_STRING_ERROR, 16, 1, JSEXN_NONE, "The argument to the File constructor {0} must be a string") -MSG_DEF(JSFILEMSG_BIDIRECTIONAL_PIPE_NOT_SUPPORTED, 17, 0, JSEXN_NONE, "Bidirectional pipes are not supported") -MSG_DEF(JSFILEMSG_OPEN_MODE_NOT_SUPPORTED_WITH_PIPES, 18, 2, JSEXN_NONE, "The opening mode you have chosen {0} is not supported by the pipe you are trying to open: {1}") -MSG_DEF(JSFILEMSG_OPEN_FAILED, 19, 1, JSEXN_NONE, "open on file {0} failed") -MSG_DEF(JSFILEMSG_CLOSE_FAILED, 20, 1, JSEXN_NONE, "close on file {0} failed") -MSG_DEF(JSFILEMSG_PCLOSE_FAILED, 21, 1, JSEXN_NONE, "pclose on file {0} failed") -MSG_DEF(JSFILEMSG_REMOVE_FAILED, 22, 1, JSEXN_NONE, "remove on file {0} failed") -MSG_DEF(JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, 23, 1, JSEXN_NONE, "Cannot access file status for {0}") -MSG_DEF(JSFILEMSG_RENAME_FAILED, 24, 2, JSEXN_NONE, "Cannot rename {0} to {1}") -MSG_DEF(JSFILEMSG_WRITE_FAILED, 25, 1, JSEXN_NONE, "Write failed on file {0}") -MSG_DEF(JSFILEMSG_READ_FAILED, 26, 1, JSEXN_NONE, "Read failed on file {0}") -MSG_DEF(JSFILEMSG_SKIP_FAILED, 27, 1, JSEXN_NONE, "Skip failed on file {0}") -MSG_DEF(JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_FUNCTION_OR_REGEX, 28, 1, JSEXN_NONE, "The first argument to file.list must be a function or a regex") -MSG_DEF(JSFILEMSG_CANNOT_DO_LIST_ON_A_FILE, 29, 1, JSEXN_NONE, "{0} must be a directory, cannot do list") -MSG_DEF(JSFILEMSG_NATIVE_OPERATION_IS_NOT_SUPPORTED, 30, 2, JSEXN_NONE, "Native operation {0} is not supported on {1}") -MSG_DEF(JSFILEMSG_CANNOT_SET_PRIVATE_FILE, 31, 1, JSEXN_NONE, "Cannot set private data for file {0}") -MSG_DEF(JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_NUMBER, 32, 2, JSEXN_NONE, "First argument to {0} must be a number, not {1}") -MSG_DEF(JSFILEMSG_CANNOT_WRITE, 33, 1, JSEXN_NONE, "Cannot write to {0}, file mode is different") -MSG_DEF(JSFILEMSG_CANNOT_READ, 34, 1, JSEXN_NONE, "Cannot read from {0}, file mode is different") -MSG_DEF(JSFILEMSG_CANNOT_FLUSH, 35, 1, JSEXN_NONE, "Flush failed on {0}") -MSG_DEF(JSFILEMSG_OP_FAILED, 36, 1, JSEXN_NONE, "File operation {0} failed") -MSG_DEF(JSFILEMSG_FILE_MUST_BE_OPEN, 37, 1, JSEXN_NONE, "File must be open for {0}") -MSG_DEF(JSFILEMSG_FILE_MUST_BE_CLOSED, 38, 1, JSEXN_NONE, "File must be closed for {0}") -MSG_DEF(JSFILEMSG_NO_RANDOM_ACCESS, 39, 1, JSEXN_NONE, "File {0} doesn't allow random access") -MSG_DEF(JSFILEMSG_OBJECT_CREATION_FAILED, 40, 1, JSEXN_NONE, "Couldn't create {0}") -MSG_DEF(JSFILEMSG_CANNOT_OPEN_DIR, 41, 1, JSEXN_NONE, "Couldn't open directory {0}") -MSG_DEF(JSFILEMSG_CANNOT_REPORT_POSITION, 42, 1, JSEXN_NONE, "Couldn't report position for {0}") -MSG_DEF(JSFILEMSG_CANNOT_SET_POSITION, 43, 1, JSEXN_NONE, "Couldn't set position for {0}") -MSG_DEF(JSFILEMSG_INIT_FAILED, 44, 0, JSEXN_NONE, "File class initialization failed") - -