Interpretation as a Local Variable Overrides Method Name

Interpretation as a local variable overrides method name?

In Ruby, because methods can be called without an explicit receiver and without parentheses, there is a syntactic ambiguity between a local variable reference and a receiverless argumentless method call:

foo

could either mean "call method foo on self with no arguments" or "dereference local variable foo".

If there exists a local variable foo in scope, this is always interpreted as a local variable dereference, never as a method call.

So, what does it mean for a local variable to "be in scope"? This is determined syntactically at parse time, not semantically at runtime. This is very important! Local variables are defined at parse time: if an assignment to a local variable is seen by the parser, the local variable is in scope from that point on. It is, however, only initialized at runtime, there is no compile time evaluation of code going on:

if false
foo = 42 # from this point on, the local variable foo is in scope
end

foo # evaluates to nil, since it is declared but not initialized

Why does it make sense for local variables to "shadow" methods and not the way around? Well, if methods did shadow local variables, there would no longer be a way to dereference those local variables. However, if local variables shadow methods, then there is still a way to call those methods: remember, the ambiguity only exists for receiverless argumentless methods calls, if you add an explicit receiver or an explicit argument list, you can still call the method:

def bar; 'Hello from method' end; public :bar

bar # => 'Hello from method'

bar = 'You will never see this' if false

bar # => nil

bar = 'Hello from local variable'

bar # => 'Hello from local variable'
bar() # => 'Hello from method'
self.bar # => 'Hello from method'

Overriding a Local Variable name in Java Bytecode using the ASM library

You'll need to write an adapter (a subclass of ClassVisitor) and chain it with reader. For instance,

ClassReader reader = new ClassReader(new FileInputStream(new File("TheClass")));
ClassWriter writer = new ClassWriter(reader, 0);
TraceClassVisitor printer = new TraceClassVisitor(writer,
new PrintWriter(System.getProperty("java.io.tmpdir")
+ File.separator + name + ".log"));
ClassAdapter adapter = new ClassAdapter(printer);
reader.accept(adapter, 0);
byte[] b = writer.toByteArray();

With it you'll get byte[], which you can save into file, or load into Class with a ClassLoader.

(TraceClassVisitor is just another ClassVisitor that I chain it also to get a human-readable log in your temp directory.)

The adapter could look like the following. The method you'll want to override is visitLocalVariable:

public class ClassAdapter extends ClassVisitor {
public ClassAdapter(ClassVisitor cv) {
super(Opcodes.ASM5, cv);
}

@Override
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
return new MethodAdapter(mv);
}
}

public class MethodAdapter extends MethodVisitor {
public MethodAdapter(MethodVisitor mv) {
super(Opcodes.ASM5, mv);
}

@Override
public void visitLocalVariable(String name, String desc, String signature,
Label start, Label end, int index) {
// Put your rename logic here
if (name.equals("c"))
name = "e";
else if (name.equals("d"))
name = "f";
super.visitLocalVariable(name, desc, signature, start, end, index);
}
}

Strange local variable behavior in Ruby

Ruby determines the lifetime of local variables while it parses the code, so even if params = 1 assignment wouldn't be reached, params will be interpreted as local variable (and set to nil by default) in this scope.

Here's the link to documentation:

http://docs.ruby-lang.org/en/2.1.0/syntax/assignment_rdoc.html#label-Local+Variables+and+Methods

Getting local variable names defined inside a method from outside the method

You can (re)-parse the method and inspect the S-EXPR tree. See below for a proof of concept. You can get hold of the file where the method is defined using Method#source_location and then read that file. There is surely room for improvement to my code but should get you started. It is a fully functional piece of code and only requires the ruby parser gem (https://github.com/whitequark/parser).

require 'parser/current'
node = Parser::CurrentRuby.parse(DATA.read) # DATA is everything that comes after __END__

def find_definition(node, name)
return node if definition_node?(node, name)
if node.respond_to?(:children)
node.children.find do |child|
find_definition(child, name)
end
end
end

def definition_node?(node, name)
return false if !node.respond_to?(:type)
node.type == :def && node.children.first == name
end

def collect_lvasgn(node)
result = []
return result if !node.respond_to?(:children)

