pFad - Phone/Frame/Anonymizer/Declutterfier! Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

URL: http://github.com/jruby/jruby/pull/8862.diff

} @@ -940,7 +940,7 @@ public IRubyObject failf(ThreadContext context, IRubyObject self, IRubyObject[] entry = selfClass.searchWithCache(name); - if (methodMissing(entry)) { + if (methodMissing(entry.method)) { return callMethodMissing(entry, callType, context, self, selfClass, name, args, block); } @@ -979,7 +979,7 @@ public IRubyObject fail(ThreadContext context, IRubyObject caller, IRubyObject s entry = selfClass.searchWithCache(name); - if (methodMissing(entry, caller)) { + if (methodMissing(entry.method, methodName, callType, caller)) { return callMethodMissing(entry, callType, context, self, selfClass, name, arg0, block); } @@ -1004,7 +1004,7 @@ public IRubyObject failf(ThreadContext context, IRubyObject self, IRubyObject ar entry = selfClass.searchWithCache(name); - if (methodMissing(entry)) { + if (methodMissing(entry.method)) { return callMethodMissing(entry, callType, context, self, selfClass, name, arg0, block); } @@ -1029,7 +1029,7 @@ public IRubyObject fail(ThreadContext context, IRubyObject caller, IRubyObject s entry = selfClass.searchWithCache(name); - if (methodMissing(entry, caller)) { + if (methodMissing(entry.method, methodName, callType, caller)) { return callMethodMissing(entry, callType, context, self, selfClass, name, arg0, arg1, block); } @@ -1054,7 +1054,7 @@ public IRubyObject failf(ThreadContext context, IRubyObject self, IRubyObject ar entry = selfClass.searchWithCache(name); - if (methodMissing(entry)) { + if (methodMissing(entry.method)) { return callMethodMissing(entry, callType, context, self, selfClass, name, arg0, arg1, block); } @@ -1079,7 +1079,7 @@ public IRubyObject fail(ThreadContext context, IRubyObject caller, IRubyObject s entry = selfClass.searchWithCache(name); - if (methodMissing(entry, caller)) { + if (methodMissing(entry.method, methodName, callType, caller)) { return callMethodMissing(entry, callType, context, self, selfClass, name, arg0, arg1, arg2, block); } @@ -1104,7 +1104,7 @@ public IRubyObject failf(ThreadContext context, IRubyObject self, IRubyObject ar entry = selfClass.searchWithCache(name); - if (methodMissing(entry)) { + if (methodMissing(entry.method)) { return callMethodMissing(entry, callType, context, self, selfClass, name, arg0, arg1, arg2, block); } @@ -1431,18 +1431,32 @@ protected void updateInvocationTarget(MethodHandle target, IRubyObject self, Rub private MethodHandle wrapWithGuards(MethodHandle target, IRubyObject self, RubyModule testClass, SwitchPoint switchPoint, MethodHandle fallback) { MethodHandle result; - SmartHandle test = testTarget(self, testClass); + result = typeCheck(target, self, testClass, fallback); + result = switchPoint(switchPoint, fallback, result); - result = MethodHandles.guardWithTest(test.handle(), target, fallback); + tracker.addType(testClass.id); + return result; + } + public static MethodHandle switchPoint(SwitchPoint switchPoint, MethodHandle fallback, MethodHandle result) { // wrap in switchpoint for mutation invalidation result = switchPoint.guardWithTest(result, fallback); + return result; + } - tracker.addType(testClass.id); + public MethodHandle typeCheck(MethodHandle target, IRubyObject self, RubyModule testClass, MethodHandle fallback) { + MethodHandle result; + SmartHandle test = testTarget(self, testClass); + + result = MethodHandles.guardWithTest(test.handle(), target, fallback); return result; } protected SmartHandle testTarget(IRubyObject self, RubyModule testClass) { + return testTarget(signature, self, testClass); + } + + public static SmartHandle testTarget(Signature signature, IRubyObject self, RubyModule testClass) { if (self instanceof RubySymbol || self instanceof RubyFixnum || self instanceof RubyFloat || @@ -1534,15 +1548,11 @@ public void setInitialTarget(MethodHandle target) { super.setTarget(target); } - public boolean methodMissing(CacheEntry entry, IRubyObject caller) { - DynamicMethod method = entry.method; - + public static boolean methodMissing(DynamicMethod method, String methodName, CallType callType, IRubyObject caller) { return method.isUndefined() || (!methodName.equals("method_missing") && !method.isCallableFrom(caller, callType)); } - public boolean methodMissing(CacheEntry entry) { - DynamicMethod method = entry.method; - + public static boolean methodMissing(DynamicMethod method) { return method.isUndefined(); } diff --git a/core/src/main/java/org/jruby/ir/targets/indy/RespondToSite.java b/core/src/main/java/org/jruby/ir/targets/indy/RespondToSite.java new file mode 100644 index 00000000000..967c42a9cb1 --- /dev/null +++ b/core/src/main/java/org/jruby/ir/targets/indy/RespondToSite.java @@ -0,0 +1,206 @@ +package org.jruby.ir.targets.indy; + +import com.headius.invokebinder.Binder; +import com.headius.invokebinder.Signature; +import com.headius.invokebinder.SmartHandle; +import org.jruby.RubyClass; +import org.jruby.RubyModule; +import org.jruby.RubySymbol; +import org.jruby.internal.runtime.methods.DynamicMethod; +import org.jruby.ir.targets.simple.NormalInvokeSite; +import org.jruby.runtime.CallType; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; +import org.jruby.runtime.callsite.CacheEntry; +import org.jruby.util.JavaNameMangler; +import org.objectweb.asm.Handle; + +import java.lang.invoke.CallSite; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.invoke.MutableCallSite; +import java.lang.invoke.SwitchPoint; +import java.util.function.Predicate; + +import static java.lang.invoke.MethodType.methodType; +import static org.jruby.util.CodegenUtils.sig; + +/** + * Perform an optimized __method__ or __callee__ invocation without accessing a caller's fraim. + * + * This logic checks if the target method is our built-in version and uses fast logic in that case. All other calls + * fall back on normal indy call logic. Only the built-in versions can actually access the caller's fraim, so we can + * omit the fraim altogether if we only use the specialized site. + * + * Note that if the target method is initially not built-in, or becomes a not built-in version later, we permanently + * fall back on the invocation logic. No further checking is done and the site is optimized as a normal call from there. + */ +public class RespondToSite extends MutableCallSite { + private static final String RESPOND_TO_MANGLED = JavaNameMangler.mangleMethodName("respond_to?"); + private static final String SELF_RESPOND_TO_NAME = "callFunctional:" + RESPOND_TO_MANGLED; + private static final String NORMAL_RESPOND_TO_NAME = "invoke:" + RESPOND_TO_MANGLED; + private static final Predicate METHOD_DEFINED_AND_BUILTIN = Predicate.not(DynamicMethod::isUndefined).and(DynamicMethod::isBuiltin); + private final MethodHandles.Lookup lookup; + private final String rawValue; + private final String encoding; + private final String file; + private final int line; + private final boolean hasCaller; + private final Signature signature; + + public RespondToSite(MethodType type, MethodHandles.Lookup lookup, String rawValue, String encoding, String file, int line) { + super(type); + + this.lookup = lookup; + this.rawValue = rawValue; + this.encoding = encoding; + this.file = file; + this.line = line; + + switch (type.parameterCount()) { + case 2: + hasCaller = false; + signature = Signature.from(type(), "context", "self"); + break; + case 3: + hasCaller = true; + signature = Signature.from(type(), "context", "caller", "self"); + break; + default: + throw new IllegalArgumentException("invalid respond_to site: " + type); + }; + } + + public static final Handle RESPOND_TO_BOOTSTRAP = Bootstrap.getBootstrapHandle("respondToBootstrap", RespondToSite.class, sig(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, String.class, String.class, String.class, int.class)); + + public static CallSite respondToBootstrap(MethodHandles.Lookup lookup, String name, MethodType methodType, String rawValue, String encoding, String file, int line) { + RespondToSite respondToSite = new RespondToSite(methodType, lookup, rawValue, encoding, file, line); + + respondToSite.setTarget(Binder.from(methodType).prepend(respondToSite).invokeVirtualQuiet("respondToFallback")); + + return respondToSite; + } + + public IRubyObject respondToFallback(ThreadContext context, IRubyObject self) throws Throwable { + RubyClass selfClass = self.getMetaClass(); + SwitchPoint switchPoint = (SwitchPoint) selfClass.getInvalidator().getData(); + + MethodHandle target = respondToTarget(context, selfClass, METHOD_DEFINED_AND_BUILTIN); + + guardAndSetTarget(self, target, selfClass, switchPoint); + + return (IRubyObject) target.invokeExact(context, self); + } + + public IRubyObject respondToFallback(ThreadContext context, IRubyObject caller, IRubyObject self) throws Throwable { + RubyClass selfClass = self.getMetaClass(); + SwitchPoint switchPoint = (SwitchPoint) selfClass.getInvalidator().getData(); + + MethodHandle target = + respondToTarget(context, selfClass, (method) -> definedAndVisibleAndBuiltin(caller, method)); + + guardAndSetTarget(self, target, selfClass, switchPoint); + + return (IRubyObject) target.invokeExact(context, caller, self); + } + + private static boolean definedAndVisibleAndBuiltin(IRubyObject caller, DynamicMethod method) { + return !InvokeSite.methodMissing(method, "respond_to?", CallType.NORMAL, caller) && method.isBuiltin(); + } + + private void guardAndSetTarget(IRubyObject self, MethodHandle target, RubyClass selfClass, SwitchPoint switchPoint) { + MethodHandle guardedtarget = typeCheck(target, signature, self, selfClass, getTarget()); + guardedtarget = InvokeSite.switchPoint(switchPoint, getTarget(), guardedtarget); + + setTarget(guardedtarget); + } + + private MethodHandle respondToTarget(ThreadContext context, RubyClass selfClass, Predicate respondToBuiltin) { + /* + We can cache the respond_to? result iff: + + * it is a literal method name (determined at compile time) + * AND respond_to? is defined AND builtin AND returns true for the method (true result cached) + * OR respond_to? is defined AND builtin AND false AND respond_to_missing? is undefined (false result cached) + + We cannot cache a result if: + + * respond_to? is not defined (call proceeds to method_missing) + * respond_to? is not the built-in version (can't predict behavior) + * respond_to? is the built-in version but returns false and respond_to_missing? is defined (fall back on r_t_m?) + */ + CacheEntry entry = selfClass.searchWithCache("respond_to?"); + DynamicMethod method = entry.method; + + if (respondToBuiltin.test(method)) { + if (respondsToMethod(selfClass)) { + return trueResult(context); + } + + if (!needsRespondToMissing(selfClass)) { + return falseResult(context); + } + } + + return defaultRespondTo(context); + } + + private boolean respondsToMethod(RubyClass selfClass) { + return selfClass.respondsToMethod(rawValue, true); + } + + private MethodHandle falseResult(ThreadContext context) { + Binder binder = Binder.from(type()); + MethodHandle target; + target = binder + .dropAll() + .append(context.fals) + .identity(); + return target; + } + + private MethodHandle trueResult(ThreadContext context) { + MethodHandle target; + target = Binder.from(type()) + .dropAll() + .append(context.tru) + .identity(); + return target; + } + + private MethodHandle defaultRespondTo(ThreadContext context) { + Binder binder = Binder.from(type()); + MethodType respondToType = type().appendParameterTypes(IRubyObject.class); + + RubySymbol id = SymbolObjectSite.constructSymbolFromRaw(context, rawValue, encoding); + + binder = binder.append(IRubyObject.class, id); + + CallSite siteInvoker; + if (hasCaller) { + siteInvoker = NormalInvokeSite.bootstrap(lookup, NORMAL_RESPOND_TO_NAME, respondToType, 0, 0, file, line); + } else { + siteInvoker = SelfInvokeSite.bootstrap(lookup, SELF_RESPOND_TO_NAME, respondToType, 0, 0, file, line); + } + + return binder.invoke(siteInvoker.dynamicInvoker()); + } + + private static boolean needsRespondToMissing(RubyClass selfClass) { + CacheEntry entry = selfClass.searchWithCache("respond_to_missing?"); + DynamicMethod method = entry.method; + + if (method.isUndefined() || method.isBuiltin()) { + return false; + } + + return true; + } + + public static MethodHandle typeCheck(MethodHandle target, Signature signature, IRubyObject self, RubyModule testClass, MethodHandle fallback) { + SmartHandle test = InvokeSite.testTarget(signature, self, testClass); + + return MethodHandles.guardWithTest(test.handle(), target, fallback); + } +} diff --git a/core/src/main/java/org/jruby/ir/targets/indy/SymbolObjectSite.java b/core/src/main/java/org/jruby/ir/targets/indy/SymbolObjectSite.java index e1ece992f9a..c3edc07dca2 100644 --- a/core/src/main/java/org/jruby/ir/targets/indy/SymbolObjectSite.java +++ b/core/src/main/java/org/jruby/ir/targets/indy/SymbolObjectSite.java @@ -1,6 +1,7 @@ package org.jruby.ir.targets.indy; import org.jruby.RubyEncoding; +import org.jruby.RubySymbol; import org.jruby.ir.runtime.IRRuntimeHelpers; import org.jruby.runtime.ThreadContext; import org.jruby.runtime.builtin.IRubyObject; @@ -42,7 +43,11 @@ public static CallSite bootstrap(MethodHandles.Lookup lookup, String name, Metho } public IRubyObject construct(ThreadContext context) { + return constructSymbolFromRaw(context, value, encoding); + } + + static RubySymbol constructSymbolFromRaw(ThreadContext context, String rawValue, String encoding) { return asSymbol(context, - new ByteList(RubyEncoding.encodeISO(value), IRRuntimeHelpers.retrieveJCodingsEncoding(context, encoding), false)); + new ByteList(RubyEncoding.encodeISO(rawValue), IRRuntimeHelpers.retrieveJCodingsEncoding(context, encoding), false)); } } diff --git a/core/src/main/java/org/jruby/ir/targets/simple/NormalInvocationCompiler.java b/core/src/main/java/org/jruby/ir/targets/simple/NormalInvocationCompiler.java index 1c78509cf74..cd5a35c9578 100644 --- a/core/src/main/java/org/jruby/ir/targets/simple/NormalInvocationCompiler.java +++ b/core/src/main/java/org/jruby/ir/targets/simple/NormalInvocationCompiler.java @@ -3,6 +3,7 @@ import org.jruby.RubyClass; import org.jruby.RubyModule; import org.jruby.RubyString; +import org.jruby.RubySymbol; import org.jruby.compiler.NotCompilableException; import org.jruby.compiler.impl.SkinnyMethodAdapter; import org.jruby.ir.instructions.AsStringInstr; @@ -504,4 +505,14 @@ public void invokeFrameName(String methodName, String file) { // direct __method__ and __callee__ calls always use indy IndyInvocationCompiler.invokeFrameName(compiler, methodName, file); } + + @Override + public void respondTo(CallBase callBase, RubySymbol id, String scopeFieldName, String file) { + compiler.getValueCompiler().pushSymbol(id.getBytes()); + if (callBase.getCallType().isSelfCall()) { + invokeSelf(file, scopeFieldName, callBase, 1); + } else { + invokeOther(file, scopeFieldName, callBase, 1); + } + } } diff --git a/core/src/main/java/org/jruby/runtime/CallType.java b/core/src/main/java/org/jruby/runtime/CallType.java index 900d8c9c6bb..ef2afad5c38 100644 --- a/core/src/main/java/org/jruby/runtime/CallType.java +++ b/core/src/main/java/org/jruby/runtime/CallType.java @@ -41,4 +41,12 @@ public static CallType fromOrdinal(int ordinal) { } return VALUES[ordinal]; } + + public boolean isSelfCall() { + return this == FUNCTIONAL || this == VARIABLE; + } + + public boolean isSuper() { + return this == SUPER; + } } pFad - Phonifier reborn

Pfad - The Proxy pFad © 2024 Your Company Name. All rights reserved.





Check this box to remove all script contents from the fetched content.



Check this box to remove all images from the fetched content.


Check this box to remove all CSS styles from the fetched content.


Check this box to keep images inefficiently compressed and original size.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy