Files
avmplus/core/MethodEnv.cpp
T
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

1641 lines
60 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"
namespace avmplus
{
void MethodEnv::argcError(int32_t argc)
{
MethodSignaturep ms = get_ms();
AvmAssert(!ms->argcOk(argc));
toplevel()->argumentErrorClass()->throwError(kWrongArgumentCountError,
core()->toErrorString(method),
core()->toErrorString(ms->requiredParamCount()),
core()->toErrorString(argc));
}
MethodEnv::MethodEnv(MethodInfo* method, ScopeChain* scope) :
method(method),
_scope(scope),
activationOrMCTable(0)
{
AvmAssert(method != NULL);
core()->exec->init(this);
if (method->declaringTraits() != this->vtable()->traits)
{
#ifdef AVMPLUS_VERBOSE
core()->console << "ERROR " << method->getMethodName() << " " << method->declaringTraits() << " " << this->vtable()->traits << "\n";
#endif
AvmAssertMsg(0, "(method->declaringTraits != vtable->traits)");
toplevel()->throwVerifyError(kCorruptABCError);
}
// activation info used to be created here, but is now created lazily
}
#ifdef DEBUGGER
void MethodEnv::debugEnter( const uint8_t* frame_sst,
CallStackNode* callstack,
FramePtr framep,
volatile intptr_t *eip)
{
AvmAssert(this != 0);
AvmAssert(callstack != 0);
callstack->init(this, framep, frame_sst, eip);
debugEnterInner();
}
void MethodEnv::debugEnterInner()
{
AvmAssert(this != 0);
AvmCore* core = this->core();
Sampler* s = core->get_sampler();
// this shouldn't ever be called unless there's a debugger
AvmAssert(core->debugger() != NULL);
Debugger* debugger = core->debugger();
if (debugger)
debugger->_debugMethod(this);
if (s)
s->sampleCheck();
// method_id can legitimately be -1 for activations, but we don't care about those here,
// so just ignore them.
const int32_t method_id = this->method->method_id();
if (method_id >= 0)
{
this->abcEnv()->invocationCount(method_id) += 1; // returns a reference, so this works
}
}
void MethodEnv::debugExit(CallStackNode* callstack)
{
AvmAssert(this != 0);
AvmAssert(callstack != 0);
AvmCore* core = this->core();
Sampler* s = core->get_sampler();
if (s != 0)
{
// do this before popping the stack, so that the time slice
// gets attributed to the function that is exiting
s->sampleCheck();
}
core->callStack = callstack->next();
// trigger a faked "line number changed" since we exited the function and are now back on the old line (well, almost)
if (core->callStack && core->callStack->linenum() > 0)
{
int32_t line = core->callStack->linenum();
core->callStack->set_linenum(-1);
Debugger* debugger = core->debugger();
if (debugger)
debugger->debugLine(line);
}
}
uint64_t MethodEnv::invocationCount() const
{
const int32_t method_id = this->method->method_id();
if (method_id < 0) return 0;
return this->abcEnv()->invocationCount(method_id);
}
#endif // DEBUGGER
void FASTCALL MethodEnv::nullcheckfail(Atom atom)
{
AvmAssert(AvmCore::isNullOrUndefined(atom));
// TypeError in ECMA
Toplevel* toplevel = this->toplevel();
GCRef<TypeErrorClass> error = toplevel->typeErrorClass();
if( error ){
error->throwError(
(atom == undefinedAtom) ? kConvertUndefinedToObjectError :
kConvertNullToObjectError);
} else {
toplevel->throwVerifyError(kCorruptABCError);
}
}
ArrayObject* MethodEnv::createRest(Atom* argv, int32_t argc)
{
// create arguments Array using argv[param_count..argc]
MethodSignaturep ms = get_ms();
const int32_t param_count = ms->param_count();
Atom* extra = argv + param_count + 1;
const int32_t extra_count = argc > param_count ? argc - param_count : 0;
return toplevel()->arrayClass()->newarray(extra, extra_count);
}
#if defined VMCFG_NANOJIT || defined VMCFG_AOT
Atom MethodEnv::getpropertyHelper(Atom obj, /* not const */ Multiname *multi, VTable *vtable, Atom index)
{
if (atomIsIntptr(index) && atomCanBeInt32(index))
{
return getpropertylate_i(obj, (int32_t)atomGetIntptr(index));
}
if (atomKind(index) == kDoubleType)
{
return getpropertylate_d(obj, AvmCore::atomToDouble(index));
}
if (AvmCore::isObject(index))
{
ScriptObject* i = AvmCore::atomToScriptObject(index);
if (i->traits() == core()->traits.qName_itraits)
{
QNameObject* qname = (QNameObject*) i;
qname->getMultiname(*multi);
}
else if(!multi->isRtns() && AvmCore::isDictionary(obj))
{
return AvmCore::atomToScriptObject(obj)->getAtomProperty(index);
}
else
{
multi->setName(core()->intern(index));
}
}
else
{
multi->setName(core()->intern(index));
}
return toplevel()->getproperty(obj, multi, vtable);
}
void MethodEnv::initpropertyHelper(Atom obj, /* not const */ Multiname *multi, Atom value, VTable *vtable, Atom index)
{
if (atomIsIntptr(index) && atomCanBeInt32(index))
{
setpropertylate_i(obj, (int32_t)atomGetIntptr(index), value);
return;
}
if (atomKind(index) == kDoubleType)
{
setpropertylate_d(obj, AvmCore::atomToDouble(index), value);
return;
}
if (AvmCore::isObject(index))
{
ScriptObject* i = AvmCore::atomToScriptObject(index);
if (i->traits() == core()->traits.qName_itraits)
{
QNameObject* qname = (QNameObject*) i;
qname->getMultiname(*multi);
}
else
{
multi->setName(core()->intern(index));
}
}
else
{
multi->setName(core()->intern(index));
}
initproperty(obj, multi, value, vtable);
}
void MethodEnv::setpropertyHelper(Atom obj, /* not const */ Multiname *multi, Atom value, VTable *vtable, Atom index)
{
// the positive-integer case is inlined into setprop_index in jit-calls.h,
// this handles the negative case
if (atomIsIntptr(index) && atomCanBeInt32(index))
{
setpropertylate_i(obj, (int32_t)atomGetIntptr(index), value);
return;
}
if (atomKind(index) == kDoubleType)
{
setpropertylate_d(obj, AvmCore::atomToDouble(index), value);
return;
}
if (AvmCore::isObject(index))
{
ScriptObject* i = AvmCore::atomToScriptObject(index);
if (i->traits() == core()->traits.qName_itraits)
{
QNameObject* qname = (QNameObject*) i;
qname->getMultiname(*multi);
}
else if(!multi->isRtns() && AvmCore::isDictionary(obj))
{
AvmCore::atomToScriptObject(obj)->setAtomProperty(index, value);
return;
}
else
{
multi->setName(core()->intern(index));
}
}
else
{
multi->setName(core()->intern(index));
}
toplevel()->setproperty(obj, multi, value, vtable);
}
Atom MethodEnv::delpropertyHelper(Atom obj, /* not const */ Multiname *multi, Atom index)
{
AvmCore* core = this->core();
if (AvmCore::isObject(obj) && AvmCore::isObject(index))
{
if (AvmCore::isXMLList(index) )
{
// Error according to E4X spec, section 11.3.1
toplevel()->throwTypeError(kDeleteTypeError, core->toErrorString(toplevel()->toTraits(index)));
}
ScriptObject* i = AvmCore::atomToScriptObject(index);
if (i->traits() == core->traits.qName_itraits)
{
QNameObject* qname = (QNameObject*) i;
qname->getMultiname(*multi);
}
else if(!multi->isRtns() && AvmCore::isDictionary(obj))
{
bool res = AvmCore::atomToScriptObject(obj)->deleteAtomProperty(index);
return res ? trueAtom : falseAtom;
}
else
{
multi->setName(core->intern(index));
}
}
else
{
multi->setName(core->intern(index));
}
return delproperty(obj, multi);
}
void MethodEnv::initMultinameLateForDelete(Multiname& name, Atom index)
{
AvmCore *core = this->core();
if (AvmCore::isObject(index))
{
if (AvmCore::isXMLList(index))
{
// Error according to E4X spec, section 11.3.1
toplevel()->throwTypeError(kDeleteTypeError, core->toErrorString(toplevel()->toTraits(index)));
}
ScriptObject* i = AvmCore::atomToScriptObject(index);
if (i->traits() == core->traits.qName_itraits)
{
QNameObject* qname = (QNameObject*) i;
bool attr = name.isAttr();
qname->getMultiname(name);
if (attr)
name.setAttr(attr);
return;
}
}
name.setName(core->intern(index));
}
#endif
ScriptObject* MethodEnv::newcatch(Traits* traits)
{
AvmCore* core = this->core();
Toplevel* toplevel = this->toplevel();
if (traits == core->traits.object_itraits)
{
// don't need temporary vtable. this is a scope for a finally clause
// todo: asc shouldn't even call OP_newcatch in a finally clause
return toplevel->objectClass->construct();
}
else
{
VTable* vt = core->newVTable(traits, NULL, toplevel);
ScopeChain* catchScope = this->scope()->cloneWithNewVTable(core->GetGC(), vt, this->abcEnv());
vt->resolveSignatures(catchScope);
return core->newObject(vt, NULL);
}
}
#ifdef VMCFG_NANOJIT
ArrayObject* MethodEnv::createArgumentsHelper(int argc, uint32_t *ap)
{
// create arguments using argv[1..argc].
// Even tho E3 says create an Object, E4 says create an Array so thats what we will do.
AvmAssert(argc >= 0);
MMgc::GC::AllocaAutoPtr _atomv;
Atom* atomv = (Atom*) avmStackAllocArray(core(), _atomv, (argc+1), sizeof(Atom));
method->getMethodSignature()->boxArgs(core(), argc, ap, atomv);
return createArguments(atomv, argc);
}
ArrayObject* MethodEnv::createRestHelper(int argc, uint32_t *ap)
{
// create rest Array using argv[param_count..argc]
MethodSignaturep ms = get_ms();
const int rest_offset = ms->rest_offset();
union {
char* extra_8;
Atom* extra;
};
extra_8 = (char*)ap + rest_offset;
const int param_count = ms->param_count();
const int extra_count = argc > param_count ? argc - param_count : 0;
return toplevel()->arrayClass()->newarray(extra, extra_count);
}
#endif // VMCFG_NANOJIT
// Keep in sync with related methods. See comments at Toplevel::getproperty.
/*bool*/ int32_t MethodEnv::haspropertylate_u(Atom obj, uint32_t index) const
{
#ifdef VMCFG_FLOAT
if (AvmCore::isFloat4(obj))
{
// See FIXME in Toplevel::getproperty() - do not absorb non-integer values outside
// the range [0,2^32-2], but let them pass through to the prototype lookup.
if (index != 4294967295U)
return int32_t(index <= 3);
}
#endif
// Primitive types are not dynamic, so we can go directly
// to their __proto__ object.
ScriptObject *o = (atomKind(obj) == kObjectType) ?
AvmCore::atomToScriptObject(obj) :
this->toplevel()->toPrototype(obj);
bool present = o->hasUintProperty(index);
return (int32_t)present;
}
// Keep in sync with related methods. See comments at Toplevel::getproperty.
/*bool*/ int32_t MethodEnv::haspropertylate_i(Atom obj, int32_t index) const
{
#ifdef VMCFG_FLOAT
if (AvmCore::isFloat4(obj))
{
// See FIXME in Toplevel::getproperty() - do not absorb non-integer values outside
// the range [0,2^32-2], but let them pass through to the prototype lookup.
if (index >= 0)
return int32_t(index <= 3);
}
#endif
// Primitive types are not dynamic, so we can go directly
// to their __proto__ object.
ScriptObject *o = (atomKind(obj) == kObjectType) ?
AvmCore::atomToScriptObject(obj) :
this->toplevel()->toPrototype(obj);
bool present;
if (index >= 0) {
present = o->hasUintProperty((uint32_t)index);
} else {
Atom interned = this->core()->internInt(index)->atom();
present = o->hasAtomProperty(interned);
}
return (int32_t)present;
}
// Numeric property name. Except for float4, no fixed bindings have numeric names.
// Keep in sync with related methods. See comments at Toplevel::getproperty.
Atom MethodEnv::getpropertylate_i(Atom obj, int32_t index) const
{
if (atomKind(obj) == kObjectType)
{
if (index >= 0)
{
// Try dynamic lookup on instance. Even if the traits are sealed,
// we might need to search the prototype chain.
return AvmCore::atomToScriptObject(obj)->getUintProperty(index);
}
else
{
// Negative - we must intern the integer.
return AvmCore::atomToScriptObject(obj)->getAtomProperty(this->core()->internInt(index)->atom());
}
}
#ifdef VMCFG_FLOAT
else if (AvmCore::isFloat4(obj))
{
if (index >= 0 && index <= 3){
float4_t f4 = AvmCore::atomToFloat4(obj);
float* pf4 = reinterpret_cast<float*>(&f4);
return core()->floatToAtom(pf4[index]);
}
// See FIXME in Toplevel::getproperty() - do not absorb non-integer values outside
// the range [0,2^32-2], but let them pass through to the prototype lookup.
if (index >= 0)
toplevel()->throwRangeError(kOutOfRangeError, this->core()->internInt(index));
}
#endif
// *No* else clause
{
// Primitive types are not dynamic, so we can go directly
// to their __proto__ object.
AvmCore* core = this->core();
Toplevel *toplevel = this->toplevel();
ScriptObject *protoObject = toplevel->toPrototype(obj);
return protoObject->ScriptObject::getStringPropertyFromProtoChain(core->internInt(index), protoObject, toplevel->toTraits(obj));
}
}
// Numeric property name. Except for float4, no fixed bindings have numeric names.
// Keep in sync with related methods. See comments at Toplevel::getproperty.
Atom MethodEnv::getpropertylate_u(Atom obj, uint32_t index) const
{
if (atomKind(obj) == kObjectType)
{
// Try dynamic lookup on instance. Even if the traits are sealed,
// we might need to search the prototype chain.
return AvmCore::atomToScriptObject(obj)->getUintProperty(index);
}
#ifdef VMCFG_FLOAT
else if (AvmCore::isFloat4(obj))
{
if (index <= 3)
{
float4_t f4 = AvmCore::atomToFloat4(obj);
float* pf4 = reinterpret_cast<float*>(&f4);
return core()->floatToAtom(pf4[index]);
}
// See FIXME in Toplevel::getproperty() - do not absorb non-integer values outside
// the range [0,2^32-2], but let them pass through to the prototype lookup.
if (index != 4294967295U)
toplevel()->throwRangeError(kOutOfRangeError, this->core()->internInt(index));
}
#endif
// *No* else clause
{
// Primitive types are not dynamic, so we can go directly
// to their __proto__ object.
AvmCore* core = this->core();
Toplevel *toplevel = this->toplevel();
ScriptObject *protoObject = toplevel->toPrototype(obj);
return protoObject->ScriptObject::getStringPropertyFromProtoChain(core->internUint32(index), protoObject, toplevel->toTraits(obj));
}
}
// Numeric property name. Except for float4, no fixed bindings have numeric names.
// Keep in sync with related methods. See comments at Toplevel::getproperty.
Atom MethodEnv::getpropertylate_d(Atom obj, double index) const
{
if (atomKind(obj) == kObjectType)
{
// Use signed conversion, as it is faster on most platforms.
int32_t index_i = int32_t(index);
if (double(index_i) == index && index_i >= 0)
{
// Try dynamic lookup on instance. Even if the traits are sealed,
// we might need to search the prototype chain.
return AvmCore::atomToScriptObject(obj)->getUintProperty(uint32_t(index_i));
}
else
{
// Negative, fractional, or out of range, so we must intern the double.
// Sufficiently large non-negative numbers will also take this slower path,
// but we know an exception will be thrown (Vector) or a dense representation
// will not be used (Array, others).
return AvmCore::atomToScriptObject(obj)->getAtomProperty(this->core()->internDouble(index)->atom());
}
}
#ifdef VMCFG_FLOAT
else if (AvmCore::isFloat4(obj))
{
float4_t f4 = AvmCore::atomToFloat4(obj);
float* pf4 = reinterpret_cast<float*>(&f4);
if (index == 0.0) return core()->floatToAtom(pf4[0]);
if (index == 1.0) return core()->floatToAtom(pf4[1]);
if (index == 2.0) return core()->floatToAtom(pf4[2]);
if (index == 3.0) return core()->floatToAtom(pf4[3]);
// See FIXME in Toplevel::getproperty() - do not absorb non-integer values outside
// the range [0,2^32-2], but let them pass through to the prototype lookup.
if (index >= 0.0 && index <= 4294967294.0) {
uint32_t u = uint32_t(index);
if (double(u) == index)
toplevel()->throwRangeError(kOutOfRangeError, this->core()->internDouble(index));
}
}
#endif
// *No* else clause
{
// Primitive types are not dynamic, so we can go directly
// to their __proto__ object.
AvmCore* core = this->core();
Toplevel *toplevel = this->toplevel();
ScriptObject *protoObject = toplevel->toPrototype(obj);
return protoObject->ScriptObject::getStringPropertyFromProtoChain(core->internDouble(index), protoObject, toplevel->toTraits(obj));
}
}
ScriptObject* MethodEnv::finddef(const Multiname* multiname) const
{
// getting toplevel() is slightly more expensive than it used to be (more indirection)...
// so only extract in the (rare) event of an exception
AbcEnv* abcEnv = this->abcEnv();
AvmCore* core = abcEnv->core();
ScriptEnv* script = core->domainMgr()->findScriptEnvInAbcEnvByMultiname(abcEnv, *multiname);
if (script == (ScriptEnv*)BIND_AMBIGUOUS)
this->toplevel()->throwReferenceError(kAmbiguousBindingError, multiname);
if (script == (ScriptEnv*)BIND_NONE)
this->toplevel()->throwReferenceError(kUndefinedVarError, multiname);
ScriptObject* global = script->global;
if (!global)
{
global = script->initGlobal();
script->coerceEnter(global->atom());
}
return global;
}
/*static*/ ScopeChain* ScriptEnv::createScriptScope(const ScopeTypeChain* stc, VTable* _vtable, AbcEnv* _abcEnv)
{
AvmCore* core = _vtable->core();
// NOTE the namespace here is the default xml namespaces. we use the caller's public
return ScopeChain::create(core->GetGC(), _vtable, _abcEnv, stc, NULL, core->findPublicNamespace());
}
ScriptObject* ScriptEnv::initGlobal()
{
AvmAssert(this->global == NULL);
// object not defined yet. define it by running the script that exports it
this->vtable()->resolveSignatures(this->scope());
// resolving the vtable also resolves the traits, if necessary
ObjectClass* const objectClass = this->toplevel()->objectClass;
ScriptObject* const delegate = objectClass ? objectClass->prototypePtr() : NULL;
global = this->core()->newObject(this->vtable(), delegate);
return global;
}
ScriptObject* MethodEnv::op_newobject(Atom* sp, int argc) const
{
// pre-size the hashtable since we know how many vars are coming
Toplevel* toplevel = this->toplevel();
AvmCore* core = toplevel->core();
VTable* object_ivtable = toplevel->objectClass->ivtable();
ScriptObject* o = ScriptObject::create(core->GetGC(), object_ivtable, toplevel->objectClass->prototypePtr(), argc);
for (; argc-- > 0; sp -= 2)
{
Atom name = sp[-1];
// verifier has stated this is a String, but we may see nullStringAtom
if (!AvmCore::isString(name))
toplevel->throwTypeError(kConvertNullToObjectError);
name = core->internString(name)->atom();
o->setAtomProperty(name, sp[0]);
}
return o;
}
Atom MethodEnv::nextname(Atom objAtom, int index) const
{
// Handle special case inputs;
// bad index returns null,
// null object throws error.
if (index <= 0)
{
return nullStringAtom;
}
if (AvmCore::isNullOrUndefined(objAtom))
{
toplevel()->throwTypeError(
(objAtom == undefinedAtom) ? kConvertUndefinedToObjectError :
kConvertNullToObjectError);
}
switch (atomKind(objAtom))
{
case kObjectType:
return AvmCore::atomToScriptObject(objAtom)->nextName(index);
case kNamespaceType:
return AvmCore::atomToNamespace(objAtom)->nextName(this->core(), index);
#ifdef VMCFG_FLOAT
case kSpecialBibopType:
if (AvmCore::isFloat4(objAtom) && index <= 4)
return atomFromIntptrValue(index-1);
// Else fall through
#endif // VMCFG_FLOAT
default:
ScriptObject* proto = toplevel()->toPrototype(objAtom); // cn: types like Number are sealed, but their prototype could have dynamic properties
return proto ? proto->nextName(index) : undefinedAtom;
}
}
Atom MethodEnv::nextvalue(Atom objAtom, int index) const
{
// Handle special case inputs;
// bad index returns undefined,
// null object throws error.
if (index <= 0)
{
return undefinedAtom;
}
if (AvmCore::isNullOrUndefined(objAtom))
{
toplevel()->throwTypeError(
(objAtom == undefinedAtom) ? kConvertUndefinedToObjectError :
kConvertNullToObjectError);
}
switch (atomKind(objAtom))
{
case kObjectType:
return AvmCore::atomToScriptObject(objAtom)->nextValue(index);
case kNamespaceType:
return AvmCore::atomToNamespace(objAtom)->nextValue(index);
case kSpecialBibopType:
#ifdef VMCFG_FLOAT
if (AvmCore::isFloat4(objAtom) && index <= 4)
{
float* f4p = (float*)atomPtr(objAtom);
return toplevel()->core()->floatToAtom( f4p[index-1] );
}
// Else fall through
#endif
default:
ScriptObject* proto = toplevel()->toPrototype(objAtom);
return (proto ? proto->nextValue(index) : undefinedAtom);
}
}
int MethodEnv::hasnext(Atom objAtom, int index) const
{
if (index < 0)
return 0;
if (!AvmCore::isNullOrUndefined(objAtom))
{
switch (atomKind(objAtom))
{
case kObjectType:
return AvmCore::atomToScriptObject(objAtom)->nextNameIndex(index);
case kNamespaceType:
return AvmCore::atomToNamespace(objAtom)->nextNameIndex(index);
#ifdef VMCFG_FLOAT
case kSpecialBibopType:
if (AvmCore::isFloat4(objAtom) && index <= 3)
return index + 1;
// Else fall through
#endif // VMCFG_FLOAT
default:
ScriptObject* proto = toplevel()->toPrototype(objAtom);
int nextIndex = ( proto ? proto->nextNameIndex(index) : 0);
return nextIndex;
}
}
else
{
return 0;
}
}
int32_t MethodEnv::hasnextproto(Atom& objAtom, int& index) const
{
if (index < 0)
return 0;
ScriptObject *delegate = NULL;
if (!AvmCore::isNullOrUndefined(objAtom))
{
switch (atomKind(objAtom))
{
case kObjectType:
{
ScriptObject *object = AvmCore::atomToScriptObject(objAtom);
delegate = object->getDelegate();
index = object->nextNameIndex(index);
}
break;
case kNamespaceType:
{
index = AvmCore::atomToNamespace(objAtom)->nextNameIndex(index);
delegate = toplevel()->namespaceClass()->prototypePtr();
}
break;
#ifdef VMCFG_FLOAT
case kSpecialBibopType:
if (AvmCore::isFloat4(objAtom) && index <= 3)
{
index++;
return 1; // I.e. "true"; but hasnextproto() traditionally returns int
}
// Else fall through
#endif // VMCFG_FLOAT
default:
{
ScriptObject* proto = toplevel()->toPrototype(objAtom);
delegate = proto ? proto->getDelegate() : NULL;
index = ( proto ? proto->nextNameIndex(index) : 0);
}
}
}
else
{
index = 0;
}
while (index == 0 && delegate != NULL)
{
// Advance to next object on prototype chain
ScriptObject *object = delegate;
objAtom = object->atom();
delegate = object->getDelegate();
index = object->nextNameIndex(index);
}
if (index == 0)
{
// If we're done, set object local to null
objAtom = nullObjectAtom;
}
return index != 0;
}
// see 13.2 creating function objects
ClassClosure* MethodEnv::newfunction(MethodInfo *function, Atom* scopes) const
{
Toplevel* toplevel = this->toplevel();
AvmCore* core = toplevel->core();
FunctionClass* functionClass = toplevel->_functionClass; // can't use functionClass(), might not be inited yet
VTable* fvtable = functionClass->ivtable();
VTable* object_ivtable = toplevel->objectClass->ivtable();
AvmAssert(fvtable->ivtable == NULL || fvtable->ivtable == object_ivtable);
fvtable->ivtable = object_ivtable;
AvmAssert(fvtable->linked);
const ScopeTypeChain* fstc = function->declaringScope();
// functions should never have 'extra' scopes
AvmAssert(fstc->size == fstc->fullsize);
MMgc::GC* gc = core->GetGC();
ScopeChain* outer = scope();
ScopeChain* fscope = ScopeChain::create(gc, fvtable, abcEnv(), fstc, outer, core->dxns());
for (int i=outer->getSize(), n=fscope->getSize(); i < n; i++)
{
fscope->setScope(gc, i, *scopes++);
}
FunctionEnv* fenv = FunctionEnv::create(gc, function, fscope);
FunctionObject* c = FunctionObject::create(gc, fvtable, fenv);
c->setDelegate(functionClass->prototypePtr());
c->createVanillaPrototype();
c->prototypePtr()->setStringProperty(core->kconstructor, c->atom());
c->prototypePtr()->setStringPropertyIsEnumerable(core->kconstructor, false);
fenv->closure = c;
return c;
}
/**
* given a classInfo, create a new ClassClosure object and return it on the stack.
*/
ClassClosure* MethodEnv::newclass(Traits* ctraits,
ClassClosure *base,
ScopeChain* outer,
Atom* scopes) const
{
AvmCore* core = this->core();
// adds clarity to what is usually just global$init()
SAMPLE_FRAME("[newclass]", core);
MMgc::GC* gc = core->GetGC();
Toplevel* toplevel = this->toplevel();
Traits* itraits = ctraits->itraits;
const BuiltinType bt = Traits::getBuiltinType(itraits);
if (bt == BUILTIN_object)
return toplevel->objectClass;
if (bt == BUILTIN_class)
return toplevel->_classClass;
AbcEnv* abcEnv = this->abcEnv();
// finish resolving the base class
if (!base && itraits->base)
{
// class has a base but no base object was provided
GCRef<TypeErrorClass> error = toplevel->typeErrorClass();
if (error)
error->throwError(kConvertNullToObjectError);
else
toplevel->throwTypeError(kCorruptABCError);
}
// have to use local variables for CodeWarrior
Traits* baseIvtableTraits = base ? base->ivtable()->traits : 0;
Traits* itraitsBase = itraits->base;
// make sure the traits of the base vtable matches the base traits
if (!((base == NULL && itraits->base == NULL) || (base != NULL && itraitsBase == baseIvtableTraits)))
{
GCRef<VerifyErrorClass> error = toplevel->verifyErrorClass();
if (error)
error->throwError(kInvalidBaseClassError);
else
toplevel->throwTypeError(kCorruptABCError);
}
VTable* ivtable = core->newVTable(itraits, base ? base->ivtable() : NULL, toplevel);
VTable* class_ivtable = toplevel->_classClass->ivtable();
VTable* cvtable = core->newVTable(ctraits, class_ivtable, toplevel);
// class scopechain = [..., class]
AvmAssert(ctraits->init != NULL);
ScopeChain* cscope = ScopeChain::create(gc, cvtable, abcEnv, ctraits->init->declaringScope(), outer, core->dxns());
int i = outer->getSize();
for (int n=cscope->getSize(); i < n; i++)
{
cscope->setScope(gc, i, *scopes++);
}
AvmAssert(itraits->init != NULL);
ScopeChain* iscope = ScopeChain::create(gc, ivtable, abcEnv, itraits->init->declaringScope(), cscope, core->dxns());
ivtable->resolveSignatures(iscope);
cvtable->ivtable = ivtable;
cvtable->resolveSignatures(cscope);
CreateClassClosureProc createClassClosure = cvtable->traits->getCreateClassClosureProc();
AvmAssert(createClassClosure != NULL);
ClassClosure* cc = (*createClassClosure)(cvtable);
if (cc->prototypePtr())
{
// C.prototype.__proto__ = Base.prototype
if (base != NULL)
cc->prototypePtr()->setDelegate(base->prototypePtr());
// C.prototype.constructor = C {DontEnum}
cc->initPrototypeConstructor();
}
AvmAssert(i == iscope->getSize()-1);
iscope->setScope(gc, i, cc->atom());
AvmAssert(toplevel->_classClass != NULL);
cc->setDelegate(toplevel->_classClass->prototypePtr());
cvtable->init->coerceEnter(cc->atom()); // ignore result
return cc;
}
void MethodEnv::initproperty(Atom obj, const Multiname* multiname, Atom value, VTable* vtable) const
{
Toplevel* toplevel = this->toplevel();
Traitsp declarer = NULL;
Binding b = toplevel->getBindingAndDeclarer(vtable->traits, *multiname, declarer);
if (AvmCore::bindingKind(b) == BKIND_CONST)
{
if (this->method != declarer->init)
toplevel->throwReferenceError(kConstWriteError, multiname, vtable->traits);
b = AvmCore::makeSlotBinding(AvmCore::bindingToSlotId(b), BKIND_VAR);
}
toplevel->setproperty_b(obj, multiname, value, vtable, b);
}
void MethodEnv::setpropertylate_i(Atom obj, int32_t index, Atom value) const
{
if (AvmCore::isObject(obj))
{
ScriptObject* o = AvmCore::atomToScriptObject(obj);
if (index >= 0)
{
o->setUintProperty(index, value);
}
else
{
// negative index - we must intern the integer
o->setAtomProperty(this->core()->internInt(index)->atom(), value);
}
}
else
{
// obj represents a primitive Number, Boolean, int, or String, and primitives
// are sealed and final. Cannot add dynamic vars to them.
// throw a ReferenceError exception Property not found and could not be created.
Multiname tempname(core()->getPublicNamespace(method->pool()), core()->internInt(index));
toplevel()->throwReferenceError(kWriteSealedError, &tempname, toplevel()->toTraits(obj));
}
}
void MethodEnv::setpropertylate_u(Atom obj, uint32_t index, Atom value) const
{
if (AvmCore::isObject(obj))
{
AvmCore::atomToScriptObject(obj)->setUintProperty(index, value);
}
else
{
// obj represents a primitive Number, Boolean, int, or String, and primitives
// are sealed and final. Cannot add dynamic vars to them.
// throw a ReferenceError exception Property not found and could not be created.
Multiname tempname(core()->getPublicNamespace(method->pool()), core()->internUint32(index));
toplevel()->throwReferenceError(kWriteSealedError, &tempname, toplevel()->toTraits(obj));
}
}
void MethodEnv::setpropertylate_d(Atom obj, double index, Atom value) const
{
if (AvmCore::isObject(obj))
{
ScriptObject* o = AvmCore::atomToScriptObject(obj);
// Use signed conversion, as it is faster on most platforms.
// We do not allocate objects large enough for an integral value
// that will not fit an int32 to index a vector or dense array,
// thus we'd have to use a string property name anyway in the
// cases where this conversion fails.
int32_t index_i = int32_t(index);
if (double(index_i) == index && index_i >= 0)
{
o->setUintProperty(uint32_t(index_i), value);
return;
}
// On the face of it, it should be sufficient to fall back to using
// setAtomProperty() here. Unfortunately, due to former differences
// in the way that setUintProperty() and setAtomProperty() handled
// out-of-range indices, a RangeError might have been thrown where
// setAtomProperty() would have thrown a ReferenceError. We have to
// preserve the distinct code paths so that the SWF10 compatibility
// logic can distinguish these cases.
// Try unsigned conversion.
uint32_t index_u = uint32_t(index);
if (double(index_u) == index)
{
o->setUintProperty(index_u, value);
return;
}
// Negative, fractional, or out of range, so we must intern the double.
// Sufficiently large non-negative numbers will also take this slower path,
// but we know an exception will be thrown (Vector) or a dense representation
// will not be used (Array, others).
o->setAtomProperty(this->core()->internDouble(index)->atom(), value);
}
else
{
// obj represents a primitive Number, Boolean, int, or String, and primitives
// are sealed and final. Cannot add dynamic vars to them.
// throw a ReferenceError exception Property not found and could not be created.
Multiname tempname(core()->getPublicNamespace(method->pool()), core()->internDouble(index));
toplevel()->throwReferenceError(kWriteSealedError, &tempname, toplevel()->toTraits(obj));
}
}
#ifdef VMCFG_FLOAT
void MethodEnv::setpropertylate_f(Atom obj, float index_f, Atom value) const
{
if (AvmCore::isObject(obj))
{
ScriptObject* o = AvmCore::atomToScriptObject(obj);
// Use signed conversion, as it is faster on most platforms.
// We do not allocate objects large enough for an integral value
// that will not fit an int32 to index a vector or dense array,
// thus we'd have to use a string property name anyway in the
// cases where this conversion fails.
int32_t index_i = int32_t(index_f);
if (float(index_i) == index_f && index_i >= 0)
{
o->setUintProperty(uint32_t(index_i), value);
return;
}
// No need to worry about SWF10 compatibility (like setpropertylate_d does)
// Negative, fractional, or out of range, so we must intern the float.
// Sufficiently large non-negative numbers will also take this slower path,
// but we know an exception will be thrown (Vector) or a dense representation
// will not be used (Array, others).
o->setAtomProperty(this->core()->internFloat(index_f)->atom(), value);
}
else
{
// obj represents a primitive Number, Boolean, int, float or String, and primitives
// are sealed and final. Cannot add dynamic vars to them.
// throw a ReferenceError exception Property not found and could not be created.
Multiname tempname(core()->getPublicNamespace(method->pool()), core()->internFloat(index_f));
toplevel()->throwReferenceError(kWriteSealedError, &tempname, toplevel()->toTraits(obj));
}
}
#endif
Atom MethodEnv::callsuper(const Multiname* multiname, int argc, Atom* atomv) const
{
VTable* base = this->vtable()->base;
Toplevel* toplevel = this->toplevel();
Binding b = toplevel->getBinding(base->traits, multiname);
switch (AvmCore::bindingKind(b))
{
default:
toplevel->throwReferenceError(kCallNotFoundError, multiname, base->traits);
case BKIND_METHOD:
{
MethodEnv* superenv = base->methods[AvmCore::bindingToMethodId(b)];
return superenv->coerceEnter(argc, atomv);
}
case BKIND_VAR:
case BKIND_CONST:
{
uint32_t slot = AvmCore::bindingToSlotId(b);
ScriptObject* function = AvmCore::atomToScriptObject(atomv[0])->getSlotObject(slot);
return op_call(this, function, argc, atomv);
}
case BKIND_SET:
{
// read on write-only property
toplevel->throwReferenceError(kWriteOnlyError, multiname, base->traits);
}
case BKIND_GET:
case BKIND_GETSET:
{
// Invoke the getter
int m = AvmCore::bindingToGetterId(b);
MethodEnv *f = base->methods[m];
Atom method = f->coerceEnter(atomv[0]);
return avmplus::op_call(this, method, argc, atomv);
}
}
}
Atom MethodEnv::delproperty(Atom obj, const Multiname* multiname) const
{
Toplevel* toplevel = this->toplevel();
Traits* traits = toplevel->toTraits(obj); // includes null check
if (AvmCore::isObject(obj))
{
Binding b = toplevel->getBinding(traits, multiname);
if (b == BIND_NONE)
{
bool b = AvmCore::atomToScriptObject(obj)->deleteMultinameProperty(multiname);
return b ? trueAtom : falseAtom;
}
else if (AvmCore::isMethodBinding(b))
{
if (AvmCore::isXMLorXMLList(obj) && multiname->containsAnyPublicNamespace())
{
// dynamic props should hide declared methods on delete
ScriptObject* so = AvmCore::atomToScriptObject(obj);
bool b = so->deleteMultinameProperty(multiname);
return b ? trueAtom : falseAtom;
}
}
}
#ifdef VMCFG_FLOAT
else if (AvmCore::isFloat4(obj))
{
// See FIXME in Toplevel::getproperty() for an explanation of this logic.
if (multiname->isValidDynamicName())
{
uint32_t index;
if (AvmCore::getIndexFromAtom(multiname->getName()->atom(), &index))
{
if (index <= 3)
return falseAtom;
return trueAtom;
}
}
toplevel->throwReferenceError(kDeleteSealedError, multiname, traits);
}
#endif
else
{
toplevel->throwReferenceError(kDeleteSealedError, multiname, traits);
}
return falseAtom;
}
Atom MethodEnv::getsuper(Atom obj, const Multiname* multiname) const
{
VTable* vtable = this->vtable()->base;
Toplevel* toplevel = this->toplevel();
Binding b = toplevel->getBinding(vtable->traits, multiname);
switch (AvmCore::bindingKind(b))
{
default:
toplevel->throwReferenceError(kReadSealedError, multiname, vtable->traits);
case BKIND_METHOD:
{
// extracting a virtual method
MethodEnv *m = vtable->methods[AvmCore::bindingToMethodId(b)];
return toplevel->methodClosureClass()->create(m, obj)->atom();
}
case BKIND_VAR:
case BKIND_CONST:
return AvmCore::atomToScriptObject(obj)->getSlotAtom(AvmCore::bindingToSlotId(b));
case BKIND_SET:
{
// read on write-only property
toplevel->throwReferenceError(kWriteOnlyError, multiname, vtable->traits);
}
case BKIND_GET:
case BKIND_GETSET:
{
// Invoke the getter
int m = AvmCore::bindingToGetterId(b);
MethodEnv *f = vtable->methods[m];
return f->coerceEnter(obj);
}
}
}
void MethodEnv::setsuper(Atom obj, const Multiname* multiname, Atom value) const
{
VTable* vtable = this->vtable()->base;
Toplevel* toplevel = this->toplevel();
Binding b = toplevel->getBinding(vtable->traits, multiname);
switch (AvmCore::bindingKind(b))
{
default:
toplevel->throwReferenceError(kWriteSealedError, multiname, vtable->traits);
case BKIND_CONST:
toplevel->throwReferenceError(kConstWriteError, multiname, vtable->traits);
case BKIND_METHOD:
toplevel->throwReferenceError(kCannotAssignToMethodError, multiname, vtable->traits);
case BKIND_GET:
toplevel->throwReferenceError(kConstWriteError, multiname, vtable->traits);
case BKIND_VAR:
// @todo, this does a redundant coercion -- no harm done, just slightly more work than necessary
AvmCore::atomToScriptObject(obj)->coerceAndSetSlotAtom(AvmCore::bindingToSlotId(b), value);
return;
case BKIND_SET:
case BKIND_GETSET:
{
// Invoke the setter
uint32_t m = AvmCore::bindingToSetterId(b);
AvmAssert(m < vtable->traits->getTraitsBindings()->methodCount);
MethodEnv* method = vtable->methods[m];
Atom atomv_out[2] = { obj, value };
// coerce value to proper type, then call enter
method->coerceEnter(1, atomv_out);
return;
}
}
}
Atom MethodEnv::findWithProperty(Atom atom, const Multiname* multiname)
{
Toplevel* toplevel = this->toplevel();
if (atomKind(atom)==kObjectType)
{
// usually scope objects are ScriptObject's
ScriptObject* o = AvmCore::atomToScriptObject(atom);
// search the delegate chain for a value. we must look at
// traits at each step along the way in case we have class
// instances on the scope chain
do
{
// ISSUE it is incorrect to return a reference to an object on the prototype
// chain, we should only return the scopechain object; the next operation
// could be a setproperty, and we don't want to mutate prototype objects this way.
// look at the traits first, stop if found.
Binding b = toplevel->getBinding(o->traits(), multiname);
if (b != BIND_NONE)
return atom;
if (o->hasMultinameProperty(multiname))
return atom;
}
while ((o = o->getDelegate()) != NULL);
}
else
{
// primitive value on scope chain!
// first iteration looks at traits only since primitive values don't have
// dynamic variables
Binding b = toplevel->getBinding(toplevel->toTraits(atom), multiname);
if (b != BIND_NONE)
return atom;
// then we continue starting at the prototype for this primitive type
ScriptObject* o = toplevel->toPrototype(atom);
do
{
Binding b = toplevel->getBinding(o->traits(), multiname);
if (b != BIND_NONE)
return atom;
if (o->hasMultinameProperty(multiname))
return atom;
}
while ((o = o->getDelegate()) != NULL);
}
return nullObjectAtom;
}
/**
* return the object on the scope chain that owns the given property.
* if no object has that property, return scope[0]. we search each
* delegate chain but only return objects that are on the scope chain.
* this way, find+get and find+set are both correct. find+set should
* not mutate a prototype object.
*/
Atom MethodEnv::findproperty(ScopeChain* outer,
Atom* scopes,
int extraScopes,
const Multiname* multiname,
bool strict,
Atom* withBase)
{
Toplevel* toplevel = this->toplevel();
// look in each with frame in the current stack frame
Atom* scopep = &scopes[extraScopes-1];
if (withBase)
{
for (; scopep >= withBase; scopep--)
{
Atom result = findWithProperty(*scopep, multiname);
if (!AvmCore::isNull(result))
{
return result;
}
}
}
// if we're in global$init (outer_depth==0), don't check "this" scope just yet.
int outer_depth = outer->getSize();
for (; scopep > scopes; scopep--)
{
Atom a = *scopep;
Traits* t = toplevel->toTraits(a);
Binding b = toplevel->getBinding(t, multiname);
if (b != BIND_NONE)
return a;
}
const ScopeTypeChain* outerTraits = outer->scopeTraits();
if (outer_depth > 0 && scopep >= scopes)
{
// consider "this" scope now, but constrain it to the declaringTraits of
// the current method (verifier ensures this is safe)
Atom a = *scopep;
Traits *t;
if (outerTraits->fullsize > outerTraits->size)
{
// scope traits has extra constraint for "this" scope, see OP_newclass in verifier
t = outerTraits->getScopeTraitsAt(outerTraits->size);
}
else
{
// "this" scope type is the runtime type
t = toplevel->toTraits(a);
}
Binding b = toplevel->getBinding(t, multiname);
if (b != BIND_NONE)
return a;
}
// now search outer scopes
for (int i=outer_depth-1; i > 0; i--)
{
if (outerTraits->getScopeIsWithAt(i))
{
Atom result = findWithProperty(outer->getScope(i), multiname);
if (!AvmCore::isNull(result))
return result;
}
else
{
// only look at the properties on the captured (verify time) type, not the actual type,
// of the outer scope object.
Atom a = outer->getScope(i);
Traits* t = outerTraits->getScopeTraitsAt(i);
Binding b = toplevel->getBinding(t, multiname);
if (b != BIND_NONE)
return a;
}
}
// No imported definitions or global scope can have attributes. (Using filter
// operator with non existent attribute will get here.
if (multiname->isAttr())
{
if (strict)
toplevel->throwReferenceError(kUndefinedVarError, multiname);
return undefinedAtom;
}
// now we have searched all the scopes, except global
{
Atom global = outer->getSize() > 0 ? outer->getScope(0) : *scopes;
Atom obj = findglobalproperty(global, multiname);
if (AvmCore::isNullOrUndefined(obj)) {
if (strict)
toplevel->throwReferenceError(kUndefinedVarError, multiname);
obj = global;
}
return obj;
}
}
Atom MethodEnv::findglobalproperty(Atom target_global, const Multiname* multiname)
{
// wildcards used here always fail later on, so just check now.
if (multiname->isAnyName() || multiname->isAnyNamespace())
return nullObjectAtom;
Toplevel* toplevel = this->toplevel();
// look for imported definition (similar logic to OP_finddef). This will
// find definitions in this script and in other scripts.
AbcEnv* abcEnv = this->abcEnv();
AvmCore* core = abcEnv->core();
ScriptEnv* script = core->domainMgr()->findScriptEnvInAbcEnvByMultiname(abcEnv, *multiname);
if (script != (ScriptEnv*)BIND_NONE)
{
if (script == (ScriptEnv*)BIND_AMBIGUOUS)
toplevel->throwReferenceError(kAmbiguousBindingError, multiname);
ScriptObject* global = script->global;
if (global == NULL)
{
global = script->initGlobal();
script->coerceEnter(script->global->atom());
}
return global->atom();
}
// no imported definition found. look for dynamic props
// on the global object
// search the delegate chain for a value. The delegate
// chain for the global object will only contain vanilla
// objects (Object.prototype) so we can skip traits
ScriptObject* o = AvmCore::isObject(target_global) ?
AvmCore::atomToScriptObject(target_global) :
toplevel->toPrototype(target_global);
do
{
if (o->hasMultinameProperty(multiname))
return target_global;
}
while ((o = o->getDelegate()) != NULL);
return nullObjectAtom;
}
Namespace* MethodEnv::internRtns(Atom nsAtom)
{
if ((atomKind(nsAtom) != kNamespaceType) || AvmCore::isNull(nsAtom))
toplevel()->throwTypeError(kIllegalNamespaceError);
return core()->internNamespace(AvmCore::atomToNamespace(nsAtom));
}
ArrayObject* MethodEnv::createArguments(Atom *atomv, int argc)
{
Toplevel* toplevel = this->toplevel();
ArrayObject *arguments = toplevel->arrayClass()->newarray(atomv+1,argc);
ScriptObject *closure;
if (method->needClosure())
{
closure = toplevel->methodClosureClass()->create(this, atomv[0]);
}
else
{
closure = ((FunctionEnv*)this)->closure;
}
arguments->setStringProperty(core()->kcallee, closure->atom());
arguments->setStringPropertyIsEnumerable(core()->kcallee, false);
return arguments;
}
Atom MethodEnv::getdescendants(Atom obj, const Multiname* multiname)
{
if (AvmCore::isObject (obj))
{
return core()->atomToScriptObject(obj)->getDescendants (multiname);
}
else
{
// Rhino simply returns undefined for other Atom types
// SpiderMonkey throws TypeError. We're doing TypeError.
toplevel()->throwTypeError(kDescendentsError, core()->toErrorString(toplevel()->toTraits(obj)));
return undefinedAtom; // not reached
}
}
void MethodEnv::checkfilter(Atom obj)
{
if ( !AvmCore::isXMLorXMLList(obj) )
{
Toplevel* toplevel = this->toplevel();
toplevel->throwTypeError(kFilterError, core()->toErrorString(toplevel->toTraits(obj)));
}
}
VTable* MethodEnv::buildActivationVTable()
{
const ScopeTypeChain* activationScopeTraits = method->activationScope();
Toplevel* toplevel = this->toplevel();
// This can happen when the ABC has MethodInfo data but not MethodBody data
if (!activationScopeTraits)
{
toplevel->throwVerifyError(kCorruptABCError);
}
Traits* activationTraits = activationScopeTraits->traits();
AvmAssert(activationTraits != NULL);
AvmCore* core = this->core();
// Bugzilla 671413 (note below is from Steven Johnson):
// This is quite possibly the ugliest hack I've ever inserted into Tamarin.
// The problem: at the ABC level, activation objects can be coerced into plain Objects,
// but they aren't plain Objects (they are distinct types). If a call to one of Object's
// methods (e.g. AS3::hasOwnProperty) is early bound to this, we'll attempt to call
// a method using a non-existent MethodEnv on the activation object's VTable -- crashamundo.
// The *proper* fix is (arguably) to smarten Verifier to ensure that activation objects
// can't be coerced, or leaked into a different structure/function that might coerce them,
// but that's tricky to do and a Quick Fix is needed for Wasabi. Hence, the pragmatic approach:
// artificially graft the MethodEnvs from Object onto each activation VTable, so that if
// such a dubious call happens, it quietly calls the proper Object method. (Arguably, a better
// approach would be to call a method that throws a VerifyError, but since this is intended
// as a temporary fix for fuzzed/malformed ABC in the first place, I don't think it's worth
// the extra effort.)
MMgc::GC* gc = core->GetGC();
VTable* object_ivtable = toplevel->objectClass->ivtable();
Traits* object_itraits = object_ivtable->traits;
uint32_t const object_method_count = object_itraits->getTraitsBindings()->methodCount;
AvmAssert(object_method_count > 0);
// activations shouldn't ever have methods.
uint32_t const activation_method_count = activationTraits->getTraitsBindings()->methodCount;
AvmAssert(activation_method_count == 0);
if (activation_method_count > 0)
toplevel->throwVerifyError(kCorruptABCError);
size_t const extraSize = sizeof(MethodEnv*) * (object_method_count-1);
VTable* activation = new (gc, MMgc::kExact, extraSize) VTable(activationTraits, NULL, toplevel);
ScopeChain* activationScope = this->scope()->cloneWithNewVTable(core->GetGC(), activation, this->abcEnv(), activationScopeTraits);
activation->resolveSignatures(activationScope);
// Bugzilla 671413
for (uint32_t i = 0; i < object_method_count; i++)
WB(gc, activation, &activation->methods[i], object_ivtable->methods[i]);
return activation;
}
VTable* MethodEnv::getActivationVTable()
{
if (!method->needActivation())
return NULL;
VTable* activation;
const int type = getType();
if (!activationOrMCTable)
{
activation = buildActivationVTable();
setActivationOrMCTable(activation, kActivation);
}
else if (type == kMethodTable)
{
activation = buildActivationVTable();
ActivationMethodTablePair *amtp = new (core()->GetGC()) ActivationMethodTablePair(activation, getMethodClosureTable());
setActivationOrMCTable(amtp, kActivationMethodTablePair);
}
else if (type == kActivationMethodTablePair)
{
activation = getPair()->activation;
}
else
{
activation = (VTable*)(activationOrMCTable&~7);
}
AvmAssert(activation != NULL);
return activation;
}
ScriptObject *MethodEnv::newUninitializedActivation()
{
VTable *vtable = getActivationVTable();
AvmCore *core = this->core();
MMgc::GC *gc = core->GetGC();
SAMPLE_FRAME("[activation-object]", core);
ScriptObject* obj = ScriptObject::create(gc, vtable, 0/*delegate*/);
AvmAssert(obj->getDelegate() == NULL); // activation objects must not have a prototype pointer.
return obj;
}
ScriptObject *MethodEnv::newActivation()
{
ScriptObject* obj = newUninitializedActivation();
MethodEnv *init = obj->vtable->init;
if (init)
init->coerceEnter(obj->atom());
AvmAssert(obj->getDelegate() == NULL); // activation objects must not have a prototype pointer.
return obj;
}
WeakKeyHashtable *MethodEnv::getMethodClosureTable()
{
int type = getType();
if(!activationOrMCTable)
{
WeakKeyHashtable *wkh = WeakKeyHashtable::create(core()->GetGC());
setActivationOrMCTable(wkh, kMethodTable);
return wkh;
}
else if(type == kActivation)
{
WeakKeyHashtable *wkh = WeakKeyHashtable::create(core()->GetGC());
ActivationMethodTablePair *amtp = new (core()->GetGC()) ActivationMethodTablePair(getActivationVTable(), wkh);
setActivationOrMCTable(amtp, kActivationMethodTablePair);
return wkh;
}
else if(type == kActivationMethodTablePair)
{
return getPair()->methodTable;
}
return (WeakKeyHashtable*)(activationOrMCTable&~7);
}
#ifdef _DEBUG
void FASTCALL check_unbox(MethodEnv* env, bool u)
{
AvmAssert((env->method->unboxThis() != 0) == u);
}
#endif
#ifdef DEBUGGER
ArrayObject* MethodEnv::getLexicalScopes()
{
if(_scope->getSize() == 0)
return NULL;
ArrayObject *scopes = toplevel()->arrayClass()->newArray(_scope->getSize());
for(int i=_scope->getSize()-1, j=0; i >= 0; i--, j++) {
Atom scope = _scope->getScope(i);
scopes->setUintProperty(j, scope);
}
return scopes;
}
#endif
}