mv x/<tool> => xtool/<tool>

This commit is contained in:
xushiwei
2024-05-11 05:27:38 +08:00
parent 67896c63a7
commit cd266213ce
25 changed files with 13 additions and 13 deletions

View File

@@ -1,60 +0,0 @@
/*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ar
import (
"errors"
"time"
)
var (
errInvalidHeader = errors.New("ar: invalid header")
errWriteTooLong = errors.New("ar: write too long")
)
const (
globalHeader = "!<arch>\n"
globalHeaderLen = len(globalHeader)
headerByteSize = 60
)
type recHeader struct {
name [16]byte
modTime [12]byte
uid [6]byte
gid [6]byte
mode [8]byte
size [10]byte
eol [2]byte
}
type Header struct {
Name string
ModTime time.Time
Uid int
Gid int
Mode int64
Size int64
}
type slicer []byte
func (sp *slicer) next(n int) (b []byte) {
s := *sp
b, *sp = s[0:n], s[n:]
return
}

View File

@@ -1,140 +0,0 @@
/*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ar
import (
"io"
"strconv"
"strings"
"unsafe"
)
// Provides read access to an ar archive.
// Call next to skip files
//
// Example:
// reader := NewReader(f)
// var buf bytes.Buffer
// for {
// _, err := reader.Next()
// if err == io.EOF {
// break
// }
// if err != nil {
// t.Errorf(err.Error())
// }
// io.Copy(&buf, reader)
// }
type Reader struct {
r io.Reader
nb int64
pad int64
}
// Copies read data to r. Strips the global ar header.
func NewReader(r io.Reader) (*Reader, error) {
buf := make([]byte, globalHeaderLen)
if _, err := io.ReadFull(r, buf); err != nil {
return nil, err
}
if string(buf) != globalHeader {
return nil, errInvalidHeader
}
return &Reader{r: r}, nil
}
func stringVal(b []byte) string {
return strings.TrimRight(string(b), " ")
}
func intVal(b []byte) (int64, error) {
return strconv.ParseInt(stringVal(b), 10, 64)
}
func (rd *Reader) skipUnread() error {
skip := rd.nb + rd.pad
rd.nb, rd.pad = 0, 0
if seeker, ok := rd.r.(io.Seeker); ok {
_, err := seeker.Seek(skip, io.SeekCurrent)
return err
}
_, err := io.CopyN(io.Discard, rd.r, skip)
return err
}
func (rd *Reader) readHeader() (header *Header, err error) {
var rec recHeader
var buf = (*[headerByteSize]byte)(unsafe.Pointer(&rec))[:]
if _, err = io.ReadFull(rd.r, buf); err != nil {
return
}
header = new(Header)
header.Name = stringVal(rec.name[:])
if header.Size, err = intVal(rec.size[:]); err != nil {
return
}
if header.Size%2 == 1 {
rd.pad = 1
} else {
rd.pad = 0
}
if rec.name[0] == '#' {
if n, e := strconv.ParseInt(strings.TrimPrefix(header.Name[3:], "#1/"), 10, 64); e == nil {
name := make([]byte, n)
if _, err = io.ReadFull(rd.r, name); err != nil {
return
}
header.Name = string(name)
header.Size -= n
}
}
rd.nb = int64(header.Size)
return
}
// Call Next() to skip to the next file in the archive file.
// Returns a Header which contains the metadata about the
// file in the archive.
func (rd *Reader) Next() (*Header, error) {
err := rd.skipUnread()
if err != nil {
return nil, err
}
return rd.readHeader()
}
// Read data from the current entry in the archive.
func (rd *Reader) Read(b []byte) (n int, err error) {
if rd.nb == 0 {
return 0, io.EOF
}
if int64(len(b)) > rd.nb {
b = b[0:rd.nb]
}
n, err = rd.r.Read(b)
rd.nb -= int64(n)
return
}

View File

@@ -1,116 +0,0 @@
/*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ar
import (
"io"
"strconv"
)
// Writer provides sequential writing of an ar archive.
// An ar archive is sequence of header file pairs
// Call WriteHeader to begin writing a new file, then call Write to supply the file's data
//
// Example:
// archive := ar.NewWriter(writer)
// archive.WriteGlobalHeader()
// header := new(ar.Header)
// header.Size = 15 // bytes
//
// if err := archive.WriteHeader(header); err != nil {
// return err
// }
//
// io.Copy(archive, data)
type Writer struct {
w io.Writer
nb int64 // number of unwritten bytes for the current file entry
}
// Create a new ar writer that writes to w
func NewWriter(w io.Writer) *Writer { return &Writer{w: w} }
func (aw *Writer) numeric(b []byte, x int64) {
s := strconv.FormatInt(x, 10)
for len(s) < len(b) {
s = s + " "
}
copy(b, []byte(s))
}
func (aw *Writer) octal(b []byte, x int64) {
s := "100" + strconv.FormatInt(x, 8)
for len(s) < len(b) {
s = s + " "
}
copy(b, []byte(s))
}
func (aw *Writer) string(b []byte, str string) {
s := str
for len(s) < len(b) {
s = s + " "
}
copy(b, []byte(s))
}
// Writes to the current entry in the ar archive
// Returns ErrWriteTooLong if more than header.Size
// bytes are written after a call to WriteHeader
func (aw *Writer) Write(b []byte) (n int, err error) {
if int64(len(b)) > aw.nb {
b = b[0:aw.nb]
err = errWriteTooLong
}
n, werr := aw.w.Write(b)
aw.nb -= int64(n)
if werr != nil {
return n, werr
}
if len(b)%2 == 1 { // data size must be aligned to an even byte
n2, _ := aw.w.Write([]byte{'\n'})
return n + n2, err
}
return
}
func (aw *Writer) WriteGlobalHeader() error {
_, err := aw.w.Write([]byte(globalHeader))
return err
}
// Writes the header to the underlying writer and prepares
// to receive the file payload
func (aw *Writer) WriteHeader(hdr *Header) error {
aw.nb = int64(hdr.Size)
header := make([]byte, headerByteSize)
s := slicer(header)
aw.string(s.next(16), hdr.Name)
aw.numeric(s.next(12), hdr.ModTime.Unix())
aw.numeric(s.next(6), int64(hdr.Uid))
aw.numeric(s.next(6), int64(hdr.Gid))
aw.octal(s.next(8), hdr.Mode)
aw.numeric(s.next(10), hdr.Size)
aw.string(s.next(2), "`\n")
_, err := aw.w.Write(header)
return err
}

View File

@@ -1,262 +0,0 @@
/*
* Copyright (c) 2022 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ast
// -----------------------------------------------------------------------------
type IncludedFrom struct {
File string `json:"file"`
}
type Loc struct {
Offset int64 `json:"offset,omitempty"` // 432
File string `json:"file,omitempty"` // "sqlite3.i"
Line int `json:"line,omitempty"`
PresumedFile string `json:"presumedFile,omitempty"`
PresumedLine int `json:"presumedLine,omitempty"`
Col int `json:"col,omitempty"`
TokLen int `json:"tokLen,omitempty"`
IncludedFrom *IncludedFrom `json:"includedFrom,omitempty"` // "sqlite3.c"
}
type Pos struct {
Offset int64 `json:"offset,omitempty"`
Col int `json:"col,omitempty"`
TokLen int `json:"tokLen,omitempty"`
IncludedFrom *IncludedFrom `json:"includedFrom,omitempty"` // "sqlite3.c"
SpellingLoc *Loc `json:"spellingLoc,omitempty"`
ExpansionLoc *Loc `json:"expansionLoc,omitempty"`
}
type Range struct {
Begin Pos `json:"begin"`
End Pos `json:"end"`
}
// -----------------------------------------------------------------------------
type ID string
type Kind string
const (
TranslationUnitDecl Kind = "TranslationUnitDecl"
TypedefType Kind = "TypedefType"
TypedefDecl Kind = "TypedefDecl"
ElaboratedType Kind = "ElaboratedType"
BuiltinType Kind = "BuiltinType"
ConstantArrayType Kind = "ConstantArrayType"
IncompleteArrayType Kind = "IncompleteArrayType"
PointerType Kind = "PointerType"
RecordType Kind = "RecordType"
RecordDecl Kind = "RecordDecl"
FieldDecl Kind = "FieldDecl"
IndirectFieldDecl Kind = "IndirectFieldDecl"
VarDecl Kind = "VarDecl"
EmptyDecl Kind = "EmptyDecl"
EnumDecl Kind = "EnumDecl"
EnumConstantDecl Kind = "EnumConstantDecl"
AlwaysInlineAttr Kind = "AlwaysInlineAttr"
AsmLabelAttr Kind = "AsmLabelAttr"
AvailabilityAttr Kind = "AvailabilityAttr"
DeprecatedAttr Kind = "DeprecatedAttr"
BuiltinAttr Kind = "BuiltinAttr"
FormatAttr Kind = "FormatAttr"
FormatArgAttr Kind = "FormatArgAttr"
ColdAttr Kind = "ColdAttr"
ConstAttr Kind = "ConstAttr"
PureAttr Kind = "PureAttr"
PackedAttr Kind = "PackedAttr"
GNUInlineAttr Kind = "GNUInlineAttr"
StrictFPAttr Kind = "StrictFPAttr"
ReturnsTwiceAttr Kind = "ReturnsTwiceAttr"
RestrictAttr Kind = "RestrictAttr"
NoThrowAttr Kind = "NoThrowAttr"
NoInlineAttr Kind = "NoInlineAttr"
NoSanitizeAttr Kind = "NoSanitizeAttr"
NonNullAttr Kind = "NonNullAttr"
MayAliasAttr Kind = "MayAliasAttr"
MSAllocatorAttr Kind = "MSAllocatorAttr"
MaxFieldAlignmentAttr Kind = "MaxFieldAlignmentAttr"
WarnUnusedResultAttr Kind = "WarnUnusedResultAttr"
AllocSizeAttr Kind = "AllocSizeAttr"
AlignedAttr Kind = "AlignedAttr"
VisibilityAttr Kind = "VisibilityAttr"
C11NoReturnAttr Kind = "C11NoReturnAttr"
FunctionProtoType Kind = "FunctionProtoType"
FunctionDecl Kind = "FunctionDecl"
ParmVarDecl Kind = "ParmVarDecl"
ParenType Kind = "ParenType"
DeclStmt Kind = "DeclStmt"
CompoundStmt Kind = "CompoundStmt"
NullStmt Kind = "NullStmt"
ForStmt Kind = "ForStmt"
WhileStmt Kind = "WhileStmt"
DoStmt Kind = "DoStmt"
GotoStmt Kind = "GotoStmt"
BreakStmt Kind = "BreakStmt"
ContinueStmt Kind = "ContinueStmt"
LabelStmt Kind = "LabelStmt"
IfStmt Kind = "IfStmt"
SwitchStmt Kind = "SwitchStmt"
CaseStmt Kind = "CaseStmt"
DefaultStmt Kind = "DefaultStmt"
ReturnStmt Kind = "ReturnStmt"
GCCAsmStmt Kind = "GCCAsmStmt"
ParenExpr Kind = "ParenExpr"
CallExpr Kind = "CallExpr"
ConstantExpr Kind = "ConstantExpr"
InitListExpr Kind = "InitListExpr"
CStyleCastExpr Kind = "CStyleCastExpr"
DeclRefExpr Kind = "DeclRefExpr"
MemberExpr Kind = "MemberExpr"
ImplicitCastExpr Kind = "ImplicitCastExpr"
ImplicitValueInitExpr Kind = "ImplicitValueInitExpr"
UnaryExprOrTypeTraitExpr Kind = "UnaryExprOrTypeTraitExpr"
OffsetOfExpr Kind = "OffsetOfExpr"
ArraySubscriptExpr Kind = "ArraySubscriptExpr"
AtomicExpr Kind = "AtomicExpr"
VAArgExpr Kind = "VAArgExpr"
CompoundAssignOperator Kind = "CompoundAssignOperator"
BinaryOperator Kind = "BinaryOperator"
UnaryOperator Kind = "UnaryOperator"
ConditionalOperator Kind = "ConditionalOperator"
CompoundLiteralExpr Kind = "CompoundLiteralExpr"
PredefinedExpr Kind = "PredefinedExpr"
CharacterLiteral Kind = "CharacterLiteral"
IntegerLiteral Kind = "IntegerLiteral"
StringLiteral Kind = "StringLiteral"
FloatingLiteral Kind = "FloatingLiteral"
ImaginaryLiteral Kind = "ImaginaryLiteral"
AllocAlignAttr Kind = "AllocAlignAttr"
DisableTailCallsAttr Kind = "DisableTailCallsAttr"
StaticAssertDecl Kind = "StaticAssertDecl"
)
type ValueCategory string
const (
RValue ValueCategory = "rvalue"
PRValue ValueCategory = "prvalue"
LValue ValueCategory = "lvalue"
)
type CC string
const (
CDecl CC = "cdecl"
)
type StorageClass string
const (
Static StorageClass = "static"
Extern StorageClass = "extern"
)
type CastKind string
const (
LValueToRValue CastKind = "LValueToRValue"
BitCast CastKind = "BitCast"
FloatingToIntegral CastKind = "FloatingToIntegral"
FloatingComplexCast CastKind = "FloatingComplexCast"
FloatingRealToComplex CastKind = "FloatingRealToComplex"
IntegralRealToComplex CastKind = "IntegralRealToComplex"
FloatingCast CastKind = "FloatingCast"
IntegralCast CastKind = "IntegralCast"
IntegralToPointer CastKind = "IntegralToPointer"
IntegralToFloating CastKind = "IntegralToFloating"
IntegralToBoolean CastKind = "IntegralToBoolean"
FloatingToBoolean CastKind = "FloatingToBoolean"
IntegralComplexToBoolean CastKind = "IntegralComplexToBoolean"
FloatingComplexToBoolean CastKind = "FloatingComplexToBoolean"
PointerToBoolean CastKind = "PointerToBoolean"
PointerToIntegral CastKind = "PointerToIntegral"
FunctionToPointerDecay CastKind = "FunctionToPointerDecay"
ArrayToPointerDecay CastKind = "ArrayToPointerDecay"
BuiltinFnToFnPtr CastKind = "BuiltinFnToFnPtr"
ToVoid CastKind = "ToVoid"
NullToPointer CastKind = "NullToPointer"
NoOp CastKind = "NoOp"
)
type (
// OpCode can be:
// + - * / || >= -- ++ etc
OpCode string
)
type Type struct {
// QualType can be:
// unsigned int
// struct ConstantString
// volatile uint32_t
// int (*)(void *, int, char **, char **)
// int (*)(const char *, ...)
// int (*)(void)
// const char *restrict
// const char [7]
// char *
// void
// ...
QualType string `json:"qualType"`
DesugaredQualType string `json:"desugaredQualType,omitempty"`
TypeAliasDeclID ID `json:"typeAliasDeclId,omitempty"`
}
type Node struct {
ID ID `json:"id,omitempty"`
Kind Kind `json:"kind,omitempty"`
Loc *Loc `json:"loc,omitempty"`
Range *Range `json:"range,omitempty"`
ReferencedMemberDecl ID `json:"referencedMemberDecl,omitempty"`
PreviousDecl ID `json:"previousDecl,omitempty"`
ParentDeclContextID ID `json:"parentDeclContextId,omitempty"`
IsImplicit bool `json:"isImplicit,omitempty"` // is this type implicit defined
IsReferenced bool `json:"isReferenced,omitempty"` // is this type refered or not
IsUsed bool `json:"isUsed,omitempty"` // is this variable used or not
IsArrow bool `json:"isArrow,omitempty"` // is ptr->member not obj.member
IsPostfix bool `json:"isPostfix,omitempty"`
IsPartOfExplicitCast bool `json:"isPartOfExplicitCast,omitempty"`
IsBitfield bool `json:"isBitfield,omitempty"`
Inline bool `json:"inline,omitempty"`
StorageClass StorageClass `json:"storageClass,omitempty"`
TagUsed string `json:"tagUsed,omitempty"` // struct | union
HasElse bool `json:"hasElse,omitempty"`
CompleteDefinition bool `json:"completeDefinition,omitempty"`
Complicated bool `json:"-"` // complicated statement
Variadic bool `json:"variadic,omitempty"`
Name string `json:"name,omitempty"`
MangledName string `json:"mangledName,omitempty"`
Type *Type `json:"type,omitempty"`
CC CC `json:"cc,omitempty"`
Field *Node `json:"field,omitempty"`
Decl *Node `json:"decl,omitempty"`
OwnedTagDecl *Node `json:"ownedTagDecl,omitempty"`
ReferencedDecl *Node `json:"referencedDecl,omitempty"`
OpCode OpCode `json:"opcode,omitempty"`
Init string `json:"init,omitempty"`
ValueCategory ValueCategory `json:"valueCategory,omitempty"`
Value interface{} `json:"value,omitempty"`
CastKind CastKind `json:"castKind,omitempty"`
Size int `json:"size,omitempty"` // array size
Inner []*Node `json:"inner,omitempty"`
ArrayFiller []*Node `json:"array_filler,omitempty"`
}
// -----------------------------------------------------------------------------

View File

@@ -1,50 +0,0 @@
/*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package clang
import (
"io"
"os"
"os/exec"
)
// -----------------------------------------------------------------------------
// Cmd represents a clang command.
type Cmd struct {
app string
Stdout io.Writer
Stderr io.Writer
}
// New creates a new clang command.
func New(app string) *Cmd {
if app == "" {
app = "clang"
}
return &Cmd{app, os.Stdout, os.Stderr}
}
func (p *Cmd) Exec(args ...string) error {
cmd := exec.Command(p.app, args...)
cmd.Stdout = p.Stdout
cmd.Stderr = p.Stderr
return cmd.Run()
}
// -----------------------------------------------------------------------------

View File

@@ -1,122 +0,0 @@
/*
* Copyright (c) 2022 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package parser
// -----------------------------------------------------------------------------
const pageSize = 1024 * 1024
type PagedWriter struct {
pages []*[pageSize]byte
last *[pageSize]byte
off int
}
func NewPagedWriter() *PagedWriter {
return &PagedWriter{last: new([pageSize]byte)}
}
func (p *PagedWriter) Write(buf []byte) (written int, err error) {
for {
n := copy(p.last[p.off:], buf[written:])
written += n
if written >= len(buf) {
p.off += n
return
}
p.pages = append(p.pages, p.last)
p.last, p.off = new([pageSize]byte), 0
}
}
func (p *PagedWriter) Len() int {
return len(p.pages)*pageSize + p.off
}
func (p *PagedWriter) Bytes() []byte {
out, n := make([]byte, p.Len()), 0
for _, page := range p.pages {
n += copy(out[n:], page[:])
}
copy(out[n:], p.last[:p.off])
return out
}
/*
func (p *PagedWriter) ToReader() *PagedReader {
return &PagedReader{src: p, curr: p.getPage(0)}
}
func (p *PagedWriter) getPage(ipage int) []byte {
if ipage == len(p.pages) { // last page
return p.last[:p.off]
}
return p.pages[ipage][:]
}
// -----------------------------------------------------------------------------
type PagedReader struct {
src *PagedWriter
curr []byte
off int
ipage int
}
func (p *PagedReader) WriteTo(w io.Writer) (written int64, err error) {
n, err := w.Write(p.curr[p.off:])
written = int64(n)
if err != nil {
return
}
src, ipage := p.src, p.ipage
for {
if ipage == len(src.pages) { // last page
p.ipage, p.off = ipage, len(p.curr)
return
}
ipage++
page := src.getPage(ipage)
n, err = w.Write(page)
written += int64(n)
if err != nil {
p.ipage, p.curr, p.off = ipage, page, n
return
}
}
}
func (p *PagedReader) Read(buf []byte) (nread int, err error) {
for {
n := copy(buf[nread:], p.curr[p.off:])
nread += n
p.off += n
if nread >= len(buf) {
return
}
src := p.src
if p.ipage == len(src.pages) { // last page
err = io.EOF
return
}
p.ipage++
p.curr, p.off = src.getPage(p.ipage), 0
}
}
*/
// -----------------------------------------------------------------------------

