package org.jruby.runtime;
import com.headius.backport9.stack.StackWalker;
import com.headius.backport9.stack.impl.StackWalker8;
import org.jcodings.Encoding;
import org.jruby.Ruby;
import org.jruby.RubyArray;
import org.jruby.RubyBoolean;
import org.jruby.RubyClass;
import org.jruby.RubyContinuation;
import org.jruby.exceptions.CatchThrow;
import org.jruby.RubyInstanceConfig;
import org.jruby.RubyModule;
import org.jruby.RubyRegexp;
import org.jruby.RubyThread;
import org.jruby.ast.executable.RuntimeCache;
import org.jruby.exceptions.Unrescuable;
import org.jruby.ext.fiber.ThreadFiber;
import org.jruby.internal.runtime.methods.DynamicMethod;
import org.jruby.ir.JIT;
import org.jruby.lexer.yacc.ISourcePosition;
import org.jruby.parser.StaticScope;
import org.jruby.runtime.backtrace.BacktraceData;
import org.jruby.runtime.backtrace.BacktraceElement;
import org.jruby.runtime.backtrace.RubyStackTraceElement;
import org.jruby.runtime.backtrace.TraceType;
import org.jruby.runtime.backtrace.TraceType.Gather;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.profile.ProfileCollection;
import org.jruby.runtime.scope.ManyVarsDynamicScope;
import org.jruby.util.RecursiveComparator;
import org.jruby.util.RubyDateFormatter;
import org.jruby.util.cli.Options;
import org.jruby.util.log.Logger;
import org.jruby.util.log.LoggerFactory;
import java.lang.ref.WeakReference;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import static org.jruby.RubyBasicObject.NEVER;
public final class ThreadContext {
private static final Logger LOG = LoggerFactory.getLogger(ThreadContext.class);
public static ThreadContext newContext(Ruby runtime) {
return new ThreadContext(runtime);
}
private final static int INITIAL_SIZE = 10;
private final static int INITIAL_FRAMES_SIZE = 10;
private final static int CALL_POLL_COUNT = 0xFFF;
public final Ruby runtime;
public final IRubyObject nil;
public final RubyBoolean tru;
public final RubyBoolean fals;
public final RuntimeCache runtimeCache;
private boolean isWithinTrace;
private RubyThread thread;
private static final WeakReference<ThreadFiber> NULL_FIBER_REF = new WeakReference<ThreadFiber>(null);
private WeakReference<ThreadFiber> fiber = NULL_FIBER_REF;
private ThreadFiber rootFiber;
private RubyDateFormatter dateFormatter;
private Frame[] frameStack = new Frame[INITIAL_FRAMES_SIZE];
private int frameIndex = -1;
private BacktraceElement[] backtrace = new BacktraceElement[INITIAL_FRAMES_SIZE];
private int backtraceIndex = -1;
private DynamicScope[] scopeStack = new DynamicScope[INITIAL_SIZE];
private int scopeIndex = -1;
private static final CatchThrow[] EMPTY_CATCHTARGET_STACK = new CatchThrow[0];
private CatchThrow[] catchStack = EMPTY_CATCHTARGET_STACK;
private int catchIndex = -1;
private boolean isProfiling = false;
private ProfileCollection profileCollection;
private boolean eventHooksEnabled = true;
CallType lastCallType;
Visibility lastVisibility;
IRubyObject lastExitStatus;
private Throwable savedExcInLambda;
@Deprecated
public transient SecureRandom secureRandom;
private static boolean tryPreferredPRNG = true;
private static boolean trySHA1PRNG = true;
public final JavaSites sites;
@SuppressWarnings("deprecation")
public SecureRandom getSecureRandom() {
SecureRandom secureRandom = this.secureRandom;
if (secureRandom == null && tryPreferredPRNG) {
try {
secureRandom = SecureRandom.getInstance(Options.PREFERRED_PRNG.load());
} catch (Exception e) {
tryPreferredPRNG = false;
}
}
if (secureRandom == null && trySHA1PRNG) {
try {
secureRandom = SecureRandom.getInstance("SHA1PRNG");
} catch (Exception e) {
trySHA1PRNG = false;
}
}
if (secureRandom == null) {
secureRandom = new SecureRandom();
}
this.secureRandom = secureRandom;
return secureRandom;
}
private Encoding[] encodingHolder;
private ThreadContext(Ruby runtime) {
this.runtime = runtime;
this.nil = runtime.getNil();
this.tru = runtime.getTrue();
this.fals = runtime.getFalse();
this.savedExcInLambda = null;
if (runtime.getInstanceConfig().isProfilingEntireRun()) {
startProfiling();
}
this.runtimeCache = runtime.getRuntimeCache();
this.sites = runtime.sites;
StaticScope topStaticScope = runtime.getStaticScopeFactory().newLocalScope(null);
pushScope(new ManyVarsDynamicScope(topStaticScope, null));
Frame[] stack = frameStack;
int length = stack.length;
for (int i = 0; i < length; i++) {
stack[i] = new Frame();
}
BacktraceElement[] stack2 = backtrace;
int length2 = stack2.length;
for (int i = 0; i < length2; i++) {
stack2[i] = new BacktraceElement();
}
ThreadContext.pushBacktrace(this, "", "", 0);
ThreadContext.pushBacktrace(this, "", "", 0);
}
@Override
protected void finalize() throws Throwable {
if (thread != null) {
thread.dispose();
}
}
public final Ruby getRuntime() {
return runtime;
}
public IRubyObject getErrorInfo() {
return thread.getErrorInfo();
}
public IRubyObject setErrorInfo(IRubyObject errorInfo) {
thread.setErrorInfo(errorInfo);
return errorInfo;
}
public Throwable getSavedExceptionInLambda() {
return savedExcInLambda;
}
public void setSavedExceptionInLambda(Throwable e) {
savedExcInLambda = e;
}
public CallType getLastCallType() {
return lastCallType;
}
public Visibility getLastVisibility() {
return lastVisibility;
}
public void setLastCallStatusAndVisibility(CallType callType, Visibility visibility) {
lastCallType = callType;
lastVisibility = visibility;
}
public IRubyObject getLastExitStatus() {
return lastExitStatus;
}
public void setLastExitStatus(IRubyObject lastExitStatus) {
this.lastExitStatus = lastExitStatus;
}
public void printScope() {
LOG.debug("SCOPE STACK:");
for (int i = 0; i <= scopeIndex; i++) {
LOG.debug("{}", scopeStack[i]);
}
}
public DynamicScope getCurrentScope() {
return scopeStack[scopeIndex];
}
public StaticScope getCurrentStaticScope() {
return scopeStack[scopeIndex].getStaticScope();
}
private void expandFrameStack() {
int newSize = frameStack.length * 2;
frameStack = fillNewFrameStack(new Frame[newSize], newSize);
}
private Frame[] fillNewFrameStack(Frame[] newFrameStack, int newSize) {
System.arraycopy(frameStack, 0, newFrameStack, 0, frameStack.length);
for (int i = frameStack.length; i < newSize; i++) {
newFrameStack[i] = new Frame();
}
return newFrameStack;
}
public void pushScope(DynamicScope scope) {
int index = ++scopeIndex;
DynamicScope[] stack = scopeStack;
stack[index] = scope;
if (index + 1 == stack.length) {
expandScopeStack();
}
}
@JIT
public DynamicScope pushNewScope(StaticScope staticScope) {
DynamicScope scope = staticScope.construct(null);
pushScope(scope);
return scope;
}
public void popScope() {
scopeStack[scopeIndex--] = null;
}
private void expandScopeStack() {
int newSize = scopeStack.length * 2;
DynamicScope[] newScopeStack = new DynamicScope[newSize];
System.arraycopy(scopeStack, 0, newScopeStack, 0, scopeStack.length);
scopeStack = newScopeStack;
}
public RubyThread getThread() {
return thread;
}
public RubyThread getFiberCurrentThread() {
return thread.getFiberCurrentThread();
}
public RubyDateFormatter getRubyDateFormatter() {
if (dateFormatter == null)
dateFormatter = new RubyDateFormatter(this);
return dateFormatter;
}
public void setThread(RubyThread thread) {
this.thread = thread;
if (thread != null) {
thread.setContext(this);
}
}
public ThreadFiber getFiber() {
ThreadFiber f = fiber.get();
if (f == null) return rootFiber;
return f;
}
public void setFiber(ThreadFiber fiber) {
this.fiber = new WeakReference<ThreadFiber>(fiber);
}
public void useRecursionGuardsFrom(ThreadContext context) {
this.symToGuards = context.symToGuards;
}
public void setRootFiber(ThreadFiber rootFiber) {
this.rootFiber = rootFiber;
}
private void expandCatchStack() {
int newSize = catchStack.length * 2;
if (newSize == 0) newSize = 1;
CatchThrow[] newCatchStack = new CatchThrow[newSize];
System.arraycopy(catchStack, 0, newCatchStack, 0, catchStack.length);
catchStack = newCatchStack;
}
@Deprecated
public void pushCatch(RubyContinuation.Continuation catchTarget) {
pushCatch((CatchThrow) catchTarget);
}
public void pushCatch(CatchThrow catchTarget) {
int index = ++catchIndex;
if (index == catchStack.length) {
expandCatchStack();
}
catchStack[index] = catchTarget;
}
public void popCatch() {
catchIndex--;
}
public CatchThrow getActiveCatch(Object tag) {
for (int i = catchIndex; i >= 0; i--) {
CatchThrow c = catchStack[i];
if (c.tag == tag) return c;
}
ThreadFiber fiber = getFiber();
ThreadFiber prev;
if (fiber != null && (prev = fiber.getData().getPrev()) != null) {
return prev.getThread().getContext().getActiveCatch(tag);
}
return null;
}
private Frame pushFrame(Frame frame) {
int index = ++this.frameIndex;
Frame[] stack = frameStack;
stack[index] = frame;
if (index + 1 == stack.length) {
expandFrameStack();
}
return frame;
}
public void pushEvalSimpleFrame(IRubyObject executeObject) {
Frame frame = getCurrentFrame();
pushCallFrame(frame.getKlazz(), frame.getName(), executeObject, Block.NULL_BLOCK);
}
private void pushCallFrame(RubyModule clazz, String name,
IRubyObject self, Block block) {
int index = ++this.frameIndex;
Frame[] stack = frameStack;
stack[index].updateFrame(clazz, self, name, block);
if (index + 1 == stack.length) {
expandFrameStack();
}
}
private void pushCallFrame(RubyModule clazz, String name,
IRubyObject self, Visibility visibility, Block block) {
int index = ++this.frameIndex;
Frame[] stack = frameStack;
stack[index].updateFrame(clazz, self, name, visibility, block);
if (index + 1 == stack.length) {
expandFrameStack();
}
}
private void pushBackrefFrame() {
int index = ++this.frameIndex;
Frame[] stack = frameStack;
stack[index].updateFrameForBackref();
if (index + 1 == stack.length) {
expandFrameStack();
}
}
private void popBackrefFrame() {
Frame[] stack = frameStack;
int index = frameIndex--;
Frame frame = stack[index];
if (frame.isCaptured()) {
stack[index] = new Frame();
} else {
frame.clearFrameForBackref();
}
}
private void pushEvalFrame(IRubyObject self) {
int index = ++this.frameIndex;
Frame[] stack = frameStack;
stack[index].updateFrameForEval(self);
if (index + 1 == stack.length) {
expandFrameStack();
}
}
public void pushFrame() {
int index = ++this.frameIndex;
Frame[] stack = frameStack;
if (index + 1 == stack.length) {
expandFrameStack();
}
}
public void popFrame() {
Frame[] stack = frameStack;
int index = frameIndex--;
Frame frame = stack[index];
if (frame.captured) {
stack[index] = new Frame();
} else {
frame.clear();
}
}
private void popFrameReal(Frame oldFrame) {
frameStack[frameIndex--] = oldFrame;
}
public Frame getCurrentFrame() {
return frameStack[frameIndex];
}
public Frame getNextFrame() {
int index = frameIndex;
Frame[] stack = frameStack;
if (index + 1 == stack.length) {
expandFrameStack();
}
return stack[index + 1];
}
public Frame getPreviousFrame() {
int index = frameIndex;
return index < 1 ? null : frameStack[index - 1];
}
public IRubyObject setBackRef(IRubyObject match) {
return getCurrentFrame().setBackRef(match);
}
public IRubyObject getBackRef() {
return frameStack[frameIndex].getBackRef(nil);
}
public IRubyObject last_match() {
return RubyRegexp.nth_match(0, frameStack[frameIndex].getBackRef(nil));
}
public IRubyObject match_pre() {
return RubyRegexp.match_pre(frameStack[frameIndex].getBackRef(nil));
}
public IRubyObject match_post() {
return RubyRegexp.match_post(frameStack[frameIndex].getBackRef(nil));
}
public IRubyObject match_last() {
return RubyRegexp.match_last(frameStack[frameIndex].getBackRef(nil));
}
public IRubyObject setLastLine(IRubyObject last) {
return getCurrentFrame().setLastLine(last);
}
public IRubyObject getLastLine() {
return getCurrentFrame().getLastLine(nil);
}
private static void expandBacktraceStack(ThreadContext context) {
int newSize = context.backtrace.length * 2;
context.backtrace = fillNewBacktrace(context, new BacktraceElement[newSize], newSize);
}
private static BacktraceElement[] fillNewBacktrace(ThreadContext context, BacktraceElement[] newBacktrace, int newSize) {
System.arraycopy(context.backtrace, 0, newBacktrace, 0, context.backtrace.length);
for (int i = context.backtrace.length; i < newSize; i++) {
newBacktrace[i] = new BacktraceElement();
}
return newBacktrace;
}
public static void pushBacktrace(ThreadContext context, String method, String file, int line) {
int index = ++context.backtraceIndex;
BacktraceElement[] stack = context.backtrace;
BacktraceElement.update(stack[index], method, file, line);
if (index + 1 == stack.length) {
ThreadContext.expandBacktraceStack(context);
}
}
public static void popBacktrace(ThreadContext context) {
context.backtraceIndex--;
}
public boolean hasAnyScopes() {
return scopeIndex > -1;
}
public boolean scopeExistsOnCallStack(DynamicScope scope) {
DynamicScope[] stack = scopeStack;
for (int i = scopeIndex; i >= 0; i--) {
if (stack[i] == scope) return true;
}
return false;
}
public String getFrameName() {
return getCurrentFrame().getName();
}
public IRubyObject getFrameSelf() {
return getCurrentFrame().getSelf();
}
public RubyModule getFrameKlazz() {
return getCurrentFrame().getKlazz();
}
public Block getFrameBlock() {
return getCurrentFrame().getBlock();
}
public String getFile() {
return backtrace[backtraceIndex].filename;
}
public int getLine() {
return backtrace[backtraceIndex].line;
}
public void setLine(int line) {
backtrace[backtraceIndex].line = line;
}
public void setFileAndLine(String file, int line) {
BacktraceElement b = backtrace[backtraceIndex];
b.filename = file;
b.line = line;
}
public void setFileAndLine(ISourcePosition position) {
BacktraceElement b = backtrace[backtraceIndex];
b.filename = position.getFile();
b.line = position.getLine();
}
public Visibility getCurrentVisibility() {
return getCurrentFrame().getVisibility();
}
public void setCurrentVisibility(Visibility visibility) {
getCurrentFrame().setVisibility(visibility);
}
public void pollThreadEvents() {
thread.pollThreadEvents(this);
}
public int callNumber = 0;
public int getCurrentTarget() {
return callNumber;
}
public void callThreadPoll() {
if ((callNumber++ & CALL_POLL_COUNT) == 0) pollThreadEvents();
}
public static void callThreadPoll(ThreadContext context) {
if ((context.callNumber++ & CALL_POLL_COUNT) == 0) context.pollThreadEvents();
}
public void trace(RubyEvent event, String name, RubyModule implClass) {
trace(event, name, implClass, backtrace[backtraceIndex].filename, backtrace[backtraceIndex].line + 1);
}
public void trace(RubyEvent event, String name, RubyModule implClass, String file, int line) {
runtime.callEventHooks(this, event, file, line, name, implClass);
}
@Deprecated
public IRubyObject getConstant(String internedName) {
return getCurrentStaticScope().getConstant(internedName);
}
public void renderCurrentBacktrace(StringBuilder sb) {
TraceType traceType = runtime.getInstanceConfig().getTraceType();
BacktraceData backtraceData = traceType.getBacktrace(this);
traceType.getFormat().renderBacktrace(backtraceData.getBacktrace(runtime), sb, false);
}
public static final StackWalker WALKER = StackWalker.getInstance();
public static final StackWalker WALKER8 = new StackWalker8();
public IRubyObject createCallerBacktrace(int level, int length, Stream<StackWalker.StackFrame> stackStream) {
runtime.incrementCallerCount();
RubyStackTraceElement[] fullTrace = getPartialTrace(level, length, stackStream);
int traceLength = safeLength(level, length, fullTrace);
if (traceLength < 0) return runtime.newEmptyArray();
final IRubyObject[] traceArray = new IRubyObject[traceLength];
for (int i = 0; i < traceLength; i++) {
traceArray[i] = RubyStackTraceElement.to_s_mri(this, fullTrace[i + level]);
}
RubyArray backTrace = RubyArray.newArrayMayCopy(runtime, traceArray);
if (RubyInstanceConfig.LOG_CALLERS) TraceType.logCaller(backTrace);
return backTrace;
}
public IRubyObject createCallerLocations(int level, Integer length, Stream<StackWalker.StackFrame> stackStream) {
runtime.incrementCallerCount();
RubyStackTraceElement[] fullTrace = getPartialTrace(level, length, stackStream);
int traceLength = safeLength(level, length, fullTrace);
if (traceLength < 0) return runtime.newEmptyArray();
RubyArray backTrace = RubyThread.Location.newLocationArray(runtime, fullTrace, level, traceLength);
if (RubyInstanceConfig.LOG_CALLERS) TraceType.logCaller(backTrace);
return backTrace;
}
private RubyStackTraceElement[] getFullTrace(Integer length, Stream<StackWalker.StackFrame> stackStream) {
if (length != null && length == 0) return RubyStackTraceElement.EMPTY_ARRAY;
return TraceType.Gather.CALLER.getBacktraceData(this, stackStream).getBacktrace(runtime);
}
private RubyStackTraceElement[] getPartialTrace(int level, Integer length, Stream<StackWalker.StackFrame> stackStream) {
if (length != null && length == 0) return RubyStackTraceElement.EMPTY_ARRAY;
return TraceType.Gather.CALLER.getBacktraceData(this, stackStream).getPartialBacktrace(runtime, level + length);
}
private static int safeLength(int level, Integer length, RubyStackTraceElement[] trace) {
final int baseLength = trace.length - level;
return Math.min(length, baseLength);
}
public RubyStackTraceElement getSingleBacktrace(int level) {
runtime.incrementWarningCount();
RubyStackTraceElement[] trace = WALKER.walk(stream -> getPartialTrace(level, 1, stream));
if (RubyInstanceConfig.LOG_WARNINGS) TraceType.logWarning(trace);
return trace.length == 0 ? null : trace[trace.length - 1];
}
public RubyStackTraceElement getSingleBacktrace() {
return getSingleBacktrace(0);
}
public boolean isEventHooksEnabled() {
return eventHooksEnabled;
}
public void setEventHooksEnabled(boolean flag) {
eventHooksEnabled = flag;
}
public Stream<BacktraceElement> getBacktrace() {
return getBacktrace(0);
}
public final Stream<BacktraceElement> getBacktrace(int level) {
int backtraceIndex = this.backtraceIndex;
if ( level < 0 ) level = backtraceIndex + 1 + level;
int end = backtraceIndex - level;
BacktraceElement[] backtrace = this.backtrace;
return IntStream.rangeClosed(0, end).mapToObj(i -> backtrace[backtraceIndex - i]);
}
public static String createRawBacktraceStringFromThrowable(final Throwable ex, final boolean color) {
return WALKER.walk(ex.getStackTrace(), stream ->
TraceType.printBacktraceJRuby(null,
new BacktraceData(stream, Stream.empty(), true, false, false).getBacktraceWithoutRuby(),
ex.getClass().getName(),
ex.getLocalizedMessage(),
color));
}
private Frame pushFrameForBlock(Binding binding) {
Frame lastFrame = getNextFrame();
Frame bindingFrame = binding.getFrame();
bindingFrame.setVisibility(binding.getVisibility());
pushFrame(bindingFrame);
return lastFrame;
}
public void preAdoptThread() {
pushFrame();
getCurrentFrame().setSelf(runtime.getTopSelf());
}
public void preExtensionLoad(IRubyObject self) {
pushFrame();
getCurrentFrame().setSelf(self);
getCurrentFrame().setVisibility(Visibility.PUBLIC);
}
public void preBsfApply(String[] names) {
StaticScope staticScope = runtime.getStaticScopeFactory().newLocalScope(null);
staticScope.setVariables(names);
pushFrame();
}
public void postBsfApply() {
popFrame();
}
public void preMethodFrameAndScope(RubyModule clazz, String name, IRubyObject self, Block block,
StaticScope staticScope) {
pushCallFrame(clazz, name, self, block);
pushScope(DynamicScope.newDynamicScope(staticScope));
}
public void preMethodFrameAndDummyScope(RubyModule clazz, String name, IRubyObject self, Block block,
StaticScope staticScope) {
pushCallFrame(clazz, name, self, block);
pushScope(staticScope.getDummyScope());
}
public void preMethodNoFrameAndDummyScope(StaticScope staticScope) {
pushScope(staticScope.getDummyScope());
}
public void postMethodFrameAndScope() {
popScope();
popFrame();
}
public void preMethodFrameOnly(RubyModule clazz, String name, IRubyObject self, Block block) {
pushCallFrame(clazz, name, self, block);
}
public void preMethodFrameOnly(RubyModule clazz, String name, IRubyObject self, Visibility visiblity, Block block) {
pushCallFrame(clazz, name, self, visiblity, block);
}
public void preMethodFrameOnly(RubyModule clazz, String name, IRubyObject self) {
pushCallFrame(clazz, name, self, Block.NULL_BLOCK);
}
public void preBackrefMethod() {
pushBackrefFrame();
}
public void postMethodFrameOnly() {
popFrame();
}
public void postBackrefMethod() {
popBackrefFrame();
}
public void preMethodScopeOnly(StaticScope staticScope) {
pushScope(DynamicScope.newDynamicScope(staticScope));
}
public void postMethodScopeOnly() {
popScope();
}
public void preMethodBacktraceAndScope(String name, StaticScope staticScope) {
preMethodScopeOnly(staticScope);
}
public void postMethodBacktraceAndScope() {
postMethodScopeOnly();
}
public void preMethodBacktraceOnly(String name) {
}
public void preMethodBacktraceDummyScope(String name, StaticScope staticScope) {
pushScope(staticScope.getDummyScope());
}
public void postMethodBacktraceOnly() {
}
public void postMethodBacktraceDummyScope() {
popScope();
}
public void prepareTopLevel(RubyClass objectClass, IRubyObject topSelf) {
pushFrame();
setCurrentVisibility(Visibility.PRIVATE);
Frame frame = getCurrentFrame();
frame.setSelf(topSelf);
getCurrentStaticScope().setModule(objectClass);
}
public void preNodeEval(IRubyObject self) {
pushEvalFrame(self);
}
public void postNodeEval() {
popFrame();
}
public void preExecuteUnder(IRubyObject executeUnderObj, RubyModule executeUnderClass, Block block) {
Frame frame = getCurrentFrame();
DynamicScope scope = getCurrentScope();
StaticScope sScope = runtime.getStaticScopeFactory().newBlockScope(scope.getStaticScope());
sScope.setModule(executeUnderClass);
pushScope(DynamicScope.newDynamicScope(sScope, scope));
pushCallFrame(frame.getKlazz(), frame.getName(), executeUnderObj, block);
getCurrentFrame().setVisibility(getPreviousFrame().getVisibility());
}
public void postExecuteUnder() {
popFrame();
popScope();
}
public void preTrace() {
setWithinTrace(true);
pushFrame();
}
public void postTrace() {
popFrame();
setWithinTrace(false);
}
public Frame preYieldSpecificBlock(Binding binding, StaticScope scope) {
Frame lastFrame = preYieldNoScope(binding);
pushScope(DynamicScope.newDynamicScope(scope, binding.getDynamicScope()));
return lastFrame;
}
public Frame preYieldNoScope(Binding binding) {
return pushFrameForBlock(binding);
}
@JIT
public Frame preYieldNoScope(Block block) {
return pushFrameForBlock(block.getBinding());
}
public void preEvalScriptlet(DynamicScope scope) {
pushScope(scope);
}
public void postEvalScriptlet() {
popScope();
}
public Frame preEvalWithBinding(Binding binding) {
return pushFrameForBlock(binding);
}
public void postEvalWithBinding(Binding binding, Frame lastFrame) {
popFrameReal(lastFrame);
}
public void postYield(Binding binding, Frame lastFrame) {
popScope();
popFrameReal(lastFrame);
}
public void postYieldNoScope(Frame lastFrame) {
popFrameReal(lastFrame);
}
public void preScopedBody(DynamicScope scope) {
pushScope(scope);
}
public void postScopedBody() {
popScope();
}
public boolean isWithinTrace() {
return isWithinTrace;
}
public void setWithinTrace(boolean isWithinTrace) {
this.isWithinTrace = isWithinTrace;
}
public Binding currentBinding() {
Frame frame = getCurrentFrame().capture();
BacktraceElement elt = backtrace[backtraceIndex];
return new Binding(frame, getCurrentScope(), elt.getMethod(), elt.getFilename(), elt.getLine());
}
public Binding currentBinding(IRubyObject self) {
Frame frame = getCurrentFrame().capture();
BacktraceElement elt = backtrace[backtraceIndex];
return new Binding(self, frame, frame.getVisibility(), getCurrentScope(), elt.getMethod(), elt.getFilename(), elt.getLine());
}
public Binding currentBinding(IRubyObject self, Visibility visibility) {
Frame frame = getCurrentFrame().capture();
BacktraceElement elt = backtrace[backtraceIndex];
return new Binding(self, frame, visibility, getCurrentScope(), elt.getMethod(), elt.getFilename(), elt.getLine());
}
public Binding currentBinding(IRubyObject self, DynamicScope scope) {
Frame frame = getCurrentFrame().capture();
BacktraceElement elt = backtrace[backtraceIndex];
return new Binding(self, frame, frame.getVisibility(), scope, elt.getMethod(), elt.getFilename(), elt.getLine());
}
public Binding currentBinding(IRubyObject self, Visibility visibility, DynamicScope scope) {
Frame frame = getCurrentFrame().capture();
BacktraceElement elt = backtrace[backtraceIndex];
return new Binding(self, frame, visibility, scope, elt.getMethod(), elt.getFilename(), elt.getLine());
}
public ProfileCollection getProfileCollection() {
return profileCollection;
}
public void startProfiling() {
isProfiling = true;
profileCollection = runtime.getProfilingService().newProfileCollection( this );
}
public void stopProfiling() {
isProfiling = false;
}
public boolean isProfiling() {
return isProfiling;
}
private int currentMethodSerial = 0;
public int profileEnter(int nextMethod) {
int previousMethodSerial = currentMethodSerial;
currentMethodSerial = nextMethod;
if (isProfiling()) {
getProfileCollection().profileEnter(nextMethod);
}
return previousMethodSerial;
}
public int profileEnter(String name, DynamicMethod nextMethod) {
return profileEnter((int) nextMethod.getSerialNumber());
}
public int profileExit(int nextMethod, long startTime) {
int previousMethodSerial = currentMethodSerial;
currentMethodSerial = nextMethod;
if (isProfiling()) {
getProfileCollection().profileExit(nextMethod, startTime);
}
return previousMethodSerial;
}
public Set<RecursiveComparator.Pair> getRecursiveSet() {
return recursiveSet;
}
public void setRecursiveSet(Set<RecursiveComparator.Pair> recursiveSet) {
this.recursiveSet = recursiveSet;
}
public void setExceptionRequiresBacktrace(boolean exceptionRequiresBacktrace) {
if (runtime.isDebug()) return;
this.exceptionRequiresBacktrace = exceptionRequiresBacktrace;
}
@JIT
public void exceptionBacktraceOn() {
setExceptionRequiresBacktrace(true);
}
@JIT
public void exceptionBacktraceOff() {
setExceptionRequiresBacktrace(false);
}
private Map<String, Map<IRubyObject, IRubyObject>> symToGuards;
private static class RecursiveError extends Error implements Unrescuable {
public RecursiveError(Object tag) {
this.tag = tag;
}
public final Object tag;
@Override
public synchronized Throwable fillInStackTrace() {
return this;
}
}
public interface RecursiveFunctionEx<T> {
IRubyObject call(ThreadContext context, T state, IRubyObject obj, boolean recur);
}
public <T> IRubyObject safeRecurse(RecursiveFunctionEx<T> func, T state, IRubyObject obj, String name, boolean outer) {
Map<IRubyObject, IRubyObject> guards = safeRecurseGetGuards(name);
boolean outermost = outer && !guards.containsKey(NEVER);
if (guards.containsKey(obj)) {
if (outer && !outermost) {
throw new RecursiveError(guards);
}
return func.call(this, state, obj, true);
} else {
if (outermost) {
return safeRecurseOutermost(func, state, obj, guards);
} else {
return safeRecurseInner(func, state, obj, guards);
}
}
}
private <T> IRubyObject safeRecurseOutermost(RecursiveFunctionEx<T> func, T state, IRubyObject obj, Map<IRubyObject, IRubyObject> guards) {
boolean recursed = false;
guards.put(NEVER, NEVER);
try {
return safeRecurseInner(func, state, obj, guards);
} catch (RecursiveError re) {
if (re.tag != guards) {
throw re;
}
recursed = true;
guards.remove(NEVER);
return func.call(this, state, obj, true);
} finally {
if (!recursed) guards.remove(NEVER);
}
}
private Map<IRubyObject, IRubyObject> safeRecurseGetGuards(String name) {
Map<String, Map<IRubyObject, IRubyObject>> symToGuards = this.symToGuards;
if (symToGuards == null) {
this.symToGuards = symToGuards = new HashMap<>();
}
Map<IRubyObject, IRubyObject> guards = symToGuards.get(name);
if (guards == null) {
guards = new IdentityHashMap<>();
symToGuards.put(name, guards);
}
return guards;
}
private <T> IRubyObject safeRecurseInner(RecursiveFunctionEx<T> func, T state, IRubyObject obj, Map<IRubyObject, IRubyObject> guards) {
try {
guards.put(obj, obj);
return func.call(this, state, obj, false);
} finally {
guards.remove(obj);
}
}
private Set<RecursiveComparator.Pair> recursiveSet;
public boolean exceptionRequiresBacktrace = true;
public Encoding[] encodingHolder() {
if (encodingHolder == null) encodingHolder = new Encoding[1];
return encodingHolder;
}
@Deprecated
public void setFile(String file) {
backtrace[backtraceIndex].filename = file;
}
@Deprecated
private org.jruby.util.RubyDateFormat dateFormat;
@Deprecated
public org.jruby.util.RubyDateFormat getRubyDateFormat() {
if (dateFormat == null) dateFormat = new org.jruby.util.RubyDateFormat("-", Locale.US);
return dateFormat;
}
}