Source code drop for the Actionscript virtual machine in the Adobe Flash Player for December 2013
1124 lines
39 KiB
C++
1124 lines
39 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/. */
|
|
|
|
#include "avmplus.h"
|
|
#include "../vprof/vprof.h"
|
|
#include "Interpreter.h"
|
|
|
|
namespace avmplus {
|
|
|
|
// Computes the size in bytes of an argument of type t, when passed
|
|
// using the VM-wide AS3 calling convention.
|
|
int argSize(Traits* t)
|
|
{
|
|
return Traits::getBuiltinType(t) == BUILTIN_number
|
|
? (int)sizeof(double)
|
|
#ifdef VMCFG_FLOAT
|
|
: Traits::getBuiltinType(t) == BUILTIN_float4
|
|
? (int) sizeof(float4_t)
|
|
#endif
|
|
: (int)sizeof(Atom);
|
|
}
|
|
|
|
BaseExecMgr* BaseExecMgr::exec(VTable* vtable)
|
|
{
|
|
return (BaseExecMgr*) vtable->core()->exec;
|
|
}
|
|
|
|
BaseExecMgr* BaseExecMgr::exec(MethodEnv* env)
|
|
{
|
|
return (BaseExecMgr*) env->core()->exec;
|
|
}
|
|
|
|
BaseExecMgr::BaseExecMgr(AvmCore* core)
|
|
: core(core)
|
|
, config(core->config)
|
|
#ifdef VMCFG_COMPILEPOLICY
|
|
, _ruleSet(NULL)
|
|
#endif
|
|
#ifdef VMCFG_VERIFYALL
|
|
, verifyFunctionQueue(core->gc, 0)
|
|
, verifyTraitsQueue(core->gc, 0)
|
|
#endif
|
|
#ifdef VMCFG_NANOJIT
|
|
, current_osr(NULL)
|
|
, jit_observer(NULL)
|
|
#endif
|
|
{
|
|
#ifdef SUPERWORD_PROFILING
|
|
WordcodeTranslator::swprofStart();
|
|
#endif
|
|
#ifdef VMCFG_COMPILEPOLICY
|
|
prepPolicyRules();
|
|
#endif
|
|
#ifdef VMCFG_NANOJIT
|
|
setupJit(core);
|
|
#endif
|
|
}
|
|
|
|
BaseExecMgr::~BaseExecMgr()
|
|
{
|
|
#ifdef SUPERWORD_PROFILING
|
|
WordcodeTranslator::swprofStop();
|
|
#endif
|
|
#ifdef VMCFG_NANOJIT
|
|
delete jit_observer;
|
|
jit_observer = NULL;
|
|
#endif
|
|
}
|
|
|
|
// Called when MethodInfo is constructed.
|
|
void BaseExecMgr::init(MethodInfo* m, const NativeMethodInfo* native_info)
|
|
{
|
|
#ifndef MEMORY_INFO
|
|
// MMGC_STATIC_ASSERT(offsetof(MethodInfo, _implGPR) == 0);
|
|
#endif
|
|
|
|
if (native_info) {
|
|
m->_apply_fastpath = 1;
|
|
m->_native.thunker = native_info->thunker;
|
|
#if defined(VMCFG_TELEMETRY_SAMPLER) && !defined(VMCFG_AOT)
|
|
m->_native.samplerThunker = native_info->samplerThunker;
|
|
#endif
|
|
#ifdef VMCFG_AOT
|
|
if(!m->isAotCompiled()) {
|
|
m->_native.handler = native_info->handler;
|
|
}
|
|
#endif
|
|
} else {
|
|
#ifdef VMCFG_VERIFYALL
|
|
if (config.verifyonly && m->isNative())
|
|
m->_hasMethodBody = 0;
|
|
#endif
|
|
}
|
|
|
|
// Initially, OSR will be allowed if we are in mixed run mode
|
|
// (interpreter + JIT) and OSR has not been globally disabled.
|
|
// Note that OSR may still be unspported for a given function
|
|
// even if enabled here -- see OSR::isSupported().
|
|
// This setting may be overridden by an explicit ExecPolicy
|
|
// attribute on the method. See AbcParser::parseTraits().
|
|
if (!m->isNative())
|
|
m->setOSR((config.runmode == RM_mixed && config.osr_enabled) ? config.osr_threshold : 0);
|
|
|
|
m->_implGPR = NULL;
|
|
m->_invoker = NULL;
|
|
if (m->isResolved() && !config.verifyall) {
|
|
// notifyMethodResolved() won't be called; this method is
|
|
// a synthetic init method that returns void. So install
|
|
// verify trampolines now.
|
|
m->_implGPR = verifyEnterGPR;
|
|
m->_invoker = verifyInvoke;
|
|
}
|
|
}
|
|
|
|
// Stub that is invoked when a methodenv is called the first time.
|
|
// Copy the invoker pointer from the underlying method, then call there.
|
|
uintptr_t BaseExecMgr::delegateInvoke(MethodEnv* env, int32_t argc, uint32_t *ap)
|
|
{
|
|
env->_implGPR = env->method->_implGPR;
|
|
return (*env->_implGPR)(env, argc, ap);
|
|
}
|
|
|
|
// Called when MethodInfo is constructed.
|
|
void BaseExecMgr::init(MethodEnv* env)
|
|
{
|
|
env->_implGPR = delegateInvoke;
|
|
}
|
|
|
|
#ifdef VMCFG_FLOAT
|
|
typedef enum _eRetTypeKinds {
|
|
kIntegerRetType = 0, // return type for ints & co; also used for pointers, typically
|
|
kFloatingPointRetType = 1, // used by functions that return float or double
|
|
kSIMDRetType = 2, // used by functions that return float4
|
|
} eRetTypeKinds;
|
|
|
|
static REALLY_INLINE eRetTypeKinds ReturnType(MethodSignaturep ms){
|
|
BuiltinType rt = ms->returnTraitsBT();
|
|
if(rt==BUILTIN_number || rt==BUILTIN_float)
|
|
return kFloatingPointRetType;
|
|
if(rt==BUILTIN_float4)
|
|
return kSIMDRetType;
|
|
return kIntegerRetType;
|
|
}
|
|
#endif
|
|
|
|
// Called after MethodInfo is resolved.
|
|
void BaseExecMgr::notifyMethodResolved(MethodInfo* m, MethodSignaturep ms)
|
|
{
|
|
if (!config.verifyall) {
|
|
m->_invoker = verifyInvoke;
|
|
#ifdef VMCFG_FLOAT
|
|
switch(ReturnType(ms)){
|
|
case kIntegerRetType:
|
|
m->_implGPR = verifyEnterGPR;
|
|
break;
|
|
case kFloatingPointRetType:
|
|
m->_implFPR = verifyEnterFPR;
|
|
break;
|
|
case kSIMDRetType:
|
|
m->_implVECR = verifyEnterVECR_adapter;
|
|
break;
|
|
}
|
|
#else
|
|
if (ms->returnTraitsBT() == BUILTIN_number)
|
|
m->_implFPR = verifyEnterFPR;
|
|
else
|
|
m->_implGPR = verifyEnterGPR;
|
|
|
|
#endif // VMCFG_FLOAT
|
|
}
|
|
}
|
|
|
|
// True if at least one argument is typed and therefore must
|
|
// be coerced on invocation.
|
|
static bool hasTypedArgs(MethodSignaturep ms)
|
|
{
|
|
int32_t param_count = ms->param_count();
|
|
for (int32_t i = 1; i <= param_count; i++) {
|
|
if (ms->paramTraits(i) != NULL) {
|
|
// at least one parameter is typed; need full coerceEnter
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Initialize the declared slots of the given object by iterating
|
|
// through the ABC traits record and assigning to nonzero slots.
|
|
void BaseExecMgr::initObj(MethodEnv* env, ScriptObject* obj)
|
|
{
|
|
struct InterpInitVisitor: public InitVisitor {
|
|
ScriptObject* obj;
|
|
InterpInitVisitor(ScriptObject* obj) : obj(obj) {}
|
|
virtual ~InterpInitVisitor() {}
|
|
void defaultVal(Atom val, uint32_t slot, Traits*) {
|
|
// Assign the default value.
|
|
// Keep in sync with interpreter INSTR(setslot).
|
|
obj->coerceAndSetSlotAtom(slot, val);
|
|
}
|
|
};
|
|
InterpInitVisitor visitor(obj);
|
|
Traits* t = env->method->declaringTraits();
|
|
const TraitsBindings *tb = t->getTraitsBindings();
|
|
t->visitInitBody(&visitor, env->toplevel(), tb);
|
|
}
|
|
|
|
Atom BaseExecMgr::initInvokeInterp(MethodEnv* env, int argc, Atom* args)
|
|
{
|
|
initObj(env, (ScriptObject*) atomPtr(args[0]));
|
|
return invokeInterp(env, argc, args);
|
|
}
|
|
|
|
Atom BaseExecMgr::initInvokeInterpNoCoerce(MethodEnv* env, int argc, Atom* args)
|
|
{
|
|
initObj(env, (ScriptObject*) atomPtr(args[0]));
|
|
return invokeInterpNoCoerce(env, argc, args);
|
|
}
|
|
|
|
void BaseExecMgr::setInterp(MethodInfo* m, MethodSignaturep ms, bool isOsr)
|
|
{
|
|
// Choose an appropriate set of interpreter invocation stubs.
|
|
// * if OSR is enabled, choose a stub that counts invocations to trigger JIT.
|
|
// * if the method is a constructor, choose a stub that initializes
|
|
// the object's fields before executing (init_).
|
|
// * if the method has no typed args, choose an invoker stub that skips
|
|
// arg type checking entirely (_nocoerce).
|
|
static const AtomMethodProc invoke_stubs[2][2][2] = {{{
|
|
BaseExecMgr::invokeInterpNoCoerce, // osr=0, ctor=0, typedargs=0
|
|
BaseExecMgr::invokeInterp // osr=0, ctor=0, typedargs=1
|
|
}, {
|
|
BaseExecMgr::initInvokeInterpNoCoerce, // osr=0, ctor=1, typedargs=0
|
|
BaseExecMgr::initInvokeInterp // osr=0, ctor=1, typedargs=1
|
|
}}, {{
|
|
#ifdef VMCFG_NANOJIT
|
|
OSR::osrInvokeInterp, // osr=1, ctor=0, typedargs=0
|
|
OSR::osrInvokeInterp // osr=1, ctor=0, typedargs=1
|
|
}, {
|
|
OSR::osrInitInvokeInterp, // osr=1, ctor=1, typedargs=0
|
|
OSR::osrInitInvokeInterp // osr=1, ctor=1, typedargs=1
|
|
#endif
|
|
}}};
|
|
int osr = isOsr ? 1 : 0;
|
|
int ctor = m->isConstructor() ? 1 : 0;
|
|
int typedargs = hasTypedArgs(ms) ? 1 : 0;
|
|
m->_implGPR = NULL;
|
|
m->_invoker = invoke_stubs[osr][ctor][typedargs];
|
|
m->_isInterpImpl = 1;
|
|
|
|
AvmAssert(!isOsr || isJitEnabled());
|
|
|
|
#ifdef VMCFG_NANOJIT
|
|
if (isJitEnabled()) {
|
|
// Choose an appropriate set of jit->interp stubs.
|
|
// * if OSR is enabled, choose a stub that counts invocations to trigger JIT.
|
|
// * if the method is a constructor, choose a stub that initializes
|
|
// the object's fields before executing (init_).
|
|
// * if the return type is double, the stub must have a signature that
|
|
// returns double. (FPR)
|
|
// * if the return type is float, the stub reuses the double (FPR)
|
|
// signature
|
|
|
|
static const GprMethodProc impl_stubs[2][2][IFFLOAT(3,2)] = {{{
|
|
BaseExecMgr::interpGPR // osr=0, ctor=0, fpr=0
|
|
, (GprMethodProc)BaseExecMgr::interpFPR // osr=0, ctor=0, fpr=1
|
|
#ifdef VMCFG_FLOAT
|
|
, (GprMethodProc)BaseExecMgr::interpVECR // osr=0, ctor=0, fpr=2
|
|
#endif
|
|
}, {
|
|
BaseExecMgr::initInterpGPR // osr=0, ctor=1, fpr=0
|
|
, (GprMethodProc)BaseExecMgr::initInterpFPR // osr=0, ctor=1, fpr=1
|
|
#ifdef VMCFG_FLOAT
|
|
, (GprMethodProc)BaseExecMgr::initInterpVECR // osr=0, ctor=1, fpr=2
|
|
#endif
|
|
}}, {{
|
|
OSR::osrInterpGPR // osr=1, ctor=0, fpr=0
|
|
, (GprMethodProc)OSR::osrInterpFPR // osr=1, ctor=0, fpr=1
|
|
#ifdef VMCFG_FLOAT
|
|
, (GprMethodProc)OSR::osrInterpVECR // osr=1, ctor=0, fpr=2
|
|
#endif
|
|
}, {
|
|
OSR::osrInitInterpGPR // osr=1, ctor=1, fpr=0
|
|
, (GprMethodProc)OSR::osrInitInterpFPR // osr=1, ctor=1, fpr=1
|
|
#ifdef VMCFG_FLOAT
|
|
, (GprMethodProc)OSR::osrInitInterpVECR // osr=1, ctor=1, fpr=2
|
|
#endif
|
|
}}};
|
|
int rtype = IFFLOAT( ReturnType(ms),
|
|
ms->returnTraitsBT() == BUILTIN_number ? 1 : 0);
|
|
m->_implGPR = impl_stubs[osr][ctor][rtype];
|
|
|
|
// The countdown was previously set to config.osr_threshold, the
|
|
// global default, and then possibly overridden by an explicit
|
|
// ExecPolicy attribute. If in fact OSR is not supported, zero
|
|
// out the countdown here. This accounts for cases in which OSR
|
|
// is permitted by policy, but still cannot be supported.
|
|
// Strictly speaking, this code is not required, but it may avoid
|
|
// unnecessary invocations of OSR:isSupported() in OSR:countEdge().
|
|
if (!isOsr)
|
|
m->_abc.countdown = 0;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
uintptr_t BaseExecMgr::verifyEnterGPR(MethodEnv* env, int32_t argc, uint32_t* ap)
|
|
{
|
|
verifyOnCall(env);
|
|
STACKADJUST(); // align stack for 32-bit Windows and MSVC compiler
|
|
uintptr_t ret = (*env->method->_implGPR)(env, argc, ap);
|
|
STACKRESTORE();
|
|
return ret;
|
|
}
|
|
|
|
double BaseExecMgr::verifyEnterFPR(MethodEnv* env, int32_t argc, uint32_t* ap)
|
|
{
|
|
verifyOnCall(env);
|
|
STACKADJUST(); // align stack for 32-bit Windows and MSVC compiler
|
|
double d = (*env->method->_implFPR)(env, argc, ap);
|
|
STACKRESTORE();
|
|
return d;
|
|
}
|
|
|
|
#ifdef VMCFG_FLOAT
|
|
float4_t BaseExecMgr::verifyEnterVECR(MethodEnv* env, int32_t argc, uint32_t* ap)
|
|
{
|
|
verifyOnCall(env);
|
|
STACKADJUST(); // align stack for 32-bit Windows and MSVC compiler
|
|
float4_t f4 = thunkEnterVECR_adapter((void*)env->method->_implVECR, env, argc, ap);
|
|
STACKRESTORE();
|
|
return f4;
|
|
}
|
|
#endif // VMCFG_FLOAT
|
|
|
|
// Entry point when the first call to the method is late bound.
|
|
Atom BaseExecMgr::verifyInvoke(MethodEnv* env, int argc, Atom* args)
|
|
{
|
|
verifyOnCall(env);
|
|
return (*env->method->_invoker)(env, argc, args);
|
|
}
|
|
|
|
void BaseExecMgr::verifyOnCall(MethodEnv* env)
|
|
{
|
|
BaseExecMgr *exec = BaseExecMgr::exec(env);
|
|
AvmAssert(!exec->config.verifyall); // never verify late in verifyall mode
|
|
|
|
#ifdef DEBUGGER
|
|
// Install a fake CallStackNode here, so that if we throw a verify error,
|
|
// we get a stack trace with the method being verified as its top entry.
|
|
CallStackNode callStackNode(env->method);
|
|
#endif
|
|
|
|
exec->verifyMethod(env->method, env->toplevel(), env->abcEnv());
|
|
|
|
// We got here by calling env->_implGPR, which was pointing to verifyEnterGPR/FPR,
|
|
// but next time we want to call the real code, not verifyEnter again.
|
|
// All other MethodEnv's in their default state will call the target method
|
|
// directly and never go through verifyEnter(). Update the copy in MethodEnv.
|
|
env->_implGPR = env->method->_implGPR;
|
|
}
|
|
|
|
// Verify the given method according to its type, with a CodeWriter
|
|
// pipeline appropriate to the current execution mode.
|
|
void BaseExecMgr::verifyMethod(MethodInfo* m, Toplevel *toplevel, AbcEnv* abc_env)
|
|
{
|
|
AvmAssert(m->declaringTraits()->isResolved());
|
|
m->resolveSignature(toplevel);
|
|
PERFM_NTPROF_BEGIN("verify-ticks");
|
|
MethodSignaturep ms = m->getMethodSignature();
|
|
if (m->isNative())
|
|
verifyNative(m, ms);
|
|
#ifdef VMCFG_NANOJIT
|
|
else if (shouldJitFirst(abc_env, m, ms)) {
|
|
verifyJit(m, ms, toplevel, abc_env, NULL);
|
|
}
|
|
#endif
|
|
else
|
|
verifyInterp(m, ms, toplevel, abc_env);
|
|
PERFM_NTPROF_END("verify-ticks");
|
|
}
|
|
|
|
/**
|
|
* If we are in a pure-interpreter mode, this simply verifies code and then
|
|
* installs a trampoline to run the interpreter. If we are in a JIT mode,
|
|
* then a previous decision has decided not to JIT-compile during verification,
|
|
* and we call OSR::isSupported() to decide whether to simply install the
|
|
* interpreter trampoline, or a countdown trampoline to trigger OSR.
|
|
*/
|
|
void BaseExecMgr::verifyInterp(MethodInfo* m, MethodSignaturep ms, Toplevel *toplevel, AbcEnv* abc_env)
|
|
{
|
|
#ifdef VMCFG_WORDCODE
|
|
WordcodeEmitter coder(m, toplevel);
|
|
#else
|
|
CodeWriter coder;
|
|
#endif
|
|
verifyCommon(m, ms, toplevel, abc_env, &coder);
|
|
|
|
#ifdef VMCFG_NANOJIT
|
|
# ifdef AVMPLUS_VERBOSE
|
|
if (m->pool()->isVerbose(VB_execpolicy))
|
|
core->console << "execpolicy interp (" << m->unique_method_id() << ") " << m << " jit-available\n";
|
|
# endif
|
|
setInterp(m, ms, OSR::isSupported(abc_env, m, ms));
|
|
#else
|
|
# ifdef AVMPLUS_VERBOSE
|
|
if (m->pool()->isVerbose(VB_execpolicy))
|
|
core->console << "execpolicy interp " << m << "\n";
|
|
# endif
|
|
setInterp(m, ms, false);
|
|
#endif
|
|
}
|
|
|
|
// run the verifier, and if an exception is thrown,
|
|
// clean up the CodeWriter chain passed in by calling coder->cleanup().
|
|
// On normal return the CodeWriters declared here get cleaned via their
|
|
// destructors, and passed-in CodeWriters are still valid.
|
|
void BaseExecMgr::verifyCommon(MethodInfo* m, MethodSignaturep ms,
|
|
Toplevel* toplevel, AbcEnv* abc_env, CodeWriter* const coder)
|
|
{
|
|
CodeWriter* volatile vcoder = coder; // Volatile for setjmp safety.
|
|
|
|
#ifdef VMCFG_VERIFYALL
|
|
VerifyallWriter verifyall(m, this, vcoder);
|
|
if (config.verifyall)
|
|
vcoder = &verifyall;
|
|
#endif
|
|
|
|
Verifier verifier(m, ms, toplevel, abc_env); // Does not throw.
|
|
TRY(core, kCatchAction_Rethrow) {
|
|
verifier.verify(vcoder); // Verify and fill vcoder pipeline.
|
|
}
|
|
CATCH (Exception *exception) {
|
|
verifier.~Verifier(); // Clean up verifier.
|
|
vcoder->cleanup(); // Cleans up all coders.
|
|
core->throwException(exception);
|
|
}
|
|
END_CATCH
|
|
END_TRY
|
|
}
|
|
|
|
#ifdef DEBUGGER
|
|
|
|
template<typename T, typename CALLT> T debugEnterExitWrapper(MethodEnv* env, GprMethodProc thunker, int32_t argc, uint32_t* argv){
|
|
CallStackNode csn(CallStackNode::kEmpty);
|
|
env->debugEnter(/*frame_sst*/NULL, &csn, /*framep*/NULL, /*eip*/NULL);
|
|
CALLT thunk = (CALLT) thunker;
|
|
const T result = thunk(env, argc, argv);
|
|
env->debugExit(&csn);
|
|
return result;
|
|
}
|
|
/*static*/
|
|
uintptr_t BaseExecMgr::debugEnterExitWrapper32(MethodEnv* env, int32_t argc, uint32_t* argv)
|
|
{
|
|
#if defined(VMCFG_TELEMETRY_SAMPLER) && !defined(VMCFG_AOT)
|
|
return debugEnterExitWrapper<uintptr_t,GprMethodProc>(env,env->core()->samplerEnabled ? env->method->_native.samplerThunker : env->method->_native.thunker,argc,argv);
|
|
#else
|
|
return debugEnterExitWrapper<uintptr_t,GprMethodProc>(env,env->method->_native.thunker,argc,argv);
|
|
#endif
|
|
}
|
|
|
|
/*static*/
|
|
double BaseExecMgr::debugEnterExitWrapperN(MethodEnv* env, int32_t argc, uint32_t* argv)
|
|
{
|
|
#if defined(VMCFG_TELEMETRY_SAMPLER) && !defined(VMCFG_AOT)
|
|
return debugEnterExitWrapper<double,FprMethodProc>(env,env->core()->samplerEnabled ? env->method->_native.samplerThunker : env->method->_native.thunker, argc,argv);
|
|
#else
|
|
return debugEnterExitWrapper<double,FprMethodProc>(env,env->method->_native.thunker, argc,argv);
|
|
#endif
|
|
}
|
|
#ifdef VMCFG_FLOAT
|
|
/*static*/
|
|
float4_t BaseExecMgr::debugEnterExitWrapperV(MethodEnv* env, int32_t argc, uint32_t* argv)
|
|
{
|
|
CallStackNode csn(CallStackNode::kEmpty);
|
|
env->debugEnter(/*frame_sst*/NULL, &csn, /*framep*/NULL, /*eip*/NULL);
|
|
#if defined(VMCFG_TELEMETRY_SAMPLER) && !defined(VMCFG_AOT)
|
|
const float4_t result = thunkEnterVECR_adapter((void*) env->core()->samplerEnabled ? env->method->_native.samplerThunker : env->method->_native.thunker, env, argc, argv);
|
|
#else
|
|
const float4_t result = thunkEnterVECR_adapter((void*) env->method->_native.thunker, env, argc, argv);
|
|
#endif
|
|
env->debugExit(&csn);
|
|
return result;
|
|
}
|
|
#endif // VMCFG_FLOAT
|
|
#endif
|
|
|
|
void BaseExecMgr::verifyNative(MethodInfo* m, MethodSignaturep ms)
|
|
{
|
|
#ifdef DEBUGGER
|
|
if (core->debugger())
|
|
{
|
|
#ifdef VMCFG_FLOAT
|
|
switch( ReturnType(ms) ){
|
|
case kIntegerRetType:
|
|
setNative(m, debugEnterExitWrapper32);
|
|
break;
|
|
case kFloatingPointRetType:
|
|
setNative(m, (GprMethodProc) debugEnterExitWrapperN);
|
|
break;
|
|
case kSIMDRetType:
|
|
setNative(m, (GprMethodProc) debugEnterVECR_adapter);
|
|
break;
|
|
}
|
|
#else
|
|
if (ms->returnTraitsBT() == BUILTIN_number)
|
|
setNative(m, (GprMethodProc) debugEnterExitWrapperN);
|
|
else
|
|
setNative(m, debugEnterExitWrapper32);
|
|
#endif // VMCFG_FLOAT
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
(void)ms;
|
|
#if defined(VMCFG_TELEMETRY_SAMPLER) && !defined(VMCFG_AOT)
|
|
setNative(m, core->samplerEnabled ? (GprMethodProc) m->_native.samplerThunker
|
|
: (GprMethodProc) m->_native.thunker);
|
|
#else
|
|
setNative(m, (GprMethodProc) m->_native.thunker);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#ifndef VMCFG_NANOJIT
|
|
|
|
// Install the generic, interpretive invoker for a native method.
|
|
// FIXME: Bug 529832 - We could specialize several common cases without JIT compiling.
|
|
void BaseExecMgr::setNative(MethodInfo* m, GprMethodProc p)
|
|
{
|
|
m->_implGPR = p;
|
|
m->_invoker = invokeGeneric;
|
|
}
|
|
|
|
// Without a JIT, we don't need to build any IMTs.
|
|
void BaseExecMgr::notifyVTableResolved(VTable*)
|
|
{}
|
|
|
|
bool BaseExecMgr::isJitEnabled() const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
#endif
|
|
|
|
// Only unbox the value (convert atom to native representation), coerce
|
|
// must have already happened.
|
|
Atom* FASTCALL BaseExecMgr::unbox1(Atom atom, Traits* t, Atom* arg0)
|
|
{
|
|
// Atom must be correct type already, we're just unboxing it.
|
|
AvmAssert(AvmCore::istype(atom, t) || AvmCore::isNullOrUndefined(atom));
|
|
switch (Traits::getBuiltinType(t))
|
|
{
|
|
case BUILTIN_any:
|
|
case BUILTIN_object:
|
|
case BUILTIN_void:
|
|
// My, that was easy.
|
|
break;
|
|
|
|
case BUILTIN_boolean:
|
|
atom = (Atom) ((atom>>3) != 0);
|
|
break;
|
|
|
|
case BUILTIN_int:
|
|
atom = AvmCore::integer_i(atom);
|
|
break;
|
|
|
|
case BUILTIN_uint:
|
|
atom = AvmCore::integer_u(atom);
|
|
break;
|
|
|
|
case BUILTIN_number:
|
|
{
|
|
#ifdef AVMPLUS_64BIT
|
|
AvmAssert(sizeof(Atom) == sizeof(double));
|
|
union
|
|
{
|
|
double d;
|
|
Atom a;
|
|
};
|
|
d = AvmCore::number_d(atom);
|
|
atom = a;
|
|
#else
|
|
AvmAssert(sizeof(Atom)*2 == sizeof(double));
|
|
union
|
|
{
|
|
double d;
|
|
Atom a[2];
|
|
};
|
|
d = AvmCore::number_d(atom);
|
|
arg0[0] = a[0];
|
|
arg0 += 1;
|
|
atom = a[1]; // Fall through, will be handled at end.
|
|
#endif
|
|
break;
|
|
}
|
|
#ifdef VMCFG_FLOAT
|
|
case BUILTIN_float:
|
|
{
|
|
union
|
|
{
|
|
float f;
|
|
Atom a;
|
|
};
|
|
f = AvmCore::atomToFloat(atom);
|
|
atom = a;
|
|
break;
|
|
}
|
|
case BUILTIN_float4:
|
|
{
|
|
union{
|
|
float4_t f4;
|
|
Atom a[4];
|
|
};
|
|
const int nAtoms = sizeof(float4_t)/sizeof(Atom);
|
|
AvmAssert(sizeof(float4_t)%sizeof(Atom)==0);
|
|
f4 = AvmCore::atomToFloat4(atom);
|
|
int i;
|
|
for(i=0;i<nAtoms-1;i++)
|
|
*arg0++ = a[i];
|
|
atom = a[i]; // this will be handled below
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
default:
|
|
atom = (Atom)atomPtr(atom);
|
|
break;
|
|
}
|
|
// Every case increments by at least 1.
|
|
arg0[0] = atom;
|
|
return arg0+1;
|
|
}
|
|
|
|
// Coerce and unbox one argument.
|
|
// Note that some of these have (partial) guts of avmplus::coerce replicated here, for efficiency.
|
|
// If you find bugs here, you might need to update avmplus::coerce as well (and vice versa).
|
|
Atom* FASTCALL BaseExecMgr::coerceUnbox1(MethodEnv* env, Atom atom, Traits* t, Atom* args)
|
|
{
|
|
// Using computed-gotos here doesn't move the needle appreciably in testing.
|
|
switch (Traits::getBuiltinType(t))
|
|
{
|
|
case BUILTIN_any:
|
|
// My, that was easy.
|
|
break;
|
|
|
|
case BUILTIN_boolean:
|
|
atom = AvmCore::boolean(atom);
|
|
break;
|
|
|
|
case BUILTIN_int:
|
|
atom = AvmCore::integer(atom);
|
|
break;
|
|
|
|
case BUILTIN_uint:
|
|
atom = AvmCore::toUInt32(atom);
|
|
break;
|
|
|
|
case BUILTIN_namespace:
|
|
// Coerce undefined -> Namespace should yield null.
|
|
if (AvmCore::isNullOrUndefined(atom))
|
|
{
|
|
atom = 0;
|
|
break;
|
|
}
|
|
if (atomKind(atom) != kNamespaceType)
|
|
goto failure;
|
|
atom = (Atom)atomPtr(atom);
|
|
break;
|
|
|
|
#ifdef VMCFG_FLOAT
|
|
case BUILTIN_float:
|
|
{
|
|
union
|
|
{
|
|
float f;
|
|
Atom a;
|
|
};
|
|
f = (float) AvmCore::number(atom);
|
|
atom = a;
|
|
break;
|
|
}
|
|
case BUILTIN_float4:
|
|
{
|
|
const int nAtoms = sizeof(float4_t)/sizeof(Atom);
|
|
union{
|
|
float4_t f4;
|
|
Atom a[nAtoms];
|
|
};
|
|
AvmAssert(sizeof(float4_t)%sizeof(Atom)==0);
|
|
AvmCore::float4(&f4, atom);
|
|
int i;
|
|
for(i=0;i<nAtoms-1;i++)
|
|
*args++ = a[i];
|
|
atom = a[i]; // this will be handled below
|
|
break;
|
|
}
|
|
#endif // VMCFG_FLOAT
|
|
case BUILTIN_number:
|
|
{
|
|
#ifdef AVMPLUS_64BIT
|
|
AvmAssert(sizeof(Atom) == sizeof(double));
|
|
union
|
|
{
|
|
double d;
|
|
Atom a;
|
|
};
|
|
d = AvmCore::number(atom);
|
|
atom = a;
|
|
#else
|
|
AvmAssert(sizeof(Atom)*2 == sizeof(double));
|
|
union
|
|
{
|
|
double d;
|
|
Atom a[2];
|
|
};
|
|
d = AvmCore::number(atom);
|
|
args[0] = a[0];
|
|
args += 1;
|
|
atom = a[1]; // Fall thru, will be handled at end.
|
|
#endif
|
|
break;
|
|
}
|
|
case BUILTIN_object:
|
|
if (atom == undefinedAtom)
|
|
atom = nullObjectAtom;
|
|
break;
|
|
|
|
case BUILTIN_string:
|
|
atom = AvmCore::isNullOrUndefined(atom) ? NULL : (Atom)env->core()->string(atom);
|
|
break;
|
|
|
|
case BUILTIN_null:
|
|
case BUILTIN_void:
|
|
AvmAssert(!"illegal, should not happen");
|
|
atom = 0;
|
|
break;
|
|
|
|
case BUILTIN_math:
|
|
case BUILTIN_methodClosure:
|
|
case BUILTIN_qName:
|
|
case BUILTIN_vector:
|
|
case BUILTIN_vectordouble:
|
|
case BUILTIN_vectorint:
|
|
case BUILTIN_vectoruint:
|
|
#ifdef VMCFG_FLOAT
|
|
case BUILTIN_vectorfloat:
|
|
case BUILTIN_vectorfloat4:
|
|
#endif
|
|
case BUILTIN_xml:
|
|
case BUILTIN_xmlList:
|
|
// A few intrinsic final classes can skip subtypeof calls.
|
|
if (AvmCore::isNullOrUndefined(atom))
|
|
{
|
|
atom = 0;
|
|
break;
|
|
}
|
|
else if (atomKind(atom) == kObjectType)
|
|
{
|
|
Traits* actual = AvmCore::atomToScriptObject(atom)->traits();
|
|
AvmAssert(actual->final);
|
|
if (actual == t)
|
|
{
|
|
atom = (Atom)atomPtr(atom);
|
|
break;
|
|
}
|
|
}
|
|
// Didn't break? that's a failure.
|
|
goto failure;
|
|
|
|
case BUILTIN_date:
|
|
case BUILTIN_array:
|
|
case BUILTIN_class:
|
|
case BUILTIN_error:
|
|
case BUILTIN_function:
|
|
case BUILTIN_none:
|
|
case BUILTIN_regexp:
|
|
case BUILTIN_vectorobj: // Unlike other vector types, vectorobj is NOT final.
|
|
if (AvmCore::isNullOrUndefined(atom))
|
|
{
|
|
atom = 0;
|
|
break;
|
|
}
|
|
else if (atomKind(atom) == kObjectType)
|
|
{
|
|
Traits* actual = AvmCore::atomToScriptObject(atom)->traits();
|
|
if (actual->subtypeof(t))
|
|
{
|
|
atom = (Atom)atomPtr(atom);
|
|
break;
|
|
}
|
|
}
|
|
// Didn't break? that's a failure.
|
|
goto failure;
|
|
default:
|
|
break;
|
|
}
|
|
// Every case increments by at least 1.
|
|
args[0] = atom;
|
|
return args+1;
|
|
|
|
failure:
|
|
AvmCore* core = env->core();
|
|
env->toplevel()->throwTypeError(kCheckTypeFailedError, core->atomToErrorString(atom), core->toErrorString(t));
|
|
return unreachableAtom;
|
|
}
|
|
|
|
// Coerce an argument to an expected type, but keep it represented as Atom.
|
|
// Note that this function is (currently) only used for interpreted functions.
|
|
inline Atom coerceAtom(AvmCore* core, Atom atom, Traits* t, Toplevel* toplevel)
|
|
{
|
|
switch (Traits::getBuiltinType(t))
|
|
{
|
|
case BUILTIN_number:
|
|
return (atomKind(atom) == kDoubleType) ? atom : core->numberAtom(atom);
|
|
case BUILTIN_int:
|
|
return (atomKind(atom) == kIntptrType) ? atom : core->intAtom(atom);
|
|
case BUILTIN_uint:
|
|
return (atomKind(atom) == kIntptrType && atom >= 0) ? atom : core->uintAtom(atom);
|
|
case BUILTIN_boolean:
|
|
return (atomKind(atom) == kBooleanType) ? atom : AvmCore::booleanAtom(atom);
|
|
case BUILTIN_object:
|
|
return (atom == undefinedAtom) ? nullObjectAtom : atom;
|
|
case BUILTIN_any:
|
|
return atom;
|
|
#ifdef VMCFG_FLOAT
|
|
case BUILTIN_float:
|
|
return AvmCore::isFloat(atom) ? atom : core->floatAtom(atom);
|
|
case BUILTIN_float4:
|
|
return AvmCore::isFloat4(atom) ? atom : core->float4Atom(atom);
|
|
#endif // VMCFG_FLOAT
|
|
default:
|
|
return toplevel->coerce(atom, t);
|
|
}
|
|
}
|
|
|
|
Atom BaseExecMgr::endCoerce(MethodEnv* env, int32_t argc, uint32_t *ap, MethodSignaturep ms)
|
|
{
|
|
// We know we have verified the method, so we can go right into it.
|
|
AvmCore* core = env->core();
|
|
const int32_t bt = ms->returnTraitsBT();
|
|
|
|
switch(bt){
|
|
case BUILTIN_number:
|
|
{
|
|
STACKADJUST(); // align stack for 32-bit Windows and MSVC compiler
|
|
double d = (*env->method->_implFPR)(env, argc, ap);
|
|
STACKRESTORE();
|
|
return core->doubleToAtom(d);
|
|
}
|
|
#ifdef VMCFG_FLOAT
|
|
case BUILTIN_float:
|
|
{
|
|
STACKADJUST(); // align stack for 32-bit Windows and MSVC compiler
|
|
// WARNING: This assumes that the calling conventions of SinglePrecisionFprMethodProc and FprMethodProc
|
|
// are identical, or at least compatible. I.e. the register used to return the "double" value is identical with
|
|
// (or a superset of) the register used to return a float value. This assumption is true for ARM (softfloat, hardfloat ABIs)
|
|
// x86 and x64 (Mac, Windows, Linux) at least. Must double-check for other platforms!! (most notably, TODO: MIPS)
|
|
typedef float (*SinglePrecisionFprMethodProc)(MethodEnv*, int32_t, uint32_t *);
|
|
float f = reinterpret_cast<SinglePrecisionFprMethodProc> (*env->method->_implFPR)(env, argc, ap);
|
|
STACKRESTORE();
|
|
return core->floatToAtom(f);
|
|
}
|
|
case BUILTIN_float4:
|
|
{
|
|
STACKADJUST(); // align stack for 32-bit Windows and MSVC compiler
|
|
float4_t f4 = thunkEnterVECR_adapter((void*)env->method->_implVECR, env, argc, ap);
|
|
STACKRESTORE();
|
|
return core->float4ToAtom(f4);
|
|
}
|
|
#endif // VMCFG_FLOAT
|
|
default:
|
|
{
|
|
STACKADJUST(); // align stack for 32-bit Windows and MSVC compiler
|
|
const Atom i = (*env->method->_implGPR)(env, argc, ap);
|
|
STACKRESTORE();
|
|
|
|
switch (bt)
|
|
{
|
|
case BUILTIN_int:
|
|
return core->intToAtom((int32_t)i);
|
|
case BUILTIN_uint:
|
|
return core->uintToAtom((uint32_t)i);
|
|
case BUILTIN_boolean:
|
|
return i ? trueAtom : falseAtom;
|
|
case BUILTIN_any:
|
|
case BUILTIN_object:
|
|
case BUILTIN_void:
|
|
return (Atom)i;
|
|
case BUILTIN_string:
|
|
return ((Stringp)i)->atom();
|
|
case BUILTIN_namespace:
|
|
return ((Namespace*)i)->atom();
|
|
default:
|
|
return ((ScriptObject*)i)->atom();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
inline void checkArgc(MethodEnv *env, int32_t argc, MethodSignaturep ms)
|
|
{
|
|
// Getting toplevel() is slightly more expensive than it used to be (more indirection)...
|
|
// so only extract in the (rare) event of an exception.
|
|
|
|
if (!ms->argcOk(argc))
|
|
env->argcError(argc);
|
|
|
|
// Should no longer be possible to have mismatched scopes; we should reject any such
|
|
// functions in makeIntoPrototypeFunction().
|
|
AvmAssert(env->method->declaringScope()->equals(env->scope()->scopeTraits()));
|
|
}
|
|
|
|
REALLY_INLINE size_t calcAtomAllocSize(int32_t argc, int32_t extra = 0)
|
|
{
|
|
// We need to check for integer overflow here; it's possible for someone to use Function.apply()
|
|
// to pass an argument Array with a huge value for length (but sparsely populated).
|
|
// But since "extra" is int32 and sizeof(Atom) is known here, we can do an efficient check
|
|
// without a full 64-bit multiply, as CheckForCallocSizeOverflow() does. (Note: yes, I know
|
|
// that a good compiler should optimize unsigned-divide-by-power-of-2 into a shift, but this is
|
|
// a hot path, so let's be skeptical and force the issue.)
|
|
#ifdef VMCFG_64BIT
|
|
uint32_t const kAtomSizeLog2 = 3;
|
|
#else
|
|
uint32_t const kAtomSizeLog2 = 2;
|
|
#endif
|
|
MMGC_STATIC_ASSERT(sizeof(Atom) == (1<<kAtomSizeLog2));
|
|
if (uint32_t(argc) > ((MMgc::GCHeap::kMaxObjectSize - size_t(extra)) >> kAtomSizeLog2))
|
|
MMgc::GCHeap::SignalObjectTooLarge();
|
|
return (argc << kAtomSizeLog2) + extra;
|
|
}
|
|
|
|
// static
|
|
inline size_t BaseExecMgr::startCoerce(MethodEnv *env, int32_t argc, MethodSignaturep ms)
|
|
{
|
|
checkArgc(env, argc, ms);
|
|
|
|
// Compute the number of rest arguments present.
|
|
const int32_t param_count = ms->param_count();
|
|
const int32_t extra = argc > param_count ? argc - param_count : 0;
|
|
AvmAssert(ms->rest_offset() > 0 && extra >= 0);
|
|
const int32_t rest_offset = ms->rest_offset();
|
|
return calcAtomAllocSize(extra, rest_offset);
|
|
}
|
|
|
|
// Specialized to be called from Function.apply().
|
|
Atom BaseExecMgr::apply(MethodEnv* env, Atom thisArg, ArrayObject *a)
|
|
{
|
|
int32_t argc = a->getLength();
|
|
if (argc == 0)
|
|
return env->coerceEnter(thisArg);
|
|
|
|
if (env->method->_apply_fastpath) {
|
|
// JIT or native method. We specialize this path to avoid
|
|
// calling alloca twice; once to unpack atoms from a[], then
|
|
// again to unpack from Atom[] to native values.
|
|
MethodSignaturep ms = env->get_ms();
|
|
const size_t extra_sz = startCoerce(env, argc, ms);
|
|
MMgc::GC::AllocaAutoPtr _ap;
|
|
uint32_t *ap = (uint32_t *)avmStackAlloc(core, _ap, extra_sz);
|
|
unboxCoerceArgs(env, thisArg, a, ap, ms);
|
|
return endCoerce(env, argc, ap, ms);
|
|
}
|
|
|
|
// Caller will coerce instance if necessary, so make sure it was done.
|
|
AvmAssertMsgCanThrow(thisArg == coerce(env, thisArg, env->get_ms()->paramTraits(0)),"",env->core());
|
|
|
|
// Tail call inhibited by local allocation/deallocation.
|
|
MMgc::GC::AllocaAutoPtr _atomv;
|
|
Atom* atomv = (Atom*)avmStackAllocArray(core, _atomv, (argc+1), sizeof(Atom));
|
|
atomv[0] = thisArg;
|
|
for (int32_t i=0 ; i < argc ; i++ )
|
|
atomv[i+1] = a->getUintProperty(i);
|
|
return env->coerceEnter(argc, atomv);
|
|
}
|
|
|
|
// Specialized to be called from Function.call().
|
|
Atom BaseExecMgr::call(MethodEnv* env, Atom thisArg, int argc, Atom *argv)
|
|
{
|
|
if (argc == 0)
|
|
return env->coerceEnter(thisArg);
|
|
|
|
if (env->method->_apply_fastpath) {
|
|
// JIT or native method. We specialize this path to avoid
|
|
// calling alloca twice; once to unpack atoms from a[], then
|
|
// again to unpack from Atom[] to native values.
|
|
MethodSignaturep ms = env->get_ms();
|
|
const size_t extra_sz = startCoerce(env, argc, ms);
|
|
MMgc::GC::AllocaAutoPtr _ap;
|
|
uint32_t *ap = (uint32_t *)avmStackAlloc(core, _ap, extra_sz);
|
|
unboxCoerceArgs(env, thisArg, argc, argv, ap, ms);
|
|
return endCoerce(env, argc, ap, ms);
|
|
}
|
|
|
|
// Caller will coerce instance if necessary, so make sure it was done.
|
|
AvmAssertMsgCanThrow(thisArg == coerce(env, thisArg, env->get_ms()->paramTraits(0)),"",env->core());
|
|
|
|
// Tail call inhibited by local allocation/deallocation.
|
|
MMgc::GC::AllocaAutoPtr _atomv;
|
|
Atom* atomv = (Atom*)avmStackAllocArray(core, _atomv, (argc+1), sizeof(Atom));
|
|
atomv[0] = thisArg;
|
|
VMPI_memcpy(atomv+1, argv, sizeof(Atom)*argc);
|
|
return env->coerceEnter(argc, atomv);
|
|
}
|
|
|
|
// Optimization opportunities: since we call interpBoxed() directly, it is
|
|
// probably possible to allocate its stack frame here and pass it in.
|
|
// If we do so then interpBoxed() should deallocate it. This affords us
|
|
// the optimization of getting rid of alloca() allocation here,
|
|
// which means improved tail calls for one. For another, if the argv
|
|
// pointer points into the stack segment s.t. argv+argc+1 equals the
|
|
// current stack pointer then the stack may be extended in place
|
|
// provided there's space. But that optimization may equally well
|
|
// be performed inside interpBoxed(), and in fact if we alloc temp
|
|
// space on the alloca stack here then interpBoxed() would always perform
|
|
// that optimization. So we'd just be moving the decision into interpBoxed().
|
|
|
|
// note that GCC typically restricts tailcalls to functions with similar signatures
|
|
// ("sibcalls") -- see http://www.ddj.com/architect/184401756 for a useful explanation.
|
|
// anyway, since we really want interpBoxed to be a tailcall from
|
|
// here, be sure to keep it using a compatible signature...
|
|
Atom BaseExecMgr::invokeInterp(MethodEnv* env, int32_t argc, Atom* atomv)
|
|
{
|
|
// The tail call to interpBoxed is important in order to keep stack consumption down in an
|
|
// interpreter-only configuration, but it's good always.
|
|
|
|
AvmAssert(isInterpreted(env));
|
|
AvmCore* core = env->core();
|
|
Toplevel* toplevel = env->toplevel();
|
|
|
|
MethodSignaturep ms = env->get_ms();
|
|
checkArgc(env, argc, ms);
|
|
|
|
// Caller will coerce instance if necessary, so make sure it was done.
|
|
AvmAssertMsgCanThrow(atomv[0] == coerce(env, atomv[0], ms->paramTraits(0)),"",env->core());
|
|
|
|
const int32_t param_count = ms->param_count();
|
|
const int32_t end = argc >= param_count ? param_count : argc;
|
|
for (int32_t i=1 ; i <= end ; i++ )
|
|
atomv[i] = coerceAtom(core, atomv[i], ms->paramTraits(i), toplevel);
|
|
return interpBoxed(env, argc, atomv);
|
|
}
|
|
|
|
// Specialized copy of invoke_interp() when there are no typed args.
|
|
Atom BaseExecMgr::invokeInterpNoCoerce(MethodEnv* env, int32_t argc, Atom* atomv)
|
|
{
|
|
// The tail call to interpBoxed is important in order to keep stack consumption down in an
|
|
// interpreter-only configuration, but it's good always.
|
|
|
|
MethodSignaturep ms = env->get_ms();
|
|
checkArgc(env, argc, ms);
|
|
|
|
#ifdef DEBUG
|
|
AvmAssert(isInterpreted(env));
|
|
// caller will coerce instance if necessary, so make sure it was done.
|
|
AvmAssertMsgCanThrow(atomv[0] == coerce(env, atomv[0], ms->paramTraits(0)),"",env->core());
|
|
|
|
const int32_t param_count = ms->param_count();
|
|
const int32_t end = argc >= param_count ? param_count : argc;
|
|
for (int32_t i=1 ; i <= end ; i++ )
|
|
AvmAssert(ms->paramTraits(i) == NULL);
|
|
#endif
|
|
|
|
return interpBoxed(env, argc, atomv);
|
|
}
|
|
|
|
// Invoker for native or jit code used before we have jit-compiled,
|
|
// or after JIT compilation of the invoker has failed.
|
|
Atom BaseExecMgr::invokeGeneric(MethodEnv *env, int32_t argc, Atom* atomv)
|
|
{
|
|
MethodSignaturep ms = env->get_ms();
|
|
const size_t extra_sz = startCoerce(env, argc, ms);
|
|
MMgc::GC::AllocaAutoPtr _ap;
|
|
uint32_t *ap = (uint32_t *)avmStackAlloc(env->core(), _ap, extra_sz);
|
|
unboxCoerceArgs(env, argc, atomv, ap, ms);
|
|
return endCoerce(env, argc, ap, ms);
|
|
}
|
|
|
|
// Convert atoms to native args. argc is the number of
|
|
// args, not counting the instance which is arg[0]. the
|
|
// layout is [instance][arg1..argN]
|
|
void BaseExecMgr::unboxCoerceArgs(MethodEnv* env, int32_t argc, Atom* in, uint32_t *argv, MethodSignaturep ms)
|
|
{
|
|
Atom* args = (Atom*)argv;
|
|
|
|
const int32_t param_count = ms->param_count();
|
|
int32_t end = argc >= param_count ? param_count : argc;
|
|
args = unbox1(in[0], ms->paramTraits(0), args); // no need to coerce
|
|
for (int32_t i=1; i <= end; i++)
|
|
args = coerceUnbox1(env, in[i], ms->paramTraits(i), args);
|
|
while (end < argc)
|
|
*args++ = in[++end];
|
|
}
|
|
|
|
// Specialized for Function.apply().
|
|
void BaseExecMgr::unboxCoerceArgs(MethodEnv* env, Atom thisArg, ArrayObject *a, uint32_t *argv, MethodSignaturep ms)
|
|
{
|
|
int32_t argc = a->getLength();
|
|
|
|
Atom *args = unbox1(thisArg, ms->paramTraits(0), (Atom *) argv);
|
|
|
|
const int32_t param_count = ms->param_count();
|
|
int32_t end = argc >= param_count ? param_count : argc;
|
|
for (int32_t i=0; i < end; i++)
|
|
args = coerceUnbox1(env, a->getUintProperty(i), ms->paramTraits(i+1), args);
|
|
while (end < argc)
|
|
*args++ = a->getUintProperty(end++);
|
|
}
|
|
|
|
// Specialized for Function.call().
|
|
void BaseExecMgr::unboxCoerceArgs(MethodEnv* env, Atom thisArg, int32_t argc, Atom* in, uint32_t *argv, MethodSignaturep ms)
|
|
{
|
|
Atom *args = unbox1(thisArg, ms->paramTraits(0), (Atom *) argv);
|
|
|
|
const int32_t param_count = ms->param_count();
|
|
int32_t end = argc >= param_count ? param_count : argc;
|
|
for (int32_t i=0; i < end; i++)
|
|
args = coerceUnbox1(env, in[i], ms->paramTraits(i+1), args);
|
|
while (end < argc)
|
|
*args++ = in[end++];
|
|
}
|
|
|
|
} // namespace avmplus
|