void Shape::dump(JSContext *cx, FILE *fp) const { JS_ASSERT(!JSID_IS_VOID(id)); if (JSID_IS_INT(id)) { fprintf(fp, "[%ld]", (long) JSID_TO_INT(id)); } else if (JSID_IS_DEFAULT_XML_NAMESPACE(id)) { fprintf(fp, "<default XML namespace>"); } else { JSLinearString *str; if (JSID_IS_ATOM(id)) { str = JSID_TO_ATOM(id); } else { JS_ASSERT(JSID_IS_OBJECT(id)); JSString *s = js_ValueToString(cx, IdToValue(id)); fputs("object ", fp); str = s ? s->ensureLinear(cx) : NULL; } if (!str) fputs("<error>", fp); else FileEscapedString(fp, str, '"'); } fprintf(fp, " g/s %p/%p slot %u attrs %x ", JS_FUNC_TO_DATA_PTR(void *, rawGetter), JS_FUNC_TO_DATA_PTR(void *, rawSetter), slot, attrs); if (attrs) { int first = 1; fputs("(", fp); #define DUMP_ATTR(name, display) if (attrs & JSPROP_##name) fputs(" " #display + first, fp), first = 0 DUMP_ATTR(ENUMERATE, enumerate); DUMP_ATTR(READONLY, readonly); DUMP_ATTR(PERMANENT, permanent); DUMP_ATTR(GETTER, getter); DUMP_ATTR(SETTER, setter); DUMP_ATTR(SHARED, shared); #undef DUMP_ATTR fputs(") ", fp); } fprintf(fp, "flags %x ", flags); if (flags) { int first = 1; fputs("(", fp); #define DUMP_FLAG(name, display) if (flags & name) fputs(" " #display + first, fp), first = 0 DUMP_FLAG(ALIAS, alias); DUMP_FLAG(HAS_SHORTID, has_shortid); DUMP_FLAG(METHOD, method); DUMP_FLAG(MARK, mark); DUMP_FLAG(SHAPE_REGEN, shape_regen); DUMP_FLAG(IN_DICTIONARY, in_dictionary); #undef DUMP_FLAG fputs(") ", fp); } fprintf(fp, "shortid %d\n", shortid); }
JSBool js_json_parse(JSContext *cx, uintN argc, Value *vp) { JSString *s = NULL; Value *argv = vp + 2; AutoValueRooter reviver(cx); if (!JS_ConvertArguments(cx, argc, Jsvalify(argv), "S / v", &s, reviver.addr())) return JS_FALSE; JSLinearString *linearStr = s->ensureLinear(cx); if (!linearStr) return JS_FALSE; JSONParser *jp = js_BeginJSONParse(cx, vp); JSBool ok = jp != NULL; if (ok) { const jschar *chars = linearStr->chars(); size_t length = linearStr->length(); ok = js_ConsumeJSONText(cx, jp, chars, length); ok &= !!js_FinishJSONParse(cx, jp, reviver.value()); } return ok; }
void Shape::dump(JSContext *cx, FILE *fp) const { jsid propid = this->propid(); JS_ASSERT(!JSID_IS_VOID(propid)); if (JSID_IS_INT(propid)) { fprintf(fp, "[%ld]", (long) JSID_TO_INT(propid)); } else if (JSID_IS_DEFAULT_XML_NAMESPACE(propid)) { fprintf(fp, "<default XML namespace>"); } else { JSLinearString *str; if (JSID_IS_ATOM(propid)) { str = JSID_TO_ATOM(propid); } else { JS_ASSERT(JSID_IS_OBJECT(propid)); JSString *s = ToStringSlow<CanGC>(cx, IdToValue(propid)); fputs("object ", fp); str = s ? s->ensureLinear(cx) : NULL; } if (!str) fputs("<error>", fp); else FileEscapedString(fp, str, '"'); } fprintf(fp, " g/s %p/%p slot %d attrs %x ", JS_FUNC_TO_DATA_PTR(void *, base()->rawGetter), JS_FUNC_TO_DATA_PTR(void *, base()->rawSetter), hasSlot() ? slot() : -1, attrs); if (attrs) { int first = 1; fputs("(", fp); #define DUMP_ATTR(name, display) if (attrs & JSPROP_##name) fputs(&(" " #display)[first], fp), first = 0 DUMP_ATTR(ENUMERATE, enumerate); DUMP_ATTR(READONLY, readonly); DUMP_ATTR(PERMANENT, permanent); DUMP_ATTR(GETTER, getter); DUMP_ATTR(SETTER, setter); DUMP_ATTR(SHARED, shared); #undef DUMP_ATTR fputs(") ", fp); } fprintf(fp, "flags %x ", flags); if (flags) { int first = 1; fputs("(", fp); #define DUMP_FLAG(name, display) if (flags & name) fputs(&(" " #display)[first], fp), first = 0 DUMP_FLAG(HAS_SHORTID, has_shortid); DUMP_FLAG(IN_DICTIONARY, in_dictionary); #undef DUMP_FLAG fputs(") ", fp); } fprintf(fp, "shortid %d\n", maybeShortid()); }
void Shape::dump(JSContext *cx, FILE *fp) const { /* This is only used from gdb, so allowing GC here would just be confusing. */ gc::AutoSuppressGC suppress(cx); jsid propid = this->propid(); JS_ASSERT(!JSID_IS_VOID(propid)); if (JSID_IS_INT(propid)) { fprintf(fp, "[%ld]", (long) JSID_TO_INT(propid)); } else { JSLinearString *str; if (JSID_IS_ATOM(propid)) { str = JSID_TO_ATOM(propid); } else { JS_ASSERT(JSID_IS_OBJECT(propid)); Value v = IdToValue(propid); JSString *s = ToStringSlow<NoGC>(cx, v); fputs("object ", fp); str = s ? s->ensureLinear(cx) : nullptr; } if (!str) fputs("<error>", fp); else FileEscapedString(fp, str, '"'); } fprintf(fp, " g/s %p/%p slot %d attrs %x ", JS_FUNC_TO_DATA_PTR(void *, base()->rawGetter), JS_FUNC_TO_DATA_PTR(void *, base()->rawSetter), hasSlot() ? slot() : -1, attrs); if (attrs) { int first = 1; fputs("(", fp); #define DUMP_ATTR(name, display) if (attrs & JSPROP_##name) fputs(&(" " #display)[first], fp), first = 0 DUMP_ATTR(ENUMERATE, enumerate); DUMP_ATTR(READONLY, readonly); DUMP_ATTR(PERMANENT, permanent); DUMP_ATTR(GETTER, getter); DUMP_ATTR(SETTER, setter); DUMP_ATTR(SHARED, shared); #undef DUMP_ATTR fputs(") ", fp); } fprintf(fp, "flags %x ", flags); if (flags) { int first = 1; fputs("(", fp); #define DUMP_FLAG(name, display) if (flags & name) fputs(&(" " #display)[first], fp), first = 0 DUMP_FLAG(IN_DICTIONARY, in_dictionary); #undef DUMP_FLAG fputs(") ", fp); } }
bool JSCompartment::wrap(JSContext *cx, JSString **strp) { JS_ASSERT(!cx->runtime()->isAtomsCompartment(this)); JS_ASSERT(cx->compartment() == this); /* If the string is already in this compartment, we are done. */ JSString *str = *strp; if (str->zone() == zone()) return true; /* If the string is an atom, we don't have to copy. */ if (str->isAtom()) { JS_ASSERT(cx->runtime()->isAtomsZone(str->zone())); return true; } /* Check the cache. */ RootedValue key(cx, StringValue(str)); if (WrapperMap::Ptr p = crossCompartmentWrappers.lookup(key)) { *strp = p->value().get().toString(); return true; } /* No dice. Make a copy, and cache it. */ Rooted<JSLinearString *> linear(cx, str->ensureLinear(cx)); if (!linear) return false; JSString *copy = js_NewStringCopyN<CanGC>(cx, linear->chars(), linear->length()); if (!copy) return false; if (!putWrapper(key, StringValue(copy))) return false; if (linear->zone()->isGCMarking()) { /* * All string wrappers are dropped when collection starts, but we * just created a new one. Mark the wrapped string to stop it being * finalized, because if it was then the pointer in this * compartment's wrapper map would be left dangling. */ JSString *tmp = linear; MarkStringUnbarriered(&cx->runtime()->gcMarker, &tmp, "wrapped string"); JS_ASSERT(tmp == linear); } *strp = copy; return true; }
/* ES5 15.12.2. */ JSBool js_json_parse(JSContext *cx, unsigned argc, Value *vp) { /* Step 1. */ JSLinearString *linear; if (argc >= 1) { JSString *str = ToString(cx, vp[2]); if (!str) return false; linear = str->ensureLinear(cx); if (!linear) return false; } else { linear = cx->runtime->atomState.typeAtoms[JSTYPE_VOID]; } JS::Anchor<JSString *> anchor(linear); Value reviver = (argc >= 2) ? vp[3] : UndefinedValue(); /* Steps 2-5. */ return ParseJSONWithReviver(cx, linear->chars(), linear->length(), reviver, vp); }
/* ES5 15.12.2. */ JSBool js_json_parse(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); /* Step 1. */ JSLinearString *linear; if (argc >= 1) { JSString *str = ToString(cx, args[0]); if (!str) return false; linear = str->ensureLinear(cx); if (!linear) return false; } else { linear = cx->names().undefined; } JS::Anchor<JSString *> anchor(linear); RootedValue reviver(cx, (argc >= 2) ? args[1] : UndefinedValue()); /* Steps 2-5. */ return ParseJSONWithReviver(cx, linear->chars(), linear->length(), reviver, args.rval()); }
JSString * js::ObjectToSource(JSContext *cx, HandleObject obj) { /* If outermost, we need parentheses to be an expression, not a block. */ bool outermost = (cx->cycleDetectorSet.count() == 0); AutoCycleDetector detector(cx, obj); if (!detector.init()) return nullptr; if (detector.foundCycle()) return js_NewStringCopyZ<CanGC>(cx, "{}"); StringBuffer buf(cx); if (outermost && !buf.append('(')) return nullptr; if (!buf.append('{')) return nullptr; RootedValue v0(cx), v1(cx); MutableHandleValue val[2] = {&v0, &v1}; RootedString str0(cx), str1(cx); MutableHandleString gsop[2] = {&str0, &str1}; AutoIdVector idv(cx); if (!GetPropertyNames(cx, obj, JSITER_OWNONLY, &idv)) return nullptr; bool comma = false; for (size_t i = 0; i < idv.length(); ++i) { RootedId id(cx, idv[i]); RootedObject obj2(cx); RootedShape shape(cx); if (!JSObject::lookupGeneric(cx, obj, id, &obj2, &shape)) return nullptr; /* Decide early whether we prefer get/set or old getter/setter syntax. */ int valcnt = 0; if (shape) { bool doGet = true; if (obj2->isNative() && !IsImplicitDenseElement(shape)) { unsigned attrs = shape->attributes(); if (attrs & JSPROP_GETTER) { doGet = false; val[valcnt].set(shape->getterValue()); gsop[valcnt].set(cx->names().get); valcnt++; } if (attrs & JSPROP_SETTER) { doGet = false; val[valcnt].set(shape->setterValue()); gsop[valcnt].set(cx->names().set); valcnt++; } } if (doGet) { valcnt = 1; gsop[0].set(nullptr); if (!JSObject::getGeneric(cx, obj, obj, id, val[0])) return nullptr; } } /* Convert id to a linear string. */ RootedValue idv(cx, IdToValue(id)); JSString *s = ToString<CanGC>(cx, idv); if (!s) return nullptr; Rooted<JSLinearString*> idstr(cx, s->ensureLinear(cx)); if (!idstr) return nullptr; /* * If id is a string that's not an identifier, or if it's a negative * integer, then it must be quoted. */ if (JSID_IS_ATOM(id) ? !IsIdentifier(idstr) : (!JSID_IS_INT(id) || JSID_TO_INT(id) < 0)) { s = js_QuoteString(cx, idstr, jschar('\'')); if (!s || !(idstr = s->ensureLinear(cx))) return nullptr; } for (int j = 0; j < valcnt; j++) { /* * Censor an accessor descriptor getter or setter part if it's * undefined. */ if (gsop[j] && val[j].isUndefined()) continue; /* Convert val[j] to its canonical source form. */ RootedString valstr(cx, ValueToSource(cx, val[j])); if (!valstr) return nullptr; const jschar *vchars = valstr->getChars(cx); if (!vchars) return nullptr; size_t vlength = valstr->length(); /* * Remove '(function ' from the beginning of valstr and ')' from the * end so that we can put "get" in front of the function definition. */ if (gsop[j] && IsFunctionObject(val[j])) { const jschar *start = vchars; const jschar *end = vchars + vlength; uint8_t parenChomp = 0; if (vchars[0] == '(') { vchars++; parenChomp = 1; } /* Try to jump "function" keyword. */ if (vchars) vchars = js_strchr_limit(vchars, ' ', end); /* * Jump over the function's name: it can't be encoded as part * of an ECMA getter or setter. */ if (vchars) vchars = js_strchr_limit(vchars, '(', end); if (vchars) { if (*vchars == ' ') vchars++; vlength = end - vchars - parenChomp; } else { gsop[j].set(nullptr); vchars = start; } } if (comma && !buf.append(", ")) return nullptr; comma = true; if (gsop[j]) if (!buf.append(gsop[j]) || !buf.append(' ')) return nullptr; if (!buf.append(idstr)) return nullptr; if (!buf.append(gsop[j] ? ' ' : ':')) return nullptr; if (!buf.append(vchars, vlength)) return nullptr; } } if (!buf.append('}')) return nullptr; if (outermost && !buf.append(')')) return nullptr; return buf.finishString(); }
/* * 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; }
JSString * js::ObjectToSource(JSContext *cx, HandleObject obj) { /* If outermost, we need parentheses to be an expression, not a block. */ bool outermost = (cx->cycleDetectorSet.count() == 0); AutoCycleDetector detector(cx, obj); if (!detector.init()) return nullptr; if (detector.foundCycle()) return NewStringCopyZ<CanGC>(cx, "{}"); StringBuffer buf(cx); if (outermost && !buf.append('(')) return nullptr; if (!buf.append('{')) return nullptr; RootedValue v0(cx), v1(cx); MutableHandleValue val[2] = {&v0, &v1}; RootedString str0(cx), str1(cx); MutableHandleString gsop[2] = {&str0, &str1}; AutoIdVector idv(cx); if (!GetPropertyNames(cx, obj, JSITER_OWNONLY | JSITER_SYMBOLS, &idv)) return nullptr; bool comma = false; for (size_t i = 0; i < idv.length(); ++i) { RootedId id(cx, idv[i]); RootedObject obj2(cx); RootedShape shape(cx); if (!JSObject::lookupGeneric(cx, obj, id, &obj2, &shape)) return nullptr; /* Decide early whether we prefer get/set or old getter/setter syntax. */ int valcnt = 0; if (shape) { bool doGet = true; if (obj2->isNative() && !IsImplicitDenseOrTypedArrayElement(shape)) { unsigned attrs = shape->attributes(); if (attrs & JSPROP_GETTER) { doGet = false; val[valcnt].set(shape->getterValue()); gsop[valcnt].set(cx->names().get); valcnt++; } if (attrs & JSPROP_SETTER) { doGet = false; val[valcnt].set(shape->setterValue()); gsop[valcnt].set(cx->names().set); valcnt++; } } if (doGet) { valcnt = 1; gsop[0].set(nullptr); if (!JSObject::getGeneric(cx, obj, obj, id, val[0])) return nullptr; } } /* Convert id to a string. */ RootedString idstr(cx); if (JSID_IS_SYMBOL(id)) { RootedValue v(cx, SymbolValue(JSID_TO_SYMBOL(id))); idstr = ValueToSource(cx, v); if (!idstr) return nullptr; } else { RootedValue idv(cx, IdToValue(id)); idstr = ToString<CanGC>(cx, idv); if (!idstr) return nullptr; /* * If id is a string that's not an identifier, or if it's a negative * integer, then it must be quoted. */ if (JSID_IS_ATOM(id) ? !IsIdentifier(JSID_TO_ATOM(id)) : JSID_TO_INT(id) < 0) { idstr = js_QuoteString(cx, idstr, char16_t('\'')); if (!idstr) return nullptr; } } for (int j = 0; j < valcnt; j++) { /* * Censor an accessor descriptor getter or setter part if it's * undefined. */ if (gsop[j] && val[j].isUndefined()) continue; /* Convert val[j] to its canonical source form. */ JSString *valsource = ValueToSource(cx, val[j]); if (!valsource) return nullptr; RootedLinearString valstr(cx, valsource->ensureLinear(cx)); if (!valstr) return nullptr; size_t voffset = 0; size_t vlength = valstr->length(); /* * Remove '(function ' from the beginning of valstr and ')' from the * end so that we can put "get" in front of the function definition. */ if (gsop[j] && IsFunctionObject(val[j])) { bool success; JS::AutoCheckCannotGC nogc; if (valstr->hasLatin1Chars()) success = ArgsAndBodySubstring(valstr->latin1Range(nogc), &voffset, &vlength); else success = ArgsAndBodySubstring(valstr->twoByteRange(nogc), &voffset, &vlength); if (!success) gsop[j].set(nullptr); } if (comma && !buf.append(", ")) return nullptr; comma = true; if (gsop[j]) { if (!buf.append(gsop[j]) || !buf.append(' ')) return nullptr; } if (JSID_IS_SYMBOL(id) && !buf.append('[')) return nullptr; if (!buf.append(idstr)) return nullptr; if (JSID_IS_SYMBOL(id) && !buf.append(']')) return nullptr; if (!buf.append(gsop[j] ? ' ' : ':')) return nullptr; if (!buf.appendSubstring(valstr, voffset, vlength)) return nullptr; } } if (!buf.append('}')) return nullptr; if (outermost && !buf.append(')')) return nullptr; return buf.finishString(); }
JSString* js::ObjectToSource(JSContext* cx, HandleObject obj) { /* If outermost, we need parentheses to be an expression, not a block. */ bool outermost = (cx->cycleDetectorSet.count() == 0); AutoCycleDetector detector(cx, obj); if (!detector.init()) return nullptr; if (detector.foundCycle()) return NewStringCopyZ<CanGC>(cx, "{}"); StringBuffer buf(cx); if (outermost && !buf.append('(')) return nullptr; if (!buf.append('{')) return nullptr; RootedValue v0(cx), v1(cx); MutableHandleValue val[2] = {&v0, &v1}; RootedString str0(cx), str1(cx); MutableHandleString gsop[2] = {&str0, &str1}; AutoIdVector idv(cx); if (!GetPropertyKeys(cx, obj, JSITER_OWNONLY | JSITER_SYMBOLS, &idv)) return nullptr; bool comma = false; for (size_t i = 0; i < idv.length(); ++i) { RootedId id(cx, idv[i]); Rooted<PropertyDescriptor> desc(cx); if (!GetOwnPropertyDescriptor(cx, obj, id, &desc)) return nullptr; int valcnt = 0; if (desc.object()) { if (desc.isAccessorDescriptor()) { if (desc.hasGetterObject() && desc.getterObject()) { val[valcnt].setObject(*desc.getterObject()); gsop[valcnt].set(cx->names().get); valcnt++; } if (desc.hasSetterObject() && desc.setterObject()) { val[valcnt].setObject(*desc.setterObject()); gsop[valcnt].set(cx->names().set); valcnt++; } } else { valcnt = 1; val[0].set(desc.value()); gsop[0].set(nullptr); } } /* Convert id to a string. */ RootedString idstr(cx); if (JSID_IS_SYMBOL(id)) { RootedValue v(cx, SymbolValue(JSID_TO_SYMBOL(id))); idstr = ValueToSource(cx, v); if (!idstr) return nullptr; } else { RootedValue idv(cx, IdToValue(id)); idstr = ToString<CanGC>(cx, idv); if (!idstr) return nullptr; /* * If id is a string that's not an identifier, or if it's a negative * integer, then it must be quoted. */ if (JSID_IS_ATOM(id) ? !IsIdentifier(JSID_TO_ATOM(id)) : JSID_TO_INT(id) < 0) { idstr = QuoteString(cx, idstr, char16_t('\'')); if (!idstr) return nullptr; } } for (int j = 0; j < valcnt; j++) { /* Convert val[j] to its canonical source form. */ JSString* valsource = ValueToSource(cx, val[j]); if (!valsource) return nullptr; RootedLinearString valstr(cx, valsource->ensureLinear(cx)); if (!valstr) return nullptr; size_t voffset = 0; size_t vlength = valstr->length(); /* * Remove '(function ' from the beginning of valstr and ')' from the * end so that we can put "get" in front of the function definition. */ if (gsop[j] && IsFunctionObject(val[j])) { bool success; JS::AutoCheckCannotGC nogc; if (valstr->hasLatin1Chars()) success = ArgsAndBodySubstring(valstr->latin1Range(nogc), &voffset, &vlength); else success = ArgsAndBodySubstring(valstr->twoByteRange(nogc), &voffset, &vlength); if (!success) gsop[j].set(nullptr); } if (comma && !buf.append(", ")) return nullptr; comma = true; if (gsop[j]) { if (!buf.append(gsop[j]) || !buf.append(' ')) return nullptr; } if (JSID_IS_SYMBOL(id) && !buf.append('[')) return nullptr; if (!buf.append(idstr)) return nullptr; if (JSID_IS_SYMBOL(id) && !buf.append(']')) return nullptr; if (!buf.append(gsop[j] ? ' ' : ':')) return nullptr; if (!buf.appendSubstring(valstr, voffset, vlength)) return nullptr; } } if (!buf.append('}')) return nullptr; if (outermost && !buf.append(')')) return nullptr; return buf.finishString(); }
// 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().set(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); RootedValue tmp(cx); if (!parser.parse(&tmp)) return false; if (tmp.isUndefined()) break; args.rval().set(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().address()); }