diff --git a/core/src/main/java/org/jruby/RubyObjectSpace.java b/core/src/main/java/org/jruby/RubyObjectSpace.java index 506bb8bfbbd..e10650c7ab4 100644 --- a/core/src/main/java/org/jruby/RubyObjectSpace.java +++ b/core/src/main/java/org/jruby/RubyObjectSpace.java @@ -37,6 +37,7 @@ import java.util.Iterator; import java.util.Map; +import java.util.stream.Stream; import org.jruby.anno.JRubyMethod; import org.jruby.anno.JRubyModule; @@ -51,6 +52,7 @@ import org.jruby.util.Inspector; import org.jruby.util.Numeric; import org.jruby.util.collections.WeakValuedIdentityMap; +import org.jruby.util.collections.WeakValuedMap; @JRubyModule(name="ObjectSpace") public class RubyObjectSpace { @@ -231,59 +233,74 @@ public WeakMap(Ruby runtime, RubyClass cls) { @JRubyMethod(name = "[]") public IRubyObject op_aref(ThreadContext context, IRubyObject key) { - IRubyObject value = map.get(key); + Map weakMap = getWeakMapFor(key); + IRubyObject value = weakMap.get(key); if (value != null) return value; return context.nil; } + private Map getWeakMapFor(IRubyObject key) { + if (key instanceof RubyFixnum || key instanceof RubyFloat) { + return valueMap; + } + + return identityMap; + } + @JRubyMethod(name = "[]=") public IRubyObject op_aref(ThreadContext context, IRubyObject key, IRubyObject value) { Ruby runtime = context.runtime; - map.put(key, value); + Map weakMap = getWeakMapFor(key); + weakMap.put(key, value); return runtime.newFixnum(System.identityHashCode(value)); } @JRubyMethod(name = "key?") public IRubyObject key_p(ThreadContext context, IRubyObject key) { - return RubyBoolean.newBoolean(context, map.get(key) != null); + Map weakMap = getWeakMapFor(key); + return RubyBoolean.newBoolean(context, weakMap.get(key) != null); } @JRubyMethod(name = "keys") public IRubyObject keys(ThreadContext context) { return context.runtime.newArrayNoCopy( - map.entrySet() - .stream() + getEntryStream() .filter(entry -> entry.getValue() != null) - .map(entry -> entry.getKey()) + .map(Map.Entry::getKey) .toArray(IRubyObject[]::new)); } + private Stream> getEntryStream() { + return Stream.concat(identityMap.entrySet().stream(), valueMap.entrySet().stream()); + } + @JRubyMethod(name = "values") public IRubyObject values(ThreadContext context) { return context.runtime.newArrayNoCopy( - map.values() - .stream() + getEntryStream() + .map(Map.Entry::getValue) .filter(ref -> ref != null) .toArray(IRubyObject[]::new)); } @JRubyMethod(name = {"length", "size"}) public IRubyObject size(ThreadContext context) { - return context.runtime.newFixnum(map.size()); + return context.runtime.newFixnum(identityMap.size() + valueMap.size()); } @JRubyMethod(name = {"include?", "member?"}) public IRubyObject member_p(ThreadContext context, IRubyObject key) { - return RubyBoolean.newBoolean(context, map.containsKey(key)); + return RubyBoolean.newBoolean(context, getWeakMapFor(key).containsKey(key)); } @JRubyMethod(name = {"each", "each_pair"}) public IRubyObject each(ThreadContext context, Block block) { - map.forEach((key, value) -> { + getEntryStream().forEach((entry) -> { + IRubyObject value = entry.getValue(); if (value != null) { - block.yieldSpecific(context, key, value); + block.yieldSpecific(context, entry.getKey(), value); } }); @@ -292,23 +309,23 @@ public IRubyObject each(ThreadContext context, Block block) { @JRubyMethod(name = "each_key") public IRubyObject each_key(ThreadContext context, Block block) { - for (Map.Entry entry : map.entrySet()) { + getEntryStream().forEach((entry) -> { if (entry.getValue() != null) { block.yieldSpecific(context, entry.getKey()); } - } + }); return this; } @JRubyMethod(name = "each_value") public IRubyObject each_value(ThreadContext context, Block block) { - for (Map.Entry entry : map.entrySet()) { + getEntryStream().forEach((entry) -> { IRubyObject value = entry.getValue(); if (value != null) { block.yieldSpecific(context, value); } - } + }); return this; } @@ -320,7 +337,7 @@ public IRubyObject inspect(ThreadContext context) { RubyString part = inspectPrefix(runtime.getCurrentContext(), metaClass.getRealClass(), inspectHashCode()); int base = part.length(); - map.entrySet().forEach(entry -> { + getEntryStream().forEach(entry -> { if (entry.getValue() != null) { if (part.length() == base) { part.cat(Inspector.COLON_SPACE); @@ -339,6 +356,7 @@ public IRubyObject inspect(ThreadContext context) { return part; } - private final WeakValuedIdentityMap map = new WeakValuedIdentityMap(); + private final WeakValuedIdentityMap identityMap = new WeakValuedIdentityMap<>(); + private final WeakValuedMap valueMap = new WeakValuedMap<>(); } } diff --git a/core/src/main/java/org/jruby/ir/IRBuilder.java b/core/src/main/java/org/jruby/ir/IRBuilder.java index 90011b3ca1c..72845d442f5 100644 --- a/core/src/main/java/org/jruby/ir/IRBuilder.java +++ b/core/src/main/java/org/jruby/ir/IRBuilder.java @@ -443,8 +443,14 @@ private void emitEnsureBlocks(IRLoop loop) { private void determineIfWeNeedLineNumber(Node node) { int currLineNum = node.getLine(); - if (currLineNum != lastProcessedLineNum && !(node instanceof NilImplicitNode)) { // Do not emit multiple line number instrs for the same line - needsLineNumInfo = node.isNewline() ? LineInfo.Coverage : LineInfo.Backtrace; + if (currLineNum != lastProcessedLineNum && !(node instanceof NilImplicitNode)) { + LineInfo needsCoverage = node.isNewline() ? LineInfo.Coverage : null; + // DefNode will set it's own line number as part of impl but if it is for coverage we emit as instr also. + if (needsCoverage != null && (!(node instanceof DefNode) || coverageMode != 0)) { // Do not emit multiple line number instrs for the same line + needsLineNumInfo = node.isNewline() ? needsCoverage : LineInfo.Backtrace; + } + + // This line is already process either by linenum or by instr which emits its own. lastProcessedLineNum = currLineNum; } } @@ -1454,8 +1460,10 @@ private void buildArrayPattern(Label testEnd, Variable result, Variable deconstr label("min_args_check_end", minArgsCheck -> { BIntInstr.Op compareOp = pattern.hasRestArg() ? BIntInstr.Op.GTE : BIntInstr.Op.EQ; addInstr(new BIntInstr(minArgsCheck, compareOp, length, new Integer(pattern.minimumArgsNum()))); - fcall(errorString, buildSelf(), "sprintf", - new FrozenString("%s: %s length mismatch (given %d, expected %d)"), deconstructed, deconstructed, as_fixnum(length), new Fixnum(pattern.minimumArgsNum())); + if (isSinglePattern) { + fcall(errorString, buildSelf(), "sprintf", + new FrozenString("%s: %s length mismatch (given %d, expected %d)"), deconstructed, deconstructed, as_fixnum(length), new Fixnum(pattern.minimumArgsNum())); + } addInstr(new CopyInstr(result, fals())); jump(testEnd); }); @@ -1785,14 +1793,15 @@ public Operand buildPatternCase(PatternCaseNode patternCase) { // build each "when" Variable deconstructed = copy(buildNil()); - for (Node aCase : patternCase.getCases().children()) { + Node[] cases = patternCase.getCases().children(); + boolean isSinglePattern = cases.length == 1; + for (Node aCase : cases) { InNode inNode = (InNode) aCase; Label bodyLabel = getNewLabel(); - boolean isSinglePattern = inNode.isSinglePattern(); - Variable eqqResult = copy(tru()); labels.add(bodyLabel); + buildPatternMatch(eqqResult, deconstructed, inNode.getExpression(), value, false, isSinglePattern, errorString); addInstr(createBranch(eqqResult, tru(), bodyLabel)); bodies.put(bodyLabel, inNode.getBody()); diff --git a/core/src/main/java/org/jruby/ir/runtime/IRRuntimeHelpers.java b/core/src/main/java/org/jruby/ir/runtime/IRRuntimeHelpers.java index 6a810019970..91767241976 100644 --- a/core/src/main/java/org/jruby/ir/runtime/IRRuntimeHelpers.java +++ b/core/src/main/java/org/jruby/ir/runtime/IRRuntimeHelpers.java @@ -1745,6 +1745,7 @@ public static RubyModule newRubyClassFromIR(Ruby runtime, String id, StaticScope @Interp public static void defInterpretedClassMethod(ThreadContext context, IRScope method, IRubyObject obj) { + context.setLine(method.getLine()); String id = method.getId(); RubyClass rubyClass = checkClassForDef(context, id, obj); @@ -1766,6 +1767,7 @@ public static void defCompiledClassMethod(ThreadContext context, MethodHandle ha StaticScope scope, String encodedArgumentDescriptors, IRubyObject obj, boolean maybeRefined, boolean receivesKeywordArgs, boolean needsToFindImplementer) { + context.setLine(line); RubyClass rubyClass = checkClassForDef(context, id, obj); if (maybeRefined) scope.captureParentRefinements(context); @@ -1787,6 +1789,7 @@ public static void defCompiledClassMethod(ThreadContext context, MethodHandle va String encodedArgumentDescriptors, IRubyObject obj, boolean maybeRefined, boolean receivesKeywordArgs, boolean needsToFindImplementer) { + context.setLine(line); RubyClass rubyClass = checkClassForDef(context, id, obj); if (maybeRefined) scope.captureParentRefinements(context); @@ -1810,6 +1813,7 @@ private static RubyClass checkClassForDef(ThreadContext context, String id, IRub @Interp public static void defInterpretedInstanceMethod(ThreadContext context, IRScope method, DynamicScope currDynScope, IRubyObject self) { + context.setLine(method.getLine()); Ruby runtime = context.runtime; RubySymbol methodName = method.getName(); RubyModule rubyClass = findInstanceMethodContainer(context, currDynScope, self); @@ -1834,6 +1838,7 @@ public static void defCompiledInstanceMethod(ThreadContext context, MethodHandle StaticScope scope, String encodedArgumentDescriptors, DynamicScope currDynScope, IRubyObject self, boolean maybeRefined, boolean receivesKeywordArgs, boolean needsToFindImplementer) { + context.setLine(line); Ruby runtime = context.runtime; RubySymbol methodName = runtime.newSymbol(id); RubyModule clazz = findInstanceMethodContainer(context, currDynScope, self); @@ -1856,6 +1861,7 @@ public static void defCompiledInstanceMethod(ThreadContext context, MethodHandle String encodedArgumentDescriptors, DynamicScope currDynScope, IRubyObject self, boolean maybeRefined, boolean receivesKeywordArgs, boolean needsToFindImplementer) { + context.setLine(line); Ruby runtime = context.runtime; RubySymbol methodName = runtime.newSymbol(id); RubyModule clazz = findInstanceMethodContainer(context, currDynScope, self); diff --git a/core/src/main/java/org/jruby/ir/targets/JVMVisitor.java b/core/src/main/java/org/jruby/ir/targets/JVMVisitor.java index 6254eeccbb3..3f342ab42d4 100644 --- a/core/src/main/java/org/jruby/ir/targets/JVMVisitor.java +++ b/core/src/main/java/org/jruby/ir/targets/JVMVisitor.java @@ -1432,6 +1432,7 @@ public void DefineClassInstr(DefineClassInstr defineclassinstr) { public void DefineClassMethodInstr(DefineClassMethodInstr defineclassmethodinstr) { IRMethod method = defineclassmethodinstr.getMethod(); + jvmMethod().updateLineNumber(method.getLine()); jvmMethod().loadContext(); JVMVisitorMethodContext context = new JVMVisitorMethodContext(); @@ -1467,6 +1468,7 @@ public void DefineInstanceMethodInstr(DefineInstanceMethodInstr defineinstanceme IRBytecodeAdapter m = jvmMethod(); SkinnyMethodAdapter a = m.adapter; + jvmMethod().updateLineNumber(method.getLine()); m.loadContext(); emitMethod(method, context);