|  | // Copyright 2016 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/xfa/cfxjse_class.h" | 
|  |  | 
|  | #include <memory> | 
|  | #include <utility> | 
|  |  | 
|  | #include "fxjs/cjs_result.h" | 
|  | #include "fxjs/js_resources.h" | 
|  | #include "fxjs/xfa/cfxjse_arguments.h" | 
|  | #include "fxjs/xfa/cfxjse_context.h" | 
|  | #include "fxjs/xfa/cfxjse_isolatetracker.h" | 
|  | #include "fxjs/xfa/cfxjse_value.h" | 
|  | #include "third_party/base/ptr_util.h" | 
|  |  | 
|  | using pdfium::fxjse::kFuncTag; | 
|  | using pdfium::fxjse::kClassTag; | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | FXJSE_FUNCTION_DESCRIPTOR* AsFunctionDescriptor(void* ptr) { | 
|  | auto* result = static_cast<FXJSE_FUNCTION_DESCRIPTOR*>(ptr); | 
|  | return result && result->tag == kFuncTag ? result : nullptr; | 
|  | } | 
|  |  | 
|  | FXJSE_CLASS_DESCRIPTOR* AsClassDescriptor(void* ptr) { | 
|  | auto* result = static_cast<FXJSE_CLASS_DESCRIPTOR*>(ptr); | 
|  | return result && result->tag == kClassTag ? result : nullptr; | 
|  | } | 
|  |  | 
|  | void V8FunctionCallback_Wrapper( | 
|  | const v8::FunctionCallbackInfo<v8::Value>& info) { | 
|  | const FXJSE_FUNCTION_DESCRIPTOR* lpFunctionInfo = | 
|  | AsFunctionDescriptor(info.Data().As<v8::External>()->Value()); | 
|  | if (!lpFunctionInfo) | 
|  | return; | 
|  |  | 
|  | ByteStringView szFunctionName(lpFunctionInfo->name); | 
|  | auto lpThisValue = pdfium::MakeUnique<CFXJSE_Value>(info.GetIsolate()); | 
|  | lpThisValue->ForceSetValue(info.Holder()); | 
|  | auto lpRetValue = pdfium::MakeUnique<CFXJSE_Value>(info.GetIsolate()); | 
|  | CFXJSE_Arguments impl(&info, lpRetValue.get()); | 
|  | lpFunctionInfo->callbackProc(lpThisValue.get(), szFunctionName, impl); | 
|  | if (!lpRetValue->DirectGetValue().IsEmpty()) | 
|  | info.GetReturnValue().Set(lpRetValue->DirectGetValue()); | 
|  | } | 
|  |  | 
|  | void V8ConstructorCallback_Wrapper( | 
|  | const v8::FunctionCallbackInfo<v8::Value>& info) { | 
|  | if (!info.IsConstructCall()) | 
|  | return; | 
|  |  | 
|  | const FXJSE_CLASS_DESCRIPTOR* lpClassDefinition = | 
|  | AsClassDescriptor(info.Data().As<v8::External>()->Value()); | 
|  | if (!lpClassDefinition) | 
|  | return; | 
|  |  | 
|  | ASSERT(info.Holder()->InternalFieldCount() == 2); | 
|  | info.Holder()->SetAlignedPointerInInternalField(0, nullptr); | 
|  | info.Holder()->SetAlignedPointerInInternalField(1, nullptr); | 
|  | } | 
|  |  | 
|  | void Context_GlobalObjToString( | 
|  | const v8::FunctionCallbackInfo<v8::Value>& info) { | 
|  | const FXJSE_CLASS_DESCRIPTOR* lpClass = | 
|  | AsClassDescriptor(info.Data().As<v8::External>()->Value()); | 
|  | if (!lpClass) | 
|  | return; | 
|  |  | 
|  | if (info.This() == info.Holder() && lpClass->name) { | 
|  | ByteString szStringVal = ByteString::Format("[object %s]", lpClass->name); | 
|  | info.GetReturnValue().Set( | 
|  | v8::String::NewFromUtf8(info.GetIsolate(), szStringVal.c_str(), | 
|  | v8::NewStringType::kNormal, | 
|  | szStringVal.GetLength()) | 
|  | .ToLocalChecked()); | 
|  | return; | 
|  | } | 
|  | v8::Local<v8::String> local_str = | 
|  | info.Holder() | 
|  | ->ObjectProtoToString(info.GetIsolate()->GetCurrentContext()) | 
|  | .FromMaybe(v8::Local<v8::String>()); | 
|  | info.GetReturnValue().Set(local_str); | 
|  | } | 
|  |  | 
|  | void DynPropGetterAdapter_MethodCallback( | 
|  | const v8::FunctionCallbackInfo<v8::Value>& info) { | 
|  | v8::Local<v8::Object> hCallBackInfo = info.Data().As<v8::Object>(); | 
|  | if (hCallBackInfo->InternalFieldCount() != 2) | 
|  | return; | 
|  |  | 
|  | auto* pClassDescriptor = static_cast<const FXJSE_CLASS_DESCRIPTOR*>( | 
|  | hCallBackInfo->GetAlignedPointerFromInternalField(0)); | 
|  | if (pClassDescriptor != &GlobalClassDescriptor && | 
|  | pClassDescriptor != &NormalClassDescriptor && | 
|  | pClassDescriptor != &VariablesClassDescriptor && | 
|  | pClassDescriptor != &kFormCalcFM2JSDescriptor) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | v8::Local<v8::String> hPropName = | 
|  | hCallBackInfo->GetInternalField(1).As<v8::String>(); | 
|  | 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()); | 
|  | v8::MaybeLocal<v8::String> str = v8::String::NewFromUtf8( | 
|  | info.GetIsolate(), err.ToDefANSI().c_str(), v8::NewStringType::kNormal); | 
|  | info.GetIsolate()->ThrowException(str.ToLocalChecked()); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (result.HasReturn()) | 
|  | info.GetReturnValue().Set(result.Return()); | 
|  | } | 
|  |  | 
|  | void DynPropGetterAdapter(const FXJSE_CLASS_DESCRIPTOR* lpClass, | 
|  | CFXJSE_Value* pObject, | 
|  | ByteStringView szPropName, | 
|  | CFXJSE_Value* pValue) { | 
|  | ASSERT(lpClass); | 
|  |  | 
|  | int32_t nPropType = | 
|  | lpClass->dynPropTypeGetter == nullptr | 
|  | ? FXJSE_ClassPropType_Property | 
|  | : lpClass->dynPropTypeGetter(pObject, szPropName, false); | 
|  | if (nPropType == FXJSE_ClassPropType_Property) { | 
|  | if (lpClass->dynPropGetter) | 
|  | lpClass->dynPropGetter(pObject, szPropName, pValue); | 
|  | } else if (nPropType == FXJSE_ClassPropType_Method) { | 
|  | if (lpClass->dynMethodCall && pValue) { | 
|  | v8::Isolate* pIsolate = pValue->GetIsolate(); | 
|  | v8::HandleScope hscope(pIsolate); | 
|  | v8::Local<v8::ObjectTemplate> hCallBackInfoTemplate = | 
|  | v8::ObjectTemplate::New(pIsolate); | 
|  | hCallBackInfoTemplate->SetInternalFieldCount(2); | 
|  | v8::Local<v8::Object> hCallBackInfo = | 
|  | hCallBackInfoTemplate | 
|  | ->NewInstance(pValue->GetIsolate()->GetCurrentContext()) | 
|  | .ToLocalChecked(); | 
|  | hCallBackInfo->SetAlignedPointerInInternalField( | 
|  | 0, const_cast<FXJSE_CLASS_DESCRIPTOR*>(lpClass)); | 
|  | hCallBackInfo->SetInternalField( | 
|  | 1, v8::String::NewFromUtf8( | 
|  | pIsolate, reinterpret_cast<const char*>(szPropName.raw_str()), | 
|  | v8::NewStringType::kNormal, szPropName.GetLength()) | 
|  | .ToLocalChecked()); | 
|  | pValue->ForceSetValue( | 
|  | v8::Function::New(pValue->GetIsolate()->GetCurrentContext(), | 
|  | DynPropGetterAdapter_MethodCallback, hCallBackInfo, | 
|  | 0, v8::ConstructorBehavior::kThrow) | 
|  | .ToLocalChecked()); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void DynPropSetterAdapter(const FXJSE_CLASS_DESCRIPTOR* lpClass, | 
|  | CFXJSE_Value* pObject, | 
|  | ByteStringView szPropName, | 
|  | CFXJSE_Value* pValue) { | 
|  | ASSERT(lpClass); | 
|  | int32_t nPropType = | 
|  | lpClass->dynPropTypeGetter == nullptr | 
|  | ? FXJSE_ClassPropType_Property | 
|  | : lpClass->dynPropTypeGetter(pObject, szPropName, false); | 
|  | if (nPropType != FXJSE_ClassPropType_Method) { | 
|  | if (lpClass->dynPropSetter) | 
|  | lpClass->dynPropSetter(pObject, szPropName, pValue); | 
|  | } | 
|  | } | 
|  |  | 
|  | bool DynPropQueryAdapter(const FXJSE_CLASS_DESCRIPTOR* lpClass, | 
|  | CFXJSE_Value* pObject, | 
|  | ByteStringView szPropName) { | 
|  | ASSERT(lpClass); | 
|  | int32_t nPropType = | 
|  | lpClass->dynPropTypeGetter == nullptr | 
|  | ? FXJSE_ClassPropType_Property | 
|  | : lpClass->dynPropTypeGetter(pObject, szPropName, true); | 
|  | return nPropType != FXJSE_ClassPropType_None; | 
|  | } | 
|  |  | 
|  | void NamedPropertyQueryCallback( | 
|  | v8::Local<v8::Name> property, | 
|  | const v8::PropertyCallbackInfo<v8::Integer>& info) { | 
|  | v8::Local<v8::Object> thisObject = info.Holder(); | 
|  | const FXJSE_CLASS_DESCRIPTOR* lpClass = | 
|  | AsClassDescriptor(info.Data().As<v8::External>()->Value()); | 
|  | if (!lpClass) | 
|  | return; | 
|  |  | 
|  | v8::HandleScope scope(info.GetIsolate()); | 
|  | v8::String::Utf8Value szPropName(info.GetIsolate(), property); | 
|  | ByteStringView szFxPropName(*szPropName, szPropName.length()); | 
|  | auto lpThisValue = pdfium::MakeUnique<CFXJSE_Value>(info.GetIsolate()); | 
|  | lpThisValue->ForceSetValue(thisObject); | 
|  | if (DynPropQueryAdapter(lpClass, lpThisValue.get(), szFxPropName)) { | 
|  | info.GetReturnValue().Set(v8::DontDelete); | 
|  | return; | 
|  | } | 
|  | const int32_t iV8Absent = 64; | 
|  | info.GetReturnValue().Set(iV8Absent); | 
|  | } | 
|  |  | 
|  | void NamedPropertyGetterCallback( | 
|  | v8::Local<v8::Name> property, | 
|  | const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | v8::Local<v8::Object> thisObject = info.Holder(); | 
|  | const FXJSE_CLASS_DESCRIPTOR* lpClass = | 
|  | AsClassDescriptor(info.Data().As<v8::External>()->Value()); | 
|  | if (!lpClass) | 
|  | return; | 
|  |  | 
|  | v8::String::Utf8Value szPropName(info.GetIsolate(), property); | 
|  | ByteStringView szFxPropName(*szPropName, szPropName.length()); | 
|  | auto lpThisValue = pdfium::MakeUnique<CFXJSE_Value>(info.GetIsolate()); | 
|  | lpThisValue->ForceSetValue(thisObject); | 
|  | auto lpNewValue = pdfium::MakeUnique<CFXJSE_Value>(info.GetIsolate()); | 
|  | DynPropGetterAdapter(lpClass, lpThisValue.get(), szFxPropName, | 
|  | lpNewValue.get()); | 
|  | info.GetReturnValue().Set(lpNewValue->DirectGetValue()); | 
|  | } | 
|  |  | 
|  | void NamedPropertySetterCallback( | 
|  | v8::Local<v8::Name> property, | 
|  | v8::Local<v8::Value> value, | 
|  | const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | v8::Local<v8::Object> thisObject = info.Holder(); | 
|  | const FXJSE_CLASS_DESCRIPTOR* lpClass = | 
|  | AsClassDescriptor(info.Data().As<v8::External>()->Value()); | 
|  | if (!lpClass) | 
|  | return; | 
|  |  | 
|  | v8::String::Utf8Value szPropName(info.GetIsolate(), property); | 
|  | ByteStringView szFxPropName(*szPropName, szPropName.length()); | 
|  | auto lpThisValue = pdfium::MakeUnique<CFXJSE_Value>(info.GetIsolate()); | 
|  | lpThisValue->ForceSetValue(thisObject); | 
|  | auto lpNewValue = pdfium::MakeUnique<CFXJSE_Value>(info.GetIsolate()); | 
|  | lpNewValue->ForceSetValue(value); | 
|  | DynPropSetterAdapter(lpClass, lpThisValue.get(), szFxPropName, | 
|  | lpNewValue.get()); | 
|  | info.GetReturnValue().Set(value); | 
|  | } | 
|  |  | 
|  | void NamedPropertyEnumeratorCallback( | 
|  | const v8::PropertyCallbackInfo<v8::Array>& info) { | 
|  | info.GetReturnValue().Set(v8::Array::New(info.GetIsolate())); | 
|  | } | 
|  |  | 
|  | void SetUpNamedPropHandler(v8::Isolate* pIsolate, | 
|  | v8::Local<v8::ObjectTemplate>* pObjectTemplate, | 
|  | const FXJSE_CLASS_DESCRIPTOR* lpClassDefinition) { | 
|  | v8::NamedPropertyHandlerConfiguration configuration( | 
|  | lpClassDefinition->dynPropGetter ? NamedPropertyGetterCallback : nullptr, | 
|  | lpClassDefinition->dynPropSetter ? NamedPropertySetterCallback : nullptr, | 
|  | lpClassDefinition->dynPropTypeGetter ? NamedPropertyQueryCallback | 
|  | : nullptr, | 
|  | nullptr, NamedPropertyEnumeratorCallback, | 
|  | v8::External::New(pIsolate, | 
|  | const_cast<FXJSE_CLASS_DESCRIPTOR*>(lpClassDefinition)), | 
|  | v8::PropertyHandlerFlags::kNonMasking); | 
|  | (*pObjectTemplate)->SetHandler(configuration); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | // static | 
|  | CFXJSE_Class* CFXJSE_Class::Create( | 
|  | CFXJSE_Context* lpContext, | 
|  | const FXJSE_CLASS_DESCRIPTOR* lpClassDefinition, | 
|  | bool bIsJSGlobal) { | 
|  | if (!lpContext || !lpClassDefinition) | 
|  | return nullptr; | 
|  |  | 
|  | CFXJSE_Class* pExistingClass = | 
|  | lpContext->GetClassByName(lpClassDefinition->name); | 
|  | if (pExistingClass) | 
|  | return pExistingClass; | 
|  |  | 
|  | v8::Isolate* pIsolate = lpContext->GetIsolate(); | 
|  | auto pClass = pdfium::MakeUnique<CFXJSE_Class>(lpContext); | 
|  | pClass->m_szClassName = lpClassDefinition->name; | 
|  | pClass->m_lpClassDefinition = lpClassDefinition; | 
|  | CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate); | 
|  | v8::Local<v8::FunctionTemplate> hFunctionTemplate = v8::FunctionTemplate::New( | 
|  | pIsolate, bIsJSGlobal ? 0 : V8ConstructorCallback_Wrapper, | 
|  | v8::External::New( | 
|  | pIsolate, const_cast<FXJSE_CLASS_DESCRIPTOR*>(lpClassDefinition))); | 
|  | hFunctionTemplate->SetClassName( | 
|  | v8::String::NewFromUtf8(pIsolate, lpClassDefinition->name, | 
|  | v8::NewStringType::kNormal) | 
|  | .ToLocalChecked()); | 
|  | hFunctionTemplate->InstanceTemplate()->SetInternalFieldCount(2); | 
|  | v8::Local<v8::ObjectTemplate> hObjectTemplate = | 
|  | hFunctionTemplate->InstanceTemplate(); | 
|  | SetUpNamedPropHandler(pIsolate, &hObjectTemplate, lpClassDefinition); | 
|  |  | 
|  | if (lpClassDefinition->methNum) { | 
|  | for (int32_t i = 0; i < lpClassDefinition->methNum; i++) { | 
|  | v8::Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New( | 
|  | pIsolate, V8FunctionCallback_Wrapper, | 
|  | v8::External::New(pIsolate, const_cast<FXJSE_FUNCTION_DESCRIPTOR*>( | 
|  | lpClassDefinition->methods + i))); | 
|  | fun->RemovePrototype(); | 
|  | hObjectTemplate->Set( | 
|  | v8::String::NewFromUtf8(pIsolate, lpClassDefinition->methods[i].name, | 
|  | v8::NewStringType::kNormal) | 
|  | .ToLocalChecked(), | 
|  | fun, | 
|  | static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete)); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (bIsJSGlobal) { | 
|  | v8::Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New( | 
|  | pIsolate, Context_GlobalObjToString, | 
|  | v8::External::New( | 
|  | pIsolate, const_cast<FXJSE_CLASS_DESCRIPTOR*>(lpClassDefinition))); | 
|  | fun->RemovePrototype(); | 
|  | hObjectTemplate->Set(v8::String::NewFromUtf8(pIsolate, "toString", | 
|  | v8::NewStringType::kNormal) | 
|  | .ToLocalChecked(), | 
|  | fun); | 
|  | } | 
|  | pClass->m_hTemplate.Reset(lpContext->GetIsolate(), hFunctionTemplate); | 
|  | CFXJSE_Class* pResult = pClass.get(); | 
|  | lpContext->AddClass(std::move(pClass)); | 
|  | return pResult; | 
|  | } | 
|  |  | 
|  | CFXJSE_Class::CFXJSE_Class(CFXJSE_Context* lpContext) : m_pContext(lpContext) {} | 
|  |  | 
|  | CFXJSE_Class::~CFXJSE_Class() {} |