/* [<][>][^][v][top][bottom][index][help] */
//
// Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010,
// 2011 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
#ifndef GNASH_PROPERTY_H
#define GNASH_PROPERTY_H
#include <boost/variant.hpp>
#include <cassert>
#include <boost/bind.hpp>
#include <typeinfo>
#include "PropFlags.h"
#include "as_value.h"
#include "ObjectURI.h"
namespace gnash {
typedef as_value (*as_c_function_ptr)(const fn_call& fn);
class as_function;
}
namespace gnash {
/// Holder for getter/setter functions
//
/// Getter setter can be user-defined or native ones.
/// This class abstracts the two.
class GetterSetter
{
class NativeGetterSetter;
// The following helper structs define common operations on the
// Two types of GetterSetter. Some operations are applicable only to
// one type.
/// Get or set a GetterSetter
//
/// @tparam S A type that determines what operation to call on the
/// GetterSetter
/// @param Arg The type of the argument to the get or set function.
template<typename Arg, typename S>
struct GetSetVisitor : boost::static_visitor<typename S::result_type>
{
GetSetVisitor(const Arg& arg) : _arg(arg) {}
template<typename T> typename S::result_type operator()(T& t) const {
return S()(t, _arg);
}
private:
const Arg& _arg;
};
/// Set a GetterSetter
struct Set
{
typedef void result_type;
template<typename T, typename Arg>
result_type operator()(T& t, Arg& a) const {
t.set(a);
}
};
/// Get a GetterSetter
struct Get
{
typedef as_value result_type;
template<typename T, typename Arg>
result_type operator()(T& t, Arg& a) const {
return t.get(a);
}
};
/// Set the underlying value of a GetterSetter
//
/// This does not apply to NativeGetterSetters.
struct SetUnderlying : boost::static_visitor<>
{
template<typename T>
result_type operator()(T& gs, const as_value& val) const {
gs.setUnderlying(val);
}
result_type operator()(NativeGetterSetter&, const as_value&) const {}
};
/// Get the underlying value of a GetterSetter
//
/// This does not apply to NativeGetterSetters.
struct GetUnderlying : boost::static_visitor<as_value>
{
template<typename T>
result_type operator()(const T& gs) const {
return gs.getUnderlying();
}
result_type operator()(const NativeGetterSetter&) const {
return result_type();
}
};
/// Mark a GetterSetter reachable
struct MarkReachable : boost::static_visitor<>
{
template<typename T>
result_type operator()(const T& gs) const {
gs.markReachableResources();
}
};
public:
/// Construct a user-defined getter-setter
GetterSetter(as_function* getter, as_function* setter)
:
_getset(UserDefinedGetterSetter(getter, setter))
{}
/// Construct a native getter-setter
GetterSetter(as_c_function_ptr getter, as_c_function_ptr setter)
:
_getset(NativeGetterSetter(getter, setter))
{}
/// Invoke the getter
as_value get(fn_call& fn) const {
GetSetVisitor<const fn_call, Get> s(fn);
return boost::apply_visitor(s, _getset);
}
/// Invoke the setter
void set(const fn_call& fn) {
GetSetVisitor<fn_call, Set> s(fn);
boost::apply_visitor(s, _getset);
}
/// Set the cache value (for user-defined getter-setters)
void setCache(const as_value& v) {
boost::apply_visitor(boost::bind(SetUnderlying(), _1, v), _getset);
}
/// Get the cache value (for user-defined getter-setters)
as_value getCache() const {
return boost::apply_visitor(GetUnderlying(), _getset);
}
void markReachableResources() const {
boost::apply_visitor(MarkReachable(), _getset);
}
private:
/// User-defined getter/setter
class UserDefinedGetterSetter
{
public:
UserDefinedGetterSetter(as_function* get, as_function* set)
:
_getter(get),
_setter(set),
_underlyingValue(),
_beingAccessed(false)
{}
/// Invoke the getter
as_value get(const fn_call& fn) const;
/// Invoke the setter
void set(const fn_call& fn);
/// Get the underlying value
const as_value& getUnderlying() const { return _underlyingValue; }
/// Set the underlying value
void setUnderlying(const as_value& v) { _underlyingValue = v; }
void markReachableResources() const;
private:
/// For SWF6 (not higher) a user-defined getter-setter would not
/// be invoked while being set. This ScopedLock helps marking a
/// Getter-Setter as being invoked in an exception-safe manner.
//
/// Note this is not thread safe and does not attempt to provide
/// thread safety.
class ScopedLock : boost::noncopyable
{
public:
explicit ScopedLock(const UserDefinedGetterSetter& na)
:
_a(na),
_obtainedLock(_a._beingAccessed ? false : true)
{
// If we didn't obtain the lock it would be true anyway,
// but it's probably polite to avoid touching it.
if (_obtainedLock) _a._beingAccessed = true;
}
~ScopedLock() { if ( _obtainedLock) _a._beingAccessed = false; }
/// Return true if the lock was obtained
//
/// If false is returned, we're being called recursively,
/// which means we should set the underlyingValue instead
/// of calling the setter (for SWF6, again).
///
bool obtainedLock() const { return _obtainedLock; }
private:
const UserDefinedGetterSetter& _a;
bool _obtainedLock;
};
as_function* _getter;
as_function* _setter;
as_value _underlyingValue;
mutable bool _beingAccessed;
};
/// Native GetterSetter
class NativeGetterSetter
{
public:
NativeGetterSetter(as_c_function_ptr get, as_c_function_ptr set)
:
_getter(get), _setter(set) {}
/// Invoke the getter
as_value get(const fn_call& fn) const {
return _getter(fn);
}
/// Invoke the setter
void set(const fn_call& fn) {
_setter(fn);
}
/// Nothing to do for native setters.
void markReachableResources() const {}
private:
as_c_function_ptr _getter;
as_c_function_ptr _setter;
};
boost::variant<UserDefinedGetterSetter, NativeGetterSetter> _getset;
};
/// An abstract property
//
/// A Property is a holder for a value or a getter-setter.
//
/// Properties have special const semantics: the value of a Property does
/// not affect its outward state, so the value of a const Property can be
/// changed.
class Property
{
/// Mark the stored value reachable.
struct SetReachable : boost::static_visitor<>
{
result_type operator()(const as_value& val) const {
val.setReachable();
}
result_type operator()(const GetterSetter& gs) const {
return gs.markReachableResources();
}
};
public:
Property(const ObjectURI& uri, const as_value& value,
const PropFlags& flags)
:
_bound(value),
_uri(uri),
_flags(flags),
_destructive(false)
{}
Property(const ObjectURI& uri,
as_function* getter, as_function* setter,
const PropFlags& flags, bool destroy = false)
:
_bound(GetterSetter(getter, setter)),
_uri(uri),
_flags(flags),
_destructive(destroy)
{}
Property(const ObjectURI& uri, as_c_function_ptr getter,
as_c_function_ptr setter, const PropFlags& flags,
bool destroy = false)
:
_bound(GetterSetter(getter, setter)),
_uri(uri),
_flags(flags),
_destructive(destroy)
{}
/// Copy constructor
Property(const Property& p)
:
_bound(p._bound),
_uri(p._uri),
_flags(p._flags),
_destructive(p._destructive)
{}
/// accessor to the properties flags
const PropFlags& getFlags() const { return _flags; }
/// Set the flags of the property
void setFlags(const PropFlags& flags) const {
_flags = flags;
}
/// Get value of this property
//
/// @param this_ptr
/// The as_object used to set the 'this' pointer.
/// for calling getter function (GetterSetterProperty);
/// it will be unused when getting or setting SimpleProperty
/// properties.
/// @return the value of this property
///
as_value getValue(const as_object& this_ptr) const;
/// Get internal cached value of this property
//
/// For simple properties, this is the usual value;
/// for user-defined getter-setter this is a cached value
/// to watch for infinitely recurse on calling the getter
/// or setter; Native getter-setter has no cache,
/// undefined will be returned for them.
as_value getCache() const;
/// Set internal cached value of this property
//
/// For simple properties, this is the usual value;
/// for user-defined getter-setter this is a cached value
/// to watch for infinitely recurse on calling the getter
/// or setter; Native getter-setter has no cache,
/// nothing would happen for them.
void setCache(const as_value& v);
/// Set value of this property
//
/// @param this_ptr The as_object used to set the 'this' pointer
/// for calling getter/setter function
/// It will be unused when getting or setting
/// simple properties.
/// This parameter is non-const as nothing prevents an
/// eventual "Setter" function from actually modifying it,
/// so we can't promise constness.
/// @param value The new value for this property. It will be used as first
/// argument of the 'setter' function if this is a Getter/Setter
/// property. @see isGetterSetter().
/// @return true if the property was set, otherwise false. Read-only
/// properties can generally not be set, but destructive
/// properties are still allowed to be replaced, as this
/// should be invisible to the user.
bool setValue(as_object& this_ptr, const as_value &value) const;
/// Is this a getter/setter property?
bool isGetterSetter() const {
return _bound.type() == typeid(GetterSetter);
}
/// Clear visibility flags
void clearVisible(int swfVersion) { _flags.clear_visible(swfVersion); }
/// The name-namespace pair (ObjectURI) of this Property
const ObjectURI& uri() const {
return _uri;
}
/// Mark this property as being reachable (for the GC)
void setReachable() const {
return boost::apply_visitor(SetReachable(), _bound);
}
private:
// Store the various types of things that can be held.
typedef boost::variant<as_value, GetterSetter> BoundType;
/// The value of the property.
mutable BoundType _bound;
/// The property identifier (name).
ObjectURI _uri;
/// Properties flags
mutable PropFlags _flags;
// If true, as soon as getValue has been invoked once, the
// returned value becomes a fixed return (though it can be
// overwritten if not readOnly)
mutable bool _destructive;
};
/// is this a read-only member ?
inline bool
readOnly(const Property& prop) {
return prop.getFlags().test<PropFlags::readOnly>();
}
/// Is this member supposed to be visible by a VM of given version ?
inline bool
visible(const Property& prop, int version) {
return prop.getFlags().get_visible(version);
}
} // namespace gnash
#endif // GNASH_PROPERTY_H