Class: Node

Inherits:
Object show all
Defined in:
ext/internal/node/node.c,
lib/internal/node/pp.rb,
lib/internal/node/to_a.rb,
lib/internal/node/obfusc.rb,
ext/internal/node/node.c,
ext/internal/vm/inline_cache/inline_cache.c

Overview

Node is a wrapper for Ruby’s Nodes, which are not objects. Nodes can be obtained from many of the other methods in the nodewrap library (see Method#body and Proc#body, for example).

Defined Under Namespace

Classes: ALIAS, ALLOCA, AND, ARGS, ARGSCAT, ARGSPUSH, ARRAY, ATTRASGN, ATTRSET, BACK_REF, BEGIN, BLOCK, BLOCK_ARG, BLOCK_PASS, BMETHOD, BREAK, CALL, CASE, CDECL, CFUNC, CLASS, COLON2, COLON3, CONST, CREF, CVAR, CVASGN, CVDECL, DASGN, DASGN_CURR, DEFINED, DEFN, DEFS, DMETHOD, DOT2, DOT3, DREGX, DREGX_ONCE, DSTR, DSYM, DVAR, DXSTR, ENSURE, EVSTR, FALSE, FBODY, FCALL, FLIP2, FLIP3, FOR, GASGN, GVAR, HASH, IASGN, IF, IFUNC, ITER, IVAR, LASGN, LIT, LVAR, MASGN, MATCH, MATCH2, MATCH3, MEMO, METHOD, MODULE, NEWLINE, NEXT, NIL, NOT, NTH_REF, OPT_N, OP_ASGN1, OP_ASGN2, OP_ASGN2_ARG, OP_ASGN_AND, OP_ASGN_OR, OR, POSTEXE, REDO, RESBODY, RESCUE, RETRY, RETURN, SCLASS, SCOPE, SELF, SPLAT, STR, SUPER, SVALUE, TO_ARY, TRUE, UNDEF, UNTIL, VALIAS, VCALL, WHEN, WHILE, XSTR, YIELD, ZARRAY, ZSUPER

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

._load(str) ⇒ Node

Load a dumped node.

Returns:



899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
# File 'ext/internal/node/node.c', line 899

static VALUE node_load(VALUE klass, VALUE str)
{
  VALUE arr, node_hash, node_id, id_hash;
  NODE * n;
  VALUE data;

  if(   rb_safe_level() >= 4
     || (rb_safe_level() >= 1 && OBJ_TAINTED(str)))
  {
    /* no playing with knives in the sandbox */
    rb_raise(rb_eSecurityError, "Insecure: can't load node");
  }

  arr = marshal_load(str);
  node_hash = rb_ary_pop(arr);
  node_id = rb_ary_pop(arr);
  id_hash = rb_hash_new();
  data = rb_hash_aref(node_hash, node_id);
  n = load_node_from_hash(data, node_id, node_hash, id_hash);
  /* TODO: Need a free function in this case */
  return wrap_node(n);
}

.compile_string(str) ⇒ Node

Compile a string into a node.

Returns:



792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
# File 'ext/internal/node/node.c', line 792

static VALUE node_compile_string(int argc, VALUE * argv, VALUE self)
{
  NODE * node;
  VALUE str = Qnil, file = Qnil, line = Qnil;

  rb_scan_args(argc, argv, "12", &str, &file, &line);

  file = NIL_P(file) ? rb_str_new2("(compiled)") : file;
  line = NIL_P(line) ? INT2NUM(1) : line;

  node = rb_compile_string(StringValuePtr(file), str, NUM2INT(line));

#ifdef RUBY_VM
  if(!node)
  {
    VALUE thread = rb_thread_current();
    rb_thread_t * th;
    GetThreadPtr(thread, th);
    rb_exc_raise(th->errinfo);
  }
#else
  if(ruby_nerrs > 0)
  {
    ruby_nerrs = 0;
    compile_error(0);
  }
#endif

  return wrap_node(node);
}

.nd_typeNodeType

Returns a NodeType structure representing the type of the node.

Returns:



404
405
406
407
408
409
410
411
412
413
# File 'ext/internal/node/node.c', line 404

static VALUE node_s_type(VALUE self)
{
  const Node_Type_Descrip * descrip;
  descrip = node_type_descrip(
      NUM2INT(rb_iv_get(self, "__type__")));
  return rb_struct_new(
      rb_cNodeType,
      rb_str_new2(descrip->name),
      INT2NUM(descrip->nt));
}

Instance Method Details

#[](member) ⇒ Object

Return the given member of a node.

Returns:



321
322
323
324
325
326
327
# File 'ext/internal/node/node.c', line 321

static VALUE node_bracket(VALUE node, VALUE member)
{
  ID id = SYMBOL_P(member)
    ? SYM2ID(member)
    : rb_intern(StringValuePtr(member));
  return rb_funcall(node, id, 0);
}

#_dumpString

Dump a node.

Returns:

  • (String)


873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
# File 'ext/internal/node/node.c', line 873

static VALUE node_dump(VALUE self, VALUE limit)
{
  NODE * n;
  VALUE node_hash, arr, s;

  if(rb_safe_level() >= 4)
  {
    /* no access to potentially sensitive data from the sandbox */
    rb_raise(rb_eSecurityError, "Insecure: can't dump node");
  }

  Data_Get_Struct(self, NODE, n);
  node_hash = node_to_hash(n);
  arr = rb_ary_new();
  rb_ary_push(arr, node_id(n));
  rb_ary_push(arr, node_hash);
  s =  marshal_dump(arr, limit);
  return s;
}

#addressNumeric

Returns a node’s address.

Returns:

  • (Numeric)


215
216
217
218
219
220
# File 'ext/internal/node/node.c', line 215

static VALUE node_address(VALUE self)
{
  NODE * n;
  Data_Get_Struct(self, NODE, n);
  return ULONG2NUM((unsigned long)(n));
}

#bytecode_compileVM::InstructionSequence

Compile a parsed node tree into a bytecode sequence.

Returns:

  • (VM::InstructionSequence)

Parameters:

  • name

    the name of the new iseq (default <unknown>)

  • filename

    the filename for the new iseq (default same as name)



833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
# File 'ext/internal/node/node.c', line 833

static VALUE node_bytecode_compile(int argc, VALUE * argv, VALUE self)
{
  NODE * node;
  VALUE name = Qnil;
  VALUE filename = Qnil;
  VALUE filepath = Qnil; /* TODO */

  rb_scan_args(argc, argv, "02", &name, &filename);

  if(name == Qnil)
  {
    name = rb_str_new2("<unknown>");
  }

  if(filename == Qnil)
  {
    filename = name;
  }

  node = unwrap_node(self);

  return rb_iseq_new(
      node,
      name,
      filename,
#if RUBY_VERSION_CODE >= 192
      filepath,
#endif
      Qfalse,
      ISEQ_TYPE_TOP);
}

#eval(Object) ⇒ Object

Evaluate a node with the given object as self and returns the result.

Returns:



603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
# File 'ext/internal/node/node.c', line 603

static VALUE node_eval(int argc, VALUE * argv, VALUE node)
{
  NODE * n = unwrap_node(node);

  VALUE self;
  VALUE cref = Qnil;
  rb_scan_args(argc, argv, "11", &self, &cref);

  if(rb_safe_level() >= 2)
  {
    /* evaluating a node can cause a crash */
    rb_raise(rb_eSecurityError, "Insecure: can't eval node");
  }

  return eval_ruby_node(n, self, cref);
}

#flagsNumeric

Returns a node’s flags.

Returns:

  • (Numeric)


228
229
230
231
232
233
# File 'ext/internal/node/node.c', line 228

static VALUE node_flags(VALUE self)
{
  NODE * n;
  Data_Get_Struct(self, NODE, n);
  return INT2NUM(n->flags);
}

#inspectString

Returns a string representation of the node’s data.

Returns:

  • (String)


379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
# File 'ext/internal/node/node.c', line 379

static VALUE node_inspect(VALUE node)
{
#ifdef HAVE_RB_PROTECT_INSPECT
  if(rb_inspecting_p(node))
  {
    VALUE str = rb_str_new2("#<");
    rb_str_cat2(str, rb_class2name(CLASS_OF(node)));
    rb_str_cat2(str, ":...>");
    return str;
  }
  else
  {
    return rb_protect_inspect(node_inspect_protect, node, 0);
  }
#else
  return rb_exec_recursive(node_inspect_protect, node, 0);
#endif
}

#membersArray of String

Return an array of strings containing the names of a node’s members.

Returns:

  • (Array of String)


310
311
312
313
# File 'ext/internal/node/node.c', line 310

static VALUE node_members(VALUE node)
{
  return node_s_members(rb_class_of(node));
}

#nd_fileString?

Returns the file the node is associated with

Returns:

  • (String, nil)


241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
# File 'ext/internal/node/node.c', line 241

static VALUE node_nd_file(VALUE self)
{
#ifdef HAVE_RB_SOURCE_FILENAME
  NODE * n;
  Data_Get_Struct(self, NODE, n);
  if(n->nd_file)
  {
    return rb_str_new2(n->nd_file);
  }
  else
  {
    return Qnil;
  }
#else
  /* nd_file has been removed in 1.9 */
  return Qnil;
#endif
}

#nd_lineNumeric

Returns the line number the node is associated with.

Returns:

  • (Numeric)


266
267
268
269
270
271
# File 'ext/internal/node/node.c', line 266

static VALUE node_nd_line(VALUE self)
{
  NODE * n;
  Data_Get_Struct(self, NODE, n);
  return LONG2NUM(nd_line(n));
}

#nd_typeNodeType

Returns a NodeType structure representing the type of the node.

Returns:



279
280
281
282
283
284
285
286
287
288
289
290
# File 'ext/internal/node/node.c', line 279

static VALUE node_nd_type(VALUE self)
{
  NODE * n;
  const Node_Type_Descrip * descrip;
  Data_Get_Struct(self, NODE, n);
  rb_check_type((VALUE)(self), T_DATA);
  descrip = node_type_descrip(nd_type(n));
  return rb_struct_new(
      rb_cNodeType,
      rb_str_new2(descrip->name),
      INT2NUM(descrip->nt));
}

#obfusc(begin_nodes = []) ⇒ Object



4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# File 'lib/internal/node/obfusc.rb', line 4

def obfusc(begin_nodes=[])
  n = self

  # The outputted code must do the following:
  #   1. Evaluate each of the begin nodes (BEGIN {...}).
  #   2. Evaluate the main node.
  return <<-END
require 'internal/node'
if RUBY_VERSION != "#{RUBY_VERSION}" then
  $stderr.puts "Wrong Ruby version; please use #{RUBY_VERSION}"
  exit 1
end
begin_nodes = Marshal.load(#{Marshal.dump(begin_nodes).inspect})
n = Marshal.load(#{Marshal.dump(n).inspect})
begin_nodes.each do |node|
  node.eval(self)
end
n.eval(self)
  END
end

#pretty_print(pp) ⇒ Object

Pretty-print node using Node#tree onto s, which can be a String or IO.



58
59
60
# File 'lib/internal/node/pp.rb', line 58

def pretty_print(pp)
  pp.text(tree())
end

#swap(another_node) ⇒ Node

Swap one node with another. Both nodes must respond to the #swap method. Returns the receiver.

Returns:



937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
# File 'ext/internal/node/node.c', line 937

static VALUE node_swap(VALUE self, VALUE other)
{
  NODE * n1;
  NODE * n2;
  NODE tmp;

  if(!rb_obj_respond_to(other, rb_intern("swap"), 0))
  {
    rb_raise(rb_eArgError, "Argument must respond to #swap");
  }

  if(   rb_safe_level() >= 4
     || (rb_safe_level() >= 1 && (OBJ_TAINTED(other) || OBJ_TAINTED(self))))
  {
    /* no playing with knives in the sandbox */
    rb_raise(rb_eSecurityError, "Insecure: can't swap node");
  }

  Data_Get_Struct(self, NODE, n1);
  Data_Get_Struct(other, NODE, n2);

  tmp = *n1;
  *n1 = *n2;
  *n2 = tmp;

  return self;
}

#to_aObject

Raises:

  • (NotImplementedError)


47
48
49
# File 'lib/internal/node/to_a.rb', line 47

def to_a
  raise NotImplementedError, "#to_a not defined for #{self.class}"
end

#tree(s = '', prefix = '') ⇒ Object

Return a string containing an ascii-art tree of the node’s structure.



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/internal/node/pp.rb', line 24

def tree(s = '', prefix = '')
  s << "NODE_#{self.nd_type.to_s} at #{self.nd_file}:#{self.nd_line}\n"
  self.members.each_with_index do |member, idx|
    last = (idx == self.members.size-1)
    s << "#{prefix}#{(last ? '+-' : '|-')}#{member} = "
    value = self[member]
    if Node === value then
      value.tree(s, prefix + (last ? '  ' : '| '))
    elsif defined?(RubyVM) and
          defined?(RubyVM::InstructionSequence) and
          RubyVM::InstructionSequence === value then
      s << "<ISeq:#{value.self.name}@#{value.self.filename}>\n"
      d = value.disasm
      lines = d.split("\n")
      lines.each_with_index do |line, line_idx|
        if line =~ /^== disasm: (.*?)=/ then line = $1; end
        if line =~ /(.*)\s+\(\s*\d+\)/ then line = $1; end
        next if line =~ /^\|-----/
        last_line = (line_idx == lines.size-1)
        s << "#{prefix}#{last ? '  ' : '| '}#{(last_line ? '+-' : '|-')}#{line}\n"
      end
      # p value.local_table
    elsif member == 'noex' then
      s << Noex.stringify(value) + "\n"
    else
      s << value.inspect + "\n"
    end
  end
  return s
end