node.children.each do |child|
if child.respond_to?(:type) && child.type == :lvasgn
result << child.children.first
else
result += collect_lvasgn(child)
end
end

result
end

definition = find_definition(node, :foo)
puts collect_lvasgn(definition)

__END__

def foo
var = 100
arr = [1,2]
if something
this = 3 * var
end
end

def bar
var = 200
arr = [3, 4]
end

Do you mind telling us WHY you want to find the variables?

Defining a method that checks the existence of a variable

In ruby, the keyword def acts a scope gate, so you lose access to local variables that are defined outside of the method definition. Alternatively, you can define methods using a block, allowing you access to variables defined outside of the method definition.

a = '123'

define_method 'a?' do
defined? a
end

For more information on this, I highly recommend reading through the first section of Metaprogramming Ruby

Unexecuted code overrides local variable

Try this:

class Foo
attr_reader :bar
def initialize
p "instance methods defined in Foo: #{self.methods(false)}"
@bar = "abc"
p "defined? @bar: #{defined? @bar}"
p "bar: #{bar}"
p "defined? bar: #{defined? bar}"
if false
bar = "123"
end
p "defined? bar, 2nd time: #{defined? bar}"
p "bar.nil? = #{bar.nil?}"
p "self.bar = #{self.bar}"
p "instance methods defined in Foo: #{self.class.instance_methods(false)}"
end
end

Foo.new
"instance methods defined in Foo: [:bar]"
"defined? @bar: instance-variable"
"bar: abc"
"defined? bar: method"
"defined? bar, 2nd time: local-variable"
"bar.nil? = true"
"self.bar = abc"
"instance methods defined in Foo: [:bar]"

The lines:

"defined? @bar: instance-variable"
"defined? bar: method"

show that @bar is an instance variable and bar is an instance method, namely the getter method for @bar created by attr_reader :bar. Before

if false
bar = "123"
end

is evaluated, Ruby peers into the if clause. There she sees bar = "123". If invoked, this would assign the value "123"to an uninitialized local variable bar.

bar= cannot be an instance method (e.g., a setter for @bar) because any method whose name ends with an equals sign must be invoked on an explicit receiver. (It works that way to allow coders to use local variables that have the same names as instance variables, minus the leading @.)

What is an "explicit" receiver? If Foo had a public instance method buz, you could write:

foo = Foo.new
foo.buz

foo is an explicit receiver for the method buz. To invoke buz from within one of Foo's instance methods, you could use an explicit receiver:

self.buz

or just write:

buz

in which case self is the implicit receiver.

As bar= can only be written with an explicit receiver, we would have write:

attr_writer :bar
...
self.bar = "123"

to invoke @bar's setter.

Where were we? Ah, we just concluded that:

if false
bar = "123"
end

would assign a value to the local variable bar if the if clause were executed, regardless of whether there exists a method Foo#bar=.

Because false is, well, false, the contents of the if clause are not executed, so the value of bar is not changed from nil.

The important thing is that the local variable bar and the instance variable @bar are just as different from each other as are night and @day. We can easily show that as follows:

a  = 'cat'
@a = 'dog'
a #=> "cat"
a = 'pig'
@a #=> "dog"

How do I override a method in C#?

Method overloading does not work for local functions. The code is using local functions and they follow regular rules for defining variables in a scope - you can't redefine it if parent scope already contains something with the same name (see Variable scope confusion in C#). Note that only name of the local method is taken into account for that check unlike class level methods that use both name and parameters to find matching function ("method overloading").

You can either

  • name local functions differently. This is likely help best approach if exercise you are trying to is about local functions. "Calculate" is pretty meaningless name and likely cause confusion. Something like "ModifyTotal" would be reasonable name for second function.
  • in some cases you can use same variable and assign lambda to it. That would work if methods have the same signature. I'd not try to go that route in the case shown in the question as parameters are different and ref/out parameters are tricky to get right (Cannot use ref or out parameter in lambda expressions).
  • avoid local methods altogether to allow parameter overloading to work by moving methods out to class level. Note that you can't capture local variable in class-level methods - since methods in the question don't capture local variable moving them at class level will make main method shorter and easier to follow.
  • inline the local methods, especially if they are called once.


Related Topics



Leave a reply



Submit