View File

@@ -1,104 +0,0 @@
/*
* Copyright (c) 2022 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package parser
import (
"bytes"
"os"
"os/exec"
"strings"
"github.com/goplus/llgo/x/clang/ast"
jsoniter "github.com/json-iterator/go"
)
type Mode uint
// -----------------------------------------------------------------------------
type ParseError struct {
Err error
Stderr []byte
}
func (p *ParseError) Error() string {
if len(p.Stderr) > 0 {
return string(p.Stderr)
}
return p.Err.Error()
}
// -----------------------------------------------------------------------------
type Config struct {
Json *[]byte
Flags []string
Stderr bool
}
func DumpAST(filename string, conf *Config) (result []byte, warning []byte, err error) {
if conf == nil {
conf = new(Config)
}
skiperr := strings.HasSuffix(filename, "vfprintf.c.i")
stdout := NewPagedWriter()
stderr := new(bytes.Buffer)
args := []string{"-Xclang", "-ast-dump=json", "-fsyntax-only", filename}
if len(conf.Flags) != 0 {
args = append(conf.Flags, args...)
}
cmd := exec.Command("clang", args...)
cmd.Stdin = os.Stdin
cmd.Stdout = stdout
if conf.Stderr && !skiperr {
cmd.Stderr = os.Stderr
} else {
cmd.Stderr = stderr
}
err = cmd.Run()
errmsg := stderr.Bytes()
if err != nil && !skiperr {
return nil, nil, &ParseError{Err: err, Stderr: errmsg}
}
return stdout.Bytes(), errmsg, nil
}
// -----------------------------------------------------------------------------
var json = jsoniter.ConfigCompatibleWithStandardLibrary
func ParseFileEx(filename string, mode Mode, conf *Config) (file *ast.Node, warning []byte, err error) {
out, warning, err := DumpAST(filename, conf)
if err != nil {
return
}
if conf != nil && conf.Json != nil {
*conf.Json = out
}
file = new(ast.Node)
err = json.Unmarshal(out, file)
if err != nil {
err = &ParseError{Err: err}
}
return
}
func ParseFile(filename string, mode Mode) (file *ast.Node, warning []byte, err error) {
return ParseFileEx(filename, mode, nil)
}
// -----------------------------------------------------------------------------

View File

@@ -1,28 +0,0 @@
/*
* Copyright (c) 2022 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package pathutil
import (
"path/filepath"
)
func Canonical(baseDir string, uri string) string {
if filepath.IsAbs(uri) {
return filepath.Clean(uri)
}
return filepath.Join(baseDir, uri)
}

View File

@@ -1,99 +0,0 @@
/*
* Copyright (c) 2022 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package preprocessor
import (
"log"
"os"
"os/exec"
"path/filepath"
"github.com/goplus/llgo/x/clang/pathutil"
)
const (
DbgFlagExecCmd = 1 << iota
DbgFlagAll = DbgFlagExecCmd
)
var (
debugExecCmd bool
)
func SetDebug(flags int) {
debugExecCmd = (flags & DbgFlagExecCmd) != 0
}
// -----------------------------------------------------------------------------
type Config struct {
Compiler string // default: clang
PPFlag string // default: -E
BaseDir string // base of include searching directory, should be absolute path
IncludeDirs []string
Defines []string
Flags []string
}
func Do(infile, outfile string, conf *Config) (err error) {
if infile, err = filepath.Abs(infile); err != nil {
return
}
if outfile, err = filepath.Abs(outfile); err != nil {
return
}
if conf == nil {
conf = new(Config)
}
base := conf.BaseDir
if base == "" {
if base, err = os.Getwd(); err != nil {
return
}
}
compiler := conf.Compiler
if compiler == "" {
compiler = "clang"
}
ppflag := conf.PPFlag
if ppflag == "" {
ppflag = "-E"
}
n := 4 + len(conf.Flags) + len(conf.IncludeDirs) + len(conf.Defines)
args := make([]string, 3, n)
args[0] = ppflag
args[1], args[2] = "-o", outfile
args = append(args, conf.Flags...)
for _, def := range conf.Defines {
args = append(args, "-D"+def)
}
for _, inc := range conf.IncludeDirs {
args = append(args, "-I"+pathutil.Canonical(base, inc))
}
args = append(args, infile)
if debugExecCmd {
log.Println("==> runCmd:", compiler, args)
}
cmd := exec.Command(compiler, args...)
cmd.Dir = filepath.Dir(infile)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}
// -----------------------------------------------------------------------------

View File

@@ -1,251 +0,0 @@
/*
* Copyright (c) 2022 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package parser
import (
"go/token"
"go/types"
"testing"
ctypes "github.com/goplus/llgo/x/clang/types"
)
// -----------------------------------------------------------------------------
var (
pkg = types.NewPackage("", "foo")
scope = pkg.Scope()
)
var (
nameInt128 = types.NewTypeName(token.NoPos, pkg, "__int128", nil)
nameUint128 = types.NewTypeName(token.NoPos, pkg, "__uint128", nil)
tyInt128 = types.NewNamed(nameInt128, types.Typ[types.String], nil)
tyUint128 = types.NewNamed(nameUint128, types.Typ[types.Rune], nil)
)
func init() {
aliasType(scope, pkg, "char", types.Typ[types.Int8])
aliasType(scope, pkg, "void", ctypes.Void)
aliasType(scope, pkg, "float", types.Typ[types.Float32])
aliasType(scope, pkg, "double", types.Typ[types.Float64])
aliasType(scope, pkg, "uint", types.Typ[types.Uint32])
aliasType(scope, pkg, ctypes.MangledName("struct", "ConstantString"), tyConstantString)
aliasType(scope, pkg, ctypes.MangledName("union", "arg"), tyArg)
aliasType(scope, pkg, "va_list", ctypes.Valist)
scope.Insert(nameInt128)
}
func aliasType(scope *types.Scope, pkg *types.Package, name string, typ types.Type) {
o := types.NewTypeName(token.NoPos, pkg, name, typ)
scope.Insert(o)
}
var (
tnameConstantString = types.NewTypeName(token.NoPos, pkg, "ConstantString", nil)
tnameArg = types.NewTypeName(token.NoPos, pkg, "UnionArg", nil)
)
var (
tyChar = types.Typ[types.Int8]
tyUchar = types.Typ[types.Uint8]
tyInt16 = types.Typ[types.Int16]
tyUint16 = types.Typ[types.Uint16]
tyInt32 = types.Typ[types.Int32]
tyUint32 = types.Typ[types.Uint32]
tyInt64 = types.Typ[types.Int64]
tyUint64 = types.Typ[types.Uint64]
tyInt = ctypes.Int
tyInt100 = types.NewArray(tyInt, 100)
tyInt3 = types.NewArray(tyInt, 3)
tyInt3_100 = types.NewArray(tyInt3, 100)
tyPInt3_100 = types.NewPointer(tyInt3_100)
tyPInt100 = types.NewPointer(tyInt100)
tyUint = ctypes.Uint
tyString = types.Typ[types.String]
tyCharPtr = types.NewPointer(tyChar)
tyCharPtrPtr = types.NewPointer(tyCharPtr)
tyConstantString = types.NewNamed(tnameConstantString, tyString, nil)
tyArg = types.NewNamed(tnameArg, tyString, nil)
tyEmptyInterface = types.NewInterfaceType(nil, nil)
)
var (
paramInt = types.NewParam(token.NoPos, pkg, "", tyInt)
paramVoidPtr = types.NewParam(token.NoPos, pkg, "", ctypes.UnsafePointer)
paramCharPtrPtr = types.NewParam(token.NoPos, pkg, "", tyCharPtrPtr)
paramAnySlice = types.NewParam(token.NoPos, pkg, "", types.NewSlice(tyEmptyInterface))
paramPAnySlice = types.NewParam(token.NoPos, pkg, "", types.NewPointer(types.NewSlice(tyEmptyInterface)))
)
var (
typesInt = types.NewTuple(paramInt)
typesIntVA = types.NewTuple(paramInt, paramAnySlice)
typesIntPVA = types.NewTuple(paramInt, paramPAnySlice)
typesVoidPtr = types.NewTuple(paramVoidPtr)
typesPICC = types.NewTuple(paramVoidPtr, paramInt, paramCharPtrPtr, paramCharPtrPtr)
)
func newFn(in, out *types.Tuple) types.Type {
return types.NewSignature(nil, in, out, false)
}
func newFnv(in, out *types.Tuple) types.Type {
return types.NewSignature(nil, in, out, true)
}
func newFnProto(in, out *types.Tuple, variadic bool) types.Type {
return ctypes.NewFunc(in, out, variadic)
}
var (
tyFnHandle = newFn(typesInt, nil)
paramFnHandle = types.NewParam(token.NoPos, pkg, "", tyFnHandle)
typesIF = types.NewTuple(paramInt, paramFnHandle)
typesF = types.NewTuple(paramFnHandle)
)
// -----------------------------------------------------------------------------
type testCase struct {
qualType string
flags int
anonym types.Type
typ types.Type
err string
}
var cases = []testCase{
{qualType: "int", typ: tyInt},
{qualType: "unsigned int", typ: tyUint},
{qualType: "struct ConstantString", typ: tyConstantString},
{qualType: "union arg", typ: tyArg},
{qualType: "volatile signed int", typ: tyInt},
{qualType: "__int128", typ: tyInt128},
{qualType: "signed", typ: tyInt},
{qualType: "signed short", typ: tyInt16},
{qualType: "signed long", typ: ctypes.Long},
{qualType: "unsigned", typ: tyUint},
{qualType: "uint", typ: tyUint32},
{qualType: "unsigned char", typ: tyUchar},
{qualType: "unsigned __int128", typ: tyUint128},
{qualType: "unsigned long", typ: ctypes.Ulong},
{qualType: "unsigned long long", typ: tyUint64},
{qualType: "long double", typ: ctypes.LongDouble},
{qualType: "_Complex float", typ: types.Typ[types.Complex64]},
{qualType: "_Complex double", typ: types.Typ[types.Complex128]},
{qualType: "_Complex long double", typ: types.Typ[types.Complex128]},
{qualType: "int (*)(void)", typ: newFn(nil, typesInt)},
{qualType: "int (void)", typ: newFnProto(nil, typesInt, false)},
{qualType: "void (void) __attribute__((noreturn))", typ: newFnProto(nil, nil, false)},
{qualType: "void (*)(void *)", typ: newFn(typesVoidPtr, nil)},
{qualType: "void (^ _Nonnull)(void)", typ: newFn(nil, nil)},
{qualType: "void (int, ...)", typ: newFnProto(typesIntVA, nil, true)},
{qualType: "void (int, va_list*)", typ: newFn(typesIntPVA, nil)},
{qualType: "va_list *", typ: types.NewPointer(types.NewSlice(tyEmptyInterface))},
{qualType: "int (*)()", typ: newFn(nil, typesInt)},
{qualType: "int (*)(int, ...)", typ: newFnv(typesIntVA, typesInt)},
{qualType: "int (*)(int, struct __va_list_tag*)", typ: newFn(typesIntVA, typesInt)},
{qualType: "int (*volatile)(int, struct __va_list_tag* restrict)", typ: newFn(typesIntVA, typesInt)},
{qualType: "int (const char *, const char *, unsigned int)", flags: FlagGetRetType, typ: tyInt},
{qualType: "const char *restrict", typ: tyCharPtr},
{qualType: "const char [7]", typ: types.NewArray(tyChar, 7)},
{qualType: "const char [7]", flags: FlagIsParam, typ: tyCharPtr},
{qualType: "char []", flags: FlagIsStructField, typ: types.NewArray(tyChar, 0)},
{qualType: "char []", flags: FlagIsExtern, typ: types.NewArray(tyChar, -1)},
{qualType: "char []", flags: 0, err: emsgDefArrWithoutLen},
{qualType: "char []", flags: FlagIsTypedef, typ: types.NewArray(tyChar, -1)},
{qualType: "char []", flags: FlagIsParam, typ: tyCharPtr},
{qualType: "int [100][3]", typ: tyInt3_100},
{qualType: "int (*)[100][3]", typ: tyPInt3_100},
{qualType: "int (*)[100]", typ: tyPInt100},
{qualType: "int (*const [2])(void *)", typ: types.NewArray(newFn(typesVoidPtr, typesInt), 2)},
{qualType: "char *", typ: tyCharPtr},
{qualType: "void", typ: ctypes.Void},
{qualType: "void *", typ: ctypes.UnsafePointer},
{qualType: "int (*_Nullable)(void *, int, char **, char **)", typ: newFn(typesPICC, typesInt)},
{qualType: "void (*(*)(int, void (*)(int)))(int)", typ: newFn(typesIF, typesF)},
{qualType: "void (*(int, void (*)(int)))(int)", typ: newFnProto(typesIF, typesF, false)},
{qualType: "void (*(int, void (*)(int)))(int)", flags: FlagGetRetType, typ: tyFnHandle},
{qualType: "int (*)(void *, int, const char *, void (**)(void *, int, void **), void **)"},
{qualType: "struct (anonymous) [2]", anonym: tyInt, typ: types.NewArray(tyInt, 2)},
{qualType: "enum a", typ: ctypes.Int},
}
type baseEnv struct {
pkg *types.Package
tyInt128 types.Type
tyUint128 types.Type
}
func (p *baseEnv) Pkg() *types.Package {
return p.pkg
}
func (p *baseEnv) Int128() types.Type {
return p.tyInt128
}
func (p *baseEnv) Uint128() types.Type {
return p.tyUint128
}
func TestCases(t *testing.T) {
sel := ""
for _, c := range cases {
if sel != "" && c.qualType != sel {
continue
}
t.Run(c.qualType, func(t *testing.T) {
conf := &Config{
Scope: scope, Flags: c.flags, Anonym: c.anonym,
ParseEnv: &baseEnv{pkg: pkg, tyInt128: tyInt128, tyUint128: tyUint128},
}
typ, _, err := ParseType(c.qualType, conf)
if err != nil {
if errMsgOf(err) != c.err {
t.Fatal("ParseType:", err, ", expected:", c.err)
}
} else if c.typ != nil && !ctypes.Identical(typ, c.typ) {
t.Fatal("ParseType:", typ, ", expected:", c.typ)
}
})
}
}
func errMsgOf(err error) string {
if e, ok := err.(*ParseTypeError); ok {
return e.ErrMsg
}
return err.Error()
}
// -----------------------------------------------------------------------------
func TestIsArrayWithoutLen(t *testing.T) {
_, _, err := ParseType("byte[]", &Config{Scope: types.Universe})
if !IsArrayWithoutLen(err) {
t.Fatal("ParseType:", err)
}
_, _, err = ParseType("byte[]", &Config{Scope: types.Universe, Flags: FlagIsExtern})
if IsArrayWithoutLen(err) {
t.Fatal("ParseType:", err)
}
}
// -----------------------------------------------------------------------------

View File

@@ -1,597 +0,0 @@
/*
* Copyright (c) 2022 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package parser
import (
"errors"
"fmt"
"go/token"
"go/types"
"io"
"log"
"strconv"
"github.com/goplus/gogen"
"github.com/goplus/llgo/x/clang/types/scanner"
ctypes "github.com/goplus/llgo/x/clang/types"
)
const (
emsgDefArrWithoutLen = "define array without length"
)
var (
ErrInvalidType = errors.New("invalid type")
)
type TypeNotFound struct {
Literal string
StructOrUnion bool
}
func (p *TypeNotFound) Error() string {
return fmt.Sprintf("type %s not found", p.Literal)
}
type ParseTypeError struct {
QualType string
ErrMsg string
}
func (p *ParseTypeError) Error() string {
return p.ErrMsg // TODO
}
func IsArrayWithoutLen(err error) bool {
if e, ok := err.(*ParseTypeError); ok {
return e.ErrMsg == emsgDefArrWithoutLen
}
return false
}
// -----------------------------------------------------------------------------
const (
FlagIsParam = 1 << iota
FlagIsStructField
FlagIsExtern
FlagIsTypedef
FlagGetRetType
)
func getRetType(flags int) bool {
return (flags & FlagGetRetType) != 0
}
type ParseEnv interface {
Pkg() *types.Package
Int128() types.Type
Uint128() types.Type
}
type Config struct {
ParseEnv
Scope *types.Scope
Anonym types.Type
Flags int
}
const (
KindFConst = 1 << iota
KindFVolatile
KindFAnonymous
KindFVariadic
)
// qualType can be:
// - unsigned int
// - struct ConstantString
// - volatile uint32_t
// - int (*)(void *, int, char **, char **)
// - int (*)(const char *, ...)
// - int (*)(void)
// - void (*(int, void (*)(int)))(int)
// - const char *restrict
// - const char [7]
// - char *
// - void
// - ...
func ParseType(qualType string, conf *Config) (t types.Type, kind int, err error) {
p := newParser(qualType, conf)
if t, kind, err = p.parse(conf.Flags); err != nil {
return
}
if p.tok != token.EOF {
err = p.newError("unexpect token " + p.tok.String())
}
return
}
// -----------------------------------------------------------------------------
type parser struct {
s scanner.Scanner
scope *types.Scope
conf *Config
tok token.Token
lit string
old struct {
tok token.Token
lit string
}
}
const (
invalidTok token.Token = -1
)
func newParser(qualType string, conf *Config) *parser {
p := &parser{scope: conf.Scope, conf: conf}
p.old.tok = invalidTok
p.s.Init(qualType)
return p
}
func (p *parser) peek() token.Token {
if p.old.tok == invalidTok {
p.old.tok, p.old.lit = p.s.Scan()
}
return p.old.tok
}
func (p *parser) next() {
if p.old.tok != invalidTok { // support unget
p.tok, p.lit = p.old.tok, p.old.lit
p.old.tok = invalidTok
return
}
p.tok, p.lit = p.s.Scan()
}
func (p *parser) unget(tok token.Token, lit string) {
p.old.tok, p.old.lit = p.tok, p.lit
p.tok, p.lit = tok, lit
}
func (p *parser) skipUntil(tok token.Token) bool {
for {
p.next()
switch p.tok {
case tok:
return true
case token.EOF:
return false
}
}
}
func (p *parser) newErrorf(format string, args ...interface{}) *ParseTypeError {
return p.newError(fmt.Sprintf(format, args...))
}
func (p *parser) newError(errMsg string) *ParseTypeError {
return &ParseTypeError{QualType: p.s.Source(), ErrMsg: errMsg}
}
// TODO(xsw): check expect results
func (p *parser) expect(tokExp token.Token) error {
p.next()
if p.tok != tokExp {
return p.newErrorf("expect %v, got %v", tokExp, p.tok)
}
return nil
}
const (
flagShort = 1 << iota
flagLong
flagLongLong
flagUnsigned
flagSigned
flagComplex
flagStructOrUnion
)
func (p *parser) lookupType(tylit string, flags int) (t types.Type, err error) {
structOrUnion := (flags & flagStructOrUnion) != 0
_, o := gogen.LookupParent(p.scope, tylit, token.NoPos)
if o == nil {
return nil, &TypeNotFound{Literal: tylit, StructOrUnion: structOrUnion}
}
t = o.Type()
if !structOrUnion && flags != 0 {
tt, ok := t.(*types.Basic)
if !ok {
tyInt128 := p.conf.Int128()
if t == tyInt128 {
switch flags {
case flagSigned:
return tyInt128, nil
case flagUnsigned:
return p.conf.Uint128(), nil
}
}
} else if (flags & flagComplex) != 0 {
switch tt.Kind() {
case types.Float32:
return types.Typ[types.Complex64], nil
case types.Float64:
return types.Typ[types.Complex128], nil
case types.Int:
return types.Typ[types.Complex128], nil
}
} else {
switch tt.Kind() {
case types.Int:
if t = intTypes[flags&^flagSigned]; t != nil {
return
}
case types.Int8:
switch flags {
case flagUnsigned:
return types.Typ[types.Uint8], nil
case flagSigned:
return types.Typ[types.Int8], nil
}
case types.Float64:
switch flags {
case flagLong:
return ctypes.LongDouble, nil
}
}
}
log.Panicln("lookupType: TODO - invalid type")
return nil, ErrInvalidType
}
if t == types.Typ[types.Int] {
return ctypes.Int, nil
}
return
}
var intTypes = [...]types.Type{
0: ctypes.Int,
flagShort: types.Typ[types.Int16],
flagLong: ctypes.Long,
flagLong | flagLongLong: types.Typ[types.Int64],
flagUnsigned: ctypes.Uint,
flagShort | flagUnsigned: types.Typ[types.Uint16],
flagLong | flagUnsigned: ctypes.Ulong,
flagLong | flagLongLong | flagUnsigned: types.Typ[types.Uint64],
flagShort | flagLong | flagLongLong | flagUnsigned: nil,
}
func (p *parser) parseArray(t types.Type, inFlags int) (types.Type, error) {
var n int64
var err error
p.next()
switch p.tok {
case token.RBRACK: // ]
if (inFlags & FlagIsStructField) != 0 {
n = 0
} else {
n = -1
}
case token.INT:
if n, err = strconv.ParseInt(p.lit, 10, 64); err != nil {
return nil, p.newError(err.Error())
}
if err = p.expect(token.RBRACK); err != nil { // ]
return nil, err
}
default:
return nil, p.newError("array length not an integer")
}
if n >= 0 || (inFlags&(FlagIsExtern|FlagIsTypedef|FlagIsParam)) != 0 {
t = types.NewArray(t, n)
} else {
return nil, p.newError(emsgDefArrWithoutLen)
}
return t, nil
}
func (p *parser) parseArrays(t types.Type, inFlags int) (ret types.Type, err error) {
if t == nil {
return nil, p.newError("array to nil")
}
var tyArr types.Type
for {
if tyArr, err = p.parseArray(tyArr, inFlags); err != nil {
return
}
if p.peek() != token.LBRACK {
return newArraysEx(t, tyArr, inFlags), nil
}
p.next()
}
}
func (p *parser) parseFunc(pkg *types.Package, t types.Type, inFlags int) (ret types.Type, err error) {
var results *types.Tuple
if ctypes.NotVoid(t) {
results = types.NewTuple(types.NewParam(token.NoPos, pkg, "", t))
}
args, variadic, err := p.parseArgs(pkg)
if err != nil {
return
}
_ = inFlags
return ctypes.NewFunc(types.NewTuple(args...), results, variadic), nil
}
func (p *parser) parseArgs(pkg *types.Package) (args []*types.Var, variadic bool, err error) {
for {
arg, kind, e := p.parse(FlagIsParam)
if e != nil {
return nil, false, e
}
if ctypes.NotVoid(arg) {
args = append(args, types.NewParam(token.NoPos, pkg, "", arg))
}
if p.tok != token.COMMA {
variadic = (kind & KindFVariadic) != 0
break
}
}
if p.tok != token.RPAREN { // )
return nil, false, p.newError("expect )")
}
return
}
func (p *parser) parseStars() (nstar int) {
for isPtr(p.peek()) {
p.next()
nstar++
}
return
}
func (p *parser) parse(inFlags int) (t types.Type, kind int, err error) {
flags := 0
for {
p.next()
retry:
switch p.tok {
case token.IDENT:
ident:
switch lit := p.lit; lit {
case "unsigned":
flags |= flagUnsigned
case "short":
flags |= flagShort
case "long":
if (flags & flagLong) != 0 {
flags |= flagLongLong
} else {
flags |= flagLong
}
case "signed":
flags |= flagSigned
case "const":
kind |= KindFConst
case "volatile":
kind |= KindFVolatile
case "_Complex":
flags |= flagComplex
case "restrict", "_Nullable", "_Nonnull":
case "enum":
if err = p.expect(token.IDENT); err != nil {
return
}
if t != nil {
return nil, 0, p.newError("illegal syntax: multiple types?")
}
t = ctypes.Int
continue
case "struct", "union":
p.next()
switch p.tok {
case token.IDENT:
case token.LPAREN:
if t == nil && p.conf.Anonym != nil {
p.skipUntil(token.RPAREN)
t = p.conf.Anonym
kind |= KindFAnonymous
continue
}
fallthrough
default:
log.Panicln("c.types.ParseType: struct/union - TODO:", p.lit)
}
lit = ctypes.MangledName(lit, p.lit)
flags |= flagStructOrUnion
fallthrough
default:
if t != nil {
return nil, 0, p.newError("illegal syntax: multiple types?")
}
if t, err = p.lookupType(lit, flags); err != nil {
return
}
flags = 0
}
if flags != 0 {
p.next()
if p.tok == token.IDENT {
goto ident
}
if t != nil {
return nil, 0, p.newError("illegal syntax: multiple types?")
}
if t, err = p.lookupType("int", flags); err != nil {
return
}
flags = 0
goto retry
}
case token.MUL: // *
if t == nil {
return nil, 0, p.newError("pointer to nil")
}
t = ctypes.NewPointer(t)
case token.LBRACK: // [
if t, err = p.parseArrays(t, inFlags); err != nil {
return
}
case token.LPAREN: // (
if t == nil {
log.Panicln("TODO")
return nil, 0, p.newError("no function return type")
}
var nstar = p.parseStars()
var nstarRet int
var tyArr types.Type
var pkg, isFn = p.conf.Pkg(), false
var args []*types.Var
var variadic bool
if nstar == 0 {
if getRetType(inFlags) {
err = nil
p.tok = token.EOF
return
}
if args, variadic, err = p.parseArgs(pkg); err != nil {
return
}
isFn = true
} else {
nextTok:
p.next()
switch p.tok {
case token.RPAREN: // )
case token.LPAREN: // (
if !isFn {
nstar, nstarRet = p.parseStars(), nstar
if nstar != 0 {
p.expect(token.RPAREN) // )
p.expect(token.LPAREN) // (
}
if args, variadic, err = p.parseArgs(pkg); err != nil {
return
}
isFn = true
goto nextTok
}
return nil, 0, p.newError("expect )")
case token.LBRACK:
if tyArr, err = p.parseArrays(ctypes.Void, 0); err != nil {
return
}
p.expect(token.RPAREN) // )
case token.IDENT:
switch p.lit {
case "_Nullable", "_Nonnull", "const", "volatile":
goto nextTok
}
fallthrough
default:
return nil, 0, p.newError("expect )")
}
}
p.next()
switch p.tok {
case token.LPAREN: // (
if t, err = p.parseFunc(pkg, t, inFlags); err != nil {
return
}
case token.LBRACK: // [
if t, err = p.parseArrays(t, 0); err != nil {
return
}
case token.EOF:
case token.IDENT:
if p.lit == "__attribute__" {
p.tok, p.lit = token.EOF, ""
p.unget(token.EOF, "")
break
}
fallthrough
default:
return nil, 0, p.newError("unexpected " + p.tok.String())
}
t = newPointers(t, nstarRet)
if isFn {
if getRetType(inFlags) {
p.tok = token.EOF
return
}
var results *types.Tuple
if ctypes.NotVoid(t) {
results = types.NewTuple(types.NewParam(token.NoPos, pkg, "", t))
}
t = ctypes.NewFunc(types.NewTuple(args...), results, variadic)
}
t = newPointers(t, nstar)
t = newArrays(t, tyArr)
case token.RPAREN:
if t == nil {
t = ctypes.Void
}
return
case token.COMMA, token.EOF:
if t == nil {
err = io.ErrUnexpectedEOF
}
return
case token.ELLIPSIS:
if t != nil {
return nil, 0, p.newError("illegal syntax: multiple types?")
}
t = ctypes.Valist
kind |= KindFVariadic
default:
log.Panicln("c.types.ParseType: unknown -", p.tok, p.lit)
}
}
}
func newPointers(t types.Type, nstar int) types.Type {
for nstar > 0 {
t = ctypes.NewPointer(t)
nstar--
}
return t
}
func isPtr(tok token.Token) bool {
return tok == token.MUL || tok == token.XOR // * or ^
}
func newArrays(t types.Type, tyArr types.Type) types.Type {
retry:
if arr, ok := tyArr.(*types.Array); ok {
t = types.NewArray(t, arr.Len())
tyArr = arr.Elem()
goto retry
}
return t
}
func newArraysEx(t types.Type, tyArr types.Type, inFlags int) types.Type {
t = newArrays(t, tyArr)
if arr, ok := t.(*types.Array); ok {
if (inFlags & FlagIsParam) != 0 {
t = ctypes.NewPointer(arr.Elem())
}
}
return t
}
// -----------------------------------------------------------------------------

View File

@@ -1,303 +0,0 @@
/*
* Copyright (c) 2022 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package scanner
import (
"fmt"
"go/token"
"unicode"
"unicode/utf8"
)
// An ErrorHandler may be provided to Scanner.Init. If a syntax error is
// encountered and a handler was installed, the handler is called with a
// position and an error message. The position points to the beginning of
// the offending token.
type ErrorHandler func(pos token.Position, msg string)
// A Scanner holds the scanner's internal state while processing
// a given text. It can be allocated as part of another data
// structure but must be initialized via Init before use.
type Scanner struct {
// immutable state
src string
// scanning state
ch rune // current character
offset int // character offset
rdOffset int // reading offset (position after current character)
// public state - ok to modify
ErrorCount int // number of errors encountered
OnErr func(msg string)
}
const (
bom = 0xFEFF // byte order mark, only permitted as very first character
eof = -1 // end of file
)
// Read the next Unicode char into s.ch.
// s.ch < 0 means end-of-file.
//
// For optimization, there is some overlap between this method and
// s.scanIdentifier.
func (s *Scanner) next() {
if s.rdOffset < len(s.src) {
s.offset = s.rdOffset
r, w := rune(s.src[s.rdOffset]), 1
switch {
case r == 0:
s.error("illegal character NUL")
case r >= utf8.RuneSelf:
// not ASCII
r, w = utf8.DecodeRuneInString(s.src[s.rdOffset:])
if r == utf8.RuneError && w == 1 {
s.error("illegal UTF-8 encoding")
} else if r == bom && s.offset > 0 {
s.error("illegal byte order mark")
}
}
s.rdOffset += w
s.ch = r
} else {
s.offset = len(s.src)
s.ch = eof
}
}
// peek returns the byte following the most recently read character without
// advancing the scanner. If the scanner is at EOF, peek returns 0.
func (s *Scanner) peek() byte {
if s.rdOffset < len(s.src) {
return s.src[s.rdOffset]
}
return 0
}
func (s *Scanner) Init(src string) {
s.src = src
s.ch = ' '
s.offset = 0
s.rdOffset = 0
s.ErrorCount = 0
s.next()
if s.ch == bom {
s.next() // ignore BOM at file beginning
}
}
func (s *Scanner) Source() string {
return s.src
}
func (s *Scanner) error(msg string) {
if s.OnErr != nil {
s.OnErr(msg)
}
s.ErrorCount++
}
func (s *Scanner) errorf(format string, args ...interface{}) {
s.error(fmt.Sprintf(format, args...))
}
func isLetter(ch rune) bool {
return 'a' <= lower(ch) && lower(ch) <= 'z' || ch == '_' || ch >= utf8.RuneSelf && unicode.IsLetter(ch)
}
func isDigit(ch rune) bool {
return isDecimal(ch) || ch >= utf8.RuneSelf && unicode.IsDigit(ch)
}
// scanIdentifier reads the string of valid identifier characters at s.offset.
// It must only be called when s.ch is known to be a valid letter.
//
// Be careful when making changes to this function: it is optimized and affects
// scanning performance significantly.
func (s *Scanner) scanIdentifier() string {
offs := s.offset
// Optimize for the common case of an ASCII identifier.
//
// Ranging over s.src[s.rdOffset:] lets us avoid some bounds checks, and
// avoids conversions to runes.
//
// In case we encounter a non-ASCII character, fall back on the slower path
// of calling into s.next().
for rdOffset, b := range s.src[s.rdOffset:] {
if 'a' <= b && b <= 'z' || 'A' <= b && b <= 'Z' || b == '_' || '0' <= b && b <= '9' {
// Avoid assigning a rune for the common case of an ascii character.
continue
}
s.rdOffset += rdOffset
if 0 < b && b < utf8.RuneSelf {
// Optimization: we've encountered an ASCII character that's not a letter
// or number. Avoid the call into s.next() and corresponding set up.
//
// Note that s.next() does some line accounting if s.ch is '\n', so this
// shortcut is only possible because we know that the preceding character
// is not '\n'.
s.ch = rune(b)
s.offset = s.rdOffset
s.rdOffset++
goto exit
}
// We know that the preceding character is valid for an identifier because
// scanIdentifier is only called when s.ch is a letter, so calling s.next()
// at s.rdOffset resets the scanner state.
s.next()
for isLetter(s.ch) || isDigit(s.ch) {
s.next()
}
goto exit
}
s.offset = len(s.src)
s.rdOffset = len(s.src)
s.ch = eof
exit:
return string(s.src[offs:s.offset])
}
func lower(ch rune) rune { return ('a' - 'A') | ch } // returns lower-case ch iff ch is ASCII letter
func isDecimal(ch rune) bool { return '0' <= ch && ch <= '9' }
func isHex(ch rune) bool { return '0' <= ch && ch <= '9' || 'a' <= lower(ch) && lower(ch) <= 'f' }
func (s *Scanner) digits(base int, invalid *int) (digsep int) {
if base <= 10 {
max := rune('0' + base)
for isDecimal(s.ch) {
if s.ch >= max && *invalid < 0 {
*invalid = s.offset // record invalid rune offset
}
digsep = 1
s.next()
}
} else {
for isHex(s.ch) {
digsep = 1
s.next()
}
}
return
}
func (s *Scanner) scanNumber() (token.Token, string) {
offs := s.offset
base := 10 // number base
prefix := rune(0) // one of 0 (decimal), '0' (0-octal), 'x', 'o', or 'b'
digsep := 0 // bit 0: digit present, bit 1: '_' present
invalid := -1 // index of invalid digit in literal, or < 0
if s.ch == '0' {
s.next()
switch lower(s.ch) {
case 'x':
s.next()
base, prefix = 16, 'x'
case 'o':
s.next()
base, prefix = 8, 'o'
case 'b':
s.next()
base, prefix = 2, 'b'
default:
base, prefix = 8, '0'
digsep = 1 // leading 0
}
}
digsep |= s.digits(base, &invalid)
if digsep&1 == 0 {
s.error(litname(prefix) + " has no digits")
}
lit := string(s.src[offs:s.offset])
if invalid >= 0 {
s.errorf("invalid digit %q in %s", lit[invalid-offs], litname(prefix))
}
return token.INT, lit
}
func litname(prefix rune) string {
switch prefix {
case 'x':
return "hexadecimal literal"
case 'o', '0':
return "octal literal"
case 'b':
return "binary literal"
}
return "decimal literal"
}
func (s *Scanner) skipWhitespace() {
for s.ch == ' ' || s.ch == '\t' || s.ch == '\n' || s.ch == '\r' {
s.next()
}
}
func (s *Scanner) Scan() (tok token.Token, lit string) {
s.skipWhitespace()
// determine token value
switch ch := s.ch; {
case isLetter(ch):
lit = s.scanIdentifier()
tok = token.IDENT
case isDecimal(ch):
tok, lit = s.scanNumber()
default:
s.next() // always make progress
switch ch {
case -1:
tok = token.EOF
case '.':
// fractions starting with a '.' are handled by outer switch
tok = token.PERIOD
if s.ch == '.' && s.peek() == '.' {
s.next()
s.next() // consume last '.'
tok = token.ELLIPSIS
}
case ',':
tok = token.COMMA
case '(':
tok = token.LPAREN
case ')':
tok = token.RPAREN
case '[':
tok = token.LBRACK
case ']':
tok = token.RBRACK
case '*':
tok = token.MUL
case '^':
tok = token.XOR
default:
// next reports unexpected BOMs - don't repeat
if ch != bom {
s.errorf("illegal character %#U", ch)
}
tok = token.ILLEGAL
lit = string(ch)
}
}
return
}

View File

@@ -1,99 +0,0 @@
/*
* Copyright (c) 2022 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package types
import (
"go/token"
"go/types"
"unsafe"
"github.com/goplus/gogen"
)
// -----------------------------------------------------------------------------
var (
Void = types.Typ[types.UntypedNil]
UnsafePointer = types.Typ[types.UnsafePointer]
Int = types.Typ[types.Int32]
Uint = types.Typ[types.Uint32]
Long = types.Typ[uintptr(types.Int32)+unsafe.Sizeof(0)>>3] // int32/int64
Ulong = types.Typ[uintptr(types.Uint32)+unsafe.Sizeof(0)>>3] // uint32/uint64
NotImpl = UnsafePointer
LongDouble = types.Typ[types.Float64]
)
func NotVoid(t types.Type) bool {
return t != Void
}
func MangledName(tag, name string) string {
return tag + "_" + name // TODO: use sth to replace _
}
// -----------------------------------------------------------------------------
var (
ValistTag types.Type
Valist types.Type = types.NewSlice(gogen.TyEmptyInterface)
)
func init() {
vaTag := types.NewTypeName(token.NoPos, types.Unsafe, MangledName("struct", "__va_list_tag"), nil)
ValistTag = types.NewNamed(vaTag, types.NewStruct(nil, nil), nil)
types.Universe.Insert(vaTag)
}
// -----------------------------------------------------------------------------
func NewFunc(params, results *types.Tuple, variadic bool) *types.Signature {
return gogen.NewCSignature(params, results, variadic)
}
func NewPointer(typ types.Type) types.Type {
switch t := typ.(type) {
case *types.Basic:
if t == Void {
return types.Typ[types.UnsafePointer]
}
case *types.Signature:
if gogen.IsCSignature(t) {
return types.NewSignatureType(nil, nil, nil, t.Params(), t.Results(), t.Variadic())
}
case *types.Named:
if typ == ValistTag {
return Valist
}
}
return types.NewPointer(typ)
}
func IsFunc(typ types.Type) bool {
sig, ok := typ.(*types.Signature)
if ok {
ok = gogen.IsCSignature(sig)
}
return ok
}
func Identical(typ1, typ2 types.Type) bool {
return types.Identical(typ1, typ2)
}
// -----------------------------------------------------------------------------

45
x/env/llvm/llvm.go vendored
View File

@@ -1,45 +0,0 @@
/*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package llvm
import (
"os"
"github.com/goplus/llgo/x/nm"
)
type Env struct {
root string
nmprefix string
}
func New() *Env {
var nmprefix string
var root = os.Getenv("LLGO_LLVM_ROOT")
if root != "" {
nmprefix = root + "/bin/llvm-"
}
return &Env{root, nmprefix}
}
func (p *Env) Root() string {
return p.root
}
func (p *Env) Nm() *nm.Cmd {
return nm.New(p.nmprefix + "nm")
}

View File

@@ -1,50 +0,0 @@
/*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package llvmlink
import (
"io"
"os"
"os/exec"
)
// -----------------------------------------------------------------------------
// Cmd represents a llvm-link command.
type Cmd struct {
app string
Stdout io.Writer
Stderr io.Writer
}
// New creates a new llvm-link command.
func New(app string) *Cmd {
if app == "" {
app = os.Getenv("LLGO_LLVM_ROOT") + "/bin/llvm-link"
}
return &Cmd{app, os.Stdout, os.Stderr}
}
func (p *Cmd) Exec(args ...string) error {
cmd := exec.Command(p.app, args...)
cmd.Stdout = p.Stdout
cmd.Stderr = p.Stderr
return cmd.Run()
}
// -----------------------------------------------------------------------------

View File

@@ -1,119 +0,0 @@
/*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package nm
import (
"bytes"
"crypto/md5"
"encoding/base64"
"log"
"os"
"path/filepath"
"strings"
)
type IndexBuilder struct {
nm *Cmd
}
func NewIndexBuilder(nm *Cmd) *IndexBuilder {
return &IndexBuilder{nm}
}
func (p *IndexBuilder) Index(fromDir []string, toDir string, progress func(path string)) error {
for _, dir := range fromDir {
if dir == "" {
continue
}
if e := p.IndexDir(dir, toDir, progress); e != nil {
if !os.IsNotExist(e) {
log.Println(e)
}
}
}
return nil
}
func (p *IndexBuilder) IndexDir(fromDir, toDir string, progress func(path string)) error {
if abs, e := filepath.Abs(fromDir); e == nil {
fromDir = abs
}
return filepath.WalkDir(fromDir, func(path string, d os.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() {
return nil
}
fname := d.Name()
switch filepath.Ext(fname) {
case ".a", ".dylib", ".tbd", ".so", ".dll", ".lib":
progress(path)
hash := md5.Sum([]byte(path))
hashStr := base64.RawURLEncoding.EncodeToString(hash[:])
outFile := filepath.Join(toDir, strings.TrimPrefix(fname, "lib")+hashStr+".pub")
e := p.IndexFile(path, outFile)
if e != nil {
log.Println(e)
}
}
return nil
})
}
func (p *IndexBuilder) IndexFile(arFile, outFile string) (err error) {
items, err := p.nm.List(arFile)
if err != nil {
if len(items) == 0 {
return
}
}
var b bytes.Buffer
b.WriteString("nm ")
b.WriteString(arFile)
b.WriteByte('\n')
nbase := b.Len()
for _, item := range items {
if item.File != "" {
b.WriteString("file ")
b.WriteString(item.File)
b.WriteByte('\n')
}
for _, sym := range item.Symbols {
switch sym.Type {
case Text, Data, BSS, Rodata, 'S', 'C', 'W', 'A':
b.WriteByte(byte(sym.Type))
b.WriteByte(' ')
b.WriteString(sym.Name)
b.WriteByte('\n')
case Undefined, LocalText, LocalData, LocalBSS, LocalASym, 'I', 'i', 'a', 'w':
/*
if sym.Type != Undefined && strings.Contains(sym.Name, "fprintf") {
log.Printf("skip symbol type %c: %s\n", sym.Type, sym.Name)
}
*/
default:
log.Printf("unknown symbol type %c: %s\n", sym.Type, sym.Name)
}
}
}
buf := b.Bytes()
if len(buf) <= nbase {
return
}
return os.WriteFile(outFile, buf, 0666)
}

