blob: f07a2a074db7151f0b4a311a983ccb81fe75b1a3 [file]
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#include "RuntimeLanguagePch.h"
#include "Library/StackScriptFunction.h"
namespace Js
{
void JavascriptExceptionObject::FillError(Js::JavascriptExceptionContext& exceptionContext, ScriptContext *scriptContext, HostWrapperCreateFuncType hostWrapperCreateFunc)
{
this->scriptContext = scriptContext;
this->exceptionContext = exceptionContext;
this->SetHostWrapperCreateFunc(hostWrapperCreateFunc);
}
void JavascriptExceptionObject::ClearError()
{
Assert(this->isPendingExceptionObject);
memset(this, 0, sizeof(JavascriptExceptionObject));
this->isPendingExceptionObject = true;
}
JavascriptExceptionObject* JavascriptExceptionObject::CloneIfStaticExceptionObject(ScriptContext* scriptContext)
{
Assert(scriptContext);
ThreadContext *threadContext = scriptContext->GetThreadContext();
JavascriptExceptionObject* exceptionObject = this;
if (this == threadContext->GetPendingOOMErrorObject())
{
AssertMsg(this->thrownObject == NULL, "ThrownObject should be NULL since at time of OOM we will not be able to allocate the thrownObject");
// Let's hope that unwinding has released enough pointers that the
// recycler will find some memory to allocate the real OutOfMemory object.
// If not, it will rethrow outOfMemory
Var thrownObject = scriptContext->GetLibrary()->CreateOutOfMemoryError();
exceptionObject = RecyclerNew(scriptContext->GetRecycler(),
JavascriptExceptionObject,
thrownObject,
scriptContext,
&this->exceptionContext);
threadContext->ClearPendingOOMError();
}
if (this == threadContext->GetPendingSOErrorObject())
{
Var thrownObject = NULL;
if (this->thrownObject == NULL)
{
AssertMsg(threadContext->IsJSRT(), "ThrownObject could be NULL for Jsrt scenarios because it is cleared in ~EnterScriptEnd. For non-jsrt cases, we should always have an allocated thrown object.");
thrownObject = scriptContext->GetLibrary()->CreateStackOverflowError();
}
else
{
thrownObject = this->GetThrownObject(scriptContext);
}
exceptionObject = RecyclerNew(scriptContext->GetRecycler(),
JavascriptExceptionObject,
thrownObject,
scriptContext,
&this->exceptionContext);
threadContext->ClearPendingSOError();
}
return exceptionObject;
}
// Returns NULL if the exception object is the static out of memory object.
Var JavascriptExceptionObject::GetThrownObject(ScriptContext * requestingScriptContext)
{
// requestingScriptContext == this->scriptContext when we have A->(cross site thunk)B->(IDispatch)A using and nested A window return
// exception backup. we can go back down to normal code path below.
if (requestingScriptContext != nullptr && hostWrapperCreateFunc != nullptr && (requestingScriptContext != this->scriptContext))
{
return hostWrapperCreateFunc(thrownObject, scriptContext, requestingScriptContext);
}
// We can have cross script context throw in both fastDOM and IE8 mode now.
if (requestingScriptContext && (thrownObject != nullptr))
{
Var rethrownObject = CrossSite::MarshalVar(requestingScriptContext, thrownObject);
// For now, there is no known host for which we need to support cross-domain
// scenario for JSRT. So skip the cross domain check for now.
if (scriptContext->GetThreadContext()->IsJSRT())
{
return rethrownObject;
}
if (rethrownObject)
{
if (JavascriptError::Is(rethrownObject))
{
JavascriptError* jsErrorObject = JavascriptError::FromVar(rethrownObject);
if (jsErrorObject->GetScriptContext() != requestingScriptContext )
{
Assert(requestingScriptContext->GetHostScriptContext());
HRESULT hr = requestingScriptContext->GetHostScriptContext()->CheckCrossDomainScriptContext(jsErrorObject->GetScriptContext());
if ( S_OK != hr )
{
JavascriptError* jsNewErrorObject = requestingScriptContext->GetLibrary()->CreateTypeError();
JavascriptError::SetErrorMessage(jsNewErrorObject, VBSERR_PermissionDenied, nullptr, requestingScriptContext);
return jsNewErrorObject;
}
}
}
else
{
if (RecyclableObject::Is(rethrownObject))
{
if (((RecyclableObject*)rethrownObject)->GetScriptContext() != requestingScriptContext)
{
Assert(requestingScriptContext->GetHostScriptContext());
HRESULT hrSecurityCheck = requestingScriptContext->GetHostScriptContext()->CheckCrossDomainScriptContext(((RecyclableObject*)rethrownObject)->GetScriptContext());
if (hrSecurityCheck != S_OK)
{
AssertMsg(hrSecurityCheck != E_ACCESSDENIED, "Invalid cross domain throw. HRESULT must either be S_OK or !E_ACCESSDENIED.");
// DOM should not throw cross domain object at all. This is defend in depth that we'll return something in requestScriptContext if they do throw
// something bad.
return requestingScriptContext->GetLibrary()->GetUndefined();
}
}
}
}
}
return rethrownObject;
}
return thrownObject;
}
FunctionBody* JavascriptExceptionObject::GetFunctionBody() const
{
// If it is a throwing function; it must be deserialized
if (exceptionContext.ThrowingFunction())
{
ParseableFunctionInfo *info = exceptionContext.ThrowingFunction()->GetParseableFunctionInfo();
if (info->IsFunctionBody())
{
return info->GetFunctionBody();
}
}
return nullptr;
}
JavascriptExceptionContext::StackFrame::StackFrame(JavascriptFunction* func, const JavascriptStackWalker& walker, bool initArgumentTypes)
{
this->functionBody = func->GetFunctionBody();
if (this->functionBody)
{
this->byteCodeOffset = walker.GetByteCodeOffset();
}
else
{
this->name = walker.GetCurrentNativeLibraryEntryName();
}
if (this->functionBody && initArgumentTypes)
{
this->argumentTypes.Init(walker);
}
}
bool JavascriptExceptionContext::StackFrame::IsScriptFunction() const
{
return functionBody != nullptr;
}
// Get function body -- available for script functions, null for native library builtin functions.
FunctionBody* JavascriptExceptionContext::StackFrame::GetFunctionBody() const
{
return functionBody;
}
LPCWSTR JavascriptExceptionContext::StackFrame::GetFunctionName() const
{
return IsScriptFunction() ?
GetFunctionBody()->GetExternalDisplayName() : PointerValue(this->name);
}
// Get function name with arguments info. Used by script WER.
HRESULT JavascriptExceptionContext::StackFrame::GetFunctionNameWithArguments(_In_ LPCWSTR *outResult) const
{
PCWSTR name = GetFunctionName();
HRESULT hr = S_OK;
if (IsScriptFunction())
{
hr = argumentTypes.ToString(name, functionBody->GetScriptContext(), outResult);
}
else
{
*outResult = name;
}
return hr;
}
void JavascriptExceptionContext::SetThrowingFunction(JavascriptFunction * function, uint32 byteCodeOffset, void * returnAddress)
{
// Unfortunately, window.onerror can ask for argument.callee.caller
// and we will return the thrown function, but the stack already unwound.
// We will need to just box the function
m_throwingFunction = StackScriptFunction::EnsureBoxed(BOX_PARAM(function, returnAddress, _u("throw")));
m_throwingFunctionByteCodeOffset = byteCodeOffset;
}
#if ENABLE_DEBUG_STACK_BACK_TRACE
void JavascriptExceptionObject::FillStackBackTrace()
{
// Note: this->scriptContext can be NULL when we throw Out Of Memory exception.
if (this->stackBackTrace == NULL && this->scriptContext != NULL)
{
Recycler* recycler = scriptContext->GetThreadContext()->GetRecycler();
HRESULT hr = NOERROR;
BEGIN_TRANSLATE_OOM_TO_HRESULT_NESTED
{
this->stackBackTrace = StackBackTrace::Capture(recycler, JavascriptExceptionObject::StackToSkip, JavascriptExceptionObject::StackTraceDepth);
}
END_TRANSLATE_OOM_TO_HRESULT(hr)
}
}
#endif
void JavascriptExceptionObject::Insert(
Field(JavascriptExceptionObject*)* head, JavascriptExceptionObject* item)
{
Assert(!item->next);
item->next = *head;
*head = item;
}
void JavascriptExceptionObject::Remove(
Field(JavascriptExceptionObject*)* head, JavascriptExceptionObject* item)
{
// Typically Insert/Remove happens in reversed order and item should be
// the front one. Loop the whole list to prevent unexpected order messup.
for (auto p = head; *p; p = &(*p)->next)
{
if (*p == item)
{
*p = item->next;
item->next = nullptr;
return;
}
}
Assert(false); // item not in list unexpected
}
//
// Support JavascriptException implementation
//
void SaveTempUncaughtException(ThreadContext* threadContext, Js::JavascriptExceptionObject* exceptionObject)
{
threadContext->SaveTempUncaughtException(exceptionObject);
}
void ClearTempUncaughtException(ThreadContext* threadContext, Js::JavascriptExceptionObject* exceptionObject)
{
threadContext->ClearTempUncaughtException(exceptionObject);
}
}