зеркало из https://github.com/mozilla/gecko-dev.git
437 строки
12 KiB
C++
437 строки
12 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)[ 2004-2006 ] Adobe Systems Incorporated. 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 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
|
|
{
|
|
using namespace MMgc;
|
|
|
|
AbstractFunction::AbstractFunction()
|
|
{
|
|
this->flags = 0;
|
|
this->method_id = -1;
|
|
}
|
|
|
|
void AbstractFunction::initParamTypes(int count)
|
|
{
|
|
MMGC_MEM_TYPE(this);
|
|
m_types = (Traits**)core()->GetGC()->Calloc(count, sizeof(Traits*), GC::kContainsPointers|GC::kZero);
|
|
}
|
|
|
|
void AbstractFunction::initDefaultValues(int count)
|
|
{
|
|
MMGC_MEM_TYPE(this);
|
|
m_values = (Atom*)core()->GetGC()->Calloc(count, sizeof(Atom), GC::kContainsPointers|GC::kZero);
|
|
}
|
|
|
|
void AbstractFunction::setParamType(int index, Traits* t)
|
|
{
|
|
AvmAssert(index >= 0 && index <= param_count);
|
|
WB(core()->GetGC(), m_types, &m_types[index], t);
|
|
}
|
|
|
|
void AbstractFunction::setDefaultValue(int index, Atom value)
|
|
{
|
|
AvmAssert(index > (param_count-optional_count) && index <= param_count);
|
|
int i = index-(param_count-optional_count)-1;
|
|
AvmAssert(i >= 0 && i < optional_count);
|
|
WBATOM(core()->GetGC(), m_values, &m_values[i], value);
|
|
}
|
|
|
|
#ifdef AVMPLUS_VERBOSE
|
|
Stringp AbstractFunction::format(const AvmCore* core) const
|
|
{
|
|
return core->concatStrings(name ? (Stringp)name : core->newString("?"),
|
|
core->newString("()"));
|
|
}
|
|
#endif //AVMPLUS_VERBOSE
|
|
|
|
bool AbstractFunction::makeMethodOf(Traits* traits)
|
|
{
|
|
if (!m_types[0])
|
|
{
|
|
declaringTraits = traits;
|
|
setParamType(0, traits);
|
|
flags |= NEED_CLOSURE;
|
|
|
|
if (traits->final)
|
|
{
|
|
// all methods of a final class are final
|
|
flags |= FINAL;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
#ifdef AVMPLUS_VERBOSE
|
|
if (pool->verbose)
|
|
core()->console << "WARNING: method " << this << " was already bound to " << declaringTraits << "\n";
|
|
#endif
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void AbstractFunction::makeIntoPrototypeFunction(const Toplevel* toplevel)
|
|
{
|
|
if (declaringTraits == NULL)
|
|
{
|
|
// make sure param & return types are fully resolved.
|
|
// this does not set the verified flag, so real verification will
|
|
// still happen before the function runs the first time.
|
|
resolveSignature(toplevel);
|
|
|
|
// ftraits = new traits, extends function
|
|
// this->declaringTraits = ftraits
|
|
// ftraits->call = this
|
|
// ftraits->construct = create object, call this(), return obj or result
|
|
|
|
AvmCore* core = this->core();
|
|
int functionSlotCount = core->traits.function_itraits->slotCount;
|
|
int functionMethodCount = core->traits.function_itraits->methodCount;
|
|
|
|
// type of F is synthetic subclass of Function, with a unique
|
|
// [[call]] property and a unique scope
|
|
|
|
Traits* ftraits = core->newTraits(core->traits.function_itraits, 1, 1,
|
|
sizeof(ClassClosure));
|
|
ftraits->slotCount = functionSlotCount;
|
|
ftraits->methodCount = functionMethodCount;
|
|
ftraits->pool = pool;
|
|
ftraits->final = true;
|
|
ftraits->needsHashtable = true;
|
|
ftraits->itraits = core->traits.object_itraits;
|
|
this->declaringTraits = ftraits;
|
|
|
|
ftraits->ns = core->publicNamespace;
|
|
#ifdef AVMPLUS_VERBOSE
|
|
ftraits->name = core->internString(
|
|
core->concatStrings(core->newString("Function-"), core->intToString(method_id))
|
|
);
|
|
#endif
|
|
|
|
ftraits->hashTableOffset = ftraits->sizeofInstance;
|
|
ftraits->setTotalSize(ftraits->hashTableOffset + sizeof(Hashtable));
|
|
|
|
ftraits->initTables();
|
|
|
|
AvmAssert(core->traits.function_itraits->linked);
|
|
ftraits->linked = true;
|
|
|
|
|
|
#ifdef AVMPLUS_UNCHECKED_HACK
|
|
// HACK - compiler should do this, and only to toplevel functions
|
|
// that meet the E4 criteria for being an "unchecked function"
|
|
// the tests below are too loose
|
|
|
|
// if all params and return types are Object then make all params optional=undefined
|
|
if (param_count == 0)
|
|
flags |= IGNORE_REST;
|
|
if (!(flags & HAS_OPTIONAL) && param_count > 0)
|
|
{
|
|
if (m_returnType == NULL)
|
|
{
|
|
for (int i=1; i <= param_count; i++)
|
|
{
|
|
if (m_types[i] != NULL)
|
|
return;
|
|
}
|
|
|
|
// make this an unchecked function
|
|
flags |= HAS_OPTIONAL | IGNORE_REST;
|
|
optional_count = param_count;
|
|
initDefaultValues(optional_count);
|
|
for (int i=1; i <= optional_count; i++)
|
|
{
|
|
// since the type is object the default value is undefined.
|
|
setDefaultValue(i, undefinedAtom);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/**
|
|
* convert native args to atoms. argc is the number of
|
|
* args, not counting the instance which is arg[0]. the
|
|
* layout is [instance][arg1..argN]
|
|
*/
|
|
void AbstractFunction::boxArgs(int argc, uint32 *ap, Atom* out)
|
|
{
|
|
// box the typed args, up to param_count
|
|
AvmCore* core = this->core();
|
|
for (int i=0; i <= argc; i++)
|
|
{
|
|
if (i <= param_count)
|
|
{
|
|
Traits* t = paramTraits(i);
|
|
AvmAssert(t != VOID_TYPE);
|
|
|
|
if (t == NUMBER_TYPE)
|
|
{
|
|
out[i] = core->doubleToAtom(*(double *)ap);
|
|
ap += 2;
|
|
}
|
|
else if (t == INT_TYPE)
|
|
{
|
|
out[i] = core->intToAtom((int32)*(Atom*) ap);
|
|
ap += sizeof(Atom) / sizeof(uint32);
|
|
}
|
|
else if (t == UINT_TYPE)
|
|
{
|
|
out[i] = core->uintToAtom((uint32)*(Atom*) ap);
|
|
ap += sizeof(Atom) / sizeof(uint32);
|
|
}
|
|
else if (t == BOOLEAN_TYPE)
|
|
{
|
|
out[i] = (*(Atom*) ap) ? trueAtom : falseAtom;
|
|
ap += sizeof(Atom) / sizeof(uint32);
|
|
}
|
|
else if (!t || t == OBJECT_TYPE)
|
|
{
|
|
out[i] = *(Atom *) ap;
|
|
ap += sizeof(Atom) / sizeof(uint32);
|
|
}
|
|
else
|
|
{
|
|
// it's a pointer type, possibly null
|
|
|
|
void* p = *(void **)ap; // unknown pointer
|
|
if (t == STRING_TYPE)
|
|
{
|
|
out[i] = ((Stringp)p)->atom();
|
|
}
|
|
else if (t == NAMESPACE_TYPE)
|
|
{
|
|
out[i] = ((Namespace*)p)->atom();
|
|
}
|
|
else
|
|
{
|
|
out[i] = ((ScriptObject*)p)->atom();
|
|
}
|
|
ap += sizeof(void *) / sizeof(uint32);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
out[i] = *(Atom *) ap;
|
|
ap += sizeof(Atom) / sizeof(uint32);
|
|
}
|
|
}
|
|
}
|
|
|
|
void AbstractFunction::resolveSignature(const Toplevel* toplevel)
|
|
{
|
|
if (!(flags & LINKED))
|
|
{
|
|
AvmCore* core = this->core();
|
|
AvmAssert(info_pos != NULL);
|
|
|
|
const byte* pos = info_pos;
|
|
|
|
Traits* t = pool->resolveTypeName(pos, toplevel, /*allowVoid=*/true);
|
|
|
|
m_returnType = t;
|
|
|
|
restOffset = 0;
|
|
|
|
// param 0 is contextual
|
|
t = m_types[0];
|
|
if (!t)
|
|
{
|
|
setParamType(0, OBJECT_TYPE);
|
|
restOffset += sizeof(Atom);
|
|
}
|
|
else
|
|
{
|
|
if (t == NUMBER_TYPE)
|
|
restOffset += sizeof(double);
|
|
else
|
|
restOffset += sizeof(Atom);
|
|
if (t->isInterface)
|
|
flags |= ABSTRACT_METHOD;
|
|
}
|
|
|
|
// param types 1..N come from abc stream
|
|
for (int i=1, n=param_count; i <= n; i++)
|
|
{
|
|
t = pool->resolveTypeName(pos, toplevel);
|
|
setParamType(i, t);
|
|
if (t == NUMBER_TYPE)
|
|
restOffset += sizeof(double);
|
|
else
|
|
restOffset += sizeof(Atom);
|
|
}
|
|
|
|
AvmCore::readU30(pos); // name_index;
|
|
pos++; // skip flags
|
|
|
|
if (flags & HAS_OPTIONAL)
|
|
{
|
|
AvmCore::readU30(pos); // optional_count
|
|
|
|
initDefaultValues(optional_count);
|
|
|
|
for (int j=0,n=optional_count; j < n; j++)
|
|
{
|
|
int param = param_count-optional_count+1+j;
|
|
int index = AvmCore::readU30(pos);
|
|
CPoolKind kind = (CPoolKind)*(pos++);
|
|
|
|
// check that the default value is legal for the param type
|
|
Traits* t = this->paramTraits(param);
|
|
AvmAssert(t != VOID_TYPE);
|
|
|
|
Atom value;
|
|
if (t == NULL)
|
|
{
|
|
value = !index ? undefinedAtom : pool->getDefaultValue(toplevel, index, kind, t);
|
|
}
|
|
else if (t == OBJECT_TYPE)
|
|
{
|
|
value = !index ? nullObjectAtom : pool->getDefaultValue(toplevel, index, kind, t);
|
|
if (value == undefinedAtom)
|
|
{
|
|
Multiname qname(t->ns, t->name);
|
|
toplevel->throwVerifyError(kIllegalDefaultValue, core->toErrorString(&qname));
|
|
}
|
|
}
|
|
else if (t == NUMBER_TYPE)
|
|
{
|
|
value = !index ? core->kNaN : pool->getDefaultValue(toplevel, index, kind, t);
|
|
if (!(AvmCore::isInteger(value)||AvmCore::isDouble(value)))
|
|
{
|
|
Multiname qname(t->ns, t->name);
|
|
toplevel->throwVerifyError(kIllegalDefaultValue, core->toErrorString(&qname));
|
|
}
|
|
}
|
|
else if (t == BOOLEAN_TYPE)
|
|
{
|
|
value = !index ? falseAtom : pool->getDefaultValue(toplevel, index, kind, t);
|
|
if (!AvmCore::isBoolean(value))
|
|
{
|
|
Multiname qname(t->ns, t->name);
|
|
toplevel->throwVerifyError(kIllegalDefaultValue, core->toErrorString(&qname));
|
|
}
|
|
}
|
|
else if (t == UINT_TYPE)
|
|
{
|
|
value = !index ? 0|kIntegerType : pool->getDefaultValue(toplevel, index, kind, t);
|
|
if (!AvmCore::isInteger(value) && !AvmCore::isDouble(value))
|
|
{
|
|
Multiname qname(t->ns, t->name);
|
|
toplevel->throwVerifyError(kIllegalDefaultValue, core->toErrorString(&qname));
|
|
}
|
|
double d = AvmCore::number_d(value);
|
|
if (d != (uint32)d)
|
|
{
|
|
Multiname qname(t->ns, t->name);
|
|
toplevel->throwVerifyError(kIllegalDefaultValue, core->toErrorString(&qname));
|
|
}
|
|
}
|
|
else if (t == INT_TYPE)
|
|
{
|
|
value = !index ? 0|kIntegerType : pool->getDefaultValue(toplevel, index, kind, t);
|
|
if (!AvmCore::isInteger(value) && !AvmCore::isDouble(value))
|
|
{
|
|
Multiname qname(t->ns, t->name);
|
|
toplevel->throwVerifyError(kIllegalDefaultValue, core->toErrorString(&qname));
|
|
}
|
|
double d = AvmCore::number_d(value);
|
|
if (d != (int)d)
|
|
{
|
|
Multiname qname(t->ns, t->name);
|
|
toplevel->throwVerifyError(kIllegalDefaultValue, core->toErrorString(&qname));
|
|
}
|
|
}
|
|
else if (t == STRING_TYPE)
|
|
{
|
|
value = !index ? nullStringAtom : pool->getDefaultValue(toplevel, index, kind, t);
|
|
if (!(AvmCore::isNull(value) || AvmCore::isString(value)))
|
|
{
|
|
Multiname qname(t->ns, t->name);
|
|
toplevel->throwVerifyError(kIllegalDefaultValue, core->toErrorString(&qname));
|
|
}
|
|
}
|
|
else if (t == NAMESPACE_TYPE)
|
|
{
|
|
value = !index ? nullNsAtom : pool->getDefaultValue(toplevel, index, kind, t);
|
|
if (!(AvmCore::isNull(value) || AvmCore::isNamespace(value)))
|
|
{
|
|
Multiname qname(t->ns, t->name);
|
|
toplevel->throwVerifyError(kIllegalDefaultValue, core->toErrorString(&qname));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// any other type: only allow null default value
|
|
value = !index ? nullObjectAtom : pool->getDefaultValue(toplevel, index, kind, t);
|
|
if (!AvmCore::isNull(value))
|
|
{
|
|
Multiname qname(t->ns, t->name);
|
|
toplevel->throwVerifyError(kIllegalDefaultValue, core->toErrorString(&qname));
|
|
}
|
|
}
|
|
|
|
setDefaultValue(param, value);
|
|
}
|
|
}
|
|
|
|
/*
|
|
// Don't need this for anything yet, so no point in wasting time parsing it. Here just so we don't
|
|
// forget about it if we add any sections after this one, and need to skip past it.
|
|
if( flags & AbstractFunction::HAS_PARAM_NAMES)
|
|
{
|
|
// AVMPlus doesn't care about the param names, just skip past them
|
|
for( int j = 0; j < param_count; ++j )
|
|
{
|
|
readU30(pos);
|
|
}
|
|
}
|
|
*/
|
|
flags |= LINKED;
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUGGER
|
|
uint32 AbstractFunction::size() const
|
|
{
|
|
uint32 size = sizeof(AbstractFunction);
|
|
size += param_count * 2 * sizeof(Atom);
|
|
return size;
|
|
}
|
|
#endif
|
|
}
|