pax_global_header00006660000000000000000000000064133114570320014511gustar00rootroot0000000000000052 comment=15f95af6e78dcd2030d8195a138bd88d4f403546 golang-github-robertkrimen-otto-0.0~git20180617.15f95af/000077500000000000000000000000001331145703200223755ustar00rootroot00000000000000golang-github-robertkrimen-otto-0.0~git20180617.15f95af/.gitignore000066400000000000000000000000741331145703200243660ustar00rootroot00000000000000/.test /otto/otto /otto/otto-* /test/test-*.js /test/tester golang-github-robertkrimen-otto-0.0~git20180617.15f95af/DESIGN.markdown000066400000000000000000000001211331145703200251040ustar00rootroot00000000000000* Designate the filename of "anonymous" source code by the hash (md5/sha1, etc.) golang-github-robertkrimen-otto-0.0~git20180617.15f95af/LICENSE000066400000000000000000000020411331145703200233770ustar00rootroot00000000000000Copyright (c) 2012 Robert Krimen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. golang-github-robertkrimen-otto-0.0~git20180617.15f95af/Makefile000066400000000000000000000021531331145703200240360ustar00rootroot00000000000000.PHONY: test test-race test-release release release-check test-262 .PHONY: parser .PHONY: otto assets underscore TESTS := \ ~ TEST := -v --run TEST := -v TEST := -v --run Test\($(subst $(eval) ,\|,$(TESTS))\) TEST := . test: parser inline.go go test -i go test $(TEST) @echo PASS parser: $(MAKE) -C parser inline.go: inline.pl ./$< > $@ ################# # release, test # ################# release: test-race test-release for package in . parser token ast file underscore registry; do (cd $$package && godocdown --signature > README.markdown); done @echo \*\*\* make release-check @echo PASS release-check: .test $(MAKE) -C test build test $(MAKE) -C .test/test262 build test @echo PASS test-262: .test $(MAKE) -C .test/test262 build test @echo PASS test-release: go test -i go test test-race: go test -race -i go test -race ################################# # otto, assets, underscore, ... # ################################# otto: $(MAKE) -C otto assets: mkdir -p .assets for file in underscore/test/*.js; do tr "\`" "_" < $$file > .assets/`basename $$file`; done underscore: $(MAKE) -C $@ golang-github-robertkrimen-otto-0.0~git20180617.15f95af/README.markdown000066400000000000000000000460531331145703200251060ustar00rootroot00000000000000# otto -- ```go import "github.com/robertkrimen/otto" ``` Package otto is a JavaScript parser and interpreter written natively in Go. http://godoc.org/github.com/robertkrimen/otto ```go import ( "github.com/robertkrimen/otto" ) ``` Run something in the VM ```go vm := otto.New() vm.Run(` abc = 2 + 2; console.log("The value of abc is " + abc); // 4 `) ``` Get a value out of the VM ```go if value, err := vm.Get("abc"); err == nil { if value_int, err := value.ToInteger(); err == nil { fmt.Printf("", value_int, err) } } ``` Set a number ```go vm.Set("def", 11) vm.Run(` console.log("The value of def is " + def); // The value of def is 11 `) ``` Set a string ```go vm.Set("xyzzy", "Nothing happens.") vm.Run(` console.log(xyzzy.length); // 16 `) ``` Get the value of an expression ```go value, _ = vm.Run("xyzzy.length") { // value is an int64 with a value of 16 value, _ := value.ToInteger() } ``` An error happens ```go value, err = vm.Run("abcdefghijlmnopqrstuvwxyz.length") if err != nil { // err = ReferenceError: abcdefghijlmnopqrstuvwxyz is not defined // If there is an error, then value.IsUndefined() is true ... } ``` Set a Go function ```go vm.Set("sayHello", func(call otto.FunctionCall) otto.Value { fmt.Printf("Hello, %s.\n", call.Argument(0).String()) return otto.Value{} }) ``` Set a Go function that returns something useful ```go vm.Set("twoPlus", func(call otto.FunctionCall) otto.Value { right, _ := call.Argument(0).ToInteger() result, _ := vm.ToValue(2 + right) return result }) ``` Use the functions in JavaScript ```go result, _ = vm.Run(` sayHello("Xyzzy"); // Hello, Xyzzy. sayHello(); // Hello, undefined result = twoPlus(2.0); // 4 `) ``` ### Parser A separate parser is available in the parser package if you're just interested in building an AST. http://godoc.org/github.com/robertkrimen/otto/parser Parse and return an AST ```go filename := "" // A filename is optional src := ` // Sample xyzzy example (function(){ if (3.14159 > 0) { console.log("Hello, World."); return; } var xyzzy = NaN; console.log("Nothing happens."); return xyzzy; })(); ` // Parse some JavaScript, yielding a *ast.Program and/or an ErrorList program, err := parser.ParseFile(nil, filename, src, 0) ``` ### otto You can run (Go) JavaScript from the commandline with: http://github.com/robertkrimen/otto/tree/master/otto $ go get -v github.com/robertkrimen/otto/otto Run JavaScript by entering some source on stdin or by giving otto a filename: $ otto example.js ### underscore Optionally include the JavaScript utility-belt library, underscore, with this import: ```go import ( "github.com/robertkrimen/otto" _ "github.com/robertkrimen/otto/underscore" ) // Now every otto runtime will come loaded with underscore ``` For more information: http://github.com/robertkrimen/otto/tree/master/underscore ### Caveat Emptor The following are some limitations with otto: * "use strict" will parse, but does nothing. * The regular expression engine (re2/regexp) is not fully compatible with the ECMA5 specification. * Otto targets ES5. ES6 features (eg: Typed Arrays) are not supported. ### Regular Expression Incompatibility Go translates JavaScript-style regular expressions into something that is "regexp" compatible via `parser.TransformRegExp`. Unfortunately, RegExp requires backtracking for some patterns, and backtracking is not supported by the standard Go engine: https://code.google.com/p/re2/wiki/Syntax Therefore, the following syntax is incompatible: (?=) // Lookahead (positive), currently a parsing error (?!) // Lookahead (backhead), currently a parsing error \1 // Backreference (\1, \2, \3, ...), currently a parsing error A brief discussion of these limitations: "Regexp (?!re)" https://groups.google.com/forum/?fromgroups=#%21topic/golang-nuts/7qgSDWPIh_E More information about re2: https://code.google.com/p/re2/ In addition to the above, re2 (Go) has a different definition for \s: [\t\n\f\r ]. The JavaScript definition, on the other hand, also includes \v, Unicode "Separator, Space", etc. ### Halting Problem If you want to stop long running executions (like third-party code), you can use the interrupt channel to do this: ```go package main import ( "errors" "fmt" "os" "time" "github.com/robertkrimen/otto" ) var halt = errors.New("Stahp") func main() { runUnsafe(`var abc = [];`) runUnsafe(` while (true) { // Loop forever }`) } func runUnsafe(unsafe string) { start := time.Now() defer func() { duration := time.Since(start) if caught := recover(); caught != nil { if caught == halt { fmt.Fprintf(os.Stderr, "Some code took to long! Stopping after: %v\n", duration) return } panic(caught) // Something else happened, repanic! } fmt.Fprintf(os.Stderr, "Ran code successfully: %v\n", duration) }() vm := otto.New() vm.Interrupt = make(chan func(), 1) // The buffer prevents blocking go func() { time.Sleep(2 * time.Second) // Stop after two seconds vm.Interrupt <- func() { panic(halt) } }() vm.Run(unsafe) // Here be dragons (risky code) } ``` Where is setTimeout/setInterval? These timing functions are not actually part of the ECMA-262 specification. Typically, they belong to the `window` object (in the browser). It would not be difficult to provide something like these via Go, but you probably want to wrap otto in an event loop in that case. For an example of how this could be done in Go with otto, see natto: http://github.com/robertkrimen/natto Here is some more discussion of the issue: * http://book.mixu.net/node/ch2.html * http://en.wikipedia.org/wiki/Reentrancy_%28computing%29 * http://aaroncrane.co.uk/2009/02/perl_safe_signals/ ## Usage ```go var ErrVersion = errors.New("version mismatch") ``` #### type Error ```go type Error struct { } ``` An Error represents a runtime error, e.g. a TypeError, a ReferenceError, etc. #### func (Error) Error ```go func (err Error) Error() string ``` Error returns a description of the error TypeError: 'def' is not a function #### func (Error) String ```go func (err Error) String() string ``` String returns a description of the error and a trace of where the error occurred. TypeError: 'def' is not a function at xyz (:3:9) at :7:1/ #### type FunctionCall ```go type FunctionCall struct { This Value ArgumentList []Value Otto *Otto } ``` FunctionCall is an encapsulation of a JavaScript function call. #### func (FunctionCall) Argument ```go func (self FunctionCall) Argument(index int) Value ``` Argument will return the value of the argument at the given index. If no such argument exists, undefined is returned. #### type Object ```go type Object struct { } ``` Object is the representation of a JavaScript object. #### func (Object) Call ```go func (self Object) Call(name string, argumentList ...interface{}) (Value, error) ``` Call a method on the object. It is essentially equivalent to: var method, _ := object.Get(name) method.Call(object, argumentList...) An undefined value and an error will result if: 1. There is an error during conversion of the argument list 2. The property is not actually a function 3. An (uncaught) exception is thrown #### func (Object) Class ```go func (self Object) Class() string ``` Class will return the class string of the object. The return value will (generally) be one of: Object Function Array String Number Boolean Date RegExp #### func (Object) Get ```go func (self Object) Get(name string) (Value, error) ``` Get the value of the property with the given name. #### func (Object) Keys ```go func (self Object) Keys() []string ``` Get the keys for the object Equivalent to calling Object.keys on the object #### func (Object) Set ```go func (self Object) Set(name string, value interface{}) error ``` Set the property of the given name to the given value. An error will result if the setting the property triggers an exception (i.e. read-only), or there is an error during conversion of the given value. #### func (Object) Value ```go func (self Object) Value() Value ``` Value will return self as a value. #### type Otto ```go type Otto struct { // Interrupt is a channel for interrupting the runtime. You can use this to halt a long running execution, for example. // See "Halting Problem" for more information. Interrupt chan func() } ``` Otto is the representation of the JavaScript runtime. Each instance of Otto has a self-contained namespace. #### func New ```go func New() *Otto ``` New will allocate a new JavaScript runtime #### func Run ```go func Run(src interface{}) (*Otto, Value, error) ``` Run will allocate a new JavaScript runtime, run the given source on the allocated runtime, and return the runtime, resulting value, and error (if any). src may be a string, a byte slice, a bytes.Buffer, or an io.Reader, but it MUST always be in UTF-8. src may also be a Script. src may also be a Program, but if the AST has been modified, then runtime behavior is undefined. #### func (Otto) Call ```go func (self Otto) Call(source string, this interface{}, argumentList ...interface{}) (Value, error) ``` Call the given JavaScript with a given this and arguments. If this is nil, then some special handling takes place to determine the proper this value, falling back to a "standard" invocation if necessary (where this is undefined). If source begins with "new " (A lowercase new followed by a space), then Call will invoke the function constructor rather than performing a function call. In this case, the this argument has no effect. ```go // value is a String object value, _ := vm.Call("Object", nil, "Hello, World.") // Likewise... value, _ := vm.Call("new Object", nil, "Hello, World.") // This will perform a concat on the given array and return the result // value is [ 1, 2, 3, undefined, 4, 5, 6, 7, "abc" ] value, _ := vm.Call(`[ 1, 2, 3, undefined, 4 ].concat`, nil, 5, 6, 7, "abc") ``` #### func (*Otto) Compile ```go func (self *Otto) Compile(filename string, src interface{}) (*Script, error) ``` Compile will parse the given source and return a Script value or nil and an error if there was a problem during compilation. ```go script, err := vm.Compile("", `var abc; if (!abc) abc = 0; abc += 2; abc;`) vm.Run(script) ``` #### func (*Otto) Copy ```go func (in *Otto) Copy() *Otto ``` Copy will create a copy/clone of the runtime. Copy is useful for saving some time when creating many similar runtimes. This method works by walking the original runtime and cloning each object, scope, stash, etc. into a new runtime. Be on the lookout for memory leaks or inadvertent sharing of resources. #### func (Otto) Get ```go func (self Otto) Get(name string) (Value, error) ``` Get the value of the top-level binding of the given name. If there is an error (like the binding does not exist), then the value will be undefined. #### func (Otto) Object ```go func (self Otto) Object(source string) (*Object, error) ``` Object will run the given source and return the result as an object. For example, accessing an existing object: ```go object, _ := vm.Object(`Number`) ``` Or, creating a new object: ```go object, _ := vm.Object(`({ xyzzy: "Nothing happens." })`) ``` Or, creating and assigning an object: ```go object, _ := vm.Object(`xyzzy = {}`) object.Set("volume", 11) ``` If there is an error (like the source does not result in an object), then nil and an error is returned. #### func (Otto) Run ```go func (self Otto) Run(src interface{}) (Value, error) ``` Run will run the given source (parsing it first if necessary), returning the resulting value and error (if any) src may be a string, a byte slice, a bytes.Buffer, or an io.Reader, but it MUST always be in UTF-8. If the runtime is unable to parse source, then this function will return undefined and the parse error (nothing will be evaluated in this case). src may also be a Script. src may also be a Program, but if the AST has been modified, then runtime behavior is undefined. #### func (Otto) Set ```go func (self Otto) Set(name string, value interface{}) error ``` Set the top-level binding of the given name to the given value. Set will automatically apply ToValue to the given value in order to convert it to a JavaScript value (type Value). If there is an error (like the binding is read-only, or the ToValue conversion fails), then an error is returned. If the top-level binding does not exist, it will be created. #### func (Otto) ToValue ```go func (self Otto) ToValue(value interface{}) (Value, error) ``` ToValue will convert an interface{} value to a value digestible by otto/JavaScript. #### type Script ```go type Script struct { } ``` Script is a handle for some (reusable) JavaScript. Passing a Script value to a run method will evaluate the JavaScript. #### func (*Script) String ```go func (self *Script) String() string ``` #### type Value ```go type Value struct { } ``` Value is the representation of a JavaScript value. #### func FalseValue ```go func FalseValue() Value ``` FalseValue will return a value representing false. It is equivalent to: ```go ToValue(false) ``` #### func NaNValue ```go func NaNValue() Value ``` NaNValue will return a value representing NaN. It is equivalent to: ```go ToValue(math.NaN()) ``` #### func NullValue ```go func NullValue() Value ``` NullValue will return a Value representing null. #### func ToValue ```go func ToValue(value interface{}) (Value, error) ``` ToValue will convert an interface{} value to a value digestible by otto/JavaScript This function will not work for advanced types (struct, map, slice/array, etc.) and you should use Otto.ToValue instead. #### func TrueValue ```go func TrueValue() Value ``` TrueValue will return a value representing true. It is equivalent to: ```go ToValue(true) ``` #### func UndefinedValue ```go func UndefinedValue() Value ``` UndefinedValue will return a Value representing undefined. #### func (Value) Call ```go func (value Value) Call(this Value, argumentList ...interface{}) (Value, error) ``` Call the value as a function with the given this value and argument list and return the result of invocation. It is essentially equivalent to: value.apply(thisValue, argumentList) An undefined value and an error will result if: 1. There is an error during conversion of the argument list 2. The value is not actually a function 3. An (uncaught) exception is thrown #### func (Value) Class ```go func (value Value) Class() string ``` Class will return the class string of the value or the empty string if value is not an object. The return value will (generally) be one of: Object Function Array String Number Boolean Date RegExp #### func (Value) Export ```go func (self Value) Export() (interface{}, error) ``` Export will attempt to convert the value to a Go representation and return it via an interface{} kind. Export returns an error, but it will always be nil. It is present for backwards compatibility. If a reasonable conversion is not possible, then the original value is returned. undefined -> nil (FIXME?: Should be Value{}) null -> nil boolean -> bool number -> A number type (int, float32, uint64, ...) string -> string Array -> []interface{} Object -> map[string]interface{} #### func (Value) IsBoolean ```go func (value Value) IsBoolean() bool ``` IsBoolean will return true if value is a boolean (primitive). #### func (Value) IsDefined ```go func (value Value) IsDefined() bool ``` IsDefined will return false if the value is undefined, and true otherwise. #### func (Value) IsFunction ```go func (value Value) IsFunction() bool ``` IsFunction will return true if value is a function. #### func (Value) IsNaN ```go func (value Value) IsNaN() bool ``` IsNaN will return true if value is NaN (or would convert to NaN). #### func (Value) IsNull ```go func (value Value) IsNull() bool ``` IsNull will return true if the value is null, and false otherwise. #### func (Value) IsNumber ```go func (value Value) IsNumber() bool ``` IsNumber will return true if value is a number (primitive). #### func (Value) IsObject ```go func (value Value) IsObject() bool ``` IsObject will return true if value is an object. #### func (Value) IsPrimitive ```go func (value Value) IsPrimitive() bool ``` IsPrimitive will return true if value is a primitive (any kind of primitive). #### func (Value) IsString ```go func (value Value) IsString() bool ``` IsString will return true if value is a string (primitive). #### func (Value) IsUndefined ```go func (value Value) IsUndefined() bool ``` IsUndefined will return true if the value is undefined, and false otherwise. #### func (Value) Object ```go func (value Value) Object() *Object ``` Object will return the object of the value, or nil if value is not an object. This method will not do any implicit conversion. For example, calling this method on a string primitive value will not return a String object. #### func (Value) String ```go func (value Value) String() string ``` String will return the value as a string. This method will make return the empty string if there is an error. #### func (Value) ToBoolean ```go func (value Value) ToBoolean() (bool, error) ``` ToBoolean will convert the value to a boolean (bool). ToValue(0).ToBoolean() => false ToValue("").ToBoolean() => false ToValue(true).ToBoolean() => true ToValue(1).ToBoolean() => true ToValue("Nothing happens").ToBoolean() => true If there is an error during the conversion process (like an uncaught exception), then the result will be false and an error. #### func (Value) ToFloat ```go func (value Value) ToFloat() (float64, error) ``` ToFloat will convert the value to a number (float64). ToValue(0).ToFloat() => 0. ToValue(1.1).ToFloat() => 1.1 ToValue("11").ToFloat() => 11. If there is an error during the conversion process (like an uncaught exception), then the result will be 0 and an error. #### func (Value) ToInteger ```go func (value Value) ToInteger() (int64, error) ``` ToInteger will convert the value to a number (int64). ToValue(0).ToInteger() => 0 ToValue(1.1).ToInteger() => 1 ToValue("11").ToInteger() => 11 If there is an error during the conversion process (like an uncaught exception), then the result will be 0 and an error. #### func (Value) ToString ```go func (value Value) ToString() (string, error) ``` ToString will convert the value to a string (string). ToValue(0).ToString() => "0" ToValue(false).ToString() => "false" ToValue(1.1).ToString() => "1.1" ToValue("11").ToString() => "11" ToValue('Nothing happens.').ToString() => "Nothing happens." If there is an error during the conversion process (like an uncaught exception), then the result will be the empty string ("") and an error. -- **godocdown** http://github.com/robertkrimen/godocdown golang-github-robertkrimen-otto-0.0~git20180617.15f95af/array_test.go000066400000000000000000000436531331145703200251140ustar00rootroot00000000000000package otto import ( "testing" ) func TestArray(t *testing.T) { tt(t, func() { test, _ := test() test(` var abc = [ undefined, "Nothing happens." ]; abc.length; `, 2) test(` abc = ""+[0, 1, 2, 3]; def = [].toString(); ghi = [null, 4, "null"].toString(); [ abc, def, ghi ]; `, "0,1,2,3,,,4,null") test(`new Array(0).length`, 0) test(`new Array(11).length`, 11) test(`new Array(11, 1).length`, 2) test(` abc = [0, 1, 2, 3]; abc.xyzzy = "Nothing happens."; delete abc[1]; var xyzzy = delete abc.xyzzy; [ abc, xyzzy, abc.xyzzy ]; `, "0,,2,3,true,") test(` var abc = [0, 1, 2, 3, 4]; abc.length = 2; abc; `, "0,1") test(`raise: [].length = 3.14159; `, "RangeError") test(`raise: new Array(3.14159); `, "RangeError") test(` Object.defineProperty(Array.prototype, "0", { value: 100, writable: false, configurable: true }); abc = [101]; abc.hasOwnProperty("0") && abc[0] === 101; `, true) test(` abc = [,,undefined]; [ abc.hasOwnProperty(0), abc.hasOwnProperty(1), abc.hasOwnProperty(2) ]; `, "false,false,true") test(` abc = Object.getOwnPropertyDescriptor(Array, "prototype"); [ [ typeof Array.prototype ], [ abc.writable, abc.enumerable, abc.configurable ] ]; `, "object,false,false,false") }) } func TestArray_toString(t *testing.T) { tt(t, func() { { test(` Array.prototype.toString = function() { return "Nothing happens."; } abc = Array.prototype.toString(); def = [].toString(); ghi = [null, 4, "null"].toString(); [ abc, def, ghi ].join(","); `, "Nothing happens.,Nothing happens.,Nothing happens.") } { test(` Array.prototype.join = undefined abc = Array.prototype.toString() def = [].toString() ghi = [null, 4, "null"].toString() abc + "," + def + "," + ghi; `, "[object Array],[object Array],[object Array]") } }) } func TestArray_toLocaleString(t *testing.T) { tt(t, func() { test, _ := test() defer mockUTC()() test(` [ 3.14159, "abc", undefined, new Date(0) ].toLocaleString(); `, "3.14159,abc,,1970-01-01 00:00:00") test(`raise: [ { toLocaleString: undefined } ].toLocaleString(); `, "TypeError") }) } func TestArray_concat(t *testing.T) { tt(t, func() { test, _ := test() test(` abc = [0, 1, 2]; def = [-1, -2, -3]; ghi = abc.concat(def); jkl = abc.concat(def, 3, 4, 5); mno = def.concat(-4, -5, abc); [ ghi, jkl, mno ].join(";"); `, "0,1,2,-1,-2,-3;0,1,2,-1,-2,-3,3,4,5;-1,-2,-3,-4,-5,0,1,2") test(` var abc = [,1]; var def = abc.concat([], [,]); def.getClass = Object.prototype.toString; [ def.getClass(), typeof def[0], def[1], typeof def[2], def.length ]; `, "[object Array],undefined,1,undefined,3") test(` Object.defineProperty(Array.prototype, "0", { value: 100, writable: false, configurable: true }); var abc = Array.prototype.concat.call(101); var hasProperty = abc.hasOwnProperty("0"); var instanceOfVerify = typeof abc[0] === "object"; var verifyValue = false; verifyValue = abc[0] == 101; var verifyEnumerable = false; for (var property in abc) { if (property === "0" && abc.hasOwnProperty("0")) { verifyEnumerable = true; } } var verifyWritable = false; abc[0] = 12; verifyWritable = abc[0] === 12; var verifyConfigurable = false; delete abc[0]; verifyConfigurable = abc.hasOwnProperty("0"); [ hasProperty, instanceOfVerify, verifyValue, !verifyConfigurable, verifyEnumerable, verifyWritable ]; `, "true,true,true,true,true,true") }) } func TestArray_splice(t *testing.T) { tt(t, func() { test, _ := test() test(` abc = [0, 1, 2]; def = abc.splice(1, 2, 3, 4, 5); ghi = [].concat(abc); jkl = ghi.splice(17, 21, 7, 8, 9); mno = [].concat(abc); pqr = mno.splice(2); [ abc, def, ghi, jkl, mno, pqr ].join(";"); `, "0,3,4,5;1,2;0,3,4,5,7,8,9;;0,3;4,5") }) } func TestArray_shift(t *testing.T) { tt(t, func() { test, _ := test() test(` abc = [0, 1, 2]; def = abc.shift(); ghi = [].concat(abc); jkl = abc.shift(); mno = [].concat(abc); pqr = abc.shift(); stu = [].concat(abc); vwx = abc.shift(); [ abc, def, ghi, jkl, mno, pqr, stu, vwx ].join(";"); `, ";0;1,2;1;2;2;;") }) } func TestArray_push(t *testing.T) { tt(t, func() { test, _ := test() test(` abc = [0]; def = abc.push(1); ghi = [].concat(abc); jkl = abc.push(2,3,4); [ abc, def, ghi, jkl ].join(";"); `, "0,1,2,3,4;2;0,1;5") }) } func TestArray_pop(t *testing.T) { tt(t, func() { test, _ := test() test(` abc = [0,1]; def = abc.pop(); ghi = [].concat(abc); jkl = abc.pop(); mno = [].concat(abc); pqr = abc.pop(); [ abc, def, ghi, jkl, mno, pqr ].join(";"); `, ";1;0;0;;") }) } func TestArray_slice(t *testing.T) { tt(t, func() { test, _ := test() test(` abc = [0,1,2,3]; def = abc.slice(); ghi = abc.slice(1); jkl = abc.slice(3,-1); mno = abc.slice(2,-1); pqr = abc.slice(-1, -10); [ abc, def, ghi, jkl, mno, pqr ].join(";"); `, "0,1,2,3;0,1,2,3;1,2,3;;2;") // Array.protoype.slice is generic test(` abc = { 0: 0, 1: 1, 2: 2, 3: 3 }; abc.length = 4; def = Array.prototype.slice.call(abc); ghi = Array.prototype.slice.call(abc,1); jkl = Array.prototype.slice.call(abc,3,-1); mno = Array.prototype.slice.call(abc,2,-1); pqr = Array.prototype.slice.call(abc,-1,-10); [ abc, def, ghi, jkl, pqr ].join(";"); `, "[object Object];0,1,2,3;1,2,3;;") }) } func TestArray_sliceArguments(t *testing.T) { tt(t, func() { test, _ := test() test(` (function(){ return Array.prototype.slice.call(arguments, 1) })({}, 1, 2, 3); `, "1,2,3") }) } func TestArray_unshift(t *testing.T) { tt(t, func() { test, _ := test() test(` abc = []; def = abc.unshift(0); ghi = [].concat(abc); jkl = abc.unshift(1,2,3,4); [ abc, def, ghi, jkl ].join(";"); `, "1,2,3,4,0;1;0;5") }) } func TestArray_reverse(t *testing.T) { tt(t, func() { test, _ := test() test(` abc = [0,1,2,3].reverse(); def = [0,1,2].reverse(); [ abc, def ]; `, "3,2,1,0,2,1,0") }) } func TestArray_sort(t *testing.T) { tt(t, func() { test, _ := test() test(` abc = [0,1,2,3].sort(); def = [3,2,1,0].sort(); ghi = [].sort(); jkl = [0].sort(); mno = [1,0].sort(); pqr = [1,5,-10, 100, 8, 72, 401, 0.05].sort(); stu = [1,5,-10, 100, 8, 72, 401, 0.05].sort(function(x, y){ return x == y ? 0 : x < y ? -1 : 1 }); vwx = [1,2,3,1,2,3].sort(); yza = [1,2,3,1,0,1,-1,0].sort(); [ abc, def, ghi, jkl, mno, pqr, stu, vwx, yza ].join(";"); `, "0,1,2,3;0,1,2,3;;0;0,1;-10,0.05,1,100,401,5,72,8;-10,0.05,1,5,8,72,100,401;1,1,2,2,3,3;-1,0,0,1,1,1,2,3") test(`Array.prototype.sort.length`, 1) }) } func TestArray_isArray(t *testing.T) { tt(t, func() { test, _ := test() test(` [ Array.isArray.length, Array.isArray(), Array.isArray([]), Array.isArray({}) ]; `, "1,false,true,false") test(`Array.isArray(Math)`, false) }) } func TestArray_indexOf(t *testing.T) { tt(t, func() { test, _ := test() test(`['a', 'b', 'c', 'b'].indexOf('b')`, 1) test(`['a', 'b', 'c', 'b'].indexOf('b', 2)`, 3) test(`['a', 'b', 'c', 'b'].indexOf('b', -2)`, 3) test(` Object.prototype.indexOf = Array.prototype.indexOf; var abc = {0: 'a', 1: 'b', 2: 'c', length: 3}; abc.indexOf('c'); `, 2) test(`[true].indexOf(true, "-Infinity")`, 0) test(` var target = {}; Math[3] = target; Math.length = 5; Array.prototype.indexOf.call(Math, target) === 3; `, true) test(` var _NaN = NaN; var abc = new Array("NaN", undefined, 0, false, null, {toString:function(){return NaN}}, "false", _NaN, NaN); abc.indexOf(NaN); `, -1) test(` var abc = {toString:function (){return 0}}; var def = 1; var ghi = -(4/3); var jkl = new Array(false, undefined, null, "0", abc, -1.3333333333333, "string", -0, true, +0, def, 1, 0, false, ghi, -(4/3)); [ jkl.indexOf(-(4/3)), jkl.indexOf(0), jkl.indexOf(-0), jkl.indexOf(1) ]; `, "14,7,7,10") }) } func TestArray_lastIndexOf(t *testing.T) { tt(t, func() { test, _ := test() test(`['a', 'b', 'c', 'b'].lastIndexOf('b')`, 3) test(`['a', 'b', 'c', 'b'].lastIndexOf('b', 2)`, 1) test(`['a', 'b', 'c', 'b'].lastIndexOf('b', -2)`, 1) test(` Object.prototype.lastIndexOf = Array.prototype.lastIndexOf; var abc = {0: 'a', 1: 'b', 2: 'c', 3: 'b', length: 4}; abc.lastIndexOf('b'); `, 3) test(` var target = {}; Math[3] = target; Math.length = 5; [ Array.prototype.lastIndexOf.call(Math, target) === 3 ]; `, "true") test(` var _NaN = NaN; var abc = new Array("NaN", undefined, 0, false, null, {toString:function(){return NaN}}, "false", _NaN, NaN); abc.lastIndexOf(NaN); `, -1) test(` var abc = {toString:function (){return 0}}; var def = 1; var ghi = -(4/3); var jkl = new Array(false, undefined, null, "0", abc, -1.3333333333333, "string", -0, true, +0, def, 1, 0, false, ghi, -(4/3)); [ jkl.lastIndexOf(-(4/3)), jkl.indexOf(0), jkl.indexOf(-0), jkl.indexOf(1) ]; `, "15,7,7,10") }) } func TestArray_every(t *testing.T) { tt(t, func() { test, _ := test() test(`raise: [].every()`, "TypeError") test(`raise: [].every("abc")`, "TypeError") test(`[].every(function() { return false })`, true) test(`[1,2,3].every(function() { return false })`, false) test(`[1,2,3].every(function() { return true })`, true) test(`[1,2,3].every(function(_, index) { if (index === 1) return true })`, false) test(` var abc = function(value, index, object) { return ('[object Math]' !== Object.prototype.toString.call(object)); }; Math.length = 1; Math[0] = 1; !Array.prototype.every.call(Math, abc); `, true) test(` var def = false; var abc = function(value, index, object) { def = true; return this === Math; }; [11].every(abc, Math) && def; `, true) test(` var def = false; var abc = function(value, index, object) { def = true; return Math; }; [11].every(abc) && def; `, true) }) } func TestArray_some(t *testing.T) { tt(t, func() { test, _ := test() test(`raise: [].some("abc")`, "TypeError") test(`[].some(function() { return true })`, false) test(`[1,2,3].some(function() { return false })`, false) test(`[1,2,3].some(function() { return true })`, true) test(`[1,2,3].some(function(_, index) { if (index === 1) return true })`, true) test(` var abc = function(value, index, object) { return ('[object Math]' !== Object.prototype.toString.call(object)); }; Math.length = 1; Math[0] = 1; !Array.prototype.some.call(Math, abc); `, true) test(` var abc = function(value, index, object) { return this === Math; }; [11].some(abc, Math); `, true) test(` var abc = function(value, index, object) { return Math; }; [11].some(abc); `, true) }) } func TestArray_forEach(t *testing.T) { tt(t, func() { test, _ := test() test(`raise: [].forEach("abc")`, "TypeError") test(` var abc = 0; [].forEach(function(value) { abc += value; }); abc; `, 0) test(` abc = 0; var def = []; [1,2,3].forEach(function(value, index) { abc += value; def.push(index); }); [ abc, def ]; `, "6,0,1,2") test(` var def = false; var abc = function(value, index, object) { def = ('[object Math]' === Object.prototype.toString.call(object)); }; Math.length = 1; Math[0] = 1; Array.prototype.forEach.call(Math, abc); def; `, true) test(` var def = false; var abc = function(value, index, object) { def = this === Math; }; [11].forEach(abc, Math); def; `, true) }) } func TestArray_indexing(t *testing.T) { tt(t, func() { test, _ := test() test(` var abc = new Array(0, 1); var def = abc.length; abc[4294967296] = 10; // 2^32 => 0 abc[4294967297] = 11; // 2^32+1 => 1 [ def, abc.length, abc[0], abc[1], abc[4294967296] ]; `, "2,2,0,1,10") test(` abc = new Array(0, 1); def = abc.length; abc[4294967295] = 10; var ghi = abc.length; abc[4294967299] = 12; var jkl = abc.length; abc[4294967294] = 11; [ def, ghi, jkl, abc.length, abc[4294967295], abc[4294967299] ]; `, "2,2,2,4294967295,10,12") }) } func TestArray_map(t *testing.T) { tt(t, func() { test, _ := test() test(`raise: [].map("abc")`, "TypeError") test(`[].map(function() { return 1 }).length`, 0) test(`[1,2,3].map(function(value) { return value * value })`, "1,4,9") test(`[1,2,3].map(function(value) { return 1 })`, "1,1,1") test(` var abc = function(value, index, object) { return ('[object Math]' === Object.prototype.toString.call(object)); }; Math.length = 1; Math[0] = 1; Array.prototype.map.call(Math, abc)[0]; `, true) test(` var abc = function(value, index, object) { return this === Math; }; [11].map(abc, Math)[0]; `, true) }) } func TestArray_filter(t *testing.T) { tt(t, func() { test, _ := test() test(`raise: [].filter("abc")`, "TypeError") test(`[].filter(function() { return 1 }).length`, 0) test(`[1,2,3].filter(function() { return false }).length`, 0) test(`[1,2,3].filter(function() { return true })`, "1,2,3") }) } func TestArray_reduce(t *testing.T) { tt(t, func() { test, _ := test() test(`raise: [].reduce("abc")`, "TypeError") test(`raise: [].reduce(function() {})`, "TypeError") test(`[].reduce(function() {}, 0)`, 0) test(`[].reduce(function() {}, undefined)`, "undefined") test(`['a','b','c'].reduce(function(result, value) { return result+', '+value })`, "a, b, c") test(`[1,2,3].reduce(function(result, value) { return result + value }, 4)`, 10) test(`[1,2,3].reduce(function(result, value) { return result + value })`, 6) }) } func TestArray_reduceRight(t *testing.T) { tt(t, func() { test, _ := test() test(`raise: [].reduceRight("abc")`, "TypeError") test(`raise: [].reduceRight(function() {})`, "TypeError") test(`[].reduceRight(function() {}, 0)`, 0) test(`[].reduceRight(function() {}, undefined)`, "undefined") test(`['a','b','c'].reduceRight(function(result, value) { return result+', '+value })`, "c, b, a") test(`[1,2,3].reduceRight(function(result, value) { return result + value }, 4)`, 10) test(`[1,2,3].reduceRight(function(result, value) { return result + value })`, 6) }) } func TestArray_defineOwnProperty(t *testing.T) { tt(t, func() { test, _ := test() test(` var abc = []; Object.defineProperty(abc, "length", { writable: false }); abc.length; `, 0) test(`raise: var abc = []; var exception; Object.defineProperty(abc, "length", { writable: false }); Object.defineProperty(abc, "length", { writable: true }); `, "TypeError") }) } func TestArray_new(t *testing.T) { tt(t, func() { test, _ := test() test(` var abc = new Array(null); var def = new Array(undefined); [ abc.length, abc[0] === null, def.length, def[0] === undefined ] `, "1,true,1,true") test(` var abc = new Array(new Number(0)); var def = new Array(new Number(4294967295)); [ abc.length, typeof abc[0], abc[0] == 0, def.length, typeof def[0], def[0] == 4294967295 ] `, "1,object,true,1,object,true") }) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/ast/000077500000000000000000000000001331145703200231645ustar00rootroot00000000000000golang-github-robertkrimen-otto-0.0~git20180617.15f95af/ast/README.markdown000066400000000000000000000344311331145703200256720ustar00rootroot00000000000000# ast -- import "github.com/robertkrimen/otto/ast" Package ast declares types representing a JavaScript AST. ### Warning The parser and AST interfaces are still works-in-progress (particularly where node types are concerned) and may change in the future. ## Usage #### type ArrayLiteral ```go type ArrayLiteral struct { LeftBracket file.Idx RightBracket file.Idx Value []Expression } ``` #### func (*ArrayLiteral) Idx0 ```go func (self *ArrayLiteral) Idx0() file.Idx ``` #### func (*ArrayLiteral) Idx1 ```go func (self *ArrayLiteral) Idx1() file.Idx ``` #### type AssignExpression ```go type AssignExpression struct { Operator token.Token Left Expression Right Expression } ``` #### func (*AssignExpression) Idx0 ```go func (self *AssignExpression) Idx0() file.Idx ``` #### func (*AssignExpression) Idx1 ```go func (self *AssignExpression) Idx1() file.Idx ``` #### type BadExpression ```go type BadExpression struct { From file.Idx To file.Idx } ``` #### func (*BadExpression) Idx0 ```go func (self *BadExpression) Idx0() file.Idx ``` #### func (*BadExpression) Idx1 ```go func (self *BadExpression) Idx1() file.Idx ``` #### type BadStatement ```go type BadStatement struct { From file.Idx To file.Idx } ``` #### func (*BadStatement) Idx0 ```go func (self *BadStatement) Idx0() file.Idx ``` #### func (*BadStatement) Idx1 ```go func (self *BadStatement) Idx1() file.Idx ``` #### type BinaryExpression ```go type BinaryExpression struct { Operator token.Token Left Expression Right Expression Comparison bool } ``` #### func (*BinaryExpression) Idx0 ```go func (self *BinaryExpression) Idx0() file.Idx ``` #### func (*BinaryExpression) Idx1 ```go func (self *BinaryExpression) Idx1() file.Idx ``` #### type BlockStatement ```go type BlockStatement struct { LeftBrace file.Idx List []Statement RightBrace file.Idx } ``` #### func (*BlockStatement) Idx0 ```go func (self *BlockStatement) Idx0() file.Idx ``` #### func (*BlockStatement) Idx1 ```go func (self *BlockStatement) Idx1() file.Idx ``` #### type BooleanLiteral ```go type BooleanLiteral struct { Idx file.Idx Literal string Value bool } ``` #### func (*BooleanLiteral) Idx0 ```go func (self *BooleanLiteral) Idx0() file.Idx ``` #### func (*BooleanLiteral) Idx1 ```go func (self *BooleanLiteral) Idx1() file.Idx ``` #### type BracketExpression ```go type BracketExpression struct { Left Expression Member Expression LeftBracket file.Idx RightBracket file.Idx } ``` #### func (*BracketExpression) Idx0 ```go func (self *BracketExpression) Idx0() file.Idx ``` #### func (*BracketExpression) Idx1 ```go func (self *BracketExpression) Idx1() file.Idx ``` #### type BranchStatement ```go type BranchStatement struct { Idx file.Idx Token token.Token Label *Identifier } ``` #### func (*BranchStatement) Idx0 ```go func (self *BranchStatement) Idx0() file.Idx ``` #### func (*BranchStatement) Idx1 ```go func (self *BranchStatement) Idx1() file.Idx ``` #### type CallExpression ```go type CallExpression struct { Callee Expression LeftParenthesis file.Idx ArgumentList []Expression RightParenthesis file.Idx } ``` #### func (*CallExpression) Idx0 ```go func (self *CallExpression) Idx0() file.Idx ``` #### func (*CallExpression) Idx1 ```go func (self *CallExpression) Idx1() file.Idx ``` #### type CaseStatement ```go type CaseStatement struct { Case file.Idx Test Expression Consequent []Statement } ``` #### func (*CaseStatement) Idx0 ```go func (self *CaseStatement) Idx0() file.Idx ``` #### func (*CaseStatement) Idx1 ```go func (self *CaseStatement) Idx1() file.Idx ``` #### type CatchStatement ```go type CatchStatement struct { Catch file.Idx Parameter *Identifier Body Statement } ``` #### func (*CatchStatement) Idx0 ```go func (self *CatchStatement) Idx0() file.Idx ``` #### func (*CatchStatement) Idx1 ```go func (self *CatchStatement) Idx1() file.Idx ``` #### type ConditionalExpression ```go type ConditionalExpression struct { Test Expression Consequent Expression Alternate Expression } ``` #### func (*ConditionalExpression) Idx0 ```go func (self *ConditionalExpression) Idx0() file.Idx ``` #### func (*ConditionalExpression) Idx1 ```go func (self *ConditionalExpression) Idx1() file.Idx ``` #### type DebuggerStatement ```go type DebuggerStatement struct { Debugger file.Idx } ``` #### func (*DebuggerStatement) Idx0 ```go func (self *DebuggerStatement) Idx0() file.Idx ``` #### func (*DebuggerStatement) Idx1 ```go func (self *DebuggerStatement) Idx1() file.Idx ``` #### type Declaration ```go type Declaration interface { // contains filtered or unexported methods } ``` All declaration nodes implement the Declaration interface. #### type DoWhileStatement ```go type DoWhileStatement struct { Do file.Idx Test Expression Body Statement } ``` #### func (*DoWhileStatement) Idx0 ```go func (self *DoWhileStatement) Idx0() file.Idx ``` #### func (*DoWhileStatement) Idx1 ```go func (self *DoWhileStatement) Idx1() file.Idx ``` #### type DotExpression ```go type DotExpression struct { Left Expression Identifier Identifier } ``` #### func (*DotExpression) Idx0 ```go func (self *DotExpression) Idx0() file.Idx ``` #### func (*DotExpression) Idx1 ```go func (self *DotExpression) Idx1() file.Idx ``` #### type EmptyStatement ```go type EmptyStatement struct { Semicolon file.Idx } ``` #### func (*EmptyStatement) Idx0 ```go func (self *EmptyStatement) Idx0() file.Idx ``` #### func (*EmptyStatement) Idx1 ```go func (self *EmptyStatement) Idx1() file.Idx ``` #### type Expression ```go type Expression interface { Node // contains filtered or unexported methods } ``` All expression nodes implement the Expression interface. #### type ExpressionStatement ```go type ExpressionStatement struct { Expression Expression } ``` #### func (*ExpressionStatement) Idx0 ```go func (self *ExpressionStatement) Idx0() file.Idx ``` #### func (*ExpressionStatement) Idx1 ```go func (self *ExpressionStatement) Idx1() file.Idx ``` #### type ForInStatement ```go type ForInStatement struct { For file.Idx Into Expression Source Expression Body Statement } ``` #### func (*ForInStatement) Idx0 ```go func (self *ForInStatement) Idx0() file.Idx ``` #### func (*ForInStatement) Idx1 ```go func (self *ForInStatement) Idx1() file.Idx ``` #### type ForStatement ```go type ForStatement struct { For file.Idx Initializer Expression Update Expression Test Expression Body Statement } ``` #### func (*ForStatement) Idx0 ```go func (self *ForStatement) Idx0() file.Idx ``` #### func (*ForStatement) Idx1 ```go func (self *ForStatement) Idx1() file.Idx ``` #### type FunctionDeclaration ```go type FunctionDeclaration struct { Function *FunctionLiteral } ``` #### type FunctionLiteral ```go type FunctionLiteral struct { Function file.Idx Name *Identifier ParameterList *ParameterList Body Statement Source string DeclarationList []Declaration } ``` #### func (*FunctionLiteral) Idx0 ```go func (self *FunctionLiteral) Idx0() file.Idx ``` #### func (*FunctionLiteral) Idx1 ```go func (self *FunctionLiteral) Idx1() file.Idx ``` #### type Identifier ```go type Identifier struct { Name string Idx file.Idx } ``` #### func (*Identifier) Idx0 ```go func (self *Identifier) Idx0() file.Idx ``` #### func (*Identifier) Idx1 ```go func (self *Identifier) Idx1() file.Idx ``` #### type IfStatement ```go type IfStatement struct { If file.Idx Test Expression Consequent Statement Alternate Statement } ``` #### func (*IfStatement) Idx0 ```go func (self *IfStatement) Idx0() file.Idx ``` #### func (*IfStatement) Idx1 ```go func (self *IfStatement) Idx1() file.Idx ``` #### type LabelledStatement ```go type LabelledStatement struct { Label *Identifier Colon file.Idx Statement Statement } ``` #### func (*LabelledStatement) Idx0 ```go func (self *LabelledStatement) Idx0() file.Idx ``` #### func (*LabelledStatement) Idx1 ```go func (self *LabelledStatement) Idx1() file.Idx ``` #### type NewExpression ```go type NewExpression struct { New file.Idx Callee Expression LeftParenthesis file.Idx ArgumentList []Expression RightParenthesis file.Idx } ``` #### func (*NewExpression) Idx0 ```go func (self *NewExpression) Idx0() file.Idx ``` #### func (*NewExpression) Idx1 ```go func (self *NewExpression) Idx1() file.Idx ``` #### type Node ```go type Node interface { Idx0() file.Idx // The index of the first character belonging to the node Idx1() file.Idx // The index of the first character immediately after the node } ``` All nodes implement the Node interface. #### type NullLiteral ```go type NullLiteral struct { Idx file.Idx Literal string } ``` #### func (*NullLiteral) Idx0 ```go func (self *NullLiteral) Idx0() file.Idx ``` #### func (*NullLiteral) Idx1 ```go func (self *NullLiteral) Idx1() file.Idx ``` #### type NumberLiteral ```go type NumberLiteral struct { Idx file.Idx Literal string Value interface{} } ``` #### func (*NumberLiteral) Idx0 ```go func (self *NumberLiteral) Idx0() file.Idx ``` #### func (*NumberLiteral) Idx1 ```go func (self *NumberLiteral) Idx1() file.Idx ``` #### type ObjectLiteral ```go type ObjectLiteral struct { LeftBrace file.Idx RightBrace file.Idx Value []Property } ``` #### func (*ObjectLiteral) Idx0 ```go func (self *ObjectLiteral) Idx0() file.Idx ``` #### func (*ObjectLiteral) Idx1 ```go func (self *ObjectLiteral) Idx1() file.Idx ``` #### type ParameterList ```go type ParameterList struct { Opening file.Idx List []*Identifier Closing file.Idx } ``` #### type Program ```go type Program struct { Body []Statement DeclarationList []Declaration File *file.File } ``` #### func (*Program) Idx0 ```go func (self *Program) Idx0() file.Idx ``` #### func (*Program) Idx1 ```go func (self *Program) Idx1() file.Idx ``` #### type Property ```go type Property struct { Key string Kind string Value Expression } ``` #### type RegExpLiteral ```go type RegExpLiteral struct { Idx file.Idx Literal string Pattern string Flags string Value string } ``` #### func (*RegExpLiteral) Idx0 ```go func (self *RegExpLiteral) Idx0() file.Idx ``` #### func (*RegExpLiteral) Idx1 ```go func (self *RegExpLiteral) Idx1() file.Idx ``` #### type ReturnStatement ```go type ReturnStatement struct { Return file.Idx Argument Expression } ``` #### func (*ReturnStatement) Idx0 ```go func (self *ReturnStatement) Idx0() file.Idx ``` #### func (*ReturnStatement) Idx1 ```go func (self *ReturnStatement) Idx1() file.Idx ``` #### type SequenceExpression ```go type SequenceExpression struct { Sequence []Expression } ``` #### func (*SequenceExpression) Idx0 ```go func (self *SequenceExpression) Idx0() file.Idx ``` #### func (*SequenceExpression) Idx1 ```go func (self *SequenceExpression) Idx1() file.Idx ``` #### type Statement ```go type Statement interface { Node // contains filtered or unexported methods } ``` All statement nodes implement the Statement interface. #### type StringLiteral ```go type StringLiteral struct { Idx file.Idx Literal string Value string } ``` #### func (*StringLiteral) Idx0 ```go func (self *StringLiteral) Idx0() file.Idx ``` #### func (*StringLiteral) Idx1 ```go func (self *StringLiteral) Idx1() file.Idx ``` #### type SwitchStatement ```go type SwitchStatement struct { Switch file.Idx Discriminant Expression Default int Body []*CaseStatement } ``` #### func (*SwitchStatement) Idx0 ```go func (self *SwitchStatement) Idx0() file.Idx ``` #### func (*SwitchStatement) Idx1 ```go func (self *SwitchStatement) Idx1() file.Idx ``` #### type ThisExpression ```go type ThisExpression struct { Idx file.Idx } ``` #### func (*ThisExpression) Idx0 ```go func (self *ThisExpression) Idx0() file.Idx ``` #### func (*ThisExpression) Idx1 ```go func (self *ThisExpression) Idx1() file.Idx ``` #### type ThrowStatement ```go type ThrowStatement struct { Throw file.Idx Argument Expression } ``` #### func (*ThrowStatement) Idx0 ```go func (self *ThrowStatement) Idx0() file.Idx ``` #### func (*ThrowStatement) Idx1 ```go func (self *ThrowStatement) Idx1() file.Idx ``` #### type TryStatement ```go type TryStatement struct { Try file.Idx Body Statement Catch *CatchStatement Finally Statement } ``` #### func (*TryStatement) Idx0 ```go func (self *TryStatement) Idx0() file.Idx ``` #### func (*TryStatement) Idx1 ```go func (self *TryStatement) Idx1() file.Idx ``` #### type UnaryExpression ```go type UnaryExpression struct { Operator token.Token Idx file.Idx // If a prefix operation Operand Expression Postfix bool } ``` #### func (*UnaryExpression) Idx0 ```go func (self *UnaryExpression) Idx0() file.Idx ``` #### func (*UnaryExpression) Idx1 ```go func (self *UnaryExpression) Idx1() file.Idx ``` #### type VariableDeclaration ```go type VariableDeclaration struct { Var file.Idx List []*VariableExpression } ``` #### type VariableExpression ```go type VariableExpression struct { Name string Idx file.Idx Initializer Expression } ``` #### func (*VariableExpression) Idx0 ```go func (self *VariableExpression) Idx0() file.Idx ``` #### func (*VariableExpression) Idx1 ```go func (self *VariableExpression) Idx1() file.Idx ``` #### type VariableStatement ```go type VariableStatement struct { Var file.Idx List []Expression } ``` #### func (*VariableStatement) Idx0 ```go func (self *VariableStatement) Idx0() file.Idx ``` #### func (*VariableStatement) Idx1 ```go func (self *VariableStatement) Idx1() file.Idx ``` #### type WhileStatement ```go type WhileStatement struct { While file.Idx Test Expression Body Statement } ``` #### func (*WhileStatement) Idx0 ```go func (self *WhileStatement) Idx0() file.Idx ``` #### func (*WhileStatement) Idx1 ```go func (self *WhileStatement) Idx1() file.Idx ``` #### type WithStatement ```go type WithStatement struct { With file.Idx Object Expression Body Statement } ``` #### func (*WithStatement) Idx0 ```go func (self *WithStatement) Idx0() file.Idx ``` #### func (*WithStatement) Idx1 ```go func (self *WithStatement) Idx1() file.Idx ``` -- **godocdown** http://github.com/robertkrimen/godocdown golang-github-robertkrimen-otto-0.0~git20180617.15f95af/ast/comments.go000066400000000000000000000161711331145703200253460ustar00rootroot00000000000000package ast import ( "fmt" "github.com/robertkrimen/otto/file" ) // CommentPosition determines where the comment is in a given context type CommentPosition int const ( _ CommentPosition = iota LEADING // Before the pertinent expression TRAILING // After the pertinent expression KEY // Before a key in an object COLON // After a colon in a field declaration FINAL // Final comments in a block, not belonging to a specific expression or the comment after a trailing , in an array or object literal IF // After an if keyword WHILE // After a while keyword DO // After do keyword FOR // After a for keyword WITH // After a with keyword TBD ) // Comment contains the data of the comment type Comment struct { Begin file.Idx Text string Position CommentPosition } // NewComment creates a new comment func NewComment(text string, idx file.Idx) *Comment { comment := &Comment{ Begin: idx, Text: text, Position: TBD, } return comment } // String returns a stringified version of the position func (cp CommentPosition) String() string { switch cp { case LEADING: return "Leading" case TRAILING: return "Trailing" case KEY: return "Key" case COLON: return "Colon" case FINAL: return "Final" case IF: return "If" case WHILE: return "While" case DO: return "Do" case FOR: return "For" case WITH: return "With" default: return "???" } } // String returns a stringified version of the comment func (c Comment) String() string { return fmt.Sprintf("Comment: %v", c.Text) } // Comments defines the current view of comments from the parser type Comments struct { // CommentMap is a reference to the parser comment map CommentMap CommentMap // Comments lists the comments scanned, not linked to a node yet Comments []*Comment // future lists the comments after a line break during a sequence of comments future []*Comment // Current is node for which comments are linked to Current Expression // wasLineBreak determines if a line break occured while scanning for comments wasLineBreak bool // primary determines whether or not processing a primary expression primary bool // afterBlock determines whether or not being after a block statement afterBlock bool } func NewComments() *Comments { comments := &Comments{ CommentMap: CommentMap{}, } return comments } func (c *Comments) String() string { return fmt.Sprintf("NODE: %v, Comments: %v, Future: %v(LINEBREAK:%v)", c.Current, len(c.Comments), len(c.future), c.wasLineBreak) } // FetchAll returns all the currently scanned comments, // including those from the next line func (c *Comments) FetchAll() []*Comment { defer func() { c.Comments = nil c.future = nil }() return append(c.Comments, c.future...) } // Fetch returns all the currently scanned comments func (c *Comments) Fetch() []*Comment { defer func() { c.Comments = nil }() return c.Comments } // ResetLineBreak marks the beginning of a new statement func (c *Comments) ResetLineBreak() { c.wasLineBreak = false } // MarkPrimary will mark the context as processing a primary expression func (c *Comments) MarkPrimary() { c.primary = true c.wasLineBreak = false } // AfterBlock will mark the context as being after a block. func (c *Comments) AfterBlock() { c.afterBlock = true } // AddComment adds a comment to the view. // Depending on the context, comments are added normally or as post line break. func (c *Comments) AddComment(comment *Comment) { if c.primary { if !c.wasLineBreak { c.Comments = append(c.Comments, comment) } else { c.future = append(c.future, comment) } } else { if !c.wasLineBreak || (c.Current == nil && !c.afterBlock) { c.Comments = append(c.Comments, comment) } else { c.future = append(c.future, comment) } } } // MarkComments will mark the found comments as the given position. func (c *Comments) MarkComments(position CommentPosition) { for _, comment := range c.Comments { if comment.Position == TBD { comment.Position = position } } for _, c := range c.future { if c.Position == TBD { c.Position = position } } } // Unset the current node and apply the comments to the current expression. // Resets context variables. func (c *Comments) Unset() { if c.Current != nil { c.applyComments(c.Current, c.Current, TRAILING) c.Current = nil } c.wasLineBreak = false c.primary = false c.afterBlock = false } // SetExpression sets the current expression. // It is applied the found comments, unless the previous expression has not been unset. // It is skipped if the node is already set or if it is a part of the previous node. func (c *Comments) SetExpression(node Expression) { // Skipping same node if c.Current == node { return } if c.Current != nil && c.Current.Idx1() == node.Idx1() { c.Current = node return } previous := c.Current c.Current = node // Apply the found comments and futures to the node and the previous. c.applyComments(node, previous, TRAILING) } // PostProcessNode applies all found comments to the given node func (c *Comments) PostProcessNode(node Node) { c.applyComments(node, nil, TRAILING) } // applyComments applies both the comments and the future comments to the given node and the previous one, // based on the context. func (c *Comments) applyComments(node, previous Node, position CommentPosition) { if previous != nil { c.CommentMap.AddComments(previous, c.Comments, position) c.Comments = nil } else { c.CommentMap.AddComments(node, c.Comments, position) c.Comments = nil } // Only apply the future comments to the node if the previous is set. // This is for detecting end of line comments and which node comments on the following lines belongs to if previous != nil { c.CommentMap.AddComments(node, c.future, position) c.future = nil } } // AtLineBreak will mark a line break func (c *Comments) AtLineBreak() { c.wasLineBreak = true } // CommentMap is the data structure where all found comments are stored type CommentMap map[Node][]*Comment // AddComment adds a single comment to the map func (cm CommentMap) AddComment(node Node, comment *Comment) { list := cm[node] list = append(list, comment) cm[node] = list } // AddComments adds a slice of comments, given a node and an updated position func (cm CommentMap) AddComments(node Node, comments []*Comment, position CommentPosition) { for _, comment := range comments { if comment.Position == TBD { comment.Position = position } cm.AddComment(node, comment) } } // Size returns the size of the map func (cm CommentMap) Size() int { size := 0 for _, comments := range cm { size += len(comments) } return size } // MoveComments moves comments with a given position from a node to another func (cm CommentMap) MoveComments(from, to Node, position CommentPosition) { for i, c := range cm[from] { if c.Position == position { cm.AddComment(to, c) // Remove the comment from the "from" slice cm[from][i] = cm[from][len(cm[from])-1] cm[from][len(cm[from])-1] = nil cm[from] = cm[from][:len(cm[from])-1] } } } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/ast/comments_test.go000066400000000000000000000025411331145703200264010ustar00rootroot00000000000000package ast import ( "github.com/robertkrimen/otto/file" "testing" ) func TestCommentMap(t *testing.T) { statement := &EmptyStatement{file.Idx(1)} comment := &Comment{1, "test", LEADING} cm := CommentMap{} cm.AddComment(statement, comment) if cm.Size() != 1 { t.Errorf("the number of comments is %v, not 1", cm.Size()) } if len(cm[statement]) != 1 { t.Errorf("the number of comments is %v, not 1", cm.Size()) } if cm[statement][0].Text != "test" { t.Errorf("the text is %v, not \"test\"", cm[statement][0].Text) } } func TestCommentMap_move(t *testing.T) { statement1 := &EmptyStatement{file.Idx(1)} statement2 := &EmptyStatement{file.Idx(2)} comment := &Comment{1, "test", LEADING} cm := CommentMap{} cm.AddComment(statement1, comment) if cm.Size() != 1 { t.Errorf("the number of comments is %v, not 1", cm.Size()) } if len(cm[statement1]) != 1 { t.Errorf("the number of comments is %v, not 1", cm.Size()) } if len(cm[statement2]) != 0 { t.Errorf("the number of comments is %v, not 0", cm.Size()) } cm.MoveComments(statement1, statement2, LEADING) if cm.Size() != 1 { t.Errorf("the number of comments is %v, not 1", cm.Size()) } if len(cm[statement2]) != 1 { t.Errorf("the number of comments is %v, not 1", cm.Size()) } if len(cm[statement1]) != 0 { t.Errorf("the number of comments is %v, not 0", cm.Size()) } } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/ast/node.go000066400000000000000000000352361331145703200244510ustar00rootroot00000000000000/* Package ast declares types representing a JavaScript AST. Warning The parser and AST interfaces are still works-in-progress (particularly where node types are concerned) and may change in the future. */ package ast import ( "github.com/robertkrimen/otto/file" "github.com/robertkrimen/otto/token" ) // All nodes implement the Node interface. type Node interface { Idx0() file.Idx // The index of the first character belonging to the node Idx1() file.Idx // The index of the first character immediately after the node } // ========== // // Expression // // ========== // type ( // All expression nodes implement the Expression interface. Expression interface { Node _expressionNode() } ArrayLiteral struct { LeftBracket file.Idx RightBracket file.Idx Value []Expression } AssignExpression struct { Operator token.Token Left Expression Right Expression } BadExpression struct { From file.Idx To file.Idx } BinaryExpression struct { Operator token.Token Left Expression Right Expression Comparison bool } BooleanLiteral struct { Idx file.Idx Literal string Value bool } BracketExpression struct { Left Expression Member Expression LeftBracket file.Idx RightBracket file.Idx } CallExpression struct { Callee Expression LeftParenthesis file.Idx ArgumentList []Expression RightParenthesis file.Idx } ConditionalExpression struct { Test Expression Consequent Expression Alternate Expression } DotExpression struct { Left Expression Identifier *Identifier } EmptyExpression struct { Begin file.Idx End file.Idx } FunctionLiteral struct { Function file.Idx Name *Identifier ParameterList *ParameterList Body Statement Source string DeclarationList []Declaration } Identifier struct { Name string Idx file.Idx } NewExpression struct { New file.Idx Callee Expression LeftParenthesis file.Idx ArgumentList []Expression RightParenthesis file.Idx } NullLiteral struct { Idx file.Idx Literal string } NumberLiteral struct { Idx file.Idx Literal string Value interface{} } ObjectLiteral struct { LeftBrace file.Idx RightBrace file.Idx Value []Property } ParameterList struct { Opening file.Idx List []*Identifier Closing file.Idx } Property struct { Key string Kind string Value Expression } RegExpLiteral struct { Idx file.Idx Literal string Pattern string Flags string Value string } SequenceExpression struct { Sequence []Expression } StringLiteral struct { Idx file.Idx Literal string Value string } ThisExpression struct { Idx file.Idx } UnaryExpression struct { Operator token.Token Idx file.Idx // If a prefix operation Operand Expression Postfix bool } VariableExpression struct { Name string Idx file.Idx Initializer Expression } ) // _expressionNode func (*ArrayLiteral) _expressionNode() {} func (*AssignExpression) _expressionNode() {} func (*BadExpression) _expressionNode() {} func (*BinaryExpression) _expressionNode() {} func (*BooleanLiteral) _expressionNode() {} func (*BracketExpression) _expressionNode() {} func (*CallExpression) _expressionNode() {} func (*ConditionalExpression) _expressionNode() {} func (*DotExpression) _expressionNode() {} func (*EmptyExpression) _expressionNode() {} func (*FunctionLiteral) _expressionNode() {} func (*Identifier) _expressionNode() {} func (*NewExpression) _expressionNode() {} func (*NullLiteral) _expressionNode() {} func (*NumberLiteral) _expressionNode() {} func (*ObjectLiteral) _expressionNode() {} func (*RegExpLiteral) _expressionNode() {} func (*SequenceExpression) _expressionNode() {} func (*StringLiteral) _expressionNode() {} func (*ThisExpression) _expressionNode() {} func (*UnaryExpression) _expressionNode() {} func (*VariableExpression) _expressionNode() {} // ========= // // Statement // // ========= // type ( // All statement nodes implement the Statement interface. Statement interface { Node _statementNode() } BadStatement struct { From file.Idx To file.Idx } BlockStatement struct { LeftBrace file.Idx List []Statement RightBrace file.Idx } BranchStatement struct { Idx file.Idx Token token.Token Label *Identifier } CaseStatement struct { Case file.Idx Test Expression Consequent []Statement } CatchStatement struct { Catch file.Idx Parameter *Identifier Body Statement } DebuggerStatement struct { Debugger file.Idx } DoWhileStatement struct { Do file.Idx Test Expression Body Statement } EmptyStatement struct { Semicolon file.Idx } ExpressionStatement struct { Expression Expression } ForInStatement struct { For file.Idx Into Expression Source Expression Body Statement } ForStatement struct { For file.Idx Initializer Expression Update Expression Test Expression Body Statement } FunctionStatement struct { Function *FunctionLiteral } IfStatement struct { If file.Idx Test Expression Consequent Statement Alternate Statement } LabelledStatement struct { Label *Identifier Colon file.Idx Statement Statement } ReturnStatement struct { Return file.Idx Argument Expression } SwitchStatement struct { Switch file.Idx Discriminant Expression Default int Body []*CaseStatement } ThrowStatement struct { Throw file.Idx Argument Expression } TryStatement struct { Try file.Idx Body Statement Catch *CatchStatement Finally Statement } VariableStatement struct { Var file.Idx List []Expression } WhileStatement struct { While file.Idx Test Expression Body Statement } WithStatement struct { With file.Idx Object Expression Body Statement } ) // _statementNode func (*BadStatement) _statementNode() {} func (*BlockStatement) _statementNode() {} func (*BranchStatement) _statementNode() {} func (*CaseStatement) _statementNode() {} func (*CatchStatement) _statementNode() {} func (*DebuggerStatement) _statementNode() {} func (*DoWhileStatement) _statementNode() {} func (*EmptyStatement) _statementNode() {} func (*ExpressionStatement) _statementNode() {} func (*ForInStatement) _statementNode() {} func (*ForStatement) _statementNode() {} func (*FunctionStatement) _statementNode() {} func (*IfStatement) _statementNode() {} func (*LabelledStatement) _statementNode() {} func (*ReturnStatement) _statementNode() {} func (*SwitchStatement) _statementNode() {} func (*ThrowStatement) _statementNode() {} func (*TryStatement) _statementNode() {} func (*VariableStatement) _statementNode() {} func (*WhileStatement) _statementNode() {} func (*WithStatement) _statementNode() {} // =========== // // Declaration // // =========== // type ( // All declaration nodes implement the Declaration interface. Declaration interface { _declarationNode() } FunctionDeclaration struct { Function *FunctionLiteral } VariableDeclaration struct { Var file.Idx List []*VariableExpression } ) // _declarationNode func (*FunctionDeclaration) _declarationNode() {} func (*VariableDeclaration) _declarationNode() {} // ==== // // Node // // ==== // type Program struct { Body []Statement DeclarationList []Declaration File *file.File Comments CommentMap } // ==== // // Idx0 // // ==== // func (self *ArrayLiteral) Idx0() file.Idx { return self.LeftBracket } func (self *AssignExpression) Idx0() file.Idx { return self.Left.Idx0() } func (self *BadExpression) Idx0() file.Idx { return self.From } func (self *BinaryExpression) Idx0() file.Idx { return self.Left.Idx0() } func (self *BooleanLiteral) Idx0() file.Idx { return self.Idx } func (self *BracketExpression) Idx0() file.Idx { return self.Left.Idx0() } func (self *CallExpression) Idx0() file.Idx { return self.Callee.Idx0() } func (self *ConditionalExpression) Idx0() file.Idx { return self.Test.Idx0() } func (self *DotExpression) Idx0() file.Idx { return self.Left.Idx0() } func (self *EmptyExpression) Idx0() file.Idx { return self.Begin } func (self *FunctionLiteral) Idx0() file.Idx { return self.Function } func (self *Identifier) Idx0() file.Idx { return self.Idx } func (self *NewExpression) Idx0() file.Idx { return self.New } func (self *NullLiteral) Idx0() file.Idx { return self.Idx } func (self *NumberLiteral) Idx0() file.Idx { return self.Idx } func (self *ObjectLiteral) Idx0() file.Idx { return self.LeftBrace } func (self *RegExpLiteral) Idx0() file.Idx { return self.Idx } func (self *SequenceExpression) Idx0() file.Idx { return self.Sequence[0].Idx0() } func (self *StringLiteral) Idx0() file.Idx { return self.Idx } func (self *ThisExpression) Idx0() file.Idx { return self.Idx } func (self *UnaryExpression) Idx0() file.Idx { return self.Idx } func (self *VariableExpression) Idx0() file.Idx { return self.Idx } func (self *BadStatement) Idx0() file.Idx { return self.From } func (self *BlockStatement) Idx0() file.Idx { return self.LeftBrace } func (self *BranchStatement) Idx0() file.Idx { return self.Idx } func (self *CaseStatement) Idx0() file.Idx { return self.Case } func (self *CatchStatement) Idx0() file.Idx { return self.Catch } func (self *DebuggerStatement) Idx0() file.Idx { return self.Debugger } func (self *DoWhileStatement) Idx0() file.Idx { return self.Do } func (self *EmptyStatement) Idx0() file.Idx { return self.Semicolon } func (self *ExpressionStatement) Idx0() file.Idx { return self.Expression.Idx0() } func (self *ForInStatement) Idx0() file.Idx { return self.For } func (self *ForStatement) Idx0() file.Idx { return self.For } func (self *FunctionStatement) Idx0() file.Idx { return self.Function.Idx0() } func (self *IfStatement) Idx0() file.Idx { return self.If } func (self *LabelledStatement) Idx0() file.Idx { return self.Label.Idx0() } func (self *Program) Idx0() file.Idx { return self.Body[0].Idx0() } func (self *ReturnStatement) Idx0() file.Idx { return self.Return } func (self *SwitchStatement) Idx0() file.Idx { return self.Switch } func (self *ThrowStatement) Idx0() file.Idx { return self.Throw } func (self *TryStatement) Idx0() file.Idx { return self.Try } func (self *VariableStatement) Idx0() file.Idx { return self.Var } func (self *WhileStatement) Idx0() file.Idx { return self.While } func (self *WithStatement) Idx0() file.Idx { return self.With } // ==== // // Idx1 // // ==== // func (self *ArrayLiteral) Idx1() file.Idx { return self.RightBracket } func (self *AssignExpression) Idx1() file.Idx { return self.Right.Idx1() } func (self *BadExpression) Idx1() file.Idx { return self.To } func (self *BinaryExpression) Idx1() file.Idx { return self.Right.Idx1() } func (self *BooleanLiteral) Idx1() file.Idx { return file.Idx(int(self.Idx) + len(self.Literal)) } func (self *BracketExpression) Idx1() file.Idx { return self.RightBracket + 1 } func (self *CallExpression) Idx1() file.Idx { return self.RightParenthesis + 1 } func (self *ConditionalExpression) Idx1() file.Idx { return self.Test.Idx1() } func (self *DotExpression) Idx1() file.Idx { return self.Identifier.Idx1() } func (self *EmptyExpression) Idx1() file.Idx { return self.End } func (self *FunctionLiteral) Idx1() file.Idx { return self.Body.Idx1() } func (self *Identifier) Idx1() file.Idx { return file.Idx(int(self.Idx) + len(self.Name)) } func (self *NewExpression) Idx1() file.Idx { return self.RightParenthesis + 1 } func (self *NullLiteral) Idx1() file.Idx { return file.Idx(int(self.Idx) + 4) } // "null" func (self *NumberLiteral) Idx1() file.Idx { return file.Idx(int(self.Idx) + len(self.Literal)) } func (self *ObjectLiteral) Idx1() file.Idx { return self.RightBrace } func (self *RegExpLiteral) Idx1() file.Idx { return file.Idx(int(self.Idx) + len(self.Literal)) } func (self *SequenceExpression) Idx1() file.Idx { return self.Sequence[0].Idx1() } func (self *StringLiteral) Idx1() file.Idx { return file.Idx(int(self.Idx) + len(self.Literal)) } func (self *ThisExpression) Idx1() file.Idx { return self.Idx + 4 } func (self *UnaryExpression) Idx1() file.Idx { if self.Postfix { return self.Operand.Idx1() + 2 // ++ -- } return self.Operand.Idx1() } func (self *VariableExpression) Idx1() file.Idx { if self.Initializer == nil { return file.Idx(int(self.Idx) + len(self.Name) + 1) } return self.Initializer.Idx1() } func (self *BadStatement) Idx1() file.Idx { return self.To } func (self *BlockStatement) Idx1() file.Idx { return self.RightBrace + 1 } func (self *BranchStatement) Idx1() file.Idx { return self.Idx } func (self *CaseStatement) Idx1() file.Idx { return self.Consequent[len(self.Consequent)-1].Idx1() } func (self *CatchStatement) Idx1() file.Idx { return self.Body.Idx1() } func (self *DebuggerStatement) Idx1() file.Idx { return self.Debugger + 8 } func (self *DoWhileStatement) Idx1() file.Idx { return self.Test.Idx1() } func (self *EmptyStatement) Idx1() file.Idx { return self.Semicolon + 1 } func (self *ExpressionStatement) Idx1() file.Idx { return self.Expression.Idx1() } func (self *ForInStatement) Idx1() file.Idx { return self.Body.Idx1() } func (self *ForStatement) Idx1() file.Idx { return self.Body.Idx1() } func (self *FunctionStatement) Idx1() file.Idx { return self.Function.Idx1() } func (self *IfStatement) Idx1() file.Idx { if self.Alternate != nil { return self.Alternate.Idx1() } return self.Consequent.Idx1() } func (self *LabelledStatement) Idx1() file.Idx { return self.Colon + 1 } func (self *Program) Idx1() file.Idx { return self.Body[len(self.Body)-1].Idx1() } func (self *ReturnStatement) Idx1() file.Idx { return self.Return } func (self *SwitchStatement) Idx1() file.Idx { return self.Body[len(self.Body)-1].Idx1() } func (self *ThrowStatement) Idx1() file.Idx { return self.Throw } func (self *TryStatement) Idx1() file.Idx { return self.Try } func (self *VariableStatement) Idx1() file.Idx { return self.List[len(self.List)-1].Idx1() } func (self *WhileStatement) Idx1() file.Idx { return self.Body.Idx1() } func (self *WithStatement) Idx1() file.Idx { return self.Body.Idx1() } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/ast/walk.go000066400000000000000000000076521331145703200244630ustar00rootroot00000000000000package ast import "fmt" // Visitor Enter method is invoked for each node encountered by Walk. // If the result visitor w is not nil, Walk visits each of the children // of node with the visitor v, followed by a call of the Exit method. type Visitor interface { Enter(n Node) (v Visitor) Exit(n Node) } // Walk traverses an AST in depth-first order: It starts by calling // v.Enter(node); node must not be nil. If the visitor v returned by // v.Enter(node) is not nil, Walk is invoked recursively with visitor // v for each of the non-nil children of node, followed by a call // of v.Exit(node). func Walk(v Visitor, n Node) { if n == nil { return } if v = v.Enter(n); v == nil { return } defer v.Exit(n) switch n := n.(type) { case *ArrayLiteral: if n != nil { for _, ex := range n.Value { Walk(v, ex) } } case *AssignExpression: if n != nil { Walk(v, n.Left) Walk(v, n.Right) } case *BadExpression: case *BinaryExpression: if n != nil { Walk(v, n.Left) Walk(v, n.Right) } case *BlockStatement: if n != nil { for _, s := range n.List { Walk(v, s) } } case *BooleanLiteral: case *BracketExpression: if n != nil { Walk(v, n.Left) Walk(v, n.Member) } case *BranchStatement: if n != nil { Walk(v, n.Label) } case *CallExpression: if n != nil { Walk(v, n.Callee) for _, a := range n.ArgumentList { Walk(v, a) } } case *CaseStatement: if n != nil { Walk(v, n.Test) for _, c := range n.Consequent { Walk(v, c) } } case *CatchStatement: if n != nil { Walk(v, n.Parameter) Walk(v, n.Body) } case *ConditionalExpression: if n != nil { Walk(v, n.Test) Walk(v, n.Consequent) Walk(v, n.Alternate) } case *DebuggerStatement: case *DoWhileStatement: if n != nil { Walk(v, n.Test) Walk(v, n.Body) } case *DotExpression: if n != nil { Walk(v, n.Left) } case *EmptyExpression: case *EmptyStatement: case *ExpressionStatement: if n != nil { Walk(v, n.Expression) } case *ForInStatement: if n != nil { Walk(v, n.Into) Walk(v, n.Source) Walk(v, n.Body) } case *ForStatement: if n != nil { Walk(v, n.Initializer) Walk(v, n.Update) Walk(v, n.Test) Walk(v, n.Body) } case *FunctionLiteral: if n != nil { Walk(v, n.Name) for _, p := range n.ParameterList.List { Walk(v, p) } Walk(v, n.Body) } case *FunctionStatement: if n != nil { Walk(v, n.Function) } case *Identifier: case *IfStatement: if n != nil { Walk(v, n.Test) Walk(v, n.Consequent) Walk(v, n.Alternate) } case *LabelledStatement: if n != nil { Walk(v, n.Statement) } case *NewExpression: if n != nil { Walk(v, n.Callee) for _, a := range n.ArgumentList { Walk(v, a) } } case *NullLiteral: case *NumberLiteral: case *ObjectLiteral: if n != nil { for _, p := range n.Value { Walk(v, p.Value) } } case *Program: if n != nil { for _, b := range n.Body { Walk(v, b) } } case *RegExpLiteral: case *ReturnStatement: if n != nil { Walk(v, n.Argument) } case *SequenceExpression: if n != nil { for _, e := range n.Sequence { Walk(v, e) } } case *StringLiteral: case *SwitchStatement: if n != nil { Walk(v, n.Discriminant) for _, c := range n.Body { Walk(v, c) } } case *ThisExpression: case *ThrowStatement: if n != nil { Walk(v, n.Argument) } case *TryStatement: if n != nil { Walk(v, n.Body) Walk(v, n.Catch) Walk(v, n.Finally) } case *UnaryExpression: if n != nil { Walk(v, n.Operand) } case *VariableExpression: if n != nil { Walk(v, n.Initializer) } case *VariableStatement: if n != nil { for _, e := range n.List { Walk(v, e) } } case *WhileStatement: if n != nil { Walk(v, n.Test) Walk(v, n.Body) } case *WithStatement: if n != nil { Walk(v, n.Object) Walk(v, n.Body) } default: panic(fmt.Sprintf("Walk: unexpected node type %T", n)) } } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/ast/walk_example_test.go000066400000000000000000000023261331145703200272260ustar00rootroot00000000000000package ast_test import ( "fmt" "log" "github.com/robertkrimen/otto/ast" "github.com/robertkrimen/otto/file" "github.com/robertkrimen/otto/parser" ) type walkExample struct { source string shift file.Idx } func (w *walkExample) Enter(n ast.Node) ast.Visitor { if id, ok := n.(*ast.Identifier); ok && id != nil { idx := n.Idx0() + w.shift - 1 s := w.source[:idx] + "new_" + w.source[idx:] w.source = s w.shift += 4 } if v, ok := n.(*ast.VariableExpression); ok && v != nil { idx := n.Idx0() + w.shift - 1 s := w.source[:idx] + "varnew_" + w.source[idx:] w.source = s w.shift += 7 } return w } func (w *walkExample) Exit(n ast.Node) { // AST node n has had all its children walked. Pop it out of your // stack, or do whatever processing you need to do, if any. } func ExampleVisitor_codeRewrite() { source := `var b = function() {test(); try {} catch(e) {} var test = "test(); var test = 1"} // test` program, err := parser.ParseFile(nil, "", source, 0) if err != nil { log.Fatal(err) } w := &walkExample{source: source} ast.Walk(w, program) fmt.Println(w.source) // Output: var varnew_b = function() {new_test(); try {} catch(new_e) {} var varnew_test = "test(); var test = 1"} // test } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/ast/walk_test.go000066400000000000000000000032231331145703200255100ustar00rootroot00000000000000package ast_test import ( "log" "testing" "github.com/robertkrimen/otto/ast" "github.com/robertkrimen/otto/file" "github.com/robertkrimen/otto/parser" ) type walker struct { stack []ast.Node source string shift file.Idx } // push and pop below are to prove the symmetry of Enter/Exit calls func (w *walker) push(n ast.Node) { w.stack = append(w.stack, n) } func (w *walker) pop(n ast.Node) { size := len(w.stack) if size <= 0 { panic("pop of empty stack") } toPop := w.stack[size-1] if toPop != n { panic("pop: nodes do not equal") } w.stack[size-1] = nil w.stack = w.stack[:size-1] } func (w *walker) Enter(n ast.Node) ast.Visitor { w.push(n) if id, ok := n.(*ast.Identifier); ok && id != nil { idx := n.Idx0() + w.shift - 1 s := w.source[:idx] + "new_" + w.source[idx:] w.source = s w.shift += 4 } if v, ok := n.(*ast.VariableExpression); ok && v != nil { idx := n.Idx0() + w.shift - 1 s := w.source[:idx] + "varnew_" + w.source[idx:] w.source = s w.shift += 7 } return w } func (w *walker) Exit(n ast.Node) { w.pop(n) } func TestVisitorRewrite(t *testing.T) { source := `var b = function() {test(); try {} catch(e) {} var test = "test(); var test = 1"} // test` program, err := parser.ParseFile(nil, "", source, 0) if err != nil { log.Fatal(err) } w := &walker{source: source} ast.Walk(w, program) xformed := `var varnew_b = function() {new_test(); try {} catch(new_e) {} var varnew_test = "test(); var test = 1"} // test` if w.source != xformed { t.Errorf("source is `%s` not `%s`", w.source, xformed) } if len(w.stack) != 0 { t.Errorf("stack should be empty, but is length: %d", len(w.stack)) } } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/bug_test.go000066400000000000000000000436771331145703200245610ustar00rootroot00000000000000package otto import ( "testing" "time" ) func Test_issue116(t *testing.T) { tt(t, func() { test, _ := test() test(` [1,-1].sort(function(a, b) { return a - b; }); `, "-1,1") }) } func Test_262(t *testing.T) { tt(t, func() { test, _ := test() // 11.13.1-1-1 test(`raise: eval("42 = 42;"); `, "ReferenceError: Invalid left-hand side in assignment") }) } func Test_issue5(t *testing.T) { tt(t, func() { test, _ := test() test(`'abc' === 'def'`, false) test(`'\t' === '\r'`, false) }) } func Test_issue13(t *testing.T) { tt(t, func() { test, tester := test() vm := tester.vm value, err := vm.ToValue(map[string]interface{}{ "string": "Xyzzy", "number": 42, "array": []string{"def", "ghi"}, }) if err != nil { t.Error(err) t.FailNow() } fn, err := vm.Object(` (function(value){ return ""+[value.string, value.number, value.array] }) `) if err != nil { t.Error(err) t.FailNow() } result, err := fn.Value().Call(fn.Value(), value) if err != nil { t.Error(err) t.FailNow() } is(result.string(), "Xyzzy,42,def,ghi") anything := struct { Abc interface{} }{ Abc: map[string]interface{}{ "def": []interface{}{ []interface{}{ "a", "b", "c", "", "d", "e", }, map[string]interface{}{ "jkl": "Nothing happens.", }, }, "ghi": -1, }, } vm.Set("anything", anything) test(` [ anything, "~", anything.Abc, "~", anything.Abc.def, "~", anything.Abc.def[1].jkl, "~", anything.Abc.ghi, ]; `, "[object Object],~,[object Object],~,a,b,c,,d,e,[object Object],~,Nothing happens.,~,-1") }) } func Test_issue16(t *testing.T) { tt(t, func() { test, vm := test() test(` var def = { "abc": ["abc"], "xyz": ["xyz"] }; def.abc.concat(def.xyz); `, "abc,xyz") vm.Set("ghi", []string{"jkl", "mno"}) test(` def.abc.concat(def.xyz).concat(ghi); `, "abc,xyz,jkl,mno") test(` ghi.concat(def.abc.concat(def.xyz)); `, "jkl,mno,abc,xyz") vm.Set("pqr", []interface{}{"jkl", 42, 3.14159, true}) test(` pqr.concat(ghi, def.abc, def, def.xyz); `, "jkl,42,3.14159,true,jkl,mno,abc,[object Object],xyz") test(` pqr.concat(ghi, def.abc, def, def.xyz).length; `, 9) }) } func Test_issue21(t *testing.T) { tt(t, func() { vm1 := New() vm1.Run(` abc = {} abc.ghi = "Nothing happens."; var jkl = 0; abc.def = function() { jkl += 1; return 1; } `) abc, err := vm1.Get("abc") is(err, nil) vm2 := New() vm2.Set("cba", abc) _, err = vm2.Run(` var pqr = 0; cba.mno = function() { pqr -= 1; return 1; } cba.def(); cba.def(); cba.def(); `) is(err, nil) jkl, err := vm1.Get("jkl") is(err, nil) is(jkl, 3) _, err = vm1.Run(` abc.mno(); abc.mno(); abc.mno(); `) is(err, nil) pqr, err := vm2.Get("pqr") is(err, nil) is(pqr, -3) }) } func Test_issue24(t *testing.T) { tt(t, func() { _, vm := test() { vm.Set("abc", []string{"abc", "def", "ghi"}) value, err := vm.Get("abc") is(err, nil) export, _ := value.Export() { value, valid := export.([]string) is(valid, true) is(value[0], "abc") is(value[2], "ghi") } } { vm.Set("abc", [...]string{"abc", "def", "ghi"}) value, err := vm.Get("abc") is(err, nil) export, _ := value.Export() { value, valid := export.([3]string) is(valid, true) is(value[0], "abc") is(value[2], "ghi") } } { vm.Set("abc", &[...]string{"abc", "def", "ghi"}) value, err := vm.Get("abc") is(err, nil) export, _ := value.Export() { value, valid := export.(*[3]string) is(valid, true) is(value[0], "abc") is(value[2], "ghi") } } { vm.Set("abc", map[int]string{0: "abc", 1: "def", 2: "ghi"}) value, err := vm.Get("abc") is(err, nil) export, _ := value.Export() { value, valid := export.(map[int]string) is(valid, true) is(value[0], "abc") is(value[2], "ghi") } } { vm.Set("abc", _abcStruct{Abc: true, Ghi: "Nothing happens."}) value, err := vm.Get("abc") is(err, nil) export, _ := value.Export() { value, valid := export.(_abcStruct) is(valid, true) is(value.Abc, true) is(value.Ghi, "Nothing happens.") } } { vm.Set("abc", &_abcStruct{Abc: true, Ghi: "Nothing happens."}) value, err := vm.Get("abc") is(err, nil) export, _ := value.Export() { value, valid := export.(*_abcStruct) is(valid, true) is(value.Abc, true) is(value.Ghi, "Nothing happens.") } } }) } func Test_issue39(t *testing.T) { tt(t, func() { test, _ := test() test(` var abc = 0, def = [], ghi = function() { if (abc < 10) return ++abc; return undefined; } for (var jkl; (jkl = ghi());) def.push(jkl); def; `, "1,2,3,4,5,6,7,8,9,10") test(` var abc = ["1", "2", "3", "4"]; var def = []; for (var ghi; (ghi = abc.shift());) { def.push(ghi); } def; `, "1,2,3,4") }) } func Test_issue64(t *testing.T) { tt(t, func() { test, vm := test() defer mockTimeLocal(time.UTC)() abc := map[string]interface{}{ "time": time.Unix(0, 0), } vm.Set("abc", abc) def := struct { Public string private string }{ "Public", "private", } vm.Set("def", def) test(`"sec" in abc.time`, false) test(` [ "Public" in def, "private" in def, def.Public, def.private ]; `, "true,false,Public,") test(`JSON.stringify(abc)`, `{"time":"1970-01-01T00:00:00Z"}`) }) } func Test_issue73(t *testing.T) { tt(t, func() { test, vm := test() vm.Set("abc", [4]int{3, 2, 1, 0}) test(` var def = [ 0, 1, 2, 3 ]; JSON.stringify(def) + JSON.stringify(abc); `, "[0,1,2,3][3,2,1,0]") }) } func Test_7_3_1(t *testing.T) { tt(t, func() { test(` eval("var test7_3_1\u2028abc = 66;"); [ abc, typeof test7_3_1 ]; `, "66,undefined") }) } func Test_7_3_3(t *testing.T) { tt(t, func() { test(`raise: eval("//\u2028 =;"); `, "SyntaxError: Unexpected token =") }) } func Test_S7_3_A2_1_T1(t *testing.T) { tt(t, func() { test, _ := test() test(`raise: eval("'\u000Astr\u000Aing\u000A'") `, "SyntaxError: Unexpected token ILLEGAL") }) } func Test_S7_8_3_A2_1_T1(t *testing.T) { tt(t, func() { test, _ := test() test(` [ .0 === 0.0, .0, .1 === 0.1, .1 ] `, "true,0,true,0.1") }) } func Test_S7_8_4_A4_2_T3(t *testing.T) { tt(t, func() { test, _ := test() test(` "\a" `, "a") }) } func Test_S7_9_A1(t *testing.T) { tt(t, func() { test, _ := test() test(` var def; abc: for (var i = 0; i <= 0; i++) { for (var j = 0; j <= 1; j++) { if (j === 0) { continue abc; } else { def = true; } } } [ def, i, j ]; `, ",1,0") }) } func Test_S7_9_A3(t *testing.T) { tt(t, func() { test, _ := test() test(` (function(){ return 1; })() `, "undefined") }) } func Test_7_3_10(t *testing.T) { tt(t, func() { test, _ := test() test(` eval("var \u0061\u0062\u0063 = 3.14159;"); abc; `, 3.14159) test(` abc = undefined; eval("var \\u0061\\u0062\\u0063 = 3.14159;"); abc; `, 3.14159) }) } func Test_bug(t *testing.T) { tt(t, func() { test, _ := test() // 10.4.2-1-5 test(` "abc\ def" `, "abcdef") test(` eval("'abc';\ 'def'") `, "def") // S12.6.1_A10 test(` var abc = 0; do { if(typeof(def) === "function"){ abc = -1; break; } else { abc = 1; break; } } while(function def(){}); abc; `, 1) // S12.7_A7 test(`raise: abc: while (true) { eval("continue abc"); } `, "SyntaxError: Undefined label 'abc'") // S15.1.2.1_A3.3_T3 test(`raise: eval("return"); `, "SyntaxError: Illegal return statement") // 15.2.3.3-2-33 test(` var abc = { "AB\n\\cd": 1 }; Object.getOwnPropertyDescriptor(abc, "AB\n\\cd").value; `, 1) // S15.3_A2_T1 test(`raise: Function.call(this, "var x / = 1;"); `, "SyntaxError: Unexpected token /") // ? test(` (function(){ var abc = []; (function(){ abc.push(0); abc.push(1); })(undefined); if ((function(){ return true; })()) { (function(){ abc.push(2); })(); } return abc; })(); `, "0,1,2") if false { // 15.9.5.43-0-10 // Should be an invalid date test(` date = new Date(1970, 0, -99999999, 0, 0, 0, 1); `, "") } // S7.8.3_A1.2_T1 test(` [ 0e1, 1e1, 2e1, 3e1, 4e1, 5e1, 6e1, 7e1, 8e1, 9e1 ]; `, "0,10,20,30,40,50,60,70,80,90") // S15.10.2.7_A3_T2 test(` var abc = /\s+abc\s+/.exec("\t abc def"); [ abc.length, abc.index, abc.input, abc ]; `, "1,0,\t abc def,\t abc ") }) } func Test_issue79(t *testing.T) { tt(t, func() { test, vm := test() vm.Set("abc", []_abcStruct{ { Ghi: "一", Def: 1, }, { Def: 3, Ghi: "三", }, { Def: 2, Ghi: "二", }, { Def: 4, Ghi: "四", }, }) test(` abc.sort(function(a,b){ return b.Def-a.Def }); def = []; for (i = 0; i < abc.length; i++) { def.push(abc[i].String()) } def; `, "四,三,二,一") }) } func Test_issue80(t *testing.T) { tt(t, func() { test, _ := test() test(` JSON.stringify([ 1401868959, 14018689591, 140186895901, 1401868959001, 14018689590001, 140186895900001, 1401868959000001, ]); `, "[1401868959,14018689591,140186895901,1401868959001,14018689590001,140186895900001,1401868959000001]") }) } func Test_issue86(t *testing.T) { tt(t, func() { test, tester := test() test(` var obj = Object.create({}, { abc: { get: function(){ return 1; } } }); obj.abc; `, 1) v, err := tester.vm.Copy().Run(`obj.abc;`) is(is(v, 1), is(nil, err)) }) } func Test_issue87(t *testing.T) { tt(t, func() { test, vm := test() test(` var def = 0; abc: { for (;;) { def = !1; break abc; } def = !0; } def; `, false) _, err := vm.Run(` /* CryptoJS v3.1.2 code.google.com/p/crypto-js (c) 2009-2013 by Jeff Mott. All rights reserved. code.google.com/p/crypto-js/wiki/License */ var CryptoJS=CryptoJS||function(h,s){var f={},g=f.lib={},q=function(){},m=g.Base={extend:function(a){q.prototype=this;var c=new q;a&&c.mixIn(a);c.hasOwnProperty("init")||(c.init=function(){c.$super.init.apply(this,arguments)});c.init.prototype=c;c.$super=this;return c},create:function(){var a=this.extend();a.init.apply(a,arguments);return a},init:function(){},mixIn:function(a){for(var c in a)a.hasOwnProperty(c)&&(this[c]=a[c]);a.hasOwnProperty("toString")&&(this.toString=a.toString)},clone:function(){return this.init.prototype.extend(this)}}, r=g.WordArray=m.extend({init:function(a,c){a=this.words=a||[];this.sigBytes=c!=s?c:4*a.length},toString:function(a){return(a||k).stringify(this)},concat:function(a){var c=this.words,d=a.words,b=this.sigBytes;a=a.sigBytes;this.clamp();if(b%4)for(var e=0;e>>2]|=(d[e>>>2]>>>24-8*(e%4)&255)<<24-8*((b+e)%4);else if(65535>>2]=d[e>>>2];else c.push.apply(c,d);this.sigBytes+=a;return this},clamp:function(){var a=this.words,c=this.sigBytes;a[c>>>2]&=4294967295<< 32-8*(c%4);a.length=h.ceil(c/4)},clone:function(){var a=m.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var c=[],d=0;d>>2]>>>24-8*(b%4)&255;d.push((e>>>4).toString(16));d.push((e&15).toString(16))}return d.join("")},parse:function(a){for(var c=a.length,d=[],b=0;b>>3]|=parseInt(a.substr(b, 2),16)<<24-4*(b%8);return new r.init(d,c/2)}},n=l.Latin1={stringify:function(a){var c=a.words;a=a.sigBytes;for(var d=[],b=0;b>>2]>>>24-8*(b%4)&255));return d.join("")},parse:function(a){for(var c=a.length,d=[],b=0;b>>2]|=(a.charCodeAt(b)&255)<<24-8*(b%4);return new r.init(d,c)}},j=l.Utf8={stringify:function(a){try{return decodeURIComponent(escape(n.stringify(a)))}catch(c){throw Error("Malformed UTF-8 data");}},parse:function(a){return n.parse(unescape(encodeURIComponent(a)))}}, u=g.BufferedBlockAlgorithm=m.extend({reset:function(){this._data=new r.init;this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=j.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var c=this._data,d=c.words,b=c.sigBytes,e=this.blockSize,f=b/(4*e),f=a?h.ceil(f):h.max((f|0)-this._minBufferSize,0);a=f*e;b=h.min(4*a,b);if(a){for(var g=0;gn;){var j;a:{j=k;for(var u=h.sqrt(j),t=2;t<=u;t++)if(!(j%t)){j=!1;break a}j=!0}j&&(8>n&&(m[n]=l(h.pow(k,0.5))),r[n]=l(h.pow(k,1/3)),n++);k++}var a=[],f=f.SHA256=q.extend({_doReset:function(){this._hash=new g.init(m.slice(0))},_doProcessBlock:function(c,d){for(var b=this._hash.words,e=b[0],f=b[1],g=b[2],j=b[3],h=b[4],m=b[5],n=b[6],q=b[7],p=0;64>p;p++){if(16>p)a[p]= c[d+p]|0;else{var k=a[p-15],l=a[p-2];a[p]=((k<<25|k>>>7)^(k<<14|k>>>18)^k>>>3)+a[p-7]+((l<<15|l>>>17)^(l<<13|l>>>19)^l>>>10)+a[p-16]}k=q+((h<<26|h>>>6)^(h<<21|h>>>11)^(h<<7|h>>>25))+(h&m^~h&n)+r[p]+a[p];l=((e<<30|e>>>2)^(e<<19|e>>>13)^(e<<10|e>>>22))+(e&f^e&g^f&g);q=n;n=m;m=h;h=j+k|0;j=g;g=f;f=e;e=k+l|0}b[0]=b[0]+e|0;b[1]=b[1]+f|0;b[2]=b[2]+g|0;b[3]=b[3]+j|0;b[4]=b[4]+h|0;b[5]=b[5]+m|0;b[6]=b[6]+n|0;b[7]=b[7]+q|0},_doFinalize:function(){var a=this._data,d=a.words,b=8*this._nDataBytes,e=8*a.sigBytes; d[e>>>5]|=128<<24-e%32;d[(e+64>>>9<<4)+14]=h.floor(b/4294967296);d[(e+64>>>9<<4)+15]=b;a.sigBytes=4*d.length;this._process();return this._hash},clone:function(){var a=q.clone.call(this);a._hash=this._hash.clone();return a}});s.SHA256=q._createHelper(f);s.HmacSHA256=q._createHmacHelper(f)})(Math); (function(){var h=CryptoJS,s=h.enc.Utf8;h.algo.HMAC=h.lib.Base.extend({init:function(f,g){f=this._hasher=new f.init;"string"==typeof g&&(g=s.parse(g));var h=f.blockSize,m=4*h;g.sigBytes>m&&(g=f.finalize(g));g.clamp();for(var r=this._oKey=g.clone(),l=this._iKey=g.clone(),k=r.words,n=l.words,j=0;j 2 (ASCII 50) +47 // radix 11 => A/a (ASCII 65/97) +54/+86 var parseInt_alphabetTable = func() []string { table := []string{"", "", "01"} for radix := 3; radix <= 36; radix += 1 { alphabet := table[radix-1] if radix <= 10 { alphabet += string(radix + 47) } else { alphabet += string(radix+54) + string(radix+86) } table = append(table, alphabet) } return table }() func digitValue(chr rune) int { switch { case '0' <= chr && chr <= '9': return int(chr - '0') case 'a' <= chr && chr <= 'z': return int(chr - 'a' + 10) case 'A' <= chr && chr <= 'Z': return int(chr - 'A' + 10) } return 36 // Larger than any legal digit value } func builtinGlobal_parseInt(call FunctionCall) Value { input := strings.Trim(call.Argument(0).string(), builtinString_trim_whitespace) if len(input) == 0 { return NaNValue() } radix := int(toInt32(call.Argument(1))) negative := false switch input[0] { case '+': input = input[1:] case '-': negative = true input = input[1:] } strip := true if radix == 0 { radix = 10 } else { if radix < 2 || radix > 36 { return NaNValue() } else if radix != 16 { strip = false } } switch len(input) { case 0: return NaNValue() case 1: default: if strip { if input[0] == '0' && (input[1] == 'x' || input[1] == 'X') { input = input[2:] radix = 16 } } } base := radix index := 0 for ; index < len(input); index++ { digit := digitValue(rune(input[index])) // If not ASCII, then an error anyway if digit >= base { break } } input = input[0:index] value, err := strconv.ParseInt(input, radix, 64) if err != nil { if err.(*strconv.NumError).Err == strconv.ErrRange { base := float64(base) // Could just be a very large number (e.g. 0x8000000000000000) var value float64 for _, chr := range input { digit := float64(digitValue(chr)) if digit >= base { goto error } value = value*base + digit } if negative { value *= -1 } return toValue_float64(value) } error: return NaNValue() } if negative { value *= -1 } return toValue_int64(value) } var parseFloat_matchBadSpecial = regexp.MustCompile(`[\+\-]?(?:[Ii]nf$|infinity)`) var parseFloat_matchValid = regexp.MustCompile(`[0-9eE\+\-\.]|Infinity`) func builtinGlobal_parseFloat(call FunctionCall) Value { // Caveat emptor: This implementation does NOT match the specification input := strings.Trim(call.Argument(0).string(), builtinString_trim_whitespace) if parseFloat_matchBadSpecial.MatchString(input) { return NaNValue() } value, err := strconv.ParseFloat(input, 64) if err != nil { for end := len(input); end > 0; end-- { input := input[0:end] if !parseFloat_matchValid.MatchString(input) { return NaNValue() } value, err = strconv.ParseFloat(input, 64) if err == nil { break } } if err != nil { return NaNValue() } } return toValue_float64(value) } // encodeURI/decodeURI func _builtinGlobal_encodeURI(call FunctionCall, escape *regexp.Regexp) Value { value := call.Argument(0) var input []uint16 switch vl := value.value.(type) { case []uint16: input = vl default: input = utf16.Encode([]rune(value.string())) } if len(input) == 0 { return toValue_string("") } output := []byte{} length := len(input) encode := make([]byte, 4) for index := 0; index < length; { value := input[index] decode := utf16.Decode(input[index : index+1]) if value >= 0xDC00 && value <= 0xDFFF { panic(call.runtime.panicURIError("URI malformed")) } if value >= 0xD800 && value <= 0xDBFF { index += 1 if index >= length { panic(call.runtime.panicURIError("URI malformed")) } // input = ..., value, value1, ... value1 := input[index] if value1 < 0xDC00 || value1 > 0xDFFF { panic(call.runtime.panicURIError("URI malformed")) } decode = []rune{((rune(value) - 0xD800) * 0x400) + (rune(value1) - 0xDC00) + 0x10000} } index += 1 size := utf8.EncodeRune(encode, decode[0]) encode := encode[0:size] output = append(output, encode...) } { value := escape.ReplaceAllFunc(output, func(target []byte) []byte { // Probably a better way of doing this if target[0] == ' ' { return []byte("%20") } return []byte(url.QueryEscape(string(target))) }) return toValue_string(string(value)) } } var encodeURI_Regexp = regexp.MustCompile(`([^~!@#$&*()=:/,;?+'])`) func builtinGlobal_encodeURI(call FunctionCall) Value { return _builtinGlobal_encodeURI(call, encodeURI_Regexp) } var encodeURIComponent_Regexp = regexp.MustCompile(`([^~!*()'])`) func builtinGlobal_encodeURIComponent(call FunctionCall) Value { return _builtinGlobal_encodeURI(call, encodeURIComponent_Regexp) } // 3B/2F/3F/3A/40/26/3D/2B/24/2C/23 var decodeURI_guard = regexp.MustCompile(`(?i)(?:%)(3B|2F|3F|3A|40|26|3D|2B|24|2C|23)`) func _decodeURI(input string, reserve bool) (string, bool) { if reserve { input = decodeURI_guard.ReplaceAllString(input, "%25$1") } input = strings.Replace(input, "+", "%2B", -1) // Ugly hack to make QueryUnescape work with our use case output, err := url.QueryUnescape(input) if err != nil || !utf8.ValidString(output) { return "", true } return output, false } func builtinGlobal_decodeURI(call FunctionCall) Value { output, err := _decodeURI(call.Argument(0).string(), true) if err { panic(call.runtime.panicURIError("URI malformed")) } return toValue_string(output) } func builtinGlobal_decodeURIComponent(call FunctionCall) Value { output, err := _decodeURI(call.Argument(0).string(), false) if err { panic(call.runtime.panicURIError("URI malformed")) } return toValue_string(output) } // escape/unescape func builtin_shouldEscape(chr byte) bool { if 'A' <= chr && chr <= 'Z' || 'a' <= chr && chr <= 'z' || '0' <= chr && chr <= '9' { return false } return !strings.ContainsRune("*_+-./", rune(chr)) } const escapeBase16 = "0123456789ABCDEF" func builtin_escape(input string) string { output := make([]byte, 0, len(input)) length := len(input) for index := 0; index < length; { if builtin_shouldEscape(input[index]) { chr, width := utf8.DecodeRuneInString(input[index:]) chr16 := utf16.Encode([]rune{chr})[0] if 256 > chr16 { output = append(output, '%', escapeBase16[chr16>>4], escapeBase16[chr16&15], ) } else { output = append(output, '%', 'u', escapeBase16[chr16>>12], escapeBase16[(chr16>>8)&15], escapeBase16[(chr16>>4)&15], escapeBase16[chr16&15], ) } index += width } else { output = append(output, input[index]) index += 1 } } return string(output) } func builtin_unescape(input string) string { output := make([]rune, 0, len(input)) length := len(input) for index := 0; index < length; { if input[index] == '%' { if index <= length-6 && input[index+1] == 'u' { byte16, err := hex.DecodeString(input[index+2 : index+6]) if err == nil { value := uint16(byte16[0])<<8 + uint16(byte16[1]) chr := utf16.Decode([]uint16{value})[0] output = append(output, chr) index += 6 continue } } if index <= length-3 { byte8, err := hex.DecodeString(input[index+1 : index+3]) if err == nil { value := uint16(byte8[0]) chr := utf16.Decode([]uint16{value})[0] output = append(output, chr) index += 3 continue } } } output = append(output, rune(input[index])) index += 1 } return string(output) } func builtinGlobal_escape(call FunctionCall) Value { return toValue_string(builtin_escape(call.Argument(0).string())) } func builtinGlobal_unescape(call FunctionCall) Value { return toValue_string(builtin_unescape(call.Argument(0).string())) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/builtin_array.go000066400000000000000000000462361331145703200256030ustar00rootroot00000000000000package otto import ( "strconv" "strings" ) // Array func builtinArray(call FunctionCall) Value { return toValue_object(builtinNewArrayNative(call.runtime, call.ArgumentList)) } func builtinNewArray(self *_object, argumentList []Value) Value { return toValue_object(builtinNewArrayNative(self.runtime, argumentList)) } func builtinNewArrayNative(runtime *_runtime, argumentList []Value) *_object { if len(argumentList) == 1 { firstArgument := argumentList[0] if firstArgument.IsNumber() { return runtime.newArray(arrayUint32(runtime, firstArgument)) } } return runtime.newArrayOf(argumentList) } func builtinArray_toString(call FunctionCall) Value { thisObject := call.thisObject() join := thisObject.get("join") if join.isCallable() { join := join._object() return join.call(call.This, call.ArgumentList, false, nativeFrame) } return builtinObject_toString(call) } func builtinArray_toLocaleString(call FunctionCall) Value { separator := "," thisObject := call.thisObject() length := int64(toUint32(thisObject.get("length"))) if length == 0 { return toValue_string("") } stringList := make([]string, 0, length) for index := int64(0); index < length; index += 1 { value := thisObject.get(arrayIndexToString(index)) stringValue := "" switch value.kind { case valueEmpty, valueUndefined, valueNull: default: object := call.runtime.toObject(value) toLocaleString := object.get("toLocaleString") if !toLocaleString.isCallable() { panic(call.runtime.panicTypeError()) } stringValue = toLocaleString.call(call.runtime, toValue_object(object)).string() } stringList = append(stringList, stringValue) } return toValue_string(strings.Join(stringList, separator)) } func builtinArray_concat(call FunctionCall) Value { thisObject := call.thisObject() valueArray := []Value{} source := append([]Value{toValue_object(thisObject)}, call.ArgumentList...) for _, item := range source { switch item.kind { case valueObject: object := item._object() if isArray(object) { length := object.get("length").number().int64 for index := int64(0); index < length; index += 1 { name := strconv.FormatInt(index, 10) if object.hasProperty(name) { valueArray = append(valueArray, object.get(name)) } else { valueArray = append(valueArray, Value{}) } } continue } fallthrough default: valueArray = append(valueArray, item) } } return toValue_object(call.runtime.newArrayOf(valueArray)) } func builtinArray_shift(call FunctionCall) Value { thisObject := call.thisObject() length := int64(toUint32(thisObject.get("length"))) if 0 == length { thisObject.put("length", toValue_int64(0), true) return Value{} } first := thisObject.get("0") for index := int64(1); index < length; index++ { from := arrayIndexToString(index) to := arrayIndexToString(index - 1) if thisObject.hasProperty(from) { thisObject.put(to, thisObject.get(from), true) } else { thisObject.delete(to, true) } } thisObject.delete(arrayIndexToString(length-1), true) thisObject.put("length", toValue_int64(length-1), true) return first } func builtinArray_push(call FunctionCall) Value { thisObject := call.thisObject() itemList := call.ArgumentList index := int64(toUint32(thisObject.get("length"))) for len(itemList) > 0 { thisObject.put(arrayIndexToString(index), itemList[0], true) itemList = itemList[1:] index += 1 } length := toValue_int64(index) thisObject.put("length", length, true) return length } func builtinArray_pop(call FunctionCall) Value { thisObject := call.thisObject() length := int64(toUint32(thisObject.get("length"))) if 0 == length { thisObject.put("length", toValue_uint32(0), true) return Value{} } last := thisObject.get(arrayIndexToString(length - 1)) thisObject.delete(arrayIndexToString(length-1), true) thisObject.put("length", toValue_int64(length-1), true) return last } func builtinArray_join(call FunctionCall) Value { separator := "," { argument := call.Argument(0) if argument.IsDefined() { separator = argument.string() } } thisObject := call.thisObject() length := int64(toUint32(thisObject.get("length"))) if length == 0 { return toValue_string("") } stringList := make([]string, 0, length) for index := int64(0); index < length; index += 1 { value := thisObject.get(arrayIndexToString(index)) stringValue := "" switch value.kind { case valueEmpty, valueUndefined, valueNull: default: stringValue = value.string() } stringList = append(stringList, stringValue) } return toValue_string(strings.Join(stringList, separator)) } func builtinArray_splice(call FunctionCall) Value { thisObject := call.thisObject() length := int64(toUint32(thisObject.get("length"))) start := valueToRangeIndex(call.Argument(0), length, false) deleteCount := length - start if arg, ok := call.getArgument(1); ok { deleteCount = valueToRangeIndex(arg, length-start, true) } valueArray := make([]Value, deleteCount) for index := int64(0); index < deleteCount; index++ { indexString := arrayIndexToString(int64(start + index)) if thisObject.hasProperty(indexString) { valueArray[index] = thisObject.get(indexString) } } // 0, <1, 2, 3, 4>, 5, 6, 7 // a, b // length 8 - delete 4 @ start 1 itemList := []Value{} itemCount := int64(len(call.ArgumentList)) if itemCount > 2 { itemCount -= 2 // Less the first two arguments itemList = call.ArgumentList[2:] } else { itemCount = 0 } if itemCount < deleteCount { // The Object/Array is shrinking stop := int64(length) - deleteCount // The new length of the Object/Array before // appending the itemList remainder // Stopping at the lower bound of the insertion: // Move an item from the after the deleted portion // to a position after the inserted portion for index := start; index < stop; index++ { from := arrayIndexToString(index + deleteCount) // Position just after deletion to := arrayIndexToString(index + itemCount) // Position just after splice (insertion) if thisObject.hasProperty(from) { thisObject.put(to, thisObject.get(from), true) } else { thisObject.delete(to, true) } } // Delete off the end // We don't bother to delete below (if any) since those // will be overwritten anyway for index := int64(length); index > (stop + itemCount); index-- { thisObject.delete(arrayIndexToString(index-1), true) } } else if itemCount > deleteCount { // The Object/Array is growing // The itemCount is greater than the deleteCount, so we do // not have to worry about overwriting what we should be moving // --- // Starting from the upper bound of the deletion: // Move an item from the after the deleted portion // to a position after the inserted portion for index := int64(length) - deleteCount; index > start; index-- { from := arrayIndexToString(index + deleteCount - 1) to := arrayIndexToString(index + itemCount - 1) if thisObject.hasProperty(from) { thisObject.put(to, thisObject.get(from), true) } else { thisObject.delete(to, true) } } } for index := int64(0); index < itemCount; index++ { thisObject.put(arrayIndexToString(index+start), itemList[index], true) } thisObject.put("length", toValue_int64(int64(length)+itemCount-deleteCount), true) return toValue_object(call.runtime.newArrayOf(valueArray)) } func builtinArray_slice(call FunctionCall) Value { thisObject := call.thisObject() length := int64(toUint32(thisObject.get("length"))) start, end := rangeStartEnd(call.ArgumentList, length, false) if start >= end { // Always an empty array return toValue_object(call.runtime.newArray(0)) } sliceLength := end - start sliceValueArray := make([]Value, sliceLength) for index := int64(0); index < sliceLength; index++ { from := arrayIndexToString(index + start) if thisObject.hasProperty(from) { sliceValueArray[index] = thisObject.get(from) } } return toValue_object(call.runtime.newArrayOf(sliceValueArray)) } func builtinArray_unshift(call FunctionCall) Value { thisObject := call.thisObject() length := int64(toUint32(thisObject.get("length"))) itemList := call.ArgumentList itemCount := int64(len(itemList)) for index := length; index > 0; index-- { from := arrayIndexToString(index - 1) to := arrayIndexToString(index + itemCount - 1) if thisObject.hasProperty(from) { thisObject.put(to, thisObject.get(from), true) } else { thisObject.delete(to, true) } } for index := int64(0); index < itemCount; index++ { thisObject.put(arrayIndexToString(index), itemList[index], true) } newLength := toValue_int64(length + itemCount) thisObject.put("length", newLength, true) return newLength } func builtinArray_reverse(call FunctionCall) Value { thisObject := call.thisObject() length := int64(toUint32(thisObject.get("length"))) lower := struct { name string index int64 exists bool }{} upper := lower lower.index = 0 middle := length / 2 // Division will floor for lower.index != middle { lower.name = arrayIndexToString(lower.index) upper.index = length - lower.index - 1 upper.name = arrayIndexToString(upper.index) lower.exists = thisObject.hasProperty(lower.name) upper.exists = thisObject.hasProperty(upper.name) if lower.exists && upper.exists { lowerValue := thisObject.get(lower.name) upperValue := thisObject.get(upper.name) thisObject.put(lower.name, upperValue, true) thisObject.put(upper.name, lowerValue, true) } else if !lower.exists && upper.exists { value := thisObject.get(upper.name) thisObject.delete(upper.name, true) thisObject.put(lower.name, value, true) } else if lower.exists && !upper.exists { value := thisObject.get(lower.name) thisObject.delete(lower.name, true) thisObject.put(upper.name, value, true) } else { // Nothing happens. } lower.index += 1 } return call.This } func sortCompare(thisObject *_object, index0, index1 uint, compare *_object) int { j := struct { name string exists bool defined bool value string }{} k := j j.name = arrayIndexToString(int64(index0)) j.exists = thisObject.hasProperty(j.name) k.name = arrayIndexToString(int64(index1)) k.exists = thisObject.hasProperty(k.name) if !j.exists && !k.exists { return 0 } else if !j.exists { return 1 } else if !k.exists { return -1 } x := thisObject.get(j.name) y := thisObject.get(k.name) j.defined = x.IsDefined() k.defined = y.IsDefined() if !j.defined && !k.defined { return 0 } else if !j.defined { return 1 } else if !k.defined { return -1 } if compare == nil { j.value = x.string() k.value = y.string() if j.value == k.value { return 0 } else if j.value < k.value { return -1 } return 1 } return int(toInt32(compare.call(Value{}, []Value{x, y}, false, nativeFrame))) } func arraySortSwap(thisObject *_object, index0, index1 uint) { j := struct { name string exists bool }{} k := j j.name = arrayIndexToString(int64(index0)) j.exists = thisObject.hasProperty(j.name) k.name = arrayIndexToString(int64(index1)) k.exists = thisObject.hasProperty(k.name) if j.exists && k.exists { jValue := thisObject.get(j.name) kValue := thisObject.get(k.name) thisObject.put(j.name, kValue, true) thisObject.put(k.name, jValue, true) } else if !j.exists && k.exists { value := thisObject.get(k.name) thisObject.delete(k.name, true) thisObject.put(j.name, value, true) } else if j.exists && !k.exists { value := thisObject.get(j.name) thisObject.delete(j.name, true) thisObject.put(k.name, value, true) } else { // Nothing happens. } } func arraySortQuickPartition(thisObject *_object, left, right, pivot uint, compare *_object) (uint, uint) { arraySortSwap(thisObject, pivot, right) // Right is now the pivot value cursor := left cursor2 := left for index := left; index < right; index++ { comparison := sortCompare(thisObject, index, right, compare) // Compare to the pivot value if comparison < 0 { arraySortSwap(thisObject, index, cursor) if cursor < cursor2 { arraySortSwap(thisObject, index, cursor2) } cursor += 1 cursor2 += 1 } else if comparison == 0 { arraySortSwap(thisObject, index, cursor2) cursor2 += 1 } } arraySortSwap(thisObject, cursor2, right) return cursor, cursor2 } func arraySortQuickSort(thisObject *_object, left, right uint, compare *_object) { if left < right { middle := left + (right-left)/2 pivot, pivot2 := arraySortQuickPartition(thisObject, left, right, middle, compare) if pivot > 0 { arraySortQuickSort(thisObject, left, pivot-1, compare) } arraySortQuickSort(thisObject, pivot2+1, right, compare) } } func builtinArray_sort(call FunctionCall) Value { thisObject := call.thisObject() length := uint(toUint32(thisObject.get("length"))) compareValue := call.Argument(0) compare := compareValue._object() if compareValue.IsUndefined() { } else if !compareValue.isCallable() { panic(call.runtime.panicTypeError()) } if length > 1 { arraySortQuickSort(thisObject, 0, length-1, compare) } return call.This } func builtinArray_isArray(call FunctionCall) Value { return toValue_bool(isArray(call.Argument(0)._object())) } func builtinArray_indexOf(call FunctionCall) Value { thisObject, matchValue := call.thisObject(), call.Argument(0) if length := int64(toUint32(thisObject.get("length"))); length > 0 { index := int64(0) if len(call.ArgumentList) > 1 { index = call.Argument(1).number().int64 } if index < 0 { if index += length; index < 0 { index = 0 } } else if index >= length { index = -1 } for ; index >= 0 && index < length; index++ { name := arrayIndexToString(int64(index)) if !thisObject.hasProperty(name) { continue } value := thisObject.get(name) if strictEqualityComparison(matchValue, value) { return toValue_uint32(uint32(index)) } } } return toValue_int(-1) } func builtinArray_lastIndexOf(call FunctionCall) Value { thisObject, matchValue := call.thisObject(), call.Argument(0) length := int64(toUint32(thisObject.get("length"))) index := length - 1 if len(call.ArgumentList) > 1 { index = call.Argument(1).number().int64 } if 0 > index { index += length } if index > length { index = length - 1 } else if 0 > index { return toValue_int(-1) } for ; index >= 0; index-- { name := arrayIndexToString(int64(index)) if !thisObject.hasProperty(name) { continue } value := thisObject.get(name) if strictEqualityComparison(matchValue, value) { return toValue_uint32(uint32(index)) } } return toValue_int(-1) } func builtinArray_every(call FunctionCall) Value { thisObject := call.thisObject() this := toValue_object(thisObject) if iterator := call.Argument(0); iterator.isCallable() { length := int64(toUint32(thisObject.get("length"))) callThis := call.Argument(1) for index := int64(0); index < length; index++ { if key := arrayIndexToString(index); thisObject.hasProperty(key) { if value := thisObject.get(key); iterator.call(call.runtime, callThis, value, toValue_int64(index), this).bool() { continue } return falseValue } } return trueValue } panic(call.runtime.panicTypeError()) } func builtinArray_some(call FunctionCall) Value { thisObject := call.thisObject() this := toValue_object(thisObject) if iterator := call.Argument(0); iterator.isCallable() { length := int64(toUint32(thisObject.get("length"))) callThis := call.Argument(1) for index := int64(0); index < length; index++ { if key := arrayIndexToString(index); thisObject.hasProperty(key) { if value := thisObject.get(key); iterator.call(call.runtime, callThis, value, toValue_int64(index), this).bool() { return trueValue } } } return falseValue } panic(call.runtime.panicTypeError()) } func builtinArray_forEach(call FunctionCall) Value { thisObject := call.thisObject() this := toValue_object(thisObject) if iterator := call.Argument(0); iterator.isCallable() { length := int64(toUint32(thisObject.get("length"))) callThis := call.Argument(1) for index := int64(0); index < length; index++ { if key := arrayIndexToString(index); thisObject.hasProperty(key) { iterator.call(call.runtime, callThis, thisObject.get(key), toValue_int64(index), this) } } return Value{} } panic(call.runtime.panicTypeError()) } func builtinArray_map(call FunctionCall) Value { thisObject := call.thisObject() this := toValue_object(thisObject) if iterator := call.Argument(0); iterator.isCallable() { length := int64(toUint32(thisObject.get("length"))) callThis := call.Argument(1) values := make([]Value, length) for index := int64(0); index < length; index++ { if key := arrayIndexToString(index); thisObject.hasProperty(key) { values[index] = iterator.call(call.runtime, callThis, thisObject.get(key), index, this) } else { values[index] = Value{} } } return toValue_object(call.runtime.newArrayOf(values)) } panic(call.runtime.panicTypeError()) } func builtinArray_filter(call FunctionCall) Value { thisObject := call.thisObject() this := toValue_object(thisObject) if iterator := call.Argument(0); iterator.isCallable() { length := int64(toUint32(thisObject.get("length"))) callThis := call.Argument(1) values := make([]Value, 0) for index := int64(0); index < length; index++ { if key := arrayIndexToString(index); thisObject.hasProperty(key) { value := thisObject.get(key) if iterator.call(call.runtime, callThis, value, index, this).bool() { values = append(values, value) } } } return toValue_object(call.runtime.newArrayOf(values)) } panic(call.runtime.panicTypeError()) } func builtinArray_reduce(call FunctionCall) Value { thisObject := call.thisObject() this := toValue_object(thisObject) if iterator := call.Argument(0); iterator.isCallable() { initial := len(call.ArgumentList) > 1 start := call.Argument(1) length := int64(toUint32(thisObject.get("length"))) index := int64(0) if length > 0 || initial { var accumulator Value if !initial { for ; index < length; index++ { if key := arrayIndexToString(index); thisObject.hasProperty(key) { accumulator = thisObject.get(key) index++ break } } } else { accumulator = start } for ; index < length; index++ { if key := arrayIndexToString(index); thisObject.hasProperty(key) { accumulator = iterator.call(call.runtime, Value{}, accumulator, thisObject.get(key), key, this) } } return accumulator } } panic(call.runtime.panicTypeError()) } func builtinArray_reduceRight(call FunctionCall) Value { thisObject := call.thisObject() this := toValue_object(thisObject) if iterator := call.Argument(0); iterator.isCallable() { initial := len(call.ArgumentList) > 1 start := call.Argument(1) length := int64(toUint32(thisObject.get("length"))) if length > 0 || initial { index := length - 1 var accumulator Value if !initial { for ; index >= 0; index-- { if key := arrayIndexToString(index); thisObject.hasProperty(key) { accumulator = thisObject.get(key) index-- break } } } else { accumulator = start } for ; index >= 0; index-- { if key := arrayIndexToString(index); thisObject.hasProperty(key) { accumulator = iterator.call(call.runtime, Value{}, accumulator, thisObject.get(key), key, this) } } return accumulator } } panic(call.runtime.panicTypeError()) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/builtin_boolean.go000066400000000000000000000013171331145703200260730ustar00rootroot00000000000000package otto // Boolean func builtinBoolean(call FunctionCall) Value { return toValue_bool(call.Argument(0).bool()) } func builtinNewBoolean(self *_object, argumentList []Value) Value { return toValue_object(self.runtime.newBoolean(valueOfArrayIndex(argumentList, 0))) } func builtinBoolean_toString(call FunctionCall) Value { value := call.This if !value.IsBoolean() { // Will throw a TypeError if ThisObject is not a Boolean value = call.thisClassObject("Boolean").primitiveValue() } return toValue_string(value.string()) } func builtinBoolean_valueOf(call FunctionCall) Value { value := call.This if !value.IsBoolean() { value = call.thisClassObject("Boolean").primitiveValue() } return value } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/builtin_date.go000066400000000000000000000357621331145703200254040ustar00rootroot00000000000000package otto import ( "math" Time "time" ) // Date const ( // TODO Be like V8? // builtinDate_goDateTimeLayout = "Mon Jan 2 2006 15:04:05 GMT-0700 (MST)" builtinDate_goDateTimeLayout = Time.RFC1123 // "Mon, 02 Jan 2006 15:04:05 MST" builtinDate_goDateLayout = "Mon, 02 Jan 2006" builtinDate_goTimeLayout = "15:04:05 MST" ) func builtinDate(call FunctionCall) Value { date := &_dateObject{} date.Set(newDateTime([]Value{}, Time.Local)) return toValue_string(date.Time().Format(builtinDate_goDateTimeLayout)) } func builtinNewDate(self *_object, argumentList []Value) Value { return toValue_object(self.runtime.newDate(newDateTime(argumentList, Time.Local))) } func builtinDate_toString(call FunctionCall) Value { date := dateObjectOf(call.runtime, call.thisObject()) if date.isNaN { return toValue_string("Invalid Date") } return toValue_string(date.Time().Local().Format(builtinDate_goDateTimeLayout)) } func builtinDate_toDateString(call FunctionCall) Value { date := dateObjectOf(call.runtime, call.thisObject()) if date.isNaN { return toValue_string("Invalid Date") } return toValue_string(date.Time().Local().Format(builtinDate_goDateLayout)) } func builtinDate_toTimeString(call FunctionCall) Value { date := dateObjectOf(call.runtime, call.thisObject()) if date.isNaN { return toValue_string("Invalid Date") } return toValue_string(date.Time().Local().Format(builtinDate_goTimeLayout)) } func builtinDate_toUTCString(call FunctionCall) Value { date := dateObjectOf(call.runtime, call.thisObject()) if date.isNaN { return toValue_string("Invalid Date") } return toValue_string(date.Time().Format(builtinDate_goDateTimeLayout)) } func builtinDate_toISOString(call FunctionCall) Value { date := dateObjectOf(call.runtime, call.thisObject()) if date.isNaN { return toValue_string("Invalid Date") } return toValue_string(date.Time().Format("2006-01-02T15:04:05.000Z")) } func builtinDate_toJSON(call FunctionCall) Value { object := call.thisObject() value := object.DefaultValue(defaultValueHintNumber) // FIXME object.primitiveNumberValue { // FIXME value.isFinite value := value.float64() if math.IsNaN(value) || math.IsInf(value, 0) { return nullValue } } toISOString := object.get("toISOString") if !toISOString.isCallable() { // FIXME panic(call.runtime.panicTypeError()) } return toISOString.call(call.runtime, toValue_object(object), []Value{}) } func builtinDate_toGMTString(call FunctionCall) Value { date := dateObjectOf(call.runtime, call.thisObject()) if date.isNaN { return toValue_string("Invalid Date") } return toValue_string(date.Time().Format("Mon, 02 Jan 2006 15:04:05 GMT")) } func builtinDate_getTime(call FunctionCall) Value { date := dateObjectOf(call.runtime, call.thisObject()) if date.isNaN { return NaNValue() } // We do this (convert away from a float) so the user // does not get something back in exponential notation return toValue_int64(int64(date.Epoch())) } func builtinDate_setTime(call FunctionCall) Value { object := call.thisObject() date := dateObjectOf(call.runtime, call.thisObject()) date.Set(call.Argument(0).float64()) object.value = date return date.Value() } func _builtinDate_beforeSet(call FunctionCall, argumentLimit int, timeLocal bool) (*_object, *_dateObject, *_ecmaTime, []int) { object := call.thisObject() date := dateObjectOf(call.runtime, call.thisObject()) if date.isNaN { return nil, nil, nil, nil } if argumentLimit > len(call.ArgumentList) { argumentLimit = len(call.ArgumentList) } if argumentLimit == 0 { object.value = invalidDateObject return nil, nil, nil, nil } valueList := make([]int, argumentLimit) for index := 0; index < argumentLimit; index++ { value := call.ArgumentList[index] nm := value.number() switch nm.kind { case numberInteger, numberFloat: default: object.value = invalidDateObject return nil, nil, nil, nil } valueList[index] = int(nm.int64) } baseTime := date.Time() if timeLocal { baseTime = baseTime.Local() } ecmaTime := ecmaTime(baseTime) return object, &date, &ecmaTime, valueList } func builtinDate_parse(call FunctionCall) Value { date := call.Argument(0).string() return toValue_float64(dateParse(date)) } func builtinDate_UTC(call FunctionCall) Value { return toValue_float64(newDateTime(call.ArgumentList, Time.UTC)) } func builtinDate_now(call FunctionCall) Value { call.ArgumentList = []Value(nil) return builtinDate_UTC(call) } // This is a placeholder func builtinDate_toLocaleString(call FunctionCall) Value { date := dateObjectOf(call.runtime, call.thisObject()) if date.isNaN { return toValue_string("Invalid Date") } return toValue_string(date.Time().Local().Format("2006-01-02 15:04:05")) } // This is a placeholder func builtinDate_toLocaleDateString(call FunctionCall) Value { date := dateObjectOf(call.runtime, call.thisObject()) if date.isNaN { return toValue_string("Invalid Date") } return toValue_string(date.Time().Local().Format("2006-01-02")) } // This is a placeholder func builtinDate_toLocaleTimeString(call FunctionCall) Value { date := dateObjectOf(call.runtime, call.thisObject()) if date.isNaN { return toValue_string("Invalid Date") } return toValue_string(date.Time().Local().Format("15:04:05")) } func builtinDate_valueOf(call FunctionCall) Value { date := dateObjectOf(call.runtime, call.thisObject()) if date.isNaN { return NaNValue() } return date.Value() } func builtinDate_getYear(call FunctionCall) Value { // Will throw a TypeError is ThisObject is nil or // does not have Class of "Date" date := dateObjectOf(call.runtime, call.thisObject()) if date.isNaN { return NaNValue() } return toValue_int(date.Time().Local().Year() - 1900) } func builtinDate_getFullYear(call FunctionCall) Value { // Will throw a TypeError is ThisObject is nil or // does not have Class of "Date" date := dateObjectOf(call.runtime, call.thisObject()) if date.isNaN { return NaNValue() } return toValue_int(date.Time().Local().Year()) } func builtinDate_getUTCFullYear(call FunctionCall) Value { date := dateObjectOf(call.runtime, call.thisObject()) if date.isNaN { return NaNValue() } return toValue_int(date.Time().Year()) } func builtinDate_getMonth(call FunctionCall) Value { date := dateObjectOf(call.runtime, call.thisObject()) if date.isNaN { return NaNValue() } return toValue_int(dateFromGoMonth(date.Time().Local().Month())) } func builtinDate_getUTCMonth(call FunctionCall) Value { date := dateObjectOf(call.runtime, call.thisObject()) if date.isNaN { return NaNValue() } return toValue_int(dateFromGoMonth(date.Time().Month())) } func builtinDate_getDate(call FunctionCall) Value { date := dateObjectOf(call.runtime, call.thisObject()) if date.isNaN { return NaNValue() } return toValue_int(date.Time().Local().Day()) } func builtinDate_getUTCDate(call FunctionCall) Value { date := dateObjectOf(call.runtime, call.thisObject()) if date.isNaN { return NaNValue() } return toValue_int(date.Time().Day()) } func builtinDate_getDay(call FunctionCall) Value { // Actually day of the week date := dateObjectOf(call.runtime, call.thisObject()) if date.isNaN { return NaNValue() } return toValue_int(dateFromGoDay(date.Time().Local().Weekday())) } func builtinDate_getUTCDay(call FunctionCall) Value { date := dateObjectOf(call.runtime, call.thisObject()) if date.isNaN { return NaNValue() } return toValue_int(dateFromGoDay(date.Time().Weekday())) } func builtinDate_getHours(call FunctionCall) Value { date := dateObjectOf(call.runtime, call.thisObject()) if date.isNaN { return NaNValue() } return toValue_int(date.Time().Local().Hour()) } func builtinDate_getUTCHours(call FunctionCall) Value { date := dateObjectOf(call.runtime, call.thisObject()) if date.isNaN { return NaNValue() } return toValue_int(date.Time().Hour()) } func builtinDate_getMinutes(call FunctionCall) Value { date := dateObjectOf(call.runtime, call.thisObject()) if date.isNaN { return NaNValue() } return toValue_int(date.Time().Local().Minute()) } func builtinDate_getUTCMinutes(call FunctionCall) Value { date := dateObjectOf(call.runtime, call.thisObject()) if date.isNaN { return NaNValue() } return toValue_int(date.Time().Minute()) } func builtinDate_getSeconds(call FunctionCall) Value { date := dateObjectOf(call.runtime, call.thisObject()) if date.isNaN { return NaNValue() } return toValue_int(date.Time().Local().Second()) } func builtinDate_getUTCSeconds(call FunctionCall) Value { date := dateObjectOf(call.runtime, call.thisObject()) if date.isNaN { return NaNValue() } return toValue_int(date.Time().Second()) } func builtinDate_getMilliseconds(call FunctionCall) Value { date := dateObjectOf(call.runtime, call.thisObject()) if date.isNaN { return NaNValue() } return toValue_int(date.Time().Local().Nanosecond() / (100 * 100 * 100)) } func builtinDate_getUTCMilliseconds(call FunctionCall) Value { date := dateObjectOf(call.runtime, call.thisObject()) if date.isNaN { return NaNValue() } return toValue_int(date.Time().Nanosecond() / (100 * 100 * 100)) } func builtinDate_getTimezoneOffset(call FunctionCall) Value { date := dateObjectOf(call.runtime, call.thisObject()) if date.isNaN { return NaNValue() } timeLocal := date.Time().Local() // Is this kosher? timeLocalAsUTC := Time.Date( timeLocal.Year(), timeLocal.Month(), timeLocal.Day(), timeLocal.Hour(), timeLocal.Minute(), timeLocal.Second(), timeLocal.Nanosecond(), Time.UTC, ) return toValue_float64(date.Time().Sub(timeLocalAsUTC).Seconds() / 60) } func builtinDate_setMilliseconds(call FunctionCall) Value { object, date, ecmaTime, value := _builtinDate_beforeSet(call, 1, true) if ecmaTime == nil { return NaNValue() } ecmaTime.millisecond = value[0] date.SetTime(ecmaTime.goTime()) object.value = *date return date.Value() } func builtinDate_setUTCMilliseconds(call FunctionCall) Value { object, date, ecmaTime, value := _builtinDate_beforeSet(call, 1, false) if ecmaTime == nil { return NaNValue() } ecmaTime.millisecond = value[0] date.SetTime(ecmaTime.goTime()) object.value = *date return date.Value() } func builtinDate_setSeconds(call FunctionCall) Value { object, date, ecmaTime, value := _builtinDate_beforeSet(call, 2, true) if ecmaTime == nil { return NaNValue() } if len(value) > 1 { ecmaTime.millisecond = value[1] } ecmaTime.second = value[0] date.SetTime(ecmaTime.goTime()) object.value = *date return date.Value() } func builtinDate_setUTCSeconds(call FunctionCall) Value { object, date, ecmaTime, value := _builtinDate_beforeSet(call, 2, false) if ecmaTime == nil { return NaNValue() } if len(value) > 1 { ecmaTime.millisecond = value[1] } ecmaTime.second = value[0] date.SetTime(ecmaTime.goTime()) object.value = *date return date.Value() } func builtinDate_setMinutes(call FunctionCall) Value { object, date, ecmaTime, value := _builtinDate_beforeSet(call, 3, true) if ecmaTime == nil { return NaNValue() } if len(value) > 2 { ecmaTime.millisecond = value[2] ecmaTime.second = value[1] } else if len(value) > 1 { ecmaTime.second = value[1] } ecmaTime.minute = value[0] date.SetTime(ecmaTime.goTime()) object.value = *date return date.Value() } func builtinDate_setUTCMinutes(call FunctionCall) Value { object, date, ecmaTime, value := _builtinDate_beforeSet(call, 3, false) if ecmaTime == nil { return NaNValue() } if len(value) > 2 { ecmaTime.millisecond = value[2] ecmaTime.second = value[1] } else if len(value) > 1 { ecmaTime.second = value[1] } ecmaTime.minute = value[0] date.SetTime(ecmaTime.goTime()) object.value = *date return date.Value() } func builtinDate_setHours(call FunctionCall) Value { object, date, ecmaTime, value := _builtinDate_beforeSet(call, 4, true) if ecmaTime == nil { return NaNValue() } if len(value) > 3 { ecmaTime.millisecond = value[3] ecmaTime.second = value[2] ecmaTime.minute = value[1] } else if len(value) > 2 { ecmaTime.second = value[2] ecmaTime.minute = value[1] } else if len(value) > 1 { ecmaTime.minute = value[1] } ecmaTime.hour = value[0] date.SetTime(ecmaTime.goTime()) object.value = *date return date.Value() } func builtinDate_setUTCHours(call FunctionCall) Value { object, date, ecmaTime, value := _builtinDate_beforeSet(call, 4, false) if ecmaTime == nil { return NaNValue() } if len(value) > 3 { ecmaTime.millisecond = value[3] ecmaTime.second = value[2] ecmaTime.minute = value[1] } else if len(value) > 2 { ecmaTime.second = value[2] ecmaTime.minute = value[1] } else if len(value) > 1 { ecmaTime.minute = value[1] } ecmaTime.hour = value[0] date.SetTime(ecmaTime.goTime()) object.value = *date return date.Value() } func builtinDate_setDate(call FunctionCall) Value { object, date, ecmaTime, value := _builtinDate_beforeSet(call, 1, true) if ecmaTime == nil { return NaNValue() } ecmaTime.day = value[0] date.SetTime(ecmaTime.goTime()) object.value = *date return date.Value() } func builtinDate_setUTCDate(call FunctionCall) Value { object, date, ecmaTime, value := _builtinDate_beforeSet(call, 1, false) if ecmaTime == nil { return NaNValue() } ecmaTime.day = value[0] date.SetTime(ecmaTime.goTime()) object.value = *date return date.Value() } func builtinDate_setMonth(call FunctionCall) Value { object, date, ecmaTime, value := _builtinDate_beforeSet(call, 2, true) if ecmaTime == nil { return NaNValue() } if len(value) > 1 { ecmaTime.day = value[1] } ecmaTime.month = value[0] date.SetTime(ecmaTime.goTime()) object.value = *date return date.Value() } func builtinDate_setUTCMonth(call FunctionCall) Value { object, date, ecmaTime, value := _builtinDate_beforeSet(call, 2, false) if ecmaTime == nil { return NaNValue() } if len(value) > 1 { ecmaTime.day = value[1] } ecmaTime.month = value[0] date.SetTime(ecmaTime.goTime()) object.value = *date return date.Value() } func builtinDate_setYear(call FunctionCall) Value { object, date, ecmaTime, value := _builtinDate_beforeSet(call, 1, true) if ecmaTime == nil { return NaNValue() } year := value[0] if 0 <= year && year <= 99 { year += 1900 } ecmaTime.year = year date.SetTime(ecmaTime.goTime()) object.value = *date return date.Value() } func builtinDate_setFullYear(call FunctionCall) Value { object, date, ecmaTime, value := _builtinDate_beforeSet(call, 3, true) if ecmaTime == nil { return NaNValue() } if len(value) > 2 { ecmaTime.day = value[2] ecmaTime.month = value[1] } else if len(value) > 1 { ecmaTime.month = value[1] } ecmaTime.year = value[0] date.SetTime(ecmaTime.goTime()) object.value = *date return date.Value() } func builtinDate_setUTCFullYear(call FunctionCall) Value { object, date, ecmaTime, value := _builtinDate_beforeSet(call, 3, false) if ecmaTime == nil { return NaNValue() } if len(value) > 2 { ecmaTime.day = value[2] ecmaTime.month = value[1] } else if len(value) > 1 { ecmaTime.month = value[1] } ecmaTime.year = value[0] date.SetTime(ecmaTime.goTime()) object.value = *date return date.Value() } // toUTCString // toISOString // toJSONString // toJSON golang-github-robertkrimen-otto-0.0~git20180617.15f95af/builtin_error.go000066400000000000000000000072471331145703200256150ustar00rootroot00000000000000package otto import ( "fmt" ) func builtinError(call FunctionCall) Value { return toValue_object(call.runtime.newError("Error", call.Argument(0), 1)) } func builtinNewError(self *_object, argumentList []Value) Value { return toValue_object(self.runtime.newError("Error", valueOfArrayIndex(argumentList, 0), 0)) } func builtinError_toString(call FunctionCall) Value { thisObject := call.thisObject() if thisObject == nil { panic(call.runtime.panicTypeError()) } name := "Error" nameValue := thisObject.get("name") if nameValue.IsDefined() { name = nameValue.string() } message := "" messageValue := thisObject.get("message") if messageValue.IsDefined() { message = messageValue.string() } if len(name) == 0 { return toValue_string(message) } if len(message) == 0 { return toValue_string(name) } return toValue_string(fmt.Sprintf("%s: %s", name, message)) } func (runtime *_runtime) newEvalError(message Value) *_object { self := runtime.newErrorObject("EvalError", message, 0) self.prototype = runtime.global.EvalErrorPrototype return self } func builtinEvalError(call FunctionCall) Value { return toValue_object(call.runtime.newEvalError(call.Argument(0))) } func builtinNewEvalError(self *_object, argumentList []Value) Value { return toValue_object(self.runtime.newEvalError(valueOfArrayIndex(argumentList, 0))) } func (runtime *_runtime) newTypeError(message Value) *_object { self := runtime.newErrorObject("TypeError", message, 0) self.prototype = runtime.global.TypeErrorPrototype return self } func builtinTypeError(call FunctionCall) Value { return toValue_object(call.runtime.newTypeError(call.Argument(0))) } func builtinNewTypeError(self *_object, argumentList []Value) Value { return toValue_object(self.runtime.newTypeError(valueOfArrayIndex(argumentList, 0))) } func (runtime *_runtime) newRangeError(message Value) *_object { self := runtime.newErrorObject("RangeError", message, 0) self.prototype = runtime.global.RangeErrorPrototype return self } func builtinRangeError(call FunctionCall) Value { return toValue_object(call.runtime.newRangeError(call.Argument(0))) } func builtinNewRangeError(self *_object, argumentList []Value) Value { return toValue_object(self.runtime.newRangeError(valueOfArrayIndex(argumentList, 0))) } func (runtime *_runtime) newURIError(message Value) *_object { self := runtime.newErrorObject("URIError", message, 0) self.prototype = runtime.global.URIErrorPrototype return self } func (runtime *_runtime) newReferenceError(message Value) *_object { self := runtime.newErrorObject("ReferenceError", message, 0) self.prototype = runtime.global.ReferenceErrorPrototype return self } func builtinReferenceError(call FunctionCall) Value { return toValue_object(call.runtime.newReferenceError(call.Argument(0))) } func builtinNewReferenceError(self *_object, argumentList []Value) Value { return toValue_object(self.runtime.newReferenceError(valueOfArrayIndex(argumentList, 0))) } func (runtime *_runtime) newSyntaxError(message Value) *_object { self := runtime.newErrorObject("SyntaxError", message, 0) self.prototype = runtime.global.SyntaxErrorPrototype return self } func builtinSyntaxError(call FunctionCall) Value { return toValue_object(call.runtime.newSyntaxError(call.Argument(0))) } func builtinNewSyntaxError(self *_object, argumentList []Value) Value { return toValue_object(self.runtime.newSyntaxError(valueOfArrayIndex(argumentList, 0))) } func builtinURIError(call FunctionCall) Value { return toValue_object(call.runtime.newURIError(call.Argument(0))) } func builtinNewURIError(self *_object, argumentList []Value) Value { return toValue_object(self.runtime.newURIError(valueOfArrayIndex(argumentList, 0))) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/builtin_function.go000066400000000000000000000071771331145703200263130ustar00rootroot00000000000000package otto import ( "fmt" "regexp" "strings" "unicode" "github.com/robertkrimen/otto/parser" ) // Function func builtinFunction(call FunctionCall) Value { return toValue_object(builtinNewFunctionNative(call.runtime, call.ArgumentList)) } func builtinNewFunction(self *_object, argumentList []Value) Value { return toValue_object(builtinNewFunctionNative(self.runtime, argumentList)) } func argumentList2parameterList(argumentList []Value) []string { parameterList := make([]string, 0, len(argumentList)) for _, value := range argumentList { tmp := strings.FieldsFunc(value.string(), func(chr rune) bool { return chr == ',' || unicode.IsSpace(chr) }) parameterList = append(parameterList, tmp...) } return parameterList } var matchIdentifier = regexp.MustCompile(`^[$_\p{L}][$_\p{L}\d}]*$`) func builtinNewFunctionNative(runtime *_runtime, argumentList []Value) *_object { var parameterList, body string count := len(argumentList) if count > 0 { tmp := make([]string, 0, count-1) for _, value := range argumentList[0 : count-1] { tmp = append(tmp, value.string()) } parameterList = strings.Join(tmp, ",") body = argumentList[count-1].string() } // FIXME function, err := parser.ParseFunction(parameterList, body) runtime.parseThrow(err) // Will panic/throw appropriately cmpl := _compiler{} cmpl_function := cmpl.parseExpression(function) return runtime.newNodeFunction(cmpl_function.(*_nodeFunctionLiteral), runtime.globalStash) } func builtinFunction_toString(call FunctionCall) Value { object := call.thisClassObject("Function") // Should throw a TypeError unless Function switch fn := object.value.(type) { case _nativeFunctionObject: return toValue_string(fmt.Sprintf("function %s() { [native code] }", fn.name)) case _nodeFunctionObject: return toValue_string(fn.node.source) case _bindFunctionObject: return toValue_string("function () { [native code] }") } panic(call.runtime.panicTypeError("Function.toString()")) } func builtinFunction_apply(call FunctionCall) Value { if !call.This.isCallable() { panic(call.runtime.panicTypeError()) } this := call.Argument(0) if this.IsUndefined() { // FIXME Not ECMA5 this = toValue_object(call.runtime.globalObject) } argumentList := call.Argument(1) switch argumentList.kind { case valueUndefined, valueNull: return call.thisObject().call(this, nil, false, nativeFrame) case valueObject: default: panic(call.runtime.panicTypeError()) } arrayObject := argumentList._object() thisObject := call.thisObject() length := int64(toUint32(arrayObject.get("length"))) valueArray := make([]Value, length) for index := int64(0); index < length; index++ { valueArray[index] = arrayObject.get(arrayIndexToString(index)) } return thisObject.call(this, valueArray, false, nativeFrame) } func builtinFunction_call(call FunctionCall) Value { if !call.This.isCallable() { panic(call.runtime.panicTypeError()) } thisObject := call.thisObject() this := call.Argument(0) if this.IsUndefined() { // FIXME Not ECMA5 this = toValue_object(call.runtime.globalObject) } if len(call.ArgumentList) >= 1 { return thisObject.call(this, call.ArgumentList[1:], false, nativeFrame) } return thisObject.call(this, nil, false, nativeFrame) } func builtinFunction_bind(call FunctionCall) Value { target := call.This if !target.isCallable() { panic(call.runtime.panicTypeError()) } targetObject := target._object() this := call.Argument(0) argumentList := call.slice(1) if this.IsUndefined() { // FIXME Do this elsewhere? this = toValue_object(call.runtime.globalObject) } return toValue_object(call.runtime.newBoundFunction(targetObject, this, argumentList)) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/builtin_json.go000066400000000000000000000170161331145703200254300ustar00rootroot00000000000000package otto import ( "bytes" "encoding/json" "fmt" "strings" ) type _builtinJSON_parseContext struct { call FunctionCall reviver Value } func builtinJSON_parse(call FunctionCall) Value { ctx := _builtinJSON_parseContext{ call: call, } revive := false if reviver := call.Argument(1); reviver.isCallable() { revive = true ctx.reviver = reviver } var root interface{} err := json.Unmarshal([]byte(call.Argument(0).string()), &root) if err != nil { panic(call.runtime.panicSyntaxError(err.Error())) } value, exists := builtinJSON_parseWalk(ctx, root) if !exists { value = Value{} } if revive { root := ctx.call.runtime.newObject() root.put("", value, false) return builtinJSON_reviveWalk(ctx, root, "") } return value } func builtinJSON_reviveWalk(ctx _builtinJSON_parseContext, holder *_object, name string) Value { value := holder.get(name) if object := value._object(); object != nil { if isArray(object) { length := int64(objectLength(object)) for index := int64(0); index < length; index += 1 { name := arrayIndexToString(index) value := builtinJSON_reviveWalk(ctx, object, name) if value.IsUndefined() { object.delete(name, false) } else { object.defineProperty(name, value, 0111, false) } } } else { object.enumerate(false, func(name string) bool { value := builtinJSON_reviveWalk(ctx, object, name) if value.IsUndefined() { object.delete(name, false) } else { object.defineProperty(name, value, 0111, false) } return true }) } } return ctx.reviver.call(ctx.call.runtime, toValue_object(holder), name, value) } func builtinJSON_parseWalk(ctx _builtinJSON_parseContext, rawValue interface{}) (Value, bool) { switch value := rawValue.(type) { case nil: return nullValue, true case bool: return toValue_bool(value), true case string: return toValue_string(value), true case float64: return toValue_float64(value), true case []interface{}: arrayValue := make([]Value, len(value)) for index, rawValue := range value { if value, exists := builtinJSON_parseWalk(ctx, rawValue); exists { arrayValue[index] = value } } return toValue_object(ctx.call.runtime.newArrayOf(arrayValue)), true case map[string]interface{}: object := ctx.call.runtime.newObject() for name, rawValue := range value { if value, exists := builtinJSON_parseWalk(ctx, rawValue); exists { object.put(name, value, false) } } return toValue_object(object), true } return Value{}, false } type _builtinJSON_stringifyContext struct { call FunctionCall stack []*_object propertyList []string replacerFunction *Value gap string } func builtinJSON_stringify(call FunctionCall) Value { ctx := _builtinJSON_stringifyContext{ call: call, stack: []*_object{nil}, } replacer := call.Argument(1)._object() if replacer != nil { if isArray(replacer) { length := objectLength(replacer) seen := map[string]bool{} propertyList := make([]string, length) length = 0 for index, _ := range propertyList { value := replacer.get(arrayIndexToString(int64(index))) switch value.kind { case valueObject: switch value.value.(*_object).class { case "String": case "Number": default: continue } case valueString: case valueNumber: default: continue } name := value.string() if seen[name] { continue } seen[name] = true length += 1 propertyList[index] = name } ctx.propertyList = propertyList[0:length] } else if replacer.class == "Function" { value := toValue_object(replacer) ctx.replacerFunction = &value } } if spaceValue, exists := call.getArgument(2); exists { if spaceValue.kind == valueObject { switch spaceValue.value.(*_object).class { case "String": spaceValue = toValue_string(spaceValue.string()) case "Number": spaceValue = spaceValue.numberValue() } } switch spaceValue.kind { case valueString: value := spaceValue.string() if len(value) > 10 { ctx.gap = value[0:10] } else { ctx.gap = value } case valueNumber: value := spaceValue.number().int64 if value > 10 { value = 10 } else if value < 0 { value = 0 } ctx.gap = strings.Repeat(" ", int(value)) } } holder := call.runtime.newObject() holder.put("", call.Argument(0), false) value, exists := builtinJSON_stringifyWalk(ctx, "", holder) if !exists { return Value{} } valueJSON, err := json.Marshal(value) if err != nil { panic(call.runtime.panicTypeError(err.Error())) } if ctx.gap != "" { valueJSON1 := bytes.Buffer{} json.Indent(&valueJSON1, valueJSON, "", ctx.gap) valueJSON = valueJSON1.Bytes() } return toValue_string(string(valueJSON)) } func builtinJSON_stringifyWalk(ctx _builtinJSON_stringifyContext, key string, holder *_object) (interface{}, bool) { value := holder.get(key) if value.IsObject() { object := value._object() if toJSON := object.get("toJSON"); toJSON.IsFunction() { value = toJSON.call(ctx.call.runtime, value, key) } else { // If the object is a GoStruct or something that implements json.Marshaler if object.objectClass.marshalJSON != nil { marshaler := object.objectClass.marshalJSON(object) if marshaler != nil { return marshaler, true } } } } if ctx.replacerFunction != nil { value = (*ctx.replacerFunction).call(ctx.call.runtime, toValue_object(holder), key, value) } if value.kind == valueObject { switch value.value.(*_object).class { case "Boolean": value = value._object().value.(Value) case "String": value = toValue_string(value.string()) case "Number": value = value.numberValue() } } switch value.kind { case valueBoolean: return value.bool(), true case valueString: return value.string(), true case valueNumber: integer := value.number() switch integer.kind { case numberInteger: return integer.int64, true case numberFloat: return integer.float64, true default: return nil, true } case valueNull: return nil, true case valueObject: holder := value._object() if value := value._object(); nil != value { for _, object := range ctx.stack { if holder == object { panic(ctx.call.runtime.panicTypeError("Converting circular structure to JSON")) } } ctx.stack = append(ctx.stack, value) defer func() { ctx.stack = ctx.stack[:len(ctx.stack)-1] }() } if isArray(holder) { var length uint32 switch value := holder.get("length").value.(type) { case uint32: length = value case int: if value >= 0 { length = uint32(value) } default: panic(ctx.call.runtime.panicTypeError(fmt.Sprintf("JSON.stringify: invalid length: %v (%[1]T)", value))) } array := make([]interface{}, length) for index, _ := range array { name := arrayIndexToString(int64(index)) value, _ := builtinJSON_stringifyWalk(ctx, name, holder) array[index] = value } return array, true } else if holder.class != "Function" { object := map[string]interface{}{} if ctx.propertyList != nil { for _, name := range ctx.propertyList { value, exists := builtinJSON_stringifyWalk(ctx, name, holder) if exists { object[name] = value } } } else { // Go maps are without order, so this doesn't conform to the ECMA ordering // standard, but oh well... holder.enumerate(false, func(name string) bool { value, exists := builtinJSON_stringifyWalk(ctx, name, holder) if exists { object[name] = value } return true }) } return object, true } } return nil, false } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/builtin_math.go000066400000000000000000000067311331145703200254120ustar00rootroot00000000000000package otto import ( "math" "math/rand" ) // Math func builtinMath_abs(call FunctionCall) Value { number := call.Argument(0).float64() return toValue_float64(math.Abs(number)) } func builtinMath_acos(call FunctionCall) Value { number := call.Argument(0).float64() return toValue_float64(math.Acos(number)) } func builtinMath_asin(call FunctionCall) Value { number := call.Argument(0).float64() return toValue_float64(math.Asin(number)) } func builtinMath_atan(call FunctionCall) Value { number := call.Argument(0).float64() return toValue_float64(math.Atan(number)) } func builtinMath_atan2(call FunctionCall) Value { y := call.Argument(0).float64() if math.IsNaN(y) { return NaNValue() } x := call.Argument(1).float64() if math.IsNaN(x) { return NaNValue() } return toValue_float64(math.Atan2(y, x)) } func builtinMath_cos(call FunctionCall) Value { number := call.Argument(0).float64() return toValue_float64(math.Cos(number)) } func builtinMath_ceil(call FunctionCall) Value { number := call.Argument(0).float64() return toValue_float64(math.Ceil(number)) } func builtinMath_exp(call FunctionCall) Value { number := call.Argument(0).float64() return toValue_float64(math.Exp(number)) } func builtinMath_floor(call FunctionCall) Value { number := call.Argument(0).float64() return toValue_float64(math.Floor(number)) } func builtinMath_log(call FunctionCall) Value { number := call.Argument(0).float64() return toValue_float64(math.Log(number)) } func builtinMath_max(call FunctionCall) Value { switch len(call.ArgumentList) { case 0: return negativeInfinityValue() case 1: return toValue_float64(call.ArgumentList[0].float64()) } result := call.ArgumentList[0].float64() if math.IsNaN(result) { return NaNValue() } for _, value := range call.ArgumentList[1:] { value := value.float64() if math.IsNaN(value) { return NaNValue() } result = math.Max(result, value) } return toValue_float64(result) } func builtinMath_min(call FunctionCall) Value { switch len(call.ArgumentList) { case 0: return positiveInfinityValue() case 1: return toValue_float64(call.ArgumentList[0].float64()) } result := call.ArgumentList[0].float64() if math.IsNaN(result) { return NaNValue() } for _, value := range call.ArgumentList[1:] { value := value.float64() if math.IsNaN(value) { return NaNValue() } result = math.Min(result, value) } return toValue_float64(result) } func builtinMath_pow(call FunctionCall) Value { // TODO Make sure this works according to the specification (15.8.2.13) x := call.Argument(0).float64() y := call.Argument(1).float64() if math.Abs(x) == 1 && math.IsInf(y, 0) { return NaNValue() } return toValue_float64(math.Pow(x, y)) } func builtinMath_random(call FunctionCall) Value { var v float64 if call.runtime.random != nil { v = call.runtime.random() } else { v = rand.Float64() } return toValue_float64(v) } func builtinMath_round(call FunctionCall) Value { number := call.Argument(0).float64() value := math.Floor(number + 0.5) if value == 0 { value = math.Copysign(0, number) } return toValue_float64(value) } func builtinMath_sin(call FunctionCall) Value { number := call.Argument(0).float64() return toValue_float64(math.Sin(number)) } func builtinMath_sqrt(call FunctionCall) Value { number := call.Argument(0).float64() return toValue_float64(math.Sqrt(number)) } func builtinMath_tan(call FunctionCall) Value { number := call.Argument(0).float64() return toValue_float64(math.Tan(number)) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/builtin_number.go000066400000000000000000000051421331145703200257440ustar00rootroot00000000000000package otto import ( "math" "strconv" ) // Number func numberValueFromNumberArgumentList(argumentList []Value) Value { if len(argumentList) > 0 { return argumentList[0].numberValue() } return toValue_int(0) } func builtinNumber(call FunctionCall) Value { return numberValueFromNumberArgumentList(call.ArgumentList) } func builtinNewNumber(self *_object, argumentList []Value) Value { return toValue_object(self.runtime.newNumber(numberValueFromNumberArgumentList(argumentList))) } func builtinNumber_toString(call FunctionCall) Value { // Will throw a TypeError if ThisObject is not a Number value := call.thisClassObject("Number").primitiveValue() radix := 10 radixArgument := call.Argument(0) if radixArgument.IsDefined() { integer := toIntegerFloat(radixArgument) if integer < 2 || integer > 36 { panic(call.runtime.panicRangeError("toString() radix must be between 2 and 36")) } radix = int(integer) } if radix == 10 { return toValue_string(value.string()) } return toValue_string(numberToStringRadix(value, radix)) } func builtinNumber_valueOf(call FunctionCall) Value { return call.thisClassObject("Number").primitiveValue() } func builtinNumber_toFixed(call FunctionCall) Value { precision := toIntegerFloat(call.Argument(0)) if 20 < precision || 0 > precision { panic(call.runtime.panicRangeError("toFixed() precision must be between 0 and 20")) } if call.This.IsNaN() { return toValue_string("NaN") } value := call.This.float64() if math.Abs(value) >= 1e21 { return toValue_string(floatToString(value, 64)) } return toValue_string(strconv.FormatFloat(call.This.float64(), 'f', int(precision), 64)) } func builtinNumber_toExponential(call FunctionCall) Value { if call.This.IsNaN() { return toValue_string("NaN") } precision := float64(-1) if value := call.Argument(0); value.IsDefined() { precision = toIntegerFloat(value) if 0 > precision { panic(call.runtime.panicRangeError("toString() radix must be between 2 and 36")) } } return toValue_string(strconv.FormatFloat(call.This.float64(), 'e', int(precision), 64)) } func builtinNumber_toPrecision(call FunctionCall) Value { if call.This.IsNaN() { return toValue_string("NaN") } value := call.Argument(0) if value.IsUndefined() { return toValue_string(call.This.string()) } precision := toIntegerFloat(value) if 1 > precision { panic(call.runtime.panicRangeError("toPrecision() precision must be greater than 1")) } return toValue_string(strconv.FormatFloat(call.This.float64(), 'g', int(precision), 64)) } func builtinNumber_toLocaleString(call FunctionCall) Value { return builtinNumber_toString(call) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/builtin_object.go000066400000000000000000000165761331145703200257370ustar00rootroot00000000000000package otto import ( "fmt" ) // Object func builtinObject(call FunctionCall) Value { value := call.Argument(0) switch value.kind { case valueUndefined, valueNull: return toValue_object(call.runtime.newObject()) } return toValue_object(call.runtime.toObject(value)) } func builtinNewObject(self *_object, argumentList []Value) Value { value := valueOfArrayIndex(argumentList, 0) switch value.kind { case valueNull, valueUndefined: case valueNumber, valueString, valueBoolean: return toValue_object(self.runtime.toObject(value)) case valueObject: return value default: } return toValue_object(self.runtime.newObject()) } func builtinObject_valueOf(call FunctionCall) Value { return toValue_object(call.thisObject()) } func builtinObject_hasOwnProperty(call FunctionCall) Value { propertyName := call.Argument(0).string() thisObject := call.thisObject() return toValue_bool(thisObject.hasOwnProperty(propertyName)) } func builtinObject_isPrototypeOf(call FunctionCall) Value { value := call.Argument(0) if !value.IsObject() { return falseValue } prototype := call.toObject(value).prototype thisObject := call.thisObject() for prototype != nil { if thisObject == prototype { return trueValue } prototype = prototype.prototype } return falseValue } func builtinObject_propertyIsEnumerable(call FunctionCall) Value { propertyName := call.Argument(0).string() thisObject := call.thisObject() property := thisObject.getOwnProperty(propertyName) if property != nil && property.enumerable() { return trueValue } return falseValue } func builtinObject_toString(call FunctionCall) Value { result := "" if call.This.IsUndefined() { result = "[object Undefined]" } else if call.This.IsNull() { result = "[object Null]" } else { result = fmt.Sprintf("[object %s]", call.thisObject().class) } return toValue_string(result) } func builtinObject_toLocaleString(call FunctionCall) Value { toString := call.thisObject().get("toString") if !toString.isCallable() { panic(call.runtime.panicTypeError()) } return toString.call(call.runtime, call.This) } func builtinObject_getPrototypeOf(call FunctionCall) Value { objectValue := call.Argument(0) object := objectValue._object() if object == nil { panic(call.runtime.panicTypeError()) } if object.prototype == nil { return nullValue } return toValue_object(object.prototype) } func builtinObject_getOwnPropertyDescriptor(call FunctionCall) Value { objectValue := call.Argument(0) object := objectValue._object() if object == nil { panic(call.runtime.panicTypeError()) } name := call.Argument(1).string() descriptor := object.getOwnProperty(name) if descriptor == nil { return Value{} } return toValue_object(call.runtime.fromPropertyDescriptor(*descriptor)) } func builtinObject_defineProperty(call FunctionCall) Value { objectValue := call.Argument(0) object := objectValue._object() if object == nil { panic(call.runtime.panicTypeError()) } name := call.Argument(1).string() descriptor := toPropertyDescriptor(call.runtime, call.Argument(2)) object.defineOwnProperty(name, descriptor, true) return objectValue } func builtinObject_defineProperties(call FunctionCall) Value { objectValue := call.Argument(0) object := objectValue._object() if object == nil { panic(call.runtime.panicTypeError()) } properties := call.runtime.toObject(call.Argument(1)) properties.enumerate(false, func(name string) bool { descriptor := toPropertyDescriptor(call.runtime, properties.get(name)) object.defineOwnProperty(name, descriptor, true) return true }) return objectValue } func builtinObject_create(call FunctionCall) Value { prototypeValue := call.Argument(0) if !prototypeValue.IsNull() && !prototypeValue.IsObject() { panic(call.runtime.panicTypeError()) } object := call.runtime.newObject() object.prototype = prototypeValue._object() propertiesValue := call.Argument(1) if propertiesValue.IsDefined() { properties := call.runtime.toObject(propertiesValue) properties.enumerate(false, func(name string) bool { descriptor := toPropertyDescriptor(call.runtime, properties.get(name)) object.defineOwnProperty(name, descriptor, true) return true }) } return toValue_object(object) } func builtinObject_isExtensible(call FunctionCall) Value { object := call.Argument(0) if object := object._object(); object != nil { return toValue_bool(object.extensible) } panic(call.runtime.panicTypeError()) } func builtinObject_preventExtensions(call FunctionCall) Value { object := call.Argument(0) if object := object._object(); object != nil { object.extensible = false } else { panic(call.runtime.panicTypeError()) } return object } func builtinObject_isSealed(call FunctionCall) Value { object := call.Argument(0) if object := object._object(); object != nil { if object.extensible { return toValue_bool(false) } result := true object.enumerate(true, func(name string) bool { property := object.getProperty(name) if property.configurable() { result = false } return true }) return toValue_bool(result) } panic(call.runtime.panicTypeError()) } func builtinObject_seal(call FunctionCall) Value { object := call.Argument(0) if object := object._object(); object != nil { object.enumerate(true, func(name string) bool { if property := object.getOwnProperty(name); nil != property && property.configurable() { property.configureOff() object.defineOwnProperty(name, *property, true) } return true }) object.extensible = false } else { panic(call.runtime.panicTypeError()) } return object } func builtinObject_isFrozen(call FunctionCall) Value { object := call.Argument(0) if object := object._object(); object != nil { if object.extensible { return toValue_bool(false) } result := true object.enumerate(true, func(name string) bool { property := object.getProperty(name) if property.configurable() || property.writable() { result = false } return true }) return toValue_bool(result) } panic(call.runtime.panicTypeError()) } func builtinObject_freeze(call FunctionCall) Value { object := call.Argument(0) if object := object._object(); object != nil { object.enumerate(true, func(name string) bool { if property, update := object.getOwnProperty(name), false; nil != property { if property.isDataDescriptor() && property.writable() { property.writeOff() update = true } if property.configurable() { property.configureOff() update = true } if update { object.defineOwnProperty(name, *property, true) } } return true }) object.extensible = false } else { panic(call.runtime.panicTypeError()) } return object } func builtinObject_keys(call FunctionCall) Value { if object, keys := call.Argument(0)._object(), []Value(nil); nil != object { object.enumerate(false, func(name string) bool { keys = append(keys, toValue_string(name)) return true }) return toValue_object(call.runtime.newArrayOf(keys)) } panic(call.runtime.panicTypeError()) } func builtinObject_getOwnPropertyNames(call FunctionCall) Value { if object, propertyNames := call.Argument(0)._object(), []Value(nil); nil != object { object.enumerate(true, func(name string) bool { if object.hasOwnProperty(name) { propertyNames = append(propertyNames, toValue_string(name)) } return true }) return toValue_object(call.runtime.newArrayOf(propertyNames)) } panic(call.runtime.panicTypeError()) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/builtin_regexp.go000066400000000000000000000032101331145703200257400ustar00rootroot00000000000000package otto import ( "fmt" ) // RegExp func builtinRegExp(call FunctionCall) Value { pattern := call.Argument(0) flags := call.Argument(1) if object := pattern._object(); object != nil { if object.class == "RegExp" && flags.IsUndefined() { return pattern } } return toValue_object(call.runtime.newRegExp(pattern, flags)) } func builtinNewRegExp(self *_object, argumentList []Value) Value { return toValue_object(self.runtime.newRegExp( valueOfArrayIndex(argumentList, 0), valueOfArrayIndex(argumentList, 1), )) } func builtinRegExp_toString(call FunctionCall) Value { thisObject := call.thisObject() source := thisObject.get("source").string() flags := []byte{} if thisObject.get("global").bool() { flags = append(flags, 'g') } if thisObject.get("ignoreCase").bool() { flags = append(flags, 'i') } if thisObject.get("multiline").bool() { flags = append(flags, 'm') } return toValue_string(fmt.Sprintf("/%s/%s", source, flags)) } func builtinRegExp_exec(call FunctionCall) Value { thisObject := call.thisObject() target := call.Argument(0).string() match, result := execRegExp(thisObject, target) if !match { return nullValue } return toValue_object(execResultToArray(call.runtime, target, result)) } func builtinRegExp_test(call FunctionCall) Value { thisObject := call.thisObject() target := call.Argument(0).string() match, _ := execRegExp(thisObject, target) return toValue_bool(match) } func builtinRegExp_compile(call FunctionCall) Value { // This (useless) function is deprecated, but is here to provide some // semblance of compatibility. // Caveat emptor: it may not be around for long. return Value{} } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/builtin_string.go000066400000000000000000000327211331145703200257650ustar00rootroot00000000000000package otto import ( "bytes" "regexp" "strconv" "strings" "unicode/utf8" ) // String func stringValueFromStringArgumentList(argumentList []Value) Value { if len(argumentList) > 0 { return toValue_string(argumentList[0].string()) } return toValue_string("") } func builtinString(call FunctionCall) Value { return stringValueFromStringArgumentList(call.ArgumentList) } func builtinNewString(self *_object, argumentList []Value) Value { return toValue_object(self.runtime.newString(stringValueFromStringArgumentList(argumentList))) } func builtinString_toString(call FunctionCall) Value { return call.thisClassObject("String").primitiveValue() } func builtinString_valueOf(call FunctionCall) Value { return call.thisClassObject("String").primitiveValue() } func builtinString_fromCharCode(call FunctionCall) Value { chrList := make([]uint16, len(call.ArgumentList)) for index, value := range call.ArgumentList { chrList[index] = toUint16(value) } return toValue_string16(chrList) } func builtinString_charAt(call FunctionCall) Value { checkObjectCoercible(call.runtime, call.This) idx := int(call.Argument(0).number().int64) chr := stringAt(call.This._object().stringValue(), idx) if chr == utf8.RuneError { return toValue_string("") } return toValue_string(string(chr)) } func builtinString_charCodeAt(call FunctionCall) Value { checkObjectCoercible(call.runtime, call.This) idx := int(call.Argument(0).number().int64) chr := stringAt(call.This._object().stringValue(), idx) if chr == utf8.RuneError { return NaNValue() } return toValue_uint16(uint16(chr)) } func builtinString_concat(call FunctionCall) Value { checkObjectCoercible(call.runtime, call.This) var value bytes.Buffer value.WriteString(call.This.string()) for _, item := range call.ArgumentList { value.WriteString(item.string()) } return toValue_string(value.String()) } func builtinString_indexOf(call FunctionCall) Value { checkObjectCoercible(call.runtime, call.This) value := call.This.string() target := call.Argument(0).string() if 2 > len(call.ArgumentList) { return toValue_int(strings.Index(value, target)) } start := toIntegerFloat(call.Argument(1)) if 0 > start { start = 0 } else if start >= float64(len(value)) { if target == "" { return toValue_int(len(value)) } return toValue_int(-1) } index := strings.Index(value[int(start):], target) if index >= 0 { index += int(start) } return toValue_int(index) } func builtinString_lastIndexOf(call FunctionCall) Value { checkObjectCoercible(call.runtime, call.This) value := call.This.string() target := call.Argument(0).string() if 2 > len(call.ArgumentList) || call.ArgumentList[1].IsUndefined() { return toValue_int(strings.LastIndex(value, target)) } length := len(value) if length == 0 { return toValue_int(strings.LastIndex(value, target)) } start := call.ArgumentList[1].number() if start.kind == numberInfinity { // FIXME // startNumber is infinity, so start is the end of string (start = length) return toValue_int(strings.LastIndex(value, target)) } if 0 > start.int64 { start.int64 = 0 } end := int(start.int64) + len(target) if end > length { end = length } return toValue_int(strings.LastIndex(value[:end], target)) } func builtinString_match(call FunctionCall) Value { checkObjectCoercible(call.runtime, call.This) target := call.This.string() matcherValue := call.Argument(0) matcher := matcherValue._object() if !matcherValue.IsObject() || matcher.class != "RegExp" { matcher = call.runtime.newRegExp(matcherValue, Value{}) } global := matcher.get("global").bool() if !global { match, result := execRegExp(matcher, target) if !match { return nullValue } return toValue_object(execResultToArray(call.runtime, target, result)) } { result := matcher.regExpValue().regularExpression.FindAllStringIndex(target, -1) matchCount := len(result) if result == nil { matcher.put("lastIndex", toValue_int(0), true) return Value{} // !match } matchCount = len(result) valueArray := make([]Value, matchCount) for index := 0; index < matchCount; index++ { valueArray[index] = toValue_string(target[result[index][0]:result[index][1]]) } matcher.put("lastIndex", toValue_int(result[matchCount-1][1]), true) return toValue_object(call.runtime.newArrayOf(valueArray)) } } var builtinString_replace_Regexp = regexp.MustCompile("\\$(?:[\\$\\&\\'\\`1-9]|0[1-9]|[1-9][0-9])") func builtinString_findAndReplaceString(input []byte, lastIndex int, match []int, target []byte, replaceValue []byte) (output []byte) { matchCount := len(match) / 2 output = input if match[0] != lastIndex { output = append(output, target[lastIndex:match[0]]...) } replacement := builtinString_replace_Regexp.ReplaceAllFunc(replaceValue, func(part []byte) []byte { // TODO Check if match[0] or match[1] can be -1 in this scenario switch part[1] { case '$': return []byte{'$'} case '&': return target[match[0]:match[1]] case '`': return target[:match[0]] case '\'': return target[match[1]:len(target)] } matchNumberParse, error := strconv.ParseInt(string(part[1:]), 10, 64) matchNumber := int(matchNumberParse) if error != nil || matchNumber >= matchCount { return []byte{} } offset := 2 * matchNumber if match[offset] != -1 { return target[match[offset]:match[offset+1]] } return []byte{} // The empty string }) output = append(output, replacement...) return output } func builtinString_replace(call FunctionCall) Value { checkObjectCoercible(call.runtime, call.This) target := []byte(call.This.string()) searchValue := call.Argument(0) searchObject := searchValue._object() // TODO If a capture is -1? var search *regexp.Regexp global := false find := 1 if searchValue.IsObject() && searchObject.class == "RegExp" { regExp := searchObject.regExpValue() search = regExp.regularExpression if regExp.global { find = -1 } } else { search = regexp.MustCompile(regexp.QuoteMeta(searchValue.string())) } found := search.FindAllSubmatchIndex(target, find) if found == nil { return toValue_string(string(target)) // !match } { lastIndex := 0 result := []byte{} replaceValue := call.Argument(1) if replaceValue.isCallable() { target := string(target) replace := replaceValue._object() for _, match := range found { if match[0] != lastIndex { result = append(result, target[lastIndex:match[0]]...) } matchCount := len(match) / 2 argumentList := make([]Value, matchCount+2) for index := 0; index < matchCount; index++ { offset := 2 * index if match[offset] != -1 { argumentList[index] = toValue_string(target[match[offset]:match[offset+1]]) } else { argumentList[index] = Value{} } } argumentList[matchCount+0] = toValue_int(match[0]) argumentList[matchCount+1] = toValue_string(target) replacement := replace.call(Value{}, argumentList, false, nativeFrame).string() result = append(result, []byte(replacement)...) lastIndex = match[1] } } else { replace := []byte(replaceValue.string()) for _, match := range found { result = builtinString_findAndReplaceString(result, lastIndex, match, target, replace) lastIndex = match[1] } } if lastIndex != len(target) { result = append(result, target[lastIndex:]...) } if global && searchObject != nil { searchObject.put("lastIndex", toValue_int(lastIndex), true) } return toValue_string(string(result)) } } func builtinString_search(call FunctionCall) Value { checkObjectCoercible(call.runtime, call.This) target := call.This.string() searchValue := call.Argument(0) search := searchValue._object() if !searchValue.IsObject() || search.class != "RegExp" { search = call.runtime.newRegExp(searchValue, Value{}) } result := search.regExpValue().regularExpression.FindStringIndex(target) if result == nil { return toValue_int(-1) } return toValue_int(result[0]) } func stringSplitMatch(target string, targetLength int64, index uint, search string, searchLength int64) (bool, uint) { if int64(index)+searchLength > searchLength { return false, 0 } found := strings.Index(target[index:], search) if 0 > found { return false, 0 } return true, uint(found) } func builtinString_split(call FunctionCall) Value { checkObjectCoercible(call.runtime, call.This) target := call.This.string() separatorValue := call.Argument(0) limitValue := call.Argument(1) limit := -1 if limitValue.IsDefined() { limit = int(toUint32(limitValue)) } if limit == 0 { return toValue_object(call.runtime.newArray(0)) } if separatorValue.IsUndefined() { return toValue_object(call.runtime.newArrayOf([]Value{toValue_string(target)})) } if separatorValue.isRegExp() { targetLength := len(target) search := separatorValue._object().regExpValue().regularExpression valueArray := []Value{} result := search.FindAllStringSubmatchIndex(target, -1) lastIndex := 0 found := 0 for _, match := range result { if match[0] == match[1] { // FIXME Ugh, this is a hack if match[0] == 0 || match[0] == targetLength { continue } } if lastIndex != match[0] { valueArray = append(valueArray, toValue_string(target[lastIndex:match[0]])) found++ } else if lastIndex == match[0] { if lastIndex != -1 { valueArray = append(valueArray, toValue_string("")) found++ } } lastIndex = match[1] if found == limit { goto RETURN } captureCount := len(match) / 2 for index := 1; index < captureCount; index++ { offset := index * 2 value := Value{} if match[offset] != -1 { value = toValue_string(target[match[offset]:match[offset+1]]) } valueArray = append(valueArray, value) found++ if found == limit { goto RETURN } } } if found != limit { if lastIndex != targetLength { valueArray = append(valueArray, toValue_string(target[lastIndex:targetLength])) } else { valueArray = append(valueArray, toValue_string("")) } } RETURN: return toValue_object(call.runtime.newArrayOf(valueArray)) } else { separator := separatorValue.string() splitLimit := limit excess := false if limit > 0 { splitLimit = limit + 1 excess = true } split := strings.SplitN(target, separator, splitLimit) if excess && len(split) > limit { split = split[:limit] } valueArray := make([]Value, len(split)) for index, value := range split { valueArray[index] = toValue_string(value) } return toValue_object(call.runtime.newArrayOf(valueArray)) } } func builtinString_slice(call FunctionCall) Value { checkObjectCoercible(call.runtime, call.This) target := call.This.string() length := int64(len(target)) start, end := rangeStartEnd(call.ArgumentList, length, false) if end-start <= 0 { return toValue_string("") } return toValue_string(target[start:end]) } func builtinString_substring(call FunctionCall) Value { checkObjectCoercible(call.runtime, call.This) target := call.This.string() length := int64(len(target)) start, end := rangeStartEnd(call.ArgumentList, length, true) if start > end { start, end = end, start } return toValue_string(target[start:end]) } func builtinString_substr(call FunctionCall) Value { target := call.This.string() size := int64(len(target)) start, length := rangeStartLength(call.ArgumentList, size) if start >= size { return toValue_string("") } if length <= 0 { return toValue_string("") } if start+length >= size { // Cap length to be to the end of the string // start = 3, length = 5, size = 4 [0, 1, 2, 3] // 4 - 3 = 1 // target[3:4] length = size - start } return toValue_string(target[start : start+length]) } func builtinString_toLowerCase(call FunctionCall) Value { checkObjectCoercible(call.runtime, call.This) return toValue_string(strings.ToLower(call.This.string())) } func builtinString_toUpperCase(call FunctionCall) Value { checkObjectCoercible(call.runtime, call.This) return toValue_string(strings.ToUpper(call.This.string())) } // 7.2 Table 2 — Whitespace Characters & 7.3 Table 3 - Line Terminator Characters const builtinString_trim_whitespace = "\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF" func builtinString_trim(call FunctionCall) Value { checkObjectCoercible(call.runtime, call.This) return toValue(strings.Trim(call.This.string(), builtinString_trim_whitespace)) } // Mozilla extension, not ECMAScript 5 func builtinString_trimLeft(call FunctionCall) Value { checkObjectCoercible(call.runtime, call.This) return toValue(strings.TrimLeft(call.This.string(), builtinString_trim_whitespace)) } // Mozilla extension, not ECMAScript 5 func builtinString_trimRight(call FunctionCall) Value { checkObjectCoercible(call.runtime, call.This) return toValue(strings.TrimRight(call.This.string(), builtinString_trim_whitespace)) } func builtinString_localeCompare(call FunctionCall) Value { checkObjectCoercible(call.runtime, call.This) this := call.This.string() that := call.Argument(0).string() if this < that { return toValue_int(-1) } else if this == that { return toValue_int(0) } return toValue_int(1) } /* An alternate version of String.trim func builtinString_trim(call FunctionCall) Value { checkObjectCoercible(call.This) return toValue_string(strings.TrimFunc(call.string(.This), isWhiteSpaceOrLineTerminator)) } */ func builtinString_toLocaleLowerCase(call FunctionCall) Value { return builtinString_toLowerCase(call) } func builtinString_toLocaleUpperCase(call FunctionCall) Value { return builtinString_toUpperCase(call) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/builtin_test.go000066400000000000000000000063051331145703200254350ustar00rootroot00000000000000package otto import ( "testing" ) func TestString_substr(t *testing.T) { tt(t, func() { test, _ := test() test(` [ "abc".substr(0,1), // "a" "abc".substr(0,2), // "ab" "abc".substr(0,3), // "abc" "abc".substr(0,4), // "abc" "abc".substr(0,9), // "abc" ]; `, "a,ab,abc,abc,abc") test(` [ "abc".substr(1,1), // "b" "abc".substr(1,2), // "bc" "abc".substr(1,3), // "bc" "abc".substr(1,4), // "bc" "abc".substr(1,9), // "bc" ]; `, "b,bc,bc,bc,bc") test(` [ "abc".substr(2,1), // "c" "abc".substr(2,2), // "c" "abc".substr(2,3), // "c" "abc".substr(2,4), // "c" "abc".substr(2,9), // "c" ]; `, "c,c,c,c,c") test(` [ "abc".substr(3,1), // "" "abc".substr(3,2), // "" "abc".substr(3,3), // "" "abc".substr(3,4), // "" "abc".substr(3,9), // "" ]; `, ",,,,") test(` [ "abc".substr(0), // "abc" "abc".substr(1), // "bc" "abc".substr(2), // "c" "abc".substr(3), // "" "abc".substr(9), // "" ]; `, "abc,bc,c,,") test(` [ "abc".substr(-9), // "abc" "abc".substr(-3), // "abc" "abc".substr(-2), // "bc" "abc".substr(-1), // "c" ]; `, "abc,abc,bc,c") test(` [ "abc".substr(-9, 1), // "a" "abc".substr(-3, 1), // "a" "abc".substr(-2, 1), // "b" "abc".substr(-1, 1), // "c" "abc".substr(-1, 2), // "c" ]; `, "a,a,b,c,c") test(`"abcd".substr(3, 5)`, "d") }) } func Test_builtin_escape(t *testing.T) { tt(t, func() { is(builtin_escape("abc"), "abc") is(builtin_escape("="), "%3D") is(builtin_escape("abc=%+32"), "abc%3D%25+32") is(builtin_escape("世界"), "%u4E16%u754C") }) } func Test_builtin_unescape(t *testing.T) { tt(t, func() { is(builtin_unescape("abc"), "abc") is(builtin_unescape("=%3D"), "==") is(builtin_unescape("abc%3D%25+32"), "abc=%+32") is(builtin_unescape("%u4E16%u754C"), "世界") }) } func TestGlobal_escape(t *testing.T) { tt(t, func() { test, _ := test() test(` [ escape("abc"), // "abc" escape("="), // "%3D" escape("abc=%+32"), // "abc%3D%25+32" escape("\u4e16\u754c"), // "%u4E16%u754C" ]; `, "abc,%3D,abc%3D%25+32,%u4E16%u754C") }) } func TestGlobal_unescape(t *testing.T) { tt(t, func() { test, _ := test() test(` [ unescape("abc"), // "abc" unescape("=%3D"), // "==" unescape("abc%3D%25+32"), // "abc=%+32" unescape("%u4E16%u754C"), // "世界" ]; `, "abc,==,abc=%+32,世界") }) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/call_test.go000066400000000000000000000635761331145703200247170ustar00rootroot00000000000000package otto import ( "reflect" "testing" ) func BenchmarkNativeCallWithString(b *testing.B) { vm := New() vm.Set("x", func(a1 string) {}) s, _ := vm.Compile("test.js", `x("zzz")`) for i := 0; i < b.N; i++ { vm.Run(s) } } func BenchmarkNativeCallWithFloat32(b *testing.B) { vm := New() vm.Set("x", func(a1 float32) {}) s, _ := vm.Compile("test.js", `x(1.1)`) for i := 0; i < b.N; i++ { vm.Run(s) } } func BenchmarkNativeCallWithFloat64(b *testing.B) { vm := New() vm.Set("x", func(a1 float64) {}) s, _ := vm.Compile("test.js", `x(1.1)`) for i := 0; i < b.N; i++ { vm.Run(s) } } func BenchmarkNativeCallWithInt(b *testing.B) { vm := New() vm.Set("x", func(a1 int) {}) s, _ := vm.Compile("test.js", `x(1)`) for i := 0; i < b.N; i++ { vm.Run(s) } } func BenchmarkNativeCallWithUint(b *testing.B) { vm := New() vm.Set("x", func(a1 uint) {}) s, _ := vm.Compile("test.js", `x(1)`) for i := 0; i < b.N; i++ { vm.Run(s) } } func BenchmarkNativeCallWithInt8(b *testing.B) { vm := New() vm.Set("x", func(a1 int8) {}) s, _ := vm.Compile("test.js", `x(1)`) for i := 0; i < b.N; i++ { vm.Run(s) } } func BenchmarkNativeCallWithUint8(b *testing.B) { vm := New() vm.Set("x", func(a1 uint8) {}) s, _ := vm.Compile("test.js", `x(1)`) for i := 0; i < b.N; i++ { vm.Run(s) } } func BenchmarkNativeCallWithInt16(b *testing.B) { vm := New() vm.Set("x", func(a1 int16) {}) s, _ := vm.Compile("test.js", `x(1)`) for i := 0; i < b.N; i++ { vm.Run(s) } } func BenchmarkNativeCallWithUint16(b *testing.B) { vm := New() vm.Set("x", func(a1 uint16) {}) s, _ := vm.Compile("test.js", `x(1)`) for i := 0; i < b.N; i++ { vm.Run(s) } } func BenchmarkNativeCallWithInt32(b *testing.B) { vm := New() vm.Set("x", func(a1 int32) {}) s, _ := vm.Compile("test.js", `x(1)`) for i := 0; i < b.N; i++ { vm.Run(s) } } func BenchmarkNativeCallWithUint32(b *testing.B) { vm := New() vm.Set("x", func(a1 uint32) {}) s, _ := vm.Compile("test.js", `x(1)`) for i := 0; i < b.N; i++ { vm.Run(s) } } func BenchmarkNativeCallWithInt64(b *testing.B) { vm := New() vm.Set("x", func(a1 int64) {}) s, _ := vm.Compile("test.js", `x(1)`) for i := 0; i < b.N; i++ { vm.Run(s) } } func BenchmarkNativeCallWithUint64(b *testing.B) { vm := New() vm.Set("x", func(a1 uint64) {}) s, _ := vm.Compile("test.js", `x(1)`) for i := 0; i < b.N; i++ { vm.Run(s) } } func BenchmarkNativeCallWithStringInt(b *testing.B) { vm := New() vm.Set("x", func(a1 string, a2 int) {}) s, _ := vm.Compile("test.js", `x("zzz", 1)`) for i := 0; i < b.N; i++ { vm.Run(s) } } func BenchmarkNativeCallWithIntVariadic0(b *testing.B) { vm := New() vm.Set("x", func(a ...int) {}) s, _ := vm.Compile("test.js", `x()`) for i := 0; i < b.N; i++ { vm.Run(s) } } func BenchmarkNativeCallWithIntVariadic1(b *testing.B) { vm := New() vm.Set("x", func(a ...int) {}) s, _ := vm.Compile("test.js", `x(1)`) for i := 0; i < b.N; i++ { vm.Run(s) } } func BenchmarkNativeCallWithIntVariadic3(b *testing.B) { vm := New() vm.Set("x", func(a ...int) {}) s, _ := vm.Compile("test.js", `x(1, 2, 3)`) for i := 0; i < b.N; i++ { vm.Run(s) } } func BenchmarkNativeCallWithIntVariadic10(b *testing.B) { vm := New() vm.Set("x", func(a ...int) {}) s, _ := vm.Compile("test.js", `x(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)`) for i := 0; i < b.N; i++ { vm.Run(s) } } func BenchmarkNativeCallWithIntArray0(b *testing.B) { vm := New() vm.Set("x", func(a []int) {}) s, _ := vm.Compile("test.js", `x([])`) for i := 0; i < b.N; i++ { vm.Run(s) } } func BenchmarkNativeCallWithIntArray1(b *testing.B) { vm := New() vm.Set("x", func(a []int) {}) s, _ := vm.Compile("test.js", `x([1])`) for i := 0; i < b.N; i++ { vm.Run(s) } } func BenchmarkNativeCallWithIntArray3(b *testing.B) { vm := New() vm.Set("x", func(a []int) {}) s, _ := vm.Compile("test.js", `x([1, 2, 3])`) for i := 0; i < b.N; i++ { vm.Run(s) } } func BenchmarkNativeCallWithIntArray10(b *testing.B) { vm := New() vm.Set("x", func(a []int) {}) s, _ := vm.Compile("test.js", `x([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])`) for i := 0; i < b.N; i++ { vm.Run(s) } } func BenchmarkNativeCallWithIntVariadicArray0(b *testing.B) { vm := New() vm.Set("x", func(a ...int) {}) s, _ := vm.Compile("test.js", `x([])`) for i := 0; i < b.N; i++ { vm.Run(s) } } func BenchmarkNativeCallWithIntVariadicArray1(b *testing.B) { vm := New() vm.Set("x", func(a ...int) {}) s, _ := vm.Compile("test.js", `x([1])`) for i := 0; i < b.N; i++ { vm.Run(s) } } func BenchmarkNativeCallWithIntVariadicArray3(b *testing.B) { vm := New() vm.Set("x", func(a ...int) {}) s, _ := vm.Compile("test.js", `x([1, 2, 3])`) for i := 0; i < b.N; i++ { vm.Run(s) } } func BenchmarkNativeCallWithIntVariadicArray10(b *testing.B) { vm := New() vm.Set("x", func(a ...int) {}) s, _ := vm.Compile("test.js", `x([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])`) for i := 0; i < b.N; i++ { vm.Run(s) } } func BenchmarkNativeCallWithStringIntVariadic0(b *testing.B) { vm := New() vm.Set("x", func(a1 string, a2 ...int) {}) s, _ := vm.Compile("test.js", `x("a")`) for i := 0; i < b.N; i++ { vm.Run(s) } } func BenchmarkNativeCallWithStringIntVariadic1(b *testing.B) { vm := New() vm.Set("x", func(a1 string, a2 ...int) {}) s, _ := vm.Compile("test.js", `x("a", 1)`) for i := 0; i < b.N; i++ { vm.Run(s) } } func BenchmarkNativeCallWithStringIntVariadic3(b *testing.B) { vm := New() vm.Set("x", func(a1 string, a2 ...int) {}) s, _ := vm.Compile("test.js", `x("a", 1, 2, 3)`) for i := 0; i < b.N; i++ { vm.Run(s) } } func BenchmarkNativeCallWithStringIntVariadic10(b *testing.B) { vm := New() vm.Set("x", func(a1 string, a2 ...int) {}) s, _ := vm.Compile("test.js", `x("a", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)`) for i := 0; i < b.N; i++ { vm.Run(s) } } func BenchmarkNativeCallWithStringIntVariadicArray0(b *testing.B) { vm := New() vm.Set("x", func(a1 string, a2 ...int) {}) s, _ := vm.Compile("test.js", `x("a", [])`) for i := 0; i < b.N; i++ { vm.Run(s) } } func BenchmarkNativeCallWithStringIntVariadicArray1(b *testing.B) { vm := New() vm.Set("x", func(a1 string, a2 ...int) {}) s, _ := vm.Compile("test.js", `x("a", [1])`) for i := 0; i < b.N; i++ { vm.Run(s) } } func BenchmarkNativeCallWithStringIntVariadicArray3(b *testing.B) { vm := New() vm.Set("x", func(a1 string, a2 ...int) {}) s, _ := vm.Compile("test.js", `x("a", [1, 2, 3])`) for i := 0; i < b.N; i++ { vm.Run(s) } } func BenchmarkNativeCallWithStringIntVariadicArray10(b *testing.B) { vm := New() vm.Set("x", func(a1 string, a2 ...int) {}) s, _ := vm.Compile("test.js", `x("a", [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])`) for i := 0; i < b.N; i++ { vm.Run(s) } } func BenchmarkNativeCallWithMap(b *testing.B) { vm := New() vm.Set("x", func(a map[string]string) {}) s, _ := vm.Compile("test.js", `x({a: "b", c: "d"})`) for i := 0; i < b.N; i++ { vm.Run(s) } } func BenchmarkNativeCallWithMapVariadic(b *testing.B) { vm := New() vm.Set("x", func(a ...map[string]string) {}) s, _ := vm.Compile("test.js", `x({a: "b", c: "d"}, {w: "x", y: "z"})`) for i := 0; i < b.N; i++ { vm.Run(s) } } func BenchmarkNativeCallWithMapVariadicArray(b *testing.B) { vm := New() vm.Set("x", func(a ...map[string]string) {}) s, _ := vm.Compile("test.js", `x([{a: "b", c: "d"}, {w: "x", y: "z"}])`) for i := 0; i < b.N; i++ { vm.Run(s) } } func BenchmarkNativeCallWithFunction(b *testing.B) { vm := New() vm.Set("x", func(a func()) {}) s, _ := vm.Compile("test.js", `x(function() {})`) for i := 0; i < b.N; i++ { vm.Run(s) } } func BenchmarkNativeCallWithFunctionInt(b *testing.B) { vm := New() vm.Set("x", func(a func(int)) {}) s, _ := vm.Compile("test.js", `x(function(n) {})`) for i := 0; i < b.N; i++ { vm.Run(s) } } func BenchmarkNativeCallWithFunctionString(b *testing.B) { vm := New() vm.Set("x", func(a func(string)) {}) s, _ := vm.Compile("test.js", `x(function(n) {})`) for i := 0; i < b.N; i++ { vm.Run(s) } } func TestNativeCallWithString(t *testing.T) { vm := New() called := false vm.Set("x", func(a1 string) { if a1 != "zzz" { t.Fail() } called = true }) s, _ := vm.Compile("test.js", `x("zzz")`) if _, err := vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } if !called { t.Fail() } } func TestNativeCallWithFloat32(t *testing.T) { vm := New() called := false vm.Set("x", func(a1 float32) { if a1 != 1.1 { t.Fail() } called = true }) s, _ := vm.Compile("test.js", `x(1.1)`) if _, err := vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } if !called { t.Fail() } } func TestNativeCallWithFloat64(t *testing.T) { vm := New() called := false vm.Set("x", func(a1 float64) { if a1 != 1.1 { t.Fail() } called = true }) s, _ := vm.Compile("test.js", `x(1.1)`) if _, err := vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } if !called { t.Fail() } } func TestNativeCallWithInt(t *testing.T) { vm := New() called := false vm.Set("x", func(a1 int) { if a1 != 1 { t.Fail() } called = true }) s, _ := vm.Compile("test.js", `x(1)`) if _, err := vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } if !called { t.Fail() } } func TestNativeCallWithUint(t *testing.T) { vm := New() called := false vm.Set("x", func(a1 uint) { if a1 != 1 { t.Fail() } called = true }) s, _ := vm.Compile("test.js", `x(1)`) if _, err := vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } if !called { t.Fail() } } func TestNativeCallWithInt8(t *testing.T) { vm := New() called := false vm.Set("x", func(a1 int8) { if a1 != 1 { t.Fail() } called = true }) s, _ := vm.Compile("test.js", `x(1)`) if _, err := vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } if !called { t.Fail() } } func TestNativeCallWithUint8(t *testing.T) { vm := New() called := false vm.Set("x", func(a1 uint8) { if a1 != 1 { t.Fail() } called = true }) s, _ := vm.Compile("test.js", `x(1)`) if _, err := vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } if !called { t.Fail() } } func TestNativeCallWithInt16(t *testing.T) { vm := New() called := false vm.Set("x", func(a1 int16) { if a1 != 1 { t.Fail() } called = true }) s, _ := vm.Compile("test.js", `x(1)`) if _, err := vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } if !called { t.Fail() } } func TestNativeCallWithUint16(t *testing.T) { vm := New() called := false vm.Set("x", func(a1 uint16) { if a1 != 1 { t.Fail() } called = true }) s, _ := vm.Compile("test.js", `x(1)`) if _, err := vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } if !called { t.Fail() } } func TestNativeCallWithInt32(t *testing.T) { vm := New() called := false vm.Set("x", func(a1 int32) { if a1 != 1 { t.Fail() } called = true }) s, _ := vm.Compile("test.js", `x(1)`) if _, err := vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } if !called { t.Fail() } } func TestNativeCallWithUint32(t *testing.T) { vm := New() called := false vm.Set("x", func(a1 uint32) { if a1 != 1 { t.Fail() } called = true }) s, _ := vm.Compile("test.js", `x(1)`) if _, err := vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } if !called { t.Fail() } } func TestNativeCallWithInt64(t *testing.T) { vm := New() called := false vm.Set("x", func(a1 int64) { if a1 != 1 { t.Fail() } called = true }) s, _ := vm.Compile("test.js", `x(1)`) if _, err := vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } if !called { t.Fail() } } func TestNativeCallWithUint64(t *testing.T) { vm := New() called := false vm.Set("x", func(a1 uint64) { if a1 != 1 { t.Fail() } called = true }) s, _ := vm.Compile("test.js", `x(1)`) if _, err := vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } if !called { t.Fail() } } func TestNativeCallWithStringInt(t *testing.T) { vm := New() called := false vm.Set("x", func(a1 string, a2 int) { if a1 != "zzz" || a2 != 1 { t.Fail() } called = true }) s, _ := vm.Compile("test.js", `x("zzz", 1)`) if _, err := vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } if !called { t.Fail() } } func TestNativeCallWithIntVariadic0(t *testing.T) { vm := New() called := false vm.Set("x", func(a ...int) { if !reflect.DeepEqual(a, []int{}) { t.Fail() } called = true }) s, _ := vm.Compile("test.js", `x()`) if _, err := vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } if !called { t.Fail() } } func TestNativeCallWithIntVariadic1(t *testing.T) { vm := New() called := false vm.Set("x", func(a ...int) { if !reflect.DeepEqual(a, []int{1}) { t.Fail() } called = true }) s, _ := vm.Compile("test.js", `x(1)`) if _, err := vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } if !called { t.Fail() } } func TestNativeCallWithIntVariadic3(t *testing.T) { vm := New() called := false vm.Set("x", func(a ...int) { if !reflect.DeepEqual(a, []int{1, 2, 3}) { t.Fail() } called = true }) s, _ := vm.Compile("test.js", `x(1, 2, 3)`) if _, err := vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } if !called { t.Fail() } } func TestNativeCallWithIntVariadic10(t *testing.T) { vm := New() called := false vm.Set("x", func(a ...int) { if !reflect.DeepEqual(a, []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) { t.Fail() } called = true }) s, _ := vm.Compile("test.js", `x(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)`) if _, err := vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } if !called { t.Fail() } } func TestNativeCallWithIntArray0(t *testing.T) { vm := New() called := false vm.Set("x", func(a []int) { if !reflect.DeepEqual(a, []int{}) { t.Fail() } called = true }) s, _ := vm.Compile("test.js", `x([])`) if _, err := vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } if !called { t.Fail() } } func TestNativeCallWithIntArray1(t *testing.T) { vm := New() called := false vm.Set("x", func(a []int) { if !reflect.DeepEqual(a, []int{1}) { t.Fail() } called = true }) s, _ := vm.Compile("test.js", `x([1])`) if _, err := vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } if !called { t.Fail() } } func TestNativeCallWithIntArray3(t *testing.T) { vm := New() called := false vm.Set("x", func(a []int) { if !reflect.DeepEqual(a, []int{1, 2, 3}) { t.Fail() } called = true }) s, _ := vm.Compile("test.js", `x([1, 2, 3])`) if _, err := vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } if !called { t.Fail() } } func TestNativeCallWithIntArray10(t *testing.T) { vm := New() called := false vm.Set("x", func(a []int) { if !reflect.DeepEqual(a, []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) { t.Fail() } called = true }) s, _ := vm.Compile("test.js", `x([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])`) if _, err := vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } if !called { t.Fail() } } func TestNativeCallWithIntVariadicArray0(t *testing.T) { vm := New() called := false vm.Set("x", func(a ...int) { if !reflect.DeepEqual(a, []int{}) { t.Fail() } called = true }) s, _ := vm.Compile("test.js", `x([])`) if _, err := vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } if !called { t.Fail() } } func TestNativeCallWithIntVariadicArray1(t *testing.T) { vm := New() called := false vm.Set("x", func(a ...int) { if !reflect.DeepEqual(a, []int{1}) { t.Fail() } called = true }) s, _ := vm.Compile("test.js", `x([1])`) if _, err := vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } if !called { t.Fail() } } func TestNativeCallWithIntVariadicArray3(t *testing.T) { vm := New() called := false vm.Set("x", func(a ...int) { if !reflect.DeepEqual(a, []int{1, 2, 3}) { t.Fail() } called = true }) s, _ := vm.Compile("test.js", `x([1, 2, 3])`) if _, err := vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } if !called { t.Fail() } } func TestNativeCallWithIntVariadicArray10(t *testing.T) { vm := New() called := false vm.Set("x", func(a ...int) { if !reflect.DeepEqual(a, []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) { t.Fail() } called = true }) s, _ := vm.Compile("test.js", `x([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])`) if _, err := vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } if !called { t.Fail() } } func TestNativeCallWithStringIntVariadic0(t *testing.T) { vm := New() called := false vm.Set("x", func(a1 string, a2 ...int) { if a1 != "a" || !reflect.DeepEqual(a2, []int{}) { t.Fail() } called = true }) s, _ := vm.Compile("test.js", `x("a")`) if _, err := vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } if !called { t.Fail() } } func TestNativeCallWithStringIntVariadic1(t *testing.T) { vm := New() called := false vm.Set("x", func(a1 string, a2 ...int) { if a1 != "a" || !reflect.DeepEqual(a2, []int{1}) { t.Fail() } called = true }) s, _ := vm.Compile("test.js", `x("a", 1)`) if _, err := vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } if !called { t.Fail() } } func TestNativeCallWithStringIntVariadic3(t *testing.T) { vm := New() called := false vm.Set("x", func(a1 string, a2 ...int) { if a1 != "a" || !reflect.DeepEqual(a2, []int{1, 2, 3}) { t.Fail() } called = true }) s, _ := vm.Compile("test.js", `x("a", 1, 2, 3)`) if _, err := vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } if !called { t.Fail() } } func TestNativeCallWithStringIntVariadic10(t *testing.T) { vm := New() called := false vm.Set("x", func(a1 string, a2 ...int) { if a1 != "a" || !reflect.DeepEqual(a2, []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) { t.Fail() } called = true }) s, _ := vm.Compile("test.js", `x("a", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)`) if _, err := vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } if !called { t.Fail() } } func TestNativeCallWithStringIntVariadicArray0(t *testing.T) { vm := New() called := false vm.Set("x", func(a1 string, a2 ...int) { if a1 != "a" || !reflect.DeepEqual(a2, []int{}) { t.Fail() } called = true }) s, _ := vm.Compile("test.js", `x("a", [])`) if _, err := vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } if !called { t.Fail() } } func TestNativeCallWithStringIntVariadicArray1(t *testing.T) { vm := New() called := false vm.Set("x", func(a1 string, a2 ...int) { if a1 != "a" || !reflect.DeepEqual(a2, []int{1}) { t.Fail() } called = true }) s, _ := vm.Compile("test.js", `x("a", [1])`) if _, err := vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } if !called { t.Fail() } } func TestNativeCallWithStringIntVariadicArray3(t *testing.T) { vm := New() called := false vm.Set("x", func(a1 string, a2 ...int) { if a1 != "a" || !reflect.DeepEqual(a2, []int{1, 2, 3}) { t.Fail() } called = true }) s, _ := vm.Compile("test.js", `x("a", [1, 2, 3])`) if _, err := vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } if !called { t.Fail() } } func TestNativeCallWithStringIntVariadicArray10(t *testing.T) { vm := New() called := false vm.Set("x", func(a1 string, a2 ...int) { if a1 != "a" || !reflect.DeepEqual(a2, []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) { t.Fail() } called = true }) s, _ := vm.Compile("test.js", `x("a", [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])`) if _, err := vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } if !called { t.Fail() } } func TestNativeCallWithMap(t *testing.T) { vm := New() called := false vm.Set("x", func(a map[string]string) { if !reflect.DeepEqual(a, map[string]string{"a": "b", "c": "d"}) { t.Fail() } called = true }) s, _ := vm.Compile("test.js", `x({a: "b", c: "d"})`) if _, err := vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } if !called { t.Fail() } } func TestNativeCallWithMapVariadic(t *testing.T) { vm := New() called := false vm.Set("x", func(a ...map[string]string) { if !reflect.DeepEqual(a, []map[string]string{{"a": "b", "c": "d"}, {"w": "x", "y": "z"}}) { t.Fail() } called = true }) s, _ := vm.Compile("test.js", `x({a: "b", c: "d"}, {w: "x", y: "z"})`) if _, err := vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } if !called { t.Fail() } } func TestNativeCallWithMapVariadicArray(t *testing.T) { vm := New() called := false vm.Set("x", func(a ...map[string]string) { if !reflect.DeepEqual(a, []map[string]string{{"a": "b", "c": "d"}, {"w": "x", "y": "z"}}) { t.Fail() } called = true }) s, _ := vm.Compile("test.js", `x([{a: "b", c: "d"}, {w: "x", y: "z"}])`) if _, err := vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } if !called { t.Fail() } } func TestNativeCallWithFunctionVoidBool(t *testing.T) { vm := New() called := false vm.Set("x", func(fn func() bool) { if !fn() { t.Fail() } called = true }) s, _ := vm.Compile("test.js", `x(function() { return true; })`) if _, err := vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } if !called { t.Fail() } } func TestNativeCallWithFunctionIntInt(t *testing.T) { vm := New() called := false vm.Set("x", func(fn func(int) int) { if fn(5) != 5 { t.Fail() } called = true }) s, _ := vm.Compile("test.js", `x(function(n) { return n; })`) if _, err := vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } if !called { t.Fail() } } func TestNativeCallWithFunctionStringString(t *testing.T) { vm := New() called := false vm.Set("x", func(fn func(string) string) { if fn("zzz") != "zzz" { t.Fail() } called = true }) s, _ := vm.Compile("test.js", `x(function(n) { return n; })`) if _, err := vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } if !called { t.Fail() } } type testNativeCallWithStruct struct { Prefix string } type testNativeCallWithStructArg struct { Text string } func (t testNativeCallWithStruct) MakeStruct(s string) testNativeCallWithStructArg { return testNativeCallWithStructArg{Text: s} } func (t testNativeCallWithStruct) MakeStructPointer(s string) *testNativeCallWithStructArg { return &testNativeCallWithStructArg{Text: s} } func (t testNativeCallWithStruct) CallWithStruct(a testNativeCallWithStructArg) string { return t.Prefix + a.Text } func (t *testNativeCallWithStruct) CallPointerWithStruct(a testNativeCallWithStructArg) string { return t.Prefix + a.Text } func (t testNativeCallWithStruct) CallWithStructPointer(a *testNativeCallWithStructArg) string { return t.Prefix + a.Text } func (t *testNativeCallWithStruct) CallPointerWithStructPointer(a *testNativeCallWithStructArg) string { return t.Prefix + a.Text } func TestNativeCallMethodWithStruct(t *testing.T) { vm := New() called := false vm.Set("x", testNativeCallWithStruct{Prefix: "a"}) vm.Set("t", func(s string) { if s != "ab" { t.Fail() } called = true }) s, _ := vm.Compile("test.js", `t(x.CallWithStruct(x.MakeStruct("b")))`) if _, err := vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } if !called { t.Fail() } } func TestNativeCallPointerMethodWithStruct(t *testing.T) { vm := New() called := false vm.Set("x", &testNativeCallWithStruct{Prefix: "a"}) vm.Set("t", func(s string) { if s != "ab" { t.Fail() } called = true }) s, _ := vm.Compile("test.js", `t(x.CallPointerWithStruct(x.MakeStruct("b")))`) if _, err := vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } if !called { t.Fail() } } func TestNativeCallMethodWithStructPointer(t *testing.T) { vm := New() called := false vm.Set("x", testNativeCallWithStruct{Prefix: "a"}) vm.Set("t", func(s string) { if s != "ab" { t.Fail() } called = true }) s, _ := vm.Compile("test.js", `t(x.CallWithStructPointer(x.MakeStructPointer("b")))`) if _, err := vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } if !called { t.Fail() } } func TestNativeCallPointerMethodWithStructPointer(t *testing.T) { vm := New() called := false vm.Set("x", &testNativeCallWithStruct{Prefix: "a"}) vm.Set("t", func(s string) { if s != "ab" { t.Fail() } called = true }) s, _ := vm.Compile("test.js", `t(x.CallPointerWithStructPointer(x.MakeStructPointer("b")))`) if _, err := vm.Run(s); err != nil { t.Logf("err should have been nil; was %s\n", err.Error()) t.Fail() } if !called { t.Fail() } } func TestNativeCallNilInterfaceArg(t *testing.T) { vm := New() vm.Set("f1", func(v interface{}) {}) vm.Call("f1", nil, nil) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/clone.go000066400000000000000000000103001331145703200240160ustar00rootroot00000000000000package otto import ( "fmt" ) type _clone struct { runtime *_runtime _object map[*_object]*_object _objectStash map[*_objectStash]*_objectStash _dclStash map[*_dclStash]*_dclStash _fnStash map[*_fnStash]*_fnStash } func (in *_runtime) clone() *_runtime { in.lck.Lock() defer in.lck.Unlock() out := &_runtime{ debugger: in.debugger, random: in.random, stackLimit: in.stackLimit, traceLimit: in.traceLimit, } clone := _clone{ runtime: out, _object: make(map[*_object]*_object), _objectStash: make(map[*_objectStash]*_objectStash), _dclStash: make(map[*_dclStash]*_dclStash), _fnStash: make(map[*_fnStash]*_fnStash), } globalObject := clone.object(in.globalObject) out.globalStash = out.newObjectStash(globalObject, nil) out.globalObject = globalObject out.global = _global{ clone.object(in.global.Object), clone.object(in.global.Function), clone.object(in.global.Array), clone.object(in.global.String), clone.object(in.global.Boolean), clone.object(in.global.Number), clone.object(in.global.Math), clone.object(in.global.Date), clone.object(in.global.RegExp), clone.object(in.global.Error), clone.object(in.global.EvalError), clone.object(in.global.TypeError), clone.object(in.global.RangeError), clone.object(in.global.ReferenceError), clone.object(in.global.SyntaxError), clone.object(in.global.URIError), clone.object(in.global.JSON), clone.object(in.global.ObjectPrototype), clone.object(in.global.FunctionPrototype), clone.object(in.global.ArrayPrototype), clone.object(in.global.StringPrototype), clone.object(in.global.BooleanPrototype), clone.object(in.global.NumberPrototype), clone.object(in.global.DatePrototype), clone.object(in.global.RegExpPrototype), clone.object(in.global.ErrorPrototype), clone.object(in.global.EvalErrorPrototype), clone.object(in.global.TypeErrorPrototype), clone.object(in.global.RangeErrorPrototype), clone.object(in.global.ReferenceErrorPrototype), clone.object(in.global.SyntaxErrorPrototype), clone.object(in.global.URIErrorPrototype), } out.eval = out.globalObject.property["eval"].value.(Value).value.(*_object) out.globalObject.prototype = out.global.ObjectPrototype // Not sure if this is necessary, but give some help to the GC clone.runtime = nil clone._object = nil clone._objectStash = nil clone._dclStash = nil clone._fnStash = nil return out } func (clone *_clone) object(in *_object) *_object { if out, exists := clone._object[in]; exists { return out } out := &_object{} clone._object[in] = out return in.objectClass.clone(in, out, clone) } func (clone *_clone) dclStash(in *_dclStash) (*_dclStash, bool) { if out, exists := clone._dclStash[in]; exists { return out, true } out := &_dclStash{} clone._dclStash[in] = out return out, false } func (clone *_clone) objectStash(in *_objectStash) (*_objectStash, bool) { if out, exists := clone._objectStash[in]; exists { return out, true } out := &_objectStash{} clone._objectStash[in] = out return out, false } func (clone *_clone) fnStash(in *_fnStash) (*_fnStash, bool) { if out, exists := clone._fnStash[in]; exists { return out, true } out := &_fnStash{} clone._fnStash[in] = out return out, false } func (clone *_clone) value(in Value) Value { out := in switch value := in.value.(type) { case *_object: out.value = clone.object(value) } return out } func (clone *_clone) valueArray(in []Value) []Value { out := make([]Value, len(in)) for index, value := range in { out[index] = clone.value(value) } return out } func (clone *_clone) stash(in _stash) _stash { if in == nil { return nil } return in.clone(clone) } func (clone *_clone) property(in _property) _property { out := in switch value := in.value.(type) { case Value: out.value = clone.value(value) case _propertyGetSet: p := _propertyGetSet{} if value[0] != nil { p[0] = clone.object(value[0]) } if value[1] != nil { p[1] = clone.object(value[1]) } out.value = p default: panic(fmt.Errorf("in.value.(Value) != true; in.value is %T", in.value)) } return out } func (clone *_clone) dclProperty(in _dclProperty) _dclProperty { out := in out.value = clone.value(in.value) return out } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/clone_test.go000066400000000000000000000003361331145703200250650ustar00rootroot00000000000000package otto import ( "testing" ) func TestCloneGetterSetter(t *testing.T) { vm := New() vm.Run(`var x = Object.create(null, { x: { get: function() {}, set: function() {}, }, })`) vm.Copy() } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/cmpl.go000066400000000000000000000006271331145703200236640ustar00rootroot00000000000000package otto import ( "github.com/robertkrimen/otto/ast" "github.com/robertkrimen/otto/file" ) type _file struct { name string src string base int // This will always be 1 or greater } type _compiler struct { file *file.File program *ast.Program } func (cmpl *_compiler) parse() *_nodeProgram { if cmpl.program != nil { cmpl.file = cmpl.program.File } return cmpl._parse(cmpl.program) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/cmpl_evaluate.go000066400000000000000000000050771331145703200255560ustar00rootroot00000000000000package otto import ( "strconv" ) func (self *_runtime) cmpl_evaluate_nodeProgram(node *_nodeProgram, eval bool) Value { if !eval { self.enterGlobalScope() defer func() { self.leaveScope() }() } self.cmpl_functionDeclaration(node.functionList) self.cmpl_variableDeclaration(node.varList) self.scope.frame.file = node.file return self.cmpl_evaluate_nodeStatementList(node.body) } func (self *_runtime) cmpl_call_nodeFunction(function *_object, stash *_fnStash, node *_nodeFunctionLiteral, this Value, argumentList []Value) Value { indexOfParameterName := make([]string, len(argumentList)) // function(abc, def, ghi) // indexOfParameterName[0] = "abc" // indexOfParameterName[1] = "def" // indexOfParameterName[2] = "ghi" // ... argumentsFound := false for index, name := range node.parameterList { if name == "arguments" { argumentsFound = true } value := Value{} if index < len(argumentList) { value = argumentList[index] indexOfParameterName[index] = name } // strict = false self.scope.lexical.setValue(name, value, false) } if !argumentsFound { arguments := self.newArgumentsObject(indexOfParameterName, stash, len(argumentList)) arguments.defineProperty("callee", toValue_object(function), 0101, false) stash.arguments = arguments // strict = false self.scope.lexical.setValue("arguments", toValue_object(arguments), false) for index, _ := range argumentList { if index < len(node.parameterList) { continue } indexAsString := strconv.FormatInt(int64(index), 10) arguments.defineProperty(indexAsString, argumentList[index], 0111, false) } } self.cmpl_functionDeclaration(node.functionList) self.cmpl_variableDeclaration(node.varList) result := self.cmpl_evaluate_nodeStatement(node.body) if result.kind == valueResult { return result } return Value{} } func (self *_runtime) cmpl_functionDeclaration(list []*_nodeFunctionLiteral) { executionContext := self.scope eval := executionContext.eval stash := executionContext.variable for _, function := range list { name := function.name value := self.cmpl_evaluate_nodeExpression(function) if !stash.hasBinding(name) { stash.createBinding(name, eval == true, value) } else { // TODO 10.5.5.e stash.setBinding(name, value, false) // TODO strict } } } func (self *_runtime) cmpl_variableDeclaration(list []string) { executionContext := self.scope eval := executionContext.eval stash := executionContext.variable for _, name := range list { if !stash.hasBinding(name) { stash.createBinding(name, eval == true, Value{}) // TODO strict? } } } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/cmpl_evaluate_expression.go000066400000000000000000000312001331145703200300200ustar00rootroot00000000000000package otto import ( "fmt" "math" "runtime" "github.com/robertkrimen/otto/token" ) func (self *_runtime) cmpl_evaluate_nodeExpression(node _nodeExpression) Value { // Allow interpreter interruption // If the Interrupt channel is nil, then // we avoid runtime.Gosched() overhead (if any) // FIXME: Test this if self.otto.Interrupt != nil { runtime.Gosched() select { case value := <-self.otto.Interrupt: value() default: } } switch node := node.(type) { case *_nodeArrayLiteral: return self.cmpl_evaluate_nodeArrayLiteral(node) case *_nodeAssignExpression: return self.cmpl_evaluate_nodeAssignExpression(node) case *_nodeBinaryExpression: if node.comparison { return self.cmpl_evaluate_nodeBinaryExpression_comparison(node) } else { return self.cmpl_evaluate_nodeBinaryExpression(node) } case *_nodeBracketExpression: return self.cmpl_evaluate_nodeBracketExpression(node) case *_nodeCallExpression: return self.cmpl_evaluate_nodeCallExpression(node, nil) case *_nodeConditionalExpression: return self.cmpl_evaluate_nodeConditionalExpression(node) case *_nodeDotExpression: return self.cmpl_evaluate_nodeDotExpression(node) case *_nodeFunctionLiteral: var local = self.scope.lexical if node.name != "" { local = self.newDeclarationStash(local) } value := toValue_object(self.newNodeFunction(node, local)) if node.name != "" { local.createBinding(node.name, false, value) } return value case *_nodeIdentifier: name := node.name // TODO Should be true or false (strictness) depending on context // getIdentifierReference should not return nil, but we check anyway and panic // so as not to propagate the nil into something else reference := getIdentifierReference(self, self.scope.lexical, name, false, _at(node.idx)) if reference == nil { // Should never get here! panic(hereBeDragons("referenceError == nil: " + name)) } return toValue(reference) case *_nodeLiteral: return node.value case *_nodeNewExpression: return self.cmpl_evaluate_nodeNewExpression(node) case *_nodeObjectLiteral: return self.cmpl_evaluate_nodeObjectLiteral(node) case *_nodeRegExpLiteral: return toValue_object(self._newRegExp(node.pattern, node.flags)) case *_nodeSequenceExpression: return self.cmpl_evaluate_nodeSequenceExpression(node) case *_nodeThisExpression: return toValue_object(self.scope.this) case *_nodeUnaryExpression: return self.cmpl_evaluate_nodeUnaryExpression(node) case *_nodeVariableExpression: return self.cmpl_evaluate_nodeVariableExpression(node) } panic(fmt.Errorf("Here be dragons: evaluate_nodeExpression(%T)", node)) } func (self *_runtime) cmpl_evaluate_nodeArrayLiteral(node *_nodeArrayLiteral) Value { valueArray := []Value{} for _, node := range node.value { if node == nil { valueArray = append(valueArray, emptyValue) } else { valueArray = append(valueArray, self.cmpl_evaluate_nodeExpression(node).resolve()) } } result := self.newArrayOf(valueArray) return toValue_object(result) } func (self *_runtime) cmpl_evaluate_nodeAssignExpression(node *_nodeAssignExpression) Value { left := self.cmpl_evaluate_nodeExpression(node.left) right := self.cmpl_evaluate_nodeExpression(node.right) rightValue := right.resolve() result := rightValue if node.operator != token.ASSIGN { result = self.calculateBinaryExpression(node.operator, left, rightValue) } self.putValue(left.reference(), result) return result } func (self *_runtime) cmpl_evaluate_nodeBinaryExpression(node *_nodeBinaryExpression) Value { left := self.cmpl_evaluate_nodeExpression(node.left) leftValue := left.resolve() switch node.operator { // Logical case token.LOGICAL_AND: if !leftValue.bool() { return leftValue } right := self.cmpl_evaluate_nodeExpression(node.right) return right.resolve() case token.LOGICAL_OR: if leftValue.bool() { return leftValue } right := self.cmpl_evaluate_nodeExpression(node.right) return right.resolve() } return self.calculateBinaryExpression(node.operator, leftValue, self.cmpl_evaluate_nodeExpression(node.right)) } func (self *_runtime) cmpl_evaluate_nodeBinaryExpression_comparison(node *_nodeBinaryExpression) Value { left := self.cmpl_evaluate_nodeExpression(node.left).resolve() right := self.cmpl_evaluate_nodeExpression(node.right).resolve() return toValue_bool(self.calculateComparison(node.operator, left, right)) } func (self *_runtime) cmpl_evaluate_nodeBracketExpression(node *_nodeBracketExpression) Value { target := self.cmpl_evaluate_nodeExpression(node.left) targetValue := target.resolve() member := self.cmpl_evaluate_nodeExpression(node.member) memberValue := member.resolve() // TODO Pass in base value as-is, and defer toObject till later? object, err := self.objectCoerce(targetValue) if err != nil { panic(self.panicTypeError("Cannot access member '%s' of %s", memberValue.string(), err.Error(), _at(node.idx))) } return toValue(newPropertyReference(self, object, memberValue.string(), false, _at(node.idx))) } func (self *_runtime) cmpl_evaluate_nodeCallExpression(node *_nodeCallExpression, withArgumentList []interface{}) Value { rt := self this := Value{} callee := self.cmpl_evaluate_nodeExpression(node.callee) argumentList := []Value{} if withArgumentList != nil { argumentList = self.toValueArray(withArgumentList...) } else { for _, argumentNode := range node.argumentList { argumentList = append(argumentList, self.cmpl_evaluate_nodeExpression(argumentNode).resolve()) } } rf := callee.reference() vl := callee.resolve() eval := false // Whether this call is a (candidate for) direct call to eval name := "" if rf != nil { switch rf := rf.(type) { case *_propertyReference: name = rf.name object := rf.base this = toValue_object(object) eval = rf.name == "eval" // Possible direct eval case *_stashReference: // TODO ImplicitThisValue name = rf.name eval = rf.name == "eval" // Possible direct eval default: // FIXME? panic(rt.panicTypeError("Here be dragons")) } } at := _at(-1) switch callee := node.callee.(type) { case *_nodeIdentifier: at = _at(callee.idx) case *_nodeDotExpression: at = _at(callee.idx) case *_nodeBracketExpression: at = _at(callee.idx) } frame := _frame{ callee: name, file: self.scope.frame.file, } if !vl.IsFunction() { if name == "" { // FIXME Maybe typeof? panic(rt.panicTypeError("%v is not a function", vl, at)) } panic(rt.panicTypeError("'%s' is not a function", name, at)) } self.scope.frame.offset = int(at) return vl._object().call(this, argumentList, eval, frame) } func (self *_runtime) cmpl_evaluate_nodeConditionalExpression(node *_nodeConditionalExpression) Value { test := self.cmpl_evaluate_nodeExpression(node.test) testValue := test.resolve() if testValue.bool() { return self.cmpl_evaluate_nodeExpression(node.consequent) } return self.cmpl_evaluate_nodeExpression(node.alternate) } func (self *_runtime) cmpl_evaluate_nodeDotExpression(node *_nodeDotExpression) Value { target := self.cmpl_evaluate_nodeExpression(node.left) targetValue := target.resolve() // TODO Pass in base value as-is, and defer toObject till later? object, err := self.objectCoerce(targetValue) if err != nil { panic(self.panicTypeError("Cannot access member '%s' of %s", node.identifier, err.Error(), _at(node.idx))) } return toValue(newPropertyReference(self, object, node.identifier, false, _at(node.idx))) } func (self *_runtime) cmpl_evaluate_nodeNewExpression(node *_nodeNewExpression) Value { rt := self callee := self.cmpl_evaluate_nodeExpression(node.callee) argumentList := []Value{} for _, argumentNode := range node.argumentList { argumentList = append(argumentList, self.cmpl_evaluate_nodeExpression(argumentNode).resolve()) } rf := callee.reference() vl := callee.resolve() name := "" if rf != nil { switch rf := rf.(type) { case *_propertyReference: name = rf.name case *_stashReference: name = rf.name default: panic(rt.panicTypeError("Here be dragons")) } } at := _at(-1) switch callee := node.callee.(type) { case *_nodeIdentifier: at = _at(callee.idx) case *_nodeDotExpression: at = _at(callee.idx) case *_nodeBracketExpression: at = _at(callee.idx) } if !vl.IsFunction() { if name == "" { // FIXME Maybe typeof? panic(rt.panicTypeError("%v is not a function", vl, at)) } panic(rt.panicTypeError("'%s' is not a function", name, at)) } self.scope.frame.offset = int(at) return vl._object().construct(argumentList) } func (self *_runtime) cmpl_evaluate_nodeObjectLiteral(node *_nodeObjectLiteral) Value { result := self.newObject() for _, property := range node.value { switch property.kind { case "value": result.defineProperty(property.key, self.cmpl_evaluate_nodeExpression(property.value).resolve(), 0111, false) case "get": getter := self.newNodeFunction(property.value.(*_nodeFunctionLiteral), self.scope.lexical) descriptor := _property{} descriptor.mode = 0211 descriptor.value = _propertyGetSet{getter, nil} result.defineOwnProperty(property.key, descriptor, false) case "set": setter := self.newNodeFunction(property.value.(*_nodeFunctionLiteral), self.scope.lexical) descriptor := _property{} descriptor.mode = 0211 descriptor.value = _propertyGetSet{nil, setter} result.defineOwnProperty(property.key, descriptor, false) default: panic(fmt.Errorf("Here be dragons: evaluate_nodeObjectLiteral: invalid property.Kind: %v", property.kind)) } } return toValue_object(result) } func (self *_runtime) cmpl_evaluate_nodeSequenceExpression(node *_nodeSequenceExpression) Value { var result Value for _, node := range node.sequence { result = self.cmpl_evaluate_nodeExpression(node) result = result.resolve() } return result } func (self *_runtime) cmpl_evaluate_nodeUnaryExpression(node *_nodeUnaryExpression) Value { target := self.cmpl_evaluate_nodeExpression(node.operand) switch node.operator { case token.TYPEOF, token.DELETE: if target.kind == valueReference && target.reference().invalid() { if node.operator == token.TYPEOF { return toValue_string("undefined") } return trueValue } } switch node.operator { case token.NOT: targetValue := target.resolve() if targetValue.bool() { return falseValue } return trueValue case token.BITWISE_NOT: targetValue := target.resolve() integerValue := toInt32(targetValue) return toValue_int32(^integerValue) case token.PLUS: targetValue := target.resolve() return toValue_float64(targetValue.float64()) case token.MINUS: targetValue := target.resolve() value := targetValue.float64() // TODO Test this sign := float64(-1) if math.Signbit(value) { sign = 1 } return toValue_float64(math.Copysign(value, sign)) case token.INCREMENT: targetValue := target.resolve() if node.postfix { // Postfix++ oldValue := targetValue.float64() newValue := toValue_float64(+1 + oldValue) self.putValue(target.reference(), newValue) return toValue_float64(oldValue) } else { // ++Prefix newValue := toValue_float64(+1 + targetValue.float64()) self.putValue(target.reference(), newValue) return newValue } case token.DECREMENT: targetValue := target.resolve() if node.postfix { // Postfix-- oldValue := targetValue.float64() newValue := toValue_float64(-1 + oldValue) self.putValue(target.reference(), newValue) return toValue_float64(oldValue) } else { // --Prefix newValue := toValue_float64(-1 + targetValue.float64()) self.putValue(target.reference(), newValue) return newValue } case token.VOID: target.resolve() // FIXME Side effect? return Value{} case token.DELETE: reference := target.reference() if reference == nil { return trueValue } return toValue_bool(target.reference().delete()) case token.TYPEOF: targetValue := target.resolve() switch targetValue.kind { case valueUndefined: return toValue_string("undefined") case valueNull: return toValue_string("object") case valueBoolean: return toValue_string("boolean") case valueNumber: return toValue_string("number") case valueString: return toValue_string("string") case valueObject: if targetValue._object().isCall() { return toValue_string("function") } return toValue_string("object") default: // FIXME ? } } panic(hereBeDragons()) } func (self *_runtime) cmpl_evaluate_nodeVariableExpression(node *_nodeVariableExpression) Value { if node.initializer != nil { // FIXME If reference is nil left := getIdentifierReference(self, self.scope.lexical, node.name, false, _at(node.idx)) right := self.cmpl_evaluate_nodeExpression(node.initializer) rightValue := right.resolve() self.putValue(left, rightValue) } return toValue_string(node.name) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/cmpl_evaluate_statement.go000066400000000000000000000242351331145703200276370ustar00rootroot00000000000000package otto import ( "fmt" "runtime" "github.com/robertkrimen/otto/token" ) func (self *_runtime) cmpl_evaluate_nodeStatement(node _nodeStatement) Value { // Allow interpreter interruption // If the Interrupt channel is nil, then // we avoid runtime.Gosched() overhead (if any) // FIXME: Test this if self.otto.Interrupt != nil { runtime.Gosched() select { case value := <-self.otto.Interrupt: value() default: } } switch node := node.(type) { case *_nodeBlockStatement: labels := self.labels self.labels = nil value := self.cmpl_evaluate_nodeStatementList(node.list) switch value.kind { case valueResult: switch value.evaluateBreak(labels) { case resultBreak: return emptyValue } } return value case *_nodeBranchStatement: target := node.label switch node.branch { // FIXME Maybe node.kind? node.operator? case token.BREAK: return toValue(newBreakResult(target)) case token.CONTINUE: return toValue(newContinueResult(target)) } case *_nodeDebuggerStatement: if self.debugger != nil { self.debugger(self.otto) } return emptyValue // Nothing happens. case *_nodeDoWhileStatement: return self.cmpl_evaluate_nodeDoWhileStatement(node) case *_nodeEmptyStatement: return emptyValue case *_nodeExpressionStatement: return self.cmpl_evaluate_nodeExpression(node.expression) case *_nodeForInStatement: return self.cmpl_evaluate_nodeForInStatement(node) case *_nodeForStatement: return self.cmpl_evaluate_nodeForStatement(node) case *_nodeIfStatement: return self.cmpl_evaluate_nodeIfStatement(node) case *_nodeLabelledStatement: self.labels = append(self.labels, node.label) defer func() { if len(self.labels) > 0 { self.labels = self.labels[:len(self.labels)-1] // Pop the label } else { self.labels = nil } }() return self.cmpl_evaluate_nodeStatement(node.statement) case *_nodeReturnStatement: if node.argument != nil { return toValue(newReturnResult(self.cmpl_evaluate_nodeExpression(node.argument).resolve())) } return toValue(newReturnResult(Value{})) case *_nodeSwitchStatement: return self.cmpl_evaluate_nodeSwitchStatement(node) case *_nodeThrowStatement: value := self.cmpl_evaluate_nodeExpression(node.argument).resolve() panic(newException(value)) case *_nodeTryStatement: return self.cmpl_evaluate_nodeTryStatement(node) case *_nodeVariableStatement: // Variables are already defined, this is initialization only for _, variable := range node.list { self.cmpl_evaluate_nodeVariableExpression(variable.(*_nodeVariableExpression)) } return emptyValue case *_nodeWhileStatement: return self.cmpl_evaluate_nodeWhileStatement(node) case *_nodeWithStatement: return self.cmpl_evaluate_nodeWithStatement(node) } panic(fmt.Errorf("Here be dragons: evaluate_nodeStatement(%T)", node)) } func (self *_runtime) cmpl_evaluate_nodeStatementList(list []_nodeStatement) Value { var result Value for _, node := range list { value := self.cmpl_evaluate_nodeStatement(node) switch value.kind { case valueResult: return value case valueEmpty: default: // We have getValue here to (for example) trigger a // ReferenceError (of the not defined variety) // Not sure if this is the best way to error out early // for such errors or if there is a better way // TODO Do we still need this? result = value.resolve() } } return result } func (self *_runtime) cmpl_evaluate_nodeDoWhileStatement(node *_nodeDoWhileStatement) Value { labels := append(self.labels, "") self.labels = nil test := node.test result := emptyValue resultBreak: for { for _, node := range node.body { value := self.cmpl_evaluate_nodeStatement(node) switch value.kind { case valueResult: switch value.evaluateBreakContinue(labels) { case resultReturn: return value case resultBreak: break resultBreak case resultContinue: goto resultContinue } case valueEmpty: default: result = value } } resultContinue: if !self.cmpl_evaluate_nodeExpression(test).resolve().bool() { // Stahp: do ... while (false) break } } return result } func (self *_runtime) cmpl_evaluate_nodeForInStatement(node *_nodeForInStatement) Value { labels := append(self.labels, "") self.labels = nil source := self.cmpl_evaluate_nodeExpression(node.source) sourceValue := source.resolve() switch sourceValue.kind { case valueUndefined, valueNull: return emptyValue } sourceObject := self.toObject(sourceValue) into := node.into body := node.body result := emptyValue object := sourceObject for object != nil { enumerateValue := emptyValue object.enumerate(false, func(name string) bool { into := self.cmpl_evaluate_nodeExpression(into) // In the case of: for (var abc in def) ... if into.reference() == nil { identifier := into.string() // TODO Should be true or false (strictness) depending on context into = toValue(getIdentifierReference(self, self.scope.lexical, identifier, false, -1)) } self.putValue(into.reference(), toValue_string(name)) for _, node := range body { value := self.cmpl_evaluate_nodeStatement(node) switch value.kind { case valueResult: switch value.evaluateBreakContinue(labels) { case resultReturn: enumerateValue = value return false case resultBreak: object = nil return false case resultContinue: return true } case valueEmpty: default: enumerateValue = value } } return true }) if object == nil { break } object = object.prototype if !enumerateValue.isEmpty() { result = enumerateValue } } return result } func (self *_runtime) cmpl_evaluate_nodeForStatement(node *_nodeForStatement) Value { labels := append(self.labels, "") self.labels = nil initializer := node.initializer test := node.test update := node.update body := node.body if initializer != nil { initialResult := self.cmpl_evaluate_nodeExpression(initializer) initialResult.resolve() // Side-effect trigger } result := emptyValue resultBreak: for { if test != nil { testResult := self.cmpl_evaluate_nodeExpression(test) testResultValue := testResult.resolve() if testResultValue.bool() == false { break } } for _, node := range body { value := self.cmpl_evaluate_nodeStatement(node) switch value.kind { case valueResult: switch value.evaluateBreakContinue(labels) { case resultReturn: return value case resultBreak: break resultBreak case resultContinue: goto resultContinue } case valueEmpty: default: result = value } } resultContinue: if update != nil { updateResult := self.cmpl_evaluate_nodeExpression(update) updateResult.resolve() // Side-effect trigger } } return result } func (self *_runtime) cmpl_evaluate_nodeIfStatement(node *_nodeIfStatement) Value { test := self.cmpl_evaluate_nodeExpression(node.test) testValue := test.resolve() if testValue.bool() { return self.cmpl_evaluate_nodeStatement(node.consequent) } else if node.alternate != nil { return self.cmpl_evaluate_nodeStatement(node.alternate) } return emptyValue } func (self *_runtime) cmpl_evaluate_nodeSwitchStatement(node *_nodeSwitchStatement) Value { labels := append(self.labels, "") self.labels = nil discriminantResult := self.cmpl_evaluate_nodeExpression(node.discriminant) target := node.default_ for index, clause := range node.body { test := clause.test if test != nil { if self.calculateComparison(token.STRICT_EQUAL, discriminantResult, self.cmpl_evaluate_nodeExpression(test)) { target = index break } } } result := emptyValue if target != -1 { for _, clause := range node.body[target:] { for _, statement := range clause.consequent { value := self.cmpl_evaluate_nodeStatement(statement) switch value.kind { case valueResult: switch value.evaluateBreak(labels) { case resultReturn: return value case resultBreak: return emptyValue } case valueEmpty: default: result = value } } } } return result } func (self *_runtime) cmpl_evaluate_nodeTryStatement(node *_nodeTryStatement) Value { tryCatchValue, exception := self.tryCatchEvaluate(func() Value { return self.cmpl_evaluate_nodeStatement(node.body) }) if exception && node.catch != nil { outer := self.scope.lexical self.scope.lexical = self.newDeclarationStash(outer) defer func() { self.scope.lexical = outer }() // TODO If necessary, convert TypeError => TypeError // That, is, such errors can be thrown despite not being JavaScript "native" // strict = false self.scope.lexical.setValue(node.catch.parameter, tryCatchValue, false) // FIXME node.CatchParameter // FIXME node.Catch tryCatchValue, exception = self.tryCatchEvaluate(func() Value { return self.cmpl_evaluate_nodeStatement(node.catch.body) }) } if node.finally != nil { finallyValue := self.cmpl_evaluate_nodeStatement(node.finally) if finallyValue.kind == valueResult { return finallyValue } } if exception { panic(newException(tryCatchValue)) } return tryCatchValue } func (self *_runtime) cmpl_evaluate_nodeWhileStatement(node *_nodeWhileStatement) Value { test := node.test body := node.body labels := append(self.labels, "") self.labels = nil result := emptyValue resultBreakContinue: for { if !self.cmpl_evaluate_nodeExpression(test).resolve().bool() { // Stahp: while (false) ... break } for _, node := range body { value := self.cmpl_evaluate_nodeStatement(node) switch value.kind { case valueResult: switch value.evaluateBreakContinue(labels) { case resultReturn: return value case resultBreak: break resultBreakContinue case resultContinue: continue resultBreakContinue } case valueEmpty: default: result = value } } } return result } func (self *_runtime) cmpl_evaluate_nodeWithStatement(node *_nodeWithStatement) Value { object := self.cmpl_evaluate_nodeExpression(node.object) outer := self.scope.lexical lexical := self.newObjectStash(self.toObject(object.resolve()), outer) self.scope.lexical = lexical defer func() { self.scope.lexical = outer }() return self.cmpl_evaluate_nodeStatement(node.body) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/cmpl_parse.go000066400000000000000000000354501331145703200250600ustar00rootroot00000000000000package otto import ( "fmt" "regexp" "github.com/robertkrimen/otto/ast" "github.com/robertkrimen/otto/file" "github.com/robertkrimen/otto/token" ) var trueLiteral = &_nodeLiteral{value: toValue_bool(true)} var falseLiteral = &_nodeLiteral{value: toValue_bool(false)} var nullLiteral = &_nodeLiteral{value: nullValue} var emptyStatement = &_nodeEmptyStatement{} func (cmpl *_compiler) parseExpression(in ast.Expression) _nodeExpression { if in == nil { return nil } switch in := in.(type) { case *ast.ArrayLiteral: out := &_nodeArrayLiteral{ value: make([]_nodeExpression, len(in.Value)), } for i, value := range in.Value { out.value[i] = cmpl.parseExpression(value) } return out case *ast.AssignExpression: return &_nodeAssignExpression{ operator: in.Operator, left: cmpl.parseExpression(in.Left), right: cmpl.parseExpression(in.Right), } case *ast.BinaryExpression: return &_nodeBinaryExpression{ operator: in.Operator, left: cmpl.parseExpression(in.Left), right: cmpl.parseExpression(in.Right), comparison: in.Comparison, } case *ast.BooleanLiteral: if in.Value { return trueLiteral } return falseLiteral case *ast.BracketExpression: return &_nodeBracketExpression{ idx: in.Left.Idx0(), left: cmpl.parseExpression(in.Left), member: cmpl.parseExpression(in.Member), } case *ast.CallExpression: out := &_nodeCallExpression{ callee: cmpl.parseExpression(in.Callee), argumentList: make([]_nodeExpression, len(in.ArgumentList)), } for i, value := range in.ArgumentList { out.argumentList[i] = cmpl.parseExpression(value) } return out case *ast.ConditionalExpression: return &_nodeConditionalExpression{ test: cmpl.parseExpression(in.Test), consequent: cmpl.parseExpression(in.Consequent), alternate: cmpl.parseExpression(in.Alternate), } case *ast.DotExpression: return &_nodeDotExpression{ idx: in.Left.Idx0(), left: cmpl.parseExpression(in.Left), identifier: in.Identifier.Name, } case *ast.EmptyExpression: return nil case *ast.FunctionLiteral: name := "" if in.Name != nil { name = in.Name.Name } out := &_nodeFunctionLiteral{ name: name, body: cmpl.parseStatement(in.Body), source: in.Source, file: cmpl.file, } if in.ParameterList != nil { list := in.ParameterList.List out.parameterList = make([]string, len(list)) for i, value := range list { out.parameterList[i] = value.Name } } for _, value := range in.DeclarationList { switch value := value.(type) { case *ast.FunctionDeclaration: out.functionList = append(out.functionList, cmpl.parseExpression(value.Function).(*_nodeFunctionLiteral)) case *ast.VariableDeclaration: for _, value := range value.List { out.varList = append(out.varList, value.Name) } default: panic(fmt.Errorf("Here be dragons: parseProgram.declaration(%T)", value)) } } return out case *ast.Identifier: return &_nodeIdentifier{ idx: in.Idx, name: in.Name, } case *ast.NewExpression: out := &_nodeNewExpression{ callee: cmpl.parseExpression(in.Callee), argumentList: make([]_nodeExpression, len(in.ArgumentList)), } for i, value := range in.ArgumentList { out.argumentList[i] = cmpl.parseExpression(value) } return out case *ast.NullLiteral: return nullLiteral case *ast.NumberLiteral: return &_nodeLiteral{ value: toValue(in.Value), } case *ast.ObjectLiteral: out := &_nodeObjectLiteral{ value: make([]_nodeProperty, len(in.Value)), } for i, value := range in.Value { out.value[i] = _nodeProperty{ key: value.Key, kind: value.Kind, value: cmpl.parseExpression(value.Value), } } return out case *ast.RegExpLiteral: return &_nodeRegExpLiteral{ flags: in.Flags, pattern: in.Pattern, } case *ast.SequenceExpression: out := &_nodeSequenceExpression{ sequence: make([]_nodeExpression, len(in.Sequence)), } for i, value := range in.Sequence { out.sequence[i] = cmpl.parseExpression(value) } return out case *ast.StringLiteral: return &_nodeLiteral{ value: toValue_string(in.Value), } case *ast.ThisExpression: return &_nodeThisExpression{} case *ast.UnaryExpression: return &_nodeUnaryExpression{ operator: in.Operator, operand: cmpl.parseExpression(in.Operand), postfix: in.Postfix, } case *ast.VariableExpression: return &_nodeVariableExpression{ idx: in.Idx0(), name: in.Name, initializer: cmpl.parseExpression(in.Initializer), } } panic(fmt.Errorf("Here be dragons: cmpl.parseExpression(%T)", in)) } func (cmpl *_compiler) parseStatement(in ast.Statement) _nodeStatement { if in == nil { return nil } switch in := in.(type) { case *ast.BlockStatement: out := &_nodeBlockStatement{ list: make([]_nodeStatement, len(in.List)), } for i, value := range in.List { out.list[i] = cmpl.parseStatement(value) } return out case *ast.BranchStatement: out := &_nodeBranchStatement{ branch: in.Token, } if in.Label != nil { out.label = in.Label.Name } return out case *ast.DebuggerStatement: return &_nodeDebuggerStatement{} case *ast.DoWhileStatement: out := &_nodeDoWhileStatement{ test: cmpl.parseExpression(in.Test), } body := cmpl.parseStatement(in.Body) if block, ok := body.(*_nodeBlockStatement); ok { out.body = block.list } else { out.body = append(out.body, body) } return out case *ast.EmptyStatement: return emptyStatement case *ast.ExpressionStatement: return &_nodeExpressionStatement{ expression: cmpl.parseExpression(in.Expression), } case *ast.ForInStatement: out := &_nodeForInStatement{ into: cmpl.parseExpression(in.Into), source: cmpl.parseExpression(in.Source), } body := cmpl.parseStatement(in.Body) if block, ok := body.(*_nodeBlockStatement); ok { out.body = block.list } else { out.body = append(out.body, body) } return out case *ast.ForStatement: out := &_nodeForStatement{ initializer: cmpl.parseExpression(in.Initializer), update: cmpl.parseExpression(in.Update), test: cmpl.parseExpression(in.Test), } body := cmpl.parseStatement(in.Body) if block, ok := body.(*_nodeBlockStatement); ok { out.body = block.list } else { out.body = append(out.body, body) } return out case *ast.FunctionStatement: return emptyStatement case *ast.IfStatement: return &_nodeIfStatement{ test: cmpl.parseExpression(in.Test), consequent: cmpl.parseStatement(in.Consequent), alternate: cmpl.parseStatement(in.Alternate), } case *ast.LabelledStatement: return &_nodeLabelledStatement{ label: in.Label.Name, statement: cmpl.parseStatement(in.Statement), } case *ast.ReturnStatement: return &_nodeReturnStatement{ argument: cmpl.parseExpression(in.Argument), } case *ast.SwitchStatement: out := &_nodeSwitchStatement{ discriminant: cmpl.parseExpression(in.Discriminant), default_: in.Default, body: make([]*_nodeCaseStatement, len(in.Body)), } for i, clause := range in.Body { out.body[i] = &_nodeCaseStatement{ test: cmpl.parseExpression(clause.Test), consequent: make([]_nodeStatement, len(clause.Consequent)), } for j, value := range clause.Consequent { out.body[i].consequent[j] = cmpl.parseStatement(value) } } return out case *ast.ThrowStatement: return &_nodeThrowStatement{ argument: cmpl.parseExpression(in.Argument), } case *ast.TryStatement: out := &_nodeTryStatement{ body: cmpl.parseStatement(in.Body), finally: cmpl.parseStatement(in.Finally), } if in.Catch != nil { out.catch = &_nodeCatchStatement{ parameter: in.Catch.Parameter.Name, body: cmpl.parseStatement(in.Catch.Body), } } return out case *ast.VariableStatement: out := &_nodeVariableStatement{ list: make([]_nodeExpression, len(in.List)), } for i, value := range in.List { out.list[i] = cmpl.parseExpression(value) } return out case *ast.WhileStatement: out := &_nodeWhileStatement{ test: cmpl.parseExpression(in.Test), } body := cmpl.parseStatement(in.Body) if block, ok := body.(*_nodeBlockStatement); ok { out.body = block.list } else { out.body = append(out.body, body) } return out case *ast.WithStatement: return &_nodeWithStatement{ object: cmpl.parseExpression(in.Object), body: cmpl.parseStatement(in.Body), } } panic(fmt.Errorf("Here be dragons: cmpl.parseStatement(%T)", in)) } func cmpl_parse(in *ast.Program) *_nodeProgram { cmpl := _compiler{ program: in, } return cmpl.parse() } func (cmpl *_compiler) _parse(in *ast.Program) *_nodeProgram { out := &_nodeProgram{ body: make([]_nodeStatement, len(in.Body)), file: in.File, } for i, value := range in.Body { out.body[i] = cmpl.parseStatement(value) } for _, value := range in.DeclarationList { switch value := value.(type) { case *ast.FunctionDeclaration: out.functionList = append(out.functionList, cmpl.parseExpression(value.Function).(*_nodeFunctionLiteral)) case *ast.VariableDeclaration: for _, value := range value.List { out.varList = append(out.varList, value.Name) } default: panic(fmt.Errorf("Here be dragons: cmpl.parseProgram.DeclarationList(%T)", value)) } } return out } type _nodeProgram struct { body []_nodeStatement varList []string functionList []*_nodeFunctionLiteral variableList []_nodeDeclaration file *file.File } type _nodeDeclaration struct { name string definition _node } type _node interface { } type ( _nodeExpression interface { _node _expressionNode() } _nodeArrayLiteral struct { value []_nodeExpression } _nodeAssignExpression struct { operator token.Token left _nodeExpression right _nodeExpression } _nodeBinaryExpression struct { operator token.Token left _nodeExpression right _nodeExpression comparison bool } _nodeBracketExpression struct { idx file.Idx left _nodeExpression member _nodeExpression } _nodeCallExpression struct { callee _nodeExpression argumentList []_nodeExpression } _nodeConditionalExpression struct { test _nodeExpression consequent _nodeExpression alternate _nodeExpression } _nodeDotExpression struct { idx file.Idx left _nodeExpression identifier string } _nodeFunctionLiteral struct { name string body _nodeStatement source string parameterList []string varList []string functionList []*_nodeFunctionLiteral file *file.File } _nodeIdentifier struct { idx file.Idx name string } _nodeLiteral struct { value Value } _nodeNewExpression struct { callee _nodeExpression argumentList []_nodeExpression } _nodeObjectLiteral struct { value []_nodeProperty } _nodeProperty struct { key string kind string value _nodeExpression } _nodeRegExpLiteral struct { flags string pattern string // Value? regexp *regexp.Regexp } _nodeSequenceExpression struct { sequence []_nodeExpression } _nodeThisExpression struct { } _nodeUnaryExpression struct { operator token.Token operand _nodeExpression postfix bool } _nodeVariableExpression struct { idx file.Idx name string initializer _nodeExpression } ) type ( _nodeStatement interface { _node _statementNode() } _nodeBlockStatement struct { list []_nodeStatement } _nodeBranchStatement struct { branch token.Token label string } _nodeCaseStatement struct { test _nodeExpression consequent []_nodeStatement } _nodeCatchStatement struct { parameter string body _nodeStatement } _nodeDebuggerStatement struct { } _nodeDoWhileStatement struct { test _nodeExpression body []_nodeStatement } _nodeEmptyStatement struct { } _nodeExpressionStatement struct { expression _nodeExpression } _nodeForInStatement struct { into _nodeExpression source _nodeExpression body []_nodeStatement } _nodeForStatement struct { initializer _nodeExpression update _nodeExpression test _nodeExpression body []_nodeStatement } _nodeIfStatement struct { test _nodeExpression consequent _nodeStatement alternate _nodeStatement } _nodeLabelledStatement struct { label string statement _nodeStatement } _nodeReturnStatement struct { argument _nodeExpression } _nodeSwitchStatement struct { discriminant _nodeExpression default_ int body []*_nodeCaseStatement } _nodeThrowStatement struct { argument _nodeExpression } _nodeTryStatement struct { body _nodeStatement catch *_nodeCatchStatement finally _nodeStatement } _nodeVariableStatement struct { list []_nodeExpression } _nodeWhileStatement struct { test _nodeExpression body []_nodeStatement } _nodeWithStatement struct { object _nodeExpression body _nodeStatement } ) // _expressionNode func (*_nodeArrayLiteral) _expressionNode() {} func (*_nodeAssignExpression) _expressionNode() {} func (*_nodeBinaryExpression) _expressionNode() {} func (*_nodeBracketExpression) _expressionNode() {} func (*_nodeCallExpression) _expressionNode() {} func (*_nodeConditionalExpression) _expressionNode() {} func (*_nodeDotExpression) _expressionNode() {} func (*_nodeFunctionLiteral) _expressionNode() {} func (*_nodeIdentifier) _expressionNode() {} func (*_nodeLiteral) _expressionNode() {} func (*_nodeNewExpression) _expressionNode() {} func (*_nodeObjectLiteral) _expressionNode() {} func (*_nodeRegExpLiteral) _expressionNode() {} func (*_nodeSequenceExpression) _expressionNode() {} func (*_nodeThisExpression) _expressionNode() {} func (*_nodeUnaryExpression) _expressionNode() {} func (*_nodeVariableExpression) _expressionNode() {} // _statementNode func (*_nodeBlockStatement) _statementNode() {} func (*_nodeBranchStatement) _statementNode() {} func (*_nodeCaseStatement) _statementNode() {} func (*_nodeCatchStatement) _statementNode() {} func (*_nodeDebuggerStatement) _statementNode() {} func (*_nodeDoWhileStatement) _statementNode() {} func (*_nodeEmptyStatement) _statementNode() {} func (*_nodeExpressionStatement) _statementNode() {} func (*_nodeForInStatement) _statementNode() {} func (*_nodeForStatement) _statementNode() {} func (*_nodeIfStatement) _statementNode() {} func (*_nodeLabelledStatement) _statementNode() {} func (*_nodeReturnStatement) _statementNode() {} func (*_nodeSwitchStatement) _statementNode() {} func (*_nodeThrowStatement) _statementNode() {} func (*_nodeTryStatement) _statementNode() {} func (*_nodeVariableStatement) _statementNode() {} func (*_nodeWhileStatement) _statementNode() {} func (*_nodeWithStatement) _statementNode() {} golang-github-robertkrimen-otto-0.0~git20180617.15f95af/cmpl_test.go000066400000000000000000000015551331145703200247240ustar00rootroot00000000000000package otto import ( "testing" "github.com/robertkrimen/otto/parser" ) func Test_cmpl(t *testing.T) { tt(t, func() { vm := New() test := func(src string, expect ...interface{}) { program, err := parser.ParseFile(nil, "", src, 0) is(err, nil) { program := cmpl_parse(program) value := vm.runtime.cmpl_evaluate_nodeProgram(program, false) if len(expect) > 0 { is(value, expect[0]) } } } test(``, Value{}) test(`var abc = 1; abc;`, 1) test(`var abc = 1 + 1; abc;`, 2) test(`1 + 2;`, 3) }) } func TestParse_cmpl(t *testing.T) { tt(t, func() { test := func(src string) { program, err := parser.ParseFile(nil, "", src, 0) is(err, nil) is(cmpl_parse(program), "!=", nil) } test(``) test(`var abc = 1; abc;`) test(` function abc() { return; } `) }) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/console.go000066400000000000000000000017411331145703200243710ustar00rootroot00000000000000package otto import ( "fmt" "os" "strings" ) func formatForConsole(argumentList []Value) string { output := []string{} for _, argument := range argumentList { output = append(output, fmt.Sprintf("%v", argument)) } return strings.Join(output, " ") } func builtinConsole_log(call FunctionCall) Value { fmt.Fprintln(os.Stdout, formatForConsole(call.ArgumentList)) return Value{} } func builtinConsole_error(call FunctionCall) Value { fmt.Fprintln(os.Stdout, formatForConsole(call.ArgumentList)) return Value{} } // Nothing happens. func builtinConsole_dir(call FunctionCall) Value { return Value{} } func builtinConsole_time(call FunctionCall) Value { return Value{} } func builtinConsole_timeEnd(call FunctionCall) Value { return Value{} } func builtinConsole_trace(call FunctionCall) Value { return Value{} } func builtinConsole_assert(call FunctionCall) Value { return Value{} } func (runtime *_runtime) newConsole() *_object { return newConsoleObject(runtime) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/date_test.go000066400000000000000000000304701331145703200247040ustar00rootroot00000000000000package otto import ( "math" "testing" "time" ) func mockTimeLocal(location *time.Location) func() { local := time.Local time.Local = location return func() { time.Local = local } } // Passing or failing should not be dependent on what time zone we're in func mockUTC() func() { return mockTimeLocal(time.UTC) } func TestDate(t *testing.T) { tt(t, func() { test, _ := test() defer mockUTC()() time0 := time.Unix(1348616313, 47*1000*1000).Local() test(`Date`, "function Date() { [native code] }") test(`new Date(0).toUTCString()`, "Thu, 01 Jan 1970 00:00:00 UTC") test(`new Date(0).toGMTString()`, "Thu, 01 Jan 1970 00:00:00 GMT") if false { // TODO toLocale{Date,Time}String test(`new Date(0).toLocaleString()`, "") test(`new Date(0).toLocaleDateString()`, "") test(`new Date(0).toLocaleTimeString()`, "") } test(`new Date(1348616313).getTime()`, 1348616313) test(`new Date(1348616313).toUTCString()`, "Fri, 16 Jan 1970 14:36:56 UTC") test(`abc = new Date(1348616313047); abc.toUTCString()`, "Tue, 25 Sep 2012 23:38:33 UTC") test(`abc.getYear()`, time0.Year()-1900) test(`abc.getFullYear()`, time0.Year()) test(`abc.getUTCFullYear()`, 2012) test(`abc.getMonth()`, int(time0.Month())-1) // Remember, the JavaScript month is 0-based test(`abc.getUTCMonth()`, 8) test(`abc.getDate()`, time0.Day()) test(`abc.getUTCDate()`, 25) test(`abc.getDay()`, int(time0.Weekday())) test(`abc.getUTCDay()`, 2) test(`abc.getHours()`, time0.Hour()) test(`abc.getUTCHours()`, 23) test(`abc.getMinutes()`, time0.Minute()) test(`abc.getUTCMinutes()`, 38) test(`abc.getSeconds()`, time0.Second()) test(`abc.getUTCSeconds()`, 33) test(`abc.getMilliseconds()`, time0.Nanosecond()/(1000*1000)) // In honor of the 47% test(`abc.getUTCMilliseconds()`, 47) _, offset := time0.Zone() test(`abc.getTimezoneOffset()`, offset/-60) test(`new Date("Xyzzy").getTime()`, math.NaN()) test(`abc.setFullYear(2011); abc.toUTCString()`, "Sun, 25 Sep 2011 23:38:33 UTC") test(`new Date(12564504e5).toUTCString()`, "Sun, 25 Oct 2009 06:00:00 UTC") test(`new Date(2009, 9, 25).toUTCString()`, "Sun, 25 Oct 2009 00:00:00 UTC") test(`+(new Date(2009, 9, 25))`, 1256428800000) format := "Mon, 2 Jan 2006 15:04:05 MST" time1 := time.Unix(1256450400, 0) time0 = time.Date(time1.Year(), time1.Month(), time1.Day(), time1.Hour(), time1.Minute(), time1.Second(), time1.Nanosecond(), time1.Location()).UTC() time0 = time.Date(time1.Year(), time1.Month(), time1.Day(), time1.Hour(), time1.Minute(), time1.Second(), 2001*1000*1000, time1.Location()).UTC() test(`abc = new Date(12564504e5); abc.setMilliseconds(2001); abc.toUTCString()`, time0.Format(format)) time0 = time.Date(time1.Year(), time1.Month(), time1.Day(), time1.Hour(), time1.Minute(), 61, time1.Nanosecond(), time1.Location()).UTC() test(`abc = new Date(12564504e5); abc.setSeconds("61"); abc.toUTCString()`, time0.Format(format)) time0 = time.Date(time1.Year(), time1.Month(), time1.Day(), time1.Hour(), 61, time1.Second(), time1.Nanosecond(), time1.Location()).UTC() test(`abc = new Date(12564504e5); abc.setMinutes("61"); abc.toUTCString()`, time0.Format(format)) time0 = time.Date(time1.Year(), time1.Month(), time1.Day(), 5, time1.Minute(), time1.Second(), time1.Nanosecond(), time1.Location()).UTC() test(`abc = new Date(12564504e5); abc.setHours("5"); abc.toUTCString()`, time0.Format(format)) time0 = time.Date(time1.Year(), time1.Month(), 26, time1.Hour(), time1.Minute(), time1.Second(), time1.Nanosecond(), time1.Location()).UTC() test(`abc = new Date(12564504e5); abc.setDate("26"); abc.toUTCString()`, time0.Format(format)) time0 = time.Date(time1.Year(), 10, time1.Day(), time1.Hour(), time1.Minute(), time1.Second(), time1.Nanosecond(), time1.Location()).UTC() test(`abc = new Date(12564504e5); abc.setMonth(9); abc.toUTCString()`, time0.Format(format)) test(`abc = new Date(12564504e5); abc.setMonth("09"); abc.toUTCString()`, time0.Format(format)) time0 = time.Date(time1.Year(), 11, time1.Day(), time1.Hour(), time1.Minute(), time1.Second(), time1.Nanosecond(), time1.Location()).UTC() test(`abc = new Date(12564504e5); abc.setMonth("10"); abc.toUTCString()`, time0.Format(format)) time0 = time.Date(2010, time1.Month(), time1.Day(), time1.Hour(), time1.Minute(), time1.Second(), time1.Nanosecond(), time1.Location()).UTC() test(`abc = new Date(12564504e5); abc.setFullYear(2010); abc.toUTCString()`, time0.Format(format)) test(`new Date("2001-01-01T10:01:02.000").getTime()`, 978343262000) // Date() test(`typeof Date()`, "string") test(`typeof Date(2006, 1, 2)`, "string") test(` abc = Object.getOwnPropertyDescriptor(Date, "parse"); [ abc.value === Date.parse, abc.writable, abc.enumerable, abc.configurable ]; `, "true,true,false,true") test(` abc = Object.getOwnPropertyDescriptor(Date.prototype, "toTimeString"); [ abc.value === Date.prototype.toTimeString, abc.writable, abc.enumerable, abc.configurable ]; `, "true,true,false,true") test(` var abc = Object.getOwnPropertyDescriptor(Date, "prototype"); [ [ typeof Date.prototype ], [ abc.writable, abc.enumerable, abc.configurable ] ]; `, "object,false,false,false") }) } func TestDate_parse(t *testing.T) { tt(t, func() { test, _ := test() defer mockUTC()() test(`Date.parse("2001-01-01T10:01:02.000")`, 978343262000) test(`Date.parse("2006-01-02T15:04:05.000")`, 1136214245000) test(`Date.parse("2006")`, 1136073600000) test(`Date.parse("1970-01-16T14:36:56+00:00")`, 1348616000) test(`Date.parse("1970-01-16T14:36:56.313+00:00")`, 1348616313) test(`Date.parse("1970-01-16T14:36:56.000")`, 1348616000) test(`Date.parse.length`, 1) }) } func TestDate_UTC(t *testing.T) { tt(t, func() { test, _ := test() defer mockUTC()() test(`Date.UTC(2009, 9, 25)`, 1256428800000) test(`Date.UTC.length`, 7) }) } func TestDate_now(t *testing.T) { tt(t, func() { test, _ := test() defer mockUTC()() // FIXME I think this too risky test(`+(""+Date.now()).substr(0, 10)`, float64(epochToInteger(timeToEpoch(time.Now()))/1000)) test(`Date.now() - Date.now(1,2,3) < 24 * 60 * 60`, true) }) } func TestDate_toISOString(t *testing.T) { tt(t, func() { test, _ := test() defer mockUTC()() test(`new Date(0).toISOString()`, "1970-01-01T00:00:00.000Z") }) } func TestDate_toJSON(t *testing.T) { tt(t, func() { test, _ := test() defer mockUTC()() test(`new Date(0).toJSON()`, "1970-01-01T00:00:00.000Z") }) } func TestDate_setYear(t *testing.T) { tt(t, func() { test, _ := test() defer mockUTC()() test(`new Date(12564504e5).setYear(96)`, 846223200000) test(`new Date(12564504e5).setYear(1996)`, 846223200000) test(`new Date(12564504e5).setYear(2000)`, 972453600000) }) } func TestDateDefaultValue(t *testing.T) { tt(t, func() { test, _ := test() defer mockUTC()() test(` var date = new Date(); date + 0 === date.toString() + "0"; `, true) }) } func TestDate_April1978(t *testing.T) { tt(t, func() { test, _ := test() defer mockUTC()() test(` var abc = new Date(1978,3); [ abc.getYear(), abc.getMonth(), abc.valueOf() ]; `, "78,3,260236800000") }) } func TestDate_setMilliseconds(t *testing.T) { tt(t, func() { test, _ := test() defer mockUTC()() test(` abc = new Date(); def = abc.setMilliseconds(); [ abc, def ]; `, "Invalid Date,NaN") }) } func TestDate_new(t *testing.T) { // FIXME? // This is probably incorrect, due to differences in Go date/time handling // versus ECMA date/time handling, but we'll leave this here for // future reference if true { return } tt(t, func() { test, _ := test() defer mockUTC()() test(` [ new Date(1899, 11).valueOf(), new Date(1899, 12).valueOf(), new Date(1900, 0).valueOf() ] `, "-2211638400000,-2208960000000,-2208960000000") }) } func TestDateComparison(t *testing.T) { tt(t, func() { test, _ := test() defer mockUTC()() test(` var now0 = Date.now(); var now1 = (new Date()).toString(); [ now0 === now1, Math.abs(now0 - Date.parse(now1)) <= 1000 ]; `, "false,true") }) } func TestDate_setSeconds(t *testing.T) { tt(t, func() { test, _ := test() defer mockUTC()() test(` abc = new Date(1980, 10); def = new Date(abc); abc.setSeconds(10, 12); def.setSeconds(10); def.setMilliseconds(12); abc.valueOf() === def.valueOf(); `, true) test(` abc = new Date(1980, 10); def = new Date(abc); abc.setUTCSeconds(10, 12); def.setUTCSeconds(10); def.setUTCMilliseconds(12); abc.valueOf() === def.valueOf(); `, true) test(`Date.prototype.setSeconds.length`, 2) test(`Date.prototype.setUTCSeconds.length`, 2) }) } func TestDate_setMinutes(t *testing.T) { tt(t, func() { test, _ := test() defer mockUTC()() test(` abc = new Date(1980, 10); def = new Date(abc); abc.setMinutes(8, 10, 12); def.setMinutes(8); def.setSeconds(10); def.setMilliseconds(12); abc.valueOf() === def.valueOf(); `, true) test(` abc = new Date(1980, 10); def = new Date(abc); abc.setUTCMinutes(8, 10, 12); def.setUTCMinutes(8); def.setUTCSeconds(10); def.setUTCMilliseconds(12); abc.valueOf() === def.valueOf(); `, true) test(`Date.prototype.setMinutes.length`, 3) test(`Date.prototype.setUTCMinutes.length`, 3) }) } func TestDate_setHours(t *testing.T) { tt(t, func() { test, _ := test() defer mockUTC()() test(` abc = new Date(1980, 10); def = new Date(abc); abc.setHours(6, 8, 10, 12); def.setHours(6); def.setMinutes(8); def.setSeconds(10); def.setMilliseconds(12); abc.valueOf() === def.valueOf(); `, true) test(` abc = new Date(1980, 10); def = new Date(abc); abc.setUTCHours(6, 8, 10, 12); def.setUTCHours(6); def.setUTCMinutes(8); def.setUTCSeconds(10); def.setUTCMilliseconds(12); abc.valueOf() === def.valueOf(); `, true) test(`Date.prototype.setHours.length`, 4) test(`Date.prototype.setUTCHours.length`, 4) }) } func TestDate_setMonth(t *testing.T) { tt(t, func() { test, _ := test() defer mockUTC()() test(` abc = new Date(1980, 10); def = new Date(abc); abc.setMonth(6, 8); def.setMonth(6); def.setDate(8); abc.valueOf() === def.valueOf(); `, true) test(` abc = new Date(1980, 10); def = new Date(abc); abc.setUTCMonth(6, 8); def.setUTCMonth(6); def.setUTCDate(8); abc.valueOf() === def.valueOf(); `, true) test(`Date.prototype.setMonth.length`, 2) test(`Date.prototype.setUTCMonth.length`, 2) }) } func TestDate_setFullYear(t *testing.T) { tt(t, func() { test, _ := test() defer mockUTC()() test(` abc = new Date(1980, 10); def = new Date(abc); abc.setFullYear(1981, 6, 8); def.setFullYear(1981); def.setMonth(6); def.setDate(8); abc.valueOf() === def.valueOf(); `, true) test(` abc = new Date(1980, 10); def = new Date(abc); abc.setUTCFullYear(1981, 6, 8); def.setUTCFullYear(1981); def.setUTCMonth(6); def.setUTCDate(8); abc.valueOf() === def.valueOf(); `, true) test(`Date.prototype.setFullYear.length`, 3) test(`Date.prototype.setUTCFullYear.length`, 3) }) } func TestDate_setTime(t *testing.T) { tt(t, func() { test, _ := test() defer mockUTC()() test(` var abc = new Date(1999, 6, 1); var def = new Date(); def.setTime(abc.getTime()); [ def, abc.valueOf() == def.valueOf() ]; `, "Thu, 01 Jul 1999 00:00:00 UTC,true") test(`Date.prototype.setTime.length`, 1) }) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/dbg.go000066400000000000000000000002761331145703200234650ustar00rootroot00000000000000// This file was AUTOMATICALLY GENERATED by dbg-import (smuggol) for github.com/robertkrimen/dbg package otto import ( Dbg "github.com/robertkrimen/otto/dbg" ) var dbg, dbgf = Dbg.New() golang-github-robertkrimen-otto-0.0~git20180617.15f95af/dbg/000077500000000000000000000000001331145703200231315ustar00rootroot00000000000000golang-github-robertkrimen-otto-0.0~git20180617.15f95af/dbg/dbg.go000066400000000000000000000203751331145703200242230ustar00rootroot00000000000000// This file was AUTOMATICALLY GENERATED by dbg-import (smuggol) from github.com/robertkrimen/dbg /* Package dbg is a println/printf/log-debugging utility library. import ( Dbg "github.com/robertkrimen/dbg" ) dbg, dbgf := Dbg.New() dbg("Emit some debug stuff", []byte{120, 121, 122, 122, 121}, math.Pi) # "2013/01/28 16:50:03 Emit some debug stuff [120 121 122 122 121] 3.141592653589793" dbgf("With a %s formatting %.2f", "little", math.Pi) # "2013/01/28 16:51:55 With a little formatting (3.14)" dbgf("%/fatal//A fatal debug statement: should not be here") # "A fatal debug statement: should not be here" # ...and then, os.Exit(1) dbgf("%/panic//Can also panic %s", "this") # "Can also panic this" # ...as a panic, equivalent to: panic("Can also panic this") dbgf("Any %s arguments without a corresponding %%", "extra", "are treated like arguments to dbg()") # "2013/01/28 17:14:40 Any extra arguments (without a corresponding %) are treated like arguments to dbg()" dbgf("%d %d", 1, 2, 3, 4, 5) # "2013/01/28 17:16:32 Another example: 1 2 3 4 5" dbgf("%@: Include the function name for a little context (via %s)", "%@") # "2013... github.com/robertkrimen/dbg.TestSynopsis: Include the function name for a little context (via %@)" By default, dbg uses log (log.Println, log.Printf, log.Panic, etc.) for output. However, you can also provide your own output destination by invoking dbg.New with a customization function: import ( "bytes" Dbg "github.com/robertkrimen/dbg" "os" ) # dbg to os.Stderr dbg, dbgf := Dbg.New(func(dbgr *Dbgr) { dbgr.SetOutput(os.Stderr) }) # A slightly contrived example: var buffer bytes.Buffer dbg, dbgf := New(func(dbgr *Dbgr) { dbgr.SetOutput(&buffer) }) */ package dbg import ( "bytes" "fmt" "io" "log" "os" "regexp" "runtime" "strings" "unicode" ) type _frmt struct { ctl string format string operandCount int panic bool fatal bool check bool } var ( ctlTest = regexp.MustCompile(`^\s*%/`) ctlScan = regexp.MustCompile(`%?/(panic|fatal|check)(?:\s|$)`) ) func operandCount(format string) int { count := 0 end := len(format) for at := 0; at < end; { for at < end && format[at] != '%' { at++ } at++ if at < end { if format[at] != '%' && format[at] != '@' { count++ } at++ } } return count } func parseFormat(format string) (frmt _frmt) { if ctlTest.MatchString(format) { format = strings.TrimLeftFunc(format, unicode.IsSpace) index := strings.Index(format, "//") if index != -1 { frmt.ctl = format[0:index] format = format[index+2:] // Skip the second slash via +2 (instead of +1) } else { frmt.ctl = format format = "" } for _, tmp := range ctlScan.FindAllStringSubmatch(frmt.ctl, -1) { for _, value := range tmp[1:] { switch value { case "panic": frmt.panic = true case "fatal": frmt.fatal = true case "check": frmt.check = true } } } } frmt.format = format frmt.operandCount = operandCount(format) return } type Dbgr struct { emit _emit } type DbgFunction func(values ...interface{}) func NewDbgr() *Dbgr { self := &Dbgr{} return self } /* New will create and return a pair of debugging functions. You can customize where they output to by passing in an (optional) customization function: import ( Dbg "github.com/robertkrimen/dbg" "os" ) # dbg to os.Stderr dbg, dbgf := Dbg.New(func(dbgr *Dbgr) { dbgr.SetOutput(os.Stderr) }) */ func New(options ...interface{}) (dbg DbgFunction, dbgf DbgFunction) { dbgr := NewDbgr() if len(options) > 0 { if fn, ok := options[0].(func(*Dbgr)); ok { fn(dbgr) } } return dbgr.DbgDbgf() } func (self Dbgr) Dbg(values ...interface{}) { self.getEmit().emit(_frmt{}, "", values...) } func (self Dbgr) Dbgf(values ...interface{}) { self.dbgf(values...) } func (self Dbgr) DbgDbgf() (dbg DbgFunction, dbgf DbgFunction) { dbg = func(vl ...interface{}) { self.Dbg(vl...) } dbgf = func(vl ...interface{}) { self.dbgf(vl...) } return dbg, dbgf // Redundant, but... } func (self Dbgr) dbgf(values ...interface{}) { var frmt _frmt if len(values) > 0 { tmp := fmt.Sprint(values[0]) frmt = parseFormat(tmp) values = values[1:] } buffer_f := bytes.Buffer{} format := frmt.format end := len(format) for at := 0; at < end; { last := at for at < end && format[at] != '%' { at++ } if at > last { buffer_f.WriteString(format[last:at]) } if at >= end { break } // format[at] == '%' at++ // format[at] == ? if format[at] == '@' { depth := 2 pc, _, _, _ := runtime.Caller(depth) name := runtime.FuncForPC(pc).Name() buffer_f.WriteString(name) } else { buffer_f.WriteString(format[at-1 : at+1]) } at++ } //values_f := append([]interface{}{}, values[0:frmt.operandCount]...) values_f := values[0:frmt.operandCount] values_dbg := values[frmt.operandCount:] if len(values_dbg) > 0 { // Adjust frmt.format: // (%v instead of %s because: frmt.check) { tmp := format if len(tmp) > 0 { if unicode.IsSpace(rune(tmp[len(tmp)-1])) { buffer_f.WriteString("%v") } else { buffer_f.WriteString(" %v") } } else if frmt.check { // Performing a check, so no output } else { buffer_f.WriteString("%v") } } // Adjust values_f: if !frmt.check { tmp := []string{} for _, value := range values_dbg { tmp = append(tmp, fmt.Sprintf("%v", value)) } // First, make a copy of values_f, so we avoid overwriting values_dbg when appending values_f = append([]interface{}{}, values_f...) values_f = append(values_f, strings.Join(tmp, " ")) } } format = buffer_f.String() if frmt.check { // We do not actually emit to the log, but panic if // a non-nil value is detected (e.g. a non-nil error) for _, value := range values_dbg { if value != nil { if format == "" { panic(value) } else { panic(fmt.Sprintf(format, append(values_f, value)...)) } } } } else { self.getEmit().emit(frmt, format, values_f...) } } // Idiot-proof &Dbgr{}, etc. func (self *Dbgr) getEmit() _emit { if self.emit == nil { self.emit = standardEmit() } return self.emit } // SetOutput will accept the following as a destination for output: // // *log.Logger Print*/Panic*/Fatal* of the logger // io.Writer - // nil Reset to the default output (os.Stderr) // "log" Print*/Panic*/Fatal* via the "log" package // func (self *Dbgr) SetOutput(output interface{}) { if output == nil { self.emit = standardEmit() return } switch output := output.(type) { case *log.Logger: self.emit = _emitLogger{ logger: output, } return case io.Writer: self.emit = _emitWriter{ writer: output, } return case string: if output == "log" { self.emit = _emitLog{} return } } panic(output) } // ======== // // = emit = // // ======== // func standardEmit() _emit { return _emitWriter{ writer: os.Stderr, } } func ln(tmp string) string { length := len(tmp) if length > 0 && tmp[length-1] != '\n' { return tmp + "\n" } return tmp } type _emit interface { emit(_frmt, string, ...interface{}) } type _emitWriter struct { writer io.Writer } func (self _emitWriter) emit(frmt _frmt, format string, values ...interface{}) { if format == "" { fmt.Fprintln(self.writer, values...) } else { if frmt.panic { panic(fmt.Sprintf(format, values...)) } fmt.Fprintf(self.writer, ln(format), values...) if frmt.fatal { os.Exit(1) } } } type _emitLogger struct { logger *log.Logger } func (self _emitLogger) emit(frmt _frmt, format string, values ...interface{}) { if format == "" { self.logger.Println(values...) } else { if frmt.panic { self.logger.Panicf(format, values...) } else if frmt.fatal { self.logger.Fatalf(format, values...) } else { self.logger.Printf(format, values...) } } } type _emitLog struct { } func (self _emitLog) emit(frmt _frmt, format string, values ...interface{}) { if format == "" { log.Println(values...) } else { if frmt.panic { log.Panicf(format, values...) } else if frmt.fatal { log.Fatalf(format, values...) } else { log.Printf(format, values...) } } } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/documentation_test.go000066400000000000000000000032261331145703200266370ustar00rootroot00000000000000package otto import ( "fmt" ) func ExampleSynopsis() { vm := New() vm.Run(` abc = 2 + 2; console.log("The value of abc is " + abc); // 4 `) value, _ := vm.Get("abc") { value, _ := value.ToInteger() fmt.Println(value) } vm.Set("def", 11) vm.Run(` console.log("The value of def is " + def); `) vm.Set("xyzzy", "Nothing happens.") vm.Run(` console.log(xyzzy.length); `) value, _ = vm.Run("xyzzy.length") { value, _ := value.ToInteger() fmt.Println(value) } value, err := vm.Run("abcdefghijlmnopqrstuvwxyz.length") fmt.Println(value) fmt.Println(err) vm.Set("sayHello", func(call FunctionCall) Value { fmt.Printf("Hello, %s.\n", call.Argument(0).String()) return UndefinedValue() }) vm.Set("twoPlus", func(call FunctionCall) Value { right, _ := call.Argument(0).ToInteger() result, _ := vm.ToValue(2 + right) return result }) value, _ = vm.Run(` sayHello("Xyzzy"); sayHello(); result = twoPlus(2.0); `) fmt.Println(value) // Output: // The value of abc is 4 // 4 // The value of def is 11 // 16 // 16 // undefined // ReferenceError: 'abcdefghijlmnopqrstuvwxyz' is not defined // Hello, Xyzzy. // Hello, undefined. // 4 } func ExampleConsole() { vm := New() console := map[string]interface{}{ "log": func(call FunctionCall) Value { fmt.Println("console.log:", formatForConsole(call.ArgumentList)) return UndefinedValue() }, } err := vm.Set("console", console) value, err := vm.Run(` console.log("Hello, World."); `) fmt.Println(value) fmt.Println(err) // Output: // console.log: Hello, World. // undefined // } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/error.go000066400000000000000000000114621331145703200240610ustar00rootroot00000000000000package otto import ( "errors" "fmt" "github.com/robertkrimen/otto/file" ) type _exception struct { value interface{} } func newException(value interface{}) *_exception { return &_exception{ value: value, } } func (self *_exception) eject() interface{} { value := self.value self.value = nil // Prevent Go from holding on to the value, whatever it is return value } type _error struct { name string message string trace []_frame offset int } func (err _error) format() string { if len(err.name) == 0 { return err.message } if len(err.message) == 0 { return err.name } return fmt.Sprintf("%s: %s", err.name, err.message) } func (err _error) formatWithStack() string { str := err.format() + "\n" for _, frame := range err.trace { str += " at " + frame.location() + "\n" } return str } type _frame struct { native bool nativeFile string nativeLine int file *file.File offset int callee string fn interface{} } var ( nativeFrame = _frame{} ) type _at int func (fr _frame) location() string { str := "" switch { case fr.native: str = "" if fr.nativeFile != "" && fr.nativeLine != 0 { str = fmt.Sprintf("%s:%d", fr.nativeFile, fr.nativeLine) } case fr.file != nil: if p := fr.file.Position(file.Idx(fr.offset)); p != nil { path, line, column := p.Filename, p.Line, p.Column if path == "" { path = "" } str = fmt.Sprintf("%s:%d:%d", path, line, column) } } if fr.callee != "" { str = fmt.Sprintf("%s (%s)", fr.callee, str) } return str } // An Error represents a runtime error, e.g. a TypeError, a ReferenceError, etc. type Error struct { _error } // Error returns a description of the error // // TypeError: 'def' is not a function // func (err Error) Error() string { return err.format() } // String returns a description of the error and a trace of where the // error occurred. // // TypeError: 'def' is not a function // at xyz (:3:9) // at :7:1/ // func (err Error) String() string { return err.formatWithStack() } func (err _error) describe(format string, in ...interface{}) string { return fmt.Sprintf(format, in...) } func (self _error) messageValue() Value { if self.message == "" { return Value{} } return toValue_string(self.message) } func (rt *_runtime) typeErrorResult(throw bool) bool { if throw { panic(rt.panicTypeError()) } return false } func newError(rt *_runtime, name string, stackFramesToPop int, in ...interface{}) _error { err := _error{ name: name, offset: -1, } description := "" length := len(in) if rt != nil && rt.scope != nil { scope := rt.scope for i := 0; i < stackFramesToPop; i++ { if scope.outer != nil { scope = scope.outer } } frame := scope.frame if length > 0 { if at, ok := in[length-1].(_at); ok { in = in[0 : length-1] if scope != nil { frame.offset = int(at) } length-- } if length > 0 { description, in = in[0].(string), in[1:] } } limit := rt.traceLimit err.trace = append(err.trace, frame) if scope != nil { for scope = scope.outer; scope != nil; scope = scope.outer { if limit--; limit == 0 { break } if scope.frame.offset >= 0 { err.trace = append(err.trace, scope.frame) } } } } else { if length > 0 { description, in = in[0].(string), in[1:] } } err.message = err.describe(description, in...) return err } func (rt *_runtime) panicTypeError(argumentList ...interface{}) *_exception { return &_exception{ value: newError(rt, "TypeError", 0, argumentList...), } } func (rt *_runtime) panicReferenceError(argumentList ...interface{}) *_exception { return &_exception{ value: newError(rt, "ReferenceError", 0, argumentList...), } } func (rt *_runtime) panicURIError(argumentList ...interface{}) *_exception { return &_exception{ value: newError(rt, "URIError", 0, argumentList...), } } func (rt *_runtime) panicSyntaxError(argumentList ...interface{}) *_exception { return &_exception{ value: newError(rt, "SyntaxError", 0, argumentList...), } } func (rt *_runtime) panicRangeError(argumentList ...interface{}) *_exception { return &_exception{ value: newError(rt, "RangeError", 0, argumentList...), } } func catchPanic(function func()) (err error) { defer func() { if caught := recover(); caught != nil { if exception, ok := caught.(*_exception); ok { caught = exception.eject() } switch caught := caught.(type) { case *Error: err = caught return case _error: err = &Error{caught} return case Value: if vl := caught._object(); vl != nil { switch vl := vl.value.(type) { case _error: err = &Error{vl} return } } err = errors.New(caught.string()) return } panic(caught) } }() function() return nil } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/error_native_test.go000066400000000000000000000016451331145703200264700ustar00rootroot00000000000000package otto import ( "testing" ) // this is its own file because the tests in it rely on the line numbers of // some of the functions defined here. putting it in with the rest of the // tests would probably be annoying. func TestErrorContextNative(t *testing.T) { tt(t, func() { vm := New() vm.Set("N", func(c FunctionCall) Value { v, err := c.Argument(0).Call(NullValue()) if err != nil { panic(err) } return v }) s, _ := vm.Compile("test.js", ` function F() { throw new Error('wow'); } function G() { return N(F); } `) vm.Run(s) f1, _ := vm.Get("G") _, err := f1.Call(NullValue()) err1 := err.(*Error) is(err1.message, "wow") is(len(err1.trace), 3) is(err1.trace[0].location(), "F (test.js:2:29)") is(err1.trace[1].location(), "github.com/robertkrimen/otto.TestErrorContextNative.func1.1 (error_native_test.go:15)") is(err1.trace[2].location(), "G (test.js:3:26)") }) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/error_test.go000066400000000000000000000222601331145703200251160ustar00rootroot00000000000000package otto import ( "testing" ) func TestError(t *testing.T) { tt(t, func() { test, _ := test() test(` [ Error.prototype.name, Error.prototype.message, Error.prototype.hasOwnProperty("message") ]; `, "Error,,true") }) } func TestError_instanceof(t *testing.T) { tt(t, func() { test, _ := test() test(`(new TypeError()) instanceof Error`, true) }) } func TestPanicValue(t *testing.T) { tt(t, func() { test, vm := test() vm.Set("abc", func(call FunctionCall) Value { value, err := call.Otto.Run(`({ def: 3.14159 })`) is(err, nil) panic(value) }) test(` try { abc(); } catch (err) { error = err; } [ error instanceof Error, error.message, error.def ]; `, "false,,3.14159") }) } func Test_catchPanic(t *testing.T) { tt(t, func() { vm := New() _, err := vm.Run(` A syntax error that does not define var; abc; `) is(err, "!=", nil) _, err = vm.Call(`abc.def`, nil) is(err, "!=", nil) }) } func TestErrorContext(t *testing.T) { tt(t, func() { vm := New() _, err := vm.Run(` undefined(); `) { err := err.(*Error) is(err.message, "'undefined' is not a function") is(len(err.trace), 1) is(err.trace[0].location(), ":2:13") } _, err = vm.Run(` ({}).abc(); `) { err := err.(*Error) is(err.message, "'abc' is not a function") is(len(err.trace), 1) is(err.trace[0].location(), ":2:14") } _, err = vm.Run(` ("abc").abc(); `) { err := err.(*Error) is(err.message, "'abc' is not a function") is(len(err.trace), 1) is(err.trace[0].location(), ":2:14") } _, err = vm.Run(` var ghi = "ghi"; ghi(); `) { err := err.(*Error) is(err.message, "'ghi' is not a function") is(len(err.trace), 1) is(err.trace[0].location(), ":3:13") } _, err = vm.Run(` function def() { undefined(); } function abc() { def(); } abc(); `) { err := err.(*Error) is(err.message, "'undefined' is not a function") is(len(err.trace), 3) is(err.trace[0].location(), "def (:3:17)") is(err.trace[1].location(), "abc (:6:17)") is(err.trace[2].location(), ":8:13") } _, err = vm.Run(` function abc() { xyz(); } abc(); `) { err := err.(*Error) is(err.message, "'xyz' is not defined") is(len(err.trace), 2) is(err.trace[0].location(), "abc (:3:17)") is(err.trace[1].location(), ":5:13") } _, err = vm.Run(` mno + 1; `) { err := err.(*Error) is(err.message, "'mno' is not defined") is(len(err.trace), 1) is(err.trace[0].location(), ":2:13") } _, err = vm.Run(` eval("xyz();"); `) { err := err.(*Error) is(err.message, "'xyz' is not defined") is(len(err.trace), 1) is(err.trace[0].location(), ":1:1") } _, err = vm.Run(` xyzzy = "Nothing happens." eval("xyzzy();"); `) { err := err.(*Error) is(err.message, "'xyzzy' is not a function") is(len(err.trace), 1) is(err.trace[0].location(), ":1:1") } _, err = vm.Run(` throw Error("xyzzy"); `) { err := err.(*Error) is(err.message, "xyzzy") is(len(err.trace), 1) is(err.trace[0].location(), ":2:19") } _, err = vm.Run(` throw new Error("xyzzy"); `) { err := err.(*Error) is(err.message, "xyzzy") is(len(err.trace), 1) is(err.trace[0].location(), ":2:23") } script1, err := vm.Compile("file1.js", `function A() { throw new Error("test"); } function C() { var o = null; o.prop = 1; } `) is(err, nil) _, err = vm.Run(script1) is(err, nil) script2, err := vm.Compile("file2.js", `function B() { A() } `) is(err, nil) _, err = vm.Run(script2) is(err, nil) script3, err := vm.Compile("file3.js", "B()") is(err, nil) _, err = vm.Run(script3) { err := err.(*Error) is(err.message, "test") is(len(err.trace), 3) is(err.trace[0].location(), "A (file1.js:2:15)") is(err.trace[1].location(), "B (file2.js:2:5)") is(err.trace[2].location(), "file3.js:1:1") } { f, _ := vm.Get("B") _, err := f.Call(UndefinedValue()) err1 := err.(*Error) is(err1.message, "test") is(len(err1.trace), 2) is(err1.trace[0].location(), "A (file1.js:2:15)") is(err1.trace[1].location(), "B (file2.js:2:5)") } { f, _ := vm.Get("C") _, err := f.Call(UndefinedValue()) err1 := err.(*Error) is(err1.message, "Cannot access member 'prop' of null") is(len(err1.trace), 1) is(err1.trace[0].location(), "C (file1.js:7:5)") } }) } func TestMakeCustomErrorReturn(t *testing.T) { tt(t, func() { vm := New() vm.Set("A", func(c FunctionCall) Value { return vm.MakeCustomError("CarrotError", "carrots is life, carrots is love") }) s, _ := vm.Compile("test.js", ` function B() { return A(); } function C() { return B(); } function D() { return C(); } `) if _, err := vm.Run(s); err != nil { panic(err) } v, err := vm.Call("D", nil) if err != nil { panic(err) } is(v.Class(), "Error") name, err := v.Object().Get("name") if err != nil { panic(err) } is(name.String(), "CarrotError") message, err := v.Object().Get("message") if err != nil { panic(err) } is(message.String(), "carrots is life, carrots is love") str, err := v.Object().Call("toString") if err != nil { panic(err) } is(str, "CarrotError: carrots is life, carrots is love") i, err := v.Export() if err != nil { panic(err) } t.Logf("%#v\n", i) }) } func TestMakeCustomError(t *testing.T) { tt(t, func() { vm := New() vm.Set("A", func(c FunctionCall) Value { panic(vm.MakeCustomError("CarrotError", "carrots is life, carrots is love")) return UndefinedValue() }) s, _ := vm.Compile("test.js", ` function B() { A(); } function C() { B(); } function D() { C(); } `) if _, err := vm.Run(s); err != nil { panic(err) } _, err := vm.Call("D", nil) if err == nil { panic("error should not be nil") } is(err.Error(), "CarrotError: carrots is life, carrots is love") er := err.(*Error) is(er.name, "CarrotError") is(er.message, "carrots is life, carrots is love") }) } func TestMakeCustomErrorFreshVM(t *testing.T) { tt(t, func() { vm := New() e := vm.MakeCustomError("CarrotError", "carrots is life, carrots is love") str, err := e.ToString() if err != nil { panic(err) } is(str, "CarrotError: carrots is life, carrots is love") }) } func TestMakeTypeError(t *testing.T) { tt(t, func() { vm := New() vm.Set("A", func(c FunctionCall) Value { panic(vm.MakeTypeError("these aren't my glasses")) return UndefinedValue() }) s, _ := vm.Compile("test.js", ` function B() { A(); } function C() { B(); } function D() { C(); } `) if _, err := vm.Run(s); err != nil { panic(err) } _, err := vm.Call("D", nil) if err == nil { panic("error should not be nil") } is(err.Error(), "TypeError: these aren't my glasses") er := err.(*Error) is(er.name, "TypeError") is(er.message, "these aren't my glasses") }) } func TestMakeRangeError(t *testing.T) { tt(t, func() { vm := New() vm.Set("A", func(c FunctionCall) Value { panic(vm.MakeRangeError("too many")) return UndefinedValue() }) s, _ := vm.Compile("test.js", ` function B() { A(); } function C() { B(); } function D() { C(); } `) if _, err := vm.Run(s); err != nil { panic(err) } _, err := vm.Call("D", nil) if err == nil { panic("error should not be nil") } is(err.Error(), "RangeError: too many") er := err.(*Error) is(er.name, "RangeError") is(er.message, "too many") }) } func TestMakeSyntaxError(t *testing.T) { tt(t, func() { vm := New() vm.Set("A", func(c FunctionCall) Value { panic(vm.MakeSyntaxError("i think you meant \"you're\"")) return UndefinedValue() }) s, _ := vm.Compile("test.js", ` function B() { A(); } function C() { B(); } function D() { C(); } `) if _, err := vm.Run(s); err != nil { panic(err) } _, err := vm.Call("D", nil) if err == nil { panic("error should not be nil") } is(err.Error(), "SyntaxError: i think you meant \"you're\"") er := err.(*Error) is(er.name, "SyntaxError") is(er.message, "i think you meant \"you're\"") }) } func TestErrorStackProperty(t *testing.T) { tt(t, func() { vm := New() s, err := vm.Compile("test.js", ` function A() { throw new TypeError('uh oh'); } function B() { return A(); } function C() { return B(); } var s = null; try { C(); } catch (e) { s = e.stack; } s; `) if err != nil { panic(err) } v, err := vm.Run(s) if err != nil { panic(err) } is(v.String(), "TypeError: uh oh\n at A (test.js:2:29)\n at B (test.js:3:26)\n at C (test.js:4:26)\n at test.js:8:10\n") }) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/evaluate.go000066400000000000000000000201121331145703200245260ustar00rootroot00000000000000package otto import ( "fmt" "math" "strings" "github.com/robertkrimen/otto/token" ) func (self *_runtime) evaluateMultiply(left float64, right float64) Value { // TODO 11.5.1 return Value{} } func (self *_runtime) evaluateDivide(left float64, right float64) Value { if math.IsNaN(left) || math.IsNaN(right) { return NaNValue() } if math.IsInf(left, 0) && math.IsInf(right, 0) { return NaNValue() } if left == 0 && right == 0 { return NaNValue() } if math.IsInf(left, 0) { if math.Signbit(left) == math.Signbit(right) { return positiveInfinityValue() } else { return negativeInfinityValue() } } if math.IsInf(right, 0) { if math.Signbit(left) == math.Signbit(right) { return positiveZeroValue() } else { return negativeZeroValue() } } if right == 0 { if math.Signbit(left) == math.Signbit(right) { return positiveInfinityValue() } else { return negativeInfinityValue() } } return toValue_float64(left / right) } func (self *_runtime) evaluateModulo(left float64, right float64) Value { // TODO 11.5.3 return Value{} } func (self *_runtime) calculateBinaryExpression(operator token.Token, left Value, right Value) Value { leftValue := left.resolve() switch operator { // Additive case token.PLUS: leftValue = toPrimitive(leftValue) rightValue := right.resolve() rightValue = toPrimitive(rightValue) if leftValue.IsString() || rightValue.IsString() { return toValue_string(strings.Join([]string{leftValue.string(), rightValue.string()}, "")) } else { return toValue_float64(leftValue.float64() + rightValue.float64()) } case token.MINUS: rightValue := right.resolve() return toValue_float64(leftValue.float64() - rightValue.float64()) // Multiplicative case token.MULTIPLY: rightValue := right.resolve() return toValue_float64(leftValue.float64() * rightValue.float64()) case token.SLASH: rightValue := right.resolve() return self.evaluateDivide(leftValue.float64(), rightValue.float64()) case token.REMAINDER: rightValue := right.resolve() return toValue_float64(math.Mod(leftValue.float64(), rightValue.float64())) // Logical case token.LOGICAL_AND: left := leftValue.bool() if !left { return falseValue } return toValue_bool(right.resolve().bool()) case token.LOGICAL_OR: left := leftValue.bool() if left { return trueValue } return toValue_bool(right.resolve().bool()) // Bitwise case token.AND: rightValue := right.resolve() return toValue_int32(toInt32(leftValue) & toInt32(rightValue)) case token.OR: rightValue := right.resolve() return toValue_int32(toInt32(leftValue) | toInt32(rightValue)) case token.EXCLUSIVE_OR: rightValue := right.resolve() return toValue_int32(toInt32(leftValue) ^ toInt32(rightValue)) // Shift // (Masking of 0x1f is to restrict the shift to a maximum of 31 places) case token.SHIFT_LEFT: rightValue := right.resolve() return toValue_int32(toInt32(leftValue) << (toUint32(rightValue) & 0x1f)) case token.SHIFT_RIGHT: rightValue := right.resolve() return toValue_int32(toInt32(leftValue) >> (toUint32(rightValue) & 0x1f)) case token.UNSIGNED_SHIFT_RIGHT: rightValue := right.resolve() // Shifting an unsigned integer is a logical shift return toValue_uint32(toUint32(leftValue) >> (toUint32(rightValue) & 0x1f)) case token.INSTANCEOF: rightValue := right.resolve() if !rightValue.IsObject() { panic(self.panicTypeError("Expecting a function in instanceof check, but got: %v", rightValue)) } return toValue_bool(rightValue._object().hasInstance(leftValue)) case token.IN: rightValue := right.resolve() if !rightValue.IsObject() { panic(self.panicTypeError()) } return toValue_bool(rightValue._object().hasProperty(leftValue.string())) } panic(hereBeDragons(operator)) } func valueKindDispatchKey(left _valueKind, right _valueKind) int { return (int(left) << 2) + int(right) } var equalDispatch map[int](func(Value, Value) bool) = makeEqualDispatch() func makeEqualDispatch() map[int](func(Value, Value) bool) { key := valueKindDispatchKey return map[int](func(Value, Value) bool){ key(valueNumber, valueObject): func(x Value, y Value) bool { return x.float64() == y.float64() }, key(valueString, valueObject): func(x Value, y Value) bool { return x.float64() == y.float64() }, key(valueObject, valueNumber): func(x Value, y Value) bool { return x.float64() == y.float64() }, key(valueObject, valueString): func(x Value, y Value) bool { return x.float64() == y.float64() }, } } type _lessThanResult int const ( lessThanFalse _lessThanResult = iota lessThanTrue lessThanUndefined ) func calculateLessThan(left Value, right Value, leftFirst bool) _lessThanResult { x := Value{} y := x if leftFirst { x = toNumberPrimitive(left) y = toNumberPrimitive(right) } else { y = toNumberPrimitive(right) x = toNumberPrimitive(left) } result := false if x.kind != valueString || y.kind != valueString { x, y := x.float64(), y.float64() if math.IsNaN(x) || math.IsNaN(y) { return lessThanUndefined } result = x < y } else { x, y := x.string(), y.string() result = x < y } if result { return lessThanTrue } return lessThanFalse } // FIXME Probably a map is not the most efficient way to do this var lessThanTable [4](map[_lessThanResult]bool) = [4](map[_lessThanResult]bool){ // < map[_lessThanResult]bool{ lessThanFalse: false, lessThanTrue: true, lessThanUndefined: false, }, // > map[_lessThanResult]bool{ lessThanFalse: false, lessThanTrue: true, lessThanUndefined: false, }, // <= map[_lessThanResult]bool{ lessThanFalse: true, lessThanTrue: false, lessThanUndefined: false, }, // >= map[_lessThanResult]bool{ lessThanFalse: true, lessThanTrue: false, lessThanUndefined: false, }, } func (self *_runtime) calculateComparison(comparator token.Token, left Value, right Value) bool { // FIXME Use strictEqualityComparison? // TODO This might be redundant now (with regards to evaluateComparison) x := left.resolve() y := right.resolve() kindEqualKind := false result := true negate := false switch comparator { case token.LESS: result = lessThanTable[0][calculateLessThan(x, y, true)] case token.GREATER: result = lessThanTable[1][calculateLessThan(y, x, false)] case token.LESS_OR_EQUAL: result = lessThanTable[2][calculateLessThan(y, x, false)] case token.GREATER_OR_EQUAL: result = lessThanTable[3][calculateLessThan(x, y, true)] case token.STRICT_NOT_EQUAL: negate = true fallthrough case token.STRICT_EQUAL: if x.kind != y.kind { result = false } else { kindEqualKind = true } case token.NOT_EQUAL: negate = true fallthrough case token.EQUAL: if x.kind == y.kind { kindEqualKind = true } else if x.kind <= valueNull && y.kind <= valueNull { result = true } else if x.kind <= valueNull || y.kind <= valueNull { result = false } else if x.kind <= valueString && y.kind <= valueString { result = x.float64() == y.float64() } else if x.kind == valueBoolean { result = self.calculateComparison(token.EQUAL, toValue_float64(x.float64()), y) } else if y.kind == valueBoolean { result = self.calculateComparison(token.EQUAL, x, toValue_float64(y.float64())) } else if x.kind == valueObject { result = self.calculateComparison(token.EQUAL, toPrimitive(x), y) } else if y.kind == valueObject { result = self.calculateComparison(token.EQUAL, x, toPrimitive(y)) } else { panic(hereBeDragons("Unable to test for equality: %v ==? %v", x, y)) } default: panic(fmt.Errorf("Unknown comparator %s", comparator.String())) } if kindEqualKind { switch x.kind { case valueUndefined, valueNull: result = true case valueNumber: x := x.float64() y := y.float64() if math.IsNaN(x) || math.IsNaN(y) { result = false } else { result = x == y } case valueString: result = x.string() == y.string() case valueBoolean: result = x.bool() == y.bool() case valueObject: result = x._object() == y._object() default: goto ERROR } } if negate { result = !result } return result ERROR: panic(hereBeDragons("%v (%v) %s %v (%v)", x, x.kind, comparator, y, y.kind)) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/file/000077500000000000000000000000001331145703200233145ustar00rootroot00000000000000golang-github-robertkrimen-otto-0.0~git20180617.15f95af/file/README.markdown000066400000000000000000000037341331145703200260240ustar00rootroot00000000000000# file -- import "github.com/robertkrimen/otto/file" Package file encapsulates the file abstractions used by the ast & parser. ## Usage #### type File ```go type File struct { } ``` #### func NewFile ```go func NewFile(filename, src string, base int) *File ``` #### func (*File) Base ```go func (fl *File) Base() int ``` #### func (*File) Name ```go func (fl *File) Name() string ``` #### func (*File) Source ```go func (fl *File) Source() string ``` #### type FileSet ```go type FileSet struct { } ``` A FileSet represents a set of source files. #### func (*FileSet) AddFile ```go func (self *FileSet) AddFile(filename, src string) int ``` AddFile adds a new file with the given filename and src. This an internal method, but exported for cross-package use. #### func (*FileSet) File ```go func (self *FileSet) File(idx Idx) *File ``` #### func (*FileSet) Position ```go func (self *FileSet) Position(idx Idx) *Position ``` Position converts an Idx in the FileSet into a Position. #### type Idx ```go type Idx int ``` Idx is a compact encoding of a source position within a file set. It can be converted into a Position for a more convenient, but much larger, representation. #### type Position ```go type Position struct { Filename string // The filename where the error occurred, if any Offset int // The src offset Line int // The line number, starting at 1 Column int // The column number, starting at 1 (The character count) } ``` Position describes an arbitrary source position including the filename, line, and column location. #### func (*Position) String ```go func (self *Position) String() string ``` String returns a string in one of several forms: file:line:column A valid position with filename line:column A valid position without filename file An invalid position with filename - An invalid position without filename -- **godocdown** http://github.com/robertkrimen/godocdown golang-github-robertkrimen-otto-0.0~git20180617.15f95af/file/file.go000066400000000000000000000065101331145703200245640ustar00rootroot00000000000000// Package file encapsulates the file abstractions used by the ast & parser. // package file import ( "fmt" "strings" "gopkg.in/sourcemap.v1" ) // Idx is a compact encoding of a source position within a file set. // It can be converted into a Position for a more convenient, but much // larger, representation. type Idx int // Position describes an arbitrary source position // including the filename, line, and column location. type Position struct { Filename string // The filename where the error occurred, if any Offset int // The src offset Line int // The line number, starting at 1 Column int // The column number, starting at 1 (The character count) } // A Position is valid if the line number is > 0. func (self *Position) isValid() bool { return self.Line > 0 } // String returns a string in one of several forms: // // file:line:column A valid position with filename // line:column A valid position without filename // file An invalid position with filename // - An invalid position without filename // func (self *Position) String() string { str := self.Filename if self.isValid() { if str != "" { str += ":" } str += fmt.Sprintf("%d:%d", self.Line, self.Column) } if str == "" { str = "-" } return str } // FileSet // A FileSet represents a set of source files. type FileSet struct { files []*File last *File } // AddFile adds a new file with the given filename and src. // // This an internal method, but exported for cross-package use. func (self *FileSet) AddFile(filename, src string) int { base := self.nextBase() file := &File{ name: filename, src: src, base: base, } self.files = append(self.files, file) self.last = file return base } func (self *FileSet) nextBase() int { if self.last == nil { return 1 } return self.last.base + len(self.last.src) + 1 } func (self *FileSet) File(idx Idx) *File { for _, file := range self.files { if idx <= Idx(file.base+len(file.src)) { return file } } return nil } // Position converts an Idx in the FileSet into a Position. func (self *FileSet) Position(idx Idx) *Position { for _, file := range self.files { if idx <= Idx(file.base+len(file.src)) { return file.Position(idx - Idx(file.base)) } } return nil } type File struct { name string src string base int // This will always be 1 or greater sm *sourcemap.Consumer } func NewFile(filename, src string, base int) *File { return &File{ name: filename, src: src, base: base, } } func (fl *File) WithSourceMap(sm *sourcemap.Consumer) *File { fl.sm = sm return fl } func (fl *File) Name() string { return fl.name } func (fl *File) Source() string { return fl.src } func (fl *File) Base() int { return fl.base } func (fl *File) Position(idx Idx) *Position { position := &Position{} offset := int(idx) - fl.base if offset >= len(fl.src) || offset < 0 { return nil } src := fl.src[:offset] position.Filename = fl.name position.Offset = offset position.Line = strings.Count(src, "\n") + 1 if index := strings.LastIndex(src, "\n"); index >= 0 { position.Column = offset - index } else { position.Column = len(src) + 1 } if fl.sm != nil { if f, _, l, c, ok := fl.sm.Source(position.Line, position.Column); ok { position.Filename, position.Line, position.Column = f, l, c } } return position } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/function_stack_test.go000066400000000000000000000033561331145703200270040ustar00rootroot00000000000000package otto import ( "testing" ) // this is its own file because the tests in it rely on the line numbers of // some of the functions defined here. putting it in with the rest of the // tests would probably be annoying. func TestFunction_stack(t *testing.T) { tt(t, func() { vm := New() s, _ := vm.Compile("fake.js", `function X(fn1, fn2, fn3) { fn1(fn2, fn3); }`) vm.Run(s) expected := []_frame{ {native: true, nativeFile: "function_stack_test.go", nativeLine: 30, offset: 0, callee: "github.com/robertkrimen/otto.TestFunction_stack.func1.2"}, {native: true, nativeFile: "function_stack_test.go", nativeLine: 25, offset: 0, callee: "github.com/robertkrimen/otto.TestFunction_stack.func1.1"}, {native: false, nativeFile: "", nativeLine: 0, offset: 29, callee: "X", file: s.program.file}, {native: false, nativeFile: "", nativeLine: 0, offset: 29, callee: "X", file: s.program.file}, } vm.Set("A", func(c FunctionCall) Value { c.Argument(0).Call(UndefinedValue()) return UndefinedValue() }) vm.Set("B", func(c FunctionCall) Value { depth := 0 for scope := c.Otto.runtime.scope; scope != nil; scope = scope.outer { // these properties are tested explicitly so that we don't test `.fn`, // which will differ from run to run is(scope.frame.native, expected[depth].native) is(scope.frame.nativeFile, expected[depth].nativeFile) is(scope.frame.nativeLine, expected[depth].nativeLine) is(scope.frame.offset, expected[depth].offset) is(scope.frame.callee, expected[depth].callee) is(scope.frame.file, expected[depth].file) depth++ } is(depth, 4) return UndefinedValue() }) x, _ := vm.Get("X") a, _ := vm.Get("A") b, _ := vm.Get("B") x.Call(UndefinedValue(), x, a, b) }) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/function_test.go000066400000000000000000000214041331145703200256110ustar00rootroot00000000000000package otto import ( "testing" ) func TestFunction(t *testing.T) { tt(t, func() { test, _ := test() test(` var abc = Object.getOwnPropertyDescriptor(Function, "prototype"); [ [ typeof Function.prototype, typeof Function.prototype.length, Function.prototype.length ], [ abc.writable, abc.enumerable, abc.configurable ] ]; `, "function,number,0,false,false,false") }) } func Test_argumentList2parameterList(t *testing.T) { tt(t, func() { is(argumentList2parameterList([]Value{toValue("abc, def"), toValue("ghi")}), []string{"abc", "def", "ghi"}) }) } func TestFunction_new(t *testing.T) { tt(t, func() { test, _ := test() test(`raise: new Function({}); `, "SyntaxError: Unexpected identifier") test(` var abc = Function("def, ghi", "jkl", "return def+ghi+jkl"); [ typeof abc, abc instanceof Function, abc("ab", "ba", 1) ]; `, "function,true,abba1") test(`raise: var abc = { toString: function() { throw 1; } }; var def = { toString: function() { throw 2; } }; var ghi = new Function(abc, def); ghi; `, "1") // S15.3.2.1_A3_T10 test(`raise: var abc = { toString: function() { return "z;x"; } }; var def = "return this"; var ghi = new Function(abc, def); ghi; `, "SyntaxError: Unexpected token ;") test(`raise: var abc; var def = "return true"; var ghi = new Function(null, def); ghi; `, "SyntaxError: Unexpected token null") }) } func TestFunction_apply(t *testing.T) { tt(t, func() { test, _ := test() test(`Function.prototype.apply.length`, 2) test(`String.prototype.substring.apply("abc", [1, 11])`, "bc") }) } func TestFunction_call(t *testing.T) { tt(t, func() { test, _ := test() test(`Function.prototype.call.length`, 1) test(`String.prototype.substring.call("abc", 1, 11)`, "bc") }) } func TestFunctionArguments(t *testing.T) { tt(t, func() { test, _ := test() // Should not be able to delete arguments test(` function abc(def, arguments){ delete def; return def; } abc(1); `, 1) // Again, should not be able to delete arguments test(` function abc(def){ delete def; return def; } abc(1); `, 1) // Test typeof of a function argument test(` function abc(def, ghi, jkl){ return typeof jkl } abc("1st", "2nd", "3rd", "4th", "5th"); `, "string") test(` function abc(def, ghi, jkl){ arguments[0] = 3.14; arguments[1] = 'Nothing happens'; arguments[2] = 42; if (3.14 === def && 'Nothing happens' === ghi && 42 === jkl) return true; } abc(-1, 4.2, 314); `, true) }) } func TestFunctionDeclarationInFunction(t *testing.T) { tt(t, func() { test, _ := test() // Function declarations happen AFTER parameter/argument declarations // That is, a function declared within a function will shadow/overwrite // declared parameters test(` function abc(def){ return def; function def(){ return 1; } } typeof abc(); `, "function") }) } func TestArguments_defineOwnProperty(t *testing.T) { tt(t, func() { test, _ := test() test(` var abc; var def = true; var ghi = {}; (function (a, b, c) { Object.defineProperty(arguments, "0", { value: 42, writable: false, enumerable: false, configurable: false }); Object.defineProperty(arguments, "1", { value: 3.14, configurable: true, enumerable: true }); abc = Object.getOwnPropertyDescriptor(arguments, "0"); for (var name in arguments) { ghi[name] = (ghi[name] || 0) + 1; if (name === "0") { def = false; } } }(0, 1, 2)); [ abc.value, abc.writable, abc.enumerable, abc.configurable, def, ghi["1"] ]; `, "42,false,false,false,true,1") }) } func TestFunction_bind(t *testing.T) { tt(t, func() { test, _ := test() defer mockUTC()() test(` abc = function(){ return "abc"; }; def = abc.bind(); [ typeof def.prototype, typeof def.hasOwnProperty, def.hasOwnProperty("caller"), def.hasOwnProperty("arguments"), def() ]; `, "object,function,true,true,abc") test(` abc = function(){ return arguments[1]; }; def = abc.bind(undefined, "abc"); ghi = abc.bind(undefined, "abc", "ghi"); [ def(), def("def"), ghi("def") ]; `, ",def,ghi") test(` var abc = function () {}; var ghi; try { Object.defineProperty(Function.prototype, "xyzzy", { value: 1001, writable: true, enumerable: true, configurable: true }); var def = abc.bind({}); ghi = !def.hasOwnProperty("xyzzy") && ghi.xyzzy === 1001; } finally { delete Function.prototype.xyzzy; } [ ghi ]; `, "true") test(` var abc = function (def, ghi) {}; var jkl = abc.bind({}); var mno = abc.bind({}, 1, 2); [ jkl.length, mno.length ]; `, "2,0") test(`raise: Math.bind(); `, "TypeError: 'bind' is not a function") test(` function construct(fn, arguments) { var bound = Function.prototype.bind.apply(fn, [null].concat(arguments)); return new bound(); } var abc = construct(Date, [1957, 4, 27]); Object.prototype.toString.call(abc); `, "[object Date]") test(` var fn = function (x, y, z) { var result = {}; result.abc = x + y + z; result.def = arguments[0] === "a" && arguments.length === 3; return result; }; var newFn = Function.prototype.bind.call(fn, {}, "a", "b", "c"); var result = new newFn(); [ result.hasOwnProperty("abc"), result.hasOwnProperty("def"), result.abc, result.def ]; `, "true,true,abc,true") test(` abc = function(){ return "abc"; }; def = abc.bind(); def.toString(); `, "function () { [native code] }") }) } func TestFunction_toString(t *testing.T) { tt(t, func() { test, _ := test() test(`raise: Function.prototype.toString.call(undefined); `, "TypeError") test(` abc = function() { return -1 ; } 1; abc.toString(); `, "function() { return -1 ;\n}") }) } func TestFunction_length(t *testing.T) { tt(t, func() { test, _ := test() test(`function a(x, y) {}; a.length`, 2) }) } func TestFunction_name(t *testing.T) { tt(t, func() { test, _ := test() test(`function a() {}; a.name`, "a") test(`function a() {}; var b = a.bind(); b.name`, "bound a") }) } func TestFunction_caller(t *testing.T) { tt(t, func() { test, vm := test() vm.Set("n", func(v Value) Value { r, err := v.Call(UndefinedValue()) if err != nil { panic(err) } return r }) test(` function a() { return a.caller; }; a() `, NullValue()) test(` function a() { return a.caller === b; }; function b() { return a(); } b() `, true) test(` function a() { return a.caller === b && b.caller === c; } function b() { return a(); } function c() { return b(); } c(); `, true) test(` function a() { return a.caller === b && b.caller === n && n.caller === c; } function b() { return a(); } function c() { return n(b); } c() `, true) test(` function e() { return e.caller === g && f.caller === g && g.caller === f; } function f(n) { return g(n - 1); } function g(n) { return n > 0 ? f(n) : e(); } f(2); `, true) }) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/functional_benchmark_test.go000066400000000000000000000560761331145703200301550ustar00rootroot00000000000000package otto import ( "fmt" "math/rand" "strings" "testing" ) func TestGoSliceQuickSort(t *testing.T) { testGoSliceSort("quickSort(testSlice, 0, testSlice.length-1);", jsQuickSort, t) } func TestGoSliceHeapSort(t *testing.T) { testGoSliceSort("heapSort(testSlice)", jsHeapSort, t) } func TestJsArrayQuicksort(t *testing.T) { testJsArraySort("quickSort(testSlice, 0, testSlice.length-1);", jsQuickSort, t) } func TestJsArrayHeapSort(t *testing.T) { testJsArraySort("heapSort(testSlice)", jsHeapSort, t) } func TestJsArrayMergeSort(t *testing.T) { testJsArraySort("testSlice = mergeSort(testSlice)", jsMergeSort, t) } func TestCryptoAes(t *testing.T) { tt(t, func() { _, vm := test() _, err := vm.Run(jsCryptoAES) is(err, nil) }) } func BenchmarkGoSliceQuickSort100000000(b *testing.B) { benchmarkGoSliceSort(100000000, "quickSort(testSlice, 0, testSlice.length-1);", jsQuickSort, b) } func BenchmarkGoSliceHeapSort100000000(b *testing.B) { benchmarkGoSliceSort(100000000, "heapSort(testSlice);", jsHeapSort, b) } func BenchmarkJsArrayQuickSort10000(b *testing.B) { benchmarkJsArraySort(10000, "quickSort(testSlice, 0, testSlice.length-1);", jsQuickSort, b) } func BenchmarkJsArrayMergeSort10000(b *testing.B) { benchmarkJsArraySort(10000, "mergeSort(testSlice);", jsMergeSort, b) } func BenchmarkJsArrayHeapSort10000(b *testing.B) { benchmarkJsArraySort(10000, "heapSort(testSlice);", jsHeapSort, b) } func BenchmarkCryptoAES(b *testing.B) { vm := New() // Make sure VM creation time is not counted in runtime test b.ResetTimer() for i := 0; i < b.N; i++ { vm.Run(jsCryptoAES) } } func testGoSliceSort(sortFuncCall string, sortCode string, t *testing.T) { tt(t, func() { test, vm := test() // inject quicksort code _, err := vm.Run(sortCode) is(err, nil) testSlice := []int{5, 3, 2, 4, 1} vm.Set("testSlice", testSlice) _, err = vm.Run(sortFuncCall) is(err, nil) is(test(`testSlice[0]`).export(), 1) is(test(`testSlice[1]`).export(), 2) is(test(`testSlice[2]`).export(), 3) is(test(`testSlice[3]`).export(), 4) is(test(`testSlice[4]`).export(), 5) is(testSlice[0], 1) is(testSlice[1], 2) is(testSlice[2], 3) is(testSlice[3], 4) is(testSlice[4], 5) }) } func testJsArraySort(sortFuncCall string, sortCode string, t *testing.T) { tt(t, func() { test, vm := test() // inject quicksort code vm.Run(sortCode) vm.Run("var testSlice = [5, 3, 2, 4, 1];") _, err := vm.Run(sortFuncCall) is(err, nil) is(test(`testSlice[0]`).export(), 1) is(test(`testSlice[1]`).export(), 2) is(test(`testSlice[2]`).export(), 3) is(test(`testSlice[3]`).export(), 4) is(test(`testSlice[4]`).export(), 5) }) } func benchmarkGoSliceSort(size int, sortFuncCall string, sortCode string, b *testing.B) { // generate arbitrary slice of 'size' testSlice := make([]int, size) for i := 0; i < size; i++ { testSlice[i] = rand.Int() } vm := New() // inject the sorting code vm.Run(sortCode) // Reset timer - everything until this point may have taken a long time b.ResetTimer() for i := 0; i < b.N; i++ { vm.Run(sortFuncCall) } } func benchmarkJsArraySort(size int, sortFuncCall string, sortCode string, b *testing.B) { // generate arbitrary slice of 'size' testSlice := make([]string, size) for i, _ := range testSlice { testSlice[i] = fmt.Sprintf("%d", rand.Int()) } jsArrayString := "[" + strings.Join(testSlice, ",") + "]" vm := New() // inject the test array vm.Run("testSlice = " + jsArrayString) // inject the sorting code vm.Run(sortCode) // Reset timer - everything until this point may have taken a long time b.ResetTimer() for i := 0; i < b.N; i++ { vm.Run(sortFuncCall) } } /**********************************************************************************************************************/ // Appendix - all the Javascript algorithm code constants const jsQuickSort = ` function quickSort(arr, left, right){ var len = arr.length, pivot, partitionIndex; if(left < right){ pivot = right; partitionIndex = partition(arr, pivot, left, right); // sort left and right quickSort(arr, left, partitionIndex - 1); quickSort(arr, partitionIndex + 1, right); } return arr; } function partition(arr, pivot, left, right){ var pivotValue = arr[pivot], partitionIndex = left; for(var i = left; i < right; i++){ if(arr[i] < pivotValue){ swap(arr, i, partitionIndex); partitionIndex++; } } swap(arr, right, partitionIndex); return partitionIndex; } function swap(arr, i, j){ var temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } ` const jsMergeSort = ` function mergeSort(arr){ var len = arr.length; if(len <2) return arr; var mid = Math.floor(len/2), left = arr.slice(0,mid), right =arr.slice(mid); // send left and right to the mergeSort to broke it down into pieces // then merge those return merge(mergeSort(left),mergeSort(right)); } function merge(left, right){ var result = [], lLen = left.length, rLen = right.length, l = 0, r = 0; while(l < lLen && r < rLen){ if(left[l] < right[r]){ result.push(left[l++]); } else{ result.push(right[r++]); } } // remaining part needs to be addred to the result return result.concat(left.slice(l)).concat(right.slice(r)); } ` const jsHeapSort = ` function heapSort(arr){ var len = arr.length, end = len-1; heapify(arr, len); while(end > 0){ swap(arr, end--, 0); siftDown(arr, 0, end); } return arr; } function heapify(arr, len){ // break the array into root + two sides, to create tree (heap) var mid = Math.floor((len-2)/2); while(mid >= 0){ siftDown(arr, mid--, len-1); } } function siftDown(arr, start, end){ var root = start, child = root*2 + 1, toSwap = root; while(child <= end){ if(arr[toSwap] < arr[child]){ swap(arr, toSwap, child); } if(child+1 <= end && arr[toSwap] < arr[child+1]){ swap(arr, toSwap, child+1) } if(toSwap != root){ swap(arr, root, toSwap); root = toSwap; } else{ return; } toSwap = root; child = root*2+1 } } function swap(arr, i, j){ var temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } ` // Copied from JetStream benchmarking suite // http://browserbench.org/JetStream/sources/crypto-aes.js const jsCryptoAES = ` /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* * AES Cipher function: encrypt 'input' with Rijndael algorithm * * takes byte-array 'input' (16 bytes) * 2D byte-array key schedule 'w' (Nr+1 x Nb bytes) * * applies Nr rounds (10/12/14) using key schedule w for 'add round key' stage * * returns byte-array encrypted value (16 bytes) */ function Cipher(input, w) { // main Cipher function [§5.1] var Nb = 4; // block size (in words): no of columns in state (fixed at 4 for AES) var Nr = w.length/Nb - 1; // no of rounds: 10/12/14 for 128/192/256-bit keys var state = [[],[],[],[]]; // initialise 4xNb byte-array 'state' with input [§3.4] for (var i=0; i<4*Nb; i++) state[i%4][Math.floor(i/4)] = input[i]; state = AddRoundKey(state, w, 0, Nb); for (var round=1; round 6 && i%Nk == 4) { temp = SubWord(temp); } for (var t=0; t<4; t++) w[i][t] = w[i-Nk][t] ^ temp[t]; } return w; } function SubWord(w) { // apply SBox to 4-byte word w for (var i=0; i<4; i++) w[i] = Sbox[w[i]]; return w; } function RotWord(w) { // rotate 4-byte word w left by one byte w[4] = w[0]; for (var i=0; i<4; i++) w[i] = w[i+1]; return w; } // Sbox is pre-computed multiplicative inverse in GF(2^8) used in SubBytes and KeyExpansion [§5.1.1] var Sbox = [0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76, 0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0, 0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15, 0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75, 0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84, 0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf, 0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8, 0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2, 0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73, 0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb, 0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79, 0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08, 0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a, 0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e, 0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf, 0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16]; // Rcon is Round Constant used for the Key Expansion [1st col is 2^(r-1) in GF(2^8)] [§5.2] var Rcon = [ [0x00, 0x00, 0x00, 0x00], [0x01, 0x00, 0x00, 0x00], [0x02, 0x00, 0x00, 0x00], [0x04, 0x00, 0x00, 0x00], [0x08, 0x00, 0x00, 0x00], [0x10, 0x00, 0x00, 0x00], [0x20, 0x00, 0x00, 0x00], [0x40, 0x00, 0x00, 0x00], [0x80, 0x00, 0x00, 0x00], [0x1b, 0x00, 0x00, 0x00], [0x36, 0x00, 0x00, 0x00] ]; /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* * Use AES to encrypt 'plaintext' with 'password' using 'nBits' key, in 'Counter' mode of operation * - see http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf * for each block * - outputblock = cipher(counter, key) * - cipherblock = plaintext xor outputblock */ function AESEncryptCtr(plaintext, password, nBits) { if (!(nBits==128 || nBits==192 || nBits==256)) return ''; // standard allows 128/192/256 bit keys // for this example script, generate the key by applying Cipher to 1st 16/24/32 chars of password; // for real-world applications, a more secure approach would be to hash the password e.g. with SHA-1 var nBytes = nBits/8; // no bytes in key var pwBytes = new Array(nBytes); for (var i=0; i>> i*8) & 0xff; for (var i=0; i<4; i++) counterBlock[i+4] = (nonce/0x100000000 >>> i*8) & 0xff; // generate key schedule - an expansion of the key into distinct Key Rounds for each round var keySchedule = KeyExpansion(key); var blockCount = Math.ceil(plaintext.length/blockSize); var ciphertext = new Array(blockCount); // ciphertext as array of strings for (var b=0; b>> c*8) & 0xff; for (var c=0; c<4; c++) counterBlock[15-c-4] = (b/0x100000000 >>> c*8) var cipherCntr = Cipher(counterBlock, keySchedule); // -- encrypt counter block -- // calculate length of final block: var blockLength = b>> c*8) & 0xff; for (var c=0; c<4; c++) counterBlock[15-c-4] = ((b/0x100000000-1) >>> c*8) & 0xff; var cipherCntr = Cipher(counterBlock, keySchedule); // encrypt counter block ciphertext[b] = unescCtrlChars(ciphertext[b]); var pt = ''; for (var i=0; i>18 & 0x3f; h2 = bits>>12 & 0x3f; h3 = bits>>6 & 0x3f; h4 = bits & 0x3f; // end of string? index to '=' in b64 if (isNaN(o3)) h4 = 64; if (isNaN(o2)) h3 = 64; // use hexets to index into b64, and append result to encoded string enc += b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4); } while (i < str.length); return enc; } function decodeBase64(str) { var o1, o2, o3, h1, h2, h3, h4, bits, i=0, enc=''; do { // unpack four hexets into three octets using index points in b64 h1 = b64.indexOf(str.charAt(i++)); h2 = b64.indexOf(str.charAt(i++)); h3 = b64.indexOf(str.charAt(i++)); h4 = b64.indexOf(str.charAt(i++)); bits = h1<<18 | h2<<12 | h3<<6 | h4; o1 = bits>>16 & 0xff; o2 = bits>>8 & 0xff; o3 = bits & 0xff; if (h3 == 64) enc += String.fromCharCode(o1); else if (h4 == 64) enc += String.fromCharCode(o1, o2); else enc += String.fromCharCode(o1, o2, o3); } while (i < str.length); return decodeUTF8(enc); // decode UTF-8 byte-array back to Unicode } function encodeUTF8(str) { // encode multi-byte string into utf-8 multiple single-byte characters str = str.replace( /[\u0080-\u07ff]/g, // U+0080 - U+07FF = 2-byte chars function(c) { var cc = c.charCodeAt(0); return String.fromCharCode(0xc0 | cc>>6, 0x80 | cc&0x3f); } ); str = str.replace( /[\u0800-\uffff]/g, // U+0800 - U+FFFF = 3-byte chars function(c) { var cc = c.charCodeAt(0); return String.fromCharCode(0xe0 | cc>>12, 0x80 | cc>>6&0x3F, 0x80 | cc&0x3f); } ); return str; } function decodeUTF8(str) { // decode utf-8 encoded string back into multi-byte characters str = str.replace( /[\u00c0-\u00df][\u0080-\u00bf]/g, // 2-byte chars function(c) { var cc = (c.charCodeAt(0)&0x1f)<<6 | c.charCodeAt(1)&0x3f; return String.fromCharCode(cc); } ); str = str.replace( /[\u00e0-\u00ef][\u0080-\u00bf][\u0080-\u00bf]/g, // 3-byte chars function(c) { var cc = (c.charCodeAt(0)&0x0f)<<12 | (c.charCodeAt(1)&0x3f<<6) | c.charCodeAt(2)&0x3f; return String.fromCharCode(cc); } ); return str; } function byteArrayToHexStr(b) { // convert byte array to hex string for displaying test vectors var s = ''; for (var i=0; iprint(<<_END_); package otto import ( "math" ) func _newContext(runtime *_runtime) { @{[ join "\n", $self->newContext() ]} } func newConsoleObject(runtime *_runtime) *_object { @{[ join "\n", $self->newConsoleObject() ]} } _END_ for (qw/int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64 float32 float64/) { $fmt->print(<<_END_); func toValue_$_(value $_) Value { return Value{ kind: valueNumber, value: value, } } _END_ } $fmt->print(<<_END_); func toValue_string(value string) Value { return Value{ kind: valueString, value: value, } } func toValue_string16(value []uint16) Value { return Value{ kind: valueString, value: value, } } func toValue_bool(value bool) Value { return Value{ kind: valueBoolean, value: value, } } func toValue_object(value *_object) Value { return Value{ kind: valueObject, value: value, } } _END_ close $fmt; sub newConsoleObject { my $self = shift; return $self->block(sub { my $class = "Console"; my @got = $self->functionDeclare( $class, "log", 0, "debug:log", 0, "info:log", 0, "error", 0, "warn:error", 0, "dir", 0, "time", 0, "timeEnd", 0, "trace", 0, "assert", 0, ); return "return @{[ $self->newObject(@got) ]}" }), ; } sub newContext { my $self = shift; return # ObjectPrototype $self->block(sub { my $class = "Object"; return ".${class}Prototype =", $self->globalPrototype( $class, "_classObject", undef, "prototypeValueObject", ), }), # FunctionPrototype $self->block(sub { my $class = "Function"; return ".${class}Prototype =", $self->globalPrototype( $class, "_classObject", ".ObjectPrototype", "prototypeValueFunction", ), }), # ObjectPrototype $self->block(sub { my $class = "Object"; my @got = $self->functionDeclare( $class, "valueOf", 0, "toString", 0, "toLocaleString", 0, "hasOwnProperty", 1, "isPrototypeOf", 1, "propertyIsEnumerable", 1, ); my @propertyMap = $self->propertyMap( @got, $self->property("constructor", undef), ); my $propertyOrder = $self->propertyOrder(@propertyMap); $propertyOrder =~ s/^propertyOrder: //; return ".${class}Prototype.property =", @propertyMap, ".${class}Prototype.propertyOrder =", $propertyOrder, }), # FunctionPrototype $self->block(sub { my $class = "Function"; my @got = $self->functionDeclare( $class, "toString", 0, "apply", 2, "call", 1, "bind", 1, ); my @propertyMap = $self->propertyMap( @got, $self->property("constructor", undef), $self->property("length", $self->numberValue(0), "0"), ); my $propertyOrder = $self->propertyOrder(@propertyMap); $propertyOrder =~ s/^propertyOrder: //; return ".${class}Prototype.property =", @propertyMap, ".${class}Prototype.propertyOrder =", $propertyOrder, }), # Object $self->block(sub { my $class = "Object"; return ".$class =", $self->globalFunction( $class, 1, $self->functionDeclare( $class, "getPrototypeOf", 1, "getOwnPropertyDescriptor", 2, "defineProperty", 3, "defineProperties", 2, "create", 2, "isExtensible", 1, "preventExtensions", 1, "isSealed", 1, "seal", 1, "isFrozen", 1, "freeze", 1, "keys", 1, "getOwnPropertyNames", 1, ), ), }), # Function $self->block(sub { my $class = "Function"; return "Function :=", $self->globalFunction( $class, 1, ), ".$class = Function", }), # Array $self->block(sub { my $class = "Array"; my @got = $self->functionDeclare( $class, "toString", 0, "toLocaleString", 0, "concat", 1, "join", 1, "splice", 2, "shift", 0, "pop", 0, "push", 1, "slice", 2, "unshift", 1, "reverse", 0, "sort", 1, "indexOf", 1, "lastIndexOf", 1, "every", 1, "some", 1, "forEach", 1, "map", 1, "filter", 1, "reduce", 1, "reduceRight", 1, ); return ".${class}Prototype =", $self->globalPrototype( $class, "_classArray", ".ObjectPrototype", undef, $self->property("length", $self->numberValue("uint32(0)"), "0100"), @got, ), ".$class =", $self->globalFunction( $class, 1, $self->functionDeclare( $class, "isArray", 1, ), ), }), # String $self->block(sub { my $class = "String"; my @got = $self->functionDeclare( $class, "toString", 0, "valueOf", 0, "charAt", 1, "charCodeAt", 1, "concat", 1, "indexOf", 1, "lastIndexOf", 1, "match", 1, "replace", 2, "search", 1, "split", 2, "slice", 2, "substring", 2, "toLowerCase", 0, "toUpperCase", 0, "substr", 2, "trim", 0, "trimLeft", 0, "trimRight", 0, "localeCompare", 1, "toLocaleLowerCase", 0, "toLocaleUpperCase", 0, ); return ".${class}Prototype =", $self->globalPrototype( $class, "_classString", ".ObjectPrototype", "prototypeValueString", $self->property("length", $self->numberValue("int(0)"), "0"), @got, ), ".$class =", $self->globalFunction( $class, 1, $self->functionDeclare( $class, "fromCharCode", 1, ), ), }), # Boolean $self->block(sub { my $class = "Boolean"; my @got = $self->functionDeclare( $class, "toString", 0, "valueOf", 0, ); return ".${class}Prototype =", $self->globalPrototype( $class, "_classObject", ".ObjectPrototype", "prototypeValueBoolean", @got, ), ".$class =", $self->globalFunction( $class, 1, $self->functionDeclare( $class, ), ), }), # Number $self->block(sub { my $class = "Number"; my @got = $self->functionDeclare( $class, "toString", 0, "valueOf", 0, "toFixed", 1, "toExponential", 1, "toPrecision", 1, "toLocaleString", 1, ); return ".${class}Prototype =", $self->globalPrototype( $class, "_classObject", ".ObjectPrototype", "prototypeValueNumber", @got, ), ".$class =", $self->globalFunction( $class, 1, $self->functionDeclare( $class, ), $self->numberConstantDeclare( "MAX_VALUE", "math.MaxFloat64", "MIN_VALUE", "math.SmallestNonzeroFloat64", "NaN", "math.NaN()", "NEGATIVE_INFINITY", "math.Inf(-1)", "POSITIVE_INFINITY", "math.Inf(+1)", ), ), }), # Math $self->block(sub { my $class = "Math"; return ".$class =", $self->globalObject( $class, $self->functionDeclare( $class, "abs", 1, "acos", 1, "asin", 1, "atan", 1, "atan2", 1, "ceil", 1, "cos", 1, "exp", 1, "floor", 1, "log", 1, "max", 2, "min", 2, "pow", 2, "random", 0, "round", 1, "sin", 1, "sqrt", 1, "tan", 1, ), $self->numberConstantDeclare( "E", "math.E", "LN10", "math.Ln10", "LN2", "math.Ln2", "LOG2E", "math.Log2E", "LOG10E", "math.Log10E", "PI", "math.Pi", "SQRT1_2", "sqrt1_2", "SQRT2", "math.Sqrt2", ) ), }), # Date $self->block(sub { my $class = "Date"; my @got = $self->functionDeclare( $class, "toString", 0, "toDateString", 0, "toTimeString", 0, "toUTCString", 0, "toISOString", 0, "toJSON", 1, "toGMTString", 0, "toLocaleString", 0, "toLocaleDateString", 0, "toLocaleTimeString", 0, "valueOf", 0, "getTime", 0, "getYear", 0, "getFullYear", 0, "getUTCFullYear", 0, "getMonth", 0, "getUTCMonth", 0, "getDate", 0, "getUTCDate", 0, "getDay", 0, "getUTCDay", 0, "getHours", 0, "getUTCHours", 0, "getMinutes", 0, "getUTCMinutes", 0, "getSeconds", 0, "getUTCSeconds", 0, "getMilliseconds", 0, "getUTCMilliseconds", 0, "getTimezoneOffset", 0, "setTime", 1, "setMilliseconds", 1, "setUTCMilliseconds", 1, "setSeconds", 2, "setUTCSeconds", 2, "setMinutes", 3, "setUTCMinutes", 3, "setHours", 4, "setUTCHours", 4, "setDate", 1, "setUTCDate", 1, "setMonth", 2, "setUTCMonth", 2, "setYear", 1, "setFullYear", 3, "setUTCFullYear", 3, ); return ".${class}Prototype =", $self->globalPrototype( $class, "_classObject", ".ObjectPrototype", "prototypeValueDate", @got, ), ".$class =", $self->globalFunction( $class, 7, $self->functionDeclare( $class, "parse", 1, "UTC", 7, "now", 0, ), ), }), # RegExp $self->block(sub { my $class = "RegExp"; my @got = $self->functionDeclare( $class, "toString", 0, "exec", 1, "test", 1, "compile", 1, ); return ".${class}Prototype =", $self->globalPrototype( $class, "_classObject", ".ObjectPrototype", "prototypeValueRegExp", @got, ), ".$class =", $self->globalFunction( $class, 2, $self->functionDeclare( $class, ), ), }), # Error $self->block(sub { my $class = "Error"; my @got = $self->functionDeclare( $class, "toString", 0, ); return ".${class}Prototype =", $self->globalPrototype( $class, "_classObject", ".ObjectPrototype", undef, @got, $self->property("name", $self->stringValue("Error")), $self->property("message", $self->stringValue("")), ), ".$class =", $self->globalFunction( $class, 1, $self->functionDeclare( $class, ), ), }), (map { my $class = "${_}Error"; $self->block(sub { my @got = $self->functionDeclare( $class, ); return ".${class}Prototype =", $self->globalPrototype( $class, "_classObject", ".ErrorPrototype", undef, @got, $self->property("name", $self->stringValue($class)), ), ".$class =", $self->globalFunction( $class, 1, $self->functionDeclare( $class, ), ), }); } qw/Eval Type Range Reference Syntax URI/), # JSON $self->block(sub { my $class = "JSON"; return ".$class =", $self->globalObject( $class, $self->functionDeclare( $class, "parse", 2, "stringify", 3, ), ), }), # Global $self->block(sub { my $class = "Global"; my @got = $self->functionDeclare( $class, "eval", 1, "parseInt", 2, "parseFloat", 1, "isNaN", 1, "isFinite", 1, "decodeURI", 1, "decodeURIComponent", 1, "encodeURI", 1, "encodeURIComponent", 1, "escape", 1, "unescape", 1, ); my @propertyMap = $self->propertyMap( @got, $self->globalDeclare( "Object", "Function", "Array", "String", "Boolean", "Number", "Math", "Date", "RegExp", "Error", "EvalError", "TypeError", "RangeError", "ReferenceError", "SyntaxError", "URIError", "JSON", ), $self->property("undefined", $self->undefinedValue(), "0"), $self->property("NaN", $self->numberValue("math.NaN()"), "0"), $self->property("Infinity", $self->numberValue("math.Inf(+1)"), "0"), ); my $propertyOrder = $self->propertyOrder(@propertyMap); $propertyOrder =~ s/^propertyOrder: //; return "runtime.globalObject.property =", @propertyMap, "runtime.globalObject.propertyOrder =", $propertyOrder, ; }), ; } sub propertyMap { my $self = shift; return "map[string]_property{", (join ",\n", @_, ""), "}", } our (@preblock, @postblock); sub block { my $self = shift; local @preblock = (); local @postblock = (); my @input = $_[0]->(); my @output; while (@input) { local $_ = shift @input; if (m/^\./) { $_ = "runtime.global$_"; } if (m/ :?=$/) { $_ .= shift @input; } push @output, $_; } return "{", @preblock, @output, @postblock, "}", ; } sub numberConstantDeclare { my $self = shift; my @got; while (@_) { my $name = shift; my $value = shift; push @got, $self->property($name, $self->numberValue($value), "0"), } return @got; } sub functionDeclare { my $self = shift; my $class = shift; my $builtin = "builtin${class}"; my @got; while (@_) { my $name = shift; my $length = shift; $name = $self->newFunction($name, "${builtin}_", $length); push @got, $self->functionProperty($name), } return @got; } sub globalDeclare { my $self = shift; my @got; while (@_) { my $name = shift; push @got, $self->property($name, $self->objectValue("runtime.global.$name"), "0101"), } return @got; } sub propertyOrder { my $self = shift; my $propertyMap = join "", @_; my (@keys) = $propertyMap =~ m/("\w+"):/g; my $propertyOrder = join "\n", "propertyOrder: []string{", (join ",\n", @keys, ""), "}"; return $propertyOrder; } sub globalObject { my $self = shift; my $name = shift; my $propertyMap = ""; if (@_) { $propertyMap = join "\n", $self->propertyMap(@_); my $propertyOrder = $self->propertyOrder($propertyMap); $propertyMap = "property: $propertyMap,\n$propertyOrder,"; } return trim <<_END_; &_object{ runtime: runtime, class: "$name", objectClass: _classObject, prototype: runtime.global.ObjectPrototype, extensible: true, $propertyMap } _END_ } sub globalFunction { my $self = shift; my $name = shift; my $length = shift; my $builtin = "builtin${name}"; my $builtinNew = "builtinNew${name}"; my $prototype = "runtime.global.${name}Prototype"; my $propertyMap = ""; unshift @_, $self->property("length", $self->numberValue($length), "0"), $self->property("prototype", $self->objectValue($prototype), "0"), ; if (@_) { $propertyMap = join "\n", $self->propertyMap(@_); my $propertyOrder = $self->propertyOrder($propertyMap); $propertyMap = "property: $propertyMap,\n$propertyOrder,"; } push @postblock, $self->statement( "$prototype.property[\"constructor\"] =", $self->property(undef, $self->objectValue("runtime.global.${name}"), "0101"), ); return trim <<_END_; &_object{ runtime: runtime, class: "Function", objectClass: _classObject, prototype: runtime.global.FunctionPrototype, extensible: true, value: @{[ $self->nativeFunctionOf($name, $builtin, $builtinNew) ]}, $propertyMap } _END_ } sub nativeCallFunction { my $self = shift; my $name = shift; my $func = shift; return trim <<_END_; _nativeCallFunction{ "$name", $func } _END_ } sub globalPrototype { my $self = shift; my $class = shift; my $classObject = shift; my $prototype = shift; my $value = shift; if (!defined $prototype) { $prototype = "nil"; } if (!defined $value) { $value = "nil"; } if ($prototype =~ m/^\./) { $prototype = "runtime.global$prototype"; } my $propertyMap = ""; if (@_) { $propertyMap = join "\n", $self->propertyMap(@_); my $propertyOrder = $self->propertyOrder($propertyMap); $propertyMap = "property: $propertyMap,\n$propertyOrder,"; } return trim <<_END_; &_object{ runtime: runtime, class: "$class", objectClass: $classObject, prototype: $prototype, extensible: true, value: $value, $propertyMap } _END_ } sub newFunction { my $self = shift; my $name = shift; my $func = shift; my $length = shift; my @name = ($name, $name); if ($name =~ m/^(\w+):(\w+)$/) { @name = ($1, $2); $name = $name[0]; } if ($func =~ m/^builtin\w+_$/) { $func = "$func$name[1]"; } my $propertyOrder = ""; my @propertyMap = ( $self->property("length", $self->numberValue($length), "0"), ); if (@propertyMap) { $propertyOrder = $self->propertyOrder(@propertyMap); $propertyOrder = "$propertyOrder,"; } my $label = functionLabel($name); push @preblock, $self->statement( "$label := @{[ trim <<_END_ ]}", &_object{ runtime: runtime, class: "Function", objectClass: _classObject, prototype: runtime.global.FunctionPrototype, extensible: true, property: @{[ join "\n", $self->propertyMap(@propertyMap) ]}, $propertyOrder value: @{[ $self->nativeFunctionOf($name, $func) ]}, } _END_ ); return $name; } sub newObject { my $self = shift; my $propertyMap = join "\n", $self->propertyMap(@_); my $propertyOrder = $self->propertyOrder($propertyMap); return trim <<_END_; &_object{ runtime: runtime, class: "Object", objectClass: _classObject, prototype: runtime.global.ObjectPrototype, extensible: true, property: $propertyMap, $propertyOrder, } _END_ } sub newPrototypeObject { my $self = shift; my $class = shift; my $objectClass = shift; my $value = shift; if (defined $value) { $value = "value: $value,"; } my $propertyMap = join "\n", $self->propertyMap(@_); my $propertyOrder = $self->propertyOrder($propertyMap); return trim <<_END_; &_object{ runtime: runtime, class: "$class", objectClass: $objectClass, prototype: runtime.global.ObjectPrototype, extensible: true, property: $propertyMap, $propertyOrder, $value } _END_ } sub functionProperty { my $self = shift; my $name = shift; return $self->property( $name, $self->objectValue(functionLabel($name)) ); } sub statement { my $self = shift; return join "\n", @_; } sub functionOf { my $self = shift; my $call = shift; my $construct = shift; if ($construct) { $construct = "construct: $construct,"; } else { $construct = ""; } return trim <<_END_ _functionObject{ call: $call, $construct } _END_ } sub nativeFunctionOf { my $self = shift; my $name = shift; my $call = shift; my $construct = shift; if ($construct) { $construct = "construct: $construct,"; } else { $construct = ""; } return trim <<_END_ _nativeFunctionObject{ name: "$name", call: $call, $construct } _END_ } sub nameProperty { my $self = shift; my $name = shift; my $value = shift; return trim <<_END_; "$name": _property{ mode: 0101, value: $value, } _END_ } sub numberValue { my $self = shift; my $value = shift; return trim <<_END_; Value{ kind: valueNumber, value: $value, } _END_ } sub property { my $self = shift; my $name = shift; my $value = shift; my $mode = shift; $mode = "0101" unless defined $mode; if (! defined $value) { $value = "Value{}"; } if (defined $name) { return trim <<_END_; "$name": _property{ mode: $mode, value: $value, } _END_ } else { return trim <<_END_; _property{ mode: $mode, value: $value, } _END_ } } sub objectProperty { my $self = shift; my $name = shift; my $value = shift; return trim <<_END_; "$name": _property{ mode: 0101, value: @{[ $self->objectValue($value)]}, } _END_ } sub objectValue { my $self = shift; my $value = shift; return trim <<_END_ Value{ kind: valueObject, value: $value, } _END_ } sub stringValue { my $self = shift; my $value = shift; return trim <<_END_ Value{ kind: valueString, value: "$value", } _END_ } sub booleanValue { my $self = shift; my $value = shift; return trim <<_END_ Value{ kind: valueBoolean, value: $value, } _END_ } sub undefinedValue { my $self = shift; return trim <<_END_ Value{ kind: valueUndefined, } _END_ } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/json_test.go000066400000000000000000000104541331145703200247400ustar00rootroot00000000000000package otto import ( "testing" ) func BenchmarkJSON_parse(b *testing.B) { vm := New() for i := 0; i < b.N; i++ { vm.Run(`JSON.parse("1")`) vm.Run(`JSON.parse("[1,2,3]")`) vm.Run(`JSON.parse('{"a":{"x":100,"y":110},"b":[10,20,30],"c":"zazazaza"}')`) vm.Run(`JSON.parse("[1,2,3]", function(k, v) { return undefined })`) } } func TestJSON_parse(t *testing.T) { tt(t, func() { test, _ := test() test(` JSON.parse("1"); `, 1) test(` JSON.parse("null"); `, "null") // TODO Can we make this nil? test(` var abc = JSON.parse('"a\uFFFFbc"'); [ abc[0], abc[2], abc[3], abc.length ]; `, "a,b,c,4") test(` JSON.parse("[1, 2, 3]"); `, "1,2,3") test(` JSON.parse('{ "abc": 1, "def":2 }').abc; `, 1) test(` JSON.parse('{ "abc": { "x": 100, "y": 110 }, "def": [ 10, 20 ,30 ], "ghi": "zazazaza" }').def; `, "10,20,30") test(`raise: JSON.parse("12\t\r\n 34"); `, "SyntaxError: invalid character '3' after top-level value") test(` JSON.parse("[1, 2, 3]", function() { return undefined }); `, "undefined") test(`raise: JSON.parse(""); `, "SyntaxError: unexpected end of JSON input") test(`raise: JSON.parse("[1, 2, 3"); `, "SyntaxError: unexpected end of JSON input") test(`raise: JSON.parse("[1, 2, ; abc=10"); `, "SyntaxError: invalid character ';' looking for beginning of value") test(`raise: JSON.parse("[1, 2, function(){}]"); `, "SyntaxError: invalid character 'u' in literal false (expecting 'a')") }) } func TestJSON_stringify(t *testing.T) { tt(t, func() { test, _ := test() defer mockUTC()() test(` JSON.stringify(function(){}); `, "undefined") test(` JSON.stringify(new Boolean(false)); `, "false") test(` JSON.stringify({a1: {b1: [1,2,3,4], b2: {c1: 1, c2: 2}}, a2: 'a2'}, null, -5); `, `{"a1":{"b1":[1,2,3,4],"b2":{"c1":1,"c2":2}},"a2":"a2"}`) test(` JSON.stringify(undefined); `, "undefined") test(` JSON.stringify(1); `, "1") test(` JSON.stringify("abc def"); `, "\"abc def\"") test(` JSON.stringify(3.14159); `, "3.14159") test(` JSON.stringify([]); `, "[]") test(` JSON.stringify([1, 2, 3]); `, "[1,2,3]") test(` JSON.stringify([true, false, null]); `, "[true,false,null]") test(` JSON.stringify({ abc: { x: 100, y: 110 }, def: [ 10, 20, 30 ], ghi: "zazazaza" }); `, `{"abc":{"x":100,"y":110},"def":[10,20,30],"ghi":"zazazaza"}`) test(` JSON.stringify([ 'e', {pluribus: 'unum'} ], null, '\t'); `, "[\n\t\"e\",\n\t{\n\t\t\"pluribus\": \"unum\"\n\t}\n]") test(` JSON.stringify(new Date(0)); `, `"1970-01-01T00:00:00.000Z"`) test(` JSON.stringify([ new Date(0) ], function(key, value){ return this[key] instanceof Date ? 'Date(' + this[key] + ')' : value }); `, `["Date(Thu, 01 Jan 1970 00:00:00 UTC)"]`) test(` JSON.stringify({ abc: 1, def: 2, ghi: 3 }, ['abc','def']); `, `{"abc":1,"def":2}`) test(`raise: var abc = { def: null }; abc.def = abc; JSON.stringify(abc) `, "TypeError: Converting circular structure to JSON") test(`raise: var abc= [ null ]; abc[0] = abc; JSON.stringify(abc); `, "TypeError: Converting circular structure to JSON") test(`raise: var abc = { def: {} }; abc.def.ghi = abc; JSON.stringify(abc) `, "TypeError: Converting circular structure to JSON") test(` var ghi = { "pi": 3.14159 }; var abc = { def: {} }; abc.ghi = ghi; abc.def.ghi = ghi; JSON.stringify(abc); `, `{"def":{"ghi":{"pi":3.14159}},"ghi":{"pi":3.14159}}`) }) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/math_test.go000066400000000000000000000160051331145703200247160ustar00rootroot00000000000000package otto import ( "math" "testing" ) var _NaN = math.NaN() var _Infinity = math.Inf(1) func TestMath_toString(t *testing.T) { tt(t, func() { test, _ := test() test(`Math.toString()`, "[object Math]") }) } func TestMath_abs(t *testing.T) { tt(t, func() { test, _ := test() test(`Math.abs(NaN)`, _NaN) test(`Math.abs(2)`, 2) test(`Math.abs(-2)`, 2) test(`Math.abs(-Infinity)`, _Infinity) test(`Math.acos(0.5)`, 1.0471975511965976) test(`Math.abs('-1')`, 1) test(`Math.abs(-2)`, 2) test(`Math.abs(null)`, 0) test(`Math.abs("string")`, _NaN) test(`Math.abs()`, _NaN) }) } func TestMath_acos(t *testing.T) { tt(t, func() { test, _ := test() test(`Math.acos(NaN)`, _NaN) test(`Math.acos(2)`, _NaN) test(`Math.acos(-2)`, _NaN) test(`1/Math.acos(1)`, _Infinity) test(`Math.acos(0.5)`, 1.0471975511965976) }) } func TestMath_asin(t *testing.T) { tt(t, func() { test, _ := test() test(`Math.asin(NaN)`, _NaN) test(`Math.asin(2)`, _NaN) test(`Math.asin(-2)`, _NaN) test(`1/Math.asin(0)`, _Infinity) test(`1/Math.asin(-0)`, -_Infinity) test(`Math.asin(0.5)`, 0.5235987755982989) }) } func TestMath_atan(t *testing.T) { tt(t, func() { test, _ := test() test(`Math.atan(NaN)`, _NaN) test(`1/Math.atan(0)`, _Infinity) test(`1/Math.atan(-0)`, -_Infinity) test(`Math.atan(Infinity)`, 1.5707963267948966) test(`Math.atan(-Infinity)`, -1.5707963267948966) // freebsd/386 1.03 => 0.4636476090008061 // darwin 1.03 => 0.46364760900080604 test(`Math.atan(0.5).toPrecision(10)`, "0.463647609") }) } func TestMath_atan2(t *testing.T) { tt(t, func() { test, _ := test() test(`Math.atan2()`, _NaN) test(`Math.atan2(NaN)`, _NaN) test(`Math.atan2(0, NaN)`, _NaN) test(`Math.atan2(1, 0)`, 1.5707963267948966) test(`Math.atan2(1, -0)`, 1.5707963267948966) test(`1/Math.atan2(0, 1)`, _Infinity) test(`1/Math.atan2(0, 0)`, _Infinity) test(`Math.atan2(0, -0)`, 3.141592653589793) test(`Math.atan2(0, -1)`, 3.141592653589793) test(`1/Math.atan2(-0, 1)`, -_Infinity) test(`1/Math.atan2(-0, 0)`, -_Infinity) test(`Math.atan2(-0, -0)`, -3.141592653589793) test(`Math.atan2(-0, -1)`, -3.141592653589793) test(`Math.atan2(-1, 0)`, -1.5707963267948966) test(`Math.atan2(-1, -0)`, -1.5707963267948966) test(`1/Math.atan2(1, Infinity)`, _Infinity) test(`Math.atan2(1, -Infinity)`, 3.141592653589793) test(`1/Math.atan2(-1, Infinity)`, -_Infinity) test(`Math.atan2(-1, -Infinity)`, -3.141592653589793) test(`Math.atan2(Infinity, 1)`, 1.5707963267948966) test(`Math.atan2(-Infinity, 1)`, -1.5707963267948966) test(`Math.atan2(Infinity, Infinity)`, 0.7853981633974483) test(`Math.atan2(Infinity, -Infinity)`, 2.356194490192345) test(`Math.atan2(-Infinity, Infinity)`, -0.7853981633974483) test(`Math.atan2(-Infinity, -Infinity)`, -2.356194490192345) }) } func TestMath_ceil(t *testing.T) { tt(t, func() { test, _ := test() test(`Math.ceil(NaN)`, _NaN) test(`Math.ceil(+0)`, 0) test(`1/Math.ceil(-0)`, -_Infinity) test(`Math.ceil(Infinity)`, _Infinity) test(`Math.ceil(-Infinity)`, -_Infinity) test(`1/Math.ceil(-0.5)`, -_Infinity) test(`Math.ceil(-11)`, -11) test(`Math.ceil(-0.5)`, 0) test(`Math.ceil(1.5)`, 2) }) } func TestMath_cos(t *testing.T) { tt(t, func() { test, _ := test() test(`Math.cos(NaN)`, _NaN) test(`Math.cos(+0)`, 1) test(`Math.cos(-0)`, 1) test(`Math.cos(Infinity)`, _NaN) test(`Math.cos(-Infinity)`, _NaN) test(`Math.cos(0.5)`, 0.8775825618903728) }) } func TestMath_exp(t *testing.T) { tt(t, func() { test, _ := test() test(`Math.exp(NaN)`, _NaN) test(`Math.exp(+0)`, 1) test(`Math.exp(-0)`, 1) test(`Math.exp(Infinity)`, _Infinity) test(`Math.exp(-Infinity)`, 0) }) } func TestMath_floor(t *testing.T) { tt(t, func() { test, _ := test() test(`Math.floor(NaN)`, _NaN) test(`Math.floor(+0)`, 0) test(`1/Math.floor(-0)`, -_Infinity) test(`Math.floor(Infinity)`, _Infinity) test(`Math.floor(-Infinity)`, -_Infinity) test(`Math.floor(-11)`, -11) test(`Math.floor(-0.5)`, -1) test(`Math.floor(1.5)`, 1) }) } func TestMath_log(t *testing.T) { tt(t, func() { test, _ := test() test(`Math.log(NaN)`, _NaN) test(`Math.log(-1)`, _NaN) test(`Math.log(+0)`, -_Infinity) test(`Math.log(-0)`, -_Infinity) test(`1/Math.log(1)`, _Infinity) test(`Math.log(Infinity)`, _Infinity) test(`Math.log(0.5)`, -0.6931471805599453) }) } func TestMath_max(t *testing.T) { tt(t, func() { test, _ := test() test(`Math.max(-11, -1, 0, 1, 2, 3, 11)`, 11) }) } func TestMath_min(t *testing.T) { tt(t, func() { test, _ := test() test(`Math.min(-11, -1, 0, 1, 2, 3, 11)`, -11) }) } func TestMath_pow(t *testing.T) { tt(t, func() { test, _ := test() test(`Math.pow(0, NaN)`, _NaN) test(`Math.pow(0, 0)`, 1) test(`Math.pow(NaN, 0)`, 1) test(`Math.pow(0, -0)`, 1) test(`Math.pow(NaN, -0)`, 1) test(`Math.pow(NaN, 1)`, _NaN) test(`Math.pow(2, Infinity)`, _Infinity) test(`1/Math.pow(2, -Infinity)`, _Infinity) test(`Math.pow(1, Infinity)`, _NaN) test(`Math.pow(1, -Infinity)`, _NaN) test(`1/Math.pow(0.1, Infinity)`, _Infinity) test(`Math.pow(0.1, -Infinity)`, _Infinity) test(`Math.pow(Infinity, 1)`, _Infinity) test(`1/Math.pow(Infinity, -1)`, _Infinity) test(`Math.pow(-Infinity, 1)`, -_Infinity) test(`Math.pow(-Infinity, 2)`, _Infinity) test(`1/Math.pow(-Infinity, -1)`, -_Infinity) test(`1/Math.pow(-Infinity, -2)`, _Infinity) test(`1/Math.pow(0, 1)`, _Infinity) test(`Math.pow(0, -1)`, _Infinity) test(`1/Math.pow(-0, 1)`, -_Infinity) test(`1/Math.pow(-0, 2)`, _Infinity) test(`Math.pow(-0, -1)`, -_Infinity) test(`Math.pow(-0, -2)`, _Infinity) test(`Math.pow(-1, 0.1)`, _NaN) test(` [ Math.pow(-1, +Infinity), Math.pow(1, Infinity) ]; `, "NaN,NaN") }) } func TestMath_round(t *testing.T) { tt(t, func() { test, _ := test() test(`Math.round(NaN)`, _NaN) test(`1/Math.round(0)`, _Infinity) test(`1/Math.round(-0)`, -_Infinity) test(`Math.round(Infinity)`, _Infinity) test(`Math.round(-Infinity)`, -_Infinity) test(`1/Math.round(0.1)`, _Infinity) test(`1/Math.round(-0.1)`, -_Infinity) test(`Math.round(3.5)`, 4) test(`Math.round(-3.5)`, -3) }) } func TestMath_sin(t *testing.T) { tt(t, func() { test, _ := test() test(`Math.sin(NaN)`, _NaN) test(`1/Math.sin(+0)`, _Infinity) test(`1/Math.sin(-0)`, -_Infinity) test(`Math.sin(Infinity)`, _NaN) test(`Math.sin(-Infinity)`, _NaN) test(`Math.sin(0.5)`, 0.479425538604203) }) } func TestMath_sqrt(t *testing.T) { tt(t, func() { test, _ := test() test(`Math.sqrt(NaN)`, _NaN) test(`Math.sqrt(-1)`, _NaN) test(`1/Math.sqrt(+0)`, _Infinity) test(`1/Math.sqrt(-0)`, -_Infinity) test(`Math.sqrt(Infinity)`, _Infinity) test(`Math.sqrt(2)`, 1.4142135623730951) }) } func TestMath_tan(t *testing.T) { tt(t, func() { test, _ := test() test(`Math.tan(NaN)`, _NaN) test(`1/Math.tan(+0)`, _Infinity) test(`1/Math.tan(-0)`, -_Infinity) test(`Math.tan(Infinity)`, _NaN) test(`Math.tan(-Infinity)`, _NaN) test(`Math.tan(0.5)`, 0.5463024898437905) }) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/native_stack_test.go000066400000000000000000000052551331145703200264450ustar00rootroot00000000000000package otto import ( "testing" ) func TestNativeStackFrames(t *testing.T) { tt(t, func() { vm := New() s, err := vm.Compile("input.js", ` function A() { ext1(); } function B() { ext2(); } A(); `) if err != nil { panic(err) } vm.Set("ext1", func(c FunctionCall) Value { if _, err := c.Otto.Eval("B()"); err != nil { panic(err) } return UndefinedValue() }) vm.Set("ext2", func(c FunctionCall) Value { { // no limit, include innermost native frames ctx := c.Otto.ContextSkip(-1, false) is(ctx.Stacktrace, []string{ "github.com/robertkrimen/otto.TestNativeStackFrames.func1.2 (native_stack_test.go:28)", "B (input.js:3:22)", "github.com/robertkrimen/otto.TestNativeStackFrames.func1.1 (native_stack_test.go:20)", "A (input.js:2:22)", "input.js:4:7", }) is(ctx.Callee, "github.com/robertkrimen/otto.TestNativeStackFrames.func1.2") is(ctx.Filename, "native_stack_test.go") is(ctx.Line, 28) is(ctx.Column, 0) } { // no limit, skip innermost native frames ctx := c.Otto.ContextSkip(-1, true) is(ctx.Stacktrace, []string{ "B (input.js:3:22)", "github.com/robertkrimen/otto.TestNativeStackFrames.func1.1 (native_stack_test.go:20)", "A (input.js:2:22)", "input.js:4:7", }) is(ctx.Callee, "B") is(ctx.Filename, "input.js") is(ctx.Line, 3) is(ctx.Column, 22) } if _, err := c.Otto.Eval("ext3()"); err != nil { panic(err) } return UndefinedValue() }) vm.Set("ext3", func(c FunctionCall) Value { { // no limit, include innermost native frames ctx := c.Otto.ContextSkip(-1, false) is(ctx.Stacktrace, []string{ "github.com/robertkrimen/otto.TestNativeStackFrames.func1.3 (native_stack_test.go:69)", "github.com/robertkrimen/otto.TestNativeStackFrames.func1.2 (native_stack_test.go:28)", "B (input.js:3:22)", "github.com/robertkrimen/otto.TestNativeStackFrames.func1.1 (native_stack_test.go:20)", "A (input.js:2:22)", "input.js:4:7", }) is(ctx.Callee, "github.com/robertkrimen/otto.TestNativeStackFrames.func1.3") is(ctx.Filename, "native_stack_test.go") is(ctx.Line, 69) is(ctx.Column, 0) } { // no limit, skip innermost native frames ctx := c.Otto.ContextSkip(-1, true) is(ctx.Stacktrace, []string{ "B (input.js:3:22)", "github.com/robertkrimen/otto.TestNativeStackFrames.func1.1 (native_stack_test.go:20)", "A (input.js:2:22)", "input.js:4:7", }) is(ctx.Callee, "B") is(ctx.Filename, "input.js") is(ctx.Line, 3) is(ctx.Column, 22) } return UndefinedValue() }) if _, err := vm.Run(s); err != nil { panic(err) } }) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/number_test.go000066400000000000000000000103551331145703200252570ustar00rootroot00000000000000package otto import ( "testing" ) func TestNumber(t *testing.T) { tt(t, func() { test, _ := test() test(` var abc = Object.getOwnPropertyDescriptor(Number, "prototype"); [ [ typeof Number.prototype ], [ abc.writable, abc.enumerable, abc.configurable ] ]; `, "object,false,false,false") }) } func TestNumber_toString(t *testing.T) { tt(t, func() { test, _ := test() test(` new Number(451).toString(); `, "451") test(` new Number(451).toString(10); `, "451") test(` new Number(451).toString(8); `, "703") test(`raise: new Number(451).toString(1); `, "RangeError: toString() radix must be between 2 and 36") test(`raise: new Number(451).toString(Infinity); `, "RangeError: toString() radix must be between 2 and 36") test(` new Number(NaN).toString() `, "NaN") test(` new Number(Infinity).toString() `, "Infinity") test(` new Number(Infinity).toString(16) `, "Infinity") test(` [ Number.prototype.toString(undefined), new Number().toString(undefined), new Number(0).toString(undefined), new Number(-1).toString(undefined), new Number(1).toString(undefined), new Number(Number.NaN).toString(undefined), new Number(Number.POSITIVE_INFINITY).toString(undefined), new Number(Number.NEGATIVE_INFINITY).toString(undefined) ] `, "0,0,0,-1,1,NaN,Infinity,-Infinity") }) } func TestNumber_toFixed(t *testing.T) { tt(t, func() { test, _ := test() test(`new Number(451).toFixed(2)`, "451.00") test(`12345.6789.toFixed()`, "12346") test(`12345.6789.toFixed(1)`, "12345.7") test(`12345.6789.toFixed(6)`, "12345.678900") test(`(1.23e-20).toFixed(2)`, "0.00") test(`2.34.toFixed(1)`, "2.3") // FIXME Wtf? "2.3" test(`-2.34.toFixed(1)`, -2.3) // FIXME Wtf? -2.3 test(`(-2.34).toFixed(1)`, "-2.3") test(`raise: new Number("a").toFixed(Number.POSITIVE_INFINITY); `, "RangeError: toFixed() precision must be between 0 and 20") test(` [ new Number(1e21).toFixed(), new Number(1e21).toFixed(0), new Number(1e21).toFixed(1), new Number(1e21).toFixed(1.1), new Number(1e21).toFixed(0.9), new Number(1e21).toFixed("1"), new Number(1e21).toFixed("1.1"), new Number(1e21).toFixed("0.9"), new Number(1e21).toFixed(Number.NaN), new Number(1e21).toFixed("some string") ]; `, "1e+21,1e+21,1e+21,1e+21,1e+21,1e+21,1e+21,1e+21,1e+21,1e+21") test(`raise: new Number(1e21).toFixed(Number.POSITIVE_INFINITY); `, "RangeError: toFixed() precision must be between 0 and 20") }) } func TestNumber_toExponential(t *testing.T) { tt(t, func() { test, _ := test() test(`new Number(451).toExponential(2)`, "4.51e+02") test(`77.1234.toExponential()`, "7.71234e+01") test(`77.1234.toExponential(4)`, "7.7123e+01") test(`77.1234.toExponential(2)`, "7.71e+01") test(`77 .toExponential()`, "7.7e+01") }) } func TestNumber_toPrecision(t *testing.T) { tt(t, func() { test, _ := test() test(`new Number(451).toPrecision()`, "451") test(`new Number(451).toPrecision(1)`, "5e+02") test(`5.123456.toPrecision()`, "5.123456") test(`5.123456.toPrecision(5)`, "5.1235") test(`5.123456.toPrecision(2)`, "5.1") test(`5.123456.toPrecision(1)`, "5") }) } func TestNumber_toLocaleString(t *testing.T) { tt(t, func() { test, _ := test() test(` [ new Number(451).toLocaleString(), new Number(451).toLocaleString(10), new Number(451).toLocaleString(8) ]; `, "451,451,703") }) } func TestValue_number(t *testing.T) { tt(t, func() { nm := toValue(0.0).number() is(nm.kind, numberInteger) nm = toValue(3.14159).number() is(nm.kind, numberFloat) }) } func Test_NaN(t *testing.T) { tt(t, func() { test, _ := test() test(` [ NaN === NaN, NaN == NaN ]; `, "false,false") }) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/object.go000066400000000000000000000072201331145703200241730ustar00rootroot00000000000000package otto type _object struct { runtime *_runtime class string objectClass *_objectClass value interface{} prototype *_object extensible bool property map[string]_property propertyOrder []string } func newObject(runtime *_runtime, class string) *_object { self := &_object{ runtime: runtime, class: class, objectClass: _classObject, property: make(map[string]_property), extensible: true, } return self } // 8.12 // 8.12.1 func (self *_object) getOwnProperty(name string) *_property { return self.objectClass.getOwnProperty(self, name) } // 8.12.2 func (self *_object) getProperty(name string) *_property { return self.objectClass.getProperty(self, name) } // 8.12.3 func (self *_object) get(name string) Value { return self.objectClass.get(self, name) } // 8.12.4 func (self *_object) canPut(name string) bool { return self.objectClass.canPut(self, name) } // 8.12.5 func (self *_object) put(name string, value Value, throw bool) { self.objectClass.put(self, name, value, throw) } // 8.12.6 func (self *_object) hasProperty(name string) bool { return self.objectClass.hasProperty(self, name) } func (self *_object) hasOwnProperty(name string) bool { return self.objectClass.hasOwnProperty(self, name) } type _defaultValueHint int const ( defaultValueNoHint _defaultValueHint = iota defaultValueHintString defaultValueHintNumber ) // 8.12.8 func (self *_object) DefaultValue(hint _defaultValueHint) Value { if hint == defaultValueNoHint { if self.class == "Date" { // Date exception hint = defaultValueHintString } else { hint = defaultValueHintNumber } } methodSequence := []string{"valueOf", "toString"} if hint == defaultValueHintString { methodSequence = []string{"toString", "valueOf"} } for _, methodName := range methodSequence { method := self.get(methodName) // FIXME This is redundant... if method.isCallable() { result := method._object().call(toValue_object(self), nil, false, nativeFrame) if result.IsPrimitive() { return result } } } panic(self.runtime.panicTypeError()) } func (self *_object) String() string { return self.DefaultValue(defaultValueHintString).string() } func (self *_object) defineProperty(name string, value Value, mode _propertyMode, throw bool) bool { return self.defineOwnProperty(name, _property{value, mode}, throw) } // 8.12.9 func (self *_object) defineOwnProperty(name string, descriptor _property, throw bool) bool { return self.objectClass.defineOwnProperty(self, name, descriptor, throw) } func (self *_object) delete(name string, throw bool) bool { return self.objectClass.delete(self, name, throw) } func (self *_object) enumerate(all bool, each func(string) bool) { self.objectClass.enumerate(self, all, each) } func (self *_object) _exists(name string) bool { _, exists := self.property[name] return exists } func (self *_object) _read(name string) (_property, bool) { property, exists := self.property[name] return property, exists } func (self *_object) _write(name string, value interface{}, mode _propertyMode) { if value == nil { value = Value{} } _, exists := self.property[name] self.property[name] = _property{value, mode} if !exists { self.propertyOrder = append(self.propertyOrder, name) } } func (self *_object) _delete(name string) { _, exists := self.property[name] delete(self.property, name) if exists { for index, property := range self.propertyOrder { if name == property { if index == len(self.propertyOrder)-1 { self.propertyOrder = self.propertyOrder[:index] } else { self.propertyOrder = append(self.propertyOrder[:index], self.propertyOrder[index+1:]...) } } } } } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/object_class.go000066400000000000000000000262561331145703200253720ustar00rootroot00000000000000package otto import ( "encoding/json" ) type _objectClass struct { getOwnProperty func(*_object, string) *_property getProperty func(*_object, string) *_property get func(*_object, string) Value canPut func(*_object, string) bool put func(*_object, string, Value, bool) hasProperty func(*_object, string) bool hasOwnProperty func(*_object, string) bool defineOwnProperty func(*_object, string, _property, bool) bool delete func(*_object, string, bool) bool enumerate func(*_object, bool, func(string) bool) clone func(*_object, *_object, *_clone) *_object marshalJSON func(*_object) json.Marshaler } func objectEnumerate(self *_object, all bool, each func(string) bool) { for _, name := range self.propertyOrder { if all || self.property[name].enumerable() { if !each(name) { return } } } } var ( _classObject, _classArray, _classString, _classArguments, _classGoStruct, _classGoMap, _classGoArray, _classGoSlice, _ *_objectClass ) func init() { _classObject = &_objectClass{ objectGetOwnProperty, objectGetProperty, objectGet, objectCanPut, objectPut, objectHasProperty, objectHasOwnProperty, objectDefineOwnProperty, objectDelete, objectEnumerate, objectClone, nil, } _classArray = &_objectClass{ objectGetOwnProperty, objectGetProperty, objectGet, objectCanPut, objectPut, objectHasProperty, objectHasOwnProperty, arrayDefineOwnProperty, objectDelete, objectEnumerate, objectClone, nil, } _classString = &_objectClass{ stringGetOwnProperty, objectGetProperty, objectGet, objectCanPut, objectPut, objectHasProperty, objectHasOwnProperty, objectDefineOwnProperty, objectDelete, stringEnumerate, objectClone, nil, } _classArguments = &_objectClass{ argumentsGetOwnProperty, objectGetProperty, argumentsGet, objectCanPut, objectPut, objectHasProperty, objectHasOwnProperty, argumentsDefineOwnProperty, argumentsDelete, objectEnumerate, objectClone, nil, } _classGoStruct = &_objectClass{ goStructGetOwnProperty, objectGetProperty, objectGet, goStructCanPut, goStructPut, objectHasProperty, objectHasOwnProperty, objectDefineOwnProperty, objectDelete, goStructEnumerate, objectClone, goStructMarshalJSON, } _classGoMap = &_objectClass{ goMapGetOwnProperty, objectGetProperty, objectGet, objectCanPut, objectPut, objectHasProperty, objectHasOwnProperty, goMapDefineOwnProperty, goMapDelete, goMapEnumerate, objectClone, nil, } _classGoArray = &_objectClass{ goArrayGetOwnProperty, objectGetProperty, objectGet, objectCanPut, objectPut, objectHasProperty, objectHasOwnProperty, goArrayDefineOwnProperty, goArrayDelete, goArrayEnumerate, objectClone, nil, } _classGoSlice = &_objectClass{ goSliceGetOwnProperty, objectGetProperty, objectGet, objectCanPut, objectPut, objectHasProperty, objectHasOwnProperty, goSliceDefineOwnProperty, goSliceDelete, goSliceEnumerate, objectClone, nil, } } // Allons-y // 8.12.1 func objectGetOwnProperty(self *_object, name string) *_property { // Return a _copy_ of the property property, exists := self._read(name) if !exists { return nil } return &property } // 8.12.2 func objectGetProperty(self *_object, name string) *_property { property := self.getOwnProperty(name) if property != nil { return property } if self.prototype != nil { return self.prototype.getProperty(name) } return nil } // 8.12.3 func objectGet(self *_object, name string) Value { property := self.getProperty(name) if property != nil { return property.get(self) } return Value{} } // 8.12.4 func objectCanPut(self *_object, name string) bool { canPut, _, _ := _objectCanPut(self, name) return canPut } func _objectCanPut(self *_object, name string) (canPut bool, property *_property, setter *_object) { property = self.getOwnProperty(name) if property != nil { switch propertyValue := property.value.(type) { case Value: canPut = property.writable() return case _propertyGetSet: setter = propertyValue[1] canPut = setter != nil return default: panic(self.runtime.panicTypeError()) } } if self.prototype == nil { return self.extensible, nil, nil } property = self.prototype.getProperty(name) if property == nil { return self.extensible, nil, nil } switch propertyValue := property.value.(type) { case Value: if !self.extensible { return false, nil, nil } return property.writable(), nil, nil case _propertyGetSet: setter = propertyValue[1] canPut = setter != nil return default: panic(self.runtime.panicTypeError()) } } // 8.12.5 func objectPut(self *_object, name string, value Value, throw bool) { if true { // Shortcut... // // So, right now, every class is using objectCanPut and every class // is using objectPut. // // If that were to no longer be the case, we would have to have // something to detect that here, so that we do not use an // incompatible canPut routine canPut, property, setter := _objectCanPut(self, name) if !canPut { self.runtime.typeErrorResult(throw) } else if setter != nil { setter.call(toValue(self), []Value{value}, false, nativeFrame) } else if property != nil { property.value = value self.defineOwnProperty(name, *property, throw) } else { self.defineProperty(name, value, 0111, throw) } return } // The long way... // // Right now, code should never get here, see above if !self.canPut(name) { self.runtime.typeErrorResult(throw) return } property := self.getOwnProperty(name) if property == nil { property = self.getProperty(name) if property != nil { if getSet, isAccessor := property.value.(_propertyGetSet); isAccessor { getSet[1].call(toValue(self), []Value{value}, false, nativeFrame) return } } self.defineProperty(name, value, 0111, throw) } else { switch propertyValue := property.value.(type) { case Value: property.value = value self.defineOwnProperty(name, *property, throw) case _propertyGetSet: if propertyValue[1] != nil { propertyValue[1].call(toValue(self), []Value{value}, false, nativeFrame) return } if throw { panic(self.runtime.panicTypeError()) } default: panic(self.runtime.panicTypeError()) } } } // 8.12.6 func objectHasProperty(self *_object, name string) bool { return self.getProperty(name) != nil } func objectHasOwnProperty(self *_object, name string) bool { return self.getOwnProperty(name) != nil } // 8.12.9 func objectDefineOwnProperty(self *_object, name string, descriptor _property, throw bool) bool { property, exists := self._read(name) { if !exists { if !self.extensible { goto Reject } if newGetSet, isAccessor := descriptor.value.(_propertyGetSet); isAccessor { if newGetSet[0] == &_nilGetSetObject { newGetSet[0] = nil } if newGetSet[1] == &_nilGetSetObject { newGetSet[1] = nil } descriptor.value = newGetSet } self._write(name, descriptor.value, descriptor.mode) return true } if descriptor.isEmpty() { return true } // TODO Per 8.12.9.6 - We should shortcut here (returning true) if // the current and new (define) properties are the same configurable := property.configurable() if !configurable { if descriptor.configurable() { goto Reject } // Test that, if enumerable is set on the property descriptor, then it should // be the same as the existing property if descriptor.enumerateSet() && descriptor.enumerable() != property.enumerable() { goto Reject } } value, isDataDescriptor := property.value.(Value) getSet, _ := property.value.(_propertyGetSet) if descriptor.isGenericDescriptor() { // GenericDescriptor } else if isDataDescriptor != descriptor.isDataDescriptor() { // DataDescriptor <=> AccessorDescriptor if !configurable { goto Reject } } else if isDataDescriptor && descriptor.isDataDescriptor() { // DataDescriptor <=> DataDescriptor if !configurable { if !property.writable() && descriptor.writable() { goto Reject } if !property.writable() { if descriptor.value != nil && !sameValue(value, descriptor.value.(Value)) { goto Reject } } } } else { // AccessorDescriptor <=> AccessorDescriptor newGetSet, _ := descriptor.value.(_propertyGetSet) presentGet, presentSet := true, true if newGetSet[0] == &_nilGetSetObject { // Present, but nil newGetSet[0] = nil } else if newGetSet[0] == nil { // Missing, not even nil newGetSet[0] = getSet[0] presentGet = false } if newGetSet[1] == &_nilGetSetObject { // Present, but nil newGetSet[1] = nil } else if newGetSet[1] == nil { // Missing, not even nil newGetSet[1] = getSet[1] presentSet = false } if !configurable { if (presentGet && (getSet[0] != newGetSet[0])) || (presentSet && (getSet[1] != newGetSet[1])) { goto Reject } } descriptor.value = newGetSet } { // This section will preserve attributes of // the original property, if necessary value1 := descriptor.value if value1 == nil { value1 = property.value } else if newGetSet, isAccessor := descriptor.value.(_propertyGetSet); isAccessor { if newGetSet[0] == &_nilGetSetObject { newGetSet[0] = nil } if newGetSet[1] == &_nilGetSetObject { newGetSet[1] = nil } value1 = newGetSet } mode1 := descriptor.mode if mode1&0222 != 0 { // TODO Factor this out into somewhere testable // (Maybe put into switch ...) mode0 := property.mode if mode1&0200 != 0 { if descriptor.isDataDescriptor() { mode1 &= ^0200 // Turn off "writable" missing mode1 |= (mode0 & 0100) } } if mode1&020 != 0 { mode1 |= (mode0 & 010) } if mode1&02 != 0 { mode1 |= (mode0 & 01) } mode1 &= 0311 // 0311 to preserve the non-setting on "writable" } self._write(name, value1, mode1) } return true } Reject: if throw { panic(self.runtime.panicTypeError()) } return false } func objectDelete(self *_object, name string, throw bool) bool { property_ := self.getOwnProperty(name) if property_ == nil { return true } if property_.configurable() { self._delete(name) return true } return self.runtime.typeErrorResult(throw) } func objectClone(in *_object, out *_object, clone *_clone) *_object { *out = *in out.runtime = clone.runtime if out.prototype != nil { out.prototype = clone.object(in.prototype) } out.property = make(map[string]_property, len(in.property)) out.propertyOrder = make([]string, len(in.propertyOrder)) copy(out.propertyOrder, in.propertyOrder) for index, property := range in.property { out.property[index] = clone.property(property) } switch value := in.value.(type) { case _nativeFunctionObject: out.value = value case _bindFunctionObject: out.value = _bindFunctionObject{ target: clone.object(value.target), this: clone.value(value.this), argumentList: clone.valueArray(value.argumentList), } case _nodeFunctionObject: out.value = _nodeFunctionObject{ node: value.node, stash: clone.stash(value.stash), } case _argumentsObject: out.value = value.clone(clone) } return out } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/object_test.go000066400000000000000000000403551331145703200252400ustar00rootroot00000000000000package otto import ( "testing" ) func TestObject_(t *testing.T) { tt(t, func() { test, _ := test() object := newObject(nil, "") is(object != nil, true) object.put("xyzzy", toValue("Nothing happens."), true) is(object.get("xyzzy"), "Nothing happens.") test(` var abc = Object.getOwnPropertyDescriptor(Object, "prototype"); [ [ typeof Object.prototype, abc.writable, abc.enumerable, abc.configurable ], ]; `, "object,false,false,false") }) } func TestStringObject(t *testing.T) { tt(t, func() { object := New().runtime.newStringObject(toValue("xyzzy")) is(object.get("1"), "y") is(object.get("10"), "undefined") is(object.get("2"), "z") }) } func TestObject_getPrototypeOf(t *testing.T) { tt(t, func() { test, _ := test() test(` abc = {}; def = Object.getPrototypeOf(abc); ghi = Object.getPrototypeOf(def); [abc,def,ghi,ghi+""]; `, "[object Object],[object Object],,null") test(` abc = Object.getOwnPropertyDescriptor(Object, "getPrototypeOf"); [ abc.value === Object.getPrototypeOf, abc.writable, abc.enumerable, abc.configurable ]; `, "true,true,false,true") }) } func TestObject_new(t *testing.T) { tt(t, func() { test, _ := test() test(` [ new Object("abc"), new Object(2+2) ]; `, "abc,4") }) } func TestObject_create(t *testing.T) { tt(t, func() { test, _ := test() test(`raise: Object.create()`, "TypeError") test(` var abc = Object.create(null) var def = Object.create({x: 10, y: 20}) var ghi = Object.create(Object.prototype) var jkl = Object.create({x: 10, y: 20}, { z: { value: 30, writable: true }, // sum: { // get: function() { // return this.x + this.y + this.z // } // } }); [ abc.prototype, def.x, def.y, ghi, jkl.x, jkl.y, jkl.z ] `, ",10,20,[object Object],10,20,30") test(` var properties = {}; Object.defineProperty(properties, "abc", { value: {}, enumerable: false }); var mno = Object.create({}, properties); mno.hasOwnProperty("abc"); `, false) }) } func TestObject_toLocaleString(t *testing.T) { tt(t, func() { test, _ := test() test(` ({}).toLocaleString(); `, "[object Object]") test(` object = { toString: function() { return "Nothing happens."; } }; object.toLocaleString(); `, "Nothing happens.") }) } func TestObject_isExtensible(t *testing.T) { tt(t, func() { test, _ := test() test(`raise: Object.isExtensible(); `, "TypeError") // FIXME terst, Why raise? test(`raise: Object.isExtensible({}); `, true) test(`Object.isExtensible.length`, 1) test(`Object.isExtensible.prototype`, "undefined") }) } func TestObject_preventExtensions(t *testing.T) { tt(t, func() { test, _ := test() test(`raise: Object.preventExtensions() `, "TypeError") test(`raise: var abc = { def: true }; var ghi = Object.preventExtensions(abc); [ ghi.def === true, Object.isExtensible(abc), Object.isExtensible(ghi) ]; `, "true,false,false") test(` var abc = new String(); var def = Object.isExtensible(abc); Object.preventExtensions(abc); var ghi = false; try { Object.defineProperty(abc, "0", { value: "~" }); } catch (err) { ghi = err instanceof TypeError; } [ def, ghi, abc.hasOwnProperty("0"), typeof abc[0] ]; `, "true,true,false,undefined") test(`Object.preventExtensions.length`, 1) test(`Object.preventExtensions.prototype`, "undefined") }) } func TestObject_isSealed(t *testing.T) { tt(t, func() { test, _ := test() test(`Object.isSealed.length`, 1) test(`Object.isSealed.prototype`, "undefined") }) } func TestObject_seal(t *testing.T) { tt(t, func() { test, _ := test() test(`raise: Object.seal()`, "TypeError") test(` var abc = {a:1,b:1,c:3}; var sealed = Object.isSealed(abc); Object.seal(abc); [sealed, Object.isSealed(abc)]; `, "false,true") test(` var abc = {a:1,b:1,c:3}; var sealed = Object.isSealed(abc); var caught = false; Object.seal(abc); abc.b = 5; Object.defineProperty(abc, "a", {value:4}); try { Object.defineProperty(abc, "a", {value:42,enumerable:false}); } catch (e) { caught = e instanceof TypeError; } [sealed, Object.isSealed(abc), caught, abc.a, abc.b]; `, "false,true,true,4,5") test(`Object.seal.length`, 1) test(`Object.seal.prototype`, "undefined") }) } func TestObject_isFrozen(t *testing.T) { tt(t, func() { test, _ := test() test(`raise: Object.isFrozen()`, "TypeError") test(`Object.isFrozen(Object.preventExtensions({a:1}))`, false) test(`Object.isFrozen({})`, false) test(` var abc = {}; Object.defineProperty(abc, "def", { value: "def", writable: true, configurable: false }); Object.preventExtensions(abc); !Object.isFrozen(abc); `, true) test(`Object.isFrozen.length`, 1) test(`Object.isFrozen.prototype`, "undefined") }) } func TestObject_freeze(t *testing.T) { tt(t, func() { test, _ := test() test(`raise: Object.freeze()`, "TypeError") test(` var abc = {a:1,b:2,c:3}; var frozen = Object.isFrozen(abc); Object.freeze(abc); abc.b = 5; [frozen, Object.isFrozen(abc), abc.b]; `, "false,true,2") test(` var abc = {a:1,b:2,c:3}; var frozen = Object.isFrozen(abc); var caught = false; Object.freeze(abc); abc.b = 5; try { Object.defineProperty(abc, "a", {value:4}); } catch (e) { caught = e instanceof TypeError; } [frozen, Object.isFrozen(abc), caught, abc.a, abc.b]; `, "false,true,true,1,2") test(`Object.freeze.length`, 1) test(`Object.freeze.prototype`, "undefined") }) } func TestObject_defineProperty(t *testing.T) { tt(t, func() { test, _ := test() test(` (function(abc, def, ghi){ Object.defineProperty(arguments, "0", { enumerable: false }); return true; })(0, 1, 2); `, true) test(` var abc = {}; abc.def = 3.14; // Default: writable: true, enumerable: true, configurable: true Object.defineProperty(abc, "def", { value: 42 }); var ghi = Object.getOwnPropertyDescriptor(abc, "def"); [ ghi.value, ghi.writable, ghi.enumerable, ghi.configurable ]; `, "42,true,true,true") // Test that we handle the case of DefineOwnProperty // where [[Writable]] is something but [[Value]] is not test(` var abc = []; Object.defineProperty(abc, "0", { writable: false }); 0 in abc; `, true) // Test that we handle the case of DefineOwnProperty // where [[Writable]] is something but [[Value]] is not // (and the property originally had something for [[Value]] test(` abc = { def: 42 }; Object.defineProperty(abc, "def", { writable: false }); abc.def; `, 42) }) } func TestObject_keys(t *testing.T) { tt(t, func() { test, _ := test() test(`Object.keys({ abc:undefined, def:undefined })`, "abc,def") test(` function abc() { this.abc = undefined; this.def = undefined; } Object.keys(new abc()) `, "abc,def") test(` function def() { this.ghi = undefined; } def.prototype = new abc(); Object.keys(new def()); `, "ghi") test(` var ghi = Object.create( { abc: undefined, def: undefined }, { ghi: { value: undefined, enumerable: true }, jkl: { value: undefined, enumerable: false } } ); Object.keys(ghi); `, "ghi") test(` (function(abc, def, ghi){ return Object.keys(arguments) })(undefined, undefined); `, "0,1") test(` (function(abc, def, ghi){ return Object.keys(arguments) })(undefined, undefined, undefined, undefined); `, "0,1,2,3") }) } func TestObject_getOwnPropertyNames(t *testing.T) { tt(t, func() { test, _ := test() test(`Object.getOwnPropertyNames({ abc:undefined, def:undefined })`, "abc,def") test(` var ghi = Object.create( { abc: undefined, def: undefined }, { ghi: { value: undefined, enumerable: true }, jkl: { value: undefined, enumerable: false } } ); Object.getOwnPropertyNames(ghi) `, "ghi,jkl") }) } func TestObjectGetterSetter(t *testing.T) { tt(t, func() { test, _ := test() test(`raise: Object.create({}, { abc: { get: function(){ return "true"; }, writable: true } }).abc; `, "TypeError") test(`raise: Object.create({}, { abc: { get: function(){ return "true"; }, writable: false } }).abc; `, "TypeError") test(` Object.create({}, { abc: { get: function(){ return "true"; } } }).abc; `, "true") test(` Object.create({xyz:true},{abc:{get:function(){return this.xyx}}}).abc; Object.create({ xyz: true }, { abc: { get: function(){ return this.xyz; } } }).abc; `, true) test(` var abc = false; var def = Object.create({}, { xyz: { set: function(value) { abc = value; } } }); def.xyz = true; [ abc ]; `, "true") test(` var abc = {}; Object.defineProperty(abc, "def", { value: "xyzzy", configurable: true }); Object.preventExtensions(abc); Object.defineProperty(abc, "def", { get: function() { return 5; } }); var def = Object.getOwnPropertyDescriptor(abc, "def"); [ abc.def, typeof def.get, typeof def.set, typeof def.value, def.configurable, def.enumerable, typeof def.writable ]; `, "5,function,undefined,undefined,true,false,undefined") test(` var abc = {}; Object.defineProperty(abc, "def", { get: function() { return 5; } configurable: true }); Object.preventExtensions(abc); Object.defineProperty(abc, "def", { value: "xyzzy", }); var def = Object.getOwnPropertyDescriptor(abc, "def"); [ abc.def, typeof def.get, typeof def.set, def.value, def.configurable, def.enumerable, def.writable ]; `, "xyzzy,undefined,undefined,xyzzy,true,false,false") test(` var abc = {}; function _get0() { return 10; } function _set(value) { abc.def = value; } Object.defineProperty(abc, "ghi", { get: _get0, set: _set, configurable: true }); function _get1() { return 20; } Object.defineProperty(abc, "ghi", { get: _get0 }); var descriptor = Object.getOwnPropertyDescriptor(abc, "ghi"); [ typeof descriptor.set ]; `, "function") test(`raise: var abc = []; Object.defineProperty(abc, "length", { get: function () { return 2; } }); `, "TypeError") test(` var abc = {}; var getter = function() { return 1; } Object.defineProperty(abc, "def", { get: getter, configurable: false }); var jkl = undefined; try { Object.defineProperty(abc, "def", { get: undefined }); } catch (err) { jkl = err; } var ghi = Object.getOwnPropertyDescriptor(abc, "def"); [ jkl instanceof TypeError, ghi.get === getter, ghi.configurable, ghi.enumerable ]; `, "true,true,false,false") test(` var abc = {}; var getter = function() { return 1; }; Object.defineProperty(abc, "def", { get: getter }); Object.defineProperty(abc, "def", { set: undefined }); var ghi = Object.getOwnPropertyDescriptor(abc, "def"); [ ghi.get === getter, ghi.set === undefined, ghi.configurable, ghi.enumerable ]; `, "true,true,false,false") test(` var abc = {}; var getter = function() { return 1; }; Object.defineProperty(abc, "def", { get: getter }); var jkl = undefined; try { Object.defineProperty(abc, "def", { set: function() {} }); } catch (err) { jkl = err; } var ghi = Object.getOwnPropertyDescriptor(abc, "def"); [ jkl instanceof TypeError, ghi.get === getter, ghi.set, ghi.configurable, ghi.enumerable ]; `, "true,true,,false,false") test(` var abc = {}; var def = "xyzzy"; Object.defineProperty(abc, "ghi", { get: undefined, set: function(value) { def = value; }, enumerable: true, configurable: true }); var hasOwn = abc.hasOwnProperty("ghi"); var descriptor = Object.getOwnPropertyDescriptor(abc, "ghi"); [ hasOwn, typeof descriptor.get ]; `, "true,undefined") test(` var abc = "xyzzy"; Object.defineProperty(Array.prototype, "abc", { get: function () { return abc; }, set: function (value) { abc = value; }, enumerable: true, configurable: true }); var def = []; def.abc = 3.14159; [ def.hasOwnProperty("abc"), def.abc, abc ]; `, "false,3.14159,3.14159") }) } func TestProperty(t *testing.T) { tt(t, func() { property := _property{} property.writeOn() is(property.writeSet(), true) property.writeClear() is(property.writeSet(), false) property.writeOff() is(property.writeSet(), true) property.writeClear() is(property.writeSet(), false) }) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/otto.go000066400000000000000000000505331331145703200237170ustar00rootroot00000000000000/* Package otto is a JavaScript parser and interpreter written natively in Go. http://godoc.org/github.com/robertkrimen/otto import ( "github.com/robertkrimen/otto" ) Run something in the VM vm := otto.New() vm.Run(` abc = 2 + 2; console.log("The value of abc is " + abc); // 4 `) Get a value out of the VM value, err := vm.Get("abc") value, _ := value.ToInteger() } Set a number vm.Set("def", 11) vm.Run(` console.log("The value of def is " + def); // The value of def is 11 `) Set a string vm.Set("xyzzy", "Nothing happens.") vm.Run(` console.log(xyzzy.length); // 16 `) Get the value of an expression value, _ = vm.Run("xyzzy.length") { // value is an int64 with a value of 16 value, _ := value.ToInteger() } An error happens value, err = vm.Run("abcdefghijlmnopqrstuvwxyz.length") if err != nil { // err = ReferenceError: abcdefghijlmnopqrstuvwxyz is not defined // If there is an error, then value.IsUndefined() is true ... } Set a Go function vm.Set("sayHello", func(call otto.FunctionCall) otto.Value { fmt.Printf("Hello, %s.\n", call.Argument(0).String()) return otto.Value{} }) Set a Go function that returns something useful vm.Set("twoPlus", func(call otto.FunctionCall) otto.Value { right, _ := call.Argument(0).ToInteger() result, _ := vm.ToValue(2 + right) return result }) Use the functions in JavaScript result, _ = vm.Run(` sayHello("Xyzzy"); // Hello, Xyzzy. sayHello(); // Hello, undefined result = twoPlus(2.0); // 4 `) Parser A separate parser is available in the parser package if you're just interested in building an AST. http://godoc.org/github.com/robertkrimen/otto/parser Parse and return an AST filename := "" // A filename is optional src := ` // Sample xyzzy example (function(){ if (3.14159 > 0) { console.log("Hello, World."); return; } var xyzzy = NaN; console.log("Nothing happens."); return xyzzy; })(); ` // Parse some JavaScript, yielding a *ast.Program and/or an ErrorList program, err := parser.ParseFile(nil, filename, src, 0) otto You can run (Go) JavaScript from the commandline with: http://github.com/robertkrimen/otto/tree/master/otto $ go get -v github.com/robertkrimen/otto/otto Run JavaScript by entering some source on stdin or by giving otto a filename: $ otto example.js underscore Optionally include the JavaScript utility-belt library, underscore, with this import: import ( "github.com/robertkrimen/otto" _ "github.com/robertkrimen/otto/underscore" ) // Now every otto runtime will come loaded with underscore For more information: http://github.com/robertkrimen/otto/tree/master/underscore Caveat Emptor The following are some limitations with otto: * "use strict" will parse, but does nothing. * The regular expression engine (re2/regexp) is not fully compatible with the ECMA5 specification. * Otto targets ES5. ES6 features (eg: Typed Arrays) are not supported. Regular Expression Incompatibility Go translates JavaScript-style regular expressions into something that is "regexp" compatible via `parser.TransformRegExp`. Unfortunately, RegExp requires backtracking for some patterns, and backtracking is not supported by the standard Go engine: https://code.google.com/p/re2/wiki/Syntax Therefore, the following syntax is incompatible: (?=) // Lookahead (positive), currently a parsing error (?!) // Lookahead (backhead), currently a parsing error \1 // Backreference (\1, \2, \3, ...), currently a parsing error A brief discussion of these limitations: "Regexp (?!re)" https://groups.google.com/forum/?fromgroups=#%21topic/golang-nuts/7qgSDWPIh_E More information about re2: https://code.google.com/p/re2/ In addition to the above, re2 (Go) has a different definition for \s: [\t\n\f\r ]. The JavaScript definition, on the other hand, also includes \v, Unicode "Separator, Space", etc. Halting Problem If you want to stop long running executions (like third-party code), you can use the interrupt channel to do this: package main import ( "errors" "fmt" "os" "time" "github.com/robertkrimen/otto" ) var halt = errors.New("Stahp") func main() { runUnsafe(`var abc = [];`) runUnsafe(` while (true) { // Loop forever }`) } func runUnsafe(unsafe string) { start := time.Now() defer func() { duration := time.Since(start) if caught := recover(); caught != nil { if caught == halt { fmt.Fprintf(os.Stderr, "Some code took to long! Stopping after: %v\n", duration) return } panic(caught) // Something else happened, repanic! } fmt.Fprintf(os.Stderr, "Ran code successfully: %v\n", duration) }() vm := otto.New() vm.Interrupt = make(chan func(), 1) // The buffer prevents blocking go func() { time.Sleep(2 * time.Second) // Stop after two seconds vm.Interrupt <- func() { panic(halt) } }() vm.Run(unsafe) // Here be dragons (risky code) } Where is setTimeout/setInterval? These timing functions are not actually part of the ECMA-262 specification. Typically, they belong to the `windows` object (in the browser). It would not be difficult to provide something like these via Go, but you probably want to wrap otto in an event loop in that case. For an example of how this could be done in Go with otto, see natto: http://github.com/robertkrimen/natto Here is some more discussion of the issue: * http://book.mixu.net/node/ch2.html * http://en.wikipedia.org/wiki/Reentrancy_%28computing%29 * http://aaroncrane.co.uk/2009/02/perl_safe_signals/ */ package otto import ( "fmt" "strings" "github.com/robertkrimen/otto/file" "github.com/robertkrimen/otto/registry" ) // Otto is the representation of the JavaScript runtime. Each instance of Otto has a self-contained namespace. type Otto struct { // Interrupt is a channel for interrupting the runtime. You can use this to halt a long running execution, for example. // See "Halting Problem" for more information. Interrupt chan func() runtime *_runtime } // New will allocate a new JavaScript runtime func New() *Otto { self := &Otto{ runtime: newContext(), } self.runtime.otto = self self.runtime.traceLimit = 10 self.Set("console", self.runtime.newConsole()) registry.Apply(func(entry registry.Entry) { self.Run(entry.Source()) }) return self } func (otto *Otto) clone() *Otto { self := &Otto{ runtime: otto.runtime.clone(), } self.runtime.otto = self return self } // Run will allocate a new JavaScript runtime, run the given source // on the allocated runtime, and return the runtime, resulting value, and // error (if any). // // src may be a string, a byte slice, a bytes.Buffer, or an io.Reader, but it MUST always be in UTF-8. // // src may also be a Script. // // src may also be a Program, but if the AST has been modified, then runtime behavior is undefined. // func Run(src interface{}) (*Otto, Value, error) { otto := New() value, err := otto.Run(src) // This already does safety checking return otto, value, err } // Run will run the given source (parsing it first if necessary), returning the resulting value and error (if any) // // src may be a string, a byte slice, a bytes.Buffer, or an io.Reader, but it MUST always be in UTF-8. // // If the runtime is unable to parse source, then this function will return undefined and the parse error (nothing // will be evaluated in this case). // // src may also be a Script. // // src may also be a Program, but if the AST has been modified, then runtime behavior is undefined. // func (self Otto) Run(src interface{}) (Value, error) { value, err := self.runtime.cmpl_run(src, nil) if !value.safe() { value = Value{} } return value, err } // Eval will do the same thing as Run, except without leaving the current scope. // // By staying in the same scope, the code evaluated has access to everything // already defined in the current stack frame. This is most useful in, for // example, a debugger call. func (self Otto) Eval(src interface{}) (Value, error) { if self.runtime.scope == nil { self.runtime.enterGlobalScope() defer self.runtime.leaveScope() } value, err := self.runtime.cmpl_eval(src, nil) if !value.safe() { value = Value{} } return value, err } // Get the value of the top-level binding of the given name. // // If there is an error (like the binding does not exist), then the value // will be undefined. func (self Otto) Get(name string) (Value, error) { value := Value{} err := catchPanic(func() { value = self.getValue(name) }) if !value.safe() { value = Value{} } return value, err } func (self Otto) getValue(name string) Value { return self.runtime.globalStash.getBinding(name, false) } // Set the top-level binding of the given name to the given value. // // Set will automatically apply ToValue to the given value in order // to convert it to a JavaScript value (type Value). // // If there is an error (like the binding is read-only, or the ToValue conversion // fails), then an error is returned. // // If the top-level binding does not exist, it will be created. func (self Otto) Set(name string, value interface{}) error { { value, err := self.ToValue(value) if err != nil { return err } err = catchPanic(func() { self.setValue(name, value) }) return err } } func (self Otto) setValue(name string, value Value) { self.runtime.globalStash.setValue(name, value, false) } func (self Otto) SetDebuggerHandler(fn func(vm *Otto)) { self.runtime.debugger = fn } func (self Otto) SetRandomSource(fn func() float64) { self.runtime.random = fn } // SetStackDepthLimit sets an upper limit to the depth of the JavaScript // stack. In simpler terms, this limits the number of "nested" function calls // you can make in a particular interpreter instance. // // Note that this doesn't take into account the Go stack depth. If your // JavaScript makes a call to a Go function, otto won't keep track of what // happens outside the interpreter. So if your Go function is infinitely // recursive, you're still in trouble. func (self Otto) SetStackDepthLimit(limit int) { self.runtime.stackLimit = limit } // SetStackTraceLimit sets an upper limit to the number of stack frames that // otto will use when formatting an error's stack trace. By default, the limit // is 10. This is consistent with V8 and SpiderMonkey. // // TODO: expose via `Error.stackTraceLimit` func (self Otto) SetStackTraceLimit(limit int) { self.runtime.traceLimit = limit } // MakeCustomError creates a new Error object with the given name and message, // returning it as a Value. func (self Otto) MakeCustomError(name, message string) Value { return self.runtime.toValue(self.runtime.newError(name, self.runtime.toValue(message), 0)) } // MakeRangeError creates a new RangeError object with the given message, // returning it as a Value. func (self Otto) MakeRangeError(message string) Value { return self.runtime.toValue(self.runtime.newRangeError(self.runtime.toValue(message))) } // MakeSyntaxError creates a new SyntaxError object with the given message, // returning it as a Value. func (self Otto) MakeSyntaxError(message string) Value { return self.runtime.toValue(self.runtime.newSyntaxError(self.runtime.toValue(message))) } // MakeTypeError creates a new TypeError object with the given message, // returning it as a Value. func (self Otto) MakeTypeError(message string) Value { return self.runtime.toValue(self.runtime.newTypeError(self.runtime.toValue(message))) } // Context is a structure that contains information about the current execution // context. type Context struct { Filename string Line int Column int Callee string Symbols map[string]Value This Value Stacktrace []string } // Context returns the current execution context of the vm, traversing up to // ten stack frames, and skipping any innermost native function stack frames. func (self Otto) Context() Context { return self.ContextSkip(10, true) } // ContextLimit returns the current execution context of the vm, with a // specific limit on the number of stack frames to traverse, skipping any // innermost native function stack frames. func (self Otto) ContextLimit(limit int) Context { return self.ContextSkip(limit, true) } // ContextSkip returns the current execution context of the vm, with a // specific limit on the number of stack frames to traverse, optionally // skipping any innermost native function stack frames. func (self Otto) ContextSkip(limit int, skipNative bool) (ctx Context) { // Ensure we are operating in a scope if self.runtime.scope == nil { self.runtime.enterGlobalScope() defer self.runtime.leaveScope() } scope := self.runtime.scope frame := scope.frame for skipNative && frame.native && scope.outer != nil { scope = scope.outer frame = scope.frame } // Get location information ctx.Filename = "" ctx.Callee = frame.callee switch { case frame.native: ctx.Filename = frame.nativeFile ctx.Line = frame.nativeLine ctx.Column = 0 case frame.file != nil: ctx.Filename = "" if p := frame.file.Position(file.Idx(frame.offset)); p != nil { ctx.Line = p.Line ctx.Column = p.Column if p.Filename != "" { ctx.Filename = p.Filename } } } // Get the current scope this Value ctx.This = toValue_object(scope.this) // Build stacktrace (up to 10 levels deep) ctx.Symbols = make(map[string]Value) ctx.Stacktrace = append(ctx.Stacktrace, frame.location()) for limit != 0 { // Get variables stash := scope.lexical for { for _, name := range getStashProperties(stash) { if _, ok := ctx.Symbols[name]; !ok { ctx.Symbols[name] = stash.getBinding(name, true) } } stash = stash.outer() if stash == nil || stash.outer() == nil { break } } scope = scope.outer if scope == nil { break } if scope.frame.offset >= 0 { ctx.Stacktrace = append(ctx.Stacktrace, scope.frame.location()) } limit-- } return } // Call the given JavaScript with a given this and arguments. // // If this is nil, then some special handling takes place to determine the proper // this value, falling back to a "standard" invocation if necessary (where this is // undefined). // // If source begins with "new " (A lowercase new followed by a space), then // Call will invoke the function constructor rather than performing a function call. // In this case, the this argument has no effect. // // // value is a String object // value, _ := vm.Call("Object", nil, "Hello, World.") // // // Likewise... // value, _ := vm.Call("new Object", nil, "Hello, World.") // // // This will perform a concat on the given array and return the result // // value is [ 1, 2, 3, undefined, 4, 5, 6, 7, "abc" ] // value, _ := vm.Call(`[ 1, 2, 3, undefined, 4 ].concat`, nil, 5, 6, 7, "abc") // func (self Otto) Call(source string, this interface{}, argumentList ...interface{}) (Value, error) { thisValue := Value{} construct := false if strings.HasPrefix(source, "new ") { source = source[4:] construct = true } // FIXME enterGlobalScope self.runtime.enterGlobalScope() defer func() { self.runtime.leaveScope() }() if !construct && this == nil { program, err := self.runtime.cmpl_parse("", source+"()", nil) if err == nil { if node, ok := program.body[0].(*_nodeExpressionStatement); ok { if node, ok := node.expression.(*_nodeCallExpression); ok { var value Value err := catchPanic(func() { value = self.runtime.cmpl_evaluate_nodeCallExpression(node, argumentList) }) if err != nil { return Value{}, err } return value, nil } } } } else { value, err := self.ToValue(this) if err != nil { return Value{}, err } thisValue = value } { this := thisValue fn, err := self.Run(source) if err != nil { return Value{}, err } if construct { result, err := fn.constructSafe(self.runtime, this, argumentList...) if err != nil { return Value{}, err } return result, nil } result, err := fn.Call(this, argumentList...) if err != nil { return Value{}, err } return result, nil } } // Object will run the given source and return the result as an object. // // For example, accessing an existing object: // // object, _ := vm.Object(`Number`) // // Or, creating a new object: // // object, _ := vm.Object(`({ xyzzy: "Nothing happens." })`) // // Or, creating and assigning an object: // // object, _ := vm.Object(`xyzzy = {}`) // object.Set("volume", 11) // // If there is an error (like the source does not result in an object), then // nil and an error is returned. func (self Otto) Object(source string) (*Object, error) { value, err := self.runtime.cmpl_run(source, nil) if err != nil { return nil, err } if value.IsObject() { return value.Object(), nil } return nil, fmt.Errorf("value is not an object") } // ToValue will convert an interface{} value to a value digestible by otto/JavaScript. func (self Otto) ToValue(value interface{}) (Value, error) { return self.runtime.safeToValue(value) } // Copy will create a copy/clone of the runtime. // // Copy is useful for saving some time when creating many similar runtimes. // // This method works by walking the original runtime and cloning each object, scope, stash, // etc. into a new runtime. // // Be on the lookout for memory leaks or inadvertent sharing of resources. func (in *Otto) Copy() *Otto { out := &Otto{ runtime: in.runtime.clone(), } out.runtime.otto = out return out } // Object{} // Object is the representation of a JavaScript object. type Object struct { object *_object value Value } func _newObject(object *_object, value Value) *Object { // value MUST contain object! return &Object{ object: object, value: value, } } // Call a method on the object. // // It is essentially equivalent to: // // var method, _ := object.Get(name) // method.Call(object, argumentList...) // // An undefined value and an error will result if: // // 1. There is an error during conversion of the argument list // 2. The property is not actually a function // 3. An (uncaught) exception is thrown // func (self Object) Call(name string, argumentList ...interface{}) (Value, error) { // TODO: Insert an example using JavaScript below... // e.g., Object("JSON").Call("stringify", ...) function, err := self.Get(name) if err != nil { return Value{}, err } return function.Call(self.Value(), argumentList...) } // Value will return self as a value. func (self Object) Value() Value { return self.value } // Get the value of the property with the given name. func (self Object) Get(name string) (Value, error) { value := Value{} err := catchPanic(func() { value = self.object.get(name) }) if !value.safe() { value = Value{} } return value, err } // Set the property of the given name to the given value. // // An error will result if the setting the property triggers an exception (i.e. read-only), // or there is an error during conversion of the given value. func (self Object) Set(name string, value interface{}) error { { value, err := self.object.runtime.safeToValue(value) if err != nil { return err } err = catchPanic(func() { self.object.put(name, value, true) }) return err } } // Keys gets the keys for the given object. // // Equivalent to calling Object.keys on the object. func (self Object) Keys() []string { var keys []string self.object.enumerate(false, func(name string) bool { keys = append(keys, name) return true }) return keys } // KeysByParent gets the keys (and those of the parents) for the given object, // in order of "closest" to "furthest". func (self Object) KeysByParent() [][]string { var a [][]string for o := self.object; o != nil; o = o.prototype { var l []string o.enumerate(false, func(name string) bool { l = append(l, name) return true }) a = append(a, l) } return a } // Class will return the class string of the object. // // The return value will (generally) be one of: // // Object // Function // Array // String // Number // Boolean // Date // RegExp // func (self Object) Class() string { return self.object.class } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/otto/000077500000000000000000000000001331145703200233625ustar00rootroot00000000000000golang-github-robertkrimen-otto-0.0~git20180617.15f95af/otto/Makefile000066400000000000000000000000431331145703200250170ustar00rootroot00000000000000.PHONY: build build: go build -a golang-github-robertkrimen-otto-0.0~git20180617.15f95af/otto/main.go000066400000000000000000000014311331145703200246340ustar00rootroot00000000000000package main import ( "flag" "fmt" "io/ioutil" "os" "github.com/robertkrimen/otto" "github.com/robertkrimen/otto/underscore" ) var flag_underscore *bool = flag.Bool("underscore", true, "Load underscore into the runtime environment") func readSource(filename string) ([]byte, error) { if filename == "" || filename == "-" { return ioutil.ReadAll(os.Stdin) } return ioutil.ReadFile(filename) } func main() { flag.Parse() if !*flag_underscore { underscore.Disable() } err := func() error { src, err := readSource(flag.Arg(0)) if err != nil { return err } vm := otto.New() _, err = vm.Run(src) return err }() if err != nil { switch err := err.(type) { case *otto.Error: fmt.Print(err.String()) default: fmt.Println(err) } os.Exit(64) } } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/otto_.go000066400000000000000000000077171331145703200240640ustar00rootroot00000000000000package otto import ( "fmt" "regexp" runtime_ "runtime" "strconv" "strings" ) var isIdentifier_Regexp *regexp.Regexp = regexp.MustCompile(`^[a-zA-Z\$][a-zA-Z0-9\$]*$`) func isIdentifier(string_ string) bool { return isIdentifier_Regexp.MatchString(string_) } func (self *_runtime) toValueArray(arguments ...interface{}) []Value { length := len(arguments) if length == 1 { if valueArray, ok := arguments[0].([]Value); ok { return valueArray } return []Value{self.toValue(arguments[0])} } valueArray := make([]Value, length) for index, value := range arguments { valueArray[index] = self.toValue(value) } return valueArray } func stringToArrayIndex(name string) int64 { index, err := strconv.ParseInt(name, 10, 64) if err != nil { return -1 } if index < 0 { return -1 } if index >= maxUint32 { // The value 2^32 (or above) is not a valid index because // you cannot store a uint32 length for an index of uint32 return -1 } return index } func isUint32(value int64) bool { return value >= 0 && value <= maxUint32 } func arrayIndexToString(index int64) string { return strconv.FormatInt(index, 10) } func valueOfArrayIndex(array []Value, index int) Value { value, _ := getValueOfArrayIndex(array, index) return value } func getValueOfArrayIndex(array []Value, index int) (Value, bool) { if index >= 0 && index < len(array) { value := array[index] if !value.isEmpty() { return value, true } } return Value{}, false } // A range index can be anything from 0 up to length. It is NOT safe to use as an index // to an array, but is useful for slicing and in some ECMA algorithms. func valueToRangeIndex(indexValue Value, length int64, negativeIsZero bool) int64 { index := indexValue.number().int64 if negativeIsZero { if index < 0 { index = 0 } // minimum(index, length) if index >= length { index = length } return index } if index < 0 { index += length if index < 0 { index = 0 } } else { if index > length { index = length } } return index } func rangeStartEnd(array []Value, size int64, negativeIsZero bool) (start, end int64) { start = valueToRangeIndex(valueOfArrayIndex(array, 0), size, negativeIsZero) if len(array) == 1 { // If there is only the start argument, then end = size end = size return } // Assuming the argument is undefined... end = size endValue := valueOfArrayIndex(array, 1) if !endValue.IsUndefined() { // Which it is not, so get the value as an array index end = valueToRangeIndex(endValue, size, negativeIsZero) } return } func rangeStartLength(source []Value, size int64) (start, length int64) { start = valueToRangeIndex(valueOfArrayIndex(source, 0), size, false) // Assume the second argument is missing or undefined length = int64(size) if len(source) == 1 { // If there is only the start argument, then length = size return } lengthValue := valueOfArrayIndex(source, 1) if !lengthValue.IsUndefined() { // Which it is not, so get the value as an array index length = lengthValue.number().int64 } return } func boolFields(input string) (result map[string]bool) { result = map[string]bool{} for _, word := range strings.Fields(input) { result[word] = true } return result } func hereBeDragons(arguments ...interface{}) string { pc, _, _, _ := runtime_.Caller(1) name := runtime_.FuncForPC(pc).Name() message := fmt.Sprintf("Here be dragons -- %s", name) if len(arguments) > 0 { message += ": " argument0 := fmt.Sprintf("%s", arguments[0]) if len(arguments) == 1 { message += argument0 } else { message += fmt.Sprintf(argument0, arguments[1:]...) } } else { message += "." } return message } func throwHereBeDragons(arguments ...interface{}) { panic(hereBeDragons(arguments...)) } func eachPair(list []interface{}, fn func(_0, _1 interface{})) { for len(list) > 0 { var _0, _1 interface{} _0 = list[0] list = list[1:] // Pop off first if len(list) > 0 { _1 = list[0] list = list[1:] // Pop off second } fn(_0, _1) } } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/otto_error_test.go000066400000000000000000000016221331145703200261620ustar00rootroot00000000000000package otto import ( "testing" ) func TestOttoError(t *testing.T) { tt(t, func() { vm := New() _, err := vm.Run(`throw "Xyzzy"`) is(err, "Xyzzy") _, err = vm.Run(`throw new TypeError()`) is(err, "TypeError") _, err = vm.Run(`throw new TypeError("Nothing happens.")`) is(err, "TypeError: Nothing happens.") _, err = ToValue([]byte{}) is(err, "TypeError: invalid value (slice): missing runtime: [] ([]uint8)") _, err = vm.Run(` (function(){ return abcdef.length })() `) is(err, "ReferenceError: 'abcdef' is not defined") _, err = vm.Run(` function start() { } start() xyzzy() `) is(err, "ReferenceError: 'xyzzy' is not defined") _, err = vm.Run(` // Just a comment xyzzy `) is(err, "ReferenceError: 'xyzzy' is not defined") }) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/otto_test.go000066400000000000000000001054611331145703200247570ustar00rootroot00000000000000package otto import ( "bytes" "io" "testing" "github.com/robertkrimen/otto/parser" ) func TestOtto(t *testing.T) { tt(t, func() { test, _ := test() test("xyzzy = 2", 2) test("xyzzy + 2", 4) test("xyzzy += 16", 18) test("xyzzy", 18) test(` (function(){ return 1 })() `, 1) test(` (function(){ return 1 }).call(this) `, 1) test(` (function(){ var result (function(){ result = -1 })() return result })() `, -1) test(` var abc = 1 abc || (abc = -1) abc `, 1) test(` var abc = (function(){ 1 === 1 })(); abc; `, "undefined") }) } func TestFunction__(t *testing.T) { tt(t, func() { test, _ := test() test(` function abc() { return 1; }; abc(); `, 1) }) } func TestIf(t *testing.T) { tt(t, func() { test, _ := test() test(` abc = undefined; def = undefined; if (true) abc = 1 else abc = 2; if (false) { def = 3; } else def = 4; [ abc, def ]; `, "1,4") test(` if (1) { abc = 1; } else { abc = 0; } abc; `, 1) test(` if (0) { abc = 1; } else { abc = 0; } abc; `, 0) test(` abc = 0; if (0) { abc = 1; } abc; `, 0) test(` abc = 0; if (abc) { abc = 1; } abc; `, 0) }) } func TestSequence(t *testing.T) { tt(t, func() { test, _ := test() test(` 1, 2, 3; `, 3) }) } func TestCall(t *testing.T) { tt(t, func() { test, _ := test() test(` Math.pow(3, 2); `, 9) }) } func TestRunFunctionWithSetArguments(t *testing.T) { tt(t, func() { vm := New() vm.Run(`var sillyFunction = function(record){record.silly = true; record.answer *= -1};`) record := map[string]interface{}{"foo": "bar", "answer": 42} // Set performs a conversion that allows the map to be addressed as a Javascript object vm.Set("argument", record) _, err := vm.Run("sillyFunction(argument)") is(err, nil) is(record["answer"].(float64), -42) is(record["silly"].(bool), true) }) } func TestRunFunctionWithArgumentsPassedToCall(t *testing.T) { tt(t, func() { vm := New() vm.Run(`var sillyFunction = function(record){record.silly = true; record.answer *= -1};`) record := map[string]interface{}{"foo": "bar", "answer": 42} _, err := vm.Call("sillyFunction", nil, record) is(err, nil) is(record["answer"].(float64), -42) is(record["silly"].(bool), true) }) } func TestMember(t *testing.T) { tt(t, func() { test, _ := test() test(` abc = [ 0, 1, 2 ]; def = { "abc": 0, "def": 1, "ghi": 2, }; [ abc[2], def.abc, abc[1], def.def ]; `, "2,0,1,1") }) } func Test_this(t *testing.T) { tt(t, func() { test, _ := test() test(` typeof this; `, "object") }) } func TestWhile(t *testing.T) { tt(t, func() { test, _ := test() test(` limit = 4 abc = 0 while (limit) { abc = abc + 1 limit = limit - 1 } abc; `, 4) }) } func TestSwitch_break(t *testing.T) { tt(t, func() { test, _ := test() test(` var abc = true; var ghi = "Xyzzy"; while (abc) { switch ('def') { case 'def': break; } ghi = "Nothing happens."; abc = false; } ghi; `, "Nothing happens.") test(` var abc = true; var ghi = "Xyzzy"; WHILE: while (abc) { switch ('def') { case 'def': break WHILE; } ghi = "Nothing happens." abc = false } ghi; `, "Xyzzy") test(` var ghi = "Xyzzy"; FOR: for (;;) { switch ('def') { case 'def': break FOR; ghi = ""; } ghi = "Nothing happens."; } ghi; `, "Xyzzy") test(` var ghi = "Xyzzy"; FOR: for (var jkl in {}) { switch ('def') { case 'def': break FOR; ghi = "Something happens."; } ghi = "Nothing happens."; } ghi; `, "Xyzzy") test(` var ghi = "Xyzzy"; function jkl() { switch ('def') { case 'def': break; ghi = ""; } ghi = "Nothing happens."; } while (abc) { jkl(); abc = false; ghi = "Something happens."; } ghi; `, "Something happens.") }) } func TestTryFinally(t *testing.T) { tt(t, func() { test, _ := test() test(` var abc; try { abc = 1; } finally { abc = 2; } abc; `, 2) test(` var abc = false, def = 0; do { def += 1; if (def > 100) { break; } try { continue; } finally { abc = true; } } while(!abc && def < 10) def; `, 1) test(` var abc = false, def = 0, ghi = 0; do { def += 1; if (def > 100) { break; } try { throw 0; } catch (jkl) { continue; } finally { abc = true; ghi = 11; } ghi -= 1; } while(!abc && def < 10) ghi; `, 11) test(` var abc = 0, def = 0; do { try { abc += 1; throw "ghi"; } finally { def = 1; continue; } def -= 1; } while (abc < 2) [ abc, def ]; `, "2,1") }) } func TestTryCatch(t *testing.T) { tt(t, func() { test, _ := test() test(` var abc = 1; try { throw 4; abc = -1; } catch (xyzzy) { abc += xyzzy + 1; } abc; `, 6) test(` abc = 1; var def; try { try { throw 4; abc = -1; } catch (xyzzy) { abc += xyzzy + 1; throw 64; } } catch (xyzzy) { def = xyzzy; abc = -2; } [ def, abc ]; `, "64,-2") }) } func TestWith(t *testing.T) { tt(t, func() { test, _ := test() test(` var def; with({ abc: 9 }) { def = abc; } def; `, 9) test(` var def; with({ abc: function(){ return 11; } }) { def = abc(); } def; `, 11) }) } func TestSwitch(t *testing.T) { tt(t, func() { test, _ := test() test(` var abc = 0; switch (0) { default: abc += 1; case 1: abc += 2; case 2: abc += 4; case 3: abc += 8; } abc; `, 15) test(` abc = 0; switch (3) { default: abc += 1; case 1: abc += 2; case 2: abc += 4; case 3: abc += 8; } abc; `, 8) test(` abc = 0; switch (60) { case 1: abc += 2; case 2: abc += 4; case 3: abc += 8; } abc; `, 0) }) } func TestForIn(t *testing.T) { tt(t, func() { test, _ := test() test(` var abc; for (property in { a: 1 }) { abc = property; } abc; `, "a") test(` var ghi; for (property in new String("xyzzy")) { ghi = property; } ghi; `, "4") }) } func TestFor(t *testing.T) { tt(t, func() { test, _ := test() test(` var abc = 7; for (i = 0; i < 3; i += 1) { abc += 1; } abc; `, 10) test(` abc = 7; for (i = 0; i < 3; i += 1) { abc += 1; if (i == 1) { break; } } abc; `, 9) test(` abc = 7; for (i = 0; i < 3; i += 1) { if (i == 2) { continue; } abc += 1; } abc; `, 9) test(` abc = 0; for (;;) { abc += 1; if (abc == 3) break; } abc; `, 3) test(` for (abc = 0; ;) { abc += 1; if (abc == 3) break; } abc; `, 3) test(` for (abc = 0; ; abc += 1) { abc += 1; if (abc == 3) break; } abc; `, 3) }) } func TestLabelled(t *testing.T) { tt(t, func() { test, _ := test() // TODO Add emergency break test(` xyzzy: for (var abc = 0; abc <= 0; abc++) { for (var def = 0; def <= 1; def++) { if (def === 0) { continue xyzzy; } else { } } } `) test(` abc = 0 def: while (true) { while (true) { abc = abc + 1 if (abc > 11) { break def; } } } abc; `, 12) test(` abc = 0 def: do { do { abc = abc + 1 if (abc > 11) { break def; } } while (true) } while (true) abc; `, 12) }) } func TestConditional(t *testing.T) { tt(t, func() { test, _ := test() test(` [ true ? false : true, true ? 1 : 0, false ? 3.14159 : "abc" ]; `, "false,1,abc") }) } func TestArrayLiteral(t *testing.T) { tt(t, func() { test, _ := test() test(` [ 1, , 3.14159 ]; `, "1,,3.14159") }) } func TestAssignment(t *testing.T) { tt(t, func() { test, _ := test() test(` var abc = 1; abc; `, 1) test(` abc += 2; abc; `, 3) }) } func TestBinaryOperation(t *testing.T) { tt(t, func() { test, _ := test() test(`0 == 1`, false) test(`1 == "1"`, true) test(`0 === 1`, false) test(`1 === "1"`, false) test(`"1" === "1"`, true) }) } func Test_typeof(t *testing.T) { tt(t, func() { test, _ := test() test(`typeof abc`, "undefined") test(`typeof abc === 'undefined'`, true) test(`typeof {}`, "object") test(`typeof null`, "object") }) } func Test_PrimitiveValueObjectValue(t *testing.T) { tt(t, func() { test, _ := test() Number11 := test(`new Number(11)`) is(Number11.float64(), 11) }) } func Test_eval(t *testing.T) { tt(t, func() { test, _ := test() // FIXME terst, Is this correct? test(` var abc = 1; `, "undefined") test(` eval("abc += 1"); `, 2) test(` (function(){ var abc = 11; eval("abc += 1"); return abc; })(); `, 12) test(`abc`, 2) test(` (function(){ try { eval("var prop = \\u2029;"); return false; } catch (abc) { return [ abc instanceof SyntaxError, abc.toString() ]; } })(); `, "true,SyntaxError: Unexpected token ILLEGAL") test(` function abc(){ this.THIS = eval("this"); } var def = new abc(); def === def.THIS; `, true) }) } func Test_evalDirectIndirect(t *testing.T) { tt(t, func() { test, _ := test() // (function () {return this;}()).abc = "global"; test(` var abc = "global"; (function(){ try { var _eval = eval; var abc = "function"; return [ _eval("\'global\' === abc"), // eval (Indirect) eval("\'function\' === abc"), // eval (Direct) ]; } finally { delete this.abc; } })(); `, "true,true") }) } func TestError_URIError(t *testing.T) { tt(t, func() { test, _ := test() test(`new URIError() instanceof URIError`, true) test(` var abc try { decodeURI("http://example.com/ _^#%") } catch (def) { abc = def instanceof URIError } abc `, true) }) } func TestTo(t *testing.T) { tt(t, func() { test, _ := test() { value, _ := test(`"11"`).ToFloat() is(value, float64(11)) } { value, _ := test(`"11"`).ToInteger() is(value, int64(11)) value, _ = test(`1.1`).ToInteger() is(value, int64(1)) } }) } func TestShouldError(t *testing.T) { tt(t, func() { test, _ := test() test(`raise: xyzzy throw new TypeError("Nothing happens.") `, "ReferenceError: 'xyzzy' is not defined") }) } func TestAPI(t *testing.T) { tt(t, func() { test, vm := test() test(` String.prototype.xyzzy = function(){ return this.length + 11 + (arguments[0] || 0) } abc = new String("xyzzy") def = "Nothing happens." abc.xyzzy() `, 16) abc, _ := vm.Get("abc") def, _ := vm.Get("def") object := abc.Object() result, _ := object.Call("xyzzy") is(result, 16) result, _ = object.Call("xyzzy", 1) is(result, 17) value, _ := object.Get("xyzzy") result, _ = value.Call(def) is(result, 27) result, _ = value.Call(def, 3) is(result, 30) object = value.Object() // Object xyzzy result, _ = object.Value().Call(def, 3) is(result, 30) test(` abc = { 'abc': 1, 'def': false, 3.14159: NaN, }; abc['abc']; `, 1) abc, err := vm.Get("abc") is(err, nil) object = abc.Object() // Object abc value, err = object.Get("abc") is(err, nil) is(value, 1) is(object.Keys(), []string{"abc", "def", "3.14159"}) test(` abc = [ 0, 1, 2, 3.14159, "abc", , ]; abc.def = true; `) abc, err = vm.Get("abc") is(err, nil) object = abc.Object() // Object abc is(object.Keys(), []string{"0", "1", "2", "3", "4", "def"}) }) } func TestObjectKeys(t *testing.T) { tt(t, func() { vm := New() vm.Eval(`var x = Object.create(null); x.a = 1`) vm.Eval(`var y = Object.create(x); y.b = 2`) o1, _ := vm.Object("x") is(o1.Keys(), []string{"a"}) is(o1.KeysByParent(), [][]string{{"a"}}) o2, _ := vm.Object("y") is(o2.Keys(), []string{"b"}) is(o2.KeysByParent(), [][]string{{"b"}, {"a"}}) }) } func TestUnicode(t *testing.T) { tt(t, func() { test, _ := test() test(`var abc = eval("\"a\uFFFFa\"");`, "undefined") test(`abc.length`, 3) test(`abc != "aa"`, true) test("abc[1] === \"\uFFFF\"", true) }) } func TestDotMember(t *testing.T) { tt(t, func() { test, _ := test() test(` abc = { ghi: 11, } abc.def = "Xyzzy" abc.null = "Nothing happens." `) test(`abc.def`, "Xyzzy") test(`abc.null`, "Nothing happens.") test(`abc.ghi`, 11) test(` abc = { null: 11, } `) test(`abc.def`, "undefined") test(`abc.null`, 11) test(`abc.ghi`, "undefined") }) } func Test_stringToFloat(t *testing.T) { tt(t, func() { is(parseNumber("10e10000"), _Infinity) is(parseNumber("10e10_."), _NaN) }) } func Test_delete(t *testing.T) { tt(t, func() { test, _ := test() test(` delete 42; `, true) test(` var abc = delete $_undefined_$; abc = abc && delete ($_undefined_$); abc; `, true) // delete should not trigger get() test(` var abc = { get def() { throw "Test_delete: delete should not trigger get()" } }; delete abc.def `, true) }) } func TestObject_defineOwnProperty(t *testing.T) { tt(t, func() { test, _ := test() test(` var object = {}; var descriptor = new Boolean(false); descriptor.configurable = true; Object.defineProperties(object, { property: descriptor }); var abc = object.hasOwnProperty("property"); delete object.property; var def = object.hasOwnProperty("property"); [ abc, def ]; `, "true,false") test(` var object = [0, 1, 2]; Object.defineProperty(object, "0", { value: 42, writable: false, enumerable: false, configurable: false }); var abc = Object.getOwnPropertyDescriptor(object, "0"); [ abc.value, abc.writable, abc.enumerable, abc.configurable ]; `, "42,false,false,false") test(` var abc = { "xyzzy": 42 }; var def = Object.defineProperties(abc, ""); abc === def; `, true) }) } func Test_assignmentEvaluationOrder(t *testing.T) { tt(t, func() { test, _ := test() test(` var abc = 0; ((abc = 1) & abc); `, 1) test(` var abc = 0; (abc & (abc = 1)); `, 0) }) } func TestOttoCall(t *testing.T) { tt(t, func() { vm := New() _, err := vm.Run(` var abc = { ghi: 1, def: function(def){ var ghi = 0; if (this.ghi) { ghi = this.ghi; } return "def: " + (def + 3.14159 + ghi); } }; function structFunc(s) { return s.Val; } `) is(err, nil) value, err := vm.Call(`abc.def`, nil, 2) is(err, nil) is(value, "def: 6.14159") value, err = vm.Call(`abc.def`, "", 2) is(err, nil) is(value, "def: 5.14159") // Do not attempt to do a ToValue on a this of nil value, err = vm.Call(`jkl.def`, nil, 1, 2, 3) is(err, "!=", nil) is(value, "undefined") value, err = vm.Call(`[ 1, 2, 3, undefined, 4 ].concat`, nil, 5, 6, 7, "abc") is(err, nil) is(value, "1,2,3,,4,5,6,7,abc") s := struct{ Val int }{Val: 10} value, err = vm.Call("structFunc", nil, s) is(err, nil) is(value, 10) }) } func TestOttoCall_new(t *testing.T) { tt(t, func() { test, vm := test() vm.Set("abc", func(call FunctionCall) Value { value, err := call.Otto.Call(`new Object`, nil, "Nothing happens.") is(err, nil) return value }) test(` def = abc(); [ def, def instanceof String ]; `, "Nothing happens.,true") }) } func TestOttoCall_newWithBrackets(t *testing.T) { tt(t, func() { test, vm := test() _, err := vm.Run(`var a = {default: function B(x) { this.x = x; } }`) is(err, nil) test(`(new a['default'](1)).x`, 1) }) } func TestOttoCall_throw(t *testing.T) { // FIXME? (Been broken for a while) // Looks like this has been broken for a while... what // behavior do we want here? if true { return } tt(t, func() { test, vm := test() vm.Set("abc", func(call FunctionCall) Value { if false { call.Otto.Call(`throw eval`, nil, "({ def: 3.14159 })") } call.Otto.Call(`throw Error`, nil, "abcdef") return Value{} }) // TODO try { abc(); } catch (err) { error = err } // Possible unrelated error case: // If error is not declared beforehand, is later referencing it a ReferenceError? // Should the catch { } declare error in the outer scope? test(` var error; try { abc(); } catch (err) { error = err; } [ error instanceof Error, error.message, error.def ]; `, "true,abcdef,") vm.Set("def", func(call FunctionCall) Value { call.Otto.Call(`throw new Object`, nil, 3.14159) return UndefinedValue() }) test(` try { def(); } catch (err) { error = err; } [ error instanceof Error, error.message, error.def, typeof error, error, error instanceof Number ]; `, "false,,,object,3.14159,true") }) } func TestOttoCopy(t *testing.T) { tt(t, func() { vm0 := New() vm0.Run(` var abc = function() { return "Xyzzy"; }; function def() { return abc() + (0 + {}); } `) value, err := vm0.Run(` def(); `) is(err, nil) is(value, "Xyzzy0[object Object]") vm1 := vm0.Copy() value, err = vm1.Run(` def(); `) is(err, nil) is(value, "Xyzzy0[object Object]") vm1.Run(` abc = function() { return 3.14159; }; `) value, err = vm1.Run(` def(); `) is(err, nil) is(value, "3.141590[object Object]") value, err = vm0.Run(` def(); `) is(err, nil) is(value, "Xyzzy0[object Object]") { vm0 := New() vm0.Run(` var global = (function () {return this;}()) var abc = 0; var vm = "vm0"; var def = (function(){ var jkl = 0; var abc = function() { global.abc += 1; jkl += 1; return 1; }; return function() { return [ vm, global.abc, jkl, abc() ]; }; })(); `) value, err := vm0.Run(` def(); `) is(err, nil) is(value, "vm0,0,0,1") vm1 := vm0.Copy() vm1.Set("vm", "vm1") value, err = vm1.Run(` def(); `) is(err, nil) is(value, "vm1,1,1,1") value, err = vm0.Run(` def(); `) is(err, nil) is(value, "vm0,1,1,1") value, err = vm1.Run(` def(); `) is(err, nil) is(value, "vm1,2,2,1") } }) } func TestOttoCall_clone(t *testing.T) { tt(t, func() { vm := New().clone() rt := vm.runtime { // FIXME terst, Check how this comparison is done is(rt.global.Array.prototype, rt.global.FunctionPrototype) is(rt.global.ArrayPrototype, "!=", nil) is(rt.global.Array.runtime, rt) is(rt.global.Array.prototype.runtime, rt) is(rt.global.Array.get("prototype")._object().runtime, rt) } { value, err := vm.Run(`[ 1, 2, 3 ].toString()`) is(err, nil) is(value, "1,2,3") } { value, err := vm.Run(`[ 1, 2, 3 ]`) is(err, nil) is(value, "1,2,3") object := value._object() is(object, "!=", nil) is(object.prototype, rt.global.ArrayPrototype) value, err = vm.Run(`Array.prototype`) is(err, nil) object = value._object() is(object.runtime, rt) is(object, "!=", nil) is(object, rt.global.ArrayPrototype) } { otto1 := New() _, err := otto1.Run(` var abc = 1; var def = 2; `) is(err, nil) otto2 := otto1.clone() value, err := otto2.Run(`abc += 1; abc;`) is(err, nil) is(value, 2) value, err = otto1.Run(`abc += 4; abc;`) is(err, nil) is(value, 5) } { vm1 := New() _, err := vm1.Run(` var abc = 1; var def = function(value) { abc += value; return abc; } `) is(err, nil) vm2 := vm1.clone() value, err := vm2.Run(`def(1)`) is(err, nil) is(value, 2) value, err = vm1.Run(`def(4)`) is(err, nil) is(value, 5) } { vm1 := New() _, err := vm1.Run(` var abc = { ghi: 1, jkl: function(value) { this.ghi += value; return this.ghi; } }; var def = { abc: abc }; `) is(err, nil) otto2 := vm1.clone() value, err := otto2.Run(`def.abc.jkl(1)`) is(err, nil) is(value, 2) value, err = vm1.Run(`def.abc.jkl(4)`) is(err, nil) is(value, 5) } { vm1 := New() _, err := vm1.Run(` var abc = function() { return "abc"; }; var def = function() { return "def"; }; `) is(err, nil) vm2 := vm1.clone() value, err := vm2.Run(` [ abc.toString(), def.toString() ]; `) is(value, `function() { return "abc"; },function() { return "def"; }`) _, err = vm2.Run(` var def = function() { return "ghi"; }; `) is(err, nil) value, err = vm1.Run(` [ abc.toString(), def.toString() ]; `) is(value, `function() { return "abc"; },function() { return "def"; }`) value, err = vm2.Run(` [ abc.toString(), def.toString() ]; `) is(value, `function() { return "abc"; },function() { return "ghi"; }`) } }) } func TestOttoRun(t *testing.T) { tt(t, func() { vm := New() program, err := parser.ParseFile(nil, "", "", 0) is(err, nil) value, err := vm.Run(program) is(err, nil) is(value, UndefinedValue()) program, err = parser.ParseFile(nil, "", "2 + 2", 0) is(err, nil) value, err = vm.Run(program) is(err, nil) is(value, 4) value, err = vm.Run(program) is(err, nil) is(value, 4) program, err = parser.ParseFile(nil, "", "var abc; if (!abc) abc = 0; abc += 2; abc;", 0) value, err = vm.Run(program) is(err, nil) is(value, 2) value, err = vm.Run(program) is(err, nil) is(value, 4) value, err = vm.Run(program) is(err, nil) is(value, 6) { src := []byte("var abc; if (!abc) abc = 0; abc += 2; abc;") value, err = vm.Run(src) is(err, nil) is(value, 8) value, err = vm.Run(bytes.NewBuffer(src)) is(err, nil) is(value, 10) value, err = vm.Run(io.Reader(bytes.NewBuffer(src))) is(err, nil) is(value, 12) } { script, err := vm.Compile("", `var abc; if (!abc) abc = 0; abc += 2; abc;`) is(err, nil) value, err = vm.Run(script) is(err, nil) is(value, 14) value, err = vm.Run(script) is(err, nil) is(value, 16) is(script.String(), "// \nvar abc; if (!abc) abc = 0; abc += 2; abc;") } }) } // This generates functions to be used by the test below. The arguments are // `src`, which is something that otto can execute, and `expected`, which is // what the result of executing `src` should be. func makeTestOttoEvalFunction(src, expected interface{}) func(c FunctionCall) Value { return func(c FunctionCall) Value { v, err := c.Otto.Eval(src) is(err, nil) if err != nil { panic(err) } i, err := v.Export() is(err, nil) if err != nil { panic(err) } is(i, expected) return v } } func TestOttoEval(t *testing.T) { tt(t, func() { vm := New() vm.Set("x1", makeTestOttoEvalFunction(`a`, 1)) vm.Set("y1", makeTestOttoEvalFunction(`b`, "hello")) vm.Set("z1", makeTestOttoEvalFunction(`c`, true)) vm.Set("w", makeTestOttoEvalFunction(`a = 2; b = 'what'; c = false; null`, nil)) vm.Set("x2", makeTestOttoEvalFunction(`a`, 2)) vm.Set("y2", makeTestOttoEvalFunction(`b`, "what")) vm.Set("z2", makeTestOttoEvalFunction(`c`, false)) // note that these variables are defined in the scope of function `t`, // so would not usually be available to the functions called below. // // this is _not_ the recommended use case for `Eval` - instead it's // intended to be used in `debugger` handlers. this code here is the // equivalent of reading behind the current stack frame in C... // technically valid, but completely insane. // // makes for a good test case though. _, err := vm.Run(`(function t() { var a = 1; var b = 'hello'; var c = true; x1(); y1(); z1(); w(); x2(); y2(); z2(); }())`) is(err, nil) }) // this test makes sure that `Eval` doesn't explode if the VM doesn't have // a scope other than global defined. tt(t, func() { vm := New() _, err := vm.Eval("null") is(err, nil) vm.Set("a", 1) vm.Set("b", 2) v, err := vm.Eval("a + b") is(err, nil) r, err := v.Export() is(err, nil) is(r, 3) }) } func TestOttoContext(t *testing.T) { // These are all the builtin global scope symbols builtins := []string{ "escape", "URIError", "RegExp", "ReferenceError", "parseFloat", "parseInt", "SyntaxError", "decodeURIComponent", "encodeURIComponent", "Infinity", "JSON", "isNaN", "unescape", "decodeURI", "Object", "Function", "RangeError", "Error", "get_context", "eval", "Number", "Math", "NaN", "Date", "Boolean", "console", "encodeURI", "EvalError", "Array", "TypeError", "String", "isFinite", "undefined", } tt(t, func() { vm := New() vm.Set("get_context", func(c FunctionCall) Value { ctx := c.Otto.Context() is(ctx.Callee, "f1") is(ctx.Filename, "") is(ctx.Line, 8) is(ctx.Column, 5) is(ctx.Stacktrace, []string{ "f1 (:8:5)", "f2 (:15:5)", "f3 (:19:5)", "t (:22:4)", }) is(len(ctx.Symbols), 9+len(builtins)) is(ctx.Symbols["a"], 1) is(ctx.Symbols["b"], "hello") is(ctx.Symbols["c"], true) is(ctx.Symbols["j"], 2) is(ctx.Symbols["f1"].IsFunction(), true) is(ctx.Symbols["f2"].IsFunction(), true) is(ctx.Symbols["f3"].IsFunction(), true) is(ctx.Symbols["t"].IsFunction(), true) callee, _ := ctx.Symbols["arguments"].Object().Get("callee") is(callee.IsDefined(), true) return Value{} }) _, err := vm.Run(`(function t() { var a = 1; var b = 'hello'; var c = true; function f1() { var j = 2; get_context(); (function() { var d = 4; })() } function f2() { f1(); } function f3() { f2(); } f3(); a = 2; b = 'goodbye'; c = false; }())`) is(err, nil) }) // this test makes sure that `Context` works on global scope by default, if // there is not a current scope. tt(t, func() { vm := New() vm.Set("get_context", func(c FunctionCall) Value { ctx := c.Otto.Context() is(ctx.Callee, "") is(ctx.Filename, "") is(ctx.Line, 3) is(ctx.Column, 4) is(ctx.Stacktrace, []string{":3:4"}) is(len(ctx.Symbols), 2+len(builtins)) is(ctx.Symbols["a"], 1) is(ctx.Symbols["b"], UndefinedValue()) return Value{} }) _, err := vm.Run(` var a = 1; get_context() var b = 2; `) is(err, nil) }) // this test makes sure variables are shadowed correctly. tt(t, func() { vm := New() vm.Set("check_context", func(c FunctionCall) Value { n, err := c.Argument(0).ToInteger() is(err, nil) ctx := c.Otto.Context() is(ctx.Symbols["a"], n) return Value{} }) _, err := vm.Run(` var a = 1; check_context(1); (function() { var a = 2; check_context(2); }()); (function(a) { check_context(3); }(3)); (function(a) { check_context(4); }).call(null, 4); check_context(1); `) is(err, nil) }) } func Test_objectLength(t *testing.T) { tt(t, func() { _, vm := test() value := vm.Set("abc", []string{"jkl", "mno"}) is(objectLength(value._object()), 2) value, _ = vm.Run(`[1, 2, 3]`) is(objectLength(value._object()), 3) value, _ = vm.Run(`new String("abcdefghi")`) is(objectLength(value._object()), 9) value, _ = vm.Run(`"abcdefghi"`) is(objectLength(value._object()), 0) }) } func Test_stackLimit(t *testing.T) { // JavaScript stack depth before entering `a` is 5; becomes 6 after // entering. setting the maximum stack depth to 5 should result in an // error ocurring at that 5 -> 6 boundary. code := ` function a() {} function b() { a(); } function c() { b(); } function d() { c(); } function e() { d(); } e(); ` // has no error tt(t, func() { _, vm := test() _, err := vm.Run(code) is(err == nil, true) }) // has error tt(t, func() { _, vm := test() vm.vm.SetStackDepthLimit(2) _, err := vm.Run(code) is(err == nil, false) }) // has error tt(t, func() { _, vm := test() vm.vm.SetStackDepthLimit(5) _, err := vm.Run(code) is(err == nil, false) }) // has no error tt(t, func() { _, vm := test() vm.vm.SetStackDepthLimit(6) _, err := vm.Run(code) is(err == nil, true) }) // has no error tt(t, func() { _, vm := test() vm.vm.SetStackDepthLimit(1) vm.vm.SetStackDepthLimit(0) _, err := vm.Run(code) is(err == nil, true) }) } func BenchmarkNew(b *testing.B) { for i := 0; i < b.N; i++ { New() } } func BenchmarkClone(b *testing.B) { vm := New() b.ResetTimer() for i := 0; i < b.N; i++ { vm.clone() } } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/panic_test.go000066400000000000000000000020521331145703200250540ustar00rootroot00000000000000package otto import ( "testing" ) func Test_panic(t *testing.T) { tt(t, func() { test, _ := test() // Test that property.value is set to something if writable is set // to something test(` var abc = []; Object.defineProperty(abc, "0", { writable: false }); Object.defineProperty(abc, "0", { writable: false }); "0" in abc; `, true) test(`raise: var abc = []; Object.defineProperty(abc, "0", { writable: false }); Object.defineProperty(abc, "0", { value: false, writable: false }); `, "TypeError") // Test that a regular expression can contain \c0410 (CYRILLIC CAPITAL LETTER A) // without panicking test(` var abc = 0x0410; var def = String.fromCharCode(abc); new RegExp("\\c" + def).exec(def); `, "null") // Test transforming a transformable regular expression without a panic test(` new RegExp("\\u0000"); new RegExp("\\undefined").test("undefined"); `, true) }) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/parser/000077500000000000000000000000001331145703200236715ustar00rootroot00000000000000golang-github-robertkrimen-otto-0.0~git20180617.15f95af/parser/Makefile000066400000000000000000000000351331145703200253270ustar00rootroot00000000000000.PHONY: test test: go test golang-github-robertkrimen-otto-0.0~git20180617.15f95af/parser/README.markdown000066400000000000000000000102031331145703200263660ustar00rootroot00000000000000# parser -- import "github.com/robertkrimen/otto/parser" Package parser implements a parser for JavaScript. import ( "github.com/robertkrimen/otto/parser" ) Parse and return an AST filename := "" // A filename is optional src := ` // Sample xyzzy example (function(){ if (3.14159 > 0) { console.log("Hello, World."); return; } var xyzzy = NaN; console.log("Nothing happens."); return xyzzy; })(); ` // Parse some JavaScript, yielding a *ast.Program and/or an ErrorList program, err := parser.ParseFile(nil, filename, src, 0) ### Warning The parser and AST interfaces are still works-in-progress (particularly where node types are concerned) and may change in the future. ## Usage #### func ParseFile ```go func ParseFile(fileSet *file.FileSet, filename string, src interface{}, mode Mode) (*ast.Program, error) ``` ParseFile parses the source code of a single JavaScript/ECMAScript source file and returns the corresponding ast.Program node. If fileSet == nil, ParseFile parses source without a FileSet. If fileSet != nil, ParseFile first adds filename and src to fileSet. The filename argument is optional and is used for labelling errors, etc. src may be a string, a byte slice, a bytes.Buffer, or an io.Reader, but it MUST always be in UTF-8. // Parse some JavaScript, yielding a *ast.Program and/or an ErrorList program, err := parser.ParseFile(nil, "", `if (abc > 1) {}`, 0) #### func ParseFunction ```go func ParseFunction(parameterList, body string) (*ast.FunctionLiteral, error) ``` ParseFunction parses a given parameter list and body as a function and returns the corresponding ast.FunctionLiteral node. The parameter list, if any, should be a comma-separated list of identifiers. #### func ReadSource ```go func ReadSource(filename string, src interface{}) ([]byte, error) ``` #### func TransformRegExp ```go func TransformRegExp(pattern string) (string, error) ``` TransformRegExp transforms a JavaScript pattern into a Go "regexp" pattern. re2 (Go) cannot do backtracking, so the presence of a lookahead (?=) (?!) or backreference (\1, \2, ...) will cause an error. re2 (Go) has a different definition for \s: [\t\n\f\r ]. The JavaScript definition, on the other hand, also includes \v, Unicode "Separator, Space", etc. If the pattern is invalid (not valid even in JavaScript), then this function returns the empty string and an error. If the pattern is valid, but incompatible (contains a lookahead or backreference), then this function returns the transformation (a non-empty string) AND an error. #### type Error ```go type Error struct { Position file.Position Message string } ``` An Error represents a parsing error. It includes the position where the error occurred and a message/description. #### func (Error) Error ```go func (self Error) Error() string ``` #### type ErrorList ```go type ErrorList []*Error ``` ErrorList is a list of *Errors. #### func (*ErrorList) Add ```go func (self *ErrorList) Add(position file.Position, msg string) ``` Add adds an Error with given position and message to an ErrorList. #### func (ErrorList) Err ```go func (self ErrorList) Err() error ``` Err returns an error equivalent to this ErrorList. If the list is empty, Err returns nil. #### func (ErrorList) Error ```go func (self ErrorList) Error() string ``` Error implements the Error interface. #### func (ErrorList) Len ```go func (self ErrorList) Len() int ``` #### func (ErrorList) Less ```go func (self ErrorList) Less(i, j int) bool ``` #### func (*ErrorList) Reset ```go func (self *ErrorList) Reset() ``` Reset resets an ErrorList to no errors. #### func (ErrorList) Sort ```go func (self ErrorList) Sort() ``` #### func (ErrorList) Swap ```go func (self ErrorList) Swap(i, j int) ``` #### type Mode ```go type Mode uint ``` A Mode value is a set of flags (or 0). They control optional parser functionality. ```go const ( IgnoreRegExpErrors Mode = 1 << iota // Ignore RegExp compatibility errors (allow backtracking) ) ``` -- **godocdown** http://github.com/robertkrimen/godocdown golang-github-robertkrimen-otto-0.0~git20180617.15f95af/parser/comments_test.go000066400000000000000000001407441331145703200271160ustar00rootroot00000000000000package parser import ( "fmt" "github.com/robertkrimen/otto/ast" "reflect" "testing" ) func checkComments(actual []*ast.Comment, expected []string, position ast.CommentPosition) error { var comments []*ast.Comment for _, c := range actual { if c.Position == position { comments = append(comments, c) } } if len(comments) != len(expected) { return fmt.Errorf("the number of comments is not correct. %v != %v", len(comments), len(expected)) } for i, v := range comments { if v.Text != expected[i] { return fmt.Errorf("comments do not match. \"%v\" != \"%v\"\n", v.Text, expected[i]) } if v.Position != position { return fmt.Errorf("the comment is not %v, was %v\n", position, v.Position) } } return nil } func displayStatements(statements []ast.Statement) { fmt.Printf("Printing statements (%v)\n", len(statements)) for k, v := range statements { fmt.Printf("Type of line %v: %v\n", k, reflect.TypeOf(v)) } } func displayComments(m ast.CommentMap) { fmt.Printf("Displaying comments:\n") for n, comments := range m { fmt.Printf("%v %v:\n", reflect.TypeOf(n), n) for i, comment := range comments { fmt.Printf(" [%v] %v @ %v\n", i, comment.Text, comment.Position) } } } func TestParser_comments(t *testing.T) { tt(t, func() { test := func(source string, chk interface{}) (*_parser, *ast.Program) { parser, program, err := testParseWithMode(source, StoreComments) is(firstErr(err), chk) // Check unresolved comments //is(len(parser.comments), 0) return parser, program } var err error var parser *_parser var program *ast.Program parser, program = test("q=2;// Hej\nv = 0", nil) is(len(program.Body), 2) err = checkComments((parser.comments.CommentMap)[program.Body[1]], []string{" Hej"}, ast.LEADING) is(err, nil) // Assignment parser, program = test("i = /*test=*/ 2", nil) is(len(program.Body), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0]], []string{}, ast.LEADING), nil) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.AssignExpression).Right], []string{"test="}, ast.LEADING), nil) is(parser.comments.CommentMap.Size(), 1) // Conditional, before consequent parser, program = test("i ? /*test?*/ 2 : 3", nil) is(len(program.Body), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0]], []string{}, ast.LEADING), nil) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.ConditionalExpression).Consequent], []string{"test?"}, ast.LEADING), nil) is(parser.comments.CommentMap.Size(), 1) // Conditional, after consequent parser, program = test("i ? 2 /*test?*/ : 3", nil) is(len(program.Body), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0]], []string{}, ast.LEADING), nil) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.ConditionalExpression).Consequent], []string{"test?"}, ast.TRAILING), nil) is(parser.comments.CommentMap.Size(), 1) // Conditional, before alternate parser, program = test("i ? 2 : /*test:*/ 3", nil) is(len(program.Body), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0]], []string{}, ast.LEADING), nil) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.ConditionalExpression).Alternate], []string{"test:"}, ast.LEADING), nil) is(parser.comments.CommentMap.Size(), 1) // Logical OR parser, program = test("i || /*test||*/ 2", nil) is(len(program.Body), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0]], []string{}, ast.LEADING), nil) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.BinaryExpression).Right], []string{"test||"}, ast.LEADING), nil) is(parser.comments.CommentMap.Size(), 1) // Logical AND parser, program = test("i && /*test&&*/ 2", nil) is(len(program.Body), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0]], []string{}, ast.LEADING), nil) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.BinaryExpression).Right], []string{"test&&"}, ast.LEADING), nil) is(parser.comments.CommentMap.Size(), 1) // Bitwise OR parser, program = test("i | /*test|*/ 2", nil) is(len(program.Body), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0]], []string{}, ast.LEADING), nil) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.BinaryExpression).Right], []string{"test|"}, ast.LEADING), nil) is(parser.comments.CommentMap.Size(), 1) // Exclusive OR parser, program = test("i ^ /*test^*/ 2", nil) is(len(program.Body), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0]], []string{}, ast.LEADING), nil) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.BinaryExpression).Right], []string{"test^"}, ast.LEADING), nil) is(parser.comments.CommentMap.Size(), 1) // Bitwise AND parser, program = test("i & /*test&*/ 2", nil) is(len(program.Body), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0]], []string{}, ast.LEADING), nil) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.BinaryExpression).Right], []string{"test&"}, ast.LEADING), nil) is(parser.comments.CommentMap.Size(), 1) // Equality parser, program = test("i == /*test==*/ 2", nil) is(len(program.Body), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0]], []string{}, ast.LEADING), nil) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.BinaryExpression).Right], []string{"test=="}, ast.LEADING), nil) is(parser.comments.CommentMap.Size(), 1) // Relational, < parser, program = test("i < /*test<*/ 2", nil) is(len(program.Body), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0]], []string{}, ast.LEADING), nil) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.BinaryExpression).Right], []string{"test<"}, ast.LEADING), nil) is(parser.comments.CommentMap.Size(), 1) // Relational, instanceof parser, program = test("i instanceof /*testinstanceof*/ thing", nil) is(len(program.Body), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0]], []string{}, ast.LEADING), nil) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.BinaryExpression).Right], []string{"testinstanceof"}, ast.LEADING), nil) is(parser.comments.CommentMap.Size(), 1) // Shift left parser, program = test("i << /*test<<*/ 2", nil) is(len(program.Body), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0]], []string{}, ast.LEADING), nil) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.BinaryExpression).Right], []string{"test<<"}, ast.LEADING), nil) is(parser.comments.CommentMap.Size(), 1) // + parser, program = test("i + /*test+*/ 2", nil) is(len(program.Body), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0]], []string{}, ast.LEADING), nil) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.BinaryExpression).Right], []string{"test+"}, ast.LEADING), nil) is(parser.comments.CommentMap.Size(), 1) // * parser, program = test("i * /*test**/ 2", nil) is(len(program.Body), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0]], []string{}, ast.LEADING), nil) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.BinaryExpression).Right], []string{"test*"}, ast.LEADING), nil) is(parser.comments.CommentMap.Size(), 1) // Unary prefix, ++ parser, program = test("++/*test++*/i", nil) is(len(program.Body), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0]], []string{}, ast.LEADING), nil) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.UnaryExpression).Operand], []string{"test++"}, ast.LEADING), nil) is(parser.comments.CommentMap.Size(), 1) // Unary prefix, delete parser, program = test("delete /*testdelete*/ i", nil) is(len(program.Body), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0]], []string{}, ast.LEADING), nil) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.UnaryExpression).Operand], []string{"testdelete"}, ast.LEADING), nil) is(parser.comments.CommentMap.Size(), 1) // Unary postfix, ++ parser, program = test("i/*test++*/++", nil) is(len(program.Body), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0]], []string{}, ast.LEADING), nil) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.UnaryExpression).Operand], []string{"test++"}, ast.TRAILING), nil) is(parser.comments.CommentMap.Size(), 1) // + pt 2 parser, program = test("i /*test+*/ + 2", nil) is(len(program.Body), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0]], []string{}, ast.LEADING), nil) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.BinaryExpression).Left], []string{"test+"}, ast.TRAILING), nil) is(parser.comments.CommentMap.Size(), 1) // Multiple comments for a single node parser, program = test("i /*test+*/ /*test+2*/ + 2", nil) is(len(program.Body), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0]], []string{}, ast.LEADING), nil) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.BinaryExpression).Left], []string{"test+", "test+2"}, ast.TRAILING), nil) is(parser.comments.CommentMap.Size(), 2) // Multiple comments for multiple nodes parser, program = test("i /*test1*/ + 2 /*test2*/ + a /*test3*/ * x /*test4*/", nil) is(len(program.Body), 1) is(parser.comments.CommentMap.Size(), 4) // Leading comment parser, program = test("/*leadingtest*/i + 2", nil) is(len(program.Body), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement)], []string{"leadingtest"}, ast.LEADING), nil) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.BinaryExpression).Left], []string{}, ast.TRAILING), nil) is(parser.comments.CommentMap.Size(), 1) // Leading comment, with semicolon parser, program = test("/*leadingtest;*/;i + 2", nil) is(len(program.Body), 2) is(checkComments((parser.comments.CommentMap)[program.Body[1]], []string{"leadingtest;"}, ast.LEADING), nil) is(parser.comments.CommentMap.Size(), 1) // Arrays parser, program = test("[1, 2 /*test2*/, 3]", nil) is(len(program.Body), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.ArrayLiteral).Value[1]], []string{"test2"}, ast.TRAILING), nil) is(parser.comments.CommentMap.Size(), 1) // Function calls parser, program = test("fun(a,b) //test", nil) is(len(program.Body), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.CallExpression)], []string{"test"}, ast.TRAILING), nil) is(parser.comments.CommentMap.Size(), 1) // Function calls, pt 2 parser, program = test("fun(a/*test1*/,b)", nil) is(len(program.Body), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.CallExpression).ArgumentList[0]], []string{"test1"}, ast.TRAILING), nil) is(parser.comments.CommentMap.Size(), 1) // Function calls, pt 3 parser, program = test("fun(/*test1*/a,b)", nil) is(len(program.Body), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.CallExpression).ArgumentList[0]], []string{"test1"}, ast.LEADING), nil) is(parser.comments.CommentMap.Size(), 1) // Arrays pt 2 parser, program = test(`["abc".substr(0,1)/*testa*/, "abc.substr(0,2)"];`, nil) is(len(program.Body), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.ArrayLiteral).Value[0]], []string{"testa"}, ast.TRAILING), nil) is(parser.comments.CommentMap.Size(), 1) // Arrays pt 3 parser, program = test(`[a, //test b];`, nil) is(len(program.Body), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.ArrayLiteral).Value[1]], []string{"test"}, ast.LEADING), nil) is(parser.comments.CommentMap.Size(), 1) // Arrays pt 4 parser, program = test(`[a, //test b, c];`, nil) is(len(program.Body), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.ArrayLiteral).Value[1]], []string{"test"}, ast.LEADING), nil) is(parser.comments.CommentMap.Size(), 1) // Arrays pt 5 parser, program = test(` [ "a1", // "a" "a2", // "ab" ]; `, nil) is(parser.comments.CommentMap.Size(), 2) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.ArrayLiteral).Value[1]], []string{" \"a\""}, ast.LEADING), nil) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.ArrayLiteral)], []string{" \"ab\""}, ast.FINAL), nil) // Arrays pt 6 parser, program = test(`[a, /*test*/ b, c];`, nil) is(len(program.Body), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.ArrayLiteral).Value[1]], []string{"test"}, ast.LEADING), nil) is(parser.comments.CommentMap.Size(), 1) // Arrays pt 7 - Empty node parser, program = test(`[a,,/*test2*/,];`, nil) is(len(program.Body), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.ArrayLiteral).Value[2]], []string{"test2"}, ast.TRAILING), nil) is(parser.comments.CommentMap.Size(), 1) // Arrays pt 8 - Trailing node parser, program = test(`[a,,,/*test2*/];`, nil) is(len(program.Body), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.ArrayLiteral)], []string{"test2"}, ast.FINAL), nil) is(parser.comments.CommentMap.Size(), 1) // Arrays pt 9 - Leading node parser, program = test(`[/*test2*/a,,,];`, nil) is(len(program.Body), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.ArrayLiteral).Value[0]], []string{"test2"}, ast.LEADING), nil) is(parser.comments.CommentMap.Size(), 1) // Object literal parser, program = test("obj = {a: 1, b: 2 /*test2*/, c: 3}", nil) is(len(program.Body), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.AssignExpression).Right.(*ast.ObjectLiteral).Value[1].Value], []string{"test2"}, ast.TRAILING), nil) is(parser.comments.CommentMap.Size(), 1) // Object literal, pt 2 parser, program = test("obj = {/*test2*/a: 1, b: 2, c: 3}", nil) is(len(program.Body), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.AssignExpression).Right.(*ast.ObjectLiteral).Value[0].Value], []string{"test2"}, ast.KEY), nil) is(parser.comments.CommentMap.Size(), 1) // Object literal, pt 3 parser, program = test("obj = {x/*test2*/: 1, y: 2, z: 3}", nil) is(len(program.Body), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.AssignExpression).Right.(*ast.ObjectLiteral).Value[0].Value], []string{"test2"}, ast.COLON), nil) is(parser.comments.CommentMap.Size(), 1) // Object literal, pt 4 parser, program = test("obj = {x: /*test2*/1, y: 2, z: 3}", nil) is(len(program.Body), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.AssignExpression).Right.(*ast.ObjectLiteral).Value[0].Value], []string{"test2"}, ast.LEADING), nil) is(parser.comments.CommentMap.Size(), 1) // Object literal, pt 5 parser, program = test("obj = {x: 1/*test2*/, y: 2, z: 3}", nil) is(len(program.Body), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.AssignExpression).Right.(*ast.ObjectLiteral).Value[0].Value], []string{"test2"}, ast.TRAILING), nil) is(parser.comments.CommentMap.Size(), 1) // Object literal, pt 6 parser, program = test("obj = {x: 1, y: 2, z: 3/*test2*/}", nil) is(len(program.Body), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.AssignExpression).Right.(*ast.ObjectLiteral).Value[2].Value], []string{"test2"}, ast.TRAILING), nil) is(parser.comments.CommentMap.Size(), 1) // Object literal, pt 7 - trailing comment parser, program = test("obj = {x: 1, y: 2, z: 3,/*test2*/}", nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.AssignExpression).Right.(*ast.ObjectLiteral)], []string{"test2"}, ast.FINAL), nil) // Line breaks parser, program = test(` t1 = "BLA DE VLA" /*Test*/ t2 = "Nothing happens." `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[1]], []string{"Test"}, ast.LEADING), nil) // Line breaks pt 2 parser, program = test(` t1 = "BLA DE VLA" /*Test*/ t2 = "Nothing happens." `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.AssignExpression).Right.(*ast.StringLiteral)], []string{"Test"}, ast.TRAILING), nil) is(checkComments((parser.comments.CommentMap)[program.Body[1].(*ast.ExpressionStatement)], []string{}, ast.LEADING), nil) // Line breaks pt 3 parser, program = test(` t1 = "BLA DE VLA" /*Test*/ /*Test2*/ t2 = "Nothing happens." `, nil) is(parser.comments.CommentMap.Size(), 2) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.AssignExpression).Right.(*ast.StringLiteral)], []string{"Test", "Test2"}, ast.TRAILING), nil) is(checkComments((parser.comments.CommentMap)[program.Body[1].(*ast.ExpressionStatement)], []string{}, ast.LEADING), nil) // Line breaks pt 4 parser, program = test(` t1 = "BLA DE VLA" /*Test*/ /*Test2*/ t2 = "Nothing happens." `, nil) is(parser.comments.CommentMap.Size(), 2) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.AssignExpression).Right.(*ast.StringLiteral)], []string{"Test"}, ast.TRAILING), nil) is(checkComments((parser.comments.CommentMap)[program.Body[1]], []string{"Test2"}, ast.LEADING), nil) // Line breaks pt 5 parser, program = test(` t1 = "BLA DE VLA"; /*Test*/ t2 = "Nothing happens." `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[1]], []string{"Test"}, ast.LEADING), nil) // Line breaks pt 6 parser, program = test(` t1 = "BLA DE VLA"; /*Test*/ /*Test2*/ t2 = "Nothing happens." `, nil) is(parser.comments.CommentMap.Size(), 2) is(checkComments((parser.comments.CommentMap)[program.Body[1]], []string{"Test", "Test2"}, ast.LEADING), nil) // Misc parser, program = test(` var x = Object.create({y: { }, // a }); `, nil) is(parser.comments.CommentMap.Size(), 1) // Misc 2 parser, program = test(` var x = Object.create({y: { }, // a // b a: 2}); `, nil) is(parser.comments.CommentMap.Size(), 2) // Statement blocks parser, program = test(` (function() { // Baseline setup }) `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.FunctionLiteral).Body], []string{" Baseline setup"}, ast.FINAL), nil) // Switches parser, program = test(` switch (switcha) { // switch comment case "switchb": a } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.SwitchStatement).Body[0]], []string{" switch comment"}, ast.LEADING), nil) // Switches pt 2 parser, program = test(` switch (switcha) { case /*switch comment*/ "switchb": a } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.SwitchStatement).Body[0].Test], []string{"switch comment"}, ast.LEADING), nil) // Switches pt 3 parser, program = test(` switch (switcha) { case "switchb" /*switch comment*/: a } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.SwitchStatement).Body[0].Test], []string{"switch comment"}, ast.TRAILING), nil) // Switches pt 4 parser, program = test(` switch (switcha) { case "switchb": /*switch comment*/ a } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.SwitchStatement).Body[0].Consequent[0]], []string{"switch comment"}, ast.LEADING), nil) // Switches pt 5 - default parser, program = test(` switch (switcha) { default: /*switch comment*/ a } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.SwitchStatement).Body[0].Consequent[0]], []string{"switch comment"}, ast.LEADING), nil) // Switches pt 6 parser, program = test(` switch (switcha) { case "switchb": /*switch comment*/a } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.SwitchStatement).Body[0].Consequent[0]], []string{"switch comment"}, ast.LEADING), nil) // Switches pt 7 parser, program = test(` switch (switcha) { case "switchb": /*switch comment*/ { a } } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.SwitchStatement).Body[0].Consequent[0]], []string{"switch comment"}, ast.LEADING), nil) // Switches pt 8 parser, program = test(` switch (switcha) { case "switchb": { a }/*switch comment*/ } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.SwitchStatement).Body[0].Consequent[0]], []string{"switch comment"}, ast.TRAILING), nil) // Switches pt 9 parser, program = test(` switch (switcha) { case "switchb": /*switch comment*/ { a } } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.SwitchStatement).Body[0].Consequent[0]], []string{"switch comment"}, ast.LEADING), nil) // Switches pt 10 parser, program = test(` switch (switcha) { case "switchb": { /*switch comment*/a } } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.SwitchStatement).Body[0].Consequent[0].(*ast.BlockStatement).List[0]], []string{"switch comment"}, ast.LEADING), nil) // For loops parser, program = test(` for(/*comment*/i = 0 ; i < 1 ; i++) { a } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ForStatement).Initializer.(*ast.SequenceExpression).Sequence[0].(*ast.AssignExpression).Left], []string{"comment"}, ast.LEADING), nil) // For loops pt 2 parser, program = test(` for(i/*comment*/ = 0 ; i < 1 ; i++) { a } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ForStatement).Initializer.(*ast.SequenceExpression).Sequence[0].(*ast.AssignExpression).Left], []string{"comment"}, ast.TRAILING), nil) // For loops pt 3 parser, program = test(` for(i = 0 ; /*comment*/i < 1 ; i++) { a } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ForStatement).Test.(*ast.BinaryExpression).Left], []string{"comment"}, ast.LEADING), nil) // For loops pt 4 parser, program = test(` for(i = 0 ;i /*comment*/ < 1 ; i++) { a } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ForStatement).Test.(*ast.BinaryExpression).Left], []string{"comment"}, ast.TRAILING), nil) // For loops pt 5 parser, program = test(` for(i = 0 ;i < 1 /*comment*/ ; i++) { a } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ForStatement).Test.(*ast.BinaryExpression).Right], []string{"comment"}, ast.TRAILING), nil) // For loops pt 6 parser, program = test(` for(i = 0 ;i < 1 ; /*comment*/ i++) { a } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ForStatement).Update.(*ast.UnaryExpression).Operand], []string{"comment"}, ast.LEADING), nil) // For loops pt 7 parser, program = test(` for(i = 0 ;i < 1 ; i++) /*comment*/ { a } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ForStatement).Body], []string{"comment"}, ast.LEADING), nil) // For loops pt 8 parser, program = test(` for(i = 0 ;i < 1 ; i++) { a }/*comment*/ `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ForStatement).Body], []string{"comment"}, ast.TRAILING), nil) // For loops pt 9 parser, program = test(` for(i = 0 ;i < 1 ; /*comment*/i++) { a } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ForStatement).Update.(*ast.UnaryExpression).Operand], []string{"comment"}, ast.LEADING), nil) // For loops pt 10 parser, program = test(` for(i = 0 ;i < 1 ; i/*comment*/++) { a } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ForStatement).Update.(*ast.UnaryExpression).Operand.(*ast.Identifier)], []string{"comment"}, ast.TRAILING), nil) // For loops pt 11 parser, program = test(` for(i = 0 ;i < 1 ; i++/*comment*/) { a } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ForStatement).Update.(*ast.UnaryExpression)], []string{"comment"}, ast.TRAILING), nil) // ForIn parser, program = test(` for(/*comment*/var i = 0 in obj) { a } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ForInStatement).Into], []string{"comment"}, ast.LEADING), nil) // ForIn pt 2 parser, program = test(` for(var i = 0 /*comment*/in obj) { a } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ForInStatement).Into.(*ast.VariableExpression).Initializer], []string{"comment"}, ast.TRAILING), nil) // ForIn pt 3 parser, program = test(` for(var i = 0 in /*comment*/ obj) { a } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ForInStatement).Source], []string{"comment"}, ast.LEADING), nil) // ForIn pt 4 parser, program = test(` for(var i = 0 in obj/*comment*/) { a } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ForInStatement).Source], []string{"comment"}, ast.TRAILING), nil) // ForIn pt 5 parser, program = test(` for(var i = 0 in obj) /*comment*/ { a } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ForInStatement).Body], []string{"comment"}, ast.LEADING), nil) // ForIn pt 6 parser, program = test(` for(var i = 0 in obj) { a }/*comment*/ `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ForInStatement).Body], []string{"comment"}, ast.TRAILING), nil) // ForIn pt 7 parser, program = test(` for(var i = 0 in obj) { a } // comment `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program], []string{" comment"}, ast.TRAILING), nil) // ForIn pt 8 parser, program = test(` for(var i = 0 in obj) { a } // comment c `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[1]], []string{" comment"}, ast.LEADING), nil) // Block parser, program = test(` /*comment*/{ a } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.BlockStatement)], []string{"comment"}, ast.LEADING), nil) // Block pt 2 parser, program = test(` { a }/*comment*/ `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.BlockStatement)], []string{"comment"}, ast.TRAILING), nil) // If then else parser, program = test(` /*comment*/ if(a) { b } else { c } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.IfStatement)], []string{"comment"}, ast.LEADING), nil) // If then else pt 2 parser, program = test(` if/*comment*/(a) { b } else { c } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.IfStatement)], []string{"comment"}, ast.IF), nil) // If then else pt 3 parser, program = test(` if(/*comment*/a) { b } else { c } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.IfStatement).Test], []string{"comment"}, ast.LEADING), nil) // If then else pt 4 parser, program = test(` if(a/*comment*/) { b } else { c } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.IfStatement).Test], []string{"comment"}, ast.TRAILING), nil) // If then else pt 4 parser, program = test(` if(a)/*comment*/ { b } else { c } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.IfStatement).Consequent], []string{"comment"}, ast.LEADING), nil) // If then else pt 5 parser, program = test(` if(a) { b } /*comment*/else { c } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.IfStatement).Consequent], []string{"comment"}, ast.TRAILING), nil) // If then else pt 6 parser, program = test(` if(a) { b } else/*comment*/ { c } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.IfStatement).Alternate], []string{"comment"}, ast.LEADING), nil) // If then else pt 7 parser, program = test(` if(a) { b } else { c }/*comment*/ `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.IfStatement).Alternate], []string{"comment"}, ast.TRAILING), nil) // If then else pt 8 parser, program = test(` if /*comment*/ (a) { b } else { c } `, nil) is(parser.comments.CommentMap.Size(), 1) // If then else pt 9 parser, program = test(` if (a) /*comment*/{ b } else { c } `, nil) is(parser.comments.CommentMap.Size(), 1) // If then else pt 10 parser, program = test(` if(a){ b } /*comment*/ else { c } `, nil) is(parser.comments.CommentMap.Size(), 1) // Do while parser, program = test(` /*comment*/do { a } while(b) `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.DoWhileStatement)], []string{"comment"}, ast.LEADING), nil) // Do while pt 2 parser, program = test(` do /*comment*/ { a } while(b) `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.DoWhileStatement)], []string{"comment"}, ast.DO), nil) // Do while pt 3 parser, program = test(` do { a } /*comment*/ while(b) `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.DoWhileStatement).Body], []string{"comment"}, ast.TRAILING), nil) // Do while pt 4 parser, program = test(` do { a } while/*comment*/(b) `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.DoWhileStatement)], []string{"comment"}, ast.WHILE), nil) // Do while pt 5 parser, program = test(` do { a } while(b)/*comment*/ `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.DoWhileStatement)], []string{"comment"}, ast.TRAILING), nil) // While parser, program = test(` /*comment*/while(a) { b } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.WhileStatement)], []string{"comment"}, ast.LEADING), nil) // While pt 2 parser, program = test(` while/*comment*/(a) { b } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.WhileStatement)], []string{"comment"}, ast.WHILE), nil) // While pt 3 parser, program = test(` while(/*comment*/a) { b } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.WhileStatement).Test], []string{"comment"}, ast.LEADING), nil) // While pt 4 parser, program = test(` while(a/*comment*/) { b } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.WhileStatement).Test], []string{"comment"}, ast.TRAILING), nil) // While pt 5 parser, program = test(` while(a) /*comment*/ { c } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.WhileStatement).Body], []string{"comment"}, ast.LEADING), nil) // While pt 6 parser, program = test(` while(a) { c }/*comment*/ `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.WhileStatement).Body], []string{"comment"}, ast.TRAILING), nil) // While pt 7 parser, program = test(` while(a) { c/*comment*/ } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.WhileStatement).Body.(*ast.BlockStatement).List[0].(*ast.ExpressionStatement).Expression.(*ast.Identifier)], []string{"comment"}, ast.TRAILING), nil) // While pt 7 parser, program = test(` while(a) { /*comment*/ } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.WhileStatement).Body.(*ast.BlockStatement)], []string{"comment"}, ast.FINAL), nil) // While pt 8 parser, program = test(` while /*comment*/(a) { } `, nil) is(parser.comments.CommentMap.Size(), 1) // While pt 9 parser, program = test(` while (a) /*comment*/{ } `, nil) is(parser.comments.CommentMap.Size(), 1) // Break parser, program = test(` while(a) { break/*comment*/; } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.WhileStatement).Body.(*ast.BlockStatement).List[0].(*ast.BranchStatement)], []string{"comment"}, ast.TRAILING), nil) // Break pt 2 parser, program = test(` while(a) { next/*comment*/: break next; } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.WhileStatement).Body.(*ast.BlockStatement).List[0].(*ast.LabelledStatement).Label], []string{"comment"}, ast.TRAILING), nil) // Break pt 3 parser, program = test(` while(a) { next:/*comment*/ break next; } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.WhileStatement).Body.(*ast.BlockStatement).List[0].(*ast.LabelledStatement)], []string{"comment"}, ast.LEADING), nil) // Break pt 4 parser, program = test(` while(a) { next: break /*comment*/next; } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.WhileStatement).Body.(*ast.BlockStatement).List[0].(*ast.LabelledStatement).Statement.(*ast.BranchStatement).Label], []string{"comment"}, ast.LEADING), nil) // Break pt 5 parser, program = test(` while(a) { next: break next/*comment*/; } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.WhileStatement).Body.(*ast.BlockStatement).List[0].(*ast.LabelledStatement).Statement.(*ast.BranchStatement).Label], []string{"comment"}, ast.TRAILING), nil) // Debugger parser, program = test(` debugger // comment `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.DebuggerStatement)], []string{" comment"}, ast.TRAILING), nil) // Debugger pt 2 parser, program = test(` debugger; // comment `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program], []string{" comment"}, ast.TRAILING), nil) // Debugger pt 3 parser, program = test(` debugger; // comment `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program], []string{" comment"}, ast.TRAILING), nil) // With parser, program = test(` /*comment*/with(a) { } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.WithStatement)], []string{"comment"}, ast.LEADING), nil) // With pt 2 parser, program = test(` with/*comment*/(a) { } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.WithStatement)], []string{"comment"}, ast.WITH), nil) // With pt 3 parser, program = test(` with(/*comment*/a) { } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.WithStatement).Object], []string{"comment"}, ast.LEADING), nil) // With pt 4 parser, program = test(` with(a/*comment*/) { } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.WithStatement).Object], []string{"comment"}, ast.TRAILING), nil) // With pt 5 parser, program = test(` with(a) /*comment*/ { } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.WithStatement).Body], []string{"comment"}, ast.LEADING), nil) // With pt 6 parser, program = test(` with(a) { }/*comment*/ `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.WithStatement).Body], []string{"comment"}, ast.TRAILING), nil) // With pt 7 parser, program = test(` with /*comment*/(a) { } `, nil) is(parser.comments.CommentMap.Size(), 1) // With pt 8 parser, program = test(` with (a) /*comment*/{ } `, nil) is(parser.comments.CommentMap.Size(), 1) // Var parser, program = test(` /*comment*/var a `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.VariableStatement)], []string{"comment"}, ast.LEADING), nil) // Var pt 2 parser, program = test(` var/*comment*/ a `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.VariableStatement).List[0]], []string{"comment"}, ast.LEADING), nil) // Var pt 3 parser, program = test(` var a/*comment*/ `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.VariableStatement).List[0]], []string{"comment"}, ast.TRAILING), nil) // Var pt 4 parser, program = test(` var a/*comment*/, b `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.VariableStatement).List[0].(*ast.VariableExpression)], []string{"comment"}, ast.TRAILING), nil) // Var pt 5 parser, program = test(` var a, /*comment*/b `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.VariableStatement).List[1].(*ast.VariableExpression)], []string{"comment"}, ast.LEADING), nil) // Var pt 6 parser, program = test(` var a, b/*comment*/ `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.VariableStatement).List[1]], []string{"comment"}, ast.TRAILING), nil) // Var pt 7 parser, program = test(` var a, b; /*comment*/ `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program], []string{"comment"}, ast.TRAILING), nil) // Return parser, program = test(` function f() { /*comment*/return o } `, nil) is(parser.comments.CommentMap.Size(), 1) // Try catch parser, program = test(` /*comment*/try { a } catch(b) { c } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.TryStatement)], []string{"comment"}, ast.LEADING), nil) // Try catch pt 2 parser, program = test(` try/*comment*/ { a } catch(b) { c } finally { } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.TryStatement).Body], []string{"comment"}, ast.LEADING), nil) // Try catch pt 3 parser, program = test(` try { a }/*comment*/ catch(b) { c } finally { } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.TryStatement).Body], []string{"comment"}, ast.TRAILING), nil) // Try catch pt 4 parser, program = test(` try { a } catch(/*comment*/b) { c } finally { } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.TryStatement).Catch.Parameter], []string{"comment"}, ast.LEADING), nil) // Try catch pt 5 parser, program = test(` try { a } catch(b/*comment*/) { c } finally { } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.TryStatement).Catch.Parameter], []string{"comment"}, ast.TRAILING), nil) // Try catch pt 6 parser, program = test(` try { a } catch(b) /*comment*/{ c } finally { } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.TryStatement).Catch.Body], []string{"comment"}, ast.LEADING), nil) // Try catch pt 7 parser, program = test(` try { a } catch(b){ c } /*comment*/ finally { } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.TryStatement).Catch.Body], []string{"comment"}, ast.TRAILING), nil) // Try catch pt 8 parser, program = test(` try { a } catch(b){ c } finally /*comment*/ { } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.TryStatement).Finally], []string{"comment"}, ast.LEADING), nil) // Try catch pt 9 parser, program = test(` try { a } catch(b){ c } finally { }/*comment*/ `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.TryStatement).Finally], []string{"comment"}, ast.TRAILING), nil) // Try catch pt 11 parser, program = test(` try { a } /*comment*/ catch(b){ c } finally { d } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.TryStatement).Body], []string{"comment"}, ast.TRAILING), nil) // Throw parser, program = test(` throw a/*comment*/ `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ThrowStatement).Argument], []string{"comment"}, ast.TRAILING), nil) // Throw pt 2 parser, program = test(` /*comment*/throw a `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ThrowStatement)], []string{"comment"}, ast.LEADING), nil) // Throw pt 3 parser, program = test(` throw /*comment*/a `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ThrowStatement).Argument], []string{"comment"}, ast.LEADING), nil) // Try catch pt 10 parser, program = test(` try { a } catch(b){ c } /*comment*/finally { } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.TryStatement).Catch.Body], []string{"comment"}, ast.TRAILING), nil) // Try catch pt 11 parser, program = test(` try { a } catch(b){ c } finally /*comment*/ { d } `, nil) is(parser.comments.CommentMap.Size(), 1) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.TryStatement).Finally], []string{"comment"}, ast.LEADING), nil) // Switch / comment parser, program = test(` var volvo = 1 //comment switch(abra) { } `, nil) is(parser.comments.CommentMap.Size(), 1) // Switch / comment parser, program = test(` f("string",{ key: "val" //comment }); `, nil) is(parser.comments.CommentMap.Size(), 1) // Switch / comment parser, program = test(` function f() { /*comment*/if(true){a++} } `, nil) is(parser.comments.CommentMap.Size(), 1) n := program.Body[0].(*ast.FunctionStatement).Function.Body.(*ast.BlockStatement).List[0] is(checkComments((parser.comments.CommentMap)[n], []string{"comment"}, ast.LEADING), nil) // Function in function parser, program = test(` function f() { /*comment*/function f2() { } } `, nil) is(parser.comments.CommentMap.Size(), 1) n = program.Body[0].(*ast.FunctionStatement).Function.Body.(*ast.BlockStatement).List[0] is(checkComments((parser.comments.CommentMap)[n], []string{"comment"}, ast.LEADING), nil) parser, program = test(` a + /*comment1*/ /*comment2*/ b/*comment3*/; /*comment4*/c `, nil) is(parser.comments.CommentMap.Size(), 4) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.BinaryExpression).Right], []string{"comment1", "comment2"}, ast.LEADING), nil) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.BinaryExpression).Right], []string{"comment3"}, ast.TRAILING), nil) is(checkComments((parser.comments.CommentMap)[program.Body[1]], []string{"comment4"}, ast.LEADING), nil) parser, program = test(` a + /*comment1*/ /*comment2*/ b/*comment3*/ /*comment4*/c `, nil) is(parser.comments.CommentMap.Size(), 4) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.BinaryExpression).Right], []string{"comment1", "comment2"}, ast.LEADING), nil) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.BinaryExpression).Right], []string{"comment3"}, ast.TRAILING), nil) is(checkComments((parser.comments.CommentMap)[program.Body[1]], []string{"comment4"}, ast.LEADING), nil) // New parser, program = test(` a = /*comment1*/new /*comment2*/ obj/*comment3*/() `, nil) is(parser.comments.CommentMap.Size(), 3) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.AssignExpression).Right], []string{"comment1"}, ast.LEADING), nil) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.AssignExpression).Right.(*ast.NewExpression).Callee], []string{"comment2"}, ast.LEADING), nil) is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.AssignExpression).Right.(*ast.NewExpression).Callee], []string{"comment3"}, ast.TRAILING), nil) }) } func TestParser_comments2(t *testing.T) { tt(t, func() { test := func(source string, chk interface{}) (*_parser, *ast.Program) { parser, program, err := testParseWithMode(source, StoreComments) is(firstErr(err), chk) // Check unresolved comments is(len(parser.comments.Comments), 0) return parser, program } parser, program := test(` a = /*comment1*/new /*comment2*/ obj/*comment3*/() `, nil) n := program.Body[0] fmt.Printf("FOUND NODE: %v, number of comments: %v\n", reflect.TypeOf(n), len(parser.comments.CommentMap[n])) displayComments(parser.comments.CommentMap) }) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/parser/dbg.go000066400000000000000000000003001331145703200247450ustar00rootroot00000000000000// This file was AUTOMATICALLY GENERATED by dbg-import (smuggol) for github.com/robertkrimen/dbg package parser import ( Dbg "github.com/robertkrimen/otto/dbg" ) var dbg, dbgf = Dbg.New() golang-github-robertkrimen-otto-0.0~git20180617.15f95af/parser/error.go000066400000000000000000000130641331145703200253550ustar00rootroot00000000000000package parser import ( "fmt" "sort" "github.com/robertkrimen/otto/file" "github.com/robertkrimen/otto/token" ) const ( err_UnexpectedToken = "Unexpected token %v" err_UnexpectedEndOfInput = "Unexpected end of input" err_UnexpectedEscape = "Unexpected escape" ) // UnexpectedNumber: 'Unexpected number', // UnexpectedString: 'Unexpected string', // UnexpectedIdentifier: 'Unexpected identifier', // UnexpectedReserved: 'Unexpected reserved word', // NewlineAfterThrow: 'Illegal newline after throw', // InvalidRegExp: 'Invalid regular expression', // UnterminatedRegExp: 'Invalid regular expression: missing /', // InvalidLHSInAssignment: 'Invalid left-hand side in assignment', // InvalidLHSInForIn: 'Invalid left-hand side in for-in', // MultipleDefaultsInSwitch: 'More than one default clause in switch statement', // NoCatchOrFinally: 'Missing catch or finally after try', // UnknownLabel: 'Undefined label \'%0\'', // Redeclaration: '%0 \'%1\' has already been declared', // IllegalContinue: 'Illegal continue statement', // IllegalBreak: 'Illegal break statement', // IllegalReturn: 'Illegal return statement', // StrictModeWith: 'Strict mode code may not include a with statement', // StrictCatchVariable: 'Catch variable may not be eval or arguments in strict mode', // StrictVarName: 'Variable name may not be eval or arguments in strict mode', // StrictParamName: 'Parameter name eval or arguments is not allowed in strict mode', // StrictParamDupe: 'Strict mode function may not have duplicate parameter names', // StrictFunctionName: 'Function name may not be eval or arguments in strict mode', // StrictOctalLiteral: 'Octal literals are not allowed in strict mode.', // StrictDelete: 'Delete of an unqualified identifier in strict mode.', // StrictDuplicateProperty: 'Duplicate data property in object literal not allowed in strict mode', // AccessorDataProperty: 'Object literal may not have data and accessor property with the same name', // AccessorGetSet: 'Object literal may not have multiple get/set accessors with the same name', // StrictLHSAssignment: 'Assignment to eval or arguments is not allowed in strict mode', // StrictLHSPostfix: 'Postfix increment/decrement may not have eval or arguments operand in strict mode', // StrictLHSPrefix: 'Prefix increment/decrement may not have eval or arguments operand in strict mode', // StrictReservedWord: 'Use of future reserved word in strict mode' // A SyntaxError is a description of an ECMAScript syntax error. // An Error represents a parsing error. It includes the position where the error occurred and a message/description. type Error struct { Position file.Position Message string } // FIXME Should this be "SyntaxError"? func (self Error) Error() string { filename := self.Position.Filename if filename == "" { filename = "(anonymous)" } return fmt.Sprintf("%s: Line %d:%d %s", filename, self.Position.Line, self.Position.Column, self.Message, ) } func (self *_parser) error(place interface{}, msg string, msgValues ...interface{}) *Error { idx := file.Idx(0) switch place := place.(type) { case int: idx = self.idxOf(place) case file.Idx: if place == 0 { idx = self.idxOf(self.chrOffset) } else { idx = place } default: panic(fmt.Errorf("error(%T, ...)", place)) } position := self.position(idx) msg = fmt.Sprintf(msg, msgValues...) self.errors.Add(position, msg) return self.errors[len(self.errors)-1] } func (self *_parser) errorUnexpected(idx file.Idx, chr rune) error { if chr == -1 { return self.error(idx, err_UnexpectedEndOfInput) } return self.error(idx, err_UnexpectedToken, token.ILLEGAL) } func (self *_parser) errorUnexpectedToken(tkn token.Token) error { switch tkn { case token.EOF: return self.error(file.Idx(0), err_UnexpectedEndOfInput) } value := tkn.String() switch tkn { case token.BOOLEAN, token.NULL: value = self.literal case token.IDENTIFIER: return self.error(self.idx, "Unexpected identifier") case token.KEYWORD: // TODO Might be a future reserved word return self.error(self.idx, "Unexpected reserved word") case token.NUMBER: return self.error(self.idx, "Unexpected number") case token.STRING: return self.error(self.idx, "Unexpected string") } return self.error(self.idx, err_UnexpectedToken, value) } // ErrorList is a list of *Errors. // type ErrorList []*Error // Add adds an Error with given position and message to an ErrorList. func (self *ErrorList) Add(position file.Position, msg string) { *self = append(*self, &Error{position, msg}) } // Reset resets an ErrorList to no errors. func (self *ErrorList) Reset() { *self = (*self)[0:0] } func (self ErrorList) Len() int { return len(self) } func (self ErrorList) Swap(i, j int) { self[i], self[j] = self[j], self[i] } func (self ErrorList) Less(i, j int) bool { x := &self[i].Position y := &self[j].Position if x.Filename < y.Filename { return true } if x.Filename == y.Filename { if x.Line < y.Line { return true } if x.Line == y.Line { return x.Column < y.Column } } return false } func (self ErrorList) Sort() { sort.Sort(self) } // Error implements the Error interface. func (self ErrorList) Error() string { switch len(self) { case 0: return "no errors" case 1: return self[0].Error() } return fmt.Sprintf("%s (and %d more errors)", self[0].Error(), len(self)-1) } // Err returns an error equivalent to this ErrorList. // If the list is empty, Err returns nil. func (self ErrorList) Err() error { if len(self) == 0 { return nil } return self } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/parser/expression.go000066400000000000000000000522031331145703200264210ustar00rootroot00000000000000package parser import ( "regexp" "github.com/robertkrimen/otto/ast" "github.com/robertkrimen/otto/file" "github.com/robertkrimen/otto/token" ) func (self *_parser) parseIdentifier() *ast.Identifier { literal := self.literal idx := self.idx if self.mode&StoreComments != 0 { self.comments.MarkComments(ast.LEADING) } self.next() exp := &ast.Identifier{ Name: literal, Idx: idx, } if self.mode&StoreComments != 0 { self.comments.SetExpression(exp) } return exp } func (self *_parser) parsePrimaryExpression() ast.Expression { literal := self.literal idx := self.idx switch self.token { case token.IDENTIFIER: self.next() if len(literal) > 1 { tkn, strict := token.IsKeyword(literal) if tkn == token.KEYWORD { if !strict { self.error(idx, "Unexpected reserved word") } } } return &ast.Identifier{ Name: literal, Idx: idx, } case token.NULL: self.next() return &ast.NullLiteral{ Idx: idx, Literal: literal, } case token.BOOLEAN: self.next() value := false switch literal { case "true": value = true case "false": value = false default: self.error(idx, "Illegal boolean literal") } return &ast.BooleanLiteral{ Idx: idx, Literal: literal, Value: value, } case token.STRING: self.next() value, err := parseStringLiteral(literal[1 : len(literal)-1]) if err != nil { self.error(idx, err.Error()) } return &ast.StringLiteral{ Idx: idx, Literal: literal, Value: value, } case token.NUMBER: self.next() value, err := parseNumberLiteral(literal) if err != nil { self.error(idx, err.Error()) value = 0 } return &ast.NumberLiteral{ Idx: idx, Literal: literal, Value: value, } case token.SLASH, token.QUOTIENT_ASSIGN: return self.parseRegExpLiteral() case token.LEFT_BRACE: return self.parseObjectLiteral() case token.LEFT_BRACKET: return self.parseArrayLiteral() case token.LEFT_PARENTHESIS: self.expect(token.LEFT_PARENTHESIS) expression := self.parseExpression() if self.mode&StoreComments != 0 { self.comments.Unset() } self.expect(token.RIGHT_PARENTHESIS) return expression case token.THIS: self.next() return &ast.ThisExpression{ Idx: idx, } case token.FUNCTION: return self.parseFunction(false) } self.errorUnexpectedToken(self.token) self.nextStatement() return &ast.BadExpression{From: idx, To: self.idx} } func (self *_parser) parseRegExpLiteral() *ast.RegExpLiteral { offset := self.chrOffset - 1 // Opening slash already gotten if self.token == token.QUOTIENT_ASSIGN { offset -= 1 // = } idx := self.idxOf(offset) pattern, err := self.scanString(offset) endOffset := self.chrOffset self.next() if err == nil { pattern = pattern[1 : len(pattern)-1] } flags := "" if self.token == token.IDENTIFIER { // gim flags = self.literal self.next() endOffset = self.chrOffset - 1 } var value string // TODO 15.10 { // Test during parsing that this is a valid regular expression // Sorry, (?=) and (?!) are invalid (for now) pattern, err := TransformRegExp(pattern) if err != nil { if pattern == "" || self.mode&IgnoreRegExpErrors == 0 { self.error(idx, "Invalid regular expression: %s", err.Error()) } } else { _, err = regexp.Compile(pattern) if err != nil { // We should not get here, ParseRegExp should catch any errors self.error(idx, "Invalid regular expression: %s", err.Error()[22:]) // Skip redundant "parse regexp error" } else { value = pattern } } } literal := self.str[offset:endOffset] return &ast.RegExpLiteral{ Idx: idx, Literal: literal, Pattern: pattern, Flags: flags, Value: value, } } func (self *_parser) parseVariableDeclaration(declarationList *[]*ast.VariableExpression) ast.Expression { if self.token != token.IDENTIFIER { idx := self.expect(token.IDENTIFIER) self.nextStatement() return &ast.BadExpression{From: idx, To: self.idx} } literal := self.literal idx := self.idx self.next() node := &ast.VariableExpression{ Name: literal, Idx: idx, } if self.mode&StoreComments != 0 { self.comments.SetExpression(node) } if declarationList != nil { *declarationList = append(*declarationList, node) } if self.token == token.ASSIGN { if self.mode&StoreComments != 0 { self.comments.Unset() } self.next() node.Initializer = self.parseAssignmentExpression() } return node } func (self *_parser) parseVariableDeclarationList(var_ file.Idx) []ast.Expression { var declarationList []*ast.VariableExpression // Avoid bad expressions var list []ast.Expression for { if self.mode&StoreComments != 0 { self.comments.MarkComments(ast.LEADING) } decl := self.parseVariableDeclaration(&declarationList) list = append(list, decl) if self.token != token.COMMA { break } if self.mode&StoreComments != 0 { self.comments.Unset() } self.next() } self.scope.declare(&ast.VariableDeclaration{ Var: var_, List: declarationList, }) return list } func (self *_parser) parseObjectPropertyKey() (string, string) { idx, tkn, literal := self.idx, self.token, self.literal value := "" if self.mode&StoreComments != 0 { self.comments.MarkComments(ast.KEY) } self.next() switch tkn { case token.IDENTIFIER: value = literal case token.NUMBER: var err error _, err = parseNumberLiteral(literal) if err != nil { self.error(idx, err.Error()) } else { value = literal } case token.STRING: var err error value, err = parseStringLiteral(literal[1 : len(literal)-1]) if err != nil { self.error(idx, err.Error()) } default: // null, false, class, etc. if matchIdentifier.MatchString(literal) { value = literal } } return literal, value } func (self *_parser) parseObjectProperty() ast.Property { literal, value := self.parseObjectPropertyKey() if literal == "get" && self.token != token.COLON { idx := self.idx _, value := self.parseObjectPropertyKey() parameterList := self.parseFunctionParameterList() node := &ast.FunctionLiteral{ Function: idx, ParameterList: parameterList, } self.parseFunctionBlock(node) return ast.Property{ Key: value, Kind: "get", Value: node, } } else if literal == "set" && self.token != token.COLON { idx := self.idx _, value := self.parseObjectPropertyKey() parameterList := self.parseFunctionParameterList() node := &ast.FunctionLiteral{ Function: idx, ParameterList: parameterList, } self.parseFunctionBlock(node) return ast.Property{ Key: value, Kind: "set", Value: node, } } if self.mode&StoreComments != 0 { self.comments.MarkComments(ast.COLON) } self.expect(token.COLON) exp := ast.Property{ Key: value, Kind: "value", Value: self.parseAssignmentExpression(), } if self.mode&StoreComments != 0 { self.comments.SetExpression(exp.Value) } return exp } func (self *_parser) parseObjectLiteral() ast.Expression { var value []ast.Property idx0 := self.expect(token.LEFT_BRACE) for self.token != token.RIGHT_BRACE && self.token != token.EOF { value = append(value, self.parseObjectProperty()) if self.token == token.COMMA { if self.mode&StoreComments != 0 { self.comments.Unset() } self.next() continue } } if self.mode&StoreComments != 0 { self.comments.MarkComments(ast.FINAL) } idx1 := self.expect(token.RIGHT_BRACE) return &ast.ObjectLiteral{ LeftBrace: idx0, RightBrace: idx1, Value: value, } } func (self *_parser) parseArrayLiteral() ast.Expression { idx0 := self.expect(token.LEFT_BRACKET) var value []ast.Expression for self.token != token.RIGHT_BRACKET && self.token != token.EOF { if self.token == token.COMMA { // This kind of comment requires a special empty expression node. empty := &ast.EmptyExpression{self.idx, self.idx} if self.mode&StoreComments != 0 { self.comments.SetExpression(empty) self.comments.Unset() } value = append(value, empty) self.next() continue } exp := self.parseAssignmentExpression() value = append(value, exp) if self.token != token.RIGHT_BRACKET { if self.mode&StoreComments != 0 { self.comments.Unset() } self.expect(token.COMMA) } } if self.mode&StoreComments != 0 { self.comments.MarkComments(ast.FINAL) } idx1 := self.expect(token.RIGHT_BRACKET) return &ast.ArrayLiteral{ LeftBracket: idx0, RightBracket: idx1, Value: value, } } func (self *_parser) parseArgumentList() (argumentList []ast.Expression, idx0, idx1 file.Idx) { if self.mode&StoreComments != 0 { self.comments.Unset() } idx0 = self.expect(token.LEFT_PARENTHESIS) if self.token != token.RIGHT_PARENTHESIS { for { exp := self.parseAssignmentExpression() if self.mode&StoreComments != 0 { self.comments.SetExpression(exp) } argumentList = append(argumentList, exp) if self.token != token.COMMA { break } if self.mode&StoreComments != 0 { self.comments.Unset() } self.next() } } if self.mode&StoreComments != 0 { self.comments.Unset() } idx1 = self.expect(token.RIGHT_PARENTHESIS) return } func (self *_parser) parseCallExpression(left ast.Expression) ast.Expression { argumentList, idx0, idx1 := self.parseArgumentList() exp := &ast.CallExpression{ Callee: left, LeftParenthesis: idx0, ArgumentList: argumentList, RightParenthesis: idx1, } if self.mode&StoreComments != 0 { self.comments.SetExpression(exp) } return exp } func (self *_parser) parseDotMember(left ast.Expression) ast.Expression { period := self.expect(token.PERIOD) literal := self.literal idx := self.idx if !matchIdentifier.MatchString(literal) { self.expect(token.IDENTIFIER) self.nextStatement() return &ast.BadExpression{From: period, To: self.idx} } self.next() return &ast.DotExpression{ Left: left, Identifier: &ast.Identifier{ Idx: idx, Name: literal, }, } } func (self *_parser) parseBracketMember(left ast.Expression) ast.Expression { idx0 := self.expect(token.LEFT_BRACKET) member := self.parseExpression() idx1 := self.expect(token.RIGHT_BRACKET) return &ast.BracketExpression{ LeftBracket: idx0, Left: left, Member: member, RightBracket: idx1, } } func (self *_parser) parseNewExpression() ast.Expression { idx := self.expect(token.NEW) callee := self.parseLeftHandSideExpression() node := &ast.NewExpression{ New: idx, Callee: callee, } if self.token == token.LEFT_PARENTHESIS { argumentList, idx0, idx1 := self.parseArgumentList() node.ArgumentList = argumentList node.LeftParenthesis = idx0 node.RightParenthesis = idx1 } if self.mode&StoreComments != 0 { self.comments.SetExpression(node) } return node } func (self *_parser) parseLeftHandSideExpression() ast.Expression { var left ast.Expression if self.token == token.NEW { left = self.parseNewExpression() } else { if self.mode&StoreComments != 0 { self.comments.MarkComments(ast.LEADING) self.comments.MarkPrimary() } left = self.parsePrimaryExpression() } if self.mode&StoreComments != 0 { self.comments.SetExpression(left) } for { if self.token == token.PERIOD { left = self.parseDotMember(left) } else if self.token == token.LEFT_BRACKET { left = self.parseBracketMember(left) } else { break } } return left } func (self *_parser) parseLeftHandSideExpressionAllowCall() ast.Expression { allowIn := self.scope.allowIn self.scope.allowIn = true defer func() { self.scope.allowIn = allowIn }() var left ast.Expression if self.token == token.NEW { var newComments []*ast.Comment if self.mode&StoreComments != 0 { newComments = self.comments.FetchAll() self.comments.MarkComments(ast.LEADING) self.comments.MarkPrimary() } left = self.parseNewExpression() if self.mode&StoreComments != 0 { self.comments.CommentMap.AddComments(left, newComments, ast.LEADING) } } else { if self.mode&StoreComments != 0 { self.comments.MarkComments(ast.LEADING) self.comments.MarkPrimary() } left = self.parsePrimaryExpression() } if self.mode&StoreComments != 0 { self.comments.SetExpression(left) } for { if self.token == token.PERIOD { left = self.parseDotMember(left) } else if self.token == token.LEFT_BRACKET { left = self.parseBracketMember(left) } else if self.token == token.LEFT_PARENTHESIS { left = self.parseCallExpression(left) } else { break } } return left } func (self *_parser) parsePostfixExpression() ast.Expression { operand := self.parseLeftHandSideExpressionAllowCall() switch self.token { case token.INCREMENT, token.DECREMENT: // Make sure there is no line terminator here if self.implicitSemicolon { break } tkn := self.token idx := self.idx if self.mode&StoreComments != 0 { self.comments.Unset() } self.next() switch operand.(type) { case *ast.Identifier, *ast.DotExpression, *ast.BracketExpression: default: self.error(idx, "Invalid left-hand side in assignment") self.nextStatement() return &ast.BadExpression{From: idx, To: self.idx} } exp := &ast.UnaryExpression{ Operator: tkn, Idx: idx, Operand: operand, Postfix: true, } if self.mode&StoreComments != 0 { self.comments.SetExpression(exp) } return exp } return operand } func (self *_parser) parseUnaryExpression() ast.Expression { switch self.token { case token.PLUS, token.MINUS, token.NOT, token.BITWISE_NOT: fallthrough case token.DELETE, token.VOID, token.TYPEOF: tkn := self.token idx := self.idx if self.mode&StoreComments != 0 { self.comments.Unset() } self.next() return &ast.UnaryExpression{ Operator: tkn, Idx: idx, Operand: self.parseUnaryExpression(), } case token.INCREMENT, token.DECREMENT: tkn := self.token idx := self.idx if self.mode&StoreComments != 0 { self.comments.Unset() } self.next() operand := self.parseUnaryExpression() switch operand.(type) { case *ast.Identifier, *ast.DotExpression, *ast.BracketExpression: default: self.error(idx, "Invalid left-hand side in assignment") self.nextStatement() return &ast.BadExpression{From: idx, To: self.idx} } return &ast.UnaryExpression{ Operator: tkn, Idx: idx, Operand: operand, } } return self.parsePostfixExpression() } func (self *_parser) parseMultiplicativeExpression() ast.Expression { next := self.parseUnaryExpression left := next() for self.token == token.MULTIPLY || self.token == token.SLASH || self.token == token.REMAINDER { tkn := self.token if self.mode&StoreComments != 0 { self.comments.Unset() } self.next() left = &ast.BinaryExpression{ Operator: tkn, Left: left, Right: next(), } } return left } func (self *_parser) parseAdditiveExpression() ast.Expression { next := self.parseMultiplicativeExpression left := next() for self.token == token.PLUS || self.token == token.MINUS { tkn := self.token if self.mode&StoreComments != 0 { self.comments.Unset() } self.next() left = &ast.BinaryExpression{ Operator: tkn, Left: left, Right: next(), } } return left } func (self *_parser) parseShiftExpression() ast.Expression { next := self.parseAdditiveExpression left := next() for self.token == token.SHIFT_LEFT || self.token == token.SHIFT_RIGHT || self.token == token.UNSIGNED_SHIFT_RIGHT { tkn := self.token if self.mode&StoreComments != 0 { self.comments.Unset() } self.next() left = &ast.BinaryExpression{ Operator: tkn, Left: left, Right: next(), } } return left } func (self *_parser) parseRelationalExpression() ast.Expression { next := self.parseShiftExpression left := next() allowIn := self.scope.allowIn self.scope.allowIn = true defer func() { self.scope.allowIn = allowIn }() switch self.token { case token.LESS, token.LESS_OR_EQUAL, token.GREATER, token.GREATER_OR_EQUAL: tkn := self.token if self.mode&StoreComments != 0 { self.comments.Unset() } self.next() exp := &ast.BinaryExpression{ Operator: tkn, Left: left, Right: self.parseRelationalExpression(), Comparison: true, } return exp case token.INSTANCEOF: tkn := self.token if self.mode&StoreComments != 0 { self.comments.Unset() } self.next() exp := &ast.BinaryExpression{ Operator: tkn, Left: left, Right: self.parseRelationalExpression(), } return exp case token.IN: if !allowIn { return left } tkn := self.token if self.mode&StoreComments != 0 { self.comments.Unset() } self.next() exp := &ast.BinaryExpression{ Operator: tkn, Left: left, Right: self.parseRelationalExpression(), } return exp } return left } func (self *_parser) parseEqualityExpression() ast.Expression { next := self.parseRelationalExpression left := next() for self.token == token.EQUAL || self.token == token.NOT_EQUAL || self.token == token.STRICT_EQUAL || self.token == token.STRICT_NOT_EQUAL { tkn := self.token if self.mode&StoreComments != 0 { self.comments.Unset() } self.next() left = &ast.BinaryExpression{ Operator: tkn, Left: left, Right: next(), Comparison: true, } } return left } func (self *_parser) parseBitwiseAndExpression() ast.Expression { next := self.parseEqualityExpression left := next() for self.token == token.AND { if self.mode&StoreComments != 0 { self.comments.Unset() } tkn := self.token self.next() left = &ast.BinaryExpression{ Operator: tkn, Left: left, Right: next(), } } return left } func (self *_parser) parseBitwiseExclusiveOrExpression() ast.Expression { next := self.parseBitwiseAndExpression left := next() for self.token == token.EXCLUSIVE_OR { if self.mode&StoreComments != 0 { self.comments.Unset() } tkn := self.token self.next() left = &ast.BinaryExpression{ Operator: tkn, Left: left, Right: next(), } } return left } func (self *_parser) parseBitwiseOrExpression() ast.Expression { next := self.parseBitwiseExclusiveOrExpression left := next() for self.token == token.OR { if self.mode&StoreComments != 0 { self.comments.Unset() } tkn := self.token self.next() left = &ast.BinaryExpression{ Operator: tkn, Left: left, Right: next(), } } return left } func (self *_parser) parseLogicalAndExpression() ast.Expression { next := self.parseBitwiseOrExpression left := next() for self.token == token.LOGICAL_AND { if self.mode&StoreComments != 0 { self.comments.Unset() } tkn := self.token self.next() left = &ast.BinaryExpression{ Operator: tkn, Left: left, Right: next(), } } return left } func (self *_parser) parseLogicalOrExpression() ast.Expression { next := self.parseLogicalAndExpression left := next() for self.token == token.LOGICAL_OR { if self.mode&StoreComments != 0 { self.comments.Unset() } tkn := self.token self.next() left = &ast.BinaryExpression{ Operator: tkn, Left: left, Right: next(), } } return left } func (self *_parser) parseConditionlExpression() ast.Expression { left := self.parseLogicalOrExpression() if self.token == token.QUESTION_MARK { if self.mode&StoreComments != 0 { self.comments.Unset() } self.next() consequent := self.parseAssignmentExpression() if self.mode&StoreComments != 0 { self.comments.Unset() } self.expect(token.COLON) exp := &ast.ConditionalExpression{ Test: left, Consequent: consequent, Alternate: self.parseAssignmentExpression(), } return exp } return left } func (self *_parser) parseAssignmentExpression() ast.Expression { left := self.parseConditionlExpression() var operator token.Token switch self.token { case token.ASSIGN: operator = self.token case token.ADD_ASSIGN: operator = token.PLUS case token.SUBTRACT_ASSIGN: operator = token.MINUS case token.MULTIPLY_ASSIGN: operator = token.MULTIPLY case token.QUOTIENT_ASSIGN: operator = token.SLASH case token.REMAINDER_ASSIGN: operator = token.REMAINDER case token.AND_ASSIGN: operator = token.AND case token.AND_NOT_ASSIGN: operator = token.AND_NOT case token.OR_ASSIGN: operator = token.OR case token.EXCLUSIVE_OR_ASSIGN: operator = token.EXCLUSIVE_OR case token.SHIFT_LEFT_ASSIGN: operator = token.SHIFT_LEFT case token.SHIFT_RIGHT_ASSIGN: operator = token.SHIFT_RIGHT case token.UNSIGNED_SHIFT_RIGHT_ASSIGN: operator = token.UNSIGNED_SHIFT_RIGHT } if operator != 0 { idx := self.idx if self.mode&StoreComments != 0 { self.comments.Unset() } self.next() switch left.(type) { case *ast.Identifier, *ast.DotExpression, *ast.BracketExpression: default: self.error(left.Idx0(), "Invalid left-hand side in assignment") self.nextStatement() return &ast.BadExpression{From: idx, To: self.idx} } exp := &ast.AssignExpression{ Left: left, Operator: operator, Right: self.parseAssignmentExpression(), } if self.mode&StoreComments != 0 { self.comments.SetExpression(exp) } return exp } return left } func (self *_parser) parseExpression() ast.Expression { next := self.parseAssignmentExpression left := next() if self.token == token.COMMA { sequence := []ast.Expression{left} for { if self.token != token.COMMA { break } self.next() sequence = append(sequence, next()) } return &ast.SequenceExpression{ Sequence: sequence, } } return left } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/parser/lexer.go000066400000000000000000000446651331145703200253560ustar00rootroot00000000000000package parser import ( "bytes" "errors" "fmt" "regexp" "strconv" "strings" "unicode" "unicode/utf8" "github.com/robertkrimen/otto/ast" "github.com/robertkrimen/otto/file" "github.com/robertkrimen/otto/token" ) type _chr struct { value rune width int } var matchIdentifier = regexp.MustCompile(`^[$_\p{L}][$_\p{L}\d}]*$`) func isDecimalDigit(chr rune) bool { return '0' <= chr && chr <= '9' } func digitValue(chr rune) int { switch { case '0' <= chr && chr <= '9': return int(chr - '0') case 'a' <= chr && chr <= 'f': return int(chr - 'a' + 10) case 'A' <= chr && chr <= 'F': return int(chr - 'A' + 10) } return 16 // Larger than any legal digit value } func isDigit(chr rune, base int) bool { return digitValue(chr) < base } func isIdentifierStart(chr rune) bool { return chr == '$' || chr == '_' || chr == '\\' || 'a' <= chr && chr <= 'z' || 'A' <= chr && chr <= 'Z' || chr >= utf8.RuneSelf && unicode.IsLetter(chr) } func isIdentifierPart(chr rune) bool { return chr == '$' || chr == '_' || chr == '\\' || 'a' <= chr && chr <= 'z' || 'A' <= chr && chr <= 'Z' || '0' <= chr && chr <= '9' || chr >= utf8.RuneSelf && (unicode.IsLetter(chr) || unicode.IsDigit(chr)) } func (self *_parser) scanIdentifier() (string, error) { offset := self.chrOffset parse := false for isIdentifierPart(self.chr) { if self.chr == '\\' { distance := self.chrOffset - offset self.read() if self.chr != 'u' { return "", fmt.Errorf("Invalid identifier escape character: %c (%s)", self.chr, string(self.chr)) } parse = true var value rune for j := 0; j < 4; j++ { self.read() decimal, ok := hex2decimal(byte(self.chr)) if !ok { return "", fmt.Errorf("Invalid identifier escape character: %c (%s)", self.chr, string(self.chr)) } value = value<<4 | decimal } if value == '\\' { return "", fmt.Errorf("Invalid identifier escape value: %c (%s)", value, string(value)) } else if distance == 0 { if !isIdentifierStart(value) { return "", fmt.Errorf("Invalid identifier escape value: %c (%s)", value, string(value)) } } else if distance > 0 { if !isIdentifierPart(value) { return "", fmt.Errorf("Invalid identifier escape value: %c (%s)", value, string(value)) } } } self.read() } literal := string(self.str[offset:self.chrOffset]) if parse { return parseStringLiteral(literal) } return literal, nil } // 7.2 func isLineWhiteSpace(chr rune) bool { switch chr { case '\u0009', '\u000b', '\u000c', '\u0020', '\u00a0', '\ufeff': return true case '\u000a', '\u000d', '\u2028', '\u2029': return false case '\u0085': return false } return unicode.IsSpace(chr) } // 7.3 func isLineTerminator(chr rune) bool { switch chr { case '\u000a', '\u000d', '\u2028', '\u2029': return true } return false } func (self *_parser) scan() (tkn token.Token, literal string, idx file.Idx) { self.implicitSemicolon = false for { self.skipWhiteSpace() idx = self.idxOf(self.chrOffset) insertSemicolon := false switch chr := self.chr; { case isIdentifierStart(chr): var err error literal, err = self.scanIdentifier() if err != nil { tkn = token.ILLEGAL break } if len(literal) > 1 { // Keywords are longer than 1 character, avoid lookup otherwise var strict bool tkn, strict = token.IsKeyword(literal) switch tkn { case 0: // Not a keyword if literal == "true" || literal == "false" { self.insertSemicolon = true tkn = token.BOOLEAN return } else if literal == "null" { self.insertSemicolon = true tkn = token.NULL return } case token.KEYWORD: tkn = token.KEYWORD if strict { // TODO If strict and in strict mode, then this is not a break break } return case token.THIS, token.BREAK, token.THROW, // A newline after a throw is not allowed, but we need to detect it token.RETURN, token.CONTINUE, token.DEBUGGER: self.insertSemicolon = true return default: return } } self.insertSemicolon = true tkn = token.IDENTIFIER return case '0' <= chr && chr <= '9': self.insertSemicolon = true tkn, literal = self.scanNumericLiteral(false) return default: self.read() switch chr { case -1: if self.insertSemicolon { self.insertSemicolon = false self.implicitSemicolon = true } tkn = token.EOF case '\r', '\n', '\u2028', '\u2029': self.insertSemicolon = false self.implicitSemicolon = true self.comments.AtLineBreak() continue case ':': tkn = token.COLON case '.': if digitValue(self.chr) < 10 { insertSemicolon = true tkn, literal = self.scanNumericLiteral(true) } else { tkn = token.PERIOD } case ',': tkn = token.COMMA case ';': tkn = token.SEMICOLON case '(': tkn = token.LEFT_PARENTHESIS case ')': tkn = token.RIGHT_PARENTHESIS insertSemicolon = true case '[': tkn = token.LEFT_BRACKET case ']': tkn = token.RIGHT_BRACKET insertSemicolon = true case '{': tkn = token.LEFT_BRACE case '}': tkn = token.RIGHT_BRACE insertSemicolon = true case '+': tkn = self.switch3(token.PLUS, token.ADD_ASSIGN, '+', token.INCREMENT) if tkn == token.INCREMENT { insertSemicolon = true } case '-': tkn = self.switch3(token.MINUS, token.SUBTRACT_ASSIGN, '-', token.DECREMENT) if tkn == token.DECREMENT { insertSemicolon = true } case '*': tkn = self.switch2(token.MULTIPLY, token.MULTIPLY_ASSIGN) case '/': if self.chr == '/' { if self.mode&StoreComments != 0 { literal := string(self.readSingleLineComment()) self.comments.AddComment(ast.NewComment(literal, self.idx)) continue } self.skipSingleLineComment() continue } else if self.chr == '*' { if self.mode&StoreComments != 0 { literal = string(self.readMultiLineComment()) self.comments.AddComment(ast.NewComment(literal, self.idx)) continue } self.skipMultiLineComment() continue } else { // Could be division, could be RegExp literal tkn = self.switch2(token.SLASH, token.QUOTIENT_ASSIGN) insertSemicolon = true } case '%': tkn = self.switch2(token.REMAINDER, token.REMAINDER_ASSIGN) case '^': tkn = self.switch2(token.EXCLUSIVE_OR, token.EXCLUSIVE_OR_ASSIGN) case '<': tkn = self.switch4(token.LESS, token.LESS_OR_EQUAL, '<', token.SHIFT_LEFT, token.SHIFT_LEFT_ASSIGN) case '>': tkn = self.switch6(token.GREATER, token.GREATER_OR_EQUAL, '>', token.SHIFT_RIGHT, token.SHIFT_RIGHT_ASSIGN, '>', token.UNSIGNED_SHIFT_RIGHT, token.UNSIGNED_SHIFT_RIGHT_ASSIGN) case '=': tkn = self.switch2(token.ASSIGN, token.EQUAL) if tkn == token.EQUAL && self.chr == '=' { self.read() tkn = token.STRICT_EQUAL } case '!': tkn = self.switch2(token.NOT, token.NOT_EQUAL) if tkn == token.NOT_EQUAL && self.chr == '=' { self.read() tkn = token.STRICT_NOT_EQUAL } case '&': if self.chr == '^' { self.read() tkn = self.switch2(token.AND_NOT, token.AND_NOT_ASSIGN) } else { tkn = self.switch3(token.AND, token.AND_ASSIGN, '&', token.LOGICAL_AND) } case '|': tkn = self.switch3(token.OR, token.OR_ASSIGN, '|', token.LOGICAL_OR) case '~': tkn = token.BITWISE_NOT case '?': tkn = token.QUESTION_MARK case '"', '\'': insertSemicolon = true tkn = token.STRING var err error literal, err = self.scanString(self.chrOffset - 1) if err != nil { tkn = token.ILLEGAL } default: self.errorUnexpected(idx, chr) tkn = token.ILLEGAL } } self.insertSemicolon = insertSemicolon return } } func (self *_parser) switch2(tkn0, tkn1 token.Token) token.Token { if self.chr == '=' { self.read() return tkn1 } return tkn0 } func (self *_parser) switch3(tkn0, tkn1 token.Token, chr2 rune, tkn2 token.Token) token.Token { if self.chr == '=' { self.read() return tkn1 } if self.chr == chr2 { self.read() return tkn2 } return tkn0 } func (self *_parser) switch4(tkn0, tkn1 token.Token, chr2 rune, tkn2, tkn3 token.Token) token.Token { if self.chr == '=' { self.read() return tkn1 } if self.chr == chr2 { self.read() if self.chr == '=' { self.read() return tkn3 } return tkn2 } return tkn0 } func (self *_parser) switch6(tkn0, tkn1 token.Token, chr2 rune, tkn2, tkn3 token.Token, chr3 rune, tkn4, tkn5 token.Token) token.Token { if self.chr == '=' { self.read() return tkn1 } if self.chr == chr2 { self.read() if self.chr == '=' { self.read() return tkn3 } if self.chr == chr3 { self.read() if self.chr == '=' { self.read() return tkn5 } return tkn4 } return tkn2 } return tkn0 } func (self *_parser) chrAt(index int) _chr { value, width := utf8.DecodeRuneInString(self.str[index:]) return _chr{ value: value, width: width, } } func (self *_parser) _peek() rune { if self.offset+1 < self.length { return rune(self.str[self.offset+1]) } return -1 } func (self *_parser) read() { if self.offset < self.length { self.chrOffset = self.offset chr, width := rune(self.str[self.offset]), 1 if chr >= utf8.RuneSelf { // !ASCII chr, width = utf8.DecodeRuneInString(self.str[self.offset:]) if chr == utf8.RuneError && width == 1 { self.error(self.chrOffset, "Invalid UTF-8 character") } } self.offset += width self.chr = chr } else { self.chrOffset = self.length self.chr = -1 // EOF } } // This is here since the functions are so similar func (self *_RegExp_parser) read() { if self.offset < self.length { self.chrOffset = self.offset chr, width := rune(self.str[self.offset]), 1 if chr >= utf8.RuneSelf { // !ASCII chr, width = utf8.DecodeRuneInString(self.str[self.offset:]) if chr == utf8.RuneError && width == 1 { self.error(self.chrOffset, "Invalid UTF-8 character") } } self.offset += width self.chr = chr } else { self.chrOffset = self.length self.chr = -1 // EOF } } func (self *_parser) readSingleLineComment() (result []rune) { for self.chr != -1 { self.read() if isLineTerminator(self.chr) { return } result = append(result, self.chr) } // Get rid of the trailing -1 result = result[:len(result)-1] return } func (self *_parser) readMultiLineComment() (result []rune) { self.read() for self.chr >= 0 { chr := self.chr self.read() if chr == '*' && self.chr == '/' { self.read() return } result = append(result, chr) } self.errorUnexpected(0, self.chr) return } func (self *_parser) skipSingleLineComment() { for self.chr != -1 { self.read() if isLineTerminator(self.chr) { return } } } func (self *_parser) skipMultiLineComment() { self.read() for self.chr >= 0 { chr := self.chr self.read() if chr == '*' && self.chr == '/' { self.read() return } } self.errorUnexpected(0, self.chr) } func (self *_parser) skipWhiteSpace() { for { switch self.chr { case ' ', '\t', '\f', '\v', '\u00a0', '\ufeff': self.read() continue case '\r': if self._peek() == '\n' { self.comments.AtLineBreak() self.read() } fallthrough case '\u2028', '\u2029', '\n': if self.insertSemicolon { return } self.comments.AtLineBreak() self.read() continue } if self.chr >= utf8.RuneSelf { if unicode.IsSpace(self.chr) { self.read() continue } } break } } func (self *_parser) skipLineWhiteSpace() { for isLineWhiteSpace(self.chr) { self.read() } } func (self *_parser) scanMantissa(base int) { for digitValue(self.chr) < base { self.read() } } func (self *_parser) scanEscape(quote rune) { var length, base uint32 switch self.chr { //case '0', '1', '2', '3', '4', '5', '6', '7': // Octal: // length, base, limit = 3, 8, 255 case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', '"', '\'', '0': self.read() return case '\r', '\n', '\u2028', '\u2029': self.scanNewline() return case 'x': self.read() length, base = 2, 16 case 'u': self.read() length, base = 4, 16 default: self.read() // Always make progress return } var value uint32 for ; length > 0 && self.chr != quote && self.chr >= 0; length-- { digit := uint32(digitValue(self.chr)) if digit >= base { break } value = value*base + digit self.read() } } func (self *_parser) scanString(offset int) (string, error) { // " ' / quote := rune(self.str[offset]) for self.chr != quote { chr := self.chr if chr == '\n' || chr == '\r' || chr == '\u2028' || chr == '\u2029' || chr < 0 { goto newline } self.read() if chr == '\\' { if quote == '/' { if self.chr == '\n' || self.chr == '\r' || self.chr == '\u2028' || self.chr == '\u2029' || self.chr < 0 { goto newline } self.read() } else { self.scanEscape(quote) } } else if chr == '[' && quote == '/' { // Allow a slash (/) in a bracket character class ([...]) // TODO Fix this, this is hacky... quote = -1 } else if chr == ']' && quote == -1 { quote = '/' } } // " ' / self.read() return string(self.str[offset:self.chrOffset]), nil newline: self.scanNewline() err := "String not terminated" if quote == '/' { err = "Invalid regular expression: missing /" self.error(self.idxOf(offset), err) } return "", errors.New(err) } func (self *_parser) scanNewline() { if self.chr == '\r' { self.read() if self.chr != '\n' { return } } self.read() } func hex2decimal(chr byte) (value rune, ok bool) { { chr := rune(chr) switch { case '0' <= chr && chr <= '9': return chr - '0', true case 'a' <= chr && chr <= 'f': return chr - 'a' + 10, true case 'A' <= chr && chr <= 'F': return chr - 'A' + 10, true } return } } func parseNumberLiteral(literal string) (value interface{}, err error) { // TODO Is Uint okay? What about -MAX_UINT value, err = strconv.ParseInt(literal, 0, 64) if err == nil { return } parseIntErr := err // Save this first error, just in case value, err = strconv.ParseFloat(literal, 64) if err == nil { return } else if err.(*strconv.NumError).Err == strconv.ErrRange { // Infinity, etc. return value, nil } err = parseIntErr if err.(*strconv.NumError).Err == strconv.ErrRange { if len(literal) > 2 && literal[0] == '0' && (literal[1] == 'X' || literal[1] == 'x') { // Could just be a very large number (e.g. 0x8000000000000000) var value float64 literal = literal[2:] for _, chr := range literal { digit := digitValue(chr) if digit >= 16 { goto error } value = value*16 + float64(digit) } return value, nil } } error: return nil, errors.New("Illegal numeric literal") } func parseStringLiteral(literal string) (string, error) { // Best case scenario... if literal == "" { return "", nil } // Slightly less-best case scenario... if !strings.ContainsRune(literal, '\\') { return literal, nil } str := literal buffer := bytes.NewBuffer(make([]byte, 0, 3*len(literal)/2)) for len(str) > 0 { switch chr := str[0]; { // We do not explicitly handle the case of the quote // value, which can be: " ' / // This assumes we're already passed a partially well-formed literal case chr >= utf8.RuneSelf: chr, size := utf8.DecodeRuneInString(str) buffer.WriteRune(chr) str = str[size:] continue case chr != '\\': buffer.WriteByte(chr) str = str[1:] continue } if len(str) <= 1 { panic("len(str) <= 1") } chr := str[1] var value rune if chr >= utf8.RuneSelf { str = str[1:] var size int value, size = utf8.DecodeRuneInString(str) str = str[size:] // \ + } else { str = str[2:] // \ switch chr { case 'b': value = '\b' case 'f': value = '\f' case 'n': value = '\n' case 'r': value = '\r' case 't': value = '\t' case 'v': value = '\v' case 'x', 'u': size := 0 switch chr { case 'x': size = 2 case 'u': size = 4 } if len(str) < size { return "", fmt.Errorf("invalid escape: \\%s: len(%q) != %d", string(chr), str, size) } for j := 0; j < size; j++ { decimal, ok := hex2decimal(str[j]) if !ok { return "", fmt.Errorf("invalid escape: \\%s: %q", string(chr), str[:size]) } value = value<<4 | decimal } str = str[size:] if chr == 'x' { break } if value > utf8.MaxRune { panic("value > utf8.MaxRune") } case '0': if len(str) == 0 || '0' > str[0] || str[0] > '7' { value = 0 break } fallthrough case '1', '2', '3', '4', '5', '6', '7': // TODO strict value = rune(chr) - '0' j := 0 for ; j < 2; j++ { if len(str) < j+1 { break } chr := str[j] if '0' > chr || chr > '7' { break } decimal := rune(str[j]) - '0' value = (value << 3) | decimal } str = str[j:] case '\\': value = '\\' case '\'', '"': value = rune(chr) case '\r': if len(str) > 0 { if str[0] == '\n' { str = str[1:] } } fallthrough case '\n': continue default: value = rune(chr) } } buffer.WriteRune(value) } return buffer.String(), nil } func (self *_parser) scanNumericLiteral(decimalPoint bool) (token.Token, string) { offset := self.chrOffset tkn := token.NUMBER if decimalPoint { offset-- self.scanMantissa(10) goto exponent } if self.chr == '0' { offset := self.chrOffset self.read() if self.chr == 'x' || self.chr == 'X' { // Hexadecimal self.read() if isDigit(self.chr, 16) { self.read() } else { return token.ILLEGAL, self.str[offset:self.chrOffset] } self.scanMantissa(16) if self.chrOffset-offset <= 2 { // Only "0x" or "0X" self.error(0, "Illegal hexadecimal number") } goto hexadecimal } else if self.chr == '.' { // Float goto float } else { // Octal, Float if self.chr == 'e' || self.chr == 'E' { goto exponent } self.scanMantissa(8) if self.chr == '8' || self.chr == '9' { return token.ILLEGAL, self.str[offset:self.chrOffset] } goto octal } } self.scanMantissa(10) float: if self.chr == '.' { self.read() self.scanMantissa(10) } exponent: if self.chr == 'e' || self.chr == 'E' { self.read() if self.chr == '-' || self.chr == '+' { self.read() } if isDecimalDigit(self.chr) { self.read() self.scanMantissa(10) } else { return token.ILLEGAL, self.str[offset:self.chrOffset] } } hexadecimal: octal: if isIdentifierStart(self.chr) || isDecimalDigit(self.chr) { return token.ILLEGAL, self.str[offset:self.chrOffset] } return tkn, self.str[offset:self.chrOffset] } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/parser/lexer_test.go000066400000000000000000000147011331145703200264010ustar00rootroot00000000000000package parser import ( "testing" "github.com/robertkrimen/otto/file" "github.com/robertkrimen/otto/terst" "github.com/robertkrimen/otto/token" ) var tt = terst.Terst var is = terst.Is func TestLexer(t *testing.T) { tt(t, func() { setup := func(src string) *_parser { parser := _newParser("", src, 1, nil) return parser } test := func(src string, test ...interface{}) { parser := setup(src) for len(test) > 0 { tkn, literal, idx := parser.scan() if len(test) > 0 { is(tkn, test[0].(token.Token)) test = test[1:] } if len(test) > 0 { is(literal, test[0].(string)) test = test[1:] } if len(test) > 0 { // FIXME terst, Fix this so that cast to file.Idx is not necessary? is(idx, file.Idx(test[0].(int))) test = test[1:] } } } test("", token.EOF, "", 1, ) test("1", token.NUMBER, "1", 1, token.EOF, "", 2, ) test(".0", token.NUMBER, ".0", 1, token.EOF, "", 3, ) test("abc", token.IDENTIFIER, "abc", 1, token.EOF, "", 4, ) test("abc(1)", token.IDENTIFIER, "abc", 1, token.LEFT_PARENTHESIS, "", 4, token.NUMBER, "1", 5, token.RIGHT_PARENTHESIS, "", 6, token.EOF, "", 7, ) test(".", token.PERIOD, "", 1, token.EOF, "", 2, ) test("===.", token.STRICT_EQUAL, "", 1, token.PERIOD, "", 4, token.EOF, "", 5, ) test(">>>=.0", token.UNSIGNED_SHIFT_RIGHT_ASSIGN, "", 1, token.NUMBER, ".0", 5, token.EOF, "", 7, ) test(">>>=0.0.", token.UNSIGNED_SHIFT_RIGHT_ASSIGN, "", 1, token.NUMBER, "0.0", 5, token.PERIOD, "", 8, token.EOF, "", 9, ) test("\"abc\"", token.STRING, "\"abc\"", 1, token.EOF, "", 6, ) test("abc = //", token.IDENTIFIER, "abc", 1, token.ASSIGN, "", 5, token.EOF, "", 9, ) test("abc = /*test*/", token.IDENTIFIER, "abc", 1, token.ASSIGN, "", 5, token.EOF, "", 15, ) test("abc = 1 / 2", token.IDENTIFIER, "abc", 1, token.ASSIGN, "", 5, token.NUMBER, "1", 7, token.SLASH, "", 9, token.NUMBER, "2", 11, token.EOF, "", 12, ) test("xyzzy = 'Nothing happens.'", token.IDENTIFIER, "xyzzy", 1, token.ASSIGN, "", 7, token.STRING, "'Nothing happens.'", 9, token.EOF, "", 27, ) test("abc = !false", token.IDENTIFIER, "abc", 1, token.ASSIGN, "", 5, token.NOT, "", 7, token.BOOLEAN, "false", 8, token.EOF, "", 13, ) test("abc = !!true", token.IDENTIFIER, "abc", 1, token.ASSIGN, "", 5, token.NOT, "", 7, token.NOT, "", 8, token.BOOLEAN, "true", 9, token.EOF, "", 13, ) test("abc *= 1", token.IDENTIFIER, "abc", 1, token.MULTIPLY_ASSIGN, "", 5, token.NUMBER, "1", 8, token.EOF, "", 9, ) test("if 1 else", token.IF, "if", 1, token.NUMBER, "1", 4, token.ELSE, "else", 6, token.EOF, "", 10, ) test("null", token.NULL, "null", 1, token.EOF, "", 5, ) test(`"\u007a\x79\u000a\x78"`, token.STRING, "\"\\u007a\\x79\\u000a\\x78\"", 1, token.EOF, "", 23, ) test(`"[First line \ Second line \ Third line\ . ]" `, token.STRING, "\"[First line \\\nSecond line \\\n Third line\\\n. ]\"", 1, token.EOF, "", 53, ) test("/", token.SLASH, "", 1, token.EOF, "", 2, ) test("var abc = \"abc\uFFFFabc\"", token.VAR, "var", 1, token.IDENTIFIER, "abc", 5, token.ASSIGN, "", 9, token.STRING, "\"abc\uFFFFabc\"", 11, token.EOF, "", 22, ) test(`'\t' === '\r'`, token.STRING, "'\\t'", 1, token.STRICT_EQUAL, "", 6, token.STRING, "'\\r'", 10, token.EOF, "", 14, ) test(`var \u0024 = 1`, token.VAR, "var", 1, token.IDENTIFIER, "$", 5, token.ASSIGN, "", 12, token.NUMBER, "1", 14, token.EOF, "", 15, ) test("10e10000", token.NUMBER, "10e10000", 1, token.EOF, "", 9, ) test(`var if var class`, token.VAR, "var", 1, token.IF, "if", 5, token.VAR, "var", 8, token.KEYWORD, "class", 12, token.EOF, "", 17, ) test(`-0`, token.MINUS, "", 1, token.NUMBER, "0", 2, token.EOF, "", 3, ) test(`.01`, token.NUMBER, ".01", 1, token.EOF, "", 4, ) test(`.01e+2`, token.NUMBER, ".01e+2", 1, token.EOF, "", 7, ) test(";", token.SEMICOLON, "", 1, token.EOF, "", 2, ) test(";;", token.SEMICOLON, "", 1, token.SEMICOLON, "", 2, token.EOF, "", 3, ) test("//", token.EOF, "", 3, ) test(";;//test", token.SEMICOLON, "", 1, token.SEMICOLON, "", 2, token.EOF, "", 9, ) test("1", token.NUMBER, "1", 1, ) test("12 123", token.NUMBER, "12", 1, token.NUMBER, "123", 4, ) test("1.2 12.3", token.NUMBER, "1.2", 1, token.NUMBER, "12.3", 5, ) test("/ /=", token.SLASH, "", 1, token.QUOTIENT_ASSIGN, "", 3, ) test(`"abc"`, token.STRING, `"abc"`, 1, ) test(`'abc'`, token.STRING, `'abc'`, 1, ) test("++", token.INCREMENT, "", 1, ) test(">", token.GREATER, "", 1, ) test(">=", token.GREATER_OR_EQUAL, "", 1, ) test(">>", token.SHIFT_RIGHT, "", 1, ) test(">>=", token.SHIFT_RIGHT_ASSIGN, "", 1, ) test(">>>", token.UNSIGNED_SHIFT_RIGHT, "", 1, ) test(">>>=", token.UNSIGNED_SHIFT_RIGHT_ASSIGN, "", 1, ) test("1 \"abc\"", token.NUMBER, "1", 1, token.STRING, "\"abc\"", 3, ) test(",", token.COMMA, "", 1, ) test("1, \"abc\"", token.NUMBER, "1", 1, token.COMMA, "", 2, token.STRING, "\"abc\"", 4, ) test("new abc(1, 3.14159);", token.NEW, "new", 1, token.IDENTIFIER, "abc", 5, token.LEFT_PARENTHESIS, "", 8, token.NUMBER, "1", 9, token.COMMA, "", 10, token.NUMBER, "3.14159", 12, token.RIGHT_PARENTHESIS, "", 19, token.SEMICOLON, "", 20, ) test("1 == \"1\"", token.NUMBER, "1", 1, token.EQUAL, "", 3, token.STRING, "\"1\"", 6, ) test("1\n[]\n", token.NUMBER, "1", 1, token.LEFT_BRACKET, "", 3, token.RIGHT_BRACKET, "", 4, ) test("1\ufeff[]\ufeff", token.NUMBER, "1", 1, token.LEFT_BRACKET, "", 5, token.RIGHT_BRACKET, "", 6, ) // ILLEGAL test(`3ea`, token.ILLEGAL, "3e", 1, token.IDENTIFIER, "a", 3, token.EOF, "", 4, ) test(`3in`, token.ILLEGAL, "3", 1, token.IN, "in", 2, token.EOF, "", 4, ) test("\"Hello\nWorld\"", token.ILLEGAL, "", 1, token.IDENTIFIER, "World", 8, token.ILLEGAL, "", 13, token.EOF, "", 14, ) test("\u203f = 10", token.ILLEGAL, "", 1, token.ASSIGN, "", 5, token.NUMBER, "10", 7, token.EOF, "", 9, ) test(`"\x0G"`, token.STRING, "\"\\x0G\"", 1, token.EOF, "", 7, ) }) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/parser/marshal_test.go000066400000000000000000000352141331145703200267130ustar00rootroot00000000000000package parser import ( "bytes" "encoding/json" "fmt" "os" "reflect" "strings" "testing" "github.com/robertkrimen/otto/ast" ) func marshal(name string, children ...interface{}) interface{} { if len(children) == 1 { if name == "" { return testMarshalNode(children[0]) } return map[string]interface{}{ name: children[0], } } map_ := map[string]interface{}{} length := len(children) / 2 for i := 0; i < length; i++ { name := children[i*2].(string) value := children[i*2+1] map_[name] = value } if name == "" { return map_ } return map[string]interface{}{ name: map_, } } func testMarshalNode(node interface{}) interface{} { switch node := node.(type) { // Expression case *ast.ArrayLiteral: return marshal("Array", testMarshalNode(node.Value)) case *ast.AssignExpression: return marshal("Assign", "Left", testMarshalNode(node.Left), "Right", testMarshalNode(node.Right), ) case *ast.BinaryExpression: return marshal("BinaryExpression", "Operator", node.Operator.String(), "Left", testMarshalNode(node.Left), "Right", testMarshalNode(node.Right), ) case *ast.BooleanLiteral: return marshal("Literal", node.Value) case *ast.CallExpression: return marshal("Call", "Callee", testMarshalNode(node.Callee), "ArgumentList", testMarshalNode(node.ArgumentList), ) case *ast.ConditionalExpression: return marshal("Conditional", "Test", testMarshalNode(node.Test), "Consequent", testMarshalNode(node.Consequent), "Alternate", testMarshalNode(node.Alternate), ) case *ast.DotExpression: return marshal("Dot", "Left", testMarshalNode(node.Left), "Member", node.Identifier.Name, ) case *ast.NewExpression: return marshal("New", "Callee", testMarshalNode(node.Callee), "ArgumentList", testMarshalNode(node.ArgumentList), ) case *ast.NullLiteral: return marshal("Literal", nil) case *ast.NumberLiteral: return marshal("Literal", node.Value) case *ast.ObjectLiteral: return marshal("Object", testMarshalNode(node.Value)) case *ast.RegExpLiteral: return marshal("Literal", node.Literal) case *ast.StringLiteral: return marshal("Literal", node.Literal) case *ast.VariableExpression: return []interface{}{node.Name, testMarshalNode(node.Initializer)} // Statement case *ast.Program: return testMarshalNode(node.Body) case *ast.BlockStatement: return marshal("BlockStatement", testMarshalNode(node.List)) case *ast.EmptyStatement: return "EmptyStatement" case *ast.ExpressionStatement: return testMarshalNode(node.Expression) case *ast.ForInStatement: return marshal("ForIn", "Into", marshal("", node.Into), "Source", marshal("", node.Source), "Body", marshal("", node.Body), ) case *ast.FunctionLiteral: return marshal("Function", testMarshalNode(node.Body)) case *ast.Identifier: return marshal("Identifier", node.Name) case *ast.IfStatement: if_ := marshal("", "Test", testMarshalNode(node.Test), "Consequent", testMarshalNode(node.Consequent), ).(map[string]interface{}) if node.Alternate != nil { if_["Alternate"] = testMarshalNode(node.Alternate) } return marshal("If", if_) case *ast.LabelledStatement: return marshal("Label", "Name", node.Label.Name, "Statement", testMarshalNode(node.Statement), ) case ast.Property: return marshal("", "Key", node.Key, "Value", testMarshalNode(node.Value), ) case *ast.ReturnStatement: return marshal("Return", testMarshalNode(node.Argument)) case *ast.SequenceExpression: return marshal("Sequence", testMarshalNode(node.Sequence)) case *ast.ThrowStatement: return marshal("Throw", testMarshalNode(node.Argument)) case *ast.VariableStatement: return marshal("Var", testMarshalNode(node.List)) } { value := reflect.ValueOf(node) if value.Kind() == reflect.Slice { tmp0 := []interface{}{} for index := 0; index < value.Len(); index++ { tmp0 = append(tmp0, testMarshalNode(value.Index(index).Interface())) } return tmp0 } } if node != nil { fmt.Fprintf(os.Stderr, "testMarshalNode(%T)\n", node) } return nil } func testMarshal(node interface{}) string { value, err := json.Marshal(testMarshalNode(node)) if err != nil { panic(err) } return string(value) } func TestParserAST(t *testing.T) { tt(t, func() { test := func(inputOutput string) { match := matchBeforeAfterSeparator.FindStringIndex(inputOutput) input := strings.TrimSpace(inputOutput[0:match[0]]) wantOutput := strings.TrimSpace(inputOutput[match[1]:]) _, program, err := testParse(input) is(err, nil) haveOutput := testMarshal(program) tmp0, tmp1 := bytes.Buffer{}, bytes.Buffer{} json.Indent(&tmp0, []byte(haveOutput), "\t\t", " ") json.Indent(&tmp1, []byte(wantOutput), "\t\t", " ") is("\n\t\t"+tmp0.String(), "\n\t\t"+tmp1.String()) } test(` --- [] `) test(` ; --- [ "EmptyStatement" ] `) test(` ;;; --- [ "EmptyStatement", "EmptyStatement", "EmptyStatement" ] `) test(` 1; true; abc; "abc"; null; --- [ { "Literal": 1 }, { "Literal": true }, { "Identifier": "abc" }, { "Literal": "\"abc\"" }, { "Literal": null } ] `) test(` { 1; null; 3.14159; ; } --- [ { "BlockStatement": [ { "Literal": 1 }, { "Literal": null }, { "Literal": 3.14159 }, "EmptyStatement" ] } ] `) test(` new abc(); --- [ { "New": { "ArgumentList": [], "Callee": { "Identifier": "abc" } } } ] `) test(` new abc(1, 3.14159) --- [ { "New": { "ArgumentList": [ { "Literal": 1 }, { "Literal": 3.14159 } ], "Callee": { "Identifier": "abc" } } } ] `) test(` true ? false : true --- [ { "Conditional": { "Alternate": { "Literal": true }, "Consequent": { "Literal": false }, "Test": { "Literal": true } } } ] `) test(` true || false --- [ { "BinaryExpression": { "Left": { "Literal": true }, "Operator": "||", "Right": { "Literal": false } } } ] `) test(` 0 + { abc: true } --- [ { "BinaryExpression": { "Left": { "Literal": 0 }, "Operator": "+", "Right": { "Object": [ { "Key": "abc", "Value": { "Literal": true } } ] } } } ] `) test(` 1 == "1" --- [ { "BinaryExpression": { "Left": { "Literal": 1 }, "Operator": "==", "Right": { "Literal": "\"1\"" } } } ] `) test(` abc(1) --- [ { "Call": { "ArgumentList": [ { "Literal": 1 } ], "Callee": { "Identifier": "abc" } } } ] `) test(` Math.pow(3, 2) --- [ { "Call": { "ArgumentList": [ { "Literal": 3 }, { "Literal": 2 } ], "Callee": { "Dot": { "Left": { "Identifier": "Math" }, "Member": "pow" } } } } ] `) test(` 1, 2, 3 --- [ { "Sequence": [ { "Literal": 1 }, { "Literal": 2 }, { "Literal": 3 } ] } ] `) test(` / abc / gim; --- [ { "Literal": "/ abc / gim" } ] `) test(` if (0) 1; --- [ { "If": { "Consequent": { "Literal": 1 }, "Test": { "Literal": 0 } } } ] `) test(` 0+function(){ return; } --- [ { "BinaryExpression": { "Left": { "Literal": 0 }, "Operator": "+", "Right": { "Function": { "BlockStatement": [ { "Return": null } ] } } } } ] `) test(` xyzzy // Ignore it // Ignore this // And this /* And all.. ... of this! */ "Nothing happens." // And finally this --- [ { "Identifier": "xyzzy" }, { "Literal": "\"Nothing happens.\"" } ] `) test(` ((x & (x = 1)) !== 0) --- [ { "BinaryExpression": { "Left": { "BinaryExpression": { "Left": { "Identifier": "x" }, "Operator": "\u0026", "Right": { "Assign": { "Left": { "Identifier": "x" }, "Right": { "Literal": 1 } } } } }, "Operator": "!==", "Right": { "Literal": 0 } } } ] `) test(` { abc: 'def' } --- [ { "BlockStatement": [ { "Label": { "Name": "abc", "Statement": { "Literal": "'def'" } } } ] } ] `) test(` // This is not an object, this is a string literal with a label! ({ abc: 'def' }) --- [ { "Object": [ { "Key": "abc", "Value": { "Literal": "'def'" } } ] } ] `) test(` [,] --- [ { "Array": [ null ] } ] `) test(` [,,] --- [ { "Array": [ null, null ] } ] `) test(` ({ get abc() {} }) --- [ { "Object": [ { "Key": "abc", "Value": { "Function": { "BlockStatement": [] } } } ] } ] `) test(` /abc/.source --- [ { "Dot": { "Left": { "Literal": "/abc/" }, "Member": "source" } } ] `) test(` xyzzy throw new TypeError("Nothing happens.") --- [ { "Identifier": "xyzzy" }, { "Throw": { "New": { "ArgumentList": [ { "Literal": "\"Nothing happens.\"" } ], "Callee": { "Identifier": "TypeError" } } } } ] `) // When run, this will call a type error to be thrown // This is essentially the same as: // // var abc = 1(function(){})() // test(` var abc = 1 (function(){ })() --- [ { "Var": [ [ "abc", { "Call": { "ArgumentList": [], "Callee": { "Call": { "ArgumentList": [ { "Function": { "BlockStatement": [] } } ], "Callee": { "Literal": 1 } } } } } ] ] } ] `) test(` "use strict" --- [ { "Literal": "\"use strict\"" } ] `) test(` "use strict" abc = 1 + 2 + 11 --- [ { "Literal": "\"use strict\"" }, { "Assign": { "Left": { "Identifier": "abc" }, "Right": { "BinaryExpression": { "Left": { "BinaryExpression": { "Left": { "Literal": 1 }, "Operator": "+", "Right": { "Literal": 2 } } }, "Operator": "+", "Right": { "Literal": 11 } } } } } ] `) test(` abc = function() { 'use strict' } --- [ { "Assign": { "Left": { "Identifier": "abc" }, "Right": { "Function": { "BlockStatement": [ { "Literal": "'use strict'" } ] } } } } ] `) test(` for (var abc in def) { } --- [ { "ForIn": { "Body": { "BlockStatement": [] }, "Into": [ "abc", null ], "Source": { "Identifier": "def" } } } ] `) test(` abc = { '"': "'", "'": '"', } --- [ { "Assign": { "Left": { "Identifier": "abc" }, "Right": { "Object": [ { "Key": "\"", "Value": { "Literal": "\"'\"" } }, { "Key": "'", "Value": { "Literal": "'\"'" } } ] } } } ] `) return test(` if (!abc && abc.jkl(def) && abc[0] === +abc[0] && abc.length < ghi) { } --- [ { "If": { "Consequent": { "BlockStatement": [] }, "Test": { "BinaryExpression": { "Left": { "BinaryExpression": { "Left": { "BinaryExpression": { "Left": null, "Operator": "\u0026\u0026", "Right": { "Call": { "ArgumentList": [ { "Identifier": "def" } ], "Callee": { "Dot": { "Left": { "Identifier": "abc" }, "Member": "jkl" } } } } } }, "Operator": "\u0026\u0026", "Right": { "BinaryExpression": { "Left": null, "Operator": "===", "Right": null } } } }, "Operator": "\u0026\u0026", "Right": { "BinaryExpression": { "Left": { "Dot": { "Left": { "Identifier": "abc" }, "Member": "length" } }, "Operator": "\u003c", "Right": { "Identifier": "ghi" } } } } } } } ] `) }) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/parser/parser.go000066400000000000000000000203411331145703200255140ustar00rootroot00000000000000/* Package parser implements a parser for JavaScript. import ( "github.com/robertkrimen/otto/parser" ) Parse and return an AST filename := "" // A filename is optional src := ` // Sample xyzzy example (function(){ if (3.14159 > 0) { console.log("Hello, World."); return; } var xyzzy = NaN; console.log("Nothing happens."); return xyzzy; })(); ` // Parse some JavaScript, yielding a *ast.Program and/or an ErrorList program, err := parser.ParseFile(nil, filename, src, 0) Warning The parser and AST interfaces are still works-in-progress (particularly where node types are concerned) and may change in the future. */ package parser import ( "bytes" "encoding/base64" "errors" "io" "io/ioutil" "github.com/robertkrimen/otto/ast" "github.com/robertkrimen/otto/file" "github.com/robertkrimen/otto/token" "gopkg.in/sourcemap.v1" ) // A Mode value is a set of flags (or 0). They control optional parser functionality. type Mode uint const ( IgnoreRegExpErrors Mode = 1 << iota // Ignore RegExp compatibility errors (allow backtracking) StoreComments // Store the comments from source to the comments map ) type _parser struct { str string length int base int chr rune // The current character chrOffset int // The offset of current character offset int // The offset after current character (may be greater than 1) idx file.Idx // The index of token token token.Token // The token literal string // The literal of the token, if any scope *_scope insertSemicolon bool // If we see a newline, then insert an implicit semicolon implicitSemicolon bool // An implicit semicolon exists errors ErrorList recover struct { // Scratch when trying to seek to the next statement, etc. idx file.Idx count int } mode Mode file *file.File comments *ast.Comments } type Parser interface { Scan() (tkn token.Token, literal string, idx file.Idx) } func _newParser(filename, src string, base int, sm *sourcemap.Consumer) *_parser { return &_parser{ chr: ' ', // This is set so we can start scanning by skipping whitespace str: src, length: len(src), base: base, file: file.NewFile(filename, src, base).WithSourceMap(sm), comments: ast.NewComments(), } } // Returns a new Parser. func NewParser(filename, src string) Parser { return _newParser(filename, src, 1, nil) } func ReadSource(filename string, src interface{}) ([]byte, error) { if src != nil { switch src := src.(type) { case string: return []byte(src), nil case []byte: return src, nil case *bytes.Buffer: if src != nil { return src.Bytes(), nil } case io.Reader: var bfr bytes.Buffer if _, err := io.Copy(&bfr, src); err != nil { return nil, err } return bfr.Bytes(), nil } return nil, errors.New("invalid source") } return ioutil.ReadFile(filename) } func ReadSourceMap(filename string, src interface{}) (*sourcemap.Consumer, error) { if src == nil { return nil, nil } switch src := src.(type) { case string: return sourcemap.Parse(filename, []byte(src)) case []byte: return sourcemap.Parse(filename, src) case *bytes.Buffer: if src != nil { return sourcemap.Parse(filename, src.Bytes()) } case io.Reader: var bfr bytes.Buffer if _, err := io.Copy(&bfr, src); err != nil { return nil, err } return sourcemap.Parse(filename, bfr.Bytes()) case *sourcemap.Consumer: return src, nil } return nil, errors.New("invalid sourcemap type") } func ParseFileWithSourceMap(fileSet *file.FileSet, filename string, javascriptSource, sourcemapSource interface{}, mode Mode) (*ast.Program, error) { src, err := ReadSource(filename, javascriptSource) if err != nil { return nil, err } if sourcemapSource == nil { lines := bytes.Split(src, []byte("\n")) lastLine := lines[len(lines)-1] if bytes.HasPrefix(lastLine, []byte("//# sourceMappingURL=data:application/json")) { bits := bytes.SplitN(lastLine, []byte(","), 2) if len(bits) == 2 { if d, err := base64.StdEncoding.DecodeString(string(bits[1])); err == nil { sourcemapSource = d } } } } sm, err := ReadSourceMap(filename, sourcemapSource) if err != nil { return nil, err } base := 1 if fileSet != nil { base = fileSet.AddFile(filename, string(src)) } parser := _newParser(filename, string(src), base, sm) parser.mode = mode program, err := parser.parse() program.Comments = parser.comments.CommentMap return program, err } // ParseFile parses the source code of a single JavaScript/ECMAScript source file and returns // the corresponding ast.Program node. // // If fileSet == nil, ParseFile parses source without a FileSet. // If fileSet != nil, ParseFile first adds filename and src to fileSet. // // The filename argument is optional and is used for labelling errors, etc. // // src may be a string, a byte slice, a bytes.Buffer, or an io.Reader, but it MUST always be in UTF-8. // // // Parse some JavaScript, yielding a *ast.Program and/or an ErrorList // program, err := parser.ParseFile(nil, "", `if (abc > 1) {}`, 0) // func ParseFile(fileSet *file.FileSet, filename string, src interface{}, mode Mode) (*ast.Program, error) { return ParseFileWithSourceMap(fileSet, filename, src, nil, mode) } // ParseFunction parses a given parameter list and body as a function and returns the // corresponding ast.FunctionLiteral node. // // The parameter list, if any, should be a comma-separated list of identifiers. // func ParseFunction(parameterList, body string) (*ast.FunctionLiteral, error) { src := "(function(" + parameterList + ") {\n" + body + "\n})" parser := _newParser("", src, 1, nil) program, err := parser.parse() if err != nil { return nil, err } return program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.FunctionLiteral), nil } // Scan reads a single token from the source at the current offset, increments the offset and // returns the token.Token token, a string literal representing the value of the token (if applicable) // and it's current file.Idx index. func (self *_parser) Scan() (tkn token.Token, literal string, idx file.Idx) { return self.scan() } func (self *_parser) slice(idx0, idx1 file.Idx) string { from := int(idx0) - self.base to := int(idx1) - self.base if from >= 0 && to <= len(self.str) { return self.str[from:to] } return "" } func (self *_parser) parse() (*ast.Program, error) { self.next() program := self.parseProgram() if false { self.errors.Sort() } if self.mode&StoreComments != 0 { self.comments.CommentMap.AddComments(program, self.comments.FetchAll(), ast.TRAILING) } return program, self.errors.Err() } func (self *_parser) next() { self.token, self.literal, self.idx = self.scan() } func (self *_parser) optionalSemicolon() { if self.token == token.SEMICOLON { self.next() return } if self.implicitSemicolon { self.implicitSemicolon = false return } if self.token != token.EOF && self.token != token.RIGHT_BRACE { self.expect(token.SEMICOLON) } } func (self *_parser) semicolon() { if self.token != token.RIGHT_PARENTHESIS && self.token != token.RIGHT_BRACE { if self.implicitSemicolon { self.implicitSemicolon = false return } self.expect(token.SEMICOLON) } } func (self *_parser) idxOf(offset int) file.Idx { return file.Idx(self.base + offset) } func (self *_parser) expect(value token.Token) file.Idx { idx := self.idx if self.token != value { self.errorUnexpectedToken(self.token) } self.next() return idx } func lineCount(str string) (int, int) { line, last := 0, -1 pair := false for index, chr := range str { switch chr { case '\r': line += 1 last = index pair = true continue case '\n': if !pair { line += 1 } last = index case '\u2028', '\u2029': line += 1 last = index + 2 } pair = false } return line, last } func (self *_parser) position(idx file.Idx) file.Position { position := file.Position{} offset := int(idx) - self.base str := self.str[:offset] position.Filename = self.file.Name() line, last := lineCount(str) position.Line = 1 + line if last >= 0 { position.Column = offset - last } else { position.Column = 1 + len(str) } return position } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/parser/parser_test.go000066400000000000000000000661661331145703200265720ustar00rootroot00000000000000package parser import ( "errors" "regexp" "strings" "testing" "github.com/robertkrimen/otto/ast" "github.com/robertkrimen/otto/file" "github.com/robertkrimen/otto/underscore" ) func firstErr(err error) error { switch err := err.(type) { case ErrorList: return err[0] } return err } var matchBeforeAfterSeparator = regexp.MustCompile(`(?m)^[ \t]*---$`) func testParse(src string) (parser *_parser, program *ast.Program, err error) { return testParseWithMode(src, 0) } func testParseWithMode(src string, mode Mode) (parser *_parser, program *ast.Program, err error) { defer func() { if tmp := recover(); tmp != nil { switch tmp := tmp.(type) { case string: if strings.HasPrefix(tmp, "SyntaxError:") { parser = nil program = nil err = errors.New(tmp) return } } panic(tmp) } }() parser = _newParser("", src, 1, nil) parser.mode = mode program, err = parser.parse() return } func TestParseFile(t *testing.T) { tt(t, func() { _, err := ParseFile(nil, "", `/abc/`, 0) is(err, nil) _, err = ParseFile(nil, "", `/(?!def)abc/`, IgnoreRegExpErrors) is(err, nil) _, err = ParseFile(nil, "", `/(?!def)abc/`, 0) is(err, "(anonymous): Line 1:1 Invalid regular expression: re2: Invalid (?!) ") _, err = ParseFile(nil, "", `/(?!def)abc/; return`, IgnoreRegExpErrors) is(err, "(anonymous): Line 1:15 Illegal return statement") _, err = ParseFile(nil, "/make-sure-file-path-is-returned-not-anonymous", `a..`, 0) is(err, "/make-sure-file-path-is-returned-not-anonymous: Line 1:3 Unexpected token .") }) } func TestParseFunction(t *testing.T) { tt(t, func() { test := func(prm, bdy string, expect interface{}) *ast.FunctionLiteral { function, err := ParseFunction(prm, bdy) is(firstErr(err), expect) return function } test("a, b,c,d", "", nil) test("a, b;,c,d", "", "(anonymous): Line 1:15 Unexpected token ;") test("this", "", "(anonymous): Line 1:11 Unexpected token this") test("a, b, c, null", "", "(anonymous): Line 1:20 Unexpected token null") test("a, b,c,d", "return;", nil) test("a, b,c,d", "break;", "(anonymous): Line 2:1 Illegal break statement") test("a, b,c,d", "{}", nil) }) } func TestParserErr(t *testing.T) { tt(t, func() { test := func(input string, expect interface{}) (*ast.Program, *_parser) { parser := _newParser("", input, 1, nil) program, err := parser.parse() is(firstErr(err), expect) return program, parser } program, parser := test("", nil) program, parser = test(` var abc; break; do { } while(true); `, "(anonymous): Line 3:9 Illegal break statement") { stmt := program.Body[1].(*ast.BadStatement) is(parser.position(stmt.From).Column, 9) is(parser.position(stmt.To).Column, 16) is(parser.slice(stmt.From, stmt.To), "break; ") } test("{", "(anonymous): Line 1:2 Unexpected end of input") test("}", "(anonymous): Line 1:1 Unexpected token }") test("3ea", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("3in", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("3in []", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("3e", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("3e+", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("3e-", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("3x", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("3x0", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("0x", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("09", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("018", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("01.0", "(anonymous): Line 1:3 Unexpected number") test("01a", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("0x3in[]", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("\"Hello\nWorld\"", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("\u203f = 10", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("x\\", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("x\\\\", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("x\\u005c", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("x\\u002a", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("x\\\\u002a", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("/\n", "(anonymous): Line 1:1 Invalid regular expression: missing /") test("var x = /(s/g", "(anonymous): Line 1:9 Invalid regular expression: Unterminated group") test("0 = 1", "(anonymous): Line 1:1 Invalid left-hand side in assignment") test("func() = 1", "(anonymous): Line 1:1 Invalid left-hand side in assignment") test("(1 + 1) = 2", "(anonymous): Line 1:2 Invalid left-hand side in assignment") test("1++", "(anonymous): Line 1:2 Invalid left-hand side in assignment") test("1--", "(anonymous): Line 1:2 Invalid left-hand side in assignment") test("--1", "(anonymous): Line 1:1 Invalid left-hand side in assignment") test("for((1 + 1) in abc) def();", "(anonymous): Line 1:1 Invalid left-hand side in for-in") test("[", "(anonymous): Line 1:2 Unexpected end of input") test("[,", "(anonymous): Line 1:3 Unexpected end of input") test("1 + {", "(anonymous): Line 1:6 Unexpected end of input") test("1 + { abc:abc", "(anonymous): Line 1:14 Unexpected end of input") test("1 + { abc:abc,", "(anonymous): Line 1:15 Unexpected end of input") test("var abc = /\n/", "(anonymous): Line 1:11 Invalid regular expression: missing /") test("var abc = \"\n", "(anonymous): Line 1:11 Unexpected token ILLEGAL") test("var if = 0", "(anonymous): Line 1:5 Unexpected token if") test("abc + 0 = 1", "(anonymous): Line 1:1 Invalid left-hand side in assignment") test("+abc = 1", "(anonymous): Line 1:1 Invalid left-hand side in assignment") test("1 + (", "(anonymous): Line 1:6 Unexpected end of input") test("\n\n\n{", "(anonymous): Line 4:2 Unexpected end of input") test("\n/* Some multiline\ncomment */\n)", "(anonymous): Line 4:1 Unexpected token )") // TODO //{ set 1 } //{ get 2 } //({ set: s(if) { } }) //({ set s(.) { } }) //({ set: s() { } }) //({ set: s(a, b) { } }) //({ get: g(d) { } }) //({ get i() { }, i: 42 }) //({ i: 42, get i() { } }) //({ set i(x) { }, i: 42 }) //({ i: 42, set i(x) { } }) //({ get i() { }, get i() { } }) //({ set i(x) { }, set i(x) { } }) test("function abc(if) {}", "(anonymous): Line 1:14 Unexpected token if") test("function abc(true) {}", "(anonymous): Line 1:14 Unexpected token true") test("function abc(false) {}", "(anonymous): Line 1:14 Unexpected token false") test("function abc(null) {}", "(anonymous): Line 1:14 Unexpected token null") test("function null() {}", "(anonymous): Line 1:10 Unexpected token null") test("function true() {}", "(anonymous): Line 1:10 Unexpected token true") test("function false() {}", "(anonymous): Line 1:10 Unexpected token false") test("function if() {}", "(anonymous): Line 1:10 Unexpected token if") test("a b;", "(anonymous): Line 1:3 Unexpected identifier") test("if.a", "(anonymous): Line 1:3 Unexpected token .") test("a if", "(anonymous): Line 1:3 Unexpected token if") test("a class", "(anonymous): Line 1:3 Unexpected reserved word") test("break\n", "(anonymous): Line 1:1 Illegal break statement") test("break 1;", "(anonymous): Line 1:7 Unexpected number") test("for (;;) { break 1; }", "(anonymous): Line 1:18 Unexpected number") test("continue\n", "(anonymous): Line 1:1 Illegal continue statement") test("continue 1;", "(anonymous): Line 1:10 Unexpected number") test("for (;;) { continue 1; }", "(anonymous): Line 1:21 Unexpected number") test("throw", "(anonymous): Line 1:1 Unexpected end of input") test("throw;", "(anonymous): Line 1:6 Unexpected token ;") test("throw \n", "(anonymous): Line 1:1 Unexpected end of input") test("for (var abc, def in {});", "(anonymous): Line 1:19 Unexpected token in") test("for ((abc in {});;);", nil) test("for ((abc in {}));", "(anonymous): Line 1:17 Unexpected token )") test("for (+abc in {});", "(anonymous): Line 1:1 Invalid left-hand side in for-in") test("if (false)", "(anonymous): Line 1:11 Unexpected end of input") test("if (false) abc(); else", "(anonymous): Line 1:23 Unexpected end of input") test("do", "(anonymous): Line 1:3 Unexpected end of input") test("while (false)", "(anonymous): Line 1:14 Unexpected end of input") test("for (;;)", "(anonymous): Line 1:9 Unexpected end of input") test("with (abc)", "(anonymous): Line 1:11 Unexpected end of input") test("try {}", "(anonymous): Line 1:1 Missing catch or finally after try") test("try {} catch {}", "(anonymous): Line 1:14 Unexpected token {") test("try {} catch () {}", "(anonymous): Line 1:15 Unexpected token )") test("\u203f = 1", "(anonymous): Line 1:1 Unexpected token ILLEGAL") // TODO // const x = 12, y; // const x, y = 12; // const x; // if(true) let a = 1; // if(true) const a = 1; test(`new abc()."def"`, "(anonymous): Line 1:11 Unexpected string") test("/*", "(anonymous): Line 1:3 Unexpected end of input") test("/**", "(anonymous): Line 1:4 Unexpected end of input") test("/*\n\n\n", "(anonymous): Line 4:1 Unexpected end of input") test("/*\n\n\n*", "(anonymous): Line 4:2 Unexpected end of input") test("/*abc", "(anonymous): Line 1:6 Unexpected end of input") test("/*abc *", "(anonymous): Line 1:9 Unexpected end of input") test("\n]", "(anonymous): Line 2:1 Unexpected token ]") test("\r\n]", "(anonymous): Line 2:1 Unexpected token ]") test("\n\r]", "(anonymous): Line 3:1 Unexpected token ]") test("//\r\n]", "(anonymous): Line 2:1 Unexpected token ]") test("//\n\r]", "(anonymous): Line 3:1 Unexpected token ]") test("/abc\\\n/", "(anonymous): Line 1:1 Invalid regular expression: missing /") test("//\r \n]", "(anonymous): Line 3:1 Unexpected token ]") test("/*\r\n*/]", "(anonymous): Line 2:3 Unexpected token ]") test("/*\r \n*/]", "(anonymous): Line 3:3 Unexpected token ]") test("\\\\", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("\\u005c", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("\\abc", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("\\u0000", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("\\u200c = []", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("\\u200D = []", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test(`"\`, "(anonymous): Line 1:1 Unexpected token ILLEGAL") test(`"\u`, "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("return", "(anonymous): Line 1:1 Illegal return statement") test("continue", "(anonymous): Line 1:1 Illegal continue statement") test("break", "(anonymous): Line 1:1 Illegal break statement") test("switch (abc) { default: continue; }", "(anonymous): Line 1:25 Illegal continue statement") test("do { abc } *", "(anonymous): Line 1:12 Unexpected token *") test("while (true) { break abc; }", "(anonymous): Line 1:16 Undefined label 'abc'") test("while (true) { continue abc; }", "(anonymous): Line 1:16 Undefined label 'abc'") test("abc: while (true) { (function(){ break abc; }); }", "(anonymous): Line 1:34 Undefined label 'abc'") test("abc: while (true) { (function(){ abc: break abc; }); }", nil) test("abc: while (true) { (function(){ continue abc; }); }", "(anonymous): Line 1:34 Undefined label 'abc'") test(`abc: if (0) break abc; else {}`, nil) test(`abc: if (0) { break abc; } else {}`, nil) test(`abc: if (0) { break abc } else {}`, nil) test("abc: while (true) { abc: while (true) {} }", "(anonymous): Line 1:21 Label 'abc' already exists") if false { // TODO When strict mode is implemented test("(function () { 'use strict'; delete abc; }())", "") } test("_: _: while (true) {]", "(anonymous): Line 1:4 Label '_' already exists") test("_:\n_:\nwhile (true) {]", "(anonymous): Line 2:1 Label '_' already exists") test("_:\n _:\nwhile (true) {]", "(anonymous): Line 2:4 Label '_' already exists") test("/Xyzzy(?!Nothing happens)/", "(anonymous): Line 1:1 Invalid regular expression: re2: Invalid (?!) ") test("function(){}", "(anonymous): Line 1:9 Unexpected token (") test("\n/*/", "(anonymous): Line 2:4 Unexpected end of input") test("/*/.source", "(anonymous): Line 1:11 Unexpected end of input") test("/\\1/.source", "(anonymous): Line 1:1 Invalid regular expression: re2: Invalid \\1 ") test("var class", "(anonymous): Line 1:5 Unexpected reserved word") test("var if", "(anonymous): Line 1:5 Unexpected token if") test("object Object", "(anonymous): Line 1:8 Unexpected identifier") test("[object Object]", "(anonymous): Line 1:9 Unexpected identifier") test("\\u0xyz", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test(`for (var abc, def in {}) {}`, "(anonymous): Line 1:19 Unexpected token in") test(`for (abc, def in {}) {}`, "(anonymous): Line 1:1 Invalid left-hand side in for-in") test(`for (var abc=def, ghi=("abc" in {}); true;) {}`, nil) { // Semicolon insertion test("this\nif (1);", nil) test("while (1) { break\nif (1); }", nil) test("throw\nif (1);", "(anonymous): Line 1:1 Illegal newline after throw") test("(function(){ return\nif (1); })", nil) test("while (1) { continue\nif (1); }", nil) test("debugger\nif (1);", nil) } { // Reserved words test("class", "(anonymous): Line 1:1 Unexpected reserved word") test("abc.class = 1", nil) test("var class;", "(anonymous): Line 1:5 Unexpected reserved word") test("const", "(anonymous): Line 1:1 Unexpected reserved word") test("abc.const = 1", nil) test("var const;", "(anonymous): Line 1:5 Unexpected reserved word") test("enum", "(anonymous): Line 1:1 Unexpected reserved word") test("abc.enum = 1", nil) test("var enum;", "(anonymous): Line 1:5 Unexpected reserved word") test("export", "(anonymous): Line 1:1 Unexpected reserved word") test("abc.export = 1", nil) test("var export;", "(anonymous): Line 1:5 Unexpected reserved word") test("extends", "(anonymous): Line 1:1 Unexpected reserved word") test("abc.extends = 1", nil) test("var extends;", "(anonymous): Line 1:5 Unexpected reserved word") test("import", "(anonymous): Line 1:1 Unexpected reserved word") test("abc.import = 1", nil) test("var import;", "(anonymous): Line 1:5 Unexpected reserved word") test("super", "(anonymous): Line 1:1 Unexpected reserved word") test("abc.super = 1", nil) test("var super;", "(anonymous): Line 1:5 Unexpected reserved word") } { // Reserved words (strict) test(`implements`, nil) test(`abc.implements = 1`, nil) test(`var implements;`, nil) test(`interface`, nil) test(`abc.interface = 1`, nil) test(`var interface;`, nil) test(`let`, nil) test(`abc.let = 1`, nil) test(`var let;`, nil) test(`package`, nil) test(`abc.package = 1`, nil) test(`var package;`, nil) test(`private`, nil) test(`abc.private = 1`, nil) test(`var private;`, nil) test(`protected`, nil) test(`abc.protected = 1`, nil) test(`var protected;`, nil) test(`public`, nil) test(`abc.public = 1`, nil) test(`var public;`, nil) test(`static`, nil) test(`abc.static = 1`, nil) test(`var static;`, nil) test(`yield`, nil) test(`abc.yield = 1`, nil) test(`var yield;`, nil) } }) } func TestParser(t *testing.T) { tt(t, func() { test := func(source string, chk interface{}) *ast.Program { _, program, err := testParse(source) is(firstErr(err), chk) return program } test(` abc -- [] `, "(anonymous): Line 3:13 Invalid left-hand side in assignment") test(` abc-- [] `, nil) test("1\n[]\n", "(anonymous): Line 2:2 Unexpected token ]") test(` function abc() { } abc() `, nil) program := test("", nil) test("//", nil) test("/* */", nil) test("/** **/", nil) test("/*****/", nil) test("/*", "(anonymous): Line 1:3 Unexpected end of input") test("#", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("/**/#", "(anonymous): Line 1:5 Unexpected token ILLEGAL") test("new +", "(anonymous): Line 1:5 Unexpected token +") program = test(";", nil) is(len(program.Body), 1) is(program.Body[0].(*ast.EmptyStatement).Semicolon, file.Idx(1)) program = test(";;", nil) is(len(program.Body), 2) is(program.Body[0].(*ast.EmptyStatement).Semicolon, file.Idx(1)) is(program.Body[1].(*ast.EmptyStatement).Semicolon, file.Idx(2)) program = test("1.2", nil) is(len(program.Body), 1) is(program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.NumberLiteral).Literal, "1.2") program = test("/* */1.2", nil) is(len(program.Body), 1) is(program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.NumberLiteral).Literal, "1.2") program = test("\n", nil) is(len(program.Body), 0) test(` if (0) { abc = 0 } else abc = 0 `, nil) test("if (0) abc = 0 else abc = 0", "(anonymous): Line 1:16 Unexpected token else") test(` if (0) { abc = 0 } else abc = 0 `, nil) test(` if (0) { abc = 1 } else { } `, nil) test(` do { } while (true) `, nil) test(` try { } finally { } `, nil) test(` try { } catch (abc) { } finally { } `, nil) test(` try { } catch (abc) { } finally { } `, nil) test(`try {} catch (abc) {} finally {}`, nil) test(` do { do { } while (0) } while (0) `, nil) test(` (function(){ try { if ( 1 ) { return 1 } return 0 } finally { } })() `, nil) test("abc = ''\ndef", nil) test("abc = 1\ndef", nil) test("abc = Math\ndef", nil) test(`"\'"`, nil) test(` abc = function(){ } abc = 0 `, nil) test("abc.null = 0", nil) test("0x41", nil) test(`"\d"`, nil) test(`(function(){return this})`, nil) test(` Object.defineProperty(Array.prototype, "0", { value: 100, writable: false, configurable: true }); abc = [101]; abc.hasOwnProperty("0") && abc[0] === 101; `, nil) test(`new abc()`, nil) test(`new {}`, nil) test(` limit = 4 result = 0 while (limit) { limit = limit - 1 if (limit) { } else { break } result = result + 1 } `, nil) test(` while (0) { if (0) { continue } } `, nil) test("var \u0061\u0062\u0063 = 0", nil) // 7_3_1 test("var test7_3_1\nabc = 66;", nil) test("var test7_3_1\u2028abc = 66;", nil) // 7_3_3 test("//\u2028 =;", "(anonymous): Line 2:2 Unexpected token =") // 7_3_10 test("var abc = \u2029;", "(anonymous): Line 2:1 Unexpected token ;") test("var abc = \\u2029;", "(anonymous): Line 1:11 Unexpected token ILLEGAL") test("var \\u0061\\u0062\\u0063 = 0;", nil) test("'", "(anonymous): Line 1:1 Unexpected token ILLEGAL") test("'\nstr\ning\n'", "(anonymous): Line 1:1 Unexpected token ILLEGAL") // S7.6_A4.3_T1 test(`var $\u0030 = 0;`, nil) // S7.6.1.1_A1.1 test(`switch = 1`, "(anonymous): Line 1:8 Unexpected token =") // S7.8.3_A2.1_T1 test(`.0 === 0.0`, nil) // 7.8.5-1 test("var regExp = /\\\rn/;", "(anonymous): Line 1:14 Invalid regular expression: missing /") // S7.8.5_A1.1_T2 test("var regExp = /=/;", nil) // S7.8.5_A1.2_T1 test("/*/", "(anonymous): Line 1:4 Unexpected end of input") // Sbp_7.9_A9_T3 test(` do { ; } while (false) true `, nil) // S7.9_A10_T10 test(` {a:1 } 3 `, nil) test(` abc ++def `, nil) // S7.9_A5.2_T1 test(` for(false;false ) { break; } `, "(anonymous): Line 3:13 Unexpected token )") // S7.9_A9_T8 test(` do {}; while (false) `, "(anonymous): Line 2:18 Unexpected token ;") // S8.4_A5 test(` "x\0y" `, nil) // S9.3.1_A6_T1 test(` 10e10000 `, nil) // 10.4.2-1-5 test(` "abc\ def" `, nil) test("'\\\n'", nil) test("'\\\r\n'", nil) //// 11.13.1-1-1 test("42 = 42;", "(anonymous): Line 1:1 Invalid left-hand side in assignment") // S11.13.2_A4.2_T1.3 test(` abc /= "1" `, nil) // 12.1-1 test(` try{};catch(){} `, "(anonymous): Line 2:13 Missing catch or finally after try") // 12.1-3 test(` try{};finally{} `, "(anonymous): Line 2:13 Missing catch or finally after try") // S12.6.3_A11.1_T3 test(` while (true) { break abc; } `, "(anonymous): Line 3:17 Undefined label 'abc'") // S15.3_A2_T1 test(`var x / = 1;`, "(anonymous): Line 1:7 Unexpected token /") test(` function abc() { if (0) return; else { } } `, nil) test("//\u2028 var =;", "(anonymous): Line 2:6 Unexpected token =") test(` throw {} `, "(anonymous): Line 2:13 Illegal newline after throw") // S7.6.1.1_A1.11 test(` function = 1 `, "(anonymous): Line 2:22 Unexpected token =") // S7.8.3_A1.2_T1 test(`0e1`, nil) test("abc = 1; abc\n++", "(anonymous): Line 2:3 Unexpected end of input") // --- test("({ get abc() {} })", nil) test(`for (abc.def in {}) {}`, nil) test(`while (true) { break }`, nil) test(`while (true) { continue }`, nil) test(`abc=/^(?:(\w+:)\/{2}(\w+(?:\.\w+)*\/?)|(.{0,2}\/{1}))?([/.]*?(?:[^?]+)?\/)?((?:[^/?]+)\.(\w+))(?:\?(\S+)?)?$/,def=/^(?:(\w+:)\/{2})|(.{0,2}\/{1})?([/.]*?(?:[^?]+)?\/?)?$/`, nil) test(`(function() { try {} catch (err) {} finally {} return })`, nil) test(`0xde0b6b3a7640080.toFixed(0)`, nil) test(`/[^-._0-9A-Za-z\xb7\xc0-\xd6\xd8-\xf6\xf8-\u037d\u37f-\u1fff\u200c-\u200d\u203f\u2040\u2070-\u218f]/`, nil) test(`/[\u0000-\u0008\u000B-\u000C\u000E-\u001F\uD800-\uDFFF\uFFFE-\uFFFF]/`, nil) test("var abc = 1;\ufeff", nil) test("\ufeff/* var abc = 1; */", nil) test(`if (-0x8000000000000000<=abc&&abc<=0x8000000000000000) {}`, nil) test(`(function(){debugger;return this;})`, nil) test(` `, nil) test(` var abc = "" debugger `, nil) test(` var abc = /\[\]$/ debugger `, nil) test(` var abc = 1 / 2 debugger `, nil) }) } func Test_parseStringLiteral(t *testing.T) { tt(t, func() { test := func(have, want string) { have, err := parseStringLiteral(have) is(err, nil) is(have, want) } test("", "") test("1(\\\\d+)", "1(\\d+)") test("\\u2029", "\u2029") test("abc\\uFFFFabc", "abc\uFFFFabc") test("[First line \\\nSecond line \\\n Third line\\\n. ]", "[First line Second line Third line. ]") test("\\u007a\\x79\\u000a\\x78", "zy\nx") // S7.8.4_A4.2_T3 test("\\a", "a") test("\u0410", "\u0410") // S7.8.4_A5.1_T1 test("\\0", "\u0000") // S8.4_A5 test("\u0000", "\u0000") // 15.5.4.20 test("'abc'\\\n'def'", "'abc''def'") // 15.5.4.20-4-1 test("'abc'\\\r\n'def'", "'abc''def'") // Octal test("\\0", "\000") test("\\00", "\000") test("\\000", "\000") test("\\09", "\0009") test("\\009", "\0009") test("\\0009", "\0009") test("\\1", "\001") test("\\01", "\001") test("\\001", "\001") test("\\0011", "\0011") test("\\1abc", "\001abc") test("\\\u4e16", "\u4e16") // err test = func(have, want string) { have, err := parseStringLiteral(have) is(err.Error(), want) is(have, "") } test(`\u`, `invalid escape: \u: len("") != 4`) test(`\u0`, `invalid escape: \u: len("0") != 4`) test(`\u00`, `invalid escape: \u: len("00") != 4`) test(`\u000`, `invalid escape: \u: len("000") != 4`) test(`\x`, `invalid escape: \x: len("") != 2`) test(`\x0`, `invalid escape: \x: len("0") != 2`) test(`\x0`, `invalid escape: \x: len("0") != 2`) }) } func Test_parseNumberLiteral(t *testing.T) { tt(t, func() { test := func(input string, expect interface{}) { result, err := parseNumberLiteral(input) is(err, nil) is(result, expect) } test("0", 0) test("0x8000000000000000", float64(9.223372036854776e+18)) }) } func TestPosition(t *testing.T) { tt(t, func() { parser := _newParser("", "// Lorem ipsum", 1, nil) // Out of range, idx0 (error condition) is(parser.slice(0, 1), "") is(parser.slice(0, 10), "") // Out of range, idx1 (error condition) is(parser.slice(1, 128), "") is(parser.str[0:0], "") is(parser.slice(1, 1), "") is(parser.str[0:1], "/") is(parser.slice(1, 2), "/") is(parser.str[0:14], "// Lorem ipsum") is(parser.slice(1, 15), "// Lorem ipsum") parser = _newParser("", "(function(){ return 0; })", 1, nil) program, err := parser.parse() is(err, nil) var node ast.Node node = program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.FunctionLiteral) is(node.Idx0(), file.Idx(2)) is(node.Idx1(), file.Idx(25)) is(parser.slice(node.Idx0(), node.Idx1()), "function(){ return 0; }") is(parser.slice(node.Idx0(), node.Idx1()+1), "function(){ return 0; })") is(parser.slice(node.Idx0(), node.Idx1()+2), "") is(node.(*ast.FunctionLiteral).Source, "function(){ return 0; }") node = program is(node.Idx0(), file.Idx(2)) is(node.Idx1(), file.Idx(25)) is(parser.slice(node.Idx0(), node.Idx1()), "function(){ return 0; }") parser = _newParser("", "(function(){ return abc; })", 1, nil) program, err = parser.parse() is(err, nil) node = program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.FunctionLiteral) is(node.(*ast.FunctionLiteral).Source, "function(){ return abc; }") parser = _newParser("", "this.style", 1, nil) program, err = parser.parse() node = program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.DotExpression).Left.(*ast.ThisExpression) is(node.Idx0(), file.Idx(1)) is(node.Idx1(), file.Idx(5)) parser = _newParser("", "(function(){ if (abc) { throw 'failed'; } })", 1, nil) program, err = parser.parse() is(err, nil) block := program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.FunctionLiteral).Body.(*ast.BlockStatement) node = block.List[0].(*ast.IfStatement) is(node.Idx0(), 21) node = node.(*ast.IfStatement).Consequent.(*ast.BlockStatement).List[0].(*ast.ThrowStatement) is(node.Idx0(), 39) }) } func BenchmarkParser(b *testing.B) { src := underscore.Source() b.ResetTimer() for i := 0; i < b.N; i++ { parser := _newParser("", src, 1, nil) parser.parse() } } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/parser/regexp.go000066400000000000000000000166251331145703200255240ustar00rootroot00000000000000package parser import ( "bytes" "fmt" "strconv" ) type _RegExp_parser struct { str string length int chr rune // The current character chrOffset int // The offset of current character offset int // The offset after current character (may be greater than 1) errors []error invalid bool // The input is an invalid JavaScript RegExp goRegexp *bytes.Buffer } // TransformRegExp transforms a JavaScript pattern into a Go "regexp" pattern. // // re2 (Go) cannot do backtracking, so the presence of a lookahead (?=) (?!) or // backreference (\1, \2, ...) will cause an error. // // re2 (Go) has a different definition for \s: [\t\n\f\r ]. // The JavaScript definition, on the other hand, also includes \v, Unicode "Separator, Space", etc. // // If the pattern is invalid (not valid even in JavaScript), then this function // returns the empty string and an error. // // If the pattern is valid, but incompatible (contains a lookahead or backreference), // then this function returns the transformation (a non-empty string) AND an error. func TransformRegExp(pattern string) (string, error) { if pattern == "" { return "", nil } // TODO If without \, if without (?=, (?!, then another shortcut parser := _RegExp_parser{ str: pattern, length: len(pattern), goRegexp: bytes.NewBuffer(make([]byte, 0, 3*len(pattern)/2)), } parser.read() // Pull in the first character parser.scan() var err error if len(parser.errors) > 0 { err = parser.errors[0] } if parser.invalid { return "", err } // Might not be re2 compatible, but is still a valid JavaScript RegExp return parser.goRegexp.String(), err } func (self *_RegExp_parser) scan() { for self.chr != -1 { switch self.chr { case '\\': self.read() self.scanEscape(false) case '(': self.pass() self.scanGroup() case '[': self.pass() self.scanBracket() case ')': self.error(-1, "Unmatched ')'") self.invalid = true self.pass() default: self.pass() } } } // (...) func (self *_RegExp_parser) scanGroup() { str := self.str[self.chrOffset:] if len(str) > 1 { // A possibility of (?= or (?! if str[0] == '?' { if str[1] == '=' || str[1] == '!' { self.error(-1, "re2: Invalid (%s) ", self.str[self.chrOffset:self.chrOffset+2]) } } } for self.chr != -1 && self.chr != ')' { switch self.chr { case '\\': self.read() self.scanEscape(false) case '(': self.pass() self.scanGroup() case '[': self.pass() self.scanBracket() default: self.pass() continue } } if self.chr != ')' { self.error(-1, "Unterminated group") self.invalid = true return } self.pass() } // [...] func (self *_RegExp_parser) scanBracket() { for self.chr != -1 { if self.chr == ']' { break } else if self.chr == '\\' { self.read() self.scanEscape(true) continue } self.pass() } if self.chr != ']' { self.error(-1, "Unterminated character class") self.invalid = true return } self.pass() } // \... func (self *_RegExp_parser) scanEscape(inClass bool) { offset := self.chrOffset var length, base uint32 switch self.chr { case '0', '1', '2', '3', '4', '5', '6', '7': var value int64 size := 0 for { digit := int64(digitValue(self.chr)) if digit >= 8 { // Not a valid digit break } value = value*8 + digit self.read() size += 1 } if size == 1 { // The number of characters read _, err := self.goRegexp.Write([]byte{'\\', byte(value) + '0'}) if err != nil { self.errors = append(self.errors, err) } if value != 0 { // An invalid backreference self.error(-1, "re2: Invalid \\%d ", value) } return } tmp := []byte{'\\', 'x', '0', 0} if value >= 16 { tmp = tmp[0:2] } else { tmp = tmp[0:3] } tmp = strconv.AppendInt(tmp, value, 16) _, err := self.goRegexp.Write(tmp) if err != nil { self.errors = append(self.errors, err) } return case '8', '9': size := 0 for { digit := digitValue(self.chr) if digit >= 10 { // Not a valid digit break } self.read() size += 1 } err := self.goRegexp.WriteByte('\\') if err != nil { self.errors = append(self.errors, err) } _, err = self.goRegexp.WriteString(self.str[offset:self.chrOffset]) if err != nil { self.errors = append(self.errors, err) } self.error(-1, "re2: Invalid \\%s ", self.str[offset:self.chrOffset]) return case 'x': self.read() length, base = 2, 16 case 'u': self.read() length, base = 4, 16 case 'b': if inClass { _, err := self.goRegexp.Write([]byte{'\\', 'x', '0', '8'}) if err != nil { self.errors = append(self.errors, err) } self.read() return } fallthrough case 'B': fallthrough case 'd', 'D', 's', 'S', 'w', 'W': // This is slightly broken, because ECMAScript // includes \v in \s, \S, while re2 does not fallthrough case '\\': fallthrough case 'f', 'n', 'r', 't', 'v': err := self.goRegexp.WriteByte('\\') if err != nil { self.errors = append(self.errors, err) } self.pass() return case 'c': self.read() var value int64 if 'a' <= self.chr && self.chr <= 'z' { value = int64(self.chr) - 'a' + 1 } else if 'A' <= self.chr && self.chr <= 'Z' { value = int64(self.chr) - 'A' + 1 } else { err := self.goRegexp.WriteByte('c') if err != nil { self.errors = append(self.errors, err) } return } tmp := []byte{'\\', 'x', '0', 0} if value >= 16 { tmp = tmp[0:2] } else { tmp = tmp[0:3] } tmp = strconv.AppendInt(tmp, value, 16) _, err := self.goRegexp.Write(tmp) if err != nil { self.errors = append(self.errors, err) } self.read() return default: // $ is an identifier character, so we have to have // a special case for it here if self.chr == '$' || !isIdentifierPart(self.chr) { // A non-identifier character needs escaping err := self.goRegexp.WriteByte('\\') if err != nil { self.errors = append(self.errors, err) } } else { // Unescape the character for re2 } self.pass() return } // Otherwise, we're a \u.... or \x... valueOffset := self.chrOffset var value uint32 { length := length for ; length > 0; length-- { digit := uint32(digitValue(self.chr)) if digit >= base { // Not a valid digit goto skip } value = value*base + digit self.read() } } if length == 4 { _, err := self.goRegexp.Write([]byte{ '\\', 'x', '{', self.str[valueOffset+0], self.str[valueOffset+1], self.str[valueOffset+2], self.str[valueOffset+3], '}', }) if err != nil { self.errors = append(self.errors, err) } } else if length == 2 { _, err := self.goRegexp.Write([]byte{ '\\', 'x', self.str[valueOffset+0], self.str[valueOffset+1], }) if err != nil { self.errors = append(self.errors, err) } } else { // Should never, ever get here... self.error(-1, "re2: Illegal branch in scanEscape") goto skip } return skip: _, err := self.goRegexp.WriteString(self.str[offset:self.chrOffset]) if err != nil { self.errors = append(self.errors, err) } } func (self *_RegExp_parser) pass() { if self.chr != -1 { _, err := self.goRegexp.WriteRune(self.chr) if err != nil { self.errors = append(self.errors, err) } } self.read() } // TODO Better error reporting, use the offset, etc. func (self *_RegExp_parser) error(offset int, msg string, msgValues ...interface{}) error { err := fmt.Errorf(msg, msgValues...) self.errors = append(self.errors, err) return err } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/parser/regexp_test.go000066400000000000000000000052241331145703200265540ustar00rootroot00000000000000package parser import ( "regexp" "testing" ) func TestRegExp(t *testing.T) { tt(t, func() { { // err test := func(input string, expect interface{}) { _, err := TransformRegExp(input) is(err, expect) } test("[", "Unterminated character class") test("(", "Unterminated group") test("(?=)", "re2: Invalid (?=) ") test("(?=)", "re2: Invalid (?=) ") test("(?!)", "re2: Invalid (?!) ") // An error anyway test("(?=", "re2: Invalid (?=) ") test("\\1", "re2: Invalid \\1 ") test("\\90", "re2: Invalid \\90 ") test("\\9123456789", "re2: Invalid \\9123456789 ") test("\\(?=)", "Unmatched ')'") test(")", "Unmatched ')'") } { // err test := func(input, expect string, expectErr interface{}) { output, err := TransformRegExp(input) is(output, expect) is(err, expectErr) } test("(?!)", "(?!)", "re2: Invalid (?!) ") test(")", "", "Unmatched ')'") test("(?!))", "", "re2: Invalid (?!) ") test("\\0", "\\0", nil) test("\\1", "\\1", "re2: Invalid \\1 ") test("\\9123456789", "\\9123456789", "re2: Invalid \\9123456789 ") } { // err test := func(input string, expect string) { result, err := TransformRegExp(input) is(err, nil) if is(result, expect) { _, err := regexp.Compile(result) if !is(err, nil) { t.Log(result) } } } test("", "") test("abc", "abc") test(`\abc`, `abc`) test(`\a\b\c`, `a\bc`) test(`\x`, `x`) test(`\c`, `c`) test(`\cA`, `\x01`) test(`\cz`, `\x1a`) test(`\ca`, `\x01`) test(`\cj`, `\x0a`) test(`\ck`, `\x0b`) test(`\+`, `\+`) test(`[\b]`, `[\x08]`) test(`\u0z01\x\undefined`, `u0z01xundefined`) test(`\\|'|\r|\n|\t|\u2028|\u2029`, `\\|'|\r|\n|\t|\x{2028}|\x{2029}`) test("]", "]") test("}", "}") test("%", "%") test("(%)", "(%)") test("(?:[%\\s])", "(?:[%\\s])") test("[[]", "[[]") test("\\101", "\\x41") test("\\51", "\\x29") test("\\051", "\\x29") test("\\175", "\\x7d") test("\\04", "\\x04") test(`<%([\s\S]+?)%>`, `<%([\s\S]+?)%>`) test(`(.)^`, "(.)^") test(`<%-([\s\S]+?)%>|<%=([\s\S]+?)%>|<%([\s\S]+?)%>|$`, `<%-([\s\S]+?)%>|<%=([\s\S]+?)%>|<%([\s\S]+?)%>|$`) test(`\$`, `\$`) test(`[G-b]`, `[G-b]`) test(`[G-b\0]`, `[G-b\0]`) } }) } func TestTransformRegExp(t *testing.T) { tt(t, func() { pattern, err := TransformRegExp(`\s+abc\s+`) is(err, nil) is(pattern, `\s+abc\s+`) is(regexp.MustCompile(pattern).MatchString("\t abc def"), true) }) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/parser/scope.go000066400000000000000000000015211331145703200253300ustar00rootroot00000000000000package parser import ( "github.com/robertkrimen/otto/ast" ) type _scope struct { outer *_scope allowIn bool inIteration bool inSwitch bool inFunction bool declarationList []ast.Declaration labels []string } func (self *_parser) openScope() { self.scope = &_scope{ outer: self.scope, allowIn: true, } } func (self *_parser) closeScope() { self.scope = self.scope.outer } func (self *_scope) declare(declaration ast.Declaration) { self.declarationList = append(self.declarationList, declaration) } func (self *_scope) hasLabel(name string) bool { for _, label := range self.labels { if label == name { return true } } if self.outer != nil && !self.inFunction { // Crossing a function boundary to look for a label is verboten return self.outer.hasLabel(name) } return false } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/parser/statement.go000066400000000000000000000554201331145703200262320ustar00rootroot00000000000000package parser import ( "github.com/robertkrimen/otto/ast" "github.com/robertkrimen/otto/token" ) func (self *_parser) parseBlockStatement() *ast.BlockStatement { node := &ast.BlockStatement{} // Find comments before the leading brace if self.mode&StoreComments != 0 { self.comments.CommentMap.AddComments(node, self.comments.FetchAll(), ast.LEADING) self.comments.Unset() } node.LeftBrace = self.expect(token.LEFT_BRACE) node.List = self.parseStatementList() if self.mode&StoreComments != 0 { self.comments.Unset() self.comments.CommentMap.AddComments(node, self.comments.FetchAll(), ast.FINAL) self.comments.AfterBlock() } node.RightBrace = self.expect(token.RIGHT_BRACE) // Find comments after the trailing brace if self.mode&StoreComments != 0 { self.comments.ResetLineBreak() self.comments.CommentMap.AddComments(node, self.comments.Fetch(), ast.TRAILING) } return node } func (self *_parser) parseEmptyStatement() ast.Statement { idx := self.expect(token.SEMICOLON) return &ast.EmptyStatement{Semicolon: idx} } func (self *_parser) parseStatementList() (list []ast.Statement) { for self.token != token.RIGHT_BRACE && self.token != token.EOF { statement := self.parseStatement() list = append(list, statement) } return } func (self *_parser) parseStatement() ast.Statement { if self.token == token.EOF { self.errorUnexpectedToken(self.token) return &ast.BadStatement{From: self.idx, To: self.idx + 1} } if self.mode&StoreComments != 0 { self.comments.ResetLineBreak() } switch self.token { case token.SEMICOLON: return self.parseEmptyStatement() case token.LEFT_BRACE: return self.parseBlockStatement() case token.IF: return self.parseIfStatement() case token.DO: statement := self.parseDoWhileStatement() self.comments.PostProcessNode(statement) return statement case token.WHILE: return self.parseWhileStatement() case token.FOR: return self.parseForOrForInStatement() case token.BREAK: return self.parseBreakStatement() case token.CONTINUE: return self.parseContinueStatement() case token.DEBUGGER: return self.parseDebuggerStatement() case token.WITH: return self.parseWithStatement() case token.VAR: return self.parseVariableStatement() case token.FUNCTION: return self.parseFunctionStatement() case token.SWITCH: return self.parseSwitchStatement() case token.RETURN: return self.parseReturnStatement() case token.THROW: return self.parseThrowStatement() case token.TRY: return self.parseTryStatement() } var comments []*ast.Comment if self.mode&StoreComments != 0 { comments = self.comments.FetchAll() } expression := self.parseExpression() if identifier, isIdentifier := expression.(*ast.Identifier); isIdentifier && self.token == token.COLON { // LabelledStatement colon := self.idx if self.mode&StoreComments != 0 { self.comments.Unset() } self.next() // : label := identifier.Name for _, value := range self.scope.labels { if label == value { self.error(identifier.Idx0(), "Label '%s' already exists", label) } } var labelComments []*ast.Comment if self.mode&StoreComments != 0 { labelComments = self.comments.FetchAll() } self.scope.labels = append(self.scope.labels, label) // Push the label statement := self.parseStatement() self.scope.labels = self.scope.labels[:len(self.scope.labels)-1] // Pop the label exp := &ast.LabelledStatement{ Label: identifier, Colon: colon, Statement: statement, } if self.mode&StoreComments != 0 { self.comments.CommentMap.AddComments(exp, labelComments, ast.LEADING) } return exp } self.optionalSemicolon() statement := &ast.ExpressionStatement{ Expression: expression, } if self.mode&StoreComments != 0 { self.comments.CommentMap.AddComments(statement, comments, ast.LEADING) } return statement } func (self *_parser) parseTryStatement() ast.Statement { var tryComments []*ast.Comment if self.mode&StoreComments != 0 { tryComments = self.comments.FetchAll() } node := &ast.TryStatement{ Try: self.expect(token.TRY), Body: self.parseBlockStatement(), } if self.mode&StoreComments != 0 { self.comments.CommentMap.AddComments(node, tryComments, ast.LEADING) self.comments.CommentMap.AddComments(node.Body, self.comments.FetchAll(), ast.TRAILING) } if self.token == token.CATCH { catch := self.idx if self.mode&StoreComments != 0 { self.comments.Unset() } self.next() self.expect(token.LEFT_PARENTHESIS) if self.token != token.IDENTIFIER { self.expect(token.IDENTIFIER) self.nextStatement() return &ast.BadStatement{From: catch, To: self.idx} } else { identifier := self.parseIdentifier() self.expect(token.RIGHT_PARENTHESIS) node.Catch = &ast.CatchStatement{ Catch: catch, Parameter: identifier, Body: self.parseBlockStatement(), } if self.mode&StoreComments != 0 { self.comments.CommentMap.AddComments(node.Catch.Body, self.comments.FetchAll(), ast.TRAILING) } } } if self.token == token.FINALLY { if self.mode&StoreComments != 0 { self.comments.Unset() } self.next() if self.mode&StoreComments != 0 { tryComments = self.comments.FetchAll() } node.Finally = self.parseBlockStatement() if self.mode&StoreComments != 0 { self.comments.CommentMap.AddComments(node.Finally, tryComments, ast.LEADING) } } if node.Catch == nil && node.Finally == nil { self.error(node.Try, "Missing catch or finally after try") return &ast.BadStatement{From: node.Try, To: node.Body.Idx1()} } return node } func (self *_parser) parseFunctionParameterList() *ast.ParameterList { opening := self.expect(token.LEFT_PARENTHESIS) if self.mode&StoreComments != 0 { self.comments.Unset() } var list []*ast.Identifier for self.token != token.RIGHT_PARENTHESIS && self.token != token.EOF { if self.token != token.IDENTIFIER { self.expect(token.IDENTIFIER) } else { identifier := self.parseIdentifier() list = append(list, identifier) } if self.token != token.RIGHT_PARENTHESIS { if self.mode&StoreComments != 0 { self.comments.Unset() } self.expect(token.COMMA) } } closing := self.expect(token.RIGHT_PARENTHESIS) return &ast.ParameterList{ Opening: opening, List: list, Closing: closing, } } func (self *_parser) parseParameterList() (list []string) { for self.token != token.EOF { if self.token != token.IDENTIFIER { self.expect(token.IDENTIFIER) } list = append(list, self.literal) self.next() if self.token != token.EOF { self.expect(token.COMMA) } } return } func (self *_parser) parseFunctionStatement() *ast.FunctionStatement { var comments []*ast.Comment if self.mode&StoreComments != 0 { comments = self.comments.FetchAll() } function := &ast.FunctionStatement{ Function: self.parseFunction(true), } if self.mode&StoreComments != 0 { self.comments.CommentMap.AddComments(function, comments, ast.LEADING) } return function } func (self *_parser) parseFunction(declaration bool) *ast.FunctionLiteral { node := &ast.FunctionLiteral{ Function: self.expect(token.FUNCTION), } var name *ast.Identifier if self.token == token.IDENTIFIER { name = self.parseIdentifier() if declaration { self.scope.declare(&ast.FunctionDeclaration{ Function: node, }) } } else if declaration { // Use expect error handling self.expect(token.IDENTIFIER) } if self.mode&StoreComments != 0 { self.comments.Unset() } node.Name = name node.ParameterList = self.parseFunctionParameterList() self.parseFunctionBlock(node) node.Source = self.slice(node.Idx0(), node.Idx1()) return node } func (self *_parser) parseFunctionBlock(node *ast.FunctionLiteral) { { self.openScope() inFunction := self.scope.inFunction self.scope.inFunction = true defer func() { self.scope.inFunction = inFunction self.closeScope() }() node.Body = self.parseBlockStatement() node.DeclarationList = self.scope.declarationList } } func (self *_parser) parseDebuggerStatement() ast.Statement { idx := self.expect(token.DEBUGGER) node := &ast.DebuggerStatement{ Debugger: idx, } if self.mode&StoreComments != 0 { self.comments.CommentMap.AddComments(node, self.comments.FetchAll(), ast.TRAILING) } self.semicolon() return node } func (self *_parser) parseReturnStatement() ast.Statement { idx := self.expect(token.RETURN) var comments []*ast.Comment if self.mode&StoreComments != 0 { comments = self.comments.FetchAll() } if !self.scope.inFunction { self.error(idx, "Illegal return statement") self.nextStatement() return &ast.BadStatement{From: idx, To: self.idx} } node := &ast.ReturnStatement{ Return: idx, } if !self.implicitSemicolon && self.token != token.SEMICOLON && self.token != token.RIGHT_BRACE && self.token != token.EOF { node.Argument = self.parseExpression() } if self.mode&StoreComments != 0 { self.comments.CommentMap.AddComments(node, comments, ast.LEADING) } self.semicolon() return node } func (self *_parser) parseThrowStatement() ast.Statement { var comments []*ast.Comment if self.mode&StoreComments != 0 { comments = self.comments.FetchAll() } idx := self.expect(token.THROW) if self.implicitSemicolon { if self.chr == -1 { // Hackish self.error(idx, "Unexpected end of input") } else { self.error(idx, "Illegal newline after throw") } self.nextStatement() return &ast.BadStatement{From: idx, To: self.idx} } node := &ast.ThrowStatement{ Throw: self.idx, Argument: self.parseExpression(), } if self.mode&StoreComments != 0 { self.comments.CommentMap.AddComments(node, comments, ast.LEADING) } self.semicolon() return node } func (self *_parser) parseSwitchStatement() ast.Statement { var comments []*ast.Comment if self.mode&StoreComments != 0 { comments = self.comments.FetchAll() } self.expect(token.SWITCH) if self.mode&StoreComments != 0 { comments = append(comments, self.comments.FetchAll()...) } self.expect(token.LEFT_PARENTHESIS) node := &ast.SwitchStatement{ Discriminant: self.parseExpression(), Default: -1, } self.expect(token.RIGHT_PARENTHESIS) if self.mode&StoreComments != 0 { comments = append(comments, self.comments.FetchAll()...) } self.expect(token.LEFT_BRACE) inSwitch := self.scope.inSwitch self.scope.inSwitch = true defer func() { self.scope.inSwitch = inSwitch }() for index := 0; self.token != token.EOF; index++ { if self.token == token.RIGHT_BRACE { self.next() break } clause := self.parseCaseStatement() if clause.Test == nil { if node.Default != -1 { self.error(clause.Case, "Already saw a default in switch") } node.Default = index } node.Body = append(node.Body, clause) } if self.mode&StoreComments != 0 { self.comments.CommentMap.AddComments(node, comments, ast.LEADING) } return node } func (self *_parser) parseWithStatement() ast.Statement { var comments []*ast.Comment if self.mode&StoreComments != 0 { comments = self.comments.FetchAll() } self.expect(token.WITH) var withComments []*ast.Comment if self.mode&StoreComments != 0 { withComments = self.comments.FetchAll() } self.expect(token.LEFT_PARENTHESIS) node := &ast.WithStatement{ Object: self.parseExpression(), } self.expect(token.RIGHT_PARENTHESIS) if self.mode&StoreComments != 0 { //comments = append(comments, self.comments.FetchAll()...) self.comments.CommentMap.AddComments(node, comments, ast.LEADING) self.comments.CommentMap.AddComments(node, withComments, ast.WITH) } node.Body = self.parseStatement() return node } func (self *_parser) parseCaseStatement() *ast.CaseStatement { node := &ast.CaseStatement{ Case: self.idx, } var comments []*ast.Comment if self.mode&StoreComments != 0 { comments = self.comments.FetchAll() self.comments.Unset() } if self.token == token.DEFAULT { self.next() } else { self.expect(token.CASE) node.Test = self.parseExpression() } if self.mode&StoreComments != 0 { self.comments.Unset() } self.expect(token.COLON) for { if self.token == token.EOF || self.token == token.RIGHT_BRACE || self.token == token.CASE || self.token == token.DEFAULT { break } consequent := self.parseStatement() node.Consequent = append(node.Consequent, consequent) } // Link the comments to the case statement if self.mode&StoreComments != 0 { self.comments.CommentMap.AddComments(node, comments, ast.LEADING) } return node } func (self *_parser) parseIterationStatement() ast.Statement { inIteration := self.scope.inIteration self.scope.inIteration = true defer func() { self.scope.inIteration = inIteration }() return self.parseStatement() } func (self *_parser) parseForIn(into ast.Expression) *ast.ForInStatement { // Already have consumed " in" source := self.parseExpression() self.expect(token.RIGHT_PARENTHESIS) body := self.parseIterationStatement() forin := &ast.ForInStatement{ Into: into, Source: source, Body: body, } return forin } func (self *_parser) parseFor(initializer ast.Expression) *ast.ForStatement { // Already have consumed " ;" var test, update ast.Expression if self.token != token.SEMICOLON { test = self.parseExpression() } if self.mode&StoreComments != 0 { self.comments.Unset() } self.expect(token.SEMICOLON) if self.token != token.RIGHT_PARENTHESIS { update = self.parseExpression() } self.expect(token.RIGHT_PARENTHESIS) body := self.parseIterationStatement() forstatement := &ast.ForStatement{ Initializer: initializer, Test: test, Update: update, Body: body, } return forstatement } func (self *_parser) parseForOrForInStatement() ast.Statement { var comments []*ast.Comment if self.mode&StoreComments != 0 { comments = self.comments.FetchAll() } idx := self.expect(token.FOR) var forComments []*ast.Comment if self.mode&StoreComments != 0 { forComments = self.comments.FetchAll() } self.expect(token.LEFT_PARENTHESIS) var left []ast.Expression forIn := false if self.token != token.SEMICOLON { allowIn := self.scope.allowIn self.scope.allowIn = false if self.token == token.VAR { var_ := self.idx var varComments []*ast.Comment if self.mode&StoreComments != 0 { varComments = self.comments.FetchAll() self.comments.Unset() } self.next() list := self.parseVariableDeclarationList(var_) if len(list) == 1 && self.token == token.IN { if self.mode&StoreComments != 0 { self.comments.Unset() } self.next() // in forIn = true left = []ast.Expression{list[0]} // There is only one declaration } else { left = list } if self.mode&StoreComments != 0 { self.comments.CommentMap.AddComments(left[0], varComments, ast.LEADING) } } else { left = append(left, self.parseExpression()) if self.token == token.IN { self.next() forIn = true } } self.scope.allowIn = allowIn } if forIn { switch left[0].(type) { case *ast.Identifier, *ast.DotExpression, *ast.BracketExpression, *ast.VariableExpression: // These are all acceptable default: self.error(idx, "Invalid left-hand side in for-in") self.nextStatement() return &ast.BadStatement{From: idx, To: self.idx} } forin := self.parseForIn(left[0]) if self.mode&StoreComments != 0 { self.comments.CommentMap.AddComments(forin, comments, ast.LEADING) self.comments.CommentMap.AddComments(forin, forComments, ast.FOR) } return forin } if self.mode&StoreComments != 0 { self.comments.Unset() } self.expect(token.SEMICOLON) initializer := &ast.SequenceExpression{Sequence: left} forstatement := self.parseFor(initializer) if self.mode&StoreComments != 0 { self.comments.CommentMap.AddComments(forstatement, comments, ast.LEADING) self.comments.CommentMap.AddComments(forstatement, forComments, ast.FOR) } return forstatement } func (self *_parser) parseVariableStatement() *ast.VariableStatement { var comments []*ast.Comment if self.mode&StoreComments != 0 { comments = self.comments.FetchAll() } idx := self.expect(token.VAR) list := self.parseVariableDeclarationList(idx) statement := &ast.VariableStatement{ Var: idx, List: list, } if self.mode&StoreComments != 0 { self.comments.CommentMap.AddComments(statement, comments, ast.LEADING) self.comments.Unset() } self.semicolon() return statement } func (self *_parser) parseDoWhileStatement() ast.Statement { inIteration := self.scope.inIteration self.scope.inIteration = true defer func() { self.scope.inIteration = inIteration }() var comments []*ast.Comment if self.mode&StoreComments != 0 { comments = self.comments.FetchAll() } self.expect(token.DO) var doComments []*ast.Comment if self.mode&StoreComments != 0 { doComments = self.comments.FetchAll() } node := &ast.DoWhileStatement{} if self.token == token.LEFT_BRACE { node.Body = self.parseBlockStatement() } else { node.Body = self.parseStatement() } self.expect(token.WHILE) var whileComments []*ast.Comment if self.mode&StoreComments != 0 { whileComments = self.comments.FetchAll() } self.expect(token.LEFT_PARENTHESIS) node.Test = self.parseExpression() self.expect(token.RIGHT_PARENTHESIS) if self.mode&StoreComments != 0 { self.comments.CommentMap.AddComments(node, comments, ast.LEADING) self.comments.CommentMap.AddComments(node, doComments, ast.DO) self.comments.CommentMap.AddComments(node, whileComments, ast.WHILE) } return node } func (self *_parser) parseWhileStatement() ast.Statement { var comments []*ast.Comment if self.mode&StoreComments != 0 { comments = self.comments.FetchAll() } self.expect(token.WHILE) var whileComments []*ast.Comment if self.mode&StoreComments != 0 { whileComments = self.comments.FetchAll() } self.expect(token.LEFT_PARENTHESIS) node := &ast.WhileStatement{ Test: self.parseExpression(), } self.expect(token.RIGHT_PARENTHESIS) node.Body = self.parseIterationStatement() if self.mode&StoreComments != 0 { self.comments.CommentMap.AddComments(node, comments, ast.LEADING) self.comments.CommentMap.AddComments(node, whileComments, ast.WHILE) } return node } func (self *_parser) parseIfStatement() ast.Statement { var comments []*ast.Comment if self.mode&StoreComments != 0 { comments = self.comments.FetchAll() } self.expect(token.IF) var ifComments []*ast.Comment if self.mode&StoreComments != 0 { ifComments = self.comments.FetchAll() } self.expect(token.LEFT_PARENTHESIS) node := &ast.IfStatement{ If: self.idx, Test: self.parseExpression(), } self.expect(token.RIGHT_PARENTHESIS) if self.token == token.LEFT_BRACE { node.Consequent = self.parseBlockStatement() } else { node.Consequent = self.parseStatement() } if self.token == token.ELSE { self.next() node.Alternate = self.parseStatement() } if self.mode&StoreComments != 0 { self.comments.CommentMap.AddComments(node, comments, ast.LEADING) self.comments.CommentMap.AddComments(node, ifComments, ast.IF) } return node } func (self *_parser) parseSourceElement() ast.Statement { statement := self.parseStatement() //self.comments.Unset() return statement } func (self *_parser) parseSourceElements() []ast.Statement { body := []ast.Statement(nil) for { if self.token != token.STRING { break } body = append(body, self.parseSourceElement()) } for self.token != token.EOF { body = append(body, self.parseSourceElement()) } return body } func (self *_parser) parseProgram() *ast.Program { self.openScope() defer self.closeScope() return &ast.Program{ Body: self.parseSourceElements(), DeclarationList: self.scope.declarationList, File: self.file, } } func (self *_parser) parseBreakStatement() ast.Statement { var comments []*ast.Comment if self.mode&StoreComments != 0 { comments = self.comments.FetchAll() } idx := self.expect(token.BREAK) semicolon := self.implicitSemicolon if self.token == token.SEMICOLON { semicolon = true self.next() } if semicolon || self.token == token.RIGHT_BRACE { self.implicitSemicolon = false if !self.scope.inIteration && !self.scope.inSwitch { goto illegal } breakStatement := &ast.BranchStatement{ Idx: idx, Token: token.BREAK, } if self.mode&StoreComments != 0 { self.comments.CommentMap.AddComments(breakStatement, comments, ast.LEADING) self.comments.CommentMap.AddComments(breakStatement, self.comments.FetchAll(), ast.TRAILING) } return breakStatement } if self.token == token.IDENTIFIER { identifier := self.parseIdentifier() if !self.scope.hasLabel(identifier.Name) { self.error(idx, "Undefined label '%s'", identifier.Name) return &ast.BadStatement{From: idx, To: identifier.Idx1()} } self.semicolon() breakStatement := &ast.BranchStatement{ Idx: idx, Token: token.BREAK, Label: identifier, } if self.mode&StoreComments != 0 { self.comments.CommentMap.AddComments(breakStatement, comments, ast.LEADING) } return breakStatement } self.expect(token.IDENTIFIER) illegal: self.error(idx, "Illegal break statement") self.nextStatement() return &ast.BadStatement{From: idx, To: self.idx} } func (self *_parser) parseContinueStatement() ast.Statement { idx := self.expect(token.CONTINUE) semicolon := self.implicitSemicolon if self.token == token.SEMICOLON { semicolon = true self.next() } if semicolon || self.token == token.RIGHT_BRACE { self.implicitSemicolon = false if !self.scope.inIteration { goto illegal } return &ast.BranchStatement{ Idx: idx, Token: token.CONTINUE, } } if self.token == token.IDENTIFIER { identifier := self.parseIdentifier() if !self.scope.hasLabel(identifier.Name) { self.error(idx, "Undefined label '%s'", identifier.Name) return &ast.BadStatement{From: idx, To: identifier.Idx1()} } if !self.scope.inIteration { goto illegal } self.semicolon() return &ast.BranchStatement{ Idx: idx, Token: token.CONTINUE, Label: identifier, } } self.expect(token.IDENTIFIER) illegal: self.error(idx, "Illegal continue statement") self.nextStatement() return &ast.BadStatement{From: idx, To: self.idx} } // Find the next statement after an error (recover) func (self *_parser) nextStatement() { for { switch self.token { case token.BREAK, token.CONTINUE, token.FOR, token.IF, token.RETURN, token.SWITCH, token.VAR, token.DO, token.TRY, token.WITH, token.WHILE, token.THROW, token.CATCH, token.FINALLY: // Return only if parser made some progress since last // sync or if it has not reached 10 next calls without // progress. Otherwise consume at least one token to // avoid an endless parser loop if self.idx == self.recover.idx && self.recover.count < 10 { self.recover.count++ return } if self.idx > self.recover.idx { self.recover.idx = self.idx self.recover.count = 0 return } // Reaching here indicates a parser bug, likely an // incorrect token list in this function, but it only // leads to skipping of possibly correct code if a // previous error is present, and thus is preferred // over a non-terminating parse. case token.EOF: return } self.next() } } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/parser_test.go000066400000000000000000000015161331145703200252620ustar00rootroot00000000000000package otto import ( "testing" ) func TestPersistence(t *testing.T) { tt(t, func() { test, _ := test() test(` function abc() { return 1; } abc.toString(); `, "function abc() { return 1; }") test(` function def() { return 3.14159; } [ abc.toString(), def.toString() ]; `, "function abc() { return 1; },function def() { return 3.14159; }") test(` eval("function ghi() { return 'ghi' }"); [ abc.toString(), def.toString(), ghi.toString() ]; `, "function abc() { return 1; },function def() { return 3.14159; },function ghi() { return 'ghi' }") test(` [ abc.toString(), def.toString(), ghi.toString() ]; `, "function abc() { return 1; },function def() { return 3.14159; },function ghi() { return 'ghi' }") test(`/* */`, UndefinedValue()) }) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/property.go000066400000000000000000000124011331145703200246060ustar00rootroot00000000000000package otto // property type _propertyMode int const ( modeWriteMask _propertyMode = 0700 modeEnumerateMask = 0070 modeConfigureMask = 0007 modeOnMask = 0111 modeOffMask = 0000 modeSetMask = 0222 // If value is 2, then mode is neither "On" nor "Off" ) type _propertyGetSet [2]*_object var _nilGetSetObject _object = _object{} type _property struct { value interface{} mode _propertyMode } func (self _property) writable() bool { return self.mode&modeWriteMask == modeWriteMask&modeOnMask } func (self *_property) writeOn() { self.mode = (self.mode & ^modeWriteMask) | (modeWriteMask & modeOnMask) } func (self *_property) writeOff() { self.mode &= ^modeWriteMask } func (self *_property) writeClear() { self.mode = (self.mode & ^modeWriteMask) | (modeWriteMask & modeSetMask) } func (self _property) writeSet() bool { return 0 == self.mode&modeWriteMask&modeSetMask } func (self _property) enumerable() bool { return self.mode&modeEnumerateMask == modeEnumerateMask&modeOnMask } func (self *_property) enumerateOn() { self.mode = (self.mode & ^modeEnumerateMask) | (modeEnumerateMask & modeOnMask) } func (self *_property) enumerateOff() { self.mode &= ^modeEnumerateMask } func (self _property) enumerateSet() bool { return 0 == self.mode&modeEnumerateMask&modeSetMask } func (self _property) configurable() bool { return self.mode&modeConfigureMask == modeConfigureMask&modeOnMask } func (self *_property) configureOn() { self.mode = (self.mode & ^modeConfigureMask) | (modeConfigureMask & modeOnMask) } func (self *_property) configureOff() { self.mode &= ^modeConfigureMask } func (self _property) configureSet() bool { return 0 == self.mode&modeConfigureMask&modeSetMask } func (self _property) copy() *_property { property := self return &property } func (self _property) get(this *_object) Value { switch value := self.value.(type) { case Value: return value case _propertyGetSet: if value[0] != nil { return value[0].call(toValue(this), nil, false, nativeFrame) } } return Value{} } func (self _property) isAccessorDescriptor() bool { setGet, test := self.value.(_propertyGetSet) return test && (setGet[0] != nil || setGet[1] != nil) } func (self _property) isDataDescriptor() bool { if self.writeSet() { // Either "On" or "Off" return true } value, valid := self.value.(Value) return valid && !value.isEmpty() } func (self _property) isGenericDescriptor() bool { return !(self.isDataDescriptor() || self.isAccessorDescriptor()) } func (self _property) isEmpty() bool { return self.mode == 0222 && self.isGenericDescriptor() } // _enumerableValue, _enumerableTrue, _enumerableFalse? // .enumerableValue() .enumerableExists() func toPropertyDescriptor(rt *_runtime, value Value) (descriptor _property) { objectDescriptor := value._object() if objectDescriptor == nil { panic(rt.panicTypeError()) } { descriptor.mode = modeSetMask // Initially nothing is set if objectDescriptor.hasProperty("enumerable") { if objectDescriptor.get("enumerable").bool() { descriptor.enumerateOn() } else { descriptor.enumerateOff() } } if objectDescriptor.hasProperty("configurable") { if objectDescriptor.get("configurable").bool() { descriptor.configureOn() } else { descriptor.configureOff() } } if objectDescriptor.hasProperty("writable") { if objectDescriptor.get("writable").bool() { descriptor.writeOn() } else { descriptor.writeOff() } } } var getter, setter *_object getterSetter := false if objectDescriptor.hasProperty("get") { value := objectDescriptor.get("get") if value.IsDefined() { if !value.isCallable() { panic(rt.panicTypeError()) } getter = value._object() getterSetter = true } else { getter = &_nilGetSetObject getterSetter = true } } if objectDescriptor.hasProperty("set") { value := objectDescriptor.get("set") if value.IsDefined() { if !value.isCallable() { panic(rt.panicTypeError()) } setter = value._object() getterSetter = true } else { setter = &_nilGetSetObject getterSetter = true } } if getterSetter { if descriptor.writeSet() { panic(rt.panicTypeError()) } descriptor.value = _propertyGetSet{getter, setter} } if objectDescriptor.hasProperty("value") { if getterSetter { panic(rt.panicTypeError()) } descriptor.value = objectDescriptor.get("value") } return } func (self *_runtime) fromPropertyDescriptor(descriptor _property) *_object { object := self.newObject() if descriptor.isDataDescriptor() { object.defineProperty("value", descriptor.value.(Value), 0111, false) object.defineProperty("writable", toValue_bool(descriptor.writable()), 0111, false) } else if descriptor.isAccessorDescriptor() { getSet := descriptor.value.(_propertyGetSet) get := Value{} if getSet[0] != nil { get = toValue_object(getSet[0]) } set := Value{} if getSet[1] != nil { set = toValue_object(getSet[1]) } object.defineProperty("get", get, 0111, false) object.defineProperty("set", set, 0111, false) } object.defineProperty("enumerable", toValue_bool(descriptor.enumerable()), 0111, false) object.defineProperty("configurable", toValue_bool(descriptor.configurable()), 0111, false) return object } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/reflect_test.go000066400000000000000000000407521331145703200254170ustar00rootroot00000000000000package otto import ( "fmt" "math" "reflect" "strings" "testing" ) type _abcStruct struct { Abc bool Def int Ghi string Jkl interface{} Mno _mnoStruct Pqr map[string]int8 } func (abc _abcStruct) String() string { return abc.Ghi } func (abc *_abcStruct) FuncPointer() string { return "abc" } func (abc _abcStruct) Func() { return } func (abc _abcStruct) FuncReturn1() string { return "abc" } func (abc _abcStruct) FuncReturn2() (string, error) { return "def", nil } func (abc _abcStruct) Func1Return1(a string) string { return a } func (abc _abcStruct) Func2Return1(x, y string) string { return x + y } func (abc _abcStruct) FuncEllipsis(xyz ...string) int { return len(xyz) } func (abc _abcStruct) FuncReturnStruct() _mnoStruct { return _mnoStruct{} } func (abs _abcStruct) Func1Int(i int) int { return i + 1 } func (abs _abcStruct) Func1Int8(i int8) int8 { return i + 1 } func (abs _abcStruct) Func1Int16(i int16) int16 { return i + 1 } func (abs _abcStruct) Func1Int32(i int32) int32 { return i + 1 } func (abs _abcStruct) Func1Int64(i int64) int64 { return i + 1 } func (abs _abcStruct) Func1Uint(i uint) uint { return i + 1 } func (abs _abcStruct) Func1Uint8(i uint8) uint8 { return i + 1 } func (abs _abcStruct) Func1Uint16(i uint16) uint16 { return i + 1 } func (abs _abcStruct) Func1Uint32(i uint32) uint32 { return i + 1 } func (abs _abcStruct) Func1Uint64(i uint64) uint64 { return i + 1 } func (abs _abcStruct) Func2Int(i, j int) int { return i + j } func (abs _abcStruct) Func2StringInt(s string, i int) string { return fmt.Sprintf("%v:%v", s, i) } func (abs _abcStruct) Func1IntVariadic(a ...int) int { t := 0 for _, i := range a { t += i } return t } func (abs _abcStruct) Func2IntVariadic(s string, a ...int) string { t := 0 for _, i := range a { t += i } return fmt.Sprintf("%v:%v", s, t) } func (abs _abcStruct) Func2IntArrayVariadic(s string, a ...[]int) string { t := 0 for _, i := range a { for _, j := range i { t += j } } return fmt.Sprintf("%v:%v", s, t) } type _mnoStruct struct { Ghi string } func (mno _mnoStruct) Func() string { return "mno" } func TestReflect(t *testing.T) { if true { return } tt(t, func() { // Testing dbgf // These should panic toValue("Xyzzy").toReflectValue(reflect.Ptr) stringToReflectValue("Xyzzy", reflect.Ptr) }) } func Test_reflectStruct(t *testing.T) { tt(t, func() { test, vm := test() // _abcStruct { abc := &_abcStruct{} vm.Set("abc", abc) test(` [ abc.Abc, abc.Ghi ]; `, "false,") abc.Abc = true abc.Ghi = "Nothing happens." test(` [ abc.Abc, abc.Ghi ]; `, "true,Nothing happens.") *abc = _abcStruct{} test(` [ abc.Abc, abc.Ghi ]; `, "false,") abc.Abc = true abc.Ghi = "Xyzzy" vm.Set("abc", abc) test(` [ abc.Abc, abc.Ghi ]; `, "true,Xyzzy") is(abc.Abc, true) test(` abc.Abc = false; abc.Def = 451; abc.Ghi = "Nothing happens."; abc.abc = "Something happens."; [ abc.Def, abc.abc ]; `, "451,Something happens.") is(abc.Abc, false) is(abc.Def, 451) is(abc.Ghi, "Nothing happens.") test(` delete abc.Def; delete abc.abc; [ abc.Def, abc.abc ]; `, "451,") is(abc.Def, 451) test(` abc.FuncPointer(); `, "abc") test(` abc.Func(); `, "undefined") test(` abc.FuncReturn1(); `, "abc") test(` abc.Func1Return1("abc"); `, "abc") test(` abc.Func2Return1("abc", "def"); `, "abcdef") test(` abc.FuncEllipsis("abc", "def", "ghi"); `, 3) test(` ret = abc.FuncReturn2(); if (ret && ret.length && ret.length == 2 && ret[0] == "def" && ret[1] === undefined) { true; } else { false; } `, true) test(` abc.FuncReturnStruct(); `, "[object Object]") test(` abc.FuncReturnStruct().Func(); `, "mno") test(` abc.Func1Int(1); `, 2) test(` abc.Func1Int(0x01 & 0x01); `, 2) test(`raise: abc.Func1Int(1.1); `, "RangeError: converting float64 to int would cause loss of precision") test(` var v = 1; abc.Func1Int(v + 1); `, 3) test(` abc.Func2Int(1, 2); `, 3) test(` abc.Func1Int8(1); `, 2) test(` abc.Func1Int16(1); `, 2) test(` abc.Func1Int32(1); `, 2) test(` abc.Func1Int64(1); `, 2) test(` abc.Func1Uint(1); `, 2) test(` abc.Func1Uint8(1); `, 2) test(` abc.Func1Uint16(1); `, 2) test(` abc.Func1Uint32(1); `, 2) test(` abc.Func1Uint64(1); `, 2) test(` abc.Func2StringInt("test", 1); `, "test:1") test(` abc.Func1IntVariadic(1, 2); `, 3) test(` abc.Func2IntVariadic("test", 1, 2); `, "test:3") test(` abc.Func2IntVariadic("test", [1, 2]); `, "test:3") test(` abc.Func2IntArrayVariadic("test", [1, 2]); `, "test:3") test(` abc.Func2IntArrayVariadic("test", [1, 2], [3, 4]); `, "test:10") test(` abc.Func2IntArrayVariadic("test", [[1, 2], [3, 4]]); `, "test:10") } }) } func Test_reflectMap(t *testing.T) { tt(t, func() { test, vm := test() // map[string]string { abc := map[string]string{ "Xyzzy": "Nothing happens.", "def": "1", } vm.Set("abc", abc) test(` abc.xyz = "pqr"; [ abc.Xyzzy, abc.def, abc.ghi ]; `, "Nothing happens.,1,") is(abc["xyz"], "pqr") } // map[string]float64 { abc := map[string]float64{ "Xyzzy": math.Pi, "def": 1, } vm.Set("abc", abc) test(` abc.xyz = "pqr"; abc.jkl = 10; [ abc.Xyzzy, abc.def, abc.ghi ]; `, "3.141592653589793,1,") is(abc["xyz"], math.NaN()) is(abc["jkl"], float64(10)) } // map[string]int32 { abc := map[string]int32{ "Xyzzy": 3, "def": 1, } vm.Set("abc", abc) test(` abc.xyz = "pqr"; abc.jkl = 10; [ abc.Xyzzy, abc.def, abc.ghi ]; `, "3,1,") is(abc["xyz"], 0) is(abc["jkl"], int32(10)) test(` delete abc["Xyzzy"]; `) _, exists := abc["Xyzzy"] is(exists, false) is(abc["Xyzzy"], 0) } // map[int32]string { abc := map[int32]string{ 0: "abc", 1: "def", } vm.Set("abc", abc) test(` abc[2] = "pqr"; //abc.jkl = 10; abc[3] = 10; [ abc[0], abc[1], abc[2], abc[3] ] `, "abc,def,pqr,10") is(abc[2], "pqr") is(abc[3], "10") test(` delete abc[2]; `) _, exists := abc[2] is(exists, false) } }) } func Test_reflectMapIterateKeys(t *testing.T) { tt(t, func() { test, vm := test() // map[string]interface{} { abc := map[string]interface{}{ "Xyzzy": "Nothing happens.", "def": 1, } vm.Set("abc", abc) test(` var keys = []; for (var key in abc) { keys.push(key); } keys.sort(); keys; `, "Xyzzy,def") } // map[uint]interface{} { abc := map[uint]interface{}{ 456: "Nothing happens.", 123: 1, } vm.Set("abc", abc) test(` var keys = []; for (var key in abc) { keys.push(key); } keys.sort(); keys; `, "123,456") } // map[byte]interface{} { abc := map[byte]interface{}{ 10: "Nothing happens.", 20: 1, } vm.Set("abc", abc) test(` for (var key in abc) { abc[key] = "123"; } `) is(abc[10], "123") is(abc[20], "123") } }) } func Test_reflectSlice(t *testing.T) { tt(t, func() { test, vm := test() // []bool { abc := []bool{ false, true, true, false, } vm.Set("abc", abc) test(` abc; `, "false,true,true,false") test(` abc[0] = true; abc[abc.length-1] = true; delete abc[2]; abc; `, "true,true,false,true") is(abc, []bool{true, true, false, true}) is(abc[len(abc)-1], true) } // []int32 { abc := make([]int32, 4) vm.Set("abc", abc) test(` abc; `, "0,0,0,0") test(`raise: abc[0] = "42"; abc[1] = 4.2; abc[2] = 3.14; abc; `, "RangeError: 4.2 to reflect.Kind: int32") is(abc, []int32{42, 0, 0, 0}) test(` delete abc[1]; delete abc[2]; `) is(abc[1], 0) is(abc[2], 0) } }) } func Test_reflectArray(t *testing.T) { tt(t, func() { test, vm := test() // []bool { abc := [4]bool{ false, true, true, false, } vm.Set("abc", abc) test(` abc; `, "false,true,true,false") // Unaddressable array test(` abc[0] = true; abc[abc.length-1] = true; abc; `, "false,true,true,false") // Again, unaddressable array is(abc, [4]bool{false, true, true, false}) is(abc[len(abc)-1], false) // ... } // []int32 { abc := make([]int32, 4) vm.Set("abc", abc) test(` abc; `, "0,0,0,0") test(`raise: abc[0] = "42"; abc[1] = 4.2; abc[2] = 3.14; abc; `, "RangeError: 4.2 to reflect.Kind: int32") is(abc, []int32{42, 0, 0, 0}) } // []bool { abc := [4]bool{ false, true, true, false, } vm.Set("abc", &abc) test(` abc; `, "false,true,true,false") test(` abc[0] = true; abc[abc.length-1] = true; delete abc[2]; abc; `, "true,true,false,true") is(abc, [4]bool{true, true, false, true}) is(abc[len(abc)-1], true) } // no common type { test(` abc = [1, 2.2, "str"]; abc; `, "1,2.2,str") val, err := vm.Get("abc") is(err, nil) abc, err := val.Export() is(err, nil) is(abc, []interface{}{int64(1), 2.2, "str"}) } // common type int { test(` abc = [1, 2, 3]; abc; `, "1,2,3") val, err := vm.Get("abc") is(err, nil) abc, err := val.Export() is(err, nil) is(abc, []int64{1, 2, 3}) } // common type string { test(` abc = ["str1", "str2", "str3"]; abc; `, "str1,str2,str3") val, err := vm.Get("abc") is(err, nil) abc, err := val.Export() is(err, nil) is(abc, []string{"str1", "str2", "str3"}) } // issue #269 { called := false vm.Set("blah", func(c FunctionCall) Value { v, err := c.Argument(0).Export() is(err, nil) is(v, []int64{3}) called = true return UndefinedValue() }) is(called, false) test(`var x = 3; blah([x])`) is(called, true) } }) } func Test_reflectArray_concat(t *testing.T) { tt(t, func() { test, vm := test() vm.Set("ghi", []string{"jkl", "mno"}) vm.Set("pqr", []interface{}{"jkl", 42, 3.14159, true}) test(` var def = { "abc": ["abc"], "xyz": ["xyz"] }; xyz = pqr.concat(ghi, def.abc, def, def.xyz); [ xyz, xyz.length ]; `, "jkl,42,3.14159,true,jkl,mno,abc,[object Object],xyz,9") }) } func Test_reflectMapInterface(t *testing.T) { tt(t, func() { test, vm := test() { abc := map[string]interface{}{ "Xyzzy": "Nothing happens.", "def": "1", "jkl": "jkl", } vm.Set("abc", abc) vm.Set("mno", &_abcStruct{}) test(` abc.xyz = "pqr"; abc.ghi = {}; abc.jkl = 3.14159; abc.mno = mno; mno.Abc = true; mno.Ghi = "Something happens."; [ abc.Xyzzy, abc.def, abc.ghi, abc.mno ]; `, "Nothing happens.,1,[object Object],[object Object]") is(abc["xyz"], "pqr") is(abc["ghi"], "[object Object]") is(abc["jkl"], float64(3.14159)) mno, valid := abc["mno"].(*_abcStruct) is(valid, true) is(mno.Abc, true) is(mno.Ghi, "Something happens.") } }) } func TestPassthrough(t *testing.T) { tt(t, func() { test, vm := test() { abc := &_abcStruct{ Mno: _mnoStruct{ Ghi: "", }, } vm.Set("abc", abc) test(` abc.Mno.Ghi; `, "") vm.Set("pqr", map[string]int8{ "xyzzy": 0, "Nothing happens.": 1, }) test(` abc.Ghi = "abc"; abc.Pqr = pqr; abc.Pqr["Nothing happens."]; `, 1) mno := _mnoStruct{ Ghi: "", } vm.Set("mno", mno) test(` abc.Mno = mno; abc.Mno.Ghi; `, "") } }) } type TestDynamicFunctionReturningInterface_MyStruct1 struct{} func (m *TestDynamicFunctionReturningInterface_MyStruct1) Error() string { return "MyStruct1" } type TestDynamicFunctionReturningInterface_MyStruct2 struct{} func (m *TestDynamicFunctionReturningInterface_MyStruct2) Error() string { return "MyStruct2" } func TestDynamicFunctionReturningInterface(t *testing.T) { tt(t, func() { test, vm := test() var l []func() error vm.Set("r", func(cb func() error) { l = append(l, cb) }) vm.Set("e1", func() error { return &TestDynamicFunctionReturningInterface_MyStruct1{} }) vm.Set("e2", func() error { return &TestDynamicFunctionReturningInterface_MyStruct2{} }) vm.Set("e3", func() error { return nil }) test("r(function() { return e1(); })", UndefinedValue()) test("r(function() { return e2(); })", UndefinedValue()) test("r(function() { return e3(); })", UndefinedValue()) test("r(function() { return null; })", UndefinedValue()) if l[0]() == nil { t.Fail() } if l[1]() == nil { t.Fail() } if l[2]() != nil { t.Fail() } if l[3]() != nil { t.Fail() } }) } func TestStructCallParameterConversion(t *testing.T) { tt(t, func() { test, vm := test() type T struct { StringValue string `json:"s"` BooleanValue bool `json:"b"` IntegerValue int `json:"i"` } var x T vm.Set("f", func(t T) bool { return t == x }) // test set properties x = T{"A", true, 1} test("f({s: 'A', b: true, i: 1})", true) // test zero-value properties x = T{"", false, 0} test("f({s: '', b: false, i: 0})", true) // test missing properties x = T{"", true, 1} test("f({b: true, i: 1})", true) x = T{"A", false, 1} test("f({s: 'A', i: 1})", true) x = T{"A", true, 0} test("f({s: 'A', b: true})", true) x = T{"", false, 0} test("f({})", true) // make sure it fails with extra properties x = T{"", false, 0} if _, err := vm.Run("f({x: true})"); err == nil { t.Fail() } }) } type TestTextUnmarshallerCallParameterConversion_MyStruct struct{} func (m *TestTextUnmarshallerCallParameterConversion_MyStruct) UnmarshalText(b []byte) error { if string(b) == "good" { return nil } return fmt.Errorf("NOT_GOOD: %s", string(b)) } func TestTextUnmarshallerCallParameterConversion(t *testing.T) { tt(t, func() { test, vm := test() vm.Set("f", func(t TestTextUnmarshallerCallParameterConversion_MyStruct) bool { return true }) // success test("f('good')", true) // explicit failure, should pass error message up if _, err := vm.Run("f('bad')"); err == nil || !strings.Contains(err.Error(), "NOT_GOOD: bad") { t.Fail() } // wrong input if _, err := vm.Run("f(null)"); err == nil { t.Fail() } // no input if _, err := vm.Run("f()"); err == nil { t.Fail() } }) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/regexp_test.go000066400000000000000000000164571331145703200252720ustar00rootroot00000000000000package otto import ( "fmt" "testing" ) func TestRegExp(t *testing.T) { tt(t, func() { test, _ := test() test(` [ /abc/.toString(), /abc/gim.toString(), ""+/abc/gi.toString(), new RegExp("1(\\d+)").toString(), ]; `, "/abc/,/abc/gim,/abc/gi,/1(\\d+)/") test(` [ new RegExp("abc").exec("123abc456"), null === new RegExp("xyzzy").exec("123abc456"), new RegExp("1(\\d+)").exec("123abc456"), new RegExp("xyzzy").test("123abc456"), new RegExp("1(\\d+)").test("123abc456"), new RegExp("abc").exec("123abc456"), ]; `, "abc,true,123,23,false,true,abc") test(`new RegExp("abc").toString()`, "/abc/") test(`new RegExp("abc", "g").toString()`, "/abc/g") test(`new RegExp("abc", "mig").toString()`, "/abc/gim") result := test(`/(a)?/.exec('b')`, ",") is(result._object().get("0"), "") is(result._object().get("1"), "undefined") is(result._object().get("length"), 2) result = test(`/(a)?(b)?/.exec('b')`, "b,,b") is(result._object().get("0"), "b") is(result._object().get("1"), "undefined") is(result._object().get("2"), "b") is(result._object().get("length"), 3) test(`/\u0041/.source`, "\\u0041") test(`/\a/.source`, "\\a") test(`/\;/.source`, "\\;") test(`/a\a/.source`, "a\\a") test(`/,\;/.source`, ",\\;") test(`/ \ /.source`, " \\ ") // Start sanity check... test("eval(\"/abc/\").source", "abc") test("eval(\"/\u0023/\").source", "#") test("eval(\"/\u0058/\").source", "X") test("eval(\"/\\\u0023/\").source == \"\\\u0023\"", true) test("'0x' + '0058'", "0x0058") test("'\\\\' + '0x' + '0058'", "\\0x0058") // ...stop sanity check test(`abc = '\\' + String.fromCharCode('0x' + '0058'); eval('/' + abc + '/').source`, "\\X") test(`abc = '\\' + String.fromCharCode('0x0058'); eval('/' + abc + '/').source == "\\\u0058"`, true) test(`abc = '\\' + String.fromCharCode('0x0023'); eval('/' + abc + '/').source == "\\\u0023"`, true) test(`abc = '\\' + String.fromCharCode('0x0078'); eval('/' + abc + '/').source == "\\\u0078"`, true) test(` var abc = Object.getOwnPropertyDescriptor(RegExp, "prototype"); [ [ typeof RegExp.prototype ], [ abc.writable, abc.enumerable, abc.configurable ] ]; `, "object,false,false,false") }) } func TestRegExp_global(t *testing.T) { tt(t, func() { test, _ := test() test(` var abc = /(?:ab|cd)\d?/g; var found = []; do { match = abc.exec("ab cd2 ab34 cd"); if (match !== null) { found.push(match[0]); } else { break; } } while (true); found; `, "ab,cd2,ab3,cd") }) } func TestRegExp_exec(t *testing.T) { tt(t, func() { test, _ := test() test(` abc = /./g; def = '123456'; ghi = 0; while (ghi < 100 && abc.exec(def) !== null) { ghi += 1; } [ ghi, def.length, ghi == def.length ]; `, "6,6,true") test(` abc = /[abc](\d)?/g; def = 'a0 b c1 d3'; ghi = 0; lastIndex = 0; while (ghi < 100 && abc.exec(def) !== null) { lastIndex = abc.lastIndex; ghi += 1; } [ ghi, lastIndex ]; `, "3,7") test(` var abc = /[abc](\d)?/.exec("a0 b c1 d3"); [ abc.length, abc.input, abc.index, abc ]; `, "2,a0 b c1 d3,0,a0,0") test(`raise: var exec = RegExp.prototype.exec; exec("Xyzzy"); `, "TypeError: Calling RegExp.exec on a non-RegExp object") test(` var abc = /\w{3}\d?/.exec("CE\uFFFFL\uFFDDbox127"); [ abc.input.length, abc.length, abc.input, abc.index, abc ]; `, "11,1,CE\uFFFFL\uFFDDbox127,5,box1") test(`RegExp.prototype.exec.length`, 1) test(`RegExp.prototype.exec.prototype`, "undefined") }) } func TestRegExp_test(t *testing.T) { tt(t, func() { test, _ := test() test(`RegExp.prototype.test.length`, 1) test(`RegExp.prototype.test.prototype`, "undefined") }) } func TestRegExp_toString(t *testing.T) { tt(t, func() { test, _ := test() test(`RegExp.prototype.toString.length`, 0) test(`RegExp.prototype.toString.prototype`, "undefined") }) } func TestRegExp_zaacbbbcac(t *testing.T) { if true { return } tt(t, func() { test, _ := test() // FIXME? TODO /(z)((a+)?(b+)?(c))*/.exec("zaacbbbcac") test(` var abc = /(z)((a+)?(b+)?(c))*/.exec("zaacbbbcac"); [ abc.length, abc.index, abc ]; `, "6,0,zaacbbbcac,z,ac,a,,c") }) } func TestRegExpCopying(t *testing.T) { tt(t, func() { test, _ := test() test(` abc = /xyzzy/i; def = RegExp(abc); abc.indicator = 1; [ abc.indicator, def.indicator ]; `, "1,1") test(`raise: RegExp(new RegExp("\\d"), "1"); `, "TypeError: Cannot supply flags when constructing one RegExp from another") }) } func TestRegExp_multiline(t *testing.T) { tt(t, func() { test, _ := test() test(` var abc = /s$/m.exec("pairs\nmakes\tdouble"); [ abc.length, abc.index, abc ]; `, "1,4,s") }) } func TestRegExp_source(t *testing.T) { tt(t, func() { test, _ := test() test(` [ /xyzzy/i.source, /./i.source ]; `, "xyzzy,.") test(` var abc = /./i; var def = new RegExp(abc); [ abc.source, def.source, abc.source === def.source ]; `, ".,.,true") test(` var abc = /./i; var def = abc.hasOwnProperty("source"); var ghi = abc.source; abc.source = "xyzzy"; [ def, abc.source ]; `, "true,.") }) } func TestRegExp_newRegExp(t *testing.T) { tt(t, func() { test, _ := test() test(` Math.toString(); var abc = new RegExp(Math,eval("\"g\"")); [ abc, abc.global ]; `, "/[object Math]/g,true") }) } func TestRegExp_flags(t *testing.T) { tt(t, func() { test, _ := test() test(` var abc = /./i; var def = new RegExp(abc); [ abc.multiline == def.multiline, abc.global == def.global, abc.ignoreCase == def.ignoreCase ]; `, "true,true,true") }) } func TestRegExp_controlCharacter(t *testing.T) { tt(t, func() { test, _ := test() for code := 0x41; code < 0x5a; code++ { string_ := string(code - 64) test(fmt.Sprintf(` var code = 0x%x; var string = String.fromCharCode(code %% 32); var result = (new RegExp("\\c" + String.fromCharCode(code))).exec(string); [ code, string, result ]; `, code), fmt.Sprintf("%d,%s,%s", code, string_, string_)) } }) } func TestRegExp_notNotEmptyCharacterClass(t *testing.T) { tt(t, func() { test, _ := test() test(` var abc = /[\s\S]a/m.exec("a\naba"); [ abc.length, abc.input, abc ]; `, "1,a\naba,\na") }) } func TestRegExp_compile(t *testing.T) { tt(t, func() { test, _ := test() test(` var abc = /[\s\S]a/; abc.compile('^\w+'); `, "undefined") }) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/registry/000077500000000000000000000000001331145703200242455ustar00rootroot00000000000000golang-github-robertkrimen-otto-0.0~git20180617.15f95af/registry/README.markdown000066400000000000000000000012301331145703200267420ustar00rootroot00000000000000# registry -- import "github.com/robertkrimen/otto/registry" Package registry is an expirmental package to facillitate altering the otto runtime via import. This interface can change at any time. ## Usage #### func Apply ```go func Apply(callback func(Entry)) ``` #### type Entry ```go type Entry struct { } ``` #### func Register ```go func Register(source func() string) *Entry ``` #### func (*Entry) Disable ```go func (self *Entry) Disable() ``` #### func (*Entry) Enable ```go func (self *Entry) Enable() ``` #### func (Entry) Source ```go func (self Entry) Source() string ``` -- **godocdown** http://github.com/robertkrimen/godocdown golang-github-robertkrimen-otto-0.0~git20180617.15f95af/registry/registry.go000066400000000000000000000014221331145703200264430ustar00rootroot00000000000000/* Package registry is an expirmental package to facillitate altering the otto runtime via import. This interface can change at any time. */ package registry var registry []*Entry = make([]*Entry, 0) type Entry struct { active bool source func() string } func newEntry(source func() string) *Entry { return &Entry{ active: true, source: source, } } func (self *Entry) Enable() { self.active = true } func (self *Entry) Disable() { self.active = false } func (self Entry) Source() string { return self.source() } func Apply(callback func(Entry)) { for _, entry := range registry { if !entry.active { continue } callback(*entry) } } func Register(source func() string) *Entry { entry := newEntry(source) registry = append(registry, entry) return entry } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/repl/000077500000000000000000000000001331145703200233375ustar00rootroot00000000000000golang-github-robertkrimen-otto-0.0~git20180617.15f95af/repl/autocompleter.go000066400000000000000000000017411331145703200265540ustar00rootroot00000000000000package repl import ( "regexp" "strings" "github.com/robertkrimen/otto" ) type autoCompleter struct { vm *otto.Otto } var lastExpressionRegex = regexp.MustCompile(`[a-zA-Z0-9]([a-zA-Z0-9\.]*[a-zA-Z0-9])?\.?$`) func (a *autoCompleter) Do(line []rune, pos int) ([][]rune, int) { lastExpression := lastExpressionRegex.FindString(string(line)) bits := strings.Split(lastExpression, ".") first := bits[:len(bits)-1] last := bits[len(bits)-1] var l []string if len(first) == 0 { c := a.vm.Context() l = make([]string, len(c.Symbols)) i := 0 for k := range c.Symbols { l[i] = k i++ } } else { r, err := a.vm.Eval(strings.Join(bits[:len(bits)-1], ".")) if err != nil { return nil, 0 } if o := r.Object(); o != nil { for _, v := range o.KeysByParent() { l = append(l, v...) } } } var r [][]rune for _, s := range l { if strings.HasPrefix(s, last) { r = append(r, []rune(strings.TrimPrefix(s, last))) } } return r, len(last) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/repl/repl.go000066400000000000000000000064561331145703200246430ustar00rootroot00000000000000// Package repl implements a REPL (read-eval-print loop) for otto. package repl import ( "fmt" "io" "strings" "sync/atomic" "github.com/robertkrimen/otto" "gopkg.in/readline.v1" ) var counter uint32 // DebuggerHandler implements otto's debugger handler signature, providing a // simple drop-in debugger implementation. func DebuggerHandler(vm *otto.Otto) { i := atomic.AddUint32(&counter, 1) // purposefully ignoring the error here - we can't do anything useful with // it except panicking, and that'd be pretty rude. it'd be easy enough for a // consumer to define an equivalent function that _does_ panic if desired. _ = RunWithPrompt(vm, fmt.Sprintf("DEBUGGER[%d]> ", i)) } // Run creates a REPL with the default prompt and no prelude. func Run(vm *otto.Otto) error { return RunWithOptions(vm, Options{}) } // RunWithPrompt runs a REPL with the given prompt and no prelude. func RunWithPrompt(vm *otto.Otto, prompt string) error { return RunWithOptions(vm, Options{ Prompt: prompt, }) } // RunWithPrelude runs a REPL with the default prompt and the given prelude. func RunWithPrelude(vm *otto.Otto, prelude string) error { return RunWithOptions(vm, Options{ Prelude: prelude, }) } // RunWithPromptAndPrelude runs a REPL with the given prompt and prelude. func RunWithPromptAndPrelude(vm *otto.Otto, prompt, prelude string) error { return RunWithOptions(vm, Options{ Prompt: prompt, Prelude: prelude, }) } // Options contains parameters for configuring a REPL session. type Options struct { // Prompt controls what's shown at the beginning of the line. If not // specified, this defaults to "> " Prompt string // Prelude is some text that's printed before control is given to the user. // By default this is empty. If specified, this will be printed with a // newline following it. Prelude string // Autocomplete controls whether this REPL session has autocompletion // enabled. The way autocomplete is implemented can incur a performance // penalty, so it's turned off by default. Autocomplete bool } // RunWithOptions runs a REPL with the given options. func RunWithOptions(vm *otto.Otto, options Options) error { prompt := options.Prompt if prompt == "" { prompt = "> " } c := &readline.Config{ Prompt: prompt, } if options.Autocomplete { c.AutoComplete = &autoCompleter{vm} } rl, err := readline.NewEx(c) if err != nil { return err } prelude := options.Prelude if prelude != "" { if _, err := io.Copy(rl.Stderr(), strings.NewReader(prelude+"\n")); err != nil { return err } rl.Refresh() } var d []string for { l, err := rl.Readline() if err != nil { if err == readline.ErrInterrupt { if d != nil { d = nil rl.SetPrompt(prompt) rl.Refresh() continue } break } return err } if l == "" { continue } d = append(d, l) s, err := vm.Compile("repl", strings.Join(d, "\n")) if err != nil { rl.SetPrompt(strings.Repeat(" ", len(prompt))) } else { rl.SetPrompt(prompt) d = nil v, err := vm.Eval(s) if err != nil { if oerr, ok := err.(*otto.Error); ok { io.Copy(rl.Stdout(), strings.NewReader(oerr.String())) } else { io.Copy(rl.Stdout(), strings.NewReader(err.Error())) } } else { rl.Stdout().Write([]byte(v.String() + "\n")) } } rl.Refresh() } return rl.Close() } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/result.go000066400000000000000000000007571331145703200242530ustar00rootroot00000000000000package otto import () type _resultKind int const ( resultNormal _resultKind = iota resultReturn resultBreak resultContinue ) type _result struct { kind _resultKind value Value target string } func newReturnResult(value Value) _result { return _result{resultReturn, value, ""} } func newContinueResult(target string) _result { return _result{resultContinue, emptyValue, target} } func newBreakResult(target string) _result { return _result{resultBreak, emptyValue, target} } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/runtime.go000066400000000000000000000512621331145703200244150ustar00rootroot00000000000000package otto import ( "encoding" "errors" "fmt" "math" "path" "reflect" "runtime" "strconv" "strings" "sync" "github.com/robertkrimen/otto/ast" "github.com/robertkrimen/otto/parser" ) type _global struct { Object *_object // Object( ... ), new Object( ... ) - 1 (length) Function *_object // Function( ... ), new Function( ... ) - 1 Array *_object // Array( ... ), new Array( ... ) - 1 String *_object // String( ... ), new String( ... ) - 1 Boolean *_object // Boolean( ... ), new Boolean( ... ) - 1 Number *_object // Number( ... ), new Number( ... ) - 1 Math *_object Date *_object // Date( ... ), new Date( ... ) - 7 RegExp *_object // RegExp( ... ), new RegExp( ... ) - 2 Error *_object // Error( ... ), new Error( ... ) - 1 EvalError *_object TypeError *_object RangeError *_object ReferenceError *_object SyntaxError *_object URIError *_object JSON *_object ObjectPrototype *_object // Object.prototype FunctionPrototype *_object // Function.prototype ArrayPrototype *_object // Array.prototype StringPrototype *_object // String.prototype BooleanPrototype *_object // Boolean.prototype NumberPrototype *_object // Number.prototype DatePrototype *_object // Date.prototype RegExpPrototype *_object // RegExp.prototype ErrorPrototype *_object // Error.prototype EvalErrorPrototype *_object TypeErrorPrototype *_object RangeErrorPrototype *_object ReferenceErrorPrototype *_object SyntaxErrorPrototype *_object URIErrorPrototype *_object } type _runtime struct { global _global globalObject *_object globalStash *_objectStash scope *_scope otto *Otto eval *_object // The builtin eval, for determine indirect versus direct invocation debugger func(*Otto) random func() float64 stackLimit int traceLimit int labels []string // FIXME lck sync.Mutex } func (self *_runtime) enterScope(scope *_scope) { scope.outer = self.scope if self.scope != nil { if self.stackLimit != 0 && self.scope.depth+1 >= self.stackLimit { panic(self.panicRangeError("Maximum call stack size exceeded")) } scope.depth = self.scope.depth + 1 } self.scope = scope } func (self *_runtime) leaveScope() { self.scope = self.scope.outer } // FIXME This is used in two places (cloning) func (self *_runtime) enterGlobalScope() { self.enterScope(newScope(self.globalStash, self.globalStash, self.globalObject)) } func (self *_runtime) enterFunctionScope(outer _stash, this Value) *_fnStash { if outer == nil { outer = self.globalStash } stash := self.newFunctionStash(outer) var thisObject *_object switch this.kind { case valueUndefined, valueNull: thisObject = self.globalObject default: thisObject = self.toObject(this) } self.enterScope(newScope(stash, stash, thisObject)) return stash } func (self *_runtime) putValue(reference _reference, value Value) { name := reference.putValue(value) if name != "" { // Why? -- If reference.base == nil // strict = false self.globalObject.defineProperty(name, value, 0111, false) } } func (self *_runtime) tryCatchEvaluate(inner func() Value) (tryValue Value, exception bool) { // resultValue = The value of the block (e.g. the last statement) // throw = Something was thrown // throwValue = The value of what was thrown // other = Something that changes flow (return, break, continue) that is not a throw // Otherwise, some sort of unknown panic happened, we'll just propagate it defer func() { if caught := recover(); caught != nil { if exception, ok := caught.(*_exception); ok { caught = exception.eject() } switch caught := caught.(type) { case _error: exception = true tryValue = toValue_object(self.newError(caught.name, caught.messageValue(), 0)) case Value: exception = true tryValue = caught default: panic(caught) } } }() tryValue = inner() return } // toObject func (self *_runtime) toObject(value Value) *_object { switch value.kind { case valueEmpty, valueUndefined, valueNull: panic(self.panicTypeError()) case valueBoolean: return self.newBoolean(value) case valueString: return self.newString(value) case valueNumber: return self.newNumber(value) case valueObject: return value._object() } panic(self.panicTypeError()) } func (self *_runtime) objectCoerce(value Value) (*_object, error) { switch value.kind { case valueUndefined: return nil, errors.New("undefined") case valueNull: return nil, errors.New("null") case valueBoolean: return self.newBoolean(value), nil case valueString: return self.newString(value), nil case valueNumber: return self.newNumber(value), nil case valueObject: return value._object(), nil } panic(self.panicTypeError()) } func checkObjectCoercible(rt *_runtime, value Value) { isObject, mustCoerce := testObjectCoercible(value) if !isObject && !mustCoerce { panic(rt.panicTypeError()) } } // testObjectCoercible func testObjectCoercible(value Value) (isObject bool, mustCoerce bool) { switch value.kind { case valueReference, valueEmpty, valueNull, valueUndefined: return false, false case valueNumber, valueString, valueBoolean: return false, true case valueObject: return true, false default: panic("this should never happen") } } func (self *_runtime) safeToValue(value interface{}) (Value, error) { result := Value{} err := catchPanic(func() { result = self.toValue(value) }) return result, err } // convertNumeric converts numeric parameter val from js to that of type t if it is safe to do so, otherwise it panics. // This allows literals (int64), bitwise values (int32) and the general form (float64) of javascript numerics to be passed as parameters to go functions easily. func (self *_runtime) convertNumeric(v Value, t reflect.Type) reflect.Value { val := reflect.ValueOf(v.export()) if val.Kind() == t.Kind() { return val } if val.Kind() == reflect.Interface { val = reflect.ValueOf(val.Interface()) } switch val.Kind() { case reflect.Float32, reflect.Float64: f64 := val.Float() switch t.Kind() { case reflect.Float64: return reflect.ValueOf(f64) case reflect.Float32: if reflect.Zero(t).OverflowFloat(f64) { panic(self.panicRangeError("converting float64 to float32 would overflow")) } return val.Convert(t) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: i64 := int64(f64) if float64(i64) != f64 { panic(self.panicRangeError(fmt.Sprintf("converting %v to %v would cause loss of precision", val.Type(), t))) } // The float represents an integer val = reflect.ValueOf(i64) default: panic(self.panicTypeError(fmt.Sprintf("cannot convert %v to %v", val.Type(), t))) } } switch val.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: i64 := val.Int() switch t.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: if reflect.Zero(t).OverflowInt(i64) { panic(self.panicRangeError(fmt.Sprintf("converting %v to %v would overflow", val.Type(), t))) } return val.Convert(t) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: if i64 < 0 { panic(self.panicRangeError(fmt.Sprintf("converting %v to %v would underflow", val.Type(), t))) } if reflect.Zero(t).OverflowUint(uint64(i64)) { panic(self.panicRangeError(fmt.Sprintf("converting %v to %v would overflow", val.Type(), t))) } return val.Convert(t) } case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: u64 := val.Uint() switch t.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: if u64 > math.MaxInt64 || reflect.Zero(t).OverflowInt(int64(u64)) { panic(self.panicRangeError(fmt.Sprintf("converting %v to %v would overflow", val.Type(), t))) } return val.Convert(t) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: if reflect.Zero(t).OverflowUint(u64) { panic(self.panicRangeError(fmt.Sprintf("converting %v to %v would overflow", val.Type(), t))) } return val.Convert(t) } } panic(self.panicTypeError(fmt.Sprintf("unsupported type %v for numeric conversion", val.Type()))) } var typeOfValue = reflect.TypeOf(Value{}) // convertCallParameter converts request val to type t if possible. // If the conversion fails due to overflow or type miss-match then it panics. // If no conversion is known then the original value is returned. func (self *_runtime) convertCallParameter(v Value, t reflect.Type) reflect.Value { if t == typeOfValue { return reflect.ValueOf(v) } if v.kind == valueObject { if gso, ok := v._object().value.(*_goStructObject); ok { if gso.value.Type().AssignableTo(t) { // please see TestDynamicFunctionReturningInterface for why this exists if t.Kind() == reflect.Interface && gso.value.Type().ConvertibleTo(t) { return gso.value.Convert(t) } else { return gso.value } } } if gao, ok := v._object().value.(*_goArrayObject); ok { if gao.value.Type().AssignableTo(t) { // please see TestDynamicFunctionReturningInterface for why this exists if t.Kind() == reflect.Interface && gao.value.Type().ConvertibleTo(t) { return gao.value.Convert(t) } else { return gao.value } } } } if t.Kind() == reflect.Interface { e := v.export() if e == nil { return reflect.Zero(t) } iv := reflect.ValueOf(e) if iv.Type().AssignableTo(t) { return iv } } tk := t.Kind() if tk == reflect.Ptr { switch v.kind { case valueEmpty, valueNull, valueUndefined: return reflect.Zero(t) default: var vv reflect.Value if err := catchPanic(func() { vv = self.convertCallParameter(v, t.Elem()) }); err == nil { if vv.CanAddr() { return vv.Addr() } pv := reflect.New(vv.Type()) pv.Elem().Set(vv) return pv } } } switch tk { case reflect.Bool: return reflect.ValueOf(v.bool()) case reflect.String: switch v.kind { case valueString: return reflect.ValueOf(v.value) case valueNumber: return reflect.ValueOf(fmt.Sprintf("%v", v.value)) } case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64: switch v.kind { case valueNumber: return self.convertNumeric(v, t) } case reflect.Slice: if o := v._object(); o != nil { if lv := o.get("length"); lv.IsNumber() { l := lv.number().int64 s := reflect.MakeSlice(t, int(l), int(l)) tt := t.Elem() if o.class == "Array" { for i := int64(0); i < l; i++ { p, ok := o.property[strconv.FormatInt(i, 10)] if !ok { continue } e, ok := p.value.(Value) if !ok { continue } ev := self.convertCallParameter(e, tt) s.Index(int(i)).Set(ev) } } else if o.class == "GoArray" { var gslice bool switch o.value.(type) { case *_goSliceObject: gslice = true case *_goArrayObject: gslice = false } for i := int64(0); i < l; i++ { var p *_property if gslice { p = goSliceGetOwnProperty(o, strconv.FormatInt(i, 10)) } else { p = goArrayGetOwnProperty(o, strconv.FormatInt(i, 10)) } if p == nil { continue } e, ok := p.value.(Value) if !ok { continue } ev := self.convertCallParameter(e, tt) s.Index(int(i)).Set(ev) } } return s } } case reflect.Map: if o := v._object(); o != nil && t.Key().Kind() == reflect.String { m := reflect.MakeMap(t) o.enumerate(false, func(k string) bool { m.SetMapIndex(reflect.ValueOf(k), self.convertCallParameter(o.get(k), t.Elem())) return true }) return m } case reflect.Func: if t.NumOut() > 1 { panic(self.panicTypeError("converting JavaScript values to Go functions with more than one return value is currently not supported")) } if o := v._object(); o != nil && o.class == "Function" { return reflect.MakeFunc(t, func(args []reflect.Value) []reflect.Value { l := make([]interface{}, len(args)) for i, a := range args { if a.CanInterface() { l[i] = a.Interface() } } rv, err := v.Call(nullValue, l...) if err != nil { panic(err) } if t.NumOut() == 0 { return nil } return []reflect.Value{self.convertCallParameter(rv, t.Out(0))} }) } case reflect.Struct: if o := v._object(); o != nil && o.class == "Object" { s := reflect.New(t) for _, k := range o.propertyOrder { var f *reflect.StructField for i := 0; i < t.NumField(); i++ { ff := t.Field(i) if j := ff.Tag.Get("json"); j != "" { if j == "-" { continue } a := strings.Split(j, ",") if a[0] == k { f = &ff break } } if ff.Name == k { f = &ff break } if strings.EqualFold(ff.Name, k) { f = &ff } } if f == nil { panic(self.panicTypeError("can't convert object; field %q was supplied but does not exist on target %v", k, t)) } ss := s for _, i := range f.Index { if ss.Kind() == reflect.Ptr { if ss.IsNil() { if !ss.CanSet() { panic(self.panicTypeError("can't set embedded pointer to unexported struct: %v", ss.Type().Elem())) } ss.Set(reflect.New(ss.Type().Elem())) } ss = ss.Elem() } ss = ss.Field(i) } ss.Set(self.convertCallParameter(o.get(k), ss.Type())) } return s.Elem() } } if tk == reflect.String { if o := v._object(); o != nil && o.hasProperty("toString") { if fn := o.get("toString"); fn.IsFunction() { sv, err := fn.Call(v) if err != nil { panic(err) } var r reflect.Value if err := catchPanic(func() { r = self.convertCallParameter(sv, t) }); err == nil { return r } } } return reflect.ValueOf(v.String()) } if v.kind == valueString { var s encoding.TextUnmarshaler if reflect.PtrTo(t).Implements(reflect.TypeOf(&s).Elem()) { r := reflect.New(t) if err := r.Interface().(encoding.TextUnmarshaler).UnmarshalText([]byte(v.string())); err != nil { panic(self.panicSyntaxError("can't convert to %s: %s", t.String(), err.Error())) } return r.Elem() } } s := "OTTO DOES NOT UNDERSTAND THIS TYPE" switch v.kind { case valueBoolean: s = "boolean" case valueNull: s = "null" case valueNumber: s = "number" case valueString: s = "string" case valueUndefined: s = "undefined" case valueObject: s = v.Class() } panic(self.panicTypeError("can't convert from %q to %q", s, t.String())) } func (self *_runtime) toValue(value interface{}) Value { switch value := value.(type) { case Value: return value case func(FunctionCall) Value: var name, file string var line int pc := reflect.ValueOf(value).Pointer() fn := runtime.FuncForPC(pc) if fn != nil { name = fn.Name() file, line = fn.FileLine(pc) file = path.Base(file) } return toValue_object(self.newNativeFunction(name, file, line, value)) case _nativeFunction: var name, file string var line int pc := reflect.ValueOf(value).Pointer() fn := runtime.FuncForPC(pc) if fn != nil { name = fn.Name() file, line = fn.FileLine(pc) file = path.Base(file) } return toValue_object(self.newNativeFunction(name, file, line, value)) case Object, *Object, _object, *_object: // Nothing happens. // FIXME We should really figure out what can come here. // This catch-all is ugly. default: { value := reflect.ValueOf(value) switch value.Kind() { case reflect.Ptr: switch reflect.Indirect(value).Kind() { case reflect.Struct: return toValue_object(self.newGoStructObject(value)) case reflect.Array: return toValue_object(self.newGoArray(value)) } case reflect.Struct: return toValue_object(self.newGoStructObject(value)) case reflect.Map: return toValue_object(self.newGoMapObject(value)) case reflect.Slice: return toValue_object(self.newGoSlice(value)) case reflect.Array: return toValue_object(self.newGoArray(value)) case reflect.Func: var name, file string var line int if v := reflect.ValueOf(value); v.Kind() == reflect.Ptr { pc := v.Pointer() fn := runtime.FuncForPC(pc) if fn != nil { name = fn.Name() file, line = fn.FileLine(pc) file = path.Base(file) } } typ := value.Type() return toValue_object(self.newNativeFunction(name, file, line, func(c FunctionCall) Value { nargs := typ.NumIn() if len(c.ArgumentList) != nargs { if typ.IsVariadic() { if len(c.ArgumentList) < nargs-1 { panic(self.panicRangeError(fmt.Sprintf("expected at least %d arguments; got %d", nargs-1, len(c.ArgumentList)))) } } else { panic(self.panicRangeError(fmt.Sprintf("expected %d argument(s); got %d", nargs, len(c.ArgumentList)))) } } in := make([]reflect.Value, len(c.ArgumentList)) callSlice := false for i, a := range c.ArgumentList { var t reflect.Type n := i if n >= nargs-1 && typ.IsVariadic() { if n > nargs-1 { n = nargs - 1 } t = typ.In(n).Elem() } else { t = typ.In(n) } // if this is a variadic Go function, and the caller has supplied // exactly the number of JavaScript arguments required, and this // is the last JavaScript argument, try treating the it as the // actual set of variadic Go arguments. if that succeeds, break // out of the loop. if typ.IsVariadic() && len(c.ArgumentList) == nargs && i == nargs-1 { var v reflect.Value if err := catchPanic(func() { v = self.convertCallParameter(a, typ.In(n)) }); err == nil { in[i] = v callSlice = true break } } in[i] = self.convertCallParameter(a, t) } var out []reflect.Value if callSlice { out = value.CallSlice(in) } else { out = value.Call(in) } switch len(out) { case 0: return Value{} case 1: return self.toValue(out[0].Interface()) default: s := make([]interface{}, len(out)) for i, v := range out { s[i] = self.toValue(v.Interface()) } return self.toValue(s) } })) } } } return toValue(value) } func (runtime *_runtime) newGoSlice(value reflect.Value) *_object { self := runtime.newGoSliceObject(value) self.prototype = runtime.global.ArrayPrototype return self } func (runtime *_runtime) newGoArray(value reflect.Value) *_object { self := runtime.newGoArrayObject(value) self.prototype = runtime.global.ArrayPrototype return self } func (runtime *_runtime) parse(filename string, src, sm interface{}) (*ast.Program, error) { return parser.ParseFileWithSourceMap(nil, filename, src, sm, 0) } func (runtime *_runtime) cmpl_parse(filename string, src, sm interface{}) (*_nodeProgram, error) { program, err := parser.ParseFileWithSourceMap(nil, filename, src, sm, 0) if err != nil { return nil, err } return cmpl_parse(program), nil } func (self *_runtime) parseSource(src, sm interface{}) (*_nodeProgram, *ast.Program, error) { switch src := src.(type) { case *ast.Program: return nil, src, nil case *Script: return src.program, nil, nil } program, err := self.parse("", src, sm) return nil, program, err } func (self *_runtime) cmpl_runOrEval(src, sm interface{}, eval bool) (Value, error) { result := Value{} cmpl_program, program, err := self.parseSource(src, sm) if err != nil { return result, err } if cmpl_program == nil { cmpl_program = cmpl_parse(program) } err = catchPanic(func() { result = self.cmpl_evaluate_nodeProgram(cmpl_program, eval) }) switch result.kind { case valueEmpty: result = Value{} case valueReference: result = result.resolve() } return result, err } func (self *_runtime) cmpl_run(src, sm interface{}) (Value, error) { return self.cmpl_runOrEval(src, sm, false) } func (self *_runtime) cmpl_eval(src, sm interface{}) (Value, error) { return self.cmpl_runOrEval(src, sm, true) } func (self *_runtime) parseThrow(err error) { if err == nil { return } switch err := err.(type) { case parser.ErrorList: { err := err[0] if err.Message == "Invalid left-hand side in assignment" { panic(self.panicReferenceError(err.Message)) } panic(self.panicSyntaxError(err.Message)) } } panic(self.panicSyntaxError(err.Error())) } func (self *_runtime) cmpl_parseOrThrow(src, sm interface{}) *_nodeProgram { program, err := self.cmpl_parse("", src, sm) self.parseThrow(err) // Will panic/throw appropriately return program } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/runtime_test.go000066400000000000000000000376771331145703200254720ustar00rootroot00000000000000package otto import ( "math" "testing" ) // FIXME terst, Review tests func TestOperator(t *testing.T) { tt(t, func() { test, vm := test() test("xyzzy = 1") test("xyzzy", 1) if true { vm.Set("twoPlusTwo", func(FunctionCall) Value { return toValue(5) }) test("twoPlusTwo( 1 )", 5) test("1 + twoPlusTwo( 1 )", 6) test("-1 + twoPlusTwo( 1 )", 4) } test("result = 4") test("result", 4) test("result += 1") test("result", 5) test("result *= 2") test("result", 10) test("result /= 2") test("result", 5) test("result = 112.51 % 3.1") test("result", 0.9100000000000019) test("result = 'Xyzzy'") test("result", "Xyzzy") test("result = 'Xyz' + 'zy'") test("result", "Xyzzy") test("result = \"Xyzzy\"") test("result", "Xyzzy") test("result = 1; result = result") test("result", 1) test(` var result64 = 64 , result10 = 10 `) test("result64", 64) test("result10", 10) test(` result = 1; result += 1; `) test("result", 2) }) } func TestFunction_(t *testing.T) { tt(t, func() { test, _ := test() test(` result = 2 xyzzy = function() { result += 1 } xyzzy() result; `, 3) test(` xyzzy = function() { return 1 } result = xyzzy() `, 1) test(` xyzzy = function() {} result = xyzzy() `, "undefined") test(` xyzzy = function() { return 64 return 1 } result = xyzzy() `, 64) test(` result = 4 xyzzy = function() { result = 2 } xyzzy(); result; `, 2) test(` result = 4 xyzzy = function() { var result result = 2 } xyzzy(); result; `, 4) test(` xyzzy = function() { var result = 4 return result } result = xyzzy() `, 4) test(` xyzzy = function() { function test() { var result = 1 return result } return test() + 1 } result = xyzzy() + 1 `, 3) test(` xyzzy = function() { function test() { var result = 1 return result } _xyzzy = 2 var result = _xyzzy + test() + 1 return result } result = xyzzy() + 1; [ result, _xyzzy ]; `, "5,2") test(` xyzzy = function(apple) { return 1 } result = xyzzy(1) `, 1) test(` xyzzy = function(apple) { return apple + 1 } result = xyzzy(2) `, 3) test(` { result = 1 result += 1; } `, 2) test(` var global = 1 outer = function() { var global = 2 var inner = function(){ return global } return inner() } result = outer() `, 2) test(` var apple = 1 var banana = function() { return apple } var cherry = function() { var apple = 2 return banana() } result = cherry() `, 1) test(` function xyz() { }; delete xyz; `, false) test(` var abc = function __factorial(def){ if (def === 1) { return def; } else { return __factorial(def-1)*def; } }; abc(3); `, 6) }) } func TestDoWhile(t *testing.T) { tt(t, func() { test, _ := test() test(` limit = 4; result = 0; do { result = result + 1; limit = limit - 1; } while (limit); result; `, 4) test(` result = eval("do {abc=1; break; abc=2;} while (0);"); [ result, abc ]; `, "1,1") }) } func TestContinueBreak(t *testing.T) { tt(t, func() { test, _ := test() test(` limit = 4 result = 0 while (limit) { limit = limit - 1 if (limit) { } else { break } result = result + 1 } [ result, limit ]; `, "3,0") test(` limit = 4 result = 0 while (limit) { limit = limit - 1 if (limit) { continue } else { break } result = result + 1 } result; `, 0) test(` limit = 4 result = 0 do { limit = limit - 1 if (limit) { continue } else { break } result = result + 1 } while (limit) result; `, 0) }) } func TestTryCatchError(t *testing.T) { tt(t, func() { test, _ := test() test(` var abc try { 1() } catch (def) { abc = def } abc; `, "TypeError: 1 is not a function") }) } func TestPositiveNegativeZero(t *testing.T) { tt(t, func() { test, _ := test() test(`1/0`, _Infinity) test(`1/-0`, -_Infinity) test(` abc = -0 1/abc `, -_Infinity) }) } func TestComparison(t *testing.T) { tt(t, func() { test, _ := test() test(` undefined = 1; undefined; `, "undefined") test("undefined == undefined", true) test("undefined != undefined", false) test("null == null", true) test("null != null", false) test("0 == 1", false) is(negativeZero(), -0) is(positiveZero(), 0) is(math.Signbit(negativeZero()), true) is(positiveZero() == negativeZero(), true) test("1 == 1", true) test("'Hello, World.' == 'Goodbye, World.'", false) test("'Hello, World.' == true", false) test("'Hello, World.' == false", false) test("'Hello, World.' == 1", false) test("1 == 'Hello, World.'", false) is(parseNumber("-1"), -1) test("0+Object", "0function Object() { [native code] }") }) } func TestComparisonRelational(t *testing.T) { tt(t, func() { test, _ := test() test("0 < 0", false) test("0 > 0", false) test("0 <= 0", true) test("0 >= 0", true) test("' 0' >= 0", true) test("'_ 0' >= 0", false) }) } func TestArguments(t *testing.T) { tt(t, func() { test, _ := test() test(` xyzzy = function() { return arguments[0] } result = xyzzy("xyzzy"); `, "xyzzy") test(` xyzzy = function() { arguments[0] = "abcdef" return arguments[0] } result = xyzzy("xyzzy"); `, "abcdef") test(` xyzzy = function(apple) { apple = "abcdef" return arguments[0] } result = xyzzy("xyzzy"); `, "abcdef") test(` (function(){ return arguments })() `, "[object Arguments]") test(` (function(){ return arguments.length })() `, 0) test(` (function(){ return arguments.length })(1, 2, 4, 8, 10) `, 5) }) } func TestObjectLiteral(t *testing.T) { tt(t, func() { test, _ := test() test(` ({}); `, "[object Object]") test(` var abc = { xyzzy: "Nothing happens.", get 1e2() { return 3.14159; }, get null() { return true; }, get "[\n]"() { return "<>"; } }; [ abc["1e2"], abc.null, abc["[\n]"] ]; `, "3.14159,true,<>") test(` var abc = { xyzzy: "Nothing happens.", set 1e2() { this[3.14159] = 100; return Math.random(); }, set null(def) { this.def = def; return Math.random(); }, }; [ abc["1e2"] = Infinity, abc[3.14159], abc.null = "xyz", abc.def ]; `, "Infinity,100,xyz,xyz") }) } func TestUnaryPrefix(t *testing.T) { tt(t, func() { test, _ := test() test(` var result = 0; [++result, result]; `, "1,1") test(` result = 0; [--result, result]; `, "-1,-1") test(` var object = { valueOf: function() { return 1; } }; result = ++object; [ result, typeof result ]; `, "2,number") test(` var object = { valueOf: function() { return 1; } }; result = --object; [ result, typeof result ]; `, "0,number") }) } func TestUnaryPostfix(t *testing.T) { tt(t, func() { test, _ := test() test(` var result = 0; result++; [ result++, result ]; `, "1,2") test(` result = 0; result--; [ result--, result ]; `, "-1,-2") test(` var object = { valueOf: function() { return 1; } }; result = object++; [ result, typeof result ]; `, "1,number") test(` var object = { valueOf: function() { return 1; } }; result = object-- [ result, typeof result ]; `, "1,number") }) } func TestBinaryLogicalOperation(t *testing.T) { tt(t, func() { test, _ := test() test(` abc = true def = false ghi = false jkl = false result = abc && def || ghi && jkl `, false) test(` abc = true def = true ghi = false jkl = false result = abc && def || ghi && jkl `, true) }) } func TestBinaryBitwiseOperation(t *testing.T) { tt(t, func() { test, _ := test() test(` abc = 1 & 2; def = 1 & 3; ghi = 1 | 3; jkl = 1 ^ 2; mno = 1 ^ 3; [ abc, def, ghi, jkl, mno ]; `, "0,1,3,3,2") }) } func TestBinaryShiftOperation(t *testing.T) { tt(t, func() { test, _ := test() test(` high = (1 << 30) - 1 + (1 << 30) low = -high - 1 abc = 23 << 1 def = -105 >> 1 ghi = 23 << 2 jkl = 1 >>> 31 mno = 1 << 64 pqr = 1 >> 2 stu = -2 >> 4 vwx = low >> 1 yz = low >>> 1 `) test("abc", 46) test("def", -53) test("ghi", 92) test("jkl", 0) test("mno", 1) test("pqr", 0) test("stu", -1) test("vwx", -1073741824) test("yz", 1073741824) }) } func TestParenthesizing(t *testing.T) { tt(t, func() { test, _ := test() test(` abc = 1 + 2 * 3 def = (1 + 2) * 3 ghi = !(false || true) jkl = !false || true `) test("abc", 7) test("def", 9) test("ghi", false) test("jkl", true) }) } func Test_instanceof(t *testing.T) { tt(t, func() { test, _ := test() test(` abc = {} instanceof Object; `, true) test(` abc = "abc" instanceof Object; `, false) test(`raise: abc = {} instanceof "abc"; `, "TypeError: Expecting a function in instanceof check, but got: abc") test(`raise: "xyzzy" instanceof Math; `, "TypeError") }) } func TestIn(t *testing.T) { tt(t, func() { test, _ := test() test(` abc = "prototype" in Object; def = "xyzzy" in Object; [ abc, def ]; `, "true,false") }) } func Test_new(t *testing.T) { tt(t, func() { test, _ := test() test(` abc = new Boolean; def = new Boolean(1); [ abc, def ]; `, "false,true") }) } func TestNewFunction(t *testing.T) { tt(t, func() { test, _ := test() test(` new Function("return 11")() `, 11) test(` abc = 10 new Function("abc += 1")() abc `, 11) test(` new Function("a", "b", "c", "return b + 2")(10, 11, 12) `, 13) test(`raise: new 1 `, "TypeError: 1 is not a function") // TODO Better error reporting: new this test(`raise: new this `, "TypeError: [object environment] is not a function") test(`raise: new {} `, "TypeError: [object Object] is not a function") }) } func TestNewPrototype(t *testing.T) { tt(t, func() { test, _ := test() test(` abc = { 'xyzzy': 'Nothing happens.' } function Xyzzy(){} Xyzzy.prototype = abc; (new Xyzzy()).xyzzy `, "Nothing happens.") }) } func TestBlock(t *testing.T) { tt(t, func() { test, _ := test() test(` var abc=0; var ghi; def: { do { abc++; if (!(abc < 10)) { break def; ghi = "ghi"; } } while (true); } [ abc,ghi ]; `, "10,") }) } func Test_toString(t *testing.T) { tt(t, func() { test, _ := test() test(` [undefined+""] `, "undefined") }) } func TestEvaluationOrder(t *testing.T) { tt(t, func() { test, _ := test() test(` var abc = 0; abc < (abc = 1) === true; `, true) }) } func TestClone(t *testing.T) { tt(t, func() { vm1 := New() vm1.Run(` var abc = 1; `) vm2 := vm1.clone() vm1.Run(` abc += 2; `) vm2.Run(` abc += 4; `) is(vm1.getValue("abc"), 3) is(vm2.getValue("abc"), 5) }) } func Test_debugger(t *testing.T) { tt(t, func() { called := false vm := New() vm.SetDebuggerHandler(func(o *Otto) { is(o, vm) called = true }) _, err := vm.Run(`debugger`) is(err, nil) is(called, true) }) tt(t, func() { called := false vm := New() vm.SetDebuggerHandler(func(o *Otto) { is(o, vm) called = true }) _, err := vm.Run(`null`) is(err, nil) is(called, false) }) tt(t, func() { vm := New() _, err := vm.Run(`debugger`) is(err, nil) }) } func Test_random(t *testing.T) { tt(t, func() { vm := New() vm.SetRandomSource(func() float64 { return 1 }) r, err := vm.Run(`Math.random()`) is(err, nil) f, err := r.ToFloat() is(err, nil) is(f, 1) }) tt(t, func() { vm := New() r1, err := vm.Run(`Math.random()`) is(err, nil) f1, err := r1.ToFloat() is(err, nil) r2, err := vm.Run(`Math.random()`) is(err, nil) f2, err := r2.ToFloat() is(err, nil) is(f1 == f2, false) }) } func Test_stringArray(t *testing.T) { getStrings := func() []string { return []string{"these", "are", "strings"} } concatStrings := func(a []string) string { if len(a) == 0 { return "" } r := a[0] for i := 1; i < len(a); i++ { r += " " r += a[i] } return r } tt(t, func() { vm := New() vm.Set("getStrings", getStrings) vm.Set("concatStrings", concatStrings) r1, err := vm.Run(`var a = getStrings(); concatStrings(a)`) is(err, nil) is(r1, "these are strings") }) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/scope.go000066400000000000000000000010151331145703200240320ustar00rootroot00000000000000package otto // _scope: // entryFile // entryIdx // top? // outer => nil // _stash: // lexical // variable // // _thisStash (ObjectEnvironment) // _fnStash // _dclStash // An ECMA-262 ExecutionContext type _scope struct { lexical _stash variable _stash this *_object eval bool // Replace this with kind? outer *_scope depth int frame _frame } func newScope(lexical _stash, variable _stash, this *_object) *_scope { return &_scope{ lexical: lexical, variable: variable, this: this, } } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/script.go000066400000000000000000000055321331145703200242350ustar00rootroot00000000000000package otto import ( "bytes" "encoding/gob" "errors" ) var ErrVersion = errors.New("version mismatch") var scriptVersion = "2014-04-13/1" // Script is a handle for some (reusable) JavaScript. // Passing a Script value to a run method will evaluate the JavaScript. // type Script struct { version string program *_nodeProgram filename string src string } // Compile will parse the given source and return a Script value or nil and // an error if there was a problem during compilation. // // script, err := vm.Compile("", `var abc; if (!abc) abc = 0; abc += 2; abc;`) // vm.Run(script) // func (self *Otto) Compile(filename string, src interface{}) (*Script, error) { return self.CompileWithSourceMap(filename, src, nil) } // CompileWithSourceMap does the same thing as Compile, but with the obvious // difference of applying a source map. func (self *Otto) CompileWithSourceMap(filename string, src, sm interface{}) (*Script, error) { program, err := self.runtime.parse(filename, src, sm) if err != nil { return nil, err } cmpl_program := cmpl_parse(program) script := &Script{ version: scriptVersion, program: cmpl_program, filename: filename, src: program.File.Source(), } return script, nil } func (self *Script) String() string { return "// " + self.filename + "\n" + self.src } // MarshalBinary will marshal a script into a binary form. A marshalled script // that is later unmarshalled can be executed on the same version of the otto runtime. // // The binary format can change at any time and should be considered unspecified and opaque. // func (self *Script) marshalBinary() ([]byte, error) { var bfr bytes.Buffer encoder := gob.NewEncoder(&bfr) err := encoder.Encode(self.version) if err != nil { return nil, err } err = encoder.Encode(self.program) if err != nil { return nil, err } err = encoder.Encode(self.filename) if err != nil { return nil, err } err = encoder.Encode(self.src) if err != nil { return nil, err } return bfr.Bytes(), nil } // UnmarshalBinary will vivify a marshalled script into something usable. If the script was // originally marshalled on a different version of the otto runtime, then this method // will return an error. // // The binary format can change at any time and should be considered unspecified and opaque. // func (self *Script) unmarshalBinary(data []byte) error { decoder := gob.NewDecoder(bytes.NewReader(data)) err := decoder.Decode(&self.version) if err != nil { goto error } if self.version != scriptVersion { err = ErrVersion goto error } err = decoder.Decode(&self.program) if err != nil { goto error } err = decoder.Decode(&self.filename) if err != nil { goto error } err = decoder.Decode(&self.src) if err != nil { goto error } return nil error: self.version = "" self.program = nil self.filename = "" self.src = "" return err } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/script_test.go000066400000000000000000000032171331145703200252720ustar00rootroot00000000000000package otto import ( "testing" ) func TestScript(t *testing.T) { tt(t, func() { vm := New() script, err := vm.Compile("xyzzy", `var abc; if (!abc) abc = 0; abc += 2; abc;`) is(err, nil) str := script.String() is(str, "// xyzzy\nvar abc; if (!abc) abc = 0; abc += 2; abc;") value, err := vm.Run(script) is(err, nil) is(value, 2) if true { return } tmp, err := script.marshalBinary() is(err, nil) is(len(tmp), 1228) { script := &Script{} err = script.unmarshalBinary(tmp) is(err, nil) is(script.String(), str) value, err = vm.Run(script) is(err, nil) is(value, 4) tmp, err = script.marshalBinary() is(err, nil) is(len(tmp), 1228) } { script := &Script{} err = script.unmarshalBinary(tmp) is(err, nil) is(script.String(), str) value, err := vm.Run(script) is(err, nil) is(value, 6) tmp, err = script.marshalBinary() is(err, nil) is(len(tmp), 1228) } { version := scriptVersion scriptVersion = "bogus" script := &Script{} err = script.unmarshalBinary(tmp) is(err, "version mismatch") is(script.String(), "// \n") is(script.version, "") is(script.program == nil, true) is(script.filename, "") is(script.src, "") scriptVersion = version } }) } func TestFunctionCall_CallerLocation(t *testing.T) { tt(t, func() { vm := New() vm.Set("loc", func(call FunctionCall) Value { return toValue(call.CallerLocation()) }) script, err := vm.Compile("somefile.js", `var where = loc();`) is(err, nil) _, err = vm.Run(script) is(err, nil) where, err := vm.Get("where") is(err, nil) is(where, "somefile.js:1:13") }) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/sourcemap_test.go000066400000000000000000000104251331145703200257630ustar00rootroot00000000000000package otto import ( "testing" ) const ( testSourcemapCodeOriginal = "function functionA(argA, argB) {\n functionB(argA, argB);\n}\n\nfunction functionB(argA, argB) {\n functionExternal(argA, argB);\n}" testSourcemapCodeMangled = "function functionA(argA,argB){functionB(argA,argB)}function functionB(argA,argB){functionExternal(argA,argB)}" testSourcemapContent = `{"version":3,"sources":["hello.js"],"names":["functionA","argA","argB","functionB","functionExternal"],"mappings":"AAAA,QAASA,WAAUC,KAAMC,MACvBC,UAAUF,KAAMC,MAGlB,QAASC,WAAUF,KAAMC,MACvBE,iBAAiBH,KAAMC"}` testSourcemapInline = "function functionA(argA,argB){functionB(argA,argB)}function functionB(argA,argB){functionExternal(argA,argB)}\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImhlbGxvLmpzIl0sIm5hbWVzIjpbImZ1bmN0aW9uQSIsImFyZ0EiLCJhcmdCIiwiZnVuY3Rpb25CIiwiZnVuY3Rpb25FeHRlcm5hbCJdLCJtYXBwaW5ncyI6IkFBQUEsUUFBU0EsV0FBVUMsS0FBTUMsTUFDdkJDLFVBQVVGLEtBQU1DLE1BR2xCLFFBQVNDLFdBQVVGLEtBQU1DLE1BQ3ZCRSxpQkFBaUJILEtBQU1DIn0=" testSourcemapOriginalStack = "ReferenceError: 'functionExternal' is not defined\n at functionB (hello.js:6:3)\n at functionA (hello.js:2:3)\n at :1:1\n" testSourcemapMangledStack = "ReferenceError: 'functionExternal' is not defined\n at functionB (hello.js:1:82)\n at functionA (hello.js:1:31)\n at :1:1\n" testSourcemapMappedStack = "ReferenceError: 'functionExternal' is not defined\n at functionB (hello.js:6:2)\n at functionA (hello.js:2:2)\n at :1:1\n" ) func TestSourceMapOriginalWithNoSourcemap(t *testing.T) { tt(t, func() { vm := New() s, err := vm.Compile("hello.js", testSourcemapCodeOriginal) if err != nil { panic(err) } if _, err := vm.Run(s); err != nil { panic(err) } _, err = vm.Run(`functionA()`) if err == nil { panic("error should not be nil") } is(err.(*Error).String(), testSourcemapOriginalStack) }) } func TestSourceMapMangledWithNoSourcemap(t *testing.T) { tt(t, func() { vm := New() s, err := vm.Compile("hello.js", testSourcemapCodeMangled) if err != nil { panic(err) } if _, err := vm.Run(s); err != nil { panic(err) } _, err = vm.Run(`functionA()`) if err == nil { panic("error should not be nil") } is(err.(*Error).String(), testSourcemapMangledStack) }) } func TestSourceMapMangledWithSourcemap(t *testing.T) { tt(t, func() { vm := New() s, err := vm.CompileWithSourceMap("hello.js", testSourcemapCodeMangled, testSourcemapContent) if err != nil { panic(err) } if _, err := vm.Run(s); err != nil { panic(err) } _, err = vm.Run(`functionA()`) if err == nil { panic("error should not be nil") } is(err.(*Error).String(), testSourcemapMappedStack) }) } func TestSourceMapMangledWithInlineSourcemap(t *testing.T) { tt(t, func() { vm := New() s, err := vm.CompileWithSourceMap("hello.js", testSourcemapInline, nil) if err != nil { panic(err) } if _, err := vm.Run(s); err != nil { panic(err) } _, err = vm.Run(`functionA()`) if err == nil { panic("error should not be nil") } is(err.(*Error).String(), testSourcemapMappedStack) }) } func TestSourceMapContextPosition(t *testing.T) { tt(t, func() { vm := New() s, err := vm.CompileWithSourceMap("hello.js", testSourcemapCodeMangled, testSourcemapContent) if err != nil { panic(err) } if _, err := vm.Run(s); err != nil { panic(err) } vm.Set("functionExternal", func(c FunctionCall) Value { ctx := c.Otto.Context() is(ctx.Filename, "hello.js") is(ctx.Line, 6) is(ctx.Column, 2) return UndefinedValue() }) if _, err := vm.Run(`functionA()`); err != nil { panic(err) } }) } func TestSourceMapContextStacktrace(t *testing.T) { tt(t, func() { vm := New() s, err := vm.CompileWithSourceMap("hello.js", testSourcemapCodeMangled, testSourcemapContent) if err != nil { panic(err) } if _, err := vm.Run(s); err != nil { panic(err) } vm.Set("functionExternal", func(c FunctionCall) Value { ctx := c.Otto.Context() is(ctx.Stacktrace, []string{ "functionB (hello.js:6:2)", "functionA (hello.js:2:2)", ":1:1", }) return UndefinedValue() }) if _, err := vm.Run(`functionA()`); err != nil { panic(err) } }) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/stash.go000066400000000000000000000143101331145703200240450ustar00rootroot00000000000000package otto import ( "fmt" ) // ====== // _stash // ====== type _stash interface { hasBinding(string) bool // createBinding(string, bool, Value) // CreateMutableBinding setBinding(string, Value, bool) // SetMutableBinding getBinding(string, bool) Value // GetBindingValue deleteBinding(string) bool // setValue(string, Value, bool) // createBinding + setBinding outer() _stash runtime() *_runtime newReference(string, bool, _at) _reference clone(clone *_clone) _stash } // ========== // _objectStash // ========== type _objectStash struct { _runtime *_runtime _outer _stash object *_object } func (self *_objectStash) runtime() *_runtime { return self._runtime } func (runtime *_runtime) newObjectStash(object *_object, outer _stash) *_objectStash { if object == nil { object = runtime.newBaseObject() object.class = "environment" } return &_objectStash{ _runtime: runtime, _outer: outer, object: object, } } func (in *_objectStash) clone(clone *_clone) _stash { out, exists := clone.objectStash(in) if exists { return out } *out = _objectStash{ clone.runtime, clone.stash(in._outer), clone.object(in.object), } return out } func (self *_objectStash) hasBinding(name string) bool { return self.object.hasProperty(name) } func (self *_objectStash) createBinding(name string, deletable bool, value Value) { if self.object.hasProperty(name) { panic(hereBeDragons()) } mode := _propertyMode(0111) if !deletable { mode = _propertyMode(0110) } // TODO False? self.object.defineProperty(name, value, mode, false) } func (self *_objectStash) setBinding(name string, value Value, strict bool) { self.object.put(name, value, strict) } func (self *_objectStash) setValue(name string, value Value, throw bool) { if !self.hasBinding(name) { self.createBinding(name, true, value) // Configurable by default } else { self.setBinding(name, value, throw) } } func (self *_objectStash) getBinding(name string, throw bool) Value { if self.object.hasProperty(name) { return self.object.get(name) } if throw { // strict? panic(self._runtime.panicReferenceError("Not Defined", name)) } return Value{} } func (self *_objectStash) deleteBinding(name string) bool { return self.object.delete(name, false) } func (self *_objectStash) outer() _stash { return self._outer } func (self *_objectStash) newReference(name string, strict bool, at _at) _reference { return newPropertyReference(self._runtime, self.object, name, strict, at) } // ========= // _dclStash // ========= type _dclStash struct { _runtime *_runtime _outer _stash property map[string]_dclProperty } type _dclProperty struct { value Value mutable bool deletable bool readable bool } func (runtime *_runtime) newDeclarationStash(outer _stash) *_dclStash { return &_dclStash{ _runtime: runtime, _outer: outer, property: map[string]_dclProperty{}, } } func (in *_dclStash) clone(clone *_clone) _stash { out, exists := clone.dclStash(in) if exists { return out } property := make(map[string]_dclProperty, len(in.property)) for index, value := range in.property { property[index] = clone.dclProperty(value) } *out = _dclStash{ clone.runtime, clone.stash(in._outer), property, } return out } func (self *_dclStash) hasBinding(name string) bool { _, exists := self.property[name] return exists } func (self *_dclStash) runtime() *_runtime { return self._runtime } func (self *_dclStash) createBinding(name string, deletable bool, value Value) { _, exists := self.property[name] if exists { panic(fmt.Errorf("createBinding: %s: already exists", name)) } self.property[name] = _dclProperty{ value: value, mutable: true, deletable: deletable, readable: false, } } func (self *_dclStash) setBinding(name string, value Value, strict bool) { property, exists := self.property[name] if !exists { panic(fmt.Errorf("setBinding: %s: missing", name)) } if property.mutable { property.value = value self.property[name] = property } else { self._runtime.typeErrorResult(strict) } } func (self *_dclStash) setValue(name string, value Value, throw bool) { if !self.hasBinding(name) { self.createBinding(name, false, value) // NOT deletable by default } else { self.setBinding(name, value, throw) } } // FIXME This is called a __lot__ func (self *_dclStash) getBinding(name string, throw bool) Value { property, exists := self.property[name] if !exists { panic(fmt.Errorf("getBinding: %s: missing", name)) } if !property.mutable && !property.readable { if throw { // strict? panic(self._runtime.panicTypeError()) } return Value{} } return property.value } func (self *_dclStash) deleteBinding(name string) bool { property, exists := self.property[name] if !exists { return true } if !property.deletable { return false } delete(self.property, name) return true } func (self *_dclStash) outer() _stash { return self._outer } func (self *_dclStash) newReference(name string, strict bool, _ _at) _reference { return &_stashReference{ name: name, base: self, } } // ======== // _fnStash // ======== type _fnStash struct { _dclStash arguments *_object indexOfArgumentName map[string]string } func (runtime *_runtime) newFunctionStash(outer _stash) *_fnStash { return &_fnStash{ _dclStash: _dclStash{ _runtime: runtime, _outer: outer, property: map[string]_dclProperty{}, }, } } func (in *_fnStash) clone(clone *_clone) _stash { out, exists := clone.fnStash(in) if exists { return out } dclStash := in._dclStash.clone(clone).(*_dclStash) index := make(map[string]string, len(in.indexOfArgumentName)) for name, value := range in.indexOfArgumentName { index[name] = value } *out = _fnStash{ _dclStash: *dclStash, arguments: clone.object(in.arguments), indexOfArgumentName: index, } return out } func getStashProperties(stash _stash) (keys []string) { switch vars := stash.(type) { case *_dclStash: for k := range vars.property { keys = append(keys, k) } case *_fnStash: for k := range vars.property { keys = append(keys, k) } case *_objectStash: for k := range vars.object.property { keys = append(keys, k) } default: panic("unknown stash type") } return } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/string_test.go000066400000000000000000000255141331145703200253000ustar00rootroot00000000000000package otto import ( "testing" ) func TestString(t *testing.T) { tt(t, func() { test, _ := test() test(` abc = (new String("xyzzy")).length; def = new String().length; ghi = new String("Nothing happens.").length; `) test("abc", 5) test("def", 0) test("ghi", 16) test(`"".length`, 0) test(`"a\uFFFFbc".length`, 4) test(`String(+0)`, "0") test(`String(-0)`, "0") test(`""+-0`, "0") test(` var abc = Object.getOwnPropertyDescriptor(String, "prototype"); [ [ typeof String.prototype ], [ abc.writable, abc.enumerable, abc.configurable ] ]; `, "object,false,false,false") }) } func TestString_charAt(t *testing.T) { tt(t, func() { test, _ := test() test(` abc = "xyzzy".charAt(0) def = "xyzzy".charAt(11) `) test("abc", "x") test("def", "") }) } func TestString_charCodeAt(t *testing.T) { tt(t, func() { test, _ := test() test(` abc = "xyzzy".charCodeAt(0) def = "xyzzy".charCodeAt(11) `) test("abc", 120) test("def", _NaN) }) } func TestString_fromCharCode(t *testing.T) { tt(t, func() { test, _ := test() test(`String.fromCharCode()`, []uint16{}) test(`String.fromCharCode(88, 121, 122, 122, 121)`, []uint16{88, 121, 122, 122, 121}) // FIXME terst, Double-check these... test(`String.fromCharCode("88", 121, 122, 122.05, 121)`, []uint16{88, 121, 122, 122, 121}) test(`String.fromCharCode("88", 121, 122, NaN, 121)`, []uint16{88, 121, 122, 0, 121}) test(`String.fromCharCode("0x21")`, []uint16{33}) test(`String.fromCharCode(-1).charCodeAt(0)`, 65535) test(`String.fromCharCode(65535).charCodeAt(0)`, 65535) test(`String.fromCharCode(65534).charCodeAt(0)`, 65534) test(`String.fromCharCode(4294967295).charCodeAt(0)`, 65535) test(`String.fromCharCode(4294967294).charCodeAt(0)`, 65534) test(`String.fromCharCode(0x0024) === "$"`, true) }) } func TestString_concat(t *testing.T) { tt(t, func() { test, _ := test() test(`"".concat()`, "") test(`"".concat("abc", "def")`, "abcdef") test(`"".concat("abc", undefined, "def")`, "abcundefineddef") }) } func TestString_indexOf(t *testing.T) { tt(t, func() { test, _ := test() test(`"".indexOf("")`, 0) test(`"".indexOf("", 11)`, 0) test(`"abc".indexOf("")`, 0) test(`"abc".indexOf("", 11)`, 3) test(`"abc".indexOf("a")`, 0) test(`"abc".indexOf("bc")`, 1) test(`"abc".indexOf("bc", 11)`, -1) test(`"$$abcdabcd".indexOf("ab", function(){return -Infinity;}())`, 2) test(`"$$abcdabcd".indexOf("ab", function(){return NaN;}())`, 2) test(` var abc = {toString:function(){return "\u0041B";}} var def = {valueOf:function(){return true;}} var ghi = "ABB\u0041BABAB"; var jkl; with(ghi) { jkl = indexOf(abc, def); } jkl; `, 3) }) } func TestString_lastIndexOf(t *testing.T) { tt(t, func() { test, _ := test() test(`"".lastIndexOf("")`, 0) test(`"".lastIndexOf("", 11)`, 0) test(`"abc".lastIndexOf("")`, 3) test(`"abc".lastIndexOf("", 11)`, 3) test(`"abc".lastIndexOf("a")`, 0) test(`"abc".lastIndexOf("bc")`, 1) test(`"abc".lastIndexOf("bc", 11)`, 1) test(`"abc".lastIndexOf("bc", 0)`, -1) test(`"abc".lastIndexOf("abcabcabc", 2)`, -1) test(`"abc".lastIndexOf("abc", 0)`, 0) test(`"abc".lastIndexOf("abc", 1)`, 0) test(`"abc".lastIndexOf("abc", 2)`, 0) test(`"abc".lastIndexOf("abc", 3)`, 0) test(` abc = new Object(true); abc.lastIndexOf = String.prototype.lastIndexOf; abc.lastIndexOf(true, false); `, 0) }) } func TestString_match(t *testing.T) { tt(t, func() { test, _ := test() test(`"abc____abc_abc___".match(/__abc/)`, "__abc") test(`"abc___abc_abc__abc__abc".match(/abc/g)`, "abc,abc,abc,abc,abc") test(`"abc____abc_abc___".match(/__abc/g)`, "__abc") test(` abc = /abc/g "abc___abc_abc__abc__abc".match(abc) `, "abc,abc,abc,abc,abc") test(`abc.lastIndex`, 23) }) } func BenchmarkString_match(b *testing.B) { vm := New() s, _ := vm.Compile("test.js", `"abc____abc_abc___".match(/__abc/g)`) for i := 0; i < b.N; i++ { _, e := vm.Run(s) if e != nil { b.Error(e.Error()) } } } func TestString_replace(t *testing.T) { tt(t, func() { test, _ := test() test(`"abc_abc".replace(/abc/, "$&123")`, "abc123_abc") test(`"abc_abc".replace(/abc/g, "$&123")`, "abc123_abc123") test(`"abc_abc_".replace(/abc/g, "$&123")`, "abc123_abc123_") test(`"_abc_abc_".replace(/abc/g, "$&123")`, "_abc123_abc123_") test(`"abc".replace(/abc/, "$&123")`, "abc123") test(`"abc_".replace(/abc/, "$&123")`, "abc123_") test("\"^abc$\".replace(/abc/, \"$`def\")", "^^def$") test("\"^abc$\".replace(/abc/, \"def$`\")", "^def^$") test(`"_abc_abd_".replace(/ab(c|d)/g, "$1")`, "_c_d_") test(` "_abc_abd_".replace(/ab(c|d)/g, function(){ }) `, "_undefined_undefined_") test(`"b".replace(/(a)?(b)?/, "_$1_")`, "__") test(` "b".replace(/(a)?(b)?/, function(a, b, c, d, e, f){ return [a, b, c, d, e, f] }) `, "b,,b,0,b,") test(` var abc = 'She sells seashells by the seashore.'; var def = /sh/; [ abc.replace(def, "$'" + 'sch') ]; `, "She sells seaells by the seashore.schells by the seashore.") }) } func BenchmarkString_replace(b *testing.B) { vm := New() s, _ := vm.Compile("test.js", `"_abc_abd_".replace(/ab(c|d)/g, "$1")`) for i := 0; i < b.N; i++ { _, e := vm.Run(s) if e != nil { b.Error(e.Error()) } } } func TestString_search(t *testing.T) { tt(t, func() { test, _ := test() test(`"abc".search(/abc/)`, 0) test(`"abc".search(/def/)`, -1) test(`"abc".search(/c$/)`, 2) test(`"abc".search(/$/)`, 3) }) } func BenchmarkString_search(b *testing.B) { vm := New() s, _ := vm.Compile("test.js", `"abc".search(/c$/)`) for i := 0; i < b.N; i++ { _, e := vm.Run(s) if e != nil { b.Error(e.Error()) } } } func TestString_split(t *testing.T) { tt(t, func() { test, _ := test() test(`"abc".split("", 1)`, "a") test(`"abc".split("", 2)`, "a,b") test(`"abc".split("", 3)`, "a,b,c") test(`"abc".split("", 4)`, "a,b,c") test(`"abc".split("", 11)`, "a,b,c") test(`"abc".split("", 0)`, "") test(`"abc".split("")`, "a,b,c") test(`"abc".split(undefined)`, "abc") test(`"__1__3_1__2__".split("_")`, ",,1,,3,1,,2,,") test(`"__1__3_1__2__".split(/_/)`, ",,1,,3,1,,2,,") test(`"ab".split(/a*/)`, ",b") test(`_ = "Aboldandcoded".split(/<(\/)?([^<>]+)>/)`, "A,,B,bold,/,B,and,,CODE,coded,/,CODE,") test(`_.length`, 13) test(`_[1] === undefined`, true) test(`_[12] === ""`, true) test(` var abc = new String("one-1 two-2 three-3"); var def = abc.split(new RegExp); [ def.constructor === Array, abc.length, def.length, def.join('') ]; `, "true,19,19,one-1 two-2 three-3") }) } func BenchmarkString_splitWithString(b *testing.B) { vm := New() vm.Set("data", "Lorem ipsum dolor sit amet, blandit nec elit. Ridiculus tortor wisi fusce vivamus") s, _ := vm.Compile("test.js", `data.split(" ")`) for i := 0; i < b.N; i++ { _, e := vm.Run(s) if e != nil { b.Error(e.Error()) } } } func BenchmarkString_splitWithRegex(b *testing.B) { vm := New() vm.Set("data", "Lorem ipsum dolor sit amet, blandit nec elit. Ridiculus tortor wisi fusce vivamus") s, _ := vm.Compile("test.js", `data.split(/ /)`) for i := 0; i < b.N; i++ { _, e := vm.Run(s) if e != nil { b.Error(e.Error()) } } } func TestString_slice(t *testing.T) { tt(t, func() { test, _ := test() test(`"abc".slice()`, "abc") test(`"abc".slice(0)`, "abc") test(`"abc".slice(0,11)`, "abc") test(`"abc".slice(0,-1)`, "ab") test(`"abc".slice(-1,11)`, "c") test(`abc = "abc"; abc.slice(abc.length+1, 0)`, "") }) } func TestString_substring(t *testing.T) { tt(t, func() { test, _ := test() test(`"abc".substring()`, "abc") test(`"abc".substring(0)`, "abc") test(`"abc".substring(0,11)`, "abc") test(`"abc".substring(11,0)`, "abc") test(`"abc".substring(0,-1)`, "") test(`"abc".substring(-1,11)`, "abc") test(`"abc".substring(11,1)`, "bc") test(`"abc".substring(1)`, "bc") test(`"abc".substring(Infinity, Infinity)`, "") }) } func TestString_toCase(t *testing.T) { tt(t, func() { test, _ := test() test(`"abc".toLowerCase()`, "abc") test(`"ABC".toLowerCase()`, "abc") test(`"abc".toLocaleLowerCase()`, "abc") test(`"ABC".toLocaleLowerCase()`, "abc") test(`"abc".toUpperCase()`, "ABC") test(`"ABC".toUpperCase()`, "ABC") test(`"abc".toLocaleUpperCase()`, "ABC") test(`"ABC".toLocaleUpperCase()`, "ABC") }) } func Test_floatToString(t *testing.T) { tt(t, func() { test, _ := test() test(`String(-1234567890)`, "-1234567890") test(`-+String(-(-1234567890))`, -1234567890) test(`String(-1e128)`, "-1e+128") test(`String(0.12345)`, "0.12345") test(`String(-0.00000012345)`, "-1.2345e-7") test(`String(0.0000012345)`, "0.0000012345") test(`String(1000000000000000000000)`, "1e+21") test(`String(1e21)`, "1e+21") test(`String(1E21)`, "1e+21") test(`String(-1000000000000000000000)`, "-1e+21") test(`String(-1e21)`, "-1e+21") test(`String(-1E21)`, "-1e+21") test(`String(0.0000001)`, "1e-7") test(`String(1e-7)`, "1e-7") test(`String(1E-7)`, "1e-7") test(`String(-0.0000001)`, "-1e-7") test(`String(-1e-7)`, "-1e-7") test(`String(-1E-7)`, "-1e-7") }) } func TestString_indexing(t *testing.T) { tt(t, func() { test, _ := test() // Actually a test of stringToArrayIndex, under the hood. test(` abc = new String("abc"); index = Math.pow(2, 32); [ abc.length, abc[index], abc[index+1], abc[index+2], abc[index+3] ]; `, "3,,,,") }) } func TestString_trim(t *testing.T) { tt(t, func() { test, _ := test() test(`' \n abc \t \n'.trim();`, "abc") test(`" abc\u000B".trim()`, "abc") test(`"abc ".trim()`, "abc") test(` var a = "\u180Eabc \u000B " var b = a.trim() a.length + b.length `, 10) }) } func TestString_trimLeft(t *testing.T) { tt(t, func() { test, _ := test() test(`" abc\u000B".trimLeft()`, "abc\u000B") test(`"abc ".trimLeft()`, "abc ") test(` var a = "\u180Eabc \u000B " var b = a.trimLeft() a.length + b.length `, 13) }) } func TestString_trimRight(t *testing.T) { tt(t, func() { test, _ := test() test(`" abc\u000B".trimRight()`, " abc") test(`" abc ".trimRight()`, " abc") test(` var a = "\u180Eabc \u000B " var b = a.trimRight() a.length + b.length `, 11) }) } func TestString_localeCompare(t *testing.T) { tt(t, func() { test, _ := test() test(`'a'.localeCompare('c');`, -1) test(`'c'.localeCompare('a');`, 1) test(`'a'.localeCompare('a');`, 0) }) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/terst/000077500000000000000000000000001331145703200235365ustar00rootroot00000000000000golang-github-robertkrimen-otto-0.0~git20180617.15f95af/terst/terst.go000066400000000000000000000361431331145703200252350ustar00rootroot00000000000000// This file was AUTOMATICALLY GENERATED by terst-import (smuggol) from github.com/robertkrimen/terst /* Package terst is a terse (terst = test + terse), easy-to-use testing library for Go. terst is compatible with (and works via) the standard testing package: http://golang.org/pkg/testing var is = terst.Is func Test(t *testing.T) { terst.Terst(t, func() { is("abc", "abc") is(1, ">", 0) var abc []int is(abc, nil) } } Do not import terst directly, instead use `terst-import` to copy it into your testing environment: https://github.com/robertkrimen/terst/tree/master/terst-import $ go get github.com/robertkrimen/terst/terst-import $ terst-import */ package terst import ( "bytes" "errors" "fmt" "math/big" "reflect" "regexp" "runtime" "strings" "sync" "testing" "time" ) // Is compares two values (got & expect) and returns true if the comparison is true, // false otherwise. In addition, if the comparison is false, Is will report the error // in a manner similar to testing.T.Error(...). Is also takes an optional argument, // a comparator, that changes how the comparison is made. The following // comparators are available: // // == # got == expect (default) // != # got != expect // // > # got > expect (float32, uint, uint16, int, int64, ...) // >= # got >= expect // < # got < expect // <= # got <= expect // // =~ # regexp.MustCompile(expect).Match{String}(got) // !~ # !regexp.MustCompile(expect).Match{String}(got) // // Basic usage with the default comparator (==): // // Is(, ) // // Specifying a different comparator: // // Is(, , ) // // A simple comparison: // // Is(2 + 2, 4) // // A bit trickier: // // Is(1, ">", 0) // Is(2 + 2, "!=", 5) // Is("Nothing happens.", "=~", `ing(\s+)happens\.$`) // // Is should only be called under a Terst(t, ...) call. For a standalone version, // use IsErr. If no scope is found and the comparison is false, then Is will panic the error. // func Is(arguments ...interface{}) bool { err := IsErr(arguments...) if err != nil { call := Caller() if call == nil { panic(err) } call.Error(err) return false } return true } type ( // ErrFail indicates a comparison failure (e.g. 0 > 1). ErrFail error // ErrInvalid indicates an invalid comparison (e.g. bool == string). ErrInvalid error ) var errInvalid = errors.New("invalid") var registry = struct { table map[uintptr]*_scope lock sync.RWMutex }{ table: map[uintptr]*_scope{}, } func registerScope(pc uintptr, scope *_scope) { registry.lock.Lock() defer registry.lock.Unlock() registry.table[pc] = scope } func scope() *_scope { scope, _ := findScope() return scope } func floatCompare(a float64, b float64) int { if a > b { return 1 } else if a < b { return -1 } // NaN == NaN return 0 } func bigIntCompare(a *big.Int, b *big.Int) int { return a.Cmp(b) } func bigInt(value int64) *big.Int { return big.NewInt(value) } func bigUint(value uint64) *big.Int { return big.NewInt(0).SetUint64(value) } type _toString interface { String() string } func toString(value interface{}) (string, error) { switch value := value.(type) { case string: return value, nil case _toString: return value.String(), nil case error: return value.Error(), nil } return "", errInvalid } func matchString(got string, expect *regexp.Regexp) (int, error) { if expect.MatchString(got) { return 0, nil } return -1, nil } func match(got []byte, expect *regexp.Regexp) (int, error) { if expect.Match(got) { return 0, nil } return -1, nil } func compareMatch(got, expect interface{}) (int, error) { switch got := got.(type) { case []byte: switch expect := expect.(type) { case string: matcher, err := regexp.Compile(expect) if err != nil { return 0, err } return match(got, matcher) case *regexp.Regexp: return match(got, expect) } default: if got, err := toString(got); err == nil { switch expect := expect.(type) { case string: matcher, err := regexp.Compile(expect) if err != nil { return 0, err } return matchString(got, matcher) case *regexp.Regexp: return matchString(got, expect) } } else { return 0, err } } return 0, errInvalid } func floatPromote(value reflect.Value) (float64, error) { kind := value.Kind() if reflect.Int <= kind && kind <= reflect.Int64 { return float64(value.Int()), nil } if reflect.Uint <= kind && kind <= reflect.Uint64 { return float64(value.Uint()), nil } if reflect.Float32 <= kind && kind <= reflect.Float64 { return value.Float(), nil } return 0, errInvalid } func bigIntPromote(value reflect.Value) (*big.Int, error) { kind := value.Kind() if reflect.Int <= kind && kind <= reflect.Int64 { return bigInt(value.Int()), nil } if reflect.Uint <= kind && kind <= reflect.Uint64 { return bigUint(value.Uint()), nil } return nil, errInvalid } func compareOther(got, expect interface{}) (int, error) { { switch expect.(type) { case float32, float64: return compareNumber(got, expect) case uint, uint8, uint16, uint32, uint64: return compareNumber(got, expect) case int, int8, int16, int32, int64: return compareNumber(got, expect) case string: var err error got, err = toString(got) if err != nil { return 0, err } case nil: got := reflect.ValueOf(got) switch got.Kind() { case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Slice, reflect.Interface: if got.IsNil() { return 0, nil } return -1, nil case reflect.Invalid: // reflect.Invalid: var abc interface{} = nil return 0, nil } return 0, errInvalid } } if reflect.ValueOf(got).Type() != reflect.ValueOf(expect).Type() { return 0, errInvalid } if reflect.DeepEqual(got, expect) { return 0, nil } return -1, nil } func compareNumber(got, expect interface{}) (int, error) { { got := reflect.ValueOf(got) k0 := got.Kind() expect := reflect.ValueOf(expect) k1 := expect.Kind() if reflect.Float32 <= k0 && k0 <= reflect.Float64 || reflect.Float32 <= k1 && k1 <= reflect.Float64 { got, err := floatPromote(got) if err != nil { return 0, err } expect, err := floatPromote(expect) if err != nil { return 0, err } return floatCompare(got, expect), nil } else { got, err := bigIntPromote(got) if err != nil { return 0, err } expect, err := bigIntPromote(expect) if err != nil { return 0, err } return got.Cmp(expect), nil } } return 0, errInvalid } // IsErr compares two values (got & expect) and returns nil if the comparison is true, an ErrFail if // the comparison is false, or an ErrInvalid if the comparison is invalid. IsErr also // takes an optional argument, a comparator, that changes how the comparison is made. // // Is & IsErr are similar but different: // // Is(...) // Should only be called within a Terst(...) call // IsErr(...) // A standalone comparator, the same as Is, just without the automatic reporting // func IsErr(arguments ...interface{}) error { var got, expect interface{} comparator := "==" switch len(arguments) { case 0, 1: return fmt.Errorf("invalid number of arguments to IsErr: %d", len(arguments)) case 2: got, expect = arguments[0], arguments[1] default: if value, ok := arguments[1].(string); ok { comparator = value } else { return fmt.Errorf("invalid comparator: %v", arguments[1]) } got, expect = arguments[0], arguments[2] } var result int var err error switch comparator { case "<", "<=", ">", ">=": result, err = compareNumber(got, expect) case "=~", "!~": result, err = compareMatch(got, expect) case "==", "!=": result, err = compareOther(got, expect) default: return fmt.Errorf("invalid comparator: %s", comparator) } if err == errInvalid { return ErrInvalid(fmt.Errorf( "\nINVALID (%s):\n got: %v (%T)\n expected: %v (%T)", comparator, got, got, expect, expect, )) } else if err != nil { return err } equality, pass := false, false switch comparator { case "==", "=~": equality = true pass = result == 0 case "!=", "!~": equality = true pass = result != 0 case "<": pass = result < 0 case "<=": pass = result <= 0 case ">": pass = result > 0 case ">=": pass = result >= 0 } if !pass { if equality { if comparator[1] == '~' { if value, ok := got.([]byte); ok { return ErrFail(fmt.Errorf( "\nFAIL (%s)\n got: %s %v%s\nexpected: %v%s", comparator, value, got, typeKindString(got), expect, typeKindString(expect), )) } } return ErrFail(fmt.Errorf( "\nFAIL (%s)\n got: %v%s\nexpected: %v%s", comparator, got, typeKindString(got), expect, typeKindString(expect), )) } return ErrFail(fmt.Errorf( "\nFAIL (%s)\n got: %v%s\nexpected: %s %v%s", comparator, got, typeKindString(got), comparator, expect, typeKindString(expect), )) } return nil } func typeKindString(value interface{}) string { reflectValue := reflect.ValueOf(value) kind := reflectValue.Kind().String() result := fmt.Sprintf("%T", value) if kind == result { if kind == "string" { return "" } return fmt.Sprintf(" (%T)", value) } return fmt.Sprintf(" (%T=%s)", value, kind) } func (scope *_scope) reset() { scope.name = "" scope.output = scope.output[:] scope.start = time.Time{} scope.duration = 0 } // Terst creates a testing scope, where Is can be called and errors will be reported // according to the top-level location of the comparison, and not where the Is call // actually takes place. For example: // // func test(value int) { // Is(value, 5) // <--- This failure is reported below. // } // // Terst(t, func(){ // // Is(2, ">", 3) // <--- An error is reported here. // // test(5) // <--- An error is reported here. // // }) // func Terst(t *testing.T, arguments ...func()) { scope := &_scope{ t: t, } pc, _, _, ok := runtime.Caller(1) // TODO Associate with the Test... func if !ok { panic("Here be dragons.") } _, scope.testFunc = findTestFunc() registerScope(pc, scope) for _, fn := range arguments { func() { scope.reset() name := scope.testFunc.Name() index := strings.LastIndex(scope.testFunc.Name(), ".") if index >= 0 { name = name[index+1:] + "(Terst)" } else { name = "(Terst)" } name = "(Terst)" scope.name = name scope.start = time.Now() defer func() { scope.duration = time.Now().Sub(scope.start) if err := recover(); err != nil { scope.t.Fail() scope.report() panic(err) } scope.report() }() fn() }() } } // From "testing" func (scope *_scope) report() { format := "~~~ %s: (Terst)\n%s" if scope.t.Failed() { fmt.Printf(format, "FAIL", scope.output) } else if testing.Verbose() && len(scope.output) > 0 { fmt.Printf(format, "PASS", scope.output) } } func (scope *_scope) log(call _entry, str string) { scope.mu.Lock() defer scope.mu.Unlock() scope.output = append(scope.output, decorate(call, str)...) } // decorate prefixes the string with the file and line of the call site // and inserts the final newline if needed and indentation tabs for formascing. func decorate(call _entry, s string) string { file, line := call.File, call.Line if call.PC > 0 { // Truncate file name at last file name separator. if index := strings.LastIndex(file, "/"); index >= 0 { file = file[index+1:] } else if index = strings.LastIndex(file, "\\"); index >= 0 { file = file[index+1:] } } else { file = "???" line = 1 } buf := new(bytes.Buffer) // Every line is indented at least one tab. buf.WriteByte('\t') fmt.Fprintf(buf, "%s:%d: ", file, line) lines := strings.Split(s, "\n") if l := len(lines); l > 1 && lines[l-1] == "" { lines = lines[:l-1] } for i, line := range lines { if i > 0 { // Second and subsequent lines are indented an extra tab. buf.WriteString("\n\t\t") } buf.WriteString(line) } buf.WriteByte('\n') return buf.String() } func findScope() (*_scope, _entry) { registry.lock.RLock() defer registry.lock.RUnlock() table := registry.table depth := 2 // Starting depth call := _entry{} for { pc, _, _, ok := runtime.Caller(depth) if !ok { break } if scope, exists := table[pc]; exists { pc, file, line, _ := runtime.Caller(depth - 3) // Terst(...) + func(){}() + fn() => ???() call.PC = pc call.File = file call.Line = line return scope, call } depth++ } return nil, _entry{} } // Call is a reference to a line immediately under a Terst testing scope. type Call struct { scope *_scope entry _entry } // Caller will search the stack, looking for a Terst testing scope. If a scope // is found, then Caller returns a Call for logging errors, accessing testing.T, etc. // If no scope is found, Caller returns nil. func Caller() *Call { scope, entry := findScope() if scope == nil { return nil } return &Call{ scope: scope, entry: entry, } } // TestFunc returns the *runtime.Func entry for the top-level Test...(t testing.T) // function. func (cl *Call) TestFunc() *runtime.Func { return cl.scope.testFunc } // T returns the original testing.T passed to Terst(...) func (cl *Call) T() *testing.T { return cl.scope.t } // Log is the terst version of `testing.T.Log` func (cl *Call) Log(arguments ...interface{}) { cl.scope.log(cl.entry, fmt.Sprintln(arguments...)) } // Logf is the terst version of `testing.T.Logf` func (cl *Call) Logf(format string, arguments ...interface{}) { cl.scope.log(cl.entry, fmt.Sprintf(format, arguments...)) } // Error is the terst version of `testing.T.Error` func (cl *Call) Error(arguments ...interface{}) { cl.scope.log(cl.entry, fmt.Sprintln(arguments...)) cl.scope.t.Fail() } // Errorf is the terst version of `testing.T.Errorf` func (cl *Call) Errorf(format string, arguments ...interface{}) { cl.scope.log(cl.entry, fmt.Sprintf(format, arguments...)) cl.scope.t.Fail() } // Skip is the terst version of `testing.T.Skip` func (cl *Call) Skip(arguments ...interface{}) { cl.scope.log(cl.entry, fmt.Sprintln(arguments...)) cl.scope.t.SkipNow() } // Skipf is the terst version of `testing.T.Skipf` func (cl *Call) Skipf(format string, arguments ...interface{}) { cl.scope.log(cl.entry, fmt.Sprintf(format, arguments...)) cl.scope.t.SkipNow() } type _scope struct { t *testing.T testFunc *runtime.Func name string mu sync.RWMutex output []byte start time.Time duration time.Duration } type _entry struct { PC uintptr File string Line int Func *runtime.Func } func _findFunc(match string) (_entry, *runtime.Func) { depth := 2 // Starting depth for { pc, file, line, ok := runtime.Caller(depth) if !ok { break } fn := runtime.FuncForPC(pc) name := fn.Name() if index := strings.LastIndex(name, match); index >= 0 { // Assume we have an instance of TestXyzzy in a _test file return _entry{ PC: pc, File: file, Line: line, Func: fn, }, fn } depth++ } return _entry{}, nil } func findTestFunc() (_entry, *runtime.Func) { return _findFunc(".Test") } func findTerstFunc() (_entry, *runtime.Func) { return _findFunc(".Terst") } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/test/000077500000000000000000000000001331145703200233545ustar00rootroot00000000000000golang-github-robertkrimen-otto-0.0~git20180617.15f95af/test/Makefile000066400000000000000000000006561331145703200250230ustar00rootroot00000000000000.PHONY: test fetch clean build err report TESTER := tester test: $(TESTER) for test in test-*.js; do ./$^ -test=true $$test 1>/dev/null || exit 1; done @echo PASS report: $(TESTER) ./$^ -report | grep -v "MT READY" fetch: $(TESTER) ./$^ fetch build: go build -a -o $(TESTER) $(TESTER): tester.go $(MAKE) build clean: rm -f test-*.js rm -f $(TESTER) err: $(TESTER) for test in test-*.js; do ./$^ $$test; done 2>$@ golang-github-robertkrimen-otto-0.0~git20180617.15f95af/test/tester.go000066400000000000000000000114411331145703200252120ustar00rootroot00000000000000package main import ( "encoding/json" "flag" "fmt" "io/ioutil" "net/http" "os" "regexp" "strings" "sync" "text/tabwriter" "github.com/robertkrimen/otto" "github.com/robertkrimen/otto/parser" ) var flag_test *bool = flag.Bool("test", false, "") var flag_report *bool = flag.Bool("report", false, "") var match_ReferenceError_not_defined = regexp.MustCompile(`^ReferenceError: \S+ is not defined$`) var match_lookahead = regexp.MustCompile(`Invalid regular expression: re2: Invalid \(\?[=!]\) `) var match_backreference = regexp.MustCompile(`Invalid regular expression: re2: Invalid \\\d `) var match_TypeError_undefined = regexp.MustCompile(`^TypeError: Cannot access member '[^']+' of undefined$`) var target = map[string]string{ "test-angular-bindonce.js": "fail", // (anonymous): Line 1:944 Unexpected token ( (and 40 more errors) "test-jsforce.js": "fail", // (anonymous): Line 9:28329 RuneError (and 5 more errors) "test-chaplin.js": "parse", // Error: Chaplin requires Common.js or AMD modules "test-dropbox.js.js": "parse", // Error: dropbox.js loaded in an unsupported JavaScript environment. "test-epitome.js": "parse", // TypeError: undefined is not a function "test-portal.js": "parse", // TypeError "test-reactive-coffee.js": "parse", // Dependencies are not met for reactive: _ and $ not found "test-scriptaculous.js": "parse", // script.aculo.us requires the Prototype JavaScript framework >= 1.6.0.3 "test-waypoints.js": "parse", // TypeError: undefined is not a function "test-webuploader.js": "parse", // Error: `jQuery` is undefined "test-xuijs.js": "parse", // TypeError: undefined is not a function } // http://cdnjs.com/ // http://api.cdnjs.com/libraries func fetch(name, location string) error { response, err := http.Get(location) if err != nil { return err } defer response.Body.Close() body, err := ioutil.ReadAll(response.Body) if err != nil { return err } if !strings.HasSuffix(location, ".js") { return nil } filename := "test-" + name + ".js" fmt.Println(filename, len(body)) return ioutil.WriteFile(filename, body, 0644) } func test(filename string) error { script, err := ioutil.ReadFile(filename) if err != nil { return err } if !*flag_report { fmt.Fprintln(os.Stdout, filename, len(script)) } parse := false option := target[filename] if option != "parse" { vm := otto.New() _, err = vm.Run(string(script)) if err != nil { value := err.Error() switch { case match_ReferenceError_not_defined.MatchString(value): case match_TypeError_undefined.MatchString(value): case match_lookahead.MatchString(value): case match_backreference.MatchString(value): default: return err } parse = true } } if parse { _, err = parser.ParseFile(nil, filename, string(script), parser.IgnoreRegExpErrors) if err != nil { return err } target[filename] = "parse" } return nil } func main() { flag.Parse() filename := "" err := func() error { if flag.Arg(0) == "fetch" { response, err := http.Get("http://api.cdnjs.com/libraries") if err != nil { return err } defer response.Body.Close() body, err := ioutil.ReadAll(response.Body) if err != nil { return err } var tmp map[string]interface{} err = json.Unmarshal(body, &tmp) if err != nil { return err } var wg sync.WaitGroup for _, value := range tmp["results"].([]interface{}) { wg.Add(1) library := value.(map[string]interface{}) go func() { defer wg.Done() fetch(library["name"].(string), library["latest"].(string)) }() } wg.Wait() return nil } if *flag_report { files, err := ioutil.ReadDir(".") if err != nil { return err } writer := tabwriter.NewWriter(os.Stdout, 0, 8, 0, '\t', 0) fmt.Fprintln(writer, "", "\t| Status") fmt.Fprintln(writer, "---", "\t| ---") for _, file := range files { filename := file.Name() if !strings.HasPrefix(filename, "test-") { continue } err := test(filename) option := target[filename] name := strings.TrimPrefix(strings.TrimSuffix(filename, ".js"), "test-") if err == nil { switch option { case "": fmt.Fprintln(writer, name, "\t| pass") case "parse": fmt.Fprintln(writer, name, "\t| pass (parse)") case "re2": continue fmt.Fprintln(writer, name, "\t| unknown (re2)") } } else { fmt.Fprintln(writer, name, "\t| fail") } } writer.Flush() return nil } filename = flag.Arg(0) return test(filename) }() if err != nil { if filename != "" { if *flag_test && target[filename] == "fail" { goto exit } fmt.Fprintf(os.Stderr, "%s: %s\n", filename, err.Error()) } else { fmt.Fprintln(os.Stderr, err) } os.Exit(64) } exit: } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/testing_test.go000066400000000000000000000046531331145703200254500ustar00rootroot00000000000000package otto import ( "errors" "strings" "testing" "time" "github.com/robertkrimen/otto/terst" ) func tt(t *testing.T, arguments ...func()) { halt := errors.New("A test was taking too long") timer := time.AfterFunc(2*time.Second, func() { panic(halt) }) defer func() { timer.Stop() }() terst.Terst(t, arguments...) } func is(arguments ...interface{}) bool { var got, expect interface{} switch len(arguments) { case 0, 1: return terst.Is(arguments...) case 2: got, expect = arguments[0], arguments[1] default: got, expect = arguments[0], arguments[2] } switch value := got.(type) { case Value: if value.value != nil { got = value.value } case *Error: if value != nil { got = value.Error() } if expect == nil { // FIXME This is weird expect = "" } } if len(arguments) == 2 { arguments[0] = got arguments[1] = expect } else { arguments[0] = got arguments[2] = expect } return terst.Is(arguments...) } func test(arguments ...interface{}) (func(string, ...interface{}) Value, *_tester) { tester := newTester() if len(arguments) > 0 { tester.test(arguments[0].(string)) } return tester.test, tester } type _tester struct { vm *Otto } func newTester() *_tester { return &_tester{ vm: New(), } } func (self *_tester) Get(name string) (Value, error) { return self.vm.Get(name) } func (self *_tester) Set(name string, value interface{}) Value { err := self.vm.Set(name, value) is(err, nil) if err != nil { terst.Caller().T().FailNow() } return self.vm.getValue(name) } func (self *_tester) Run(src interface{}) (Value, error) { return self.vm.Run(src) } func (self *_tester) test(name string, expect ...interface{}) Value { vm := self.vm raise := false defer func() { if caught := recover(); caught != nil { if exception, ok := caught.(*_exception); ok { caught = exception.eject() } if raise { if len(expect) > 0 { is(caught, expect[0]) } } else { dbg("Panic, caught:", caught) panic(caught) } } }() var value Value var err error if isIdentifier(name) { value = vm.getValue(name) } else { source := name index := strings.Index(source, "raise:") if index == 0 { raise = true source = source[6:] source = strings.TrimLeft(source, " ") } value, err = vm.runtime.cmpl_run(source, nil) if err != nil { panic(err) } } value = value.resolve() if len(expect) > 0 { is(value, expect[0]) } return value } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/token/000077500000000000000000000000001331145703200235155ustar00rootroot00000000000000golang-github-robertkrimen-otto-0.0~git20180617.15f95af/token/Makefile000066400000000000000000000000541331145703200251540ustar00rootroot00000000000000token_const.go: tokenfmt ./$^ | gofmt > $@ golang-github-robertkrimen-otto-0.0~git20180617.15f95af/token/README.markdown000066400000000000000000000054371331145703200262270ustar00rootroot00000000000000# token -- import "github.com/robertkrimen/otto/token" Package token defines constants representing the lexical tokens of JavaScript (ECMA5). ## Usage ```go const ( ILLEGAL EOF COMMENT KEYWORD STRING BOOLEAN NULL NUMBER IDENTIFIER PLUS // + MINUS // - MULTIPLY // * SLASH // / REMAINDER // % AND // & OR // | EXCLUSIVE_OR // ^ SHIFT_LEFT // << SHIFT_RIGHT // >> UNSIGNED_SHIFT_RIGHT // >>> AND_NOT // &^ ADD_ASSIGN // += SUBTRACT_ASSIGN // -= MULTIPLY_ASSIGN // *= QUOTIENT_ASSIGN // /= REMAINDER_ASSIGN // %= AND_ASSIGN // &= OR_ASSIGN // |= EXCLUSIVE_OR_ASSIGN // ^= SHIFT_LEFT_ASSIGN // <<= SHIFT_RIGHT_ASSIGN // >>= UNSIGNED_SHIFT_RIGHT_ASSIGN // >>>= AND_NOT_ASSIGN // &^= LOGICAL_AND // && LOGICAL_OR // || INCREMENT // ++ DECREMENT // -- EQUAL // == STRICT_EQUAL // === LESS // < GREATER // > ASSIGN // = NOT // ! BITWISE_NOT // ~ NOT_EQUAL // != STRICT_NOT_EQUAL // !== LESS_OR_EQUAL // <= GREATER_OR_EQUAL // >= LEFT_PARENTHESIS // ( LEFT_BRACKET // [ LEFT_BRACE // { COMMA // , PERIOD // . RIGHT_PARENTHESIS // ) RIGHT_BRACKET // ] RIGHT_BRACE // } SEMICOLON // ; COLON // : QUESTION_MARK // ? IF IN DO VAR FOR NEW TRY THIS ELSE CASE VOID WITH WHILE BREAK CATCH THROW RETURN TYPEOF DELETE SWITCH DEFAULT FINALLY FUNCTION CONTINUE DEBUGGER INSTANCEOF ) ``` #### type Token ```go type Token int ``` Token is the set of lexical tokens in JavaScript (ECMA5). #### func IsKeyword ```go func IsKeyword(literal string) (Token, bool) ``` IsKeyword returns the keyword token if literal is a keyword, a KEYWORD token if the literal is a future keyword (const, let, class, super, ...), or 0 if the literal is not a keyword. If the literal is a keyword, IsKeyword returns a second value indicating if the literal is considered a future keyword in strict-mode only. 7.6.1.2 Future Reserved Words: const class enum export extends import super 7.6.1.2 Future Reserved Words (strict): implements interface let package private protected public static #### func (Token) String ```go func (tkn Token) String() string ``` String returns the string corresponding to the token. For operators, delimiters, and keywords the string is the actual token string (e.g., for the token PLUS, the String() is "+"). For all other tokens the string corresponds to the token name (e.g. for the token IDENTIFIER, the string is "IDENTIFIER"). -- **godocdown** http://github.com/robertkrimen/godocdown golang-github-robertkrimen-otto-0.0~git20180617.15f95af/token/token.go000066400000000000000000000047101331145703200251660ustar00rootroot00000000000000// Package token defines constants representing the lexical tokens of JavaScript (ECMA5). package token import ( "strconv" ) // Token is the set of lexical tokens in JavaScript (ECMA5). type Token int // String returns the string corresponding to the token. // For operators, delimiters, and keywords the string is the actual // token string (e.g., for the token PLUS, the String() is // "+"). For all other tokens the string corresponds to the token // name (e.g. for the token IDENTIFIER, the string is "IDENTIFIER"). // func (tkn Token) String() string { if 0 == tkn { return "UNKNOWN" } if tkn < Token(len(token2string)) { return token2string[tkn] } return "token(" + strconv.Itoa(int(tkn)) + ")" } // This is not used for anything func (tkn Token) precedence(in bool) int { switch tkn { case LOGICAL_OR: return 1 case LOGICAL_AND: return 2 case OR, OR_ASSIGN: return 3 case EXCLUSIVE_OR: return 4 case AND, AND_ASSIGN, AND_NOT, AND_NOT_ASSIGN: return 5 case EQUAL, NOT_EQUAL, STRICT_EQUAL, STRICT_NOT_EQUAL: return 6 case LESS, GREATER, LESS_OR_EQUAL, GREATER_OR_EQUAL, INSTANCEOF: return 7 case IN: if in { return 7 } return 0 case SHIFT_LEFT, SHIFT_RIGHT, UNSIGNED_SHIFT_RIGHT: fallthrough case SHIFT_LEFT_ASSIGN, SHIFT_RIGHT_ASSIGN, UNSIGNED_SHIFT_RIGHT_ASSIGN: return 8 case PLUS, MINUS, ADD_ASSIGN, SUBTRACT_ASSIGN: return 9 case MULTIPLY, SLASH, REMAINDER, MULTIPLY_ASSIGN, QUOTIENT_ASSIGN, REMAINDER_ASSIGN: return 11 } return 0 } type _keyword struct { token Token futureKeyword bool strict bool } // IsKeyword returns the keyword token if literal is a keyword, a KEYWORD token // if the literal is a future keyword (const, let, class, super, ...), or 0 if the literal is not a keyword. // // If the literal is a keyword, IsKeyword returns a second value indicating if the literal // is considered a future keyword in strict-mode only. // // 7.6.1.2 Future Reserved Words: // // const // class // enum // export // extends // import // super // // 7.6.1.2 Future Reserved Words (strict): // // implements // interface // let // package // private // protected // public // static // func IsKeyword(literal string) (Token, bool) { if keyword, exists := keywordTable[literal]; exists { if keyword.futureKeyword { return KEYWORD, keyword.strict } return keyword.token, false } return 0, false } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/token/token_const.go000066400000000000000000000157361331145703200264060ustar00rootroot00000000000000package token const ( _ Token = iota ILLEGAL EOF COMMENT KEYWORD STRING BOOLEAN NULL NUMBER IDENTIFIER PLUS // + MINUS // - MULTIPLY // * SLASH // / REMAINDER // % AND // & OR // | EXCLUSIVE_OR // ^ SHIFT_LEFT // << SHIFT_RIGHT // >> UNSIGNED_SHIFT_RIGHT // >>> AND_NOT // &^ ADD_ASSIGN // += SUBTRACT_ASSIGN // -= MULTIPLY_ASSIGN // *= QUOTIENT_ASSIGN // /= REMAINDER_ASSIGN // %= AND_ASSIGN // &= OR_ASSIGN // |= EXCLUSIVE_OR_ASSIGN // ^= SHIFT_LEFT_ASSIGN // <<= SHIFT_RIGHT_ASSIGN // >>= UNSIGNED_SHIFT_RIGHT_ASSIGN // >>>= AND_NOT_ASSIGN // &^= LOGICAL_AND // && LOGICAL_OR // || INCREMENT // ++ DECREMENT // -- EQUAL // == STRICT_EQUAL // === LESS // < GREATER // > ASSIGN // = NOT // ! BITWISE_NOT // ~ NOT_EQUAL // != STRICT_NOT_EQUAL // !== LESS_OR_EQUAL // <= GREATER_OR_EQUAL // >= LEFT_PARENTHESIS // ( LEFT_BRACKET // [ LEFT_BRACE // { COMMA // , PERIOD // . RIGHT_PARENTHESIS // ) RIGHT_BRACKET // ] RIGHT_BRACE // } SEMICOLON // ; COLON // : QUESTION_MARK // ? firstKeyword IF IN DO VAR FOR NEW TRY THIS ELSE CASE VOID WITH WHILE BREAK CATCH THROW RETURN TYPEOF DELETE SWITCH DEFAULT FINALLY FUNCTION CONTINUE DEBUGGER INSTANCEOF lastKeyword ) var token2string = [...]string{ ILLEGAL: "ILLEGAL", EOF: "EOF", COMMENT: "COMMENT", KEYWORD: "KEYWORD", STRING: "STRING", BOOLEAN: "BOOLEAN", NULL: "NULL", NUMBER: "NUMBER", IDENTIFIER: "IDENTIFIER", PLUS: "+", MINUS: "-", MULTIPLY: "*", SLASH: "/", REMAINDER: "%", AND: "&", OR: "|", EXCLUSIVE_OR: "^", SHIFT_LEFT: "<<", SHIFT_RIGHT: ">>", UNSIGNED_SHIFT_RIGHT: ">>>", AND_NOT: "&^", ADD_ASSIGN: "+=", SUBTRACT_ASSIGN: "-=", MULTIPLY_ASSIGN: "*=", QUOTIENT_ASSIGN: "/=", REMAINDER_ASSIGN: "%=", AND_ASSIGN: "&=", OR_ASSIGN: "|=", EXCLUSIVE_OR_ASSIGN: "^=", SHIFT_LEFT_ASSIGN: "<<=", SHIFT_RIGHT_ASSIGN: ">>=", UNSIGNED_SHIFT_RIGHT_ASSIGN: ">>>=", AND_NOT_ASSIGN: "&^=", LOGICAL_AND: "&&", LOGICAL_OR: "||", INCREMENT: "++", DECREMENT: "--", EQUAL: "==", STRICT_EQUAL: "===", LESS: "<", GREATER: ">", ASSIGN: "=", NOT: "!", BITWISE_NOT: "~", NOT_EQUAL: "!=", STRICT_NOT_EQUAL: "!==", LESS_OR_EQUAL: "<=", GREATER_OR_EQUAL: ">=", LEFT_PARENTHESIS: "(", LEFT_BRACKET: "[", LEFT_BRACE: "{", COMMA: ",", PERIOD: ".", RIGHT_PARENTHESIS: ")", RIGHT_BRACKET: "]", RIGHT_BRACE: "}", SEMICOLON: ";", COLON: ":", QUESTION_MARK: "?", IF: "if", IN: "in", DO: "do", VAR: "var", FOR: "for", NEW: "new", TRY: "try", THIS: "this", ELSE: "else", CASE: "case", VOID: "void", WITH: "with", WHILE: "while", BREAK: "break", CATCH: "catch", THROW: "throw", RETURN: "return", TYPEOF: "typeof", DELETE: "delete", SWITCH: "switch", DEFAULT: "default", FINALLY: "finally", FUNCTION: "function", CONTINUE: "continue", DEBUGGER: "debugger", INSTANCEOF: "instanceof", } var keywordTable = map[string]_keyword{ "if": _keyword{ token: IF, }, "in": _keyword{ token: IN, }, "do": _keyword{ token: DO, }, "var": _keyword{ token: VAR, }, "for": _keyword{ token: FOR, }, "new": _keyword{ token: NEW, }, "try": _keyword{ token: TRY, }, "this": _keyword{ token: THIS, }, "else": _keyword{ token: ELSE, }, "case": _keyword{ token: CASE, }, "void": _keyword{ token: VOID, }, "with": _keyword{ token: WITH, }, "while": _keyword{ token: WHILE, }, "break": _keyword{ token: BREAK, }, "catch": _keyword{ token: CATCH, }, "throw": _keyword{ token: THROW, }, "return": _keyword{ token: RETURN, }, "typeof": _keyword{ token: TYPEOF, }, "delete": _keyword{ token: DELETE, }, "switch": _keyword{ token: SWITCH, }, "default": _keyword{ token: DEFAULT, }, "finally": _keyword{ token: FINALLY, }, "function": _keyword{ token: FUNCTION, }, "continue": _keyword{ token: CONTINUE, }, "debugger": _keyword{ token: DEBUGGER, }, "instanceof": _keyword{ token: INSTANCEOF, }, "const": _keyword{ token: KEYWORD, futureKeyword: true, }, "class": _keyword{ token: KEYWORD, futureKeyword: true, }, "enum": _keyword{ token: KEYWORD, futureKeyword: true, }, "export": _keyword{ token: KEYWORD, futureKeyword: true, }, "extends": _keyword{ token: KEYWORD, futureKeyword: true, }, "import": _keyword{ token: KEYWORD, futureKeyword: true, }, "super": _keyword{ token: KEYWORD, futureKeyword: true, }, "implements": _keyword{ token: KEYWORD, futureKeyword: true, strict: true, }, "interface": _keyword{ token: KEYWORD, futureKeyword: true, strict: true, }, "let": _keyword{ token: KEYWORD, futureKeyword: true, strict: true, }, "package": _keyword{ token: KEYWORD, futureKeyword: true, strict: true, }, "private": _keyword{ token: KEYWORD, futureKeyword: true, strict: true, }, "protected": _keyword{ token: KEYWORD, futureKeyword: true, strict: true, }, "public": _keyword{ token: KEYWORD, futureKeyword: true, strict: true, }, "static": _keyword{ token: KEYWORD, futureKeyword: true, strict: true, }, } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/token/tokenfmt000077500000000000000000000076321331145703200253020ustar00rootroot00000000000000#!/usr/bin/env perl use strict; use warnings; my (%token, @order, @keywords); { my $keywords; my @const; push @const, <<_END_; package token const( _ Token = iota _END_ for (split m/\n/, <<_END_) { ILLEGAL EOF COMMENT KEYWORD STRING BOOLEAN NULL NUMBER IDENTIFIER PLUS + MINUS - MULTIPLY * SLASH / REMAINDER % AND & OR | EXCLUSIVE_OR ^ SHIFT_LEFT << SHIFT_RIGHT >> UNSIGNED_SHIFT_RIGHT >>> AND_NOT &^ ADD_ASSIGN += SUBTRACT_ASSIGN -= MULTIPLY_ASSIGN *= QUOTIENT_ASSIGN /= REMAINDER_ASSIGN %= AND_ASSIGN &= OR_ASSIGN |= EXCLUSIVE_OR_ASSIGN ^= SHIFT_LEFT_ASSIGN <<= SHIFT_RIGHT_ASSIGN >>= UNSIGNED_SHIFT_RIGHT_ASSIGN >>>= AND_NOT_ASSIGN &^= LOGICAL_AND && LOGICAL_OR || INCREMENT ++ DECREMENT -- EQUAL == STRICT_EQUAL === LESS < GREATER > ASSIGN = NOT ! BITWISE_NOT ~ NOT_EQUAL != STRICT_NOT_EQUAL !== LESS_OR_EQUAL <= GREATER_OR_EQUAL <= LEFT_PARENTHESIS ( LEFT_BRACKET [ LEFT_BRACE { COMMA , PERIOD . RIGHT_PARENTHESIS ) RIGHT_BRACKET ] RIGHT_BRACE } SEMICOLON ; COLON : QUESTION_MARK ? firstKeyword IF IN DO VAR FOR NEW TRY THIS ELSE CASE VOID WITH WHILE BREAK CATCH THROW RETURN TYPEOF DELETE SWITCH DEFAULT FINALLY FUNCTION CONTINUE DEBUGGER INSTANCEOF lastKeyword _END_ chomp; next if m/^\s*#/; my ($name, $symbol) = m/(\w+)\s*(\S+)?/; if (defined $symbol) { push @order, $name; push @const, "$name // $symbol"; $token{$name} = $symbol; } elsif (defined $name) { $keywords ||= $name eq 'firstKeyword'; push @const, $name; #$const[-1] .= " Token = iota" if 2 == @const; if ($name =~ m/^([A-Z]+)/) { push @keywords, $name if $keywords; push @order, $name; if ($token{SEMICOLON}) { $token{$name} = lc $1; } else { $token{$name} = $name; } } } else { push @const, ""; } } push @const, ")"; print join "\n", @const, ""; } { print <<_END_; var token2string = [...]string{ _END_ for my $name (@order) { print "$name: \"$token{$name}\",\n"; } print <<_END_; } _END_ print <<_END_; var keywordTable = map[string]_keyword{ _END_ for my $name (@keywords) { print <<_END_ "@{[ lc $name ]}": _keyword{ token: $name, }, _END_ } for my $name (qw/ const class enum export extends import super /) { print <<_END_ "$name": _keyword{ token: KEYWORD, futureKeyword: true, }, _END_ } for my $name (qw/ implements interface let package private protected public static /) { print <<_END_ "$name": _keyword{ token: KEYWORD, futureKeyword: true, strict: true, }, _END_ } print <<_END_; } _END_ } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/type_arguments.go000066400000000000000000000055471331145703200260050ustar00rootroot00000000000000package otto import ( "strconv" ) func (runtime *_runtime) newArgumentsObject(indexOfParameterName []string, stash _stash, length int) *_object { self := runtime.newClassObject("Arguments") for index, _ := range indexOfParameterName { name := strconv.FormatInt(int64(index), 10) objectDefineOwnProperty(self, name, _property{Value{}, 0111}, false) } self.objectClass = _classArguments self.value = _argumentsObject{ indexOfParameterName: indexOfParameterName, stash: stash, } self.prototype = runtime.global.ObjectPrototype self.defineProperty("length", toValue_int(length), 0101, false) return self } type _argumentsObject struct { indexOfParameterName []string // function(abc, def, ghi) // indexOfParameterName[0] = "abc" // indexOfParameterName[1] = "def" // indexOfParameterName[2] = "ghi" // ... stash _stash } func (in _argumentsObject) clone(clone *_clone) _argumentsObject { indexOfParameterName := make([]string, len(in.indexOfParameterName)) copy(indexOfParameterName, in.indexOfParameterName) return _argumentsObject{ indexOfParameterName, clone.stash(in.stash), } } func (self _argumentsObject) get(name string) (Value, bool) { index := stringToArrayIndex(name) if index >= 0 && index < int64(len(self.indexOfParameterName)) { name := self.indexOfParameterName[index] if name == "" { return Value{}, false } return self.stash.getBinding(name, false), true } return Value{}, false } func (self _argumentsObject) put(name string, value Value) { index := stringToArrayIndex(name) name = self.indexOfParameterName[index] self.stash.setBinding(name, value, false) } func (self _argumentsObject) delete(name string) { index := stringToArrayIndex(name) self.indexOfParameterName[index] = "" } func argumentsGet(self *_object, name string) Value { if value, exists := self.value.(_argumentsObject).get(name); exists { return value } return objectGet(self, name) } func argumentsGetOwnProperty(self *_object, name string) *_property { property := objectGetOwnProperty(self, name) if value, exists := self.value.(_argumentsObject).get(name); exists { property.value = value } return property } func argumentsDefineOwnProperty(self *_object, name string, descriptor _property, throw bool) bool { if _, exists := self.value.(_argumentsObject).get(name); exists { if !objectDefineOwnProperty(self, name, descriptor, false) { return self.runtime.typeErrorResult(throw) } if value, valid := descriptor.value.(Value); valid { self.value.(_argumentsObject).put(name, value) } return true } return objectDefineOwnProperty(self, name, descriptor, throw) } func argumentsDelete(self *_object, name string, throw bool) bool { if !objectDelete(self, name, throw) { return false } if _, exists := self.value.(_argumentsObject).get(name); exists { self.value.(_argumentsObject).delete(name) } return true } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/type_array.go000066400000000000000000000054571331145703200251160ustar00rootroot00000000000000package otto import ( "strconv" ) func (runtime *_runtime) newArrayObject(length uint32) *_object { self := runtime.newObject() self.class = "Array" self.defineProperty("length", toValue_uint32(length), 0100, false) self.objectClass = _classArray return self } func isArray(object *_object) bool { return object != nil && (object.class == "Array" || object.class == "GoArray") } func objectLength(object *_object) uint32 { if object == nil { return 0 } switch object.class { case "Array": return object.get("length").value.(uint32) case "String": return uint32(object.get("length").value.(int)) case "GoArray": return uint32(object.get("length").value.(int)) } return 0 } func arrayUint32(rt *_runtime, value Value) uint32 { nm := value.number() if nm.kind != numberInteger || !isUint32(nm.int64) { // FIXME panic(rt.panicRangeError()) } return uint32(nm.int64) } func arrayDefineOwnProperty(self *_object, name string, descriptor _property, throw bool) bool { lengthProperty := self.getOwnProperty("length") lengthValue, valid := lengthProperty.value.(Value) if !valid { panic("Array.length != Value{}") } length := lengthValue.value.(uint32) if name == "length" { if descriptor.value == nil { return objectDefineOwnProperty(self, name, descriptor, throw) } newLengthValue, isValue := descriptor.value.(Value) if !isValue { panic(self.runtime.panicTypeError()) } newLength := arrayUint32(self.runtime, newLengthValue) descriptor.value = toValue_uint32(newLength) if newLength > length { return objectDefineOwnProperty(self, name, descriptor, throw) } if !lengthProperty.writable() { goto Reject } newWritable := true if descriptor.mode&0700 == 0 { // If writable is off newWritable = false descriptor.mode |= 0100 } if !objectDefineOwnProperty(self, name, descriptor, throw) { return false } for newLength < length { length-- if !self.delete(strconv.FormatInt(int64(length), 10), false) { descriptor.value = toValue_uint32(length + 1) if !newWritable { descriptor.mode &= 0077 } objectDefineOwnProperty(self, name, descriptor, false) goto Reject } } if !newWritable { descriptor.mode &= 0077 objectDefineOwnProperty(self, name, descriptor, false) } } else if index := stringToArrayIndex(name); index >= 0 { if index >= int64(length) && !lengthProperty.writable() { goto Reject } if !objectDefineOwnProperty(self, strconv.FormatInt(index, 10), descriptor, false) { goto Reject } if index >= int64(length) { lengthProperty.value = toValue_uint32(uint32(index + 1)) objectDefineOwnProperty(self, "length", *lengthProperty, false) return true } } return objectDefineOwnProperty(self, name, descriptor, throw) Reject: if throw { panic(self.runtime.panicTypeError()) } return false } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/type_boolean.go000066400000000000000000000004021331145703200254000ustar00rootroot00000000000000package otto import ( "strconv" ) func (runtime *_runtime) newBooleanObject(value Value) *_object { return runtime.newPrimitiveObject("Boolean", toValue_bool(value.bool())) } func booleanToString(value bool) string { return strconv.FormatBool(value) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/type_date.go000066400000000000000000000137731331145703200247150ustar00rootroot00000000000000package otto import ( "fmt" "math" "regexp" Time "time" ) type _dateObject struct { time Time.Time // Time from the "time" package, a cached version of time epoch int64 value Value isNaN bool } var ( invalidDateObject = _dateObject{ time: Time.Time{}, epoch: -1, value: NaNValue(), isNaN: true, } ) type _ecmaTime struct { year int month int day int hour int minute int second int millisecond int location *Time.Location // Basically, either local or UTC } func ecmaTime(goTime Time.Time) _ecmaTime { return _ecmaTime{ goTime.Year(), dateFromGoMonth(goTime.Month()), goTime.Day(), goTime.Hour(), goTime.Minute(), goTime.Second(), goTime.Nanosecond() / (100 * 100 * 100), goTime.Location(), } } func (self *_ecmaTime) goTime() Time.Time { return Time.Date( self.year, dateToGoMonth(self.month), self.day, self.hour, self.minute, self.second, self.millisecond*(100*100*100), self.location, ) } func (self *_dateObject) Time() Time.Time { return self.time } func (self *_dateObject) Epoch() int64 { return self.epoch } func (self *_dateObject) Value() Value { return self.value } // FIXME A date should only be in the range of -100,000,000 to +100,000,000 (1970): 15.9.1.1 func (self *_dateObject) SetNaN() { self.time = Time.Time{} self.epoch = -1 self.value = NaNValue() self.isNaN = true } func (self *_dateObject) SetTime(time Time.Time) { self.Set(timeToEpoch(time)) } func epoch2dateObject(epoch float64) _dateObject { date := _dateObject{} date.Set(epoch) return date } func (self *_dateObject) Set(epoch float64) { // epoch self.epoch = epochToInteger(epoch) // time time, err := epochToTime(epoch) self.time = time // Is either a valid time, or the zero-value for time.Time // value & isNaN if err != nil { self.isNaN = true self.epoch = -1 self.value = NaNValue() } else { self.value = toValue_int64(self.epoch) } } func epochToInteger(value float64) int64 { if value > 0 { return int64(math.Floor(value)) } return int64(math.Ceil(value)) } func epochToTime(value float64) (time Time.Time, err error) { epochWithMilli := value if math.IsNaN(epochWithMilli) || math.IsInf(epochWithMilli, 0) { err = fmt.Errorf("Invalid time %v", value) return } epoch := int64(epochWithMilli / 1000) milli := int64(epochWithMilli) % 1000 time = Time.Unix(int64(epoch), milli*1000000).UTC() return } func timeToEpoch(time Time.Time) float64 { return float64(time.UnixNano() / (1000 * 1000)) } func (runtime *_runtime) newDateObject(epoch float64) *_object { self := runtime.newObject() self.class = "Date" // FIXME This is ugly... date := _dateObject{} date.Set(epoch) self.value = date return self } func (self *_object) dateValue() _dateObject { value, _ := self.value.(_dateObject) return value } func dateObjectOf(rt *_runtime, _dateObject *_object) _dateObject { if _dateObject == nil || _dateObject.class != "Date" { panic(rt.panicTypeError()) } return _dateObject.dateValue() } // JavaScript is 0-based, Go is 1-based (15.9.1.4) func dateToGoMonth(month int) Time.Month { return Time.Month(month + 1) } func dateFromGoMonth(month Time.Month) int { return int(month) - 1 } // Both JavaScript & Go are 0-based (Sunday == 0) func dateToGoDay(day int) Time.Weekday { return Time.Weekday(day) } func dateFromGoDay(day Time.Weekday) int { return int(day) } func newDateTime(argumentList []Value, location *Time.Location) (epoch float64) { pick := func(index int, default_ float64) (float64, bool) { if index >= len(argumentList) { return default_, false } value := argumentList[index].float64() if math.IsNaN(value) || math.IsInf(value, 0) { return 0, true } return value, false } if len(argumentList) >= 2 { // 2-argument, 3-argument, ... var year, month, day, hour, minute, second, millisecond float64 var invalid bool if year, invalid = pick(0, 1900.0); invalid { goto INVALID } if month, invalid = pick(1, 0.0); invalid { goto INVALID } if day, invalid = pick(2, 1.0); invalid { goto INVALID } if hour, invalid = pick(3, 0.0); invalid { goto INVALID } if minute, invalid = pick(4, 0.0); invalid { goto INVALID } if second, invalid = pick(5, 0.0); invalid { goto INVALID } if millisecond, invalid = pick(6, 0.0); invalid { goto INVALID } if year >= 0 && year <= 99 { year += 1900 } time := Time.Date(int(year), dateToGoMonth(int(month)), int(day), int(hour), int(minute), int(second), int(millisecond)*1000*1000, location) return timeToEpoch(time) } else if len(argumentList) == 0 { // 0-argument time := Time.Now().UTC() return timeToEpoch(time) } else { // 1-argument value := valueOfArrayIndex(argumentList, 0) value = toPrimitive(value) if value.IsString() { return dateParse(value.string()) } return value.float64() } INVALID: epoch = math.NaN() return } var ( dateLayoutList = []string{ "2006", "2006-01", "2006-01-02", "2006T15:04", "2006-01T15:04", "2006-01-02T15:04", "2006T15:04:05", "2006-01T15:04:05", "2006-01-02T15:04:05", "2006T15:04:05.000", "2006-01T15:04:05.000", "2006-01-02T15:04:05.000", "2006T15:04-0700", "2006-01T15:04-0700", "2006-01-02T15:04-0700", "2006T15:04:05-0700", "2006-01T15:04:05-0700", "2006-01-02T15:04:05-0700", "2006T15:04:05.000-0700", "2006-01T15:04:05.000-0700", "2006-01-02T15:04:05.000-0700", Time.RFC1123, } matchDateTimeZone = regexp.MustCompile(`^(.*)(?:(Z)|([\+\-]\d{2}):(\d{2}))$`) ) func dateParse(date string) (epoch float64) { // YYYY-MM-DDTHH:mm:ss.sssZ var time Time.Time var err error { date := date if match := matchDateTimeZone.FindStringSubmatch(date); match != nil { if match[2] == "Z" { date = match[1] + "+0000" } else { date = match[1] + match[3] + match[4] } } for _, layout := range dateLayoutList { time, err = Time.Parse(layout, date) if err == nil { break } } } if err != nil { return math.NaN() } return float64(time.UnixNano()) / (1000 * 1000) // UnixMilli() } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/type_error.go000066400000000000000000000012711331145703200251170ustar00rootroot00000000000000package otto func (rt *_runtime) newErrorObject(name string, message Value, stackFramesToPop int) *_object { self := rt.newClassObject("Error") if message.IsDefined() { msg := message.string() self.defineProperty("message", toValue_string(msg), 0111, false) self.value = newError(rt, name, stackFramesToPop, msg) } else { self.value = newError(rt, name, stackFramesToPop) } self.defineOwnProperty("stack", _property{ value: _propertyGetSet{ rt.newNativeFunction("get", "internal", 0, func(FunctionCall) Value { return toValue_string(self.value.(_error).formatWithStack()) }), &_nilGetSetObject, }, mode: modeConfigureMask & modeOnMask, }, false) return self } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/type_function.go000066400000000000000000000213561331145703200256210ustar00rootroot00000000000000package otto // _constructFunction type _constructFunction func(*_object, []Value) Value // 13.2.2 [[Construct]] func defaultConstruct(fn *_object, argumentList []Value) Value { object := fn.runtime.newObject() object.class = "Object" prototype := fn.get("prototype") if prototype.kind != valueObject { prototype = toValue_object(fn.runtime.global.ObjectPrototype) } object.prototype = prototype._object() this := toValue_object(object) value := fn.call(this, argumentList, false, nativeFrame) if value.kind == valueObject { return value } return this } // _nativeFunction type _nativeFunction func(FunctionCall) Value // ===================== // // _nativeFunctionObject // // ===================== // type _nativeFunctionObject struct { name string file string line int call _nativeFunction // [[Call]] construct _constructFunction // [[Construct]] } func (runtime *_runtime) _newNativeFunctionObject(name, file string, line int, native _nativeFunction, length int) *_object { self := runtime.newClassObject("Function") self.value = _nativeFunctionObject{ name: name, file: file, line: line, call: native, construct: defaultConstruct, } self.defineProperty("name", toValue_string(name), 0000, false) self.defineProperty("length", toValue_int(length), 0000, false) return self } func (runtime *_runtime) newNativeFunctionObject(name, file string, line int, native _nativeFunction, length int) *_object { self := runtime._newNativeFunctionObject(name, file, line, native, length) self.defineOwnProperty("caller", _property{ value: _propertyGetSet{ runtime._newNativeFunctionObject("get", "internal", 0, func(fc FunctionCall) Value { for sc := runtime.scope; sc != nil; sc = sc.outer { if sc.frame.fn == self { if sc.outer == nil || sc.outer.frame.fn == nil { return nullValue } return runtime.toValue(sc.outer.frame.fn) } } return nullValue }, 0), &_nilGetSetObject, }, mode: 0000, }, false) return self } // =================== // // _bindFunctionObject // // =================== // type _bindFunctionObject struct { target *_object this Value argumentList []Value } func (runtime *_runtime) newBoundFunctionObject(target *_object, this Value, argumentList []Value) *_object { self := runtime.newClassObject("Function") self.value = _bindFunctionObject{ target: target, this: this, argumentList: argumentList, } length := int(toInt32(target.get("length"))) length -= len(argumentList) if length < 0 { length = 0 } self.defineProperty("name", toValue_string("bound "+target.get("name").String()), 0000, false) self.defineProperty("length", toValue_int(length), 0000, false) self.defineProperty("caller", Value{}, 0000, false) // TODO Should throw a TypeError self.defineProperty("arguments", Value{}, 0000, false) // TODO Should throw a TypeError return self } // [[Construct]] func (fn _bindFunctionObject) construct(argumentList []Value) Value { object := fn.target switch value := object.value.(type) { case _nativeFunctionObject: return value.construct(object, fn.argumentList) case _nodeFunctionObject: argumentList = append(fn.argumentList, argumentList...) return object.construct(argumentList) } panic(fn.target.runtime.panicTypeError()) } // =================== // // _nodeFunctionObject // // =================== // type _nodeFunctionObject struct { node *_nodeFunctionLiteral stash _stash } func (runtime *_runtime) newNodeFunctionObject(node *_nodeFunctionLiteral, stash _stash) *_object { self := runtime.newClassObject("Function") self.value = _nodeFunctionObject{ node: node, stash: stash, } self.defineProperty("name", toValue_string(node.name), 0000, false) self.defineProperty("length", toValue_int(len(node.parameterList)), 0000, false) self.defineOwnProperty("caller", _property{ value: _propertyGetSet{ runtime.newNativeFunction("get", "internal", 0, func(fc FunctionCall) Value { for sc := runtime.scope; sc != nil; sc = sc.outer { if sc.frame.fn == self { if sc.outer == nil || sc.outer.frame.fn == nil { return nullValue } return runtime.toValue(sc.outer.frame.fn) } } return nullValue }), &_nilGetSetObject, }, mode: 0000, }, false) return self } // ======= // // _object // // ======= // func (self *_object) isCall() bool { switch fn := self.value.(type) { case _nativeFunctionObject: return fn.call != nil case _bindFunctionObject: return true case _nodeFunctionObject: return true } return false } func (self *_object) call(this Value, argumentList []Value, eval bool, frame _frame) Value { switch fn := self.value.(type) { case _nativeFunctionObject: // Since eval is a native function, we only have to check for it here if eval { eval = self == self.runtime.eval // If eval is true, then it IS a direct eval } // Enter a scope, name from the native object... rt := self.runtime if rt.scope != nil && !eval { rt.enterFunctionScope(rt.scope.lexical, this) rt.scope.frame = _frame{ native: true, nativeFile: fn.file, nativeLine: fn.line, callee: fn.name, file: nil, fn: self, } defer func() { rt.leaveScope() }() } return fn.call(FunctionCall{ runtime: self.runtime, eval: eval, This: this, ArgumentList: argumentList, Otto: self.runtime.otto, }) case _bindFunctionObject: // TODO Passthrough site, do not enter a scope argumentList = append(fn.argumentList, argumentList...) return fn.target.call(fn.this, argumentList, false, frame) case _nodeFunctionObject: rt := self.runtime stash := rt.enterFunctionScope(fn.stash, this) rt.scope.frame = _frame{ callee: fn.node.name, file: fn.node.file, fn: self, } defer func() { rt.leaveScope() }() callValue := rt.cmpl_call_nodeFunction(self, stash, fn.node, this, argumentList) if value, valid := callValue.value.(_result); valid { return value.value } return callValue } panic(self.runtime.panicTypeError("%v is not a function", toValue_object(self))) } func (self *_object) construct(argumentList []Value) Value { switch fn := self.value.(type) { case _nativeFunctionObject: if fn.call == nil { panic(self.runtime.panicTypeError("%v is not a function", toValue_object(self))) } if fn.construct == nil { panic(self.runtime.panicTypeError("%v is not a constructor", toValue_object(self))) } return fn.construct(self, argumentList) case _bindFunctionObject: return fn.construct(argumentList) case _nodeFunctionObject: return defaultConstruct(self, argumentList) } panic(self.runtime.panicTypeError("%v is not a function", toValue_object(self))) } // 15.3.5.3 func (self *_object) hasInstance(of Value) bool { if !self.isCall() { // We should not have a hasInstance method panic(self.runtime.panicTypeError()) } if !of.IsObject() { return false } prototype := self.get("prototype") if !prototype.IsObject() { panic(self.runtime.panicTypeError()) } prototypeObject := prototype._object() value := of._object().prototype for value != nil { if value == prototypeObject { return true } value = value.prototype } return false } // ============ // // FunctionCall // // ============ // // FunctionCall is an encapsulation of a JavaScript function call. type FunctionCall struct { runtime *_runtime _thisObject *_object eval bool // This call is a direct call to eval This Value ArgumentList []Value Otto *Otto } // Argument will return the value of the argument at the given index. // // If no such argument exists, undefined is returned. func (self FunctionCall) Argument(index int) Value { return valueOfArrayIndex(self.ArgumentList, index) } func (self FunctionCall) getArgument(index int) (Value, bool) { return getValueOfArrayIndex(self.ArgumentList, index) } func (self FunctionCall) slice(index int) []Value { if index < len(self.ArgumentList) { return self.ArgumentList[index:] } return []Value{} } func (self *FunctionCall) thisObject() *_object { if self._thisObject == nil { this := self.This.resolve() // FIXME Is this right? self._thisObject = self.runtime.toObject(this) } return self._thisObject } func (self *FunctionCall) thisClassObject(class string) *_object { thisObject := self.thisObject() if thisObject.class != class { panic(self.runtime.panicTypeError()) } return self._thisObject } func (self FunctionCall) toObject(value Value) *_object { return self.runtime.toObject(value) } // CallerLocation will return file location information (file:line:pos) where this function is being called. func (self FunctionCall) CallerLocation() string { // see error.go for location() return self.runtime.scope.outer.frame.location() } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/type_go_array.go000066400000000000000000000062031331145703200255710ustar00rootroot00000000000000package otto import ( "reflect" "strconv" ) func (runtime *_runtime) newGoArrayObject(value reflect.Value) *_object { self := runtime.newObject() self.class = "GoArray" self.objectClass = _classGoArray self.value = _newGoArrayObject(value) return self } type _goArrayObject struct { value reflect.Value writable bool propertyMode _propertyMode } func _newGoArrayObject(value reflect.Value) *_goArrayObject { writable := value.Kind() == reflect.Ptr // The Array is addressable (like a Slice) mode := _propertyMode(0010) if writable { mode = 0110 } self := &_goArrayObject{ value: value, writable: writable, propertyMode: mode, } return self } func (self _goArrayObject) getValue(index int64) (reflect.Value, bool) { value := reflect.Indirect(self.value) if index < int64(value.Len()) { return value.Index(int(index)), true } return reflect.Value{}, false } func (self _goArrayObject) setValue(index int64, value Value) bool { indexValue, exists := self.getValue(index) if !exists { return false } reflectValue, err := value.toReflectValue(reflect.Indirect(self.value).Type().Elem().Kind()) if err != nil { panic(err) } indexValue.Set(reflectValue) return true } func goArrayGetOwnProperty(self *_object, name string) *_property { // length if name == "length" { return &_property{ value: toValue(reflect.Indirect(self.value.(*_goArrayObject).value).Len()), mode: 0, } } // .0, .1, .2, ... index := stringToArrayIndex(name) if index >= 0 { object := self.value.(*_goArrayObject) value := Value{} reflectValue, exists := object.getValue(index) if exists { value = self.runtime.toValue(reflectValue.Interface()) } return &_property{ value: value, mode: object.propertyMode, } } return objectGetOwnProperty(self, name) } func goArrayEnumerate(self *_object, all bool, each func(string) bool) { object := self.value.(*_goArrayObject) // .0, .1, .2, ... for index, length := 0, object.value.Len(); index < length; index++ { name := strconv.FormatInt(int64(index), 10) if !each(name) { return } } objectEnumerate(self, all, each) } func goArrayDefineOwnProperty(self *_object, name string, descriptor _property, throw bool) bool { if name == "length" { return self.runtime.typeErrorResult(throw) } else if index := stringToArrayIndex(name); index >= 0 { object := self.value.(*_goArrayObject) if object.writable { if self.value.(*_goArrayObject).setValue(index, descriptor.value.(Value)) { return true } } return self.runtime.typeErrorResult(throw) } return objectDefineOwnProperty(self, name, descriptor, throw) } func goArrayDelete(self *_object, name string, throw bool) bool { // length if name == "length" { return self.runtime.typeErrorResult(throw) } // .0, .1, .2, ... index := stringToArrayIndex(name) if index >= 0 { object := self.value.(*_goArrayObject) if object.writable { indexValue, exists := object.getValue(index) if exists { indexValue.Set(reflect.Zero(reflect.Indirect(object.value).Type().Elem())) return true } } return self.runtime.typeErrorResult(throw) } return self.delete(name, throw) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/type_go_map.go000066400000000000000000000044611331145703200252340ustar00rootroot00000000000000package otto import ( "reflect" ) func (runtime *_runtime) newGoMapObject(value reflect.Value) *_object { self := runtime.newObject() self.class = "Object" // TODO Should this be something else? self.objectClass = _classGoMap self.value = _newGoMapObject(value) return self } type _goMapObject struct { value reflect.Value keyKind reflect.Kind valueKind reflect.Kind } func _newGoMapObject(value reflect.Value) *_goMapObject { if value.Kind() != reflect.Map { dbgf("%/panic//%@: %v != reflect.Map", value.Kind()) } self := &_goMapObject{ value: value, keyKind: value.Type().Key().Kind(), valueKind: value.Type().Elem().Kind(), } return self } func (self _goMapObject) toKey(name string) reflect.Value { reflectValue, err := stringToReflectValue(name, self.keyKind) if err != nil { panic(err) } return reflectValue } func (self _goMapObject) toValue(value Value) reflect.Value { reflectValue, err := value.toReflectValue(self.valueKind) if err != nil { panic(err) } return reflectValue } func goMapGetOwnProperty(self *_object, name string) *_property { object := self.value.(*_goMapObject) value := object.value.MapIndex(object.toKey(name)) if value.IsValid() { return &_property{self.runtime.toValue(value.Interface()), 0111} } // Other methods if method := self.value.(*_goMapObject).value.MethodByName(name); (method != reflect.Value{}) { return &_property{ value: self.runtime.toValue(method.Interface()), mode: 0110, } } return nil } func goMapEnumerate(self *_object, all bool, each func(string) bool) { object := self.value.(*_goMapObject) keys := object.value.MapKeys() for _, key := range keys { if !each(toValue(key).String()) { return } } } func goMapDefineOwnProperty(self *_object, name string, descriptor _property, throw bool) bool { object := self.value.(*_goMapObject) // TODO ...or 0222 if descriptor.mode != 0111 { return self.runtime.typeErrorResult(throw) } if !descriptor.isDataDescriptor() { return self.runtime.typeErrorResult(throw) } object.value.SetMapIndex(object.toKey(name), object.toValue(descriptor.value.(Value))) return true } func goMapDelete(self *_object, name string, throw bool) bool { object := self.value.(*_goMapObject) object.value.SetMapIndex(object.toKey(name), reflect.Value{}) // FIXME return true } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/type_go_map_test.go000066400000000000000000000015131331145703200262660ustar00rootroot00000000000000package otto import ( "fmt" "sort" "testing" ) type GoMapTest map[string]int func (s GoMapTest) Join() string { joinedStr := "" // Ordering the map takes some effort // because map iterators in golang are unordered by definition. // So we need to extract keys, sort them, and then generate K/V pairs // All of this is meant to ensure that the test is predictable. keys := make([]string, len(s)) i := 0 for key, _ := range s { keys[i] = key i++ } sort.Strings(keys) for _, key := range keys { joinedStr += key + ": " + fmt.Sprintf("%d", s[key]) + " " } return joinedStr } func TestGoMap(t *testing.T) { tt(t, func() { test, vm := test() vm.Set("TestMap", GoMapTest{"one": 1, "two": 2, "three": 3}) is(test(`TestMap["one"]`).export(), 1) is(test(`TestMap.Join()`).export(), "one: 1 three: 3 two: 2 ") }) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/type_go_slice.go000066400000000000000000000056201331145703200255540ustar00rootroot00000000000000package otto import ( "reflect" "strconv" ) func (runtime *_runtime) newGoSliceObject(value reflect.Value) *_object { self := runtime.newObject() self.class = "GoArray" // TODO GoSlice? self.objectClass = _classGoSlice self.value = _newGoSliceObject(value) return self } type _goSliceObject struct { value reflect.Value } func _newGoSliceObject(value reflect.Value) *_goSliceObject { self := &_goSliceObject{ value: value, } return self } func (self _goSliceObject) getValue(index int64) (reflect.Value, bool) { if index < int64(self.value.Len()) { return self.value.Index(int(index)), true } return reflect.Value{}, false } func (self _goSliceObject) setValue(index int64, value Value) bool { indexValue, exists := self.getValue(index) if !exists { return false } reflectValue, err := value.toReflectValue(self.value.Type().Elem().Kind()) if err != nil { panic(err) } indexValue.Set(reflectValue) return true } func goSliceGetOwnProperty(self *_object, name string) *_property { // length if name == "length" { return &_property{ value: toValue(self.value.(*_goSliceObject).value.Len()), mode: 0, } } // .0, .1, .2, ... index := stringToArrayIndex(name) if index >= 0 { value := Value{} reflectValue, exists := self.value.(*_goSliceObject).getValue(index) if exists { value = self.runtime.toValue(reflectValue.Interface()) } return &_property{ value: value, mode: 0110, } } // Other methods if method := self.value.(*_goSliceObject).value.MethodByName(name); (method != reflect.Value{}) { return &_property{ value: self.runtime.toValue(method.Interface()), mode: 0110, } } return objectGetOwnProperty(self, name) } func goSliceEnumerate(self *_object, all bool, each func(string) bool) { object := self.value.(*_goSliceObject) // .0, .1, .2, ... for index, length := 0, object.value.Len(); index < length; index++ { name := strconv.FormatInt(int64(index), 10) if !each(name) { return } } objectEnumerate(self, all, each) } func goSliceDefineOwnProperty(self *_object, name string, descriptor _property, throw bool) bool { if name == "length" { return self.runtime.typeErrorResult(throw) } else if index := stringToArrayIndex(name); index >= 0 { if self.value.(*_goSliceObject).setValue(index, descriptor.value.(Value)) { return true } return self.runtime.typeErrorResult(throw) } return objectDefineOwnProperty(self, name, descriptor, throw) } func goSliceDelete(self *_object, name string, throw bool) bool { // length if name == "length" { return self.runtime.typeErrorResult(throw) } // .0, .1, .2, ... index := stringToArrayIndex(name) if index >= 0 { object := self.value.(*_goSliceObject) indexValue, exists := object.getValue(index) if exists { indexValue.Set(reflect.Zero(object.value.Type().Elem())) return true } return self.runtime.typeErrorResult(throw) } return self.delete(name, throw) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/type_go_slice_test.go000066400000000000000000000006131331145703200266100ustar00rootroot00000000000000package otto import "testing" type GoSliceTest []int func (s GoSliceTest) Sum() int { sum := 0 for _, v := range s { sum += v } return sum } func TestGoSlice(t *testing.T) { tt(t, func() { test, vm := test() vm.Set("TestSlice", GoSliceTest{1, 2, 3}) is(test(`TestSlice.length`).export(), 3) is(test(`TestSlice[1]`).export(), 2) is(test(`TestSlice.Sum()`).export(), 6) }) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/type_go_struct.go000066400000000000000000000070561331145703200260060ustar00rootroot00000000000000package otto import ( "encoding/json" "reflect" ) // FIXME Make a note about not being able to modify a struct unless it was // passed as a pointer-to: &struct{ ... } // This seems to be a limitation of the reflect package. // This goes for the other Go constructs too. // I guess we could get around it by either: // 1. Creating a new struct every time // 2. Creating an addressable? struct in the constructor func (runtime *_runtime) newGoStructObject(value reflect.Value) *_object { self := runtime.newObject() self.class = "Object" // TODO Should this be something else? self.objectClass = _classGoStruct self.value = _newGoStructObject(value) return self } type _goStructObject struct { value reflect.Value } func _newGoStructObject(value reflect.Value) *_goStructObject { if reflect.Indirect(value).Kind() != reflect.Struct { dbgf("%/panic//%@: %v != reflect.Struct", value.Kind()) } self := &_goStructObject{ value: value, } return self } func (self _goStructObject) getValue(name string) reflect.Value { if validGoStructName(name) { // Do not reveal hidden or unexported fields if field := reflect.Indirect(self.value).FieldByName(name); (field != reflect.Value{}) { return field } if method := self.value.MethodByName(name); (method != reflect.Value{}) { return method } } return reflect.Value{} } func (self _goStructObject) field(name string) (reflect.StructField, bool) { return reflect.Indirect(self.value).Type().FieldByName(name) } func (self _goStructObject) method(name string) (reflect.Method, bool) { return reflect.Indirect(self.value).Type().MethodByName(name) } func (self _goStructObject) setValue(name string, value Value) bool { field, exists := self.field(name) if !exists { return false } fieldValue := self.getValue(name) reflectValue, err := value.toReflectValue(field.Type.Kind()) if err != nil { panic(err) } fieldValue.Set(reflectValue) return true } func goStructGetOwnProperty(self *_object, name string) *_property { object := self.value.(*_goStructObject) value := object.getValue(name) if value.IsValid() { return &_property{self.runtime.toValue(value.Interface()), 0110} } return objectGetOwnProperty(self, name) } func validGoStructName(name string) bool { if name == "" { return false } return 'A' <= name[0] && name[0] <= 'Z' // TODO What about Unicode? } func goStructEnumerate(self *_object, all bool, each func(string) bool) { object := self.value.(*_goStructObject) // Enumerate fields for index := 0; index < reflect.Indirect(object.value).NumField(); index++ { name := reflect.Indirect(object.value).Type().Field(index).Name if validGoStructName(name) { if !each(name) { return } } } // Enumerate methods for index := 0; index < object.value.NumMethod(); index++ { name := object.value.Type().Method(index).Name if validGoStructName(name) { if !each(name) { return } } } objectEnumerate(self, all, each) } func goStructCanPut(self *_object, name string) bool { object := self.value.(*_goStructObject) value := object.getValue(name) if value.IsValid() { return true } return objectCanPut(self, name) } func goStructPut(self *_object, name string, value Value, throw bool) { object := self.value.(*_goStructObject) if object.setValue(name, value) { return } objectPut(self, name, value, throw) } func goStructMarshalJSON(self *_object) json.Marshaler { object := self.value.(*_goStructObject) goValue := reflect.Indirect(object.value).Interface() switch marshaler := goValue.(type) { case json.Marshaler: return marshaler } return nil } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/type_number.go000066400000000000000000000002231331145703200252520ustar00rootroot00000000000000package otto func (runtime *_runtime) newNumberObject(value Value) *_object { return runtime.newPrimitiveObject("Number", value.numberValue()) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/type_reference.go000066400000000000000000000045241331145703200257300ustar00rootroot00000000000000package otto type _reference interface { invalid() bool // IsUnresolvableReference getValue() Value // getValue putValue(Value) string // PutValue delete() bool } // PropertyReference type _propertyReference struct { name string strict bool base *_object runtime *_runtime at _at } func newPropertyReference(rt *_runtime, base *_object, name string, strict bool, at _at) *_propertyReference { return &_propertyReference{ runtime: rt, name: name, strict: strict, base: base, at: at, } } func (self *_propertyReference) invalid() bool { return self.base == nil } func (self *_propertyReference) getValue() Value { if self.base == nil { panic(self.runtime.panicReferenceError("'%s' is not defined", self.name, self.at)) } return self.base.get(self.name) } func (self *_propertyReference) putValue(value Value) string { if self.base == nil { return self.name } self.base.put(self.name, value, self.strict) return "" } func (self *_propertyReference) delete() bool { if self.base == nil { // TODO Throw an error if strict return true } return self.base.delete(self.name, self.strict) } // ArgumentReference func newArgumentReference(runtime *_runtime, base *_object, name string, strict bool, at _at) *_propertyReference { if base == nil { panic(hereBeDragons()) } return newPropertyReference(runtime, base, name, strict, at) } type _stashReference struct { name string strict bool base _stash } func (self *_stashReference) invalid() bool { return false // The base (an environment) will never be nil } func (self *_stashReference) getValue() Value { return self.base.getBinding(self.name, self.strict) } func (self *_stashReference) putValue(value Value) string { self.base.setValue(self.name, value, self.strict) return "" } func (self *_stashReference) delete() bool { if self.base == nil { // This should never be reached, but just in case return false } return self.base.deleteBinding(self.name) } // getIdentifierReference func getIdentifierReference(runtime *_runtime, stash _stash, name string, strict bool, at _at) _reference { if stash == nil { return newPropertyReference(runtime, nil, name, strict, at) } if stash.hasBinding(name) { return stash.newReference(name, strict, at) } return getIdentifierReference(runtime, stash.outer(), name, strict, at) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/type_regexp.go000066400000000000000000000077211331145703200252660ustar00rootroot00000000000000package otto import ( "fmt" "regexp" "unicode/utf8" "github.com/robertkrimen/otto/parser" ) type _regExpObject struct { regularExpression *regexp.Regexp global bool ignoreCase bool multiline bool source string flags string } func (runtime *_runtime) newRegExpObject(pattern string, flags string) *_object { self := runtime.newObject() self.class = "RegExp" global := false ignoreCase := false multiline := false re2flags := "" // TODO Maybe clean up the panicking here... TypeError, SyntaxError, ? for _, chr := range flags { switch chr { case 'g': if global { panic(runtime.panicSyntaxError("newRegExpObject: %s %s", pattern, flags)) } global = true case 'm': if multiline { panic(runtime.panicSyntaxError("newRegExpObject: %s %s", pattern, flags)) } multiline = true re2flags += "m" case 'i': if ignoreCase { panic(runtime.panicSyntaxError("newRegExpObject: %s %s", pattern, flags)) } ignoreCase = true re2flags += "i" } } re2pattern, err := parser.TransformRegExp(pattern) if err != nil { panic(runtime.panicTypeError("Invalid regular expression: %s", err.Error())) } if len(re2flags) > 0 { re2pattern = fmt.Sprintf("(?%s:%s)", re2flags, re2pattern) } regularExpression, err := regexp.Compile(re2pattern) if err != nil { panic(runtime.panicSyntaxError("Invalid regular expression: %s", err.Error()[22:])) } self.value = _regExpObject{ regularExpression: regularExpression, global: global, ignoreCase: ignoreCase, multiline: multiline, source: pattern, flags: flags, } self.defineProperty("global", toValue_bool(global), 0, false) self.defineProperty("ignoreCase", toValue_bool(ignoreCase), 0, false) self.defineProperty("multiline", toValue_bool(multiline), 0, false) self.defineProperty("lastIndex", toValue_int(0), 0100, false) self.defineProperty("source", toValue_string(pattern), 0, false) return self } func (self *_object) regExpValue() _regExpObject { value, _ := self.value.(_regExpObject) return value } func execRegExp(this *_object, target string) (match bool, result []int) { if this.class != "RegExp" { panic(this.runtime.panicTypeError("Calling RegExp.exec on a non-RegExp object")) } lastIndex := this.get("lastIndex").number().int64 index := lastIndex global := this.get("global").bool() if !global { index = 0 } if 0 > index || index > int64(len(target)) { } else { result = this.regExpValue().regularExpression.FindStringSubmatchIndex(target[index:]) } if result == nil { //this.defineProperty("lastIndex", toValue_(0), 0111, true) this.put("lastIndex", toValue_int(0), true) return // !match } match = true startIndex := index endIndex := int(lastIndex) + result[1] // We do this shift here because the .FindStringSubmatchIndex above // was done on a local subordinate slice of the string, not the whole string for index, _ := range result { result[index] += int(startIndex) } if global { //this.defineProperty("lastIndex", toValue_(endIndex), 0111, true) this.put("lastIndex", toValue_int(endIndex), true) } return // match } func execResultToArray(runtime *_runtime, target string, result []int) *_object { captureCount := len(result) / 2 valueArray := make([]Value, captureCount) for index := 0; index < captureCount; index++ { offset := 2 * index if result[offset] != -1 { valueArray[index] = toValue_string(target[result[offset]:result[offset+1]]) } else { valueArray[index] = Value{} } } matchIndex := result[0] if matchIndex != 0 { matchIndex = 0 // Find the rune index in the string, not the byte index for index := 0; index < result[0]; { _, size := utf8.DecodeRuneInString(target[index:]) matchIndex += 1 index += size } } match := runtime.newArrayOf(valueArray) match.defineProperty("input", toValue_string(target), 0111, false) match.defineProperty("index", toValue_int(matchIndex), 0111, false) return match } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/type_string.go000066400000000000000000000042041331145703200252730ustar00rootroot00000000000000package otto import ( "strconv" "unicode/utf8" ) type _stringObject interface { Length() int At(int) rune String() string } type _stringASCII string func (str _stringASCII) Length() int { return len(str) } func (str _stringASCII) At(at int) rune { return rune(str[at]) } func (str _stringASCII) String() string { return string(str) } type _stringWide struct { string string length int runes []rune } func (str _stringWide) Length() int { return str.length } func (str _stringWide) At(at int) rune { if str.runes == nil { str.runes = []rune(str.string) } return str.runes[at] } func (str _stringWide) String() string { return str.string } func _newStringObject(str string) _stringObject { for i := 0; i < len(str); i++ { if str[i] >= utf8.RuneSelf { goto wide } } return _stringASCII(str) wide: return &_stringWide{ string: str, length: utf8.RuneCountInString(str), } } func stringAt(str _stringObject, index int) rune { if 0 <= index && index < str.Length() { return str.At(index) } return utf8.RuneError } func (runtime *_runtime) newStringObject(value Value) *_object { str := _newStringObject(value.string()) self := runtime.newClassObject("String") self.defineProperty("length", toValue_int(str.Length()), 0, false) self.objectClass = _classString self.value = str return self } func (self *_object) stringValue() _stringObject { if str, ok := self.value.(_stringObject); ok { return str } return nil } func stringEnumerate(self *_object, all bool, each func(string) bool) { if str := self.stringValue(); str != nil { length := str.Length() for index := 0; index < length; index++ { if !each(strconv.FormatInt(int64(index), 10)) { return } } } objectEnumerate(self, all, each) } func stringGetOwnProperty(self *_object, name string) *_property { if property := objectGetOwnProperty(self, name); property != nil { return property } // TODO Test a string of length >= +int32 + 1? if index := stringToArrayIndex(name); index >= 0 { if chr := stringAt(self.stringValue(), int(index)); chr != utf8.RuneError { return &_property{toValue_string(string(chr)), 0} } } return nil } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/underscore/000077500000000000000000000000001331145703200245465ustar00rootroot00000000000000golang-github-robertkrimen-otto-0.0~git20180617.15f95af/underscore/Makefile000066400000000000000000000003731331145703200262110ustar00rootroot00000000000000.PHONY: source source: source.go underscore.js: curl -kL http://underscorejs.org/underscore.js > $@ source.go: underscore.js go-bindata -f underscore -p underscore -u true < $< 2>/dev/null | grep -v '^//' | gofmt > $@ head -4 $< >> $@ mv $< .. golang-github-robertkrimen-otto-0.0~git20180617.15f95af/underscore/README.markdown000066400000000000000000000016601331145703200272520ustar00rootroot00000000000000# underscore -- import "github.com/robertkrimen/otto/underscore" Package underscore contains the source for the JavaScript utility-belt library. import ( _ "github.com/robertkrimen/otto/underscore" ) // Every Otto runtime will now include underscore http://underscorejs.org https://github.com/documentcloud/underscore By importing this package, you'll automatically load underscore every time you create a new Otto runtime. To prevent this behavior, you can do the following: import ( "github.com/robertkrimen/otto/underscore" ) func init() { underscore.Disable() } ## Usage #### func Disable ```go func Disable() ``` Disable underscore runtime inclusion. #### func Enable ```go func Enable() ``` Enable underscore runtime inclusion. #### func Source ```go func Source() string ``` Source returns the underscore source. -- **godocdown** http://github.com/robertkrimen/godocdown golang-github-robertkrimen-otto-0.0~git20180617.15f95af/underscore/source.go000066400000000000000000007632511331145703200264130ustar00rootroot00000000000000package underscore func underscore() []byte { return []byte{ 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x55, 0x6e, 0x64, 0x65, 0x72, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x6a, 0x73, 0x20, 0x31, 0x2e, 0x34, 0x2e, 0x34, 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x6a, 0x73, 0x2e, 0x6f, 0x72, 0x67, 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x39, 0x2d, 0x32, 0x30, 0x31, 0x33, 0x20, 0x4a, 0x65, 0x72, 0x65, 0x6d, 0x79, 0x20, 0x41, 0x73, 0x68, 0x6b, 0x65, 0x6e, 0x61, 0x73, 0x2c, 0x20, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x55, 0x6e, 0x64, 0x65, 0x72, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x20, 0x6d, 0x61, 0x79, 0x20, 0x62, 0x65, 0x20, 0x66, 0x72, 0x65, 0x65, 0x6c, 0x79, 0x20, 0x64, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x64, 0x20, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x4d, 0x49, 0x54, 0x20, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x2e, 0x0a, 0x0a, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x42, 0x61, 0x73, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x73, 0x65, 0x74, 0x75, 0x70, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x45, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, 0x6f, 0x6f, 0x74, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2c, 0x20, 0x60, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x60, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x72, 0x6f, 0x77, 0x73, 0x65, 0x72, 0x2c, 0x20, 0x6f, 0x72, 0x20, 0x60, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x60, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x0a, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x6f, 0x6f, 0x74, 0x20, 0x3d, 0x20, 0x74, 0x68, 0x69, 0x73, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x53, 0x61, 0x76, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x60, 0x5f, 0x60, 0x20, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x55, 0x6e, 0x64, 0x65, 0x72, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x20, 0x3d, 0x20, 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x5f, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x45, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x67, 0x65, 0x74, 0x73, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x20, 0x6f, 0x75, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20, 0x6c, 0x6f, 0x6f, 0x70, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x65, 0x72, 0x20, 0x3d, 0x20, 0x7b, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x53, 0x61, 0x76, 0x65, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x69, 0x6e, 0x69, 0x66, 0x69, 0x65, 0x64, 0x20, 0x28, 0x62, 0x75, 0x74, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x67, 0x7a, 0x69, 0x70, 0x70, 0x65, 0x64, 0x29, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3a, 0x0a, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x20, 0x3d, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x74, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x4f, 0x62, 0x6a, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x20, 0x3d, 0x20, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x74, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x46, 0x75, 0x6e, 0x63, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x20, 0x3d, 0x20, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x74, 0x79, 0x70, 0x65, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x20, 0x71, 0x75, 0x69, 0x63, 0x6b, 0x20, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x20, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x73, 0x70, 0x65, 0x65, 0x64, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x63, 0x6f, 0x72, 0x65, 0x20, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x70, 0x75, 0x73, 0x68, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x70, 0x75, 0x73, 0x68, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x63, 0x61, 0x74, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x63, 0x61, 0x74, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x6f, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x4f, 0x62, 0x6a, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x74, 0x6f, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x61, 0x73, 0x4f, 0x77, 0x6e, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x4f, 0x62, 0x6a, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x68, 0x61, 0x73, 0x4f, 0x77, 0x6e, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, 0x6c, 0x6c, 0x20, 0x2a, 0x2a, 0x45, 0x43, 0x4d, 0x41, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x35, 0x2a, 0x2a, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x77, 0x65, 0x20, 0x68, 0x6f, 0x70, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x75, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x61, 0x72, 0x65, 0x20, 0x64, 0x65, 0x63, 0x6c, 0x61, 0x72, 0x65, 0x64, 0x20, 0x68, 0x65, 0x72, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x76, 0x61, 0x72, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x46, 0x6f, 0x72, 0x45, 0x61, 0x63, 0x68, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x66, 0x6f, 0x72, 0x45, 0x61, 0x63, 0x68, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4d, 0x61, 0x70, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x6d, 0x61, 0x70, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x52, 0x65, 0x64, 0x75, 0x63, 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x52, 0x65, 0x64, 0x75, 0x63, 0x65, 0x52, 0x69, 0x67, 0x68, 0x74, 0x20, 0x20, 0x3d, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, 0x52, 0x69, 0x67, 0x68, 0x74, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x45, 0x76, 0x65, 0x72, 0x79, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x65, 0x76, 0x65, 0x72, 0x79, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x53, 0x6f, 0x6d, 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x73, 0x6f, 0x6d, 0x65, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4c, 0x61, 0x73, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x20, 0x20, 0x3d, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x6c, 0x61, 0x73, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x49, 0x73, 0x41, 0x72, 0x72, 0x61, 0x79, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x69, 0x73, 0x41, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x42, 0x69, 0x6e, 0x64, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x46, 0x75, 0x6e, 0x63, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x62, 0x69, 0x6e, 0x64, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, 0x73, 0x61, 0x66, 0x65, 0x20, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x55, 0x6e, 0x64, 0x65, 0x72, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x75, 0x73, 0x65, 0x20, 0x62, 0x65, 0x6c, 0x6f, 0x77, 0x2e, 0x0a, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x5f, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6f, 0x62, 0x6a, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x6f, 0x66, 0x20, 0x5f, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x28, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x6f, 0x66, 0x20, 0x5f, 0x29, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x5f, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x5f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x20, 0x3d, 0x20, 0x6f, 0x62, 0x6a, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x55, 0x6e, 0x64, 0x65, 0x72, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x2a, 0x2a, 0x4e, 0x6f, 0x64, 0x65, 0x2e, 0x6a, 0x73, 0x2a, 0x2a, 0x2c, 0x20, 0x77, 0x69, 0x74, 0x68, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x77, 0x61, 0x72, 0x64, 0x73, 0x2d, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x74, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6f, 0x6c, 0x64, 0x20, 0x60, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x29, 0x60, 0x20, 0x41, 0x50, 0x49, 0x2e, 0x20, 0x49, 0x66, 0x20, 0x77, 0x65, 0x27, 0x72, 0x65, 0x20, 0x69, 0x6e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x72, 0x6f, 0x77, 0x73, 0x65, 0x72, 0x2c, 0x20, 0x61, 0x64, 0x64, 0x20, 0x60, 0x5f, 0x60, 0x20, 0x61, 0x73, 0x20, 0x61, 0x20, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x76, 0x69, 0x61, 0x20, 0x61, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x2c, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x43, 0x6c, 0x6f, 0x73, 0x75, 0x72, 0x65, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x20, 0x22, 0x61, 0x64, 0x76, 0x61, 0x6e, 0x63, 0x65, 0x64, 0x22, 0x20, 0x6d, 0x6f, 0x64, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x74, 0x79, 0x70, 0x65, 0x6f, 0x66, 0x20, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x20, 0x21, 0x3d, 0x3d, 0x20, 0x27, 0x75, 0x6e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x27, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x74, 0x79, 0x70, 0x65, 0x6f, 0x66, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x21, 0x3d, 0x3d, 0x20, 0x27, 0x75, 0x6e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x27, 0x20, 0x26, 0x26, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x20, 0x3d, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x20, 0x3d, 0x20, 0x5f, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x2e, 0x5f, 0x20, 0x3d, 0x20, 0x5f, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x5f, 0x20, 0x3d, 0x20, 0x5f, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x20, 0x3d, 0x20, 0x27, 0x31, 0x2e, 0x34, 0x2e, 0x34, 0x27, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x54, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x72, 0x6e, 0x65, 0x72, 0x73, 0x74, 0x6f, 0x6e, 0x65, 0x2c, 0x20, 0x61, 0x6e, 0x20, 0x60, 0x65, 0x61, 0x63, 0x68, 0x60, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x61, 0x6b, 0x61, 0x20, 0x60, 0x66, 0x6f, 0x72, 0x45, 0x61, 0x63, 0x68, 0x60, 0x2e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x75, 0x69, 0x6c, 0x74, 0x2d, 0x69, 0x6e, 0x20, 0x60, 0x66, 0x6f, 0x72, 0x45, 0x61, 0x63, 0x68, 0x60, 0x2c, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x73, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x72, 0x61, 0x77, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x44, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x2a, 0x2a, 0x45, 0x43, 0x4d, 0x41, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x35, 0x2a, 0x2a, 0x27, 0x73, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x60, 0x66, 0x6f, 0x72, 0x45, 0x61, 0x63, 0x68, 0x60, 0x20, 0x69, 0x66, 0x20, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x65, 0x61, 0x63, 0x68, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x65, 0x61, 0x63, 0x68, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x66, 0x6f, 0x72, 0x45, 0x61, 0x63, 0x68, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6f, 0x62, 0x6a, 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x46, 0x6f, 0x72, 0x45, 0x61, 0x63, 0x68, 0x20, 0x26, 0x26, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x66, 0x6f, 0x72, 0x45, 0x61, 0x63, 0x68, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x46, 0x6f, 0x72, 0x45, 0x61, 0x63, 0x68, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x66, 0x6f, 0x72, 0x45, 0x61, 0x63, 0x68, 0x28, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6f, 0x62, 0x6a, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x2b, 0x6f, 0x62, 0x6a, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x76, 0x61, 0x72, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x30, 0x2c, 0x20, 0x6c, 0x20, 0x3d, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x6c, 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x6f, 0x62, 0x6a, 0x5b, 0x69, 0x5d, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x65, 0x72, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x76, 0x61, 0x72, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x69, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x2e, 0x68, 0x61, 0x73, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x29, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x6f, 0x62, 0x6a, 0x5b, 0x6b, 0x65, 0x79, 0x5d, 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x2c, 0x20, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x65, 0x72, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x74, 0x6f, 0x20, 0x65, 0x61, 0x63, 0x68, 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x44, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x2a, 0x2a, 0x45, 0x43, 0x4d, 0x41, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x35, 0x2a, 0x2a, 0x27, 0x73, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x60, 0x6d, 0x61, 0x70, 0x60, 0x20, 0x69, 0x66, 0x20, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x6d, 0x61, 0x70, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x20, 0x3d, 0x20, 0x5b, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6f, 0x62, 0x6a, 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4d, 0x61, 0x70, 0x20, 0x26, 0x26, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x6d, 0x61, 0x70, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4d, 0x61, 0x70, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x6d, 0x61, 0x70, 0x28, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x61, 0x63, 0x68, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x5b, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x5d, 0x20, 0x3d, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x27, 0x52, 0x65, 0x64, 0x75, 0x63, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x6e, 0x6f, 0x20, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x27, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x2a, 0x2a, 0x52, 0x65, 0x64, 0x75, 0x63, 0x65, 0x2a, 0x2a, 0x20, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x20, 0x75, 0x70, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x61, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2c, 0x20, 0x61, 0x6b, 0x61, 0x20, 0x60, 0x69, 0x6e, 0x6a, 0x65, 0x63, 0x74, 0x60, 0x2c, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x6f, 0x72, 0x20, 0x60, 0x66, 0x6f, 0x6c, 0x64, 0x6c, 0x60, 0x2e, 0x20, 0x44, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x2a, 0x2a, 0x45, 0x43, 0x4d, 0x41, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x35, 0x2a, 0x2a, 0x27, 0x73, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x60, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, 0x60, 0x20, 0x69, 0x66, 0x20, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x66, 0x6f, 0x6c, 0x64, 0x6c, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x69, 0x6e, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x20, 0x3d, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x3e, 0x20, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6f, 0x62, 0x6a, 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x6f, 0x62, 0x6a, 0x20, 0x3d, 0x20, 0x5b, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x52, 0x65, 0x64, 0x75, 0x63, 0x65, 0x20, 0x26, 0x26, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x52, 0x65, 0x64, 0x75, 0x63, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x62, 0x69, 0x6e, 0x64, 0x28, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x20, 0x3f, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, 0x28, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x29, 0x20, 0x3a, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, 0x28, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x61, 0x63, 0x68, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x20, 0x3d, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x20, 0x3d, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x29, 0x20, 0x74, 0x68, 0x72, 0x6f, 0x77, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x54, 0x79, 0x70, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x54, 0x68, 0x65, 0x20, 0x72, 0x69, 0x67, 0x68, 0x74, 0x2d, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, 0x2c, 0x20, 0x61, 0x6c, 0x73, 0x6f, 0x20, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x20, 0x61, 0x73, 0x20, 0x60, 0x66, 0x6f, 0x6c, 0x64, 0x72, 0x60, 0x2e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x44, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x2a, 0x2a, 0x45, 0x43, 0x4d, 0x41, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x35, 0x2a, 0x2a, 0x27, 0x73, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x60, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, 0x52, 0x69, 0x67, 0x68, 0x74, 0x60, 0x20, 0x69, 0x66, 0x20, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, 0x52, 0x69, 0x67, 0x68, 0x74, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x66, 0x6f, 0x6c, 0x64, 0x72, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x20, 0x3d, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x3e, 0x20, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6f, 0x62, 0x6a, 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x6f, 0x62, 0x6a, 0x20, 0x3d, 0x20, 0x5b, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x52, 0x65, 0x64, 0x75, 0x63, 0x65, 0x52, 0x69, 0x67, 0x68, 0x74, 0x20, 0x26, 0x26, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, 0x52, 0x69, 0x67, 0x68, 0x74, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x52, 0x65, 0x64, 0x75, 0x63, 0x65, 0x52, 0x69, 0x67, 0x68, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x62, 0x69, 0x6e, 0x64, 0x28, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x20, 0x3f, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, 0x52, 0x69, 0x67, 0x68, 0x74, 0x28, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x29, 0x20, 0x3a, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, 0x52, 0x69, 0x67, 0x68, 0x74, 0x28, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x3d, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x21, 0x3d, 0x3d, 0x20, 0x2b, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6b, 0x65, 0x79, 0x73, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x3d, 0x20, 0x6b, 0x65, 0x79, 0x73, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x61, 0x63, 0x68, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, 0x6b, 0x65, 0x79, 0x73, 0x20, 0x3f, 0x20, 0x6b, 0x65, 0x79, 0x73, 0x5b, 0x2d, 0x2d, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x5d, 0x20, 0x3a, 0x20, 0x2d, 0x2d, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x20, 0x3d, 0x20, 0x6f, 0x62, 0x6a, 0x5b, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x20, 0x3d, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x2c, 0x20, 0x6f, 0x62, 0x6a, 0x5b, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5d, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x29, 0x20, 0x74, 0x68, 0x72, 0x6f, 0x77, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x54, 0x79, 0x70, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x70, 0x61, 0x73, 0x73, 0x65, 0x73, 0x20, 0x61, 0x20, 0x74, 0x72, 0x75, 0x74, 0x68, 0x20, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x20, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x64, 0x20, 0x61, 0x73, 0x20, 0x60, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x60, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x66, 0x69, 0x6e, 0x64, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x6e, 0x79, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x29, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x72, 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x74, 0x68, 0x65, 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x70, 0x61, 0x73, 0x73, 0x20, 0x61, 0x20, 0x74, 0x72, 0x75, 0x74, 0x68, 0x20, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x44, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x2a, 0x2a, 0x45, 0x43, 0x4d, 0x41, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x35, 0x2a, 0x2a, 0x27, 0x73, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x60, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x60, 0x20, 0x69, 0x66, 0x20, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x64, 0x20, 0x61, 0x73, 0x20, 0x60, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x60, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x20, 0x3d, 0x20, 0x5b, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6f, 0x62, 0x6a, 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x20, 0x26, 0x26, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x28, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x61, 0x63, 0x68, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x29, 0x29, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x5b, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x5d, 0x20, 0x3d, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x74, 0x68, 0x65, 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x61, 0x20, 0x74, 0x72, 0x75, 0x74, 0x68, 0x20, 0x74, 0x65, 0x73, 0x74, 0x20, 0x66, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x21, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x44, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x20, 0x77, 0x68, 0x65, 0x74, 0x68, 0x65, 0x72, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x20, 0x61, 0x20, 0x74, 0x72, 0x75, 0x74, 0x68, 0x20, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x44, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x2a, 0x2a, 0x45, 0x43, 0x4d, 0x41, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x35, 0x2a, 0x2a, 0x27, 0x73, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x60, 0x65, 0x76, 0x65, 0x72, 0x79, 0x60, 0x20, 0x69, 0x66, 0x20, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x64, 0x20, 0x61, 0x73, 0x20, 0x60, 0x61, 0x6c, 0x6c, 0x60, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x65, 0x76, 0x65, 0x72, 0x79, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x61, 0x6c, 0x6c, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x7c, 0x7c, 0x20, 0x28, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6f, 0x62, 0x6a, 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x45, 0x76, 0x65, 0x72, 0x79, 0x20, 0x26, 0x26, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x65, 0x76, 0x65, 0x72, 0x79, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x45, 0x76, 0x65, 0x72, 0x79, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x65, 0x76, 0x65, 0x72, 0x79, 0x28, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x61, 0x63, 0x68, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x28, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x26, 0x26, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x29, 0x29, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x65, 0x72, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x21, 0x21, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x44, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x20, 0x69, 0x66, 0x20, 0x61, 0x74, 0x20, 0x6c, 0x65, 0x61, 0x73, 0x74, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x20, 0x61, 0x20, 0x74, 0x72, 0x75, 0x74, 0x68, 0x20, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x44, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x2a, 0x2a, 0x45, 0x43, 0x4d, 0x41, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x35, 0x2a, 0x2a, 0x27, 0x73, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x60, 0x73, 0x6f, 0x6d, 0x65, 0x60, 0x20, 0x69, 0x66, 0x20, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x64, 0x20, 0x61, 0x73, 0x20, 0x60, 0x61, 0x6e, 0x79, 0x60, 0x2e, 0x0a, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x73, 0x6f, 0x6d, 0x65, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x61, 0x6e, 0x79, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x7c, 0x7c, 0x20, 0x28, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6f, 0x62, 0x6a, 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x53, 0x6f, 0x6d, 0x65, 0x20, 0x26, 0x26, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x73, 0x6f, 0x6d, 0x65, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x53, 0x6f, 0x6d, 0x65, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x73, 0x6f, 0x6d, 0x65, 0x28, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x61, 0x63, 0x68, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x7c, 0x7c, 0x20, 0x28, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x29, 0x29, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x65, 0x72, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x21, 0x21, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x44, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x20, 0x69, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x6f, 0x72, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x20, 0x61, 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x28, 0x75, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x60, 0x3d, 0x3d, 0x3d, 0x60, 0x29, 0x2e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x64, 0x20, 0x61, 0x73, 0x20, 0x60, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x60, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6f, 0x62, 0x6a, 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x20, 0x26, 0x26, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x28, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x29, 0x20, 0x21, 0x3d, 0x20, 0x2d, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x6e, 0x79, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x6e, 0x76, 0x6f, 0x6b, 0x65, 0x20, 0x61, 0x20, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x20, 0x28, 0x77, 0x69, 0x74, 0x68, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x29, 0x20, 0x6f, 0x6e, 0x20, 0x65, 0x76, 0x65, 0x72, 0x79, 0x20, 0x69, 0x74, 0x65, 0x6d, 0x20, 0x69, 0x6e, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x69, 0x6e, 0x76, 0x6f, 0x6b, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x61, 0x72, 0x67, 0x73, 0x20, 0x3d, 0x20, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x20, 0x32, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x69, 0x73, 0x46, 0x75, 0x6e, 0x63, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x6d, 0x61, 0x70, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x69, 0x73, 0x46, 0x75, 0x6e, 0x63, 0x20, 0x3f, 0x20, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x20, 0x3a, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5b, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5d, 0x29, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x73, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x6e, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x20, 0x75, 0x73, 0x65, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x60, 0x6d, 0x61, 0x70, 0x60, 0x3a, 0x20, 0x66, 0x65, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x20, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x70, 0x6c, 0x75, 0x63, 0x6b, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x6d, 0x61, 0x70, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x7b, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5b, 0x6b, 0x65, 0x79, 0x5d, 0x3b, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x6e, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x20, 0x75, 0x73, 0x65, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x60, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x60, 0x3a, 0x20, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x20, 0x60, 0x6b, 0x65, 0x79, 0x3a, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x60, 0x20, 0x70, 0x61, 0x69, 0x72, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x77, 0x68, 0x65, 0x72, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x61, 0x74, 0x74, 0x72, 0x73, 0x2c, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x2e, 0x69, 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x28, 0x61, 0x74, 0x74, 0x72, 0x73, 0x29, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x3f, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x20, 0x3a, 0x20, 0x5b, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x5b, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x3f, 0x20, 0x27, 0x66, 0x69, 0x6e, 0x64, 0x27, 0x20, 0x3a, 0x20, 0x27, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x27, 0x5d, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x76, 0x61, 0x72, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x69, 0x6e, 0x20, 0x61, 0x74, 0x74, 0x72, 0x73, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x61, 0x74, 0x74, 0x72, 0x73, 0x5b, 0x6b, 0x65, 0x79, 0x5d, 0x20, 0x21, 0x3d, 0x3d, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5b, 0x6b, 0x65, 0x79, 0x5d, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x72, 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x6e, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x20, 0x75, 0x73, 0x65, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x60, 0x66, 0x69, 0x6e, 0x64, 0x60, 0x3a, 0x20, 0x67, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x20, 0x60, 0x6b, 0x65, 0x79, 0x3a, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x60, 0x20, 0x70, 0x61, 0x69, 0x72, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x66, 0x69, 0x6e, 0x64, 0x57, 0x68, 0x65, 0x72, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x61, 0x74, 0x74, 0x72, 0x73, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x77, 0x68, 0x65, 0x72, 0x65, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x61, 0x74, 0x74, 0x72, 0x73, 0x2c, 0x20, 0x74, 0x72, 0x75, 0x65, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x6f, 0x72, 0x20, 0x28, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2d, 0x62, 0x61, 0x73, 0x65, 0x64, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x29, 0x2e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x61, 0x6e, 0x27, 0x74, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x73, 0x20, 0x6c, 0x6f, 0x6e, 0x67, 0x65, 0x72, 0x20, 0x74, 0x68, 0x61, 0x6e, 0x20, 0x36, 0x35, 0x2c, 0x35, 0x33, 0x35, 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x53, 0x65, 0x65, 0x3a, 0x20, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x62, 0x75, 0x67, 0x73, 0x2e, 0x77, 0x65, 0x62, 0x6b, 0x69, 0x74, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x73, 0x68, 0x6f, 0x77, 0x5f, 0x62, 0x75, 0x67, 0x2e, 0x63, 0x67, 0x69, 0x3f, 0x69, 0x64, 0x3d, 0x38, 0x30, 0x37, 0x39, 0x37, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x6d, 0x61, 0x78, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x26, 0x26, 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x41, 0x72, 0x72, 0x61, 0x79, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x26, 0x26, 0x20, 0x6f, 0x62, 0x6a, 0x5b, 0x30, 0x5d, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x2b, 0x6f, 0x62, 0x6a, 0x5b, 0x30, 0x5d, 0x20, 0x26, 0x26, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x3c, 0x20, 0x36, 0x35, 0x35, 0x33, 0x35, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x4d, 0x61, 0x74, 0x68, 0x2e, 0x6d, 0x61, 0x78, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x4d, 0x61, 0x74, 0x68, 0x2c, 0x20, 0x6f, 0x62, 0x6a, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x26, 0x26, 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x2d, 0x49, 0x6e, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x7b, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x20, 0x3a, 0x20, 0x2d, 0x49, 0x6e, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x20, 0x2d, 0x49, 0x6e, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x61, 0x63, 0x68, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x20, 0x3d, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x3f, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x29, 0x20, 0x3a, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x20, 0x3e, 0x3d, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x20, 0x26, 0x26, 0x20, 0x28, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x7b, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x3a, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x20, 0x3a, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x28, 0x6f, 0x72, 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2d, 0x62, 0x61, 0x73, 0x65, 0x64, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x29, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x6d, 0x69, 0x6e, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x26, 0x26, 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x41, 0x72, 0x72, 0x61, 0x79, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x26, 0x26, 0x20, 0x6f, 0x62, 0x6a, 0x5b, 0x30, 0x5d, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x2b, 0x6f, 0x62, 0x6a, 0x5b, 0x30, 0x5d, 0x20, 0x26, 0x26, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x3c, 0x20, 0x36, 0x35, 0x35, 0x33, 0x35, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x4d, 0x61, 0x74, 0x68, 0x2e, 0x6d, 0x69, 0x6e, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x4d, 0x61, 0x74, 0x68, 0x2c, 0x20, 0x6f, 0x62, 0x6a, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x26, 0x26, 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x49, 0x6e, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x7b, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x20, 0x3a, 0x20, 0x49, 0x6e, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x20, 0x49, 0x6e, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x61, 0x63, 0x68, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x20, 0x3d, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x3f, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x29, 0x20, 0x3a, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x20, 0x3c, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x20, 0x26, 0x26, 0x20, 0x28, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x7b, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x3a, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x20, 0x3a, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x53, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x61, 0x6e, 0x64, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x64, 0x20, 0x3d, 0x20, 0x5b, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x61, 0x63, 0x68, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x61, 0x6e, 0x64, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x28, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2b, 0x2b, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x64, 0x5b, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x2d, 0x20, 0x31, 0x5d, 0x20, 0x3d, 0x20, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x64, 0x5b, 0x72, 0x61, 0x6e, 0x64, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x64, 0x5b, 0x72, 0x61, 0x6e, 0x64, 0x5d, 0x20, 0x3d, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x64, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, 0x6e, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x6f, 0x20, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x20, 0x6c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x49, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x20, 0x3f, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x3a, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x7b, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x5b, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5d, 0x3b, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x53, 0x6f, 0x72, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x27, 0x73, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, 0x62, 0x79, 0x20, 0x61, 0x20, 0x63, 0x72, 0x69, 0x74, 0x65, 0x72, 0x69, 0x6f, 0x6e, 0x20, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x61, 0x6e, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x73, 0x6f, 0x72, 0x74, 0x42, 0x79, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x6c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x49, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x70, 0x6c, 0x75, 0x63, 0x6b, 0x28, 0x5f, 0x2e, 0x6d, 0x61, 0x70, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x3a, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3a, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x72, 0x69, 0x74, 0x65, 0x72, 0x69, 0x61, 0x20, 0x3a, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x2e, 0x73, 0x6f, 0x72, 0x74, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6c, 0x65, 0x66, 0x74, 0x2c, 0x20, 0x72, 0x69, 0x67, 0x68, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x61, 0x20, 0x3d, 0x20, 0x6c, 0x65, 0x66, 0x74, 0x2e, 0x63, 0x72, 0x69, 0x74, 0x65, 0x72, 0x69, 0x61, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x62, 0x20, 0x3d, 0x20, 0x72, 0x69, 0x67, 0x68, 0x74, 0x2e, 0x63, 0x72, 0x69, 0x74, 0x65, 0x72, 0x69, 0x61, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x61, 0x20, 0x21, 0x3d, 0x3d, 0x20, 0x62, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x61, 0x20, 0x3e, 0x20, 0x62, 0x20, 0x7c, 0x7c, 0x20, 0x61, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x30, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x61, 0x20, 0x3c, 0x20, 0x62, 0x20, 0x7c, 0x7c, 0x20, 0x62, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x30, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x2d, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6c, 0x65, 0x66, 0x74, 0x2e, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3c, 0x20, 0x72, 0x69, 0x67, 0x68, 0x74, 0x2e, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3f, 0x20, 0x2d, 0x31, 0x20, 0x3a, 0x20, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x2c, 0x20, 0x27, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x27, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, 0x6e, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x20, 0x22, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x62, 0x79, 0x22, 0x20, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x7b, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x6c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x49, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x7c, 0x7c, 0x20, 0x5f, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x61, 0x63, 0x68, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x3d, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6f, 0x62, 0x6a, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x28, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x27, 0x73, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, 0x62, 0x79, 0x20, 0x61, 0x20, 0x63, 0x72, 0x69, 0x74, 0x65, 0x72, 0x69, 0x6f, 0x6e, 0x2e, 0x20, 0x50, 0x61, 0x73, 0x73, 0x20, 0x65, 0x69, 0x74, 0x68, 0x65, 0x72, 0x20, 0x61, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x74, 0x6f, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x62, 0x79, 0x2c, 0x20, 0x6f, 0x72, 0x20, 0x61, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x72, 0x69, 0x74, 0x65, 0x72, 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x42, 0x79, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, 0x5f, 0x2e, 0x68, 0x61, 0x73, 0x28, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x29, 0x20, 0x3f, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x5b, 0x6b, 0x65, 0x79, 0x5d, 0x20, 0x3a, 0x20, 0x28, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x5b, 0x6b, 0x65, 0x79, 0x5d, 0x20, 0x3d, 0x20, 0x5b, 0x5d, 0x29, 0x29, 0x2e, 0x70, 0x75, 0x73, 0x68, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x62, 0x79, 0x20, 0x61, 0x20, 0x63, 0x65, 0x72, 0x74, 0x61, 0x69, 0x6e, 0x20, 0x63, 0x72, 0x69, 0x74, 0x65, 0x72, 0x69, 0x6f, 0x6e, 0x2e, 0x20, 0x50, 0x61, 0x73, 0x73, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x65, 0x69, 0x74, 0x68, 0x65, 0x72, 0x20, 0x61, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x62, 0x79, 0x2c, 0x20, 0x6f, 0x72, 0x20, 0x61, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x63, 0x72, 0x69, 0x74, 0x65, 0x72, 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x42, 0x79, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x5f, 0x2e, 0x68, 0x61, 0x73, 0x28, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x29, 0x29, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x5b, 0x6b, 0x65, 0x79, 0x5d, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x5b, 0x6b, 0x65, 0x79, 0x5d, 0x2b, 0x2b, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x55, 0x73, 0x65, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x6f, 0x20, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x20, 0x6f, 0x75, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x6d, 0x61, 0x6c, 0x6c, 0x65, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x61, 0x74, 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x61, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x62, 0x65, 0x20, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x65, 0x64, 0x20, 0x73, 0x6f, 0x20, 0x61, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x6d, 0x61, 0x69, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x20, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2e, 0x20, 0x55, 0x73, 0x65, 0x73, 0x20, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x20, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x73, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x20, 0x3f, 0x20, 0x5f, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x20, 0x3a, 0x20, 0x6c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x49, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x28, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x3d, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x6f, 0x62, 0x6a, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6c, 0x6f, 0x77, 0x20, 0x3d, 0x20, 0x30, 0x2c, 0x20, 0x68, 0x69, 0x67, 0x68, 0x20, 0x3d, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x77, 0x68, 0x69, 0x6c, 0x65, 0x20, 0x28, 0x6c, 0x6f, 0x77, 0x20, 0x3c, 0x20, 0x68, 0x69, 0x67, 0x68, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6d, 0x69, 0x64, 0x20, 0x3d, 0x20, 0x28, 0x6c, 0x6f, 0x77, 0x20, 0x2b, 0x20, 0x68, 0x69, 0x67, 0x68, 0x29, 0x20, 0x3e, 0x3e, 0x3e, 0x20, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x5b, 0x6d, 0x69, 0x64, 0x5d, 0x29, 0x20, 0x3c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x3f, 0x20, 0x6c, 0x6f, 0x77, 0x20, 0x3d, 0x20, 0x6d, 0x69, 0x64, 0x20, 0x2b, 0x20, 0x31, 0x20, 0x3a, 0x20, 0x68, 0x69, 0x67, 0x68, 0x20, 0x3d, 0x20, 0x6d, 0x69, 0x64, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6c, 0x6f, 0x77, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x53, 0x61, 0x66, 0x65, 0x6c, 0x79, 0x20, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x20, 0x61, 0x6e, 0x79, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x20, 0x61, 0x20, 0x72, 0x65, 0x61, 0x6c, 0x2c, 0x20, 0x6c, 0x69, 0x76, 0x65, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x74, 0x6f, 0x41, 0x72, 0x72, 0x61, 0x79, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5b, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x2e, 0x69, 0x73, 0x41, 0x72, 0x72, 0x61, 0x79, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6f, 0x62, 0x6a, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x2b, 0x6f, 0x62, 0x6a, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x6d, 0x61, 0x70, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x5f, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x6f, 0x66, 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x61, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6f, 0x62, 0x6a, 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x6f, 0x62, 0x6a, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x2b, 0x6f, 0x62, 0x6a, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x29, 0x20, 0x3f, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x3a, 0x20, 0x5f, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x20, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x47, 0x65, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x20, 0x50, 0x61, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x2a, 0x2a, 0x6e, 0x2a, 0x2a, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x4e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x20, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x64, 0x20, 0x61, 0x73, 0x20, 0x60, 0x68, 0x65, 0x61, 0x64, 0x60, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x60, 0x74, 0x61, 0x6b, 0x65, 0x60, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x2a, 0x2a, 0x67, 0x75, 0x61, 0x72, 0x64, 0x2a, 0x2a, 0x20, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x73, 0x20, 0x69, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x60, 0x5f, 0x2e, 0x6d, 0x61, 0x70, 0x60, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x74, 0x61, 0x6b, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x6e, 0x2c, 0x20, 0x67, 0x75, 0x61, 0x72, 0x64, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x6e, 0x20, 0x21, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x26, 0x26, 0x20, 0x21, 0x67, 0x75, 0x61, 0x72, 0x64, 0x20, 0x3f, 0x20, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x6e, 0x29, 0x20, 0x3a, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x5b, 0x30, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x65, 0x76, 0x65, 0x72, 0x79, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x20, 0x62, 0x75, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x61, 0x73, 0x74, 0x20, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x20, 0x45, 0x73, 0x70, 0x65, 0x63, 0x69, 0x61, 0x6c, 0x6c, 0x79, 0x20, 0x75, 0x73, 0x65, 0x66, 0x75, 0x6c, 0x20, 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x20, 0x50, 0x61, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x2a, 0x2a, 0x6e, 0x2a, 0x2a, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x74, 0x68, 0x65, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, 0x69, 0x6e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x61, 0x73, 0x74, 0x20, 0x4e, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x2a, 0x2a, 0x67, 0x75, 0x61, 0x72, 0x64, 0x2a, 0x2a, 0x20, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x20, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x73, 0x20, 0x69, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x77, 0x69, 0x74, 0x68, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x60, 0x5f, 0x2e, 0x6d, 0x61, 0x70, 0x60, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x6e, 0x2c, 0x20, 0x67, 0x75, 0x61, 0x72, 0x64, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x2d, 0x20, 0x28, 0x28, 0x6e, 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x7c, 0x7c, 0x20, 0x67, 0x75, 0x61, 0x72, 0x64, 0x20, 0x3f, 0x20, 0x31, 0x20, 0x3a, 0x20, 0x6e, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x47, 0x65, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x61, 0x73, 0x74, 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x20, 0x50, 0x61, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x2a, 0x2a, 0x6e, 0x2a, 0x2a, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x61, 0x73, 0x74, 0x20, 0x4e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x2a, 0x2a, 0x67, 0x75, 0x61, 0x72, 0x64, 0x2a, 0x2a, 0x20, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x20, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x73, 0x20, 0x69, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x60, 0x5f, 0x2e, 0x6d, 0x61, 0x70, 0x60, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x6c, 0x61, 0x73, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x6e, 0x2c, 0x20, 0x67, 0x75, 0x61, 0x72, 0x64, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x28, 0x6e, 0x20, 0x21, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x26, 0x26, 0x20, 0x21, 0x67, 0x75, 0x61, 0x72, 0x64, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x4d, 0x61, 0x74, 0x68, 0x2e, 0x6d, 0x61, 0x78, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x2d, 0x20, 0x6e, 0x2c, 0x20, 0x30, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x5b, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x2d, 0x20, 0x31, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x65, 0x76, 0x65, 0x72, 0x79, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x20, 0x62, 0x75, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x20, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x64, 0x20, 0x61, 0x73, 0x20, 0x60, 0x74, 0x61, 0x69, 0x6c, 0x60, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x60, 0x64, 0x72, 0x6f, 0x70, 0x60, 0x2e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x45, 0x73, 0x70, 0x65, 0x63, 0x69, 0x61, 0x6c, 0x6c, 0x79, 0x20, 0x75, 0x73, 0x65, 0x66, 0x75, 0x6c, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x20, 0x50, 0x61, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x6e, 0x20, 0x2a, 0x2a, 0x6e, 0x2a, 0x2a, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, 0x65, 0x73, 0x74, 0x20, 0x4e, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x2a, 0x2a, 0x67, 0x75, 0x61, 0x72, 0x64, 0x2a, 0x2a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x20, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x73, 0x20, 0x69, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x60, 0x5f, 0x2e, 0x6d, 0x61, 0x70, 0x60, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x72, 0x65, 0x73, 0x74, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x74, 0x61, 0x69, 0x6c, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x64, 0x72, 0x6f, 0x70, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x6e, 0x2c, 0x20, 0x67, 0x75, 0x61, 0x72, 0x64, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x28, 0x6e, 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x7c, 0x7c, 0x20, 0x67, 0x75, 0x61, 0x72, 0x64, 0x20, 0x3f, 0x20, 0x31, 0x20, 0x3a, 0x20, 0x6e, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x54, 0x72, 0x69, 0x6d, 0x20, 0x6f, 0x75, 0x74, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x79, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x5f, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20, 0x72, 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x20, 0x60, 0x66, 0x6c, 0x61, 0x74, 0x74, 0x65, 0x6e, 0x60, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x66, 0x6c, 0x61, 0x74, 0x74, 0x65, 0x6e, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x2c, 0x20, 0x73, 0x68, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x2c, 0x20, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x61, 0x63, 0x68, 0x28, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x2e, 0x69, 0x73, 0x41, 0x72, 0x72, 0x61, 0x79, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x68, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x20, 0x3f, 0x20, 0x70, 0x75, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x20, 0x3a, 0x20, 0x66, 0x6c, 0x61, 0x74, 0x74, 0x65, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x73, 0x68, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x2c, 0x20, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x2e, 0x70, 0x75, 0x73, 0x68, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x6c, 0x79, 0x20, 0x66, 0x6c, 0x61, 0x74, 0x74, 0x65, 0x6e, 0x65, 0x64, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x66, 0x6c, 0x61, 0x74, 0x74, 0x65, 0x6e, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x73, 0x68, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6c, 0x61, 0x74, 0x74, 0x65, 0x6e, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x73, 0x68, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x2c, 0x20, 0x5b, 0x5d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x64, 0x6f, 0x65, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x28, 0x73, 0x29, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x64, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x20, 0x31, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x20, 0x61, 0x20, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x2d, 0x66, 0x72, 0x65, 0x65, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x20, 0x49, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x68, 0x61, 0x73, 0x20, 0x61, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x62, 0x65, 0x65, 0x6e, 0x20, 0x73, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x2c, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x75, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x20, 0x66, 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x2e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x64, 0x20, 0x61, 0x73, 0x20, 0x60, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x60, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x75, 0x6e, 0x69, 0x71, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x69, 0x73, 0x53, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x2c, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x2e, 0x69, 0x73, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x69, 0x73, 0x53, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x29, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x20, 0x3d, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x69, 0x73, 0x53, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x73, 0x53, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x20, 0x3d, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x20, 0x3d, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x3f, 0x20, 0x5f, 0x2e, 0x6d, 0x61, 0x70, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x20, 0x3a, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x20, 0x3d, 0x20, 0x5b, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x73, 0x65, 0x65, 0x6e, 0x20, 0x3d, 0x20, 0x5b, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x61, 0x63, 0x68, 0x28, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x69, 0x73, 0x53, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x20, 0x3f, 0x20, 0x28, 0x21, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x7c, 0x7c, 0x20, 0x73, 0x65, 0x65, 0x6e, 0x5b, 0x73, 0x65, 0x65, 0x6e, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x2d, 0x20, 0x31, 0x5d, 0x20, 0x21, 0x3d, 0x3d, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x20, 0x3a, 0x20, 0x21, 0x5f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x28, 0x73, 0x65, 0x65, 0x6e, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x65, 0x6e, 0x2e, 0x70, 0x75, 0x73, 0x68, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x2e, 0x70, 0x75, 0x73, 0x68, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x5b, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x75, 0x6e, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x65, 0x61, 0x63, 0x68, 0x20, 0x64, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x63, 0x74, 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x6f, 0x66, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x61, 0x73, 0x73, 0x65, 0x64, 0x2d, 0x69, 0x6e, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x75, 0x6e, 0x69, 0x6f, 0x6e, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x75, 0x6e, 0x69, 0x71, 0x28, 0x63, 0x6f, 0x6e, 0x63, 0x61, 0x74, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x41, 0x72, 0x72, 0x61, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x20, 0x65, 0x76, 0x65, 0x72, 0x79, 0x20, 0x69, 0x74, 0x65, 0x6d, 0x20, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x20, 0x62, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x70, 0x61, 0x73, 0x73, 0x65, 0x64, 0x2d, 0x69, 0x6e, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x65, 0x73, 0x74, 0x20, 0x3d, 0x20, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x20, 0x31, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x28, 0x5f, 0x2e, 0x75, 0x6e, 0x69, 0x71, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x29, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x69, 0x74, 0x65, 0x6d, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x65, 0x76, 0x65, 0x72, 0x79, 0x28, 0x72, 0x65, 0x73, 0x74, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x28, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x2c, 0x20, 0x69, 0x74, 0x65, 0x6d, 0x29, 0x20, 0x3e, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x54, 0x61, 0x6b, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x20, 0x62, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x61, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x6f, 0x66, 0x20, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x4f, 0x6e, 0x6c, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x20, 0x69, 0x6e, 0x20, 0x6a, 0x75, 0x73, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x72, 0x65, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x64, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x65, 0x73, 0x74, 0x20, 0x3d, 0x20, 0x63, 0x6f, 0x6e, 0x63, 0x61, 0x74, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x41, 0x72, 0x72, 0x61, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2c, 0x20, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x20, 0x31, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x7b, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x21, 0x5f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x28, 0x72, 0x65, 0x73, 0x74, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x3b, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x5a, 0x69, 0x70, 0x20, 0x74, 0x6f, 0x67, 0x65, 0x74, 0x68, 0x65, 0x72, 0x20, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x73, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x2d, 0x2d, 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x73, 0x68, 0x61, 0x72, 0x65, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x61, 0x6e, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x67, 0x6f, 0x20, 0x74, 0x6f, 0x67, 0x65, 0x74, 0x68, 0x65, 0x72, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x7a, 0x69, 0x70, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x61, 0x72, 0x67, 0x73, 0x20, 0x3d, 0x20, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x6d, 0x61, 0x78, 0x28, 0x5f, 0x2e, 0x70, 0x6c, 0x75, 0x63, 0x6b, 0x28, 0x61, 0x72, 0x67, 0x73, 0x2c, 0x20, 0x27, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x27, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x20, 0x3d, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x28, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x76, 0x61, 0x72, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x5b, 0x69, 0x5d, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x70, 0x6c, 0x75, 0x63, 0x6b, 0x28, 0x61, 0x72, 0x67, 0x73, 0x2c, 0x20, 0x22, 0x22, 0x20, 0x2b, 0x20, 0x69, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x73, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x73, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2e, 0x20, 0x50, 0x61, 0x73, 0x73, 0x20, 0x65, 0x69, 0x74, 0x68, 0x65, 0x72, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x6f, 0x66, 0x20, 0x60, 0x5b, 0x6b, 0x65, 0x79, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5d, 0x60, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x70, 0x61, 0x69, 0x72, 0x73, 0x2c, 0x20, 0x6f, 0x72, 0x20, 0x74, 0x77, 0x6f, 0x20, 0x70, 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x61, 0x6d, 0x65, 0x20, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x2d, 0x2d, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x6b, 0x65, 0x79, 0x73, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x66, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6c, 0x69, 0x73, 0x74, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x7b, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x7b, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x76, 0x61, 0x72, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x30, 0x2c, 0x20, 0x6c, 0x20, 0x3d, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x6c, 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x5b, 0x6c, 0x69, 0x73, 0x74, 0x5b, 0x69, 0x5d, 0x5d, 0x20, 0x3d, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x5b, 0x69, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x5b, 0x6c, 0x69, 0x73, 0x74, 0x5b, 0x69, 0x5d, 0x5b, 0x30, 0x5d, 0x5d, 0x20, 0x3d, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x5b, 0x69, 0x5d, 0x5b, 0x31, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x72, 0x6f, 0x77, 0x73, 0x65, 0x72, 0x20, 0x64, 0x6f, 0x65, 0x73, 0x6e, 0x27, 0x74, 0x20, 0x73, 0x75, 0x70, 0x70, 0x6c, 0x79, 0x20, 0x75, 0x73, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x20, 0x28, 0x49, 0x27, 0x6d, 0x20, 0x6c, 0x6f, 0x6f, 0x6b, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x74, 0x20, 0x79, 0x6f, 0x75, 0x2c, 0x20, 0x2a, 0x2a, 0x4d, 0x53, 0x49, 0x45, 0x2a, 0x2a, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x77, 0x65, 0x20, 0x6e, 0x65, 0x65, 0x64, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x6f, 0x63, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x6e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x69, 0x74, 0x65, 0x6d, 0x20, 0x69, 0x6e, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x6f, 0x72, 0x20, 0x2d, 0x31, 0x20, 0x69, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x69, 0x74, 0x65, 0x6d, 0x20, 0x69, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x44, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x2a, 0x2a, 0x45, 0x43, 0x4d, 0x41, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x35, 0x2a, 0x2a, 0x27, 0x73, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x60, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x60, 0x20, 0x69, 0x66, 0x20, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x69, 0x73, 0x20, 0x6c, 0x61, 0x72, 0x67, 0x65, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x61, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x20, 0x69, 0x6e, 0x20, 0x73, 0x6f, 0x72, 0x74, 0x20, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2c, 0x20, 0x70, 0x61, 0x73, 0x73, 0x20, 0x60, 0x74, 0x72, 0x75, 0x65, 0x60, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x2a, 0x2a, 0x69, 0x73, 0x53, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x2a, 0x2a, 0x20, 0x74, 0x6f, 0x20, 0x75, 0x73, 0x65, 0x20, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x20, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x69, 0x74, 0x65, 0x6d, 0x2c, 0x20, 0x69, 0x73, 0x53, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x2d, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x30, 0x2c, 0x20, 0x6c, 0x20, 0x3d, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x69, 0x73, 0x53, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x74, 0x79, 0x70, 0x65, 0x6f, 0x66, 0x20, 0x69, 0x73, 0x53, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x20, 0x3d, 0x3d, 0x20, 0x27, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x27, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x28, 0x69, 0x73, 0x53, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x20, 0x3c, 0x20, 0x30, 0x20, 0x3f, 0x20, 0x4d, 0x61, 0x74, 0x68, 0x2e, 0x6d, 0x61, 0x78, 0x28, 0x30, 0x2c, 0x20, 0x6c, 0x20, 0x2b, 0x20, 0x69, 0x73, 0x53, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x73, 0x53, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x73, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x69, 0x74, 0x65, 0x6d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x5b, 0x69, 0x5d, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x69, 0x74, 0x65, 0x6d, 0x20, 0x3f, 0x20, 0x69, 0x20, 0x3a, 0x20, 0x2d, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x20, 0x26, 0x26, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x28, 0x69, 0x74, 0x65, 0x6d, 0x2c, 0x20, 0x69, 0x73, 0x53, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x6c, 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x20, 0x69, 0x66, 0x20, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x5b, 0x69, 0x5d, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x69, 0x74, 0x65, 0x6d, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x69, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x2d, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x44, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x2a, 0x2a, 0x45, 0x43, 0x4d, 0x41, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x35, 0x2a, 0x2a, 0x27, 0x73, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x60, 0x6c, 0x61, 0x73, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x60, 0x20, 0x69, 0x66, 0x20, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x6c, 0x61, 0x73, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x69, 0x74, 0x65, 0x6d, 0x2c, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x2d, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x68, 0x61, 0x73, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x21, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4c, 0x61, 0x73, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x20, 0x26, 0x26, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x6c, 0x61, 0x73, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4c, 0x61, 0x73, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x68, 0x61, 0x73, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3f, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x6c, 0x61, 0x73, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x28, 0x69, 0x74, 0x65, 0x6d, 0x2c, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x29, 0x20, 0x3a, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x6c, 0x61, 0x73, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x28, 0x69, 0x74, 0x65, 0x6d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x28, 0x68, 0x61, 0x73, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3f, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x3a, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x77, 0x68, 0x69, 0x6c, 0x65, 0x20, 0x28, 0x69, 0x2d, 0x2d, 0x29, 0x20, 0x69, 0x66, 0x20, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x5b, 0x69, 0x5d, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x69, 0x74, 0x65, 0x6d, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x69, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x2d, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x65, 0x74, 0x69, 0x63, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x20, 0x41, 0x20, 0x70, 0x6f, 0x72, 0x74, 0x20, 0x6f, 0x66, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x50, 0x79, 0x74, 0x68, 0x6f, 0x6e, 0x20, 0x60, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x28, 0x29, 0x60, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x20, 0x53, 0x65, 0x65, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x5b, 0x74, 0x68, 0x65, 0x20, 0x50, 0x79, 0x74, 0x68, 0x6f, 0x6e, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5d, 0x28, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x64, 0x6f, 0x63, 0x73, 0x2e, 0x70, 0x79, 0x74, 0x68, 0x6f, 0x6e, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x2f, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x23, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x29, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x73, 0x74, 0x61, 0x72, 0x74, 0x2c, 0x20, 0x73, 0x74, 0x6f, 0x70, 0x2c, 0x20, 0x73, 0x74, 0x65, 0x70, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x3c, 0x3d, 0x20, 0x31, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x6f, 0x70, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x20, 0x7c, 0x7c, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x65, 0x70, 0x20, 0x3d, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x5b, 0x32, 0x5d, 0x20, 0x7c, 0x7c, 0x20, 0x31, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6c, 0x65, 0x6e, 0x20, 0x3d, 0x20, 0x4d, 0x61, 0x74, 0x68, 0x2e, 0x6d, 0x61, 0x78, 0x28, 0x4d, 0x61, 0x74, 0x68, 0x2e, 0x63, 0x65, 0x69, 0x6c, 0x28, 0x28, 0x73, 0x74, 0x6f, 0x70, 0x20, 0x2d, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x29, 0x20, 0x2f, 0x20, 0x73, 0x74, 0x65, 0x70, 0x29, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x69, 0x64, 0x78, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x20, 0x3d, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x28, 0x6c, 0x65, 0x6e, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x77, 0x68, 0x69, 0x6c, 0x65, 0x28, 0x69, 0x64, 0x78, 0x20, 0x3c, 0x20, 0x6c, 0x65, 0x6e, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x5b, 0x69, 0x64, 0x78, 0x2b, 0x2b, 0x5d, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x20, 0x2b, 0x3d, 0x20, 0x73, 0x74, 0x65, 0x70, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x61, 0x68, 0x65, 0x6d, 0x29, 0x20, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x28, 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x60, 0x74, 0x68, 0x69, 0x73, 0x60, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x6c, 0x79, 0x29, 0x2e, 0x20, 0x44, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x2a, 0x2a, 0x45, 0x43, 0x4d, 0x41, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x35, 0x2a, 0x2a, 0x27, 0x73, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x60, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x62, 0x69, 0x6e, 0x64, 0x60, 0x20, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x62, 0x69, 0x6e, 0x64, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x2e, 0x62, 0x69, 0x6e, 0x64, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x42, 0x69, 0x6e, 0x64, 0x20, 0x26, 0x26, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x42, 0x69, 0x6e, 0x64, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x42, 0x69, 0x6e, 0x64, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x2c, 0x20, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x20, 0x31, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x61, 0x72, 0x67, 0x73, 0x20, 0x3d, 0x20, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x20, 0x32, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x73, 0x2e, 0x63, 0x6f, 0x6e, 0x63, 0x61, 0x74, 0x28, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x50, 0x61, 0x72, 0x74, 0x69, 0x61, 0x6c, 0x6c, 0x79, 0x20, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x20, 0x61, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x62, 0x79, 0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x68, 0x61, 0x73, 0x20, 0x68, 0x61, 0x64, 0x20, 0x73, 0x6f, 0x6d, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x69, 0x74, 0x73, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x70, 0x72, 0x65, 0x2d, 0x66, 0x69, 0x6c, 0x6c, 0x65, 0x64, 0x2c, 0x20, 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, 0x20, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x69, 0x6e, 0x67, 0x20, 0x69, 0x74, 0x73, 0x20, 0x64, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x20, 0x60, 0x74, 0x68, 0x69, 0x73, 0x60, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x70, 0x61, 0x72, 0x74, 0x69, 0x61, 0x6c, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x61, 0x72, 0x67, 0x73, 0x20, 0x3d, 0x20, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x20, 0x31, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x73, 0x2e, 0x63, 0x6f, 0x6e, 0x63, 0x61, 0x74, 0x28, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x42, 0x69, 0x6e, 0x64, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x27, 0x73, 0x20, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x20, 0x55, 0x73, 0x65, 0x66, 0x75, 0x6c, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x65, 0x6e, 0x73, 0x75, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x61, 0x74, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x73, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x6f, 0x6e, 0x20, 0x61, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x62, 0x65, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x74, 0x6f, 0x20, 0x69, 0x74, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x62, 0x69, 0x6e, 0x64, 0x41, 0x6c, 0x6c, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x73, 0x20, 0x3d, 0x20, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x20, 0x31, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x73, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x73, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x61, 0x63, 0x68, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x73, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x66, 0x29, 0x20, 0x7b, 0x20, 0x6f, 0x62, 0x6a, 0x5b, 0x66, 0x5d, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x62, 0x69, 0x6e, 0x64, 0x28, 0x6f, 0x62, 0x6a, 0x5b, 0x66, 0x5d, 0x2c, 0x20, 0x6f, 0x62, 0x6a, 0x29, 0x3b, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x4d, 0x65, 0x6d, 0x6f, 0x69, 0x7a, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x65, 0x78, 0x70, 0x65, 0x6e, 0x73, 0x69, 0x76, 0x65, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x62, 0x79, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x69, 0x74, 0x73, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x69, 0x7a, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x2c, 0x20, 0x68, 0x61, 0x73, 0x68, 0x65, 0x72, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x20, 0x3d, 0x20, 0x7b, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x68, 0x61, 0x73, 0x68, 0x65, 0x72, 0x20, 0x7c, 0x7c, 0x20, 0x28, 0x68, 0x61, 0x73, 0x68, 0x65, 0x72, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x3d, 0x20, 0x68, 0x61, 0x73, 0x68, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x68, 0x61, 0x73, 0x28, 0x6d, 0x65, 0x6d, 0x6f, 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x29, 0x20, 0x3f, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x5b, 0x6b, 0x65, 0x79, 0x5d, 0x20, 0x3a, 0x20, 0x28, 0x6d, 0x65, 0x6d, 0x6f, 0x5b, 0x6b, 0x65, 0x79, 0x5d, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x73, 0x20, 0x61, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x6f, 0x66, 0x20, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x69, 0x74, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x73, 0x75, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x64, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x2c, 0x20, 0x77, 0x61, 0x69, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x61, 0x72, 0x67, 0x73, 0x20, 0x3d, 0x20, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x20, 0x32, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x65, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x6e, 0x75, 0x6c, 0x6c, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x73, 0x29, 0x3b, 0x20, 0x7d, 0x2c, 0x20, 0x77, 0x61, 0x69, 0x74, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x44, 0x65, 0x66, 0x65, 0x72, 0x73, 0x20, 0x61, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x69, 0x6e, 0x67, 0x20, 0x69, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x72, 0x75, 0x6e, 0x20, 0x61, 0x66, 0x74, 0x65, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x20, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x20, 0x68, 0x61, 0x73, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x63, 0x6c, 0x65, 0x61, 0x72, 0x65, 0x64, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x64, 0x65, 0x66, 0x65, 0x72, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x5f, 0x2c, 0x20, 0x5b, 0x66, 0x75, 0x6e, 0x63, 0x2c, 0x20, 0x31, 0x5d, 0x2e, 0x63, 0x6f, 0x6e, 0x63, 0x61, 0x74, 0x28, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x20, 0x31, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x61, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x74, 0x68, 0x61, 0x74, 0x2c, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x69, 0x6e, 0x76, 0x6f, 0x6b, 0x65, 0x64, 0x2c, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x62, 0x65, 0x20, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x65, 0x64, 0x20, 0x61, 0x74, 0x20, 0x6d, 0x6f, 0x73, 0x74, 0x20, 0x6f, 0x6e, 0x63, 0x65, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x64, 0x75, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x20, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x74, 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x2c, 0x20, 0x77, 0x61, 0x69, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x73, 0x2c, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x2c, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x20, 0x3d, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x44, 0x61, 0x74, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x20, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x73, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6e, 0x6f, 0x77, 0x20, 0x3d, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x44, 0x61, 0x74, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x65, 0x6d, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x3d, 0x20, 0x77, 0x61, 0x69, 0x74, 0x20, 0x2d, 0x20, 0x28, 0x6e, 0x6f, 0x77, 0x20, 0x2d, 0x20, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x20, 0x3d, 0x20, 0x74, 0x68, 0x69, 0x73, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x72, 0x67, 0x73, 0x20, 0x3d, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x72, 0x65, 0x6d, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x3c, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6c, 0x65, 0x61, 0x72, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x28, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x20, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x20, 0x3d, 0x20, 0x6e, 0x6f, 0x77, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x73, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x20, 0x3d, 0x20, 0x73, 0x65, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x28, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x2c, 0x20, 0x72, 0x65, 0x6d, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x61, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x74, 0x68, 0x61, 0x74, 0x2c, 0x20, 0x61, 0x73, 0x20, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x61, 0x73, 0x20, 0x69, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x69, 0x6e, 0x76, 0x6f, 0x6b, 0x65, 0x64, 0x2c, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x6e, 0x6f, 0x74, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x62, 0x65, 0x20, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x65, 0x64, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x62, 0x65, 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x20, 0x61, 0x66, 0x74, 0x65, 0x72, 0x20, 0x69, 0x74, 0x20, 0x73, 0x74, 0x6f, 0x70, 0x73, 0x20, 0x62, 0x65, 0x69, 0x6e, 0x67, 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x4e, 0x20, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x2e, 0x20, 0x49, 0x66, 0x20, 0x60, 0x69, 0x6d, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x60, 0x20, 0x69, 0x73, 0x20, 0x70, 0x61, 0x73, 0x73, 0x65, 0x64, 0x2c, 0x20, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x6c, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x65, 0x64, 0x67, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x65, 0x61, 0x64, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x72, 0x61, 0x69, 0x6c, 0x69, 0x6e, 0x67, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x64, 0x65, 0x62, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x2c, 0x20, 0x77, 0x61, 0x69, 0x74, 0x2c, 0x20, 0x69, 0x6d, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x2c, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x20, 0x3d, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x73, 0x20, 0x3d, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x20, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x69, 0x6d, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x29, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x73, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x4e, 0x6f, 0x77, 0x20, 0x3d, 0x20, 0x69, 0x6d, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x20, 0x26, 0x26, 0x20, 0x21, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6c, 0x65, 0x61, 0x72, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x28, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x20, 0x3d, 0x20, 0x73, 0x65, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x28, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x2c, 0x20, 0x77, 0x61, 0x69, 0x74, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x63, 0x61, 0x6c, 0x6c, 0x4e, 0x6f, 0x77, 0x29, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x73, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x61, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x62, 0x65, 0x20, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x64, 0x20, 0x61, 0x74, 0x20, 0x6d, 0x6f, 0x73, 0x74, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x2c, 0x20, 0x6e, 0x6f, 0x20, 0x6d, 0x61, 0x74, 0x74, 0x65, 0x72, 0x20, 0x68, 0x6f, 0x77, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x6f, 0x66, 0x74, 0x65, 0x6e, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x20, 0x69, 0x74, 0x2e, 0x20, 0x55, 0x73, 0x65, 0x66, 0x75, 0x6c, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x6c, 0x61, 0x7a, 0x79, 0x20, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x6f, 0x6e, 0x63, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x61, 0x6e, 0x20, 0x3d, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x72, 0x61, 0x6e, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x61, 0x6e, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x20, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x70, 0x61, 0x73, 0x73, 0x65, 0x64, 0x20, 0x61, 0x73, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x2c, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x69, 0x6e, 0x67, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x64, 0x6a, 0x75, 0x73, 0x74, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x20, 0x72, 0x75, 0x6e, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x61, 0x66, 0x74, 0x65, 0x72, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x6c, 0x79, 0x20, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x77, 0x72, 0x61, 0x70, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x2c, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x61, 0x72, 0x67, 0x73, 0x20, 0x3d, 0x20, 0x5b, 0x66, 0x75, 0x6e, 0x63, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x75, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x61, 0x72, 0x67, 0x73, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x73, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x61, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2c, 0x20, 0x65, 0x61, 0x63, 0x68, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x66, 0x6f, 0x6c, 0x6c, 0x6f, 0x77, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x73, 0x20, 0x3d, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x61, 0x72, 0x67, 0x73, 0x20, 0x3d, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x76, 0x61, 0x72, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x73, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x2d, 0x20, 0x31, 0x3b, 0x20, 0x69, 0x20, 0x3e, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x69, 0x2d, 0x2d, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x72, 0x67, 0x73, 0x20, 0x3d, 0x20, 0x5b, 0x66, 0x75, 0x6e, 0x63, 0x73, 0x5b, 0x69, 0x5d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x73, 0x29, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x72, 0x67, 0x73, 0x5b, 0x30, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x61, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x62, 0x65, 0x20, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x64, 0x20, 0x61, 0x66, 0x74, 0x65, 0x72, 0x20, 0x62, 0x65, 0x69, 0x6e, 0x67, 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x20, 0x4e, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x61, 0x66, 0x74, 0x65, 0x72, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x20, 0x3c, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x28, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x2d, 0x2d, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x20, 0x3c, 0x20, 0x31, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x27, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x44, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x2a, 0x2a, 0x45, 0x43, 0x4d, 0x41, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x35, 0x2a, 0x2a, 0x27, 0x73, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x60, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x60, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x20, 0x3d, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x20, 0x7c, 0x7c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6f, 0x62, 0x6a, 0x20, 0x21, 0x3d, 0x3d, 0x20, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x29, 0x20, 0x74, 0x68, 0x72, 0x6f, 0x77, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x54, 0x79, 0x70, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x27, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x27, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6b, 0x65, 0x79, 0x73, 0x20, 0x3d, 0x20, 0x5b, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x76, 0x61, 0x72, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x69, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x2e, 0x68, 0x61, 0x73, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x29, 0x29, 0x20, 0x6b, 0x65, 0x79, 0x73, 0x5b, 0x6b, 0x65, 0x79, 0x73, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x5d, 0x20, 0x3d, 0x20, 0x6b, 0x65, 0x79, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6b, 0x65, 0x79, 0x73, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x27, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x5b, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x76, 0x61, 0x72, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x69, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x2e, 0x68, 0x61, 0x73, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x29, 0x29, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2e, 0x70, 0x75, 0x73, 0x68, 0x28, 0x6f, 0x62, 0x6a, 0x5b, 0x6b, 0x65, 0x79, 0x5d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x20, 0x61, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x20, 0x61, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x60, 0x5b, 0x6b, 0x65, 0x79, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5d, 0x60, 0x20, 0x70, 0x61, 0x69, 0x72, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x70, 0x61, 0x69, 0x72, 0x73, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x70, 0x61, 0x69, 0x72, 0x73, 0x20, 0x3d, 0x20, 0x5b, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x76, 0x61, 0x72, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x69, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x2e, 0x68, 0x61, 0x73, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x29, 0x29, 0x20, 0x70, 0x61, 0x69, 0x72, 0x73, 0x2e, 0x70, 0x75, 0x73, 0x68, 0x28, 0x5b, 0x6b, 0x65, 0x79, 0x2c, 0x20, 0x6f, 0x62, 0x6a, 0x5b, 0x6b, 0x65, 0x79, 0x5d, 0x5d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x70, 0x61, 0x69, 0x72, 0x73, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x69, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x7b, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x76, 0x61, 0x72, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x69, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x2e, 0x68, 0x61, 0x73, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x29, 0x29, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x5b, 0x6f, 0x62, 0x6a, 0x5b, 0x6b, 0x65, 0x79, 0x5d, 0x5d, 0x20, 0x3d, 0x20, 0x6b, 0x65, 0x79, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x20, 0x73, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x20, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x64, 0x20, 0x61, 0x73, 0x20, 0x60, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x60, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x5b, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x76, 0x61, 0x72, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x69, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x2e, 0x69, 0x73, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x5b, 0x6b, 0x65, 0x79, 0x5d, 0x29, 0x29, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x2e, 0x70, 0x75, 0x73, 0x68, 0x28, 0x6b, 0x65, 0x79, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x2e, 0x73, 0x6f, 0x72, 0x74, 0x28, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x20, 0x61, 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x70, 0x61, 0x73, 0x73, 0x65, 0x64, 0x2d, 0x69, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x28, 0x73, 0x29, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x61, 0x63, 0x68, 0x28, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x20, 0x31, 0x29, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x76, 0x61, 0x72, 0x20, 0x70, 0x72, 0x6f, 0x70, 0x20, 0x69, 0x6e, 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x62, 0x6a, 0x5b, 0x70, 0x72, 0x6f, 0x70, 0x5d, 0x20, 0x3d, 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5b, 0x70, 0x72, 0x6f, 0x70, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x70, 0x79, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x77, 0x68, 0x69, 0x74, 0x65, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x64, 0x20, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x70, 0x69, 0x63, 0x6b, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x63, 0x6f, 0x70, 0x79, 0x20, 0x3d, 0x20, 0x7b, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6b, 0x65, 0x79, 0x73, 0x20, 0x3d, 0x20, 0x63, 0x6f, 0x6e, 0x63, 0x61, 0x74, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x41, 0x72, 0x72, 0x61, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2c, 0x20, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x20, 0x31, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x61, 0x63, 0x68, 0x28, 0x6b, 0x65, 0x79, 0x73, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6b, 0x65, 0x79, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6b, 0x65, 0x79, 0x20, 0x69, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x63, 0x6f, 0x70, 0x79, 0x5b, 0x6b, 0x65, 0x79, 0x5d, 0x20, 0x3d, 0x20, 0x6f, 0x62, 0x6a, 0x5b, 0x6b, 0x65, 0x79, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x63, 0x6f, 0x70, 0x79, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x70, 0x79, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x6c, 0x61, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x64, 0x20, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x6f, 0x6d, 0x69, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x63, 0x6f, 0x70, 0x79, 0x20, 0x3d, 0x20, 0x7b, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6b, 0x65, 0x79, 0x73, 0x20, 0x3d, 0x20, 0x63, 0x6f, 0x6e, 0x63, 0x61, 0x74, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x41, 0x72, 0x72, 0x61, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2c, 0x20, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x20, 0x31, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x76, 0x61, 0x72, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x69, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x5f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x28, 0x6b, 0x65, 0x79, 0x73, 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x29, 0x29, 0x20, 0x63, 0x6f, 0x70, 0x79, 0x5b, 0x6b, 0x65, 0x79, 0x5d, 0x20, 0x3d, 0x20, 0x6f, 0x62, 0x6a, 0x5b, 0x6b, 0x65, 0x79, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x63, 0x6f, 0x70, 0x79, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x46, 0x69, 0x6c, 0x6c, 0x20, 0x69, 0x6e, 0x20, 0x61, 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x20, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x61, 0x63, 0x68, 0x28, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x20, 0x31, 0x29, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x76, 0x61, 0x72, 0x20, 0x70, 0x72, 0x6f, 0x70, 0x20, 0x69, 0x6e, 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6f, 0x62, 0x6a, 0x5b, 0x70, 0x72, 0x6f, 0x70, 0x5d, 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x6f, 0x62, 0x6a, 0x5b, 0x70, 0x72, 0x6f, 0x70, 0x5d, 0x20, 0x3d, 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5b, 0x70, 0x72, 0x6f, 0x70, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, 0x28, 0x73, 0x68, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x2d, 0x63, 0x6c, 0x6f, 0x6e, 0x65, 0x64, 0x29, 0x20, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x63, 0x6c, 0x6f, 0x6e, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x5f, 0x2e, 0x69, 0x73, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x41, 0x72, 0x72, 0x61, 0x79, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x3f, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x28, 0x29, 0x20, 0x3a, 0x20, 0x5f, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x28, 0x7b, 0x7d, 0x2c, 0x20, 0x6f, 0x62, 0x6a, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x6e, 0x76, 0x6f, 0x6b, 0x65, 0x73, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x54, 0x68, 0x65, 0x20, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x70, 0x75, 0x72, 0x70, 0x6f, 0x73, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x20, 0x69, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x22, 0x74, 0x61, 0x70, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x22, 0x20, 0x61, 0x20, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x20, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2c, 0x20, 0x69, 0x6e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x20, 0x74, 0x6f, 0x20, 0x70, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x6f, 0x6e, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x20, 0x77, 0x69, 0x74, 0x68, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x74, 0x61, 0x70, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x72, 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x60, 0x69, 0x73, 0x45, 0x71, 0x75, 0x61, 0x6c, 0x60, 0x2e, 0x0a, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x65, 0x71, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x61, 0x2c, 0x20, 0x62, 0x2c, 0x20, 0x61, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x2c, 0x20, 0x62, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x65, 0x71, 0x75, 0x61, 0x6c, 0x2e, 0x20, 0x60, 0x30, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x2d, 0x30, 0x60, 0x2c, 0x20, 0x62, 0x75, 0x74, 0x20, 0x74, 0x68, 0x65, 0x79, 0x20, 0x61, 0x72, 0x65, 0x6e, 0x27, 0x74, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x53, 0x65, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x48, 0x61, 0x72, 0x6d, 0x6f, 0x6e, 0x79, 0x20, 0x60, 0x65, 0x67, 0x61, 0x6c, 0x60, 0x20, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x3a, 0x20, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x69, 0x6b, 0x69, 0x2e, 0x65, 0x63, 0x6d, 0x61, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x64, 0x6f, 0x6b, 0x75, 0x2e, 0x70, 0x68, 0x70, 0x3f, 0x69, 0x64, 0x3d, 0x68, 0x61, 0x72, 0x6d, 0x6f, 0x6e, 0x79, 0x3a, 0x65, 0x67, 0x61, 0x6c, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x61, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x62, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x20, 0x21, 0x3d, 0x3d, 0x20, 0x30, 0x20, 0x7c, 0x7c, 0x20, 0x31, 0x20, 0x2f, 0x20, 0x61, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x20, 0x2f, 0x20, 0x62, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, 0x20, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x20, 0x69, 0x73, 0x20, 0x6e, 0x65, 0x63, 0x65, 0x73, 0x73, 0x61, 0x72, 0x79, 0x20, 0x62, 0x65, 0x63, 0x61, 0x75, 0x73, 0x65, 0x20, 0x60, 0x6e, 0x75, 0x6c, 0x6c, 0x20, 0x3d, 0x3d, 0x20, 0x75, 0x6e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x60, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x61, 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x20, 0x7c, 0x7c, 0x20, 0x62, 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x62, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x55, 0x6e, 0x77, 0x72, 0x61, 0x70, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x61, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x6f, 0x66, 0x20, 0x5f, 0x29, 0x20, 0x61, 0x20, 0x3d, 0x20, 0x61, 0x2e, 0x5f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x62, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x6f, 0x66, 0x20, 0x5f, 0x29, 0x20, 0x62, 0x20, 0x3d, 0x20, 0x62, 0x2e, 0x5f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x20, 0x60, 0x5b, 0x5b, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x5d, 0x5d, 0x60, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x20, 0x3d, 0x20, 0x74, 0x6f, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x20, 0x21, 0x3d, 0x20, 0x74, 0x6f, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x62, 0x29, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x20, 0x28, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x73, 0x2c, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x2c, 0x20, 0x64, 0x61, 0x74, 0x65, 0x73, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x65, 0x61, 0x6e, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x27, 0x5b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x5d, 0x27, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x50, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, 0x69, 0x72, 0x20, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x65, 0x71, 0x75, 0x69, 0x76, 0x61, 0x6c, 0x65, 0x6e, 0x74, 0x3b, 0x20, 0x74, 0x68, 0x75, 0x73, 0x2c, 0x20, 0x60, 0x22, 0x35, 0x22, 0x60, 0x20, 0x69, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x65, 0x71, 0x75, 0x69, 0x76, 0x61, 0x6c, 0x65, 0x6e, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x60, 0x6e, 0x65, 0x77, 0x20, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x28, 0x22, 0x35, 0x22, 0x29, 0x60, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x20, 0x3d, 0x3d, 0x20, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x28, 0x62, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x27, 0x5b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x5d, 0x27, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x60, 0x4e, 0x61, 0x4e, 0x60, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x65, 0x71, 0x75, 0x69, 0x76, 0x61, 0x6c, 0x65, 0x6e, 0x74, 0x2c, 0x20, 0x62, 0x75, 0x74, 0x20, 0x6e, 0x6f, 0x6e, 0x2d, 0x72, 0x65, 0x66, 0x6c, 0x65, 0x78, 0x69, 0x76, 0x65, 0x2e, 0x20, 0x41, 0x6e, 0x20, 0x60, 0x65, 0x67, 0x61, 0x6c, 0x60, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x20, 0x69, 0x73, 0x20, 0x70, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x65, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x20, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x20, 0x21, 0x3d, 0x20, 0x2b, 0x61, 0x20, 0x3f, 0x20, 0x62, 0x20, 0x21, 0x3d, 0x20, 0x2b, 0x62, 0x20, 0x3a, 0x20, 0x28, 0x61, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x20, 0x3f, 0x20, 0x31, 0x20, 0x2f, 0x20, 0x61, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x20, 0x2f, 0x20, 0x62, 0x20, 0x3a, 0x20, 0x61, 0x20, 0x3d, 0x3d, 0x20, 0x2b, 0x62, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x27, 0x5b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x44, 0x61, 0x74, 0x65, 0x5d, 0x27, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x27, 0x5b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x42, 0x6f, 0x6f, 0x6c, 0x65, 0x61, 0x6e, 0x5d, 0x27, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x6f, 0x65, 0x72, 0x63, 0x65, 0x20, 0x64, 0x61, 0x74, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x65, 0x61, 0x6e, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x20, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2e, 0x20, 0x44, 0x61, 0x74, 0x65, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, 0x65, 0x69, 0x72, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x20, 0x4e, 0x6f, 0x74, 0x65, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x64, 0x61, 0x74, 0x65, 0x73, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x6f, 0x66, 0x20, 0x60, 0x4e, 0x61, 0x4e, 0x60, 0x20, 0x61, 0x72, 0x65, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x65, 0x71, 0x75, 0x69, 0x76, 0x61, 0x6c, 0x65, 0x6e, 0x74, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x2b, 0x61, 0x20, 0x3d, 0x3d, 0x20, 0x2b, 0x62, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x67, 0x45, 0x78, 0x70, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, 0x65, 0x69, 0x72, 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x27, 0x5b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x52, 0x65, 0x67, 0x45, 0x78, 0x70, 0x5d, 0x27, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x2e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x62, 0x2e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x26, 0x26, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x3d, 0x3d, 0x20, 0x62, 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x26, 0x26, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x62, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x26, 0x26, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x2e, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x43, 0x61, 0x73, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x62, 0x2e, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x43, 0x61, 0x73, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x74, 0x79, 0x70, 0x65, 0x6f, 0x66, 0x20, 0x61, 0x20, 0x21, 0x3d, 0x20, 0x27, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x27, 0x20, 0x7c, 0x7c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x6f, 0x66, 0x20, 0x62, 0x20, 0x21, 0x3d, 0x20, 0x27, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x27, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, 0x73, 0x73, 0x75, 0x6d, 0x65, 0x20, 0x65, 0x71, 0x75, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x63, 0x79, 0x63, 0x6c, 0x69, 0x63, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x73, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x63, 0x79, 0x63, 0x6c, 0x69, 0x63, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x73, 0x20, 0x69, 0x73, 0x20, 0x61, 0x64, 0x61, 0x70, 0x74, 0x65, 0x64, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x45, 0x53, 0x20, 0x35, 0x2e, 0x31, 0x20, 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x31, 0x35, 0x2e, 0x31, 0x32, 0x2e, 0x33, 0x2c, 0x20, 0x61, 0x62, 0x73, 0x74, 0x72, 0x61, 0x63, 0x74, 0x20, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x60, 0x4a, 0x4f, 0x60, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x3d, 0x20, 0x61, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x77, 0x68, 0x69, 0x6c, 0x65, 0x20, 0x28, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x2d, 0x2d, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x20, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x20, 0x50, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x69, 0x73, 0x20, 0x69, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x65, 0x6c, 0x79, 0x20, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x6f, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x20, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x61, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x5b, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x5d, 0x20, 0x3d, 0x3d, 0x20, 0x61, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x62, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x5b, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x5d, 0x20, 0x3d, 0x3d, 0x20, 0x62, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, 0x64, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x72, 0x61, 0x76, 0x65, 0x72, 0x73, 0x65, 0x64, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x2e, 0x70, 0x75, 0x73, 0x68, 0x28, 0x61, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x2e, 0x70, 0x75, 0x73, 0x68, 0x28, 0x62, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x3d, 0x20, 0x30, 0x2c, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x6c, 0x79, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x27, 0x5b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x5d, 0x27, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x20, 0x69, 0x66, 0x20, 0x61, 0x20, 0x64, 0x65, 0x65, 0x70, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x20, 0x69, 0x73, 0x20, 0x6e, 0x65, 0x63, 0x65, 0x73, 0x73, 0x61, 0x72, 0x79, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x3d, 0x20, 0x61, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x62, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x44, 0x65, 0x65, 0x70, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x20, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x6e, 0x6f, 0x6e, 0x2d, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x20, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x77, 0x68, 0x69, 0x6c, 0x65, 0x20, 0x28, 0x73, 0x69, 0x7a, 0x65, 0x2d, 0x2d, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x28, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x65, 0x71, 0x28, 0x61, 0x5b, 0x73, 0x69, 0x7a, 0x65, 0x5d, 0x2c, 0x20, 0x62, 0x5b, 0x73, 0x69, 0x7a, 0x65, 0x5d, 0x2c, 0x20, 0x61, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x2c, 0x20, 0x62, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x29, 0x29, 0x29, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x64, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x65, 0x71, 0x75, 0x69, 0x76, 0x61, 0x6c, 0x65, 0x6e, 0x74, 0x2c, 0x20, 0x62, 0x75, 0x74, 0x20, 0x60, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x60, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x64, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x20, 0x61, 0x72, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x61, 0x43, 0x74, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x61, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x62, 0x43, 0x74, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x62, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x6f, 0x72, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x61, 0x43, 0x74, 0x6f, 0x72, 0x20, 0x21, 0x3d, 0x3d, 0x20, 0x62, 0x43, 0x74, 0x6f, 0x72, 0x20, 0x26, 0x26, 0x20, 0x21, 0x28, 0x5f, 0x2e, 0x69, 0x73, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x61, 0x43, 0x74, 0x6f, 0x72, 0x29, 0x20, 0x26, 0x26, 0x20, 0x28, 0x61, 0x43, 0x74, 0x6f, 0x72, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x6f, 0x66, 0x20, 0x61, 0x43, 0x74, 0x6f, 0x72, 0x29, 0x20, 0x26, 0x26, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x62, 0x43, 0x74, 0x6f, 0x72, 0x29, 0x20, 0x26, 0x26, 0x20, 0x28, 0x62, 0x43, 0x74, 0x6f, 0x72, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x6f, 0x66, 0x20, 0x62, 0x43, 0x74, 0x6f, 0x72, 0x29, 0x29, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x44, 0x65, 0x65, 0x70, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x76, 0x61, 0x72, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x69, 0x6e, 0x20, 0x61, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x2e, 0x68, 0x61, 0x73, 0x28, 0x61, 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x29, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x6f, 0x66, 0x20, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x2b, 0x2b, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x44, 0x65, 0x65, 0x70, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x20, 0x65, 0x61, 0x63, 0x68, 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x28, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x68, 0x61, 0x73, 0x28, 0x62, 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x29, 0x20, 0x26, 0x26, 0x20, 0x65, 0x71, 0x28, 0x61, 0x5b, 0x6b, 0x65, 0x79, 0x5d, 0x2c, 0x20, 0x62, 0x5b, 0x6b, 0x65, 0x79, 0x5d, 0x2c, 0x20, 0x61, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x2c, 0x20, 0x62, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x29, 0x29, 0x29, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x45, 0x6e, 0x73, 0x75, 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x62, 0x6f, 0x74, 0x68, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x6f, 0x66, 0x20, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x6b, 0x65, 0x79, 0x20, 0x69, 0x6e, 0x20, 0x62, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x2e, 0x68, 0x61, 0x73, 0x28, 0x62, 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x29, 0x20, 0x26, 0x26, 0x20, 0x21, 0x28, 0x73, 0x69, 0x7a, 0x65, 0x2d, 0x2d, 0x29, 0x29, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x21, 0x73, 0x69, 0x7a, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x72, 0x61, 0x76, 0x65, 0x72, 0x73, 0x65, 0x64, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x2e, 0x70, 0x6f, 0x70, 0x28, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x2e, 0x70, 0x6f, 0x70, 0x28, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x50, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x61, 0x20, 0x64, 0x65, 0x65, 0x70, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x20, 0x74, 0x6f, 0x20, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x20, 0x69, 0x66, 0x20, 0x74, 0x77, 0x6f, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x65, 0x71, 0x75, 0x61, 0x6c, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x45, 0x71, 0x75, 0x61, 0x6c, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x61, 0x2c, 0x20, 0x62, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x65, 0x71, 0x28, 0x61, 0x2c, 0x20, 0x62, 0x2c, 0x20, 0x5b, 0x5d, 0x2c, 0x20, 0x5b, 0x5d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x73, 0x20, 0x61, 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2c, 0x20, 0x6f, 0x72, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x3f, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, 0x6e, 0x20, 0x22, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x68, 0x61, 0x73, 0x20, 0x6e, 0x6f, 0x20, 0x65, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x6f, 0x77, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6f, 0x62, 0x6a, 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x72, 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x2e, 0x69, 0x73, 0x41, 0x72, 0x72, 0x61, 0x79, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7c, 0x7c, 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x76, 0x61, 0x72, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x69, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x2e, 0x68, 0x61, 0x73, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x29, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x72, 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x73, 0x20, 0x61, 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x61, 0x20, 0x44, 0x4f, 0x4d, 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x3f, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x21, 0x21, 0x28, 0x6f, 0x62, 0x6a, 0x20, 0x26, 0x26, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x54, 0x79, 0x70, 0x65, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x31, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x73, 0x20, 0x61, 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3f, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x44, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x45, 0x43, 0x4d, 0x41, 0x35, 0x27, 0x73, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x69, 0x73, 0x41, 0x72, 0x72, 0x61, 0x79, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x41, 0x72, 0x72, 0x61, 0x79, 0x20, 0x3d, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x49, 0x73, 0x41, 0x72, 0x72, 0x61, 0x79, 0x20, 0x7c, 0x7c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x6f, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x3d, 0x3d, 0x20, 0x27, 0x5b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x5d, 0x27, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x73, 0x20, 0x61, 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x20, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x3f, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, 0x64, 0x64, 0x20, 0x73, 0x6f, 0x6d, 0x65, 0x20, 0x69, 0x73, 0x54, 0x79, 0x70, 0x65, 0x20, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x3a, 0x20, 0x69, 0x73, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x20, 0x69, 0x73, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x69, 0x73, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2c, 0x20, 0x69, 0x73, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x2c, 0x20, 0x69, 0x73, 0x44, 0x61, 0x74, 0x65, 0x2c, 0x20, 0x69, 0x73, 0x52, 0x65, 0x67, 0x45, 0x78, 0x70, 0x2e, 0x0a, 0x20, 0x20, 0x65, 0x61, 0x63, 0x68, 0x28, 0x5b, 0x27, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x27, 0x2c, 0x20, 0x27, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x27, 0x2c, 0x20, 0x27, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x27, 0x2c, 0x20, 0x27, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x27, 0x2c, 0x20, 0x27, 0x44, 0x61, 0x74, 0x65, 0x27, 0x2c, 0x20, 0x27, 0x52, 0x65, 0x67, 0x45, 0x78, 0x70, 0x27, 0x5d, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x5b, 0x27, 0x69, 0x73, 0x27, 0x20, 0x2b, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x5d, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x6f, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x3d, 0x3d, 0x20, 0x27, 0x5b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x27, 0x20, 0x2b, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2b, 0x20, 0x27, 0x5d, 0x27, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x61, 0x20, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x62, 0x72, 0x6f, 0x77, 0x73, 0x65, 0x72, 0x73, 0x20, 0x28, 0x61, 0x68, 0x65, 0x6d, 0x2c, 0x20, 0x49, 0x45, 0x29, 0x2c, 0x20, 0x77, 0x68, 0x65, 0x72, 0x65, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x74, 0x68, 0x65, 0x72, 0x65, 0x20, 0x69, 0x73, 0x6e, 0x27, 0x74, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x69, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x22, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x20, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x5f, 0x2e, 0x69, 0x73, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x28, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x29, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x21, 0x21, 0x28, 0x6f, 0x62, 0x6a, 0x20, 0x26, 0x26, 0x20, 0x5f, 0x2e, 0x68, 0x61, 0x73, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x27, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x65, 0x27, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x20, 0x60, 0x69, 0x73, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x60, 0x20, 0x69, 0x66, 0x20, 0x61, 0x70, 0x70, 0x72, 0x6f, 0x70, 0x72, 0x69, 0x61, 0x74, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x74, 0x79, 0x70, 0x65, 0x6f, 0x66, 0x20, 0x28, 0x2f, 0x2e, 0x2f, 0x29, 0x20, 0x21, 0x3d, 0x3d, 0x20, 0x27, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x27, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x79, 0x70, 0x65, 0x6f, 0x66, 0x20, 0x6f, 0x62, 0x6a, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x27, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x27, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x73, 0x20, 0x61, 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x61, 0x20, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x65, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x3f, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x46, 0x69, 0x6e, 0x69, 0x74, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x69, 0x73, 0x46, 0x69, 0x6e, 0x69, 0x74, 0x65, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x26, 0x26, 0x20, 0x21, 0x69, 0x73, 0x4e, 0x61, 0x4e, 0x28, 0x70, 0x61, 0x72, 0x73, 0x65, 0x46, 0x6c, 0x6f, 0x61, 0x74, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x60, 0x4e, 0x61, 0x4e, 0x60, 0x3f, 0x20, 0x28, 0x4e, 0x61, 0x4e, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x64, 0x6f, 0x65, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x65, 0x71, 0x75, 0x61, 0x6c, 0x20, 0x69, 0x74, 0x73, 0x65, 0x6c, 0x66, 0x29, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x4e, 0x61, 0x4e, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x26, 0x26, 0x20, 0x6f, 0x62, 0x6a, 0x20, 0x21, 0x3d, 0x20, 0x2b, 0x6f, 0x62, 0x6a, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x73, 0x20, 0x61, 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x61, 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x65, 0x61, 0x6e, 0x3f, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x42, 0x6f, 0x6f, 0x6c, 0x65, 0x61, 0x6e, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x74, 0x72, 0x75, 0x65, 0x20, 0x7c, 0x7c, 0x20, 0x6f, 0x62, 0x6a, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x20, 0x7c, 0x7c, 0x20, 0x74, 0x6f, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x3d, 0x3d, 0x20, 0x27, 0x5b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x42, 0x6f, 0x6f, 0x6c, 0x65, 0x61, 0x6e, 0x5d, 0x27, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x73, 0x20, 0x61, 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x65, 0x71, 0x75, 0x61, 0x6c, 0x20, 0x74, 0x6f, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x3f, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x4e, 0x75, 0x6c, 0x6c, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x73, 0x20, 0x61, 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x20, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x75, 0x6e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x3f, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x55, 0x6e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x63, 0x75, 0x74, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x20, 0x69, 0x66, 0x20, 0x61, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x68, 0x61, 0x73, 0x20, 0x61, 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x20, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x20, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6c, 0x79, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x6f, 0x6e, 0x20, 0x69, 0x74, 0x73, 0x65, 0x6c, 0x66, 0x20, 0x28, 0x69, 0x6e, 0x20, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x20, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x2c, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x6f, 0x6e, 0x20, 0x61, 0x20, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x74, 0x79, 0x70, 0x65, 0x29, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x68, 0x61, 0x73, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x68, 0x61, 0x73, 0x4f, 0x77, 0x6e, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x20, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x75, 0x6e, 0x20, 0x55, 0x6e, 0x64, 0x65, 0x72, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x6a, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x2a, 0x6e, 0x6f, 0x43, 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74, 0x2a, 0x20, 0x6d, 0x6f, 0x64, 0x65, 0x2c, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x60, 0x5f, 0x60, 0x20, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x69, 0x74, 0x73, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x20, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x2e, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x61, 0x20, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x55, 0x6e, 0x64, 0x65, 0x72, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x6e, 0x6f, 0x43, 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x5f, 0x20, 0x3d, 0x20, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x55, 0x6e, 0x64, 0x65, 0x72, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x68, 0x69, 0x73, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x4b, 0x65, 0x65, 0x70, 0x20, 0x74, 0x68, 0x65, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x75, 0x6e, 0x20, 0x61, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x2a, 0x2a, 0x6e, 0x2a, 0x2a, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x2c, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x61, 0x63, 0x63, 0x75, 0x6d, 0x20, 0x3d, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x28, 0x6e, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x76, 0x61, 0x72, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x6e, 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x20, 0x61, 0x63, 0x63, 0x75, 0x6d, 0x5b, 0x69, 0x5d, 0x20, 0x3d, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x69, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x63, 0x63, 0x75, 0x6d, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x20, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x20, 0x62, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x20, 0x6d, 0x69, 0x6e, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6d, 0x61, 0x78, 0x20, 0x28, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x29, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6d, 0x69, 0x6e, 0x2c, 0x20, 0x6d, 0x61, 0x78, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6d, 0x61, 0x78, 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x78, 0x20, 0x3d, 0x20, 0x6d, 0x69, 0x6e, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x6e, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6d, 0x69, 0x6e, 0x20, 0x2b, 0x20, 0x4d, 0x61, 0x74, 0x68, 0x2e, 0x66, 0x6c, 0x6f, 0x6f, 0x72, 0x28, 0x4d, 0x61, 0x74, 0x68, 0x2e, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x28, 0x29, 0x20, 0x2a, 0x20, 0x28, 0x6d, 0x61, 0x78, 0x20, 0x2d, 0x20, 0x6d, 0x69, 0x6e, 0x20, 0x2b, 0x20, 0x31, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x4c, 0x69, 0x73, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x48, 0x54, 0x4d, 0x4c, 0x20, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x69, 0x65, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x65, 0x73, 0x63, 0x61, 0x70, 0x69, 0x6e, 0x67, 0x2e, 0x0a, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x4d, 0x61, 0x70, 0x20, 0x3d, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x3a, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x27, 0x26, 0x27, 0x3a, 0x20, 0x27, 0x26, 0x61, 0x6d, 0x70, 0x3b, 0x27, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x27, 0x3c, 0x27, 0x3a, 0x20, 0x27, 0x26, 0x6c, 0x74, 0x3b, 0x27, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x27, 0x3e, 0x27, 0x3a, 0x20, 0x27, 0x26, 0x67, 0x74, 0x3b, 0x27, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x27, 0x22, 0x27, 0x3a, 0x20, 0x27, 0x26, 0x71, 0x75, 0x6f, 0x74, 0x3b, 0x27, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x27, 0x22, 0x3a, 0x20, 0x27, 0x26, 0x23, 0x78, 0x32, 0x37, 0x3b, 0x27, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x27, 0x2f, 0x27, 0x3a, 0x20, 0x27, 0x26, 0x23, 0x78, 0x32, 0x46, 0x3b, 0x27, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x4d, 0x61, 0x70, 0x2e, 0x75, 0x6e, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x69, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x28, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x4d, 0x61, 0x70, 0x2e, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x67, 0x65, 0x78, 0x65, 0x73, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x64, 0x20, 0x69, 0x6d, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x6c, 0x79, 0x20, 0x61, 0x62, 0x6f, 0x76, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x65, 0x67, 0x65, 0x78, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x3a, 0x20, 0x20, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x52, 0x65, 0x67, 0x45, 0x78, 0x70, 0x28, 0x27, 0x5b, 0x27, 0x20, 0x2b, 0x20, 0x5f, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x28, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x4d, 0x61, 0x70, 0x2e, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x29, 0x2e, 0x6a, 0x6f, 0x69, 0x6e, 0x28, 0x27, 0x27, 0x29, 0x20, 0x2b, 0x20, 0x27, 0x5d, 0x27, 0x2c, 0x20, 0x27, 0x67, 0x27, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x6e, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x3a, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x52, 0x65, 0x67, 0x45, 0x78, 0x70, 0x28, 0x27, 0x28, 0x27, 0x20, 0x2b, 0x20, 0x5f, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x28, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x4d, 0x61, 0x70, 0x2e, 0x75, 0x6e, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x29, 0x2e, 0x6a, 0x6f, 0x69, 0x6e, 0x28, 0x27, 0x7c, 0x27, 0x29, 0x20, 0x2b, 0x20, 0x27, 0x29, 0x27, 0x2c, 0x20, 0x27, 0x67, 0x27, 0x29, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x65, 0x73, 0x63, 0x61, 0x70, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x75, 0x6e, 0x65, 0x73, 0x63, 0x61, 0x70, 0x69, 0x6e, 0x67, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x73, 0x20, 0x74, 0x6f, 0x2f, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x48, 0x54, 0x4d, 0x4c, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x65, 0x61, 0x63, 0x68, 0x28, 0x5b, 0x27, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x27, 0x2c, 0x20, 0x27, 0x75, 0x6e, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x27, 0x5d, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x5b, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5d, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x27, 0x27, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x27, 0x27, 0x20, 0x2b, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x29, 0x2e, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x28, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x65, 0x67, 0x65, 0x78, 0x65, 0x73, 0x5b, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5d, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x4d, 0x61, 0x70, 0x5b, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5d, 0x5b, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x64, 0x20, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x69, 0x6e, 0x76, 0x6f, 0x6b, 0x65, 0x20, 0x69, 0x74, 0x3b, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x77, 0x69, 0x73, 0x65, 0x2c, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x69, 0x74, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2c, 0x20, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x3d, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5b, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x20, 0x3f, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x29, 0x20, 0x3a, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, 0x64, 0x64, 0x20, 0x79, 0x6f, 0x75, 0x72, 0x20, 0x6f, 0x77, 0x6e, 0x20, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x55, 0x6e, 0x64, 0x65, 0x72, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x6d, 0x69, 0x78, 0x69, 0x6e, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x61, 0x63, 0x68, 0x28, 0x5f, 0x2e, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x20, 0x3d, 0x20, 0x5f, 0x5b, 0x6e, 0x61, 0x6d, 0x65, 0x5d, 0x20, 0x3d, 0x20, 0x6f, 0x62, 0x6a, 0x5b, 0x6e, 0x61, 0x6d, 0x65, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x74, 0x79, 0x70, 0x65, 0x5b, 0x6e, 0x61, 0x6d, 0x65, 0x5d, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x61, 0x72, 0x67, 0x73, 0x20, 0x3d, 0x20, 0x5b, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x5f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x75, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x61, 0x72, 0x67, 0x73, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x5f, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x73, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x20, 0x69, 0x64, 0x20, 0x28, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x20, 0x77, 0x69, 0x74, 0x68, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x65, 0x6e, 0x74, 0x69, 0x72, 0x65, 0x20, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x20, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x29, 0x2e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x55, 0x73, 0x65, 0x66, 0x75, 0x6c, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x72, 0x79, 0x20, 0x44, 0x4f, 0x4d, 0x20, 0x69, 0x64, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x69, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x49, 0x64, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x69, 0x64, 0x20, 0x3d, 0x20, 0x2b, 0x2b, 0x69, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x20, 0x2b, 0x20, 0x27, 0x27, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x20, 0x3f, 0x20, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x20, 0x2b, 0x20, 0x69, 0x64, 0x20, 0x3a, 0x20, 0x69, 0x64, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x42, 0x79, 0x20, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x20, 0x55, 0x6e, 0x64, 0x65, 0x72, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x20, 0x75, 0x73, 0x65, 0x73, 0x20, 0x45, 0x52, 0x42, 0x2d, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x20, 0x64, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x73, 0x2c, 0x20, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x66, 0x6f, 0x6c, 0x6c, 0x6f, 0x77, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x20, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x75, 0x73, 0x65, 0x20, 0x61, 0x6c, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x64, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x20, 0x3d, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x20, 0x20, 0x20, 0x20, 0x3a, 0x20, 0x2f, 0x3c, 0x25, 0x28, 0x5b, 0x5c, 0x73, 0x5c, 0x53, 0x5d, 0x2b, 0x3f, 0x29, 0x25, 0x3e, 0x2f, 0x67, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x6f, 0x6c, 0x61, 0x74, 0x65, 0x20, 0x3a, 0x20, 0x2f, 0x3c, 0x25, 0x3d, 0x28, 0x5b, 0x5c, 0x73, 0x5c, 0x53, 0x5d, 0x2b, 0x3f, 0x29, 0x25, 0x3e, 0x2f, 0x67, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3a, 0x20, 0x2f, 0x3c, 0x25, 0x2d, 0x28, 0x5b, 0x5c, 0x73, 0x5c, 0x53, 0x5d, 0x2b, 0x3f, 0x29, 0x25, 0x3e, 0x2f, 0x67, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x57, 0x68, 0x65, 0x6e, 0x20, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x69, 0x7a, 0x69, 0x6e, 0x67, 0x20, 0x60, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x60, 0x2c, 0x20, 0x69, 0x66, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x64, 0x6f, 0x6e, 0x27, 0x74, 0x20, 0x77, 0x61, 0x6e, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x61, 0x6e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x72, 0x20, 0x65, 0x73, 0x63, 0x61, 0x70, 0x69, 0x6e, 0x67, 0x20, 0x72, 0x65, 0x67, 0x65, 0x78, 0x2c, 0x20, 0x77, 0x65, 0x20, 0x6e, 0x65, 0x65, 0x64, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x69, 0x73, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x67, 0x75, 0x61, 0x72, 0x61, 0x6e, 0x74, 0x65, 0x65, 0x64, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x0a, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6e, 0x6f, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x20, 0x3d, 0x20, 0x2f, 0x28, 0x2e, 0x29, 0x5e, 0x2f, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x65, 0x72, 0x74, 0x61, 0x69, 0x6e, 0x20, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x20, 0x6e, 0x65, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x64, 0x20, 0x73, 0x6f, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x74, 0x68, 0x65, 0x79, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x62, 0x65, 0x20, 0x70, 0x75, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x20, 0x61, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x6c, 0x69, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x2e, 0x0a, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x27, 0x22, 0x3a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x27, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x27, 0x5c, 0x5c, 0x27, 0x3a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x27, 0x5c, 0x5c, 0x27, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x27, 0x5c, 0x72, 0x27, 0x3a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x27, 0x72, 0x27, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x27, 0x5c, 0x6e, 0x27, 0x3a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x27, 0x6e, 0x27, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x27, 0x5c, 0x74, 0x27, 0x3a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x27, 0x74, 0x27, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x27, 0x5c, 0x75, 0x32, 0x30, 0x32, 0x38, 0x27, 0x3a, 0x20, 0x27, 0x75, 0x32, 0x30, 0x32, 0x38, 0x27, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x27, 0x5c, 0x75, 0x32, 0x30, 0x32, 0x39, 0x27, 0x3a, 0x20, 0x27, 0x75, 0x32, 0x30, 0x32, 0x39, 0x27, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x72, 0x20, 0x3d, 0x20, 0x2f, 0x5c, 0x5c, 0x7c, 0x27, 0x7c, 0x5c, 0x72, 0x7c, 0x5c, 0x6e, 0x7c, 0x5c, 0x74, 0x7c, 0x5c, 0x75, 0x32, 0x30, 0x32, 0x38, 0x7c, 0x5c, 0x75, 0x32, 0x30, 0x32, 0x39, 0x2f, 0x67, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x4a, 0x61, 0x76, 0x61, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x2d, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x2c, 0x20, 0x73, 0x69, 0x6d, 0x69, 0x6c, 0x61, 0x72, 0x20, 0x74, 0x6f, 0x20, 0x4a, 0x6f, 0x68, 0x6e, 0x20, 0x52, 0x65, 0x73, 0x69, 0x67, 0x27, 0x73, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x55, 0x6e, 0x64, 0x65, 0x72, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x20, 0x61, 0x72, 0x62, 0x69, 0x74, 0x72, 0x61, 0x72, 0x79, 0x20, 0x64, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x73, 0x2c, 0x20, 0x70, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x73, 0x20, 0x77, 0x68, 0x69, 0x74, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2c, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x63, 0x74, 0x6c, 0x79, 0x20, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x73, 0x20, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x73, 0x20, 0x77, 0x69, 0x74, 0x68, 0x69, 0x6e, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x6f, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x28, 0x7b, 0x7d, 0x2c, 0x20, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2c, 0x20, 0x5f, 0x2e, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x6f, 0x6d, 0x62, 0x69, 0x6e, 0x65, 0x20, 0x64, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x73, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x72, 0x65, 0x67, 0x75, 0x6c, 0x61, 0x72, 0x20, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x76, 0x69, 0x61, 0x20, 0x61, 0x6c, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x20, 0x3d, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x52, 0x65, 0x67, 0x45, 0x78, 0x70, 0x28, 0x5b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x20, 0x7c, 0x7c, 0x20, 0x6e, 0x6f, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x29, 0x2e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x6f, 0x6c, 0x61, 0x74, 0x65, 0x20, 0x7c, 0x7c, 0x20, 0x6e, 0x6f, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x29, 0x2e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x20, 0x7c, 0x7c, 0x20, 0x6e, 0x6f, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x29, 0x2e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5d, 0x2e, 0x6a, 0x6f, 0x69, 0x6e, 0x28, 0x27, 0x7c, 0x27, 0x29, 0x20, 0x2b, 0x20, 0x27, 0x7c, 0x24, 0x27, 0x2c, 0x20, 0x27, 0x67, 0x27, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2c, 0x20, 0x65, 0x73, 0x63, 0x61, 0x70, 0x69, 0x6e, 0x67, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x6c, 0x69, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x73, 0x20, 0x61, 0x70, 0x70, 0x72, 0x6f, 0x70, 0x72, 0x69, 0x61, 0x74, 0x65, 0x6c, 0x79, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x3d, 0x20, 0x22, 0x5f, 0x5f, 0x70, 0x2b, 0x3d, 0x27, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x78, 0x74, 0x2e, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x28, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x2c, 0x20, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x6f, 0x6c, 0x61, 0x74, 0x65, 0x2c, 0x20, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x2c, 0x20, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x2b, 0x3d, 0x20, 0x74, 0x65, 0x78, 0x74, 0x2e, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x28, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2e, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x28, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x72, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x29, 0x20, 0x7b, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x27, 0x5c, 0x5c, 0x27, 0x20, 0x2b, 0x20, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x73, 0x5b, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x5d, 0x3b, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x2b, 0x3d, 0x20, 0x22, 0x27, 0x2b, 0x5c, 0x6e, 0x28, 0x28, 0x5f, 0x5f, 0x74, 0x3d, 0x28, 0x22, 0x20, 0x2b, 0x20, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x20, 0x2b, 0x20, 0x22, 0x29, 0x29, 0x3d, 0x3d, 0x6e, 0x75, 0x6c, 0x6c, 0x3f, 0x27, 0x27, 0x3a, 0x5f, 0x2e, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x28, 0x5f, 0x5f, 0x74, 0x29, 0x29, 0x2b, 0x5c, 0x6e, 0x27, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x6f, 0x6c, 0x61, 0x74, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x2b, 0x3d, 0x20, 0x22, 0x27, 0x2b, 0x5c, 0x6e, 0x28, 0x28, 0x5f, 0x5f, 0x74, 0x3d, 0x28, 0x22, 0x20, 0x2b, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x6f, 0x6c, 0x61, 0x74, 0x65, 0x20, 0x2b, 0x20, 0x22, 0x29, 0x29, 0x3d, 0x3d, 0x6e, 0x75, 0x6c, 0x6c, 0x3f, 0x27, 0x27, 0x3a, 0x5f, 0x5f, 0x74, 0x29, 0x2b, 0x5c, 0x6e, 0x27, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x2b, 0x3d, 0x20, 0x22, 0x27, 0x3b, 0x5c, 0x6e, 0x22, 0x20, 0x2b, 0x20, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x20, 0x2b, 0x20, 0x22, 0x5c, 0x6e, 0x5f, 0x5f, 0x70, 0x2b, 0x3d, 0x27, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x20, 0x2b, 0x20, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x2b, 0x3d, 0x20, 0x22, 0x27, 0x3b, 0x5c, 0x6e, 0x22, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x66, 0x20, 0x61, 0x20, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x69, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2c, 0x20, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x29, 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x3d, 0x20, 0x27, 0x77, 0x69, 0x74, 0x68, 0x28, 0x6f, 0x62, 0x6a, 0x7c, 0x7c, 0x7b, 0x7d, 0x29, 0x7b, 0x5c, 0x6e, 0x27, 0x20, 0x2b, 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x2b, 0x20, 0x27, 0x7d, 0x5c, 0x6e, 0x27, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x3d, 0x20, 0x22, 0x76, 0x61, 0x72, 0x20, 0x5f, 0x5f, 0x74, 0x2c, 0x5f, 0x5f, 0x70, 0x3d, 0x27, 0x27, 0x2c, 0x5f, 0x5f, 0x6a, 0x3d, 0x41, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x6a, 0x6f, 0x69, 0x6e, 0x2c, 0x22, 0x20, 0x2b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x3d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x5f, 0x5f, 0x70, 0x2b, 0x3d, 0x5f, 0x5f, 0x6a, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x27, 0x27, 0x29, 0x3b, 0x7d, 0x3b, 0x5c, 0x6e, 0x22, 0x20, 0x2b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x2b, 0x20, 0x22, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x5f, 0x70, 0x3b, 0x5c, 0x6e, 0x22, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x72, 0x79, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x20, 0x3d, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x7c, 0x7c, 0x20, 0x27, 0x6f, 0x62, 0x6a, 0x27, 0x2c, 0x20, 0x27, 0x5f, 0x27, 0x2c, 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x20, 0x63, 0x61, 0x74, 0x63, 0x68, 0x20, 0x28, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x2e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x3d, 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x72, 0x6f, 0x77, 0x20, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x64, 0x61, 0x74, 0x61, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x28, 0x64, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x5f, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x64, 0x61, 0x74, 0x61, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2c, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x5f, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x64, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x61, 0x73, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x6e, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x70, 0x72, 0x65, 0x63, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x2e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x3d, 0x20, 0x27, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x27, 0x20, 0x2b, 0x20, 0x28, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x7c, 0x7c, 0x20, 0x27, 0x6f, 0x62, 0x6a, 0x27, 0x29, 0x20, 0x2b, 0x20, 0x27, 0x29, 0x7b, 0x5c, 0x6e, 0x27, 0x20, 0x2b, 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x2b, 0x20, 0x27, 0x7d, 0x27, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, 0x64, 0x64, 0x20, 0x61, 0x20, 0x22, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x22, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x64, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x2e, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x28, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x4f, 0x4f, 0x50, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x66, 0x20, 0x55, 0x6e, 0x64, 0x65, 0x72, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x20, 0x69, 0x73, 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x20, 0x61, 0x73, 0x20, 0x61, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x61, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x74, 0x68, 0x61, 0x74, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x62, 0x65, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x4f, 0x4f, 0x2d, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x2e, 0x20, 0x54, 0x68, 0x69, 0x73, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x20, 0x68, 0x6f, 0x6c, 0x64, 0x73, 0x20, 0x61, 0x6c, 0x74, 0x65, 0x72, 0x65, 0x64, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x20, 0x57, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x20, 0x6d, 0x61, 0x79, 0x20, 0x62, 0x65, 0x20, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x48, 0x65, 0x6c, 0x70, 0x65, 0x72, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x6f, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x65, 0x20, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x20, 0x3f, 0x20, 0x5f, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x2e, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x28, 0x29, 0x20, 0x3a, 0x20, 0x6f, 0x62, 0x6a, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, 0x64, 0x64, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x55, 0x6e, 0x64, 0x65, 0x72, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x6d, 0x69, 0x78, 0x69, 0x6e, 0x28, 0x5f, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, 0x64, 0x64, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x6d, 0x75, 0x74, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x2e, 0x0a, 0x20, 0x20, 0x65, 0x61, 0x63, 0x68, 0x28, 0x5b, 0x27, 0x70, 0x6f, 0x70, 0x27, 0x2c, 0x20, 0x27, 0x70, 0x75, 0x73, 0x68, 0x27, 0x2c, 0x20, 0x27, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x27, 0x2c, 0x20, 0x27, 0x73, 0x68, 0x69, 0x66, 0x74, 0x27, 0x2c, 0x20, 0x27, 0x73, 0x6f, 0x72, 0x74, 0x27, 0x2c, 0x20, 0x27, 0x73, 0x70, 0x6c, 0x69, 0x63, 0x65, 0x27, 0x2c, 0x20, 0x27, 0x75, 0x6e, 0x73, 0x68, 0x69, 0x66, 0x74, 0x27, 0x5d, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x20, 0x3d, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x5b, 0x6e, 0x61, 0x6d, 0x65, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x74, 0x79, 0x70, 0x65, 0x5b, 0x6e, 0x61, 0x6d, 0x65, 0x5d, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6f, 0x62, 0x6a, 0x20, 0x3d, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x5f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x28, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x27, 0x73, 0x68, 0x69, 0x66, 0x74, 0x27, 0x20, 0x7c, 0x7c, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x27, 0x73, 0x70, 0x6c, 0x69, 0x63, 0x65, 0x27, 0x29, 0x20, 0x26, 0x26, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x20, 0x6f, 0x62, 0x6a, 0x5b, 0x30, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2c, 0x20, 0x6f, 0x62, 0x6a, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, 0x64, 0x64, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x2e, 0x0a, 0x20, 0x20, 0x65, 0x61, 0x63, 0x68, 0x28, 0x5b, 0x27, 0x63, 0x6f, 0x6e, 0x63, 0x61, 0x74, 0x27, 0x2c, 0x20, 0x27, 0x6a, 0x6f, 0x69, 0x6e, 0x27, 0x2c, 0x20, 0x27, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x27, 0x5d, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x20, 0x3d, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x5b, 0x6e, 0x61, 0x6d, 0x65, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x74, 0x79, 0x70, 0x65, 0x5b, 0x6e, 0x61, 0x6d, 0x65, 0x5d, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2c, 0x20, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x5f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x28, 0x5f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x74, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x7b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x53, 0x74, 0x61, 0x72, 0x74, 0x20, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x20, 0x55, 0x6e, 0x64, 0x65, 0x72, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x3a, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x68, 0x69, 0x73, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x2c, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x45, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x61, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x5f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x0a, 0x7d, 0x29, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x74, 0x68, 0x69, 0x73, 0x29, 0x3b, 0x0a, } } // Underscore.js 1.4.4 // http://underscorejs.org // (c) 2009-2013 Jeremy Ashkenas, DocumentCloud Inc. // Underscore may be freely distributed under the MIT license. golang-github-robertkrimen-otto-0.0~git20180617.15f95af/underscore/testify000077500000000000000000000031041331145703200261610ustar00rootroot00000000000000#!/usr/bin/env perl use strict; use warnings; my $underscore_test = shift @ARGV || ""; if (!-d $underscore_test) { print <<_END_; Usage: testify ./underscore/test # Should look something like: arrays.js chaining.js collections.js functions.js index.html objects.js speed.js utility.js vendor _END_ if ($underscore_test) { die "!: Not a directory: $underscore_test\n" } exit; } chdir $underscore_test or die "!: $!"; my @js = <*.js>; for my $file (@js) { open my $fh, '<', $file or die "!: $!"; my $tests = join "", <$fh>; my @tests = $tests =~ m/ ^(\s{2}test\(.*? ^\s{2}}\);)$ /mgxs; close $fh; next unless @tests; print "$file: ", scalar(@tests), "\n"; my $underscore_name = "underscore_$file"; $underscore_name =~ s/.js$//; my $go_file = "${underscore_name}_test.go"; $go_file =~ s/.js$/.go/; open $fh, '>', $go_file or die "!: $!"; $fh->print(<<_END_); package otto import ( "testing" ) _END_ my $count = 0; for my $test (@tests) { $test =~ s/`([^`]+)`/<$1>/g; my ($name) = $test =~ m/^\s*test\(['"]([^'"]+)['"]/; $fh->print(<<_END_); // $name func Test_${underscore_name}_$count(t *testing.T) { tt(t, func(){ test := underscoreTest() test(` $test `) }) } _END_ $count++; } } # test('#779 - delimeters are applied to unescaped text.', 1, function() { # var template = _.template('<<\nx\n>>', null, {evaluate: /<<(.*?)>>/g}); # strictEqual(template(), '<<\nx\n>>'); # }); golang-github-robertkrimen-otto-0.0~git20180617.15f95af/underscore/underscore.go000066400000000000000000000016441331145703200272530ustar00rootroot00000000000000/* Package underscore contains the source for the JavaScript utility-belt library. import ( _ "github.com/robertkrimen/otto/underscore" ) // Every Otto runtime will now include underscore http://underscorejs.org https://github.com/documentcloud/underscore By importing this package, you'll automatically load underscore every time you create a new Otto runtime. To prevent this behavior, you can do the following: import ( "github.com/robertkrimen/otto/underscore" ) func init() { underscore.Disable() } */ package underscore import ( "github.com/robertkrimen/otto/registry" ) var entry *registry.Entry = registry.Register(func() string { return Source() }) // Enable underscore runtime inclusion. func Enable() { entry.Enable() } // Disable underscore runtime inclusion. func Disable() { entry.Disable() } // Source returns the underscore source. func Source() string { return string(underscore()) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/underscore_arrays_test.go000066400000000000000000000272611331145703200275250ustar00rootroot00000000000000package otto import ( "testing" ) // first func Test_underscore_arrays_0(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("first", function() { equal(_.first([1,2,3]), 1, 'can pull out the first element of an array'); equal(_([1, 2, 3]).first(), 1, 'can perform OO-style "first()"'); equal(_.first([1,2,3], 0).join(', '), "", 'can pass an index to first'); equal(_.first([1,2,3], 2).join(', '), '1, 2', 'can pass an index to first'); equal(_.first([1,2,3], 5).join(', '), '1, 2, 3', 'can pass an index to first'); var result = (function(){ return _.first(arguments); })(4, 3, 2, 1); equal(result, 4, 'works on an arguments object.'); result = _.map([[1,2,3],[1,2,3]], _.first); equal(result.join(','), '1,1', 'works well with _.map'); result = (function() { return _.take([1,2,3], 2); })(); equal(result.join(','), '1,2', 'aliased as take'); equal(_.first(null), undefined, 'handles nulls'); }); `) }) } // rest func Test_underscore_arrays_1(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("rest", function() { var numbers = [1, 2, 3, 4]; equal(_.rest(numbers).join(", "), "2, 3, 4", 'working rest()'); equal(_.rest(numbers, 0).join(", "), "1, 2, 3, 4", 'working rest(0)'); equal(_.rest(numbers, 2).join(', '), '3, 4', 'rest can take an index'); var result = (function(){ return _(arguments).tail(); })(1, 2, 3, 4); equal(result.join(', '), '2, 3, 4', 'aliased as tail and works on arguments object'); result = _.map([[1,2,3],[1,2,3]], _.rest); equal(_.flatten(result).join(','), '2,3,2,3', 'works well with _.map'); result = (function(){ return _(arguments).drop(); })(1, 2, 3, 4); equal(result.join(', '), '2, 3, 4', 'aliased as drop and works on arguments object'); }); `) }) } // initial func Test_underscore_arrays_2(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("initial", function() { equal(_.initial([1,2,3,4,5]).join(", "), "1, 2, 3, 4", 'working initial()'); equal(_.initial([1,2,3,4],2).join(", "), "1, 2", 'initial can take an index'); var result = (function(){ return _(arguments).initial(); })(1, 2, 3, 4); equal(result.join(", "), "1, 2, 3", 'initial works on arguments object'); result = _.map([[1,2,3],[1,2,3]], _.initial); equal(_.flatten(result).join(','), '1,2,1,2', 'initial works with _.map'); }); `) }) } // last func Test_underscore_arrays_3(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("last", function() { equal(_.last([1,2,3]), 3, 'can pull out the last element of an array'); equal(_.last([1,2,3], 0).join(', '), "", 'can pass an index to last'); equal(_.last([1,2,3], 2).join(', '), '2, 3', 'can pass an index to last'); equal(_.last([1,2,3], 5).join(', '), '1, 2, 3', 'can pass an index to last'); var result = (function(){ return _(arguments).last(); })(1, 2, 3, 4); equal(result, 4, 'works on an arguments object'); result = _.map([[1,2,3],[1,2,3]], _.last); equal(result.join(','), '3,3', 'works well with _.map'); equal(_.last(null), undefined, 'handles nulls'); }); `) }) } // compact func Test_underscore_arrays_4(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("compact", function() { equal(_.compact([0, 1, false, 2, false, 3]).length, 3, 'can trim out all falsy values'); var result = (function(){ return _.compact(arguments).length; })(0, 1, false, 2, false, 3); equal(result, 3, 'works on an arguments object'); }); `) }) } // flatten func Test_underscore_arrays_5(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("flatten", function() { var list = [1, [2], [3, [[[4]]]]]; deepEqual(_.flatten(list), [1,2,3,4], 'can flatten nested arrays'); deepEqual(_.flatten(list, true), [1,2,3,[[[4]]]], 'can shallowly flatten nested arrays'); var result = (function(){ return _.flatten(arguments); })(1, [2], [3, [[[4]]]]); deepEqual(result, [1,2,3,4], 'works on an arguments object'); }); `) }) } // without func Test_underscore_arrays_6(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("without", function() { var list = [1, 2, 1, 0, 3, 1, 4]; equal(_.without(list, 0, 1).join(', '), '2, 3, 4', 'can remove all instances of an object'); var result = (function(){ return _.without(arguments, 0, 1); })(1, 2, 1, 0, 3, 1, 4); equal(result.join(', '), '2, 3, 4', 'works on an arguments object'); var list = [{one : 1}, {two : 2}]; ok(_.without(list, {one : 1}).length == 2, 'uses real object identity for comparisons.'); ok(_.without(list, list[0]).length == 1, 'ditto.'); }); `) }) } // uniq func Test_underscore_arrays_7(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("uniq", function() { var list = [1, 2, 1, 3, 1, 4]; equal(_.uniq(list).join(', '), '1, 2, 3, 4', 'can find the unique values of an unsorted array'); var list = [1, 1, 1, 2, 2, 3]; equal(_.uniq(list, true).join(', '), '1, 2, 3', 'can find the unique values of a sorted array faster'); var list = [{name:'moe'}, {name:'curly'}, {name:'larry'}, {name:'curly'}]; var iterator = function(value) { return value.name; }; equal(_.map(_.uniq(list, false, iterator), iterator).join(', '), 'moe, curly, larry', 'can find the unique values of an array using a custom iterator'); equal(_.map(_.uniq(list, iterator), iterator).join(', '), 'moe, curly, larry', 'can find the unique values of an array using a custom iterator without specifying whether array is sorted'); var iterator = function(value) { return value +1; }; var list = [1, 2, 2, 3, 4, 4]; equal(_.uniq(list, true, iterator).join(', '), '1, 2, 3, 4', 'iterator works with sorted array'); var result = (function(){ return _.uniq(arguments); })(1, 2, 1, 3, 1, 4); equal(result.join(', '), '1, 2, 3, 4', 'works on an arguments object'); }); `) }) } // intersection func Test_underscore_arrays_8(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("intersection", function() { var stooges = ['moe', 'curly', 'larry'], leaders = ['moe', 'groucho']; equal(_.intersection(stooges, leaders).join(''), 'moe', 'can take the set intersection of two arrays'); equal(_(stooges).intersection(leaders).join(''), 'moe', 'can perform an OO-style intersection'); var result = (function(){ return _.intersection(arguments, leaders); })('moe', 'curly', 'larry'); equal(result.join(''), 'moe', 'works on an arguments object'); }); `) }) } // union func Test_underscore_arrays_9(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("union", function() { var result = _.union([1, 2, 3], [2, 30, 1], [1, 40]); equal(result.join(' '), '1 2 3 30 40', 'takes the union of a list of arrays'); var result = _.union([1, 2, 3], [2, 30, 1], [1, 40, [1]]); equal(result.join(' '), '1 2 3 30 40 1', 'takes the union of a list of nested arrays'); }); `) }) } // difference func Test_underscore_arrays_10(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("difference", function() { var result = _.difference([1, 2, 3], [2, 30, 40]); equal(result.join(' '), '1 3', 'takes the difference of two arrays'); var result = _.difference([1, 2, 3, 4], [2, 30, 40], [1, 11, 111]); equal(result.join(' '), '3 4', 'takes the difference of three arrays'); }); `) }) } // zip func Test_underscore_arrays_11(t *testing.T) { tt(t, func() { test, _ := test_() test(` test('zip', function() { var names = ['moe', 'larry', 'curly'], ages = [30, 40, 50], leaders = [true]; var stooges = _.zip(names, ages, leaders); equal(String(stooges), 'moe,30,true,larry,40,,curly,50,', 'zipped together arrays of different lengths'); }); `) }) } // object func Test_underscore_arrays_12(t *testing.T) { tt(t, func() { test, _ := test_() test(` test('object', function() { var result = _.object(['moe', 'larry', 'curly'], [30, 40, 50]); var shouldBe = {moe: 30, larry: 40, curly: 50}; ok(_.isEqual(result, shouldBe), 'two arrays zipped together into an object'); result = _.object([['one', 1], ['two', 2], ['three', 3]]); shouldBe = {one: 1, two: 2, three: 3}; ok(_.isEqual(result, shouldBe), 'an array of pairs zipped together into an object'); var stooges = {moe: 30, larry: 40, curly: 50}; ok(_.isEqual(_.object(_.pairs(stooges)), stooges), 'an object converted to pairs and back to an object'); ok(_.isEqual(_.object(null), {}), 'handles nulls'); }); `) }) } // indexOf func Test_underscore_arrays_13(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("indexOf", function() { var numbers = [1, 2, 3]; numbers.indexOf = null; equal(_.indexOf(numbers, 2), 1, 'can compute indexOf, even without the native function'); var result = (function(){ return _.indexOf(arguments, 2); })(1, 2, 3); equal(result, 1, 'works on an arguments object'); equal(_.indexOf(null, 2), -1, 'handles nulls properly'); var numbers = [10, 20, 30, 40, 50], num = 35; var index = _.indexOf(numbers, num, true); equal(index, -1, '35 is not in the list'); numbers = [10, 20, 30, 40, 50]; num = 40; index = _.indexOf(numbers, num, true); equal(index, 3, '40 is in the list'); numbers = [1, 40, 40, 40, 40, 40, 40, 40, 50, 60, 70]; num = 40; index = _.indexOf(numbers, num, true); equal(index, 1, '40 is in the list'); numbers = [1, 2, 3, 1, 2, 3, 1, 2, 3]; index = _.indexOf(numbers, 2, 5); equal(index, 7, 'supports the fromIndex argument'); }); `) }) } // lastIndexOf func Test_underscore_arrays_14(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("lastIndexOf", function() { var numbers = [1, 0, 1]; equal(_.lastIndexOf(numbers, 1), 2); numbers = [1, 0, 1, 0, 0, 1, 0, 0, 0]; numbers.lastIndexOf = null; equal(_.lastIndexOf(numbers, 1), 5, 'can compute lastIndexOf, even without the native function'); equal(_.lastIndexOf(numbers, 0), 8, 'lastIndexOf the other element'); var result = (function(){ return _.lastIndexOf(arguments, 1); })(1, 0, 1, 0, 0, 1, 0, 0, 0); equal(result, 5, 'works on an arguments object'); equal(_.indexOf(null, 2), -1, 'handles nulls properly'); numbers = [1, 2, 3, 1, 2, 3, 1, 2, 3]; var index = _.lastIndexOf(numbers, 2, 2); equal(index, 1, 'supports the fromIndex argument'); }); `) }) } // range func Test_underscore_arrays_15(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("range", function() { equal(_.range(0).join(''), '', 'range with 0 as a first argument generates an empty array'); equal(_.range(4).join(' '), '0 1 2 3', 'range with a single positive argument generates an array of elements 0,1,2,...,n-1'); equal(_.range(5, 8).join(' '), '5 6 7', 'range with two arguments a & b, a<b generates an array of elements a,a+1,a+2,...,b-2,b-1'); equal(_.range(8, 5).join(''), '', 'range with two arguments a & b, b<a generates an empty array'); equal(_.range(3, 10, 3).join(' '), '3 6 9', 'range with three arguments a & b & c, c < b-a, a < b generates an array of elements a,a+c,a+2c,...,b - (multiplier of a) < c'); equal(_.range(3, 10, 15).join(''), '3', 'range with three arguments a & b & c, c > b-a, a < b generates an array with a single element, equal to a'); equal(_.range(12, 7, -2).join(' '), '12 10 8', 'range with three arguments a & b & c, a > b, c < 0 generates an array of elements a,a-c,a-2c and ends with the number not less than b'); equal(_.range(0, -10, -1).join(' '), '0 -1 -2 -3 -4 -5 -6 -7 -8 -9', 'final example in the Python docs'); }); `) }) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/underscore_chaining_test.go000066400000000000000000000043531331145703200300010ustar00rootroot00000000000000package otto import ( "testing" ) // map/flatten/reduce func Test_underscore_chaining_0(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("map/flatten/reduce", function() { var lyrics = [ "I'm a lumberjack and I'm okay", "I sleep all night and I work all day", "He's a lumberjack and he's okay", "He sleeps all night and he works all day" ]; var counts = _(lyrics).chain() .map(function(line) { return line.split(''); }) .flatten() .reduce(function(hash, l) { hash[l] = hash[l] || 0; hash[l]++; return hash; }, {}).value(); ok(counts['a'] == 16 && counts['e'] == 10, 'counted all the letters in the song'); }); `) }) } // select/reject/sortBy func Test_underscore_chaining_1(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("select/reject/sortBy", function() { var numbers = [1,2,3,4,5,6,7,8,9,10]; numbers = _(numbers).chain().select(function(n) { return n % 2 == 0; }).reject(function(n) { return n % 4 == 0; }).sortBy(function(n) { return -n; }).value(); equal(numbers.join(', '), "10, 6, 2", "filtered and reversed the numbers"); }); `) }) } // select/reject/sortBy in functional style func Test_underscore_chaining_2(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("select/reject/sortBy in functional style", function() { var numbers = [1,2,3,4,5,6,7,8,9,10]; numbers = _.chain(numbers).select(function(n) { return n % 2 == 0; }).reject(function(n) { return n % 4 == 0; }).sortBy(function(n) { return -n; }).value(); equal(numbers.join(', '), "10, 6, 2", "filtered and reversed the numbers"); }); `) }) } // reverse/concat/unshift/pop/map func Test_underscore_chaining_3(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("reverse/concat/unshift/pop/map", function() { var numbers = [1,2,3,4,5]; numbers = _(numbers).chain() .reverse() .concat([5, 5, 5]) .unshift(17) .pop() .map(function(n){ return n * 2; }) .value(); equal(numbers.join(', '), "34, 10, 8, 6, 4, 2, 10, 10", 'can chain together array functions.'); }); `) }) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/underscore_collections_test.go000066400000000000000000000500411331145703200305320ustar00rootroot00000000000000package otto import ( "testing" ) // each func Test_underscore_collections_0(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("each", function() { _.each([1, 2, 3], function(num, i) { equal(num, i + 1, 'each iterators provide value and iteration count'); }); var answers = []; _.each([1, 2, 3], function(num){ answers.push(num * this.multiplier);}, {multiplier : 5}); equal(answers.join(', '), '5, 10, 15', 'context object property accessed'); answers = []; _.forEach([1, 2, 3], function(num){ answers.push(num); }); equal(answers.join(', '), '1, 2, 3', 'aliased as "forEach"'); answers = []; var obj = {one : 1, two : 2, three : 3}; obj.constructor.prototype.four = 4; _.each(obj, function(value, key){ answers.push(key); }); equal(answers.join(", "), 'one, two, three', 'iterating over objects works, and ignores the object prototype.'); delete obj.constructor.prototype.four; var answer = null; _.each([1, 2, 3], function(num, index, arr){ if (_.include(arr, num)) answer = true; }); ok(answer, 'can reference the original collection from inside the iterator'); answers = 0; _.each(null, function(){ ++answers; }); equal(answers, 0, 'handles a null properly'); }); `) }) } // map func Test_underscore_collections_1(t *testing.T) { tt(t, func() { test, _ := test_() test(` test('map', function() { var doubled = _.map([1, 2, 3], function(num){ return num * 2; }); equal(doubled.join(', '), '2, 4, 6', 'doubled numbers'); doubled = _.collect([1, 2, 3], function(num){ return num * 2; }); equal(doubled.join(', '), '2, 4, 6', 'aliased as "collect"'); var tripled = _.map([1, 2, 3], function(num){ return num * this.multiplier; }, {multiplier : 3}); equal(tripled.join(', '), '3, 6, 9', 'tripled numbers with context'); var doubled = _([1, 2, 3]).map(function(num){ return num * 2; }); equal(doubled.join(', '), '2, 4, 6', 'OO-style doubled numbers'); // TEST: ReferenceError: document is not defined return; if (document.querySelectorAll) { var ids = _.map(document.querySelectorAll('#map-test *'), function(n){ return n.id; }); deepEqual(ids, ['id1', 'id2'], 'Can use collection methods on NodeLists.'); } var ids = _.map($('#map-test').children(), function(n){ return n.id; }); deepEqual(ids, ['id1', 'id2'], 'Can use collection methods on jQuery Array-likes.'); var ids = _.map(document.images, function(n){ return n.id; }); ok(ids[0] == 'chart_image', 'can use collection methods on HTMLCollections'); var ifnull = _.map(null, function(){}); ok(_.isArray(ifnull) && ifnull.length === 0, 'handles a null properly'); }); `) }) } // reduce func Test_underscore_collections_2(t *testing.T) { tt(t, func() { test, _ := test_() test(` test('reduce', function() { var sum = _.reduce([1, 2, 3], function(sum, num){ return sum + num; }, 0); equal(sum, 6, 'can sum up an array'); var context = {multiplier : 3}; sum = _.reduce([1, 2, 3], function(sum, num){ return sum + num * this.multiplier; }, 0, context); equal(sum, 18, 'can reduce with a context object'); sum = _.inject([1, 2, 3], function(sum, num){ return sum + num; }, 0); equal(sum, 6, 'aliased as "inject"'); sum = _([1, 2, 3]).reduce(function(sum, num){ return sum + num; }, 0); equal(sum, 6, 'OO-style reduce'); var sum = _.reduce([1, 2, 3], function(sum, num){ return sum + num; }); equal(sum, 6, 'default initial value'); var ifnull; try { _.reduce(null, function(){}); } catch (ex) { ifnull = ex; } ok(ifnull instanceof TypeError, 'handles a null (without inital value) properly'); ok(_.reduce(null, function(){}, 138) === 138, 'handles a null (with initial value) properly'); equal(_.reduce([], function(){}, undefined), undefined, 'undefined can be passed as a special case'); raises(function() { _.reduce([], function(){}); }, TypeError, 'throws an error for empty arrays with no initial value'); }); `) }) } // reduceRight func Test_underscore_collections_3(t *testing.T) { tt(t, func() { test, _ := test_() test(` test('reduceRight', function() { var list = _.reduceRight(["foo", "bar", "baz"], function(memo, str){ return memo + str; }, ''); equal(list, 'bazbarfoo', 'can perform right folds'); var list = _.foldr(["foo", "bar", "baz"], function(memo, str){ return memo + str; }, ''); equal(list, 'bazbarfoo', 'aliased as "foldr"'); var list = _.foldr(["foo", "bar", "baz"], function(memo, str){ return memo + str; }); equal(list, 'bazbarfoo', 'default initial value'); var ifnull; try { _.reduceRight(null, function(){}); } catch (ex) { ifnull = ex; } ok(ifnull instanceof TypeError, 'handles a null (without inital value) properly'); var sum = _.reduceRight({a: 1, b: 2, c: 3}, function(sum, num){ return sum + num; }); equal(sum, 6, 'default initial value on object'); ok(_.reduceRight(null, function(){}, 138) === 138, 'handles a null (with initial value) properly'); equal(_.reduceRight([], function(){}, undefined), undefined, 'undefined can be passed as a special case'); raises(function() { _.reduceRight([], function(){}); }, TypeError, 'throws an error for empty arrays with no initial value'); // Assert that the correct arguments are being passed. var args, memo = {}, object = {a: 1, b: 2}, lastKey = _.keys(object).pop(); var expected = lastKey == 'a' ? [memo, 1, 'a', object] : [memo, 2, 'b', object]; _.reduceRight(object, function() { args || (args = _.toArray(arguments)); }, memo); deepEqual(args, expected); // And again, with numeric keys. object = {'2': 'a', '1': 'b'}; lastKey = _.keys(object).pop(); args = null; expected = lastKey == '2' ? [memo, 'a', '2', object] : [memo, 'b', '1', object]; _.reduceRight(object, function() { args || (args = _.toArray(arguments)); }, memo); deepEqual(args, expected); }); `) }) } // find func Test_underscore_collections_4(t *testing.T) { tt(t, func() { test, _ := test_() test(` test('find', function() { var array = [1, 2, 3, 4]; strictEqual(_.find(array, function(n) { return n > 2; }), 3, 'should return first found '); strictEqual(_.find(array, function() { return false; }), void 0, 'should return if is not found'); }); `) }) } // detect func Test_underscore_collections_5(t *testing.T) { tt(t, func() { test, _ := test_() test(` test('detect', function() { var result = _.detect([1, 2, 3], function(num){ return num * 2 == 4; }); equal(result, 2, 'found the first "2" and broke the loop'); }); `) }) } // select func Test_underscore_collections_6(t *testing.T) { tt(t, func() { test, _ := test_() test(` test('select', function() { var evens = _.select([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; }); equal(evens.join(', '), '2, 4, 6', 'selected each even number'); evens = _.filter([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; }); equal(evens.join(', '), '2, 4, 6', 'aliased as "filter"'); }); `) }) } // reject func Test_underscore_collections_7(t *testing.T) { tt(t, func() { test, _ := test_() test(` test('reject', function() { var odds = _.reject([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; }); equal(odds.join(', '), '1, 3, 5', 'rejected each even number'); var context = "obj"; var evens = _.reject([1, 2, 3, 4, 5, 6], function(num){ equal(context, "obj"); return num % 2 != 0; }, context); equal(evens.join(', '), '2, 4, 6', 'rejected each odd number'); }); `) }) } // all func Test_underscore_collections_8(t *testing.T) { tt(t, func() { test, _ := test_() test(` test('all', function() { ok(_.all([], _.identity), 'the empty set'); ok(_.all([true, true, true], _.identity), 'all true values'); ok(!_.all([true, false, true], _.identity), 'one false value'); ok(_.all([0, 10, 28], function(num){ return num % 2 == 0; }), 'even numbers'); ok(!_.all([0, 11, 28], function(num){ return num % 2 == 0; }), 'an odd number'); ok(_.all([1], _.identity) === true, 'cast to boolean - true'); ok(_.all([0], _.identity) === false, 'cast to boolean - false'); ok(_.every([true, true, true], _.identity), 'aliased as "every"'); ok(!_.all([undefined, undefined, undefined], _.identity), 'works with arrays of undefined'); }); `) }) } // any func Test_underscore_collections_9(t *testing.T) { tt(t, func() { test, _ := test_() test(` test('any', function() { var nativeSome = Array.prototype.some; Array.prototype.some = null; ok(!_.any([]), 'the empty set'); ok(!_.any([false, false, false]), 'all false values'); ok(_.any([false, false, true]), 'one true value'); ok(_.any([null, 0, 'yes', false]), 'a string'); ok(!_.any([null, 0, '', false]), 'falsy values'); ok(!_.any([1, 11, 29], function(num){ return num % 2 == 0; }), 'all odd numbers'); ok(_.any([1, 10, 29], function(num){ return num % 2 == 0; }), 'an even number'); ok(_.any([1], _.identity) === true, 'cast to boolean - true'); ok(_.any([0], _.identity) === false, 'cast to boolean - false'); ok(_.some([false, false, true]), 'aliased as "some"'); Array.prototype.some = nativeSome; }); `) }) } // include func Test_underscore_collections_10(t *testing.T) { tt(t, func() { test, _ := test_() test(` test('include', function() { ok(_.include([1,2,3], 2), 'two is in the array'); ok(!_.include([1,3,9], 2), 'two is not in the array'); ok(_.contains({moe:1, larry:3, curly:9}, 3) === true, '_.include on objects checks their values'); ok(_([1,2,3]).include(2), 'OO-style include'); }); `) }) } // invoke func Test_underscore_collections_11(t *testing.T) { tt(t, func() { test, _ := test_() test(` test('invoke', function() { var list = [[5, 1, 7], [3, 2, 1]]; var result = _.invoke(list, 'sort'); equal(result[0].join(', '), '1, 5, 7', 'first array sorted'); equal(result[1].join(', '), '1, 2, 3', 'second array sorted'); }); `) }) } // invoke w/ function reference func Test_underscore_collections_12(t *testing.T) { tt(t, func() { test, _ := test_() test(` test('invoke w/ function reference', function() { var list = [[5, 1, 7], [3, 2, 1]]; var result = _.invoke(list, Array.prototype.sort); equal(result[0].join(', '), '1, 5, 7', 'first array sorted'); equal(result[1].join(', '), '1, 2, 3', 'second array sorted'); }); `) }) } // invoke when strings have a call method func Test_underscore_collections_13(t *testing.T) { tt(t, func() { test, _ := test_() test(` test('invoke when strings have a call method', function() { String.prototype.call = function() { return 42; }; var list = [[5, 1, 7], [3, 2, 1]]; var s = "foo"; equal(s.call(), 42, "call function exists"); var result = _.invoke(list, 'sort'); equal(result[0].join(', '), '1, 5, 7', 'first array sorted'); equal(result[1].join(', '), '1, 2, 3', 'second array sorted'); delete String.prototype.call; equal(s.call, undefined, "call function removed"); }); `) }) } // pluck func Test_underscore_collections_14(t *testing.T) { tt(t, func() { test, _ := test_() test(` test('pluck', function() { var people = [{name : 'moe', age : 30}, {name : 'curly', age : 50}]; equal(_.pluck(people, 'name').join(', '), 'moe, curly', 'pulls names out of objects'); }); `) }) } // where func Test_underscore_collections_15(t *testing.T) { tt(t, func() { test, _ := test_() test(` test('where', function() { var list = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}]; var result = _.where(list, {a: 1}); equal(result.length, 3); equal(result[result.length - 1].b, 4); result = _.where(list, {b: 2}); equal(result.length, 2); equal(result[0].a, 1); }); `) }) } // findWhere func Test_underscore_collections_16(t *testing.T) { tt(t, func() { test, _ := test_() test(` test('findWhere', function() { var list = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}, {a: 2, b: 4}]; var result = _.findWhere(list, {a: 1}); deepEqual(result, {a: 1, b: 2}); result = _.findWhere(list, {b: 4}); deepEqual(result, {a: 1, b: 4}); }); `) }) } // max func Test_underscore_collections_17(t *testing.T) { tt(t, func() { test, _ := test_() test(` test('max', function() { equal(3, _.max([1, 2, 3]), 'can perform a regular Math.max'); var neg = _.max([1, 2, 3], function(num){ return -num; }); equal(neg, 1, 'can perform a computation-based max'); equal(-Infinity, _.max({}), 'Maximum value of an empty object'); equal(-Infinity, _.max([]), 'Maximum value of an empty array'); equal(_.max({'a': 'a'}), -Infinity, 'Maximum value of a non-numeric collection'); // TEST: Takes too long return; equal(299999, _.max(_.range(1,300000)), "Maximum value of a too-big array"); }); `) }) } // min func Test_underscore_collections_18(t *testing.T) { tt(t, func() { test, _ := test_() test(` test('min', function() { equal(1, _.min([1, 2, 3]), 'can perform a regular Math.min'); var neg = _.min([1, 2, 3], function(num){ return -num; }); equal(neg, 3, 'can perform a computation-based min'); equal(Infinity, _.min({}), 'Minimum value of an empty object'); equal(Infinity, _.min([]), 'Minimum value of an empty array'); equal(_.min({'a': 'a'}), Infinity, 'Minimum value of a non-numeric collection'); var now = new Date(9999999999); var then = new Date(0); equal(_.min([now, then]), then); // TEST: Takes too long return; equal(1, _.min(_.range(1,300000)), "Minimum value of a too-big array"); }); `) }) } // sortBy func Test_underscore_collections_19(t *testing.T) { tt(t, func() { test, _ := test_() test(` test('sortBy', function() { var people = [{name : 'curly', age : 50}, {name : 'moe', age : 30}]; people = _.sortBy(people, function(person){ return person.age; }); equal(_.pluck(people, 'name').join(', '), 'moe, curly', 'stooges sorted by age'); var list = [undefined, 4, 1, undefined, 3, 2]; equal(_.sortBy(list, _.identity).join(','), '1,2,3,4,,', 'sortBy with undefined values'); var list = ["one", "two", "three", "four", "five"]; var sorted = _.sortBy(list, 'length'); equal(sorted.join(' '), 'one two four five three', 'sorted by length'); function Pair(x, y) { this.x = x; this.y = y; } var collection = [ new Pair(1, 1), new Pair(1, 2), new Pair(1, 3), new Pair(1, 4), new Pair(1, 5), new Pair(1, 6), new Pair(2, 1), new Pair(2, 2), new Pair(2, 3), new Pair(2, 4), new Pair(2, 5), new Pair(2, 6), new Pair(undefined, 1), new Pair(undefined, 2), new Pair(undefined, 3), new Pair(undefined, 4), new Pair(undefined, 5), new Pair(undefined, 6) ]; var actual = _.sortBy(collection, function(pair) { return pair.x; }); deepEqual(actual, collection, 'sortBy should be stable'); }); `) }) } // groupBy func Test_underscore_collections_20(t *testing.T) { tt(t, func() { test, _ := test_() test(` test('groupBy', function() { var parity = _.groupBy([1, 2, 3, 4, 5, 6], function(num){ return num % 2; }); ok('0' in parity && '1' in parity, 'created a group for each value'); equal(parity[0].join(', '), '2, 4, 6', 'put each even number in the right group'); var list = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"]; var grouped = _.groupBy(list, 'length'); equal(grouped['3'].join(' '), 'one two six ten'); equal(grouped['4'].join(' '), 'four five nine'); equal(grouped['5'].join(' '), 'three seven eight'); var context = {}; _.groupBy([{}], function(){ ok(this === context); }, context); grouped = _.groupBy([4.2, 6.1, 6.4], function(num) { return Math.floor(num) > 4 ? 'hasOwnProperty' : 'constructor'; }); equal(grouped.constructor.length, 1); equal(grouped.hasOwnProperty.length, 2); var array = [{}]; _.groupBy(array, function(value, index, obj){ ok(obj === array); }); var array = [1, 2, 1, 2, 3]; var grouped = _.groupBy(array); equal(grouped['1'].length, 2); equal(grouped['3'].length, 1); }); `) }) } // countBy func Test_underscore_collections_21(t *testing.T) { tt(t, func() { test, _ := test_() test(` test('countBy', function() { var parity = _.countBy([1, 2, 3, 4, 5], function(num){ return num % 2 == 0; }); equal(parity['true'], 2); equal(parity['false'], 3); var list = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"]; var grouped = _.countBy(list, 'length'); equal(grouped['3'], 4); equal(grouped['4'], 3); equal(grouped['5'], 3); var context = {}; _.countBy([{}], function(){ ok(this === context); }, context); grouped = _.countBy([4.2, 6.1, 6.4], function(num) { return Math.floor(num) > 4 ? 'hasOwnProperty' : 'constructor'; }); equal(grouped.constructor, 1); equal(grouped.hasOwnProperty, 2); var array = [{}]; _.countBy(array, function(value, index, obj){ ok(obj === array); }); var array = [1, 2, 1, 2, 3]; var grouped = _.countBy(array); equal(grouped['1'], 2); equal(grouped['3'], 1); }); `) }) } // sortedIndex func Test_underscore_collections_22(t *testing.T) { tt(t, func() { test, _ := test_() test(` test('sortedIndex', function() { var numbers = [10, 20, 30, 40, 50], num = 35; var indexForNum = _.sortedIndex(numbers, num); equal(indexForNum, 3, '35 should be inserted at index 3'); var indexFor30 = _.sortedIndex(numbers, 30); equal(indexFor30, 2, '30 should be inserted at index 2'); var objects = [{x: 10}, {x: 20}, {x: 30}, {x: 40}]; var iterator = function(obj){ return obj.x; }; strictEqual(_.sortedIndex(objects, {x: 25}, iterator), 2); strictEqual(_.sortedIndex(objects, {x: 35}, 'x'), 3); var context = {1: 2, 2: 3, 3: 4}; iterator = function(obj){ return this[obj]; }; strictEqual(_.sortedIndex([1, 3], 2, iterator, context), 1); }); `) }) } // shuffle func Test_underscore_collections_23(t *testing.T) { tt(t, func() { test, _ := test_() test(` test('shuffle', function() { var numbers = _.range(10); var shuffled = _.shuffle(numbers).sort(); notStrictEqual(numbers, shuffled, 'original object is unmodified'); equal(shuffled.join(','), numbers.join(','), 'contains the same members before and after shuffle'); }); `) }) } // toArray func Test_underscore_collections_24(t *testing.T) { tt(t, func() { test, _ := test_() test(` test('toArray', function() { ok(!_.isArray(arguments), 'arguments object is not an array'); ok(_.isArray(_.toArray(arguments)), 'arguments object converted into array'); var a = [1,2,3]; ok(_.toArray(a) !== a, 'array is cloned'); equal(_.toArray(a).join(', '), '1, 2, 3', 'cloned array contains same elements'); var numbers = _.toArray({one : 1, two : 2, three : 3}); equal(numbers.join(', '), '1, 2, 3', 'object flattened into array'); // TEST: ReferenceError: document is not defined return; // test in IE < 9 try { var actual = _.toArray(document.childNodes); } catch(ex) { } ok(_.isArray(actual), 'should not throw converting a node list'); }); `) }) } // size func Test_underscore_collections_25(t *testing.T) { tt(t, func() { test, _ := test_() test(` test('size', function() { equal(_.size({one : 1, two : 2, three : 3}), 3, 'can compute the size of an object'); equal(_.size([1, 2, 3]), 3, 'can compute the size of an array'); var func = function() { return _.size(arguments); }; equal(func(1, 2, 3, 4), 4, 'can test the size of the arguments object'); equal(_.size('hello'), 5, 'can compute the size of a string'); equal(_.size(null), 0, 'handles nulls'); }); `) }) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/underscore_functions_test.go000066400000000000000000000141611331145703200302270ustar00rootroot00000000000000package otto import ( "testing" ) // bind func Test_underscore_functions_0(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("bind", function() { var context = {name : 'moe'}; var func = function(arg) { return "name: " + (this.name || arg); }; var bound = _.bind(func, context); equal(bound(), 'name: moe', 'can bind a function to a context'); bound = _(func).bind(context); equal(bound(), 'name: moe', 'can do OO-style binding'); bound = _.bind(func, null, 'curly'); equal(bound(), 'name: curly', 'can bind without specifying a context'); func = function(salutation, name) { return salutation + ': ' + name; }; func = _.bind(func, this, 'hello'); equal(func('moe'), 'hello: moe', 'the function was partially applied in advance'); func = _.bind(func, this, 'curly'); equal(func(), 'hello: curly', 'the function was completely applied in advance'); func = function(salutation, firstname, lastname) { return salutation + ': ' + firstname + ' ' + lastname; }; func = _.bind(func, this, 'hello', 'moe', 'curly'); equal(func(), 'hello: moe curly', 'the function was partially applied in advance and can accept multiple arguments'); func = function(context, message) { equal(this, context, message); }; _.bind(func, 0, 0, 'can bind a function to <0>')(); _.bind(func, '', '', 'can bind a function to an empty string')(); _.bind(func, false, false, 'can bind a function to ')(); // These tests are only meaningful when using a browser without a native bind function // To test this with a modern browser, set underscore's nativeBind to undefined var F = function () { return this; }; var Boundf = _.bind(F, {hello: "moe curly"}); equal(Boundf().hello, "moe curly", "When called without the new operator, it's OK to be bound to the context"); }); `) }) } // partial func Test_underscore_functions_1(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("partial", function() { var obj = {name: 'moe'}; var func = function() { return this.name + ' ' + _.toArray(arguments).join(' '); }; obj.func = _.partial(func, 'a', 'b'); equal(obj.func('c', 'd'), 'moe a b c d', 'can partially apply'); }); `) }) } // bindAll func Test_underscore_functions_2(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("bindAll", function() { var curly = {name : 'curly'}, moe = { name : 'moe', getName : function() { return 'name: ' + this.name; }, sayHi : function() { return 'hi: ' + this.name; } }; curly.getName = moe.getName; _.bindAll(moe, 'getName', 'sayHi'); curly.sayHi = moe.sayHi; equal(curly.getName(), 'name: curly', 'unbound function is bound to current object'); equal(curly.sayHi(), 'hi: moe', 'bound function is still bound to original object'); curly = {name : 'curly'}; moe = { name : 'moe', getName : function() { return 'name: ' + this.name; }, sayHi : function() { return 'hi: ' + this.name; } }; _.bindAll(moe); curly.sayHi = moe.sayHi; equal(curly.sayHi(), 'hi: moe', 'calling bindAll with no arguments binds all functions to the object'); }); `) }) } // memoize func Test_underscore_functions_3(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("memoize", function() { var fib = function(n) { return n < 2 ? n : fib(n - 1) + fib(n - 2); }; var fastFib = _.memoize(fib); equal(fib(10), 55, 'a memoized version of fibonacci produces identical results'); equal(fastFib(10), 55, 'a memoized version of fibonacci produces identical results'); var o = function(str) { return str; }; var fastO = _.memoize(o); equal(o('toString'), 'toString', 'checks hasOwnProperty'); equal(fastO('toString'), 'toString', 'checks hasOwnProperty'); }); `) }) } // once func Test_underscore_functions_4(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("once", function() { var num = 0; var increment = _.once(function(){ num++; }); increment(); increment(); equal(num, 1); }); `) }) } // wrap func Test_underscore_functions_5(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("wrap", function() { var greet = function(name){ return "hi: " + name; }; var backwards = _.wrap(greet, function(func, name){ return func(name) + ' ' + name.split('').reverse().join(''); }); equal(backwards('moe'), 'hi: moe eom', 'wrapped the saluation function'); var inner = function(){ return "Hello "; }; var obj = {name : "Moe"}; obj.hi = _.wrap(inner, function(fn){ return fn() + this.name; }); equal(obj.hi(), "Hello Moe"); var noop = function(){}; var wrapped = _.wrap(noop, function(fn){ return Array.prototype.slice.call(arguments, 0); }); var ret = wrapped(['whats', 'your'], 'vector', 'victor'); deepEqual(ret, [noop, ['whats', 'your'], 'vector', 'victor']); }); `) }) } // compose func Test_underscore_functions_6(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("compose", function() { var greet = function(name){ return "hi: " + name; }; var exclaim = function(sentence){ return sentence + '!'; }; var composed = _.compose(exclaim, greet); equal(composed('moe'), 'hi: moe!', 'can compose a function that takes another'); composed = _.compose(greet, exclaim); equal(composed('moe'), 'hi: moe!', 'in this case, the functions are also commutative'); }); `) }) } // after func Test_underscore_functions_7(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("after", function() { var testAfter = function(afterAmount, timesCalled) { var afterCalled = 0; var after = _.after(afterAmount, function() { afterCalled++; }); while (timesCalled--) after(); return afterCalled; }; equal(testAfter(5, 5), 1, "after(N) should fire after being called N times"); equal(testAfter(5, 4), 0, "after(N) should not fire unless called N times"); equal(testAfter(0, 0), 1, "after(0) should fire immediately"); }); `) }) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/underscore_objects_test.go000066400000000000000000000715331331145703200276560ustar00rootroot00000000000000package otto import ( "testing" ) // keys func Test_underscore_objects_0(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("keys", function() { equal(_.keys({one : 1, two : 2}).join(', '), 'one, two', 'can extract the keys from an object'); // the test above is not safe because it relies on for-in enumeration order var a = []; a[1] = 0; equal(_.keys(a).join(', '), '1', 'is not fooled by sparse arrays; see issue #95'); raises(function() { _.keys(null); }, TypeError, 'throws an error for values'); raises(function() { _.keys(void 0); }, TypeError, 'throws an error for values'); raises(function() { _.keys(1); }, TypeError, 'throws an error for number primitives'); raises(function() { _.keys('a'); }, TypeError, 'throws an error for string primitives'); raises(function() { _.keys(true); }, TypeError, 'throws an error for boolean primitives'); }); `) }) } // values func Test_underscore_objects_1(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("values", function() { equal(_.values({one: 1, two: 2}).join(', '), '1, 2', 'can extract the values from an object'); equal(_.values({one: 1, two: 2, length: 3}).join(', '), '1, 2, 3', '... even when one of them is "length"'); }); `) }) } // pairs func Test_underscore_objects_2(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("pairs", function() { deepEqual(_.pairs({one: 1, two: 2}), [['one', 1], ['two', 2]], 'can convert an object into pairs'); deepEqual(_.pairs({one: 1, two: 2, length: 3}), [['one', 1], ['two', 2], ['length', 3]], '... even when one of them is "length"'); }); `) }) } // invert func Test_underscore_objects_3(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("invert", function() { var obj = {first: 'Moe', second: 'Larry', third: 'Curly'}; equal(_.keys(_.invert(obj)).join(' '), 'Moe Larry Curly', 'can invert an object'); ok(_.isEqual(_.invert(_.invert(obj)), obj), 'two inverts gets you back where you started'); var obj = {length: 3}; ok(_.invert(obj)['3'] == 'length', 'can invert an object with "length"') }); `) }) } // functions func Test_underscore_objects_4(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("functions", function() { var obj = {a : 'dash', b : _.map, c : (/yo/), d : _.reduce}; ok(_.isEqual(['b', 'd'], _.functions(obj)), 'can grab the function names of any passed-in object'); var Animal = function(){}; Animal.prototype.run = function(){}; equal(_.functions(new Animal).join(''), 'run', 'also looks up functions on the prototype'); }); `) }) } // extend func Test_underscore_objects_5(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("extend", function() { var result; equal(_.extend({}, {a:'b'}).a, 'b', 'can extend an object with the attributes of another'); equal(_.extend({a:'x'}, {a:'b'}).a, 'b', 'properties in source override destination'); equal(_.extend({x:'x'}, {a:'b'}).x, 'x', 'properties not in source dont get overriden'); result = _.extend({x:'x'}, {a:'a'}, {b:'b'}); ok(_.isEqual(result, {x:'x', a:'a', b:'b'}), 'can extend from multiple source objects'); result = _.extend({x:'x'}, {a:'a', x:2}, {a:'b'}); ok(_.isEqual(result, {x:2, a:'b'}), 'extending from multiple source objects last property trumps'); result = _.extend({}, {a: void 0, b: null}); equal(_.keys(result).join(''), 'ab', 'extend does not copy undefined values'); try { result = {}; _.extend(result, null, undefined, {a:1}); } catch(ex) {} equal(result.a, 1, 'should not error on or sources'); }); `) }) } // pick func Test_underscore_objects_6(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("pick", function() { var result; result = _.pick({a:1, b:2, c:3}, 'a', 'c'); ok(_.isEqual(result, {a:1, c:3}), 'can restrict properties to those named'); result = _.pick({a:1, b:2, c:3}, ['b', 'c']); ok(_.isEqual(result, {b:2, c:3}), 'can restrict properties to those named in an array'); result = _.pick({a:1, b:2, c:3}, ['a'], 'b'); ok(_.isEqual(result, {a:1, b:2}), 'can restrict properties to those named in mixed args'); var Obj = function(){}; Obj.prototype = {a: 1, b: 2, c: 3}; ok(_.isEqual(_.pick(new Obj, 'a', 'c'), {a:1, c: 3}), 'include prototype props'); }); `) }) } // omit func Test_underscore_objects_7(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("omit", function() { var result; result = _.omit({a:1, b:2, c:3}, 'b'); ok(_.isEqual(result, {a:1, c:3}), 'can omit a single named property'); result = _.omit({a:1, b:2, c:3}, 'a', 'c'); ok(_.isEqual(result, {b:2}), 'can omit several named properties'); result = _.omit({a:1, b:2, c:3}, ['b', 'c']); ok(_.isEqual(result, {a:1}), 'can omit properties named in an array'); var Obj = function(){}; Obj.prototype = {a: 1, b: 2, c: 3}; ok(_.isEqual(_.omit(new Obj, 'b'), {a:1, c: 3}), 'include prototype props'); }); `) }) } // defaults func Test_underscore_objects_8(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("defaults", function() { var result; var options = {zero: 0, one: 1, empty: "", nan: NaN, string: "string"}; _.defaults(options, {zero: 1, one: 10, twenty: 20}); equal(options.zero, 0, 'value exists'); equal(options.one, 1, 'value exists'); equal(options.twenty, 20, 'default applied'); _.defaults(options, {empty: "full"}, {nan: "nan"}, {word: "word"}, {word: "dog"}); equal(options.empty, "", 'value exists'); ok(_.isNaN(options.nan), "NaN isn't overridden"); equal(options.word, "word", 'new value is added, first one wins'); try { options = {}; _.defaults(options, null, undefined, {a:1}); } catch(ex) {} equal(options.a, 1, 'should not error on or sources'); }); `) }) } // clone func Test_underscore_objects_9(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("clone", function() { var moe = {name : 'moe', lucky : [13, 27, 34]}; var clone = _.clone(moe); equal(clone.name, 'moe', 'the clone as the attributes of the original'); clone.name = 'curly'; ok(clone.name == 'curly' && moe.name == 'moe', 'clones can change shallow attributes without affecting the original'); clone.lucky.push(101); equal(_.last(moe.lucky), 101, 'changes to deep attributes are shared with the original'); equal(_.clone(undefined), void 0, 'non objects should not be changed by clone'); equal(_.clone(1), 1, 'non objects should not be changed by clone'); equal(_.clone(null), null, 'non objects should not be changed by clone'); }); `) }) } // isEqual func Test_underscore_objects_10(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("isEqual", function() { function First() { this.value = 1; } First.prototype.value = 1; function Second() { this.value = 1; } Second.prototype.value = 2; // Basic equality and identity comparisons. ok(_.isEqual(null, null), " is equal to "); ok(_.isEqual(), " is equal to "); ok(!_.isEqual(0, -0), "<0> is not equal to <-0>"); ok(!_.isEqual(-0, 0), "Commutative equality is implemented for <0> and <-0>"); ok(!_.isEqual(null, undefined), " is not equal to "); ok(!_.isEqual(undefined, null), "Commutative equality is implemented for and "); // String object and primitive comparisons. ok(_.isEqual("Curly", "Curly"), "Identical string primitives are equal"); ok(_.isEqual(new String("Curly"), new String("Curly")), "String objects with identical primitive values are equal"); ok(_.isEqual(new String("Curly"), "Curly"), "String primitives and their corresponding object wrappers are equal"); ok(_.isEqual("Curly", new String("Curly")), "Commutative equality is implemented for string objects and primitives"); ok(!_.isEqual("Curly", "Larry"), "String primitives with different values are not equal"); ok(!_.isEqual(new String("Curly"), new String("Larry")), "String objects with different primitive values are not equal"); ok(!_.isEqual(new String("Curly"), {toString: function(){ return "Curly"; }}), "String objects and objects with a custom method are not equal"); // Number object and primitive comparisons. ok(_.isEqual(75, 75), "Identical number primitives are equal"); ok(_.isEqual(new Number(75), new Number(75)), "Number objects with identical primitive values are equal"); ok(_.isEqual(75, new Number(75)), "Number primitives and their corresponding object wrappers are equal"); ok(_.isEqual(new Number(75), 75), "Commutative equality is implemented for number objects and primitives"); ok(!_.isEqual(new Number(0), -0), " and <-0> are not equal"); ok(!_.isEqual(0, new Number(-0)), "Commutative equality is implemented for and <-0>"); ok(!_.isEqual(new Number(75), new Number(63)), "Number objects with different primitive values are not equal"); ok(!_.isEqual(new Number(63), {valueOf: function(){ return 63; }}), "Number objects and objects with a method are not equal"); // Comparisons involving . ok(_.isEqual(NaN, NaN), " is equal to "); ok(!_.isEqual(61, NaN), "A number primitive is not equal to "); ok(!_.isEqual(new Number(79), NaN), "A number object is not equal to "); ok(!_.isEqual(Infinity, NaN), " is not equal to "); // Boolean object and primitive comparisons. ok(_.isEqual(true, true), "Identical boolean primitives are equal"); ok(_.isEqual(new Boolean, new Boolean), "Boolean objects with identical primitive values are equal"); ok(_.isEqual(true, new Boolean(true)), "Boolean primitives and their corresponding object wrappers are equal"); ok(_.isEqual(new Boolean(true), true), "Commutative equality is implemented for booleans"); ok(!_.isEqual(new Boolean(true), new Boolean), "Boolean objects with different primitive values are not equal"); // Common type coercions. ok(!_.isEqual(true, new Boolean(false)), "Boolean objects are not equal to the boolean primitive "); ok(!_.isEqual("75", 75), "String and number primitives with like values are not equal"); ok(!_.isEqual(new Number(63), new String(63)), "String and number objects with like values are not equal"); ok(!_.isEqual(75, "75"), "Commutative equality is implemented for like string and number values"); ok(!_.isEqual(0, ""), "Number and string primitives with like values are not equal"); ok(!_.isEqual(1, true), "Number and boolean primitives with like values are not equal"); ok(!_.isEqual(new Boolean(false), new Number(0)), "Boolean and number objects with like values are not equal"); ok(!_.isEqual(false, new String("")), "Boolean primitives and string objects with like values are not equal"); ok(!_.isEqual(12564504e5, new Date(2009, 9, 25)), "Dates and their corresponding numeric primitive values are not equal"); // Dates. ok(_.isEqual(new Date(2009, 9, 25), new Date(2009, 9, 25)), "Date objects referencing identical times are equal"); ok(!_.isEqual(new Date(2009, 9, 25), new Date(2009, 11, 13)), "Date objects referencing different times are not equal"); ok(!_.isEqual(new Date(2009, 11, 13), { getTime: function(){ return 12606876e5; } }), "Date objects and objects with a method are not equal"); ok(!_.isEqual(new Date("Curly"), new Date("Curly")), "Invalid dates are not equal"); // Functions. ok(!_.isEqual(First, Second), "Different functions with identical bodies and source code representations are not equal"); // RegExps. ok(_.isEqual(/(?:)/gim, /(?:)/gim), "RegExps with equivalent patterns and flags are equal"); ok(!_.isEqual(/(?:)/g, /(?:)/gi), "RegExps with equivalent patterns and different flags are not equal"); ok(!_.isEqual(/Moe/gim, /Curly/gim), "RegExps with different patterns and equivalent flags are not equal"); ok(!_.isEqual(/(?:)/gi, /(?:)/g), "Commutative equality is implemented for RegExps"); ok(!_.isEqual(/Curly/g, {source: "Larry", global: true, ignoreCase: false, multiline: false}), "RegExps and RegExp-like objects are not equal"); // Empty arrays, array-like objects, and object literals. ok(_.isEqual({}, {}), "Empty object literals are equal"); ok(_.isEqual([], []), "Empty array literals are equal"); ok(_.isEqual([{}], [{}]), "Empty nested arrays and objects are equal"); ok(!_.isEqual({length: 0}, []), "Array-like objects and arrays are not equal."); ok(!_.isEqual([], {length: 0}), "Commutative equality is implemented for array-like objects"); ok(!_.isEqual({}, []), "Object literals and array literals are not equal"); ok(!_.isEqual([], {}), "Commutative equality is implemented for objects and arrays"); // Arrays with primitive and object values. ok(_.isEqual([1, "Larry", true], [1, "Larry", true]), "Arrays containing identical primitives are equal"); ok(_.isEqual([(/Moe/g), new Date(2009, 9, 25)], [(/Moe/g), new Date(2009, 9, 25)]), "Arrays containing equivalent elements are equal"); // Multi-dimensional arrays. var a = [new Number(47), false, "Larry", /Moe/, new Date(2009, 11, 13), ['running', 'biking', new String('programming')], {a: 47}]; var b = [new Number(47), false, "Larry", /Moe/, new Date(2009, 11, 13), ['running', 'biking', new String('programming')], {a: 47}]; ok(_.isEqual(a, b), "Arrays containing nested arrays and objects are recursively compared"); // Overwrite the methods defined in ES 5.1 section 15.4.4. a.forEach = a.map = a.filter = a.every = a.indexOf = a.lastIndexOf = a.some = a.reduce = a.reduceRight = null; b.join = b.pop = b.reverse = b.shift = b.slice = b.splice = b.concat = b.sort = b.unshift = null; // Array elements and properties. ok(_.isEqual(a, b), "Arrays containing equivalent elements and different non-numeric properties are equal"); a.push("White Rocks"); ok(!_.isEqual(a, b), "Arrays of different lengths are not equal"); a.push("East Boulder"); b.push("Gunbarrel Ranch", "Teller Farm"); ok(!_.isEqual(a, b), "Arrays of identical lengths containing different elements are not equal"); // Sparse arrays. ok(_.isEqual(Array(3), Array(3)), "Sparse arrays of identical lengths are equal"); ok(!_.isEqual(Array(3), Array(6)), "Sparse arrays of different lengths are not equal when both are empty"); // Simple objects. ok(_.isEqual({a: "Curly", b: 1, c: true}, {a: "Curly", b: 1, c: true}), "Objects containing identical primitives are equal"); ok(_.isEqual({a: /Curly/g, b: new Date(2009, 11, 13)}, {a: /Curly/g, b: new Date(2009, 11, 13)}), "Objects containing equivalent members are equal"); ok(!_.isEqual({a: 63, b: 75}, {a: 61, b: 55}), "Objects of identical sizes with different values are not equal"); ok(!_.isEqual({a: 63, b: 75}, {a: 61, c: 55}), "Objects of identical sizes with different property names are not equal"); ok(!_.isEqual({a: 1, b: 2}, {a: 1}), "Objects of different sizes are not equal"); ok(!_.isEqual({a: 1}, {a: 1, b: 2}), "Commutative equality is implemented for objects"); ok(!_.isEqual({x: 1, y: undefined}, {x: 1, z: 2}), "Objects with identical keys and different values are not equivalent"); // contains nested objects and arrays. a = { name: new String("Moe Howard"), age: new Number(77), stooge: true, hobbies: ["acting"], film: { name: "Sing a Song of Six Pants", release: new Date(1947, 9, 30), stars: [new String("Larry Fine"), "Shemp Howard"], minutes: new Number(16), seconds: 54 } }; // contains equivalent nested objects and arrays. b = { name: new String("Moe Howard"), age: new Number(77), stooge: true, hobbies: ["acting"], film: { name: "Sing a Song of Six Pants", release: new Date(1947, 9, 30), stars: [new String("Larry Fine"), "Shemp Howard"], minutes: new Number(16), seconds: 54 } }; ok(_.isEqual(a, b), "Objects with nested equivalent members are recursively compared"); // Instances. ok(_.isEqual(new First, new First), "Object instances are equal"); ok(!_.isEqual(new First, new Second), "Objects with different constructors and identical own properties are not equal"); ok(!_.isEqual({value: 1}, new First), "Object instances and objects sharing equivalent properties are not equal"); ok(!_.isEqual({value: 2}, new Second), "The prototype chain of objects should not be examined"); // Circular Arrays. (a = []).push(a); (b = []).push(b); ok(_.isEqual(a, b), "Arrays containing circular references are equal"); a.push(new String("Larry")); b.push(new String("Larry")); ok(_.isEqual(a, b), "Arrays containing circular references and equivalent properties are equal"); a.push("Shemp"); b.push("Curly"); ok(!_.isEqual(a, b), "Arrays containing circular references and different properties are not equal"); // More circular arrays #767. a = ["everything is checked but", "this", "is not"]; a[1] = a; b = ["everything is checked but", ["this", "array"], "is not"]; ok(!_.isEqual(a, b), "Comparison of circular references with non-circular references are not equal"); // Circular Objects. a = {abc: null}; b = {abc: null}; a.abc = a; b.abc = b; ok(_.isEqual(a, b), "Objects containing circular references are equal"); a.def = 75; b.def = 75; ok(_.isEqual(a, b), "Objects containing circular references and equivalent properties are equal"); a.def = new Number(75); b.def = new Number(63); ok(!_.isEqual(a, b), "Objects containing circular references and different properties are not equal"); // More circular objects #767. a = {everything: "is checked", but: "this", is: "not"}; a.but = a; b = {everything: "is checked", but: {that:"object"}, is: "not"}; ok(!_.isEqual(a, b), "Comparison of circular references with non-circular object references are not equal"); // Cyclic Structures. a = [{abc: null}]; b = [{abc: null}]; (a[0].abc = a).push(a); (b[0].abc = b).push(b); ok(_.isEqual(a, b), "Cyclic structures are equal"); a[0].def = "Larry"; b[0].def = "Larry"; ok(_.isEqual(a, b), "Cyclic structures containing equivalent properties are equal"); a[0].def = new String("Larry"); b[0].def = new String("Curly"); ok(!_.isEqual(a, b), "Cyclic structures containing different properties are not equal"); // Complex Circular References. a = {foo: {b: {foo: {c: {foo: null}}}}}; b = {foo: {b: {foo: {c: {foo: null}}}}}; a.foo.b.foo.c.foo = a; b.foo.b.foo.c.foo = b; ok(_.isEqual(a, b), "Cyclic structures with nested and identically-named properties are equal"); // Chaining. ok(!_.isEqual(_({x: 1, y: undefined}).chain(), _({x: 1, z: 2}).chain()), 'Chained objects containing different values are not equal'); a = _({x: 1, y: 2}).chain(); b = _({x: 1, y: 2}).chain(); equal(_.isEqual(a.isEqual(b), _(true)), true, ' can be chained'); // TEST: ??? return; // Objects from another frame. ok(_.isEqual({}, iObject)); }); `) }) } // isEmpty func Test_underscore_objects_11(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("isEmpty", function() { ok(!_([1]).isEmpty(), '[1] is not empty'); ok(_.isEmpty([]), '[] is empty'); ok(!_.isEmpty({one : 1}), '{one : 1} is not empty'); ok(_.isEmpty({}), '{} is empty'); ok(_.isEmpty(new RegExp('')), 'objects with prototype properties are empty'); ok(_.isEmpty(null), 'null is empty'); ok(_.isEmpty(), 'undefined is empty'); ok(_.isEmpty(''), 'the empty string is empty'); ok(!_.isEmpty('moe'), 'but other strings are not'); var obj = {one : 1}; delete obj.one; ok(_.isEmpty(obj), 'deleting all the keys from an object empties it'); }); `) }) } // isElement func Test_underscore_objects_12(t *testing.T) { // TEST: ReferenceError: $ is not defined if true { return } tt(t, func() { test, _ := test_() test(` test("isElement", function() { ok(!_.isElement('div'), 'strings are not dom elements'); ok(_.isElement($('html')[0]), 'the html tag is a DOM element'); ok(_.isElement(iElement), 'even from another frame'); }); `) }) } // isArguments func Test_underscore_objects_13(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("isArguments", function() { var args = (function(){ return arguments; })(1, 2, 3); ok(!_.isArguments('string'), 'a string is not an arguments object'); ok(!_.isArguments(_.isArguments), 'a function is not an arguments object'); ok(_.isArguments(args), 'but the arguments object is an arguments object'); ok(!_.isArguments(_.toArray(args)), 'but not when it\'s converted into an array'); ok(!_.isArguments([1,2,3]), 'and not vanilla arrays.'); // TEST: ReferenceError: iArguments is not defined return; ok(_.isArguments(iArguments), 'even from another frame'); }); `) }) } // isObject func Test_underscore_objects_14(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("isObject", function() { ok(_.isObject(arguments), 'the arguments object is object'); ok(_.isObject([1, 2, 3]), 'and arrays'); // TEST: ReferenceError: $ is not defined return; ok(_.isObject($('html')[0]), 'and DOM element'); ok(_.isObject(iElement), 'even from another frame'); ok(_.isObject(function () {}), 'and functions'); ok(_.isObject(iFunction), 'even from another frame'); ok(!_.isObject(null), 'but not null'); ok(!_.isObject(undefined), 'and not undefined'); ok(!_.isObject('string'), 'and not string'); ok(!_.isObject(12), 'and not number'); ok(!_.isObject(true), 'and not boolean'); ok(_.isObject(new String('string')), 'but new String()'); }); `) }) } // isArray func Test_underscore_objects_15(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("isArray", function() { ok(!_.isArray(arguments), 'the arguments object is not an array'); ok(_.isArray([1, 2, 3]), 'but arrays are'); // TEST: ??? return; ok(_.isArray(iArray), 'even from another frame'); }); `) }) } // isString func Test_underscore_objects_16(t *testing.T) { // TEST: ReferenceError: document is not defined if true { return } tt(t, func() { test, _ := test_() test(` test("isString", function() { ok(!_.isString(document.body), 'the document body is not a string'); ok(_.isString([1, 2, 3].join(', ')), 'but strings are'); // TEST: ??? return; ok(_.isString(iString), 'even from another frame'); }); `) }) } // isNumber func Test_underscore_objects_17(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("isNumber", function() { ok(!_.isNumber('string'), 'a string is not a number'); ok(!_.isNumber(arguments), 'the arguments object is not a number'); ok(!_.isNumber(undefined), 'undefined is not a number'); ok(_.isNumber(3 * 4 - 7 / 10), 'but numbers are'); ok(_.isNumber(NaN), 'NaN *is* a number'); ok(_.isNumber(Infinity), 'Infinity is a number'); // TEST: ??? return; ok(_.isNumber(iNumber), 'even from another frame'); ok(!_.isNumber('1'), 'numeric strings are not numbers'); }); `) }) } // isBoolean func Test_underscore_objects_18(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("isBoolean", function() { ok(!_.isBoolean(2), 'a number is not a boolean'); ok(!_.isBoolean("string"), 'a string is not a boolean'); ok(!_.isBoolean("false"), 'the string "false" is not a boolean'); ok(!_.isBoolean("true"), 'the string "true" is not a boolean'); ok(!_.isBoolean(arguments), 'the arguments object is not a boolean'); ok(!_.isBoolean(undefined), 'undefined is not a boolean'); ok(!_.isBoolean(NaN), 'NaN is not a boolean'); ok(!_.isBoolean(null), 'null is not a boolean'); ok(_.isBoolean(true), 'but true is'); ok(_.isBoolean(false), 'and so is false'); // TEST: ??? return; ok(_.isBoolean(iBoolean), 'even from another frame'); }); `) }) } // isFunction func Test_underscore_objects_19(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("isFunction", function() { ok(!_.isFunction([1, 2, 3]), 'arrays are not functions'); ok(!_.isFunction('moe'), 'strings are not functions'); ok(_.isFunction(_.isFunction), 'but functions are'); // TEST: ??? return; ok(_.isFunction(iFunction), 'even from another frame'); }); `) }) } // isDate func Test_underscore_objects_20(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("isDate", function() { ok(!_.isDate(100), 'numbers are not dates'); ok(!_.isDate({}), 'objects are not dates'); ok(_.isDate(new Date()), 'but dates are'); // TEST: ??? return; ok(_.isDate(iDate), 'even from another frame'); }); `) }) } // isRegExp func Test_underscore_objects_21(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("isRegExp", function() { ok(!_.isRegExp(_.identity), 'functions are not RegExps'); ok(_.isRegExp(/identity/), 'but RegExps are'); // TEST: ??? return; ok(_.isRegExp(iRegExp), 'even from another frame'); }); `) }) } // isFinite func Test_underscore_objects_22(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("isFinite", function() { ok(!_.isFinite(undefined), 'undefined is not Finite'); ok(!_.isFinite(null), 'null is not Finite'); ok(!_.isFinite(NaN), 'NaN is not Finite'); ok(!_.isFinite(Infinity), 'Infinity is not Finite'); ok(!_.isFinite(-Infinity), '-Infinity is not Finite'); ok(_.isFinite('12'), 'Numeric strings are numbers'); ok(!_.isFinite('1a'), 'Non numeric strings are not numbers'); ok(!_.isFinite(''), 'Empty strings are not numbers'); var obj = new Number(5); ok(_.isFinite(obj), 'Number instances can be finite'); ok(_.isFinite(0), '0 is Finite'); ok(_.isFinite(123), 'Ints are Finite'); ok(_.isFinite(-12.44), 'Floats are Finite'); }); `) }) } // isNaN func Test_underscore_objects_23(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("isNaN", function() { ok(!_.isNaN(undefined), 'undefined is not NaN'); ok(!_.isNaN(null), 'null is not NaN'); ok(!_.isNaN(0), '0 is not NaN'); ok(_.isNaN(NaN), 'but NaN is'); // TEST: ??? return; ok(_.isNaN(iNaN), 'even from another frame'); ok(_.isNaN(new Number(NaN)), 'wrapped NaN is still NaN'); }); `) }) } // isNull func Test_underscore_objects_24(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("isNull", function() { ok(!_.isNull(undefined), 'undefined is not null'); ok(!_.isNull(NaN), 'NaN is not null'); ok(_.isNull(null), 'but null is'); // TEST: ??? return; ok(_.isNull(iNull), 'even from another frame'); }); `) }) } // isUndefined func Test_underscore_objects_25(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("isUndefined", function() { ok(!_.isUndefined(1), 'numbers are defined'); ok(!_.isUndefined(null), 'null is defined'); ok(!_.isUndefined(false), 'false is defined'); ok(!_.isUndefined(NaN), 'NaN is defined'); ok(_.isUndefined(), 'nothing is undefined'); ok(_.isUndefined(undefined), 'undefined is undefined'); // TEST: ??? return; ok(_.isUndefined(iUndefined), 'even from another frame'); }); `) }) } // tap func Test_underscore_objects_26(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("tap", function() { var intercepted = null; var interceptor = function(obj) { intercepted = obj; }; var returned = _.tap(1, interceptor); equal(intercepted, 1, "passes tapped object to interceptor"); equal(returned, 1, "returns tapped object"); returned = _([1,2,3]).chain(). map(function(n){ return n * 2; }). max(). tap(interceptor). value(); ok(returned == 6 && intercepted == 6, 'can use tapped objects in a chain'); }); `) }) } // has func Test_underscore_objects_27(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("has", function () { var obj = {foo: "bar", func: function () {} }; ok (_.has(obj, "foo"), "has() checks that the object has a property."); ok (_.has(obj, "baz") == false, "has() returns false if the object doesn't have the property."); ok (_.has(obj, "func"), "has() works for functions too."); obj.hasOwnProperty = null; ok (_.has(obj, "foo"), "has() works even when the hasOwnProperty method is deleted."); var child = {}; child.prototype = obj; ok (_.has(child, "foo") == false, "has() does not check the prototype chain for a property.") }); `) }) } golang-github-robertkrimen-otto-0.0~git20180617.15f95af/underscore_test.go000066400000000000000000000076561331145703200261520ustar00rootroot00000000000000package otto import ( "testing" "github.com/robertkrimen/otto/terst" "github.com/robertkrimen/otto/underscore" ) func init() { underscore.Disable() } // A persistent handle for the underscore tester // We do not run underscore tests in parallel, so it is okay to stash globally // (Maybe use sync.Pool in the future...) var tester_ *_tester // A tester for underscore: test_ => test(underscore) :) func test_(arguments ...interface{}) (func(string, ...interface{}) Value, *_tester) { tester := tester_ if tester == nil { tester = newTester() tester.underscore() // Load underscore and testing shim, etc. tester_ = tester } return tester.test, tester } func (self *_tester) underscore() { vm := self.vm _, err := vm.Run(underscore.Source()) if err != nil { panic(err) } vm.Set("assert", func(call FunctionCall) Value { if !call.Argument(0).bool() { message := "Assertion failed" if len(call.ArgumentList) > 1 { message = call.ArgumentList[1].string() } t := terst.Caller().T() is(message, nil) t.Fail() return falseValue } return trueValue }) vm.Run(` var templateSettings; function _setup() { templateSettings = _.clone(_.templateSettings); } function _teardown() { _.templateSettings = templateSettings; } function module() { /* Nothing happens. */ } function equals(a, b, emit) { assert(a == b, emit + ", <" + a + "> != <" + b + ">"); } var equal = equals; function notStrictEqual(a, b, emit) { assert(a !== b, emit); } function strictEqual(a, b, emit) { assert(a === b, emit); } function ok(a, emit) { assert(a, emit); } function raises(fn, want, emit) { var have, _ok = false; if (typeof want === "string") { emit = want; want = null; } try { fn(); } catch(tmp) { have = tmp; } if (have) { if (!want) { _ok = true; } else if (want instanceof RegExp) { _ok = want.test(have); } else if (have instanceof want) { _ok = true } else if (want.call({}, have) === true) { _ok = true; } } ok(_ok, emit); } function test(name){ _setup() try { templateSettings = _.clone(_.templateSettings); if (arguments.length == 3) { count = 0 for (count = 0; count < arguments[1]; count++) { arguments[2]() } } else { // For now. arguments[1]() } } finally { _teardown() } } function deepEqual(a, b, emit) { // Also, for now. assert(_.isEqual(a, b), emit) } `) } func Test_underscore(t *testing.T) { tt(t, func() { test, _ := test_() test(` _.map([1, 2, 3], function(value){ return value + 1 }) `, "2,3,4") test(` abc = _.find([1, 2, 3, -1], function(value) { return value == -1 }) `, -1) test(`_.isEqual(1, 1)`, true) test(`_.isEqual([], [])`, true) test(`_.isEqual(['b', 'd'], ['b', 'd'])`, true) test(`_.isEqual(['b', 'd', 'c'], ['b', 'd', 'e'])`, false) test(`_.isFunction(function(){})`, true) test(`_.template('

\u2028<%= "\\u2028\\u2029" %>\u2029

')()`, "

\u2028\u2028\u2029\u2029

") }) } // TODO Test: typeof An argument reference // TODO Test: abc = {}; abc == Object(abc) golang-github-robertkrimen-otto-0.0~git20180617.15f95af/underscore_utility_test.go000066400000000000000000000304501331145703200277210ustar00rootroot00000000000000package otto import ( "testing" ) // #750 - Return _ instance. func Test_underscore_utility_0(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("#750 - Return _ instance.", 2, function() { var instance = _([]); ok(_(instance) === instance); ok(new _(instance) === instance); }); `) }) } // identity func Test_underscore_utility_1(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("identity", function() { var moe = {name : 'moe'}; equal(_.identity(moe), moe, 'moe is the same as his identity'); }); `) }) } // random func Test_underscore_utility_2(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("random", function() { var array = _.range(1000); var min = Math.pow(2, 31); var max = Math.pow(2, 62); ok(_.every(array, function() { return _.random(min, max) >= min; }), "should produce a random number greater than or equal to the minimum number"); ok(_.some(array, function() { return _.random(Number.MAX_VALUE) > 0; }), "should produce a random number when passed "); }); `) }) } // uniqueId func Test_underscore_utility_3(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("uniqueId", function() { var ids = [], i = 0; while(i++ < 100) ids.push(_.uniqueId()); equal(_.uniq(ids).length, ids.length, 'can generate a globally-unique stream of ids'); }); `) }) } // times func Test_underscore_utility_4(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("times", function() { var vals = []; _.times(3, function (i) { vals.push(i); }); ok(_.isEqual(vals, [0,1,2]), "is 0 indexed"); // vals = []; _(3).times(function(i) { vals.push(i); }); ok(_.isEqual(vals, [0,1,2]), "works as a wrapper"); // collects return values ok(_.isEqual([0, 1, 2], _.times(3, function(i) { return i; })), "collects return values"); }); `) }) } // mixin func Test_underscore_utility_5(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("mixin", function() { _.mixin({ myReverse: function(string) { return string.split('').reverse().join(''); } }); equal(_.myReverse('panacea'), 'aecanap', 'mixed in a function to _'); equal(_('champ').myReverse(), 'pmahc', 'mixed in a function to the OOP wrapper'); }); `) }) } // _.escape func Test_underscore_utility_6(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("_.escape", function() { equal(_.escape("Curly & Moe"), "Curly & Moe"); equal(_.escape("Curly & Moe"), "Curly &amp; Moe"); equal(_.escape(null), ''); }); `) }) } // _.unescape func Test_underscore_utility_7(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("_.unescape", function() { var string = "Curly & Moe"; equal(_.unescape("Curly & Moe"), string); equal(_.unescape("Curly &amp; Moe"), "Curly & Moe"); equal(_.unescape(null), ''); equal(_.unescape(_.escape(string)), string); }); `) }) } // template func Test_underscore_utility_8(t *testing.T) { tt(t, func() { test, _ := test_() test(` test("template", function() { var basicTemplate = _.template("<%= thing %> is gettin' on my noives!"); var result = basicTemplate({thing : 'This'}); equal(result, "This is gettin' on my noives!", 'can do basic attribute interpolation'); var sansSemicolonTemplate = _.template("A <% this %> B"); equal(sansSemicolonTemplate(), "A B"); var backslashTemplate = _.template("<%= thing %> is \\ridanculous"); equal(backslashTemplate({thing: 'This'}), "This is \\ridanculous"); var escapeTemplate = _.template('<%= a ? "checked=\\"checked\\"" : "" %>'); equal(escapeTemplate({a: true}), 'checked="checked"', 'can handle slash escapes in interpolations.'); var fancyTemplate = _.template("
    <% \ for (var key in people) { \ %>
  • <%= people[key] %>
  • <% } %>
"); result = fancyTemplate({people : {moe : "Moe", larry : "Larry", curly : "Curly"}}); equal(result, "
  • Moe
  • Larry
  • Curly
", 'can run arbitrary javascript in templates'); var escapedCharsInJavascriptTemplate = _.template("
    <% _.each(numbers.split('\\n'), function(item) { %>
  • <%= item %>
  • <% }) %>
"); result = escapedCharsInJavascriptTemplate({numbers: "one\ntwo\nthree\nfour"}); equal(result, "
  • one
  • two
  • three
  • four
", 'Can use escaped characters (e.g. \\n) in Javascript'); var namespaceCollisionTemplate = _.template("<%= pageCount %> <%= thumbnails[pageCount] %> <% _.each(thumbnails, function(p) { %>
\">
<% }); %>"); result = namespaceCollisionTemplate({ pageCount: 3, thumbnails: { 1: "p1-thumbnail.gif", 2: "p2-thumbnail.gif", 3: "p3-thumbnail.gif" } }); equal(result, "3 p3-thumbnail.gif
"); var noInterpolateTemplate = _.template("

Just some text. Hey, I know this is silly but it aids consistency.

"); result = noInterpolateTemplate(); equal(result, "

Just some text. Hey, I know this is silly but it aids consistency.

"); var quoteTemplate = _.template("It's its, not it's"); equal(quoteTemplate({}), "It's its, not it's"); var quoteInStatementAndBody = _.template("<%\ if(foo == 'bar'){ \ %>Statement quotes and 'quotes'.<% } %>"); equal(quoteInStatementAndBody({foo: "bar"}), "Statement quotes and 'quotes'."); var withNewlinesAndTabs = _.template('This\n\t\tis: <%= x %>.\n\tok.\nend.'); equal(withNewlinesAndTabs({x: 'that'}), 'This\n\t\tis: that.\n\tok.\nend.'); var template = _.template("<%- value %>"); var result = template({value: "