Ver Fonte

Use source name provided by source map. See #235.

Dmitry Panov há 4 anos atrás
pai
commit
6b6d5e2b5d
9 ficheiros alterados com 124 adições e 173 exclusões
  1. 0 3
      ast/node.go
  2. 3 3
      compiler.go
  3. 99 19
      file/file.go
  4. 6 6
      file/file_test.go
  5. 1 37
      parser/parser.go
  6. 4 3
      parser/statement.go
  7. 10 9
      runtime.go
  8. 0 92
      srcfile.go
  9. 1 1
      vm.go

+ 0 - 3
ast/node.go

@@ -13,7 +13,6 @@ import (
 	"github.com/dop251/goja/file"
 	"github.com/dop251/goja/file"
 	"github.com/dop251/goja/token"
 	"github.com/dop251/goja/token"
 	"github.com/dop251/goja/unistring"
 	"github.com/dop251/goja/unistring"
-	"github.com/go-sourcemap/sourcemap"
 )
 )
 
 
 // All nodes implement the Node interface.
 // All nodes implement the Node interface.
@@ -399,8 +398,6 @@ type Program struct {
 	DeclarationList []Declaration
 	DeclarationList []Declaration
 
 
 	File *file.File
 	File *file.File
-
-	SourceMap *sourcemap.Consumer
 }
 }
 
 
 // ==== //
 // ==== //

+ 3 - 3
compiler.go

@@ -21,7 +21,7 @@ const (
 
 
 type CompilerError struct {
 type CompilerError struct {
 	Message string
 	Message string
-	File    *SrcFile
+	File    *file.File
 	Offset  int
 	Offset  int
 }
 }
 
 
@@ -43,7 +43,7 @@ type Program struct {
 	values []Value
 	values []Value
 
 
 	funcName unistring.String
 	funcName unistring.String
-	src      *SrcFile
+	src      *file.File
 	srcMap   []srcMapItem
 	srcMap   []srcMapItem
 }
 }
 
 
