Class: RMasm::Struct

Inherits:
DataType show all
Extended by:
Enumerable
Defined in:
lib/rmasm/struct.rb

Overview

Main struct class. A Struct is a RMasm::DataType supporting inheritance. A struct can have any primitive fields and struct/union fields

Defined Under Namespace

Classes: Field, Path

Constant Summary collapse

@@align =
Align.new

Class Method Summary collapse

Methods inherited from DataType

read, valid?, write

Class Method Details

.[](arg) ⇒ Object

Find a field by symbol name



348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
# File 'lib/rmasm/struct.rb', line 348

def [](arg)
  return rmasm_cannot_use_this_struct if @rmasm_errors > 0
  result = nil

  if arg.is_a?(Integer)
    all_fields = []
    self.each {|field| all_fields << field }
    if !(arg >= 0 && arg < all_fields.length)
      return Report.error(:R20K3, arg, self, "0..#{all_fields.length-1}")
    end
    result = all_fields[arg]
  else
    # super is not working here. use superclass instead
    result = superclass[arg] if superclass != Struct
    if (result.nil?)
      result = @map_fields[arg]
    end
  end

  result
end

.__fields__Object



370
371
372
373
374
# File 'lib/rmasm/struct.rb', line 370

def __fields__()
  all_fields = []
  self.each {|field| all_fields << field }
  all_fields
end

.align(*args) ⇒ Object

Get or Set alignment for this structure



313
314
315
316
317
318
319
320
321
322
323
324
325
# File 'lib/rmasm/struct.rb', line 313

def align(*args)
  if (args.empty?)
    if self == Struct
      @@align
    else
      @align
    end
  elsif args.length != 1
    return Report.error(:ARGS, args.length, "1", "struct.align")
  else
    self.align = args[0]
  end
end

.align=(arg) ⇒ Object

Set alignment for this structure



328
329
330
331
332
333
334
# File 'lib/rmasm/struct.rb', line 328

def align=(arg)
  if self == Struct
    @@align.value = arg
  else
    @align.value = arg
  end
end

.eachObject

Iterate on declared fields



337
338
339
340
341
342
343
344
345
# File 'lib/rmasm/struct.rb', line 337

def each
  return rmasm_cannot_use_this_struct if @rmasm_errors > 0
  # Add superclass fields first
  if superclass != Struct
    superclass.each {|field| yield field}
  end
  # the add self fields
  @fields.each { |field| yield field }
end

.inherited(subclass) ⇒ Object

Called when Struct is subclassed. Declare class variable on subclass



285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
# File 'lib/rmasm/struct.rb', line 285

def inherited(subclass)
  @offset ||= 0

  # Inherit alignment from parent class
  last_field = nil
  inherited_align = @@align.value
  if self != Struct
    inherited_align = @align.value
    last_field = @rmasm_last_field
  end

  subclass.instance_variable_set :@align, Align.new(inherited_align) # Set align as the same as the global struct align
  subclass.instance_variable_set :@rmasm_errors, 0 # @rmasm_errors > 0, number of errors while defining this struct
  subclass.instance_variable_set :@rmasm_declaring_struct, true # is the structure is being declared
  subclass.instance_variable_set :@rmasm_declaring_union, false # is a union being declared
  subclass.instance_variable_set :@rmasm_declaring_union_max_sizeof, 0 # holds the maximum size inside a union
  subclass.instance_variable_set :@rmasm_last_field, last_field # contains the last field in the structure (using inheritance)
  subclass.instance_variable_set :@fields, [] # sequential list of the fields
  subclass.instance_variable_set :@map_fields, {} # map of a fields
  subclass.instance_variable_set :@offset, @offset # offset of the next field == sizeof this struct
end

.rmasm_cannot_use_this_structObject



382
383
384
# File 'lib/rmasm/struct.rb', line 382

def rmasm_cannot_use_this_struct
  return Report.error(:R2081, self )
end

.rmasm_define_struct(owner, *args, &block) ⇒ Object

Define a new structure. Used internally when calling top level struct(…) method



410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
# File 'lib/rmasm/struct.rb', line 410

