mv x/<tool> => xtool/<tool>
This commit is contained in:
60
xtool/ar/common.go
Normal file
60
xtool/ar/common.go
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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
|
||||
}
|
||||
140
xtool/ar/reader.go
Normal file
140
xtool/ar/reader.go
Normal file
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* 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
|
||||
}
|
||||
116
xtool/ar/writer.go
Normal file
116
xtool/ar/writer.go
Normal file
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* 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
|
||||
}
|
||||
262
xtool/clang/ast/ast.go
Normal file
262
xtool/clang/ast/ast.go
Normal file
@@ -0,0 +1,262 @@
|
||||
/*
|
||||
* 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"`
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
50
xtool/clang/clang.go
Normal file
50
xtool/clang/clang.go
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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()
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
122
xtool/clang/parser/pages.go
Normal file
122
xtool/clang/parser/pages.go
Normal file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* 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
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
104
xtool/clang/parser/parse.go
Normal file
104
xtool/clang/parser/parse.go
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* 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/xtool/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)
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
28
xtool/clang/pathutil/pathutil.go
Normal file
28
xtool/clang/pathutil/pathutil.go
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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)
|
||||
}
|
||||
99
xtool/clang/preprocessor/preprocessor.go
Normal file
99
xtool/clang/preprocessor/preprocessor.go
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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/xtool/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()
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
251
xtool/clang/types/parser/_parser_test.go
Normal file
251
xtool/clang/types/parser/_parser_test.go
Normal file
@@ -0,0 +1,251 @@
|
||||
/*
|
||||
* 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)
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
597
xtool/clang/types/parser/parser.go
Normal file
597
xtool/clang/types/parser/parser.go
Normal file
@@ -0,0 +1,597 @@
|
||||
/*
|
||||
* 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/xtool/clang/types/scanner"
|
||||
|
||||
ctypes "github.com/goplus/llgo/xtool/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
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
303
xtool/clang/types/scanner/scanner.go
Normal file
303
xtool/clang/types/scanner/scanner.go
Normal file
@@ -0,0 +1,303 @@
|
||||
/*
|
||||
* 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
|
||||
}
|
||||
99
xtool/clang/types/types.go
Normal file
99
xtool/clang/types/types.go
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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
xtool/env/llvm/llvm.go
vendored
Normal file
45
xtool/env/llvm/llvm.go
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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/xtool/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")
|
||||
}
|
||||
50
xtool/llvm/llvmlink/link.go
Normal file
50
xtool/llvm/llvmlink/link.go
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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()
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
119
xtool/nm/index.go
Normal file
119
xtool/nm/index.go
Normal file
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* 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)
|
||||
}
|
||||
208
xtool/nm/nm.go
Normal file
208
xtool/nm/nm.go
Normal file
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
* 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,
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
129
xtool/nm/query.go
Normal file
129
xtool/nm/query.go
Normal file
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* 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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user