Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' into test-parameters-refactor
Browse files Browse the repository at this point in the history
headius committed May 9, 2015
2 parents e3dd9c4 + 4e2ae1b commit e97d9e0
Showing 18 changed files with 197 additions and 105 deletions.
87 changes: 78 additions & 9 deletions core/src/main/java/org/jruby/ir/IRBuilder.java
Original file line number Diff line number Diff line change
@@ -2215,6 +2215,13 @@ Exception region start marker_2 (protected by whichever block handles exceptions
* ****************************************************************/
public Operand buildEnsureNode(EnsureNode ensureNode) {
// Save $! in a temp var so it can be restored when the exception gets handled.
Variable savedGlobalException = createTemporaryVariable();
addInstr(new GetGlobalVariableInstr(savedGlobalException, "$!"));

// prepare $!-clearing ensure block
EnsureBlockInfo lastErrorReset = resetLastErrorPreamble(savedGlobalException);

Node bodyNode = ensureNode.getBodyNode();

// ------------ Build the body of the ensure block ------------
@@ -2242,7 +2249,7 @@ public Operand buildEnsureNode(EnsureNode ensureNode) {
activeRescuers.push(ebi.dummyRescueBlockLabel);

// Generate IR for code being protected
Operand rv = bodyNode instanceof RescueNode ? buildRescueInternal((RescueNode) bodyNode, ebi) : build(bodyNode);
Operand rv = bodyNode instanceof RescueNode ? buildRescueInternal((RescueNode) bodyNode, ebi, savedGlobalException) : build(bodyNode);

// End of protected region
addInstr(new ExceptionRegionEndMarkerInstr());
@@ -2280,6 +2287,9 @@ public Operand buildEnsureNode(EnsureNode ensureNode) {
// End label for the exception region
addInstr(new LabelInstr(ebi.end));

// close out the $!-clearing region
resetLastErrorPostamble(lastErrorReset);

return rv;
}

@@ -3023,20 +3033,80 @@ public Operand buildRegexp(RegexpNode reNode) {
}

public Operand buildRescue(RescueNode node) {
return buildRescueInternal(node, null);
// Save $! in a temp var so it can be restored when the exception gets handled.
Variable savedGlobalException = createTemporaryVariable();
addInstr(new GetGlobalVariableInstr(savedGlobalException, "$!"));

// prepare $!-clearing ensure block
EnsureBlockInfo lastErrorReset = resetLastErrorPreamble(savedGlobalException);

// build the rescue itself
Operand rv = buildRescueInternal(node, null, savedGlobalException);

// close out the $!-clearing region
resetLastErrorPostamble(lastErrorReset);

return rv;
}

private Operand buildRescueInternal(RescueNode rescueNode, EnsureBlockInfo ensure) {
// Labels marking start, else, end of the begin-rescue(-ensure)-end block
Label rBeginLabel = ensure == null ? getNewLabel() : ensure.regionStart;
Label rEndLabel = ensure == null ? getNewLabel() : ensure.end;
Label rescueLabel = getNewLabel(); // Label marking start of the first rescue code.
private void resetLastErrorPostamble(EnsureBlockInfo lastErrorReset) {
addInstr(new ExceptionRegionEndMarkerInstr());
activeRescuers.pop();

// We don't reset $! for normal exit, but we do need to jump past the outer finally
addInstr(new JumpInstr(lastErrorReset.end));

// Pop the current ensure block info node
activeEnsureBlockStack.pop();

// ------------ Emit the ensure body alongwith dummy rescue block ------------
// Now build the dummy rescue block that
// catches all exceptions thrown by the body
Variable exc2 = createTemporaryVariable();
addInstr(new LabelInstr(lastErrorReset.dummyRescueBlockLabel));
addInstr(new ReceiveJRubyExceptionInstr(exc2));

// Now emit the ensure body's stashed instructions
lastErrorReset.emitBody(this);

// Return (rethrow exception/end)
// rethrows the caught exception from the dummy ensure block
addInstr(new ThrowExceptionInstr(exc2));

// End label for the exception region
addInstr(new LabelInstr(lastErrorReset.end));
}

private EnsureBlockInfo resetLastErrorPreamble(Variable savedGlobalException) {
EnsureBlockInfo lastErrorReset = new EnsureBlockInfo(scope,
null,
getCurrentLoop(),
activeRescuers.peek());

lastErrorReset.addInstr(new PutGlobalVarInstr("$!", savedGlobalException));

// ------------ Build the protected region ------------
activeEnsureBlockStack.push(lastErrorReset);

// Start of protected region
addInstr(new LabelInstr(lastErrorReset.regionStart));
addInstr(new ExceptionRegionStartMarkerInstr(lastErrorReset.dummyRescueBlockLabel));
activeRescuers.push(lastErrorReset.dummyRescueBlockLabel);
return lastErrorReset;
}

private Operand buildRescueInternal(RescueNode rescueNode, EnsureBlockInfo ensure, Variable savedGlobalException) {
// Wrap the entire begin+rescue+ensure+else with $!-resetting "finally" logic

// Save $! in a temp var so it can be restored when the exception gets handled.
Variable savedGlobalException = createTemporaryVariable();
addInstr(new GetGlobalVariableInstr(savedGlobalException, "$!"));
if (ensure != null) ensure.savedGlobalException = savedGlobalException;

// Labels marking start, else, end of the begin-rescue(-ensure)-end block
Label rBeginLabel = ensure == null ? getNewLabel() : ensure.regionStart;
Label rEndLabel = ensure == null ? getNewLabel() : ensure.end;
Label rescueLabel = getNewLabel(); // Label marking start of the first rescue code.

addInstr(new LabelInstr(rBeginLabel));

// Placeholder rescue instruction that tells rest of the compiler passes the boundaries of the rescue block.
@@ -3111,7 +3181,6 @@ private Operand buildRescueInternal(RescueNode rescueNode, EnsureBlockInfo ensur
// End label -- only if there is no ensure block! With an ensure block, you end at ensureEndLabel.
if (ensure == null) addInstr(new LabelInstr(rEndLabel));

activeRescueBlockStack.pop();
return rv;
}

2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/lexer/yacc/RubyLexer.java
Original file line number Diff line number Diff line change
@@ -813,7 +813,7 @@ private int getIntegerToken(String value, int radix, int suffix) {
* mri: is_identchar
*/
public boolean isIdentifierChar(int c) {
return !eofp && (current_enc.isAlnum(c) || c == '_' || isMultiByteChar(c));
return !eofp && (Character.isLetterOrDigit(c) || c == '_' || isMultiByteChar(c));
}

public boolean isASCII(int c) {
24 changes: 0 additions & 24 deletions lib/ruby/stdlib/jruby/ext.rb
Original file line number Diff line number Diff line change
@@ -45,28 +45,4 @@ def steal_methods(type, *method_names)
end
end
end

class ::Method
def args
self_r = JRuby.reference0(self)
method = self_r.get_method
args_ary = []

case method
when MethodArgs2
return Helpers.parameter_list_to_parameters(JRuby.runtime, method.parameter_list, true)
when IRMethodArgs
a = method.parameter_list
(0...(a.size)).step(2) do |i|
args_ary << (a[i+1] == "" ? [a[i].to_sym] : [a[i].to_sym, a[i+1].to_sym])
end
else
if method.arity == Arity::OPTIONAL
args_ary << [:rest]
end
end

args_ary
end
end
end
15 changes: 15 additions & 0 deletions spec/ruby/language/fixtures/super.rb
Original file line number Diff line number Diff line change
@@ -355,4 +355,19 @@ class AnonymousModuleIncludedTwice < AnonymousModuleIncludedTwiceBase
whatever
whatever
end

module ZSuperWithBlock
class A
def a
yield
end
end

class B < A
def a
super { 14 }
end
end
end

end
5 changes: 5 additions & 0 deletions spec/ruby/language/super_spec.rb
Original file line number Diff line number Diff line change
@@ -161,4 +161,9 @@ def a(arg)
it "invokes methods from a chain of anonymous modules" do
Super::AnonymousModuleIncludedTwice.new.a([]).should == ["anon", "anon", "non-anon"]
end

it "can accept a block but still pass the original arguments" do
Super::ZSuperWithBlock::B.new.a.should == 14
end

end
37 changes: 0 additions & 37 deletions spec/truffle/tags/core/method/parameters_tags.txt
Original file line number Diff line number Diff line change
@@ -1,38 +1 @@
fails:Method#parameters returns an empty Array when the method expects no arguments
fails:Method#parameters returns [[:req,:name]] for a method expecting one required argument called 'name'
fails:Method#parameters returns [[:req,:a],[:req,:b]] for a method expecting two required arguments called 'a' and 'b''
fails:Method#parameters returns [[:block,:blk]] for a method expecting one block argument called 'a'
fails:Method#parameters returns [[:req,:a],[:block,:b] for a method expecting a required argument ('a') and a block argument ('b')
fails:Method#parameters returns [[:req,:a],[:req,:b],[:block,:c] for a method expecting two required arguments ('a','b') and a block argument ('c')
fails:Method#parameters returns [[:opt,:a]] for a method expecting one optional argument ('a')
fails:Method#parameters returns [[:req,:a],[:opt,:b]] for a method expecting one required argument ('a') and one optional argument ('b')
fails:Method#parameters returns [[:req,:a],[:opt,:b],[:opt,:c]] for a method expecting one required argument ('a') and two optional arguments ('b','c')
fails:Method#parameters returns [[:req,:a],[:req,:b],[:opt,:c]] for a method expecting two required arguments ('a','b') and one optional arguments ('c')
fails:Method#parameters returns [[:opt,:a],[:block,:b]] for a method expecting one required argument ('a') and one block argument ('b')
fails:Method#parameters returns [[:req,:a],[:opt,:b],[:block,:c]] for a method expecting one required argument ('a'), one optional argument ('b'), and a block ('c')
fails:Method#parameters returns [[:req,:a],[:opt,:b],[:opt,:c],[:block,:d]] for a method expecting one required argument ('a'), two optional arguments ('b','c'), and a block ('d')
fails:Method#parameters returns [[:rest,:a]] for a method expecting a single splat argument ('a')
fails:Method#parameters returns [[:req,:a],[:rest,:b]] for a method expecting a splat argument ('a') and a required argument ('b')
fails:Method#parameters returns [[:req,:a],[:req,:b],[:rest,:c]] for a method expecting two required arguments ('a','b') and a splat argument ('c')
fails:Method#parameters returns [[:req,:a],[:opt,:b],[:rest,:c]] for a method expecting a required argument ('a','b'), an optional argument ('b'), and a splat argument ('c')
fails:Method#parameters returns [[:req,:a],[:req,:b],[:opt,:b],[:rest,:d]] for a method expecting two required arguments ('a','b'), an optional argument ('c'), and a splat argument ('d')
fails:Method#parameters returns [[:req,:a],[:opt,:b],[:opt,:c],[:rest,:d]] for a method expecting a required argument ('a'), two optional arguments ('b','c'), and a splat argument ('d')
fails:Method#parameters returns [[:rest,:a],[:block,:b]] for a method expecting a splat argument ('a') and a block argument ('b')
fails:Method#parameters returns [[:req,:a],[:rest,:b],[:block,:c]] for a method expecting a required argument ('a'), a splat argument ('b'), and a block ('c')
fails:Method#parameters returns [[:req,:a],[:req,:b],[:rest,:c],[:block,:d]] for a method expecting two required arguments ('a','b'), a splat argument ('c'), and a block ('d')
fails:Method#parameters returns [[:req,:a],[:opt,:b],[:rest,:c],[:block,:d]] for a method expecting a required argument ('a'), a splat argument ('c'), and a block ('d')
fails:Method#parameters returns [[:req,:a],[:req,:b],[:opt,:c],[:block,:d]] for a method expecting two required arguments ('a','b'), an optional argument ('c'), a splat argument ('d'), and a block ('e')
fails:Method#parameters returns [[:rest,:a],[:req,:b]] for a method expecting a splat argument ('a') and a required argument ('b')
fails:Method#parameters returns [[:rest,:a],[:req,:b],[:req,:c]] for a method expecting a splat argument ('a') and two required arguments ('b','c')
fails:Method#parameters returns [[:rest,:a],[:req,:b],[:block,:c]] for a method expecting a splat argument ('a'), a required argument ('b'), and a block ('c')
fails:Method#parameters works with ->(){} as the value of an optional argument
fails:Method#parameters returns [] for a define_method method with explicit no-args || specification
fails:Method#parameters returns [[:rest, :x]] for a define_method method with rest arg 'x' only
fails:Method#parameters returns [[:req, :x]] for a define_method method expecting one required argument 'x'
fails:Method#parameters returns [[:req, :x], [:req, :y]] for a define_method method expecting two required arguments 'x' and 'y'
fails:Method#parameters returns [] for a define_method method with no args specification
fails:Method#parameters returns [[:req]] for a define_method method with a grouping as its only argument
fails:Method#parameters returns [[:opt, :x]] for a define_method method with an optional argument 'x'
fails:Method#parameters returns [[:rest]] for a Method generated by respond_to_missing?
fails:Method#parameters adds nameless rest arg for "star" argument
fails:Method#parameters returns the args and block for a splat and block argument
2 changes: 0 additions & 2 deletions spec/truffle/tags/core/module/alias_method_tags.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
fails:Module#alias_method converts the names using #to_str
fails:Module#alias_method is a private method
fails:Module#alias_method aliasing special methods keeps initialize private when aliasing
fails:Module#alias_method aliasing special methods keeps initialize_copy private when aliasing
fails:Module#alias_method aliasing special methods keeps initialize_clone private when aliasing
fails:Module#alias_method aliasing special methods keeps initialize_dup private when aliasing
fails:Module#alias_method aliasing special methods keeps respond_to_missing? private when aliasing
fails:Module#alias_method preserves the arguments information of the original methods
1 change: 0 additions & 1 deletion test/jruby.index
Original file line number Diff line number Diff line change
@@ -86,7 +86,6 @@ jruby/test_null_channel
jruby/test_irubyobject_java_passing
jruby/test_jruby_object_input_stream
jruby/test_jar_on_load_path
jruby/test_jruby_ext
jruby/test_jruby_core_ext
jruby/test_thread_context_frame_dereferences_unreachable_variables
jruby/test_context_classloader
17 changes: 0 additions & 17 deletions test/jruby/test_jruby_ext.rb

This file was deleted.

5 changes: 3 additions & 2 deletions tool/jt.rb
Original file line number Diff line number Diff line change
@@ -149,8 +149,9 @@ def help
puts ' --asm show assembly (implies --graal)'
puts ' --server run an instrumentation server on port 8080'
puts ' --igv make sure IGV is running and dump Graal graphs after partial escape (implies --graal)'
puts ' --jdebug run a JDWP debug server on 8000'
puts 'jt e 14 + 2 evaluate an expression'
puts 'jt puts 14 + 2 evaluate and print an expression'
puts 'jt print 14 + 2 evaluate and print an expression'
puts 'jt test run all mri tests and specs'
puts 'jt test mri run mri tests'
puts 'jt test specs run all specs'
@@ -247,7 +248,7 @@ def e(*args)
run '-e', args.join(' ')
end

def puts(*args)
def print(*args)
e 'puts', *args
end

Original file line number Diff line number Diff line change
@@ -16,6 +16,8 @@
import com.oracle.truffle.api.nodes.IndirectCallNode;
import com.oracle.truffle.api.source.NullSourceSection;
import com.oracle.truffle.api.source.SourceSection;
import org.jruby.ast.ArgsNode;
import org.jruby.runtime.Helpers;
import org.jruby.truffle.nodes.core.BasicObjectNodes.ReferenceEqualNode;
import org.jruby.truffle.nodes.objects.ClassNode;
import org.jruby.truffle.nodes.objects.ClassNodeGen;
@@ -133,6 +135,26 @@ public RubyModule owner(RubyMethod method) {

}

@CoreMethod(names = "parameters")
public abstract static class ParametersNode extends CoreMethodArrayArgumentsNode {

public ParametersNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

@CompilerDirectives.TruffleBoundary
@Specialization
public RubyArray parameters(RubyMethod method) {
final ArgsNode argsNode = method.getMethod().getSharedMethodInfo().getParseTree().findFirstChild(ArgsNode.class);

final String[] parameters = Helpers.encodeParameterList((ArgsNode) argsNode).split(";");

return (RubyArray) getContext().toTruffle(Helpers.parameterListToParameters(getContext().getRuntime(),
parameters, true));
}

}

@CoreMethod(names = "receiver")
public abstract static class ReceiverNode extends CoreMethodArrayArgumentsNode {

Original file line number Diff line number Diff line change
@@ -281,25 +281,32 @@ public Object compare(VirtualFrame frame, RubyModule self, RubyBasicObject other
}

@CoreMethod(names = "alias_method", required = 2)
public abstract static class AliasMethodNode extends CoreMethodArrayArgumentsNode {
@NodeChildren({
@NodeChild(type = RubyNode.class, value = "module"),
@NodeChild(type = RubyNode.class, value = "newName"),
@NodeChild(type = RubyNode.class, value = "oldName")
})
public abstract static class AliasMethodNode extends CoreMethodNode {

public AliasMethodNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

@Specialization
public RubyModule aliasMethod(RubyModule module, RubySymbol newName, RubySymbol oldName) {
CompilerDirectives.transferToInterpreter();
@CreateCast("newName")
public RubyNode coercetNewNameToString(RubyNode newName) {
return SymbolOrToStrNodeGen.create(getContext(), getSourceSection(), newName);
}

module.alias(this, newName.toString(), oldName.toString());
return module;
@CreateCast("oldName")
public RubyNode coerceOldNameToString(RubyNode oldName) {
return SymbolOrToStrNodeGen.create(getContext(), getSourceSection(), oldName);
}

@Specialization
public RubyModule aliasMethod(RubyModule module, RubyString newName, RubyString oldName) {
public RubyModule aliasMethod(RubyModule module, String newName, String oldName) {
CompilerDirectives.transferToInterpreter();

module.alias(this, newName.toString(), oldName.toString());
module.alias(this, newName, oldName);
return module;
}

Loading

0 comments on commit e97d9e0

Please sign in to comment.