Class: NodeMarshal

Inherits:
Object
  • Object
show all
Defined in:
lib/node-marshal.rb,
ext/node-marshal/nodedump.c

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#new(: srcfile, filename) ⇒ Object #new(: binfile, filename) ⇒ Object #new(: srcmemory, srcstr) ⇒ Object #new(: binmemory, binstr) ⇒ Object

Creates NodeMarshal class example from the source code or dumped syntax tree (NODEs), i.e. preparsed and packed source code. Created object can be used either for code execution or for saving it in the preparsed form (useful for code obfuscation/protection)



1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
# File 'ext/node-marshal/nodedump.c', line 1772

static VALUE m_nodedump_init(VALUE self, VALUE source, VALUE info)
{
	ID id_usr;
	rb_iv_set(self, "@show_offsets", Qfalse);
	Check_Type(source, T_SYMBOL);
	id_usr = SYM2ID(source);
	if (id_usr == rb_intern("srcfile"))
	{
		return m_nodedump_from_source(self, info);
	}
	else if (id_usr == rb_intern("srcmemory"))
	{
		return m_nodedump_from_string(self, info);
	}
	else if (id_usr == rb_intern("binmemory"))
	{
		return m_nodedump_from_memory(self, info);
	}
	else if (id_usr == rb_intern("binfile"))
	{
		VALUE cFile = rb_const_get(rb_cObject, rb_intern("File"));
		VALUE bin = rb_funcall(cFile, rb_intern("binread"), 1, info);
		return m_nodedump_from_memory(self, bin);
	}
	else
	{
		rb_raise(rb_eArgError, "Invalid source type (it must be :srcfile, :srcmemory, :binmemory of :binfile)");
	}
	return Qnil;
}

Class Method Details

.base85r_decode(input) ⇒ Object

Decode ASCII string in the modified BASE85 format to the binary string (useful for obfuscation of .rb source files)



2279
2280
2281
2282
# File 'ext/node-marshal/nodedump.c', line 2279

static VALUE m_base85r_decode(VALUE obj, VALUE input)
{
	return base85r_decode(input);
}

.base85r_encode(input) ⇒ Object

Encode arbitrary binary string to the ASCII string using modified version of BASE85 (useful for obfuscation of .rb source files)



2266
2267
2268
2269
# File 'ext/node-marshal/nodedump.c', line 2266

static VALUE m_base85r_encode(VALUE obj, VALUE input)
{
	return base85r_encode(input);
}

.compile_rb_file(outfile, inpfile, *args) ⇒ Object

call-seq:

NodeMarshal::compile_rb_file(outfile, inpfile, opts)

Reads .rb file (Ruby source) and compiles it to .rb file containing compressed AST node and its loader. This functions is an envelope for NodeMarshal#to_compiled_rb



91
92
93
94
95
# File 'lib/node-marshal.rb', line 91

def self.compile_rb_file(outfile, inpfile, *args)
	node = NodeMarshal.new(:srcfile, inpfile)
	node.to_compiled_rb(outfile, *args)
	return true
end

Instance Method Details

#change_literal(old_lit, new_lit) ⇒ Object

Update the array with the list of literals (to be used for code obfuscation) Warning! This function is a stub!



1663
1664
1665
1666
1667
# File 'ext/node-marshal/nodedump.c', line 1663

static VALUE m_nodedump_change_literal(VALUE self, VALUE old_lit, VALUE new_lit)
{
    /* TO BE IMPLEMENTED */
    return self;
}

#change_symbol(old_sym, new_sym) ⇒ Object

Replace one symbol by another (to be used for code obfuscation)

  • old_sym – String that contains symbol name to be replaced

  • new_sym – String that contains new name of the symbol



1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
# File 'ext/node-marshal/nodedump.c', line 1575

