|
|
|
@ -480,4 +480,117 @@ concept with the wrapper implementation, we arrive at
|
|
|
|
|
lastline=97
|
|
|
|
|
]{lst/ctor-factory-priv.js}
|
|
|
|
|
|
|
|
|
|
(INCOMPLETE.)
|
|
|
|
|
In order to expose the private methods \emph{only} from within wrapped methods,
|
|
|
|
|
\jsref{lst:ctor-factory-priv} relies on the fact that only the constructor
|
|
|
|
|
factory knows the name of the private ``prototype'' object (as denoted by
|
|
|
|
|
\var{\_privname}). Wrappers, before invoking the wrapped function (method), will
|
|
|
|
|
assign the private object to \code{this.\_priv} (line 84) and unassign it after
|
|
|
|
|
the wrapped function returns (line 91).\footnote{We set the value to
|
|
|
|
|
\keyword{undefined} rather than using the \operator{delete} operator because the
|
|
|
|
|
latter causes a slight performance hit under v8.} Methods may then access
|
|
|
|
|
private members by referencing \code{this.\_priv}.
|
|
|
|
|
% TODO: REFERENCE NEEDED FOR ABOVE V8 MENTION
|
|
|
|
|
|
|
|
|
|
\func{copyTo()}, now receiving both public and private destination objects as
|
|
|
|
|
arguments, will place all members prefixed with an underscore on the private
|
|
|
|
|
member object (lines 71--72). As has already been mentioned, the member will be
|
|
|
|
|
wrapped regardless of whether or not it is private (line 74), ensuring that
|
|
|
|
|
public methods also have access to private members.
|
|
|
|
|
|
|
|
|
|
\jsref{lst:ctor-factory-priv-ex} demonstrates how this implementation may be
|
|
|
|
|
used to define a private method \func{\_getPrivValue()} that is accessable only
|
|
|
|
|
to other methods; attempting to invoke the method publically would result in a
|
|
|
|
|
\code{TypeError} (resulting from an attempt to call \keyword{undefined} as if it
|
|
|
|
|
were a function). Also note that \var{Foo.\_\_priv}, although defined from
|
|
|
|
|
within the method \func{getValue()}, is \keyword{undefined} after the method
|
|
|
|
|
returns.
|
|
|
|
|
|
|
|
|
|
\lstinputlisting[%
|
|
|
|
|
label=lst:ctor-factory-priv-ex,
|
|
|
|
|
caption=Demonstrating use of private methods with constructor factory,
|
|
|
|
|
firstline=107
|
|
|
|
|
]{lst/ctor-factory-priv.js}
|
|
|
|
|
|
|
|
|
|
\subsubsection{Wrapper Implementation Concerns}
|
|
|
|
|
The wrapper implementation is not without its dilemmas. Firstly, consider how
|
|
|
|
|
\func{wrap()} clears \var{\_\_priv} in \jsref{lst:ctor-factory-priv} (lines
|
|
|
|
|
84--91). The wrapper requires that the call stack be cleared up to the point of
|
|
|
|
|
the invocation of the wrapped function. Consequently, this means that any code
|
|
|
|
|
executed before the call stack is cleared to that point will have access to the
|
|
|
|
|
instance during which time \code{this.\_priv} is assigned, giving that code
|
|
|
|
|
access to private members and breaking encapsulation.
|
|
|
|
|
|
|
|
|
|
\lstinputlisting[%
|
|
|
|
|
label=lst:ctor-factory-priv-exploit-stack,
|
|
|
|
|
caption=Exploiting wrapper implementation via callbacks to gain access to
|
|
|
|
|
private members outside of the class
|
|
|
|
|
]{lst/ctor-factory-priv-exploit.js}
|
|
|
|
|
|
|
|
|
|
This fatal flaw is demonstrated in \jsref{lst:ctor-factory-priv-exploit-stack},
|
|
|
|
|
which executes a callback before the wrapped function returns. That callback,
|
|
|
|
|
which has access to the instance that called it, is able to access the private
|
|
|
|
|
members because it is executed before its caller returns. There are three ways
|
|
|
|
|
to work around this:
|
|
|
|
|
|
|
|
|
|
\begin{enumerate}
|
|
|
|
|
\item Remove the assignment before invoking the callback,
|
|
|
|
|
\item Use \func{setTimeout()} or \func{setInterval()} to invoke the callback,
|
|
|
|
|
allowing the stack to clear before the callback is invoked, or
|
|
|
|
|
\item Do not invoke any functions that are not defined on the class itself.
|
|
|
|
|
\end{enumerate}
|
|
|
|
|
|
|
|
|
|
None of the above options are acceptable solutions. The first option adds
|
|
|
|
|
unnecessary logic to the method that makes assumptions about the underlying
|
|
|
|
|
system, which is especially dangerous if the implementation of \func{wrap()}
|
|
|
|
|
were to ever change. The second solution does not suffer from the same design
|
|
|
|
|
issues as the first, but forces the method to be asynchronous, which is not
|
|
|
|
|
always desirable. The third option is terribly prohibitive, as it not only
|
|
|
|
|
disallows any type of serial callback, but also disallows invoking any methods
|
|
|
|
|
of injected dependencies.
|
|
|
|
|
|
|
|
|
|
% TODO: Reference to section where this issue is highlighted, once available
|
|
|
|
|
A proper solution to this issue is obvious, but its discussion will be deferred
|
|
|
|
|
to future implementations due to additional complexities raised when dealing
|
|
|
|
|
with properties. Until that time, the reader should be aware of the issue and
|
|
|
|
|
consider potential solutions.
|
|
|
|
|
|
|
|
|
|
The second concern is a bit more subtle. Once again, we focus around lines
|
|
|
|
|
82--91 in \jsref{lst:ctor-factory-priv}. Consider what problems that this
|
|
|
|
|
wrapper may cause when dealing with nested method calls --- that is, one method
|
|
|
|
|
calling another on the same instance.
|
|
|
|
|
|
|
|
|
|
\lstinputlisting[%
|
|
|
|
|
label=lst:ctor-factory-priv-nested-clear,
|
|
|
|
|
caption=Problems with nested method calls given the \func{wrap()}
|
|
|
|
|
implementation in \jsref{lst:ctor-factory-priv}
|
|
|
|
|
]{lst/ctor-factory-priv-nested-clear.js}
|
|
|
|
|
|
|
|
|
|
This issues is demonstrated by \jsref{lst:ctor-factory-priv-nested-clear}. The
|
|
|
|
|
\var{Database} class's \func{\_\_construct()} method performs two private method
|
|
|
|
|
calls --- \func{\_resolveHost()}, to get the IP address of the host, and
|
|
|
|
|
\func{\_connect()}, which attempts to connect to the database. Unfortunately,
|
|
|
|
|
after the call to \func{\_resolveHost()}, its wrapper sets \var{\_\_priv} to
|
|
|
|
|
\keyword{undefined} (line 91 in \jsref{lst:ctor-factory-priv}), which will cause
|
|
|
|
|
the second method call to fail!
|
|
|
|
|
|
|
|
|
|
To resolve this issue, \func{wrap()} could store the previous value of
|
|
|
|
|
\code{this.\_\_priv} and then, instead of setting the value to
|
|
|
|
|
\keyword{undefined} after the wrapped function has returned, restore
|
|
|
|
|
\code{this.\_\_priv} to its original value. This modification is shown in
|
|
|
|
|
\jsref{lst:ctor-factory-priv-nested-fix}.
|
|
|
|
|
|
|
|
|
|
\lstinputlisting[%
|
|
|
|
|
label=lst:ctor-factory-priv-nested-fix,
|
|
|
|
|
caption=Fixing private object assignment in nested wrapped function calls by
|
|
|
|
|
restoring the previous value,
|
|
|
|
|
firstnumber=80
|
|
|
|
|
]{lst/ctor-factory-priv-nested-fix.js}
|
|
|
|
|
|
|
|
|
|
When the first wrapper is invoked, the previous value of \code{this.\_\_priv}
|
|
|
|
|
will be \keyword{undefined}, allowing the wrapper to continue to operate as it
|
|
|
|
|
used to. When nested wrappers are invoked, the previous value will contain the
|
|
|
|
|
private member object and will allow \code{this.\_\_priv} to be properly
|
|
|
|
|
restored on return. This functions much like a stack, using the call stack
|
|
|
|
|
instead of an array.\footnote{This solution could have easily been worked into
|
|
|
|
|
\jsref{lst:ctor-factory-priv}, but hopefully the additional discussion provided
|
|
|
|
|
insight into critical design decisions.}
|
|
|
|
|