bool BaseProxyHandler::boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const { vp.setUndefined(); return true; }
bool BaseProxyHandler::set(JSContext* cx, HandleObject proxy, HandleObject receiver, HandleId id, bool strict, MutableHandleValue vp) const { assertEnteredPolicy(cx, proxy, id, SET); // This method is not covered by any spec, but we follow ES6 draft rev 28 // (2014 Oct 14) 9.1.9 fairly closely, adapting it slightly for // SpiderMonkey's particular foibles. // Steps 2-3. (Step 1 is a superfluous assertion.) Rooted<PropertyDescriptor> ownDesc(cx); if (!getOwnPropertyDescriptor(cx, proxy, id, &ownDesc)) return false; // Step 4. if (!ownDesc.object()) { // The spec calls this variable "parent", but that word has weird // connotations in SpiderMonkey, so let's go with "proto". RootedObject proto(cx); if (!GetPrototype(cx, proxy, &proto)) return false; if (proto) return SetProperty(cx, proto, receiver, id, vp, strict); // Change ownDesc to be a complete descriptor for a configurable, // writable, enumerable data property. Then fall through to step 5. ownDesc.clear(); ownDesc.setAttributes(JSPROP_ENUMERATE); } // Step 5. if (ownDesc.isDataDescriptor()) { // Steps 5.a-b, adapted to our nonstandard implementation of ES6 // [[Set]] return values. if (!ownDesc.isWritable()) { if (strict) return JSObject::reportReadOnly(cx, id, JSREPORT_ERROR); if (cx->compartment()->options().extraWarnings(cx)) return JSObject::reportReadOnly(cx, id, JSREPORT_STRICT | JSREPORT_WARNING); return true; } // Nonstandard SpiderMonkey special case: setter ops. StrictPropertyOp setter = ownDesc.setter(); MOZ_ASSERT(setter != JS_StrictPropertyStub); if (setter && setter != JS_StrictPropertyStub) return CallSetter(cx, receiver, id, setter, ownDesc.attributes(), strict, vp); // Steps 5.c-d. Adapt for SpiderMonkey by using HasOwnProperty instead // of the standard [[GetOwnProperty]]. bool existingDescriptor; if (!HasOwnProperty(cx, receiver, id, &existingDescriptor)) return false; // Steps 5.e-f. unsigned attrs = existingDescriptor ? JSPROP_IGNORE_ENUMERATE | JSPROP_IGNORE_READONLY | JSPROP_IGNORE_PERMANENT : JSPROP_ENUMERATE; // A very old nonstandard SpiderMonkey extension: default to the Class // getter and setter ops. const Class* clasp = receiver->getClass(); MOZ_ASSERT(clasp->getProperty != JS_PropertyStub); MOZ_ASSERT(clasp->setProperty != JS_StrictPropertyStub); return DefineProperty(cx, receiver, id, vp, clasp->getProperty, clasp->setProperty, attrs); } // Step 6. MOZ_ASSERT(ownDesc.isAccessorDescriptor()); RootedObject setter(cx); if (ownDesc.hasSetterObject()) setter = ownDesc.setterObject(); if (!setter) return js_ReportGetterOnlyAssignment(cx, strict); RootedValue setterValue(cx, ObjectValue(*setter)); return InvokeGetterOrSetter(cx, receiver, setterValue, 1, vp.address(), vp); }
bool JSCompartment::wrap(JSContext *cx, MutableHandleValue vp, HandleObject existingArg) { JS_ASSERT(cx->compartment() == this); JS_ASSERT(this != rt->atomsCompartment); JS_ASSERT_IF(existingArg, existingArg->compartment() == cx->compartment()); JS_ASSERT_IF(existingArg, vp.isObject()); JS_ASSERT_IF(existingArg, IsDeadProxyObject(existingArg)); unsigned flags = 0; JS_CHECK_CHROME_RECURSION(cx, return false); AutoDisableProxyCheck adpc(rt); /* 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->zone() == zone()) return true; /* If the string is an atom, we don't have to copy. */ if (str->isAtom()) { JS_ASSERT(str->zone() == cx->runtime()->atomsCompartment->zone()); 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()) { RootedObject obj(cx, &vp.toObject()); if (obj->compartment() == this) return WrapForSameCompartment(cx, obj, vp); /* Translate StopIteration singleton. */ if (obj->is<StopIterationObject>()) return js_FindClassObject(cx, JSProto_StopIteration, vp); /* Unwrap the object, but don't unwrap outer windows. */ obj = UncheckedUnwrap(obj, /* 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.set(p->value); if (vp.isObject()) { DebugOnly<JSObject *> obj = &vp.toObject(); JS_ASSERT(obj->is<CrossCompartmentWrapperObject>()); JS_ASSERT(obj->getParent() == global); } return true; } if (vp.isString()) { Rooted<JSLinearString *> str(cx, vp.toString()->ensureLinear(cx)); if (!str) return false; JSString *wrapped = js_NewStringCopyN<CanGC>(cx, str->chars(), str->length()); if (!wrapped) return false; vp.setString(wrapped); if (!putWrapper(key, vp)) return false; if (str->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 = str; MarkStringUnbarriered(&rt->gcMarker, &tmp, "wrapped string"); JS_ASSERT(tmp == str); } return true; } RootedObject proto(cx, Proxy::LazyProto); RootedObject obj(cx, &vp.toObject()); RootedObject existing(cx, existingArg); if (existing) { /* Is it possible to reuse |existing|? */ if (!existing->getTaggedProto().isLazy() || // Note: don't use is<ObjectProxyObject>() here -- it also matches subclasses! existing->getClass() != &ObjectProxyObject::class_ || existing->getParent() != global || obj->isCallable()) { existing = NULL; } } /* * 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); wrapper = cx->runtime()->wrapObjectCallback(cx, existing, 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); return putWrapper(key, vp); }
// Extracting machine code as JS object. The result has the "code" property, as // a Uint8Array, and the "segments" property as array objects. The objects // contain offsets in the "code" array and basic information about a code // segment/function body. bool Module::extractCode(JSContext* cx, MutableHandleValue vp) { RootedPlainObject result(cx, NewBuiltinClassInstance<PlainObject>(cx)); if (!result) return false; RootedObject code(cx, JS_NewUint8Array(cx, code_.length())); if (!code) return false; memcpy(code->as<TypedArrayObject>().viewDataUnshared(), code_.begin(), code_.length()); RootedValue value(cx, ObjectValue(*code)); if (!JS_DefineProperty(cx, result, "code", value, JSPROP_ENUMERATE)) return false; RootedObject segments(cx, NewDenseEmptyArray(cx)); if (!segments) return false; for (const CodeRange& p : metadata_->codeRanges) { RootedObject segment(cx, NewObjectWithGivenProto<PlainObject>(cx, nullptr)); if (!segment) return false; value.setNumber((uint32_t)p.begin()); if (!JS_DefineProperty(cx, segment, "begin", value, JSPROP_ENUMERATE)) return false; value.setNumber((uint32_t)p.end()); if (!JS_DefineProperty(cx, segment, "end", value, JSPROP_ENUMERATE)) return false; value.setNumber((uint32_t)p.kind()); if (!JS_DefineProperty(cx, segment, "kind", value, JSPROP_ENUMERATE)) return false; if (p.isFunction()) { value.setNumber((uint32_t)p.funcIndex()); if (!JS_DefineProperty(cx, segment, "funcIndex", value, JSPROP_ENUMERATE)) return false; value.setNumber((uint32_t)p.funcNonProfilingEntry()); if (!JS_DefineProperty(cx, segment, "funcBodyBegin", value, JSPROP_ENUMERATE)) return false; value.setNumber((uint32_t)p.funcProfilingEpilogue()); if (!JS_DefineProperty(cx, segment, "funcBodyEnd", value, JSPROP_ENUMERATE)) return false; } if (!NewbornArrayPush(cx, segments, ObjectValue(*segment))) return false; } value.setObject(*segments); if (!JS_DefineProperty(cx, result, "segments", value, JSPROP_ENUMERATE)) return false; vp.setObject(*result); return true; }
static bool PreprocessValue(JSContext *cx, HandleObject holder, KeyType key, MutableHandleValue vp, StringifyContext *scx) { RootedString keyStr(cx); /* Step 2. */ if (vp.get().isObject()) { RootedValue toJSON(cx); RootedId id(cx, NameToId(cx->runtime->atomState.toJSONAtom)); Rooted<JSObject*> obj(cx, &vp.get().toObject()); if (!GetMethod(cx, obj, id, 0, &toJSON)) return false; if (js_IsCallable(toJSON)) { keyStr = KeyStringifier<KeyType>::toString(cx, key); if (!keyStr) return false; InvokeArgsGuard args; if (!cx->stack.pushInvokeArgs(cx, 1, &args)) return false; args.setCallee(toJSON); args.setThis(vp); args[0] = StringValue(keyStr); if (!Invoke(cx, args)) return false; vp.set(args.rval()); } } /* Step 3. */ if (scx->replacer && scx->replacer->isCallable()) { if (!keyStr) { keyStr = KeyStringifier<KeyType>::toString(cx, key); if (!keyStr) return false; } InvokeArgsGuard args; if (!cx->stack.pushInvokeArgs(cx, 2, &args)) return false; args.setCallee(ObjectValue(*scx->replacer)); args.setThis(ObjectValue(*holder)); args[0] = StringValue(keyStr); args[1] = vp; if (!Invoke(cx, args)) return false; vp.set(args.rval()); } /* Step 4. */ if (vp.get().isObject()) { JSObject &obj = vp.get().toObject(); if (ObjectClassIs(obj, ESClass_Number, cx)) { double d; if (!ToNumber(cx, vp, &d)) return false; vp.set(NumberValue(d)); } else if (ObjectClassIs(obj, ESClass_String, cx)) { JSString *str = ToStringSlow(cx, vp); if (!str) return false; vp.set(StringValue(str)); } else if (ObjectClassIs(obj, ESClass_Boolean, cx)) { if (!BooleanGetPrimitiveValue(cx, obj, vp.address())) return false; JS_ASSERT(vp.get().isBoolean()); } } return true; }
bool js::DirectEvalStringFromIon(JSContext *cx, HandleObject scopeobj, HandleScript callerScript, HandleValue thisValue, HandleString str, jsbytecode *pc, MutableHandleValue vp) { AssertInnerizedScopeChain(cx, *scopeobj); Rooted<GlobalObject*> scopeObjGlobal(cx, &scopeobj->global()); if (!GlobalObject::isRuntimeCodeGenEnabled(cx, scopeObjGlobal)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CSP_BLOCKED_EVAL); return false; } // ES5 15.1.2.1 steps 2-8. unsigned staticLevel = callerScript->staticLevel() + 1; Rooted<JSFlatString*> flatStr(cx, str->ensureFlat(cx)); if (!flatStr) return false; size_t length = flatStr->length(); ConstTwoByteChars chars(flatStr->chars(), length); EvalJSONResult ejr = TryEvalJSON(cx, callerScript, chars, length, vp); if (ejr != EvalJSON_NotJSON) return ejr == EvalJSON_Success; EvalScriptGuard esg(cx); esg.lookupInEvalCache(flatStr, callerScript, pc); if (!esg.foundScript()) { JSScript *maybeScript; const char *filename; unsigned lineno; JSPrincipals *originPrincipals; uint32_t pcOffset; DescribeScriptedCallerForCompilation(cx, &maybeScript, &filename, &lineno, &pcOffset, &originPrincipals, CALLED_FROM_JSOP_EVAL); const char *introducerFilename = filename; if (maybeScript && maybeScript->scriptSource()->introducerFilename()) introducerFilename = maybeScript->scriptSource()->introducerFilename(); CompileOptions options(cx); options.setFileAndLine(filename, 1) .setCompileAndGo(true) .setForEval(true) .setNoScriptRval(false) .setOriginPrincipals(originPrincipals) .setIntroductionInfo(introducerFilename, "eval", lineno, maybeScript, pcOffset); SourceBufferHolder srcBuf(chars.get(), length, SourceBufferHolder::NoOwnership); JSScript *compiled = frontend::CompileScript(cx, &cx->tempLifoAlloc(), scopeobj, callerScript, options, srcBuf, flatStr, staticLevel); if (!compiled) return false; esg.setNewScript(compiled); } // Primitive 'this' values should have been filtered out by Ion. If boxed, // the calling frame cannot be updated to store the new object. JS_ASSERT(thisValue.isObject() || thisValue.isUndefined() || thisValue.isNull()); return ExecuteKernel(cx, esg.script(), *scopeobj, thisValue, ExecuteType(DIRECT_EVAL), NullFramePtr() /* evalInFrame */, vp.address()); }
bool ToUnicode(JSContext* cx, const char* src, MutableHandleValue rval) { nsresult rv; if (!mDecoder) { // use app default locale nsCOMPtr<nsILocaleService> localeService = do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv); if (NS_SUCCEEDED(rv)) { nsCOMPtr<nsILocale> appLocale; rv = localeService->GetApplicationLocale(getter_AddRefs(appLocale)); if (NS_SUCCEEDED(rv)) { nsAutoString localeStr; rv = appLocale-> GetCategory(NS_LITERAL_STRING(NSILOCALE_TIME), localeStr); MOZ_ASSERT(NS_SUCCEEDED(rv), "failed to get app locale info"); nsCOMPtr<nsIPlatformCharset> platformCharset = do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &rv); if (NS_SUCCEEDED(rv)) { nsAutoCString charset; rv = platformCharset->GetDefaultCharsetForLocale(localeStr, charset); if (NS_SUCCEEDED(rv)) { // get/create unicode decoder for charset nsCOMPtr<nsICharsetConverterManager> ccm = do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv); if (NS_SUCCEEDED(rv)) ccm->GetUnicodeDecoder(charset.get(), getter_AddRefs(mDecoder)); } } } } } int32_t srcLength = strlen(src); if (mDecoder) { int32_t unicharLength = srcLength; char16_t *unichars = (char16_t *)JS_malloc(cx, (srcLength + 1) * sizeof(char16_t)); if (unichars) { rv = mDecoder->Convert(src, &srcLength, unichars, &unicharLength); if (NS_SUCCEEDED(rv)) { // terminate the returned string unichars[unicharLength] = 0; // nsIUnicodeDecoder::Convert may use fewer than srcLength PRUnichars if (unicharLength + 1 < srcLength + 1) { char16_t *shrunkUnichars = (char16_t *)JS_realloc(cx, unichars, (unicharLength + 1) * sizeof(char16_t)); if (shrunkUnichars) unichars = shrunkUnichars; } JSString *str = JS_NewUCString(cx, reinterpret_cast<jschar*>(unichars), unicharLength); if (str) { rval.setString(str); return true; } } JS_free(cx, unichars); } } xpc::Throw(cx, NS_ERROR_OUT_OF_MEMORY); return false; }
bool JavaScriptShared::fromVariant(JSContext* cx, const JSVariant& from, MutableHandleValue to) { switch (from.type()) { case JSVariant::TUndefinedVariant: to.set(UndefinedValue()); return true; case JSVariant::TNullVariant: to.set(NullValue()); return true; case JSVariant::TObjectVariant: { JSObject* obj = fromObjectVariant(cx, from.get_ObjectVariant()); if (!obj) return false; to.set(ObjectValue(*obj)); return true; } case JSVariant::TSymbolVariant: { Symbol* sym = fromSymbolVariant(cx, from.get_SymbolVariant()); if (!sym) return false; to.setSymbol(sym); return true; } case JSVariant::Tdouble: to.set(JS_NumberValue(from.get_double())); return true; case JSVariant::Tbool: to.setBoolean(from.get_bool()); return true; case JSVariant::TnsString: { const nsString& old = from.get_nsString(); JSString* str = JS_NewUCStringCopyN(cx, old.BeginReading(), old.Length()); if (!str) return false; to.set(StringValue(str)); return true; } case JSVariant::TJSIID: { nsID iid; const JSIID& id = from.get_JSIID(); ConvertID(id, &iid); JSCompartment* compartment = GetContextCompartment(cx); RootedObject global(cx, JS_GetGlobalForCompartmentOrNull(cx, compartment)); JSObject* obj = xpc_NewIDObject(cx, global, iid); if (!obj) return false; to.set(ObjectValue(*obj)); return true; } default: MOZ_CRASH("NYI"); return false; } }
static bool GetLocationProperty(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp) { #if !defined(XP_WIN) && !defined(XP_UNIX) //XXX: your platform should really implement this return false; #else JS::RootedScript script(cx); JS_DescribeScriptedCaller(cx, &script, nullptr); const char *filename = JS_GetScriptFilename(cx, script); if (filename) { nsresult rv; nsCOMPtr<nsIXPConnect> xpc = do_GetService(kXPConnectServiceContractID, &rv); #if defined(XP_WIN) // convert from the system codepage to UTF-16 int bufferSize = MultiByteToWideChar(CP_ACP, 0, filename, -1, nullptr, 0); nsAutoString filenameString; filenameString.SetLength(bufferSize); MultiByteToWideChar(CP_ACP, 0, filename, -1, (LPWSTR)filenameString.BeginWriting(), filenameString.Length()); // remove the null terminator filenameString.SetLength(bufferSize - 1); // replace forward slashes with backslashes, // since nsLocalFileWin chokes on them char16_t* start = filenameString.BeginWriting(); char16_t* end = filenameString.EndWriting(); while (start != end) { if (*start == L'/') *start = L'\\'; start++; } #elif defined(XP_UNIX) NS_ConvertUTF8toUTF16 filenameString(filename); #endif nsCOMPtr<nsIFile> location; if (NS_SUCCEEDED(rv)) { rv = NS_NewLocalFile(filenameString, false, getter_AddRefs(location)); } if (!location && gWorkingDirectory) { // could be a relative path, try appending it to the cwd // and then normalize nsAutoString absolutePath(*gWorkingDirectory); absolutePath.Append(filenameString); rv = NS_NewLocalFile(absolutePath, false, getter_AddRefs(location)); } if (location) { nsCOMPtr<nsIXPConnectJSObjectHolder> locationHolder; bool symlink; // don't normalize symlinks, because that's kind of confusing if (NS_SUCCEEDED(location->IsSymlink(&symlink)) && !symlink) location->Normalize(); rv = xpc->WrapNative(cx, obj, location, NS_GET_IID(nsIFile), getter_AddRefs(locationHolder)); if (NS_SUCCEEDED(rv) && locationHolder->GetJSObject()) { vp.set(OBJECT_TO_JSVAL(locationHolder->GetJSObject())); } } } return true; #endif }