static VALUE m_nodedump_change_symbol(VALUE self, VALUE old_sym, VALUE new_sym)
{
	VALUE val_nodehash = rb_iv_get(self, "@nodehash");
	VALUE syms, key;
	// Check if node is position-independent
	// (i.e. with initialized NODEInfo structure that contains
	// relocations for symbols)
	if (val_nodehash == Qnil)
		rb_raise(rb_eArgError, "This node is not preparsed into Hash");
	// Check data types of the input array
	if (TYPE(old_sym) != T_STRING)
	{
		rb_raise(rb_eArgError, "old_sym argument must be a string");
	}
	if (TYPE(new_sym) != T_STRING)
	{
		rb_raise(rb_eArgError, "new_sym argument must be a string");
	}
	// Get the symbol table from the Hash
	syms = rb_hash_aref(val_nodehash, ID2SYM(rb_intern("symbols")));
	if (syms == Qnil)
		rb_raise(rb_eArgError, "Preparsed hash has no :symbols field");
	// Check if new_sym is present in the symbol table
	key = rb_funcall(syms, rb_intern("find_index"), 1, new_sym);
	if (key != Qnil)
	{
		rb_raise(rb_eArgError, "new_sym value must be absent in table of symbols");
	}
	// Change the symbol in the preparsed Hash
	key = rb_funcall(syms, rb_intern("find_index"), 1, old_sym);
	if (key == Qnil)
		return Qnil;
	RARRAY_PTR(syms)[FIX2INT(key)] = new_sym;
	return self;
}

#compileObject

Creates the RubyVM::InstructionSequence object from the node



1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
# File 'ext/node-marshal/nodedump.c', line 1676

static VALUE m_nodedump_compile(VALUE self)
{
	NODE *node = RNODE(rb_iv_get(self, "@node"));
	VALUE nodename = rb_iv_get(self, "@nodename");
	VALUE filename = rb_iv_get(self, "@filename");
	VALUE filepath = rb_iv_get(self, "@filepath");
#ifndef WITH_RB_ISEQW_NEW
	/* For Pre-2.3 */
	return rb_iseq_new_top(node, nodename, filename, filepath, Qfalse);
#else
	/* For Ruby 2.3 */
	return rb_iseqw_new(rb_iseq_new_top(node, nodename, filename, filepath, Qfalse));
#endif
}

#dump_treeObject

Transforms Ruby syntax tree (NODE) to the String using rb_parser_dump_tree function from node.c (see Ruby source code).



1810
1811
1812
1813
1814
# File 'ext/node-marshal/nodedump.c', line 1810

static VALUE m_nodedump_parser_dump_tree(VALUE self)
{
	NODE *node = RNODE(rb_iv_get(self, "@node"));
	return rb_parser_dump_tree(node, 0);
}

#dump_tree_shortObject

Transforms Ruby syntax tree (NODE) to the String using custom function instead of rb_parser_dump_tree function.

See also #show_offsets, #show_offsets=



1825
1826
1827
1828
1829
1830
1831
1832
# File 'ext/node-marshal/nodedump.c', line 1825

static VALUE m_nodedump_dump_tree_short(VALUE self)
{
	VALUE str = rb_str_new2(""); // Output string
	NODE *node = RNODE(rb_iv_get(self, "@node"));
	int show_offsets = (rb_iv_get(self, "@show_offsets") == Qtrue) ? 1 : 0;
	print_node(str, node, 0, show_offsets);
	return str;
}

#filenameObject

Returns name of file that was used for node generation and will be used by YARV (or nil/<compiled> if a string of code was used)



2205
2206
2207
2208
# File 'ext/node-marshal/nodedump.c', line 2205

static VALUE m_nodedump_filename(VALUE self)
{
	return rb_funcall(rb_iv_get(self, "@filename"), rb_intern("dup"), 0);
}

#filename=(val) ⇒ Object

Sets name of file that was used for node generation and will be used by YARV (or nil/<compiled> if a string of code was used)



2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
# File 'ext/node-marshal/nodedump.c', line 2214

static VALUE m_nodedump_set_filename(VALUE self, VALUE val)
{
	if (val != Qnil)
	{
		Check_Type(val, T_STRING);
		rb_iv_set(self, "@filename", rb_funcall(val, rb_intern("dup"), 0));	
	}
	else
	{
		rb_iv_set(self, "@filename", Qnil);
	}
	return self;
}

#filepathObject

