mirror of
				https://github.com/taigrr/gopher-os
				synced 2025-01-18 04:43:13 -08:00 
			
		
		
		
	acpi: refactor scope lookup code and move it into the entity pkg
This commit is contained in:
		
							parent
							
								
									44896b1680
								
							
						
					
					
						commit
						89882545f2
					
				| @ -1,68 +1,14 @@ | |||||||
| package aml | package entity | ||||||
| 
 | 
 | ||||||
| import "strings" | import "strings" | ||||||
| 
 | 
 | ||||||
| // Visitor is a function invoked by the VM for each AML tree entity that matches | // ResolveScopedPath examines a path expression and attempts to break it down | ||||||
| // a particular type. The return value controls whether the children of this |  | ||||||
| // entity should also be visited. |  | ||||||
| type Visitor func(depth int, obj Entity) (keepRecursing bool) |  | ||||||
| 
 |  | ||||||
| // EntityType defines the type of entity that visitors should inspect. |  | ||||||
| type EntityType uint8 |  | ||||||
| 
 |  | ||||||
| // The list of supported EntityType values. EntityTypeAny works as a wildcard |  | ||||||
| // allowing the visitor to inspect all entities in the AML tree. |  | ||||||
| const ( |  | ||||||
| 	EntityTypeAny EntityType = iota |  | ||||||
| 	EntityTypeDevice |  | ||||||
| 	EntityTypeProcessor |  | ||||||
| 	EntityTypePowerResource |  | ||||||
| 	EntityTypeThermalZone |  | ||||||
| 	EntityTypeMethod |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // scopeVisit descends a scope hierarchy and invokes visitorFn for each entity |  | ||||||
| // that matches entType. |  | ||||||
| func scopeVisit(depth int, ent Entity, entType EntityType, visitorFn Visitor) bool { |  | ||||||
| 	op := ent.getOpcode() |  | ||||||
| 	switch { |  | ||||||
| 	case (entType == EntityTypeAny) || |  | ||||||
| 		(entType == EntityTypeDevice && op == opDevice) || |  | ||||||
| 		(entType == EntityTypeProcessor && op == opProcessor) || |  | ||||||
| 		(entType == EntityTypePowerResource && op == opPowerRes) || |  | ||||||
| 		(entType == EntityTypeThermalZone && op == opThermalZone) || |  | ||||||
| 		(entType == EntityTypeMethod && op == opMethod): |  | ||||||
| 		// If the visitor returned false we should not visit the children |  | ||||||
| 		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 |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	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) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return true |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // scopeResolvePath examines a path expression and attempts to break it down |  | ||||||
| // into a parent and child segment. The parent segment is looked up via the | // into a parent and child segment. The parent segment is looked up via the | ||||||
| // regular scope rules specified in page 252 of the ACPI 6.2 spec. If the | // regular scope rules specified in page 252 of the ACPI 6.2 spec. If the | ||||||
| // parent scope is found then the function returns back the parent entity and | // parent scope is found then the function returns back the parent entity and | ||||||
| // the name of the child that should be appended to it. If the expression | // the name of the child that should be appended to it. If the expression | ||||||
| // lookup fails then the function returns nil, "". | // lookup fails then the function returns nil, "". | ||||||
| func scopeResolvePath(curScope, rootScope ScopeEntity, expr string) (parent ScopeEntity, name string) { | func ResolveScopedPath(curScope, rootScope Container, expr string) (parent Container, name string) { | ||||||
| 	if len(expr) <= 1 { | 	if len(expr) <= 1 { | ||||||
| 		return nil, "" | 		return nil, "" | ||||||
| 	} | 	} | ||||||
| @ -75,8 +21,8 @@ func scopeResolvePath(curScope, rootScope ScopeEntity, expr string) (parent Scop | |||||||
| 			return rootScope, expr[1:] | 			return rootScope, expr[1:] | ||||||
| 		case '^': | 		case '^': | ||||||
| 			lastHatIndex := strings.LastIndexByte(expr, '^') | 			lastHatIndex := strings.LastIndexByte(expr, '^') | ||||||
| 			if target := scopeFind(curScope, rootScope, expr[:lastHatIndex+1]); target != nil { | 			if target := FindInScope(curScope, rootScope, expr[:lastHatIndex+1]); target != nil { | ||||||
| 				return target.(ScopeEntity), expr[lastHatIndex+1:] | 				return target.(Container), expr[lastHatIndex+1:] | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			return nil, "" | 			return nil, "" | ||||||
| @ -86,14 +32,14 @@ func scopeResolvePath(curScope, rootScope ScopeEntity, expr string) (parent Scop | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Pattern looks like: \FOO.BAR.BAZ or ^+FOO.BAR.BAZ or FOO.BAR.BAZ | 	// Pattern looks like: \FOO.BAR.BAZ or ^+FOO.BAR.BAZ or FOO.BAR.BAZ | ||||||
| 	if target := scopeFind(curScope, rootScope, expr[:lastDotIndex]); target != nil { | 	if target := FindInScope(curScope, rootScope, expr[:lastDotIndex]); target != nil { | ||||||
| 		return target.(ScopeEntity), expr[lastDotIndex+1:] | 		return target.(Container), expr[lastDotIndex+1:] | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return nil, "" | 	return nil, "" | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // scopeFind attempts to find an object with the given name using the rules | // FindInScope attempts to find an object with the given name using the rules | ||||||
| // specified in page 252 of the ACPI 6.2 spec: | // specified in page 252 of the ACPI 6.2 spec: | ||||||
| // | // | ||||||
| // There are two types of namespace paths: an absolute namespace path (that is, | // There are two types of namespace paths: an absolute namespace path (that is, | ||||||
| @ -104,7 +50,7 @@ func scopeResolvePath(curScope, rootScope ScopeEntity, expr string) (parent Scop | |||||||
| // or Parent Prefixes, ‘^’, the search rules do not apply. If the search rules | // or Parent Prefixes, ‘^’, the search rules do not apply. If the search rules | ||||||
| // do not apply to a relative namespace path, the namespace object is looked up | // do not apply to a relative namespace path, the namespace object is looked up | ||||||
| // relative to the current namespace | // relative to the current namespace | ||||||
| func scopeFind(curScope, rootScope ScopeEntity, name string) Entity { | func FindInScope(curScope, rootScope Container, name string) Entity { | ||||||
| 	nameLen := len(name) | 	nameLen := len(name) | ||||||
| 	if nameLen == 0 { | 	if nameLen == 0 { | ||||||
| 		return nil | 		return nil | ||||||
| @ -113,7 +59,7 @@ func scopeFind(curScope, rootScope ScopeEntity, name string) Entity { | |||||||
| 	switch { | 	switch { | ||||||
| 	case name[0] == '\\': // relative to the root scope | 	case name[0] == '\\': // relative to the root scope | ||||||
| 		if nameLen > 1 { | 		if nameLen > 1 { | ||||||
| 			return scopeFindRelative(rootScope, name[1:]) | 			return findRelativeToScope(rootScope, name[1:]) | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		// Name was just `\`; this matches the root namespace | 		// Name was just `\`; this matches the root namespace | ||||||
| @ -130,7 +76,7 @@ func scopeFind(curScope, rootScope ScopeEntity, name string) Entity { | |||||||
| 				} | 				} | ||||||
| 			default: | 			default: | ||||||
| 				// Found the start of the name. Look it up relative to curNs | 				// Found the start of the name. Look it up relative to curNs | ||||||
| 				return scopeFindRelative(curScope, name[startIndex:]) | 				return findRelativeToScope(curScope, name[startIndex:]) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| @ -139,7 +85,7 @@ func scopeFind(curScope, rootScope ScopeEntity, name string) Entity { | |||||||
| 	case strings.ContainsRune(name, '.'): | 	case strings.ContainsRune(name, '.'): | ||||||
| 		// If the name contains any '.' then we still need to look it | 		// If the name contains any '.' then we still need to look it | ||||||
| 		// up relative to the current scope | 		// up relative to the current scope | ||||||
| 		return scopeFindRelative(curScope, name) | 		return findRelativeToScope(curScope, name) | ||||||
| 	default: | 	default: | ||||||
| 		// We can apply the search rules described by the spec | 		// We can apply the search rules described by the spec | ||||||
| 		for s := curScope; s != nil; s = s.Parent() { | 		for s := curScope; s != nil; s = s.Parent() { | ||||||
| @ -155,11 +101,11 @@ func scopeFind(curScope, rootScope ScopeEntity, name string) Entity { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // scopeFindRelative returns the Entity referenced by path relative | // findRelativeToScope returns the Entity referenced by path relative | ||||||
| // to the provided Namespace. If the name contains dots, each segment | // to the provided Namespace. If the name contains dots, each segment | ||||||
| // is used to access a nested namespace. If the path does not point | // is used to access a nested namespace. If the path does not point | ||||||
| // to a NamedObject then lookupRelativeTo returns back nil. | // to a NamedObject then lookupRelativeTo returns back nil. | ||||||
| func scopeFindRelative(ns ScopeEntity, path string) Entity { | func findRelativeToScope(ns Container, path string) Entity { | ||||||
| 	var matchName string | 	var matchName string | ||||||
| matchNextPathSegment: | matchNextPathSegment: | ||||||
| 	for { | 	for { | ||||||
| @ -170,12 +116,7 @@ matchNextPathSegment: | |||||||
| 
 | 
 | ||||||
| 			// Search for a scoped child named "matchName" | 			// Search for a scoped child named "matchName" | ||||||
| 			for _, child := range ns.Children() { | 			for _, child := range ns.Children() { | ||||||
| 				childNs, ok := child.(ScopeEntity) | 				if childNs, ok := child.(Container); ok && childNs.Name() == matchName { | ||||||
| 				if !ok { |  | ||||||
| 					continue |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				if childNs.Name() == matchName { |  | ||||||
| 					ns = childNs | 					ns = childNs | ||||||
| 					continue matchNextPathSegment | 					continue matchNextPathSegment | ||||||
| 				} | 				} | ||||||
							
								
								
									
										215
									
								
								src/gopheros/device/acpi/aml/entity/scope_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										215
									
								
								src/gopheros/device/acpi/aml/entity/scope_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,215 @@ | |||||||
|  | package entity | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"reflect" | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func TestResolveScopedPath(t *testing.T) { | ||||||
|  | 	scopeMap := genTestScopes() | ||||||
|  | 
 | ||||||
|  | 	specs := []struct { | ||||||
|  | 		curScope   Container | ||||||
|  | 		pathExpr   string | ||||||
|  | 		wantParent Entity | ||||||
|  | 		wantName   string | ||||||
|  | 	}{ | ||||||
|  | 		{ | ||||||
|  | 			scopeMap["IDE0"].(Container), | ||||||
|  | 			`\_SB_`, | ||||||
|  | 			scopeMap[`\`], | ||||||
|  | 			"_SB_", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			scopeMap["IDE0"].(Container), | ||||||
|  | 			`^FOO`, | ||||||
|  | 			scopeMap[`PCI0`], | ||||||
|  | 			"FOO", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			scopeMap["IDE0"].(Container), | ||||||
|  | 			`^^FOO`, | ||||||
|  | 			scopeMap[`_SB_`], | ||||||
|  | 			"FOO", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			scopeMap["IDE0"].(Container), | ||||||
|  | 			`_ADR`, | ||||||
|  | 			scopeMap[`IDE0`], | ||||||
|  | 			"_ADR", | ||||||
|  | 		}, | ||||||
|  | 		// Paths with dots | ||||||
|  | 		{ | ||||||
|  | 			scopeMap["IDE0"].(Container), | ||||||
|  | 			`\_SB_.PCI0.IDE0._ADR`, | ||||||
|  | 			scopeMap[`IDE0`], | ||||||
|  | 			"_ADR", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			scopeMap["PCI0"].(Container), | ||||||
|  | 			`IDE0._ADR`, | ||||||
|  | 			scopeMap[`IDE0`], | ||||||
|  | 			"_ADR", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			scopeMap["PCI0"].(Container), | ||||||
|  | 			`_CRS`, | ||||||
|  | 			scopeMap[`PCI0`], | ||||||
|  | 			"_CRS", | ||||||
|  | 		}, | ||||||
|  | 		// Bad queries | ||||||
|  | 		{ | ||||||
|  | 			scopeMap["PCI0"].(Container), | ||||||
|  | 			`FOO.BAR.BAZ`, | ||||||
|  | 			nil, | ||||||
|  | 			"", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			scopeMap["PCI0"].(Container), | ||||||
|  | 			``, | ||||||
|  | 			nil, | ||||||
|  | 			"", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			scopeMap["PCI0"].(Container), | ||||||
|  | 			`\`, | ||||||
|  | 			nil, | ||||||
|  | 			"", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			scopeMap["PCI0"].(Container), | ||||||
|  | 			`^^^^^^^^^BADPATH`, | ||||||
|  | 			nil, | ||||||
|  | 			"", | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	root := scopeMap[`\`].(Container) | ||||||
|  | 	for specIndex, spec := range specs { | ||||||
|  | 		gotParent, gotName := ResolveScopedPath(spec.curScope, root, spec.pathExpr) | ||||||
|  | 		if !reflect.DeepEqual(gotParent, spec.wantParent) { | ||||||
|  | 			t.Errorf("[spec %d] expected lookup to return %#v; got %#v", specIndex, spec.wantParent, gotParent) | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if gotName != spec.wantName { | ||||||
|  | 			t.Errorf("[spec %d] expected lookup to return node name %q; got %q", specIndex, spec.wantName, gotName) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestFindInScope(t *testing.T) { | ||||||
|  | 	scopeMap := genTestScopes() | ||||||
|  | 
 | ||||||
|  | 	specs := []struct { | ||||||
|  | 		curScope Container | ||||||
|  | 		lookup   string | ||||||
|  | 		want     Entity | ||||||
|  | 	}{ | ||||||
|  | 		// Search rules do not apply for these cases | ||||||
|  | 		{ | ||||||
|  | 			scopeMap["PCI0"].(Container), | ||||||
|  | 			`\`, | ||||||
|  | 			scopeMap[`\`], | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			scopeMap["PCI0"].(Container), | ||||||
|  | 			"IDE0._ADR", | ||||||
|  | 			scopeMap["_ADR"], | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			scopeMap["IDE0"].(Container), | ||||||
|  | 			"^^PCI0.IDE0._ADR", | ||||||
|  | 			scopeMap["_ADR"], | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			scopeMap["IDE0"].(Container), | ||||||
|  | 			`\_SB_.PCI0.IDE0._ADR`, | ||||||
|  | 			scopeMap["_ADR"], | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			scopeMap["IDE0"].(Container), | ||||||
|  | 			`\_SB_.PCI0`, | ||||||
|  | 			scopeMap["PCI0"], | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			scopeMap["IDE0"].(Container), | ||||||
|  | 			`^`, | ||||||
|  | 			scopeMap["PCI0"], | ||||||
|  | 		}, | ||||||
|  | 		// Bad queries | ||||||
|  | 		{ | ||||||
|  | 			scopeMap["_SB_"].(Container), | ||||||
|  | 			"PCI0.USB._CRS", | ||||||
|  | 			nil, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			scopeMap["IDE0"].(Container), | ||||||
|  | 			"^^^^^^^^^^^^^^^^^^^", | ||||||
|  | 			nil, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			scopeMap["IDE0"].(Container), | ||||||
|  | 			`^^^^^^^^^^^FOO`, | ||||||
|  | 			nil, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			scopeMap["IDE0"].(Container), | ||||||
|  | 			"FOO", | ||||||
|  | 			nil, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			scopeMap["IDE0"].(Container), | ||||||
|  | 			"", | ||||||
|  | 			nil, | ||||||
|  | 		}, | ||||||
|  | 		// Search rules apply for these cases | ||||||
|  | 		{ | ||||||
|  | 			scopeMap["IDE0"].(Container), | ||||||
|  | 			"_CRS", | ||||||
|  | 			scopeMap["_CRS"], | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	root := scopeMap[`\`].(Container) | ||||||
|  | 	for specIndex, spec := range specs { | ||||||
|  | 		if got := FindInScope(spec.curScope, root, spec.lookup); !reflect.DeepEqual(got, spec.want) { | ||||||
|  | 			t.Errorf("[spec %d] expected lookup to return %#v; got %#v", specIndex, spec.want, got) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func genTestScopes() map[string]Entity { | ||||||
|  | 	// Setup the example tree from page 252 of the acpi 6.2 spec | ||||||
|  | 	// \ | ||||||
|  | 	//  SB | ||||||
|  | 	//    \ | ||||||
|  | 	//     PCI0 | ||||||
|  | 	//         | _CRS | ||||||
|  | 	//         \ | ||||||
|  | 	//          IDE0 | ||||||
|  | 	//              | _ADR | ||||||
|  | 	ideScope := NewScope(OpScope, 42, `IDE0`) | ||||||
|  | 	pciScope := NewScope(OpScope, 42, `PCI0`) | ||||||
|  | 	sbScope := NewScope(OpScope, 42, `_SB_`) | ||||||
|  | 	rootScope := NewScope(OpScope, 42, `\`) | ||||||
|  | 
 | ||||||
|  | 	adr := NewMethod(42, `_ADR`) | ||||||
|  | 	crs := NewMethod(42, `_CRS`) | ||||||
|  | 
 | ||||||
|  | 	// Setup tree | ||||||
|  | 	ideScope.Append(adr) | ||||||
|  | 	pciScope.Append(crs) | ||||||
|  | 	pciScope.Append(ideScope) | ||||||
|  | 	sbScope.Append(pciScope) | ||||||
|  | 	rootScope.Append(sbScope) | ||||||
|  | 
 | ||||||
|  | 	return map[string]Entity{ | ||||||
|  | 		"IDE0": ideScope, | ||||||
|  | 		"PCI0": pciScope, | ||||||
|  | 		"_SB_": sbScope, | ||||||
|  | 		"\\":   rootScope, | ||||||
|  | 		"_ADR": adr, | ||||||
|  | 		"_CRS": crs, | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @ -1,294 +0,0 @@ | |||||||
| package aml |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"reflect" |  | ||||||
| 	"testing" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| 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{}) |  | ||||||
| 	ide.Append(&namedEntity{op: opProcessor}) |  | ||||||
| 	ide.Append(&namedEntity{op: opProcessor}) |  | ||||||
| 	ide.Append(&namedEntity{op: opPowerRes}) |  | ||||||
| 	ide.Append(&namedEntity{op: opPowerRes}) |  | ||||||
| 	ide.Append(&namedEntity{op: opPowerRes}) |  | ||||||
| 	ide.Append(&namedEntity{op: opThermalZone}) |  | ||||||
| 	ide.Append(&namedEntity{op: opThermalZone}) |  | ||||||
| 	ide.Append(&namedEntity{op: opThermalZone}) |  | ||||||
| 	ide.Append(&namedEntity{op: opThermalZone}) |  | ||||||
| 	ide.Append(&Method{}) |  | ||||||
| 	ide.Append(&Method{}) |  | ||||||
| 	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 |  | ||||||
| 		keepRecursingFn func(Entity) bool |  | ||||||
| 		wantHits        int |  | ||||||
| 	}{ |  | ||||||
| 		{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.keepRecursingFn(obj) |  | ||||||
| 		}) |  | ||||||
| 
 |  | ||||||
| 		if hits != spec.wantHits { |  | ||||||
| 			t.Errorf("[spec %d] expected visitor to be called %d times; got %d", specIndex, spec.wantHits, hits) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func TestScopeResolvePath(t *testing.T) { |  | ||||||
| 	scopeMap := genTestScopes() |  | ||||||
| 
 |  | ||||||
| 	specs := []struct { |  | ||||||
| 		curScope   ScopeEntity |  | ||||||
| 		pathExpr   string |  | ||||||
| 		wantParent Entity |  | ||||||
| 		wantName   string |  | ||||||
| 	}{ |  | ||||||
| 		{ |  | ||||||
| 			scopeMap["IDE0"].(*scopeEntity), |  | ||||||
| 			`\_SB_`, |  | ||||||
| 			scopeMap[`\`], |  | ||||||
| 			"_SB_", |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			scopeMap["IDE0"].(*scopeEntity), |  | ||||||
| 			`^FOO`, |  | ||||||
| 			scopeMap[`PCI0`], |  | ||||||
| 			"FOO", |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			scopeMap["IDE0"].(*scopeEntity), |  | ||||||
| 			`^^FOO`, |  | ||||||
| 			scopeMap[`_SB_`], |  | ||||||
| 			"FOO", |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			scopeMap["IDE0"].(*scopeEntity), |  | ||||||
| 			`_ADR`, |  | ||||||
| 			scopeMap[`IDE0`], |  | ||||||
| 			"_ADR", |  | ||||||
| 		}, |  | ||||||
| 		// Paths with dots |  | ||||||
| 		{ |  | ||||||
| 			scopeMap["IDE0"].(*scopeEntity), |  | ||||||
| 			`\_SB_.PCI0.IDE0._ADR`, |  | ||||||
| 			scopeMap[`IDE0`], |  | ||||||
| 			"_ADR", |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			scopeMap["PCI0"].(*scopeEntity), |  | ||||||
| 			`IDE0._ADR`, |  | ||||||
| 			scopeMap[`IDE0`], |  | ||||||
| 			"_ADR", |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			scopeMap["PCI0"].(*scopeEntity), |  | ||||||
| 			`_CRS`, |  | ||||||
| 			scopeMap[`PCI0`], |  | ||||||
| 			"_CRS", |  | ||||||
| 		}, |  | ||||||
| 		// Bad queries |  | ||||||
| 		{ |  | ||||||
| 			scopeMap["PCI0"].(*scopeEntity), |  | ||||||
| 			`FOO.BAR.BAZ`, |  | ||||||
| 			nil, |  | ||||||
| 			"", |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			scopeMap["PCI0"].(*scopeEntity), |  | ||||||
| 			``, |  | ||||||
| 			nil, |  | ||||||
| 			"", |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			scopeMap["PCI0"].(*scopeEntity), |  | ||||||
| 			`\`, |  | ||||||
| 			nil, |  | ||||||
| 			"", |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			scopeMap["PCI0"].(*scopeEntity), |  | ||||||
| 			`^^^^^^^^^BADPATH`, |  | ||||||
| 			nil, |  | ||||||
| 			"", |  | ||||||
| 		}, |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	root := scopeMap[`\`].(*scopeEntity) |  | ||||||
| 	for specIndex, spec := range specs { |  | ||||||
| 		gotParent, gotName := scopeResolvePath(spec.curScope, root, spec.pathExpr) |  | ||||||
| 		if !reflect.DeepEqual(gotParent, spec.wantParent) { |  | ||||||
| 			t.Errorf("[spec %d] expected lookup to return %#v; got %#v", specIndex, spec.wantParent, gotParent) |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if gotName != spec.wantName { |  | ||||||
| 			t.Errorf("[spec %d] expected lookup to return node name %q; got %q", specIndex, spec.wantName, gotName) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func TestScopeFind(t *testing.T) { |  | ||||||
| 	scopeMap := genTestScopes() |  | ||||||
| 
 |  | ||||||
| 	specs := []struct { |  | ||||||
| 		curScope ScopeEntity |  | ||||||
| 		lookup   string |  | ||||||
| 		want     Entity |  | ||||||
| 	}{ |  | ||||||
| 		// Search rules do not apply for these cases |  | ||||||
| 		{ |  | ||||||
| 			scopeMap["PCI0"].(*scopeEntity), |  | ||||||
| 			`\`, |  | ||||||
| 			scopeMap[`\`], |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			scopeMap["PCI0"].(*scopeEntity), |  | ||||||
| 			"IDE0._ADR", |  | ||||||
| 			scopeMap["_ADR"], |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			scopeMap["IDE0"].(*scopeEntity), |  | ||||||
| 			"^^PCI0.IDE0._ADR", |  | ||||||
| 			scopeMap["_ADR"], |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			scopeMap["IDE0"].(*scopeEntity), |  | ||||||
| 			`\_SB_.PCI0.IDE0._ADR`, |  | ||||||
| 			scopeMap["_ADR"], |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			scopeMap["IDE0"].(*scopeEntity), |  | ||||||
| 			`\_SB_.PCI0`, |  | ||||||
| 			scopeMap["PCI0"], |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			scopeMap["IDE0"].(*scopeEntity), |  | ||||||
| 			`^`, |  | ||||||
| 			scopeMap["PCI0"], |  | ||||||
| 		}, |  | ||||||
| 		// Bad queries |  | ||||||
| 		{ |  | ||||||
| 			scopeMap["_SB_"].(*scopeEntity), |  | ||||||
| 			"PCI0.USB._CRS", |  | ||||||
| 			nil, |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			scopeMap["IDE0"].(*scopeEntity), |  | ||||||
| 			"^^^^^^^^^^^^^^^^^^^", |  | ||||||
| 			nil, |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			scopeMap["IDE0"].(*scopeEntity), |  | ||||||
| 			`^^^^^^^^^^^FOO`, |  | ||||||
| 			nil, |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			scopeMap["IDE0"].(*scopeEntity), |  | ||||||
| 			"FOO", |  | ||||||
| 			nil, |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			scopeMap["IDE0"].(*scopeEntity), |  | ||||||
| 			"", |  | ||||||
| 			nil, |  | ||||||
| 		}, |  | ||||||
| 		// Search rules apply for these cases |  | ||||||
| 		{ |  | ||||||
| 			scopeMap["IDE0"].(*scopeEntity), |  | ||||||
| 			"_CRS", |  | ||||||
| 			scopeMap["_CRS"], |  | ||||||
| 		}, |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	root := scopeMap[`\`].(*scopeEntity) |  | ||||||
| 	for specIndex, spec := range specs { |  | ||||||
| 		if got := scopeFind(spec.curScope, root, spec.lookup); !reflect.DeepEqual(got, spec.want) { |  | ||||||
| 			t.Errorf("[spec %d] expected lookup to return %#v; got %#v", specIndex, spec.want, got) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func genTestScopes() map[string]Entity { |  | ||||||
| 	// Setup the example tree from page 252 of the acpi 6.2 spec |  | ||||||
| 	// \ |  | ||||||
| 	//  SB |  | ||||||
| 	//    \ |  | ||||||
| 	//     PCI0 |  | ||||||
| 	//         | _CRS |  | ||||||
| 	//         \ |  | ||||||
| 	//          IDE0 |  | ||||||
| 	//              | _ADR |  | ||||||
| 	ideScope := &scopeEntity{name: `IDE0`} |  | ||||||
| 	pciScope := &scopeEntity{name: `PCI0`} |  | ||||||
| 	sbScope := &scopeEntity{name: `_SB_`} |  | ||||||
| 	rootScope := &scopeEntity{name: `\`} |  | ||||||
| 
 |  | ||||||
| 	adr := &namedEntity{name: `_ADR`} |  | ||||||
| 	crs := &namedEntity{name: `_CRS`} |  | ||||||
| 
 |  | ||||||
| 	// Setup tree |  | ||||||
| 	ideScope.Append(adr) |  | ||||||
| 	pciScope.Append(crs) |  | ||||||
| 	pciScope.Append(ideScope) |  | ||||||
| 	sbScope.Append(pciScope) |  | ||||||
| 	rootScope.Append(sbScope) |  | ||||||
| 
 |  | ||||||
| 	return map[string]Entity{ |  | ||||||
| 		"IDE0": ideScope, |  | ||||||
| 		"PCI0": pciScope, |  | ||||||
| 		"_SB_": sbScope, |  | ||||||
| 		"\\":   rootScope, |  | ||||||
| 		"_ADR": adr, |  | ||||||
| 		"_CRS": crs, |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user