// Copyright 2014 PDFium Authors. All rights reserved. | |
// Use of this source code is governed by a BSD-style license that can be | |
// found in the LICENSE file. | |
// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com | |
#include "xfa/src/foxitlib.h" | |
#include "fxv8.h" | |
#include "context.h" | |
#include "class.h" | |
#include "value.h" | |
#include "scope_inline.h" | |
#include "util_inline.h" | |
static void FXJSE_V8ConstructorCallback_Wrapper( | |
const v8::FunctionCallbackInfo<v8::Value>& info); | |
static void FXJSE_V8FunctionCallback_Wrapper( | |
const v8::FunctionCallbackInfo<v8::Value>& info); | |
static void FXJSE_V8GetterCallback_Wrapper( | |
v8::Local<v8::String> property, | |
const v8::PropertyCallbackInfo<v8::Value>& info); | |
static void FXJSE_V8SetterCallback_Wrapper( | |
v8::Local<v8::String> property, | |
v8::Local<v8::Value> value, | |
const v8::PropertyCallbackInfo<void>& info); | |
void FXJSE_DefineFunctions(FXJSE_HCONTEXT hContext, | |
const FXJSE_FUNCTION* lpFunctions, | |
int nNum) { | |
CFXJSE_Context* lpContext = reinterpret_cast<CFXJSE_Context*>(hContext); | |
ASSERT(lpContext); | |
CFXJSE_ScopeUtil_IsolateHandleContext scope(lpContext); | |
v8::Isolate* pIsolate = lpContext->GetRuntime(); | |
v8::Local<v8::Object> hGlobalObject = | |
FXJSE_GetGlobalObjectFromContext(scope.GetLocalContext()); | |
for (int32_t i = 0; i < nNum; i++) { | |
hGlobalObject->ForceSet( | |
v8::String::NewFromUtf8(pIsolate, lpFunctions[i].name), | |
v8::Function::New( | |
pIsolate, FXJSE_V8FunctionCallback_Wrapper, | |
v8::External::New(pIsolate, | |
const_cast<FXJSE_FUNCTION*>(lpFunctions + i))), | |
static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete)); | |
} | |
} | |
FXJSE_HCLASS FXJSE_DefineClass(FXJSE_HCONTEXT hContext, | |
const FXJSE_CLASS* lpClass) { | |
CFXJSE_Context* lpContext = reinterpret_cast<CFXJSE_Context*>(hContext); | |
ASSERT(lpContext); | |
return reinterpret_cast<FXJSE_HCLASS>( | |
CFXJSE_Class::Create(lpContext, lpClass, FALSE)); | |
} | |
FXJSE_HCLASS FXJSE_GetClass(FXJSE_HCONTEXT hContext, | |
const CFX_ByteStringC& szName) { | |
return reinterpret_cast<FXJSE_HCLASS>(CFXJSE_Class::GetClassFromContext( | |
reinterpret_cast<CFXJSE_Context*>(hContext), szName)); | |
} | |
static void FXJSE_V8FunctionCallback_Wrapper( | |
const v8::FunctionCallbackInfo<v8::Value>& info) { | |
const FXJSE_FUNCTION* lpFunctionInfo = | |
static_cast<FXJSE_FUNCTION*>(info.Data().As<v8::External>()->Value()); | |
if (!lpFunctionInfo) { | |
return; | |
} | |
CFX_ByteStringC szFunctionName(lpFunctionInfo->name); | |
CFXJSE_Value* lpThisValue = CFXJSE_Value::Create(info.GetIsolate()); | |
lpThisValue->ForceSetValue(info.This()); | |
CFXJSE_Value* lpRetValue = CFXJSE_Value::Create(info.GetIsolate()); | |
CFXJSE_ArgumentsImpl impl = {&info, lpRetValue}; | |
lpFunctionInfo->callbackProc(reinterpret_cast<FXJSE_HOBJECT>(lpThisValue), | |
szFunctionName, | |
reinterpret_cast<CFXJSE_Arguments&>(impl)); | |
if (!lpRetValue->DirectGetValue().IsEmpty()) { | |
info.GetReturnValue().Set(lpRetValue->DirectGetValue()); | |
} | |
delete lpRetValue; | |
lpRetValue = NULL; | |
delete lpThisValue; | |
lpThisValue = NULL; | |
} | |
static void FXJSE_V8ClassGlobalConstructorCallback_Wrapper( | |
const v8::FunctionCallbackInfo<v8::Value>& info) { | |
const FXJSE_CLASS* lpClassDefinition = | |
static_cast<FXJSE_CLASS*>(info.Data().As<v8::External>()->Value()); | |
if (!lpClassDefinition) { | |
return; | |
} | |
CFX_ByteStringC szFunctionName(lpClassDefinition->name); | |
CFXJSE_Value* lpThisValue = CFXJSE_Value::Create(info.GetIsolate()); | |
lpThisValue->ForceSetValue(info.This()); | |
CFXJSE_Value* lpRetValue = CFXJSE_Value::Create(info.GetIsolate()); | |
CFXJSE_ArgumentsImpl impl = {&info, lpRetValue}; | |
lpClassDefinition->constructor(reinterpret_cast<FXJSE_HOBJECT>(lpThisValue), | |
szFunctionName, | |
reinterpret_cast<CFXJSE_Arguments&>(impl)); | |
if (!lpRetValue->DirectGetValue().IsEmpty()) { | |
info.GetReturnValue().Set(lpRetValue->DirectGetValue()); | |
} | |
delete lpRetValue; | |
lpRetValue = NULL; | |
delete lpThisValue; | |
lpThisValue = NULL; | |
} | |
static void FXJSE_V8GetterCallback_Wrapper( | |
v8::Local<v8::String> property, | |
const v8::PropertyCallbackInfo<v8::Value>& info) { | |
const FXJSE_PROPERTY* lpPropertyInfo = | |
static_cast<FXJSE_PROPERTY*>(info.Data().As<v8::External>()->Value()); | |
if (!lpPropertyInfo) { | |
return; | |
} | |
CFX_ByteStringC szPropertyName(lpPropertyInfo->name); | |
CFXJSE_Value* lpThisValue = CFXJSE_Value::Create(info.GetIsolate()); | |
CFXJSE_Value* lpPropValue = CFXJSE_Value::Create(info.GetIsolate()); | |
lpThisValue->ForceSetValue(info.This()); | |
lpPropertyInfo->getProc(reinterpret_cast<FXJSE_HOBJECT>(lpThisValue), | |
szPropertyName, | |
reinterpret_cast<FXJSE_HVALUE>(lpPropValue)); | |
info.GetReturnValue().Set(lpPropValue->DirectGetValue()); | |
delete lpThisValue; | |
lpThisValue = NULL; | |
delete lpPropValue; | |
lpPropValue = NULL; | |
} | |
static void FXJSE_V8SetterCallback_Wrapper( | |
v8::Local<v8::String> property, | |
v8::Local<v8::Value> value, | |
const v8::PropertyCallbackInfo<void>& info) { | |
const FXJSE_PROPERTY* lpPropertyInfo = | |
static_cast<FXJSE_PROPERTY*>(info.Data().As<v8::External>()->Value()); | |
if (!lpPropertyInfo) { | |
return; | |
} | |
CFX_ByteStringC szPropertyName(lpPropertyInfo->name); | |
CFXJSE_Value* lpThisValue = CFXJSE_Value::Create(info.GetIsolate()); | |
CFXJSE_Value* lpPropValue = CFXJSE_Value::Create(info.GetIsolate()); | |
lpThisValue->ForceSetValue(info.This()); | |
lpPropValue->ForceSetValue(value); | |
lpPropertyInfo->setProc(reinterpret_cast<FXJSE_HOBJECT>(lpThisValue), | |
szPropertyName, | |
reinterpret_cast<FXJSE_HVALUE>(lpPropValue)); | |
delete lpThisValue; | |
lpThisValue = NULL; | |
delete lpPropValue; | |
lpPropValue = NULL; | |
} | |
static void FXJSE_V8ConstructorCallback_Wrapper( | |
const v8::FunctionCallbackInfo<v8::Value>& info) { | |
const FXJSE_CLASS* lpClassDefinition = | |
static_cast<FXJSE_CLASS*>(info.Data().As<v8::External>()->Value()); | |
if (!lpClassDefinition) { | |
return; | |
} | |
FXSYS_assert(info.This()->InternalFieldCount()); | |
info.This()->SetAlignedPointerInInternalField(0, NULL); | |
} | |
FXJSE_HRUNTIME CFXJSE_Arguments::GetRuntime() const { | |
const CFXJSE_ArgumentsImpl* lpArguments = | |
reinterpret_cast<const CFXJSE_ArgumentsImpl* const>(this); | |
return reinterpret_cast<FXJSE_HRUNTIME>( | |
lpArguments->m_pRetValue->GetIsolate()); | |
} | |
int32_t CFXJSE_Arguments::GetLength() const { | |
const CFXJSE_ArgumentsImpl* lpArguments = | |
reinterpret_cast<const CFXJSE_ArgumentsImpl* const>(this); | |
return lpArguments->m_pInfo->Length(); | |
} | |
FXJSE_HVALUE CFXJSE_Arguments::GetValue(int32_t index) const { | |
const CFXJSE_ArgumentsImpl* lpArguments = | |
reinterpret_cast<const CFXJSE_ArgumentsImpl* const>(this); | |
CFXJSE_Value* lpArgValue = CFXJSE_Value::Create(v8::Isolate::GetCurrent()); | |
ASSERT(lpArgValue); | |
lpArgValue->ForceSetValue((*lpArguments->m_pInfo)[index]); | |
return reinterpret_cast<FXJSE_HVALUE>(lpArgValue); | |
} | |
FX_BOOL CFXJSE_Arguments::GetBoolean(int32_t index) const { | |
const CFXJSE_ArgumentsImpl* lpArguments = | |
reinterpret_cast<const CFXJSE_ArgumentsImpl* const>(this); | |
return (*lpArguments->m_pInfo)[index]->BooleanValue(); | |
} | |
int32_t CFXJSE_Arguments::GetInt32(int32_t index) const { | |
const CFXJSE_ArgumentsImpl* lpArguments = | |
reinterpret_cast<const CFXJSE_ArgumentsImpl* const>(this); | |
return static_cast<int32_t>((*lpArguments->m_pInfo)[index]->NumberValue()); | |
} | |
FX_FLOAT CFXJSE_Arguments::GetFloat(int32_t index) const { | |
const CFXJSE_ArgumentsImpl* lpArguments = | |
reinterpret_cast<const CFXJSE_ArgumentsImpl* const>(this); | |
return static_cast<FX_FLOAT>((*lpArguments->m_pInfo)[index]->NumberValue()); | |
} | |
CFX_ByteString CFXJSE_Arguments::GetUTF8String(int32_t index) const { | |
const CFXJSE_ArgumentsImpl* lpArguments = | |
reinterpret_cast<const CFXJSE_ArgumentsImpl* const>(this); | |
v8::Local<v8::String> hString = (*lpArguments->m_pInfo)[index]->ToString(); | |
v8::String::Utf8Value szStringVal(hString); | |
return CFX_ByteString(*szStringVal); | |
} | |
void* CFXJSE_Arguments::GetObject(int32_t index, FXJSE_HCLASS hClass) const { | |
const CFXJSE_ArgumentsImpl* lpArguments = | |
reinterpret_cast<const CFXJSE_ArgumentsImpl* const>(this); | |
v8::Local<v8::Value> hValue = (*lpArguments->m_pInfo)[index]; | |
ASSERT(!hValue.IsEmpty()); | |
if (!hValue->IsObject()) { | |
return NULL; | |
} | |
CFXJSE_Class* lpClass = reinterpret_cast<CFXJSE_Class*>(hClass); | |
return FXJSE_RetrieveObjectBinding(hValue.As<v8::Object>(), lpClass); | |
} | |
FXJSE_HVALUE CFXJSE_Arguments::GetReturnValue() { | |
const CFXJSE_ArgumentsImpl* lpArguments = | |
reinterpret_cast<const CFXJSE_ArgumentsImpl* const>(this); | |
return reinterpret_cast<FXJSE_HVALUE>(lpArguments->m_pRetValue); | |
} | |
static void FXJSE_Context_GlobalObjToString( | |
const v8::FunctionCallbackInfo<v8::Value>& info) { | |
const FXJSE_CLASS* lpClass = | |
static_cast<FXJSE_CLASS*>(info.Data().As<v8::External>()->Value()); | |
if (!lpClass) { | |
return; | |
} | |
if (info.This() == info.Holder() && lpClass->name) { | |
CFX_ByteString szStringVal; | |
szStringVal.Format("[object %s]", lpClass->name); | |
info.GetReturnValue().Set(v8::String::NewFromUtf8( | |
info.GetIsolate(), (const FX_CHAR*)szStringVal, | |
v8::String::kNormalString, szStringVal.GetLength())); | |
} else { | |
info.GetReturnValue().Set(info.This()->ObjectProtoToString()); | |
} | |
} | |
CFXJSE_Class* CFXJSE_Class::Create(CFXJSE_Context* lpContext, | |
const FXJSE_CLASS* lpClassDefinition, | |
FX_BOOL bIsJSGlobal) { | |
if (!lpContext || !lpClassDefinition) { | |
return NULL; | |
} | |
CFXJSE_Class* pClass = | |
GetClassFromContext(lpContext, lpClassDefinition->name); | |
if (pClass) { | |
return pClass; | |
} | |
v8::Isolate* pIsolate = lpContext->m_pIsolate; | |
pClass = new CFXJSE_Class(lpContext); | |
pClass->m_szClassName = lpClassDefinition->name; | |
pClass->m_lpClassDefinition = lpClassDefinition; | |
CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate); | |
v8::Local<v8::FunctionTemplate> hFunctionTemplate = v8::FunctionTemplate::New( | |
pIsolate, bIsJSGlobal ? 0 : FXJSE_V8ConstructorCallback_Wrapper, | |
v8::External::New(pIsolate, const_cast<FXJSE_CLASS*>(lpClassDefinition))); | |
hFunctionTemplate->SetClassName( | |
v8::String::NewFromUtf8(pIsolate, lpClassDefinition->name)); | |
hFunctionTemplate->InstanceTemplate()->SetInternalFieldCount(1); | |
v8::Local<v8::ObjectTemplate> hObjectTemplate = | |
hFunctionTemplate->InstanceTemplate(); | |
SetUpNamedPropHandler(pIsolate, hObjectTemplate, lpClassDefinition); | |
if (lpClassDefinition->propNum) { | |
for (int32_t i = 0; i < lpClassDefinition->propNum; i++) { | |
hObjectTemplate->SetNativeDataProperty( | |
v8::String::NewFromUtf8(pIsolate, | |
lpClassDefinition->properties[i].name), | |
lpClassDefinition->properties[i].getProc | |
? FXJSE_V8GetterCallback_Wrapper | |
: NULL, | |
lpClassDefinition->properties[i].setProc | |
? FXJSE_V8SetterCallback_Wrapper | |
: NULL, | |
v8::External::New(pIsolate, const_cast<FXJSE_PROPERTY*>( | |
lpClassDefinition->properties + i)), | |
static_cast<v8::PropertyAttribute>(v8::DontDelete)); | |
} | |
} | |
if (lpClassDefinition->methNum) { | |
for (int32_t i = 0; i < lpClassDefinition->methNum; i++) { | |
hObjectTemplate->Set( | |
v8::String::NewFromUtf8(pIsolate, lpClassDefinition->methods[i].name), | |
v8::FunctionTemplate::New( | |
pIsolate, FXJSE_V8FunctionCallback_Wrapper, | |
v8::External::New(pIsolate, const_cast<FXJSE_FUNCTION*>( | |
lpClassDefinition->methods + i))), | |
static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete)); | |
} | |
} | |
if (lpClassDefinition->constructor) { | |
if (bIsJSGlobal) { | |
hObjectTemplate->Set( | |
v8::String::NewFromUtf8(pIsolate, lpClassDefinition->name), | |
v8::FunctionTemplate::New( | |
pIsolate, FXJSE_V8ClassGlobalConstructorCallback_Wrapper, | |
v8::External::New(pIsolate, | |
const_cast<FXJSE_CLASS*>(lpClassDefinition))), | |
static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete)); | |
} else { | |
v8::Local<v8::Context> hLocalContext = | |
v8::Local<v8::Context>::New(pIsolate, lpContext->m_hContext); | |
FXJSE_GetGlobalObjectFromContext(hLocalContext) | |
->Set(v8::String::NewFromUtf8(pIsolate, lpClassDefinition->name), | |
v8::Function::New( | |
pIsolate, FXJSE_V8ClassGlobalConstructorCallback_Wrapper, | |
v8::External::New(pIsolate, const_cast<FXJSE_CLASS*>( | |
lpClassDefinition)))); | |
} | |
} | |
if (bIsJSGlobal) { | |
hObjectTemplate->Set( | |
v8::String::NewFromUtf8(pIsolate, "toString"), | |
v8::FunctionTemplate::New( | |
pIsolate, FXJSE_Context_GlobalObjToString, | |
v8::External::New(pIsolate, | |
const_cast<FXJSE_CLASS*>(lpClassDefinition)))); | |
} | |
pClass->m_hTemplate.Reset(lpContext->m_pIsolate, hFunctionTemplate); | |
lpContext->m_rgClasses.Add(pClass); | |
return pClass; | |
} | |
CFXJSE_Class* CFXJSE_Class::GetClassFromContext(CFXJSE_Context* pContext, | |
const CFX_ByteStringC& szName) { | |
for (int count = pContext->m_rgClasses.GetSize(), i = 0; i < count; i++) { | |
CFXJSE_Class* pClass = pContext->m_rgClasses[i]; | |
if (pClass->m_szClassName == szName) { | |
return pClass; | |
} | |
} | |
return NULL; | |
} |