src/scriptobject/common/ClassDefinition.ps1
# Copyright 2019, Adam Edwards # # 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. # This provides a system-indepent definition of what defines a class # of objects for use in a type system. In theory types defined this # way are not tied to a particular implementation in terms of how # objects of the class are instantiated and execute at runtime. class Method { [string] $name [ScriptBlock] $block [bool] $isStatic [bool] $isSystem Method([string] $name, [ScriptBlock] $methodBlock, $isStatic, $isSystem) { $this.name = $name $this.block = $methodBlock $this.isStatic = $isStatic $this.isSystem = $isSystem } } class Property { [string] $name = $null [type] $type = $null [object] $value = $null [bool] $isStatic [bool] $isSystem [bool] $isReadOnly Property([string] $name, $value, [bool] $isStatic, [bool] $isSystem, [bool] $isReadOnly) { $this.isStatic = $isStatic $this.isSystem = $isSystem $this.isReadOnly = $isReadOnly $this.name = $name if ( $value -is [TypedValue] ) { $this.type = $value.type $this.value = $value.value } else { $this.type = $null $this.value = $value } } } class TypedValue { [type] $type = $null [object] $value = $null TypedValue($type, $value) { $this.type = $type $this.value = $value } } class ClassInfo { ClassInfo([ClassDefinition] $classDefinition, $prototype, $module) { $this.classDefinition = $classDefinition $this.prototype = $prototype $this.module = $module } [ClassInfo] NewCopy() { $newDefinition = $this.classDefinition.NewCopy() $newPrototype = $this.prototype.psobject.copy() return [ClassInfo]::new($newDefinition, $newPrototype, $this.module) } [ClassDefinition] $classDefinition $prototype = $null $module = $null } class ClassDefinition { ClassDefinition([string] $name, [Method[]] $instanceMethods, [Method[]] $staticMethods, [Property[]] $instanceProperties, [Property[]] $staticProperties, $constructorMethodName) { $this.name = $name $this.constructorMethodName = $constructorMethodName foreach ( $instanceMethod in $instanceMethods ) { if ( $constructorMethodName -and $instanceMethod.Name -eq $constructorMethodName ) { $this.constructor = $instanceMethod.block } else { $this.instanceMethods[$instanceMethod.name] = $instanceMethod } } foreach ( $instanceProperty in $instanceProperties ) { $this.instanceProperties[$instanceProperty.name] = $instanceProperty } foreach ( $staticMethod in $staticMethods ) { $this.staticMethods[$staticMethod.name] = $staticMethod } foreach ( $staticProperty in $staticProperties ) { $this.staticProperties[$staticProperty.name] = $staticProperty } } static [ClassDefinition] GetFilteredDefinition([ClassDefinition] $classDefinition, [string[]] $excludedMethods, [string[]] $excludedProperties) { $newDefinition = [ClassDefinition]::new( $classDefinition.name, $null, $null, $null, $null, $null ) $newDefinition.instanceMethods = [ClassDefinition]::GetFilteredTable($classDefinition.instanceMethods, $excludedMethods) $newDefinition.staticMethods = [ClassDefinition]::GetFilteredTable($classDefinition.staticMethods, $excludedMethods) $newDefinition.instanceProperties = [ClassDefinition]::GetFilteredTable($classDefinition.instanceProperties, $excludedProperties) $newDefinition.staticProperties = [ClassDefinition]::GetFilteredTable($classDefinition.staticProperties, $excludedProperties) $newDefinition.constructor = $classDefinition.constructor return $newDefinition } [void] CopyPrototype([bool] $staticContext, $existingObject) { $builder = [NativeObjectBuilder]::new($null, $existingObject, [NativeObjectBuilderMode]::Modify) $this.WritePrototype($builder, $staticContext) } [object] ToPrototype([bool] $staticContext) { $builder = [NativeObjectBuilder]::new($this.name, $null, [NativeObjectBuilderMode]::Create) $this.WritePrototype($builder, $staticContext) return $builder.GetObject() } [Method] GetMethod($methodName, [bool] $static) { $methodTable = if ( $static ) { $this.staticMethods } else { $this.instanceMethods } return $methodTable[$methodName] } [Method[]] GetInstanceMethods() { return $this.instanceMethods.values } [Property[]] GetInstanceProperties() { return $this.instanceProperties.values } [Method[]] GetStaticMethods() { return $this.staticMethods.values } [Property[]] GetStaticProperties() { return $this.staticProperties.values } [ClassDefinition] NewCopy() { $_instanceMethods = $this.CopyClassData($this.InstanceMethods).values $_instanceProperties = $this.CopyClassData($this.InstanceProperties).values $_staticMethods = $this.CopyClassData($this.StaticMethods).values $_staticProperties = $this.CopyClassData($this.StaticProperties).values return $this::new($this.name, $_instanceMethods, $_staticMethods, $_instanceProperties, $_staticProperties, $this.constructorMethodName) } hidden [HashTable] CopyClassData([HashTable] $data) { $newData = @{} foreach ( $elementName in $data.keys ) { $newData.Add($elementName, $data[$elementName].psobject.copy()) } return $newData } hidden [void] WritePrototype([NativeObjectBuilder] $builder, [bool] $staticContext) { $methods = if ( $staticContext ) { $this.staticMethods.values } else { $this.instanceMethods.values } $properties = if ( $staticContext ) { $this.staticProperties.values } else { $this.instanceProperties.values } $methods | foreach { $builder.AddMethod($_.name, $_.block) } $properties | foreach { $builder.AddProperty($_.name, $_.type, $_.value, $_.isReadOnly) } } hidden static [HashTable] GetFilteredTable([HashTable] $source, [string[]] $keyFilter) { $result = @{} if ( $source ) { $source.GetEnumerator() | where name -notin $keyFilter | foreach { $result.Add($_.name, $_.value) } } return $result } [string] $name = $null [string] $constructorMethodName = $null [ScriptBlock] $constructor = $null [HashTable] $instanceMethods = @{} [HashTable] $instanceproperties = @{} [HashTable] $staticMethods = @{} [HashTable] $staticProperties = @{} } |