Files
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

1239 lines
41 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/. */
//
// Non-Inline implementations of functions defined in instr.h
//
#include "avmplus.h"
#ifdef PERFM
#define DOPROF
#endif /* PERFM */
#include "../vprof/vprof.h"
namespace avmplus
{
template <class E>
Atom callprop_b(E env, Atom base, const Multiname* multiname, int argc, Atom* atomv, VTable* vtable, Binding b)
{
AssertNotNull(base);
switch (AvmCore::bindingKind(b))
{
case BKIND_METHOD:
return call_method_binding(base, vtable, b, argc, atomv);
case BKIND_VAR:
case BKIND_CONST:
return call_slot_binding(env, base, b, argc, atomv);
case BKIND_GET:
case BKIND_GETSET:
return op_call(env, call_get_binding(base, vtable, b), argc, atomv);
case BKIND_SET:
// read on write-only property
env->toplevel()->throwReferenceError(kWriteOnlyError, multiname, vtable->traits);
return unreachableAtom;
default:
if (isObjectPtr(base)) {
return call_obj_dynamic(base, multiname, argc, atomv);
} else {
return call_prim_dynamic(env, base, multiname, argc, atomv);
}
}
}
template Atom callprop_b(Toplevel*, Atom, const Multiname*, int, Atom*, VTable*, Binding);
/**
* find the binding for a property given a full multiname reference. The lookup
* must produce a single binding, or it's an error. Note that the name could be
* bound to the same binding in multiple namespaces.
*/
template <class E>
Binding getBinding(E env, Traits* traits, const Multiname* ref)
{
Binding b = BIND_NONE;
if (traits && ref->isBinding())
{
// note, you no longer must resolve the traits in order to find the binding!
TraitsBindingsp tb = traits->getTraitsBindings();
if (!ref->isNsset())
{
b = tb->findBinding(ref->getName(), ref->getNamespace());
}
else
{
b = tb->findBinding(ref->getName(), ref->getNsset());
if (b == BIND_AMBIGUOUS)
{
// ERROR. more than one binding is available. throw exception.
env->toplevel()->throwTypeError(kAmbiguousBindingError, env->core()->toErrorString(ref));
}
}
}
return b;
}
template Binding getBinding(const Toplevel*, Traits*, const Multiname*);
template Binding getBinding(Toplevel*, Traits*, const Multiname*);
template <class E>
VTable* toVTable(E env, Atom atom)
{
if (!AvmCore::isNullOrUndefined(atom))
{
if (atomKind(atom) == kObjectType)
return AvmCore::atomToScriptObject(atom)->vtable;
Toplevel* toplevel = env->toplevel();
switch (atomKind(atom))
{
case kNamespaceType:
return toplevel->namespaceClass()->ivtable();
case kStringType:
return toplevel->stringClass()->ivtable();
case kBooleanType:
return toplevel->booleanClass()->ivtable();
case kIntptrType:
case kDoubleType:
// ISSUE what about int?
return toplevel->numberClass()->ivtable();
#ifdef VMCFG_FLOAT
case kSpecialBibopType:
if( bibopKind(atom) == kBibopFloatType )
return toplevel->floatClass()->ivtable();
if( bibopKind(atom) == kBibopFloat4Type )
return toplevel->float4Class()->ivtable();
AvmAssertMsg(false,"Unhandled bibop kind");
#endif // VMCFG_FLOAT
}
}
// TypeError in ECMA
env->toplevel()->throwTypeError(
(atom == undefinedAtom) ? kConvertUndefinedToObjectError :
kConvertNullToObjectError);
// unreachable
return NULL;
}
template VTable* toVTable(Toplevel*, Atom);
template VTable* toVTable(MethodEnv*, Atom);
template <class E>
Atom op_applytype(E env, Atom factory, int argc, Atom* args)
{
if (AvmCore::isObject(factory))
return AvmCore::atomToScriptObject(factory)->applyTypeArgs(argc, args);
env->toplevel()->throwTypeError(kTypeAppOfNonParamType);
return unreachableAtom;
}
template Atom op_applytype(Toplevel*, Atom, int, Atom*);
template Atom op_applytype(MethodEnv*, Atom, int, Atom*);
template <class E>
Atom astype_late(E caller_env, Atom value, Atom type) {
Traits* itraits = caller_env->toplevel()->toClassITraits(type);
return AvmCore::astype(value, itraits);
}
template Atom astype_late(MethodEnv*, Atom, Atom);
template <class E>
Atom instanceof(E caller_env, Atom val, Atom ctor) {
return caller_env->toplevel()->instanceof(val, ctor);
}
template Atom instanceof(MethodEnv*, Atom, Atom);
template <class E>
Atom op_in(E caller_env, Atom name, Atom obj) {
return caller_env->toplevel()->in_operator(name, obj);
}
template Atom op_in(MethodEnv*, Atom, Atom);
/**
* finddef_miss handles the uncommon case when finddef_cache finds a null
* pointer. Its a separate function to avoid polluting finddef_cache's
* stack frame with spilled registers from the slow path.
*/
NO_INLINE
ScriptObject* FASTCALL finddef_miss(ScriptObject** obj_ptr, MethodFrame* frame) {
MethodEnv* env = frame->env();
FinddefTable& table = *env->abcEnv()->finddefTable();
// recover name_id by figuring out where obj_ptr points in the table.
// name_id is not passed in so we can comply with FASTCALL's
// limit of two register args.
ScriptObject** obj0_ptr = (ScriptObject**) &table[0];
int name_id = int(obj_ptr - obj0_ptr);
// execute finddef and save the result
const Multiname* name = env->method->pool()->precomputedMultiname(name_id);
ScriptObject* obj = env->finddef(name);
WBRC(env->core()->gc, &table, &table[name_id].object, obj);
return obj;
}
/**
* called by JIT or Interpreter code for OP_finddef. The JIT code has a stashed
* pointer to the whole FinddefTable, and passes in a pointer to the desired
* element. (we do this to keep this call to 2 parameters). We use
* MethodFrame* here instead of MethodEnv* because its cheaper for the
* JIT to rematerialize MethodFrame* than to load MethodEnv*. from stack.
*/
ScriptObject* FASTCALL finddef_cache(ScriptObject** obj_ptr,
MethodFrame* frame) {
ScriptObject* obj = *obj_ptr;
if (obj)
return obj;
return finddef_miss(obj_ptr, frame);
}
Atom constructprop(Toplevel* toplevel, const Multiname* multiname, int argc, Atom* atomv, VTable* vtable)
{
Binding b = getBinding(toplevel, vtable->traits, multiname);
Atom obj = atomv[0];
switch (AvmCore::bindingKind(b))
{
case BKIND_METHOD:
{
// can't invoke method as constructor
MethodEnv* env = vtable->methods[AvmCore::bindingToMethodId(b)];
toplevel->throwTypeError(kCannotCallMethodAsConstructor, toplevel->core()->toErrorString(env->method));
}
case BKIND_VAR:
case BKIND_CONST:
{
ScriptObject* ctor = AvmCore::atomToScriptObject(obj)->getSlotObject(AvmCore::bindingToSlotId(b));
// This code is a bit subtle. Note that ScriptObject::getSlotObject() will return NULL if the
// slot is an SST_scriptobject and is NULL, or an SST_atom that is null, undefined, or not
// an object. These subsume the test isObject(ctor) in op_construct(), which is inlined here.
// We once included an extra check, resulting in throwing a different exception when taking this
// path in interpreted code. We must emulate this old behavior for backward compatibility.
// It is unfortunate that CLASS_TYPE and FUNCTION_TYPE expand into references to 'core', otherwise
// we could push the next line down into the exceptional path.
AvmCore* core = toplevel->core();
if (!ctor || (!ctor->traits()->subtypeof(CLASS_TYPE) && !ctor->traits()->subtypeof(FUNCTION_TYPE))) {
// We know that an exception will be thrown eventually, so now do the version check.
if (core->currentBugCompatibility()->bugzilla456852a) {
// Correct behavior is to throw kConstructOfNonFunctionError as op_construct would do.
if (!ctor) {
toplevel->throwTypeError(kConstructOfNonFunctionError);
}
} else {
// Stay compatible with old behavior.
toplevel->throwTypeError(kNotConstructorError, core->toErrorString(multiname));
}
}
// inlined equivalent of op_construct
return ctor->construct(argc, atomv);
}
case BKIND_GET:
case BKIND_GETSET:
{
// Invoke the getter
int m = AvmCore::bindingToGetterId(b);
MethodEnv *f = vtable->methods[m];
Atom ctor = f->coerceEnter(obj);
return op_construct(toplevel, ctor, argc, atomv);
}
case BKIND_SET:
{
// read on write-only property
toplevel->throwReferenceError(kWriteOnlyError, multiname, vtable->traits);
}
default:
{
ScriptObject* o;
if (atomKind(obj)==kObjectType)
{
o = AvmCore::atomToScriptObject(obj);
}
else
{
#ifdef VMCFG_FLOAT
if (AvmCore::isFloat4(obj))
{
// See FIXME in Toplevel::getproperty for why this is "correct".
if (multiname->isValidDynamicName())
{
uint32_t index;
if (AvmCore::getIndexFromAtom(multiname->getName()->atom(), &index))
toplevel->throwTypeError(kConstructOfNonFunctionError);
}
}
#endif
// primitive types are not dynamic, so we can go directly
// to their __proto__ object
o = toplevel->toPrototype(obj);
}
atomv[0] = o->atom(); // replace receiver
Atom ctor = o->getMultinameProperty(multiname);
return op_construct(toplevel, ctor, argc, atomv);
}
}
}
/**
* implements ECMA implicit coersion. returns the coerced value,
* or throws a TypeError if coersion is not possible.
*/
// NOTE: parts of this function have been explicitly inlined into BaseExecMgr::unbox1 for
// efficiency. If you change/fix this method, you may need to change/fix MethodEnv::unbox1 as well.
Atom coerceImpl(const Toplevel* toplevel, Atom atom, Traits* expected)
{
AvmAssert(expected != NULL);
AvmAssert(!AvmCore::atomDoesNotNeedCoerce(atom, BuiltinType(expected->builtinType)));
Traits* actual;
AvmCore* core = toplevel->core();
// these types always succeed
switch (expected->builtinType)
{
case BUILTIN_boolean:
return AvmCore::booleanAtom(atom);
case BUILTIN_number:
return core->numberAtom(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
case BUILTIN_string:
return AvmCore::isNullOrUndefined(atom) ? nullStringAtom : core->string(atom)->atom();
case BUILTIN_int:
return core->intAtom(atom);
case BUILTIN_uint:
return core->uintAtom(atom);
case BUILTIN_object:
return atom == undefinedAtom ? nullObjectAtom : atom;
}
// else fall thru
if (AvmCore::isNullOrUndefined(atom))
return (expected->builtinType == BUILTIN_void) ? undefinedAtom : nullObjectAtom;
switch (atomKind(atom))
{
case kStringType:
actual = core->traits.string_itraits;
break;
case kBooleanType:
actual = core->traits.boolean_itraits;
break;
case kDoubleType:
actual = core->traits.number_itraits;
break;
case kIntptrType:
actual = core->traits.int_itraits;
break;
case kNamespaceType:
actual = core->traits.namespace_itraits;
break;
case kObjectType:
actual = AvmCore::atomToScriptObject(atom)->traits();
break;
#ifdef VMCFG_FLOAT
case kSpecialBibopType:
AvmAssert(atom != AtomConstants::undefinedAtom);
if(bibopKind(atom) == kBibopFloatType)
{
actual = core->traits.float_itraits;
break;
}
if(bibopKind(atom) == kBibopFloat4Type)
{
actual = core->traits.float4_itraits;
break;
}
// fall trhu to default
#endif
default:
// unexpected atom type
AvmAssert(false);
return unreachableAtom;
}
if (!actual->subtypeof(expected))
{
// failed
#ifdef AVMPLUS_VERBOSE
//core->console << "checktype failed " << expected << " <- " << atom << "\n";
#endif
toplevel->throwTypeError(kCheckTypeFailedError, core->atomToErrorString(atom), core->toErrorString(expected));
}
return atom;
}
#define AssertObjTraits(env,t) \
AvmAssert(t && !t->isMachineType());\
AvmAssert(t != env->core()->traits.string_itraits);\
AvmAssert(t != env->core()->traits.namespace_itraits);
static void throwCheckTypeError(MethodEnv* env, Atom atom, Traits* t)
{
AvmCore *core = env->core();
env->toplevel()->throwTypeError(kCheckTypeFailedError,
core->atomToErrorString(atom),
core->toErrorString(t));
}
void coerceobj_obj(MethodEnv* caller_env, ScriptObject* obj, Traits* t)
{
AssertObjTraits(caller_env, t);
if (obj && !obj->traits()->subtypeof(t))
throwCheckTypeError(caller_env, obj->atom(), t);
}
void coerceobj_atom(MethodEnv *env, Atom atom, Traits* t)
{
AssertObjTraits(env, t);
if (!AvmCore::isNullOrUndefined(atom) &&
(atomKind(atom) != kObjectType || !atomObj(atom)->traits()->subtypeof(t)))
throwCheckTypeError(env, atom, t);
}
void coercens_atom(MethodEnv *env, Atom atom)
{
if (!AvmCore::isNullOrUndefined(atom) && atomKind(atom) != kNamespaceType)
throwCheckTypeError(env, atom, env->core()->traits.namespace_itraits);
}
#ifdef VMCFG_FLOAT
#define INTPTRASDOUBLE(v) ( (double) atomGetIntptr(v) )
template<typename T> T __multiply(T a, T b){ return a * b; }
template<typename T> T __divide(T a, T b){ return a / b; }
template<typename T> T __subtract(T a, T b){ return a - b; }
static double __modulo(double a, double b){ return MathUtils::mod(a,b); }
static float __modulo(float a, float b){ return (float) MathUtils::mod(a,b); }
static float4_t __modulo(const float4_t& a, const float4_t& b){
float x= __modulo(f4_x(a), f4_x(b));
float y= __modulo(f4_y(a), f4_y(b));
float z= __modulo(f4_z(a), f4_z(b));
float w= __modulo(f4_w(a), f4_w(b));
float4_t res = {x, y, z, w};
return res;
}
template<> float4_t __multiply<float4_t>(float4_t a, float4_t b) { return f4_mul(a,b);}
template<> float4_t __divide<float4_t> (float4_t a, float4_t b) { return f4_div(a,b);}
template<> float4_t __subtract<float4_t>(float4_t a, float4_t b) { return f4_sub(a,b);}
#define op_generic(name) \
Atom op_##name(AvmCore* core, Atom lhs, Atom rhs)\
{\
tagprof("op_##name val1", lhs);\
tagprof("op_##name val2", rhs);\
\
if (atomIsBothIntptr(lhs,rhs))\
{\
double res = __##name( INTPTRASDOUBLE(lhs), INTPTRASDOUBLE(rhs));\
intptr_t res_int = intptr_t(res);\
if( atomIsValidIntptrValue(res_int) && res==(double)res_int)\
return (res_int << 3) | kIntptrType;\
}\
\
lhs = AvmCore::primitive(lhs); \
rhs = AvmCore::primitive(rhs); \
\
if(AvmCore::isFloat4(lhs) || AvmCore::isFloat4(rhs)){\
float4_decl_v(lhs);\
float4_decl_v(rhs);\
return core->float4ToAtom( __##name(lhsv, rhsv) );\
}\
if(AvmCore::isFloat(lhs) && AvmCore::isFloat(rhs))\
return core->floatToAtom( __##name(AvmCore::atomToFloat(lhs),AvmCore::atomToFloat(rhs)));\
else\
return core->doubleToAtom(__##name( AvmCore::number(lhs), AvmCore::number(rhs)) );\
}
op_generic(divide);
op_generic(subtract);
op_generic(multiply);
op_generic(modulo);
Atom op_negate(AvmCore* core, Atom val) {
tagprof("op_negate val1", val);
val = AvmCore::primitive(val); // slow, but let's get over this...
if(AvmCore::isFloat(val)){
return core->floatToAtom(-AvmCore::atomToFloat(val));
}
if(AvmCore::isFloat4(val)){
const static float4_t Zero = {-0.0, -0.0, -0.0, -0.0};
float4_decl_v(val);
return core->float4ToAtom( f4_sub(Zero, valv) );
}
if(atomIsIntptr(val) && val != zeroIntAtom){
double res = - INTPTRASDOUBLE(val);
intptr_t res_int = intptr_t(res);
// note: we can't negate "0" as integer, but we should've guarded against that case with the zeroIntAtom test
AvmAssert(res_int != 0);
if( atomIsValidIntptrValue(res_int) && res == (double)res_int)
return (res_int << 3) | kIntptrType;
}
return core->doubleToAtom( - AvmCore::number(val) );
}
#endif
#ifdef VMCFG_FLOAT
template<bool float_enabled> Atom op_add_impl(AvmCore* core, Atom lhs, Atom rhs)
#else
Atom op_add(AvmCore* core, Atom lhs, Atom rhs)
#endif // VMCFG_FLOAT
{
tagprof("op_add val1", lhs);
tagprof("op_add val2", rhs);
#ifdef AVMPLUS_64BIT
// since 64-bit int atoms expect exactly 54 bits of precision, we want to shift bit 54+3 up into the sign bit and back down
# define SIGN_EXTEND(v) ((intptr_t(v) << (atomSignExtendShift-AtomConstants::kAtomTypeSize)) >> (atomSignExtendShift-AtomConstants::kAtomTypeSize))
#else
# define SIGN_EXTEND(v) (intptr_t(v))
#endif
// integer optimization based on the one from Interpreter.cpp, modified
// to reduce the # of alu instructions
if (atomIsBothIntptr(lhs,rhs))
{
// Subtract off one of the type tags so result will be properly tagged.
// Sign-extend 53-bit intptr value on 64-bit platforms.
intptr_t const sum = SIGN_EXTEND(lhs + rhs - kIntptrType);
// No overflow occurs if the signs of the arguments are different,
// or if the sign of the result matches that of the arguments.
// Rather than isolating the sign bits by masking off the others, we exploit
// the fact that the comparisons with zero implicitly ignore the other bits.
if ((lhs ^ rhs) < 0 || (lhs ^ sum) >= 0) {
// No overflow, sum is correctly tagged atom
return sum;
}
// Both integers, but overflow happens. Intentionally add these
// without casting to int32_t. If the sum of the shifted values overflow,
// we know the unshifted values will not overflow with a word-sized add.
return core->allocDouble(double(atomGetIntptr(lhs) + atomGetIntptr(rhs)));
}
if (AvmCore::isNumeric(lhs) && AvmCore::isNumeric(rhs))
{
// C++ porting note. if either side is undefined NaN then result must be NaN,
// which is assumed to be taken care of by IEEE 748 double add.
goto add_numbers;
}
if (AvmCore::isString(lhs) || AvmCore::isString(rhs) || AvmCore::isDate(lhs) || AvmCore::isDate(rhs))
{
goto concat_strings;
}
// then look for the more unlikely cases
// E4X, section 11.4.1, pg 53
if (AvmCore::isXMLorXMLList(lhs) && AvmCore::isXMLorXMLList(rhs))
{
XMLListObject *l = XMLListObject::create(core->GetGC(), atomObj(lhs)->toplevel()->xmlListClass());
l->_append(lhs);
l->_append(rhs);
return l->atom();
}
// to catch oddball cases like:
// function foo() { };
// foo.prototype.valueOf = function() { return new Object(); }
// foo.prototype.toString = function() { return 2; }
// print( new foo() + 33 ); // should be 35
//
// we need to follow the E3 spec:
// 1. call ToPrimitive() on lhs and rhs, then
// if L is String || R is String, concat, else add toNumber(lhs) to toNumber(rhs)
// ToPrimitive() will call [[DefaultValue]], which calls valueOf(). If the result is
// a primitive, return that value else call toString() instead.
// from E3:
// NOTE No hint is provided in the calls to ToPrimitive in steps 5 and 6. All native ECMAScript objects except Date objects handle
// the absence of a hint as if the hint Number were given; Date objects handle the absence of a hint as if the hint String were given.
// Host objects may handle the absence of a hint in some other manner.
lhs = AvmCore::primitive(lhs); // Date is handled above with the String argument case, we don't have to check for it here.
rhs = AvmCore::primitive(rhs);
if (!(AvmCore::isString(lhs) || AvmCore::isString(rhs)))
{
goto add_numbers;
}
// else fall thru to concat_strings
concat_strings:
return core->concatStrings(core->string(lhs), core->string(rhs))->atom();
add_numbers:
#ifdef VMCFG_FLOAT
// if both are floats, the addition is done on float; if any is float4, addition is done on float4
if(float_enabled)
{
if(AvmCore::isFloat4(lhs) || AvmCore::isFloat4(rhs))
{
float4_decl_v(lhs);
float4_decl_v(rhs);
return core->float4ToAtom( f4_add(lhsv , rhsv));
}
if(AvmCore::isFloat(lhs) && AvmCore::isFloat(rhs))
return core->floatToAtom(AvmCore::singlePrecisionFloat(lhs) + AvmCore::singlePrecisionFloat(rhs));
}
#endif
return core->doubleToAtom(AvmCore::number(lhs) + AvmCore::number(rhs));
}
#ifdef VMCFG_FLOAT
Atom op_add(AvmCore* core, Atom lhs, Atom rhs)
{
return op_add_impl<true>(core, lhs, rhs);
}
Atom op_add_nofloat(AvmCore* core, Atom lhs, Atom rhs)
{
return op_add_impl<false>( core, lhs, rhs);
}
#endif // VMCFG_FLOAT
#ifdef VMCFG_FASTPATH_ADD
// Auxilliary predicate for assertions.
// Return true if result of adding lhs and rhs will require
// a kDoubleType atom instead of a kIntptrType atom.
#if defined(VMCFG_FASTPATH_ADD_INLINE) && defined(DEBUG)
static bool addIntptrOverflow(intptr_t lhs, intptr_t rhs)
{
intptr_t const sum = lhs + rhs;
if ((lhs ^ rhs) < 0 || (lhs ^ sum) >= 0) {
// add overflowed, numerical result will not fit in intptr_t
return true;
}
// verify that tagged result will not fit in kIntptrType
intptr_t const extended = SIGN_EXTEND(sum << kAtomTypeSize) >> kAtomTypeSize;
return (sum == extended);
}
#endif
// Specialized helper functions called by code generated for the OP_add instruction.
// These take into account operands with known specific types and invariants previously
// established by inline code.
// atom + atom => atom
#ifdef VMCFG_FLOAT
template<bool float_support> Atom op_add_a_aa_impl(AvmCore* core, Atom lhs, Atom rhs)
#else
Atom op_add_a_aa(AvmCore* core, Atom lhs, Atom rhs)
#endif // VMCFG_FLOAT
{
#ifndef VMCFG_FASTPATH_ADD_INLINE
// Fastpath for intptr+intptr
if (atomIsBothIntptr(lhs, rhs))
{
intptr_t const sum = SIGN_EXTEND(lhs + rhs - kIntptrType);
if ((lhs ^ rhs) < 0 || (lhs ^ sum) >= 0) {
// No overflow, sum is correctly tagged atom
return sum;
}
// Overflow, allocate heap double
return core->allocDouble(double(atomGetIntptr(lhs) + atomGetIntptr(rhs)));
}
#else
// Inline code should handle intptr+intptr case, except where result must be double.
AvmAssert(!atomIsBothIntptr(lhs, rhs) || addIntptrOverflow(atomGetIntptr(lhs), atomGetIntptr(rhs)));
#endif
if (atomIsIntptr(lhs))
{
if (AvmCore::isDouble(rhs))
{
return core->doubleToAtom(double(atomGetIntptr(lhs)) + AvmCore::atomToDouble(rhs));
}
else if (atomIsIntptr(rhs))
{
// We should arrive here only on overflow in inline fastpath.
return core->doubleToAtom(double(atomGetIntptr(lhs) + double(atomGetIntptr(rhs))));
}
}
else if (AvmCore::isDouble(lhs))
{
if (atomIsIntptr(rhs))
{
return core->doubleToAtom(AvmCore::atomToDouble(lhs) + double(atomGetIntptr(rhs)));
}
else if (AvmCore::isDouble(rhs))
{
return core->doubleToAtom(AvmCore::atomToDouble(lhs) + AvmCore::atomToDouble(rhs));
}
}
if (AvmCore::isString(lhs))
{
if (AvmCore::isString(rhs))
{
// AvmCore::concatStrings() coerces a null argument to the string "null".
// Here, we know that neither lhs nor rhs are null, so we can concatenate w/o the coercion.
return String::concatStrings(AvmCore::atomToString(lhs),
AvmCore::atomToString(rhs))->atom();
}
else
{
goto concat_strings;
}
}
else if (AvmCore::isDate(lhs) || AvmCore::isDate(rhs))
{
goto concat_strings;
}
if (AvmCore::isXMLorXMLList(lhs) && AvmCore::isXMLorXMLList(rhs))
{
XMLListObject *l = XMLListObject::create(core->GetGC(), atomObj(lhs)->toplevel()->xmlListClass());
l->_append(lhs);
l->_append(rhs);
return l->atom();
}
lhs = AvmCore::primitive(lhs);
rhs = AvmCore::primitive(rhs);
if (!(AvmCore::isString(lhs) || AvmCore::isString(rhs)))
{
#ifdef VMCFG_FLOAT
if(float_support){
if(AvmCore::isFloat4(lhs) || AvmCore::isFloat4(rhs))
{
float4_decl_v(lhs);
float4_decl_v(rhs);
return core->float4ToAtom( f4_add(lhsv, rhsv) );
}
if(AvmCore::isFloat(lhs) && AvmCore::isFloat(rhs))
return core->floatToAtom(AvmCore::atomToFloat(lhs) + AvmCore::atomToFloat(rhs));
}
#endif
return core->doubleToAtom(AvmCore::number(lhs) + AvmCore::number(rhs));
}
concat_strings:
return core->concatStrings(core->string(lhs), core->string(rhs))->atom();
}
// atom + int => atom
#ifdef VMCFG_FLOAT
template<bool float_support> Atom op_add_a_ai_impl(AvmCore* core, Atom lhs, int32_t rhs)
#else
Atom op_add_a_ai(AvmCore* core, Atom lhs, int32_t rhs)
#endif // VMCFG_FLOAT
{
if (atomIsIntptr(lhs))
{
#ifndef VMCFG_FASTPATH_ADD_INLINE
// Fastpath for intptr+int with no overflow
#ifdef AVMPLUS_64BIT
// A 32-bit integer is guaranteed convertible to an atom of type kIntptrType.
intptr_t lhsv = lhs;
intptr_t rhsv = intptr_t(rhs) << AtomConstants::kAtomTypeSize;
intptr_t const sum = SIGN_EXTEND(lhsv + rhsv);
if ((lhsv ^ rhsv) < 0 || (lhsv ^ sum) >= 0) {
// No overflow, sum is correctly tagged atom
return sum;
}
#else
intptr_t const lhsv = atomGetIntptr(lhs);
intptr_t const rhsv = rhs;
intptr_t const sum = lhsv + rhsv;
if ((lhsv ^ rhsv) < 0 || (lhsv ^ sum) >= 0) {
// No overflow
if (atomIsValidIntptrValue(sum))
return atomFromIntptrValue(sum);
else
return core->allocDouble(double(sum));
}
#endif
#else
// Inline code should handle intptr+int case, except where result must be double.
// It is allowable for the inline code to punt if the 32-bit int argument is itself too large for an intptr.
AvmAssert(!atomIsValidIntptrValue(rhs) || addIntptrOverflow(atomGetIntptr(lhs), rhs));
#endif
return core->doubleToAtom(double(atomGetIntptr(lhs)) + double(rhs));
}
else if (AvmCore::isDouble(lhs))
{
return core->doubleToAtom(AvmCore::atomToDouble(lhs) + double(rhs));
}
if (AvmCore::isString(lhs) || AvmCore::isDate(lhs)) goto concat_strings;
lhs = AvmCore::primitive(lhs);
if (!AvmCore::isString(lhs))
{
#ifdef VMCFG_FLOAT
if(float_support && AvmCore::isFloat4(lhs)) {
float4_t op2 = {(float)rhs, (float)rhs, (float)rhs, (float)rhs};
return core->float4ToAtom( f4_add( AvmCore::atomToFloat4(lhs), op2) );
}
#endif // VMCFG_FLOAT
return core->doubleToAtom(AvmCore::number(lhs) + double(rhs));
}
concat_strings:
// See AvmCore::string()
Stringp s = MathUtils::convertIntegerToStringBase10(core, rhs, MathUtils::kTreatAsSigned);
return core->concatStrings(core->string(lhs), s)->atom();
}
// int + atom => atom
#ifdef VMCFG_FLOAT
template<bool float_support> Atom op_add_a_ia_impl(AvmCore* core, int32_t lhs, Atom rhs)
#else
Atom op_add_a_ia(AvmCore* core, int32_t lhs, Atom rhs)
#endif // VMCFG_FLOAT
{
if (atomIsIntptr(rhs))
{
#ifndef VMCFG_FASTPATH_ADD_INLINE
// Fastpath for int+intptr with no overflow
#ifdef AVMPLUS_64BIT
// A 32-bit integer is guaranteed convertible to an atom of type kIntptrType.
intptr_t lhsv = intptr_t(lhs) << AtomConstants::kAtomTypeSize;
intptr_t rhsv = rhs;
intptr_t const sum = SIGN_EXTEND(lhsv + rhsv);
if ((lhsv ^ rhsv) < 0 || (lhsv ^ sum) >= 0) {
// No overflow, sum is correctly tagged atom
return sum;
}
#else
intptr_t const lhsv = lhs;
intptr_t const rhsv = atomGetIntptr(rhs);
intptr_t const sum = lhsv + rhsv;
if ((lhsv ^ rhsv) < 0 || (lhsv ^ sum) >= 0) {
// No overflow
if (atomIsValidIntptrValue(sum))
return atomFromIntptrValue(sum);
else
return core->allocDouble(double(sum));
}
#endif
#else
// Inline code should handle int+intptr case, except where result must be double.
// It is allowable for the inline code to punt if the 32-bit int argument is itself too large for an intptr.
AvmAssert(!atomIsValidIntptrValue(lhs) || addIntptrOverflow(lhs, atomGetIntptr(rhs)));
#endif
return core->doubleToAtom(double(lhs) + double(atomGetIntptr(rhs)));
}
else if (AvmCore::isDouble(rhs))
{
return core->doubleToAtom(double(lhs) + AvmCore::atomToDouble(rhs));
}
if (AvmCore::isString(rhs) || AvmCore::isDate(rhs)) goto concat_strings;
rhs = AvmCore::primitive(rhs);
if (!AvmCore::isString(rhs))
{
#ifdef VMCFG_FLOAT
if( float_support && AvmCore::isFloat4(rhs)) {
float4_t op1 = {(float)lhs, (float)lhs, (float)lhs, (float)lhs};
return core->float4ToAtom( f4_add( op1, AvmCore::atomToFloat4(rhs)) );
}
#endif
return core->doubleToAtom(double(lhs) + AvmCore::number(rhs));
}
concat_strings:
// See AvmCore::string()
Stringp s = MathUtils::convertIntegerToStringBase10(core, lhs, MathUtils::kTreatAsSigned);
return core->concatStrings(s, core->string(rhs))->atom();
}
// atom + double => atom
#ifdef VMCFG_FLOAT
template<bool float_support> Atom op_add_a_ad_impl(AvmCore* core, Atom lhs, double rhs)
#else
Atom op_add_a_ad(AvmCore* core, Atom lhs, double rhs)
#endif // VMCFG_FLOAT
{
if (AvmCore::isDouble(lhs))
{
return core->doubleToAtom(AvmCore::atomToDouble(lhs) + rhs);
}
else if (atomIsIntptr(lhs))
{
return core->doubleToAtom(double(atomGetIntptr(lhs)) + rhs);
}
if (AvmCore::isString(lhs) || AvmCore::isDate(lhs)) goto concat_strings;
lhs = AvmCore::primitive(lhs);
if (!AvmCore::isString(lhs))
{
#ifdef VMCFG_FLOAT
if(float_support && AvmCore::isFloat4(lhs)) {
float4_t op2 = {(float)rhs, (float)rhs, (float)rhs, (float)rhs};
return core->float4ToAtom( f4_add( AvmCore::atomToFloat4(lhs), op2) );
}
#endif // VMCFG_FLOAT
return core->doubleToAtom(AvmCore::number(lhs) + rhs);
}
concat_strings:
// See AvmCore::string()
Stringp s = core->doubleToString(rhs);
return core->concatStrings(core->string(lhs), s)->atom();
}
// double + atom => atom
#ifdef VMCFG_FLOAT
template<bool float_support> Atom op_add_a_da_impl(AvmCore* core, double lhs, Atom rhs)
#else
Atom op_add_a_da(AvmCore* core, double lhs, Atom rhs)
#endif
{
if (AvmCore::isDouble(rhs))
{
return core->doubleToAtom(lhs + AvmCore::atomToDouble(rhs));
}
else if (atomIsIntptr(rhs))
{
return core->doubleToAtom(lhs + double(atomGetIntptr(rhs)));
}
if (AvmCore::isString(rhs) || AvmCore::isDate(rhs)) goto concat_strings;
rhs = AvmCore::primitive(rhs);
if (!AvmCore::isString(rhs))
{
#ifdef VMCFG_FLOAT
if(float_support && AvmCore::isFloat4(rhs)) {
float4_t op1 = {(float)lhs, (float)lhs, (float)lhs, (float)lhs};
return core->float4ToAtom( f4_add( op1, AvmCore::atomToFloat4(rhs)) );
}
#endif // VMCFG_FLOAT
return core->doubleToAtom(lhs + AvmCore::number(rhs));
}
concat_strings:
// See AvmCore::string()
Stringp s = core->doubleToString(lhs);
return core->concatStrings(s, core->string(rhs))->atom();
}
#ifdef VMCFG_FLOAT
Atom op_add_a_aa(AvmCore* core, Atom lhs, Atom rhs){
return op_add_a_aa_impl<true>(core, lhs, rhs);
}
Atom op_add_a_aa_nofloat(AvmCore* core, Atom lhs, Atom rhs){
return op_add_a_aa_impl<false>(core, lhs, rhs);
}
Atom op_add_a_ai(AvmCore* core, Atom lhs, int32_t rhs){
return op_add_a_ai_impl<true>(core, lhs, rhs);
}
Atom op_add_a_ai_nofloat(AvmCore* core, Atom lhs, int32_t rhs){
return op_add_a_ai_impl<false>(core, lhs, rhs);
}
Atom op_add_a_ia(AvmCore* core, int32_t lhs, Atom rhs){
return op_add_a_ia_impl<true>(core, lhs, rhs);
}
Atom op_add_a_ia_nofloat(AvmCore* core, int32_t lhs, Atom rhs){
return op_add_a_ia_impl<false>(core, lhs, rhs);
}
Atom op_add_a_ad(AvmCore* core, Atom lhs, double rhs){
return op_add_a_ad_impl<true>(core, lhs, rhs);
}
Atom op_add_a_ad_nofloat(AvmCore* core, Atom lhs, double rhs){
return op_add_a_ad_impl<false>(core, lhs, rhs);
}
Atom op_add_a_da(AvmCore* core, double lhs, Atom rhs){
return op_add_a_da_impl<true>(core, lhs, rhs);
}
Atom op_add_a_da_nofloat(AvmCore* core, double lhs, Atom rhs){
return op_add_a_da_impl<false>(core, lhs, rhs);
}
#endif /* VMCFG_FLOAT */
#endif /* VMCFG_FASTPATH_ADD */
void FASTCALL mop_rangeCheckFailed(MethodEnv* env)
{
env->toplevel()->throwRangeError(kInvalidRangeError);
}
// note: all of the mop_xxx load/store functions assume
// that the caller has done range checking, and the address
// is safe for its intended use...
int32_t FASTCALL mop_lix8(const void* addr)
{
// loads a signed byte, sign-extends
return *(const int8_t*)(addr);
}
int32_t FASTCALL mop_liz8(const void* addr)
{
// loads an unsigned byte, zero-extends
return *(const uint8_t*)(addr);
}
int32_t FASTCALL mop_lix16(const void* addr)
{
// loads an signed short, sign-extends
#if defined(VMCFG_UNALIGNED_INT_ACCESS) && defined(VMCFG_LITTLE_ENDIAN)
return *(const int16_t*)(addr);
#else
const uint8_t* u = (const uint8_t*)addr;
return int16_t((uint16_t(u[1]) << 8) | uint16_t(u[0]));
#endif
}
int32_t FASTCALL mop_liz16(const void* addr)
{
// loads an unsigned short, zero-extends
#if defined(VMCFG_UNALIGNED_INT_ACCESS) && defined(VMCFG_LITTLE_ENDIAN)
return *(const uint16_t*)(addr);
#else
const uint8_t* u = (const uint8_t*)addr;
return (uint16_t(u[1]) << 8) |
uint16_t(u[0]);
#endif
}
int32_t FASTCALL mop_li32(const void* addr)
{
#if defined(VMCFG_UNALIGNED_INT_ACCESS) && defined(VMCFG_LITTLE_ENDIAN)
return *(const int32_t*)(addr);
#else
const uint8_t* u = (const uint8_t*)addr;
return (uint32_t(u[3]) << 24) |
(uint32_t(u[2]) << 16) |
(uint32_t(u[1]) << 8) |
uint32_t(u[0]);
#endif
}
double FASTCALL mop_lf32(const void* addr)
{
#if defined(VMCFG_UNALIGNED_FP_ACCESS) && defined(VMCFG_LITTLE_ENDIAN)
return *(const float*)(addr);
#else
union {
uint32_t a;
float b;
};
const uint8_t* u = (const uint8_t*)addr;
// Bugzilla 569691: Do not try to be clever here by loading from
// '*(uint32_t*)u' into 'a', even if both VMCFG_UNALIGNED_INT_ACCESS and
// VMCFG_LITTLE_ENDIAN are set - gcc may emit code that loads directly
// from the ARM VFP register, and that requires VMCFG_UNALIGNED_FP_ACCESS.
a = (uint32_t(u[3]) << 24) |
(uint32_t(u[2]) << 16) |
(uint32_t(u[1]) << 8) |
uint32_t(u[0]);
return b;
#endif
}
double FASTCALL mop_lf64(const void* addr)
{
#if defined(VMCFG_UNALIGNED_FP_ACCESS) && defined(VMCFG_LITTLE_ENDIAN)
return *(const double*)(addr);
#else
// The layout in memory is little-endian with the least significant
// word first.
const uint8_t* u = (const uint8_t*)addr;
double_overlay d;
// Bugzilla 685441: Do not copy 32-bit blocks here for some ARM platforms
// to avoid unaligned memory access, even though VMCFG_UNALIGNED_INT_ACCESS
// is turned on.
d.words.lsw = ((uint64_t(u[3]) << 24) |
(uint64_t(u[2]) << 16) |
(uint64_t(u[1]) << 8) |
(uint64_t(u[0])));
d.words.msw = ((uint32_t(u[7]) << 24) |
(uint32_t(u[6]) << 16) |
(uint32_t(u[5]) << 8) |
(uint32_t(u[4])));
return d.value;
#endif
}
#ifdef VMCFG_FLOAT
void FASTCALL mop_lf32x4(float4_t* result, const void* addr)
{
#if defined(VMCFG_LITTLE_ENDIAN)
// Note, this depends on one of three conditions being true:
// - float4_t is a generic structure type that is not mapped directly
// to the SIMD type, and ByteArray storage is 4-byte aligned
// - SIMD loads from unaligned addresses is OK
// - ByteArray storage is 16-byte aligned
*result = *(const float4_t*)addr;
#else
// 2011-12-16: This has probably never been tested: there have been no
// big-endian systems where VMCFG_FLOAT has been enabled.
const uint8_t* u = (const uint8_t*)addr;
float4_t result;
float_overlay f;
f.word = ((uint32_t(u[3]) << 24) | (uint32_t(u[2]) << 16) | (uint32_t([1]) << 8) | uint32_t(u[0]));
result->x = f.value;
f.word = ((uint32_t(u[7]) << 24) | (uint32_t(u[6]) << 16) | (uint32_t([5]) << 8) | uint32_t(u[4]));
result->y = f.value;
f.word = ((uint32_t(u[11]) << 24) | (uint32_t(u[10]) << 16) | (uint32_t([9]) << 8) | uint32_t(u[8]));
result->z = f.value;
f.word = ((uint32_t(u[15]) << 24) | (uint32_t(u[14]) << 16) | (uint32_t([13]) << 8) | uint32_t(u[12]));
result->w = f.value;
#endif
}
#endif
void FASTCALL mop_si8(void* addr, int32_t value)
{
*(uint8_t*)(addr) = uint8_t(value);
}
void FASTCALL mop_si16(void* addr, int32_t value)
{
#if defined(VMCFG_UNALIGNED_INT_ACCESS) && defined(VMCFG_LITTLE_ENDIAN)
*(uint16_t*)(addr) = uint16_t(value);
#else
uint8_t* u = (uint8_t*)addr;
u[0] = uint8_t(value);
u[1] = uint8_t(value >> 8);
#endif
}
void FASTCALL mop_si32(void* addr, int32_t value)
{
#if defined(VMCFG_UNALIGNED_INT_ACCESS) && defined(VMCFG_LITTLE_ENDIAN)
*(int32_t*)(addr) = int32_t(value);
#else
uint8_t* u = (uint8_t*)addr;
u[0] = uint8_t(value);
u[1] = uint8_t(value >> 8);
u[2] = uint8_t(value >> 16);
u[3] = uint8_t(value >> 24);
#endif
}
void mop_sf32(void* addr, double value)
{
#if defined(VMCFG_UNALIGNED_FP_ACCESS) && defined(VMCFG_LITTLE_ENDIAN)
*(float*)(addr) = float(value);
#else
union {
float a;
uint32_t b;
};
a = float(value);
uint8_t* u = (uint8_t*)addr;
// Bugzilla 569691: Do not try to be clever here by storing from 'b' into
// '*(uint32_t*)u', even if both VMCFG_UNALIGNED_INT_ACCESS and
// VMCFG_LITTLE_ENDIAN are set - gcc will emit code that stores directly
// from the ARM VFP register, and that requires VMCFG_UNALIGNED_FP_ACCESS.
u[0] = uint8_t(b);
u[1] = uint8_t(b >> 8);
u[2] = uint8_t(b >> 16);
u[3] = uint8_t(b >> 24);
#endif
}
void mop_sf64(void* addr, double value)
{
#if defined(VMCFG_UNALIGNED_FP_ACCESS) && defined(VMCFG_LITTLE_ENDIAN)
*(double*)(addr) = value;
#else
double_overlay d(value);
uint8_t* u = (uint8_t*)addr;
// Bugzilla 685441: Do not copy 32-bit blocks here for some ARM platforms
// to avoid unaligned memory access, even though VMCFG_UNALIGNED_INT_ACCESS
// is turned on.
u[0] = uint8_t(d.words.lsw);
u[1] = uint8_t(d.words.lsw >> 8);
u[2] = uint8_t(d.words.lsw >> 16);
u[3] = uint8_t(d.words.lsw >> 24);
u[4] = uint8_t(d.words.msw);
u[5] = uint8_t(d.words.msw >> 8);
u[6] = uint8_t(d.words.msw >> 16);
u[7] = uint8_t(d.words.msw >> 24);
#endif
}
#ifdef VMCFG_FLOAT
void mop_sf32x4(void* addr, const float4_t& value)
{
#if defined(VMCFG_LITTLE_ENDIAN)
// Note, this depends on one of three conditions being true:
// - float4_t is a generic structure type that is not mapped directly
// to the SIMD type, and ByteArray storage is 4-byte aligned
// - SIMD stores to unaligned addresses is OK
// - ByteArray storage is 16-byte aligned
*(float4_t*)addr = value;
#else
// 2011-12-16: This has probably never been tested: there have been no
// big-endian systems where VMCFG_FLOAT has been enabled.
uint8_t* u = (uint8_t*)addr;
float_overlay f;
f.value = value.x;
u[0] = uint8_t(f.word);
u[1] = uint8_t(f.word >> 8);
u[2] = uint8_t(f.word >> 16);
u[3] = uint8_t(f.word >> 24);
f.value = value.y;
u[4] = uint8_t(f.word);
u[5] = uint8_t(f.word >> 8);
u[6] = uint8_t(f.word >> 16);
u[7] = uint8_t(f.word >> 24);
f.value = value.z;
u[8] = uint8_t(f.word);
u[9] = uint8_t(f.word >> 8);
u[10] = uint8_t(f.word >> 16);
u[11] = uint8_t(f.word >> 24);
f.value = value.w;
u[12] = uint8_t(f.word);
u[13] = uint8_t(f.word >> 8);
u[14] = uint8_t(f.word >> 16);
u[15] = uint8_t(f.word >> 24);
#endif
}
#endif
template <class E> NO_INLINE
Atom op_call_error(E env)
{
env->toplevel()->throwTypeErrorWithName(kCallOfNonFunctionError, "value");
return unreachableAtom;
}
template Atom op_call_error(const MethodEnv*);
template Atom op_call_error(MethodEnv*);
template Atom op_call_error(Toplevel*);
} // namespace avmplus