• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 The PDFium Authors
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/cfxjs_engine.h"
8 
9 #include <memory>
10 #include <utility>
11 
12 #include "core/fxcrt/check.h"
13 #include "core/fxcrt/check_op.h"
14 #include "core/fxcrt/stl_util.h"
15 #include "core/fxcrt/unowned_ptr.h"
16 #include "fxjs/cfx_v8_array_buffer_allocator.h"
17 #include "fxjs/cjs_object.h"
18 #include "fxjs/fxv8.h"
19 #include "fxjs/xfa/cfxjse_runtimedata.h"
20 #include "v8/include/v8-context.h"
21 #include "v8/include/v8-exception.h"
22 #include "v8/include/v8-isolate.h"
23 #include "v8/include/v8-message.h"
24 #include "v8/include/v8-primitive.h"
25 #include "v8/include/v8-script.h"
26 #include "v8/include/v8-util.h"
27 
28 namespace {
29 
30 unsigned int g_embedderDataSlot = 1u;
31 v8::Isolate* g_isolate = nullptr;
32 size_t g_isolate_ref_count = 0;
33 CFX_V8ArrayBufferAllocator* g_arrayBufferAllocator = nullptr;
34 v8::Global<v8::ObjectTemplate>* g_DefaultGlobalObjectTemplate = nullptr;
35 
36 // Only the address matters, values are for humans debugging. ASLR should
37 // ensure that these values are unlikely to arise otherwise. Keep these
38 // wchar_t to prevent the compiler from doing something clever, like
39 // aligning them on a byte boundary to save space, which would make them
40 // incompatible for use as V8 aligned pointers.
41 const wchar_t kPerObjectDataTag[] = L"CFXJS_PerObjectData";
42 const wchar_t kPerIsolateDataTag[] = L"CFXJS_PerIsolateData";
43 
GetAlignedPointerForPerObjectDataTag()44 void* GetAlignedPointerForPerObjectDataTag() {
45   return const_cast<void*>(static_cast<const void*>(kPerObjectDataTag));
46 }
47 
GetLineAndColumnFromError(v8::Local<v8::Message> message,v8::Local<v8::Context> context)48 std::pair<int, int> GetLineAndColumnFromError(v8::Local<v8::Message> message,
49                                               v8::Local<v8::Context> context) {
50   if (message.IsEmpty())
51     return std::make_pair(-1, -1);
52   return std::make_pair(message->GetLineNumber(context).FromMaybe(-1),
53                         message->GetStartColumn());
54 }
55 
56 }  // namespace
57 
58 // static
SetNewDataInObject(FXJSOBJTYPE eObjType,uint32_t nObjDefnID,v8::Local<v8::Object> pObj)59 void CFXJS_PerObjectData::SetNewDataInObject(FXJSOBJTYPE eObjType,
60                                              uint32_t nObjDefnID,
61                                              v8::Local<v8::Object> pObj) {
62   if (pObj->InternalFieldCount() == 2) {
63     pObj->SetAlignedPointerInInternalField(
64         0, GetAlignedPointerForPerObjectDataTag());
65     pObj->SetAlignedPointerInInternalField(
66         1, new CFXJS_PerObjectData(eObjType, nObjDefnID));
67   }
68 }
69 
70 // static
GetFromObject(v8::Local<v8::Object> pObj)71 CFXJS_PerObjectData* CFXJS_PerObjectData::GetFromObject(
72     v8::Local<v8::Object> pObj) {
73   if (pObj.IsEmpty()) {
74     return nullptr;
75   }
76   if (HasInternalFields(pObj)) {
77     return ExtractFromObject(pObj);
78   }
79   // `pObj` might be the global object proxy, in which case its prototype
80   // is the global object with the internal fields.
81   v8::Local<v8::Value> proto = pObj->GetPrototype();
82   if (proto.IsEmpty() || !proto->IsObject()) {
83     return nullptr;
84   }
85   pObj = proto.As<v8::Object>();
86   if (!HasInternalFields(pObj)) {
87     return nullptr;
88   }
89   // Double-check that this was really the global object.
90   CFXJS_PerObjectData* result = ExtractFromObject(pObj);
91   return result->m_ObjType == FXJSOBJTYPE_GLOBAL ? result : nullptr;
92 }
93 
94 //  static
HasInternalFields(v8::Local<v8::Object> pObj)95 bool CFXJS_PerObjectData::HasInternalFields(v8::Local<v8::Object> pObj) {
96   return pObj->InternalFieldCount() == 2 &&
97          pObj->GetAlignedPointerFromInternalField(0) ==
98              GetAlignedPointerForPerObjectDataTag();
99 }
100 
101 //  static
ExtractFromObject(v8::Local<v8::Object> pObj)102 CFXJS_PerObjectData* CFXJS_PerObjectData::ExtractFromObject(
103     v8::Local<v8::Object> pObj) {
104   return static_cast<CFXJS_PerObjectData*>(
105       pObj->GetAlignedPointerFromInternalField(1));
106 }
107 
CFXJS_PerObjectData(FXJSOBJTYPE eObjType,uint32_t nObjDefnID)108 CFXJS_PerObjectData::CFXJS_PerObjectData(FXJSOBJTYPE eObjType,
109                                          uint32_t nObjDefnID)
110     : m_ObjType(eObjType), m_ObjDefnID(nObjDefnID) {}
111 
112 CFXJS_PerObjectData::~CFXJS_PerObjectData() = default;
113 
114 // Global weak map to save dynamic objects.
115 class V8TemplateMapTraits final
116     : public v8::StdMapTraits<CFXJS_PerObjectData*, v8::Object> {
117  public:
118   using WeakCallbackDataType = CFXJS_PerObjectData;
119   using MapType = v8::
120       GlobalValueMap<WeakCallbackDataType*, v8::Object, V8TemplateMapTraits>;
121 
122   static const v8::PersistentContainerCallbackType kCallbackType =
123       v8::kWeakWithInternalFields;
124 
WeakCallbackParameter(MapType * map,WeakCallbackDataType * key,v8::Local<v8::Object> value)125   static WeakCallbackDataType* WeakCallbackParameter(
126       MapType* map,
127       WeakCallbackDataType* key,
128       v8::Local<v8::Object> value) {
129     return key;
130   }
131   static MapType* MapFromWeakCallbackInfo(
132       const v8::WeakCallbackInfo<WeakCallbackDataType>&);
KeyFromWeakCallbackInfo(const v8::WeakCallbackInfo<WeakCallbackDataType> & data)133   static WeakCallbackDataType* KeyFromWeakCallbackInfo(
134       const v8::WeakCallbackInfo<WeakCallbackDataType>& data) {
135     return data.GetParameter();
136   }
OnWeakCallback(const v8::WeakCallbackInfo<WeakCallbackDataType> & data)137   static void OnWeakCallback(
138       const v8::WeakCallbackInfo<WeakCallbackDataType>& data) {}
139   static void DisposeWeak(
140       const v8::WeakCallbackInfo<WeakCallbackDataType>& data);
141   static void Dispose(v8::Isolate* isolate,
142                       v8::Global<v8::Object> value,
143                       WeakCallbackDataType* key);
DisposeCallbackData(WeakCallbackDataType * callbackData)144   static void DisposeCallbackData(WeakCallbackDataType* callbackData) {}
145 };
146 
147 class V8TemplateMap {
148  public:
149   using WeakCallbackDataType = CFXJS_PerObjectData;
150   using MapType = v8::
151       GlobalValueMap<WeakCallbackDataType*, v8::Object, V8TemplateMapTraits>;
152 
V8TemplateMap(v8::Isolate * isolate)153   explicit V8TemplateMap(v8::Isolate* isolate) : m_map(isolate) {}
154   ~V8TemplateMap() = default;
155 
SetAndMakeWeak(v8::Local<v8::Object> handle)156   void SetAndMakeWeak(v8::Local<v8::Object> handle) {
157     WeakCallbackDataType* key = CFXJS_PerObjectData::GetFromObject(handle);
158     DCHECK(!m_map.Contains(key));
159 
160     // Inserting an object into a GlobalValueMap with the appropriate traits
161     // has the side-effect of making the object weak deep in the guts of V8,
162     // and arranges for it to be cleaned up by the methods in the traits.
163     m_map.Set(key, handle);
164   }
165 
GetMap()166   MapType* GetMap() { return &m_map; }
167 
168  private:
169   MapType m_map;
170 };
171 
172 class CFXJS_ObjDefinition {
173  public:
CFXJS_ObjDefinition(v8::Isolate * isolate,const char * sObjName,FXJSOBJTYPE eObjType,CFXJS_Engine::Constructor pConstructor,CFXJS_Engine::Destructor pDestructor)174   CFXJS_ObjDefinition(v8::Isolate* isolate,
175                       const char* sObjName,
176                       FXJSOBJTYPE eObjType,
177                       CFXJS_Engine::Constructor pConstructor,
178                       CFXJS_Engine::Destructor pDestructor)
179       : m_ObjName(sObjName),
180         m_ObjType(eObjType),
181         m_pConstructor(pConstructor),
182         m_pDestructor(pDestructor),
183         m_pIsolate(isolate) {
184     v8::Isolate::Scope isolate_scope(isolate);
185     v8::HandleScope handle_scope(isolate);
186     v8::Local<v8::FunctionTemplate> fn = v8::FunctionTemplate::New(isolate);
187     fn->InstanceTemplate()->SetInternalFieldCount(2);
188     fn->InstanceTemplate()->SetImmutableProto();
189     fn->SetCallHandler(CallHandler, v8::Number::New(isolate, eObjType));
190     if (eObjType == FXJSOBJTYPE_GLOBAL) {
191       fn->InstanceTemplate()->Set(v8::Symbol::GetToStringTag(isolate),
192                                   fxv8::NewStringHelper(isolate, "global"));
193     }
194     m_FunctionTemplate.Reset(isolate, fn);
195     m_Signature.Reset(isolate, v8::Signature::New(isolate, fn));
196   }
197 
CallHandler(const v8::FunctionCallbackInfo<v8::Value> & info)198   static void CallHandler(const v8::FunctionCallbackInfo<v8::Value>& info) {
199     v8::Isolate* isolate = info.GetIsolate();
200     if (!info.IsConstructCall()) {
201       fxv8::ThrowExceptionHelper(isolate, "illegal constructor");
202       return;
203     }
204     if (info.Data().As<v8::Int32>()->Value() != FXJSOBJTYPE_DYNAMIC) {
205       fxv8::ThrowExceptionHelper(isolate, "not a dynamic object");
206       return;
207     }
208     v8::Local<v8::Object> holder = info.This();
209     DCHECK_EQ(holder->InternalFieldCount(), 2);
210     holder->SetAlignedPointerInInternalField(0, nullptr);
211     holder->SetAlignedPointerInInternalField(1, nullptr);
212   }
213 
GetObjType() const214   FXJSOBJTYPE GetObjType() const { return m_ObjType; }
GetObjName() const215   const char* GetObjName() const { return m_ObjName; }
GetIsolate() const216   v8::Isolate* GetIsolate() const { return m_pIsolate; }
217 
DefineConst(const char * sConstName,v8::Local<v8::Value> pDefault)218   void DefineConst(const char* sConstName, v8::Local<v8::Value> pDefault) {
219     GetInstanceTemplate()->Set(GetIsolate(), sConstName, pDefault);
220   }
221 
DefineProperty(v8::Local<v8::String> sPropName,v8::AccessorNameGetterCallback pPropGet,v8::AccessorNameSetterCallback pPropPut)222   void DefineProperty(v8::Local<v8::String> sPropName,
223                       v8::AccessorNameGetterCallback pPropGet,
224                       v8::AccessorNameSetterCallback pPropPut) {
225     GetInstanceTemplate()->SetNativeDataProperty(sPropName, pPropGet, pPropPut);
226   }
227 
DefineMethod(v8::Local<v8::String> sMethodName,v8::FunctionCallback pMethodCall)228   void DefineMethod(v8::Local<v8::String> sMethodName,
229                     v8::FunctionCallback pMethodCall) {
230     v8::Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(
231         GetIsolate(), pMethodCall, v8::Local<v8::Value>(), GetSignature());
232     fun->RemovePrototype();
233     GetInstanceTemplate()->Set(sMethodName, fun, v8::ReadOnly);
234   }
235 
DefineAllProperties(v8::NamedPropertyQueryCallback pPropQurey,v8::NamedPropertyGetterCallback pPropGet,v8::NamedPropertySetterCallback pPropPut,v8::NamedPropertyDeleterCallback pPropDel,v8::NamedPropertyEnumeratorCallback pPropEnum)236   void DefineAllProperties(v8::NamedPropertyQueryCallback pPropQurey,
237                            v8::NamedPropertyGetterCallback pPropGet,
238                            v8::NamedPropertySetterCallback pPropPut,
239                            v8::NamedPropertyDeleterCallback pPropDel,
240                            v8::NamedPropertyEnumeratorCallback pPropEnum) {
241     GetInstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
242         pPropGet, pPropPut, pPropQurey, pPropDel, pPropEnum,
243         v8::Local<v8::Value>(),
244         v8::PropertyHandlerFlags::kOnlyInterceptStrings));
245   }
246 
GetInstanceTemplate()247   v8::Local<v8::ObjectTemplate> GetInstanceTemplate() {
248     v8::EscapableHandleScope scope(GetIsolate());
249     v8::Local<v8::FunctionTemplate> function =
250         m_FunctionTemplate.Get(GetIsolate());
251     return scope.Escape(function->InstanceTemplate());
252   }
253 
GetSignature()254   v8::Local<v8::Signature> GetSignature() {
255     v8::EscapableHandleScope scope(GetIsolate());
256     return scope.Escape(m_Signature.Get(GetIsolate()));
257   }
258 
RunConstructor(CFXJS_Engine * pEngine,v8::Local<v8::Object> obj,v8::Local<v8::Object> proxy)259   void RunConstructor(CFXJS_Engine* pEngine,
260                       v8::Local<v8::Object> obj,
261                       v8::Local<v8::Object> proxy) {
262     if (m_pConstructor)
263       m_pConstructor(pEngine, obj, proxy);
264   }
265 
RunDestructor(v8::Local<v8::Object> obj)266   void RunDestructor(v8::Local<v8::Object> obj) {
267     if (m_pDestructor)
268       m_pDestructor(obj);
269   }
270 
271  private:
272   UnownedPtr<const char> const m_ObjName;
273   const FXJSOBJTYPE m_ObjType;
274   const CFXJS_Engine::Constructor m_pConstructor;
275   const CFXJS_Engine::Destructor m_pDestructor;
276   UnownedPtr<v8::Isolate> m_pIsolate;
277   v8::Global<v8::FunctionTemplate> m_FunctionTemplate;
278   v8::Global<v8::Signature> m_Signature;
279 };
280 
GetGlobalObjectTemplate(v8::Isolate * pIsolate)281 static v8::Local<v8::ObjectTemplate> GetGlobalObjectTemplate(
282     v8::Isolate* pIsolate) {
283   CFXJS_PerIsolateData* pIsolateData = CFXJS_PerIsolateData::Get(pIsolate);
284   for (uint32_t i = 1; i <= pIsolateData->CurrentMaxObjDefinitionID(); ++i) {
285     CFXJS_ObjDefinition* pObjDef = pIsolateData->ObjDefinitionForID(i);
286     if (pObjDef->GetObjType() == FXJSOBJTYPE_GLOBAL)
287       return pObjDef->GetInstanceTemplate();
288   }
289   if (!g_DefaultGlobalObjectTemplate) {
290     v8::Local<v8::ObjectTemplate> hGlobalTemplate =
291         v8::ObjectTemplate::New(pIsolate);
292     hGlobalTemplate->Set(v8::Symbol::GetToStringTag(pIsolate),
293                          fxv8::NewStringHelper(pIsolate, "global"));
294     g_DefaultGlobalObjectTemplate =
295         new v8::Global<v8::ObjectTemplate>(pIsolate, hGlobalTemplate);
296   }
297   return g_DefaultGlobalObjectTemplate->Get(pIsolate);
298 }
299 
Dispose(v8::Isolate * isolate,v8::Global<v8::Object> value,WeakCallbackDataType * key)300 void V8TemplateMapTraits::Dispose(v8::Isolate* isolate,
301                                   v8::Global<v8::Object> value,
302                                   WeakCallbackDataType* key) {
303   v8::Local<v8::Object> obj = value.Get(isolate);
304   if (obj.IsEmpty())
305     return;
306   uint32_t id = CFXJS_Engine::GetObjDefnID(obj);
307   if (id == 0)
308     return;
309   CFXJS_PerIsolateData* pIsolateData = CFXJS_PerIsolateData::Get(isolate);
310   CFXJS_ObjDefinition* pObjDef = pIsolateData->ObjDefinitionForID(id);
311   if (!pObjDef)
312     return;
313   pObjDef->RunDestructor(obj);
314   CFXJS_Engine::FreePerObjectData(obj);
315 }
316 
DisposeWeak(const v8::WeakCallbackInfo<WeakCallbackDataType> & data)317 void V8TemplateMapTraits::DisposeWeak(
318     const v8::WeakCallbackInfo<WeakCallbackDataType>& data) {
319   // TODO(tsepez): this is expected be called during GC.
320 }
321 
MapFromWeakCallbackInfo(const v8::WeakCallbackInfo<WeakCallbackDataType> & info)322 V8TemplateMapTraits::MapType* V8TemplateMapTraits::MapFromWeakCallbackInfo(
323     const v8::WeakCallbackInfo<WeakCallbackDataType>& info) {
324   auto* pIsolateData = CFXJS_PerIsolateData::Get(info.GetIsolate());
325   V8TemplateMap* pObjsMap = pIsolateData->GetDynamicObjsMap();
326   return pObjsMap ? pObjsMap->GetMap() : nullptr;
327 }
328 
FXJS_Initialize(unsigned int embedderDataSlot,v8::Isolate * pIsolate)329 void FXJS_Initialize(unsigned int embedderDataSlot, v8::Isolate* pIsolate) {
330   if (g_isolate) {
331     DCHECK_EQ(g_embedderDataSlot, embedderDataSlot);
332     DCHECK_EQ(g_isolate, pIsolate);
333     return;
334   }
335   g_embedderDataSlot = embedderDataSlot;
336   g_isolate = pIsolate;
337 }
338 
FXJS_Release()339 void FXJS_Release() {
340   DCHECK(!g_isolate || g_isolate_ref_count == 0);
341   delete g_DefaultGlobalObjectTemplate;
342   g_DefaultGlobalObjectTemplate = nullptr;
343   g_isolate = nullptr;
344 
345   delete g_arrayBufferAllocator;
346   g_arrayBufferAllocator = nullptr;
347 }
348 
FXJS_GetIsolate(v8::Isolate ** pResultIsolate)349 bool FXJS_GetIsolate(v8::Isolate** pResultIsolate) {
350   if (g_isolate) {
351     *pResultIsolate = g_isolate;
352     return false;
353   }
354   // Provide backwards compatibility when no external isolate.
355   if (!g_arrayBufferAllocator)
356     g_arrayBufferAllocator = new CFX_V8ArrayBufferAllocator();
357   v8::Isolate::CreateParams params;
358   params.array_buffer_allocator = g_arrayBufferAllocator;
359   *pResultIsolate = v8::Isolate::New(params);
360   return true;
361 }
362 
FXJS_GlobalIsolateRefCount()363 size_t FXJS_GlobalIsolateRefCount() {
364   return g_isolate_ref_count;
365 }
366 
367 // static
SetUp(v8::Isolate * pIsolate)368 void CFXJS_PerIsolateData::SetUp(v8::Isolate* pIsolate) {
369   if (!pIsolate->GetData(g_embedderDataSlot))
370     pIsolate->SetData(g_embedderDataSlot, new CFXJS_PerIsolateData(pIsolate));
371 }
372 
373 // static
Get(v8::Isolate * pIsolate)374 CFXJS_PerIsolateData* CFXJS_PerIsolateData::Get(v8::Isolate* pIsolate) {
375   auto* result =
376       static_cast<CFXJS_PerIsolateData*>(pIsolate->GetData(g_embedderDataSlot));
377   CHECK(result->m_Tag == kPerIsolateDataTag);
378   return result;
379 }
380 
CFXJS_PerIsolateData(v8::Isolate * pIsolate)381 CFXJS_PerIsolateData::CFXJS_PerIsolateData(v8::Isolate* pIsolate)
382     : m_Tag(kPerIsolateDataTag),
383       m_pDynamicObjsMap(std::make_unique<V8TemplateMap>(pIsolate)) {}
384 
385 CFXJS_PerIsolateData::~CFXJS_PerIsolateData() = default;
386 
CurrentMaxObjDefinitionID() const387 uint32_t CFXJS_PerIsolateData::CurrentMaxObjDefinitionID() const {
388   return fxcrt::CollectionSize<uint32_t>(m_ObjectDefnArray);
389 }
390 
ObjDefinitionForID(uint32_t id) const391 CFXJS_ObjDefinition* CFXJS_PerIsolateData::ObjDefinitionForID(
392     uint32_t id) const {
393   return id > 0 && id <= CurrentMaxObjDefinitionID()
394              ? m_ObjectDefnArray[id - 1].get()
395              : nullptr;
396 }
397 
AssignIDForObjDefinition(std::unique_ptr<CFXJS_ObjDefinition> pDefn)398 uint32_t CFXJS_PerIsolateData::AssignIDForObjDefinition(
399     std::unique_ptr<CFXJS_ObjDefinition> pDefn) {
400   m_ObjectDefnArray.push_back(std::move(pDefn));
401   return CurrentMaxObjDefinitionID();
402 }
403 
CFXJS_Engine()404 CFXJS_Engine::CFXJS_Engine() : CFX_V8(nullptr) {}
405 
CFXJS_Engine(v8::Isolate * pIsolate)406 CFXJS_Engine::CFXJS_Engine(v8::Isolate* pIsolate) : CFX_V8(pIsolate) {}
407 
408 CFXJS_Engine::~CFXJS_Engine() = default;
409 
410 // static
GetObjDefnID(v8::Local<v8::Object> pObj)411 uint32_t CFXJS_Engine::GetObjDefnID(v8::Local<v8::Object> pObj) {
412   CFXJS_PerObjectData* pData = CFXJS_PerObjectData::GetFromObject(pObj);
413   return pData ? pData->GetObjDefnID() : 0;
414 }
415 
416 // static
SetBinding(v8::Local<v8::Object> pObj,std::unique_ptr<CFXJS_PerObjectData::Binding> p)417 void CFXJS_Engine::SetBinding(v8::Local<v8::Object> pObj,
418                               std::unique_ptr<CFXJS_PerObjectData::Binding> p) {
419   CFXJS_PerObjectData* pPerObjectData =
420       CFXJS_PerObjectData::GetFromObject(pObj);
421   if (pPerObjectData) {
422     pPerObjectData->SetBinding(std::move(p));
423   }
424 }
425 
426 // static
FreePerObjectData(v8::Local<v8::Object> pObj)427 void CFXJS_Engine::FreePerObjectData(v8::Local<v8::Object> pObj) {
428   CFXJS_PerObjectData* pData = CFXJS_PerObjectData::GetFromObject(pObj);
429   pObj->SetAlignedPointerInInternalField(0, nullptr);
430   pObj->SetAlignedPointerInInternalField(1, nullptr);
431   delete pData;
432 }
433 
DefineObj(const char * sObjName,FXJSOBJTYPE eObjType,CFXJS_Engine::Constructor pConstructor,CFXJS_Engine::Destructor pDestructor)434 uint32_t CFXJS_Engine::DefineObj(const char* sObjName,
435                                  FXJSOBJTYPE eObjType,
436                                  CFXJS_Engine::Constructor pConstructor,
437                                  CFXJS_Engine::Destructor pDestructor) {
438   v8::Isolate::Scope isolate_scope(GetIsolate());
439   v8::HandleScope handle_scope(GetIsolate());
440   CFXJS_PerIsolateData::SetUp(GetIsolate());
441   CFXJS_PerIsolateData* pIsolateData = CFXJS_PerIsolateData::Get(GetIsolate());
442   return pIsolateData->AssignIDForObjDefinition(
443       std::make_unique<CFXJS_ObjDefinition>(GetIsolate(), sObjName, eObjType,
444                                             pConstructor, pDestructor));
445 }
446 
DefineObjMethod(uint32_t nObjDefnID,const char * sMethodName,v8::FunctionCallback pMethodCall)447 void CFXJS_Engine::DefineObjMethod(uint32_t nObjDefnID,
448                                    const char* sMethodName,
449                                    v8::FunctionCallback pMethodCall) {
450   v8::Isolate::Scope isolate_scope(GetIsolate());
451   v8::HandleScope handle_scope(GetIsolate());
452   CFXJS_PerIsolateData* pIsolateData = CFXJS_PerIsolateData::Get(GetIsolate());
453   CFXJS_ObjDefinition* pObjDef = pIsolateData->ObjDefinitionForID(nObjDefnID);
454   pObjDef->DefineMethod(NewString(sMethodName), pMethodCall);
455 }
456 
DefineObjProperty(uint32_t nObjDefnID,const char * sPropName,v8::AccessorNameGetterCallback pPropGet,v8::AccessorNameSetterCallback pPropPut)457 void CFXJS_Engine::DefineObjProperty(uint32_t nObjDefnID,
458                                      const char* sPropName,
459                                      v8::AccessorNameGetterCallback pPropGet,
460                                      v8::AccessorNameSetterCallback pPropPut) {
461   v8::Isolate::Scope isolate_scope(GetIsolate());
462   v8::HandleScope handle_scope(GetIsolate());
463   CFXJS_PerIsolateData* pIsolateData = CFXJS_PerIsolateData::Get(GetIsolate());
464   CFXJS_ObjDefinition* pObjDef = pIsolateData->ObjDefinitionForID(nObjDefnID);
465   pObjDef->DefineProperty(NewString(sPropName), pPropGet, pPropPut);
466 }
467 
DefineObjAllProperties(uint32_t nObjDefnID,v8::NamedPropertyQueryCallback pPropQurey,v8::NamedPropertyGetterCallback pPropGet,v8::NamedPropertySetterCallback pPropPut,v8::NamedPropertyDeleterCallback pPropDel,v8::NamedPropertyEnumeratorCallback pPropEnum)468 void CFXJS_Engine::DefineObjAllProperties(
469     uint32_t nObjDefnID,
470     v8::NamedPropertyQueryCallback pPropQurey,
471     v8::NamedPropertyGetterCallback pPropGet,
472     v8::NamedPropertySetterCallback pPropPut,
473     v8::NamedPropertyDeleterCallback pPropDel,
474     v8::NamedPropertyEnumeratorCallback pPropEnum) {
475   v8::Isolate::Scope isolate_scope(GetIsolate());
476   v8::HandleScope handle_scope(GetIsolate());
477   CFXJS_PerIsolateData* pIsolateData = CFXJS_PerIsolateData::Get(GetIsolate());
478   CFXJS_ObjDefinition* pObjDef = pIsolateData->ObjDefinitionForID(nObjDefnID);
479   pObjDef->DefineAllProperties(pPropQurey, pPropGet, pPropPut, pPropDel,
480                                pPropEnum);
481 }
482 
DefineObjConst(uint32_t nObjDefnID,const char * sConstName,v8::Local<v8::Value> pDefault)483 void CFXJS_Engine::DefineObjConst(uint32_t nObjDefnID,
484                                   const char* sConstName,
485                                   v8::Local<v8::Value> pDefault) {
486   v8::Isolate::Scope isolate_scope(GetIsolate());
487   v8::HandleScope handle_scope(GetIsolate());
488   CFXJS_PerIsolateData* pIsolateData = CFXJS_PerIsolateData::Get(GetIsolate());
489   CFXJS_ObjDefinition* pObjDef = pIsolateData->ObjDefinitionForID(nObjDefnID);
490   pObjDef->DefineConst(sConstName, pDefault);
491 }
492 
DefineGlobalMethod(const char * sMethodName,v8::FunctionCallback pMethodCall)493 void CFXJS_Engine::DefineGlobalMethod(const char* sMethodName,
494                                       v8::FunctionCallback pMethodCall) {
495   v8::Isolate::Scope isolate_scope(GetIsolate());
496   v8::HandleScope handle_scope(GetIsolate());
497   v8::Local<v8::FunctionTemplate> fun =
498       v8::FunctionTemplate::New(GetIsolate(), pMethodCall);
499   fun->RemovePrototype();
500   GetGlobalObjectTemplate(GetIsolate())
501       ->Set(NewString(sMethodName), fun, v8::ReadOnly);
502 }
503 
DefineGlobalConst(const wchar_t * sConstName,v8::FunctionCallback pConstGetter)504 void CFXJS_Engine::DefineGlobalConst(const wchar_t* sConstName,
505                                      v8::FunctionCallback pConstGetter) {
506   v8::Isolate::Scope isolate_scope(GetIsolate());
507   v8::HandleScope handle_scope(GetIsolate());
508   v8::Local<v8::FunctionTemplate> fun =
509       v8::FunctionTemplate::New(GetIsolate(), pConstGetter);
510   fun->RemovePrototype();
511   GetGlobalObjectTemplate(GetIsolate())
512       ->SetAccessorProperty(NewString(sConstName), fun);
513 }
514 
InitializeEngine()515 void CFXJS_Engine::InitializeEngine() {
516   if (GetIsolate() == g_isolate)
517     ++g_isolate_ref_count;
518 
519   v8::Isolate::Scope isolate_scope(GetIsolate());
520   v8::HandleScope handle_scope(GetIsolate());
521 
522   // This has to happen before we call GetGlobalObjectTemplate because that
523   // method gets the PerIsolateData from GetIsolate().
524   CFXJS_PerIsolateData::SetUp(GetIsolate());
525 
526   v8::Local<v8::Context> v8Context = v8::Context::New(
527       GetIsolate(), nullptr, GetGlobalObjectTemplate(GetIsolate()));
528 
529   // May not have the internal fields when called from tests, so clear these
530   // in case we don't process a FXJSOBJTYPE_GLOBAL below.
531   v8::Local<v8::Object> pThisProxy = v8Context->Global();
532   if (pThisProxy->InternalFieldCount() == 2) {
533     pThisProxy->SetAlignedPointerInInternalField(0, nullptr);
534     pThisProxy->SetAlignedPointerInInternalField(1, nullptr);
535   }
536   v8::Local<v8::Object> pThis = pThisProxy->GetPrototype().As<v8::Object>();
537   if (pThis->InternalFieldCount() == 2) {
538     pThis->SetAlignedPointerInInternalField(0, nullptr);
539     pThis->SetAlignedPointerInInternalField(1, nullptr);
540   }
541 
542   v8::Context::Scope context_scope(v8Context);
543   CFXJS_PerIsolateData* pIsolateData = CFXJS_PerIsolateData::Get(GetIsolate());
544   uint32_t maxID = pIsolateData->CurrentMaxObjDefinitionID();
545   m_StaticObjects.resize(maxID + 1);
546   for (uint32_t i = 1; i <= maxID; ++i) {
547     CFXJS_ObjDefinition* pObjDef = pIsolateData->ObjDefinitionForID(i);
548     if (pObjDef->GetObjType() == FXJSOBJTYPE_GLOBAL) {
549       CFXJS_PerObjectData::SetNewDataInObject(FXJSOBJTYPE_GLOBAL, i, pThis);
550       pObjDef->RunConstructor(this, pThis, pThisProxy);
551     } else if (pObjDef->GetObjType() == FXJSOBJTYPE_STATIC) {
552       v8::Local<v8::String> pObjName = NewString(pObjDef->GetObjName());
553       v8::Local<v8::Object> obj = NewFXJSBoundObject(i, FXJSOBJTYPE_STATIC);
554       if (!obj.IsEmpty()) {
555         v8Context->Global()->Set(v8Context, pObjName, obj).FromJust();
556         m_StaticObjects[i] = v8::Global<v8::Object>(GetIsolate(), obj);
557       }
558     }
559   }
560 
561   m_V8Context.Reset(GetIsolate(), v8Context);
562 }
563 
ReleaseEngine()564 void CFXJS_Engine::ReleaseEngine() {
565   v8::Isolate::Scope isolate_scope(GetIsolate());
566   v8::HandleScope handle_scope(GetIsolate());
567   v8::Local<v8::Context> context = GetV8Context();
568   v8::Context::Scope context_scope(context);
569   CFXJS_PerIsolateData* pIsolateData = CFXJS_PerIsolateData::Get(GetIsolate());
570   if (!pIsolateData)
571     return;
572 
573   m_ConstArrays.clear();
574 
575   for (uint32_t i = 1; i <= pIsolateData->CurrentMaxObjDefinitionID(); ++i) {
576     CFXJS_ObjDefinition* pObjDef = pIsolateData->ObjDefinitionForID(i);
577     v8::Local<v8::Object> pObj;
578     if (pObjDef->GetObjType() == FXJSOBJTYPE_GLOBAL) {
579       pObj =
580           context->Global()->GetPrototype()->ToObject(context).ToLocalChecked();
581     } else if (!m_StaticObjects[i].IsEmpty()) {
582       pObj = v8::Local<v8::Object>::New(GetIsolate(), m_StaticObjects[i]);
583       m_StaticObjects[i].Reset();
584     }
585     if (!pObj.IsEmpty()) {
586       pObjDef->RunDestructor(pObj);
587       FreePerObjectData(pObj);
588     }
589   }
590 
591   m_V8Context.Reset();
592 
593   if (GetIsolate() == g_isolate && --g_isolate_ref_count > 0)
594     return;
595 
596   delete pIsolateData;
597   GetIsolate()->SetData(g_embedderDataSlot, nullptr);
598 }
599 
Execute(const WideString & script)600 std::optional<IJS_Runtime::JS_Error> CFXJS_Engine::Execute(
601     const WideString& script) {
602   v8::Isolate::Scope isolate_scope(GetIsolate());
603   v8::TryCatch try_catch(GetIsolate());
604   v8::Local<v8::Context> context = GetIsolate()->GetCurrentContext();
605   v8::Local<v8::Script> compiled_script;
606   if (!v8::Script::Compile(context, NewString(script.AsStringView()))
607            .ToLocal(&compiled_script)) {
608     v8::String::Utf8Value error(GetIsolate(), try_catch.Exception());
609     v8::Local<v8::Message> msg = try_catch.Message();
610     auto [line, column] = GetLineAndColumnFromError(msg, context);
611     return IJS_Runtime::JS_Error(line, column, WideString::FromUTF8(*error));
612   }
613 
614   v8::Local<v8::Value> result;
615   if (!compiled_script->Run(context).ToLocal(&result)) {
616     v8::String::Utf8Value error(GetIsolate(), try_catch.Exception());
617     auto msg = try_catch.Message();
618     auto [line, column] = GetLineAndColumnFromError(msg, context);
619     return IJS_Runtime::JS_Error(line, column, WideString::FromUTF8(*error));
620   }
621   return std::nullopt;
622 }
623 
NewFXJSBoundObject(uint32_t nObjDefnID,FXJSOBJTYPE type)624 v8::Local<v8::Object> CFXJS_Engine::NewFXJSBoundObject(uint32_t nObjDefnID,
625                                                        FXJSOBJTYPE type) {
626   v8::Isolate::Scope isolate_scope(GetIsolate());
627   v8::Local<v8::Context> context = GetIsolate()->GetCurrentContext();
628   CFXJS_PerIsolateData* pData = CFXJS_PerIsolateData::Get(GetIsolate());
629   if (!pData)
630     return v8::Local<v8::Object>();
631 
632   CFXJS_ObjDefinition* pObjDef = pData->ObjDefinitionForID(nObjDefnID);
633   if (!pObjDef)
634     return v8::Local<v8::Object>();
635 
636   v8::Local<v8::Object> obj;
637   if (!pObjDef->GetInstanceTemplate()->NewInstance(context).ToLocal(&obj))
638     return v8::Local<v8::Object>();
639 
640   CFXJS_PerObjectData::SetNewDataInObject(type, nObjDefnID, obj);
641   pObjDef->RunConstructor(this, obj, obj);
642   if (type == FXJSOBJTYPE_DYNAMIC) {
643     auto* pIsolateData = CFXJS_PerIsolateData::Get(GetIsolate());
644     V8TemplateMap* pObjsMap = pIsolateData->GetDynamicObjsMap();
645     if (pObjsMap)
646       pObjsMap->SetAndMakeWeak(obj);
647   }
648   return obj;
649 }
650 
GetThisObj()651 v8::Local<v8::Object> CFXJS_Engine::GetThisObj() {
652   v8::Isolate::Scope isolate_scope(GetIsolate());
653   if (!CFXJS_PerIsolateData::Get(GetIsolate())) {
654     return v8::Local<v8::Object>();
655   }
656 
657   // Return the global object.
658   v8::Local<v8::Context> context = GetIsolate()->GetCurrentContext();
659   return context->Global()->GetPrototype()->ToObject(context).ToLocalChecked();
660 }
661 
Error(const WideString & message)662 void CFXJS_Engine::Error(const WideString& message) {
663   fxv8::ThrowExceptionHelper(GetIsolate(), message.AsStringView());
664 }
665 
GetV8Context()666 v8::Local<v8::Context> CFXJS_Engine::GetV8Context() {
667   return v8::Local<v8::Context>::New(GetIsolate(), m_V8Context);
668 }
669 
670 // static
GetBinding(v8::Isolate * pIsolate,v8::Local<v8::Object> pObj)671 CFXJS_PerObjectData::Binding* CFXJS_Engine::GetBinding(
672     v8::Isolate* pIsolate,
673     v8::Local<v8::Object> pObj) {
674   auto* pData = CFXJS_PerObjectData::GetFromObject(pObj);
675   return pData ? pData->GetBinding() : nullptr;
676 }
677 
GetConstArray(const WideString & name)678 v8::Local<v8::Array> CFXJS_Engine::GetConstArray(const WideString& name) {
679   return v8::Local<v8::Array>::New(GetIsolate(), m_ConstArrays[name]);
680 }
681 
SetConstArray(const WideString & name,v8::Local<v8::Array> array)682 void CFXJS_Engine::SetConstArray(const WideString& name,
683                                  v8::Local<v8::Array> array) {
684   m_ConstArrays[name] = v8::Global<v8::Array>(GetIsolate(), array);
685 }
686