diff --git a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/CallFrame.java b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/CallFrame.java index 0ab3768c7eda..ca8a82fa21ad 100644 --- a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/CallFrame.java +++ b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/CallFrame.java @@ -42,7 +42,7 @@ public final class CallFrame { private final byte typeTag; private final long classId; - private final MethodRef method; + private final MethodVersionRef methodVersion; private final long methodId; private final long codeIndex; private final long threadId; @@ -55,14 +55,14 @@ public final class CallFrame { private Object scope; private final TruffleLogger logger; - public CallFrame(long threadId, byte typeTag, long classId, MethodRef method, long methodId, long codeIndex, Frame frame, Node currentNode, RootNode rootNode, + public CallFrame(long threadId, byte typeTag, long classId, MethodVersionRef methodVersion, long methodId, long codeIndex, Frame frame, Node currentNode, RootNode rootNode, DebugStackFrame debugStackFrame, JDWPContext context, TruffleLogger logger) { this.threadId = threadId; this.typeTag = typeTag; this.classId = classId; - this.method = method; + this.methodVersion = methodVersion; this.methodId = methodId; - this.codeIndex = method != null && method.isObsolete() ? -1 : codeIndex; + this.codeIndex = methodVersion != null && methodVersion.isObsolete() ? -1 : codeIndex; this.frame = frame; this.currentNode = currentNode; this.rootNode = rootNode; @@ -85,15 +85,22 @@ public long getClassId() { return classId; } + public MethodVersionRef getMethodVersion() { + return methodVersion; + } + public MethodRef getMethod() { - return method; + if (methodVersion != null) { + return methodVersion.getMethod(); + } + return null; } public long getMethodId() { - if (method == null) { + if (methodVersion == null) { return methodId; } - return method.isObsolete() ? 0 : methodId; + return methodVersion.isObsolete() ? 0 : methodId; } public long getCodeIndex() { diff --git a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/Ids.java b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/Ids.java index 664cd2f9e9da..24dfc0441f3c 100644 --- a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/Ids.java +++ b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/Ids.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -172,31 +172,6 @@ private synchronized long generateUniqueId(T object) { return id; } - public void replaceObject(T original, T replacement) { - int id = (int) getIdAsLong(original); - objects[id] = new WeakReference<>(replacement); - log(() -> "Replaced ID: " + id); - } - - @SuppressWarnings({"unchecked", "rawtypes"}) - public void updateId(KlassRef klass) { - // remove existing ID - removeId(klass); - Long theId = innerClassIDMap.get(klass.getNameAsString()); - if (theId != null) { - // then inject klass under the new ID - objects[(int) (long) theId] = new WeakReference(klass); - } - } - - @SuppressWarnings({"unchecked", "rawtypes"}) - private void removeId(KlassRef klass) { - int id = (int) getId(klass); - if (id > 0) { - objects[id] = new WeakReference<>(null); - } - } - public boolean checkRemoved(long refTypeId) { return innerClassIDMap.containsValue(refTypeId); } diff --git a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/JDWPContext.java b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/JDWPContext.java index a69260c15242..18a6103cc09b 100644 --- a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/JDWPContext.java +++ b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/JDWPContext.java @@ -73,7 +73,7 @@ public interface JDWPContext { * @param root the Truffle root node object * @return the declaring method of the root node */ - MethodRef getMethodFromRootNode(RootNode root); + MethodVersionRef getMethodFromRootNode(RootNode root); /** * @return guest language array of all active threads diff --git a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/MethodRef.java b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/MethodRef.java index 92784b3f0675..986236e98b2c 100644 --- a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/MethodRef.java +++ b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/MethodRef.java @@ -175,14 +175,6 @@ public interface MethodRef { */ boolean hasSourceFileAttribute(); - /** - * Determines if the code index is located in the source file on the last line of this method. - * - * @param codeIndex - * @return true if last line, false otherwise - */ - boolean isLastLine(long codeIndex); - /** * Returns the klass that declares this method. * @@ -234,16 +226,6 @@ public interface MethodRef { void disposeHooks(); - /** - * Determine if this method is obsolete. A method is obsolete if it has been replaced by a - * non-equivalent method using the RedefineClasses command. The original and redefined methods - * are considered equivalent if their bytecodes are the same except for indices into the - * constant pool and the referenced constants are equal. - * - * @return true if the method is obsolete - */ - boolean isObsolete(); - /** * Returns the last bci of the method. * @@ -264,4 +246,11 @@ public interface MethodRef { * @return true if the method is a static initializer */ boolean isClassInitializer(); + + /** + * Determines if this method was removed by redefinition. + * + * @return true if removed + */ + boolean isRemovedByRedefinition(); } diff --git a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/MethodVersionRef.java b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/MethodVersionRef.java new file mode 100644 index 000000000000..180570b093c1 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/MethodVersionRef.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.espresso.jdwp.api; + +public interface MethodVersionRef { + /** + * Returns the MethodRef corresponding to this method version. + * + * @return the MethodRef + */ + MethodRef getMethod(); + + /** + * Determine if this method is obsolete. A method is obsolete if it has been replaced by a + * non-equivalent method using the RedefineClasses command. The original and redefined methods + * are considered equivalent if their bytecodes are the same except for indices into the + * constant pool and the referenced constants are equal. + * + * @return true if the method is obsolete + */ + boolean isObsolete(); +} diff --git a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/VMListener.java b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/VMListener.java index 5adc987ad0ba..edd7a8017446 100644 --- a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/VMListener.java +++ b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/VMListener.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2029, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -96,7 +96,7 @@ public interface VMListener { * This method should be called when when the monitor wait(timeout) method is invoked in the * guest VM. A monitor wait event will then be sent through JDWP, if there was a request for the * current thread. - * + * * @param monitor the monitor object * @param timeout the timeout in ms before the wait will time out */ diff --git a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/DebuggerController.java b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/DebuggerController.java index 66a228bf0187..efe8d39aee49 100644 --- a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/DebuggerController.java +++ b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/DebuggerController.java @@ -68,6 +68,7 @@ import com.oracle.truffle.espresso.jdwp.api.JDWPOptions; import com.oracle.truffle.espresso.jdwp.api.KlassRef; import com.oracle.truffle.espresso.jdwp.api.MethodRef; +import com.oracle.truffle.espresso.jdwp.api.MethodVersionRef; import com.oracle.truffle.espresso.jdwp.api.VMEventListener; public final class DebuggerController implements ContextsListener { @@ -415,10 +416,10 @@ private void doStepOut(SuspendedInfo susp) { SteppingInfo steppingInfo = commandRequestIds.get(susp.getThread()); if (steppingInfo != null && stepOutBCI != -1) { // record the location that we'll land on after the step out completes - MethodRef method = context.getMethodFromRootNode(callerRoot); + MethodVersionRef method = context.getMethodFromRootNode(callerRoot); if (method != null) { - KlassRef klass = method.getDeclaringKlassRef(); - steppingInfo.setStepOutBCI(context.getIds().getIdAsLong(klass), context.getIds().getIdAsLong(method), stepOutBCI); + KlassRef klass = method.getMethod().getDeclaringKlassRef(); + steppingInfo.setStepOutBCI(context.getIds().getIdAsLong(klass), context.getIds().getIdAsLong(method.getMethod()), stepOutBCI); } } } @@ -869,16 +870,17 @@ public CallFrame[] getCallFrames(Object guestThread) { List<CallFrame> callFrames = new ArrayList<>(); Truffle.getRuntime().iterateFrames(frameInstance -> { KlassRef klass; - MethodRef method; + MethodVersionRef methodVersion; RootNode root = getRootNode(frameInstance); if (root == null) { return null; } - method = getContext().getMethodFromRootNode(root); - if (method == null) { + methodVersion = getContext().getMethodFromRootNode(root); + if (methodVersion == null) { return null; } + MethodRef method = methodVersion.getMethod(); klass = method.getDeclaringKlassRef(); long klassId = ids.getIdAsLong(klass); long methodId = ids.getIdAsLong(method); @@ -914,7 +916,7 @@ public CallFrame[] getCallFrames(Object guestThread) { if (currentNode instanceof RootNode) { currentNode = context.getInstrumentableNode((RootNode) currentNode); } - callFrames.add(new CallFrame(context.getIds().getIdAsLong(guestThread), typeTag, klassId, method, methodId, codeIndex, frame, currentNode, root, null, context, jdwpLogger)); + callFrames.add(new CallFrame(context.getIds().getIdAsLong(guestThread), typeTag, klassId, methodVersion, methodId, codeIndex, frame, currentNode, root, null, context, jdwpLogger)); return null; }); return callFrames.toArray(new CallFrame[0]); @@ -1227,11 +1229,11 @@ private CallFrame[] createCallFrames(long threadId, Iterable<DebugStackFrame> st } Frame rawFrame = frame.getRawFrame(context.getLanguageClass(), FrameInstance.FrameAccess.READ_WRITE); - MethodRef method = context.getMethodFromRootNode(root); - KlassRef klass = method.getDeclaringKlassRef(); + MethodVersionRef methodVersion = context.getMethodFromRootNode(root); + KlassRef klass = methodVersion.getMethod().getDeclaringKlassRef(); klassId = ids.getIdAsLong(klass); - methodId = ids.getIdAsLong(method); + methodId = ids.getIdAsLong(methodVersion.getMethod()); typeTag = TypeTag.getKind(klass); // check if we have a dedicated step out code index on the top frame @@ -1241,7 +1243,7 @@ private CallFrame[] createCallFrames(long threadId, Iterable<DebugStackFrame> st codeIndex = context.getBCI(rawNode, rawFrame); } - list.addLast(new CallFrame(threadId, typeTag, klassId, method, methodId, codeIndex, rawFrame, rawNode, root, frame, context, jdwpLogger)); + list.addLast(new CallFrame(threadId, typeTag, klassId, methodVersion, methodId, codeIndex, rawFrame, rawNode, root, frame, context, jdwpLogger)); frameCount++; if (frameLimit != -1 && frameCount >= frameLimit) { return list.toArray(new CallFrame[0]); diff --git a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/JDWP.java b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/JDWP.java index 05b8bbc01799..6c822d309410 100644 --- a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/JDWP.java +++ b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/JDWP.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1518,7 +1518,8 @@ static CommandResult createReply(Packet packet, JDWPContext context) { if (method == null) { return new CommandResult(reply); } - reply.writeBoolean(method.isObsolete()); + // only condition to check here is if removed by redefinition + reply.writeBoolean(method.isRemovedByRedefinition()); } return new CommandResult(reply); } @@ -2154,7 +2155,7 @@ static CommandResult createReply(Packet packet, DebuggerController controller) { reply.writeLong(controller.getContext().getIds().getIdAsLong(frame)); reply.writeByte(frame.getTypeTag()); reply.writeLong(frame.getClassId()); - reply.writeLong(frame.getMethod().isObsolete() ? 0 : controller.getContext().getIds().getIdAsLong(frame.getMethod())); + reply.writeLong(frame.getMethodVersion().isObsolete() ? 0 : controller.getContext().getIds().getIdAsLong(frame.getMethod())); reply.writeLong(frame.getCodeIndex()); } return new CommandResult(reply); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Method.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Method.java index 3a5704cc944e..e391615c0db6 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Method.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Method.java @@ -115,6 +115,7 @@ import com.oracle.truffle.espresso.jdwp.api.KlassRef; import com.oracle.truffle.espresso.jdwp.api.MethodHook; import com.oracle.truffle.espresso.jdwp.api.MethodRef; +import com.oracle.truffle.espresso.jdwp.api.MethodVersionRef; import com.oracle.truffle.espresso.jni.Mangle; import com.oracle.truffle.espresso.meta.EspressoError; import com.oracle.truffle.espresso.meta.Meta; @@ -276,22 +277,6 @@ public Attribute getAttribute(Symbol<Name> attrName) { return getLinkedMethod().getAttribute(attrName); } - @Override - @TruffleBoundary - public int bciToLineNumber(int bci) { - if (bci < 0) { - return bci; - } - if (isNative()) { - return EspressoStackElement.NATIVE_BCI; - } - if (getCodeAttribute() == null) { - assert isAbstract(); - return EspressoStackElement.UNKNOWN_BCI; - } - return getCodeAttribute().bciToLineNumber(bci); - } - @Override public EspressoContext getContext() { return declaringKlass.getContext(); @@ -849,11 +834,6 @@ public Method setPoisonPill() { return this; } - @Override - public boolean hasSourceFileAttribute() { - return declaringKlass.getAttribute(Names.SourceFile) != null; - } - public String report(int curBCI) { String sourceFile = getDeclaringKlass().getSourceFile(); if (sourceFile == null) { @@ -1000,103 +980,6 @@ public String getInteropString() { return getNameAsString() + AbstractLookupNode.METHOD_SELECTION_SEPARATOR + getRawSignature(); } - @Override - public boolean hasActiveHook() { - return hasActiveHook.get(); - } - - @Override - public synchronized MethodHook[] getMethodHooks() { - return Arrays.copyOf(hooks, hooks.length); - } - - @Override - public synchronized void addMethodHook(MethodHook info) { - hasActiveHook.set(true); - hooks = Arrays.copyOf(hooks, hooks.length + 1); - hooks[hooks.length - 1] = info; - } - - private void expectActiveHooks() { - if (hooks.length == 0) { - throw new RuntimeException("Method: " + getNameAsString() + " expected to contain method hook"); - } - } - - @Override - public synchronized void removeMethodHook(int requestId) { - expectActiveHooks(); - boolean removed = false; - // shrink the array to avoid null values - if (hooks.length == 1) { - // make sure it's the right hook - if (hooks[0].getRequestId() == requestId) { - hooks = MethodHook.EMPTY; - hasActiveHook.set(false); - removed = true; - } - } else { - int removeIndex = -1; - for (int i = 0; i < hooks.length; i++) { - if (hooks[i].getRequestId() == requestId) { - removeIndex = i; - break; - } - } - if (removeIndex != -1) { - MethodHook[] temp = new MethodHook[hooks.length - 1]; - for (int i = 0; i < temp.length; i++) { - temp[i] = i < removeIndex ? hooks[i] : hooks[i + 1]; - } - hooks = temp; - removed = true; - } - } - if (!removed) { - throw new RuntimeException("Method: " + getNameAsString() + " should contain method hook"); - } - } - - @Override - public synchronized void removeMethodHook(MethodHook hook) { - expectActiveHooks(); - boolean removed = false; - // shrink the array to avoid null values - if (hooks.length == 1) { - // make sure it's the right hook - if (hooks[0] == hook) { - hooks = MethodHook.EMPTY; - hasActiveHook.set(false); - removed = true; - } - } else { - int removeIndex = -1; - for (int i = 0; i < hooks.length; i++) { - if (hooks[i] == hook) { - removeIndex = i; - break; - } - } - if (removeIndex != -1) { - MethodHook[] temp = new MethodHook[hooks.length - 1]; - for (int i = 0; i < temp.length; i++) { - temp[i] = i < removeIndex ? hooks[i] : hooks[i + 1]; - } - hooks = temp; - removed = true; - } - } - if (!removed) { - throw new RuntimeException("Method: " + getNameAsString() + " should contain method hook"); - } - } - - @Override - public synchronized void disposeHooks() { - hasActiveHook.set(false); - hooks = MethodHook.EMPTY; - } - SharedRedefinitionContent redefine(ObjectKlass.KlassVersion klassVersion, ParserMethod newMethod, ParserKlass newKlass) { // install the new method version immediately LinkedMethod newLinkedMethod = new LinkedMethod(newMethod); @@ -1149,6 +1032,7 @@ public void removedByRedefinition() { removedByRedefinition = true; } + @Override public boolean isRemovedByRedefinition() { return removedByRedefinition; } @@ -1407,11 +1291,6 @@ public String getSourceFile() { return getDeclaringKlass().getSourceFile(); } - @Override - public String getNameAsString() { - return getName().toString(); - } - @Override public String getSignatureAsString() { return getRawSignature().toString(); @@ -1461,40 +1340,40 @@ public LineNumberTableAttribute getLineNumberTable() { @Override public Object invokeMethodVirtual(Object... args) { - getMethodVersion().checkRemovedByRedefinition(); + checkRemovedByRedefinition(); return invokeDirectVirtual(args); } @Override public Object invokeMethodStatic(Object... args) { - getMethodVersion().checkRemovedByRedefinition(); + checkRemovedByRedefinition(); return invokeDirectStatic(args); } @Override public Object invokeMethodSpecial(Object... args) { - getMethodVersion().checkRemovedByRedefinition(); + checkRemovedByRedefinition(); return invokeDirectSpecial(args); } @Override public Object invokeMethodInterface(Object... args) { - getMethodVersion().checkRemovedByRedefinition(); + checkRemovedByRedefinition(); return invokeDirectInterface(args); } @Override public Object invokeMethodNonVirtual(Object... args) { - getMethodVersion().checkRemovedByRedefinition(); + checkRemovedByRedefinition(); return invokeDirect(args); } - @Override - public boolean isLastLine(long codeIndex) { - LineNumberTableAttribute table = getLineNumberTable(); - int lastLine = table.getLastLine(); - int lineAt = table.getLineNumber((int) codeIndex); - return lastLine == lineAt; + private void checkRemovedByRedefinition() { + if (isRemovedByRedefinition()) { + Meta meta = getMeta(); + throw meta.throwExceptionWithMessage(meta.java_lang_NoSuchMethodError, + meta.toGuestString(getDeclaringKlass().getNameAsString() + "." + getName() + getRawSignature())); + } } @Override @@ -1507,11 +1386,6 @@ public int getLastLine() { return getLineNumberTable().getLastLine(); } - @Override - public boolean isObsolete() { - return !getMethodVersion().klassVersion.getAssumption().isValid(); - } - @Override public long getLastBCI() { int bci = 0; @@ -1529,6 +1403,126 @@ public long getLastBCI() { return bci; } + @Override + public int bciToLineNumber(int bci) { + if (bci < 0) { + return bci; + } + if (isNative()) { + return EspressoStackElement.NATIVE_BCI; + } + if (getCodeAttribute() == null) { + assert isAbstract(); + return EspressoStackElement.UNKNOWN_BCI; + } + return getCodeAttribute().bciToLineNumber(bci); + } + + @Override + public boolean hasSourceFileAttribute() { + return declaringKlass.getAttribute(Names.SourceFile) != null; + } + + @Override + public String getNameAsString() { + return getName().toString(); + } + + @Override + public boolean hasActiveHook() { + return hasActiveHook.get(); + } + + @Override + public synchronized MethodHook[] getMethodHooks() { + return Arrays.copyOf(hooks, hooks.length); + } + + @Override + public synchronized void addMethodHook(MethodHook info) { + hasActiveHook.set(true); + hooks = Arrays.copyOf(hooks, hooks.length + 1); + hooks[hooks.length - 1] = info; + } + + private void expectActiveHooks() { + if (hooks.length == 0) { + throw new RuntimeException("Method: " + getNameAsString() + " expected to contain method hook"); + } + } + + @Override + public synchronized void removeMethodHook(int requestId) { + expectActiveHooks(); + boolean removed = false; + // shrink the array to avoid null values + if (hooks.length == 1) { + // make sure it's the right hook + if (hooks[0].getRequestId() == requestId) { + hooks = MethodHook.EMPTY; + hasActiveHook.set(false); + removed = true; + } + } else { + int removeIndex = -1; + for (int i = 0; i < hooks.length; i++) { + if (hooks[i].getRequestId() == requestId) { + removeIndex = i; + break; + } + } + if (removeIndex != -1) { + MethodHook[] temp = new MethodHook[hooks.length - 1]; + for (int i = 0; i < temp.length; i++) { + temp[i] = i < removeIndex ? hooks[i] : hooks[i + 1]; + } + hooks = temp; + removed = true; + } + } + if (!removed) { + throw new RuntimeException("Method: " + getNameAsString() + " should contain method hook"); + } + } + + @Override + public synchronized void removeMethodHook(MethodHook hook) { + expectActiveHooks(); + boolean removed = false; + // shrink the array to avoid null values + if (hooks.length == 1) { + // make sure it's the right hook + if (hooks[0] == hook) { + hooks = MethodHook.EMPTY; + hasActiveHook.set(false); + removed = true; + } + } else { + int removeIndex = -1; + for (int i = 0; i < hooks.length; i++) { + if (hooks[i] == hook) { + removeIndex = i; + break; + } + } + if (removeIndex != -1) { + MethodHook[] temp = new MethodHook[hooks.length - 1]; + for (int i = 0; i < temp.length; i++) { + temp[i] = i < removeIndex ? hooks[i] : hooks[i + 1]; + } + hooks = temp; + removed = true; + } + } + if (!removed) { + throw new RuntimeException("Method: " + getNameAsString() + " should contain method hook"); + } + } + + public synchronized void disposeHooks() { + hasActiveHook.set(false); + hooks = MethodHook.EMPTY; + } // endregion Methodref impl private static final class Continuum { @@ -1586,7 +1580,7 @@ public CallTarget getContinuableCallTarget(MethodVersion mv, int bci) { * Each time a method is changed via class redefinition it gets a new version, and therefore a * new MethodVersion object. */ - public final class MethodVersion implements ModifiersProvider { + public final class MethodVersion implements MethodVersionRef, ModifiersProvider { private static final int CODE_FLAGS_MASK = 0b00001111; private static final int CODE_FLAGS_READY = 0x1; private static final int CODE_FLAGS_HAS_JSR = 0x2; @@ -1712,11 +1706,17 @@ public LinkedMethod getLinkedMethod() { return linkedMethod; } + @Override @Idempotent public Method getMethod() { return Method.this; } + @Override + public boolean isObsolete() { + return !klassVersion.getAssumption().isValid(); + } + public Symbol<Name> getName() { return linkedMethod.getName(); } @@ -1977,14 +1977,6 @@ public LineNumberTableAttribute getLineNumberTableAttribute() { return LineNumberTableAttribute.EMPTY; } - private void checkRemovedByRedefinition() { - if (getMethod().isRemovedByRedefinition()) { - Meta meta = getMeta(); - throw meta.throwExceptionWithMessage(meta.java_lang_NoSuchMethodError, - meta.toGuestString(getMethod().getDeclaringKlass().getNameAsString() + "." + getName() + getRawSignature())); - } - } - @Override public String toString() { return "EspressoMethod<" + getDeclaringKlass().getType() + "." + getName() + getRawSignature() + ">"; diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoContext.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoContext.java index ce8b3b5adf8d..43d6f70f1add 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoContext.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoContext.java @@ -1143,14 +1143,14 @@ public void reportOnContendedMonitorEntered(StaticObject obj) { espressoEnv.getEventListener().onContendedMonitorEntered(obj); } - public boolean reportOnMethodEntry(Method.MethodVersion method, Object scope) { + public boolean reportOnMethodEntry(Method.MethodVersion methodVersion, Object scope) { assert shouldReportVMEvents(); - return espressoEnv.getEventListener().onMethodEntry(method.getMethod(), scope); + return espressoEnv.getEventListener().onMethodEntry(methodVersion.getMethod(), scope); } - public boolean reportOnMethodReturn(Method.MethodVersion method, Object returnValue) { + public boolean reportOnMethodReturn(Method.MethodVersion methodVersion, Object returnValue) { assert shouldReportVMEvents(); - return espressoEnv.getEventListener().onMethodReturn(method.getMethod(), returnValue); + return espressoEnv.getEventListener().onMethodReturn(methodVersion.getMethod(), returnValue); } public boolean reportOnFieldModification(Field field, StaticObject receiver, Object value) { diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/JDWPContextImpl.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/JDWPContextImpl.java index 3ac3e34e5cda..cbeaf8075598 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/JDWPContextImpl.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/JDWPContextImpl.java @@ -59,6 +59,7 @@ import com.oracle.truffle.espresso.jdwp.api.JDWPContext; import com.oracle.truffle.espresso.jdwp.api.KlassRef; import com.oracle.truffle.espresso.jdwp.api.MethodRef; +import com.oracle.truffle.espresso.jdwp.api.MethodVersionRef; import com.oracle.truffle.espresso.jdwp.api.ModuleRef; import com.oracle.truffle.espresso.jdwp.api.MonitorStackInfo; import com.oracle.truffle.espresso.jdwp.api.RedefineInfo; @@ -306,9 +307,9 @@ public String getStringValue(Object object) { } @Override - public MethodRef getMethodFromRootNode(RootNode root) { + public MethodVersionRef getMethodFromRootNode(RootNode root) { if (root != null && root instanceof EspressoRootNode) { - return ((EspressoRootNode) root).getMethod(); + return ((EspressoRootNode) root).getMethodVersion(); } return null; } @@ -646,7 +647,7 @@ public int getNextBCI(RootNode callerRoot, Frame frame) { if (callerRoot instanceof EspressoRootNode espressoRootNode) { int bci = (int) readBCIFromFrame(callerRoot, frame); if (bci >= 0) { - BytecodeStream bs = new BytecodeStream(espressoRootNode.getMethod().getOriginalCode()); + BytecodeStream bs = new BytecodeStream(espressoRootNode.getMethodVersion().getOriginalCode()); return bs.nextBCI(bci); } } @@ -665,8 +666,8 @@ public long readBCIFromFrame(RootNode root, Frame frame) { public CallFrame locateObjectWaitFrame() { Object currentThread = asGuestThread(Thread.currentThread()); KlassRef klass = context.getMeta().java_lang_Object; - MethodRef method = context.getMeta().java_lang_Object_wait; - return new CallFrame(ids.getIdAsLong(currentThread), TypeTag.CLASS, ids.getIdAsLong(klass), method, ids.getIdAsLong(method), 0, null, null, null, null, null, LOGGER); + MethodVersionRef methodVersion = context.getMeta().java_lang_Object_wait.getMethodVersion(); + return new CallFrame(ids.getIdAsLong(currentThread), TypeTag.CLASS, ids.getIdAsLong(klass), methodVersion, ids.getIdAsLong(methodVersion.getMethod()), 0, null, null, null, null, null, LOGGER); } @Override