// Machine.cpp A machine to run AS3 code, with AS2 code in the future
//
//   Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012
//   Free Software Foundation, Inc.
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
//

#include "Machine.h"
#include "as_object.h"
#include "ClassHierarchy.h"
#include "namedStrings.h"
#include "AbcBlock.h"
#include "MultiName.h"
#include "fn_call.h"
#include "abc_function.h"
#include "VM.h"
#include "Globals.h"
#include "Global_as.h"
#include "Class.h"
#include "CodeStream.h"
#include "SWF.h"

namespace gnash {
namespace abc {

/// The type of exceptions thrown by ActionScript.
class ASException
{
public:
	as_value mValue;

	ASException(as_value &v) { mValue = v; }
	ASException() { mValue.set_undefined(); }
};

class ASReferenceError : public ASException
{
public:
	ASReferenceError() : ASException()
	{/**/}
};

class ASTypeError : public ASException
{
public:
	ASTypeError() : ASException()
	{}
};

/// Functions for getting pool constants.
//
/// TODO: it's quite possible for a malformed SWF to ask for out-of-bounds
/// pool access, although at the moment it's mainly Gnash bugs causing this.
/// Throwing an exception is good here, but it's not clear which one.
namespace {

inline const std::string&
pool_string(std::uint32_t index, AbcBlock *pool)
{
	if (!pool) throw ASException();
    try {
        return pool->stringPoolAt(index);
    }
    catch (std::range_error& e) {
        throw ASException();
    }
}

inline int
pool_int(std::uint32_t index, AbcBlock *pool)
{
	if (!pool) throw ASException();
    try {
        return pool->integerPoolAt(index);
    }
    catch (std::range_error& e) {
        throw ASException();
    }
}

inline unsigned int
pool_uint(std::uint32_t index, AbcBlock *pool)
{
	if (!pool) throw ASException();
    try {
        return pool->uIntegerPoolAt(index);
    }
    catch (std::range_error& e) {
        throw ASException();
    }
}

inline double
pool_double(std::uint32_t index, AbcBlock *pool)
{
	if (!pool) throw ASException();
    try {
        return pool->doublePoolAt(index);
    }
    catch (std::range_error& e) {
        throw ASException();
    }
}

inline Namespace*
pool_namespace(std::uint32_t index, AbcBlock *pool)
{
	if (!pool) throw ASException();
    try {
        return pool->namespacePoolAt(index);
    }
    catch (std::range_error& e) {
        throw ASException();
    }

}

inline Method*
pool_method(std::uint32_t index, AbcBlock* pool)
{
	if (!pool) throw ASException();
    try {
        return pool->methodPoolAt(index);
    }
    catch (std::range_error& e) {
        throw ASException();
    }
}

inline Class*
pool_script(std::uint32_t index, AbcBlock* pool)
{
	if (!pool) throw ASException();
    try {
        return pool->classPoolAt(index);
    }
    catch (std::range_error& e) {
        throw ASException();
    }
}

// Don't make this a reference or you'll taint the pool.
inline MultiName
pool_name(std::uint32_t index, AbcBlock* pool)
{
	if (!pool) throw ASException();
	try {
        MultiName multiname = pool->multinamePoolAt(index);
        return multiname;
    }
    catch (std::range_error& e) {
        throw ASException();
    }
}

} // anonymous namespace

/// ENSURE_NUMBER makes sure that the given argument is a number,
/// calling the valueOf method if necessary -- it's a macro so that
/// the valueOf method may be pushed if needed, and then whatever
/// opcode asked for this will be re-entered.
#define ENSURE_NUMBER(vte)													\
{																			\
	as_value *e = &vte;														\
	if (e->is_object())														\
	{																		\
		Property *b = e->to_object(*_global)->findProperty(NSV::PROP_VALUE_OF, 0);	\
		if (b)																\
		{																	\
			mStream->seekTo(opStart);										\
			pushGet(e->to_object(*_global), *e, b);							\
			break;															\
		}																	\
	}																		\
}												   /* end of ENSURE_NUMBER */

/// ENSURE_OBJECT will throw an exception if the argument isn't an
/// object. It's a macro to match with the other ENSURE_ macros.
#ifdef PRETEND
#define ENSURE_OBJECT(vte)													\
{																			\
	if (!vte.is_object())													\
		throw ASException();												\
}												   /* end of ENSURE_OBJECT */
#else
#define ENSURE_OBJECT(vte)
#endif

/// ENSURE_STRING makes sure that the given argument is a string,
/// calling the toString method if necessary -- it's a macro so that
/// the toString may be pushed if needed, and then whatever opcode
/// asked for this will be re-entered.
#define ENSURE_STRING(vte)													\
{																			\
	as_value *c = &vte; /* Don't call vte multiple times */					\
	if (c->is_object())														\
	{																		\
		Property *d = c->to_object(*_global)->findProperty(NSV::PROP_TO_STRING, 0);	\
		if (d)																\
		{																	\
			mStream->seekTo(opStart);										\
			pushGet(c->to_object(*_global), *c, d);							\
			break;															\
		}																	\
	}																		\
}												   /* end of ENSURE_STRING */

/// ABSTRACT_COMPARE is the abstract comparison as described in the ECMA
/// standard.  The 'truth_of_undefined' is used to specify which value
/// should be set for NaN values. It's a macro so that calls may be
/// pushed in the ENSURE_STRING and ENSURE_NUMBER macros.
#define ABSTRACT_COMPARE(store, rv1, rv2, truth_of_undefined) 				\
{ 																			\
	as_value &a = rv1; /* Don't call rv1 multiple times */					\
	as_value &b = rv2; /* Don't call rv2 multiple times */					\
	if (a.ptype() == PTYPE_STRING && b.ptype() == PTYPE_STRING) 			\
	{ 																		\
		ENSURE_STRING(a); 													\
		ENSURE_STRING(b); 													\
		store = a.to_string() < b.to_string(); 								\
	} 																		\
	else 																	\
	{ 																		\
		ENSURE_NUMBER(a); 													\
		ENSURE_NUMBER(b); 													\
		double ad = a.to_number(); double bd = b.to_number();				\
		if (isNaN(ad) || isNaN(bd))											\
			store = truth_of_undefined; 									\
		else if (isInf(ad) && ad > 0)	 									\
			store = false; 													\
		else if (isInf(bd) && bd > 0)	 									\
			store = true; 													\
		else if (isInf(bd) && bd < 0)	 									\
			store = false; 													\
		else if (isInf(ad) && ad < 0)										\
			store = true;													\
		else 																\
			store = ad < bd; 												\
	} 																		\
}												/* end of ABSTRACT_COMPARE */

inline bool abstractEquality(const as_value& a, const as_value& b,
       bool strictness_on)
{
    // TODO: this is a very quick hack to fix some tests without touching
    // as_value. Tamarin has a detailed algorithm for working out equality,
    // which can be implemented as a separate member function of as_value.
    if (a.is_object() && !b.is_object()) {
        return a.to_string() == b.to_string();
    }
    if ( strictness_on ) return a.strictly_equals(b);
    else return a.equals(b);
}								

/// NB: the stubbed but unimplemented as_value::conforms_to no longer exists,
/// but the code is left here for later reference.
#define ABSTRACT_TYPELATE(st, checkval, matchval)							\
{																			\
	bool *store = &st;														\
	/*as_value &a = checkval;  Don't call checkval multiple times */		\
	as_value &b = matchval; /* Don't call matchval multiple times */		\
	*store = true;															\
	if (b.is_object())														\
	{																		\
		as_value v;															\
		b.to_object(*_global)->get_member(NSV::INTERNAL_TYPE, &v);					\
		if (true) /*(!a.conforms_to(mST.find(v.to_string()))) */	\
			*store = false;													\
	}																		\
	else if (b.is_string())													\
	{																		\
		if (true) /*(!a.conforms_to(mST.find(b.to_string())))	*/   	    \
			*store = false;													\
	}																		\
	else																	\
		*store = false;														\
}											   /* end of ABSTRACT_TYPELATE */

#define JUMPIF(jtruth)														\
{																			\
	std::int32_t jumpOffset = mStream->read_S24();								\
	if (jtruth)																\
		mStream->seekBy(jumpOffset);										\
	break;																	\
}														  /* end of JUMPIF */

namespace {

/// Switch the execution context to AVM2, and make sure it's
/// switched back again to what it was before even when there's an exception.
class AVM2Switcher
{
public:
    AVM2Switcher(VM& vm)
        :
        _vm(vm),
        _ver(vm.getAVMVersion())
    {
        _vm.setAVMVersion(VM::AVM2);
    }

