Home | History | Annotate | Download | only in fxjs
      1 // Copyright 2016 PDFium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
      6 
      7 #include "fxjs/cfxjse_class.h"
      8 
      9 #include <memory>
     10 #include <utility>
     11 
     12 #include "fxjs/cfxjse_arguments.h"
     13 #include "fxjs/cfxjse_context.h"
     14 #include "fxjs/cfxjse_value.h"
     15 #include "fxjs/cjs_return.h"
     16 #include "fxjs/js_resources.h"
     17 #include "third_party/base/ptr_util.h"
     18 
     19 namespace {
     20 
     21 void V8FunctionCallback_Wrapper(
     22     const v8::FunctionCallbackInfo<v8::Value>& info) {
     23   const FXJSE_FUNCTION_DESCRIPTOR* lpFunctionInfo =
     24       static_cast<FXJSE_FUNCTION_DESCRIPTOR*>(
     25           info.Data().As<v8::External>()->Value());
     26   if (!lpFunctionInfo)
     27     return;
     28 
     29   ByteStringView szFunctionName(lpFunctionInfo->name);
     30   auto lpThisValue = pdfium::MakeUnique<CFXJSE_Value>(info.GetIsolate());
     31   lpThisValue->ForceSetValue(info.Holder());
     32   auto lpRetValue = pdfium::MakeUnique<CFXJSE_Value>(info.GetIsolate());
     33   CFXJSE_Arguments impl(&info, lpRetValue.get());
     34   lpFunctionInfo->callbackProc(lpThisValue.get(), szFunctionName, impl);
     35   if (!lpRetValue->DirectGetValue().IsEmpty())
     36     info.GetReturnValue().Set(lpRetValue->DirectGetValue());
     37 }
     38 
     39 void V8ConstructorCallback_Wrapper(
     40     const v8::FunctionCallbackInfo<v8::Value>& info) {
     41   if (!info.IsConstructCall())
     42     return;
     43 
     44   const FXJSE_CLASS_DESCRIPTOR* lpClassDefinition =
     45       static_cast<FXJSE_CLASS_DESCRIPTOR*>(
     46           info.Data().As<v8::External>()->Value());
     47   if (!lpClassDefinition)
     48     return;
     49 
     50   ASSERT(info.Holder()->InternalFieldCount());
     51   info.Holder()->SetAlignedPointerInInternalField(0, nullptr);
     52 }
     53 
     54 void Context_GlobalObjToString(
     55     const v8::FunctionCallbackInfo<v8::Value>& info) {
     56   const FXJSE_CLASS_DESCRIPTOR* lpClass = static_cast<FXJSE_CLASS_DESCRIPTOR*>(
     57       info.Data().As<v8::External>()->Value());
     58   if (!lpClass)
     59     return;
     60 
     61   if (info.This() == info.Holder() && lpClass->name) {
     62     ByteString szStringVal = ByteString::Format("[object %s]", lpClass->name);
     63     info.GetReturnValue().Set(v8::String::NewFromUtf8(
     64         info.GetIsolate(), szStringVal.c_str(), v8::String::kNormalString,
     65         szStringVal.GetLength()));
     66     return;
     67   }
     68   v8::Local<v8::String> local_str =
     69       info.Holder()
     70           ->ObjectProtoToString(info.GetIsolate()->GetCurrentContext())
     71           .FromMaybe(v8::Local<v8::String>());
     72   info.GetReturnValue().Set(local_str);
     73 }
     74 
     75 void DynPropGetterAdapter_MethodCallback(
     76     const v8::FunctionCallbackInfo<v8::Value>& info) {
     77   v8::Local<v8::Object> hCallBackInfo = info.Data().As<v8::Object>();
     78   FXJSE_CLASS_DESCRIPTOR* lpClass = static_cast<FXJSE_CLASS_DESCRIPTOR*>(
     79       hCallBackInfo->GetAlignedPointerFromInternalField(0));
     80   v8::Local<v8::String> hPropName =
     81       hCallBackInfo->GetInternalField(1).As<v8::String>();
     82   ASSERT(lpClass && !hPropName.IsEmpty());
     83 
     84   v8::String::Utf8Value szPropName(info.GetIsolate(), hPropName);
     85   WideString szFxPropName = WideString::FromUTF8(*szPropName);
     86 
     87   CJS_Return result = lpClass->dynMethodCall(info, szFxPropName);
     88   if (result.HasError()) {
     89     WideString err = JSFormatErrorString(*szPropName, "", result.Error());
     90     v8::MaybeLocal<v8::String> str = v8::String::NewFromUtf8(
     91         info.GetIsolate(), ByteString::FromUnicode(err).c_str(),
     92         v8::NewStringType::kNormal);
     93     info.GetIsolate()->ThrowException(str.ToLocalChecked());
     94     return;
     95   }
     96   if (result.HasReturn())
     97     info.GetReturnValue().Set(result.Return());
     98 }
     99 
    100 void DynPropGetterAdapter(const FXJSE_CLASS_DESCRIPTOR* lpClass,
    101                           CFXJSE_Value* pObject,
    102                           const ByteStringView& szPropName,
    103                           CFXJSE_Value* pValue) {
    104   ASSERT(lpClass);
    105 
    106   int32_t nPropType =
    107       lpClass->dynPropTypeGetter == nullptr
    108           ? FXJSE_ClassPropType_Property
    109           : lpClass->dynPropTypeGetter(pObject, szPropName, false);
    110   if (nPropType == FXJSE_ClassPropType_Property) {
    111     if (lpClass->dynPropGetter)
    112       lpClass->dynPropGetter(pObject, szPropName, pValue);
    113   } else if (nPropType == FXJSE_ClassPropType_Method) {
    114     if (lpClass->dynMethodCall && pValue) {
    115       v8::Isolate* pIsolate = pValue->GetIsolate();
    116       v8::HandleScope hscope(pIsolate);
    117       v8::Local<v8::ObjectTemplate> hCallBackInfoTemplate =
    118           v8::ObjectTemplate::New(pIsolate);
    119       hCallBackInfoTemplate->SetInternalFieldCount(2);
    120       v8::Local<v8::Object> hCallBackInfo =
    121           hCallBackInfoTemplate->NewInstance();
    122       hCallBackInfo->SetAlignedPointerInInternalField(
    123           0, const_cast<FXJSE_CLASS_DESCRIPTOR*>(lpClass));
    124       hCallBackInfo->SetInternalField(
    125           1, v8::String::NewFromUtf8(
    126                  pIsolate, reinterpret_cast<const char*>(szPropName.raw_str()),
    127                  v8::String::kNormalString, szPropName.GetLength()));
    128       pValue->ForceSetValue(
    129           v8::Function::New(pValue->GetIsolate()->GetCurrentContext(),
    130                             DynPropGetterAdapter_MethodCallback, hCallBackInfo,
    131                             0, v8::ConstructorBehavior::kThrow)
    132               .ToLocalChecked());
    133     }
    134   }
    135 }
    136 
    137 void DynPropSetterAdapter(const FXJSE_CLASS_DESCRIPTOR* lpClass,
    138                           CFXJSE_Value* pObject,
    139                           const ByteStringView& szPropName,
    140                           CFXJSE_Value* pValue) {
    141   ASSERT(lpClass);
    142   int32_t nPropType =
    143       lpClass->dynPropTypeGetter == nullptr
    144           ? FXJSE_ClassPropType_Property
    145           : lpClass->dynPropTypeGetter(pObject, szPropName, false);
    146   if (nPropType != FXJSE_ClassPropType_Method) {
    147     if (lpClass->dynPropSetter)
    148       lpClass->dynPropSetter(pObject, szPropName, pValue);
    149   }
    150 }
    151 
    152 bool DynPropQueryAdapter(const FXJSE_CLASS_DESCRIPTOR* lpClass,
    153                          CFXJSE_Value* pObject,
    154                          const ByteStringView& szPropName) {
    155   ASSERT(lpClass);
    156   int32_t nPropType =
    157       lpClass->dynPropTypeGetter == nullptr
    158           ? FXJSE_ClassPropType_Property
    159           : lpClass->dynPropTypeGetter(pObject, szPropName, true);
    160   return nPropType != FXJSE_ClassPropType_None;
    161 }
    162 
    163 void NamedPropertyQueryCallback(
    164     v8::Local<v8::Name> property,
    165     const v8::PropertyCallbackInfo<v8::Integer>& info) {
    166   v8::Local<v8::Object> thisObject = info.Holder();
    167   const FXJSE_CLASS_DESCRIPTOR* lpClass = static_cast<FXJSE_CLASS_DESCRIPTOR*>(
    168       info.Data().As<v8::External>()->Value());
    169   v8::Isolate* pIsolate = info.GetIsolate();
    170   v8::HandleScope scope(pIsolate);
    171   v8::String::Utf8Value szPropName(pIsolate, property);
    172   ByteStringView szFxPropName(*szPropName, szPropName.length());
    173   auto lpThisValue = pdfium::MakeUnique<CFXJSE_Value>(info.GetIsolate());
    174   lpThisValue->ForceSetValue(thisObject);
    175   if (DynPropQueryAdapter(lpClass, lpThisValue.get(), szFxPropName)) {
    176     info.GetReturnValue().Set(v8::DontDelete);
    177     return;
    178   }
    179   const int32_t iV8Absent = 64;
    180   info.GetReturnValue().Set(iV8Absent);
    181 }
    182 
    183 void NamedPropertyGetterCallback(
    184     v8::Local<v8::Name> property,
    185     const v8::PropertyCallbackInfo<v8::Value>& info) {
    186   v8::Local<v8::Object> thisObject = info.Holder();
    187   const FXJSE_CLASS_DESCRIPTOR* lpClass = static_cast<FXJSE_CLASS_DESCRIPTOR*>(
    188       info.Data().As<v8::External>()->Value());
    189   v8::String::Utf8Value szPropName(info.GetIsolate(), property);
    190   ByteStringView szFxPropName(*szPropName, szPropName.length());
    191   auto lpThisValue = pdfium::MakeUnique<CFXJSE_Value>(info.GetIsolate());
    192   lpThisValue->ForceSetValue(thisObject);
    193   auto lpNewValue = pdfium::MakeUnique<CFXJSE_Value>(info.GetIsolate());
    194   DynPropGetterAdapter(lpClass, lpThisValue.get(), szFxPropName,
    195                        lpNewValue.get());
    196   info.GetReturnValue().Set(lpNewValue->DirectGetValue());
    197 }
    198 
    199 void NamedPropertySetterCallback(
    200     v8::Local<v8::Name> property,
    201     v8::Local<v8::Value> value,
    202     const v8::PropertyCallbackInfo<v8::Value>& info) {
    203   v8::Local<v8::Object> thisObject = info.Holder();
    204   const FXJSE_CLASS_DESCRIPTOR* lpClass = static_cast<FXJSE_CLASS_DESCRIPTOR*>(
    205       info.Data().As<v8::External>()->Value());
    206   v8::String::Utf8Value szPropName(info.GetIsolate(), property);
    207   ByteStringView szFxPropName(*szPropName, szPropName.length());
    208   auto lpThisValue = pdfium::MakeUnique<CFXJSE_Value>(info.GetIsolate());
    209   lpThisValue->ForceSetValue(thisObject);
    210 
    211   auto lpNewValue = pdfium::MakeUnique<CFXJSE_Value>(info.GetIsolate());
    212   lpNewValue->ForceSetValue(value);
    213   DynPropSetterAdapter(lpClass, lpThisValue.get(), szFxPropName,
    214                        lpNewValue.get());
    215   info.GetReturnValue().Set(value);
    216 }
    217 
    218 void NamedPropertyEnumeratorCallback(
    219     const v8::PropertyCallbackInfo<v8::Array>& info) {
    220   info.GetReturnValue().Set(v8::Array::New(info.GetIsolate()));
    221 }
    222 
    223 }  // namespace
    224 
    225 // static
    226 CFXJSE_Class* CFXJSE_Class::Create(
    227     CFXJSE_Context* lpContext,
    228     const FXJSE_CLASS_DESCRIPTOR* lpClassDefinition,
    229     bool bIsJSGlobal) {
    230   if (!lpContext || !lpClassDefinition)
    231     return nullptr;
    232 
    233   CFXJSE_Class* pExistingClass =
    234       lpContext->GetClassByName(lpClassDefinition->name);
    235   if (pExistingClass)
    236     return pExistingClass;
    237 
    238   v8::Isolate* pIsolate = lpContext->GetIsolate();
    239   auto pClass = pdfium::MakeUnique<CFXJSE_Class>(lpContext);
    240   pClass->m_szClassName = lpClassDefinition->name;
    241   pClass->m_lpClassDefinition = lpClassDefinition;
    242   CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
    243   v8::Local<v8::FunctionTemplate> hFunctionTemplate = v8::FunctionTemplate::New(
    244       pIsolate, bIsJSGlobal ? 0 : V8ConstructorCallback_Wrapper,
    245       v8::External::New(
    246           pIsolate, const_cast<FXJSE_CLASS_DESCRIPTOR*>(lpClassDefinition)));
    247   hFunctionTemplate->SetClassName(
    248       v8::String::NewFromUtf8(pIsolate, lpClassDefinition->name));
    249   hFunctionTemplate->InstanceTemplate()->SetInternalFieldCount(2);
    250   v8::Local<v8::ObjectTemplate> hObjectTemplate =
    251       hFunctionTemplate->InstanceTemplate();
    252   SetUpNamedPropHandler(pIsolate, hObjectTemplate, lpClassDefinition);
    253 
    254   if (lpClassDefinition->methNum) {
    255     for (int32_t i = 0; i < lpClassDefinition->methNum; i++) {
    256       v8::Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(
    257           pIsolate, V8FunctionCallback_Wrapper,
    258           v8::External::New(pIsolate, const_cast<FXJSE_FUNCTION_DESCRIPTOR*>(
    259                                           lpClassDefinition->methods + i)));
    260       fun->RemovePrototype();
    261       hObjectTemplate->Set(
    262           v8::String::NewFromUtf8(pIsolate, lpClassDefinition->methods[i].name),
    263           fun,
    264           static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete));
    265     }
    266   }
    267 
    268   if (bIsJSGlobal) {
    269     v8::Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(
    270         pIsolate, Context_GlobalObjToString,
    271         v8::External::New(
    272             pIsolate, const_cast<FXJSE_CLASS_DESCRIPTOR*>(lpClassDefinition)));
    273     fun->RemovePrototype();
    274     hObjectTemplate->Set(v8::String::NewFromUtf8(pIsolate, "toString"), fun);
    275   }
    276   pClass->m_hTemplate.Reset(lpContext->GetIsolate(), hFunctionTemplate);
    277   CFXJSE_Class* pResult = pClass.get();
    278   lpContext->AddClass(std::move(pClass));
    279   return pResult;
    280 }
    281 
    282 // static
    283 void CFXJSE_Class::SetUpNamedPropHandler(
    284     v8::Isolate* pIsolate,
    285     v8::Local<v8::ObjectTemplate>& hObjectTemplate,
    286     const FXJSE_CLASS_DESCRIPTOR* lpClassDefinition) {
    287   v8::NamedPropertyHandlerConfiguration configuration(
    288       lpClassDefinition->dynPropGetter ? NamedPropertyGetterCallback : 0,
    289       lpClassDefinition->dynPropSetter ? NamedPropertySetterCallback : 0,
    290       lpClassDefinition->dynPropTypeGetter ? NamedPropertyQueryCallback : 0, 0,
    291       NamedPropertyEnumeratorCallback,
    292       v8::External::New(pIsolate,
    293                         const_cast<FXJSE_CLASS_DESCRIPTOR*>(lpClassDefinition)),
    294       v8::PropertyHandlerFlags::kNonMasking);
    295   hObjectTemplate->SetHandler(configuration);
    296 }
    297 
    298 CFXJSE_Class::CFXJSE_Class(CFXJSE_Context* lpContext)
    299     : m_lpClassDefinition(nullptr), m_pContext(lpContext) {}
    300 
    301 CFXJSE_Class::~CFXJSE_Class() {}
    302