// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
#include "ClearScriptV8Native.h"
//-----------------------------------------------------------------------------
// FromMaybeScope
//-----------------------------------------------------------------------------
class FromMaybeScope final
{
};
//-----------------------------------------------------------------------------
// FromMaybeFailure
//-----------------------------------------------------------------------------
class FromMaybeFailure final
{
};
//-----------------------------------------------------------------------------
// local helper functions
//-----------------------------------------------------------------------------
template
inline T FromMaybe(FromMaybeScope& /*scope*/, const v8::Maybe& maybe)
{
T result;
if (maybe.To(&result))
{
return result;
}
throw FromMaybeFailure();
}
//-----------------------------------------------------------------------------
template
inline v8::Local FromMaybe(FromMaybeScope& /*scope*/, const v8::MaybeLocal& maybe)
{
v8::Local result;
if (maybe.ToLocal(&result))
{
return result;
}
throw FromMaybeFailure();
}
//-----------------------------------------------------------------------------
template
inline T FromMaybeDefault(const v8::Maybe& maybe, const T& defaultValue = T())
{
return maybe.FromMaybe(defaultValue);
}
//-----------------------------------------------------------------------------
template
inline v8::Local FromMaybeDefault(const v8::MaybeLocal& maybe, const v8::Local& defaultValue = v8::Local())
{
return maybe.FromMaybe(defaultValue);
}
//-----------------------------------------------------------------------------
inline v8::Local ValueAsString(const v8::Local& hValue)
{
return (!hValue.IsEmpty() && hValue->IsString()) ? hValue.As() : v8::Local();
}
//-----------------------------------------------------------------------------
inline v8::Local ValueAsObject(const v8::Local& hValue)
{
return (!hValue.IsEmpty() && hValue->IsObject()) ? hValue.As() : v8::Local();
}
//-----------------------------------------------------------------------------
inline v8::Local ValueAsExternal(const v8::Local& hValue)
{
return (!hValue.IsEmpty() && hValue->IsExternal()) ? hValue.As() : v8::Local();
}
//-----------------------------------------------------------------------------
inline v8::Local ValueAsBigInt(const v8::Local& hValue)
{
return (!hValue.IsEmpty() && hValue->IsBigInt()) ? hValue.As() : v8::Local();
}
//-----------------------------------------------------------------------------
template
static V8ContextImpl* GetContextImplFromHolder(const TInfo& info)
{
auto hHolder = info.Holder();
if (!hHolder.IsEmpty() && hHolder->InternalFieldCount() > 0)
{
auto hField = hHolder->GetInternalField(0);
if (!hField.IsEmpty() && !hField->IsUndefined())
{
return static_cast(hHolder->GetAlignedPointerFromInternalField(0));
}
}
return nullptr;
}
//-----------------------------------------------------------------------------
template
static V8ContextImpl* GetContextImplFromData(const TInfo& info)
{
auto hContextImpl = ::ValueAsExternal(info.Data());
return !hContextImpl.IsEmpty() ? static_cast(hContextImpl->Value()) : nullptr;
}
//-----------------------------------------------------------------------------
// V8ContextImpl implementation
//-----------------------------------------------------------------------------
#define BEGIN_ISOLATE_SCOPE \
{ \
DISABLE_WARNING(4456) /* declaration hides previous local declaration */ \
V8IsolateImpl::Scope t_IsolateScope(*m_spIsolateImpl); \
DEFAULT_WARNING(4456)
#define END_ISOLATE_SCOPE \
IGNORE_UNUSED(t_IsolateScope); \
}
#define FROM_MAYBE_TRY \
{ \
DISABLE_WARNING(4456) /* declaration hides previous local declaration */ \
FromMaybeScope t_FromMaybeScope; \
DEFAULT_WARNING(4456) \
try \
{
#define FROM_MAYBE_CATCH \
IGNORE_UNUSED(t_FromMaybeScope); \
} \
catch (const FromMaybeFailure&) \
{ \
#define FROM_MAYBE_END \
IGNORE_UNUSED(t_FromMaybeScope); \
} \
}
#define FROM_MAYBE_CATCH_CONSUME \
FROM_MAYBE_CATCH \
FROM_MAYBE_END
#define FROM_MAYBE(...) \
(::FromMaybe(t_FromMaybeScope, __VA_ARGS__))
#define FROM_MAYBE_DEFAULT(...) \
(::FromMaybeDefault(__VA_ARGS__))
#define BEGIN_CONTEXT_SCOPE \
{ \
DISABLE_WARNING(4456) /* declaration hides previous local declaration */ \
Scope t_ContextScope(this); \
DEFAULT_WARNING(4456)
#define END_CONTEXT_SCOPE \
IGNORE_UNUSED(t_ContextScope); \
}
#define BEGIN_EXECUTION_SCOPE \
{ \
DISABLE_WARNING(4456) /* declaration hides previous local declaration */ \
V8IsolateImpl::ExecutionScope t_ExecutionScope(*m_spIsolateImpl); \
V8IsolateImpl::TryCatch t_TryCatch(*m_spIsolateImpl); \
DEFAULT_WARNING(4456)
#define END_EXECUTION_SCOPE \
IGNORE_UNUSED(t_TryCatch); \
IGNORE_UNUSED(t_ExecutionScope); \
}
#define EXECUTION_STARTED \
(t_ExecutionScope.ExecutionStarted())
#define BEGIN_DOCUMENT_SCOPE(DOCUMENT_INFO) \
{ \
DISABLE_WARNING(4456) /* declaration hides previous local declaration */ \
V8IsolateImpl::DocumentScope t_DocumentScope(*m_spIsolateImpl, DOCUMENT_INFO); \
DEFAULT_WARNING(4456)
#define END_DOCUMENT_SCOPE \
IGNORE_UNUSED(t_DocumentScope); \
}
#define VERIFY(RESULT) \
(Verify(t_ExecutionScope, t_TryCatch, RESULT))
#define VERIFY_MAYBE(RESULT) \
VERIFY(FROM_MAYBE_DEFAULT(RESULT))
#define VERIFY_CHECKPOINT() \
(Verify(t_ExecutionScope, t_TryCatch))
#define CALLBACK_RETURN(RESULT) \
BEGIN_COMPOUND_MACRO \
info.GetReturnValue().Set(RESULT); \
return; \
END_COMPOUND_MACRO
//-----------------------------------------------------------------------------
static std::atomic s_InstanceCount(0);
//-----------------------------------------------------------------------------
V8ContextImpl::V8ContextImpl(V8IsolateImpl* pIsolateImpl, const StdString& name):
V8ContextImpl(SharedPtr(pIsolateImpl), name, Options())
{
}
//-----------------------------------------------------------------------------
V8ContextImpl::V8ContextImpl(SharedPtr&& spIsolateImpl, const StdString& name, const Options& options):
m_Name(name),
m_spIsolateImpl(std::move(spIsolateImpl)),
m_DateTimeConversionEnabled(HasFlag(options.Flags, Flags::EnableDateTimeConversion)),
m_HideHostExceptions(HasFlag(options.Flags, Flags::HideHostExceptions)),
m_pvV8ObjectCache(nullptr),
m_AllowHostObjectConstructorCall(false)
{
VerifyNotOutOfMemory();
BEGIN_ISOLATE_SCOPE
FROM_MAYBE_TRY
if (HasFlag(options.Flags, Flags::DisableGlobalMembers))
{
m_hContext = CreatePersistent(CreateContext());
}
else
{
auto hGlobalTemplate = CreateObjectTemplate();
hGlobalTemplate->SetInternalFieldCount(1);
hGlobalTemplate->SetHandler(v8::NamedPropertyHandlerConfiguration(GetGlobalProperty, SetGlobalProperty, QueryGlobalProperty, DeleteGlobalProperty, GetGlobalPropertyNames, v8::Local(), v8::PropertyHandlerFlags::kNonMasking));
hGlobalTemplate->SetHandler(v8::IndexedPropertyHandlerConfiguration(GetGlobalProperty, SetGlobalProperty, QueryGlobalProperty, DeleteGlobalProperty, GetGlobalPropertyIndices));
m_hContext = CreatePersistent(CreateContext(nullptr, hGlobalTemplate));
auto hGlobal = ::ValueAsObject(m_hContext->Global()->GetPrototype());
if (!hGlobal.IsEmpty() && (hGlobal->InternalFieldCount() > 0))
{
hGlobal->SetAlignedPointerInInternalField(0, this);
}
}
auto hContextImpl = CreateExternal(this);
v8::Local hGetIteratorFunction;
v8::Local hGetAsyncIteratorFunction;
v8::Local hGetJsonFunction;
v8::Local hToFunctionFunction;
BEGIN_CONTEXT_SCOPE
m_hContext->SetAlignedPointerInEmbedderData(1, this);
m_hIsHostObjectKey = CreatePersistent(CreateSymbol());
FROM_MAYBE(m_hContext->Global()->Set(m_hContext, CreateString("isHostObjectKey"), m_hIsHostObjectKey));
m_hHostExceptionKey = CreatePersistent(CreateString("hostException"));
m_hCacheKey = CreatePersistent(CreatePrivate());
m_hAccessTokenKey = CreatePersistent(CreatePrivate());
m_hInternalUseOnly = CreatePersistent(CreateString("The invoked function is for ClearScript internal use only"));
m_hStackKey = CreatePersistent(CreateString("stack"));
m_hObjectNotInvocable = CreatePersistent(CreateString("The object does not support invocation"));
m_hMethodOrPropertyNotFound = CreatePersistent(CreateString("Method or property not found"));
m_hPropertyValueNotInvocable = CreatePersistent(CreateString("The property value does not support invocation"));
m_hInvalidModuleRequest = CreatePersistent(CreateString("Invalid module load request"));
m_hConstructorKey = CreatePersistent(CreateString("constructor"));
hGetIteratorFunction = CreateFunctionTemplate(GetHostObjectIterator, hContextImpl);
hGetAsyncIteratorFunction = CreateFunctionTemplate(GetHostObjectAsyncIterator, hContextImpl);
hGetJsonFunction = CreateFunctionTemplate(GetHostObjectJson, hContextImpl);
hToFunctionFunction = CreateFunctionTemplate(CreateFunctionForHostDelegate, hContextImpl);
m_hAccessToken = CreatePersistent(CreateObject());
m_hFlushFunction = CreatePersistent(FROM_MAYBE(v8::Function::New(m_hContext, FlushCallback)));
m_hTerminationException = CreatePersistent(v8::Exception::Error(CreateString("Script execution was interrupted")));
END_CONTEXT_SCOPE
auto hToJSON = CreateString("toJSON");
m_hHostObjectTemplate = CreatePersistent(CreateFunctionTemplate());
m_hHostObjectTemplate->SetClassName(CreateString("HostObject"));
m_hHostObjectTemplate->SetCallHandler(HostObjectConstructorCallHandler, hContextImpl);
m_hHostObjectTemplate->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(GetHostObjectProperty, SetHostObjectProperty, QueryHostObjectProperty, DeleteHostObjectProperty, GetHostObjectPropertyNames, hContextImpl, v8::PropertyHandlerFlags::kNone));
m_hHostObjectTemplate->InstanceTemplate()->SetHandler(v8::IndexedPropertyHandlerConfiguration(GetHostObjectProperty, SetHostObjectProperty, QueryHostObjectProperty, DeleteHostObjectProperty, GetHostObjectPropertyIndices, hContextImpl));
m_hHostObjectTemplate->PrototypeTemplate()->Set(GetIteratorSymbol(), hGetIteratorFunction);
m_hHostObjectTemplate->PrototypeTemplate()->Set(GetAsyncIteratorSymbol(), hGetAsyncIteratorFunction);
m_hHostObjectTemplate->PrototypeTemplate()->Set(hToJSON, hGetJsonFunction);
m_hHostInvocableTemplate = CreatePersistent(CreateFunctionTemplate());
m_hHostInvocableTemplate->SetClassName(CreateString("HostInvocable"));
m_hHostInvocableTemplate->SetCallHandler(HostObjectConstructorCallHandler, hContextImpl);
m_hHostInvocableTemplate->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(GetHostObjectProperty, SetHostObjectProperty, QueryHostObjectProperty, DeleteHostObjectProperty, GetHostObjectPropertyNames, hContextImpl, v8::PropertyHandlerFlags::kNone));
m_hHostInvocableTemplate->InstanceTemplate()->SetHandler(v8::IndexedPropertyHandlerConfiguration(GetHostObjectProperty, SetHostObjectProperty, QueryHostObjectProperty, DeleteHostObjectProperty, GetHostObjectPropertyIndices, hContextImpl));
m_hHostInvocableTemplate->PrototypeTemplate()->Set(GetIteratorSymbol(), hGetIteratorFunction);
m_hHostInvocableTemplate->PrototypeTemplate()->Set(GetAsyncIteratorSymbol(), hGetAsyncIteratorFunction);
m_hHostInvocableTemplate->PrototypeTemplate()->Set(hToJSON, hGetJsonFunction);
m_hHostInvocableTemplate->InstanceTemplate()->SetCallAsFunctionHandler(InvokeHostObject, hContextImpl);
m_hHostDelegateTemplate = CreatePersistent(CreateFunctionTemplate());
m_hHostDelegateTemplate->SetClassName(CreateString("HostDelegate"));
m_hHostDelegateTemplate->SetCallHandler(HostObjectConstructorCallHandler, hContextImpl);
m_hHostDelegateTemplate->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(GetHostObjectProperty, SetHostObjectProperty, QueryHostObjectProperty, DeleteHostObjectProperty, GetHostObjectPropertyNames, hContextImpl, v8::PropertyHandlerFlags::kNone));
m_hHostDelegateTemplate->InstanceTemplate()->SetHandler(v8::IndexedPropertyHandlerConfiguration(GetHostObjectProperty, SetHostObjectProperty, QueryHostObjectProperty, DeleteHostObjectProperty, GetHostObjectPropertyIndices, hContextImpl));
m_hHostDelegateTemplate->PrototypeTemplate()->Set(GetIteratorSymbol(), hGetIteratorFunction);
m_hHostDelegateTemplate->PrototypeTemplate()->Set(GetAsyncIteratorSymbol(), hGetAsyncIteratorFunction);
m_hHostDelegateTemplate->PrototypeTemplate()->Set(hToJSON, hGetJsonFunction);
m_hHostDelegateTemplate->InstanceTemplate()->SetCallAsFunctionHandler(InvokeHostObject, hContextImpl);
m_hHostDelegateTemplate->InstanceTemplate()->SetHostDelegate(); // instructs our patched V8 typeof implementation to return "function"
m_hHostDelegateTemplate->PrototypeTemplate()->Set(CreateString("toFunction"), hToFunctionFunction);
m_pvV8ObjectCache = HostObjectUtil::GetInstance().CreateV8ObjectCache();
m_spIsolateImpl->AddContext(this, options);
FROM_MAYBE_CATCH
Teardown();
throw V8Exception(V8Exception::Type::General, m_Name, StdString(SL("The V8 runtime cannot initialize the script engine because a script exception is pending")), false /*executionStarted*/);
FROM_MAYBE_END
END_ISOLATE_SCOPE
++s_InstanceCount;
}
//-----------------------------------------------------------------------------
size_t V8ContextImpl::GetInstanceCount()
{
return s_InstanceCount;
}
//-----------------------------------------------------------------------------
size_t V8ContextImpl::GetMaxIsolateHeapSize()
{
return m_spIsolateImpl->GetMaxHeapSize();
}
//-----------------------------------------------------------------------------
void V8ContextImpl::SetMaxIsolateHeapSize(size_t value)
{
m_spIsolateImpl->SetMaxHeapSize(value);
}
//-----------------------------------------------------------------------------
double V8ContextImpl::GetIsolateHeapSizeSampleInterval()
{
return m_spIsolateImpl->GetHeapSizeSampleInterval();
}
//-----------------------------------------------------------------------------
void V8ContextImpl::SetIsolateHeapSizeSampleInterval(double value)
{
m_spIsolateImpl->SetHeapSizeSampleInterval(value);
}
//-----------------------------------------------------------------------------
size_t V8ContextImpl::GetMaxIsolateStackUsage()
{
return m_spIsolateImpl->GetMaxStackUsage();
}
//-----------------------------------------------------------------------------
void V8ContextImpl::SetMaxIsolateStackUsage(size_t value)
{
m_spIsolateImpl->SetMaxStackUsage(value);
}
//-----------------------------------------------------------------------------
void V8ContextImpl::CallWithLock(CallWithLockCallback* pCallback, void* pvArg)
{
VerifyNotOutOfMemory();
BEGIN_ISOLATE_SCOPE
(*pCallback)(pvArg);
END_ISOLATE_SCOPE
}
//-----------------------------------------------------------------------------
V8Value V8ContextImpl::GetRootObject()
{
BEGIN_CONTEXT_SCOPE
return ExportValue(m_hContext->Global());
END_CONTEXT_SCOPE
}
//-----------------------------------------------------------------------------
void V8ContextImpl::SetGlobalProperty(const StdString& name, const V8Value& value, bool globalMembers)
{
BEGIN_CONTEXT_SCOPE
FROM_MAYBE_TRY
auto hName = FROM_MAYBE(CreateString(name));
auto hValue = ::ValueAsObject(ImportValue(value));
v8::Local hOldValue;
if (FROM_MAYBE(m_hContext->Global()->HasOwnProperty(m_hContext, hName)))
{
hOldValue = ::ValueAsObject(FROM_MAYBE(m_hContext->Global()->GetRealNamedProperty(m_hContext, hName)));
}
auto result = FROM_MAYBE(m_hContext->Global()->DefineOwnProperty(m_hContext, hName, hValue, v8::ReadOnly));
if (result && globalMembers && !hValue.IsEmpty())
{
if (!hOldValue.IsEmpty())
{
for (auto it = m_GlobalMembersStack.begin(); it != m_GlobalMembersStack.end(); it++)
{
if (it->first == name)
{
Dispose(it->second);
m_GlobalMembersStack.erase(it);
break;
}
}
}
m_GlobalMembersStack.emplace_back(name, CreatePersistent(hValue));
}
FROM_MAYBE_CATCH
throw V8Exception(V8Exception::Type::General, m_Name, StdString(SL("The V8 runtime cannot perform the requested operation because a script exception is pending")), false /*executionStarted*/);
FROM_MAYBE_END
END_CONTEXT_SCOPE
}
//-----------------------------------------------------------------------------
void V8ContextImpl::AwaitDebuggerAndPause()
{
m_spIsolateImpl->AwaitDebuggerAndPause();
}
//-----------------------------------------------------------------------------
void V8ContextImpl::CancelAwaitDebugger()
{
m_spIsolateImpl->CancelAwaitDebugger();
}
//-----------------------------------------------------------------------------
V8Value V8ContextImpl::Execute(const V8DocumentInfo& documentInfo, const StdString& code, bool evaluate)
{
BEGIN_CONTEXT_SCOPE
BEGIN_DOCUMENT_SCOPE(documentInfo)
BEGIN_EXECUTION_SCOPE
FROM_MAYBE_TRY
auto codeDigest = code.GetDigest();
v8::ScriptCompiler::Source source(FROM_MAYBE(CreateString(code)), CreateScriptOrigin(documentInfo));
v8::Local hResult;
if (documentInfo.IsModule())
{
auto hModule = GetCachedModule(documentInfo.GetUniqueId(), codeDigest);
if (hModule.IsEmpty())
{
hModule = VERIFY_MAYBE(CompileModule(&source));
if (hModule.IsEmpty())
{
throw V8Exception(V8Exception::Type::General, m_Name, StdString(SL("Module compilation failed; no additional information was provided by the V8 runtime")), false /*executionStarted*/);
}
CacheModule(documentInfo, codeDigest, hModule);
}
if (hModule->GetStatus() == v8::Module::kUninstantiated)
{
ASSERT_EVAL(VERIFY_MAYBE(hModule->InstantiateModule(m_hContext, V8IsolateImpl::ModuleResolveCallback)));
}
if (hModule->GetStatus() == v8::Module::kInstantiated)
{
hResult = VERIFY_MAYBE(hModule->Evaluate(m_hContext));
if (!hModule->IsGraphAsync() && hResult->IsPromise())
{
auto hPromise = hResult.As();
if (hPromise->State() == v8::Promise::PromiseState::kFulfilled)
{
hResult = hPromise->Result();
}
else if (hPromise->State() == v8::Promise::PromiseState::kRejected)
{
auto hException = hPromise->Result();
if (hException->IsObject())
{
auto hExceptionObject = hException.As();
auto hHostException = FROM_MAYBE(hExceptionObject->Get(m_hContext, m_hHostExceptionKey));
throw V8Exception(V8Exception::Type::General, m_Name, CreateStdString(hExceptionObject), CreateStdString(FROM_MAYBE(hExceptionObject->Get(m_hContext, m_hStackKey))), EXECUTION_STARTED, ExportValue(hException), ExportValue(hHostException));
}
throw V8Exception(V8Exception::Type::General, m_Name, CreateStdString(hException), StdString(), EXECUTION_STARTED, ExportValue(hException), V8Value(V8Value::Undefined));
}
}
}
else
{
evaluate = false;
}
}
else
{
auto hScript = GetCachedScript(documentInfo.GetUniqueId(), codeDigest);
if (hScript.IsEmpty())
{
hScript = VERIFY_MAYBE(CompileUnboundScript(&source));
if (hScript.IsEmpty())
{
throw V8Exception(V8Exception::Type::General, m_Name, StdString(SL("Script compilation failed; no additional information was provided by the V8 runtime")), false /*executionStarted*/);
}
CacheScript(documentInfo, codeDigest, hScript);
}
hResult = VERIFY_MAYBE(hScript->BindToCurrentContext()->Run(m_hContext));
}
if (!evaluate)
{
hResult = GetUndefined();
}
return ExportValue(hResult);
FROM_MAYBE_CATCH
throw V8Exception(V8Exception::Type::General, m_Name, StdString(SL("The V8 runtime cannot perform the requested operation because a script exception is pending")), EXECUTION_STARTED);
FROM_MAYBE_END
END_EXECUTION_SCOPE
END_DOCUMENT_SCOPE
END_CONTEXT_SCOPE
}
//-----------------------------------------------------------------------------
V8ScriptHolder* V8ContextImpl::Compile(const V8DocumentInfo& documentInfo, StdString&& code)
{
BEGIN_CONTEXT_SCOPE
BEGIN_DOCUMENT_SCOPE(documentInfo)
BEGIN_EXECUTION_SCOPE
FROM_MAYBE_TRY
auto codeDigest = code.GetDigest();
v8::ScriptCompiler::Source source(FROM_MAYBE(CreateString(code)), CreateScriptOrigin(documentInfo));
std::unique_ptr upScriptHolder;
if (documentInfo.IsModule())
{
auto hModule = GetCachedModule(documentInfo.GetUniqueId(), codeDigest);
if (hModule.IsEmpty())
{
hModule = VERIFY_MAYBE(CompileModule(&source));
if (hModule.IsEmpty())
{
throw V8Exception(V8Exception::Type::General, m_Name, StdString(SL("Module compilation failed; no additional information was provided by the V8 runtime")), false /*executionStarted*/);
}
CacheModule(documentInfo, codeDigest, hModule);
}
upScriptHolder.reset(new V8ScriptHolderImpl(GetWeakBinding(), ::PtrFromHandle(CreatePersistent(hModule)), documentInfo, codeDigest, std::move(code)));
}
else
{
auto hScript = GetCachedScript(documentInfo.GetUniqueId(), codeDigest);
if (hScript.IsEmpty())
{
hScript = VERIFY_MAYBE(CompileUnboundScript(&source));
if (hScript.IsEmpty())
{
throw V8Exception(V8Exception::Type::General, m_Name, StdString(SL("Script compilation failed; no additional information was provided by the V8 runtime")), false /*executionStarted*/);
}
CacheScript(documentInfo, codeDigest, hScript);
}
upScriptHolder.reset(new V8ScriptHolderImpl(GetWeakBinding(), ::PtrFromHandle(CreatePersistent(hScript)), documentInfo, codeDigest));
}
return upScriptHolder.release();
FROM_MAYBE_CATCH
throw V8Exception(V8Exception::Type::General, m_Name, StdString(SL("The V8 runtime cannot perform the requested operation because a script exception is pending")), EXECUTION_STARTED);
FROM_MAYBE_END
END_EXECUTION_SCOPE
END_DOCUMENT_SCOPE
END_CONTEXT_SCOPE
}
//-----------------------------------------------------------------------------
V8ScriptHolder* V8ContextImpl::Compile(const V8DocumentInfo& documentInfo, StdString&& code, V8CacheType cacheType, std::vector& cacheBytes)
{
if (cacheType == V8CacheType::None)
{
cacheBytes.clear();
return Compile(documentInfo, std::move(code));
}
BEGIN_CONTEXT_SCOPE
BEGIN_DOCUMENT_SCOPE(documentInfo)
BEGIN_EXECUTION_SCOPE
FROM_MAYBE_TRY
auto codeDigest = code.GetDigest();
v8::ScriptCompiler::Source source(FROM_MAYBE(CreateString(code)), CreateScriptOrigin(documentInfo));
std::unique_ptr upScriptHolder;
std::unique_ptr upCachedData;
if (documentInfo.IsModule())
{
auto hModule = GetCachedModule(documentInfo.GetUniqueId(), codeDigest);
if (hModule.IsEmpty())
{
hModule = VERIFY_MAYBE(CompileModule(&source));
if (hModule.IsEmpty())
{
throw V8Exception(V8Exception::Type::General, m_Name, StdString(SL("Module compilation failed; no additional information was provided by the V8 runtime")), false /*executionStarted*/);
}
CacheModule(documentInfo, codeDigest, hModule);
}
upScriptHolder.reset(new V8ScriptHolderImpl(GetWeakBinding(), ::PtrFromHandle(CreatePersistent(hModule)), documentInfo, codeDigest, std::move(code)));
upCachedData.reset(v8::ScriptCompiler::CreateCodeCache(hModule->GetUnboundModuleScript()));
}
else
{
auto hScript = GetCachedScript(documentInfo.GetUniqueId(), codeDigest);
if (hScript.IsEmpty())
{
hScript = VERIFY_MAYBE(CompileUnboundScript(&source));
if (hScript.IsEmpty())
{
throw V8Exception(V8Exception::Type::General, m_Name, StdString(SL("Script compilation failed; no additional information was provided by the V8 runtime")), false /*executionStarted*/);
}
CacheScript(documentInfo, codeDigest, hScript);
}
upScriptHolder.reset(new V8ScriptHolderImpl(GetWeakBinding(), ::PtrFromHandle(CreatePersistent(hScript)), documentInfo, codeDigest));
upCachedData.reset(v8::ScriptCompiler::CreateCodeCache(hScript));
}
cacheBytes.clear();
if (upCachedData && (upCachedData->length > 0) && (upCachedData->data != nullptr))
{
cacheBytes.resize(upCachedData->length);
memcpy(cacheBytes.data(), upCachedData->data, upCachedData->length);
if (documentInfo.IsModule())
{
upScriptHolder->SetCacheBytes(cacheBytes);
}
}
return upScriptHolder.release();
FROM_MAYBE_CATCH
throw V8Exception(V8Exception::Type::General, m_Name, StdString(SL("The V8 runtime cannot perform the requested operation because a script exception is pending")), EXECUTION_STARTED);
FROM_MAYBE_END
END_EXECUTION_SCOPE
END_DOCUMENT_SCOPE
END_CONTEXT_SCOPE
}
//-----------------------------------------------------------------------------
V8ScriptHolder* V8ContextImpl::Compile(const V8DocumentInfo& documentInfo, StdString&& code, V8CacheType cacheType, const std::vector& cacheBytes, bool& cacheAccepted)
{
if ((cacheType == V8CacheType::None) || (cacheBytes.size() < 1))
{
cacheAccepted = false;
return Compile(documentInfo, std::move(code));
}
BEGIN_CONTEXT_SCOPE
BEGIN_DOCUMENT_SCOPE(documentInfo)
BEGIN_EXECUTION_SCOPE
FROM_MAYBE_TRY
auto codeDigest = code.GetDigest();
auto pCachedData = new v8::ScriptCompiler::CachedData(cacheBytes.data(), static_cast(cacheBytes.size()), v8::ScriptCompiler::CachedData::BufferNotOwned);
v8::ScriptCompiler::Source source(FROM_MAYBE(CreateString(code)), CreateScriptOrigin(documentInfo), pCachedData);
std::unique_ptr upScriptHolder;
if (documentInfo.IsModule())
{
auto hModule = GetCachedModule(documentInfo.GetUniqueId(), codeDigest);
if (hModule.IsEmpty())
{
hModule = VERIFY_MAYBE(CompileModule(&source, v8::ScriptCompiler::kConsumeCodeCache));
if (hModule.IsEmpty())
{
throw V8Exception(V8Exception::Type::General, m_Name, StdString(SL("Module compilation failed; no additional information was provided by the V8 runtime")), false /*executionStarted*/);
}
CacheModule(documentInfo, codeDigest, hModule);
}
upScriptHolder.reset(new V8ScriptHolderImpl(GetWeakBinding(), ::PtrFromHandle(CreatePersistent(hModule)), documentInfo, codeDigest, std::move(code)));
}
else
{
auto hScript = GetCachedScript(documentInfo.GetUniqueId(), codeDigest);
if (hScript.IsEmpty())
{
hScript = VERIFY_MAYBE(CompileUnboundScript(&source, v8::ScriptCompiler::kConsumeCodeCache));
if (hScript.IsEmpty())
{
throw V8Exception(V8Exception::Type::General, m_Name, StdString(SL("Script compilation failed; no additional information was provided by the V8 runtime")), false /*executionStarted*/);
}
CacheScript(documentInfo, codeDigest, hScript);
}
upScriptHolder.reset(new V8ScriptHolderImpl(GetWeakBinding(), ::PtrFromHandle(CreatePersistent(hScript)), documentInfo, codeDigest));
}
cacheAccepted = !pCachedData->rejected;
if (cacheAccepted && documentInfo.IsModule())
{
upScriptHolder->SetCacheBytes(cacheBytes);
}
return upScriptHolder.release();
FROM_MAYBE_CATCH
throw V8Exception(V8Exception::Type::General, m_Name, StdString(SL("The V8 runtime cannot perform the requested operation because a script exception is pending")), EXECUTION_STARTED);
FROM_MAYBE_END
END_EXECUTION_SCOPE
END_DOCUMENT_SCOPE
END_CONTEXT_SCOPE
}
//-----------------------------------------------------------------------------
bool V8ContextImpl::CanExecute(const SharedPtr& spHolder)
{
return spHolder->IsSameIsolate(m_spIsolateImpl);
}
//-----------------------------------------------------------------------------
V8Value V8ContextImpl::Execute(const SharedPtr& spHolder, bool evaluate)
{
BEGIN_CONTEXT_SCOPE
BEGIN_DOCUMENT_SCOPE(spHolder->GetDocumentInfo())
BEGIN_EXECUTION_SCOPE
FROM_MAYBE_TRY
if (!CanExecute(spHolder))
{
throw V8Exception(V8Exception::Type::General, m_Name, StdString(SL("Invalid compiled script")), false /*executionStarted*/);
}
v8::Local hResult;
if (spHolder->GetDocumentInfo().IsModule())
{
auto codeDigest = spHolder->GetCode().GetDigest();
auto hModule = GetCachedModule(spHolder->GetDocumentInfo().GetUniqueId(), codeDigest);
if (hModule.IsEmpty())
{
if (spHolder->GetCacheBytes().size() > 0)
{
auto pCachedData = new v8::ScriptCompiler::CachedData(spHolder->GetCacheBytes().data(), static_cast(spHolder->GetCacheBytes().size()), v8::ScriptCompiler::CachedData::BufferNotOwned);
v8::ScriptCompiler::Source source(FROM_MAYBE(CreateString(spHolder->GetCode())), CreateScriptOrigin(spHolder->GetDocumentInfo()), pCachedData);
hModule = VERIFY_MAYBE(CompileModule(&source));
_ASSERTE(!pCachedData->rejected);
}
else
{
v8::ScriptCompiler::Source source(FROM_MAYBE(CreateString(spHolder->GetCode())), CreateScriptOrigin(spHolder->GetDocumentInfo()));
hModule = VERIFY_MAYBE(CompileModule(&source));
}
if (hModule.IsEmpty())
{
throw V8Exception(V8Exception::Type::General, m_Name, StdString(SL("Module compilation failed; no additional information was provided by the V8 runtime")), false /*executionStarted*/);
}
CacheModule(spHolder->GetDocumentInfo(), codeDigest, hModule);
}
if (hModule->GetStatus() == v8::Module::kUninstantiated)
{
ASSERT_EVAL(VERIFY_MAYBE(hModule->InstantiateModule(m_hContext, V8IsolateImpl::ModuleResolveCallback)));
}
if (hModule->GetStatus() == v8::Module::kInstantiated)
{
hResult = VERIFY_MAYBE(hModule->Evaluate(m_hContext));
if (!hModule->IsGraphAsync() && hResult->IsPromise())
{
auto hPromise = hResult.As();
if (hPromise->State() == v8::Promise::PromiseState::kFulfilled)
{
hResult = hPromise->Result();
}
else if (hPromise->State() == v8::Promise::PromiseState::kRejected)
{
auto hException = hPromise->Result();
if (hException->IsObject())
{
auto hExceptionObject = hException.As();
auto hHostException = FROM_MAYBE(hExceptionObject->Get(m_hContext, m_hHostExceptionKey));
throw V8Exception(V8Exception::Type::General, m_Name, CreateStdString(hExceptionObject), CreateStdString(FROM_MAYBE(hExceptionObject->Get(m_hContext, m_hStackKey))), EXECUTION_STARTED, ExportValue(hException), ExportValue(hHostException));
}
throw V8Exception(V8Exception::Type::General, m_Name, CreateStdString(hException), StdString(), EXECUTION_STARTED, ExportValue(hException), V8Value(V8Value::Undefined));
}
}
}
else
{
evaluate = false;
}
}
else
{
auto hScript = ::HandleFromPtr(spHolder->GetScript());
hResult = VERIFY_MAYBE(hScript->BindToCurrentContext()->Run(m_hContext));
}
if (!evaluate)
{
hResult = GetUndefined();
}
return ExportValue(hResult);
FROM_MAYBE_CATCH
throw V8Exception(V8Exception::Type::General, m_Name, StdString(SL("The V8 runtime cannot perform the requested operation because a script exception is pending")), EXECUTION_STARTED);
FROM_MAYBE_END
END_EXECUTION_SCOPE
END_DOCUMENT_SCOPE
END_CONTEXT_SCOPE
}
//-----------------------------------------------------------------------------
void V8ContextImpl::Interrupt()
{
TerminateExecution();
}
//-----------------------------------------------------------------------------
void V8ContextImpl::CancelInterrupt()
{
CancelTerminateExecution();
}
//-----------------------------------------------------------------------------
bool V8ContextImpl::GetEnableIsolateInterruptPropagation()
{
return m_spIsolateImpl->GetEnableInterruptPropagation();
}
//-----------------------------------------------------------------------------
void V8ContextImpl::SetEnableIsolateInterruptPropagation(bool value)
{
m_spIsolateImpl->SetEnableInterruptPropagation(value);
}
//-----------------------------------------------------------------------------
bool V8ContextImpl::GetDisableIsolateHeapSizeViolationInterrupt()
{
return m_spIsolateImpl->GetDisableHeapSizeViolationInterrupt();
}
//-----------------------------------------------------------------------------
void V8ContextImpl::SetDisableIsolateHeapSizeViolationInterrupt(bool value)
{
m_spIsolateImpl->SetDisableHeapSizeViolationInterrupt(value);
}
//-----------------------------------------------------------------------------
void V8ContextImpl::GetIsolateHeapStatistics(v8::HeapStatistics& heapStatistics)
{
m_spIsolateImpl->GetHeapStatistics(heapStatistics);
}
//-----------------------------------------------------------------------------
V8Isolate::Statistics V8ContextImpl::GetIsolateStatistics()
{
return m_spIsolateImpl->GetStatistics();
}
//-----------------------------------------------------------------------------
V8ContextImpl::Statistics V8ContextImpl::GetStatistics()
{
_ASSERTE(m_spIsolateImpl->IsCurrent() && m_spIsolateImpl->IsLocked());
return m_Statistics;
}
//-----------------------------------------------------------------------------
void V8ContextImpl::CollectGarbage(bool exhaustive)
{
m_spIsolateImpl->CollectGarbage(exhaustive);
}
//-----------------------------------------------------------------------------
void V8ContextImpl::OnAccessSettingsChanged()
{
BEGIN_CONTEXT_SCOPE
BEGIN_EXECUTION_SCOPE
Dispose(m_hAccessToken);
m_hAccessToken = CreatePersistent(CreateObject());
END_EXECUTION_SCOPE
END_CONTEXT_SCOPE
}
//-----------------------------------------------------------------------------
bool V8ContextImpl::BeginCpuProfile(const StdString& name, v8::CpuProfilingMode mode, bool recordSamples)
{
return m_spIsolateImpl->BeginCpuProfile(name, mode, recordSamples);
}
//-----------------------------------------------------------------------------
bool V8ContextImpl::EndCpuProfile(const StdString& name, V8Isolate::CpuProfileCallback* pCallback, void* pvArg)
{
return m_spIsolateImpl->EndCpuProfile(name, pCallback, pvArg);
}
//-----------------------------------------------------------------------------
void V8ContextImpl::CollectCpuProfileSample()
{
return m_spIsolateImpl->CollectCpuProfileSample();
}
//-----------------------------------------------------------------------------
uint32_t V8ContextImpl::GetCpuProfileSampleInterval()
{
return m_spIsolateImpl->GetCpuProfileSampleInterval();
}
//-----------------------------------------------------------------------------
void V8ContextImpl::SetCpuProfileSampleInterval(uint32_t value)
{
m_spIsolateImpl->SetCpuProfileSampleInterval(value);
}
//-----------------------------------------------------------------------------
void V8ContextImpl::WriteIsolateHeapSnapshot(void* pvStream)
{
m_spIsolateImpl->WriteHeapSnapshot(pvStream);
}
//-----------------------------------------------------------------------------
void V8ContextImpl::Flush()
{
BEGIN_CONTEXT_SCOPE
BEGIN_EXECUTION_SCOPE
(void)m_hFlushFunction->Call(m_hContext, GetUndefined(), 0, nullptr);
END_EXECUTION_SCOPE
END_CONTEXT_SCOPE
}
//-----------------------------------------------------------------------------
void V8ContextImpl::Destroy()
{
m_spIsolateImpl->CallWithLockNoWait(true /*allowNesting*/, [this] (V8IsolateImpl* /*pIsolateImpl*/)
{
delete this;
});
}
//-----------------------------------------------------------------------------
V8Value V8ContextImpl::GetV8ObjectProperty(void* pvObject, const StdString& name)
{
BEGIN_CONTEXT_SCOPE
BEGIN_EXECUTION_SCOPE
FROM_MAYBE_TRY
return ExportValue(FROM_MAYBE(::HandleFromPtr(pvObject)->Get(m_hContext, FROM_MAYBE(CreateString(name)))));
FROM_MAYBE_CATCH
throw V8Exception(V8Exception::Type::General, m_Name, StdString(SL("The V8 runtime cannot perform the requested operation because a script exception is pending")), EXECUTION_STARTED);
FROM_MAYBE_END
END_EXECUTION_SCOPE
END_CONTEXT_SCOPE
}
//-----------------------------------------------------------------------------
bool V8ContextImpl::TryGetV8ObjectProperty(void* pvObject, const StdString& name, V8Value& value)
{
BEGIN_CONTEXT_SCOPE
BEGIN_EXECUTION_SCOPE
FROM_MAYBE_TRY
auto hObject = ::HandleFromPtr(pvObject);
auto hName = FROM_MAYBE(CreateString(name));
if (FROM_MAYBE(hObject->Has(m_hContext, hName)))
{
value = ExportValue(FROM_MAYBE(::HandleFromPtr(pvObject)->Get(m_hContext, hName)));
return true;
}
return false;
FROM_MAYBE_CATCH
throw V8Exception(V8Exception::Type::General, m_Name, StdString(SL("The V8 runtime cannot perform the requested operation because a script exception is pending")), EXECUTION_STARTED);
FROM_MAYBE_END
END_EXECUTION_SCOPE
END_CONTEXT_SCOPE
}
//-----------------------------------------------------------------------------
void V8ContextImpl::SetV8ObjectProperty(void* pvObject, const StdString& name, const V8Value& value)
{
BEGIN_CONTEXT_SCOPE
BEGIN_EXECUTION_SCOPE
FROM_MAYBE_TRY
ASSERT_EVAL(FROM_MAYBE(::HandleFromPtr(pvObject)->Set(m_hContext, FROM_MAYBE(CreateString(name)), ImportValue(value))));
FROM_MAYBE_CATCH
throw V8Exception(V8Exception::Type::General, m_Name, StdString(SL("The V8 runtime cannot perform the requested operation because a script exception is pending")), EXECUTION_STARTED);
FROM_MAYBE_END
END_EXECUTION_SCOPE
END_CONTEXT_SCOPE
}
//-----------------------------------------------------------------------------
bool V8ContextImpl::DeleteV8ObjectProperty(void* pvObject, const StdString& name)
{
BEGIN_CONTEXT_SCOPE
FROM_MAYBE_TRY
return FROM_MAYBE(::HandleFromPtr(pvObject)->Delete(m_hContext, FROM_MAYBE(CreateString(name))));
FROM_MAYBE_CATCH
throw V8Exception(V8Exception::Type::General, m_Name, StdString(SL("The V8 runtime cannot perform the requested operation because a script exception is pending")), false /*executionStarted*/);
FROM_MAYBE_END
END_CONTEXT_SCOPE
}
//-----------------------------------------------------------------------------
void V8ContextImpl::GetV8ObjectPropertyNames(void* pvObject, bool includeIndices, std::vector& names)
{
BEGIN_CONTEXT_SCOPE
GetV8ObjectPropertyNames(::HandleFromPtr(pvObject), names, v8::ONLY_ENUMERABLE, includeIndices ? v8::IndexFilter::kIncludeIndices : v8::IndexFilter::kSkipIndices);
END_CONTEXT_SCOPE
}
//-----------------------------------------------------------------------------
V8Value V8ContextImpl::GetV8ObjectProperty(void* pvObject, int index)
{
BEGIN_CONTEXT_SCOPE
BEGIN_EXECUTION_SCOPE
FROM_MAYBE_TRY
return ExportValue(FROM_MAYBE(::HandleFromPtr(pvObject)->Get(m_hContext, index)));
FROM_MAYBE_CATCH
throw V8Exception(V8Exception::Type::General, m_Name, StdString(SL("The V8 runtime cannot perform the requested operation because a script exception is pending")), EXECUTION_STARTED);
FROM_MAYBE_END
END_EXECUTION_SCOPE
END_CONTEXT_SCOPE
}
//-----------------------------------------------------------------------------
void V8ContextImpl::SetV8ObjectProperty(void* pvObject, int index, const V8Value& value)
{
BEGIN_CONTEXT_SCOPE
BEGIN_EXECUTION_SCOPE
FROM_MAYBE_TRY
ASSERT_EVAL(FROM_MAYBE(::HandleFromPtr(pvObject)->Set(m_hContext, index, ImportValue(value))));
FROM_MAYBE_CATCH
throw V8Exception(V8Exception::Type::General, m_Name, StdString(SL("The V8 runtime cannot perform the requested operation because a script exception is pending")), EXECUTION_STARTED);
FROM_MAYBE_END
END_EXECUTION_SCOPE
END_CONTEXT_SCOPE
}
//-----------------------------------------------------------------------------
bool V8ContextImpl::DeleteV8ObjectProperty(void* pvObject, int index)
{
BEGIN_CONTEXT_SCOPE
FROM_MAYBE_TRY
return FROM_MAYBE(::HandleFromPtr(pvObject)->Delete(m_hContext, index));
FROM_MAYBE_CATCH
throw V8Exception(V8Exception::Type::General, m_Name, StdString(SL("The V8 runtime cannot perform the requested operation because a script exception is pending")), false /*executionStarted*/);
FROM_MAYBE_END
END_CONTEXT_SCOPE
}
//-----------------------------------------------------------------------------
void V8ContextImpl::GetV8ObjectPropertyIndices(void* pvObject, std::vector& indices)
{
BEGIN_CONTEXT_SCOPE
GetV8ObjectPropertyIndices(::HandleFromPtr(pvObject), indices, v8::ONLY_ENUMERABLE);
END_CONTEXT_SCOPE
}
//-----------------------------------------------------------------------------
V8Value V8ContextImpl::InvokeV8Object(void* pvObject, bool asConstructor, const std::vector& args)
{
BEGIN_CONTEXT_SCOPE
BEGIN_EXECUTION_SCOPE
v8::Local hObject = ::HandleFromPtr(pvObject);
if (!hObject->IsCallable())
{
FROM_MAYBE_TRY
auto hError = v8::Exception::TypeError(m_hObjectNotInvocable).As();
throw V8Exception(V8Exception::Type::General, m_Name, CreateStdString(hError), CreateStdString(FROM_MAYBE(hError->Get(m_hContext, m_hStackKey))), EXECUTION_STARTED, ExportValue(hError), V8Value(V8Value::Undefined));
FROM_MAYBE_CATCH
throw V8Exception(V8Exception::Type::General, m_Name, StdString(SL("The object does not support invocation")), EXECUTION_STARTED);
FROM_MAYBE_END
}
std::vector importedArgs;
ImportValues(args, importedArgs);
if (asConstructor)
{
return ExportValue(VERIFY_MAYBE(hObject->CallAsConstructor(m_hContext, static_cast(importedArgs.size()), importedArgs.data())));
}
return ExportValue(VERIFY_MAYBE(hObject->CallAsFunction(m_hContext, hObject, static_cast(importedArgs.size()), importedArgs.data())));
END_EXECUTION_SCOPE
END_CONTEXT_SCOPE
}
//-----------------------------------------------------------------------------
V8Value V8ContextImpl::InvokeV8ObjectMethod(void* pvObject, const StdString& name, const std::vector& args)
{
BEGIN_CONTEXT_SCOPE
BEGIN_EXECUTION_SCOPE
FROM_MAYBE_TRY
v8::Local hObject = ::HandleFromPtr(pvObject);
auto hMethod = ::ValueAsObject(VERIFY_MAYBE(hObject->Get(m_hContext, FROM_MAYBE(CreateString(name)))));
if (hMethod.IsEmpty())
{
FROM_MAYBE_TRY
auto hError = v8::Exception::TypeError(m_hMethodOrPropertyNotFound).As();
throw V8Exception(V8Exception::Type::General, m_Name, CreateStdString(hError), CreateStdString(FROM_MAYBE(hError->Get(m_hContext, m_hStackKey))), EXECUTION_STARTED, ExportValue(hError), V8Value(V8Value::Undefined));
FROM_MAYBE_CATCH
throw V8Exception(V8Exception::Type::General, m_Name, StdString(SL("Method or property not found")), EXECUTION_STARTED);
FROM_MAYBE_END
}
if (!hMethod->IsCallable())
{
FROM_MAYBE_TRY
auto hError = v8::Exception::TypeError(m_hPropertyValueNotInvocable).As();
throw V8Exception(V8Exception::Type::General, m_Name, CreateStdString(hError), CreateStdString(FROM_MAYBE(hError->Get(m_hContext, m_hStackKey))), EXECUTION_STARTED, ExportValue(hError), V8Value(V8Value::Undefined));
FROM_MAYBE_CATCH
throw V8Exception(V8Exception::Type::General, m_Name, StdString(SL("The property value does not support invocation")), EXECUTION_STARTED);
FROM_MAYBE_END
}
std::vector importedArgs;
ImportValues(args, importedArgs);
return ExportValue(VERIFY_MAYBE(hMethod->CallAsFunction(m_hContext, hObject, static_cast(importedArgs.size()), importedArgs.data())));
FROM_MAYBE_CATCH
throw V8Exception(V8Exception::Type::General, m_Name, StdString(SL("The V8 runtime cannot perform the requested operation because a script exception is pending")), EXECUTION_STARTED);
FROM_MAYBE_END
END_EXECUTION_SCOPE
END_CONTEXT_SCOPE
}
//-----------------------------------------------------------------------------
void V8ContextImpl::GetV8ObjectArrayBufferOrViewInfo(void* pvObject, V8Value& arrayBuffer, size_t& offset, size_t& size, size_t& length)
{
BEGIN_CONTEXT_SCOPE
v8::Local hObject = ::HandleFromPtr(pvObject);
if (hObject->IsArrayBuffer())
{
auto hArrayBuffer = hObject.As();
arrayBuffer = ExportValue(hObject);
offset = 0;
size = hArrayBuffer->ByteLength();
length = size;
return;
}
if (hObject->IsSharedArrayBuffer())
{
auto hSharedArrayBuffer = hObject.As();
arrayBuffer = ExportValue(hObject);
offset = 0;
size = hSharedArrayBuffer->ByteLength();
length = size;
return;
}
if (hObject->IsDataView())
{
auto hDataView = hObject.As();
arrayBuffer = ExportValue(hDataView->Buffer());
offset = hDataView->ByteOffset();
size = hDataView->ByteLength();
length = size;
return;
}
if (hObject->IsTypedArray())
{
auto hTypedArray = hObject.As();
arrayBuffer = ExportValue(hTypedArray->Buffer());
offset = hTypedArray->ByteOffset();
size = hTypedArray->ByteLength();
length = hTypedArray->Length();
return;
}
throw V8Exception(V8Exception::Type::General, m_Name, StdString(SL("The object is not a V8 array buffer or view")), false /*executionStarted*/);
END_CONTEXT_SCOPE
}
//-----------------------------------------------------------------------------
void V8ContextImpl::InvokeWithV8ObjectArrayBufferOrViewData(void* pvObject, V8ObjectHelpers::ArrayBufferOrViewDataCallback* pCallback, void* pvArg)
{
BEGIN_CONTEXT_SCOPE
v8::Local hObject = ::HandleFromPtr(pvObject);
if (hObject->IsArrayBuffer())
{
auto hArrayBuffer = hObject.As();
auto spBackingStore = hArrayBuffer->GetBackingStore();
(*pCallback)(spBackingStore->Data(), pvArg);
return;
}
if (hObject->IsSharedArrayBuffer())
{
auto hSharedArrayBuffer = hObject.As();
auto spBackingStore = hSharedArrayBuffer->GetBackingStore();
(*pCallback)(spBackingStore->Data(), pvArg);
return;
}
if (hObject->IsDataView())
{
auto hDataView = hObject.As();
auto spBackingStore = hDataView->Buffer()->GetBackingStore();
(*pCallback)(static_cast(spBackingStore->Data()) + hDataView->ByteOffset(), pvArg);
return;
}
if (hObject->IsTypedArray())
{
auto hTypedArray = hObject.As();
auto spBackingStore = hTypedArray->Buffer()->GetBackingStore();
(*pCallback)(static_cast(spBackingStore->Data()) + hTypedArray->ByteOffset(), pvArg);
return;
}
throw V8Exception(V8Exception::Type::General, m_Name, StdString(SL("The object is not a V8 array buffer or view")), false /*executionStarted*/);
END_CONTEXT_SCOPE
}
//-----------------------------------------------------------------------------
void V8ContextImpl::InitializeImportMeta(v8::Local /*hContext*/, v8::Local hModule, v8::Local hMeta)
{
BEGIN_CONTEXT_SCOPE
FROM_MAYBE_TRY
try
{
for (const auto& entry : m_ModuleCache)
{
if (entry.hModule == hModule)
{
for (const auto& pair : HostObjectUtil::GetInstance().CreateModuleContext(entry.DocumentInfo))
{
(void)hMeta->Set(m_hContext, FROM_MAYBE(CreateString(pair.first)), ImportValue(pair.second));
}
return;
}
}
}
catch (const HostException& exception)
{
ThrowScriptException(exception);
}
FROM_MAYBE_CATCH_CONSUME
END_CONTEXT_SCOPE
}
//-----------------------------------------------------------------------------
v8::MaybeLocal V8ContextImpl::ImportModule(v8::Local hHostDefinedOptions, v8::Local /*hResourceName*/, v8::Local hSpecifier, v8::Local /*hImportAssertions*/)
{
V8DocumentInfo sourceDocumentInfo;
const V8DocumentInfo* pSourceDocumentInfo = nullptr;
if (!hHostDefinedOptions.IsEmpty())
{
auto hOptions = hHostDefinedOptions.As();
if (hOptions->Length() > 0)
{
auto hUniqueId = ::ValueAsBigInt(GetPrimitiveArrayItem(hOptions, 0));
if (!hUniqueId.IsEmpty())
{
auto uniqueId = hUniqueId->Uint64Value();
if (TryGetCachedScriptInfo(uniqueId, sourceDocumentInfo) || TryGetCachedModuleInfo(uniqueId, sourceDocumentInfo))
{
pSourceDocumentInfo = &sourceDocumentInfo;
}
}
}
}
return ImportModule(pSourceDocumentInfo, hSpecifier);
}
//-----------------------------------------------------------------------------
v8::MaybeLocal V8ContextImpl::ImportModule(const V8DocumentInfo* pSourceDocumentInfo, v8::Local hSpecifier)
{
BEGIN_CONTEXT_SCOPE
V8IsolateImpl::TryCatch outerTryCatch(*m_spIsolateImpl);
FROM_MAYBE_TRY
auto hResolver = FROM_MAYBE(v8::Promise::Resolver::New(m_hContext));
V8IsolateImpl::TryCatch innerTryCatch(*m_spIsolateImpl);
FROM_MAYBE_TRY
auto hModule = FROM_MAYBE(ResolveModule(hSpecifier, pSourceDocumentInfo));
if (hModule->GetStatus() == v8::Module::kUninstantiated)
{
ASSERT_EVAL(FROM_MAYBE(hModule->InstantiateModule(m_hContext, V8IsolateImpl::ModuleResolveCallback)));
}
if (hModule->GetStatus() == v8::Module::kInstantiated)
{
FROM_MAYBE(hModule->Evaluate(m_hContext));
}
ASSERT_EVAL(FROM_MAYBE(hResolver->Resolve(m_hContext, hModule->GetModuleNamespace())));
return hResolver->GetPromise();
FROM_MAYBE_CATCH_CONSUME
auto innerHasCaught = innerTryCatch.HasCaught();
_ASSERTE(innerHasCaught);
if (innerHasCaught)
{
(void)hResolver->Reject(m_hContext, innerTryCatch.Exception());
}
return hResolver->GetPromise();
FROM_MAYBE_CATCH_CONSUME
auto outerHasCaught = outerTryCatch.HasCaught();
_ASSERTE(outerHasCaught);
if (outerHasCaught)
{
outerTryCatch.ReThrow();
}
return v8::MaybeLocal();
END_CONTEXT_SCOPE
}
//-----------------------------------------------------------------------------
v8::MaybeLocal V8ContextImpl::ResolveModule(v8::Local hSpecifier, v8::Local hReferrer)
{
V8DocumentInfo sourceDocumentInfo;
const V8DocumentInfo* pSourceDocumentInfo = nullptr;
if (TryGetCachedModuleInfo(hReferrer, sourceDocumentInfo))
{
pSourceDocumentInfo = &sourceDocumentInfo;
}
return ResolveModule(hSpecifier, pSourceDocumentInfo);
}
//-----------------------------------------------------------------------------
v8::MaybeLocal V8ContextImpl::ResolveModule(v8::Local hSpecifier, const V8DocumentInfo* pSourceDocumentInfo)
{
BEGIN_CONTEXT_SCOPE
V8IsolateImpl::TryCatch tryCatch(*m_spIsolateImpl);
FROM_MAYBE_TRY
if (pSourceDocumentInfo == nullptr)
{
pSourceDocumentInfo = m_spIsolateImpl->GetDocumentInfo();
}
if (pSourceDocumentInfo == nullptr)
{
ThrowException(v8::Exception::Error(m_hInvalidModuleRequest));
}
else
{
try
{
V8DocumentInfo documentInfo;
V8Value exportsValue(V8Value::Undefined);
auto code = HostObjectUtil::GetInstance().LoadModule(*pSourceDocumentInfo, CreateStdString(hSpecifier), documentInfo, exportsValue);
auto codeDigest = code.GetDigest();
auto hModule = GetCachedModule(documentInfo.GetUniqueId(), codeDigest);
if (!hModule.IsEmpty())
{
return hModule;
}
if (!documentInfo.IsModule())
{
std::vector names;
std::vector exports;
auto hExports = ::ValueAsObject(ImportValue(exportsValue));
if (!hExports.IsEmpty())
{
auto hOwnPropertyNames = FROM_MAYBE(hExports->GetOwnPropertyNames(m_hContext, v8::SKIP_SYMBOLS, v8::KeyConversionMode::kNoNumbers));
if (!hOwnPropertyNames.IsEmpty())
{
auto length = hOwnPropertyNames->Length();
names.reserve(length);
exports.reserve(length);
for (uint32_t index = 0; index < length; index++)
{
auto hName = ::ValueAsString(FROM_MAYBE(hOwnPropertyNames->Get(m_hContext, index)));
if (!hName.IsEmpty())
{
names.push_back(hName);
exports.push_back({ CreatePersistent(hName), CreatePersistent(FROM_MAYBE(hExports->Get(m_hContext, hName))) });
}
}
}
}
hModule = CreateSyntheticModule(FROM_MAYBE(CreateString(documentInfo.GetResourceName())), names, PopulateSyntheticModule);
m_SyntheticModuleData.push_back({ CreatePersistent(hModule), std::move(exports) });
}
else
{
BEGIN_DOCUMENT_SCOPE(documentInfo)
v8::ScriptCompiler::Source source(FROM_MAYBE(CreateString(code)), CreateScriptOrigin(documentInfo));
hModule = FROM_MAYBE(CompileModule(&source));
END_DOCUMENT_SCOPE
}
if (!hModule.IsEmpty())
{
CacheModule(documentInfo, codeDigest, hModule);
}
return hModule;
}
catch (const HostException& exception)
{
ThrowScriptException(exception);
}
}
FROM_MAYBE_CATCH_CONSUME
auto hasCaught = tryCatch.HasCaught();
_ASSERTE(hasCaught);
if (hasCaught)
{
tryCatch.ReThrow();
}
return v8::MaybeLocal();
END_CONTEXT_SCOPE
}
//-----------------------------------------------------------------------------
v8::MaybeLocal V8ContextImpl::PopulateSyntheticModule(v8::Local hContext, v8::Local hModule)
{
if (hContext->GetNumberOfEmbedderDataFields() > 1)
{
auto pContextImpl = reinterpret_cast(hContext->GetAlignedPointerFromEmbedderData(1));
if (pContextImpl != nullptr)
{
return pContextImpl->PopulateSyntheticModule(hModule);
}
}
return v8::MaybeLocal();
}
//-----------------------------------------------------------------------------
v8::MaybeLocal V8ContextImpl::PopulateSyntheticModule(v8::Local hModule)
{
FROM_MAYBE_TRY
auto hResolver = FROM_MAYBE(v8::Promise::Resolver::New(m_hContext));
ASSERT_EVAL(FROM_MAYBE(hResolver->Resolve(m_hContext, GetUndefined())));
for (auto itModule = m_SyntheticModuleData.begin(); itModule != m_SyntheticModuleData.end(); itModule++)
{
if (itModule->hModule == hModule)
{
Dispose(itModule->hModule);
for (auto itExport = itModule->Exports.begin(); itExport != itModule->Exports.end(); itExport++)
{
FROM_MAYBE(SetSyntheticModuleExport(hModule, itExport->hName, itExport->hValue));
Dispose(itExport->hName);
Dispose(itExport->hValue);
}
m_SyntheticModuleData.erase(itModule);
break;
}
}
return hResolver->GetPromise();
FROM_MAYBE_CATCH
return v8::MaybeLocal();
FROM_MAYBE_END
}
//-----------------------------------------------------------------------------
void V8ContextImpl::Teardown()
{
_ASSERTE(m_spIsolateImpl->IsCurrent() && m_spIsolateImpl->IsLocked());
m_spIsolateImpl->RemoveContext(this);
if (m_pvV8ObjectCache != nullptr)
{
std::vector v8ObjectPtrs;
HostObjectUtil::GetInstance().GetAllCachedV8Objects(m_pvV8ObjectCache, v8ObjectPtrs);
for (auto pvV8Object : v8ObjectPtrs)
{
auto hObject = ::HandleFromPtr(pvV8Object);
auto pHolder = GetHostObjectHolder(hObject);
if (pHolder != nullptr)
{
delete pHolder;
}
ClearWeak(hObject);
Dispose(hObject);
}
HostObjectUtil::GetInstance().Release(m_pvV8ObjectCache);
m_pvV8ObjectCache = nullptr;
}
for (auto itModule = m_SyntheticModuleData.rbegin(); itModule != m_SyntheticModuleData.rend(); itModule++)
{
Dispose(itModule->hModule);
for (auto itExport = itModule->Exports.begin(); itExport != itModule->Exports.end(); itExport++)
{
Dispose(itExport->hName);
Dispose(itExport->hValue);
}
}
ClearModuleCache();
for (auto it = m_GlobalMembersStack.rbegin(); it != m_GlobalMembersStack.rend(); it++)
{
Dispose(it->second);
}
Dispose(m_hAsyncGeneratorConstructor);
Dispose(m_hToJsonFunction);
Dispose(m_hToAsyncIteratorFunction);
Dispose(m_hToIteratorFunction);
Dispose(m_hHostDelegateTemplate);
Dispose(m_hHostInvocableTemplate);
Dispose(m_hHostObjectTemplate);
Dispose(m_hTerminationException);
Dispose(m_hFlushFunction);
Dispose(m_hConstructorKey);
Dispose(m_hInvalidModuleRequest);
Dispose(m_hPropertyValueNotInvocable);
Dispose(m_hMethodOrPropertyNotFound);
Dispose(m_hObjectNotInvocable);
Dispose(m_hStackKey);
Dispose(m_hInternalUseOnly);
Dispose(m_hAccessToken);
Dispose(m_hAccessTokenKey);
Dispose(m_hCacheKey);
Dispose(m_hHostExceptionKey);
Dispose(m_hIsHostObjectKey);
// As of V8 3.16.0, the global property getter for a disposed context
// may be invoked during GC after the V8ContextImpl instance is gone.
auto hGlobal = ::ValueAsObject(m_hContext->Global()->GetPrototype());
if (!hGlobal.IsEmpty() && (hGlobal->InternalFieldCount() > 0))
{
hGlobal->SetAlignedPointerInInternalField(0, nullptr);
}
if (m_hContext->GetNumberOfEmbedderDataFields() > 1)
{
m_hContext->SetAlignedPointerInEmbedderData(1, nullptr);
}
Dispose(m_hContext);
}
//-----------------------------------------------------------------------------
V8ContextImpl::~V8ContextImpl()
{
--s_InstanceCount;
Teardown();
ContextDisposedNotification();
}
//-----------------------------------------------------------------------------
SharedPtr V8ContextImpl::GetWeakBinding()
{
if (m_spWeakBinding.IsEmpty())
{
m_spWeakBinding = new V8WeakContextBinding(m_spIsolateImpl, this);
}
return m_spWeakBinding;
}
//-----------------------------------------------------------------------------
HostObjectHolder* V8ContextImpl::GetHostObjectHolder(v8::Local hObject)
{
if (!hObject.IsEmpty())
{
auto hHolder = ::ValueAsExternal(FROM_MAYBE_DEFAULT(hObject->GetPrivate(m_hContext, GetHostObjectHolderKey())));
if (!hHolder.IsEmpty())
{
return static_cast(hHolder->Value());
}
}
return nullptr;
}
//-----------------------------------------------------------------------------
bool V8ContextImpl::SetHostObjectHolder(v8::Local hObject, HostObjectHolder* pHolder)
{
if (!hObject.IsEmpty())
{
return FROM_MAYBE_DEFAULT(hObject->SetPrivate(m_hContext, GetHostObjectHolderKey(), CreateExternal(pHolder)));
}
return false;
}
//-----------------------------------------------------------------------------
void* V8ContextImpl::GetHostObject(v8::Local hObject)
{
auto pHolder = GetHostObjectHolder(hObject);
if (pHolder != nullptr)
{
return pHolder->GetObject();
}
return nullptr;
}
//-----------------------------------------------------------------------------
bool V8ContextImpl::CheckContextImplForGlobalObjectCallback(V8ContextImpl* pContextImpl)
{
if (pContextImpl == nullptr)
{
return false;
}
if (pContextImpl->IsExecutionTerminating())
{
pContextImpl->ThrowException(pContextImpl->m_hTerminationException);
return false;
}
return true;
}
//-----------------------------------------------------------------------------
bool V8ContextImpl::CheckContextImplForHostObjectCallback(V8ContextImpl* pContextImpl)
{
if (pContextImpl == nullptr)
{
return false;
}
if (pContextImpl->IsExecutionTerminating())
{
pContextImpl->ThrowException(pContextImpl->m_hTerminationException);
return false;
}
return true;
}
//-----------------------------------------------------------------------------
void V8ContextImpl::GetV8ObjectPropertyNames(v8::Local hObject, std::vector& names, v8::PropertyFilter filter, v8::IndexFilter indexFilter)
{
names.clear();
FROM_MAYBE_TRY
auto hNames = FROM_MAYBE(hObject->GetPropertyNames(m_hContext, v8::KeyCollectionMode::kIncludePrototypes, static_cast(filter | v8::SKIP_SYMBOLS), indexFilter, v8::KeyConversionMode::kConvertToString));
auto nameCount = static_cast(hNames->Length());
names.reserve(nameCount);
for (auto index = 0; index < nameCount; index++)
{
auto hName = FROM_MAYBE(hNames->Get(m_hContext, index));
if (hName->IsString())
{
names.push_back(CreateStdString(hName));
}
}
FROM_MAYBE_CATCH_CONSUME
}
//-----------------------------------------------------------------------------
void V8ContextImpl::GetV8ObjectPropertyIndices(v8::Local hObject, std::vector& indices, v8::PropertyFilter filter)
{
indices.clear();
FROM_MAYBE_TRY
auto hNames = FROM_MAYBE(hObject->GetPropertyNames(m_hContext, v8::KeyCollectionMode::kIncludePrototypes, static_cast(filter | v8::SKIP_SYMBOLS), v8::IndexFilter::kIncludeIndices, v8::KeyConversionMode::kKeepNumbers));
auto nameCount = static_cast(hNames->Length());
indices.reserve(nameCount);
for (auto index = 0; index < nameCount; index++)
{
auto hName = FROM_MAYBE(hNames->Get(m_hContext, index));
if (hName->IsInt32())
{
indices.push_back(FROM_MAYBE(hName->Int32Value(m_hContext)));
}
else if (hName->IsUint32())
{
auto value = FROM_MAYBE(hName->Uint32Value(m_hContext));
if (value IsNumber())
{
auto value = FROM_MAYBE(hName->NumberValue(m_hContext));
if (value == std::round(value) && (value >= static_cast(INT_MIN)) && (value m_GlobalMembersStack;
if (stack.size() > 0)
{
for (auto it = stack.rbegin(); it != stack.rend(); it++)
{
if (FROM_MAYBE(it->second->HasOwnProperty(pContextImpl->m_hContext, hName)))
{
CALLBACK_RETURN(FROM_MAYBE(it->second->Get(pContextImpl->m_hContext, hName)));
}
}
}
}
FROM_MAYBE_CATCH_CONSUME
}
//-----------------------------------------------------------------------------
void V8ContextImpl::SetGlobalProperty(v8::Local hKey, v8::Local hValue, const v8::PropertyCallbackInfo& info)
{
auto hName = ::ValueAsString(hKey);
if (hName.IsEmpty())
{
return;
}
FROM_MAYBE_TRY
auto pContextImpl = ::GetContextImplFromHolder(info);
if (CheckContextImplForGlobalObjectCallback(pContextImpl))
{
const auto& stack = pContextImpl->m_GlobalMembersStack;
if (stack.size() > 0)
{
for (auto it = stack.rbegin(); it != stack.rend(); it++)
{
if (FROM_MAYBE(it->second->HasOwnProperty(pContextImpl->m_hContext, hName)))
{
(void)it->second->Set(pContextImpl->m_hContext, hName, hValue);
CALLBACK_RETURN(hValue);
}
}
}
}
FROM_MAYBE_CATCH_CONSUME
}
//-----------------------------------------------------------------------------
void V8ContextImpl::QueryGlobalProperty(v8::Local hKey, const v8::PropertyCallbackInfo& info)
{
auto hName = ::ValueAsString(hKey);
if (hName.IsEmpty())
{
return;
}
FROM_MAYBE_TRY
auto pContextImpl = ::GetContextImplFromHolder(info);
if (CheckContextImplForGlobalObjectCallback(pContextImpl))
{
const auto& stack = pContextImpl->m_GlobalMembersStack;
if (stack.size() > 0)
{
for (auto it = stack.rbegin(); it != stack.rend(); it++)
{
if (FROM_MAYBE(it->second->HasOwnProperty(pContextImpl->m_hContext, hName)))
{
CALLBACK_RETURN(FROM_MAYBE(it->second->GetPropertyAttributes(pContextImpl->m_hContext, hName)));
}
}
}
}
FROM_MAYBE_CATCH_CONSUME
}
//-----------------------------------------------------------------------------
void V8ContextImpl::DeleteGlobalProperty(v8::Local hKey, const v8::PropertyCallbackInfo& info)
{
auto hName = ::ValueAsString(hKey);
if (hName.IsEmpty())
{
return;
}
FROM_MAYBE_TRY
auto pContextImpl = ::GetContextImplFromHolder(info);
if (CheckContextImplForGlobalObjectCallback(pContextImpl))
{
const auto& stack = pContextImpl->m_GlobalMembersStack;
if (stack.size() > 0)
{
for (auto it = stack.rbegin(); it != stack.rend(); it++)
{
if (FROM_MAYBE(it->second->HasOwnProperty(pContextImpl->m_hContext, hName)))
{
// WORKAROUND: v8::Object::Delete() crashes if a custom property deleter calls
// ThrowException(). Interestingly, there is no crash if the same deleter is
// invoked directly from script via the delete operator.
auto pvObject = pContextImpl->GetHostObject(it->second);
if (pvObject != nullptr)
{
try
{
CALLBACK_RETURN(HostObjectUtil::GetInstance().DeleteProperty(pvObject, pContextImpl->CreateStdString(hName)));
}
catch (const HostException&)
{
CALLBACK_RETURN(false);
}
}
CALLBACK_RETURN(FROM_MAYBE(it->second->Delete(pContextImpl->m_hContext, hName)));
}
}
}
}
FROM_MAYBE_CATCH_CONSUME
}
//-----------------------------------------------------------------------------
void V8ContextImpl::GetGlobalPropertyNames(const v8::PropertyCallbackInfo& info)
{
FROM_MAYBE_TRY
auto pContextImpl = ::GetContextImplFromHolder(info);
if (CheckContextImplForGlobalObjectCallback(pContextImpl))
{
try
{
const auto& stack = pContextImpl->m_GlobalMembersStack;
if (stack.size() > 0)
{
std::vector names;
for (auto it = stack.rbegin(); it != stack.rend(); it++)
{
std::vector tempNames;
auto pvObject = pContextImpl->GetHostObject(it->second);
if (pvObject != nullptr)
{
HostObjectUtil::GetInstance().GetPropertyNames(pvObject, tempNames);
}
else
{
pContextImpl->GetV8ObjectPropertyNames(it->second, tempNames, v8::ONLY_ENUMERABLE, v8::IndexFilter::kSkipIndices);
}
names.insert(names.end(), tempNames.begin(), tempNames.end());
}
std::sort(names.begin(), names.end());
auto newEnd = std::unique(names.begin(), names.end());
auto nameCount = static_cast(newEnd - names.begin());
auto hImportedNames = pContextImpl->CreateArray(nameCount);
for (auto index = 0; index < nameCount; index++)
{
ASSERT_EVAL(FROM_MAYBE(hImportedNames->Set(pContextImpl->m_hContext, index, FROM_MAYBE(pContextImpl->CreateString(names[index])))));
}
CALLBACK_RETURN(hImportedNames);
}
}
catch (const HostException& exception)
{
pContextImpl->ThrowScriptException(exception);
}
}
FROM_MAYBE_CATCH_CONSUME
}
//-----------------------------------------------------------------------------
void V8ContextImpl::GetGlobalProperty(uint32_t index, const v8::PropertyCallbackInfo& info)
{
FROM_MAYBE_TRY
auto pContextImpl = ::GetContextImplFromHolder(info);
if (CheckContextImplForGlobalObjectCallback(pContextImpl))
{
const auto& stack = pContextImpl->m_GlobalMembersStack;
if (stack.size() > 0)
{
auto hName = FROM_MAYBE(pContextImpl->CreateInteger(index)->ToString(pContextImpl->m_hContext));
for (auto it = stack.rbegin(); it != stack.rend(); it++)
{
if (FROM_MAYBE(it->second->HasOwnProperty(pContextImpl->m_hContext, hName)))
{
CALLBACK_RETURN(FROM_MAYBE(it->second->Get(pContextImpl->m_hContext, index)));
}
}
}
}
FROM_MAYBE_CATCH_CONSUME
}
//-----------------------------------------------------------------------------
void V8ContextImpl::SetGlobalProperty(uint32_t index, v8::Local hValue, const v8::PropertyCallbackInfo& info)
{
FROM_MAYBE_TRY
auto pContextImpl = ::GetContextImplFromHolder(info);
if (CheckContextImplForGlobalObjectCallback(pContextImpl))
{
const auto& stack = pContextImpl->m_GlobalMembersStack;
if (stack.size() > 0)
{
auto hName = FROM_MAYBE(pContextImpl->CreateInteger(index)->ToString(pContextImpl->m_hContext));
for (auto it = stack.rbegin(); it != stack.rend(); it++)
{
if (FROM_MAYBE(it->second->HasOwnProperty(pContextImpl->m_hContext, hName)))
{
(void)it->second->Set(pContextImpl->m_hContext, index, hValue);
CALLBACK_RETURN(hValue);
}
}
}
}
FROM_MAYBE_CATCH_CONSUME
}
//-----------------------------------------------------------------------------
void V8ContextImpl::QueryGlobalProperty(uint32_t index, const v8::PropertyCallbackInfo& info)
{
FROM_MAYBE_TRY
auto pContextImpl = ::GetContextImplFromHolder(info);
if (CheckContextImplForGlobalObjectCallback(pContextImpl))
{
const auto& stack = pContextImpl->m_GlobalMembersStack;
if (stack.size() > 0)
{
auto hIndex = pContextImpl->CreateInteger(index);
auto hName = FROM_MAYBE(hIndex->ToString(pContextImpl->m_hContext));
for (auto it = stack.rbegin(); it != stack.rend(); it++)
{
if (FROM_MAYBE(it->second->HasOwnProperty(pContextImpl->m_hContext, hName)))
{
CALLBACK_RETURN(FROM_MAYBE(it->second->GetPropertyAttributes(pContextImpl->m_hContext, hIndex)));
}
}
}
}
FROM_MAYBE_CATCH_CONSUME
}
//-----------------------------------------------------------------------------
void V8ContextImpl::DeleteGlobalProperty(uint32_t index, const v8::PropertyCallbackInfo& info)
{
FROM_MAYBE_TRY
auto pContextImpl = ::GetContextImplFromHolder(info);
if (CheckContextImplForGlobalObjectCallback(pContextImpl))
{
const auto& stack = pContextImpl->m_GlobalMembersStack;
if (stack.size() > 0)
{
auto hName = FROM_MAYBE(pContextImpl->CreateInteger(index)->ToString(pContextImpl->m_hContext));
for (auto it = stack.rbegin(); it != stack.rend(); it++)
{
if (FROM_MAYBE(it->second->HasOwnProperty(pContextImpl->m_hContext, hName)))
{
CALLBACK_RETURN(FROM_MAYBE(it->second->Delete(pContextImpl->m_hContext, index)));
}
}
}
}
FROM_MAYBE_CATCH_CONSUME
}
//-----------------------------------------------------------------------------
void V8ContextImpl::GetGlobalPropertyIndices(const v8::PropertyCallbackInfo& info)
{
FROM_MAYBE_TRY
auto pContextImpl = ::GetContextImplFromHolder(info);
if (CheckContextImplForGlobalObjectCallback(pContextImpl))
{
try
{
const auto& stack = pContextImpl->m_GlobalMembersStack;
if (stack.size() > 0)
{
std::vector indices;
for (auto it = stack.rbegin(); it != stack.rend(); it++)
{
std::vector tempIndices;
auto pvObject = pContextImpl->GetHostObject(it->second);
if (pvObject != nullptr)
{
HostObjectUtil::GetInstance().GetPropertyIndices(pvObject, tempIndices);
}
else
{
pContextImpl->GetV8ObjectPropertyIndices(it->second, tempIndices, v8::ONLY_ENUMERABLE);
}
indices.insert(indices.end(), tempIndices.begin(), tempIndices.end());
}
std::sort(indices.begin(), indices.end());
auto newEnd = std::unique(indices.begin(), indices.end());
auto indexCount = static_cast(newEnd - indices.begin());
auto hImportedIndices = pContextImpl->CreateArray(indexCount);
for (auto index = 0; index < indexCount; index++)
{
ASSERT_EVAL(FROM_MAYBE(hImportedIndices->Set(pContextImpl->m_hContext, index, pContextImpl->CreateInteger(indices[index]))));
}
CALLBACK_RETURN(hImportedIndices);
}
}
catch (const HostException& exception)
{
pContextImpl->ThrowScriptException(exception);
}
}
FROM_MAYBE_CATCH_CONSUME
}
//-----------------------------------------------------------------------------
void V8ContextImpl::HostObjectConstructorCallHandler(const v8::FunctionCallbackInfo& info)
{
auto pContextImpl = ::GetContextImplFromData(info);
if ((pContextImpl != nullptr) && !pContextImpl->m_AllowHostObjectConstructorCall)
{
pContextImpl->ThrowException(v8::Exception::Error(pContextImpl->m_hInternalUseOnly));
}
}
//-----------------------------------------------------------------------------
void V8ContextImpl::GetHostObjectIterator(const v8::FunctionCallbackInfo& info)
{
FROM_MAYBE_TRY
auto pContextImpl = ::GetContextImplFromData(info);
if (pContextImpl != nullptr)
{
auto pvObject = pContextImpl->GetHostObject(info.Holder());
if (pvObject != nullptr)
{
try
{
if (pContextImpl->m_hToIteratorFunction.IsEmpty())
{
auto hEngineInternal = FROM_MAYBE(pContextImpl->m_hContext->Global()->Get(pContextImpl->m_hContext, pContextImpl->CreateString("EngineInternal"))).As();
pContextImpl->m_hToIteratorFunction = pContextImpl->CreatePersistent(FROM_MAYBE(hEngineInternal->Get(pContextImpl->m_hContext, pContextImpl->CreateString("toIterator"))).As());
}
v8::Local args[] = { pContextImpl->ImportValue(HostObjectUtil::GetInstance().GetEnumerator(pvObject)) };
CALLBACK_RETURN(FROM_MAYBE(pContextImpl->m_hToIteratorFunction->Call(pContextImpl->m_hContext, pContextImpl->GetUndefined(), 1, args)));
}
catch (const HostException& exception)
{
pContextImpl->ThrowScriptException(exception);
}
}
}
FROM_MAYBE_CATCH_CONSUME
}
//-----------------------------------------------------------------------------
void V8ContextImpl::GetHostObjectAsyncIterator(const v8::FunctionCallbackInfo& info)
{
FROM_MAYBE_TRY
auto pContextImpl = ::GetContextImplFromData(info);
if (pContextImpl != nullptr)
{
auto pvObject = pContextImpl->GetHostObject(info.Holder());
if (pvObject != nullptr)
{
try
{
if (pContextImpl->m_hToAsyncIteratorFunction.IsEmpty())
{
auto hEngineInternal = FROM_MAYBE(pContextImpl->m_hContext->Global()->Get(pContextImpl->m_hContext, pContextImpl->CreateString("EngineInternal"))).As();
pContextImpl->m_hToAsyncIteratorFunction = pContextImpl->CreatePersistent(FROM_MAYBE(hEngineInternal->Get(pContextImpl->m_hContext, pContextImpl->CreateString("toAsyncIterator"))).As());
}
v8::Local args[] = { pContextImpl->ImportValue(HostObjectUtil::GetInstance().GetAsyncEnumerator(pvObject)) };
CALLBACK_RETURN(FROM_MAYBE(pContextImpl->m_hToAsyncIteratorFunction->Call(pContextImpl->m_hContext, pContextImpl->GetUndefined(), 1, args)));
}
catch (const HostException& exception)
{
pContextImpl->ThrowScriptException(exception);
}
}
}
FROM_MAYBE_CATCH_CONSUME
}
//-----------------------------------------------------------------------------
void V8ContextImpl::GetHostObjectJson(const v8::FunctionCallbackInfo& info)
{
FROM_MAYBE_TRY
auto pContextImpl = ::GetContextImplFromData(info);
if (pContextImpl != nullptr)
{
auto hObject = info.Holder();
if (pContextImpl->GetHostObject(hObject) != nullptr)
{
try
{
if (pContextImpl->m_hToJsonFunction.IsEmpty())
{
auto hEngineInternal = FROM_MAYBE(pContextImpl->m_hContext->Global()->Get(pContextImpl->m_hContext, pContextImpl->CreateString("EngineInternal"))).As();
pContextImpl->m_hToJsonFunction = pContextImpl->CreatePersistent(FROM_MAYBE(hEngineInternal->Get(pContextImpl->m_hContext, pContextImpl->CreateString("toJson"))).As());
}
v8::Local args[] = { info[0], hObject };
CALLBACK_RETURN(FROM_MAYBE(pContextImpl->m_hToJsonFunction->Call(pContextImpl->m_hContext, pContextImpl->GetUndefined(), 2, args)));
}
catch (const HostException& exception)
{
pContextImpl->ThrowScriptException(exception);
}
}
CALLBACK_RETURN(hObject);
}
FROM_MAYBE_CATCH_CONSUME
}
//-----------------------------------------------------------------------------
void V8ContextImpl::CreateFunctionForHostDelegate(const v8::FunctionCallbackInfo& info)
{
FROM_MAYBE_TRY
auto pContextImpl = ::GetContextImplFromData(info);
if (pContextImpl != nullptr)
{
CALLBACK_RETURN(FROM_MAYBE(v8::Function::New(pContextImpl->m_hContext, InvokeHostDelegate, info.Holder())));
}
FROM_MAYBE_CATCH_CONSUME
}
//-----------------------------------------------------------------------------
void V8ContextImpl::InvokeHostDelegate(const v8::FunctionCallbackInfo& info)
{
auto hTarget = ::ValueAsObject(info.Data());
if (!hTarget.IsEmpty())
{
auto argCount = info.Length();
if (argCount < 1)
{
if (info.IsConstructCall())
{
CALLBACK_RETURN(FROM_MAYBE_DEFAULT(hTarget->CallAsConstructor(info.GetIsolate()->GetCurrentContext(), 0, nullptr)));
}
CALLBACK_RETURN(FROM_MAYBE_DEFAULT(hTarget->CallAsFunction(info.GetIsolate()->GetCurrentContext(), hTarget, 0, nullptr)));
}
std::vector args;
args.reserve(argCount);
for (auto index = 0; index < argCount; index++)
{
args.push_back(info[index]);
}
if (info.IsConstructCall())
{
CALLBACK_RETURN(FROM_MAYBE_DEFAULT(hTarget->CallAsConstructor(info.GetIsolate()->GetCurrentContext(), argCount, args.data())));
}
CALLBACK_RETURN(FROM_MAYBE_DEFAULT(hTarget->CallAsFunction(info.GetIsolate()->GetCurrentContext(), hTarget, argCount, args.data())));
}
}
//-----------------------------------------------------------------------------
void V8ContextImpl::GetHostObjectProperty(v8::Local hKey, const v8::PropertyCallbackInfo& info)
{
FROM_MAYBE_TRY
auto pContextImpl = ::GetContextImplFromData(info);
if (CheckContextImplForHostObjectCallback(pContextImpl))
{
auto hHolder = info.Holder();
auto pvObject = pContextImpl->GetHostObject(hHolder);
auto hName = ::ValueAsString(hKey);
if (hName.IsEmpty())
{
if (!hKey.IsEmpty() && hKey->StrictEquals(pContextImpl->m_hIsHostObjectKey))
{
CALLBACK_RETURN((pvObject != nullptr) ? pContextImpl->GetTrue() : pContextImpl->GetFalse());
}
return;
}
if (pvObject != nullptr)
{
try
{
auto hAccessToken = FROM_MAYBE(hHolder->GetPrivate(pContextImpl->m_hContext, pContextImpl->m_hAccessTokenKey));
if (pContextImpl->m_hAccessToken != hAccessToken)
{
ASSERT_EVAL(FROM_MAYBE(hHolder->DeletePrivate(pContextImpl->m_hContext, pContextImpl->m_hCacheKey)));
ASSERT_EVAL(FROM_MAYBE(hHolder->SetPrivate(pContextImpl->m_hContext, pContextImpl->m_hAccessTokenKey, pContextImpl->m_hAccessToken)));
}
else
{
auto hCache = ::ValueAsObject(FROM_MAYBE(hHolder->GetPrivate(pContextImpl->m_hContext, pContextImpl->m_hCacheKey)));
if (!hCache.IsEmpty() && FROM_MAYBE(hCache->HasOwnProperty(pContextImpl->m_hContext, hName)))
{
CALLBACK_RETURN(FROM_MAYBE(hCache->Get(pContextImpl->m_hContext, hName)));
}
}
bool isCacheable;
auto hResult = pContextImpl->ImportValue(HostObjectUtil::GetInstance().GetProperty(pvObject, pContextImpl->CreateStdString(hName), isCacheable));
if (isCacheable && !hResult.IsEmpty())
{
auto hCache = ::ValueAsObject(FROM_MAYBE(hHolder->GetPrivate(pContextImpl->m_hContext, pContextImpl->m_hCacheKey)));
if (hCache.IsEmpty())
{
hCache = pContextImpl->CreateObject();
ASSERT_EVAL(FROM_MAYBE(hHolder->SetPrivate(pContextImpl->m_hContext, pContextImpl->m_hCacheKey, hCache)));
}
ASSERT_EVAL(FROM_MAYBE(hCache->Set(pContextImpl->m_hContext, hName, hResult)));
}
CALLBACK_RETURN(hResult);
}
catch (const HostException& exception)
{
pContextImpl->ThrowScriptException(exception);
}
}
}
FROM_MAYBE_CATCH_CONSUME
}
//-----------------------------------------------------------------------------
void V8ContextImpl::SetHostObjectProperty(v8::Local hKey, v8::Local hValue, const v8::PropertyCallbackInfo& info)
{
auto hName = ::ValueAsString(hKey);
if (hName.IsEmpty())
{
return;
}
auto pContextImpl = ::GetContextImplFromData(info);
if (CheckContextImplForHostObjectCallback(pContextImpl))
{
auto pvObject = pContextImpl->GetHostObject(info.Holder());
if (pvObject != nullptr)
{
try
{
HostObjectUtil::GetInstance().SetProperty(pvObject, pContextImpl->CreateStdString(hName), pContextImpl->ExportValue(hValue));
CALLBACK_RETURN(hValue);
}
catch (const HostException& exception)
{
pContextImpl->ThrowScriptException(exception);
}
}
}
}
//-----------------------------------------------------------------------------
void V8ContextImpl::QueryHostObjectProperty(v8::Local hKey, const v8::PropertyCallbackInfo& info)
{
auto hName = ::ValueAsString(hKey);
if (hName.IsEmpty())
{
return;
}
auto pContextImpl = ::GetContextImplFromData(info);
if (CheckContextImplForHostObjectCallback(pContextImpl))
{
auto pvObject = pContextImpl->GetHostObject(info.Holder());
if (pvObject != nullptr)
{
try
{
std::vector names;
HostObjectUtil::GetInstance().GetPropertyNames(pvObject, names);
auto name = pContextImpl->CreateStdString(hName);
for (auto it = names.begin(); it != names.end(); it++)
{
if (it->Compare(name) == 0)
{
CALLBACK_RETURN(v8::None);
}
}
}
catch (const HostException& exception)
{
pContextImpl->ThrowScriptException(exception);
}
}
}
}
//-----------------------------------------------------------------------------
void V8ContextImpl::DeleteHostObjectProperty(v8::Local hKey, const v8::PropertyCallbackInfo& info)
{
auto hName = ::ValueAsString(hKey);
if (hName.IsEmpty())
{
return;
}
auto pContextImpl = ::GetContextImplFromData(info);
if (CheckContextImplForHostObjectCallback(pContextImpl))
{
auto pvObject = pContextImpl->GetHostObject(info.Holder());
if (pvObject != nullptr)
{
try
{
CALLBACK_RETURN(HostObjectUtil::GetInstance().DeleteProperty(pvObject, pContextImpl->CreateStdString(hName)));
}
catch (const HostException& exception)
{
pContextImpl->ThrowScriptException(exception);
}
}
}
}
//-----------------------------------------------------------------------------
void V8ContextImpl::GetHostObjectPropertyNames(const v8::PropertyCallbackInfo& info)
{
FROM_MAYBE_TRY
auto pContextImpl = ::GetContextImplFromData(info);
if (CheckContextImplForHostObjectCallback(pContextImpl))
{
auto pvObject = pContextImpl->GetHostObject(info.Holder());
if (pvObject != nullptr)
{
try
{
std::vector names;
HostObjectUtil::GetInstance().GetPropertyNames(pvObject, names);
auto nameCount = static_cast(names.size());
auto hImportedNames = pContextImpl->CreateArray(nameCount);
for (auto index = 0; index < nameCount; index++)
{
ASSERT_EVAL(FROM_MAYBE(hImportedNames->Set(pContextImpl->m_hContext, index, FROM_MAYBE(pContextImpl->CreateString(names[index])))));
}
CALLBACK_RETURN(hImportedNames);
}
catch (const HostException& exception)
{
pContextImpl->ThrowScriptException(exception);
}
}
}
FROM_MAYBE_CATCH_CONSUME
}
//-----------------------------------------------------------------------------
void V8ContextImpl::GetHostObjectProperty(uint32_t index, const v8::PropertyCallbackInfo& info)
{
auto pContextImpl = ::GetContextImplFromData(info);
if (CheckContextImplForHostObjectCallback(pContextImpl))
{
auto pvObject = pContextImpl->GetHostObject(info.Holder());
if (pvObject != nullptr)
{
try
{
CALLBACK_RETURN(pContextImpl->ImportValue(HostObjectUtil::GetInstance().GetProperty(pvObject, index)));
}
catch (const HostException& exception)
{
pContextImpl->ThrowScriptException(exception);
}
}
}
}
//-----------------------------------------------------------------------------
void V8ContextImpl::SetHostObjectProperty(uint32_t index, v8::Local hValue, const v8::PropertyCallbackInfo& info)
{
auto pContextImpl = ::GetContextImplFromData(info);
if (CheckContextImplForHostObjectCallback(pContextImpl))
{
auto pvObject = pContextImpl->GetHostObject(info.Holder());
if (pvObject != nullptr)
{
try
{
HostObjectUtil::GetInstance().SetProperty(pvObject, index, pContextImpl->ExportValue(hValue));
CALLBACK_RETURN(hValue);
}
catch (const HostException& exception)
{
pContextImpl->ThrowScriptException(exception);
}
}
}
}
//-----------------------------------------------------------------------------
void V8ContextImpl::QueryHostObjectProperty(uint32_t index, const v8::PropertyCallbackInfo& info)
{
auto pContextImpl = ::GetContextImplFromData(info);
if (CheckContextImplForHostObjectCallback(pContextImpl))
{
auto pvObject = pContextImpl->GetHostObject(info.Holder());
if (pvObject != nullptr)
{
try
{
std::vector indices;
HostObjectUtil::GetInstance().GetPropertyIndices(pvObject, indices);
for (auto it = indices.begin(); it < indices.end(); it++)
{
if (*it == static_cast(index))
{
CALLBACK_RETURN(v8::None);
}
}
}
catch (const HostException& exception)
{
pContextImpl->ThrowScriptException(exception);
}
}
}
}
//-----------------------------------------------------------------------------
void V8ContextImpl::DeleteHostObjectProperty(uint32_t index, const v8::PropertyCallbackInfo& info)
{
auto pContextImpl = ::GetContextImplFromData(info);
if (CheckContextImplForHostObjectCallback(pContextImpl))
{
auto pvObject = pContextImpl->GetHostObject(info.Holder());
if (pvObject != nullptr)
{
try
{
CALLBACK_RETURN(HostObjectUtil::GetInstance().DeleteProperty(pvObject, index));
}
catch (const HostException& exception)
{
pContextImpl->ThrowScriptException(exception);
}
}
}
}
//-----------------------------------------------------------------------------
void V8ContextImpl::GetHostObjectPropertyIndices(const v8::PropertyCallbackInfo& info)
{
FROM_MAYBE_TRY
auto pContextImpl = ::GetContextImplFromData(info);
if (CheckContextImplForHostObjectCallback(pContextImpl))
{
auto pvObject = pContextImpl->GetHostObject(info.Holder());
if (pvObject != nullptr)
{
try
{
std::vector indices;
HostObjectUtil::GetInstance().GetPropertyIndices(pvObject, indices);
auto indexCount = static_cast(indices.size());
auto hImportedIndices = pContextImpl->CreateArray(indexCount);
for (auto index = 0; index < indexCount; index++)
{
ASSERT_EVAL(FROM_MAYBE(hImportedIndices->Set(pContextImpl->m_hContext, index, pContextImpl->CreateInteger(indices[index]))));
}
CALLBACK_RETURN(hImportedIndices);
}
catch (const HostException& exception)
{
pContextImpl->ThrowScriptException(exception);
}
}
}
FROM_MAYBE_CATCH_CONSUME
}
//-----------------------------------------------------------------------------
void V8ContextImpl::InvokeHostObject(const v8::FunctionCallbackInfo& info)
{
auto pContextImpl = ::GetContextImplFromData(info);
if (CheckContextImplForHostObjectCallback(pContextImpl))
{
auto pvObject = pContextImpl->GetHostObject(info.Holder());
if (pvObject != nullptr)
{
try
{
auto argCount = info.Length();
std::vector exportedArgs;
exportedArgs.reserve(argCount);
for (auto index = 0; index < argCount; index++)
{
exportedArgs.push_back(pContextImpl->ExportValue(info[index]));
}
CALLBACK_RETURN(pContextImpl->ImportValue(HostObjectUtil::GetInstance().Invoke(pvObject, info.IsConstructCall(), exportedArgs)));
}
catch (const HostException& exception)
{
pContextImpl->ThrowScriptException(exception);
}
}
}
}
//-----------------------------------------------------------------------------
void V8ContextImpl::FlushCallback(const v8::FunctionCallbackInfo& /*info*/)
{
}
//-----------------------------------------------------------------------------
void V8ContextImpl::DisposeWeakHandle(v8::Isolate* pIsolate, Persistent* phObject, HostObjectHolder* pHolder, void* pvV8ObjectCache)
{
IGNORE_UNUSED(pIsolate);
ASSERT_EVAL(HostObjectUtil::GetInstance().RemoveV8ObjectCacheEntry(pvV8ObjectCache, pHolder->GetObject()));
delete pHolder;
phObject->Dispose();
}
//-----------------------------------------------------------------------------
bool V8ContextImpl::TryGetCachedModuleInfo(v8::Local hModule, V8DocumentInfo& documentInfo)
{
_ASSERTE(m_spIsolateImpl->IsCurrent() && m_spIsolateImpl->IsLocked());
for (auto it = m_ModuleCache.begin(); it != m_ModuleCache.end(); it++)
{
if (it->hModule == hModule)
{
m_ModuleCache.splice(m_ModuleCache.begin(), m_ModuleCache, it);
documentInfo = it->DocumentInfo;
return true;
}
}
return false;
}
//-----------------------------------------------------------------------------
bool V8ContextImpl::TryGetCachedModuleInfo(uint64_t uniqueId, V8DocumentInfo& documentInfo)
{
_ASSERTE(m_spIsolateImpl->IsCurrent() && m_spIsolateImpl->IsLocked());
for (auto it = m_ModuleCache.begin(); it != m_ModuleCache.end(); it++)
{
if (it->DocumentInfo.GetUniqueId() == uniqueId)
{
m_ModuleCache.splice(m_ModuleCache.begin(), m_ModuleCache, it);
documentInfo = it->DocumentInfo;
return true;
}
}
return false;
}
//-----------------------------------------------------------------------------
v8::Local V8ContextImpl::GetCachedModule(uint64_t uniqueId, size_t codeDigest)
{
_ASSERTE(m_spIsolateImpl->IsCurrent() && m_spIsolateImpl->IsLocked());
for (auto it = m_ModuleCache.begin(); it != m_ModuleCache.end(); it++)
{
if ((it->DocumentInfo.GetUniqueId() == uniqueId) && (it->CodeDigest == codeDigest))
{
m_ModuleCache.splice(m_ModuleCache.begin(), m_ModuleCache, it);
return it->hModule;
}
}
return v8::Local();
}
//-----------------------------------------------------------------------------
void V8ContextImpl::CacheModule(const V8DocumentInfo& documentInfo, size_t codeDigest, v8::Local hModule)
{
_ASSERTE(m_spIsolateImpl->IsCurrent() && m_spIsolateImpl->IsLocked());
auto maxModuleCacheSize = HostObjectUtil::GetInstance().GetMaxModuleCacheSize();
while (m_ModuleCache.size() >= maxModuleCacheSize)
{
Dispose(m_ModuleCache.back().hModule);
m_ModuleCache.pop_back();
}
_ASSERTE(std::none_of(m_ModuleCache.begin(), m_ModuleCache.end(),
[&documentInfo, codeDigest] (const ModuleCacheEntry& entry)
{
return (entry.DocumentInfo.GetUniqueId() == documentInfo.GetUniqueId()) && (entry.CodeDigest == codeDigest);
}));
ModuleCacheEntry entry { documentInfo, codeDigest, CreatePersistent(hModule) };
m_ModuleCache.push_front(std::move(entry));
m_Statistics.ModuleCacheSize = m_ModuleCache.size();
}
//-----------------------------------------------------------------------------
void V8ContextImpl::ClearModuleCache()
{
_ASSERTE(m_spIsolateImpl->IsCurrent() && m_spIsolateImpl->IsLocked());
while (m_ModuleCache.size() > 0)
{
Dispose(m_ModuleCache.front().hModule);
m_ModuleCache.pop_front();
}
m_Statistics.ModuleCacheSize = m_ModuleCache.size();
}
//-----------------------------------------------------------------------------
bool V8ContextImpl::TryGetCachedScriptInfo(uint64_t uniqueId, V8DocumentInfo& documentInfo)
{
return m_spIsolateImpl->TryGetCachedScriptInfo(uniqueId, documentInfo);
}
//-----------------------------------------------------------------------------
v8::Local V8ContextImpl::GetCachedScript(uint64_t uniqueId, size_t codeDigest)
{
return m_spIsolateImpl->GetCachedScript(uniqueId, codeDigest);
}
//-----------------------------------------------------------------------------
void V8ContextImpl::CacheScript(const V8DocumentInfo& documentInfo, size_t codeDigest, v8::Local hScript)
{
m_spIsolateImpl->CacheScript(documentInfo, codeDigest, hScript);
}
//-----------------------------------------------------------------------------
v8::Local V8ContextImpl::ImportValue(const V8Value& value)
{
FROM_MAYBE_TRY
if (value.IsNonexistent())
{
return v8::Local();
}
if (value.IsUndefined())
{
return GetUndefined();
}
if (value.IsNull())
{
return GetNull();
}
{
bool result;
if (value.AsBoolean(result))
{
return result ? GetTrue() : GetFalse();
}
}
{
double result;
if (value.AsNumber(result))
{
return CreateNumber(result);
}
}
{
int32_t result;
if (value.AsInt32(result))
{
return CreateInteger(result);
}
}
{
uint32_t result;
if (value.AsUInt32(result))
{
return CreateInteger(result);
}
}
{
const StdString* pString;
if (value.AsString(pString))
{
return FROM_MAYBE(CreateString(*pString));
}
}
{
HostObjectHolder* pHolder;
if (value.AsHostObject(pHolder))
{
auto pvV8Object = HostObjectUtil::GetInstance().GetCachedV8Object(m_pvV8ObjectCache, pHolder->GetObject());
if (pvV8Object != nullptr)
{
return CreateLocal(::HandleFromPtr(pvV8Object));
}
v8::Local hObject;
auto invocability = HostObjectUtil::GetInstance().GetInvocability(pHolder->GetObject());
if (invocability == IHostObjectUtil::Invocability::None)
{
BEGIN_PULSE_VALUE_SCOPE(&m_AllowHostObjectConstructorCall, true)
hObject = FROM_MAYBE(m_hHostObjectTemplate->InstanceTemplate()->NewInstance(m_hContext));
END_PULSE_VALUE_SCOPE
}
else if (invocability == IHostObjectUtil::Invocability::Delegate)
{
BEGIN_PULSE_VALUE_SCOPE(&m_AllowHostObjectConstructorCall, true)
hObject = FROM_MAYBE(m_hHostDelegateTemplate->InstanceTemplate()->NewInstance(m_hContext));
END_PULSE_VALUE_SCOPE
}
else
{
BEGIN_PULSE_VALUE_SCOPE(&m_AllowHostObjectConstructorCall, true)
hObject = FROM_MAYBE(m_hHostInvocableTemplate->InstanceTemplate()->NewInstance(m_hContext));
END_PULSE_VALUE_SCOPE
}
ASSERT_EVAL(SetHostObjectHolder(hObject, pHolder = pHolder->Clone()));
ASSERT_EVAL(FROM_MAYBE(hObject->SetPrivate(m_hContext, m_hAccessTokenKey, m_hAccessToken)));
pvV8Object = ::PtrFromHandle(MakeWeak(CreatePersistent(hObject), pHolder, m_pvV8ObjectCache, DisposeWeakHandle));
HostObjectUtil::GetInstance().CacheV8Object(m_pvV8ObjectCache, pHolder->GetObject(), pvV8Object);
return hObject;
}
}
{
V8ObjectHolder* pHolder;
V8Value::Subtype subtype;
V8Value::Flags flags;
if (value.AsV8Object(pHolder, subtype, flags))
{
if (pHolder->IsSameIsolate(m_spIsolateImpl))
{
return CreateLocal(::HandleFromPtr(pHolder->GetObject()));
}
if (HasFlag(flags, V8Value::Flags::Shared))
{
const auto& spSharedObjectInfo = pHolder->GetSharedObjectInfo();
if (!spSharedObjectInfo.IsEmpty())
{
switch (subtype)
{
case V8Value::Subtype::ArrayBuffer:
return CreateSharedArrayBuffer(spSharedObjectInfo->GetBackingStore());
case V8Value::Subtype::DataView:
return v8::DataView::New(CreateSharedArrayBuffer(spSharedObjectInfo->GetBackingStore()), spSharedObjectInfo->GetOffset(), spSharedObjectInfo->GetSize());
case V8Value::Subtype::Uint8Array:
return v8::Uint8Array::New(CreateSharedArrayBuffer(spSharedObjectInfo->GetBackingStore()), spSharedObjectInfo->GetOffset(), spSharedObjectInfo->GetLength());
case V8Value::Subtype::Uint8ClampedArray:
return v8::Uint8ClampedArray::New(CreateSharedArrayBuffer(spSharedObjectInfo->GetBackingStore()), spSharedObjectInfo->GetOffset(), spSharedObjectInfo->GetLength());
case V8Value::Subtype::Int8Array:
return v8::Int8Array::New(CreateSharedArrayBuffer(spSharedObjectInfo->GetBackingStore()), spSharedObjectInfo->GetOffset(), spSharedObjectInfo->GetLength());
case V8Value::Subtype::Uint16Array:
return v8::Uint16Array::New(CreateSharedArrayBuffer(spSharedObjectInfo->GetBackingStore()), spSharedObjectInfo->GetOffset(), spSharedObjectInfo->GetLength());
case V8Value::Subtype::Int16Array:
return v8::Int16Array::New(CreateSharedArrayBuffer(spSharedObjectInfo->GetBackingStore()), spSharedObjectInfo->GetOffset(), spSharedObjectInfo->GetLength());
case V8Value::Subtype::Uint32Array:
return v8::Uint32Array::New(CreateSharedArrayBuffer(spSharedObjectInfo->GetBackingStore()), spSharedObjectInfo->GetOffset(), spSharedObjectInfo->GetLength());
case V8Value::Subtype::Int32Array:
return v8::Int32Array::New(CreateSharedArrayBuffer(spSharedObjectInfo->GetBackingStore()), spSharedObjectInfo->GetOffset(), spSharedObjectInfo->GetLength());
case V8Value::Subtype::BigUint64Array:
return v8::BigUint64Array::New(CreateSharedArrayBuffer(spSharedObjectInfo->GetBackingStore()), spSharedObjectInfo->GetOffset(), spSharedObjectInfo->GetLength());
case V8Value::Subtype::BigInt64Array:
return v8::BigInt64Array::New(CreateSharedArrayBuffer(spSharedObjectInfo->GetBackingStore()), spSharedObjectInfo->GetOffset(), spSharedObjectInfo->GetLength());
case V8Value::Subtype::Float32Array:
return v8::Float32Array::New(CreateSharedArrayBuffer(spSharedObjectInfo->GetBackingStore()), spSharedObjectInfo->GetOffset(), spSharedObjectInfo->GetLength());
case V8Value::Subtype::Float64Array:
return v8::Float64Array::New(CreateSharedArrayBuffer(spSharedObjectInfo->GetBackingStore()), spSharedObjectInfo->GetOffset(), spSharedObjectInfo->GetLength());
default:
break;
}
}
}
return GetUndefined();
}
}
{
double result;
if (value.AsDateTime(result))
{
return FROM_MAYBE(v8::Date::New(m_hContext, result));
}
}
{
const V8BigInt* pBigInt;
if (value.AsBigInt(pBigInt))
{
const auto& words = pBigInt->GetWords();
const auto wordCount = static_cast(std::min(words.size(), static_cast(INT_MAX)));
return FROM_MAYBE(v8::BigInt::NewFromWords(m_hContext, pBigInt->GetSignBit(), wordCount, words.data()));
}
}
FROM_MAYBE_CATCH_CONSUME
return GetUndefined();
}
//-----------------------------------------------------------------------------
V8Value V8ContextImpl::ExportValue(v8::Local hValue)
{
FROM_MAYBE_TRY
if (hValue.IsEmpty())
{
return V8Value(V8Value::Nonexistent);
}
if (hValue->IsUndefined())
{
return V8Value(V8Value::Undefined);
}
if (hValue->IsNull())
{
return V8Value(V8Value::Null);
}
if (hValue->IsBoolean())
{
return V8Value(m_spIsolateImpl->BooleanValue(hValue));
}
if (hValue->IsNumber())
{
return V8Value(FROM_MAYBE(hValue->NumberValue(m_hContext)));
}
if (hValue->IsInt32())
{
return V8Value(FROM_MAYBE(hValue->Int32Value(m_hContext)));
}
if (hValue->IsUint32())
{
return V8Value(FROM_MAYBE(hValue->Uint32Value(m_hContext)));
}
if (hValue->IsString())
{
return V8Value(new StdString(CreateStdString(hValue)));
}
if (m_DateTimeConversionEnabled && hValue->IsDate())
{
return V8Value(V8Value::DateTime, hValue.As()->ValueOf());
}
auto hBigInt = ::ValueAsBigInt(hValue);
if (!hBigInt.IsEmpty())
{
auto signBit = 0;
std::vector words;
auto wordCount = hBigInt->WordCount();
if (wordCount > 0)
{
words.resize(wordCount);
hBigInt->ToWordsArray(&signBit, &wordCount, words.data());
}
return V8Value(new V8BigInt(signBit, std::move(words)));
}
auto hObject = ::ValueAsObject(hValue);
if (!hObject.IsEmpty())
{
auto pHolder = GetHostObjectHolder(hObject);
if (pHolder != nullptr)
{
return V8Value(pHolder->Clone());
}
auto subtype = V8Value::Subtype::None;
auto flags = V8Value::Flags::None;
SharedPtr spSharedObjectInfo;
if (hObject->IsFunction())
{
subtype = V8Value::Subtype::Function;
if (hObject->IsAsyncFunction())
{
flags = CombineFlags(flags, V8Value::Flags::Async);
}
if (hObject->IsGeneratorFunction())
{
flags = CombineFlags(flags, V8Value::Flags::Generator);
}
}
else if (hObject->IsGeneratorObject())
{
subtype = V8Value::Subtype::Iterator;
if (m_hAsyncGeneratorConstructor.IsEmpty())
{
auto hEngineInternal = FROM_MAYBE(m_hContext->Global()->Get(m_hContext, CreateString("EngineInternal"))).As();
m_hAsyncGeneratorConstructor = CreatePersistent(FROM_MAYBE(hEngineInternal->Get(m_hContext, CreateString("asyncGenerator"))));
}
if (FROM_MAYBE(hObject->Get(m_hContext, m_hConstructorKey))->StrictEquals(m_hAsyncGeneratorConstructor))
{
flags = CombineFlags(flags, V8Value::Flags::Async);
}
}
else if (hObject->IsMapIterator() || hObject->IsSetIterator())
{
subtype = V8Value::Subtype::Iterator;
}
else if (hObject->IsPromise())
{
subtype = V8Value::Subtype::Promise;
}
else if (hObject->IsArray())
{
subtype = V8Value::Subtype::Array;
}
else if (hObject->IsArrayBuffer())
{
subtype = V8Value::Subtype::ArrayBuffer;
}
else if (hObject->IsSharedArrayBuffer())
{
subtype = V8Value::Subtype::ArrayBuffer;
flags = CombineFlags(flags, V8Value::Flags::Shared);
auto hSharedArrayBuffer = hObject.As();
auto size = hSharedArrayBuffer->ByteLength();
spSharedObjectInfo = new V8SharedObjectInfo(hSharedArrayBuffer->GetBackingStore(), 0, size, size);
}
else if (hObject->IsArrayBufferView())
{
auto hArrayBufferView = hObject.As();
auto offset = hArrayBufferView->ByteOffset();
auto size = hArrayBufferView->ByteLength();
auto spBackingStore = hArrayBufferView->Buffer()->GetBackingStore();
if (spBackingStore->IsShared())
{
flags = CombineFlags(flags, V8Value::Flags::Shared);
}
if (hObject->IsDataView())
{
subtype = V8Value::Subtype::DataView;
if (HasFlag(flags, V8Value::Flags::Shared))
{
spSharedObjectInfo = new V8SharedObjectInfo(std::move(spBackingStore), offset, size, size);
}
}
else if (hObject->IsTypedArray())
{
if (hObject->IsUint8Array())
{
subtype = V8Value::Subtype::Uint8Array;
}
else if (hObject->IsUint8ClampedArray())
{
subtype = V8Value::Subtype::Uint8ClampedArray;
}
else if (hObject->IsInt8Array())
{
subtype = V8Value::Subtype::Int8Array;
}
else if (hObject->IsUint16Array())
{
subtype = V8Value::Subtype::Uint16Array;
}
else if (hObject->IsInt16Array())
{
subtype = V8Value::Subtype::Int16Array;
}
else if (hObject->IsUint32Array())
{
subtype = V8Value::Subtype::Uint32Array;
}
else if (hObject->IsInt32Array())
{
subtype = V8Value::Subtype::Int32Array;
}
else if (hObject->IsBigUint64Array())
{
subtype = V8Value::Subtype::BigUint64Array;
}
else if (hObject->IsBigInt64Array())
{
subtype = V8Value::Subtype::BigInt64Array;
}
else if (hObject->IsFloat32Array())
{
subtype = V8Value::Subtype::Float32Array;
}
else if (hObject->IsFloat64Array())
{
subtype = V8Value::Subtype::Float64Array;
}
if (HasFlag(flags, V8Value::Flags::Shared) && (subtype != V8Value::Subtype::None))
{
auto hTypedArray = hObject.As();
spSharedObjectInfo = new V8SharedObjectInfo(std::move(spBackingStore), offset, size, hTypedArray->Length());
}
}
}
return V8Value(new V8ObjectHolderImpl(GetWeakBinding(), ::PtrFromHandle(CreatePersistent(hObject)), hObject->GetIdentityHash(), spSharedObjectInfo), subtype, flags);
}
FROM_MAYBE_CATCH_CONSUME
return V8Value(V8Value::Undefined);
}
//-----------------------------------------------------------------------------
void V8ContextImpl::ImportValues(const std::vector& values, std::vector& importedValues)
{
importedValues.clear();
auto valueCount = static_cast(values.size());
importedValues.reserve(valueCount);
for (auto index = 0; index < valueCount; index++)
{
importedValues.push_back(ImportValue(values[index]));
}
}
//-----------------------------------------------------------------------------
v8::ScriptOrigin V8ContextImpl::CreateScriptOrigin(const V8DocumentInfo& documentInfo)
{
FROM_MAYBE_TRY
auto uniqueId = documentInfo.GetUniqueId();
_ASSERTE(uniqueId > 0);
auto hHostDefinedOptions = CreatePrimitiveArray(1);
SetPrimitiveArrayItem(hHostDefinedOptions, 0, CreateBigInt(uniqueId));
return CreateScriptOrigin(
FROM_MAYBE(CreateString(documentInfo.GetResourceName())),
0,
0,
false,
-1,
(documentInfo.GetSourceMapUrl().GetLength() > 0) ? FROM_MAYBE(CreateString(documentInfo.GetSourceMapUrl())) : v8::Local(),
false,
false,
documentInfo.IsModule(),
hHostDefinedOptions
);
FROM_MAYBE_CATCH
throw;
FROM_MAYBE_END
}
//-----------------------------------------------------------------------------
void V8ContextImpl::Verify(const V8IsolateImpl::ExecutionScope& isolateExecutionScope, const v8::TryCatch& tryCatch)
{
if (tryCatch.HasCaught())
{
if (!tryCatch.CanContinue())
{
VerifyNotOutOfMemory();
throw V8Exception(V8Exception::Type::Interrupt, m_Name, StdString(SL("Script execution interrupted by host")), CreateStdString(FROM_MAYBE_DEFAULT(tryCatch.StackTrace(m_hContext))), isolateExecutionScope.ExecutionStarted(), V8Value(V8Value::Null), V8Value(V8Value::Undefined));
}
auto hException = tryCatch.Exception();
if (hException->SameValue(m_hTerminationException))
{
VerifyNotOutOfMemory();
throw V8Exception(V8Exception::Type::Interrupt, m_Name, StdString(SL("Script execution interrupted by host")), CreateStdString(FROM_MAYBE_DEFAULT(tryCatch.StackTrace(m_hContext))), isolateExecutionScope.ExecutionStarted(), V8Value(V8Value::Null), V8Value(V8Value::Undefined));
}
StdString message;
bool stackOverflow;
StdString constructorName;
if (hException->IsObject())
{
constructorName = CreateStdString(hException.As()->GetConstructorName());
}
auto value = CreateStdString(hException);
if (value.GetLength() > 0)
{
message = std::move(value);
stackOverflow = (strcmp(message.ToUTF8().c_str(), "RangeError: Maximum call stack size exceeded") == 0);
}
else if (!hException->IsObject())
{
message = SL("Unknown error; an unrecognized value was thrown and not caught");
stackOverflow = false;
}
else
{
if ((constructorName == SL("Error")) || (constructorName == SL("RangeError")))
{
// It is unclear why V8 sometimes throws Error or RangeError objects that convert
// to empty strings, but it probably has to do with memory pressure. It seems to
// happen only during stack overflow recovery.
message = SL("Unknown error (");
message += constructorName;
message += SL("); potential stack overflow detected");
stackOverflow = true;
}
else if (constructorName.GetLength() > 0)
{
message = SL("Unknown error (");
message += constructorName;
message += SL(")");
stackOverflow = false;
}
else
{
message = SL("Unknown error; an unrecognized object was thrown and not caught");
stackOverflow = false;
}
}
StdString stackTrace;
V8Value hostException(V8Value::Undefined);
if (stackOverflow)
{
stackTrace = message;
}
else
{
auto hStackTrace = FROM_MAYBE_DEFAULT(tryCatch.StackTrace(m_hContext));
if (!hStackTrace.IsEmpty())
{
stackTrace = CreateStdString(hStackTrace);
}
auto hMessage = tryCatch.Message();
if (!hMessage.IsEmpty())
{
if (message.GetLength() < 1)
{
message = CreateStdString(hMessage->Get());
}
stackTrace = message;
auto hMessageStackTrace = hMessage->GetStackTrace();
auto frameCount = !hMessageStackTrace.IsEmpty() ? hMessageStackTrace->GetFrameCount() : 0;
auto usedSourceLine = false;
if ((frameCount < 1) || (constructorName == SL("SyntaxError")))
{
auto hScriptResourceName = hMessage->GetScriptResourceName();
if (!hScriptResourceName.IsEmpty() && !hScriptResourceName->IsNull() && !hScriptResourceName->IsUndefined())
{
auto hScriptName = FROM_MAYBE_DEFAULT(hScriptResourceName->ToString(m_hContext));
if (!hScriptName.IsEmpty() && (hScriptName->Length() > 0))
{
stackTrace += SL("\n at ");
stackTrace += CreateStdString(hScriptName);
}
else
{
stackTrace += SL("\n at ");
}
}
else
{
stackTrace += SL("\n at ");
}
stackTrace += SL(':');
stackTrace += StdString(std::to_string(FROM_MAYBE_DEFAULT(hMessage->GetLineNumber(m_hContext))));
stackTrace += SL(':');
stackTrace += StdString(std::to_string(FROM_MAYBE_DEFAULT(hMessage->GetStartColumn(m_hContext)) + 1));
auto hSourceLine = FROM_MAYBE_DEFAULT(hMessage->GetSourceLine(m_hContext));
if (!hSourceLine.IsEmpty() && (hSourceLine->Length() > 0))
{
stackTrace += SL(" -> ");
stackTrace += CreateStdString(hSourceLine);
}
usedSourceLine = true;
}
for (int index = 0; index < frameCount; index++)
{
auto hFrame = GetStackFrame(hMessageStackTrace, index);
stackTrace += SL("\n at ");
auto hFunctionName = hFrame->GetFunctionName();
if (!hFunctionName.IsEmpty() && (hFunctionName->Length() > 0))
{
if (hFrame->IsConstructor())
{
stackTrace += SL("new ");
}
stackTrace += CreateStdString(hFunctionName);
stackTrace += SL(" (");
}
auto hScriptName = hFrame->GetScriptName();
if (!hScriptName.IsEmpty() && (hScriptName->Length() > 0))
{
stackTrace += CreateStdString(hScriptName);
}
else
{
stackTrace += SL("");
}
stackTrace += SL(':');
auto lineNumber = hFrame->GetLineNumber();
if (lineNumber != v8::Message::kNoLineNumberInfo)
{
stackTrace += StdString(std::to_string(lineNumber));
}
stackTrace += SL(':');
auto column = hFrame->GetColumn();
if (column != v8::Message::kNoColumnInfo)
{
stackTrace += StdString(std::to_string(column));
}
if (!hFunctionName.IsEmpty() && (hFunctionName->Length() > 0))
{
stackTrace += L')';
}
if (!usedSourceLine)
{
auto hSourceLine = FROM_MAYBE_DEFAULT(hMessage->GetSourceLine(m_hContext));
if (!hSourceLine.IsEmpty() && (hSourceLine->Length() > 0))
{
stackTrace += SL(" -> ");
stackTrace += CreateStdString(hSourceLine);
}
usedSourceLine = true;
}
}
}
if (hException->IsObject())
{
hostException = ExportValue(FROM_MAYBE_DEFAULT(hException.As()->Get(m_hContext, m_hHostExceptionKey)));
}
}
throw V8Exception(V8Exception::Type::General, m_Name, std::move(message), std::move(stackTrace), isolateExecutionScope.ExecutionStarted(), ExportValue(hException), std::move(hostException));
}
}
//-----------------------------------------------------------------------------
void V8ContextImpl::VerifyNotOutOfMemory()
{
if (m_spIsolateImpl->IsOutOfMemory())
{
m_spIsolateImpl->ThrowOutOfMemoryException();
}
}
//-----------------------------------------------------------------------------
void V8ContextImpl::ThrowScriptException(const HostException& exception)
{
FROM_MAYBE_TRY
// WARNING: Error instantiation may fail during script interruption. Check Exception::Error()
// result to avoid access violations. Extra defense is warranted here.
if (!IsExecutionTerminating())
{
auto hException = v8::Exception::Error(FROM_MAYBE(CreateString(exception.GetMessage()))).As();
if (!hException.IsEmpty() && hException->IsObject())
{
if (!m_HideHostExceptions)
{
auto hHostException = ImportValue(exception.GetException());
if (!hHostException.IsEmpty() && hHostException->IsObject())
{
ASSERT_EVAL(FROM_MAYBE(hException->Set(m_hContext, m_hHostExceptionKey, hHostException)));
}
}
ThrowException(hException);
}
}
FROM_MAYBE_CATCH_CONSUME
}