Exemple #1
0
/*
 * Compile a new |RegExpShared| for the |RegExpObject|.
 *
 * Per ECMAv5 15.10.4.1, we act on combinations of (pattern, flags) as
 * arguments:
 *
 *  RegExp, undefined => flags := pattern.flags
 *  RegExp, _ => throw TypeError
 *  _ => pattern := ToString(pattern) if defined(pattern) else ''
 *       flags := ToString(flags) if defined(flags) else ''
 */
static bool
CompileRegExpObject(JSContext *cx, RegExpObjectBuilder &builder, CallArgs args)
{
    if (args.length() == 0) {
        RegExpStatics *res = cx->regExpStatics();
        Rooted<JSAtom*> empty(cx, cx->runtime->emptyString);
        RegExpObject *reobj = builder.build(empty, res->getFlags());
        if (!reobj)
            return false;
        args.rval().setObject(*reobj);
        return true;
    }

    Value sourceValue = args[0];

    /*
     * If we get passed in an object whose internal [[Class]] property is
     * "RegExp", return a new object with the same source/flags.
     */
    if (IsObjectWithClass(sourceValue, ESClass_RegExp, cx)) {
        /*
         * Beware, sourceObj may be a (transparent) proxy to a RegExp, so only
         * use generic (proxyable) operations on sourceObj that do not assume
         * sourceObj.isRegExp().
         */
        JSObject &sourceObj = sourceValue.toObject();

        if (args.hasDefined(1)) {
            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NEWREGEXP_FLAGGED);
            return false;
        }

        /*
         * Only extract the 'flags' out of sourceObj; do not reuse the
         * RegExpShared since it may be from a different compartment.
         */
        RegExpFlag flags;
        {
            RegExpGuard g;
            if (!RegExpToShared(cx, sourceObj, &g))
                return false;

            flags = g->getFlags();
        }

        /*
         * 'toSource' is a permanent read-only property, so this is equivalent
         * to executing RegExpObject::getSource on the unwrapped object.
         */
        RootedValue v(cx);
        if (!sourceObj.getProperty(cx, cx->runtime->atomState.sourceAtom, &v))
            return false;

        Rooted<JSAtom*> sourceAtom(cx, &v.toString()->asAtom());
        RegExpObject *reobj = builder.build(sourceAtom, flags);
        if (!reobj)
            return false;

        args.rval().setObject(*reobj);
        return true;
    }

    RootedAtom source(cx);
    if (sourceValue.isUndefined()) {
        source = cx->runtime->emptyString;
    } else {
        /* Coerce to string and compile. */
        JSString *str = ToString(cx, sourceValue);
        if (!str)
            return false;

        source = js_AtomizeString(cx, str);
        if (!source)
            return false;
    }

    RegExpFlag flags = RegExpFlag(0);
    if (args.hasDefined(1)) {
        JSString *flagStr = ToString(cx, args[1]);
        if (!flagStr)
            return false;
        args[1].setString(flagStr);
        if (!ParseRegExpFlags(cx, flagStr, &flags))
            return false;
    }

    RootedAtom escapedSourceStr(cx, EscapeNakedForwardSlashes(cx, source));
    if (!escapedSourceStr)
        return false;

    if (!js::detail::RegExpCode::checkSyntax(cx, NULL, escapedSourceStr))
        return false;

    RegExpStatics *res = cx->regExpStatics();
    RegExpObject *reobj = builder.build(escapedSourceStr, RegExpFlag(flags | res->getFlags()));
    if (!reobj)
        return false;

    args.rval().setObject(*reobj);
    return true;
}
Exemple #2
0
/*
 * Determines whether a value which has passed by ES5 150.2.3 Str steps 1-4's
 * gauntlet will result in Str returning |undefined|.  This function is used to
 * properly omit properties resulting in such values when stringifying objects,
 * while properly stringifying such properties as null when they're encountered
 * in arrays.
 */
