// Copyright 2016 The PDFium Authors // 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/xfa/cfxjse_class.h" #include #include #include "fxjs/cjs_result.h" #include "fxjs/fxv8.h" #include "fxjs/js_resources.h" #include "fxjs/xfa/cfxjse_context.h" #include "fxjs/xfa/cfxjse_isolatetracker.h" #include "fxjs/xfa/cfxjse_value.h" #include "third_party/base/check.h" #include "third_party/base/check_op.h" #include "v8/include/v8-container.h" #include "v8/include/v8-external.h" #include "v8/include/v8-function-callback.h" #include "v8/include/v8-function.h" #include "v8/include/v8-isolate.h" #include "v8/include/v8-object.h" #include "v8/include/v8-primitive.h" #include "v8/include/v8-template.h" using pdfium::fxjse::kClassTag; using pdfium::fxjse::kFuncTag; namespace { FXJSE_FUNCTION_DESCRIPTOR* AsFunctionDescriptor(void* ptr) { auto* result = static_cast(ptr); return result && result->tag == kFuncTag ? result : nullptr; } FXJSE_CLASS_DESCRIPTOR* AsClassDescriptor(void* ptr) { auto* result = static_cast(ptr); return result && result->tag == kClassTag ? result : nullptr; } void V8FunctionCallback_Wrapper( const v8::FunctionCallbackInfo& info) { const FXJSE_FUNCTION_DESCRIPTOR* pFunctionInfo = AsFunctionDescriptor(info.Data().As()->Value()); if (!pFunctionInfo) return; pFunctionInfo->callbackProc(CFXJSE_HostObject::FromV8(info.Holder()), info); } void V8ConstructorCallback_Wrapper( const v8::FunctionCallbackInfo& info) { if (!info.IsConstructCall()) return; const FXJSE_CLASS_DESCRIPTOR* pClassDescriptor = AsClassDescriptor(info.Data().As()->Value()); if (!pClassDescriptor) return; DCHECK_EQ(info.Holder()->InternalFieldCount(), 2); info.Holder()->SetAlignedPointerInInternalField(0, nullptr); info.Holder()->SetAlignedPointerInInternalField(1, nullptr); } void Context_GlobalObjToString( const v8::FunctionCallbackInfo& info) { const FXJSE_CLASS_DESCRIPTOR* pClassDescriptor = AsClassDescriptor(info.Data().As()->Value()); if (!pClassDescriptor) return; if (info.This() == info.Holder() && pClassDescriptor->name) { ByteString szStringVal = ByteString::Format("[object %s]", pClassDescriptor->name); info.GetReturnValue().Set( fxv8::NewStringHelper(info.GetIsolate(), szStringVal.AsStringView())); return; } v8::Local local_str = info.Holder() ->ObjectProtoToString(info.GetIsolate()->GetCurrentContext()) .FromMaybe(v8::Local()); info.GetReturnValue().Set(local_str); } void DynPropGetterAdapter_MethodCallback( const v8::FunctionCallbackInfo& info) { v8::Local hCallBackInfo = info.Data().As(); if (hCallBackInfo->InternalFieldCount() != 2) return; auto* pClassDescriptor = static_cast( hCallBackInfo->GetAlignedPointerFromInternalField(0)); if (pClassDescriptor != &kGlobalClassDescriptor && pClassDescriptor != &kNormalClassDescriptor && pClassDescriptor != &kVariablesClassDescriptor && pClassDescriptor != &kFormCalcDescriptor) { return; } v8::Local hPropName = hCallBackInfo->GetInternalField(1).As().As(); if (hPropName.IsEmpty()) return; v8::String::Utf8Value szPropName(info.GetIsolate(), hPropName); CJS_Result result = pClassDescriptor->dynMethodCall(info, WideString::FromUTF8(*szPropName)); if (result.HasError()) { WideString err = JSFormatErrorString(pClassDescriptor->name, *szPropName, result.Error()); fxv8::ThrowExceptionHelper(info.GetIsolate(), err.AsStringView()); return; } if (result.HasReturn()) info.GetReturnValue().Set(result.Return()); } void DynPropGetterAdapter(v8::Isolate* pIsolate, const FXJSE_CLASS_DESCRIPTOR* pClassDescriptor, v8::Local pObject, ByteStringView szPropName, CFXJSE_Value* pValue) { DCHECK(pClassDescriptor); FXJSE_ClassPropType nPropType = pClassDescriptor->dynPropTypeGetter ? pClassDescriptor->dynPropTypeGetter(pIsolate, pObject, szPropName, false) : FXJSE_ClassPropType::kProperty; if (nPropType == FXJSE_ClassPropType::kProperty) { if (pClassDescriptor->dynPropGetter) { pValue->ForceSetValue(pIsolate, pClassDescriptor->dynPropGetter( pIsolate, pObject, szPropName)); } } else if (nPropType == FXJSE_ClassPropType::kMethod) { if (pClassDescriptor->dynMethodCall && pValue) { v8::HandleScope hscope(pIsolate); v8::Local hCallBackInfoTemplate = v8::ObjectTemplate::New(pIsolate); hCallBackInfoTemplate->SetInternalFieldCount(2); v8::Local hCallBackInfo = hCallBackInfoTemplate->NewInstance(pIsolate->GetCurrentContext()) .ToLocalChecked(); hCallBackInfo->SetAlignedPointerInInternalField( 0, const_cast(pClassDescriptor)); hCallBackInfo->SetInternalField( 1, fxv8::NewStringHelper(pIsolate, szPropName)); pValue->ForceSetValue( pIsolate, v8::Function::New(pIsolate->GetCurrentContext(), DynPropGetterAdapter_MethodCallback, hCallBackInfo, 0, v8::ConstructorBehavior::kThrow) .ToLocalChecked()); } } } void DynPropSetterAdapter(v8::Isolate* pIsolate, const FXJSE_CLASS_DESCRIPTOR* pClassDescriptor, v8::Local pObject, ByteStringView szPropName, CFXJSE_Value* pValue) { DCHECK(pClassDescriptor); FXJSE_ClassPropType nPropType = pClassDescriptor->dynPropTypeGetter ? pClassDescriptor->dynPropTypeGetter(pIsolate, pObject, szPropName, false) : FXJSE_ClassPropType::kProperty; if (nPropType != FXJSE_ClassPropType::kMethod) { if (pClassDescriptor->dynPropSetter) { pClassDescriptor->dynPropSetter(pIsolate, pObject, szPropName, pValue->GetValue(pIsolate)); } } } bool DynPropQueryAdapter(v8::Isolate* pIsolate, const FXJSE_CLASS_DESCRIPTOR* pClassDescriptor, v8::Local pObject, ByteStringView szPropName) { FXJSE_ClassPropType nPropType = pClassDescriptor->dynPropTypeGetter ? pClassDescriptor->dynPropTypeGetter( pIsolate, pObject, szPropName, true) : FXJSE_ClassPropType::kProperty; return nPropType != FXJSE_ClassPropType::kNone; } void NamedPropertyQueryCallback( v8::Local property, const v8::PropertyCallbackInfo& info) { const FXJSE_CLASS_DESCRIPTOR* pClass = AsClassDescriptor(info.Data().As()->Value()); if (!pClass) return; v8::HandleScope scope(info.GetIsolate()); v8::String::Utf8Value szPropName(info.GetIsolate(), property); ByteStringView szFxPropName(*szPropName, szPropName.length()); if (DynPropQueryAdapter(info.GetIsolate(), pClass, info.Holder(), szFxPropName)) { info.GetReturnValue().Set(v8::DontDelete); return; } const int32_t iV8Absent = 64; info.GetReturnValue().Set(iV8Absent); } void NamedPropertyGetterCallback( v8::Local property, const v8::PropertyCallbackInfo& info) { const FXJSE_CLASS_DESCRIPTOR* pClass = AsClassDescriptor(info.Data().As()->Value()); if (!pClass) return; v8::String::Utf8Value szPropName(info.GetIsolate(), property); ByteStringView szFxPropName(*szPropName, szPropName.length()); auto pNewValue = std::make_unique(); DynPropGetterAdapter(info.GetIsolate(), pClass, info.Holder(), szFxPropName, pNewValue.get()); info.GetReturnValue().Set(pNewValue->DirectGetValue()); } void NamedPropertySetterCallback( v8::Local property, v8::Local value, const v8::PropertyCallbackInfo& info) { const FXJSE_CLASS_DESCRIPTOR* pClass = AsClassDescriptor(info.Data().As()->Value()); if (!pClass) return; v8::String::Utf8Value szPropName(info.GetIsolate(), property); ByteStringView szFxPropName(*szPropName, szPropName.length()); auto pNewValue = std::make_unique(info.GetIsolate(), value); DynPropSetterAdapter(info.GetIsolate(), pClass, info.Holder(), szFxPropName, pNewValue.get()); info.GetReturnValue().Set(value); } void NamedPropertyEnumeratorCallback( const v8::PropertyCallbackInfo& info) { info.GetReturnValue().Set(v8::Array::New(info.GetIsolate())); } void SetUpNamedPropHandler(v8::Isolate* pIsolate, v8::Local pObjectTemplate, const FXJSE_CLASS_DESCRIPTOR* pClassDescriptor) { v8::NamedPropertyHandlerConfiguration configuration( pClassDescriptor->dynPropGetter ? NamedPropertyGetterCallback : nullptr, pClassDescriptor->dynPropSetter ? NamedPropertySetterCallback : nullptr, pClassDescriptor->dynPropTypeGetter ? NamedPropertyQueryCallback : nullptr, nullptr, NamedPropertyEnumeratorCallback, v8::External::New(pIsolate, const_cast(pClassDescriptor)), v8::PropertyHandlerFlags::kNonMasking); pObjectTemplate->SetHandler(configuration); } } // namespace // static CFXJSE_Class* CFXJSE_Class::Create( CFXJSE_Context* pContext, const FXJSE_CLASS_DESCRIPTOR* pClassDescriptor, bool bIsJSGlobal) { if (!pContext || !pClassDescriptor) return nullptr; CFXJSE_Class* pExistingClass = pContext->GetClassByName(pClassDescriptor->name); if (pExistingClass) return pExistingClass; v8::Isolate* pIsolate = pContext->GetIsolate(); auto pClass = std::make_unique(pContext); pClass->m_szClassName = pClassDescriptor->name; pClass->m_pClassDescriptor = pClassDescriptor; CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate); v8::Local hFunctionTemplate = v8::FunctionTemplate::New( pIsolate, bIsJSGlobal ? 0 : V8ConstructorCallback_Wrapper, v8::External::New(pIsolate, const_cast(pClassDescriptor))); v8::Local classname = fxv8::NewStringHelper(pIsolate, pClassDescriptor->name); hFunctionTemplate->SetClassName(classname); hFunctionTemplate->PrototypeTemplate()->Set( v8::Symbol::GetToStringTag(pIsolate), classname, static_cast(v8::ReadOnly | v8::DontEnum)); hFunctionTemplate->InstanceTemplate()->SetInternalFieldCount(2); v8::Local hObjectTemplate = hFunctionTemplate->InstanceTemplate(); SetUpNamedPropHandler(pIsolate, hObjectTemplate, pClassDescriptor); if (pClassDescriptor->methNum) { for (int32_t i = 0; i < pClassDescriptor->methNum; i++) { v8::Local fun = v8::FunctionTemplate::New( pIsolate, V8FunctionCallback_Wrapper, v8::External::New(pIsolate, const_cast( pClassDescriptor->methods + i))); fun->RemovePrototype(); hObjectTemplate->Set( fxv8::NewStringHelper(pIsolate, pClassDescriptor->methods[i].name), fun, static_cast(v8::ReadOnly | v8::DontDelete)); } } if (bIsJSGlobal) { v8::Local fn = v8::FunctionTemplate::New( pIsolate, Context_GlobalObjToString, v8::External::New( pIsolate, const_cast(pClassDescriptor))); fn->RemovePrototype(); hObjectTemplate->Set(fxv8::NewStringHelper(pIsolate, "toString"), fn); } pClass->m_hTemplate.Reset(pContext->GetIsolate(), hFunctionTemplate); CFXJSE_Class* pResult = pClass.get(); pContext->AddClass(std::move(pClass)); return pResult; } CFXJSE_Class::CFXJSE_Class(const CFXJSE_Context* pContext) : m_pContext(pContext) {} CFXJSE_Class::~CFXJSE_Class() = default; v8::Local CFXJSE_Class::GetTemplate( v8::Isolate* pIsolate) { return v8::Local::New(pIsolate, m_hTemplate); }