Class: Furnace::AVM2::Binary::Record

Inherits:
Object
  • Object
show all
Defined in:
lib/furnace-avm2/binary/record.rb

Direct Known Subclasses

ABC::Record

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Class Attribute Details

.codebaseObject (readonly)

Returns the value of attribute codebase.



10
11
12
# File 'lib/furnace-avm2/binary/record.rb', line 10

def codebase
  @codebase
end

.formatObject (readonly)

Returns the value of attribute format.



10
11
12
# File 'lib/furnace-avm2/binary/record.rb', line 10

def format
  @format
end

.tracingObject

Returns the value of attribute tracing.



123
124
125
# File 'lib/furnace-avm2/binary/record.rb', line 123

def tracing
  @tracing
end

Instance Attribute Details

#rootObject (readonly)

Returns the value of attribute root.



151
152
153
# File 'lib/furnace-avm2/binary/record.rb', line 151

def root
  @root
end

Class Method Details

.codegenObject



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/furnace-avm2/binary/record.rb', line 44

def codegen
  tracing = $avm2_binary_trace

  gen_if = lambda do |code, options, &block|
    if options.include? :if
      if options[:if].to_proc.arity == 0
        code << "if instance_exec(&options[:if])"
      else
        code << "if instance_exec(self, &options[:if])"
      end
    end

    block.call(code)

    if options.include? :if
      code << "end"
    end
  end

  gen_read = lambda do |index|
    method, name, options = @format[index]
    code = []

    code << "options = self.class.format[#{index}][2]"

    gen_if.(code, options) do
      code << "self.class.trace_scope(#{name}) do" if tracing
      code << "  value = read_#{method}(io, options)"
      code << "  self.class.trace_value(value)" if tracing
      code << "  @#{name} = value"
      code << "end" if tracing
    end

    code.join "\n"
  end

  gen_write = lambda do |index|
    method, name, options = @format[index]
    code = []

    code << "options = self.class.format[#{index}][2]"

    gen_if.(code, options) do
      code << "self.class.trace_scope(#{name}) do" if tracing
      if options.include? :value
        code << "value = fetch(options[:value])"
      else
        code << "value = @#{name}"
      end
      code << "  self.class.trace_value(value)" if tracing
      code << "  value = write_#{method}(io, value, options)"
      code << "end" if tracing
    end

    code.join "\n"
  end

  class_eval <<-CODE, "(generated-io:#{self})"
  def initialize(options={})
    @root = options[:parent].root if options[:parent]
    #{@format.map { |f| "@#{f[1]} = nil\n" }.join}

    #{"initialize_record(options)" if instance_methods.include? :initialize_record}
  end

  def read(io)
    #{"before_read(io)" if instance_methods.include? :before_read}
    #{@format.each_index.map { |index| gen_read.(index) }.join "\n"}
    #{"after_read(io)" if instance_methods.include? :after_read}
  end

  def write(io)
    #{"before_write(io)" if instance_methods.include? :before_write}
    #{@format.each_index.map { |index| gen_write.(index) }.join "\n"}
    #{"after_write(io)" if instance_methods.include? :after_write}
    end
  CODE
end

.codegen_eachObject



38
39
40
41
42
# File 'lib/furnace-avm2/binary/record.rb', line 38

def codegen_each
  Record.codebase.each do |klass|
    klass.codegen
  end
end

.inherited(klass) ⇒ Object



12
13
14
15
16
17
18
# File 'lib/furnace-avm2/binary/record.rb', line 12

def inherited(klass)
  Record.register klass

  klass.class_exec do
    @format = []
  end
end

.method_missing(method, name, options = {}, &block) ⇒ Object



20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/furnace-avm2/binary/record.rb', line 20

def method_missing(method, name, options={}, &block)
  if instance_methods.include? :"read_#{method}"
    options.merge!(:block => block) if block

    check(method, options)

    attr_accessor name
    @format << [method, name, options.freeze]
  else
    super
  end
end

.register(klass) ⇒ Object



33
34
35
36
# File 'lib/furnace-avm2/binary/record.rb', line 33

def register(klass)
  @codebase ||= []
  @codebase << klass
end

.traceObject



125
126
127
128
129
130
131
# File 'lib/furnace-avm2/binary/record.rb', line 125

def trace
  AVM2::Binary::Record.tracing = true

  yield
ensure
  AVM2::Binary::Record.tracing = false
end

.trace_scope(scope) ⇒ Object



133
134
135
136
137
138
139
140
141
142
# File 'lib/furnace-avm2/binary/record.rb', line 133

def trace_scope(scope)
  Thread.current[:binary_trace_scope] ||= []
  Thread.current[:binary_trace_scope].push scope
  Thread.current[:binary_trace_nested] = false

  yield
ensure
  Thread.current[:binary_trace_scope].pop
  Thread.current[:binary_trace_nested] = true
end

.trace_value(value) ⇒ Object



144
145
146
147
148
# File 'lib/furnace-avm2/binary/record.rb', line 144

def trace_value(value)
  return if value.is_a?(Array) && value[0].is_a?(Record)

  puts "#{Thread.current[:binary_trace_scope].join(".")} = #{value}"
end

Instance Method Details

#byte_lengthObject



167
168
169
170
171
# File 'lib/furnace-avm2/binary/record.rb', line 167

def byte_length
  self.class.format.map do |method, name, options|
    send(:"length_#{method}", instance_variable_get(:"@#{name}"), options)
  end.reduce(0, :+)
end

#inspectObject



163
164
165
# File 'lib/furnace-avm2/binary/record.rb', line 163

def inspect
  "AVM2Record(#{self.class.name.split("::").last})"
end

#to_hashObject



153
154
155
156
157
158
159
160
161
# File 'lib/furnace-avm2/binary/record.rb', line 153

def to_hash
  hash = {}

  self.class.format.each do |method, name, options|
    hash[name] = instance_variable_get :"@#{name}"
  end

  hash
end