Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve stack depth #4382

Merged
merged 2 commits into from Dec 14, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -7,33 +7,67 @@
import org.jruby.runtime.ArgumentDescriptor;
import org.jruby.runtime.Block;
import org.jruby.runtime.DynamicScope;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.Visibility;
import org.jruby.runtime.builtin.IRubyObject;

import java.lang.invoke.MethodHandle;

public class CompiledIRMetaClassBody extends CompiledIRMethod {
private final boolean pushNewDynScope;
private final boolean popDynScope;
private final boolean scope;

public CompiledIRMetaClassBody(MethodHandle handle, IRScope scope, RubyModule implementationClass) {
super(handle, scope, Visibility.PUBLIC, implementationClass, scope.receivesKeywordArgs());

boolean reuseParentDynScope = scope.getFlags().contains(IRFlags.REUSE_PARENT_DYNSCOPE);
this.pushNewDynScope = !scope.getFlags().contains(IRFlags.DYNSCOPE_ELIMINATED) && !reuseParentDynScope;
this.popDynScope = this.pushNewDynScope || reuseParentDynScope;
this.scope = !scope.getFlags().contains(IRFlags.DYNSCOPE_ELIMINATED);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This simplification confuses me. Did we never reuse dyn scopes?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class is only used for metaclass bodies, which never reuse their parent scope. I realized this and removed that logic, and the rest boiled away.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah I guess that is probably true of any hard scope. Almost makes me think we should probably ask scope if it needs a scope or not instead of interpreting our flags per its runtime object (method, block). Anyways, thanks for the explanation.

}

public ArgumentDescriptor[] getArgumentDescriptors() {
return new ArgumentDescriptor[0];
return ArgumentDescriptor.EMPTY_ARRAY;
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, Block block) {
StaticScope staticScope1 = this.staticScope;
RubyModule implementationClass1 = this.implementationClass;
pre(context, staticScope1, implementationClass1, self, name, block);

try {
return (IRubyObject) this.variable.invokeExact(context, staticScope1, self, IRubyObject.NULL_ARRAY, block, implementationClass1, name);
} catch (Throwable t) {
Helpers.throwException(t);
return null; // not reached
} finally {
post(context);
}
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args, Block block) {
throw new RuntimeException("BUG: this path should never be called");
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, Block block) {
throw new RuntimeException("BUG: this path should never be called");
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, Block block) {
throw new RuntimeException("BUG: this path should never be called");
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) {
throw new RuntimeException("BUG: this path should never be called");
}

@Override
protected void post(ThreadContext context) {
// update call stacks (pop: ..)
context.popFrame();
if (popDynScope) {
if (scope) {
context.popScope();
}
}
Expand All @@ -42,12 +76,13 @@ protected void post(ThreadContext context) {
protected void pre(ThreadContext context, StaticScope staticScope, RubyModule implementationClass, IRubyObject self, String name, Block block) {
// update call stacks (push: frame, class, scope, etc.)
context.preMethodFrameOnly(implementationClass, name, self, block);
if (pushNewDynScope) {
if (scope) {
// Add a parent-link to current dynscope to support non-local returns cheaply
// This doesn't affect variable scoping since local variables will all have
// the right scope depth.
context.pushScope(DynamicScope.newDynamicScope(staticScope, context.getCurrentScope()));
}
context.setCurrentVisibility(getVisibility());
}

}
Expand Up @@ -22,7 +22,6 @@ public class CompiledIRMethod extends AbstractIRMethod {
protected final MethodHandle specific;
protected final int specificArity;

private final boolean hasExplicitCallProtocol;
private final boolean hasKwargs;

public CompiledIRMethod(MethodHandle variable, IRScope method, Visibility visibility,
Expand All @@ -39,7 +38,6 @@ public CompiledIRMethod(MethodHandle variable, MethodHandle specific, int specif
// unboxing -- it was a simple path to hacking this in).
this.specificArity = hasKwargs ? -1 : specificArity;
this.method.getStaticScope().determineModule();
this.hasExplicitCallProtocol = method.hasExplicitCallProtocol();
this.hasKwargs = hasKwargs;

setHandle(variable);
Expand Down Expand Up @@ -81,190 +79,145 @@ protected void pre(ThreadContext context, StaticScope staticScope, RubyModule im

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args, Block block) {
if (!hasExplicitCallProtocol) return callNoProtocol(context, self, name, args, block);

if (hasKwargs) args = IRRuntimeHelpers.frobnicateKwargsArgument(context, args, getSignature().required());

return invokeExact(this.variable, context, staticScope, self, args, block, implementationClass, name);
try {
return (IRubyObject) this.variable.invokeExact(context, staticScope, self, args, block, implementationClass, name);
}
catch (Throwable t) {
Helpers.throwException(t);
return null; // not reached
}
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, Block block) {
if (specificArity != 0) return call(context, self, clazz, name, IRubyObject.NULL_ARRAY, block);

if (!hasExplicitCallProtocol) return callNoProtocol(context, self, clazz, name, block);

return invokeExact(this.specific, context, staticScope, self, block, implementationClass, name);
try {
return (IRubyObject) this.specific.invokeExact(context, staticScope, self, block, implementationClass, name);
}
catch (Throwable t) {
Helpers.throwException(t);
return null; // not reached
}
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, Block block) {
if (!hasExplicitCallProtocol) return callNoProtocol(context, self, clazz, name, arg0, block);

if (specificArity != 1) return call(context, self, clazz, name, new IRubyObject[]{arg0}, block);

return invokeExact(this.specific, context, staticScope, self, arg0, block, implementationClass, name);
try {
return (IRubyObject) this.specific.invokeExact(context, staticScope, self, arg0, block, implementationClass, name);
}
catch (Throwable t) {
Helpers.throwException(t);
return null; // not reached
}
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, Block block) {
if (!hasExplicitCallProtocol) return callNoProtocol(context, self, clazz, name, arg0, arg1, block);

if (specificArity != 2) return call(context, self, clazz, name, new IRubyObject[] {arg0, arg1}, block);

return invokeExact(this.specific, context, staticScope, self, arg0, arg1, block, implementationClass, name);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) {
if (!hasExplicitCallProtocol) return callNoProtocol(context, self, clazz, name, arg0, arg1, arg2, block);

if (specificArity != 3) return call(context, self, clazz, name, new IRubyObject[] {arg0, arg1, arg2 }, block);

return invokeExact(this.specific, context, staticScope, self, arg0, arg1, arg2, block, implementationClass, name);
}

private IRubyObject callNoProtocol(ThreadContext context, IRubyObject self, String name, IRubyObject[] args, Block block) {
StaticScope staticScope = this.staticScope;
RubyModule implementationClass = this.implementationClass;
pre(context, staticScope, implementationClass, self, name, block);

if (hasKwargs) args = IRRuntimeHelpers.frobnicateKwargsArgument(context, args, getSignature().required());

try {
return invokeExact(this.variable, context, staticScope, self, args, block, implementationClass, name);
return (IRubyObject) this.specific.invokeExact(context, staticScope, self, arg0, arg1, block, implementationClass, name);
}
finally { post(context); }
}

public final IRubyObject callNoProtocol(ThreadContext context, IRubyObject self, RubyModule clazz, String name, Block block) {
if (specificArity != 0) return call(context, self, clazz, name, IRubyObject.NULL_ARRAY, block);

StaticScope staticScope = this.staticScope;
RubyModule implementationClass = this.implementationClass;
pre(context, staticScope, implementationClass, self, name, block);

try {
return invokeExact(this.specific, context, staticScope, self, block, implementationClass, name);
}
finally { post(context); }
}

public final IRubyObject callNoProtocol(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, Block block) {
if (specificArity != 1) return call(context, self, clazz, name, Helpers.arrayOf(arg0), block);

StaticScope staticScope = this.staticScope;
RubyModule implementationClass = this.implementationClass;
pre(context, staticScope, implementationClass, self, name, block);

try {
return invokeExact(this.specific, context, staticScope, self, arg0, block, implementationClass, name);
catch (Throwable t) {
Helpers.throwException(t);
return null; // not reached
}
finally { post(context); }
}

public final IRubyObject callNoProtocol(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, Block block) {
if (specificArity != 2) return call(context, self, clazz, name, Helpers.arrayOf(arg0, arg1), block);

StaticScope staticScope = this.staticScope;
RubyModule implementationClass = this.implementationClass;
pre(context, staticScope, implementationClass, self, name, block);
@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) {
if (specificArity != 3) return call(context, self, clazz, name, new IRubyObject[] {arg0, arg1, arg2 }, block);

try {
return invokeExact(this.specific, context, staticScope, self, arg0, arg1, block, implementationClass, name);
return (IRubyObject) this.specific.invokeExact(context, staticScope, self, arg0, arg1, arg2, block, implementationClass, name);
}
finally { post(context); }
}

public final IRubyObject callNoProtocol(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) {
if (specificArity != 3) return call(context, self, clazz, name, Helpers.arrayOf(arg0, arg1, arg2), block);

StaticScope staticScope = this.staticScope;
RubyModule implementationClass = this.implementationClass;
pre(context, staticScope, implementationClass, self, name, block);

try {
return invokeExact(this.specific, context, staticScope, self, arg0, arg1, arg2, block, implementationClass, name);
catch (Throwable t) {
Helpers.throwException(t);
return null; // not reached
}
finally { post(context); }
}

public String getFile() {
return method.getFileName();
}

public int getLine() {
return method.getLineNumber();
}

@Override
public String toString() {
return getClass().getName() + '@' + Integer.toHexString(hashCode()) + ' ' + method + ' ' + getSignature();
}
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args) {
if (hasKwargs) args = IRRuntimeHelpers.frobnicateKwargsArgument(context, args, getSignature().required());

private static IRubyObject invokeExact(MethodHandle method,
ThreadContext context, StaticScope staticScope, IRubyObject self,
IRubyObject[] args, Block block,
RubyModule implementationClass, String name) {
try {
return (IRubyObject) method.invokeExact(context, staticScope, self, args, block, implementationClass, name);
return (IRubyObject) this.variable.invokeExact(context, staticScope, self, args, Block.NULL_BLOCK, implementationClass, name);
}
catch (Throwable t) {
Helpers.throwException(t);
return null; // not reached
}
}

private static IRubyObject invokeExact(MethodHandle method,
ThreadContext context, StaticScope staticScope, IRubyObject self,
Block block,
RubyModule implementationClass, String name) {
@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name) {
if (specificArity != 0) return call(context, self, clazz, name, IRubyObject.NULL_ARRAY, Block.NULL_BLOCK);

try {
return (IRubyObject) method.invokeExact(context, staticScope, self, block, implementationClass, name);
return (IRubyObject) this.specific.invokeExact(context, staticScope, self, Block.NULL_BLOCK, implementationClass, name);
}
catch (Throwable t) {
Helpers.throwException(t);
return null; // not reached
}
}

private static IRubyObject invokeExact(MethodHandle method,
ThreadContext context, StaticScope staticScope, IRubyObject self,
IRubyObject arg0, Block block,
RubyModule implementationClass, String name) {
@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0) {
if (specificArity != 1) return call(context, self, clazz, name, new IRubyObject[]{arg0}, Block.NULL_BLOCK);

try {
return (IRubyObject) method.invokeExact(context, staticScope, self, arg0, block, implementationClass, name);
return (IRubyObject) this.specific.invokeExact(context, staticScope, self, arg0, Block.NULL_BLOCK, implementationClass, name);
}
catch (Throwable t) {
Helpers.throwException(t);
return null; // not reached
}
}

private static IRubyObject invokeExact(MethodHandle method,
ThreadContext context, StaticScope staticScope, IRubyObject self,
IRubyObject arg0, IRubyObject arg1, Block block,
RubyModule implementationClass, String name) {
@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1) {
if (specificArity != 2) return call(context, self, clazz, name, new IRubyObject[] {arg0, arg1}, Block.NULL_BLOCK);

try {
return (IRubyObject) method.invokeExact(context, staticScope, self, arg0, arg1, block, implementationClass, name);
return (IRubyObject) this.specific.invokeExact(context, staticScope, self, arg0, arg1, Block.NULL_BLOCK, implementationClass, name);
}
catch (Throwable t) {
Helpers.throwException(t);
return null; // not reached
}
}

private static IRubyObject invokeExact(MethodHandle method,
ThreadContext context, StaticScope staticScope, IRubyObject self,
IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block,
RubyModule implementationClass, String name) {
@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
if (specificArity != 3) return call(context, self, clazz, name, new IRubyObject[] {arg0, arg1, arg2 }, Block.NULL_BLOCK);

try {
return (IRubyObject) method.invokeExact(context, staticScope, self, arg0, arg1, arg2, block, implementationClass, name);
return (IRubyObject) this.specific.invokeExact(context, staticScope, self, arg0, arg1, arg2, Block.NULL_BLOCK, implementationClass, name);
}
catch (Throwable t) {
Helpers.throwException(t);
return null; // not reached
}
}

public String getFile() {
return method.getFileName();
}

public int getLine() {
return method.getLineNumber();
}

@Override
public String toString() {
return getClass().getName() + '@' + Integer.toHexString(hashCode()) + ' ' + method + ' ' + getSignature();
}

}