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