Class: Node

Inherits:
Object show all
Defined in:
ext/nodewrap.c,
lib/nodepp.rb,
lib/as_code.rb,
lib/node_to_a.rb,
lib/as_expression.rb,
ext/nodewrap.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, CVAR2, CVASGN, CVDECL, DASGN, DASGN_CURR, DEFINED, DEFN, DEFS, DMETHOD, DOT2, DOT3, DREGX, DREGX_ONCE, DSTR, DSYM, DVAR, DXSTR, ENSURE, EVSTR, FALSENODE, 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, NILNODE, NOT, NTH_REF, OPT_N, OP_ASGN1, OP_ASGN2, OP_ASGN_AND, OP_ASGN_OR, OR, POSTEXE, REDO, RESBODY, RESCUE, RESTARGS, RETRY, RETURN, SCLASS, SCOPE, SELF, STR, SUPER, TRUENODE, 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:



1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
# File 'ext/nodewrap.c', line 1370

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

  if(   ruby_safe_level >= 4
     || (ruby_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:



1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
# File 'ext/nodewrap.c', line 1286

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(STR2CSTR(file), str, NUM2INT(line));

  if(ruby_nerrs > 0)
  {
#ifdef RUBY_HAS_YARV
    ruby_nerrs = 0;
    rb_exc_raise(GET_THREAD()->errinfo);
#else
    compile_error(0);
#endif
  }

  return wrap_node(node);
}

.define_code(klass, &block) ⇒ Object



50
51
52
53
54
# File 'lib/as_code.rb', line 50

def define_code(klass, &block)
  if const_defined?(klass) then
    const_get(klass).instance_eval { define_method(:as_code_impl, &block) }
  end
end

.define_expression(klass, &block) ⇒ Object



60
61
62
63
64
# File 'lib/as_expression.rb', line 60

def define_expression(klass, &block)
  if const_defined?(klass) then
    const_get(klass).instance_eval { define_method(:as_expression_impl, &block) }
  end
end

Instance Method Details

#[](member) ⇒ Object

Return the given member of a node.

Returns:



384
385
386
387
388
389
390
# File 'ext/nodewrap.c', line 384

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

#_dumpString

Dump a node.

Returns:

  • (String)


1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
# File 'ext/nodewrap.c', line 1344

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

  if(ruby_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);
  VALUE s =  marshal_dump(arr, limit);
  return s;
}

#addressNumeric

Returns a node’s address.

Returns:

  • (Numeric)


283
284
285
286
287
288
# File 'ext/nodewrap.c', line 283

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

#as_code(indent = 0, *args) ⇒ Object



33
34
35
# File 'lib/as_code.rb', line 33

def as_code(indent=0, *args)
  return as_code_impl(self, indent, *args)
end

#as_expression(*args) ⇒ Object

Return an string describing this node as a single expression. By default, this just returns the name of the node’s type, but some node types override this method to produce more meaningful output.



37
38
39
# File 'lib/as_expression.rb', line 37

def as_expression(*args)
  return as_expression_impl(self, *args)
end

#as_paren_expression(*args) ⇒ Object

Return a string as with as_expression, but surround it with parens if it is a composite expression, so that it can be used to form more complex expressions.



44
45
46
47
48
49
50
# File 'lib/as_expression.rb', line 44

def as_paren_expression(*args)
  expr = self.as_expression(*args)
  if not OMIT_PARENS[self.class] then
    expr = "(#{expr})"
  end
  return expr
end

#bytecode_compileVM::InstructionSequence

Compile a parsed node tree into a bytecode sequence.



1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
# File 'ext/nodewrap.c', line 1318

static VALUE node_bytecode_compile(int argc, VALUE * argv, VALUE self)
{
  NODE * node = unwrap_node(self);
  VALUE opt = Qnil;
  rb_compile_option_t option;

  rb_scan_args(argc, argv, "01", &opt);
  make_compile_option(&option, opt);

  return rb_iseq_new_with_opt(
      node,
      rb_str_new2("<main>"),
      rb_str_new2(node->nd_file),
      Qfalse,
      ISEQ_TYPE_TOP,
      &option);
}

#eval(Object) ⇒ Object

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

Returns:



1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
# File 'ext/nodewrap.c', line 1120

static VALUE node_eval(VALUE node, VALUE self)
{
  NODE * n = unwrap_node(node);

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

#ifdef RUBY_HAS_YARV
  return yarvcore_eval_parsed(n, rb_str_new2("(eval)"));
#else
  {
    /* Ruby doesn't give us access to rb_eval, so we have to fake it. */
    struct BLOCK * b;
    VALUE proc;

    proc = create_proc(rb_cProc, Qnil, n, 0);
    Data_Get_Struct(proc, struct BLOCK, b);
    b->self = self;
    return rb_funcall(proc, rb_intern("call"), 0);
  }
#endif
}

#flagsNumeric

Returns a node’s flags.

Returns:

  • (Numeric)


296
297
298
299
300
301
# File 'ext/nodewrap.c', line 296

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)


436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
# File 'ext/nodewrap.c', line 436

static VALUE node_inspect(VALUE node)
{
#if RUBY_VERSION_CODE < 190
  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)


373
374
375
376
# File 'ext/nodewrap.c', line 373

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)


309
310
311
312
313
314
315
316
317
318
319
320
321
# File 'ext/nodewrap.c', line 309

static VALUE node_nd_file(VALUE self)
{
  NODE * n;
  Data_Get_Struct(self, NODE, n);
  if(n->nd_file)
  {
    return rb_str_new2(n->nd_file);
  }
  else
  {
    return Qnil;
  }
}

#nd_lineNumeric

Returns the line number the node is associated with.

Returns:

  • (Numeric)


329
330
331
332
333
334
# File 'ext/nodewrap.c', line 329

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:



342
343
344
345
346
347
348
349
350
351
352
353
# File 'ext/nodewrap.c', line 342

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));
}

#pretty_print(pp) ⇒ Object

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



57
58
59
# File 'lib/nodepp.rb', line 57

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

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

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



23
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
# File 'lib/nodepp.rb', line 23

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 Object.const_defined?(:VM) and
          VM.const_defined?(:InstructionSequence) and
          VM::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, idx|
        if line =~ /^== disasm: (.*?)=/ then line = $1; end
        if line =~ /(.*)\s+\(\s*\d+\)/ then line = $1; end
        next if line =~ /^\|-----/
        last_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