static inline bool
IsFilteredValue(const Value &v)
{
    return v.isUndefined() || js_IsCallable(v) || VALUE_IS_XML(v);
}
Exemple #3
0
bool
js::SetElement(JSContext *cx, ObjectImpl *obj, ObjectImpl *receiver, uint32_t index,
               const Value &v, bool *succeeded)
{
    NEW_OBJECT_REPRESENTATION_ONLY();

    do {
        MOZ_ASSERT(obj);

        if (static_cast<JSObject *>(obj)->isProxy()) { // XXX
            MOZ_NOT_REACHED("NYI: proxy [[SetP]]");
            return false;
        }

        PropDesc ownDesc;
        if (!GetOwnElement(cx, obj, index, &ownDesc))
            return false;

        if (!ownDesc.isUndefined()) {
            if (ownDesc.isDataDescriptor()) {
                if (!ownDesc.writable()) {
                    *succeeded = false;
                    return true;
                }

                if (receiver == obj) {
                    PropDesc updateDesc = PropDesc::valueOnly(v);
                    return DefineElement(cx, receiver, index, updateDesc, false, succeeded);
                }

                PropDesc newDesc;
                return DefineElement(cx, receiver, index, newDesc, false, succeeded);
            }

            if (ownDesc.isAccessorDescriptor()) {
                Value setter = ownDesc.setterValue();
                if (setter.isUndefined()) {
                    *succeeded = false;
                    return true;
                }

                InvokeArgsGuard args;
                if (!cx->stack.pushInvokeArgs(cx, 1, &args))
                    return false;

                /* Push set, receiver, and v as the sole argument. */
                args.calleev() = setter;
                args.thisv() = ObjectValue(*receiver);
                args[0] = v;

                *succeeded = true;
                return Invoke(cx, args);
            }

            MOZ_NOT_REACHED("NYI: setting PropertyOp-based property");
            return false;
        }

        obj = obj->getProto();
        if (obj)
            continue;

        PropDesc newDesc(v, PropDesc::Writable, PropDesc::Enumerable, PropDesc::Configurable);
        return DefineElement(cx, receiver, index, newDesc, false, succeeded);
    } while (false);

    MOZ_NOT_REACHED("buggy control flow");
    return false;
}
static JSBool
CountHeap(JSContext *cx, unsigned argc, jsval *vp)
{
    jsval v;
    int32_t traceKind;
    JSString *str;
    JSCountHeapTracer countTracer;
    JSCountHeapNode *node;
    size_t counter;

    Value startValue = UndefinedValue();
    if (argc > 0) {
        v = JS_ARGV(cx, vp)[0];
        if (JSVAL_IS_TRACEABLE(v)) {
            startValue = v;
        } else if (!JSVAL_IS_NULL(v)) {
            JS_ReportError(cx,
                           "the first argument is not null or a heap-allocated "
                           "thing");
            return JS_FALSE;
        }
    }

    traceKind = -1;
    if (argc > 1) {
        str = JS_ValueToString(cx, JS_ARGV(cx, vp)[1]);
        if (!str)
            return JS_FALSE;
        JSFlatString *flatStr = JS_FlattenString(cx, str);
        if (!flatStr)
            return JS_FALSE;
        for (size_t i = 0; ;) {
            if (JS_FlatStringEqualsAscii(flatStr, traceKindNames[i].name)) {
                traceKind = traceKindNames[i].kind;
                break;
            }
            if (++i == ArrayLength(traceKindNames)) {
                JSAutoByteString bytes(cx, str);
                if (!!bytes)
                    JS_ReportError(cx, "trace kind name '%s' is unknown", bytes.ptr());
                return JS_FALSE;
            }
        }
    }

    JS_TracerInit(&countTracer.base, JS_GetRuntime(cx), CountHeapNotify);
    if (!countTracer.visited.init()) {
        JS_ReportOutOfMemory(cx);
        return JS_FALSE;
    }
    countTracer.ok = true;
    countTracer.traceList = NULL;
    countTracer.recycleList = NULL;

    if (startValue.isUndefined()) {
        JS_TraceRuntime(&countTracer.base);
    } else {
        JS_CallValueTracer(&countTracer.base, startValue, "root");
    }

    counter = 0;
    while ((node = countTracer.traceList) != NULL) {
        if (traceKind == -1 || node->kind == traceKind)
            counter++;
        countTracer.traceList = node->next;
        node->next = countTracer.recycleList;
        countTracer.recycleList = node;
        JS_TraceChildren(&countTracer.base, node->thing, node->kind);
    }
    while ((node = countTracer.recycleList) != NULL) {
        countTracer.recycleList = node->next;
        js_free(node);
    }
    if (!countTracer.ok) {
        JS_ReportOutOfMemory(cx);
        return false;
    }

    *vp = JS_NumberValue((double) counter);
    return true;
}
Exemple #5
0
/*
 * Compile a new |RegExpPrivate| for the |RegExpObject|.
 *
 * Per ECMAv5 15.10.4.1, we act on combinations of (pattern, flags) as
 * arguments:
 *
 *  RegExp, undefined => flags := pattern.flags
 *  RegExp, _ => throw TypeError
 *  _ => pattern := ToString(pattern) if defined(pattern) else ''
 *       flags := ToString(flags) if defined(flags) else ''
 */
