Commit c305005d authored by Felix Lange's avatar Felix Lange

Merge pull request #2227 from bas-vk/mathrandom

console: seed random number generator
parents 17649edd 6777531a
...@@ -84,7 +84,7 @@ ...@@ -84,7 +84,7 @@
}, },
{ {
"ImportPath": "github.com/robertkrimen/otto", "ImportPath": "github.com/robertkrimen/otto",
"Rev": "c21072f61b64b51ea58138ccacf0a85d54b9f07c" "Rev": "53221230c215611a90762720c9042ac782ef74ee"
}, },
{ {
"ImportPath": "github.com/syndtr/goleveldb/leveldb", "ImportPath": "github.com/syndtr/goleveldb/leveldb",
......
package 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 // After a key or keyword
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
TBD
)
// Comment contains the data of the comment
type Comment struct {
Begin file.Idx
Text string
Position CommentPosition
}
// 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"
default:
return "???"
}
}
// String returns a stringified version of the comment
func (c Comment) String() string {
return fmt.Sprintf("Comment: %v", c.Text)
}
// 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 {
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]
}
}
}
...@@ -86,6 +86,11 @@ type ( ...@@ -86,6 +86,11 @@ type (
Identifier Identifier Identifier Identifier
} }
EmptyExpression struct {
Begin file.Idx
End file.Idx
}
FunctionLiteral struct { FunctionLiteral struct {
Function file.Idx Function file.Idx
Name *Identifier Name *Identifier
...@@ -185,6 +190,7 @@ func (*BracketExpression) _expressionNode() {} ...@@ -185,6 +190,7 @@ func (*BracketExpression) _expressionNode() {}
func (*CallExpression) _expressionNode() {} func (*CallExpression) _expressionNode() {}
func (*ConditionalExpression) _expressionNode() {} func (*ConditionalExpression) _expressionNode() {}
func (*DotExpression) _expressionNode() {} func (*DotExpression) _expressionNode() {}
func (*EmptyExpression) _expressionNode() {}
func (*FunctionLiteral) _expressionNode() {} func (*FunctionLiteral) _expressionNode() {}
func (*Identifier) _expressionNode() {} func (*Identifier) _expressionNode() {}
func (*NewExpression) _expressionNode() {} func (*NewExpression) _expressionNode() {}
...@@ -399,6 +405,7 @@ func (self *BracketExpression) Idx0() file.Idx { return self.Left.Idx0() } ...@@ -399,6 +405,7 @@ func (self *BracketExpression) Idx0() file.Idx { return self.Left.Idx0() }
func (self *CallExpression) Idx0() file.Idx { return self.Callee.Idx0() } func (self *CallExpression) Idx0() file.Idx { return self.Callee.Idx0() }
func (self *ConditionalExpression) Idx0() file.Idx { return self.Test.Idx0() } func (self *ConditionalExpression) Idx0() file.Idx { return self.Test.Idx0() }
func (self *DotExpression) Idx0() file.Idx { return self.Left.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 *FunctionLiteral) Idx0() file.Idx { return self.Function }
func (self *Identifier) Idx0() file.Idx { return self.Idx } func (self *Identifier) Idx0() file.Idx { return self.Idx }
func (self *NewExpression) Idx0() file.Idx { return self.New } func (self *NewExpression) Idx0() file.Idx { return self.New }
...@@ -447,6 +454,7 @@ func (self *BracketExpression) Idx1() file.Idx { return self.RightBracket + ...@@ -447,6 +454,7 @@ func (self *BracketExpression) Idx1() file.Idx { return self.RightBracket +
func (self *CallExpression) Idx1() file.Idx { return self.RightParenthesis + 1 } func (self *CallExpression) Idx1() file.Idx { return self.RightParenthesis + 1 }
func (self *ConditionalExpression) Idx1() file.Idx { return self.Test.Idx1() } func (self *ConditionalExpression) Idx1() file.Idx { return self.Test.Idx1() }
func (self *DotExpression) Idx1() file.Idx { return self.Identifier.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 *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 *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 *NewExpression) Idx1() file.Idx { return self.RightParenthesis + 1 }
......
...@@ -117,7 +117,13 @@ func builtinMath_pow(call FunctionCall) Value { ...@@ -117,7 +117,13 @@ func builtinMath_pow(call FunctionCall) Value {
} }
func builtinMath_random(call FunctionCall) Value { func builtinMath_random(call FunctionCall) Value {
return toValue_float64(rand.Float64()) 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 { func builtinMath_round(call FunctionCall) Value {
......
...@@ -82,6 +82,9 @@ func (cmpl *_compiler) parseExpression(in ast.Expression) _nodeExpression { ...@@ -82,6 +82,9 @@ func (cmpl *_compiler) parseExpression(in ast.Expression) _nodeExpression {
identifier: in.Identifier.Name, identifier: in.Identifier.Name,
} }
case *ast.EmptyExpression:
return nil
case *ast.FunctionLiteral: case *ast.FunctionLiteral:
name := "" name := ""
if in.Name != nil { if in.Name != nil {
......
...@@ -363,6 +363,10 @@ func (self Otto) SetDebuggerHandler(fn func(vm *Otto)) { ...@@ -363,6 +363,10 @@ func (self Otto) SetDebuggerHandler(fn func(vm *Otto)) {
self.runtime.debugger = fn self.runtime.debugger = fn
} }
func (self Otto) SetRandomSource(fn func() float64) {
self.runtime.random = fn
}
// Context is a structure that contains information about the current execution // Context is a structure that contains information about the current execution
// context. // context.
type Context struct { type Context struct {
......
...@@ -120,6 +120,7 @@ func isLineTerminator(chr rune) bool { ...@@ -120,6 +120,7 @@ func isLineTerminator(chr rune) bool {
func (self *_parser) scan() (tkn token.Token, literal string, idx file.Idx) { func (self *_parser) scan() (tkn token.Token, literal string, idx file.Idx) {
self.implicitSemicolon = false self.implicitSemicolon = false
self.skippedLineBreak = false
for { for {
self.skipWhiteSpace() self.skipWhiteSpace()
...@@ -238,9 +239,20 @@ func (self *_parser) scan() (tkn token.Token, literal string, idx file.Idx) { ...@@ -238,9 +239,20 @@ func (self *_parser) scan() (tkn token.Token, literal string, idx file.Idx) {
tkn = self.switch2(token.MULTIPLY, token.MULTIPLY_ASSIGN) tkn = self.switch2(token.MULTIPLY, token.MULTIPLY_ASSIGN)
case '/': case '/':
if self.chr == '/' { if self.chr == '/' {
if self.mode&StoreComments != 0 {
runes := self.readSingleLineComment()
literal = string(runes)
tkn = token.COMMENT
return
}
self.skipSingleLineComment() self.skipSingleLineComment()
continue continue
} else if self.chr == '*' { } else if self.chr == '*' {
if self.mode&StoreComments != 0 {
literal = string(self.readMultiLineComment())
tkn = token.COMMENT
return
}
self.skipMultiLineComment() self.skipMultiLineComment()
continue continue
} else { } else {
...@@ -411,6 +423,39 @@ func (self *_RegExp_parser) read() { ...@@ -411,6 +423,39 @@ func (self *_RegExp_parser) read() {
} }
} }
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() { func (self *_parser) skipSingleLineComment() {
for self.chr != -1 { for self.chr != -1 {
self.read() self.read()
...@@ -442,6 +487,7 @@ func (self *_parser) skipWhiteSpace() { ...@@ -442,6 +487,7 @@ func (self *_parser) skipWhiteSpace() {
continue continue
case '\r': case '\r':
if self._peek() == '\n' { if self._peek() == '\n' {
self.skippedLineBreak = true
self.read() self.read()
} }
fallthrough fallthrough
...@@ -449,6 +495,7 @@ func (self *_parser) skipWhiteSpace() { ...@@ -449,6 +495,7 @@ func (self *_parser) skipWhiteSpace() {
if self.insertSemicolon { if self.insertSemicolon {
return return
} }
self.skippedLineBreak = true
self.read() self.read()
continue continue
} }
......
...@@ -49,6 +49,7 @@ type Mode uint ...@@ -49,6 +49,7 @@ type Mode uint
const ( const (
IgnoreRegExpErrors Mode = 1 << iota // Ignore RegExp compatibility errors (allow backtracking) IgnoreRegExpErrors Mode = 1 << iota // Ignore RegExp compatibility errors (allow backtracking)
StoreComments // Store the comments from source to the comments map
) )
type _parser struct { type _parser struct {
...@@ -79,6 +80,10 @@ type _parser struct { ...@@ -79,6 +80,10 @@ type _parser struct {
mode Mode mode Mode
file *file.File file *file.File
comments []*ast.Comment
commentMap *ast.CommentMap
skippedLineBreak bool
} }
func _newParser(filename, src string, base int) *_parser { func _newParser(filename, src string, base int) *_parser {
...@@ -88,6 +93,9 @@ func _newParser(filename, src string, base int) *_parser { ...@@ -88,6 +93,9 @@ func _newParser(filename, src string, base int) *_parser {
length: len(src), length: len(src),
base: base, base: base,
file: file.NewFile(filename, src, base), file: file.NewFile(filename, src, base),
comments: make([]*ast.Comment, 0),
commentMap: &ast.CommentMap{},
skippedLineBreak: false,
} }
} }
...@@ -184,6 +192,9 @@ func (self *_parser) parse() (*ast.Program, error) { ...@@ -184,6 +192,9 @@ func (self *_parser) parse() (*ast.Program, error) {
if false { if false {
self.errors.Sort() self.errors.Sort()
} }
self.addCommentStatements(program, ast.FINAL)
return program, self.errors.Err() return program, self.errors.Err()
} }
...@@ -270,3 +281,63 @@ func (self *_parser) position(idx file.Idx) file.Position { ...@@ -270,3 +281,63 @@ func (self *_parser) position(idx file.Idx) file.Position {
return position return position
} }
// findComments finds the following comments.
// Comments on the same line will be grouped together and returned.
// After the first line break, comments will be added as statement comments.
func (self *_parser) findComments(ignoreLineBreak bool) []*ast.Comment {
if self.mode&StoreComments == 0 {
return nil
}
comments := make([]*ast.Comment, 0)
newline := false
for self.implicitSemicolon == false || ignoreLineBreak {
if self.token != token.COMMENT {
break
}
comment := &ast.Comment{
Begin: self.idx,
Text: self.literal,
Position: ast.TBD,
}
newline = self.skippedLineBreak || newline
if newline && !ignoreLineBreak {
self.comments = append(self.comments, comment)
} else {
comments = append(comments, comment)
}
self.next()
}
return comments
}
// addCommentStatements will add the previously parsed, not positioned comments to the provided node
func (self *_parser) addCommentStatements(node ast.Node, position ast.CommentPosition) {
if len(self.comments) > 0 {
self.commentMap.AddComments(node, self.comments, position)
// Reset comments
self.comments = make([]*ast.Comment, 0)
}
}
// fetchComments fetches the current comments, resets the slice and returns the comments
func (self *_parser) fetchComments() (comments []*ast.Comment) {
comments = self.comments
self.comments = nil
return comments
}
// consumeComments consumes the current comments and appends them to the provided node
func (self *_parser) consumeComments(node ast.Node, position ast.CommentPosition) {
self.commentMap.AddComments(node, self.comments, position)
self.comments = nil
}
...@@ -55,6 +55,7 @@ type _runtime struct { ...@@ -55,6 +55,7 @@ type _runtime struct {
otto *Otto otto *Otto
eval *_object // The builtin eval, for determine indirect versus direct invocation eval *_object // The builtin eval, for determine indirect versus direct invocation
debugger func(*Otto) debugger func(*Otto)
random func() float64
labels []string // FIXME labels []string // FIXME
lck sync.Mutex lck sync.Mutex
......
...@@ -18,8 +18,11 @@ ...@@ -18,8 +18,11 @@
package jsre package jsre
import ( import (
crand "crypto/rand"
"encoding/binary"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"math/rand"
"sync" "sync"
"time" "time"
...@@ -70,6 +73,18 @@ func New(assetPath string) *JSRE { ...@@ -70,6 +73,18 @@ func New(assetPath string) *JSRE {
return re return re
} }
// randomSource returns a pseudo random value generator.
func randomSource() *rand.Rand {
bytes := make([]byte, 8)
seed := time.Now().UnixNano()
if _, err := crand.Read(bytes); err == nil {
seed = int64(binary.LittleEndian.Uint64(bytes))
}
src := rand.NewSource(seed)
return rand.New(src)
}
// This function runs the main event loop from a goroutine that is started // This function runs the main event loop from a goroutine that is started
// when JSRE is created. Use Stop() before exiting to properly stop it. // when JSRE is created. Use Stop() before exiting to properly stop it.
// The event loop processes vm access requests from the evalQueue in a // The event loop processes vm access requests from the evalQueue in a
...@@ -81,6 +96,9 @@ func New(assetPath string) *JSRE { ...@@ -81,6 +96,9 @@ func New(assetPath string) *JSRE {
// called from JS through an RPC call. // called from JS through an RPC call.
func (self *JSRE) runEventLoop() { func (self *JSRE) runEventLoop() {
vm := otto.New() vm := otto.New()
r := randomSource()
vm.SetRandomSource(r.Float64)
registry := map[*jsTimer]*jsTimer{} registry := map[*jsTimer]*jsTimer{}
ready := make(chan *jsTimer) ready := make(chan *jsTimer)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment