mirror of
https://github.com/taigrr/gopher-os
synced 2025-01-18 04:43:13 -08:00
Merge pull request #60 from achilleasa/improve-handling-of-fwd-decls-in-aml-parser
Improve handling of forward declarations in AML parser
This commit is contained in:
commit
61597cff56
@ -88,10 +88,12 @@ func (ent *namedEntity) setArg(argIndex uint8, arg interface{}) bool {
|
||||
func (ent *namedEntity) TableHandle() uint8 { return ent.tableHandle }
|
||||
func (ent *namedEntity) setTableHandle(h uint8) { ent.tableHandle = h }
|
||||
|
||||
// constEntity is an unnamedEntity which always evaluates to a constant value.
|
||||
// Calls to setArg for argument index 0 will memoize the argument value that is
|
||||
// constEntity is an optionally-named entity which always
|
||||
// evaluates to a constant value. Calls to setArg for
|
||||
// argument index 0 will memoize the argument value that is
|
||||
// stored inside this entity.
|
||||
type constEntity struct {
|
||||
name string
|
||||
tableHandle uint8
|
||||
op opcode
|
||||
args []interface{}
|
||||
@ -114,7 +116,7 @@ func (ent *constEntity) setOpcode(op opcode) {
|
||||
ent.val = uint64(1<<64 - 1)
|
||||
}
|
||||
}
|
||||
func (ent *constEntity) Name() string { return "" }
|
||||
func (ent *constEntity) Name() string { return ent.name }
|
||||
func (ent *constEntity) Parent() ScopeEntity { return ent.parent }
|
||||
func (ent *constEntity) setParent(parent ScopeEntity) { ent.parent = parent }
|
||||
func (ent *constEntity) getArgs() []interface{} { return ent.args }
|
||||
@ -406,21 +408,33 @@ type namedReference struct {
|
||||
|
||||
func (ref *namedReference) Resolve(errWriter io.Writer, rootNs ScopeEntity) bool {
|
||||
if ref.target == nil {
|
||||
ref.target = scopeFind(ref.parent, rootNs, ref.targetName)
|
||||
if ref.target = scopeFind(ref.parent, rootNs, ref.targetName); ref.target == nil {
|
||||
kfmt.Fprintf(errWriter, "could not resolve referenced symbol: %s (parent: %s)\n", ref.targetName, ref.parent.Name())
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if ref.target == nil {
|
||||
kfmt.Fprintf(errWriter, "could not resolve referenced symbol: %s (parent: %s)\n", ref.targetName, ref.parent.Name())
|
||||
}
|
||||
|
||||
return ref.target != nil
|
||||
return true
|
||||
}
|
||||
|
||||
// methodInvocationEntity describes an AML method invocation.
|
||||
type methodInvocationEntity struct {
|
||||
unnamedEntity
|
||||
|
||||
methodDef *Method
|
||||
methodName string
|
||||
method *Method
|
||||
}
|
||||
|
||||
func (m *methodInvocationEntity) Resolve(errWriter io.Writer, rootNs ScopeEntity) bool {
|
||||
if m.method == nil {
|
||||
var isMethod bool
|
||||
if m.method, isMethod = scopeFind(m.parent, rootNs, m.methodName).(*Method); !isMethod {
|
||||
kfmt.Fprintf(errWriter, "could not resolve merenced method: %s (parent: %s)\n", m.methodName, m.parent.Name())
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Method defines an invocable AML method.
|
||||
|
@ -133,6 +133,8 @@ func TestEntityResolveErrors(t *testing.T) {
|
||||
&indexFieldEntity{connectionName: `\`, indexRegName: `\`, dataRegName: "DAT0"},
|
||||
// Unknown reference
|
||||
&namedReference{unnamedEntity: unnamedEntity{parent: scope}, targetName: "TRG0"},
|
||||
// Unknown method name
|
||||
&methodInvocationEntity{unnamedEntity: unnamedEntity{parent: scope}, methodName: "MTH0"},
|
||||
}
|
||||
|
||||
for specIndex, spec := range specs {
|
||||
@ -141,3 +143,31 @@ func TestEntityResolveErrors(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMethodInvocationResolver(t *testing.T) {
|
||||
scope := &scopeEntity{name: `\`}
|
||||
scope.Append(&Method{
|
||||
scopeEntity: scopeEntity{
|
||||
name: "MTH0",
|
||||
},
|
||||
})
|
||||
|
||||
validInv := &methodInvocationEntity{
|
||||
methodName: "MTH0",
|
||||
}
|
||||
|
||||
invalidInv := &methodInvocationEntity{
|
||||
methodName: "FOO0",
|
||||
}
|
||||
|
||||
scope.Append(validInv)
|
||||
scope.Append(invalidInv)
|
||||
|
||||
if !validInv.Resolve(ioutil.Discard, scope) {
|
||||
t.Fatal("expected method invocation to resolve method", validInv.methodName)
|
||||
}
|
||||
|
||||
if invalidInv.Resolve(ioutil.Discard, scope) {
|
||||
t.Fatal("expected method invocation NOT to resolve method", invalidInv.methodName)
|
||||
}
|
||||
}
|
||||
|
@ -21,13 +21,20 @@ type Parser struct {
|
||||
scopeStack []ScopeEntity
|
||||
tableName string
|
||||
tableHandle uint8
|
||||
|
||||
// methodArgCount is initialized in a pre-parse step with the names and expected
|
||||
// number of args for each function declaration. This is required as function
|
||||
// invocations do not employ any mechanism to indicate the number of args that
|
||||
// need to be parsed. Moreover, the spec allows for forward function declarations.
|
||||
methodArgCount map[string]uint8
|
||||
}
|
||||
|
||||
// NewParser returns a new AML parser instance.
|
||||
func NewParser(errWriter io.Writer, rootEntity ScopeEntity) *Parser {
|
||||
return &Parser{
|
||||
errWriter: errWriter,
|
||||
root: rootEntity,
|
||||
errWriter: errWriter,
|
||||
root: rootEntity,
|
||||
methodArgCount: make(map[string]uint8),
|
||||
}
|
||||
}
|
||||
|
||||
@ -43,7 +50,12 @@ func (p *Parser) ParseAML(tableHandle uint8, tableName string, header *table.SDT
|
||||
uint32(unsafe.Sizeof(table.SDTHeader{})),
|
||||
)
|
||||
|
||||
// Pass 1: decode bytecode and build entitites
|
||||
// Pass 1: scan bytecode and locate all method declarations. This allows us to
|
||||
// properly parse the arguments to method invocations at pass 2 even if the
|
||||
// the name of the invoked method is a forward reference.
|
||||
p.detectMethodDeclarations()
|
||||
|
||||
// Pass 2: decode bytecode and build entitites
|
||||
p.scopeStack = nil
|
||||
p.scopeEnter(p.root)
|
||||
if !p.parseObjList(header.Length) {
|
||||
@ -53,12 +65,27 @@ func (p *Parser) ParseAML(tableHandle uint8, tableName string, header *table.SDT
|
||||
}
|
||||
p.scopeExit()
|
||||
|
||||
// Pass 2: resolve forward references
|
||||
// Pass 3: check parents and resolve forward references
|
||||
var resolveFailed bool
|
||||
scopeVisit(0, p.root, EntityTypeAny, func(_ int, ent Entity) bool {
|
||||
// Skip method bodies; their contents will be lazily resolved by the interpreter
|
||||
if _, isMethod := ent.(*Method); isMethod {
|
||||
return false
|
||||
}
|
||||
|
||||
// Populate parents for any entity args that are also entities but are not
|
||||
// linked to a parent (e.g. a package inside a named entity).
|
||||
for _, arg := range ent.getArgs() {
|
||||
if argEnt, isArgEnt := arg.(Entity); isArgEnt && argEnt.Parent() == nil {
|
||||
argEnt.setParent(ent.Parent())
|
||||
}
|
||||
}
|
||||
|
||||
if res, ok := ent.(resolver); ok && !res.Resolve(p.errWriter, p.root) {
|
||||
resolveFailed = true
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
@ -69,6 +96,57 @@ func (p *Parser) ParseAML(tableHandle uint8, tableName string, header *table.SDT
|
||||
return nil
|
||||
}
|
||||
|
||||
// detectMethodDeclarations scans the AML byte-stream looking for function
|
||||
// declarations. For each discovered function, the method will parse its flags
|
||||
// and update the methodArgCount map with the number of required arguments.
|
||||
func (p *Parser) detectMethodDeclarations() {
|
||||
var (
|
||||
next *opcodeInfo
|
||||
method string
|
||||
startOffset = p.r.Offset()
|
||||
curOffset, pkgLen uint32
|
||||
flags uint64
|
||||
ok bool
|
||||
)
|
||||
|
||||
for !p.r.EOF() {
|
||||
if next, ok = p.nextOpcode(); !ok {
|
||||
// Skip one byte to the right and try again. Maybe we are stuck inside
|
||||
// the contents of a string or buffer
|
||||
_, _ = p.r.ReadByte()
|
||||
continue
|
||||
}
|
||||
|
||||
if next.op != opMethod {
|
||||
continue
|
||||
}
|
||||
|
||||
// Parse pkg len; if this fails then this is not a method declaration
|
||||
curOffset = p.r.Offset()
|
||||
if pkgLen, ok = p.parsePkgLength(); !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// Parse method name
|
||||
if method, ok = p.parseNameString(); !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// The next byte encodes the method flags which also contains the arg count
|
||||
// at bits 0:2
|
||||
if flags, ok = p.parseNumConstant(1); !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
p.methodArgCount[method] = uint8(flags) & 0x7
|
||||
|
||||
// At this point we can use the pkg length to skip over the term list
|
||||
p.r.SetOffset(curOffset + pkgLen)
|
||||
}
|
||||
|
||||
p.r.SetOffset(startOffset)
|
||||
}
|
||||
|
||||
// parseObjList tries to parse an AML object list. Object lists are usually
|
||||
// specified together with a pkgLen block which is used to calculate the max
|
||||
// read offset that the parser may reach.
|
||||
@ -96,7 +174,7 @@ func (p *Parser) parseObj() bool {
|
||||
curOffset = p.r.Offset()
|
||||
if info, ok = p.nextOpcode(); !ok {
|
||||
p.r.SetOffset(curOffset)
|
||||
return p.parseMethodInvocationOrNameRef()
|
||||
return p.parseNamedRef()
|
||||
}
|
||||
|
||||
hasPkgLen := info.flags.is(opFlagHasPkgLen) || info.argFlags.contains(opArgTermList) || info.argFlags.contains(opArgFieldList)
|
||||
@ -352,34 +430,29 @@ func (p *Parser) makeObjForOpcode(info *opcodeInfo) Entity {
|
||||
return obj
|
||||
}
|
||||
|
||||
// parseMethodInvocationOrNameRef attempts to parse a method invocation and its term
|
||||
// args. This method first scans the NameString and performs a lookup. If the
|
||||
// lookup returns a method definition then we consult it to figure out how many
|
||||
// arguments we need to parse.
|
||||
// parseNamedRef attempts to parse either a method invocation or a named
|
||||
// reference. As AML allows for forward references, the actual contents for
|
||||
// this entity will not be known until the entire AML stream has been parsed.
|
||||
//
|
||||
// Grammar:
|
||||
// MethodInvocation := NameString TermArgList
|
||||
// TermArgList = Nothing | TermArg TermArgList
|
||||
// TermArg = Type2Opcode | DataObject | ArgObj | LocalObj | MethodInvocation
|
||||
func (p *Parser) parseMethodInvocationOrNameRef() bool {
|
||||
invocationStartOffset := p.r.Offset()
|
||||
func (p *Parser) parseNamedRef() bool {
|
||||
name, ok := p.parseNameString()
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
// Lookup Name and try matching it to a function definition
|
||||
if methodDef, ok := scopeFind(p.scopeCurrent(), p.root, name).(*Method); ok {
|
||||
var (
|
||||
invocation = &methodInvocationEntity{
|
||||
methodDef: methodDef,
|
||||
}
|
||||
curOffset uint32
|
||||
argIndex uint8
|
||||
arg Entity
|
||||
)
|
||||
var (
|
||||
curOffset uint32
|
||||
argIndex uint8
|
||||
arg Entity
|
||||
argList []interface{}
|
||||
)
|
||||
|
||||
for argIndex < methodDef.argCount && !p.r.EOF() {
|
||||
if argCount, isMethod := p.methodArgCount[name]; isMethod {
|
||||
for argIndex < argCount && !p.r.EOF() {
|
||||
// Peek next opcode
|
||||
curOffset = p.r.Offset()
|
||||
nextOpcode, ok := p.nextOpcode()
|
||||
@ -390,7 +463,7 @@ func (p *Parser) parseMethodInvocationOrNameRef() bool {
|
||||
arg, ok = p.parseArgObj()
|
||||
default:
|
||||
// It may be a nested invocation or named ref
|
||||
ok = p.parseMethodInvocationOrNameRef()
|
||||
ok = p.parseNamedRef()
|
||||
if ok {
|
||||
arg = p.scopeCurrent().lastChild()
|
||||
p.scopeCurrent().removeChild(arg)
|
||||
@ -403,23 +476,24 @@ func (p *Parser) parseMethodInvocationOrNameRef() bool {
|
||||
break
|
||||
}
|
||||
|
||||
invocation.setArg(argIndex, arg)
|
||||
argList = append(argList, arg)
|
||||
argIndex++
|
||||
}
|
||||
|
||||
if argIndex != methodDef.argCount {
|
||||
kfmt.Fprintf(p.errWriter, "[table: %s, offset: %d] argument mismatch (exp: %d, got %d) for invocation of method: %s\n", p.tableName, invocationStartOffset, methodDef.argCount, argIndex, name)
|
||||
// Check whether all expected arguments have been parsed
|
||||
if argIndex != argCount {
|
||||
kfmt.Fprintf(p.errWriter, "[table: %s, offset: %d] unexpected arglist end for method %s invocation: expected %d; got %d\n", p.tableName, p.r.Offset(), name, argCount, argIndex)
|
||||
return false
|
||||
}
|
||||
|
||||
p.scopeCurrent().Append(invocation)
|
||||
return true
|
||||
return p.scopeCurrent().Append(&methodInvocationEntity{
|
||||
unnamedEntity: unnamedEntity{args: argList},
|
||||
methodName: name,
|
||||
})
|
||||
}
|
||||
|
||||
// This is a name reference; assume it's a forward reference for now
|
||||
// and delegate its resolution to a post-parse step.
|
||||
p.scopeCurrent().Append(&namedReference{targetName: name})
|
||||
return true
|
||||
// Otherwise this is a reference to a named entity
|
||||
return p.scopeCurrent().Append(&namedReference{targetName: name})
|
||||
}
|
||||
|
||||
func (p *Parser) nextOpcode() (*opcodeInfo, bool) {
|
||||
@ -830,7 +904,7 @@ func (p *Parser) parseTarget() (interface{}, bool) {
|
||||
}
|
||||
|
||||
// In this case, this is either a NameString or a control method invocation.
|
||||
if ok := p.parseMethodInvocationOrNameRef(); ok {
|
||||
if ok := p.parseNamedRef(); ok {
|
||||
obj := p.scopeCurrent().lastChild()
|
||||
p.scopeCurrent().removeChild(obj)
|
||||
return obj, ok
|
||||
|
@ -3,6 +3,7 @@ package aml
|
||||
import (
|
||||
"gopheros/device/acpi/table"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
@ -14,6 +15,7 @@ func TestParser(t *testing.T) {
|
||||
specs := [][]string{
|
||||
[]string{"DSDT.aml", "SSDT.aml"},
|
||||
[]string{"parser-testsuite-DSDT.aml"},
|
||||
[]string{"parser-testsuite-fwd-decls-DSDT.aml"},
|
||||
}
|
||||
|
||||
for specIndex, spec := range specs {
|
||||
@ -29,7 +31,11 @@ func TestParser(t *testing.T) {
|
||||
rootNS.Append(&scopeEntity{op: opScope, name: `_SI_`}) // System indicators
|
||||
rootNS.Append(&scopeEntity{op: opScope, name: `_TZ_`}) // ACPI 1.0 thermal zone namespace
|
||||
|
||||
p := NewParser(ioutil.Discard, rootNS)
|
||||
// Inject pre-defined OSPM objects
|
||||
rootNS.Append(&constEntity{name: "_OS_", val: "gopheros"})
|
||||
rootNS.Append(&constEntity{name: "_REV", val: uint64(2)})
|
||||
|
||||
p := NewParser(os.Stderr, rootNS)
|
||||
|
||||
for _, tableName := range spec {
|
||||
tableName = strings.Replace(tableName, ".aml", "", -1)
|
||||
@ -91,6 +97,30 @@ func TestTableHandleAssignment(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestParserForwardDeclParsing(t *testing.T) {
|
||||
var resolver = mockResolver{
|
||||
tableFiles: []string{"parser-testsuite-fwd-decls-DSDT.aml"},
|
||||
}
|
||||
|
||||
// Create default scopes
|
||||
rootNS := &scopeEntity{op: opScope, name: `\`}
|
||||
rootNS.Append(&scopeEntity{op: opScope, name: `_GPE`}) // General events in GPE register block
|
||||
rootNS.Append(&scopeEntity{op: opScope, name: `_PR_`}) // ACPI 1.0 processor namespace
|
||||
rootNS.Append(&scopeEntity{op: opScope, name: `_SB_`}) // System bus with all device objects
|
||||
rootNS.Append(&scopeEntity{op: opScope, name: `_SI_`}) // System indicators
|
||||
rootNS.Append(&scopeEntity{op: opScope, name: `_TZ_`}) // ACPI 1.0 thermal zone namespace
|
||||
|
||||
p := NewParser(ioutil.Discard, rootNS)
|
||||
|
||||
for _, tableName := range resolver.tableFiles {
|
||||
tableName = strings.Replace(tableName, ".aml", "", -1)
|
||||
if err := p.ParseAML(0, tableName, resolver.LookupTable(tableName)); err != nil {
|
||||
t.Errorf("[%s]: %v", tableName, err)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsePkgLength(t *testing.T) {
|
||||
specs := []struct {
|
||||
payload []byte
|
||||
@ -307,13 +337,12 @@ func TestParserErrorHandling(t *testing.T) {
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("parseMethodInvocationOrNameRef errors", func(t *testing.T) {
|
||||
t.Run("parseNamedRef errors", func(t *testing.T) {
|
||||
t.Run("missing args", func(t *testing.T) {
|
||||
p.root = &scopeEntity{op: opScope, name: `\`}
|
||||
p.root.Append(&Method{
|
||||
scopeEntity: scopeEntity{name: "MTHD"},
|
||||
argCount: 10,
|
||||
})
|
||||
p.methodArgCount = map[string]uint8{
|
||||
"MTHD": 10,
|
||||
}
|
||||
|
||||
mockParserPayload(p, []byte{
|
||||
'M', 'T', 'H', 'D',
|
||||
@ -321,8 +350,8 @@ func TestParserErrorHandling(t *testing.T) {
|
||||
})
|
||||
|
||||
p.scopeEnter(p.root)
|
||||
if p.parseMethodInvocationOrNameRef() {
|
||||
t.Fatal("expected parseMethodInvocationOrNameRef to return false")
|
||||
if p.parseNamedRef() {
|
||||
t.Fatal("expected parseNamedRef to return false")
|
||||
}
|
||||
})
|
||||
})
|
||||
@ -556,6 +585,77 @@ func TestParserErrorHandling(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestDetectMethodDeclarations(t *testing.T) {
|
||||
p := &Parser{
|
||||
errWriter: ioutil.Discard,
|
||||
}
|
||||
|
||||
validMethod := []byte{
|
||||
byte(opMethod),
|
||||
5, // pkgLen
|
||||
'M', 'T', 'H', 'D',
|
||||
2, // flags (2 args)
|
||||
}
|
||||
|
||||
t.Run("success", func(t *testing.T) {
|
||||
mockParserPayload(p, validMethod)
|
||||
p.methodArgCount = make(map[string]uint8)
|
||||
p.detectMethodDeclarations()
|
||||
|
||||
argCount, inMap := p.methodArgCount["MTHD"]
|
||||
if !inMap {
|
||||
t.Error(`detectMethodDeclarations failed to parse method "MTHD"`)
|
||||
}
|
||||
|
||||
if exp := uint8(2); argCount != exp {
|
||||
t.Errorf(`expected arg count for "MTHD" to be %d; got %d`, exp, argCount)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("bad pkgLen", func(t *testing.T) {
|
||||
mockParserPayload(p, []byte{
|
||||
byte(opMethod),
|
||||
// lead byte bits (6:7) indicate 1 extra byte that is missing
|
||||
byte(1 << 6),
|
||||
})
|
||||
|
||||
p.methodArgCount = make(map[string]uint8)
|
||||
p.detectMethodDeclarations()
|
||||
})
|
||||
|
||||
t.Run("error parsing namestring", func(t *testing.T) {
|
||||
mockParserPayload(p, append([]byte{
|
||||
byte(opMethod),
|
||||
byte(5), // pkgLen
|
||||
10, // bogus char, not part of namestring
|
||||
}, validMethod...))
|
||||
|
||||
p.methodArgCount = make(map[string]uint8)
|
||||
p.detectMethodDeclarations()
|
||||
|
||||
argCount, inMap := p.methodArgCount["MTHD"]
|
||||
if !inMap {
|
||||
t.Error(`detectMethodDeclarations failed to parse method "MTHD"`)
|
||||
}
|
||||
|
||||
if exp := uint8(2); argCount != exp {
|
||||
t.Errorf(`expected arg count for "MTHD" to be %d; got %d`, exp, argCount)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("error parsing method flags", func(t *testing.T) {
|
||||
mockParserPayload(p, []byte{
|
||||
byte(opMethod),
|
||||
byte(5), // pkgLen
|
||||
'F', 'O', 'O', 'F',
|
||||
// Missing flag byte
|
||||
})
|
||||
|
||||
p.methodArgCount = make(map[string]uint8)
|
||||
p.detectMethodDeclarations()
|
||||
})
|
||||
}
|
||||
|
||||
func mockParserPayload(p *Parser, payload []byte) *table.SDTHeader {
|
||||
resolver := fixedPayloadResolver{payload}
|
||||
header := resolver.LookupTable("DSDT")
|
||||
|
@ -36,12 +36,20 @@ func scopeVisit(depth int, ent Entity, entType EntityType, visitorFn Visitor) bo
|
||||
if !visitorFn(depth, ent) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Visit any args that are also entities
|
||||
for _, arg := range ent.getArgs() {
|
||||
if argEnt, isEnt := arg.(Entity); isEnt && !scopeVisit(depth+1, argEnt, entType, visitorFn) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the entity defines a scope we need to visit the child entities.
|
||||
if scopeEnt, ok := ent.(ScopeEntity); ok {
|
||||
for _, child := range scopeEnt.Children() {
|
||||
scopeVisit(depth+1, child, entType, visitorFn)
|
||||
switch typ := ent.(type) {
|
||||
case ScopeEntity:
|
||||
// If the entity defines a scope we need to visit the child entities.
|
||||
for _, child := range typ.Children() {
|
||||
_ = scopeVisit(depth+1, child, entType, visitorFn)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,9 @@ func TestScopeVisit(t *testing.T) {
|
||||
scopeMap := genTestScopes()
|
||||
root := scopeMap[`\`].(*scopeEntity)
|
||||
|
||||
keepRecursing := func(Entity) bool { return true }
|
||||
stopRecursing := func(Entity) bool { return false }
|
||||
|
||||
// Append special entities under IDE0
|
||||
ide := scopeMap["IDE0"].(*scopeEntity)
|
||||
ide.Append(&Device{})
|
||||
@ -26,26 +29,53 @@ func TestScopeVisit(t *testing.T) {
|
||||
ide.Append(&Method{})
|
||||
ide.Append(&Method{})
|
||||
ide.Append(&Method{})
|
||||
ide.Append(&methodInvocationEntity{
|
||||
unnamedEntity: unnamedEntity{
|
||||
args: []interface{}{
|
||||
&constEntity{val: uint64(1)},
|
||||
&constEntity{val: uint64(2)},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
specs := []struct {
|
||||
searchType EntityType
|
||||
keepRecursing bool
|
||||
wantHits int
|
||||
searchType EntityType
|
||||
keepRecursingFn func(Entity) bool
|
||||
wantHits int
|
||||
}{
|
||||
{EntityTypeAny, true, 21},
|
||||
{EntityTypeAny, false, 1},
|
||||
{EntityTypeDevice, true, 1},
|
||||
{EntityTypeProcessor, true, 2},
|
||||
{EntityTypePowerResource, true, 3},
|
||||
{EntityTypeThermalZone, true, 4},
|
||||
{EntityTypeMethod, true, 5},
|
||||
{EntityTypeAny, keepRecursing, 24},
|
||||
{EntityTypeAny, stopRecursing, 1},
|
||||
{
|
||||
EntityTypeAny,
|
||||
func(ent Entity) bool {
|
||||
// Stop recursing after visiting the methodInvocationEntity
|
||||
_, isInv := ent.(*methodInvocationEntity)
|
||||
return !isInv
|
||||
},
|
||||
22,
|
||||
},
|
||||
|
||||
{
|
||||
EntityTypeAny,
|
||||
func(ent Entity) bool {
|
||||
// Stop recursing after visiting the first constEntity
|
||||
_, isConst := ent.(*constEntity)
|
||||
return !isConst
|
||||
},
|
||||
23,
|
||||
},
|
||||
{EntityTypeDevice, keepRecursing, 1},
|
||||
{EntityTypeProcessor, keepRecursing, 2},
|
||||
{EntityTypePowerResource, keepRecursing, 3},
|
||||
{EntityTypeThermalZone, keepRecursing, 4},
|
||||
{EntityTypeMethod, keepRecursing, 5},
|
||||
}
|
||||
|
||||
for specIndex, spec := range specs {
|
||||
var hits int
|
||||
scopeVisit(0, root, spec.searchType, func(_ int, obj Entity) bool {
|
||||
hits++
|
||||
return spec.keepRecursing
|
||||
return spec.keepRecursingFn(obj)
|
||||
})
|
||||
|
||||
if hits != spec.wantHits {
|
||||
|
Binary file not shown.
@ -49,6 +49,8 @@ DefinitionBlock ("parser-testsuite-DSDT.aml", "DSDT", 2, "GOPHER", "GOPHEROS", 0
|
||||
|
||||
// Other entity types
|
||||
Event(HLO0)
|
||||
Mutex(MUT0,1)
|
||||
Signal(HLO0)
|
||||
|
||||
// Other executable bits
|
||||
Method (EXE0, 1, Serialized)
|
||||
@ -79,12 +81,10 @@ DefinitionBlock ("parser-testsuite-DSDT.aml", "DSDT", 2, "GOPHER", "GOPHEROS", 0
|
||||
Reset(HLO0)
|
||||
|
||||
// Mutex support
|
||||
Mutex(MUT0, 1)
|
||||
Acquire(MUT0, 0xffff) // no timeout
|
||||
Release(MUT0)
|
||||
|
||||
// Signal/Wait
|
||||
Signal(HLO0)
|
||||
Wait(HLO0, 0xffff)
|
||||
|
||||
// Get monotonic timer value
|
||||
|
Binary file not shown.
@ -0,0 +1,16 @@
|
||||
DefinitionBlock ("parser-testsuite-fwd-decls-DSDT.aml", "DSDT", 2, "GOPHER", "GOPHEROS", 0x00000002)
|
||||
{
|
||||
Method(NST0, 1, NotSerialized)
|
||||
{
|
||||
// Invoke a method which has not been defined at the time the parser
|
||||
// reaches this block (forward declaration)
|
||||
Return(NST1(Arg0))
|
||||
}
|
||||
|
||||
// The declaration of NST1 in the AML stream occurs after the declaration
|
||||
// of NST0 method above.
|
||||
Method(NST1, 1, NotSerialized)
|
||||
{
|
||||
Return(Arg0+42)
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user