aboutsummaryrefslogtreecommitdiffstats
path: root/lib/asn1kit/compile.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/asn1kit/compile.rb')
-rw-r--r--lib/asn1kit/compile.rb707
1 files changed, 707 insertions, 0 deletions
diff --git a/lib/asn1kit/compile.rb b/lib/asn1kit/compile.rb
new file mode 100644
index 0000000..fd95a6b
--- /dev/null
+++ b/lib/asn1kit/compile.rb
@@ -0,0 +1,707 @@
+module ASN1Kit
+ module Internal
+ using ::Module.new {
+ refine(Object) do
+ def unreachable
+ raise "internal error: reached unreachable"
+ end
+ end
+
+ include ASN1Kit::Internal::CompileType
+ include ASN1Kit::Internal::CompileBitString
+ include ASN1Kit::Internal::CompileEnumerated
+ include ASN1Kit::Internal::CompileSequence
+ include ASN1Kit::Internal::CompileSequenceOf
+ include ASN1Kit::Internal::CompileSetOf
+ include ASN1Kit::Internal::CompileObjectIdentifier
+ include ASN1Kit::Internal::CompileRelativeOID
+ }
+
+ class TypeReference
+ attr_reader :name
+
+ def initialize(name)
+ @name = name
+ end
+
+ def inspect_abbrev
+ inspect
+ end
+
+ def tagging(tagging, tag_class, tag_number)
+ if defined?(@tagging)
+ unreachable
+ end
+ @tagging = tagging
+ @tag_class = tag_class
+ @tag_number = tag_number
+ self
+ end
+
+ def unwrap(state)
+ type = state.type_symbols[@name] or
+ raise "unresolved typereference: %s" % @name
+ if type.is_a?(TypeReference)
+ unreachable
+ end
+
+ if defined?(@tagging)
+ orig = type
+ type = type.tagging(@tagging, @tag_class, @tag_number)
+ state.mark_resolved(type) if orig.alias
+ end
+ type
+ end
+ end
+
+ class UnresolvedValue
+ attr_accessor :type, :value
+
+ def initialize(type, value)
+ @type = type
+ @value = value
+ end
+
+ private def unwrap_reference(ret, state)
+ while true
+ case ret
+ when Nodes::ValuereferenceNode
+ name = ret.name
+ ret = state.value_symbols[name] or
+ raise ParseError, "unresolved value: %p" % name
+ else
+ break
+ end
+ end
+ ret
+ end
+
+ def unwrap_as_number(state)
+ case ret = unwrap_reference(@value, state)
+ when ::Integer
+ ret
+ when ASN1Kit::Integer
+ ret.value
+ else
+ raise ParseError, "valuereference %p does not resolve to number" %
+ @value.name
+ end
+ end
+
+ def unwrap(state)
+ if @value.is_a?(Nodes::ValuereferenceNode)
+ # DefinedValue, INTEGER, or ENUMERATED
+ if @type <= ASN1Kit::Integer
+ num = @type.named_number(@value.name)
+ return @type.new(num) if num
+ elsif @type <= ASN1Kit::Enumerated
+ return @type.new(@value.name) if @type.name?(@value.name)
+ end
+
+ ret = unwrap_reference(@value, state)
+ state.resolve(ret)
+ if ret.class != @type
+ # Duplicating already-resolved value
+ ret = ret.cast_to(@type)
+ state.mark_resolved(ret)
+ # TODO: Should casted new instance have the same alias?
+ # ret.alias = @value.name
+ end
+ ret
+ else
+ # Not a reference value, but haven't been resolved
+ ret = @value.shallow_compile_to(@type, state)
+ state.resolve(ret)
+ ret
+ end
+ end
+
+ def inspect_abbrev
+ inspect
+ end
+
+ private def inspect_inner
+ "#<$UnresolvedValue #{@type.inspect} #{@value.inspect}>"
+ end
+ end
+
+ class CompileState
+ attr_reader :type_symbols, :value_symbols
+
+ def initialize(extensibility_implied:, tag_default:)
+ @type_symbols = {}
+ @value_symbols = {}
+ @extensibility_implied = extensibility_implied
+ @tag_default = tag_default
+ @done = ::Set.new
+ end
+
+ def mark_resolved(obj)
+ return true if @done.include?(obj)
+ @done << obj
+ nil
+ end
+
+ def resolve(obj)
+ return obj if mark_resolved(obj)
+ obj._compile_fixup(self)
+ obj
+ end
+
+ def merge
+ @type_symbols.merge(@value_symbols)
+ end
+
+ def extensibility_implied?
+ @extensibility_implied
+ end
+
+ def tag_default
+ @tag_default
+ end
+ end
+
+ module Nodes
+ class ModuleDefinitionNode
+ attr_reader :assignments
+
+ def initialize(name, ident, tag_default, extension_default, (exports, imports, assignments))
+ @name = name
+ @ident = ident
+ @tag_default = tag_default
+ @extension_default = extension_default
+
+ @exports = exports
+ @imports = imports
+ @assignments = assignments || []
+ end
+
+ # Compile nodes to Module
+ def compile
+ state = CompileState.new(extensibility_implied: @extension_default, tag_default: @tag_default)
+
+ @assignments.each do |name, val1, val2|
+ # raise "duplicate symbol name: #{@name}.#{name}" if value_symbols[name]
+ if val2
+ # ValueAssignment
+ state.value_symbols[name] = [val1, val2]
+ else
+ # TypeAssignment
+ state.type_symbols[name] = val1
+ end
+ end
+
+ # Step 1. Resolve top-level types
+ state.type_symbols.transform_values! { |typenode|
+ # Here the result is either a (incomplete) subclass of
+ # ASN1Kit::Type or an instance of Internal::TypeReference.
+ typenode.shallow_compile(state)
+ }
+ state.type_symbols.each { |name, type|
+ if type.is_a?(Internal::TypeReference)
+ while type.is_a?(Internal::TypeReference)
+ case type
+ when Internal::TypeReference
+ type = state.type_symbols[type.name] ||
+ raise("unresolved type: %p", type)
+ else break
+ end
+ end
+ # Subclassing to assign a new alias
+ type = Class.new(type)
+ type.alias = name
+ state.mark_resolved(type) # Mark as resolved
+ state.type_symbols[name] = type
+ else
+ # |type| is a builtin type; subclassing
+ if type.alias
+ type = Class.new(type)
+ state.mark_resolved(type) # Mark as resolved
+ state.type_symbols[name] = type
+ end
+ type.alias = name
+ end
+ }
+
+ # Step 2. Compile top-level values
+ state.value_symbols.transform_values! { |typenode, valuenode|
+ type = typenode.shallow_compile(state)
+ type = type.unwrap(state) if type.is_a?(Internal::TypeReference)
+ valuenode.shallow_compile_to(type, state)
+ }
+ state.value_symbols.each { |name, value|
+ if value.is_a?(Internal::UnresolvedValue)
+ # Duplicating to assign a new alias
+ value = value.unwrap(state).dup
+ value.alias = name
+ state.mark_resolved(value) # Mark as resolved
+ state.value_symbols[name] = value
+ else
+ value.alias = name
+ end
+ }
+
+ # Step 3. Fixup incomplete resulting instances
+ state.type_symbols.each { |name, type|
+ state.resolve(type)
+ }
+ state.value_symbols.each { |name, value|
+ state.resolve(value.class) # immediate types
+ state.resolve(value)
+ }
+
+ if @ident
+ oid = @ident.shallow_compile_to(ASN1Kit::ObjectIdentifier, state)
+ state.resolve(oid)
+ else
+ oid = nil
+ end
+ mod = ASN1Kit::Module.new(@name, state.merge, oid: oid)
+
+ mod
+ end
+ end
+
+ class Constant
+ attr_reader :value
+
+ def initialize(value, name: nil)
+ @value = value
+ @name = name
+ end
+ end
+
+ class ValueNode
+ end
+
+ # A 'Value' which is an immediate value
+ class SimpleValueNode < ValueNode
+ def initialize(type, value)
+ @type = type
+ @value = value
+ end
+
+ def shallow_compile_to(type, state)
+ case @type
+ when :bstring
+ v = @value.gsub(/[^01]/, "")
+ decoded = [v].pack("B*")
+ if type <= ASN1Kit::BitString
+ type.new(decoded, v.size)
+ elsif type <= ASN1Kit::OctetString
+ type.new(decoded)
+ else
+ raise "unable to compile bstring into %p" % type
+ end
+ when :hstring
+ v = @value.gsub(/[^0-9A-F]/, "")
+ decoded = [v].pack("H*")
+ if type <= ASN1Kit::BitString
+ type.new(decoded, v.size * 4)
+ elsif type <= ASN1Kit::OctetString
+ type.new(decoded)
+ else
+ raise "unable to compile hstring into %p" % type
+ end
+ when :boolean
+ unless type <= ASN1Kit::Boolean
+ raise "unable to compile BooleanValue into %p" % type
+ end
+ type.new(@value)
+ when :null
+ unless type <= ASN1Kit::Null
+ raise "unable to compile NULL into %p" % type
+ end
+ type.new
+ when :integer
+ # may be RealValue
+ if type <= ASN1Kit::Integer
+ type.new(@value)
+ elsif type <= ASN1Kit::Real
+ type.new(@value)
+ else
+ raise "unable to compile IntegerValue into %p" % type
+ end
+ when :real
+ unless type <= ASN1Kit::Real
+ raise "unable to compile RealValue into %p" % type
+ end
+ v = @value == :PLUS_INFINITY ? ASN1Kit::Real::PLUS_INFINITY :
+ @value == :MINUS_INFINITY ? ASN1Kit::Real::MINUS_INFINITY :
+ @value == :NOT_A_NUMBER ? ASN1Kit::Real::NOT_A_NUMBER : @value
+ type.new(v)
+ else
+ raise "unknown simple value node type: %p" % @type
+ end
+ end
+ end
+
+ # A Value surrounded by "{" and "}"
+ #
+ # value Type ::= { a, b }
+ #
+ # cannot be parsed by the LALR(1) parser, because this requires knowledge
+ # about 'Type'.
+ class UnparsedValueNode < ValueNode
+ def initialize(body)
+ @body = body
+ end
+
+ def shallow_compile_to(type, state)
+ case
+ when type <= ASN1Kit::BitString
+ list = Parser.new.parse("__CompoundBitStringValue " << @body)
+ type.new_with_names(*list)
+ when type <= ASN1Kit::ObjectIdentifier || type <= ASN1Kit::RelativeOID
+ # ObjectIdentifierValueNode
+ oid_value = Parser.new.parse("__ObjectIdentifierValue " << @body)
+ oid_value.shallow_compile_to(type, state)
+ when type <= ASN1Kit::Sequence || type <= ASN1Kit::Set
+ cvl = Parser.new.parse("__SequenceValue " << @body)
+ hash = {}
+ cvl.each do |ident, val|
+ if hash[ident]
+ raise ParseError, "duplicate name in ComponentValueList: %p" %
+ ident
+ end
+ t = type.component_type(ident)
+ unless t
+ raise ParseError, "invalid name in ComponentValueList: %p" %
+ ident
+ end
+ hash[ident] = Internal::UnresolvedValue.new(t, val)
+ end
+ type.new(hash)
+ when type <= ASN1Kit::SequenceOf || type <= ASN1Kit::SetOf
+ # FIXME: iff |type| uses SEQUENCE OF NamedType form,
+ # "{" namedValueList "}" form should be used
+ cvl = Parser.new.parse("__SequenceOfValue " << @body)
+ type.new(cvl)
+ when false
+ # Object
+ else
+ raise ParseError, "unable to parse value as type %p" % type
+ end
+ end
+ end
+
+ # Represents an ObjectIdentifierValue or a RelativeOIDValue
+ class ObjectIdentifierValueNode < ValueNode
+ def initialize(components)
+ @components = components
+ end
+
+ def shallow_compile_to(type, state)
+ unless type <= ASN1Kit::ObjectIdentifier || type <= ASN1Kit::RelativeOID
+ raise ParseError, "ObjectIdentifierValue is incompatible with %p" % type
+ end
+
+ if type <= ASN1Kit::ObjectIdentifier
+ # TODO: This is not implementing the spec properly: e.g.
+ #
+ # a OBJECT IDENTIFIER ::= { 1 data }
+ #
+ # is not supported at the moment.
+ if !@components[0][1]
+ case @components[0][0]
+ when "itu-t", "ccitt"
+ @components[0][1] = 0
+ when "iso"
+ @components[0][1] = 1
+ when "joint-iso-itu-t", "joint-iso-ccitt"
+ @components[0][1] = 2
+ else
+ # The first component seems to be DefinedValue
+ n, = @components.shift
+ node = ValuereferenceNode.new(n)
+ base = Internal::UnresolvedValue.new(type, node)
+ end
+ end
+ end
+
+ arys = [ary = []]
+ @components.map do |name, value|
+ if value
+ # NameAndNumberForm/NumberForm
+ ary << Internal::UnresolvedValue.new(:number, value)
+ else
+ # DefinedValue (a kind of RelativeOID)
+ node = ValuereferenceNode.new(name)
+ arys << Internal::UnresolvedValue.new(ASN1Kit::RelativeOID, node)
+ arys << ary = []
+ end
+ end
+
+ if type <= ASN1Kit::ObjectIdentifier
+ type._new_internal(base, arys)
+ else
+ type._new_internal(arys)
+ end
+ end
+ end
+
+ class DefinedValueNode
+ attr_reader :module
+ attr_reader :name
+
+ def initialize(name, mod = nil)
+ @name = name
+ @module = mod if mod # External
+ end
+ end
+
+ # May be INTEGER or ENUMERATED item identifier
+ class ValuereferenceNode < DefinedValueNode
+ def compile
+ self
+ end
+
+ def shallow_compile_to(type, state)
+ Internal::UnresolvedValue.new(type, self)
+ end
+ end
+
+ class TypeNode
+ def subtype
+ @subtype ||= {}
+ end
+
+ def update(hash)
+ subtype.merge!(hash)
+ self
+ end
+ end
+
+ class ComponentTypeNode
+ attr_reader :name, :type, :default, :optional
+
+ def initialize(name, type, default: nil, optional: nil)
+ @name = name
+ @type = type
+ @default = default
+ @optional = optional
+ end
+ end
+
+ class ComponentTypeListsTypeNode < TypeNode
+ def initialize(component_type_lists)
+ @component_type_lists = component_type_lists
+ end
+
+ def shallow_compile_internal(klass, state)
+ flattened = @component_type_lists.flat_map do |obj|
+ case obj
+ when :extension
+ [:extension]
+ when Array
+ obj.map { |ctnode|
+ type = ctnode.type.shallow_compile(state)
+ if ctnode.default
+ default = Internal::UnresolvedValue.new(type, ctnode.default)
+ else
+ default = nil
+ end
+ ASN1Kit::Sequence::ComponentType.new(ctnode.name, type,
+ default: default,
+ optional: ctnode.optional)
+ }
+ else unreachable
+ end
+ end
+
+ klass[*flattened]
+ end
+ end
+
+ class SequenceTypeNode < ComponentTypeListsTypeNode
+ def shallow_compile(state)
+ shallow_compile_internal(ASN1Kit::Sequence, state)
+ end
+ end
+
+ class SetTypeNode < ComponentTypeListsTypeNode
+ def shallow_compile(state)
+ shallow_compile_internal(ASN1Kit::Set, state)
+ end
+ end
+
+ class SequenceOfTypeNode < TypeNode
+ def initialize(type, name: nil)
+ @type = type
+ # TODO: How NamedType alternative should be handled?
+ end
+
+ def shallow_compile(state)
+ ASN1Kit::SequenceOf[@type.shallow_compile(state)]
+ end
+ end
+
+ class SetOfTypeNode < TypeNode
+ def initialize(type, name: nil)
+ @type = type
+ end
+
+ def shallow_compile(state)
+ ASN1Kit::SetOf[@type.shallow_compile(state)]
+ end
+ end
+
+ class SimpleTypeNode < TypeNode
+ attr_reader :type, :arg1
+
+ def initialize(type, arg1 = nil)
+ @type = type
+ @arg1 = arg1
+ end
+
+ def [](name)
+ @named_numbers[name]
+ end
+
+ def shallow_compile(state)
+ case @type
+ when :BOOLEAN
+ ASN1Kit::Boolean
+ when :INTEGER
+ ASN1Kit::Integer[*@arg1]
+ when :REAL
+ ASN1Kit::Real
+ when :BIT_STRING
+ if @arg1
+ nbs = @arg1.map { |id, val|
+ val = Internal::UnresolvedValue.new(nil, val) unless val.is_a?(::Integer)
+ [id, val]
+ }
+ end
+ ASN1Kit::BitString[*nbs]
+ when :OCTET_STRING
+ ASN1Kit::OctetString
+ when :NULL
+ ASN1Kit::Null
+ when :OBJECT_IDENTIFIER
+ ASN1Kit::ObjectIdentifier
+ when :RELATIVE_OID
+ ASN1Kit::RelativeOID
+ when :ENUMERATED
+ # FIXME UGLY
+ root, additional = @arg1
+ [root, additional].compact.each do |ary|
+ ary.map! { |name, value|
+ if value
+ [name, Internal::UnresolvedValue.new(nil, value)]
+ else
+ [name, nil]
+ end
+ }
+ end
+ extensible = !!additional || state.extensibility_implied?
+ ASN1Kit::Enumerated._new_internal(root, additional||[], extensible: extensible)
+ when :CHOICE
+ ASN1Kit::Choice
+
+ # CharacterStringType
+ when :UTF8String
+ ASN1Kit::UTF8String
+ when :NumericString
+ ASN1Kit::NumericString
+ when :PrintableString
+ ASN1Kit::PrintableString
+ when :TeletexString, :T61String
+ ASN1Kit::TeletexString
+ when :VideotexString
+ ASN1Kit::VideotexString
+ when :IA5String
+ ASN1Kit::IA5String
+ when :GraphicString
+ ASN1Kit::GraphicString
+ when :VisibleString, :ISO646String
+ ASN1Kit::VisibleString
+ when :GeneralString
+ ASN1Kit::GeneralString
+ when :UniversalString
+ ASN1Kit::UniversalString
+ when :BMPString
+ ASN1Kit::BMPString
+ when :CHARACTER_STRING
+ ASN1Kit::CharacterString
+
+ # UsefulType
+ when :GeneralizedTime
+ ASN1Kit::GeneralizedTime
+ when :UTCTime
+ ASN1Kit::UTCTime
+ when :ObjectDescriptor
+ ASN1Kit::ObjectDescriptor
+ else
+ raise "unknown builtin type type: %p" % @type
+ end
+ end
+ end
+
+ class TaggedTypeNode < TypeNode
+ def initialize(type, tagging, class_name, class_number)
+ @type = type
+ @tagging = tagging
+ @class_name = class_name
+ @class_number = class_number
+ end
+
+ def shallow_compile(state)
+ tagging = @tagging || state.tag_default
+ base = @type.shallow_compile(state)
+ base.tagging(tagging, @class_name, @class_number)
+ end
+ end
+
+ NamedTypeNode = Struct.new(:name, :type)
+
+ class ReferencedTypeNode < TypeNode
+ def initialize(name)
+ @name = name
+ end
+ end
+
+ class DefinedTypeNode < ReferencedTypeNode
+ end
+
+ class TypereferenceNode < DefinedTypeNode
+ attr_reader :name
+
+ def initialize(name)
+ @name = name
+ end
+
+ def compile(state)
+ ret = state.resolved(@name)
+ return ret if ret
+ state.push()
+ self
+ end
+
+ def shallow_compile(state)
+ Internal::TypeReference.new(@name)
+ end
+ end
+
+ class Constraint
+ def initialize(name, arg1)
+ @name = name
+ @arg1 = arg1
+ end
+ end
+
+ class SizeConstraint < Constraint
+ def initialize(constraint)
+ @constraint = constraint
+ end
+ end
+
+ class ValueRange
+ def initialize(lower_endpoint, upper_endpoint)
+ @lower = lower_endpoint
+ @upper = upper_endpoint
+ end
+ end
+ end
+ end
+end