def rmasm_define_struct(owner, *args, &block)
  # Adjust owner to the closest module
  if ! owner.is_a?(Module)
    owner = owner.class
    if ! owner.is_a?(Module)
      Report::warning(:W2001, owner)
      owner = Object
    end
  end

  # Check arguments
  if (args.length != 1)
    return Report.error(:ARGS, args.length, "1", "struct :StructName do ... end")
  end

  parent_struct = Struct
  sym = args[0]

  # If first arg is a hash, then check the size
  if (sym.is_a?(Hash))
    if (sym.size != 1)
      return Report.error(:ARGS, args.length, "1", "struct :StructName => InheritedStruct do ... end")
    end
    sym, parent_struct = sym.shift

    # If the parent struct is a symbol, try to resolve it dynamically
    if parent_struct.is_a?(Symbol)
      inherited_name = parent_struct
      parent_struct = owner.dyn_const_get(inherited_name)
      if parent_struct.nil?
        return Report.error(:R20Q2, inherited_name, sym)
      end
    end
  end

  # Check the first arg is a symbol
  if !sym.is_a?(Symbol)
    return Report.error(:R2012, sym.class, sym)
  end

  # Check the symbol is a valid class name
  if ! (/^[A-Z][a-zA-Z_0-9]*$/.match(sym.to_s))
    return Report.error(:R2021, sym)
  end

  # Check that the parent class is a Struct
  if parent_struct.nil? || !(parent_struct.class.is_a?(Class) && parent_struct.ancestors.include?(Struct) )
    return Report.error(:R2031, parent_struct.class, sym)
  end

  # Check that the parent class is not being declared (nested struct declaration)
  if ( parent_struct.rmasm_declaring_struct )
    return Report.error(:R2041, parent_struct )
  end

  # Check that the parent class does not have any errors
  if ( parent_struct != Struct && parent_struct.rmasm_errors > 0)
    return Report.error(:R20C2, sym, parent_struct )
  end

  # Check that the block is not nil
  if ( block.nil?)
    return Report.error(:BLOCK_BODY, "struct :#{sym}")
  end

  # Remove the structure from the central repository
  map = StructManager.instance.get_owner_holder(owner)

  # Check that we are not declaring a struct with the same name
  if map.has_key?(sym) || owner.const_defined?(sym)
    return Report.error(:R20P1, sym)
  end

  # Create new class structure from parent_struct
  new_struct = Class.new(parent_struct)
  # Get binding from inside struct in order to resolve correctly constants from method missing
  new_struct.rmasm_binding = owner.class_eval("binding")

  # Set symbol on owner with new_struct class
  owner.const_set sym, new_struct

  # Evaluate safely the block
  begin
    new_struct.rmasm_declaring_struct = true

    new_struct.instance_eval &block
  rescue Exception => exp
    new_struct.rmasm_declaring_struct = nil
    if new_struct.rmasm_errors > 0
      new_struct.rmasm_cannot_use_this_struct
    end
    raise exp
  end
  new_struct.rmasm_declaring_struct = nil

  # Add the current structure to the structure map
  map[sym] = new_struct

  if new_struct.rmasm_errors > 0
    return new_struct.rmasm_cannot_use_this_struct
  end
  return new_struct
end

.rmasm_define_struct_by_block_binding(symbol, *args, &block) ⇒ Object



386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
# File 'lib/rmasm/struct.rb', line 386

def rmasm_define_struct_by_block_binding(symbol, *args, &block)
  # Check that the block is not nil
  if ( block.nil?)
    return Report.error(:BLOCK_BODY, "struct :#{sym}")
  end

  # Check arguments
  if (args.length > 1)
    return Report.error(:ARGS, args.length, "0..1", ":symbol.struct(InheritedStruct) do ... end")
  end

  if !args.empty?
    params = {symbol => args[0]}
  else
    params = symbol
  end

  # Get the owner from the block
  owner = eval("self", block.binding)

  rmasm_define_struct(owner, params, &block)
end

.sizeofObject

Returns the sizeof this structure



377
378
379
380
# File 'lib/rmasm/struct.rb', line 377

def sizeof()
  return rmasm_cannot_use_this_struct if @rmasm_errors > 0
  @offset
end