253 lines
6.1 KiB
Go
253 lines
6.1 KiB
Go
package sourcemap
|
|
|
|
import (
|
|
"errors"
|
|
"iter"
|
|
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/core"
|
|
)
|
|
|
|
type Mapping struct {
|
|
GeneratedLine int
|
|
GeneratedCharacter int
|
|
SourceIndex SourceIndex
|
|
SourceLine int
|
|
SourceCharacter int
|
|
NameIndex NameIndex
|
|
}
|
|
|
|
func (m *Mapping) Equals(other *Mapping) bool {
|
|
return m == other || m.GeneratedLine == other.GeneratedLine &&
|
|
m.GeneratedCharacter == other.GeneratedCharacter &&
|
|
m.SourceIndex == other.SourceIndex &&
|
|
m.SourceLine == other.SourceLine &&
|
|
m.SourceCharacter == other.SourceCharacter &&
|
|
m.NameIndex == other.NameIndex
|
|
}
|
|
|
|
func (m *Mapping) IsSourceMapping() bool {
|
|
return m.SourceIndex != MissingSource &&
|
|
m.SourceLine != MissingLineOrColumn &&
|
|
m.SourceCharacter != MissingLineOrColumn
|
|
}
|
|
|
|
const (
|
|
MissingSource SourceIndex = -1
|
|
MissingName NameIndex = -1
|
|
MissingLineOrColumn int = -1
|
|
)
|
|
|
|
type MappingsDecoder struct {
|
|
mappings string
|
|
done bool
|
|
pos int
|
|
generatedLine int
|
|
generatedCharacter int
|
|
sourceIndex SourceIndex
|
|
sourceLine int
|
|
sourceCharacter int
|
|
nameIndex NameIndex
|
|
error error
|
|
mappingPool core.Pool[Mapping]
|
|
}
|
|
|
|
func DecodeMappings(mappings string) *MappingsDecoder {
|
|
return &MappingsDecoder{mappings: mappings}
|
|
}
|
|
|
|
func (d *MappingsDecoder) MappingsString() string {
|
|
return d.mappings
|
|
}
|
|
|
|
func (d *MappingsDecoder) Pos() int {
|
|
return d.pos
|
|
}
|
|
|
|
func (d *MappingsDecoder) Error() error {
|
|
return d.error
|
|
}
|
|
|
|
func (d *MappingsDecoder) State() *Mapping {
|
|
return d.captureMapping(true /*hasSource*/, true /*hasName*/)
|
|
}
|
|
|
|
func (d *MappingsDecoder) Values() iter.Seq[*Mapping] {
|
|
return func(yield func(*Mapping) bool) {
|
|
for value, done := d.Next(); !done; value, done = d.Next() {
|
|
if !yield(value) {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (d *MappingsDecoder) Next() (value *Mapping, done bool) {
|
|
for !d.done && d.pos < len(d.mappings) {
|
|
ch := d.mappings[d.pos]
|
|
if ch == ';' {
|
|
// new line
|
|
d.generatedLine++
|
|
d.generatedCharacter = 0
|
|
d.pos++
|
|
continue
|
|
}
|
|
|
|
if ch == ',' {
|
|
// Next entry is on same line - no action needed
|
|
d.pos++
|
|
continue
|
|
}
|
|
|
|
hasSource := false
|
|
hasName := false
|
|
d.generatedCharacter += d.base64VLQFormatDecode()
|
|
if d.hasReportedError() {
|
|
return d.stopIterating()
|
|
}
|
|
if d.generatedCharacter < 0 {
|
|
return d.setErrorAndStopIterating("Invalid generatedCharacter found")
|
|
}
|
|
|
|
if !d.isSourceMappingSegmentEnd() {
|
|
hasSource = true
|
|
|
|
d.sourceIndex += SourceIndex(d.base64VLQFormatDecode())
|
|
if d.hasReportedError() {
|
|
return d.stopIterating()
|
|
}
|
|
if d.sourceIndex < 0 {
|
|
return d.setErrorAndStopIterating("Invalid sourceIndex found")
|
|
}
|
|
if d.isSourceMappingSegmentEnd() {
|
|
return d.setErrorAndStopIterating("Unsupported Format: No entries after sourceIndex")
|
|
}
|
|
|
|
d.sourceLine += d.base64VLQFormatDecode()
|
|
if d.hasReportedError() {
|
|
return d.stopIterating()
|
|
}
|
|
if d.sourceLine < 0 {
|
|
return d.setErrorAndStopIterating("Invalid sourceLine found")
|
|
}
|
|
if d.isSourceMappingSegmentEnd() {
|
|
return d.setErrorAndStopIterating("Unsupported Format: No entries after sourceLine")
|
|
}
|
|
|
|
d.sourceCharacter += d.base64VLQFormatDecode()
|
|
if d.hasReportedError() {
|
|
return d.stopIterating()
|
|
}
|
|
if d.sourceCharacter < 0 {
|
|
return d.setErrorAndStopIterating("Invalid sourceCharacter found")
|
|
}
|
|
|
|
if !d.isSourceMappingSegmentEnd() {
|
|
hasName = true
|
|
d.nameIndex += NameIndex(d.base64VLQFormatDecode())
|
|
if d.hasReportedError() {
|
|
return d.stopIterating()
|
|
}
|
|
if d.nameIndex < 0 {
|
|
return d.setErrorAndStopIterating("Invalid nameIndex found")
|
|
}
|
|
|
|
if !d.isSourceMappingSegmentEnd() {
|
|
return d.setErrorAndStopIterating("Unsupported Error Format: Entries after nameIndex")
|
|
}
|
|
}
|
|
}
|
|
|
|
return d.captureMapping(hasSource, hasName), false
|
|
}
|
|
|
|
return d.stopIterating()
|
|
}
|
|
|
|
func (d *MappingsDecoder) captureMapping(hasSource bool, hasName bool) *Mapping {
|
|
mapping := d.mappingPool.New()
|
|
mapping.GeneratedLine = d.generatedLine
|
|
mapping.GeneratedCharacter = d.generatedCharacter
|
|
mapping.SourceIndex = core.IfElse(hasSource, d.sourceIndex, MissingSource)
|
|
mapping.SourceLine = core.IfElse(hasSource, d.sourceLine, MissingLineOrColumn)
|
|
mapping.SourceCharacter = core.IfElse(hasSource, d.sourceCharacter, MissingLineOrColumn)
|
|
mapping.NameIndex = core.IfElse(hasName, d.nameIndex, MissingName)
|
|
return mapping
|
|
}
|
|
|
|
func (d *MappingsDecoder) stopIterating() (*Mapping, bool) {
|
|
d.done = true
|
|
return nil, true
|
|
}
|
|
|
|
func (d *MappingsDecoder) setError(err string) {
|
|
d.error = errors.New(err)
|
|
}
|
|
|
|
func (d *MappingsDecoder) setErrorAndStopIterating(err string) (*Mapping, bool) {
|
|
d.setError(err)
|
|
return d.stopIterating()
|
|
}
|
|
|
|
func (d *MappingsDecoder) hasReportedError() bool {
|
|
return d.error != nil
|
|
}
|
|
|
|
func (d *MappingsDecoder) isSourceMappingSegmentEnd() bool {
|
|
return d.pos == len(d.mappings) || d.mappings[d.pos] == ',' || d.mappings[d.pos] == ';'
|
|
}
|
|
|
|
func (d *MappingsDecoder) base64VLQFormatDecode() int {
|
|
moreDigits := true
|
|
shiftCount := 0
|
|
value := 0
|
|
for ; moreDigits; d.pos++ {
|
|
if d.pos >= len(d.mappings) {
|
|
d.setError("Error in decoding base64VLQFormatDecode, past the mapping string")
|
|
return -1
|
|
}
|
|
|
|
// 6 digit number
|
|
currentByte := base64FormatDecode(d.mappings[d.pos])
|
|
if currentByte == -1 {
|
|
d.setError("Invalid character in VLQ")
|
|
return -1
|
|
}
|
|
|
|
// If msb is set, we still have more bits to continue
|
|
moreDigits = (currentByte & 32) != 0
|
|
|
|
// least significant 5 bits are the next msbs in the final value.
|
|
value = value | ((currentByte & 31) << shiftCount)
|
|
shiftCount += 5
|
|
}
|
|
|
|
// Least significant bit if 1 represents negative and rest of the msb is actual absolute value
|
|
if (value & 1) == 0 {
|
|
// + number
|
|
value = value >> 1
|
|
} else {
|
|
// - number
|
|
value = value >> 1
|
|
value = -value
|
|
}
|
|
|
|
return value
|
|
}
|
|
|
|
func base64FormatDecode(ch byte) int {
|
|
switch {
|
|
case ch >= 'A' && ch <= 'Z':
|
|
return int(ch - 'A')
|
|
case ch >= 'a' && ch <= 'z':
|
|
return int(ch - 'a' + 26)
|
|
case ch >= '0' && ch <= '9':
|
|
return int(ch - '0' + 52)
|
|
case ch == '+':
|
|
return 62
|
|
case ch == '/':
|
|
return 63
|
|
default:
|
|
return -1
|
|
}
|
|
}
|