    ~AVM2Switcher()
    {
        _vm.setAVMVersion(_ver);
    }

private:
    VM& _vm;
    VM::AVMVersion _ver;
};

}

Machine::Machine(VM& vm)
    :
    _stack(),
    _registers(),
    _scopeStack(),
    mStream(0),
    mST(vm.getStringTable()),
    mDefaultXMLNamespace(0),
    mCurrentScope(0),
    mGlobalScope(0),
    mDefaultThis(0),
    mThis(0),
    _global(0),
    mGlobalReturn(),
    mIgnoreReturn(),
    mExitWithReturn(false),
    mPoolObject(0),
    mCurrentFunction(0),
    _vm(vm)
{
               
}

void
Machine::init()
{

    // TODO: The Global constructor needs the Machine and VM to be more or less
    // fully constructed, so we might think how to do this better.
    AVM2Global* g = new AVM2Global(*this, _vm);
    _global = g;
    
    AVM2Switcher switcher(_vm);
    g->registerClasses();

}

Global_as*
Machine::global()
{
    return _global;
}

void
Machine::push_scope_stack(as_value object)
{
    as_object* scopeObj = object.to_object(*_global);
    assert(scopeObj);
    log_abc("Pushing value %s onto scope stack.", object);
    _scopeStack.push(scopeObj);
    print_scope_stack();
}

void
Machine::execute()
{

    // This automatically switches back again when we leave this scope.
    AVM2Switcher avm2(_vm);

    assert(mStream);

	for (;;) {
		std::size_t opStart = mStream->tellg();
        
        try {

            SWF::abc_action_type opcode = static_cast<SWF::abc_action_type>(
                    mStream->read_as3op());
            
            log_abc("** Executing opcode: %s (%d) **", opcode, (int)opcode);
            
            switch (opcode) 
            {
                default:
                    throw ASException();
                
                /// This is not actually an opcode -- it occurs when the
                /// stream is empty. We may need to return from a function,
                /// or we may be done.
                case SWF::ABC_ACTION_END:
                    return;

                /// 0x01 ABC_ACTION_BKPT
                /// Do: Enter the debugger if one has been invoked.
                /// This is a no-op. Enable it if desired.
                /// 0x02 ABC_ACTION_NOP
                /// Do: Nothing.
                /// 0xF3 ABC_ACTION_TIMESTAMP
                case SWF::ABC_ACTION_NOP:
                case SWF::ABC_ACTION_BKPT:
                case SWF::ABC_ACTION_TIMESTAMP:
                    break;

                /// 0x03 ABC_ACTION_THROW
                /// Stack In:
                ///  obj -- an object
                /// Stack Out:
                ///  .
                /// Do: Throw obj as an exception
                /// Equivalent: ACTIONTHROW
                case SWF::ABC_ACTION_THROW:
                {
                    throw ASException(_stack.pop());
                    break;
                }

                /// 0x04 ABC_ACTION_GETSUPER
                /// Stream:
                ///  name_id -- V32 index to multiname 'name'
                /// Stack In:
                ///  [ns [n]] -- Namespace stuff.
                ///  obj -- an object
                /// Stack Out:
                ///  obj.super.name
                ///  May be the same as the value of obj.name (E.g. inherited
                /// variables)
                case SWF::ABC_ACTION_GETSUPER:
                {
                    // Get the name.
                    MultiName a = pool_name(mStream->read_V32(), mPoolObject);
                    // Finish it, if necessary.
                    _stack.drop(completeName(a));
                    // Get the target object.
                    ENSURE_OBJECT(_stack.top(0));
                    
                    as_object *super = _stack.top(0).to_object(*_global)->
                        get_prototype();
                    
                    // If we don't have a super, throw.
                    if (!super) throw ASReferenceError();
                    const ObjectURI uri(a.getGlobalName(),
                            a.getNamespace()->getURI());
                    Property *b = super->findProperty(uri);
                    // The object is on the top already.
                    pushGet(super, _stack.top(0), b);
                    break;
                }

                /// 0x05 ABC_ACTION_SETSUPER
                /// Stream: UV32 index to multiname 'name'
                /// Stack In:
                ///  val -- an object
                ///  [ns [n]] -- Namespace stuff.
                ///  obj -- an object
                /// Stack Out:
                ///  .
                /// Do: Set obj.super.name to val, if allowable.
                case SWF::ABC_ACTION_SETSUPER:
                {
                    // Get and finish the name.
                    MultiName a = pool_name(mStream->read_V32(), mPoolObject);
                    as_value vobj = _stack.pop(); // The value

                    _stack.drop(completeName(a));

                    ENSURE_OBJECT(_stack.top(0));
                    
                    // This is all wrong. It needs fixing once supers are
                    // correctly implemented.
                    as_object* obj = _stack.pop().to_object(*_global);
                    if (!obj) throw ASReferenceError();

                    as_object* super = obj->get_prototype();
                    if (!super) throw ASReferenceError();

                    const ObjectURI uri(a.getGlobalName(),
                            a.getNamespace()->getURI());
                    Property* b = super->findProperty(uri);

                    _stack.push(vobj);
                    pushSet(super, vobj, b);
                    break;
                }

                /// 0x06 ABC_ACTION_DXNS
                /// Default XML Namespace
                /// Stream: UV32 index to string pool 'nsname'
                /// Do: Create a new public namespace with name nsname, and make
                /// this the default XML namespace. 
                case SWF::ABC_ACTION_DXNS:
                {
                    std::uint32_t soffset = mStream->read_V32();
                    const std::string& uri = pool_string(soffset, mPoolObject);

                    ClassHierarchy& ch = _global->classHierarchy();
                    mDefaultXMLNamespace = ch.anonNamespace(mST.find(uri));
                    break;
                }

                /// 0x07 ABC_ACTION_DXNSLATE
                /// Stack In:
                ///  nsname -- a string object
                /// Stack Out:
                ///  .
                /// Do: Same as ABC_ACTION_DXNS, but the uri is in the stack,
                /// not the stream.
                case SWF::ABC_ACTION_DXNSLATE:
                {
                    ENSURE_STRING(_stack.top(0));
                    const std::string& uri = _stack.top(0).to_string();
                    
                    ClassHierarchy& ch = _global->classHierarchy();
                    mDefaultXMLNamespace = ch.anonNamespace(mST.find(uri));
                    _stack.drop(1);
                    break;
                }

                /// 0x08 ABC_ACTION_KILL
                /// Stream: UV32 frame pointer offset 'offset'
                /// Frame: 
                ///  Kill at offset
                /// Equivalent: ACTION_DELETE
                case SWF::ABC_ACTION_KILL:
                {
                    std::uint32_t regNum = mStream->read_V32();
                    setRegister(regNum, as_value());
                    break;
                }

                /// 0x09 ABC_ACTION_LABEL
                /// Do: Unknown purpose, Tamarin does nothing.
                case SWF::ABC_ACTION_LABEL:
                {
                    break;
                }

                /// 0x0C ABC_ACTION_IFNLT
                /// Stream: S24 jump offset 'jump'
                /// Stack In:
                ///  b -- an object
                ///  a -- an object
                /// Stack Out:
                ///  .
                /// Do: If !(a < b) move by jump in stream, as ABC_ACTION_JUMP
                /// does.
                case SWF::ABC_ACTION_IFNLT:
                {
                    bool truth;
                    ABSTRACT_COMPARE(truth, _stack.top(1), _stack.top(0),
                            false);
                    _stack.drop(2);
                    JUMPIF(!truth); // truth is: a < b
                    break;
                }

                /// 0x0D ABC_ACTION_IFNLE
                /// Stream: S24 jump offset 'jump'
                /// Stack In:
                ///  b -- an object
                ///  a -- an object
                /// Stack Out:
                ///  .
                /// Do: If !(a <= b) move by jump in stream, as ABC_ACTION_JUMP
                /// does.
                case SWF::ABC_ACTION_IFNLE:
                {
                    bool truth;
                    ABSTRACT_COMPARE(truth, _stack.top(0), _stack.top(1), true);
                    _stack.drop(2);
                    JUMPIF(truth); // truth is: b < a
                    break;
                }

                /// 0x0E ABC_ACTION_IFNGT
                /// Stream: S24 jump offset 'jump'
                /// Stack In:
                ///  b -- an object
                ///  a -- an object
                /// Stack Out:
                ///  .
                /// Do: If !(a > b) move by jump in stream, as ABC_ACTION_JUMP
                /// does.
                case SWF::ABC_ACTION_IFNGT:
                {
                    bool truth;
                    ABSTRACT_COMPARE(truth, _stack.top(0), _stack.top(1),
                            false);
                    _stack.drop(2);
                    JUMPIF(!truth); // truth is: b < a
                    break;
                }

                /// 0x0F ABC_ACTION_IFNGE
                /// Stream: S24 jump offset 'jump'
                /// Stack In:
                ///  a -- an object
                ///  b -- an object
                /// Stack Out:
                ///  .
                /// Do: If !(a >= b) move by jump in stream, as ABC_ACTION_JUMP
                /// does.
                case SWF::ABC_ACTION_IFNGE:
                {
                    bool truth;
                    ABSTRACT_COMPARE(truth, _stack.top(1), _stack.top(0), true);
                    _stack.drop(2);
                    JUMPIF(truth); // truth is: a < b
                    break;
                }

                /// 0x10 ABC_ACTION_JUMP
                /// Stream: S24 jump offset 'jump'
                /// Do: If jump is negative, check for interrupts. Move by
                /// jump in stream.
                /// Equivalent: ACTION_BRANCHALWAYS
                case SWF::ABC_ACTION_JUMP:
                {
                    const std::int32_t bytes = mStream->read_S24();
                    log_abc("ABC_ACTION_JUMP: Jumping %d bytes.",bytes);
                    mStream->seekBy(bytes);
                    break;
                }

                /// 0x11 ABC_ACTION_IFTRUE
                /// Stream: S24 jump offset 'jump'
                /// Stack In:
                ///  a -- an object
                /// Stack Out:
                ///  .
                /// Do: If a is 'true', move by jump in stream, as
                /// ABC_ACTION_JUMP does.
                /// Equivalent: ACTION_BRANCHIFTRUE
                case SWF::ABC_ACTION_IFTRUE:
                {
                    const std::int32_t bytes = mStream->read_S24();
                    if (pop_stack().to_bool()) {
                        log_abc("ABC_ACTION_IFTRUE: Jumping %d bytes.",bytes);
                        mStream->seekBy(bytes);
                    }
                    else {
                        log_abc("ABC_ACTION_IFTRUE: Would have jumped %d "
                                "bytes.", bytes);
                    }
                    break;
                }

                /// 0x12 ABC_ACTION_IFFALSE
                /// Stream: S24 jump offset 'jump'
                /// Stack In:
                ///  a -- an object
                /// Stack Out:
                ///  .
                /// Do: If a is 'false', move by jump in stream, as
                /// ABC_ACTION_JUMP does.
                case SWF::ABC_ACTION_IFFALSE:
                {
                    const std::int32_t bytes = mStream->read_S24();
                    const bool truth = pop_stack().to_bool();
                    if (!truth) {
                        log_abc("ABC_ACTION_IFFALSE: Jumping %d bytes.", bytes);
                        mStream->seekBy(bytes);
                    }
                    break;
                }

                /// 0x13 ABC_ACTION_IFEQ
                /// Stream: S24 jump offset 'jump'
                /// Stack In:
                ///  b -- an object
                ///  a -- an object
                /// Stack Out:
                ///  .
                /// Do: If a == b (weakly), move by jump in stream, as
                /// ABC_ACTION_JUMP does.
                case SWF::ABC_ACTION_IFEQ:
                {
                    const std::int32_t bytes = mStream->read_S24();
                    const as_value b = pop_stack();
                    const as_value a = pop_stack();
                    if (a.equals(b)) {
                        log_abc("Jumping %d bytes.", bytes);
                        mStream->seekBy(bytes);
                    }
                    else{
                        log_abc("Would have jumped %d bytes", bytes);
                    }
                    break;
                }

                /// 0x14 ABC_ACTION_IFNE
                /// Stream: S24 jump offset 'jump'
                /// Stack In:
                ///  b -- an object
                ///  a -- an object
                /// Stack Out:
                ///  .
                /// Do: If a != b (weakly), move by jump in stream, as
                /// ABC_ACTION_JUMP does.
                case SWF::ABC_ACTION_IFNE:
                {
                    as_value a = pop_stack();
                    as_value b = pop_stack();
                    const std::int32_t bytes = mStream->read_S24();
                    if (!a.equals(b)) {
                        log_abc("Jumping... %d bytes.", bytes);
                        mStream->seekBy(bytes);
                    }
                    else {
                        log_abc("Would have jumped %d bytes", bytes);
                    }
                    break;
                }

                /// 0x15 ABC_ACTION_IFLT
                /// Stream: S24 jump offset 'jump'
                /// Stack In:
                ///  b -- an object
                ///  a -- an object
                /// Stack Out:
                ///  .
                /// Do: If a < b move by jump in stream, as ABC_ACTION_JUMP
                /// does.
                case SWF::ABC_ACTION_IFLT:
                {
                    as_value b = pop_stack();
                    as_value a = pop_stack();
                    const std::int32_t bytes = mStream->read_S24();
                    const bool jump = newLessThan(a, b, _vm).to_bool();
                    if (jump) {
                        log_abc("Jumping... %d bytes.", bytes);
                        mStream->seekBy(bytes);
                    }
                    else {
                        log_abc("Would have jumped %d bytes", bytes);
                    }
                    break;
                }

                /// 0x16 ABC_ACTION_IFLE
                /// Stream: S24 jump offset 'jump'
                /// Stack In:
                ///  b -- an object
                ///  a -- an object
                /// Stack Out:
                ///  .
                /// Do: If a <= b move by jump in stream, as ABC_ACTION_JUMP
                /// does.
                case SWF::ABC_ACTION_IFLE:
                {
                    bool truth;
                    ABSTRACT_COMPARE(truth, _stack.top(0), _stack.top(1), true);
                    _stack.drop(2);
                    JUMPIF(!truth); // truth is: b < a
                    break;
                }

                /// 0x17 ABC_ACTION_IFGT
                /// Stream: S24 jump offset 'jump'
                /// Stack In:
                ///  b -- an object
                ///  a -- an object
                /// Stack Out:
                ///  .
                /// Do: If a > b move by jump in stream, as ABC_ACTION_JUMP does.
                case SWF::ABC_ACTION_IFGT:
                {
                    std::int32_t bytes = mStream->read_S24();
                    bool truth;
                    // If b < a, then a > b, with undefined as false
                    ABSTRACT_COMPARE(truth, _stack.top(0), _stack.top(1), false);
                    _stack.drop(2);
                    if (truth) {
                        log_abc("Jumping %d bytes.",bytes);
                        mStream->seekBy(bytes);
                    }
                    else{
                        log_abc("Would have jumped %d bytes.",bytes);
                    }
                    break;
                }

                /// 0x18 ABC_ACTION_IFGE
                /// Stream: S24 jump offset 'jump'
                /// Stack In:
                ///  b -- an object
                ///  a -- an object
                /// Stack Out:
                ///  .
                /// Do: If a >= b move by jump in stream, as ABC_ACTION_JUMP
                /// does.
                case SWF::ABC_ACTION_IFGE:
                {
                    bool truth;
                    ABSTRACT_COMPARE(truth, _stack.top(0), _stack.top(1), true);
                    _stack.drop(2);
                    JUMPIF(!truth); // truth is: a < b
                    break;
                }

                /// 0x19 ABC_ACTION_IFSTRICTEQ
                /// Stream: S24 jump offset 'jump'
                /// Stack In:
                ///  b -- an object
                ///  a -- an object
                /// Stack Out:
                ///  .
                /// Do: If a == b (strictly), move by jump in stream, as
                /// ABC_ACTION_JUMP
                case SWF::ABC_ACTION_IFSTRICTEQ:
                {
                    bool truth = abstractEquality(_stack.top(1), _stack.top(0),
                            true);
                    _stack.drop(2);
                    JUMPIF(truth);
                    break;
                }

                /// 0x1A ABC_ACTION_IFSTRICTNE
                /// Stream: S24 jump offset 'jump'
                /// Stack In:
                ///  b -- an object
                ///  a -- an object
                /// Stack Out:
                ///  .
                /// Do: If a != b (strongly), move by jump in stream, as
                /// ABC_ACTION_JUMP
                case SWF::ABC_ACTION_IFSTRICTNE:
                {
                    const bool truth = abstractEquality(_stack.top(1),
                            _stack.top(0), true);
                    _stack.drop(2);
                    JUMPIF(!truth);
                    break;
                }

                /// 0x18 ABC_ACTION_LOOKUPSWITCH
                /// Stream: 3 bytes | V32 count as 'case_count - 1' |
                /// case_count of S24 as 'cases'
                /// Stack In:
                ///  index -- an integer object
                /// Stack Out:
                ///  .
                /// Do: If index >= case_count, reset stream to position on
                /// op entry. Otherwise, move by cases[index] - 1 from stream
                /// position on op entry.
                case SWF::ABC_ACTION_LOOKUPSWITCH:
                {
                    std::size_t npos = mStream->tellg();
                    if (!_stack.top(0).is_number()) throw ASException();

                    std::uint32_t index =
                        toNumber(_stack.top(0), getVM(fn));
                    _stack.drop(1);

                    mStream->seekBy(3); // Skip the intial offset.
                    std::uint32_t cases = mStream->read_V32();
                    // Read from our original position and use it to skip
                    // if the case is out of range.
                    if (index > cases) {
                        mStream->seekTo(npos);
                        mStream->seekTo(npos + mStream->read_S24());
                    }
                    else {
                        mStream->seekTo(npos + 3 * (index + 1));
                        std::uint32_t newpos = mStream->read_S24();
                        mStream->seekTo(npos - 1 + newpos);
                    }
                    break;
                }

                /// 0x30 ABC_ACTION_PUSHSCOPE
                case SWF::ABC_ACTION_PUSHSCOPE:
                {
                    as_value scope_value = pop_stack();
                    if (!scope_value.to_object(*_global)) {
                        // Should throw an exception.
                        IF_VERBOSE_ASCODING_ERRORS(
                        log_aserror(_("Can't push a null value onto the "
                                "scope stack (%s)."), scope_value);
                        );
                        break;
                    }	
                    push_scope_stack(scope_value);
                    break;
                }

                /// 0x1C ABC_ACTION_PUSHWITH
                /// Stack In:
                ///  scope -- a scope
                /// Stack Out:
                ///  .
                /// Do: Enter scope with previous scope as its base.
                /// If 0x1C, start a new base if the previous one was global.
                case SWF::ABC_ACTION_PUSHWITH:
                {
                    log_unimpl("ABC_ACTION_PUSHWITH");
                    // A scope object is just a regular object.
            // 		ENSURE_OBJECT(_stack.top(0));
            // 		as_object *a = _stack.top(0).to_object(*_global);
            // 
            // 		if (!_scopeStack.empty())
            // 			a->set_prototype(_scopeStack.top(0).mScope);
            // 		else
            // 			a->set_prototype(NULL);
            // 
            // 		if (opcode == SWF::ABC_ACTION_PUSHWITH &&
            // 				_scopeStack.totalSize() == _scopeStack.size())
            // 		{
            // 			_scopeStack.push(Scope(0, a));
            // 		}
            // 		else
            // 		{
            // 			_scopeStack.push(Scope(_scopeStack.size(), a));
            // 		}
            // 		mCurrentScope = a;
            // 		_stack.drop(1);
                    break;
                }

                /// 0x1D ABC_ACTION_POPSCOPE
                /// Do: exit current scope. Clear the base if the depth is now 
                ///  shallower than the base's depth.
                case SWF::ABC_ACTION_POPSCOPE:
                {
                    pop_scope_stack();
                    break;
                }

                /// 0x1E ABC_ACTION_NEXTNAME
                /// Stack In:
                ///  index -- an integer object
                ///  obj -- an object
                /// Stack Out:
                ///  name -- the key name of the property at index in obj
                case SWF::ABC_ACTION_NEXTNAME:
                {
                    ENSURE_NUMBER(_stack.top(0));
                    ENSURE_OBJECT(_stack.top(1));
                    as_object *obj = _stack.top(1).to_object(*_global);
                    const std::uint32_t index =
                        toNumber(_stack.top(0), getVM(fn));
                    
                    if (!obj) {
                        // TODO: check what to do here.
                        log_debug("ABC_ACTION_NEXTNAME: expecting object on "
                                "stack, got %s", _stack.top(1));
                        _stack.drop(2);
                        break;
                    }
                    
                    _stack.drop(1);
                    const Property *b = obj->getByIndex(index);
                    if (b) _stack.top(0) = mST.value(getName(b->uri()));
                    else _stack.top(0) = "";
                    break;
                }

                /// 0x1F ABC_ACTION_HASNEXT
                /// Stack In:
                ///  index -- an integer object
                ///  obj -- an object
                /// Stack Out:
                ///  next_index -- next index after index in obj, or 0 if none.
                /// Do: If the index is 0, return the first logical property.
                /// We'll do this by name, since the name id can be used for
                /// this directly.
                case SWF::ABC_ACTION_HASNEXT:
                {
                    ENSURE_NUMBER(_stack.top(0));
                    ENSURE_OBJECT(_stack.top(1));
                    as_object *obj = _stack.top(1).to_object(*_global);
                    std::uint32_t index =
                        toNumber(_stack.top(0), getVM(fn));
                    _stack.drop(1);
                    assert(obj);
                    _stack.top(0) = obj->nextIndex(index);
                    break;
                }

                /// 0x20 ABC_ACTION_PUSHNULL
                /// Stack Out:
                ///  n -- a Null object.
                case SWF::ABC_ACTION_PUSHNULL:
                {
                    as_value value = as_value();
                    value.set_null();
                    push_stack(value);
                    break;
                }

                /// 0x21 ABC_ACTION_PUSHUNDEFINED
                /// Stack Out:
                ///  n -- an Undefined object.
                case SWF::ABC_ACTION_PUSHUNDEFINED:
                {
                    _stack.grow(1);
                    _stack.top(0).set_undefined();
                    break;
                }

                /// 0x23 ABC_ACTION_NEXTVALUE
                /// Stack In:
                ///  index -- an integer object
                ///  obj -- an object (namespaces okay)
                /// Stack Out:
                ///  value -- the value of the key value pair in obj at index.
                case SWF::ABC_ACTION_NEXTVALUE:
                {
                    ENSURE_NUMBER(_stack.top(0));
                    ENSURE_OBJECT(_stack.top(1));
                    as_object *obj = _stack.top(1).to_object(*_global);
                    const std::uint32_t index =
                        toNumber(_stack.top(0), getVM(fn));
                    const Property *b = obj->getByIndex(index);
                    _stack.drop(1);
                    if (!b) _stack.top(0).set_undefined();
                    else {
                        _stack.drop(1);
                        pushGet(obj, _stack.top(0), const_cast<Property*>(b));
                    }
                    break;
                }

                /// 0x24 ABC_ACTION_PUSHBYTE
                /// Stream: S8 as 'byte'
                /// Stack Out:
                ///  byte -- as a raw byte
                case SWF::ABC_ACTION_PUSHBYTE:
                {
                    const std::int8_t b = mStream->read_s8();
                    push_stack(b);
                    break;
                }

                /// 0x25 ABC_ACTION_PUSHSHORT
                /// Stream: V32 as 'value'
                /// Stack Out:
                ///  value -- as a raw integer
                case SWF::ABC_ACTION_PUSHSHORT:
                {
                    const std::int16_t s =
                        static_cast<std::int16_t>(mStream->read_V32());
                    push_stack(s);
                    break;
                }

                /// 0x26 ABC_ACTION_PUSHTRUE
                /// Stack Out:
                ///  true -- the True object
                case SWF::ABC_ACTION_PUSHTRUE:
                    _stack.grow(1);
                    _stack.top(0).set_bool(true);
                    break;

                /// 0x27 ABC_ACTION_PUSHFALSE
                /// Stack Out:
                ///  false -- the False object
                case SWF::ABC_ACTION_PUSHFALSE:
                    push_stack(false);
                    break;

                /// 0x28 ABC_ACTION_PUSHNAN
                /// Stack Out:
                ///  NaN -- the NaN object
                case SWF::ABC_ACTION_PUSHNAN:
                    _stack.grow(1);
                    setNaN(_stack.top(0));
                    break;
            
                /// 0x29 ABC_ACTION_POP
                /// Stack In:
                ///  a -- anything
                /// Stack Out:
                ///  .
                case SWF::ABC_ACTION_POP:
                    pop_stack();
                    break;
            
                /// 0x2A ABC_ACTION_DUP
                /// Stack In:
                ///  a -- anything
                /// Stack Out:
                ///  a
                ///  a
                case SWF::ABC_ACTION_DUP:
                    _stack.grow(1);
                    _stack.top(0) = _stack.top(1);
                    break;

                /// 0x2B ABC_ACTION_SWAP
                /// Stack In:
                ///  a -- anything
                ///  b -- anything
                /// Stack Out:
                ///  b
                ///  a
                case SWF::ABC_ACTION_SWAP:
                {
                    as_value inter = _stack.top(0);
                    _stack.top(0) = _stack.top(1);
                    _stack.top(1) = inter;
                    break;
                }

                /// 0x2C ABC_ACTION_PUSHSTRING
                /// Stream: V32 string pool index 'index'
                /// Stack Out:
                ///  value -- String object from string_pool[index]
                case SWF::ABC_ACTION_PUSHSTRING:
                    push_stack(pool_string(mStream->read_V32(), mPoolObject));
                    break;

                /// 0x2D ABC_ACTION_PUSHINT
                /// Stream: V32 int pool index 'index'
                /// Stack Out:
                ///  value -- Integer object from integer_pool[index]
                case SWF::ABC_ACTION_PUSHINT:
                    push_stack(pool_int(mStream->read_V32(), mPoolObject));
                    break;

                /// 0x2E ABC_ACTION_PUSHUINT
                /// Stream: V32 uint pool index 'index'
                /// Stack Out:
                ///  value -- Unsigned Integer object from unsigned_integer_pool[index]
                case SWF::ABC_ACTION_PUSHUINT:
                {
                    _stack.grow(1);
                    _stack.top(0) = pool_uint(mStream->read_V32(), mPoolObject);
                    break;
                }

                /// 0x2F ABC_ACTION_PUSHDOUBLE
                /// Stream: V32 double pool index 'index'
                /// Stack Out:
                ///  value -- Double object from double_pool[index]
                case SWF::ABC_ACTION_PUSHDOUBLE:
                {
                    push_stack(as_value(pool_double(mStream->read_V32(), mPoolObject)));
                    break;
                }

                /// 0x31 ABC_ACTION_PUSHNAMESPACE
                /// Stream: V32 namespace pool index 'index'
                /// Stack Out:
                ///  ns -- Namespace object from namespace_pool[index]
                case SWF::ABC_ACTION_PUSHNAMESPACE:
                {
#if 0
                    Namespace *ns = pool_namespace(mStream->read_V32(),
                            mPoolObject);
                    _stack.grow(1);

                    // Here we should probably construct a Namespace object,
                    // but there is no need to do it using as_value's
                    // constructor.

                    _stack.top(0) = *ns;
                    break;
#endif
                }

                /// 0x32 ABC_ACTION_HASNEXT2
                /// Stream: V32 frame location 'objloc' | V32 frame location
                /// 'indexloc'
                /// Stack Out:
                ///  truth -- True if frame[objloc] has key/val pair after
                ///   frame[indexloc], following delegates (__proto__) objects
                ///   if needed. False, otherwise.
                /// Frame:
                ///  Change at objloc to object which possessed next value.
                ///  Change at indexloc to index (as object) of the next value.
                /// N.B.: A value of '0' for indexloc initializes to the
                /// first logical property.
                case SWF::ABC_ACTION_HASNEXT2:
                {
                    const std::int32_t oindex = mStream->read_V32();
                    const std::int32_t iindex = mStream->read_V32();

                    const as_value& objv = getRegister(oindex);
                    const as_value& indexv = getRegister(iindex);
                    
                    log_abc("HASNEXT2: Object is %s, index is %d",
                            objv, indexv);

                    as_object *obj = objv.to_object(*_global);
                    if (!obj) {
                        // TODO: Check what to do here.
                        log_error("ABC_ACTION_HASNEXT2: expecting object in "
                                "register %d, got %s", oindex, objv);
                        // Stack is unchanged, so just break? Or push false?
                        // Or throw?
                        break;
                    }
                    
                    std::uint32_t index = toInt(indexv);

                    as_object *owner = 0;
                    int next = obj->nextIndex(index, &owner);
                    log_abc("HASNEXT2: Next index is %d", next);

                    if (next) {
                        push_stack(true);
                        if (owner) setRegister(oindex, owner);
                        else {
                            as_value null;
                            null.set_null();
                            setRegister(oindex, null);
                        }
                        setRegister(iindex, next);
                    }
                    else {
                        push_stack(false);
                        as_value null;
                        null.set_null();
                        setRegister(oindex, null);
                        setRegister(iindex, 0.0);
                    }
                    break;
                }

                /// 0x40 ABC_ACTION_NEWFUNCTION
                /// Stream: V32 'index'
                /// Stack Out:
                ///  func -- the function object
                /// Do: Information about function is in the pool at index. Construct
                /// the function from this information and bind the current scope.
                case SWF::ABC_ACTION_NEWFUNCTION:
                {
                    std::int32_t method_index = mStream->read_V32();
                    log_abc("Creating new abc_function: method index=%u",method_index);
                    Method *m = pool_method(method_index, mPoolObject);
                    abc_function* new_function = m->getPrototype();
                    
                    push_stack(as_value(new_function));
                    break;
                }
                
                /// 0x41 ABC_ACTION_CALL
                /// Stream: V32 'arg_count'
                /// Stack In:
                ///  argN ... arg1 -- the arg_count arguments to pass
                ///  obj -- the object to which the function belongs
                ///  func -- the function to be called
                /// Stack Out:
                ///  value -- the value returned by obj->func(arg1, ...., argN)
                case SWF::ABC_ACTION_CALL:
                {
                    std::uint32_t argc = mStream->read_V32();
                    ENSURE_OBJECT(_stack.top(argc + 1)); // The func
                    ENSURE_OBJECT(_stack.top(argc)); // The 'this'
                    as_function *f = _stack.top(argc + 1).to_function();
                    as_object *obj = _stack.top(argc).to_object(*_global);
                    // We start with argc + 2 values related to this call
                    // on the stack. We want to end with 1 value. We pass
                    // argc values (the parameters), so we need to drop
                    // one more than we pass and store the return just
                    // below that one. Thus:
                    // return is _stack.top(argc + 1)
                    // bottom of arguments is argc deep
                    // drop 1 more value than is passed, on return
                    pushCall(f, obj, _stack.top(argc + 1), argc, -1);
                    break;
                }

                /// 0x42 ABC_ACTION_CONSTRUCT
                /// Stream: V32 'arg_count'
                /// Stack In:
                ///  argN ... arg1 -- the arg_count arguments to pass
                ///  function -- constructor for the object to be constructed
                /// Stack Out:
                ///  value -- obj after it has been constructed as
                ///  obj(arg1, ..., argN)
                case SWF::ABC_ACTION_CONSTRUCT:
                {
                    std::uint32_t argc = mStream->read_V32();
                    as_function *f = _stack.top(argc).to_function();
                    if (!f) {
                        log_abc("CONSTRUCT: No function on stack!");
                        break;
                    }
                    Property b(0, 0, f, NULL);
                    pushCall(f, NULL, _stack.top(argc), argc, 0);
                    break;
                }

                /// 0x43 ABC_ACTION_CALLMETHOD
                /// Stream: V32 'method_id + 1' | V32 'arg_count'
                /// Stack In:
                ///  argN ... arg1 -- the arg_count arguments to pass
                ///  obj -- the object to be called
                /// Stack Out:
                ///  value -- the value returned by obj::'method_id'(arg1,
                ///         ..., argN)
                case SWF::ABC_ACTION_CALLMETHOD:
                {
                    std::uint32_t dispatch_id = mStream->read_V32() - 1;
                    std::uint32_t argc = mStream->read_V32();
                    ENSURE_OBJECT(_stack.top(argc));
                    as_object *obj = _stack.top(argc).to_object(*_global);
                    const Property *f = obj->getByIndex(dispatch_id);
                    as_function* func;
#if 0
                    if (f->isGetterSetter())
                    {
                        // Likely an error, but try to handle it.
                        func = f->getGetter();
                    }
                    else
#endif
                    if (f->getValue(*obj).is_function())
                        func = f->getValue(*obj).to_function();
                    else
                    {
                        // Definitely an error, and not the kind we can handle.
                        throw ASException();
                    }
                    pushCall(func, obj, _stack.top(argc), argc, 0);
                    break;
                }

                /// 0x44 ABC_ACTION_CALLSTATIC
                /// Stream: V32 'method_id' | V32 'arg_count'
                /// Stack In:
                ///  argN ... arg1 -- the arg_count arguments to pass
                ///  obj -- the object to act as a receiver for the static call
                /// Stack Out:
                ///  value -- the value returned by obj->ABC::'method_id' (arg1,
                ///  ..., argN)
                case SWF::ABC_ACTION_CALLSTATIC:
                {
                    Method *m = pool_method(mStream->read_V32(), mPoolObject);
                    std::uint32_t argc = mStream->read_V32();
                    as_function *func = m->getPrototype();
                    ENSURE_OBJECT(_stack.top(argc));
                    as_object *obj = _stack.top(argc).to_object(*_global);
                    pushCall(func, obj, _stack.top(argc), argc, 0);
                    break;
                }
                /// 0x45 ABC_ACTION_CALLSUPER
                /// 0x4E ABC_ACTION_CALLSUPERVOID
                /// Stream: V32 'name_offset' | V32 'arg_count'
                /// Stack In:
                ///  [ns [n]] -- Namespace stuff
                ///  argN ... arg1 -- the arg_count arguments to pass
                ///  obj -- the object whose super is to be called
                /// Stack Out:
                ///  0x45: value -- the value returned by obj::(resolve)'name_offset'::
                ///        super(arg1, ..., argN)
                ///  0x4E: .
                case SWF::ABC_ACTION_CALLSUPER:
                case SWF::ABC_ACTION_CALLSUPERVOID:
                {
                    MultiName a = pool_name(mStream->read_V32(), mPoolObject);
                    std::uint32_t argc = mStream->read_V32();
                    int dropsize = completeName(a);
                    ENSURE_OBJECT(_stack.top(argc + dropsize));
                    _stack.drop(dropsize);

                    // get_super is wrong!
                    as_object* super =
                        _stack.top(argc).to_object(*_global)->get_super();

                    if (!super) throw ASReferenceError();
                    
                    const ObjectURI uri(a.getGlobalName(),
                            a.getNamespace()->getURI());
                    Property* b = super->findProperty(uri);
                    
                    if (!b) throw ASReferenceError();
                    
                    as_function *f = // b->isGetterSetter() ? b->getGetter() :
                        b->getValue(*super).to_function();

                    if (opcode == SWF::ABC_ACTION_CALLSUPER) {
                        pushCall(f, super, _stack.top(argc), argc, 0);
                    }
                    else {
                        // Void call
                        pushCall(f, super, mIgnoreReturn, argc, -1); // drop obj too.
                    }
                    break;
                }
                
                /// 0x46 ABC_ACTION_CALLPROPERTY
                /// 0x4C ABC_ACTION_CALLPROPLEX
                /// 0x4F ABC_ACTION_CALLPROPVOID
                /// Stream: V32 'name_offset' | V32 'arg_count'
                /// Stack In:
                ///  argN ... arg1 -- the arg_count arguments to pass
                ///  [ns [n]] -- Namespace stuff
                ///  obj -- The object whose property is to be accessed.
                /// Stack Out:
                ///  value -- the value from obj::(resolve)'name_offset'
                ///      (arg1, ..., argN)
                ///  (unless ABC_ACTION_CALL_PROPVOID, then: . )
                /// NB: Calls getter/setter if they exist.
                /// If the opcode is ABC_ACTION_CALLPROPLEX, obj is
                /// not altered by getter/setters
                case SWF::ABC_ACTION_CALLPROPERTY:
                case SWF::ABC_ACTION_CALLPROPLEX:
                case SWF::ABC_ACTION_CALLPROPVOID:
                {
                    as_value result;
                    MultiName a = pool_name(mStream->read_V32(), mPoolObject);
                    std::uint32_t argc = mStream->read_V32();

                    fn_call::Args args;
                    get_args(argc, args);

                    if (a.isRuntime()) {
                        _stack.drop(completeName(a));
                    }
                    
                    log_abc("CALL_PROP*: calling property %s of object %s",
                            mST.value(a.getGlobalName()), _stack.top(0));

                    as_value object_val = pop_stack();

                    as_object *object = object_val.to_object(*_global);
                    if (!object) {
                        log_abc(_("CALLPROP: Can't call a method of a value "
                                "that doesn't cast to an object (%s)."),
                                object_val);
                    }
                    else {

                        as_value property =
                            getMember(*object, a.getGlobalName());
                    
                        if (!property.is_undefined() && !property.is_null()) {
                            log_abc("Calling method %s on object %s",
                                    property, object_val);
                            as_environment env = as_environment(_vm);
                            result = invoke(property,env,object,args);

                        }
                        else {
                            log_abc(_("CALLPROP: Property '%s' of object '%s' "
                                    "is '%s', cannot call as method"),
                                    mPoolObject->stringPoolAt(a.getABCName()),
                                    object_val, property);
                        }

                    }
                    
                    if (opcode == SWF::ABC_ACTION_CALLPROPERTY) {
                        push_stack(result);
                    }

            /*		int shift = completeName(a, argc);
                    ENSURE_OBJECT(_stack.top(shift + argc));
                    as_object *obj = _stack.top(argc + shift).to_object(*_global);
                    Property *b = obj->findProperty(a.getABCName(), 
                        a.getNamespace()->getURI());
                    if (!b)
                        throw ASReferenceError();

                    as_function *func;
                    if (b->isGetterSetter())
                    {
                        if (lex_only)
                        {
                            _stack.top(argc + shift).set_undefined();
                            _stack.drop(argc + shift);
                            break;
                        }
#if 0
                        else
                        {
                            //func = b->getGetter();
                            log_error("Can't do  ABC_ACTION_CALLPROPVOID or ABC_ACTION_CALLPROPERTY")
                            break;
                        }
#endif
                    }
                    //else
                        func = b->getValue(obj).to_function();

                    if (opcode == SWF::ABC_ACTION_CALLPROPVOID)
                        pushCall(func, obj, mIgnoreReturn, argc, -shift - 1);
                    else
                        pushCall(func, obj, _stack.top(argc + shift), argc, -shift);*/
                    break;
                }
                
                /// 0x47 ABC_ACTION_RETURNVOID
                /// Do: Return an undefined object up the callstack.
                case SWF::ABC_ACTION_RETURNVOID:
                    mStream->seekTo(0);
                    if (!mStateStack.size()) return;
                    
                    mGlobalReturn = as_value();
                    restoreState();
                    
                    if (mExitWithReturn) return;
                    break;
                
                /// 0x48 ABC_ACTION_RETURNVALUE
                /// Stack In:
                ///  value -- value to be returned
                /// Stack Out:
                ///  .
                /// Do: Return value up the callstack.
                case SWF::ABC_ACTION_RETURNVALUE:
                    mStream->seekTo(0);
                    
                    // Slot the return.
                    mGlobalReturn = pop_stack();
                    // And restore the previous state.
                    restoreState();
                    return;
                
                /// 0x49 ABC_ACTION_CONSTRUCTSUPER
                /// Stream: V32 'arg_count'
                /// Stack In:
                ///  argN ... arg1 -- the arg_count arguments
                ///  obj -- the object whose super's constructor should be
                ///         invoked
                /// Stack Out:
                ///  .
                case SWF::ABC_ACTION_CONSTRUCTSUPER:
                {
                    std::uint32_t argc = mStream->read_V32();
                    fn_call::Args args;
                    get_args(argc, args);
                    
                    as_object* obj = _stack.top(argc).to_object(*_global);

                    // Is get_prototype what we want?
                    as_object* super = obj ? obj->get_prototype() : 0;
                    log_abc("CONSTRUCTSUPER: object %s, super %s, args %s",
                            _stack.top(argc), super, argc);

                    if (!super) {
                        log_error("ABC_ACTION_CONSTRUCTSUPER: No super found");
                        throw ASException();
                    }

                    as_value c = getMember(*super, NSV::PROP_CONSTRUCTOR);
                    pushCall(c.to_function(), super, mIgnoreReturn,
                            argc, -1);
                    break;
                    
                }
                
                /// 0x4A ABC_ACTION_CONSTRUCTPROP
                /// Stream: V32 'name_offset' | V32 'arg_count'
                /// Stack In:
                ///  argN ... arg1 -- the arg_count arguments to pass
                ///  [ns [n]] -- Namespace stuff
                ///  obj -- the object whose property should be constructed
                /// Stack Out:
                ///  value -- the newly constructed prop from obj::(resolve)
                ///   'name_offset'(arg1, ..., argN)
                case SWF::ABC_ACTION_CONSTRUCTPROP:
                {
                    print_stack();
                    as_environment env = as_environment(_vm);
                    MultiName a = pool_name(mStream->read_V32(), mPoolObject);
                    
                    std::uint32_t argc = mStream->read_V32();
                    fn_call::Args args;
                    get_args(argc, args);
                    
                    log_abc("CONSTRUCT_PROP: will try to construct property "
                            "%s on object %s", mST.value(a.getGlobalName()),
                            _stack.top(0));

                    as_object* object = pop_stack().to_object(*_global);

                    if (!object) {
                        //TODO: Should this result in an exeception or an 
                        // actionscript error?
                        log_abc("Can't construct property on a null object. "
                                "Property not constructed.");
                        push_stack(as_value());
                        break;
                    }
                    
                    string_table::key ns = a.getNamespace() ?
                        a.getNamespace()->getURI() : 0;

                    as_value c = getMember(*object, 
                            ObjectURI(a.getGlobalName(), ns));

                    // TODO: don't do this. Scriptes should not be functions;
                    // we should always use the constructor member, most
                    // likely.
                    as_function* ctor = c.to_function();
                    
                    if (ctor) {
                        as_object* newobj = constructInstance(*ctor, env, args);
                        push_stack(as_value(newobj));
                    }

                    // TODO: This is more or less how everything should be done.
                    else {
                        log_abc("The property we found (%s) is not a "
                                "constructor", c);

                        if (c.is_null() || c.is_undefined()) {

                            log_abc("Constructor is undefined, will not "
                                    "construct property.");
                            push_stack(as_value());
                        }
                        else {
                            as_value val = c.to_object(*_global)->getMember(
                                    NSV::PROP_CONSTRUCTOR);

                            invoke(val, env, c.to_object(*_global), args);

                            // Push the constructed property
                            push_stack(c);
                        }
                    }
                    
                    break;
                }
                /// 0x55 ABC_ACTION_NEWOBJECT
                /// Stream: V32 'arg_count'
                /// Stack In:
                ///  prop_value_1 -- a value object
                ///  prop_name_1 -- a string
                ///  .
                ///  . (arg_count value/name pairs in all)
                ///  .
                ///  prop_value_n -- a value object
                ///  prop_name_n -- a string
                /// Stack Out:
                ///  obj -- A new object which contains all of the given
                ///  properties.
                /// NB: This builds an object from its properties, it's not
                ///  a constructor.
                case SWF::ABC_ACTION_NEWOBJECT:
                {
                    as_object *obj = _global->createObject();
                    std::uint32_t argc = mStream->read_V32();
                    int i = argc;
                    while (i--)
                    {
                        as_value val = pop_stack();
                        as_value name = pop_stack();
                        obj->init_member(name.to_string(),val,0,0);
                    }
                    push_stack(as_value(obj));
                    break;
                }
            /// 0x56 ABC_ACTION_NEWARRAY
            /// Stream: V32 'array_size'
            /// Stack In:
            ///  value_n -- a value
            ///  .
            ///  . (array_size of these)
            ///  .
            ///  value_1 -- a value
            /// Stack Out:
            ///  array -- an array { value_1, value_2, ..., value_n }
                case SWF::ABC_ACTION_NEWARRAY:
                {
                    std::uint32_t asize = mStream->read_V32();
                    std::uint32_t i = asize;

                    as_object* arr = _global->createArray();
                    while (i--) {
                        callMethod(arr, NSV::PROP_PUSH, pop_stack());
                    }
                    push_stack(as_value(arr));
                    break;
                }

                /// 0x57 ABC_ACTION_NEWACTIVATION
                /// Stack Out:
                ///  vtable -- A new virtual table, which has the
                ///  previous one as a parent.
                case SWF::ABC_ACTION_NEWACTIVATION:
                {
                    // TODO:  The function contains traits that need to be
                    // included in the activation object.
                    // For now we are using the function object as the
                    // activation object.  There is probably
                    // a better way.
                    //
                    if (!mCurrentFunction->needsActivation()) {
                        log_abc("NEWACTIVATION: called for a function without"
                                "the NEED_ACTIVATION flag");
                    }
                    push_stack(as_value(mCurrentFunction));
                    break;
                }

                /// 0x58 ABC_ACTION_NEWCLASS
                /// Stream: V32 'class_id'
                /// Stack In:
                ///  obj -- An object to be turned into a class. Its super
                ///     is constructed.
                /// Stack Out:
                ///  class -- The newly made class, made from obj and the
                ///     information at cinits_pool[class_id]
                /// NB: This depends on scope and scope base (to determine
                ///     lifetime(?))
                case SWF::ABC_ACTION_NEWCLASS:
                {
                    std::uint32_t cid = mStream->read_V32();
                    log_abc("Class index: %s", cid);
                    Class* c = pool_script(cid, mPoolObject);
                    log_abc("Creating new class id=%u name=%s", c->getName(),
                            mST.value(c->getName()));
                    
                    // This may be 0, and that's fine.
                    as_object* base_class = pop_stack().to_object(*_global);
                    as_object* new_class = c->getPrototype();
                    
                    new_class->set_prototype(base_class);
                    
                    //Create the class.
                    Method* scmethod = c->getStaticConstructor();
                    // What if there isn't one?
                    assert(scmethod);
                    
                    as_function* ctor = c->getConstructor()->getPrototype();
                    new_class->init_member(NSV::PROP_CONSTRUCTOR, ctor, 0);

                    push_stack(new_class);

                    // Call the class's static constructor (which may be
                    // undefined).
                    as_environment env = as_environment(_vm);

                    /// This can be null.
                    as_function* staticCtor = scmethod->getPrototype();
                    
                    // We don't care about the return.
                    fn_call::Args args;
                    invoke(staticCtor, env, new_class, args);

                    log_abc("NEWCLASS(%1%) finished.",
                            mST.value(c->getName()));

                    break;
                }
                
                /// 0x59 ABC_ACTION_GETDESCENDANTS
                /// Stream: V32 'name_id'
                /// Stack In:
                ///  value -- Whose descendants to get
                ///  [ns [n]] -- Namespace stuff
                /// Stack Out:
                ///  ?
                /// NB: This op seems to always throw a TypeError in Tamarin, though I
                /// assume that it ought to do something to yield a list of
                /// descendants of a class.
                case SWF::ABC_ACTION_GETDESCENDANTS:
                {
                    MultiName a = pool_name(mStream->read_V32(), mPoolObject);
                    //as_value &v = _stack.top(0);
                    ENSURE_OBJECT(v);
                    _stack.drop(1);
                    _stack.drop(completeName(a));
                    // TODO: Decide or discover what to do with this.
                    LOG_ONCE( log_unimpl("ABC_ACTION_GETDESCENDANTS") );
                    break;
                }
            /// 0x5A ABC_ACTION_NEWCATCH
            /// Stream: V32 'catch_id'
            /// Stack Out:
            ///  vtable -- vtable suitable to catch an exception of type in
            ///         catch_id.
            /// NB: Need more information on how exceptions are set up.
                case SWF::ABC_ACTION_NEWCATCH:
                {
                    // TODO: Decide if we need this. (Might be a no-op.)
                    break;
                }

                /// 0x5D ABC_ACTION_FINDPROPSTRICT
                /// 0x5E ABC_ACTION_FINDPROPERTY
                /// Stream: V32 'name_id'
                /// Stack In:
                ///  [ns [n]] -- Namespace stuff
                /// Stack Out:
                ///  owner -- object which owns property given by looking
                ///  up the name_id.
                ///  0x5D is the undefined object if not found
                ///  0x5E throws a ReferenceError if not found
                case SWF::ABC_ACTION_FINDPROPSTRICT:
                case SWF::ABC_ACTION_FINDPROPERTY:
                {
                    MultiName a = pool_name(mStream->read_V32(), mPoolObject);
                    if (a.isRuntime()) {
                        _stack.drop(completeName(a));
                    }

                    as_value ret = find_prop_strict(a);


            /*		_stack.drop(completeName(a));
                    as_object *owner;
                    Property *b = mCurrentScope->findProperty(a.getABCName(), 
                        a.getNamespace()->getURI(), &owner);
                    if (!b)
                    {
                        if (opcode == SWF::ABC_ACTION_FINDPROPSTRICT)
                            throw ASReferenceError();
                        else
                            _stack.push(as_value());
                    }
                    else
                    {
                        _stack.push(owner);
                    }*/
                    break;
                }
            /// 0x5F ABC_ACTION_FINDDEF
            /// Stream: V32 'name_id' (no ns expansion)
            /// Stack Out:
            ///  def -- The definition of the name at name_id.
                case SWF::ABC_ACTION_FINDDEF:
                {
                    MultiName a = pool_name(mStream->read_V32(), mPoolObject);
                    // The name is expected to be complete.
                    // TODO
                    break;
                }

                /// 0x60 ABC_ACTION_GETLEX
                /// Stream: V32 'name_id' (no ns expansion)
                /// Stack Out:
                ///  property -- The result of 0x5D (ABC_ACTION_FINDPROPSTRICT)
                ///   + 0x66 (ABC_ACTION_GETPROPERTY)
                case SWF::ABC_ACTION_GETLEX:
                {
                    MultiName a = pool_name(mStream->read_V32(), mPoolObject);
                
                    as_value val = find_prop_strict(a);

                    log_abc("GETLEX: found value %s", val);
                    _stack.top(0) = val;

                    break;
                }
                ///  ABC_ACTION_SETPROPERTY
                /// Stream: V32 'name_id'
                /// Stack In:
                ///  value -- The value to be used
                ///  [ns [n]] -- Namespace stuff
                ///      OR
                ///  [key] -- Key name for property. Will not have both
                ///          Namespace and key.
                ///  obj -- The object whose property is to be set
                /// Stack Out:
                ///  .
                /// NB: If the name at name_id is completely qualified,
                ///     neither a namespace nor a key is needed.  If the
                ///     name_id refers to a name with a runtime
                ///     namespace, then this will be used.  If neither of
                ///     those is true and obj is a dictionary and key is
                ///     a name, then the name_id is discarded and key/value
                ///     is set in the dictionary obj instead.
                case SWF::ABC_ACTION_SETPROPERTY:
                {
                    as_value value = pop_stack();
                    string_table::key ns = 0;
                    string_table::key name = 0;

                    MultiName a = pool_name(mStream->read_V32(), mPoolObject);
                    // TODO: If multiname is runtime we need to also pop
                    // namespace and name values off the stack.
                    if (a.flags() == MultiName::KIND_MultinameL) {
                        log_abc("SETPROPERTY: name is a late runtime "
                                "multiname");
                        as_value nameValue = pop_stack();
                        name = mST.find(nameValue.to_string());
                    }
                    else name = a.getGlobalName();

                    as_value val = pop_stack();
                    as_object *object = val.to_object(*_global);

                    if (!object) {
                        log_error("ABC_ACTION_SETPROPERTY: expecting object "
                                "on stack, got %s!", val);
                        break;
                    }

                    object->set_member(ObjectURI(name, ns), value, false);
                    break;
                }

                /// 0x62 ABC_ACTION_GETLOCAL
                /// Stream: V32 'frame_index'
                /// Frame: value at frame_index is needed
                /// Stack Out:
                ///  value
                case SWF::ABC_ACTION_GETLOCAL:
                {
                    std::uint32_t index = mStream->read_V32();
                    push_stack(getRegister(index));
                    break;
                }

                /// 0x63 ABC_ACTION_SETLOCAL
                /// Stream: V32 'frame_index'
                /// Frame: obj at frame_index is set to value
                /// Stack In:
                ///  value
                /// Stack Out:
                ///  .
                case SWF::ABC_ACTION_SETLOCAL:
                {
                    std::uint32_t index = mStream->read_V32();
                    log_abc("Register index: %u",index);
                    setRegister(index, pop_stack());
                    break;
                }

                /// 0x64 ABC_ACTION_GETGLOBALSCOPE
                /// Stack Out:
                ///  global -- The global scope object
                case SWF::ABC_ACTION_GETGLOBALSCOPE:
                {
                    push_stack(_scopeStack.value(0));
                    break;
                }

                /// 0x65 ABC_ACTION_GETSCOPEOBJECT
                /// Stream: S8 'depth'
                /// Stack Out:
                ///  scope -- The scope object at depth
                case SWF::ABC_ACTION_GETSCOPEOBJECT:
                {
                    std::uint8_t depth = mStream->read_u8();
                    push_stack(get_scope_stack(depth));
                    print_scope_stack();
                    break;
                }
                
                /// 0x66 ABC_ACTION_GETPROPERTY
                /// Stream: V32 'name_id'
                /// Stack In:
                ///  [ns [n]] -- Namespace stuff
                ///      OR
                ///  [key] -- Key name for property. Will not have both
                ///           Namespace and key.
                ///  obj -- The object whose property is to be retrieved
                /// Stack Out:
                ///  prop -- The requested property.
                /// NB: See 0x61 (ABC_ACTION_SETPROPETY) for the decision of
                ///     ns/key.
                case SWF::ABC_ACTION_GETPROPERTY:
                {
                    string_table::key ns = 0;
                    string_table::key name = 0;
                    MultiName a = pool_name(mStream->read_V32(), mPoolObject);
                    // TODO: If multiname is runtime we need to also pop
                    // namespace and name values of the stack.
                    if (a.flags() == MultiName::KIND_MultinameL) {
                        as_value nameValue = pop_stack();
                        name = mST.find(nameValue.to_string());
                    }
                    else name = a.getGlobalName();

                    as_value object_val = pop_stack();
                    as_object* object = object_val.to_object(*_global);
                    
                    log_abc(_("GETPROPERTY: Looking for property "
                            "%s of object %s"), mST.value(name), object_val);

                    if (!object) {
                        log_abc(_("GETPROPERTY: expecting object on "
                                    "stack, got %s."), object_val);
                        push_stack(as_value());
                        break;
                    }
                    
                    as_value prop;
                    
                    const ObjectURI uri(name, ns);
                    const bool found = object->get_member(uri, &prop);
                    if (!found) {
                        log_abc("GETPROPERTY: property %s not found",
                                mST.value(name));
                    }
                    else {
                        log_abc("GETPROPERTY: property %s is %s",
                                mST.value(name), prop);
                    }

                    push_stack(prop);
                    break;
                }

                /// 0x68 ABC_ACTION_INITPROPERTY
                /// Stream V32 'name_id'
                /// Stack In:
                ///  value -- The value to be put into the property.
                ///  [ns [n]] -- Namespace stuff
                ///  obj -- The object whose property is to be initialized
                /// Stack Out:
                ///  .
                /// Do:
                ///  Set obj::(resolve)'name_id' to value, set bindings
                /// from the context.
                case SWF::ABC_ACTION_INITPROPERTY:
                {
                    std::uint32_t index = mStream->read_V32();
                    MultiName a = pool_name(index, mPoolObject);
                    as_value v = pop_stack();
                    // TODO: If multiname is a runtime mutiname we need to also
                    // pop name and namespace values.
                    as_value object_val = pop_stack();

                    as_object* object = object_val.to_object(*_global);
                    if (!object) {
                        log_abc("INITPROPERTY: expecting object on stack, "
                                "got %s", object_val);
                    }
                    else {
                        object->set_member(a.getGlobalName(), v, false);
                    }
                    break;
                }

                /// 0x6A ABC_ACTION_DELETEPROPERTY
                /// Stream: V32 'name_id'
                /// Stack In:
                ///  [ns [n]] -- Namespace stuff
                ///  obj -- The object whose property should be deleted.
                /// Stack Out:
                ///  truth -- True if property was deleted or did not exist,
                ///           else False.
                case SWF::ABC_ACTION_DELETEPROPERTY:
                {
                    MultiName a = pool_name(mStream->read_V32(), mPoolObject);
                    _stack.drop(completeName(a));
                    as_object* obj = _stack.top(0).to_object(*_global);

                    if (!obj) {
                        // TODO: what here?
                        log_abc("DELETEPROPERTY: expecting object on stack, "
                                "got %s", _stack.top(0));
                        break;
                    }

                    // Look in the global namespace if there is none specified.
                    Namespace* n = a.getNamespace();
                    const string_table::key ns = n ? n->getURI() : 0;
                    const string_table::key prop = a.getGlobalName();

                    const bool deleted = obj->delProperty(
                            ObjectURI(prop, ns)).second;
                    _stack.top(0) = deleted;
                    break;
                }

                /// 0x6C ABC_ACTION_GETSLOT
                /// Stream: V32 'slot_index + 1'
                /// Stack In:
                ///  obj -- The object which owns the desired slot.
                /// Stack Out:
                ///  slot -- obj.slots[slot_index]
                case SWF::ABC_ACTION_GETSLOT:
                {
                    as_value val;
                    std::uint32_t sindex = mStream->read_V32();
                    as_object* object = pop_stack().to_object(*_global);
                    if (!object) {
                        log_abc("GETSLOT: Did not find expected object on "
                                "stack");
                        break;
                    } 

                    object->get_member_slot(sindex + 1, &val);

                    log_abc("object has value %s at real_slot=%u abc_slot=%u",
                            val, sindex + 1, sindex);
                    push_stack(val);
                    
                    break;
                }

                /// 0x6D ABC_ACTION_SETSLOT
                /// Stream: V32 'slot_index + 1'
                /// Stack In:
                ///  value -- The value intended for the slot.
                ///  obj -- The object whose slot should be set.
                /// Stack Out:
                ///  .
                /// Do: obj.slots[slot_index] = value
                //
                /// Slot index must be greater than 0 and less than or
                /// equal to the number of slots (so one-based index?).
                case SWF::ABC_ACTION_SETSLOT:
                {
                    std::uint32_t sindex = mStream->read_V32();
                    as_value value = pop_stack();
                    as_value object = pop_stack();
                    log_abc("SETSLOT object: %s, value: %s, index: %s",
                            object, value, sindex);

                    as_object* obj = object.to_object(*_global);
                    if ( ! obj )
                    {
                        IF_VERBOSE_ASCODING_ERRORS(
                        log_aserror(_("ABC_ACTION_SETSLOT: "
                            "unexpected non-object stack value %s"), object);
                        );
                        break;
                    }

                    // We use sindex + 1, because currently as_object sets
                    // a property at a slot index 1 higher than the
                    // index the AbcBlock thinks the property is at.
                    if ( ! obj->set_member_slot(sindex+1, value) )
                    {
                        log_abc("Failed to set property at "
                                "real_slot=%u abc_slot=%u", sindex+1, sindex);
                    }
                    else
                    {
                        log_abc("Set property at real_slot=%u abc_slot=%u",
                                sindex+1, sindex);
                    }

                    break;
                }

                /// 0x6E ABC_ACTION_GETGLOBALSLOT
                /// Stream: V32 'slot_index + 1'
                /// Stack In:
                ///  .
                /// Stack Out:
                ///  slot -- globals.slots[slot_index]
                /// NB: Deprecated
                case SWF::ABC_ACTION_GETGLOBALSLOT:
                {
                    std::uint32_t sindex = mStream->read_V32();
                    if (!sindex)
                        throw ASException();
                    --sindex;
                    _stack.grow(1);
                    //TODO: _stack.top(0) = mGlobal.getSlot(sindex);
                    break;
                }

                /// 0x6F ABC_ACTION_SETGLOBALSLOT
                /// Stream: V32 'slot_index + 1'
                /// Stack In:
                ///  value -- The value to be placed into the slot.
                /// Stack Out:
                ///  .
                /// Do: globals[slot_index] = value
                /// NB: Deprecated
                case SWF::ABC_ACTION_SETGLOBALSLOT:
                {
                    std::uint32_t sindex = mStream->read_V32();
                    if (!sindex)
                        throw ASException();
                    --sindex;
                    //TODO: mGlobal.setSlot(sindex, _stack.pop());
                    break;
                }

                /// 0x70 ABC_ACTION_CONVERT_S
                /// Stack In:
                ///  value -- An object
                /// Stack Out:
                ///  str_value -- value as a string
                case SWF::ABC_ACTION_CONVERT_S:
                    _stack.top(0) = _stack.top(0).to_string();
                    break;

                /// 0x71 ABC_ACTION_ESC_XELEM
                /// Stack In:
                ///  value -- An object to be escaped
                /// Stack Out:
                ///  str_value -- value as a string, escaped suitably for
                ///         an XML element.
                case SWF::ABC_ACTION_ESC_XELEM:
                    log_unimpl("ABC_ACTION_ESC_XELEM");
                    //TODO: set _stack.top(0) to an escaped string.
                    break;

                /// 0x72 ABC_ACTION_ESC_XATTR
                /// Stack In:
                ///  value -- An object to be escaped
                /// Stack Out:
                ///  str_value -- value as a string, escaped suitably for an
                ///     XML attribute.
                case SWF::ABC_ACTION_ESC_XATTR:
                    log_unimpl("ABC_ACTION_ESC_XATTR");
                    //TODO: set _stack.top(0) to an escaped string.
                    break;

                /// 0x73 ABC_ACTION_CONVERT_I
                /// 0x83 ABC_ACTION_COERCE_I (deprecated)
                /// Stack In:
                ///  value -- An object to be converted to Integer
                /// Stack Out:
                ///  int_value -- value as an integer object
                case SWF::ABC_ACTION_CONVERT_I:
                case SWF::ABC_ACTION_COERCE_I:
                    _stack.top(0) = toInt(_stack.top(0));
                    break;

                /// 0x74 ABC_ACTION_CONVERT_U
                /// 0x88 ABC_ACTION_COERCE_U (deprecated)
                /// Stack In:
                ///  value -- An object to be converted to unsigned integer
                /// Stack Out:
                ///  int_value -- value as an unsigned integer object
                case SWF::ABC_ACTION_CONVERT_U:
                case SWF::ABC_ACTION_COERCE_U:
                    _stack.top(0) = static_cast<std::uint32_t>(
                            toNumber(_stack.top(0), getVM(fn)));
                    break;

                /// 0x75 ABC_ACTION_CONVERT_D
                /// 0x84 ABC_ACTION_COERCE_D (deprecated)
                /// Stack In:
                ///  value -- An object to be converted to a double
                /// Stack Out:
                ///  double_value -- value as a double object
                case SWF::ABC_ACTION_CONVERT_D:
                case SWF::ABC_ACTION_COERCE_D:
                    _stack.top(0) = toNumber(_stack.top(0), getVM(fn));
                    break;

                /// 0x76 ABC_ACTION_CONVERT_B
                /// 0x81 ABC_ACTION_COERCE_B (deprecated)
                /// Stack In:
                ///  value -- An object to be converted to a boolean
                /// Stack Out:
                ///  bool_value -- value as a boolean object
                case SWF::ABC_ACTION_CONVERT_B:
                case SWF::ABC_ACTION_COERCE_B:
                    _stack.top(0) = _stack.top(0).to_bool();
                    break;

                /// 0x77 ABC_ACTION_CONVERT_O
                /// Stack In:
                ///  obj -- An object
                /// Stack Out:
                ///  obj -- An object
                /// Do: If obj is Undefined or Null, throw TypeError
                case SWF::ABC_ACTION_CONVERT_O:
                {
                    _stack.top(0) = _stack.top(0).to_object(*_global);
                    if (_stack.top(0).is_undefined() || _stack.top(0).is_null())
                        throw ASTypeError();
                    break;
                }

                /// 0x78 ABC_ACTION_CHECKFILTER
                /// Stack In:
                ///  obj -- An object
                /// Stack Out:
                ///  obj -- An object
                /// Do: If obj is not XML based, throw TypeError
                case SWF::ABC_ACTION_CHECKFILTER:
                {
                    if (!_stack.top(0).is_object()) { 
                        // TODO: check whether it is an XML object.
                        throw ASTypeError();
                    }
                    break;
                }

                /// 0x80 ABC_ACTION_COERCE
                /// Stream: V32 'name_index'
                /// Stack In:
                ///  [ns [n]] -- Possibly name/namespace stuff
                ///  obj -- An object to be converted
                /// Stack Out:
                ///  coerced_obj -- The object as the desired (resolve)
                //      'name_index' type.
                case SWF::ABC_ACTION_COERCE:
                {
                    // TODO: handle runtime names?
                    MultiName a = pool_name(mStream->read_V32(), mPoolObject);

                    as_value value = _stack.top(0);
                    log_abc("COERCE: object for conversion is %s, "
                            "desired type %s", value,
                            mST.value(a.getGlobalName()));

                    // Examples of desired type: "Sprite" "Button",
                    // "GlobalListener", "Object".
                    // Tamarin seems to look up the traits of the
                    // target type. If it's a builtin type (boolean, number,
                    // string, in, uint, object, "any") it succeeds. 
                    // Otherwise check null or undefined and do something.
                    // Otherwise look at the type traits of the original. If
                    // these traits contain the expected interface, return the
                    // original value. Otherwise throw error.
                    break;
                }
                /// 0x82 ABC_ACTION_COERCE_A
                /// Stack In:
                ///  obj -- An object to be converted
                /// Stack Out:
                ///  obj
                /// Do: Nothing. (The 'a' is for atom, and it's unclear
                /// if anything is needed.)
                case SWF::ABC_ACTION_COERCE_A:
                {
                    break;
                }

                /// 0x85 ABC_ACTION_COERCE_S
                /// Stack In:
                ///  obj -- An object to be converted
                /// Stack Out:
                ///  str_obj -- obj as string. nullString object if obj is
                ///  Null or Undefined
                case SWF::ABC_ACTION_COERCE_S:
                {
                    if (_stack.top(0).is_undefined() ||
                            _stack.top(0).is_null()) {
                        _stack.top(0) = "";
                    }
                    else _stack.top(0) = _stack.top(0).to_string();
                    break;
                }

                /// 0x86 ABC_ACTION_ASTYPE
                /// Stream: V32 'name_index'
                /// Stack In:
                ///  [ns [n]] -- Possible namespace stuff
                ///  obj -- An object to be checked
                /// Stack Out:
                ///  cobj -- obj if obj is of type (resolve)'name_index',
                ///     otherwise Null
                case SWF::ABC_ACTION_ASTYPE:
                {
                    MultiName a = pool_name(mStream->read_V32(), mPoolObject);
                    as_value value = pop_stack();
                    //TODO: Make sure the value is of the correct type;
                    push_stack(value);
                    break;
                }

                /// 0x87 ABC_ACTION_ASTYPELATE
                /// Stack In:
                ///  valid -- The object whose type is to be matched
                ///  obj -- An object to be checked
                /// Stack Out:
                ///  cobj -- obj if type of obj conforms to valid, otherwise
                ///     Null
                case SWF::ABC_ACTION_ASTYPELATE:
                {
                    as_value type = pop_stack();
                    as_value value = pop_stack();
                    //TODO: If value is not the type defined by type, then push null.
                    push_stack(value);
                    break;
                }
            /// 0x89 ABC_ACTION_COERCE_O
            /// Stack In:
            ///  obj -- An object
            /// Stack Out:
            ///  cobj -- obj if obj is not Undefined, otherwise Null
                case SWF::ABC_ACTION_COERCE_O:
                {
                    if (_stack.top(0).is_undefined())
                        _stack.top(0) = _stack.top(0).to_object(*_global);
                    else
                        _stack.top(0).set_undefined();
                    break;
                }
            /// 0x90 ABC_ACTION_NEGATE
            /// Stack In:
            ///  obj -- An object
            /// Stack Out:
            ///  negdouble -- -1.0 * (double) obj
                case SWF::ABC_ACTION_NEGATE:
                {
                    _stack.top(0) = -toNumber(_stack.top(0), getVM(fn));
                    break;
                }
            /// 0x91 ABC_ACTION_INCREMENT
            /// Stack In:
            ///  num -- A number, integer or double
            /// Stack Out:
            ///  num + 1
                case SWF::ABC_ACTION_INCREMENT:
                {
                    as_value val = pop_stack();
                    push_stack(as_value(val.to_number() + 1));
                    break;
                }

                /// 0x92 ABC_ACTION_INCLOCAL
                /// Stream: V32 'frame_addr'
                /// Frame: Load i from frame_addr and increment it.
                case SWF::ABC_ACTION_INCLOCAL:
                {
                    std::uint32_t foff = mStream->read_V32();
                    setRegister(foff, toNumber(getRegister(foff), getVM(fn)) + 1);
                    break;
                }

                /// 0x93 ABC_ACTION_DECREMENT
                /// Stack In:
                ///  num -- A number, integer or double
                /// Stack Out:
                ///  num - 1
                case SWF::ABC_ACTION_DECREMENT:
                {
                    _stack.top(0) = toNumber(_stack.top(0), getVM(fn)) - 1;
                    break;
                }

                /// 0x94 ABC_ACTION_DECLOCAL
                /// Stream: V32 'frame_addr'
                /// Frame: Load i from frame_addr and decrement it.
                case SWF::ABC_ACTION_DECLOCAL:
                {
                    const std::uint32_t foff = mStream->read_V32();
                    setRegister(foff, toNumber(getRegister(foff), getVM(fn)) - 1);
                    break;
                }

                /// 0x95 ABC_ACTION_ABC_TYPEOF
                /// Stack In:
                ///  obj -- An object
                /// Stack Out:
                ///  type -- typeof(obj) as a string
                case SWF::ABC_ACTION_ABC_TYPEOF:
                    _stack.top(0) = _stack.top(0).typeOf();
                    break;

                /// 0x96 ABC_ACTION_NOT
                /// Stack In:
                ///  obj -- An object
                /// Stack Out:
                ///  nobj -- A truth object with value !((Boolean) obj)
                case SWF::ABC_ACTION_NOT:
                    _stack.top(0).set_bool(!_stack.top(0).to_bool());
                    break;

                /// 0x97 ABC_ACTION_BITNOT
                /// Stack In:
                ///  obj -- An object
                /// Stack Out:
                ///  nint -- ~((Int) obj)
                case SWF::ABC_ACTION_BITNOT:
                    _stack.top(0) = ~toInt(_stack.top(0));
                    break;

                /// 0xA0 ABC_ACTION_ADD	
                /// Stack In:
                /// a
                /// b
                /// Stack Out:
                /// a + b (double if numeric)
                case SWF::ABC_ACTION_ADD:
                    newAdd(_stack.top(1), _stack.top(0), _vm);
                    _stack.drop(1);
                    break;
                
                /// 0xA1 ABC_ACTION_SUBTRACT
                /// Stack In:
                ///  b
                ///  a
                /// Stack Out:
                ///  a - b (double)
                case SWF::ABC_ACTION_SUBTRACT:
                    subtract(_stack.top(1), _stack.top(0), _vm);
                    _stack.drop(1);
                    break;

                /// 0xA2 ABC_ACTION_MULTIPLY
                /// Stack In:
                ///  a
                ///  b
                /// Stack Out:
                ///  a * b (double)
                case SWF::ABC_ACTION_MULTIPLY:
                    _stack.top(1) = toNumber(_stack.top(1), getVM(fn)) * toNumber(_stack.top(0), getVM(fn));
                    _stack.drop(1);
                    break;

                /// 0xA3 ABC_ACTION_DIVIDE
                /// Stack In:
                ///  b
                ///  a
                /// Stack Out:
                ///  a / b (double)
                case SWF::ABC_ACTION_DIVIDE:
                    _stack.top(1) = toNumber(_stack.top(1), getVM(fn)) / toNumber(_stack.top(0), getVM(fn));
                    _stack.drop(1);
                    break;

                /// 0xA4 ABC_ACTION_MODULO
                /// Stack In:
                ///  b
                ///  a
                /// Stack Out:
                ///  a % b (not integer mod, but remainder)
                case SWF::ABC_ACTION_MODULO:
                {
                    // TODO: test this properly and fix the UB (overflow).
                    double result = toNumber(_stack.top(1), getVM(fn)) / toNumber(_stack.top(0), getVM(fn));
                    int trunc_result = static_cast<int> (result);
                    _stack.top(1) = toNumber(_stack.top(1), getVM(fn)) - 
                        (trunc_result * toNumber(_stack.top(0), getVM(fn)));
                    _stack.drop(1);
                    break;
                }

                /// 0xA5 ABC_ACTION_LSHIFT
                /// Stack In:
                ///  b
                ///  a
                /// Stack Out:
                ///  a << b
                case SWF::ABC_ACTION_LSHIFT:
                {
                    _stack.top(1) = toInt(_stack.top(1)) << toInt(_stack.top(0));
                    _stack.drop(1);
                    break;
                }

                /// 0xA6 ABC_ACTION_RSHIFT
                /// Stack In:
                ///  a
                ///  b
                /// Stack Out:
                ///  a >> b
                case SWF::ABC_ACTION_RSHIFT:
                {
                    _stack.top(1) = toInt(_stack.top(1)) >> toInt(_stack.top(0));
                    _stack.drop(1);
                    break;
                }

                /// 0xA7 ABC_ACTION_URSHIFT
                /// Stack In:
                ///  b
                ///  a
                /// Stack Out:
                ///  ((unsigned) a) >> b
                case SWF::ABC_ACTION_URSHIFT:
                {
                    _stack.top(1) =
                        static_cast<std::uint32_t>(toNumber(_stack.top(1), getVM(fn)))
                        >> toInt(_stack.top(0));
                    _stack.drop(1);
                    break;
                }

                /// 0xA8 ABC_ACTION_BITAND
                ///  a
                ///  b
                /// Stack Out:
                ///  a & b
                case SWF::ABC_ACTION_BITAND:
                    _stack.top(1) = toInt(_stack.top(1)) & toInt(_stack.top(0));
                    _stack.drop(1);
                    break;

                /// 0xA9 ABC_ACTION_BITOR
                /// Stack In:
                ///  b
                ///  a
                /// Stack Out:
                ///  a | b
                case SWF::ABC_ACTION_BITOR:
                    _stack.top(1) = toInt(_stack.top(1)) | toInt(_stack.top(0));
                    _stack.drop(1);
                    break;

                /// 0xAA ABC_ACTION_BITXOR
                /// Stack In:
                ///  b
                ///  a
                /// Stack Out:
                ///  a ^ b
                case SWF::ABC_ACTION_BITXOR:
                {
                    _stack.top(1) = toInt(_stack.top(1)) ^ toInt(_stack.top(0));
                    _stack.drop(1);
                    break;
                }

                /// 0xAB ABC_ACTION_EQUALS
                /// Stack In:
                ///  b
                ///  a
                /// Stack Out:
                ///  truth -- Truth of (a == b) (weakly)
                case SWF::ABC_ACTION_EQUALS:
                {
                    bool truth = abstractEquality(_stack.top(1), _stack.top(0), false);
                    _stack.drop(1);
                    _stack.top(0).set_bool(truth);
                    break;
                }

                /// 0xAC ABC_ACTION_STRICTEQUALS
                /// Stack In:
                ///  b
                ///  a
                /// Stack Out:
                ///  truth -- Truth of (a == b) (strongly, as in 
                ///   0x19 (ABC_ACTION_IFSTRICTEQ))
                case SWF::ABC_ACTION_STRICTEQUALS:
                {
                    bool truth = abstractEquality(_stack.top(1), _stack.top(0), true);
                    _stack.drop(1);
                    _stack.top(0).set_bool(truth);
                    break;
                }

                /// 0xAD ABC_ACTION_LESSTHAN
                /// Stack In:
                ///  b
                ///  a
                /// Stack Out:
                ///  truth -- Truth of (a < b)
                case SWF::ABC_ACTION_LESSTHAN:
                {
                    bool truth;
                    ABSTRACT_COMPARE(truth, _stack.top(1), _stack.top(0), false);
                    _stack.drop(1);
                    _stack.top(0).set_bool(truth); // truth is a < b
                    break;
                }

                /// 0xAE ABC_ACTION_LESSEQUALS
                /// Stack In:
                ///  b
                ///  a
                /// Stack Out:
                ///  truth -- Truth of (a <= b)
                case SWF::ABC_ACTION_LESSEQUALS:
                {
                    bool truth;
                    ABSTRACT_COMPARE(truth, _stack.top(0), _stack.top(1), true);
                    _stack.drop(1);
                    _stack.top(0).set_bool(!truth); // truth is b < a
                    break;
                }

                /// 0xAF ABC_ACTION_GREATERTHAN
                /// Stack In:
                ///  b
                ///  a
                /// Stack Out:
                ///  truth -- Truth of (a > b)
                case SWF::ABC_ACTION_GREATERTHAN:
                {
                    bool truth;
                    ABSTRACT_COMPARE(truth, _stack.top(0), _stack.top(1), false);
                    _stack.drop(1);
                    _stack.top(0).set_bool(truth); // truth is b < a
                    break;
                }

                /// 0xB0 ABC_ACTION_GREATEREQUALS
                /// Stack In:
                ///  b
                ///  a
                /// Stack Out:
                ///  truth -- Truth of (a >= b)
                case SWF::ABC_ACTION_GREATEREQUALS:
                {
                    bool truth;
                    ABSTRACT_COMPARE(truth, _stack.top(1), _stack.top(0), true);
                    _stack.drop(1);
                    _stack.top(0).set_bool(!truth); // truth is a < b
                    break;
                }

                /// 0xB1 ABC_ACTION_INSTANCEOF
                /// Stack In:
                ///  super -- An object
                ///  val -- An object
                /// Stack Out:
                ///  truth -- Truth of "val is an instance of super"
                case SWF::ABC_ACTION_INSTANCEOF:
                {
                    bool truth;
                    ABSTRACT_TYPELATE(truth, _stack.top(1), _stack.top(0));
                    _stack.top(1).set_bool(truth);
                    _stack.drop(1);
                    break;
                }

                /// 0xB2 ABC_ACTION_ISTYPE
                /// Stream: V32 'name_id'
                /// Stack In:
                ///  [ns] -- Namespace stuff
                ///  obj -- An object
                /// Stack Out:
                ///  truth -- Truth of "obj is of the type given in (resolve)'name_id'"
                case SWF::ABC_ACTION_ISTYPE:
                {
                    MultiName a = pool_name(mStream->read_V32(), mPoolObject);
                    _stack.drop(completeName(a));
                    // TODO: Implement it.
                    //_stack.top(0).set_bool(_stack.top(0).conforms_to(a.getABCName()));
                }

                /// 0xB3 ABC_ACTION_ISTYPELATE
                /// Stack In:
                ///  type -- A type to match
                ///  obj -- An object
                /// Stack Out:
                ///  truth -- Truth of "obj is of type"
                case SWF::ABC_ACTION_ISTYPELATE:
                {
                    as_value type = pop_stack();
                    as_value value = pop_stack();
                    as_object* const valueObject = value.to_object(*_global);
                    as_object* const typeObject = type.to_object(*_global);

                    if (!valueObject || !typeObject) {
                        // TODO: what here!?
                        // This should eventually be a malformed SWF error.
                        log_error("ACTION_ISTYPELATE: require two objects "
                                "on stack, got obj: %s, type: %s.",
                                value, type);
                        break;
                    }
                    const bool truth = valueObject->instanceOf(typeObject);
                    push_stack(truth);
                    break;
                }

                /// 0xB4 ABC_ACTION_IN
                /// Stack In:
                ///  obj -- The object to search for it
                ///  name -- The name to find
                /// Stack Out:
                ///  truth -- True if name is in current namespace or anywhere in object.
                ///   Don't look in the namespace if obj is a dictionary.
                /// NB: Since there doesn't seem to be a way to make a dictionary, this
                /// is not done. If there is, fix this lack.
                case SWF::ABC_ACTION_IN:
                {
                    log_unimpl("ABC_ACTION_IN");
                    //TODO: _stack.top(1).set_bool(_stack.top(1).to_object(*_global).contains(_stack.top(0)));
                    _stack.drop(1);
                    break;
                }

                /// 0xC0 ABC_ACTION_INCREMENT_I
                /// See: 0x91 (ABC_ACTION_INCREMENT), but forces types to int, not double
                case SWF::ABC_ACTION_INCREMENT_I:
                {
                    _stack.top(0) = toInt(_stack.top(0)) + 1;
                    break;
                }

                /// 0xC1 ABC_ACTION_DECREMENT_I
                /// See: 0x93 (ABC_ACTION_DECREMENT), but forces types to int, not double
                case SWF::ABC_ACTION_DECREMENT_I:
                {
                    _stack.top(0) = toInt(_stack.top(0)) - 1;
                    break;
                }

                /// 0xC2 ABC_ACTION_INCLOCAL_I
                /// See: 0x92 (ABC_ACTION_INCLOCAL), but forces types to int
                /// not double
                case SWF::ABC_ACTION_INCLOCAL_I:
                {
                    const std::uint32_t foff = mStream->read_V32();
                    setRegister(foff,  toInt(getRegister(foff)) + 1);
                    break;
                }

                /// 0xC3 ABC_ACTION_DECLOCAL_I
                /// See: 0x94 (ABC_ACTION_DECLOCAL), but forces types to int,
                /// not double
                case SWF::ABC_ACTION_DECLOCAL_I:
                {
                    const std::uint32_t foff = mStream->read_V32();
                    setRegister(foff, toInt(getRegister(foff)) - 1);
                    break;
                }

                /// 0xC4 ABC_ACTION_NEGATE_I
                /// See: 0x90 (ABC_ACTION_NEGATE), but forces type to int,
                /// not double
                case SWF::ABC_ACTION_NEGATE_I:
                {
                    _stack.top(0) = - toInt(_stack.top(0));
                    break;
                }

                /// 0xC5 ABC_ACTION_ADD_I
                /// See: 0xA0 (ABC_ACTION_ADD), but forces type to int
                case SWF::ABC_ACTION_ADD_I:
                {
                    _stack.top(1) = toInt(_stack.top(1)) +
                        toInt(_stack.top(0));
                    _stack.drop(1);
                    break;
                }

                /// 0xC6 ABC_ACTION_SUBTRACT_I
                /// See: 0xA1 (ABC_ACTION_SUBTRACT), but forces type to int
                case SWF::ABC_ACTION_SUBTRACT_I:
                {
                    _stack.top(1) = toInt(_stack.top(1)) -
                        toInt(_stack.top(0));
                    _stack.drop(1);
                    break;
                }

                /// 0xC7 ABC_ACTION_MULTIPLY_I
                /// See: 0xA2 (ABC_ACTION_MULTIPLY), but forces type to int
                case SWF::ABC_ACTION_MULTIPLY_I:
                {
                    _stack.top(1) = toInt(_stack.top(0)) * toInt(_stack.top(1));
                    _stack.drop(1);
                    break;
                }

                /// 0xD0 ABC_ACTION_GETLOCAL0
                /// 0xD1 ABC_ACTION_GETLOCAL1
                /// 0xD2 ABC_ACTION_GETLOCAL2
                /// 0xD3 ABC_ACTION_GETLOCAL3
                /// Frame: Load frame[#] as val
                /// Stack Out:
                ///  val
                case SWF::ABC_ACTION_GETLOCAL0:
                case SWF::ABC_ACTION_GETLOCAL1:
                case SWF::ABC_ACTION_GETLOCAL2:
                case SWF::ABC_ACTION_GETLOCAL3:
                {
                    //We shouldn't need to a call grow stack, because each function should now how big the stack will need to be and should allocate all the space, when it is loaded into the vm.
            //		GROW_STACK();
            //		_stack.grow(1);
            //		_stack.push() instead?

                    push_stack(getRegister(opcode- SWF::ABC_ACTION_GETLOCAL0));
            //		_stack.top(0) = _registers.value(opcode - SWF::ABC_ACTION_GETLOCAL0);
                    break;
                }
            /// 0xD4 ABC_ACTION_SETLOCAL0
            /// 0xD5 ABC_ACTION_SETLOCAL1
            /// 0xD6 ABC_ACTION_SETLOCAL2
            /// 0xD7 ABC_ACTION_SETLOCAL3
            /// Frame: Store val as frame[#]
            /// Stack In:
            ///  val
            /// Stack Out:
            ///  .
                case SWF::ABC_ACTION_SETLOCAL0:
                case SWF::ABC_ACTION_SETLOCAL1:
                case SWF::ABC_ACTION_SETLOCAL2:
                case SWF::ABC_ACTION_SETLOCAL3:
                {
                    setRegister(opcode - SWF::ABC_ACTION_SETLOCAL0,
                            pop_stack());
                    break;
                }

            /// 0xEF ABC_ACTION_DEBUG
            /// Stream: 7 bytes of unknown stuff to be skipped
            /// Do: skip ahead 7 bytes in stream
                case SWF::ABC_ACTION_DEBUG:
                {
                    mStream->seekBy(7);
                    break;
                }
            /// 0xF0 ABC_ACTION_DEBUGLINE
            /// Stream: V32 'line_number'
            /// Do: Nothing, but line_number is for the debugger if wanted.
                case SWF::ABC_ACTION_DEBUGLINE:
                {
                    mStream->skip_V32();
                    break;
                }
            /// 0xF1 ABC_ACTION_DEBUGFILE
            /// Stream: V32 'name_offset'
            /// Do: Nothing. 'name_offset' into string pool is the file name if wanted.
                case SWF::ABC_ACTION_DEBUGFILE:
                {
                    mStream->skip_V32();
                    break;
                }

                /// 0xF2 ABC_ACTION_BKPTLINE
                /// Stream: V32 'line_number'
                /// Do: Enter debugger if present, line_number is the
                /// line number in source.
                case SWF::ABC_ACTION_BKPTLINE:
                    mStream->skip_V32();
                    break;
            }

            log_abc("* DONE *");
            IF_VERBOSE_ACTION(print_stack());
        } 
        catch (const StackException& s) {
            log_error("Threw a stack exception during AVM2 execution.");
        }
        catch (ASException& s) {
            // TODO: The exception should be pushed back into AS execution
            // somehow.
            log_unimpl("Caught an AS exception from AVM2 execution.");
        }

	} 
} 

void
Machine::getMember(Class* pDefinition, MultiName& name,
	as_value& instance)
{
	if (!instance.is_object())
		throw ASTypeError();
#if 0
	if (!pBinding->isGetSet())
	{
		//TODO: _stack.push(pBinding->getFromInstance(instance));
		return;
	}

	// This is a getter, so we need to execute it. Even those
	// written in C++ get called like this, with pushCall handling.
	// And push the instance ('this')
	_stack.push(instance);
	pushCall(1, &_stack.top(0), pBinding); //TODO: pBinding->getGetter());
#else
UNUSED(pDefinition);
UNUSED(name);
#endif
}

void
Machine::setMember(Class *pDefinition, MultiName& name, as_value& instance,
	as_value& newvalue)
{
	if (!instance.is_object())
		throw ASReferenceError();

	return;
	// TODO:
#if 0
	asBinding *pBinding = pDefinition->getBinding(name.getABCName());

	if (pBinding->isReadOnly())
		throw ASReferenceError();

	if (!pBinding->isGetSet())
	{
		//TODO: pBinding->setInInstance(instance, newvalue);
		return;
	}

	// Two parameters -- the target object, the value to set.
	_stack.push(instance);
	_stack.push(newvalue);
	pushCall(2, &_stack.top(1), pBinding); //TODO: pBinding->getSetter());
#else
UNUSED(pDefinition);
UNUSED(name);
UNUSED(newvalue);
#endif
}

int
Machine::completeName(MultiName& name, int offset)
{
    
    // TODO: implement this properly.
    // Should this really be called when there's nothing on the stack?
    if (_stack.empty()) return 0;

	int size = 0;

	if (name.isRuntime())
	{
		as_value obj = _stack.top(offset);

		if (name.isRtns()) {
			++size; // Ignore the Namespace.
        }
	}
	else if (name.isRtns())
	{
		//TODO: This should be a namespace //name.setNamespace(_stack.top(offset));
		++size;
	}
	return size;
}

Class*
Machine::findSuper(as_value &v, bool find_for_primitive)
{
	if (v.is_undefined() || v.is_null()) return NULL;

	if (v.is_object()) {
		Class *pProto = NULL; // TODO: v.to_object(*_global)->getScript();
		return pProto ? pProto->getSuper() : NULL;
	}

	if (!find_for_primitive) return 0;

	if (v.is_number()) {
		return NULL; // TODO: _classes->getScript(NSV::CLASS_NUMBER);
	}

	// And so on...
	// TODO: Other primitives
	return 0;
}

void
Machine::immediateFunction(const as_function* func, as_object* thisptr,
        as_value& storage, unsigned char stack_in, short stack_out)
{
    assert(func);

	// TODO: Set up the fn to use the stack
    fn_call::Args args;
    size_t st = 0;
    while (st < stack_in) {
        args += _stack.top(st);
        ++st;
    }

	fn_call fn(thisptr, as_environment(_vm), args);
    _stack.drop(stack_in - stack_out);
	saveState();
    _stack.grow(stack_in - stack_out);
    _stack.setDownstop(stack_in);
	mThis = thisptr;
	storage = const_cast<as_function*>(func)->call(fn);
	restoreState();
}

void
Machine::pushGet(as_object *this_obj, as_value &return_slot, Property *prop)
{
	if (!prop) return;

	if (prop->isGetterSetter()) {
		//TODO pushCall(prop->getGetter(), this_obj, return_slot, 0);
		return;
	}

	return_slot = prop->getValue(*this_obj);
}

void
Machine::pushSet(as_object *this_obj, as_value &value, Property *prop)
{
	if (!prop) return;

	if (prop->isGetterSetter()) {
		_stack.push(value);
		//TODO pushCall(prop->getSetter(), this_obj, mIgnoreReturn, 1);
		return;
	}

	prop->setValue(*this_obj, value);
}

void
Machine::pushCall(as_function *func, as_object *pthis, as_value& return_slot,
	unsigned char stack_in, short stack_out)
{

    log_abc("Pushing function call for function %s", func);

	if (1 || func->isBuiltin())
	{
		immediateFunction(func, pthis, return_slot, stack_in, stack_out);
		return;
	}
	// TODO: Make this work for stackless.

	// Here is where the SafeStack shines:
	// We set the stack the way it should be on return.
	_stack.drop(stack_in - stack_out);
	// We save that state.
	saveState();
	// Set the 'this' for the new call
	mThis = pthis;
	// Retrieve the stack. (It wasn't lost)
	_stack.grow(stack_in - stack_out);
	// And then we set the downstop
	_stack.setDownstop(stack_in);

	// When control goes to the main loop of the interpreter, it will
	// automatically start executing the method.
}

void
Machine::restoreState()
{
	log_abc("Restoring state.");
	State &s = mStateStack.top(0);
	s.to_debug_string();
//	_stack.setAllSizes(s._stackTotalSize, s._stackDepth);
	_scopeStack.setAllSizes(s.mScopeTotalSize, s._scopeStackDepth);
	mStream = s.mStream;
	_registers = s._registers;
	mCurrentFunction = s.mFunction;
//	mExitWithReturn = s.mReturn;
//	mDefaultXMLNamespace = s.mDefaultXMLNamespace;
//	mCurrentScope = s.mCurrentScope;
//	mGlobalReturn = s.mGlobalReturn;
//	mThis = s.mThis;
	mStateStack.pop();
}

void
Machine::saveState()
{
	log_abc("Saving state.");
	mStateStack.grow(1);
	State &s = mStateStack.top(0);
	s._stackDepth = _stack.getDownstop();
	s._stackTotalSize = _stack.totalSize();
	s._scopeStackDepth = _scopeStack.getDownstop();
	s.mScopeTotalSize = _scopeStack.totalSize();
	s.mStream = mStream;
	s.to_debug_string();
	s._registers = _registers;
	s.mFunction = mCurrentFunction;
//	s.mReturn = mExitWithReturn;
//	s.mDefaultXMLNamespace = mDefaultXMLNamespace;
//	s.mCurrentScope = mCurrentScope;
//	s.mGlobalReturn = mGlobalReturn;
//	s.mThis = mThis;
}

void
Machine::initMachine(AbcBlock* pool_block)
{
	mPoolObject = pool_block;
	log_debug("Getting entry script.");
	Class* start_script = pool_block->scripts().back();
	log_debug("Getting constructor.");
	Method* constructor = start_script->getConstructor();
	clearRegisters(constructor->getMaxRegisters());
	log_debug("Loading code stream.");
	mStream = constructor->getBody();
	mCurrentFunction = constructor->getPrototype();
	setRegister(0, _global);
}

//This is called by abc_functions to execute their code stream.
//TODO: There is probably a better way to do this, once we understand what the VM is supposed
//todo, this should be fixed.
as_value
Machine::executeFunction(Method* method, const fn_call& fn)
{
	
    //TODO: Figure out a good way to use the State object to handle
    //returning values.
	mCurrentFunction = method->getPrototype();
	bool prev_ext = mExitWithReturn;
	CodeStream *stream = method->getBody();
    
    // Protect the current stack from alteration
    // TODO: use saveState only, but not before checking other effects.
    size_t stackdepth = _stack.fixDownstop();
    size_t stacksize = _stack.totalSize();
    size_t scopedepth = _scopeStack.fixDownstop();
    size_t scopesize = _scopeStack.totalSize();
	
    saveState();
	mStream = stream;
	clearRegisters(method->getMaxRegisters());
	
    log_abc("Executing function: max registers %s, scope depth %s, "
            "max scope %s, max stack: %s", method->getMaxRegisters(),
            method->scopeDepth(), method->maxScope(), method->maxStack());
	mExitWithReturn = true;
	setRegister(0, fn.this_ptr);
	for (unsigned int i=0;i<fn.nargs;i++) {
		setRegister(i + 1, fn.arg(i));
	}
	
    execute();
	mExitWithReturn = prev_ext;
	
    _stack.setAllSizes(stacksize, stackdepth);
    _scopeStack.setAllSizes(scopesize, scopedepth);

	return mGlobalReturn;
}

void
Machine::executeCodeblock(CodeStream* stream)
{
	mStream = stream;
	execute();
}

void
Machine::markReachableResources() const
{
    _global->setReachable();
}

void
Machine::instantiateClass(std::string className, as_object* /*global*/)
{

    if (!mPoolObject) {
        log_debug("No ABC block! Can't instantiate class!");
        return;
    }

    log_debug("instantiateClass: class name %s", className);

	Class* cl = mPoolObject->locateClass(className);
    if (!cl)
    {
        /// This seems like a big error.
        log_error("Could not locate class '%s' for instantiation", className);
        return;
    }
	
    Method* ctor = cl->getConstructor();

    if (!ctor) {
        log_error("Class found has no constructor, can't instantiate "
                "class");
        return;
    }

	clearRegisters(ctor->getMaxRegisters());
	mCurrentFunction = ctor->getPrototype();

    // Protect the current stack from alteration
    // TODO: use saveState
    size_t stackdepth = _stack.fixDownstop();
    size_t stacksize = _stack.totalSize();
    size_t scopedepth = _scopeStack.fixDownstop();
    size_t scopesize = _scopeStack.totalSize();

    // The value at _registers[0] is generally pushed to the stack for
    // CONSTRUCTSUPER, which apparently expects the object whose super
    // is to be constructed. The pp's stack names the class for instantiation
    // at register 0 when the constructor body is executed, which must
    // correspond to the class prototype.
	setRegister(0, cl->getPrototype());
	executeCodeblock(ctor->getBody());
    log_debug("Finished instantiating class %s", className);

    _stack.setAllSizes(stacksize, stackdepth);
    _scopeStack.setAllSizes(scopesize, scopedepth);

}

as_value
Machine::find_prop_strict(MultiName multiname)
{
	
    log_abc("Looking for property %2% in namespace %1%",
            mST.value(multiname.getNamespace()->getURI()),
            mST.value(multiname.getGlobalName()));

    // We should not push anything onto the scope stack here; whatever is
    // needed should already be pushed. The pp will not call FINDPROP*
    // when the scope stack is empty.
    //
    // However, the complete scope stack, including elements that are
    // 'invisible' to this scope, is available
	as_value val;
    print_scope_stack();
    const string_table::key var = multiname.getGlobalName();
    const string_table::key ns = multiname.getNamespace()->getURI();

	for (size_t i = 0; i < _scopeStack.totalSize(); ++i)
    {
		as_object* scope_object = _scopeStack.at(i);
		if (!scope_object) {
			log_abc("Scope object is NULL.");
			continue;
		}
        
        if (scope_object->get_member(ObjectURI(var, ns), &val)) {
            push_stack(_scopeStack.at(i));
			return val;
		}
	}

    // TODO: find out what to do here.
    log_abc("Failed to find property in scope stack.");
	as_object* null = 0;
    push_stack(null);
    return val;
}

void
Machine::print_stack()
{

	std::stringstream ss;
	ss << "Stack: ";
	for (unsigned int i = 0; i < _stack.totalSize(); ++i) {
		if (i!=0) ss << " | ";
		ss << _stack.at(i);
	}
	log_abc("%s", ss.str());
}

void
Machine::print_scope_stack()
{

	std::stringstream ss;
	ss << "ScopeStack: ";

    size_t totalSize = _scopeStack.totalSize();

    for (unsigned int i = 0; i < totalSize; ++i) {
		ss << as_value(_scopeStack.at(i)).toDebugString();
	}
	log_abc("%s", ss.str());
}	

void
Machine::get_args(size_t argc, fn_call::Args& args)
{
    std::vector<as_value> v(argc);
	for (size_t i = argc; i > 0; --i) {
		v.at(i-1) = pop_stack();
	}
    args.swap(v);
}

void
Machine::clearRegisters(std::uint32_t maxRegisters)
{
	_registers.clear();
	_registers.resize(maxRegisters);
}


} // namespace abc
} // namespace gnash
