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/xfa/cfxjse_class.h"
8 
9 #include <memory>
10 #include <utility>
11 
12 #include "fxjs/cfx_v8.h"
13 #include "fxjs/cjs_result.h"
14 #include "fxjs/fxv8.h"
15 #include "fxjs/js_resources.h"
16 #include "fxjs/xfa/cfxjse_context.h"
17 #include "fxjs/xfa/cfxjse_isolatetracker.h"
18 #include "fxjs/xfa/cfxjse_value.h"
19 
20 using pdfium::fxjse::kClassTag;
21 using pdfium::fxjse::kFuncTag;
22 
23 namespace {
24 
AsFunctionDescriptor(void * ptr)25 FXJSE_FUNCTION_DESCRIPTOR* AsFunctionDescriptor(void* ptr) {
26   auto* result = static_cast<FXJSE_FUNCTION_DESCRIPTOR*>(ptr);
27   return result && result->tag == kFuncTag ? result : nullptr;
28 }
29 
AsClassDescriptor(void * ptr)30 FXJSE_CLASS_DESCRIPTOR* AsClassDescriptor(void* ptr) {
31   auto* result = static_cast<FXJSE_CLASS_DESCRIPTOR*>(ptr);
32   return result && result->tag == kClassTag ? result : nullptr;
33 }
34 
V8FunctionCallback_Wrapper(const v8::FunctionCallbackInfo<v8::Value> & info)35 void V8FunctionCallback_Wrapper(
36     const v8::FunctionCallbackInfo<v8::Value>& info) {
37   const FXJSE_FUNCTION_DESCRIPTOR* lpFunctionInfo =
38       AsFunctionDescriptor(info.Data().As<v8::External>()->Value());
39   if (!lpFunctionInfo)
40     return;
41 
42   lpFunctionInfo->callbackProc(CFXJSE_HostObject::FromV8(info.Holder()), info);
43 }
44 
V8ConstructorCallback_Wrapper(const v8::FunctionCallbackInfo<v8::Value> & info)45 void V8ConstructorCallback_Wrapper(
46     const v8::FunctionCallbackInfo<v8::Value>& info) {
47   if (!info.IsConstructCall())
48     return;
49 
50   const FXJSE_CLASS_DESCRIPTOR* lpClassDefinition =
51       AsClassDescriptor(info.Data().As<v8::External>()->Value());
52   if (!lpClassDefinition)
53     return;
54 
55   ASSERT(info.Holder()->InternalFieldCount() == 2);
56   info.Holder()->SetAlignedPointerInInternalField(0, nullptr);
57   info.Holder()->SetAlignedPointerInInternalField(1, nullptr);
58 }
59 
Context_GlobalObjToString(const v8::FunctionCallbackInfo<v8::Value> & info)60 void Context_GlobalObjToString(
61     const v8::FunctionCallbackInfo<v8::Value>& info) {
62   const FXJSE_CLASS_DESCRIPTOR* lpClass =
63       AsClassDescriptor(info.Data().As<v8::External>()->Value());
64   if (!lpClass)
65     return;
66 
67   if (info.This() == info.Holder() && lpClass->name) {
68     ByteString szStringVal = ByteString::Format("[object %s]", lpClass->name);
69     info.GetReturnValue().Set(
70         fxv8::NewStringHelper(info.GetIsolate(), szStringVal.AsStringView()));
71     return;
72   }
73   v8::Local<v8::String> local_str =
74       info.Holder()
75           ->ObjectProtoToString(info.GetIsolate()->GetCurrentContext())
76           .FromMaybe(v8::Local<v8::String>());
77   info.GetReturnValue().Set(local_str);
78 }
79 
DynPropGetterAdapter_MethodCallback(const v8::FunctionCallbackInfo<v8::Value> & info)80 void DynPropGetterAdapter_MethodCallback(
81     const v8::FunctionCallbackInfo<v8::Value>& info) {
82   v8::Local<v8::Object> hCallBackInfo = info.Data().As<v8::Object>();
83   if (hCallBackInfo->InternalFieldCount() != 2)
84     return;
85 
86   auto* pClassDescriptor = static_cast<const FXJSE_CLASS_DESCRIPTOR*>(
87       hCallBackInfo->GetAlignedPointerFromInternalField(0));
88   if (pClassDescriptor != &GlobalClassDescriptor &&
89       pClassDescriptor != &NormalClassDescriptor &&
90       pClassDescriptor != &VariablesClassDescriptor &&
91       pClassDescriptor != &kFormCalcFM2JSDescriptor) {
92     return;
93   }
94 
95   v8::Local<v8::String> hPropName =
96       hCallBackInfo->GetInternalField(1).As<v8::String>();
97   if (hPropName.IsEmpty())
98     return;
99 
100   v8::String::Utf8Value szPropName(info.GetIsolate(), hPropName);
101   CJS_Result result =
102       pClassDescriptor->dynMethodCall(info, WideString::FromUTF8(*szPropName));
103 
104   if (result.HasError()) {
105     WideString err = JSFormatErrorString(pClassDescriptor->name, *szPropName,
106                                          result.Error());
107     fxv8::ThrowExceptionHelper(info.GetIsolate(), err.AsStringView());
108     return;
109   }
110 
111   if (result.HasReturn())
112     info.GetReturnValue().Set(result.Return());
113 }
114 
DynPropGetterAdapter(v8::Isolate * pIsolate,const FXJSE_CLASS_DESCRIPTOR * lpClass,v8::Local<v8::Object> pObject,ByteStringView szPropName,CFXJSE_Value * pValue)115 void DynPropGetterAdapter(v8::Isolate* pIsolate,
116                           const FXJSE_CLASS_DESCRIPTOR* lpClass,
117                           v8::Local<v8::Object> pObject,
118                           ByteStringView szPropName,
119                           CFXJSE_Value* pValue) {
120   ASSERT(lpClass);
121 
122   int32_t nPropType =
123       lpClass->dynPropTypeGetter == nullptr
124           ? FXJSE_ClassPropType_Property
125           : lpClass->dynPropTypeGetter(pIsolate, pObject, szPropName, false);
126   if (nPropType == FXJSE_ClassPropType_Property) {
127     if (lpClass->dynPropGetter)
128       lpClass->dynPropGetter(pIsolate, pObject, szPropName, pValue);
129   } else if (nPropType == FXJSE_ClassPropType_Method) {
130     if (lpClass->dynMethodCall && pValue) {
131       v8::HandleScope hscope(pIsolate);
132       v8::Local<v8::ObjectTemplate> hCallBackInfoTemplate =
133           v8::ObjectTemplate::New(pIsolate);
134       hCallBackInfoTemplate->SetInternalFieldCount(2);
135       v8::Local<v8::Object> hCallBackInfo =
136           hCallBackInfoTemplate->NewInstance(pIsolate->GetCurrentContext())
137               .ToLocalChecked();
138       hCallBackInfo->SetAlignedPointerInInternalField(
139           0, const_cast<FXJSE_CLASS_DESCRIPTOR*>(lpClass));
140       hCallBackInfo->SetInternalField(
141           1, fxv8::NewStringHelper(pIsolate, szPropName));
142       pValue->ForceSetValue(
143           pIsolate,
144           v8::Function::New(pIsolate->GetCurrentContext(),
145                             DynPropGetterAdapter_MethodCallback, hCallBackInfo,
146                             0, v8::ConstructorBehavior::kThrow)
147               .ToLocalChecked());
148     }
149   }
150 }
151 
DynPropSetterAdapter(v8::Isolate * pIsolate,const FXJSE_CLASS_DESCRIPTOR * lpClass,v8::Local<v8::Object> pObject,ByteStringView szPropName,CFXJSE_Value * pValue)152 void DynPropSetterAdapter(v8::Isolate* pIsolate,
153                           const FXJSE_CLASS_DESCRIPTOR* lpClass,
154                           v8::Local<v8::Object> pObject,
155                           ByteStringView szPropName,
156                           CFXJSE_Value* pValue) {
157   ASSERT(lpClass);
158   int32_t nPropType =
159       lpClass->dynPropTypeGetter == nullptr
160           ? FXJSE_ClassPropType_Property
161           : lpClass->dynPropTypeGetter(pIsolate, pObject, szPropName, false);
162   if (nPropType != FXJSE_ClassPropType_Method) {
163     if (lpClass->dynPropSetter)
164       lpClass->dynPropSetter(pIsolate, pObject, szPropName, pValue);
165   }
166 }
167 
DynPropQueryAdapter(v8::Isolate * pIsolate,const FXJSE_CLASS_DESCRIPTOR * lpClass,v8::Local<v8::Object> pObject,ByteStringView szPropName)168 bool DynPropQueryAdapter(v8::Isolate* pIsolate,
169                          const FXJSE_CLASS_DESCRIPTOR* lpClass,
170                          v8::Local<v8::Object> pObject,
171                          ByteStringView szPropName) {
172   ASSERT(lpClass);
173   int32_t nPropType =
174       lpClass->dynPropTypeGetter == nullptr
175           ? FXJSE_ClassPropType_Property
176           : lpClass->dynPropTypeGetter(pIsolate, pObject, szPropName, true);
177   return nPropType != FXJSE_ClassPropType_None;
178 }
179 
NamedPropertyQueryCallback(v8::Local<v8::Name> property,const v8::PropertyCallbackInfo<v8::Integer> & info)180 void NamedPropertyQueryCallback(
181     v8::Local<v8::Name> property,
182     const v8::PropertyCallbackInfo<v8::Integer>& info) {
183   const FXJSE_CLASS_DESCRIPTOR* pClass =
184       AsClassDescriptor(info.Data().As<v8::External>()->Value());
185   if (!pClass)
186     return;
187 
188   v8::HandleScope scope(info.GetIsolate());
189   v8::String::Utf8Value szPropName(info.GetIsolate(), property);
190   ByteStringView szFxPropName(*szPropName, szPropName.length());
191   if (DynPropQueryAdapter(info.GetIsolate(), pClass, info.Holder(),
192                           szFxPropName)) {
193     info.GetReturnValue().Set(v8::DontDelete);
194     return;
195   }
196   const int32_t iV8Absent = 64;
197   info.GetReturnValue().Set(iV8Absent);
198 }
199 
NamedPropertyGetterCallback(v8::Local<v8::Name> property,const v8::PropertyCallbackInfo<v8::Value> & info)200 void NamedPropertyGetterCallback(
201     v8::Local<v8::Name> property,
202     const v8::PropertyCallbackInfo<v8::Value>& info) {
203   const FXJSE_CLASS_DESCRIPTOR* pClass =
204       AsClassDescriptor(info.Data().As<v8::External>()->Value());
205   if (!pClass)
206     return;
207 
208   v8::String::Utf8Value szPropName(info.GetIsolate(), property);
209   ByteStringView szFxPropName(*szPropName, szPropName.length());
210   auto pNewValue = std::make_unique<CFXJSE_Value>();
211   DynPropGetterAdapter(info.GetIsolate(), pClass, info.Holder(), szFxPropName,
212                        pNewValue.get());
213   info.GetReturnValue().Set(pNewValue->DirectGetValue());
214 }
215 
NamedPropertySetterCallback(v8::Local<v8::Name> property,v8::Local<v8::Value> value,const v8::PropertyCallbackInfo<v8::Value> & info)216 void NamedPropertySetterCallback(
217     v8::Local<v8::Name> property,
218     v8::Local<v8::Value> value,
219     const v8::PropertyCallbackInfo<v8::Value>& info) {
220   const FXJSE_CLASS_DESCRIPTOR* pClass =
221       AsClassDescriptor(info.Data().As<v8::External>()->Value());
222   if (!pClass)
223     return;
224 
225   v8::String::Utf8Value szPropName(info.GetIsolate(), property);
226   ByteStringView szFxPropName(*szPropName, szPropName.length());
227   auto pNewValue = std::make_unique<CFXJSE_Value>(info.GetIsolate(), value);
228   DynPropSetterAdapter(info.GetIsolate(), pClass, info.Holder(), szFxPropName,
229                        pNewValue.get());
230   info.GetReturnValue().Set(value);
231 }
232 
NamedPropertyEnumeratorCallback(const v8::PropertyCallbackInfo<v8::Array> & info)233 void NamedPropertyEnumeratorCallback(
234     const v8::PropertyCallbackInfo<v8::Array>& info) {
235   info.GetReturnValue().Set(v8::Array::New(info.GetIsolate()));
236 }
237 
SetUpNamedPropHandler(v8::Isolate * pIsolate,v8::Local<v8::ObjectTemplate> * pObjectTemplate,const FXJSE_CLASS_DESCRIPTOR * lpClassDefinition)238 void SetUpNamedPropHandler(v8::Isolate* pIsolate,
239                            v8::Local<v8::ObjectTemplate>* pObjectTemplate,
240                            const FXJSE_CLASS_DESCRIPTOR* lpClassDefinition) {
241   v8::NamedPropertyHandlerConfiguration configuration(
242       lpClassDefinition->dynPropGetter ? NamedPropertyGetterCallback : nullptr,
243       lpClassDefinition->dynPropSetter ? NamedPropertySetterCallback : nullptr,
244       lpClassDefinition->dynPropTypeGetter ? NamedPropertyQueryCallback
245                                            : nullptr,
246       nullptr, NamedPropertyEnumeratorCallback,
247       v8::External::New(pIsolate,
248                         const_cast<FXJSE_CLASS_DESCRIPTOR*>(lpClassDefinition)),
249       v8::PropertyHandlerFlags::kNonMasking);
250   (*pObjectTemplate)->SetHandler(configuration);
251 }
252 
253 }  // namespace
254 
255 // static
Create(CFXJSE_Context * lpContext,const FXJSE_CLASS_DESCRIPTOR * lpClassDefinition,bool bIsJSGlobal)256 CFXJSE_Class* CFXJSE_Class::Create(
257     CFXJSE_Context* lpContext,
258     const FXJSE_CLASS_DESCRIPTOR* lpClassDefinition,
259     bool bIsJSGlobal) {
260   if (!lpContext || !lpClassDefinition)
261     return nullptr;
262 
263   CFXJSE_Class* pExistingClass =
264       lpContext->GetClassByName(lpClassDefinition->name);
265   if (pExistingClass)
266     return pExistingClass;
267 
268   v8::Isolate* pIsolate = lpContext->GetIsolate();
269   auto pClass = std::make_unique<CFXJSE_Class>(lpContext);
270   pClass->m_szClassName = lpClassDefinition->name;
271   pClass->m_lpClassDefinition = lpClassDefinition;
272   CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
273   v8::Local<v8::FunctionTemplate> hFunctionTemplate = v8::FunctionTemplate::New(
274       pIsolate, bIsJSGlobal ? 0 : V8ConstructorCallback_Wrapper,
275       v8::External::New(
276           pIsolate, const_cast<FXJSE_CLASS_DESCRIPTOR*>(lpClassDefinition)));
277   v8::Local<v8::String> classname =
278       fxv8::NewStringHelper(pIsolate, lpClassDefinition->name);
279   hFunctionTemplate->SetClassName(classname);
280   hFunctionTemplate->PrototypeTemplate()->Set(
281       v8::Symbol::GetToStringTag(pIsolate), classname,
282       static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
283   hFunctionTemplate->InstanceTemplate()->SetInternalFieldCount(2);
284   v8::Local<v8::ObjectTemplate> hObjectTemplate =
285       hFunctionTemplate->InstanceTemplate();
286   SetUpNamedPropHandler(pIsolate, &hObjectTemplate, lpClassDefinition);
287 
288   if (lpClassDefinition->methNum) {
289     for (int32_t i = 0; i < lpClassDefinition->methNum; i++) {
290       v8::Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(
291           pIsolate, V8FunctionCallback_Wrapper,
292           v8::External::New(pIsolate, const_cast<FXJSE_FUNCTION_DESCRIPTOR*>(
293                                           lpClassDefinition->methods + i)));
294       fun->RemovePrototype();
295       hObjectTemplate->Set(
296           fxv8::NewStringHelper(pIsolate, lpClassDefinition->methods[i].name),
297           fun,
298           static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete));
299     }
300   }
301 
302   if (bIsJSGlobal) {
303     v8::Local<v8::FunctionTemplate> fn = v8::FunctionTemplate::New(
304         pIsolate, Context_GlobalObjToString,
305         v8::External::New(
306             pIsolate, const_cast<FXJSE_CLASS_DESCRIPTOR*>(lpClassDefinition)));
307     fn->RemovePrototype();
308     hObjectTemplate->Set(fxv8::NewStringHelper(pIsolate, "toString"), fn);
309   }
310   pClass->m_hTemplate.Reset(lpContext->GetIsolate(), hFunctionTemplate);
311   CFXJSE_Class* pResult = pClass.get();
312   lpContext->AddClass(std::move(pClass));
313   return pResult;
314 }
315 
CFXJSE_Class(CFXJSE_Context * lpContext)316 CFXJSE_Class::CFXJSE_Class(CFXJSE_Context* lpContext) : m_pContext(lpContext) {}
317 
318 CFXJSE_Class::~CFXJSE_Class() = default;
319 
GetTemplate(v8::Isolate * pIsolate)320 v8::Local<v8::FunctionTemplate> CFXJSE_Class::GetTemplate(
321     v8::Isolate* pIsolate) {
322   return v8::Local<v8::FunctionTemplate>::New(pIsolate, m_hTemplate);
323 }
324