From 28e882951168dcd7fb483bd144adc52f1deec90a Mon Sep 17 00:00:00 2001 From: Mike Gerwitz Date: Mon, 5 Mar 2012 23:20:29 -0500 Subject: [PATCH] Near-complete private member implementation section --- coope.bib | 25 +- coope.sty | 3 + data/func-wrap-blogic.dat | 3 + data/func-wrap-blogic.tex | 27 ++ data/func-wrap-invoke.dat | 3 + data/func-wrap-invoke.tex | 33 ++ data/stack-limits.dat | 8 + data/stack-limits.eps | 639 ++++++++++++++++++++++++++++++++++++++ data/stack-limits.tex | 24 ++ lst/ctor-factory-priv.js | 123 ++++++++ lst/ctor-factory.js | 2 +- lst/func-wrap.js | 33 ++ sec/encap-hacks.tex | 204 +++++++++++- sec/licenses.tex | 11 + 14 files changed, 1128 insertions(+), 10 deletions(-) create mode 100644 data/func-wrap-blogic.dat create mode 100644 data/func-wrap-blogic.tex create mode 100644 data/func-wrap-invoke.dat create mode 100644 data/func-wrap-invoke.tex create mode 100644 data/stack-limits.dat create mode 100644 data/stack-limits.eps create mode 100644 data/stack-limits.tex create mode 100644 lst/ctor-factory-priv.js create mode 100644 lst/func-wrap.js diff --git a/coope.bib b/coope.bib index e907e38..d17fe35 100644 --- a/coope.bib +++ b/coope.bib @@ -1,8 +1,31 @@ @manual{es5-call, organization = "ECMA International", - title = "ECMA-262", + title = "ECMA-262", edition = "5.1", year = "2011", month = "Jun", note = "Section 10.4.3" } + +@misc{jsperf-func-wrap, + author = {Mike Gerwitz}, + title = {Function Wrapper (Invocation)}, + note = {Accessed: 25 Feb 2012}, + howpublished = {\url{http://jsperf.com/coope-function-wrapper}}, +} + +@misc{jsperf-func-wrap-blogic, + author = {Mike Gerwitz}, + title = {Function Wrapper (w/ Business Logic)}, + note = {Accessed: 25 Feb 2012}, + howpublished = {\url{http://jsperf.com/coope-function-wrapper-w-blogic}}, +} + +@book{oreilly-hpj, + author = {Nicholas C. Zakas}, + title = {High Performance JavaScript}, + publisher = {O'Reilly Media, Inc}, + year = {2010}, + note = {Reference for some of the call stack limits mentioned in the + article \citenf} +} diff --git a/coope.sty b/coope.sty index fe4620f..a740d10 100644 --- a/coope.sty +++ b/coope.sty @@ -8,6 +8,9 @@ \usepackage{hyperref} \usepackage{cite} \usepackage{multirow} +\usepackage{subfig} +\usepackage{gnuplottex} +\usepackage{epstopdf} \hypersetup{colorlinks, citecolor=black, diff --git a/data/func-wrap-blogic.dat b/data/func-wrap-blogic.dat new file mode 100644 index 0000000..446ab70 --- /dev/null +++ b/data/func-wrap-blogic.dat @@ -0,0 +1,3 @@ +Browser,No Wrapper,Factory,% Change +"Chromium 14",52342,52342,0.00 +"Firefox 8",65947,65947,0.00 diff --git a/data/func-wrap-blogic.tex b/data/func-wrap-blogic.tex new file mode 100644 index 0000000..2bd0131 --- /dev/null +++ b/data/func-wrap-blogic.tex @@ -0,0 +1,27 @@ +\begin{gnuplot}[terminal=epslatex] + set size 0.55,0.7 + set auto x + set yrange [0:*] + set ylabel "Ops/s" + set key above horizontal autotitle columnheader + + # styling + set decimal locale + set format "%'g" + set border 3 front + set style data histogram + set style fill solid border -1 + set tics scale 0.0 + set grid y + set xtics offset 1 + + # to align properly with LaTeX subfloat caption and not stretch the page + set lmargin 4 + set rmargin 0 + + # load data from csv file + set datafile separator "," + plot "data/func-wrap-blogic.dat" u 2:xtic(1) lc rgb "gray", \ + '' u 3 lc rgb "#eeeeec", \ + '' using ($0+0.125):($2+3000):(sprintf("%'g",$2)) with labels notitle +\end{gnuplot} diff --git a/data/func-wrap-invoke.dat b/data/func-wrap-invoke.dat new file mode 100644 index 0000000..9743f78 --- /dev/null +++ b/data/func-wrap-invoke.dat @@ -0,0 +1,3 @@ +Browser,No Wrapper,Closure,Factory,% Change +"Chromium 14",734,70,64,1135 +"Firefox 8",381,2,2,18864 diff --git a/data/func-wrap-invoke.tex b/data/func-wrap-invoke.tex new file mode 100644 index 0000000..59e63e3 --- /dev/null +++ b/data/func-wrap-invoke.tex @@ -0,0 +1,33 @@ +% Funny thing, this. Must be placed within a separate file; pasting this inline +% causes errors. +\begin{gnuplot}[terminal=epslatex] + set size 0.55,0.7 + set auto x + set yrange [0:*] + set ylabel "Ops/s (millions)" + set key above horizontal autotitle columnheader + + # styling + set decimal locale + set format "%'g" + set border 3 front + set style data histogram + set style fill solid border -1 + set tics scale 0.0 + set grid y + set xtics offset 1 + + # to align properly with LaTeX subfloat caption and not stretch the page + set lmargin 1 + set rmargin 0 + + # load data from csv file + set datafile separator "," + plot "data/func-wrap-invoke.dat" using 2:xtic(1) lc rgb "gray", \ + '' u 4 lc rgb "#eeeeec", \ + '' using ($0):($2+30):($2) with labels notitle, \ + '' using ($0+0.25):($4+30):($4) with labels notitle, \ + '' using ($0+0.45):($4+230):\ + (sprintf('\\footnotesize'."(-%'g".'\\%)',$5)) \ + with labels notitle rotate by 45 +\end{gnuplot} diff --git a/data/stack-limits.dat b/data/stack-limits.dat new file mode 100644 index 0000000..f0bfcc0 --- /dev/null +++ b/data/stack-limits.dat @@ -0,0 +1,8 @@ +"Chromium 1",21837 +"Chromium 14",26139 +"Firefox 3",3000 +"Firefox 8",39051 +"Internet Explorer 7",1789 +"Internet Explorer 8",2232 +"Safari 3.2",500 +"Safari 4",37448 diff --git a/data/stack-limits.eps b/data/stack-limits.eps new file mode 100644 index 0000000..2e94c6c --- /dev/null +++ b/data/stack-limits.eps @@ -0,0 +1,639 @@ +%!PS-Adobe-2.0 EPSF-2.0 +%%Title: stack-limits.tex +%%Creator: gnuplot 4.4 patchlevel 2 +%%CreationDate: Sun Feb 26 16:39:09 2012 +%%DocumentFonts: +%%BoundingBox: 50 50 410 302 +%%EndComments +%%BeginProlog +/gnudict 256 dict def +gnudict begin +% +% The following true/false flags may be edited by hand if desired. +% The unit line width and grayscale image gamma correction may also be changed. +% +/Color false def +/Blacktext true def +/Solid false def +/Dashlength 1 def +/Landscape false def +/Level1 false def +/Rounded false def +/ClipToBoundingBox false def +/TransparentPatterns false def +/gnulinewidth 5.000 def +/userlinewidth gnulinewidth def +/Gamma 1.0 def +% +/vshift -73 def +/dl1 { + 10.0 Dashlength mul mul + Rounded { currentlinewidth 0.75 mul sub dup 0 le { pop 0.01 } if } if +} def +/dl2 { + 10.0 Dashlength mul mul + Rounded { currentlinewidth 0.75 mul add } if +} def +/hpt_ 31.5 def +/vpt_ 31.5 def +/hpt hpt_ def +/vpt vpt_ def +Level1 {} { +/SDict 10 dict def +systemdict /pdfmark known not { + userdict /pdfmark systemdict /cleartomark get put +} if +SDict begin [ + /Title (stack-limits.tex) + /Subject (gnuplot plot) + /Creator (gnuplot 4.4 patchlevel 2) + /Author (mikegerwitz) +% /Producer (gnuplot) +% /Keywords () + /CreationDate (Sun Feb 26 16:39:09 2012) + /DOCINFO pdfmark +end +} ifelse +/doclip { + ClipToBoundingBox { + newpath 50 50 moveto 410 50 lineto 410 302 lineto 50 302 lineto closepath + clip + } if +} def +% +% Gnuplot Prolog Version 4.4 (August 2010) +% +%/SuppressPDFMark true def +% +/M {moveto} bind def +/L {lineto} bind def +/R {rmoveto} bind def +/V {rlineto} bind def +/N {newpath moveto} bind def +/Z {closepath} bind def +/C {setrgbcolor} bind def +/f {rlineto fill} bind def +/g {setgray} bind def +/Gshow {show} def % May be redefined later in the file to support UTF-8 +/vpt2 vpt 2 mul def +/hpt2 hpt 2 mul def +/Lshow {currentpoint stroke M 0 vshift R + Blacktext {gsave 0 setgray show grestore} {show} ifelse} def +/Rshow {currentpoint stroke M dup stringwidth pop neg vshift R + Blacktext {gsave 0 setgray show grestore} {show} ifelse} def +/Cshow {currentpoint stroke M dup stringwidth pop -2 div vshift R + Blacktext {gsave 0 setgray show grestore} {show} ifelse} def +/UP {dup vpt_ mul /vpt exch def hpt_ mul /hpt exch def + /hpt2 hpt 2 mul def /vpt2 vpt 2 mul def} def +/DL {Color {setrgbcolor Solid {pop []} if 0 setdash} + {pop pop pop 0 setgray Solid {pop []} if 0 setdash} ifelse} def +/BL {stroke userlinewidth 2 mul setlinewidth + Rounded {1 setlinejoin 1 setlinecap} if} def +/AL {stroke userlinewidth 2 div setlinewidth + Rounded {1 setlinejoin 1 setlinecap} if} def +/UL {dup gnulinewidth mul /userlinewidth exch def + dup 1 lt {pop 1} if 10 mul /udl exch def} def +/PL {stroke userlinewidth setlinewidth + Rounded {1 setlinejoin 1 setlinecap} if} def +3.8 setmiterlimit +% Default Line colors +/LCw {1 1 1} def +/LCb {0 0 0} def +/LCa {0 0 0} def +/LC0 {1 0 0} def +/LC1 {0 1 0} def +/LC2 {0 0 1} def +/LC3 {1 0 1} def +/LC4 {0 1 1} def +/LC5 {1 1 0} def +/LC6 {0 0 0} def +/LC7 {1 0.3 0} def +/LC8 {0.5 0.5 0.5} def +% Default Line Types +/LTw {PL [] 1 setgray} def +/LTb {BL [] LCb DL} def +/LTa {AL [1 udl mul 2 udl mul] 0 setdash LCa setrgbcolor} def +/LT0 {PL [] LC0 DL} def +/LT1 {PL [4 dl1 2 dl2] LC1 DL} def +/LT2 {PL [2 dl1 3 dl2] LC2 DL} def +/LT3 {PL [1 dl1 1.5 dl2] LC3 DL} def +/LT4 {PL [6 dl1 2 dl2 1 dl1 2 dl2] LC4 DL} def +/LT5 {PL [3 dl1 3 dl2 1 dl1 3 dl2] LC5 DL} def +/LT6 {PL [2 dl1 2 dl2 2 dl1 6 dl2] LC6 DL} def +/LT7 {PL [1 dl1 2 dl2 6 dl1 2 dl2 1 dl1 2 dl2] LC7 DL} def +/LT8 {PL [2 dl1 2 dl2 2 dl1 2 dl2 2 dl1 2 dl2 2 dl1 4 dl2] LC8 DL} def +/Pnt {stroke [] 0 setdash gsave 1 setlinecap M 0 0 V stroke grestore} def +/Dia {stroke [] 0 setdash 2 copy vpt add M + hpt neg vpt neg V hpt vpt neg V + hpt vpt V hpt neg vpt V closepath stroke + Pnt} def +/Pls {stroke [] 0 setdash vpt sub M 0 vpt2 V + currentpoint stroke M + hpt neg vpt neg R hpt2 0 V stroke + } def +/Box {stroke [] 0 setdash 2 copy exch hpt sub exch vpt add M + 0 vpt2 neg V hpt2 0 V 0 vpt2 V + hpt2 neg 0 V closepath stroke + Pnt} def +/Crs {stroke [] 0 setdash exch hpt sub exch vpt add M + hpt2 vpt2 neg V currentpoint stroke M + hpt2 neg 0 R hpt2 vpt2 V stroke} def +/TriU {stroke [] 0 setdash 2 copy vpt 1.12 mul add M + hpt neg vpt -1.62 mul V + hpt 2 mul 0 V + hpt neg vpt 1.62 mul V closepath stroke + Pnt} def +/Star {2 copy Pls Crs} def +/BoxF {stroke [] 0 setdash exch hpt sub exch vpt add M + 0 vpt2 neg V hpt2 0 V 0 vpt2 V + hpt2 neg 0 V closepath fill} def +/TriUF {stroke [] 0 setdash vpt 1.12 mul add M + hpt neg vpt -1.62 mul V + hpt 2 mul 0 V + hpt neg vpt 1.62 mul V closepath fill} def +/TriD {stroke [] 0 setdash 2 copy vpt 1.12 mul sub M + hpt neg vpt 1.62 mul V + hpt 2 mul 0 V + hpt neg vpt -1.62 mul V closepath stroke + Pnt} def +/TriDF {stroke [] 0 setdash vpt 1.12 mul sub M + hpt neg vpt 1.62 mul V + hpt 2 mul 0 V + hpt neg vpt -1.62 mul V closepath fill} def +/DiaF {stroke [] 0 setdash vpt add M + hpt neg vpt neg V hpt vpt neg V + hpt vpt V hpt neg vpt V closepath fill} def +/Pent {stroke [] 0 setdash 2 copy gsave + translate 0 hpt M 4 {72 rotate 0 hpt L} repeat + closepath stroke grestore Pnt} def +/PentF {stroke [] 0 setdash gsave + translate 0 hpt M 4 {72 rotate 0 hpt L} repeat + closepath fill grestore} def +/Circle {stroke [] 0 setdash 2 copy + hpt 0 360 arc stroke Pnt} def +/CircleF {stroke [] 0 setdash hpt 0 360 arc fill} def +/C0 {BL [] 0 setdash 2 copy moveto vpt 90 450 arc} bind def +/C1 {BL [] 0 setdash 2 copy moveto + 2 copy vpt 0 90 arc closepath fill + vpt 0 360 arc closepath} bind def +/C2 {BL [] 0 setdash 2 copy moveto + 2 copy vpt 90 180 arc closepath fill + vpt 0 360 arc closepath} bind def +/C3 {BL [] 0 setdash 2 copy moveto + 2 copy vpt 0 180 arc closepath fill + vpt 0 360 arc closepath} bind def +/C4 {BL [] 0 setdash 2 copy moveto + 2 copy vpt 180 270 arc closepath fill + vpt 0 360 arc closepath} bind def +/C5 {BL [] 0 setdash 2 copy moveto + 2 copy vpt 0 90 arc + 2 copy moveto + 2 copy vpt 180 270 arc closepath fill + vpt 0 360 arc} bind def +/C6 {BL [] 0 setdash 2 copy moveto + 2 copy vpt 90 270 arc closepath fill + vpt 0 360 arc closepath} bind def +/C7 {BL [] 0 setdash 2 copy moveto + 2 copy vpt 0 270 arc closepath fill + vpt 0 360 arc closepath} bind def +/C8 {BL [] 0 setdash 2 copy moveto + 2 copy vpt 270 360 arc closepath fill + vpt 0 360 arc closepath} bind def +/C9 {BL [] 0 setdash 2 copy moveto + 2 copy vpt 270 450 arc closepath fill + vpt 0 360 arc closepath} bind def +/C10 {BL [] 0 setdash 2 copy 2 copy moveto vpt 270 360 arc closepath fill + 2 copy moveto + 2 copy vpt 90 180 arc closepath fill + vpt 0 360 arc closepath} bind def +/C11 {BL [] 0 setdash 2 copy moveto + 2 copy vpt 0 180 arc closepath fill + 2 copy moveto + 2 copy vpt 270 360 arc closepath fill + vpt 0 360 arc closepath} bind def +/C12 {BL [] 0 setdash 2 copy moveto + 2 copy vpt 180 360 arc closepath fill + vpt 0 360 arc closepath} bind def +/C13 {BL [] 0 setdash 2 copy moveto + 2 copy vpt 0 90 arc closepath fill + 2 copy moveto + 2 copy vpt 180 360 arc closepath fill + vpt 0 360 arc closepath} bind def +/C14 {BL [] 0 setdash 2 copy moveto + 2 copy vpt 90 360 arc closepath fill + vpt 0 360 arc} bind def +/C15 {BL [] 0 setdash 2 copy vpt 0 360 arc closepath fill + vpt 0 360 arc closepath} bind def +/Rec {newpath 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto + neg 0 rlineto closepath} bind def +/Square {dup Rec} bind def +/Bsquare {vpt sub exch vpt sub exch vpt2 Square} bind def +/S0 {BL [] 0 setdash 2 copy moveto 0 vpt rlineto BL Bsquare} bind def +/S1 {BL [] 0 setdash 2 copy vpt Square fill Bsquare} bind def +/S2 {BL [] 0 setdash 2 copy exch vpt sub exch vpt Square fill Bsquare} bind def +/S3 {BL [] 0 setdash 2 copy exch vpt sub exch vpt2 vpt Rec fill Bsquare} bind def +/S4 {BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt Square fill Bsquare} bind def +/S5 {BL [] 0 setdash 2 copy 2 copy vpt Square fill + exch vpt sub exch vpt sub vpt Square fill Bsquare} bind def +/S6 {BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt vpt2 Rec fill Bsquare} bind def +/S7 {BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt vpt2 Rec fill + 2 copy vpt Square fill Bsquare} bind def +/S8 {BL [] 0 setdash 2 copy vpt sub vpt Square fill Bsquare} bind def +/S9 {BL [] 0 setdash 2 copy vpt sub vpt vpt2 Rec fill Bsquare} bind def +/S10 {BL [] 0 setdash 2 copy vpt sub vpt Square fill 2 copy exch vpt sub exch vpt Square fill + Bsquare} bind def +/S11 {BL [] 0 setdash 2 copy vpt sub vpt Square fill 2 copy exch vpt sub exch vpt2 vpt Rec fill + Bsquare} bind def +/S12 {BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt2 vpt Rec fill Bsquare} bind def +/S13 {BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt2 vpt Rec fill + 2 copy vpt Square fill Bsquare} bind def +/S14 {BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt2 vpt Rec fill + 2 copy exch vpt sub exch vpt Square fill Bsquare} bind def +/S15 {BL [] 0 setdash 2 copy Bsquare fill Bsquare} bind def +/D0 {gsave translate 45 rotate 0 0 S0 stroke grestore} bind def +/D1 {gsave translate 45 rotate 0 0 S1 stroke grestore} bind def +/D2 {gsave translate 45 rotate 0 0 S2 stroke grestore} bind def +/D3 {gsave translate 45 rotate 0 0 S3 stroke grestore} bind def +/D4 {gsave translate 45 rotate 0 0 S4 stroke grestore} bind def +/D5 {gsave translate 45 rotate 0 0 S5 stroke grestore} bind def +/D6 {gsave translate 45 rotate 0 0 S6 stroke grestore} bind def +/D7 {gsave translate 45 rotate 0 0 S7 stroke grestore} bind def +/D8 {gsave translate 45 rotate 0 0 S8 stroke grestore} bind def +/D9 {gsave translate 45 rotate 0 0 S9 stroke grestore} bind def +/D10 {gsave translate 45 rotate 0 0 S10 stroke grestore} bind def +/D11 {gsave translate 45 rotate 0 0 S11 stroke grestore} bind def +/D12 {gsave translate 45 rotate 0 0 S12 stroke grestore} bind def +/D13 {gsave translate 45 rotate 0 0 S13 stroke grestore} bind def +/D14 {gsave translate 45 rotate 0 0 S14 stroke grestore} bind def +/D15 {gsave translate 45 rotate 0 0 S15 stroke grestore} bind def +/DiaE {stroke [] 0 setdash vpt add M + hpt neg vpt neg V hpt vpt neg V + hpt vpt V hpt neg vpt V closepath stroke} def +/BoxE {stroke [] 0 setdash exch hpt sub exch vpt add M + 0 vpt2 neg V hpt2 0 V 0 vpt2 V + hpt2 neg 0 V closepath stroke} def +/TriUE {stroke [] 0 setdash vpt 1.12 mul add M + hpt neg vpt -1.62 mul V + hpt 2 mul 0 V + hpt neg vpt 1.62 mul V closepath stroke} def +/TriDE {stroke [] 0 setdash vpt 1.12 mul sub M + hpt neg vpt 1.62 mul V + hpt 2 mul 0 V + hpt neg vpt -1.62 mul V closepath stroke} def +/PentE {stroke [] 0 setdash gsave + translate 0 hpt M 4 {72 rotate 0 hpt L} repeat + closepath stroke grestore} def +/CircE {stroke [] 0 setdash + hpt 0 360 arc stroke} def +/Opaque {gsave closepath 1 setgray fill grestore 0 setgray closepath} def +/DiaW {stroke [] 0 setdash vpt add M + hpt neg vpt neg V hpt vpt neg V + hpt vpt V hpt neg vpt V Opaque stroke} def +/BoxW {stroke [] 0 setdash exch hpt sub exch vpt add M + 0 vpt2 neg V hpt2 0 V 0 vpt2 V + hpt2 neg 0 V Opaque stroke} def +/TriUW {stroke [] 0 setdash vpt 1.12 mul add M + hpt neg vpt -1.62 mul V + hpt 2 mul 0 V + hpt neg vpt 1.62 mul V Opaque stroke} def +/TriDW {stroke [] 0 setdash vpt 1.12 mul sub M + hpt neg vpt 1.62 mul V + hpt 2 mul 0 V + hpt neg vpt -1.62 mul V Opaque stroke} def +/PentW {stroke [] 0 setdash gsave + translate 0 hpt M 4 {72 rotate 0 hpt L} repeat + Opaque stroke grestore} def +/CircW {stroke [] 0 setdash + hpt 0 360 arc Opaque stroke} def +/BoxFill {gsave Rec 1 setgray fill grestore} def +/Density { + /Fillden exch def + currentrgbcolor + /ColB exch def /ColG exch def /ColR exch def + /ColR ColR Fillden mul Fillden sub 1 add def + /ColG ColG Fillden mul Fillden sub 1 add def + /ColB ColB Fillden mul Fillden sub 1 add def + ColR ColG ColB setrgbcolor} def +/BoxColFill {gsave Rec PolyFill} def +/PolyFill {gsave Density fill grestore grestore} def +/h {rlineto rlineto rlineto gsave closepath fill grestore} bind def +% +% PostScript Level 1 Pattern Fill routine for rectangles +% Usage: x y w h s a XX PatternFill +% x,y = lower left corner of box to be filled +% w,h = width and height of box +% a = angle in degrees between lines and x-axis +% XX = 0/1 for no/yes cross-hatch +% +/PatternFill {gsave /PFa [ 9 2 roll ] def + PFa 0 get PFa 2 get 2 div add PFa 1 get PFa 3 get 2 div add translate + PFa 2 get -2 div PFa 3 get -2 div PFa 2 get PFa 3 get Rec + gsave 1 setgray fill grestore clip + currentlinewidth 0.5 mul setlinewidth + /PFs PFa 2 get dup mul PFa 3 get dup mul add sqrt def + 0 0 M PFa 5 get rotate PFs -2 div dup translate + 0 1 PFs PFa 4 get div 1 add floor cvi + {PFa 4 get mul 0 M 0 PFs V} for + 0 PFa 6 get ne { + 0 1 PFs PFa 4 get div 1 add floor cvi + {PFa 4 get mul 0 2 1 roll M PFs 0 V} for + } if + stroke grestore} def +% +/languagelevel where + {pop languagelevel} {1} ifelse + 2 lt + {/InterpretLevel1 true def} + {/InterpretLevel1 Level1 def} + ifelse +% +% PostScript level 2 pattern fill definitions +% +/Level2PatternFill { +/Tile8x8 {/PaintType 2 /PatternType 1 /TilingType 1 /BBox [0 0 8 8] /XStep 8 /YStep 8} + bind def +/KeepColor {currentrgbcolor [/Pattern /DeviceRGB] setcolorspace} bind def +<< Tile8x8 + /PaintProc {0.5 setlinewidth pop 0 0 M 8 8 L 0 8 M 8 0 L stroke} +>> matrix makepattern +/Pat1 exch def +<< Tile8x8 + /PaintProc {0.5 setlinewidth pop 0 0 M 8 8 L 0 8 M 8 0 L stroke + 0 4 M 4 8 L 8 4 L 4 0 L 0 4 L stroke} +>> matrix makepattern +/Pat2 exch def +<< Tile8x8 + /PaintProc {0.5 setlinewidth pop 0 0 M 0 8 L + 8 8 L 8 0 L 0 0 L fill} +>> matrix makepattern +/Pat3 exch def +<< Tile8x8 + /PaintProc {0.5 setlinewidth pop -4 8 M 8 -4 L + 0 12 M 12 0 L stroke} +>> matrix makepattern +/Pat4 exch def +<< Tile8x8 + /PaintProc {0.5 setlinewidth pop -4 0 M 8 12 L + 0 -4 M 12 8 L stroke} +>> matrix makepattern +/Pat5 exch def +<< Tile8x8 + /PaintProc {0.5 setlinewidth pop -2 8 M 4 -4 L + 0 12 M 8 -4 L 4 12 M 10 0 L stroke} +>> matrix makepattern +/Pat6 exch def +<< Tile8x8 + /PaintProc {0.5 setlinewidth pop -2 0 M 4 12 L + 0 -4 M 8 12 L 4 -4 M 10 8 L stroke} +>> matrix makepattern +/Pat7 exch def +<< Tile8x8 + /PaintProc {0.5 setlinewidth pop 8 -2 M -4 4 L + 12 0 M -4 8 L 12 4 M 0 10 L stroke} +>> matrix makepattern +/Pat8 exch def +<< Tile8x8 + /PaintProc {0.5 setlinewidth pop 0 -2 M 12 4 L + -4 0 M 12 8 L -4 4 M 8 10 L stroke} +>> matrix makepattern +/Pat9 exch def +/Pattern1 {PatternBgnd KeepColor Pat1 setpattern} bind def +/Pattern2 {PatternBgnd KeepColor Pat2 setpattern} bind def +/Pattern3 {PatternBgnd KeepColor Pat3 setpattern} bind def +/Pattern4 {PatternBgnd KeepColor Landscape {Pat5} {Pat4} ifelse setpattern} bind def +/Pattern5 {PatternBgnd KeepColor Landscape {Pat4} {Pat5} ifelse setpattern} bind def +/Pattern6 {PatternBgnd KeepColor Landscape {Pat9} {Pat6} ifelse setpattern} bind def +/Pattern7 {PatternBgnd KeepColor Landscape {Pat8} {Pat7} ifelse setpattern} bind def +} def +% +% +%End of PostScript Level 2 code +% +/PatternBgnd { + TransparentPatterns {} {gsave 1 setgray fill grestore} ifelse +} def +% +% Substitute for Level 2 pattern fill codes with +% grayscale if Level 2 support is not selected. +% +/Level1PatternFill { +/Pattern1 {0.250 Density} bind def +/Pattern2 {0.500 Density} bind def +/Pattern3 {0.750 Density} bind def +/Pattern4 {0.125 Density} bind def +/Pattern5 {0.375 Density} bind def +/Pattern6 {0.625 Density} bind def +/Pattern7 {0.875 Density} bind def +} def +% +% Now test for support of Level 2 code +% +Level1 {Level1PatternFill} {Level2PatternFill} ifelse +% +/Symbol-Oblique /Symbol findfont [1 0 .167 1 0 0] makefont +dup length dict begin {1 index /FID eq {pop pop} {def} ifelse} forall +currentdict end definefont pop +end +%%EndProlog +gnudict begin +gsave +doclip +50 50 translate +0.050 0.050 scale +0 setgray +newpath +1.000 UL +LTb +1.000 UL +LTa +1342 1993 M +5461 0 V +stroke +LTb +1342 1993 M +5461 0 R +stroke +LTa +1342 2258 M +5461 0 V +stroke +LTb +1342 2258 M +5461 0 R +stroke +LTa +1342 2523 M +5461 0 V +stroke +LTb +1342 2523 M +5461 0 R +stroke +LTa +1342 2788 M +5461 0 V +stroke +LTb +1342 2788 M +5461 0 R +stroke +LTa +1342 3053 M +5461 0 V +stroke +LTb +1342 3053 M +5461 0 R +stroke +LTa +1342 3319 M +5461 0 V +stroke +LTb +1342 3319 M +5461 0 R +stroke +LTa +1342 3584 M +5461 0 V +stroke +LTb +1342 3584 M +5461 0 R +stroke +LTa +1342 3849 M +5461 0 V +stroke +LTb +1342 3849 M +5461 0 R +stroke +LTa +1342 4114 M +5461 0 V +stroke +LTb +1342 4114 M +5461 0 R +stroke +LTa +1342 4379 M +5461 0 V +stroke +LTb +1342 4379 M +5461 0 R +1949 1993 M +0 2386 R +2556 1993 M +0 2386 R +3162 1993 M +0 2386 R +3769 1993 M +0 2386 R +4376 1993 M +0 2386 R +4983 1993 M +0 2386 R +5589 1993 M +0 2386 R +6196 1993 M +0 2386 R +-4854 0 R +0 -2386 V +5461 0 V +0 2386 R +-5461 0 R +1.000 UP +stroke +LT0 +0.75 0.75 0.75 C 1.000 1949 1993 203 1159 BoxColFill +LTb +1949 1993 N +0 1158 V +202 0 V +0 -1158 V +-202 0 V +Z stroke +LT0 +0.75 0.75 0.75 C 1.000 2556 1993 203 1387 BoxColFill +LTb +2556 1993 N +0 1386 V +202 0 V +0 -1386 V +-202 0 V +Z stroke +LT0 +0.75 0.75 0.75 C 1.000 3162 1993 204 160 BoxColFill +LTb +3162 1993 N +0 159 V +203 0 V +0 -159 V +-203 0 V +Z stroke +LT0 +0.75 0.75 0.75 C 1.000 3769 1993 203 2072 BoxColFill +LTb +3769 1993 N +0 2071 V +202 0 V +0 -2071 V +-202 0 V +Z stroke +LT0 +0.75 0.75 0.75 C 1.000 4376 1993 203 96 BoxColFill +LTb +4376 1993 N +0 95 V +202 0 V +0 -95 V +-202 0 V +Z stroke +LT0 +0.75 0.75 0.75 C 1.000 4983 1993 203 119 BoxColFill +LTb +4983 1993 N +0 118 V +202 0 V +0 -118 V +-202 0 V +Z stroke +LT0 +0.75 0.75 0.75 C 1.000 5589 1993 204 28 BoxColFill +LTb +5589 1993 N +0 27 V +203 0 V +0 -27 V +-203 0 V +Z stroke +LT0 +0.75 0.75 0.75 C 1.000 6196 1993 203 1987 BoxColFill +LTb +6196 1993 N +0 1986 V +202 0 V +0 -1986 V +-202 0 V +Z stroke +LT0 +0.75 0.75 0.75 C 1.000 UL +LT1 +1.000 UP +LTb +1.000 UL +LTb +1342 4379 M +0 -2386 V +5461 0 V +0 2386 R +-5461 0 R +1.000 UP +stroke +grestore +end +showpage +%%Trailer diff --git a/data/stack-limits.tex b/data/stack-limits.tex new file mode 100644 index 0000000..0c4ca0c --- /dev/null +++ b/data/stack-limits.tex @@ -0,0 +1,24 @@ +\begin{gnuplot}[terminal=epslatex,scale=0.8] + set size 0.9,1 + set auto x + set yrange [0:*] + set ylabel "Call stack size" + set key off + + # styling + set decimal locale + set format "%'g" + set border 3 front + set style data histogram + set style fill solid border -1 + set tics scale 0.0 + set xtics rotate by -45 offset 0.3 + set grid y + + set bmargin 5 + + # load data from csv file + set datafile separator "," + plot "data/stack-limits.dat" using 2:xtic(1) lc rgb "gray", \ + '' using ($0+0.15):($2+2000):(sprintf("%'g",$2)) with labels +\end{gnuplot} diff --git a/lst/ctor-factory-priv.js b/lst/ctor-factory-priv.js new file mode 100644 index 0000000..341450f --- /dev/null +++ b/lst/ctor-factory-priv.js @@ -0,0 +1,123 @@ +var Class = ( function( extending ) +{ + // implementation left to reader + var _privname = getRandomName(); + + var C = function( dfn ) + { + // extend from an empty base + return C.extend( null, dfn ); + }; + + C.extend = function( base, dfn ) + { + base = base || function() {}; + + // prevent ctor invocation + extending = true; + + var ctor = function() + { + // do nothing if extending + if ( extending ) + { + return; + } + + // call "actual" constructor + this.__construct && + this.__construct.apply( + this, arguments + ); + }; + + // public prototype + ctor.prototype = new base(); + ctor.prototype.constructor = ctor; + + // private prototype (read-only, + // non-configurable, non-enumerable) + Object.defineProperty( + ctor, _privname, { value: {} } + ); + + copyTo( + ctor.prototype, + ctor[ _privname ], + dfn + ); + + // done extending; clear flag to + // ensure ctor can be invoked + extending = false; + + return ctor; + }; + + function copyTo( pub, priv, members ) + { + var hasOwn = Object.prototype + .hasOwnProperty; + + for ( var member in members ) + { + if ( !hasOwn.call( members, member ) ) + { + continue; + } + + // if prefixed with an underscore, + // assign to private "prototype" + var dest = ( member[ 0 ] === '_' ) + ? priv : pub; + + dest[ member ] = wrap( + members[ member ] + ); + } + } + + function wrap( method ) + { + return function() + { + this.__priv = + this.constructor[ _privname ]; + + var retval = method.apply( + this, arguments + ); + + this.__priv = undefined; + return retval; + }; + } + + return C; +} )( false ); + + +// dummy implementation +function getRandomName() +{ + return '__privobj'; +} + + +var Foo = Class( +{ + getValue: function() + { + return this.__priv._getPrivValue(); + }, + + _getPrivValue: function() + { + return 'private'; + } +} ); + +var inst = new Foo(); +inst.getValue(); // "private" +inst._getPrivValue; // undefined +inst.__priv; // undefined diff --git a/lst/ctor-factory.js b/lst/ctor-factory.js index de63fc3..ea2b064 100644 --- a/lst/ctor-factory.js +++ b/lst/ctor-factory.js @@ -54,7 +54,7 @@ var Class = ( function( extending ) dest[ member ] = members[ member ]; } - }; + } return C; } )( false ); diff --git a/lst/func-wrap.js b/lst/func-wrap.js new file mode 100644 index 0000000..ba7b06d --- /dev/null +++ b/lst/func-wrap.js @@ -0,0 +1,33 @@ +function wrap( func ) +{ + return function() + { + return 'one ' + + func.apply( this, arguments ) + + ' three'; + }; +} + +function str( value ) +{ + return value; +} + +wrap( str )( 'two' ); // "one two three" + + +// demonstrating wrappers with prototypes +function Foo( value ) +{ + this._value = value; +}; + +Foo.prototype = { + bar: wrap( function() + { + return this._value; + } ) +}; + +var inst = new Foo( '2' ); +inst.bar(); // "one 2 three" diff --git a/sec/encap-hacks.tex b/sec/encap-hacks.tex index 9a2134f..30af510 100644 --- a/sec/encap-hacks.tex +++ b/sec/encap-hacks.tex @@ -1,5 +1,5 @@ \section{Encapsulating the Hacks} -\label{sec:encap} +\label{sec:encap-hacks} Imagine jumping into a project in order to make a simple modification and then seeing the code in \jsref{lst:prot-share}. This is a far cry from the simple protected member declarations in traditional classical object-oriented @@ -182,7 +182,7 @@ of that base constructor. \subsubsection{Factory Conveniences} Although our constructor factory described in section~\ref{sec:ctor-factory} is thus far very simple, one should take the time to realize what a powerful -abstraction has been created; it allows us to inject our own code in any part of +abstraction has been created: it allows us to inject our own code in any part of the constructor creation process, giving us full control over our class-like objects. Indeed, this abstraction will be used as a strong foundation going forward throughout all of section~\ref{sec:encap}. In the meantime, we can take @@ -242,7 +242,7 @@ require the use of the keyword, as has been demonstrated throughout this article with the various \var{Error} types. Secondly, the omission of the keyword would allow us to jump immediately into calling a method on an object without dealing with awkward precedence rules: \code{Foo( "Name" ).getName()} vs. \code{( new -Foo( "Name" ) ).getName()}. However, those reasons exit more to offer syntactic +Foo( "Name" ) ).getName()}. However, those reasons exist more to offer syntactic sugar; they do little to persuade those who do want or not mind the \operator{new} operator. @@ -287,9 +287,197 @@ instance of itself through a recursive call. Alternatively, the reader may decide to throw an error instead of automatically returning a new instance. This would require the use of the \operator{new} -operator, while still ensuring the global scope will not be polluted with -unnecessary values. If the constructor is in strict mode, then the error would -help to point out bugs in the code. However, for the reason that the keyword is -optional for many core ECMAScript constructors, the author recommends the -implementation in \jsref{lst:new-global-fix}. +operator for instantiation, while still ensuring that the global scope will not +be polluted with unnecessary values. If the constructor is in strict mode, then +the pollution of the global scope would not be an issue and the error would +instead help to point out inconsistencies in the code. However, for the reason +that the keyword is optional for many core ECMAScript constructors, the author +recommends the implementation in \jsref{lst:new-global-fix}. +\subsection{Private Member Encapsulation} +Section~\ref{sec:encap} discussed the encapsulation of private member data +by means of private property and method objects, avoiding the performance impact +of privileged members (see section~\ref{sec:privileged}). In order to avoid +memory leaks, the private data was stored on the instance itself rather than a +truly encapsulated object. The amount of code required for this implementation +is relatively small, but it is still repeated unnecessarily between all +constructors. + +The private member implementation had two distinct pieces --- private +properties, as demonstrated in \jsref{lst:encap-inst}, and private methods, as +demonstrated in \jsref{lst:method-priv}. This distinction is important, as +private methods should not be redefined for each new instance (see +\fref{fig:proto-priv-cmp}). Properties, however, \emph{must} have their values +copied for each new instance to prevent references from being shared between +multiple instances (see \jsref{lst:proto-reuse}; this is not an issue for +scalars). For the time being, we will focus on the method implementation and +leave the manual declaration of private properties to the \func{\_\_construct()} +method. + +The listings in section~\ref{sec:encap} were derived from a simple concept --- +the private member objects were within the scope of the prototype members. +However, if we are encapsulating this hack within our constructor factory, then +the members (the definition object) would be declared \emph{outside} the scope +of any private member objects that are hidden within our factory. To expose the +private ``prototype'' object, we could accept a function instead of a definition +object, which would expose a reference to the object (\jsref{lst:prot-func}). +However, this would be very unnatural and unintuitive. To keep our ``class'' +declarations simple, another method is needed. + +Consider the private member concept in a classical sense --- the data should be +available only to the methods of the class, but should not be accessible outside +of them. That is, given any class \code{C} with private property \code{C.\_priv} +and public method \code{C.getPrivValue()}, and an instance \code{i} of class +\code{C}, \code{i.\_priv} should not be defined unless within the context of +\code{i.getPrivValue()}. Consider then the only means of exposing that data to +the members of the prototype in ECMAScript without use of closures: through the +instance itself (\keyword{this}). This naturally derives an implementation that +had not been previously considered due to the impracticality of its use without +an automated factory --- exposing private members before a method invocation and +revoking them after the method has returned. + +To accomplish this, the factory must be able to intelligently determine when a +method is being invoked. This leads us into a somewhat sensitive topic --- +function wrapping. In order to perform additional logic on invocation of a +particular method, it must be wrapped within another function. This +\dfn{wrapper} would expose the private data on \keyword{this}, invoke the +original function associated with the method call, remove the reference and then +return whatever value was returned by the original function. This creates the +illusion of invoking the method directly.\footnote{This is the same concept used +to emulate \code{Function.bind()} in pre-ECMAScript 5 environments. This concept +can also be easily extended to create \dfn{partially applied functions}.} + +\lstinputlisting[% + label=lst:func-wrap, + caption=Wrapping a function by returning a \emph{new} function which calls the + original, + lastline=16 +]{lst/func-wrap.js} + +\jsref{lst:func-wrap} demonstrates the basic concept of a function wrapper. +\func{wrap()} accepts a single argument, \var{func}, and returns a new anonymous +function which invokes \var{func}, returning its value with a prefix and suffix. +Note how all arguments are forwarded to \var{func}, allowing us to invoke our +wrapped function as if it were the original. Also note the context in which +\var{func} is being called (the first argument of \func{apply()}). By binding +\keyword{this} of \var{func} to \keyword{this} of our wrapper, we are +effectively forwarding it. This detail is especially important if we are using +a wrapper within a prototype, as we \emph{must} bind \keyword{this} to the +instance that the method is being invoked upon. Use of \func{wrap()} with a +prototype is demonstrated in \jsref{lst:func-wrap-ex} below. + +\lstinputlisting[% + label=lst:func-wrap-ex, + caption=Using \func{wrap()} from \jsref{lst:func-wrap} with prototypes, + firstnumber=last, + firstline=20 +]{lst/func-wrap.js} + +It is this concept that will be used to implement method wrapping in our +constructor factory. For each function $f$ of definition object $D$, $f'$ will +be created using a method similar to \jsref{lst:func-wrap-ex}. $f'$ will invoke +$f$ after setting the private member object on \keyword{this}, then reset it +after $f$ returns. Finally, the return value of $f$ will be returned by $f'$. It +should be noted that $f'$ must exist even if $f$ is public, since public methods +may still need access to private members.\footnote{As we will see in the +examination of \fref{fig:func-wrap-perf}, the performance impact of this +decision is minimal.} + +\begin{figure*}[t] +\center +\subfloat[% + Wrapper performance \emph{(invocation only)}. Operations per second rounded to + millions.\cite{jsperf-func-wrap} Numbers in parenthesis indicate percent + change between the two values, indicating a significant performance loss. +]{ + \input{data/func-wrap-invoke.tex} + \label{fig:func-wrap-perf-invoke} +} +\quad +\subfloat[% + Wrapper performance \emph{with business logic} + (\code{(new Array(100)).join(',|').split('|')}); performance + impact is negligible. Operations per second.\cite{jsperf-func-wrap-blogic} +]{ + \input{data/func-wrap-blogic.tex} + \label{fig:func-wrap-perf-blogic} +} +\caption{Function wrapping performance considerations. When measuring invocation +performance, the wrapper appears to be a terrible solution to any problem. +However, when considering the business logic the remainder of the software is +likely to contain, the effects of the wrapper are negligible. As such, worrying +about the wrapper is likely to be a micro-optimization, unless dealing with +call stack limitations. The wrapper in these tests simply invokes the wrapped +method with \code{Function.apply()}, forwarding all arguments.} +\label{fig:func-wrap-perf} +\end{figure*} + +Many readers are likely to be concerned about a decision that wraps every +function of our definition object, as this will require two function calls each +time a method is invoked. \fref{fig:func-wrap-perf-invoke} shows why this detail +is likely to be a concern --- invoking our wrapped function is so slow in +comparison to invoking the original function directly that the solution seems +prohibitive. However, one must consider how functions are \emph{actually} used +--- to perform some sort of business logic. It is rare that we would invoke +bodiless functions continuously in a loop. Rather, we should take into +consideration the \emph{percent change between function invocations that contain +some sort of business logic}. This is precisely what +\frefpg{fig:func-wrap-perf-blogic} takes into consideration, showing that our +invocation worry is would actually be a micro-optimization. For example, in +software that performs DOM manipulation, the performance impact of +wrapper invocation is likely to be negligible due to repaints being highly +intensive operations. + +One legitimate concern of our wrapper implementation, however, is limited +call stack space. The wrapper will effectively cut the remaining stack space in +half if dealing with recursive operations on itself, which may be a problem +for environments that do not support tail call optimizations, or for algorithms +that are not written in such a way that tail call optimizations can be +performed.\footnote{Another concern is that the engine may not be able to +perform tail call optimization because the function may recurse on the wrapper +instead of itself.} In such a situation, we can avoid the problem entirely by +recommending that heavy recursive algorithms do not invoke wrapped methods; +instead, the recursive operation can be performed using ``normal'' (unwrapped) +functions and its result returned by a wrapped method call. + +\begin{figure}[t] + \center + \input{data/stack-limits.tex} + \caption{Call stack limits of various common browsers. \cite{oreilly-hpj} + Determining the call stack limit for your own environment is as simple as + incrementing a counter for each recursive call until an error is thrown.} + \label{fig:stack-limits} +\end{figure} + +That said, call stack sizes for ECMAScript environments are growing increasingly +larger. Call stack limits for common browsers (including historical versions for +comparison) are listed in \frefpg{fig:stack-limits}. Should this limit be +reached, another alternative is to use \func{setTimeout()} to reset the stack +and continue the recursive operation. This can also have the benefit of making +the operation asynchronous. + +Factoring this logic into the constructor factory is further complicated by our +inability to distinguish between members intended to be public and those +intended to be private. In section~\ref{sec:encap}, this issue was not a concern +because the members could be explicitly specified separately per implementation. +With the factory, we are provided only a single definition object; asking for +multiple would be confusing, messy and unnatural to those coming from other +classical object-oriented languages. Therefore, our second task shall be to +augment \func{copyTo()} in \jsref{lst:ctor-factory} to distinguish between +public and private members. + +Section~\ref{sec:privileged} mentioned the convention of using a single +underscore as a prefix for member names to denote a private member (e.g. +\code{this.\_foo}). We will adopt this convention for our definition object, as +it is both simple and performant (only a single-character check). Combining this +concept with the wrapper implementation, we arrive at +\jsref{lst:ctor-factory-priv}. + +\lstinputlisting[% + label=lst:ctor-factory-priv, + caption=Altering the constructor factory in \jsref{lst:ctor-factory} to + support private methods in a manner similar to \jsref{lst:method-priv}, + lastline=97 +]{lst/ctor-factory-priv.js} + +(INCOMPLETE.) diff --git a/sec/licenses.tex b/sec/licenses.tex index 84b224d..e37dd40 100644 --- a/sec/licenses.tex +++ b/sec/licenses.tex @@ -46,3 +46,14 @@ The following listings are provided under alternative licenses. \small \item[\jsref{lst:defprop-check}] GNU LGPL (taken from ease.js) \end{description} + +\subsection{Reference Licenses} +The reader should be warned that certain references mentioned within this +article are non-free (that is, their licenses do not permit redistribution, +modification, or derivative works). These references are denoted with +``\citenf''. While these resources are not the official documentation for free +software (and consequently do not pose a direct threat to the users of free +software), be aware that they do prohibit you from helping your friends and +neighbors by giving them a copy. + +This article itself holds no such restrictions.