blob: c2c8e5fec22268a19dd2ad279c7336c1bb474220 [file] [log] [blame]
dsinclair08fea802016-07-12 10:37:52 -07001// 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
dsinclair43554682016-09-29 17:29:48 -07007#include "fxjs/cfxjse_class.h"
dsinclair08fea802016-07-12 10:37:52 -07008
dsinclair43554682016-09-29 17:29:48 -07009#include "fxjs/cfxjse_context.h"
10#include "fxjs/cfxjse_value.h"
dsinclair08fea802016-07-12 10:37:52 -070011
12namespace {
13
14void V8FunctionCallback_Wrapper(
15 const v8::FunctionCallbackInfo<v8::Value>& info) {
16 const FXJSE_FUNCTION_DESCRIPTOR* lpFunctionInfo =
17 static_cast<FXJSE_FUNCTION_DESCRIPTOR*>(
18 info.Data().As<v8::External>()->Value());
19 if (!lpFunctionInfo)
20 return;
21
22 CFX_ByteStringC szFunctionName(lpFunctionInfo->name);
23 std::unique_ptr<CFXJSE_Value> lpThisValue(
24 new CFXJSE_Value(info.GetIsolate()));
25 lpThisValue->ForceSetValue(info.This());
26 std::unique_ptr<CFXJSE_Value> lpRetValue(new CFXJSE_Value(info.GetIsolate()));
27 CFXJSE_Arguments impl(&info, lpRetValue.get());
28 lpFunctionInfo->callbackProc(lpThisValue.get(), szFunctionName, impl);
29 if (!lpRetValue->DirectGetValue().IsEmpty())
30 info.GetReturnValue().Set(lpRetValue->DirectGetValue());
31}
32
33void V8ClassGlobalConstructorCallback_Wrapper(
34 const v8::FunctionCallbackInfo<v8::Value>& info) {
35 const FXJSE_CLASS_DESCRIPTOR* lpClassDefinition =
36 static_cast<FXJSE_CLASS_DESCRIPTOR*>(
37 info.Data().As<v8::External>()->Value());
38 if (!lpClassDefinition)
39 return;
40
41 CFX_ByteStringC szFunctionName(lpClassDefinition->name);
42 std::unique_ptr<CFXJSE_Value> lpThisValue(
43 new CFXJSE_Value(info.GetIsolate()));
44 lpThisValue->ForceSetValue(info.This());
45 std::unique_ptr<CFXJSE_Value> lpRetValue(new CFXJSE_Value(info.GetIsolate()));
46 CFXJSE_Arguments impl(&info, lpRetValue.get());
47 lpClassDefinition->constructor(lpThisValue.get(), szFunctionName, impl);
48 if (!lpRetValue->DirectGetValue().IsEmpty())
49 info.GetReturnValue().Set(lpRetValue->DirectGetValue());
50}
51
52void V8GetterCallback_Wrapper(v8::Local<v8::String> property,
53 const v8::PropertyCallbackInfo<v8::Value>& info) {
54 const FXJSE_PROPERTY_DESCRIPTOR* lpPropertyInfo =
55 static_cast<FXJSE_PROPERTY_DESCRIPTOR*>(
56 info.Data().As<v8::External>()->Value());
57 if (!lpPropertyInfo)
58 return;
59
60 CFX_ByteStringC szPropertyName(lpPropertyInfo->name);
61 std::unique_ptr<CFXJSE_Value> lpThisValue(
62 new CFXJSE_Value(info.GetIsolate()));
63 std::unique_ptr<CFXJSE_Value> lpPropValue(
64 new CFXJSE_Value(info.GetIsolate()));
65 lpThisValue->ForceSetValue(info.This());
66 lpPropertyInfo->getProc(lpThisValue.get(), szPropertyName, lpPropValue.get());
67 info.GetReturnValue().Set(lpPropValue->DirectGetValue());
68}
69
70void V8SetterCallback_Wrapper(v8::Local<v8::String> property,
71 v8::Local<v8::Value> value,
72 const v8::PropertyCallbackInfo<void>& info) {
73 const FXJSE_PROPERTY_DESCRIPTOR* lpPropertyInfo =
74 static_cast<FXJSE_PROPERTY_DESCRIPTOR*>(
75 info.Data().As<v8::External>()->Value());
76 if (!lpPropertyInfo)
77 return;
78
79 CFX_ByteStringC szPropertyName(lpPropertyInfo->name);
80 std::unique_ptr<CFXJSE_Value> lpThisValue(
81 new CFXJSE_Value(info.GetIsolate()));
82 std::unique_ptr<CFXJSE_Value> lpPropValue(
83 new CFXJSE_Value(info.GetIsolate()));
84 lpThisValue->ForceSetValue(info.This());
85 lpPropValue->ForceSetValue(value);
86 lpPropertyInfo->setProc(lpThisValue.get(), szPropertyName, lpPropValue.get());
87}
88
89void V8ConstructorCallback_Wrapper(
90 const v8::FunctionCallbackInfo<v8::Value>& info) {
91 if (!info.IsConstructCall())
92 return;
93
94 const FXJSE_CLASS_DESCRIPTOR* lpClassDefinition =
95 static_cast<FXJSE_CLASS_DESCRIPTOR*>(
96 info.Data().As<v8::External>()->Value());
97 if (!lpClassDefinition)
98 return;
99
100 ASSERT(info.This()->InternalFieldCount());
101 info.This()->SetAlignedPointerInInternalField(0, nullptr);
102}
103
104void Context_GlobalObjToString(
105 const v8::FunctionCallbackInfo<v8::Value>& info) {
106 const FXJSE_CLASS_DESCRIPTOR* lpClass = static_cast<FXJSE_CLASS_DESCRIPTOR*>(
107 info.Data().As<v8::External>()->Value());
108 if (!lpClass)
109 return;
110
111 if (info.This() == info.Holder() && lpClass->name) {
112 CFX_ByteString szStringVal;
113 szStringVal.Format("[object %s]", lpClass->name);
114 info.GetReturnValue().Set(v8::String::NewFromUtf8(
115 info.GetIsolate(), szStringVal.c_str(), v8::String::kNormalString,
116 szStringVal.GetLength()));
117 return;
118 }
119 v8::Local<v8::String> local_str =
120 info.This()
121 ->ObjectProtoToString(info.GetIsolate()->GetCurrentContext())
122 .FromMaybe(v8::Local<v8::String>());
123 info.GetReturnValue().Set(local_str);
124}
125
126void DynPropGetterAdapter_MethodCallback(
127 const v8::FunctionCallbackInfo<v8::Value>& info) {
128 v8::Local<v8::Object> hCallBackInfo = info.Data().As<v8::Object>();
129 FXJSE_CLASS_DESCRIPTOR* lpClass = static_cast<FXJSE_CLASS_DESCRIPTOR*>(
130 hCallBackInfo->GetAlignedPointerFromInternalField(0));
131 v8::Local<v8::String> hPropName =
132 hCallBackInfo->GetInternalField(1).As<v8::String>();
133 ASSERT(lpClass && !hPropName.IsEmpty());
134 v8::String::Utf8Value szPropName(hPropName);
135 CFX_ByteStringC szFxPropName = *szPropName;
136 std::unique_ptr<CFXJSE_Value> lpThisValue(
137 new CFXJSE_Value(info.GetIsolate()));
138 lpThisValue->ForceSetValue(info.This());
139 std::unique_ptr<CFXJSE_Value> lpRetValue(new CFXJSE_Value(info.GetIsolate()));
140 CFXJSE_Arguments impl(&info, lpRetValue.get());
141 lpClass->dynMethodCall(lpThisValue.get(), szFxPropName, impl);
142 if (!lpRetValue->DirectGetValue().IsEmpty())
143 info.GetReturnValue().Set(lpRetValue->DirectGetValue());
144}
145
146void DynPropGetterAdapter(const FXJSE_CLASS_DESCRIPTOR* lpClass,
147 CFXJSE_Value* pObject,
148 const CFX_ByteStringC& szPropName,
149 CFXJSE_Value* pValue) {
150 ASSERT(lpClass);
151 int32_t nPropType =
152 lpClass->dynPropTypeGetter == nullptr
153 ? FXJSE_ClassPropType_Property
154 : lpClass->dynPropTypeGetter(pObject, szPropName, FALSE);
155 if (nPropType == FXJSE_ClassPropType_Property) {
156 if (lpClass->dynPropGetter)
157 lpClass->dynPropGetter(pObject, szPropName, pValue);
158 } else if (nPropType == FXJSE_ClassPropType_Method) {
159 if (lpClass->dynMethodCall && pValue) {
160 v8::Isolate* pIsolate = pValue->GetIsolate();
161 v8::HandleScope hscope(pIsolate);
162 v8::Local<v8::ObjectTemplate> hCallBackInfoTemplate =
163 v8::ObjectTemplate::New(pIsolate);
164 hCallBackInfoTemplate->SetInternalFieldCount(2);
165 v8::Local<v8::Object> hCallBackInfo =
166 hCallBackInfoTemplate->NewInstance();
167 hCallBackInfo->SetAlignedPointerInInternalField(
168 0, const_cast<FXJSE_CLASS_DESCRIPTOR*>(lpClass));
169 hCallBackInfo->SetInternalField(
170 1, v8::String::NewFromUtf8(
171 pIsolate, reinterpret_cast<const char*>(szPropName.raw_str()),
172 v8::String::kNormalString, szPropName.GetLength()));
173 pValue->ForceSetValue(
174 v8::Function::New(pValue->GetIsolate()->GetCurrentContext(),
175 DynPropGetterAdapter_MethodCallback, hCallBackInfo,
176 0, v8::ConstructorBehavior::kThrow)
177 .ToLocalChecked());
178 }
179 }
180}
181
182void DynPropSetterAdapter(const FXJSE_CLASS_DESCRIPTOR* lpClass,
183 CFXJSE_Value* pObject,
184 const CFX_ByteStringC& szPropName,
185 CFXJSE_Value* pValue) {
186 ASSERT(lpClass);
187 int32_t nPropType =
188 lpClass->dynPropTypeGetter == nullptr
189 ? FXJSE_ClassPropType_Property
190 : lpClass->dynPropTypeGetter(pObject, szPropName, FALSE);
191 if (nPropType != FXJSE_ClassPropType_Method) {
192 if (lpClass->dynPropSetter)
193 lpClass->dynPropSetter(pObject, szPropName, pValue);
194 }
195}
196
197FX_BOOL DynPropQueryAdapter(const FXJSE_CLASS_DESCRIPTOR* lpClass,
198 CFXJSE_Value* pObject,
199 const CFX_ByteStringC& szPropName) {
200 ASSERT(lpClass);
201 int32_t nPropType =
202 lpClass->dynPropTypeGetter == nullptr
203 ? FXJSE_ClassPropType_Property
204 : lpClass->dynPropTypeGetter(pObject, szPropName, TRUE);
205 return nPropType != FXJSE_ClassPropType_None;
206}
207
208FX_BOOL DynPropDeleterAdapter(const FXJSE_CLASS_DESCRIPTOR* lpClass,
209 CFXJSE_Value* pObject,
210 const CFX_ByteStringC& szPropName) {
211 ASSERT(lpClass);
212 int32_t nPropType =
213 lpClass->dynPropTypeGetter == nullptr
214 ? FXJSE_ClassPropType_Property
215 : lpClass->dynPropTypeGetter(pObject, szPropName, FALSE);
216 if (nPropType != FXJSE_ClassPropType_Method) {
217 if (lpClass->dynPropDeleter)
218 return lpClass->dynPropDeleter(pObject, szPropName);
219 return nPropType == FXJSE_ClassPropType_Property ? FALSE : TRUE;
220 }
221 return FALSE;
222}
223
224void NamedPropertyQueryCallback(
225 v8::Local<v8::Name> property,
226 const v8::PropertyCallbackInfo<v8::Integer>& info) {
227 v8::Local<v8::Object> thisObject = info.This();
228 const FXJSE_CLASS_DESCRIPTOR* lpClass = static_cast<FXJSE_CLASS_DESCRIPTOR*>(
229 info.Data().As<v8::External>()->Value());
230 v8::Isolate* pIsolate = info.GetIsolate();
231 v8::HandleScope scope(pIsolate);
232 v8::String::Utf8Value szPropName(property);
233 CFX_ByteStringC szFxPropName(*szPropName, szPropName.length());
234 std::unique_ptr<CFXJSE_Value> lpThisValue(
235 new CFXJSE_Value(info.GetIsolate()));
236 lpThisValue->ForceSetValue(thisObject);
237 if (DynPropQueryAdapter(lpClass, lpThisValue.get(), szFxPropName)) {
238 info.GetReturnValue().Set(v8::DontDelete);
239 return;
240 }
241 const int32_t iV8Absent = 64;
242 info.GetReturnValue().Set(iV8Absent);
243}
244
245void NamedPropertyDeleterCallback(
246 v8::Local<v8::Name> property,
247 const v8::PropertyCallbackInfo<v8::Boolean>& info) {
248 v8::Local<v8::Object> thisObject = info.This();
249 const FXJSE_CLASS_DESCRIPTOR* lpClass = static_cast<FXJSE_CLASS_DESCRIPTOR*>(
250 info.Data().As<v8::External>()->Value());
251 v8::Isolate* pIsolate = info.GetIsolate();
252 v8::HandleScope scope(pIsolate);
253 v8::String::Utf8Value szPropName(property);
254 CFX_ByteStringC szFxPropName(*szPropName, szPropName.length());
255 std::unique_ptr<CFXJSE_Value> lpThisValue(
256 new CFXJSE_Value(info.GetIsolate()));
257 lpThisValue->ForceSetValue(thisObject);
258 info.GetReturnValue().Set(
259 !!DynPropDeleterAdapter(lpClass, lpThisValue.get(), szFxPropName));
260}
261
262void NamedPropertyGetterCallback(
263 v8::Local<v8::Name> property,
264 const v8::PropertyCallbackInfo<v8::Value>& info) {
265 v8::Local<v8::Object> thisObject = info.This();
266 const FXJSE_CLASS_DESCRIPTOR* lpClass = static_cast<FXJSE_CLASS_DESCRIPTOR*>(
267 info.Data().As<v8::External>()->Value());
268 v8::String::Utf8Value szPropName(property);
269 CFX_ByteStringC szFxPropName(*szPropName, szPropName.length());
270 std::unique_ptr<CFXJSE_Value> lpThisValue(
271 new CFXJSE_Value(info.GetIsolate()));
272 lpThisValue->ForceSetValue(thisObject);
273 std::unique_ptr<CFXJSE_Value> lpNewValue(new CFXJSE_Value(info.GetIsolate()));
274 DynPropGetterAdapter(lpClass, lpThisValue.get(), szFxPropName,
275 lpNewValue.get());
276 info.GetReturnValue().Set(lpNewValue->DirectGetValue());
277}
278
279void NamedPropertySetterCallback(
280 v8::Local<v8::Name> property,
281 v8::Local<v8::Value> value,
282 const v8::PropertyCallbackInfo<v8::Value>& info) {
283 v8::Local<v8::Object> thisObject = info.This();
284 const FXJSE_CLASS_DESCRIPTOR* lpClass = static_cast<FXJSE_CLASS_DESCRIPTOR*>(
285 info.Data().As<v8::External>()->Value());
286 v8::String::Utf8Value szPropName(property);
287 CFX_ByteStringC szFxPropName(*szPropName, szPropName.length());
288 std::unique_ptr<CFXJSE_Value> lpThisValue(
289 new CFXJSE_Value(info.GetIsolate()));
290 lpThisValue->ForceSetValue(thisObject);
291
weili1a241992016-09-12 15:10:40 -0700292 std::unique_ptr<CFXJSE_Value> lpNewValue(new CFXJSE_Value(info.GetIsolate()));
dsinclair08fea802016-07-12 10:37:52 -0700293 lpNewValue->ForceSetValue(value);
weili1a241992016-09-12 15:10:40 -0700294 DynPropSetterAdapter(lpClass, lpThisValue.get(), szFxPropName,
295 lpNewValue.get());
dsinclair08fea802016-07-12 10:37:52 -0700296 info.GetReturnValue().Set(value);
297}
298
299void NamedPropertyEnumeratorCallback(
300 const v8::PropertyCallbackInfo<v8::Array>& info) {
301 const FXJSE_CLASS_DESCRIPTOR* lpClass = static_cast<FXJSE_CLASS_DESCRIPTOR*>(
302 info.Data().As<v8::External>()->Value());
303 v8::Isolate* pIsolate = info.GetIsolate();
304 v8::Local<v8::Array> newArray = v8::Array::New(pIsolate, lpClass->propNum);
305 for (int i = 0; i < lpClass->propNum; i++) {
306 newArray->Set(
307 i, v8::String::NewFromUtf8(pIsolate, lpClass->properties[i].name));
308 }
309 info.GetReturnValue().Set(newArray);
310}
311
312} // namespace
313
314// static
315CFXJSE_Class* CFXJSE_Class::Create(
316 CFXJSE_Context* lpContext,
317 const FXJSE_CLASS_DESCRIPTOR* lpClassDefinition,
318 FX_BOOL bIsJSGlobal) {
319 if (!lpContext || !lpClassDefinition)
320 return nullptr;
321
322 CFXJSE_Class* pClass =
323 GetClassFromContext(lpContext, lpClassDefinition->name);
324 if (pClass)
325 return pClass;
326
327 v8::Isolate* pIsolate = lpContext->m_pIsolate;
328 pClass = new CFXJSE_Class(lpContext);
329 pClass->m_szClassName = lpClassDefinition->name;
330 pClass->m_lpClassDefinition = lpClassDefinition;
331 CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
332 v8::Local<v8::FunctionTemplate> hFunctionTemplate = v8::FunctionTemplate::New(
333 pIsolate, bIsJSGlobal ? 0 : V8ConstructorCallback_Wrapper,
334 v8::External::New(
335 pIsolate, const_cast<FXJSE_CLASS_DESCRIPTOR*>(lpClassDefinition)));
336 hFunctionTemplate->SetClassName(
337 v8::String::NewFromUtf8(pIsolate, lpClassDefinition->name));
338 hFunctionTemplate->InstanceTemplate()->SetInternalFieldCount(1);
339 v8::Local<v8::ObjectTemplate> hObjectTemplate =
340 hFunctionTemplate->InstanceTemplate();
341 SetUpNamedPropHandler(pIsolate, hObjectTemplate, lpClassDefinition);
342
343 if (lpClassDefinition->propNum) {
344 for (int32_t i = 0; i < lpClassDefinition->propNum; i++) {
345 hObjectTemplate->SetNativeDataProperty(
346 v8::String::NewFromUtf8(pIsolate,
347 lpClassDefinition->properties[i].name),
348 lpClassDefinition->properties[i].getProc ? V8GetterCallback_Wrapper
349 : nullptr,
350 lpClassDefinition->properties[i].setProc ? V8SetterCallback_Wrapper
351 : nullptr,
352 v8::External::New(pIsolate, const_cast<FXJSE_PROPERTY_DESCRIPTOR*>(
353 lpClassDefinition->properties + i)),
354 static_cast<v8::PropertyAttribute>(v8::DontDelete));
355 }
356 }
357 if (lpClassDefinition->methNum) {
358 for (int32_t i = 0; i < lpClassDefinition->methNum; i++) {
359 v8::Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(
360 pIsolate, V8FunctionCallback_Wrapper,
361 v8::External::New(pIsolate, const_cast<FXJSE_FUNCTION_DESCRIPTOR*>(
362 lpClassDefinition->methods + i)));
363 fun->RemovePrototype();
364 hObjectTemplate->Set(
365 v8::String::NewFromUtf8(pIsolate, lpClassDefinition->methods[i].name),
366 fun,
367 static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete));
368 }
369 }
370 if (lpClassDefinition->constructor) {
371 if (bIsJSGlobal) {
372 hObjectTemplate->Set(
373 v8::String::NewFromUtf8(pIsolate, lpClassDefinition->name),
374 v8::FunctionTemplate::New(
375 pIsolate, V8ClassGlobalConstructorCallback_Wrapper,
376 v8::External::New(pIsolate, const_cast<FXJSE_CLASS_DESCRIPTOR*>(
377 lpClassDefinition))),
378 static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete));
379 } else {
380 v8::Local<v8::Context> hLocalContext =
381 v8::Local<v8::Context>::New(pIsolate, lpContext->m_hContext);
382 FXJSE_GetGlobalObjectFromContext(hLocalContext)
383 ->Set(v8::String::NewFromUtf8(pIsolate, lpClassDefinition->name),
384 v8::Function::New(
385 pIsolate, V8ClassGlobalConstructorCallback_Wrapper,
386 v8::External::New(pIsolate,
387 const_cast<FXJSE_CLASS_DESCRIPTOR*>(
388 lpClassDefinition))));
389 }
390 }
391 if (bIsJSGlobal) {
392 v8::Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(
393 pIsolate, Context_GlobalObjToString,
394 v8::External::New(
395 pIsolate, const_cast<FXJSE_CLASS_DESCRIPTOR*>(lpClassDefinition)));
396 fun->RemovePrototype();
397 hObjectTemplate->Set(v8::String::NewFromUtf8(pIsolate, "toString"), fun);
398 }
399 pClass->m_hTemplate.Reset(lpContext->m_pIsolate, hFunctionTemplate);
400 lpContext->m_rgClasses.push_back(std::unique_ptr<CFXJSE_Class>(pClass));
401 return pClass;
402}
403
404// static
405CFXJSE_Class* CFXJSE_Class::GetClassFromContext(CFXJSE_Context* pContext,
406 const CFX_ByteStringC& szName) {
407 for (const auto& pClass : pContext->m_rgClasses) {
408 if (pClass->m_szClassName == szName)
409 return pClass.get();
410 }
411 return nullptr;
412}
413
414// static
415void CFXJSE_Class::SetUpNamedPropHandler(
416 v8::Isolate* pIsolate,
417 v8::Local<v8::ObjectTemplate>& hObjectTemplate,
418 const FXJSE_CLASS_DESCRIPTOR* lpClassDefinition) {
419 v8::NamedPropertyHandlerConfiguration configuration(
420 lpClassDefinition->dynPropGetter ? NamedPropertyGetterCallback : 0,
421 lpClassDefinition->dynPropSetter ? NamedPropertySetterCallback : 0,
422 lpClassDefinition->dynPropTypeGetter ? NamedPropertyQueryCallback : 0,
423 lpClassDefinition->dynPropDeleter ? NamedPropertyDeleterCallback : 0,
424 NamedPropertyEnumeratorCallback,
425 v8::External::New(pIsolate,
426 const_cast<FXJSE_CLASS_DESCRIPTOR*>(lpClassDefinition)),
427 v8::PropertyHandlerFlags::kNonMasking);
428 hObjectTemplate->SetHandler(configuration);
429}
430
431CFXJSE_Class::CFXJSE_Class(CFXJSE_Context* lpContext)
432 : m_lpClassDefinition(nullptr), m_pContext(lpContext) {}
433
434CFXJSE_Class::~CFXJSE_Class() {}