Bloody hell, a couple beers and several hours later and I can't get this
patch working.
Attached is a partial patch for optimizing the processing of opt and
rest args in Ruby method definitions. The bonuses are that it doesn't
use an ArrayList at all, it totally skips iterative processing of
arguments in a few cases, and should be cheaper when constructing
restargs arrays in all cases.
Except it doesn't work. Or rather, it doesn't work in ant test, but
works for simple cases.
Compare the old logic (too complicated) with the new logic (too
complicated) and see if you can figure out what's wrong. The current
theory is that changing restargs in the eventual args array to be an
actual subarray rather than just "more elements" has broken some JRuby
internals.
I'd love to be able to land this, but I'm not going to get it working
this evening. Give it a go if you love array juggling.
- Charlie
Index: src/org/jruby/internal/runtime/methods/DefaultMethod.java
===================================================================
--- src/org/jruby/internal/runtime/methods/DefaultMethod.java (revision 3577)
+++ src/org/jruby/internal/runtime/methods/DefaultMethod.java (working copy)
@@ -250,71 +250,108 @@
}
private IRubyObject[] prepareOptOrRestArgs(ThreadContext context, Ruby
runtime, IRubyObject[] args, int expectedArgsCount, int restArg, boolean
hasOptArgs) {
- if (restArg == -1 && hasOptArgs) {
+ if (hasOptArgs) {
+ int argsLength = args.length;
int opt = expectedArgsCount + argsNode.getOptArgs().size();
-
- if (opt < args.length) {
- throw runtime.newArgumentError("wrong # of arguments(" +
args.length + " for " + opt + ")");
+ if (restArg == -1) {
+ // no rest arg, make sure args.length == opt
+ if (opt < args.length) {
+ // too many
+ throw runtime.newArgumentError("wrong # of arguments(" +
args.length + " for " + opt + ")");
+ } else {
+ if (args.length < opt) {
+ // fewer explicit args, but opt can fill in
+ IRubyObject[] newArgs = new IRubyObject[opt];
+ System.arraycopy(args, 0, newArgs, 0, args.length);
+ args = newArgs;
+ } else {
+ // exactly as many args as needed, proceed to opt
assignment
+ }
+ }
+ } else {
+ // rest arg, make sure args.length = opt + 1
+ if (args.length < expectedArgsCount) {
+ // too few
+ throw runtime.newArgumentError("wrong # of arguments(" +
args.length + " for " + opt + ")");
+ } if (args.length < opt) {
+ // exactly as many args as explicit, rest = nil
+ IRubyObject[] newArgs = new IRubyObject[opt + 1];
+ System.arraycopy(args, 0, newArgs, 0, args.length);
+ args = newArgs;
+ args[opt] = RubyArray.newArrayNoCopy(runtime,
IRubyObject.NULL_ARRAY);
+ context.getCurrentScope().setValue(restArg, args[opt], 0);
+ } else {
+ // more args than explicit
+ IRubyObject[] newArgs = new IRubyObject[opt + 1];
+ IRubyObject[] restArgs = new IRubyObject[args.length -
opt];
+ System.arraycopy(args, 0, newArgs, 0, opt);
+ System.arraycopy(args, opt, restArgs, 0, args.length -
opt);
+ newArgs[opt] = RubyArray.newArrayNoCopy(runtime, restArgs);
+ args = newArgs;
+ context.getCurrentScope().setValue(restArg, args[opt], 0);
+ }
}
- }
-
- int count = expectedArgsCount;
- if (argsNode.getOptArgs() != null) {
- count += argsNode.getOptArgs().size();
- }
-
- ArrayList allArgs = new ArrayList();
-
- // Combine static and optional args into a single list allArgs
- for (int i = 0; i < count && i < args.length; i++) {
- allArgs.add(args[i]);
- }
-
- if (hasOptArgs) {
- ListNode optArgs = argsNode.getOptArgs();
-
- int j = 0;
- for (int i = expectedArgsCount; i < args.length && j <
optArgs.size(); i++, j++) {
- // in-frame EvalState should already have receiver set as
self, continue to use it
- AssignmentVisitor.assign(runtime, context,
context.getFrameSelf(), optArgs.get(j), args[i], Block.NULL_BLOCK, true);
- expectedArgsCount++;
+
+ // continue on to hanldle opt args, if any
+ int count = expectedArgsCount;
+ if (argsNode.getOptArgs() != null) {
+ count += argsNode.getOptArgs().size();
}
-
- // assign the default values, adding to the end of allArgs
- while (j < optArgs.size()) {
- // in-frame EvalState should already have receiver set as
self, continue to use it
- allArgs.add(EvaluationState.eval(runtime, context,
optArgs.get(j++), context.getFrameSelf(), Block.NULL_BLOCK));
+
+ if (hasOptArgs) {
+ ListNode optArgs = argsNode.getOptArgs();
+
+ int j = 0;
+ int optSize = optArgs.size();
+ for (int i = expectedArgsCount; i < argsLength && j < optSize;
i++, j++) {
+ // in-frame EvalState should already have receiver set as
self, continue to use it
+ AssignmentVisitor.assign(runtime, context,
context.getFrameSelf(), optArgs.get(j), args[i], Block.NULL_BLOCK, true);
+ expectedArgsCount++;
+ }
+
+ // assign the default values, adding to the end of allArgs
+ while (j < optArgs.size()) {
+ // in-frame EvalState should already have receiver set as
self, continue to use it
+ args[expectedArgsCount + j] =
EvaluationState.eval(runtime, context, optArgs.get(j++),
context.getFrameSelf(), Block.NULL_BLOCK);
+ }
}
- }
-
- // build an array from *rest type args, also adding to allArgs
-
- // ENEBO: Does this next comment still need to be done since I killed
hasLocalVars:
- // move this out of the scope.hasLocalVariables() condition to deal
- // with anonymous restargs (* versus *rest)
-
-
- // none present ==> -1
- // named restarg ==> >=0
- // anonymous restarg ==> -2
- if (restArg != -1) {
- for (int i = expectedArgsCount; i < args.length; i++) {
- allArgs.add(args[i]);
- }
-
- // only set in scope if named
- if (restArg >= 0) {
- RubyArray array = runtime.newArray(args.length -
expectedArgsCount);
- for (int i = expectedArgsCount; i < args.length; i++) {
- array.append(args[i]);
+
+ // build an array from *rest type args, also adding to allArgs
+
+ // ENEBO: Does this next comment still need to be done since I
killed hasLocalVars:
+ // move this out of the scope.hasLocalVariables() condition to deal
+ // with anonymous restargs (* versus *rest)
+
+ return args;
+ } else {
+ if (restArg == -1) {
+ throw new RuntimeException("We should not be here if there's
no opt and no rest args. Report this at www.jruby.org.");
+ } else {
+ // rest arg, make sure args.length >= expected + 1
+ if (args.length < expectedArgsCount) {
+ // too few
+ throw runtime.newArgumentError("wrong # of arguments(" +
args.length + " for " + expectedArgsCount + ")");
+ } if (args.length == expectedArgsCount) {
+ // exactly as many args as explicit, rest = nil
+ IRubyObject[] newArgs = new IRubyObject[expectedArgsCount
+ 1];
+ System.arraycopy(args, 0, newArgs, 0, args.length);
+ args = newArgs;
+ args[restArg] = RubyArray.newArrayNoCopy(runtime,
IRubyObject.NULL_ARRAY);
+ context.getCurrentScope().setValue(restArg, args[restArg],
0);
+ return args;
+ } else {
+ // more args than explicit
+ IRubyObject[] newArgs = new IRubyObject[expectedArgsCount
+ 1];
+ IRubyObject[] restArgs = new IRubyObject[args.length -
expectedArgsCount];
+ System.arraycopy(args, 0, newArgs, 0, expectedArgsCount);
+ System.arraycopy(args, expectedArgsCount, restArgs, 0,
args.length - expectedArgsCount);
+ newArgs[expectedArgsCount] =
RubyArray.newArrayNoCopy(runtime, restArgs);
+ args = newArgs;
+ context.getCurrentScope().setValue(restArg,
args[expectedArgsCount], 0);
+ return args;
}
-
- context.getCurrentScope().setValue(restArg, array, 0);
}
}
-
- args = (IRubyObject[])allArgs.toArray(new IRubyObject[allArgs.size()]);
- return args;
}
private void traceReturn(ThreadContext context, Ruby runtime, RubyBinding
binding, String name) {
Index: src/org/jruby/RubyArray.java
===================================================================
--- src/org/jruby/RubyArray.java (revision 3576)
+++ src/org/jruby/RubyArray.java (working copy)
@@ -327,6 +327,17 @@
return new RubyArray(runtime, args);
}
+ public static RubyArray newArrayNoCopy(Ruby runtime, IRubyObject[] args,
int start, int len) {
+ return new RubyArray(runtime, args, start, len);
+ }
+
+ public static RubyArray newArrayNoCopyShared(Ruby runtime, IRubyObject[]
args, int start, int len) {
+ RubyArray result = new RubyArray(runtime, args, start, len);
+ result.shared = true;
+
+ return result;
+ }
+
public static RubyArray newArrayNoCopyLight(Ruby runtime, IRubyObject[]
args) {
RubyArray arr = new RubyArray(runtime, false);
arr.values = args;
@@ -358,6 +369,16 @@
values = vals;
realLength = vals.length;
}
+
+ /*
+ * plain internal array assignment
+ */
+ private RubyArray(Ruby runtime, IRubyObject[] vals, int start, int len) {
+ super(runtime, runtime.getArray());
+ values = vals;
+ begin = start;
+ realLength = len;
+ }
/* rb_ary_new2
* just allocates the internal array
---------------------------------------------------------------------
To unsubscribe from this list please visit:
http://xircles.codehaus.org/manage_email