static bool
CompileRegExpObject(JSContext *cx, RegExpObject *obj, uintN argc, Value *argv, Value *rval)
{
    if (argc == 0) {
        if (!ResetRegExpObjectWithStatics(cx, obj, cx->runtime->emptyString))
            return false;
        *rval = ObjectValue(*obj);
        return true;
    }

    Value sourceValue = argv[0];
    if (ValueIsRegExp(sourceValue)) {
        /*
         * If we get passed in a |RegExpObject| source we return a new
         * object with the same |RegExpPrivate|.
         *
         * Note: the regexp static flags are not taken into consideration here.
         */
        JSObject &sourceObj = sourceValue.toObject();
        if (argc >= 2 && !argv[1].isUndefined()) {
            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NEWREGEXP_FLAGGED);
            return false;
        }

        RegExpPrivate *rep = sourceObj.asRegExp()->getPrivate();
        if (!rep)
            return false;

        rep->incref(cx);
        if (!ResetRegExpObject(cx, obj, AlreadyIncRefed<RegExpPrivate>(rep)))
            return false;
        *rval = ObjectValue(*obj);
        return true;
    }

    JSString *sourceStr;
    if (sourceValue.isUndefined()) {
        sourceStr = cx->runtime->emptyString;
    } else {
        /* Coerce to string and compile. */
        sourceStr = js_ValueToString(cx, sourceValue);
        if (!sourceStr)
            return false;
    }

    RegExpFlag flags = RegExpFlag(0);
    if (argc > 1 && !argv[1].isUndefined()) {
        JSString *flagStr = js_ValueToString(cx, argv[1]);
        if (!flagStr)
            return false;
        argv[1].setString(flagStr);
        if (!ParseRegExpFlags(cx, flagStr, &flags))
            return false;
    }

    JSString *escapedSourceStr = EscapeNakedForwardSlashes(cx, sourceStr);
    if (!escapedSourceStr)
        return false;

    if (!ResetRegExpObjectWithStatics(cx, obj, escapedSourceStr, flags))
        return false;
    *rval = ObjectValue(*obj);
    return true;
}
Exemple #6
0
bool
js::GetElement(JSContext *cx, ObjectImpl *obj, ObjectImpl *receiver, uint32_t index,
               Value *vp)
{
    NEW_OBJECT_REPRESENTATION_ONLY();

    do {
        MOZ_ASSERT(obj);

        if (static_cast<JSObject *>(obj)->isProxy()) { // XXX
            MOZ_NOT_REACHED("NYI: proxy [[GetP]]");
            return false;
        }

        PropDesc desc;
        if (!GetOwnElement(cx, obj, index, &desc))
            return false;

        /* No property?  Recur or bottom out. */
        if (desc.isUndefined()) {
            obj = obj->getProto();
            if (obj)
                continue;

            vp->setUndefined();
            return true;
        }

        /* If it's a data property, return the value. */
        if (desc.isDataDescriptor()) {
            *vp = desc.value();
            return true;
        }

        /* If it's an accessor property, call its [[Get]] with the receiver. */
        if (desc.isAccessorDescriptor()) {
            Value get = desc.getterValue();
            if (get.isUndefined()) {
                vp->setUndefined();
                return true;
            }

            InvokeArgsGuard args;
            if (!cx->stack.pushInvokeArgs(cx, 0, &args))
                return false;

            /* Push get, receiver, and no args. */
            args.calleev() = get;
            args.thisv() = ObjectValue(*receiver);

            bool ok = Invoke(cx, args);
            *vp = args.rval();
            return ok;
        }

        /* Otherwise it's a PropertyOp-based property.  XXX handle this! */
        MOZ_NOT_REACHED("NYI: handle PropertyOp'd properties here");
        return false;
    } while (false);

    MOZ_NOT_REACHED("buggy control flow");
    return false;
}
// Common code implementing direct and indirect eval.
//
// Evaluate call.argv[2], if it is a string, in the context of the given calling
// frame, with the provided scope chain, with the semantics of either a direct
// or indirect eval (see ES5 10.4.2).  If this is an indirect eval, scopeobj
// must be a global object.
//
// On success, store the completion value in call.rval and return true.
static bool
EvalKernel(JSContext *cx, const CallArgs &args, EvalType evalType, StackFrame *caller,
           HandleObject scopeobj)
{
    JS_ASSERT((evalType == INDIRECT_EVAL) == (caller == NULL));
    JS_ASSERT_IF(evalType == INDIRECT_EVAL, scopeobj->isGlobal());
    AssertInnerizedScopeChain(cx, *scopeobj);

    if (!scopeobj->global().isRuntimeCodeGenEnabled(cx)) {
        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CSP_BLOCKED_EVAL);
        return false;
    }

    // ES5 15.1.2.1 step 1.
    if (args.length() < 1) {
        args.rval().setUndefined();
        return true;
    }
    if (!args[0].isString()) {
        args.rval() = args[0];
        return true;
    }
    JSString *str = args[0].toString();

    // ES5 15.1.2.1 steps 2-8.

    // Per ES5, indirect eval runs in the global scope. (eval is specified this
    // way so that the compiler can make assumptions about what bindings may or
    // may not exist in the current frame if it doesn't see 'eval'.)
    unsigned staticLevel;
    RootedValue thisv(cx);
    if (evalType == DIRECT_EVAL) {
        staticLevel = caller->script()->staticLevel + 1;

        // Direct calls to eval are supposed to see the caller's |this|. If we
        // haven't wrapped that yet, do so now, before we make a copy of it for
        // the eval code to use.
        if (!ComputeThis(cx, caller))
            return false;
        thisv = caller->thisValue();
    } else {
        JS_ASSERT(args.callee().global() == *scopeobj);
        staticLevel = 0;

        // Use the global as 'this', modulo outerization.
        JSObject *thisobj = scopeobj->thisObject(cx);
        if (!thisobj)
            return false;
        thisv = ObjectValue(*thisobj);
    }

    Rooted<JSLinearString*> linearStr(cx, str->ensureLinear(cx));
    if (!linearStr)
        return false;
    const jschar *chars = linearStr->chars();
    size_t length = linearStr->length();

    SkipRoot skip(cx, &chars);

    // If the eval string starts with '(' or '[' and ends with ')' or ']', it may be JSON.
    // Try the JSON parser first because it's much faster.  If the eval string
    // isn't JSON, JSON parsing will probably fail quickly, so little time
    // will be lost.
    //
    // Don't use the JSON parser if the caller is strict mode code, because in
    // strict mode object literals must not have repeated properties, and the
    // JSON parser cheerfully (and correctly) accepts them.  If you're parsing
    // JSON with eval and using strict mode, you deserve to be slow.
    if (length > 2 &&
        ((chars[0] == '[' && chars[length - 1] == ']') ||
        (chars[0] == '(' && chars[length - 1] == ')')) &&
         (!caller || !caller->script()->strictModeCode))
    {
        // Remarkably, JavaScript syntax is not a superset of JSON syntax:
        // strings in JavaScript cannot contain the Unicode line and paragraph
        // terminator characters U+2028 and U+2029, but strings in JSON can.
        // Rather than force the JSON parser to handle this quirk when used by
        // eval, we simply don't use the JSON parser when either character
        // appears in the provided string.  See bug 657367.
        for (const jschar *cp = &chars[1], *end = &chars[length - 2]; ; cp++) {
            if (*cp == 0x2028 || *cp == 0x2029)
                break;

            if (cp == end) {
                bool isArray = (chars[0] == '[');
                JSONParser parser(cx, isArray ? chars : chars + 1, isArray ? length : length - 2,
                                  JSONParser::StrictJSON, JSONParser::NoError);
                Value tmp;
                if (!parser.parse(&tmp))
                    return false;
                if (tmp.isUndefined())
                    break;
                args.rval() = tmp;
                return true;
            }
        }
    }

    EvalScriptGuard esg(cx);

    JSPrincipals *principals = PrincipalsForCompiledCode(args, cx);

    if (evalType == DIRECT_EVAL && caller->isNonEvalFunctionFrame())
        esg.lookupInEvalCache(linearStr, caller->fun(), staticLevel);

    if (!esg.foundScript()) {
        unsigned lineno;
        const char *filename;
        JSPrincipals *originPrincipals;
        CurrentScriptFileLineOrigin(cx, &filename, &lineno, &originPrincipals,
                                    evalType == DIRECT_EVAL ? CALLED_FROM_JSOP_EVAL
                                                            : NOT_CALLED_FROM_JSOP_EVAL);

        CompileOptions options(cx);
        options.setFileAndLine(filename, lineno)
               .setCompileAndGo(true)
               .setNoScriptRval(false)
               .setPrincipals(principals)
               .setOriginPrincipals(originPrincipals);
        JSScript *compiled = frontend::CompileScript(cx, scopeobj, caller, options,
                                                     chars, length, linearStr,
                                                     staticLevel);
        if (!compiled)
            return false;

        esg.setNewScript(compiled);
    }

    return ExecuteKernel(cx, esg.script(), *scopeobj, thisv, ExecuteType(evalType),
                         NULL /* evalInFrame */, &args.rval());
}
Exemple #8
0
/*
 * Compile a new |RegExpPrivate| for the |RegExpObject|.
 *
 * Per ECMAv5 15.10.4.1, we act on combinations of (pattern, flags) as
 * arguments:
 *
 *  RegExp, undefined => flags := pattern.flags
 *  RegExp, _ => throw TypeError
 *  _ => pattern := ToString(pattern) if defined(pattern) else ''
 *       flags := ToString(flags) if defined(flags) else ''
 */