Returns path of file that was used for node generation and will be used by YARV (or nil/<compiled> if a string of code was used)



2232
2233
2234
2235
# File 'ext/node-marshal/nodedump.c', line 2232

static VALUE m_nodedump_filepath(VALUE self)
{
	return rb_funcall(rb_iv_get(self, "@filepath"), rb_intern("dup"), 0);
}

#filepath=Object

Sets the path of file that was used for node generation and will be used by YARV (or nil/<compiled> if a string of code was used)



2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
# File 'ext/node-marshal/nodedump.c', line 2244

static VALUE m_nodedump_set_filepath(VALUE self, VALUE val)
{
	if (val != Qnil)
	{
		Check_Type(val, T_STRING);
		rb_iv_set(self, "@filepath", rb_funcall(val, rb_intern("dup"), 0));
	}
	else
	{
		rb_iv_set(self, "@filepath", Qnil);
	}
	return self;
}

#get_aliases_table(our_symbols) ⇒ Object

call-seq:

obj.get_aliases_table(our_symbols)

Returns a hash that has “old_sym_name”=>“new_sym_name”,… format. “new_sym_name” are generated automatically.

  • our_symbols – An array that contains the list of symbols (AS STRINGS,

NOT AS SYMBOLS) that can be renamed.



155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/node-marshal.rb', line 155

def get_aliases_table(our_symbols)
	symbols_ary = get_safe_symbols(our_symbols)
	pos = 0;
	aliases_ary = symbols_ary.map do |sym|
		pos += 1
		if sym.length > 1 && sym[0..1] == '@@'
			"@@q#{pos}"
		elsif sym[0] == '@'
			"@q#{pos}"
		elsif sym[0] =~ /[A-Z]/
			"Q#{pos}"
		elsif sym[0] =~ /[a-z]/
			"q#{pos}"
		end
	end
	[symbols_ary, aliases_ary].transpose.to_h
end

#get_safe_symbols(our_symbols) ⇒ Object

call-seq:

obj.get_safe_symbols(our_symbols)

