1/* 2Package otto is a JavaScript parser and interpreter written natively in Go. 3 4http://godoc.org/github.com/robertkrimen/otto 5 6 import ( 7 "github.com/robertkrimen/otto" 8 ) 9 10Run something in the VM 11 12 vm := otto.New() 13 vm.Run(` 14 abc = 2 + 2; 15 console.log("The value of abc is " + abc); // 4 16 `) 17 18Get a value out of the VM 19 20 value, err := vm.Get("abc") 21 value, _ := value.ToInteger() 22 } 23 24Set a number 25 26 vm.Set("def", 11) 27 vm.Run(` 28 console.log("The value of def is " + def); 29 // The value of def is 11 30 `) 31 32Set a string 33 34 vm.Set("xyzzy", "Nothing happens.") 35 vm.Run(` 36 console.log(xyzzy.length); // 16 37 `) 38 39Get the value of an expression 40 41 value, _ = vm.Run("xyzzy.length") 42 { 43 // value is an int64 with a value of 16 44 value, _ := value.ToInteger() 45 } 46 47An error happens 48 49 value, err = vm.Run("abcdefghijlmnopqrstuvwxyz.length") 50 if err != nil { 51 // err = ReferenceError: abcdefghijlmnopqrstuvwxyz is not defined 52 // If there is an error, then value.IsUndefined() is true 53 ... 54 } 55 56Set a Go function 57 58 vm.Set("sayHello", func(call otto.FunctionCall) otto.Value { 59 fmt.Printf("Hello, %s.\n", call.Argument(0).String()) 60 return otto.Value{} 61 }) 62 63Set a Go function that returns something useful 64 65 vm.Set("twoPlus", func(call otto.FunctionCall) otto.Value { 66 right, _ := call.Argument(0).ToInteger() 67 result, _ := vm.ToValue(2 + right) 68 return result 69 }) 70 71Use the functions in JavaScript 72 73 result, _ = vm.Run(` 74 sayHello("Xyzzy"); // Hello, Xyzzy. 75 sayHello(); // Hello, undefined 76 77 result = twoPlus(2.0); // 4 78 `) 79 80Parser 81 82A separate parser is available in the parser package if you're just interested in building an AST. 83 84http://godoc.org/github.com/robertkrimen/otto/parser 85 86Parse and return an AST 87 88 filename := "" // A filename is optional 89 src := ` 90 // Sample xyzzy example 91 (function(){ 92 if (3.14159 > 0) { 93 console.log("Hello, World."); 94 return; 95 } 96 97 var xyzzy = NaN; 98 console.log("Nothing happens."); 99 return xyzzy; 100 })(); 101 ` 102 103 // Parse some JavaScript, yielding a *ast.Program and/or an ErrorList 104 program, err := parser.ParseFile(nil, filename, src, 0) 105 106otto 107 108You can run (Go) JavaScript from the commandline with: http://github.com/robertkrimen/otto/tree/master/otto 109 110 $ go get -v github.com/robertkrimen/otto/otto 111 112Run JavaScript by entering some source on stdin or by giving otto a filename: 113 114 $ otto example.js 115 116underscore 117 118Optionally include the JavaScript utility-belt library, underscore, with this import: 119 120 import ( 121 "github.com/robertkrimen/otto" 122 _ "github.com/robertkrimen/otto/underscore" 123 ) 124 125 // Now every otto runtime will come loaded with underscore 126 127For more information: http://github.com/robertkrimen/otto/tree/master/underscore 128 129Caveat Emptor 130 131The following are some limitations with otto: 132 133 * "use strict" will parse, but does nothing. 134 * The regular expression engine (re2/regexp) is not fully compatible with the ECMA5 specification. 135 * Otto targets ES5. ES6 features (eg: Typed Arrays) are not supported. 136 137Regular Expression Incompatibility 138 139Go translates JavaScript-style regular expressions into something that is "regexp" compatible via `parser.TransformRegExp`. 140Unfortunately, RegExp requires backtracking for some patterns, and backtracking is not supported by the standard Go engine: https://code.google.com/p/re2/wiki/Syntax 141 142Therefore, the following syntax is incompatible: 143 144 (?=) // Lookahead (positive), currently a parsing error 145 (?!) // Lookahead (backhead), currently a parsing error 146 \1 // Backreference (\1, \2, \3, ...), currently a parsing error 147 148A brief discussion of these limitations: "Regexp (?!re)" https://groups.google.com/forum/?fromgroups=#%21topic/golang-nuts/7qgSDWPIh_E 149 150More information about re2: https://code.google.com/p/re2/ 151 152In addition to the above, re2 (Go) has a different definition for \s: [\t\n\f\r ]. 153The JavaScript definition, on the other hand, also includes \v, Unicode "Separator, Space", etc. 154 155Halting Problem 156 157If you want to stop long running executions (like third-party code), you can use the interrupt channel to do this: 158 159 package main 160 161 import ( 162 "errors" 163 "fmt" 164 "os" 165 "time" 166 167 "github.com/robertkrimen/otto" 168 ) 169 170 var halt = errors.New("Stahp") 171 172 func main() { 173 runUnsafe(`var abc = [];`) 174 runUnsafe(` 175 while (true) { 176 // Loop forever 177 }`) 178 } 179 180 func runUnsafe(unsafe string) { 181 start := time.Now() 182 defer func() { 183 duration := time.Since(start) 184 if caught := recover(); caught != nil { 185 if caught == halt { 186 fmt.Fprintf(os.Stderr, "Some code took to long! Stopping after: %v\n", duration) 187 return 188 } 189 panic(caught) // Something else happened, repanic! 190 } 191 fmt.Fprintf(os.Stderr, "Ran code successfully: %v\n", duration) 192 }() 193 194 vm := otto.New() 195 vm.Interrupt = make(chan func(), 1) // The buffer prevents blocking 196 197 go func() { 198 time.Sleep(2 * time.Second) // Stop after two seconds 199 vm.Interrupt <- func() { 200 panic(halt) 201 } 202 }() 203 204 vm.Run(unsafe) // Here be dragons (risky code) 205 } 206 207Where is setTimeout/setInterval? 208 209These timing functions are not actually part of the ECMA-262 specification. Typically, they belong to the `windows` object (in the browser). 210It 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. 211 212For an example of how this could be done in Go with otto, see natto: 213 214http://github.com/robertkrimen/natto 215 216Here is some more discussion of the issue: 217 218* http://book.mixu.net/node/ch2.html 219 220* http://en.wikipedia.org/wiki/Reentrancy_%28computing%29 221 222* http://aaroncrane.co.uk/2009/02/perl_safe_signals/ 223 224*/ 225package otto 226 227import ( 228 "fmt" 229 "strings" 230 231 "github.com/robertkrimen/otto/file" 232 "github.com/robertkrimen/otto/registry" 233) 234 235// Otto is the representation of the JavaScript runtime. Each instance of Otto has a self-contained namespace. 236type Otto struct { 237 // Interrupt is a channel for interrupting the runtime. You can use this to halt a long running execution, for example. 238 // See "Halting Problem" for more information. 239 Interrupt chan func() 240 runtime *_runtime 241} 242 243// New will allocate a new JavaScript runtime 244func New() *Otto { 245 self := &Otto{ 246 runtime: newContext(), 247 } 248 self.runtime.otto = self 249 self.runtime.traceLimit = 10 250 self.Set("console", self.runtime.newConsole()) 251 252 registry.Apply(func(entry registry.Entry) { 253 self.Run(entry.Source()) 254 }) 255 256 return self 257} 258 259func (otto *Otto) clone() *Otto { 260 self := &Otto{ 261 runtime: otto.runtime.clone(), 262 } 263 self.runtime.otto = self 264 return self 265} 266 267// Run will allocate a new JavaScript runtime, run the given source 268// on the allocated runtime, and return the runtime, resulting value, and 269// error (if any). 270// 271// src may be a string, a byte slice, a bytes.Buffer, or an io.Reader, but it MUST always be in UTF-8. 272// 273// src may also be a Script. 274// 275// src may also be a Program, but if the AST has been modified, then runtime behavior is undefined. 276// 277func Run(src interface{}) (*Otto, Value, error) { 278 otto := New() 279 value, err := otto.Run(src) // This already does safety checking 280 return otto, value, err 281} 282 283// Run will run the given source (parsing it first if necessary), returning the resulting value and error (if any) 284// 285// src may be a string, a byte slice, a bytes.Buffer, or an io.Reader, but it MUST always be in UTF-8. 286// 287// If the runtime is unable to parse source, then this function will return undefined and the parse error (nothing 288// will be evaluated in this case). 289// 290// src may also be a Script. 291// 292// src may also be a Program, but if the AST has been modified, then runtime behavior is undefined. 293// 294func (self Otto) Run(src interface{}) (Value, error) { 295 value, err := self.runtime.cmpl_run(src, nil) 296 if !value.safe() { 297 value = Value{} 298 } 299 return value, err 300} 301 302// Eval will do the same thing as Run, except without leaving the current scope. 303// 304// By staying in the same scope, the code evaluated has access to everything 305// already defined in the current stack frame. This is most useful in, for 306// example, a debugger call. 307func (self Otto) Eval(src interface{}) (Value, error) { 308 if self.runtime.scope == nil { 309 self.runtime.enterGlobalScope() 310 defer self.runtime.leaveScope() 311 } 312 313 value, err := self.runtime.cmpl_eval(src, nil) 314 if !value.safe() { 315 value = Value{} 316 } 317 return value, err 318} 319 320// Get the value of the top-level binding of the given name. 321// 322// If there is an error (like the binding does not exist), then the value 323// will be undefined. 324func (self Otto) Get(name string) (Value, error) { 325 value := Value{} 326 err := catchPanic(func() { 327 value = self.getValue(name) 328 }) 329 if !value.safe() { 330 value = Value{} 331 } 332 return value, err 333} 334 335func (self Otto) getValue(name string) Value { 336 return self.runtime.globalStash.getBinding(name, false) 337} 338 339// Set the top-level binding of the given name to the given value. 340// 341// Set will automatically apply ToValue to the given value in order 342// to convert it to a JavaScript value (type Value). 343// 344// If there is an error (like the binding is read-only, or the ToValue conversion 345// fails), then an error is returned. 346// 347// If the top-level binding does not exist, it will be created. 348func (self Otto) Set(name string, value interface{}) error { 349 { 350 value, err := self.ToValue(value) 351 if err != nil { 352 return err 353 } 354 err = catchPanic(func() { 355 self.setValue(name, value) 356 }) 357 return err 358 } 359} 360 361func (self Otto) setValue(name string, value Value) { 362 self.runtime.globalStash.setValue(name, value, false) 363} 364 365func (self Otto) SetDebuggerHandler(fn func(vm *Otto)) { 366 self.runtime.debugger = fn 367} 368 369func (self Otto) SetRandomSource(fn func() float64) { 370 self.runtime.random = fn 371} 372 373// SetStackDepthLimit sets an upper limit to the depth of the JavaScript 374// stack. In simpler terms, this limits the number of "nested" function calls 375// you can make in a particular interpreter instance. 376// 377// Note that this doesn't take into account the Go stack depth. If your 378// JavaScript makes a call to a Go function, otto won't keep track of what 379// happens outside the interpreter. So if your Go function is infinitely 380// recursive, you're still in trouble. 381func (self Otto) SetStackDepthLimit(limit int) { 382 self.runtime.stackLimit = limit 383} 384 385// SetStackTraceLimit sets an upper limit to the number of stack frames that 386// otto will use when formatting an error's stack trace. By default, the limit 387// is 10. This is consistent with V8 and SpiderMonkey. 388// 389// TODO: expose via `Error.stackTraceLimit` 390func (self Otto) SetStackTraceLimit(limit int) { 391 self.runtime.traceLimit = limit 392} 393 394// MakeCustomError creates a new Error object with the given name and message, 395// returning it as a Value. 396func (self Otto) MakeCustomError(name, message string) Value { 397 return self.runtime.toValue(self.runtime.newError(name, self.runtime.toValue(message), 0)) 398} 399 400// MakeRangeError creates a new RangeError object with the given message, 401// returning it as a Value. 402func (self Otto) MakeRangeError(message string) Value { 403 return self.runtime.toValue(self.runtime.newRangeError(self.runtime.toValue(message))) 404} 405 406// MakeSyntaxError creates a new SyntaxError object with the given message, 407// returning it as a Value. 408func (self Otto) MakeSyntaxError(message string) Value { 409 return self.runtime.toValue(self.runtime.newSyntaxError(self.runtime.toValue(message))) 410} 411 412// MakeTypeError creates a new TypeError object with the given message, 413// returning it as a Value. 414func (self Otto) MakeTypeError(message string) Value { 415 return self.runtime.toValue(self.runtime.newTypeError(self.runtime.toValue(message))) 416} 417 418// Context is a structure that contains information about the current execution 419// context. 420type Context struct { 421 Filename string 422 Line int 423 Column int 424 Callee string 425 Symbols map[string]Value 426 This Value 427 Stacktrace []string 428} 429 430// Context returns the current execution context of the vm, traversing up to 431// ten stack frames, and skipping any innermost native function stack frames. 432func (self Otto) Context() Context { 433 return self.ContextSkip(10, true) 434} 435 436// ContextLimit returns the current execution context of the vm, with a 437// specific limit on the number of stack frames to traverse, skipping any 438// innermost native function stack frames. 439func (self Otto) ContextLimit(limit int) Context { 440 return self.ContextSkip(limit, true) 441} 442 443// ContextSkip returns the current execution context of the vm, with a 444// specific limit on the number of stack frames to traverse, optionally 445// skipping any innermost native function stack frames. 446func (self Otto) ContextSkip(limit int, skipNative bool) (ctx Context) { 447 // Ensure we are operating in a scope 448 if self.runtime.scope == nil { 449 self.runtime.enterGlobalScope() 450 defer self.runtime.leaveScope() 451 } 452 453 scope := self.runtime.scope 454 frame := scope.frame 455 456 for skipNative && frame.native && scope.outer != nil { 457 scope = scope.outer 458 frame = scope.frame 459 } 460 461 // Get location information 462 ctx.Filename = "<unknown>" 463 ctx.Callee = frame.callee 464 465 switch { 466 case frame.native: 467 ctx.Filename = frame.nativeFile 468 ctx.Line = frame.nativeLine 469 ctx.Column = 0 470 case frame.file != nil: 471 ctx.Filename = "<anonymous>" 472 473 if p := frame.file.Position(file.Idx(frame.offset)); p != nil { 474 ctx.Line = p.Line 475 ctx.Column = p.Column 476 477 if p.Filename != "" { 478 ctx.Filename = p.Filename 479 } 480 } 481 } 482 483 // Get the current scope this Value 484 ctx.This = toValue_object(scope.this) 485 486 // Build stacktrace (up to 10 levels deep) 487 ctx.Symbols = make(map[string]Value) 488 ctx.Stacktrace = append(ctx.Stacktrace, frame.location()) 489 for limit != 0 { 490 // Get variables 491 stash := scope.lexical 492 for { 493 for _, name := range getStashProperties(stash) { 494 if _, ok := ctx.Symbols[name]; !ok { 495 ctx.Symbols[name] = stash.getBinding(name, true) 496 } 497 } 498 stash = stash.outer() 499 if stash == nil || stash.outer() == nil { 500 break 501 } 502 } 503 504 scope = scope.outer 505 if scope == nil { 506 break 507 } 508 if scope.frame.offset >= 0 { 509 ctx.Stacktrace = append(ctx.Stacktrace, scope.frame.location()) 510 } 511 limit-- 512 } 513 514 return 515} 516 517// Call the given JavaScript with a given this and arguments. 518// 519// If this is nil, then some special handling takes place to determine the proper 520// this value, falling back to a "standard" invocation if necessary (where this is 521// undefined). 522// 523// If source begins with "new " (A lowercase new followed by a space), then 524// Call will invoke the function constructor rather than performing a function call. 525// In this case, the this argument has no effect. 526// 527// // value is a String object 528// value, _ := vm.Call("Object", nil, "Hello, World.") 529// 530// // Likewise... 531// value, _ := vm.Call("new Object", nil, "Hello, World.") 532// 533// // This will perform a concat on the given array and return the result 534// // value is [ 1, 2, 3, undefined, 4, 5, 6, 7, "abc" ] 535// value, _ := vm.Call(`[ 1, 2, 3, undefined, 4 ].concat`, nil, 5, 6, 7, "abc") 536// 537func (self Otto) Call(source string, this interface{}, argumentList ...interface{}) (Value, error) { 538 539 thisValue := Value{} 540 541 construct := false 542 if strings.HasPrefix(source, "new ") { 543 source = source[4:] 544 construct = true 545 } 546 547 // FIXME enterGlobalScope 548 self.runtime.enterGlobalScope() 549 defer func() { 550 self.runtime.leaveScope() 551 }() 552 553 if !construct && this == nil { 554 program, err := self.runtime.cmpl_parse("", source+"()", nil) 555 if err == nil { 556 if node, ok := program.body[0].(*_nodeExpressionStatement); ok { 557 if node, ok := node.expression.(*_nodeCallExpression); ok { 558 var value Value 559 err := catchPanic(func() { 560 value = self.runtime.cmpl_evaluate_nodeCallExpression(node, argumentList) 561 }) 562 if err != nil { 563 return Value{}, err 564 } 565 return value, nil 566 } 567 } 568 } 569 } else { 570 value, err := self.ToValue(this) 571 if err != nil { 572 return Value{}, err 573 } 574 thisValue = value 575 } 576 577 { 578 this := thisValue 579 580 fn, err := self.Run(source) 581 if err != nil { 582 return Value{}, err 583 } 584 585 if construct { 586 result, err := fn.constructSafe(self.runtime, this, argumentList...) 587 if err != nil { 588 return Value{}, err 589 } 590 return result, nil 591 } 592 593 result, err := fn.Call(this, argumentList...) 594 if err != nil { 595 return Value{}, err 596 } 597 return result, nil 598 } 599} 600 601// Object will run the given source and return the result as an object. 602// 603// For example, accessing an existing object: 604// 605// object, _ := vm.Object(`Number`) 606// 607// Or, creating a new object: 608// 609// object, _ := vm.Object(`({ xyzzy: "Nothing happens." })`) 610// 611// Or, creating and assigning an object: 612// 613// object, _ := vm.Object(`xyzzy = {}`) 614// object.Set("volume", 11) 615// 616// If there is an error (like the source does not result in an object), then 617// nil and an error is returned. 618func (self Otto) Object(source string) (*Object, error) { 619 value, err := self.runtime.cmpl_run(source, nil) 620 if err != nil { 621 return nil, err 622 } 623 if value.IsObject() { 624 return value.Object(), nil 625 } 626 return nil, fmt.Errorf("value is not an object") 627} 628 629// ToValue will convert an interface{} value to a value digestible by otto/JavaScript. 630func (self Otto) ToValue(value interface{}) (Value, error) { 631 return self.runtime.safeToValue(value) 632} 633 634// Copy will create a copy/clone of the runtime. 635// 636// Copy is useful for saving some time when creating many similar runtimes. 637// 638// This method works by walking the original runtime and cloning each object, scope, stash, 639// etc. into a new runtime. 640// 641// Be on the lookout for memory leaks or inadvertent sharing of resources. 642func (in *Otto) Copy() *Otto { 643 out := &Otto{ 644 runtime: in.runtime.clone(), 645 } 646 out.runtime.otto = out 647 return out 648} 649 650// Object{} 651 652// Object is the representation of a JavaScript object. 653type Object struct { 654 object *_object 655 value Value 656} 657 658func _newObject(object *_object, value Value) *Object { 659 // value MUST contain object! 660 return &Object{ 661 object: object, 662 value: value, 663 } 664} 665 666// Call a method on the object. 667// 668// It is essentially equivalent to: 669// 670// var method, _ := object.Get(name) 671// method.Call(object, argumentList...) 672// 673// An undefined value and an error will result if: 674// 675// 1. There is an error during conversion of the argument list 676// 2. The property is not actually a function 677// 3. An (uncaught) exception is thrown 678// 679func (self Object) Call(name string, argumentList ...interface{}) (Value, error) { 680 // TODO: Insert an example using JavaScript below... 681 // e.g., Object("JSON").Call("stringify", ...) 682 683 function, err := self.Get(name) 684 if err != nil { 685 return Value{}, err 686 } 687 return function.Call(self.Value(), argumentList...) 688} 689 690// Value will return self as a value. 691func (self Object) Value() Value { 692 return self.value 693} 694 695// Get the value of the property with the given name. 696func (self Object) Get(name string) (Value, error) { 697 value := Value{} 698 err := catchPanic(func() { 699 value = self.object.get(name) 700 }) 701 if !value.safe() { 702 value = Value{} 703 } 704 return value, err 705} 706 707// Set the property of the given name to the given value. 708// 709// An error will result if the setting the property triggers an exception (i.e. read-only), 710// or there is an error during conversion of the given value. 711func (self Object) Set(name string, value interface{}) error { 712 { 713 value, err := self.object.runtime.safeToValue(value) 714 if err != nil { 715 return err 716 } 717 err = catchPanic(func() { 718 self.object.put(name, value, true) 719 }) 720 return err 721 } 722} 723 724// Keys gets the keys for the given object. 725// 726// Equivalent to calling Object.keys on the object. 727func (self Object) Keys() []string { 728 var keys []string 729 self.object.enumerate(false, func(name string) bool { 730 keys = append(keys, name) 731 return true 732 }) 733 return keys 734} 735 736// KeysByParent gets the keys (and those of the parents) for the given object, 737// in order of "closest" to "furthest". 738func (self Object) KeysByParent() [][]string { 739 var a [][]string 740 741 for o := self.object; o != nil; o = o.prototype { 742 var l []string 743 744 o.enumerate(false, func(name string) bool { 745 l = append(l, name) 746 return true 747 }) 748 749 a = append(a, l) 750 } 751 752 return a 753} 754 755// Class will return the class string of the object. 756// 757// The return value will (generally) be one of: 758// 759// Object 760// Function 761// Array 762// String 763// Number 764// Boolean 765// Date 766// RegExp 767// 768func (self Object) Class() string { 769 return self.object.class 770} 771