Files
avmplus/core/NativeFunction.h
dmcallis 65a0592776 Initial source code drop
Source code drop for the Actionscript virtual machine in the Adobe
Flash Player for December 2013
2013-12-02 16:04:04 -08:00

355 lines
13 KiB
C++

/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __avmplus_NativeFunction__
#define __avmplus_NativeFunction__
#ifdef VMCFG_AOT
#include "CdeclThunk.h"
struct AOTInfo;
#endif
namespace avmplus
{
#ifdef _DEBUG
#define AvmThunk_DEBUG_ONLY(...) __VA_ARGS__
#else
#define AvmThunk_DEBUG_ONLY(...)
#endif /* DEBUG */
// Historically, bools are passed in as int32_t, as some calling conventions
// would only use a single byte and leave the remainder of the argument word
// as trash; this was used to ensure it was well-defined. (It's not clear to
// me if this is still an issue, but easier to maintain the convention than
// change now.)
typedef int32_t bool32;
#ifdef VMCFG_AOT
typedef void (AvmPlusScriptableObject::*AvmThunkNativeMethodHandler)();
typedef void (*AvmThunkNativeFunctionHandler)(AvmPlusScriptableObject* obj);
#endif
const uintptr_t kUnboxMask = ~uintptr_t(7);
#ifdef _DEBUG
extern void FASTCALL check_unbox(MethodEnv* env, bool u);
REALLY_INLINE uintptr_t _AvmThunkUnbox_AvmReceiver(MethodEnv* env, uintptr_t r)
{
check_unbox(env, false);
AvmAssert((r & ~kUnboxMask) == 0);
return r;
}
#define AvmThunkUnbox_AvmReceiver(t,r) ((t)(_AvmThunkUnbox_AvmReceiver(env, uintptr_t(r))))
REALLY_INLINE uintptr_t _AvmThunkUnbox_AvmAtomReceiver(MethodEnv* env, uintptr_t r)
{
check_unbox(env, true);
return r & kUnboxMask;
}
#define AvmThunkUnbox_AvmAtomReceiver(t,r) ((t)(_AvmThunkUnbox_AvmAtomReceiver(env, uintptr_t(r))))
#else
#define AvmThunkUnbox_AvmReceiver(t,r) ((t)(uintptr_t(r)))
#define AvmThunkUnbox_AvmAtomReceiver(t,r) ((t)(uintptr_t(r) & kUnboxMask))
#endif
#define AvmThunkUnbox_OBJECT(t,r) ((t)(r))
#define AvmThunkUnbox_BOOLEAN(t,r) ((r) != 0)
#define AvmThunkUnbox_INT(t,r) int32_t(r)
#define AvmThunkUnbox_UINT(t,r) uint32_t(r)
#define AvmThunkUnbox_NAMESPACE(t,r) ((t)(r))
#define AvmThunkUnbox_ATOM(t,r) ((t)(r))
#define AvmThunkUnbox_STRING(t,r) ((t)(r))
#define AvmThunkUnbox_VOID(t,r) (error ??? illegal)
#define AvmThunkUnbox_DOUBLE(t,r) AvmThunkUnbox_double_impl(&(r))
#define AvmThunkUnbox_FLOAT(t,r) (*(const t*)(&(r)))
#define AvmThunkUnbox_FLOAT4(t,r) (*(const t*)(&(r)))
#define AvmThunkArgSize_OBJECT 1
#define AvmThunkArgSize_BOOLEAN 1
#define AvmThunkArgSize_INT 1
#define AvmThunkArgSize_UINT 1
#define AvmThunkArgSize_NAMESPACE 1
#define AvmThunkArgSize_ATOM 1
#define AvmThunkArgSize_STRING 1
#define AvmThunkArgSize_VOID (error ??? illegal)
#define AvmThunkArgSize_FLOAT 1
#ifdef AVMPLUS_64BIT
#define AvmThunkArgSize_DOUBLE 1
#define AvmThunkArgSize_FLOAT4 2
#else
#define AvmThunkArgSize_DOUBLE 2
#define AvmThunkArgSize_FLOAT4 4
#endif
REALLY_INLINE double AvmThunkUnbox_double_impl(const Atom* b)
{
#if defined(AVMPLUS_64BIT)
MMGC_STATIC_ASSERT(sizeof(Atom) == sizeof(double));
return *(const double*)b;
#elif defined(VMCFG_UNALIGNED_FP_ACCESS)
MMGC_STATIC_ASSERT(sizeof(Atom)*2 == sizeof(double));
return *(const double*)b;
#else
MMGC_STATIC_ASSERT(sizeof(Atom)*2 == sizeof(double));
double_overlay u;
u.bits32[0] = b[0];
u.bits32[1] = b[1];
return u.value;
#endif
}
// trick, since values are compile-time known we usually don't need to call intToAtom, can statically transform them
// good for ints and ints currently
#define AvmThunkCanBeSmallIntAtom(v) (!((v) & 0xF0000000))
#define AvmThunkSmallIntAtom(v) ((((Atom)(v))<<3) | kIntptrType)
// note, this isn't complete -- only the ones currently needed are defined.
// expand as necessary. macros to take advantage of the fact that most
// args are compile-time constants.
#define AvmThunkCoerce_INT_DOUBLE(v) double(v)
#define AvmThunkCoerce_INT_UINT(v) uint32_t(v)
#define AvmThunkCoerce_INT_ATOM(v) (AvmThunkCanBeSmallIntAtom(v) ? AvmThunkSmallIntAtom(v) : env->core()->intAtom(v))
#define AvmThunkCoerce_UINT_DOUBLE(v) double(v)
#define AvmThunkCoerce_UINT_INT(v) int32_t(v)
#define AvmThunkCoerce_UINT_ATOM(v) (AvmThunkCanBeSmallIntAtom(v) ? AvmThunkSmallIntAtom(v) : env->core()->intAtom(v))
#define AvmThunkCoerce_BOOLEAN_ATOM(v) ((v) ? trueAtom : falseAtom)
#define AvmThunkCoerce_ATOM_DOUBLE(v) (MathUtils::kNaN)
#define AvmThunkCoerce_ATOM_STRING(v) (NULL)
#define AvmThunkCoerce_ATOM_OBJECT(v) (NULL)
#define AvmThunkCoerce_STRING_ATOM(v) ((v) ? (v)->atom() : nullStringAtom)
#define AvmThunkGetConstantString(v) (env->method->pool()->getString(v))
#ifdef VMCFG_AOT
union AvmThunkNativeHandler
{
AvmThunkNativeMethodHandler method;
AvmThunkNativeFunctionHandler function;
};
#endif
struct NativeMethodInfo
{
public:
#ifdef VMCFG_AOT
AvmThunkNativeHandler handler;
#endif
GprMethodProc thunker;
#if defined(VMCFG_TELEMETRY_SAMPLER) && !defined(VMCFG_AOT)
GprMethodProc samplerThunker;
#endif
int32_t method_id;
};
struct NativeClassInfo
{
public:
CreateClassClosureProc createClassClosure;
int32_t class_id;
uint16_t sizeofClass;
uint16_t offsetofSlotsClass;
uint16_t sizeofInstance;
uint16_t offsetofSlotsInstance;
bool hasCustomConstruct;
bool isRestrictedInheritance;
bool isAbstractBase;
};
// ---------------
class NativeInitializer
{
public:
NativeInitializer(AvmCore* core,
char const* const* versioned_uris,
#ifdef VMCFG_AOT
const AOTInfo *aotInfo,
#else
const uint8_t* abcData,
uint32_t abcDataLen,
#endif
uint32_t methodCount,
uint32_t classCount);
~NativeInitializer();
PoolObject* parseBuiltinABC(Domain* domain);
const NativeMethodInfo* getNativeInfo(uint32_t i) const { return get_method(i); }
#ifdef VMCFG_AOT
bool getCompiledInfo(NativeMethodInfo *info, AvmThunkNativeHandler* handlerOut, Multiname &returnTypeName, uint32_t i) const;
bool hasBuiltins() const { return methodCount || classCount; }
const AOTInfo* get_aotInfo() const { return aotInfo; }
#endif
void fillInMethods(const NativeMethodInfo* methodEntry);
void fillInClasses(const NativeClassInfo* classEntry);
#ifdef VMCFG_AOT
REALLY_INLINE const NativeClassInfo* get_class(uint32_t i) const
{
return i < classCount ? classes[i] : 0;
}
#else
REALLY_INLINE const NativeClassInfo* get_class(uint32_t i) const { AvmAssert(i < classCount); return classes[i]; }
#endif
private:
typedef const NativeMethodInfo* MethodType;
typedef const NativeClassInfo* ClassType;
REALLY_INLINE const NativeMethodInfo* get_method(uint32_t i) const { AvmAssert(i < methodCount); return methods[i]; }
private:
AvmCore* const core;
char const* const* versioned_uris;
const uint8_t* const abcData;
uint32_t const abcDataLen;
MethodType* const methods;
ClassType* const classes;
const uint32_t methodCount;
const uint32_t classCount;
#ifdef VMCFG_AOT
const AOTInfo* aotInfo;
const AvmThunkNativeFunctionHandler* compiledMethods;
const uint32_t compiledMethodCount;
#endif
};
#ifdef VMCFG_AOT
#define _NATIVE_METHOD_CAST_PTR(CLS, PTR) \
reinterpret_cast<AvmThunkNativeMethodHandler>((void(CLS::*)())(PTR))
#define AVMTHUNK_DECLARE_NATIVE_INITIALIZER(NAME) \
extern PoolObject* initBuiltinABC_##NAME(AvmCore* core, Domain* domain); \
extern const NativeClassInfo NAME##_classEntries[]; \
extern const NativeMethodInfo NAME##_methodEntries[];
#else
#define AVMTHUNK_DECLARE_NATIVE_INITIALIZER(NAME) \
extern PoolObject* initBuiltinABC_##NAME(AvmCore* core, Domain* domain);
#endif
#define AVMTHUNK_BEGIN_NATIVE_TABLES(NAME)
#define AVMTHUNK_END_NATIVE_TABLES()
// ---------------
#ifdef VMCFG_AOT
#define AVMTHUNK_BEGIN_NATIVE_METHODS(NAME) \
const NativeMethodInfo NAME##_methodEntries[] = {
#else
#define AVMTHUNK_BEGIN_NATIVE_METHODS(NAME) \
static const NativeMethodInfo NAME##_methodEntries[] = {
#endif
#ifdef VMCFG_AOT
#define _AVMTHUNK_NATIVE_METHOD(CLS, METHID, IMPL) \
{ { _NATIVE_METHOD_CAST_PTR(CLS, &IMPL) }, (GprMethodProc)avmplus::NativeID::METHID##_thunk, avmplus::NativeID::METHID },
#else
#ifdef VMCFG_TELEMETRY_SAMPLER
#define _AVMTHUNK_NATIVE_METHOD(CLS, METHID, IMPL) \
{ (GprMethodProc)avmplus::NativeID::METHID##_thunk, (GprMethodProc)avmplus::NativeID::METHID##_sampler_thunk, avmplus::NativeID::METHID },
#else
#define _AVMTHUNK_NATIVE_METHOD(CLS, METHID, IMPL) \
{ (GprMethodProc)avmplus::NativeID::METHID##_thunk, avmplus::NativeID::METHID },
#endif
#endif
#define AVMTHUNK_NATIVE_METHOD(METHID, IMPL) \
_AVMTHUNK_NATIVE_METHOD(ScriptObject, METHID, IMPL)
#define AVMTHUNK_NATIVE_METHOD_STRING(METHID, IMPL) \
_AVMTHUNK_NATIVE_METHOD(avmplus::String, METHID, IMPL)
#define AVMTHUNK_NATIVE_METHOD_NAMESPACE(METHID, IMPL) \
_AVMTHUNK_NATIVE_METHOD(avmplus::Namespace, METHID, IMPL)
#ifdef VMCFG_AOT
// AOT build env is ok with designated inits
#define AVMTHUNK_NATIVE_FUNCTION(METHID, IMPL) \
{ { function: reinterpret_cast<AvmThunkNativeFunctionHandler>(IMPL) }, (GprMethodProc)avmplus::NativeID::METHID##_thunk, avmplus::NativeID::METHID },
#define AVMTHUNK_END_NATIVE_METHODS() \
{ { NULL }, NULL, -1 } };
#else
#ifdef VMCFG_TELEMETRY_SAMPLER
#define AVMTHUNK_NATIVE_FUNCTION(METHID, IMPL) \
{ (GprMethodProc)avmplus::NativeID::METHID##_thunk, (GprMethodProc)avmplus::NativeID::METHID##_sampler_thunk, avmplus::NativeID::METHID },
#define AVMTHUNK_END_NATIVE_METHODS() \
{ NULL, NULL, -1 } };
#else
#define AVMTHUNK_NATIVE_FUNCTION(METHID, IMPL) \
{ (GprMethodProc)avmplus::NativeID::METHID##_thunk, avmplus::NativeID::METHID },
#define AVMTHUNK_END_NATIVE_METHODS() \
{ NULL, -1 } };
#endif
#endif
// ---------------
#define AVMTHUNK_BEGIN_NATIVE_CLASSES(NAME) \
const NativeClassInfo NAME##_classEntries[] = {
#define AVMTHUNK_NATIVE_CLASS(CLSID, CLS, FQCLS, OFFSETOFSLOTSCLS, INST, OFFSETOFSLOTSINST, CUSTOMCONSTRUCT, RESTRICTEDINHERITANCE, ABSTRACTBASE) \
{ FQCLS::createClassClosure,\
avmplus::NativeID::CLSID,\
sizeof(FQCLS),\
OFFSETOFSLOTSCLS,\
sizeof(INST),\
OFFSETOFSLOTSINST,\
CUSTOMCONSTRUCT,\
RESTRICTEDINHERITANCE,\
ABSTRACTBASE\
},
#define AVMTHUNK_END_NATIVE_CLASSES() \
{ NULL, -1, 0, 0, 0, 0, false, false, false } };
#ifdef VMCFG_AOT
#define AVMTHUNK_DEFINE_NATIVE_INITIALIZER(NAME)
#else
#define AVMTHUNK_DEFINE_NATIVE_INITIALIZER(NAME) \
PoolObject* initBuiltinABC_##NAME(AvmCore* core, Domain* domain) { \
NativeInitializer ninit(core, \
avmplus::NativeID::NAME##_versioned_uris, \
avmplus::NativeID::NAME##_abc_data, \
avmplus::NativeID::NAME##_abc_length, \
avmplus::NativeID::NAME##_abc_method_count, \
avmplus::NativeID::NAME##_abc_class_count); \
ninit.fillInClasses(NAME##_classEntries); \
ninit.fillInMethods(NAME##_methodEntries); \
return ninit.parseBuiltinABC(domain); \
}
#endif
#define AVM_INIT_BUILTIN_ABC_IN_DOMAIN(MAPNAME, CORE, DOMAIN) \
avmplus::NativeID::initBuiltinABC_##MAPNAME((CORE), (DOMAIN))
#define AVM_INIT_BUILTIN_ABC(MAPNAME, CORE) \
avmplus::NativeID::initBuiltinABC_##MAPNAME((CORE), (CORE)->builtinDomain)
class ClassManifestBase : public MMgc::GCTraceableObject
{
public:
avmplus::ScriptEnv* env() const { return _env; }
protected:
uint32_t const _count;
avmplus::ScriptEnv* const _env; // const non-RC, so no WB needed
avmplus::ClassClosure* _classes[1]; // lying, really [_count]. written with explicit WBRCs.
protected:
REALLY_INLINE ClassManifestBase(uint32_t count, avmplus::ScriptEnv* e) : _count(count), _env(e) { }
ClassClosure* FASTCALL lazyInitClass(uint32_t class_id);
void fillInClass(uint32_t class_id, ClassClosure* c);
virtual bool gcTrace(MMgc::GC* gc, size_t);
};
}
#endif /* __avmplus_NativeFunction__ */