@@ -250,7 +250,7 @@ func (c *compiler) markBlockStart() {
 }
 }
 
 
 func (c *compiler) compile(in *ast.Program) {
 func (c *compiler) compile(in *ast.Program) {
-	c.p.src = NewSrcFile(in.File.Name(), in.File.Source(), in.SourceMap)
+	c.p.src = in.File
 
 
 	if len(in.Body) > 0 {
 	if len(in.Body) > 0 {
 		if !c.scope.strict {
 		if !c.scope.strict {

+ 99 - 19
file/file.go

@@ -4,7 +4,11 @@ package file
 
 
 import (
 import (
 	"fmt"
 	"fmt"
-	"strings"
+	"path"
+	"sort"
+	"sync"
+
+	"github.com/go-sourcemap/sourcemap"
 )
 )
 
 
 // Idx is a compact encoding of a source position within a file set.
 // Idx is a compact encoding of a source position within a file set.
@@ -16,7 +20,6 @@ type Idx int
 // including the filename, line, and column location.
 // including the filename, line, and column location.
 type Position struct {
 type Position struct {
 	Filename string // The filename where the error occurred, if any
 	Filename string // The filename where the error occurred, if any
-	Offset   int    // The src offset
 	Line     int    // The line number, starting at 1
 	Line     int    // The line number, starting at 1
 	Column   int    // The column number, starting at 1 (The character count)
 	Column   int    // The column number, starting at 1 (The character count)
 
 
@@ -35,7 +38,7 @@ func (self *Position) isValid() bool {
 //	file                An invalid position with filename
 //	file                An invalid position with filename
 //	-                   An invalid position without filename
 //	-                   An invalid position without filename
 //
 //
-func (self *Position) String() string {
+func (self Position) String() string {
 	str := self.Filename
 	str := self.Filename
 	if self.isValid() {
 	if self.isValid() {
 		if str != "" {
 		if str != "" {
@@ -89,29 +92,23 @@ func (self *FileSet) File(idx Idx) *File {
 }
 }
 
 
 // Position converts an Idx in the FileSet into a Position.
 // Position converts an Idx in the FileSet into a Position.
-func (self *FileSet) Position(idx Idx) *Position {
-	position := &Position{}
+func (self *FileSet) Position(idx Idx) Position {
 	for _, file := range self.files {
 	for _, file := range self.files {
 		if idx <= Idx(file.base+len(file.src)) {
 		if idx <= Idx(file.base+len(file.src)) {
-			offset := int(idx) - file.base
-			src := file.src[:offset]
-			position.Filename = file.name
-			position.Offset = offset
-			position.Line = 1 + strings.Count(src, "\n")
-			if index := strings.LastIndex(src, "\n"); index >= 0 {
-				position.Column = offset - index
-			} else {
-				position.Column = 1 + len(src)
-			}
+			return file.Position(int(idx) - file.base)
 		}
 		}
 	}
 	}
-	return position
+	return Position{}
 }
 }
 
 
 type File struct {
 type File struct {
-	name string
-	src  string
-	base int // This will always be 1 or greater
+	mu                sync.Mutex
+	name              string
+	src               string
+	base              int // This will always be 1 or greater
+	sourceMap         *sourcemap.Consumer
+	lineOffsets       []int
+	lastScannedOffset int
 }
 }
 
 
 func NewFile(filename, src string, base int) *File {
 func NewFile(filename, src string, base int) *File {
@@ -133,3 +130,86 @@ func (fl *File) Source() string {
 func (fl *File) Base() int {
 func (fl *File) Base() int {
 	return fl.base
 	return fl.base
 }
 }
+
+func (fl *File) SetSourceMap(m *sourcemap.Consumer) {
+	fl.sourceMap = m
+}
+
+func (fl *File) Position(offset int) Position {
+	var line int
+	var lineOffsets []int
+	fl.mu.Lock()
+	if offset > fl.lastScannedOffset {
+		line = fl.scanTo(offset)
+		lineOffsets = fl.lineOffsets
+		fl.mu.Unlock()
+	} else {
+		lineOffsets = fl.lineOffsets
+		fl.mu.Unlock()
+		line = sort.Search(len(lineOffsets), func(x int) bool { return lineOffsets[x] > offset }) - 1
+	}
+
+	var lineStart int
+	if line >= 0 {
+		lineStart = lineOffsets[line]
+	}
+
+	row := line + 2
+	col := offset - lineStart + 1
+
+	if fl.sourceMap != nil {
+		if source, _, row, col, ok := fl.sourceMap.Source(row, col); ok {
+			if !path.IsAbs(source) {
+				source = path.Join(path.Dir(fl.name), source)
+			}
+			return Position{
+				Filename: source,
+				Line:     row,
+				Column:   col,
+			}
+		}
+	}
+
+	return Position{
+		Filename: fl.name,
+		Line:     row,
+		Column:   col,
+	}
+}
+
+func findNextLineStart(s string) int {
+	for pos, ch := range s {
+		switch ch {
+		case '\r':
+			if pos < len(s)-1 && s[pos+1] == '\n' {
+				return pos + 2
+			}
+			return pos + 1
+		case '\n':
+			return pos + 1
+		case '\u2028', '\u2029':
+			return pos + 3
+		}
+	}
+	return -1
+}
+
+func (fl *File) scanTo(offset int) int {
+	o := fl.lastScannedOffset
+	for o < offset {
+		p := findNextLineStart(fl.src[o:])
+		if p == -1 {
+			fl.lastScannedOffset = len(fl.src)
+			return len(fl.lineOffsets) - 1
+		}
+		o = o + p
+		fl.lineOffsets = append(fl.lineOffsets, o)
+	}
+	fl.lastScannedOffset = o
+
+	if o == offset {
+		return len(fl.lineOffsets) - 1
+	}
+
+	return len(fl.lineOffsets) - 2
+}

+ 6 - 6
srcfile_test.go → file/file_test.go

@@ -1,4 +1,4 @@
-package goja
+package file
 
 
 import "testing"
 import "testing"
 
 
@@ -6,7 +6,7 @@ func TestPosition(t *testing.T) {
 	const SRC = `line1
 	const SRC = `line1
 line2
 line2
 line3`
 line3`
-	f := NewSrcFile("", SRC, nil)
+	f := NewFile("", SRC, 0)
 
 
 	tests := []struct {
 	tests := []struct {
 		offset int
 		offset int
@@ -27,17 +27,17 @@ line3`
 	}
 	}
 
 
 	for i, test := range tests {
 	for i, test := range tests {
-		if p := f.Position(test.offset); p.Line != test.line || p.Col != test.col {
-			t.Fatalf("%d. Line: %d, col: %d", i, p.Line, p.Col)
+		if p := f.Position(test.offset); p.Line != test.line || p.Column != test.col {
+			t.Fatalf("%d. Line: %d, col: %d", i, p.Line, p.Column)
 		}
 		}
 	}
 	}
 }
 }
 
 
-func TestSrcFileConcurrency(t *testing.T) {
+func TestFileConcurrency(t *testing.T) {
 	const SRC = `line1
 	const SRC = `line1
 line2
 line2
 line3`
 line3`
-	f := NewSrcFile("", SRC, nil)
+	f := NewFile("", SRC, 0)
 	go func() {
 	go func() {
 		f.Position(12)
 		f.Position(12)
 	}()
 	}()

+ 1 - 37
parser/parser.go

@@ -264,42 +264,6 @@ func (self *_parser) expect(value token.Token) file.Idx {
 	return idx
 	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 {
 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
+	return self.file.Position(int(idx) - self.base)
 }
 }

+ 4 - 3
parser/statement.go

@@ -579,12 +579,13 @@ func (self *_parser) parseSourceElements() []ast.Statement {
 func (self *_parser) parseProgram() *ast.Program {
 func (self *_parser) parseProgram() *ast.Program {
 	self.openScope()
 	self.openScope()
 	defer self.closeScope()
 	defer self.closeScope()
-	return &ast.Program{
+	prg := &ast.Program{
 		Body:            self.parseSourceElements(),
 		Body:            self.parseSourceElements(),
 		DeclarationList: self.scope.declarationList,
 		DeclarationList: self.scope.declarationList,
 		File:            self.file,
 		File:            self.file,
-		SourceMap:       self.parseSourceMap(),
 	}
 	}
+	self.file.SetSourceMap(self.parseSourceMap())
+	return prg
 }
 }
 
 
 func extractSourceMapLine(str string) string {
 func extractSourceMapLine(str string) string {
@@ -624,7 +625,7 @@ func (self *_parser) parseSourceMap() *sourcemap.Consumer {
 			var smUrl *url.URL
 			var smUrl *url.URL
 			if smUrl, err = url.Parse(urlStr); err == nil {
 			if smUrl, err = url.Parse(urlStr); err == nil {
 				p := smUrl.Path
 				p := smUrl.Path
-				if !strings.HasPrefix(p, "/") {
+				if !path.IsAbs(p) {
 					baseName := self.file.Name()
 					baseName := self.file.Name()
 					baseUrl, err1 := url.Parse(baseName)
 					baseUrl, err1 := url.Parse(baseName)
 					if err1 == nil && baseUrl.Scheme != "" {
 					if err1 == nil && baseUrl.Scheme != "" {

+ 10 - 9
runtime.go

@@ -4,6 +4,7 @@ import (
 	"bytes"
 	"bytes"
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
+	"github.com/dop251/goja/file"
 	"go/ast"
 	"go/ast"
 	"hash/maphash"
 	"hash/maphash"
 	"math"
 	"math"
@@ -193,7 +194,7 @@ func (f *StackFrame) SrcName() string {
 	if f.prg == nil {
 	if f.prg == nil {
 		return "<native>"
 		return "<native>"
 	}
 	}
-	return f.prg.src.name
+	return f.prg.src.Name()
 }
 }
 
 
 func (f *StackFrame) FuncName() string {
 func (f *StackFrame) FuncName() string {
@@ -206,12 +207,9 @@ func (f *StackFrame) FuncName() string {
 	return f.funcName.String()
 	return f.funcName.String()
 }
 }
 
 
-func (f *StackFrame) Position() Position {
+func (f *StackFrame) Position() file.Position {
 	if f.prg == nil || f.prg.src == nil {
 	if f.prg == nil || f.prg.src == nil {
-		return Position{
-			0,
-			0,
-		}
+		return file.Position{}
 	}
 	}
 	return f.prg.src.Position(f.prg.sourceOffset(f.pc))
 	return f.prg.src.Position(f.prg.sourceOffset(f.pc))
 }
 }
@@ -222,13 +220,16 @@ func (f *StackFrame) Write(b *bytes.Buffer) {
 			b.WriteString(n.String())
 			b.WriteString(n.String())
 			b.WriteString(" (")
 			b.WriteString(" (")
 		}
 		}
-		if n := f.prg.src.name; n != "" {
-			b.WriteString(n)
+		p := f.Position()
+		if p.Filename != "" {
+			b.WriteString(p.Filename)
 		} else {
 		} else {
 			b.WriteString("<eval>")
 			b.WriteString("<eval>")
 		}
 		}
 		b.WriteByte(':')
 		b.WriteByte(':')
-		b.WriteString(f.Position().String())
+		b.WriteString(strconv.Itoa(p.Line))
+		b.WriteByte(':')
+		b.WriteString(strconv.Itoa(p.Column))
 		b.WriteByte('(')
 		b.WriteByte('(')
 		b.WriteString(strconv.Itoa(f.pc))
 		b.WriteString(strconv.Itoa(f.pc))
 		b.WriteByte(')')
 		b.WriteByte(')')

+ 0 - 92
srcfile.go

@@ -1,92 +0,0 @@
-package goja
-
-import (
-	"fmt"
-	"github.com/go-sourcemap/sourcemap"
-	"sort"
-	"strings"
-	"sync"
-)
-
-type Position struct {
-	Line, Col int
-}
-
-type SrcFile struct {
-	name string
-	src  string
-
-	lineOffsets       []int
-	lineOffsetsLock   sync.Mutex
-	lastScannedOffset int
-	sourceMap         *sourcemap.Consumer
-}
-
-func NewSrcFile(name, src string, sourceMap *sourcemap.Consumer) *SrcFile {
-	return &SrcFile{
-		name:      name,
-		src:       src,
-		sourceMap: sourceMap,
-	}
-}
-
-func (f *SrcFile) Position(offset int) Position {
-	var line int
-	var lineOffsets []int
-	f.lineOffsetsLock.Lock()
-	if offset > f.lastScannedOffset {
-		line = f.scanTo(offset)
-		lineOffsets = f.lineOffsets
-		f.lineOffsetsLock.Unlock()
-	} else {
-		lineOffsets = f.lineOffsets
-		f.lineOffsetsLock.Unlock()
-		line = sort.Search(len(lineOffsets), func(x int) bool { return lineOffsets[x] > offset }) - 1
-	}
-
-	var lineStart int
-	if line >= 0 {
-		lineStart = lineOffsets[line]
-	}
-
-	row := line + 2
-	col := offset - lineStart + 1
-
-	if f.sourceMap != nil {
-		if _, _, row, col, ok := f.sourceMap.Source(row, col); ok {
-			return Position{
-				Line: row,
-				Col:  col,
-			}
-		}
-	}
-
-	return Position{
-		Line: row,
-		Col:  col,
-	}
-}
-
-func (f *SrcFile) scanTo(offset int) int {
-	o := f.lastScannedOffset
-	for o < offset {
-		p := strings.Index(f.src[o:], "\n")
-		if p == -1 {
-			f.lastScannedOffset = len(f.src)
-			return len(f.lineOffsets) - 1
-		}
-		o = o + p + 1
-		f.lineOffsets = append(f.lineOffsets, o)
-	}
-	f.lastScannedOffset = o
-
-	if o == offset {
-		return len(f.lineOffsets) - 1
-	}
-
-	return len(f.lineOffsets) - 2
-}
-
-func (p Position) String() string {
-	return fmt.Sprintf("%d:%d", p.Line, p.Col)
-}

+ 1 - 1
vm.go

@@ -1946,7 +1946,7 @@ func (n *newFunc) exec(vm *vm) {
 	obj := vm.r.newFunc(n.name, int(n.length), n.strict)
 	obj := vm.r.newFunc(n.name, int(n.length), n.strict)
 	obj.prg = n.prg
 	obj.prg = n.prg
 	obj.stash = vm.stash
 	obj.stash = vm.stash
-	obj.src = n.prg.src.src[n.srcStart:n.srcEnd]
+	obj.src = n.prg.src.Source()[n.srcStart:n.srcEnd]
 	vm.push(obj.val)
 	vm.push(obj.val)
 	vm.pc++
 	vm.pc++
 }
 }