View File

@@ -1,208 +0,0 @@
/*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package nm
import (
"bytes"
"errors"
"fmt"
"os"
"os/exec"
"strings"
)
var (
errInvalidOutput = errors.New("invalid nm output")
)
// -----------------------------------------------------------------------------
// Cmd represents a nm command.
type Cmd struct {
app string
}
// New creates a new nm command.
func New(app string) *Cmd {
if app == "" {
app = "nm"
}
return &Cmd{app}
}
// -----------------------------------------------------------------------------
// SymbolType represents a symbol type.
type SymbolType uint8
const (
Undefined = SymbolType('U') // Undefined
Text = SymbolType('T') // Text (code) section symbol
Data = SymbolType('D') // Data (global var) section symbol
Rodata = SymbolType('R') // Read-only data (rodata) section symbol
BSS = SymbolType('B') // BSS (uninitialized global var) section symbol
LocalText = SymbolType('t') // Local text (code) section symbol
LocalData = SymbolType('d') // Local data (local var) section symbol
LocalBSS = SymbolType('b') // Local BSS (uninitialized local var) section symbol
LocalASym = SymbolType('s') // Local symbol in an assembler source file
)
// Symbol represents a symbol in an object file.
type Symbol struct {
Name string // symbol name
Addr uint64 // symbol address
Type SymbolType // symbol type
FAddr bool // address is valid
}
// ObjectFile represents an object file.
type ObjectFile struct {
File string // file name
Symbols []*Symbol // symbols
}
// List lists symbols in an archive file.
func (p *Cmd) List(arfile string) (items []*ObjectFile, err error) {
var stdout bytes.Buffer
var stderr bytes.Buffer
cmd := exec.Command(p.app, arfile)
cmd.Stdout = &stdout
cmd.Stderr = &stderr
e := cmd.Run()
if stderr.Len() > 0 {
listError(stderr.Bytes())
}
items, err = listOutput(stdout.Bytes())
if err == nil {
err = e
}
return
}
func listError(data []byte) {
sep := []byte{'\n'}
nosym := []byte(": no symbols")
lines := bytes.Split(data, sep)
for _, line := range lines {
if len(line) == 0 || bytes.HasSuffix(line, nosym) {
continue
}
os.Stderr.Write(line)
os.Stderr.Write(sep)
}
}
func listOutput(data []byte) (items []*ObjectFile, err error) {
sep := []byte{'\n'}
item := &ObjectFile{}
lines := bytes.Split(data, sep)
for _, line := range lines {
if len(line) == 0 {
if item.File == "" && len(item.Symbols) > 0 {
items = append(items, item)
}
item = nil
continue
}
if item == nil {
s := string(line)
if strings.HasSuffix(s, ":") {
item = &ObjectFile{File: s[:len(s)-1]}
items = append(items, item)
continue
}
err = errInvalidOutput
return
}
if len(line) < 10 {
err = errInvalidOutput
return
}
var sym *Symbol
if is64bits(line) {
sym = &Symbol{
Name: string(line[19:]),
Type: SymbolType(line[17]),
}
if sym.FAddr = hasAddr(line); sym.FAddr {
sym.Addr = hexUint64(line)
}
} else {
sym = &Symbol{
Name: string(line[11:]),
Type: SymbolType(line[9]),
}
if sym.FAddr = hasAddr(line); sym.FAddr {
sym.Addr = uint64(hexUint32(line))
}
}
item.Symbols = append(item.Symbols, sym)
}
return
}
func hasAddr(line []byte) bool {
c := line[0]
return c != ' ' && c != '-'
}
func is64bits(line []byte) bool {
if line[0] != ' ' {
return line[8] != ' '
}
return line[9] == ' '
}
func hexUint64(b []byte) uint64 {
defer func() {
if e := recover(); e != nil {
fmt.Fprintln(os.Stderr, "-->", string(b))
panic(e)
}
}()
_ = b[15] // bounds check hint to compiler; see golang.org/issue/14808
return hex(b[15]) | hex(b[14])<<4 | hex(b[13])<<8 | hex(b[12])<<12 |
hex(b[11])<<16 | hex(b[10])<<20 | hex(b[9])<<24 | hex(b[8])<<28 |
hex(b[7])<<32 | hex(b[6])<<36 | hex(b[5])<<40 | hex(b[4])<<44 |
hex(b[3])<<48 | hex(b[2])<<52 | hex(b[1])<<56 | hex(b[0])<<60
}
func hexUint32(b []byte) uint64 {
defer func() {
if e := recover(); e != nil {
fmt.Fprintln(os.Stderr, "-->", string(b))
panic(e)
}
}()
_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
return hex(b[7]) | hex(b[6])<<4 | hex(b[5])<<8 | hex(b[4])<<12 |
hex(b[3])<<16 | hex(b[2])<<20 | hex(b[1])<<24 | hex(b[0])<<28
}
func hex(b byte) uint64 {
return hexTable[b]
}
var hexTable = []uint64{
'0': 0, '1': 1, '2': 2, '3': 3, '4': 4,
'5': 5, '6': 6, '7': 7, '8': 8, '9': 9,
'a': 10, 'b': 11, 'c': 12, 'd': 13, 'e': 14, 'f': 15,
'A': 10, 'B': 11, 'C': 12, 'D': 13, 'E': 14, 'F': 15,
}
// -----------------------------------------------------------------------------

View File

@@ -1,129 +0,0 @@
/*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package nm
import (
"bufio"
"os"
"strings"
)
// MatchedItem represents a matched item
type MatchedItem struct {
ObjFile string
Symbol string
Type SymbolType
}
// MatchedFile represents a matched file
type MatchedFile struct {
ArFile string
Items []*MatchedItem
}
// Query queries symbol in index files (allow wildcard).
func Query(dir string, query string) (files []*MatchedFile, err error) {
fis, err := os.ReadDir(dir)
if err != nil {
return
}
dir += "/"
for _, fi := range fis {
if fi.IsDir() {
continue
}
idxFile := fi.Name()
if !strings.HasSuffix(idxFile, ".pub") {
continue
}
files = queryIndex(files, dir+fi.Name(), query)
}
return
}
func queryIndex(files []*MatchedFile, idxFile, query string) []*MatchedFile {
f, err := os.Open(idxFile)
if err != nil {
return files
}
defer f.Close()
r := bufio.NewReader(f)
line, err := r.ReadString('\n')
if err != nil || !strings.HasPrefix(line, "nm ") {
return files
}
var items []*MatchedItem
arFile := line[3 : len(line)-1]
objFile := ""
query, flags := parseQuery(query)
for {
line, err = r.ReadString('\n')
if err != nil {
break
}
if strings.HasPrefix(line, "file ") {
objFile = line[5 : len(line)-1]
continue
}
typ := line[0]
sym := line[2 : len(line)-1]
if !match(sym, query, flags) {
continue
}
items = append(items, &MatchedItem{
ObjFile: objFile,
Symbol: sym,
Type: SymbolType(typ),
})
}
if len(items) > 0 {
files = append(files, &MatchedFile{ArFile: arFile, Items: items})
}
return files
}
const (
flagSuffix = 1 << iota
flagPrefix
)
func parseQuery(query string) (text string, flags int) {
if strings.HasSuffix(query, "*") {
query = query[:len(query)-1]
flags = flagPrefix
}
if strings.HasPrefix(query, "*") {
query = query[1:]
flags |= flagSuffix
}
text = query
return
}
func match(s, query string, flags int) bool {
switch flags {
case 0:
return s == query
case flagPrefix:
return strings.HasPrefix(s, query)
case flagSuffix:
return strings.HasSuffix(s, query)
default:
return strings.Contains(s, query)
}
}