Month: January 2008

Pointcut Your Javascript

Posted by – January 24, 2008

Sorry for all of the RKelly updates, but thats what I’ve been doing in my free time. I mean, besides J-School. But I don’t think anyone wants to read my poor Japanese!

I wanted to make my ASTs easily searchable, so I added an AOP style interface to my AST. The AST will now let you pointcut your javascript and give you back a list of all of the points it finds. Take this javascript for example:

try {
  Element.update('another_id',"blah blah blah");
  Element.update(10,"blah blah blah");
} catch (e) { }

Lets say you wanted to find every place that Element.update() was called with 2 arguments, and mess with those arguments. You can pointcut the ast, then modify the arguments by giving the AST a pattern to match on like so:

parser = RKelly::Parser.new
ast = parser.parse(DATA.read)
ast.pointcut("Element.update(Object, Object)").matches.each do |m|
  m.arguments.value.each { |x|
    x.value = rand(20)
  }
end

puts ast.to_ecma

But lets say you only want to update the call that was made with a number as the first parameter. No problem! Just change your pattern to use a number, like so:

parser = RKelly::Parser.new
ast = parser.parse(DATA.read)
ast.pointcut("Element.update(Number, Object)").matches.each do |m|
  m.arguments.value.each { |x|
    x.value = rand(20)
  }
end

puts ast.to_ecma

You can even get more specific and match the arguments exactly. For example, matching just the function call where the first argument is ‘another_id’:

parser = RKelly::Parser.new
ast = parser.parse(DATA.read)
ast.pointcut("Element.update('another_id', Object)").matches.each do |m|
  m.arguments.value.each { |x|
    x.value = rand(20)
  }
end

Maybe you don’t care the update is being called on an Element, but you want to match all places that update is being called on something. The pointcut will match on node type too, so this is a perfectly valid pattern:

parser = RKelly::Parser.new
ast = parser.parse(DATA.read)
ast.pointcut("ResolveNode.update('another_id', Object)").matches.each do |m|
  m.arguments.value.each { |x|
    x.value = rand(20)
  }
end

Hopefully you get the picture. This feature isn’t full tested yet, but I think I might do my first RKelly release after I finish testing.

Clean it up! RKelly::Nodes::Node#to_ecma

Posted by – January 23, 2008

Last night at Nerd Club, I decided to add a “to_ecma” method to RKelly. This means you can turn your ECMA AST back in to ECMAScript, with the added bonus of properly indenting your code. For example:

require 'rkelly'

parser = RKelly::Parser.new
puts parser.parse(DATA.read).to_ecma

__END__
function yo(a,b,a) { this.be.some(); if(nasty) { code();} }

will output this:

function yo(a, b, a){
  this.be.some();
  if(nasty) {
    code();
  }
}

Now you should be able to modify your AST and generate javascript. Next I want to add finders to easily find nodes in the tree.

パンツないな興奮 – Javascript stuff

Posted by – January 19, 2008

While writing RKelly (a pure ruby javascript interpreter), I’ve run across weird cases in the ECMA spec that I didn’t know about before. For example, when coercing an object into a number, first the “valueOf” property is checked. That seemed to make sense to me. But then the spec says that if there is no “valueOf” property, to check the “toString” property and try to coerce that in to a number.

So, this bit of code:

var x = new Object;
x.valueOf = function() { return 11; };
x++;

is equivalent to this bit of code:

var x = new Object;
x.stringValue = function() { return "11"; }
x++;

That is some underpants-free excitement.

mechanize version 0.7.0 has been released!

Posted by – January 15, 2008

The Mechanize library is used for automating interaction with websites.
Mechanize automatically stores and sends cookies, follows redirects,
can follow links, and submit forms. Form fields can be populated and
submitted. Mechanize also keeps track of the sites that you have visited as
a history.

Changes:

# Mechanize CHANGELOG

## 0.7.0

* Removed Ruby 1.8.2 support
* Changed parser to lazily parse links
* Lazily parsing document
* Adding verify_callback for SSL requests. Thanks Mike Dalessio!
* Fixed a bug with Accept-Language header. Thanks Bill Siggelkow.

*

Happy New Year! (RKelly Progress report)

Posted by – January 3, 2008

I’ve just started getting the runtime working with RKelly. Its working well enough at this point that I was able to execute my earlier Fibonacci example. I’ve added a method to the runtime that allows you to define ruby functions that may be called from inside javascript. For example, the alert function in the following example is defined in ruby and delegates to puts.

runtime = RKelly::Runtime.new

runtime.define_function(:alert) do |*args|
  puts(*args)
end

runtime.execute(<<END
function f(n) {
  var s = 0;
  if(n == 0) return(s);
  if(n == 1) {
    s += 1;
    return(s);
  } else {
    return(f(n - 1) + f(n - 2));
  }
}
alert(f(20));
END
)

Here is the execution time with ruby 1.8.6 on my machine:

[aaron@mac-mini rkelly]$ time ~/.multiruby/install/1.8.6-p111/bin/ruby -I lib test.rb
6765

real 0m54.332s
user 0m53.913s
sys 0m0.336s
[aaron@mac-mini rkelly]$
[/sourcecode]

Same code, same machine, but with ruby 1.9.0:


[aaron@mac-mini rkelly]$ time ~/.multiruby/install/1.9.0-0/bin/ruby -I lib test.rb
6765

real 0m20.863s
user 0m20.678s
sys 0m0.142s
[aaron@mac-mini rkelly]$
[/sourcecode]

I need to get loops working next!