1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
|
#!/bin/sh -e
# OOP in shell.
# LGPL copyright 2000 by Joey Hess <joey@kitenet.net>
# Adam Heath <doogie@debian.org>
_shoop () {
local TRUEOBJ=$1 TRYOBJ=$2 METH=$3 TRUEMETH=$1_$3 TRYMETH=$2_$3 LASTMETH=$METH
shift 3
case "$1" in
=|=q|=p|.=|.=q|.=p|.=qp|:|:p)
local varmeth=$1 append="" quiet="" private=""; shift
if [ "${varmeth%p}" != $varmeth ]; then private=1; varmeth=${varmeth%p}; fi
# This block is for introspect.
if [ "$_shoop_introspect" ] &&
eval [ -z \"\$_shooptype_$TRYMETH\$private\" ]; then
eval "_shoopdefines_$TRUEOBJ=\"\$_shoopdefines_$TRUEOBJ $METH\""
fi
if [ -z "$_shoopnocache_" ]; then
eval $_shoopcacheclear_
fi
# Some various assignment modifiers.
if [ "${varmeth#.}" != $varmeth ]; then append=1 varmeth=${varmeth#.}; fi
if [ "${varmeth%q}" != $varmeth ]; then quiet=1 varmeth=${varmeth%q}; fi
if [ "$varmeth" = = ]; then
if [ "$append" ];then set -- "$(eval eval "\$_shoop_$TRUEMETH") $@"; fi
if [ ! "$quiet" ]; then echo -n $@; fi
eval "_shoop_$TRUEMETH='echo -n $@'
_shooptype_$TRUEMETH=variable"
else
if [ "$quiet" ]; then echo "Invalid modifier(q) on assignment!($TRUEOBJ.$METH)" >&2; fi
if [ "$append" ];then
eval eval "_shoop_$TRUEMETH=\'\$_shoop_$TRUEMETH;\$@\'
_shooptype_$TRUEMETH=method"
else
eval "_shoop_$TRUEMETH='$@'
_shooptype_$TRUEMETH=method"
fi
fi
return
;;
esac
if eval [ \"\$_shooptype_$TRYMETH\" ]; then
local THIS=$TRUEOBJ
eval eval "\$_shoop_$TRYMETH"
return
else
eval local P PARENTS=\"$(eval eval "\$_shoop_${TRYOBJ}_parent")\"\
THIS=$TRUEOBJ GETMETH="" NEWPARENTS=""
if [ -z "$_shoopnocache_" ]; then
# If this object is found in the cache, than short-circuit
# the resolving code.
eval local CACHE=\"\$_shoopcache_link_$TRUEMETH\"
if [ "$CACHE" ]; then
eval eval \$$CACHE
return
fi
fi
# 1st stage resolver. Look at the immediate parents.
for P in $PARENTS; do
eval GETMETH=\"\$_shoop_${P}_$METH\"
if [ "$GETMETH" ]; then
eval "$GETMETH"
return
fi
# Save the parents of the current parents, for use in the
# 2nd stage resolver. Yes, this slows the 1st stage down,
# but barely. However, it greatly speeds up the 2nd stage,
# which is where most of the time will be spent. This
# gave an 8% speedup in the 2nd stage, and only noise in
# the first.
NEWPARENTS="$NEWPARENTS $(eval eval "\$_shoop_${P}_parent")"
done
# 1st stage found no match, so resolve the inheritance tree,
# starting at the second level, and loop over untested super
# classes.
local orgargs="$@"
set -- $NEWPARENTS
while [ $# -gt 0 ];do
P=$1
eval GETMETH="\$_shoop_${P}_$METH"
if [ "$GETMETH" ]; then
set -- $orgargs
# Save a reference to the resolved object in the cache for the
# true object.
if [ -z "$_shoopnocache_" ]; then
eval _shoopcache_link_${THIS}_$METH=_shoop_${P}_$METH\
_shoopcache_=\"\$_shoopcache_\
_shoopcache_method_$METH _shoopcache_link_${THIS}_$METH \"\
_shoopcache_method_$METH=\"\$_shoopcache_method_$METH\
_shoopcache_link_${THIS}_$METH\"\
_shoopcache_linkmethod_${P}_$METH=\"\$_shoopcache_linkmethod_${P}_$METH\
_shoopcache_link_${THIS}_$METH\"
fi
eval "$GETMETH"
return
fi
shift
set -- $(eval eval "\$_shoop_${P}_parent") "$@"
done
echo "\"$METH\" is undefined for $TRYOBJ." >&2
return 1
fi
}
# _shoopcache_link_DESCENDENT_counter=_shoop_OBJECT_counter
# _shoopcache_= _shoopcache_method_new _shoopcache_link_GRANDCHILD_new _shoopcache_method_counter _shoopcache_link_DESCENDENT_counter
# _shoopcache_method_counter= _shoopcache_link_DESCENDENT_counter
# _shoopcache_linkmethod_OBJECT_counter= _shoopcache_link_DESCENDENT_counter
IFS=" " _shoopcacheclear_="
if eval [ \\\"\\\$_shoopcache_method_\$METH\\\" ]; then
# Ok, the current METH is already in someone's cache.
# Find out if it is THIS object that is referenced.
if eval [ -z \\\"\\\$_shoopcache_linkmethod_\$TRUEMETH\\\" ]; then
# Someone is referencing \$METH, and it isn't TRUEMETH, so
# that means we have to erase all references for \$METH.
#
# TODO: Only erase if $TRUE was in the parent path of
# \$_shoopcache_method_\$METH
eval unset _shoopcache_method_\$METH\
\\\$_shoopcache_method_\$METH\
_shoopcache_linkmethod_\$TRUEMETH\
\\\$_shoopcache_linkmethod_\$TRUEMETH
fi
fi
"
# Temporarily turn on introspection, so the base object has everything
# recorded about it as it is being created.
_shoop_introspect=1
# Create a method to create a new object.
# We clear the whole cache, whenever a new object is created. This
# is sub-optimal, as it should really only dump cache chains that
# have traversed this object.
#
# The reason for this, is because we use lazy resolving. You can
# set your parents to non-existant objects, and define those objects
# at a later time. However, if the newer object contains a method
# that has already been resolved(and cached) by the first object,
# this will lead to a cache inconsistency.
IFS=" " _shoop OBJECT OBJECT new :p '
local OBJNAME=$1
eval "$OBJNAME () { shift; _shoop $OBJNAME $OBJNAME \"\$@\"; };"
if [ $THIS != $OBJNAME ]; then
_shoop $OBJNAME $OBJNAME parent = $THIS >/dev/null
fi
eval unset _shoopcache_ \$_shoopcache_ || true
'
# Create the base object via the method already defined on it.
_shoop OBJECT OBJECT new OBJECT
# Define the parent variable
OBJECT . parent = ""
# This method handles calling an overridden method of your parent.
OBJECT . super :p '_shoop $THIS $($THIS . parent) "$LASTMETH" "$@"; return'
# Now if you want introspection, you have to turn it back on.
unset _shoop_introspect
|