// For prototype chain to install cross-site thunk. // When we change prototype using __proto__, those prototypes might not have cross-site thunks // installed even though the CEO is accessed from a different context. During ChangePrototype time // we don't really know where the requestContext is. // Force installing cross-site thunk for all prototype changes. It's a relatively less frequently used // scenario. void CrossSite::ForceCrossSiteThunkOnPrototypeChain(RecyclableObject* object) { if (TaggedNumber::Is(object)) { return; } while (DynamicType::Is(object->GetTypeId()) && !JavascriptProxy::Is(object)) { DynamicObject* dynamicObject = DynamicObject::UnsafeFromVar(object); if (!dynamicObject->IsCrossSiteObject() && !dynamicObject->IsExternal()) { // force to install cross-site thunk on prototype objects. dynamicObject->MarshalToScriptContext(nullptr); } object = object->GetPrototype(); } return; }
Var CrossSite::MarshalVarInner(ScriptContext* scriptContext, __in Js::RecyclableObject* object, bool fRequestWrapper) { if (scriptContext == object->GetScriptContext()) { if (DoRequestWrapper(object, fRequestWrapper)) { // If we get here then we need to either wrap in the caller's type system or we need to return undefined. // VBScript will pass in the scriptContext (requestContext) from the JavascriptDispatch and this will be the // same as the object's script context and so we have to safely pretend this value doesn't exist. return scriptContext->GetLibrary()->GetUndefined(); } return object; } AssertMsg(scriptContext->GetThreadContext() == object->GetScriptContext()->GetThreadContext(), "ScriptContexts should belong to same threadcontext for marshalling."); // In heapenum, we are traversing through the object graph to dump out the content of recyclable objects. The content // of the objects are duplicated to the heapenum result, and we are not storing/changing the object graph during heap enum. // We don't actually need to do cross site thunk here. if (scriptContext->GetRecycler()->IsHeapEnumInProgress()) { return object; } #if ENABLE_TTD if (scriptContext->IsTTDSnapshotOrInflateInProgress()) { return object; } #endif // Marshaling should not cause any re-entrancy. JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); #if ENABLE_COPYONACCESS_ARRAY JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(object); #endif TypeId typeId = object->GetTypeId(); AssertMsg(typeId != TypeIds_Enumerator, "enumerator shouldn't be marshalled here"); // At the moment the mental model for WithScopeObject Marshaling is this: // Are we trying to marshal a WithScopeObject in the Frame Display? - then 1) unwrap in MarshalFrameDisplay, // 2) marshal the wrapped object, 3) Create a new WithScopeObject in the current scriptContext and re-wrap. // We can avoid copying the WithScopeObject because it has no properties and never should. // Thus creating a new WithScopeObject per context in MarshalFrameDisplay should be kosher. // If it is not a FrameDisplay then we should not marshal. We can wrap cross context objects with a // withscopeObject in a different context. When we unwrap for property lookups and the wrapped object // is cross context, then we marshal the wrapped object into the current scriptContext, thus avoiding // the need to copy the WithScopeObject itself. Thus We don't have to handle marshaling the WithScopeObject // in non-FrameDisplay cases. AssertMsg(typeId != TypeIds_WithScopeObject, "WithScopeObject shouldn't be marshalled here"); if (StaticType::Is(typeId)) { TTD_XSITE_LOG(object->GetScriptContext(), "CloneToScriptContext", object); return object->CloneToScriptContext(scriptContext); } if (typeId == TypeIds_ModuleRoot) { RootObjectBase *moduleRoot = static_cast<RootObjectBase*>(object); HostObjectBase * hostObject = moduleRoot->GetHostObject(); // When marshaling module root, all we need is the host object. // So, if the module root which is being marshaled has host object, marshal it. if (hostObject) { TTD_XSITE_LOG(object->GetScriptContext(), "hostObject", hostObject); Var hostDispatch = hostObject->GetHostDispatchVar(); return CrossSite::MarshalVar(scriptContext, hostDispatch); } } if (typeId == TypeIds_Function) { if (object == object->GetScriptContext()->GetLibrary()->GetDefaultAccessorFunction() ) { TTD_XSITE_LOG(object->GetScriptContext(), "DefaultAccessorFunction", object); return scriptContext->GetLibrary()->GetDefaultAccessorFunction(); } if (DoRequestWrapper(object, fRequestWrapper)) { TTD_XSITE_LOG(object->GetScriptContext(), "CreateWrappedExternalFunction", object); // Marshal as a cross-site thunk if necessary before re-wrapping in an external function thunk. MarshalVarInner(scriptContext, object, false); return scriptContext->GetLibrary()->CreateWrappedExternalFunction(static_cast<JavascriptExternalFunction*>(object)); } } // We have an object marshaled, we need to keep track of the related script context // so optimization overrides can be updated as a group scriptContext->optimizationOverrides.Merge(&object->GetScriptContext()->optimizationOverrides); DynamicObject * dynamicObject = DynamicObject::FromVar(object); if (!dynamicObject->IsExternal()) { if (!dynamicObject->IsCrossSiteObject()) { if (JavascriptProxy::Is(dynamicObject)) { // We don't need to marshal the prototype chain in the case of Proxy. Otherwise we will go to the user code. TTD_XSITE_LOG(object->GetScriptContext(), "MarshalDynamicObject", object); MarshalDynamicObject(scriptContext, dynamicObject); } else { TTD_XSITE_LOG(object->GetScriptContext(), "MarshalDynamicObjectAndPrototype", object); MarshalDynamicObjectAndPrototype(scriptContext, dynamicObject); } } } else { MarshalPrototypeChain(scriptContext, dynamicObject); if (Js::JavascriptConversion::IsCallable(dynamicObject)) { TTD_XSITE_LOG(object->GetScriptContext(), "MarshalToScriptContext", object); dynamicObject->MarshalToScriptContext(scriptContext); } } return dynamicObject; }