static bool
CompileRegExpObject(JSContext *cx, RegExpObjectBuilder &builder,
                    uintN argc, Value *argv, Value *rval)
{
    if (argc == 0) {
        RegExpStatics *res = cx->regExpStatics();
        RegExpObject *reobj = builder.build(cx->runtime->emptyString, res->getFlags());
        if (!reobj)
            return false;
        *rval = ObjectValue(*reobj);
        return true;
    }

    Value sourceValue = argv[0];
    if (ValueIsRegExp(sourceValue)) {
        /*
         * If we get passed in a |RegExpObject| source we return a new
         * object with the same source/flags.
         *
         * Note: the regexp static flags are not taken into consideration here.
         */
        JSObject &sourceObj = sourceValue.toObject();
        if (argc >= 2 && !argv[1].isUndefined()) {
            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NEWREGEXP_FLAGGED);
            return false;
        }

        RegExpObject *reobj = builder.build(sourceObj.asRegExp());
        if (!reobj)
            return false;
        *rval = ObjectValue(*reobj);
        return true;
    }

    JSLinearString *sourceStr;
    if (sourceValue.isUndefined()) {
        sourceStr = cx->runtime->emptyString;
    } else {
        /* Coerce to string and compile. */
        JSString *str = ToString(cx, sourceValue);
        if (!str)
            return false;
        sourceStr = str->ensureLinear(cx);
        if (!sourceStr)
            return false;
    }

    RegExpFlag flags = RegExpFlag(0);
    if (argc > 1 && !argv[1].isUndefined()) {
        JSString *flagStr = ToString(cx, argv[1]);
        if (!flagStr)
            return false;
        argv[1].setString(flagStr);
        if (!ParseRegExpFlags(cx, flagStr, &flags))
            return false;
    }

    JSLinearString *escapedSourceStr = EscapeNakedForwardSlashes(cx, sourceStr);
    if (!escapedSourceStr)
        return false;

    if (!CheckRegExpSyntax(cx, escapedSourceStr))
        return false;

    RegExpStatics *res = cx->regExpStatics();
    RegExpObject *reobj = builder.build(escapedSourceStr, RegExpFlag(flags | res->getFlags()));
    if (!reobj)
        return NULL;

    *rval = ObjectValue(*reobj);
    return true;
}
static char *
FormatFrame(JSContext *cx, const ScriptFrameIter &iter, char *buf, int num,
            JSBool showArgs, JSBool showLocals, JSBool showThisProps)
{
    JSScript* script = iter.script();
    jsbytecode* pc = iter.pc();

    JSAutoCompartment ac(cx, iter.fp()->scopeChain());

    const char *filename = script->filename;
    unsigned lineno = PCToLineNumber(script, pc);
    JSFunction *fun = iter.fp()->maybeFun();
    JSString *funname = NULL;
    if (fun)
        funname = fun->atom();

    JSObject *callObj = NULL;
    AutoPropertyDescArray callProps(cx);

    if (showArgs || showLocals) {
        callObj = JS_GetFrameCallObject(cx, Jsvalify(iter.fp()));
        if (callObj)
            callProps.fetch(callObj);
    }

    Value thisVal = UndefinedValue();
    AutoPropertyDescArray thisProps(cx);
    if (ComputeThis(cx, iter.fp())) {
        thisVal = iter.fp()->thisValue();
        if (showThisProps && !thisVal.isPrimitive())
            thisProps.fetch(&thisVal.toObject());
    }

    // print the frame number and function name
    if (funname) {
        JSAutoByteString funbytes;
        buf = JS_sprintf_append(buf, "%d %s(", num, funbytes.encode(cx, funname));
    } else if (fun) {
        buf = JS_sprintf_append(buf, "%d anonymous(", num);
    } else {
        buf = JS_sprintf_append(buf, "%d <TOP LEVEL>", num);
    }
    if (!buf)
        return buf;

    // print the function arguments
    if (showArgs && callObj) {
        uint32_t namedArgCount = 0;
        for (uint32_t i = 0; i < callProps->length; i++) {
            JSPropertyDesc* desc = &callProps->array[i];
            JSAutoByteString nameBytes;
            const char *name = NULL;
            if (JSVAL_IS_STRING(desc->id))
                name = FormatValue(cx, desc->id, nameBytes);

            JSAutoByteString valueBytes;
            const char *value = FormatValue(cx, desc->value, valueBytes);

            buf = JS_sprintf_append(buf, "%s%s%s%s%s%s",
                                    namedArgCount ? ", " : "",
                                    name ? name :"",
                                    name ? " = " : "",
                                    desc->value.isString() ? "\"" : "",
                                    value ? value : "?unknown?",
                                    desc->value.isString() ? "\"" : "");
            if (!buf)
                return buf;
            namedArgCount++;
        }

        // print any unnamed trailing args (found in 'arguments' object)
        Value val;
        if (JS_GetProperty(cx, callObj, "arguments", &val) && val.isObject()) {
            uint32_t argCount;
            JSObject* argsObj = &val.toObject();
            if (JS_GetProperty(cx, argsObj, "length", &val) &&
                ToUint32(cx, val, &argCount) &&
                argCount > namedArgCount)
            {
                for (uint32_t k = namedArgCount; k < argCount; k++) {
                    char number[8];
                    JS_snprintf(number, 8, "%d", (int) k);

                    if (JS_GetProperty(cx, argsObj, number, &val)) {
                        JSAutoByteString valueBytes;
                        const char *value = FormatValue(cx, val, valueBytes);
                        buf = JS_sprintf_append(buf, "%s%s%s%s",
                                                k ? ", " : "",
                                                val.isString() ? "\"" : "",
                                                value ? value : "?unknown?",
                                                val.isString() ? "\"" : "");
                        if (!buf)
                            return buf;
                    }
                }
            }
        }
    }

    // print filename and line number
    buf = JS_sprintf_append(buf, "%s [\"%s\":%d]\n",
                            fun ? ")" : "",
                            filename ? filename : "<unknown>",
                            lineno);
    if (!buf)
        return buf;

    // print local variables
    if (showLocals && callProps->array) {
        for (uint32_t i = 0; i < callProps->length; i++) {
            JSPropertyDesc* desc = &callProps->array[i];
            JSAutoByteString nameBytes;
            JSAutoByteString valueBytes;
            const char *name = FormatValue(cx, desc->id, nameBytes);
            const char *value = FormatValue(cx, desc->value, valueBytes);

            if (name && value) {
                buf = JS_sprintf_append(buf, "    %s = %s%s%s\n",
                                        name,
                                        desc->value.isString() ? "\"" : "",
                                        value,
                                        desc->value.isString() ? "\"" : "");
                if (!buf)
                    return buf;
            }
        }
    }

    // print the value of 'this'
    if (showLocals) {
        if (!thisVal.isUndefined()) {
            JSAutoByteString thisValBytes;
            if (JSString* thisValStr = ToString(cx, thisVal)) {
                if (const char *str = thisValBytes.encode(cx, thisValStr)) {
                    buf = JS_sprintf_append(buf, "    this = %s\n", str);
                    if (!buf)
                        return buf;
                }
            }
        } else {
            buf = JS_sprintf_append(buf, "    <failed to get 'this' value>\n");
        }
    }

    // print the properties of 'this', if it is an object
    if (showThisProps && thisProps->array) {
        for (uint32_t i = 0; i < thisProps->length; i++) {
            JSPropertyDesc* desc = &thisProps->array[i];
            if (desc->flags & JSPD_ENUMERATE) {
                JSAutoByteString nameBytes;
                JSAutoByteString valueBytes;
                const char *name = FormatValue(cx, desc->id, nameBytes);
                const char *value = FormatValue(cx, desc->value, valueBytes);
                if (name && value) {
                    buf = JS_sprintf_append(buf, "    this.%s = %s%s%s\n",
                                            name,
                                            desc->value.isString() ? "\"" : "",
                                            value,
                                            desc->value.isString() ? "\"" : "");
                    if (!buf)
                        return buf;
                }
            }
        }
    }

    return buf;
}
Exemple #10
0
/*
 * Determines whether a value which has passed by ES5 150.2.3 Str steps 1-4's
 * gauntlet will result in Str returning |undefined|.  This function is used to
 * properly omit properties resulting in such values when stringifying objects,
 * while properly stringifying such properties as null when they're encountered
 * in arrays.
 */
