diff options
Diffstat (limited to 'lib/asn1kit/compile.rb')
-rw-r--r-- | lib/asn1kit/compile.rb | 707 |
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 |