/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sts=4 et sw=4 tw=99: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */#include"vm/ProxyObject.h"#include"jscompartment.h"#include"proxy/DeadObjectProxy.h"#include"proxy/ScriptedProxyHandler.h"#include"jsobjinlines.h"usingnamespacejs;/* static */ProxyObject*ProxyObject::New(JSContext*cx,constBaseProxyHandler*handler,HandleValuepriv,TaggedProtoproto_,constProxyOptions&options){Rooted<TaggedProto>proto(cx,proto_);constClass*clasp=options.clasp();MOZ_ASSERT(isValidProxyClass(clasp));MOZ_ASSERT(clasp->shouldDelayMetadataBuilder());MOZ_ASSERT_IF(proto.isObject(),cx->compartment()==proto.toObject()->compartment());MOZ_ASSERT(clasp->hasFinalize());/* * Eagerly mark properties unknown for proxies, so we don't try to track * their properties and so that we don't need to walk the compartment if * their prototype changes later. But don't do this for DOM proxies, * because we want to be able to keep track of them in typesets in useful * ways. */if(proto.isObject()&&!options.singleton()&&!clasp->isDOMClass()){RootedObjectprotoObj(cx,proto.toObject());if(!JSObject::setNewGroupUnknown(cx,clasp,protoObj))returnnullptr;}// Ensure that the wrapper has the same lifetime assumptions as the// wrappee. Prefer to allocate in the nursery, when possible.NewObjectKindnewKind=NurseryAllocatedProxy;if(options.singleton()){MOZ_ASSERT(priv.isGCThing()&&priv.toGCThing()->isTenured());newKind=SingletonObject;}elseif((priv.isGCThing()&&priv.toGCThing()->isTenured())||!handler->canNurseryAllocate()||!handler->finalizeInBackground(priv)){newKind=TenuredObject;}gc::AllocKindallocKind=gc::GetGCObjectKind(clasp);if(handler->finalizeInBackground(priv))allocKind=GetBackgroundAllocKind(allocKind);AutoSetNewObjectMetadatametadata(cx);// Note: this will initialize the object's |data| to strange values, but we// will immediately overwrite those below.RootedObjectobj(cx,NewObjectWithGivenTaggedProto(cx,clasp,proto,allocKind,newKind));if(!obj)returnnullptr;Rooted<ProxyObject*>proxy(cx,&obj->as<ProxyObject>());new(proxy->data.values)detail::ProxyValueArray;proxy->data.handler=handler;proxy->setCrossCompartmentPrivate(priv);/* Don't track types of properties of non-DOM and non-singleton proxies. */if(newKind!=SingletonObject&&!clasp->isDOMClass())MarkObjectGroupUnknownProperties(cx,proxy->group());returnproxy;}gc::AllocKindProxyObject::allocKindForTenure()const{gc::AllocKindallocKind=gc::GetGCObjectKind(group()->clasp());if(data.handler->finalizeInBackground(const_cast<ProxyObject*>(this)->private_()))allocKind=GetBackgroundAllocKind(allocKind);returnallocKind;}/* static */size_tProxyObject::objectMovedDuringMinorGC(TenuringTracer*trc,JSObject*dst,JSObject*src){ProxyObject&psrc=src->as<ProxyObject>();ProxyObject&pdst=dst->as<ProxyObject>();// We're about to sweep the nursery heap, so migrate the inline// ProxyValueArray to the malloc heap if they were nursery allocated.if(trc->runtime()->gc.nursery.isInside(psrc.data.values))pdst.data.values=js_new<detail::ProxyValueArray>(*psrc.data.values);elsetrc->runtime()->gc.nursery.removeMallocedBuffer(psrc.data.values);returnsizeof(detail::ProxyValueArray);}voidProxyObject::setCrossCompartmentPrivate(constValue&priv){*slotOfPrivate()=priv;}voidProxyObject::setSameCompartmentPrivate(constValue&priv){MOZ_ASSERT(IsObjectValueInCompartment(priv,compartment()));*slotOfPrivate()=priv;}voidProxyObject::nuke(){// When nuking scripted proxies, isCallable and isConstructor values for// the proxy needs to be preserved. Do this before clearing the target.uint32_tcallable=handler()->isCallable(this)?ScriptedProxyHandler::IS_CALLABLE:0;uint32_tconstructor=handler()->isConstructor(this)?ScriptedProxyHandler::IS_CONSTRUCTOR:0;setExtra(ScriptedProxyHandler::IS_CALLCONSTRUCT_EXTRA,PrivateUint32Value(callable|constructor));// Clear the target reference.setSameCompartmentPrivate(NullValue());// Update the handler to make this a DeadObjectProxy.setHandler(&DeadObjectProxy::singleton);// The proxy's extra slots are not cleared and will continue to be// traced. This avoids the possibility of triggering write barriers while// nuking proxies in dead compartments which could otherwise cause those// compartments to be kept alive. Note that these are slots cannot hold// cross compartment pointers, so this cannot cause the target compartment// to leak.}JS_FRIEND_API(void)js::SetValueInProxy(Value*slot,constValue&value){// Slots in proxies are not GCPtrValues, so do a cast whenever assigning// values to them which might trigger a barrier.*reinterpret_cast<GCPtrValue*>(slot)=value;}