static inline bool
IsFilteredValue(const Value &v)
{
    return v.isUndefined() || js_IsCallable(v) || (v.isObject() && v.toObject().isXML());
}
bool
js::SetElement(JSContext *cx, Handle<ObjectImpl*> obj, Handle<ObjectImpl*> receiver,
               uint32_t index, const Value &v, unsigned resolveFlags, bool *succeeded)
{
    NEW_OBJECT_REPRESENTATION_ONLY();

    Rooted<ObjectImpl*> current(cx, obj);

    MOZ_ASSERT(receiver);

    do {
        MOZ_ASSERT(current);

        if (Downcast(current)->isProxy()) {
            MOZ_NOT_REACHED("NYI: proxy [[SetP]]");
            return false;
        }

        PropDesc ownDesc;
        if (!GetOwnElement(cx, current, index, resolveFlags, &ownDesc))
            return false;

        if (!ownDesc.isUndefined()) {
            if (ownDesc.isDataDescriptor()) {
                if (!ownDesc.writable()) {
                    *succeeded = false;
                    return true;
                }

                if (receiver == current) {
                    PropDesc updateDesc = PropDesc::valueOnly(v);
                    return DefineElement(cx, receiver, index, updateDesc, false, resolveFlags,
                                         succeeded);
                }

                PropDesc newDesc;
                return DefineElement(cx, receiver, index, newDesc, false, resolveFlags, succeeded);
            }

            if (ownDesc.isAccessorDescriptor()) {
                Value setter = ownDesc.setterValue();
                if (setter.isUndefined()) {
                    *succeeded = false;
                    return true;
                }

                InvokeArgsGuard args;
                if (!cx->stack.pushInvokeArgs(cx, 1, &args))
                    return false;

                /* Push set, receiver, and v as the sole argument. */
                args.setCallee(setter);
                args.setThis(ObjectValue(*current));
                args[0] = v;

                *succeeded = true;
                return Invoke(cx, args);
            }

            MOZ_NOT_REACHED("NYI: setting PropertyOp-based property");
            return false;
        }

        current = current->getProto();
        if (current)
            continue;

        PropDesc newDesc(v, PropDesc::Writable, PropDesc::Enumerable, PropDesc::Configurable);
        return DefineElement(cx, receiver, index, newDesc, false, resolveFlags, succeeded);
    } while (false);

    MOZ_NOT_REACHED("buggy control flow");
    return false;
}
bool
js::GetElement(JSContext *cx, Handle<ObjectImpl*> obj, Handle<ObjectImpl*> receiver, uint32_t index,
               unsigned resolveFlags, Value *vp)
{
    NEW_OBJECT_REPRESENTATION_ONLY();

    Rooted<ObjectImpl*> current(cx, obj);

    do {
        MOZ_ASSERT(current);

        if (Downcast(current)->isProxy()) {
            MOZ_NOT_REACHED("NYI: proxy [[GetP]]");
            return false;
        }

        PropDesc desc;
        if (!GetOwnElement(cx, current, index, resolveFlags, &desc))
            return false;

        /* No property?  Recur or bottom out. */
        if (desc.isUndefined()) {
            current = current->getProto();
            if (current)
                continue;

            vp->setUndefined();
            return true;
        }

        /* If it's a data property, return the value. */
        if (desc.isDataDescriptor()) {
            *vp = desc.value();
            return true;
        }

        /* If it's an accessor property, call its [[Get]] with the receiver. */
        if (desc.isAccessorDescriptor()) {
            Value get = desc.getterValue();
            if (get.isUndefined()) {
                vp->setUndefined();
                return true;
            }

            InvokeArgsGuard args;
            if (!cx->stack.pushInvokeArgs(cx, 0, &args))
                return false;

            /* Push get, receiver, and no args. */
            args.setCallee(get);
            args.setThis(ObjectValue(*current));

            bool ok = Invoke(cx, args);
            *vp = args.rval();
            return ok;
        }

        /* Otherwise it's a PropertyOp-based property.  XXX handle this! */
        MOZ_NOT_REACHED("NYI: handle PropertyOp'd properties here");
        return false;
    } while (false);

    MOZ_NOT_REACHED("buggy control flow");
    return false;
}
Exemple #13
0
bool
JSStructuredCloneWriter::startWrite(const Value &v)
{
    assertSameCompartment(context(), v);

    if (v.isString()) {
        return writeString(SCTAG_STRING, v.toString());
    } else if (v.isNumber()) {
        return out.writeDouble(v.toNumber());
    } else if (v.isBoolean()) {
        return out.writePair(SCTAG_BOOLEAN, v.toBoolean());
    } else if (v.isNull()) {
        return out.writePair(SCTAG_NULL, 0);
    } else if (v.isUndefined()) {
        return out.writePair(SCTAG_UNDEFINED, 0);
    } else if (v.isObject()) {
        RootedObject obj(context(), &v.toObject());

        // The object might be a security wrapper. See if we can clone what's
        // behind it. If we can, unwrap the object.
        obj = CheckedUnwrap(obj);
        if (!obj) {
            JS_ReportError(context(), "Permission denied to access object");
            return false;
        }

        AutoCompartment ac(context(), obj);

        bool backref;
        if (!startObject(obj, &backref))
            return false;
        if (backref)
            return true;

        if (obj->isRegExp()) {
            RegExpObject &reobj = obj->asRegExp();
            return out.writePair(SCTAG_REGEXP_OBJECT, reobj.getFlags()) &&
                   writeString(SCTAG_STRING, reobj.getSource());
        } else if (obj->isDate()) {
            double d = js_DateGetMsecSinceEpoch(obj);
            return out.writePair(SCTAG_DATE_OBJECT, 0) && out.writeDouble(d);
        } else if (obj->isTypedArray()) {
            return writeTypedArray(obj);
        } else if (obj->isArrayBuffer() && obj->asArrayBuffer().hasData()) {
            return writeArrayBuffer(obj);
        } else if (obj->isObject() || obj->isArray()) {
            return traverseObject(obj);
        } else if (obj->isBoolean()) {
            return out.writePair(SCTAG_BOOLEAN_OBJECT, obj->asBoolean().unbox());
        } else if (obj->isNumber()) {
            return out.writePair(SCTAG_NUMBER_OBJECT, 0) &&
                   out.writeDouble(obj->asNumber().unbox());
        } else if (obj->isString()) {
            return writeString(SCTAG_STRING_OBJECT, obj->asString().unbox());
        }

        if (callbacks && callbacks->write)
            return callbacks->write(context(), this, obj, closure);
        /* else fall through */
    }

    JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, JSMSG_SC_UNSUPPORTED_TYPE);
    return false;
}
    CompletionType evaluate()
    {
        Register<Value> value;
        double          number;
        std::string     s;
        const char*     p;
        char*           end;
        int             base;
        long            integer;
        Value*          x;
        CompletionType  result;
        SourceElements* program;

        switch (method)
        {
        case Eval:
            x = getScopeChain()->get("x");
            if (!x->isString())
            {
                value = x;
                break;
            }

            s = x->toString();
            setSource(s.c_str());
            yyparse();
            program = getProgram();

#ifdef VERBOSE
            program->print();
#endif

            try
            {
                result = program->evaluate();
            }
            catch (Value* value)
            {
                result.setType(CompletionType::Throw);
                result.setValue(value);
            }

            delete program;

            if (result.isNormal())
            {
                if (result.getValue())
                {
                    value = result.getValue();
                }
                else
                {
                    value = UndefinedValue::getInstance();
                }
                Value::sweep(true);
            }
            else
            {
                ASSERT(result.isThrow());
                value = result.getValue();
                Value::sweep(true);
                throw result.getValue();
            }
            break;
        case ParseInt:
            x = getScopeChain()->get("radix");
            if (x->isUndefined())
            {
                base = 0;
            }
            else
            {
                base = (int) (x->toNumber());
                if (base < 2 || 36 < base)
                {
                    value = new NumberValue(NAN);
                    break;
                }
            }

            s = getScopeChain()->get("string")->toString();
            p = s.c_str();
            integer = strtol(p, &end, base);
            if (p == end)
            {
                value = new NumberValue(NAN);
            }
            else
            {
                value = new NumberValue(integer);
            }
            break;
        case ParseFloat:
            s = getScopeChain()->get("string")->toString();
            p = s.c_str();
            p = skipSpace(p);
            number = strtod(p, &end);
            if (number == 0.0 && p == end)
            {
                value = new NumberValue(NAN);
            }
            else
            {
                value = new NumberValue(number);
            }
            break;
        case IsNaN:
            number = getScopeChain()->get("number")->toNumber();
            value = BoolValue::getInstance(isnan(number) ? true : false);
            break;
        case IsFinite:
            number = getScopeChain()->get("number")->toNumber();
            value = BoolValue::getInstance(isfinite(number) ? true : false);
            break;
        case DecodeURI:
            s = getScopeChain()->get("encodedURI")->toString();
            value = new StringValue(decode(s, "#" + reservedURISet));
            break;
        case EncodeURI:
            s = getScopeChain()->get("uri")->toString();
            value = new StringValue(encode(s, reservedURISet + unescapedURIComponentSet + "#"));
            break;
        case DecodeURIComponent:
            s = getScopeChain()->get("encodedURIComponent")->toString();
            value = new StringValue(decode(s, ""));
            break;
        case EncodeURIComponent:
            s = getScopeChain()->get("uriComponent")->toString();
            value = new StringValue(encode(s, unescapedURIComponentSet));
            break;
        case GetOrderedMap:
            // TODO(ishibashi.kenichi): remove later. Temporary test code.
            value = createOrderedMap();
            break;
        }
        return CompletionType(CompletionType::Return, value, "");
    }
