From b7c7df6a979d16d926814f601246234bf65adbc2 Mon Sep 17 00:00:00 2001 From: Tom Sepez Date: Fri, 9 Feb 2018 19:08:59 +0000 Subject: Rename fxjs_v8.{h,cpp} to cfxjs_engine.{h,cpp} Place the template map definitions entirely in .cpp file. Change-Id: I2643f1b99f5582b69aa985857c4aa6f9b5ab57c8 Reviewed-on: https://pdfium-review.googlesource.com/26150 Commit-Queue: Tom Sepez Reviewed-by: dsinclair --- BUILD.gn | 6 +- fxjs/JS_Define.h | 2 +- fxjs/cfxjs_engine.cpp | 581 +++++++++++++++++++++++++++++++++++++ fxjs/cfxjs_engine.h | 157 ++++++++++ fxjs/cfxjs_engine_embeddertest.cpp | 99 +++++++ fxjs/cfxjse_context.cpp | 2 +- fxjs/cfxjse_engine.cpp | 2 +- fxjs/cfxjse_runtimedata.cpp | 2 +- fxjs/cjs_object.h | 2 +- fxjs/cjs_return.h | 2 +- fxjs/cjs_runtime.h | 2 +- fxjs/fxjs_v8.cpp | 537 ---------------------------------- fxjs/fxjs_v8.h | 200 ------------- fxjs/fxjs_v8_embeddertest.cpp | 97 ------- testing/js_embedder_test.h | 2 +- testing/xfa_js_embedder_test.h | 2 +- 16 files changed, 849 insertions(+), 846 deletions(-) create mode 100644 fxjs/cfxjs_engine.cpp create mode 100644 fxjs/cfxjs_engine.h create mode 100644 fxjs/cfxjs_engine_embeddertest.cpp delete mode 100644 fxjs/fxjs_v8.cpp delete mode 100644 fxjs/fxjs_v8.h delete mode 100644 fxjs/fxjs_v8_embeddertest.cpp diff --git a/BUILD.gn b/BUILD.gn index 2355bb3122..7488f878ea 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -1203,6 +1203,8 @@ jumbo_static_library("fxjs") { "fxjs/JS_Define.h", "fxjs/cfx_v8.cpp", "fxjs/cfx_v8.h", + "fxjs/cfxjs_engine.cpp", + "fxjs/cfxjs_engine.h", "fxjs/cjs_annot.cpp", "fxjs/cjs_annot.h", "fxjs/cjs_app.cpp", @@ -1271,8 +1273,6 @@ jumbo_static_library("fxjs") { "fxjs/cjs_util.h", "fxjs/cjs_zoomtype.cpp", "fxjs/cjs_zoomtype.h", - "fxjs/fxjs_v8.cpp", - "fxjs/fxjs_v8.h", "fxjs/global_timer.cpp", "fxjs/global_timer.h", "fxjs/js_resources.cpp", @@ -3028,8 +3028,8 @@ test("pdfium_embeddertests") { if (pdf_enable_v8) { sources += [ + "fxjs/cfxjs_engine_embeddertest.cpp", "fxjs/cjs_publicmethods_embeddertest.cpp", - "fxjs/fxjs_v8_embeddertest.cpp", "testing/js_embedder_test.cpp", "testing/js_embedder_test.h", ] diff --git a/fxjs/JS_Define.h b/fxjs/JS_Define.h index 58c094fe37..d9ab139374 100644 --- a/fxjs/JS_Define.h +++ b/fxjs/JS_Define.h @@ -10,9 +10,9 @@ #include #include +#include "fxjs/cfxjs_engine.h" #include "fxjs/cjs_object.h" #include "fxjs/cjs_return.h" -#include "fxjs/fxjs_v8.h" #include "fxjs/js_resources.h" #include "third_party/base/ptr_util.h" diff --git a/fxjs/cfxjs_engine.cpp b/fxjs/cfxjs_engine.cpp new file mode 100644 index 0000000000..c773a16324 --- /dev/null +++ b/fxjs/cfxjs_engine.cpp @@ -0,0 +1,581 @@ +// Copyright 2014 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#include "fxjs/cfxjs_engine.h" + +#include +#include +#include + +#include "fxjs/cfxjse_runtimedata.h" +#include "fxjs/cjs_object.h" + +// Keep this consistent with the values defined in gin/public/context_holder.h +// (without actually requiring a dependency on gin itself for the standalone +// embedders of PDFIum). The value we want to use is: +// kPerContextDataStartIndex + kEmbedderPDFium, which is 3. +static const unsigned int kPerContextDataIndex = 3u; +static unsigned int g_embedderDataSlot = 1u; +static v8::Isolate* g_isolate = nullptr; +static size_t g_isolate_ref_count = 0; +static CFX_V8ArrayBufferAllocator* g_arrayBufferAllocator = nullptr; +static v8::Global* g_DefaultGlobalObjectTemplate = nullptr; +static wchar_t kPerObjectDataTag[] = L"CFXJS_PerObjectData"; + +// Global weak map to save dynamic objects. +class V8TemplateMapTraits : public v8::StdMapTraits { + public: + typedef v8::GlobalValueMap MapType; + typedef void WeakCallbackDataType; + + static WeakCallbackDataType* + WeakCallbackParameter(MapType* map, void* key, v8::Local value) { + return key; + } + static MapType* MapFromWeakCallbackInfo( + const v8::WeakCallbackInfo&); + + static void* KeyFromWeakCallbackInfo( + const v8::WeakCallbackInfo& data) { + return data.GetParameter(); + } + static const v8::PersistentContainerCallbackType kCallbackType = + v8::kWeakWithInternalFields; + static void DisposeWeak( + const v8::WeakCallbackInfo& data) {} + static void OnWeakCallback( + const v8::WeakCallbackInfo& data) {} + static void Dispose(v8::Isolate* isolate, + v8::Global value, + void* key); + static void DisposeCallbackData(WeakCallbackDataType* callbackData) {} +}; + +class V8TemplateMap { + public: + typedef v8::GlobalValueMap MapType; + + explicit V8TemplateMap(v8::Isolate* isolate); + ~V8TemplateMap(); + + void set(void* key, v8::Local handle); + + friend class V8TemplateMapTraits; + + private: + MapType m_map; +}; + +class CFXJS_PerObjectData { + public: + explicit CFXJS_PerObjectData(int nObjDefID) : m_ObjDefID(nObjDefID) {} + + ~CFXJS_PerObjectData() = default; + + static void SetInObject(CFXJS_PerObjectData* pData, + v8::Local pObj) { + if (pObj->InternalFieldCount() == 2) { + pObj->SetAlignedPointerInInternalField(0, pData); + pObj->SetAlignedPointerInInternalField( + 1, static_cast(kPerObjectDataTag)); + } + } + + static CFXJS_PerObjectData* GetFromObject(v8::Local pObj) { + if (pObj.IsEmpty() || pObj->InternalFieldCount() != 2 || + pObj->GetAlignedPointerFromInternalField(1) != + static_cast(kPerObjectDataTag)) { + return nullptr; + } + return static_cast( + pObj->GetAlignedPointerFromInternalField(0)); + } + + const int m_ObjDefID; + std::unique_ptr m_pPrivate; +}; + +class CFXJS_ObjDefinition { + public: + static int MaxID(v8::Isolate* pIsolate) { + return FXJS_PerIsolateData::Get(pIsolate)->m_ObjectDefnArray.size(); + } + + static CFXJS_ObjDefinition* ForID(v8::Isolate* pIsolate, int id) { + // Note: GetAt() halts if out-of-range even in release builds. + return FXJS_PerIsolateData::Get(pIsolate)->m_ObjectDefnArray[id].get(); + } + + CFXJS_ObjDefinition(v8::Isolate* isolate, + const char* sObjName, + FXJSOBJTYPE eObjType, + CFXJS_Engine::Constructor pConstructor, + CFXJS_Engine::Destructor pDestructor) + : m_ObjName(sObjName), + m_ObjType(eObjType), + m_pConstructor(pConstructor), + m_pDestructor(pDestructor), + m_pIsolate(isolate) { + v8::Isolate::Scope isolate_scope(isolate); + v8::HandleScope handle_scope(isolate); + + v8::Local fun = v8::FunctionTemplate::New(isolate); + fun->InstanceTemplate()->SetInternalFieldCount(2); + fun->SetCallHandler([](const v8::FunctionCallbackInfo& info) { + v8::Local holder = info.Holder(); + ASSERT(holder->InternalFieldCount() == 2); + holder->SetAlignedPointerInInternalField(0, nullptr); + holder->SetAlignedPointerInInternalField(1, nullptr); + }); + if (eObjType == FXJSOBJTYPE_GLOBAL) { + fun->InstanceTemplate()->Set( + v8::Symbol::GetToStringTag(isolate), + v8::String::NewFromUtf8(isolate, "global", v8::NewStringType::kNormal) + .ToLocalChecked()); + } + m_FunctionTemplate.Reset(isolate, fun); + + v8::Local sig = v8::Signature::New(isolate, fun); + m_Signature.Reset(isolate, sig); + } + + int AssignID() { + FXJS_PerIsolateData* pData = FXJS_PerIsolateData::Get(m_pIsolate); + pData->m_ObjectDefnArray.emplace_back(this); + return pData->m_ObjectDefnArray.size() - 1; + } + + v8::Local GetInstanceTemplate() { + v8::EscapableHandleScope scope(m_pIsolate); + v8::Local function = + m_FunctionTemplate.Get(m_pIsolate); + return scope.Escape(function->InstanceTemplate()); + } + + v8::Local GetSignature() { + v8::EscapableHandleScope scope(m_pIsolate); + return scope.Escape(m_Signature.Get(m_pIsolate)); + } + + const char* const m_ObjName; + const FXJSOBJTYPE m_ObjType; + const CFXJS_Engine::Constructor m_pConstructor; + const CFXJS_Engine::Destructor m_pDestructor; + + v8::Isolate* m_pIsolate; + v8::Global m_FunctionTemplate; + v8::Global m_Signature; +}; + +static v8::Local GetGlobalObjectTemplate( + v8::Isolate* pIsolate) { + int maxID = CFXJS_ObjDefinition::MaxID(pIsolate); + for (int i = 0; i < maxID; ++i) { + CFXJS_ObjDefinition* pObjDef = CFXJS_ObjDefinition::ForID(pIsolate, i); + if (pObjDef->m_ObjType == FXJSOBJTYPE_GLOBAL) + return pObjDef->GetInstanceTemplate(); + } + if (!g_DefaultGlobalObjectTemplate) { + v8::Local hGlobalTemplate = + v8::ObjectTemplate::New(pIsolate); + hGlobalTemplate->Set( + v8::Symbol::GetToStringTag(pIsolate), + v8::String::NewFromUtf8(pIsolate, "global", v8::NewStringType::kNormal) + .ToLocalChecked()); + g_DefaultGlobalObjectTemplate = + new v8::Global(pIsolate, hGlobalTemplate); + } + return g_DefaultGlobalObjectTemplate->Get(pIsolate); +} + +void V8TemplateMapTraits::Dispose(v8::Isolate* isolate, + v8::Global value, + void* key) { + v8::Local obj = value.Get(isolate); + if (obj.IsEmpty()) + return; + int id = CFXJS_Engine::GetObjDefnID(obj); + if (id == -1) + return; + CFXJS_ObjDefinition* pObjDef = CFXJS_ObjDefinition::ForID(isolate, id); + if (!pObjDef) + return; + if (pObjDef->m_pDestructor) + pObjDef->m_pDestructor(obj); + CFXJS_Engine::FreeObjectPrivate(obj); +} + +V8TemplateMapTraits::MapType* V8TemplateMapTraits::MapFromWeakCallbackInfo( + const v8::WeakCallbackInfo& data) { + V8TemplateMap* pMap = + (FXJS_PerIsolateData::Get(data.GetIsolate()))->m_pDynamicObjsMap.get(); + return pMap ? &pMap->m_map : nullptr; +} + +void FXJS_Initialize(unsigned int embedderDataSlot, v8::Isolate* pIsolate) { + if (g_isolate) { + ASSERT(g_embedderDataSlot == embedderDataSlot); + ASSERT(g_isolate == pIsolate); + return; + } + g_embedderDataSlot = embedderDataSlot; + g_isolate = pIsolate; +} + +void FXJS_Release() { + ASSERT(!g_isolate || g_isolate_ref_count == 0); + delete g_DefaultGlobalObjectTemplate; + g_DefaultGlobalObjectTemplate = nullptr; + g_isolate = nullptr; + + delete g_arrayBufferAllocator; + g_arrayBufferAllocator = nullptr; +} + +bool FXJS_GetIsolate(v8::Isolate** pResultIsolate) { + if (g_isolate) { + *pResultIsolate = g_isolate; + return false; + } + // Provide backwards compatibility when no external isolate. + if (!g_arrayBufferAllocator) + g_arrayBufferAllocator = new CFX_V8ArrayBufferAllocator(); + v8::Isolate::CreateParams params; + params.array_buffer_allocator = g_arrayBufferAllocator; + *pResultIsolate = v8::Isolate::New(params); + return true; +} + +size_t FXJS_GlobalIsolateRefCount() { + return g_isolate_ref_count; +} + +V8TemplateMap::V8TemplateMap(v8::Isolate* isolate) : m_map(isolate) {} + +V8TemplateMap::~V8TemplateMap() {} + +void V8TemplateMap::set(void* key, v8::Local handle) { + ASSERT(!m_map.Contains(key)); + m_map.Set(key, handle); +} + +FXJS_PerIsolateData::~FXJS_PerIsolateData() {} + +// static +void FXJS_PerIsolateData::SetUp(v8::Isolate* pIsolate) { + if (!pIsolate->GetData(g_embedderDataSlot)) + pIsolate->SetData(g_embedderDataSlot, new FXJS_PerIsolateData(pIsolate)); +} + +// static +FXJS_PerIsolateData* FXJS_PerIsolateData::Get(v8::Isolate* pIsolate) { + return static_cast( + pIsolate->GetData(g_embedderDataSlot)); +} + +FXJS_PerIsolateData::FXJS_PerIsolateData(v8::Isolate* pIsolate) + : m_pDynamicObjsMap(new V8TemplateMap(pIsolate)) {} + +CFXJS_Engine::CFXJS_Engine() : CFX_V8(nullptr) {} + +CFXJS_Engine::CFXJS_Engine(v8::Isolate* pIsolate) : CFX_V8(pIsolate) {} + +CFXJS_Engine::~CFXJS_Engine() = default; + +// static +CFXJS_Engine* CFXJS_Engine::EngineFromIsolateCurrentContext( + v8::Isolate* pIsolate) { + return EngineFromContext(pIsolate->GetCurrentContext()); +} + +// static +CFXJS_Engine* CFXJS_Engine::EngineFromContext(v8::Local pContext) { + return static_cast( + pContext->GetAlignedPointerFromEmbedderData(kPerContextDataIndex)); +} + +// static +int CFXJS_Engine::GetObjDefnID(v8::Local pObj) { + CFXJS_PerObjectData* pData = CFXJS_PerObjectData::GetFromObject(pObj); + return pData ? pData->m_ObjDefID : -1; +} + +// static +void CFXJS_Engine::SetObjectPrivate(v8::Local pObj, + std::unique_ptr p) { + CFXJS_PerObjectData* pPerObjectData = + CFXJS_PerObjectData::GetFromObject(pObj); + if (!pPerObjectData) + return; + pPerObjectData->m_pPrivate = std::move(p); +} + +// static +void CFXJS_Engine::FreeObjectPrivate(v8::Local pObj) { + CFXJS_PerObjectData* pData = CFXJS_PerObjectData::GetFromObject(pObj); + pObj->SetAlignedPointerInInternalField(0, nullptr); + pObj->SetAlignedPointerInInternalField(1, nullptr); + delete pData; +} + +void CFXJS_Engine::SetIntoContext(v8::Local pContext) { + pContext->SetAlignedPointerInEmbedderData(kPerContextDataIndex, this); +} + +int CFXJS_Engine::DefineObj(const char* sObjName, + FXJSOBJTYPE eObjType, + CFXJS_Engine::Constructor pConstructor, + CFXJS_Engine::Destructor pDestructor) { + v8::Isolate::Scope isolate_scope(GetIsolate()); + v8::HandleScope handle_scope(GetIsolate()); + FXJS_PerIsolateData::SetUp(GetIsolate()); + CFXJS_ObjDefinition* pObjDef = new CFXJS_ObjDefinition( + GetIsolate(), sObjName, eObjType, pConstructor, pDestructor); + return pObjDef->AssignID(); +} + +void CFXJS_Engine::DefineObjMethod(int nObjDefnID, + const char* sMethodName, + v8::FunctionCallback pMethodCall) { + v8::Isolate::Scope isolate_scope(GetIsolate()); + v8::HandleScope handle_scope(GetIsolate()); + CFXJS_ObjDefinition* pObjDef = + CFXJS_ObjDefinition::ForID(GetIsolate(), nObjDefnID); + v8::Local fun = v8::FunctionTemplate::New( + GetIsolate(), pMethodCall, v8::Local(), + pObjDef->GetSignature()); + fun->RemovePrototype(); + pObjDef->GetInstanceTemplate()->Set(NewString(sMethodName), fun, + v8::ReadOnly); +} + +void CFXJS_Engine::DefineObjProperty(int nObjDefnID, + const char* sPropName, + v8::AccessorGetterCallback pPropGet, + v8::AccessorSetterCallback pPropPut) { + v8::Isolate::Scope isolate_scope(GetIsolate()); + v8::HandleScope handle_scope(GetIsolate()); + CFXJS_ObjDefinition* pObjDef = + CFXJS_ObjDefinition::ForID(GetIsolate(), nObjDefnID); + pObjDef->GetInstanceTemplate()->SetAccessor(NewString(sPropName), pPropGet, + pPropPut); +} + +void CFXJS_Engine::DefineObjAllProperties( + int nObjDefnID, + v8::NamedPropertyQueryCallback pPropQurey, + v8::NamedPropertyGetterCallback pPropGet, + v8::NamedPropertySetterCallback pPropPut, + v8::NamedPropertyDeleterCallback pPropDel) { + v8::Isolate::Scope isolate_scope(GetIsolate()); + v8::HandleScope handle_scope(GetIsolate()); + CFXJS_ObjDefinition* pObjDef = + CFXJS_ObjDefinition::ForID(GetIsolate(), nObjDefnID); + pObjDef->GetInstanceTemplate()->SetNamedPropertyHandler(pPropGet, pPropPut, + pPropQurey, pPropDel); +} + +void CFXJS_Engine::DefineObjConst(int nObjDefnID, + const char* sConstName, + v8::Local pDefault) { + v8::Isolate::Scope isolate_scope(GetIsolate()); + v8::HandleScope handle_scope(GetIsolate()); + CFXJS_ObjDefinition* pObjDef = + CFXJS_ObjDefinition::ForID(GetIsolate(), nObjDefnID); + pObjDef->GetInstanceTemplate()->Set(GetIsolate(), sConstName, pDefault); +} + +void CFXJS_Engine::DefineGlobalMethod(const char* sMethodName, + v8::FunctionCallback pMethodCall) { + v8::Isolate::Scope isolate_scope(GetIsolate()); + v8::HandleScope handle_scope(GetIsolate()); + v8::Local fun = + v8::FunctionTemplate::New(GetIsolate(), pMethodCall); + fun->RemovePrototype(); + GetGlobalObjectTemplate(GetIsolate()) + ->Set(NewString(sMethodName), fun, v8::ReadOnly); +} + +void CFXJS_Engine::DefineGlobalConst(const wchar_t* sConstName, + v8::FunctionCallback pConstGetter) { + v8::Isolate::Scope isolate_scope(GetIsolate()); + v8::HandleScope handle_scope(GetIsolate()); + v8::Local fun = + v8::FunctionTemplate::New(GetIsolate(), pConstGetter); + fun->RemovePrototype(); + GetGlobalObjectTemplate(GetIsolate()) + ->SetAccessorProperty(NewString(sConstName), fun); +} + +void CFXJS_Engine::InitializeEngine() { + if (GetIsolate() == g_isolate) + ++g_isolate_ref_count; + + v8::Isolate::Scope isolate_scope(GetIsolate()); + v8::HandleScope handle_scope(GetIsolate()); + + // This has to happen before we call GetGlobalObjectTemplate because that + // method gets the PerIsolateData from GetIsolate(). + FXJS_PerIsolateData::SetUp(GetIsolate()); + + v8::Local v8Context = v8::Context::New( + GetIsolate(), nullptr, GetGlobalObjectTemplate(GetIsolate())); + v8::Context::Scope context_scope(v8Context); + SetIntoContext(v8Context); + + int maxID = CFXJS_ObjDefinition::MaxID(GetIsolate()); + m_StaticObjects.resize(maxID + 1); + for (int i = 0; i < maxID; ++i) { + CFXJS_ObjDefinition* pObjDef = CFXJS_ObjDefinition::ForID(GetIsolate(), i); + if (pObjDef->m_ObjType == FXJSOBJTYPE_GLOBAL) { + CFXJS_PerObjectData::SetInObject(new CFXJS_PerObjectData(i), + v8Context->Global() + ->GetPrototype() + ->ToObject(v8Context) + .ToLocalChecked()); + if (pObjDef->m_pConstructor) { + pObjDef->m_pConstructor(this, v8Context->Global() + ->GetPrototype() + ->ToObject(v8Context) + .ToLocalChecked()); + } + } else if (pObjDef->m_ObjType == FXJSOBJTYPE_STATIC) { + v8::Local pObjName = NewString(pObjDef->m_ObjName); + v8::Local obj = NewFXJSBoundObject(i, true); + if (!obj.IsEmpty()) { + v8Context->Global()->Set(v8Context, pObjName, obj).FromJust(); + m_StaticObjects[i] = v8::Global(GetIsolate(), obj); + } + } + } + m_V8Context.Reset(GetIsolate(), v8Context); +} + +void CFXJS_Engine::ReleaseEngine() { + v8::Isolate::Scope isolate_scope(GetIsolate()); + v8::HandleScope handle_scope(GetIsolate()); + v8::Local context = GetV8Context(); + v8::Context::Scope context_scope(context); + FXJS_PerIsolateData* pData = FXJS_PerIsolateData::Get(GetIsolate()); + if (!pData) + return; + + m_ConstArrays.clear(); + + int maxID = CFXJS_ObjDefinition::MaxID(GetIsolate()); + for (int i = 0; i < maxID; ++i) { + CFXJS_ObjDefinition* pObjDef = CFXJS_ObjDefinition::ForID(GetIsolate(), i); + v8::Local pObj; + if (pObjDef->m_ObjType == FXJSOBJTYPE_GLOBAL) { + pObj = + context->Global()->GetPrototype()->ToObject(context).ToLocalChecked(); + } else if (!m_StaticObjects[i].IsEmpty()) { + pObj = v8::Local::New(GetIsolate(), m_StaticObjects[i]); + m_StaticObjects[i].Reset(); + } + + if (!pObj.IsEmpty()) { + if (pObjDef->m_pDestructor) + pObjDef->m_pDestructor(pObj); + FreeObjectPrivate(pObj); + } + } + + m_V8Context.Reset(); + + if (GetIsolate() == g_isolate && --g_isolate_ref_count > 0) + return; + + delete pData; + GetIsolate()->SetData(g_embedderDataSlot, nullptr); +} + +int CFXJS_Engine::Execute(const WideString& script, FXJSErr* pError) { + v8::Isolate::Scope isolate_scope(GetIsolate()); + v8::TryCatch try_catch(GetIsolate()); + v8::Local context = GetIsolate()->GetCurrentContext(); + v8::Local compiled_script; + if (!v8::Script::Compile(context, NewString(script.AsStringView())) + .ToLocal(&compiled_script)) { + v8::String::Utf8Value error(GetIsolate(), try_catch.Exception()); + // TODO(tsepez): return error via pError->message. + return -1; + } + + v8::Local result; + if (!compiled_script->Run(context).ToLocal(&result)) { + v8::String::Utf8Value error(GetIsolate(), try_catch.Exception()); + // TODO(tsepez): return error via pError->message. + return -1; + } + return 0; +} + +v8::Local CFXJS_Engine::NewFXJSBoundObject(int nObjDefnID, + bool bStatic) { + v8::Isolate::Scope isolate_scope(GetIsolate()); + v8::Local context = GetIsolate()->GetCurrentContext(); + FXJS_PerIsolateData* pData = FXJS_PerIsolateData::Get(GetIsolate()); + if (!pData) + return v8::Local(); + + if (nObjDefnID < 0 || nObjDefnID >= CFXJS_ObjDefinition::MaxID(GetIsolate())) + return v8::Local(); + + CFXJS_ObjDefinition* pObjDef = + CFXJS_ObjDefinition::ForID(GetIsolate(), nObjDefnID); + v8::Local obj; + if (!pObjDef->GetInstanceTemplate()->NewInstance(context).ToLocal(&obj)) + return v8::Local(); + + CFXJS_PerObjectData* pObjData = new CFXJS_PerObjectData(nObjDefnID); + CFXJS_PerObjectData::SetInObject(pObjData, obj); + if (pObjDef->m_pConstructor) + pObjDef->m_pConstructor(this, obj); + + if (!bStatic && FXJS_PerIsolateData::Get(GetIsolate())->m_pDynamicObjsMap) + FXJS_PerIsolateData::Get(GetIsolate()) + ->m_pDynamicObjsMap->set(pObjData, obj); + + return obj; +} + +v8::Local CFXJS_Engine::GetThisObj() { + v8::Isolate::Scope isolate_scope(GetIsolate()); + if (!FXJS_PerIsolateData::Get(GetIsolate())) + return v8::Local(); + + // Return the global object. + v8::Local context = GetIsolate()->GetCurrentContext(); + return context->Global()->GetPrototype()->ToObject(context).ToLocalChecked(); +} + +void CFXJS_Engine::Error(const WideString& message) { + GetIsolate()->ThrowException(NewString(message.AsStringView())); +} + +CJS_Object* CFXJS_Engine::GetObjectPrivate(v8::Local pObj) { + CFXJS_PerObjectData* pData = CFXJS_PerObjectData::GetFromObject(pObj); + if (!pData && !pObj.IsEmpty()) { + // It could be a global proxy object. + v8::Local v = pObj->GetPrototype(); + v8::Local context = GetIsolate()->GetCurrentContext(); + if (v->IsObject()) { + pData = CFXJS_PerObjectData::GetFromObject( + v->ToObject(context).ToLocalChecked()); + } + } + return pData ? pData->m_pPrivate.get() : nullptr; +} + +v8::Local CFXJS_Engine::GetConstArray(const WideString& name) { + return v8::Local::New(GetIsolate(), m_ConstArrays[name]); +} + +void CFXJS_Engine::SetConstArray(const WideString& name, + v8::Local array) { + m_ConstArrays[name] = v8::Global(GetIsolate(), array); +} diff --git a/fxjs/cfxjs_engine.h b/fxjs/cfxjs_engine.h new file mode 100644 index 0000000000..5f883f7e4a --- /dev/null +++ b/fxjs/cfxjs_engine.h @@ -0,0 +1,157 @@ +// Copyright 2014 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +// CFXJS_ENGINE is a layer that makes it easier to define native objects in V8, +// but has no knowledge of PDF-specific native objects. It could in theory be +// used to implement other sets of native objects. + +// PDFium code should include this file rather than including V8 headers +// directly. + +#ifndef FXJS_CFXJS_ENGINE_H_ +#define FXJS_CFXJS_ENGINE_H_ + +#include +#include +#include +#include + +#include "core/fxcrt/fx_string.h" +#include "fxjs/cfx_v8.h" +#include "v8/include/v8-util.h" +#include "v8/include/v8.h" + +#ifdef PDF_ENABLE_XFA +// CFXJS_ENGINE doesn't interpret this class, it is just passed along to XFA. +class CFXJSE_RuntimeData; +#endif // PDF_ENABLE_XFA + +class CFXJS_ObjDefinition; +class CJS_Object; +class V8TemplateMap; + +// CFXJS_ENGINE places no restrictions on this class; it merely passes it +// on to caller-provided methods. +class IJS_EventContext; // A description of the event that caused JS execution. + +enum FXJSOBJTYPE { + FXJSOBJTYPE_DYNAMIC = 0, // Created by native method and returned to JS. + FXJSOBJTYPE_STATIC, // Created by init and hung off of global object. + FXJSOBJTYPE_GLOBAL, // The global object itself (may only appear once). +}; + +struct FXJSErr { + const wchar_t* message; + const wchar_t* srcline; + unsigned linnum; +}; + +class FXJS_PerIsolateData { + public: + ~FXJS_PerIsolateData(); + + static void SetUp(v8::Isolate* pIsolate); + static FXJS_PerIsolateData* Get(v8::Isolate* pIsolate); + + std::vector> m_ObjectDefnArray; +#ifdef PDF_ENABLE_XFA + std::unique_ptr m_pFXJSERuntimeData; +#endif // PDF_ENABLE_XFA + std::unique_ptr m_pDynamicObjsMap; + + protected: + explicit FXJS_PerIsolateData(v8::Isolate* pIsolate); +}; + +void FXJS_Initialize(unsigned int embedderDataSlot, v8::Isolate* pIsolate); +void FXJS_Release(); + +// Gets the global isolate set by FXJS_Initialize(), or makes a new one each +// time if there is no such isolate. Returns true if a new isolate had to be +// created. +bool FXJS_GetIsolate(v8::Isolate** pResultIsolate); + +// Get the global isolate's ref count. +size_t FXJS_GlobalIsolateRefCount(); + +class CFXJS_Engine : public CFX_V8 { + public: + explicit CFXJS_Engine(v8::Isolate* pIsolate); + ~CFXJS_Engine() override; + + using Constructor = + std::function obj)>; + using Destructor = std::function obj)>; + + static CFXJS_Engine* EngineFromIsolateCurrentContext(v8::Isolate* pIsolate); + static CFXJS_Engine* EngineFromContext(v8::Local pContext); + + static int GetObjDefnID(v8::Local pObj); + + static void SetObjectPrivate(v8::Local pObj, + std::unique_ptr p); + static void FreeObjectPrivate(v8::Local pObj); + + void SetIntoContext(v8::Local pContext); + + // Always returns a valid, newly-created objDefnID. + int DefineObj(const char* sObjName, + FXJSOBJTYPE eObjType, + Constructor pConstructor, + Destructor pDestructor); + + void DefineObjMethod(int nObjDefnID, + const char* sMethodName, + v8::FunctionCallback pMethodCall); + void DefineObjProperty(int nObjDefnID, + const char* sPropName, + v8::AccessorGetterCallback pPropGet, + v8::AccessorSetterCallback pPropPut); + void DefineObjAllProperties(int nObjDefnID, + v8::NamedPropertyQueryCallback pPropQurey, + v8::NamedPropertyGetterCallback pPropGet, + v8::NamedPropertySetterCallback pPropPut, + v8::NamedPropertyDeleterCallback pPropDel); + void DefineObjConst(int nObjDefnID, + const char* sConstName, + v8::Local pDefault); + void DefineGlobalMethod(const char* sMethodName, + v8::FunctionCallback pMethodCall); + void DefineGlobalConst(const wchar_t* sConstName, + v8::FunctionCallback pConstGetter); + + // Called after FXJS_Define* calls made. + void InitializeEngine(); + void ReleaseEngine(); + + // Called after FXJS_InitializeEngine call made. + int Execute(const WideString& script, FXJSErr* perror); + + v8::Local GetThisObj(); + v8::Local NewFXJSBoundObject(int nObjDefnID, + bool bStatic = false); + // Retrieve native object binding. + CJS_Object* GetObjectPrivate(v8::Local pObj); + + void Error(const WideString& message); + + v8::Local GetV8Context() { + return v8::Local::New(GetIsolate(), m_V8Context); + } + + v8::Local GetConstArray(const WideString& name); + void SetConstArray(const WideString& name, v8::Local array); + + protected: + CFXJS_Engine(); + + private: + v8::Global m_V8Context; + std::vector> m_StaticObjects; + std::map> m_ConstArrays; +}; + +#endif // FXJS_CFXJS_ENGINE_H_ diff --git a/fxjs/cfxjs_engine_embeddertest.cpp b/fxjs/cfxjs_engine_embeddertest.cpp new file mode 100644 index 0000000000..f25bfbe060 --- /dev/null +++ b/fxjs/cfxjs_engine_embeddertest.cpp @@ -0,0 +1,99 @@ +// Copyright 2015 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "fxjs/cfxjs_engine.h" + +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/js_embedder_test.h" + +namespace { + +const double kExpected0 = 6.0; +const double kExpected1 = 7.0; +const double kExpected2 = 8.0; + +const wchar_t kScript0[] = L"fred = 6"; +const wchar_t kScript1[] = L"fred = 7"; +const wchar_t kScript2[] = L"fred = 8"; + +} // namespace + +class CFXJSEngineEmbedderTest : public JSEmbedderTest { + public: + void ExecuteInCurrentContext(const WideString& script) { + FXJSErr error; + int sts = engine()->Execute(script, &error); + EXPECT_EQ(0, sts); + } + void CheckAssignmentInCurrentContext(double expected) { + v8::Local This = engine()->GetThisObj(); + v8::Local fred = engine()->GetObjectProperty(This, L"fred"); + EXPECT_TRUE(fred->IsNumber()); + EXPECT_EQ(expected, engine()->ToDouble(fred)); + } +}; + +TEST_F(CFXJSEngineEmbedderTest, Getters) { + v8::Isolate::Scope isolate_scope(isolate()); + v8::HandleScope handle_scope(isolate()); + v8::Context::Scope context_scope(GetV8Context()); + + ExecuteInCurrentContext(WideString(kScript1)); + CheckAssignmentInCurrentContext(kExpected1); +} + +TEST_F(CFXJSEngineEmbedderTest, MultipleEngines) { + v8::Isolate::Scope isolate_scope(isolate()); + v8::HandleScope handle_scope(isolate()); + + CFXJS_Engine engine1(isolate()); + engine1.InitializeEngine(); + + CFXJS_Engine engine2(isolate()); + engine2.InitializeEngine(); + + v8::Local context1 = engine1.GetV8Context(); + v8::Local context2 = engine2.GetV8Context(); + + v8::Context::Scope context_scope(GetV8Context()); + ExecuteInCurrentContext(WideString(kScript0)); + CheckAssignmentInCurrentContext(kExpected0); + + { + v8::Context::Scope context_scope1(context1); + ExecuteInCurrentContext(WideString(kScript1)); + CheckAssignmentInCurrentContext(kExpected1); + } + { + v8::Context::Scope context_scope2(context2); + ExecuteInCurrentContext(WideString(kScript2)); + CheckAssignmentInCurrentContext(kExpected2); + } + + CheckAssignmentInCurrentContext(kExpected0); + + { + v8::Context::Scope context_scope1(context1); + CheckAssignmentInCurrentContext(kExpected1); + { + v8::Context::Scope context_scope2(context2); + CheckAssignmentInCurrentContext(kExpected2); + } + CheckAssignmentInCurrentContext(kExpected1); + } + { + v8::Context::Scope context_scope2(context2); + CheckAssignmentInCurrentContext(kExpected2); + { + v8::Context::Scope context_scope1(context1); + CheckAssignmentInCurrentContext(kExpected1); + } + CheckAssignmentInCurrentContext(kExpected2); + } + + CheckAssignmentInCurrentContext(kExpected0); + + engine1.ReleaseEngine(); + engine2.ReleaseEngine(); +} diff --git a/fxjs/cfxjse_context.cpp b/fxjs/cfxjse_context.cpp index 61c330d687..d12758de2a 100644 --- a/fxjs/cfxjse_context.cpp +++ b/fxjs/cfxjse_context.cpp @@ -8,9 +8,9 @@ #include +#include "fxjs/cfxjs_engine.h" #include "fxjs/cfxjse_class.h" #include "fxjs/cfxjse_value.h" -#include "fxjs/fxjs_v8.h" #include "third_party/base/ptr_util.h" namespace { diff --git a/fxjs/cfxjse_engine.cpp b/fxjs/cfxjse_engine.cpp index d5a1511a54..47911f8a2d 100644 --- a/fxjs/cfxjse_engine.cpp +++ b/fxjs/cfxjse_engine.cpp @@ -11,10 +11,10 @@ #include "core/fxcrt/autorestorer.h" #include "core/fxcrt/cfx_widetextbuf.h" #include "core/fxcrt/fx_extension.h" +#include "fxjs/cfxjs_engine.h" #include "fxjs/cfxjse_class.h" #include "fxjs/cfxjse_resolveprocessor.h" #include "fxjs/cfxjse_value.h" -#include "fxjs/fxjs_v8.h" #include "third_party/base/ptr_util.h" #include "third_party/base/stl_util.h" #include "xfa/fxfa/cxfa_eventparam.h" diff --git a/fxjs/cfxjse_runtimedata.cpp b/fxjs/cfxjse_runtimedata.cpp index b5c2de9297..0153e81a6c 100644 --- a/fxjs/cfxjse_runtimedata.cpp +++ b/fxjs/cfxjse_runtimedata.cpp @@ -8,8 +8,8 @@ #include +#include "fxjs/cfxjs_engine.h" #include "fxjs/cfxjse_isolatetracker.h" -#include "fxjs/fxjs_v8.h" CFXJSE_RuntimeData::CFXJSE_RuntimeData(v8::Isolate* pIsolate) : m_pIsolate(pIsolate) {} diff --git a/fxjs/cjs_object.h b/fxjs/cjs_object.h index 22a5e1625a..7b26fba527 100644 --- a/fxjs/cjs_object.h +++ b/fxjs/cjs_object.h @@ -11,8 +11,8 @@ #include #include "fpdfsdk/fsdk_define.h" +#include "fxjs/cfxjs_engine.h" #include "fxjs/cjs_runtime.h" -#include "fxjs/fxjs_v8.h" struct JSConstSpec { enum Type { Number = 0, String = 1 }; diff --git a/fxjs/cjs_return.h b/fxjs/cjs_return.h index 61c5778cda..37c91caafe 100644 --- a/fxjs/cjs_return.h +++ b/fxjs/cjs_return.h @@ -7,7 +7,7 @@ #ifndef FXJS_CJS_RETURN_H_ #define FXJS_CJS_RETURN_H_ -#include "fxjs/fxjs_v8.h" +#include "fxjs/cfxjs_engine.h" class CJS_Return { public: diff --git a/fxjs/cjs_runtime.h b/fxjs/cjs_runtime.h index 9d0d47fa81..0aab84828f 100644 --- a/fxjs/cjs_runtime.h +++ b/fxjs/cjs_runtime.h @@ -15,8 +15,8 @@ #include "core/fxcrt/observable.h" #include "fpdfsdk/cpdfsdk_formfillenvironment.h" +#include "fxjs/cfxjs_engine.h" #include "fxjs/cjs_eventhandler.h" -#include "fxjs/fxjs_v8.h" #include "fxjs/ijs_runtime.h" class CJS_EventContext; diff --git a/fxjs/fxjs_v8.cpp b/fxjs/fxjs_v8.cpp deleted file mode 100644 index f29dca6807..0000000000 --- a/fxjs/fxjs_v8.cpp +++ /dev/null @@ -1,537 +0,0 @@ -// Copyright 2014 PDFium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com - -#include "fxjs/fxjs_v8.h" - -#include -#include -#include - -#include "fxjs/cfxjse_runtimedata.h" -#include "fxjs/cjs_object.h" - -// Keep this consistent with the values defined in gin/public/context_holder.h -// (without actually requiring a dependency on gin itself for the standalone -// embedders of PDFIum). The value we want to use is: -// kPerContextDataStartIndex + kEmbedderPDFium, which is 3. -static const unsigned int kPerContextDataIndex = 3u; -static unsigned int g_embedderDataSlot = 1u; -static v8::Isolate* g_isolate = nullptr; -static size_t g_isolate_ref_count = 0; -static CFX_V8ArrayBufferAllocator* g_arrayBufferAllocator = nullptr; -static v8::Global* g_DefaultGlobalObjectTemplate = nullptr; -static wchar_t kPerObjectDataTag[] = L"CFXJS_PerObjectData"; - -class CFXJS_PerObjectData { - public: - explicit CFXJS_PerObjectData(int nObjDefID) : m_ObjDefID(nObjDefID) {} - - ~CFXJS_PerObjectData() = default; - - static void SetInObject(CFXJS_PerObjectData* pData, - v8::Local pObj) { - if (pObj->InternalFieldCount() == 2) { - pObj->SetAlignedPointerInInternalField(0, pData); - pObj->SetAlignedPointerInInternalField( - 1, static_cast(kPerObjectDataTag)); - } - } - - static CFXJS_PerObjectData* GetFromObject(v8::Local pObj) { - if (pObj.IsEmpty() || pObj->InternalFieldCount() != 2 || - pObj->GetAlignedPointerFromInternalField(1) != - static_cast(kPerObjectDataTag)) { - return nullptr; - } - return static_cast( - pObj->GetAlignedPointerFromInternalField(0)); - } - - const int m_ObjDefID; - std::unique_ptr m_pPrivate; -}; - -class CFXJS_ObjDefinition { - public: - static int MaxID(v8::Isolate* pIsolate) { - return FXJS_PerIsolateData::Get(pIsolate)->m_ObjectDefnArray.size(); - } - - static CFXJS_ObjDefinition* ForID(v8::Isolate* pIsolate, int id) { - // Note: GetAt() halts if out-of-range even in release builds. - return FXJS_PerIsolateData::Get(pIsolate)->m_ObjectDefnArray[id].get(); - } - - CFXJS_ObjDefinition(v8::Isolate* isolate, - const char* sObjName, - FXJSOBJTYPE eObjType, - CFXJS_Engine::Constructor pConstructor, - CFXJS_Engine::Destructor pDestructor) - : m_ObjName(sObjName), - m_ObjType(eObjType), - m_pConstructor(pConstructor), - m_pDestructor(pDestructor), - m_pIsolate(isolate) { - v8::Isolate::Scope isolate_scope(isolate); - v8::HandleScope handle_scope(isolate); - - v8::Local fun = v8::FunctionTemplate::New(isolate); - fun->InstanceTemplate()->SetInternalFieldCount(2); - fun->SetCallHandler([](const v8::FunctionCallbackInfo& info) { - v8::Local holder = info.Holder(); - ASSERT(holder->InternalFieldCount() == 2); - holder->SetAlignedPointerInInternalField(0, nullptr); - holder->SetAlignedPointerInInternalField(1, nullptr); - }); - if (eObjType == FXJSOBJTYPE_GLOBAL) { - fun->InstanceTemplate()->Set( - v8::Symbol::GetToStringTag(isolate), - v8::String::NewFromUtf8(isolate, "global", v8::NewStringType::kNormal) - .ToLocalChecked()); - } - m_FunctionTemplate.Reset(isolate, fun); - - v8::Local sig = v8::Signature::New(isolate, fun); - m_Signature.Reset(isolate, sig); - } - - int AssignID() { - FXJS_PerIsolateData* pData = FXJS_PerIsolateData::Get(m_pIsolate); - pData->m_ObjectDefnArray.emplace_back(this); - return pData->m_ObjectDefnArray.size() - 1; - } - - v8::Local GetInstanceTemplate() { - v8::EscapableHandleScope scope(m_pIsolate); - v8::Local function = - m_FunctionTemplate.Get(m_pIsolate); - return scope.Escape(function->InstanceTemplate()); - } - - v8::Local GetSignature() { - v8::EscapableHandleScope scope(m_pIsolate); - return scope.Escape(m_Signature.Get(m_pIsolate)); - } - - const char* const m_ObjName; - const FXJSOBJTYPE m_ObjType; - const CFXJS_Engine::Constructor m_pConstructor; - const CFXJS_Engine::Destructor m_pDestructor; - - v8::Isolate* m_pIsolate; - v8::Global m_FunctionTemplate; - v8::Global m_Signature; -}; - -static v8::Local GetGlobalObjectTemplate( - v8::Isolate* pIsolate) { - int maxID = CFXJS_ObjDefinition::MaxID(pIsolate); - for (int i = 0; i < maxID; ++i) { - CFXJS_ObjDefinition* pObjDef = CFXJS_ObjDefinition::ForID(pIsolate, i); - if (pObjDef->m_ObjType == FXJSOBJTYPE_GLOBAL) - return pObjDef->GetInstanceTemplate(); - } - if (!g_DefaultGlobalObjectTemplate) { - v8::Local hGlobalTemplate = - v8::ObjectTemplate::New(pIsolate); - hGlobalTemplate->Set( - v8::Symbol::GetToStringTag(pIsolate), - v8::String::NewFromUtf8(pIsolate, "global", v8::NewStringType::kNormal) - .ToLocalChecked()); - g_DefaultGlobalObjectTemplate = - new v8::Global(pIsolate, hGlobalTemplate); - } - return g_DefaultGlobalObjectTemplate->Get(pIsolate); -} - -void V8TemplateMapTraits::Dispose(v8::Isolate* isolate, - v8::Global value, - void* key) { - v8::Local obj = value.Get(isolate); - if (obj.IsEmpty()) - return; - int id = CFXJS_Engine::GetObjDefnID(obj); - if (id == -1) - return; - CFXJS_ObjDefinition* pObjDef = CFXJS_ObjDefinition::ForID(isolate, id); - if (!pObjDef) - return; - if (pObjDef->m_pDestructor) - pObjDef->m_pDestructor(obj); - CFXJS_Engine::FreeObjectPrivate(obj); -} - -V8TemplateMapTraits::MapType* V8TemplateMapTraits::MapFromWeakCallbackInfo( - const v8::WeakCallbackInfo& data) { - V8TemplateMap* pMap = - (FXJS_PerIsolateData::Get(data.GetIsolate()))->m_pDynamicObjsMap.get(); - return pMap ? &pMap->m_map : nullptr; -} - -void FXJS_Initialize(unsigned int embedderDataSlot, v8::Isolate* pIsolate) { - if (g_isolate) { - ASSERT(g_embedderDataSlot == embedderDataSlot); - ASSERT(g_isolate == pIsolate); - return; - } - g_embedderDataSlot = embedderDataSlot; - g_isolate = pIsolate; -} - -void FXJS_Release() { - ASSERT(!g_isolate || g_isolate_ref_count == 0); - delete g_DefaultGlobalObjectTemplate; - g_DefaultGlobalObjectTemplate = nullptr; - g_isolate = nullptr; - - delete g_arrayBufferAllocator; - g_arrayBufferAllocator = nullptr; -} - -bool FXJS_GetIsolate(v8::Isolate** pResultIsolate) { - if (g_isolate) { - *pResultIsolate = g_isolate; - return false; - } - // Provide backwards compatibility when no external isolate. - if (!g_arrayBufferAllocator) - g_arrayBufferAllocator = new CFX_V8ArrayBufferAllocator(); - v8::Isolate::CreateParams params; - params.array_buffer_allocator = g_arrayBufferAllocator; - *pResultIsolate = v8::Isolate::New(params); - return true; -} - -size_t FXJS_GlobalIsolateRefCount() { - return g_isolate_ref_count; -} - -V8TemplateMap::V8TemplateMap(v8::Isolate* isolate) : m_map(isolate) {} - -V8TemplateMap::~V8TemplateMap() {} - -void V8TemplateMap::set(void* key, v8::Local handle) { - ASSERT(!m_map.Contains(key)); - m_map.Set(key, handle); -} - -FXJS_PerIsolateData::~FXJS_PerIsolateData() {} - -// static -void FXJS_PerIsolateData::SetUp(v8::Isolate* pIsolate) { - if (!pIsolate->GetData(g_embedderDataSlot)) - pIsolate->SetData(g_embedderDataSlot, new FXJS_PerIsolateData(pIsolate)); -} - -// static -FXJS_PerIsolateData* FXJS_PerIsolateData::Get(v8::Isolate* pIsolate) { - return static_cast( - pIsolate->GetData(g_embedderDataSlot)); -} - -FXJS_PerIsolateData::FXJS_PerIsolateData(v8::Isolate* pIsolate) - : m_pDynamicObjsMap(new V8TemplateMap(pIsolate)) {} - -CFXJS_Engine::CFXJS_Engine() : CFX_V8(nullptr) {} - -CFXJS_Engine::CFXJS_Engine(v8::Isolate* pIsolate) : CFX_V8(pIsolate) {} - -CFXJS_Engine::~CFXJS_Engine() = default; - -// static -CFXJS_Engine* CFXJS_Engine::EngineFromIsolateCurrentContext( - v8::Isolate* pIsolate) { - return EngineFromContext(pIsolate->GetCurrentContext()); -} - -// static -CFXJS_Engine* CFXJS_Engine::EngineFromContext(v8::Local pContext) { - return static_cast( - pContext->GetAlignedPointerFromEmbedderData(kPerContextDataIndex)); -} - -// static -int CFXJS_Engine::GetObjDefnID(v8::Local pObj) { - CFXJS_PerObjectData* pData = CFXJS_PerObjectData::GetFromObject(pObj); - return pData ? pData->m_ObjDefID : -1; -} - -// static -void CFXJS_Engine::SetObjectPrivate(v8::Local pObj, - std::unique_ptr p) { - CFXJS_PerObjectData* pPerObjectData = - CFXJS_PerObjectData::GetFromObject(pObj); - if (!pPerObjectData) - return; - pPerObjectData->m_pPrivate = std::move(p); -} - -// static -void CFXJS_Engine::FreeObjectPrivate(v8::Local pObj) { - CFXJS_PerObjectData* pData = CFXJS_PerObjectData::GetFromObject(pObj); - pObj->SetAlignedPointerInInternalField(0, nullptr); - pObj->SetAlignedPointerInInternalField(1, nullptr); - delete pData; -} - -void CFXJS_Engine::SetIntoContext(v8::Local pContext) { - pContext->SetAlignedPointerInEmbedderData(kPerContextDataIndex, this); -} - -int CFXJS_Engine::DefineObj(const char* sObjName, - FXJSOBJTYPE eObjType, - CFXJS_Engine::Constructor pConstructor, - CFXJS_Engine::Destructor pDestructor) { - v8::Isolate::Scope isolate_scope(GetIsolate()); - v8::HandleScope handle_scope(GetIsolate()); - FXJS_PerIsolateData::SetUp(GetIsolate()); - CFXJS_ObjDefinition* pObjDef = new CFXJS_ObjDefinition( - GetIsolate(), sObjName, eObjType, pConstructor, pDestructor); - return pObjDef->AssignID(); -} - -void CFXJS_Engine::DefineObjMethod(int nObjDefnID, - const char* sMethodName, - v8::FunctionCallback pMethodCall) { - v8::Isolate::Scope isolate_scope(GetIsolate()); - v8::HandleScope handle_scope(GetIsolate()); - CFXJS_ObjDefinition* pObjDef = - CFXJS_ObjDefinition::ForID(GetIsolate(), nObjDefnID); - v8::Local fun = v8::FunctionTemplate::New( - GetIsolate(), pMethodCall, v8::Local(), - pObjDef->GetSignature()); - fun->RemovePrototype(); - pObjDef->GetInstanceTemplate()->Set(NewString(sMethodName), fun, - v8::ReadOnly); -} - -void CFXJS_Engine::DefineObjProperty(int nObjDefnID, - const char* sPropName, - v8::AccessorGetterCallback pPropGet, - v8::AccessorSetterCallback pPropPut) { - v8::Isolate::Scope isolate_scope(GetIsolate()); - v8::HandleScope handle_scope(GetIsolate()); - CFXJS_ObjDefinition* pObjDef = - CFXJS_ObjDefinition::ForID(GetIsolate(), nObjDefnID); - pObjDef->GetInstanceTemplate()->SetAccessor(NewString(sPropName), pPropGet, - pPropPut); -} - -void CFXJS_Engine::DefineObjAllProperties( - int nObjDefnID, - v8::NamedPropertyQueryCallback pPropQurey, - v8::NamedPropertyGetterCallback pPropGet, - v8::NamedPropertySetterCallback pPropPut, - v8::NamedPropertyDeleterCallback pPropDel) { - v8::Isolate::Scope isolate_scope(GetIsolate()); - v8::HandleScope handle_scope(GetIsolate()); - CFXJS_ObjDefinition* pObjDef = - CFXJS_ObjDefinition::ForID(GetIsolate(), nObjDefnID); - pObjDef->GetInstanceTemplate()->SetNamedPropertyHandler(pPropGet, pPropPut, - pPropQurey, pPropDel); -} - -void CFXJS_Engine::DefineObjConst(int nObjDefnID, - const char* sConstName, - v8::Local pDefault) { - v8::Isolate::Scope isolate_scope(GetIsolate()); - v8::HandleScope handle_scope(GetIsolate()); - CFXJS_ObjDefinition* pObjDef = - CFXJS_ObjDefinition::ForID(GetIsolate(), nObjDefnID); - pObjDef->GetInstanceTemplate()->Set(GetIsolate(), sConstName, pDefault); -} - -void CFXJS_Engine::DefineGlobalMethod(const char* sMethodName, - v8::FunctionCallback pMethodCall) { - v8::Isolate::Scope isolate_scope(GetIsolate()); - v8::HandleScope handle_scope(GetIsolate()); - v8::Local fun = - v8::FunctionTemplate::New(GetIsolate(), pMethodCall); - fun->RemovePrototype(); - GetGlobalObjectTemplate(GetIsolate()) - ->Set(NewString(sMethodName), fun, v8::ReadOnly); -} - -void CFXJS_Engine::DefineGlobalConst(const wchar_t* sConstName, - v8::FunctionCallback pConstGetter) { - v8::Isolate::Scope isolate_scope(GetIsolate()); - v8::HandleScope handle_scope(GetIsolate()); - v8::Local fun = - v8::FunctionTemplate::New(GetIsolate(), pConstGetter); - fun->RemovePrototype(); - GetGlobalObjectTemplate(GetIsolate()) - ->SetAccessorProperty(NewString(sConstName), fun); -} - -void CFXJS_Engine::InitializeEngine() { - if (GetIsolate() == g_isolate) - ++g_isolate_ref_count; - - v8::Isolate::Scope isolate_scope(GetIsolate()); - v8::HandleScope handle_scope(GetIsolate()); - - // This has to happen before we call GetGlobalObjectTemplate because that - // method gets the PerIsolateData from GetIsolate(). - FXJS_PerIsolateData::SetUp(GetIsolate()); - - v8::Local v8Context = v8::Context::New( - GetIsolate(), nullptr, GetGlobalObjectTemplate(GetIsolate())); - v8::Context::Scope context_scope(v8Context); - SetIntoContext(v8Context); - - int maxID = CFXJS_ObjDefinition::MaxID(GetIsolate()); - m_StaticObjects.resize(maxID + 1); - for (int i = 0; i < maxID; ++i) { - CFXJS_ObjDefinition* pObjDef = CFXJS_ObjDefinition::ForID(GetIsolate(), i); - if (pObjDef->m_ObjType == FXJSOBJTYPE_GLOBAL) { - CFXJS_PerObjectData::SetInObject(new CFXJS_PerObjectData(i), - v8Context->Global() - ->GetPrototype() - ->ToObject(v8Context) - .ToLocalChecked()); - if (pObjDef->m_pConstructor) { - pObjDef->m_pConstructor(this, v8Context->Global() - ->GetPrototype() - ->ToObject(v8Context) - .ToLocalChecked()); - } - } else if (pObjDef->m_ObjType == FXJSOBJTYPE_STATIC) { - v8::Local pObjName = NewString(pObjDef->m_ObjName); - v8::Local obj = NewFXJSBoundObject(i, true); - if (!obj.IsEmpty()) { - v8Context->Global()->Set(v8Context, pObjName, obj).FromJust(); - m_StaticObjects[i] = v8::Global(GetIsolate(), obj); - } - } - } - m_V8Context.Reset(GetIsolate(), v8Context); -} - -void CFXJS_Engine::ReleaseEngine() { - v8::Isolate::Scope isolate_scope(GetIsolate()); - v8::HandleScope handle_scope(GetIsolate()); - v8::Local context = GetV8Context(); - v8::Context::Scope context_scope(context); - FXJS_PerIsolateData* pData = FXJS_PerIsolateData::Get(GetIsolate()); - if (!pData) - return; - - m_ConstArrays.clear(); - - int maxID = CFXJS_ObjDefinition::MaxID(GetIsolate()); - for (int i = 0; i < maxID; ++i) { - CFXJS_ObjDefinition* pObjDef = CFXJS_ObjDefinition::ForID(GetIsolate(), i); - v8::Local pObj; - if (pObjDef->m_ObjType == FXJSOBJTYPE_GLOBAL) { - pObj = - context->Global()->GetPrototype()->ToObject(context).ToLocalChecked(); - } else if (!m_StaticObjects[i].IsEmpty()) { - pObj = v8::Local::New(GetIsolate(), m_StaticObjects[i]); - m_StaticObjects[i].Reset(); - } - - if (!pObj.IsEmpty()) { - if (pObjDef->m_pDestructor) - pObjDef->m_pDestructor(pObj); - FreeObjectPrivate(pObj); - } - } - - m_V8Context.Reset(); - - if (GetIsolate() == g_isolate && --g_isolate_ref_count > 0) - return; - - delete pData; - GetIsolate()->SetData(g_embedderDataSlot, nullptr); -} - -int CFXJS_Engine::Execute(const WideString& script, FXJSErr* pError) { - v8::Isolate::Scope isolate_scope(GetIsolate()); - v8::TryCatch try_catch(GetIsolate()); - v8::Local context = GetIsolate()->GetCurrentContext(); - v8::Local compiled_script; - if (!v8::Script::Compile(context, NewString(script.AsStringView())) - .ToLocal(&compiled_script)) { - v8::String::Utf8Value error(GetIsolate(), try_catch.Exception()); - // TODO(tsepez): return error via pError->message. - return -1; - } - - v8::Local result; - if (!compiled_script->Run(context).ToLocal(&result)) { - v8::String::Utf8Value error(GetIsolate(), try_catch.Exception()); - // TODO(tsepez): return error via pError->message. - return -1; - } - return 0; -} - -v8::Local CFXJS_Engine::NewFXJSBoundObject(int nObjDefnID, - bool bStatic) { - v8::Isolate::Scope isolate_scope(GetIsolate()); - v8::Local context = GetIsolate()->GetCurrentContext(); - FXJS_PerIsolateData* pData = FXJS_PerIsolateData::Get(GetIsolate()); - if (!pData) - return v8::Local(); - - if (nObjDefnID < 0 || nObjDefnID >= CFXJS_ObjDefinition::MaxID(GetIsolate())) - return v8::Local(); - - CFXJS_ObjDefinition* pObjDef = - CFXJS_ObjDefinition::ForID(GetIsolate(), nObjDefnID); - v8::Local obj; - if (!pObjDef->GetInstanceTemplate()->NewInstance(context).ToLocal(&obj)) - return v8::Local(); - - CFXJS_PerObjectData* pObjData = new CFXJS_PerObjectData(nObjDefnID); - CFXJS_PerObjectData::SetInObject(pObjData, obj); - if (pObjDef->m_pConstructor) - pObjDef->m_pConstructor(this, obj); - - if (!bStatic && FXJS_PerIsolateData::Get(GetIsolate())->m_pDynamicObjsMap) - FXJS_PerIsolateData::Get(GetIsolate()) - ->m_pDynamicObjsMap->set(pObjData, obj); - - return obj; -} - -v8::Local CFXJS_Engine::GetThisObj() { - v8::Isolate::Scope isolate_scope(GetIsolate()); - if (!FXJS_PerIsolateData::Get(GetIsolate())) - return v8::Local(); - - // Return the global object. - v8::Local context = GetIsolate()->GetCurrentContext(); - return context->Global()->GetPrototype()->ToObject(context).ToLocalChecked(); -} - -void CFXJS_Engine::Error(const WideString& message) { - GetIsolate()->ThrowException(NewString(message.AsStringView())); -} - -CJS_Object* CFXJS_Engine::GetObjectPrivate(v8::Local pObj) { - CFXJS_PerObjectData* pData = CFXJS_PerObjectData::GetFromObject(pObj); - if (!pData && !pObj.IsEmpty()) { - // It could be a global proxy object. - v8::Local v = pObj->GetPrototype(); - v8::Local context = GetIsolate()->GetCurrentContext(); - if (v->IsObject()) { - pData = CFXJS_PerObjectData::GetFromObject( - v->ToObject(context).ToLocalChecked()); - } - } - return pData ? pData->m_pPrivate.get() : nullptr; -} - -v8::Local CFXJS_Engine::GetConstArray(const WideString& name) { - return v8::Local::New(GetIsolate(), m_ConstArrays[name]); -} - -void CFXJS_Engine::SetConstArray(const WideString& name, - v8::Local array) { - m_ConstArrays[name] = v8::Global(GetIsolate(), array); -} diff --git a/fxjs/fxjs_v8.h b/fxjs/fxjs_v8.h deleted file mode 100644 index 9ef746018c..0000000000 --- a/fxjs/fxjs_v8.h +++ /dev/null @@ -1,200 +0,0 @@ -// Copyright 2014 PDFium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com - -// FXJS_V8 is a layer that makes it easier to define native objects in V8, but -// has no knowledge of PDF-specific native objects. It could in theory be used -// to implement other sets of native objects. - -// PDFium code should include this file rather than including V8 headers -// directly. - -#ifndef FXJS_FXJS_V8_H_ -#define FXJS_FXJS_V8_H_ - -#include -#include -#include -#include - -#include "core/fxcrt/fx_string.h" -#include "fxjs/cfx_v8.h" -#include "v8/include/v8-util.h" -#include "v8/include/v8.h" - -#ifdef PDF_ENABLE_XFA -// FXJS_V8 doesn't interpret this class, it is just passed along to XFA. -class CFXJSE_RuntimeData; -#endif // PDF_ENABLE_XFA - -class CFXJS_ObjDefinition; -class CJS_Object; - -// FXJS_V8 places no restrictions on this class; it merely passes it -// on to caller-provided methods. -class IJS_EventContext; // A description of the event that caused JS execution. - -enum FXJSOBJTYPE { - FXJSOBJTYPE_DYNAMIC = 0, // Created by native method and returned to JS. - FXJSOBJTYPE_STATIC, // Created by init and hung off of global object. - FXJSOBJTYPE_GLOBAL, // The global object itself (may only appear once). -}; - -struct FXJSErr { - const wchar_t* message; - const wchar_t* srcline; - unsigned linnum; -}; - -// Global weak map to save dynamic objects. -class V8TemplateMapTraits : public v8::StdMapTraits { - public: - typedef v8::GlobalValueMap MapType; - typedef void WeakCallbackDataType; - - static WeakCallbackDataType* - WeakCallbackParameter(MapType* map, void* key, v8::Local value) { - return key; - } - static MapType* MapFromWeakCallbackInfo( - const v8::WeakCallbackInfo&); - - static void* KeyFromWeakCallbackInfo( - const v8::WeakCallbackInfo& data) { - return data.GetParameter(); - } - static const v8::PersistentContainerCallbackType kCallbackType = - v8::kWeakWithInternalFields; - static void DisposeWeak( - const v8::WeakCallbackInfo& data) {} - static void OnWeakCallback( - const v8::WeakCallbackInfo& data) {} - static void Dispose(v8::Isolate* isolate, - v8::Global value, - void* key); - static void DisposeCallbackData(WeakCallbackDataType* callbackData) {} -}; - -class V8TemplateMap { - public: - typedef v8::GlobalValueMap MapType; - - explicit V8TemplateMap(v8::Isolate* isolate); - ~V8TemplateMap(); - - void set(void* key, v8::Local handle); - - friend class V8TemplateMapTraits; - - private: - MapType m_map; -}; - -class FXJS_PerIsolateData { - public: - ~FXJS_PerIsolateData(); - - static void SetUp(v8::Isolate* pIsolate); - static FXJS_PerIsolateData* Get(v8::Isolate* pIsolate); - - std::vector> m_ObjectDefnArray; -#ifdef PDF_ENABLE_XFA - std::unique_ptr m_pFXJSERuntimeData; -#endif // PDF_ENABLE_XFA - std::unique_ptr m_pDynamicObjsMap; - - protected: - explicit FXJS_PerIsolateData(v8::Isolate* pIsolate); -}; - -void FXJS_Initialize(unsigned int embedderDataSlot, v8::Isolate* pIsolate); -void FXJS_Release(); - -// Gets the global isolate set by FXJS_Initialize(), or makes a new one each -// time if there is no such isolate. Returns true if a new isolate had to be -// created. -bool FXJS_GetIsolate(v8::Isolate** pResultIsolate); - -// Get the global isolate's ref count. -size_t FXJS_GlobalIsolateRefCount(); - -class CFXJS_Engine : public CFX_V8 { - public: - explicit CFXJS_Engine(v8::Isolate* pIsolate); - ~CFXJS_Engine() override; - - using Constructor = - std::function obj)>; - using Destructor = std::function obj)>; - - static CFXJS_Engine* EngineFromIsolateCurrentContext(v8::Isolate* pIsolate); - static CFXJS_Engine* EngineFromContext(v8::Local pContext); - - static int GetObjDefnID(v8::Local pObj); - - static void SetObjectPrivate(v8::Local pObj, - std::unique_ptr p); - static void FreeObjectPrivate(v8::Local pObj); - - void SetIntoContext(v8::Local pContext); - - // Always returns a valid, newly-created objDefnID. - int DefineObj(const char* sObjName, - FXJSOBJTYPE eObjType, - Constructor pConstructor, - Destructor pDestructor); - - void DefineObjMethod(int nObjDefnID, - const char* sMethodName, - v8::FunctionCallback pMethodCall); - void DefineObjProperty(int nObjDefnID, - const char* sPropName, - v8::AccessorGetterCallback pPropGet, - v8::AccessorSetterCallback pPropPut); - void DefineObjAllProperties(int nObjDefnID, - v8::NamedPropertyQueryCallback pPropQurey, - v8::NamedPropertyGetterCallback pPropGet, - v8::NamedPropertySetterCallback pPropPut, - v8::NamedPropertyDeleterCallback pPropDel); - void DefineObjConst(int nObjDefnID, - const char* sConstName, - v8::Local pDefault); - void DefineGlobalMethod(const char* sMethodName, - v8::FunctionCallback pMethodCall); - void DefineGlobalConst(const wchar_t* sConstName, - v8::FunctionCallback pConstGetter); - - // Called after FXJS_Define* calls made. - void InitializeEngine(); - void ReleaseEngine(); - - // Called after FXJS_InitializeEngine call made. - int Execute(const WideString& script, FXJSErr* perror); - - v8::Local GetThisObj(); - v8::Local NewFXJSBoundObject(int nObjDefnID, - bool bStatic = false); - // Retrieve native object binding. - CJS_Object* GetObjectPrivate(v8::Local pObj); - - void Error(const WideString& message); - - v8::Local GetV8Context() { - return v8::Local::New(GetIsolate(), m_V8Context); - } - - v8::Local GetConstArray(const WideString& name); - void SetConstArray(const WideString& name, v8::Local array); - - protected: - CFXJS_Engine(); - - private: - v8::Global m_V8Context; - std::vector> m_StaticObjects; - std::map> m_ConstArrays; -}; - -#endif // FXJS_FXJS_V8_H_ diff --git a/fxjs/fxjs_v8_embeddertest.cpp b/fxjs/fxjs_v8_embeddertest.cpp deleted file mode 100644 index 77bea86fe1..0000000000 --- a/fxjs/fxjs_v8_embeddertest.cpp +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2015 PDFium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/js_embedder_test.h" - -namespace { - -const double kExpected0 = 6.0; -const double kExpected1 = 7.0; -const double kExpected2 = 8.0; - -const wchar_t kScript0[] = L"fred = 6"; -const wchar_t kScript1[] = L"fred = 7"; -const wchar_t kScript2[] = L"fred = 8"; - -} // namespace - -class FXJSV8EmbedderTest : public JSEmbedderTest { - public: - void ExecuteInCurrentContext(const WideString& script) { - FXJSErr error; - int sts = engine()->Execute(script, &error); - EXPECT_EQ(0, sts); - } - void CheckAssignmentInCurrentContext(double expected) { - v8::Local This = engine()->GetThisObj(); - v8::Local fred = engine()->GetObjectProperty(This, L"fred"); - EXPECT_TRUE(fred->IsNumber()); - EXPECT_EQ(expected, engine()->ToDouble(fred)); - } -}; - -TEST_F(FXJSV8EmbedderTest, Getters) { - v8::Isolate::Scope isolate_scope(isolate()); - v8::HandleScope handle_scope(isolate()); - v8::Context::Scope context_scope(GetV8Context()); - - ExecuteInCurrentContext(WideString(kScript1)); - CheckAssignmentInCurrentContext(kExpected1); -} - -TEST_F(FXJSV8EmbedderTest, MultipleEngines) { - v8::Isolate::Scope isolate_scope(isolate()); - v8::HandleScope handle_scope(isolate()); - - CFXJS_Engine engine1(isolate()); - engine1.InitializeEngine(); - - CFXJS_Engine engine2(isolate()); - engine2.InitializeEngine(); - - v8::Local context1 = engine1.GetV8Context(); - v8::Local context2 = engine2.GetV8Context(); - - v8::Context::Scope context_scope(GetV8Context()); - ExecuteInCurrentContext(WideString(kScript0)); - CheckAssignmentInCurrentContext(kExpected0); - - { - v8::Context::Scope context_scope1(context1); - ExecuteInCurrentContext(WideString(kScript1)); - CheckAssignmentInCurrentContext(kExpected1); - } - { - v8::Context::Scope context_scope2(context2); - ExecuteInCurrentContext(WideString(kScript2)); - CheckAssignmentInCurrentContext(kExpected2); - } - - CheckAssignmentInCurrentContext(kExpected0); - - { - v8::Context::Scope context_scope1(context1); - CheckAssignmentInCurrentContext(kExpected1); - { - v8::Context::Scope context_scope2(context2); - CheckAssignmentInCurrentContext(kExpected2); - } - CheckAssignmentInCurrentContext(kExpected1); - } - { - v8::Context::Scope context_scope2(context2); - CheckAssignmentInCurrentContext(kExpected2); - { - v8::Context::Scope context_scope1(context1); - CheckAssignmentInCurrentContext(kExpected1); - } - CheckAssignmentInCurrentContext(kExpected2); - } - - CheckAssignmentInCurrentContext(kExpected0); - - engine1.ReleaseEngine(); - engine2.ReleaseEngine(); -} diff --git a/testing/js_embedder_test.h b/testing/js_embedder_test.h index 4bd5433b19..44245e3024 100644 --- a/testing/js_embedder_test.h +++ b/testing/js_embedder_test.h @@ -7,7 +7,7 @@ #include -#include "fxjs/fxjs_v8.h" +#include "fxjs/cfxjs_engine.h" #include "testing/embedder_test.h" class JSEmbedderTest : public EmbedderTest { diff --git a/testing/xfa_js_embedder_test.h b/testing/xfa_js_embedder_test.h index 44840b31eb..0ddb02ce70 100644 --- a/testing/xfa_js_embedder_test.h +++ b/testing/xfa_js_embedder_test.h @@ -8,8 +8,8 @@ #include #include +#include "fxjs/cfxjs_engine.h" #include "fxjs/cfxjse_value.h" -#include "fxjs/fxjs_v8.h" #include "testing/embedder_test.h" #include "xfa/fxfa/parser/cxfa_document.h" #include "xfa/fxfa/parser/cxfa_node.h" -- cgit v1.2.3