1608 lines
44 KiB
C++
1608 lines
44 KiB
C++
/* ***** BEGIN LICENSE BLOCK *****
|
||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||
*
|
||
* The contents of this file are subject to the Mozilla Public License Version
|
||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||
* the License. You may obtain a copy of the License at
|
||
* http://www.mozilla.org/MPL/
|
||
*
|
||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||
* for the specific language governing rights and limitations under the
|
||
* License.
|
||
*
|
||
* The Original Code is [Open Source Virtual Machine.].
|
||
*
|
||
* The Initial Developer of the Original Code is
|
||
* Adobe System Incorporated.
|
||
* Portions created by the Initial Developer are Copyright (C) 1993-2006
|
||
* the Initial Developer. All Rights Reserved.
|
||
*
|
||
* Contributor(s):
|
||
* Adobe AS3 Team
|
||
*
|
||
* Alternatively, the contents of this file may be used under the terms of
|
||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||
* of those above. If you wish to allow use of your version of this file only
|
||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||
* use your version of this file under the terms of the MPL, indicate your
|
||
* decision by deleting the provisions above and replace them with the notice
|
||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||
* the provisions above, a recipient may use your version of this file under
|
||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||
*
|
||
* ***** END LICENSE BLOCK ***** */
|
||
|
||
|
||
#include "avmplus.h"
|
||
|
||
namespace avmplus
|
||
{
|
||
#undef DEBUG_EARLY_BINDING
|
||
Atom MethodEnv::coerceEnter(int argc, Atom* atomv)
|
||
{
|
||
Toplevel* toplevel = vtable->toplevel;
|
||
|
||
if (!method->argcOk(argc))
|
||
{
|
||
toplevel->argumentErrorClass()->throwError(kWrongArgumentCountError, core()->toErrorString((AbstractFunction*)method), core()->toErrorString(method->requiredParamCount()), core()->toErrorString(argc));
|
||
}
|
||
|
||
AbstractFunction* method = this->method;
|
||
|
||
// just do enough to resolve signatures. Don't do a full verify yet.
|
||
method->resolveSignature(toplevel);
|
||
|
||
// check receiver type first
|
||
// caller will coerce instance if necessary,
|
||
// so make sure it was done.
|
||
AvmAssert(atomv[0] == toplevel->coerce(atomv[0], method->paramTraits(0)));
|
||
|
||
// now unbox everything, including instance and rest args
|
||
int extra = argc > method->param_count ? argc - method->param_count : 0;
|
||
AvmAssert(method->restOffset > 0 && extra >= 0);
|
||
uint32 *ap = (uint32 *) alloca(method->restOffset + sizeof(Atom)*extra);
|
||
|
||
unboxCoerceArgs(argc, atomv, ap);
|
||
|
||
// we know we have verified the method, so we can go right into it.
|
||
Traits* returnType = method->returnTraits();
|
||
AvmCore* core = this->core();
|
||
if (returnType == NUMBER_TYPE)
|
||
{
|
||
AvmAssert(method->implN != NULL);
|
||
double d = method->implN(this, argc, ap);
|
||
return core->doubleToAtom(d);
|
||
}
|
||
else
|
||
{
|
||
AvmAssert(method->impl32 != NULL);
|
||
Atom i = method->impl32(this, argc, ap);
|
||
if (returnType == INT_TYPE)
|
||
return core->intToAtom(i);
|
||
else if (returnType == UINT_TYPE)
|
||
return core->uintToAtom((uint32)i);
|
||
else if (returnType == BOOLEAN_TYPE)
|
||
return i ? trueAtom : falseAtom;
|
||
else if (!returnType || returnType == OBJECT_TYPE || returnType == VOID_TYPE)
|
||
return (Atom)i;
|
||
else if (returnType == STRING_TYPE)
|
||
return ((Stringp)i)->atom();
|
||
else if (returnType == NAMESPACE_TYPE)
|
||
return ((Namespace*)i)->atom();
|
||
else
|
||
return ((ScriptObject*)i)->atom();
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* 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 MethodEnv::unboxCoerceArgs(int argc, Atom* in, uint32 *argv)
|
||
{
|
||
AbstractFunction* f = this->method;
|
||
AvmCore* core = this->core();
|
||
Toplevel* toplevel = this->toplevel();
|
||
|
||
Atom *args = (Atom *) argv;
|
||
|
||
for (int i=0; i <= argc; i++)
|
||
{
|
||
if (i <= f->param_count)
|
||
{
|
||
Traits* t = f->paramTraits(i);
|
||
AvmAssert(t != VOID_TYPE);
|
||
|
||
if (t == NUMBER_TYPE)
|
||
{
|
||
union {
|
||
double d;
|
||
uint32 l[2];
|
||
};
|
||
d = core->number(in[i]);
|
||
#ifdef AVMPLUS_64BIT
|
||
AvmAssert(sizeof(Atom) == sizeof(double));
|
||
*(double *) args = d;
|
||
args++;
|
||
#else
|
||
AvmAssert(sizeof(Atom) * 2 == sizeof(double));
|
||
*args++ = l[0];
|
||
*args++ = l[1];
|
||
#endif
|
||
}
|
||
else if (t == INT_TYPE)
|
||
{
|
||
*args++ = core->integer(in[i]);
|
||
}
|
||
else if (t == UINT_TYPE)
|
||
{
|
||
*args++ = core->toUInt32(in[i]);
|
||
}
|
||
else if (t == BOOLEAN_TYPE)
|
||
{
|
||
*args++ = core->boolean(in[i]);
|
||
}
|
||
else if (t == OBJECT_TYPE)
|
||
{
|
||
*args++ = in[i] == undefinedAtom ? nullObjectAtom : in[i];
|
||
}
|
||
else if (!t)
|
||
{
|
||
*args++ = in[i];
|
||
}
|
||
else
|
||
{
|
||
// ScriptObject, String, or Namespace, or Null
|
||
*args++ = toplevel->coerce(in[i],t) & ~7;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
*args++ = in[i];
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
Atom MethodEnv::delegateInvoke(MethodEnv* env, int argc, uint32 *ap)
|
||
{
|
||
env->impl32 = env->method->impl32;
|
||
return env->impl32(env, argc, ap);
|
||
}
|
||
|
||
MethodEnv::MethodEnv(void *addr, VTable *vtable)
|
||
: vtable(vtable), method(NULL)
|
||
{
|
||
typedef Atom (*AtomMethodProc)(MethodEnv*, int, uint32 *);
|
||
impl32 = *(AtomMethodProc*) &addr;
|
||
}
|
||
|
||
MethodEnv::MethodEnv(AbstractFunction* method, VTable *vtable)
|
||
: vtable(vtable), method(method)
|
||
{
|
||
// make the first call go to the method impl
|
||
impl32 = delegateInvoke;
|
||
|
||
AvmCore* core = vtable->traits->core;
|
||
#ifdef AVMPLUS_VERBOSE
|
||
if (method->declaringTraits != vtable->traits)
|
||
{
|
||
core->console << "ERROR " << method->name << " " << method->declaringTraits << " " << vtable->traits << "\n";
|
||
}
|
||
#endif
|
||
if(method->declaringTraits != vtable->traits){
|
||
AvmAssertMsg(0, "(method->declaringTraits != vtable->traits)");
|
||
toplevel()->throwVerifyError(kCorruptABCError);
|
||
}
|
||
|
||
if (method->flags & AbstractFunction::NEED_ACTIVATION)
|
||
{
|
||
// This can happen when the ABC has MethodInfo data but not MethodBody data
|
||
if (!method->activationTraits)
|
||
{
|
||
toplevel()->throwVerifyError(kCorruptABCError);
|
||
}
|
||
|
||
VTable *activation = core->newVTable(method->activationTraits, NULL, vtable->scope, vtable->abcEnv, toplevel());
|
||
activation->resolveSignatures();
|
||
setActivationOrMCTable(activation, kActivation);
|
||
}
|
||
|
||
// register this env in the callstatic method table
|
||
int method_id = method->method_id;
|
||
if (method_id != -1)
|
||
{
|
||
AbcEnv* abcEnv = vtable->abcEnv;
|
||
AvmAssert(abcEnv->pool == (PoolObject *) method->pool);
|
||
if (abcEnv->methods[method_id] == NULL)
|
||
{
|
||
abcEnv->setMethod(method_id, this);
|
||
}
|
||
#ifdef AVMPLUS_VERBOSE
|
||
else if (method->pool->verbose)
|
||
{
|
||
core->console << "WARNING: tried to re-register global MethodEnv for " << method << "\n";
|
||
}
|
||
#endif
|
||
}
|
||
|
||
}
|
||
|
||
#ifdef DEBUGGER
|
||
void MethodEnv::debugEnter(int argc, uint32 *ap,
|
||
Traits**frameTraits, int localCount,
|
||
CallStackNode* callstack,
|
||
Atom* framep, volatile sintptr *eip)
|
||
{
|
||
AvmCore* core = this->core();
|
||
|
||
// update profiler
|
||
sendEnter(argc, ap);
|
||
|
||
// dont reset the parameter traits since they are setup in the prologue
|
||
int firstLocalAt = method->param_count+1;
|
||
AvmAssert(!frameTraits || localCount >= firstLocalAt);
|
||
if (frameTraits) memset(&frameTraits[firstLocalAt], 0, (localCount-firstLocalAt)*sizeof(Traits*));
|
||
if (callstack) callstack->initialize(this, method, framep, frameTraits, argc, ap, eip);
|
||
if (core->debugger) core->debugger->_debugMethod(this);
|
||
|
||
core->sampleCheck();
|
||
}
|
||
|
||
void MethodEnv::debugExit(CallStackNode* callstack)
|
||
{
|
||
AvmAssert(this != 0);
|
||
AvmCore* core = this->core();
|
||
|
||
// update profiler
|
||
sendExit();
|
||
|
||
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)
|
||
{
|
||
int line = core->callStack->linenum;
|
||
core->callStack->linenum = -1;
|
||
if (core->debugger) core->debugger->debugLine(line);
|
||
}
|
||
}
|
||
|
||
void MethodEnv::sendEnter(int /*argc*/, uint32 * /*ap*/)
|
||
{
|
||
Profiler *profiler = core()->profiler;
|
||
if (profiler->profilingDataWanted && !core()->sampler()->sampling)
|
||
profiler->sendFunctionEnter(method);
|
||
}
|
||
|
||
void MethodEnv::sendExit()
|
||
{
|
||
Profiler *profiler = core()->profiler;
|
||
if (profiler->profilingDataWanted && !core()->sampler()->sampling)
|
||
profiler->sendFunctionExit();
|
||
}
|
||
#endif
|
||
|
||
void MethodEnv::nullcheck(Atom atom)
|
||
{
|
||
if (!AvmCore::isNullOrUndefined(atom))
|
||
return;
|
||
|
||
// TypeError in ECMA
|
||
ErrorClass *error = toplevel()->typeErrorClass();
|
||
if( error ){
|
||
error->throwError(
|
||
(atom == undefinedAtom) ? kConvertUndefinedToObjectError :
|
||
kConvertNullToObjectError);
|
||
} else {
|
||
toplevel()->throwVerifyError(kCorruptABCError);
|
||
}
|
||
}
|
||
|
||
void MethodEnv::npe()
|
||
{
|
||
toplevel()->throwTypeError(kConvertNullToObjectError);
|
||
}
|
||
|
||
void MethodEnv::interrupt()
|
||
{
|
||
vtable->traits->core->interrupt(this);
|
||
}
|
||
|
||
Traits* MethodEnv::toClassITraits(Atom atom)
|
||
{
|
||
switch (atom&7)
|
||
{
|
||
case kObjectType:
|
||
{
|
||
if( !AvmCore::isNull(atom) )
|
||
{
|
||
Traits* itraits = AvmCore::atomToScriptObject(atom)->traits()->itraits;
|
||
if (itraits == NULL)
|
||
toplevel()->throwTypeError(kIsTypeMustBeClassError);
|
||
return itraits;
|
||
}
|
||
// else fall through and report an error
|
||
}
|
||
default:
|
||
// TypeError in ECMA
|
||
// ISSUE the error message should say "whatever" is not a class
|
||
toplevel()->throwTypeError(
|
||
(atom == undefinedAtom) ? kConvertUndefinedToObjectError :
|
||
kConvertNullToObjectError);
|
||
return NULL;
|
||
}
|
||
}
|
||
|
||
ArrayObject* MethodEnv::createRest(Atom* argv, int argc)
|
||
{
|
||
// create arguments Array using argv[param_count..argc]
|
||
Atom* extra = argv + method->param_count + 1;
|
||
int extra_count = argc > method->param_count ? argc - method->param_count : 0;
|
||
return toplevel()->arrayClass->newarray(extra, extra_count);
|
||
}
|
||
|
||
#ifdef AVMPLUS_MIR
|
||
|
||
Atom MethodEnv::getpropertyHelper(Atom obj, Multiname *multi, VTable *vtable, Atom index)
|
||
{
|
||
if ((index&7) == kIntegerType)
|
||
{
|
||
return getpropertylate_i(obj, index>>3);
|
||
}
|
||
|
||
if ((index&7) == kDoubleType)
|
||
{
|
||
int i = AvmCore::integer_i(index);
|
||
if ((double)i == AvmCore::atomToDouble(index))
|
||
{
|
||
return getpropertylate_i(obj, i);
|
||
}
|
||
}
|
||
|
||
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() && core()->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, Multiname *multi, Atom value, VTable *vtable, Atom index)
|
||
{
|
||
if ((index&7) == kIntegerType)
|
||
{
|
||
setpropertylate_i(obj, index>>3, value);
|
||
return;
|
||
}
|
||
|
||
if ((index&7) == kDoubleType)
|
||
{
|
||
int i = core()->integer(index);
|
||
uint32 u = (uint32)(i);
|
||
if ((double)u == AvmCore::atomToDouble(index))
|
||
{
|
||
setpropertylate_u(obj, u, value);
|
||
return;
|
||
}
|
||
else if ((double)i == AvmCore::atomToDouble(index))
|
||
{
|
||
setpropertylate_i(obj, i, 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, Multiname *multi, Atom value, VTable *vtable, Atom index)
|
||
{
|
||
if ((index&7) == kIntegerType)
|
||
{
|
||
setpropertylate_i(obj, index>>3, value);
|
||
return;
|
||
}
|
||
|
||
if ((index&7) == kDoubleType)
|
||
{
|
||
int i = core()->integer(index);
|
||
uint32 u = (uint32)(i);
|
||
if ((double)u == AvmCore::atomToDouble(index))
|
||
{
|
||
setpropertylate_u(obj, u, value);
|
||
return;
|
||
}
|
||
else if ((double)i == AvmCore::atomToDouble(index))
|
||
{
|
||
setpropertylate_i(obj, i, 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() && core()->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, Multiname *multi, Atom index)
|
||
{
|
||
AvmCore* core = this->core();
|
||
|
||
if (AvmCore::isObject(obj) && AvmCore::isObject(index))
|
||
{
|
||
if( core->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() && core->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 (core->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));
|
||
}
|
||
|
||
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, vtable->scope, vtable->abcEnv, toplevel);
|
||
vt->resolveSignatures();
|
||
return core->newObject(vt, NULL);
|
||
}
|
||
}
|
||
|
||
ArrayObject* MethodEnv::createArgumentsHelper(int argc, uint32 *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);
|
||
Atom* atomv = (Atom*) alloca((argc+1) * sizeof(Atom));
|
||
method->boxArgs(argc, ap, atomv);
|
||
return createArguments(atomv, argc);
|
||
}
|
||
|
||
ArrayObject* MethodEnv::createRestHelper(int argc, uint32 *ap)
|
||
{
|
||
// create rest Array using argv[param_count..argc]
|
||
Atom* extra = (Atom*) (method->restOffset + (char*)ap);
|
||
int extra_count = argc > method->param_count ? argc - method->param_count : 0;
|
||
return toplevel()->arrayClass->newarray(extra, extra_count);
|
||
}
|
||
|
||
#endif // AVMPLUS_MIR
|
||
|
||
Atom MethodEnv::getpropertylate_i(Atom obj, int index) const
|
||
{
|
||
// here we put the case for bind-none, since we know there are no bindings
|
||
// with numeric names.
|
||
if ((obj&7) == 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(method->core()->internInt(index)->atom());
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// primitive types are not dynamic, so we can go directly
|
||
// to their __proto__ object
|
||
AvmCore* core = method->core();
|
||
Toplevel *toplevel = this->toplevel();
|
||
ScriptObject *protoObject = toplevel->toPrototype(obj);
|
||
return protoObject->ScriptObject::getStringPropertyFromProtoChain(core->internInt(index), protoObject, toplevel->toTraits(obj));
|
||
}
|
||
}
|
||
|
||
Atom MethodEnv::getpropertylate_u(Atom obj, uint32 index) const
|
||
{
|
||
// here we put the case for bind-none, since we know there are no bindings
|
||
// with numeric names.
|
||
if ((obj&7) == 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);
|
||
}
|
||
else
|
||
{
|
||
// primitive types are not dynamic, so we can go directly
|
||
// to their __proto__ object
|
||
AvmCore* core = method->core();
|
||
Toplevel *toplevel = this->toplevel();
|
||
ScriptObject *protoObject = toplevel->toPrototype(obj);
|
||
return protoObject->ScriptObject::getStringPropertyFromProtoChain(core->internUint32(index), protoObject, toplevel->toTraits(obj));
|
||
}
|
||
}
|
||
|
||
ScriptObject* MethodEnv::finddef(Multiname* multiname) const
|
||
{
|
||
Toplevel* toplevel = vtable->toplevel;
|
||
|
||
ScriptEnv* script = getScriptEnv(multiname);
|
||
if (script == (ScriptEnv*)BIND_AMBIGUOUS)
|
||
toplevel->throwReferenceError(kAmbiguousBindingError, multiname);
|
||
|
||
if (script == (ScriptEnv*)BIND_NONE)
|
||
toplevel->throwReferenceError(kUndefinedVarError, multiname);
|
||
|
||
ScriptObject* global = script->global;
|
||
if (!global)
|
||
{
|
||
global = script->initGlobal();
|
||
Atom argv[1] = { global->atom() };
|
||
script->coerceEnter(0, argv);
|
||
}
|
||
return global;
|
||
}
|
||
|
||
ScriptEnv* MethodEnv::getScriptEnv(Multiname *multiname) const
|
||
{
|
||
ScriptEnv *se = (ScriptEnv*)abcEnv()->domainEnv->getScriptInit(multiname);
|
||
if(!se)
|
||
{
|
||
// check privates
|
||
se = (ScriptEnv*)abcEnv()->privateScriptEnvs.getMulti(multiname);
|
||
}
|
||
return se;
|
||
}
|
||
|
||
ScriptObject* MethodEnv::finddefNsset(NamespaceSet* nsset, Stringp name) const
|
||
{
|
||
Multiname m(nsset);
|
||
m.setName(name);
|
||
return finddef(&m);
|
||
}
|
||
|
||
ScriptObject* MethodEnv::finddefNs(Namespace* ns, Stringp name) const
|
||
{
|
||
Multiname m(ns, name);
|
||
return finddef(&m);
|
||
}
|
||
|
||
ScriptObject* ScriptEnv::initGlobal()
|
||
{
|
||
// object not defined yet. define it by running the script that exports it
|
||
Traits* traits = vtable->traits;
|
||
vtable->resolveSignatures();
|
||
NativeScriptInfo* nativeEntry = traits->getNativeScriptInfo();
|
||
|
||
Toplevel* toplevel = this->toplevel();
|
||
traits->resolveSignatures(toplevel);
|
||
ScriptObject* delegate = toplevel->objectClass->prototype;
|
||
|
||
if (nativeEntry != NULL)
|
||
{
|
||
// special script with native impl object
|
||
return global = nativeEntry->handler(vtable, delegate);
|
||
}
|
||
else
|
||
{
|
||
// ordinary user script
|
||
return global = (ScriptObject*) method->core()->newObject(vtable, delegate);
|
||
}
|
||
}
|
||
|
||
ScriptObject* MethodEnv::op_newobject(Atom* sp, int argc) const
|
||
{
|
||
// pre-size the hashtable since we know how many vars are coming
|
||
VTable* object_vtable = toplevel()->object_vtable;
|
||
AvmCore* core = method->core();
|
||
|
||
ScriptObject* o = new (core->GetGC(), object_vtable->getExtraSize())
|
||
ScriptObject(object_vtable, toplevel()->objectClass->prototype,
|
||
2*argc+1);
|
||
|
||
for (; argc-- > 0; sp -= 2)
|
||
{
|
||
Atom name = sp[-1];
|
||
//verifier should ensure names are String
|
||
//todo have the verifier take care of interning too
|
||
AvmAssert(AvmCore::isString(name));
|
||
|
||
o->setAtomProperty(core->internString(name)->atom(), sp[0]);
|
||
}
|
||
return o;
|
||
}
|
||
|
||
|
||
|
||
Atom MethodEnv::nextname(Atom objAtom, int index) const
|
||
{
|
||
if (index <= 0)
|
||
return nullStringAtom;
|
||
|
||
switch (objAtom&7)
|
||
{
|
||
case kObjectType:
|
||
return AvmCore::atomToScriptObject(objAtom)->nextName(index);
|
||
case kNamespaceType:
|
||
return AvmCore::atomToNamespace(objAtom)->nextName(method->core(), index);
|
||
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
|
||
{
|
||
if (index <= 0)
|
||
return undefinedAtom;
|
||
|
||
switch (objAtom&7)
|
||
{
|
||
case kObjectType:
|
||
return AvmCore::atomToScriptObject(objAtom)->nextValue(index);
|
||
case kNamespaceType:
|
||
return AvmCore::atomToNamespace(objAtom)->nextValue(index);
|
||
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 (objAtom&7)
|
||
{
|
||
case kObjectType:
|
||
return AvmCore::atomToScriptObject(objAtom)->nextNameIndex(index);
|
||
case kNamespaceType:
|
||
return AvmCore::atomToNamespace(objAtom)->nextNameIndex(index);
|
||
default:
|
||
ScriptObject* proto = toplevel()->toPrototype(objAtom);
|
||
int nextIndex = ( proto ? proto->nextNameIndex(index) : 0);
|
||
return nextIndex;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
int MethodEnv::hasnext2(Atom& objAtom, int& index) const
|
||
{
|
||
if (index < 0)
|
||
return 0;
|
||
|
||
ScriptObject *delegate = NULL;
|
||
|
||
if (!AvmCore::isNullOrUndefined(objAtom))
|
||
{
|
||
switch (objAtom&7)
|
||
{
|
||
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->prototype;
|
||
}
|
||
break;
|
||
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;
|
||
}
|
||
|
||
Atom MethodEnv::in(Atom nameatom, Atom obj) const
|
||
{
|
||
AvmCore* core = method->core();
|
||
Traits *t = toplevel()->toTraits(obj); // includes null check
|
||
|
||
if(!core->isDictionaryLookup(nameatom, obj))
|
||
{
|
||
Stringp name = core->intern(nameatom);
|
||
|
||
// ISSUE should we try this on each object on the proto chain or just the first?
|
||
if (t->findBinding(name, core->publicNamespace) != BIND_NONE)
|
||
{
|
||
return trueAtom;
|
||
}
|
||
|
||
nameatom = name->atom();
|
||
}
|
||
|
||
ScriptObject* o = (obj&7)==kObjectType ? AvmCore::atomToScriptObject(obj) :
|
||
toplevel()->toPrototype(obj);
|
||
do
|
||
{
|
||
if (o->hasAtomProperty(nameatom))
|
||
return trueAtom;
|
||
}
|
||
while ((o = o->getDelegate()) != NULL);
|
||
return falseAtom;
|
||
}
|
||
|
||
// see 13.2 creating function objects
|
||
ClassClosure* MethodEnv::newfunction(AbstractFunction *function,
|
||
ScopeChain* outer,
|
||
Atom* scopes) const
|
||
{
|
||
AvmCore* core = this->core();
|
||
AbcEnv* abcEnv = vtable->abcEnv;
|
||
|
||
// TODO: if we have already created a function and the scope chain
|
||
// is the same as last time, re-use the old closure?
|
||
|
||
// declaringTraits must have been filled in by verifier.
|
||
Traits* ftraits = function->declaringTraits;
|
||
AvmAssert(ftraits != NULL);
|
||
AvmAssert(ftraits->scope != NULL);
|
||
|
||
ScopeChain* scope = ScopeChain::create(core->GetGC(), ftraits->scope, outer, *core->dxnsAddr);
|
||
|
||
for (int i=outer->getSize(), n=scope->getSize(); i < n; i++)
|
||
{
|
||
scope->setScope(i, *scopes++);
|
||
}
|
||
|
||
FunctionClass* functionClass = toplevel()->functionClass;
|
||
|
||
// the vtable for the new function object
|
||
VTable* fvtable = core->newVTable(ftraits, functionClass->ivtable(), scope, abcEnv, toplevel());
|
||
fvtable->resolveSignatures();
|
||
FunctionEnv *fenv = new (core->GetGC()) FunctionEnv(function, fvtable);
|
||
fvtable->call = fenv;
|
||
fvtable->ivtable = toplevel()->object_vtable;
|
||
|
||
ClassClosure* c = new (core->GetGC(), fvtable->getExtraSize()) ClassClosure(fvtable);
|
||
c->setDelegate( functionClass->prototype );
|
||
|
||
c->createVanillaPrototype();
|
||
c->prototype->setStringProperty(core->kconstructor, c->atom());
|
||
c->prototype->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(AbstractFunction* cinit,
|
||
ClassClosure *base,
|
||
ScopeChain* outer,
|
||
Atom* scopes) const
|
||
{
|
||
AvmCore* core = this->core();
|
||
Toplevel* toplevel = this->toplevel();
|
||
|
||
Traits* ctraits = cinit->declaringTraits;
|
||
Traits* itraits = ctraits->itraits;
|
||
|
||
// finish resolving the base class
|
||
if (!base && itraits->base)
|
||
{
|
||
// class has a base but no base object was provided
|
||
ErrorClass *error = toplevel->typeErrorClass();
|
||
if( error )
|
||
error->throwError(kConvertNullToObjectError);
|
||
else
|
||
toplevel->throwTypeError(kCorruptABCError);
|
||
}
|
||
|
||
// make sure the traits of the base vtable matches the base traits
|
||
if (!(base == NULL && itraits->base == NULL || base != NULL && itraits->base == base->ivtable()->traits))
|
||
{
|
||
ErrorClass *error = toplevel->verifyErrorClass();
|
||
if( error )
|
||
error->throwError(kInvalidBaseClassError);
|
||
else
|
||
toplevel->throwTypeError(kCorruptABCError);
|
||
}
|
||
|
||
ctraits->resolveSignatures(toplevel);
|
||
itraits->resolveSignatures(toplevel);
|
||
|
||
// class scopechain = [..., class]
|
||
ScopeChain* cscope = ScopeChain::create(core->GetGC(), ctraits->scope, outer, *core->dxnsAddr);
|
||
|
||
int staticScopesCount = 0;
|
||
|
||
int i = outer->getSize();
|
||
for (int n=cscope->getSize()-staticScopesCount; i < n; i++)
|
||
{
|
||
cscope->setScope(i, *scopes++);
|
||
}
|
||
|
||
ScopeChain* iscope = ScopeChain::create(core->GetGC(), itraits->scope, cscope, *core->dxnsAddr);
|
||
|
||
AbcEnv *abcEnv = vtable->abcEnv;
|
||
VTable* cvtable = core->newVTable(ctraits, toplevel->class_vtable, cscope, abcEnv, toplevel);
|
||
cvtable->resolveSignatures();
|
||
VTable* ivtable = core->newVTable(itraits, base ? base->ivtable() : NULL, iscope, abcEnv, toplevel);
|
||
ivtable->resolveSignatures();
|
||
cvtable->ivtable = ivtable;
|
||
|
||
if (itraits == core->traits.object_itraits) {
|
||
// we just defined Object
|
||
toplevel->object_vtable = ivtable;
|
||
}
|
||
else if (itraits == core->traits.class_itraits) {
|
||
// we just defined Class
|
||
toplevel->class_vtable = ivtable;
|
||
cvtable->base = ivtable;
|
||
toplevel->objectClass->vtable->base = ivtable;
|
||
}
|
||
|
||
NativeClassInfo* nativeEntry;
|
||
ClassClosure *cc;
|
||
if ((nativeEntry = cvtable->traits->getNativeClassInfo()) != NULL)
|
||
{
|
||
cc = nativeEntry->handler(cvtable);
|
||
}
|
||
else
|
||
{
|
||
cc = new (core->GetGC(), cvtable->getExtraSize()) ClassClosure(cvtable);
|
||
AvmAssert(cc->prototype == NULL);
|
||
cc->createVanillaPrototype();
|
||
}
|
||
|
||
if (cc->prototype)
|
||
{
|
||
// C.prototype.__proto__ = Base.prototype
|
||
if (base != NULL)
|
||
cc->prototype->setDelegate( base->prototype );
|
||
|
||
// C.prototype.constructor = C {DontEnum}
|
||
cc->prototype->setStringProperty(core->kconstructor, cc->atom());
|
||
cc->prototype->setStringPropertyIsEnumerable(core->kconstructor, false);
|
||
}
|
||
|
||
AvmAssert(i == iscope->getSize()-1);
|
||
iscope->setScope(i, cc->atom());
|
||
if (toplevel->classClass)
|
||
{
|
||
cc->setDelegate( toplevel->classClass->prototype );
|
||
}
|
||
|
||
// Invoke the class init function.
|
||
Atom argv[1] = { cc->atom() };
|
||
cvtable->init->coerceEnter(0, argv);
|
||
return cc;
|
||
}
|
||
|
||
void MethodEnv::initproperty(Atom obj, Multiname* multiname, Atom value, VTable* vtable) const
|
||
{
|
||
Toplevel* toplevel = this->toplevel();
|
||
Binding b = toplevel->getBinding(vtable->traits, multiname);
|
||
if ((b&7) == BIND_CONST)
|
||
{
|
||
if (vtable->init != this)
|
||
toplevel->throwReferenceError(kConstWriteError, multiname, vtable->traits);
|
||
b = (b & ~7) | BIND_VAR;
|
||
}
|
||
toplevel->setproperty_b(obj, multiname, value, vtable, b);
|
||
}
|
||
|
||
void MethodEnv::setpropertylate_i(Atom obj, int 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(method->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()->publicNamespace, core()->internInt(index));
|
||
toplevel()->throwReferenceError(kWriteSealedError, &tempname, toplevel()->toTraits(obj));
|
||
}
|
||
}
|
||
|
||
void MethodEnv::setpropertylate_u(Atom obj, uint32 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()->publicNamespace, core()->internUint32(index));
|
||
toplevel()->throwReferenceError(kWriteSealedError, &tempname, toplevel()->toTraits(obj));
|
||
}
|
||
}
|
||
|
||
Atom MethodEnv::callsuper(Multiname* multiname, int argc, Atom* atomv) const
|
||
{
|
||
VTable* base = vtable->base;
|
||
Toplevel* toplevel = this->toplevel();
|
||
Binding b = toplevel->getBinding(base->traits, multiname);
|
||
switch (b&7)
|
||
{
|
||
default:
|
||
toplevel->throwReferenceError(kCallNotFoundError, multiname, base->traits);
|
||
|
||
case BIND_METHOD:
|
||
{
|
||
#ifdef DEBUG_EARLY_BINDING
|
||
core()->console << "callsuper method " << base->traits << " " << multiname->name << "\n";
|
||
#endif
|
||
MethodEnv* superenv = base->methods[AvmCore::bindingToMethodId(b)];
|
||
return superenv->coerceEnter(argc, atomv);
|
||
}
|
||
case BIND_VAR:
|
||
case BIND_CONST:
|
||
{
|
||
#ifdef DEBUG_EARLY_BINDING
|
||
core()->console << "callsuper slot " << base->traits << " " << multiname->name << "\n";
|
||
#endif
|
||
uint32 slot = AvmCore::bindingToSlotId(b);
|
||
Atom method = AvmCore::atomToScriptObject(atomv[0])->getSlotAtom(slot);
|
||
return toplevel->op_call(method, argc, atomv);
|
||
}
|
||
case BIND_SET:
|
||
{
|
||
#ifdef DEBUG_EARLY_BINDING
|
||
core()->console << "callsuper setter " << base->traits << " " << multiname->name << "\n";
|
||
#endif
|
||
// read on write-only property
|
||
toplevel->throwReferenceError(kWriteOnlyError, multiname, base->traits);
|
||
}
|
||
case BIND_GET:
|
||
case BIND_GETSET:
|
||
{
|
||
#ifdef DEBUG_EARLY_BINDING
|
||
core()->console << "callsuper getter " << base->traits << " " << multiname->name << "\n";
|
||
#endif
|
||
// Invoke the getter
|
||
int m = AvmCore::bindingToGetterId(b);
|
||
MethodEnv *f = base->methods[m];
|
||
Atom atomv_out[1] = { atomv[0] };
|
||
Atom method = f->coerceEnter(0, atomv_out);
|
||
return toplevel->op_call(method, argc, atomv);
|
||
}
|
||
}
|
||
}
|
||
|
||
Atom MethodEnv::delproperty(Atom obj, 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 (multiname->contains(core()->publicNamespace) && toplevel->isXmlBase(obj))
|
||
{
|
||
// dynamic props should hide declared methods on delete
|
||
ScriptObject* so = AvmCore::atomToScriptObject(obj);
|
||
bool b = so->deleteMultinameProperty(multiname);
|
||
return b ? trueAtom : falseAtom;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
toplevel->throwReferenceError(kDeleteSealedError, multiname, traits);
|
||
}
|
||
|
||
return falseAtom;
|
||
}
|
||
|
||
Atom MethodEnv::getsuper(Atom obj, Multiname* multiname) const
|
||
{
|
||
VTable* vtable = this->vtable->base;
|
||
Toplevel* toplevel = this->toplevel();
|
||
Binding b = toplevel->getBinding(vtable->traits, multiname);
|
||
switch (b&7)
|
||
{
|
||
default:
|
||
toplevel->throwReferenceError(kReadSealedError, multiname, vtable->traits);
|
||
|
||
case BIND_METHOD:
|
||
{
|
||
#ifdef DEBUG_EARLY_BINDING
|
||
core->console << "getsuper method " << vtable->traits << " " << multiname->name << "\n";
|
||
#endif
|
||
// extracting a virtual method
|
||
MethodEnv *m = vtable->methods[AvmCore::bindingToMethodId(b)];
|
||
return toplevel->methodClosureClass->create(m, obj)->atom();
|
||
}
|
||
|
||
case BIND_VAR:
|
||
case BIND_CONST:
|
||
#ifdef DEBUG_EARLY_BINDING
|
||
core->console << "getsuper slot " << vtable->traits << " " << multiname->name << "\n";
|
||
#endif
|
||
return AvmCore::atomToScriptObject(obj)->getSlotAtom(AvmCore::bindingToSlotId(b));
|
||
|
||
case BIND_SET:
|
||
{
|
||
#ifdef DEBUG_EARLY_BINDING
|
||
core->console << "getsuper setter " << vtable->traits << " " << multiname->name << "\n";
|
||
#endif
|
||
// read on write-only property
|
||
toplevel->throwReferenceError(kWriteOnlyError, multiname, vtable->traits);
|
||
}
|
||
case BIND_GET:
|
||
case BIND_GETSET:
|
||
{
|
||
#ifdef DEBUG_EARLY_BINDING
|
||
core->console << "getsuper getter " << vtable->traits << " " << multiname->name << "\n";
|
||
#endif
|
||
// Invoke the getter
|
||
int m = AvmCore::bindingToGetterId(b);
|
||
MethodEnv *f = vtable->methods[m];
|
||
Atom atomv_out[1] = { obj };
|
||
return f->coerceEnter(0, atomv_out);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
void MethodEnv::setsuper(Atom obj, Multiname* multiname, Atom value) const
|
||
{
|
||
VTable* vtable = this->vtable->base;
|
||
Toplevel* toplevel = this->toplevel();
|
||
Binding b = toplevel->getBinding(vtable->traits, multiname);
|
||
switch (b&7)
|
||
{
|
||
default:
|
||
toplevel->throwReferenceError(kWriteSealedError, multiname, vtable->traits);
|
||
|
||
case BIND_CONST:
|
||
toplevel->throwReferenceError(kConstWriteError, multiname, vtable->traits);
|
||
|
||
case BIND_METHOD:
|
||
toplevel->throwReferenceError(kCannotAssignToMethodError, multiname, vtable->traits);
|
||
|
||
case BIND_GET:
|
||
#ifdef DEBUG_EARLY_BINDING
|
||
core()->console << "setsuper getter " << vtable->traits << " " << multiname->name << "\n";
|
||
#endif
|
||
toplevel->throwReferenceError(kConstWriteError, multiname, vtable->traits);
|
||
|
||
case BIND_VAR:
|
||
#ifdef DEBUG_EARLY_BINDING
|
||
core()->console << "setsuper slot " << vtable->traits << " " << multiname->name << "\n";
|
||
#endif
|
||
AvmCore::atomToScriptObject(obj)->setSlotAtom(AvmCore::bindingToSlotId(b), value);
|
||
return;
|
||
|
||
case BIND_SET:
|
||
case BIND_GETSET:
|
||
{
|
||
#ifdef DEBUG_EARLY_BINDING
|
||
core()->console << "setsuper setter " << vtable->traits << " " << multiname->name << "\n";
|
||
#endif
|
||
// Invoke the setter
|
||
uint32 m = AvmCore::bindingToSetterId(b);
|
||
AvmAssert(m < vtable->traits->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, Multiname* multiname)
|
||
{
|
||
Toplevel* toplevel = this->toplevel();
|
||
if ((atom&7)==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,
|
||
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;
|
||
}
|
||
|
||
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->scopes[outerTraits->size].traits;
|
||
}
|
||
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->scopes[i].isWith)
|
||
{
|
||
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->scopes[i].traits;
|
||
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
|
||
|
||
// look for imported definition (similar logic to OP_finddef). This will
|
||
// find definitions in this script and in other scripts.
|
||
ScriptEnv* script = getScriptEnv(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();
|
||
Atom argv[1] = { script->global->atom() };
|
||
script->coerceEnter(0, argv);
|
||
}
|
||
return global->atom();
|
||
}
|
||
|
||
// no imported definition found. look for dynamic props
|
||
// on the global object
|
||
|
||
ScriptObject* global = AvmCore::atomToScriptObject(
|
||
outer_depth > 0 ? outer->getScope(0) : *scopes
|
||
);
|
||
|
||
// 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 = global;
|
||
do
|
||
{
|
||
if (o->hasMultinameProperty(multiname))
|
||
return global->atom();
|
||
}
|
||
while ((o = o->getDelegate()) != NULL);
|
||
|
||
// If a variable cannot be found
|
||
// throw reference error if strict
|
||
|
||
if (strict)
|
||
toplevel->throwReferenceError(kUndefinedVarError, multiname);
|
||
|
||
return global->atom();
|
||
}
|
||
|
||
Namespace* MethodEnv::internRtns(Atom nsAtom)
|
||
{
|
||
if (((nsAtom&7) != 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->flags & AbstractFunction::NEED_CLOSURE)
|
||
{
|
||
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, 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
|
||
}
|
||
}
|
||
|
||
Atom MethodEnv::getdescendantslate(Atom obj, Atom index, bool attr)
|
||
{
|
||
if (AvmCore::isObject(index))
|
||
{
|
||
ScriptObject* i = AvmCore::atomToScriptObject(index);
|
||
if (i->traits() == core()->traits.qName_itraits)
|
||
{
|
||
QNameObject* qname = (QNameObject*) i;
|
||
Multiname n2;
|
||
qname->getMultiname(n2);
|
||
if (attr)
|
||
n2.setAttr(attr);
|
||
return getdescendants(obj, &n2);
|
||
}
|
||
}
|
||
|
||
// convert index to string
|
||
|
||
AvmCore* core = this->core();
|
||
Multiname name(core->publicNamespace, core->intern(index));
|
||
if (attr)
|
||
name.setAttr();
|
||
return getdescendants(obj, &name);
|
||
}
|
||
|
||
void MethodEnv::checkfilter(Atom obj)
|
||
{
|
||
if ( !toplevel()->isXmlBase(obj) )
|
||
{
|
||
toplevel()->throwTypeError(kFilterError, core()->toErrorString(toplevel()->toTraits(obj)));
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* implements ECMA implicit coersion. returns the coerced value,
|
||
* or throws a TypeError if coersion is not possible.
|
||
*/
|
||
ScriptObject* MethodEnv::coerceAtom2SO(Atom atom, Traits* expected) const
|
||
{
|
||
AvmAssert(expected != NULL);
|
||
AvmAssert(!expected->isMachineType);
|
||
AvmAssert(expected != core()->traits.string_itraits);
|
||
AvmAssert(expected != core()->traits.namespace_itraits);
|
||
|
||
if (AvmCore::isNullOrUndefined(atom))
|
||
return NULL;
|
||
|
||
ScriptObject *so;
|
||
if ((atom&7) == kObjectType &&
|
||
(so=AvmCore::atomToScriptObject(atom))->traits()->containsInterface(expected))
|
||
{
|
||
return so;
|
||
}
|
||
else
|
||
{
|
||
// failed
|
||
#ifdef AVMPLUS_VERBOSE
|
||
//core->console << "checktype failed " << expected << " <- " << atom << "\n";
|
||
#endif
|
||
toplevel()->throwTypeError(kCheckTypeFailedError, core()->atomToErrorString(atom), core()->toErrorString(expected));
|
||
return NULL;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* implements ECMA as operator. Returns the same value, or null.
|
||
*/
|
||
Atom MethodEnv::astype(Atom atom, Traits* expected)
|
||
{
|
||
return core()->istype(atom, expected) ? atom : nullObjectAtom;
|
||
}
|
||
|
||
VTable *MethodEnv::getActivation()
|
||
{
|
||
int type = getType();
|
||
switch(type)
|
||
{
|
||
case kActivation:
|
||
return (VTable *)(activationOrMCTable&~3);
|
||
case kActivationMethodTablePair:
|
||
return getPair()->activation;
|
||
default:
|
||
return NULL;
|
||
}
|
||
}
|
||
|
||
WeakKeyHashtable *MethodEnv::getMethodClosureTable()
|
||
{
|
||
int type = getType();
|
||
if(!activationOrMCTable)
|
||
{
|
||
WeakKeyHashtable *wkh = new (core()->GetGC()) WeakKeyHashtable(core()->GetGC());
|
||
setActivationOrMCTable(wkh, kMethodTable);
|
||
return wkh;
|
||
}
|
||
else if(type == kActivation)
|
||
{
|
||
WeakKeyHashtable *wkh = new (core()->GetGC()) WeakKeyHashtable(core()->GetGC());
|
||
ActivationMethodTablePair *amtp = new (core()->GetGC()) ActivationMethodTablePair(getActivation(), wkh);
|
||
setActivationOrMCTable(amtp, kActivationMethodTablePair);
|
||
return wkh;
|
||
}
|
||
else if(type == kActivationMethodTablePair)
|
||
{
|
||
return getPair()->methodTable;
|
||
}
|
||
return (WeakKeyHashtable*)(activationOrMCTable&~3);
|
||
}
|
||
}
|