Returns an array that contains strings with the names of symbols that are safe to change. It excludes symbols that are present in the table of literals (and their derivatives such as @x and x=). Such operation is useful for attr_readed, attr_writer and another similar metaprogramming techniques handling

  • our_symbols symbols created during node creation (must be found manually by the user by means of Symbol.all_symbols calling BEFORE and AFTER node creation.



137
138
139
140
141
142
143
144
145
# File 'lib/node-marshal.rb', line 137

def get_safe_symbols(our_symbols)
	self.to_hash # To initialize Hash with preparsed Ruby AST NODE
	symbolic_literals =  self.literals.select {|x| x.is_a?(Symbol)}.map {|x| x.to_s}
	fixed_symbols = [] + symbolic_literals
	fixed_symbols += symbolic_literals.map {|x| "@#{x}"}
	fixed_symbols += symbolic_literals.map {|x| "#{x}="}
	our_symbols = our_symbols.dup
	our_symbols -= fixed_symbols
end

#inspectObject

Gives the information about the node



2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
# File 'ext/node-marshal/nodedump.c', line 2097

static VALUE m_nodedump_inspect(VALUE self)
{
	static char str[1024], buf[512];
	VALUE num_of_nodes, nodename, filepath, filename;
	VALUE val_obj_addresses, val_nodeinfo;
	// Get generic information about node
	num_of_nodes = rb_iv_get(self, "@num_of_nodes");
	nodename = rb_iv_get(self, "@nodename");
	filepath = rb_iv_get(self, "@filepath");
	filename = rb_iv_get(self, "@filename");
	// Generate string with generic information about node
	sprintf(str,
		"----- NodeMarshal:0x%"PRIxPTR"\n"
		"    num_of_nodes: %d\n    nodename: %s\n    filepath: %s\n    filename: %s\n",
		(uintptr_t) (self),
		(num_of_nodes == Qnil) ? -1 : FIX2INT(num_of_nodes),
		(nodename == Qnil) ? "nil" : RSTRING_PTR(nodename),
		(filepath == Qnil) ? "nil" : RSTRING_PTR(filepath),
		(filename == Qnil) ? "nil" : RSTRING_PTR(filename)
		);
	// Check if the information about node struct is available
	val_nodeinfo = rb_iv_get(self, "@nodeinfo");
	val_obj_addresses = rb_iv_get(self, "@obj_addresses");
	if (val_nodeinfo == Qnil && val_obj_addresses == Qnil)
	{
		m_nodedump_to_hash(self);
		val_nodeinfo = rb_iv_get(self, "@nodeinfo");
	}
	// Information about preparsed node
	// a) NODEInfo struct
	if (val_nodeinfo == Qnil)
	{
		sprintf(buf, "    NODEInfo struct is empty\n");
	}
	else
	{
		NODEInfo *ninfo;
		Data_Get_Struct(val_nodeinfo, NODEInfo, ninfo);
		sprintf(buf, 
			"    NODEInfo struct:\n"
			"      syms hash len (Symbols):         %d\n"
			"      lits hash len (Literals):        %d\n"
			"      idtabs hash len (ID tables):     %d\n"
			"      gentries hash len (Global vars): %d\n"
			"      nodes hash len (Nodes):          %d\n"
			"      pnodes hash len (Parent nodes):  %d\n"			
#ifdef USE_RB_ARGS_INFO
			"      args hash len (args info):       %d\n"
#endif
			,
			FIX2INT(rb_funcall(ninfo->syms.vals, rb_intern("length"), 0)),
			FIX2INT(rb_funcall(ninfo->lits.vals, rb_intern("length"), 0)),
			FIX2INT(rb_funcall(ninfo->idtabs.vals, rb_intern("length"), 0)),
			FIX2INT(rb_funcall(ninfo->gentries.vals, rb_intern("length"), 0)),
			FIX2INT(rb_funcall(ninfo->nodes.vals, rb_intern("length"), 0)),
			FIX2INT(rb_funcall(ninfo->pnodes.vals, rb_intern("length"), 0))			
#ifdef USE_RB_ARGS_INFO
			,
			FIX2INT(rb_funcall(ninfo->args.vals, rb_intern("length"), 0))
#endif
		);
	}
	strcat(str, buf);
	// b) NODEObjAddresses struct
	if (val_obj_addresses == Qnil)
	{
		sprintf(buf, "    NODEObjAddresses struct is empty\n");
	}
	else
	{
		NODEObjAddresses *objadr;
		Data_Get_Struct(val_obj_addresses, NODEObjAddresses, objadr);
		sprintf(buf, 
			"    NODEObjAddresses struct:\n"
			"      syms_len (Num of symbols):      %d\n"
			"      lits_len (Num of literals):     %d\n"
			"      idtbls_len (Num of ID tables):  %d\n"
			"      gvars_len (Num of global vars): %d\n"
			"      nodes_len (Num of nodes):       %d\n"
#ifdef USE_RB_ARGS_INFO
			"      args_len: (Num of args info):   %d\n"
#endif
			, objadr->syms_len, objadr->lits_len,
			objadr->idtbls_len, objadr->gvars_len,
			objadr->nodes_len
#ifdef USE_RB_ARGS_INFO
			, objadr->args_len
#endif
		);
	}
	strcat(str, buf);
	strcat(str, "------------------\n");
	// Generate output string
	return rb_str_new2(str);
}

#literalsObject

Return array with the list of literals



1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
# File 'ext/node-marshal/nodedump.c', line 1614

static VALUE m_nodedump_literals(VALUE self)
{
	int i;
	VALUE val_relocs, val_nodeinfo, lits;
	// Variant 1: node loaded from file. It uses NODEObjAddresses struct
	// with the results of Ruby NODE structure parsing.
	val_relocs = rb_iv_get(self, "@obj_addresses");
	if (val_relocs != Qnil)
	{
		NODEObjAddresses *relocs;

		Data_Get_Struct(val_relocs, NODEObjAddresses, relocs);
		lits = rb_ary_new();
		for (i = 0; i < relocs->lits_len; i++)
		{
			VALUE val = relocs->lits_adr[i];
			int t = TYPE(val);
			if (t != T_SYMBOL && t != T_FLOAT && t != T_FIXNUM)
				val = rb_funcall(val, rb_intern("dup"), 0);
			rb_ary_push(lits, val);
		}
		return lits;
	}
	// Variant 2: node saved to file (parsed from memory). It uses
	// NODEInfo struct that is initialized during node dump parsing.
	val_nodeinfo = rb_iv_get(self, "@nodeinfo");
	if (val_nodeinfo != Qnil)
	{
		NODEInfo *ninfo;
		VALUE *ary;
		Data_Get_Struct(val_nodeinfo, NODEInfo, ninfo);
		lits = rb_funcall(ninfo->lits.vals, rb_intern("values"), 0);
		ary = RARRAY_PTR(lits);
		for (i = 0; i < RARRAY_LEN(lits); i++)
		{
			int t = TYPE(ary[i]);
			if (t != T_SYMBOL && t != T_FLOAT && t != T_FIXNUM)
				ary[i] = rb_funcall(ary[i], rb_intern("dup"), 0);
		}
		return lits;
	}
	rb_raise(rb_eArgError, "Literals information not initialized. Run to_hash before reading.");
}

#nodeObject

Returns node object



2301
2302
2303
2304
# File 'ext/node-marshal/nodedump.c', line 2301

static VALUE m_nodedump_node(VALUE self)
{
	return rb_iv_get(self, "@node");
}

#nodenameObject

Returns node name (usually <main>)



2196
2197
2198
2199
# File 'ext/node-marshal/nodedump.c', line 2196

static VALUE m_nodedump_nodename(VALUE self)
{
	return rb_funcall(rb_iv_get(self, "@nodename"), rb_intern("dup"), 0);
}

#rebuildObject

call-seq:

obj.rebuild

Rebuilds the node by converting it to the binary dump and further restoring of it from this dump. It doesn’t change the original node and returns rebuilt node.



205
206
207
# File 'lib/node-marshal.rb', line 205

def rebuild
	NodeMarshal.new(:binmemory, to_bin)
end

#rename_ivars(*args) ⇒ Object

call-seq:

obj.rename_ivars


175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'lib/node-marshal.rb', line 175

def rename_ivars(*args)
	if args.size == 0
		excl_names = []
	else
		excl_names = args[0]
	end

	to_hash
	syms = @nodehash[:symbols].select {|x| (x =~ /@[^@]/) == 0}
	pos = 1;
	syms_new = syms.map do |x|
		if excl_names.find_index(x[1..-1]) != nil
			str = x
		else
			str = "@ivar#{pos}"
		end
		pos = pos + 1;
		str
	end
	syms_subs =  [syms, syms_new].transpose.to_h
	replace_symbols(syms_subs)
	self
end

#replace_symbols(syms_subs) ⇒ Object

call-seq:

obj.replace_symbols(syms_subs)

Replaces some symbols inside parsed AST to user-defined aliases. It is designed to make code obfuscation easier. Be careful when using this ability: it is possible to break external libraries calls, operators overloading and some metaprogramming techniques.

  • syms_subs – Hash with the table of aliases. Keys are original names,

values are aliases. Keys and values MUST BE strings (not symbols!).



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/node-marshal.rb', line 106

def replace_symbols(syms_subs)
	# Check input data
	# a) type
	if !(syms_subs.is_a?(Hash))
		raise "symb_subs must be a hash"
	end
	# b) uniqueness of values inside the hash
	values = syms_subs.values
	if values.size != values.uniq.size
		raise ArgumentError, "values (new names) must be unique"
	end
	# c) uniqueness of values after replacement
	# TODO: MAKE IT!!!
	# Use NodeMarshal C part to replace the symbols
	self.to_hash # To initialize Hash with preparsed Ruby AST NODE
	syms_subs.each do |key, value|
		change_symbol(key, value)
	end
	self
end

#show_offsetsObject

Returns show_offsets property (used by NodeMarshal#dump_tree_short) It can be either true or false



1841
1842
1843
1844
# File 'ext/node-marshal/nodedump.c', line 1841

static VALUE m_nodedump_show_offsets(VALUE self)
{
	return rb_iv_get(self, "@show_offsets");
}

#show_offsets=Object

Sets show_offsets property (used by NodeMarshal#dump_tree_short) It can be either true or false



1853
1854
1855
1856
1857
1858
1859
1860
# File 'ext/node-marshal/nodedump.c', line 1853

static VALUE m_nodedump_set_show_offsets(VALUE self, VALUE value)
{
	if (value != Qtrue && value != Qfalse)
	{
		rb_raise(rb_eArgError, "show_offsets property must be either true or false");
	}
	return rb_iv_set(self, "@show_offsets", value);
}

#symbolsObject

Return array with the list of symbols



1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
# File 'ext/node-marshal/nodedump.c', line 1534

static VALUE m_nodedump_symbols(VALUE self)
{
	int i;
	VALUE val_relocs, val_nodeinfo, syms;
	// Variant 1: node loaded from file
	val_relocs = rb_iv_get(self, "@obj_addresses");
	if (val_relocs != Qnil)
	{
		NODEObjAddresses *relocs;
		Data_Get_Struct(val_relocs, NODEObjAddresses, relocs);
		syms = rb_ary_new();
		for (i = 0; i < relocs->syms_len; i++)
			rb_ary_push(syms, ID2SYM(relocs->syms_adr[i]));
		return syms;
	}
	// Variant 2: node saved to file (parsed from memory)
	val_nodeinfo = rb_iv_get(self, "@nodeinfo");
	if (val_nodeinfo != Qnil)
	{
		NODEInfo *ninfo;
		VALUE *ary;
		Data_Get_Struct(val_nodeinfo, NODEInfo, ninfo);
		syms = rb_funcall(ninfo->syms.vals, rb_intern("values"), 0);
		ary = RARRAY_PTR(syms);
		for (i = 0; i < RARRAY_LEN(syms); i++)
		{
			ary[i] = rb_funcall(ary[i], rb_intern("to_sym"), 0);
		}
		return syms;
	}
	rb_raise(rb_eArgError, "Symbol information not initialized. Run to_hash before reading.");
}

#to_aObject

Converts node to the array (mainly to allow exploration of AST by the user). It shows information about rb_args_info and ID *tbl that are not displayed by NodeMarshal#dump_tree and NodeMarshal#dump_tree_short.



2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
# File 'ext/node-marshal/nodedump.c', line 2065

static VALUE m_nodedump_to_a(VALUE self)
{
	NODE *node = RNODE(rb_iv_get(self, "@node"));
	VALUE gc_was_disabled = rb_gc_disable();
	VALUE ary = m_node_to_ary(node);
	if (gc_was_disabled == Qfalse)
	{
		rb_gc_enable();
	}
	return ary;
}

#to_aObject

Converts node to the array (mainly to allow exploration of AST by the user). It shows information about rb_args_info and ID *tbl that are not displayed by NodeMarshal#dump_tree and NodeMarshal#dump_tree_short.



2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
# File 'ext/node-marshal/nodedump.c', line 2065

static VALUE m_nodedump_to_a(VALUE self)
{
	NODE *node = RNODE(rb_iv_get(self, "@node"));
	VALUE gc_was_disabled = rb_gc_disable();
	VALUE ary = m_node_to_ary(node);
	if (gc_was_disabled == Qfalse)
	{
		rb_gc_enable();
	}
	return ary;
}

#to_binObject

Converts NodeMarshal class example to the binary string that can be saved to the file and used for loading the node from the file. Format of the obtained binary dump depends on used platform (especially size of the pointer) and Ruby version.



2087
2088
2089
2090
2091
2092
# File 'ext/node-marshal/nodedump.c', line 2087

static VALUE m_nodedump_to_bin(VALUE self)
{
	VALUE hash = m_nodedump_to_hash(self);
	VALUE cMarshal = rb_const_get(rb_cObject, rb_intern("Marshal"));
	return rb_funcall(cMarshal, rb_intern("dump"), 1, hash);
}

#to_compiled_rb(outfile, *args) ⇒ Object

See also NodeMarshal::compile_rb_file



37
38
39
40
41
42
43
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
# File 'lib/node-marshal.rb', line 37

def to_compiled_rb(outfile, *args)
	compress = true
	so_path = "require_relative '../ext/node-marshal/nodemarshal.so'"
	if args.length > 0
		opts = args[0]
		if opts.has_key?(:compress)
			compress = opts[:compress]
		end
		if opts.has_key?(:so_path)
			so_path = opts[:so_path]
		end
	end
	# Compression
	if compress
		if !defined?(Zlib)
			raise "Compression is not supported: Zlib is absent"
		end
		zlib_include = "require 'zlib'"
		data_txt = NodeMarshal.base85r_encode(Zlib::deflate(self.to_bin))
		data_bin = "Zlib::inflate(NodeMarshal.base85r_decode(data_txt))"
	else
		zlib_include = "# No compression"
		data_txt = self.to_text
		data_bin = "NodeMarshal.base85r_decode(data_txt)"
	end
	# Document header
	txt = <<EOS
# Ruby compressed source code
# RUBY_PLATFORM: #{RUBY_PLATFORM}
# RUBY_VERSION: #{RUBY_VERSION}
#{zlib_include}
#{so_path}
data_txt = <<DATABLOCK
#{data_txt}
DATABLOCK
data_bin = #{data_bin}
node = NodeMarshal.new(:binmemory, data_bin)
node.filename = __FILE__
node.filepath = File.expand_path(node.filename)
node.compile.eval
EOS
	# Process input arguments
	if outfile != nil
		File.open(outfile, 'w') {|fp| fp << txt}
	end
	return txt
end

#to_hashObject

Converts NodeMarshal class example to the hash that contains full and independent from data structures memory addresses information. Format of the obtained hash depends on used platform (especially size of the pointer) and Ruby version.

Format of the hash

Part 1: Signatures

  • MAGIC – NODEMARSHAL11

  • RUBY_PLATFORM – saved RUBY_PLATFORM constant value

  • RUBY_VERSION – saved RUBY_VERSION constant value

Part 2: Program loadable elements.

All loadable elements are arrays. Index of the array element means its identifier that is used in the node tree.

  • literals – program literals (strings, ranges etc.)

  • symbols – program symbols (values have either String or Fixnum data type; numbers are used for symbols that cannot be represented as strings)

  • global_entries – global variables information

  • id_tables – array of arrays. Each array contains symbols IDs

  • args – information about code block argument(s)

Part 3: Nodes information

  • nodes – string that contains binary encoded information about the nodes

  • num_of_nodes – number of nodes in the nodes field

  • nodename – name of the node (usually “<main>”)

  • filename – name (without path) of .rb file used for the node generation

  • filepath – name (with full path) of .rb file used for the node generation



1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
# File 'ext/node-marshal/nodedump.c', line 1900

static VALUE m_nodedump_to_hash(VALUE self)
{
	NODE *node = RNODE(rb_iv_get(self, "@node"));
	NODEInfo *info;
	VALUE ans, num, val_info, gc_was_disabled;
	// DISABLE GARBAGE COLLECTOR (important for dumping)
	gc_was_disabled = rb_gc_disable();
	// Convert the node to the form with relocs (i.e. the information about node)
	// if such form is not present
	val_info = rb_iv_get(self, "@nodeinfo");
	if (val_info == Qnil)
	{
		val_info = Data_Make_Struct(cNodeInfo, NODEInfo,
			NODEInfo_mark, NODEInfo_free, info); // This data envelope cannot exist without NODE
		NODEInfo_init(info);
		rb_iv_set(self, "@nodeinfo", val_info);
		num = INT2FIX(count_num_of_nodes(node, node, info));
		rb_iv_set(self, "@nodeinfo_num_of_nodes", num);
		// Convert node to NODEInfo structure
		ans = NODEInfo_toHash(info);
		rb_hash_aset(ans, ID2SYM(rb_intern("num_of_nodes")), num);
		rb_hash_aset(ans, ID2SYM(rb_intern("nodename")), rb_iv_get(self, "@nodename"));
		rb_hash_aset(ans, ID2SYM(rb_intern("filename")), rb_iv_get(self, "@filename"));
		rb_hash_aset(ans, ID2SYM(rb_intern("filepath")), rb_iv_get(self, "@filepath"));
		rb_iv_set(self, "@nodehash", ans);
	}
	else
	{
		ans = rb_iv_get(self, "@nodehash");
	}
	// ENABLE GARBAGE COLLECTOR (important for dumping)
	if (gc_was_disabled == Qfalse)
	{
		rb_gc_enable();
	}
	return ans;
}

#to_hashObject

Converts NodeMarshal class example to the hash that contains full and independent from data structures memory addresses information. Format of the obtained hash depends on used platform (especially size of the pointer) and Ruby version.

Format of the hash

Part 1: Signatures

  • MAGIC – NODEMARSHAL11

  • RUBY_PLATFORM – saved RUBY_PLATFORM constant value

  • RUBY_VERSION – saved RUBY_VERSION constant value

Part 2: Program loadable elements.

All loadable elements are arrays. Index of the array element means its identifier that is used in the node tree.

  • literals – program literals (strings, ranges etc.)

  • symbols – program symbols (values have either String or Fixnum data type; numbers are used for symbols that cannot be represented as strings)

  • global_entries – global variables information

  • id_tables – array of arrays. Each array contains symbols IDs

  • args – information about code block argument(s)

Part 3: Nodes information

  • nodes – string that contains binary encoded information about the nodes

  • num_of_nodes – number of nodes in the nodes field

  • nodename – name of the node (usually “<main>”)

  • filename – name (without path) of .rb file used for the node generation

  • filepath – name (with full path) of .rb file used for the node generation



1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
# File 'ext/node-marshal/nodedump.c', line 1900

static VALUE m_nodedump_to_hash(VALUE self)
{
	NODE *node = RNODE(rb_iv_get(self, "@node"));
	NODEInfo *info;
	VALUE ans, num, val_info, gc_was_disabled;
	// DISABLE GARBAGE COLLECTOR (important for dumping)
	gc_was_disabled = rb_gc_disable();
	// Convert the node to the form with relocs (i.e. the information about node)
	// if such form is not present
	val_info = rb_iv_get(self, "@nodeinfo");
	if (val_info == Qnil)
	{
		val_info = Data_Make_Struct(cNodeInfo, NODEInfo,
			NODEInfo_mark, NODEInfo_free, info); // This data envelope cannot exist without NODE
		NODEInfo_init(info);
		rb_iv_set(self, "@nodeinfo", val_info);
		num = INT2FIX(count_num_of_nodes(node, node, info));
		rb_iv_set(self, "@nodeinfo_num_of_nodes", num);
		// Convert node to NODEInfo structure
		ans = NODEInfo_toHash(info);
		rb_hash_aset(ans, ID2SYM(rb_intern("num_of_nodes")), num);
		rb_hash_aset(ans, ID2SYM(rb_intern("nodename")), rb_iv_get(self, "@nodename"));
		rb_hash_aset(ans, ID2SYM(rb_intern("filename")), rb_iv_get(self, "@filename"));
		rb_hash_aset(ans, ID2SYM(rb_intern("filepath")), rb_iv_get(self, "@filepath"));
		rb_iv_set(self, "@nodehash", ans);
	}
	else
	{
		ans = rb_iv_get(self, "@nodehash");
	}
	// ENABLE GARBAGE COLLECTOR (important for dumping)
	if (gc_was_disabled == Qfalse)
	{
		rb_gc_enable();
	}
	return ans;
}

#to_textObject

Converts NodeMarshal class example to the text string (modified Base85 encoding) that can be saved to the file and used for loading the node from the file. Format of the obtained binary dump depends on used platform (especially size of the pointer) and Ruby version.



2292
2293
2294
2295
2296
# File 'ext/node-marshal/nodedump.c', line 2292

static VALUE m_nodedump_to_text(VALUE self)
{
	VALUE bin = m_nodedump_to_bin(self);
	return base85r_encode(bin);
}