JSBool EvalScriptVersion16(JSContext *cx, uintN argc, jsval *vp) { JS_ASSERT(argc == 1); jsval *argv = JS_ARGV(cx, vp); JS_ASSERT(JSVAL_IS_STRING(argv[0])); JSString *str = JSVAL_TO_STRING(argv[0]); const jschar *chars = str->getChars(cx); JS_ASSERT(chars); size_t len = str->length(); return callbackData->evalVersion(chars, len, JSVERSION_1_6); }
/** * Handles an assertion failure in self-hosted code just like an assertion * failure in C++ code. Information about the failure can be provided in args[0]. */ static bool intrinsic_AssertionFailed(JSContext *cx, unsigned argc, Value *vp) { #ifdef DEBUG CallArgs args = CallArgsFromVp(argc, vp); if (args.length() > 0) { // try to dump the informative string JSString *str = ToString<CanGC>(cx, args[0]); if (str) { const jschar *chars = str->getChars(cx); if (chars) { fprintf(stderr, "Self-hosted JavaScript assertion info: "); JSString::dumpChars(chars, str->length()); fputc('\n', stderr); } } } #endif JS_ASSERT(false); return false; }
bool JSCompartment::wrap(JSContext *cx, Value *vp) { JS_ASSERT(cx->compartment == this); unsigned flags = 0; JS_CHECK_RECURSION(cx, return false); /* Only GC things have to be wrapped or copied. */ if (!vp->isMarkable()) return true; if (vp->isString()) { JSString *str = vp->toString(); /* If the string is already in this compartment, we are done. */ if (str->compartment() == this) return true; /* If the string is an atom, we don't have to copy. */ if (str->isAtom()) { JS_ASSERT(str->compartment() == cx->runtime->atomsCompartment); return true; } } /* * Wrappers should really be parented to the wrapped parent of the wrapped * object, but in that case a wrapped global object would have a NULL * parent without being a proper global object (JSCLASS_IS_GLOBAL). Instead, * we parent all wrappers to the global object in their home compartment. * This loses us some transparency, and is generally very cheesy. */ JSObject *global; if (cx->hasfp()) { global = &cx->fp()->scopeChain().global(); } else { global = JS_ObjectToInnerObject(cx, cx->globalObject); if (!global) return false; } /* Unwrap incoming objects. */ if (vp->isObject()) { JSObject *obj = &vp->toObject(); /* If the object is already in this compartment, we are done. */ if (obj->compartment() == this) return true; /* Translate StopIteration singleton. */ if (obj->isStopIteration()) return js_FindClassObject(cx, NULL, JSProto_StopIteration, vp); /* Don't unwrap an outer window proxy. */ if (!obj->getClass()->ext.innerObject) { obj = UnwrapObject(&vp->toObject(), true, &flags); vp->setObject(*obj); if (obj->compartment() == this) return true; if (cx->runtime->preWrapObjectCallback) { obj = cx->runtime->preWrapObjectCallback(cx, global, obj, flags); if (!obj) return false; } vp->setObject(*obj); if (obj->compartment() == this) return true; } else { if (cx->runtime->preWrapObjectCallback) { obj = cx->runtime->preWrapObjectCallback(cx, global, obj, flags); if (!obj) return false; } JS_ASSERT(!obj->isWrapper() || obj->getClass()->ext.innerObject); vp->setObject(*obj); } #ifdef DEBUG { JSObject *outer = obj; OBJ_TO_OUTER_OBJECT(cx, outer); JS_ASSERT(outer && outer == obj); } #endif } /* If we already have a wrapper for this value, use it. */ if (WrapperMap::Ptr p = crossCompartmentWrappers.lookup(*vp)) { *vp = p->value; if (vp->isObject()) { JSObject *obj = &vp->toObject(); JS_ASSERT(obj->isCrossCompartmentWrapper()); if (global->getClass() != &dummy_class && obj->getParent() != global) { do { if (!obj->setParent(cx, global)) return false; obj = obj->getProto(); } while (obj && obj->isCrossCompartmentWrapper()); } } return true; } if (vp->isString()) { Value orig = *vp; JSString *str = vp->toString(); const jschar *chars = str->getChars(cx); if (!chars) return false; JSString *wrapped = js_NewStringCopyN(cx, chars, str->length()); if (!wrapped) return false; vp->setString(wrapped); return crossCompartmentWrappers.put(orig, *vp); } JSObject *obj = &vp->toObject(); /* * Recurse to wrap the prototype. Long prototype chains will run out of * stack, causing an error in CHECK_RECURSE. * * Wrapping the proto before creating the new wrapper and adding it to the * cache helps avoid leaving a bad entry in the cache on OOM. But note that * if we wrapped both proto and parent, we would get infinite recursion * here (since Object.prototype->parent->proto leads to Object.prototype * itself). */ JSObject *proto = obj->getProto(); if (!wrap(cx, &proto)) return false; /* * We hand in the original wrapped object into the wrap hook to allow * the wrap hook to reason over what wrappers are currently applied * to the object. */ JSObject *wrapper = cx->runtime->wrapObjectCallback(cx, obj, proto, global, flags); if (!wrapper) return false; vp->setObject(*wrapper); if (wrapper->getProto() != proto && !SetProto(cx, wrapper, proto, false)) return false; if (!crossCompartmentWrappers.put(GetProxyPrivate(wrapper), *vp)) return false; if (!wrapper->setParent(cx, global)) return false; return true; }
static JSBool JO(JSContext *cx, Value *vp, StringifyContext *scx) { JSObject *obj = &vp->toObject(); CycleDetector detect(scx, obj); if (!detect.init(cx)) return JS_FALSE; if (!scx->sb.append('{')) return JS_FALSE; Value vec[3] = { NullValue(), NullValue(), NullValue() }; AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(vec), vec); Value& outputValue = vec[0]; Value& whitelistElement = vec[1]; AutoIdRooter idr(cx); jsid& id = *idr.addr(); Value *keySource = vp; bool usingWhitelist = false; // if the replacer is an array, we use the keys from it if (scx->replacer && JS_IsArrayObject(cx, scx->replacer)) { usingWhitelist = true; vec[2].setObject(*scx->replacer); keySource = &vec[2]; } JSBool memberWritten = JS_FALSE; AutoIdVector props(cx); if (!GetPropertyNames(cx, &keySource->toObject(), JSITER_OWNONLY, &props)) return JS_FALSE; for (size_t i = 0, len = props.length(); i < len; i++) { outputValue.setUndefined(); if (!usingWhitelist) { if (!js_ValueToStringId(cx, IdToValue(props[i]), &id)) return JS_FALSE; } else { // skip non-index properties jsuint index = 0; if (!js_IdIsIndex(props[i], &index)) continue; if (!scx->replacer->getProperty(cx, props[i], &whitelistElement)) return JS_FALSE; if (!js_ValueToStringId(cx, whitelistElement, &id)) return JS_FALSE; } // We should have a string id by this point. Either from // JS_Enumerate's id array, or by converting an element // of the whitelist. JS_ASSERT(JSID_IS_ATOM(id)); if (!JS_GetPropertyById(cx, obj, id, Jsvalify(&outputValue))) return JS_FALSE; if (outputValue.isObjectOrNull() && !js_TryJSON(cx, &outputValue)) return JS_FALSE; // call this here, so we don't write out keys if the replacer function // wants to elide the value. if (!CallReplacerFunction(cx, id, obj, scx, &outputValue)) return JS_FALSE; JSType type = JS_TypeOfValue(cx, Jsvalify(outputValue)); // elide undefined values and functions and XML if (outputValue.isUndefined() || type == JSTYPE_FUNCTION || type == JSTYPE_XML) continue; // output a comma unless this is the first member to write if (memberWritten && !scx->sb.append(',')) return JS_FALSE; memberWritten = JS_TRUE; if (!WriteIndent(cx, scx, scx->depth)) return JS_FALSE; // Be careful below, this string is weakly rooted JSString *s = js_ValueToString(cx, IdToValue(id)); if (!s) return JS_FALSE; JS::Anchor<JSString *> anchor(s); size_t length = s->length(); const jschar *chars = s->getChars(cx); if (!chars) return JS_FALSE; if (!write_string(cx, scx->sb, chars, length) || !scx->sb.append(':') || !(scx->gap.empty() || scx->sb.append(' ')) || !Str(cx, id, obj, scx, &outputValue, true)) { return JS_FALSE; } } if (memberWritten && !WriteIndent(cx, scx, scx->depth - 1)) return JS_FALSE; return scx->sb.append('}'); }
bool JSCompartment::wrap(JSContext *cx, Value *vp) { JS_ASSERT(cx->compartment == this); unsigned flags = 0; JS_CHECK_RECURSION(cx, return false); #ifdef DEBUG struct AutoDisableProxyCheck { JSRuntime *runtime; AutoDisableProxyCheck(JSRuntime *rt) : runtime(rt) { runtime->gcDisableStrictProxyCheckingCount++; } ~AutoDisableProxyCheck() { runtime->gcDisableStrictProxyCheckingCount--; } } adpc(rt); #endif /* Only GC things have to be wrapped or copied. */ if (!vp->isMarkable()) return true; if (vp->isString()) { JSString *str = vp->toString(); /* If the string is already in this compartment, we are done. */ if (str->compartment() == this) return true; /* If the string is an atom, we don't have to copy. */ if (str->isAtom()) { JS_ASSERT(str->compartment() == cx->runtime->atomsCompartment); return true; } } /* * Wrappers should really be parented to the wrapped parent of the wrapped * object, but in that case a wrapped global object would have a NULL * parent without being a proper global object (JSCLASS_IS_GLOBAL). Instead, * we parent all wrappers to the global object in their home compartment. * This loses us some transparency, and is generally very cheesy. */ HandleObject global = cx->global(); /* Unwrap incoming objects. */ if (vp->isObject()) { Rooted<JSObject*> obj(cx, &vp->toObject()); if (obj->compartment() == this) return WrapForSameCompartment(cx, obj, vp); /* Translate StopIteration singleton. */ if (obj->isStopIteration()) { RootedValue vvp(cx, *vp); bool result = js_FindClassObject(cx, JSProto_StopIteration, &vvp); *vp = vvp; return result; } /* Unwrap the object, but don't unwrap outer windows. */ obj = UnwrapObject(&vp->toObject(), /* stopAtOuter = */ true, &flags); if (obj->compartment() == this) return WrapForSameCompartment(cx, obj, vp); if (cx->runtime->preWrapObjectCallback) { obj = cx->runtime->preWrapObjectCallback(cx, global, obj, flags); if (!obj) return false; } if (obj->compartment() == this) return WrapForSameCompartment(cx, obj, vp); vp->setObject(*obj); #ifdef DEBUG { JSObject *outer = GetOuterObject(cx, obj); JS_ASSERT(outer && outer == obj); } #endif } RootedValue key(cx, *vp); /* If we already have a wrapper for this value, use it. */ if (WrapperMap::Ptr p = crossCompartmentWrappers.lookup(key)) { *vp = p->value; if (vp->isObject()) { RootedObject obj(cx, &vp->toObject()); JS_ASSERT(obj->isCrossCompartmentWrapper()); if (obj->getParent() != global) { do { if (!JSObject::setParent(cx, obj, global)) return false; obj = obj->getProto(); } while (obj && obj->isCrossCompartmentWrapper()); } } return true; } if (vp->isString()) { RootedValue orig(cx, *vp); JSString *str = vp->toString(); const jschar *chars = str->getChars(cx); if (!chars) return false; JSString *wrapped = js_NewStringCopyN(cx, chars, str->length()); if (!wrapped) return false; vp->setString(wrapped); return crossCompartmentWrappers.put(orig, *vp); } RootedObject obj(cx, &vp->toObject()); /* * Recurse to wrap the prototype. Long prototype chains will run out of * stack, causing an error in CHECK_RECURSE. * * Wrapping the proto before creating the new wrapper and adding it to the * cache helps avoid leaving a bad entry in the cache on OOM. But note that * if we wrapped both proto and parent, we would get infinite recursion * here (since Object.prototype->parent->proto leads to Object.prototype * itself). */ RootedObject proto(cx, obj->getProto()); if (!wrap(cx, proto.address())) return false; /* * We hand in the original wrapped object into the wrap hook to allow * the wrap hook to reason over what wrappers are currently applied * to the object. */ RootedObject wrapper(cx, cx->runtime->wrapObjectCallback(cx, obj, proto, global, flags)); if (!wrapper) return false; // We maintain the invariant that the key in the cross-compartment wrapper // map is always directly wrapped by the value. JS_ASSERT(Wrapper::wrappedObject(wrapper) == &key.get().toObject()); vp->setObject(*wrapper); if (!crossCompartmentWrappers.put(key, *vp)) return false; return true; }