Exemple #15
0
/*
 * Determines whether a value which has passed by ES5 150.2.3 Str steps 1-4's
 * gauntlet will result in Str returning |undefined|.  This function is used to
 * properly omit properties resulting in such values when stringifying objects,
 * while properly stringifying such properties as null when they're encountered
 * in arrays.
 */
static inline bool
IsFilteredValue(const Value &v)
{
    return v.isUndefined() || v.isSymbol() || IsCallable(v);
}
void
StackFrame::initFromBailout(JSContext *cx, SnapshotIterator &iter)
{
    uint32 exprStackSlots = iter.slots() - script()->nfixed;

#ifdef TRACK_SNAPSHOTS
    iter.spewBailingFrom();
#endif
    IonSpew(IonSpew_Bailouts, " expr stack slots %u, is function frame %u",
            exprStackSlots, isFunctionFrame());

    if (iter.bailoutKind() == Bailout_ArgumentCheck) {
        // Temporary hack -- skip the (unused) scopeChain, because it could be
        // bogus (we can fail before the scope chain slot is set). Strip the
        // hasScopeChain flag and we'll check this later to run prologue().
        iter.skip();
        flags_ &= ~StackFrame::HAS_SCOPECHAIN;
    } else {
        Value v = iter.read();
        if (v.isObject()) {
            scopeChain_ = &v.toObject();
            flags_ |= StackFrame::HAS_SCOPECHAIN;
            if (isFunctionFrame() && fun()->isHeavyweight())
                flags_ |= StackFrame::HAS_CALL_OBJ;
        } else {
            JS_ASSERT(v.isUndefined());
        }
    }

    // Assume that all new stack frames have had their entry flag set if
    // profiling has been turned on. This will be corrected if necessary
    // elsewhere.
    if (cx->runtime->spsProfiler.enabled())
        setPushedSPSFrame();

    if (isFunctionFrame()) {
        Value thisv = iter.read();
        formals()[-1] = thisv;

        // The new |this| must have already been constructed prior to an Ion
        // constructor running.
        if (isConstructing())
            JS_ASSERT(!thisv.isPrimitive());

        JS_ASSERT(iter.slots() >= CountArgSlots(fun()));
        IonSpew(IonSpew_Bailouts, " frame slots %u, nargs %u, nfixed %u",
                iter.slots(), fun()->nargs, script()->nfixed);

        for (uint32 i = 0; i < fun()->nargs; i++) {
            Value arg = iter.read();
            formals()[i] = arg;
        }
    }
    exprStackSlots -= CountArgSlots(maybeFun());

    for (uint32 i = 0; i < script()->nfixed; i++) {
        Value slot = iter.read();
        slots()[i] = slot;
    }

    IonSpew(IonSpew_Bailouts, " pushing %u expression stack slots", exprStackSlots);
    FrameRegs &regs = cx->regs();
    for (uint32 i = 0; i < exprStackSlots; i++) {
        Value v;

        // If coming from an invalidation bailout, and this is the topmost
        // value, and a value override has been specified, don't read from the
        // iterator. Otherwise, we risk using a garbage value.
        if (!iter.moreFrames() && i == exprStackSlots - 1 && cx->runtime->hasIonReturnOverride())
            v = iter.skip();
        else
            v = iter.read();

        *regs.sp++ = v;
    }
    unsigned pcOff = iter.pcOffset();
    regs.pc = script()->code + pcOff;

    if (iter.resumeAfter())
        regs.pc = GetNextPc(regs.pc);

    IonSpew(IonSpew_Bailouts, " new PC is offset %u within script %p (line %d)",
            pcOff, (void *)script(), PCToLineNumber(script(), regs.pc));
    JS_ASSERT(exprStackSlots == js_ReconstructStackDepth(cx, script(), regs.pc));
}
Exemple #17
0
void
StackFrame::initFromBailout(JSContext *cx, SnapshotIterator &iter)
{
    uint32_t exprStackSlots = iter.slots() - script()->nfixed;

#ifdef TRACK_SNAPSHOTS
    iter.spewBailingFrom();
#endif
    IonSpew(IonSpew_Bailouts, " expr stack slots %u, is function frame %u",
            exprStackSlots, isFunctionFrame());

    if (iter.bailoutKind() == Bailout_ArgumentCheck) {
        // Temporary hack -- skip the (unused) scopeChain, because it could be
        // bogus (we can fail before the scope chain slot is set). Strip the
        // hasScopeChain flag.  If a call object is needed, it will get handled later
        // by |ThunkToInterpreter| which call |EnsureHasScopeObjects|.
        iter.skip();
        flags_ &= ~StackFrame::HAS_SCOPECHAIN;

        // If the script binds arguments, then skip the snapshot slot reserved to hold
        // its value.
        if (script()->argumentsHasVarBinding())
            iter.skip();
        flags_ &= ~StackFrame::HAS_ARGS_OBJ;
    } else {
        Value scopeChain = iter.read();
        JS_ASSERT(scopeChain.isObject() || scopeChain.isUndefined());
        if (scopeChain.isObject()) {
            scopeChain_ = &scopeChain.toObject();
            flags_ |= StackFrame::HAS_SCOPECHAIN;
            if (isFunctionFrame() && fun()->isHeavyweight())
                flags_ |= StackFrame::HAS_CALL_OBJ;
        }

        // The second slot will be an arguments object if the script needs one.
        if (script()->argumentsHasVarBinding()) {
            Value argsObj = iter.read();
            JS_ASSERT(argsObj.isObject() || argsObj.isUndefined());
            if (argsObj.isObject())
                initArgsObj(argsObj.toObject().asArguments());
        }
    }

    // Assume that all new stack frames have had their entry flag set if
    // profiling has been turned on. This will be corrected if necessary
    // elsewhere.
    if (cx->runtime->spsProfiler.enabled())
        setPushedSPSFrame();

    if (isFunctionFrame()) {
        Value thisv = iter.read();
        formals()[-1] = thisv;

        // The new |this| must have already been constructed prior to an Ion
        // constructor running.
        if (isConstructing())
            JS_ASSERT(!thisv.isPrimitive());

        JS_ASSERT(iter.slots() >= CountArgSlots(script(), fun()));
        IonSpew(IonSpew_Bailouts, " frame slots %u, nargs %u, nfixed %u",
                iter.slots(), fun()->nargs, script()->nfixed);

        for (uint32_t i = 0; i < fun()->nargs; i++) {
            Value arg = iter.read();
            formals()[i] = arg;
        }
    }
    exprStackSlots -= CountArgSlots(script(), maybeFun());

    for (uint32_t i = 0; i < script()->nfixed; i++) {
        Value slot = iter.read();
        slots()[i] = slot;
    }

    IonSpew(IonSpew_Bailouts, " pushing %u expression stack slots", exprStackSlots);
    FrameRegs &regs = cx->regs();
    for (uint32_t i = 0; i < exprStackSlots; i++) {
        Value v;

        // If coming from an invalidation bailout, and this is the topmost
        // value, and a value override has been specified, don't read from the
        // iterator. Otherwise, we risk using a garbage value.
        if (!iter.moreFrames() && i == exprStackSlots - 1 && cx->runtime->hasIonReturnOverride())
            v = iter.skip();
        else
            v = iter.read();

        *regs.sp++ = v;
    }
    unsigned pcOff = iter.pcOffset();
    regs.pc = script()->code + pcOff;

    if (iter.resumeAfter())
        regs.pc = GetNextPc(regs.pc);

    IonSpew(IonSpew_Bailouts, " new PC is offset %u within script %p (line %d)",
            pcOff, (void *)script(), PCToLineNumber(script(), regs.pc));

    // For fun.apply({}, arguments) the reconstructStackDepth will be atleast 4,
    // but it could be that we inlined the funapply. In that case exprStackSlots,
    // will have the real arguments in the slots and not always be equal.
    JS_ASSERT_IF(JSOp(*regs.pc) != JSOP_FUNAPPLY,
                 exprStackSlots == js_ReconstructStackDepth(cx, script(), regs.pc));
}